# `Scales.Labels`
[🔗](https://github.com/giggleton/scales/blob/v0.1.0/lib/scales/labels.ex#L1)

Labels for a scale's breaks: numbers written plainly (`plain/1`), in decimals (`number/2`),
with a comma between thousands (`comma/2`), as percentages (`percent/2`), as amounts of money
(`currency/2`), in scientific notation (`scientific/2`), in whichever way suits their size
(`number_auto/1`), as powers (`log/2`, or as text, `format_log/2`), as bytes (`bytes/2`), as
ordinals (`ordinal/2`), as p-values (`pvalue/2`), as spans of time (`timespan/2`), and dates,
date-times and times of day in a format (`date/2`, `time/2`) or by what changed from the break
before (`date_short/2`); and text broken into lines (`wrap/2`) or looked up in a dictionary
(`dictionary/2`).

Each labeller is handed every break an axis or a legend draws and returns a label for each, in
order, so the labels can share their decimals: `0.5`, `1` and `1.25` are labelled `0.50`,
`1.00` and `1.25`. A label is text, but for `log/2`'s powers (`t:power_label/0`). A missing
break, `nil`, has no label, `nil`, as R's `NA` has none, but where `number_auto/1` writes it
`"NA"`. One that takes options is handed on inside a function:

    fn breaks -> Scales.Labels.comma(breaks, accuracy: 0.1) end

Labellers are composed by piping one's labels into the next, where R composes them with
`compose_label()`:

    fn breaks ->
      breaks
      |> Scales.Labels.number(accuracy: 0.1)
      |> Enum.map(fn label -> label && label <> " m" end)
    end

`plain/1` writes numbers as Elixir shows them, with what an axis read at a glance needs on top;
the others write them as R's scales does, each a label to the character.

## How a label is written

Each number is multiplied by `scale:`, then rounded to a multiple of `accuracy:`, a half going
to the even neighbour (`2.5` to `2`, `3.5` to `4`). A number that rounds to zero has no sign.
One that `scale:` takes past the largest float is labelled `Inf` or `-Inf`, without
`prefix:` and `suffix:` where rounding takes it past (and inside them where only the scaling
does), and takes no part in the others' accuracy, cents or step.
It is written in plain decimals, with as many decimals as the accuracy has (`0.01` writes two,
`1` or more none) up to twenty, or more if a label needs them to show its first seven
significant digits (an accuracy of `0.25` writes `0.25` and `0.50`); every label shares the
most any needs. Only labels more than 310 characters longer than their scientific form, past
about 1e290, are written in scientific notation, as R writes them. `big_mark:` goes between
each three digits of the whole part
and `decimal_mark:` before the decimals; then `prefix:` and `suffix:` go around the number,
and the sign outside the prefix (`-$13.50`).

Left out, the accuracy is worked out from the breaks themselves: the power of ten at or below
the smallest gap between two of them when every break is a multiple of it, a tenth of it
otherwise, and never more than 1. One break, or breaks closer than about 1.5e-8, take an
accuracy of 1.

Every formatter takes the same options, listed with its own defaults in its documentation.

## Shortening by steps

`scale_cut:` divides each label by the largest step it reaches and writes the step's suffix
after the number, before `suffix:`: under `cut_short_scale/1`, 1500 is written `1.5K` and two
million `2M`. A label that is not a whole number of its step, but is a whole number of the step
below, is written in the step below when the two steps are not a power of ten apart: under
`cut_time_scale/1`, 90 seconds is `90s`, not `1.5m`. Labels in each step share their decimals,
worked out from that step's labels alone when `accuracy:` is not given. The steps must include
one at threshold 0, whose suffix is written after the labels below every other step, and no
threshold may be negative or repeated. `cut_short_scale/1`, `cut_long_scale/1`, `cut_si/1`
and `cut_time_scale/1` give the common sets.

# `power`

```elixir
@type power() :: {:power, String.t(), String.t()}
```

A power `log/2` labels a break with, its base and its exponent each written as text: `10^3`
is `{:power, "10", "3"}`.

# `power_label`

```elixir
@type power_label() :: power() | {:signed, String.t(), power()} | String.t() | nil
```

A label of `log/2`: a power, a power with a sign before it (`-10^3` is
`{:signed, "-", {:power, "10", "3"}}`), the text `"0"` for a signed zero, or `nil` for a
missing break.

# `step`

```elixir
@type step() :: {String.t(), number()}
```

A step for `scale_cut:`: the suffix written after a label, and the threshold it starts at.

# `bytes`

```elixir
@spec bytes([number() | nil], keyword()) :: [String.t() | nil]
```

Labels numbers of bytes, R's `label_bytes()`: in the unit that suits each, `1500` is `2 kB`, or
all in one unit.

Each label is written as `number/2` writes it, the unit its suffix. Under `:auto_si` and
`:auto_binary`, each break takes the largest unit it reaches, as `scale_cut:` does, and plain
bytes are `B`, a break that is not a whole number of its unit but is of plain bytes written in
bytes (`1500` is `1 500 B` under `:auto_binary`); in one unit, `scale:` is divided by the
unit's size. Labels are rounded to whole units unless `accuracy:` says otherwise.

## Options

* `:units` - `:auto_si`, each label in the SI unit, a power of 1000, that suits it; `:auto_binary`, in the binary unit, a power of 1024; or a unit every label is written in, spelt as R spells it: `"kB"`, `"MB"`, `"GB"`, `"TB"`, `"PB"`, `"EB"`, `"ZB"`, `"YB"`, `"kiB"`, `"MiB"`, `"GiB"`, `"TiB"`, `"PiB"`, `"EiB"`, `"ZiB"`, `"YiB"`. The default value is `:auto_si`.

* `:accuracy` - A positive number each label is rounded to a multiple of, in its unit, or `nil` to work it out from the breaks (see the module's documentation). The default value is `1`.

* `:scale` - A positive number each break is multiplied by before it is written. The default value is `1`.

* `:prefix` (`t:String.t/0`) - Text written before the number, after a negative's sign. The default value is `""`.

* `:big_mark` (`t:String.t/0`) - Text written between each three digits of the whole part. The default value is `" "`.

* `:decimal_mark` - Text written before the decimals, at least one character. The default value is `"."`.

* `:trim` (`t:boolean/0`) - `false` pads each number on the left with spaces to the width of the widest, before its prefix and its sign: `"-$    1"` beside `"$1,000"`. The default value is `true`.

* `:style_negative` - How a negative is marked: `:hyphen` (`-1`), `:minus`, a true minus sign (`−1`), or `:parens` (`(1)`). The default value is `:hyphen`.

* `:style_positive` - How a positive is marked: `:none` (`1`), `:plus` (`+1`) or `:space`, a figure space as wide as a digit (`"\u20071"`). The default value is `:none`.

## Examples

    iex> Scales.Labels.bytes([0, 1500, 2.0e6, nil])
    ["0 B", "2 kB", "2 MB", nil]

    iex> Scales.Labels.bytes([512, 1024, 1_048_576], units: :auto_binary)
    ["512 B", "1 kiB", "1 MiB"]

    iex> Scales.Labels.bytes([1500, 2500, 1.0e6], accuracy: nil)
    ["1.5 kB", "2.5 kB", "1 MB"]

    iex> Scales.Labels.bytes([1.0e6, 2.5e6], units: "kB", big_mark: ",")
    ["1,000 kB", "2,500 kB"]

# `comma`

```elixir
@spec comma([number() | nil], keyword()) :: [String.t() | nil]
```

Labels numbers with a comma between each three digits of the whole part, R's `label_comma()`.

## Options

* `:accuracy` - A positive number each label is rounded to a multiple of, in the label's own units, after `scale:`; worked out from the breaks when not given (see the module's documentation).

* `:scale` - A positive number each break is multiplied by before it is written. The default value is `1`.

* `:prefix` (`t:String.t/0`) - Text written before the number, after a negative's sign. The default value is `""`.

* `:suffix` (`t:String.t/0`) - Text written after the number. The default value is `""`.

* `:big_mark` (`t:String.t/0`) - Text written between each three digits of the whole part. The default value is `","`.

* `:decimal_mark` - Text written before the decimals, at least one character. The default value is `"."`.

* `:trim` (`t:boolean/0`) - `false` pads each number on the left with spaces to the width of the widest, before its prefix and its sign: `"-$    1"` beside `"$1,000"`. The default value is `true`.

* `:style_negative` - How a negative is marked: `:hyphen` (`-1`), `:minus`, a true minus sign (`−1`), or `:parens` (`(1)`). The default value is `:hyphen`.

* `:style_positive` - How a positive is marked: `:none` (`1`), `:plus` (`+1`) or `:space`, a figure space as wide as a digit (`"\u20071"`). The default value is `:none`.

* `:scale_cut` - Steps to shorten each label by, a list of `{suffix, threshold}` pairs (see the module's documentation).

## Examples

    iex> Scales.Labels.comma([-100_000, 1_234_567.891, 0.125])
    ["-100,000", "1,234,568", "0"]

    iex> Scales.Labels.comma([0.5, 1, 1.25])
    ["0.50", "1.00", "1.25"]

    iex> Scales.Labels.comma([0, 1250, 2500], accuracy: 0.1, scale: 0.001, suffix: "k")
    ["0.0k", "1.2k", "2.5k"]

    iex> Scales.Labels.comma([0, 1500, 1.0e6], scale_cut: Scales.Labels.cut_short_scale())
    ["0", "2K", "1M"]

# `currency`

```elixir
@spec currency([number() | nil], keyword()) :: [String.t() | nil]
```

Labels amounts of money, R's `label_currency()`, in dollars unless `prefix:` and `suffix:` say
otherwise.

Without `accuracy:` or `scale_cut:`, labels show cents when some amount is not whole and none
is larger than `largest_with_fractional:`, and whole amounts otherwise. When both marks would
be a comma, the thousands are marked with a space instead.

## Options

* `:accuracy` - A positive number each label is rounded to a multiple of, in the label's own units, after `scale:`; worked out from the breaks when not given (see the module's documentation).

* `:scale` - A positive number each break is multiplied by before it is written. The default value is `1`.

* `:prefix` (`t:String.t/0`) - Text written before the number, after a negative's sign. The default value is `"$"`.

* `:suffix` (`t:String.t/0`) - Text written after the number. The default value is `""`.

* `:big_mark` (`t:String.t/0`) - Text written between each three digits of the whole part. The default value is `","`.

* `:decimal_mark` - Text written before the decimals, at least one character. The default value is `"."`.

* `:trim` (`t:boolean/0`) - `false` pads each number on the left with spaces to the width of the widest, before its prefix and its sign: `"-$    1"` beside `"$1,000"`. The default value is `true`.

* `:style_negative` - How a negative is marked: `:hyphen` (`-1`), `:minus`, a true minus sign (`−1`), or `:parens` (`(1)`). The default value is `:hyphen`.

* `:style_positive` - How a positive is marked: `:none` (`1`), `:plus` (`+1`) or `:space`, a figure space as wide as a digit (`"\u20071"`). The default value is `:none`.

* `:scale_cut` - Steps to shorten each label by, a list of `{suffix, threshold}` pairs (see the module's documentation).

* `:largest_with_fractional` - The largest amount that shows cents when neither `accuracy:` nor `scale_cut:` is given. The default value is `100000`.

## Examples

    iex> Scales.Labels.currency([0, 50, 100])
    ["$0", "$50", "$100"]

    iex> Scales.Labels.currency([-13.5, 0, 13.5])
    ["-$13.50", "$0.00", "$13.50"]

    iex> Scales.Labels.currency([1234.5, 1], big_mark: ".", decimal_mark: ",", prefix: "", suffix: " €")
    ["1.234,50 €", "1,00 €"]

    iex> Scales.Labels.currency([0, 2.5e5, 5.0e5, 1.0e6], scale_cut: Scales.Labels.cut_short_scale())
    ["$0", "$250K", "$500K", "$1M"]

# `cut_long_scale`

```elixir
@spec cut_long_scale(keyword()) :: [step()]
```

The long scale's steps for `scale_cut:`, R's `cut_long_scale()`: thousands `K`, millions `M`,
billions `B` (1e12) and trillions `T` (1e18).

## Options

* `:space` (`t:boolean/0`) - `true` puts a space before each suffix (`2 K`). The default value is `false`.

## Examples

    iex> Scales.Labels.currency([0, 1.0e9, 2.0e12], scale_cut: Scales.Labels.cut_long_scale())
    ["$0", "$1,000M", "$2B"]

# `cut_short_scale`

```elixir
@spec cut_short_scale(keyword()) :: [step()]
```

The short scale's steps for `scale_cut:`, R's `cut_short_scale()`: thousands `K`, millions `M`,
billions `B` (1e9) and trillions `T` (1e12).

## Options

* `:space` (`t:boolean/0`) - `true` puts a space before each suffix (`2 K`). The default value is `false`.

## Examples

    iex> Scales.Labels.cut_short_scale()
    [{"", 0}, {"K", 1000}, {"M", 1000000}, {"B", 1000000000}, {"T", 1000000000000}]

    iex> Scales.Labels.comma([-2000, 0, 2000], scale_cut: Scales.Labels.cut_short_scale(space: true))
    ["-2 K", "0 ", "2 K"]

# `cut_si`

```elixir
@spec cut_si(String.t()) :: [step()]
```

The SI prefixes' steps for `scale_cut:`, R's `cut_si()`, from yocto (1e-24) to yotta (1e24),
each written with a space and `unit` after it (`" kg"`), and `unit` alone below a yocto and
from 1 to 1000.

## Examples

    iex> Scales.Labels.comma([0.001, 1, 1500], scale_cut: Scales.Labels.cut_si("g"))
    ["1 mg", "1 g", "2 kg"]

# `cut_time_scale`

```elixir
@spec cut_time_scale(keyword()) :: [step()]
```

Steps of time for `scale_cut:`, R's `cut_time_scale()`, for labels in seconds: nanoseconds `ns`,
microseconds `μs`, milliseconds `ms`, seconds `s`, minutes `m`, hours `h`, days `d` and weeks
`w`.

## Options

* `:space` (`t:boolean/0`) - `true` puts a space before each suffix (`2 K`). The default value is `false`.

## Examples

    iex> Scales.Labels.comma([0, 45, 90, 120, 5400], scale_cut: Scales.Labels.cut_time_scale())
    ["0", "45s", "90s", "2m", "90m"]

# `date`

```elixir
@spec date([Date.t() | NaiveDateTime.t() | DateTime.t() | nil], keyword()) :: [
  String.t() | nil
]
```

Labels dates and date-times in a format, R's `label_date()`, by `Calendar.strftime/3`, but for
`%Y`, the year unpadded (the year 900 is `900`).

A `DateTime` is shifted into `tz:` first, UTC unless given; a `NaiveDateTime` is written as it
is, its zone UTC, and a `Date` at midnight UTC, so `%H` writes `00` and `%Z` `UTC`.

## Options

* `:format` - The format each break is written in, as `Calendar.strftime/3` takes it, but for `%Y`, the year unpadded. The default value is `"%Y-%m-%d"`.

* `:tz` (`t:String.t/0`) - The time zone each `DateTime` is shifted into before it is written, one `time_zone_database:` knows; a `Date` is written at midnight UTC and a `NaiveDateTime` or `Time` as it is. The default value is `"Etc/UTC"`.

* `:time_zone_database` (`t:atom/0`) - The time zone database that reads `tz:`, such as `Tz.TimeZoneDatabase`; Elixir's own, which knows UTC alone, when not given. The default value is `Calendar.UTCOnlyTimeZoneDatabase`.

## Examples

    iex> Scales.Labels.date([~D[2020-01-01], nil, ~D[2021-06-15]])
    ["2020-01-01", nil, "2021-06-15"]

    iex> Scales.Labels.date([~N[2020-03-05 14:07:09]], format: "%d %b %H:%M")
    ["05 Mar 14:07"]

    iex> Scales.Labels.date([~U[2021-03-14 06:30:00Z], ~U[2021-03-14 07:30:00Z]],
    ...>   format: "%H:%M %Z",
    ...>   tz: "America/New_York",
    ...>   time_zone_database: Tz.TimeZoneDatabase
    ...> )
    ["01:30 EST", "03:30 EDT"]

# `date_short`

```elixir
@spec date_short([Date.t() | NaiveDateTime.t() | DateTime.t() | nil], keyword()) :: [
  String.t() | nil
]
```

Labels dates and date-times with what changed from the break before, R's
`label_date_short()`: each part, a year, a month, a day and a time of day, written where it or
a coarser part changed, finest first, one part to a line.

The first break, and one after a missing break, show every part. The time is left out where
every break is at midnight, then the day where every break is a 1st, then the month where every
break is in January. A break with no part to write, as a date repeated, is written in full,
`2020-01-05`, or `2020-01-05 10:00:00` where one such break is not at midnight in `tz:`.

What changed is read on each break's own clock, a `DateTime` in its own zone; each part is
written as `date/2` writes it, a `DateTime` shifted into `tz:`.

## Options

* `:format` (`t:keyword/0`) - Each part's format, as `Calendar.strftime/3` takes it, but for `%Y`, the year unpadded; a part left out keeps its default. The default value is `[]`.

  * `:year` - The year's format, or `nil` for a year never written. The default value is `"%Y"`.

  * `:month` - The month's format, or `nil` for a month never written. The default value is `"%b"`.

  * `:day` - The day's format, or `nil` for a day never written. The default value is `"%d"`.

  * `:time` - The time's format, or `nil` for a time never written. The default value is `"%H:%M"`.

* `:sep` (`t:String.t/0`) - The text written between the parts. The default value is `"\n"`.

* `:leading` (`t:String.t/0`) - The text that replaces a `0` at the start of each part: `" "` writes the 5th `" 5"`, `""` writes it `"5"`. The default value is `"0"`.

* `:tz` (`t:String.t/0`) - The time zone each `DateTime` is shifted into before it is written, one `time_zone_database:` knows; a `Date` is written at midnight UTC and a `NaiveDateTime` or `Time` as it is. The default value is `"Etc/UTC"`.

* `:time_zone_database` (`t:atom/0`) - The time zone database that reads `tz:`, such as `Tz.TimeZoneDatabase`; Elixir's own, which knows UTC alone, when not given. The default value is `Calendar.UTCOnlyTimeZoneDatabase`.

## Examples

    iex> Scales.Labels.date_short([~D[2019-11-01], ~D[2019-12-01], ~D[2020-01-01], ~D[2020-02-01]])
    ["Nov\n2019", "Dec", "Jan\n2020", "Feb"]

    iex> Scales.Labels.date_short([~N[2020-01-01 18:00:00], ~N[2020-01-01 21:00:00], ~N[2020-01-02 00:00:00]])
    ["18:00\n01\nJan\n2020", "21:00", "00:00\n02"]

    iex> Scales.Labels.date_short([~D[2020-01-05], ~D[2020-01-06], ~D[2020-02-07]],
    ...>   format: [month: "%B"],
    ...>   sep: " ",
    ...>   leading: ""
    ...> )
    ["5 January 2020", "6", "7 February"]

# `dictionary`

```elixir
@spec dictionary([term()], keyword()) :: [String.t() | nil]
```

Labels values by a dictionary, R's `label_dictionary()`: each value the dictionary holds takes
its label, and each it lacks, a missing one included, `nomatch:`'s; with no `nomatch:`, a string
it lacks is its own label and a missing value stays missing.

A value is matched by `Scales.DataRange.key/1`, so a key of `2` names `2.0`, and strings match
exactly, case and accents as written; a key matches a value of its own type, so `"1"` names the
string and never the number `1`. A value that is not a string, which the dictionary lacks,
needs a `nomatch:`, or raises.

## Options

* `:dictionary` - Required. A map of values to their labels, each a string or `nil` for none.

* `:nomatch` - The label of a value the dictionary lacks, a missing one included: a string, or `nil` for none; `:none` keeps a string as its own label and a missing value missing. The default value is `:none`.

## Examples

    iex> Scales.Labels.dictionary(["a", "b", "c", nil], dictionary: %{"a" => "Apple", "b" => "Banana"})
    ["Apple", "Banana", "c", nil]

    iex> Scales.Labels.dictionary(["a", "c", nil], dictionary: %{"a" => "Apple"}, nomatch: "?")
    ["Apple", "?", "?"]

    iex> Scales.Labels.dictionary([4.0, 6.0, 8.0], dictionary: %{4 => "four", 6 => "six", 8 => "eight"})
    ["four", "six", "eight"]

# `format_log`

```elixir
@spec format_log([number() | nil], keyword()) :: [String.t() | nil]
```

Writes numbers as powers of `base:` in text, R's `format_log()`: `log/2`'s labels each written
`base^exponent`, its exponent as the exponents are written together, padded on the left to the
widest, in up to `digits:` significant digits.

## Options

* `:base` - The base of the logarithm, a positive number other than 1. The default value is `10`.

* `:signed` - Whether each label is signed, `+` or `-` before its power and a zero `"0"`; `:auto` signs them when any break is zero or negative. Unsigned, a break at or below zero raises. The default value is `:auto`.

* `:digits` - An integer from 1 to 22, the most significant digits shown, though no label shows more than 7 (see the function's documentation). The default value is `7`.

## Examples

    iex> Scales.Labels.format_log([0.01, 1, 100])
    ["10^-2", "10^ 0", "10^ 2"]

    iex> Scales.Labels.format_log([0.5, 2, 8], digits: 3)
    ["10^-0.301", "10^ 0.301", "10^ 0.903"]

    iex> Scales.Labels.format_log([-100, 0, 100])
    ["-10^2", "0", "+10^2"]

# `log`

```elixir
@spec log([number() | nil], keyword()) :: [power_label()]
```

Labels numbers as powers of `base:`, R's `label_log()`: `1000` is `{:power, "10", "3"}`, drawn
as a superscript.

Each break's exponent, its logarithm in `base:`, is rounded to the seventh significant digit
of the largest exponent's, then the exponents are written together in up to `digits:`
significant digits, sharing their decimals, and each is read back and written alone in up to
15, its zeros after the last significant digit dropped: `2` is `{:power, "10", "0.301"}`, and
`1` beside it `{:power, "10", "0"}`. No exponent shows more than 7 significant digits. The
base is written in up to 15 significant digits too, in plain or scientific notation,
whichever is shorter (`"2.71828182845905"` for e, `"1e+06"`).

Signed, each power has its sign before it, `{:signed, "-", power}` or `{:signed, "+", power}`,
and a zero is labelled `"0"`. A missing break has no label.

## Options

* `:base` - The base of the logarithm, a positive number other than 1. The default value is `10`.

* `:signed` - Whether each label is signed, `+` or `-` before its power and a zero `"0"`; `:auto` signs them when any break is zero or negative. Unsigned, a break at or below zero raises. The default value is `:auto`.

* `:digits` - An integer from 1 to 22, the most significant digits shown, though no label shows more than 7 (see the function's documentation). The default value is `3`.

## Examples

    iex> Scales.Labels.log([0.01, 1, 100])
    [{:power, "10", "-2"}, {:power, "10", "0"}, {:power, "10", "2"}]

    iex> Scales.Labels.log([1, 2, 8], base: 2)
    [{:power, "2", "0"}, {:power, "2", "1"}, {:power, "2", "3"}]

    iex> Scales.Labels.log([-100, 0, 100])
    [{:signed, "-", {:power, "10", "2"}}, "0", {:signed, "+", {:power, "10", "2"}}]

# `number`

```elixir
@spec number([number() | nil], keyword()) :: [String.t() | nil]
```

Labels numbers in decimals, R's `label_number()`, with a space between each three digits of the
whole part.

## Options

* `:accuracy` - A positive number each label is rounded to a multiple of, in the label's own units, after `scale:`; worked out from the breaks when not given (see the module's documentation).

* `:scale` - A positive number each break is multiplied by before it is written. The default value is `1`.

* `:prefix` (`t:String.t/0`) - Text written before the number, after a negative's sign. The default value is `""`.

* `:suffix` (`t:String.t/0`) - Text written after the number. The default value is `""`.

* `:big_mark` (`t:String.t/0`) - Text written between each three digits of the whole part. The default value is `" "`.

* `:decimal_mark` - Text written before the decimals, at least one character. The default value is `"."`.

* `:trim` (`t:boolean/0`) - `false` pads each number on the left with spaces to the width of the widest, before its prefix and its sign: `"-$    1"` beside `"$1,000"`. The default value is `true`.

* `:style_negative` - How a negative is marked: `:hyphen` (`-1`), `:minus`, a true minus sign (`−1`), or `:parens` (`(1)`). The default value is `:hyphen`.

* `:style_positive` - How a positive is marked: `:none` (`1`), `:plus` (`+1`) or `:space`, a figure space as wide as a digit (`"\u20071"`). The default value is `:none`.

* `:scale_cut` - Steps to shorten each label by, a list of `{suffix, threshold}` pairs (see the module's documentation).

## Examples

    iex> Scales.Labels.number([-2.5, 0, 1.0e6])
    ["-2.5", "0.0", "1 000 000.0"]

    iex> Scales.Labels.number([1, nil, 1.5])
    ["1.0", nil, "1.5"]

    iex> Scales.Labels.number([0.001, 0.0025], scale: 1000, suffix: " ms")
    ["1.0 ms", "2.5 ms"]

# `number_auto`

```elixir
@spec number_auto([number() | nil]) :: [String.t() | nil]
```

Labels numbers in whichever way suits their size, R's `label_number_auto()`.

The way is chosen by the largest and smallest magnitudes of the breaks other than zero:

- any above a million: each label in whole numbers with a space between each three digits, as
  `number(breaks, accuracy: 1)` writes it, or in scientific notation, whichever is shorter,
  the whole number on a tie;
- any below 0.001: each in plain decimals or in scientific notation, whichever is shorter,
  plain on a tie;
- all positive, from 1000 to 2200, as years are: in plain decimals, with no mark between
  thousands;
- any above 1000: as `number(breaks, accuracy: 1)` writes them;
- otherwise in plain decimals, or in scientific notation where that is narrower for all.

Plain decimals and scientific notation are written with every label sharing its decimals, in
up to 7 significant digits, each way on its own when the shorter of two is taken. A missing
break is labelled with the text `"NA"` in the years' way and the last, and has no label in the
others.

## Examples

    iex> Scales.Labels.number_auto([0, 2.5e6, 5.0e6])
    ["0", "2.5e+06", "5.0e+06"]

    iex> Scales.Labels.number_auto([1990, 2000, 2010])
    ["1990", "2000", "2010"]

    iex> Scales.Labels.number_auto([0, 5000, 10_000, nil])
    ["0", "5 000", "10 000", nil]

    iex> Scales.Labels.number_auto([0, 0.5, nil])
    ["0.0", "0.5", "NA"]

# `ordinal`

```elixir
@spec ordinal([number() | nil], keyword()) :: [String.t() | nil]
```

Labels numbers as ordinals, R's `label_ordinal()`: `1st`, `2nd`, `3rd`, `4th`.

Each break is rounded to a whole number, a half to the even one, and written as `number/2`
writes it with an accuracy of 1, then the suffix of the first rule whose regex matches the
whole number's digits: unmarked and unpadded, a negative's with a `-` before them (`"-12"`).
R matches the text it writes a number in alone, which for a round number is scientific
notation wherever that is shorter (`2e+05`); the digits are matched here, so a rule reading the
last digit sees `200000`'s `0`, and English ordinals from 1e21 up, which R writes `...st` from
`1e+21`, are `...th`. A break no rule matches raises, so the rules end with one every break
matches, as `ordinal_english/0`'s do.

## Options

* `:rules` - A list of `{suffix, regex}` pairs, each break taking the suffix of the first whose regex matches it (see the function's documentation); `ordinal_english/0`'s when not given.

* `:prefix` (`t:String.t/0`) - Text written before the number, after a negative's sign. The default value is `""`.

* `:suffix` (`t:String.t/0`) - Text written after the ordinal's. The default value is `""`.

* `:big_mark` (`t:String.t/0`) - Text written between each three digits of the whole part. The default value is `" "`.

* `:trim` (`t:boolean/0`) - `false` pads each number on the left with spaces to the width of the widest, before its prefix and its sign: `"-$    1"` beside `"$1,000"`. The default value is `true`.

* `:style_negative` - How a negative is marked: `:hyphen` (`-1`), `:minus`, a true minus sign (`−1`), or `:parens` (`(1)`). The default value is `:hyphen`.

* `:style_positive` - How a positive is marked: `:none` (`1`), `:plus` (`+1`) or `:space`, a figure space as wide as a digit (`"\u20071"`). The default value is `:none`.

## Examples

    iex> Scales.Labels.ordinal([1, 2, 3, 4, 11, 12, 13, 21, nil])
    ["1st", "2nd", "3rd", "4th", "11th", "12th", "13th", "21st", nil]

    iex> Scales.Labels.ordinal([1, 1001, 2.5], big_mark: ",", prefix: "#")
    ["#1st", "#1,001st", "#2nd"]

    iex> Scales.Labels.ordinal([1, 2], rules: [{"º", ~r/./}])
    ["1º", "2º"]

# `ordinal_english`

```elixir
@spec ordinal_english() :: [{String.t(), Regex.t()}]
```

English ordinals' rules for `ordinal/2`, R's `ordinal_english()`: `st` after a 1, `nd` after a
2 and `rd` after a 3, but for 11, 12 and 13, and `th` after anything else.

## Examples

    iex> Scales.Labels.ordinal_english() |> Enum.map(fn {suffix, _rule} -> suffix end)
    ["st", "nd", "rd", "th", "th", "th"]

# `percent`

```elixir
@spec percent([number() | nil], keyword()) :: [String.t() | nil]
```

Labels proportions as percentages, R's `label_percent()`: each is multiplied by 100 and written
with a `%`.

Thousands are marked with a space (`1 234%`).

## Options

* `:accuracy` - A positive number each label is rounded to a multiple of, in the label's own units, after `scale:`; worked out from the breaks when not given (see the module's documentation).

* `:scale` - A positive number each break is multiplied by before it is written. The default value is `100`.

* `:prefix` (`t:String.t/0`) - Text written before the number, after a negative's sign. The default value is `""`.

* `:suffix` (`t:String.t/0`) - Text written after the number. The default value is `"%"`.

* `:big_mark` (`t:String.t/0`) - Text written between each three digits of the whole part. The default value is `" "`.

* `:decimal_mark` - Text written before the decimals, at least one character. The default value is `"."`.

* `:trim` (`t:boolean/0`) - `false` pads each number on the left with spaces to the width of the widest, before its prefix and its sign: `"-$    1"` beside `"$1,000"`. The default value is `true`.

* `:style_negative` - How a negative is marked: `:hyphen` (`-1`), `:minus`, a true minus sign (`−1`), or `:parens` (`(1)`). The default value is `:hyphen`.

* `:style_positive` - How a positive is marked: `:none` (`1`), `:plus` (`+1`) or `:space`, a figure space as wide as a digit (`"\u20071"`). The default value is `:none`.

* `:scale_cut` - Steps to shorten each label by, a list of `{suffix, threshold}` pairs (see the module's documentation).

## Examples

    iex> Scales.Labels.percent([0, 0.25, 0.5, 0.75, 1])
    ["0%", "25%", "50%", "75%", "100%"]

    iex> Scales.Labels.percent([0.1234, 0.5], accuracy: 0.1)
    ["12.3%", "50.0%"]

    iex> Scales.Labels.percent([-0.5, 0.125, 12.345])
    ["-50%", "12%", "1 234%"]

# `plain`

```elixir
@spec plain([float()]) :: [String.t()]
```

Labels numbers plainly, as Elixir shows a float, with what an axis read at a glance needs on
top: every label gets as many decimals as the most precise one needs, none when every break is
whole, never scientific notation and no thousands separators. Each break is first rounded to 15
significant digits, which drops the rounding error that adding up steps leaves in the last
digits (`0.3`, not `0.30000000000000004`), and a negative zero is written `0`. R's default
labels come from `format()` instead, which writes `1e+05` where this writes `100000`.

## Examples

    iex> Scales.Labels.plain([0.0, 2.5, 5.0])
    ["0.0", "2.5", "5.0"]

    iex> Scales.Labels.plain([0.0, 250_000.0])
    ["0", "250000"]

# `pvalue`

```elixir
@spec pvalue([number() | nil], keyword()) :: [String.t() | nil]
```

Labels p-values, R's `label_pvalue()`: each rounded to `accuracy:` as `number/2` rounds it,
one below the accuracy written as less than it (`<0.001`) and one above 1 less the accuracy
as more than that (`>0.999`).

## Options

* `:accuracy` - A positive number no larger than 0.5 each label is rounded to a multiple of; below it a p-value is written as less than it, and above 1 less it as more than that. The default value is `0.001`.

* `:decimal_mark` - Text written before the decimals, at least one character. The default value is `"."`.

* `:prefix` - Three strings, `{below, between, above}`, written before a p-value below the accuracy, one between, and one above 1 less it; `{"<", "", ">"}` when not given.

* `:add_p` (`t:boolean/0`) - `true` writes the prefixes `{"p<", "p=", "p>"}`; not taken with `prefix:`. The default value is `false`.

## Examples

    iex> Scales.Labels.pvalue([0.0001, 0.05, 0.5, 0.9999, nil])
    ["<0.001", "0.050", "0.500", ">0.999", nil]

    iex> Scales.Labels.pvalue([0.001, 0.123, 0.999], accuracy: 0.01, add_p: true)
    ["p<0.01", "p=0.12", "p>0.99"]

    iex> Scales.Labels.pvalue([0.0001, 0.05], prefix: {"less than ", "", "more than "})
    ["less than 0.001", "0.050"]

# `scientific`

```elixir
@spec scientific([number() | nil], keyword()) :: [String.t() | nil]
```

Labels numbers in scientific notation, R's `label_scientific()`.

Each break is multiplied by `scale:` and rounded to `digits:` significant digits, a half to the
even neighbour of the number as a float scaled into its digits (`0.15` to one digit is `2e-01`).
The labels are then written together, their mantissas sharing the most decimals any needs, in
at most 7 significant digits: `digits:` above 7 shows 7. They are never padded. A break that
`scale:` takes past the largest float is written `Inf` or `-Inf`.

## Options

* `:digits` - An integer from 1 to 22, the most significant digits shown, though no label shows more than 7 (see the function's documentation). The default value is `3`.

* `:scale` - A positive number each break is multiplied by before it is written. The default value is `1`.

* `:prefix` (`t:String.t/0`) - Text written before the number. The default value is `""`.

* `:suffix` (`t:String.t/0`) - Text written after the number. The default value is `""`.

* `:decimal_mark` - Text written before the decimals, at least one character. The default value is `"."`.

## Examples

    iex> Scales.Labels.scientific([0.15, 1.5, 12345])
    ["1.50e-01", "1.50e+00", "1.23e+04"]

    iex> Scales.Labels.scientific([1, 10, nil, 100])
    ["1e+00", "1e+01", nil, "1e+02"]

    iex> Scales.Labels.scientific([1234, 5], decimal_mark: ",", suffix: " m")
    ["1,23e+03 m", "5,00e+00 m"]

# `time`

```elixir
@spec time([NaiveDateTime.t() | DateTime.t() | Time.t() | nil], keyword()) :: [
  String.t() | nil
]
```

Labels times of day and date-times in a format, R's `label_time()`, by `Calendar.strftime/3`.

A `DateTime` is shifted into `tz:` first, UTC unless given; a `NaiveDateTime` is written as it
is, its zone UTC, and a `Time` as it is. A format writing a part of a date (`%Y`, `%d`) raises
on a `Time`.

## Options

* `:format` - The format each break is written in, as `Calendar.strftime/3` takes it, but for `%Y`, the year unpadded. The default value is `"%H:%M:%S"`.

* `:tz` (`t:String.t/0`) - The time zone each `DateTime` is shifted into before it is written, one `time_zone_database:` knows; a `Date` is written at midnight UTC and a `NaiveDateTime` or `Time` as it is. The default value is `"Etc/UTC"`.

* `:time_zone_database` (`t:atom/0`) - The time zone database that reads `tz:`, such as `Tz.TimeZoneDatabase`; Elixir's own, which knows UTC alone, when not given. The default value is `Calendar.UTCOnlyTimeZoneDatabase`.

## Examples

    iex> Scales.Labels.time([~T[00:00:00], ~T[01:01:01], ~T[23:59:59], nil])
    ["00:00:00", "01:01:01", "23:59:59", nil]

    iex> Scales.Labels.time([~T[00:00:59], ~T[00:59:59.9]], format: "%H:%M")
    ["00:00", "00:59"]

    iex> Scales.Labels.time([~U[2020-01-01 03:00:00Z]],
    ...>   tz: "America/New_York",
    ...>   time_zone_database: Tz.TimeZoneDatabase
    ...> )
    ["22:00:00"]

# `timespan`

```elixir
@spec timespan([number() | nil], keyword()) :: [String.t() | nil]
```

Labels spans of time, R's `label_timespan()`: each break, counted in `unit:`, turned into
seconds and written as `number/2` writes it under `cut_time_scale/1`'s steps, `90` minutes
being `90m` and `120` `2h`.

## Options

* `:unit` - The unit the breaks are counted in, `:second`, `:minute`, `:hour`, `:day` or `:week`. The default value is `:second`.

* `:space` (`t:boolean/0`) - `true` puts a space before each unit's suffix (`90 s`). The default value is `false`.

* `:accuracy` - A positive number each label is rounded to a multiple of, in the label's own units, after `scale:`; worked out from the breaks when not given (see the module's documentation).

* `:scale` - A positive number each break is multiplied by before it is written. The default value is `1`.

* `:prefix` (`t:String.t/0`) - Text written before the number, after a negative's sign. The default value is `""`.

* `:suffix` (`t:String.t/0`) - Text written after the number. The default value is `""`.

* `:big_mark` (`t:String.t/0`) - Text written between each three digits of the whole part. The default value is `" "`.

* `:decimal_mark` - Text written before the decimals, at least one character. The default value is `"."`.

* `:trim` (`t:boolean/0`) - `false` pads each number on the left with spaces to the width of the widest, before its prefix and its sign: `"-$    1"` beside `"$1,000"`. The default value is `true`.

* `:style_negative` - How a negative is marked: `:hyphen` (`-1`), `:minus`, a true minus sign (`−1`), or `:parens` (`(1)`). The default value is `:hyphen`.

* `:style_positive` - How a positive is marked: `:none` (`1`), `:plus` (`+1`) or `:space`, a figure space as wide as a digit (`"\u20071"`). The default value is `:none`.

## Examples

    iex> Scales.Labels.timespan([0, 30, 90, 3600, 86_400])
    ["0", "30s", "90s", "1h", "1d"]

    iex> Scales.Labels.timespan([0.5, 1.5, 36], unit: :hour)
    ["30m", "90m", "36h"]

    iex> Scales.Labels.timespan([1, 60, nil], space: true, suffix: " ago")
    ["1 s ago", "1 m ago", nil]

# `wrap`

```elixir
@spec wrap([String.t() | nil], keyword()) :: [String.t()]
```

Breaks text into lines shorter than `width:` columns, R's `label_wrap()`, as R's `strwrap()`
breaks it, the lines joined by `"\n"`. A missing label, `nil`, is written `"NA"`.

A word goes on the line before it while the line stays shorter than the width, and a word wider
than that has a line of its own, never split. Whitespace between words (spaces, tabs and line
breaks, nothing else) becomes one space, or two after a word ending a sentence, in `.`, `?` or
`!` and then at most one of `)`, `"` and `'`, where two or more whitespace characters followed
it. A line holding only whitespace starts a new paragraph, an empty line before it.

A character is as wide as R counts it: most Chinese, Japanese and Korean characters, fullwidth
forms and emoji take two columns, and combining marks, zero-width spaces and joiners none, so a
word made of those alone is dropped as whitespace is.

## Options

* `:width` (`t:pos_integer/0`) - Required. How many columns each line of a label is kept under.

## Examples

    iex> Scales.Labels.wrap(["Conservation Dependent", "Least concern", nil], width: 10)
    ["Conservation\nDependent", "Least\nconcern", "NA"]

    iex> Scales.Labels.wrap(["Mr.  Smith went  to Washington."], width: 20)
    ["Mr.  Smith went to\nWashington."]

    iex> Scales.Labels.wrap(["日本語 日本語"], width: 10)
    ["日本語\n日本語"]

---

*Consult [api-reference.md](api-reference.md) for complete listing*
