// https://github.com/dotnet/corefx/blob/master/src/System.Numerics.Vectors/src/System/Numerics/Matrix4x4.cs
        //  public static Matrix4x4 Add(Matrix4x4 value1, Matrix4x4 value2)
        public static ovrMatrix4f Add(ovrMatrix4f _a, ovrMatrix4f _b)
        {
            // when is this of any use?

            ovrMatrix4f _o;
            __Matrix4x4 o = &_o;
            __Matrix4x4 a = &_a;
            __Matrix4x4 b = &_b;

            {
                o.M[0, 0] = a.M[0, 0] + b.M[0, 0];
                o.M[1, 0] = a.M[1, 0] + b.M[1, 0];
                o.M[2, 0] = a.M[2, 0] + b.M[2, 0];
                o.M[3, 0] = a.M[3, 0] + b.M[3, 0];

                o.M[0, 1] = a.M[0, 1] + b.M[0, 1];
                o.M[1, 1] = a.M[1, 1] + b.M[1, 1];
                o.M[2, 1] = a.M[2, 1] + b.M[2, 1];
                o.M[3, 1] = a.M[3, 1] + b.M[3, 1];

                o.M[0, 2] = a.M[0, 2] + b.M[0, 2];
                o.M[1, 2] = a.M[1, 2] + b.M[1, 2];
                o.M[2, 2] = a.M[2, 2] + b.M[2, 2];
                o.M[3, 2] = a.M[3, 2] + b.M[3, 2];

                o.M[0, 3] = a.M[0, 3] + b.M[0, 3];
                o.M[1, 3] = a.M[1, 3] + b.M[1, 3];
                o.M[2, 3] = a.M[2, 3] + b.M[2, 3];
                o.M[3, 3] = a.M[3, 3] + b.M[3, 3];

                // operating on fixed buffers, yet we want the dimensional array magic.
            }

            return(_o);
        }
        public static ovrMatrix4f Multiply(__Matrix4x4 a, __Matrix4x4 b, __Matrix4x4 c)
        {
            var ab = Multiply(a, b);
            //__ovrMatrix4f ab = &_ab;

            var abc = Multiply(&ab, c);

            return(abc);
        }
        //public static ovrMatrix4f TanAngleMatrixFromProjection(__ovrMatrix4f projection)
        public static ovrMatrix4f TanAngleMatrixFromProjection(ovrMatrix4f __projection)
        {
            ConsoleExtensions.trace("enter TanAngleMatrixFromProjection");

            __Matrix4x4 projection = &__projection;

            // A projection matrix goes from a view point to NDC, or -1 to 1 space.
            // Scale and bias to convert that to a 0 to 1 space.
            //const ovrMatrix4f tanAngleMatrix =
            //float[,] tanAngleMatrix =
            //{
            //    { 0.5f * projection.M[0,0], 0.5f * projection.M[0,1], 0.5f * projection.M[0,2] - 0.5f, 0.5f * projection.M[0,3] },
            //    { 0.5f * projection.M[1,0], 0.5f * projection.M[1,1], 0.5f * projection.M[1,2] - 0.5f, 0.5f * projection.M[1,3] },
            //    {0.0f, 0.0f, -1.0f, 0.0f},
            //    {0.0f, 0.0f, -1.0f, 0.0f }
            //};

            float[] tanAngleMatrix =
            {
                0.5f * projection.M[0,   0], 0.5f * projection.M[0,   1], 0.5f * projection.M[0, 2] - 0.5f, 0.5f * projection.M[0, 3],
                0.5f * projection.M[1,   0], 0.5f * projection.M[1,   1], 0.5f * projection.M[1, 2] - 0.5f, 0.5f * projection.M[1, 3],
                0.0f,                  0.0f,                 -1.0f, 0.0f,
                0.0f,                  0.0f,                 -1.0f, 0.0f
            };

            ovrMatrix4f that_data;
            __Matrix4x4 that = &that_data;

            //that_data.M = tanAngleMatrix;
            //that_data.M[0] = tanAngleMatrix[0];
            //for (int i = 0; i < 16; i++)
            //{
            //    // this will cause stack corruption. why?
            //    that.M[i, i] = tanAngleMatrix[i];
            //}

            for (int x = 0; x < 4; x++)
            {
                for (int y = 0; y < 4; y++)
                {
                    //that.M[x, y] = tanAngleMatrix[x, y];
                    //that.M[x, y] = tanAngleMatrix[x + 4 * y];

                    // memorylayout the same as for CLR?
                    that.M[x, y] = tanAngleMatrix[y + 4 * x];
                }
            }

            //jni/OVRWindWheelNDK.dll.c:2651:28: error: expected expression before '?' token
            //     singleArray7 = calloc [?][?];
            //                            ^

            ConsoleExtensions.trace("exit TanAngleMatrixFromProjection, stack still valid?");
            return(that_data);
        }
        public static ovrMatrix4f Transpose(__Matrix4x4 a)
        {
            ovrMatrix4f _o;
            __Matrix4x4 o = &_o;

            o.M[0, 0] = a.M[0, 0]; o.M[0, 1] = a.M[1, 0]; o.M[0, 2] = a.M[2, 0]; o.M[0, 3] = a.M[3, 0];
            o.M[1, 0] = a.M[0, 1]; o.M[1, 1] = a.M[1, 1]; o.M[1, 2] = a.M[2, 1]; o.M[1, 3] = a.M[3, 1];
            o.M[2, 0] = a.M[0, 2]; o.M[2, 1] = a.M[1, 2]; o.M[2, 2] = a.M[2, 2]; o.M[2, 3] = a.M[3, 2];
            o.M[3, 0] = a.M[0, 3]; o.M[3, 1] = a.M[1, 3]; o.M[3, 2] = a.M[2, 3]; o.M[3, 3] = a.M[3, 3];

            return(_o);
        }
        public static ovrMatrix4f CreateTranslation(float x, float y, float z, float scale = 1.0f)
        {
            ovrMatrix4f _o;
            __Matrix4x4 o = &_o;

            o.M[0, 0] = scale; o.M[0, 1] = 0.0f; o.M[0, 2] = 0.0f; o.M[0, 3] = x;
            o.M[1, 0] = 0.0f; o.M[1, 1] = scale; o.M[1, 2] = 0.0f; o.M[1, 3] = y;
            o.M[2, 0] = 0.0f; o.M[2, 1] = 0.0f; o.M[2, 2] = scale; o.M[2, 3] = z;
            o.M[3, 0] = 0.0f; o.M[3, 1] = 0.0f; o.M[3, 2] = 0.0f; o.M[3, 3] = 1.0f;

            return(_o);
        }
        public static ovrMatrix4f Multiply(__Matrix4x4 a, __Matrix4x4 b)
        {
            // perhaps the compiler could decide to optimize and move to a byref pointer version?


            // memory dereference magic
            // yet, in glsl we wont have pointers, all we will have is copy struct?
            // http://www.gamedev.net/topic/347232-glsl-pointers/

            // Error	3	A pointer must be indexed by only one value	X:\jsc.svn\examples\java\android\vr\OVRWindWheelNDK\OVRWindWheelNDK\References\VrApi.ovrMatrix4f.cs	93	98	OVRWindWheelNDK
            //Error	5	The type of a local declared in a fixed statement must be a pointer type	X:\jsc.svn\examples\java\android\vr\OVRWindWheelNDK\OVRWindWheelNDK\References\VrApi.ovrMatrix4f.cs	92	20	OVRWindWheelNDK

            ovrMatrix4f _o;
            __Matrix4x4 o = &_o;

            //__ovrMatrix4f a = &_a;
            //__ovrMatrix4f b = &_b;

            {
                o.M[0, 0] = a.M[0, 0] * b.M[0, 0] + a.M[0, 1] * b.M[1, 0] + a.M[0, 2] * b.M[2, 0] + a.M[0, 3] * b.M[3, 0];
                o.M[1, 0] = a.M[1, 0] * b.M[0, 0] + a.M[1, 1] * b.M[1, 0] + a.M[1, 2] * b.M[2, 0] + a.M[1, 3] * b.M[3, 0];
                o.M[2, 0] = a.M[2, 0] * b.M[0, 0] + a.M[2, 1] * b.M[1, 0] + a.M[2, 2] * b.M[2, 0] + a.M[2, 3] * b.M[3, 0];
                o.M[3, 0] = a.M[3, 0] * b.M[0, 0] + a.M[3, 1] * b.M[1, 0] + a.M[3, 2] * b.M[2, 0] + a.M[3, 3] * b.M[3, 0];

                o.M[0, 1] = a.M[0, 0] * b.M[0, 1] + a.M[0, 1] * b.M[1, 1] + a.M[0, 2] * b.M[2, 1] + a.M[0, 3] * b.M[3, 1];
                o.M[1, 1] = a.M[1, 0] * b.M[0, 1] + a.M[1, 1] * b.M[1, 1] + a.M[1, 2] * b.M[2, 1] + a.M[1, 3] * b.M[3, 1];
                o.M[2, 1] = a.M[2, 0] * b.M[0, 1] + a.M[2, 1] * b.M[1, 1] + a.M[2, 2] * b.M[2, 1] + a.M[2, 3] * b.M[3, 1];
                o.M[3, 1] = a.M[3, 0] * b.M[0, 1] + a.M[3, 1] * b.M[1, 1] + a.M[3, 2] * b.M[2, 1] + a.M[3, 3] * b.M[3, 1];

                o.M[0, 2] = a.M[0, 0] * b.M[0, 2] + a.M[0, 1] * b.M[1, 2] + a.M[0, 2] * b.M[2, 2] + a.M[0, 3] * b.M[3, 2];
                o.M[1, 2] = a.M[1, 0] * b.M[0, 2] + a.M[1, 1] * b.M[1, 2] + a.M[1, 2] * b.M[2, 2] + a.M[1, 3] * b.M[3, 2];
                o.M[2, 2] = a.M[2, 0] * b.M[0, 2] + a.M[2, 1] * b.M[1, 2] + a.M[2, 2] * b.M[2, 2] + a.M[2, 3] * b.M[3, 2];
                o.M[3, 2] = a.M[3, 0] * b.M[0, 2] + a.M[3, 1] * b.M[1, 2] + a.M[3, 2] * b.M[2, 2] + a.M[3, 3] * b.M[3, 2];

                o.M[0, 3] = a.M[0, 0] * b.M[0, 3] + a.M[0, 1] * b.M[1, 3] + a.M[0, 2] * b.M[2, 3] + a.M[0, 3] * b.M[3, 3];
                o.M[1, 3] = a.M[1, 0] * b.M[0, 3] + a.M[1, 1] * b.M[1, 3] + a.M[1, 2] * b.M[2, 3] + a.M[1, 3] * b.M[3, 3];
                o.M[2, 3] = a.M[2, 0] * b.M[0, 3] + a.M[2, 1] * b.M[1, 3] + a.M[2, 2] * b.M[2, 3] + a.M[2, 3] * b.M[3, 3];
                o.M[3, 3] = a.M[3, 0] * b.M[0, 3] + a.M[3, 1] * b.M[1, 3] + a.M[3, 2] * b.M[2, 3] + a.M[3, 3] * b.M[3, 3];

                // operating on fixed buffers, yet we want the dimensional array magic.
            }

            return(_o);
        }
        public static ovrMatrix4f CreateProjection(
            float minX,
            float maxX,
            float minY,
            float maxY,
            float nearZ,
            float farZ)
        {
            float width   = maxX - minX;
            float height  = maxY - minY;
            float offsetZ = nearZ;      // set to zero for a [0,1] clip space

            ovrMatrix4f that_data;
            __Matrix4x4 that = &that_data;

            // what about jagged arrays?
            if (farZ <= nearZ)
            {
                // place the far plane at infinity
                that.M[0, 0] = 2 * nearZ / width;
                that.M[0, 1] = 0;
                that.M[0, 2] = (maxX + minX) / width;
                that.M[0, 3] = 0;

                that.M[1, 0] = 0;
                that.M[1, 1] = 2 * nearZ / height;
                that.M[1, 2] = (maxY + minY) / height;
                that.M[1, 3] = 0;

                that.M[2, 0] = 0;
                that.M[2, 1] = 0;
                that.M[2, 2] = -1;
                that.M[2, 3] = -(nearZ + offsetZ);

                that.M[3, 0] = 0;
                that.M[3, 1] = 0;
                that.M[3, 2] = -1;
                that.M[3, 3] = 0;
            }
            else
            {
                // normal projection
                that.M[0, 0] = 2 * nearZ / width;
                that.M[0, 1] = 0;
                that.M[0, 2] = (maxX + minX) / width;
                that.M[0, 3] = 0;

                that.M[1, 0] = 0;
                that.M[1, 1] = 2 * nearZ / height;
                that.M[1, 2] = (maxY + minY) / height;
                that.M[1, 3] = 0;

                that.M[2, 0] = 0;
                that.M[2, 1] = 0;
                that.M[2, 2] = -(farZ + offsetZ) / (farZ - nearZ);
                that.M[2, 3] = -(farZ * (nearZ + offsetZ)) / (farZ - nearZ);

                that.M[3, 0] = 0;
                that.M[3, 1] = 0;
                that.M[3, 2] = -1;
                that.M[3, 3] = 0;
            }

            return(that_data);
        }
        public static ovrMatrix4f CreateRotation(float radiansX, float radiansY, float radiansZ)
        {
            // X:\jsc.svn\examples\c\Test\TestStackFloatArray\TestStackFloatArray\Class1.cs

            // http://stackoverflow.com/questions/9087563/maximum-native-memory-that-can-be-allocated-to-an-android-app

            // Error	3	Cannot initialize object of type 'float*' with a collection initializer	X:\jsc.svn\examples\java\android\vr\OVRWindWheelNDK\OVRWindWheelNDK\References\VrApi.ovrMatrix4f.cs	248	17	OVRWindWheelNDK

            // I/DEBUG   ( 9747): signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x8

            float sinX = math.sinf(radiansX);
            float cosX = math.cosf(radiansX);

            // Error	3	Cannot take the address of, get the size of, or declare a pointer to a managed type ('float[*,*]')	X:\jsc.svn\examples\java\android\vr\OVRWindWheelNDK\OVRWindWheelNDK\References\VrApi.ovrMatrix4f.cs	292	61	OVRWindWheelNDK

            // we will leak a lot of heap...
            // not the heap. the stack thankyou. almost playing GC?
            // since we are not returning this array, it should live on stack. this way we wont leak memory as we are running without GC..
            //float[] rotationX = stackalloc float[16];
            //float[] rotationXx = new float[16];

            //// I/xNativeActivity( 7804): \VrApi.ovrMatrix4f.cs:338 out of heap? errno: 12 Out of memory
            //if (rotationXx == null)
            //{
            //    // https://android.googlesource.com/platform/development/+/c817c5210e4207908b83faaf08a2c5b95251f871/ndk/platforms/android-5/arch-x86/usr/include/malloc.h

            //    ConsoleExtensions.trace("out of heap?");

            //    //I/System.Console(19158): 4ad6:480b enter GetAllNetworkInterfaces
            //    //I/xNativeActivity(18481): \VrApi.ovrMatrix4f.cs:343 out of heap? errno: 12 Out of memory
            //    //I/xNativeActivity(18481): \VrCubeWorld.AppThread.cs:71 mallinfo  total allocated space:  -2083023504
            //    //I/xNativeActivity(18481): \VrCubeWorld.AppThread.cs:72 mallinfo  total free  space:  76049040

            //    //malloc_h.malloc_stats();

            //    VrCubeWorld.ovrAppThread.xmallinfo();

            //    unistd._exit(43);
            //}


            //stdlib_h.free(rotationXx);


            float[] rotationX =
            {
                1,    0,     0, 0,
                0, cosX, -sinX, 0,
                0, sinX, cosX,  0,
                0,    0,     0, 1
            };

            float sinY = math.sinf(radiansY);
            float cosY = math.cosf(radiansY);

            float[] rotationY =
            {
                cosY,  0, sinY, 0,
                0,     1,    0, 0,
                -sinY, 0, cosY, 0,
                0,     0,    0, 1
            };


            float sinZ = math.sinf(radiansZ);
            float cosZ = math.cosf(radiansZ);

            float[] rotationZ =
            {
                cosZ, -sinZ, 0, 0,
                sinZ, cosZ,  0, 0,
                0,        0, 1, 0,
                0,        0, 0, 1
            };



            __Matrix4x4 x = rotationX;
            __Matrix4x4 y = rotationY;
            __Matrix4x4 z = rotationZ;

            //ovrMatrix4f o;

            ovrMatrix4f rotationXY  = __Matrix4x4.Multiply(y, x);
            ovrMatrix4f rotationXYZ = __Matrix4x4.Multiply(z, &rotationXY);

            // copy it back..
            return(rotationXYZ);
        }
 public ovrMatrix4f Multiply(__Matrix4x4 b)
 {
     return(__Matrix4x4.Multiply(this, b));
 }
        public static ovrMatrix4f Transpose(__Matrix4x4 a)
        {
            ovrMatrix4f _o;
            __Matrix4x4 o = &_o;

            o.M[0, 0] = a.M[0, 0]; o.M[0, 1] = a.M[1, 0]; o.M[0, 2] = a.M[2, 0]; o.M[0, 3] = a.M[3, 0];
            o.M[1, 0] = a.M[0, 1]; o.M[1, 1] = a.M[1, 1]; o.M[1, 2] = a.M[2, 1]; o.M[1, 3] = a.M[3, 1];
            o.M[2, 0] = a.M[0, 2]; o.M[2, 1] = a.M[1, 2]; o.M[2, 2] = a.M[2, 2]; o.M[2, 3] = a.M[3, 2];
            o.M[3, 0] = a.M[0, 3]; o.M[3, 1] = a.M[1, 3]; o.M[3, 2] = a.M[2, 3]; o.M[3, 3] = a.M[3, 3];

            return _o;
        }
        public static ovrMatrix4f Multiply(__Matrix4x4 a, __Matrix4x4 b)
        {
            // perhaps the compiler could decide to optimize and move to a byref pointer version?


            // memory dereference magic
            // yet, in glsl we wont have pointers, all we will have is copy struct?
            // http://www.gamedev.net/topic/347232-glsl-pointers/

            // Error	3	A pointer must be indexed by only one value	X:\jsc.svn\examples\java\android\vr\OVRWindWheelNDK\OVRWindWheelNDK\References\VrApi.ovrMatrix4f.cs	93	98	OVRWindWheelNDK
            //Error	5	The type of a local declared in a fixed statement must be a pointer type	X:\jsc.svn\examples\java\android\vr\OVRWindWheelNDK\OVRWindWheelNDK\References\VrApi.ovrMatrix4f.cs	92	20	OVRWindWheelNDK

            ovrMatrix4f _o;
            __Matrix4x4 o = &_o;
            //__ovrMatrix4f a = &_a;
            //__ovrMatrix4f b = &_b;

            {
                o.M[0, 0] = a.M[0, 0] * b.M[0, 0] + a.M[0, 1] * b.M[1, 0] + a.M[0, 2] * b.M[2, 0] + a.M[0, 3] * b.M[3, 0];
                o.M[1, 0] = a.M[1, 0] * b.M[0, 0] + a.M[1, 1] * b.M[1, 0] + a.M[1, 2] * b.M[2, 0] + a.M[1, 3] * b.M[3, 0];
                o.M[2, 0] = a.M[2, 0] * b.M[0, 0] + a.M[2, 1] * b.M[1, 0] + a.M[2, 2] * b.M[2, 0] + a.M[2, 3] * b.M[3, 0];
                o.M[3, 0] = a.M[3, 0] * b.M[0, 0] + a.M[3, 1] * b.M[1, 0] + a.M[3, 2] * b.M[2, 0] + a.M[3, 3] * b.M[3, 0];

                o.M[0, 1] = a.M[0, 0] * b.M[0, 1] + a.M[0, 1] * b.M[1, 1] + a.M[0, 2] * b.M[2, 1] + a.M[0, 3] * b.M[3, 1];
                o.M[1, 1] = a.M[1, 0] * b.M[0, 1] + a.M[1, 1] * b.M[1, 1] + a.M[1, 2] * b.M[2, 1] + a.M[1, 3] * b.M[3, 1];
                o.M[2, 1] = a.M[2, 0] * b.M[0, 1] + a.M[2, 1] * b.M[1, 1] + a.M[2, 2] * b.M[2, 1] + a.M[2, 3] * b.M[3, 1];
                o.M[3, 1] = a.M[3, 0] * b.M[0, 1] + a.M[3, 1] * b.M[1, 1] + a.M[3, 2] * b.M[2, 1] + a.M[3, 3] * b.M[3, 1];

                o.M[0, 2] = a.M[0, 0] * b.M[0, 2] + a.M[0, 1] * b.M[1, 2] + a.M[0, 2] * b.M[2, 2] + a.M[0, 3] * b.M[3, 2];
                o.M[1, 2] = a.M[1, 0] * b.M[0, 2] + a.M[1, 1] * b.M[1, 2] + a.M[1, 2] * b.M[2, 2] + a.M[1, 3] * b.M[3, 2];
                o.M[2, 2] = a.M[2, 0] * b.M[0, 2] + a.M[2, 1] * b.M[1, 2] + a.M[2, 2] * b.M[2, 2] + a.M[2, 3] * b.M[3, 2];
                o.M[3, 2] = a.M[3, 0] * b.M[0, 2] + a.M[3, 1] * b.M[1, 2] + a.M[3, 2] * b.M[2, 2] + a.M[3, 3] * b.M[3, 2];

                o.M[0, 3] = a.M[0, 0] * b.M[0, 3] + a.M[0, 1] * b.M[1, 3] + a.M[0, 2] * b.M[2, 3] + a.M[0, 3] * b.M[3, 3];
                o.M[1, 3] = a.M[1, 0] * b.M[0, 3] + a.M[1, 1] * b.M[1, 3] + a.M[1, 2] * b.M[2, 3] + a.M[1, 3] * b.M[3, 3];
                o.M[2, 3] = a.M[2, 0] * b.M[0, 3] + a.M[2, 1] * b.M[1, 3] + a.M[2, 2] * b.M[2, 3] + a.M[2, 3] * b.M[3, 3];
                o.M[3, 3] = a.M[3, 0] * b.M[0, 3] + a.M[3, 1] * b.M[1, 3] + a.M[3, 2] * b.M[2, 3] + a.M[3, 3] * b.M[3, 3];

                // operating on fixed buffers, yet we want the dimensional array magic.
            }

            return _o;
        }
        public static ovrMatrix4f Multiply(__Matrix4x4 a, __Matrix4x4 b, __Matrix4x4 c)
        {
            var ab = Multiply(a, b);
            //__ovrMatrix4f ab = &_ab;

            var abc = Multiply(&ab, c);

            return abc;

        }
 public ovrMatrix4f Multiply(__Matrix4x4 b)
 {
     return __Matrix4x4.Multiply(this, b);
 }