using System;
using System.Security;
using System.Runtime.InteropServices;

namespace mkl
{
    public unsafe static class IntelMathLib
    {
        public static long SetMode(long mode)
        {
            return VML.SetMode(mode);
        }

        /** Managed callback function */
        public static float[] Sin(float[] vector)
        {
            float[] returnVector = new float[vector.Length];
            VML.vsSin(vector.Length, vector, returnVector);
            return returnVector;
        }

        /** Managed callback function */
        public static float[] Cos(float[] vector)
        {
            float[] returnVector = new float[vector.Length];
            VML.vsCos(vector.Length, vector, returnVector);
            return returnVector;
        }

        public static void SinByRef(ref float[] vector)
        {
            VML.vsSin(vector.Length, vector, vector);
        }

        /** Managed callback function */
        public static void CosByRef(float[] vector)
        {
            VML.vsCos(vector.Length, vector, vector);
        }

        /** Managed callback function */
        public static void Sin(int length, float* vector, float* result)
        {
            VML.vmsSin(length, vector, result);
        }

        /** Managed callback function */
        public static void Cos(int length, float* vector, float* result)
        {
            VML.vsCos(length, vector, result);
        }

        /** Managed callback function */
        public static void Hypot(int length, float* reals, float* images, float* result)
        {
            VML.vsHypot(length, reals, images, ref result);
        }

        /** Managed callback function */
        public static void Atan2(int length, float* reals, float* images, float* result)
        {
            VML.vsAtan2(length, images, reals, ref result);
        }

        /** Managed callback function */
        public static void Mult(int length, float* a, float* b, float* result)
        {
            VML.vsMul(length, a, b, ref result);
        }

        /** Managed callback function */
        public static void Add(int length, float* a, float* b, float* result)
        {
            VML.vsAdd(length, a, b, ref result);
        }

        public static float Sin(float val)
        {
            float[] valVector = new float[1] { val };
            VML.vsSin(1, valVector, valVector);
            return valVector[0];
        }

        /** Managed callback function */
        public static float Cos(float val)
        {
            float[] valVector = new float[1] { val };
            VML.vsCos(1, valVector, valVector);
            return valVector[0];
        }

        /** Managed callback function */
        public static float Sin_F(float[] vector)
        {
            VML.vsSin(vector.Length, vector, vector);
            return vector[0];
        }

        /** Managed callback function */
        public static float Cos_F(float[] vector)
        {
            VML.vsCos(vector.Length, vector, vector);
            return vector[0];
        }
    }
   
    public sealed class VML
    {
        private VML() { }

        public static long SetMode(long mode)
        {
            return VMLNative.vmlSetMode(mode);
        }

        /** VML vdDiv wrapper */
        public static void vddiv(int N, double[] A, double[] B, double[] Y)
        {
            VMLNative.vdDiv(N, A, B, Y);
        }

        public static void vsSin(int N, float[] A, float[] Y)
        {
            VMLNative.vsSin(N, A, Y);
        }

        public unsafe static void vmsSin(int N, float* A, float* Y)
        {
            VMLNative.vmsSin(N, A, Y, 3);
        }

        public unsafe static void vsCos(int N, float* A, float* Y)
        {
            VMLNative.vsCos(N, A, Y);
            //VMLNative.vmsCos(N, A, Y, 3);
        }

        public unsafe static void vsHypot(int N, float* a, float* b, ref float* c)
        {
            //VMLNative.vmsHypot(N, a, b, c, 3);
            VMLNative.vsHypot(N, a, b, c);
        }

        public unsafe static void vsAtan2(int N, float* a, float* b, ref float* c)
        {
            //VMLNative.vmsAtan2(N, a, b, c, 3);
            VMLNative.vsAtan2(N, a, b, c);
        }

        public unsafe static void vsMul(int N, float* A, float* B, ref float* Y)
        {
            VMLNative.vsMul(N, A, B, Y);
            //VMLNative.vmsMul(N, A, B, Y, 3);
        }

        public unsafe static void vsAdd(int N, float* A, float* B, ref float* Y)
        {
            VMLNative.vsAdd(N, A, B, Y);
            //VMLNative.vmsAdd(N, A, B, Y, 3);
        }

        public static void vsCos(int N, float[] A, float[] Y)
        {
            VMLNative.vsCos(N, A, Y);
        }

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
        public struct VmlErrorContext
        {
            public int iCode;   /* Error status value */
            public int iIndex;  /* Index for bad array element,
                                 * or bad array dimension,
                                 *  or bad array pointer */
            public double dbA1; /* Error argument 1 */
            public double dbA2; /* Error argument 2 */
            public double dbR1; /* Error result 1 */
            public double dbR2; /* Error result 2 */
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
            public String cFuncName; /* Function name */
            int iFuncNameLen;   /* Length of functionname*/
        }
        /** VML callback */
        /** Attribute UnmanagedFunctionPointer requires .NET Framework >=2.0 */
        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate int
        vmlDelegateErrorCallBack(ref VmlErrorContext handle);
        /** VML vmlSetErrorCallBack wrapper */
        public static vmlDelegateErrorCallBack
        vmlSetErrorCallBack(vmlDelegateErrorCallBack handle)
        {
            return VMLNative.vmlSetErrorCallBack(handle);
        }
        /** VML vmlClearErrorCallBack wrapper */
        public static vmlDelegateErrorCallBack vmlClearErrorCallBack()
        {
            return VMLNative.vmlClearErrorCallBack();
        }
    }

    /** VML native declarations */
    [SuppressUnmanagedCodeSecurity]
    internal sealed unsafe class VMLNative
    {
        private VMLNative() { }

        /** VML vdDiv native declaration */
        [DllImport("mkl_rt.dll", CallingConvention = CallingConvention.Cdecl,
             ExactSpelling = true, SetLastError = false)]
        internal static extern int vdDiv(int N,
            [In] double[] A, [In] double[] B, [Out] double[] Y);

        /** VML vdDiv native declaration */
        [DllImport("mkl_rt.dll", CallingConvention = CallingConvention.Cdecl,
             ExactSpelling = true, SetLastError = false)]
        internal static extern int vsSin(int N,
            [In] float[] A, [Out] float[] Y);

        /** VML vdDiv native declaration */
        [DllImport("mkl_rt.dll", CallingConvention = CallingConvention.Cdecl,
             ExactSpelling = true, SetLastError = false)]
        internal static extern int vmsSin(int N,
            [In] float* A, [Out] float* Y, long mode);

        /** VML vdDiv native declaration */
        [DllImport("mkl_rt.dll", CallingConvention = CallingConvention.Cdecl,
             ExactSpelling = true, SetLastError = false)]
        internal static extern int vmsCos(int N,
            [In] float* A, [Out] float* Y, long mode);

        /** VML vdDiv native declaration */
        [DllImport("mkl_rt.dll", CallingConvention = CallingConvention.Cdecl,
             ExactSpelling = true, SetLastError = false)]
        internal static extern int vsCos(int N,
            [In] float* A, [Out] float* Y);
       
        /** VML vdDiv native declaration */
        [DllImport("mkl_rt.dll", CallingConvention = CallingConvention.Cdecl,
             ExactSpelling = true, SetLastError = false)]
        internal static extern int vmsMul(int N,
            [In] float* A, [In] float* B, [Out] float* Y, long mode);

        [DllImport("mkl_rt.dll", CallingConvention = CallingConvention.Cdecl,
             ExactSpelling = true, SetLastError = false)]
        internal static extern int vsMul(int N,
            [In] float* A, [In] float* B, [Out] float* Y);

        /** VML vmsAdd native declaration */
        [DllImport("mkl_rt.dll", CallingConvention = CallingConvention.Cdecl,
             ExactSpelling = true, SetLastError = false)]
        internal static extern int vmsAdd(int N,
            [In] float* A, [In] float* B, [Out] float* Y, long mode);

        /** VML vmsAdd native declaration */
        [DllImport("mkl_rt.dll", CallingConvention = CallingConvention.Cdecl,
             ExactSpelling = true, SetLastError = false)]
        internal static extern int vsAdd(int N,
            [In] float* A, [In] float* B, [Out] float* Y);

        /** VML vdDiv native declaration */
        [DllImport("mkl_rt.dll", CallingConvention = CallingConvention.Cdecl,
             ExactSpelling = true, SetLastError = false)]
        internal static extern int vsHypot(int N,
            [In] float* a, [In] float* b, [Out] float* c);

        /** VML vdDiv native declaration */
        [DllImport("mkl_rt.dll", CallingConvention = CallingConvention.Cdecl,
             ExactSpelling = true, SetLastError = false)]
        internal static extern int vmsHypot(int N,
            [In] float* a, [In] float* b, [Out] float* c, long mode);

        [DllImport("mkl_rt.dll", CallingConvention = CallingConvention.Cdecl,
            ExactSpelling = true, SetLastError = false)]
        internal static extern int vmsAtan2(int N,
            [In] float* a, [In] float* b, [Out] float* c, long mode);

        [DllImport("mkl_rt.dll", CallingConvention = CallingConvention.Cdecl,
           ExactSpelling = true, SetLastError = false)]
        internal static extern int vsAtan2(int N,
            [In] float* a, [In] float* b, [Out] float* c);

        /** VML vdDiv native declaration */
        [DllImport("mkl_rt.dll", CallingConvention = CallingConvention.Cdecl,
             ExactSpelling = true, SetLastError = false)]
        internal static extern int vsCos(int N,
            [In] float[] A, [Out] float[] Y);

        [DllImport("mkl_rt.dll", CallingConvention = CallingConvention.Cdecl,
           ExactSpelling = true, SetLastError = false)]
        internal static extern long vmlSetMode(long mode); 

        /** VML Callback */
        /** VML native vmlSetErrorCallBack declaration */
        [DllImport("mkl_rt.dll", CallingConvention = CallingConvention.Cdecl,
             ExactSpelling = true, SetLastError = false)]
        internal static extern VML.vmlDelegateErrorCallBack
            vmlSetErrorCallBack(VML.vmlDelegateErrorCallBack handle);
        /** VML native vmlClearErrorCallBack declaration */
        [DllImport("mkl_rt.dll", CallingConvention = CallingConvention.Cdecl,
             ExactSpelling = true, SetLastError = false)]
        internal static extern VML.vmlDelegateErrorCallBack
            vmlClearErrorCallBack();       
    }
}
