- #include <iostream> 
- 	#include <sstream> 
- 	#include <memory> 
- 	#include <map> 
- 	#include <vector> 
-   
- 	struct Item 
- 	{ 
- 		public: 
- 		std::string id; 
- 		std::string name; 
- 		double price; 
-   
- 		Item(const std::string& id, const std::string& name, double price) : id(id), name(name), price(price) {} 
-   
- 		//just to be able to put it in a map 
- 		bool operator<(const Item& b) const 
- 		{ 
- 			return price < b.price; 
- 		} 
- 		friend std::ostream& operator<<(std::ostream& out, const Item& item); 
- 	}; 
-   
- 	std::ostream& operator<<(std::ostream& out, const Item& item) 
- 	{ 
- 		return out << '[' << item.id << "] " << item.name << " (" << item.price << " $)"; 
- 	} 
-   
- 	//indexing the tours by their respective IDs 
- 	const std::map<std::string, Item> tours { 
- 		{"OH", Item("OH", "Opera House Tour", 300.0)}, 
- 		{"BC", Item("BC", "Syndney Bridge Climb", 110.0)}, 
- 		{"SK", Item("SK", "Syndney Sky Tower", 30.0)}, 
- 	}; 
-   
- 	class Rule 
- 	{ 
- 		public: 
- 		virtual std::map<Item, int> applyDiscount(std::map<Item, int> items) = 0; 
- 	}; 
-   
- 	class FreeSkyTourRule : public Rule 
- 	{ 
- 		std::map<Item, int> applyDiscount(std::map<Item, int> items) 
- 		{ 
- 			auto oh = items.find(tours.at("OH")); 
- 			if(oh == items.end()) 
- 				return items; 
- 			auto sk = items.find(tours.at("SK")); 
- 			if(sk == items.end()) 
- 				return items; 
- 			Item discount("DC", "Free Sky Discount", -sk->first.price); 
- 			items[discount] = sk->second > oh->second ? oh->second : sk->second; 
- 			return items; 
- 		} 
- 	}; 
-   
- 	class ThreeForTwoRule : public Rule 
- 	{ 
- 		std::map<Item, int> applyDiscount(std::map<Item, int> items) 
- 		{ 
- 			auto oh = items.find(tours.at("OH")); 
- 			if(oh == items.end()) 
- 				return items; 
- 			int quantity = oh->second; 
- 			Item discount("DC", "Opera House Discount", -oh->first.price); 
- 			if(quantity > 2) 
- 				items[discount] = oh->second / 3; 
- 			return items; 
- 		} 
- 	}; 
-   
- 	class BridgeClimbDiscountRule : public Rule 
- 	{ 
- 		std::map<Item, int> applyDiscount(std::map<Item, int> items) 
- 		{ 
- 			auto bc = items.find(tours.at("BC")); 
- 			if(bc == items.end()) 
- 				return items; 
- 			int quantity = bc->second; 
- 			if(quantity > 4) 
- 			{ 
- 				Item discount("DC", "Bridge Climb Discount", -20.0); 
- 				items[discount] = quantity; 
- 			} 
- 			return items; 
- 		} 
- 	}; 
-   
- 	class ShoppingCart 
- 	{ 
- 		private: 
- 		std::map<Item, int> items; 
- 		std::vector<std::unique_ptr<Rule>> rules; 
-   
- 		public: 
- 		void addItem(Item item) 
- 		{ 
- 			items[item]++; 
- 		} 
-   
- 		void addRule(std::unique_ptr<Rule> rule) 
- 		{ 
- 			rules.push_back(std::move(rule)); 
- 		} 
-   
- 		std::map<Item, int> getBill() const 
- 		{ 
- 			std::map<Item, int> bill = items; 
- 			for(auto& rule: rules) 
- 				bill = rule->applyDiscount(bill); 
- 			return bill; 
- 		} 
-   
- 		double total(const std::map<Item, int>& bill) const 
- 		{ 
- 			double sum = 0; 
- 			for(auto& kv: bill) 
- 				sum += kv.first.price * kv.second; 
- 			return sum; 
- 		} 
-   
- 		double total() const 
- 		{ 
- 			return total(getBill()); 
- 		} 
- 	}; 
-   
- 	int main() 
- 	{ 
- 		std::string line; 
- 		while(getline(std::cin, line)) 
- 		{ 
- 			std::string entry; 
- 			std::stringstream ss(line); 
- 			ShoppingCart cart; 
- 			while(ss >> entry) 
- 			{ 
- 				cart.addItem(tours.at(entry)); 
- 			} 
- 			cart.addRule(std::unique_ptr<Rule>(new BridgeClimbDiscountRule)); 
- 			cart.addRule(std::unique_ptr<Rule>(new ThreeForTwoRule)); 
- 			cart.addRule(std::unique_ptr<Rule>(new FreeSkyTourRule)); 
- 			std::cout << line << std::endl; 
- 			auto bill = cart.getBill(); 
- 			for(auto& kv: bill) 
- 				std::cout << "\tx" << kv.second << " : " << kv.first << std::endl; 
- 			std::cout << "\tTotal in $ : " << cart.total(bill) << std::endl; 
- 			std::cout << std::endl; 
- 		} 
- 		return 0; 
- 	}