Source: matrixy.js

// Generated by CoffeeScript 1.10.0
var arrayFns, checkCanMultiply, checkSizesMatch, compose, createLiftFunctions, expect, lift1, lift2, lift2Infix, liftAllOutputs, liftInput, ref, ref1, t;

expect = require('chai').expect;


/**
 * API for dealing with matrices as wrapped functions.
 * @module matrixy
 */

arrayFns = require('./arrays');

expect = require('chai').expect;

ref = require('./functional_utils'), compose = ref.compose, createLiftFunctions = ref.createLiftFunctions;


/*
 * Internal reference to module.exports.
 * @private
 */

t = this;

ref1 = createLiftFunctions({
  wrap: function(arrays) {
    if (Array.isArray(arrays)) {
      return t.createMatrix(arrays);
    } else {
      return arrays;
    }
  },
  unwrap: function(matrix) {
    if (typeof matrix === 'function') {
      return matrix();
    } else {
      return matrix;
    }
  }
}), liftInput = ref1.liftInput, lift1 = ref1.lift1, lift2 = ref1.lift2, lift2Infix = ref1.lift2Infix, liftAllOutputs = ref1.liftAllOutputs;


/**
 * A Matrix. Can be given a {@link module:matrixy.MatrixOperation}.
 * @callback module:matrixy.Matrix
 * @param {module:matrixy.MatrixOperation} [op]
 * @return {Array.<Array.<Number>>|*} Internal 2D array if no params given, else result of op.
 */


/**
 * Operation on a {@link module:matrixy.Matrix}.
 * @callback module:matrixy.MatrixOperation
 * @param {module:matrixy.Matrix} matrix
 * @return {*}
 */


/**
 * Create a {@link module:matrixy.Matrix} from a 2D array.
 * @param {!Array.<Array.<Number>>} arrays - 2D array
 * @return {module:matrixy.Matrix}
 */

this.createMatrix = function(arrays) {
  var wrapper;
  expect(arrays, 'createMatrix').to.not.be.empty;
  expect(arrays[0], 'createMatrix').to.not.be.empty;

  /**
   * @param {module:matrixy.MatrixOperation} [op]
   * @return {Array.<Array.<Number>>|*} Internal 2D array if no params given, else result of op.
   */
  return wrapper = function(op) {
    return (typeof op === "function" ? op(wrapper) : void 0) || arrays;
  };
};


/**
 * Create an immutable {@link module:matrixy.Matrix} from a 2D array.
 * All {@link module:matrixy.MatrixOperation}s will be performed on a copy of the 2D array.
 * @param {!Array.<Array.<Number>>} arrays
 * @return {module:matrixy.Matrix}
 */

this.createImmutableMatrix = function(arrays) {
  var wrapper;
  return wrapper = function(op) {
    return (typeof op === "function" ? op(wrapper) : void 0) || arrayFns.copy(arrays);
  };
};


/**
 * Create an identity {@link module:matrixy.Matrix} of the given size.
 * @function
 * @param {!Integer} size
 * @return {module:matrixy.Matrix}
 */

this.createIdentityMatrix = compose(t.createMatrix, arrayFns.createIdentity);


/**
 * Create a blank {@link module:matrixy.Matrix} of the given size.
 * @function
 * @param {!Integer} size
 * @return {module:matrixy.Matrix}
 */

this.createBlankMatrix = compose(t.createMatrix, arrayFns.createBlank);


/**
 * Return a copy of the given Matrix.
 * @param  {module:matrixy.Matrix} matrix
 * @return {module:matrixy.Matrix}
 */

this.copy = function(matrix) {
  return t.createMatrix(arrayFns.copy(matrix()));
};


/**
 * Get an entry in the matrix at position (row, col).
 * @param  {Integer} row
 * @param  {Integer} col
 * @return {Number}
 */

this.get = function(row, col) {
  return function(m) {
    return m()[row][col];
  };
};


/**
 * Set an entry in the matrix to a given value.
 * @param {Integer} row
 * @param {Integer} col
 * @return {module:matrixy~setter}
 */

this.set = function(row, col) {

  /**
   * @namespace module:matrixy~setter
   */
  return {

    /**
     * Set to the given value.
     * @memberOf module:matrixy~setter
     * @param  {Number} value
     * @return {module:matrixy.MatrixOperation}
     */
    to: function(value) {
      return function(m) {
        return m()[row][col] = value;
      };
    },

    /**
     * Equivalent to the += operator.
     * @memberOf module:matrixy~setter
     * @param  {Number} value
     * @return {module:matrixy.MatrixOperation}
     */
    plusEquals: function(value) {
      return function(m) {
        return m()[row][col] += value;
      };
    }
  };
};


/**
 * Get the number of rows in the given matrix.
 * @function
 * @param {module:matrixy.Matrix} matrix
 * @return {Number}
 */

this.numOfRowsOf = liftInput(arrayFns.getNumOfRows);


/**
 * Get the number of columns in the given matrix.
 * @function
 * @param {module:matrixy.Matrix} matrix
 * @return {Number}
 */

this.numOfColsOf = liftInput(arrayFns.getNumOfColumns);


/**
 * @see {@link module:arrays.getDimensions}
 * @function
 * @param {module:matrixy.Matrix} matrix
 * @return {Array.<Integer>} e.g. [2, 3] for a 2x3 matrix
 */

this.getDimensionsOf = liftInput(arrayFns.getDimensions);


/**
 * @see {@link module:arrays.getSize}
 * @function
 * @param {module:matrixy.Matrix} matrix
 * @return {String}
 */

this.sizeOf = liftInput(arrayFns.getSize);


/**
 * @see {@link module:arrays.isLowerTriangular}
 * @function
 * @param {module:matrixy.Matrix} matrix
 * @return {Boolean}
 */

this.isLowerTriangular = liftInput(arrayFns.isLowerTriangular);


/**
 * @see {@link module:arrays.isUpperTriangular}
 * @function
 * @param {module:matrixy.Matrix} matrix
 * @return {Boolean}
 */

this.isUpperTriangular = liftInput(arrayFns.isUpperTriangular);


/**
 * Get the wrapped 2D arrays of a {@link module:matrixy.Matrix}
 * @function
 * @param {module:matrixy.Matrix} matrix
 * @return {Array.<Array.<Number>>}
 */

this.innerArraysOf = liftInput(function(arrays) {
  return arrays;
});


/*
 * @private
 * @param  {Array.<Array.<Number>>} a1
 * @param  {Array.<Array.<Number>>} a2
 */

checkSizesMatch = function(a1, a2) {
  var size1, size2;
  size1 = arrayFns.getSize(a1);
  size2 = arrayFns.getSize(a2);
  return expect(size1, 'Matrix size').to.equal(size2);
};


/*
 * @private
 * @param  {Array.<Array.<Number>>} a1
 * @param  {Array.<Array.<Number>>} a2
 */

checkCanMultiply = function(a1, a2) {
  var numOfColsIn1, numOfRowsIn2, size1, size2;
  numOfColsIn1 = arrayFns.getNumOfColumns(a1);
  numOfRowsIn2 = arrayFns.getNumOfRows(a2);
  if (numOfColsIn1 !== numOfRowsIn2) {
    size1 = arrayFns.getSize(a1);
    size2 = arrayFns.getSize(a2);
    throw {
      name: 'IllegalArgumentException',
      message: "Can't multiply a " + size1 + " matrix by a " + size2 + " matrix."
    };
  }
};


/**
 * Infix function to add two Matrices.
 * @example
 * c = a(plus(b)); // JavaScript
 * c = a plus b # CoffeeScript
 * @function
 * @param  {module:matrixy.Matrix} matrix
 * @return {module:matrixy.MatrixOperation}
 */

this.plus = lift2Infix(function(a1, a2) {
  checkSizesMatch(a1, a2);
  return arrayFns.add(a1, a2);
});


/**
 * Infix function to subtract a Matrix from another
 * @example
 * c = a(minus(b)); // JavaScript
 * c = a minus b # CoffeeScript
 * @function
 * @param  {module:matrixy.Matrix} matrix
 * @return {module:matrixy.MatrixOperation}
 */

this.minus = lift2Infix(function(a1, a2) {
  checkSizesMatch(a1, a2);
  return arrayFns.subtract(a1, a2);
});


/**
 * Infix function to multiply two Matrices.
 * @example
 * c = a(times(b)); // JavaScript
 * c = a times b # CoffeeScript
 * @function
 * @param  {(module:matrixy.Matrix|Number)} matrix
 * @return {module:matrixy.MatrixOperation}
 */

this.times = lift2Infix(function(a1, a2) {
  if (typeof a2 !== 'number') {
    checkCanMultiply(a1, a2);
  }
  return arrayFns.multiply(a1, a2);
});


/**
 * Infix function to divide a Matrix by another
 * @example
 * c = a(dividedBy(b)); // JavaScript
 * c = a dividedBy b # CoffeeScript
 * @function
 * @param  {module:matrixy.Matrix} matrix
 * @return {module:matrixy.MatrixOperation}
 */

this.dividedBy = lift2Infix(arrayFns.divide);


/**
 * @typedef module:matrixy.LUP
 * @type {object}
 * @property {module:matrixy.Matrix} l - Lower triangular matrix.
 * @property {module:matrixy.Matrix} u - Upper triangular matrix.
 * @property {module:matrixy.Matrix} p - Permutation matrix.
 */


/**
 * Decompose this matrix into an LU pair.
 * @see {@link module:arrays.decompose}
 * @function
 * @param {module:matrixy.Matrix} matrix
 * @return {LUP}
 */

this.decompose = liftAllOutputs(arrayFns.decompose);


/**
 * Solve the matrix equation Ax = b for x with matrix size N.
 * @see {@link module:arrays.solve}
 * @function
 * @param {module:matrixy.Matrix} A - NxN
 * @param {module:matrixy.Matrix} b - Nx1
 * @return {module:matrixy.Matrix} Nx1
 */

this.solve = lift2(arrayFns.solve);


/**
 * Return a transposed version of the given {@link module:matrixy.Matrix}.
 * @function
 * @param {module:matrixy.Matrix} matrix
 * @return {module:matrixy.Matrix} Transposed matrix.
 */

this.transpose = lift1(arrayFns.transpose);


/**
 * Return an inverted version of the given {@link module:matrixy.Matrix}.
 * @function
 * @param {module:matrixy.Matrix} matrix
 * @return {module:matrixy.Matrix} Inverted matrix.
 */

this.invert = lift1(arrayFns.invert);


/**
 * Is it possible to invert the given 2D array?
 * @function
 * @param {module:matrixy.Matrix} matrix
 * @return {Boolean}
 */

this.isInvertible = lift1(arrayFns.isInvertible);


/**
 * Return the determinant of the given {@link module:matrixy.Matrix}.
 * @function
 * @param {module:matrixy.Matrix} matrix - Square matrix
 * @return {Number}
 */

this.determinantOf = lift1(function(arrays) {
  expect(arrays).to.satisfy(arrayFns.isSquare, 'Can only find determinant of a square matrix');
  return arrayFns.determinant(arrays);
});