import java.util.*;
import java.util.stream.Stream;

public class EulerCycle {

	public static List<Integer> eulerCycleUndirected(List<Integer>[] graph, int u) {
		Set<Long> usedEdges = new HashSet<>();
		int n = graph.length;
		int[] curEdge = new int[n];
		List<Integer> res = new ArrayList<>();
		dfs(graph, curEdge, usedEdges, res, u);
		Collections.reverse(res);
		return res;
	}

	static void dfs(List<Integer>[] graph, int[] curEdge, Set<Long> usedEdges, List<Integer> res, int u) {
		while (curEdge[u] < graph[u].size()) {
			int v = graph[u].get(curEdge[u]++);
			if (usedEdges.add(((long) Math.min(u, v) << 32) + Math.max(u, v)))
				dfs(graph, curEdge, usedEdges, res, v);
		}
		res.add(u);
	}

	public static List<Integer> eulerCycleUndirected2(List<Integer>[] graph, int u) {
		int[] curEdge = new int[graph.length];
		List<Integer> res = new ArrayList<>();
		Stack<Integer> stack = new Stack<>();
		Set<Long> usedEdges = new HashSet<>();
		stack.add(u);
		while (!stack.isEmpty()) {
			u = stack.pop();
			while (curEdge[u] < graph[u].size()) {
				int v = graph[u].get(curEdge[u]++);
				if (usedEdges.add((((long) Math.min(u, v) << 32) + Math.max(u, v)))) {
					stack.push(u);
					u = v;
				}
			}
			res.add(u);
		}
		Collections.reverse(res);
		return res;
	}

	public static List<Integer> eulerCycleDirected(List<Integer>[] graph, int u) {
		int n = graph.length;
		int[] curEdge = new int[n];
		List<Integer> res = new ArrayList<>();
		dfs(graph, curEdge, res, u);
		Collections.reverse(res);
		return res;
	}

	static void dfs(List<Integer>[] graph, int[] curEdge, List<Integer> res, int u) {
		while (curEdge[u] < graph[u].size()) {
			dfs(graph, curEdge, res, graph[u].get(curEdge[u]++));
		}
		res.add(u);
	}

	public static List<Integer> eulerCycleDirected2(List<Integer>[] graph, int v) {
		int[] curEdge = new int[graph.length];
		List<Integer> res = new ArrayList<>();
		Stack<Integer> stack = new Stack<>();
		stack.add(v);
		while (!stack.isEmpty()) {
			v = stack.pop();
			while (curEdge[v] < graph[v].size()) {
				stack.push(v);
				v = graph[v].get(curEdge[v]++);
			}
			res.add(v);
		}
		Collections.reverse(res);
		return res;
	}

	// Usage example
	public static void main(String[] args) {
		int n = 5;
		List<Integer>[] g = Stream.generate(ArrayList::new).limit(n).toArray(List[]::new);
		g[0].add(1);
		g[1].add(2);
		g[2].add(0);

		g[1].add(3);
		g[3].add(4);
		g[4].add(1);

		System.out.println(eulerCycleDirected(g, 0));
		System.out.println(eulerCycleDirected2(g, 0));

		n = 5;
		g = Stream.generate(ArrayList::new).limit(n).toArray(List[]::new);
		g[0].add(1);
		g[1].add(0);
		g[1].add(2);
		g[2].add(1);
		g[2].add(3);
		g[3].add(2);
		g[0].add(3);
		g[3].add(0);

		g[0].add(4);
		g[4].add(0);
		g[1].add(4);
		g[4].add(1);

		g[0].add(2);
		g[2].add(0);
		g[1].add(3);
		g[3].add(1);

		System.out.println(eulerCycleUndirected(g, 2));
		System.out.println(eulerCycleUndirected2(g, 2));
	}
}
