When a triadiagonal matrix is stored by rows in a one-dimensional
array element, the mapping is element[0:3n-3] = [M(1,1), M(1,2),
M(2,1), M(2,2), M(2,3), M(3,2), M(3,3), M(3,4), M(4,3), ...].
If |i-j| > 1, then M(i,j) is not on the tridiagonal.
For elements in the tridiagonal, we see that the row 1 elements
are stored in element[0] and element[1]. So, when
i = 1, M(i,j) is at element[j-1].
When i > 1, there are i-1 rows that come
before the first element of row i. These i-1 rows
contain 3(i-1)-1 elements. Within row i,
M(i,j) is the (j-i+2)th element. So, if i > 1, M(i,j) is at
element[j+2i-3].
When i = 1, j+2i-3 = j-1. So, this formula
may also be used for the case i = 1.
Note also that the lower diagonal is stored in positions 2, 5, 8, ...
of the one-dimensional array; the main diagonal occupies positions
0, 3, 6, ...; and the upper diagonal ocupies positions
1, 4, 7, ....
With these observations in mind, we arrive at the following
code:
public class TridiagonalByRows
{
// data members
int rows; // matrix dimension
Object zero; // zero element
Object [] element; // element array
// constructor
public TridiagonalByRows(int theRows, Object theZero)
{
// validate theRows
if (theRows < 1)
throw new IllegalArgumentException
("number of rows must be > 0");
// create and initialize the matrix
rows = theRows;
zero = theZero;
element = new Object [3 * rows - 2];
for (int i = 0; i < 3 * rows - 2; i++)
element[i] = zero;
}
// methods
/** create string suitable for output */
public String toString()
{
StringBuffer s = new StringBuffer();
s.append("Lower diagonal is \n");
for (int i = 2; i < 3 * rows - 2; i += 3)
s.append(element[i] + " ");
s.append("\n");
s.append("Main diagonal is \n");
for (int i = 0; i < 3 * rows - 2; i += 3)
s.append(element[i] + " ");
s.append("\n");
s.append("Upper diagonal is \n");
for (int i = 1; i < 3 * rows - 2; i += 3)
s.append(element[i] + " ");
s.append("\n");
// create equivalent String
return new String(s);
}
/** input a tridiagonal matrix from the given input stream */
public void input(Object theZero, MyInputStream inStream)
{
Method inputMethod;
Object [] inputMethodArgs = {inStream};
Class [] parameterTypes = {inStream.getClass()};
zero = theZero;
try
{
// get the proper method to be used to read in the values
inputMethod = theZero.getClass().
getMethod("input", parameterTypes);
// input number of rows in the matrix
System.out.println("Enter number of rows");
rows = inStream.readInteger();
// validate input
if (rows < 1)
throw new IllegalArgumentException
("number of rows must be > 0");
// create the element array
element = new Object [3 * rows - 2];
// input elements
System.out.println("Enter lower diagonal");
for (int i = 2; i < 3 * rows - 2; i += 3)
element[i] = inputMethod.invoke(null, inputMethodArgs);
System.out.println("Enter main diagonal");
for (int i = 0; i < 3 * rows - 2; i += 3)
element[i] = inputMethod.invoke(null, inputMethodArgs);
System.out.println("Enter upper diagonal");
for (int i = 1; i < 3 * rows - 2; i += 3)
element[i] = inputMethod.invoke(null, inputMethodArgs);
}
catch (Exception e)
{
System.out.println(e);
throw new IllegalArgumentException
("matrix element type "
+ "must have the static method input() defined");
}
}
/** throws IndexOutOfBoundsException when i < 1
* or j < 1 or i > rows or j > rows */
void checkIndex(int i, int j)
{
if (i < 1 || j < 1 || i > rows || j > rows)
throw new IndexOutOfBoundsException
("i = " + i + " j = " + j +
" rows = " + rows + " cols = " + rows);
}
/** return the element this(i,j)
* throws IndexOutOfBoundsException when i or j invalid */
public Object get(int i, int j)
{
checkIndex(i, j);
// determine element to return
switch (i - j)
{
case 1: case 0: case -1: // in tridiagonal
return element[2 * i + j - 3];
default: return zero;
}
}
/** set this(i,j) = newValue
* throws IndexOutOfBoundsException when i or j invalid */
public void set(int i, int j, Object newValue)
{
checkIndex(i, j);
// store newValue
switch (i - j)
{
case 1: case 0: case -1: // in tridiagonal
element[2 * i + j - 3] = newValue;
break;
default: if (!((Zero) newValue).equalsZero())
throw new IllegalArgumentException
("elements not on tridiagonal must be zero");
}
}
/** return this + b */
public TridiagonalByRows add(TridiagonalByRows b)
{
if (rows != b.rows)
throw new IllegalArgumentException
("Matrices must have same dimensions");
// create result array w
TridiagonalByRows w = new TridiagonalByRows(rows, zero);
for (int i = 0; i < 3 * rows - 2; i++)
w.element[i] = ((Computable) element[i]).add(b.element[i]);
return w;
}
/** return this - b */
public TridiagonalByRows subtract(TridiagonalByRows b)
{
if (rows != b.rows)
throw new IllegalArgumentException
("Matrices must have same dimensions");
// create result array w
TridiagonalByRows w = new TridiagonalByRows(rows, zero);
for (int i = 0; i < 3 * rows - 2; i++)
w.element[i] = ((Computable) element[i]).subtract(b.element[i]);
return w;
}
/** return the transpose of this */
public TridiagonalByRows transpose()
{
// create result array w
TridiagonalByRows w = new TridiagonalByRows(rows, zero);
// copy lower diagonal of this to upper diagonal of w and copy
// the upper diagonal of this to lower diagonal of w
for (int i = 1; i < 3 * rows - 2; i += 3)
{
w.element[i] = element[i + 1];
w.element[i + 1] = element[i];
}
// copy main diagonal of this to main diagonal of w
for (int i = 0; i < 3 * rows - 2; i += 3)
w.element[i] = element[i];
return w;
}
}