diff --git a/main.cpp b/main.cpp index e1900b1..d9e3ab8 100644 --- a/main.cpp +++ b/main.cpp @@ -24,18 +24,21 @@ int main() init_pair(COLOR_SELECTED, COLOR_YELLOW, -1); wnoutrefresh(stdscr); /* if not present the first real refresh is missed */ - Tree test1("Dechkovi"); //addTree Dechkovi addTree Karamanovi load 0 load 1 (to work with these example trees) + Tree test1("Dechkovi"); + test1.loadFromFile(); + Tree test2("Karamanovi"); - test1.addPerson("Rumen", true, Nobody, Nobody, {1968, 8, 9}); - test1.addPerson("Nadejda", false, Nobody, Nobody, {1972, 12, 15}); - test1.addRelation(0, Spouse, 1); - test1.addPerson("Venelin", true, 0, 1, {1998, 6, 9}); - test1.addPerson("Vasil", true, 0, 1, {2001, 1, 16}); + /* test1.addPerson("Rumen", true, Nobody, Nobody, {1968, 8, 9}); */ + /* test1.addPerson("Nadejda", false, Nobody, Nobody, {1972, 12, 15}); */ + /* test1.addRelation(0, Spouse, 1); */ + /* test1.addPerson("Venelin", true, 0, 1, {1998, 6, 9}); */ + /* test1.addPerson("Vasil", true, 0, 1, {2001, 1, 16}); */ /* test1.addRelation(2, Brother, 3); */ /* test1.addPerson("Maria-Veronika", 2009, 9, 3, 1, 0, 1); */ /* test1.addRelation(2, Brother, 4); */ /* test1.addRelation(3, Brother, 4); */ + test1.saveToFile(); test2.addPerson("Vasil Dimitrov", true, Nobody, Nobody, {1941, 5, 18}); test2.addPerson("Maria", false); @@ -58,8 +61,7 @@ int main() /* test1.display(); */ - /* test1.saveTree(); */ - /* test2.saveTree(); */ + /* test2.saveToFile(); */ Tree firstTree("firstTree"); @@ -86,7 +88,7 @@ int main() /* firstTree.print(); */ firstTree.display(); - /* firstTree.saveTree(); */ + /* firstTree.saveToFile(); */ /* firstTree.printRel(3); */ firstTree.printOldestAncestors(3); @@ -167,11 +169,11 @@ int main() /* case 2: //load */ /* cin >> input; */ /* trees.push(Tree(input)); */ - /* if (!trees[trees.gNum() - 1].loadTree()) */ + /* if (!trees[trees.gNum() - 1].loadFromFile()) */ /* std::cout << "File doesnt exist or is empty.\n\n"; */ /* break; */ /* case 3: //save */ - /* trees[curTree].saveTree(); */ + /* trees[curTree].saveToFile(); */ /* break; */ /* case 4: //addPerson */ /* std::cin >> name >> day >> month >> year >> sex; */ diff --git a/person.cpp b/person.cpp index 9078e48..1884061 100644 --- a/person.cpp +++ b/person.cpp @@ -62,7 +62,6 @@ void EventTime::print() const Person::Person(const char* name, EventTime birth, bool isMale, EventTime death) : name_(name) - , numRel_(0) , birth_(birth) , isMale_(isMale) , death_(death) @@ -72,24 +71,23 @@ Person::Person(const char* name, EventTime birth, bool isMale, EventTime death) bool Person::operator==(const Person &other)const { - return name_ == other.name_ && isMale_ == other.isMale_; //TODO && day == other.day || !day || !other.day) && (month == other.month || !month || !other.month) && (year == other.year || !year || !other.year); + return name_ == other.name_; //&& isMale_ == other.isMale_; //TODO && day == other.day || !day || !other.day) && (month == other.month || !month || !other.month) && (year == other.year || !year || !other.year); } -void Person::save(std::ofstream &file)const +void Person::write(std::ofstream &file)const { // char nameLen = name_.size(); file.write(reinterpret_cast(&nameLen), sizeof(nameLen)); file.write(name_.data(), nameLen * sizeof(name_[0])); - file.write(reinterpret_cast(&numRel_), sizeof(numRel_)); file.write(reinterpret_cast(&isMale_), sizeof(isMale_)); //file.write(reinterpret_cast(&day), sizeof(day)); TODO //file.write(reinterpret_cast(&month), sizeof(month)); //file.write(reinterpret_cast(&year), sizeof(year)); } -void Person::load(std::ifstream &file) +void Person::read(std::ifstream &file) { U8 nameLen; file.read((char*)&nameLen, sizeof(nameLen)); //Check len @@ -99,7 +97,6 @@ void Person::load(std::ifstream &file) nameBuf[nameLen] = '\0'; name_ = nameBuf; - file.read(reinterpret_cast(&numRel_), sizeof(numRel_)); file.read(reinterpret_cast(&isMale_), sizeof(isMale_)); //file.read(reinterpret_cast(&day), sizeof(day)); TODO //file.read(reinterpret_cast(&month), sizeof(month)); diff --git a/person.h b/person.h index e3c6232..d9c8bb0 100644 --- a/person.h +++ b/person.h @@ -20,13 +20,19 @@ struct EventTime I8 minute; //0-59 }; -enum: U8 { +enum Genders: U8 +{ + FEMALE = 0, + MALE +}; + +enum BoxColors: U8 { COLOR_MALE = 1, COLOR_FEMALE, COLOR_SELECTED }; -enum: U8 { +enum DisplayDimensons: U8 { BOX_HEIGHT = 5, LINK_HEIGHT = 3 }; @@ -35,20 +41,21 @@ class Person { public: Person() = default; - Person(const char* name, EventTime birth, bool sex, EventTime death); + Person(const char* name, EventTime birth={}, bool sex=true, EventTime death={}); /* Person(Person&&) = default; */ /* Person& operator=(Person&&) = default; */ /* Person& operator=(const Person&) = default; */ - bool operator==(const Person &other)const; + bool operator==(const Person &other) const; - void save(std::ofstream &file)const;// - void load(std::ifstream &file); + void write(std::ofstream &file) const;// + void read(std::ifstream &file); void rename(const char* newName); void displayInfo() const; bool isMale() const; + const EventTime& birth() const { return birth_; }; U16 boxWidth() const; /* Draw a Person's box on the terminal @@ -56,14 +63,8 @@ public: returns the width of the box*/ void draw(I16 drawY, I16 drawX, bool selected, bool hasSpouse, bool isSpouse, bool hasChildren=false) const; - /* WINDOW* createPad()const; */ - - /* staitc constexpr U8 BOX_HEIGHT = 5; */ - friend class Tree; - private: std::string name_; - unsigned numRel_; //Number of close relatives - mother, father, brother, sister, wife/husband, child, ex-wife/husband EventTime birth_; bool isMale_; EventTime death_; diff --git a/readme.md b/readme.md index c0295b5..e69de29 100644 --- a/readme.md +++ b/readme.md @@ -1,72 +0,0 @@ -# FamilyTree - -I wrote this program in C ++ on Visual Studio 2017, starting with a blank project. I used iostream for input and output and fstream for saving and loading trees. Instead of using vector, I chose to write a dynamic array myself. I used HxD hex editor to look for errors in the files. -In the program, "family trees" are weighted graphs implemented through a list of neighbors. The program allows for addition, subtraction (removal of common members) and comparison of trees, saving them to and loading them from files and more. - -Thus large constructs consisting of multiple family trees can be created and managed using this program. - -# How to use - -The program works with an array of trees numbered from 0. In order to execute a command on a tree you need to enter its number(treeID). -(S/E) = (Space/Enter) - -Relations are as followed and cap sensitive: - - Father - Mother - Son - Daughter - Brother - Sister - HalfBrother - HalfSister - Wife - Husband - ExWife - ExHusband - -List of commands: - - "quit" (S/E) - close the program - - "chooseTree" (S/E) TreeID (S/E) - Choose tree to execute commands on - - "addTree" (S/E) TreeName (S/E) - Adds a tree to the array - - "rename" (S/E) NewName (S/E) - Renames a tree - - "load" (S/E) TreeID (S/E) - Loads a tree based on its name - - "save" (S/E) TreeID (S/E) - Saves a tree based on its name - - "addPerson" (S/E) BirthDay (S/E) BirthMonth (S/E) BirthYear (S/E) PersonSex (S/E) - Add a person to a tree with treeID with a birthday on BirthDay.BirthMonth.BirthYear and sex (PersonSex) 0 = Male 1 = Female - - "addPersonWP" (S/E) BirthDay (S/E) BirthMonth (S/E) BirthYear (S/E) PersonSex (S/E) FatherID (S/E) MotherID - The same as addPerson but adds father and motherbased on their IDs - - "addRelation" (S/E) FirstRelativeName (S/E) Relation (S/E) SecondRelativeName - Adds a relation betwean two members based on their full names - - "addRelationID" (S/E) FirstRelativeID (S/E) Relation (S/E) SecondRelativeID - Same as addElation but based on ID - - "findID" (S/E) SoughtPersonName (S/E) - Prints the ID of a person with the given name - - "equate" (S/E) FirstTreeID (S/E) SecondTreeID - Enter the IDs of two trees, the first one becomes equal to the second one - - "combine" (S/E) FirstTreeID (S/E) SecondTreeID - Enter the IDs of two trees, the first one becomes equal to the combinatio of the two - - "subtract" (S/E) FirstTreeID (S/E) SecondTreeID - Enter the IDs of two trees, the members that are present in both trees are removed from the first tree - - "removePerson" (S/E) Name - Remove person with the given name - - "removePersonID" (S/E) Name - Remove person with the given ID - - "removeRelation" (S/E) FirstName (S/E) SecondName - Remove the relation betwean the people with the given names if they have one - - "removeRelationID" (S/E) FirstName (S/E) SecondName - Same as removeRelation but with IDs instead - - "prtRelatives" (S/E) PersonID - Print the relatives the person with the given ID in the console - - "prtMember" (S/E) PersonID - Print the personal data of a member on the console - - "prtCommonAncestor" (S/E) FirstPersonID (S/E) SecondPersonID (S/E) - - "prtOldestAncestor" (S/E) PersonID (S/E) diff --git a/tree.cpp b/tree.cpp index 67d599b..cdaa0db 100644 --- a/tree.cpp +++ b/tree.cpp @@ -1,69 +1,18 @@ +#include"tree.h" #include #include #include -#include"tree.h" #include"person.h" #include"help.h" using std::cout; +using std::ofstream; +using std::ifstream; using std::vector; Tree Tree::operator+ (const Tree& other) const { - /* Tree result(treeName + other.treeName); //The tree resulting from the addition */ - /* vector newId; //The indexes members of the II tree will have in result */ - /* newId.reserve(other.numPeople); */ - - /* U32 idCounter = numPeople; */ - /* for (U32 i = 0, j; i < other.numPeople; ++i) //Searches for people present in both trees and gives them the same index in result */ - /* { */ - /* for (j = 0; j < numPeople; ++j) */ - /* { */ - /* if (other.people[i] == people[j]) */ - /* { */ - /* newId[i] = j; */ - /* break; */ - /* } */ - /* } */ - /* if (j >= numPeople) */ - /* newId[i] = ++idCounter; */ - /* } */ - /* result.numPeople = idCounter; //The number of members the resulting tree has */ - - /* result.people.reserve(result.numPeople); */ - /* result.people = people; //Adds the members of the I tree to the resulting tree */ - /* for (U32 i=0; i < other.numPeople; ++i) //Adds the members of the II tree to the resulting tree */ - /* { */ - /* if (newId[i] >= numPeople) */ - /* result.people.push_back(other.people[i]); */ - /* } */ - - /* result.relations.reserve(result.numPeople); */ - /* result.relations = relations; */ - - /* for (U32 i=0; i < other.numPeople; ++i) //Adds the relatives from the II tree */ - /* { */ - /* if (newId[i] >= numPeople) //If the member is found in the II tree but not in the I */ - /* { */ - /* result.relations.push_back(other.relations[i]); */ - /* } */ - /* else //If he's found in both */ - /* { */ - /* for (person_id j = 0; j < other.relations[i].size(); ++j) //Adds the number of relatives from the II to the number in the I */ - /* { */ - /* if (newId[other.relations[i][j].id] > numPeople) */ - /* ++result.people[newId[i]].numRel_; */ - /* } */ - - /* result.relations[newId[i]].reserve(result.people[newId[i]].numRel_); */ - - /* result.relations[newId[i]] = relations[newId[i]]; */ - - /* result.relations[newId[i]] += other.relations[i]; //The relatives from the II tree */ - /* } */ - /* } */ Tree result(*this); - return result += other; } @@ -102,12 +51,12 @@ Tree& Tree::operator+=(const Tree& other) relations.push_back(other.relations[i]); for (Relation& rel: relations.back()) rel.id = newId[rel.id]; - /* for (person_id j = 0; j < other.people[i].numRel_; ++j) */ + /* for (person_id j = 0; j < other.relations[i].size(); ++j) */ /* relations[newId[i]][j].id = newId[ relations[newId[i]][j].id ]; */ } else //if present in both trees { - /* for (person_id j = 0; j < other.people[i].numRel_; ++j) //combine relations */ + /* for (person_id j = 0; j < other.relations[i].size(); ++j) //combine relations */ /* { */ /* if (newId[other.relations[i][j].id] >= numPeople) */ /* ++people[newId[i]].numRel_; */ @@ -124,11 +73,6 @@ Tree& Tree::operator+=(const Tree& other) } } - /* for (person_id j = 0; j < other.people[i].numRel_; ++j) */ - /* { */ - /* relations[newId[i]][j + people[newId[i]].numRel_ - other.people[i].numRel_].id = newId[other.relations[i][j].id]; */ - /* relations[newId[i]][j + people[newId[i]].numRel_ - other.people[i].numRel_].type = other.relations[i][j].type; */ - /* } */ } } @@ -154,30 +98,28 @@ Tree& Tree::operator-= (const Tree& other) } - void Tree::rename(const char* newName) { treeName = newName; } -person_id Tree::findId(const char* name, const short year, const unsigned char month, const unsigned char day) const +person_id Tree::findId(const char* name, bool m) const { - //TODO - //Person sought(name, day, month, year,) + Person sought(name, {}, m); for (person_id i = 0; i < numPeople; ++i) { - if (name == people[i].name_) //&& (year == people[i].year || !year || !people[i].year) && (month == people[i].month || !month || !people[i].month) && (day == people[i].day || !day || people[i].day)) + if (sought == people[i]) return i; } return Nobody; } -person_id Tree::findOldestAncestor(unsigned id, bool isMale)const +person_id Tree::findOldestAncestor(person_id id, bool isMale) const { - for (unsigned i = 0; i < people[id].numRel_; ++i) + for (const Relation& rel: relations[id]) { - if (relations[id][i].type == Child && people[ relations[id][i].id ].isMale_ == isMale) - return findOldestAncestor(relations[id][i].id, isMale); + if (rel.type == Child && people[ rel.id ].isMale() == isMale) + return findOldestAncestor(rel.id, isMale); } return id; } @@ -193,9 +135,9 @@ person_id Tree::findCommonAncestor(const unsigned firstId, const unsigned second U16 oldestBday = SHRT_MAX; for (person_id i = 0; i < numPeople; ++i) { - if (visited[i] == 2 && people[i].birth_.year < oldestBday) + if (visited[i] == 2 && people[i].birth().year < oldestBday) { - oldestBday = people[i].birth_.year; + oldestBday = people[i].birth().year; oldest = i; } } @@ -207,7 +149,7 @@ person_id Tree::findParent(person_id child, bool isParentMale) const person_id parent = Nobody; for(const Relation& rel: relations[child]) { - if (rel.type == Child && people[rel.id].isMale()) + if (rel.type == Child && people[rel.id].isMale() == isParentMale) parent = rel.id; } return parent; @@ -250,52 +192,42 @@ RelType Tree::findRelation(person_id first, person_id second) const return None; } -void Tree::saveTree() const +void Tree::saveToFile() const { - std::ofstream peopleFile(treeName+ ".people", std::ios::binary); + ofstream file(treeName + ".famt", std::ios::binary); - peopleFile.write(reinterpret_cast(&numPeople), sizeof(numPeople)); - for (unsigned i = 0; i < numPeople; ++i) - people[i].save(peopleFile); - peopleFile.close(); + file.write((const char*)(&numPeople), sizeof(numPeople)); + for (const Person& p: people) + p.write(file); - std::ofstream relationFile(treeName + ".relations", std::ios::binary); - - for (unsigned i = 0; i < numPeople; ++i) + for (auto& rels: relations) { - //relationFile.write((char*)&people[i].numRel_, sizeof(people[i].numRel_)); - for (unsigned j = 0; j < people[i].numRel_; ++j) - relationFile.write(reinterpret_cast(&relations[i][j]), sizeof(relations[i][j])); + U32 numRels = rels.size(); + file.write((const char*)(& numRels), sizeof(numRels)); + file.write((const char*)(rels.data()), sizeof(Relation) * numRels); } - relationFile.close(); } -bool Tree::loadTree() +bool Tree::loadFromFile() { - std::ifstream peopleFile(treeName + ".people", std::ios::binary); - if (peopleFile.peek() == std::char_traits::eof()) - { - peopleFile.close(); - return 0; - } + ifstream file(treeName + ".famt", std::ios::binary); + + file.read((char*)(&numPeople), sizeof(numPeople)); - peopleFile.read(reinterpret_cast(&numPeople), sizeof(numPeople)); people.resize(numPeople); - for (unsigned i = 0; i < numPeople; ++i) - people[i].load(peopleFile); - peopleFile.close(); + for (Person& p: people) + p.read(file); - - std::ifstream relationFile(treeName + ".relations", std::ios::binary); relations.resize(numPeople); - for (unsigned i = 0; i < numPeople; ++i) + for (auto& rels: relations) { - relations[i].resize(people[i].numRel_); - for (unsigned j = 0; j < people[i].numRel_; ++j) - relationFile.read(reinterpret_cast(&relations[i][j]), sizeof(relations[i][j])); + U32 numRels; + file.read((char*)(& numRels), sizeof(numRels)); + + rels.resize(numRels); + file.read((char*)(rels.data()), sizeof(Relation) * numRels); } - relationFile.close(); - return 1; + return true; } void Tree::addPerson(const char* name, bool isMale, unsigned father, unsigned mother, EventTime birth, EventTime death) @@ -320,10 +252,10 @@ bool Tree::addRelation(const char* firstName, const RelType type, const char* se bool Tree::addRelation(const unsigned firstId, const RelType type, const unsigned secondId, bool opposite)// { if (firstId == Nobody || firstId >= numPeople || secondId == Nobody || secondId >= numPeople || type == None) - return 0; + return false; unsigned i = 0; - for (; i < people[firstId].numRel_; ++i) //If they are already relatives + for (; i < relations[firstId].size(); ++i) //If they are already relatives { if (relations[firstId][i].id == secondId) { @@ -331,10 +263,9 @@ bool Tree::addRelation(const unsigned firstId, const RelType type, const unsigne break; } } - if (i >= people[firstId].numRel_) + if (i >= relations[firstId].size()) { relations[firstId].push_back({secondId, type}); - ++people[firstId].numRel_; } if (!opposite) @@ -356,7 +287,7 @@ bool Tree::addRelation(const unsigned firstId, const RelType type, const unsigne default: assert(!"Unexpected case"); break; } } - return 1; + return true; } void Tree::removeRelation(const char* firstName, const char* secondName) @@ -379,10 +310,10 @@ void Tree::removePerson(const person_id id) } } } - for (unsigned i = 0; i < people[numPeople - 2].numRel_; ++i) //Move the last in his place and update IDs + for (unsigned i = 0; i < relations[numPeople - 2].size(); ++i) //Move the last in his place and update IDs { relId = relations[numPeople - 1][i].id; - for (unsigned j = 0; j < people[relId].numRel_; ++j) + for (unsigned j = 0; j < relations[relId].size(); ++j) { if (relations[relId][j].id == numPeople - 1) { @@ -396,10 +327,10 @@ void Tree::removePerson(const person_id id) --numPeople; } -void Tree::removePerson(const char* personName, short year, unsigned char month, unsigned char day) -{ - removePerson(findId(personName, year, month, day)); -} +/* void Tree::removePerson(const char* personName, short year, unsigned char month, unsigned char day) */ +/* { */ +/* removePerson(findId(personName, year, month, day)); */ +/* } */ void Tree::removeRelation(const unsigned firstId, const unsigned secondId) { @@ -492,8 +423,8 @@ void Tree::print() const member.displayInfo(); } -void Tree::printRel(const unsigned id)const -{ +/* void Tree::printRel(const unsigned id)const */ +/* { */ /* cout << people[id].name_; */ /* if (people[id].numRel_ == 0) */ /* cout << " has no known relatives"; */ @@ -541,7 +472,7 @@ void Tree::printRel(const unsigned id)const /* } */ /* cout << '\n'; */ /* } */ -} +/* } */ void Tree::printMember(const unsigned id)const { diff --git a/tree.h b/tree.h index 6fe7dbe..f752dc9 100644 --- a/tree.h +++ b/tree.h @@ -44,7 +44,7 @@ public: void rename(const char* newName); - person_id findId(const char*, short year=0, const unsigned char month=0, const unsigned char day=0) const; + person_id findId(const char* name, bool=true) const; person_id findOldestAncestor(person_id id, bool sex = 0) const;// //oldest common ancestor ID, a person is considerd an ancestor of himself person_id findCommonAncestor(person_id firstId, person_id secondId) const; @@ -53,22 +53,22 @@ public: person_id findSpouse(person_id person) const; std::vector findChildren(person_id person) const; - void saveTree()const;// - bool loadTree(); + void saveToFile()const;// + bool loadFromFile(); void addPerson(const char* name, bool isMale, person_id father=Nobody, person_id mother=Nobody, EventTime birth={}, EventTime death={}); bool addRelation(const char* firstName, RelType, const char* secondName); //Adds a new relation or edits an old one. 0 - failure 1 - success bool addRelation(person_id firstId, RelType, person_id secondId, bool opposite=0); //0 - failure 1 - success void removePerson(person_id id); //Removes a member and the last one takes his ID - void removePerson(const char*, short year = 0, const unsigned char month = 0, const unsigned char day = 0); + /* void removePerson(const char*, short year = 0, const unsigned char month = 0, const unsigned char day = 0); */ void removeRelation(person_id firstId, person_id secondId); void removeRelation(const char* firstName, const char* secondName); void display(); void print()const; - void printRel(person_id id)const; //Prints out close relatives + /* void printRel(person_id id)const; //Prints out close relatives */ void printMember(person_id id)const; void printOldestAncestors(person_id id)const;// @@ -89,7 +89,7 @@ private: void selectLeftSibSpouse(person_id person); void selectRightSib(person_id person); - std::string treeName = "Unnamed"; + std::string treeName; unsigned numPeople = 0; std::vector people; //The data for each member std::vector> relations; //The relatives of each member