Skip to content
This repository was archived by the owner on Jun 29, 2026. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 27 additions & 9 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,31 @@ on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest

steps:
- name: Install gtest manually
run: sudo apt-get install libgtest-dev && cd /usr/src/gtest && sudo cmake CMakeLists.txt && sudo make && ls -l lib && sudo cp lib/*.a /usr/lib && sudo ln -s /usr/lib/libgtest.a /usr/local/lib/libgtest.a && sudo ln -s /usr/lib/libgtest_main.a /usr/local/lib/libgtest_main.a
- uses: actions/checkout@v1
- name: configure
run: mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-Werror" ..
- name: make
run: cd build && make
- name: Run Test
run: /home/runner/work/mod-lab06-markov/mod-lab06-markov/build/test/mod-lab06-markov.test
- name: Install gtest manually
run: |
sudo apt-get update
sudo apt-get install -y libgtest-dev
cd /usr/src/gtest
sudo cmake CMakeLists.txt
sudo make
sudo cp lib/*.a /usr/lib

- uses: actions/checkout@v3

- name: Configure
run: |
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_CXX_FLAGS="-Werror" ..

- name: Build
run: |
cd build
make

- name: Run Test
run: |
/home/runner/work/mod-lab06-markov/mod-lab06-markov/build/mod-lab06-markov.test
29 changes: 21 additions & 8 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,13 +1,26 @@
cmake_minimum_required (VERSION 3.10.2)
project (mod-lab06-markov)
cmake_minimum_required(VERSION 3.10)
project(MarkovTextGenerator)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3 -g")

include_directories(include)

set(tool_dest "bin")
set(lib_dest "lib")
set(include_dest "include/")
add_executable(markov_gen
src/main.cpp
src/textgen.cpp
)

add_subdirectory(src)
add_subdirectory(test)
enable_testing()
find_package(GTest REQUIRED)

add_executable(mod-lab06-markov.test
test/tests.cpp
src/textgen.cpp
)

target_link_libraries(mod-lab06-markov.test
GTest::GTest
GTest::Main
)

add_test(NAME MarkovTests COMMAND mod-lab06-markov.test)
35 changes: 35 additions & 0 deletions include/textgen.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright 2026 Anastasia Astafyeva

#ifndef INCLUDE_TEXTGEN_H_
#define INCLUDE_TEXTGEN_H_

#include <deque>
#include <map>
#include <random>
#include <string>
#include <vector>

class MarkovTextGenerator {
private:
typedef std::deque<std::string> Prefix;
typedef std::map<Prefix, std::vector<std::string> > StateTable;

StateTable statetab;
int npref;
std::mt19937 rng;

public:
explicit MarkovTextGenerator(int prefixSize = 2);

void buildFromFile(const std::string& filename);
void buildFromText(const std::string& text);

std::string generate(int maxWords);

StateTable& getStateTable();
void clear();
void addSuffix(const Prefix& prefix, const std::string& suffix);
std::string getRandomSuffix(const Prefix& prefix);
};

#endif // INCLUDE_TEXTGEN_H_
1 change: 1 addition & 0 deletions input.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Жил старик со своею старухой у самого синего моря Они жили в ветхой землянке ровно тридцать лет и три года Старик ловил неводом рыбу старуха пряла свою пряжу Раз он в море синее закинул невод пришел невод с одною тиной Он в другой раз закинул невод пришел невод с травой морскою В третий раз закинул он невод пришел невод с одною рыбкой с непростою золотою Как взмолится золотая рыбка голосом молвит человечьим Отпусти ты старче меня в море дорогое дам тебе откуп чем только пожелаешь
1 change: 1 addition & 0 deletions result/gen.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Жил старик своею старухой пряла свою пряжу Раз он море синее закинул невод пришел невод с травой морскою третий раз закинул невод пришел невод с одною рыбкой с золотою Как взмолится золотая рыбка голосом молвит человечьим Отпусти ты старче меня море дорогое дам тебе откуп чем только пожелаешь старик ловил неводом рыбу старуха пряла свою пряжу Раз он море синее закинул невод пришел невод с одною тиной другой раз закинул он невод пришел невод с травой морскою В третий раз закинул невод пришел невод с одною рыбкой с золотою Как взмолится золотая рыбка голосом молвит человечьим Отпусти ты старче меня море дорогое дам тебе откуп чем только пожелаешь
48 changes: 48 additions & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright 2026 Anastasia Astafyeva

#include <algorithm>
#include <fstream>
#include <iostream>
#include <string>

#include "textgen.h"

int main(int argc, char* argv[]) {
try {
std::string inputFile = "input.txt";
std::string outputFile = "result/gen.txt";
int prefixSize = 2;
int maxWords = 1000;

if (argc > 1) {
inputFile = argv[1];
}

if (argc > 2) {
maxWords = std::stoi(argv[2]);
}

MarkovTextGenerator generator(prefixSize);

std::cout << "Building model from " << inputFile << "..." << std::endl;
generator.buildFromFile(inputFile);

std::cout << "Generating " << maxWords << " words..." << std::endl;
std::string generated = generator.generate(maxWords);

std::ofstream out(outputFile);
out << generated;
out.close();

std::cout << "Generated text saved to " << outputFile << std::endl;
std::cout << "\n--- Preview ---\n";

size_t previewSize = std::min<size_t>(500, generated.size());
std::cout << generated.substr(0, previewSize) << "...\n";
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
return 1;
}

return 0;
}
140 changes: 140 additions & 0 deletions src/textgen.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
// Copyright 2026 Anastasia Astafyeva

#include "textgen.h"

#include <algorithm>
#include <cctype>
#include <fstream>
#include <random>
#include <sstream>
#include <stdexcept>
#include <string>
#include <vector>

MarkovTextGenerator::MarkovTextGenerator(int prefixSize)
: npref(prefixSize), rng(std::random_device()()) {
}

void MarkovTextGenerator::buildFromFile(const std::string& filename) {
std::ifstream file(filename);

if (!file.is_open()) {
throw std::runtime_error("Cannot open file: " + filename);
}

std::stringstream buffer;
buffer << file.rdbuf();
buildFromText(buffer.str());
}

void MarkovTextGenerator::buildFromText(const std::string& text) {
std::istringstream iss(text);
std::vector<std::string> words;
std::string word;

while (iss >> word) {
word.erase(
std::remove_if(
word.begin(),
word.end(),
[](char c) {
return std::ispunct(static_cast<unsigned char>(c));
}),
word.end());

if (!word.empty()) {
words.push_back(word);
}
}

if (words.size() < static_cast<size_t>(npref + 1)) {
throw std::runtime_error("Text too short for building model");
}

Prefix prefix;

for (int i = 0; i < npref; ++i) {
prefix.push_back(words[i]);
}

for (size_t i = npref; i < words.size(); ++i) {
statetab[prefix].push_back(words[i]);
prefix.pop_front();
prefix.push_back(words[i]);
}
}

void MarkovTextGenerator::addSuffix(
const Prefix& prefix,
const std::string& suffix) {
statetab[prefix].push_back(suffix);
}

std::string MarkovTextGenerator::getRandomSuffix(
const Prefix& prefix) {
StateTable::iterator it = statetab.find(prefix);

if (it == statetab.end() || it->second.empty()) {
return "";
}

std::uniform_int_distribution<size_t> dist(
0,
it->second.size() - 1);

return it->second[dist(rng)];
}

std::string MarkovTextGenerator::generate(int maxWords) {
if (statetab.empty()) {
throw std::runtime_error("No data in state table");
}

StateTable::iterator it = statetab.begin();

std::uniform_int_distribution<size_t> dist(
0,
statetab.size() - 1);

std::advance(it, dist(rng));

Prefix prefix = it->first;

std::vector<std::string> result(
prefix.begin(),
prefix.end());

for (int i = npref; i < maxWords; ++i) {
std::string next = getRandomSuffix(prefix);

if (next.empty()) {
break;
}

result.push_back(next);

prefix.pop_front();
prefix.push_back(next);
}

std::stringstream ss;

for (size_t i = 0; i < result.size(); ++i) {
if (i > 0) {
ss << " ";
}

ss << result[i];
}

return ss.str();
}

MarkovTextGenerator::StateTable&
MarkovTextGenerator::getStateTable() {
return statetab;
}

void MarkovTextGenerator::clear() {
statetab.clear();
}
Loading