// BasicMatrix
// Version 1.0.2

// By David Evans, 2008

public class BasicMatrix {
	private double[][] data;
	private int d1, d2;
	
	public double[] getCol( int a ) {
		return data[ a ];
	}
	
	public BasicMatrix( ) {
	}
	public BasicMatrix( int size ) {
		if( size < 0 )
			throw new InvalidMathException( );
		d1 = size;
		d2 = size;
		data = new double[ d1 ][ d2 ];
		int a, b;
		for( a = 0; a != d1; ++ a )
			for( b = 0; b != d2; ++ b )
				data[ a ][ b ] = a == b ? 1.0 : 0.0;
	}
	public BasicMatrix( int D1, int D2 ) {
		if( D1 == D2 ) {
			if( D1 < 0 )
				throw new InvalidMathException( );
			d1 = D1;
			d2 = d1;
			data = new double[ d1 ][ d2 ];
			int a, b;
			for( a = 0; a != d1; ++ a )
				for( b = 0; b != d2; ++ b )
					data[ a ][ b ] = a == b ? 1.0 : 0.0;
		} else {
			if( D1 < 0 || D2 < 0 )
				throw new InvalidMathException( );
			d1 = D1;
			d2 = D2;
			data = new double[ d1 ][ d2 ];
			int a, b;
			for( a = 0; a != d1; ++ a )
				for( b = 0; b != d2; ++ b )
					data[ a ][ b ] = 0.0;
		}
	}
	public BasicMatrix( BasicMatrix clone ) {
		d1 = clone.getDimension1Int( );
		d2 = clone.getDimension2Int( );
		data = new double[ d1 ][ d2 ];
		int a, b;
		for( a = 0; a != d1; ++ a )
			for( b = 0; b != d2; ++ b )
				data[ a ][ b ] = clone.getValue( a, b );
	}
	private BasicMatrix( int D1, int D2, double[][] dat ) {
		d1 = D1;
		d2 = D2;
		data = dat;
	}
	
	public int getDimension1( ) {
		return d1;
	}
	public int getDimension2( ) {
		return d2;
	}
	public int getDimension1Int( ) {
		return d1;
	}
	public int getDimension2Int( ) {
		return d2;
	}
	public double getValue( int p1, int p2 ) {
		return data[ p1 ][ p2 ];
	}
	public void setValue( int p1, int p2, long to ) {
		data[ p1 ][ p2 ] = (double) to;
	}
	public void setValue( int p1, int p2, double to ) {
		data[ p1 ][ p2 ] = to;
	}
	
	public BasicMatrix _add( double that ) {
		double[][] dat = new double[ d1 ][ d2 ];
		int a, b;
		for( a = 0; a != d1; ++ a )
			for( b = 0; b != d2; ++ b )
				dat[ a ][ b ] = data[ a ][ b ] + that;
		return new BasicMatrix( d1, d2, dat );
	}
	public BasicMatrix add( BasicMatrix that ) {
		if( !sameSize( that ) )
			throw new InvalidMathException( );
		double[][] dat = new double[ d1 ][ d2 ];
		int a, b;
		for( a = 0; a != d1; ++ a )
			for( b = 0; b != d2; ++ b )
				dat[ a ][ b ] = data[ a ][ b ] + that.getValue( a, b );
		return new BasicMatrix( d1, d2, dat );
	}
	public BasicMatrix _subtract( double that ) {
		double[][] dat = new double[ d1 ][ d2 ];
		int a, b;
		for( a = 0; a != d1; ++ a )
			for( b = 0; b != d2; ++ b )
				dat[ a ][ b ] = data[ a ][ b ] - that;
		return new BasicMatrix( d1, d2, dat );
	}
	public BasicMatrix subtract( BasicMatrix that ) {
		if( !sameSize( that ) )
			throw new InvalidMathException( );
		double[][] dat = new double[ d1 ][ d2 ];
		int a, b;
		for( a = 0; a != d1; ++ a )
			for( b = 0; b != d2; ++ b )
				dat[ a ][ b ] = data[ a ][ b ] - that.getValue( a, b );
		return new BasicMatrix( d1, d2, dat );
	}
	public BasicMatrix _multiply( double that ) {
		double[][] dat = new double[ d1 ][ d2 ];
		int a, b;
		for( a = 0; a != d1; ++ a )
			for( b = 0; b != d2; ++ b )
				dat[ a ][ b ] = data[ a ][ b ] * that;
		return new BasicMatrix( d1, d2, dat );
	}
	public BasicMatrix multiply( BasicMatrix that ) {
		if( d2 != that.getDimension1Int( ) )
			throw new InvalidMathException( );
		int d3 = that.getDimension2Int( );
		double[][] dat = new double[ d1 ][ d3 ];
		int a, b, c;
		double d;
		for( a = 0; a != d1; ++ a )
			for( b = 0; b != d3; ++ b ) {
				d = 0.0;
				for( c = 0; c != d2; ++ c )
					d += data[ a ][ c ] * that.getValue( c, b );
				dat[ a ][ b ] = d;
			}
		return new BasicMatrix( d1, d3, dat );
	}
	public BasicMatrix _divide( double that ) {
		double[][] dat = new double[ d1 ][ d2 ];
		int a, b;
		for( a = 0; a != d1; ++ a )
			for( b = 0; b != d2; ++ b )
				dat[ a ][ b ] = data[ a ][ b ] / that;
		return new BasicMatrix( d1, d2, dat );
	}
	public BasicMatrix divide( BasicMatrix that ) {
		return multiply( that.inverse( ) );
	}
	public BasicMatrix _divideWhole( double that ) {
		double[][] dat = new double[ d1 ][ d2 ];
		int a, b;
		for( a = 0; a != d1; ++ a )
			for( b = 0; b != d2; ++ b )
				dat[ a ][ b ] = Math.floor( data[ a ][ b ] / that );
		return new BasicMatrix( d1, d2, dat );
	}
	public BasicMatrix _power( double that ) {
		double[][] dat = new double[ d1 ][ d2 ];
		int a, b;
		for( a = 0; a != d1; ++ a )
			for( b = 0; b != d2; ++ b )
				dat[ a ][ b ] = Math.pow( data[ a ][ b ], that );
		return new BasicMatrix( d1, d2, dat );
	}
	public BasicMatrix power( double that ) {
		if( !square( ) )
			throw new InvalidMathException( );
		if( that != (int) that )
			throw new InvalidMathException( );
		return power( (int) that );
	}
	public BasicMatrix power( int that ) {
		if( !square( ) )
			throw new InvalidMathException( );
		switch( (int) that ) {
		case 0:	return new BasicMatrix( d1 );
		case 1:	return new BasicMatrix( this );
		case -1:return inverse( );
		}
		BasicMatrix a, b;
		if( that < 0 ) {
			a = inverse( );
			that = -that;
		} else
			a = this;
		
		b = a;
		while( that != 1 ) {
			b = b.multiply( a );
			that --;
		}
		return b;
	}
	public BasicMatrix _modulus( double that ) {
		double[][] dat = new double[ d1 ][ d2 ];
		int a, b;
		for( a = 0; a != d1; ++ a )
			for( b = 0; b != d2; ++ b )
				dat[ a ][ b ] = data[ a ][ b ] % that;
		return new BasicMatrix( d1, d2, dat );
	}
	public BasicMatrix _range( double low, double high ) {
		double[][] dat = new double[ d1 ][ d2 ];
		int a, b;
		for( a = 0; a != d1; ++ a )
			for( b = 0; b != d2; ++ b )
				dat[ a ][ b ] = Math.max( low, Math.min( high, data[ a ][ b ] ) );
		return new BasicMatrix( d1, d2, dat );
	}
	public BasicMatrix _reverseSubtract( double that ) {
		double[][] dat = new double[ d1 ][ d2 ];
		int a, b;
		for( a = 0; a != d1; ++ a )
			for( b = 0; b != d2; ++ b )
				dat[ a ][ b ] = that - data[ a ][ b ];
		return new BasicMatrix( d1, d2, dat );
	}
	public BasicMatrix reverseSubtract( BasicMatrix that ) {
		if( !sameSize( that ) )
			throw new InvalidMathException( );
		double[][] dat = new double[ d1 ][ d2 ];
		int a, b;
		for( a = 0; a != d1; ++ a )
			for( b = 0; b != d2; ++ b )
				dat[ a ][ b ] = that.getValue( a, b ) - data[ a ][ b ];
		return new BasicMatrix( d1, d2, dat );
	}
	public BasicMatrix reverseMultiply( BasicMatrix that ) {
		return that.multiply( this );
	}
	public BasicMatrix _reverseDivide( double that ) {
		double[][] dat = new double[ d1 ][ d2 ];
		int a, b;
		for( a = 0; a != d1; ++ a )
			for( b = 0; b != d2; ++ b )
				dat[ a ][ b ] = that / data[ a ][ b ];
		return new BasicMatrix( d1, d2, dat );
	}
	public BasicMatrix reverseDivide( BasicMatrix that ) {
		return that.multiply( inverse( ) );
	}
	public BasicMatrix _reverseDivideWhole( double that ) {
		double[][] dat = new double[ d1 ][ d2 ];
		int a, b;
		for( a = 0; a != d1; ++ a )
			for( b = 0; b != d2; ++ b )
				dat[ a ][ b ] = Math.floor( that / data[ a ][ b ] );
		return new BasicMatrix( d1, d2, dat );
	}
	public BasicMatrix _reversePower( double that ) {
		double[][] dat = new double[ d1 ][ d2 ];
		int a, b;
		for( a = 0; a != d1; ++ a )
			for( b = 0; b != d2; ++ b )
				dat[ a ][ b ] = Math.pow( that, data[ a ][ b ] );
		return new BasicMatrix( d1, d2, dat );
	}
	public BasicMatrix _reverseModulus( double that ) {
		double[][] dat = new double[ d1 ][ d2 ];
		int a, b;
		for( a = 0; a != d1; ++ a )
			for( b = 0; b != d2; ++ b )
				dat[ a ][ b ] = that % data[ a ][ b ];
		return new BasicMatrix( d1, d2, dat );
	}
	
	public double sum( ) {
		double ret = 0.0;
		int a, b;
		for( a = 0; a != d1; ++ a )
			for( b = 0; b != d2; ++ b )
				ret += data[ a ][ b ];
		return ret;
	}
	public double squareDiff( BasicMatrix that ) {
		if( !sameSize( that ) )
			throw new InvalidMathException( );
		double ret = 0.0;
		int a, b;
		for( a = 0; a != d1; ++ a )
			for( b = 0; b != d2; ++ b )
				ret += (data[ a ][ b ] - that.getValue( a, b )) * (data[ a ][ b ] - that.getValue( a, b ));
		return ret;
	}
	public double determinant( ) {
		if( !square( ) )
			throw new InvalidMathException( );
		double[][] dat = new double[ d1 ][ d2 ];
		int a, b, c;
		double d, ret = 1.0;
		double[] e, f;
		for( a = 0; a != d1; ++ a )
			for( b = 0; b != d2; ++ b )
				dat[ a ][ b ] = data[ a ][ b ];
		for( a = 0; a != d1; ++ a ) {
			e = dat[ a ];
			if( e[ a ] == 0.0 ) {
				for( b = a + 1; b != d1; ++ b )
					if( dat[ b ][ a ] != 0.0 )
						break;
				if( b == d1 )
					return 0.0;
				ret = -ret;
				e = dat[ b ]; dat[ b ] = dat[ a ]; dat[ a ] = e;
			}
			d = e[ a ];
			for( b = a + 1; b != d1; ++ b )
				e[ b ] /= d;
			ret *= d;
			for( b = 0; b != a; ++ b ) {
				if( (d = dat[ b ][ a ]) == 0 )
					continue;
				f = dat[ b ];
				for( c = a + 1; c != d1; ++ c )
					f[ c ] -= e[ c ] * d;
			}
			for( b = a + 1; b != d1; ++ b ) {
				if( (d = dat[ b ][ a ]) == 0 )
					continue;
				f = dat[ b ];
				for( c = a + 1; c != d1; ++ c )
					f[ c ] -= e[ c ] * d;
			}
		}
		return ret;
	}
	public BasicMatrix inverse( ) {
		if( !square( ) )
			throw new InvalidMathException( );
		double[][] dat = new double[ d1 ][ d1 ], dat2 = new double[ d1 ][ d1 ];
		int a, b, c;
		double d;
		double[] e, f, g, h;
		for( a = 0; a != d1; ++ a )
			for( b = 0; b != d2; ++ b ) {
				dat[ a ][ b ] = data[ a ][ b ];
				dat2[ a ][ b ] = a == b ? 1.0 : 0.0;
			}
		
//		double n = d1 / 80.0;
		for( a = 0; a != d1; ++ a ) {
			e = dat[ a ];
//			if( a > n ) {
//				System.out.print( "." );
//				n += d1 / 80.0;
//			}
			if( e[ a ] == 0.0 ) {
				for( b = a + 1; b != d1; ++ b )
					if( dat[ b ][ a ] != 0.0 )
						break;
				if( b == d1 )
					throw new NoInverseException( );
				e = dat[ b ]; dat[ b ] = dat[ a ]; dat[ a ] = e;
				f = dat2[ b ]; dat2[ b ] = dat2[ a ]; dat2[ a ] = f;
			}
			f = dat2[ a ];
			d = e[ a ];
			for( b = 0; b != a + 1; ++ b )
				f[ b ] /= d;
			for( b = a + 1; b != d1; ++ b ) {
				e[ b ] /= d;
				f[ b ] /= d;
			}
			for( b = 0; b != a; ++ b ) {
				if( (d = dat[ b ][ a ]) == 0.0 )
					continue;
				g = dat[ b ];
				h = dat2[ b ];
				for( c = 0; c != a + 1; ++ c )
					h[ c ] -= f[ c ] * d;
				for( c = a + 1; c != d1; ++ c ) {
					g[ c ] -= e[ c ] * d;
					h[ c ] -= f[ c ] * d;
				}
			}
			for( b = a + 1; b != d1; ++ b ) {
				if( (d = dat[ b ][ a ]) == 0.0 )
					continue;
				g = dat[ b ];
				h = dat2[ b ];
				for( c = 0; c != a + 1; ++ c )
					h[ c ] -= f[ c ] * d;
				for( c = a + 1; c != d1; ++ c ) {
					g[ c ] -= e[ c ] * d;
					h[ c ] -= f[ c ] * d;
				}
			}
		}
//		System.out.print( "\n" );
		
		return new BasicMatrix( d1, d2, dat2 );
	}
	public BasicMatrix transpose( ) {
		double[][] dat = new double[ d2 ][ d1 ];
		int a, b;
		for( a = 0; a != d1; ++ a )
			for( b = 0; b != d2; ++ b )
				dat[ b ][ a ] = data[ a ][ b ];
		return new BasicMatrix( d2, d1, dat );
	}
	public BasicMatrix leftInverse( ) {
		if( d1 == d2 )
			return inverse( );
		if( d1 < d2 )
			throw new InvalidMathException( );
		BasicMatrix T = transpose( );
		return T.multiply( this ).inverse( ).multiply( T );
	}
	public BasicMatrix rightInverse( ) {
		if( d1 == d2 )
			return inverse( );
		if( d1 > d2 )
			throw new InvalidMathException( );
		BasicMatrix T = transpose( );
		return T.multiply( multiply( T ).inverse( ) );
	}
	
	public String toString( ) {
		String ret = "";
		int a, b;
		for( a = 0; a != d1; ++ a ) {
			ret += "\n    ";
			if( a == 0 )
				ret += "/";
			else if( a == d1 - 1 )
				ret += "\\";
			else
				ret += "|";
			
			for( b = 0; b != d2; ++ b )
				ret += ("  " + Math.round( data[ a ][ b ] * 1000 ) * 0.001 + "0000000").substring( 0, 7 );
			
			if( a == 0 )
				ret += "  \\";
			else if( a == d1 - 1 )
				ret += "  /";
			else
				ret += "  |";
		}
		return ret + "\n";
	}
	
	public boolean square( ) {
		return d1 == d2;
	}
	public boolean sameSize( BasicMatrix that ) {
		return d1 == that.getDimension1Int( ) && d2 == that.getDimension2Int( );
	}
	public boolean equals( double that ) {
		if( d1 != 1 || d2 != 1 )
			return false;
		return data[ 0 ][ 0 ] == that;
	}
	public boolean equals( BasicMatrix that ) {
		if( !sameSize( that ) )
			return false;
		int a, b;
		for( a = 0; a != d1; ++ a )
			for( b = 0; b != d2; ++ b )
				if( data[ a ][ b ] != that.getValue( a, b ) )
					return false;
		return true;
	}
	
	public BasicMatrix clone( )			{ return new BasicMatrix( this ); }
}