     with ConsAK; use ConsAK;
     with ErroAK; use ErroAK;
     with FileAK; use FileAK; -- for Load and Save
     with InteAK; use InteAK;
     with FloaAK; use FloaAK;
     with MathAK; use MathAK;
     with Text_IO; use Text_IO;

     package body A83_015_Matrix is
     ---------------------------------------------------------------------------
     ---------------------------------------------------------------------------

     function "*" (
       M : Matrix_Type;
       F : float )
       return Matrix_Type is
     ---------------------------------------------------------------------------
       MR : Matrix_Type ( M'range ( 1 ), M'range ( 2 ) ) := M;
     begin
       for Row in MR'range ( 1 ) loop
	 for Col in MR'range ( 2 ) loop
	   MR ( Row, Col ) := MR ( Row, Col ) * F;
	 end loop;
       end loop;
       return MR;
     end "*";

     function "*" (
       M1 : Matrix_Type;
       M2 : Matrix_Type )
       return Matrix_Type is
     ---------------------------------------------------------------------------
       R : Matrix_Type ( M1'range ( 1 ), M2'range ( 2 ) )
	 := ( others => ( others => 0.0 ) );
     begin
--     Put_Line ( M1 );
--     Pause;
--     Put_Line ( M2 );
--     Pause;
       if ( M1'last ( 2 ) - M1'first ( 2 ) + 1 )
	 /= ( M2'last ( 1 ) - M2'first ( 1 ) + 1 ) then
	   Put_Line ( "# of columns of 1st matrix ("
	     & integer'image ( M1'last ( 2 ) - M1'first ( 2 ) + 1 )
	     & ") must equal # of rows of 2nd matrix ("
	     & integer'image ( M2'last ( 1 ) - M2'first ( 1 ) + 1 )
	     & ")." );
	   Pause;
	   raise constraint_error;
       end if;
       for Row in R'range ( 1 ) loop
	 for Col in R'range ( 2 ) loop
	   R ( Row, Col ) := 0.0;
	   for R_Col in M1'range ( 2 ) loop
	     R ( Row, Col ) := R ( Row, Col )
	       + M1 ( Row, R_Col ) * M2 ( R_Col, Col );
	   end loop;
	 end loop;
       end loop;
       return R;
     end "*";

     function "*" (
       M : Matrix_Type;
       V : Vector_Type )
       return Vector_Type is
     ----------------------------------------------------------------------
     begin
       return Vector_Convert ( M * Matrix_Convert ( V ) );
     end "*";

     function "/" (
       M : Matrix_Type;
       F : float )
       return Matrix_Type is
     ---------------------------------------------------------------------------
       MR : Matrix_Type ( M'range ( 1 ), M'range ( 2 ) ) := M;
     begin
       for Row in MR'range ( 1 ) loop
	 for Col in MR'range ( 2 ) loop
	   MR ( Row, Col ) := MR ( Row, Col ) / F;
	 end loop;
       end loop;
       return MR;
     end "/";

     function "-" (
       M : Matrix_Type )
       return Matrix_Type is
     ----------------------------------------------------------------------
       Temp : Matrix_Type ( M'range ( 1 ), M'range ( 2 ) );
     begin
       for Row in M'range ( 1 ) loop
	 for Col in M'range ( 2 ) loop
	   Temp ( Row, Col ) := - M ( Row, Col );
	 end loop;
       end loop;
       return Temp;
     end "-";

     function "<=" (
       M1, M2 : Matrix_Type )
       return boolean is
     ---------------------------------------------------------------------------
     begin
       for Row in M1'range ( 1 ) loop
	 for Col in M1'range ( 2 ) loop
	   if M1 ( Row, Col ) > M2 ( Row, Col ) then
	     return false;
	   end if;
	 end loop;
       end loop;
       return true;
     end "<=";

     function Adjoint (
       M : Matrix_Type )
       return Matrix_Type is
     ---------------------------------------------------------------------------
       R : Matrix_Type ( M'range ( 1 ), M'range ( 2 ) );
     begin
       for Row in R'range ( 1 ) loop
	 for Col in R'range ( 2 ) loop
	   R ( Row, Col ) := Cofactor ( M, Row, Col );
	 end loop;
       end loop;
       return Transpose ( R );
     end Adjoint;

     function Ask (
       Row_Default : in positive := 2;
       Col_Default : in positive := 2 )
       return Matrix_Type is
     ----------------------------------------------------------------------
       Rows, Cols : positive;
       Good : boolean;
     begin
       loop
	 Good := true;
	 Rows := InteAK.Ask_Pos ( "How many rows are in your matrix?    ",
	   Row_Default, 1 );
	 Cols := InteAK.Ask_Pos ( "How many columns are in your matrix? ",
	   Col_Default, 1 );
	 declare
	   Matrix : Matrix_Type ( 1..Rows, 1..Cols );
	 begin
	   Put_Line (
	     "Please enter all values in floating-point format." );
	   Put_Line ( "For example, instead of entering 0, enter 0.0." );
	   for Row in 1..Rows loop
	     Put_Line ( "Please enter row" & positive'image ( Row )
	       & " of your matrix (example:  1.0 -3.4 1.0e-10)." );
	     for Col in 1..Cols loop
	       begin
		 Get ( Matrix ( Row, Col ) );
	       exception
		 when others =>
     --          Had to be changed when I added Load and Save.
     --          when Data_Error =>
		   Put_Line (
		     "Please enter all values in floating-point format." );
		   Put_Line (
		     "For example, instead of entering 0, enter 0.0." );
		   Pause;
		   Put_Line ( "" );
		   Good := false;
	       end;
	       exit when not Good;
	     end loop;
	     exit when not Good;
	   end loop;
	   if Good then
	     New_Line;
	     Put_Line ( Matrix );
	     Flush;
	     Ask ( Good, "Is this the matrix you meant? ", true );
	     if Good then
	       return Matrix;
	     end if;
	   end if;
	 end;
       end loop;
       exception
	 when others =>
	   Notify ( "function MatrAK.Ask return Matrix_Type" );
	   raise;
     end Ask;

     procedure Ask (
       M      : in out Matrix_Type;
       Prompt : in     string ) is
     ----------------------------------------------------------------------
       Good : boolean;
     begin
       loop
	 Good := true;
	 Put_Line ( Prompt );
	 for Row in M'range ( 1 ) loop
	   Put_Line ( "Please enter row" & integer'image ( Row ) & "." );
	   for Col in M'range ( 2 ) loop
	     begin
	       Get ( M ( Row, Col ) );
	     exception
	       when others =>
--             when Data_Error =>
		 Put_Line (
		   "Please enter all values in floating-point format." );
		 Put_Line (
		   "For example, instead of entering 0, enter 0.0." );
		 Pause;
		 Put_Line ( "" );
		 Good := false;
	     end;
	     exit when not Good;
	   end loop;
	   Flush;
	   exit when not Good;
	 end loop;
	 exit when Good;
       end loop;
     end Ask;

     function Cofactor (
       M        : Matrix_Type;
       Row, Col : integer )
       return float is
     ---------------------------------------------------------------------------
     begin
       return ( ( - 1.0 ) ** ( Row + Col ) ) * Minor ( M, Row, Col );
     exception
       when others =>
	 Notify ( "MatrAK.Cofactor" );
	 raise;
     end Cofactor;

     function Column (
       M   : Matrix_Type;
       Col : integer )
       return Vector_Type is
     ----------------------------------------------------------------------
       V : Vector_Type ( M'range ( 1 ) );
     begin
       for Row in V'range loop
	 V ( Row ) := M ( Row, Col );
       end loop;
       return V;
     end Column;

     function Concatenate (
       M1, M2 : Matrix_Type;
       Col    : boolean := true )
       return Matrix_Type is
     ---------------------------------------------------------------------------
     -- If input Col is true, then concatenates two matrices to make one bigger
     -- matrix with same number of rows but with the number of columns equal to
     -- sum of columns of the two inputs.
     --
     -- M1 := ( ( 1, 2 ),    M2 := ( ( 5, 6 ),
     --         ( 3, 4 ) );          ( 7, 8 ) );
     --
     -- M3 := Concatenate ( M1, M2, true ) = ( ( 1, 2, 5, 6 ),
     --                                        ( 3, 4, 7, 8 ) );
     --
     -- If input Col is false, then concatenates two matrices to make one bigger
     -- matrix with same number of columns but with the number of rows equal
     -- to sum of rows of the two inputs.
     --
     -- M1 := ( ( 1, 2 ),    M2 := ( ( 5, 6 ),
     --         ( 3, 4 ) );          ( 7, 8 ) );
     --
     -- M3 := Concatenate ( M1, M2, false ) = ( ( 1, 2 ),
     --                                         ( 3, 4 ),
     --                                         ( 5, 6 ),
     --                                         ( 7, 8 ) );
     ---------------------------------------------------------------------------
     begin
       if Col then
	 declare
	   M3 : Matrix_Type ( M1'range ( 1 ),
	     M1'first ( 2 )..( M1'last ( 2 ) + M2'last ( 2 ) ) );
	 begin
	   -- Rows of M1 and M2 must be equal.
	   if M1'last ( 1 ) /= M2'last ( 1 ) then
	     raise constraint_error;
	   end if;
	   for Row in M1'range ( 1 ) loop
	     for Col in M1'range ( 2 ) loop
	       M3 ( Row, Col ) := M1 ( Row, Col );
	     end loop;
	     for Col in ( M1'last ( 2 ) + 1 )..M3'last ( 2 ) loop
	       M3 ( Row, Col ) := M2 ( Row, Col - M1'last ( 2 ) );
	     end loop;
	   end loop;
	   return M3;
	 end;
       else
	 declare
	   M3 : Matrix_Type (
	     M1'first ( 1 )..( M1'last ( 1 ) + M2'last ( 1 ) ),
	     M1'range ( 2 ) );
	 begin
	   -- Columns of M1 and M2 must be equal.
	   if M1'last ( 2 ) /= M2'last ( 2 ) then
	     raise constraint_error;
	   end if;
	   for Col in M3'range ( 2 ) loop
	     for Row in M1'range ( 1 ) loop
	       M3 ( Row, Col ) := M1 ( Row, Col );
	     end loop;
	     for Row in ( M1'last ( 1 ) + 1 )..M3'last ( 1 ) loop
	       M3 ( Row, Col ) := M2 ( Row - M1'last ( 1 ), Col );
	     end loop;
	   end loop;
	   return M3;
	 end;
       end if;
     end Concatenate;

     function Concatenate (
       V1, V2 : Vector_Type )
       return Matrix_Type is
     ----------------------------------------------------------------------
     -- Example:  V1 = ( 1, 2, 3 ), V2 = ( 4, 5, 6 )
     --   returns ( ( 1, 4 ),
     --             ( 2, 5 ),
     --             ( 3, 6 ) )
     ----------------------------------------------------------------------
     begin
       return Concatenate ( Matrix_Convert ( V1 ), Matrix_Convert ( V2 ) );
     end Concatenate;

     procedure Demo is
     ---------------------------------------------------------------------------
       Default :          natural :=  1;
       Min     : constant natural :=  0;
       Max     : constant natural := 20;
       Option  :          natural;
     begin
       loop
	 Put_Line ( Copyright );
	 Put_Line ( Description );
	 New_Line;
	 Put_Line ( " 0 = Quit" );
	 Put_Line ( " 1 = ""*"" (multiplies a matrix by a float)" );
	 Put_Line ( " 2 = ""*"" (multiplies matrices)" );
	 Put_Line ( " 3 = ""/"" (divides matrix by float)" );
	 Put_Line ( " 4 = ""<=""" );
	 Put_Line ( " 5 = Adjoint" );
	 Put_Line ( " 6 = Ask" );
	 Put_Line ( " 7 = Cofactor" );
	 Put_Line ( " 8 = Concatenate (matrix)" );
	 Put_Line ( " 9 = Determinant" );
	 Put_Line ( "10 = Fill (matrix)" );
	 Put_Line ( "11 = Identity" );
	 Put_Line ( "12 = Inverse" );
	 Put_Line ( "13 = Is_Square" );
	 Put_Line ( "14 = Load" );
	 Put_Line ( "15 = Minor" );
	 Put_Line ( "16 = Put_Line" );
	 Put_Line ( "17 = Save" );
	 Put_Line ( "18 = Slice" );
	 Put_Line ( "19 = Sub_Matrix" );
	 Put_Line ( "20 = Transpose" );
	 New_Line;
	 Option := Ask_Nat ( "Option ", Default, Min, Max );
	 Default := Option + 1;
	 if Default > Max then
	   Default := Min;
	 end if;
	 case Option is
	   when  0 => exit;
	   when  1 => Put_Line ( A83_015_Matrix.Ask * Ask (
	     "Please enter a floating-point value: " ) );
	   when  2 =>
	     Put_Line ( "Warning:  please enter the second matrix first!" );
	     -- Don't know why this is necessary, but it is.
	     -- Possibly compiler dependent bug.
	     Pause;
	     Put_Line ( A83_015_Matrix.Ask * A83_015_Matrix.Ask );
	   when  3 => Put_Line ( A83_015_Matrix.Ask / Ask (
	     "Please enter a non-zero floating-point value: " ) );
	   when  4 => Put_Line ( A83_015_Matrix.Ask <= A83_015_Matrix.Ask );
	   when  5 => Put_Line ( Adjoint ( Ask ) );
	   when  6 => Put_Line ( A83_015_Matrix.Ask );
--         when  7 => cofactor
--         when  8 => concatenate
	   when  9 => Put_Line ( Determinant ( Ask ) );
	   when 10 => Put_Line ( Fill ( Ask ) ); -- need more
	   when 11 => Put_Line ( Identity ( Ask ) );
	   when 12 => Put_Line ( Inverse ( Ask ) );
	   when 13 => Put_Line ( Is_Square ( Ask ) );
	   when 14 =>
	     declare
	       Rows, Cols : positive := 10;
	       File_Name : string ( 1..12 ) := "Matrix.Mat  ";
	     begin
	       Rows := Ask_Pos ( "Rows    ", Rows );
	       Cols := Ask_Pos ( "Columns ", Cols );
	       declare
		 M : Matrix_Type ( 1..Rows, 1..Cols );
	       begin
		 Load ( M, File_Name );
		 Put_Line ( M );
	       end;
	     end;
     --    when 15 => Put_Line ( Minor ( Ask ) );
	   when 16 => Put_Line ( A83_015_Matrix.Ask );
	   when 17 =>
	     declare
	       File_Name : string ( 1..12 ) := "Matrix.Mat  ";
	     begin
	       Ask ( File_Name, "File Name ", File_Name );
	       Save ( Ask, File_Name );
	     end;
	   when 18 => Put_Line ( Slice ( Ask, Ask ) );
     --    when 19 => Put_Line ( Sub_Matrix ( Ask ) );
	   when 20 => Put_Line ( Transpose ( Ask ) );
	   when others =>
	     Put_Line (
	       "The demonstration of that option is currently not available." );
	 end case;
	 Pause;
	 New_Line;
       end loop;
     end Demo;

     function Determinant (
       M : Matrix_Type )
       return float is
     ---------------------------------------------------------------------------
       Sum : float := 0.0;
     begin
       if not Is_Square ( M ) then
	 raise constraint_error;
       end if;
       if M'length = 2 then
	 Sum := M ( M'first ( 1 ), M'first ( 2 ) )
	      * M ( M'last  ( 1 ), M'last  ( 2 ) )
	      - M ( M'last  ( 1 ), M'first ( 2 ) )
	      * M ( M'first ( 1 ), M'last  ( 2 ) );
       else
	 for Row in M'range loop
	   Sum := Sum + M ( Row, 1 ) * Cofactor ( M, Row, 1 );
	 end loop;
       end if;
       return Sum;
     end Determinant;

     function Fill (
       M : Matrix_Type;
       F : float := 0.0 )
       return Matrix_Type is
     ---------------------------------------------------------------------------
     -- Returns the matrix filled with F.
     ---------------------------------------------------------------------------
       R : Matrix_Type ( M'range ( 1 ), M'range ( 2 ) )
	 := ( others => ( others => F ) );
     begin
       return R;
     end Fill;

     function Identity (
       M : Matrix_Type )
       return Matrix_Type is
     ---------------------------------------------------------------------------
     -- Must be square.
     ---------------------------------------------------------------------------
       B : Matrix_Type ( M'range ( 1 ), M'range ( 2 ) )
	 := ( others => ( others => 0.0 ) );
     begin
       if not Is_Square ( M ) then
	 raise constraint_error;
       end if;
       for index in B'range loop
	 B ( index, index ) := 1.0;
       end loop;
       return B;
     end Identity;

     function Inverse (
       M : Matrix_Type )
       return Matrix_Type is
     ---------------------------------------------------------------------------
     -- M * Inverse ( M ) = I
     -- Inverse is not the transpose.
     ---------------------------------------------------------------------------
     begin
       return ( Adjoint ( M ) / Determinant ( M ) );
     end Inverse;

     function Is_Square (
       M : Matrix_Type )
       return boolean is
     ---------------------------------------------------------------------------
     begin
       if ( M'first ( 1 ) /= M'first ( 2 ) )
	 or ( M'last ( 1 ) /= M'last ( 2 ) ) then
	   return false;
       else
	 return true;
       end if;
     end Is_Square;

     procedure Load (
       M         :    out Matrix_Type;
       File_Name : in     string ) is
     ----------------------------------------------------------------------
       File : File_Type;
     begin
       M := ( others => ( others => 0.0 ) );
       Open ( File, In_File, File_Name );
       for Row in M'range ( 1 ) loop
	 for Col in M'range ( 2 ) loop
	   exit when End_Of_File ( File );
	   Get ( File, M ( Row, Col ) );
	 end loop;
       end loop;
       Close ( File );
     end Load;

     function Matrix_Convert (
       V : Vector_Type )
       return Matrix_Type is
     ----------------------------------------------------------------------
       Temp : Matrix_Type ( V'range, 1..1 );
     begin
       for Row in V'range loop
	 Temp ( Row, 1 ) := V ( Row );
       end loop;
       return Temp;
     end Matrix_Convert;

     function Minor (
       A        : Matrix_Type;
       Row, Col : integer )
       return float is
     ---------------------------------------------------------------------------
     -- "The determinants of the square submatrices of any matrix A, obtained
     -- by striking out certain rows or columns, or both, are called the
     -- determinants or minors of A."
     -- Eshbach, "Handbook of Engineering Fundamentals", 3rd Ed., 1975, p230.
     ---------------------------------------------------------------------------
     begin
       return Determinant ( Sub_Matrix ( A, Row, Col ) );
     end;

     procedure Put_Line (
       M : in Matrix_Type ) is
     ---------------------------------------------------------------------------
     begin
       for Row in M'range ( 1 ) loop
	 for Col in M'range ( 2 ) loop
	   Put ( M ( Row, Col ), 3, 2 );
	 end loop;
	 New_Line;
       end loop;
     end Put_Line;

     function Row (
       M   : Matrix_Type;
       Row : integer )
       return Vector_Type is
     ----------------------------------------------------------------------
       V : Vector_Type ( M'range ( 2 ) );
     begin
       for Col in V'range loop
	 V ( Col ) := M ( Row, Col );
       end loop;
       return V;
     end Row;

     procedure Save (
       M         : in     Matrix_Type;
       File_Name : in     string ) is
     ----------------------------------------------------------------------
       File : File_Type;
     begin
       Create ( File, Out_File, File_Name );
       for Row in M'range ( 1 ) loop
	 for Col in M'range ( 2 ) loop
	   Put ( File, M ( Row, Col ) );
	   Put ( File, ' ' );
	 end loop;
	 Put_Line ( File, "" );
       end loop;
       Close ( File );
     end Save;

     function Slice (
       Tiny, Huge : Matrix_Type )
       return Matrix_Type is
     ---------------------------------------------------------------------------
     -- Returns a matrix of the ranges and dimensions of Tiny from Huge.
     ---------------------------------------------------------------------------
       R : Matrix_Type ( Tiny'range ( 1 ), Tiny'range ( 2 ) );
     begin
       for Row in R'range ( 1 ) loop
	 for Col in R'range ( 2 ) loop
	   R ( Row, Col ) := Huge ( Row, Col );
	 end loop;
       end loop;
       return R;
     end Slice;

     function Sub_Matrix (
       A        : Matrix_Type;
       Row, Col : integer )
       return Matrix_Type is
     ---------------------------------------------------------------------------
     -- Returns the matrix generated by striking the row and column that
     -- contains the element ( Row, Col ).
     ---------------------------------------------------------------------------
       S : Matrix_Type ( A'first ( 1 )..( A'last ( 1 ) - 1 ),
			 A'first ( 2 )..( A'last ( 2 ) - 1 ) );
     begin
       for R in S'range ( 1 ) loop
	 for C in S'range ( 2 ) loop
	   S ( R , C ) := A ( R + boolean'pos ( R >= Row ),
	     C + boolean'pos ( C >= Col ) );
	 end loop;
       end loop;
       return S;
     end Sub_Matrix;

     function Transpose (
       M : Matrix_Type )
       return Matrix_Type is
     ---------------------------------------------------------------------------
       B : Matrix_Type ( M'range ( 2 ), M'range ( 1 ) );
     begin
       for Row in B'range ( 1 ) loop
	 for Col in B'range ( 2 ) loop
	   B ( Row, Col ) := M ( Col, Row );
	 end loop;
       end loop;
       return B;
     end Transpose;

     function Vector_Convert (
       M : Matrix_Type )
       return Vector_Type is
     ----------------------------------------------------------------------
       Temp : Vector_Type ( M'range ( 1 ) );
     begin
       if M'first ( 2 ) /= M'last ( 2 ) then
	 raise Constraint_Error;
       end if;
       for Row in M'range ( 1 ) loop
	 Temp ( Row ) := M ( Row, M'first ( 2 ) );
       end loop;
       return Temp;
     end Vector_Convert;

     ---------------------------------------------------------------------------
     ---------------------------------------------------------------------------
     end A83_015_Matrix;
