(function() {

    'use strict';

    var Vec3 = require('./Vec3');
    var Mat33 = require('./Mat33');
    var Mat44 = require('./Mat44');

    /**
     * Instantiates a Quaternion object.
     * @class Quaternion
     * @classdesc A quaternion representing an orientation.
     */
    function Quaternion() {
        switch ( arguments.length ) {
            case 1:
                // array or Quaternion argument
                var argument = arguments[0];
                if ( argument.w !== undefined ) {
                    this.w = argument.w;
                } else if ( argument[0] !== undefined ) {
                    this.w = argument[0];
                } else {
                    this.w = 1.0;
                }
                this.x = argument.x || argument[1] || 0.0;
                this.y = argument.y || argument[2] || 0.0;
                this.z = argument.z || argument[3] || 0.0;
                break;
            case 4:
                // individual component arguments
                this.w = arguments[0];
                this.x = arguments[1];
                this.y = arguments[2];
                this.z = arguments[3];
                break;
            default:
                this.w = 1;
                this.x = 0;
                this.y = 0;
                this.z = 0;
                break;
        }
    }

    /**
     * Returns a quaternion that represents an oreintation matching
     * the identity matrix.
     * @memberof Quaternion
     *
     * @returns {Quaternion} The identity quaternion.
     */
    Quaternion.identity = function() {
        return new Quaternion( 1, 0, 0, 0 );
    };

    /**
     * Returns a new Quaternion with each component negated.
     * @memberof Quaternion
     *
     * @returns {Quaternion} The negated quaternion.
     */
     Quaternion.prototype.negate = function() {
        return new Quaternion( -this.w, -this.x, -this.y, -this.z );
    };

    /**
     * Concatenates the rotations of the two quaternions, returning
     * a new Quaternion object.
     * @memberof Quaternion
     *
     * @param {Quaternion|Array} that - The quaterion to concatenate.
     *
     * @returns {Quaternion} The resulting concatenated quaternion.
     */
    Quaternion.prototype.multQuat = function( that ) {
        that = ( that instanceof Array ) ? new Quaternion( that ) : that;
        var w = (that.w * this.w) - (that.x * this.x) - (that.y * this.y) - (that.z * this.z),
            x = this.y*that.z - this.z*that.y + this.w*that.x + this.x*that.w,
            y = this.z*that.x - this.x*that.z + this.w*that.y + this.y*that.w,
            z = this.x*that.y - this.y*that.x + this.w*that.z + this.z*that.w;
        return new Quaternion( w, x, y, z );
    };

    /**
     * Applies the orientation of the quaternion as a rotation
     * matrix to the provided vector, returning a new Vec3 object.
     * @memberof Quaternion
     *
     * @param {Vec3|Vec4|Array} that - The vector to rotate.
     *
     * @returns {Vec3} The resulting rotated vector.
     */
    Quaternion.prototype.rotate = function( that ) {
        that = ( that instanceof Array ) ? new Vec3( that ) : that;
        var vq = new Quaternion( 0, that.x, that.y, that.z ),
            r = this.multQuat( vq ).multQuat( this.inverse() );
        return new Vec3( r.x, r.y, r.z );
    };

    /**
     * Returns the x-axis of the rotation matrix that the quaternion represents.
     * @memberof Quaternion
     *
     * @returns {Vec3} The x-axis of the rotation matrix represented by the quaternion.
     */
    Quaternion.prototype.xAxis = function() {
        var yy = this.y*this.y,
            zz = this.z*this.z,
            xy = this.x*this.y,
            xz = this.x*this.z,
            yw = this.y*this.w,
            zw = this.z*this.w;
        return new Vec3(
            1 - 2*yy - 2*zz,
            2*xy + 2*zw,
            2*xz - 2*yw ).normalize();
    };

    /**
     * Returns the y-axis of the rotation matrix that the quaternion represents.
     * @memberof Quaternion
     *
     * @returns {Vec3} The y-axis of the rotation matrix represented by the quaternion.
     */
    Quaternion.prototype.yAxis = function() {
        var xx = this.x*this.x,
            zz = this.z*this.z,
            xy = this.x*this.y,
            xw = this.x*this.w,
            yz = this.y*this.z,
            zw = this.z*this.w;
        return new Vec3(
            2*xy - 2*zw,
            1 - 2*xx - 2*zz,
            2*yz + 2*xw ).normalize();
    };

    /**
     * Returns the z-axis of the rotation matrix that the quaternion represents.
     * @memberof Quaternion
     *
     * @returns {Vec3} The z-axis of the rotation matrix represented by the quaternion.
     */
    Quaternion.prototype.zAxis = function() {
        var xx = this.x*this.x,
            yy = this.y*this.y,
            xz = this.x*this.z,
            xw = this.x*this.w,
            yz = this.y*this.z,
            yw = this.y*this.w;
        return new Vec3(
            2*xz + 2*yw,
            2*yz - 2*xw,
            1 - 2*xx - 2*yy ).normalize();
    };

    /**
     * Returns the axes of the rotation matrix that the quaternion represents.
     * @memberof Quaternion
     *
     * @returns {Object} The axes of the matrix represented by the quaternion.
     */
    Quaternion.prototype.axes = function() {
        var xx = this.x*this.x,
            yy = this.y*this.y,
            zz = this.z*this.z,
            xy = this.x*this.y,
            xz = this.x*this.z,
            xw = this.x*this.w,
            yz = this.y*this.z,
            yw = this.y*this.w,
            zw = this.z*this.w;
        return {
            x: new Vec3( 1 - 2*yy - 2*zz, 2*xy + 2*zw, 2*xz - 2*yw ),
            y: new Vec3( 2*xy - 2*zw, 1 - 2*xx - 2*zz, 2*yz + 2*xw ),
            z: new Vec3( 2*xz + 2*yw, 2*yz - 2*xw, 1 - 2*xx - 2*yy )
        };
    };

    /**
     * Returns the rotation matrix that the quaternion represents.
     * @memberof Quaternion
     *
     * @returns {Mat33} The rotation matrix represented by the quaternion.
     */
    Quaternion.prototype.matrix = function() {
        var xx = this.x*this.x,
            yy = this.y*this.y,
            zz = this.z*this.z,
            xy = this.x*this.y,
            xz = this.x*this.z,
            xw = this.x*this.w,
            yz = this.y*this.z,
            yw = this.y*this.w,
            zw = this.z*this.w;
        return new Mat33([
            1 - 2*yy - 2*zz, 2*xy + 2*zw, 2*xz - 2*yw,
            2*xy - 2*zw, 1 - 2*xx - 2*zz, 2*yz + 2*xw,
            2*xz + 2*yw, 2*yz - 2*xw, 1 - 2*xx - 2*yy ]);
    };

    /**
     * Returns a quaternion representing the rotation defined by an axis
     * and an angle.
     * @memberof Quaternion
     *
     * @param {number} angle - The angle of the rotation, in radians.
     * @param {Vec3|Array} axis - The axis of the rotation.
     *
     * @returns {Quaternion} The quaternion representing the rotation.
     */
    Quaternion.rotation = function( angle, axis ) {
        if ( axis instanceof Array ) {
            axis = new Vec3( axis );
        }
        // normalize arguments
        axis = axis.normalize();
        // set quaternion for the equivolent rotation
        var modAngle = ( angle > 0 ) ? angle % (2*Math.PI) : angle % (-2*Math.PI),
            sina = Math.sin( modAngle/2 ),
            cosa = Math.cos( modAngle/2 );
        return new Quaternion(
            cosa,
            axis.x * sina,
            axis.y * sina,
            axis.z * sina ).normalize();
    };

    /**
     * Returns a rotation matrix to rotate a vector from one direction to
     * another.
     * @memberof Quaternion
     *
     * @param {Vec3} from - The starting direction.
     * @param {Vec3} to - The ending direction.
     *
     * @returns {Quaternion} The quaternion representing the rotation.
     */
    Quaternion.rotationFromTo = function( fromVec, toVec ) {
        fromVec = new Vec3( fromVec );
        toVec = new Vec3( toVec );
        var cross = fromVec.cross( toVec );
        var dot = fromVec.dot( toVec );
        var fLen = fromVec.length();
        var tLen = toVec.length();
        var w = Math.sqrt( ( fLen * fLen ) * ( tLen * tLen ) ) + dot;
        return new Quaternion( w, cross.x, cross.y, cross.z ).normalize();
    };

    /**
     * Returns a quaternion that has been spherically interpolated between
     * two provided quaternions for a given t value.
     * @memberof Quaternion
     *
     * @param {Quaternion} fromRot - The rotation at t = 0.
     * @param {Quaternion} toRot - The rotation at t = 1.
     * @param {number} t - The t value, from 0 to 1.
     *
     * @returns {Quaternion} The quaternion representing the interpolated rotation.
     */
    Quaternion.slerp = function( fromRot, toRot, t ) {
        if ( fromRot instanceof Array ) {
            fromRot = new Quaternion( fromRot );
        }
        if ( toRot instanceof Array ) {
            toRot = new Quaternion( toRot );
        }
        // calculate angle between
        var cosHalfTheta = ( fromRot.w * toRot.w ) +
            ( fromRot.x * toRot.x ) +
            ( fromRot.y * toRot.y ) +
            ( fromRot.z * toRot.z );
        // if fromRot=toRot or fromRot=-toRot then theta = 0 and we can return from
        if ( Math.abs( cosHalfTheta ) >= 1 ) {
            return new Quaternion(
                fromRot.w,
                fromRot.x,
                fromRot.y,
                fromRot.z );
        }
        // cosHalfTheta must be positive to return the shortest angle
        if ( cosHalfTheta < 0 ) {
            fromRot = fromRot.negate();
            cosHalfTheta = -cosHalfTheta;
        }
        var halfTheta = Math.acos( cosHalfTheta );
        var sinHalfTheta = Math.sqrt( 1 - cosHalfTheta * cosHalfTheta );
        var scaleFrom = Math.sin( ( 1.0 - t ) * halfTheta ) / sinHalfTheta;
        var scaleTo = Math.sin( t * halfTheta ) / sinHalfTheta;
        return new Quaternion(
            fromRot.w * scaleFrom + toRot.w * scaleTo,
            fromRot.x * scaleFrom + toRot.x * scaleTo,
            fromRot.y * scaleFrom + toRot.y * scaleTo,
            fromRot.z * scaleFrom + toRot.z * scaleTo );
    };

    /**
     * Returns true if the vector components match those of a provided vector.
     * An optional epsilon value may be provided.
     * @memberof Quaternion
     *
     * @param {Quaternion|Array} - The vector to calculate the dot product with.
     * @param {number} - The epsilon value. Optional.
     *
     * @returns {boolean} Whether or not the vector components match.
     */
    Quaternion.prototype.equals = function( that, epsilon ) {
        var w = that.w !== undefined ? that.w : that[0],
            x = that.x !== undefined ? that.x : that[1],
            y = that.y !== undefined ? that.y : that[2],
            z = that.z !== undefined ? that.z : that[3];
        epsilon = epsilon === undefined ? 0 : epsilon;
        return ( this.w === w || Math.abs( this.w - w ) <= epsilon ) &&
            ( this.x === x || Math.abs( this.x - x ) <= epsilon ) &&
            ( this.y === y || Math.abs( this.y - y ) <= epsilon ) &&
            ( this.z === z || Math.abs( this.z - z ) <= epsilon );
    };

    /**
     * Returns a new Quaternion of unit length.
     * @memberof Quaternion
     *
     * @returns {Quaternion} The quaternion of unit length.
     */
    Quaternion.prototype.normalize = function() {
        var mag = Math.sqrt(
                this.x*this.x +
                this.y*this.y +
                this.z*this.z +
                this.w*this.w );
        if ( mag !== 0 ) {
            return new Quaternion(
                this.w / mag,
                this.x / mag,
                this.y / mag,
                this.z / mag );
        }
        return new Quaternion();
    };

    /**
     * Returns the conjugate of the quaternion.
     * @memberof Quaternion
     *
     * @returns {Quaternion} The conjugate of the quaternion.
     */
    Quaternion.prototype.conjugate = function() {
         return new Quaternion( this.w, -this.x, -this.y, -this.z );
    };

    /**
     * Returns the inverse of the quaternion.
     * @memberof Quaternion
     *
     * @returns {Quaternion} The inverse of the quaternion.
     */
    Quaternion.prototype.inverse = function() {
        return this.conjugate();
    };

    /**
     * Returns a random Quaternion of unit length.
     * @memberof Quaternion
     *
     * @returns {Quaternion} A random vector of unit length.
     */
    Quaternion.random = function() {
        var axis = Vec3.random().normalize(),
            angle = Math.random();
        return Quaternion.rotation( angle, axis );
    };

    /**
     * Returns a string representation of the quaternion.
     * @memberof Quaternion
     *
     * @returns {String} The string representation of the quaternion.
     */
    Quaternion.prototype.toString = function() {
        return this.x + ', ' + this.y + ', ' + this.z + ', ' + this.w;
    };

    /**
     * Returns an array representation of the quaternion.
     * @memberof Quaternion
     *
     * @returns {Array} The quaternion as an array.
     */
    Quaternion.prototype.toArray = function() {
        return [  this.w, this.x, this.y, this.z ];
    };

    /**
     * Decomposes the matrix into the corresponding rotation, and scale components.
     * a scale.
     * @memberof Mat33
     *
     * @returns {Object} The decomposed components of the matrix.
     */
    Mat33.prototype.decompose = function() {
        // axis vectors
        var x = new Vec3( this.data[0], this.data[1], this.data[2] );
        var y = new Vec3( this.data[3], this.data[4], this.data[5] );
        var z = new Vec3( this.data[6], this.data[7], this.data[8] );
        // scale needs unnormalized vectors
        var scale = new Vec3( x.length(), y.length(), z.length() );
        // rotation needs normalized vectors
        x = x.normalize();
        y = y.normalize();
        z = z.normalize();
        var trace = x.x + y.y + z.z;
        var s;
        var rotation;
        if ( trace > 0 ) {
            s = 0.5 / Math.sqrt( trace + 1.0 );
            rotation = new Quaternion(
                0.25 / s,
                ( y.z - z.y ) * s,
                ( z.x - x.z ) * s,
                ( x.y - y.x ) * s );
        } else if ( x.x > y.y && x.x > z.z ) {
            s = 2.0 * Math.sqrt( 1.0 + x.x - y.y - z.z );
            rotation = new Quaternion(
                ( y.z - z.y ) / s,
                0.25 * s,
                ( y.x + x.y ) / s,
                ( z.x + x.z ) / s );
        } else if ( y.y > z.z ) {
            s = 2.0 * Math.sqrt( 1.0 + y.y - x.x - z.z );
            rotation = new Quaternion(
                ( z.x - x.z ) / s,
                ( y.x + x.y ) / s,
                0.25 * s,
                ( z.y + y.z ) / s );
        } else {
            s = 2.0 * Math.sqrt( 1.0 + z.z - x.x - y.y );
            rotation = new Quaternion(
                ( x.y - y.x ) / s,
                ( z.x + x.z ) / s,
                ( z.y + y.z ) / s,
                0.25 * s );
        }
        return {
            rotation: rotation,
            scale: scale
        };
    };

    /**
     * Decomposes the matrix into the corresponding rotation, translation, and scale components.
     * @memberof Mat44
     *
     * @returns {Object} The decomposed components of the matrix.
     */
    Mat44.prototype.decompose = function() {
        // translation
        var translation = new Vec3( this.data[12], this.data[13], this.data[14] );
        // axis vectors
        var x = new Vec3( this.data[0], this.data[1], this.data[2] );
        var y = new Vec3( this.data[4], this.data[5], this.data[6] );
        var z = new Vec3( this.data[8], this.data[9], this.data[10] );
        // scale needs unnormalized vectors
        var scale = new Vec3( x.length(), y.length(), z.length() );
        // rotation needs normalized vectors
        x = x.normalize();
        y = y.normalize();
        z = z.normalize();
        var trace = x.x + y.y + z.z;
        var s;
        var rotation;
        if ( trace > 0 ) {
            s = 0.5 / Math.sqrt( trace + 1.0 );
            rotation = new Quaternion(
                0.25 / s,
                ( y.z - z.y ) * s,
                ( z.x - x.z ) * s,
                ( x.y - y.x ) * s );
        } else if ( x.x > y.y && x.x > z.z ) {
            s = 2.0 * Math.sqrt( 1.0 + x.x - y.y - z.z );
            rotation = new Quaternion(
                ( y.z - z.y ) / s,
                0.25 * s,
                ( y.x + x.y ) / s,
                ( z.x + x.z ) / s );
        } else if ( y.y > z.z ) {
            s = 2.0 * Math.sqrt( 1.0 + y.y - x.x - z.z );
            rotation = new Quaternion(
                ( z.x - x.z ) / s,
                ( y.x + x.y ) / s,
                0.25 * s,
                ( z.y + y.z ) / s );
        } else {
            s = 2.0 * Math.sqrt( 1.0 + z.z - x.x - y.y );
            rotation = new Quaternion(
                ( x.y - y.x ) / s,
                ( z.x + x.z ) / s,
                ( z.y + y.z ) / s,
                0.25 * s );
        }
        return {
            rotation: rotation,
            scale: scale,
            translation: translation
        };
    };

    module.exports = Quaternion;

}());


'use strict'

/* Dependencies. */
var assert = require('assert')
var array = require('x-is-array')
var object = require('x-is-object')

var inspect

try {
  // eslint-disable-next-line import/no-dynamic-require, no-useless-concat
  inspect = require('ut' + 'il').inspect
} catch (error) {
  /* Empty. */
}

/* Expose. */
exports = wrap(unist)
module.exports = exports

exports.parent = wrap(parent)
exports.text = wrap(text)
exports.void = wrap(empty)
exports.wrap = wrap

/* Identifier to check if a value is seen. */
var ID = '__unist__'

/* List of specced properties. */
var defined = ['type', 'value', 'children', 'position']

/* Wrapper around `Node` which adds the current node
 * (and parent, if available), to the message. */
function wrap(fn) {
  return wrapped

  function wrapped(node, parent) {
    try {
      fn(node, parent)
    } catch (error) {
      if (!error[ID]) {
        error[ID] = true

        error.message += ': `' + view(node) + '`'

        if (parent) {
          error.message += ' in `' + view(parent) + '`'
        }
      }

      throw error
    }
  }
}

/* Assert. */
function unist(node) {
  var type
  var children
  var value
  var key
  var index
  var length

  assert.ok(object(node), 'node should be an object')

  type = node.type
  children = node.children
  value = node.value

  assert.ok('type' in node, 'node should have a type')
  assert.strictEqual(typeof type, 'string', '`type` should be a string')
  assert.notStrictEqual(type, '', '`type` should not be empty')

  if (value != null) {
    assert.strictEqual(typeof value, 'string', '`value` should be a string')
  }

  location(node.position)

  for (key in node) {
    if (defined.indexOf(key) === -1) {
      vanilla(key, node[key])
    }
  }

  if (children != null) {
    assert.ok(array(children), '`children` should be an array')
    index = -1
    length = children.length

    while (++index < length) {
      exports(children[index], node)
    }
  }
}

/* Assert `value` (which lives at `key`) can be stringified
 * and re-parsed to the same (deep) value. */
function vanilla(key, value) {
  try {
    assert.deepStrictEqual(value, JSON.parse(JSON.stringify(value)))
  } catch (error) {
    assert.fail('non-specced property `' + key + '` should be JSON')
  }
}

/* Stringify a value to inspect it.  Tries `JSON.stringify()`,
 * and if that fails uses `String()` instead.  If `stringify()`
 * works. */
function view(value) {
  try {
    /* eslint-disable no-else-return */
    /* istanbul ignore else - Browser. */
    if (inspect) {
      return inspect(value, {colors: false})
    } else {
      return JSON.stringify(value)
    }
  } catch (error) {
    /* istanbul ignore next - Cyclical. */
    return String(value)
  }
}

/* Assert `node` is a parent node. */
function parent(node) {
  unist(node)

  assert.strictEqual('value' in node, false, 'parent should not have `value`')
  assert.ok('children' in node, 'parent should have `children`')
}

/* Assert `node` is a text node. */
function text(node) {
  unist(node)

  assert.strictEqual(
    'children' in node,
    false,
    'text should not have `children`'
  )
  assert.ok('value' in node, 'text should have `value`')
}

/* Assert `node` is a Unist node, but neither parent nor
 * text. */
function empty(node) {
  unist(node)

  assert.strictEqual('value' in node, false, 'void should not have `value`')
  assert.strictEqual(
    'children' in node,
    false,
    'void should not have `children`'
  )
}

/* Assert `location` is a Unist Location. */
function location(location) {
  if (location != null) {
    assert.ok(object(location), '`position` should be an object')

    position(location.start, 'position.start')
    position(location.end, 'position.end')
  }
}

/* Assert `location` is a Unist Location. */
function position(position, name) {
  if (position != null) {
    assert.ok(object(position), '`' + name + '` should be an object')

    if (position.line != null) {
      assert.ok('line' in position, '`' + name + '` should have numeric `line`')
      assert.ok(position.line >= 1, '`' + name + '.line` should be gte `1`')
    }

    if (position.column != null) {
      assert.ok(
        'column' in position,
        '`' + name + '` should have numeric `column`'
      )
      assert.ok(position.column >= 1, '`' + name + '.column` should be gte `1`')
    }
  }
}


(function() {

    'use strict';

    var EPSILON = require('./Epsilon');

    /**
     * Instantiates a Vec3 object.
     * @class Vec3
     * @classdesc A three component vector.
     */
    function Vec3() {
        switch ( arguments.length ) {
            case 1:
                // array or VecN argument
                var argument = arguments[0];
                this.x = argument.x || argument[0] || 0.0;
                this.y = argument.y || argument[1] || 0.0;
                this.z = argument.z || argument[2] || 0.0;
                break;
            case 3:
                // individual component arguments
                this.x = arguments[0];
                this.y = arguments[1];
                this.z = arguments[2];
                break;
            default:
                this.x = 0.0;
                this.y = 0.0;
                this.z = 0.0;
                break;
        }
    }

    /**
     * Returns a new Vec3 with each component negated.
     * @memberof Vec3
     *
     * @returns {Vec3} The negated vector.
     */
    Vec3.prototype.negate = function() {
        return new Vec3( -this.x, -this.y, -this.z );
    };

    /**
     * Adds the vector with the provided vector argument, returning a new Vec3
     * object representing the sum.
     * @memberof Vec3
     *
     * @param {Vec3|Vec4|Array} that - The vector to add.
     *
     * @returns {Vec3} The sum of the two vectors.
     */
    Vec3.prototype.add = function( that ) {
        if ( that instanceof Array ) {
            return new Vec3( this.x + that[0], this.y + that[1], this.z + that[2] );
        }
        return new Vec3( this.x + that.x, this.y + that.y, this.z + that.z );
    };

    /**
     * Subtracts the provided vector argument from the vector, returning a new
     * Vec3 object representing the difference.
     * @memberof Vec3
     *
     * @param {Vec3|Vec4|Array} - The vector to subtract.
     *
     * @returns {Vec3} The difference of the two vectors.
     */
    Vec3.prototype.sub = function( that ) {
        if ( that instanceof Array ) {
            return new Vec3( this.x - that[0], this.y - that[1], this.z - that[2] );
        }
        return new Vec3( this.x - that.x, this.y - that.y, this.z - that.z );
    };

    /**
     * Multiplies the vector with the provided scalar argument, returning a new Vec3
     * object representing the scaled vector.
     * @memberof Vec3
     *
     * @param {number} - The scalar to multiply the vector by.
     *
     * @returns {Vec3} The scaled vector.
     */
    Vec3.prototype.multScalar = function( that ) {
        return new Vec3( this.x * that, this.y * that, this.z * that );
    };

    /**
     * Divides the vector with the provided scalar argument, returning a new Vec3
     * object representing the scaled vector.
     * @memberof Vec3
     *
     * @param {number} - The scalar to divide the vector by.
     *
     * @returns {Vec3} The scaled vector.
     */
    Vec3.prototype.divScalar = function( that ) {
        return new Vec3( this.x / that, this.y / that, this.z / that );
    };

    /**
     * Calculates and returns the dot product of the vector and the provided
     * vector argument.
     * @memberof Vec3
     *
     * @param {Vec3|Vec4|Array} - The other vector argument.
     *
     * @returns {number} The dot product.
     */
    Vec3.prototype.dot = function( that ) {
        if ( that instanceof Array ) {
            return ( this.x * that[0] ) + ( this.y * that[1] ) + ( this.z * that[2] );
        }
        return ( this.x * that.x ) + ( this.y * that.y ) + ( this.z * that.z );
    };

    /**
     * Calculates and returns the cross product of the vector and the provided
     * vector argument.
     * @memberof Vec3
     *
     * @param {Vec3|Vec4|Array} - The other vector argument.
     *
     * @returns {number} The 2D cross product.
     */
    Vec3.prototype.cross = function( that ) {
        if ( that instanceof Array ) {
            return new Vec3(
                ( this.y * that[2] ) - ( that[1] * this.z ),
                (-this.x * that[2] ) + ( that[0] * this.z ),
                ( this.x * that[1] ) - ( that[0] * this.y ) );
        }
        return new Vec3(
            ( this.y * that.z ) - ( that.y * this.z ),
            (-this.x * that.z ) + ( that.x * this.z ),
            ( this.x * that.y ) - ( that.x * this.y ) );
    };

    /**
     * If no argument is provided, this function returns the scalar length of
     * the vector. If an argument is provided, this method will return a new
     * Vec3 scaled to the provided length.
     * @memberof Vec3
     *
     * @param {number} - The length to scale the vector to. Optional.
     *
     * @returns {number|Vec3} Either the length, or new scaled vector.
     */
    Vec3.prototype.length = function( length ) {
        if ( length === undefined ) {
            var len = this.dot( this );
            if ( Math.abs( len - 1.0 ) < EPSILON ) {
                return len;
            } else {
                return Math.sqrt( len );
            }
        }
        return this.normalize().multScalar( length );
    };

    /**
     * Returns the squared length of the vector.
     * @memberof Vec3
     *
     * @returns {number} The squared length of the vector.
     */
    Vec3.prototype.lengthSquared = function() {
        return this.dot( this );
    };

    /**
     * Returns true if the vector components match those of a provided vector.
     * An optional epsilon value may be provided.
     * @memberof Vec3
     *
     * @param {Vec3|Vec4|Array} that - The vector to test equality with.
     * @param {number} epsilon - The epsilon value. Optional.
     *
     * @returns {boolean} Whether or not the vector components match.
     */
    Vec3.prototype.equals = function( that, epsilon ) {
        var x = that.x !== undefined ? that.x : that[0],
            y = that.y !== undefined ? that.y : that[1],
            z = that.z !== undefined ? that.z : that[2];
        epsilon = epsilon === undefined ? 0 : epsilon;
        return ( this.x === x || Math.abs( this.x - x ) <= epsilon ) &&
            ( this.y === y || Math.abs( this.y - y ) <= epsilon ) &&
            ( this.z === z || Math.abs( this.z - z ) <= epsilon );
    };

    /**
     * Returns a new Vec3 of unit length.
     * @memberof Vec3
     *
     * @returns {Vec3} The vector of unit length.
     */
    Vec3.prototype.normalize = function() {
        var mag = this.length();
        if ( mag === 0 ) {
            throw 'Cannot normalize a vector of zero length';
        }
        return new Vec3(
            this.x / mag,
            this.y / mag,
            this.z / mag );
    };

    /**
     * Given a plane normal, returns the projection of the vector onto the plane.
     * @memberof Vec3
     *
     * @param {Vec3|Vec4|Array} normal - The plane normal.
     *
     * @returns {number} The unsigned angle in radians.
     */
    Vec3.prototype.projectOntoPlane =  function( n ) {
        var dist = this.dot( n );
        return this.sub( n.multScalar( dist ) );
    };

    /**
     * Returns the unsigned angle between this angle and the argument, projected
     * onto a plane, in radians.
     * @memberof Vec3
     *
     * @param {Vec3|Vec4|Array} that - The vector to measure the angle from.
     * @param {Vec3|Vec4|Array} normal - The reference vector to measure the
     *                              direction of the angle. If not provided will
     *                              use a.cross( b ). (Optional)
     *
     * @returns {number} The unsigned angle in radians.
     */
    Vec3.prototype.unsignedAngle = function( that, normal ) {
        var a = this;
        var b = new Vec3( that );
        var cross = a.cross( b );
        var n = new Vec3( normal || cross );
        var pa = a.projectOntoPlane( n ).normalize();
        var pb = b.projectOntoPlane( n ).normalize();
        var dot = pa.dot( pb );

        // faster, less robuest
        //var ndot = Math.max( -1, Math.min( 1, dot ) );
        //var angle = Math.acos( ndot );

        // slower, but more robust
        var angle = Math.atan2( pa.cross( pb ).length(), dot );

        if ( n.dot( cross ) < 0 ) {
            if ( angle >= Math.PI * 0.5 ) {
                angle = Math.PI + Math.PI - angle;
            } else {
                angle = 2 * Math.PI - angle;
            }
        }
        return angle;
    };

    /**
     * Returns a random Vec3 of unit length.
     * @memberof Vec3
     *
     * @returns {Vec3} A random vector of unit length.
     */
    Vec3.random = function() {
        return new Vec3(
            Math.random(),
            Math.random(),
            Math.random() ).normalize();
    };

    /**
     * Returns a string representation of the vector.
     * @memberof Vec3
     *
     * @returns {String} The string representation of the vector.
     */
    Vec3.prototype.toString = function() {
        return this.x + ', ' + this.y + ', ' + this.z;
    };

    /**
     * Returns an array representation of the vector.
     * @memberof Vec3
     *
     * @returns {Array} The vector as an array.
     */
    Vec3.prototype.toArray = function() {
        return [ this.x, this.y, this.z ];
    };

    module.exports = Vec3;

}());


/**
 * Constructor
 *
 * @param {[type]}   data     [description]
 * @param {[type]}   options  [description]
 * @param {Function} callback [description]
 */
function Snackbar(data, options, callback){
	if (data !== "") {
		this.options = this.activateOptions(options);
		this.data = data;
		this.callback = callback;
		this.start();
		this.snackbar();
	} else {
		console.warn("SnackbarLight: You can not create a empty snackbar please give it a string.");
	}
}

Snackbar.prototype = {

	/**
	 * Default options
	 *
	 * @type {Object}
	 */
	options: {
		// How long it takes for the snackbar to disappear
		timeout: 5000,
		// Wich class is used to tell that the snackbar is active
		activeClass: "active",
		// Name of the link or action if specified
		link: false,
		// If not used clicking will activate the callback or do nothing
		url: "#",
	},

	/**
	 * Create container for the snackbar
	 *
	 * @return {void}
	 */
	start: function() {
		if (!document.getElementById("snackbar-container")) {
			var snackbarContainer = document.createElement("div");

			snackbarContainer.setAttribute("id", "snackbar-container");

			document.body.appendChild(snackbarContainer);
		}
	},

	/**
	 * Timer
	 *
	 * @param  {Function} callback
	 * @param  {int}   delay
	 * @return {void}
	 */
	timer: function(callback, delay) {
	    var remaining = delay;

	    this.timer = {
	    	// Create random timer id
	    	timerId: Math.round(Math.random()*1000),

		    pause: function() {
		        // Clear the timeout
		        window.clearTimeout(this.timerId);
		        // Set the remaining to what time remains
		        remaining -= new Date() - start;
		    },

		    resume: function() {
		        start = new Date();
		        // Clear the timeout
		        window.clearTimeout(this.timerId);
		        // Set the timeout again
		        this.timerId = window.setTimeout(callback, remaining);
		    },
	    };
	    // Start the timer
	    this.timer.resume();
	},

	/**
	 * snackbar
	 *
	 * @return {void}
	 */
	snackbar: function() {
		var __self = this,
			snackbar = document.createElement("div");

		// Put the snackbar inside the snackbar container
		document.getElementById("snackbar-container").appendChild(snackbar);

	  	// Set the html inside the snackbar
	  	snackbar.innerHTML = this.getData();

		// Set the class of the snackbar
		snackbar.setAttribute("class", "snackbar");

		// Wait to set the active class so animations will be activated
		setTimeout(function() {
			snackbar.setAttribute("class","snackbar " + __self.options.activeClass);
		}, 50);

		// If the timeout is false the snackbar will not be destroyed after some time
		// only when the user clicks on it
		if (this.options.timeout !== false) {
			// Start the timer
			this.timer(function() {
				snackbar.setAttribute("class", "snackbar");
				__self.destroy(snackbar);
			}, this.options.timeout);
		}

		// Add the event listeners
		this.listeners(snackbar);
	},

	/**
	 * Get the data/ message to display in the snackbar
	 *
	 * @return {string}
	 */
	getData: function() {
		if (this.options.link !== false) {
			return "" + this.data + "" + this.options.link + "";
		}
		return "" + this.data + "";
	},

	/**
	 * Activate the listeners
	 *
	 * @param  {Object} element
	 * @return {void}
	 */
	listeners: function(element) {
		var __self = this;
		// Adding event listener for when user clicks on the snackbar to remove it
		element.addEventListener("click", function(){
			if (typeof __self.callback == "function") {
				__self.callback();
			}
			element.setAttribute("class", "snackbar");
	    	__self.destroy(element);
		});

		// Stopping the timer when user hovers on the snackbar
		element.addEventListener("mouseenter",function(){
			__self.timer.pause();
		});
		element.addEventListener("mouseout",function(){
			__self.timer.resume();
		});
	},

	/**
	 * Remove the snackbar
	 *
	 * @param  {object} element
	 * @return {void}
	 */
	destroy: function(element) {
		// Delay for removing the element (for when there are animations)
		this.timer.pause();
		setTimeout(function() {
			element.remove();
		}, 10000);
	},

	/**
	 * Compare the options to the default ones.
	 *
	 * @param  {Object} newOptions
	 * @return {Object}
	 */
	activateOptions: function(newOptions) {
		var __self = this,
  			options = newOptions || {};

    	for (var opt in this.options) {
        	if (__self.options.hasOwnProperty(opt) && !options.hasOwnProperty(opt)) {
            	options[opt] = __self.options[opt];
        	}
        }
       	return options;
	},
};

///////////////////////////////
// Vuejs/ browserify support //
///////////////////////////////
SnackbarLight = {
	/**
	 * Install function for Vue
	 *
	 * @param  {Object} Vue
	 * @return {void}
	 */
	install: function(Vue){
		var __self = this;
		Vue.prototype.$snackbar = {};
	  	Vue.prototype.$snackbar.create = function(data, options, callback){
	  		__self.create(data, options, callback);
	  	};
	},

	/**
	 * Create a new snackbar
	 *
	 * @param  {string}   data
	 * @param  {Object}   options
	 * @param  {Function} callback
	 * @return {void}
	 */
	create: function(data, options, callback){
		new Snackbar(data, options, callback);
	}
};

if (typeof exports == "object") {
	// Export
    module.exports = SnackbarLight;
} else if (typeof define == "function" && define.amd) {
	define([], function(){ return SnackbarLight });
} else if (window.Vue) {
	// Vue use if vue is being used on the page
	Vue.use(SnackbarLight);
}

/////////////////
// Data-toggle //
/////////////////

// Search all elements for the data toggle the snackbar
var elements = document.querySelectorAll("[data-toggle=snackbar]");

// Loop them and add event listeners to them
for (var i = elements.length - 1; i >= 0; i--) {
	elements[i].addEventListener("click", function(){
		var options = {};

		if (this.getAttribute("data-link") !== null) {
			options.link = this.getAttribute("data-link");
		}
		if (this.getAttribute("data-timeout") !== null) {
			options.timeout = this.getAttribute("data-timeout");
		}
		if (this.getAttribute("data-activeClass") !== null) {
			options.activeClass = this.getAttribute("data-active-class");
		}
		if (this.getAttribute("data-url")) {
			options.url = this.getAttribute("data-url");
		}

		new Snackbar(this.getAttribute("data-content"), options);
	});
}


'use strict';

var bs58check = require('bs58check');
var secp256k1 = require('secp256k1');
var zbufferutils = require('./bufferutils');
var zcrypto = require('./crypto');
var zopcodes = require('./opcodes');
var zconfig = require('./config');

/*
 * Makes a private key
 * @param {String phrase (Password phrase)
 * @return {Sting} Private key
 */
function mkPrivKey(phrase) {
  return zcrypto.sha256(Buffer.from(phrase, 'utf-8'));
}

/*
 * Converts a private key to WIF format
 * @param {String} privKey (private key)
 * @param {boolean} toCompressed (Convert to WIF compressed key or nah)
 * @param {String} wif (wif hashing bytes (default: 0x80))
 * @return {Sting} WIF format (uncompressed)
 */
function privKeyToWIF(privKey) {
  var toCompressed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
  var wif = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : zconfig.mainnet.wif;

  if (toCompressed) privKey = privKey + '01';

  return bs58check.encode(Buffer.from(wif + privKey, 'hex'));
}

/*
 * Returns private key's public Key
 * @param {String} privKey (private key)
 * @param {boolean} toCompressed (Convert to public key compressed key or nah)
 * @return {Sting} Public Key (default: uncompressed)
 */
function privKeyToPubKey(privKey) {
  var toCompressed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;

  var pkBuffer = Buffer.from(privKey, 'hex');
  var publicKey = secp256k1.publicKeyCreate(pkBuffer, toCompressed);
  return publicKey.toString('hex');
}

/*
 * Given a WIF format pk, convert it back to the original pk
 * @param {String} privKey (private key)
 * @return {Sting} Public Key (uncompressed)
 */
function WIFToPrivKey(wifPk) {
  var og = bs58check.decode(wifPk, 'hex').toString('hex');
  og = og.substr(2, og.length); // remove WIF format ('80')

  // remove the '01' at the end to 'compress it' during WIF conversion
  if (og.length > 64) {
    og = og.substr(0, 64);
  }

  return og;
}

/*
 * Converts public key to zencash address
 * @param {String} pubKey (public key)
 * @param {String} pubKeyHash (public key hash (optional, else use defaul))
 * @return {String} zencash address
 */
function pubKeyToAddr(pubKey) {
  var pubKeyHash = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : zconfig.mainnet.pubKeyHash;

  var hash160 = zcrypto.hash160(Buffer.from(pubKey, 'hex'));
  return bs58check.encode(Buffer.from(pubKeyHash + hash160, 'hex')).toString('hex');
}

/*
 * Given a list of public keys, create a M-of-N redeemscript
 * @param {[String]} pubKey (array of public keys, NOT ADDRESS)
 * @param {Int} M [2 or 3] in M-of-N multisig
 * @param {Int} N [3 or 4] in M-of-N multisig
 * @return {String} RedeemScript
 */
function mkMultiSigRedeemScript(pubKeys, M, N) {
  // https://github.com/ZencashOfficial/zen/blob/b7a7c4c4199f5e9f49868631fe5f2f6de6ba4f9a/src/script/standard.cpp#L411
  if (M > N && M <= 1) throw new Error('Invalid Multi Sig Type');
  var OP_1 = Buffer.from(zopcodes.OP_1, 'hex');
  var OP_START = (OP_1.readInt8(0) + (M - 1)).toString(16);
  var OP_END = (OP_1.readInt8(0) + (N - 1)).toString(16);

  return OP_START + pubKeys.map(function (x) {
    return zbufferutils.getPushDataLength(x) + x;
  }).join('') + OP_END + zopcodes.OP_CHECKMULTISIG;
}

/*
 * Reference: http://www.soroushjp.com/2014/12/20/bitcoin-multisig-the-hard-way-understanding-raw-multisignature-bitcoin-transactions/
 * Given the multi sig redeem script, return the corresponding address
 * @param {String} RedeemScript (redeem script)
 * @return {String} Address
 */
function multiSigRSToAddress(redeemScript) {
  var scriptHash = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : zconfig.mainnet.scriptHash;

  // Protocol: RIPEMD160(SHA256(script))
  var s256 = zcrypto.sha256(Buffer.from(redeemScript, 'hex'));
  var r160 = zcrypto.ripemd160(Buffer.from(s256, 'hex'));
  return bs58check.encode(Buffer.from(scriptHash + r160, 'hex'));
}

module.exports = {
  mkPrivKey: mkPrivKey,
  privKeyToWIF: privKeyToWIF,
  privKeyToPubKey: privKeyToPubKey,
  pubKeyToAddr: pubKeyToAddr,
  WIFToPrivKey: WIFToPrivKey,
  mkMultiSigRedeemScript: mkMultiSigRedeemScript,
  multiSigRSToAddress: multiSigRSToAddress
};


(function() {

    'use strict';

    var Vec3 = require('./Vec3');
    var Mat44 = require('./Mat44');
    var Quaternion = require('./Quaternion');

    /**
     * Instantiates a Transform object.
     * @class Transform
     * @classdesc A transform representing an orientation, position, and scale.
     */
    function Transform( that ) {
        that = that || {};
        if ( that.data instanceof Array ) {
            // Mat33 or Mat44, extract transform components
            that = that.decompose();
            this.rotation = that.rotation;
            this.translation = that.translation || new Vec3();
            this.scale = that.scale;
        } else {
            // set individual components, by value
            this.rotation = that.rotation ? new Quaternion( that.rotation ) : new Quaternion();
            this.translation = that.translation ? new Vec3( that.translation ) : new Vec3();
            if ( typeof that.scale === 'number' ) {
                this.scale = new Vec3( that.scale, that.scale, that.scale );
            } else {
                this.scale = that.scale ? new Vec3( that.scale ) : new Vec3( 1, 1, 1 );
            }
        }
    }

    /**
     * Returns an identity transform.
     * @memberof Transform
     *
     * @returns {Transform} An identity transform.
     */
    Transform.identity = function() {
        return new Transform({
            rotation: new Quaternion(),
            translation: new Vec3(),
            scale: new Vec3( 1, 1, 1 )
        });
    };

    /**
     * Rotates the transform such that x-axis matches the provided vector.
     * @memberof Transform
     *
     * @param {Vec3|Array} x - The x-axis to rotate to.
     *
     * @returns {Transform} The transform object, for chaining.
     */
    Transform.prototype.rotateXTo = function( x ) {
        var rot = Quaternion.rotationFromTo( this.xAxis(), x );
        this.rotation = rot.multQuat( this.rotation );
        return this;
    };

    /**
     * Rotates the transform such that y-axis matches the provided vector.
     * @memberof Transform
     *
     * @param {Vec3|Array} y - The y-axis to rotate to.
     *
     * @returns {Transform} The transform object, for chaining.
     */
    Transform.prototype.rotateYTo = function( y ) {
        var rot = Quaternion.rotationFromTo( this.yAxis(), y );
        this.rotation = rot.multQuat( this.rotation );
        return this;
    };

    /**
     * Rotates the transform such that z-axis matches the provided vector.
     * @memberof Transform
     *
     * @param {Vec3|Array} z - The z-axis to rotate to.
     *
     * @returns {Transform} The transform object, for chaining.
     */
    Transform.prototype.rotateZTo = function( z ) {
        var rot = Quaternion.rotationFromTo( this.zAxis(), z );
        this.rotation = rot.multQuat( this.rotation );
        return this;
    };

    /**
     * Returns the x-axis of the transform.
     * @memberof Transform
     *
     * @returns {Vec3} The x-axis of the transform.
     */
    Transform.prototype.xAxis = function() {
        return this.rotation.xAxis();
    };

    /**
     * Returns the y-axis of the transform.
     * @memberof Transform
     *
     * @returns {Vec3} The y-axis of the transform.
     */
    Transform.prototype.yAxis = function() {
        return this.rotation.yAxis();
    };

    /**
     * Returns the z-axis of the transform.
     * @memberof Transform
     *
     * @returns {Vec3} The z-axis of the transform.
     */
    Transform.prototype.zAxis = function() {
        return this.rotation.zAxis();
    };

    /**
     * Returns the axes of the transform.
     * @memberof Transform
     *
     * @returns {Object} The axes of the transform.
     */
    Transform.prototype.axes = function() {
        return this.rotation.axes();
    };

    /**
     * Returns the transform's scale matrix.
     * @memberof Transform
     *
     * @returns {Mat44} The scale matrix.
     */
    Transform.prototype.scaleMatrix = function() {
        return Mat44.scale( this.scale );
    };

    /**
     * Returns the transform's rotation matrix.
     * @memberof Transform
     *
     * @returns {Mat44} The rotation matrix.
     */
    Transform.prototype.rotationMatrix = function() {
        return this.rotation.matrix().toMat44();
    };

    /**
     * Returns the transform's translation matrix.
     * @memberof Transform
     *
     * @returns {Mat44} The translation matrix.
     */
    Transform.prototype.translationMatrix = function() {
        return Mat44.translation( this.translation );
    };

    /**
     * Returns the transform's affine-transformation matrix.
     * @memberof Transform
     *
     * @returns {Mat44} The affine-transformation matrix.
     */
    Transform.prototype.matrix = function() {
        // T * R * S
        return this.translationMatrix()
            .multMat44( this.rotationMatrix() )
            .multMat44( this.scaleMatrix() );
    };

    /**
     * Returns the inverse of the transform's scale matrix.
     * @memberof Transform
     *
     * @returns {Mat44} The inverse scale matrix.
     */
    Transform.prototype.inverseScaleMatrix = function() {
        return Mat44.scale([
            1 / this.scale.x,
            1 / this.scale.y,
            1 / this.scale.z
        ]);
    };

    /**
     * Returns the inverse of the transform's rotation matrix.
     * @memberof Transform
     *
     * @returns {Mat44} The inverse rotation matrix.
     */
    Transform.prototype.inverseRotationMatrix = function() {
        return this.rotation.matrix().inverse().toMat44();
    };

    /**
     * Returns the inverse of the transform's translation matrix.
     * @memberof Transform
     *
     * @returns {Mat44} The inverse translation matrix.
     */
    Transform.prototype.inverseTranslationMatrix = function() {
        return Mat44.translation( this.translation.negate() );
    };

    /**
     * Returns the inverse of the transform's affine-transformation matrix.
     * @memberof Transform
     *
     * @returns {Mat44} The inverse affine-transformation matrix.
     */
    Transform.prototype.inverseMatrix = function() {
        // S^-1 * R^-1 * T^-1
        return this.inverseScaleMatrix()
            .multMat44( this.inverseRotationMatrix() )
            .multMat44( this.inverseTranslationMatrix() );
    };

    /**
     * Returns the transform's view matrix.
     * @memberof Transform
     *
     * @returns {Mat44} The view matrix.
     */
    Transform.prototype.viewMatrix = function() {
        return this.matrix().view();
    };

    /**
     * Translates the transform in world space.
     * @memberof Transform
     *
     * @param {Vec3} translation - The translation vector.
     *
     * @returns {Transform} The transform for chaining.
     */
    Transform.prototype.translateWorld = function( translation ) {
        this.translation = this.translation.add( translation );
        return this;
    };

    /**
     * Translates the transform in local space.
     * @memberof Transform
     *
     * @param {Vec3} translation - The translation vector.
     *
     * @returns {Transform} The transform for chaining.
     */
    Transform.prototype.translateLocal = function( translation ) {
        if ( translation instanceof Array ) {
           translation = new Vec3( translation );
       }
        var axes = this.axes();
        this.translation = this.translation
            .add( axes.x.multScalar( translation.x ) )
            .add( axes.y.multScalar( translation.y ) )
            .add( axes.z.multScalar( translation.z ) );
        return this;
    };

    /**
     * Rotates the transform by an angle around an axis in world space.
     * @memberof Transform
     *
     * @param {number} angle - The angle of the rotation, in radians.
     * @param {Vec3} axis - The axis of the rotation.
     *
     * @returns {Transform} The transform for chaining.
     */
    Transform.prototype.rotateWorld = function( angle, axis ) {
        var rot = Quaternion.rotation( angle, axis );
        this.rotation = rot.multQuat( this.rotation );
        return this;
    };

    /**
     * Rotates the transform by an angle around an axis in local space.
     * @memberof Transform
     *
     * @param {number} angle - The angle of the rotation, in radians.
     * @param {Vec3} axis - The axis of the rotation.
     *
     * @returns {Transform} The transform for chaining.
     */
    Transform.prototype.rotateLocal = function( angle, axis ) {
        return this.rotateWorld( angle, this.rotationMatrix().multVec3( axis ) );
    };

    /**
     * Transforms the vector or matrix argument from the transforms local space
     * to the world space.
     * @memberof Transform
     *
     * @param {Vec3|Vec4} vec - The vector argument to transform.
     *
     * @returns {Transform} The transform for chaining.
     */
    Transform.prototype.localToWorld = function( vec ) {
        return this.matrix().multVec3( vec );
    };

    /**
     * Transforms the vector or matrix argument from world space to the
     * transforms local space.
     * @memberof Transform
     *
     * @param {Vec3|Vec4} vec - The vector argument to transform.
     *
     * @returns {Transform} The transform for chaining.
     */
    Transform.prototype.worldToLocal = function( vec ) {
        return this.inverseMatrix().multVec3( vec );
    };

    /**
     * Returns true if the all components match those of a provided transform.
     * An optional epsilon value may be provided.
     * @memberof Transform
     *
     * @param {Transform} that - The matrix to test equality with.
     * @param {number} epsilon - The epsilon value. Optional.
     *
     * @returns {boolean} Whether or not the transform components match.
     */
    Transform.prototype.equals = function( that, epsilon ) {
        return this.matrix().equals( that.matrix(), epsilon );
    };

    /**
     * Returns a transform with a random translation, orientation, and scale.
     * @memberof Transform
     *
     * @returns {Transform} The random transform.
     */
    Transform.random = function() {
        return new Transform({
            translation: Vec3.random(),
            scale: Vec3.random(),
        }).rotateXTo( Vec3.random() );
    };

    /**
     * Returns a string representation of the transform.
     * @memberof Transform
     *
     * @returns {String} The string representation of the transform.
     */
    Transform.prototype.toString = function() {
        return this.matrix().toString();
    };

    module.exports = Transform;

}());


/**
 * 作者: bullub
 * 日期: 16/10/14 15:18
 * 用途:
 */
"use strict";

const gutil = require("gulp-util");
const through2 = require("through2");
const path = require("path");
const File = require("vinyl");
const HTMLParser = require("htmlparser2");

const PluginError = gutil.PluginError;

const SCRIPT = "script";
const STYLE = "style";
const TEMPLATE = "template";

const TEMPLATE_ESCAPE_REG = /'/mg
const TEMPLATE_ESCAPE_REG2 = /\r?\n/mg;
const SCRIPT_REPLACER_REG = /^\s*export\s+default\s*/im;
const VUE_COMPONENT_IMPORT_REG = /^\s*import\s+([^\s]+)\s+from\s+([^;\n]+)[\s;]+?$/mg;

module.exports = function () {
    return through2.obj(vuePack);
};

/**
 * 打包组件成js和css文件
 * @param file
 * @param encoding
 * @param callback
 */
function vuePack(file, encoding, callback) {
    if (!file) {
        throw new PluginError('gulp-vue-pack', 'file不存在');
    }

    if (file.isStream()) {
        throw new PluginError('gulp-vue-pack', '只支持.vue文件');
    }

    if (!file.contents) {
        //非文件,是目录
        callback();
        return;
    }

    let fileName = path.basename(file.path, ".vue");

    let fileContent = file.contents.toString(encoding);

    let contents = parseVueToContents(fileContent, fileName, path.dirname(file.path));
    let fpath = path.dirname(file.path);

    this.push(createFile(file.base, file.cwd, fpath, fileName + ".js", contents.js));

    //如果css文件无内容，则不生成css文件
    if(contents.css.length > 0) {
        this.push(createFile(file.base, file.cwd, fpath, fileName + ".css", contents.css));
    }
    callback();

}

function createFile(base, cwd, fpath, fileName, content) {
    return new File({
        base: base,
        cwd: cwd,
        path: path.join(fpath, fileName),
        contents: new Buffer(content)
    });
}

function parseVueToContents(vueContent, fileName, filePath) {

    let scriptContents = "";
    let styleContents = "";
    let templateContents = "";

    let DomUtils = HTMLParser.DomUtils;
    let domEls = HTMLParser.parseDOM(vueContent, {lowerCaseTags: true});

    for (let i = 0, len = domEls.length; i < len; i++) {
        switch (domEls[i].name) {
            case SCRIPT:
                scriptContents = DomUtils.getText(domEls[i]);
                break;
            case TEMPLATE:
                templateContents = DomUtils.getInnerHTML(domEls[i]);
                break;
            case STYLE:
                styleContents = DomUtils.getText(domEls[i]).trim();
                break;
        }
    }

    let jsContent = convertToJSContent(scriptContents, templateContents, styleContents, fileName, filePath);

    return {
        js: jsContent,
        css: styleContents
    }
}

/**
 * 将vue文件中的内容，进行转换，生成多页引用的vue
 * @param script 脚本内容
 * @param template 模板内容
 * @param style 样式内容
 * @param fileName 文件名
 * @param filePath 文件路径
 * @returns {*}
 */
function convertToJSContent(script, template, style, fileName, filePath) {

    if (!script) {
        return "";
    }

    //兼容 windows
    filePath = filePath.replace(/\\/g, "/");

    let jsFileContent = `(function(global, Vue, undefined){
    if(!global.__FORGE_ES6_VUE_COMPONENTS__) {
        global.__FORGE_ES6_VUE_COMPONENTS__ = {};
    }
`;

    if (style && style.length > 0) {
        jsFileContent += `
    (function(){
        function getCurrentScriptBase() {
            var src,
                lidx,
                scripts;

            if (document.currentScript) {
                src = document.currentScript.src;
            } else {
                scripts = document.getElementsByTagName('script');
                src = scripts[scripts.length - 1].src;
            }

            lidx = src.lastIndexOf("/");

            return src.substring(0, lidx);
        }

        var styleLink = document.createElement('link');
        styleLink.rel = "stylesheet";
        styleLink.href = getCurrentScriptBase() + "/" + "` + fileName + `.css";
        document.head.appendChild(styleLink);
    }());\n`;
    }

    jsFileContent += processJavascript(fileName, script, processTemplate(template), style, filePath);

    jsFileContent += "\n\nglobal." + fileName + " = " + fileName + ";\n\n";

    //伪造ES6格式的VUE组件
    jsFileContent += "global.__FORGE_ES6_VUE_COMPONENTS__['" + filePath + "/" + fileName + ".vue']=" + fileName + ";\n";

    jsFileContent += "Vue.component('vue" + fileName.replace(/([A-Z])/g, "-$1").toLowerCase() + "', " + fileName + ");\n\n";

    jsFileContent += "\n}(window, Vue));";

    return jsFileContent;
}

/**
 * 转义模板
 * @param template
 * @returns {string}
 */
function processTemplate(template) {
    return "'" + template.replace(TEMPLATE_ESCAPE_REG, "\\'").replace(TEMPLATE_ESCAPE_REG2, "\\\n") + "'";
}

/**
 * 处理js  将es6写的带export的部分转换成普通的组件定义
 * @param fileName
 * @param script
 * @param processedTemplate
 * @param style
 * @returns {string|*}
 */
function processJavascript(fileName, script, processedTemplate, style, filePath) {

    script = script.replace(VUE_COMPONENT_IMPORT_REG, function (matchedLine, variableName, vuePath, index, contents) {
        return "var " + variableName + " = global.__FORGE_ES6_VUE_COMPONENTS__['" + path.resolve(filePath, vuePath).replace(/\\/g, "/") + "']";
    });

    script = script.replace(SCRIPT_REPLACER_REG, "var " + fileName + " = Vue.extend(");

    script += ");\n";

    script += fileName + ".options.template = " + processedTemplate;

    // script = script.replace(/__gvptemplate/m, processedTemplate);

    return script;

}


(function () {

    'use strict';

    var Vec3 = require('./Vec3');

    function closestPointOnEdge( a, b, point ) {
        var ab = b.sub( a );
        // project c onto ab, computing parameterized position d(t) = a + t*(b * a)
        var t = new Vec3( point ).sub( a ).dot( ab ) / ab.dot( ab );
        // If outside segment, clamp t (and therefore d) to the closest endpoint
        if ( t < 0 ) {
            t = 0;
        }
        if ( t > 1 ) {
            t = 1;
        }
        // compute projected position from the clamped t
        return a.add( ab.multScalar( t ) );
    }

    /**
     * Instantiates a Triangle object.
     * @class Triangle
     * @classdesc A CCW-winded triangle object.
     */
    function Triangle() {
        switch ( arguments.length ) {
            case 1:
                // array or object argument
                var arg = arguments[0];
                this.a = new Vec3( arg[0] || arg.a );
                this.b = new Vec3( arg[1] || arg.b );
                this.c = new Vec3( arg[2] || arg.c );
                break;
            case 3:
                // individual vector arguments
                this.a = new Vec3( arguments[0] );
                this.b = new Vec3( arguments[1] );
                this.c = new Vec3( arguments[2] );
                break;
            default:
                this.a = new Vec3( 0, 0, 0 );
                this.b = new Vec3( 1, 0, 0 );
                this.c = new Vec3( 1, 1, 0 );
                break;
        }
    }

    /**
     * Returns the radius of the bounding sphere of the triangle.
     * @memberof Triangle
     *
     * @returns {number} The radius of the bounding sphere.
     */
    Triangle.prototype.radius = function() {
        var centroid = this.centroid(),
            aDist = this.a.sub( centroid ).length(),
            bDist = this.b.sub( centroid ).length(),
            cDist = this.c.sub( centroid ).length();
        return Math.max( aDist, Math.max( bDist, cDist ) );
    };

    /**
     * Returns the centroid of the triangle.
     * @memberof Triangle
     *
     * @returns {number} The centroid of the triangle.
     */
    Triangle.prototype.centroid = function() {
        return this.a
            .add( this.b )
            .add( this.c )
            .divScalar( 3 );
    };

    /**
     * Returns the normal of the triangle.
     * @memberof Triangle
     *
     * @returns {number} The normal of the triangle.
     */
    Triangle.prototype.normal = function() {
        var ab = this.b.sub( this.a ),
            ac = this.c.sub( this.a );
        return ab.cross( ac ).normalize();
    };

    /**
     * Returns the area of the triangle.
     * @memberof Triangle
     *
     * @returns {number} The area of the triangle.
     */
    Triangle.prototype.area = function() {
        var ab = this.b.sub( this.a ),
            ac = this.c.sub( this.a );
        return ab.cross( ac ).length() / 2.0;
    };

    /**
     * Returns true if the point is inside the triangle. The point must be
     * coplanar.
     * @memberof Triangle
     *
     * @param {Vec3|Array} point - The point to test.
     *
     * @returns {boolean} Whether or not the point is inside the triangle.
     */
    Triangle.prototype.isInside = function( point ) {
        var p = new Vec3( point );
        // Translate point and triangle so that point lies at origin
        var a = this.a.sub( p );
        var b = this.b.sub( p );
        var c = this.c.sub( p );
        // Compute normal vectors for triangles pab and pbc
        var u = b.cross( c );
        var v = c.cross( a );
        // Make sure they are both pointing in the same direction
        if ( u.dot( v ) < 0.0 ) {
            return false;
        }
        // Compute normal vector for triangle pca
        var w = a.cross( b );
        // Make sure it points in the same direction as the first two
        if ( u.dot( w ) < 0.0 ) {
            return false;
        }
        // Otherwise P must be in (or on) the triangle
        return true;
    };

    /**
     * Intersect the triangle and return intersection information.
     * @memberof Triangle
     *
     * @param {Vec3|Array} origin - The origin of the intersection ray
     * @param {Vec3|Array} direction - The direction of the intersection ray.
     * @param {boolean} ignoreBehind - Whether or not to ignore intersections behind the origin of the ray.
     * @param {boolean} ignoreBackface - Whether or not to ignore the backface of the triangle.
     *
     * @returns {Object|boolean} The intersection information, or false if there is no intersection.
     */
    Triangle.prototype.intersect = function( origin, direction, ignoreBehind, ignoreBackface ) {
        // Compute ray/plane intersection
        var o = new Vec3( origin );
        var d = new Vec3( direction );
        var normal = this.normal();
        var dn = d.dot( normal );
        if ( dn === 0 ) {
            // ray is parallel to plane
            // TODO: check if ray is co-linear and intersects?
            return false;
        }
        var t = this.a.sub( o ).dot( normal ) / dn;
        if ( ignoreBehind && ( t < 0 ) ) {
            // plane is behind ray
            return false;
        }
        if ( ignoreBackface ) {
            //  ignore triangles facing away from ray
            if ( ( t > 0 && dn > 0 ) || ( t < 0 && dn < 0 ) ) {
                // triangle is facing opposite the direction of intersection
                return false;
            }
        }
        var position = o.add( d.multScalar( t ) );
        // check if point is inside the triangle
        if ( !this.isInside( position ) ) {
            return false;
        }
        return {
            position: position,
            normal: normal,
            t: t
        };
    };

    /**
     * Returns the closest point on the triangle to the specified point.
     * @memberof Triangle
     *
     * @param {Vec3|Array} point - The point to test.
     *
     * @returns {Vec3} The closest point on the edge.
     */
    Triangle.prototype.closestPoint = function( point ) {
        var e0 = closestPointOnEdge( this.a, this.b, point );
        var e1 = closestPointOnEdge( this.b, this.c, point );
        var e2 = closestPointOnEdge( this.c, this.a, point );
        var d0 = ( e0.sub( point ) ).lengthSquared();
        var d1 = ( e1.sub( point ) ).lengthSquared();
        var d2 = ( e2.sub( point ) ).lengthSquared();
        if ( d0 < d1 ) {
            return ( d0 < d2 ) ? e0 : e2;
        } else {
            return ( d1 < d2 ) ? e1 : e2;
        }
    };

    /**
     * Returns a random Triangle of unit length.
     * @memberof Triangle
     *
     * @returns {Triangle} A random triangle of unit radius.
     */
    Triangle.random = function() {
        var a = Vec3.random(),
            b = Vec3.random(),
            c = Vec3.random(),
            centroid = a.add( b ).add( c ).divScalar( 3 ),
            aCent = a.sub( centroid ),
            bCent = b.sub( centroid ),
            cCent = c.sub( centroid ),
            aDist = aCent.length(),
            bDist = bCent.length(),
            cDist = cCent.length(),
            maxDist = Math.max( Math.max( aDist, bDist ), cDist ),
            scale = 1 / maxDist;
        return new Triangle(
            aCent.multScalar( scale ),
            bCent.multScalar( scale ),
            cCent.multScalar( scale ) );
    };

    /**
     * Returns true if the vector components match those of a provided triangle.
     * An optional epsilon value may be provided.
     * @memberof Triangle
     *
     * @param {Triangle} that - The vector to test equality with.
     * @param {number} epsilon - The epsilon value. Optional.
     *
     * @returns {boolean} Whether or not the vector components match.
     */
    Triangle.prototype.equals = function( that, epsilon ) {
        epsilon = epsilon === undefined ? 0 : epsilon;
        return this.a.equals( that.a, epsilon ) &&
            this.b.equals( that.b, epsilon ) &&
            this.c.equals( that.c, epsilon );
    };

    /**
     * Returns a string representation of the vector.
     * @memberof Triangle
     *
     * @returns {String} The string representation of the vector.
     */
    Triangle.prototype.toString = function() {
        return this.a.toString() + ', ' +
            this.b.toString() + ', ' +
            this.c.toString();
    };

    module.exports = Triangle;

}());


'use strict';

var bs58check = require('bs58check');

var elliptic = require('elliptic');
var secp256k1 = new elliptic.ec('secp256k1'); /* eslint new-cap: ["error", { "newIsCap": false }] */
var varuint = require('varuint-bitcoin');
var zconfig = require('./config');
var zbufferutils = require('./bufferutils');
var zcrypto = require('./crypto');
var zconstants = require('./constants');
var zaddress = require('./address');
var zopcodes = require('./opcodes');

/* More info: https://github.com/ZencashOfficial/zen/blob/master/src/script/standard.cpp#L377
 * Given an address, generates a pubkeyhash replay type script needed for the transaction
 * @param {String} address
 * @param {Number} blockHeight
 * @param {Number} blockHash
 * @param {String} pubKeyHash (optional)
 * return {String} pubKeyScript
 */
function mkPubkeyHashReplayScript(address, blockHeight, blockHash) {
  var pubKeyHash = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : zconfig.mainnet.pubKeyHash;

  // Get lengh of pubKeyHash (so we know where to substr later on)
  var addrHex = bs58check.decode(address).toString('hex');

  // Cut out pubKeyHash
  var subAddrHex = addrHex.substring(pubKeyHash.length, addrHex.length);

  // Minimal encoding
  var blockHeightBuffer = Buffer.alloc(4);
  blockHeightBuffer.writeUInt32LE(blockHeight, 0);
  if (blockHeightBuffer[3] === 0x00) {
    blockHeightBuffer = blockHeightBuffer.slice(0, 3);
  }
  var blockHeightHex = blockHeightBuffer.toString('hex');

  // block hash is encoded in little indian
  var blockHashHex = Buffer.from(blockHash, 'hex').reverse().toString('hex');

  // '14' is the length of the subAddrHex (in bytes)
  return zopcodes.OP_DUP + zopcodes.OP_HASH160 + zbufferutils.getPushDataLength(subAddrHex) + subAddrHex + zopcodes.OP_EQUALVERIFY + zopcodes.OP_CHECKSIG + zbufferutils.getPushDataLength(blockHashHex) + blockHashHex + zbufferutils.getPushDataLength(blockHeightHex) + blockHeightHex + zopcodes.OP_CHECKBLOCKATHEIGHT;
}

/*
 * Given an address, generates a script hash replay type script needed for the transaction
 * @param {String} address
 * @param {Number} blockHeight
 * @param {Number} blockHash
 * return {String} scriptHash script
 */
function mkScriptHashReplayScript(address, blockHeight, blockHash) {
  var addrHex = bs58check.decode(address).toString('hex');
  var subAddrHex = addrHex.substring(4, addrHex.length); // Cut out the '00' (we also only want 14 bytes instead of 16)

  var blockHeightBuffer = Buffer.alloc(4);
  blockHeightBuffer.writeUInt32LE(blockHeight, 0);
  if (blockHeightBuffer[3] === 0x00) {
    blockHeightBuffer = blockHeightBuffer.slice(0, 3);
  }
  var blockHeightHex = blockHeightBuffer.toString('hex');

  // Need to reverse it
  var blockHashHex = Buffer.from(blockHash, 'hex').reverse().toString('hex');

  return zopcodes.OP_HASH160 + zbufferutils.getPushDataLength(subAddrHex) + subAddrHex + zopcodes.OP_EQUAL + zbufferutils.getPushDataLength(blockHashHex) + blockHashHex + zbufferutils.getPushDataLength(blockHeightHex) + blockHeightHex + zopcodes.OP_CHECKBLOCKATHEIGHT;
}

/*
 * Given an address, generates an output script
 * @param {String} address
 * @param {Number} blockHeight
 * @param {Number} blockHash
 * return {String} output script
 */
function addressToScript(address, blockHeight, blockHash) {
  // P2SH replay starts with a 's', or 'r'
  if (address[1] === 's' || address[1] === 'r') {
    return mkScriptHashReplayScript(address, blockHeight, blockHash);
  }

  // P2PKH-replay is a replacement for P2PKH
  return mkPubkeyHashReplayScript(address, blockHeight, blockHash);
}

/*
 * Signature hashing for TXOBJ
 * @param {String} address
 * @param {Number} i, which transaction input to sign
 * @param {String} hex string of script
 * @param {String} hash code (SIGHASH_ALL, SIGHASH_NONE...)
 * return {String} output script
 */
function signatureForm(txObj, i, script, hashcode) {
  // Copy object so we don't rewrite it
  var newTx = JSON.parse(JSON.stringify(txObj));

  // Only sign the specified index
  for (var j = 0; j < newTx.ins.length; j++) {
    newTx.ins[j].script = '';
  }

  newTx.ins[i].script = script;

  if (hashcode === zconstants.SIGHASH_NONE) {
    newTx.outs = [];
  } else if (hashcode === zconstants.SIGHASH_SINGLE) {
    newTx.outs = newTx.outs.slice(0, newTx.ins.length);
    for (var _j = 0; _j < newTx.ins.length - 1; ++_j) {
      newTx.outs[_j].satoshis = Math.pow(2, 64) - 1;
      newTx.outs[_j].script = '';
    }
  } else if (hashcode === zconstants.SIGHASH_ANYONECANPAY) {
    newTx.ins = [newTx.ins[i]];
  }

  return newTx;
}

/*
 * Deserializes a hex string into a TXOBJ
 * @param {String} hex string
 * @return {Object} txOBJ
 */
function deserializeTx(hexStr) {
  var buf = Buffer.from(hexStr, 'hex');
  var offset = 0;

  // Out txobj
  var txObj = { version: 0, locktime: 0, ins: [], outs: []

    // Version
  };txObj.version = buf.readUInt32LE(offset);
  offset += 4;

  // Vins
  var vinLen = varuint.decode(buf, offset);
  offset += varuint.decode.bytes;
  for (var i = 0; i < vinLen; i++) {
    // Else its
    var hash = buf.slice(offset, offset + 32);
    offset += 32;

    var vout = buf.readUInt32LE(offset);
    offset += 4;

    var scriptLen = varuint.decode(buf, offset);
    offset += varuint.decode.bytes;

    var script = buf.slice(offset, offset + scriptLen);
    offset += scriptLen;

    var sequence = buf.slice(offset, offset + 4).toString('hex');
    offset += 4;

    txObj.ins.push({
      output: { hash: hash.reverse().toString('hex'), vout: vout },
      script: script.toString('hex'),
      sequence: sequence,
      prevScriptPubKey: ''
    });
  }

  // Vouts
  var voutLen = varuint.decode(buf, offset);
  offset += varuint.decode.bytes;
  for (var _i = 0; _i < voutLen; _i++) {
    var satoshis = zbufferutils.readUInt64LE(buf, offset);
    offset += 8;

    var _scriptLen = varuint.decode(buf, offset);
    offset += varuint.decode.bytes;

    var _script = buf.slice(offset, offset + _scriptLen);
    offset += _scriptLen;

    txObj.outs.push({
      satoshis: satoshis,
      script: _script.toString('hex')
    });
  }

  // Locktime
  txObj.locktime = buf.readInt32LE(offset);
  offset += 4;

  return txObj;
}

/*
 * Serializes a TXOBJ into hex string
 * @param {Object} txObj
 * return {String} hex string of txObj
 */
function serializeTx(txObj) {
  var serializedTx = '';
  var _buf16 = Buffer.alloc(4);

  // Version
  _buf16.writeUInt16LE(txObj.version, 0);
  serializedTx += _buf16.toString('hex');

  // History
  serializedTx += zbufferutils.numToVarInt(txObj.ins.length);
  txObj.ins.map(function (i) {
    // Txids and vouts
    _buf16.writeUInt16LE(i.output.vout, 0);
    serializedTx += Buffer.from(i.output.hash, 'hex').reverse().toString('hex');
    serializedTx += _buf16.toString('hex');

    // Script Signature
    // Doesn't work for length > 253 ....
    serializedTx += zbufferutils.getPushDataLength(i.script);
    serializedTx += i.script;

    // Sequence
    serializedTx += i.sequence;
  });

  // Outputs
  serializedTx += zbufferutils.numToVarInt(txObj.outs.length);
  txObj.outs.map(function (o) {
    // Write 64bit buffers
    // JS only supports 56 bit
    // https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/src/bufferutils.js#L25
    var _buf32 = Buffer.alloc(8);

    // Satohis
    _buf32.writeInt32LE(o.satoshis & -1, 0);
    _buf32.writeUInt32LE(Math.floor(o.satoshis / 0x100000000), 4);

    // ScriptPubKey
    serializedTx += _buf32.toString('hex');
    serializedTx += zbufferutils.getPushDataLength(o.script);
    serializedTx += o.script;
  });

  // Locktime
  _buf16.writeUInt16LE(txObj.locktime, 0);
  serializedTx += _buf16.toString('hex');

  return serializedTx;
}

/*
 * Creates a raw transaction
 * @param {[HISTORY]} history type, array of transaction history
 * @param {[RECIPIENTS]} recipient type, array of address on where to send coins to
 * @param {Number} blockHeight (latest - 300)
 * @param {String} blockHash of blockHeight
 * @return {TXOBJ} Transction Object (see TXOBJ type for info about structure)
 */
function createRawTx(history, recipients, blockHeight, blockHash) {
  var txObj = { locktime: 0, version: 1, ins: [], outs: [] };

  txObj.ins = history.map(function (h) {
    return {
      output: { hash: h.txid, vout: h.vout },
      script: '',
      prevScriptPubKey: h.scriptPubKey,
      sequence: 'ffffffff'
    };
  });
  txObj.outs = recipients.map(function (o) {
    return {
      script: addressToScript(o.address, blockHeight, blockHash),
      satoshis: o.satoshis
    };
  });

  return txObj;
}

/*
 * Gets signature for the vin script
 * @params {string} privKey private key
 * @params {TXOBJ} signingTx a txobj whereby all the vin script's field are empty except for the one that needs to be signed
 * @params {number} hashcode
*/
function getScriptSignature(privKey, signingTx, hashcode) {
  // Buffer
  var _buf16 = Buffer.alloc(4);
  _buf16.writeUInt16LE(hashcode, 0);

  var signingTxHex = serializeTx(signingTx);
  var signingTxWithHashcode = signingTxHex + _buf16.toString('hex');

  // Sha256 it twice, according to spec
  var msg = zcrypto.sha256x2(Buffer.from(signingTxWithHashcode, 'hex'));

  // Signing it
  var rawsig = secp256k1.sign(Buffer.from(msg, 'hex'), Buffer.from(privKey, 'hex'), { canonical: true });

  // Convert it to DER format
  // Appending 01 to it cause
  // ScriptSig = <varint of total sig length> <SIG from code, including appended 01 SIGNHASH> <length of pubkey (0x21 or 0x41)> <pubkey>
  // https://bitcoin.stackexchange.com/a/36481
  var signatureDER = Buffer.from(rawsig.toDER()).toString('hex') + '01';

  return signatureDER;
}

/*
 * Signs the raw transaction
 * @param {String} rawTx raw transaction
 * @param {Int} i
 * @param {privKey} privKey (not WIF format)
 * @param {compressPubKey} compress public key before appending to scriptSig? (default false)
 * @param {hashcode} hashcode (default SIGHASH_ALL)
 * return {String} signed transaction
 */
function signTx(_txObj, i, privKey) {
  var compressPubKey = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
  var hashcode = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : zconstants.SIGHASH_ALL;

  // Make a copy
  var txObj = JSON.parse(JSON.stringify(_txObj));

  // Prepare our signature
  // Get script from the current tx input
  var script = txObj.ins[i].prevScriptPubKey;

  // Populate current tx in with the prevScriptPubKey
  var signingTx = signatureForm(txObj, i, script, hashcode);

  // Get script signature
  var scriptSig = getScriptSignature(privKey, signingTx, hashcode);

  // Chuck it back into txObj and add pubkey
  // Protocol:
  // PUSHDATA
  // signature data and SIGHASH_ALL
  // PUSHDATA
  // public key data
  var pubKey = zaddress.privKeyToPubKey(privKey, compressPubKey);

  txObj.ins[i].script = zbufferutils.getPushDataLength(scriptSig) + scriptSig + zbufferutils.getPushDataLength(pubKey) + pubKey;

  return txObj;
}

/*
 * Gets signatures needed for multi-sign tx
 * @param {String} _txObj transaction object you wanna sign
 * @param {Int} index fof tx.in to sign
 * @param {privKey} One of the M private keys you (NOT WIF format!!!)
 * @param {string} redeemScript (redeemScript of the multi-sig)
 * @param {string} hashcode (SIGHASH_ALL, SIGHASH_NONE, etc)
 * return {String} signature
 */
function multiSign(_txObj, i, privKey, redeemScript) {
  var hashcode = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : zconstants.SIGHASH_ALL;

  // Make a copy
  var txObj = JSON.parse(JSON.stringify(_txObj));

  // Populate current tx.ins[i] with the redeemScript
  var signingTx = signatureForm(txObj, i, redeemScript, hashcode);

  return getScriptSignature(privKey, signingTx, hashcode);
}

/*
 * Applies the signatures to the transaction object
 * NOTE: You NEED to supply the signatures in order.
 *       E.g. You made sigAddr1 with priv1, priv3, priv2
 *            You can provide signatures of (priv1, priv2) (priv3, priv2) ...
 *            But not (priv2, priv1)
 * @param {String} _txObj transaction object you wanna sign
 * @param {Int} index fof tx.in to sign
 * @param {[string]} signatures obtained from multiSign
 * @param {string} redeemScript (redeemScript of the multi-sig)
 * @param {string} hashcode (SIGHASH_ALL, SIGHASH_NONE, etc)
 * return {String} signature
 */
function applyMultiSignatures(_txObj, i, signatures, redeemScript) {
  // Make a copy
  var txObj = JSON.parse(JSON.stringify(_txObj));

  var redeemScriptPushDataLength = zbufferutils.getPushDataLength(redeemScript);

  // Lmao no idea, just following the source code
  if (redeemScriptPushDataLength.length > 2) {
    if (redeemScriptPushDataLength.length === 6) {
      redeemScriptPushDataLength = redeemScriptPushDataLength.slice(2, 4);
    }
  }

  // http://www.soroushjp.com/2014/12/20/bitcoin-multisig-the-hard-way-understanding-raw-multisignature-bitcoin-transactions/
  txObj.ins[i].script = zopcodes.OP_0 + signatures.map(function (x) {
    return zbufferutils.getPushDataLength(x) + x;
  }).join('') + zopcodes.OP_PUSHDATA1 + redeemScriptPushDataLength + redeemScript;

  return txObj;
}

module.exports = {
  addressToScript: addressToScript,
  createRawTx: createRawTx,
  mkPubkeyHashReplayScript: mkPubkeyHashReplayScript,
  mkScriptHashReplayScript: mkScriptHashReplayScript,
  signatureForm: signatureForm,
  serializeTx: serializeTx,
  deserializeTx: deserializeTx,
  signTx: signTx,
  multiSign: multiSign,
  applyMultiSignatures: applyMultiSignatures,
  getScriptSignature: getScriptSignature
};


'use strict';

var fs = require('fs');
var spawn = require('cross-spawn');
var extend = require('extend-shallow');
var isWindows = require('is-windows');
var isExtglob = require('is-extglob');
var bashPath;

/**
 * Returns true if `str` matches the given `pattern`.
 *
 * ```js
 * var bash = require('bash-match');
 * console.log(bash('foo', 'f*'));
 * //=> true
 *
 * console.log(bash('foo', 'b*'));
 * //=> false
 * ```
 *
 * @param {String} `str`
 * @param {String} `pattern`
 * @param {Options} `options` Set `strictErrors` to true to throw when bash throws an error. Otherwise it just returns false.
 * @return {Boolean}
 * @details false
 * @api public
 */

function bash(str, pattern, options) {
  if (typeof str !== 'string') {
    throw new TypeError('expected a string');
  }
  if (typeof pattern !== 'string') {
    throw new TypeError('expected a string');
  }

  if (isWindows()) {
    throw new Error('bash-match does not work on windows');
  }

  try {
    var opts = createOptions(pattern, options);
    var res = spawn.sync(getBashPath(), cmd(str, pattern, opts), opts);
    var err = toString(res.stderr);
    if (err) {
      return handleError(err, opts);
    }
    return !!toString(res.stdout);
  } catch (err) {
    return handleError(err, opts);
  }
}

/**
 * Returns true if `str` matches the given `pattern`. Alias for the [main export](#bash).
 *
 * ```js
 * var bash = require('bash-match');
 * console.log(bash.isMatch('foo', 'f*'));
 * //=> true
 *
 * console.log(bash.isMatch('foo', 'b*'));
 * //=> false
 * ```
 *
 * @param {String} `str`
 * @param {String} `pattern`
 * @param {Options} `options` Set `strictErrors` to true to throw when bash throws an error. Otherwise it just returns false.
 * @return {Boolean}
 * @api public
 */

bash.isMatch = function(fixture, pattern, options) {
  return bash(fixture, pattern, options);
};

/**
 * Takes a `list` of strings and a glob `pattern`, and returns an array
 * of strings that match the pattern.
 *
 * ```js
 * var bash = require('bash-match');
 * console.log(bash.match(['foo', 'bar'], 'b*'));
 * //=> ['bar']
 * ```
 *
 * @param {Array} `array` List of strings to match
 * @param {String} `pattern` Glob pattern
 * @param {Options} `options` Set `strictErrors` to true to throw when bash throws an error. Otherwise it just returns false.
 * @return {Boolean}
 * @api public
 */

bash.match = function(list, pattern, options) {
  list = Array.isArray(list) ? list : [list];
  var matches = [];
  var len = list.length;
  var idx = -1;
  while (++idx < len) {
    var fixture = list[idx];
    if (bash.isMatch(fixture, pattern, options)) {
      matches.push(fixture);
    }
  }
  return matches;
};

/**
 * Create the command to use
 */

function cmd(str, pattern, options) {
  var valid = ['dotglob', 'extglob', 'failglob', 'globstar', 'nocaseglob', 'nullglob'];
  var args = [];

  for (var key in options) {
    if (options.hasOwnProperty(key) && valid.indexOf(key) !== -1) {
      args.push('-O', key);
    }
  }
  args.push('-c', 'IFS=$"\n"; if [[ "' + str + '" = ' + pattern + ' ]]; then echo true; fi');
  return args;
}

/**
 * Stringify `buf`
 */

function toString(buf) {
  return (buf && buf.toString() || '').trim();
}

/**
 * Handle errors
 */

function handleError(err, options) {
  if (options && options.strictErrors === true) {
    throw err;
  }
  return false;
}

/**
 * Shallow clone and create options
 */

function createOptions(pattern, options) {
  if (options && options.normalized === true) return options;
  var opts = extend({cwd: process.cwd()}, options);
  if (opts.nocase === true) opts.nocaseglob = true;
  if (opts.nonull === true) opts.nullglob = true;
  if (opts.dot === true) opts.dotglob = true;
  if (!opts.hasOwnProperty('globstar') && pattern.indexOf('**') !== -1) {
    opts.globstar = true;
  }
  if (!opts.hasOwnProperty('extglob') && isExtglob(pattern)) {
    opts.extglob = true;
  }
  opts.normalized = true;
  return opts;
}

/**
 * Get bash path
 */

function getBashPath() {
  if (bashPath) return bashPath;
  if (fs.existsSync('/usr/local/bin/bash')) {
    bashPath = '/usr/local/bin/bash';
  } else if (fs.existsSync('/bin/bash')) {
    bashPath = '/bin/bash';
  } else {
    bashPath = 'bash';
  }
  return bashPath;
}

/**
 * Expose `bash`
 */

module.exports = bash;