public static SparseMatrixQuaternion operator *(SparseMatrixQuaternion left, SparseMatrixQuaternion right)
        {
            //Make sure matrix dimensions are equal
            if (left.columnCount != right.rowCount)
            {
                throw new Exception("The dimension of two matrix must be equal");
            }

            SparseMatrixQuaternion result = new SparseMatrixQuaternion(left.rowCount, right.columnCount);

            int leftNNZ = left.mapData.Count;
            int rightNNZ = right.mapData.Count;

            #region Left < Right
            //We use right as stardand sight
            //if (leftNNZ < rightNNZ)
            //{
            //Connection nonezero for each row of matrix a 
            List<KeyValuePair<int, Quaternion>>[] bRows = new List<KeyValuePair<int, Quaternion>>[right.rowCount];
            for (int i = 0; i < bRows.Length; i++)
            {
                bRows[i] = new List<KeyValuePair<int, Quaternion>>();
            }


            foreach (KeyValuePair<Pair, Quaternion> item in right.mapData)
            {
                Pair pair = item.Key;
                Quaternion value = item.Value;

                bRows[pair.Key].Add(new KeyValuePair<int, Quaternion>(pair.Value, value));
            }

            //Compute C = A*B
            foreach (KeyValuePair<Pair, Quaternion> item in left.mapData)
            {
                Pair pair = item.Key;
                int mA = pair.Key;
                int nA = pair.Value;
                Quaternion value = item.Value;

                List<KeyValuePair<int, Quaternion>> bRow = bRows[nA];

                for (int i = 0; i < bRow.Count; i++)
                {
                    int k = bRow[i].Key;

                    Pair pair2 = new Pair(mA, k);

                    if (result.mapData.ContainsKey(pair2))
                    {
                        result.mapData[pair2] += value * bRow[i].Value;
                    }
                    else
                    {
                        result.mapData.Add(pair2, value * bRow[i].Value);
                    }


                }
            }

            //}
            #endregion
            #region Right < Left

            //else if (leftNNZ > rightNNZ)
            //{
            //    //Connection nonezero for each row of matrix a 
            //    List<KeyValuePair<int, double>>[] aCols = new List<KeyValuePair<int, double>>[left.columnCount];
            //    for (int i = 0; i < aCols.Length; i++)
            //    {
            //        aCols[i] = new List<KeyValuePair<int, double>>();
            //    }

            //    foreach (KeyValuePair<Pair, double> item in left.mapData)
            //    {
            //        Pair pair = item.Key;
            //        double value = item.Value;

            //        aCols[pair.Value].Add(new KeyValuePair<int, double>(pair.Key, value));
            //    }

            //    //Compute C = A*B
            //    foreach (KeyValuePair<Pair, double> item in right.mapData)
            //    {
            //        Pair pair = item.Key;
            //        int mA = pair.Key;
            //        int nA = pair.Value;
            //        double value = item.Value;

            //        List<KeyValuePair<int, double>> aCol = aCols[mA];

            //        for (int i = 0; i < aCol.Count; i++)
            //        {
            //            int k = aCol[i].Key;

            //            Pair pair2 = new Pair(k, nA);

            //            if (result.mapData.ContainsKey(pair2))
            //            {
            //                result.mapData[pair2] += value * aCol[i].Value;
            //            }
            //            else
            //            {
            //                result.mapData.Add(pair2, value * aCol[i].Value);
            //            }

            //        }
            //    }

            //}


            #endregion

            return result;
        }
        public Quaternion this[int row, int column]
        {
            set
            {
                if (row > rowCount - 1 || column > columnCount - 1)
                {
                    throw new IndexOutOfRangeException();
                }

                Pair pair = new Pair(row, column);

                if (value.Equals(Quaternion.Zero))
                {
                    if (mapData.ContainsKey(pair))
                    {
                        mapData.Remove(pair);
                    }
                    else
                    {
                        return;
                    }
                }

                mapData[pair] = value;
            }
            get
            {
                if (row > rowCount - 1 || column > columnCount - 1)
                {
                    throw new IndexOutOfRangeException();
                }

                Pair pair = new Pair(row, column);

                if (mapData.ContainsKey(pair))
                {
                    return mapData[pair];
                }
                else
                {
                    return Quaternion.Zero;
                }
            }
        }
        public SparseMatrixQuaternion SubMatrix(int rowStart, int rowEnd, int columnStart, int columnEnd)
        {
            if (rowEnd < rowStart || columnEnd < columnStart)
            {
                throw new ArgumentOutOfRangeException();
            }

            int rows = rowEnd - rowStart + 1;
            int columns = columnEnd - columnStart + 1;

            SparseMatrixQuaternion subMatrix = new SparseMatrixQuaternion(rows, columns);

            int subMatrixEntiries = rows * columns;

            if (subMatrixEntiries < mapData.Count)
            {
                for (int i = 0; i < rows; i++)
                {
                    int rowInx = i + rowStart;
                    for (int j = 0; j < columns; j++)
                    {
                        int columnInx = j + columnStart;
                        Pair pair = new Pair(rowInx, columnInx);
                        if (!mapData.ContainsKey(pair))
                        {
                            continue;
                        }

                        Pair subPair = new Pair(i, j);
                        subMatrix.mapData[subPair] = mapData[pair];

                    }
                }
            }
            else if (subMatrixEntiries >= mapData.Count)
            {
                foreach (KeyValuePair<Pair, Quaternion> item in mapData)
                {
                    Pair pair = item.Key;
                    Quaternion value = item.Value;

                    int m = pair.Key;
                    int n = pair.Value;

                    if ((m >= rowStart && m <= rowEnd) && (n >= columnStart && m <= columnEnd))
                    {
                        int i = m - rowStart;
                        int j = n - columnStart;

                        subMatrix.mapData[new Pair(i, j)] = value;
                    }

                }
            }

            return subMatrix;
        }
        public static SparseMatrixDouble ReadFromFile(String path)
        {
            StreamReader sr = new StreamReader(path);
            String line = null;

            int m = int.MinValue;
            int n = int.MinValue;

            int count = 0;
            SparseMatrixDouble sm = null;

            while ((line = sr.ReadLine()) != null)
            {
                String[] token = line.Split(' ');

                if (count == 0)
                {
                    m = int.Parse(token[1]);
                    n = int.Parse(token[3]);
                    int nnz = int.Parse(token[5]);
                    sm = new SparseMatrixDouble(m, n);

                    count++;
                    continue;
                }

                int index_i = int.Parse(token[0]);
                int index_j = int.Parse(token[1]);
                double value = double.Parse(token[2]);
                if (value != 0)
                {
                    Pair newPair = new Pair(index_i, index_j);
                    sm.Datas.Add(newPair, value);
                }

            }

            sr.Close();

            GC.Collect();
            return sm;
        }