using System;
using System.Text;
using CrossCutting;

namespace InterpreterLibrary.Models.Transform
{
    internal class PerspectiveTransform : ICloneable
    {
        


        

        private const double PerspectiveDivideEpsilon = 1.0e-10;

        
        private double _m00, _m01, _m02, _m10, _m11, _m12, _m20, _m21, _m22;

        
        public PerspectiveTransform()
        {
            _m00 = _m11 = _m22 = 1.0;
            _m01 = _m02 = _m10 = _m12 = _m20 = _m21 = 0.0;
        }


        
        public PerspectiveTransform(double m00, double m01, double m02,
            double m10, double m11, double m12,
            double m20, double m21, double m22)
        {
            _m00 = m00;
            _m01 = m01;
            _m02 = m02;
            _m10 = m10;
            _m11 = m11;
            _m12 = m12;
            _m20 = m20;
            _m21 = m21;
            _m22 = m22;
        }

        
        public PerspectiveTransform(float[] flatmatrix)
        {
            if (flatmatrix == null)
                throw new ArgumentException($"{nameof(flatmatrix)} cannot be null");

            _m00 = flatmatrix[0];
            _m01 = flatmatrix[1];
            _m02 = flatmatrix[2];
            _m10 = flatmatrix[3];
            _m11 = flatmatrix[4];
            _m12 = flatmatrix[5];
            _m20 = flatmatrix[6];
            _m21 = flatmatrix[7];
            _m22 = flatmatrix[8];
        }

        
        public PerspectiveTransform(float[][] matrix)
        {
            if (matrix == null)
                throw new ArgumentException($"{nameof(matrix)} cannot be null");

            _m00 = matrix[0][0];
            _m01 = matrix[0][1];
            _m02 = matrix[0][2];
            _m10 = matrix[1][0];
            _m11 = matrix[1][1];
            _m12 = matrix[1][2];
            _m20 = matrix[2][0];
            _m21 = matrix[2][1];
            _m22 = matrix[2][2];
        }

        
        public PerspectiveTransform(double[] flatmatrix)
        {
            if (flatmatrix == null)
                throw new ArgumentException($"{nameof(flatmatrix)} cannot be null");

            _m00 = flatmatrix[0];
            _m01 = flatmatrix[1];
            _m02 = flatmatrix[2];
            _m10 = flatmatrix[3];
            _m11 = flatmatrix[4];
            _m12 = flatmatrix[5];
            _m20 = flatmatrix[6];
            _m21 = flatmatrix[7];
            _m22 = flatmatrix[8];
        }

        
        public PerspectiveTransform(double[][] matrix)
        {
            if (matrix == null)
                throw new ArgumentException($"{nameof(matrix)} cannot be null");

            _m00 = matrix[0][0];
            _m01 = matrix[0][1];
            _m02 = matrix[0][2];
            _m10 = matrix[1][0];
            _m11 = matrix[1][1];
            _m12 = matrix[1][2];
            _m20 = matrix[2][0];
            _m21 = matrix[2][1];
            _m22 = matrix[2][2];
        }

        
        public object Clone()
        {
            return MemberwiseClone();
        }


        
        private void MakeAdjoint()
        {
            var m00p = _m11 * _m22 - _m12 * _m21;
            var m01p = _m12 * _m20 - _m10 * _m22; 
            var m02p = _m10 * _m21 - _m11 * _m20;
            var m10p = _m02 * _m21 - _m01 * _m22; 
            var m11p = _m00 * _m22 - _m02 * _m20;
            var m12p = _m01 * _m20 - _m00 * _m21; 
            var m20p = _m01 * _m12 - _m02 * _m11;
            var m21p = _m02 * _m10 - _m00 * _m12; 
            var m22p = _m00 * _m11 - _m01 * _m10;

            
            _m00 = m00p;
            _m01 = m10p;
            _m02 = m20p;
            _m10 = m01p;
            _m11 = m11p;
            _m12 = m21p;
            _m20 = m02p;
            _m21 = m12p;
            _m22 = m22p;
        }

        
        private void Normalize()
        {
            var invscale = 1.0 / _m22;
            _m00 *= invscale;
            _m01 *= invscale;
            _m02 *= invscale;
            _m10 *= invscale;
            _m11 *= invscale;
            _m12 *= invscale;
            _m20 *= invscale;
            _m21 *= invscale;
            _m22 = 1.0;
        }

        private static void getSquareToQuad(double x0, double y0,
            double x1, double y1,
            double x2, double y2,
            double x3, double y3,
            PerspectiveTransform tx)
        {
            var dx3 = x0 - x1 + x2 - x3;
            var dy3 = y0 - y1 + y2 - y3;

            tx._m22 = 1.0F;

            if (dx3 == 0.0F && dy3 == 0.0F)
            {
                
                tx._m00 = x1 - x0;
                tx._m01 = x2 - x1;
                tx._m02 = x0;
                tx._m10 = y1 - y0;
                tx._m11 = y2 - y1;
                tx._m12 = y0;
                tx._m20 = 0.0F;
                tx._m21 = 0.0F;
            }
            else
            {
                var dx1 = x1 - x2;
                var dy1 = y1 - y2;
                var dx2 = x3 - x2;
                var dy2 = y3 - y2;

                var invdet = 1.0F / (dx1 * dy2 - dx2 * dy1);
                tx._m20 = (dx3 * dy2 - dx2 * dy3) * invdet;
                tx._m21 = (dx1 * dy3 - dx3 * dy1) * invdet;
                tx._m00 = x1 - x0 + tx._m20 * x1;
                tx._m01 = x3 - x0 + tx._m21 * x3;
                tx._m02 = x0;
                tx._m10 = y1 - y0 + tx._m20 * y1;
                tx._m11 = y3 - y0 + tx._m21 * y3;
                tx._m12 = y0;
            }
        }

        
        public static PerspectiveTransform GetSquareToQuad(double x0, double y0,
            double x1, double y1,
            double x2, double y2,
            double x3, double y3)
        {
            var tx = new PerspectiveTransform();
            getSquareToQuad(x0, y0, x1, y1, x2, y2, x3, y3, tx);
            return tx;
        }


        
        public static PerspectiveTransform GetSquareToQuad(float x0, float y0,
            float x1, float y1,
            float x2, float y2,
            float x3, float y3)
        {
            return GetSquareToQuad(x0, y0,
                x1, y1,
                x2, y2,
                x3, (double)y3);
        }


        
        public static PerspectiveTransform GetQuadToSquare(double x0, double y0,
            double x1, double y1,
            double x2, double y2,
            double x3, double y3)
        {
            var tx = new PerspectiveTransform();
            getSquareToQuad(x0, y0, x1, y1, x2, y2, x3, y3, tx);
            tx.MakeAdjoint();
            return tx;
        }

        
        public static PerspectiveTransform GetQuadToSquare(float x0, float y0,
            float x1, float y1,
            float x2, float y2,
            float x3, float y3)
        {
            return GetQuadToSquare(x0, y0,
                x1, y1,
                x2, y2,
                x3, (double)y3);
        }

        
        public static PerspectiveTransform GetQuadToQuad(double x0, double y0,
            double x1, double y1,
            double x2, double y2,
            double x3, double y3,
            double x0p, double y0p,
            double x1p, double y1p,
            double x2p, double y2p,
            double x3p, double y3p)
        {
            var tx1 =
                GetQuadToSquare(x0, y0, x1, y1, x2, y2, x3, y3);

            var tx2 =
                GetSquareToQuad(x0p, y0p, x1p, y1p, x2p, y2p, x3p, y3p);

            tx1.Concatenate(tx2);
            return tx1;
        }


        
        public static PerspectiveTransform GetQuadToQuad(float x0, float y0,
            float x1, float y1,
            float x2, float y2,
            float x3, float y3,
            float x0p, float y0p,
            float x1p, float y1p,
            float x2p, float y2p,
            float x3p, float y3p)
        {
            return GetQuadToQuad(x0, y0,
                x1, y1,
                x2, y2,
                x3, y3,
                x0p, y0p,
                x1p, y1p,
                x2p, y2p,
                x3p, (double)y3p);
        }

        
        public double GetDeterminant()
        {
            return _m00 * (_m11 * _m22 - _m12 * _m21) -
                   _m01 * (_m10 * _m22 - _m12 * _m20) +
                   _m02 * (_m10 * _m21 - _m11 * _m20);
        }

        
        public double[] GetMatrix(double[] flatmatrix)
        {
            if (flatmatrix == null)
                flatmatrix = new double[9];

            flatmatrix[0] = _m00;
            flatmatrix[1] = _m01;
            flatmatrix[2] = _m02;
            flatmatrix[3] = _m10;
            flatmatrix[4] = _m11;
            flatmatrix[5] = _m12;
            flatmatrix[6] = _m20;
            flatmatrix[7] = _m21;
            flatmatrix[8] = _m22;

            return flatmatrix;
        }

        
        public double[,] GetMatrix(double[,] matrix)
        {
            if (matrix == null)
                matrix = new double[3, 3];

            matrix[0, 0] = _m00;
            matrix[0, 1] = _m01;
            matrix[0, 2] = _m02;
            matrix[1, 0] = _m10;
            matrix[1, 1] = _m11;
            matrix[1, 2] = _m12;
            matrix[2, 0] = _m20;
            matrix[2, 1] = _m21;
            matrix[2, 2] = _m22;

            return matrix;
        }

        
        public void Translate(double tx, double ty)
        {
            var Tx = new PerspectiveTransform();
            Tx.SetToTranslation(tx, ty);
            Concatenate(Tx);
        }

        
        public void rotate(double theta)
        {
            var Tx = new PerspectiveTransform();
            Tx.SetToRotation(theta);
            Concatenate(Tx);
        }

        
        public void rotate(double theta, double x, double y)
        {
            var Tx = new PerspectiveTransform();
            Tx.SetToRotation(theta, x, y);
            Concatenate(Tx);
        }

        
        public void Scale(double sx, double sy)
        {
            var Tx = new PerspectiveTransform();
            Tx.SetToScale(sx, sy);
            Concatenate(Tx);
        }

        
        public void Shear(double shx, double shy)
        {
            var Tx = new PerspectiveTransform();
            Tx.SetToShear(shx, shy);
            Concatenate(Tx);
        }

        
        public void SetToIdentity()
        {
            _m00 = _m11 = _m22 = 1.0;
            _m01 = _m10 = _m02 = _m20 = _m12 = _m21 = 0.0;
        }

        
        public void SetToTranslation(double tx, double ty)
        {
            _m00 = 1.0;
            _m01 = 0.0;
            _m02 = tx;
            _m10 = 0.0;
            _m11 = 1.0;
            _m12 = ty;
            _m20 = 0.0;
            _m21 = 0.0;
            _m22 = 1.0;
        }

        
        public void SetToRotation(double theta)
        {
            _m00 = Math.Cos(theta);
            _m01 = -Math.Sin(theta);
            _m02 = 0.0;
            _m10 = -_m01;
            _m11 = _m00;
            _m12 = 0.0;
            _m20 = 0.0;
            _m21 = 0.0;
            _m22 = 1.0;
        }

        
        public void SetToRotation(double theta, double x, double y)
        {
            SetToRotation(theta);
            var sin = _m10;
            var oneMinusCos = 1.0 - _m00;
            _m02 = x * oneMinusCos + y * sin;
            _m12 = y * oneMinusCos - x * sin;
        }

        
        public void SetToScale(double sx, double sy)
        {
            _m00 = sx;
            _m01 = 0.0;
            _m02 = 0.0;
            _m10 = 0.0;
            _m11 = sy;
            _m12 = 0.0;
            _m20 = 0.0;
            _m21 = 0.0;
            _m22 = 1.0;
        }

        
        public void SetToShear(double shx, double shy)
        {
            _m00 = 1.0;
            _m01 = shx;
            _m02 = 0.0;
            _m10 = shy;
            _m11 = 1.0;
            _m12 = 0.0;
            _m20 = 0.0;
            _m21 = 0.0;
            _m22 = 1.0;
        }

        
        public void SetTransform(PerspectiveTransform Tx)
        {
            if (Tx == null)
                throw new ArgumentException($"{nameof(Tx)} cannot be null");

            _m00 = Tx._m00;
            _m01 = Tx._m01;
            _m02 = Tx._m02;
            _m10 = Tx._m10;
            _m11 = Tx._m11;
            _m12 = Tx._m12;
            _m20 = Tx._m20;
            _m21 = Tx._m21;
            _m22 = Tx._m22;
        }

        
        public void SetTransform(float m00, float m10, float m20,
            float m01, float m11, float m21,
            float m02, float m12, float m22)
        {
            _m00 = m00;
            _m01 = m01;
            _m02 = m02;
            _m10 = m10;
            _m11 = m11;
            _m12 = m12;
            _m20 = m20;
            _m21 = m21;
            _m22 = m22;
        }

        
        public void SetTransform(double[][] matrix)
        {
            if (matrix == null)
                throw new ArgumentException($"{nameof(matrix)} cannot be null");

            _m00 = matrix[0][0];
            _m01 = matrix[0][1];
            _m02 = matrix[0][2];
            _m10 = matrix[1][0];
            _m11 = matrix[1][1];
            _m12 = matrix[1][2];
            _m20 = matrix[2][0];
            _m21 = matrix[2][1];
            _m22 = matrix[2][2];
        }

        
        public void Concatenate(PerspectiveTransform Tx)
        {
            if (Tx == null)
                throw new ArgumentException($"{nameof(Tx)} cannot be null");

            var m00p = _m00 * Tx._m00 + _m10 * Tx._m01 + _m20 * Tx._m02;
            var m10p = _m00 * Tx._m10 + _m10 * Tx._m11 + _m20 * Tx._m12;
            var m20p = _m00 * Tx._m20 + _m10 * Tx._m21 + _m20 * Tx._m22;
            var m01p = _m01 * Tx._m00 + _m11 * Tx._m01 + _m21 * Tx._m02;
            var m11p = _m01 * Tx._m10 + _m11 * Tx._m11 + _m21 * Tx._m12;
            var m21p = _m01 * Tx._m20 + _m11 * Tx._m21 + _m21 * Tx._m22;
            var m02p = _m02 * Tx._m00 + _m12 * Tx._m01 + _m22 * Tx._m02;
            var m12p = _m02 * Tx._m10 + _m12 * Tx._m11 + _m22 * Tx._m12;
            var m22p = _m02 * Tx._m20 + _m12 * Tx._m21 + _m22 * Tx._m22;

            _m00 = m00p;
            _m10 = m10p;
            _m20 = m20p;
            _m01 = m01p;
            _m11 = m11p;
            _m21 = m21p;
            _m02 = m02p;
            _m12 = m12p;
            _m22 = m22p;
        }


        
        public void PreConcatenate(PerspectiveTransform Tx)
        {
            if (Tx == null)
                throw new ArgumentException($"{nameof(Tx)} cannot be null");

            var m00p = Tx._m00 * _m00 + Tx._m10 * _m01 + Tx._m20 * _m02;
            var m10p = Tx._m00 * _m10 + Tx._m10 * _m11 + Tx._m20 * _m12;
            var m20p = Tx._m00 * _m20 + Tx._m10 * _m21 + Tx._m20 * _m22;
            var m01p = Tx._m01 * _m00 + Tx._m11 * _m01 + Tx._m21 * _m02;
            var m11p = Tx._m01 * _m10 + Tx._m11 * _m11 + Tx._m21 * _m12;
            var m21p = Tx._m01 * _m20 + Tx._m11 * _m21 + Tx._m21 * _m22;
            var m02p = Tx._m02 * _m00 + Tx._m12 * _m01 + Tx._m22 * _m02;
            var m12p = Tx._m02 * _m10 + Tx._m12 * _m11 + Tx._m22 * _m12;
            var m22p = Tx._m02 * _m20 + Tx._m12 * _m21 + Tx._m22 * _m22;

            _m00 = m00p;
            _m10 = m10p;
            _m20 = m20p;
            _m01 = m01p;
            _m11 = m11p;
            _m21 = m21p;
            _m02 = m02p;
            _m12 = m12p;
            _m22 = m22p;
        }

        
        public PerspectiveTransform CreateInverse()
        {
            var tx = (PerspectiveTransform)Clone();
            tx.MakeAdjoint();
            if (Math.Abs(tx._m22) < PerspectiveDivideEpsilon)
                throw
                    new Exception(
                        "Can't invert transformation");
            tx.Normalize();
            return tx;
        }

        
        public PerspectiveTransform CreateAdjoint()
        {
            var tx = (PerspectiveTransform)Clone();
            tx.MakeAdjoint();
            return tx;
        }

        
        public PointF Transform(PointF ptSrc, ref PointF ptDst)
        {
            if (ptSrc == null)
                throw new ArgumentException($"{nameof(ptSrc)} cannot be null");

            if (ptDst == null)
                ptDst = new PointF();

            double x = ptSrc.X;
            double y = ptSrc.Y;
            var w = _m20 * x + _m21 * y + _m22;

            ptDst = new PointF(
                (float) ((_m00 * x + _m01 * y + _m02) / w),
                (float) ((_m10 * x + _m11 * y + _m12) / w)
            );
            return ptDst;
        }

        
        public void Transform(PointF[] ptSrc, int srcOff,
            PointF[] ptDst, int dstOff,
            int numPts)
        {
            if (ptSrc == null || ptDst == null)
                throw new ArgumentException();

            while (numPts-- > 0)
            {
                
                var src = ptSrc[srcOff++];

                double x = src.X;
                double y = src.Y;
                var w = _m20 * x + _m21 * y + _m22;

                if (w == 0)
                {
                    ptDst[dstOff].X = (float)x;
                    ptDst[dstOff].Y = (float)y;
                }
                else
                {
                    ptDst[dstOff].X = (float)((_m00 * x + _m01 * y + _m02) / w);
                    ptDst[dstOff].Y = (float)((_m10 * x + _m11 * y + _m12) / w);
                }
                ++dstOff;
            }
        }

        
        public void Transform(float[] srcPts, int srcOff,
            float[] dstPts, int dstOff,
            int numPts)
        {
            if (srcPts == null)
                throw new ArgumentException($"{nameof(srcPts)} cannot be null");

            if (dstPts == null)
                dstPts = new float[numPts * 2 + dstOff];

            while (numPts-- > 0)
            {
                var x = srcPts[srcOff++];
                var y = srcPts[srcOff++];
                var w = _m20 * x + _m21 * y + _m22;

                if (w == 0)
                {
                    dstPts[dstOff++] = x;
                    dstPts[dstOff++] = y;
                }
                else
                {
                    dstPts[dstOff++] = (float)((_m00 * x + _m01 * y + _m02) / w);
                    dstPts[dstOff++] = (float)((_m10 * x + _m11 * y + _m12) / w);
                }
            }
        }

        
        public void Transform(double[] srcPts, int srcOff,
            double[] dstPts, int dstOff,
            int numPts)
        {
            if (srcPts == null)
                throw new ArgumentException($"{nameof(srcPts)} cannot be null");

            if (dstPts == null)
                dstPts = new double[numPts * 2 + dstOff];

            while (numPts-- > 0)
            {
                var x = srcPts[srcOff++];
                var y = srcPts[srcOff++];
                var w = _m20 * x + _m21 * y + _m22;

                if (w == 0)
                {
                    dstPts[dstOff++] = x;
                    dstPts[dstOff++] = y;
                }
                else
                {
                    dstPts[dstOff++] = (_m00 * x + _m01 * y + _m02) / w;
                    dstPts[dstOff++] = (_m10 * x + _m11 * y + _m12) / w;
                }
            }
        }

        
        public void Transform(float[] srcPts, int srcOff,
            double[] dstPts, int dstOff,
            int numPts)
        {
            if (srcPts == null)
                throw new ArgumentException($"{nameof(srcPts)} cannot be null");

            if (dstPts == null)
                dstPts = new double[numPts * 2 + dstOff];

            while (numPts-- > 0)
            {
                var x = srcPts[srcOff++];
                var y = srcPts[srcOff++];
                var w = _m20 * x + _m21 * y + _m22;

                if (w == 0)
                {
                    dstPts[dstOff++] = x;
                    dstPts[dstOff++] = y;
                }
                else
                {
                    dstPts[dstOff++] = (_m00 * x + _m01 * y + _m02) / w;
                    dstPts[dstOff++] = (_m10 * x + _m11 * y + _m12) / w;
                }
            }
        }

        
        public void Transform(double[] srcPts, int srcOff,
            float[] dstPts, int dstOff,
            int numPts)
        {
            if (srcPts == null)
                throw new ArgumentException($"{nameof(srcPts)} cannot be null");

            if (dstPts == null)
                dstPts = new float[numPts * 2 + dstOff];

            while (numPts-- > 0)
            {
                var x = srcPts[srcOff++];
                var y = srcPts[srcOff++];
                var w = _m20 * x + _m21 * y + _m22;

                if (w == 0)
                {
                    dstPts[dstOff++] = (float)x;
                    dstPts[dstOff++] = (float)y;
                }
                else
                {
                    dstPts[dstOff++] = (float)((_m00 * x + _m01 * y + _m02) / w);
                    dstPts[dstOff++] = (float)((_m10 * x + _m11 * y + _m12) / w);
                }
            }
        }

        
        public PointF InverseTransform(PointF ptSrc)

        {
            if (ptSrc == null)
                throw new ArgumentException($"{nameof(ptSrc)} cannot be null");

            
            double x = ptSrc.X;
            double y = ptSrc.Y;

            var tmp_x = (_m11 * _m22 - _m12 * _m21) * x +
                        (_m02 * _m21 - _m01 * _m22) * y +
                        (_m01 * _m12 - _m02 * _m11);
            var tmp_y = (_m12 * _m20 - _m10 * _m22) * x +
                        (_m00 * _m22 - _m02 * _m20) * y +
                        (_m02 * _m10 - _m00 * _m12);
            var w = (_m10 * _m21 - _m11 * _m20) * x +
                    (_m01 * _m20 - _m00 * _m21) * y +
                    (_m00 * _m11 - _m01 * _m10);

            var wabs = w;
            if (w < 0)
                wabs = -w;
            if (wabs < PerspectiveDivideEpsilon)
                throw new Exception("Transformation cannot be inverted");

            return new PointF(
                (float) (tmp_x / w),
                (float) (tmp_y / w));
        }

        
        public void InverseTransform(double[] srcPts, int srcOff,
            double[] dstPts, int dstOff,
            int numPts)
        {
            if (srcPts == null)
                throw new ArgumentException($"{nameof(srcPts)} cannot be null");

            if (dstPts == null)
                dstPts = new double[numPts * 2 + dstOff];

            while (numPts-- > 0)
            {
                var x = srcPts[srcOff++];
                var y = srcPts[srcOff++];

                var tmp_x = (_m11 * _m22 - _m12 * _m21) * x +
                            (_m02 * _m21 - _m01 * _m22) * y +
                            (_m01 * _m12 - _m02 * _m11);
                var tmp_y = (_m12 * _m20 - _m10 * _m22) * x +
                            (_m00 * _m22 - _m02 * _m20) * y +
                            (_m02 * _m10 - _m00 * _m12);
                var w = (_m10 * _m21 - _m11 * _m20) * x +
                        (_m01 * _m20 - _m00 * _m21) * y +
                        (_m00 * _m11 - _m01 * _m10);

                var wabs = w;
                if (w < 0)
                    wabs = -w;
                if (wabs < PerspectiveDivideEpsilon)
                    throw new Exception("Transformation cannot be inverted");

                dstPts[dstOff++] = tmp_x / w;
                dstPts[dstOff++] = tmp_y / w;
            }
        }

        
        public override string ToString()
        {
            var sb = new StringBuilder();
            sb.Append("Perspective transform matrix\n");
            sb.Append(_m00);
            sb.Append("\t");
            sb.Append(_m01);
            sb.Append("\t");
            sb.Append(_m02);
            sb.Append("\n");
            sb.Append(_m10);
            sb.Append("\t");
            sb.Append(_m11);
            sb.Append("\t");
            sb.Append(_m12);
            sb.Append("\n");
            sb.Append(_m20);
            sb.Append("\t");
            sb.Append(_m21);
            sb.Append("\t");
            sb.Append(_m22);
            sb.Append("\n");
            return sb.ToString();
        }

        
        public bool IsIdentity => _m01 == 0.0 && _m02 == 0.0 &&
                   _m10 == 0.0 && _m12 == 0.0 &&
                   _m20 == 0.0 && _m21 == 0.0 &&
                   _m22 != 0.0 && _m00 / _m22 == 1.0 && _m11 / _m22 == 1.0;



        
        public override bool Equals(object obj)
        {
            if (!(obj is PerspectiveTransform))
                return false;

            var a = (PerspectiveTransform)obj;

            return _m00 == a._m00 && _m10 == a._m10 && _m20 == a._m20 &&
                   _m01 == a._m01 && _m11 == a._m11 && _m21 == a._m21 &&
                   _m02 == a._m02 && _m12 == a._m12 && _m22 == a._m22;
        }

        public override int GetHashCode()
        {
            return Convert.ToInt32(_m10 + _m00 + _m01 + _m02 + _m11 + _m11 + _m12 + _m20 + _m21 + _m22);
        }
    }
}
