

//  matrix and vector functions.
//
//

function vector_add(a, b, result){
    if(!result) result = [];
	if(!a) throw "first argument to vector_add missing";
	if(!b) throw "second argument to vector_add missing";

    if(a.length > b.lemgth){
        var tmp = a;
        a = b; b = tmp; }

    for(var i = 0; i < a.length; ++i){
        result[i] = a[i] + b[i]; }

    for(var i = a.length; i < b.length; ++i){
        result[i] = b[i]; }

    return result; }

    
function vector_cpy(result, a){
	if(!a) throw "first argument to vector_cpy missing";
    for(var i = 0; i < a.length; ++i){
        result[i] = a[i]; }
}



function vector_minus(a, b, result){
    if(!result) result = [];
	if(!a) throw "first argument to vector_minus missing";
	if(!b) throw "second argument to vector_minus missing";

    var lim = Math.max(a.length, b.length);
    
    for(var i = 0; i < lim; ++i){
		var aValue, bValue;
		if(i < a.length)
			aValue = a[i];
		else
			aValue = 0;

		if(i < b.length)
			bValue = b[i];
		else
			bValue = 0;
			
		result[i] = aValue - bValue; }
        
        
    return result; }
   


function vector_dot(a, b){
    var lim = Math.min(a.length, b.length);
	if(!a) throw "first argument to vector_dot missing";
	if(!b) throw "second argument to vector_dot missing";
	
    var result = 0;

    for(var i = 0; i < lim; ++i){
        result += a[i] * b[i]; }

    return result; }
    
    
function vector_norm(a){
	if(!a) throw "argument to vector_norm missing";
    return Math.sqrt(vector_dot(a,a)); }

    
    
    
function vector_scale(a, s, result){
	if(!a) throw "first argument to vector_scale missing";
	if(s === undefined || s === null) throw "scalar argument to vector_scale missing";

    if(!result) result = [];
    
    for(var i = 0; i < a.length; ++i){
        result[i] = s * a[i]; }

    return result; }



// returns true iff the zero tail padded vectors match
function vector_cmp(a, b){
	if(!a) throw "first argument to vector_cmp missing";
	if(!b) throw "second argument to vector_cmp missing";

    if(a.length > b.length){
         var tmp = a;
         a = b; b = tmp; }

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

    for(var i = a.length; i < b.length; ++i){
        if(b[i] != 0) 
			return false; }
            
    return true; }
    
    
    
function vector_midpoint(a, b, result){
	if(!a) throw "first argument to vector_midpoint missing";
	if(!b) throw "second argument to vector_midpoint missing";

    if(b.length > a.length){
        var tmp = a;
        a = b; b = tmp; }
        
    if(!result) result = new Array(b.length);
        
    for(var i = 0; i < a.length; ++i){
        result[i] = (a[i] + b[i])/2; }
        
    for(var i = a.length; i < b.length; ++i){
        result[i] = b[i]; }
    
    return result;
}




//this function may not care, but the expected matrix format in this project is a list of column vectors.
function matrix_transpose(matrix){
	if(!matrix) throw "argument to matrix_transpose missing";

    var result = [];
    var dim0 = matrix.length;
    if(!dim0) 
		return result;

    var dim1 = matrix[0].length;

    for(var i = 0; i < dim1; ++i){
        var colvector = [];
        
        for(var j = 0; j < dim0; ++j){
            colvector.push(matrix[j][i]); }

        result.push(colvector); }
        
    return result; }
    


    
    

function matrix_mult(a, b){
	if(!a) throw "first argument to matrix_mult missing";
	if(!b) throw "second argument to matrix_mult missing";

    var result = [];
    var a = matrix_transpose(a);
    var dim0 = a.length;
    var dim1 = b.length;

    for(var i = 0; i < dim1; ++i){
        var colvector = [];
        
        for(var j = 0; j < dim0; ++j){
            colvector.push( vector_dot(a[j], b[i]) );  }
        result.push(colvector); }

    return result; }


    
// finds the inverse of a 3 by 3 matrix, returns false if matrix has determinant zero.
//

function matrix33inv(m){
    if(!m) throw "argument to matrix33inv missing";

    // the matrix format is a list of column vectors.
    var a = m[0][0];
	var b = m[1][0];
	var c = m[2][0];
	var d = m[0][1];
	var e = m[1][1];
	var f = m[2][1];
	var g = m[0][2];
	var h = m[1][2];
	var k = m[2][2];
	
	// [ a b c
    //   d e f
    //   g h k ]
	
	
    var A = e*k - f*h;
	var B = f*g - d*k;
	var C = d*h - e*g;	
	var D = h*c - b*k;
	var E = a*k - c*g;
	var F = g*b - a*h;
	var G = b*f - e*c;
	var H = d*c - a*f;
	var K = a*e - d*b;
	
	
    var det = (a*A + b*B + c*C);
	
	
	if(det == 0) return false;
	
	// the multiplicative inverse of the determinant.
	var di = 1/det;
	
	// the matrix format is a list of column vectors.
	var result = [[di*A, di*B, di*C],
		          [di*D, di*E, di*F],
		          [di*G, di*H, di*K]];

	return result;  }

	
// does not check if function is differentiable.

function compute_gradient(func, point, error){
	if(!func) throw "function argument to compute_gradient missing";
	if(!point) throw "point argument to compute_gradient missing";

    if(!error) error = 0.00001;

    // find gradient
    var otherpoint = point.slice(0);
    var gradient = new Array(point.length);
    var pointvalue = func(point);
    
    for(var i = 0; i < point.length; ++i){
        otherpoint[i] += error;
        var othervalue = func(otherpoint)
        gradient[i] = (othervalue - pointvalue) / error;
        
        otherpoint[i] -= error; }
    
    return gradient;
}
    
    
    

//
// equation at:
// http://inside.mines.edu/~gmurray/ArbitraryAxisRotation/ArbitraryAxisRotation.html
// creates a rotation matrix about the given vector.
//
function vector_rotation(vector, s){
	if(!vector) throw "vector argument to vector_rotation missing";
	if(!s) throw "angle argument to vector_rotation missing";
	
    var len = Math.sqrt(vector[0]*vector[0]
                        + vector[1]*vector[1]
                        + vector[2]*vector[2]);

    var u = vector[0]/len;
    var v = vector[1]/len;
    var w = vector[2]/len;
    
    var _cos = Math.cos(s);
    var _sin = Math.sin(s);
    var _1mcos = 1 - _cos;

    return [[u*u*_1mcos + _cos, u*v*_1mcos - w*_sin, u*w*_1mcos + v*_sin],
            [u*v*_1mcos + w*_sin, v*v*_1mcos + _cos, v*w*_1mcos - u*_sin],
            [u*w*_1mcos - v*_sin, v*w*_1mcos + u*_sin, w*w*_1mcos + _cos]]; }
