//
//  main.cpp
//  Map and Unordered Map (STL)
//
//  Created by Himanshu on 23/03/22.
//

#include <iostream>
#include <map>
#include <unordered_map>
#include <string>
using namespace std;

void printUnorderedMap (unordered_map<string, int> myMap) {
    for (auto& it: myMap) {
        cout<<(it.first)<<": "<<(it.second)<<" ";
    }
    cout<<endl;
}

void printMap (map<string, int> myMap) {
    for (auto& it: myMap) {
        cout<<(it.first)<<": "<<(it.second)<<" ";
    }
    cout<<endl;
}

int main () {
    
    unordered_map<string, int> initUnorderedMap = { {"beta", 2}, {"gamma", 3}, {"alpha", 1} };
    map<string, int> initMap = { {"beta", 2}, {"gamma", 3}, {"alpha", 1} };
    
    unordered_map<string, int> myUnorderedMap;
    map<string, int> myMap;
    
    map<string, int>::iterator it;
    
    //Iterating over 'unordered_map'
    cout<<"initUnorderedMap elements:"<<endl;
    printUnorderedMap(initUnorderedMap);
    
    //Iterating over 'map'
    cout<<endl<<"initMap elements:"<<endl;
    printMap(initMap);
    
    cout<<"Elements in the initMap (map) container are kept sorted based on their key values"<<endl;
    
    initUnorderedMap.clear();
    //size of unordered_map after clear
    cout<<endl<<"Size of unordered_map after clear: "<<initUnorderedMap.size()<<endl;
    
    initMap.clear();
    //size of map after clear
    cout<<endl<<"Size of map after clear: "<<initMap.size()<<endl;
    
    myUnorderedMap.insert({"first", 30});
    myUnorderedMap["second"] = 20;
    myUnorderedMap["third"] = 10;
    
    myMap.insert({"first", 10});
    myMap["second"] = 20;
    myMap["third"] = 30;
    
    //Iterating over 'myUnorderedMap'
    cout<<endl<<"myUnorderedMap elements:"<<endl;
    printUnorderedMap(myUnorderedMap);
    
    //Iterating over 'myMap'
    cout<<endl<<"myMap elements:"<<endl;
    printMap(myMap);
    
    //Iterating over 'myUnorderedMap' after erase
    myUnorderedMap.erase("second");
    cout<<endl<<"myUnorderedMap elements after erase 'second': "<<endl;
    printUnorderedMap(myUnorderedMap);
    
    //Iterating over 'myMap' after erase
    myMap.erase(myMap.begin());
    cout<<endl<<"myMap elements after erase myMap.begin(): "<<endl;
    printMap(myMap);
    cout<<endl<<"Find operation on map and unordered_map:"<<endl;
    
    if (myUnorderedMap.find("second") != myUnorderedMap.end()) {
        cout<<"key 'second' is present in myUnorderedMap"<<endl;
    } else {
        cout<<"key 'second' is not present in myUnorderedMap"<<endl;
    }
    
    if (myMap.find("second") != myMap.end()) {
        cout<<"key 'second' is present in myMap"<<endl;
    } else {
        cout<<"key 'second' is not present in myMap"<<endl;
    }
    
    myMap.clear();
    
    myMap["a"] = 1;
    myMap["b"] = 2;
    myMap["c"] = 3;
    myMap["d"] = 4;
    myMap["e"] = 5;
    myMap["f"] = 6;
    myMap["g"] = 7;
    myMap["h"] = 8;
    
    //Iterating over 'myMap'
    cout<<endl<<"myMap new elements after clear:"<<endl;
    printMap(myMap);
    
    //Iterating over 'myMap' after erase on a range
    myMap.erase(myMap.find("f"), myMap.end());
    cout<<endl<<"myMap elements after erasing a range of elements:"<<endl;
    printMap(myMap);
    
    //Upper bound of "c" in myMap
    cout<<endl<<"(Upper bound method is available for map but not for unordered_map) Upper bound of 'c' in myMap: ";
    cout<<((myMap.upper_bound("c"))->first)<<": "<<((myMap.upper_bound("c"))->second)<<endl;
    
    //Lower bound of "c" in myMap
    it  = myMap.lower_bound("c");
    cout<<endl<<"(Lower bound method is available for map but not for unordered_map) Lower bound of 'c' in myMap: ";
    cout<<it->first<<": "<<it->second<<endl;
    
    return 0;
}