Skip to content

Commit

Permalink
cpp hw
Browse files Browse the repository at this point in the history
  • Loading branch information
maxim092001 committed Mar 12, 2021
1 parent 8d0e46f commit 3f8589f
Show file tree
Hide file tree
Showing 77 changed files with 2,195 additions and 0 deletions.
1 change: 1 addition & 0 deletions cpp/8-puzzle
Submodule 8-puzzle added at fb27a7
49 changes: 49 additions & 0 deletions cpp/genome-assembly/.github/workflows/cpp_project.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
name: CPP project with GTest CI

on: [push]

jobs:
build:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v1
with:
submodules: recursive
- name: Prepare build dir
run: mkdir build
- name: Generate build files using cmake
run: cmake ..
working-directory: ./build
- name: Run make
run: make
working-directory: ./build
- name: Run tests
timeout-minutes: 15
run: ./test/runUnitTests
working-directory: ./build
- name: Prepare ASAN build dir
run: mkdir build_asan
- name: Generate ASAN build files using cmake
run: cmake .. -DCMAKE_BUILD_TYPE=ASAN
working-directory: ./build_asan
- name: Run make
run: make
working-directory: ./build_asan
- name: Run tests
timeout-minutes: 15
run: ./test/runUnitTests
working-directory: ./build_asan
- name: Prepare USAN build dir
run: mkdir build_usan
- name: Generate USAN build files using cmake
run: cmake .. -DCMAKE_BUILD_TYPE=USAN
working-directory: ./build_usan
- name: Run make
run: make
working-directory: ./build_usan
- name: Run tests
timeout-minutes: 15
run: ./test/runUnitTests
working-directory: ./build_usan
41 changes: 41 additions & 0 deletions cpp/genome-assembly/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Prerequisites
*.d

# Compiled Object files
*.slo
*.lo
*.o
*.obj

# Precompiled Headers
*.gch
*.pch

# Compiled Dynamic libraries
*.so
*.dylib
*.dll

# Fortran module files
*.mod
*.smod

# Compiled Static libraries
*.lai
*.la
*.a
*.lib

# Executables
*.exe
*.out
*.app

# ViM temporary files
.*.swp

CMakeCache.txt
CMakeFiles/*
CTestTestfile.cmake
Makefile
cmake_install.cmake
6 changes: 6 additions & 0 deletions cpp/genome-assembly/.gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[submodule "googletest"]
path = googletest
url = https://github.com/google/googletest.git
[submodule "test"]
path = test
url = https://github.com/itiviti-cpp-master/genome-assembly-test.git
44 changes: 44 additions & 0 deletions cpp/genome-assembly/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
cmake_minimum_required(VERSION 3.13)

include(test/Strict.cmake)

set(PROJECT_NAME genome_assembly)
project(${PROJECT_NAME})

# Set up the compiler flags
set(CMAKE_CXX_FLAGS "-g")
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# Inlcude directories
set(COMMON_INCLUDES ${PROJECT_SOURCE_DIR}/include)
include_directories(${COMMON_INCLUDES})

# Source files
file(GLOB SRC_FILES ${PROJECT_SOURCE_DIR}/src/*.cpp)

# Separate executable: main
list(REMOVE_ITEM SRC_FILES ${PROJECT_SOURCE_DIR}/src/main.cpp)

# Compile source files into a library
add_library(genome_assembly_lib ${SRC_FILES})
target_compile_options(genome_assembly_lib PUBLIC ${COMPILE_OPTS})
target_link_options(genome_assembly_lib PUBLIC ${LINK_OPTS})

# Main is separate
add_executable(genome-assembly ${PROJECT_SOURCE_DIR}/src/main.cpp)
target_compile_options(genome-assembly PRIVATE ${COMPILE_OPTS})
target_link_options(genome-assembly PRIVATE ${LINK_OPTS})

# linking Main against the library
target_link_libraries(genome-assembly genome_assembly_lib)

# google test is a git submodule
add_subdirectory(googletest)

enable_testing()

# test is a git submodule
add_subdirectory(test)

add_test(NAME tests COMMAND runUnitTests)
70 changes: 70 additions & 0 deletions cpp/genome-assembly/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# genome-assembly

## Теоретическое введение

Сборка генома - это вычислительный процесс, в котором определяется последовательность нуклеотидов составляющих геном организма. В настоящее время иследователи не могут прочитать последовательность нуклеотидов всей хромосомы. Вместо этого, они полагаются на высокотехнологичные химические методы, чтобы определить порядок нуклеобаз вдоль некой короткой части нити ДНК. Короткие фрагменты, полученные в результате, называются "reads".

Чтобы опеределить геном, исследователи проводят данную химическую операцию несколько раз над несколькими копиями одного и того же генома, каждый раз получая некоторую короткую последовательность какой-то его части. Чтобы "собрать геном", они должны использовать пересечения в полученных строчках, чтобы понять какие последовательности примыкают друг к другу. Например, получив две короткие последовательности GGTG**ACC** и **ACC**TCGA, они могут заключить, что они обе относятся к подстроке GGTGACCTCGA.

В общем случае неизвестно, каким образом пересекаются полученные фрагменты. К тому же, в ДНК две комплементарные нити и мы не знаем, с какой из них получен тот или иной фрагмент, и должны ли мы использовать его или его комплементарное отражение. Некоторую сложность привносит то, что современные методы чтения не идеальны и зачастую содержат ошибки. Также, некоторые регионы генома могут быть совсем не покрыты фрагментами. Однако, в нашей задаче мы возьмем упрощенный идеальный случай: мы будем считать, что наши фрагменты не содержат ошибок, получены с одной и той же нити ДНК, а также полностью покрывают геном. Причем мы точно знаем, размер пересекающихся частей фрагментов и то, что он одинаков для всех фрагментов.

## Задача

Разработать функцию, принимающую в качестве аргументов набор N строк одинаковой длины D в произвольном порядке, полученных в результате чтения, и натуральное число K (K < D) - длину пересекающихся концов фрагментов, возвращающую одну строку - геном, собранный из **всех** фрагментов. Гарантируется, что для всех входных данных такая строка существует и при том только одна.

```c++
std::string assemble(size_t k, const std::vector<std::string> & fragments);
```
Необходимо решить данную задачу, разбив входные фрагменты на все возможные подстроки размером (k + 1). Каждая такая подстрока будет соответсвовать переходу в направленном графе, где первой вершиной будет префикс размера k подстроки, а второй - суффикс размера k этой же подстроки.
Например, для фрагмента ATCG и k = 2, подстроками будут ATC и TCG. Из первой подстроки получим две вершины и ребро: (AT) -> (TC), из второй: (TC) -> (CG).
Повторив операцию для каждого исходного фрагмента, получим набор вершин и ребер. Склеив одинаковые вершины (но не склеивая ребра!), получим направленный граф, в котором присутсвует Эйлеров путь. Найдя этот путь, мы получим искомую строку - геном.
## Пример решения
Возьмем входные данные из первого примера ниже.
K=2, [AATCT, ACGAA, GCTAC]
1) Разобьем первый фрагмент на все возможные подстроки размером k + 1: AATCT -> AAT, ATC, TCT;
2) Проделаем то же самое с остальными фрагментами: ACGAA -> ACG, CGA, GAA; GCTAC -> GCT, GTA, TAC;
3) Каждая из полученных маленьких строк представляет собой переход в графе с вершинами из подстрок размером k. <br>
AAT: (AA) -> (AT);<br>
ATC: (AT) -> (TC);<br>
TCT: (TC) -> (CT);<br>
...<br>
TAC: (TA) -> (AC);<br>
4) Составим граф из получившихся переходов, склеив одинаковые вершины
![Graph](https://user-images.githubusercontent.com/9121511/79051884-589fbe00-7c3b-11ea-88f3-26764afed4e6.png)
5) Найдем путь Эйлера и используя его, составим искомую строку
GC -> CT -> TA -> AC -> CG -> GA -> AA -> AT -> TC -> CT
Для составления строки проведем операцию, обратную операции на шаге 3 для каждой пары последовательных вершин на пути.<br>
GC -> CT: GCT<br>
CT -> TA: CTA<br>
TA -> AC: TAC<br>
AC -> CG: ACG<br>
CG -> GA: CGA<br>
GA -> AA: GAA<br>
AA -> AT: AAT<br>
AT -> TC: ATC<br>
TC -> CT: TCT<br>
и наложим полученные фрагменты друг на друга в порядке, заданным путем:
```
GCT
CTA
TAC
ACG
CGA
GAA
AAT
ATC
TCT
GCTACGAATCT
```
Искомая строка: GCTACGAATCT
## Примеры
Входные данные|Результат
--------------|---------
K=2<br>AATCT<br>ACGAA<br>GCTAC | GCTACGAATCT
K=3<br>AGCGDTA<br>DTACCCC<br>DTACTGG<br>TGGADTA | AGCGDTACTGGADTACCCC
11 changes: 11 additions & 0 deletions cpp/genome-assembly/include/genome.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#pragma once

#include <string>
#include <vector>

namespace genome
{

std::string assembly(size_t k, const std::vector<std::string> &input);

}
43 changes: 43 additions & 0 deletions cpp/genome-assembly/include/graph.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#pragma once

#include <list>
#include <string_view>
#include <set>
#include <map>
#include <vector>

namespace genome {

// one possible starting point
//

using Node = std::string_view;

class Edge {
public:
Edge(Node from, Node to);

const auto &from() const { return m_from; }

const auto &to() const { return m_to; }

bool operator==(const Edge &other) const;

private:
Node m_from;
Node m_to;
size_t m_id;
static size_t id;
};

class Graph {
public:
void add_edge(Edge edge);

std::vector<Edge> find_euler_path();

private:
std::map<Node, std::multiset<Node>> g;
};

} // namespace genome
30 changes: 30 additions & 0 deletions cpp/genome-assembly/src/genome.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#include "genome.h"
#include "graph.h"

namespace genome {

std::string generate_string(const std::vector<Edge> & euler_path) {
std::string ans;
ans = euler_path[0].from();
for (const auto & i : euler_path) {
ans += i.to().back();
}
return ans;
}

std::string assembly(size_t k, const std::vector<std::string> & input) {
if (k == 0 || input.empty()) {
return "";
}
Graph g = Graph();

for (const auto &i : input) {
std::string_view view(i);
for (size_t j = 0; j + k < i.size(); j++) {
std::string_view temp = view.substr(j, k + 1);
g.add_edge(Edge(temp.substr(0, k), temp.substr(1, k)));
}
}
return generate_string(g.find_euler_path());
}
}
67 changes: 67 additions & 0 deletions cpp/genome-assembly/src/graph.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#include "graph.h"
#include <cmath>
#include <stack>
#include <vector>
#include <algorithm>

namespace genome {

size_t Edge::id = 0;

Edge::Edge(Node from, Node to)
: m_from(from), m_to(to), m_id(id++) {
}

bool Edge::operator==(const Edge &other) const {
return m_from == other.m_from && m_to == other.m_to && m_id == other.m_id;
}

void Graph::add_edge(Edge edge) {
g[edge.from()].insert(edge.to());
}

std::vector<Edge> Graph::find_euler_path() {
std::map<Node, int> incoming;
for (const auto & t : g) {
for (const auto & node : t.second) {
incoming[node]++;
}
}

Node start_node;

for (const auto & t : g) {
if (t.second.size() - incoming[t.first] == 1) {
start_node = t.first;
break;
}
}

std::stack<Node> stack;
stack.push(start_node);
std::vector<Node> nodes_on_path;

while (!stack.empty()) {
Node v = stack.top();
std::multiset<Node>& set = g[v];
if (!set.empty()) {
stack.push(*set.begin());
set.erase(set.begin());
} else {
nodes_on_path.emplace_back(v);
stack.pop();
}
}

std::reverse(nodes_on_path.begin(), nodes_on_path.end());

std::vector<Edge> edge_path;

for (size_t i = 0; i < nodes_on_path.size() - 1; ++i) {
edge_path.emplace_back(nodes_on_path[i], nodes_on_path[i + 1]);
}

return edge_path;
}

} // namespace genome
24 changes: 24 additions & 0 deletions cpp/genome-assembly/src/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#include "genome.h"

#include <iostream>
#include <string>
#include <vector>

int main()
{
const std::vector<std::string> reads = {"AATCT", "ACGAA", "GCTAC"};
const std::size_t k = 2;
std::cout << "K=" << k << ", [";
bool first = true;
for (const auto & r : reads) {
if (!first) {
std::cout << ", ";
}
else {
first = false;
}
std::cout << r;
}
std::cout << "]\n" << genome::assembly(k, reads) << std::endl;
return 0;
}
Loading

0 comments on commit 3f8589f

Please sign in to comment.