commit c46805e77bdb49f62044cff883789a478754a20c Author: Flinner Yuu Date: Wed Dec 4 11:51:16 2024 +0300 feat: init diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..b4c72df --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,24 @@ +# Minimum required version of CMake +cmake_minimum_required(VERSION 3.10) + +# Project name +project(ClinicProject) + +# Set the C++ standard +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# Add the executable target for the project +add_executable(ClinicApp + main.cpp + Clinic.cpp + Patient.cpp + Visit.cpp + +) + +# Include directories if necessary (e.g., for .hpp files) +include_directories(${CMAKE_SOURCE_DIR}) + +# If you have any custom configurations or testing requirements, +# you can define them here, such as linking libraries or adding flags. diff --git a/Clinic.cpp b/Clinic.cpp new file mode 100644 index 0000000..a270e6d --- /dev/null +++ b/Clinic.cpp @@ -0,0 +1,172 @@ +#include "Patient.hpp" +#include +#include +#include +#include +#include +#include +#include + +#ifndef CLINIC_ +#define CLINIC_ + +enum class MenuChoice { + NewPatient = 1, + NewVisit = 2, + DisplayPatient = 3, + Quit = 4, +}; + +struct Clinic { +public: + std::vector patients; + std::string filename; + +private: + // C++ types are getting crazy here :( I really miss Rust + // `std::optional` needed because a patient might not be found + // `std::reference_wrapper` because `std::optional` deoesn't accept references + // I want to reference because (I want to save cpu cycles from memory copies) + std::optional> + find_patient(u_int32_t patient_id) { + auto it = std::find_if(patients.begin(), patients.end(), + [&id = patient_id](const Patient &p) -> bool { + return p.get_id() == id; + }); + if (it != patients.end()) + return *it; + return std::nullopt; + }; + +public: + Clinic(std::string filename) : filename(filename) { + /// this file contains a list of patient IDs. + // 1 + // 2 + // 4 (could be non-sequential + // but always sorted!) + u_int32_t patient_id; + std::ifstream file{filename}; + if (!file.is_open()) { + std::cerr << "File " << filename << " couldn't be opened :(\n"; + exit(1); + } + + while (file >> patient_id) + patients.push_back(Patient(patient_id)); + } + + // writes the patients.txt file containing patient IDs. + void write_file() { + std::ofstream file{filename}; + for (auto p : patients) + file << p.get_id() << "\n"; + } + u_int32_t generate_id() { + u_int32_t id{}; + // O(n), could be improved by using UUIDs, or store max_id in Clinic + for (auto p : patients) + id = std::max(id, p.get_id()); + id++; + return id; + } + + MenuChoice menu_main() { + int choice{}; + std::cout << "==============================================\n"; + std::cout << "Please choose a command:\n"; + std::cout << "1. add new patient\n"; + std::cout << "2. add new visit\n"; + std::cout << "3. display patient information\n"; + std::cout << "4. quit\n"; + std::cout << ">> "; + + std::cin >> choice; + + while (choice < int(MenuChoice::NewPatient) || + choice > int(MenuChoice::Quit)) { + std::cout << "Invalid input! Try again :)\n"; + std::cout << "Please choose a command:\n"; + std::cout << "1. add new patient\n"; + std::cout << "2. add new visit\n"; + std::cout << "3. display patient information\n"; + std::cout << "4. quit\n"; + std::cout << ">> "; + std::cin >> choice; + } + return static_cast(choice); + } + + void menu_patient_info() { + int patient_id; + std::cout << "Patient file number: "; + std::cin >> patient_id; + auto patient = find_patient(patient_id); + if (patient.has_value()) + std::cout << patient.value(); + else + std::cout << "No Patient exists with id= " << patient_id << "\n"; + std::cout << "--------------------\n\n"; + } + + void menu_add_new_patient() { + std::string reading_string; + std::string input_string; + Patient patient{}; + std::stringstream patient_stream; + + // not the most optimal way, but the lazy way + // see: https://ammar.engineer/posts/2023/06/03/laziness/ + // this "emulates" a file so that I can resuse my >> operator + reading_string += std::to_string(generate_id()) + "\n"; + std::cout << "Generate " << reading_string; + std::cout << "First name (no spaces): "; + std::cin >> input_string; + reading_string += input_string + "\n"; + std::cout << "Last name (no spaces): "; + std::cin >> input_string; + reading_string += input_string + "\n"; + std::cout << "Gender (M or F): "; + std::cin >> input_string; + reading_string += input_string + "\n"; + std::cout << "Age: "; + std::cin >> input_string; + reading_string += input_string + "\n"; + std::cout << reading_string; + patient_stream = std::stringstream(reading_string); + + // all of that just for this moment: + patient_stream >> patient; + patients.push_back(patient); // save in memory + patient.write_patient(); // write to file + write_file(); + } + void menu_add_vist() { + std::string reading_string; + std::string input_string; + int patient_id; + + std::cout << "Patient file number: "; + std::cin >> patient_id; + std::cout << "Date:"; + std::cin >> input_string; + reading_string += input_string + " "; + std::cout << "Doctor's First Name: "; + std::cin >> input_string; + reading_string += input_string + " "; + std::cout << "Doctors's Last Name: "; + std::cin >> input_string; + reading_string += input_string + " "; + + std::stringstream visit_stream = std::stringstream(reading_string); + Visit visit; + visit_stream >> visit; + auto patient = find_patient(patient_id); + if (patient.has_value()) + patient.value().get().add_visit(visit); + else + std::cout << "No Patient exists with id= " << patient_id << "\n"; + } +}; + +#endif diff --git a/Patient.cpp b/Patient.cpp new file mode 100644 index 0000000..5ae863a --- /dev/null +++ b/Patient.cpp @@ -0,0 +1,111 @@ +// The class Patient stores the personal information of a patient as well as +// his/her history of medical visits as a vector of Visit objects, all as +// private data members. The class interface allows user code to: + +// - Instantiate a Patient object given (as parameters) all his/her +// personal information. No visit history is required. The same +// function should also create a patient record file according to +// the format specified above. + +// - Instantiate a Patient object given (as parameter) his/her ID +// number. The personal information as well as the visit history of +// the patients to be read from his existing record file. + +// - Add one Visit record given a Visit object as parameter. The same +// function should record the Visit in the patient file. + +// - Print the patient record according to the format specified in +// the files above using the << operator (including history) + +#include "Patient.hpp" +#include "Visit.hpp" +#include +#include +#include +#include +#include +#include +#include +#include + +//////////////////////////////////// +// File: patient_files/3.txt // +// 3 // +// Amirah // +// Asaad // +// F // +// 26 // +// 3 1/1/2018 Abdullah Alshareef // +// 9 4/1/2018 Helen Philips // +// 14 8/1/2018 Abdullah Alshareef // +//////////////////////////////////// + +Patient::Patient(u_int32_t id) : id(id) { + auto patient_file_path = "patient_files/" + std::to_string(id) + ".txt"; + std::ifstream patient_file{patient_file_path}; + if (patient_file.is_open()) { + patient_file >> *this; + } else { + std::cerr << "Failed to read patient file at: " + patient_file_path; + exit(1); + } +} + +void Patient::write_patient() { + // create patient's file + // this will overwrite any patient with the same id + auto patient_file_path = "patient_files/" + std::to_string(id) + ".txt"; + std::ofstream patient_file{patient_file_path}; + if (patient_file.is_open()) { + patient_file << *this; + } else { + std::cerr << "Failed to create patient file at: " + patient_file_path; + exit(1); + } +} + +u_int32_t Patient::get_id() const { return id; } + +void Patient::add_visit(Visit const &visit) { + visits.push_back(visit); + // could have logic to append data and not re-write the whole thing, but not + // worth it + // this rewrites the entire file each time a visits gets added + write_patient(); +} + +std::ostream &operator<<(std::ostream &out, Patient &patient) { + // clang-format off + out << patient.id << "\n"; + out << patient.first_name << "\n"<< patient.last_name << "\n"; + out << patient.gender_char() << "\n"; + out << patient.age << "\n"; + for (auto visit : patient.visits) + out << visit << "\n"; + return out; + // clang-format on +} +std::istream &operator>>(std::istream &in, Patient &patient) { + char gender_char{}; + in >> patient.id; + in >> patient.first_name >> patient.last_name; + in >> gender_char; + in >> patient.age; + + patient.gender = gender_char == 'M' ? true : false; + + std::string visit_string; + Visit visit{}; + + // read to string, convert to string stream, read from that stream again + // weird C++ gymnastics (or bad design by me) + getline(in, visit_string); // first string is always empty for some reason + while (getline(in, visit_string)) { + std::stringstream visit_stream(visit_string); + visit_stream >> visit; + patient.visits.push_back(visit); + } + + return in; + // clang-format on +} diff --git a/Patient.hpp b/Patient.hpp new file mode 100644 index 0000000..3708b95 --- /dev/null +++ b/Patient.hpp @@ -0,0 +1,53 @@ +#ifndef PATIENT_HPP +#define PATIENT_HPP + +#include "Visit.hpp" +#include +#include +#include +#include +#include +#include + +//////////////////////////////////// +// File: patient_files/3.txt // +// 3 // +// Amirah // +// Asaad // +// F // +// 26 // +// 3 1/1/2018 Abdullah Alshareef // +// 9 4/1/2018 Helen Philips // +// 14 8/1/2018 Abdullah Alshareef // +//////////////////////////////////// + +class Patient { +private: + // clang-format off + u_int32_t id; + std::string first_name; + std::string last_name; + bool gender; // true gender = M, false gender = F + u_int32_t age; + std::vector visits; + + char gender_char() { return gender ? 'M' : 'F'; } + +public: + void write_patient(); + Patient(){}; + Patient(u_int32_t id, std::string first_name, std::string last_name, bool gender, u_int age) + : id(id), first_name(first_name), last_name(last_name), gender(gender), age(age) { + // clang-format on + write_patient(); + } + Patient(u_int32_t id); + u_int32_t get_id() const; + +public: + void add_visit(Visit const &visit); + friend std::ostream &operator<<(std::ostream &out, Patient &patient); + friend std::istream &operator>>(std::istream &in, Patient &patient); +}; + +#endif diff --git a/Visit.cpp b/Visit.cpp new file mode 100644 index 0000000..0f7bdca --- /dev/null +++ b/Visit.cpp @@ -0,0 +1,23 @@ +#include +#include +#include +#include + +#include "Visit.hpp" + +std::istream &operator>>(std::istream &in, Visit &visit) { + in >> visit.visit_id; + in >> visit.date; + in >> visit.doctor_first_name; + in >> visit.doctor_last_name; + return in; +} + +std::ostream &operator<<(std::ostream &out, Visit &visit) { + auto v = visit; + out << v.visit_id << " " // + << v.date << " " // + << v.doctor_first_name << " " // + << v.doctor_last_name; + return out; +} diff --git a/Visit.hpp b/Visit.hpp new file mode 100644 index 0000000..531615a --- /dev/null +++ b/Visit.hpp @@ -0,0 +1,35 @@ +#ifndef VISIT_HPP +#define VISIT_HPP + +#include +#include +#include +#include +#include + +class Visit { + // clang-format off +private: + u_int32_t visit_id{}; + u_int32_t patient_id{};//.txt + std::string date{}; //"dd/mm/yyyy" + std::string doctor_first_name{}; + std::string doctor_last_name{}; + + // clang-format on +public: + Visit() {} + Visit(u_int32_t visit_id, u_int32_t patient_id, std::string date, + std::string doctor_first_name, std::string doctor_last_name) + : visit_id(visit_id), patient_id(patient_id), date(date), + doctor_first_name(doctor_first_name), + doctor_last_name(doctor_last_name) {} + // clang-format off + // clang-format on + + friend std::istream &operator>>(std::istream &in, Visit &visit); + + friend std::ostream &operator<<(std::ostream &out, Visit &visit); +}; + +#endif diff --git a/linkedList.cpp b/linkedList.cpp new file mode 100644 index 0000000..2829d12 --- /dev/null +++ b/linkedList.cpp @@ -0,0 +1,201 @@ +#include +#include +#ifndef LINKED_LIST +#define LINKED_LIST + +using namespace std; + +struct node { + int data; + node *next; + node *previous; +}; + +class List { + // private: +public: + node *head{}; + node *tail{}; + +public: + // A default constructor, which creates an empty list + List() {} + // Not specified in the HW, but needed in my opinion + List(node *head, node *tail) : head(head), tail(tail) {} + // A parametrized constructor, which creates a list out of the elements of an + // array, keeping them in the same given order. + List(char *data, size_t n) { + for (int i{0}; i < n; i++) + appendList(data[i]); + } + // A copy constructor. + List(List const &list) { + auto h = list.head; + while (h != list.tail->next) { + appendList(h->data); + h = h->next; + } + } + + bool isEmptyList(); + std::ostream &printList(std::ostream &out); + void prependList(int); + void appendList(int); + bool findInList(int); + void pop(); + void popBack(); + void removeFromList(node *); + void removeFromList(int); + + friend List operator+(int const a, List const list) { + // copy + List newlist(list); + newlist.prependList(a); + return newlist; + } + friend List operator+(List const list, int a) { + // copy + List newlist(list); + newlist.appendList(a); + return newlist; + } + friend List operator+(List const list1, List const list2) { + // copies + List newlist1(list1); + node *h = list2.head; + while (h != list2.tail->next) { + newlist1.appendList(h->data); + h = h->next; + } + + return newlist1; + } + + node operator[](int i) { + auto h = head; + for (int j{0}; j < i; j++) + h = h->next; + return *h; + } +}; +std::ostream &operator<<(std::ostream &out, List list) { + return list.printList(out); +} + +bool List::isEmptyList() { + if (head == nullptr && tail == nullptr) + return true; + return false; +} +void List::appendList(int data) { + node *node1 = new node{data, nullptr, tail}; + if (head == nullptr && tail == nullptr) { + head = node1; + tail = node1; + } else { + tail->next = node1; + tail = node1; + } +} + +std::ostream &List::printList(std::ostream &out) { + if (isEmptyList()) { + out << "List is empty" << endl; + return out; + } + node *traveller = head; + while (traveller != tail) { + out << traveller->data << "=>"; + traveller = traveller->next; + } + out << tail->data << endl; + return out; +} + +void List::prependList(int data) { + node *node1 = new node{data, head, nullptr}; + if (head == nullptr && tail == nullptr) { + head = node1; + tail = node1; + } else { + head->previous = node1; + head = node1; + } +} + +bool List::findInList(int key) { + // returns a pointer to the first occurrence of the key in the list + node *traveller = head; + while (traveller != tail->next) { + if (traveller->data == key) { + // return traveller; + return true; + } + traveller = traveller->next; + } + return false; +} +void List::pop() { + if (isEmptyList()) + return; + if (head == tail) // one node + { + delete head; + head = nullptr; + tail = nullptr; + return; + } + node *firstNode = head; + head = head->next; + head->previous = nullptr; + delete firstNode; +} + +void List::popBack() { + if (isEmptyList()) + return; + if (head == tail) // one node + { + delete head; + head = nullptr; + tail = nullptr; + return; + } + node *lastNode = tail; + tail = tail->previous; + tail->next = nullptr; + delete lastNode; +} + +void List::removeFromList(node *node1) { + if (node1 == head) { + pop(); + return; + } + if (node1 == tail) { + popBack(); + return; + } + if (node1) { + node *tempPrev = node1->previous; + node *tempNext = node1->next; + tempPrev->next = tempNext; + tempNext->previous = tempPrev; + delete node1; + } +} + +void List::removeFromList(int key) { + node *traveller = head; + while (traveller != tail->next) { + if (traveller->data == key) { + removeFromList(traveller); + return; + } + traveller = traveller->next; + } + + // note that this deletes only the first occurrence of the key in the list +} + +#endif diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..46451bf --- /dev/null +++ b/main.cpp @@ -0,0 +1,73 @@ +#include "Clinic.cpp" +#include "Patient.hpp" +#include "linkedList.cpp" +#include +#include +#include +#include +#include +#include +#include + +int main() { + // this program implements the sieve of Erathosthenes using linked lists + // it does not use/test all the list functions + List primes{nullptr, nullptr}; + for (int i = 2; i <= 20; i++) { + primes.appendList(i); + } + node *p = primes.head; + while (p != primes.tail) { + int prime = p->data; + int tailData = primes.tail->data; + for (int composite = 2 * prime; composite <= tailData; composite += prime) { + primes.removeFromList( + composite); // if the composite is not in the list, nothing happens + } + p = p->next; + } + std::cout << primes; + + // testing copy + List primes_copy(primes); + std::cout << "primes: " << primes; + std::cout << "primes: " << primes_copy + 1; + std::cout << "primes+primes: " << primes_copy + primes_copy; + + // should return 5 + std::cout << "primes[2] = "; + std::cout << primes_copy[2].data << std::endl; + + return 0; +} + +// int main() { +// Clinic clinic("patients.txt"); +// std::cout << "Welcome to patient management\n\n"; + +// // run util main_menu recieves a non-exit value +// MenuChoice choice; +// Patient patient{}; +// std::string reading_string; +// std::string input_string; + +// while (1) { +// choice = clinic.menu_main(); +// switch (choice) { +// case MenuChoice::NewPatient: +// clinic.menu_add_new_patient(); +// break; +// case MenuChoice::NewVisit: +// clinic.menu_add_vist(); +// break; +// case MenuChoice::DisplayPatient: +// clinic.menu_patient_info(); +// break; +// case MenuChoice::Quit: +// return 0; +// break; +// } +// } + +// return 0; +// } diff --git a/patient_files/1.txt b/patient_files/1.txt new file mode 100644 index 0000000..4b8790c --- /dev/null +++ b/patient_files/1.txt @@ -0,0 +1,9 @@ +1 +Joe +Doe +M +18 +1 11/12/103 Mike WWW +1 11/12/103 Mike WWW +1 11/12/103 Mike WWW +77 /88/9999 Doctr Last diff --git a/patient_files/6.txt b/patient_files/6.txt new file mode 100644 index 0000000..5bcc488 --- /dev/null +++ b/patient_files/6.txt @@ -0,0 +1,6 @@ +6 +Karim +Alaswad +M +14 +16 12/1/2018 Helen Philips diff --git a/patients.txt b/patients.txt new file mode 100644 index 0000000..9364ec4 --- /dev/null +++ b/patients.txt @@ -0,0 +1,3 @@ +1 +3 +4