mirror of
https://github.com/ETLCPP/etl.git
synced 2026-10-28 18:07:10 +08:00
Merge branch 'development' into feature/to_string-float-uses-double-math
This commit is contained in:
commit
763f6fead7
@ -120,6 +120,38 @@ The documentation is written in [Goldmark](https://github.com/teekennedy/goldmar
|
||||
Hugo uses [Goldmark](https://github.com/teekennedy/goldmark-markdown) as its default Markdown processor for versions 0.60.0 and newer.
|
||||
It is built into Hugo, providing high-performance and fully [CommonMark](https://commonmark.org/)-compliant rendering, along with support for [GitHub](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax) Markdown.
|
||||
|
||||
### Documenting Algorithmic Complexity
|
||||
|
||||
When documenting the complexity of an operation, use the `complexity` shortcode instead of writing raw Big-O text.
|
||||
Place it on its own `**Complexity**` lines, right after the `**Description**` line/paragraph, for example:
|
||||
|
||||
```markdown
|
||||
**Description**
|
||||
Returns the number of linked items.
|
||||
|
||||
**Complexity**
|
||||
{{< complexity "constant" "1" >}}
|
||||
```
|
||||
|
||||
The shortcode takes two arguments: a complexity class (used to color and label it) and the expression to show
|
||||
inside `O(...)`. The supported classes are:
|
||||
|
||||
```markdown
|
||||
**Complexity**
|
||||
{{< complexity "constant" "1" >}}
|
||||
**Complexity**
|
||||
{{< complexity "linear" "N" >}}, where N is number of items in the tree, and assuming comparator is {{< complexity "constant" "1" >}}.
|
||||
**Complexity**
|
||||
{{< complexity "logarithmic" "log N" >}}, where N is number of items in the tree.
|
||||
**Complexity**
|
||||
{{< complexity "linearithmic" "N log N" >}}, where N is number of items in the tree.
|
||||
**Complexity**
|
||||
{{< complexity "polynomial" "N²" >}}, where N is number of items in the tree.
|
||||
**Complexity**
|
||||
{{< complexity "constant" "1" >}} when something, or
|
||||
{{< complexity "exponential" "2ᴺ" >}} some alternative, where N is number of items in the tree.
|
||||
```
|
||||
|
||||
### Starting a New Page
|
||||
|
||||
A template file is provided at `docs/page-template.md` to use as a starting point. Copy it to the
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
title: "Why CFOs should care about Open Source"
|
||||
description: "The business and financial case for commercially sponsoring the Embedded Template Library (ETL)."
|
||||
date: 2026-09-12
|
||||
weight: 3003
|
||||
weight: 2
|
||||
---
|
||||
|
||||
If your engineering team is using the **Embedded Template Library (ETL)** in commercial products, your company is benefiting from a critical piece of software infrastructure.
|
||||
|
||||
6
docs/containers/trees/_index.md
Normal file
6
docs/containers/trees/_index.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
title: "Trees"
|
||||
weight: 100
|
||||
---
|
||||
|
||||
Tree like containers.
|
||||
560
docs/containers/trees/intrusive-avl-tree.md
Normal file
560
docs/containers/trees/intrusive-avl-tree.md
Normal file
@ -0,0 +1,560 @@
|
||||
---
|
||||
title: "intrusive_avl_tree"
|
||||
weight: 1
|
||||
---
|
||||
|
||||
{{< callout type="info">}}
|
||||
Header: `intrusive_avl_tree.h`
|
||||
Similar to: `std::set` / `std::map` (ordering only, no ownership)
|
||||
{{< /callout >}}
|
||||
|
||||
An intrusive, self-balancing AVL tree.
|
||||
|
||||
```cpp
|
||||
template <typename TValue, size_t ID_ = 0>
|
||||
etl::intrusive_avl_tree
|
||||
```
|
||||
|
||||
`TValue` is the type that contains the actual values. It must derive from
|
||||
`etl::intrusive_avl_tree_base<ID_>::link_type`, exposed as the tree's own
|
||||
`link_type`. Unlike `etl::intrusive_list`/`etl::intrusive_forward_list`,
|
||||
which are templated directly on a link type and expect the node to derive
|
||||
from it publicly, `intrusive_avl_tree` is templated only on `TValue` and an
|
||||
`ID_`. Its `link_type` is a deliberately opaque wrapper around
|
||||
`etl::tree_link<ID_>` that exposes almost nothing as public (or even
|
||||
protected) API, by design, so that inheriting from it imposes as little
|
||||
"intrusive"-ness as possible, avoids name conflicts, and hides implementation
|
||||
details.
|
||||
|
||||
As with the other intrusive containers, an object may inherit multiple
|
||||
`link_type`s with different `ID_` values to be a member of several trees (or
|
||||
lists) at once:
|
||||
|
||||
```cpp
|
||||
typedef etl::intrusive_avl_tree_base<0>::link_type FirstLink;
|
||||
typedef etl::intrusive_avl_tree_base<1>::link_type SecondLink;
|
||||
|
||||
struct Item : public FirstLink, public SecondLink
|
||||
{
|
||||
int value;
|
||||
};
|
||||
|
||||
etl::intrusive_avl_tree<Item, 0> tree0;
|
||||
etl::intrusive_avl_tree<Item, 1> tree1;
|
||||
```
|
||||
|
||||
See also:
|
||||
[Intrusive Links]({{% relref "../../utilities/intrusive-links.md" %}})
|
||||
[intrusive_list]({{% relref "../lists/intrusive-list.md" %}})
|
||||
[intrusive_forward_list]({{% relref "../lists/intrusive-forward-list.md" %}})
|
||||
|
||||
There is no class-level comparator template parameter. Every ordering
|
||||
operation instead takes its own comparator functor at the call site.
|
||||
Lookup functions (`find`, `lower_bound`, `upper_bound`, `find_or_insert`)
|
||||
take a unary comparator accepting a single `const value_type&` and returning
|
||||
an `int`: `>0` if the search target is "greater" than the argument, `0` if
|
||||
found, `<0` if "smaller". The range constructor takes a binary comparator
|
||||
`int(const value_type& lhs, const value_type& rhs)`; `etl::compare<value_type>::cmp`
|
||||
can be used directly if `value_type` defines `<`.
|
||||
|
||||
## Member types
|
||||
|
||||
```cpp
|
||||
link_type (nested, inherited from etl::intrusive_avl_tree_base<ID_>)
|
||||
value_type TValue
|
||||
pointer value_type*
|
||||
const_pointer const value_type*
|
||||
reference value_type&
|
||||
const_reference const value_type&
|
||||
size_type size_t
|
||||
rvalue_reference value_type&& // C++11
|
||||
```
|
||||
|
||||
## The link type
|
||||
|
||||
`TValue` must derive from `link_type`. It has its own set of special member
|
||||
functions that a node author relies on.
|
||||
|
||||
```cpp
|
||||
link_type()
|
||||
```
|
||||
**Description**
|
||||
Default constructor. Leaves the item unlinked.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
link_type(link_type&& other)
|
||||
```
|
||||
**Description**
|
||||
C++11. Constructs a new item by moving `other` into `this`. After
|
||||
construction, `this` replaces `other` in the same tree position that
|
||||
`other` occupied, so no tree balancing is needed. `other` becomes unlinked.
|
||||
|
||||
**Complexity**
|
||||
{{< complexity "constant" "1" >}}
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
link_type& operator=(link_type&& other)
|
||||
```
|
||||
**Description**
|
||||
C++11. Assigns `other` by moving it into `this`. Does nothing on
|
||||
self-assignment. After assignment, `this` replaces `other` in the same tree
|
||||
position that `other` occupied; `other` becomes unlinked. `this` might end
|
||||
up in a different tree than it started in.
|
||||
|
||||
**Complexity**
|
||||
{{< complexity "logarithmic" "log N" >}} if `this` is already linked to a tree (it has to be erased first, with rebalancing), or
|
||||
{{< complexity "constant" "1" >}} if `this` is not linked, where `N` is number of items in the tree.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
link_type(const link_type&) = delete;
|
||||
link_type& operator=(const link_type&) = delete;
|
||||
```
|
||||
**Description**
|
||||
C++11. Copy construction and assignment are disabled.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
~link_type()
|
||||
```
|
||||
**Description**
|
||||
Might rotate the tree as necessary to keep it balanced. The tree is not the
|
||||
real owner of its nodes' memory - the node's actual owner may destroy it at
|
||||
any time, with or without a prior explicit `erase`. If the link is still
|
||||
linked to a tree when it's destroyed, the destructor unlinks it
|
||||
automatically; otherwise the node's former parent and children would keep
|
||||
dangling pointers to it, breaking the tree and leading to undefined
|
||||
behaviour. This is what makes it safe to embed a tree node in an object
|
||||
whose lifetime is managed elsewhere.
|
||||
|
||||
**Complexity**
|
||||
{{< complexity "logarithmic" "log N" >}}, where `N` is number of items in the tree.
|
||||
|
||||
## Constructors
|
||||
|
||||
```cpp
|
||||
etl::intrusive_avl_tree<TValue, ID_>()
|
||||
```
|
||||
**Description**
|
||||
Default constructor. Constructs an empty tree.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
template <typename TIterator, typename TBinaryCompare>
|
||||
etl::intrusive_avl_tree<TValue, ID_>(TIterator first, TIterator last, TBinaryCompare binary_comp)
|
||||
```
|
||||
**Description**
|
||||
Constructs the tree from the range [`first`, `last`) of items, using
|
||||
`binary_comp` to order them. All items in the range must be unlinked
|
||||
initially; if any item is already linked to some other tree, throws
|
||||
`etl::intrusive_avl_tree_value_is_already_linked`, and any items already
|
||||
inserted are unlinked again - it's all or nothing.
|
||||
If duplicates should be kept, `binary_comp` should return a non-zero result
|
||||
even for "equal" items - `<0` prepends a duplicate, `>0` appends one.
|
||||
Returning `0` for equal items keeps only the first of each run of duplicates
|
||||
in the range; the rest are left unlinked. `etl::compare<value_type>::cmp`
|
||||
can be used directly as `binary_comp`.
|
||||
|
||||
**Complexity**
|
||||
{{< complexity "linearithmic" "N log N" >}}, where `N` is number of items in the range.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
intrusive_avl_tree(intrusive_avl_tree&&) = default;
|
||||
```
|
||||
**Description**
|
||||
C++11. Move constructor.
|
||||
|
||||
**Complexity**
|
||||
{{< complexity "constant" "1" >}}
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
intrusive_avl_tree(const intrusive_avl_tree&) = delete;
|
||||
```
|
||||
**Description**
|
||||
C++11. Copy construction is disabled.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
~intrusive_avl_tree()
|
||||
```
|
||||
**Description**
|
||||
Every remaining node is unlinked; none of the value objects are destroyed,
|
||||
since the tree never owns their memory.
|
||||
|
||||
**Complexity**
|
||||
{{< complexity "linear" "N" >}}, where `N` is number of items in the tree.
|
||||
|
||||
## Assignment
|
||||
|
||||
```cpp
|
||||
intrusive_avl_tree& operator=(intrusive_avl_tree&&) = default;
|
||||
```
|
||||
**Description**
|
||||
C++11. Move assignment. Does nothing on self-assignment. All of `this`
|
||||
tree's former items have to be unlinked.
|
||||
|
||||
**Complexity**
|
||||
{{< complexity "linear" "N" >}}, where `N` is the size of `this` tree before assignment.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
intrusive_avl_tree& operator=(const intrusive_avl_tree&) = delete;
|
||||
```
|
||||
**Description**
|
||||
C++11. Copy assignment is disabled.
|
||||
|
||||
## Capacity
|
||||
|
||||
```cpp
|
||||
bool empty() const
|
||||
```
|
||||
**Description**
|
||||
Returns `true` if the tree has no linked items.
|
||||
|
||||
**Complexity**
|
||||
{{< complexity "constant" "1" >}}
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
size_t size() const
|
||||
```
|
||||
**Description**
|
||||
Returns the number of linked items.
|
||||
|
||||
**Complexity**
|
||||
{{< complexity "constant" "1" >}}
|
||||
|
||||
## Iterators
|
||||
|
||||
Only bidirectional `iterator`/`const_iterator` are provided; there is no
|
||||
`reverse_iterator` (use `visit_in_order` with `is_reverse = true` instead).
|
||||
An iterator may be in one of three logical states: it references a real
|
||||
node (`has_value() == true`), it is the terminal sentinel (`it == end()`),
|
||||
or it is "valueless" (`!has_value() && it != end()` - e.g. a
|
||||
default-constructed iterator, or the result of `get_parent()`/`get_child()`
|
||||
when there is no such node).
|
||||
|
||||
```cpp
|
||||
iterator begin()
|
||||
const_iterator begin() const
|
||||
const_iterator cbegin() const
|
||||
```
|
||||
**Description**
|
||||
Returns an iterator to the smallest item. Returns `end()` if the tree is
|
||||
empty.
|
||||
|
||||
**Complexity**
|
||||
{{< complexity "logarithmic" "log N" >}}, where `N` is number of items in the tree.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
iterator end()
|
||||
const_iterator end() const
|
||||
const_iterator cend() const
|
||||
```
|
||||
**Description**
|
||||
Returns an iterator to the terminal sentinel.
|
||||
|
||||
**Complexity**
|
||||
{{< complexity "constant" "1" >}}
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
iterator min()
|
||||
const_iterator min() const
|
||||
```
|
||||
**Description**
|
||||
Equivalent to `begin()`.
|
||||
|
||||
**Complexity**
|
||||
{{< complexity "logarithmic" "log N" >}}, where `N` is number of items in the tree.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
iterator max()
|
||||
const_iterator max() const
|
||||
```
|
||||
**Description**
|
||||
Equivalent to `--end()`.
|
||||
|
||||
**Complexity**
|
||||
{{< complexity "logarithmic" "log N" >}}, where `N` is number of items in the tree.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
iterator& operator++()
|
||||
iterator operator++(int)
|
||||
```
|
||||
**Description**
|
||||
Advances the iterator to the next ("greater") item.
|
||||
|
||||
**Complexity**
|
||||
{{< complexity "constant" "1" >}} amortized,
|
||||
{{< complexity "logarithmic" "log N" >}} worst-case, where `N` is number of items in the tree.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
iterator& operator--()
|
||||
iterator operator--(int)
|
||||
```
|
||||
**Description**
|
||||
Moves the iterator to the previous ("smaller") item.
|
||||
|
||||
**Complexity**
|
||||
{{< complexity "constant" "1" >}} amortized,
|
||||
{{< complexity "logarithmic" "log N" >}} worst-case, where `N` is number of items in the tree.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
reference operator*() const
|
||||
pointer operator->() const
|
||||
pointer get() const
|
||||
```
|
||||
**Description**
|
||||
Dereferences an iterator that has a value. `operator*`/`operator->` throw
|
||||
`etl::intrusive_avl_tree_iterator_exception` if `has_value()` is `false`.
|
||||
`get()` returns `nullptr` instead of throwing.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
bool has_value() const
|
||||
explicit operator bool() const
|
||||
```
|
||||
**Description**
|
||||
Returns `true` if the iterator references a real node.
|
||||
|
||||
## Advanced traversal
|
||||
|
||||
These are normally only needed for advanced traversal.
|
||||
|
||||
```cpp
|
||||
iterator get_root()
|
||||
const_iterator get_root() const
|
||||
```
|
||||
**Description**
|
||||
Returns an iterator to the root node. Valueless (`has_value() == false`) if
|
||||
the tree is empty.
|
||||
|
||||
**Complexity**
|
||||
{{< complexity "constant" "1" >}}
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
int_fast8_t get_balance_factor() const
|
||||
```
|
||||
**Description**
|
||||
Returns the node's AVL balance factor: `-1`, `0` or `+1`.
|
||||
|
||||
**Complexity**
|
||||
{{< complexity "constant" "1" >}}
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
iterator get_parent() const
|
||||
```
|
||||
**Description**
|
||||
Returns an iterator to the node's parent. Valueless if there is no parent.
|
||||
|
||||
**Complexity**
|
||||
{{< complexity "constant" "1" >}}
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
iterator get_child(bool is_right) const
|
||||
```
|
||||
**Description**
|
||||
Returns an iterator to the node's left (`is_right == false`) or right
|
||||
(`is_right == true`) child. Valueless if there is no such child.
|
||||
|
||||
**Complexity**
|
||||
{{< complexity "constant" "1" >}}
|
||||
|
||||
## Lookup
|
||||
|
||||
```cpp
|
||||
template <typename TCompare>
|
||||
iterator find(TCompare comp)
|
||||
template <typename TCompare>
|
||||
const_iterator find(TCompare comp) const
|
||||
```
|
||||
**Description**
|
||||
Finds an item using the unary comparator `comp`. Returns `end()` if there
|
||||
is no matching item.
|
||||
|
||||
**Complexity**
|
||||
{{< complexity "logarithmic" "log N" >}}, where `N` is number of items in the tree,
|
||||
and assuming `comp` is {{< complexity "constant" "1" >}}.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
template <typename TCompare>
|
||||
iterator lower_bound(TCompare comp)
|
||||
template <typename TCompare>
|
||||
const_iterator lower_bound(TCompare comp) const
|
||||
```
|
||||
**Description**
|
||||
Returns an iterator to the first item that compares as "not less" than the
|
||||
target of `comp`. Returns `end()` if no such item exists.
|
||||
|
||||
**Complexity**
|
||||
{{< complexity "logarithmic" "log N" >}}, where `N` is number of items in the tree,
|
||||
and assuming `comp` is {{< complexity "constant" "1" >}}.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
template <typename TCompare>
|
||||
iterator upper_bound(TCompare comp)
|
||||
template <typename TCompare>
|
||||
const_iterator upper_bound(TCompare comp) const
|
||||
```
|
||||
**Description**
|
||||
Returns an iterator to the first item that compares as "greater" than the
|
||||
target of `comp`. Returns `end()` if no such item exists.
|
||||
|
||||
**Complexity**
|
||||
{{< complexity "logarithmic" "log N" >}}, where `N` is number of items in the tree,
|
||||
and assuming `comp` is {{< complexity "constant" "1" >}}.
|
||||
|
||||
## Modifiers
|
||||
|
||||
```cpp
|
||||
template <typename TCompare, typename TFactory>
|
||||
etl::pair<iterator, bool> find_or_insert(TCompare comp, TFactory factory)
|
||||
```
|
||||
**Description**
|
||||
Finds an existing item using the unary comparator `comp`; if not found,
|
||||
calls `factory()` (no arguments) and inserts the item it returns at the
|
||||
position where the search stopped. This operation never invalidates
|
||||
existing iterators, though depending on where the new item was linked,
|
||||
existing iterators may skip over it.
|
||||
`factory` must return the address of the new value (castable to
|
||||
`link_type*`). If it returns `nullptr`, the tree is left unmodified and the
|
||||
result iterator is valueless. If the returned value is already linked to
|
||||
any tree, throws `etl::intrusive_avl_tree_value_is_already_linked`.
|
||||
Returns a pair of: an iterator to the found or inserted item (never `end()`,
|
||||
but may be valueless), and a `bool` that is `true` if the tree was modified.
|
||||
If duplicates are wanted, `comp` should return a non-zero result even for
|
||||
"equal" items - `+1` appends after existing duplicates, `-1` prepends
|
||||
before them.
|
||||
|
||||
**Complexity**
|
||||
{{< complexity "logarithmic" "log N" >}}, where `N` is number of items in the tree,
|
||||
and assuming `comp` and `factory` are {{< complexity "constant" "1" >}}.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
iterator erase(iterator position)
|
||||
iterator erase(const_iterator position)
|
||||
```
|
||||
**Description**
|
||||
Erases the item at `position`, including tree rebalancing. `position` must
|
||||
reference a real item (asserts/throws
|
||||
`etl::intrusive_avl_tree_iterator_exception` otherwise) and must originate
|
||||
from this tree instance. Invalidates any existing iterator to the erased
|
||||
item, but no others. Returns an iterator to the next node.
|
||||
Use `clear()` instead if every item needs to be erased - no rebalancing is
|
||||
involved.
|
||||
|
||||
**Complexity**
|
||||
{{< complexity "logarithmic" "log N" >}}, where `N` is number of items in the tree.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
void clear()
|
||||
```
|
||||
**Description**
|
||||
Unlinks every item, leaving the tree empty. Invalidates all existing
|
||||
iterators. This is cheaper than calling `erase` on every item, which would
|
||||
cost {{< complexity "linearithmic" "N log N" >}} once intermediate
|
||||
rebalancing is taken into account.
|
||||
|
||||
**Complexity**
|
||||
{{< complexity "linear" "N" >}}, where `N` is number of items in the tree.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
void swap(intrusive_avl_tree& other)
|
||||
friend void swap(intrusive_avl_tree& lhs, intrusive_avl_tree& rhs)
|
||||
```
|
||||
**Description**
|
||||
Swaps the contents of two trees. Does nothing on self-swap.
|
||||
|
||||
**Complexity**
|
||||
{{< complexity "constant" "1" >}}
|
||||
|
||||
## Traversal
|
||||
|
||||
Each of these visits every item exactly once, without recursion, and has a
|
||||
`const` overload. The visitor must not modify the tree during visitation.
|
||||
|
||||
```cpp
|
||||
template <typename Visitor>
|
||||
void visit_in_order(bool is_reverse, Visitor visitor)
|
||||
```
|
||||
**Description**
|
||||
Visits every item in sorted order. `is_reverse` selects ascending (`false`)
|
||||
or descending (`true`) order.
|
||||
|
||||
**Complexity**
|
||||
{{< complexity "linear" "N" >}}, where `N` is number of items in the tree.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
template <typename Visitor>
|
||||
void visit_pre_order(bool is_reverse, Visitor visitor)
|
||||
```
|
||||
**Description**
|
||||
Visits every item in pre-order (parent before children).
|
||||
|
||||
**Complexity**
|
||||
{{< complexity "linear" "N" >}}, where `N` is number of items in the tree.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
template <typename Visitor>
|
||||
void visit_post_order(bool is_reverse, Visitor visitor)
|
||||
```
|
||||
**Description**
|
||||
Visits every item in post-order (children before parent).
|
||||
|
||||
**Complexity**
|
||||
{{< complexity "linear" "N" >}}, where `N` is number of items in the tree.
|
||||
|
||||
## Exceptions
|
||||
|
||||
```cpp
|
||||
intrusive_avl_tree_exception // base exception
|
||||
intrusive_avl_tree_iterator_exception // invalid iterator use
|
||||
intrusive_avl_tree_value_is_already_linked // item already linked to a tree
|
||||
```
|
||||
@ -1,6 +1,6 @@
|
||||
---
|
||||
title: "Sponsors of the ETL"
|
||||
weight: 3002
|
||||
weight: 3
|
||||
---
|
||||
|
||||
A special thank you to all of our past and current sponsors. Your generosity directly funds the continuous development and maintenance of the ETL.
|
||||
|
||||
@ -13,6 +13,11 @@ A quick introduction to containers.
|
||||
|
||||
---
|
||||
|
||||
[Intrusive AVL Tree](./intrusive-avl-tree-tutorial)
|
||||
Ordering objects that already exist, without a second container.
|
||||
|
||||
---
|
||||
|
||||
[observer](./observer-tutorial)
|
||||
A templated set of classes to allow easier implementation of the observer pattern.
|
||||
|
||||
|
||||
210
docs/tutorials/intrusive-avl-tree-tutorial.md
Normal file
210
docs/tutorials/intrusive-avl-tree-tutorial.md
Normal file
@ -0,0 +1,210 @@
|
||||
---
|
||||
title: "Intrusive AVL Tree"
|
||||
---
|
||||
|
||||
A tutorial on how `etl::intrusive_avl_tree` can give ordered,
|
||||
{{< complexity "logarithmic" "log N" >}} access to objects that already
|
||||
exist elsewhere, without a second container to track them.
|
||||
|
||||
## Overview
|
||||
|
||||
Imagine a set of driver/sensor tasks, each owned by its own subsystem. They
|
||||
are created and destroyed at unpredictable times as modules attach and
|
||||
detach, so there is no compile-time upper bound on how many exist at once.
|
||||
A scheduler needs to visit the tasks that are currently due, in ascending
|
||||
deadline order, on every tick.
|
||||
|
||||
A normal fixed-capacity ordered container (`etl::set`/`etl::map`) would need
|
||||
a second, separately-sized place to store pointers to these tasks. Instead,
|
||||
embedding an `etl::intrusive_avl_tree` link directly into each task object
|
||||
gives the scheduler ordered, {{< complexity "logarithmic" "log N" >}}
|
||||
access to exactly the tasks that exist right now - nothing more is
|
||||
allocated, and nothing needs to be kept in sync by hand. That's the payoff of "intrusive": the tree links the
|
||||
objects that already exist, rather than storing copies of them.
|
||||
|
||||
The example below also demonstrates two things that fall out of the design
|
||||
for free:
|
||||
- **Move support.** When the `std::vector` that owns the tasks reallocates,
|
||||
every task is moved into new storage - and each move takes over its
|
||||
task's exact tree position, with no rebalancing and no code of its own.
|
||||
- **Safe removal without an explicit `erase()` call.** Removing a task's
|
||||
owning object removes it from the schedule too, with no dangling entry
|
||||
left behind - whether that's because the object is destroyed while still
|
||||
linked (`link_type`'s destructor unlinks it automatically), or, as in
|
||||
this example, because `std::vector::erase` move-assigns another task over
|
||||
it (`link_type`'s move assignment unlinks `this` before taking over the
|
||||
moved-from tree position).
|
||||
|
||||
## Example
|
||||
|
||||
```cpp
|
||||
#include "etl/intrusive_avl_tree.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
// A driver/sensor task that already exists as a long-lived object owned by
|
||||
// its own subsystem. Inheriting from the tree's link_type is all it takes
|
||||
// to make it schedulable - no separate fixed-capacity container is needed
|
||||
// to track which tasks exist.
|
||||
class SensorTask : public etl::intrusive_avl_tree_base<0>::link_type
|
||||
{
|
||||
public:
|
||||
SensorTask(uint32_t id_, uint32_t deadline_, uint32_t period_)
|
||||
: id(id_), deadline(deadline_), period(period_)
|
||||
{
|
||||
}
|
||||
|
||||
// Orders primarily by deadline; ties are broken by id so two tasks due
|
||||
// on the same tick still occupy distinct tree positions.
|
||||
struct Locate
|
||||
{
|
||||
uint32_t target_deadline;
|
||||
uint32_t target_id;
|
||||
|
||||
int operator()(const SensorTask& node) const
|
||||
{
|
||||
if (target_deadline != node.deadline)
|
||||
{
|
||||
return (target_deadline < node.deadline) ? -1 : 1;
|
||||
}
|
||||
if (target_id != node.id)
|
||||
{
|
||||
return (target_id < node.id) ? -1 : 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
uint32_t id;
|
||||
uint32_t deadline; // next tick at which this task must be serviced
|
||||
uint32_t period;
|
||||
};
|
||||
|
||||
typedef etl::intrusive_avl_tree<SensorTask> Schedule;
|
||||
|
||||
// The only place a task gets linked - called whenever a module attaches,
|
||||
// or whenever a task is rescheduled after being serviced.
|
||||
void schedule_task(Schedule& schedule, SensorTask& task)
|
||||
{
|
||||
schedule.find_or_insert(SensorTask::Locate{ task.deadline, task.id },
|
||||
[&task] { return &task; });
|
||||
}
|
||||
|
||||
// Visits every currently-due task in ascending deadline order, services it,
|
||||
// then reschedules it at its next deadline.
|
||||
void service_due_tasks(Schedule& schedule, uint32_t now)
|
||||
{
|
||||
for (auto it = schedule.begin(); (it != schedule.end()) && (it->deadline <= now); )
|
||||
{
|
||||
SensorTask& task = *it;
|
||||
it = schedule.erase(it); // O(log N), rebalances
|
||||
|
||||
std::cout << "servicing task " << task.id << " (was due at " << task.deadline << ")\n";
|
||||
|
||||
task.deadline = now + task.period;
|
||||
schedule_task(schedule, task); // reschedule at the new deadline
|
||||
}
|
||||
}
|
||||
|
||||
void print_schedule(const Schedule& schedule)
|
||||
{
|
||||
for (auto it = schedule.begin(); it != schedule.end(); ++it)
|
||||
{
|
||||
std::cout << " task " << it->id << " due at " << it->deadline << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
Schedule schedule;
|
||||
|
||||
// Driver registry: a plain std::vector that a subsystem uses to own its
|
||||
// tasks for whatever reason it already needs to - the tree does not
|
||||
// require this vector to exist, it's just a stand-in for "wherever the
|
||||
// real owner happens to keep the object".
|
||||
std::vector<SensorTask> driver_registry;
|
||||
driver_registry.reserve(2);
|
||||
|
||||
driver_registry.emplace_back(1, 100, 1000);
|
||||
schedule_task(schedule, driver_registry.back());
|
||||
|
||||
driver_registry.emplace_back(2, 300, 1000);
|
||||
schedule_task(schedule, driver_registry.back());
|
||||
|
||||
std::cout << "Initial schedule (ascending deadline):\n";
|
||||
print_schedule(schedule);
|
||||
|
||||
std::cout << "\nschedule.size() before growth: " << schedule.size() << "\n";
|
||||
|
||||
// driver_registry.reserve()'d capacity was 2; this emplace_back forces a
|
||||
// reallocation. std::vector move-constructs every existing SensorTask
|
||||
// into the new storage. Each move steals its task's exact tree position
|
||||
// (O(1), no rebalancing) - the tree needs no code of its own to survive
|
||||
// the underlying storage moving.
|
||||
driver_registry.emplace_back(3, 200, 1100);
|
||||
schedule_task(schedule, driver_registry.back());
|
||||
|
||||
std::cout << "schedule.size() after growth: " << schedule.size() << "\n\n";
|
||||
|
||||
std::cout << "Schedule after growth (still correctly ordered):\n";
|
||||
print_schedule(schedule);
|
||||
|
||||
std::cout << "\nServicing due tasks at tick 250:\n";
|
||||
service_due_tasks(schedule, 250);
|
||||
|
||||
std::cout << "\nSchedule after servicing:\n";
|
||||
print_schedule(schedule);
|
||||
|
||||
// Unplugging a module removes its SensorTask from driver_registry. Task 2
|
||||
// isn't the last element, so std::vector::erase() doesn't just destroy it
|
||||
// in place: it move-assigns the following element (task 3) into task 2's
|
||||
// slot, then destroys the now-duplicate last slot. That move-assignment
|
||||
// is SensorTask's (implicitly-generated) one, which calls
|
||||
// link_type::operator=(link_type&&): it first unlinks *this* - task 2's
|
||||
// slot, still linked at deadline 300 - from the tree, then takes over
|
||||
// task 3's tree position. The destructor that runs afterwards, on the
|
||||
// vacated last slot, fires on an already-unlinked, moved-from object, so
|
||||
// it has nothing left to do - no explicit schedule.erase() call is made
|
||||
// here, and no dangling entry is left behind.
|
||||
std::cout << "\nschedule.size() before unplugging task 2: " << schedule.size() << "\n";
|
||||
|
||||
auto pos = std::find_if(driver_registry.begin(), driver_registry.end(),
|
||||
[](const SensorTask& t) { return t.id == 2; });
|
||||
driver_registry.erase(pos); // move-assigns task 3 over task 2, unlinking task 2
|
||||
|
||||
std::cout << "schedule.size() after unplugging task 2: " << schedule.size() << "\n";
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
## Program output
|
||||
|
||||
```
|
||||
Initial schedule (ascending deadline):
|
||||
task 1 due at 100
|
||||
task 2 due at 300
|
||||
|
||||
schedule.size() before growth: 2
|
||||
schedule.size() after growth: 3
|
||||
|
||||
Schedule after growth (still correctly ordered):
|
||||
task 1 due at 100
|
||||
task 3 due at 200
|
||||
task 2 due at 300
|
||||
|
||||
Servicing due tasks at tick 250:
|
||||
servicing task 1 (was due at 100)
|
||||
servicing task 3 (was due at 200)
|
||||
|
||||
Schedule after servicing:
|
||||
task 2 due at 300
|
||||
task 1 due at 1250
|
||||
task 3 due at 1350
|
||||
|
||||
schedule.size() before unplugging task 2: 3
|
||||
schedule.size() after unplugging task 2: 2
|
||||
```
|
||||
@ -26,7 +26,8 @@ including the header to override automatic detection.
|
||||
|
||||
When `ETL_HAS_FLOATING_POINT_CLAMPED_VALUE` is `0`, `T` must be an integral
|
||||
type. When it is `1`, `float`, `double`, and `long double` may also be used with
|
||||
runtime bounds.
|
||||
runtime bounds. `clamped_value_range<T>` and `referenced_clamped_value<T>` are
|
||||
available in C++11 and later.
|
||||
|
||||
```cpp
|
||||
// C++11 and later
|
||||
@ -48,6 +49,20 @@ etl::clamped_value<float, -1.5f, 2.5f> float_ct(0.5f);
|
||||
#endif
|
||||
```
|
||||
|
||||
In C++11 and later, multiple values can share one immutable runtime range:
|
||||
|
||||
```cpp
|
||||
const etl::clamped_value_range<float> range(-1.5f, 2.5f);
|
||||
|
||||
etl::referenced_clamped_value<float> value1(range, 0.5f);
|
||||
etl::referenced_clamped_value<float> value2(range, 1.5f);
|
||||
```
|
||||
|
||||
`referenced_clamped_value<T>` stores an
|
||||
`etl::reference_wrapper<const clamped_value_range<T>>`. The range must outlive
|
||||
every value that refers to it. Construction from a temporary range is deleted.
|
||||
Copying or swapping a referenced value also copies or swaps its range reference.
|
||||
|
||||
## Types
|
||||
|
||||
`difference_type` is the signed counterpart of `T` for integral values. For
|
||||
@ -55,6 +70,9 @@ floating-point values, `difference_type` is `T`, allowing fractional steps.
|
||||
`limits_type` and `difference_limits_type` provide the corresponding
|
||||
`etl::numeric_limits` specializations.
|
||||
|
||||
`referenced_clamped_value<T>` also defines `range_type` as
|
||||
`clamped_value_range<T>`.
|
||||
|
||||
## Constructors
|
||||
|
||||
```cpp
|
||||
@ -76,6 +94,23 @@ runtime-bound specialization. Supplying `Min` and `Max` selects fixed
|
||||
compile-time bounds. NaN bounds and initial values are rejected. Infinite
|
||||
bounds and values are permitted.
|
||||
|
||||
### Referenced ranges
|
||||
|
||||
```cpp
|
||||
clamped_value_range<T> range(min, max);
|
||||
referenced_clamped_value<T> value(range);
|
||||
referenced_clamped_value<T> value(range, initial);
|
||||
```
|
||||
|
||||
The one-argument value constructor initializes to `range.min()`. The
|
||||
two-argument form clamps `initial` to the shared range. A range is immutable
|
||||
after construction, so every referring value observes stable bounds. A
|
||||
default-constructed range uses the full representable range of `T`.
|
||||
|
||||
`referenced_clamped_value<T>` is non-owning. The referenced range must outlive
|
||||
the value and any copies of it. The constructors taking `range_type&&` are
|
||||
deleted to prevent direct construction from a temporary range.
|
||||
|
||||
## Modifiers
|
||||
|
||||
```cpp
|
||||
@ -93,6 +128,10 @@ counterpart of `T` as its step type. For floating-point values, the step type
|
||||
is `T` and may be fractional. Steps saturate in constant time at the minimum
|
||||
or maximum rather than wrapping. NaN steps are rejected.
|
||||
|
||||
`referenced_clamped_value<T>` supports `set(T)`, `to_min()`, `to_max()`, and
|
||||
`advance(difference_type)`. It does not provide `set(min, max)` because its
|
||||
range is immutable and shared.
|
||||
|
||||
Positive steps move toward `max`; negative steps move toward `min`. Infinite
|
||||
steps saturate at a finite bound. Infinite values and bounds remain valid.
|
||||
|
||||
@ -122,6 +161,14 @@ ETL_NODISCARD T max() const noexcept;
|
||||
Gets the current value and its bounds. Both compile-time and runtime
|
||||
specializations provide these accessors as const-qualified member functions.
|
||||
|
||||
`referenced_clamped_value<T>` additionally provides:
|
||||
|
||||
```cpp
|
||||
ETL_NODISCARD const range_type& range() const noexcept;
|
||||
```
|
||||
|
||||
This returns the exact `clamped_value_range<T>` object referenced by the value.
|
||||
|
||||
## Operators
|
||||
|
||||
```cpp
|
||||
@ -143,6 +190,8 @@ floating-point values, assignment and compound-assignment reject NaN.
|
||||
Prefix increment and decrement return a reference to the updated object.
|
||||
Postfix increment and decrement return a copy of the value before it was
|
||||
updated. All four operations move by one and saturate at the applicable bound.
|
||||
`referenced_clamped_value<T>` provides the same conversion, assignment,
|
||||
increment, decrement, and compound-assignment operators.
|
||||
|
||||
## Operations
|
||||
|
||||
@ -153,6 +202,10 @@ ETL_CONSTEXPR14 void swap(clamped_value& lhs, clamped_value& rhs);
|
||||
|
||||
Swaps clamped values. Runtime values also swap their bounds.
|
||||
|
||||
Swapping two `referenced_clamped_value<T>` objects swaps both their current
|
||||
values and range references. Copy construction and copy assignment preserve
|
||||
the source's range identity; they do not copy the range itself.
|
||||
|
||||
Equality, inequality, and relational comparisons are provided. Comparisons
|
||||
against arithmetic values use the implicit conversion to `T`. Equality
|
||||
compares the current values; runtime bounds do not affect equality. Runtime
|
||||
@ -169,3 +222,124 @@ assignments, and steps also trigger an ETL assertion. Depending on the ETL
|
||||
error-handler configuration, an assertion may throw `etl::exception`, invoke a
|
||||
configured handler, or terminate. Operations that validate input are only
|
||||
`noexcept` when exceptions are disabled.
|
||||
|
||||
The same validation applies when constructing `clamped_value_range<T>` and
|
||||
when assigning or advancing a `referenced_clamped_value<T>`.
|
||||
|
||||
## Shared Referenced Ranges
|
||||
|
||||
`clamped_value_range<T>` and `referenced_clamped_value<T>` are available in
|
||||
C++11 and later. They let independent values share immutable runtime bounds.
|
||||
|
||||
### `clamped_value_range<T>`
|
||||
|
||||
```cpp
|
||||
template <typename T>
|
||||
class clamped_value_range;
|
||||
```
|
||||
|
||||
#### Constructors
|
||||
|
||||
```cpp
|
||||
clamped_value_range();
|
||||
clamped_value_range(T min, T max);
|
||||
```
|
||||
|
||||
The default constructor uses the full representable range of `T`. The
|
||||
two-argument constructor stores immutable bounds and rejects reversed or NaN
|
||||
bounds.
|
||||
|
||||
#### Accessors
|
||||
|
||||
```cpp
|
||||
ETL_NODISCARD T min() const noexcept;
|
||||
ETL_NODISCARD T max() const noexcept;
|
||||
```
|
||||
|
||||
### `referenced_clamped_value<T>`
|
||||
|
||||
```cpp
|
||||
template <typename T>
|
||||
class referenced_clamped_value;
|
||||
```
|
||||
|
||||
The class stores a current value and an
|
||||
`etl::reference_wrapper<const clamped_value_range<T>>`. It does not own the
|
||||
range, which must outlive the value and all copies of it.
|
||||
|
||||
Member types are `range_type`, `difference_type`, `limits_type`, and
|
||||
`difference_limits_type`, with the same numeric meanings as `clamped_value<T>`.
|
||||
|
||||
#### Constructors
|
||||
|
||||
```cpp
|
||||
explicit referenced_clamped_value(const range_type& range);
|
||||
referenced_clamped_value(const range_type& range, T initial);
|
||||
referenced_clamped_value(const referenced_clamped_value& other);
|
||||
|
||||
referenced_clamped_value(range_type&&) = delete;
|
||||
referenced_clamped_value(range_type&&, T) = delete;
|
||||
```
|
||||
|
||||
The one-argument constructor initializes to `range.min()`. The two-argument
|
||||
constructor clamps `initial`. Temporary ranges are rejected to prevent an
|
||||
immediately dangling reference. Copy construction preserves the source value
|
||||
and range identity.
|
||||
|
||||
#### Modifiers
|
||||
|
||||
```cpp
|
||||
void set(T value);
|
||||
void to_min() noexcept;
|
||||
void to_max() noexcept;
|
||||
void advance(difference_type n);
|
||||
```
|
||||
|
||||
These operations mirror `clamped_value<T>`. There is no `set(min, max)` because
|
||||
the referenced range is immutable and shared.
|
||||
|
||||
#### Accessors
|
||||
|
||||
```cpp
|
||||
ETL_NODISCARD T get() const noexcept;
|
||||
ETL_NODISCARD T min() const noexcept;
|
||||
ETL_NODISCARD T max() const noexcept;
|
||||
ETL_NODISCARD const range_type& range() const noexcept;
|
||||
ETL_NODISCARD operator T() const noexcept;
|
||||
```
|
||||
|
||||
`range()` returns the exact range object held by reference.
|
||||
|
||||
#### Operators
|
||||
|
||||
```cpp
|
||||
referenced_clamped_value& operator=(const referenced_clamped_value& other) & noexcept;
|
||||
referenced_clamped_value& operator=(T value) &;
|
||||
referenced_clamped_value& operator++() & noexcept;
|
||||
referenced_clamped_value operator++(int) noexcept;
|
||||
referenced_clamped_value& operator--() & noexcept;
|
||||
referenced_clamped_value operator--(int) noexcept;
|
||||
referenced_clamped_value& operator+=(difference_type n) &;
|
||||
referenced_clamped_value& operator-=(difference_type n) &;
|
||||
```
|
||||
|
||||
Copy assignment copies both the current value and range reference. Arithmetic
|
||||
and value assignment clamp to the referenced bounds. Comparisons compare only
|
||||
current values, not range identity or bounds.
|
||||
|
||||
#### Swap
|
||||
|
||||
```cpp
|
||||
void swap(referenced_clamped_value& other) noexcept;
|
||||
void swap(referenced_clamped_value& lhs,
|
||||
referenced_clamped_value& rhs) noexcept;
|
||||
```
|
||||
|
||||
Swap exchanges both current values and range references, keeping each value
|
||||
associated with its original bounds.
|
||||
|
||||
#### Errors
|
||||
|
||||
Invalid or NaN range bounds trigger an ETL assertion. NaN initial values,
|
||||
assignments, and steps are also rejected. Infinite values follow the same
|
||||
behavior as `clamped_value<T>`.
|
||||
|
||||
@ -297,6 +297,8 @@ template <const size_t ID_>
|
||||
struct tree_link
|
||||
```
|
||||
|
||||
`etl::intrusive_avl_tree` is built on top of `tree_link` - see [intrusive_avl_tree]({{% relref "../containers/trees/intrusive-avl-tree.md" %}}).
|
||||
|
||||
### Template parameters
|
||||
```cpp
|
||||
const size_t ID_
|
||||
|
||||
@ -1,222 +1,6 @@
|
||||
/* Definitions for light mode */
|
||||
html:not(.dark) .highlight .chroma .c1
|
||||
{
|
||||
color: forestgreen !important;
|
||||
}
|
||||
|
||||
html:not(.dark) .highlight .chroma .cp,
|
||||
html:not(.dark) .highlight .chroma .cpf {
|
||||
color: mediumslateblue !important;
|
||||
}
|
||||
|
||||
html:not(.dark) h1 {
|
||||
color: white !important;
|
||||
width: 100%;
|
||||
background-color: coral !important;
|
||||
border: width 0.15rem solid rgb(163, 81, 51) !important;
|
||||
padding: 0.5rem 0.5rem !important;
|
||||
}
|
||||
|
||||
html:not(.dark) h2 {
|
||||
color: royalblue !important;
|
||||
border-bottom: 3px solid black !important;
|
||||
}
|
||||
|
||||
html:not(.dark) h3,
|
||||
html:not(.dark) h4
|
||||
{
|
||||
color: darkgoldenrod !important;
|
||||
border-bottom: 2px solid darkgrey !important;
|
||||
}
|
||||
|
||||
html:not(.dark) p {
|
||||
color: black !important;
|
||||
}
|
||||
|
||||
html:not(.dark) dl,
|
||||
html:not(.dark) dl dt code,
|
||||
html:not(.dark) dl dd {
|
||||
color: black !important;
|
||||
background-color: #eeeeee !important;
|
||||
}
|
||||
|
||||
html:not(.dark) code,
|
||||
html:not(.dark) pre,
|
||||
html:not(.dark) pre code {
|
||||
color: black !important;
|
||||
background-color: #eeeeee !important;
|
||||
}
|
||||
|
||||
html:not(.dark) hr {
|
||||
background-color: #eeeeee !important;
|
||||
}
|
||||
|
||||
html:not(.dark) .hx\:font-extrabold {
|
||||
font-family: Roboto, sans-serif !important;
|
||||
font-size: 1.6rem !important;
|
||||
color: royalblue !important;
|
||||
}
|
||||
|
||||
/* inactive */
|
||||
html:not(.dark) .hx\:text-gray-500 {
|
||||
color: black !important;
|
||||
}
|
||||
|
||||
/* active */
|
||||
html:not(.dark) .hextra-sidebar-active-item {
|
||||
color: black !important;
|
||||
}
|
||||
|
||||
html:not(.dark) .content p.label-text {
|
||||
color: darkgoldenrod !important;
|
||||
}
|
||||
|
||||
/* Regular item hover */
|
||||
html:not(.dark) .hextra-sidebar-item a:hover,
|
||||
html:not(.dark) .hextra-sidebar-item a:hover span {
|
||||
background-color: rgb(250, 195, 175) !important;
|
||||
color: black !important;
|
||||
}
|
||||
|
||||
/* Active item default + hover - must come AFTER regular hover */
|
||||
html:not(.dark) .hextra-sidebar-item[data-active="true"] a,
|
||||
html:not(.dark) .hextra-sidebar-item[data-active="true"] a span,
|
||||
html:not(.dark) .hextra-sidebar-item[data-active="true"] a:hover,
|
||||
html:not(.dark) .hextra-sidebar-item[data-active="true"] a:hover span {
|
||||
background-color: coral !important;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
|
||||
/* Definitions for dark mode */
|
||||
html.dark .highlight .chroma .c1
|
||||
{
|
||||
color: lightgreen !important;
|
||||
}
|
||||
|
||||
html.dark .highlight .chroma .cp,
|
||||
html.dark.highlight .chroma .cpf {
|
||||
color: lightsteelblue !important;
|
||||
}
|
||||
|
||||
html.dark body {
|
||||
background-color: #1e1e1e !important;
|
||||
}
|
||||
|
||||
html.dark main {
|
||||
background-color: #252525 !important;
|
||||
}
|
||||
|
||||
html.dark .hx\:dark\:shadow-\[0_-12px_16px_\#111\] {
|
||||
box-shadow: 0 -12px 16px #1e1e1e !important;
|
||||
}
|
||||
|
||||
html.dark .hx\:dark\:bg-dark {
|
||||
background-color: #1e1e1e !important;
|
||||
border-color: grey !important;
|
||||
}
|
||||
|
||||
html.dark h1
|
||||
{
|
||||
color: white !important;
|
||||
width: 100%;
|
||||
background-color: rgb(177, 87, 54) !important;
|
||||
padding: 0.5rem 0.5rem !important;
|
||||
}
|
||||
|
||||
html.dark h2 {
|
||||
color: #7099c7 !important;
|
||||
border-bottom: 2px solid #c0c0c0 !important;
|
||||
}
|
||||
|
||||
html.dark h3,
|
||||
html.dark h4 {
|
||||
color: lightgreen !important;
|
||||
border-bottom: 2px solid #505050 !important;
|
||||
}
|
||||
|
||||
html.dark p {
|
||||
color: #c0c0c0 !important;
|
||||
}
|
||||
|
||||
html.dark p strong {
|
||||
color: #c4c3a6;
|
||||
}
|
||||
|
||||
html.dark dl,
|
||||
html.dark dl dt code,
|
||||
html.dark dl dd {
|
||||
color: #c0c0c0 !important;
|
||||
background-color: #444444 !important;
|
||||
}
|
||||
|
||||
html.dark code,
|
||||
html.dark pre,
|
||||
html.dark pre code {
|
||||
color: #c0c0c0 !important;
|
||||
background-color: #444444 !important;
|
||||
}
|
||||
|
||||
html.dark hr {
|
||||
background-color: #505050 !important;
|
||||
}
|
||||
|
||||
html.dark .hx\:font-extrabold {
|
||||
font-family: Roboto, sans-serif !important;
|
||||
font-size: 1.6rem !important;
|
||||
color: #7099c7 !important;
|
||||
}
|
||||
|
||||
/* inactive */
|
||||
html.dark .hx\:text-gray-500 {
|
||||
color: #c0c0c0 !important;
|
||||
}
|
||||
|
||||
/* active */
|
||||
html.dark .hextra-sidebar-active-item {
|
||||
color: #c0c0c0 !important;
|
||||
}
|
||||
|
||||
html.dark .hextra-code-block .highlight .chroma span.p {
|
||||
color: #c0c0c0;
|
||||
}
|
||||
|
||||
html.dark .content p.label-text {
|
||||
color: salmon !important;
|
||||
}
|
||||
|
||||
/* Regular item hover */
|
||||
html.dark .hextra-sidebar-item a:hover,
|
||||
html.dark .hextra-sidebar-item a:hover span {
|
||||
background-color: rgb(250, 195, 175) !important;
|
||||
color: black !important;
|
||||
}
|
||||
|
||||
/* Active item default + hover - must come AFTER regular hover */
|
||||
html.dark .hextra-sidebar-item[data-active="true"] a,
|
||||
html.dark .hextra-sidebar-item[data-active="true"] a span,
|
||||
html.dark .hextra-sidebar-item[data-active="true"] a:hover,
|
||||
html.dark .hextra-sidebar-item[data-active="true"] a:hover span {
|
||||
background-color: rgb(177, 87, 54) !important;
|
||||
color: #e0e0e0 !important;
|
||||
}
|
||||
|
||||
/* Common definitions */
|
||||
|
||||
@font-face {
|
||||
font-family: Roboto;
|
||||
src: url('/fonts/roboto-v51-latin-regular.woff2') format('woff2');
|
||||
font-weight:500;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Roboto Mono';
|
||||
src: url('/fonts/roboto-mono-v31-latin-500.woff2') format('woff2');
|
||||
font-weight: nornal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/**********************/
|
||||
/* .highlight .chroma */
|
||||
/**********************/
|
||||
.highlight .chroma .k,
|
||||
.highlight .chroma .kc,
|
||||
.highlight .chroma .kd,
|
||||
@ -233,64 +17,21 @@ html.dark .hextra-sidebar-item[data-active="true"] a:hover span {
|
||||
font-style: normal !important;
|
||||
}
|
||||
|
||||
.hx\:mb-0 {
|
||||
display: block;
|
||||
width: 100%;
|
||||
html.dark .highlight .chroma .cp,
|
||||
html.dark.highlight .chroma .cpf {
|
||||
color: lightgreen !important;
|
||||
color: lightsteelblue !important;
|
||||
}
|
||||
|
||||
.hextra-code-block {
|
||||
margin-top: 0.5rem !important;
|
||||
|
||||
}
|
||||
|
||||
/* .highlight .chroma .c1
|
||||
{
|
||||
html:not(.dark) .highlight .chroma .cp,
|
||||
html:not(.dark) .highlight .chroma .cpf {
|
||||
color: forestgreen !important;
|
||||
}
|
||||
|
||||
.highlight .chroma .cp,
|
||||
.highlight .chroma .cpf {
|
||||
color: mediumslateblue !important;
|
||||
} */
|
||||
|
||||
html.dark .hextra-code-block .highlight .chroma .cs,
|
||||
html.dark .hextra-code-block .highlight .chroma .cp,
|
||||
html.dark .hextra-code-block .highlight .chroma .cpf {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.badges {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px;
|
||||
justify-content: flex-start;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.badges p {
|
||||
display: contents;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.badges div {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.content ul li {
|
||||
font-family: Roboto, sans-serif !important;
|
||||
font-style: normal !important;
|
||||
font-weight: normal !important;
|
||||
font-size: 1.1rem !important;
|
||||
}
|
||||
|
||||
.err {
|
||||
background-color: transparent !important;
|
||||
color: inherit !important;
|
||||
}
|
||||
|
||||
/***********************/
|
||||
/* h1 */
|
||||
/***********************/
|
||||
h1 {
|
||||
font-family: Roboto, sans-serif !important;
|
||||
font-style: i !important;
|
||||
@ -301,6 +42,25 @@ h1 {
|
||||
border-radius: 0.5rem !important;
|
||||
}
|
||||
|
||||
html.dark h1
|
||||
{
|
||||
color: white !important;
|
||||
width: 100%;
|
||||
background-color: rgb(177, 87, 54) !important;
|
||||
padding: 0.5rem 0.5rem !important;
|
||||
}
|
||||
|
||||
html:not(.dark) h1 {
|
||||
color: white !important;
|
||||
width: 100%;
|
||||
background-color: coral !important;
|
||||
border: width 0.15rem solid rgb(163, 81, 51) !important;
|
||||
padding: 0.5rem 0.5rem !important;
|
||||
}
|
||||
|
||||
/***********************/
|
||||
/* h2 */
|
||||
/***********************/
|
||||
h2 {
|
||||
font-family: Roboto, sans-serif !important;
|
||||
font-style: normal !important;
|
||||
@ -310,6 +70,19 @@ h2 {
|
||||
margin-bottom: 0.5em !important;
|
||||
}
|
||||
|
||||
html.dark h2 {
|
||||
color: #7099c7 !important;
|
||||
border-bottom: 2px solid #c0c0c0 !important;
|
||||
}
|
||||
|
||||
html:not(.dark) h2 {
|
||||
color: royalblue !important;
|
||||
border-bottom: 3px solid black !important;
|
||||
}
|
||||
|
||||
/***********************/
|
||||
/* h3 */
|
||||
/***********************/
|
||||
h3 {
|
||||
font-family: Roboto, sans-serif !important;
|
||||
font-style: normal !important;
|
||||
@ -321,6 +94,20 @@ h3 {
|
||||
#display: inline-block;
|
||||
}
|
||||
|
||||
html.dark h3 {
|
||||
color: lightgreen !important;
|
||||
border-bottom: 2px solid #505050 !important;
|
||||
}
|
||||
|
||||
html:not(.dark) h3
|
||||
{
|
||||
color: darkgoldenrod !important;
|
||||
border-bottom: 2px solid darkgrey !important;
|
||||
}
|
||||
|
||||
/***********************/
|
||||
/* h4 */
|
||||
/***********************/
|
||||
h4 {
|
||||
font-family: Roboto, sans-serif !important;
|
||||
font-style: normal !important;
|
||||
@ -331,6 +118,43 @@ h4 {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
html.dark h4 {
|
||||
color: lightgreen !important;
|
||||
border-bottom: 2px solid #505050 !important;
|
||||
}
|
||||
|
||||
html:not(.dark) h4
|
||||
{
|
||||
color: darkgoldenrod !important;
|
||||
border-bottom: 2px solid darkgrey !important;
|
||||
}
|
||||
|
||||
/***********************/
|
||||
/* p */
|
||||
/***********************/
|
||||
p {
|
||||
font-family: Roboto, sans-serif !important;
|
||||
font-style: normal !important;
|
||||
font-weight: normal !important;
|
||||
font-size: 1.1rem !important;
|
||||
margin-top: 0.5em !important;
|
||||
}
|
||||
|
||||
html.dark p {
|
||||
color: #c0c0c0 !important;
|
||||
}
|
||||
|
||||
html.dark p strong {
|
||||
color: #c4c3a6;
|
||||
}
|
||||
|
||||
html:not(.dark) p {
|
||||
color: black !important;
|
||||
}
|
||||
|
||||
/***********************/
|
||||
/* dl */
|
||||
/***********************/
|
||||
dl {
|
||||
font-family: 'Roboto Mono', monospace !important;
|
||||
font-style: normal !important;
|
||||
@ -352,6 +176,366 @@ dl dd {
|
||||
font-size: 1rem !important;
|
||||
}
|
||||
|
||||
html.dark dl,
|
||||
html.dark dl dt code,
|
||||
html.dark dl dd {
|
||||
color: #c0c0c0 !important;
|
||||
background-color: #444444 !important;
|
||||
}
|
||||
|
||||
html:not(.dark) dl,
|
||||
html:not(.dark) dl dt code,
|
||||
html:not(.dark) dl dd {
|
||||
color: black !important;
|
||||
background-color: #eeeeee !important;
|
||||
}
|
||||
|
||||
/***********************/
|
||||
/* pre & code */
|
||||
/***********************/
|
||||
code {
|
||||
font-family: 'Roboto Mono', monospace !important;
|
||||
font-weight: normal !important;
|
||||
font-style: normal !important;
|
||||
font-size: 0.9rem !important;
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
pre {
|
||||
font-family: 'Roboto Mono', monospace !important;
|
||||
font-size: 0.9rem !important;
|
||||
margin-top: 0em !important;
|
||||
margin-bottom: 0em !important;
|
||||
padding-left: 0.5em !important;
|
||||
padding-right: 0.5em !important;
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
pre code {
|
||||
font-family: 'Roboto Mono', monospace !important;
|
||||
font-style: normal !important;
|
||||
font-weight: normal !important;
|
||||
font-size: 0.9rem !important;
|
||||
margin-left: 0 !important;
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
html.dark code,
|
||||
html.dark pre,
|
||||
html.dark pre code {
|
||||
color: #c0c0c0 !important;
|
||||
background-color: #444444 !important;
|
||||
}
|
||||
|
||||
html:not(.dark) code,
|
||||
html:not(.dark) pre,
|
||||
html:not(.dark) pre code {
|
||||
color: black !important;
|
||||
background-color: #eeeeee !important;
|
||||
}
|
||||
|
||||
/***********************/
|
||||
/* hr */
|
||||
/***********************/
|
||||
hr {
|
||||
height: 2px !important;
|
||||
border: none !important;
|
||||
margin-top: 1em !important;
|
||||
margin-bottom: 1em !important;
|
||||
}
|
||||
|
||||
html.dark hr {
|
||||
background-color: #505050 !important;
|
||||
}
|
||||
|
||||
html:not(.dark) hr {
|
||||
background-color: #eeeeee !important;
|
||||
}
|
||||
|
||||
/***********************/
|
||||
/* body */
|
||||
/***********************/
|
||||
html.dark body {
|
||||
background-color: #1e1e1e !important;
|
||||
}
|
||||
|
||||
/***********************/
|
||||
/* .hx\:font-extrabold */
|
||||
/***********************/
|
||||
html.dark .hx\:font-extrabold {
|
||||
font-family: Roboto, sans-serif !important;
|
||||
font-size: 1.6rem !important;
|
||||
color: #7099c7 !important;
|
||||
}
|
||||
|
||||
html:not(.dark) .hx\:font-extrabold {
|
||||
font-family: Roboto, sans-serif !important;
|
||||
font-size: 1.6rem !important;
|
||||
color: royalblue !important;
|
||||
}
|
||||
|
||||
/***********************/
|
||||
/* .hx\:text-gray-500 */
|
||||
/* inactive */
|
||||
/***********************/
|
||||
html.dark .hx\:text-gray-500 {
|
||||
color: #c0c0c0 !important;
|
||||
}
|
||||
|
||||
html:not(.dark) .hx\:text-gray-500 {
|
||||
color: black !important;
|
||||
}
|
||||
|
||||
/*******************************/
|
||||
/* .hextra-sidebar-active-item */
|
||||
/* active */
|
||||
/*******************************/
|
||||
html.dark .hextra-sidebar-active-item {
|
||||
color: #c0c0c0 !important;
|
||||
}
|
||||
|
||||
html:not(.dark) .hextra-sidebar-active-item {
|
||||
color: black !important;
|
||||
}
|
||||
|
||||
/*************************/
|
||||
/* .content p.label-text */
|
||||
/*************************/
|
||||
html.dark .content p.label-text {
|
||||
color: salmon !important;
|
||||
}
|
||||
|
||||
html:not(.dark) .content p.label-text {
|
||||
color: darkgoldenrod !important;
|
||||
}
|
||||
|
||||
/********************************/
|
||||
/* .hextra-sidebar-item a:hover */
|
||||
/********************************/
|
||||
html.dark .hextra-sidebar-item a:hover,
|
||||
html.dark .hextra-sidebar-item a:hover span {
|
||||
background-color: rgb(250, 195, 175) !important;
|
||||
color: black !important;
|
||||
}
|
||||
|
||||
html:not(.dark) .hextra-sidebar-item a:hover,
|
||||
html:not(.dark) .hextra-sidebar-item a:hover span {
|
||||
background-color: rgb(250, 195, 175) !important;
|
||||
color: black !important;
|
||||
}
|
||||
|
||||
/*********************************/
|
||||
/* .hextra-sidebar-item */
|
||||
/* Active item default + hover */
|
||||
/* Must come AFTER regular hover */
|
||||
/*********************************/
|
||||
html.dark .hextra-sidebar-item[data-active="true"] a,
|
||||
html.dark .hextra-sidebar-item[data-active="true"] a span,
|
||||
html.dark .hextra-sidebar-item[data-active="true"] a:hover,
|
||||
html.dark .hextra-sidebar-item[data-active="true"] a:hover span {
|
||||
background-color: rgb(177, 87, 54) !important;
|
||||
color: #e0e0e0 !important;
|
||||
}
|
||||
|
||||
html:not(.dark) .hextra-sidebar-item[data-active="true"] a,
|
||||
html:not(.dark) .hextra-sidebar-item[data-active="true"] a span,
|
||||
html:not(.dark) .hextra-sidebar-item[data-active="true"] a:hover,
|
||||
html:not(.dark) .hextra-sidebar-item[data-active="true"] a:hover span {
|
||||
background-color: coral !important;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
/***********************/
|
||||
/* main */
|
||||
/***********************/
|
||||
html.dark main {
|
||||
background-color: #252525 !important;
|
||||
}
|
||||
|
||||
/***********************/
|
||||
/* .hx\:dark\:shadow */
|
||||
/***********************/
|
||||
html.dark .hx\:dark\:shadow-\[0_-12px_16px_\#111\] {
|
||||
box-shadow: 0 -12px 16px #1e1e1e !important;
|
||||
}
|
||||
|
||||
/***********************/
|
||||
/* .hx\:dark\:bg-dark */
|
||||
/***********************/
|
||||
html.dark .hx\:dark\:bg-dark {
|
||||
background-color: #1e1e1e !important;
|
||||
border-color: grey !important;
|
||||
}
|
||||
|
||||
/***********************/
|
||||
/* hextra-code-block */
|
||||
/***********************/
|
||||
.hextra-code-block {
|
||||
margin-top: 0.5rem !important;
|
||||
}
|
||||
|
||||
html.dark .hextra-code-block .highlight .chroma span.p {
|
||||
color: #c0c0c0;
|
||||
}
|
||||
|
||||
html.dark .hextra-code-block .highlight .chroma .cs,
|
||||
html.dark .hextra-code-block .highlight .chroma .cp,
|
||||
html.dark .hextra-code-block .highlight .chroma .cpf {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
/***********************/
|
||||
/* complexity */
|
||||
/***********************/
|
||||
.complexity {
|
||||
padding: 3px 6px;
|
||||
border-radius: 0.5rem !important;
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem !important;
|
||||
letter-spacing: 0.04em;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
html.dark .complexity {
|
||||
&.constant {
|
||||
color: #7CFFB2;
|
||||
background: rgba(0,255,120,0.08);
|
||||
border-color: rgba(0,255,120,0.25);
|
||||
}
|
||||
&.logarithmic {
|
||||
color: #7CE7FF;
|
||||
background: rgba(0,200,255,0.08);
|
||||
border-color: rgba(0,200,255,0.18);
|
||||
}
|
||||
&.linear {
|
||||
color: #6AB7FF;
|
||||
background: rgba(80,140,255,0.08);
|
||||
border-color: rgba(80,140,255,0.18);
|
||||
}
|
||||
&.linearithmic {
|
||||
color: #B78CFF;
|
||||
background: rgba(140,80,255,0.08);
|
||||
border-color: rgba(140,80,255,0.18);
|
||||
}
|
||||
&.polynomial {
|
||||
color: #FFD36A;
|
||||
background: rgba(255,190,0,0.08);
|
||||
border-color: rgba(255,190,0,0.18);
|
||||
}
|
||||
&.exponential {
|
||||
color: #ff7d9c;
|
||||
background: rgba(255,0,80,0.08);
|
||||
border-color: rgba(255,0,80,0.2);
|
||||
}
|
||||
}
|
||||
|
||||
html:not(.dark) .complexity {
|
||||
&.constant {
|
||||
color: #15803D;
|
||||
background: rgba(34,197,94,0.10);
|
||||
border-color: rgba(34,197,94,0.22);
|
||||
}
|
||||
|
||||
&.logarithmic {
|
||||
color: #0E7490;
|
||||
background: rgba(6,182,212,0.10);
|
||||
border-color: rgba(6,182,212,0.22);
|
||||
}
|
||||
|
||||
&.linear {
|
||||
color: #2563EB;
|
||||
background: rgba(59,130,246,0.10);
|
||||
border-color: rgba(59,130,246,0.22);
|
||||
}
|
||||
|
||||
&.linearithmic {
|
||||
color: #7C3AED;
|
||||
background: rgba(139,92,246,0.10);
|
||||
border-color: rgba(139,92,246,0.22);
|
||||
}
|
||||
|
||||
&.polynomial {
|
||||
color: #CA8A04;
|
||||
background: rgba(234,179,8,0.12);
|
||||
border-color: rgba(234,179,8,0.24);
|
||||
}
|
||||
|
||||
&.exponential {
|
||||
color: #DC2626;
|
||||
background: rgba(239,68,68,0.10);
|
||||
border-color: rgba(239,68,68,0.22);
|
||||
}
|
||||
}
|
||||
|
||||
/***********************/
|
||||
/* font-face */
|
||||
/***********************/
|
||||
@font-face {
|
||||
font-family: Roboto;
|
||||
src: url('/fonts/roboto-v51-latin-regular.woff2') format('woff2');
|
||||
font-weight:500;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Roboto Mono';
|
||||
src: url('/fonts/roboto-mono-v31-latin-500.woff2') format('woff2');
|
||||
font-weight: nornal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/***********************/
|
||||
/* .hx\:mb */
|
||||
/***********************/
|
||||
.hx\:mb-0 {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/***********************/
|
||||
/* .badges */
|
||||
/***********************/
|
||||
.badges {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px;
|
||||
justify-content: flex-start;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.badges p {
|
||||
display: contents;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.badges div {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
/***********************/
|
||||
/* .content */
|
||||
/***********************/
|
||||
.content ul li {
|
||||
font-family: Roboto, sans-serif !important;
|
||||
font-style: normal !important;
|
||||
font-weight: normal !important;
|
||||
font-size: 1.1rem !important;
|
||||
}
|
||||
|
||||
/***********************/
|
||||
/* .err */
|
||||
/***********************/
|
||||
.err {
|
||||
background-color: transparent !important;
|
||||
color: inherit !important;
|
||||
}
|
||||
|
||||
/***********************/
|
||||
/* ol */
|
||||
/***********************/
|
||||
ol {
|
||||
font-family: 'Roboto Mono', monospace !important;
|
||||
font-style: normal !important;
|
||||
@ -373,51 +557,10 @@ ol li code {
|
||||
font-size: 1rem !important;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: 'Roboto Mono', monospace !important;
|
||||
font-weight: normal !important;
|
||||
font-style: normal !important;
|
||||
font-size: 0.9rem !important;
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
pre code {
|
||||
font-family: 'Roboto Mono', monospace !important;
|
||||
font-style: normal !important;
|
||||
font-weight: normal !important;
|
||||
font-size: 0.9rem !important;
|
||||
margin-left: 0 !important;
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
pre {
|
||||
font-family: 'Roboto Mono', monospace !important;
|
||||
font-size: 0.9rem !important;
|
||||
margin-top: 0em !important;
|
||||
margin-bottom: 0em !important;
|
||||
padding-left: 0.5em !important;
|
||||
padding-right: 0.5em !important;
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
hr {
|
||||
height: 2px !important;
|
||||
border: none !important;
|
||||
margin-top: 1em !important;
|
||||
margin-bottom: 1em !important;
|
||||
}
|
||||
|
||||
p {
|
||||
font-family: Roboto, sans-serif !important;
|
||||
font-style: normal !important;
|
||||
font-weight: normal !important;
|
||||
font-size: 1.1rem !important;
|
||||
margin-top: 0.5em !important;
|
||||
}
|
||||
|
||||
/***********************/
|
||||
/* tr */
|
||||
/***********************/
|
||||
tr td code{
|
||||
font-family: 'Roboto Mono', monospace !important;
|
||||
font-size: 0.8rem !important;
|
||||
}
|
||||
|
||||
|
||||
|
||||
3
hugo/layouts/shortcodes/complexity.html
Normal file
3
hugo/layouts/shortcodes/complexity.html
Normal file
@ -0,0 +1,3 @@
|
||||
<span class="complexity {{ .Get 0 }}">
|
||||
𝐎({{ .Get 1 }})
|
||||
</span>
|
||||
@ -35,6 +35,7 @@ SOFTWARE.
|
||||
#include "absolute.h"
|
||||
#include "algorithm.h"
|
||||
#include "error_handler.h"
|
||||
#include "functional.h"
|
||||
#include "limits.h"
|
||||
#include "math.h"
|
||||
#include "static_assert.h"
|
||||
@ -978,6 +979,400 @@ namespace etl
|
||||
T min_value;
|
||||
T max_value;
|
||||
};
|
||||
|
||||
#if ETL_USING_CPP11
|
||||
//***************************************************************************
|
||||
/// An immutable pair of runtime limits that may be shared by multiple
|
||||
/// referenced_clamped_value objects.
|
||||
///@tparam T An integral type, or a floating-point type when
|
||||
/// ETL_HAS_FLOATING_POINT_CLAMPED_VALUE is `1`.
|
||||
///\ingroup clamped_value
|
||||
//***************************************************************************
|
||||
template <typename T>
|
||||
class clamped_value_range
|
||||
{
|
||||
public:
|
||||
|
||||
typedef typename private_clamped_value::traits<T>::limits_type limits_type;
|
||||
|
||||
//*************************************************************************
|
||||
/// Constructs the full representable range of T.
|
||||
//*************************************************************************
|
||||
ETL_CONSTEXPR14 clamped_value_range()
|
||||
: min_value(limits_type::lowest())
|
||||
, max_value(limits_type::max())
|
||||
{
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Constructs a range with the supplied limits.
|
||||
/// Reversed bounds and NaN bounds are rejected.
|
||||
///\param min_ The minimum value.
|
||||
///\param max_ The maximum value.
|
||||
//*************************************************************************
|
||||
ETL_CONSTEXPR14 clamped_value_range(T min_, T max_)
|
||||
: min_value(min_)
|
||||
, max_value(max_)
|
||||
{
|
||||
private_clamped_value::validate(min_);
|
||||
private_clamped_value::validate(max_);
|
||||
ETL_ASSERT(min_ <= max_, ETL_ERROR_GENERIC("clamped_value_range: invalid range"));
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Gets the minimum value.
|
||||
//*************************************************************************
|
||||
ETL_NODISCARD ETL_CONSTEXPR T min() const ETL_NOEXCEPT
|
||||
{
|
||||
return min_value;
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Gets the maximum value.
|
||||
//*************************************************************************
|
||||
ETL_NODISCARD ETL_CONSTEXPR T max() const ETL_NOEXCEPT
|
||||
{
|
||||
return max_value;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
const T min_value;
|
||||
const T max_value;
|
||||
};
|
||||
|
||||
//***************************************************************************
|
||||
/// Provides a value clamped to a shared immutable runtime range.
|
||||
/// The referenced range must outlive this object.
|
||||
///@tparam T An integral type, or a floating-point type when
|
||||
/// ETL_HAS_FLOATING_POINT_CLAMPED_VALUE is `1`.
|
||||
///\ingroup clamped_value
|
||||
//***************************************************************************
|
||||
template <typename T>
|
||||
class referenced_clamped_value
|
||||
{
|
||||
public:
|
||||
|
||||
/// The referenced range type.
|
||||
typedef clamped_value_range<T> range_type;
|
||||
|
||||
/// The type used to advance or subtract from the stored value.
|
||||
typedef typename private_clamped_value::traits<T>::difference_type difference_type;
|
||||
|
||||
/// Numeric limits for the stored value type.
|
||||
typedef typename private_clamped_value::traits<T>::limits_type limits_type;
|
||||
|
||||
/// Numeric limits for difference_type.
|
||||
typedef typename private_clamped_value::traits<T>::difference_limits_type difference_limits_type;
|
||||
|
||||
//*************************************************************************
|
||||
/// Constructs a value initialized to the minimum of the referenced range.
|
||||
///\param range_ The range to reference. It must outlive this object.
|
||||
//*************************************************************************
|
||||
explicit referenced_clamped_value(const range_type& range_)
|
||||
: value(range_.min())
|
||||
, value_range(etl::cref(range_))
|
||||
{
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Constructs a value clamped to the referenced range.
|
||||
///\param range_ The range to reference. It must outlive this object.
|
||||
///\param initial The initial value.
|
||||
//*************************************************************************
|
||||
referenced_clamped_value(const range_type& range_, T initial)
|
||||
: value(private_clamped_value::validated_clamp(initial, range_.min(), range_.max()))
|
||||
, value_range(etl::cref(range_))
|
||||
{
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Prevents construction with a temporary range.
|
||||
//*************************************************************************
|
||||
referenced_clamped_value(range_type&&) = delete;
|
||||
referenced_clamped_value(range_type&&, T) = delete;
|
||||
|
||||
//*************************************************************************
|
||||
/// Copies the value and its range reference.
|
||||
///\param other The value to copy.
|
||||
//*************************************************************************
|
||||
referenced_clamped_value(const referenced_clamped_value& other) ETL_NOEXCEPT
|
||||
: value(other.value)
|
||||
, value_range(other.value_range)
|
||||
{
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Copy assignment. Copies the value and its range reference.
|
||||
///\param other The value and range reference to copy.
|
||||
///\return A reference to this value.
|
||||
//*************************************************************************
|
||||
referenced_clamped_value& operator=(const referenced_clamped_value& other) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
value = other.value;
|
||||
value_range = other.value_range;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Sets the value and clamps it to the referenced range.
|
||||
///\param value_ The value to assign.
|
||||
//*************************************************************************
|
||||
void set(T value_)
|
||||
{
|
||||
value = private_clamped_value::validated_clamp(value_, min(), max());
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Resets the value to the minimum of the referenced range.
|
||||
//*************************************************************************
|
||||
void to_min() ETL_NOEXCEPT
|
||||
{
|
||||
value = min();
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Resets the value to the maximum of the referenced range.
|
||||
//*************************************************************************
|
||||
void to_max() ETL_NOEXCEPT
|
||||
{
|
||||
value = max();
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Advances the value and saturates it at the referenced bounds.
|
||||
///\param n The difference to add.
|
||||
//*************************************************************************
|
||||
void advance(difference_type n)
|
||||
{
|
||||
private_clamped_value::validate(n);
|
||||
value = private_clamped_value::advance(value, min(), max(), n);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Converts to the underlying value type.
|
||||
///\return The current value.
|
||||
//*************************************************************************
|
||||
ETL_NODISCARD operator T() const ETL_NOEXCEPT
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Advances by one and saturates at the maximum.
|
||||
///\return A reference to this value.
|
||||
//*************************************************************************
|
||||
referenced_clamped_value& operator++() ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT
|
||||
{
|
||||
value = private_clamped_value::advance(value, min(), max(), difference_type(1));
|
||||
return *this;
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Advances by one and returns the previous value.
|
||||
///\return The value before incrementing.
|
||||
//*************************************************************************
|
||||
referenced_clamped_value operator++(int) ETL_NOEXCEPT
|
||||
{
|
||||
referenced_clamped_value temp(*this);
|
||||
++(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Subtracts one and saturates at the minimum.
|
||||
///\return A reference to this value.
|
||||
//*************************************************************************
|
||||
referenced_clamped_value& operator--() ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT
|
||||
{
|
||||
value = private_clamped_value::advance(value, min(), max(), difference_type(-1));
|
||||
return *this;
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Subtracts one and returns the previous value.
|
||||
///\return The value before decrementing.
|
||||
//*************************************************************************
|
||||
referenced_clamped_value operator--(int) ETL_NOEXCEPT
|
||||
{
|
||||
referenced_clamped_value temp(*this);
|
||||
--(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Assigns a value and clamps it to the referenced range.
|
||||
///\param value_ The value to assign.
|
||||
///\return A reference to this value.
|
||||
//*************************************************************************
|
||||
referenced_clamped_value& operator=(T value_) ETL_LVALUE_REF_QUALIFIER
|
||||
{
|
||||
set(value_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Adds a difference and clamps to the referenced range.
|
||||
///\param n The difference to add.
|
||||
///\return A reference to this value.
|
||||
//*************************************************************************
|
||||
referenced_clamped_value& operator+=(difference_type n) ETL_LVALUE_REF_QUALIFIER
|
||||
{
|
||||
advance(n);
|
||||
return *this;
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Subtracts a difference and clamps to the referenced range.
|
||||
///\param n The difference to subtract.
|
||||
///\return A reference to this value.
|
||||
//*************************************************************************
|
||||
referenced_clamped_value& operator-=(difference_type n) ETL_LVALUE_REF_QUALIFIER
|
||||
{
|
||||
private_clamped_value::validate(n);
|
||||
value = private_clamped_value::subtract(value, min(), max(), n);
|
||||
return *this;
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Gets the current value.
|
||||
///\return The current value.
|
||||
//*************************************************************************
|
||||
ETL_NODISCARD
|
||||
T get() const ETL_NOEXCEPT
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Gets the minimum of the referenced range.
|
||||
///\return The minimum value.
|
||||
//*************************************************************************
|
||||
ETL_NODISCARD
|
||||
T min() const ETL_NOEXCEPT
|
||||
{
|
||||
return value_range.get().min();
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Gets the maximum of the referenced range.
|
||||
///\return The maximum value.
|
||||
//*************************************************************************
|
||||
ETL_NODISCARD
|
||||
T max() const ETL_NOEXCEPT
|
||||
{
|
||||
return value_range.get().max();
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Gets the referenced range.
|
||||
///\return The referenced range.
|
||||
//*************************************************************************
|
||||
ETL_NODISCARD
|
||||
const range_type& range() const ETL_NOEXCEPT
|
||||
{
|
||||
return value_range.get();
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Swaps the values and range references.
|
||||
///\param other The value to swap with.
|
||||
//*************************************************************************
|
||||
void swap(referenced_clamped_value& other) ETL_NOEXCEPT
|
||||
{
|
||||
using ETL_OR_STD::swap;
|
||||
swap(value, other.value);
|
||||
swap(value_range, other.value_range);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Swaps the values and range references.
|
||||
///\param lhs The first value.
|
||||
///\param rhs The second value.
|
||||
//*************************************************************************
|
||||
friend void swap(referenced_clamped_value& lhs, referenced_clamped_value& rhs) ETL_NOEXCEPT
|
||||
{
|
||||
lhs.swap(rhs);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Compares current values for equality.
|
||||
///\param lhs The left-hand value.
|
||||
///\param rhs The right-hand value.
|
||||
///\return `true` if the current values are equal.
|
||||
//*************************************************************************
|
||||
friend bool operator==(const referenced_clamped_value& lhs, const referenced_clamped_value& rhs) ETL_NOEXCEPT
|
||||
{
|
||||
#include "private/diagnostic_float_equal_push.h"
|
||||
return lhs.value == rhs.value;
|
||||
#include "private/diagnostic_pop.h"
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Compares current values for inequality.
|
||||
///\param lhs The left-hand value.
|
||||
///\param rhs The right-hand value.
|
||||
///\return `true` if the current values differ.
|
||||
//*************************************************************************
|
||||
friend bool operator!=(const referenced_clamped_value& lhs, const referenced_clamped_value& rhs) ETL_NOEXCEPT
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Compares current values using less-than.
|
||||
///\param lhs The left-hand value.
|
||||
///\param rhs The right-hand value.
|
||||
///\return `true` if lhs is less than rhs.
|
||||
//*************************************************************************
|
||||
friend bool operator<(const referenced_clamped_value& lhs, const referenced_clamped_value& rhs) ETL_NOEXCEPT
|
||||
{
|
||||
return lhs.value < rhs.value;
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Compares current values using less-than-or-equal.
|
||||
///\param lhs The left-hand value.
|
||||
///\param rhs The right-hand value.
|
||||
///\return `true` if lhs is less than or equal to rhs.
|
||||
//*************************************************************************
|
||||
friend bool operator<=(const referenced_clamped_value& lhs, const referenced_clamped_value& rhs) ETL_NOEXCEPT
|
||||
{
|
||||
return !(rhs < lhs);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Compares current values using greater-than.
|
||||
///\param lhs The left-hand value.
|
||||
///\param rhs The right-hand value.
|
||||
///\return `true` if lhs is greater than rhs.
|
||||
//*************************************************************************
|
||||
friend bool operator>(const referenced_clamped_value& lhs, const referenced_clamped_value& rhs) ETL_NOEXCEPT
|
||||
{
|
||||
return rhs < lhs;
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Compares current values using greater-than-or-equal.
|
||||
///\param lhs The left-hand value.
|
||||
///\param rhs The right-hand value.
|
||||
///\return `true` if lhs is greater than or equal to rhs.
|
||||
//*************************************************************************
|
||||
friend bool operator>=(const referenced_clamped_value& lhs, const referenced_clamped_value& rhs) ETL_NOEXCEPT
|
||||
{
|
||||
return !(lhs < rhs);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
T value;
|
||||
etl::reference_wrapper<const range_type> value_range;
|
||||
};
|
||||
#endif
|
||||
} // namespace etl
|
||||
|
||||
#endif
|
||||
|
||||
@ -37,3 +37,13 @@ etl::clamped_value<double> clamped_double_run_time;
|
||||
#if ETL_HAS_COMPILE_TIME_FLOATING_POINT_CLAMPED_VALUE
|
||||
etl::clamped_value<float, -1.0f, 1.0f> clamped_float_compile_time;
|
||||
#endif
|
||||
|
||||
#if ETL_USING_CPP11
|
||||
const etl::clamped_value_range<int> referenced_clamped_range(0, 10);
|
||||
etl::referenced_clamped_value<int> referenced_clamped_int(referenced_clamped_range);
|
||||
|
||||
#if ETL_HAS_FLOATING_POINT_CLAMPED_VALUE
|
||||
const etl::clamped_value_range<float> referenced_clamped_float_range(-1.0f, 1.0f);
|
||||
etl::referenced_clamped_value<float> referenced_clamped_float(referenced_clamped_float_range);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@ -531,5 +531,157 @@ namespace
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if ETL_USING_CPP11
|
||||
//*************************************************************************
|
||||
TEST(test_referenced_clamped_value_uses_shared_range)
|
||||
{
|
||||
const etl::clamped_value_range<int> range(2, 7);
|
||||
etl::referenced_clamped_value<int> value1(range, 1);
|
||||
etl::referenced_clamped_value<int> value2(range, 8);
|
||||
|
||||
CHECK_EQUAL(2, value1.get());
|
||||
CHECK_EQUAL(7, value2.get());
|
||||
CHECK_EQUAL(2, value1.min());
|
||||
CHECK_EQUAL(7, value1.max());
|
||||
CHECK_EQUAL(&range, &value1.range());
|
||||
CHECK_EQUAL(&range, &value2.range());
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_referenced_clamped_value_arithmetic_saturates)
|
||||
{
|
||||
const etl::clamped_value_range<int> range(-2, 3);
|
||||
etl::referenced_clamped_value<int> value(range, 0);
|
||||
|
||||
value += 2;
|
||||
CHECK_EQUAL(2, value.get());
|
||||
++value;
|
||||
++value;
|
||||
CHECK_EQUAL(3, value.get());
|
||||
value -= 4;
|
||||
CHECK_EQUAL(-1, value.get());
|
||||
--value;
|
||||
--value;
|
||||
CHECK_EQUAL(-2, value.get());
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_referenced_clamped_value_modifiers_and_postfix_operators)
|
||||
{
|
||||
const etl::clamped_value_range<int> range(2, 7);
|
||||
etl::referenced_clamped_value<int> value(range);
|
||||
|
||||
CHECK_EQUAL(2, value.get());
|
||||
value.set(4);
|
||||
CHECK_EQUAL(4, value.get());
|
||||
|
||||
etl::referenced_clamped_value<int> previous = value++;
|
||||
CHECK_EQUAL(4, previous.get());
|
||||
CHECK_EQUAL(5, value.get());
|
||||
|
||||
previous = value--;
|
||||
CHECK_EQUAL(5, previous.get());
|
||||
CHECK_EQUAL(4, value.get());
|
||||
|
||||
value.to_max();
|
||||
CHECK_EQUAL(7, value.get());
|
||||
value.to_min();
|
||||
CHECK_EQUAL(2, value.get());
|
||||
|
||||
value = 100;
|
||||
CHECK_EQUAL(7, value.get());
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_referenced_clamped_value_default_range_and_comparisons)
|
||||
{
|
||||
const etl::clamped_value_range<int> default_range;
|
||||
const etl::clamped_value_range<int> range(0, 10);
|
||||
etl::referenced_clamped_value<int> value1(range, 3);
|
||||
etl::referenced_clamped_value<int> value2(range, 4);
|
||||
const int converted = value1;
|
||||
|
||||
CHECK_EQUAL(etl::numeric_limits<int>::lowest(), default_range.min());
|
||||
CHECK_EQUAL(etl::numeric_limits<int>::max(), default_range.max());
|
||||
CHECK_EQUAL(3, converted);
|
||||
CHECK(value1 == value1);
|
||||
CHECK(value1 != value2);
|
||||
CHECK(value1 < value2);
|
||||
CHECK(value1 <= value2);
|
||||
CHECK(value2 > value1);
|
||||
CHECK(value2 >= value1);
|
||||
CHECK(value1 < 4);
|
||||
CHECK(3 == value1);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_referenced_clamped_value_copy_assignment_and_swap_ranges)
|
||||
{
|
||||
const etl::clamped_value_range<int> range1(0, 10);
|
||||
const etl::clamped_value_range<int> range2(20, 30);
|
||||
etl::referenced_clamped_value<int> value1(range1, 4);
|
||||
etl::referenced_clamped_value<int> value2(range2, 24);
|
||||
|
||||
etl::referenced_clamped_value<int> copy(value1);
|
||||
CHECK_EQUAL(&range1, ©.range());
|
||||
CHECK_EQUAL(4, copy.get());
|
||||
|
||||
copy = value2;
|
||||
CHECK_EQUAL(&range2, ©.range());
|
||||
CHECK_EQUAL(24, copy.get());
|
||||
|
||||
swap(value1, value2);
|
||||
CHECK_EQUAL(&range2, &value1.range());
|
||||
CHECK_EQUAL(24, value1.get());
|
||||
CHECK_EQUAL(&range1, &value2.range());
|
||||
CHECK_EQUAL(4, value2.get());
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_referenced_clamped_value_rejects_invalid_range)
|
||||
{
|
||||
CHECK_THROW((etl::clamped_value_range<int>(7, 2)), etl::exception);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_referenced_clamped_value_rejects_temporary_range)
|
||||
{
|
||||
typedef etl::clamped_value_range<int> range_type;
|
||||
typedef etl::referenced_clamped_value<int> value_type;
|
||||
|
||||
CHECK_FALSE((etl::is_constructible<value_type, range_type&&>::value));
|
||||
CHECK_FALSE((etl::is_constructible<value_type, range_type&&, int>::value));
|
||||
}
|
||||
|
||||
#if ETL_HAS_FLOATING_POINT_CLAMPED_VALUE
|
||||
//*************************************************************************
|
||||
TEST(test_referenced_floating_clamped_value)
|
||||
{
|
||||
const etl::clamped_value_range<float> range(-1.5F, 2.5F);
|
||||
etl::referenced_clamped_value<float> value(range, 0.5F);
|
||||
|
||||
value += 1.25F;
|
||||
CHECK_CLOSE(1.75F, value.get(), 0.0001F);
|
||||
value.advance(10.0F);
|
||||
CHECK_CLOSE(2.5F, value.get(), 0.0001F);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_referenced_floating_clamped_value_rejects_nan)
|
||||
{
|
||||
const float nan = etl::numeric_limits<float>::quiet_NaN();
|
||||
|
||||
CHECK_THROW((etl::clamped_value_range<float>(nan, 1.0F)), etl::exception);
|
||||
|
||||
const etl::clamped_value_range<float> range(-1.0F, 1.0F);
|
||||
CHECK_THROW((etl::referenced_clamped_value<float>(range, nan)), etl::exception);
|
||||
|
||||
etl::referenced_clamped_value<float> value(range, 0.0F);
|
||||
CHECK_THROW(value = nan, etl::exception);
|
||||
CHECK_THROW(value.advance(nan), etl::exception);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user