You are given an undirected graph consisting of $n$ vertices and $m$ edges. Your task is to find the number of connected components which are cycles.

Here are some definitions of graph theory.

An undirected graph consists of two sets: set of nodes (called vertices) and set of edges. Each edge connects a pair of vertices. All edges are bidirectional (i.e. if a vertex $a$ is connected with a vertex $b$, a vertex $b$ is also connected with a vertex $a$). An edge can't connect vertex with itself, there is at most one edge between a pair of vertices.

Two vertices $u$ and $v$ belong to the same connected component if and only if there is at least one path along edges connecting $u$ and $v$.

A connected component is a cycle if and only if its vertices can be reordered in such a way that:  the first vertex is connected with the second vertex by an edge,  the second vertex is connected with the third vertex by an edge,  ...  the last vertex is connected with the first vertex by an edge,  all the described edges of a cycle are distinct. 

A cycle doesn't contain any other edges except described above. By definition any cycle contains three or more vertices. [Image] There are $6$ connected components, $2$ of them are cycles: $[7, 10, 16]$ and $[5, 11, 9, 15]$. 


-----Input-----

The first line contains two integer numbers $n$ and $m$ ($1 \le n \le 2 \cdot 10^5$, $0 \le m \le 2 \cdot 10^5$) — number of vertices and edges.

The following $m$ lines contains edges: edge $i$ is given as a pair of vertices $v_i$, $u_i$ ($1 \le v_i, u_i \le n$, $u_i \ne v_i$). There is no multiple edges in the given graph, i.e. for each pair ($v_i, u_i$) there no other pairs ($v_i, u_i$) and ($u_i, v_i$) in the list of edges.


-----Output-----

Print one integer — the number of connected components which are also cycles.


-----Examples-----
Input
5 4
1 2
3 4
5 4
3 5

Output
1

Input
17 15
1 8
1 12
5 11
11 9
9 15
15 5
4 13
3 13
4 3
10 16
7 10
16 7
14 3
14 4
17 6

Output
2



-----Note-----

In the first example only component $[3, 4, 5]$ is also a cycle.

The illustration above corresponds to the second example.

Source: codeforces | Difficulty: MEDIUM
Tags: Spanning trees

Graph algorithms

Graph traversal


Solutions:
(n, m) = map(int, input().split())
adj = [0 for _ in range(n)]
p = [i for i in range(n)]
sz = [1 for i in range(n)]

def find(i):
	if i != p[i]:
		p[i] = find(p[i])
	return p[i]

def union(i, j):
	i = find(i)
	j = find(j)
	if sz[i] > sz[j]:
		sz[i] += sz[j]
		p[j] = i
	else:
		sz[j] += sz[i]
		p[i] = j
for _ in range(m):
	(a, b) = map(int, input().split())
	a -= 1
	b -= 1
	adj[a] += 1
	adj[b] += 1
	if find(a) != find(b):
		union(a, b)
l = set()
for i in range(n):
	l.add(find(i))
for i in range(n):
	if adj[i] != 2:
		if find(i) in l:
			l.remove(find(i))
print(len(l))


(n, m) = map(int, input().split())
edges = __import__('collections').defaultdict(list)
connected = set()
for _ in range(m):
	(v1, v2) = map(int, input().split())
	connected.add((v1, v2) if v1 < v2 else (v2, v1))
	edges[v1].append(v2)
	edges[v2].append(v1)
components = set((i for i in edges.keys() if len(edges[i]) == 2))
count = 0
while components:
	(current, last) = edges[components.pop()]
	while current in components:
		components.remove(current)
		(v1, v2) = edges[current]
		(contains_v1, contains_v2) = (v1 in components, v2 in components)
		if not contains_v1 and (not contains_v2):
			break
		else:
			current = v1 if contains_v1 else v2
		if current == last:
			count += 1
print(count)


from collections import defaultdict
(V, E) = input().split()
(V, E) = (int(V), int(E))
G = defaultdict(set)
for _ in range(E):
	(v1, v2) = input().split()
	(v1, v2) = (int(v1), int(v2))
	G[v1].add(v2)
	G[v2].add(v1)
cycle_cnt = 0
visited = set()
for v in G:
	if v in visited:
		continue
	stack = [v]
	path = []
	while stack:
		node = stack.pop()
		if node in visited:
			continue
		visited.add(node)
		path.append(node)
		stack.extend(G[node])
	ok = True
	for node in path:
		if len(G[node]) != 2:
			ok = False
			break
	cycle_cnt += ok
print(cycle_cnt)


(v, e) = map(int, input().strip().split())
edges = [[] for _ in range(v)]
for i in range(e):
	(x, y) = input().strip().split()
	x = int(x) - 1
	y = int(y) - 1
	edges[x].append(y)
	edges[y].append(x)
degreeTwo = [i for i in range(v) if len(edges[i]) == 2]
components = [-1] * v
numComp = 0
ans = 0
for x in degreeTwo:
	if components[x] == -1:
		flag = True
		queue = [x]
		while queue:
			curr = queue.pop(0)
			flag = flag and len(edges[curr]) == 2
			components[curr] = numComp
			for neigh in edges[curr]:
				if components[neigh] == -1:
					queue.append(neigh)
					components[neigh] = numComp
		numComp += 1
		ans += int(flag)
print(ans)


from sys import stdin, stdout
from math import gcd, ceil, sqrt
ii1 = lambda : int(stdin.readline().strip())
is1 = lambda : stdin.readline().strip()
iia = lambda : list(map(int, stdin.readline().strip().split()))
isa = lambda : stdin.readline().strip().split()
mod = 1000000007

def dfs(cur, d, visited):
	queue = [cur]
	graph = []
	while len(queue):
		cur = queue.pop()
		graph.append(cur)
		visited[cur] = 1
		for i in d.get(cur, []):
			if not visited[i]:
				queue.append(i)
	return graph
(n, m) = iia()
d = {}
visited = [0] * (n + 1)
degree = [0] * (n + 1)
for _ in range(m):
	(a, b) = iia()
	d.setdefault(a, []).append(b)
	d.setdefault(b, []).append(a)
	degree[a] += 1
	degree[b] += 1
res = 0
for i in range(1, n + 1):
	if not visited[i]:
		cur = dfs(i, d, visited)
		for deg in cur:
			if degree[deg] != 2:
				break
		else:
			res += 1
print(res)


rd = lambda : map(int, input().split())
(n, m) = rd()
d = {}

def f(x):
	d[x] = d.get(x, 0) + 1
uf = [i for i in range(n)]

def find(i):
	p = uf[i]
	if i == p:
		return p
	else:
		uf[i] = find(p)
		return uf[i]
for _ in range(m):
	(u, v) = sorted(rd())
	u -= 1
	v -= 1
	f(u)
	f(v)
	uf[find(v)] = find(u)
r = [1] * n
for i in range(n):
	r[find(i)] &= d.get(i, 0) == 2
print(sum((i == uf[i] for i in range(n))) + sum(r) - n)


def main():
	[n, m] = [int(x) for x in input().split()]
	g = [[] for _ in range(0, n)]
	mark = [False] * n
	for i in range(0, m):
		[v, u] = [int(x) for x in input().split()]
		v -= 1
		u -= 1
		g[v].append(u)
		g[u].append(v)
	res = 0
	for i in range(0, n):
		if not mark[i]:
			cntV = 0
			cntEdge = 0
			condition = True
			queue = []
			ptr = 0
			queue.append(i)
			while ptr < len(queue):
				v = queue[ptr]
				ptr += 1
				condition &= len(g[v]) is 2
				cntV += 1
				for _ in range(0, len(g[v])):
					u = g[v][_]
					cntEdge += 1
					if mark[u] is False:
						mark[u] = True
						queue.append(u)
			cntEdge //= 2
			if cntEdge == cntV and condition == True:
				res += 1
	print(res)
main()


import sys
from collections import Counter

def input():
	return sys.stdin.readline().strip()

def list2d(a, b, c):
	return [[c] * b for i in range(a)]

def list3d(a, b, c, d):
	return [[[d] * c for j in range(b)] for i in range(a)]

def list4d(a, b, c, d, e):
	return [[[[e] * d for j in range(c)] for j in range(b)] for i in range(a)]

def ceil(x, y=1):
	return int(-(-x // y))

def INT():
	return int(input())

def MAP():
	return map(int, input().split())

def LIST(N=None):
	return list(MAP()) if N is None else [INT() for i in range(N)]

def Yes():
	print('Yes')

def No():
	print('No')

def YES():
	print('YES')

def NO():
	print('NO')
INF = 10 ** 18
MOD = 10 ** 9 + 7

class UnionFind:

	def __init__(self, n):
		self.n = n
		self.par = [i for i in range(n)]
		self.rank = [0] * n
		self.size = [1] * n
		self.tree = [True] * n
		self.grpcnt = n

	def find(self, x):
		t = []
		while self.par[x] != x:
			t.append(x)
			x = self.par[x]
		for i in t:
			self.par[i] = x
		return self.par[x]

	def union(self, x, y):
		x = self.find(x)
		y = self.find(y)
		if x == y:
			self.tree[x] = False
			return
		if not self.tree[x] or not self.tree[y]:
			self.tree[x] = self.tree[y] = False
		self.grpcnt -= 1
		if self.rank[x] < self.rank[y]:
			self.par[x] = y
			self.size[y] += self.size[x]
		else:
			self.par[y] = x
			self.size[x] += self.size[y]
			if self.rank[x] == self.rank[y]:
				self.rank[x] += 1

	def is_same(self, x, y):
		return self.find(x) == self.find(y)

	def get_size(self, x=None):
		if x is not None:
			return self.size[self.find(x)]
		else:
			return self.grpcnt

	def is_tree(self, x):
		return self.tree[self.find(x)]
(N, M) = MAP()
uf = UnionFind(N)
nodes = [[] for i in range(N)]
for i in range(M):
	(a, b) = MAP()
	a -= 1
	b -= 1
	uf.union(a, b)
	nodes[a].append(b)
	nodes[b].append(a)
edgecnt = [set() for i in range(N)]
for i in range(N):
	edgecnt[uf.find(i)].add(len(nodes[i]))
ans = 0
for i in range(N):
	if len(edgecnt[i]) == 1 and list(edgecnt[i])[0] == 2:
		ans += 1
print(ans)


from collections import defaultdict

def creating_graph(v, w, graph, visited):
	graph[v].append(w)
	graph[w].append(v)
	visited[v] = False
	visited[w] = False

def compute(graph, visited):
	circle_count = 0
	for v in graph:
		if visited[v] == False:
			queue = [v]
			res = True
			while queue:
				cur = queue.pop(0)
				if len(graph[cur]) == 2:
					for n_v in graph[cur]:
						if visited[n_v] == False:
							queue.append(n_v)
							visited[n_v] = True
						elif len(graph[n_v]) != 2:
							res = False
				else:
					res = False
			if res:
				circle_count += 1
	return circle_count
graph = defaultdict(list)
visited = dict()
(n, m) = map(int, input().split())
for _ in range(m):
	(v, w) = map(int, input().split())
	creating_graph(v, w, graph, visited)
print(compute(graph, visited))


def bfs(root):
	q = deque([root])
	vis[root] = True
	ans = 0
	tf = True
	while q:
		x = q.popleft()
		c = 0
		for i in d[x]:
			c += 1
			if not vis[i]:
				q.append(i)
				vis[i] = True
		if c != 2:
			tf = False
	return tf
from collections import defaultdict
from collections import deque
d = defaultdict(list)
(n, m) = map(int, input().split())
for _ in range(m):
	(u, v) = map(int, input().split())
	d[u].append(v)
	d[v].append(u)
vis = [False] * (n + 1)
f = 0
for i in range(1, n + 1):
	if not vis[i] and bfs(i):
		f += 1
print(f)


import operator
data = list(map(int, input().split(' ')))
n = data[0]
m = data[1]
edge = {}
for i in range(n):
	edge[i + 1] = []
for _ in range(m):
	data = list(map(int, input().split(' ')))
	i = data[0]
	j = data[1]
	edge[i].append(j)
	edge[j].append(i)
candidate = [k for (k, v) in edge.items() if len(v) == 2]
result = 0
used = {}

def _iscircle(c, result):
	n = edge[c][0]
	used[c] = 1
	last = c
	while n not in used:
		used[n] = 1
		if len(edge[n]) != 2:
			return result
		t = edge[n][0]
		if t != last:
			last = n
			if t == c:
				return result + 1
			n = t
			continue
		last = n
		t = edge[n][1]
		if t == c:
			return result + 1
		n = t
	return result
for i in candidate:
	result = _iscircle(i, result)
print(result)


(n, m) = map(int, input().split())
a = [[] for _ in range(n + 1)]
for i in range(m):
	(a1, a2) = map(int, input().split())
	a[a2].append(a1)
	a[a1].append(a2)
(ans, f, flag) = (0, [0] * (n + 1), 1)

def dfs(x):
	f[x] = 1
	mid = []
	mid.append(x)
	while mid:
		v = mid.pop()
		if len(a[v]) != 2:
			global flag
			flag = 0
		for i in a[v]:
			if not f[i]:
				f[i] = 1
				mid.append(i)
for i in range(1, n + 1):
	if not f[i]:
		flag = 1
		dfs(i)
		ans += flag
print(ans)


(n, m) = map(int, input().split())
(eg, vis) = ([[] for _ in range(n + 1)], set())
for (x, y) in [map(int, input().split()) for _ in range(m)]:
	eg[x].append(y)
	eg[y].append(x)
cnt = 0

def dfs(now):
	q = [now]
	ok = True
	while q:
		(fa, sz) = (q.pop(), 0)
		if fa in vis:
			continue
		vis.add(fa)
		if len(eg[fa]) != 2:
			ok = False
		for i in eg[fa]:
			if i not in vis:
				q.append(i)
	return ok
for i in range(1, n + 1):
	if i not in vis:
		if dfs(i):
			cnt += 1
print(cnt)


def read():
	(a, b) = [int(x) for x in input().split(' ')]
	return (a, b)
adj = None
vis = None
path = None

def dfs(ini):
	stack = [ini]
	while len(stack) > 0:
		u = stack.pop()
		if vis[u]:
			continue
		path.append(u)
		vis[u] = True
		for v in adj[u]:
			if not vis[v]:
				stack.append(v)

def main():
	(n, m) = read()
	global adj
	global vis
	global path
	adj = [[] for _ in range(n)]
	vis = [False for _ in range(n)]
	for i in range(m):
		(u, v) = read()
		u -= 1
		v -= 1
		adj[u].append(v)
		adj[v].append(u)
	res = 0
	for i in range(n):
		if not vis[i]:
			path = []
			dfs(i)
			flg = True
			for j in path:
				if len(adj[j]) != 2:
					flg = False
					break
			if flg and len(path) >= 3:
				res += 1
	print(res)
main()


def is_cycle(v, v_adj, visited):
	init_v = v
	parent_v = v
	if v in visited or len(v_adj[v]) != 2:
		return False
	visited.add(v)
	v = v_adj[v][0]
	while v != init_v:
		if v in visited:
			return False
		visited.add(v)
		if len(v_adj[v]) != 2:
			return False
		if v_adj[v][0] != parent_v:
			parent_v = v
			v = v_adj[v][0]
		else:
			parent_v = v
			v = v_adj[v][1]
	return True
(n_v, n_e) = map(int, input().split())
v = [list() for i in range(n_v)]
for i in range(n_e):
	(f, t) = map(int, input().split())
	v[f - 1].append(t - 1)
	v[t - 1].append(f - 1)
visited = set()
n_cycles = 0
for vi in range(n_v):
	if is_cycle(vi, v, visited):
		n_cycles += 1
print(n_cycles)


from collections import deque
(n, m) = map(int, input().split())
deg = [0] * (n + 1)
G = [[] for _ in range(n + 1)]
for _ in range(m):
	(u, v) = map(int, input().split())
	deg[u] += 1
	deg[v] += 1
	G[u].append(v)
	G[v].append(u)
C = [0] * (n + 1)

def dfs(v, cc):
	ok = True
	q = [v]
	C[v] = cc
	sz = 1
	while q:
		v = q.pop()
		if len(G[v]) != 2:
			ok = False
		for w in G[v]:
			if C[w] == 0:
				C[w] = cc
				sz += 1
				q.append(w)
	return (sz, ok)
cc = 0
cycles = 0
for v in range(1, n + 1):
	if C[v] == 0:
		cc += 1
		(sz, ok) = dfs(v, cc)
		if ok and sz > 2:
			cycles += 1
print(cycles)


from collections import Counter, deque
import string
import math
import bisect
import sys, threading
sys.setrecursionlimit(10 ** 9)
threading.stack_size(10 ** 8)
from fractions import Fraction

def array_int():
	return [int(i) for i in sys.stdin.readline().split()]

def vary(arrber_of_variables):
	if arrber_of_variables == 1:
		return int(sys.stdin.readline())
	if arrber_of_variables >= 2:
		return map(int, sys.stdin.readline().split())

def makedict(var):
	return dict(Counter(var))
flag = 0

def solve():

	def dfs(node, nodePr):
		nodePr.append(node)
		visited[node] = 1
		for i in graph[node]:
			if visited[i] == 0:
				dfs(i, nodePr)
		return
	testcases = 1
	for _ in range(testcases):
		(n, m) = vary(2)
		graph = [[] for i in range(n + 1)]
		for i in range(m):
			(x, y) = vary(2)
			graph[x].append(y)
			graph[y].append(x)
		count = 0
		visited = [0] * (n + 1)
		par = [-1] * (n + 1)
		for i in range(1, n + 1):
			if visited[i] == 0:
				nodePr = []
				dfs(i, nodePr)
				flag = 0
				for i in nodePr:
					if len(graph[i]) != 2:
						flag = 1
				if flag == 0:
					count += 1
		print(count)
threading.Thread(target=solve).start()


import sys
from math import sqrt, gcd, ceil, log
from bisect import bisect, bisect_left
from collections import defaultdict, Counter, deque
from heapq import heapify, heappush, heappop
inp = sys.stdin.readline
read = lambda : list(map(int, inp().strip().split()))

def main():
	(n, m) = read()
	graph = defaultdict(list)
	for i in range(m):
		(u, v) = read()
		graph[u].append(v)
		graph[v].append(u)
	visited = [0] * (n + 1)
	ans = 0
	for i in range(1, n + 1):
		if not visited[i]:
			f = len(graph[i]) == 2
			stack = [i]
			while stack:
				elem = stack.pop()
				visited[elem] = 1
				f &= len(graph[elem]) == 2
				for j in graph[elem]:
					if not visited[j]:
						stack.append(j)
			ans += f
	print(ans)
main()


from sys import stdin, stdout

def INI():
	return int(stdin.readline())

def INL():
	return [int(_) for _ in stdin.readline().split()]

def INS():
	return stdin.readline()

def MOD():
	return pow(10, 9) + 7

def OPS(ans):
	stdout.write(str(ans) + '\n')

def OPL(ans):
	[stdout.write(str(_) + ' ') for _ in ans]
	stdout.write('\n')
from types import GeneratorType

def bootstrap(f, stack=[]):

	def wrappedfunc(*args, **kwargs):
		if stack:
			return f(*args, **kwargs)
		else:
			to = f(*args, **kwargs)
			while True:
				if type(to) is GeneratorType:
					stack.append(to)
					to = next(to)
				else:
					stack.pop()
					if not stack:
						break
					to = stack[-1].send(to)
			return to
	return wrappedfunc

@bootstrap
def dfs(v):
	global f
	Vis[v] = True
	if Degree[v] != 2:
		f = 0
	for _ in X[v]:
		if not Vis[_]:
			yield dfs(_)
	yield
(n, m) = INL()
X = [[] for _ in range(200000 + 5)]
Degree = [0 for _ in range(200000 + 5)]
for _ in range(m):
	(u, v) = INL()
	X[u].append(v)
	X[v].append(u)
	Degree[u] += 1
	Degree[v] += 1
Vis = [False for _ in range(200000 + 5)]
ans = 0
for _ in range(1, n + 1):
	global f
	f = 1
	if not Vis[_]:
		dfs(_)
		if f == 1:
			ans += 1
OPS(ans)


(n, m) = map(int, input().split())
par = [-1] * n

def find(x):
	if par[x] != -1:
		x = find(par[x])
	return x

def union(x, y):
	a = find(x)
	b = find(y)
	(b, a) = sorted([a, b])
	if a == b:
		return
	par[a] = b
deg = [0] * n
for i in range(m):
	(u, v) = map(int, input().split())
	deg[u - 1] += 1
	deg[v - 1] += 1
	union(u - 1, v - 1)
R = dict()
out = 0
for i in range(n):
	if par[i] == -1:
		R[i] = []
for i in range(n):
	a = find(i)
	R[a].append(i)
for i in R:
	if len(R[i]) > 2:
		x = 1
		for j in R[i]:
			if deg[j] != 2:
				x = 0
				break
		if x == 1:
			out += 1
print(out)


from collections import defaultdict, deque

class Graph:

	def __init__(self, n):
		self.graph = defaultdict(list)
		self.vertices = n
		self.visited = [False] * (n + 1)
		self.count = 0

	def addEdge(self, fr, to):
		self.graph[fr].append(to)
		self.graph[to].append(fr)

	def BFS(self, root):
		queue = deque()
		Connected = []
		queue.append(root)
		Connected.append(root)
		self.visited[root] = True
		while queue:
			current = queue.popleft()
			for i in self.graph[current]:
				if self.visited[i] == False:
					queue.append(i)
					Connected.append(i)
					self.visited[i] = True
		for x in Connected:
			if len(self.graph[x]) != 2:
				return
		self.count += 1

	def DisconnectedCover(self):
		for i in range(1, self.vertices + 1):
			if self.visited[i] == False:
				self.BFS(i)

	def printer(self):
		print(self.count)
(n, m) = [int(x) for x in input().split()]
G = Graph(n)
for i in range(m):
	(a, b) = map(int, input().split())
	G.addEdge(a, b)
G.DisconnectedCover()
G.printer()


import math, sys, bisect, heapq
from collections import defaultdict, Counter, deque
from itertools import groupby, accumulate
from functools import lru_cache
int1 = lambda x: int(x) - 1
input = iter(sys.stdin.buffer.read().decode().splitlines()).__next__
aj = lambda : list(map(int, input().split()))

def list3d(a, b, c, d):
	return [[[d] * c for j in range(b)] for i in range(a)]

def Y(c):
	print(['NO', 'YES'][c])

def y(c):
	print(['no', 'yes'][c])

def Yy(c):
	print(['No', 'Yes'][c])
G = defaultdict(list)

def addEdge(u, v):
	G[u].append(v)
	G[v].append(u)
(N, M) = aj()
for i in range(M):
	(u, v) = aj()
	addEdge(u, v)
vis1 = [False] * (N + 1)
vis2 = [False] * (N + 1)

def dfs(node):
	s = deque()
	s.append(node)
	vis1[node] = True
	f = 0
	count = 0
	while s:
		x = s.pop()
		count += 1
		g = G.get(x, [])
		if len(g) > 2:
			f = 1
		for i in g:
			if not vis1[i]:
				vis1[i] = True
				s.append(i)
	if f:
		return 0
	vis2[node] = True
	s = deque()
	s.append((node, -1))
	while s:
		(x, y) = s.pop()
		g = G.get(x, [])
		for i in g:
			if not vis2[i]:
				vis2[i] = True
				s.append((i, x))
			elif i != y:
				return 1
	return 0
ans = 0
for i in range(1, N + 1):
	if not vis1[i]:
		v = dfs(i)
		ans += v
print(ans)


(n, m) = map(int, input().split())
adj = [[] for _ in range(n)]
for _ in range(m):
	(u, v) = map(int, input().split())
	u -= 1
	v -= 1
	adj[u].append(v)
	adj[v].append(u)
out = 0
marks = [None for _ in range(n)]

def dfs(s):
	stack = [s]
	while stack:
		s = stack.pop()
		if marks[s] is None:
			marks[s] = True
			CC.append(s)
			for j in adj[s]:
				stack.append(j)

def graph_cyclic():
	for j in CC:
		degree = len(adj[j])
		if degree != 2:
			return False
	return True
cc_cyclic = 0
for v in range(n):
	if marks[v] is None:
		CC = []
		dfs(v)
		if graph_cyclic():
			cc_cyclic += 1
print(cc_cyclic)


import sys, math
input = sys.stdin.readline

def getInts():
	return [int(s) for s in input().split()]

def getInt():
	return int(input())

def getStrs():
	return [s for s in input().split()]

def getStr():
	return input().strip()

def listStr():
	return list(input().strip())
import collections as col
import math
from types import GeneratorType

def solve():

	def bootstrap(f, stack=[]):

		def wrappedfunc(*args, **kwargs):
			if stack:
				return f(*args, **kwargs)
			else:
				to = f(*args, **kwargs)
				while True:
					if type(to) is GeneratorType:
						stack.append(to)
						to = next(to)
					else:
						stack.pop()
						if not stack:
							break
						to = stack[-1].send(to)
				return to
		return wrappedfunc

	@bootstrap
	def dfs(graph, visited, node, nodes):
		if node not in visited:
			visited.add(node)
			nodes.remove(node)
		for neighbour in graph[node]:
			if neighbour not in visited:
				yield dfs(graph, visited, neighbour, nodes)
		yield (visited, nodes)
	(N, M) = getInts()
	graph = col.defaultdict(set)
	for n in range(M):
		(U, V) = getInts()
		graph[U].add(V)
		graph[V].add(U)
	nodes = set([j + 1 for j in range(N)])
	ans = 0
	while nodes:
		node = next(iter(nodes))
		if not graph[node]:
			nodes.remove(node)
			continue
		(visited, nodes) = dfs(graph, set(), node, nodes)
		flag = True
		for node in visited:
			if len(graph[node]) != 2:
				flag = False
				break
		if flag:
			ans += 1
	return ans
print(solve())


import os
import sys
from io import BytesIO, IOBase
from collections import defaultdict
_str = str
str = lambda x=b'': x if type(x) is bytes else _str(x).encode()
BUFSIZE = 8192

class FastIO(IOBase):
	newlines = 0

	def __init__(self, file):
		self._fd = file.fileno()
		self.buffer = BytesIO()
		self.writable = 'x' in file.mode or 'r' not in file.mode
		self.write = self.buffer.write if self.writable else None

	def read(self):
		while True:
			b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
			if not b:
				break
			ptr = self.buffer.tell()
			(self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
		self.newlines = 0
		return self.buffer.read()

	def readline(self):
		while self.newlines == 0:
			b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
			self.newlines = b.count(b'\n') + (not b)
			ptr = self.buffer.tell()
			(self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
		self.newlines -= 1
		return self.buffer.readline()

	def flush(self):
		if self.writable:
			os.write(self._fd, self.buffer.getvalue())
			(self.buffer.truncate(0), self.buffer.seek(0))

class IOWrapper(IOBase):

	def __init__(self, file):
		self.buffer = FastIO(file)
		self.flush = self.buffer.flush
		self.writable = self.buffer.writable
		self.write = lambda s: self.buffer.write(s.encode('ascii'))
		self.read = lambda : self.buffer.read().decode('ascii')
		self.readline = lambda : self.buffer.readline().decode('ascii')
input = lambda : sys.stdin.readline().rstrip('\r\n')

class DSU:

	def __init__(self, n):
		self.parent = [i for i in range(n)]
		self.rank = [1] * n
		self.cycle = set()

	def find(self, x):
		if self.parent[x] != x:
			self.parent[x] = self.find(self.parent[x])
		return self.parent[x]

	def union(self, a, b):
		(s1, s2) = (self.find(a), self.find(b))
		if s1 != s2:
			if self.rank[s1] < self.rank[s2]:
				self.parent[s1] = s2
				self.rank[s2] += self.rank[s1]
			else:
				self.parent[s2] = s1
				self.rank[s1] += self.rank[s2]
		else:
			x = self.find(s1)
			if x not in self.cycle:
				self.cycle.add(x)
			else:
				self.cycle.remove(x)

	@property
	def getParent(self):
		return self.parent

	@property
	def getRank(self):
		return self.rank

	@property
	def getCycle(self):
		return self.cycle

def solve():
	(n, m) = map(int, input().split())
	d = DSU(n + 1)
	degre = [0] * (n + 1)
	for _ in range(m):
		(a, b) = map(int, input().split())
		degre[a] += 1
		degre[b] += 1
		d.union(a, b)
	leader = defaultdict(list)
	cycle = d.getCycle
	for i in range(1, n + 1):
		x = d.find(i)
		if x in cycle:
			leader[x].append(degre[i] == 2)
	ans = 0
	for (k, v) in leader.items():
		if all(v):
			ans += 1
	print(ans)
solve()


(n, m) = map(int, input().split())
graph = [[] for i in range(n + 1)]
for i in range(m):
	(v_i, u_i) = map(int, input().split())
	graph[v_i].append(u_i)
	graph[u_i].append(v_i)
st = set((i for i in range(n + 1) if len(graph[i]) == 2))
res = 0
while st:
	i = st.pop()
	(v, u) = graph[i]
	while u in st:
		st.remove(u)
		(v_i, u_i) = graph[u]
		if v_i in st:
			u = v_i
		elif u_i in st:
			u = u_i
		else:
			break
		if v == u:
			res += 1
print(res)


import heapq

def findc(node):
	p = [node]
	visited[node] = True
	stack = [node]
	while stack:
		node = stack.pop()
		for child in g[node]:
			if visited[child] == False:
				visited[child] = True
				stack.append(child)
				p.append(child)
	return p
(n, m) = map(int, input().split())
g = {i: [] for i in range(1, n + 1)}
degree = [0 for i in range(n + 1)]
visited = [False for i in range(n + 1)]
for i in range(m):
	(a, b) = map(int, input().split())
	g[a].append(b)
	g[b].append(a)
	degree[a] = degree[a] + 1
	degree[b] = degree[b] + 1
c = []
for i in range(1, n + 1):
	if visited[i] == False:
		c.append(findc(i))
ans = 0
for i in c:
	f = 0
	l = 0
	for node in i:
		if degree[node] != 2:
			f = 1
			break
		else:
			l = l + 1
	if f == 0 and l == len(i):
		ans = ans + 1
print(ans)


from collections import deque

def bfs(v):
	q = deque()
	q.append(v)
	w = []
	while len(q):
		cur = q.popleft()
		w.append(cur)
		p[cur] = c
		for i in g[cur]:
			if p[i] == 0:
				q.append(i)
				p[i] = c
	k.append(w)
(n, m) = map(int, input().split())
g = [[] for i in range(n + 1)]
p = [0] * (n + 1)
k = [[]]
for i in range(m):
	(a, b) = map(int, input().split())
	g[a].append(b)
	g[b].append(a)
c = 0
for v in range(1, n + 1):
	if p[v] == 0:
		c += 1
		bfs(v)
ans = 0
for i in range(1, len(k)):
	fl = True
	for j in range(len(k[i])):
		if len(g[k[i][j]]) != 2:
			fl = False
	if fl:
		ans += 1
print(ans)


import sys
input = sys.stdin.readline
from collections import deque
mod = 998244353
(n, m) = map(int, input().split())
a = [i for i in range(n + 1)]
rank = [0 for i in range(n + 1)]
deg = [0 for i in range(n + 1)]

def find(a, i):
	if i == a[i]:
		return i
	else:
		a[i] = find(a, a[i])
		return a[i]

def union(a, x, y):
	xs = find(a, x)
	ys = find(a, y)
	if xs != ys:
		if rank[ys] > rank[xs]:
			(xs, ys) = (ys, xs)
		a[ys] = xs
		rank[xs] += 1
for i in range(m):
	(x, y) = map(int, input().split())
	deg[x] += 1
	deg[y] += 1
	union(a, x, y)
f = {}
for i in range(1, n + 1):
	if find(a, i) not in f:
		f[a[i]] = [i]
	else:
		f[a[i]].append(i)
c = 0
for i in f:
	flag = 0
	for j in f[i]:
		if j >= n + 1:
			print(-1)
			exit(0)
		if deg[j] != 2:
			flag = 1
			break
	if flag == 0:
		c += 1
print(c)


import math, string, itertools, fractions, heapq, collections, re, array, bisect, sys, copy, functools
inf = 10 ** 20
eps = 1.0 / 10 ** 10
mod = 10 ** 9 + 7
mod2 = 998244353
dd = [(-1, 0), (0, 1), (1, 0), (0, -1)]
ddn = [(-1, 0), (-1, 1), (0, 1), (1, 1), (1, 0), (1, -1), (0, -1), (-1, -1)]

def LI():
	return list(map(int, sys.stdin.readline().split()))

def LLI():
	return [list(map(int, l.split())) for l in sys.stdin.readlines()]

def LI_():
	return [int(x) - 1 for x in sys.stdin.readline().split()]

def LF():
	return [float(x) for x in sys.stdin.readline().split()]

def LS():
	return sys.stdin.readline().split()

def I():
	return int(sys.stdin.readline())

def F():
	return float(sys.stdin.readline())

def S():
	return input()

def pf(s):
	return print(s, flush=True)

def pe(s):
	return print(str(s), file=sys.stderr)

def JA(a, sep):
	return sep.join(map(str, a))

def JAA(a, s, t):
	return s.join((t.join(map(str, b)) for b in a))

def IF(c, t, f):
	return t if c else f

def YES(c):
	return IF(c, 'YES', 'NO')

def Yes(c):
	return IF(c, 'Yes', 'No')

class UnionFind:

	def __init__(self, size):
		self.table = [-1 for _ in range(size)]

	def find(self, x):
		if self.table[x] < 0:
			return x
		else:
			self.table[x] = self.find(self.table[x])
			return self.table[x]

	def union(self, x, y):
		s1 = self.find(x)
		s2 = self.find(y)
		if s1 != s2:
			if self.table[s1] <= self.table[s2]:
				self.table[s1] += self.table[s2]
				self.table[s2] = s1
			else:
				self.table[s2] += self.table[s1]
				self.table[s1] = s2
			return True
		return False

	def same(self, x, y):
		return self.find(x) == self.find(y)

	def subsetall(self):
		a = []
		for i in range(len(self.table)):
			if self.table[i] < 0:
				a.append((i, -self.table[i]))
		return a

def main():
	t = 1
	rr = []
	for _ in range(t):
		(n, m) = LI()
		uv = [LI_() for _ in range(m)]
		uf = UnionFind(n)
		e = collections.defaultdict(set)
		for (u, v) in uv:
			e[u].add(v)
			e[v].add(u)
			uf.union(u, v)
		t = [None] * n
		for i in range(n):
			if len(e[i]) != 2:
				t[uf.find(i)] = 1
		r = 0
		for i in range(n):
			if uf.find(i) == i and t[i] is None:
				r += 1
		rr.append(r)
	return JA(rr, '\n')
print(main())


import io, os
input = io.BytesIO(os.read(0, os.fstat(0).st_size)).readline
from collections import defaultdict
(n, m) = map(int, input().split())
adj = defaultdict(list)
ct = 0
for i in range(m):
	(u, v) = map(int, input().split())
	adj[u].append(v)
	adj[v].append(u)
v = [0] * (n + 1)
cmps = []
for i in range(1, n + 1):
	if not v[i]:
		q = [i]
		cmp = []
		while q:
			e = q.pop()
			v[e] = 1
			cmp.append(e)
			for j in adj[e]:
				if not v[j]:
					q.append(j)
		cmps.append(cmp)
for i in cmps:
	ok = True
	for j in i:
		if len(adj[j]) != 2:
			ok = False
	if ok:
		ct += 1
print(ct)


def find_connected(start, graph, visited, count_nodes):
	q = []
	q.append(start)
	visited[start] = True
	cycle = True
	while q:
		temp = q.pop(0)
		if cycle == True:
			cycle = count_nodes[temp] == 2
		for nb in graph[temp]:
			if visited[nb] == False:
				q.append(nb)
				visited[nb] = True
	return cycle
(n, m) = map(int, input().split())
graph = [[] for i in range(n + 1)]
count_nodes = [0 for i in range(n + 1)]
for i in range(m):
	(u, v) = map(int, input().split())
	graph[u].append(v)
	graph[v].append(u)
	count_nodes[u] += 1
	count_nodes[v] += 1
visited = [False for i in range(n + 1)]
count = 0
for i in range(1, n + 1):
	if visited[i] == False:
		cycle = find_connected(i, graph, visited, count_nodes)
		if cycle:
			count += 1
print(count)


graph = {}
(vert, edge) = map(int, input().split())
reached = set()
for _ in range(edge):
	(a, b) = map(int, input().split())
	if a not in graph.keys():
		graph.update({a: [b]})
	else:
		graph[a].append(b)
	if b not in graph.keys():
		graph.update({b: [a]})
	else:
		graph[b].append(a)
cycle = dict(filter(lambda x: len(x[1]) == 2, graph.items()))
cyc = 0
while cycle:
	target = next(iter(cycle))
	(left, right) = cycle.pop(target)
	while right in cycle.keys():
		(l1, r1) = cycle.pop(right)
		if r1 in cycle.keys():
			right = r1
		elif l1 in cycle.keys():
			right = l1
		else:
			break
		if right == left:
			cyc += 1
print(cyc)


(n, m) = [int(s) for s in input().split()]
g = [[] for x in range(n)]
for i in range(m):
	(a, b) = [int(s) for s in input().split()]
	g[a - 1].append(b - 1)
	g[b - 1].append(a - 1)
seen = [False for x in range(n)]
q = []
ans = 0
for i in range(len(g)):
	if not seen[i]:
		q.append(i)
		seen[i] = True
	is_cycle = True
	first = i
	cycle_len = 0
	while len(q) > 0:
		v = q.pop()
		cycle_len += 1
		if len(g[v]) != 2:
			is_cycle = False
		if len(g[v]) == 2 and (g[v][0] == first or g[v][1] == first) and (cycle_len > 2) and is_cycle:
			ans += 1
			break
		for vv in g[v]:
			if not seen[vv]:
				q.append(vv)
				seen[vv] = True
print(ans)


import queue
(n, m) = map(int, input().split())
adj = [[] for i in range(n + 1)]
visited = [False for i in range(n + 1)]

def bfs(u):
	st = []
	st.append(u)
	flag = len(adj[u]) == 2
	while len(st) > 0:
		u = st[-1]
		st.pop()
		for v in adj[u]:
			if visited[v] == False:
				if len(adj[v]) != 2:
					flag = False
				visited[v] = True
				st.append(v)
	return flag
for _ in range(m):
	(u, v) = map(int, input().split())
	adj[u].append(v)
	adj[v].append(u)
res = 0
for i in range(1, n + 1):
	if visited[i] == False:
		res += bfs(i) == True
print(res)


def ConnectedCopmonents(graph, vertex, isVisited):
	stack = [vertex]
	cmpts = []
	while stack:
		curr = stack.pop()
		if not isVisited[curr - 1]:
			isVisited[curr - 1] = True
			cmpts.append(curr)
			if len(graph[curr]) == 2:
				for kid in graph[curr]:
					stack.append(kid)
			else:
				return []
	return cmpts

def IsCycle(cmpts, graph):
	count = 0
	for i in range(len(cmpts)):
		if i == len(cmpts) - 1:
			if cmpts[0] in graph[cmpts[i]]:
				count += 1
		elif cmpts[i] in graph[cmpts[i + 1]]:
			count += 1
	if count == len(cmpts):
		return True
	return False

def Solve(graph):
	isVisited = [False] * len(graph.keys())
	connected_cmpts = []
	for i in graph:
		ans = ConnectedCopmonents(graph, i, isVisited)
		if ans:
			connected_cmpts.append(ans)
	count = 0
	for i in connected_cmpts:
		if len(i) >= 3 and IsCycle(i, graph):
			count += 1
	return count

def main():
	(n, m) = map(int, input().split())
	graph = {}
	for i in range(1, n + 1):
		graph[i] = []
	for i in range(m):
		(a, b) = map(int, input().split())
		graph[a].append(b)
		graph[b].append(a)
	print(Solve(graph))
main()


from collections import defaultdict, deque
from sys import *

def bfs(g, v, i, cnt):
	q = deque([i])
	usd = set()
	while q:
		t = q.popleft()
		usd.add(t)
		for j in g[t]:
			if cnt[j] != 2:
				return 0
			v[j] = False
			if j not in usd:
				q.appendleft(j)
	return 1
(n, m) = map(int, stdin.readline().split())
g = defaultdict(list)
cnt = defaultdict(int)
v = [True] * (n + 1)
d = 0
for i in range(m):
	(a, b) = map(int, stdin.readline().split())
	g[a].append(b)
	g[b].append(a)
	cnt[a] += 1
	cnt[b] += 1
for i in g:
	if v[i] == True:
		d += bfs(g, v, i, cnt)
print(d)


from collections import *

class graph:

	def __init__(self, n):
		self.n = n
		self.g = defaultdict(list)
		self.visited = [False] * (n + 1)
		self.l = list()

	def add_edge(self, u, v):
		self.g[u].append(v)
		self.g[v].append(u)

	def dfs(self, ele, a):
		queue = deque([ele])
		while queue:
			ele = queue.pop()
			self.visited[ele] = True
			a.append(ele)
			for i in self.g[ele]:
				if self.visited[i] == False:
					self.visited[i] = True
					queue.append(i)

	def cycles(self):
		for i in range(self.n):
			self.l.append(list())
			self.dfs(i + 1, self.l[-1])
			if len(self.l[-1]) == 1:
				self.l.pop()
		count = 0
		for i in self.l:
			count += 1
			for j in i:
				if len(self.g[j]) != 2:
					count -= 1
					break
		return count
(n, m) = [int(x) for x in input().split()]
a = graph(n)
while m:
	m -= 1
	(u, v) = [int(x) for x in input().split()]
	a.add_edge(u, v)
print(a.cycles())


from collections import defaultdict
(n, m) = map(int, input().split())
edges = defaultdict(list)
connected = set()
for _ in range(m):
	(v1, v2) = map(int, input().split())
	connected.add((v1, v2) if v1 < v2 else (v2, v1))
	edges[v1].append(v2)
	edges[v2].append(v1)
components = set((i for i in edges.keys() if len(edges[i]) == 2))
count = 0
while components:
	(l, r) = edges[components.pop()]
	while l in components:
		components.remove(l)
		(v1, v2) = edges[l]
		if v1 in components:
			l = v1
		elif v2 in components:
			l = v2
		else:
			break
		if l == r:
			count += 1
print(count)


from sys import stdin, stdout
from collections import Counter
st = lambda : list(stdin.readline().strip())
li = lambda : list(map(int, stdin.readline().split()))
mp = lambda : map(int, stdin.readline().split())
inp = lambda : int(stdin.readline())
pr = lambda n: stdout.write(str(n) + '\n')
mod = 1000000007
INF = float('inf')

def DFS(n):
	v[n] = True
	stack = [n]
	p = set()
	while stack:
		a = stack.pop()
		p.add(len(d[a]))
		for i in d[a]:
			if i == par[a]:
				continue
			elif not v[i]:
				v[i] = True
				stack.append(i)
				par[i] = a
	return p == {2}
(n, m) = mp()
d = {i: [] for i in range(1, n + 1)}
for i in range(m):
	(a, b) = mp()
	d[a].append(b)
	d[b].append(a)
v = [False for i in range(n + 1)]
ans = 0
par = [-1 for i in range(n + 1)]
for i in range(1, n + 1):
	if not v[i]:
		cycle = DFS(i)
		if cycle:
			ans += 1
pr(ans)


from sys import stdin
input = lambda : stdin.readline().strip()
from math import ceil, sqrt, factorial, gcd
from collections import deque

def cycle_detection(x):
	for i in x:
		if len(graph[i]) == 2:
			pass
		else:
			return 0
	return 1

def finding_graphs():
	while stack:
		x = stack.pop()
		if x not in visited:
			for i in graph[x]:
				if i not in visited:
					stack.append(i)
			visited.add(x)
			total_graph[-1].append(x)
(n, m) = map(int, input().split())
graph = {i: set() for i in range(1, n + 1)}
for i in range(m):
	(a, b) = map(int, input().split())
	graph[a].add(b)
	graph[b].add(a)
total_graph = []
visited = set()
for i in range(1, n + 1):
	if i not in visited:
		total_graph.append([])
		stack = [i]
		finding_graphs()
count = 0
for i in total_graph:
	count += cycle_detection(i)
print(count)


def bfs(a, i, visited, connected):
	visited[i] = True
	q = list(a[i])
	j = 0
	while j < len(q):
		cur = q[j]
		if not visited[cur]:
			connected.append(cur)
			visited[cur] = True
			for k in range(len(a[cur])):
				q.append(a[cur][k])
		j += 1
	return connected

def check(connected, a, n):
	if len(connected) < 3:
		return False
	for i in range(1, len(connected)):
		node = connected[i]
		if len(a[node]) != 2:
			return False
	return True
(n, m) = map(int, input().split())
a = []
visited = []
for i in range(n):
	a.append([])
	visited.append(False)
for i in range(m):
	(u, v) = map(int, input().split())
	a[u - 1].append(v - 1)
	a[v - 1].append(u - 1)
res = 0
for i in range(n):
	if not visited[i]:
		connected = bfs(a, i, visited, [i])
		if check(connected, a, n):
			res += 1
print(res)


(n, m) = list(map(int, input().strip().split()))
visited = [0] * (n + 1)
edges = [[] for _ in range(n + 1)]
for i in range(m):
	(x, y) = list(map(int, input().strip().split()))
	edges[x].append(y)
	edges[y].append(x)
res = 0
for i in range(1, n + 1):
	if visited[i]:
		continue
	visited[i] = 1
	if len(edges[i]) <= 1 or len(edges[i]) > 2:
		continue
	next = edges[i][0]
	cur = i
	while next != i and (not visited[next]):
		visited[next] = 1
		if len(edges[next]) <= 1 or len(edges[next]) > 2:
			visited[next] = 1
			break
		if edges[next][0] == cur:
			cur = next
			next = edges[next][1]
		else:
			cur = next
			next = edges[next][0]
	if next == i:
		res += 1
print(res)


import sys, threading
sys.setrecursionlimit(10 ** 7)
threading.stack_size(2 ** 26)

def dfs(v, c):
	t = threading.current_thread()
	t.color[v] = c
	good = len(t.g[v]) == 2
	for u in t.g[v]:
		if t.color[u] == -1:
			good &= dfs(u, c)
	return good

def solve():
	t = threading.current_thread()
	(n, m) = map(int, input().split())
	t.g = [[] for _ in range(n)]
	for _ in range(m):
		(u, v) = map(int, input().split())
		u -= 1
		v -= 1
		t.g[u].append(v)
		t.g[v].append(u)
	t.color = [-1] * n
	cnt = 0
	res = 0
	for v in range(n):
		if t.color[v] == -1:
			res += dfs(v, cnt)
			cnt += 1
	print(res)
thread = threading.Thread(target=solve)
thread.start()
thread.join()


from collections import deque

def dfs(graph, visited, start):
	stack = deque([start])
	visited[start] = True
	cycle = True
	while stack:
		node = stack.popleft()
		if len(graph[node]) != 2:
			cycle = False
		for i in graph[node]:
			if not visited[i]:
				stack.append(i)
				visited[i] = True
	return cycle
(n, m) = map(int, input().split())
graph = [[] for i in range(n)]
for i in range(m):
	(u, v) = map(int, input().split())
	u -= 1
	v -= 1
	graph[u].append(v)
	graph[v].append(u)
cnt = 0
visited = [False] * n
for i in range(n):
	if not visited[i] and dfs(graph, visited, i):
		cnt += 1
print(cnt)


from collections import defaultdict as dd, deque as dq

def bfs(u, vis, d):
	q = dq([u])
	cou = 0
	coud = 0
	while q:
		a = q.pop()
		vis[a] = 1
		cou += 1
		if len(d[a]) == 2:
			coud += 1
		for i in d[a]:
			if vis[i] == 0:
				q.appendleft(i)
	if cou == coud:
		return True
	return False
import sys
input = sys.stdin.readline
(n, m) = map(int, input().split())
d = dd(list)
for i in range(m):
	(u, v) = map(int, input().split())
	d[u].append(v)
	d[v].append(u)
vis = [0] * (n + 1)
cou = 0
for i in range(1, n + 1):
	if vis[i] == 0 and len(d[i]) == 2:
		if bfs(i, vis, d):
			cou += 1
print(cou)


(n, m) = map(int, input().split())
a = [0] * n
b = [0] * n
c = [0] * n
for i in range(m):
	(k, l) = map(int, input().split())
	a[k - 1] += 1
	a[l - 1] += 1
	if a[k - 1] == 1:
		b[k - 1] = l - 1
	elif a[k - 1] == 2:
		c[k - 1] = l - 1
	else:
		a[k - 1] = 3
	if a[l - 1] == 1:
		b[l - 1] = k - 1
	elif a[l - 1] == 2:
		c[l - 1] = k - 1
	else:
		a[l - 1] = 3
d = []
x = 0
for i in range(n):
	j = i
	if a[j] == 2:
		a[j] = 3
		d.append(j)
		j = b[j]
		while a[j] == 2:
			d.append(j)
			a[j] = 3
			if b[j] == d[len(d) - 2]:
				j = c[j]
			else:
				j = b[j]
			if (b[j] == d[0] or c[j] == d[0]) and a[j] == 2:
				x += 1
				break
		d = []
print(x)


class UnionFind:

	def __init__(self, N):
		self.par = [i for i in range(N)]
		self.rank = [1 for i in range(N)]
		self.rank[0] = 0

	def union(self, x, y):
		if not self.is_same_set(x, y):
			par_x = self.find_par(x)
			par_y = self.find_par(y)
			if self.rank[par_x] > self.rank[par_y]:
				self.par[par_y] = par_x
				self.rank[par_x] += self.rank[par_y]
				self.rank[par_y] = 0
			else:
				self.par[par_x] = par_y
				self.rank[par_y] += self.rank[par_x]
				self.rank[par_x] = 0

	def find_par(self, x):
		if x == self.par[x]:
			return x
		self.par[x] = self.find_par(self.par[x])
		return self.par[x]

	def is_same_set(self, x, y):
		return self.find_par(x) == self.find_par(y)

	def size(self, x):
		return self.rank[self.find_par(x)]
(v, e) = map(int, input().split())
uf = UnionFind(v + 1)
adj = [[] for i in range(v + 1)]
for i in range(e):
	(a, b) = map(int, input().split())
	uf.union(a, b)
	adj[a].append(b)
	adj[b].append(a)
nao_resps = set()
for i in range(v + 1):
	par_i = uf.find_par(i)
	if len(adj[i]) != 2:
		nao_resps.add(par_i)
print(len(set(uf.par) - nao_resps))


from __future__ import division, print_function
from fractions import Fraction
import sys
import os
from io import BytesIO, IOBase
from itertools import *
import bisect
from heapq import *
from math import ceil, floor
from copy import *
from collections import deque, defaultdict
from collections import Counter as counter
from itertools import combinations
from itertools import permutations as permutate
from bisect import bisect_left as bl
from operator import *
from bisect import bisect_right as br
from bisect import bisect
BUFSIZE = 8192
from sys import stderr

class FastIO(IOBase):
	newlines = 0

	def __init__(self, file):
		self._fd = file.fileno()
		self.buffer = BytesIO()
		self.writable = 'x' in file.mode or 'r' not in file.mode
		self.write = self.buffer.write if self.writable else None

	def read(self):
		while True:
			b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
			if not b:
				break
			ptr = self.buffer.tell()
			(self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
		self.newlines = 0
		return self.buffer.read()

	def readline(self):
		while self.newlines == 0:
			b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
			self.newlines = b.count(b'\n') + (not b)
			ptr = self.buffer.tell()
			(self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
		self.newlines -= 1
		return self.buffer.readline()

	def flush(self):
		if self.writable:
			os.write(self._fd, self.buffer.getvalue())
			(self.buffer.truncate(0), self.buffer.seek(0))

class IOWrapper(IOBase):

	def __init__(self, file):
		self.buffer = FastIO(file)
		self.flush = self.buffer.flush
		self.writable = self.buffer.writable
		self.write = lambda s: self.buffer.write(s.encode('ascii'))
		self.read = lambda : self.buffer.read().decode('ascii')
		self.readline = lambda : self.buffer.readline().decode('ascii')

def print(*args, **kwargs):
	(sep, file) = (kwargs.pop('sep', ' '), kwargs.pop('file', sys.stdout))
	at_start = True
	for x in args:
		if not at_start:
			file.write(sep)
		file.write(str(x))
		at_start = False
	file.write(kwargs.pop('end', '\n'))
	if kwargs.pop('flush', False):
		file.flush()
if sys.version_info[0] < 3:
	(sys.stdin, sys.stdout) = (FastIO(sys.stdin), FastIO(sys.stdout))
else:
	(sys.stdin, sys.stdout) = (IOWrapper(sys.stdin), IOWrapper(sys.stdout))
from types import GeneratorType

def iterative(f, stack=[]):

	def wrapped_func(*args, **kwargs):
		if stack:
			return f(*args, **kwargs)
		to = f(*args, **kwargs)
		while True:
			if type(to) is GeneratorType:
				stack.append(to)
				to = next(to)
				continue
			stack.pop()
			if not stack:
				break
			to = stack[-1].send(to)
		return to
	return wrapped_func

class SortedList:

	def __init__(self, iterable=[], _load=200):
		values = sorted(iterable)
		self._len = _len = len(values)
		self._load = _load
		self._lists = _lists = [values[i:i + _load] for i in range(0, _len, _load)]
		self._list_lens = [len(_list) for _list in _lists]
		self._mins = [_list[0] for _list in _lists]
		self._fen_tree = []
		self._rebuild = True

	def _fen_build(self):
		self._fen_tree[:] = self._list_lens
		_fen_tree = self._fen_tree
		for i in range(len(_fen_tree)):
			if i | i + 1 < len(_fen_tree):
				_fen_tree[i | i + 1] += _fen_tree[i]
		self._rebuild = False

	def _fen_update(self, index, value):
		if not self._rebuild:
			_fen_tree = self._fen_tree
			while index < len(_fen_tree):
				_fen_tree[index] += value
				index |= index + 1

	def _fen_query(self, end):
		if self._rebuild:
			self._fen_build()
		_fen_tree = self._fen_tree
		x = 0
		while end:
			x += _fen_tree[end - 1]
			end &= end - 1
		return x

	def _fen_findkth(self, k):
		_list_lens = self._list_lens
		if k < _list_lens[0]:
			return (0, k)
		if k >= self._len - _list_lens[-1]:
			return (len(_list_lens) - 1, k + _list_lens[-1] - self._len)
		if self._rebuild:
			self._fen_build()
		_fen_tree = self._fen_tree
		idx = -1
		for d in reversed(range(len(_fen_tree).bit_length())):
			right_idx = idx + (1 << d)
			if right_idx < len(_fen_tree) and k >= _fen_tree[right_idx]:
				idx = right_idx
				k -= _fen_tree[idx]
		return (idx + 1, k)

	def _delete(self, pos, idx):
		_lists = self._lists
		_mins = self._mins
		_list_lens = self._list_lens
		self._len -= 1
		self._fen_update(pos, -1)
		del _lists[pos][idx]
		_list_lens[pos] -= 1
		if _list_lens[pos]:
			_mins[pos] = _lists[pos][0]
		else:
			del _lists[pos]
			del _list_lens[pos]
			del _mins[pos]
			self._rebuild = True

	def _loc_left(self, value):
		if not self._len:
			return (0, 0)
		_lists = self._lists
		_mins = self._mins
		(lo, pos) = (-1, len(_lists) - 1)
		while lo + 1 < pos:
			mi = lo + pos >> 1
			if value <= _mins[mi]:
				pos = mi
			else:
				lo = mi
		if pos and value <= _lists[pos - 1][-1]:
			pos -= 1
		_list = _lists[pos]
		(lo, idx) = (-1, len(_list))
		while lo + 1 < idx:
			mi = lo + idx >> 1
			if value <= _list[mi]:
				idx = mi
			else:
				lo = mi
		return (pos, idx)

	def _loc_right(self, value):
		if not self._len:
			return (0, 0)
		_lists = self._lists
		_mins = self._mins
		(pos, hi) = (0, len(_lists))
		while pos + 1 < hi:
			mi = pos + hi >> 1
			if value < _mins[mi]:
				hi = mi
			else:
				pos = mi
		_list = _lists[pos]
		(lo, idx) = (-1, len(_list))
		while lo + 1 < idx:
			mi = lo + idx >> 1
			if value < _list[mi]:
				idx = mi
			else:
				lo = mi
		return (pos, idx)

	def add(self, value):
		_load = self._load
		_lists = self._lists
		_mins = self._mins
		_list_lens = self._list_lens
		self._len += 1
		if _lists:
			(pos, idx) = self._loc_right(value)
			self._fen_update(pos, 1)
			_list = _lists[pos]
			_list.insert(idx, value)
			_list_lens[pos] += 1
			_mins[pos] = _list[0]
			if _load + _load < len(_list):
				_lists.insert(pos + 1, _list[_load:])
				_list_lens.insert(pos + 1, len(_list) - _load)
				_mins.insert(pos + 1, _list[_load])
				_list_lens[pos] = _load
				del _list[_load:]
				self._rebuild = True
		else:
			_lists.append([value])
			_mins.append(value)
			_list_lens.append(1)
			self._rebuild = True

	def discard(self, value):
		_lists = self._lists
		if _lists:
			(pos, idx) = self._loc_right(value)
			if idx and _lists[pos][idx - 1] == value:
				self._delete(pos, idx - 1)

	def remove(self, value):
		_len = self._len
		self.discard(value)
		if _len == self._len:
			raise ValueError('{0!r} not in list'.format(value))

	def pop(self, index=-1):
		(pos, idx) = self._fen_findkth(self._len + index if index < 0 else index)
		value = self._lists[pos][idx]
		self._delete(pos, idx)
		return value

	def bisect_left(self, value):
		(pos, idx) = self._loc_left(value)
		return self._fen_query(pos) + idx

	def bisect_right(self, value):
		(pos, idx) = self._loc_right(value)
		return self._fen_query(pos) + idx

	def count(self, value):
		return self.bisect_right(value) - self.bisect_left(value)

	def __len__(self):
		return self._len

	def __getitem__(self, index):
		(pos, idx) = self._fen_findkth(self._len + index if index < 0 else index)
		return self._lists[pos][idx]

	def __delitem__(self, index):
		(pos, idx) = self._fen_findkth(self._len + index if index < 0 else index)
		self._delete(pos, idx)

	def __contains__(self, value):
		_lists = self._lists
		if _lists:
			(pos, idx) = self._loc_left(value)
			return idx < len(_lists[pos]) and _lists[pos][idx] == value
		return False

	def __iter__(self):
		return (value for _list in self._lists for value in _list)

	def __reversed__(self):
		return (value for _list in reversed(self._lists) for value in reversed(_list))

	def __repr__(self):
		return 'SortedList({0})'.format(list(self))
mod = 1000000007

def inp():
	return sys.stdin.readline().rstrip('\r\n')

def out(var):
	sys.stdout.write(str(var))

def lis():
	return list(map(int, inp().split()))

def stringlis():
	return list(map(str, inp().split()))

def sep():
	return map(int, inp().split())

def strsep():
	return map(str, inp().split())

def fsep():
	return map(float, inp().split())

def nextline():
	out('\n')

def testcase(t):
	for p in range(t):
		solve()

def pow(x, y, p):
	res = 1
	x = x % p
	if x == 0:
		return 0
	while y > 0:
		if y & 1 == 1:
			res = res * x % p
		y = y >> 1
		x = x * x % p
	return res
from functools import reduce

def factors(n):
	return set(reduce(list.__add__, ([i, n // i] for i in range(1, int(n ** 0.5) + 1) if n % i == 0)))

def gcd(a, b):
	if a == b:
		return a
	while b > 0:
		(a, b) = (b, a % b)
	return a

def prefix_sum(ar):
	return list(accumulate(ar))

def suffix_sum(ar):
	return list(accumulate(ar[::-1]))[::-1]

def N():
	return int(inp())
dx = [0, 0, 1, -1]
dy = [1, -1, 0, 0]

def YES():
	print('YES')

def NO():
	print('NO')

def Yes():
	print('Yes')

def No():
	print('No')
from collections import defaultdict

def numberOfSetBits(i):
	i = i - (i >> 1 & 1431655765)
	i = (i & 858993459) + (i >> 2 & 858993459)
	return ((i + (i >> 4) & 252645135) * 16843009 & 4294967295) >> 24

class MergeFind:

	def __init__(self, n):
		self.parent = list(range(n))
		self.size = [1] * n
		self.num_sets = n

	def find(self, a):
		to_update = []
		while a != self.parent[a]:
			to_update.append(a)
			a = self.parent[a]
		for b in to_update:
			self.parent[b] = a
		return self.parent[a]

	def merge(self, a, b):
		a = self.find(a)
		b = self.find(b)
		if a == b:
			return
		if self.size[a] < self.size[b]:
			(a, b) = (b, a)
		self.num_sets -= 1
		self.parent[b] = a
		self.size[a] += self.size[b]

	def set_size(self, a):
		return self.size[self.find(a)]

	def __len__(self):
		return self.num_sets

def lcm(a, b):
	return abs(a // gcd(a, b) * b)

def solve():

	class MergeFind:

		def __init__(self, n):
			self.parent = list(range(n))
			self.size = [1] * n
			self.num_sets = n
			self.lista = [[_] for _ in range(n)]

		def find(self, a):
			to_update = []
			while a != self.parent[a]:
				to_update.append(a)
				a = self.parent[a]
			for b in to_update:
				self.parent[b] = a
			return self.parent[a]

		def merge(self, a, b):
			a = self.find(a)
			b = self.find(b)
			if a == b:
				return
			if self.size[a] < self.size[b]:
				(a, b) = (b, a)
			self.num_sets -= 1
			self.parent[b] = a
			self.size[a] += self.size[b]
			self.lista[a] += self.lista[b]
			self.lista[b] = []

		def set_size(self, a):
			return self.size[self.find(a)]

		def __len__(self):
			return self.num_sets
	(n, m) = sep()
	degree = [0] * n
	d = MergeFind(n)
	for _ in range(m):
		(a, b) = sep()
		a -= 1
		b -= 1
		d.merge(a, b)
		degree[a] += 1
		degree[b] += 1
	c = 0
	for i in range(n):
		if d.find(i) == i:
			temp = [degree[i] for i in d.lista[i]]
			if temp.count(2) == len(temp):
				c += 1
	print(c)
solve()


from collections import defaultdict
import io, os
input = io.BytesIO(os.read(0, os.fstat(0).st_size)).readline
(v, e) = map(int, input().split())
graph = defaultdict(list)
for i in range(e):
	(a, b) = map(int, input().split())
	graph[a].append(b)
	graph[b].append(a)
vis = [False] * (v + 1)
vis[0] = True
res = []
for i in range(1, v + 1):
	if not vis[i]:
		q = [i]
		ans = []
		while q:
			e = q.pop()
			vis[e] = True
			ans.append(e)
			for j in graph[e]:
				if not vis[j]:
					q.append(j)
		res.append(ans)
count = 0
for i in res:
	ok = True
	for j in i:
		if len(graph[j]) != 2:
			ok = False
	if ok:
		count += 1
print(count)


(n, m) = map(int, input().split())
friend = [[] for i in range(n)]
for i in range(m):
	(u, v) = map(int, input().split())
	friend[u - 1].append(v - 1)
	friend[v - 1].append(u - 1)
step = [len(friend[i]) for i in range(n)]
s = set()
ans = 0
ass = s.add
for i in range(n):
	if i in s:
		continue
	Q = i
	k = 1
	ass(Q)
	while True:
		if step[Q] != 2:
			ass(Q)
			break
		elif friend[Q][0] not in s:
			Q = friend[Q][0]
			ass(Q)
			k += 1
		elif friend[Q][1] not in s:
			Q = friend[Q][1]
			ass(Q)
			k += 1
		elif friend[Q][0] == i or friend[Q][1] == i:
			if k > 2:
				ans += 1
			break
		else:
			ass(Q)
			break
print(ans)


input = __import__('sys').stdin.readline
(n, m) = map(int, input().split())
e = [[] for i in range(n + 1)]
for i in range(m):
	(u, v) = map(int, input().split())
	e[u].append(v)
	e[v].append(u)
sol = 0
used = [False for i in range(n + 1)]
for i in range(1, n):
	if used[i]:
		continue
	cc = []
	q = [i]
	used[i] = True
	while len(q):
		u = q.pop()
		cc.append(u)
		for v in e[u]:
			if not used[v]:
				q.append(v)
				used[v] = True
	ce = 0
	for u in cc:
		ce += len(e[u]) == 2
	if ce == len(cc):
		sol += 1
print(sol)


(n, m) = map(int, input().split())
G = [0] * n
parent = list(range(n))

def FindAncester(i):
	if i == parent[i]:
		pass
	else:
		parent[i] = FindAncester(parent[i])
	return parent[i]

def MergeTwo(i, j):
	A = FindAncester(i)
	B = FindAncester(j)
	parent[B] = parent[A]
for i in range(m):
	(a, b) = map(int, input().split())
	(a, b) = (a - 1, b - 1)
	if a > b:
		(a, b) = (b, a)
	G[a] += 1
	G[b] += 1
	MergeTwo(a, b)
R = {}
count = 0
for i in range(n):
	a = FindAncester(i)
	if a in R:
		R[a].append(i)
	else:
		R[a] = [i]
for i in R:
	if len(R[i]) > 2:
		x = 1
		for j in R[i]:
			if G[j] != 2:
				x = 0
				break
		if x == 1:
			count += 1
print(count)


from collections import deque
(n, m) = map(int, input().split())
G = {i: [] for i in range(n)}
for _ in range(m):
	(u, v) = map(int, input().split())
	G[u - 1].append(v - 1)
	G[v - 1].append(u - 1)
answer = 0
visited = [False] * n
for node in G:
	if not visited[node] and len(G[node]) == 2:
		component = []
		to_visit = deque([node])
		visited[node] = True
		while to_visit:
			x = to_visit.popleft()
			component.append(x)
			for neighbour in G[x]:
				if not visited[neighbour]:
					to_visit.append(neighbour)
					visited[neighbour] = True
		if all([len(G[node]) == 2 for node in component]):
			answer += 1
print(answer)


from sys import stdin
from collections import deque
(nV, nE) = map(int, stdin.readline().split())
g = [[] for _ in range(nV)]
for _ in range(nE):
	(u, v) = map(lambda x: int(x) - 1, stdin.readline().split())
	g[u].append(v)
	g[v].append(u)

def bfs(v):
	visited[v] = True
	q = deque([v])
	res = True
	while q:
		cur = q.pop()
		res &= len(g[cur]) == 2
		for to in g[cur]:
			if not visited[to]:
				visited[to] = True
				q.appendleft(to)
	return res

def dfs(v):
	visited[v] = True
	res = len(g[v]) == 2
	for to in g[v]:
		if not visited[to]:
			res &= dfs(to)
	return res
visited = [False] * nV
ret = 0
for v in range(nV):
	if not visited[v]:
		ret += bfs(v)
print(ret)


from collections import deque
from sys import stdin
input = stdin.readline
(n, m) = map(int, input().split())
arr = [[] for i in range(n + 1)]
for i in range(m):
	(u, v) = map(int, input().split())
	arr[u].append(v)
	arr[v].append(u)
vis = [-1] * (n + 1)
count = 0
for i in range(1, n + 1):
	if vis[i] == -1:
		l = []
		q = deque()
		q.append(i)
		while len(q) != 0:
			x = q.popleft()
			l.append(x)
			vis[x] = 1
			for ele in arr[x]:
				if vis[ele] == -1:
					q.append(ele)
		flag = True
		for ele in l:
			if len(arr[ele]) != 2:
				flag = False
				break
		if flag:
			count = count + 1
print(count)


from collections import defaultdict
import sys
input = __import__('sys').stdin.readline

def dfs(node):
	global adj
	rtn = len(adj[node]) == 2
	stack = []
	stack.append(node)
	while stack:
		node = stack.pop()
		vis[node] = True
		rtn &= len(adj[node]) == 2
		for to in adj[node]:
			if not vis[to]:
				stack.append(to)
	return rtn
(n, m) = map(int, input().split())
adj = defaultdict(list)
cnt = [0] * n
for i in range(m):
	(u, v) = map(int, input().split())
	u -= 1
	v -= 1
	adj[u].append(v)
	adj[v].append(u)
	cnt[u] += 1
	cnt[v] += 1
vis = [False] * n
ans = 0
for i in range(n):
	if not vis[i]:
		cur = []
		ok = dfs(i)
		if ok:
			ans += 1
print(ans)


from sys import stdin
import sys
sys.setrecursionlimit(200001)

def dfs(s):
	global used, dictt, cont
	stack = []
	stack.append(s)
	while len(stack) > 0:
		s = stack[-1]
		stack.pop()
		if not used[s]:
			cont.append(s)
			used[s] = True
			if s in dictt:
				for node in dictt[s]:
					if not used[node]:
						stack.append(node)
global dictt
dictt = {}
(n, m) = map(int, stdin.readline().split())
if m == 0:
	print('0')
	sys.exit()
for i in range(m):
	(u, v) = map(int, stdin.readline().split())
	if u not in dictt:
		dictt[u] = [v]
	else:
		dictt[u].append(v)
	if v not in dictt:
		dictt[v] = [u]
	else:
		dictt[v].append(u)
global used, cont
count = 0
used = [False] * (n + 1)
for i in range(1, n + 1):
	if not used[i]:
		cont = []
		dfs(i)
		for j in cont:
			if j not in dictt or len(dictt[j]) != 2:
				break
		else:
			count += 1
print(count)


(n, m) = map(int, input().split())
b = [[] for i in range(n + 1)]
for i in range(m):
	(v1, v2) = map(int, input().split())
	b[v1].append(v2)
	b[v2].append(v1)
temp = set((i for i in range(n + 1) if len(b[i]) == 2))
res = 0
while temp:
	v = temp.pop()
	(R, L) = b[v]
	while L in temp:
		temp.remove(L)
		(V1, V2) = b[L]
		if V1 in temp:
			L = V1
		elif V2 in temp:
			L = V2
		else:
			break
		if L == R:
			res += 1
print(res)


(n, m) = map(int, input().split())
g = [[] for _ in range(n)]
for _ in range(m):
	(a, b) = map(lambda x: int(x) - 1, input().split())
	g[a].append(b)
	g[b].append(a)
from collections import deque

def is_cycle(n, g, visited, start):
	cycle = True
	deq = deque([start])
	visited[start] = 1
	while deq:
		node = deq.popleft()
		if len(g[node]) != 2:
			cycle = False
		for nb in g[node]:
			if not visited[nb]:
				deq.append(nb)
				visited[nb] = 1
	return cycle

def solve(n, g):
	count = 0
	visited = [0] * n
	for node in range(n):
		if not visited[node] and is_cycle(n, g, visited, node):
			count += 1
	return count
print(solve(n, g))


import sys
sys.setrecursionlimit(10 ** 9)
import threading
threading.stack_size(67108864)

def main():
	(n, m) = [int(x) for x in input().split()]
	graph = []
	for i in range(n):
		graph.append([])
	deg = [0] * n
	for i in range(m):
		(a, b) = [int(x) - 1 for x in input().split()]
		deg[a] += 1
		deg[b] += 1
		graph[a].append(b)
		graph[b].append(a)
	used = [0] * n

	def dfs(v):
		is_cir = 1
		if deg[v] != 2:
			is_cir = 0
		for child in graph[v]:
			if not used[child]:
				used[child] = 1
				is_cir = min(dfs(child), is_cir)
		return is_cir
	head = 0
	count = 0
	while 1:
		used[head] = 1
		is_cir = dfs(head)
		count += is_cir
		f = 0
		for i in range(head, n):
			if not used[i]:
				head = i
				f = 1
				break
		if not f:
			print(count)
			exit()
thread = threading.Thread(target=main)
thread.start()
thread.join()


from sys import stdin, stdout

class SOLVE:

	def bfs(self, src, node, vis):
		vis[src] = 1
		queue = [src]
		flg = 1
		while len(queue):
			u = queue[0]
			del queue[0]
			if len(node[u]) != 2:
				flg = 0
			for v in node[u]:
				if vis[v] == 0:
					vis[v] = 1
					queue.append(v)
		return flg

	def solve(self):
		R = stdin.readline
		W = stdout.write
		(n, m) = [int(x) for x in R().split()]
		node = [[] for i in range(n + 1)]
		for i in range(m):
			(u, v) = [int(x) for x in R().split()]
			node[u].append(v)
			node[v].append(u)
		vis = [0 for i in range(n + 1)]
		cycles = 0
		for i in range(1, n + 1):
			if vis[i] == 0:
				cycles += self.bfs(i, node, vis)
		W('%d\n' % cycles)
		return 0

def main():
	s = SOLVE()
	s.solve()
main()


(n, m) = map(int, input().split())
adj = [[] for _ in range(n)]
for _ in range(m):
	(u, v) = map(int, input().split())
	u -= 1
	v -= 1
	adj[u].append(v)
	adj[v].append(u)
out = 0
marks = [None for _ in range(n)]

def dfs(x):
	queue = [x]
	while queue:
		x = queue.pop()
		if marks[x] is not None:
			continue
		marks[x] = True
		path.append(x)
		for j in adj[x]:
			queue.append(j)

def graph_cyclic():
	for j in path:
		degree = len(adj[j])
		if degree != 2:
			return False
	return True
cc_cyclic = 0
cur_comp = 0
for i in range(n):
	if marks[i] is None:
		path = []
		dfs(i)
		if graph_cyclic():
			cc_cyclic += 1
		cur_comp += 1
print(cc_cyclic)


import sys
from math import *

def minp():
	return sys.stdin.readline().strip()

def mint():
	return int(minp())

def mints():
	return map(int, minp().split())
(n, m) = mints()
e = [[] for i in range(n)]
for i in range(m):
	(a, b) = mints()
	e[a - 1].append(b - 1)
	e[b - 1].append(a - 1)
q = [0] * n
w = [True] * n
ql = 0
qr = 0
r = 0
for i in range(n):
	if w[i]:
		w[i] = False
		q[qr] = i
		qr += 1
		ok = True
		while ql < qr:
			x = q[ql]
			ql += 1
			if len(e[x]) != 2:
				ok = False
			for v in e[x]:
				if w[v]:
					w[v] = False
					q[qr] = v
					qr += 1
		if ok:
			r += 1
print(r)


import sys
import math
from collections import defaultdict
try:
	sys.stdin = open('input.txt', 'r')
	sys.stdout = open('output.txt', 'w')
except:
	pass

class DisjointSet:

	def __init__(self, n):
		self.parent = [0]
		for i in range(1, n + 1):
			self.parent.append(i)

	def find(self, node):
		node_copy = node
		while node != self.parent[node]:
			node = self.parent[node]
		parent_node = node
		while node_copy != self.parent[node_copy]:
			next_node = self.parent[node_copy]
			self.parent[node_copy] = parent_node
			node_copy = next_node
		return parent_node

	def union(self, u, v):
		leader_u = self.find(u)
		leader_v = self.find(v)
		if leader_u != leader_v:
			self.parent[leader_u] = leader_v
(n, m) = [int(x) for x in input().split()]
ds = DisjointSet(n)
cycled_components = set()
degree = defaultdict(lambda : 0)
for i in range(m):
	(u, v) = [int(x) for x in input().split()]
	degree[u] += 1
	degree[v] += 1
	uL = ds.find(u)
	vL = ds.find(v)
	if uL == vL:
		if uL not in cycled_components:
			cycled_components.add(uL)
	elif uL not in cycled_components and vL not in cycled_components:
		ds.union(u, v)
for i in range(1, n + 1):
	iL = ds.find(i)
	if iL in cycled_components:
		if degree[i] != 2:
			cycled_components.remove(iL)
print(len(cycled_components))


import sys, math
from collections import deque, defaultdict
import operator as op
from functools import reduce
from itertools import permutations
I = sys.stdin.readline

def ii():
	return int(I().strip())

def li():
	return list(map(int, I().strip().split()))

def mi():
	return map(int, I().strip().split())

def slist():
	return list(I().strip().split())

def ncr(n, r):
	r = min(r, n - r)
	numer = reduce(op.mul, range(n, n - r, -1), 1)
	denom = reduce(op.mul, range(1, r + 1), 1)
	return numer // denom

def gcd(x, y):
	while y:
		(x, y) = (y, x % y)
	return x

def isPrime(n):
	if n <= 1:
		return False
	elif n <= 2:
		return True
	else:
		for i in range(2, int(n ** 0.5) + 1):
			if n % i == 0:
				return False
		return True

def dfs(x, visited, d, cnt):
	visited[x] = cnt
	for i in range(len(d[x])):
		if visited[d[x][i]] == 0:
			dfs(d[x][i], visited, d, cnt)

def main():
	d = defaultdict(list)
	(n, e) = mi()
	visited = [0] * (n + 1)
	for i in range(e):
		(u, v) = mi()
		d[u].append(v)
		d[v].append(u)
	cnt = 1
	for i in range(1, n + 1):
		if visited[i] == 0:
			stack = [i]
			while len(stack) != 0:
				x = stack.pop()
				visited[x] = cnt
				for j in d[x]:
					if visited[j] == 0:
						stack.append(j)
			cnt += 1
	my_d = defaultdict(list)
	for i in range(1, n + 1):
		my_d[visited[i]].append(i)
	ans = 0
	for i in my_d:
		if len(my_d[i]) > 2:
			flag = 1
			for j in my_d[i]:
				if len(d[j]) != 2:
					flag = 0
					break
			if flag:
				ans += 1
	print(ans)
main()


[n, m] = map(int, input().strip().split())
bis = [tuple(map(int, input().strip().split())) for _ in range(m)]
tos = [[] for _ in range(n)]
for (u, v) in bis:
	tos[u - 1].append(v - 1)
	tos[v - 1].append(u - 1)
cands = set((i for i in range(n) if len(tos[i]) == 2))
res = 0
while cands:
	v = cands.pop()
	(R, L) = tos[v]
	while L in cands:
		cands.remove(L)
		(V1, V2) = tos[L]
		if V1 in cands:
			L = V1
		elif V2 in cands:
			L = V2
		else:
			break
		if L == R:
			res += 1
print(res)


(N, M) = map(int, input().split())
G = {}
for i in range(M):
	(a, b) = map(int, input().split())
	if a not in G:
		G[a] = set()
	if b not in G:
		G[b] = set()
	G[a].add(b)
	G[b].add(a)

def dfs(graph, start):
	visited = set()
	stack = [start]
	while stack:
		node = stack.pop()
		if node not in visited:
			visited.add(node)
			for i in graph[node]:
				stack.append(i)
	return visited
arr = []
seen = set()
for x in G:
	if x not in seen:
		P = dfs(G, x)
		seen |= P
		arr.append((list(P)[0], P))
cnt = 0
for x in arr:
	if x[0] in x[1] and all([len(G[y]) == 2 for y in x[1]]):
		cnt += 1
print(cnt)


import collections
[n, m] = list(map(int, input().split()))
graph = {}
for _ in range(m):
	[v_i, u_i] = list(map(int, input().split()))
	if v_i not in graph:
		graph[v_i] = [u_i]
	else:
		graph[v_i].append(u_i)
	if u_i not in graph:
		graph[u_i] = [v_i]
	else:
		graph[u_i].append(v_i)
visited = set()

def is_valid(start_node):
	deq = collections.deque()
	deq.append(start_node)
	valid = True
	while deq:
		l = len(deq)
		for _ in range(l):
			node = deq.popleft()
			if node in visited:
				continue
			visited.add(node)
			if len(graph[node]) != 2:
				valid = False
			for child in graph[node]:
				deq.append(child)
	return valid
conn_comp = 0
for start_node in graph:
	if start_node in visited:
		continue
	if is_valid(start_node):
		conn_comp += 1
print(conn_comp)


import sys, re, math
from collections import deque, defaultdict, Counter, OrderedDict
from math import ceil, sqrt, hypot, factorial, pi, sin, cos, radians
from heapq import heappush, heappop, heapify, nlargest, nsmallest

def STR():
	return list(input())

def INT():
	return int(input())

def MAP():
	return map(int, input().split())

def LIST():
	return list(map(int, input().split()))

def list2d(a, b, c):
	return [[c] * b for i in range(a)]

def sortListWithIndex(listOfTuples, idx):
	return sorted(listOfTuples, key=lambda x: x[idx])

def sortDictWithVal(passedDic):
	temp = sorted(passedDic.items(), key=lambda kv: (kv[1], kv[0]))
	toret = {}
	for tup in temp:
		toret[tup[0]] = tup[1]
	return toret

def sortDictWithKey(passedDic):
	return dict(OrderedDict(sorted(passedDic.items())))
sys.setrecursionlimit(10 ** 9)
INF = float('inf')
mod = 10 ** 9 + 7
global vis, temp
temp = set()
(n, m) = MAP()
graph = defaultdict(list)
for _ in range(m):
	(temp1, temp2) = MAP()
	graph[temp1].append(temp2)
	graph[temp2].append(temp1)
vis = [False] * (n + 1)
ans = 0
for i in graph:
	if vis[i] == False:
		res = True
		stack = [i]
		vis[i] = True
		while stack:
			node = stack.pop()
			if len(graph[node]) == 2:
				for child in graph[node]:
					if vis[child] == False:
						stack.append(child)
						vis[child] = True
					elif len(graph[child]) != 2:
						res &= False
			else:
				res &= False
		if res:
			ans += 1
print(ans)


import sys
input = sys.stdin.readline
(n, m) = map(int, input().split())
dic = {}
for i in range(1, n + 1):
	dic[i] = set({})
for i in range(m):
	(u, v) = map(int, input().split())
	dic[u].add(v)
	dic[v].add(u)
se = set({})
ans = 0
for i in range(1, n + 1):
	if not i in se:
		se.add(i)
		ch = {i}
		f1 = False
		f2 = True
		while ch:
			ne = set({})
			for j in ch:
				xx = dic[j].copy()
				le = len(xx)
				if le != 2:
					f2 = False
				for k in xx:
					if k in se:
						f1 = True
					else:
						se.add(k)
						ne.add(k)
			ch = ne.copy()
		if f1 and f2:
			ans += 1
print(ans)


GI = lambda : int(input())
GIS = lambda : map(int, input().split())
LGIS = lambda : list(GIS())

def main():
	(n, m) = GIS()
	ns = {}
	for i in range(m):
		(a, b) = GIS()
		for (i, j) in ((a, b), (b, a)):
			ns.setdefault(i, []).append(j)
	visited = set()
	cycles = 0
	for v in range(1, n + 1):
		if v in visited:
			continue
		visited.add(v)
		if len(ns.get(v, [])) != 2:
			continue
		first_v = last_v = v
		v = ns[v][0]
		while v != first_v:
			if v in visited or len(ns[v]) != 2:
				break
			visited.add(v)
			for nv in ns[v]:
				if nv != last_v:
					break
			(v, last_v) = (nv, v)
		else:
			cycles += 1
	print(cycles)
main()


import math, sys
from collections import Counter, defaultdict

def li():
	return list(map(int, sys.stdin.readline().split()))

def ls():
	return list(map(int, list(input())))

def la():
	return list(input())

def ii():
	return int(input())

def dic(x):
	return defaultdict(lambda : x)

def isPrime(n):
	i = 2
	if n == 1:
		return False
	while i <= int(math.sqrt(n)):
		if n % i == 0:
			return False
		i = i + 1
	return True

def gcd(a, b):
	if b == 0:
		return a
	return gcd(b, a % b)

def binarySearch(arr, x):
	(l, r) = (0, len(arr) - 1)
	while l <= r:
		mid = l + (r - l) // 2
		if arr[mid] == x:
			return True
		elif arr[mid] < x:
			l = mid + 1
		else:
			r = mid - 1
	return False
(n, m) = li()
g = defaultdict(set)
for _ in range(m):
	(x, y) = li()
	g[x].add(y)
	g[y].add(x)
visited = [False] * (n + 1)
connected = 0

def bfs(v, cyclic):
	queue = [v]
	visited[v] = True
	while len(queue) != 0:
		front = queue.pop()
		if len(g[front]) != 2:
			cyclic = False
		for c in g[front]:
			if not visited[c]:
				visited[c] = True
				queue.append(c)
	return cyclic
cyclic = 0
for i in range(1, n + 1):
	if not visited[i]:
		connected += 1
		c = True
		if bfs(i, c):
			cyclic += 1
print(cyclic)


from collections import defaultdict
graph = defaultdict(list)

def count_cc():
	cc = 0
	for i in range(1, n + 1):
		if not visited[i]:
			if dfs(i):
				cc += 1
	return cc

def dfs(i):
	stack = [i]
	cc_size = 0
	cycle = True
	while len(stack):
		t = stack.pop()
		cc_size += 1
		if not visited[t]:
			visited[t] = True
			if len(graph[t]) != 2:
				cycle = False
			for adj in graph[t]:
				stack.append(adj)
	if cc_size > 2 and cycle:
		return True
	return False

def add_edge(i, j):
	graph[i].append(j)
(n, m) = list(map(int, input().split()))
for e in range(0, m):
	(i, j) = list(map(int, input().split()))
	add_edge(i, j)
	add_edge(j, i)
visited = [False for node in range(n + 1)]
print(count_cc())


__file__ = __file__.startswith('/home/a_farazei')

def input_():
	global __file__
	if not __file__:
		return input()
	if not hasattr(__file__, 'readline'):
		__file__ = open('input.txt', 'r')
	return __file__.readline()
ans = 0
(n, m) = map(int, input_().split())
g = [[] for _ in range(n + 1)]
u = [0] * (n + 1)

def dfs(v):
	s = [(v, 0)]
	(max_k, max_v) = (-1, -1)
	while len(s):
		(v, k) = s.pop()
		u[v] = 1
		if k > max_k:
			(max_k, max_v) = (k, v)
		if len(g[v]) == 2:
			s += [(x, k + 1) for x in g[v] if not u[x]][:1]
		else:
			return (-1, -1)
	return (max_v, max_k)
for _ in range(m):
	(a, b) = map(int, input_().split())
	g[a].append(b)
	g[b].append(a)
for i in range(1, n + 1):
	if not u[i]:
		(v, k) = dfs(i)
		if k > 1 and i in g[v]:
			ans += 1
print(ans)


import sys, threading

def main():
	(n, m) = map(int, input().split())
	adj = [[] for i in range(n)]
	for i in range(m):
		(a, b) = map(int, input().split())
		adj[a - 1].append(b - 1)
		adj[b - 1].append(a - 1)
	visited = [False] * n
	if n < 3:
		print(0)
		return

	def dfs(s, p):
		par = 0
		if not visited[s]:
			visited[s] = True
		for u in adj[s]:
			par += 1
			if not visited[u]:
				dfs(u, s)
		if par == 2:
			tab.append(True)
		else:
			tab.append(False)
		return tab
	n_comp = 0
	n_comp_cycle = 0
	while not all(visited):
		for i in range(n):
			tab = []
			if not visited[i]:
				n_comp_cycle += all(dfs(i, -1))
				n_comp += 1
	print(n_comp_cycle)
	return
sys.setrecursionlimit(10 ** 6)
threading.stack_size(10 ** 8)
t = threading.Thread(target=main)
t.start()
t.join()


import sys
input = sys.stdin.readline
from collections import deque
(n, m) = map(int, input().split())
graph = [[] for i in range(n + 1)]
for i in range(m):
	(a, b) = map(int, input().split())
	graph[a].append(b)
	graph[b].append(a)
visited = [False] * (n + 1)
c = 0
for i in range(1, n + 1):
	if not visited[i]:
		found = True
		queue = deque([i])
		visited[i] = True
		while queue:
			node = queue.popleft()
			if len(graph[node]) != 2:
				found = False
			for n in graph[node]:
				if not visited[n]:
					queue.append(n)
					visited[n] = True
		if found:
			c += 1
print(c)


import sys
from collections import defaultdict
from collections import deque
input = sys.stdin.readline

def get_adj_mat(src, dst, n):
	matrix = defaultdict(list)
	for (src_i, dst_i) in zip(src, dst):
		matrix[src_i].append(dst_i)
		matrix[dst_i].append(src_i)
	return matrix

def has_cycle(vertex, adj_mat, seen):
	queue = deque([])
	queue.appendleft((vertex, -1))
	(is_cycle, has_broken) = (False, False)
	while queue:
		(vertex, parent) = queue.pop()
		if len(adj_mat[vertex]) != 2:
			has_broken = True
		for child in adj_mat[vertex]:
			if child != parent:
				if seen[child]:
					is_cycle = True
				else:
					seen[child] = True
					queue.appendleft((child, vertex))
	return int(is_cycle and (not has_broken))

def solve(src, dst, n):
	adj_mat = get_adj_mat(src, dst, n)
	seen = [False] * n
	num_cyc_segm = 0
	for i in range(n):
		if not seen[i]:
			num_cyc_segm += has_cycle(i, adj_mat, seen)
	return num_cyc_segm
(n, m) = [int(val) for val in input().split()]
(src, dst) = ([], [])
for _ in range(m):
	(src_i, dst_i) = [int(val) for val in input().split()]
	(src.append(src_i - 1), dst.append(dst_i - 1))
print(solve(src, dst, n))


from sys import stdin
from collections import defaultdict, deque
import io

def find_cycle(graph, visited, node):
	stack = deque()
	stack.append((node, None))
	cycle = False
	while stack:
		(node, parent) = stack.pop()
		if node in visited:
			cycle = True
		else:
			stack.extend(((child, node) for child in graph[node] if child != parent))
			visited.add(node)
	return cycle

def cycliccomps(graph):
	visited = set()
	result = 0
	for node in graph:
		if node in visited or len(graph[node]) == 2:
			continue
		find_cycle(graph, visited, node)
	for node in graph:
		if node in visited:
			continue
		if find_cycle(graph, visited, node):
			result += 1
	return result
input = stdin
input.readline()
graph = defaultdict(list)
for line in input:
	(n1, n2) = line.split()
	graph[int(n1)].append(int(n2))
	graph[int(n2)].append(int(n1))
print(cycliccomps(graph))


from collections import defaultdict as df
from sys import stdin, stdout
from collections import deque
(n, m) = list(map(int, stdin.readline().split()))
d = df(list)
for i in range(m):
	(a, b) = list(map(int, stdin.readline().split()))
	d[a].append(b)
	d[b].append(a)
visited = [0] * (n + 1)
compo = 0
q = deque()
for i in range(1, n + 1):
	if visited[i] == False:
		q.append(i)
		flag = 1
		while len(q) > 0:
			start = q.pop()
			visited[start] = True
			if len(d[start]) != 2:
				flag = 0
			for j in d[start]:
				if visited[j] == False:
					q.append(j)
		if flag == 1:
			compo += 1
stdout.write(str(compo))
stdout.write('\n')


(n, m) = list(map(int, input().split()))
g = [[] for i in range(n)]
dg = [0 for i in range(n)]
for i in range(m):
	(u, v) = list(map(int, input().split()))
	g[u - 1].append(v - 1)
	g[v - 1].append(u - 1)
	dg[u - 1] += 1
	dg[v - 1] += 1
vis = [0 for i in range(n)]
ans = 0
for i in range(n):
	if vis[i] != 0:
		continue
	q = [i]
	if dg[i] == 2:
		ans1 = 1
	else:
		ans1 = 0
	while len(q) > 0:
		v = q[0]
		for u in g[v]:
			if vis[u] == 0:
				vis[u] = 1
				q.append(u)
				if dg[u] != 2:
					ans1 = 0
		del q[0]
	ans += ans1
print(ans)


def dfsconnring(graph, n):
	count = 0
	main = []
	cc = 0
	visited = [0] * n
	for i in range(n):
		if visited[i] == 0 and len(graph[i]) == 2:
			stack = [i]
		else:
			continue
		h = 0
		l = []
		while stack:
			p = stack.pop()
			if visited[p] == 0:
				visited[p] += 1
				l.append(p)
			if len(graph[p]) != 2:
				h = 1
			for yu in graph[p]:
				if visited[yu] == 0:
					stack.append(yu)
		if h == 0 and len(l) > 0:
			cc += 1
	return cc
from collections import defaultdict
(n, m) = map(int, input().split())
graph = defaultdict(list)
for i in range(m):
	(a, b) = map(int, input().split())
	a = a - 1
	b = b - 1
	graph[a].append(b)
	graph[b].append(a)
print(dfsconnring(graph, n))


def Find(x, par):
	if par[x] < 0:
		return x
	else:
		par[x] = Find(par[x], par)
		return par[x]

def Unite(x, y, par, rank):
	x = Find(x, par)
	y = Find(y, par)
	if x != y:
		if rank[x] < rank[y]:
			par[y] += par[x]
			par[x] = y
		else:
			par[x] += par[y]
			par[y] = x
			if rank[x] == rank[y]:
				rank[x] += 1

def Same(x, y, par):
	return Find(x, par) == Find(y, par)

def Size(x, par):
	return -par[Find(x, par)]
import sys
import io, os
input = sys.stdin.buffer.readline
(n, m) = map(int, input().split())
par = [-1] * n
rank = [0] * n
g = [[] for _ in range(n)]
for i in range(m):
	(v, u) = map(int, input().split())
	(v, u) = (v - 1, u - 1)
	g[v].append(u)
	g[u].append(v)
	Unite(v, u, par, rank)
X = [[] for _ in range(n)]
for i in range(n):
	j = Find(i, par)
	d = len(g[i])
	X[j].append(d)
ans = 0
for i in range(n):
	l = X[i]
	s = set(l)
	if len(l) >= 3 and len(s) == 1 and (l[0] == 2):
		ans += 1
print(ans)


(n, m) = map(int, input().split())
e = [[] for i in range(n)]
for i in range(m):
	(v, u) = map(int, input().split())
	(v, u) = (v - 1, u - 1)
	e[u].append(v)
	e[v].append(u)
answer = 0
v = [0] * n
for i in range(n):
	if v[i] > 0:
		continue
	cycle = True
	j = 0
	stack = [i]
	while len(stack) > j:
		current = stack[j]
		j += 1
		if v[current] > 0:
			continue
		if len(e[current]) != 2:
			cycle = False
		stack.extend(e[current])
		v[current] = 1
	if cycle:
		answer += 1
print(answer)


import sys, math, bisect
sys.setrecursionlimit(10 ** 7)
from random import randint
inf = float('inf')
mod = 10 ** 9 + 7

def lcm(a, b):
	return int(a / math.gcd(a, b) * b)

def gcd(a, b):
	return int(math.gcd(a, b))

def tobinary(n):
	return bin(n)[2:]

def binarySearch(a, x):
	i = bisect.bisect_left(a, x)
	if i != len(a) and a[i] == x:
		return i
	else:
		return -1

def lowerBound(a, x):
	i = bisect.bisect_left(a, x)
	if i:
		return i - 1
	else:
		return -1

def upperBound(a, x):
	i = bisect.bisect_right(a, x)
	if i != len(a) + 1 and a[i - 1] == x:
		return i - 1
	else:
		return -1

def primesInRange(n):
	ans = []
	prime = [True for i in range(n + 1)]
	p = 2
	while p * p <= n:
		if prime[p] == True:
			for i in range(p * p, n + 1, p):
				prime[i] = False
		p += 1
	for p in range(2, n + 1):
		if prime[p]:
			ans.append(p)
	return ans

def primeFactors(n):
	factors = []
	while n % 2 == 0:
		factors.append(2)
		n = n // 2
	for i in range(3, int(math.sqrt(n)) + 1, 2):
		while n % i == 0:
			factors.append(i)
			n = n // i
	if n > 2:
		factors.append(n)
	return factors

def isPrime(n, k=5):
	if n < 2:
		return True
	for i in range(0, k):
		a = randint(1, n - 1)
		if pow(a, n - 1, n) != 1:
			return False
	return True
from collections import deque, defaultdict, Counter
from heapq import heappush, heappop, heapify
import string
(n, m) = map(int, input().split())
arr = []
for _ in range(m):
	arr.append(list(map(int, input().split())))
graph = defaultdict(list)
outGoing = [0] * (n + 1)
for (a, b) in arr:
	outGoing[a] += 1
	outGoing[b] += 1
	graph[a].append(b)
	graph[b].append(a)
visited = set()
Components = []

def findCc(src, cc):
	Q = deque()
	Q.append(src)
	visited.add(src)
	cc.append(src)
	while Q:
		s = Q.popleft()
		for node in graph[s]:
			if node not in visited:
				visited.add(node)
				cc.append(node)
				Q.append(node)
	return cc
for i in range(1, n + 1):
	if i not in visited:
		cc = []
		Components.append(findCc(i, cc))
ans = 0
for i in Components:
	flag = True
	for j in i:
		if outGoing[j] != 2:
			flag = False
			break
	ans += flag
print(ans)


from sys import stdin, stdout
import collections, heapq, bisect, math
input = stdin.readline

def rint():
	return int(input())

def rstr():
	return input().strip()

def rlstr():
	return list(input().strip().split())

def rlint():
	return list(map(int, input().split()))

def main():
	(n, m) = rlint()
	graph = collections.defaultdict(list)
	for _ in range(m):
		(u, v) = rlint()
		graph[u].append(v)
		graph[v].append(u)
	visit = [False] * (n + 1)
	res = 0
	for i in range(1, n + 1):
		if not visit[i]:
			queue = collections.deque()
			queue.append(i)
			visit[i] = True
			flag = len(graph[i]) == 2
			while queue:
				u = queue.popleft()
				for v in graph[u]:
					if not visit[v]:
						if len(graph[v]) != 2:
							flag = False
						visit[v] = True
						queue.append(v)
			if flag:
				res += 1
	print(res)
main()


(n, m) = map(int, input().split())
b = [[] for i in range(n + 1)]
for i in range(m):
	(c, d) = map(int, input().split())
	b[c].append(d)
	b[d].append(c)
cands = set((i for i in range(n + 1) if len(b[i]) == 2))
res = 0
while cands:
	v = cands.pop()
	(R, L) = b[v]
	while L in cands:
		cands.remove(L)
		(V1, V2) = b[L]
		if V1 in cands:
			L = V1
		elif V2 in cands:
			L = V2
		else:
			break
		if L == R:
			res += 1
print(res)


(n, m) = map(int, input().split())
d = [0] * n
s = list(range(n))

def parent(x):
	path = []
	while s[x] != x:
		path.append(x)
		x = s[x]
	for y in path:
		s[y] = x
	return x

def merge(a, b):
	a = parent(a)
	b = parent(b)
	s[b] = a
for i in range(m):
	(u, v) = map(int, input().split())
	(u, v) = (u - 1, v - 1)
	d[u] += 1
	d[v] += 1
	merge(u, v)
ans = [0] * n
for i in range(n):
	ans[parent(i)] = 1
for i in range(n):
	if d[i] != 2:
		ans[s[i]] = 0
print(sum(ans))


import sys, math
from collections import Counter, deque, defaultdict
mod = 10 ** 9 + 7
INF = float('inf')

def inp():
	return int(sys.stdin.readline())

def inpl():
	return list(map(int, sys.stdin.readline().split()))

class UnionFind:

	def __init__(self, n):
		self.n = n
		self.parents = [-1] * n

	def find(self, x):
		if self.parents[x] < 0:
			return x
		else:
			self.parents[x] = self.find(self.parents[x])
			return self.parents[x]

	def union(self, x, y):
		x = self.find(x)
		y = self.find(y)
		if x == y:
			return
		if self.parents[x] > self.parents[y]:
			(x, y) = (y, x)
		self.parents[x] += self.parents[y]
		self.parents[y] = x

	def size(self, x):
		return -self.parents[self.find(x)]

	def same(self, x, y):
		return self.find(x) == self.find(y)

	def members(self, x):
		root = self.find(x)
		return [i for i in range(self.n) if self.find(i) == root]

	def roots(self):
		return [i for (i, x) in enumerate(self.parents) if x < 0]

	def group_count(self):
		return len(self.roots())

	def all_group_members(self):
		return {r: self.members(r) for r in self.roots()}
(n, m) = inpl()
uf = UnionFind(n)
g = [[] for _ in range(n)]
for _ in range(m):
	(a, b) = inpl()
	(a, b) = (a - 1, b - 1)
	uf.union(a, b)
	g[a].append(b)
	g[b].append(a)
ch = [[] for _ in range(n)]
for i in range(n):
	now = uf.find(i)
	ch[now].append(i)
res = 0
for i in range(n):
	if ch[i] == [] or len(ch) < 3:
		continue
	d = defaultdict(int)
	for now in ch[i]:
		for x in g[now]:
			d[x] += 1
	if len(ch[i]) != len(list(d)):
		continue
	for now in ch[i]:
		if d[now] != 2:
			break
	else:
		res += 1
print(res)


def go():
	(n, m) = (int(i) for i in input().split(' '))
	graph = {}
	for _ in range(m):
		(a, b) = (int(i) for i in input().split(' '))
		graph.setdefault(a, [])
		graph.setdefault(b, [])
		graph[a].append(b)
		graph[b].append(a)
	output = 0
	cands = set((i for i in graph if len(graph[i]) == 2))
	while cands:
		c = cands.pop()
		(r, l) = graph[c]
		while l in cands:
			cands.remove(l)
			(c1, c2) = graph[l]
			if c1 in cands:
				l = c1
			elif c2 in cands:
				l = c2
			else:
				break
			if l == r:
				output += 1
	return output
print(go())


from sys import stdin

def main():
	(n, m) = map(int, stdin.readline().split())
	adj = [[] for _ in range(n)]
	for _ in range(m):
		(v, u) = map(int, stdin.readline().split())
		adj[v - 1].append(u - 1)
		adj[u - 1].append(v - 1)
	check = [False] * n
	stack = []
	count = 0
	for i in range(n):
		if not check[i]:
			first = i
			cycle = 1
			stack = [(i, -1)]
			while stack:
				(c, p) = stack.pop()
				if check[c]:
					continue
				else:
					check[c] = True
					if len(adj[c]) > 2 or len(adj[c]) <= 1:
						cycle = 0
					for nb in adj[c]:
						if nb != p:
							stack.append((nb, i))
			count += cycle
	print(count)
main()


import sys
input = sys.stdin.readline
from math import ceil, floor

def swaparr(arr, a, b):
	temp = arr[a]
	arr[a] = arr[b]
	arr[b] = temp

def gcd(a, b):
	if a == 0:
		return b
	return gcd(b % a, a)

def nCr(n, k):
	if k > n - k:
		k = n - k
	res = 1
	for i in range(k):
		res = res * (n - i)
		res = res / (i + 1)
	return int(res)

def upper_bound(a, x, lo=0):
	hi = len(a)
	while lo < hi:
		mid = (lo + hi) // 2
		if a[mid] < x:
			lo = mid + 1
		else:
			hi = mid
	return lo

def primefs(n):
	primes = {}
	while n % 2 == 0:
		primes[2] = primes.get(2, 0) + 1
		n = n // 2
	for i in range(3, int(n ** 0.5) + 2, 2):
		while n % i == 0:
			primes[i] = primes.get(i, 0) + 1
			n = n // i
	if n > 2:
		primes[n] = primes.get(n, 0) + 1
	return primes

def power(x, y, p):
	res = 1
	x = x % p
	if x == 0:
		return 0
	while y > 0:
		if y & 1 == 1:
			res = res * x % p
		y = y >> 1
		x = x * x % p
	return res

def swap(a, b):
	temp = a
	a = b
	b = temp
	return (a, b)

def find(x, link):
	p = x
	while p != link[p]:
		p = link[p]
	while x != p:
		nex = link[x]
		link[x] = p
		x = nex
	return p

def union(x, y, link, size):
	x = find(x, link)
	y = find(y, link)
	if size[x] < size[y]:
		(x, y) = swap(x, y)
	if x != y:
		size[x] += size[y]
		link[y] = x

def sieve(n):
	prime = [True for i in range(n + 1)]
	p = 2
	while p * p <= n:
		if prime[p] == True:
			for i in range(p * p, n + 1, p):
				prime[i] = False
		p += 1
	return prime
MAXN = int(1000000.0 + 5)

def spf_sieve():
	spf[1] = 1
	for i in range(2, MAXN):
		spf[i] = i
	for i in range(4, MAXN, 2):
		spf[i] = 2
	for i in range(3, ceil(MAXN ** 0.5), 2):
		if spf[i] == i:
			for j in range(i * i, MAXN, i):
				if spf[j] == j:
					spf[j] = i

def factoriazation(x):
	ret = {}
	while x != 1:
		ret[spf[x]] = ret.get(spf[x], 0) + 1
		x = x // spf[x]
	return ret

def int_array():
	return list(map(int, input().strip().split()))

def str_array():
	return input().strip().split()
MOD = int(1000000000.0) + 7
CMOD = 998244353
INF = float('inf')
NINF = -float('inf')
(n, m) = int_array()
graph = [[] for _ in range(n + 1)]
link = [i for i in range(n + 1)]
size = [1] * (n + 1)
for __ in range(m):
	(u, v) = int_array()
	graph[u].append(v)
	graph[v].append(u)
	union(u, v, link, size)
final = [i for i in range(n + 1)]
for i in range(1, n + 1):
	final[i] = find(i, link)
comps = {}
for i in range(1, n + 1):
	t = final[i]
	if t not in comps:
		comps[t] = [i]
	else:
		comps[t].append(i)
ans = 0
for i in comps:
	f = 1
	for j in comps[i]:
		if len(graph[j]) != 2:
			f = 0
			break
	if f:
		ans += 1
print(ans)


from sys import stdin
input = stdin.readline
R = lambda : map(int, input().split())
I = lambda : int(input())
S = lambda : input().rstrip('\n')
(n, m) = R()
v = [[] for i in range(n)]
for i in range(m):
	(a, b) = R()
	v[a - 1] += (b - 1,)
	v[b - 1] += (a - 1,)
vl = [len(v[i]) for i in range(n)]
vst = [0] * n
ans = 0
for i in range(n):
	if vst[i]:
		continue
	flg = 1
	stack = [i]
	vst[i] = 1
	if vl[i] != 2:
		flg = 0
	while stack:
		p = stack.pop()
		for el in v[p]:
			if not vst[el]:
				vst[el] = 1
				stack += (el,)
				if vl[el] != 2:
					flg = 0
	if flg:
		ans += 1
print(ans)


(n, m) = map(int, input().split())
a = []
for i in range(n):
	a.append([])
u = [False] * n
for i in range(m):
	(x, y) = map(int, input().split())
	x -= 1
	y -= 1
	a[y].append(x)
	a[x].append(y)
ans = 0
for j in range(n):
	if u[j]:
		continue
	q = []
	i = 0
	q.append(j)
	u[j] = True
	s = 0
	h = 0
	while i < len(q):
		v = q[i]
		i += 1
		s = max(s, len(a[v]))
		h += len(a[v])
		for x in a[v]:
			if not u[x]:
				u[x] = True
				q.append(x)
	if h == 2 * len(q) and s == 2 and (len(q) > 2):
		ans += 1
print(ans)


import io, os
input = io.BytesIO(os.read(0, os.fstat(0).st_size)).readline
from types import GeneratorType

def bootstrap(f, stack=[]):

	def wrappedfunc(*args, **kwargs):
		if stack:
			return f(*args, **kwargs)
		else:
			to = f(*args, **kwargs)
			while True:
				if type(to) is GeneratorType:
					stack.append(to)
					to = next(to)
				else:
					stack.pop()
					if not stack:
						break
					to = stack[-1].send(to)
			return to
	return wrappedfunc
(n, m) = map(int, input().split())
adj = [[] for i in range(n + 1)]
edge_co = {i: 0 for i in range(1, n + 1)}
for i in range(m):
	(s, e) = map(int, input().split())
	adj[s].append(e)
	adj[e].append(s)
	edge_co[s] += 1
	edge_co[e] += 1
visited = [0] * (n + 1)

@bootstrap
def dfs(node):
	if visited[node]:
		yield
	visited[node] = 1
	for i in adj[node]:
		yield dfs(i)
	res.append(node)
	yield
ans = 0
for i in range(1, n + 1):
	if visited[i] == 0:
		res = []
		dfs(i)
		f = True
		for i in res:
			if edge_co[i] != 2:
				f = False
				break
		if f:
			ans += 1
print(ans)


import sys
from collections import deque
input = lambda : sys.stdin.readline().strip('\r\n')
(n, m) = map(int, input().split())
g = [[] for _ in range(n)]
for _ in range(m):
	(a, b) = map(int, input().split())
	g[a - 1].append(b - 1)
	g[b - 1].append(a - 1)
ans = 0
cur = []
visited = [False] * n

def BFS(node):
	q = deque()
	q.append(node)
	while q:
		node = q.popleft()
		if not visited[node]:
			visited[node] = True
			cur.append(node)
			for child in g[node]:
				q.append(child)
for v in range(n):
	cur.clear()
	if not visited[v]:
		BFS(v)
		cycle = True
		for u in cur:
			if len(g[u]) != 2:
				cycle = False
				break
		if cycle:
			ans += 1
print(ans)


from collections import defaultdict
from collections import deque
(n, m) = list(map(int, input().split()))
d = defaultdict(list)
ans = 0
for i in range(m):
	(x, y) = map(int, input().split())
	d[x].append(y)
	d[y].append(x)
q = deque()
visited = [0] * (n + 1)
for i in range(1, n + 1):
	if visited[i] == 0:
		q.append(i)
		visited[i] = 1
		f = 1
		while q:
			j = q.popleft()
			if len(d[j]) != 2:
				f = 0
			for k in d[j]:
				if visited[k] == 0:
					q.append(k)
					visited[k] = 1
		if f:
			ans = ans + 1
print(ans)


import collections
(n, m) = map(int, input().split())
rs = []
for i in range(m):
	(u, v) = map(int, input().split())
	rs.append((u - 1, v - 1))
d = dict()
for v in range(n):
	d[v] = set()
for r in rs:
	u = r[0]
	v = r[1]
	setU = d.get(u, set())
	setV = d.get(v, set())
	setU.add(v)
	setV.add(u)
	d[u] = setU
	d[v] = setV
seen = [False] * n
result = 0
for v in range(n):
	if seen[v]:
		continue
	good = True
	deq = collections.deque([v])
	while len(deq) > 0:
		currV = deq.pop()
		seen[currV] = True
		if len(d[currV]) != 2:
			good = False
		for u in d[currV]:
			if seen[u]:
				continue
			seen[u] = True
			deq.append(u)
	if good:
		result += 1
print(result)


(n_vertices, n_edges) = map(int, input().split())
edges = {}
for vertex in range(1, n_vertices + 1):
	edges[vertex] = []
for _ in range(n_edges):
	(v1, v2) = map(int, input().split())
	edges[v1].append(v2)
	edges[v2].append(v1)
searchable_edges = {}
for vertex in edges:
	if len(edges[vertex]) == 0 or len(edges[vertex]) == 2:
		searchable_edges[vertex] = edges[vertex]
visited = set()

def check_cycle(initial):
	global visited
	visited.add(initial)
	previous = initial
	if len(edges[initial]) == 0 or len(searchable_edges[initial]) > 2:
		return False
	nextv = searchable_edges[initial][0]
	while nextv:
		temp_next = None
		if nextv == initial and len(visited) > 2:
			return True
		if nextv not in searchable_edges:
			return False
		if len(searchable_edges[nextv]) > 2:
			return False
		for vertex in searchable_edges[nextv]:
			if vertex != previous and vertex in searchable_edges:
				if vertex in visited and vertex != initial:
					return False
				temp_next = vertex
				break
		visited.add(nextv)
		previous = nextv
		nextv = temp_next
cycles = 0
for vertex in searchable_edges:
	if check_cycle(vertex):
		cycles += 1
print(cycles)


import sys
sys.setrecursionlimit(10000000)
(n, m) = map(int, input().split())
adj = [[] for _ in range(n)]
for _ in range(m):
	(a, b) = map(int, input().split())
	a -= 1
	b -= 1
	adj[a].append(b)
	adj[b].append(a)
out = 0
comp = [None for _ in range(n)]

def dfs(v, i):
	stack = [v]
	while stack:
		v = stack.pop()
		if comp[v] is not None:
			continue
		comp[v] = i
		path.append(v)
		for j in adj[v]:
			stack.append(j)

def graph_cyclic():
	for j in path:
		degree = len(adj[j])
		if degree != 2:
			return False
	return True
cc_cyclic = 0
cur_comp = 0
for i in range(n):
	if comp[i] is None:
		path = []
		dfs(i, cur_comp)
		if graph_cyclic():
			cc_cyclic += 1
		cur_comp += 1
print(cc_cyclic)


def dfs(v, g, vis):
	count = 1
	first = v
	st = [v]
	vis[v] = True
	while st:
		v = st[-1]
		st.pop()
		if len(g[v]) != 2 or (vis[g[v][0]] and vis[g[v][1]] and (g[v][0] != first) and (g[v][1] != first)):
			count = 0
		for i in range(len(g[v])):
			if not vis[g[v][i]]:
				vis[g[v][i]] = True
				st.append(g[v][i])
				if v == first and count:
					break
	if len(g[first]) == 2 and (not vis[g[first][1]]):
		st.append(g[first][1])
		while st:
			v = st[-1]
			st.pop()
			for i in range(len(g[v])):
				if not vis[g[v][i]]:
					vis[g[v][i]] = True
					st.append(g[v][i])
	return count
(v, e) = map(int, input().split())
g = [[] for i in range(v)]
for i in range(e):
	(f, to) = map(int, input().split())
	g[f - 1].append(to - 1)
	g[to - 1].append(f - 1)
vis = [False] * v
ans = 0
for i in range(v):
	if not vis[i]:
		ans += dfs(i, g, vis)
print(ans)


from collections import deque

def doBFS(start):
	nb = 1
	cycle = 1
	d = deque()
	d.append((-1, start))
	seen[start] = 1
	while d:
		(pre, node) = d.popleft()
		if len(graph[node]) != 2:
			cycle = 0
		for neigh in graph[node]:
			if not seen[neigh]:
				nb += 1
				d.appendleft((node, neigh))
				seen[neigh] = 1
		if not d and start not in graph[node]:
			cycle = 0
	return cycle
(n, m) = map(int, input().split())
graph = [list() for k in range(n)]
for k in range(m):
	(a, b) = map(lambda x: int(x) - 1, input().split())
	graph[a].append(b)
	graph[b].append(a)
seen = [0 for k in range(n)]
cpt = 0
for k in range(n):
	if not seen[k]:
		if doBFS(k):
			cpt += 1
print(cpt)


from sys import *
from collections import defaultdict, deque

class SortedList:

	def __init__(self, iterable=None, _load=200):
		if iterable is None:
			iterable = []
		values = sorted(iterable)
		self._len = _len = len(values)
		self._load = _load
		self._lists = _lists = [values[i:i + _load] for i in range(0, _len, _load)]
		self._list_lens = [len(_list) for _list in _lists]
		self._mins = [_list[0] for _list in _lists]
		self._fen_tree = []
		self._rebuild = True

	def _fen_build(self):
		self._fen_tree[:] = self._list_lens
		_fen_tree = self._fen_tree
		for i in range(len(_fen_tree)):
			if i | i + 1 < len(_fen_tree):
				_fen_tree[i | i + 1] += _fen_tree[i]
		self._rebuild = False

	def _fen_update(self, index, value):
		if not self._rebuild:
			_fen_tree = self._fen_tree
			while index < len(_fen_tree):
				_fen_tree[index] += value
				index |= index + 1

	def _fen_query(self, end):
		if self._rebuild:
			self._fen_build()
		_fen_tree = self._fen_tree
		x = 0
		while end:
			x += _fen_tree[end - 1]
			end &= end - 1
		return x

	def _fen_findkth(self, k):
		_list_lens = self._list_lens
		if k < _list_lens[0]:
			return (0, k)
		if k >= self._len - _list_lens[-1]:
			return (len(_list_lens) - 1, k + _list_lens[-1] - self._len)
		if self._rebuild:
			self._fen_build()
		_fen_tree = self._fen_tree
		idx = -1
		for d in reversed(range(len(_fen_tree).bit_length())):
			right_idx = idx + (1 << d)
			if right_idx < len(_fen_tree) and k >= _fen_tree[right_idx]:
				idx = right_idx
				k -= _fen_tree[idx]
		return (idx + 1, k)

	def _delete(self, pos, idx):
		_lists = self._lists
		_mins = self._mins
		_list_lens = self._list_lens
		self._len -= 1
		self._fen_update(pos, -1)
		del _lists[pos][idx]
		_list_lens[pos] -= 1
		if _list_lens[pos]:
			_mins[pos] = _lists[pos][0]
		else:
			del _lists[pos]
			del _list_lens[pos]
			del _mins[pos]
			self._rebuild = True

	def _loc_left(self, value):
		if not self._len:
			return (0, 0)
		_lists = self._lists
		_mins = self._mins
		(lo, pos) = (-1, len(_lists) - 1)
		while lo + 1 < pos:
			mi = lo + pos >> 1
			if value <= _mins[mi]:
				pos = mi
			else:
				lo = mi
		if pos and value <= _lists[pos - 1][-1]:
			pos -= 1
		_list = _lists[pos]
		(lo, idx) = (-1, len(_list))
		while lo + 1 < idx:
			mi = lo + idx >> 1
			if value <= _list[mi]:
				idx = mi
			else:
				lo = mi
		return (pos, idx)

	def _loc_right(self, value):
		if not self._len:
			return (0, 0)
		_lists = self._lists
		_mins = self._mins
		(pos, hi) = (0, len(_lists))
		while pos + 1 < hi:
			mi = pos + hi >> 1
			if value < _mins[mi]:
				hi = mi
			else:
				pos = mi
		_list = _lists[pos]
		(lo, idx) = (-1, len(_list))
		while lo + 1 < idx:
			mi = lo + idx >> 1
			if value < _list[mi]:
				idx = mi
			else:
				lo = mi
		return (pos, idx)

	def add(self, value):
		_load = self._load
		_lists = self._lists
		_mins = self._mins
		_list_lens = self._list_lens
		self._len += 1
		if _lists:
			(pos, idx) = self._loc_right(value)
			self._fen_update(pos, 1)
			_list = _lists[pos]
			_list.insert(idx, value)
			_list_lens[pos] += 1
			_mins[pos] = _list[0]
			if _load + _load < len(_list):
				_lists.insert(pos + 1, _list[_load:])
				_list_lens.insert(pos + 1, len(_list) - _load)
				_mins.insert(pos + 1, _list[_load])
				_list_lens[pos] = _load
				del _list[_load:]
				self._rebuild = True
		else:
			_lists.append([value])
			_mins.append(value)
			_list_lens.append(1)
			self._rebuild = True

	def discard(self, value):
		_lists = self._lists
		if _lists:
			(pos, idx) = self._loc_right(value)
			if idx and _lists[pos][idx - 1] == value:
				self._delete(pos, idx - 1)

	def remove(self, value):
		_len = self._len
		self.discard(value)
		if _len == self._len:
			raise ValueError('{0!r} not in list'.format(value))

	def pop(self, index=-1):
		(pos, idx) = self._fen_findkth(self._len + index if index < 0 else index)
		value = self._lists[pos][idx]
		self._delete(pos, idx)
		return value

	def bisect_left(self, value):
		(pos, idx) = self._loc_left(value)
		return self._fen_query(pos) + idx

	def bisect_right(self, value):
		(pos, idx) = self._loc_right(value)
		return self._fen_query(pos) + idx

	def count(self, value):
		return self.bisect_right(value) - self.bisect_left(value)

	def __len__(self):
		return self._len

	def __getitem__(self, index):
		(pos, idx) = self._fen_findkth(self._len + index if index < 0 else index)
		return self._lists[pos][idx]

	def __delitem__(self, index):
		(pos, idx) = self._fen_findkth(self._len + index if index < 0 else index)
		self._delete(pos, idx)

	def __contains__(self, value):
		_lists = self._lists
		if _lists:
			(pos, idx) = self._loc_left(value)
			return idx < len(_lists[pos]) and _lists[pos][idx] == value
		return False

	def __iter__(self):
		return (value for _list in self._lists for value in _list)

	def __reversed__(self):
		return (value for _list in reversed(self._lists) for value in reversed(_list))

	def __repr__(self):
		return 'SortedList({0})'.format(list(self))

def bfs(graph, s, a, ans, n):
	vis = {}
	queue = deque()
	queue.append(s)
	vis[s] = True
	ver = True
	while queue:
		s = queue.popleft()
		try:
			nodes.remove(s)
		except:
			pass
		curr = len(graph[s])
		if curr != 2:
			ver = False
		elif curr == 2 and ver == True:
			ver = True
		for i in graph[s]:
			if i not in vis:
				queue.append(i)
				vis[i] = True
	if ver:
		ans[0] += 1
(n, m) = map(int, stdin.readline().split())
d = defaultdict(list)
sets = set()
nodes = SortedList([])
for i in range(m):
	(u, v) = map(int, stdin.readline().split())
	d[u].append(v)
	d[v].append(u)
for i in d:
	if len(d[i]) == 2:
		sets.add(i)
sets = list(sets)
for i in sets:
	nodes.add(i)
ans = [0]
while nodes:
	bfs(d, nodes[0], nodes, ans, n)
stdout.write(str(ans[0]))


from collections import defaultdict
import threading
from sys import stdin, setrecursionlimit
setrecursionlimit(10 ** 6)
input = stdin.readline

def dfs(node, vis, g, comp):
	vis[node] = 1
	comp.append(node)
	for i in g[node]:
		if vis[i] == 0:
			dfs(i, vis, g, comp)

def main():
	(n, m) = map(int, input().strip().split())
	g = defaultdict(list)
	connections = [0] * (n + 1)
	for i in range(m):
		(x, y) = map(int, input().strip().split())
		g[x].append(y)
		g[y].append(x)
		connections[x] += 1
		connections[y] += 1
	vis = [0] * (n + 1)
	ans = 0
	for i in range(1, n + 1):
		if vis[i] == 0:
			comp = []
			dfs(i, vis, g, comp)
			flag = False
			for node in comp:
				if connections[node] != 2:
					flag = True
					break
			if not flag:
				ans += 1
	print(ans)
threading.stack_size(10 ** 8)
t = threading.Thread(target=main)
t.start()
t.join()


from collections import deque

def is_cycle(start):
	if visited[start]:
		return False
	is_cycle_flag = True
	q = deque([start])
	while q:
		pos = q.popleft()
		if len(graph[pos]) != 2:
			is_cycle_flag = False
		for next_pos in graph[pos]:
			if visited[next_pos]:
				continue
			visited[next_pos] = True
			q.append(next_pos)
	return is_cycle_flag
(n, m) = map(int, input().split())
info = [list(map(int, input().split())) for i in range(m)]
graph = [[] for i in range(n)]
for i in range(m):
	(a, b) = info[i]
	graph[a - 1].append(b - 1)
	graph[b - 1].append(a - 1)
visited = [False] * n
ans = 0
for i in range(n):
	if is_cycle(i):
		ans += 1
print(ans)


import math, sys
from collections import defaultdict, deque
import bisect as bi

def yes():
	print('YES')

def no():
	print('NO')

def I():
	return int(sys.stdin.readline())

def In():
	return map(int, sys.stdin.readline().split())

def Sn():
	return sys.stdin.readline().strip()

def dict(a):
	d = {}
	for x in a:
		if d.get(x, -1) != -1:
			d[x] += 1
		else:
			d[x] = 1
	return d

def find_gt(a, x):
	i = bi.bisect_left(a, x)
	if i != len(a):
		return i
	else:
		return -1

def main():
	try:
		(n, m) = In()
		d = defaultdict(list)
		for i in range(m):
			(a, b) = In()
			d[a].append(b)
			d[b].append(a)
		visit = [False] * (n + 1)
		parent = [0] * (n + 1)
		ans = 0
		for i in range(1, n + 1):
			if not visit[i]:
				ver = []
				ok = False
				q = deque([i])
				parent[i] = i
				while q:
					node = q.popleft()
					if visit[node]:
						continue
					ver.append(node)
					visit[node] = True
					for x in d[node]:
						if not visit[x]:
							q.appendleft(x)
							parent[x] = node
						elif visit[x] and parent[node] != x:
							ok = True
				if ok:
					for x in ver:
						if len(d[x]) != 2:
							ok = False
					if ok:
						ans += 1
		print(ans)
	except:
		pass
M = 998244353
P = 1000000007
for _ in range(1):
	main()


from collections import defaultdict
import sys
input = sys.stdin.readline

def countEdges(graph, visited, v):
	queue = [v]
	visited[v] = True
	ans = True
	while queue:
		s = queue.pop()
		ans = ans and len(graph[s]) == 2
		for i in graph[s]:
			if visited[i] == False:
				queue.append(i)
				visited[i] = True
	return ans

def solve(graph, n):
	visited = [False] * (n + 1)
	ans = 0
	for i in range(1, n + 1):
		if visited[i] == False:
			ans += int(countEdges(graph, visited, i))
	return ans
(n, m) = map(int, input().split())
graph = defaultdict(list)
for _ in range(m):
	(a, b) = map(int, input().split())
	graph[a].append(b)
	graph[b].append(a)
print(solve(graph, n))


from collections import deque

class CodeforcesTask977ESolution:

	def __init__(self):
		self.result = ''
		self.n_m = []
		self.edges = []

	def read_input(self):
		self.n_m = [int(x) for x in input().split(' ')]
		for x in range(self.n_m[1]):
			self.edges.append([int(y) for y in input().split(' ')])

	def process_task(self):
		graph = [[] for x in range(self.n_m[0])]
		lens = [0] * self.n_m[0]
		for edge in self.edges:
			graph[edge[0] - 1].append(edge[1])
			graph[edge[1] - 1].append(edge[0])
			lens[edge[0] - 1] += 1
			lens[edge[1] - 1] += 1
		comp_cnt = 0
		component = [0] * self.n_m[0]
		for x in range(self.n_m[0]):
			if not component[x]:
				comp_cnt += 1
				to_visit = deque([x + 1])
				while to_visit:
					visiting = to_visit.popleft()
					if not component[visiting - 1]:
						component[visiting - 1] = comp_cnt
						for g in graph[visiting - 1]:
							if not component[g - 1]:
								to_visit.append(g)
		can_be = [1] * comp_cnt
		for x in range(self.n_m[0]):
			if lens[x] != 2:
				can_be[component[x] - 1] = 0
		self.result = str(sum(can_be))

	def get_result(self):
		return self.result
Solution = CodeforcesTask977ESolution()
Solution.read_input()
Solution.process_task()
print(Solution.get_result())


from sys import stdin, stdout
import math
(N, M) = [int(x) for x in stdin.readline().split()]
label = [x for x in range(N)]
degree = [0] * N

def getf(f, v):
	if f[v] == v:
		return v
	else:
		f[v] = getf(f, f[v])
		return f[v]

def merge(f, v, u):
	t1 = getf(f, v)
	t2 = getf(f, u)
	if t1 != t2:
		f[t2] = t1
for i in range(M):
	(u, v) = [int(x) for x in stdin.readline().split()]
	if u > v:
		(u, v) = (v, u)
	degree[u - 1] += 1
	degree[v - 1] += 1
	merge(label, u - 1, v - 1)
group = {}
for j in range(N):
	if label[j] == j:
		group[j] = 1
for j in range(N):
	root = getf(label, j)
	if degree[j] != 2:
		group[root] = -1
res = 0
for root in group:
	valid = group[root]
	if valid == 1:
		res += 1
print(res)


from collections import deque

class Node:

	def __init__(self, val):
		self.val = val
		self.child = []

class Graph:

	def __init__(self, size):
		self.node = [Node(i + 1) for i in range(size)]
		self.deg = [0 for i in range(len(self.node))]

	def insert(self, val1, val2):
		self.node[val1 - 1].child.append(self.node[val2 - 1])
		self.node[val2 - 1].child.append(self.node[val1 - 1])
		self.deg[val1 - 1] += 1
		self.deg[val2 - 1] += 1

	def dfs(self):
		visited = {i.val: False for i in self.node}
		c = 0
		for i in self.node:
			if not visited[i.val]:
				g = []
				incre = False
				self.dfs_help(g, i, visited)
				for j in g:
					if self.deg[j.val - 1] == 2:
						incre = True
					else:
						incre = False
						break
				if incre:
					c += 1
		return c

	def dfs_help(self, neigh, n, v):
		stack = deque()
		stack.append(n)
		v[n.val] = True
		neigh.append(n)
		while len(stack) != 0:
			curr = stack.pop()
			for i in curr.child:
				if not v[i.val]:
					v[i.val] = True
					stack.append(i)
					neigh.append(i)

	def pr(self):
		for i in self.node:
			print(i.val)
			print(i.child)
a = input().split()
a = [int(i) for i in a]
g = Graph(a[0])
for i in range(a[1]):
	b = input().split()
	b = [int(i) for i in b]
	g.insert(b[0], b[1])
print(g.dfs())


(n, m) = map(int, input().split())
ans = 0
good = 0
adj = [[] for _ in range(n)]
vis = [False for _ in range(n)]
for _ in range(m):
	(u, v) = map(int, input().split())
	u -= 1
	v -= 1
	adj[u].append(v)
	adj[v].append(u)

def dfs(v):
	vis[v] = True
	s = []
	s.append(v)
	while s:
		u = s.pop()
		if len(adj[u]) != 2:
			global good
			good = 0
		for i in adj[u]:
			if not vis[i]:
				vis[i] = True
				s.append(i)
for i in range(n):
	if not vis[i]:
		good = 1
		dfs(i)
		ans += good
print(ans)


from sys import stdin
from collections import deque

def bfs(x):
	vis[x] = True
	q = deque([x])
	ans = 1
	while q:
		cur = q.popleft()
		ans &= len(g[cur]) == 2
		for x in g[cur]:
			if not vis[x]:
				vis[x] = True
				q.append(x)
	return ans
(n, m) = map(int, stdin.readline().split())
g = [[] for _ in range(n)]
vis = [False] * n
ans = 0
for _ in range(m):
	(u, v) = map(lambda x: int(x) - 1, stdin.readline().split())
	g[u].append(v)
	g[v].append(u)
for x in range(n):
	if not vis[x]:
		ans += bfs(x)
print(ans)


T = input().split(' ')
n = int(T[0])
m = int(T[1])
B = [0] * (n + 1)
S1 = [0] * (n + 1)
S2 = [0] * (n + 1)
for i in range(m):
	S = input().split(' ')
	a = int(S[0])
	b = int(S[1])
	if S1[a] == 0:
		S1[a] = b
	elif S2[a] == 0:
		S2[a] = b
	else:
		B[a] = 1
		B[b] = 1
	if S1[b] == 0:
		S1[b] = a
	elif S2[b] == 0:
		S2[b] = a
	else:
		B[a] = 1
		B[b] = 1
for i in range(1, n + 1):
	if S1[i] == 0 or S2[i] == 0:
		B[i] = 1
sol = 0
for ind in range(1, n + 1):
	if B[ind] == 0:
		v = ind
		w = S2[ind]
		c = True
		while w != ind:
			B[v] = 1
			if B[w] == 0:
				u = w
				if S1[w] == v:
					w = S2[w]
				else:
					w = S1[w]
			else:
				B[ind] = 0
				c = False
				break
			v = u
		if c:
			B[v] = 1
			sol += 1
print(sol)


import sys
sys.setrecursionlimit(100000)

class UnionFind:

	def __init__(self, n):
		self.n = [-1] * n
		self.r = [0] * n
		self.siz = n

	def find_root(self, x):
		if self.n[x] < 0:
			return x
		else:
			self.n[x] = self.find_root(self.n[x])
			return self.n[x]

	def unite(self, x, y):
		x = self.find_root(x)
		y = self.find_root(y)
		if x == y:
			return
		elif self.r[x] > self.r[y]:
			self.n[x] += self.n[y]
			self.n[y] = x
		else:
			self.n[y] += self.n[x]
			self.n[x] = y
			if self.r[x] == self.r[y]:
				self.r[y] += 1
		self.siz -= 1

	def root_same(self, x, y):
		return self.find_root(x) == self.find_root(y)

	def count(self, x):
		return -self.n[self.find_root(x)]

	def size(self):
		return self.siz
(n, m) = map(int, input().split())
u = UnionFind(n)
into = [0] * n
for _ in range(m):
	(a, b) = map(int, input().split())
	a -= 1
	b -= 1
	into[a] += 1
	into[b] += 1
	u.unite(a, b)
co = [True] * n
ans = 0
for i in range(n):
	if into[i] != 2:
		co[u.find_root(i)] = False
for i in range(n):
	if u.find_root(i) == i:
		ans += co[i]
print(ans)


import math
import sys

def bfs(start, flag):
	used[start] = flag
	q = [start]
	while q != []:
		if len(g[q[0]]) != 2:
			ff[0] = 0
		for u in g[q[0]]:
			b[-1] += 1
			if used[u] == 0:
				count[-1] += 1
				used[u] = flag
				q.append(u)
		del q[0]
(n, m) = map(int, input().split())
g = [[] for i in range(n)]
for _ in range(m):
	(v, u) = map(int, input().split())
	u -= 1
	v -= 1
	g[v].append(u)
	g[u].append(v)
used = [0] * n
count = []
b = []
answer = 0
rt = 1
for i in range(n):
	if used[i] == 0:
		count.append(1)
		b.append(0)
		ff = [1]
		bfs(i, rt)
		rt += 1
		b[-1] = b[-1] // 2
		if b[-1] == count[-1] and ff[0] == 1:
			answer += 1
print(answer)


class Graph:

	def __init__(self, n):
		self.n = n
		self.adj = [[] for i in range(n)]

	def add(self, v, u):
		self.adj[v].append(u)
		self.adj[u].append(v)

def bfs(s):
	q = [s]
	beg = 0
	used[s] = True
	ok = True
	while beg < len(q):
		v = q[beg]
		beg += 1
		if len(g.adj[v]) != 2:
			ok = False
		for u in g.adj[v]:
			if used[u]:
				continue
			q.append(u)
			used[u] = True
	return ok
(n, m) = map(int, input().split())
g = Graph(n)
for i in range(m):
	(v, u) = map(int, input().split())
	v -= 1
	u -= 1
	g.add(v, u)
used = [False for v in range(n)]
ans = 0
for v in range(n):
	if not used[v]:
		cur = bfs(v)
		ans += cur
print(ans)


import sys
(n, m) = map(int, sys.stdin.readline().strip().split())
edges = [[] for _ in range(n)]
for i in range(m):
	(x, y) = map(int, sys.stdin.readline().strip().split())
	edges[x - 1].append(y)
	edges[y - 1].append(x)
ans = 0
visited = [False] * n
for i in range(n):
	if visited[i - 1]:
		continue
	cycle = True
	stack = [i]
	visited[i - 1] = True
	while stack:
		vertex = stack.pop()
		if len(edges[vertex - 1]) != 2:
			cycle = False
		for j in edges[vertex - 1]:
			if not visited[j - 1]:
				visited[j - 1] = True
				stack.append(j)
	ans += int(cycle)
print(ans)


(v, e) = map(int, input().split())

class graph:

	def __init__(self, v):
		self.v = v
		self.count = 0
		self.b = False
		self.ar = []
		self.b = False
		self.length = 0
		for i in range(v):
			br = []
			self.ar.append(br)

	def add(self, s, d):
		self.ar[s].append(d)
		self.ar[d].append(s)

	def bfsU(self, visited, s):
		queue = []
		queue.append(s)
		visited[s] = True
		while len(queue) != 0:
			self.length += 1
			val = queue.pop(0)
			if len(self.ar[val]) != 2:
				self.b = True
			for i in self.ar[val]:
				if visited[i] == False:
					queue.append(i)
					visited[i] = True
		return self.b | False

	def bfs(self, v):
		self.point = -1
		visited = [False] * v
		for i in range(v):
			self.b = False
			self.length = 0
			if visited[i] == False:
				b = self.bfsU(visited, i)
				if b == False and self.length > 2:
					self.count += 1
g = graph(v)
for i in range(e):
	(a, b) = map(int, input().split())
	g.add(a - 1, b - 1)
g.bfs(v)
print(g.count)


import sys
input = sys.stdin.readline
(N, M) = map(int, input().split())
G = {}
for _ in range(M):
	(a, b) = map(int, input().split())
	if a not in G:
		G[a] = set()
	if b not in G:
		G[b] = set()
	G[a].add(b)
	G[b].add(a)
seen = set()

def dfs(graph, start):
	visited = set()
	stack = [start]
	while stack:
		node = stack.pop()
		if node not in visited:
			visited.add(node)
			stack.extend(graph[node])
	return visited

def dfs_paths(graph, start, goal):
	stack = [(start, [start])]
	while stack:
		(node, path) = stack.pop()
		for next in graph[node] - set(path):
			if next == goal:
				yield (path + [next])
			else:
				stack.append((next, path + [next]))
CC = []
for x in G:
	if x not in seen:
		P = dfs(G, x)
		seen |= P
		CC.append((list(P)[0], P))
cnt = 0
for x in CC:
	if x[0] in x[1] and all([len(G[y]) == 2 for y in x[1]]):
		cnt += 1
print(cnt)


from collections import defaultdict, deque
import sys
sys.setrecursionlimit(10 ** 5)
(n, m) = map(int, input().split())
gr = defaultdict(list)
for _ in range(m):
	(a, b) = map(int, input().split())
	gr[a].append(b)
	gr[b].append(a)
vis = [False] * (n + 1)
ans = 0

def bfs(i):
	q = deque([i])
	vis[i] = True
	f = 0
	c = 0
	while q:
		c += 1
		curr = q.pop()
		if len(gr[curr]) != 2:
			f = 1
		for j in gr[curr]:
			if not vis[j]:
				vis[j] = True
				q.appendleft(j)
	if f == 1 or c < 3:
		return 0
	else:
		return 1
for i in range(1, n + 1):
	if not vis[i]:
		ans += bfs(i)
print(ans)


import sys
from collections import defaultdict

def readline():
	return tuple(map(int, input().split()))

def readlines():
	return (tuple(map(int, line.split())) for line in sys.stdin.readlines())

def main():
	(n, m) = readline()
	edges = defaultdict(list)
	for (a, b) in readlines():
		edges[a].append(b)
		edges[b].append(a)
	loop_components = 0
	for key in tuple(edges.keys()):
		if key in edges:
			component = [key]
			is_loop = True
			while component:
				pos = component.pop()
				try:
					neighbours = edges.pop(pos)
				except KeyError:
					pass
				else:
					is_loop &= len(neighbours) == 2
					component.extend(neighbours)
			loop_components += is_loop
	print(loop_components)
main()


from collections import defaultdict
MAXN = 20 ** 5 + 1

def is_cyclic(graph, root, seen) -> bool:
	stack = [root]
	nodes_cnt = 0
	while len(stack) > 0:
		v = stack.pop()
		seen[v] = True
		nodes_cnt += 1
		if len(graph[v]) != 2:
			return False
		for w in graph[v]:
			if not seen[w]:
				stack.append(w)
				break
		if len(stack) == 0:
			if root in graph[v] and nodes_cnt >= 3:
				return True
	return False

def main() -> None:
	(n, m) = map(int, input().split())
	graph = defaultdict(list)
	nodes = set()
	for _ in range(m):
		(v, u) = map(int, input().split())
		graph[v].append(u)
		graph[u].append(v)
		nodes.add(u)
		nodes.add(v)
	cyclic_components = 0
	seen = [False] * MAXN
	for v in nodes:
		if seen[v]:
			continue
		if is_cyclic(graph, v, seen):
			cyclic_components += 1
	print(cyclic_components)
main()


def bfs(start):
	visited[start] = True
	queue = [start]
	valid = True
	while queue:
		vertex = queue.pop()
		if len(graph[vertex]) != 2:
			valid = False
		for i in graph[vertex]:
			if not visited[i]:
				visited[i] = True
				queue.append(i)
	return valid
(n, m) = map(int, input().split())
cycles = 0
graph = [[] for _ in range(n)]
visited = [False] * n
for _ in range(m):
	(v, u) = map(lambda num: int(num) - 1, input().split())
	graph[v].append(u)
	graph[u].append(v)
for node in range(n):
	if not visited[node]:
		if bfs(node):
			cycles += 1
print(cycles)


def main():
	import collections
	import sys
	input = sys.stdin.buffer.readline
	(n, m) = map(int, input().split())
	G = [set() for i in range(n)]
	for i in range(m):
		(u, v) = map(int, input().split())
		u -= 1
		v -= 1
		G[u].add(v)
		G[v].add(u)
	g = [1] * n
	ans = 0
	for i in range(n):
		if g[i]:
			q = collections.deque()
			q.append((i, i))
			while q:
				(x, y) = q.popleft()
				g[x] = 0
				if len(G[x]) == 2:
					for z in G[x]:
						if z == y:
							continue
						if z == i:
							ans += 1
						elif g[z]:
							q.append((z, x))
						if x == i:
							break
	print(ans)
main()


from sys import stdin, stdout, setrecursionlimit
import threading
setrecursionlimit(10 ** 9)
threading.stack_size(10 ** 8)

class DFS_General:

	def __init__(self, edges, n):
		self.n = n
		self.pi = [-1 for _ in range(n)]
		self.visit = [False for _ in range(n)]
		self.Ady = edges
		self.compo = [-1 for _ in range(n)]
		self.count = 0

	def DFS_visit(self, u):
		self.visit[u] = True
		for v in self.Ady[u]:
			if not self.visit[v]:
				self.pi[v] = u
				self.DFS_visit(v)
		self.compo[u] = self.count

	def DFS(self):
		for i in range(self.n):
			if not self.visit[i]:
				self.count += 1
				self.DFS_visit(i)

def Solution():
	n_m = stdin.readline().split()
	n = int(n_m[0])
	m = int(n_m[1])
	Ady = [[] for _ in range(n)]
	degree = [0 for _ in range(n)]
	for i in range(m):
		stri = stdin.readline().split()
		item = (int(stri[0]) - 1, int(stri[1]) - 1)
		Ady[item[0]].append(item[1])
		Ady[item[1]].append(item[0])
		degree[item[0]] += 1
		degree[item[1]] += 1
	DFS_ = DFS_General(Ady, n)
	DFS_.DFS()
	verif = [True for _ in range(DFS_.count)]
	for i in range(n):
		if degree[i] != 2:
			verif[DFS_.compo[i] - 1] = False
	var = 0
	for item in verif:
		if item:
			var += 1
	stdout.write(str(var))
threading.Thread(target=Solution).start()


import sys, os
from io import BytesIO, IOBase
from math import floor, gcd, fabs, factorial, fmod, sqrt, inf, log
from collections import defaultdict as dd, deque
from heapq import merge, heapify, heappop, heappush, nsmallest
from bisect import bisect_left as bl, bisect_right as br, bisect
BUFSIZE = 8192

class FastIO(IOBase):
	newlines = 0

	def __init__(self, file):
		self._fd = file.fileno()
		self.buffer = BytesIO()
		self.writable = 'x' in file.mode or 'r' not in file.mode
		self.write = self.buffer.write if self.writable else None

	def read(self):
		while True:
			b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
			if not b:
				break
			ptr = self.buffer.tell()
			(self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
		self.newlines = 0
		return self.buffer.read()

	def readline(self):
		while self.newlines == 0:
			b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
			self.newlines = b.count(b'\n') + (not b)
			ptr = self.buffer.tell()
			(self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
		self.newlines -= 1
		return self.buffer.readline()

	def flush(self):
		if self.writable:
			os.write(self._fd, self.buffer.getvalue())
			(self.buffer.truncate(0), self.buffer.seek(0))

class IOWrapper(IOBase):

	def __init__(self, file):
		self.buffer = FastIO(file)
		self.flush = self.buffer.flush
		self.writable = self.buffer.writable
		self.write = lambda s: self.buffer.write(s.encode('ascii'))
		self.read = lambda : self.buffer.read().decode('ascii')
		self.readline = lambda : self.buffer.readline().decode('ascii')
(stdin, stdout) = (IOWrapper(sys.stdin), IOWrapper(sys.stdout))
mod = pow(10, 9) + 7
mod2 = 998244353

def inp():
	return stdin.readline().strip()

def iinp():
	return int(inp())

def out(var, end='\n'):
	stdout.write(str(var) + '\n')

def outa(*var, end='\n'):
	stdout.write(' '.join(map(str, var)) + end)

def lmp():
	return list(mp())

def mp():
	return map(int, inp().split())

def l1d(n, val=0):
	return [val for i in range(n)]

def l2d(n, m, val=0):
	return [l1d(m, val) for j in range(n)]

def ceil(a, b):
	return (a + b - 1) // b
S1 = 'abcdefghijklmnopqrstuvwxyz'
S2 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'

def isprime(x):
	if x <= 1:
		return False
	if x in (2, 3):
		return True
	if x % 2 == 0:
		return False
	for i in range(3, int(sqrt(x)) + 1, 2):
		if x % i == 0:
			return False
	return True
(n, m) = mp()
edges = l2d(n + 1, 0)
for i in range(m):
	(x, y) = mp()
	edges[x].append(y)
	edges[y].append(x)
ans = 0
vis = l1d(n + 1)

def dfs(i, cc):
	st = [i]
	while st:
		node = st.pop()
		vis[node] = 1
		cc.append(node)
		for nxt in edges[node]:
			if not vis[nxt]:
				st.append(nxt)
for i in range(1, n + 1):
	if not vis[i]:
		cc = []
		dfs(i, cc)
		ans += all((len(edges[node]) == 2 for node in cc))
print(ans)


from collections import defaultdict

def is_single(nodes, graph):
	for node in nodes:
		if len(graph[node]) != 2:
			return False
	return True
(n, m) = [int(val) for val in input().split(' ')]
graph = {i: [] for i in range(n)}
for _ in range(m):
	(u, v) = [int(val) for val in input().split(' ')]
	graph[u - 1] += [v - 1]
	graph[v - 1] += [u - 1]
count = 0
visited = set()
for i in range(n):
	if i in visited:
		continue
	queue = [i]
	component = set()
	while queue:
		item = queue.pop(0)
		for nb in graph[item]:
			if nb not in visited:
				visited.add(nb)
				component.add(nb)
				queue.append(nb)
	if not component:
		continue
	if is_single(component, graph):
		count += 1
print(count)


import os
import sys
from io import BytesIO, IOBase
import math
from decimal import Decimal
from decimal import *
from collections import defaultdict, deque
import heapq
getcontext().prec = 25
abcd = 'abcdefghijklmnopqrstuvwxyz'
MOD = pow(10, 9) + 7
BUFSIZE = 8192
from bisect import bisect_left, bisect_right

class FastIO(IOBase):
	newlines = 0

	def __init__(self, file):
		self._fd = file.fileno()
		self.buffer = BytesIO()
		self.writable = 'x' in file.mode or 'r' not in file.mode
		self.write = self.buffer.write if self.writable else None

	def read(self):
		while True:
			b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
			if not b:
				break
			ptr = self.buffer.tell()
			(self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
		self.newlines = 0
		return self.buffer.read()

	def readline(self):
		while self.newlines == 0:
			b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
			self.newlines = b.count(b'\n') + (not b)
			ptr = self.buffer.tell()
			(self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
		self.newlines -= 1
		return self.buffer.readline()

	def flush(self):
		if self.writable:
			os.write(self._fd, self.buffer.getvalue())
			(self.buffer.truncate(0), self.buffer.seek(0))

class IOWrapper(IOBase):

	def __init__(self, file):
		self.buffer = FastIO(file)
		self.flush = self.buffer.flush
		self.writable = self.buffer.writable
		self.write = lambda s: self.buffer.write(s.encode('ascii'))
		self.read = lambda : self.buffer.read().decode('ascii')
		self.readline = lambda : self.buffer.readline().decode('ascii')
(sys.stdin, sys.stdout) = (IOWrapper(sys.stdin), IOWrapper(sys.stdout))
input = lambda : sys.stdin.readline().rstrip('\r\n')

def dfs(i):
	visited[i] = True
	an = True
	f = deque(g[i])
	if len(g[i]) != 2:
		an = False
	while f:
		z = f.popleft()
		if not visited[z]:
			if len(g[z]) != 2:
				an = False
			visited[z] = True
			for j in g[z]:
				if not visited[j]:
					f.append(j)
	return an
(n, k) = map(int, input().split(' '))
g = [list() for i in range(n + 1)]
for i in range(k):
	(x, y) = map(int, input().split(' '))
	g[x].append(y)
	g[y].append(x)
ans = 0
visited = [False] * (n + 1)
for i in range(1, n + 1):
	if not visited[i]:
		test = dfs(i)
		if test:
			ans += 1
print(ans)


(n, m) = map(int, input().split())
graph = [[] for _ in range(n + 1)]
for _ in range(m):
	(a, b) = map(int, input().split())
	graph[a].append(b)
	graph[b].append(a)
to_check = set((i for i in range(n + 1) if len(graph[i]) == 2))
cnt = 0
while to_check:
	node = to_check.pop()
	(right, left) = graph[node]
	while left in to_check:
		to_check.remove(left)
		(v1, v2) = graph[left]
		if v1 in to_check:
			left = v1
		elif v2 in to_check:
			left = v2
		else:
			break
		if left == right:
			cnt += 1
print(cnt)


import sys
inf = float('inf')
(MOD, mod) = (1000000007, 998244353)
from collections import deque, Counter, OrderedDict, defaultdict

def get_array():
	return list(map(int, sys.stdin.readline().strip().split()))

def get_ints():
	return map(int, sys.stdin.readline().strip().split())

def input():
	return sys.stdin.readline().strip()
(n, m) = get_ints()
mydict = defaultdict(list)
for i in range(m):
	(x, y) = get_ints()
	mydict[x].append(y)
	mydict[y].append(x)
ans = 0
visited = [False] * (n + 1)
for i in range(1, n + 1):
	if visited[i]:
		continue
	cycle = True
	stack = [i]
	visited[i] = True
	while stack:
		node = stack.pop()
		if len(mydict[node]) != 2:
			cycle = False
		for j in mydict[node]:
			if not visited[j]:
				visited[j] = True
				stack.append(j)
	ans += int(cycle)
print(ans)


from collections import deque
(vertices, arestas) = [int(z) for z in input().split()]
grafos = {}
visitados = [False for i in range(vertices)]
queue = deque()

def bfs(visited, graph, node):
	visited[node - 1] = True
	queue.append(node)
	resp = 1
	while queue:
		s = queue.popleft()
		if len(graph[s]) != 2:
			resp = 0
		for neighbour in graph[s]:
			if not visited[neighbour - 1]:
				visited[neighbour - 1] = True
				queue.append(neighbour)
	return resp
for i in range(vertices):
	grafos[i + 1] = []
for i in range(arestas):
	(n1, n2) = [int(z) for z in input().split()]
	grafos[n1].append(n2)
	grafos[n2].append(n1)
res = 0
for i in range(1, vertices + 1):
	if not visitados[i - 1]:
		res += bfs(visitados, grafos, i)
print(res)


import math
import os
import sys
from io import BytesIO, IOBase
BUFSIZE = 8192

class FastIO(IOBase):
	newlines = 0

	def __init__(self, file):
		self._fd = file.fileno()
		self.buffer = BytesIO()
		self.writable = 'x' in file.mode or 'r' not in file.mode
		self.write = self.buffer.write if self.writable else None

	def read(self):
		while True:
			b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
			if not b:
				break
			ptr = self.buffer.tell()
			(self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
		self.newlines = 0
		return self.buffer.read()

	def readline(self):
		while self.newlines == 0:
			b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
			self.newlines = b.count(b'\n') + (not b)
			ptr = self.buffer.tell()
			(self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
		self.newlines -= 1
		return self.buffer.readline()

	def flush(self):
		if self.writable:
			os.write(self._fd, self.buffer.getvalue())
			(self.buffer.truncate(0), self.buffer.seek(0))

class IOWrapper(IOBase):

	def __init__(self, file):
		self.buffer = FastIO(file)
		self.flush = self.buffer.flush
		self.writable = self.buffer.writable
		self.write = lambda s: self.buffer.write(s.encode('ascii'))
		self.read = lambda : self.buffer.read().decode('ascii')
		self.readline = lambda : self.buffer.readline().decode('ascii')
(sys.stdin, sys.stdout) = (IOWrapper(sys.stdin), IOWrapper(sys.stdout))
input = lambda : sys.stdin.readline().rstrip('\r\n')
from collections import defaultdict
(n, m) = map(int, input().split())
s = set()
ans = []
d = defaultdict(list)
for i in range(m):
	(a, b) = map(int, input().split())
	d[a].append(b)
	d[b].append(a)
ans = 0
for i in range(1, n + 1):
	if i in s:
		continue
	elif len(d[i]) != 2:
		continue
	else:
		st = i
		prev = -1
		while True:
			if len(d[st]) != 2:
				break
			if prev == d[st][0]:
				st1 = d[st][1]
			else:
				st1 = d[st][0]
			prev = st
			st = st1
			if st in s:
				break
			s.add(st)
			if st == i:
				ans += 1
				break
print(ans)


(n, m) = map(int, input().split())
adj = [[] for _ in range(n + 1)]
for i in range(m):
	(u, v) = map(int, input().split())
	adj[u].append(v)
	adj[v].append(u)
(ans, vis) = (0, [0] * (n + 1))

def dfs(x):
	stack = []
	stack.append(x)
	vis[x] = 1
	while stack:
		y = stack.pop()
		if len(adj[y]) != 2:
			global flag
			flag = 0
		for i in adj[y]:
			if not vis[i]:
				vis[i] = 1
				stack.append(i)
for i in range(1, n + 1):
	if not vis[i]:
		flag = 1
		dfs(i)
		ans += flag
print(ans)


def process(G):
	g = {}
	for (a, b) in G:
		if a not in g:
			g[a] = []
		if b not in g:
			g[b] = []
		g[a].append(b)
		g[b].append(a)
	two = set([x for x in g if len(g[x]) == 2])
	answer = 0
	while len(two) > 0:
		seen = set([])
		for x in two:
			break
		seen.add(x)
		(a, b) = g[x]
		is_cycle = True
		while True:
			seen.add(b)
			if b not in two:
				is_cycle = False
				break
			(a1, b1) = g[b]
			if a1 not in seen:
				new_b = a1
			elif b1 not in seen:
				new_b = b1
			else:
				break
			b = new_b
		for x in seen:
			if x in two:
				two.remove(x)
			else:
				is_cycle = False
		if is_cycle:
			answer += 1
	return answer
(n, m) = [int(x) for x in input().split()]
G = []
for i in range(m):
	(a, b) = [int(x) for x in input().split()]
	G.append([a, b])
print(process(G))


import bisect
import sys
from collections import deque
input = sys.stdin.readline

def main():
	(n, m) = map(int, input().split())
	g = [[] for _ in range(n)]
	for _ in range(m):
		(a, b) = map(int, input().split())
		g[a - 1].append(b - 1)
		g[b - 1].append(a - 1)
	ans = 0
	current = []
	used = [False] * n

	def bfs(node):
		q = deque()
		q.append(node)
		while q:
			node = q.popleft()
			if not used[node]:
				used[node] = True
				current.append(node)
				for child in g[node]:
					q.append(child)
	for v in range(n):
		current.clear()
		if not used[v]:
			bfs(v)
			cycle = True
			for u in current:
				if len(g[u]) != 2:
					cycle = False
					break
			if cycle:
				ans += 1
	print(ans)
main()


from collections import deque

def bfs(i):
	q = deque()
	q.append(i)
	visit[i] = 1
	ok = 1
	while q:
		j = q.popleft()
		for k in G[j]:
			if not visit[k]:
				q.append(k)
				visit[k] = 1
		if ok:
			if not len(G[j]) == 2:
				ok = 0
	return ok
(n, m) = map(int, input().split())
G = []
for _ in range(n + 1):
	G.append([])
for _ in range(m):
	(a, b) = map(int, input().split())
	G[a].append(b)
	G[b].append(a)
visit = [0] * (n + 1)
ans = 0
for i in range(1, n + 1):
	if not visit[i]:
		if bfs(i):
			ans += 1
print(ans)


(n, m) = map(int, input().split())
adj = {}
for i in range(n):
	adj[i] = []
for _ in range(m):
	(a, b) = map(int, input().split())
	a -= 1
	b -= 1
	adj[a].append(b)
	adj[b].append(a)
(ans, visited) = (0, [False] * n)
for i in range(n):
	if visited[i]:
		continue
	visited[i] = True
	comp = [i]
	inc = 1
	while comp:
		v = comp.pop()
		if len(adj[v]) != 2:
			inc = 0
		for j in adj[v]:
			if not visited[j]:
				visited[j] = True
				comp.append(j)
	ans += inc
print(ans)


M = lambda : map(int, input().split())
(n, m) = M()
graph = [set() for i in range(n)]
for _ in range(m):
	(a, b) = M()
	graph[a - 1].add(b - 1)
	graph[b - 1].add(a - 1)
visited = [1 for i in range(n)]
count = 0
stack = []
for i in range(n):
	if visited[i]:
		flag = 1
		stack += [i]
		visited[i] = 0
		while stack:
			x = stack.pop(0)
			if len(graph[x]) != 2:
				flag = 0
			for j in graph[x]:
				if visited[j]:
					stack += [j]
					visited[j] = 0
		if flag == 1:
			count += 1
print(count)


import sys
from math import *

def input():
	return sys.stdin.readline().strip()

def input_l():
	return map(int, input().split())
(n, m) = input_l()
e = [[] for i in range(n)]
for i in range(m):
	(a, b) = input_l()
	e[a - 1].append(b - 1)
	e[b - 1].append(a - 1)
q = [0] * n
w = [True] * n
o = 0
p = 0
r = 0
for i in range(n):
	if w[i]:
		w[i] = False
		q[p] = i
		p += 1
		fg = True
		while o < p:
			x = q[o]
			o += 1
			if len(e[x]) != 2:
				fg = False
			for v in e[x]:
				if w[v]:
					w[v] = False
					q[p] = v
					p += 1
		if fg:
			r += 1
print(r)


(n, m) = map(int, input().split())
g = [[] for _ in range(n + 1)]
degree = [0] * (n + 1)
used = [False] * (n + 1)
for i in range(m):
	(x, y) = map(int, input().split())
	g[x].append(y)
	g[y].append(x)
	degree[x] += 1
	degree[y] += 1
result = 0
for i in range(1, n + 1):
	if not used[i]:
		component = []
		queue = [i]
		while queue:
			v = queue.pop()
			for u in filter(lambda x: not used[x], g[v]):
				used[u] = True
				component.append(u)
				queue.append(u)
		if component and all(map(lambda x: degree[x] == 2, component)):
			result += 1
print(result)


(n, m) = map(int, input().split())
from collections import defaultdict, deque
g = defaultdict(lambda : [])
for _ in range(m):
	(a, b) = map(int, input().split())
	g[a].append(b)
	g[b].append(a)
vis = [-1 for x in range(n + 1)]

def bfs(i):
	q = deque([i])
	vis[i] = 1
	f = 0
	c = 0
	while q:
		c += 1
		curr = q.pop()
		if len(g[curr]) != 2:
			f = 1
		for j in g[curr]:
			if vis[j] == -1:
				vis[j] = 1
				q.appendleft(j)
	if f == 1 or c < 3:
		return 0
	else:
		return 1

def dfs(i):
	stack = []
	f = 0
	c = 0
	stack.append(i)
	while stack:
		c += 1
		curr = stack.pop()
		vis[curr] = 1
		if len(g[curr]) != 2:
			f = 1
		for j in g[curr]:
			if vis[j] == -1:
				stack.append(j)
	if f == 0 and c > 2:
		return 1
	else:
		return 0
ans = 0
for i in range(1, n + 1):
	if vis[i] == -1:
		ans += bfs(i)
print(ans)


import sys
from collections import defaultdict
next(sys.stdin)
graph = defaultdict(list)
for line in sys.stdin:
	(v1, v2) = map(int, line.rstrip().split())
	graph[v1].append(v2)
	graph[v2].append(v1)
components = []
visited = set()
for v in graph:
	if v in visited:
		continue
	new_component = []
	frontier = [v]
	visited.add(v)
	while frontier:
		current_vertex = frontier.pop()
		new_component.append(current_vertex)
		for neighbour in graph[current_vertex]:
			if neighbour in visited:
				continue
			visited.add(neighbour)
			frontier.append(neighbour)
	components.append(new_component)

def is_cycle(component):
	return all((len(graph[v]) == 2 for v in component))
result = sum((is_cycle(component) for component in components))
print(result)


(n, m) = map(int, input().split())
g = [[] for i in range(n)]
for _ in range(m):
	(u, v) = map(int, input().split())
	g[u - 1].append(v - 1)
	g[v - 1].append(u - 1)
cands = set((i for i in range(n) if len(g[i]) == 2))
res = 0
while cands:
	v = cands.pop()
	(r, l) = g[v]
	while l in cands:
		cands.remove(l)
		(v1, v2) = g[l]
		if v1 in cands:
			l = v1
		elif v2 in cands:
			l = v2
		else:
			break
		if l == r:
			res += 1
print(res)


import sys
sys.setrecursionlimit(10 ** 6)
debug_mode = True if len(sys.argv) > 1 and sys.argv[1] == '-d' else False
if debug_mode:
	import os
	inf = open(os.path.basename(__file__).replace('.py', '.in'))

	def input():
		return inf.readline()
from collections import defaultdict, deque

def main():
	(n, m) = list(map(int, input().strip().split()))
	G = defaultdict(list)
	for _ in range(m):
		(u, v) = list(map(int, input().strip().split()))
		G[u].append(v)
		G[v].append(u)
	S = set()
	components = []
	for u in range(1, n + 1):
		if u in S:
			continue
		Q = deque()
		Q.append(u)
		compS = set()
		while Q:
			u = Q.popleft()
			if u in S:
				continue
			S.add(u)
			compS.add(u)
			Q.extend(G[u])
		components.append(compS)
	cnt = 0
	for component in components:
		if all((len(G[u]) == 2 for u in component)):
			cnt += 1
	print(cnt)
main()
if debug_mode:
	inf.close()


from collections import defaultdict

class UnionFind:

	def __init__(self, num_vertices: int):
		self._parents = [i for i in range(num_vertices)]
		self._rank = [1 for _ in range(num_vertices)]

	def find(self, p: int) -> int:
		if self._parents[p] != p:
			self._parents[p] = self.find(self._parents[p])
		return self._parents[p]

	def union(self, left: int, right: int):
		l_root = self.find(left)
		r_root = self.find(right)
		if l_root == r_root:
			return
		if self._rank[l_root] > self._rank[r_root]:
			self._parents[r_root] = l_root
		elif self._rank[l_root] < self._rank[r_root]:
			self._parents[l_root] = r_root
		else:
			self._parents[l_root] = r_root
			self._rank[r_root] += 1
l1 = input()
(n_s, m_s) = l1.split()
(n, m) = (int(n_s), int(m_s))
edges = []
uf = UnionFind(n)
for _ in range(m):
	l = input()
	(v1s, v2s) = l.split()
	(v1, v2) = (int(v1s) - 1, int(v2s) - 1)
	edges.append((v1, v2))
for (v1, v2) in edges:
	uf.union(v1, v2)
components = {}
for v in range(n):
	root = uf.find(v)
	if root not in components:
		components[root] = []
	components[root].append(v)
degrees = defaultdict(lambda : 0)
for (u, v) in edges:
	degrees[u] += 1
	degrees[v] += 1
all_degree_2_components = []
for (root, verts) in components.items():
	if all(map(lambda vert: degrees[vert] == 2, verts)):
		all_degree_2_components.append(root)
print(len(all_degree_2_components))


(n, m) = map(int, input().split(' '))
connected = {}
for i in range(m):
	(u, v) = map(int, input().split(' '))
	if u in connected.keys():
		connected[u].add(v)
	else:
		connected[u] = {v}
	if v in connected.keys():
		connected[v].add(u)
	else:
		connected[v] = {u}
connected = {k: v for (k, v) in connected.items() if len(v) == 2}
visited = set()
count = 0
for p in connected.keys():
	if p not in visited:
		last = p
		current = connected[p].pop()
		found = False
		i = 0
		while current and (not found):
			visited.add(last)
			if current in connected:
				if i >= 1 and p in connected[current]:
					found = True
				last = current
				current = connected[current] - visited
				if len(current) > 0:
					current = current.pop()
				else:
					current = None
			else:
				current = None
			i += 1
		if found:
			count += 1
print(count)


def connected_components(neighbors):
	seen = set()

	def component(node):
		nodes = set([node])
		while nodes:
			node = nodes.pop()
			seen.add(node)
			nodes |= neighbors[node] - seen
			yield node
	for node in neighbors:
		if node not in seen:
			yield component(node)
from collections import defaultdict
graph = defaultdict(set)
(n, m) = map(int, input().split())
for _ in range(m):
	(u, v) = map(int, input().split())
	graph[u].add(v)
	graph[v].add(u)
total = 0
for component in connected_components(graph):
	nodes = list(component)
	size = len(nodes)
	seen = set()
	current = nodes[0]
	while len(seen) < size:
		choice = list(graph[current])
		if len(choice) != 2:
			break
		seen.add(current)
		possible = [c for c in choice if c not in seen]
		if not possible:
			break
		current = possible[0]
	if len(seen) == size:
		total += 1
print(total)


import sys
from collections import defaultdict
input = lambda : sys.stdin.readline().rstrip('\r\n')

class DSU:

	def __init__(self, n):
		self.parent = [i for i in range(n)]
		self.rank = [1] * n
		self.cycle = set()

	def find(self, x):
		if self.parent[x] != x:
			self.parent[x] = self.find(self.parent[x])
		return self.parent[x]

	def union(self, a, b):
		(s1, s2) = (self.find(a), self.find(b))
		if s1 != s2:
			if self.rank[s1] < self.rank[s2]:
				self.parent[s1] = s2
				self.rank[s2] += self.rank[s1]
			else:
				self.parent[s2] = s1
				self.rank[s1] += self.rank[s2]
		else:
			x = self.find(s1)
			if x not in self.cycle:
				self.cycle.add(x)
			else:
				self.cycle.remove(x)

	@property
	def getParent(self):
		return self.parent

	@property
	def getRank(self):
		return self.rank

	@property
	def getCycle(self):
		return self.cycle

def solve():
	(n, m) = map(int, input().split())
	d = DSU(n + 1)
	degre = [0] * (n + 1)
	for _ in range(m):
		(a, b) = map(int, input().split())
		degre[a] += 1
		degre[b] += 1
		d.union(a, b)
	leader = defaultdict(list)
	cycle = d.getCycle
	for i in range(1, n + 1):
		x = d.find(i)
		if x in cycle:
			leader[x].append(degre[i] == 2)
	ans = 0
	for (k, v) in leader.items():
		if all(v):
			ans += 1
	print(ans)
solve()


from collections import Counter, defaultdict
BS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'

def to_base(s, b):
	res = ''
	while s:
		res += BS[s % b]
		s //= b
	return res[::-1] or '0'
alpha = 'abcdefghijklmnopqrstuvwxyz'
from math import floor, ceil, pi
primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013, 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, 1087, 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151, 1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223, 1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291, 1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, 1381, 1399, 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451, 1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499, 1511, 1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583, 1597, 1601, 1607, 1609, 1613, 1619, 1621, 1627, 1637, 1657, 1663, 1667, 1669, 1693, 1697, 1699, 1709, 1721, 1723, 1733, 1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789, 1801, 1811, 1823, 1831, 1847, 1861, 1867, 1871, 1873, 1877, 1879, 1889, 1901, 1907, 1913, 1931, 1933, 1949, 1951, 1973, 1979, 1987, 1993, 1997, 1999, 2003, 2011, 2017, 2027, 2029, 2039, 2053, 2063, 2069, 2081, 2083, 2087, 2089, 2099, 2111, 2113, 2129, 2131, 2137, 2141, 2143, 2153, 2161, 2179, 2203, 2207, 2213, 2221, 2237, 2239, 2243, 2251, 2267, 2269, 2273, 2281, 2287, 2293, 2297, 2309, 2311, 2333, 2339, 2341, 2347, 2351, 2357, 2371, 2377, 2381, 2383, 2389, 2393, 2399, 2411, 2417, 2423, 2437, 2441, 2447, 2459, 2467, 2473, 2477, 2503, 2521, 2531, 2539, 2543, 2549, 2551, 2557, 2579, 2591, 2593, 2609, 2617, 2621, 2633, 2647, 2657, 2659, 2663, 2671, 2677, 2683, 2687, 2689, 2693, 2699, 2707, 2711, 2713, 2719, 2729, 2731, 2741, 2749, 2753, 2767, 2777, 2789, 2791, 2797, 2801, 2803, 2819, 2833, 2837, 2843, 2851, 2857, 2861, 2879, 2887, 2897, 2903, 2909, 2917, 2927, 2939, 2953, 2957, 2963, 2969, 2971, 2999, 3001, 3011, 3019, 3023, 3037, 3041, 3049, 3061, 3067, 3079, 3083, 3089, 3109, 3119, 3121, 3137, 3163, 3167, 3169, 3181, 3187, 3191, 3203, 3209, 3217, 3221, 3229, 3251, 3253, 3257, 3259, 3271, 3299, 3301, 3307, 3313, 3319, 3323, 3329, 3331, 3343, 3347, 3359, 3361, 3371, 3373, 3389, 3391, 3407, 3413, 3433, 3449, 3457, 3461, 3463, 3467, 3469, 3491, 3499, 3511, 3517, 3527, 3529, 3533, 3539, 3541, 3547, 3557, 3559, 3571, 3581, 3583, 3593, 3607, 3613, 3617, 3623, 3631, 3637, 3643, 3659, 3671, 3673, 3677, 3691, 3697, 3701, 3709, 3719, 3727, 3733, 3739, 3761, 3767, 3769, 3779, 3793, 3797, 3803, 3821, 3823, 3833, 3847, 3851, 3853, 3863, 3877, 3881, 3889, 3907, 3911, 3917, 3919, 3923, 3929, 3931, 3943, 3947, 3967, 3989, 4001, 4003, 4007, 4013, 4019, 4021, 4027, 4049, 4051, 4057, 4073, 4079, 4091, 4093, 4099, 4111, 4127, 4129, 4133, 4139, 4153, 4157, 4159, 4177, 4201, 4211, 4217, 4219, 4229, 4231, 4241, 4243, 4253, 4259, 4261, 4271, 4273, 4283, 4289, 4297, 4327, 4337, 4339, 4349, 4357, 4363, 4373, 4391, 4397, 4409, 4421, 4423, 4441, 4447, 4451, 4457, 4463, 4481, 4483, 4493, 4507, 4513, 4517, 4519, 4523, 4547, 4549, 4561, 4567, 4583, 4591, 4597, 4603, 4621, 4637, 4639, 4643, 4649, 4651, 4657, 4663, 4673, 4679, 4691, 4703, 4721, 4723, 4729, 4733, 4751, 4759, 4783, 4787, 4789, 4793, 4799, 4801, 4813, 4817, 4831, 4861, 4871, 4877, 4889, 4903, 4909, 4919, 4931, 4933, 4937, 4943, 4951, 4957, 4967, 4969, 4973, 4987, 4993, 4999, 5003, 5009, 5011, 5021, 5023, 5039, 5051, 5059, 5077, 5081, 5087, 5099, 5101, 5107, 5113, 5119, 5147, 5153, 5167, 5171, 5179, 5189, 5197, 5209, 5227, 5231, 5233, 5237, 5261, 5273, 5279, 5281, 5297, 5303, 5309, 5323, 5333, 5347, 5351, 5381, 5387, 5393, 5399, 5407, 5413, 5417, 5419, 5431, 5437, 5441, 5443, 5449, 5471, 5477, 5479, 5483, 5501, 5503, 5507, 5519, 5521, 5527, 5531, 5557, 5563, 5569, 5573, 5581, 5591, 5623, 5639, 5641, 5647, 5651, 5653, 5657, 5659, 5669, 5683, 5689, 5693, 5701, 5711, 5717, 5737, 5741, 5743, 5749, 5779, 5783, 5791, 5801, 5807, 5813, 5821, 5827, 5839, 5843, 5849, 5851, 5857, 5861, 5867, 5869, 5879, 5881, 5897, 5903, 5923, 5927, 5939, 5953, 5981, 5987, 6007, 6011, 6029, 6037, 6043, 6047, 6053, 6067, 6073, 6079, 6089, 6091, 6101, 6113, 6121, 6131, 6133, 6143, 6151, 6163, 6173, 6197, 6199, 6203, 6211, 6217, 6221, 6229, 6247, 6257, 6263, 6269, 6271, 6277, 6287, 6299, 6301, 6311, 6317, 6323, 6329, 6337, 6343, 6353, 6359, 6361, 6367, 6373, 6379, 6389, 6397, 6421, 6427, 6449, 6451, 6469, 6473, 6481, 6491, 6521, 6529, 6547, 6551, 6553, 6563, 6569, 6571, 6577, 6581, 6599, 6607, 6619, 6637, 6653, 6659, 6661, 6673, 6679, 6689, 6691, 6701, 6703, 6709, 6719, 6733, 6737, 6761, 6763, 6779, 6781, 6791, 6793, 6803, 6823, 6827, 6829, 6833, 6841, 6857, 6863, 6869, 6871, 6883, 6899, 6907, 6911, 6917, 6947, 6949, 6959, 6961, 6967, 6971, 6977, 6983, 6991, 6997, 7001, 7013, 7019, 7027, 7039, 7043, 7057, 7069, 7079, 7103, 7109, 7121, 7127, 7129, 7151, 7159, 7177, 7187, 7193, 7207, 7211, 7213, 7219, 7229, 7237, 7243, 7247, 7253, 7283, 7297, 7307, 7309, 7321, 7331, 7333, 7349, 7351, 7369, 7393, 7411, 7417, 7433, 7451, 7457, 7459, 7477, 7481, 7487, 7489, 7499, 7507, 7517, 7523, 7529, 7537, 7541, 7547, 7549, 7559, 7561, 7573, 7577, 7583, 7589, 7591, 7603, 7607, 7621, 7639, 7643, 7649, 7669, 7673, 7681, 7687, 7691, 7699, 7703, 7717, 7723, 7727, 7741, 7753, 7757, 7759, 7789, 7793, 7817, 7823, 7829, 7841, 7853, 7867, 7873, 7877, 7879, 7883, 7901, 7907, 7919]

def primef(n, plst=[]):
	if n == 1:
		return plst
	else:
		for m in primes:
			if n % m == 0:
				return primef(n // m, plst + [m])
		return primef(1, plst + [n])

def lmii():
	return list(map(int, input().split()))

def ii():
	return int(input())

def countOverlapping(string, sub):
	count = start = 0
	while True:
		start = string.find(sub, start) + 1
		if start > 0:
			count += 1
		else:
			return count
(n, m) = lmii()
graph = defaultdict(set)
for i in range(m):
	(a, b) = lmii()
	graph[a].add(b)
	graph[b].add(a)
nodes = set(range(1, n + 1))
count = 0
while nodes:
	ne = nodes.pop()
	queue = [ne]
	cycle = True
	found = 1
	while queue:
		nen = queue.pop(0)
		if len(graph[nen]) != 2:
			cycle = False
		for child in graph[nen]:
			if child in nodes:
				nodes.remove(child)
				found += 1
				queue.append(child)
	if found >= 3 and cycle:
		count += 1
print(count)


import math
import time
from collections import defaultdict, deque
from sys import stdin, stdout
from bisect import bisect_left, bisect_right
(n, m) = map(int, stdin.readline().split())
graph = defaultdict(lambda : [])
ans = 0
for _ in range(m):
	(a, b) = map(int, stdin.readline().split())
	graph[a].append(b)
	graph[b].append(a)
visited = [False] * (n + 1)
for i in range(1, n + 1):
	if not visited[i]:
		visited[i] = True
		temp = []
		q = deque()
		q.append(i)
		while len(q) > 0:
			tt = q.pop()
			temp.append(tt)
			for j in graph[tt]:
				if not visited[j]:
					visited[j] = True
					q.append(j)
		poss = True
		for i in temp:
			if len(graph[i]) != 2:
				poss = False
				break
		if poss:
			ans += 1
print(ans)


def dfs(graph, start):
	(stack, path) = ([start], [])
	while stack:
		vertex = stack.pop()
		if vertex in visited:
			continue
		visited.add(vertex)
		path.append(vertex)
		for neighbor in graph[vertex]:
			stack.append(neighbor)
	return path
graph = dict()
visited = set()
verts = list()
(n, m) = map(int, input().split())
for i in range(m):
	(u, v) = map(int, input().split())
	if u in graph:
		graph[u].add(v)
	else:
		graph[u] = {v}
	if v in graph:
		graph[v].add(u)
	else:
		graph[v] = {u}
cnt = 0
for i in graph:
	path = dfs(graph, i)
	if len(path) > 2:
		valid = True
		for v in path:
			if len(graph[v]) != 2:
				valid = False
				break
		if valid:
			cnt += 1
print(cnt)


import sys
input = sys.stdin.readline

class Unionfind:

	def __init__(self, n):
		self.par = [-1] * n
		self.rank = [1] * n

	def root(self, x):
		p = x
		while not self.par[p] < 0:
			p = self.par[p]
		while x != p:
			tmp = x
			x = self.par[x]
			self.par[tmp] = p
		return p

	def unite(self, x, y):
		(rx, ry) = (self.root(x), self.root(y))
		if rx == ry:
			return False
		if self.rank[rx] < self.rank[ry]:
			(rx, ry) = (ry, rx)
		self.par[rx] += self.par[ry]
		self.par[ry] = rx
		if self.rank[rx] == self.rank[ry]:
			self.rank[rx] += 1

	def is_same(self, x, y):
		return self.root(x) == self.root(y)

	def count(self, x):
		return -self.par[self.root(x)]
(n, m) = map(int, input().split())
uf = Unionfind(n)
G = [[] for _ in range(n)]
for _ in range(m):
	(u, v) = map(int, input().split())
	uf.unite(u - 1, v - 1)
	G[u - 1].append(v - 1)
	G[v - 1].append(u - 1)
rs = set((uf.root(i) for i in range(n)))
d = {}
for r in rs:
	d[r] = True
for i in range(n):
	r = uf.root(i)
	if len(G[i]) != 2:
		d[r] = False
ans = list(d.values()).count(True)
print(ans)


from collections import deque, defaultdict
(n, m) = map(int, input().split())
d = defaultdict(deque)
for line in range(m):
	(frm, to) = map(int, input().split())
	d[frm].append(to)
	d[to].append(frm)
visited = set()
visitedinloop = set()
queue = deque()
final = 0
for i in range(1, n + 1):
	if i not in visited:
		toadd = True
		visited.add(i)
		queue += d[i]
		toadd = len(d[i]) == 2 and toadd
		visitedinloop.add(i)
		while queue:
			tocheck = queue.popleft()
			if tocheck not in visitedinloop:
				visited.add(tocheck)
				visitedinloop.add(tocheck)
				queue += d[tocheck]
				toadd = len(d[tocheck]) == 2 and toadd
		final += toadd
print(final)


import sys
sys.setrecursionlimit(268435456)
(n, m) = list(map(int, input().split()))
grafo = {}
for _ in range(m):
	(i, j) = list(map(int, input().split()))
	l = grafo.get(i, None)
	if l:
		l.append(j)
	else:
		l = [j]
	grafo[i] = l
	l = grafo.get(j, None)
	if l:
		l.append(i)
	else:
		l = [i]
	grafo[j] = l
v = [0] * (n + 1)
g = [0] * (n + 1)
current_group = 0

def passa(pos):
	fila = []
	fila.append(pos)
	global current_group
	global v
	global g
	v[pos] = 1
	current_group += 1
	while fila:
		current = fila.pop(0)
		g[current] = current_group
		l = grafo.get(current, [])
		for i in l:
			if v[i] == 0:
				v[i] = 1
				fila.append(i)
for i in range(1, n + 1):
	if v[i] == 0:
		passa(i)
s = set()
for i in range(1, len(g)):
	l = grafo.get(i, None)
	if not l:
		s.add(g[i])
		continue
	if len(l) != 2:
		s.add(g[i])
		continue
from collections import Counter
print(len(Counter(g)) - 1 - len(s))


from collections import deque
(n, m) = map(int, input().split())
graph = [[] for _ in range(n)]
for _ in range(m):
	(u, v) = map(int, input().split())
	graph[u - 1].append(v - 1)
	graph[v - 1].append(u - 1)
seen = [False] * n

def bfs(i):
	seen[i] = True
	q = deque([i])
	while q:
		node = q.popleft()
		for c_node in graph[node]:
			if seen[c_node]:
				continue
			seen[c_node] = True
			q.append(c_node)
ans = 0
for i in range(n):
	if seen[i]:
		continue
	if len(graph[i]) != 2:
		bfs(i)
		continue
	start = i
	seen[i] = True
	now = graph[i][0]
	bef = i
	while now != start:
		if len(graph[now]) != 2:
			bfs(now)
			bfs(graph[start][1])
			break
		seen[now] = True
		if graph[now][0] == bef:
			bef = now
			now = graph[now][1]
		else:
			bef = now
			now = graph[now][0]
	else:
		ans += 1
print(ans)


import sys
import math
import collections
import heapq
input = sys.stdin.readline
(n, m) = (int(i) for i in input().split())
l1 = [0] * n

def bfs(d, d1, v):
	q = [v]
	l1[v] = 1
	c1 = 1
	c2 = 1
	while c2 > 0:
		s = q.pop(0)
		c2 -= 1
		if d1[s] != 2:
			c1 = 0
		for i in d[s]:
			if l1[i] == 0:
				q.append(i)
				c2 += 1
				l1[i] = 1
	return c1
d = collections.defaultdict(list)
d1 = {}
for i in range(n):
	d1[i] = 0
for i in range(m):
	(u, v) = (int(i) for i in input().split())
	d[u - 1].append(v - 1)
	d[v - 1].append(u - 1)
	d1[u - 1] += 1
	d1[v - 1] += 1
c = 0
for i in range(n):
	if l1[i] == 0:
		k = bfs(d, d1, i)
		c += k
print(c)


flag = 1

def bfs(partida):
	visited[partida] = 1
	queue = []
	queue.append(partida)
	while queue:
		vertex = queue.pop()
		if len(graph[vertex]) != 2:
			global flag
			flag = 0
		for i in graph[vertex]:
			if visited[i] == 0:
				visited[i] = 1
				queue.append(i)
(numvertices, numedges) = map(int, input().split())
cycles = 0
graph = [[] for n in range(numvertices + 1)]
visited = [0] * (numvertices + 1)
for element in range(numedges):
	(partida, chegada) = map(int, input().split())
	graph[partida].append(chegada)
	graph[chegada].append(partida)
for element in range(1, numvertices + 1):
	flag = 1
	if visited[element] == 0:
		bfs(element)
		if flag == 1:
			cycles += 1
print(cycles)


(n, m) = map(int, input().split())
d = [[] for _ in range(n + 1)]
for _ in range(m):
	(u, v) = map(int, input().split())
	d[u].append(v)
	d[v].append(u)
two = set((i for i in range(n + 1) if len(d[i]) == 2))
c = 0
while two:
	first = two.pop()
	nxt = d[first][0]
	prv = first
	while nxt in two:
		two.remove(nxt)
		if d[nxt][0] != prv:
			prv = nxt
			nxt = d[nxt][0]
		else:
			prv = nxt
			nxt = d[nxt][1]
		if nxt == first:
			c += 1
print(c)


from sys import stdin

def main():
	(n, m) = map(int, input().split())
	g = [[] for _ in range(n)]
	for s in stdin.read().splitlines():
		(u, v) = map(int, s.split())
		g[u - 1].append(v - 1)
		g[v - 1].append(u - 1)
	(avail, res) = ([True] * n, 0)
	for (u, a) in enumerate(avail):
		if a:
			(stack, avail[u], cycle) = ([u], False, 1)
			while stack:
				u = stack.pop()
				vv = g[u]
				if len(vv) != 2:
					cycle = 0
				for v in vv:
					if avail[v]:
						avail[v] = False
						stack.append(v)
			res += cycle
	print(res)
main()


from collections import defaultdict

class Graph:

	def __init__(self, n_vert):
		self.edges = defaultdict(set)
		self.vertices = set(range(1, n_vert))

	def add_edge(self, v1, v2):
		self.edges[v1].add(v2)
		self.edges[v2].add(v1)

	def find_connected_comps(self):
		visited = set()
		result = []
		count = 0
		for vertex in self.vertices:
			if vertex in visited:
				continue
			visited.add(vertex)
			comp = set([vertex])
			q = [vertex]
			while len(q) > 0:
				v = q.pop(0)
				for conn in self.edges[v]:
					if conn in visited:
						continue
					visited.add(conn)
					comp.add(conn)
					q.append(conn)
			if is_cycle(self, comp):
				count += 1
		print(count)

def is_cycle(graph, component):
	if len(component) < 3:
		return False
	for vertex in component:
		if len(graph.edges[vertex]) != 2:
			return False
	return True

def read_numbers():
	return (int(s) for s in input().strip().split(' '))

def main():
	(vertices, edges) = read_numbers()
	g = Graph(vertices)
	for _ in range(edges):
		(v1, v2) = read_numbers()
		g.add_edge(v1, v2)
	g.find_connected_comps()
main()


def bfs(i, d, mark):
	queue = [i]
	res = 'Yes'
	mark[i] = True
	while queue:
		q = queue.pop(0)
		for (j, x) in enumerate(d[q]):
			if mark[x] == False:
				mark[x] = True
				queue.append(x)
		if j != 1:
			res = 'No'
	return res
(n, m) = map(int, input().split())
d = {}
for i in range(m):
	(a, b) = map(int, input().split())
	(a, b) = (a - 1, b - 1)
	if d.get(a) == None:
		d[a] = []
	if d.get(b) == None:
		d[b] = []
	d[a].append(b)
	d[b].append(a)
otvet = 0
mark = {i: False for i in range(n)}
for i in range(n):
	if d.get(i) == None:
		continue
	if mark[i] == False:
		res = bfs(i, d, mark)
		if res == 'Yes':
			otvet += 1
print(otvet)


(n, m) = map(int, input().split())
adj = [[] for i in range(n)]
for _ in range(m):
	(a, b) = map(int, input().split())
	adj[a - 1].append(b - 1)
	adj[b - 1].append(a - 1)
c = 0
vis = [False] * n
for i in range(n):
	if vis[i] == False:
		vis[i] = True
		q = [i]
		f = 1
		while q:
			v = q.pop()
			if len(adj[v]) != 2:
				f = 0
			for j in adj[v]:
				if not vis[j]:
					vis[j] = True
					q.append(j)
		c += f
print(c)


def cycle(reference, Graph):
	count = 0
	while reference:
		(left_key, right_key) = Graph[reference.pop()]
		while left_key in reference:
			reference.remove(left_key)
			(new_l_key, new_r_key) = Graph[left_key]
			if new_l_key in reference:
				left_key = new_l_key
			elif new_r_key in reference:
				left_key = new_r_key
			else:
				break
			if left_key == right_key:
				count += 1
	print(count)
(n, m) = map(int, input().split())
Graph = [[] for _ in range(n + 1)]
for i in range(m):
	(v, u) = map(int, input().split())
	Graph[v].append(u)
	Graph[u].append(v)
reference = set((vert for vert in range(n + 1) if len(Graph[vert]) == 2))
cycle(reference, Graph)


import sys, os, io

def rs():
	return sys.stdin.readline().rstrip()

def ri():
	return int(sys.stdin.readline())

def ria():
	return list(map(int, sys.stdin.readline().split()))

def ws(s):
	sys.stdout.write(s + '\n')

def wi(n):
	sys.stdout.write(str(n) + '\n')

def wia(a):
	sys.stdout.write(' '.join([str(x) for x in a]) + '\n')
import math, datetime, functools, itertools, operator, bisect, fractions, statistics
from collections import deque, defaultdict, OrderedDict, Counter
from fractions import Fraction
from decimal import Decimal
from sys import stdout
from heapq import heappush, heappop, heapify, _heapify_max, _heappop_max, nsmallest, nlargest
INF = 99999999999999999999999999999999

def main():
	mod = 1000000007
	starttime = datetime.datetime.now()
	if os.path.exists('input.txt'):
		sys.stdin = open('input.txt', 'r')
		sys.stdout = open('output.txt', 'w')
	tc = 1
	for _ in range(tc):
		(n, m) = ria()
		visited = [False] * (n + 1)
		graph = [[] for i in range(n + 1)]
		for i in range(m):
			(a, b) = ria()
			graph[a].append(b)
			graph[b].append(a)

		def dfs(start):
			flag = 1
			stack = [start]
			while stack:
				start = stack[-1]
				if not visited[start]:
					if len(graph[start]) != 2:
						flag = 0
					visited[start] = True
					for child in graph[start]:
						if not visited[child]:
							stack.append(child)
				else:
					stack.pop()
			return flag
		ans = 0
		for i in range(1, n + 1):
			if not visited[i]:
				ans += dfs(i)
		wi(ans)
	endtime = datetime.datetime.now()
	time = (endtime - starttime).total_seconds() * 1000
	if os.path.exists('input.txt'):
		print('Time:', time, 'ms')

class FastReader(io.IOBase):
	newlines = 0

	def __init__(self, fd, chunk_size=1024 * 8):
		self._fd = fd
		self._chunk_size = chunk_size
		self.buffer = io.BytesIO()

	def read(self):
		while True:
			b = os.read(self._fd, max(os.fstat(self._fd).st_size, self._chunk_size))
			if not b:
				break
			ptr = self.buffer.tell()
			(self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
		self.newlines = 0
		return self.buffer.read()

	def readline(self, size=-1):
		while self.newlines == 0:
			b = os.read(self._fd, max(os.fstat(self._fd).st_size, self._chunk_size if size == -1 else size))
			self.newlines = b.count(b'\n') + (not b)
			ptr = self.buffer.tell()
			(self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
		self.newlines -= 1
		return self.buffer.readline()

class FastWriter(io.IOBase):

	def __init__(self, fd):
		self._fd = fd
		self.buffer = io.BytesIO()
		self.write = self.buffer.write

	def flush(self):
		os.write(self._fd, self.buffer.getvalue())
		(self.buffer.truncate(0), self.buffer.seek(0))

class FastStdin(io.IOBase):

	def __init__(self, fd=0):
		self.buffer = FastReader(fd)
		self.read = lambda : self.buffer.read().decode('ascii')
		self.readline = lambda : self.buffer.readline().decode('ascii')

class FastStdout(io.IOBase):

	def __init__(self, fd=1):
		self.buffer = FastWriter(fd)
		self.write = lambda s: self.buffer.write(s.encode('ascii'))
		self.flush = self.buffer.flush
sys.stdin = FastStdin()
sys.stdout = FastStdout()
main()


(n, m) = map(int, input().split())
g = [[] for _ in range(n)]
for _ in range(m):
	(a, b) = map(int, input().split())
	g[a - 1].append(b - 1)
	g[b - 1].append(a - 1)
v = [-1] * n
for i in range(n):
	if len(g[i]) == 2:
		v[i] = 1
o = [0]

def dfs(r):
	if v[g[r][0]] == -1 or v[g[r][1]] == -1:
		return
	s = [g[r][0]]
	v[g[r][0]] = 0
	while s:
		x = s.pop()
		if v[g[x][0]] == -1 or v[g[x][1]] == -1:
			break
		if r in g[x]:
			if v[r] == 1:
				v[r] = 0
			else:
				o[0] += 1
				break
		if v[g[x][0]] == 1:
			s.append(g[x][0])
			v[g[x][0]] = 0
		elif v[g[x][1]] == 1:
			s.append(g[x][1])
			v[g[x][1]] = 0
		else:
			break
for i in range(n):
	if v[i] == 1:
		dfs(i)
print(o[0])