#include <iostream>
#include <string>
#include <vector>
#include <deque>
#include <list>
#include <set>
#include <unordered_set>
#include <map>
#include <unordered_map>
#include <array>
#include <queue>
#include <stack>
#include <algorithm>
#include <tuple>
#include <numeric>
#include <type_traits>
#include <limits>

/*
 * 重み無しグラフで、辺集合表現から隣接集合表現へ変換
 *
 * @tparam Directed 無向グラフなら false、有向グラフなら true
 * @tparam V        ノード番号の型
 */
template<bool Directed, typename V>
std::vector<std::unordered_set<V>> edges2neighs(const std::vector<std::pair<V, V>>& edges, V n)
{
	std::vector<std::unordered_set<V>> neighs(n);
	for (const auto& edge : edges) {
		auto u = std::get<0>(edge);
		auto v = std::get<1>(edge);
		neighs[u].insert(v);
		if (!Directed)
			neighs[v].insert(u);
	}
	return neighs;
}

/*
 * 重み付きグラフで、辺集合表現から隣接集合表現へ変換
 *
 * @tparam Directed 無向グラフなら false、有向グラフなら true
 * @tparam V        ノード番号の型
 * @tparam W        重みの型
 */
template<bool Directed, typename V, typename W>
std::vector<std::unordered_map<V, W>> edges2neighs(const std::vector<std::tuple<V, V, W>>& edges, V n)
{
	std::vector<std::unordered_set<V>> neighs(n);
	for (const auto& edge : edges) {
		auto u = std::get<0>(edge);
		auto v = std::get<1>(edge);
		auto w = std::get<2>(edge);
		neighs[u].insert({v, w});
		if (!Directed)
			neighs[v].insert({u, w});
	}
	return neighs;
}


// グラフの travarse で、 visit set を管理する必要があるが、適切な管理方法は外包するアルゴリズムによって変わるので、それを抽象化したい

/**
 * bit vector として管理する visit set
 */
template<typename V>
class vector_visitset {
private:
	std::vector<char> visited_;
	V num_visited_;

public:
	vector_visitset(V n) : visited_(n), num_visited_(0) {}

	void visit(V n)
	{
		if (!visited_[n]) {
			visited_[n] = 1;
			++num_visited_;
		}
	}

	bool is_visited(V n) const
	{
		return visited_[n];
	}

	bool is_all_visited() const
	{
		return num_visited_ == visited_.size();
	}
};

/**
 * unordered_set として管理する visit set
 *
 * 訪れていない適当なノードを取り出す操作が O(1) で行える
 *
 * @note unordered_set なので空間効率はあまり良くない
 */
template<typename V>
class unordered_visitset {
private:
	std::unordered_set<V> unvisited_;

public:
	unordered_visitset(V n) : unvisited_(n)
	{
		for (V i = 0; i < n; ++n)
			unvisited_.insert(n);
	}

	void visit(V n)
	{
		unvisited_.erase(n);
	}

	bool is_visited(V n) const
	{
		return unvisited_.find(n) == unvisited_.end();
	}

	bool is_all_visited() const
	{
		return unvisited_.empty();
	}

	V pop_unvisited()
	{
		// is_all_visited() == true のときに呼ぶと落ちる
		V ret = *unvisited_.begin();
		unvisited_.erase(unvisited_.begin());
		return ret;
	}
};


/*
 * 重み無しグラフの隣接集合表現を受け取り、深さ優先探索してノードをポストオーダーで列挙して関数を呼ぶ
 *
 * @tparam V ノード番号の型
 * @tparam F 呼ぶ関数の型
 */
template<typename V, typename F>
void graph_depthfirst_postorder_call(const std::vector<std::unordered_set<V>>& neighs, V n, const F& fn, V start = 0);

template<typename V, typename F, typename VS>
void graph_depthfirst_postorder_call(const std::vector<std::unordered_set<V>>& neighs, V n, VS& vs, const F& fn, V start = 0);

template<typename V, typename F, typename VS>
void graph_depthfirst_postorder_call_impl(VS& vs, const std::vector<std::unordered_set<V>>& neighs, V cur, const F& fn)
{
	for (auto neigh : neighs[cur]) {
		if (vs.is_visited(neigh))
			continue;
		vs.visit(neigh);
		graph_depthfirst_postorder_call_impl(vs, neighs, neigh, fn);
	}
	fn(cur);
}

template<typename V, typename F, typename VS>
void graph_depthfirst_postorder_call(const std::vector<std::unordered_set<V>>& neighs, V n, VS& vs, const F& fn, V start /* = 0 */)
{
	vs.visit(start);
	graph_depthfirst_postorder_call_impl(vs, neighs, start, fn);
}

template<typename V, typename F>
void graph_depthfirst_postorder_call(const std::vector<std::unordered_set<V>>& neighs, V n, const F& fn, V start /* = 0 */)
{
	vector_visitset<V> vs(n);
	graph_depthfirst_postorder_call(neighs, n, vs, fn, start);
}


/*
 * 重み無しグラフの隣接集合表現を受け取り、深さ優先探索してノードをポストオーダーで列挙して返す
 *
 * @tparam V ノード番号の型
 * @note start から到達可能なノードのみが返る
 */
template<typename V>
std::vector<V> graph_depthfirst_postorder_enum(const std::vector<std::unordered_set<V>>& neighs, V n, V start = 0)
{
	std::vector<V> nodes(n);
	size_t num = 0;
	graph_depthfirst_postorder_call(neighs, n, [&nodes, &num](V node){
		nodes[num++] = node;
	}, start);
	nodes.resize(num);
	return nodes;
}


int main()
{
	// AGC035B

	int N, M;
	std::vector<std::pair<int, int>> E;

	std::cin >> N >> M;
	E.resize(M);
	for (auto& e : E) {
		std::cin >> e.first >> e.second;
		--e.first;
		--e.second;
	}

	if (M % 2 == 1) {
		std::cout << "-1\n";
		return 0;
	}

	auto neighs = edges2neighs<false>(E, N);
	auto nodes = graph_depthfirst_postorder_enum(neighs, N);

	std::vector<char> parity(N);
	for (auto node : nodes) {
		for (auto neigh : neighs[node]) {
			if (parity[node]) {
				std::cout << (node + 1) << " " << (neigh + 1) << "\n";
				parity[node] = 0;
			} else {
				std::cout << (neigh + 1) << " " << (node + 1) << "\n";
				parity[neigh] ^= 1;
			}
			neighs[neigh].erase(node);
		}
	}
}


Measured memory_bytes: 23808