306 lines
8.3 KiB
C++
306 lines
8.3 KiB
C++
/* ************************************************************************** */
|
||
/* */
|
||
/* ::: :::::::: */
|
||
/* BitcoinExchange.cpp :+: :+: :+: */
|
||
/* +:+ +:+ +:+ */
|
||
/* By: adjoly <adjoly@student.42angouleme.fr> +#+ +:+ +#+ */
|
||
/* +#+#+#+#+#+ +#+ */
|
||
/* Created: 2025/05/21 10:39:45 by adjoly #+# #+# */
|
||
/* Updated: 2025/06/03 11:40:22 by adjoly ### ########.fr */
|
||
/* */
|
||
/* ************************************************************************** */
|
||
|
||
#include <BitcoinExchange.hpp>
|
||
#include <algorithm>
|
||
#include <cctype>
|
||
#include <cstddef>
|
||
#include <cstdio>
|
||
#include <cstdlib>
|
||
#include <exception>
|
||
#include <fstream>
|
||
#include <functional>
|
||
#include <ostream>
|
||
#include <sstream>
|
||
#include <stdexcept>
|
||
#include <string>
|
||
#include <utility>
|
||
|
||
const uint32_t Date::_d_in_m[12] = {31, 29, 31, 30, 31, 30,
|
||
31, 31, 30, 31, 30, 31};
|
||
|
||
void _log(std::string emoji, std::string what, std::string who,
|
||
std::string str) {
|
||
#ifdef VERBOSE
|
||
if (who.empty())
|
||
std::cout << "「" << emoji << "」" << what << ": " << str << std::endl;
|
||
else
|
||
std::cout << "「" << emoji << "」" << what << "(" << who << "): " << str
|
||
<< std::endl;
|
||
#else
|
||
(void)emoji, (void)what, (void)who, (void)str;
|
||
#endif
|
||
}
|
||
|
||
bool Date::_isMonthValid(uint16_t month) const {
|
||
if (month >= 1 && month <= 12)
|
||
return true;
|
||
return false;
|
||
}
|
||
|
||
bool Date::_isDayValid(uint16_t day, uint16_t month) const {
|
||
if (day >= 1 && day <= _d_in_m[month - 1])
|
||
return true;
|
||
return false;
|
||
}
|
||
|
||
Date::Date(void) : _year(0), _month(1), _day(1) {
|
||
_log("➕", "Date", "", "default constructor called");
|
||
}
|
||
|
||
Date::Date(std::string date) {
|
||
_log("➕", "Date", "", "string constructor called");
|
||
std::istringstream iss(date);
|
||
char del1, del2;
|
||
uint32_t year;
|
||
uint16_t month, day;
|
||
|
||
if (iss >> year >> del1 >> month >> del2 >> day) {
|
||
if (del1 == '-' && del2 == '-' && iss.eof()) {
|
||
if (_isMonthValid(month) && _isDayValid(day, month)) {
|
||
_year = year;
|
||
_month = month;
|
||
_day = day;
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
throw std::runtime_error("bad input => " + date);
|
||
}
|
||
|
||
Date::Date(const Date &cpy)
|
||
: _year(cpy._year), _month(cpy._month), _day(cpy._day) {
|
||
_log("➕", "Date", "", "copy constructor called");
|
||
}
|
||
|
||
Date::~Date(void) { _log("➖", "Date", "", "destructor called"); }
|
||
|
||
Date &Date::operator=(const Date &cpy) {
|
||
_log("➕", "Date", "", "copy assignement constructor called");
|
||
if (&cpy != this) {
|
||
_year = cpy._year;
|
||
_month = cpy._month;
|
||
_day = cpy._day;
|
||
}
|
||
return *this;
|
||
}
|
||
|
||
uint32_t Date::getYear(void) const { return _year; }
|
||
uint16_t Date::getMonth(void) const { return _month; }
|
||
uint16_t Date::getDay(void) const { return _day; }
|
||
|
||
bool Date::operator==(const Date &other) const {
|
||
return other._year == _year && other._month == _month && other._day == _day;
|
||
}
|
||
|
||
bool Date::operator!=(const Date &other) const {
|
||
return other._year != _year || other._month != _month || other._day != _day;
|
||
}
|
||
|
||
bool Date::operator<(const Date &other) const {
|
||
if (_year != other._year)
|
||
return _year < other._year;
|
||
if (_month != other._month)
|
||
return _month < other._month;
|
||
return _day < other._day;
|
||
}
|
||
|
||
bool Date::operator>(const Date &other) const {
|
||
if (_year != other._year)
|
||
return _year > other._year;
|
||
if (_month != other._month)
|
||
return _month > other._month;
|
||
return _day > other._day;
|
||
}
|
||
|
||
bool Date::operator<=(const Date &other) const {
|
||
return (*this) < other || (*this) == other;
|
||
}
|
||
|
||
bool Date::operator>=(const Date &other) const {
|
||
return (*this) > other || (*this) == other;
|
||
}
|
||
|
||
std::ostream &operator<<(std::ostream &os, const Date &date) {
|
||
std::stringstream month;
|
||
std::stringstream day;
|
||
if (date.getMonth() < 10)
|
||
month << "0" << date.getMonth();
|
||
else
|
||
month << date.getMonth();
|
||
if (date.getDay() < 10)
|
||
day << "0" << date.getDay();
|
||
else
|
||
day << date.getDay();
|
||
|
||
os << date.getYear() << "-" << month.str() << "-" << day.str();
|
||
return os;
|
||
}
|
||
|
||
CsvParser::CsvParser(void) : _is("data.csv") {
|
||
_log("➕", "CsvParser", "", "default constructor called");
|
||
|
||
if (_is.is_open() != true)
|
||
throw std::runtime_error("could not open database file");
|
||
std::string buf;
|
||
|
||
std::getline(_is, buf);
|
||
if (buf != "date,exchange_rate")
|
||
throw std::runtime_error("first line not good");
|
||
|
||
std::getline(_is, buf);
|
||
while (buf != "") {
|
||
size_t i = buf.find(",");
|
||
if (i == std::string::npos)
|
||
throw std::runtime_error("did not found ,");
|
||
Date date(buf.substr(0, i));
|
||
float nb;
|
||
if (buf.substr(i + 1, buf.length()) == "0")
|
||
nb = 0;
|
||
else {
|
||
nb = std::atof(buf.substr(i + 1, buf.length()).c_str());
|
||
if (nb == 0)
|
||
throw std::runtime_error("number not well formatted - " +
|
||
buf.substr(i + 1, buf.length()));
|
||
}
|
||
_csv.insert(std::make_pair(date, nb));
|
||
std::getline(_is, buf);
|
||
}
|
||
}
|
||
|
||
CsvParser::CsvParser(const CsvParser &cpy) : _is(cpy._filename.c_str()) {
|
||
_log("➕", "CsvParser", "", "copy constructor called");
|
||
if (&cpy != this) {
|
||
_filename = cpy._filename;
|
||
_csv = cpy._csv;
|
||
}
|
||
}
|
||
|
||
CsvParser::~CsvParser(void) { _log("➖", "CsvParser", "", "destructor called"); }
|
||
|
||
CsvParser &CsvParser::operator=(const CsvParser &cpy) {
|
||
_log("➕", "CsvParser", "", "copy assignement constructor called");
|
||
if (&cpy != this) {
|
||
_csv = cpy._csv;
|
||
_filename = cpy._filename;
|
||
}
|
||
return *this;
|
||
}
|
||
|
||
std::map<Date, float> CsvParser::getCsv(void) const { return _csv; }
|
||
|
||
BitcoinExchange::BitcoinExchange(void)
|
||
: _is("input.txt"), _filename("input.txt") {
|
||
_log("➕", "BitcoinExchange", "", "default constructor called");
|
||
if (!_is.is_open())
|
||
throw std::runtime_error("could not open file - " + _filename);
|
||
}
|
||
|
||
BitcoinExchange::BitcoinExchange(char *av) : _is(av), _filename(av) {
|
||
_log("➕", "BitcoinExchange", "", "file name constructor called");
|
||
try {
|
||
_csv = new CsvParser();
|
||
} catch (std::exception &e) {
|
||
throw e;
|
||
}
|
||
|
||
if (!_is.is_open())
|
||
throw std::runtime_error("could not open file - " + _filename);
|
||
|
||
std::string buf;
|
||
|
||
std::getline(_is, buf);
|
||
if (buf != "date | value")
|
||
throw std::runtime_error("file has not the good format - " + buf);
|
||
}
|
||
|
||
BitcoinExchange::BitcoinExchange(const BitcoinExchange &) {
|
||
_log("➕", "BitcoinExchange", "", "copy constructor called");
|
||
}
|
||
|
||
BitcoinExchange &BitcoinExchange::operator=(const BitcoinExchange &) {
|
||
_log("➕", "BitcoinExchange", "", "copy assignement constructor called");
|
||
|
||
return *this;
|
||
}
|
||
|
||
BitcoinExchange::~BitcoinExchange(void) {
|
||
_log("➖", "BitcoinExchange", "", "destructor called");
|
||
delete _csv;
|
||
}
|
||
|
||
void BitcoinExchange::print(void) {
|
||
std::pair<Date, float> pair;
|
||
std::getline(_is, _line);
|
||
while (!_line.empty()) {
|
||
try {
|
||
pair = _parseLine();
|
||
} catch (std::exception &e) {
|
||
std::cerr << "Error: " << e.what() << std::endl;
|
||
std::getline(_is, _line);
|
||
continue;
|
||
}
|
||
_printPair(pair);
|
||
std::getline(_is, _line);
|
||
}
|
||
}
|
||
|
||
std::pair<Date, float> BitcoinExchange::_parseLine(void) {
|
||
std::pair<Date, float> pair;
|
||
std::string buf;
|
||
size_t i;
|
||
|
||
for (i = 0;
|
||
i < _line.length() && (std::isdigit(_line[i]) || _line[i] == '-');
|
||
i++) {
|
||
buf.push_back(_line[i]);
|
||
}
|
||
try {
|
||
pair.first = Date(buf);
|
||
} catch (std::exception &e) {
|
||
throw std::runtime_error("bad input => " + _line);
|
||
}
|
||
if (_line[i] != ' ' || _line[i + 1] != '|' || _line[i + 2] != ' ') {
|
||
throw std::runtime_error("bad input => " + _line);
|
||
}
|
||
if (_line.substr(i + 3) == "0") {
|
||
pair.second = 0;
|
||
} else {
|
||
float f = std::atof(_line.substr(i + 3).c_str());
|
||
if (f == 0) {
|
||
throw std::runtime_error("bad input => " + _line);
|
||
} else if (f > 1000)
|
||
throw std::runtime_error("too large number.");
|
||
else if (f < 0)
|
||
throw std::runtime_error("not a positive number.");
|
||
pair.second = f;
|
||
}
|
||
|
||
return pair;
|
||
}
|
||
|
||
void BitcoinExchange::_printPair(std::pair<Date, float> pair) {
|
||
auto map = _csv->getCsv();
|
||
auto it = std::find_if(map.begin(), map.end(), CompareDate(pair.first));
|
||
if (it == map.end())
|
||
throw std::out_of_range("out of range");
|
||
|
||
float i = it->second * pair.second;
|
||
std::cout << pair.first << " => " << pair.second << " = " << i << std::endl;
|
||
}
|
||
|
||
CompareDate::CompareDate(Date date) : _date(date) {}
|
||
|
||
bool CompareDate::operator()(const std::pair<Date, float> &pair) const {
|
||
return pair.first >= _date;
|
||
}
|