module strassens_matmul;
 
debug {
    static import std;
}
 
package {
    ulong getRowSize(T)(T[][] mat) {
        return mat[0].length;
    }
 
    ulong getColumnSize(T)(T[][] mat) {
        return mat.length;
    }
 
    T[][] createMatrix(T)(const ulong rowSize, const ulong columnSize) {
        return new T[][](rowSize, columnSize);
    }
 
    /// row and column are 0 index-based
    T* getPointPtr(T)(T[][] mat, const ulong row, const ulong column) {
        return &mat[row][column];
    }
 
    T getPointCopy(T)(T[][] mat, const ulong row, const ulong column) {
        return mat[row][column];
    }
 
    T[][] mulIterative(T)(scope const T[][] mat1, scope const T[][] mat2) {
        auto result = createMatrix!T(mat1.getRowSize, mat2.getColumnSize);
        foreach (row; 0 .. mat1.getRowSize()) {
            foreach (column; 0 .. mat2.getColumnSize()) {
                T value;
                foreach (i; 0 .. mat1.getRowSize()) {
                    value += mat1.getPointCopy(row, i) * mat2.getPointCopy(i, column);
                }
                *result.getPointPtr(row, column) = value;
            }
        }
        return result;
    }
}
 
void main() {
    const uint[][] matA = [[10, 20, 10], [4, 5, 6], [2, 3, 5]];
    const uint[][] matB = [[3, 2, 4], [3, 3, 9], [4, 4, 2]];
    const uint[][] matC = [[130, 120, 240], [51, 47, 73], [35, 33, 45]];
    assert(matA.mulIterative(matB) == matC);
}
 
				bW9kdWxlIHN0cmFzc2Vuc19tYXRtdWw7CgpkZWJ1ZyB7CiAgICBzdGF0aWMgaW1wb3J0IHN0ZDsKfQoKcGFja2FnZSB7CiAgICB1bG9uZyBnZXRSb3dTaXplKFQpKFRbXVtdIG1hdCkgewogICAgICAgIHJldHVybiBtYXRbMF0ubGVuZ3RoOwogICAgfQoKICAgIHVsb25nIGdldENvbHVtblNpemUoVCkoVFtdW10gbWF0KSB7CiAgICAgICAgcmV0dXJuIG1hdC5sZW5ndGg7CiAgICB9CgogICAgVFtdW10gY3JlYXRlTWF0cml4KFQpKGNvbnN0IHVsb25nIHJvd1NpemUsIGNvbnN0IHVsb25nIGNvbHVtblNpemUpIHsKICAgICAgICByZXR1cm4gbmV3IFRbXVtdKHJvd1NpemUsIGNvbHVtblNpemUpOwogICAgfQoKICAgIC8vLyByb3cgYW5kIGNvbHVtbiBhcmUgMCBpbmRleC1iYXNlZAogICAgVCogZ2V0UG9pbnRQdHIoVCkoVFtdW10gbWF0LCBjb25zdCB1bG9uZyByb3csIGNvbnN0IHVsb25nIGNvbHVtbikgewogICAgICAgIHJldHVybiAmbWF0W3Jvd11bY29sdW1uXTsKICAgIH0KCiAgICBUIGdldFBvaW50Q29weShUKShUW11bXSBtYXQsIGNvbnN0IHVsb25nIHJvdywgY29uc3QgdWxvbmcgY29sdW1uKSB7CiAgICAgICAgcmV0dXJuIG1hdFtyb3ddW2NvbHVtbl07CiAgICB9CgogICAgVFtdW10gbXVsSXRlcmF0aXZlKFQpKHNjb3BlIGNvbnN0IFRbXVtdIG1hdDEsIHNjb3BlIGNvbnN0IFRbXVtdIG1hdDIpIHsKICAgICAgICBhdXRvIHJlc3VsdCA9IGNyZWF0ZU1hdHJpeCFUKG1hdDEuZ2V0Um93U2l6ZSwgbWF0Mi5nZXRDb2x1bW5TaXplKTsKICAgICAgICBmb3JlYWNoIChyb3c7IDAgLi4gbWF0MS5nZXRSb3dTaXplKCkpIHsKICAgICAgICAgICAgZm9yZWFjaCAoY29sdW1uOyAwIC4uIG1hdDIuZ2V0Q29sdW1uU2l6ZSgpKSB7CiAgICAgICAgICAgICAgICBUIHZhbHVlOwogICAgICAgICAgICAgICAgZm9yZWFjaCAoaTsgMCAuLiBtYXQxLmdldFJvd1NpemUoKSkgewogICAgICAgICAgICAgICAgICAgIHZhbHVlICs9IG1hdDEuZ2V0UG9pbnRDb3B5KHJvdywgaSkgKiBtYXQyLmdldFBvaW50Q29weShpLCBjb2x1bW4pOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgKnJlc3VsdC5nZXRQb2ludFB0cihyb3csIGNvbHVtbikgPSB2YWx1ZTsKICAgICAgICAgICAgfQogICAgICAgIH0KICAgICAgICByZXR1cm4gcmVzdWx0OwogICAgfQp9Cgp2b2lkIG1haW4oKSB7CiAgICBjb25zdCB1aW50W11bXSBtYXRBID0gW1sxMCwgMjAsIDEwXSwgWzQsIDUsIDZdLCBbMiwgMywgNV1dOwogICAgY29uc3QgdWludFtdW10gbWF0QiA9IFtbMywgMiwgNF0sIFszLCAzLCA5XSwgWzQsIDQsIDJdXTsKICAgIGNvbnN0IHVpbnRbXVtdIG1hdEMgPSBbWzEzMCwgMTIwLCAyNDBdLCBbNTEsIDQ3LCA3M10sIFszNSwgMzMsIDQ1XV07CiAgICBhc3NlcnQobWF0QS5tdWxJdGVyYXRpdmUobWF0QikgPT0gbWF0Qyk7Cn0K
				
				
				
				
				
			 
			
				
			
			
				
	
		
	
	
	prog.d:30:42: error: template strassens_matmul.getRowSize cannot deduce function from argument types !()(const(uint[][])), candidates are:
         auto result = createMatrix!T(mat1.getRowSize, mat2.getColumnSize);
                                          ^
prog.d:8:11: note: strassens_matmul.getRowSize(T)(T[][] mat)
     ulong getRowSize(T)(T[][] mat) {
           ^
prog.d:30:59: error: template strassens_matmul.getColumnSize cannot deduce function from argument types !()(const(uint[][])), candidates are:
         auto result = createMatrix!T(mat1.getRowSize, mat2.getColumnSize);
                                                           ^
prog.d:12:11: note: strassens_matmul.getColumnSize(T)(T[][] mat)
     ulong getColumnSize(T)(T[][] mat) {
           ^
prog.d:31:43: error: template strassens_matmul.getRowSize cannot deduce function from argument types !()(const(uint[][])), candidates are:
         foreach (row; 0 .. mat1.getRowSize()) {
                                           ^
prog.d:8:11: note: strassens_matmul.getRowSize(T)(T[][] mat)
     ulong getRowSize(T)(T[][] mat) {
           ^
prog.d:48:29: error: template instance strassens_matmul.mulIterative!uint error instantiating
     assert(matA.mulIterative(matB) == matC);
                             ^
prog.d:48:29: error: function strassens_matmul.mulIterative!uint.mulIterative is not accessible from module strassens_matmul
     assert(matA.mulIterative(matB) == matC);
                             ^