---
title: "How genevintage works"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{How genevintage works}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r setup, include = FALSE}
knitr::opts_chunk$set(collapse = TRUE, comment = "#>", fig.width = 7, fig.height = 4.2)
library(genevintage)
```

`genevintage` answers two questions about a set of gene identifiers: which
species, and which release. Both are answered from the identifiers alone, using
the bundled fingerprint index.

## Detecting species

`detect_species()` first works out what fraction of the input each candidate
species' identifier prefix explains:

```{r frac}
ids <- c(
  "ENSMUSG00000051951", "ENSMUSG00000089699", "ENSMUSG00000051951",
  "TP53"
)
cand <- detect_species(ids)
head(cand[, c("species", "prefix", "frac")])
```

Three of the four inputs (one a repeat) carry the `ENSMUSG` prefix, so every
mouse strain that shares it ties at `frac = 0.75`. Ties are broken in order by:

1. **Prefix length.** A longer matching prefix ranks higher.
2. **`reach`.** Could the species have issued the input's highest identifier
   number? The largest number in the input is compared with the largest `qmax`
   (the highest identifier number in a gene set) ever indexed for the species,
   within `QMAX_TOLERANCE`.
3. **`fit`.** How close is the input's gene count to a real gene set? It is the
   smallest log-scale gap between the number of unique IDs and any indexed
   release's gene count. It is only informative for a full gene set.

Any remaining tie is broken by species name, for a stable order; such species are
indistinguishable from the identifiers alone. `reach` and `fit` are used for
ranking only and are not returned.

## Determining the annotation release

`detect_release()` compares the input against the fingerprint index and rules
out releases that could not have produced it, using two facts:

1. Identifier numbers (usually) only go up, so the highest number in the input is a
   floor, i.e. no earlier release would have it
2. A gene's version only goes up too, so the highest version in the input is
   also a floor.

`QMAX_TOLERANCE` lets a release's `qmax` fall slightly short of the
input's highest number. Ensembl sometimes retires its top-numbered genes, which
lowers `qmax` without changing the assembly. `qmax_violations()` lists every
case in the shipped index:

```{r violations}
qmax_violations()
```

Where a violation exists, `detect_release()` skips the floor for that
species, since the check would fail against the species' own history.

Among the releases left feasible, distance ranks the rest:

```{r dist}
ids2 <- readLines(system.file("extdata", "example_ids.txt.gz",
  package = "genevintage"
))
head(detect_release(ids2), 3)
```

`dist` is the absolute difference in `log1p(gene count)` between the input
and each candidate release, so a thousand genes matter less in human (tens of
thousands of genes) than in a species with a few hundred. For species whose
identifier numbers are ordered, it adds the same comparison on the 90th
percentile of identifier numbers, which separates releases that share a gene
count but use different numbers.

Most non-Ensembl schemes carry no order in their identifier numbers. Such
schemes skip the numeric floor and the quantile term entirely:

```{r unordered}
head(detect_release(c("YAL001C", "YAL002W", "YAL003W"), species = "yeast"), 5)
```

## Handling GENCODE and Ensembl

Human and mouse are indexed against GENCODE as well as Ensembl, since their
RNA-seq is often quantified against GENCODE, whose gene set differs in size from
Ensembl's at the same release. `detect_release()` ranks across both and reports
which one each row came from in `source`.

## Caution around new releases

The index covers a fixed range of releases. If your best match is the oldest
or newest release in that range, treat it with caution. Your IDs may come from
a release just outside the range.
`detect_release()` warns when this happens.

```{r edge}
head(detect_release(c("ENSG00000141510", "ENSG00000012048"), species = "human"), 3)
```
