**Use case**

Generate models once and reuse them.

**Describe the solution you'd like**

Add command line options:
```
--save file - write serialized models on disk after training and before generating the data; the user can set --limit to 0 to only train the models and save them without proceeding to data generation;
--load file - instead of training the models, load the prepared models from the file and start generating the data.
```

Serialization format: one byte for version (equals to 0), then simple binary serialization of all the models; the whole file is compressed with ZSTD level 1 using CompressedWriteBuffer (no need to allow any compression options to choose).
{
    size_t num_bits_left_half = num_bits / 2;
    size_t num_bits_right_half = num_bits - num_bits_left_half;
    UInt64 left_half = maskBits(x >> num_bits_right_half, num_bits_left_half);
    UInt64 right_half = maskBits(x, num_bits_right_half);
    UInt64 new_left_half = right_half;
    UInt64 new_right_half = left_half ^ maskBits(hash(right_half, seed, round), num_bits_left_half);
    return (new_left_half << num_bits_left_half) ^ new_right_half;
}
static UInt64 feistelNetwork(UInt64 x, size_t num_bits, UInt64 seed, size_t num_rounds = 4)
{
    UInt64 bits = maskBits(x, num_bits);
    for (size_t i = 0; i < num_rounds; ++i)
        bits = feistelRound(bits, num_bits, seed, i);
    return (x & ~((1ULL << num_bits) - 1)) ^ bits;
}
static UInt64 transform(UInt64 x, UInt64 seed)
{
    
    if (x == 0 || x == 1)
        return x;
    
    if (x == 2 || x == 3)
        return x ^ (seed & 1);
    size_t num_leading_zeros = __builtin_clzll(x);
    return feistelNetwork(x, 64 - num_leading_zeros - 1, seed);
}
class UnsignedIntegerModel : public IModel