// File: node.h

#ifndef NODE_H
#define NODE_H

#include <iostream>

namespace joelinkedlist
{
class Node
{
    int    data;
    Node*    next;
public:
    Node(int d,Node* n);
    int get_data() const;
    Node* get_next() const;
    void set_next(Node* ptr);
};

std::ostream& operator<<(std::ostream&, const Node&);
}

#endif



// File: node.cpp

#include "node.h"
#include <iostream>
using namespace std;

joelinkedlist::Node::Node(int d, Node* n)
: data(d), next(n)
{
}

int joelinkedlist::Node::get_data() const
{
    return data;
}

using namespace joelinkedlist;

Node* Node::get_next() const
{
    return next;
}

void Node::set_next(Node* ptr)
{
    next = ptr;
}

namespace joelinkedlist
{
    ostream& operator<<(ostream& out, const Node& obj)
    {
        out << obj.get_data();
        return out;
    }
}

// File: list.h

#ifndef LIST_H
#define LIST_H

#include "node.h"
#include <iostream>

namespace joelinkedlist
{
    class List
    {
        Node* top;
    public:
        List();
        ~List();
        void push(int item);
        int pop();
        Node* get_top() const;
        bool remove(int item);
        Node* find(int item) const;
        bool remove_last();
    };

    std::ostream& operator<<(std::ostream&, const List&);
}

#endif

// File: list.cpp

#include <iostream>
#include <cstdlib>
using namespace std;

#include "list.h"
using joelinkedlist::List;
using joelinkedlist::Node;

List::List() : top(0)
{ }

List::~List()
{
    Node* temp = top;
    while (temp != nullptr) {
        top = top -> get_next();
        delete temp;
        temp = top;
    }
}

void List::push(int item)
{
    Node* temp = new Node(item, top);
    top = temp;
}

int List::pop()
{
    Node* temp = top;
    top = top->get_next();
    int value = temp->get_data();
    delete temp;
    return value;
}

Node* List::get_top() const
{
    return top;
}

Node* List::find(int item) const
{
    Node* temp = top;
    while (temp != 0) {
        if (temp->get_data() == item) return temp;
        temp = temp -> get_next();
    }
    return 0;
}

bool List::remove(int item)
{
    if (!find(item)) {
        cerr << item << " is not in the List\n";
        return false;
    }
    Node* temp1 = top;
    Node* temp2;
    if (top->get_data() == item) {
        top = top -> get_next();
        delete temp1;
        return true;
    }
    while (temp1->get_next()->get_data() != item) {
        temp1 = temp1 -> get_next();
    }
    temp2 = temp1 -> get_next();
    temp1->set_next(temp2->get_next());
    delete temp2;
    return true;
}

namespace joelinkedlist
{
    ostream& operator<<(ostream& out, const List& object)
    {
        Node* temp = object.get_top();
        while (temp != 0) {
            out << *temp << ' ';
            temp = temp -> get_next();
        }
        return out;
    }
}

// File: main.cpp

#include <iostream>
using namespace std;

#include "list.h"
using joelinkedlist::List;

int main()
{
    List L;
    L.push(2);
    L.push(4);
    L.push(6);
    L.push(8);
    L.push(10);
    cout << L << endl;

    cout << "top value is " << L.get_top()->get_data() << endl;

    if (L.find(2)) cout << 2 << " is in the list\n";
    if (L.find(5)) cout << 5 << " is in the list\n";
    if (L.find(6)) cout << 6 << " is in the list\n";
    if (L.find(10)) cout << 10 << " is in the list\n";

    cout << L.pop() << " removed from the list\n";
    cout << L << endl;

    L.remove(3);
    L.remove(6);
    cout << L << endl;

    L.remove(2);
    L.remove(8);
    cout << L << endl;
}