# primarycensored 1.5.1

This patch release fixes a performance regression introduced in 1.5.0 that slowed the Stan likelihood for positive-support delays.

## Bug fixes

- Fixed a performance regression in the Stan `primarycensored_lpmf` and `primarycensored_lcdf` functions for positive-support delays.
  After the lower-truncation guard was relaxed in 1.5.0 to support negative `L`, every likelihood evaluation for positive-support delays entered a truncation-normalisation block that cancels to a no-op but still adds gradient calculations (an `exp`/`log_diff_exp` per call), giving a roughly 30% slowdown on the likelihood block.
  The guard now skips this block unless there is a finite `D`, a strictly positive `L`, or a finite `L` on a real-support distribution, restoring 1.4.0 performance without changing results or losing the negative-support behaviour.
  Thanks to @sbfnk for reporting (#323).

# primarycensored 1.5.0

This minor release extends the `L` (lower truncation) parameter to accept negative and `-Inf` values in both the R and Stan code, letting delay distributions with support below zero (e.g. normal, logistic, Cauchy, Gumbel) be used with primary censoring and fitted via `fitdistdoublecens()` and `pcd_cmdstan_model()`. The default value of `L` has changed from `0` to `-Inf`, which leaves results unchanged for distributions with non-negative support (e.g. lognormal, gamma, Weibull). It also fixes a normalisation bug in the exponential growth primary distribution and rewrites the analytical CDFs in a CDF-direct form, dropping the `pracma` dependency.

## Breaking changes

- The default value of `L` in `pprimarycensored()`, `dprimarycensored()`, `qprimarycensored()`, `rprimarycensored()`, and `pcens_quantile()` has changed from `0` to `-Inf`. `fitdistdoublecens()` now treats a missing `L` column as `L = -Inf` to match. For delay distributions with support on the non-negative reals (e.g. lognormal, gamma, Weibull) `F_cens(0) = 0`, so the new default leaves results unchanged. Callers that relied on the implicit left truncation at `0` (for example to truncate signed-support delays such as `pnorm`) must now pass `L = 0` explicitly (or add an `L` column in `fitdistdoublecens()`). (#267)

## New features

- `L` may now be negative or `-Inf` in `pprimarycensored()`, `dprimarycensored()`, `qprimarycensored()`, and `rprimarycensored()`. This lets delay distributions with support below zero (e.g. normal, logistic, Cauchy) be used with primary censoring. `L = -Inf` is the sentinel for "no left truncation"; any finite `L` left-truncates the distribution at `L`. (#267)
- The Stan functions and `pcd_as_stan_data()` mirror the R-side handling of `L`: negative and `-Inf` values are accepted, and a missing `start_relative_obs_time` column defaults to `-Inf`. `pcd_cmdstan_model()` now accepts negative observed delays and fully-negative truncation windows, letting distributions with support on the reals (e.g. logistic, Cauchy, Gumbel) be fitted. (#313)
- The `dist_id` upper bound in `pcens_model.stan` has been raised from `17` to `25`, exposing every delay distribution that `dist_lcdf` already dispatches (Normal, Double Exponential, Pareto, scaled inverse chi-square, Student's t, Uniform, von Mises) through `pcd_cmdstan_model()`. (#314)

## Documentation

- Added a CDF-direct form of the primary-censored analytic solutions to the "Why it works" and "Analytic solutions" vignettes alongside the existing survival-function form.
- Added a "Fitting delay distributions with negative support" vignette that walks through estimating a logistic-distributed serial interval with both `fitdistdoublecens()` and `pcd_cmdstan_model()` from doubly-censored, right-truncated samples that include negative observed delays.

## Bug fixes

- Fixed incorrect normalisation in `dexpgrowth()`, `pexpgrowth()`, and their Stan equivalents (`expgrowth_pdf`, `expgrowth_lpdf`, `expgrowth_cdf`, `expgrowth_rng`) when `min` is non-zero.
  The PDF and CDF formulas contained a stray `exp(-r * min)` factor from using `exp(r * (x - min))` instead of `exp(r * x)`.
  The Stan RNG had a compensating `xmin +` offset.
  The bug did not affect results when `min = 0` (the default and only value used within the package's primary censoring functions).
  Thanks to @TimTaylor for reporting (#290).

## Internal

- Rewrote the analytical primary-censored CDFs (Gamma, Log-Normal, Weibull with uniform primary) in R and Stan to use a CDF-direct algebraic form, $F_{S_+}(d) = [d F_T(d) - q F_T(q) - E(\tilde F(d) - \tilde F(q))]/w_P$. In Stan this unifies the `q = 0` and `q > 0` code paths (single algebraic expression, better for NUTS), the outer `log_diff_exp` ordering is now mathematically guaranteed, and the Gamma case uses the incomplete-gamma recursion $P(k{+}1, y) = P(k, y) - y^k e^{-y}/\Gamma(k{+}1)$ to halve `gamma_lcdf` evaluations. Behaviour and tests are unchanged.
- Replaced `pracma::gammainc` with `stats::pgamma` in the Weibull `g()` helper, dropping the `pracma` dependency. The previous `pwindow > 3` fallback to numeric integration (and the internal overflow guard) is no longer needed — the base R implementation is stable across the full parameter range. Closes #127.

# primarycensored 1.4.0

This major release adds left-truncation support via the `L` parameter, enabling distributions to be truncated over `[L, D]` rather than just `[0, D]`. It also removes deprecated functionality that was soft-deprecated in version 1.1.0 and removes the `lifecycle` and `rlang` packages from dependencies.

## Breaking changes

- Removed deprecated `pdist_name` and `dprimary_name` arguments from `pprimarycensored()`, `dprimarycensored()`, `new_pcens()`, and `fitdistdoublecens()`. Use `add_name_attribute()` on the `pdist` and `dprimary` functions instead to enable analytical solutions.
- Removed deprecated support for passing numeric values to the `pwindow` and `D` arguments in `fitdistdoublecens()`. These must now be column names in `censdata`.
- Removed `lifecycle` and `rlang` packages from dependencies.
- R functions now include an `L` parameter before `D` in their signatures. If you were using positional arguments for `D` (e.g., `pprimarycensored(x, pdist, pwindow, 10)`), you must now use named arguments (e.g., `pprimarycensored(x, pdist, pwindow, D = 10)`). The affected functions are `dprimarycensored()`, `pprimarycensored()`, `rprimarycensored()`, and `qprimarycensored()`. (#63)
- Stan functions now include an `L` parameter for lower truncation, placed before `D`. This affects `primarycensored_lpmf`, `primarycensored_lcdf`, `primarycensored_cdf`, `primarycensored_pmf`, and all vectorized variants. Update your Stan code to include the new parameter: `primarycensored_lpmf(d | dist_id, params, pwindow, d_upper, L, D, primary_id, primary_params)`. (#63)

## New features

- Added `dependencies` argument to `pcd_load_stan_functions()` that automatically resolves and includes all functions that the requested functions depend on. When `TRUE`, dependencies are included in topological order (dependencies before functions that use them). (#171)
- Added `pcd_stan_function_deps()` to query the dependency graph of Stan functions, returning all dependencies for a given function in topological order. (#171)
- Added 8 distributions to `pcd_distributions` that were previously only available in Stan: normal, inverse chi-square, double exponential, pareto, scaled inverse chi-square, student t, uniform, and von Mises (IDs 18–25). These are now accessible via `pcd_stan_dist_id()`. (#277)
- Added left-truncation support via the `L` parameter to all primary censored distribution functions (`dprimarycensored()`, `pprimarycensored()`, `rprimarycensored()`, `qprimarycensored()`) and Stan functions. The `L` parameter specifies the minimum delay (lower truncation point), enabling distributions to be truncated over `[L, D]` rather than just `[0, D]`. This is useful for generation intervals and other settings where delays below a threshold cannot occur. Defaults to `L = 0` for backward compatibility. (#63)

## Bug fixes

- Fixed `expgrowth_lpdf` Stan function to support negative growth rates. Previously, `log(r)` returned NaN for `r < 0`. Now uses `log(abs(r))` and `log(abs(...))` for the denominator. (#276)
- Fixed Stan `dist_lcdf` distribution ID mapping to match R's `pcd_distributions` table. Previously, most distribution IDs were mismatched between R and Stan (e.g. `pcd_stan_dist_id("weibull")` returned 3 but Stan's `dist_lcdf` used ID 3 for the Normal distribution). Only lognormal (1), gamma (2), and exponential (4) were correct. This affected the ODE numerical integration path for all mismatched distributions. (#277)
- Renamed `min` and `max` parameters to `xmin` and `xmax` in Stan functions (`expgrowth_pdf`, `expgrowth_lpdf`, `expgrowth_cdf`, `expgrowth_lcdf`, `expgrowth_rng`, `primary_lpdf`) to avoid conflicts with Stan built-in functions. CmdStan 2.38.0 now strictly enforces reserved keyword restrictions when exposing Stan functions to R. (#258)

## Documentation

- Updated vignette titles and section headers to use sentence case consistently. (#273)

# primarycensored 1.3.0

This minor release improves documentation for `fitdistdoublecens()` and adds learning objective sections to vignettes. It also fixes floating-point precision issues in `dprimarycensored()` and adds bounds checking to CDF methods.

## Documentation

- Improved documentation for `fitdistdoublecens()`:
  - Expanded function description to explain primary censoring, secondary censoring, and truncation.
  - Added detailed `@details` sections explaining how distribution names are resolved (e.g., `"gamma"` uses `dgamma()` and `pgamma()`), and what the function does internally.
  - Enhanced `distr` parameter documentation with examples and guidance on custom distributions.
- Added "What you will learn" and "How you might adapt this vignette" sections to both the fitdistrplus and Stan fitting vignettes to help users understand learning objectives and adapt the examples for their own data.

## Package

- Added tests to verify that `pcd_as_stan_data()` and `fitdistdoublecens()` correctly handle data frames with additional columns beyond those required. (#213)

## Bug fixes

- Fixed an issue where `dprimarycensored()` could return very small negative values (e.g., -2.2e-16) due to floating-point precision when computing PMF as CDF differences. PMF values are now clamped to be non-negative. (#238)
- Added bounds checking to all `pcens_cdf()` methods to ensure CDF values are always in [0, 1], complementing the existing upper bound check with a lower bound check.

# primarycensored 1.2.0

This minor release adds quantile functions for primary censored distributions and enhances the `fitdistdoublecens()` function to support varying primary censoring windows and truncation times. The release also improves documentation, particularly the Stan reference, making it easier for users to navigate and work with the Stan code.

## Package

- Added new functions to compute quantiles of the primary censored distribution:
  - `pcens_quantile()`: Core implementation using numerical optimisation to find
    quantiles by inverting the CDF. The implementation allows for analytical
    solutions to be added in future versions using the same interface as `pcens_cdf()`.
  - `qpcens()` and `qprimarycensored()`: Convenient wrapper functions that provide
    alternative interfaces to `pcens_quantile()`.
- Added support to `fitdistdoublecens()` to allow for varying primary censoring windows and truncation times. As part of this refactor the interface has also been improved to allow for more flexible data input.

## Documentation

- Removed authorship from vignettes.
- Updated the `fitdistrplus` vignette to use more complex data where the relative observation time is not constant. Also removed the note that the `fitdistdoublecens()` function is limited to a single primary censoring windows and truncation time as this is no longer the case.
- The [Stan Reference](https://primarycensored.epinowcast.org/stan/) has been enhanced to make it easier to navigate the Stan code.

# primarycensored 1.1.0

This minor release improves the documentation and the internal system used to automatically discover analytical solutions. It also adds lookups for supported supported distributions and tooling for working with these lookups. This makes it easier for package developers using `primarycensored` and also makes it easier to work with the Stan likelihood functions by improving the discoverability of the distribution functions. Minor bugs were also fixed.

## Package

- Updated the CI so that stan code is also tested on Windows and Mac. This is to ensure that the stan code is compatible with these platforms and in response to a CI bug in `epidist`.
- Revised approach to automatic discovery of distribution functions. This soft deprecates the `pdist_name` and `dprimary_name` arguments throughout. Users wishing to pass distribution names (i.e. to potentially leverage analytical solutions) are advised to use the newly introduced `add_name_attribute()` function. Adds transient dependency on `lifecycle` and `rlang` packages. See #188 by @pearsonca.
- Added `pcd_stan_dist_id()` to allow for discovery of distribution IDs for Stan models.
- Added `pcd_dist_name()` to allow for discovery of distribution names for R functions as needed for `add_name_attribute()`.
- Added documentation to suggest the use of `methods(pcens_cdf)` to find analytical solutions.
- Added new data sets `pcd_distributions` and `pcd_primary_distributions` to document the distributions supported by `primarycensored`.

## Documentation

- Added a CRAN downloads badge to the README.
- Corrected how to specify an empty array in the docs of `primarycensored_lcdf()`.
- All documentation was reviewed and updated to enhance readability and clarity.

## Bug fixes

- Added a missing `@family` tag to the `pcens` functions. This omission resulted in the Weibull analytical solution not being visible in the package documentation.
- Added precalculation of vector sizes to the `primarycensored_cdf()` stan function, avoiding errors on some platforms due to narrowing conversions in aggregate initialisation.
- Changed `D` to be of type real in `pcens_model.stan` in order to support infinite `relative_obs_time`.
- Switched to using `num_elements()` over `size()` in all stan code to resolve compilation issues on some platforms as reported by @sbfnk.

# primarycensored 1.0.0

This is the first major release of `primarycensored` and has been submitted to CRAN.

## Package

- Fix internal package misspelling of `primary_lpdf`.
- Move to "stable" lifecycle status.
- Added `rhub` checks to the `Github Actions` workflow.
- Added `dependencies: "hard"` to the `R-CMD-check` workflow to ensure checks pass without optional dependencies.
- Improved handling of examples that use optional dependencies.
- Check all URLs for redirects.
- Ensure that all functions have documented return values.

# primarycensored 0.6.0

This release renames the package to `primarycensored` from `primarycensoredist` and also renames many of the functions to remove the `dist` in their name. This was done to make the package name and the functions more consistent and to remove the need to use the `dist` suffix. It also aligns it with the new `PrimaryCensored.jl` package in our [Julia ecosystem](https://github.com/Epiaware).

Aside from name changes, this release also adds an analytical solution for the weibull distribution with uniform primary censoring, removes the need to assign functions to the global environment for `fitdistdoublecens()` by using `withr`, and adds a `check_truncation()` function to check if the truncation time is larger than the maximum observed delay. This is used in `fitdistdoublecens()` and `pcd_as_stan_data()` to ensure that the truncation time is appropriate to maximise computational efficiency.

## Package

* Removed the need to assign functions to the global environment for `fitdistdoublecens()` by using `withr`.
* Added a `check_truncation()` function to check if the truncation time is larger than the maximum observed delay. This is used in `fitdistdoublecens()` and `pcd_as_stan_data()` to ensure that the truncation time is appropriate to maximise computational efficiency.
* `pcd_as_cmdstan_data()` has been renamed to `pcd_as_stan_data()` to better reflect that it is used for `Stan` models in general rather than just the `CmdStan` models.
* The stan code has been refactored into a folder of functions within the current `stan` folder and the `stan` model has been moved into the `stan` folder. All paths to the stan code have been updated to reflect this.
* Added R and stan implementations of the primary censored cdf for the weibull distribution with uniform primary censoring.
* The package has been renamed to `primarycensored` as have all functions that use "dist" in their name.

## Documentation

* Simplified the "Analytic solutions" vignette by removing verbose derivation details.
* Added links between vignettes to make it easier to navigate the documentation.
* Added explicit usage of `pdist`, `dprimary`, `rdist`, and `rprimary` arguments in the getting started vignette to make it easier to link to mathematical details.
* Fixed error in "Analytic solutions" vignette where the Weibull density was not being treated as zero for negative delays.
* Split "Why it works" vignette into two separate vignettes, "Why it works" and "Analytic solutions for censored delay distributions".

# primarycensored 0.5.0

This release adds a new `{touchstone}` based benchmark suite to the package. It also adds a new "How it works" vignette which aims to give the reader more details into how the primary censored distributions work.

As part of the "How it works" we (@SamuelBrand1) found analytical solutions for the gamma, lognormal, and weibull distributions with uniform primary censoring. These are now implemented for the lognormal and gamma distributions in the `R` and `stan` code providing significant speedups to the fitting process (~10-20 times faster). The Weibull will be added in the next release.

## Package

* Add `{touchstone}` based benchmarks for benchmarking R utility functions, and fitting the `stan` and `fitdistplus` models.
* Added a "How it works" vignette.
* Added R infrastructure for analytical solutions via the `primarycensored` S3 class.
* Added Weibull analytical solution to "How it works" vignette.
* Added analytical solutions for the gamma and lognormal distributions with uniform primary censoring to both the `R` and `stan` code.
* Added numerical protection to ensure that CDFs for delays greater than the maximum truncation are exactly 1.

# primarycensored 0.4.0

In this release, we have added a new package `stan` model for fitting distributions using the `cmdstanr` package. We have also added a new function `fitdistdoublecens()` to allow for fitting of double censored and truncated data using the `fitdistrplus` package. As well as these functionality improvements this release focuses on improving the stability of the `stan` model and improving the speed of the `primarycensored_ode` function.

## Package

* Added a new function `fitdistdoublecens()` to allow for fitting of double censored and truncated data using the `fitdistrplus` package.
* Added low level tests for the Stan `primarycensored_ode` function.
* Rephrased the stan code to use a ODE solver rather than a numerical integration method. This allows for much faster and more stable computation of the likelihood
* Added a `CmdStan` model for fitting distributions using the `cmdstanr` package.
* Added helpers functions for working with the new `CmdStan` model and added an example to the vignette.
* Added parameter recovery tests for the new `CmdStan` model which tests the `primarycensored_lpmf` function when used with NUTS based fitting.

# primarycensored 0.3.0

This release fixes and improves truncation handling across the code base. It also adds a new vignette showcasing how to use the `primarycensored` and `fitdistrplus` packages together to fit distributions.

## Package

* Updated the approach to truncation to be outside the primary censored distribution integral.
* Improved tests that compare random sampling and probability mass/density functions between R and Stan.
* Improved cross-testing between R and Stan implementations of the primary censored distributions.
* Worked on improving the stability of the `primarycensored_lpmf` when used for NUTS based fitting (i.e. in Stan).

## Documentation

* @athowes improved the getting started vignette by catching a few grammar errors and simplifying language.
* Added a new vignette showcasing how to use the `primarycensored` and `fitdistrplus` packages together to fit distributions.

# primarycensored 0.2.0

This release puts in place initial documentation and vignettes. It also includes a new primary censored distribution interface to allow for non-secondary event censored distributions. Development of this release as identified some numerical issues in the
gradient evaluations for the primary censored distributions which may lead to breaking
interface changes in `0.3.0` for the Stan code.

## Package

* Added support for `swindow = 0` to `rprimarycensored` to allow for non-secondary event censored distributions.
* Adapted `rprimarycensored` so that truncation is based on the primary censored distribution before secondary events are censored. This better matches the generative process.
* Added a new Stan interface tool to enable finding which files functions are implemented in the Stan code.

## Documentation

* Added a getting started vignette.
* Added a vignette showcasing how to use the package Stan code with `cmdstanr`.
* Added a vignette showcasing how to fit distributions using the `cmdstanr` package.

# primarycensored 0.1.0

This is the initial `primarycensored` release and includes R and stan tools for dealing with potentially truncated primary event censored delay distributions. We expect all current features to work but the UI may change as the package matures over the next few versions.

## Package

* Added package skeleton.
* Added checking input functions.
* Added stan functions for primary censored and truncated distributions.
* Added R functions for primary censored and truncated distributions.
* Add R function to facilitate working with the Stan code.
* Added tests for primary censored and truncated distributions.
* Added tests to compare R and Stan implementations.
* Added tests for the R functions that facilitate working with the Stan code.
* Resolved R CMD check errors, warnings and notes.
* Added a hexsticker.
* Added vignette skeletons in preparation for `0.2.0` release.
