#include <iostream>

struct Snode
{
    char data;
    int count;
    Snode *previous = nullptr;
    Snode *next = nullptr;
    Snode(char a, int c) : data(a), count(c) {}
};

class set
{
private:
    Snode *head = nullptr;
    Snode *tail = nullptr;

    void append(char value, int count)
    {
        Snode *temp = new Snode(value, count);

        if (!head)
            head = temp;

        if (tail)
        {
            temp->previous = tail;
            tail->next = temp;
        }
        tail = temp;
    }

    void remove(Snode *node)
    {
        if (node->previous)
            node->previous->next = node->next;

        if (node->next)
            node->next->previous = node->previous;

        if (head == node)
            head = node->next;

        if (tail == node)
            tail = node->previous;

        delete node;
    }

    void swap(set &other)
    {
        Snode *ptr = head;
        head = other.head;
        other.head = ptr;

        ptr = tail;
        tail = other.tail;
        other.tail = ptr;
    }

public:
    set() = default;

    set(const set &src)
    {
        Snode *temp = src.head;
        while (temp)
        {
            append(temp->data, temp->count);
            temp = temp->next;
        }
    }

    set(set &&src)
    {
        src.swap(*this);
    }

    ~set()
    {
        Snode *temp = head;
        while (temp)
        {
            Snode *next = temp->next;
            delete temp;
            temp = next;
        }
    }

    set& operator=(const set &rhs)
    {
        if (&rhs != this)
        {
            set temp(rhs);
            temp.swap(*this);
        }
        return *this;
    }

    set& operator=(set &&rhs)
    {
        rhs.swap(*this);
        return *this;
    }

    bool isAvailable(char value)
    {
        return (find(value) != nullptr);
    }

    Snode* find(char value)
    {
        Snode *temp = head;    
        while (temp)
        {
            if (temp->data == value)
                return temp;    
            temp = temp->next;
        }    
        return nullptr;
    }

    bool isFirst(char value)
    {
        return ((head) && (head->data == value));
    }

    bool isLast(char value)
    {
        return ((tail) && (tail->data == value));
    }

    void display()
    {
        Snode *temp = head;
        while (temp)
        {
            std::cout << temp->data << " " << temp->count << std::endl;
            temp = temp->next;
        }
    }

    void insert(char value)
    {
        Snode *temp = find(value);
        if (temp)
            temp->count += 1;
        else
            append(value, 1);
    }

    int count(char value)
    {
        Snode *temp = find(value);
        return (temp) ? temp->count : 0;
    }

    void deleteFirst()
    {
        if (head)
            remove(head);
    }

    void deleteLast()
    {
        if (tail)
            remove(tail);
    }

    void remove(char value)
    {
        Snode *temp = find(value);
        if (temp)
        {
            if (temp->count > 1)
                temp->count -= 1;
            else
                remove(temp);
        }
    }   
};

int main()
{
    //defining a mySet as a "set" type
    set mySet;

    //adding values to create nodes
    mySet.insert('c');
    mySet.insert('a');
    mySet.insert('a');
    mySet.insert('c');
    mySet.insert('c');

    set myCopiedSet = mySet; // make a copy of the list

    //adding more values to create nodes
    myCopiedSet.insert('a');
    myCopiedSet.insert('b');
    myCopiedSet.insert('b');
    myCopiedSet.insert('c');

    // another test
    set myTestSet;
    myTestSet.insert('c');
    myTestSet.insert('c');
    myTestSet.insert('j');
    myTestSet.insert('j');
    myTestSet.insert('j');
    myTestSet.insert('r');

    //displaying nodes through "value count" format
    std::cout << "original:" << std::endl;
    mySet.display();    
    std::cout << "copy:" << std::endl;
    myCopiedSet.display();
    std::cout << "test:" << std::endl;
    myTestSet.display();

    return 0;
}