- Use extract_array() in as.array() methods for SVT_SparseArray and
  NaArray objects. See comments above the .from_SVT_SparseArray_to_array()
  and .from_NaArray_to_array() functions in R/SVT_SparseArray-class.R and
  R/NaArray-class.R

- Make operations from the Math group work on SVT_SparseArray objects of
  type "integer".

- Compare and Logic operations between a SparseArray (or NaArray) object 'x'
  and an atomic vector 'y' are only supported if the latter is a single value
  at the moment. Relax this i.e. we also want to support atomic vectors that
  have a length equal to (or a divisor of) 'dim(x)[[1]]'.
  Note that this was relaxed for Arith operations in SparseArray 1.7.5.

- Implement Arith operations between an SVT_SparseArray (or NaArray) object
  and a tile object (see ?as_tile in S4Arrays >= 1.7.3). Only support "simple
  tiles" for now, that is, tiles where all the dimensions are ineffective
  except one. Note that:
    1. A "simple tile" is typically obtained from an ordinary vector with
       something like 'as_tile(<vector>, along)'. The primary use case for
       this kind of tile is to be able to replace 't(t(x) / colSums(x))'
       with 'x / as_tile(colSums(x), along=2)'.
    2. The 'recycle.along' argument was recently added to internal
       helper .Arith_SVT1_v2() to support exactly this use case
       in the most efficient way. More precisely,
       .Arith_SVT1_v2(..., recycle.along=recycle.along) is intended
       to be the workhorse behind the Arith methods for SVT_SparseArray#tile
       and tile#SVT_SparseArray.
    3. Similar work needs to be done for NaArray objects i.e.
       the 'recycle.along' argument needs to be added to .Arith_NaSVT1_v2()
       and .Arith_v1_NaSVT2(), and Arith methods for NaArray#tile and
       tile#NaArray need to be defined.
  All this will require extensive unit tests and documentation with
  good examples (the examples should emphasize the benefits of doing
  'x / as_tile(colSums(x), along=2)' over 't(t(x) / colSums(x))').

- Implement Compare and Logic operations between an SVT_SparseArray (or
  NaArray) object and a tile object. See previous item for the details.

- Some Ops operations (Arith/Compare/Logic) between an SVT_SparseArray 'svt'
  object and an ordinary vector or tile 'v' are currently not allowed because
  they don't preserve sparsity **in general**. For example:
    o 'svt - v' is rejected whatever the content of 'v' is;
    o 'svt / v' is rejected if 'v' contains zeros or NAs.
  The idea behind this appraoch is that if the user really wants to perform
  these operations, and if they know what they're doing, then they first need
  to **explicitly** turn 'svt' into an ordinary array (or some other dense
  representation), but we're not going to do this for them because:
    1. we want these operations to always behave as endomorphisms (i.e.
       preserve the class;
    2. the consequences of blindly doing something like 'as.array(svt) - v' can
       be catastrophic so we prefer to leave that responsibility to the user.
  However, it still makes sense to allow these operations if the percentage
  of sparsity-breaking values in 'v' is small. For example:
    o 'svt - v' still makes sense if 'v' contains only a few nonzero values;
    o 'svt / v' still makes sense if 'v' contains only a few zeros or NAs.
  In both cases, we can easily implement these operations as endomorphisms
  without increasing too much the density of the input object. See for example
  these implementations:
  https://github.com/YosefLab/scone/blob/120e114d293d0695e2c18b3e80459cf2251ba516/R/pareto_norm.R#L88-L131
  So maybe do something like this in SparseArray: revisit all cases
  where 'Ops(svt, v)' and 'Ops(v, svt)' are currently not allowed and
  relax the criteria for allowing them or not. The new criteria could be
  that the percentage of sparsity-breaking values in 'v' is below a certain
  threshold e.g. 10% or 15% (should this be a global option?). And if the
  criteria is satisfied then perform the operation.

- NaArray objects:

  1. Implement methods rowAnyNAs(), rowMins, rowMaxs(), rowMeans(), rowVars(),
     and rowSds(). Add unit tests.

  2. Add vignette for NaArray objects.

- Support single cell normalization & PCA on an SVT_SparseMatrix object
  as described in the "HDF5Array performance" vignette in HDF5Array.
  This means that we need to be able to call simple_normalize() and
  simple_PCA() on an SVT_SparseMatrix object. Right now, simple_normalize()
  fails at the 't(mat) * 10000 / colSums(mat)' step because arithmetic
  operations are not supported between a SparseArray object and a vector
  of length != 1. It's also failing later when calling log1p() because
  the log1p() method for SVT_SparseArray objects only supports input of
  type "double" at the moment.

- Once the previous item is addressed, add timings for svt1, svt2, svt3,
  svt4, svt5, svt1n, svt2n, svt3n, svt4n, and svt5n to the "HDF5Array
  performance" vignette. Of course we expect them to beat the timings
  obtained for on-disk representations TENxMatrix and HDF5Matrix but at
  the cost of drastically increased memory usage.

- Speed up more row summarization methods for SparseArray and NaArray objects
  by supporting natively more operations in .Call entry point C_rowStats_SVT.
  Note that doing this for rowAnyNAs(), rowMins(), rowMaxs(), rowSums(),
  rowMeans(), rowVars(), and rowSds(), led to speedups of 35x, 6x, 6x, 20x,
  18x, 5x, and 5x, respectively, on big SVT_SparseArray objects, or more.
  It also reduced memory usage significantly.

- Implement which() methods for SparseArray and NaArray objects of type
  logical.

- Do we need to implement a fast nzvals() setter for SVT_SparseMatrix objects
  and a fast nnavals() setter for NaArray objects? Default methods work fine
  and are fast but could specialized methods be faster?

- Add unit tests for nzwhich() and nzvals() methods for COO_SparseArray
  and SVT_SparseArray objects, and for nnawhich() and nnavals() methods
  for NaArray objects. More generally we need unit tests for all the
  is_nonzero(), nz*(), is_nonna(), and nna*() methods.

- Implement readMatrixMarket(), similar to Matrix::readMM() but should
  return a COO_SparseMatrix object instead of a dgTMatrix object. Note that
  the easy/lazy implementation could simply be

      as(Matrix::readMM(.), COO_SparseMatrix)

  However it wouldn't be really justified to introduce a new function just
  for that. So hopefully a native implementation will improve efficiency
  enough to be worth it and justify a dedicated function.
  See https://math.nist.gov/MatrixMarket/formats.html

- To write a SparseMatrix object to a Matrix Market file, do we need a
  dedicated writeMatrixMarket() function or should we just define a
  Matrix::writeMM() method for SparseMatrix objects? That method could
  simply do

      Matrix::writeMM(as(x, "CsparseMatrix"))

- Support names() getter and setter on a 1D SparseArray or NaArray object
  as a shortcut for 'dimnames()[[1L]]' and 'dimnames()[[1L]] <- value'
  respectively. This is to mimic the R base array API.

- Fix rbind() between an SVT_SparseMatrix and an ordinary vector or matrix:
    > rbind(SVT_SparseArray(dim=6:5), logical(5))
    Error: C stack usage  7971988 is too close to the limit
    > rbind(SVT_SparseArray(dim=6:5), matrix(ncol=5))
    Error: C stack usage  7971988 is too close to the limit

- Parallelize more operations (with OpenMP) e.g. rowsum(). Right now only %*%,
  crossprod(), tcrossprod(), and the col*() methods (matrixStats operations)
  are parallelized.

- Implement coercion from Hits to SVT_SparseMatrix. The returned object
  should be an integer SVT_SparseMatrix with only zeros and ones that is
  the adjacency matrix of the bipartite graph represented by the Hits object.
  It will be fully lacunar.
  Note that in the case of a SelfHits object the result will be a square
  SVT_SparseMatrix.
  Question: should multiple edges between the same two nodes produce values
  > 1 in the adjacency matrix? (Google this.) This means that the resulting
  SVT_SparseMatrix won't necessarily be fully lacunar.

- Use "sparseness" instead of "sparsity" in the doc when referring to the
  quality of being structurally sparse. Use "sparsity" to refer to the
  number (>= 0 and <= 1) that measures how sparse an object is.
  sparsity = 1 - density

- Speed up row selection: x[row_idx, ]
  THIS in particular is VERY slow on a SVT_SparseArray object:
      set.seed(123)
      svt2 <- 0.5 * poissonSparseMatrix(170000, 5800, density=0.1)
      dgcm2 <- as(svt2, "dgCMatrix")
      system.time(svt2[-2, ])    # arghhh!!
      #   user  system elapsed
      #  4.606   0.180   4.804
      system.time(dgcm2[-2, ])
      #   user  system elapsed
      #  0.267   0.244   0.513
  Good news is that this is very fast:
      system.time(svt2[2, ])
      #   user  system elapsed
      #  0.001   0.000   0.002
      system.time(dgcm2[2, ])
      #   user  system elapsed
      #  0.165   0.000   0.165

- Support subsetting by a character matrix in .subset_SVT_by_Mindex().

- Support subsetting by a character vector in .subset_SVT_by_Lindex() in
  the 1D case.

- Implement double-bracket subsetting of SVT_SparseArray objects. Both,
  the 1D-style form (e.g. 'svt[[8]]') and the N-dimensional form (e.g.
  'svt[[2,5,1]]' for a 3D object).

- Implement table() method for SVT_SparseArray objects of type logical,
  integer, or raw (should it go in R/SparseArray-summarization.R?)

- Improve readSparseCSV() functionality by adding a few read.table-like
  args to it. See https://github.com/Bioconductor/SparseArray/issues/5
  for the details.

- We can't use base::apply() or base::asplit() on a SparseArray derivative
  because they turn it into an ordinary (i.e. dense) array.
  So turn apply() and asplit() into generic functions and implement methods
  for SparseArray objects. These methods could either:
  1. Mimic the behavior of base::apply() and base::asplit() but preverve
     sparseness. However, it should be clarified in the man page for the
     matrixStats methods that using apply() to compute stats on an object
     with > 2 dimensions is not going to be as efficient as using
     the 'dims' argument (almost all matrixStats methods support it). The
     latter should be 10x or 100x faster when used on big objects.
  2. Just fail with a friendly error message.

- Implement chol() for symmetric positive-definite square SVT_SparseMatrix
  objects. See '?base::chol' in base R.

- Implement svd() for SVT_SparseMatrix objects. See '?base::svd' in base R.

- Implement prcomp() for SVT_SparseMatrix objects. See '?stats::prcomp' in
  base R.

- More SBT ("Sparse Buffer Tree") use cases:

  1. Implement C helper _push_vals_to_SBT_by_Mindex(), and modify coercion
     from COO_SparseArray to SVT_SparseArray to use that instead of
     C_subassign_SVT_by_Mindex(). This will probably lead to a cleaner/simpler
     implementation. But will it also be faster?

  2. Revisit implementation of C_readSparseCSV_as_SVT_SparseMatrix(): Try to
     use an SBT instead of an ExtendableJaggedArray. Performance should not
     be impacted. Then we can get rid of the ExtendableJaggedArray thing.

- Implement `|`, `&`, and `!` for "raw" SVT_SparseArray objects. Important:
  they must perform bitwise operations like with "raw" vectors (see ?raw).

- Implement optimized arep_times() and arep_each() methods for SVT_SparseArray
  objects. The default methods in S4Arrays work but are not super efficient
  on SVT_SparseArray objects. This should significantly speed up the kronecker
  product between two SVT_SparseArray objects. In particular, does this
  improvement make kronecker() as fast on SVT_SparseArray objects as on
  dgCMatrix objects?

- A quick scan of BioC 3.18 software packages reveals that 25+ packages call
  the kronecker() function. Would be interesting to know how many of them need
  to do this on sparse arrays. Would they benefit from switching from dgCMatrix
  to SVT_SparseArray in the sparse case?

- Try to speed up SVT_SparseArray transposition by implementing a one-pass
  approach that uses ExtendableJaggedArray intermediate buffers (offss, valss).
  See src/readSparseCSV.c where this approach is already used.
  Note that this will require that ExtendableJaggedArray structs are able
  to support other types of columns (only support int at the moment).

- Support 'match(svt, table)' where 'svt' is an SVT_SparseArray object
  and 'table' an atomic vector. This will give us 'svt %in% table' for free.

- Implement more matrixStats methods for SVT_SparseMatrix objects. Those
  that are still missing and are actually used in Bioconductor are:
  rowMeans2, rowSums2, rowRanks, rowQuantiles, rowMads, rowIQRs, rowAlls,
  rowCumsums, rowWeightedMeans, rowAnyNAs) + corresponding col* methods.

- Implement more summarization methods for SVT_SparseArray objects.
  See R/SparseArray-summarization.R

- Add unit tests for the SVT_SparseArray stuff.


TAKE A LOOK AT THE FOLLOWING POTENTIAL USE CASES:

- Hi-C sequencing data. See Sunduz Keles work (BioC2024) and Jacques Serizay
  packages HiCExperiment and HiContacts.

- Support RcppML::nmf

- Support some of the SparseM (CRAN) operations.

- Go after dgCMatrix objects in ExperimentHub (query(eh, "dgCMatrix")),
  convert them to SVT_SparseMatrix objects and try to do the things that
  are usually done on them.

- Convert 8322787x1098 dgTMatrix (ExperimentHub resource EH5453) to
  SVT_SparseMatrix and try to do the things that the curatedMetagenomicData
  folks usually do on it.

