I think it would be beneficial for you to break the solution into quadrants that you are trying to fill. All this will be under the assumption that we will combine only 4 matrices in this 2x2 configuration. The same strategies described here can be applied to other sizes of combined matrices.
So, given the 4 matrices A, B, C, and D, we will try to build the resulting matrix in this layout:
+---+---+ | A | B | +---+---+ | C | D | +---+---+
Before we begin, we will need to figure out the size of the final result. This, hopefully, makes sense. We will have the upper half, lower half, left half and right half.
rows_top = max(rows_A, rows_B) rows_bottom = max(rows_C, rows_D) rows_result = rows_top + rows_bottom cols_left = max(cols_A, cols_C) cols_right = max(cols_B, cols_D) cols_result = cols_left + cols_right
Then we want to consider which areas of the result matrix we want to copy each of the 4 matrices. Given the beginning in the upper left corner, everything in the right half will be shifted by the size of the left half, everything in the lower half will be shifted by the size of the upper half. The offsets for each of the matrices will be as follows:
offset_A = (0, 0) offset_B = (0, cols_left) offset_C = (rows_top, 0) offset_D = (rows_top, cols_left)
Now with all this information, we can begin to create a matrix of results. Just copy the values โโfrom each matrix into the result using offsets.
So, in code, I would do the following:
// I'm just going to use plain 2D arrays here public T[,] Combine<T>(T[,] a, T[,] b, T[,] c, T[,] d) { // get the total rows var rows_top = Math.Max(a.GetLength(0), b.GetLength(0)); var rows_bottom = Math.Max(c.GetLength(0), d.GetLength(0)); var rows_result = rows_top + rows_bottom; // get the total columns var cols_left = Math.Max(a.GetLength(1), c.GetLength(1)); var cols_right = Math.Max(b.GetLength(1), d.GetLength(1)); var cols_result = cols_left + cols_right; // get the offsets var offset_a = Tuple.Create(0, 0); var offset_b = Tuple.Create(0, cols_left); var offset_c = Tuple.Create(rows_top, 0); var offset_d = Tuple.Create(rows_top, cols_left); // fill 'er up var result = new T[rows_result, cols_result]; Fill(result, a, offset_a); Fill(result, b, offset_b); Fill(result, c, offset_c); Fill(result, d, offset_d); return result; } public void Fill<T>(T[,] result, T[,] source, Tuple<int, int> offset) { for (var i = 0; i < source.GetLength(0); i++) for (var j = 0; j < source.GetLength(1); j++) result[offset.Item1 + i, offset.Item2 + j] = source[i, j]; }
Then, to demonstrate the result in terms of case 2:
const string A = "A", B = "B", C = "C", D = "D"; var a = new string[1,1] { { A }, }; var b = new string[3, 3] { { B, B, B }, { B, B, B }, { B, B, B }, }; var c = new string[2, 2] { { C, C }, { C, C }, }; var d = new string[4, 4] { { D, D, D, D }, { D, D, D, D }, { D, D, D, D }, { D, D, D, D }, }; var result = Combine(a, b, c, d);
This, of course, can be generalized to a matrix of any matrix size. The concept is the same at every stage of the process.
Given mxn matrices, we will try to build the resulting matrix in this layout:
+-----+-----+-----+ | 0,0 | ... | 0,n | +-----+-----+-----+ | ... | | ... | +-----+-----+-----+ | m,0 | ... | m,n | +-----+-----+-----+
Get the sizes of each of the fragments.
rows_0 = max(rows_0_0, ..., rows_0_n) ... rows_m = max(rows_m_0, ..., rows_m_n) rows_result = sum(rows_0, ..., rows_m) cols_0 = max(cols_0_0, ..., cols_m_0) ... cols_n = max(cols_0_n, ..., cols_m_n) cols_result = sum(cols_0, ..., cols_m)
Get offsets for each of the matrices. Each vertical slice is shifted to the left by the total number of columns in the previous vertical slices. Each horizontal slice is shifted down by the total number of rows in previous horizontal slices.
offset_0_0 = (0, 0) ... offset_m_n = (sum(rows_0, ..., rows_m-1), sum(cols_0, ..., cols_n-1))
So now we can create a matrix of results.
public T[,] Combine<T>(T[,][,] m) { // get the rows var rows = GetSliceRows(m); var rows_result = rows.Sum(); // get the cols var cols = GetSliceCols(m); var cols_result = cols.Sum(); // get the offsets var offsets = GetOffsets(rows, cols); // fill 'er up var result = new T[rows_result, cols_result]; Fill(result, m, offsets); return result; } public int[] GetSliceRows<T>(T[,][,] m) { var sliceRows = new int[m.GetLength(0)]; var segments = m.GetLength(1); for (var i = 0; i < sliceRows.Length; i++) { sliceRows[i] = Enumerable.Range(0, segments) .Select(j => m[i, j].GetLength(0)) .Max(); } return sliceRows; } public int[] GetSliceCols<T>(T[,][,] m) { var sliceCols = new int[m.GetLength(1)]; var segments = m.GetLength(0); for (var j = 0; j < sliceCols.Length; j++) { sliceCols[j] = Enumerable.Range(0, segments) .Select(i => m[i, j].GetLength(1)) .Max(); } return sliceCols; } public Tuple<int, int>[,] GetOffsets(int[] rows, int[] cols) { var offsets = new Tuple<int, int>[rows.Length, cols.Length]; for (var i = 0; i < rows.Length; i++) for (var j = 0; j < cols.Length; j++) offsets[i, j] = Tuple.Create( rows.Take(i).Sum(), cols.Take(j).Sum() ); return offsets; } public void Fill<T>(T[,] result, T[,][,] m, Tuple<int, int>[,] offsets) { for (var i = 0; i < m.GetLength(0); i++) for (var j = 0; j < m.GetLength(1); j++) Fill(result, m[i, j], offsets[i, j]); } public void Fill<T>(T[,] result, T[,] source, Tuple<int, int> offset) { for (var i = 0; i < source.GetLength(0); i++) for (var j = 0; j < source.GetLength(1); j++) result[offset.Item1 + i, offset.Item2 + j] = source[i, j]; }