//Correctness and Structucal Integrity (v1)

//Finished and removed single nested loop (advised by chatGPT to reduce time complexity). Also removed if statement identifed previously. 
//Tested code but not extensively.
//It was created based on refinement from several attempts (notably obtaining positive changes in Test case 28)
//And feeding it back into earlier iteration
//All Programiz challenge test cases have passed

//ALL FORMATTED INDENTED CORRECTLY
//TERMS Matrix A and Matrix B will be used to discuss the pairing of the first and second matrix in transaction

//ALSO NOTE, in my comments, there is reference to Matrix A, Matrix B, Matrix C
//In absence of a Matrix C (references are to Matrix A and Matrix B)
//In presence of Matrix C (references are to Matrix A, Matrix B, Matrix C, Matrix, B, Matrix C........)

//ANY CATCH STATEMENT WHICH REQUIRES A MORE USER FRIENDLY MESSAGE CAN INCLUDE ONE OF THESE (IF REQUIRED)
/*
System.out.println("2Matrix("+(counter)+")" + "   row:" + ee +"\thas no content at index["+m+"]");
System.out.println("Hence can not perform multiplication with Matrix("+(counter+1)+")" + "   row:" + (rowCounterNextMatrix+1)+"\t" 
+  "  index["+z+"]: " + rowInfo.get(z));


System.out.println("1Matrix("+(counter)+")" + "   row:" + rowCounterFirstMatrix+"\t" +  "value at index["+z+"]: " + ee.get(posPrevMatrixRowLevel) 
+"  has no available supporting entry in \nMatrix("+(counter+1)+")" + "   row:" + (rowCounterNextMatrix+1)+"\t" +  "\t index["+z+"]: " + " row content:" + rowInfo);
*/


/*
//**********USAGE*************************
BEFORE CONFIGURING TEST CASES REMEMBER THESE CRITERIA AS MINIMUM:

Numbers columms in Matrix A/C  = Number rows in Matrix B 
Jagged arrays supported under validation rules
Size first row (number columns) in Matrix A/C and number rows in Matrix B will ALWAYS determine matrixMultiplication size 
//**********************************
*/

import java.util.*;

public class Solution 
{
    //current matrix
    static List<List<Integer>> matrix = new ArrayList<>();
    //next matrix
    static List<List <Integer>> nextMatrix;
    
    public static void main (String [] args)
    {
        //This holds the test cases. It is exactly same data type as given in Programiz
        List <List<List<Integer>>> testMatrix = new ArrayList<>(); 
        
        //TEST CASES
        //We have to ensure that the test case is a 3d array as oppose to a 2d array to contain all the matrixes
        //Integer [][][]test = new Integer[][][] {   {{1},{5}},{{9,88}}};
        
        //Only 1 matrix defined
        //Integer [][][]test = new Integer[][][] {   {{1,2,3},{4,5,6}}}; 
        
        //As per challenge  - Test Case 1
        //Integer [][][]test = new Integer[][][] {   {{1,2,3},{4,5,6}},  {{7,8},{9,10},{11,12}},  { {13,14}, {15,16} } }; 
        
        //Adaptation  Test Case 1
        //Integer [][][]test = new Integer[][][] {   {{1,2,3,5,6},{4,5,6}},  {{7,8},{9,10},{11,12}},  { {13,14}, {15,16} } }; 
        
        //As per challenge  - Test Case 2
        //Integer [][][]test = new Integer[][][] {   {{1,2,3,4}},{{5},{6},{7},{8}}};
        
        //As per challenge  - Test Case 3/4  (both same)
        //Integer [][][]test = new Integer[][][] {   {{1, 2}, {3, 4}}, {{5, 6}, {7, 8}}, {{9, 10}, {11, 12}}  };
        
        //TEST CASES EXPLORING JAGGED ARRAYS (variants of TEST CASE 28 IN Document)
        //Integer [][][]test = new Integer[][][] {   {{1,2,3},{4,5,6}},  {{7,8},{10},{11,12}},  { {13,14}, {15,16} } };
        
        //TEST CASES EXPLORING JAGGED ARRAYS (variants of TEST CASE 28 IN Document) 
        //This relies on the matrixMultiplication initialisation
        //Integer [][][]test = new Integer[][][] {   {{1,2,3},{4,5,6}},  {{7,8,999},{10,11},{11,12}},  { {13,14}, {15,16} } }; 
       
        //TEST CASES EXPLORING JAGGED ARRAYS (variants of TEST CASE 28 IN Document)
        //Integer [][][]test = new Integer[][][] {   {{1,2,3},{4,5}},  {{7,8},{10,11},{11,12}},  { {13,14,17}, {15,16} } }; 
        
        //TEST CASES EXPLORING JAGGED ARRAYS (variants of TEST CASE 28 IN Document) 
        //Integer [][][]test = new Integer[][][] {   {{1,2},{4,5,6}},  {{7,8},{10,11},{11,12}},  { {13,14,17}, {15,16,17} } }; 
        
        //TEST CASES EXPLORING JAGGED ARRAYS (variants of TEST CASE 28 IN Document)
        //This will get picked up on area of code I had written towards bottom part of code
        //Integer [][][]test = new Integer[][][] {   {{},{4,5,6}},  {{7,8,6},{10,14},{11,12}},  { {13}, {15,16} } }; 
    
        //HERE!!!!!!!!!!!!!!!!!!!!!!!! - NEED TO CHECK THIS!!!!
        //Integer [][][]test = new Integer[][][] {   {{1,2},{3,4},{5,6}},  {{7,8},{10,14},{0}},  { {13}, {15,16} } }; 
        
        //With this test case it has not performed jump to new column
        //Integer [][][]test = new Integer[][][] {   {{1,2,3},{4,5,6}},  {{7,8},{9,10},{11,12}},  { {0}, {16,17},{5}} }; 
        
        //Integer [][][]test = new Integer[][][] {   {{1,2,3},{4,5,6}},  {{7,8},{9,10},{11}},  { {13,14}, {16,17} } }; 
        
        //TEST CASES EXPLORING JAGGED ARRAYS (variants of TEST CASE 28 IN Document) 
        //Integer [][][]test = new Integer[][][] {   {{1,2,3},{4,5,6}},  {{7,8},{9,2},{11,12}},  { {13,14}, {16,87},{3,4} } }; 
    
        //TEST CASES EXPLORING JAGGED ARRAYS (variants of TEST CASE 28 IN Document)
        //Integer [][][]test = new Integer[][][] {   {{1,2,3},{4,5,6}},  {{},{10,14},{11,12}},  { {13}, {15,16} } }; 
       
        //TEST CASES EXPLORING JAGGED ARRAYS (variants of TEST CASE 28 IN Document)
        //Integer [][][]test = new Integer[][][] {   {{1,2,3},{4,5,6}},  {{1,7},{25,14},{}},  { {13,15}, {15,16} } };
        
        //TEST CASES EXPLORING JAGGED ARRAYS (variants of TEST CASE 28 IN Document)
        //This will be handled validation in later part of code
        //Integer [][][]test = new Integer[][][] {   {{1,2,3},{}},  {{1,7},{25,14},{4,8}},  { {13,15}, {15,16} } }; 
       
        //TEST CASES EXPLORING JAGGED ARRAYS (variants of TEST CASE 28 IN Document
        //Integer [][][]test = new Integer[][][] {   {{1,2,3},{4,5,6}},  {{1,7},{25,14},{4,8}},  { {}, {15,16} } }; 
       
        //TEST CASES EXPLORING JAGGED ARRAYS (variants of TEST CASE 28 IN Document)
        //Integer [][][]test = new Integer[][][] {   {{1,2,3},{4,5,6}},  {{1,7},{25,14},{4,8}},  { {34,45}, {} } }; 
        
        //TEST CASES EXPLORING JAGGED ARRAYS (TEST CASE 28 IN Document exactly)
        //Integer [][][]test = new Integer[][][] {   {{1,2,3},{4,5,6}},  {{7,8,6},{10,14},{11,12}},  { {13,14}, {15,16} } }; 
       
        //TEST CASES EXPLORING JAGGED ARRAYS (TEST CASE 28 IN Document exactly)
        //But it is error free, so informed end user excess numbers
        //Integer [][][]test = new Integer[][][] {   {{1,2,3},{4,5,6,9}},  {{7,8},{9,10},{11,12}},  { {13,14}, {15,16} } };
        
        //TEST CASES EXPLORING JAGGED ARRAYS BUT EXPLORING CHANGES IN MATRIX A (TEST CASE 28 IN Document exactly)
        //Integer [][][]test = new Integer[][][] {   {{1,2,3},{4}},  {{7,8},{9,10},{11,12}},  { {13,14}, {15,16} } };
        
        //TEST CASES EXPLORING JAGGED ARRAYS BUT EXPLORING CHANGES IN MATRIX A (TEST CASE 28 IN Document exactly)
        //But it is error free, so informed end user excess numbers
        //Integer [][][]test = new Integer[][][] {   {{1,2,3},{4,7,2,6}},  {{7,8},{9,10},{11,12}},  { {13,14}, {15,16} } };
         
        //TEST CASES EXPLORING JAGGED ARRAYS (TEST CASE 28 IN Document exactly)
        Integer [][][]test = new Integer[][][] {   {{1,2,3},{4,5,6}},  {{},{10,14},{11,12}},  { {13,14}, {15,16} } };
       
        //Integer [][][]test = new Integer[][][] {   {{1,2,3},{4,5,6}},  {{11,14},{},{19,12}},  { {13,14}, {15,16} } }; 
        //Integer [][][]test = new Integer[][][] {   {{1,2,3},{4,5,6}},  {{11,14},{44,55},{19,12}},  { {13,14}, {} } };
        
        //Integer [][][]test = new Integer[][][] {   {{1,2,3},{4,5,6}},  {{11,14},{44,55},{19,12}},  { {13,14}, {0} } }; 
       
        //Integer [][][]test = new Integer[][][] {   {{1,2,3},{4,5,6}},  {{11,14},{44,55},{19,12}},  { {13,14}, {0,5},{0} } }; 
        
        //TEST CASES EXPLORING JAGGED ARRAYS (TEST CASE 28 IN Document exactly)
        //Integer [][][]test = new Integer[][][] {   {{1,2,3},{4,5,6}},  {{7,8},{10,14},{11,12}},  { {13}, {15,16} } }; 
       
        //ADDITIONAL TEST CASES
        
        //Integer [][][]test = new Integer[][][] {   {{1,2,3},{4,5,6}},  {{11,14},{44,55},{19,12}},  { {13,14}, {0} } }; 
        
        //Integer [][][]test = new Integer[][][] {   {{1,2,3},{4,5,6}},  {{7,8},{10,14},{15,12,65,55}},  { {13,99}, {15,16} } }; 
             
        //Integer [][][]test = new Integer[][][] {   {{1,2,3},{4,5,6},{7,8,9}},  {{10,11,12},{13,14,15},{16,17,18}},  { {}, {22,23,24}} };
        
        //Integer [][][]test = new Integer[][][] {   {{1,2,3},{4,5,6}},  {{7,8},{9,10},{11,12}},  { {13,14}, {15,16} },    { {17,18}, {19,20} },{ {17,18}, {19,20} }}; 
        
        //Integer [][][]test = new Integer[][][] {   {{1,2,3},{4,5,6}},  {{7,8},{9,10},{11,12}},  { {13,14}, {15,16}},  { {17,18}, {19,20},{99,18}, {19,20}}, { {21,22,23,24}, {25,26,27,28},{29,30,31,32}} };
                                                                                            
        //Integer [][][]test = new Integer[][][] {   {{1,2,3},{4,5,6},{7,8,9}},  {{10,11,12},{13,14,15},{16,17,18}},  { {19,20,21}, {22,23,24}} };
        
        //active
        //Integer [][][]test = new Integer[][][] {   {{1,2,3},{4,5,6},{7,8,9}},  {{11,12},{13,14,15},{16,17,18}} };
        
        //Integer [][][]test = new Integer[][][] {   {{},{4,5,6},{7,8,9}},  {{10,11,12},{13,14,15},{16,17,18}} };


	//***********AI generates test cases  v3.4***********************************
        //SOME WILL FAIL SINCE CODE NOT DESIGNED FOR THESE SCENARIOS
        //Integer[][][] test = new Integer[][][] {{{1,2,3},{4,5,6},{7,8,9}},{{9,8,7},{6,5,4},{3,2,1}}};
        
        //Integer[][][] test = new Integer[][][] {{{1},{2,3},{4,5,6}},{{6,7},{8,9},{10,11}}};
        
        //Integer[][][] test = new Integer[][][] {{{1,2}},{{3},{4}},{{5,6}},{{7},{8}}};
        
        //Integer[][][] test = new Integer[][][] {{{1,2,3,4}},{{5},{6},{7},{8}}};
        
        //Integer[][][] test = new Integer[][][] {{{1,null,3},{4,5,null}},{{7,8},{9,10},{11,12}}};
        
        //Integer[][][] test = new Integer[][][] {{{}},{{1,2},{3,4}}};
        
        //Integer[][][] test = new Integer[][][] {{{1},{2},{3},{4}},{{1,2,3,4}}};
        
        //Integer[][][] test = new Integer[][][] {{{1}},{{2,3}},{{4},{5},{6}},{{7,8,9}}};
        
        //Integer[][][] test = new Integer[][][] {{{1,2},{3,4}},{{1,2},{3,4}},{{1,2},{3,4}}};
        
        //Integer[][][] test = new Integer[][][] {{{1,2,3,4,5}},{{6},{7},{8},{9},{10}}};
        
        //Integer[][][] test = new Integer[][][] {{{1,2},{3}},{{4,5,6}},{{7},{8,9}}};

        //Integer[][][] test = new Integer[][][] {{{1}},{{2}},{{3}},{{4}},{{5}}};
        
        Integer[][][] test = new Integer[][][] {{{},{1,2}},{{3},{4,5}}};
        
        //Integer[][][] test = new Integer[][][] {{{1,2,3,4,5},{1,2,3,4,5},{1,2,3,4,5}},{{1,2},{3,4},{5,6},{7,8},{9,10}}};
        
        //Integer[][][] test = new Integer[][][] {{{1,2},{3}},{{4,5},{6,7},{8,9}}};
        
        //Integer[][][] test = new Integer[][][] {{{1,2},{3,4}},{{5,6,7},{8,9,10}}};
        
        //Integer[][][] test = new Integer[][][] {{{1,2}},{{}},{{3,4}}};
        
        //Integer[][][] test = new Integer[][][] {{{1,2,3},{4,5}},{{6},{7},{8}}};
        
        //Integer[][][] test = new Integer[][][] {{{1,null,3,4},{5,6,null,8}},{{9,10},{11,12},{13,14},{15,16}}};
        
        //Integer[][][] test = new Integer[][][] {{{1,2}},{{3,4,5}},{{6,7}}};
        
        //Integer[][][] test = new Integer[][][] {{{5}},{{6}},{{7}}};
        
        //Integer[][][] test = new Integer[][][] {{{0,0,0},{null,0}},{{0},{0},{0}}};
        
        //Integer[][][] test = new Integer[][][] {{{1,2,3,4}},{{1},{null},{3},{4}}};
        
        //Integer[][][] test = new Integer[][][] {{{1,2},{3,4,5},{6}},{{7},{8},{9}}};
        
        //Integer[][][] test = new Integer[][][] {{{10,11,12},{13,14,15}},{{2,3},{4,5},{6,7}},{{1},{2}}};

        //Integer[][][] test = new Integer[][][] {{{1,2}},{{},{}},{{3,4}}};

        //Integer[][][] test = new Integer[][][] {{{1,2,3,4},{5,6,7,8},{9,10,11,12},{13,14,15,16}},{{16,15,14,13},{12,11,10,9},{8,7,6,5},{4,3,2,1}}};
        
        //Integer[][][] test = new Integer[][][] {{{1,2,3},{},{4,5}},{{6},{7,8},{9,10,11}}};
        
        //Integer[][][] test = new Integer[][][] {{{1,2,3},{4,5,6}},{{7},{8},{9}}};
        
        //Integer[][][] test = new Integer[][][] {{{3,1,8,null,0}},{{9}}};

        

	System.out.println("***********ENFORCES Correctness and Structucal Integrity***********");


        //We know the items in test array are 3d array
        //so need to use techniques to first get each matrix
        for (Integer [][] item: test)
        {
            //instantiates the matrix. Although this could also be outside of the for loop
            //I left it inside for more clarity
            matrix = new ArrayList<>();
            
            //processes each row in matrix    
            for (Integer [] row: item)
            {
                //it populates it into the matrix
                matrix.add(Arrays.asList(row));
            }
            //it adds entire matrix List<List<Integer>>  into    List<List<List<Integer>>> (stores collection of matrixes)
            testMatrix.add(matrix);
        }
        
        //the whole method signature satisfies that proposed in Programiz challenge
        //Method call to below
        System.out.println("\n\n***FINAL OUTCOME***: \n" + matrixMultiplication(testMatrix));
    }
    
    public static List<List<Integer>> matrixMultiplication(List<List<List<Integer>>> matrices) 
    {
        //this will hold row content of Matrix B
        //It will take exact value as follows  nextMatrix.get(m);
        List<Integer> rowInfo = new ArrayList<>();
                            
        //keeps track of row of next matrix (counter+1)  relative to current matrix(counter)
        //NOTE: counter is stepping by two each time
        int rowCounterNextMatrix;
        
        //This is a multi-purpose variable
        //we know number rows in Matrix A should be equal to number columns in Matrix B
        //But during execution we might find that number columns in Matrix B exceeds that amount
        //Strictly speaking it is incorrect configuration.. But having too many elements in row in
        //Matrix B is less detrimental... seekPosition acknowledges this and assists with
        //generation of excessRowReport at end informing end user surplus elements in row in Matrix B

	    //We also use this variable for examining if number elements in row in Matrix A/C should be same as numbers rows (column length) in Matrix B
        //if numElementsInRow(Matrix A/C) is greater than column length in Matrix B nextMatrix.size()), we need to obtain all integer values in 
	    //matrix.get(rowCounterFirstMatrix) and store information in excessColReport
        int seekPosition=0;        
        
        //This variable was introduced due to improvisations
        //notably because it does not skip to next column such as test case
        //Integer [][][]test = new Integer[][][] {   {{1,2,3},{4,5,6},{7,8,9}},  {{10,11,12},{13,14,15},{16,17,18}} };
        //I can not rely on the logic at bottom based on 
        //numWritesToLastIndexOnRowMultiplicationMatrix  since it is based on z which is related 
        //to column number.. So I have to use different dependency.
        //We know number writes to any index in multiplications matrix is equal to number rows in Matrix B (since we move downwards)
        //in Matrix B and across Matrix A/C
        int numWritesAtIndexWithinMultiplicationMatrix;
        
	    //this takes the information from matrixMultiplicationCalculations to ensure calculations is displayed correctly for each
	    //row in multiplication matrix
        StringJoiner matrixRowCalculations = new StringJoiner("");
        
	    //This keeps record of excess elements in row in Matrix B
	    StringJoiner excessRowItems=new StringJoiner(",");
        
        //used in scenarios such as validation checks to show row examined in Matrix A
        String rowMatrix=null;
        
        //used as part of the validation checks to present detailed comment to end user
        //if the length row is outside expected parameters of multiplication matrix
        String currentEntry=null;
        String prevEntry=null;
        
        //These mantain reports of Matrix B so that information can be provided right at end of code execution.
        //this deals with situations in Matrix B where there might be too many row entries.
        //number rows Matrix B should be equal to number columns in Matrix A
        //it of course does not deal with less rows in Matrix B than columns in Matrix A. This would cause error.
        StringJoiner excessRowReport = new StringJoiner("\n");
        
        //These mantain reports of Matrix B so that information can be provided right at end of code execution.
        //This deals with Matrix A. It would look at situations where there are too many columns in certain rows.
        //It keeps this level of information since the matrix is still valid... But informs end user of rows with too many elements.
        //it of course does not deal with column number (Matrix A) which is less than rows Matrix B
        StringJoiner excessColReport = new StringJoiner("\n");
        
        //This is used to store the results of multiplications undertaken
        //It stores and presents entries until end of lifecycle of Matrix A x Matrix B
        StringJoiner [][] matrixMultiplicationCalculations=null;
        
        //This is used in conjunction with z variable (getting each variable on row in Matrix B) in which it reaches end of the
        //matrix multuplication array. It increments numWritesToLastIndexonRowMultiplicationMatrix once it reached end each row.
        //We know that it there are two rows in the would have reached last index of matrixMultiplication same number of times
        //Since we know that number rows in Matrix B (effectively the size of the column) that determines the number times z has reached
        //length of (matrixMultiplication-1) due to zero indexing
        int numWritesToLastIndexOnRowMultiplicationMatrix;
        
        //This boolean will be set to true when numWritesToLastIndexOnRowMultiplicationMatrix is the same as number rows in
        //Matrix B. Since we know each element in column (whether 1 or more) governs a write to the matrixMultiplication[last index]
        boolean hasReachedEndRow=false;
        
        //Depending on its flag value, it can either be used to configure matrixMultiplication or provide summary matrix multiplied
        //it also is key flag to ensure matrixMultiplication is stored amongst other matrices.
        boolean hasStartedMultiplication=false;
        
        //This simply holds the value of multiplication undertaken between Matrix A (single element) x Matrix B (single element) 
        Integer multiplication=0;
        
        //zero index row counter Matrix A/C
        int rowCounterFirstMatrix=0;
        
        //used in several validation checks to see if rows on Matrix B (height of column) is equal Matrix A/C (elements in row)
        int rowsNextMatrix=0;
        
        //gets the number of rows which in effect is the seperate List<List<Integer>> which correspond seperate matrix
        int matricesSize=matrices.size();
        
        //used for indexing across code. One of the main variables. it performs steps in two to ensure Matrix A[0]x B[1], store result in Matrix C[2]..
        //and then perform Matrix C[2] x Matrix B[3], store result Matrix C[4]  and then perform Matrix C[4] x Matrix B[5]
        int counter=0;
        
        //refers to Matrix A/C. Number elements/size of valid column on Matrix B
        int numElementsInRow=0;
        
        //set to ensure the entire row has been processed from Matrix A/C
        //boolean hasProcessed=false;
        
        //this is index in the row of Matrix A to ensure it traverses in conjunction with movement in column
        //in Matrix B
        int posPrevMatrixRowLevel=0;
        
        //simply counts number matrixes in matrices
        //it is used to display All matrix before the main code execution
        int numMatrixes=0;
        
        //core area of code.. It stores calculation for indexes from Matrix A (element in row) and 
        //Matrix B (element in column) 
        Integer [][]matrixMultiplication = null;
        
        //it has to convert the above matrixMultiplication into this format before it can be added back into
        //matrices (which is a collection of <List<Integer>>)
        List <List<Integer>> matrixMultiplicationList = new ArrayList<>();
        
        //used with Matrix A/C to demonstrate any discarded row elements (shorten column) in order to preserve compatability with
        //matrixMultiplication array
        String currentColumnEntry;
        
        //This keeps record of currentColumnEntry and also the discarded elements
        //presented at end of execution
        StringJoiner excessColItems=new StringJoiner(",");

        System.out.println("***ALL MATRIX*******: " + matrices.size());
        
        //Gets all rows for each matrix in matrices
        do
        {
            matrix=matrices.get(numMatrixes);
            
            System.out.println("\nMatrix" +"("+numMatrixes+"):");

            for (List<Integer> row: matrix)
            {
                System.out.println(row);
            }
            numMatrixes++;
            
        }while(numMatrixes<matrices.size());
        
        System.out.println("\n\n\n");
        numMatrixes=0;
        
        //****MAIN START OF CORE CODE LOGIC***************
        //This goes through counter (applicable matrix within matrices)
        do
        {
            //ensures it ready to configure the matrixMultiplication
            //hasProcessed=false;
            
            //loads matrix into List<List<Integer>>
            matrix=matrices.get(counter);
           
            //We would only provide summary of Matrix A/C x Matrix B
            //if counter is greater than 0 and has configured size
            //matrixMultiplication and se hasStartedMultiplication to true
            
            if (counter>0 && hasStartedMultiplication)
            {
                System.out.println("\n***SUMMARY****");
                System.out.println("Matrix multiplication: " + "Matrix ("+(counter-2)+") x Matrix ("+(counter-1)+")");
                
                //we know it has started multiplication so there are two or more matrix entries
                //However in reality this if statement can be removed since even if there were two matrixes
                //counter would perform step by 2 and exceed matricesSize
                //I implemented this in early part of my coding to keep conditions stringent
                if (matrices.size()!=2)
                {
                    System.out.println("ADDED Multiplication Matrix into the chain at index: " + (counter));
                    matrices.add(counter,matrixMultiplicationList);
                    hasStartedMultiplication=false;
                    System.out.println("Matrix("+counter+")");
                    
                    //prints out multiplication matrix
                    for (Integer []m: matrixMultiplication)
                    {
                        System.out.println(Arrays.toString(m));
                        
                        matrixMultiplicationList.add(Arrays.asList(m));
                    }
                    
                    //All steps are stored in matrixMultiplicationCalculations
                    //we simply add results from row in array into StringJoiner
                    //This ensures end user sees the calculations in expected format such as
                    //***CALCULATION STEPS*******
                    //[(1x7 =7)+(2x9 =18)+(3x11 =33)] [(1x8 =8)+(2x10 =20)+(3x12 =36)]
                    //[(4x7 =28)+(5x9 =45)+(6x11 =66)] [(4x8 =32)+(5x10 =50)+(6x12 =72)]
                    
                    System.out.println("\n***CALCULATION STEPS*******");
                    
                    for (int q=0; q<matrixMultiplicationCalculations.length; q++)
                    {
                        matrixRowCalculations = new StringJoiner("   ");
                
                        for (int r=0; r<matrixMultiplicationCalculations[0].length; r++)
                        {
                            matrixRowCalculations.add(" ["+matrixMultiplicationCalculations[q][r]+"]");
                        }
                        System.out.println(matrixRowCalculations);
                    }
                    
                    //Matrix (counter+1) is always Matrix B
		            //Matrix (counter) is either Matrix A or Matrix C
                    System.out.println("\nIt will perform: " + "Matrix("+counter+")   x  Matrix("+(counter+1)+")");
                    
                    //it presents Matrix A/C onto the screen
                    for (List<Integer> w: matrices.get((counter+1)))
                    {
                        System.out.println("\t\t\t\t"+String.valueOf(w));
                    }
                    
                    //crucial to update the size of matrices given addition of another
                    matricesSize=matrices.size();
                    //we now load Matrix A/C into matrix List 
                    matrix=matrices.get(counter);
                }
            } //end if statement
            System.out.println("\n\n------Matrix" +"("+counter+"):");
            
            //we perform this again since this is effectively main starting point in the loop
            //i.e getting matrix(0),  matrix(2)
            matrix=matrices.get(counter);
            
            //prints matrix onto screen
            for (List<Integer> row: matrix)
            {
                System.out.println(row);
            }
            
            //Create a new object reference since each is unique to formation
            //matrix C
            matrixMultiplicationList = new ArrayList<>();
            
            //if there is not a next matrix, there is only 1 matrix in matrices.
            //it will not be applicable in any other scenarios due to counter step 2
            //increments prior to do while termination
	        //No calculations required
            try
            {
                nextMatrix=matrices.get(counter+1);
            }
            catch (IndexOutOfBoundsException e)
            {
                System.out.println("Single matrix only");
                matrix=matrices.get(counter);
                System.out.println("\n***CALCULATION STEPS*******");
            
                for (List<Integer> row: matrix)
                {
                    System.out.println(row);
                }
                System.exit(0);
            }
            
            System.out.println("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");
            
            //The StringJoiner dimensions is exact replica of the matrixMultiplication
            //difference is the indexes in matrixMultiplication solely keeps running total
            matrixMultiplicationCalculations=new StringJoiner[matrix.size()][nextMatrix.get(0).size()];
            
            //Informs end user of the dimensions for storage multiplication matrix
	        System.out.println("Size of Matrix multiplication row: " + matrix.size());
            System.out.println("Size of Matrix multiplication columns: " + nextMatrix.get(0).size());

            
            System.out.println("*******Customising Storage Grid for Multiplication Matrix**********["+matrix.size()+"]["+nextMatrix.get(0).size()+"]***************");
            
            //it sets a StringJoiner with "+" to ensure each time entry added to the index, it can format outputs
            //corretly
            for (int r=0; r<matrix.size(); r++)
            {
                for (int q=0; q<nextMatrix.get(0).size(); q++)
                {
                    System.out.println("Configuring for index: " + r + "," + q);
                    matrixMultiplicationCalculations[r][q] = new StringJoiner("+");
                }
            }
            
            //**** This is now logic driving movement
            //It processes the row in matrix.. The main code is now starting
            //This can be taken to be either Matrix A or Matrix C
            for (List<Integer> ee: matrix)
            {
                //catpures row to be used during validation messages
                //I have refrained from using this variable elsewhere in order to preserve
                //usage of ee for Collection
                //I had to keep this value since ee is not in scope during validation phases
                rowMatrix = String.valueOf(ee);
                
                //Displays data for the row
                System.out.println("THIS IS ROW COUNTER MATRIX("+counter+"): " + rowCounterFirstMatrix);
                System.out.println("------------------------------------------THIS IS ROW DATA MATRIX("+counter+"): " + ee);
                
                //adjusting to default values
                //hasProcessed=false;
                numWritesToLastIndexOnRowMultiplicationMatrix=0;
                hasReachedEndRow=false;
                
                //now going through each element in row of the matrix
                for (int k=0; k<ee.size(); k++)
                {
                    //counter operates zero indexing, so checking it has not reached the last matrix
                    if(counter!=matricesSize-1)
                    {
                        //not entirely sure purpose of this logic.
                        //it is looking at size of first row.
                        //This must have been benefical during early test phase but no longer needed
                        
                        //if (nextMatrix.get(0).size()==1)
                        //{
                           // hasProcessed=false;
                        //}
                        
                        //Again this variable seems to serve no purpose since it sets value true once it leaves this loop
                        //But before it reaches here again, it sets the value to false..
                        //And there are no scenarios in any loop statements where there is a dependency on this.
                        //Again it was used during my early development phase, so I will just remove this
                        //if (!hasProcessed)
                        //{
                            
                            //sets back to 0 since it will 
                            rowCounterNextMatrix=0;
                            
                            //if it hasn't started multiplication, its prequisite is to create
                            //value 0 into each location...
                            //We have to remember matrixMultiplication is non-primitive (Integer) and its default value be null.
                            //Since we are updating value in matrixMultiplication multiple times, we are always getting previously stored value
                            //This gives a NullPointerException otherwise for first write at each index location.
                            //My alternative approach would have been to catch this exception and perform matrixMultiplication=multiplication.
                            //For now, I will leave my approach intact 
                            //It is for test cases such as:
                            //Integer [][][]test = new Integer[][][] {   {{1,2,3},{4,5,6}},  {{7,8},{10,14},{11,12}},  { {13}, {15,16} } };
                            
                            //We can see Once it has generated Matrix C, it will use the next matrix
                            //Matrix (W=2)  x  (H=1) configured to store Matrix 2 (Matrix C) x  Matrix 3 ( {13}, {15,16} })
                            //We can see it has taken  1 as height from one element in {13} and this is used as number rows in
                            //matrixMultiplication   (new Integer[nextMatrix.get(0).size()][matrix.size()]);
                            //So effectively once we move into the below for loop, it uses matrix.size (number rows in matrix)
                            //We know that this is 2 rows   from             matrix(2) or (matrix C) which is      [60,72]
                                                                                                               //[144,174]
                            
                            //And using two rows to fill the a value in => matrixMultiplication[a=1][b]
                            //is in violation to the matrix configured
                            //SO, the try and catch is required
                            
                            if (!hasStartedMultiplication)
                            {
                                System.out.println("****************************CONFIGURING SIZE FOR BLANK MULTIPLICATION MATRIX*******************************************");
                                matrixMultiplication = new Integer[matrix.size()][nextMatrix.get(0).size()];
                                
                                System.out.println("Multiplication matrix (W=" + matrix.size()+")  x  (H=" + nextMatrix.get(0).size()+")" 
                                + " configured to store Matrix " + counter + " x  Matrix " + (counter+1));
                                
                                try
                                {
                                    for (int a = 0; a < matrix.size(); a++) 
                                    {
                                        for (int b = 0; b < nextMatrix.get(0).size(); b++) 
                                        {
                                            matrixMultiplication[a][b] = 0;
                                        }
                                    }
                                }
                                
                                catch (ArrayIndexOutOfBoundsException e)
                                {
                                    System.out.println("\n33The current dimension of matrixMultiplication: " 
                                    + "["+matrix.size()+"]"+ "["+nextMatrix.get(0).size()+"]");
                                }
                                
                                //This is set to true since we know as the code block above, we can not provide a summary of the calculations
                                //unless it carries this value
                                hasStartedMultiplication=true;
                            }
                            
                            //I introduced this here since it does not fit within the catch framework that appears later on
                            //which catches all my main issues.
                            //I have chosen to exit due to scenarios such as
                            //Integer [][][]test = new Integer[][][] {   {{1,2,3},{4,5,6},{7,8,9}},  {{10,11,12},{13,14,15},{16,17,18}},  { {}, {22,23,24}} };
                            //If I do not exit, once it reaches this code further down
                            //if (numElementsInRow>matrixMultiplication[0].length)  I will get ArrayIndexOutOfBoundsException
                            //since matrixMultiplication[0].length  has not been initialised due to     { {},
                            //Since it will fail on this line which is towards end of my code, I could have potentially handled the scenario
                            //Once again, I am not aiming to break my code once its in a functional state
                            
                            if (nextMatrix.get(0).size()==0)
                            {
                                System.out.println("8The current dimension of matrixMultiplication: " 
                                + "["+matrix.size()+"]"+"["+nextMatrix.get(0).size()+"]");
                                System.out.println("2It EXPECTS: " + nextMatrix.get(0).size() + " elements per row");
                                System.out.println("Hence multiplication matrix has no width");
                                System.out.println("matrix" + "("+(counter+1)+")" + " row:" + rowCounterNextMatrix +  " " 
                                + nextMatrix.get(0) + " size: " + nextMatrix.get(0).size());
                                System.out.println(matrices);
                                //Crucial to exit as per explanation above
                                System.exit(0);
                            }
                            
                            //THIS IS WHERE THE NESTED BECOMES EXTREME
                            //IN SUMMARY
			                //For each row in Matrix A/C (Already active)
			                //For each element in row Matrix A/C (Already active)
                            //For each COLUMN(z) in Matrix B
                            //For each ROW (y) in matrix B
                            //If Matrix B (ROW Number y) is less than number columns.    I AM NOT 100% sure about rationale
                            //For each ROW (m) in matrix B
                            //For each element in row(m)
                            
                            //This now goes through each element in first row of Matrix B
                            //This is considered to be column number
                            //And we know that matrixMultiplication is configured a part this size
                            //nextMatrix.get(0)
                            for (int z=0; z<nextMatrix.get(0).size(); z++)
                            {
                                System.out.println("\n*****************Column number: " + z);
                                
                                //when starting a new column, we know we are also starting at
                                //first index in Matrix A
                                posPrevMatrixRowLevel=0;
                                //There would also have been no writes to first index on row of Matrix A
                                numWritesAtIndexWithinMultiplicationMatrix=0;
                                
                                //We now go through each row in matrix B (length of columns)
				                //CHATGPT has correctly identified this as a repeat examination as int=m
				                //so it is being commented out
                                //for (int y=0; y<nextMatrix.size();y++)
                                //{   
                                    //I do not believe there is a relationship between length of column and number
                                    //columns in Matrix B. This has to be phased out... It is perhaps misinterpretation
                                    //during development phase...
				                    
				                    //if (y<nextMatrix.get(0).size())
                                    //{
                                         
                                        for (int m=0; m<nextMatrix.size(); m++)
                                        {
                                            for (int j=0;j<nextMatrix.get(m).size();j++)
                                            {   
                                                //if it starting on first row of Matrix B
                                                //it sets this variable back to 0
                                                //Once again, this variable was already reset once it did new row of Matrix A
                                                //this is equivalent to first row of Matrix B (i.e first entry in column)
                                                //if (m==0)
                                                //{
                                                //    rowCounterNextMatrix=0;
                                                //}
                                                
                                                //This is extremely critical part of code
                                                //We need to perform a break since the value in posPrevMatrixRowLevel
                                                //is number rows
                                                //if we decide not to break, when it reaches
                                                //System.out.println("Matrix["+counter+"] \trow element["+posPrevMatrixRowLevel+"]: " +ee.get(posPrevMatrixRowLevel) +"\t\t" + ee);
                                                //ee.get(3) for
                                                //Integer [][][]test = new Integer[][][] {   {{1,2,3},{4,5,6}},  {{7,8},{9,10},{11,12}},  { {13,14}, {15,16} },    { {17,18}, {19,20} },{ {17,18}, {19,20} }}; 
                                                //would attempt to get row four from here, which does not exist
                                                //Matrix(0):
                                                //[1, 2, 3]
                                                //[4, 5, 6]
                                                
                                                //By performing a break, we effectively find ourselves
                                                //breaking the inner loop, but we will continue to run through next few iterations of
                                                //m until it reaches the end
                                                //for (int m=0; m<nextMatrix.size(); m++)
                                                //{
                                                    //for (int j=0;j<=nextMatrix.get(m).size();j++)
                                                    //{
                                                    
                                                //So strictly speaking, we should increase the value of m=nextMatrix.size()
                                                //but this invalidates the outcome, so a break sufficed
                                                
                                                //also we know at this point, posPrevMatrixRowLevel= 3
                                                //we know ee.get(3) is invalid
                                                //So when we reach this area of code below, it  will cause another exception
                                                //System.out.println("Matrix["+counter+"] \trow element["+posPrevMatrixRowLevel+"]: " +ee.get(posPrevMatrixRowLevel) +"\t\t" + ee);
                                                //We also do not want to set this back to 0 since it may trigger more calculations
                                                //BREAK seems only option to gracefully get out nested area and force new column
                                                
                                                if (numWritesAtIndexWithinMultiplicationMatrix==nextMatrix.size())
                                                {
                                                    //posPrevMatrixRowLevel=0;   //Not an option
                                                    System.out.println("****************************************************************");
                                                    //m=nextMatrix.size()-1;     //Not an option
                                                    break;
                                                }
                                                
                                                //if it hasn't written number writes per index (governed by rows in Matrix B, number elements in the column)
                                                //We inform end user next row is retrieved from Matrix B.
                                                //This is part of the process getting the row so the correct element can be obtained depending on the column
                                                //it is procesing
                                                else
                                                {
                                                    System.out.println("retrieving row: " + rowCounterNextMatrix 
                                                    + " from matrix (" + (counter+1)+")");    
                                                }
                                                
                                                //we now get information from row in Matrix B and load it into a List
                                                rowInfo=new ArrayList<>(nextMatrix.get(m));
                                                
                                                //if it hasn't reached end of the row
                                                //Note the value (hasReachedEndRow) will be set to true when z has reached the last element on
                                                //matrixMultiplication...
                                                //But we also know the number of times it performs write to the last row index of the matrix
                                                //is determined by the column length (nextMatrix.size()) in Matrix B 
                                                //This area of code will appear later on where it demonstrates this
                                                
                                                //****THIS IS LONG SECTION OF CODE WHICH DEALS WITH VALIDITY OF MATRIXES FROM PERSPECTIVE
                                                //OF INVALIDATING AGAINST MATRIX MULTIPLICATION AND ALSO VALIDATING (GIVEN ANY SURPLUS DIMENSIONS)
                                                if (!hasReachedEndRow)
                                                {
                                                    //We are now getting variable ready to scan through the row and ascertain if
                                                    //it is in violation to the multiplication matrix
                                                    seekPosition=0;
                                                    //We will store excess items in a StringJoiner so that it can be presented to end user in meaningful way
                                                    //it will provide information on all applicable element
                                                    excessRowItems = new StringJoiner(",");
                                                    
                                                    
                                                    //Just a reminder of the relationship
                                                    //We know elements in row (Matrix A) = column length in Matrix B
                                                    //length column determines the width of the multiplication matrix
                                                    
                                                    //it checks the number elements (number columns) in row in Matrix B
                                                    //against length of multiplication matrix
                                                    //since we know both values should be equal in non jagged matrix B
                                                    if (rowInfo.size()!=matrixMultiplication[0].length)
                                                    {
                                                        //It informs end user expectations
                                                        currentEntry = "2Row length:" + rowInfo.size()+" (matrix" + "("+(counter+1)+") row:" 
                                                        +rowCounterNextMatrix+ "   " + nextMatrix.get(m)+") "  
                                                        + " inconsistent with length expectations" 
                                                        + "("+matrixMultiplication[0].length+") in multiplcation matrix";
                                                        
                                                        //I have used a not equality since due to nature of the code, there is
                                                        //repeat looping.. Otherwise end user will get same message outputted multiple
                                                        //times for same row of Matrix B
                                                        //Hence I created prevEntry once it exits this area of code so that it preserves
                                                        //previous result for comparison
                                                        if (!currentEntry.equals(prevEntry))
                                                        {
                                                            //it outputs information to screen
                                                            System.out.println(currentEntry);
                                                            //it stores value as part of excessRowReport which refers to analyis on Matrix B
                                                            excessRowReport.add(currentEntry);
                                                            
                                                            //This gets each element from row in Matrix B
                                                            for (int x: nextMatrix.get(m))
                                                            {
                                                                //it now incremennts seekPosition
                                                                seekPosition++;
                                                                
                                                                //it continues the increment until the variable
                                                                //is greater than length of multiplication matrix
                                                                //We know that number columns in Matrix A = number rows Matrix B
                                                                
                                                                //We know in this example it satisfies condition.. But we coulde easily extend
                                                                //rows in Matrix B as shown in brackets...
                                                                //It is beyond matrixMultiplication[0].length which is taken to be
                                                                //2 based on length of row nextMatrix.get(0)   [7, 8]
                                                                
                                                                //Matrix(A):
                                                                //[1, 2, 3]
                                                                //[4, 5, 6]

                                                                //Matrix(N):
                                                                //[7, 8]
                                                                //[9, 10, (23,25)]
                                                                //[11, 12]
                                                                
                                                                //We need means to capture this information and present to end user
                                                                //It has not invalidated calculations
                                                                                                                            
                                                                if (seekPosition>matrixMultiplication[0].length)
                                                                {
                                                                    excessRowItems.add(String.valueOf(x));
                                
                                                                }
                                                            }
                                                        }
                                                    }
                                                    
                                                    //Now we perform assignment as explained above to negate replication
                                                    prevEntry = currentEntry;
                                                    
                                                    //We only want to inform end user of these captured values if seekPosition
                                                    //exceeds 0 (i.e there are more elements in row beyond nextMatrix.get(0)    
                                                    if (seekPosition!=0)
                                                    {
                                                        System.out.println("["+excessRowItems+"] are exempt from multiplcation matrix");
                                                        excessRowReport.add("["+excessRowItems+"] are exempt from multiplcation matrix");
                                                        
                                                        //It would find itself here for test cases such as
                                                        //Integer [][][]test = new Integer[][][] {   {{1,2,3},{4,5,6}},  {{7,8},{10,14},{11,12}},  { {13}, {15,16} } }; 
                                                        
                                                        //We can see that for {13}  Matrix(3) row 0, the length is less than
                                                        //matrixMultiplication[0].length
                                                        //But we need to clarify why we have used excessRowItems and no reference to
                                                        //seekPosition... We know seekPosition is not equal to 0.
                                                        // which is as expected sine it has found {13}
                                                        //On other hand, we know that it has not exceeded matrixMultiplication[0].length
                                                        //Hence it has not performed write into excessRowItems (final report to end user) since
                                                        //{13} does not meet matrix requirements
                                                        
                                                        if(excessRowItems.toString().length()==0)
                                                        {
                                                            //It currently informs end user and since there is violation, it has to
                                                            //terminate the code
                                                            System.out.println("10The current dimension of matrixMultiplication: " 
                                                            + "["+matrix.size()+"]"+"["+nextMatrix.get(0).size()+"]");
                                                            System.out.println("3It EXPECTS: " + nextMatrix.get(0).size() + " elements per row");
                                                
                                                            System.out.println("matrix" + "("+(counter+1)+")" + " row:" + rowCounterNextMatrix 
                                                            +  " " + "(content:"+nextMatrix.get(m) +")" + " size:" + nextMatrix.get(m).size() 
                                                            + " invalidates this");
                                                            System.out.println(matrices);
                                                            
                                                            //I had previously performed exit out of the code in situations such as above
                                                            //We can see that as it checks the first row [11, 12]
                                                            //there are insufficient elements to support calculation..
                                                            //But we know there is sufficient content to perform multiplications with first
                                                            //two columns... Only the third column is affected..
                                                            //So I have let the code execution flow and perform calculations up to point in which
                                                            //missing element is detrimental
                                                            
                                                            //Matrix(A):
                                                            //[1, 2, 3]
                                                            //[4, 5, 6]
                                                            //[7, 8, 9]

                                                            //Matrix(B):
                                                            //[11, 12]
                                                            //[13, 14, 15]
                                                            //[16, 17, 18]
                                                            
                                                            //System.exit(0);
                                                        }
                                                    }
                                                    
                                                    //variable not used
                                                    //int backupRowIndex = rowCounterNextMatrix;
                                                    
                                                    //This section of the code is where practically all of the exception handling occurs.
                                                    //It will need a detailed explanation of why the following scenarios are arising....
                                                    //The best way to explore this would be removing the exit statements and I can exactly give suitable examples
                                                    //again.
                                                    
                                                    //In a situation as follows, we can see it will enter Matrix (1), row 1 and it is in harmony
                                                    //with expectations always since we know we have used property of nextMatrix.get(0) to form dimension of the matrix
                                                    //Matrix(0):
                                                    //[1, 2, 3]
                                                    //[4, 5, 6]
                                                    //[7, 8, 9]

                                                    //Matrix(1):
                                                    //[10,11, 12]
                                                    //[13, 14, 15]
                                                    //[16, 17, 18]
                                                    
                                                    //But if reduced it as follows
                                                    //Matrix(1):
                                                    //[11, 12]
                                                    //[13, 14, 15]
                                                    //[16, 17, 18]
                                                    
                                                    //The is the scenario described above in which I removed the System.exit(0)
                                                    
                                                    //But for situations like, I have now designed my code to handle this scenario and terminate
                                                    //code at most extreme position possible
                                                    
                                                    //We need to understand first why a scenario such as WILL NOT enter the try statement
                                                    //and move into catch in which code will terminate...
                                                    //This is just one example of many that helped me create the try and catch area of the code 
                                                    //Matrix(0):
                                                    //[1, 2, 3]
                                                    //[4, 5, 6]
                                                    //[7, 8, 9]

                                                    //Matrix(1):
                                                    //[11, 12]
                                                    //[13, 14, 15]
                                                    //[16, 17, 18]
                                                    
                                                    //It is difficult to give example test case scenario for examples below
                                                    //unless explored individually
                                                    try
                                                    {
                                                        System.out.println("Matrix["+(counter+1)+"] \trow element["+z+"]: " + rowInfo.get(z) +"\t\t" + nextMatrix.get(m));
                                                        
                                                    }
                                                    catch (IndexOutOfBoundsException e)
                                                    {
                                                        System.out.println("\n24The current dimension of matrixMultiplication: " 
                                                        + "["+nextMatrix.get(0).size()+"]"+"["+matrix.size()+"]");
                                                        System.out.println("4It EXPECTS: " + nextMatrix.get(0).size() + " elements per row");
                                                        System.out.println("matrix" + "("+(counter+1)+")" + " row:" + rowCounterNextMatrix 
                                                        +  " " + nextMatrix.get(m) + " size: " + nextMatrix.get(m).size() + " invalidates this");
                                                        System.out.println(matrices);
                                                        //System.exit(0);
                                                    }
                                                    
                                                    try
                                                    {
                                                        System.out.println("Matrix["+counter+"] \trow element["+posPrevMatrixRowLevel+"]: " +ee.get(posPrevMatrixRowLevel) +"\t\t" + ee);
                                                    }
                                                    catch (ArrayIndexOutOfBoundsException e)
                                                    {
                                                        System.out.println("\n1The current dimension of matrixMultiplication: " 
                                                        + "["+matrixMultiplication.length+"]"+"["+matrixMultiplication[0].length+"]");
                                                        
							//elements in row of Matrix B (number columns) is greater than rows in multiplication matrix     
                                                        if (rowInfo.size()>matrixMultiplication.length)
                                                        {
                                                            System.out.println("1Accessing matrix(" + counter+") row index: " 
                                                            + rowCounterNextMatrix + " from " + nextMatrix.get(m) + " invalidates this");
                                                            System.out.println("Remove " + nextMatrix.get(m) + " and rows beyond from matrix(" 
                                                            + (counter+1)+") row index: " + m);
                                                        }
                                			
							//elements in row of Matrix A/C (number columns) is greater than rows in multiplication matrix
                                                        if(ee.size()>matrixMultiplication.length)
                                                        {
                                                            System.out.println("5It EXPECTS: " + matrix.size() + " elements per row");
                                                            System.out.println("matrix" + "("+(counter)+")" + " row:" + rowCounterFirstMatrix 
                                                            +  " " + matrix.get(rowCounterFirstMatrix) + " size: " 
                                                            + matrix.get(rowCounterFirstMatrix).size() + " invalidates this");
                                                        }
                                			//size of row in Matrix A/C is greater than number columns in multiplication matrix
                                                        if(nextMatrix.get(m).size()>matrixMultiplication[0].length)
                                                        {
                                                            System.out.println("6It EXPECTS: " + matrix.size() + " elements per row");
                                                            System.out.println("matrix" + "("+(counter+1)+")" 
                                                            + " row:" + rowCounterNextMatrix +  " " + nextMatrix.get(m) + " size: " 
                                                            + nextMatrix.get(m).size() + " invalidates this");
                                                        }
                                                        
                                                        //I removed all the system.exit(0) from my code and I found that it was only
                                                        //in this one that I had to re-instate the system.exit(0) otherwise I would get exception.
                                                        //This was previously the first block in the catch statement
                                                        //I have not moved it to the bottom and exploring.. If I find that exceptions escalate
                                                        //it will be placed again at the top.
                                                        //The code will already be more calculated since this would be the only reason to perform
                                                        //exit in this block

							                            //number elements in row of Matrix A/C is less than row counter for Matrix B
                                                        if (ee.size()<rowCounterNextMatrix)
                                                        {
                                                            System.out.println("2Accessing matrix(" + counter+") row index: " 
                                                            + rowCounterNextMatrix + " from " + ee + " invalidates this");
                                                            System.out.println("Remove " + nextMatrix.get(m) 
                                                            + " and rows beyond from matrix(" + (counter+1)+") row index: " + m);
                                                            System.out.println(matrices);
                                                            System.exit(0);
                                                        }
                                                        
                                                        //I have commented this out since it appears to be surplus information on the screen
                                                        //System.out.println(matrices);
                                                        //System.exit(0);
                                                    }
                                                    
                                                    //As a result of moving the System.exit(0) above, I found that it caused exception for
                                                    //following test case
                                                    //Integer [][][]test = new Integer[][][] {   {{1,2,3},{4,5,6}},  {{11,14},{44,55},{19,12}},  { {13,14}, {0} } }; 
                                                    //when performing operation with
                                                    //Matrix(2)           against          Matrix(3):
                                                    //[156, 160]                           [13, 14]     
                                                    //[378, 403]                           [0] 
                                                    
                                                    //System.out.println()
                                                    //Unlike before, where it performed
                                                    //(156x13 =2028) and realised there was not another element below
                                                    //14 and terminated...
                                                    //The code now continues further with  (156 x 13) + (160 x 0)
                                                    //to satisfy  matrixMultiplication[0][0]
                                                    //this is now better since its fully valid
                                                    
                                                    //And now as we expect, it starts matrixMultiplication [0][1]
                                                    //it has capacity to perform  (156 x 14)
                                                    //But we can see now there is no element below 14..
                                                    //so it correctly causes the multiplication to stall.
                                                    //I didn't previously have any exception handling around this
                                                    //since it was not previously required...
                                                    //I will create handling for IndexOutOfBoundsException
                                                    
                                                    //System.out.println(ee.get(posPrevMatrixRowLevel));
                                                    //System.out.println(z);
                                                    
                                                    try
                                                    {   //this is the principle multiplication between element in Matrix A/C and Matrix B
                                                        multiplication =  ee.get(posPrevMatrixRowLevel) *rowInfo.get(z);
                                                    }
                                                    
                                                    //Integer [][][]test = new Integer[][][] {   {{1,2,3},{4,5}},  {{7,8},{10,11},{11,12}},  { {13,14,17}, {15,16} } }; 
                                                    //for cases such as this where it performs     4 x 7,  5 x 10 but we can see that {4,5} is not long
                                                    //enough to perform   N   x  11 
                                                    
                                                    //Matrix(0):
                                                    //[1, 2, 3]
                                                    //[4, 5, N]

                                                    //Matrix(1):
                                                    //[7, 8]
                                                    //[10, 11]
                                                    //[11, 12]

                                                    //So I introduced catch block to compensate for this
                                                    catch (ArrayIndexOutOfBoundsException e)
                                                    {
                                                        System.out.println("2Matrix("+(counter)+")" + "   row:" + ee +"\thas no content at index["+m+"]");
                                                        System.out.println("Hence can not perform multiplication with Matrix("+(counter+1)+")" + "   row:" + (rowCounterNextMatrix+1)+"\t" 
                                                        +  "  index["+z+"]: " + rowInfo.get(z));
                                                        System.exit(0);
                                                    }
                                                    
                                                    //we can see how this test case output is almost identical but this time the issue
                                                    //relates to inadequate items in Matrix B
                                                    //Integer [][][]test = new Integer[][][] {   {{1,2,3},{4,5,6}},  {{7,8},{10},{11,12}},  { {13,14}, {15,16} } };
                                                    //Matrix(0):
                                                    //[1, 2, 3]
                                                    //[4, 5, 6]

                                                    //Matrix(1):
                                                    //[7, 8]
                                                    //[10,X]
                                                    //[11, 12]
                                                    
                                                    catch (IndexOutOfBoundsException e)
                                                    {
                                                        System.out.println("1Matrix("+(counter)+")" + "   row:" + rowCounterFirstMatrix+"\t" +  "value at index["+z+"]: " + ee.get(posPrevMatrixRowLevel) 
                                                        +"  has no available supporting entry in \nMatrix("+(counter+1)+")" + "   row:" + (rowCounterNextMatrix+1)+"\t" +  "\t index["+z+"]: " + " row content:" + rowInfo);
                                                        
                                                        //we would need to exit out here WITHOUT fail
                                                        //since there are no two elements available to multiply under validation rules
                                                        System.exit(0);
                                                    }
                                                    
                                                    //This outputs on the screen multiplication taking place
                                                    System.out.println(ee.get(posPrevMatrixRowLevel) + " X " + rowInfo.get(z) + "= " + multiplication);
                                                    //This is example where it will reach here
                                                    //Integer [][][]test = new Integer[][][] {   {{1,2,3},{4,5,6}},  {{7,8,999},{10,11},{11,12}}}; 

                                                    //Matrix(0):
                                                    //[1, 2, 3]
                                                    //[4, 5, 6]

                                                    //Matrix(1):  
                                                    //[7, 8, 999]
                                                    //[10, 11]
                                                    //[11, 12]
                                                    
                                                    System.out.println(z);
                                                    System.out.println(rowCounterFirstMatrix);
                                                    
                                                    //since we are referring to writing into dimensions of the matrixMultiplication,
                                                    //anything that will violate dimensions, there is no point of return and need to invalid the
                                                    //matrix
                                                    
                                                    //System.out.println(rowCounterFirstMatrix);
                                                    //System.out.println(z);
                                                    //System.out.println(multiplication);
                                                    //System.out.println(matrixMultiplication[rowCounterFirstMatrix][z]);
                                                    
                                                    try
                                                    {
                                                        matrixMultiplication[rowCounterFirstMatrix][z] = matrixMultiplication[rowCounterFirstMatrix][z]  
                                                        + multiplication;
                                                    }
                                                    
                                                    //There are several test matrices that fall under this cateogory
                                                    catch (ArrayIndexOutOfBoundsException e)
                                                    {
                                                        System.out.println("\n2The current dimension of matrixMultiplication: " 
                                                        + "["+matrix.size()+"]"+"["+nextMatrix.get(0).size()+"]");
                                                        System.out.println("1It EXPECTS: " + nextMatrix.get(0).size() + " elements per row");
                                                        System.out.println("matrix" + "("+(counter+1)+")" + " row:" + rowCounterNextMatrix +  " " 
                                                        + nextMatrix.get(m) + " size: " + nextMatrix.get(m).size() + " invalidates this");
                                                        
                                                        //I have also included a more user friendly message to ensure end user can visualise
                                                        //issue much easier....
                                                        System.out.println("1Matrix("+(counter)+")" + "   row:" + rowCounterFirstMatrix+"\t" +  "value at index["+z+"]: " + ee.get(posPrevMatrixRowLevel) 
                                                        +"  has no available supporting entry in \nMatrix("+(counter+1)+")" + "   row:" + (rowCounterNextMatrix+1)+"\t" +  "\t index["+z+"]: " + " row content:" + nextMatrix.get(m+1));
                                                        
                                                        //System.out.println(matrices);
                                                        System.exit(0);
                                                    }
                                                    System.out.println("Value in multiplication matrix: " 
                                                    + matrixMultiplication[rowCounterFirstMatrix][z] + "\t\t" 
                                                    + "["+rowCounterFirstMatrix+"],["+z+"]" );
                                                    
                                                    //it now considers this as a write to an index.
                                                    //it might be its completed (1x2)
                                                    //or (1x2)+(5x4)  to represent transactions
                                                    numWritesAtIndexWithinMultiplicationMatrix++;
                            
                                                    //we now store the calculation undertaken in multidimensional StringJoiner....
                                                    matrixMultiplicationCalculations[rowCounterFirstMatrix][z].add("(" + ee.get(posPrevMatrixRowLevel) 
                                                    + "x" +rowInfo.get(z) +" ="+String.valueOf(multiplication)+")");
                                                    
                                                    //This outputs the calculation that has been stored
                                                    System.out.println(matrixMultiplicationCalculations[rowCounterFirstMatrix][z]+"\n");
                                                    
                                                    //everytime that z (which counts through columns in matrix B) reaches end matrixMultiplication
                                                    //it is considered to have written to last index in the row
                                                    if (z==(matrixMultiplication[0].length-1))
                                                    {
                                                        //written to last index in row of matrixMultiplication
                                                        numWritesToLastIndexOnRowMultiplicationMatrix++;
                                                        
                                                        //we know that if it has written to the last index same number times as rows
                                                        //in Matrix B
                                                        if (numWritesToLastIndexOnRowMultiplicationMatrix==nextMatrix.size())
                                                        {
                                                            //it sets the flag
                                                            hasReachedEndRow=true;
                                                        }
                                                    }
                                                    //we know in Matrix A, we will now move to next available row
                                                    posPrevMatrixRowLevel++;
                                                    
                                                    //We are now ready to progress to the next row in Matrix B
                                                    //I had to move this below the try and catch since those blocks relied on this
                                                    //variable and otherwise gave wrong information on screen.
                                                    //it did not affect the logic previously.
                                                    //because I have removed several system.exit(0) it is finding that code is executing further
                                                    //and geting more opportunities to increment counter of row.
                                                    
                                                    break;
                                                }
                                                else
                                                {
                                                    System.out.println("---------------------------------------------------");
                                                    System.out.println("previous written to each index location of multiplication matrix: " 
                                                    + nextMatrix.size() + "  MAXIMUM times");
                                                    System.out.println("---------------------------------------------------");
						    
						                            //this was considered ineffective since it performed break out of current for loop ONLY 
                                                    //break;
                                                    
                                                    //I need to take following actions to ensure code breaks out following for loops which will then force
                                                    // a new column
                                                    //for (int y=0; y<nextMatrix.size();y++)
                                                    //for (int m=0; m<nextMatrix.size(); m++)
                                                    //for (int j=0;j<=nextMatrix.get(m).size();j++)
                                                    //best action would be to maximise each value
                                                    //since j relies on m, I had to set value of m so that it does not render
                                                    //ArrayIndexOutOfBoundsException
                                                    j=nextMatrix.get(m).size();
                                                    m=nextMatrix.size()-1;
                                                    
                                                    //Since the loop is phased out for this variable as per chatGPT advise, this is phased out
                                                    //y=nextMatrix.size();
						                            //IT WILL EFFECTIVELY BREAK OUT OF 5 BRACES (})
                                                }
                                            }
                                            
                                             //issue I faced with this variable now is that since I relaxed the number of
                                            //System.exit(0) I found the code looped more times in this code block
                                            //so the variable kept increasing.. It did not have impact on logic of main code
                                            //since this variable resets once it gets out of this area (5 braces downwards)
                                            //it had impact on screen messages. So I had to set a scenario where it reversed the rowcount again
                                            rowCounterNextMatrix=0;
                                        }
                                    //}     
                                //}  //we have to get the code to break beyond here (when we have written to each index location maximum times).
                                    //This is principle of forcing new column.
                            }
                            //hasProcessed=true;
                        //}
                        //IN HERE, CAN CONSIDER CHANGING ee output to actual elements that have been processed
                        //it would be row length of the matrixMultiplication
                        System.out.println("Processed matrix("+(counter+1)+")  column("+k+")"+  " AGAINST " + "MATRIX("+counter+")  row("+rowCounterFirstMatrix+"): " + ee);
                    }
                    System.out.println("REACH1");
                    //variable mainly used during validation checks.. It refers to elements in Matrix A/C               
                    numElementsInRow++;
                }
                System.out.println("REACH2");
                //We are simply outputting Matrix B to the screen
                if(counter!=(matricesSize-1))
                {
                    for (List<Integer> rows: matrices.get((counter+1)))
                    {
                        rowsNextMatrix++;
                    }
                    
                    //I have to make this slightly more strict
                    //Since we know if elements in row in Matrix A (row 2) is greater than matrix A (row 1)
                    //we can strictly contine with calculations similar to if in Matrix B (row 2) is greater length (row 1)
                    //But we would need to capture all information in a similar way
                    //And put information into excessColumnReport instead
                    // As long as these are fulfilled Numbers columms in Matrix A (taken from .get(0)  = Number rows in Matrix B (.size())
                    //we expect those to be accepted values in matrixMultiplication
                    
                    if ((numElementsInRow!=rowsNextMatrix && numElementsInRow<matrices.get(counter+1).size()) || numElementsInRow==0)
                    {
                        System.out.println("INVALID VERIFICATION=>  columns " + "("+numElementsInRow+") in Matrix"  + "("+counter+")" 
                        + "(Row:" +rowCounterFirstMatrix + " content: " +  rowMatrix +  ") should match " 
                        +  rowsNextMatrix + " rows in matrix("+(counter+1)+")");
                        System.out.println(matrices);
                        System.exit(0);
                    }
                    
                    //end user receives information that at minimum rows in Matrix A = columns Matrix B
                    else
                    {
                        System.out.println("VERIFICATION SUCCESSFUL(MATRIX("+counter+")  row("+rowCounterFirstMatrix+")) =>  ***COLUMNS" + "("+numElementsInRow+")\t   Matrix("+(counter+1)+") =>  ***ROWS("+rowsNextMatrix+")");
                        
                        excessColItems = new StringJoiner(",");
                        //we are using this since there are more columns than reference expected
                        //we could also use matrix.get(0) since this determines the properties for matrix multiplcation

                        //NOTE: We are not relying on prevEntry since I expect no duplications since these are issues with
                        //Matrix A/C as oppose to Matrix B.. And the most outer for loops are focused around Matrix A/C
                        System.out.println("REACH4");
                        //number elements in row in Matrix A should be same as numbers rows (column length) in Matrix B
                        if (numElementsInRow>nextMatrix.size())
                        {
                            currentColumnEntry = "columns" + "("+numElementsInRow+") in Matrix"  + "("+counter+")" 
                        + "(Row:" +rowCounterFirstMatrix + " content: " +  rowMatrix +  ") inconsistent with " 
                        +  rowsNextMatrix + " rows in matrix("+(counter+1)+").";
                            
                            System.out.println(currentColumnEntry);
                            
                            //We once again perform seekPosition since to investigating excess row lengths for Matrix B                            
                            for (int x: matrix.get(rowCounterFirstMatrix))
                            {
                                seekPosition++;
                                            
                                if (seekPosition>rowsNextMatrix)
                                {
                                    //We capture values in the row if it exceeds length of matrixMultiplication
                                    excessColItems.add(String.valueOf(x));
                                }
                            }
                            //Similar level of logic to excessRowReport
                            if (seekPosition!=0)
                            {
                                System.out.println("["+excessColItems+"] are exempt from multiplcation matrix");
                                
                                excessColReport.add(currentColumnEntry);
                                excessColReport.add("["+excessColItems+"] are exempt from multiplcation matrix"+"\n");
                            }
                            seekPosition=0;
                        }
                        
                    }
                    rowsNextMatrix=0;
                }
                numElementsInRow=0;
                rowCounterFirstMatrix++;
            }
            rowCounterFirstMatrix=0;
                    
            counter=counter+2;
            rowsNextMatrix=0;
            //numMatrixes++;
             
        
        }while (counter<matricesSize);
        
        //This prints out the final matrixMultiplication onto the screen
	System.out.println("\nMatrix Multiplication");
        for (Integer []m: matrixMultiplication)
        {
            System.out.println(Arrays.toString(m));
        }
        
        System.out.println("\n***CALCULATION STEPS*******");
    
        //We stored all the calculations undertaken of the last two matrixes multiplied together in
        //matrixMultiplicationCalculations
        //We now retrieve the content on a row to row basis
        //I figured this was quickest viable route, however I have created another
        //StringJoiner to ensure I can finalise padding for the final time
        /*
        for (StringJoiner []s: matrixMultiplicationCalculations)
        {
            System.out.println(Arrays.toString(s));
        }
        */
        
        for (int q=0; q<matrixMultiplicationCalculations.length; q++)
        {
            matrixRowCalculations = new StringJoiner("");
                
            for (int r=0; r<matrixMultiplicationCalculations[0].length; r++)
            {
                matrixRowCalculations.add(" ["+matrixMultiplicationCalculations[q][r]+"]");
                    
            }
            System.out.println(matrixRowCalculations);
        }
        
        //this now stores the result from the matrixMultiplication
        //and adds it into List<List<Integer>> so that it matches expected datatype of the challenge
        for (Integer []m: matrixMultiplication)
        {
            matrixMultiplicationList.add(Arrays.asList(m));
        }
        
        //The reports are rendered onto the screen
        System.out.println("\n-----MATRIX A   Analysis------");
        System.out.println(excessColReport);
        
        System.out.println("\n\n-----MATRIX B   Analysis------");
        System.out.println(excessRowReport);
        
        //The value is returned back to method call in main method
        return matrixMultiplicationList;
    }
}