//public abstract SparseColumnStorage<T> Permute(int[] perm, int[] qperm = null, bool symbolic = false);

        /// <summary>
        /// Permutes a sparse matrix, C = PAQ.
        /// </summary>
        /// <param name="pinv">Permutation vector of length m.</param>
        /// <param name="q">Permutation vector of length n.</param>
        /// <param name="result">Permuted matrix, C = PAQ.</param>
        public virtual void Permute(int[] pinv, int[] q, SymbolicColumnStorage result)
        {
            int i, j, k, nz = 0;

            int n = this.columnCount;

            int[] ap = this.ColumnPointers;
            int[] ai = this.RowIndices;

            // Allocate memory if needed.
            if (result.ColumnPointers == null)
            {
                result.ColumnPointers = new int[ap.Length];
                result.RowIndices     = new int[ai.Length];
            }

            int[] cp = result.ColumnPointers;
            int[] ci = result.RowIndices;

            for (k = 0; k < n; k++)
            {
                cp[k] = nz; // column k of C is column q[k] of A
                j     = q != null ? (q[k]) : k;
                for (i = ap[j]; i < ap[j + 1]; i++)
                {
                    ci[nz++] = pinv != null ? (pinv[ai[i]]) : ai[i];
                }
            }

            // Finalize the last column of result matrix.
            cp[n] = nz;
        }
        public SymbolicColumnStorage Clone()
        {
            int m   = this.RowCount;
            int n   = this.ColumnCount;
            int nnz = this.NonZerosCount;

            var result = new SymbolicColumnStorage(m, n, nnz, true);

            Buffer.BlockCopy(this.ColumnPointers, 0, result.ColumnPointers, 0, (n + 1) * Constants.SizeOfInt);
            Buffer.BlockCopy(this.RowIndices, 0, result.RowIndices, 0, nnz * Constants.SizeOfInt);

            return(result);
        }
        /// <summary>
        /// Sparse matrix multiplication, C = A*B
        /// </summary>
        /// <param name="other">column-compressed matrix</param>
        /// <returns>C = A*B</returns>
        public SymbolicColumnStorage Multiply(SymbolicColumnStorage other)
        {
            int p, j, nz = 0;

            if (this.columnCount != other.rowCount)
            {
                throw new ArgumentException();
            }

            int m = this.rowCount;
            int n = other.columnCount;

            int anz = this.NonZerosCount;
            int bnz = other.NonZerosCount;

            var bp = other.ColumnPointers;
            var bi = other.RowIndices;

            var result = new SymbolicColumnStorage(m, n, 0, false);

            var cp = new int[n + 1];
            var ci = new int[2 * Math.Max(anz, bnz)];

            int[] work = new int[m];

            for (j = 0; j < n; j++)
            {
                if (nz + m > ci.Length)
                {
                    Array.Resize(ref ci, 2 * ci.Length + m);
                }

                // Column j of C starts here
                cp[j] = nz;

                for (p = bp[j]; p < bp[j + 1]; p++)
                {
                    nz = this.Scatter(bi[p], work, j + 1, ci, nz);
                }
            }

            cp[n] = nz;

            // Remove extra space from C
            Array.Resize(ref ci, nz);

            result.ColumnPointers = cp;
            result.RowIndices     = ci;

            return(result);
        }
        /// <summary>
        /// Symbolic sum C = A + B
        /// </summary>
        /// <param name="other">column-compressed matrix</param>
        /// <returns>Sum C = A + B</returns>
        public SymbolicColumnStorage Add(SymbolicColumnStorage other)
        {
            int j, nz = 0;

            // check inputs
            if (rowCount != other.rowCount || columnCount != other.columnCount)
            {
                throw new ArgumentException();
            }

            int m = rowCount;
            int n = other.columnCount;

            var bi = other.ColumnPointers;

            int anz = ColumnPointers[columnCount];
            int bnz = bi[n];

            // Workspace
            var w = new int[m];

            // Allocate result: (anz + bnz) is an upper bound
            var cp = new int[bi.Length];
            var ci = new int[anz + bnz];

            for (j = 0; j < n; j++)
            {
                // Column j of result starts here
                cp[j] = nz;
                nz    = this.Scatter(j, w, j + 1, ci, nz);  // A(:,j)
                nz    = other.Scatter(j, w, j + 1, ci, nz); // B(:,j)
            }

            // Finalize the last column
            cp[n] = nz;

            // Remove extra space
            Array.Resize(ref ci, nz);

            var result = new SymbolicColumnStorage(m, n, 0, false);

            result.ColumnPointers = cp;
            result.RowIndices     = ci;

            return(result);
        }
        internal static SymbolicColumnStorage Create <T>(CompressedColumnStorage <T> mat, bool allocate = true)
            where T : struct, IEquatable <T>, IFormattable
        {
            int m   = mat.RowCount;
            int n   = mat.ColumnCount;
            int nnz = mat.NonZerosCount;

            var result = new SymbolicColumnStorage(m, n, nnz, allocate);

            if (allocate)
            {
                Buffer.BlockCopy(mat.ColumnPointers, 0, result.ColumnPointers, 0, (n + 1) * Constants.SizeOfInt);
                Buffer.BlockCopy(mat.RowIndices, 0, result.RowIndices, 0, nnz * Constants.SizeOfInt);
            }
            else
            {
                result.ColumnPointers = mat.ColumnPointers;
                result.RowIndices     = mat.RowIndices;
            }

            return(result);
        }
        /// <summary>
        /// Computes the transpose of a sparse matrix, C = A';
        /// </summary>
        /// <returns>Transposed matrix, C = A'</returns>
        public virtual SymbolicColumnStorage Transpose()
        {
            int j, k, p;

            int m = this.RowCount;
            int n = this.ColumnCount;

            var result = new SymbolicColumnStorage(n, m, 0, false);

            var ci = new int[m + 1];
            var cj = new int[RowIndices.Length];

            int[] w = new int[rowCount];

            for (p = 0; p < ColumnPointers[columnCount]; p++)
            {
                // Row counts.
                w[RowIndices[p]]++;
            }

            // Column pointers.
            Helper.CumulativeSum(ci, w, rowCount);

            for (j = 0; j < columnCount; j++)
            {
                for (p = ColumnPointers[j]; p < ColumnPointers[j + 1]; p++)
                {
                    k = w[RowIndices[p]]++;

                    // Place A(i,j) as entry C(j,i)
                    cj[k] = j;
                }
            }

            result.ColumnPointers = ci;
            result.RowIndices     = cj;

            return(result);
        }