-- MatrixImpl.mesa
-- last edit March 2, 1983 4:58 pm Sturgis

DIRECTORY
 Matrix USING[];



MatrixImpl: PROGRAM EXPORTS Matrix =

BEGIN

Matrix: TYPE = REF MatrixBody;
MatrixElement: TYPE = REF ANY;

MatrixBody: PUBLIC TYPE = RECORD
[
nRows: CARDINAL,
nColumns: CARDINAL,
firstRow: Row,
firstColumn: Column,

recentX: CARDINAL,
recentColumn: Column, -- if not NIL then corresponds to recentX
recentY: CARDINAL,
recentRow: Row -- if not NIL then corresponds to recentY
];

Row: TYPE = REF RowBody;
Column: TYPE = REF ColumnBody;
CrossPoint: TYPE = REF CrossPointBody;

RowBody: TYPE = RECORD
[
next: Row, -- i.e. y+1
rowInfo: MatrixElement,
firstCrossPoint: CrossPoint,
recentX: CARDINAL,
recentCrossPoint: CrossPoint -- if not NIL then corresponds to recentX
];

ColumnBody: TYPE = RECORD
[
next: Column, -- i.e. x+1
columnInfo: MatrixElement
];

CrossPointBody: TYPE = RECORD
[
element: MatrixElement,
next: CrossPoint -- i.e. <x+1, y>
];



CreateMatrix: PUBLIC PROCEDURE RETURNS[Matrix] =
BEGIN
RETURN[NEW[MatrixBody ← [
nRows: 0,
   nColumns: 0,
   firstRow: NIL,
   firstColumn: NIL,
   recentX: 0,
   recentY: 0,
   recentColumn: NIL,
   recentRow: NIL]]];
END;

AddColumnAt: PUBLIC PROCEDURE[m: Matrix, x: CARDINAL] =
BEGIN
prevCol: Column ← MoveToColumn[m, x-1];
nextCol: Column ← IF prevCol = NIL THEN m.firstColumn ELSE prevCol.next;
newCol: Column ← NEW[ColumnBody ← [nextCol, NIL]];
IF prevCol # NIL THEN prevCol.next ← newCol ELSE m.firstColumn ← newCol;
m.recentX ← x-1;
m.recentColumn ← prevCol;

FOR row: Row ← m.firstRow, row.next WHILE row # NIL DO
 prevCrossPnt: CrossPoint ← MoveToCrossPntInRow[m, row, x-1];
 nextCrossPnt: CrossPoint ← IF prevCrossPnt = NIL THEN row.firstCrossPoint ELSE prevCrossPnt.next;
 newCrossPnt: CrossPoint ← NEW[CrossPointBody ← [NIL, nextCrossPnt]];
 IF prevCrossPnt = NIL THEN row.firstCrossPoint ← newCrossPnt ELSE prevCrossPnt.next ← newCrossPnt;
 ENDLOOP;

m.recentY ← 0;
m.recentRow ← NIL;
m.nColumns ← m.nColumns+1;
END;

AddRowAt: PUBLIC PROCEDURE[m: Matrix, y: CARDINAL] =
BEGIN
prevRow: Row ← MoveToRow[m, y-1];
nextRow: Row ← IF prevRow = NIL THEN m.firstRow ELSE prevRow.next;
newRow: Row ← NEW[RowBody ← [nextRow, NIL, NIL, 0, NIL]];
newCrossPnt: CrossPoint ← NIL;
IF prevRow # NIL THEN prevRow.next ← newRow ELSE m.firstRow ← newRow;

FOR I: CARDINAL IN [1..m.nColumns] DO
 newCrossPnt ← NEW[CrossPointBody ← [NIL, newCrossPnt]];
 ENDLOOP;

newRow.firstCrossPoint ← newCrossPnt;
newRow.recentX ← 0;
newRow.recentCrossPoint ← NIL;
m.nRows ← m.nRows+1;
END;

DeleteColumnAt: PUBLIC PROCEDURE[m: Matrix, x: CARDINAL] =
BEGIN
prevCol: Column ← MoveToColumn[m, x-1];
oldCol: Column ← IF prevCol = NIL THEN m.firstColumn ELSE prevCol.next;
IF prevCol = NIL THEN m.firstColumn ← oldCol.next ELSE prevCol.next ← oldCol.next;

FOR row: Row ← m.firstRow, row.next WHILE row # NIL DO
 prevCrossPnt: CrossPoint ← MoveToCrossPntInRow[m, row, x-1];
 oldCrossPnt: CrossPoint ← IF prevCrossPnt = NIL THEN row.firstCrossPoint ELSE prevCrossPnt.next;
 IF prevCrossPnt = NIL THEN row.firstCrossPoint ← oldCrossPnt.next ELSE prevCrossPnt.next ← oldCrossPnt.next
 ENDLOOP;
m.nColumns ← m.nColumns-1;
END;

DeleteRowAt: PUBLIC PROCEDURE[m: Matrix, y: CARDINAL] =
BEGIN
prevRow: Row ← MoveToRow[m, y-1];
oldRow: Row ← IF prevRow = NIL THEN m.firstRow ELSE prevRow.next;
IF prevRow = NIL THEN m.firstRow ← oldRow.next ELSE prevRow.next ← oldRow.next;
m.nRows ← m.nRows-1;
END;

SetElementAt: PUBLIC PROCEDURE[m: Matrix, x, y: CARDINAL, e: MatrixElement] =
BEGIN
crossPoint: CrossPoint ← MoveToCrossPoint[m, x, y];
crossPoint.element ← e;
END;

GetElementAt: PUBLIC PROCEDURE[m: Matrix, x, y: CARDINAL] RETURNS[MatrixElement] =
BEGIN
crossPoint: CrossPoint ← MoveToCrossPoint[m, x, y];
RETURN[crossPoint.element];
END;

SetRowInfoAt: PUBLIC PROCEDURE[m: Matrix, y: CARDINAL, e: MatrixElement] =
BEGIN
row: Row ← MoveToRow[m, y];
row.rowInfo ← e;
END;

SetColumnInfoAt: PUBLIC PROCEDURE[m: Matrix, x: CARDINAL, e: MatrixElement] =
BEGIN
column: Column ← MoveToColumn[m, x];
column.columnInfo ← e;
END;

GetRowInfoAt: PUBLIC PROCEDURE[m: Matrix, y: CARDINAL] RETURNS[MatrixElement] =
BEGIN
row: Row ← MoveToRow[m, y];
RETURN[row.rowInfo];
END;

GetColumnInfoAt: PUBLIC PROCEDURE[m: Matrix, x: CARDINAL] RETURNS[MatrixElement] =
BEGIN
column: Column ← MoveToColumn[m, x];
RETURN[column.columnInfo];
END;


OutOfBounds: PUBLIC ERROR = CODE;


-- support code

MoveToColumn: PROCEDURE[m: Matrix, x: CARDINAL] RETURNS[Column] =
 BEGIN
 col: Column;
 IF x > m.nColumns THEN ERROR;
 IF x >= m.recentX AND m.recentX > 0 THEN
  BEGIN
  col ← m.recentColumn;
  FOR I: CARDINAL IN (m.recentX..x] DO col ← col.next ENDLOOP;
  END
  ELSE IF x >= 1 THEN
   BEGIN
   col ← m.firstColumn;
   FOR I: CARDINAL IN (1..x] DO col ← col.next ENDLOOP;
   END
  ELSE
   col ← NIL;
 m.recentX ← x;
 m.recentColumn ← col;
 RETURN[col];
 END;

MoveToCrossPoint: PROCEDURE[m: Matrix, x, y: CARDINAL] RETURNS[CrossPoint] =
 {RETURN[MoveToCrossPntInRow[m, MoveToRow[m, y], x]]};

MoveToRow: PROCEDURE[m: Matrix, y: CARDINAL] RETURNS[Row] =
 BEGIN
 row: Row;
 IF y > m.nRows THEN ERROR;
 IF y >= m.recentY AND m.recentY > 0THEN
  BEGIN
  row ← m.recentRow;
  FOR I: CARDINAL IN (m.recentY..y] DO row ← row.next ENDLOOP;
  END
  ELSE IF y >= 1 THEN
   BEGIN
   row ← m.firstRow;
   FOR J: CARDINAL IN (1..y] DO row ← row.next ENDLOOP;
   END
  ELSE
   row ← NIL;
 m.recentY ← y;
 m.recentRow ← row;
 RETURN[row];
 END;

MoveToCrossPntInRow: PROCEDURE[m: Matrix, row: Row, x: CARDINAL] RETURNS[CrossPoint] =
 BEGIN
 crossPoint: CrossPoint;
 IF x > m.nColumns THEN ERROR;
 IF x >= row.recentX AND row.recentX > 0 THEN
  BEGIN
  crossPoint ← row.recentCrossPoint;
  FOR I: CARDINAL IN (row.recentX..x] DO crossPoint ← crossPoint.next ENDLOOP;
  END
  ELSE IF x >= 1 THEN
   BEGIN
   crossPoint ← row.firstCrossPoint;
   FOR I: CARDINAL IN (1..x] DO crossPoint ← crossPoint.next ENDLOOP;
   END
  ELSE
   crossPoint ← NIL;
 row.recentX ← x;
 row.recentCrossPoint ← crossPoint;
 RETURN[crossPoint];
 END;


END..


-- March 2, 1983 4:01 pm: Sturgis, re-write matrix impl from scratch, to not use doubly linked lists, or orthogonal links.
-- RTE: March 2, 1983 4:51 pm: Forgot to increment and decrement nColumns and nRows.
-- RTE: March 2, 1983 4:57 pm: add column forgot to install the newly created crosspoints in their rows.