#include <iostream>
#include <vector>
#include <string>
using namespace std;

enum Suit { CLUBS, DIAMONDS, HEARTS, SPADES };

enum Rank { ACE=1, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE,
    TEN, JACK, QUEEN, KING };

// declare that Deck is a structure, without defining it
struct Deck;

struct Card
{
    Rank rank;
    Suit suit;

    Card (Suit s, Rank r);
    Card ();
    void print () const;
    bool isGreater (const Card& c2) const;
    int find (const Deck& deck) const;
};

Card::Card (Suit s, Rank r) {
    suit = s;  rank = r;
}

Card::Card () {
    suit = CLUBS; rank = ACE;
}

void Card::print () const
{
    vector<string> suits (4);
    suits[CLUBS] = "Clubs";
    suits[DIAMONDS] = "Diamonds";
    suits[HEARTS] = "Hearts";
    suits[SPADES] = "Spades";

    vector<string> ranks (14);
    ranks[ACE] = "Ace";
    ranks[TWO] = "2";
    ranks[THREE] = "3";
    ranks[FOUR] = "4";
    ranks[FIVE] = "5";
    ranks[SIX] = "6";
    ranks[SEVEN] = "7";
    ranks[EIGHT] = "8";
    ranks[NINE] = "9";
    ranks[TEN] = "10";
    ranks[JACK] = "Jack";
    ranks[QUEEN] = "Queen";
    ranks[KING] = "King";

    cout << ranks[rank] << " of " << suits[suit] << endl;
}

bool Card::isGreater (const Card& c2) const
{
    // first check the suits
    if (suit > c2.suit) return true;
    if (suit < c2.suit) return false;

    // if the suits are equal, check the ranks
    if (rank > c2.rank) return true;
    if (rank < c2.rank) return false;

    // if the ranks are also equal, return false
    return false;
}

bool equals (const Card& c1, const Card& c2)
{
    return (c1.rank == c2.rank && c1.suit == c2.suit);
}

struct Deck {
    vector<Card> cards;

    Deck (int n);
    Deck ();
    void print () const;
    Deck subdeck (int low, int high) const;
    void shuffle ();
    void sort ();
    Deck mergeSort () const;
};

Deck::Deck (int size)
{
    vector<Card> temp (size);
    cards = temp;
}

Deck::Deck ()
{
    vector<Card> temp (52);
    cards = temp;

    int i = 0;
    for (Suit suit = CLUBS; suit <= SPADES; suit = Suit(suit+1)) {
        for (Rank rank = ACE; rank <= KING; rank = Rank(rank+1)) {
            cards[i].suit = suit;
            cards[i].rank = rank;
            i++;
        }
    }
}

vector<Card> BuildDeck () {
    vector<Card> deck (52);
    int index = 0;
    for (Suit suit = CLUBS; suit <= SPADES; suit = Suit(suit+1)) {
        for (Rank rank = ACE; rank <= KING; rank = Rank(rank+1)) {
            deck[index].suit = suit;
            deck[index].rank = rank;
            index++;
        }
    }
    return deck;
}

void Deck::print () const {
    for (int i = 0; i < (int) cards.size(); i++) {
        cards[i].print ();
    }
}

void printDeck (const Deck& deck) {
    for (int i = 0; i<52; i++) {
        deck.cards[i].print();
    }
}

Deck Deck::subdeck (int low, int high) const {
    Deck sub (high-low+1);
    for (int i = 0; i < (int) sub.cards.size(); i++) {
        sub.cards[i] = cards[low+i];
    }
    return sub;
}

int Card::find (const Deck& deck) const {
    for (int i = 0; i < deck.cards.size(); i++) {
        if (equals (deck.cards[i], *this)) return i;
    }
    return -1;
}

void Deck::shuffle () {
    for (int i=0; i<cards.size(); i++) {
        // choose a random number between i and cards.size()
        int randCard = i + rand() % (cards.size() - i);
        // swap the ith card and the randomly-chosen card
        Card temp = cards[i];
        cards[i] = cards[randCard];
        cards[randCard] = temp;
    }
}

void Deck::sort () {
    for (int i=0; i<cards.size(); i++) {
        // find the lowest card at or to the right of i
        int lowest = i;
        for (int j=i+1; j<cards.size(); j++) {
            if (cards[lowest].isGreater(cards[j])) lowest = j;
        }
        // swap the ith card and the lowest card
        Card temp = cards[i];
        cards[i] = cards[lowest];
        cards[lowest] = temp;
        // cards 0 to i are now the lowest in deck and sorted
    }
}

Deck merge (const Deck& d1, const Deck& d2) {
    // create a new deck big enough for all the cards
    Deck result (d1.cards.size() + d2.cards.size());

    // use the index i to keep track of where we are in
    // the first deck, and the index j for the second deck
    int i = 0;
    int j = 0;

    // the index k traverses the result deck
    for (int k = 0; k < result.cards.size(); k++) {
        Card winner;       // lower card
        // if d1 is empty, d2 wins; if d2 is empty, d1 wins;
        if (i >= d1.cards.size()) {
            winner = d2.cards[j]; j++;
        } else if (j >= d2.cards.size()) {
            winner = d1.cards[i]; i++;
        // otherwise, compare the two cards
        } else if (d2.cards[j].isGreater(d1.cards[i])) {
            winner = d1.cards[i]; i++;
        } else {winner = d2.cards[j]; j++; }
        // add the winner to the new deck
        result.cards[k] = winner;
    }
    return result;
}

Deck Deck::mergeSort () const {
    // if the deck is 0 or 1 cards, return it
    if (cards.size() <= 1) return *this;

    // find the midpoint of the deck
    int mid = (cards.size()-1) / 2;
    // divide the deck into two subdecks
    Deck d1 = subdeck(0, mid);
    Deck d2 = subdeck(mid+1, cards.size()-1);
    // sort the subdecks using mergesort
    Deck sd1 = d1.mergeSort();
    Deck sd2 = d2.mergeSort();
    // merge the two halves and return the result
    return merge(sd1,sd2);
}

int main () {
	Deck AllAC (10);
    Deck newDeck;
//    newDeck.print();
    newDeck.shuffle();
    newDeck.print();
//    newDeck.sort();
    newDeck.mergeSort().print();

    return 0;
}

