/// <summary>
        /// GEneral Matrix Multiply this array
        /// </summary>
        /// <overloads>General Matrix Multiply for double, float, complex and fcomplex arrays</overloads>
        /// <param name="A"><![CDATA[ILArray<>]]> matrix A</param>
        /// <param name="B"><![CDATA[ILArray<>]]> matrix B</param>
        /// <returns><![CDATA[ILArray<double>]]> new array - result of matrix multiplication</returns>
        /// <remarks>Both arrays must be matrices. The matrix will be multiplied only
        /// if dimensions match accordingly. Therefore B's number of rows must
        /// equal A's number of columns. An Exception will be thrown otherwise.
        /// The multiplication will carried out on BLAS libraries, if availiable and the
        /// storage memory structure meets BLAS's requirements. If not it will be done inside .NET's
        /// framework 'by hand'. This is especially true for referencing storages with
        /// irregular dimensions. However, even GEMM on those reference storages linking into
        /// a physical storage can (an will) be carried out via BLAS dll's, if the spacing
        /// into dimensions matches the requirements of BLAS. Those are:
        /// <list>
        /// <item>the elements of one dimension will be adjecently layed out, and</item>
        /// <item>the elements of the second dimension must be regular (evenly) spaced</item>
        /// </list>
        /// <para>For reference arrays where the spacing between adjecent elements do not meet the
        /// requirements above, the matrix multiplication will be made without optimization and
        /// therefore suffer from low performance in relation to solid arrays. See <a href="http://ilnumerics.net?site=5142">online documentation: referencing for ILNumerics.Net</a></para>
        /// </remarks>
        /// <exception cref="ILNumerics.Exceptions.ILArgumentSizeException">if at least one arrays is not a matrix</exception>
        /// <exception cref="ILNumerics.Exceptions.ILDimensionMismatchException">if the size of both matrices do not match</exception>
        public static ILArray <fcomplex> multiply(ILArray <fcomplex> A, ILArray <fcomplex> B)
        {
            ILArray <fcomplex> ret = null;

            if (A.Dimensions.NumberOfDimensions != 2 ||
                B.Dimensions.NumberOfDimensions != 2)
            {
                throw new ILArgumentSizeException("Matrix multiply: arguments must be 2-d.");
            }
            if (A.Dimensions[1] != B.Dimensions[0])
            {
                throw new ILDimensionMismatchException("Matrix multiply: inner matrix dimensions must match.");
            }
            // decide wich method to use
            // test auf Regelmigkeit der Dimensionen
            int  spacingA0;
            int  spacingA1;
            int  spacingB0;
            int  spacingB1;
            char transA, transB;

            fcomplex [] retArr = null;
            isSuitableForLapack(A, B, out spacingA0, out spacingA1, out spacingB0, out spacingB1, out transA, out transB);
            if (A.m_dimensions.NumberOfElements > ILAtlasMinimumElementSize ||
                B.m_dimensions.NumberOfElements > ILAtlasMinimumElementSize)
            {
                // do BLAS GEMM
                retArr = new  fcomplex [A.m_dimensions[0] * B.m_dimensions[1]];
                if (((spacingA0 == 1 && spacingA1 > int.MinValue) || (spacingA1 == 1 && spacingA0 > int.MinValue)) &&
                    ((spacingB0 == 1 && spacingB1 > int.MinValue) || (spacingB1 == 1 && spacingB0 > int.MinValue)))
                {
                    ret = new  ILArray <fcomplex> (retArr, new ILDimension(A.m_dimensions[0], B.m_dimensions[1]));
                    if (transA == 't')
                    {
                        spacingA1 = spacingA0;
                    }
                    if (transB == 't')
                    {
                        spacingB1 = spacingB0;
                    }
                    unsafe
                    {
                        fixed(fcomplex *ptrC = retArr)
                        fixed(fcomplex * pA = A.m_data)
                        fixed(fcomplex * pB = B.m_data)
                        {
                            fcomplex *ptrA = pA + A.getBaseIndex(0);
                            fcomplex *ptrB = pB + B.getBaseIndex(0);

                            if (transA == 't')
                            {
                                spacingA1 = spacingA0;
                            }
                            if (transB == 't')
                            {
                                spacingB1 = spacingB0;
                            }
                            Lapack.cgemm(transA, transB, A.m_dimensions[0], B.m_dimensions[1],
                                         A.m_dimensions[1], ( fcomplex )1.0, (IntPtr)ptrA, spacingA1,
                                         (IntPtr)ptrB, spacingB1, ( fcomplex )1.0, retArr, A.m_dimensions[0]);
                        }
                    }
                    return(ret);
                }
            }
            // do GEMM by hand
            retArr = new  fcomplex [A.m_dimensions[0] * B.m_dimensions[1]];
            ret    = new  ILArray <fcomplex> (retArr, A.m_dimensions[0], B.m_dimensions[1]);
            unsafe {
                int in2Len1 = B.m_dimensions[1];
                int in1Len0 = A.m_dimensions[0];
                int in1Len1 = A.m_dimensions[1];
                fixed(fcomplex *ptrC = retArr)
                {
                    fcomplex *pC = ptrC;

                    for (int c = 0; c < in2Len1; c++)
                    {
                        for (int r = 0; r < in1Len0; r++)
                        {
                            for (int n = 0; n < in1Len1; n++)
                            {
                                *pC += A.GetValue(r, n) * B.GetValue(n, c);
                            }
                            pC++;
                        }
                    }
                }
            }
            return(ret);
        }