The hardware and bandwidth for this mirror is donated by dogado GmbH, the Webhosting and Full Service-Cloud Provider. Check out our Wordpress Tutorial.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]dogado.de.

Statistical Comparison of ROC Curves (pAUC Analysis)

Colin G. Tredoux

Tamsyn M. Naylor

2026-11-02

Acknowledgement. Large language models supplied by Anthropic and OpenAI were used in preparing this vignette, to assist with checking code, testing the veracity of the approaches described, and on occasion editing the text. Responsibility for the content rests with the authors.

library(r4lineups)
library(ggplot2)

Introduction

Eyewitness researchers frequently need to decide whether one identification procedure is better than another: whether simultaneous lineups beat sequential ones, whether a longer retention interval hurts, whether lineup size is important.

Since about 2012 the dominant way of answering such questions has been to compare ROC curves, and usually to reduce each curve to a single number — the partial area under it (pAUC) — and test whether the two numbers differ. This vignette shows how to do that with compare_pauc(). It also explains what the curves and the areas mean, and why the approach is contested.

What is an ROC curve?

Consider a lineup procedure and a set of witnesses, some viewing lineups containing the culprit (target-present) and some viewing lineups containing an innocent suspect instead (target-absent). Two things can happen that we care about:

Any procedure can be made to produce more correct identifications simply by encouraging witnesses to choose more freely — but that also produces more false identifications. The two rates move together, which is exactly why comparing procedures on identification rate alone is uninformative.

The ROC curve addresses this by plotting one against the other as we vary how demanding we are about accepting an identification. In eyewitness work the lever is confidence: count only identifications made with the highest confidence, and both rates are low; progressively admit lower-confidence identifications and both rates rise. Each confidence threshold gives one point, and joining them gives the curve.

set.seed(2026)

strong <- as.data.frame(simulate_lineup_data(
  n_tp = 400, n_ta = 400, d_prime = 2.0, c_criterion = 0.9,
  lineup_size = 6, conf_levels = 5,
  foil_bias = c(0.4, 0.2, 0.0, -0.4, -1.0), seed = 1
))

roc_points <- make_rocdata(strong, lineup_size = 6)$roc_data

knitr::kable(
  roc_points[, c("confidence", "n_correct_ids", "n_false_ids",
                 "correct_id_rate", "false_id_rate")],
  digits = 4, row.names = FALSE,
  caption = "ROC points for one simulated condition (400 target-present and 400 target-absent lineups, d' = 2.0). Each row is a confidence threshold: rows lower down admit less confident identifications."
)
ROC points for one simulated condition (400 target-present and 400 target-absent lineups, d’ = 2.0). Each row is a confidence threshold: rows lower down admit less confident identifications.
confidence n_correct_ids n_false_ids correct_id_rate false_id_rate
6 0 0 0.0000 0.0000
5 81 1 0.2025 0.0025
4 135 3 0.3375 0.0075
3 202 12 0.5050 0.0300
2 251 30 0.6275 0.0750
1 286 45 0.7150 0.1125

Reading down the table: taking only identifications at the highest confidence gives a correct identification rate of about 0.2 against a false identification rate of about 0.002. Admitting every identification regardless of confidence raises the correct rate to about 0.72 but the false rate to about 0.11.

A curve that bows further towards the top-left corner — more correct identifications for the same number of false ones — indicates a better procedure.

Discriminability and criterion

The ROC curve exists to separate two things that identification rates confound.

Discriminability is the witness’s ability to tell the culprit apart from innocent lineup members. It is a property of memory and of the viewing conditions, and it is what the shape and position of the ROC curve reflect. A procedure that genuinely improves memory moves the whole curve outward.

Criterion is how willing a witness is to identify anyone at all. A cautious witness identifies rarely; a willing one identifies often. Changing the criterion moves you along a curve without changing which curve you are on.

The distinction is important because a procedure can change identification rates without improving anything. If sequential lineups simply make witnesses more cautious, they will produce fewer false identifications and fewer correct ones, and a comparison of raw rates will make them look better or worse depending on which rate the reader happens to weight. The ROC curve answers the question that survives a criterion shift: at the same false identification rate, which procedure gets more correct identifications?

This is the argument Wixted and Mickes (2012) made in urging the field to abandon the diagnosticity ratio in favour of ROC analysis, and it is why ROC analysis became standard.

Why lineup ROCs are hard

A textbook ROC curve runs from (0,0) to (1,1). Lineup ROC curves do not, and this is the source of most of the difficulty.

roc_df <- roc_points[order(roc_points$false_id_rate), ]

ggplot(roc_df, aes(x = false_id_rate, y = correct_id_rate)) +
  geom_abline(slope = 1, intercept = 0, linetype = "dotted", colour = "grey60") +
  geom_line(linewidth = 1, colour = "steelblue") +
  geom_point(size = 2.5, colour = "steelblue") +
  annotate("segment",
           x = max(roc_df$false_id_rate), xend = 1,
           y = max(roc_df$correct_id_rate), yend = 1,
           linetype = "dashed", colour = "firebrick") +
  annotate("text", x = 0.55, y = 0.72, colour = "firebrick", hjust = 0,
           label = "no data here:\nwitnesses never choose\nthis freely") +
  coord_equal(xlim = c(0, 1), ylim = c(0, 1)) +
  labs(
    x = "False identification rate",
    y = "Correct identification rate",
    title = "A lineup ROC curve occupies only a corner of the space",
    subtitle = "Simulated data, 400 target-present and 400 target-absent lineups, d' = 2.0"
  ) +
  theme_minimal(base_size = 12)

The curve stops at a false identification rate of about 0.11. It stops there because that is what happens when every identification is counted, however hesitant. There is no way to push further to the right: doing so would require witnesses to identify someone more often than they are willing to, and the experiment provides no lever for that.

Two consequences follow, and both are important:

  1. The full area under the curve cannot be computed without inventing the part of the curve that was never observed. Anything that extrapolates to (1,1) — the dashed red line above — is making up data.
  2. Different procedures stop at different places. A procedure that makes witnesses more willing to choose produces a longer curve. The curves being compared therefore cover different intervals.

From AUC to pAUC

The standard response is to give up on the full area and compute the area only over the region that was actually observed: the partial AUC.

cutoff <- max(roc_df$false_id_rate)
shade <- rbind(
  data.frame(false_id_rate = 0, correct_id_rate = 0),
  roc_df[, c("false_id_rate", "correct_id_rate")],
  data.frame(false_id_rate = cutoff, correct_id_rate = 0)
)

ggplot(roc_df, aes(x = false_id_rate, y = correct_id_rate)) +
  geom_polygon(data = shade, fill = "steelblue", alpha = 0.25) +
  geom_line(linewidth = 1, colour = "steelblue") +
  geom_point(size = 2.5, colour = "steelblue") +
  labs(
    x = "False identification rate",
    y = "Correct identification rate",
    title = "Partial AUC is the shaded area, over the observed region only",
    subtitle = "Simulated data, d' = 2.0. Note the x-axis stops well short of 1"
  ) +
  theme_minimal(base_size = 12)

Partial AUC is defended on four grounds, which are taken separately here because they are not equally strong:

  1. It is computable. Unlike full AUC, it requires no extrapolation into territory the data never reached. This is the serious argument.
  2. Policy relevance. Very high false identification rates are not operating points any justice system would accept, so the region omitted is arguably the region nobody cares about.
  3. Stability. Estimates over the well-populated region are less variable than estimates that depend on the sparse tail.
  4. Comparability. If two curves are integrated over the same region, the comparison is at least like-for-like.

Point 4 carries a condition that is easy to violate, and the next section is about what happens when it is.

Critical commentary: pAUC is contested

It would be misleading to present pAUC as settled method. It is not, and the objections are substantive.

The region problem

Partial AUC requires a region, and with two curves of different lengths there is no choice of region that is obviously right.

This is not hypothetical. compare_pauc() defaults to the largest region both curves attain, which is the second option above, and it says so when the two differ:

weak <- as.data.frame(simulate_lineup_data(
  n_tp = 400, n_ta = 400, d_prime = 1.4, c_criterion = 0.9,
  lineup_size = 6, conf_levels = 5,
  foil_bias = c(0.4, 0.2, 0.0, -0.4, -1.0), seed = 2
))

reach_strong <- max(make_rocdata(strong, lineup_size = 6)$roc_data$false_id_rate)
reach_weak   <- max(make_rocdata(weak,   lineup_size = 6)$roc_data$false_id_rate)

knitr::kable(
  data.frame(
    Condition = c("Strong (d' = 2.0)", "Weak (d' = 1.4)"),
    `Maximum false-ID rate reached` = round(c(reach_strong, reach_weak), 4),
    check.names = FALSE
  ),
  row.names = FALSE,
  caption = "The two simulated conditions stop at different points, so there is no single region both fully cover."
)
The two simulated conditions stop at different points, so there is no single region both fully cover.
Condition Maximum false-ID rate reached
Strong (d’ = 2.0) 0.1125
Weak (d’ = 1.4) 0.1150

The deeper objection

Smith, Lampinen, Wells, Smalarz and Mackovichova (2019) go further and argue that pAUC does not measure what researchers want it to measure. Their claim is that pAUC fails to track the diagnostic utility of a lineup — the thing that is actually important for deciding which procedure a police force should adopt — and that neither choice of region repairs this. They propose deviation from perfect performance (DPP) instead, which is implemented in this package and covered in vignette("calibration_decision_analysis").

What the curve leaves out

A further criticism concerns what enters the analysis at all. The ROC curve above is built from suspect identifications only. A witness who identifies a foil has made an error that the analysis largely sets aside, and a witness who rejects the lineup contributes nothing to either axis.

Yet foil identifications are informative: they say something about how well the lineup was constructed, and a procedure that converts suspect identifications into foil identifications has genuinely protected an innocent suspect. Lineup rejections are informative too. The full ROC approach, which uses every response, is one answer to this; see vignette("fullroc_analysis").

The point is not that pAUC should be abandoned, but that a single number computed over a contested region from a subset of the responses is carrying more weight in this literature than it can comfortably bear. Reporting it alongside DPP, and alongside the full response distribution, is a more defensible practice than reporting it alone.

Key articles

Reference Contribution
Wixted & Mickes (2012) The case for ROC analysis, and against the diagnosticity ratio, in eyewitness research
Gronlund, Wixted & Mickes (2014) Accessible account of applying ROC analysis to identification procedures
Smith et al. (2019) The case against pAUC: it does not track diagnostic utility. Proposes DPP
Mickes et al. (2024) pyWitness, the reference implementation this package’s ROC code was checked against

The statistical framework

What the test actually is

compare_pauc() computes

\[Z = \frac{\widehat{pAUC}_1 - \widehat{pAUC}_2}{\widehat{SE}(\widehat{pAUC}_1 - \widehat{pAUC}_2)}\]

and refers \(Z\) to a standard normal distribution.

It is important to make a point of clarification here about what kind of test this is, because it is easy to assume it is something it is not. It is a Wald test: an estimate divided by an estimate of its own standard error. It is not a comparison of two standard normal deviates, and it is not a test on transformed proportions. The normal reference distribution comes from the asymptotic normality of the estimator, not from any assumption that the underlying data are normal.

The standard error in the denominator is obtained by bootstrap: the witnesses in each condition are resampled with replacement, the whole ROC and its partial area are recomputed for each resample, and the standard deviation of the resulting differences is taken as \(\widehat{SE}\).

Why bootstrap the standard error?

Because no usable closed-form expression exists. The partial area is a non-linear functional of a step function estimated from two correlated sets of counts, computed over a region that is itself determined by the data. Deriving its sampling variance analytically is not practical; resampling estimates it directly and makes no distributional assumption in doing so.

Is normality reasonable?

The Wald test assumes the difference in pAUC is approximately normally distributed. That can be checked, rather than assumed:

# Stored with the vignette; set R4LINEUPS_PRECOMPUTE=true to recompute.
comparison <- compare_pauc(
  strong, weak,
  lineup_size = 6,
  label1 = "Strong (d' = 2.0)",
  label2 = "Weak (d' = 1.4)",
  n_bootstrap = 600,
  seed = 123
)
boot_diff <- comparison$bootstrap_results$diff_boot

knitr::kable(
  data.frame(
    Quantity = c("Mean", "SD", "Skewness", "Excess kurtosis",
                 "Shapiro-Wilk p"),
    Value = c(
      round(mean(boot_diff), 5),
      round(sd(boot_diff), 5),
      round(mean((boot_diff - mean(boot_diff))^3) / sd(boot_diff)^3, 3),
      round(mean((boot_diff - mean(boot_diff))^4) / sd(boot_diff)^4 - 3, 3),
      signif(shapiro.test(boot_diff)$p.value, 3)
    )
  ),
  row.names = FALSE,
  caption = "Shape of the bootstrap distribution of the pAUC difference, simulated data (600 bootstrap resamples)."
)
Shape of the bootstrap distribution of the pAUC difference, simulated data (600 bootstrap resamples).
Quantity Value
Mean 0.01547
SD 0.00674
Skewness -0.04200
Excess kurtosis 0.54700
Shapiro-Wilk p 0.08370
ggplot(data.frame(diff = boot_diff), aes(x = diff)) +
  geom_histogram(aes(y = after_stat(density)), bins = 40,
                 fill = "steelblue", colour = "white", alpha = 0.8) +
  stat_function(fun = dnorm,
                args = list(mean = mean(boot_diff), sd = sd(boot_diff)),
                colour = "firebrick", linewidth = 1) +
  geom_vline(xintercept = 0, linetype = "dotted") +
  labs(
    x = "pAUC difference (strong - weak)",
    y = "Density",
    title = "Bootstrap distribution of the pAUC difference",
    subtitle = "Simulated data. Red curve = normal with the same mean and SD; dotted line = no difference"
  ) +
  theme_minimal(base_size = 12)

Typically the distribution is close to normal but not exactly so — mild skew is common, because pAUC is bounded and the region is data-determined. With several hundred resamples a formal normality test will often reject even when the departure is too small to be important. The practical question is not whether normality holds exactly but whether it holds well enough that the interval is about right, and that is best answered by comparing the two intervals directly:

knitr::kable(
  data.frame(
    Method = c("Wald (normal approximation)", "Bootstrap percentile"),
    Lower = round(c(comparison$ci_diff[["lower"]],
                    unname(quantile(boot_diff, 0.025))), 5),
    Upper = round(c(comparison$ci_diff[["upper"]],
                    unname(quantile(boot_diff, 0.975))), 5)
  ),
  row.names = FALSE,
  caption = "95% confidence intervals for the pAUC difference by two methods, simulated data."
)
95% confidence intervals for the pAUC difference by two methods, simulated data.
Method Lower Upper
Wald (normal approximation) 0.00431 0.03072
Bootstrap percentile 0.00230 0.02886

When the two agree closely, as here, the normal approximation is doing no harm. When they diverge, prefer the percentile interval: it inherits whatever shape the bootstrap distribution actually has, rather than imposing symmetry on it.

A worked comparison

print(comparison)
#> 
#> === pAUC Comparison Analysis ===
#> 
#> Conditions:
#>   Strong (d' = 2.0): pAUC = 0.0617 (SE = 0.0051)
#>   Weak (d' = 1.4): pAUC = 0.0442 (SE = 0.0041)
#> 
#> Difference: 0.0175
#>   95% CI: [0.0043, 0.0307]
#> 
#> Statistical Test:
#>   Z = 2.6
#>   p-value = 0.009329
#> 
#> Interpretation: Strong (d' = 2.0) has higher discriminability than Weak (d' = 1.4) (**)
#> 
#> Max false ID rate cutoff: 0.1125
#> Bootstrap samples: 600
#> 
#> Note: *** p<0.001, ** p<0.01, * p<0.05, ns = not significant
plot(comparison, show_cutoff = TRUE, show_test_results = TRUE)

summary(comparison)
#> 
#> === pAUC Comparison Summary ===
#> 
#> Condition 1: Strong (d' = 2.0) 
#>   Sample size: 800 
#>   Target-present: 400 
#>   Target-absent: 400 
#>   pAUC: 0.0617 
#>   SE(pAUC): 0.0051 
#>   95% CI: [0.0518, 0.0717]
#> 
#> Condition 2: Weak (d' = 1.4) 
#>   Sample size: 800 
#>   Target-present: 400 
#>   Target-absent: 400 
#>   pAUC: 0.0442 
#>   SE(pAUC): 0.0041 
#>   95% CI: [0.0361, 0.0523]
#> 
#> Difference (Strong (d' = 2.0) - Weak (d' = 1.4)):
#>   Estimate: 0.0175 
#>   SE(diff): 0.0067 
#>   95% CI: [0.0043, 0.0307]
#>   Z-score: 2.6 
#>   p-value: 0.009329 
#> 
#> SE-standardized difference (depends on sample size; not Cohen d): 3.781 
#> 
#> Analysis Details:
#>   Max false ID rate cutoff: 0.1125 
#>   Bootstrap samples: 600 
#>   Confidence level:95%

Specifying the region explicitly

The default region is the largest both curves reach. A policy-relevant cutoff can be imposed instead, provided it is one both curves actually attain:

comparison_10 <- compare_pauc(
  strong, weak,
  lineup_size = 6,
  max_false_id_rate = 0.10,
  label1 = "Strong", label2 = "Weak",
  n_bootstrap = 300,
  seed = 456
)

knitr::kable(
  data.frame(
    Region = c(sprintf("Default (to %.3f)", comparison$max_false_id_rate),
               "Policy cutoff (to 0.100)"),
    `pAUC difference` = round(c(comparison$pauc_diff,
                                comparison_10$pauc_diff), 5),
    `p-value` = signif(c(comparison$p_value, comparison_10$p_value), 3),
    check.names = FALSE
  ),
  row.names = FALSE,
  caption = "The same two conditions compared over two different regions, simulated data."
)
The same two conditions compared over two different regions, simulated data.
Region pAUC difference p-value
Default (to 0.113) 0.01752 0.00933
Policy cutoff (to 0.100) 0.01527 0.00212

The estimates differ because they are areas over different intervals. This is not an inconsistency to be resolved but the region problem in miniature: the region is part of the estimand, and reporting a pAUC without stating the region it covers is incomplete.

How many bootstrap resamples?

The bootstrap standard error is itself an estimate, and too few resamples make it unstable. For exploratory work a few hundred is adequate; anything intended for publication should use 2000 or more. This vignette uses small numbers so that it builds quickly.

Multiple comparisons

Comparing three or more conditions pairwise inflates the family-wise error rate in the usual way:

medium <- as.data.frame(simulate_lineup_data(
  n_tp = 400, n_ta = 400, d_prime = 1.7, c_criterion = 0.9,
  lineup_size = 6, conf_levels = 5,
  foil_bias = c(0.4, 0.2, 0.0, -0.4, -1.0), seed = 3
))

pairs <- list(
  `Strong vs Medium` = list(strong, medium),
  `Strong vs Weak`   = list(strong, weak),
  `Medium vs Weak`   = list(medium, weak)
)
# Stored with the vignette; set R4LINEUPS_PRECOMPUTE=true to recompute.
results <- do.call(rbind, lapply(names(pairs), function(nm) {
  cmp <- compare_pauc(pairs[[nm]][[1]], pairs[[nm]][[2]],
                      lineup_size = 6, n_bootstrap = 200, seed = 41)
  data.frame(Comparison = nm,
             `pAUC difference` = round(cmp$pauc_diff, 5),
             Z = round(cmp$z_score, 3),
             p = signif(cmp$p_value, 4),
             check.names = FALSE)
}))
results$`p (Bonferroni)` <- signif(p.adjust(results$p, method = "bonferroni"), 4)

knitr::kable(
  results, row.names = FALSE,
  caption = "Pairwise pAUC comparisons with Bonferroni adjustment, simulated data (400 target-present and 400 target-absent lineups per condition)."
)
Pairwise pAUC comparisons with Bonferroni adjustment, simulated data (400 target-present and 400 target-absent lineups per condition).
Comparison pAUC difference Z p p (Bonferroni)
Strong vs Medium 0.00335 0.598 0.549600 1.00000
Strong vs Weak 0.01752 2.718 0.006571 0.01971
Medium vs Weak 0.01192 2.190 0.028510 0.08553

Bonferroni is the most conservative option and the easiest to defend; Holm is uniformly more powerful and equally easy to justify (p.adjust(method = "holm")). Better still, where the design allows it, is to nominate one primary comparison in advance and treat the rest as exploratory.

Power

Planning a study that will compare pAUC needs a power analysis, which simulate_power_analysis() provides — it uses compare_pauc() internally as its test. See vignette("simulation_power_analysis").

Technical Details

How pAUC is computed

The ROC points are the cumulative correct- and false-identification rates at each confidence threshold \(c\):

\[\text{CID}(c) = \frac{\#\{\text{suspect IDs in target-present lineups with confidence} \ge c\}}{N_{\text{target-present}}}\]

\[\text{FID}(c) = \frac{\#\{\text{suspect IDs in target-absent lineups with confidence} \ge c\}}{N_{\text{target-absent}}}\]

Where target-absent lineups contain no designated innocent suspect, foil identifications divided by lineup size are used to estimate the false identification rate instead.

The partial area over \([0, f_{\max}]\) is computed by the trapezoidal rule over the observed points:

\[\widehat{pAUC} = \sum_{i=2}^{k} \bigl(\text{FID}_i - \text{FID}_{i-1}\bigr) \cdot \frac{\text{CID}_i + \text{CID}_{i-1}}{2}\]

If \(f_{\max}\) falls between two observed points, the curve is linearly interpolated to that point. If \(f_{\max}\) lies beyond the last observed point, no extrapolation is performed: the sum simply stops at the data. Supplying a cutoff beyond a curve’s reach therefore produces a warning and an area truncated at the observed reach, which is smaller than the area up to the requested cutoff would be. Read the warning; it means the two areas being compared may not cover the same false-ID range.

How the standard error is obtained

For each of \(B\) bootstrap resamples, witnesses within each condition are resampled with replacement, preserving the target-present and target-absent sample sizes; the ROC and its partial area are recomputed; and the difference is recorded. Then

\[\widehat{SE}(\widehat{pAUC}_1 - \widehat{pAUC}_2) = \operatorname{sd}\bigl(\{\widehat{pAUC}_1^{(b)} - \widehat{pAUC}_2^{(b)}\}_{b=1}^{B}\bigr)\]

The two conditions are resampled independently, which is correct for a between-subjects design and gives \(\widehat{SE}^2 \approx \widehat{SE}_1^2 + \widehat{SE}_2^2\). It would be wrong for a within-subjects design in which the same witnesses contribute to both conditions, because the two areas would then be correlated and the independent resampling would overstate the standard error. This package provides no within-subjects version.

Assumptions

  1. The two conditions are independent samples of witnesses.
  2. The difference in pAUC is approximately normally distributed — checkable, as above, and avoidable by using the percentile interval.
  3. The region of integration is one both curves attain.
  4. Witnesses are independent of one another. If witnesses viewed multiple lineups, or lineups were constructed from a small pool of faces, this is violated and the standard error will be too small.

Effect size

summary() reports a standardised effect size formed by dividing the pAUC difference by a pooled bootstrap standard deviation. It is a useful relative summary, but it should not be read against Cohen’s conventions for \(d\): those were calibrated for standardised mean differences between raw observations, not for a difference between two areas scaled by a resampling standard deviation, and the two are not on comparable footings.

Summary

  1. ROC analysis separates discriminability from criterion, which is why it displaced the diagnosticity ratio.
  2. Lineup ROC curves are truncated — they occupy a corner of the ROC space — which is why the partial area is used.
  3. The region of integration is part of the estimand. Always report it.
  4. The test is a Wald test with a bootstrap standard error. Check the bootstrap distribution; prefer the percentile interval if it is skewed.
  5. pAUC is contested. Smith et al. (2019) argue it does not track diagnostic utility, and the analysis discards foil identifications and rejections. Report it alongside DPP and the full response distribution rather than alone.

References

Gronlund, S. D., Wixted, J. T., & Mickes, L. (2014). Evaluating eyewitness identification procedures using receiver operating characteristic analysis. Current Directions in Psychological Science, 23(1), 3-10.

Mickes, L., Seale-Carlisle, T. M., Chen, X., & Boogert, S. (2024). pyWitness 1.0: A python eyewitness identification analysis toolkit. Behavior Research Methods, 56, 1533-1550.

Smith, A. M., Lampinen, J. M., Wells, G. L., Smalarz, L., & Mackovichova, S. (2019). Deviation from perfect performance measures the diagnostic utility of eyewitness lineups but partial area under the ROC curve does not. Journal of Applied Research in Memory and Cognition, 8(1), 50-59.

Wixted, J. T., & Mickes, L. (2012). The field of eyewitness memory should abandon probative value and embrace receiver operating characteristic analysis. Perspectives on Psychological Science, 7(3), 275-278.

Further Reading

These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.
Health stats visible at Monitor.