Skip to content

Commit b6aaaf8

Browse files
committed
Implement BitcoinExchange and RPN classes; add input processing and error handling; create PmergeMe for sorting; include test script and Makefile updates.
1 parent 6089b22 commit b6aaaf8

File tree

17 files changed

+576
-91
lines changed

17 files changed

+576
-91
lines changed

09/ex00/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
btc

09/ex00/BitcoinExchange.cpp

Lines changed: 113 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -2,70 +2,137 @@
22

33
#include <fstream>
44
#include <iostream>
5-
#include <istream>
65
#include <sstream>
7-
#include <stdexcept>
86

9-
BitcoinExchange::BitcoinExchange() {
10-
// Constructor implementation
11-
}
7+
BitcoinExchange::BitcoinExchange() {}
8+
9+
BitcoinExchange::BitcoinExchange(const BitcoinExchange& src) { *this = src; }
1210

13-
BitcoinExchange::~BitcoinExchange() {
14-
// Destructor implementation
11+
BitcoinExchange& BitcoinExchange::operator=(const BitcoinExchange& rhs) {
12+
if (this != &rhs) {
13+
this->_data = rhs._data;
14+
}
15+
return *this;
1516
}
1617

17-
BitcoinExchange::BitcoinExchange(const BitcoinExchange &other) : data(other.data) {
18-
// Copy constructor implementation
18+
BitcoinExchange::~BitcoinExchange() {}
19+
20+
BitcoinExchange::BitcoinExchange(const std::string& dbPath) {
21+
std::ifstream dbFile(dbPath.c_str());
22+
if (!dbFile.is_open()) {
23+
throw std::runtime_error("Error: could not open database file.");
24+
}
25+
26+
std::string line;
27+
std::getline(dbFile, line);
28+
29+
if (line != "date,exchange_rate") {
30+
std::cerr << "Error: bad database format." << std::endl;
31+
}
32+
33+
while (std::getline(dbFile, line)) {
34+
std::stringstream ss(line);
35+
std::string date;
36+
std::string rateStr;
37+
float rate;
38+
39+
if (std::getline(ss, date, ',') && std::getline(ss, rateStr)) {
40+
char* end;
41+
rate = std::strtof(rateStr.c_str(), &end);
42+
if (*end == '\0') this->_data[date] = rate;
43+
}
44+
}
1945
}
2046

21-
BitcoinExchange &BitcoinExchange::operator=(const BitcoinExchange &other) {
22-
if (this != &other) {
23-
data = other.data;
47+
bool BitcoinExchange::_isValidDate(const std::string& date) {
48+
if (date.length() != 10) return false;
49+
if (date[4] != '-' || date[7] != '-') return false;
50+
51+
for (int i = 0; i < 10; ++i) {
52+
if (i == 4 || i == 7) continue;
53+
if (!std::isdigit(date[i])) return false;
2454
}
25-
return *this;
55+
56+
int year = std::atoi(date.substr(0, 4).c_str());
57+
int month = std::atoi(date.substr(5, 2).c_str());
58+
int day = std::atoi(date.substr(8, 2).c_str());
59+
60+
if (year < 2009 || month < 1 || month > 12 || day < 1 || day > 31) {
61+
return false;
62+
}
63+
64+
return true;
2665
}
2766

28-
void BitcoinExchange::parseLine(const std::string &line) {
29-
std::istringstream stream(line);
30-
std::string date, valueStr;
31-
if (std::getline(stream, date, ',') && std::getline(stream, valueStr)) {
32-
float value = std::stof(valueStr);
33-
validateDate(date);
34-
validateValue(value);
35-
addValue(date, value);
67+
bool BitcoinExchange::_isValidValue(const std::string& valueStr, float& value) {
68+
char* end;
69+
value = std::strtof(valueStr.c_str(), &end);
70+
71+
while (*end != '\0' && std::isspace(*end)) {
72+
end++;
73+
}
74+
if (*end != '\0') return false; // Check if the entire string was consumed
75+
76+
if (value < 0) {
77+
std::cout << "Error: not a positive number." << std::endl;
78+
return false;
3679
}
80+
if (value > 1000) {
81+
std::cout << "Error: too large a number." << std::endl;
82+
return false;
83+
}
84+
return true;
3785
}
3886

39-
/**
40-
* @brief When loading data from the data.csv file, we need to do ',' splitting.
41-
* This is not the same as the splitting in the input.txt file, which uses '|' as a separator.
42-
*
43-
* @param filename
44-
* @param splitChars
45-
*/
46-
void BitcoinExchange::loadData(const std::string &filename, const std::string &splitChars) {
47-
std::ifstream file(filename);
87+
void BitcoinExchange::processInputFile(const std::string& inputPath) {
88+
std::ifstream inputFile(inputPath.c_str());
89+
if (!inputFile.is_open()) {
90+
std::cout << "Error: could not open file." << std::endl;
91+
return;
92+
}
93+
4894
std::string line;
95+
std::getline(inputFile, line);
4996

50-
if (!file.is_open()) {
51-
throw std::runtime_error("Could not open file: " + filename);
97+
if (line != "date | value") {
98+
std::cout << "Error: bad input => " << line << std::endl;
99+
return;
52100
}
53101

54-
// check the first line (either "date, value" or "date | value")
55-
if (std::getline(file, line)) {
56-
if (line.find(',') != std::string::npos) {
57-
// If the line contains a comma, we assume it's a CSV format
58-
while (std::getline(file, line)) {
59-
parseLine(line);
60-
}
61-
} else if (line.find('|') != std::string::npos) {
62-
// If the line contains a pipe, we assume it's a different format
63-
while (std::getline(file, line)) {
64-
std::replace(line.begin(), line.end(), '|', ',');
65-
parseLine(line);
102+
while (std::getline(inputFile, line)) {
103+
std::stringstream ss(line);
104+
std::string date;
105+
std::string separator;
106+
std::string valueStr;
107+
108+
ss >> date >> separator >> valueStr;
109+
110+
if (date.empty() || separator != "|" || valueStr.empty()) {
111+
std::cout << "Error: bad input => " << line << std::endl;
112+
continue;
113+
}
114+
115+
float value;
116+
if (!_isValidDate(date)) {
117+
std::cout << "Error: bad input => " << date << std::endl;
118+
continue;
119+
}
120+
if (!_isValidValue(valueStr, value)) {
121+
continue;
122+
}
123+
124+
// Find the exchange rate
125+
std::map<std::string, float>::iterator it = _data.lower_bound(date);
126+
127+
if (it == _data.end() || it->first != date) {
128+
if (it == _data.begin()) {
129+
std::cout << "Error: no data available for or before " << date << std::endl;
130+
continue;
66131
}
67-
} else {
68-
throw std::runtime_error("Unknown file format in: " + filename);
132+
--it; // Decrement to get the closest earlier date
69133
}
134+
135+
float exchangeRate = it->second;
136+
std::cout << date << " => " << value << " = " << value * exchangeRate << std::endl;
70137
}
71138
}

09/ex00/BitcoinExchange.hpp

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,22 @@
11
#pragma once
22

3+
#include <cstdlib>
34
#include <map>
45
#include <string>
56

67
class BitcoinExchange {
8+
private:
9+
std::map<std::string, float> _data;
10+
bool _isValidDate(const std::string& date);
11+
bool _isValidValue(const std::string& valueStr, float& value);
12+
713
public:
814
BitcoinExchange();
15+
BitcoinExchange(const BitcoinExchange& src);
16+
BitcoinExchange& operator=(const BitcoinExchange& rhs);
917
~BitcoinExchange();
10-
BitcoinExchange(const BitcoinExchange &other);
11-
BitcoinExchange &operator=(const BitcoinExchange &other);
1218

13-
void loadData(const std::string &filename, const std::string &splitChars);
14-
float getValue(const std::string &date) const;
15-
void printAllValues() const;
16-
void printValue(const std::string &date) const;
17-
void addValue(const std::string &date, float value);
18-
void removeValue(const std::string &date);
19-
void clearData();
20-
bool isValidValue(float value) const;
21-
std::map<std::string, float> getData() const;
22-
23-
private:
24-
std::map<std::string, float> data;
19+
BitcoinExchange(const std::string& dbPath);
2520

26-
void parseLine(const std::string &line);
27-
void validateDate(const std::string &date) const;
28-
void validateValue(float value) const;
21+
void processInputFile(const std::string& inputPath);
2922
};

09/ex00/Makefile

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
NAME = mutantstack
2-
SRC = main.cpp
3-
HEADERS = MutantStack.hpp
1+
NAME = btc
2+
SRC = main.cpp BitcoinExchange.cpp
3+
HEADERS = BitcoinExchange.hpp
44
BUILD_DIR = build
55
BUILD = $(addprefix $(BUILD_DIR)/, $(SRC:.cpp=.o))
66

77
all: $(NAME)
88

99
$(NAME): $(BUILD_DIR) $(BUILD)
10-
@c++ $(BUILD) -o $(NAME)
10+
@c++ -Werror -Wall -Wextra $(BUILD) -o $(NAME)
1111

1212
$(BUILD_DIR)/%.o: %.cpp | $(BUILD_DIR)
13-
@c++ -Werror -Wall -Wextra -Wno-implicit-fallthrough -std=c++23 -c $< -o $@
13+
@c++ -Werror -Wall -Wextra -Wno-implicit-fallthrough -std=c++17 -c $< -o $@
1414

1515
$(BUILD_DIR):
1616
@mkdir -p $(BUILD_DIR)
@@ -27,7 +27,11 @@ format:
2727
@clang-format -i $(SRC) $(HEADERS)
2828

2929
run: all
30-
@printf "\n🤖 08/ex02 $(NAME) output:\n\n"
31-
@./$(NAME)
30+
@printf "\n🤖 09/ex00 $(NAME) output:\n\n"
31+
@printf "\nDefault input file: input.txt\n\n"
32+
@./$(NAME) input.txt
33+
@printf "\nCustom input file: correct_input.txt\n\n"
34+
@./$(NAME) correct_input.txt
35+
36+
.PHONY: all clean fclean re format run
3237

33-
.PHONY: all clean fclean re

09/ex00/correct_input.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
date | value
2+
2011-01-03 | 3
3+
2011-01-03 | 2
4+
2011-01-03 | 1
5+
2011-01-03 | 1.2
6+
2011-01-09 | 1
7+
2012-01-11 | 1

09/ex00/main.cpp

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,19 @@
22

33
#include "BitcoinExchange.hpp"
44

5-
int main(int argc, const char** argv) {
6-
(void)argc;
7-
(void)argv;
5+
int main(int argc, char** argv) {
6+
if (argc != 2) {
7+
std::cerr << "Error: could not open file." << std::endl;
8+
return 1;
9+
}
10+
11+
try {
12+
BitcoinExchange btc("data.csv");
13+
btc.processInputFile(argv[1]);
14+
} catch (const std::exception& e) {
15+
std::cerr << e.what() << std::endl;
16+
return 1;
17+
}
18+
819
return 0;
9-
}
20+
}

09/ex01/.gitignore

Whitespace-only changes.

09/ex01/Makefile

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
NAME = mutantstack
2-
SRC = main.cpp
3-
HEADERS = MutantStack.hpp
1+
NAME = RPN
2+
SRC = main.cpp RPN.cpp
3+
HEADERS = RPN.hpp
44
BUILD_DIR = build
55
BUILD = $(addprefix $(BUILD_DIR)/, $(SRC:.cpp=.o))
66

77
all: $(NAME)
88

99
$(NAME): $(BUILD_DIR) $(BUILD)
10-
@c++ $(BUILD) -o $(NAME)
10+
@c++ -Werror -Wall -Wextra $(BUILD) -o $(NAME)
1111

1212
$(BUILD_DIR)/%.o: %.cpp | $(BUILD_DIR)
13-
@c++ -Werror -Wall -Wextra -Wno-implicit-fallthrough -std=c++23 -c $< -o $@
13+
@c++ -Werror -Wall -Wextra -Wno-implicit-fallthrough -std=c++17 -c $< -o $@
1414

1515
$(BUILD_DIR):
1616
@mkdir -p $(BUILD_DIR)
@@ -27,7 +27,14 @@ format:
2727
@clang-format -i $(SRC) $(HEADERS)
2828

2929
run: all
30-
@printf "\n🤖 08/ex02 $(NAME) output:\n\n"
31-
@./$(NAME)
32-
33-
.PHONY: all clean fclean re
30+
@printf "\n🤖 09/ex00 $(NAME) output:\n\n"
31+
@printf "\nTest: \"8 9 * 9 - 9 - 9 - 4 - 1 +\"\n"
32+
@./$(NAME) "8 9 * 9 - 9 - 9 - 4 - 1 +"
33+
@printf "\nTest: \"7 7 * 7 -\"\n"
34+
@./$(NAME) "7 7 * 7 -"
35+
@printf "\nTest: \"1 2 * 2 / 2 * 2 4 - +\"\n"
36+
@./$(NAME) "1 2 * 2 / 2 * 2 4 - +"
37+
@printf "\nTest: \"(1 + 1)\"\n"
38+
@./$(NAME) "(1 + 1)"
39+
40+
.PHONY: all clean fclean re format run

09/ex01/RPN.cpp

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
#include "RPN.hpp"
2+
3+
#include <iostream>
4+
#include <sstream>
5+
#include <stack>
6+
7+
void RPN::evaluate(const std::string& expression) {
8+
std::stack<int> stack;
9+
std::stringstream ss(expression);
10+
std::string token;
11+
12+
while (ss >> token) {
13+
if (token.length() == 1 && std::isdigit(token[0])) {
14+
stack.push(std::atoi(token.c_str()));
15+
} else if (token.length() == 1 && (token[0] == '+' || token[0] == '-' || token[0] == '*' || token[0] == '/')) {
16+
if (stack.size() < 2) {
17+
std::cerr << "Error: insufficient arguments" << std::endl;
18+
return;
19+
}
20+
int b = stack.top();
21+
stack.pop();
22+
int a = stack.top();
23+
stack.pop();
24+
25+
if (token[0] == '+') {
26+
stack.push(a + b);
27+
} else if (token[0] == '-') {
28+
stack.push(a - b);
29+
} else if (token[0] == '*') {
30+
stack.push(a * b);
31+
} else if (token[0] == '/') {
32+
if (b == 0) {
33+
std::cerr << "Error: division by zero!" << std::endl;
34+
return;
35+
}
36+
stack.push(a / b);
37+
}
38+
} else {
39+
std::cerr << "Error: invalid token" << std::endl;
40+
return;
41+
}
42+
}
43+
44+
if (stack.size() != 1) {
45+
std::cerr << "Error: incorrect amont of operands" << std::endl;
46+
return;
47+
}
48+
49+
std::cout << stack.top() << std::endl;
50+
}

0 commit comments

Comments
 (0)