# Zkouška C++ 24. 6. 2026

## Idea
Při práci s fyzikálními veličinami nás často nezajímá jen hodnota každé proměnné, ale i o kterou veličinu se jedná a jakou má jednotku. Např. proměnná obsahující rychlost může nabývat hodnotu _v_ = 7 660 _m_/_s_. Cílem úlohy je umožnit vývojářům použít pomocné typy, které tuto informaci u proměnných umožňují uchovat. Kompilátor nesmí umožnit přiřazení, sčítání či odčítání dvou proměnných různých jednotek a zároveň musí být schopen odvodit jednotku násobku či podílu dvou proměnných.

Nebudete vytvářet systém pro konkrétní soustavu jednotek, ale obecnou sadu šablon, nad kterou si bude moci uživatel svou vlastní soustavu jednotek, např. SI. V takovém systemu jsou:

- Základní jednotky, např. _m_, _kg_, _s_.
- Odvozená jednotky, např. _m_/_s_, _N_ = _kg_._m_._s⁻²_.

Libovolnou jednotku lze vyjádřit pomocí celočíselných exponentů základních jednotek. Např. v SI máme 7 základních jednotek:
```text
[s, m, kg, A, K, mol, cd]
```

Čas v sekundách lze reprezentovat jako vektor:
```text
[1, 0, 0, 0, 0, 0, 0]
```

Sílu v _N_ = _kg_._m_._s⁻²_ jako:
```text
[-2, 1, 1, 0, 0, 0, 0]
```   

Pokud násobíte hodnoty se dvěma různými jednotkami, jednotku jejich násobku získáte součtem těchto vektorů, jednotku podílu hodnot pak rozdílem jejich vektorů.

## Použití
```cpp
#include "units.hpp"
#include <iostream>

enum class si_units
{
    second,
    metre,
    kilogram,
    ampere,
    kelvin,
    mole,
    candela,
    _count
};


using second = basic_unit<si_units, si_units::second>;
using metre = basic_unit<si_units, si_units::metre>;
using kilogram = basic_unit<si_units, si_units::kilogram>;

using metre_per_second = divided_unit<metre, second>;
using newton = divided_unit<multiplied_unit<kilogram, metre>, multiplied_unit<second, second>>;


int main()
{
    quantity<metre> l(2.1);
    quantity<second> t(0.9);
    auto v1(l / t);

    quantity<metre_per_second> v2{ 2.5 };

    std::cout << (v1 + v2).value() << std::endl;

    // Won't compile
    //std::cout << (l + t).value() << std::endl;
}
```

## Rozhraní
```cpp
TEnum
```   
Tento typ uvedený v šablonách níže je `enum` dodaný uživatelem, který obsahuje základní jednotky pro danou soustavu (předpokládáme fyzikální, ale lze využít i třeba pro finančnictví). `TEnum` musí obsahovat položku `_count`, která není fyzikální jednotkou sama o sobě, ale obsahuje počet prvků v `enum`u (nejlepší je ji přidat jako jeho poslední položku, jak je vidět v příkladu výše).
```cpp
template <typename TEnum, typename TPowers>
struct unit;
```   

Pomocná "tagová" šablona obsahující `enum`, který určuje množinu základních jednotek, a statický vektor exponentů těchto jednotek.
```cpp
template <typename TEnum, TEnum Index>
using basic_unit = ...
```   

Typová definice, která vytvoří typ `unit` na základě určené soustavy jednotek a konkrétní vybrané základní jednotky. Statický vektor bude mít délku `TEnum::_count`, na indexu `Index` bude obsahovat `1` a na ostatních místech `0`.
```cpp
template <typename TFirstUnit, typename ... TOtherUnits>
using multiplied_unit = ...
``` 

Typová definice, která vytvoří typ `unit` nad danou soustavou jednotek, výsledný vektor bude obsahovat součet vektorů všech zadaných jednotek.
```cpp
template <typename TDividendUnit, typename TDivisorUnit>
using divided_unit = ...
```  

Typová definice, která vytvoří typ `unit` nad danou soustavou jednotek, výsledný vektor bude obsahovat rozdíl vektorů zadaných jednotek.
```cpp
template <typename TUnit, typename TValue = double>
struct quantity;
```
Struktura pro uchování hodnoty typu `TValue` s jednotkou (typu `unit`) `TUnit`. Nároky:

-   Z hodnoty typu `TValue` je možné ji vytvořit jen explicitně, tj. není možné do ní omylem přiřadit hodnotu bez jednotky.
- Pomocí veřejné metody `value()` je možné získat uloženou hodnotu.
- Sčítat a odečítat je možné pouze instance se stejným typem `TUnit` i `TValue`.
- Násobit a dělit je možné libovolné instance se stejným typem `TValue`.
  - Násobek dvou instancí má `TUnit` typu `multiplied_unit<TUnit1, TUnit2>`.
  - Podíl dvou instancí má `TUnit` typu `divided_unit<TUnit1, TUnit2>`.

## Tipy
- Argument `TPowers` šablony `unit` representuje "vektor" exponentů. Pro tento účel lze použít `std::integer_sequence<int, /*...*/>`.
- Při implementaci `basic_unit` bude pravděpodobně nutné explicitně přetypovat konstanty `Index` a `TEnum::_count` na `int`.
- Šablona `std::make_integer_sequence` může být užitežná při implemntaci `basic_unit`.
- Jediné místo, kde je rekurze opravdu nutná, je případ `multiplied_unit` se třemi nebo více argumenty.
- V úloze je skryto několik problémů, které budou pravděpodobně vyžadovat parciální specializace šablon tříd/struktur.
- K řešení úlohy stačí 100 řádek kódu.

## Odevzdávání

Odevzdané řešení musí obsahovat soubor `units.hpp`.


### nuance
- We also gotten (?subset of) tests available in recodex to download, shortly after we started.
- It was enough to submit only one header file with everything in it.