local instructions

This commit is contained in:
vrd
2023-11-02 21:25:56 +02:00
parent 6a71d7e13c
commit bc5e5d1deb
11 changed files with 210 additions and 166 deletions
+62
View File
@@ -0,0 +1,62 @@
tft - Terminal Family Tree
==========================
tft is an ncurses program that can create and display family trees on the terminal.
Webpage
-------
https://vene.volconst.com/en/tft
Dependecies
-----------
ncursesw shared library and headers (libncursesw5-dev on Debian/Ubuntu)
Installation
------------
Make sure you satisfy the dependencies and execute:
git clone https://gitlab.com/venelin98/tft.git
cd tft
sudo ./install.sh
Creating trees
--------------
Trees are created in plain text files following the tft file format.
Spaces are not important but new lines are considered as separators.
Adding a member
---------------
A new line begining with a name is considered a member definition, the name can include spaces.
After the name the sex can be specifed with a comma followed by either M or F, if not given male is assumed.
For example:
Adam, M
Declares member Adam that is male.
Adding member information
-------------------------
In the lines following a member declaration, various info can be added:
s: - Spouses, must be followed by comma separated names of already declared members, "-" can be given instead in which case the last declared person of the opposite sex is used
p: - Parents, must be followed by 1 or 2 names separated by a comma, "-" can be given instead in which case the previous parents are used
b: - Birthday in the format yyyy.mm.dd, day and month can be ommited
d: - Death date
Duplicate names
---------------
A number after a name is used to disambiguate when multiple people have identical names e.g.:
"Adam 0" is the first added Adam, "Adam 1" is the second
Viewing trees
-------------
After creating a tree file you can view it using tft:
tft my_tree.tft
Once the tree is displayed you can navigate it using:
arrows - move the camera
h, j, k, l - move around the tree
Enter, Space - display the selected person's tree
+ - show full names
- - show first names only
w - close the current tree
УПЪТВАНЕ НА БЪЛГАРСКИ:
https://vene.volconst.com/bg/tft
+1 -1
View File
@@ -32,4 +32,4 @@ g++ -o tft $language $optimizations $warnings $other -DNDEBUG -pipe -s *.cpp -ln
#-static -lncursesw -ltinfo
chmod 775 tft
exec mv tft /usr/local/bin
mv tft /usr/local/bin
-5
View File
@@ -1,5 +0,0 @@
For information visit:
https://vene.volconst.com/en/tft
Упътване на български:
https://vene.volconst.com/bg/tft
-1
View File
@@ -10,7 +10,6 @@ using std::string_view;
using std::string;
using std::vector;
using std::pair;
/* using std::endl; */
namespace /* internal */
{
+2 -4
View File
@@ -60,7 +60,7 @@ bool Person::operator==(const Person &other)const
void Person::displayInfo()const
{
WINDOW* info_tab = newwin(LINES, COLS, 0, 0);
AutoWin info_tab( newwin(LINES, COLS, 0, 0) );
waddstr(info_tab, name.data());
waddch(info_tab,'\n');
@@ -78,7 +78,6 @@ void Person::displayInfo()const
werase(info_tab);
wrefresh(info_tab); /* todo: dont update */
delwin(info_tab);
}
@@ -118,7 +117,7 @@ U16 Person::draw(const I16 drawY, const I16 drawX, bool selected, bool hasSpouse
if(willBeVisible(drawY, drawX, BOX_HEIGHT, width))
{
WINDOW* pad = newpad(BOX_HEIGHT, width);
AutoWin pad( newpad(BOX_HEIGHT, width) );
auto color_pair = COLOR_PAIR( selected ? COLOR_SELECTED
: (sex==M ? COLOR_MALE : COLOR_FEMALE) );
@@ -138,7 +137,6 @@ U16 Person::draw(const I16 drawY, const I16 drawX, bool selected, bool hasSpouse
/* wattroff(pad, color_pair); */
drawpad(pad, drawY, drawX);
delwin(pad);
}
return width;
}
+4 -18
View File
@@ -2,49 +2,35 @@
#include "utils.hpp"
#include "parser.hpp"
#include <ncursesw/ncurses.h>
/* #include <string_view> */
#include <iostream>
#include <string.h>
/* using std::string_view; */
using std::cout;
/* using namespace std::literals; */
int main(int argc, char* argv[])
{
if(argc < 2)
{
cout << "Usage: tft FILES...\n";
return 0;
return 1;
}
if(!strcmp(argv[1], "--help"))
{
cout << "Usage: tft FILES...\n"
<< "Display the combined family tree in the FILES\n";
<< "Display the combined family tree from the FILES\n\n"
<< VISUALIZATION_HELP_STR;
return 0;
}
Tree tree( parseTftFile(argv[1]) );
/* bool negative = false; */
for(int i = 2; i < argc; ++i)
{
tree += parseTftFile(argv[i]);
/* if( !strcmp(argv[i], "-") ) */
/* { */
/* negative = true; */
/* } */
/* else */
/* { */
/* if(negative) */
/* tree -= parseTftFile(argv[i]); */
/* else */
/* tree += parseTftFile(argv[i]); */
/* } */
}
init();
init_ncurses();
tree.display();
return 0;
+81 -86
View File
@@ -18,15 +18,15 @@ Tree Tree::operator+ (const Tree& other) const
Tree& Tree::operator+=(const Tree& other)
{
vector<person_id> newId; //The indexes people from II tree will have in I tree
newId.reserve(other.people.size());
newId.reserve(other.people_.size());
U32 initNum = people.size(); /* number of people at the start */
U32 initNum = people_.size(); /* number of people at the start */
U32 idCounter = initNum;
for (person_id i = 0, j; i < other.people.size(); ++i) //find the new ids for people in II, people present in both trees get the ids from I
for (person_id i = 0, j; i < other.people_.size(); ++i) //find the new ids for people in II, people present in both trees get the ids from I
{
for (j = 0; j < initNum; ++j)
{
if (other.people[i] == people[j])
if (other.people_[i] == people_[j])
{
newId[i] = j;
break;
@@ -36,31 +36,31 @@ Tree& Tree::operator+=(const Tree& other)
newId[i] = idCounter++;
}
people.reserve(idCounter);
for (person_id i = 0; i < other.people.size(); ++i) /* add the personal data of II to I */
people_.reserve(idCounter);
for (person_id i = 0; i < other.people_.size(); ++i) /* add the personal data of II to I */
{
if (newId[i] >= initNum) /* isnt already in I */
people.push_back(other.people[i]);
people_.push_back(other.people_[i]);
}
relations.reserve(idCounter);
for (person_id i = 0; i < other.people.size(); ++i) //merge the relative data
relations_.reserve(idCounter);
for (person_id i = 0; i < other.people_.size(); ++i) //merge the relative data
{
if (newId[i] >= initNum) /* isnt already in I */
{
relations.push_back(other.relations[i]);
for (Relation& rel: relations.back())
relations_.push_back(other.relations_[i]);
for (Relation& rel: relations_.back())
rel.id = newId[rel.id];
}
else //if present in both trees
{
person_id idIn1 = newId[i];
relations[idIn1].reserve(relations[idIn1].size() + other.relations[i].size());
relations_[idIn1].reserve(relations_[idIn1].size() + other.relations_[i].size());
for (Relation rel: other.relations[i])
for (Relation rel: other.relations_[i])
{
if(newId[rel.id] >= initNum) /* not already in I */
relations[idIn1].push_back( {newId[rel.id], rel.type} );
relations_[idIn1].push_back( {newId[rel.id], rel.type} );
}
}
@@ -71,11 +71,11 @@ Tree& Tree::operator+=(const Tree& other)
Tree& Tree::operator-= (const Tree& other)
{
for (U32 i = 0; i < people.size(); ++i)
for (U32 i = 0; i < people_.size(); ++i)
{
for (const Person& othPers: other.people)
for (const Person& othPers: other.people_)
{
if (people[i] == othPers)
if (people_[i] == othPers)
{
removePerson(i);
--i;
@@ -89,20 +89,20 @@ Tree& Tree::operator-= (const Tree& other)
void Tree::rename(const char* newName)
{
treeName = newName;
treeName_ = newName;
}
person_id Tree::last()
{
return people.size() - 1;
return people_.size() - 1;
}
vector<person_id> Tree::findId(const string& name) const
{
vector<person_id> res;
for (person_id i = 0; i < people.size(); ++i)
for (person_id i = 0; i < people_.size(); ++i)
{
if (name == people[i].name)
if (name == people_[i].name)
res.push_back(i);
}
return res;
@@ -111,9 +111,9 @@ vector<person_id> Tree::findId(const string& name) const
/* person_id Tree::findId(const string& name, Sex s) const */
/* { */
/* Person sought(name, {}, s); */
/* for (person_id i = 0; i < people.size(); ++i) */
/* for (person_id i = 0; i < people_.size(); ++i) */
/* { */
/* if (sought == people[i]) */
/* if (sought == people_[i]) */
/* return i; */
/* } */
/* return Nobody; */
@@ -135,9 +135,9 @@ person_id Tree::findRootAncestor(person_id id) const
person_id Tree::findOldestAncestor(person_id id, Sex sex) const
{
for (const Relation& rel: relations[id])
for (const Relation& rel: relations_[id])
{
if (rel.type == Child && people[ rel.id ].sex == sex)
if (rel.type == Child && people_[ rel.id ].sex == sex)
return findOldestAncestor(rel.id, sex);
}
return id;
@@ -148,18 +148,18 @@ person_id Tree::findCommonAncestor(const unsigned first, const unsigned second)
if(first == Nobody || second == Nobody)
return Nobody;
vector<U8> visited(people.size()); //if a member is visited twice he's a common ancestor
vector<U8> visited(people_.size()); //if a member is visited twice he's a common ancestor
commonAncestorRec(first, visited);
commonAncestorRec(second, visited);
person_id oldest = Nobody;
U16 oldestBday = SHRT_MAX;
for (person_id i = 0; i < people.size(); ++i)
for (person_id i = 0; i < people_.size(); ++i)
{
if (visited[i] == 2 && people[i].birth.year < oldestBday)
if (visited[i] == 2 && people_[i].birth.year < oldestBday)
{
/* oldestBday = people[i].birth.year; */ //todo figure it out
/* oldestBday = people_[i].birth.year; */ //todo figure it out
/* oldest = i; */
return i;
}
@@ -170,9 +170,9 @@ person_id Tree::findCommonAncestor(const unsigned first, const unsigned second)
person_id Tree::findParent(person_id child, Sex parentSex) const
{
person_id parent = Nobody;
for(const Relation& rel: relations[child])
for(const Relation& rel: relations_[child])
{
if (rel.type == Child && people[rel.id].sex == parentSex)
if (rel.type == Child && people_[rel.id].sex == parentSex)
parent = rel.id;
}
return parent;
@@ -184,7 +184,7 @@ vector<person_id> Tree::findChildren(person_id person) const
return {};
vector<person_id> children;
for(const Relation& rel: relations[person])
for(const Relation& rel: relations_[person])
{
if (rel.type == Parent)
children.push_back(rel.id);
@@ -206,7 +206,7 @@ vector<person_id> Tree::findChildren(person_id person, person_id exclude) const
person_id Tree::findSpouse(person_id person) const
{
person_id spouse = Nobody;
for(const Relation& rel: relations[person])
for(const Relation& rel: relations_[person])
{
if (rel.type == Spouse)
{
@@ -220,7 +220,7 @@ person_id Tree::findSpouse(person_id person) const
RelType Tree::findRelation(person_id first, person_id second) const
{
const auto& curRels = relations[first];
const auto& curRels = relations_[first];
for(const Relation& rel: curRels)
{
if (rel.id == second)
@@ -231,11 +231,11 @@ RelType Tree::findRelation(person_id first, person_id second) const
void Tree::addPerson(const char* name, Sex sex, unsigned father, unsigned mother, EventTime birth, EventTime death)
{
people.emplace_back(name, birth, sex, death);
people_.emplace_back(name, birth, sex, death);
relations.emplace_back(); /* relations of the new person */
relations_.emplace_back(); /* relations of the new person */
person_id new_person_id = people.size() - 1;
person_id new_person_id = people_.size() - 1;
addRelation(father, Parent, new_person_id);
addRelation(mother, Parent, new_person_id);
@@ -250,7 +250,7 @@ void Tree::addPerson(const char* name, Sex sex, unsigned father, unsigned mother
bool Tree::addRelation(const unsigned first, const RelType type, const unsigned second, bool update)
{
if (first == Nobody || first >= people.size() || second == Nobody || second >= people.size() || type == None)
if (first == Nobody || first >= people_.size() || second == Nobody || second >= people_.size() || type == None)
return false;
switch (type)
@@ -284,17 +284,17 @@ bool Tree::addRelation(const unsigned first, const RelType type, const unsigned
void Tree::addRelOneSide(person_id first, RelType type, person_id second)
{
unsigned i = 0;
for (; i < relations[first].size(); ++i) //Check if they are already relatives
for (; i < relations_[first].size(); ++i) //Check if they are already relatives
{
if (relations[first][i].id == second)
if (relations_[first][i].id == second)
{
relations[first][i].type = type;
relations_[first][i].type = type;
break;
}
}
if (i >= relations[first].size())
if (i >= relations_[first].size())
{
relations[first].push_back({second, type});
relations_[first].push_back({second, type});
}
}
@@ -306,32 +306,32 @@ void Tree::addRelOneSide(person_id first, RelType type, person_id second)
void Tree::removePerson(const person_id id)
{
person_id relId;
for (Relation rel: relations[id])
for (Relation rel: relations_[id])
{
relId = rel.id;
for (U32 j = 0; j < relations[relId].size(); ++j)
for (U32 j = 0; j < relations_[relId].size(); ++j)
{
if (relations[relId][j].id == id)
if (relations_[relId][j].id == id)
{
remove(relations[relId], j);
remove(relations_[relId], j);
break;
}
}
}
for (unsigned i = 0; i < relations[people.size() - 2].size(); ++i) //Move the last in his place and update IDs
for (unsigned i = 0; i < relations_[people_.size() - 2].size(); ++i) //Move the last in his place and update IDs
{
relId = relations[people.size() - 1][i].id;
for (unsigned j = 0; j < relations[relId].size(); ++j)
relId = relations_[people_.size() - 1][i].id;
for (unsigned j = 0; j < relations_[relId].size(); ++j)
{
if (relations[relId][j].id == people.size() - 1)
if (relations_[relId][j].id == people_.size() - 1)
{
relations[relId][j].id = id;
relations_[relId][j].id = id;
break;
}
}
}
remove(relations, id);
remove(people, id);
remove(relations_, id);
remove(people_, id);
}
/* void Tree::removePerson(const char* personName, short year, unsigned char month, unsigned char day) */
@@ -341,19 +341,19 @@ void Tree::removePerson(const person_id id)
void Tree::removeRelation(const unsigned first, const unsigned second)
{
for (unsigned i = 0; i < relations[first].size(); ++i)
for (unsigned i = 0; i < relations_[first].size(); ++i)
{
if (relations[first][i].id == second)
if (relations_[first][i].id == second)
{
remove(relations[first], i);
remove(relations_[first], i);
break;
}
}
for (unsigned i = 0; i < relations[second].size(); ++i)
for (unsigned i = 0; i < relations_[second].size(); ++i)
{
if (relations[second][i].id == first)
if (relations_[second][i].id == first)
{
remove(relations[second], i);
remove(relations_[second], i);
break;
}
}
@@ -361,12 +361,9 @@ void Tree::removeRelation(const unsigned first, const unsigned second)
static void displayHelp()
{
WINDOW* help_tab = newwin(LINES, COLS, 0, 0);
AutoWin help_tab( newwin(LINES, COLS, 0, 0) );
waddstr(help_tab, "Enter - focus the currently selected person\n" /* todo */
"Arrow keys - move the camera\n"
"H, J, K, L - move the selection\n"
"I - display info about the selected person\n");
waddstr(help_tab, VISUALIZATION_HELP_STR);
mvwaddstr(help_tab, LINES-1, 0, "Press any key to return.");
@@ -375,12 +372,11 @@ static void displayHelp()
werase(help_tab);
wnoutrefresh(help_tab);
delwin(help_tab);
}
void Tree::display()
{
if(people.size() == 0)
if(people_.size() == 0)
return;
bool to_draw = true;
@@ -436,7 +432,7 @@ void Tree::display()
selectRight();
break;
case DISPLAY_INFO:
people[selected_].displayInfo();
people_[selected_].displayInfo();
break;
default:
to_draw = false; /* invalid key, don't redraw */
@@ -492,19 +488,19 @@ void Tree::draw() const
void Tree::print() const
{
for(const Person& member: people)
for(const Person& member: people_)
member.displayInfo();
}
void Tree::printMember(const unsigned id)const
{
people[id].displayInfo();
people_[id].displayInfo();
}
void Tree::printOldestAncestors(const unsigned id)const
{
people[findOldestAncestor(id, M)].displayInfo();
people[findOldestAncestor(id, F)].displayInfo();
people_[findOldestAncestor(id, M)].displayInfo();
people_[findOldestAncestor(id, F)].displayInfo();
}
@@ -512,7 +508,7 @@ void Tree::commonAncestorRec(person_id id, vector<U8> &visited) const
{
++visited[id];
auto& curRels = relations[id];
auto& curRels = relations_[id];
for (const Relation& rel: curRels)
{
if (rel.type == Child)
@@ -520,11 +516,12 @@ void Tree::commonAncestorRec(person_id id, vector<U8> &visited) const
}
}
// return the height of the space occupied by the link
static U8 linkToFirstBorn(I16 startY, I16 startX, bool moreSiblings)
{
if (willBeVisible(startY, startX, 3, 1))
{
WINDOW* pad = newpad(3, 1);
AutoWin pad( newpad(3, 1) );
waddch(pad, ACS_VLINE);
mvwaddch(pad, 1, 0,
@@ -532,7 +529,6 @@ static U8 linkToFirstBorn(I16 startY, I16 startX, bool moreSiblings)
mvwaddch(pad, 2, 0, ACS_VLINE);
drawpad(pad, startY, startX);
delwin(pad);
}
return 3;
}
@@ -543,7 +539,7 @@ static U8 linkToChild(I16 startY, I16 startX, I16 endX, bool moreSiblings)
const I16 len = endX - startX;
if (willBeVisible(startY, startX, 2, len))
{
WINDOW* pad = newpad(2, len);
AutoWin pad( newpad(2, len) );
for(U16 i = 0; i < len-1; ++i)
waddch(pad, ACS_HLINE);
@@ -553,7 +549,6 @@ static U8 linkToChild(I16 startY, I16 startX, I16 endX, bool moreSiblings)
mvwaddch(pad, 1, len-1, ACS_VLINE);
drawpad(pad, startY, startX);
delwin(pad);
}
return 2;
}
@@ -561,13 +556,13 @@ static U8 linkToChild(I16 startY, I16 startX, I16 endX, bool moreSiblings)
/* I16 Tree::drawLine(person_id id, I16 drawY, I16 drawX) const */
/* { */
/* people[id].draw(drawY, drawX, true, false, true); */
/* people_[id].draw(drawY, drawX, true, false, true); */
/* vector<person_id> children; */
/* for(U32 i=0; i<relations[id].size(); ++i) */
/* for(U32 i=0; i<relations_[id].size(); ++i) */
/* { */
/* if (relations[id][i].type == RelType::Parent) */
/* children.push_back(relations[id][i].id); */
/* if (relations_[id][i].type == RelType::Parent) */
/* children.push_back(relations_[id][i].id); */
/* } */
/* if (!children.empty()) */
@@ -587,13 +582,13 @@ static U8 linkToChild(I16 startY, I16 startX, I16 endX, bool moreSiblings)
/* return drawX; */
/* } */
/* return drawX + people[id].boxWidth() + dist_between_; */
/* return drawX + people_[id].boxWidth() + dist_between_; */
/* } */
I16 Tree::drawLineWithWives(const person_id id, I16 drawY, const I16 drawX) const
{
const Person& person = people[id];
const auto& rels = relations[id];
const Person& person = people_[id];
const auto& rels = relations_[id];
person_id spouse = Nobody;
vector<person_id> children;
@@ -612,7 +607,7 @@ I16 Tree::drawLineWithWives(const person_id id, I16 drawY, const I16 drawX) cons
U16 spouse_w = 0;
if (spouse != Nobody)
{
U16 spouse_box = people[spouse].draw(drawY, drawX + person_w + spouse_dist_,
U16 spouse_box = people_[spouse].draw(drawY, drawX + person_w + spouse_dist_,
spouse == selected_, false, false, false, zoom_==1);
spouse_w = spouse_dist_ + spouse_box;
}
@@ -658,7 +653,7 @@ void Tree::selectLeft()
void Tree::selectDown()
{
const auto& curRels = relations[selected_];
const auto& curRels = relations_[selected_];
for(const Relation& rel: curRels)
{
+8 -8
View File
@@ -5,6 +5,10 @@
#include "person.hpp"
using person_id = U32;
enum: person_id {
Nobody = UINT32_MAX
};
enum RelType : U8
{
None = 0,
@@ -24,16 +28,12 @@ struct Relation
RelType type;
};
enum: person_id {
Nobody = UINT_MAX
};
class Tree
{
public:
Tree() = default;
template <class Str>
explicit Tree(Str&& Name) : treeName(std::forward<Str>(Name)) {}
explicit Tree(Str&& Name) : treeName_(std::forward<Str>(Name)) {}
/* Tree(const Tree&) = default; */
/* Tree& operator= (const Tree& other); */
@@ -96,9 +96,9 @@ private:
void selectLeftSibSpouse(person_id);
void selectRightSib(person_id);
std::string treeName;
std::vector<Person> people; //The data for each member
std::vector<std::vector<Relation>> relations; //The relatives of each member
std::string treeName_;
std::vector<Person> people_; //The data for each member
std::vector<std::vector<Relation>> relations_; //The relatives of each member
person_id focused_ = 0; /* the tree was draw based on this person */
person_id selected_ = 0;
+25 -1
View File
@@ -6,7 +6,21 @@
#include <sys/mman.h>
#include <iostream>
void init()
AutoWin::AutoWin(WINDOW* win)
: w_(win)
{}
AutoWin::~AutoWin()
{
delwin(w_);
}
AutoWin::operator WINDOW*()
{
return w_;
}
void init_ncurses()
{
setlocale(LC_ALL, "en_US.UTF-8");
@@ -66,3 +80,13 @@ void drawpad(WINDOW* pad, I16 drawY, I16 drawX)
drawY, drawX,
LINES - 1, COLS - 1);
}
I32 min(I32 a, I32 b)
{
return a <= b ? a : b;
}
I32 max(I32 a, I32 b)
{
return a >= b ? a : b;
}
+25 -36
View File
@@ -1,27 +1,23 @@
#pragma once
#include <utility>
#include <ncursesw/ncurses.h>
#include "common/basicTypes.h"
/* template <class Vec> */
/* Vec& operator+=(Vec& lhs, Vec&& rhs) // Combine 2 vectors */
/* { */
/* lhs.reserve(lhs.size() + rhs.size()); */
#define VISUALIZATION_HELP_STR "Visualization defaults (can be changed in config.h):\n" \
"Enter - focus the currently selected person\n" \
"Arrow keys - move the camera\n" \
"h, j, k, l - move the selection\n" \
"i - display info about the selected person\n"
/* for(auto&& el: rhs) */
/* lhs.push_back( std::move(el) ); */
/* return lhs; */
/* } */
class AutoWin // ncurses windows that is freed on destruction
{
public:
explicit AutoWin(WINDOW*);
~AutoWin();
/* template <class Vec> */
/* Vec& operator+=(Vec& lhs, const Vec& rhs) // Combine 2 vectors */
/* { */
/* lhs.reserve(lhs.size() + rhs.size()); */
/* for(const auto& el: rhs) */
/* lhs.push_back(el); */
/* return lhs; */
/* } */
operator WINDOW*();
private:
WINDOW* w_;
};
using fd_t = int; /* file descriptor */
struct MappedFile
@@ -30,31 +26,24 @@ struct MappedFile
unsigned len;
};
void init();
void init_ncurses();
MappedFile mapfile(fd_t);
fd_t openOrDie(const char* path);
template <class Vec>
void remove(Vec& vec, size_t pos) /* Remove from vector by moving the last element in its place */
{
vec[pos] = vec[vec.size() - 1];
vec.pop_back();
}
inline I32 min(I32 a, I32 b)
{
return a <= b ? a : b;
}
inline I32 max(I32 a, I32 b)
{
return a >= b ? a : b;
}
/* wheather a pad with the given coordinates will be visible */
bool willBeVisible(I16 drawY, I16 drawX, U16 height, U16 width);
/* like pnoutrefresh but pminrow,col are calculated based on width,height and
top left coords, which can be negative (outside the screen) */
void drawpad(WINDOW* pad, I16 drawY, I16 drawX);
I32 min(I32 a, I32 b);
I32 max(I32 a, I32 b);
template <class Vec>
void remove(Vec& vec, size_t pos) /* Remove from vector by moving the last element in its place */
{
vec[pos] = vec[vec.size() - 1];
vec.pop_back();
}
-4
View File
@@ -1,7 +1,3 @@
--help
help - based on user config
findRelative
findRelation - extend