# `NumberF`
[🔗](https://github.com/jamesnjovu/elixir_number_functions/blob/v0.3.0/lib/number_f.ex#L1)

# NumberF

A comprehensive utility library for number formatting, calculation, and manipulation in Elixir.

## Features

- **Number Formatting**: Format numbers as currency, with commas, custom delimiters, abbreviated forms (K, M, B)
- **Text Representation**: Convert numbers to words, ordinals, Roman numerals
- **Financial Calculations**: Simple/compound interest, EMI calculations, currency conversions, tax calculations
- **Statistical Functions**: Mean, median, mode, standard deviation, variance
- **Date Calculations**: Age calculation, business days, payment terms
- **Validation**: Credit card validation (Luhn algorithm), number format validation
- **String Generation**: Random string generation, password creation
- **Type Conversions**: Convert between numeric types and formats
- **Memory Size**: Convert byte counts to human-readable formats
- **Phone Formatting**: Format phone numbers based on country codes
- **Unit Conversion**: Convert between metric and imperial units
- **Tax Calculations**: VAT, sales tax, income tax calculations
- **Precision Handling**: Different rounding strategies and precision handling
- **Internationalization**: Format numbers according to locale conventions

## Installation

Add `number_f` to your list of dependencies in `mix.exs`:

```elixir
def deps do
[
  {:number_f, "~> 0.3.0"}
]
end
```

## Key Functionality

### Currency & Number Formatting

NumberF provides comprehensive formatting capabilities for currency and numbers:

```elixir
# Format as currency with default (ZMW)
NumberF.currency(1234.567)                    # => "ZMW 1,234.57"

# Format as US Dollars with 2 decimal places
NumberF.currency(1234.567, "USD", 2)          # => "USD 1,234.57"

# Format with French locale (space as thousands separator, comma as decimal)
NumberF.format_number(1234567.89, "fr-FR")    # => "1 234 567,89"
```

### Number to Words Conversion

Convert numeric values to their word representations:

```elixir
# Convert whole number with default currency (Kwacha)
NumberF.to_words(42)                          # => "Forty Two Kwacha"

# Convert decimal with custom currency
NumberF.to_words(42.75, "Dollars", "Cents")   # => "Forty Two Dollars And Seventy Five Cents"

# Advanced internationalization
NumberF.spell_number(42, "fr")                # => "Quarante-deux"
```

### Financial Calculations

Perform complex financial operations with ease:

```elixir
# Calculate simple interest (principal, rate, time)
NumberF.simple_interest(1000, 0.05, 2)        # => 100.0

# Calculate compound interest with monthly compounding
NumberF.compound_interest(1000, 0.05, 2, 12)  # => 104.94

# Calculate loan EMI (principal, rate, term in months)
NumberF.calculate_emi(100000, 0.10, 12)       # => 8791.59

# Calculate VAT
NumberF.calculate_vat(100, 0.2)               # => %{net: 100.0, vat: 20.0, gross: 120.0}
```

### Statistical Functions

Analyze numerical data with statistical functions:

```elixir
NumberF.mean([1, 2, 3, 4, 5])                 # => 3.0
NumberF.median([1, 3, 5, 7, 9])               # => 5
NumberF.mode([1, 2, 2, 3, 3, 3, 4])           # => [3]
NumberF.standard_deviation([2, 4, 4, 4, 5, 5, 7, 9])  # => 2.0
```

### Internationalization & Localization

Format numbers according to locale-specific conventions:

```elixir
# Format with US locale
NumberF.format_currency(1234.56, "en-US")     # => "$1,234.56"

# Format with French locale
NumberF.format_currency(1234.56, "fr-FR")     # => "1 234,56 €"

# Format with German locale but using USD
NumberF.format_currency(1234.56, "de-DE", currency_code: "USD")  # => "1.234,56 $"
```

## Module Organization

NumberF is organized into specialized modules for different functionality areas:

- **Core**: `NumberF`, `NumberF.Registry`
- **Calculation**: `NumberF.Calculations`, `NumberF.Financial`, `NumberF.Statistics`, `NumberF.Precision`, `NumberF.Tax`
- **Formatting**: `NumberF.Formatter`, `NumberF.CustomFormatter`, `NumberF.Currencies`
- **Conversion**: `NumberF.Metrics`
- **Validation**: `NumberF.Validation`
- **Date**: `NumberF.DateCalculations`
- **I18n**: `NumberF.I18n`

Every function documented on those modules is also available directly from
`NumberF`, which is the recommended entry point. Modules not listed above
(`NumberF.Currency`, `NumberF.Memory`, `NumberF.Randomizer`, `NumberF.Helper`,
`NumberF.NumbersToWords`, `NumberF.NumberToWord`) are internal implementation
details and are not part of the public API.

For more information, see the [full documentation on HexDocs](https://hexdocs.pm/number_f).

# `abbreviate_number`

Formats large numbers as K, M, B (e.g., 1.2K, 3.4M).

## Parameters
  - `number`: The number to abbreviate
  - `precision`: Number of decimal places (default: 1)

## Examples

    iex> NumberF.abbreviate_number(1234)
    "1.2K"

    iex> NumberF.abbreviate_number(1234567)
    "1.2M"

    iex> NumberF.abbreviate_number(1234567890)
    "1.2B"

# `accounting_format`

Accounting format: negatives in parentheses.

## Examples

    iex> NumberF.accounting_format(-1234.56, unit: "$")
    "($1,234.56)"

# `decimal_to_fraction`

Approximates a decimal as a fraction.

# `engineering_notation`

Formats a number in engineering notation (exponents are multiples of three).

## Examples

    iex> NumberF.engineering_notation(0.000123)
    "123.0e-6"

# `format_bytes`

Human-readable byte size. `base: :binary` for KiB/MiB.

## Examples

    iex> NumberF.format_bytes(1_500_000)
    "1.5 MB"

# `format_duration`

Human-readable duration. `format:` `:short`, `:long` or `:clock`.

## Examples

    iex> NumberF.format_duration(3725)
    "1h 2m 5s"

# `format_fraction`

Formats a numerator and denominator as a fraction.

# `format_percentage`

Formats a value as a percentage string.

# `format_phone`

Formats phone numbers based on country code.

## Parameters
  - `number`: The phone number as a string
  - `country_code`: The country code (default: "ZM" for Zambia)

## Examples

    iex> NumberF.format_phone("260977123456", "ZM")
    "+260 97 712 3456"

    iex> NumberF.format_phone("14155552671", "US")
    "+1 (415) 555-2671"

# `format_significant`

Formats to significant figures, keeping trailing zeros.

## Examples

    iex> NumberF.format_significant(1.5, 4)
    "1.500"

# `format_with_units`

Formats a number with a custom prefix and suffix.

# `from_roman`

Converts Roman numerals to Arabic numbers.

## Parameters
  - `roman`: The Roman numeral string

## Examples

    iex> NumberF.from_roman("IV")
    4

    iex> NumberF.from_roman("XLII")
    42

    iex> NumberF.from_roman("MCMXCIX")
    1999

# `number_to_currency`

Formats a number as currency using the low-level formatter.

# `number_to_delimited`

Formats a number into a delimited format with options for customization.

## Parameters
  - `number`: The number to format
  - `options`: Keyword options for formatting:
    - `delimiter`: Character used as thousand delimiter (default: ",")
    - `separator`: Character used as decimal separator (default: ".")
    - `precision`: Number of decimal places (default: 2)

## Examples

    iex> NumberF.number_to_delimited(1234567.89)
    "1,234,567.89"

    iex> NumberF.number_to_delimited(1234567.89, delimiter: ".", separator: ",")
    "1.234.567,89"

    iex> NumberF.number_to_delimited(1234567.89, precision: 0)
    "1,234,568"

# `ordinal`

Converts numbers to ordinals (1st, 2nd, 3rd, etc.).

## Parameters
  - `number`: The number to convert

## Examples

    iex> NumberF.ordinal(1)
    "1st"

    iex> NumberF.ordinal(2)
    "2nd"

    iex> NumberF.ordinal(3)
    "3rd"

    iex> NumberF.ordinal(4)
    "4th"

# `pad_number`

Pads a number to a fixed width.

## Examples

    iex> NumberF.pad_number(42, 6, char: "0")
    "000042"

# `pluralize`

Pairs a count with a correctly pluralised noun.

## Examples

    iex> NumberF.pluralize(3, "person", "people")
    "3 people"

# `scientific_notation`

Formats a number in scientific notation.

## Examples

    iex> NumberF.scientific_notation(0.000123)
    "1.23e-4"

# `significant_figures`

Rounds to a number of significant figures.

## Examples

    iex> NumberF.significant_figures(1234.5678, 3)
    1230.0

# `sum_decimal`

Sums a list of decimal numbers.

## Parameters
  - `list`: A list of decimal values, potentially nested

## Examples

    iex> NumberF.sum_decimal([Decimal.new("1.2"), Decimal.new("3.4"), [Decimal.new("5.6")]])
    Decimal.new("10.2")

    iex> NumberF.sum_decimal([])
    Decimal.new("0")

This function flattens any nested lists, then uses `Enum.reduce/3` to sum all the decimal values.

# `to_boolean`

Converts a string to a boolean value.

## Parameters
  - `value`: The string value to convert. Accepts the following:
    - `true`, `yes`, `on` convert to `true`
    - `false`, `no`, `off` convert to `false`
    - Any other value raises an ArgumentError

## Examples

    iex> NumberF.to_boolean("true")
    true

    iex> NumberF.to_boolean("yes")
    true

    iex> NumberF.to_boolean("false")
    false

    iex> NumberF.to_boolean("no")
    false

# `to_decimal`

Converts a value to a decimal.

## Parameters
  - `value`: The value to convert (string or number)

## Examples

    iex> NumberF.to_decimal("123.45")
    Decimal.new("123.45")

    iex> NumberF.to_decimal(123)
    Decimal.new("123")

# `to_float`

Converts a value to a float.

## Parameters
  - `value`: The value to convert (string or number)

## Examples

    iex> NumberF.to_float("123.45")
    123.45

    iex> NumberF.to_float(123)
    123.0

# `to_int`

Converts a string to an integer.

## Parameters
  - `value`: The string value to convert

## Examples

    iex> NumberF.to_int("123")
    123

    iex> NumberF.to_int("123.45")
    123

# `to_roman`

Converts Arabic numbers to Roman numerals.

## Parameters
  - `number`: The number to convert (1-3999)

## Examples

    iex> NumberF.to_roman(4)
    "IV"

    iex> NumberF.to_roman(42)
    "XLII"

    iex> NumberF.to_roman(1999)
    "MCMXCIX"

# `with_sign`

Prefixes a number with an explicit sign.

## Examples

    iex> NumberF.with_sign(42)
    "+42"

# `comma_separated`

Formats a number into comma-separated format with the specified precision.

## Parameters
  - `number`: The number to format
  - `precision`: Decimal places (default: 2)

## Examples

    iex> NumberF.comma_separated(1234567.89)
    "1,234,567.89"

    iex> NumberF.comma_separated(1234567.89, 0)
    "1,234,568"

    iex> NumberF.comma_separated(nil, 2)
    nil

# `convert_between_currencies`

Converts between two currencies using a rate table.

# `currency`

Formats a number into currency with the specified unit and precision.

## Features
- Automatic digit grouping with thousands separators
- Configurable decimal precision
- Support for custom currency symbols/codes
- Consistent formatting for financial applications

## Parameters
  - `number`: The number to format
  - `unit`: The currency unit (default: "ZMW")
  - `precision`: Decimal places (default: 2)

## Examples

    iex> NumberF.currency(1234.567)
    "ZMW 1,234.57"

    iex> NumberF.currency(1234.567, "USD", 2)
    "USD 1,234.57"

    iex> NumberF.currency(1234567.89, "€", 0)
    "€ 1,234,568"

    iex> NumberF.currency(nil, "USD", 2)
    nil

## Common Use Cases
- Financial reporting and analysis
- E-commerce price displays
- Invoice and receipt generation
- Banking and financial applications

## Related Functions
- `NumberF.comma_separated/2`: Format numbers with commas but no currency
- `NumberF.format_currency/3`: Format with locale-specific currency settings
- `NumberF.number_to_delimited/2`: Format with custom delimiters

See also: `NumberF.Currencies` for currency-specific information.

# `currency_data`

Returns a map of currency information with ISO codes, symbols, and formatting details.

## Examples

    iex> NumberF.currency_data()["USD"]
    %{
      name: "US Dollar",
      symbol: "$",
      symbol_first: true,
      symbol_space: false,
      decimal_places: 2,
      thousands_separator: ",",
      decimal_separator: "."
    }

# `format_with_currency`

Formats a currency with the specified currency code's rules.

## Parameters
  - `number`: The number to format
  - `currency_code`: ISO currency code (e.g., "USD", "EUR")
  - `options`: Additional options (override currency defaults)

## Examples

    iex> NumberF.format_with_currency(1234.56, "USD")
    "$1,234.56"

    iex> NumberF.format_with_currency(1234.56, "EUR")
    "1.234,56 €"

# `get_currency`

Gets currency details for a specific currency code.

## Parameters
  - `currency_code`: ISO currency code (e.g., "USD", "EUR")

## Examples

    iex> NumberF.get_currency("USD")
    %{
      name: "US Dollar",
      symbol: "$",
      symbol_first: true,
      symbol_space: false,
      decimal_places: 2,
      thousands_separator: ",",
      decimal_separator: "."
    }

# `parse_currency`

Parses a currency string into a number.

# `to_words`

Converts a number into words with customizable currency terms.

## Features
- Convert integers and decimals to their word representations
- Support for custom currency terms for main and fractional units
- Handles numbers from zero to trillions
- Properly handles decimal values and zero cases

## Parameters
  - `amount`: The number to convert
  - `main_currency`: The main currency name (default: "Kwacha")
  - `sec_currency`: The secondary currency name (default: "Ngwee")

## Examples

    iex> NumberF.to_words(20.0)
    "Twenty Kwacha"

    iex> NumberF.to_words(42.75, "Dollars", "Cents")
    "Forty Two Dollars And Seventy Five Cents"

    iex> NumberF.to_words(1234567.89, "Euros", "Cents")
    "One Million Two Hundred Thirty Four Thousand Five Hundred Sixty Seven Euros And Eighty Nine Cents"

    iex> NumberF.to_words(0, "Euros", "Cents")
    "zero Euros"

## Common Use Cases
- Check writing and financial documents
- Legal documents requiring numeric values in words
- Invoices and receipts
- Educational applications

## Related Functions
- `NumberF.spell_number/3`: Spell numbers in different languages
- `NumberF.to_roman/1`: Convert to Roman numerals
- `NumberF.ordinal/1`: Convert to ordinal forms (1st, 2nd, 3rd)

See also: `NumberF.NumbersToWords` for more advanced word conversion options.

# `amortization_schedule`

Full amortization schedule for a loan, one entry per period.

## Examples

    iex> NumberF.amortization_schedule(100_000, 0.10, 12) |> List.last() |> Map.get(:balance)
    0.0

# `annuity_future_value`

Future value of a stream of equal payments.

## Examples

    iex> NumberF.annuity_future_value(100, 0.05, 10)
    1257.79

# `annuity_payment`

Payment required to amortise a present value over n periods.

## Examples

    iex> NumberF.annuity_payment(10_000, 0.05, 10)
    1295.05

# `annuity_present_value`

Present value of a stream of equal payments.

## Examples

    iex> NumberF.annuity_present_value(100, 0.05, 10)
    772.17

# `break_even_point`

Units that must be sold to cover fixed costs.

## Examples

    iex> NumberF.break_even_point(10_000, 25, 15)
    1000.0

# `cagr`

Compound annual growth rate, as a percentage.

## Examples

    iex> NumberF.cagr(1000, 2000, 5)
    14.87

# `calculate_emi`

Calculates Equated Monthly Installment (EMI) for loans.

## Parameters
  - `principal`: The loan amount
  - `rate`: The annual interest rate as a decimal (e.g., 0.05 for 5%)
  - `term_months`: The loan term in months

## Examples

    iex> NumberF.calculate_emi(100000, 0.10, 12)
    8791.59

# `compound_interest`

Calculates compound interest with optional compounding frequency.

## Features
- Support for different compounding frequencies (annual, semi-annual, quarterly, monthly, etc.)
- Precise calculations using Elixir's floating-point operations
- Results rounded to 2 decimal places by default
- Guard clauses ensure valid inputs

## Parameters
  - `principal`: The principal amount
  - `rate`: The annual interest rate as a decimal (e.g., 0.05 for 5%)
  - `time`: The time period in years
  - `frequency`: Number of times interest is compounded per year (default: 1)

## Examples

    iex> NumberF.compound_interest(1000, 0.05, 2)
    102.5  # Annual compounding (1 time per year)

    iex> NumberF.compound_interest(1000, 0.05, 2, 12)
    104.94  # Monthly compounding (12 times per year)

    iex> NumberF.compound_interest(10000, 0.08, 5, 4)
    4859.47  # Quarterly compounding (4 times per year)

## Formula
The compound interest is calculated using the formula:
```
A = P(1 + r/n)^(nt) - P
```
Where:
- A = Interest amount
- P = Principal
- r = Annual interest rate (decimal)
- n = Compounding frequency
- t = Time in years

## Common Use Cases
- Investment calculations
- Loan and mortgage analysis
- Retirement planning
- Financial education tools

## Related Functions
- `NumberF.simple_interest/3`: Calculate simple interest
- `NumberF.calculate_emi/3`: Calculate equated monthly installments
- `NumberF.Tax.calculate_capital_gains_tax/3`: Calculate capital gains tax

See also: `NumberF.Financial` for more financial calculations.

# `convert_currency`

Converts an amount between currencies based on exchange rates.

## Parameters
  - `amount`: The amount to convert
  - `from_rate`: The exchange rate of the source currency
  - `to_rate`: The exchange rate of the target currency

## Examples

    iex> NumberF.convert_currency(100, 1, 1.1)
    110.0

# `depreciation_declining_balance`

Declining-balance depreciation schedule. `factor` defaults to 2.

## Examples

    iex> NumberF.depreciation_declining_balance(10_000, 1_000, 5) |> hd()
    %{year: 1, depreciation: 4000.0, book_value: 6000.0}

# `depreciation_straight_line`

Straight-line depreciation per year.

## Examples

    iex> NumberF.depreciation_straight_line(10_000, 1_000, 5)
    1800.0

# `effective_annual_rate`

Effective annual rate for a nominal rate compounded n times per year.

## Examples

    iex> NumberF.effective_annual_rate(0.12, 12)
    0.1268

# `future_value`

Future value of a present sum.

## Examples

    iex> NumberF.future_value(1000, 0.05, 10)
    1628.89

# `irr`

Internal rate of return. Returns `{:ok, rate}` or an error tuple.

## Examples

    iex> {:ok, rate} = NumberF.irr([-1000, 300, 400, 500, 600])
    iex> Float.round(rate, 4)
    0.2489

# `loan_payment`

Level loan payment. Unlike `calculate_emi/3`'s history, safe at a zero rate.

## Examples

    iex> NumberF.loan_payment(100_000, 0.0, 12)
    8333.33

# `npv`

Net present value of a series of cash flows, the first at t=0.

## Examples

    iex> NumberF.npv(0.1, [-1000, 300, 400, 500, 600])
    388.77

# `payback_period`

Years until cumulative cash flows repay an initial investment.

## Examples

    iex> NumberF.payback_period(1000, [300, 400, 500])
    {:ok, 2.6}

# `present_value`

Present value of a future sum.

## Examples

    iex> NumberF.present_value(1000, 0.05, 10)
    613.91

# `roi`

Return on investment, as a percentage.

## Examples

    iex> NumberF.roi(1500, 1000)
    50.0

# `simple_interest`

Calculates simple interest based on principal, rate and time.

## Parameters
  - `principal`: The principal amount
  - `rate`: The annual interest rate as a decimal (e.g., 0.05 for 5%)
  - `time`: The time period in years

## Examples

    iex> NumberF.simple_interest(1000, 0.05, 2)
    100.0

# `calculate_capital_gains_tax`

Calculates capital gains tax on a gain.

# `calculate_corporate_tax`

Calculates corporate tax on a profit.

# `calculate_income_tax`

Calculates progressive income tax against a bracket table.

## Examples

    iex> brackets = NumberF.income_tax_brackets()["US"]
    iex> result = NumberF.calculate_income_tax(75_000, brackets)
    iex> is_number(result.tax) and is_number(result.effective_rate)
    true

# `calculate_payroll_tax`

Calculates employee and employer payroll contributions.

# `calculate_sales_tax`

Calculates sales tax for a given amount and rate.

## Parameters
  - `amount`: The amount before tax
  - `rate`: The sales tax rate as a decimal (e.g., 0.06 for 6%)
  - `options`: Additional options
    - `:round_to`: Round the tax amount to the nearest value (default: 0.01)

## Examples

    iex> NumberF.calculate_sales_tax(100, 0.06)
    %{subtotal: 100.0, tax: 6.0, total: 106.0}

# `calculate_vat`

Calculates Value Added Tax (VAT) for a given amount and rate.

## Parameters
  - `amount`: The amount before tax
  - `rate`: The VAT rate as a decimal (e.g., 0.2 for 20%)
  - `included`: Whether the amount already includes VAT (default: false)

## Examples

    iex> NumberF.calculate_vat(100, 0.2)
    %{net: 100.0, vat: 20.0, gross: 120.0}

    iex> NumberF.calculate_vat(120, 0.2, true)
    %{net: 100.0, vat: 20.0, gross: 120.0}

# `calculate_withholding_tax`

Calculates withholding tax on a gross amount.

# `income_tax_brackets`

Reference income tax brackets by country.

These are reference data, not legal advice: rates change and jurisdictions differ.
Supply your own brackets for anything with financial consequence.

## Examples

    iex> NumberF.income_tax_brackets() |> Map.keys() |> Enum.sort()
    ["Germany", "UK", "US", "Zambia"]

# `vat_rates`

Returns common VAT rates for different countries.

## Examples

    iex> NumberF.vat_rates()["UK"]
    0.2

    iex> NumberF.vat_rates()["Germany"]
    0.19

# `coefficient_of_variation`

Standard deviation as a proportion of the mean — a unitless measure of spread.

## Examples

    iex> NumberF.coefficient_of_variation([2, 4, 4, 4, 5, 5, 7, 9])
    0.4

# `correlation`

Pearson correlation coefficient between two equal-length lists, in -1..1.

## Examples

    iex> NumberF.correlation([1, 2, 3, 4, 5], [2, 4, 5, 4, 5])
    0.7745966692414833

# `covariance`

Population covariance of two equal-length lists.

## Examples

    iex> NumberF.covariance([1, 2, 3, 4, 5], [2, 4, 5, 4, 5])
    1.2

# `cumulative_sum`

Running total of a list.

## Examples

    iex> NumberF.cumulative_sum([1, 2, 3, 4])
    [1, 3, 6, 10]

# `frequency_distribution`

Counts how often each value occurs.

## Examples

    iex> NumberF.frequency_distribution([1, 2, 2, 3, 3, 3])
    %{1 => 1, 2 => 2, 3 => 3}

# `geometric_mean`

Geometric mean — the correct average for growth rates and ratios.

## Examples

    iex> NumberF.geometric_mean([1, 4, 16]) |> Float.round(4)
    4.0

# `harmonic_mean`

Harmonic mean — the correct average for rates over a fixed distance.

## Examples

    iex> NumberF.harmonic_mean([40, 60]) |> Float.round(6)
    48.0

# `iqr`

Interquartile range — the spread of the middle half of the data.

## Examples

    iex> NumberF.iqr([1, 2, 3, 4, 5, 6, 7, 8])
    3.5

# `linear_regression`

Least-squares linear regression, returning slope, intercept and r-squared.

## Examples

    iex> NumberF.linear_regression([1, 2, 3, 4, 5], [2, 4, 5, 4, 5]).slope
    0.6

# `mean`

Calculates the arithmetic mean of a list of numbers.

## Features
- Handles lists of any size
- Returns nil for empty lists
- Works with integers and floating-point numbers
- Accurately calculates average using sum and count

## Parameters
  - `numbers`: A list of numbers

## Examples

    iex> NumberF.mean([1, 2, 3, 4, 5])
    3.0

    iex> NumberF.mean([1.5, 2.5, 3.5])
    2.5

    iex> NumberF.mean([42])
    42.0

    iex> NumberF.mean([])
    nil

## Mathematical Definition
The arithmetic mean is the sum of all values divided by the number of values.

## Common Use Cases
- Data analysis and statistics
- Financial analysis (average returns, costs, etc.)
- Scientific calculations
- Performance metrics

## Related Functions
- `NumberF.median/1`: Calculate the median value
- `NumberF.mode/1`: Find the most frequent value(s)
- `NumberF.standard_deviation/1`: Calculate standard deviation
- `NumberF.variance/1`: Calculate variance

See also: `NumberF.Statistics` for more statistical functions.

# `median`

Finds the median value from a list of numbers.

## Parameters
  - `numbers`: A list of numbers

## Examples

    iex> NumberF.median([1, 3, 5, 7, 9])
    5

    iex> NumberF.median([1, 3, 5, 7])
    4.0

# `mode`

Finds the most frequently occurring value(s) in a list.

## Parameters
  - `numbers`: A list of numbers

## Examples

    iex> NumberF.mode([1, 2, 2, 3, 3, 3, 4])
    [3]

    iex> NumberF.mode([1, 1, 2, 2, 3])
    [1, 2]

# `moving_average`

Simple moving average over a sliding window.

## Examples

    iex> NumberF.moving_average([1, 2, 3, 4, 5], 3)
    [2.0, 3.0, 4.0]

# `normalize`

Rescales a list to the 0..1 range (min-max normalisation).

> #### Not a fitted scaler {: .warning}
>
> This computes its statistics from the list you pass in, so it has no
> fit/transform split. Scaling a test set with it does **not** apply the
> training set's scaler — it fits a new one from the test data, silently
> corrupting the features and leaking test-set statistics.
>
> For a machine-learning pipeline use `Scholar.Preprocessing.StandardScaler`,
> whose `fit/2` and `transform/2` are separate for exactly this reason. This
> function is for exploratory work on a single dataset.

## Examples

    iex> NumberF.normalize([10, 20, 30])
    [0.0, 0.5, 1.0]

# `outliers`

Values falling outside the expected spread. `method: :iqr` (default) or `:zscore`.

## Examples

    iex> NumberF.outliers([1, 2, 3, 4, 1000])
    [1000]

# `percentile`

Returns the value at the given percentile (0..100), by linear interpolation.

## Examples

    iex> NumberF.percentile([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 25)
    3.25

# `quartiles`

Returns the first, second and third quartiles.

## Examples

    iex> NumberF.quartiles([1, 2, 3, 4, 5, 6, 7, 8])
    %{q1: 2.75, q2: 4.5, q3: 6.25}

# `range`

Calculates the range (difference between max and min values) of a dataset.

## Parameters
  - `numbers`: A list of numbers

## Examples

    iex> NumberF.range([2, 4, 4, 4, 5, 5, 7, 9])
    7.0

# `remove_outliers`

The list with its outliers removed.

## Examples

    iex> NumberF.remove_outliers([1, 2, 3, 4, 1000])
    [1, 2, 3, 4]

# `sample_standard_deviation`

Calculates the **sample** standard deviation of a dataset (divides by N-1).

See `sample_variance/1`. `NumberF.standard_deviation/1` is the population form.

## Examples

    iex> NumberF.sample_standard_deviation([2, 4, 4, 4, 5, 5, 7, 9])
    2.138089935299395

# `sample_variance`

Calculates the **sample** variance of a dataset (divides by N-1).

Use this when the list is a sample rather than the whole population.
`NumberF.variance/1` is the population form.

## Precision

The two differ by a factor of `N/(N-1)`, so they converge as the dataset grows
but diverge sharply for small lists. Most statistical software defaults to the
sample form; this library keeps `variance/1` as the population form for
backwards compatibility, so pick one explicitly.

## Examples

    iex> NumberF.sample_variance([2, 4, 4, 4, 5, 5, 7, 9])
    4.571428571428571

# `standard_deviation`

Calculates standard deviation of a dataset.

## Parameters
  - `numbers`: A list of numbers

## Examples

    iex> NumberF.standard_deviation([2, 4, 4, 4, 5, 5, 7, 9])
    2.0

# `summary`

A one-call description of a dataset: count, min, quartiles, max, mean, stddev.

## Examples

    iex> NumberF.summary([1, 2, 3, 4, 5]).median
    3

# `trimmed_mean`

Mean after discarding a proportion from each end — robust to unreliable extremes.

## Examples

    iex> NumberF.trimmed_mean([1, 2, 3, 4, 100], 0.2)
    3.0

# `variance`

Calculates the variance of a dataset.

## Parameters
  - `numbers`: A list of numbers

## Examples

    iex> NumberF.variance([2, 4, 4, 4, 5, 5, 7, 9])
    4.0

# `weighted_mean`

Mean weighted by a matching list of weights.

## Examples

    iex> NumberF.weighted_mean([90, 80, 70], [0.5, 0.3, 0.2])
    83.0

# `z_score`

How many standard deviations a value sits from the mean.

## Examples

    iex> NumberF.z_score(6, 4, 2)
    1.0

# `z_scores`

Standardises a list to zero mean and unit variance.

> #### Not a fitted scaler {: .warning}
>
> This computes its statistics from the list you pass in, so it has no
> fit/transform split. Scaling a test set with it does **not** apply the
> training set's scaler — it fits a new one from the test data, silently
> corrupting the features and leaking test-set statistics.
>
> For a machine-learning pipeline use `Scholar.Preprocessing.StandardScaler`,
> whose `fit/2` and `transform/2` are separate for exactly this reason. This
> function is for exploratory work on a single dataset.

## Examples

    iex> NumberF.z_scores([2, 4, 6])
    [-1.224744871391589, 0.0, 1.224744871391589]

# `approximately_equal`

Checks if two floating point numbers are approximately equal.

## Parameters
  - `a`: First number
  - `b`: Second number
  - `epsilon`: Maximum allowed difference (default: 1.0e-10)

## Examples

    iex> NumberF.approximately_equal(0.1 + 0.2, 0.3)
    true

    iex> NumberF.approximately_equal(0.1, 0.2)
    false

# `bankers_round`

Rounds a number using banker's rounding (round to even).
This is more statistically unbiased than standard rounding.

## Parameters
  - `number`: The number to round
  - `precision`: Number of decimal places (default: 2)

## Examples

    iex> NumberF.bankers_round(2.5, 0)
    2.0

    iex> NumberF.bankers_round(3.5, 0)
    4.0

# `ceiling`

Rounds a number up to a specified precision (ceiling).

## Parameters
  - `number`: The number to round
  - `precision`: Number of decimal places (default: 2)

## Examples

    iex> NumberF.ceiling(3.14159, 2)
    3.15

    iex> NumberF.ceiling(3.14159, 1)
    3.2

# `clamp`

Constrains a value to a range.

## Examples

    iex> NumberF.clamp(15, 0, 10)
    10

    iex> NumberF.clamp(-5, 0, 10)
    0

# `custom_round`

Rounds to a precision using an explicit mode.

Modes: `:half_up` (default), `:half_down`, `:half_even`, `:ceiling`, `:floor`,
`:truncate`.

## Examples

    iex> NumberF.custom_round(2.565, 2, :half_down)
    2.56

# `floor`

Rounds a number down to a specified precision (floor).

## Parameters
  - `number`: The number to round
  - `precision`: Number of decimal places (default: 2)

## Examples

    iex> NumberF.floor(3.14159, 2)
    3.14

    iex> NumberF.floor(3.14159, 1)
    3.1

# `precise_format`

Formats a number to a fixed number of decimal places, padding with zeros.

Unlike `round_to/3`, trailing zeros are preserved, so the width is stable.

## Examples

    iex> NumberF.precise_format(3.14159, 3)
    "3.142"

# `round_half_away_from_zero`

Rounds half away from zero — "round half up" as most people mean it.

Contrast `bankers_round/2`, which rounds half to even to avoid upward bias.

## Examples

    iex> NumberF.round_half_away_from_zero(-2.5, 0)
    -3.0

# `round_half_even`

Rounds half to even. A discoverable alias for `bankers_round/2`.

## Examples

    iex> NumberF.round_half_even(2.5, 0)
    2.0

# `round_to`

Rounds a number to a specific increment.

## Parameters
  - `number`: The number to round
  - `increment`: The increment to round to (default: 1.0)
  - `strategy`: The rounding strategy (:nearest, :up, :down, or :bankers)

## Examples

    iex> NumberF.round_to(3.14159, 0.05, :nearest)
    3.15

    iex> NumberF.round_to(3.14159, 0.1, :up)
    3.2

    iex> NumberF.round_to(3.14159, 0.1, :down)
    3.1

# `round_with_precision`

Rounds a number to a specified number of decimal places.

## Parameters
  - `number`: The number to round
  - `precision`: Number of decimal places (default: 2)

## Examples

    iex> NumberF.round_with_precision(3.14159, 2)
    3.14

    iex> NumberF.round_with_precision(3.14159, 4)
    3.1416

# `safe_divide`

Divides, returning `default` instead of raising when the denominator is zero.

## Examples

    iex> NumberF.safe_divide(10, 0)
    0.0

    iex> NumberF.safe_divide(10, 4)
    2.5

# `sanitize_float`

Replaces non-finite float values with a safe default.

## Examples

    iex> NumberF.sanitize_float(:nan)
    0.0

# `sign`

Returns -1, 0 or 1 according to the sign of the number.

## Examples

    iex> NumberF.sign(-4.2)
    -1

# `truncate`

Truncates a number to a precision, cutting digits rather than rounding.

## Examples

    iex> NumberF.truncate(3.999, 2)
    3.99

# `card_brand`

Identifies a card network from its number.

## Examples

    iex> NumberF.card_brand("4111111111111111")
    :visa

# `is_valid_credit_card?`

Validates credit card numbers using the Luhn algorithm.

## Parameters
  - `number`: The credit card number as a string
  - `type`: Card type to validate against (default: :any)
    - Options: :any, :visa, :mastercard, :amex, :discover

## Examples

    iex> NumberF.is_valid_credit_card?("4111111111111111")
    true

    iex> NumberF.is_valid_credit_card?("4111111111111112")
    false

# `is_valid_integer?`

Checks if a string is a valid integer.

## Parameters
  - `str`: The string to check

## Examples

    iex> NumberF.is_valid_integer?("123")
    true

    iex> NumberF.is_valid_integer?("123.45")
    false

# `is_valid_number?`

Checks if a string is a valid number format.

## Parameters
  - `str`: The string to check

## Examples

    iex> NumberF.is_valid_number?("123")
    true

    iex> NumberF.is_valid_number?("123.45")
    true

    iex> NumberF.is_valid_number?("abc")
    false

# `luhn_check_digit`

Computes the Luhn check digit for a number.

## Examples

    iex> NumberF.luhn_check_digit("411111111111111")
    1

# `valid_currency_code?`

Whether the code is a currency this library knows.

## Examples

    iex> NumberF.valid_currency_code?("USD")
    true

# `valid_ean?`

Validates an EAN-8 or EAN-13 barcode.

## Examples

    iex> NumberF.valid_ean?("4006381333931")
    true

# `valid_iban?`

Validates an IBAN by its mod-97 checksum.

## Examples

    iex> NumberF.valid_iban?("GB82 WEST 1234 5698 7654 32")
    true

# `valid_imei?`

Validates a 15-digit IMEI.

## Examples

    iex> NumberF.valid_imei?("490154203237518")
    true

# `valid_isbn?`

Validates an ISBN-10 or ISBN-13.

## Examples

    iex> NumberF.valid_isbn?("978-3-16-148410-0")
    true

# `valid_luhn?`

Validates any Luhn-checksummed number.

## Examples

    iex> NumberF.valid_luhn?("4111111111111111")
    true

# `valid_percentage?`

Whether a value is a number in 0..100.

## Examples

    iex> NumberF.valid_percentage?(42.5)
    true

# `valid_routing_number?`

Validates a US ABA routing number.

## Examples

    iex> NumberF.valid_routing_number?("021000021")
    true

# `valid_upc?`

Validates a UPC-A barcode.

## Examples

    iex> NumberF.valid_upc?("036000291452")
    true

# `combinations`

Calculates combinations (n choose k).

## Parameters
  - `n`: The total number of items
  - `k`: The number of items to choose

## Examples

    iex> NumberF.combinations(5, 2)
    10

    iex> NumberF.combinations(10, 3)
    120

# `digit_sum`

Sum of the decimal digits.

## Examples

    iex> NumberF.digit_sum(12345)
    15

# `digital_root`

Repeatedly sums the digits until one remains.

## Examples

    iex> NumberF.digital_root(12345)
    6

# `digits`

The decimal digits of an integer.

## Examples

    iex> NumberF.digits(12345)
    [1, 2, 3, 4, 5]

# `divisors`

All positive divisors of n, ascending.

## Examples

    iex> NumberF.divisors(28)
    [1, 2, 4, 7, 14, 28]

# `factorial`

Calculates the factorial of a non-negative integer.

## Parameters
  - `n`: The non-negative integer

## Examples

    iex> NumberF.factorial(5)
    120

    iex> NumberF.factorial(0)
    1

# `fibonacci`

The nth Fibonacci number, exact for any n.

## Examples

    iex> NumberF.fibonacci(10)
    55

# `fibonacci_sequence`

The first n Fibonacci numbers.

## Examples

    iex> NumberF.fibonacci_sequence(8)
    [0, 1, 1, 2, 3, 5, 8, 13]

# `from_base`

Parses an integer written in a base from 2 to 36.

## Examples

    iex> NumberF.from_base("FF", 16)
    255

# `from_binary`

Parses a binary integer.

## Examples

    iex> NumberF.from_binary("101")
    5

# `from_hex`

Parses a hexadecimal integer.

## Examples

    iex> NumberF.from_hex("FF")
    255

# `from_octal`

Parses an octal integer.

## Examples

    iex> NumberF.from_octal("10")
    8

# `gcd`

Calculates the Greatest Common Divisor (GCD) of two integers.

## Parameters
  - `a`: First integer
  - `b`: Second integer

## Examples

    iex> NumberF.gcd(48, 18)
    6

    iex> NumberF.gcd(7, 13)
    1

# `in_range?`

Checks if a number is within a specified range (inclusive).

## Parameters
  - `value`: The number to check
  - `min`: The minimum value of the range
  - `max`: The maximum value of the range

## Examples

    iex> NumberF.in_range?(5, 1, 10)
    true

    iex> NumberF.in_range?(15, 1, 10)
    false

# `interpolate`

Performs a linear interpolation between two points.

## Parameters
  - `x`: The x value to interpolate at
  - `x0`: The x coordinate of the first point
  - `y0`: The y coordinate of the first point
  - `x1`: The x coordinate of the second point
  - `y1`: The y coordinate of the second point

## Examples

    iex> NumberF.interpolate(2.5, 2, 10, 3, 20)
    15.0

# `is_prime?`

Checks if a number is prime.

## Parameters
  - `n`: The number to check

## Examples

    iex> NumberF.is_prime?(7)
    true

    iex> NumberF.is_prime?(6)
    false

# `lcm`

Calculates the Least Common Multiple (LCM) of two integers.

## Parameters
  - `a`: First integer
  - `b`: Second integer

## Examples

    iex> NumberF.lcm(4, 6)
    12

    iex> NumberF.lcm(21, 6)
    42

# `log_base`

Logarithm in an arbitrary base.

## Examples

    iex> NumberF.log_base(1024, 2)
    10.0

# `nth_root`

The real nth root of a number.

## Examples

    iex> NumberF.nth_root(27, 3)
    3.0

# `palindrome?`

Whether the digits read the same both ways.

## Examples

    iex> NumberF.palindrome?(12321)
    true

# `percentage`

Calculates a percentage with specified precision.

## Parameters
  - `value`: The value to calculate percentage for
  - `total`: The total value (100%)
  - `precision`: Number of decimal places (default: 2)

## Examples

    iex> NumberF.percentage(25, 100)
    25.0

    iex> NumberF.percentage(1, 3, 2)
    33.33

# `perfect_square?`

Whether n is a perfect square, without float error.

## Examples

    iex> NumberF.perfect_square?(144)
    true

# `permutations`

Ordered arrangements of k items from n (nPk).

## Examples

    iex> NumberF.permutations(5, 2)
    20

# `prime_factors`

Prime factorisation, ascending and with repeats.

## Examples

    iex> NumberF.prime_factors(360)
    [2, 2, 2, 3, 3, 5]

# `reverse_number`

The integer with its digits reversed.

## Examples

    iex> NumberF.reverse_number(12345)
    54321

# `round_to_nearest`

Rounds a number to the nearest specified value.

## Parameters
  - `value`: The number to round
  - `nearest`: The nearest value to round to (default: 1.0)

## Examples

    iex> NumberF.round_to_nearest(12.3)
    12.0

    iex> NumberF.round_to_nearest(12.3, 5)
    10.0

    iex> NumberF.round_to_nearest(12.3, 0.5)
    12.5

# `to_base`

Renders an integer in a base from 2 to 36.

## Examples

    iex> NumberF.to_base(255, 16)
    "FF"

# `to_binary`

Renders an integer in binary.

## Examples

    iex> NumberF.to_binary(5)
    "101"

# `to_degrees`

Converts radians to degrees.

## Parameters
  - `radians`: The angle in radians

## Examples

    iex> NumberF.to_degrees(3.14159)
    179.99984796050427

# `to_hex`

Renders an integer in hexadecimal.

## Examples

    iex> NumberF.to_hex(255)
    "FF"

# `to_octal`

Renders an integer in octal.

## Examples

    iex> NumberF.to_octal(8)
    "10"

# `to_radians`

Converts degrees to radians.

## Parameters
  - `degrees`: The angle in degrees

## Examples

    iex> NumberF.to_radians(180)
    3.141592653589793

# `acres_to_hectares`

Converts acres to hectares.

# `area_units`

Area conversion factors, relative to square metres.

# `celsius_to_fahrenheit`

Converts Celsius to Fahrenheit.

## Parameters
  - `celsius`: The temperature in Celsius

## Examples

    iex> NumberF.celsius_to_fahrenheit(0)
    32.0

    iex> NumberF.celsius_to_fahrenheit(100)
    212.0

# `cm_to_inches`

Converts centimeters to inches.

## Parameters
  - `cm`: The length in centimeters

## Examples

    iex> NumberF.cm_to_inches(25.4)
    10.0

# `convert_units`

Converts between arbitrary units from one of the unit tables.

Unit keys are strings, not atoms.

## Examples

    iex> NumberF.convert_units(100, "km", "mi", NumberF.length_units()) |> Float.round(2)
    62.14

# `fahrenheit_to_celsius`

Converts Fahrenheit to Celsius.

## Parameters
  - `fahrenheit`: The temperature in Fahrenheit

## Examples

    iex> NumberF.fahrenheit_to_celsius(32)
    0.0

    iex> NumberF.fahrenheit_to_celsius(212)
    100.0

# `hectares_to_acres`

Converts hectares to acres.

# `inches_to_cm`

Converts inches to centimeters.

## Parameters
  - `inches`: The length in inches

## Examples

    iex> NumberF.inches_to_cm(10)
    25.4

    iex> NumberF.inches_to_cm(3.5)
    8.89

# `kg_to_pounds`

Converts kilograms to pounds.

## Parameters
  - `kg`: The weight in kilograms

## Examples

    iex> NumberF.kg_to_pounds(4.54)
    10.01

# `km_to_miles`

Converts kilometers to miles.

## Parameters
  - `km`: The distance in kilometers

## Examples

    iex> NumberF.km_to_miles(16.09)
    10.0

# `length_units`

Length conversion factors, relative to metres.

# `miles_to_km`

Converts miles to kilometers.

## Parameters
  - `miles`: The distance in miles

## Examples

    iex> NumberF.miles_to_km(10)
    16.09

# `ml_to_oz`

Converts millilitres to fluid ounces.

# `oz_to_ml`

Converts fluid ounces to millilitres.

# `pounds_to_kg`

Converts pounds to kilograms.

# `volume_units`

Volume conversion factors, relative to litres.

# `weight_units`

Weight conversion factors, relative to kilograms.

# `add_business_days`

Adds business days to a date. A negative count subtracts them.

## Examples

    iex> NumberF.add_business_days(~D[2024-01-15], -3)
    ~D[2024-01-10]

# `age_in_months`

Age in whole months.

## Examples

    iex> NumberF.age_in_months(~D[2020-01-15], ~D[2024-08-22])
    55

# `business_days_between`

Calculates the number of business days between two dates.

## Parameters
  - `date1`: The first date
  - `date2`: The second date

## Examples

    iex> NumberF.business_days_between(~D[2023-01-02], ~D[2023-01-08])
    5

# `calculate_age`

Calculates age based on birth date.

## Parameters
  - `birth_date`: Birth date as Date struct

## Examples

    iex> birth_date = ~D[1990-01-15]
    iex> age = NumberF.calculate_age(birth_date)
    iex> is_integer(age) and age >= 0
    true

# `days_between`

Calculates the number of days between two dates.

## Parameters
  - `date1`: The first date
  - `date2`: The second date

## Examples

    iex> NumberF.days_between(~D[2023-01-01], ~D[2023-01-10])
    9

# `days_in_month`

Number of days in a month.

## Examples

    iex> NumberF.days_in_month(2024, 2)
    29

# `fiscal_year`

Fiscal year containing the date, named for the year it ends in.

## Examples

    iex> NumberF.fiscal_year(~D[2024-08-22], 4)
    2025

# `is_business_day?`

Determines if a date is a business day.

## Parameters
  - `date`: The date to check

## Examples

    iex> NumberF.is_business_day?(~D[2023-01-02])
    true

    iex> NumberF.is_business_day?(~D[2023-01-01])
    false

# `leap_year?`

Whether a year is a leap year.

## Examples

    iex> NumberF.leap_year?(2024)
    true

# `month_end`

Last day of the date's month.

## Examples

    iex> NumberF.month_end(~D[2024-02-10])
    ~D[2024-02-29]

# `month_start`

First day of the date's month.

## Examples

    iex> NumberF.month_start(~D[2024-08-22])
    ~D[2024-08-01]

# `next_business_day`

Returns the next business day after the given date.

## Examples

    iex> NumberF.next_business_day(~D[2024-01-12])
    ~D[2024-01-15]

# `payment_due_date`

Calculates payment due date based on invoice date and terms.

## Parameters
  - `invoice_date`: The invoice date as Date struct
  - `terms_days`: Payment terms in days (default: 30)

## Examples

    iex> invoice_date = ~D[2023-01-15]
    iex> NumberF.payment_due_date(invoice_date)
    ~D[2023-02-14]

    iex> invoice_date = ~D[2023-01-15]
    iex> NumberF.payment_due_date(invoice_date, 45)
    ~D[2023-03-01]

# `quarter`

Calendar quarter of a date, 1 to 4.

## Examples

    iex> NumberF.quarter(~D[2024-08-22])
    3

# `quarter_end`

Last day of the date's quarter.

## Examples

    iex> NumberF.quarter_end(~D[2024-08-22])
    ~D[2024-09-30]

# `quarter_start`

First day of the date's quarter.

## Examples

    iex> NumberF.quarter_start(~D[2024-08-22])
    ~D[2024-07-01]

# `week_number`

ISO-8601 week number.

## Examples

    iex> NumberF.week_number(~D[2024-08-22])
    34

# `default_password`

Generates a default password with pre-defined complexity.
The pattern is: capitalized 3-letter string + @ + 4 random digits.

## Examples

    iex> result = NumberF.default_password()
    iex> String.length(result) == 8 and String.contains?(result, "@")
    true

# `memory_size_cal`

Converts a memory size in bytes to a human-readable format.
Automatically selects the appropriate unit (B, KB, MB, GB) based on size.

## Parameters
  - `size`: The size in bytes

## Examples

    iex> NumberF.memory_size_cal(500)
    "500 B"

    iex> NumberF.memory_size_cal(1024)
    "1.0 KB"

    iex> NumberF.memory_size_cal(1048576)
    "1.0 MB"

    iex> NumberF.memory_size_cal(1073741824)
    "1.0 GB"

# `randomizer`

Generates a random string of the specified length.

## Parameters
  - `length`: The length of the string
  - `type`: Type of string, with options:
    - `:all` (alphanumeric) - default
    - `:alpha` (alphabetical)
    - `:numeric` (numbers)
    - `:upcase` (uppercase)
    - `:downcase` (lowercase)

## Examples

    iex> result = NumberF.randomizer(10)
    iex> String.length(result)
    10

    iex> result = NumberF.randomizer(5, :numeric)
    iex> String.length(result) == 5 and String.match?(result, ~r/^[0-9]+$/)
    true

    iex> result = NumberF.randomizer(6, :upcase)
    iex> String.length(result) == 6 and Regex.match?(~r/^[A-Z]+$/, result)
    true

# `format_currency`

Formats a number as currency according to locale-specific settings.

## Features
- Supports 100+ international locale formats
- Proper positioning of currency symbols
- Correct thousands and decimal separators for each locale
- Configurable precision and currency code

## Parameters
  - `number`: The number to format
  - `locale`: The locale code (e.g., "en-US", "fr-FR")
  - `options`: Additional formatting options
    - `:precision`: Number of decimal places (default: 2)
    - `:currency_code`: ISO currency code to override the locale default
    - `:symbol`: Whether to include the currency symbol (default: true)

## Examples

    iex> NumberF.format_currency(1234.56, "en-US")
    "$1,234.56"

    iex> NumberF.format_currency(1234.56, "fr-FR")
    "1 234,56 €"

    iex> NumberF.format_currency(1234.56, "de-DE", currency_code: "USD")
    "1.234,56 $"

    iex> NumberF.format_currency(1234.56, "en-US", symbol: false)
    "1,234.56"

## Supported Locales
This function supports all major world locales including but not limited to:
en-US, en-GB, fr-FR, de-DE, es-ES, it-IT, ja-JP, zh-CN, ru-RU, pt-BR, and many more.

## Common Use Cases
- Multi-language applications
- Financial applications with international users
- E-commerce with global customers
- Travel and currency conversion applications

## Related Functions
- `NumberF.format_number/3`: Format without currency symbols
- `NumberF.I18n.get_locale_settings/1`: Get formatting rules for a locale
- `NumberF.spell_number/3`: Spell out numbers in different languages

See also: `NumberF.I18n` for more internationalization functions.

# `format_number`

Formats a number according to locale-specific settings.

## Parameters
  - `number`: The number to format
  - `locale`: The locale code (e.g., "en-US", "fr-FR")
  - `options`: Additional formatting options
    - `:precision`: Number of decimal places (default: 2)

## Examples

    iex> NumberF.format_number(1234567.89, "en-US")
    "1,234,567.89"

    iex> NumberF.format_number(1234567.89, "fr-FR")
    "1 234 567,89"

# `get_currency_names`

Returns the main and sub unit names for a currency in a given language.

# `get_locale_settings`

Returns the formatting settings for a locale.

## Examples

    iex> NumberF.get_locale_settings("en-US").currency_symbol
    "$"

# `parse_number`

Parses a locale-formatted number string back into a number.

## Examples

    iex> NumberF.parse_number("1.234,56", "de-DE")
    1234.56

# `spell_number`

Spells out a number in the specified language.

## Parameters
  - `number`: The number to spell out
  - `language`: The language code (e.g., "en", "fr")
  - `options`: Additional options
    - `:capitalize`: Whether to capitalize the first letter (default: true)
    - `:currency`: Whether to add currency names (default: false)
    - `:currency_code`: ISO currency code (default: nil)

## Examples

    iex> NumberF.spell_number(42, "en")
    "Forty-two"

    iex> NumberF.spell_number(42, "fr")
    "Quarante-deux"

# `spelling_languages`

Lists the languages `NumberF.spell_number/3` can spell numbers in.

Deliberately distinct from the 22 locales `NumberF.format_number/2` supports:
formatting a locale and spelling its language are different capabilities, and
conflating them is how an unsupported language used to return English silently.

## Examples

    iex> NumberF.spelling_languages()
    ["de", "en", "es", "fr"]

# `category_details`

Returns metadata for a single function category.

# `documentation`

Returns a markdown documentation of all NumberF modules and functions.

## Examples

    iex> markdown = NumberF.documentation()
    iex> String.starts_with?(markdown, "# NumberF Library")
    true

# `function_categories`

Returns all function categories available in NumberF.

## Examples

    iex> NumberF.function_categories() |> Enum.map(& &1.name) |> Enum.take(3)
    ["Formatting", "Conversion", "Generation"]

# `module_details`

Returns metadata for a single NumberF module.

# `modules`

Returns information about all NumberF modules.

## Examples

    iex> NumberF.modules() |> Enum.map(& &1.name) |> Enum.take(3)
    ["NumberF", "NumberF.Currency", "NumberF.CustomFormatter"]

# `modules_by_type`

Returns information about NumberF modules of a specific type.

## Parameters
  - `type`: The module type (e.g., :formatting, :calculation)

## Examples

    iex> NumberF.modules_by_type(:formatting) |> Enum.map(& &1.name)
    ["NumberF.Currency", "NumberF.CustomFormatter", "NumberF.Formatter", "NumberF.Currencies"]

# `reference`

Returns all modules and their functions as a reference.

This function provides a comprehensive overview of all available functionality
in the NumberF library, organized by module.

## Examples

    iex> ref = NumberF.reference()
    iex> is_map(ref)
    true

---

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