<
typenameT1,
typenameT2,
typenameT3,
typenameT4,
typenameT5>
class CKmerCountTemplate{
21 typedefvariant<vector<pair<T1,size_t>>, vector<pair<T2,size_t>>, vector<pair<T3,size_t>>, vector<pair<T4,size_t>>, vector<pair<T5,size_t>> >
Type;
29 returnapply_visitor(container_size(),
m_container);
37cerr <<
"Can't insert in not initialized container"<< endl;
44cerr <<
"Can't insert in not initialized container"<< endl;
59apply_visitor(extract_uniq(min_count),
m_container,
uniq.m_container);
67cerr <<
"Can't merge different kmers"<< endl;
100 throwruntime_error(
"Not supported kmer length");
107 typedef typenamepair_t::first_type large_t;
108 autoit = lower_bound(v.begin(), v.end(),
kmer.
get<large_t>(), [](
constpair_t& element,
constlarge_t& target){ return element.first < target; });
109 if(it == v.end() || it->first !=
kmer.
get<large_t>())
121 structcontainer_size :
publicstatic_visitor<size_t> {
template<
typenameT>
size_t operator()(
const T& v)
const{
returnv.size();} };
122 structcontainer_capacity :
publicstatic_visitor<size_t> {
template<
typenameT>
size_t operator()(
const T& v)
const{
returnv.capacity();} };
123 structelement_size :
publicstatic_visitor<size_t> {
template<
typenameT>
size_t operator()(
const T& v)
const{
return sizeof(
typename T::value_type);} };
124 structclear :
publicstatic_visitor<> {
template<
typenameT>
void operator()(
T& v)
const{ v.clear();} };
128 typedef typenameT::value_type::first_type large_t;
129v.push_back(make_pair(
kmer.
get<large_t>(),
count));
135 template<
typenameT>
void operator()(
T&
a,
const T&
b)
const{
a.insert(
a.end(),
b.begin(),
b.end()); }
136 template<
typenameT,
typenameU>
void operator()(
T&
a,
const U&
b)
const{ cerr <<
"Can't copy from different type container"<< endl;
exit(1); }
141merged.reserve(
a.size()+
b.size());
142merge(
a.begin(),
a.end(),
b.begin(),
b.end(), back_inserter(merged));
145 template<
typenameT,
typenameU>
void operator()(
T&
a,
const U&
b)
const{ cerr <<
"This is not happening!"<< endl;
exit(1); }
163 structcontainer_sort :
publicstatic_visitor<> {
template<
typenameT>
void operator()(
T& v)
const{
sort(v.begin(), v.end()); }};
166 template<
typenameT,
typenameU>
void operator()(
T&
a,
U&
b)
const{ cerr <<
"Can't swap different type containers"<< endl;
exit(1); }
168 struct uniq:
publicstatic_visitor<> {
171 typedef typenameT::iterator iter_t;
172iter_t nextp = v.begin();
173 for(iter_t
ip= v.begin();
ip!= v.end(); ) {
175 while(++
ip!= v.end() && workp->first ==
ip->first)
176workp->second +=
ip->second;
180v.erase(nextp, v.end());
188 if(
a.empty())
return;
191 for(
size_t i= 1;
i<
a.size(); ++
i) {
203 b.push_back(
a[0]);
204 for(
size_t i= 1;
i<
a.size(); ++
i) {
205 if(
b.back().first <
a[
i].first) {
208 b.push_back(
a[
i]);
210 b.back().second +=
a[
i].second;
216 template<
typenameT,
typenameU>
void operator()(
T&
a,
U&
b)
const{ cerr <<
"This is not happening!"<< endl;
exit(1); }
220 struct save:
publicstatic_visitor<> {
223 size_tnum = v.size();
224 os.write(
reinterpret_cast<const char*
>(&num),
sizeofnum);
226 os.write(
reinterpret_cast<const char*
>(&v[0]), num*
sizeof(v[0]));
230 struct load:
publicstatic_visitor<> {
234 is.read(
reinterpret_cast<char*
>(&num),
sizeofnum);
237 is.read(
reinterpret_cast<char*
>(&v[0]), num*
sizeof(v[0]));
248 template<
typenameT1,
typenameT2,
typenameT3,
typenameT4,
typenameT5,
typenameV>
class CKmerMapTemplate{
251 template<
typenameT>
252 size_t operator()(
const T& kmer)
const{
returnkmer.oahash(); }
254 typedefvariant<unordered_map<T1,V,kmer_hash>, unordered_map<T2,V,kmer_hash>, unordered_map<T3,V,kmer_hash>, unordered_map<T4,V,kmer_hash>, unordered_map<T5,V,kmer_hash>>
Type;
266cerr <<
"Can't insert in not initialized container"<< endl;
288 throwruntime_error(
"Not supported kmer length");
290 struct container_size:
publicstatic_visitor<size_t> {
template<
typenameT>
size_t operator()(
const T& v)
const{
returnv.size();} };
296 struct mapper:
publicstatic_visitor<V&> {
299 typedef typenameT::key_type large_t;
300 returnv[
kmer.
get<large_t>()];
304 struct find:
publicstatic_visitor<V*> {
307 typedef typenameT::key_type large_t;
308 typenameT::iterator it = v.find(
kmer.
get<large_t>());
310 return&(it->second);
320 template<
typenameV>
323 typedefvector<pair<int,size_t>>
TBins;
326 intpeak =
min(rlimit,(
int)bins.size()-SLOPE_LEN-1);
327 while(peak >= SLOPE_LEN) {
329 for(
int i= 1;
i<= SLOPE_LEN && maxim; ++
i)
330maxim = bins[peak+
i].second < bins[peak].second;
331 for(
int i= 1;
i<= SLOPE_LEN && maxim; ++
i)
332maxim = bins[peak-
i].second < bins[peak].second;
338 if(peak < SLOPE_LEN)
342 for(
int i= 1;
i<= peak; ++
i) {
343 if(bins[
i].second < bins[valley].second)
349 for(
int i= valley;
i< (
int)bins.size(); ++
i) {
350 if(bins[
i].second > bins[peak].second)
354 if(bins[valley].second < 0.7*bins[peak].second)
361 unsignedMIN_NUM = 100;
363 for(
auto& bin : bins) {
364 if(bin.second >= MIN_NUM)
365gsize += bin.first*bin.second;
370 for(
auto& bin : bins) {
371gs += bin.first*bin.second;
385 for(
int i=
max(0, v);
i<= rl; ++
i)
386 g+= bins[
i].second;
388 if((v >= 0 &&
g> genome) ||
g> 10*genome) {
400 returnmake_pair(valley, rlimit);
403 template<
typenameT>
447 in.read(
reinterpret_cast<char*
>(&bin_num),
sizeofbin_num);
448 for(
int i= 0;
i< bin_num; ++
i) {
449pair<int, size_t> bin;
450 in.read(
reinterpret_cast<char*
>(&bin),
sizeofbin);
465 return(index ==
GraphSize() ? 0 : 2*(index+1));
468 return(index ==
GraphSize() ? 0 : 2*(index+1)+1);
472 if(kmer_seq.find_first_not_of(
"ACGT") != string::npos || (
int)kmer_seq.size() !=
KmerLen())
474 TKmerkmer(kmer_seq);
488 return min(plusf,1-plusf);
527vector<Successor> successors;
532bitset<4> branches(node%2 ? (branch_info >> 4) : branch_info);
533 if(branches.count()) {
535 for(
intnt = 0; nt < 4; ++nt) {
548node = (node%2 == 0 ? node+1 : node-1);
563 return m_bins[
r.first].first;
580 for(
int i= (
int)read.size()-1;
i>= 0; --
i) {
584shift = (shift+2)%64;
597 for(
intnum = nextp/64; num > 0; --num)
621num +=
l-kmer_len+1;
627 size_t NXX(
doublexx)
const{
629 sort(read_length.begin(), read_length.end());
632 for(
intj = (
int)read_length.size()-1; j >= 0 &&
len< xx*
m_total_seq; --j) {
633nxx = read_length[j];
634 len+= read_length[j];
639 size_t N50()
const{
return NXX(0.5); }
698 for(
int i= 0;
i< read_length; ++
i) {
702reverse(read.begin(), read.end());
732 size_tread_len = is.
ReadLen();
740 size_tbit_to = bit_from+2*read_len;
747 template<
typenameDest>
748 void CopyBits(
size_tbit_from,
size_tbit_to, Dest& destination,
size_tdestination_bit_from,
size_tdestination_size)
const{
749 if(bit_to <= bit_from)
752 size_tword = bit_from/64;
753 size_tlast_word = (bit_to-1)/64;
754 unsignedshift = bit_from%64;
755 size_tdestination_word = destination_bit_from/64;
756 unsigneddestination_shift = destination_bit_from%64;
759 if(destination_shift > 0) {
760destination[destination_word] += (chunk << destination_shift);
761 if(shift <= destination_shift)
763 if(shift < destination_shift && destination_word < destination_size)
764destination[destination_word] += (chunk >> (64-destination_shift));
766destination[destination_word] = chunk;
768destination_shift = (destination_shift+64-shift)%64;
770 for( ; word <= last_word; ++word, ++destination_word) {
771 if(destination_shift > 0) {
772destination[destination_word] += (
m_storage[word] << destination_shift);
773 if(destination_word+1 < destination_size)
774destination[destination_word+1] += (
m_storage[word] >> (64-destination_shift));
776destination[destination_word] =
m_storage[word];
779 intpartial_bits = (destination_bit_from+bit_to-bit_from)%64;
780 if(partial_bits > 0) {
782destination[destination_size-1] &=
mask;
796 typedefdeque<CDBGraph::Successor>
TBases;
810 while(s.size() <
len) {
812 if(successors.empty())
815node = successors[0].m_node;
816s.push_back(successors[0].m_nt);
822 strings(1, base.
m_nt);
828 while(s.size() <
len) {
831 if(successors.size() != 1)
833node = successors[0].m_node;
834s.push_back(successors[0].m_nt);
843 if(successors.size() > 1) {
845 for(
auto& suc : successors) {
850successors.pop_back();
858 for(
intj = 0; target < 0 && j < (
int)successors.size(); ++j) {
862 if(target >= 0 &&
GoodNode(successors[target].m_node)) {
864 for(
intj = 0; j < (
int)successors.size(); ) {
866successors.erase(successors.begin()+j);
873 for(
intj = 0; target < 0 && j < (
int)successors.size(); ++j) {
877 if(target >= 0 &&
GoodNode(successors[target].m_node)) {
879 for(
intj = 0; j < (
int)successors.size(); ) {
881successors.erase(successors.begin()+j);
888 boolhas_both =
false;
889 for(
intj = 0; !has_both && j < (
int)successors.size(); ++j) {
891 doubleminusf = 1.- plusf;
892has_both =
GoodNode(successors[j].m_node) && (
min(plusf,minusf) > 0.25);
896 for(
intj = 0; j < (
int)successors.size(); ) {
898 doubleminusf = 1.- plusf;
899 if(
min(plusf,minusf) < fraction*
max(plusf,minusf))
900successors.erase(successors.begin()+j);
922deque<SElement> storage;
923 typedefunordered_map<CDBGraph::Node,SElement*> TElementMap;
924TElementMap current_elements;
928 for(
auto& suc : successors) {
929storage.push_back(
SElement(suc, 0));
930current_elements[suc.m_node] = &storage.back();
933list<SElement> connections;
934 for(
intstep = 1; step < steps && !current_elements.empty(); ++step) {
935TElementMap new_elements;
936 for(
auto& el : current_elements) {
939 if(el.second == 0) {
940 for(
auto& suc : successors) {
941new_elements[suc.m_node] = 0;
942 if(suc.m_node == last_node) {
948 for(
auto& suc : successors) {
949storage.push_back(
SElement(suc, el.second));
950 if(suc.m_node == last_node) {
951 if(!connections.empty()) {
955connections.push_back(storage.back());
958pair<TElementMap::iterator, bool> rslt = new_elements.insert(make_pair(suc.m_node, &storage.back()));
959 if(!rslt.second || !
GoodNode(suc.m_node))
960rslt.first->second = 0;
965 swap(current_elements, new_elements);
970 if(connections.empty())
975bases.first.push_front(el.
m_suc);
978bases.first.push_front(el.
m_suc);
985 typedefunordered_map<CDBGraph::Node, tuple<TSeqList::iterator, bool>>
TBranch;
989 for(
auto&
leaf: branch) {
992 if(successors.empty()) {
993sequences.erase(get<0>(
leaf.second));
996 for(
int i= successors.size()-1;
i>= 0; --
i) {
997TSeqList::iterator is = get<0>(
leaf.second);
999sequences.push_front(*is);
1000is = sequences.begin();
1002 TBases& bases = get<0>(*is);
1003 size_t& abundance = get<1>(*is);
1004bases.push_back(successors[
i]);
1008pair<TBranch::iterator, bool> rslt = new_branch.insert(make_pair(node, make_tuple(is, get<1>(
leaf.second))));
1010get<1>(rslt.first->second) =
true;
1011TSeqList::iterator& js = get<0>(rslt.first->second);
1012 if(abundance > get<1>(*js)) {
1013sequences.erase(js);
1016sequences.erase(is);
1022 swap(branch, new_branch);
1025 TBases JumpOver(vector<CDBGraph::Successor>& successors,
intmax_extent,
intmin_extent) {
1027 if(max_extent == 0)
1032 for(
auto& suc : successors) {
1034extensions[suc.m_node] = make_tuple(sequences.begin(),
false);
1037 while(!extensions.empty() && extensions.size() <
m_max_branch) {
1038TSeqList::iterator is = get<0>(extensions.begin()->second);
1039 int len= get<0>(*is).size();
1040 if(
len== max_extent)
1044 if(extensions.empty())
1047 if(extensions.size() == 1 &&
len+1 >= min_extent)
1051 if(extensions.size() == 1 && !get<1>(extensions.begin()->second)) {
1052TSeqList::iterator is = get<0>(extensions.begin()->second);
1053 TBases& bases = get<0>(*is);
1054 boolall_good =
true;
1055 for(
auto& base : bases) {
1072 intmax_extent =
m_jump;
1077 if(successors.empty())
1081 if(successors.size() > 1) {
1082step =
JumpOver(successors, max_extent, 0);
1084step.push_back(successors.front());
1089 boolall_good =
true;
1090 for(
auto& s : step) {
1099 intstep_size = step.size();
1104 if(predecessors.empty())
1108 if(predecessors.size() > 1 || step_size > 1)
1109step_back =
JumpOver(predecessors, max_extent, step_size);
1111step_back.push_back(predecessors.front());
1113 intstep_back_size = step_back.size();
1114 if(step_back_size < step_size)
1118 for(
int i= 0;
i<= step_size-2 && good; ++
i)
1123 intovershoot = step_back_size-step_size;
1127 if(overshoot > 0) {
1131 if(oversuc.empty())
1134 if(oversuc.size() > 1 || overshoot > 1)
1135step_over =
JumpOver(oversuc, max_extent, overshoot);
1137step_over.push_back(oversuc.front());
1139 if((
int)step_over.size() < overshoot)
1141 for(
int i= 0;
i< overshoot && good; ++
i)
1147 for(
auto& s : step) {
1149 returnmake_pair(extension, s.m_node);
1151extension.push_back(s);
1154node = extension.back().m_node;
1157 returnmake_pair(extension, 0);
1177 for(
const auto& base : to_left) {
1181 m_kmers.push_back(initial_node);
1182 stringikmer = graph.
GetNodeSeq(initial_node);
1183 m_seq.insert(
m_seq.end(), ikmer.begin(), ikmer.end());
1184 for(
const auto& base : to_right) {
1185 m_seq.push_back(base.m_nt);
1186 m_kmers.push_back(base.m_node);
1195 stringkmer = graph.
GetNodeSeq(takeoff_node);
1196 m_seq.insert(
m_seq.end(), kmer.begin()+1, kmer.end());
1197 for(
const auto& base : extension) {
1198 m_seq.push_back(base.m_nt);
1199 m_kmers.push_back(base.m_node);
1268 for(
size_t i= 0;
i<
m_kmers.size(); ++
i) {
1297 if(minkmer && minkmer%2)
1333pair<TBases, CDBGraph::Node> to_right =
ExtendToRightMT(initial_node);
1336 if(!to_left.second && !to_right.second && (
int)(to_left.first.size()+
m_graph.
KmerLen()+to_right.first.size()) < min_len) {
1338 for(
auto& base : to_right.first)
1340 for(
auto& base : to_left.first)
Entry point class for large integer usage.
std::array< const char, 4 > bin2NT
ncbi::TMaskedQueryRegions mask
string MostLikelySeq(CDBGraph::Successor base, unsigned len) const
tuple< TBases, size_t > TSequence
list< SContig > TContigList
tuple< TStrList::iterator, int > TContigEnd
string StringentExtension(CDBGraph::Node node, unsigned len) const
TBases JumpOver(vector< CDBGraph::Successor > &successors, int max_extent, int min_extent)
deque< CDBGraph::Successor > TBases
unordered_map< CDBGraph::Node, tuple< TSeqList::iterator, bool > > TBranch
SContig GetContigForKmerMT(CDBGraph::Node initial_node, int min_len)
pair< TBases, EConnectionStatus > ConnectTwoNodes(const CDBGraph::Node &first_node, const CDBGraph::Node &last_node, int steps) const
void OneStepBranchExtend(TBranch &branch, TSeqList &sequences)
bool GoodNode(const CDBGraph::Node &node) const
string MostLikelyExtension(CDBGraph::Node node, unsigned len) const
list< TSequence > TSeqList
pair< TBases, CDBGraph::Node > ExtendToRightMT(const CDBGraph::Node &initial_node)
void FilterNeighbors(vector< CDBGraph::Successor > &successors) const
CDBGraphDigger(CDBGraph &graph, double fraction, int jump, int low_count)
Node GetNode(const string &kmer_seq) const
bool GraphIsStranded() const
int Abundance(const Node &node) const
double MinFraction(const Node &node) const
bool ClearVisited(const Node &node)
CDBGraph(const TKmerCount &kmers, const TBins &bins, bool is_stranded)
Node GetNode(const TKmer &kmer) const
static Node ReverseComplement(Node node)
vector< Successor > GetNodeSuccessors(const Node &node) const
size_t ElementSize() const
string GetNodeSeq(const Node &node) const
vector< SAtomic< uint8_t > > m_visited
bool SetVisited(const Node &node, uint8_t value=1, uint8_t expected=0)
double PlusFraction(const Node &node) const
TKmer GetNodeKmer(const Node &node) const
int HistogramMinimum() const
uint8_t IsVisited(const Node &node) const
size_t Find(const TKmer &kmer) const
void PushBackElementsFrom(const CKmerCountTemplate &other)
variant< vector< pair< T1, size_t > >, vector< pair< T2, size_t > >, vector< pair< T3, size_t > >, vector< pair< T4, size_t > >, vector< pair< T5, size_t > > > Type
void Save(ostream &out) const
void Swap(CKmerCountTemplate &other)
void MergeTwoSorted(const CKmerCountTemplate &other)
void SortAndExtractUniq(int min_count, CKmerCountTemplate &uniq)
void Reserve(size_t rsrv)
size_t GetCount(size_t index) const
size_t ElementSize() const
CKmerCountTemplate(int kmer_len=0)
void SortAndUniq(int min_count)
pair< TKmer, size_t > GetKmerCount(size_t index) const
void PushBack(const TKmer &kmer, size_t count)
void UpdateCount(size_t count, size_t index)
V * Find(const TKmer &kmer)
V & operator[](const TKmer &kmer)
void Reserve(size_t rsrv)
variant< unordered_map< T1, V, kmer_hash >, unordered_map< T2, V, kmer_hash >, unordered_map< T3, V, kmer_hash >, unordered_map< T4, V, kmer_hash >, unordered_map< T5, V, kmer_hash > > Type
CKmerMapTemplate(int kmer_len=0)
kmer_iterator(int kmer_len, const CReadHolder &rholder, size_t position=0, size_t position_in_read=0, size_t read=0)
kmer_iterator & operator++()
friend bool operator!=(kmer_iterator const &li, kmer_iterator const &ri)
const CReadHolder & m_readholder
friend bool operator==(kmer_iterator const &li, kmer_iterator const &ri)
uint32_t m_position_in_read
friend bool operator==(string_iterator const &li, string_iterator const &ri)
friend bool operator!=(string_iterator const &li, string_iterator const &ri)
kmer_iterator KmersForRead(int kmer_len) const
string_iterator & operator++()
string_iterator(const CReadHolder &rholder, size_t position=0, size_t read=0)
const CReadHolder & m_readholder
kmer_iterator kbegin(int kmer_len) const
size_t KmerNum(unsigned kmer_len) const
void PushBack(const string &read)
kmer_iterator kend() const
deque< uint64_t > m_storage
void CopyBits(size_t bit_from, size_t bit_to, Dest &destination, size_t destination_bit_from, size_t destination_size) const
void Swap(CReadHolder &other)
void PushBack(const string_iterator &is)
deque< uint32_t > m_read_length
string_iterator sbegin() const
size_t NXX(double xx) const
string_iterator send() const
const U & get() const
Get the value of the IntegerTemplate object as a U type, U being one of the T1,T2,...
std::string toString(size_t sizeKmer) const
Get an ASCII string representation of a kmer encoded as a IntegerTemplate object.
void * getPointer()
Get pointer to the actual data - used for EMPHF.
std::ofstream out("events_result.xml")
main entry point for tests
static DLIST_TYPE *DLIST_NAME() first(DLIST_LIST_TYPE *list)
static const char * expected[]
TResidue Complement(TResidue c)
void ReverseComplement(const BidirectionalIterator &first, const BidirectionalIterator &last)
void swap(NCBI_NS_NCBI::pair_base_member< T1, T2 > &pair1, NCBI_NS_NCBI::pair_base_member< T1, T2 > &pair2)
unsigned int
A callback function used to compare two keys in a database.
IntegerTemplate< LargeInt< 1 >, LargeInt< 2 >, LargeInt< 4 >, LargeInt< 8 >, LargeInt< 16 > > TKmer
vector< pair< int, size_t > > TBins
pair< int, int > HistogramRange(const TBins &bins)
CKmerCountTemplate< LargeInt< 1 >, LargeInt< 2 >, LargeInt< 4 >, LargeInt< 8 >, LargeInt< 16 > > TKmerCount
LargeInt< precision > revcomp(const LargeInt< precision > &x, size_t sizeKmer)
int FindValleyAndPeak(const TBins &bins, int rlimit)
constexpr auto sort(_Init &&init)
constexpr auto rotate(list< Ts... >) -> decltype((list<>{}+...+rotate_item< Ts >{}))
double value_type
The numeric datatype used by the parser.
const GenericPointer< typename T::ValueType > T2 value
std::istream & in(std::istream &in_, double &x_)
double r(size_t dimension_, const Int4 *score_, const double *prob_, double theta_)
static SLJIT_INLINE sljit_ins l(sljit_gpr r, sljit_s32 d, sljit_gpr x, sljit_gpr b)
SContig(const string &contig, CDBGraph &graph)
size_t MinKmerPosition() const
SContig(SContig *link, int shift, CDBGraph::Node takeoff_node, const TBases &extension, CDBGraph::Node rnode, const CDBGraph &graph)
bool operator<(const SContig &other) const
SAtomic< uint8_t > m_is_taken
void AddToRight(const SContig &other)
deque< CDBGraph::Node > m_kmers
void RotateCircularToMinKmer()
SContig(const TBases &to_left, const TBases &to_right, CDBGraph::Node initial_node, CDBGraph::Node lnode, CDBGraph::Node rnode, const CDBGraph &graph)
CDBGraph::Node m_next_left
CDBGraph::Node m_next_right
void SelectMinDirection()
void AddToLeft(const SContig &other)
CDBGraph::Successor m_suc
SElement(CDBGraph::Successor suc, SElement *link)
Successor(const Node &node, char c)
void operator()(T &v) const
size_t operator()(const T &v) const
size_t operator()(const T &v) const
void operator()(T &v) const
size_t operator()(const T &v) const
size_t operator()(const T &v) const
find_kmer(const TKmer &k)
size_t operator()(T &v) const
pair< TKmer, size_t > operator()(T &v) const
void operator()(T &v) const
void operator()(T &a, const T &b) const
void operator()(T &a, const T &b) const
push_back(const TKmer &k, size_t c)
void operator()(T &v) const
void operator()(T &v) const
void operator()(T &v) const
void operator()(T &a, T &b) const
void operator()(T &v) const
void operator()(T &v) const
update_count(size_t c, size_t i)
size_t operator()(const T &v) const
V * operator()(T &v) const
size_t operator()(const T &kmer) const
V & operator()(T &v) const
void operator()(T &v) const
SAtomic(const SAtomic &other)
bool Set(T value, T expected=0)
SAtomic(const atomic< T > &a)
SAtomic & operator=(const SAtomic &other)
int g(Seg_Gsm *spe, Seq_Mtf *psm, Thd_Gsm *tdg)
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4