#include #include #include #include #include "largest.h" struct NegativePriceException { double wrong_value; }; // Uma classe para representar precos. // Serve para teste de uso de tipos definidos pelo usuario. class Price { int _cents; // Guarda o numero de centavos. public: // Valor inicial em centavos (default = 0). // Ao inicializar, lanca excecao se o preco for negativo. Price(int value_in_cents = 0) : _cents(value_in_cents) { if (_cents < 0) { throw NegativePriceException{_cents/100.0}; } } // Valor inicial em dolares. // Simplesmente calcula centavos e usa o construtor de centavos. Price(double value_in_dollars) : Price(static_cast(round(100 * value_in_dollars))) { } // Retorna o preco em dolares. double dollars() const { return _cents/100.0; } // Retorna o preco em centavos. double cents() const { return _cents; } // Operador de comparacao (menor). friend bool operator<(Price const &a, Price const &b) { return a._cents < b._cents; } // Operador de igualdade. friend bool operator==(Price const &a, Price const &b) { return a._cents == b._cents; } // Operador de insercao em stream de saida. friend std::ostream &operator<<(std::ostream &os, Price const &p) { os << p.dollars(); return os; } }; // Uma funcao (template) para inserir os maiores valores registrados // em uma stream de saida. template void show(std::ostream &os, Largest const &largest) { auto values = largest.retrieve(); if (values.size() > 0) { for (auto v: values) { os << v << " "; } } else { os << ""; } } // Testa insercao de alguns elementos. // Tambem testa duplicacao. void test_initial() { // Cria container para 3 valores. Largest li(3); // Verifica que inicialmente esta vazio. if (auto v = li.retrieve(); v.size() != 0) { std::cerr << "Should be empty on creation.\n"; } else { std::cout << "Created empty, as expected.\n"; } // Insere um elemento e testa. li.put(1); if (auto v = li.retrieve(); v.size() != 1) { std::cerr << "Wrong size after one insertion.\n"; } else { if (v[0] != 1) { std::cerr << "Wrong value after one insertion.\n"; } else { std::cout << "One insertion passed.\n"; } } // Insere o segundo elemento e testa. li.put(2); if (auto v = li.retrieve(); v.size() != 2) { std::cerr << "Wrong size after two insertions.\n" << v.size() << std::endl; } else { if (v[0] != 2 || v[1] != 1) { std::cerr << "Wrong values after two insertions.\n"; } else { std::cout << "Two insertions passed.\n"; } } // Insere elemento duplicado e testa. li.put(2); if (auto v = li.retrieve(); v.size() != 2) { std::cerr << "Wrong size after duplicate insertion.\n"; } else { if (v[0] != 2 || v[1] != 1) { std::cerr << "Wrong values after duplicate insertion.\n"; } else { std::cout << "Duplicate insertion passed.\n"; } } // Insere outro elemento duplicado e testa. li.put(1); if (auto v = li.retrieve(); v.size() != 2) { std::cerr << "Wrong size after other duplicate insertion.\n"; } else { if (v[0] != 2 || v[1] != 1) { std::cerr << "Wrong values after other duplicate insertion.\n"; } else { std::cout << "Other duplicate insertion passed.\n"; } } } // Testa o funcionamento para int. void test_integers() { // Cria container com 3 elementos. Largest li(3); // Insere elementos da lista, em ordem. std::vector vi{1, 4, 7, 3, 2, 0, -5, 0, 2, 1, 8, 4}; for (auto x: vi) { li.put(x); } // Verifica com o resultado esperado. std::vector expected_values{8, 7, 4}; if (auto v = li.retrieve(); v != expected_values) { std::cerr << "Returned values "; show(std::cerr, li); std::cerr << " not as expected.\n"; } else { std::cout << "Integer values match.\n"; } } // Testa o funcionamento para double. void test_doubles() { // Cria um container para 5 elementos. Largest ld(5); // Insere elementos da lista, em ordem. std::vector vd{0.0, 0.0, 0.0, 1.1, 2.2, 3.3, 3.2, 2.1, 1.0, 1.1, -4.7, 10.10, 20.20, -100.0}; for (auto x: vd) { ld.put(x); } // Verifica resultado esperado. std::vector expected_double_values{20.2, 10.1, 3.3, 3.2, 2.2}; if (auto v = ld.retrieve(); v != expected_double_values) { std::cerr << "Returned values "; show(std::cerr, ld); std::cerr << " not as expected.\n"; } else { std::cout << "Double values match.\n"; } } // Testa o funcionamento para Price. void test_prices() { try { // Cria container para 4 valores. Largest lp(4); // Insere elementos da lista, em ordem. std::vector vp{Price{}, Price{1000}, Price{12.30}, Price{14.75}, Price{2142}, Price{35.07}, Price{1475}}; for (auto p: vp) { lp.put(p); } // Verifica resultado esperado. std::vector expected_price_values{Price{3507}, Price{21.42}, Price{14.75}, Price{1230}}; if (auto v = lp.retrieve(); v != expected_price_values) { std::cerr << "Returned values "; show(std::cerr, lp); std::cerr << " not as expected.\n"; } else { std::cout << "Price values match.\n"; } } catch (NegativePriceException e) { std::cerr << "Wrong value for a price: " << e.wrong_value << std::endl; } } // Testa insercao de muitos numeros aleatorios. void test_random() { // Cria container para 1000 maiores valores. Largest li(1000); // Prepara para gerar numeros aleatorios. std::random_device rd; std::default_random_engine eng(rd()); // Usa uma distribuicao uniforme de 0 a 1.000.000 std::uniform_int_distribution dist(0, 1'000'000); // Gera 10.000.000 de numeros aleatorios e insere em li. for (int i = 0; i < 10'000'000; ++i) { li.put(dist(eng)); } // Agora verifica se nao tem duplicaco e esta em ordem. auto values = li.retrieve(); for (size_t i = 0; i < values.size()-1; ++i) { if (values[i] <= values[i+1]) { // Erro! std::cerr << "Wrong order in random insertion.\n"; return; } } std::cout << "Random insertion passed.\n"; } // O programa principal roda alguns testes. int main(int, char const *[]) { // Roda os diversos testes. test_initial(); test_integers(); test_doubles(); test_prices(); test_random(); return 0; }