CHANGES IN VERSION 1.99.31
--------------------------

    o kontextualEngine() (the engine of Statial::kontextualTest()) now has labelClustering = FALSE by default: the
      image variances are no longer multiplied by the label-clustering factor. The option is kept. This matches
      spicy(), where the factor has been off since 1.99.8.
    o Why: with the factor, most of the weight goes to a few images. When the conditions were shuffled between
      patients (5 cohorts, 30 shuffles each), a few triples came up far more often than chance with the factor on;
      without it every cohort stayed at or below the nominal level (3.0-4.4% of p-values below 0.05) and gave
      no shuffle with a BH discovery (1 of 150 with the factor). It also gives more discoveries on the real labels
      (Cords 83 -> 94, Vannan 121 -> 161 triples).

CHANGES IN VERSION 1.99.30
--------------------------

    o A radius is now not tested when a condition's effective patients fall below min(3, m_g / 2), where m_g is the
      number of that condition's patients that contribute at that radius; before, the bound was 3. For conditions of
      6 or more patients nothing changes. The rule applies wherever the old one did: spicy() (method = "cell", both
      effects, with or without adjustment, Hartung-Knapp, and each level with more than two conditions) and
      kontextualEngine(). The label stays "few_effective_units".
    o Small studies get most of their tests back. Over the small real-data analyses, the radii of the grid that are
      tested rise from 78% to 95%, and a condition of 3 patients can now be tested (none could before). In
      diabetesData (4 patients per stage), 167 pairs are tested for the allocation effect (110 before) and 167 for the
      count effect (133 before). The kontextualEngine() example uses alpha cells again.

CHANGES IN VERSION 1.99.29
--------------------------

    o spicy() (method = "cell", both effects) and kontextualEngine() no longer test a radius where a condition has
      fewer than 3 effective patients: Kish's (sum w)^2 / sum w^2 of the patients' weights in that condition. A
      patient's weight grows with the information of its images, and when the patients do not differ (frailty
      variance 0) nothing caps it, so one patient with many cells can carry nearly all of a condition's weight. Such a
      radius has p-value NA (and the unadjusted one; with adjustment, adjusted_for is "none (few_effective_units)")
      and is left out of the combination over radii (davies, maxT, cauchy), as a radius with no informative patient
      is. With more than two conditions, a pair where any condition has fewer than 3 is not tested. A condition with
      fewer than 3 patients is never tested, and one with 3 or 4 rarely is.
    o The test now shares one dispersion between the conditions. Each condition's CR2 variance is divided by its
      model-based variance, the ratios are averaged with the conditions' numbers of patients as weights, and the
      model-based variance of the difference is multiplied by this average; the Satterthwaite df are unchanged.
      Before, each condition's CR2 variance entered on its own, so with conditions of unequal size and skewed patient
      effects a small condition's low spread set the standard error and the test gave too many small p-values. With
      adjustment, the ratios are those of each condition's coefficient in the adjusted design, and every coefficient's
      variance (also the reported effect of each adjustment) is scaled by their average. variance = "hartung_knapp" is
      unchanged.
    o Images with a missing condition are now also left out of the choice of an allocation pair's side and of the
      between-image variance (tau2_image), so the result equals the one with those images removed, also with
      adjustment and several images per patient. Their per-image effect and weight (pairwiseAssoc, imageWeights) are
      NA.
    o Survival is unchanged.
    o Calibration, on 11 real-data analyses with 30 relabellings of the conditions between patients each (the two
      worst-calibrated, sorin and cords, and 9 others), as the share of relabellings with any BH-significant pair for
      the combination over 25 radii (davies). The allocation effect: from 37% to 3.3% in sorin (404 patients, 340
      against 64), from 23% to 3.3% in cords (one patient carried 99% of a condition's weight), and from 5.2% to 3.0%
      on average over the other 9; for one radius, from 17% to 3.3% in sorin and from 17% to 6.7% in cords. The count
      effect: from 33% to 3.3% in sorin, from 27% to 0% in cords and from 4.1% to 3.0% over the other 9; for one
      radius, from 20% to 3.3%, from 17% to 0% and from 5.9% to 2.6%. On the 12 worst-calibrated of the paper's
      analyses, the any-BH rate falls from 10.8% to 1.7% for the allocation effect (one radius: 5.0% to 2.5%) and
      from 10.0% to 0.8% for the count effect (one radius: 5.6% to 1.1%).
    o The pooled dispersion costs power where the conditions' dispersions differ. With the true labels, BH-significant
      pairs over the 11 analyses go from 535 to 510 for the allocation effect (vannan 132 to 72, while karimi goes from
      68 to 90 and jackson from 7 to 15) and from 641 to 546 for the count effect (vannan 224 to 67, while karimi goes
      from 61 to 84 and metabric from 60 to 71).
    o Results change at every condition test (the standard error, so the p-value; the estimates are unchanged) and
      wherever images lack a condition. On the reference configurations, a radius's p-value moves by a median of 1e-4
      to 0.05 depending on the data set, and a combined p-value by at most 0.66. diabetesData, with 4 patients in each
      of its three stages, no longer tests 69 of its 179 allocation pairs at r = 50, and kontextualEngine() tests
      neither triple alpha -> Tc, Th within T cells there (the example now uses delta cells). Survival is identical()
      to 1.99.28's.

CHANGES IN VERSION 1.99.28
--------------------------

    o spicy() (method = "cell") leaves images whose condition is missing out of the unadjusted test. Before, the test
      of two conditions counted them in the second condition: its excess, the difference, the standard error, the df,
      the p-value and the frailty variance all included them. This affected the test without adjustment and the
      unadjusted_* columns of an adjusted test. The adjusted design already left these images out, so its results are
      unchanged, except where its frailty variance is held at the unadjusted value (an added column that varies
      between the images of a patient, such as adjustAbundance with several images per patient). There the held
      variance is now that of the images with a condition, and the adjusted test changes with it (#83).
    o With more than two conditions, an image with a missing condition stopped every pair from being tested ("no pair
      could be tested"). These images are now left out of the test, as with two conditions.
    o The test now equals the one with these images removed, except for two steps that use every image: the side of
      an allocation pair (attraction or avoidance), and the between-image variance (tau2_image) when patients have
      several images.
    o Results with no missing condition are unchanged: every reference configuration is identical() to 1.99.27's.

CHANGES IN VERSION 1.99.27
--------------------------

    o spicy() (method = "cell", effect = "allocation") no longer tests a radius where one condition has no
      informative patient. The allocation effect is bounded: an image of an avoiding pair with no `to` cell next to a
      `from` cell has the effect -1 exactly, and an image of an attracting pair with every `to` cell next to one has
      +1. A patient is informative at a radius when at least one of its images is not at -1 or +1. When every patient
      of a condition is at the bound, that condition's CR2 variance is exactly 0, the standard error rests on the few
      informative patients of the other condition, and the Satterthwaite df stay at 9-14: on relabelled real data such
      tests reached t of -44 to -63. Such a radius now has p-value NA (and the unadjusted one; with adjustment,
      adjusted_for is "none (no_information)"), and it is left out of the combination over radii (davies, maxT,
      cauchy). The rule is the same for one radius, for adjusted and unadjusted tests, and with more than two
      conditions, where a pair with any condition without an informative patient is not tested. The printed summary
      counts the pairs not tested.
    o Calibration, on the 80 real-data analyses of the paper with 30 relabellings of the conditions between
      patients each: the share of relabellings with any BH-significant pair falls from 9.8% to 3.4% for the
      combination over 25 radii (davies) and from 3.5% to 2.8% for one radius; the rate of combined null p-values
      below 1e-6 falls from 1.5e-5 to 2.5e-7. With the true labels, BH-significant pairs fall from 781 to 711. On 162
      synthetic data sets with known truth, power is unchanged and the false-positive rate is lower.
    o Results change only at such radii and in the combined p-values that included them. Of the reference
      configurations, the 25-radius synthetic data with 40 types change at 156 of 40000 radius tests (all at the
      smallest radius, avoiding pairs) and in the combined p-value of 131 of 1600 pairs, and diabetesData with its
      three stages (r = 50) no longer tests 14 of 193 pairs; every other configuration, every count-effect result
      (the count effect has no bound) and Kontextual are identical() to 1.99.26's. Survival is unchanged: its score
      test has no condition groups, and a response with no variation at all is already not tested.

CHANGES IN VERSION 1.99.26
--------------------------

    o spicy() (method = "cell") with adjustAbundance or covariates no longer fits the adjusted design at a radius
      where the pair's response has no variation, as where no `to` cell has a `from` neighbour (small r) or every
      one has (saturated, large r). There the unadjusted test is exactly 0 / 0 and its radius is left out; the
      adjusted design turned those exact zeros into rounding noise (estimates and standard errors of about 1e-16),
      so the radius was kept with an arbitrary t and influence vector. That noise moved the combined p-values
      (davies, maxT, cauchy) arbitrarily: with age perturbed by a relative 1e-10, 21-28% of the adjusted combined
      p-values on the Jackson, Keren and Schurch data moved by more than 0.01, and single-radius adjusted tests at
      such radii gave noise p-values. Such a radius is now not tested: adjusted_for is "none (no_variation)", its
      p-value is NA, and nothing of it enters the combination, which equals that with the radius left out. The same
      perturbation now moves no combined p-value by more than 4e-9. Adjusted p-values at no-variation radii, and the
      combined p-values that included them, change: 162 of 576 pairs on Jackson (25 radii), 55 of 256 on Keren and 85
      of 324 on Schurch, none crossing 0.05; for t04 -> t05 of the 10-type synthetic data (r = 5, 10, ..., 125, age
      and abundance, davies) the combined p is 0.772 (0.967 before). Unadjusted results, and adjusted ones at radii
      with variation, are identical() to 1.99.25's.
    o The core's design test (design_tests()) reports reason "no_variation" when the design fits the response
      exactly: a weighted residual sum of squares at most (64 eps n)^2 times the weighted sum of squares of the
      response. This covers the paths that do not go through the unadjusted test first (more than two conditions,
      survival, Hartung-Knapp). A survival test is adjusted for abundance only where the unadjusted one was done.
    o With several radii and something to adjust for, a radius where no pair's design was fitted has the same effect
      columns as the others (NA); before, combining the radii stopped with an error.

CHANGES IN VERSION 1.99.25
--------------------------

    o spicy(combine = "maxT") computes the max-T p-value, the probability that a multivariate normal vector with the
      radii's sandwich correlation leaves the cube [-c, c]^K, by the quasi-Monte Carlo method of Genz (1992) and
      Genz & Bretz (2009), with an absolute error target of 1e-4, in place of recursive Gauss-Legendre quadrature.
      The variables are separated on the Cholesky factor of the correlation, reordered so the narrowest expected
      interval comes first, and integrated by Korobov lattice rules of growing size (the baker's transformation,
      antithetic points, 12 random shifts) until 3.5 standard errors are below 1e-4, or 100,000 points per shift.
      The shifts come from a fixed seed, so the same inputs give the same p-value on every call, at any number of
      cores. The method and its lattice parameters are written from the publications (inst/scripts/
      korobov_search.cpp finds the parameters); no code is taken from mvtnorm. Two radii keep the quadrature,
      which is accurate to about 1e-10 there; the quadrature itself remains in the core
      (mvn_outside_quadrature()) for comparison.
      The quadrature took 3.2 million evaluations per pair with six radii; the new method takes about 26,000 (18 ms
      against 233 ms per pair). On one core, for 50 images of 5,000 cells with r = c(5, 10, 20, 30, 40, 50), 80 cell
      types take 141 s (1,438 s before), 40 types 32 s (381 s), 20 types 8.4 s (90 s), 10 types 2.1 s (23 s) and
      5 types 0.6 s (5.6 s).
      Against mvtnorm::pmvnorm() at an absolute error of 1e-6, on the 100 tests of 10 cell types the new p-values
      differ by at most 6.1e-5 (0.44% relative at p < 0.05), and on 308 random and singular correlation matrices
      with three to six radii by at most 1.0e-4 (4.8% relative for 0.001 <= p < 0.05); pmvnorm() at 1e-4 differs
      from the same reference by up to 8.3e-4. On the timing data (4,200 pairs at 10, 20 and 40 types, allocation and
      count), 6 p-values change by more than 1e-4 (at most 1.4e-4) and none crosses 0.05; the other columns are
      unchanged, and results without maxT are identical() to 1.99.24's.

CHANGES IN VERSION 1.99.24
--------------------------

    o spicy() (method = "cell", two conditions) tests every pair at every radius in one call to the C++ core
      (src/core/src/pairs.cpp, pair_tests()): the image rows, the image random effect, the frailty test, the
      adjusted design and, with several radii, the max-T, Davies or Cauchy combination. Before, each (pair, radius)
      test went back to R, which turned the core's results into data.frames one row at a time; that bookkeeping,
      not the arithmetic, took over 95% of the time. The tests, degrees of freedom, variances and combinations are
      unchanged and the results are identical() to 1.99.23's. On one core, for 50 images of 5,000 cells: with
      r = seq_len(25) * 2 and combine = "davies", 80 cell types (6,320 pairs) take 8.6 s (263 s before), 40 types
      3.1 s (62 s), 20 types 1.4 s (15 s) and 10 types 0.9 s (4.8 s); with r = 25, 80 types take 0.6 s (10.9 s)
      and 40 types 0.3 s (2.5 s). Ten million cells with r = 25 and 10 types take 9.3 s, as before. With
      `cores` > 1 the pairs are also tested on that many threads, with identical results. More than two conditions,
      survival outcomes and Kontextual keep their per-pair path, with the core's image rows now returned as
      data.frames without a call to as.data.frame().
    o writeCellStore() writes the image column as a categorical always (a character column with the images in
      order of first appearance, a factor with its own levels), so Python's spatialdata.read_zarr() reads stores
      made from a data.frame with character image IDs (it requires a categorical region key). The instance IDs are
      written in 16 bits at least, which spatialdata also requires. In a column with missing values, the mask and
      the numeric or logical values now carry the "array" encoding attributes, as anndata writes them, so anndata
      no longer gives an OldFormatWarning on R stores. spicy() results from these stores are identical to those before.

CHANGES IN VERSION 1.99.23
--------------------------

    o writeCellStore() writes the store itself, a chunk of 2^20 rows at a time, in place of one anndataR call that
      wrote every array as a single chunk. It failed at 100 million cells (on the row index), and reading any slice
      of a store it wrote decompressed the whole column. Every column, the row index and obsm "spatial" (float64,
      as the cells give it) are now in chunks of 2^20 rows compressed with zstd, so spicy() reads a store a block at
      a time. The layout is the AnnData encoding as before, with the same arrays, data types and attributes, and
      spicy() results are identical to those from the old stores and from the cells in memory. Writing 100 million
      cells takes 81 s and about 0.4 GB beyond the data.frame, and spicy(r = 25) from that store peaks at 1.8 GB on
      one core and 5.2 GB on 8 (8.8 GB and 15.6 GB from the store written as single chunks). The region and instance keys in uns are now string
      scalars and the per-image table carries the SpatialData table attributes, so Python's spatialdata reads the
      store. anndataR is no longer suggested.

CHANGES IN VERSION 1.99.22
--------------------------

    o spicy() (method = "cell"): images whose condition is missing are now left out of the covariate design, as
      they already were of the unadjusted test. Before, any such image made the adjusted design rank deficient, so
      the covariate adjustment was dropped (`adjusted_for` "none (design_not_full_rank)") and the unadjusted test
      reported, for some pairs or, from a store, for all of them (GitHub issue #82).
    o Tests: the check that workers of a started BiocParallel param run one BLAS thread is skipped where BLAS can
      only run one thread (the Bioconductor builders).

CHANGES IN VERSION 1.99.21
--------------------------

    o spicy() (method = "cell") takes `imageInfo`: a table with one row per image (a data.frame whose imageID column,
      or else its row names, gives the image IDs, or the name of a table of the SpatialData store or object) for the
      condition, subject, covariates or survival outcome when they are not columns of the cell table. It works for
      cells in memory and in a store, so a survival outcome (a Surv column of imageInfo) can now be analysed from a
      store. Images missing from imageInfo, and rows for images not among the cells, are errors. Results are
      identical to those with the same columns in the cell table. The argument choosing a store's cell table is now
      called `tableKey` (it was `table`), matching spicyr's `table_key`.
    o writeCellStore() writes a data.frame of cells (and optionally a per-image table) as a SpatialData Zarr store,
      which spicy() and spatialdataR's readSpatialData() read. The vignette ends with a small example: a store of 40
      METABRIC cores, analysed from its path and as a SpatialData object, with results identical to the data.frame.
      spatialdataR and anndataR are suggested.
    o The image-by-image loop of the cell method is in the C++ core shared with spicyr (src/core/src/stream.cpp):
      the passes over the image and cell-type codes, the gathering of each batch of whole images and the neighbour
      sums of each image in a Dataset of its own, the images shared among `cores` threads. The R bindings only
      describe R's vectors to it. Results are identical (to 1.99.20 on 100 million and 1 billion cells, and on the
      tests). From a store of 100 million cells, spicy(r = 25) takes 196 s and 1.5 GB on one core (250 s and 1.7 GB
      before, run the same night) and 78 s and 5.2 GB on 8 cores (109 s and 5.1 GB); 1 billion cells take 15
      minutes and 5.3 GB on 8 cores and 44 minutes and 1.65 GB on one, on a busy server.

CHANGES IN VERSION 1.99.20
--------------------------

    o spicy() (method = "cell") reads a SpatialData or AnnData Zarr store directly: give its path as `cells`, with
      `table` choosing the table and the column arguments naming obs columns (`imageID` defaults to the table's
      region key and `spatialCoords` to obsm "spatial"). One pass over the image, cell type, condition and subject
      columns, a block of rows at a time, gives the cells per image and type and the image-level values; the
      coordinates and cell types are then read a batch of whole images at a time, so the table is never held in
      memory. Results are identical to those of the same cells as a data.frame. Each image of a batch runs on its
      own, the images shared among `cores` threads. A SpatialData object (spatialdataR) is read from the store it
      came from, or else from its table. Reading a store needs the Rarr package (Suggests). spicy(r = 25) from a
      store of 1 billion cells (500 images, 10 types) takes 38 minutes and at most 1.6 GB on one core, and
      14 minutes and 6.3 GB on 8; 100 million cells take 224 s (1.7 GB) and 93 s (5.1 GB).

CHANGES IN VERSION 1.99.19
--------------------------

    o spicy() (method = "cell") reads the cells image by image: the neighbour search and its sums run on one batch
      of whole images at a time, gathered from the cell table's own columns, so the cells are neither sorted nor
      copied as a whole. Image IDs and cell types that are plain character columns become factors in one pass.
      Results are identical; at 100 million cells (50 images) the peak memory above the data falls from about
      17 GB to about 1 GB (0.8 GB of it the integer codes of the two factors), and the run takes 241 s
      instead of 363 s.

CHANGES IN VERSION 1.99.18
--------------------------

    o spicy() (method = "cell") needs much less memory and time on large data: it no longer makes a cellID and an
      imageCellID string for every cell (it uses neither), keeps one row per image instead of a reordered copy of the
      whole cell table, and takes the cells per image and type from the counts it already has. Results are identical.

CHANGES IN VERSION 1.99.17
--------------------------

    o spicy() with several radii finds every cell's neighbours once, at the largest radius, and counts them for each
      radius from that one search. Results are identical; an omnibus over 25 radii takes about 4 times less time.

CHANGES IN VERSION 1.99.16
--------------------------

    o R-level parallel loops (method = "image" with sigma or a concave window, weightsByPair, survival models) run
      on BiocParallel instead of parallel::mclapply: forked workers start once per call and share the data,
      tasks are handed out in about eight chunks per worker, and socket workers are used on Windows. BLAS and
      OpenMP run one thread in each worker during the call; OpenBLAS otherwise starts one thread per core in
      every worker, and forked workers can hang after the session has used several OpenMP threads.
      BiocParallel and RhpcBLASctl are imported; parallel is no longer imported.

CHANGES IN VERSION 1.99.15
--------------------------

    o spicy(combine = "davies") combines many radii by Davies' (1987) bound for the maximum of the standardised
      effect over the radii, from the same per-radius tests and sandwich correlations as the max-T. It allows a fine
      grid of radii; combine = "maxT" now stops with a message for more than six radii.

CHANGES IN VERSION 1.99.14
--------------------------

    o spicy(weightsByPair = TRUE) no longer fails for a cell type paired with itself: both terms of the weight
      model have the same covariate, so it is fitted as one monotone smooth, which gives the predictions scam()
      gives for the two-term model.

CHANGES IN VERSION 1.99.13
--------------------------

    o spicy() fits one model for the cell method: the patient frailty and the image random effect, without the
      label-clustering inflation. The arguments frailty, labelClustering and imageHeterogeneity are removed, and
      variance = "auto" is removed: the variance is CR2 on Satterthwaite df for every study size unless
      variance = "hartung_knapp" is given.

CHANGES IN VERSION 1.99.12
--------------------------

    o Cell method: images within a patient now have their own random effect (imageHeterogeneity = TRUE, the
      default). Each image is weighted by 1 / (its sampling variance + the between-image variance), with the
      between-image variance estimated by DerSimonian-Laird from the spread within patients and reported as
      tau2_image, so one large image can no longer outweigh a patient's other images. With one image per patient
      the results are unchanged.

CHANGES IN VERSION 1.99.11
--------------------------

    o Concave windows (method = "image", window = "concave") are computed by a C++ port of concaveman, which
      gives the same polygon as concaveman::concaveman(); concaveman is no longer suggested.

CHANGES IN VERSION 1.99.10
--------------------------

    o New buildKnnGraph(): the k-nearest-neighbour graph between the cells of each image, from the C++ core,
      stored as a colPair for convPairs(). It gives the same graph as imcRtools::buildSpatialGraph(type =
      "knn"), so imcRtools is no longer suggested; pkgdown is no longer suggested either.

CHANGES IN VERSION 1.99.9
-------------------------

    o Vignette: the numbers quoted in the text are computed from the results, and the statements about them
      are checked when the vignette is built. The shuffled-label check uses 10 shuffles.

CHANGES IN VERSION 1.99.8
-------------------------

    o spicy(labelClustering = ) defaults to FALSE: the within-image variance is no longer inflated for
      `to` cells that cluster among themselves unless asked for.

CHANGES IN VERSION 1.99.7
-------------------------

    o spicy(variance = ) defaults to "cr2" again (CR2 on Satterthwaite df). "auto" (the Hartung-Knapp
      variance when a condition has at most 5 patients, CR2 otherwise) remains available as an option.

CHANGES IN VERSION 1.99.6
-------------------------

    o The allocation effect is now scaled on two sides. A pair whose `to` cells avoid the `from` cells (fewer
      `to` cells with a `from` cell within r than expected, summed over all images, without the conditions)
      uses (observed share - q) / q, the fraction of the `to` cells that would have a `from` cell nearby that
      were moved away; an attracting pair keeps (observed share - q) / (1 - q). Before, avoidance used the
      attraction scale, on which the effect of a fixed avoidance grows with q, i.e. with how many `from`
      cells there are and how densely they are packed, and is unbounded below as q approaches 1. The
      results gain a `side` column ("attract" or "avoid").

    o spicy(variance = ) gains "auto", the new default: the Hartung-Knapp variance on m - 2 df when a
      condition has at most 5 patients (CR2's Satterthwaite df are then very small), CR2 otherwise. The
      variance used is returned in $variance.

CHANGES IN VERSION 1.99.5
-------------------------

    o The cell method's default effect is the allocation effect (effect = "allocation"): the extra fraction
      of `to` cells with at least one `from` cell within r, (observed share - q) / (1 - q), q its exact
      expectation under random labelling. If a fraction f of the `to` cells is moved next to `from` cells,
      the effect is f, however many `from` cells there are and however densely they are packed. The count
      excess of earlier versions (extra `from` cells per `to` cell) is effect = "count"; it grows with how
      densely the `from` cells are packed, so a change in packing alone can appear as a change in
      co-localisation. Self-pairs use the exact random-labelling expectation of the allocation count.

    o spicy(adjustAbundance = ) now defaults to FALSE (kontextualEngine() is unchanged). The log-share
      adjustment does not separate more `from` cells from more densely packed ones, and it removes real
      effects when the share tracks the condition.

    o Survival results: the column log_hr_per_cell is renamed log_hr_per_unit, the log hazard ratio per unit
      of the effect (per 100% of `to` cells for the allocation effect, per extra `from` cell per `to` cell
      for the count).

    o The label-clustering factor of the allocation effect is computed on its binary scores.

    o Survival results no longer repeat the main test in unadjusted_* columns when abundance is not
      adjusted.

    o k nearest neighbours: images with at most k cells were read out of bounds in the neighbour sums; they
      are now skipped.

CHANGES IN VERSION 1.99.4
-------------------------

    o The label-clustering factor of a `to` type is the median over every counted (`from`) type, whichever pairs
      are requested, in spicy() and kontextualEngine(). A pair's result no longer depends on the other pairs asked
      for: spicy(from = "A", to = "B") gives exactly the A__B row of spicy(). Before, the factor rested on the
      requested pairs only (one ratio per image for a single pair), which in Kontextual simulations cost power.

CHANGES IN VERSION 1.99.3
-------------------------

NEW FEATURES

    o kontextualEngine(), the engine of Statial::kontextualTest() (documented there): Statial's
      Kontextual statistic compared in each image with its exact expectation under random labelling
      within the parent, combined by spicy()'s frailty GEE. Internal; topPairs(), signifPlot() and
      spicyBoxPlot(parent = ) work on its results.

    o signifPlot() draws the survival results of the cell method (it failed before).

    o spicy(alternateResult = ) warns when given Kontextual values, which it compares between conditions
      as they are, and points to Statial::kontextualTest().

CHANGES IN VERSION 1.99.2
-------------------------

    o The direction of a pair is reversed relative to 1.99.0 and 1.99.1 (and restored to that of
      earlier development versions): from -> to now asks whether `to` cells are placed near
      `from` cells, with the excess counted as extra `from` cells around each `to` cell. A result
      reported as A -> B by 1.99.0 or 1.99.1 is reported as B -> A now, with the same numbers.

CHANGES IN VERSION 1.99.0 (spicyR 2.0)
--------------------------------------

MAJOR CHANGES

    o spicy() now runs spicyR Cell by default (method = "cell"): for each pair from -> to,
      the excess number of `from` cells within r of each `to` cell beyond random labelling of
      the observed cells (are `to` cells placed near `from` cells more than other cells are?), combined over images and patients by a frailty GEE and tested with a
      CR2 variance on Satterthwaite degrees of freedom, with patients (`subject`) as the units.
      The original test is available with method = "image" (and is chosen automatically when
      `alternateResult` or `sigma` is given).

    o The direction of a pair: `to` is the type whose placement is tested (its cells are
      relabelled at random under the null, and the excess is per `to` cell), `from` the type
      counted around it.

NEW FEATURES

    o By default the test is adjusted for the abundance of the counted (`from`) type (the log of its share
      of all cells in each image; adjustAbundance = TRUE) and for any covariates; the unadjusted
      test is reported in the unadjusted_* columns, and each adjustment's effect and p-value in
      abundance_effect and <covariate>_effect columns.

    o spicyBoxPlot() shows a point per image sized by its weight in the test, and is interactive
      with interactive = TRUE (plotly); plotImage(r = ) draws the radius around each to cell.

    o Two or more conditions (each level against the reference), image- or patient-level
      covariates, k nearest neighbours (k), several radii combined by max-T with the sandwich
      correlation or by the Cauchy combination (r = c(...), combine), Hartung-Knapp variance for
      very few patients, and survival outcomes (a Surv condition: score test and hazard ratio).

    o The statistics run in a C++17 core shared verbatim with the Python twin, spicyr
      (https://github.com/SydneyBioX/spicyr-py), which reproduces spicyR's results.

    o Much faster: every ordered pair of 22 cell types from 400,000 cells (METABRIC) in seconds.

    o spicyR 2.0 was written with AI assistance (Claude, Anthropic, through Claude Code), directed by the
      authors: the C++ core, its R bindings, R/cell.R and R/spicy_main.R, the C++ ports
      of the image method's model fits, the tests (including a plain-R reference written from the
      method's equations) and the vignettes. The commits concerned carry a Co-Authored-By line.

    o Far fewer dependencies.

CHANGES IN VERSION 1.18.0
-------------------------

NEW FEATURES

    o Added functionality for survival and mixed effect survival analysis.
    
    o Added new plotting functions for survival analysis.
    
    o Added support for using weights for alternative results.
    

BUG FIXES

    o Fixed issue with bubble plot where unicode characters don't show up on
      certain operating systems.
    
    o Fixed issue with bubble plot where legend was not accurately showing 
      conditions or phenotypes on the plot.


SIGNIFICANT USER-VISIBLE CHANGES

    o Vignette has been updated with new dataset showcasing more functionality
      of spicyR.
      
DEPRECATED ARGUMENTS

    o `nCores` argument deprecated for both getPairwise() and spicy(), replaced 
    with `cores`.
    
    o `BPPARAM` argument deprecated for both getPairwise() and spicy(), replaced 
    with `cores`.
    
    o `imageIDCol` argument deprecated for both getPairwise() and spicy(), replaced 
    with `imageID`.
    
    o `cellTypeCol` argument deprecated for both getPairwise() and spicy(), replaced 
    with `cellType`.
    
    o `spatialCoordCols` argument deprecated for both getPairwise() and spicy(), replaced 
    with `spatialCoords`.
    
    o `Rs` argument deprecated for both getPairwise() and spicy(), replaced 
    with `r`.
    
* Deprecation warnings have been added to guide users to the new argument names.

    

