# lawref.units

*module*

Алгебра размерностей [§49.1](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/08-part-viii-type-system.ru.md#491-алгебра-размерностей-magnitude-errata-e-0136-decision-0154)–[§49.2](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/08-part-viii-type-system.ru.md#492-объявление-единиц-errata-e-0136-e-0150) (errata E-0136, DECISION-0154).

Значение `Magnitude` — точный рациональный коэффициент и канонический
мультимножитель ПРОСТЫХ единиц. Размерность и масштаб здесь ВЫЧИСЛЯЮТСЯ по
реестру объявлений, а не хранятся в значении: второй источник истины разошёлся
бы с реестром молча ([§49.1](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/08-part-viii-type-system.ru.md#491-алгебра-размерностей-magnitude-errata-e-0136-decision-0154)).

Канон идентичности — чистая функция мультимножителя и НИКОГДА не разбирается
обратно: имя базовой единицы само может содержать подчёркивание
(`ft_per_half_min`), и грамматика «база `_per_` база» неоднозначна. Отсюда же
следует, что литерал `Magnitude` не входная поверхность — переход делает терм
`magnitude(q)`.

Реализация опирается ТОЛЬКО на `int` и `Fraction`: `Decimal` зависит от
контекста, а коэффициент величины обязан быть точным при любой разрядности
(полномочие владельца: BigInt только для новых операций).

## lawref.units.BASE_DIMENSIONS

*attribute* · *module attribute*

```python
BASE_DIMENSIONS = ('Length', 'Mass', 'Time', 'Current', 'Temperature', 'Amount', 'Luminosity')
```

[View source](https://github.com-arxohq/arxo-io/law/blob/2edc2b92ce22b52e03f4081d2769a58229684379/engines/lawref/lawref/units.py#L26-L27)

## lawref.units.DIMENSIONLESS

*attribute* · *module attribute*

```python
DIMENSIONLESS = (0) * len(BASE_DIMENSIONS)
```

[View source](https://github.com-arxohq/arxo-io/law/blob/2edc2b92ce22b52e03f4081d2769a58229684379/engines/lawref/lawref/units.py#L31-L31)

## lawref.units.Mag

*class* · *dataclass*

```python
class Mag
```

Значение `Magnitude`: коэффициент и мультимножитель простых единиц.

`factors` — кортеж пар (имя простой единицы, ненулевой показатель),
отсортированный по имени: канон держится КОНСТРУКТОРОМ `mag`, а не
договорённостью вызывающих.

[View source](https://github.com-arxohq/arxo-io/law/blob/2edc2b92ce22b52e03f4081d2769a58229684379/engines/lawref/lawref/units.py#L43-L53)

### lawref.units.Mag.coeff

*attribute* · *instance attribute*

```python
coeff: Fraction
```

[View source](https://github.com-arxohq/arxo-io/law/blob/2edc2b92ce22b52e03f4081d2769a58229684379/engines/lawref/lawref/units.py#L52-L52)

### lawref.units.Mag.factors

*attribute* · *instance attribute*

```python
factors: tuple[tuple[str, int], ...]
```

[View source](https://github.com-arxohq/arxo-io/law/blob/2edc2b92ce22b52e03f4081d2769a58229684379/engines/lawref/lawref/units.py#L53-L53)

## lawref.units.UnitError

*class*

```python
class UnitError(Exception)
```

Bases: `Exception`

Отказ алгебры размерностей; `code` — машинный код issue ([§49.1](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/08-part-viii-type-system.ru.md#491-алгебра-размерностей-magnitude-errata-e-0136-decision-0154)).

[View source](https://github.com-arxohq/arxo-io/law/blob/2edc2b92ce22b52e03f4081d2769a58229684379/engines/lawref/lawref/units.py#L34-L40)

### lawref.units.UnitError.code

*attribute* · *instance attribute*

```python
code = code
```

[View source](https://github.com-arxohq/arxo-io/law/blob/2edc2b92ce22b52e03f4081d2769a58229684379/engines/lawref/lawref/units.py#L39-L39)

### lawref.units.UnitError.message

*attribute* · *instance attribute*

```python
message = message
```

[View source](https://github.com-arxohq/arxo-io/law/blob/2edc2b92ce22b52e03f4081d2769a58229684379/engines/lawref/lawref/units.py#L40-L40)

## lawref.units.canon

*function*

```python
def canon(factors: tuple[tuple[str, int], ...]) -> str
```

Идентичность составной единицы [§49.1](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/08-part-viii-type-system.ru.md#491-алгебра-размерностей-magnitude-errata-e-0136-decision-0154) — ЧИСТАЯ функция мультимножителя.

Показатель в записи — модуль; знак решает половину. Пустой числитель
печатается `1`: безразмерная величина остаётся величиной, а не числом
(число извлекает `scalar_of`).

[View source](https://github.com-arxohq/arxo-io/law/blob/2edc2b92ce22b52e03f4081d2769a58229684379/engines/lawref/lawref/units.py#L64-L78)

## lawref.units.combine

*function*

```python
def combine(left: Mag, right: Mag, divide: bool) -> Mag
```

Произведение либо частное величин ([§58.1](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/09-part-ix-values-terms-expressions.ru.md#581-арифметика-величин-magnitude-errata-e-0136-decision-0154)): показатели складываются
(вычитаются), нулевые снимаются, коэффициенты считаются точно.

[View source](https://github.com-arxohq/arxo-io/law/blob/2edc2b92ce22b52e03f4081d2769a58229684379/engines/lawref/lawref/units.py#L219-L227)

## lawref.units.convert

*function*

```python
def convert(table: dict[str, dict[str, Any]], value: Mag, target: str) -> Mag
```

Явная конверсия внутри размерности ([§259.1](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/34-part-xxxiii-standard-library.ru.md#2591-алгебра-величин-errata-e-0136-decision-0154)).

[View source](https://github.com-arxohq/arxo-io/law/blob/2edc2b92ce22b52e03f4081d2769a58229684379/engines/lawref/lawref/units.py#L277-L289)

## lawref.units.dimension

*function*

```python
def dimension(table: dict[str, dict[str, Any]], factors: tuple[tuple[str, int], ...]) -> tuple[int, ...]
```

Вектор показателей по семи базовым SI ([§49.1](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/08-part-viii-type-system.ru.md#491-алгебра-размерностей-magnitude-errata-e-0136-decision-0154)).

Размерность ПРОСТОЙ единицы — сама произведение базовых: у литра это
`Length^3`. Без произведения децилитр был бы невыразим вовсе — масштаб
1/10 от литра не сводится к целой степени простой длины, а множителя у
производной единицы нет по построению (иначе литр и децилитр получили бы
ОДИН мультимножитель и один канон, то есть перестали бы различаться).

[View source](https://github.com-arxohq/arxo-io/law/blob/2edc2b92ce22b52e03f4081d2769a58229684379/engines/lawref/lawref/units.py#L161-L182)

## lawref.units.expand

*function*

```python
def expand(table: dict[str, dict[str, Any]], name: str, seen: tuple[str, ...] = ()) -> dict[str, int]
```

Имя единицы → мультимножитель ПРОСТЫХ единиц ([§49.2](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/08-part-viii-type-system.ru.md#492-объявление-единиц-errata-e-0136-e-0150)).

Алиас разворачивается в цель, производная — в свои множители, простая — в
саму себя. Цикл ловится путём, а не глубиной: сообщение обязано назвать
цепочку, иначе автор ищет её вручную.

[View source](https://github.com-arxohq/arxo-io/law/blob/2edc2b92ce22b52e03f4081d2769a58229684379/engines/lawref/lawref/units.py#L120-L148)

## lawref.units.exponent

*function*

```python
def exponent(table: dict[str, dict[str, Any]], value: Mag, name: str) -> int
```

Показатель ПРОСТОЙ единицы `name` в мультимножителе величины ([§259.1](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/34-part-xxxiii-standard-library.ru.md#2591-алгебра-величин-errata-e-0136-decision-0154),
errata E-0139). Алиас разрешается к своей простой цели; производная
отвергается — у неё нет одного показателя; отсутствующий множитель — 0.
Читаются `factors`, а не строка канона: обратного разбора нет ([§49.1](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/08-part-viii-type-system.ru.md#491-алгебра-размерностей-magnitude-errata-e-0136-decision-0154)).

[View source](https://github.com-arxohq/arxo-io/law/blob/2edc2b92ce22b52e03f4081d2769a58229684379/engines/lawref/lawref/units.py#L314-L341)

## lawref.units.factors_text

*function*

```python
def factors_text(factors: tuple[tuple[str, int], ...]) -> str
```

Мультимножитель для сообщения об отказе: `ft2^1`, `ft^2·s^-1` (E-0151).
Печатается показатель каждого множителя, включая 1, — иначе запись
снова совпала бы с каноном и ничего не различала.

[View source](https://github.com-arxohq/arxo-io/law/blob/2edc2b92ce22b52e03f4081d2769a58229684379/engines/lawref/lawref/units.py#L261-L265)

## lawref.units.mag

*function*

```python
def mag(coeff: Fraction, factors: dict[str, int] | tuple[tuple[str, int], ...]) -> Mag
```

Конструктор канона: нулевые показатели сняты, имена по возрастанию байтов.

[View source](https://github.com-arxohq/arxo-io/law/blob/2edc2b92ce22b52e03f4081d2769a58229684379/engines/lawref/lawref/units.py#L56-L61)

## lawref.units.project

*function*

```python
def project(table: dict[str, dict[str, Any]], value: Mag, target: str) -> Fraction
```

Величина алгебры → коэффициент в ЗАЯВЛЕННОЙ единице ([§259.1](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/34-part-xxxiii-standard-library.ru.md#2591-алгебра-величин-errata-e-0136-decision-0154)).

Обратный ход алгебры, и он ПРОВЕРЯЕМЫЙ, а не перетегировка: мультимножитель
величины обязан СОВПАСТЬ с раскрытием названной единицы. `6 N · 50 m` даёт
{kg:1, m:2, s:-2}, и `J` раскрывается в тот же набор — заявление принято;
напиши автор `m`, и набор {m:1} не совпадёт, а таран №4 (работа с тегом
ДЛИНЫ при статусе TRUE_ONLY) станет громким отказом.

Приведения масштабов здесь НЕТ намеренно: `km` и `m` — разные наборы, и
перевод остаётся у `unit_convert`. Иначе проекция молча меняла бы число.

[View source](https://github.com-arxohq/arxo-io/law/blob/2edc2b92ce22b52e03f4081d2769a58229684379/engines/lawref/lawref/units.py#L292-L311)

## lawref.units.promote

*function*

```python
def promote(table: dict[str, dict[str, Any]], tag: str, value: Fraction) -> Mag
```

`Quantity[tag]` → `Magnitude` ([§259.1](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/34-part-xxxiii-standard-library.ru.md#2591-алгебра-величин-errata-e-0136-decision-0154)): тег разрешается в реестре.

Непрозрачные теги права (`mrp`, `business_day`, «щелчок») не разрешаются и
в алгебру не переходят — это рамка 2 DECISION-0068, а не недоделка.

[View source](https://github.com-arxohq/arxo-io/law/blob/2edc2b92ce22b52e03f4081d2769a58229684379/engines/lawref/lawref/units.py#L268-L274)

## lawref.units.same_unit

*function*

```python
def same_unit(op: str, left: Mag, right: Mag) -> None
```

Тождество идентичности единицы ([§58.1](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/09-part-ix-values-terms-expressions.ru.md#581-арифметика-величин-magnitude-errata-e-0136-decision-0154)): сложение, вычитание и сравнение.

Тождество — по МУЛЬТИМНОЖИТЕЛЮ, а не по строке канона (errata E-0151):
строка сравнивается первой только ради прежней формулировки отказа.

Отказ называет РОД: разные масштабы ОДНОЙ размерности сюда попадают тем же
путём, что разные размерности, и до errata обе половины отвечали одним
`DIMENSION_MISMATCH` — диагностика врала (замер фазы 0, [§3](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/02-part-ii-goals-and-scope.ru.md#3-цели-law-dsl-core-01) п. 2
DECISION-0154). Здесь код один, но сообщение называет обе единицы, а
различение размерности живёт у `unit_convert`/`scalar_of`.

[View source](https://github.com-arxohq/arxo-io/law/blob/2edc2b92ce22b52e03f4081d2769a58229684379/engines/lawref/lawref/units.py#L230-L258)

## lawref.units.scalar

*function*

```python
def scalar(table: dict[str, dict[str, Any]], value: Mag) -> Fraction
```

Величина НУЛЕВОЙ размерности → точный `Rational` ([§259.1](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/34-part-xxxiii-standard-library.ru.md#2591-алгебра-величин-errata-e-0136-decision-0154)).

[View source](https://github.com-arxohq/arxo-io/law/blob/2edc2b92ce22b52e03f4081d2769a58229684379/engines/lawref/lawref/units.py#L344-L351)

## lawref.units.scale

*function*

```python
def scale(table: dict[str, dict[str, Any]], factors: tuple[tuple[str, int], ...]) -> Fraction
```

Точный масштаб мультимножителя к произведению базовых единиц ([§49.1](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/08-part-viii-type-system.ru.md#491-алгебра-размерностей-magnitude-errata-e-0136-decision-0154)).

[View source](https://github.com-arxohq/arxo-io/law/blob/2edc2b92ce22b52e03f4081d2769a58229684379/engines/lawref/lawref/units.py#L199-L212)

## lawref.units.unit_table

*function*

```python
def unit_table(nodes: list[dict[str, Any]] | None) -> dict[str, dict[str, Any]]
```

Индекс `unit_decl` по ИМЕНИ единицы ([§49.2](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/08-part-viii-type-system.ru.md#492-объявление-единиц-errata-e-0136-e-0150)).

Имя, объявленное дважды РАЗНЫМ содержимым, из индекса выбывает: разрешить
его пришлось бы порядком узлов, то есть молча выбрать одно из двух
объявлений. Дубликат байт в байт (одна декларация, доехавшая двумя
путями импорта) схлопывается.

[View source](https://github.com-arxohq/arxo-io/law/blob/2edc2b92ce22b52e03f4081d2769a58229684379/engines/lawref/lawref/units.py#L85-L107)
