# phontrast 2.3.1

## CRAN resubmission fixes

- Replaced the non-running `extract_mfcc()` example with a fast, executable
  example that creates and removes a short WAV file in the R session's
  temporary directory. The optional `tuneR` dependency is guarded with
  `requireNamespace()` as recommended for packages in `Suggests`.
- Unwrapped the short `phontrast()` and `hier_boot_jsd_model()` bootstrap
  examples after confirming that they run well under five seconds. Reduced the
  illustrative `estimate_jsd()` bootstrap count so that example is also under
  five seconds and can be unwrapped. No examples now use `\dontrun{}` or
  `\donttest{}`.
- Removed all direct access to `.GlobalEnv`. Seeded KDE subsampling and
  multivariate-normal Monte Carlo now use a private deterministic generator,
  retaining reproducibility without reading, writing, or replacing the user's
  `.Random.seed`. Unseeded stochastic calls retain their previous behavior.

# phontrast 2.3.0

## Distribution-aware, accountable plotting

- **New flagship visualization `plot_contrast()`.** Draws the *same density
  model the distributional metrics are computed from*: under
  `density = "kde"` it shows highest-density regions of each category's kernel
  density estimate (same bandwidth selection and ks evaluation as the
  metrics); under `density = "mvnorm"` it shows coverage ellipses of the
  fitted Gaussians used by the parametric backend. The pointwise minimum of
  the two densities -- the mass the proportional-overlap metric integrates --
  is shaded directly (a ribbon in 1-D, a soft raster in 2-D, normalized across
  panels so fainter panels genuinely overlap less).
- **Accountability by default.** Panels are annotated with the Jensen-Shannon
  divergence and proportional overlap computed by `phontrast()` under the
  plotted density model (optionally with bootstrap intervals via `n_boot`);
  the caption records the estimator configuration (backend, bandwidth,
  `mc_n`, seed, n); and the full annotation table is attached to the plot as
  `attr(p, "contrast_metrics")`.
- **Results plot themselves.** `phontrast()` output now carries class
  `"phontrast_contrast"`, so `plot()` and `ggplot2::autoplot()` draw the
  metric comparison directly via `plot_overlap_metrics()`.
- **A shared visual identity.** New exported `theme_phontrast()`,
  `scale_colour_phontrast()` / `scale_color_phontrast()` /
  `scale_fill_phontrast()`, and `phontrast_palette()` (a colorblind-safe
  Okabe-Ito palette ordered so the leading pair maximizes contrast for
  two-category plots). All plotting functions -- including the existing
  `plot_overlap_metrics()`, `plot_category_space()`, and
  `plot_category_pca()`, whose arguments and behavior are otherwise
  unchanged -- now share this theme and palette.
- Plotting remains optional: `ggplot2` stays in Suggests.

# phontrast 2.2.0

## Pluggable density backend for the distributional metrics

- **Added a `density` argument to decouple the density *estimator* from the
  *metric*.** The distributional metrics (Jensen-Shannon divergence and
  proportional overlap) previously always used kernel density estimation. They
  now accept `density = "kde"` (the default, unchanged) or `density = "mvnorm"`,
  which fits one multivariate normal per category and estimates the metric
  between the two Gaussians. This lets the estimator behind JSD and overlap be
  matched to the same multivariate-normal assumptions the Pillai, Bhattacharyya,
  and Mahalanobis metrics already make, and makes the metric x estimator
  interaction a controlled choice rather than hard-wired to KDE.
- Jensen-Shannon divergence between two Gaussians has no closed form (the
  mixture is a Gaussian mixture), so the `"mvnorm"` backend estimates it by
  **fresh-sample Monte-Carlo**: it draws `mc_n` points (default `10000`) from
  each fitted Gaussian and averages the log density ratio. The estimand is the
  JSD / overlap between the fitted Gaussians; `eval_seed` makes the draw
  reproducible without disturbing the caller's random-number stream. The
  Gaussian fit has no self-kernel, so the KDE-specific leave-one-out correction
  does not apply.
- `density` (and `mc_n`) are threaded through `phontrast()`,
  `compare_overlap_metrics()`, `estimate_jsd()`, `estimate_overlap()`,
  `jsd_summary()`, `global_boot_jsd()`, `jsd_kde_nd()`, and
  `percent_overlap_kde()`. In `phontrast()` the argument affects only the
  Jensen-Shannon and overlap columns; the Pillai, Bhattacharyya, and
  Mahalanobis columns are parametric by construction and are unchanged.
- The multivariate-normal log-density and sampler are implemented in base R via
  a Cholesky solve, adding no new package dependency. Rank-deficient category
  covariances are regularized with a small ridge before factorization.
- Fully backward compatible: the default remains `density = "kde"` and all
  existing results are unchanged.

# phontrast 2.1.0

## Monte-Carlo JSD no longer floors small divergences to exactly 0

- **Fixed `method = "mc"` (the default KDE JSD estimator) flooring small but
  real divergences to exactly 0.** The full leave-one-out correction could
  collapse a category's self-density at isolated points, driving the raw
  plug-in mean negative, which the final clamp then floored to 0 -- while
  `method = "legacy"` still reported a nonzero contrast. The estimator now uses
  a *partial* leave-one-out correction that removes a sample-size-scaled
  fraction `n / (n + 20)` of each point's own kernel: half at 20 tokens per
  category (the `min_tokens` default), approaching the full correction as the
  category grows. The corrected density stays strictly positive, so
  near-merged categories yield small positive estimates instead of exact 0.
- Validated against grid-integrated JSD of the same KDEs: on 44 real
  speaker contrasts (20 tokens per phase, 2-D formant space) the previous
  estimator returned exactly 0 for 24 speakers; the corrected one returns 0
  for none, tracks the grid reference more closely than either the previous
  default or `method = "legacy"`, and still passes the package's
  grid-calibration test at n = 200 within the original tolerance.
- **This changes `method = "mc"` results relative to 2.0.x**, most visibly for
  small divergences and small samples (estimates that were floored at 0 become
  small positive values; others typically shift upward slightly).
  `method = "legacy"` is unchanged, and `loo = FALSE` is unchanged.

## CRAN preparation

- Replaced the relative `ROADMAP.md` link in the README with a plain reference,
  since `ROADMAP.md` is excluded from the built package; this resolves the
  "invalid file URI" flagged by the CRAN incoming checks.

# phontrast 2.0.2

## CRAN preparation

- Kept non-package content out of the built tarball: the (untracked) local
  `OSF/` reproducibility-data directory and the top-level `ROADMAP.md` are now
  listed in `.Rbuildignore`, resolving the non-portable-paths and
  non-standard-top-level-files NOTEs from `R CMD check`. Added "mel" to
  `inst/WORDLIST`.
- Trimmed the `estimate_jsd()` example to a fast point estimate and moved the
  bootstrap demonstrations into `\donttest{}`, keeping every example under
  CRAN's execution-time limit.

# phontrast 2.0.1

## CRAN preparation

- Prepared the package for CRAN submission: expanded acronyms and added a
  Jensen-Shannon divergence reference (Lin, 1991) to the `Description`,
  normalized non-ASCII characters in the R sources, added `inst/WORDLIST` and
  `cran-comments.md`, and removed the AI assistant from `Authors@R` (the AI-use
  acknowledgment remains in the README). No user-facing code changes.

# phontrast 2.0.0

## Package renamed: phonJSD is now phontrast

- **The package has been renamed from `phonJSD` to `phontrast`** and reoriented
  around comparing *multiple* category contrast and separation metrics rather
  than Jensen-Shannon divergence alone. Update your code from `library(phonJSD)`
  to `library(phontrast)`. Function names are unchanged except as noted below,
  and no metric estimates change relative to 1.2.0 -- this release is a rename
  and API reframe, not a numerical change.
- The GitHub repository moved to <https://github.com/berrygrant/phontrast> (old
  links redirect), and the Zenodo concept DOI (10.5281/zenodo.20816585) is
  unchanged, so existing citations continue to resolve.

## New unified entry point: `phontrast()`

- Added **`phontrast()`**, the package's headline function: compute and compare
  any subset of the contrast metrics -- Jensen-Shannon divergence and distance,
  Pillai-Bartlett trace, Bhattacharyya distance and affinity, Mahalanobis
  distance, and proportional overlap -- for a two-category contrast in one call,
  globally or by group, wide or tidy long, with optional bootstrap intervals.
- The new `metrics` argument selects which metrics to compute (default: all),
  e.g. `phontrast(data, features, "vowel", metrics = c("jsd", "pillai"))`.
- **`compare_overlap_metrics()` is deprecated** in favor of `phontrast()`. It
  still works (it calls `phontrast()` with `output = "wide"`) and emits a
  deprecation message; it will be removed in a future release.

## Roadmap

- The next priority (P1) is a full architectural redesign around a metric
  registry with a uniform per-metric interface, a generalized bootstrap that
  works for any metric, and first-class metric orientation. See `ROADMAP.md`.

# phonJSD 1.2.0

## Corrected KDE estimator (changes results)

- **KDE-based JSD and percent overlap now use a consistent Monte-Carlo plug-in
  estimator by default (`method = "mc"`).** Each category's KDE is evaluated at
  that category's own observations and the true log density ratio against the
  mixture is averaged (with a leave-one-out bias correction, `loo = TRUE`). This
  estimates the continuous Jensen-Shannon divergence in any dimension, replacing
  the previous self-normalized sample-point index, which was a bounded relative
  separation measure rather than the JSD integral and depended on `eval_on`.
- **This changes the numbers relative to phonJSD 1.0.0.** To reproduce 1.0.0
  results exactly, pass `method = "legacy"` to `jsd_kde_nd()`,
  `percent_overlap_kde()`, `estimate_jsd()`, `estimate_overlap()`,
  `jsd_summary()`, `global_boot_jsd()`, or `compare_overlap_metrics()`. The
  `eval_on` control applies to `method = "legacy"` only.
- The `fast_diag` engine now evaluates true (normalized) densities, matching
  `ks::kde()` to machine precision for diagonal bandwidths.

## API

- Aligned defaults across the estimation API: `min_tokens = 20` and
  `n_boot = 1000` everywhere (previously `estimate_jsd()` defaulted to
  `min_tokens = 5`, and `jsd_summary()`/`boot_jsd()`/`hier_boot_jsd_model()` to
  `min_tokens = 30` / `n_boot = 300`).
- Standardized the `estimate_*`/`global_*` wrappers to return tibbles uniformly.
- Documented the `pillai_p_value` (wide) and `p_value` (long) columns returned
  by `compare_overlap_metrics()`.

## Metrics

- Added opt-in high-dimensional KDE speed controls: `bw = "scott.diag"`,
  evaluation-point subsampling via `eval_n`/`eval_seed`, and
  `engine = "fast_diag"` for chunked diagonal-Gaussian KDE evaluation.
- Added `engine = "fast_diagonal"` as an alias for `engine = "fast_diag"`.
- Extended the same KDE controls across JSD, percent overlap, and
  `compare_overlap_metrics()` so separation and overlap estimates use aligned
  density-estimation settings.
- Exposed the same KDE controls in lower-level JSD wrappers including
  `speaker_jsd()`, `boot_jsd()`, `jsd_summary()`, and `global_boot_jsd()`.
- Allowed grouped metric wrappers to use multiple grouping columns via
  `group_col = c("Sex", "Style")`; grouped outputs retain a single labeled
  `group` column.
- Switched `extract_mfcc()` to `tuneR::melfcc()` and removed the stale
  `seewave::mfcc()` reference.

## Documentation

- Documented the high-dimensional fast KDE path in the README and generated
  function manuals.
- Updated the GitHub Actions checkout step to a current Node runtime action.

## Bug fixes

- Clamped `jsd()` output to the mathematical range `[0, 1]`, so floating-point
  rounding on near-identical categories can no longer yield `NaN`
  Jensen-Shannon distances (`est_distance = TRUE`) or abort
  `hier_boot_jsd_model()` through `prepare_jsd_beta()`.
- Fixed a latent `sample()` edge case in `hier_boot_jsd_model()` that could
  misdraw a single numeric group identifier.

## Robustness

- Grouped metric wrappers now keep a group as `NA` when its metric cannot be
  computed and emit a single summarizing warning, instead of the previous
  inconsistent behavior where `speaker_pillai()`/`speaker_bhatt()` silently
  dropped failed groups (so `estimate_pillai()` and `estimate_jsd()` could
  return different rows for the same data) while other metrics returned a silent
  `NA`.
- Added input validation at the main entry points: `data` must be a data frame,
  `category_col` a single column name, `features` a non-empty character vector,
  and `features` may not overlap with `category_col`/`group_col`.

## Package quality

- Removed the unused `LazyData` field (no `data/` directory), the unused `lme4`
  suggestion, and dead `dplyr` imports (`filter`, `n`, `ungroup`); added `URL`
  and `BugReports`; standardized the author name to "Grant M. Berry"; and scoped
  the CI workflow to the existing `main` branch.

# phonJSD 1.0.0

## Visualization

- Added ggplot2-backed `plot_overlap_metrics()` and `plot_category_space()`
  helpers for visualizing metric comparisons and one- or two-dimensional
  phonological category spaces.
- Added `plot_category_pca()` for two-dimensional PCA diagnostics of arbitrary
  multidimensional feature spaces.

## Metrics

- Added `compare_overlap_metrics()` to compute Pillai trace, Bhattacharyya
  distance and affinity, Jensen-Shannon divergence and distance, Mahalanobis
  distance, and percent overlap in one global or grouped comparison table.
- Added one-dimensional KDE support for JSD and percent-overlap estimates.
- Aligned KDE bandwidth/evaluation controls across JSD and percent overlap.
- Made grouped JSD bootstrap intervals respect `conf_level`.
- Improved empty grouped outputs and small-sample diagnostics across metric
  wrappers.
- Made two-category metric checks ignore unused factor levels after filtering,
  so filtered factor data such as PB52 `I/i` contrasts work without manually
  calling `droplevels()`.
- Clarified that percent-overlap outputs are 0--1 proportions, not 0--100
  percentages.

## Documentation

- Added quick-start and multidimensional-workflow vignettes.
- Added a README quick start that starts from a vowel token table and moves to
  metric output.
- Added metric-choice guidance and PB52 small-sample notes.
- Added preferred-path and metric-direction guidance to the package manual.
- Added runnable examples for the main metric comparison, JSD estimation,
  direct KDE JSD, beta-regression preparation, MFCC extraction, and
  hierarchical bootstrap modeling workflows.
- Standardized JSD bootstrap outputs with `conf_level`, `ci_lower`, and
  `ci_upper` columns while retaining `jsd_low` and `jsd_high` as aliases.
- Added optional bootstrapping to `compare_overlap_metrics()` with progress
  messages and metric-specific confidence intervals.

## Maintenance

- Aligned DESCRIPTION metadata with the v1.0.0 README/DOI release.
- Added explicit Author and Maintainer fields for source-package checks on
  current R tooling.
- Removed old draft analysis scripts from the main package branch; the final
  LabPhon 2026 poster and reproducibility bundle live on `labphon_2026`.

# phonJSD 0.5.0

## Package Quality

- Added testthat coverage for the discrete JSD core, grouped classical metrics,
  KDE-based JSD wrappers, and grouped bootstrap summaries.
- Added `.Rbuildignore` entries so local analysis, load-test, and manuscript
  artifacts are excluded from package builds.
- Fixed the MIT license stub expected by R package tooling.

## Metrics

- Centralized column validation, complete-case filtering, and two-category
  checks across metric functions.
- Corrected discrete KL/JSD handling of zero-probability events.
- Made grouped bootstrap JSD report the number of successful bootstrap
  replicates.
- Replaced bootstrap `replicate()` control-flow edge cases with explicit
  `vapply()` iteration.
- Removed deprecated tidyselect usage in grouped JSD summaries.

## Documentation

- Updated generated Rd documentation for KL/JSD and bootstrap JSD outputs.
- Updated README release metadata and feature summary for v0.5.0.
