**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).
{
            UInt64 range = total + static_cast<UInt64>(count_end * end_multiplier);
            if (range == 0)
                return END;
            random %= range;
            UInt64 sum = 0;
            for (const auto & elem : buckets)
            {
                sum += elem.second;
                if (sum > random)
                    return elem.first;
            }
            return END;
        }
    };
    using Table = HashMap<NGramHash, Histogram, TrivialHash>;
    Table table;
    MarkovModelParameters params;
    std::vector<CodePoint> code_points;
    
    static constexpr CodePoint BEGIN = -1;
    
    static constexpr CodePoint END = -2;
    static NGramHash hashContext(const CodePoint * begin, const CodePoint * end)
    {
        return CRC32Hash()(StringRef(reinterpret_cast<const char *>(begin), (end - begin) * sizeof(CodePoint)));
    }
    
    static CodePoint readCodePoint(const char *& pos, const char * end)