parent
e6fbe24a70
commit
0c661404c2
@ -0,0 +1,81 @@ |
|||||||
|
## Task |
||||||
|
|
||||||
|
(Task description is vaguely reproduced from memory.) |
||||||
|
|
||||||
|
Implement a hostname allocator supporting two commands, `A hosttype` and `D hostname`, such that: |
||||||
|
|
||||||
|
* `A hosttype` allocates and returns unique hostname of the form `hosttype123` with the smallest possible integer number (starting with 1) currently free for this host type; |
||||||
|
* `D hostname` (where `hostname` is always of the form `hosttype123`) frees this hostname (or does nothing if it wasn't allocated before), allowing to reuse it later. |
||||||
|
|
||||||
|
For example: |
||||||
|
|
||||||
|
``` |
||||||
|
A gateway |
||||||
|
A gateway |
||||||
|
A gateway |
||||||
|
A proxy |
||||||
|
A proxy |
||||||
|
D gateway2 |
||||||
|
D gateway5 |
||||||
|
A proxy |
||||||
|
A gateway |
||||||
|
A gateway |
||||||
|
A gateway |
||||||
|
``` |
||||||
|
should result in the following allocations made |
||||||
|
``` |
||||||
|
gateway1 |
||||||
|
gateway2 |
||||||
|
gateway3 |
||||||
|
proxy1 |
||||||
|
proxy2 |
||||||
|
proxy3 |
||||||
|
gateway2 |
||||||
|
gateway4 |
||||||
|
gateway5 |
||||||
|
``` |
||||||
|
|
||||||
|
No error handling is necessary; panicking if any of the input values is not in the expected format is acceptable. |
||||||
|
|
||||||
|
## Goals |
||||||
|
|
||||||
|
The solution should be reasonably fast for both types of commands; it should be implemented within 1-2 hours; readability and maintainability can suffer. |
||||||
|
|
||||||
|
## Algorithm |
||||||
|
|
||||||
|
Store hashmap of allocators by host types. |
||||||
|
|
||||||
|
For every host type, store a self-balancing tree (with access and modify operations taking `O(tree depth) = O(log(tree length))`) with non-adjacent ranges of reserved numbers; |
||||||
|
"non-adjacent" as in at least one unallocated number should separate any two ranges. |
||||||
|
This way, for every "free" operation, finding a matching range (if any) and updating the tree should take `O(log(number of ranges for host type))`; |
||||||
|
and for every "allocate" operation, only the first or the first two ranges need to be checked and updated, |
||||||
|
which might mean constant time in best case (depending on the tree implementation) and `O(log(number of ranges for host type))` in worst case |
||||||
|
(plus `O(log(number of host types))`). |
||||||
|
|
||||||
|
See [src/ranged_number_allocator.rs](src/ranged_number_allocator.rs) for more details. |
||||||
|
|
||||||
|
The asymptotic complexity above is theoretical; in practice, ideal trees don't actually work like that. |
||||||
|
In this implementation, Rust's [BTreeMap](https://doc.rust-lang.org/beta/std/collections/struct.BTreeMap.html) is used |
||||||
|
which is supposed to provide the same logarithmic asymptotic complexity in practice. |
||||||
|
|
||||||
|
Memory requirements are `O(log(number of ranges))` plus combined length of all host types. |
||||||
|
|
||||||
|
Constant factors were not considered in this implementation. |
||||||
|
|
||||||
|
## Usage |
||||||
|
|
||||||
|
Nightly Rust is needed (because `btree_cursors` unstable feature is used). |
||||||
|
This implementation can probably be rewritten from `btree_cursors` feature to [`range_mut`](https://doc.rust-lang.org/beta/std/collections/struct.BTreeMap.html#method.range_mut) method, |
||||||
|
but then it will be even more complex and even more difficult to reason about. |
||||||
|
|
||||||
|
Tests are in [`tests/`](tests) and can be run with: |
||||||
|
|
||||||
|
``` |
||||||
|
cargo test |
||||||
|
``` |
||||||
|
|
||||||
|
Sample input commands are in [`sample.in`](sample.in) and can be run with: |
||||||
|
|
||||||
|
``` |
||||||
|
cargo run < sample.in |
||||||
|
``` |
Loading…
Reference in new issue