from bisect import bisect_left as bl, bisect_right as br, insort
import sys
import heapq
# from math import *
from collections import defaultdict as dd, deque
def data(): return sys.stdin.readline().strip()
def mdata(): return map(int, data().split())
#sys.setrecursionlimit(1000000)
mod = int(1e9 + 7)

n,m=mdata()
x=list(mdata())
d=dd(int)
for i in x:
    d[i]=1
s=set(x)
cnt=m
l=[]
ans=0
t=1
while cnt>0:
    s1=set()
    for i in s:
        if d[i+1]==0:
            s1.add(i+1)
            d[i+1]=1
            ans+=t
            cnt-=1
            l.append(i+1)
        if cnt==0:
            break
        if d[i-1]==0:
            s1.add(i-1)
            d[i-1]=1
            ans+=t
            cnt-=1
            l.append(i-1)
        if cnt==0:
            break
    t+=1
    s=s1
print(ans)
print(*l)


# cook your dish here
from collections import deque

treeCount, personCount = map(int,input().rstrip().split())
treePos = list(map(int,input().rstrip().split()))
trees = set(treePos)
q = deque([(t,0) for t in trees])
pcount = 0
pcost = 0
ppos = set()
while len(q)>0:
    tmp = q.popleft()
    if tmp[0] not in ppos:
        if tmp[0]+1 not in trees:
            q.append((tmp[0]+1,tmp[1]+1))
        if tmp[0]-1 not in trees:
            q.append((tmp[0]-1,tmp[1]+1))
        if tmp[1]>0:
            ppos.add(tmp[0])
            pcount += 1
            pcost += tmp[1]
            if pcount==personCount:
                break
tstr = " ".join(str(i) for i in ppos)
print(pcost)
print(tstr)


# cook your dish here
from collections import deque

treeCount, personCount = map(int,input().rstrip().split())
treePos = list(map(int,input().rstrip().split()))
trees = set(treePos)
q = deque([(t,0) for t in trees])
pcount = 0
pcost = 0
ppos = set()
while len(q)>0:
    tmp = q.popleft()
    if tmp[0] not in ppos:
        q.append((tmp[0]+1,tmp[1]+1))
        q.append((tmp[0]-1,tmp[1]+1))
        if tmp[0] not in trees:
            ppos.add(tmp[0])
            pcount += 1
            pcost += tmp[1]
            if pcount==personCount:
                break
tstr = " ".join(str(i) for i in ppos)
print(pcost)
print(tstr)


from collections import deque
n, m = list(map(int, input().split()))
X = set(map(int, input().split()))
# n = 3
# m = 5
# X = set([0, 3, 1])
A = [] # pos, tree, distance
Y = set([])
for i in X:
	if i-1 not in X:
		A.append([i-1, i, 1])
	if i+1 not in X:
		A.append([i+1, i, 1])
A = deque(A)
d = 0
while m != 0:
	s = A[0]
	# print(s)
	if s[0] not in Y:
		Y.add(s[0])
	else:
		A.popleft()
		continue
	d += s[2]
	if s[0] < s[1]:
		if s[0]-1 in X:
			A.popleft()
		else:
			s[0] -= 1
			s[2] += 1
			A.popleft()
			A.append(s)
	else:
		if s[0]+1 in X:
			A.popleft()
		else:
			s[0] += 1
			s[2] += 1
			A.popleft()
			A.append(s)
	m -= 1

print(d)
print(' '.join(list(map(str, Y))))


import queue

Q=queue.Queue()
S=set()

n,m=[int(i) for i in input().split()]
x=[int(i) for i in input().split()]

#n=100000
#m=100000
#x=[int(i) for i in range(n)]

for i in x:
    Q.put([i,0])
    S.add(i)

ans=[]
res=0

while len(ans)<m:
    t=Q.get()
    if not S.__contains__(t[0]-1):
        S.add(t[0]-1)
        Q.put([t[0]-1,t[1]+1])
    if not S.__contains__(t[0]+1):
        S.add(t[0]+1)
        Q.put([t[0]+1,t[1]+1])
    if t[1]:
        ans.append(t[0])
        res+=t[1]

print(res)
for i in range(m):
    print(ans[i],end=' ')


n,m=[int(i) for i in input().split()]
x=[int(i) for i in input().split()]

S=set()
Q=[]
hd=0

for i in x:
    Q.append([i,0])
    S.add(i)

ans=[]
res=0

while len(ans)<m:
    t=Q[hd]
    hd+=1
    if not S.__contains__(t[0]-1):
        S.add(t[0]-1)
        Q.append([t[0]-1,t[1]+1])
    if not S.__contains__(t[0]+1):
        S.add(t[0]+1)
        Q.append([t[0]+1,t[1]+1])
    if t[1]:
        ans.append(t[0])
        res+=t[1]

print(res)
for i in range(m):
    print(ans[i],end=' ')


n,m = map(int,input().split())
l = list(map(int,input().split()))
l.sort()
s = set(l)
dis = 0
count = 0
ans = []
i = 1
sum1 = 0
while len(ans) < m:
	flag = 0
	for j in range(n):
		if l[j]-i in s and l[j]+i in s:
			continue
		if l[j]-i not in s:
			s.add(l[j]-i)
			sum1 = sum1 + i
			ans.append(l[j]-i)
			if len(ans) == m:
				flag = 1
				break
		if l[j]+i not in s:
			s.add(l[j]+i)
			sum1 = sum1 + i
			ans.append(l[j]+i)
			if len(ans) == m:
				flag = 1
				break
	if flag == 1:
		break
	i = i + 1
print(sum1)
print(*ans)


n,m = map(int,input().split())
l = list(map(int,input().split()))
t1 = min(l)
t2 = max(l)
s = set(l)
dis = 0
count = 0
ans = []
i = 1
sum1 = 0
while len(ans) < m:
	flag = 0
	if len(s) == t2-t1+1:
		break
	for j in range(n):
		if l[j]-i in s and l[j]+i in s:
			continue
		if l[j] != t1  and l[j]-i not in s:
			s.add(l[j]-i)
			sum1 = sum1 + i
			ans.append(l[j]-i)
			if len(ans) == m:
				flag = 1
				break
		if l[j] != t2 and l[j]+i not in s:
			s.add(l[j]+i)
			sum1 = sum1 + i
			ans.append(l[j]+i)
			if len(ans) == m:
				flag = 1
				break
	if flag == 1:
		break
	i = i + 1
i = 1
while len(ans) < m:
	ans.append(t1-i)
	sum1 = sum1+i
	if len(ans) < m:
		ans.append(t2+i)
		sum1 = sum1+i
	else:
		break
	i = i + 1
print(sum1)
print(*ans)


from collections import deque
n,m = map(int,input().split())
l = list(map(int,input().split()))
t1 = min(l)
t2 = max(l)
d = {}
q = []
ans = []
dis = 0
q = deque(q)
d1 = 0
for i in range(len(l)):
	d[l[i]] = 1
	q.append((l[i],1))
while len(ans) < m:
	pos,d1 = q.popleft()

	if pos-1 in d:
		pass
	else:
		dis = dis + d1
		d[pos-1] = d[pos]+1
		q.append((pos-1,d[pos-1]))
		ans.append(pos-1)
	if len(ans) == m:
		break
	if pos+1 in d:
		pass
	else:
		dis = dis + d1
		d[pos+1] = d[pos]+1
		q.append((pos+1,d[pos+1]))
		ans.append(pos+1)
print(dis)
print(*ans)


n,m = map(int,input().split())
l = list(map(int,input().split()))
s = set(l)
dis = 0
count = 0
ans = []
i = 1
sum1 = 0
while len(ans) < m:
	flag = 0
	for j in range(n):
		if l[j]-i in s and l[j]+i in s:
			continue
		if l[j]-i not in s:
			s.add(l[j]-i)
			sum1 = sum1 + i
			ans.append(l[j]-i)
			if len(ans) == m:
				flag = 1
				break
		if l[j]+i not in s:
			s.add(l[j]+i)
			sum1 = sum1 + i
			ans.append(l[j]+i)
			if len(ans) == m:
				flag = 1
				break
	if flag == 1:
		break
	i = i + 1
print(sum1)
print(*ans)


n,m = map(int,input().split())
l = list(map(int,input().split()))
t1 = min(l)
t2 = max(l)
s = set(l)
dis = 0
count = 0
ans = []
i = 1
sum1 = 0
while len(ans) < m:
	flag = 0
	if len(s) == t2-t1+1:
		break
	for j in range(n):
		if l[j]-i in s and l[j]+i in s:
			continue
		if l[j] != t1  and l[j]-i not in s:
			s.add(l[j]-i)
			sum1 = sum1 + i
			ans.append(l[j]-i)
			if len(ans) == m:
				flag = 1
				break
		if l[j] != t2 and l[j]+i not in s:
			s.add(l[j]+i)
			sum1 = sum1 + i
			ans.append(l[j]+i)
			if len(ans) == m:
				flag = 1
				break
	if flag == 1:
		break
	i = i + 1
i = 1
l.sort()
while len(ans) < m:
	ans.append(t1-i)
	sum1 = sum1+i
	if len(ans) < m:
		ans.append(t2+i)
		sum1 = sum1+i
	else:
		break
	i = i + 1
print(sum1)
print(*ans)


from collections import deque
n,m = map(int,input().split())
l = list(map(int,input().split()))
t1 = min(l)
t2 = max(l)
d = {}
q = []
ans = []
dis = 0
q = deque(q)
d1 = 0
for i in range(len(l)):
	d[l[i]] = 1
	q.append((l[i],1))
while len(ans) < m:
	pos,d1 = q.popleft()

	if pos-d1 in d:
		pass
	else:
		dis = dis + d1
		d[pos-d1] = d[pos]+d1
		q.append((pos-d1,d[pos-d1]))
		ans.append(pos-d1)
	if len(ans) == m:
		break
	if pos+d1 in d:
		pass
	else:
		dis = dis + d1
		d[pos+d1] = d[pos]+d1
		q.append((pos+d1,d[pos+d1]))
		ans.append(pos+d1)
print(dis)
print(*ans)


n,m = map(int,input().split())
l = list(map(int,input().split()))
l.sort()
s = set(l)
dis = 0
count = 0
ans = []
i = 1
sum1 = 0
while len(ans) < m:
	flag = 0
	if len(s) == l[-1]-l[0]+1:
		break
	for j in range(n):
		if l[j]-i in s and l[j]+i in s:
			continue
		if j != 0 and l[j]-i not in s:
			s.add(l[j]-i)
			sum1 = sum1 + i
			ans.append(l[j]-i)
			if len(ans) == m:
				flag = 1
				break
		if j != len(l)-1 and l[j]+i not in s:
			s.add(l[j]+i)
			sum1 = sum1 + i
			ans.append(l[j]+i)
			if len(ans) == m:
				flag = 1
				break
	if flag == 1:
		break
	i = i + 1
i = 1
l.sort()
while len(ans) < m:
	ans.append(l[0]-i)
	sum1 = sum1+i
	if len(ans) < m:
		ans.append(l[-1]+i)
		sum1 = sum1+i
	else:
		break
	i = i + 1
print(sum1)
print(*ans)


n,y = map(int , input().split(' '))
x=list(map(int , input().split(' ')))
d={}
for i in x :
    d[i]=0
ans=[]
ind=0
f_ans=0
while len(ans)!=y :
    if d[x[ind]] :
        f_ans+=d[x[ind]]
        ans.append(x[ind])
    if x[ind]+1 not in d :
        d[x[ind]+1]=d[x[ind]]+1
        x.append(x[ind]+1)
    if x[ind]-1 not in d :
        d[x[ind]-1]=d[x[ind]]+1
        x.append(x[ind]-1)
    ind+=1

print(f_ans)
print(*ans)


import heapq
n,m=map(int,input().split())
x=[int(o) for o in input().split()]
c=0
i=1
j=1
k=0
s=set(x)
h=[]
ans=[]
for i in range(n):
    if x[i]-1 not in s:
        heapq.heappush(h,[1,-1,x[i]-1])

    if x[i]+1 not in s:
        heapq.heappush(h,[1,1,x[i]+1])
while c!=m:
    lol=heapq.heappop(h)
    if lol[1]>0:
        if lol[2] not in s:
            s.add(lol[2])
            ans.append(lol[2])
            c+=1
            k+=lol[0]
            lol[1]+=1
            lol[0]+=1
            lol[2]+=1
            # c+=1
            if lol[2] not in s:
                heapq.heappush(h,lol[:])
                # c+=1
    else:
        if lol[2] not in s:
            s.add(lol[2])
            ans.append(lol[2])
            c+=1
            k+=lol[0]
            lol[1]-=1
            lol[0]+=1
            lol[2]-=1
            # c+=1
            if lol[2] not in s:
                heapq.heappush(h,lol[:])

print(k)
print(*ans)


from collections import deque
n,m=map(int,input().split())
x=[int(o) for o in input().split()]
s=set(x)
q=deque()
ans=[]
lol=0
#print(x)
for i in x:
    q.append([i,0])
    # print(i)
lans=0
while lans!=m:
    source=q.popleft()
    # print(source)
    if source[0]+1 not in s:
        s.add(source[0]+1)
        q.append([source[0]+1,source[1]+1])
        lol+=source[1]+1
        ans.append(source[0]+1)
        lans+=1
    if source[0]-1 not in s:
        s.add(source[0]-1)
        q.append([source[0]-1,source[1]+1])
        lol+=source[1]+1
        ans.append(source[0]-1)
        lans+=1
print(lol)
print(*ans)


from collections import deque
n,m=map(int,input().split())
a=list(map(int,input().split()))
s=set()
s1=set()
d=deque()
for i in a:
    d.append(i)
    s.add(i)
    s1.add(i)
d.append(None)
level=0
ans=[]
an=0
# print(d)
while(m>0):
    t=d.popleft()
    # print(t)
    # print(d)
    if t==None:
        level+=1
        d.append(None)
        continue
    if t in s1:
        if t-1 not in s:
            s.add(t-1)
            d.append(t-1)
        if t+1 not in s:
            s.add(t+1)
            d.append(t+1)
    else:
        m-=1
        ans.append(t)
        an+=level
        if t-1 not in s:
            s.add(t-1)
            d.append(t-1)
        if t+1 not in s:
            s.add(t+1)
            d.append(t+1)
print(an)
print(*ans)


n, m = map(int, input().split())
trees = list(map(int, input().split()))
from collections import deque
q = deque()
for t in trees:
    q.append((t,1))
    q.append((t,-1))

cost = 0
people = []
occupied = set(trees)
while q:
    pos, d = q.popleft()
    if (pos+d) in occupied:
        continue
    people.append(pos+d)
    occupied.add(pos+d)
    cost+=abs(d)
    if len(people) == m:
        break
    q.append((pos, d+1 if d >0 else d-1))
print(cost)
print(*people)


n, m = map(int, input().split())
trees = set(map(int, input().split()))

d = 1
cost = 0
p = set()
cnt = 0
while cnt<m:
    for t in trees:
        for q in [-1, 1]:
            if t+q*d not in p and t+q*d not in trees:
                p.add(t+q*d)
                cost+=d
                cnt+=1
            if cnt==m: break
        else:
            continue
        break
    d += 1
print(cost)
print(*p)


n,m=[int(x) for x in input().split()]
a=list(sorted([int(x) for x in input().split()]))
b=list(sorted([[a[i+1]-a[i]-1,i] for i in range(0,n-1)]))
x=1
k=0
while b[k][0]==0:
    k+=1
s=0
su=0
res=[]
while m!=0:
    if m>2:
        su+=2*x
        m-=2
        res+=[a[0]-x,a[-1]+x]
        tm=m
        tk=k
        for i in range(k,len(b)):
            if b[i][0]>1:
                if m>1:
                    res+=[a[b[i][1]]+x,a[b[i][1]+1]-x]
                    m-=2
                else:
                    m=0
                    res.append(a[b[i][1]]+x)
                    break
            else:
                if m>1:
                    tk+=1
                    m-=1
                    res.append(a[b[i][1]]+x)
                else:
                    m=0
                    res.append(a[b[i][1]]+x)
                    break
        k=tk
        su+=x*(tm-m)
    elif m==2:
        su+=2*x
        res+=[a[0]-x,a[-1]+x]
        m=0
    else:
        m=0
        su+=x
        res.append(a[0]-x)
    x+=1
print(su)
print(*res)


n,m=[int(x) for x in input().split()]
a=list(sorted([int(x) for x in input().split()]))
b=[a[i]-a[i-1]-1 for i in range(1,n)]
c=set()
for i in range(n-1):
    if b[i]!=0:
        c.add(i)
res=[]
su=0
x=1
while m!=0:
    if m>2:
        res.extend([a[0]-x,a[-1]+x])
        su+=2*x
        m-=2
        tmp=set()
        for i in c:
            if m>1:
                if b[i]>2:
                    m-=2
                    b[i]-=2
                    su+=2*x
                    res.extend([a[i]+x,a[i+1]-x])
                elif b[i]==2:
                    m-=2
                    su+=2*x
                    b[i]=0
                    tmp.add(i)
                    res.extend([a[i]+x,a[i+1]-x])
                else:
                    m-=1
                    su+=x
                    b[i]=0
                    tmp.add(i)
                    res.append(a[i]+x)
            else:
                m=0
                su+=x
                res.append(a[i]+x)
        if m==0:
            continue
        c-=tmp
    elif m==2:
        res.extend([a[0]-x,a[-1]+x])
        m=0
        su+=2*x
    else:
        res.append(a[0]-x)
        m=0
        su+=x
    x+=1
print(su)
print(*res)


n,m=[int(x) for x in input().split()]
a=list(sorted([int(x) for x in input().split()]))
b=[a[i]-a[i-1]-1 for i in range(1,n)]
c=set()
for i in range(n-1):
    if b[i]!=0:
        c.add(i)
res=set()
su=0
x=1
while m!=0:
    res.add(a[0]-x)
    su+=x
    m-=1
    if m!=0:
        res.add(a[-1]+x)
        su+=x
        m-=1
        if m!=0:
            tmp=set()
            for i in c:
                if b[i]==1:
                    res.add(a[i]+x)
                    su+=x
                    m-=1
                    b[i]=0
                    tmp.add(i)
                else:
                    res.add(a[i]+x)
                    su+=x
                    m-=1
                    b[i]-=1
                    if m!=0:
                        res.add(a[i+1]-x)
                        su+=x
                        m-=1
                        b[i]-=1
                    if b[i]==0:
                        tmp.add(i)
                if m==0:
                    break
            if m==0:
                continue
            c-=tmp
    x+=1
print(su)
print(*res)


n,m=[int(x) for x in input().split()]
a=list(sorted([int(x) for x in input().split()]))
b=[a[i]-a[i-1]-1 for i in range(1,n)]
c=set()
for i in range(n-1):
    if b[i]!=0:
        c.add(i)
res=[]
su=0
x=1
while m!=0:
    if m>2:
        res.extend([a[0]-x,a[-1]+x])
        su+=2*x
        m-=2
        tmp=set()
        for i in c:
            if m>1:
                if b[i]>2:
                    m-=2
                    b[i]-=2
                    su+=2*x
                    tmp.add(i)
                    res.extend([a[i]+x,a[i+1]-x])
                elif b[i]==2:
                    m-=2
                    su+=2*x
                    b[i]=0
                    res.extend([a[i]+x,a[i+1]-x])
                else:
                    m-=1
                    su+=x
                    b[i]=0
                    res.append(a[i]+x)
            else:
                m=0
                res.append(a[i]+x)
                su+=x
            if m==0:
                break
        if m==0:
            continue
        c=tmp|set()
    elif m==2:
        res.extend([a[0]-x,a[-1]+x])
        m=0
        su+=2*x
    else:
        res.append(a[0]-x)
        m=0
        su+=x
    x+=1
print(su)
print(*res)


n,m=[int(x) for x in input().split()]
a=list(sorted([int(x) for x in input().split()]))
b=list(sorted([[a[i+1]-a[i]-1,i] for i in range(0,n-1)]))
x=1
k=0
while k<n-1 and b[k][0]==0:
    k+=1
s=0
su=0
res=[]
while m!=0:
    if m>2:
        su+=2*x
        m-=2
        res+=[a[0]-x,a[-1]+x]
        tm=m
        tk=k
        for i in range(k,len(b)):
            if b[i][0]>1:
                if m>1:
                    res+=[a[b[i][1]]+x,a[b[i][1]+1]-x]
                    m-=2
                    b[i][0]-=2
                    if b[i][0]==0:
                        tk+=1
                else:
                    m=0
                    res.append(a[b[i][1]]+x)
                    break
            else:
                if m>1:
                    tk+=1
                    m-=1
                    b[i][0]-=1
                    res.append(a[b[i][1]]+x)
                else:
                    m=0
                    res.append(a[b[i][1]]+x)
                    break
        k=tk
        su+=x*(tm-m)
    elif m==2:
        su+=2*x
        res+=[a[0]-x,a[-1]+x]
        m=0
    else:
        m=0
        su+=x
        res.append(a[0]-x)
    x+=1
print(su)
print(*res)


n,m=[int(x) for x in input().split()]
a=list(sorted([int(x) for x in input().split()]))
b=[a[i]-a[i-1]-1 for i in range(1,n)]
c=set()
for i in range(n-1):
    if b[i]!=0:
        c.add(i)
res=[]
su=0
x=1
while m!=0:
    if m>2:
        res.extend([a[0]-x,a[-1]+x])
        su+=2*x
        m-=2
        tmp=set()
        for i in c:
            if m>1:
                if b[i]>2:
                    m-=2
                    b[i]-=2
                    su+=2*x
                    res.extend([a[i]+x,a[i+1]-x])
                elif b[i]==2:
                    m-=2
                    su+=2*x
                    b[i]=0
                    tmp.add(i)
                    res.extend([a[i]+x,a[i+1]-x])
                else:
                    m-=1
                    su+=x
                    b[i]=0
                    tmp.add(i)
                    res.append(a[i]+x)
            else:
                m=0
                res.append(a[i]+x)
                su+=x
            if m==0:
                break
        if m==0:
            continue
        c-=tmp
    elif m==2:
        res.extend([a[0]-x,a[-1]+x])
        m=0
        su+=2*x
    else:
        res.append(a[0]-x)
        m=0
        su+=x
    x+=1
print(su)
print(*res)


using System;
using System.Collections.Generic;
class Program
{
	static void Main(string[] args)
	{
		string[] str = Console.ReadLine().Split();
		int N = int.Parse(str[0]);
		int M = int.Parse(str[1]);
      	if(N>M){
          	Console.WriteLine(-1);
        } else {
			var C = new HashSet<int>[N];
			var C1 = new HashSet<int>();
			for(var i=0;i<N;i++){
				C1.Add(i);
            }
			for(var i=0;i<N;i++){
				C[i] = new HashSet<int>(C1);
			}
			for(var i=0;i<M;i++){
				string[] str2 = Console.ReadLine().Split();
				int a1 = int.Parse(str2[0]);
				int a2 = int.Parse(str2[1]);
				C[a1-1].Remove(a2-1);
				C[a2-1].Remove(a1-1);
			}
			var uf = new UnionFind(N);
			for(var i=0;i<N;i++){
				foreach(var j in C[i]){
					uf.Unite(i,j);
				}
			}
			int[]D = new int[N];
			var list = new List<int>();
			for(var i=0;i<N;i++){
				int ar = uf.Root(i);
				if(D[ar]==0){
					list.Add(ar);
				}
				D[ar]++;
			}
			int[]ans = new int[N];
			if(list.Count==3){
				for(var i=0;i<N;i++){
					int ar2 = uf.Root(i);
					for(var j=0;j<3;j++){
						if(ar2==list[j]){
							ans[i] = j+1;
						}
					}
				}
				Console.Write(ans[0]);
				for(var i=1;i<N;i++){
					Console.Write(" "+ans[i]);
				}
			} else {
				Console.WriteLine(-1);
			}
        }
    }
	public class UnionFind
	{
		private int[] data;
		public UnionFind(int size){
			data = new int[size];
			for (int i = 0; i < size; i++) data[i] = -1;
			}
			public bool Unite(int x, int y){
			x = Root(x);
			y = Root(y);
			if (x != y){
				if (data[y] < data[x]){
					var tmp = y;
					y = x;
					x = tmp;
				}
				data[x] += data[y];
				data[y] = x;
			}
			return x != y;
		}
		public bool IsSameGroup(int x, int y){
			return Root(x) == Root(y);
		}
		public int Root(int x){
			return data[x] < 0 ? x : data[x] = Root(data[x]);
		}
	}
}


using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Threading;
using System.Runtime.CompilerServices;
using System.Text;
using System.Diagnostics;
using System.Numerics;
using static System.Console;
using static System.Convert;
using static System.Math;
using static Template;

class Solver
{
    public Scanner sc;
    public void Solve()
    {
        int N,M;
        sc.Make(out N,out M);
        var g=Create(N,()=>new List<int>());
        for (int i = 0; i < M; i++)
        {
            int a,b;
            sc.Make(out a,out b);a--;b--;
            g[a].Add(b);
            g[b].Add(a);
        }
        var d=new Dictionary<ulong,int>();
        var hash=new ulong[N];
        for (int i = 0; i < N; i++)
        {
            g[i]=g[i].Shuffle().OrderBy(v=>v).ToList();
            hash[i]=new RollingHash(g[i]).GetHash();
            if(!d.ContainsKey(hash[i]))
                d[hash[i]]=d.Count;
        }
        if(d.Count!=3)Fail(-1);
        var res=new int[N];
        for(int i=0;i<N;i++)res[i]=d[hash[i]]+1;
        Console.WriteLine(res.Concat(" "));

    }
}

public class RollingHash
{
    public List<int> str { get; }
    private ulong[] hashTable;
    private static ulong[] pow;
    private static uint Base { get; }
    const ulong MASK30 = (1UL << 30) - 1;
    const ulong MASK31 = (1UL << 31) - 1;
    private const ulong MOD = (1UL << 61) - 1;
    public RollingHash(List<int> str)
    {
        this.str = str;
        hashTable = new ulong[str.Count + 1];
        for (var j = 1; j <= str.Count; j++)
            hashTable[j] = CalcMod(Mul(hashTable[j - 1], Base) + (ulong)str[j - 1]);
    }
    static RollingHash()
    {
        var rnd = new Random(); int min = 150, max = int.MaxValue;
        Base = (uint)rnd.Next(min, max);
        pow = new ulong[100100];
        pow[0] = 1;
        for (var j = 1; j < pow.Length; j++)
            pow[j] = Mul(pow[j - 1], Base);
    }

    private static ulong Mul(ulong a, ulong b)
    {
        var au = a >> 31;
        var ad = a & MASK31;
        var bu = b >> 31;
        var bd = b & MASK31;
        var mid = ad * bu + au * bd;
        var midu = mid >> 30;
        var midd = (mid & MASK30);
        return CalcMod(au * bu * 2 + midu + (midd << 31) + ad * bd);
    }
    private static ulong CalcMod(ulong val)
    {
        val = (val & MOD) + (val >> 61);
        if (val > MOD) val -= MOD;
        return val;
    }

    public ulong GetHash(int l = 0, int r = -1)
    {
        if (r < 0) r = str.Count;
        return CalcMod(hashTable[r] + MOD * ((1UL << 3) - 1) - Mul(hashTable[l], pow[r - l]));
    }
    //thisの[l1:)とhの[l2:)のLCP
    public int LCP(RollingHash h, int l1 = 0, int l2 = 0)
    {
        int l = 0, r = Min(str.Count - l1, h.str.Count - l2) + 1;
        while (r - l > 1)
        {
            var m = (r + l) / 2;
            if (GetHash(l1, l1 + m) == h.GetHash(l2, l2 + m)) l = m;
            else r = m;
        }
        return l;
    }
    public int LCP(int l1 = 0, int l2 = 0) => LCP(this, l1, l2);
}
#region Template
public static class Template
{
    static void Main(string[] args)
    {
        Console.SetOut(new StreamWriter(Console.OpenStandardOutput()) { AutoFlush = false });
        var sol = new Solver() { sc = new Scanner() };
        //var th = new Thread(sol.Solve, 1 << 26);th.Start();th.Join();
        sol.Solve();
        Console.Out.Flush();
    }
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool chmin<T>(ref T a, T b) where T : IComparable<T> { if (a.CompareTo(b) > 0) { a = b; return true; } return false; }
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool chmax<T>(ref T a, T b) where T : IComparable<T> { if (a.CompareTo(b) < 0) { a = b; return true; } return false; }
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static void swap<T>(ref T a, ref T b) { var t = b; b = a; a = t; }
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static void swap<T>(this IList<T> A, int i, int j) { var t = A[i]; A[i] = A[j]; A[j] = t; }
    public static T[] Create<T>(int n, Func<T> f) { var rt = new T[n]; for (var i = 0; i < rt.Length; ++i) rt[i] = f(); return rt; }
    public static T[] Create<T>(int n, Func<int, T> f) { var rt = new T[n]; for (var i = 0; i < rt.Length; ++i) rt[i] = f(i); return rt; }
    public static string Concat<T>(this IEnumerable<T> A, string sp) => string.Join(sp, A);
    public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> A) => A.OrderBy(v => Guid.NewGuid());
    public static int CompareTo<T>(this T[] A, T[] B, Comparison<T> cmp = null) { cmp = cmp ?? Comparer<T>.Default.Compare; for (var i = 0; i < Min(A.Length, B.Length); i++) { int c = cmp(A[i], B[i]); if (c > 0) return 1; else if (c < 0) return -1; } if (A.Length == B.Length) return 0; if (A.Length > B.Length) return 1; else return -1; }
    public static int ArgMax<T>(this IList<T> A, Comparison<T> cmp = null) { cmp = cmp ?? Comparer<T>.Default.Compare; T max = A[0]; int rt = 0; for (int i = 1; i < A.Count; i++) if (cmp(max, A[i]) < 0) { max = A[i]; rt = i; } return rt; }
    public static T PopBack<T>(this List<T> A) { var v = A[A.Count - 1]; A.RemoveAt(A.Count - 1); return v; }
    public static void Fail<T>(T s) { Console.WriteLine(s); Console.Out.Close(); Environment.Exit(0); }
}
public class Scanner
{
    public string Str => Console.ReadLine().Trim();
    public int Int => int.Parse(Str);
    public long Long => long.Parse(Str);
    public double Double => double.Parse(Str);
    public int[] ArrInt => Str.Split(' ').Select(int.Parse).ToArray();
    public long[] ArrLong => Str.Split(' ').Select(long.Parse).ToArray();
    public char[][] Grid(int n) => Create(n, () => Str.ToCharArray());
    public int[] ArrInt1D(int n) => Create(n, () => Int);
    public long[] ArrLong1D(int n) => Create(n, () => Long);
    public int[][] ArrInt2D(int n) => Create(n, () => ArrInt);
    public long[][] ArrLong2D(int n) => Create(n, () => ArrLong);
    private Queue<string> q = new Queue<string>();
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public T Next<T>() { if (q.Count == 0) foreach (var item in Str.Split(' ')) q.Enqueue(item); return (T)Convert.ChangeType(q.Dequeue(), typeof(T)); }
    public void Make<T1>(out T1 v1) => v1 = Next<T1>();
    public void Make<T1, T2>(out T1 v1, out T2 v2) { v1 = Next<T1>(); v2 = Next<T2>(); }
    public void Make<T1, T2, T3>(out T1 v1, out T2 v2, out T3 v3) { Make(out v1, out v2); v3 = Next<T3>(); }
    public void Make<T1, T2, T3, T4>(out T1 v1, out T2 v2, out T3 v3, out T4 v4) { Make(out v1, out v2, out v3); v4 = Next<T4>(); }
    public void Make<T1, T2, T3, T4, T5>(out T1 v1, out T2 v2, out T3 v3, out T4 v4, out T5 v5) { Make(out v1, out v2, out v3, out v4); v5 = Next<T5>(); }
    public void Make<T1, T2, T3, T4, T5, T6>(out T1 v1, out T2 v2, out T3 v3, out T4 v4, out T5 v5, out T6 v6) { Make(out v1, out v2, out v3, out v4, out v5); v6 = Next<T6>(); }
    public void Make<T1, T2, T3, T4, T5, T6, T7>(out T1 v1, out T2 v2, out T3 v3, out T4 v4, out T5 v5, out T6 v6, out T7 v7) { Make(out v1, out v2, out v3, out v4, out v5, out v6); v7 = Next<T7>(); }
    //public (T1, T2) Make<T1, T2>() { Make(out T1 v1, out T2 v2); return (v1, v2); }
    //public (T1, T2, T3) Make<T1, T2, T3>() { Make(out T1 v1, out T2 v2, out T3 v3); return (v1, v2, v3); }
    //public (T1, T2, T3, T4) Make<T1, T2, T3, T4>() { Make(out T1 v1, out T2 v2, out T3 v3, out T4 v4); return (v1, v2, v3, v4); }
}

#endregion


using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Threading;
using System.Runtime.CompilerServices;
using System.Text;
using System.Diagnostics;
using System.Numerics;
using static System.Console;
using static System.Convert;
using static System.Math;
using static Template;

class Solver
{
    public Scanner sc;
    public void Solve()
    {
        int N,M;
        sc.Make(out N,out M);
        var g=Create(N,()=>new List<int>());
        var uf=new UnionFind(N);
        for (int i = 0; i < M; i++)
        {
            int a,b;
            sc.Make(out a,out b);a--;b--;
            uf.Union(a,b);
            g[a].Add(b);
            g[b].Add(a);
        }
        if(uf.GroupCount!=1)Fail(-1);
        var d=new Dictionary<ulong,int>();
        var hash=new ulong[N];
        for (int i = 0; i < N; i++)
        {
            g[i]=g[i].Shuffle().OrderBy(v=>v).ToList();
            hash[i]=new RollingHash(g[i]).GetHash();
            if(!d.ContainsKey(hash[i]))
                d[hash[i]]=d.Count;
        }
        if(d.Count!=3)Fail(-1);
        var res=new int[N];
        for(int i=0;i<N;i++)res[i]=d[hash[i]]+1;
        Console.WriteLine(res.Concat(" "));

    }
}
public class UnionFind
{
    public int GroupCount { get; private set; }
    protected int[] data;
    public virtual int this[int i] => Find(i);
    public UnionFind(int size)
    {
        data = Create(size, () => -1);
        GroupCount = size;
    }
    protected int Find(int i)
        => data[i] < 0 ? i : (data[i] = Find(data[i]));
    public int Size(int i)
        => -data[Find(i)];
    public virtual bool Union(int u, int v)
    {
        u = Find(u); v = Find(v);
        if (u == v) return false;
        if (data[u] > data[v])
            swap(ref u, ref v);
        GroupCount--;
        data[u] += data[v];
        data[v] = u;
        return true;
    }
}
public class RollingHash
{
    public List<int> str { get; }
    private ulong[] hashTable;
    private static ulong[] pow;
    private static uint Base { get; }
    const ulong MASK30 = (1UL << 30) - 1;
    const ulong MASK31 = (1UL << 31) - 1;
    private const ulong MOD = (1UL << 61) - 1;
    public RollingHash(List<int> str)
    {
        this.str = str;
        hashTable = new ulong[str.Count + 1];
        for (var j = 1; j <= str.Count; j++)
            hashTable[j] = CalcMod(Mul(hashTable[j - 1], Base) + (ulong)str[j - 1]);
    }
    static RollingHash()
    {
        var rnd = new Random(); int min = 150, max = int.MaxValue;
        Base = (uint)rnd.Next(min, max);
        pow = new ulong[100100];
        pow[0] = 1;
        for (var j = 1; j < pow.Length; j++)
            pow[j] = Mul(pow[j - 1], Base);
    }

    private static ulong Mul(ulong a, ulong b)
    {
        var au = a >> 31;
        var ad = a & MASK31;
        var bu = b >> 31;
        var bd = b & MASK31;
        var mid = ad * bu + au * bd;
        var midu = mid >> 30;
        var midd = (mid & MASK30);
        return CalcMod(au * bu * 2 + midu + (midd << 31) + ad * bd);
    }
    private static ulong CalcMod(ulong val)
    {
        val = (val & MOD) + (val >> 61);
        if (val > MOD) val -= MOD;
        return val;
    }

    public ulong GetHash(int l = 0, int r = -1)
    {
        if (r < 0) r = str.Count;
        return CalcMod(hashTable[r] + MOD * ((1UL << 3) - 1) - Mul(hashTable[l], pow[r - l]));
    }
    //thisの[l1:)とhの[l2:)のLCP
    public int LCP(RollingHash h, int l1 = 0, int l2 = 0)
    {
        int l = 0, r = Min(str.Count - l1, h.str.Count - l2) + 1;
        while (r - l > 1)
        {
            var m = (r + l) / 2;
            if (GetHash(l1, l1 + m) == h.GetHash(l2, l2 + m)) l = m;
            else r = m;
        }
        return l;
    }
    public int LCP(int l1 = 0, int l2 = 0) => LCP(this, l1, l2);
}
#region Template
public static class Template
{
    static void Main(string[] args)
    {
        Console.SetOut(new StreamWriter(Console.OpenStandardOutput()) { AutoFlush = false });
        var sol = new Solver() { sc = new Scanner() };
        //var th = new Thread(sol.Solve, 1 << 26);th.Start();th.Join();
        sol.Solve();
        Console.Out.Flush();
    }
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool chmin<T>(ref T a, T b) where T : IComparable<T> { if (a.CompareTo(b) > 0) { a = b; return true; } return false; }
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool chmax<T>(ref T a, T b) where T : IComparable<T> { if (a.CompareTo(b) < 0) { a = b; return true; } return false; }
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static void swap<T>(ref T a, ref T b) { var t = b; b = a; a = t; }
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static void swap<T>(this IList<T> A, int i, int j) { var t = A[i]; A[i] = A[j]; A[j] = t; }
    public static T[] Create<T>(int n, Func<T> f) { var rt = new T[n]; for (var i = 0; i < rt.Length; ++i) rt[i] = f(); return rt; }
    public static T[] Create<T>(int n, Func<int, T> f) { var rt = new T[n]; for (var i = 0; i < rt.Length; ++i) rt[i] = f(i); return rt; }
    public static string Concat<T>(this IEnumerable<T> A, string sp) => string.Join(sp, A);
    public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> A) => A.OrderBy(v => Guid.NewGuid());
    public static int CompareTo<T>(this T[] A, T[] B, Comparison<T> cmp = null) { cmp = cmp ?? Comparer<T>.Default.Compare; for (var i = 0; i < Min(A.Length, B.Length); i++) { int c = cmp(A[i], B[i]); if (c > 0) return 1; else if (c < 0) return -1; } if (A.Length == B.Length) return 0; if (A.Length > B.Length) return 1; else return -1; }
    public static int ArgMax<T>(this IList<T> A, Comparison<T> cmp = null) { cmp = cmp ?? Comparer<T>.Default.Compare; T max = A[0]; int rt = 0; for (int i = 1; i < A.Count; i++) if (cmp(max, A[i]) < 0) { max = A[i]; rt = i; } return rt; }
    public static T PopBack<T>(this List<T> A) { var v = A[A.Count - 1]; A.RemoveAt(A.Count - 1); return v; }
    public static void Fail<T>(T s) { Console.WriteLine(s); Console.Out.Close(); Environment.Exit(0); }
}
public class Scanner
{
    public string Str => Console.ReadLine().Trim();
    public int Int => int.Parse(Str);
    public long Long => long.Parse(Str);
    public double Double => double.Parse(Str);
    public int[] ArrInt => Str.Split(' ').Select(int.Parse).ToArray();
    public long[] ArrLong => Str.Split(' ').Select(long.Parse).ToArray();
    public char[][] Grid(int n) => Create(n, () => Str.ToCharArray());
    public int[] ArrInt1D(int n) => Create(n, () => Int);
    public long[] ArrLong1D(int n) => Create(n, () => Long);
    public int[][] ArrInt2D(int n) => Create(n, () => ArrInt);
    public long[][] ArrLong2D(int n) => Create(n, () => ArrLong);
    private Queue<string> q = new Queue<string>();
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public T Next<T>() { if (q.Count == 0) foreach (var item in Str.Split(' ')) q.Enqueue(item); return (T)Convert.ChangeType(q.Dequeue(), typeof(T)); }
    public void Make<T1>(out T1 v1) => v1 = Next<T1>();
    public void Make<T1, T2>(out T1 v1, out T2 v2) { v1 = Next<T1>(); v2 = Next<T2>(); }
    public void Make<T1, T2, T3>(out T1 v1, out T2 v2, out T3 v3) { Make(out v1, out v2); v3 = Next<T3>(); }
    public void Make<T1, T2, T3, T4>(out T1 v1, out T2 v2, out T3 v3, out T4 v4) { Make(out v1, out v2, out v3); v4 = Next<T4>(); }
    public void Make<T1, T2, T3, T4, T5>(out T1 v1, out T2 v2, out T3 v3, out T4 v4, out T5 v5) { Make(out v1, out v2, out v3, out v4); v5 = Next<T5>(); }
    public void Make<T1, T2, T3, T4, T5, T6>(out T1 v1, out T2 v2, out T3 v3, out T4 v4, out T5 v5, out T6 v6) { Make(out v1, out v2, out v3, out v4, out v5); v6 = Next<T6>(); }
    public void Make<T1, T2, T3, T4, T5, T6, T7>(out T1 v1, out T2 v2, out T3 v3, out T4 v4, out T5 v5, out T6 v6, out T7 v7) { Make(out v1, out v2, out v3, out v4, out v5, out v6); v7 = Next<T7>(); }
    //public (T1, T2) Make<T1, T2>() { Make(out T1 v1, out T2 v2); return (v1, v2); }
    //public (T1, T2, T3) Make<T1, T2, T3>() { Make(out T1 v1, out T2 v2, out T3 v3); return (v1, v2, v3); }
    //public (T1, T2, T3, T4) Make<T1, T2, T3, T4>() { Make(out T1 v1, out T2 v2, out T3 v3, out T4 v4); return (v1, v2, v3, v4); }
}

#endregion


using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace Complete_Tripartite
{
    internal class Program
    {
        private static readonly StreamReader reader = new StreamReader(Console.OpenStandardInput(1024*10), Encoding.ASCII, false, 1024*10);
        private static readonly StreamWriter writer = new StreamWriter(Console.OpenStandardOutput(1024*10), Encoding.ASCII, 1024*10);

        private static void Main(string[] args)
        {
            Solve(args);
            writer.Flush();
        }

        private static void Solve(string[] args)
        {
            int n = Next();
            int m = Next();

            var lists = new List<int>[n];
            for (int i = 0; i < lists.Length; i++)
            {
                lists[i] = new List<int>(2);
            }
            for (int i = 0; i < m; i++)
            {
                int a = Next() - 1;
                int b = Next() - 1;
                lists[a].Add(b);
                lists[b].Add(a);
            }

            foreach (var t in lists)
            {
                t.Sort();
                t.Add(n);
            }

            var nn = new int[n];
            int g = 0;
            for (int i = 0; i < n; i++)
            {
                if (nn[i] == 0)
                {
                    g++;
                    if (g == 4)
                    {
                        writer.WriteLine("-1");
                        return;
                    }
                    nn[i] = g;
                    int index = 0;
                    for (int j = i + 1; j < n; j++)
                    {
                        while (lists[i][index] < j)
                        {
                            index++;
                        }
                        if (lists[i][index] == j)
                            continue;
                        if (lists[i].Count != lists[j].Count)
                        {
                            writer.WriteLine("-1");
                            return;
                        }
                        for (int k = 0; k < lists[i].Count; k++)
                        {
                            if (lists[i][k] != lists[j][k])
                            {
                                writer.WriteLine("-1");
                                return;
                            }
                        }
                        nn[j] = g;
                    }
                }
            }
            if (g != 3)
            {
                writer.WriteLine("-1");
                return;
            }

            foreach (int i in nn)
            {
                writer.Write(i);
                writer.Write(' ');
            }
            writer.WriteLine();
        }

        private static int Next()
        {
            int c;
            int res = 0;
            do
            {
                c = reader.Read();
                if (c == -1)
                    return res;
            } while (c < '0' || c > '9');
            res = c - '0';
            while (true)
            {
                c = reader.Read();
                if (c < '0' || c > '9')
                    return res;
                res *= 10;
                res += c - '0';
            }
        }
    }
}


#![allow(unused_imports)]
use std::io::{BufReader, BufWriter, stdin, stdout, Write};
use std::cmp::{min, max};
use std::collections::{VecDeque, HashMap, BTreeMap, HashSet, BTreeSet, BinaryHeap};

struct UnionFind {
	par:Vec<usize>,
	sz:Vec<usize>,
}

impl UnionFind {
	fn init(&mut self, n:usize) {
		self.par = vec![0; n + 1];
		self.sz = vec![1; n + 1];
	}
	fn find(&mut self, x:usize) -> usize {
		let p = self.par[x];
		if p == 0 {
			return x;
		}
		self.par[x] = self.find(p);
		return self.par[x];
	}
	fn merge(&mut self, x:usize, y:usize) -> bool {
		let x = self.find(x);
		let y = self.find(y);
		if x == y {
			return false;
		}
		self.par[y] = x;
		self.sz[x] += self.sz[y];
		return true;
	}
	fn size(&mut self, x:usize) -> usize {
		return self.sz[x];
	}
}

fn main() {
	let mut inp = Iinput::default();
	let out = &mut BufWriter::new(stdout());
	let n = inp.scan::<usize>();
	let m = inp.scan::<usize>();
	let mut edge = vec![Vec::<usize>::new(); n + 1];
	for _ in 0..m {
		let x = inp.scan::<usize>();
		let y = inp.scan::<usize>();
		edge[x].push(y);
		edge[y].push(x);
	}
	let mut st = Vec::<usize>::new();
	for i in 1..n+1 {
		st.push(i);
	}
	let mut check = vec![0; n + 1];
	let mut uf:UnionFind = UnionFind { par:Vec::new(), sz:Vec::new() };
	uf.init(n);
	for i in 1..n+1 {
		for &j in &edge[i] {
			check[uf.find(j)] += 1;
		}
		let mut nxt = Vec::<usize>::new();
		for &j in &st {
			if uf.size(j) == check[j] {
				nxt.push(j);
			}
			else {
				uf.merge(i, j);
			}
		}
		nxt.push(uf.find(i));
		st = nxt;
		for &j in &edge[i] {
			check[uf.find(j)] = 0;
		}
	}
	if st.len() != 3 {
		writeln!(out, "-1");
		return;
	}
	for i in 1..n+1 {
		for &j in &edge[i] {
			if uf.find(i) == uf.find(j) {
				writeln!(out, "-1");
				return;
			}
		}
	}
	let mut ans = vec![0 as usize; n + 1];
	for i in 0..3 {
		for j in 1..n+1 {
			if uf.find(j) == st[i] {
				ans[j] = i + 1;
			}
		}
	}
	for i in 1..n+1 {
		write!(out, "{} ", ans[i]);
	}
}

#[derive(Default)]
struct Iinput {
	buffer: Vec<String>
}

impl Iinput {
	fn scan<T: std::str::FromStr>(&mut self) -> T {
		loop {
			if let Some(token) = self.buffer.pop() {
				return token.parse().ok().expect("Parsing Error");
			}
			let mut s = String::new();
			stdin().read_line(&mut s).expect("Reading Error");
			self.buffer = s.split_whitespace().rev().map(String::from).collect();
		}
	}
}


use std::io::{Read,stdin};
use std::collections::BinaryHeap;

fn main() {
    let mut buf = String::new();
    stdin().read_to_string(&mut buf).unwrap();
    let mut tok = buf.split_whitespace();
    let mut get = || tok.next().unwrap();
    macro_rules! get {
        ($t:ty) => (get().parse::<$t>().unwrap());
        () => (get!(usize));
    }

    let n = get!();
    let m = get!();

    let mut graph = vec![vec![]; n+1];
    for _ in 0 .. m {
        let a = get!();
        let b = get!();
        graph[a].push(b);
        graph[b].push(a);
    }
    let mut sets = vec![0; n+1];
    let mut nots = vec![0; n+1];
    let mut stk = BinaryHeap::new();
    for i in 1 ..= n {
        if graph[i].len() == 0 {
            println!("-1");
            return;
        }
        stk.push(i);
    }
    while let Some(v) = stk.pop() {
        let v = v % (n+1);
        if sets[v] == 0 {
            for g in 1 ..= 3 {
                if nots[v] & (1 << g) == 0 {
                    sets[v] = g;
                    break;
                }
            }
        }
        let g = sets[v];
        if g == 0 {
            println!("-1");
            return;
        }
        for &w in graph[v].iter() {
            if sets[w] == g {
                println!("-1");
                return;
            }
            if sets[w] != 0 {
                continue;
            }
            nots[w] |= 1 << g;
            match nots[w] {
            6 => sets[w] = 3, // (1<<1)|(1<<2)
            10 => sets[w] = 2, // (1<<1)|(1<<3)
            12 => sets[w] = 1, // (1<<2)|(1<<3)
            14 => {
                println!("-1");
                return;
            },
            _ => {},
            }
            let c = ((nots[w]>>3)&1)+((nots[w]>>2)&1)+((nots[w]>>1)&1);
            stk.push(c * (n+1) + w);
        }
    }
    let mut ss = vec![0; 4];
    for i in 1 ..= n {
        if sets[i] == 0 {
            println!("-1");
            return;
        }
        ss[sets[i]] += 1;
    }
    for i in 1 .. ss.len() {
        if ss[i] == 0 {
            println!("-1");
            return;
        }
    }
    for i in 1 ..= n {
        if i > 1 {
            print!(" ");
        }
        print!("{}", sets[i]);
    }
    println!();
}


use std::io::{Read,stdin};
use std::collections::BinaryHeap;

fn main() {
    let mut buf = String::new();
    stdin().read_to_string(&mut buf).unwrap();
    let mut tok = buf.split_whitespace();
    let mut get = || tok.next().unwrap();
    macro_rules! get {
        ($t:ty) => (get().parse::<$t>().unwrap());
        () => (get!(usize));
    }

    let n = get!();
    let m = get!();

    let mut graph = vec![vec![]; n+1];
    for _ in 0 .. m {
        let a = get!();
        let b = get!();
        graph[a].push(b);
        graph[b].push(a);
    }
    let mut sets = vec![0; n+1];
    let mut nots = vec![0; n+1];
    let mut stk = BinaryHeap::new();
    for i in 1 ..= n {
        if graph[i].len() < 2 {
            println!("-1");
            return;
        }
        stk.push(i);
    }
    while let Some(v) = stk.pop() {
        let v = v % (n+1);
        if sets[v] == 0 {
            for g in 1 ..= 3 {
                if nots[v] & (1 << g) == 0 {
                    sets[v] = g;
                    break;
                }
            }
        }
        let g = sets[v];
        if g == 0 {
            println!("-1");
            return;
        }
        for &w in graph[v].iter() {
            if sets[w] == g {
                println!("-1");
                return;
            }
            if sets[w] != 0 {
                continue;
            }
            nots[w] |= 1 << g;
            match nots[w] {
            6 => sets[w] = 3, // (1<<1)|(1<<2)
            10 => sets[w] = 2, // (1<<1)|(1<<3)
            12 => sets[w] = 1, // (1<<2)|(1<<3)
            14 => {
                println!("-1");
                return;
            },
            _ => {},
            }
            let c = ((nots[w]>>3)&1)+((nots[w]>>2)&1)+((nots[w]>>1)&1);
            stk.push(c * (n+1) + w);
        }
    }
    let mut ss = vec![0; 4];
    for i in 1 ..= n {
        if sets[i] == 0 {
            println!("-1");
            return;
        }
        ss[sets[i]] += 1;
    }
    for i in 1 ..= n {
        let mut tt = vec![0; 4];
        for &w in graph[i].iter() {
            tt[sets[w]] += 1;
            if sets[w] == sets[i] {
                println!("-1");
                return;
            }
        }
        if n - ss[sets[i]] != tt.iter().sum() {
            println!("-1");
            return;
        }
    }
    for i in 1 .. ss.len() {
        if ss[i] == 0 {
            println!("-1");
            return;
        }
    }
    for i in 1 ..= n {
        if i > 1 {
            print!(" ");
        }
        print!("{}", sets[i]);
    }
    println!();
}


use std::io::{Read,stdin};
use std::collections::BinaryHeap;

fn main() {
    let mut buf = String::new();
    stdin().read_to_string(&mut buf).unwrap();
    let mut tok = buf.split_whitespace();
    let mut get = || tok.next().unwrap();
    macro_rules! get {
        ($t:ty) => (get().parse::<$t>().unwrap());
        () => (get!(usize));
    }

    let n = get!();
    let m = get!();

    let mut graph = vec![vec![]; n+1];
    let mut s = 1;
    for _ in 0 .. m {
        let a = get!();
        let b = get!();
        graph[a].push(b);
        graph[b].push(a);
        s = a;
    }
    let mut sets = vec![0; n+1];
    let mut nots = vec![0; n+1];
    sets[s] = 1;
    let mut stk = BinaryHeap::new();
    stk.push(s);
    while let Some(v) = stk.pop() {
        let v = v % (n+1);
        if sets[v] == 0 {
            for g in 1 ..= 3 {
                if nots[v] & (1 << g) == 0 {
                    sets[v] = g;
                    break;
                }
            }
        }
        let g = sets[v];
        if g == 0 {
            println!("-1");
            return;
        }
        for &w in graph[v].iter() {
            if sets[w] == g {
                println!("-1");
                return;
            }
            if sets[w] != 0 {
                continue;
            }
            nots[w] |= 1 << g;
            match nots[w] {
            6 => sets[w] = 3, // (1<<1)|(1<<2)
            10 => sets[w] = 2, // (1<<1)|(1<<3)
            12 => sets[w] = 1, // (1<<2)|(1<<3)
            14 => {
                println!("-1");
                return;
            },
            _ => {},
            }
            let c = ((nots[w]>>3)&1)+((nots[w]>>2)&1)+((nots[w]>>1)&1);
            stk.push(c * (n+1) + w);
        }
    }
    let mut ss = vec![0; 4];
    for i in 1 ..= n {
        if sets[i] == 0 {
            println!("-1");
            return;
        }
        ss[sets[i]] += 1;
    }
    for i in 1 .. ss.len() {
        if ss[i] == 0 {
            println!("-1");
            return;
        }
    }
    for i in 1 ..= n {
        if i > 1 {
            print!(" ");
        }
        print!("{}", sets[i]);
    }
    println!();
}


use std::io::{Read,stdin};
use std::collections::BinaryHeap;

fn main() {
    let mut buf = String::new();
    stdin().read_to_string(&mut buf).unwrap();
    let mut tok = buf.split_whitespace();
    let mut get = || tok.next().unwrap();
    macro_rules! get {
        ($t:ty) => (get().parse::<$t>().unwrap());
        () => (get!(usize));
    }

    let n = get!();
    let m = get!();

    let mut graph = vec![vec![]; n+1];
    let mut s = 1;
    for _ in 0 .. m {
        let a = get!();
        let b = get!();
        graph[a].push(b);
        graph[b].push(a);
        s = a;
    }
    let mut sets = vec![0; n+1];
    let mut nots = vec![0; n+1];
    sets[s] = 1;
    let mut stk = BinaryHeap::new();
    stk.push(s);
    while let Some(v) = stk.pop() {
        let v = v % (n+1);
        if sets[v] == 0 {
            for g in 1 ..= 3 {
                if nots[v] & (1 << g) == 0 {
                    sets[v] = g;
                    break;
                }
            }
        }
        let g = sets[v];
        if g == 0 {
            println!("-1");
            return;
        }
        for &w in graph[v].iter() {
            if sets[w] == g {
                println!("-1");
                return;
            }
            if sets[w] != 0 {
                continue;
            }
            nots[w] |= 1 << g;
            match nots[w] {
            6 => sets[w] = 3, // (1<<1)|(1<<2)
            10 => sets[w] = 2, // (1<<1)|(1<<3)
            12 => sets[w] = 1, // (1<<2)|(1<<3)
            14 => {
                println!("-1");
                return;
            },
            _ => {},
            }
            let c = ((nots[w]>>3)&1)+((nots[w]>>2)&1)+((nots[w]>>1)&1);
            stk.push(c * (n+1) + w);
        }
    }
    for i in 1 ..= n {
        if sets[i] == 0 {
            println!("-1");
            return;
        }
    }
    for i in 1 ..= n {
        if i > 1 {
            print!(" ");
        }
        print!("{}", sets[i]);
    }
    println!();
}


use std::io::{Read,stdin};
use std::collections::BinaryHeap;

fn main() {
    let mut buf = String::new();
    stdin().read_to_string(&mut buf).unwrap();
    let mut tok = buf.split_whitespace();
    let mut get = || tok.next().unwrap();
    macro_rules! get {
        ($t:ty) => (get().parse::<$t>().unwrap());
        () => (get!(usize));
    }

    let n = get!();
    let m = get!();

    let mut graph = vec![vec![]; n+1];
    for _ in 0 .. m {
        let a = get!();
        let b = get!();
        graph[a].push(b);
        graph[b].push(a);
    }
    let mut sets = vec![0; n+1];
    let mut nots = vec![0; n+1];
    let mut stk = BinaryHeap::new();
    for i in 1 ..= n {
        stk.push(i);
    }
    while let Some(v) = stk.pop() {
        let v = v % (n+1);
        if sets[v] == 0 {
            for g in 1 ..= 3 {
                if nots[v] & (1 << g) == 0 {
                    sets[v] = g;
                    break;
                }
            }
        }
        let g = sets[v];
        if g == 0 {
            println!("-1");
            return;
        }
        for &w in graph[v].iter() {
            if sets[w] == g {
                println!("-1");
                return;
            }
            if sets[w] != 0 {
                continue;
            }
            nots[w] |= 1 << g;
            match nots[w] {
            6 => sets[w] = 3, // (1<<1)|(1<<2)
            10 => sets[w] = 2, // (1<<1)|(1<<3)
            12 => sets[w] = 1, // (1<<2)|(1<<3)
            14 => {
                println!("-1");
                return;
            },
            _ => {},
            }
            let c = ((nots[w]>>3)&1)+((nots[w]>>2)&1)+((nots[w]>>1)&1);
            stk.push(c * (n+1) + w);
        }
    }
    let mut ss = vec![0; 4];
    for i in 1 ..= n {
        if sets[i] == 0 {
            println!("-1");
            return;
        }
        ss[sets[i]] += 1;
    }
    for i in 1 .. ss.len() {
        if ss[i] == 0 {
            println!("-1");
            return;
        }
    }
    for i in 1 ..= n {
        if i > 1 {
            print!(" ");
        }
        print!("{}", sets[i]);
    }
    println!();
}


use std::io::{Read,stdin};

fn main() {
    let mut buf = String::new();
    stdin().read_to_string(&mut buf).unwrap();
    let mut tok = buf.split_whitespace();
    let mut get = || tok.next().unwrap();
    macro_rules! get {
        ($t:ty) => (get().parse::<$t>().unwrap());
        () => (get!(usize));
    }

    let n = get!();
    let m = get!();

    let mut graph = vec![vec![]; n+1];
    let mut s = 1;
    for _ in 0 .. m {
        let a = get!();
        let b = get!();
        graph[a].push(b);
        graph[b].push(a);
        s = a;
    }
    let mut sets = vec![0; n+1];
    let mut nots = vec![0; n+1];
    sets[s] = 1;
    let mut stk = vec![];
    stk.push(s);
    while let Some(v) = stk.pop() {
        if sets[v] == 0 {
            for g in 1 ..= 3 {
                if nots[v] & (1 << g) == 0 {
                    sets[v] = g;
                    break;
                }
            }
        }
        let g = sets[v];
        for &w in graph[v].iter() {
            if sets[w] == g {
                println!("-1");
                return;
            }
            if sets[w] != 0 {
                continue;
            }
            nots[w] |= 1 << g;
            if nots[w] == (1<<1)|(1<<2)|(1<<3) {
                println!("-1");
                return;
            }
            stk.push(w);
        }
    }
    for i in 1 ..= n {
        if i > 1 {
            print!(" ");
        }
        print!("{}", std::cmp::max(1, sets[i]));
    }
    println!();
}


use std::io::{Read,stdin};
use std::collections::BinaryHeap;

fn main() {
    let mut buf = String::new();
    stdin().read_to_string(&mut buf).unwrap();
    let mut tok = buf.split_whitespace();
    let mut get = || tok.next().unwrap();
    macro_rules! get {
        ($t:ty) => (get().parse::<$t>().unwrap());
        () => (get!(usize));
    }

    let n = get!();
    let m = get!();

    let mut graph = vec![vec![]; n+1];
    let mut s = 1;
    for _ in 0 .. m {
        let a = get!();
        let b = get!();
        graph[a].push(b);
        graph[b].push(a);
        s = a;
    }
    let mut sets = vec![0; n+1];
    let mut nots = vec![0; n+1];
    sets[s] = 1;
    let mut stk = BinaryHeap::new();
    stk.push(s);
    while let Some(v) = stk.pop() {
        let v = v % (n+1);
        if sets[v] == 0 {
            for g in 1 ..= 3 {
                if nots[v] & (1 << g) == 0 {
                    sets[v] = g;
                    break;
                }
            }
        }
        let g = sets[v];
        for &w in graph[v].iter() {
            if sets[w] == g {
                println!("-1");
                return;
            }
            if sets[w] != 0 {
                continue;
            }
            nots[w] |= 1 << g;
            match nots[w] {
            6 => sets[w] = 3, // (1<<1)|(1<<2)
            10 => sets[w] = 2, // (1<<1)|(1<<3)
            12 => sets[w] = 1, // (1<<2)|(1<<3)
            14 => {
                println!("-1");
                return;
            },
            _ => {},
            }
            let c = ((nots[w]>>3)&1)+((nots[w]>>2)&1)+((nots[w]>>1)&1);
            stk.push(c * (n+1) + w);
        }
    }
    for i in 1 ..= n {
        if sets[i] == 0 {
            println!("-1");
            return;
        }
    }
    for i in 1 ..= n {
        if i > 1 {
            print!(" ");
        }
        print!("{}", sets[i]);
    }
    println!();
}


use std::io::{Read,stdin};

fn main() {
    let mut buf = String::new();
    stdin().read_to_string(&mut buf).unwrap();
    let mut tok = buf.split_whitespace();
    let mut get = || tok.next().unwrap();
    macro_rules! get {
        ($t:ty) => (get().parse::<$t>().unwrap());
        () => (get!(usize));
    }

    let n = get!();
    let m = get!();

    let mut graph = vec![vec![]; n+1];
    let mut s = 1;
    for _ in 0 .. m {
        let a = get!();
        let b = get!();
        graph[a].push(b);
        graph[b].push(a);
        s = a;
    }
    let mut sets = vec![0; n+1];
    let mut nots = vec![0; n+1];
    sets[s] = 1;
    let mut stk = vec![];
    stk.push(s);
    while let Some(v) = stk.pop() {
        if sets[v] == 0 {
            for g in 1 ..= 3 {
                if nots[v] & (1 << g) == 0 {
                    sets[v] = g;
                    break;
                }
            }
        }
        let g = sets[v];
        for &w in graph[v].iter() {
            if sets[w] == g {
                println!("-1");
                return;
            }
            if sets[w] != 0 {
                continue;
            }
            nots[w] |= 1 << g;
            match nots[w] {
            6 => sets[w] = 3, // (1<<2)|(1<<4)
            10 => sets[w] = 2, // (1<<2)|(1<<8)
            12 => sets[w] = 1, // (1<<4)|(1<<8)
            14 => {
                println!("-1");
                return;
            },
            _ => {},
            }
            stk.push(w);
        }
    }
    for i in 1 ..= n {
        if sets[i] == 0 {
            println!("-1");
            return;
        }
    }
    for i in 1 ..= n {
        if i > 1 {
            print!(" ");
        }
        print!("{}", sets[i]);
    }
    println!();
}


use std::io::{Read,stdin};
use std::collections::BinaryHeap;

fn main() {
    let mut buf = String::new();
    stdin().read_to_string(&mut buf).unwrap();
    let mut tok = buf.split_whitespace();
    let mut get = || tok.next().unwrap();
    macro_rules! get {
        ($t:ty) => (get().parse::<$t>().unwrap());
        () => (get!(usize));
    }

    let n = get!();
    let m = get!();

    let mut graph = vec![vec![]; n+1];
    for _ in 0 .. m {
        let a = get!();
        let b = get!();
        graph[a].push(b);
        graph[b].push(a);
    }
    let mut sets = vec![0; n+1];
    let mut nots = vec![0; n+1];
    let mut stk = BinaryHeap::new();
    for i in 1 ..= n {
        if graph[i].len() < 2 {
            println!("-1");
            return;
        }
        stk.push(i);
    }
    while let Some(v) = stk.pop() {
        let v = v % (n+1);
        if sets[v] == 0 {
            for g in 1 ..= 3 {
                if nots[v] & (1 << g) == 0 {
                    sets[v] = g;
                    break;
                }
            }
        }
        let g = sets[v];
        if g == 0 {
            println!("-1");
            return;
        }
        for &w in graph[v].iter() {
            if sets[w] == g {
                println!("-1");
                return;
            }
            if sets[w] != 0 {
                continue;
            }
            nots[w] |= 1 << g;
            match nots[w] {
            6 => sets[w] = 3, // (1<<1)|(1<<2)
            10 => sets[w] = 2, // (1<<1)|(1<<3)
            12 => sets[w] = 1, // (1<<2)|(1<<3)
            14 => {
                println!("-1");
                return;
            },
            _ => {},
            }
            let c = ((nots[w]>>3)&1)+((nots[w]>>2)&1)+((nots[w]>>1)&1);
            stk.push(c * (n+1) + w);
        }
    }
    let mut ss = vec![0; 4];
    for i in 1 ..= n {
        if sets[i] == 0 {
            println!("-1");
            return;
        }
        ss[sets[i]] += 1;
    }
    for i in 1 .. ss.len() {
        if ss[i] == 0 {
            println!("-1");
            return;
        }
    }
    for i in 1 ..= n {
        if i > 1 {
            print!(" ");
        }
        print!("{}", sets[i]);
    }
    println!();
}


mod io {
    use std::io::{self, BufRead};
    use std::str::{self, FromStr, SplitAsciiWhitespace};

    pub type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;

    pub struct Scanner<'a, R> {
        reader: R,
        buf_str: Vec<u8>,
        buf_iter: SplitAsciiWhitespace<'a>,
    }

    impl<R: BufRead> Scanner<'_, R> {
        pub fn new(reader: R) -> Self {
            Self {
                reader,
                buf_str: Vec::new(),
                buf_iter: "".split_ascii_whitespace(),
            }
        }

        pub fn try_token<T>(&mut self) -> Result<T>
            where T: FromStr,
                  <T as FromStr>::Err: 'static + std::error::Error
        {
            loop {
                if let Some(token) = self.buf_iter.next() {
                    return Ok(token.parse()?);
                }
                self.buf_str.clear();
                let bytes_read = self.reader
                    .read_until(b'\n', &mut self.buf_str)?;
                if bytes_read == 0 {
                    return Err(Box::new(io::Error::from(io::ErrorKind::UnexpectedEof)));
                }
                self.buf_iter = unsafe {
                    let slice = str::from_utf8_unchecked(&self.buf_str);
                    ::std::mem::transmute(slice.split_ascii_whitespace())
                }
            }
        }

        pub fn token<T>(&mut self) -> T
            where T: FromStr,
                  <T as FromStr>::Err: 'static + std::error::Error
        {
            self.try_token().unwrap()
        }
    }
}

use std::io::{stdin, stdout, BufRead, Write};
use io::Scanner;

fn main() {
    let stdin = stdin();
    solve(Scanner::new(stdin.lock()), stdout().lock());
}

use std::collections::{HashSet, HashMap, hash_map::DefaultHasher};
use std::hash::{Hasher, Hash};
use std::iter::repeat_with;

fn solve<R: BufRead, W: Write>(mut scan: Scanner<R>, mut out: W) {
    let n: usize = scan.token();
    let m: usize = scan.token();
    let mut adjacent: Vec<HashSet<u32>> =
        repeat_with(|| HashSet::default())
            .take(n + 1).collect();
    for _ in 0..m {
        let a: u32 = scan.token();
        let b: u32 = scan.token();
        adjacent[a as usize].insert(b);
        adjacent[b as usize].insert(a);
    }

    let mut sets: HashMap<u64, (u32, u32)> = HashMap::new();
    let mut set: Vec<u32> = vec![0; n + 1];
    let mut s_count: u32 = 0;
    for (u, adj) in adjacent.iter().enumerate().skip(1) {
        let h = adj.iter()
            .fold(0u64, |acc, adj_u| {
                let mut hasher = DefaultHasher::new();
                adj_u.hash(&mut hasher);
                acc ^ hasher.finish()
            });
        let s = sets.entry(h)
            .or_insert_with(|| {
                s_count += 1;
                (s_count, 0)
            });
        s.1 += 1;
        set[u] = s.0;
        if s_count > 3 { break; }
    }

    if s_count == 3 {
        set.into_iter()
            .skip(1)
            .for_each(|s| write!(out, "{} ", s).unwrap());
    } else {
        writeln!(out, "-1").unwrap();
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use helpers::t;

    #[test]
    fn example1() {
        let mut out = String::new();
        solve(Scanner::new("6 11
1 2
1 3
1 4
1 5
1 6
2 4
2 5
2 6
3 4
3 5
3 6
".as_bytes()), unsafe { out.as_mut_vec() });
        // "1 2 2 3 3 3 "
        let mut r: Vec<i32> = out.split_ascii_whitespace()
            .map(|s| s.parse().unwrap())
            .collect();
        println!("Expected pattern 'a b b c c c '\n found '{}'", out);
        assert_ne!(r[0], r[1]);
        assert_eq!(r[1], r[2]);
        assert_ne!(r[2], r[3]);
        assert_eq!(r[3], r[4]);
        assert_eq!(r[4], r[5]);
        assert_ne!(r[0], r[3]);
        r.sort();
        r.dedup_by_key(|&mut x| x);
        assert_eq!(r, [1, 2, 3]);
    }

    #[test]
    fn example2() {
        t!("4 6
1 2
1 3
1 4
2 3
2 4
3 4
", "-1\n");
    }

    #[test]
    fn t() {
        t!("3 0\n", "-1\n");
        t!("3 2\n1 2\n1 3\n", "-1\n");

        let mut out = String::new();
        solve(Scanner::new("3 3\n1 2\n1 3\n2 3".as_bytes()), unsafe { out.as_mut_vec() });
        let mut r = out.split_ascii_whitespace().collect::<Vec<_>>();
        r.sort();
        assert_eq!(r, ["1", "2", "3"]);
    }

    #[test]
    fn large_input() {
        use std::fmt::Write;
        // release build
        //            defHasher adjHash8b  DsuHash8b rewrite
        // 100000 199997 215 s  126 s      122 s     90 ms
        // 20000 39997   8.5 s  4.9 s      4.9 s
        // 10000 19997   2.2 s  1.2 s      1.2 s
        let mut inp = "100000 199997\n1 2\n".to_owned();
        for i in 3..=100000 {
            inp.write_fmt(format_args!("1 {0}\n2 {0}\n", i)).unwrap();
        }
        let mut out = String::new();
        solve(Scanner::new(inp.as_bytes()), unsafe { out.as_mut_vec() });
        assert_ne!(out, "-1\n");
//        assert!(false);
    }
}


mod io {
    use std::io::{self, BufRead};
    use std::str::{self, FromStr, SplitAsciiWhitespace};

    pub type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;

    pub struct Scanner<'a, R> {
        reader: R,
        buf_str: Vec<u8>,
        buf_iter: SplitAsciiWhitespace<'a>,
    }

    impl<R: BufRead> Scanner<'_, R> {
        pub fn new(reader: R) -> Self {
            Self {
                reader,
                buf_str: Vec::new(),
                buf_iter: "".split_ascii_whitespace(),
            }
        }

        pub fn try_token<T>(&mut self) -> Result<T>
            where T: FromStr,
                  <T as FromStr>::Err: 'static + std::error::Error
        {
            loop {
                if let Some(token) = self.buf_iter.next() {
                    return Ok(token.parse()?);
                }
                self.buf_str.clear();
                let bytes_read = self.reader
                    .read_until(b'\n', &mut self.buf_str)?;
                if bytes_read == 0 {
                    return Err(Box::new(io::Error::from(io::ErrorKind::UnexpectedEof)));
                }
                self.buf_iter = unsafe {
                    let slice = str::from_utf8_unchecked(&self.buf_str);
                    ::std::mem::transmute(slice.split_ascii_whitespace())
                }
            }
        }

        pub fn token<T>(&mut self) -> T
            where T: FromStr,
                  <T as FromStr>::Err: 'static + std::error::Error
        {
            self.try_token().unwrap()
        }
    }
}

mod data_struct {
    use std::collections::HashSet;
    use super::BuildHash8b;

    pub struct Dsu {
        pub parent: Vec<u32>,
        pub size: Vec<u32>,
        pub sets: HashSet<u32, BuildHash8b>,
    }

    impl Dsu {
        pub fn new(size: usize) -> Dsu {
            Dsu {
                parent: vec![0; size + 1],
                size: vec![0; size + 1],
                sets: HashSet::default(),
            }
        }

        pub fn make_set(&mut self, v: u32) {
            self.parent[v as usize] = v;
            self.size[v as usize] = 1;
            self.sets.insert(v);
        }

        pub fn find_set(&mut self, v: u32) -> u32 {
            if v == self.parent[v as usize] {
                v
            } else {
                self.parent[v as usize] = self.find_set(self.parent[v as usize]);
                self.parent[v as usize]
            }
        }

        pub fn union_sets(&mut self, mut a: u32, mut b: u32) {
            a = self.find_set(a);
            b = self.find_set(b);
            if a != b {
                if self.size[a as usize] < self.size[b as usize] {
                    std::mem::swap(&mut a, &mut b);
                }
                self.parent[b as usize] = a;
                self.size[a as usize] += self.size[b as usize];
                self.sets.remove(&b);
            }
        }
    }
}

use std::io::{stdin, stdout, BufRead, Write};
use io::Scanner;

fn main() {
    let stdin = stdin();
    solve(Scanner::new(stdin.lock()), stdout().lock());
}

use std::collections::{HashSet, HashMap};
use std::hash::{Hasher, BuildHasherDefault};
use std::iter::repeat_with;

use data_struct::Dsu;

#[derive(Default)]
pub struct Hash8b(u64);

pub type BuildHash8b = BuildHasherDefault<Hash8b>;

impl Hasher for Hash8b {
    fn finish(&self) -> u64 { self.0 }
    fn write(&mut self, bytes: &[u8]) {
        let mut buf = [0u8; 8];
        buf.as_mut().write_all(bytes).unwrap();
        self.0 = u64::from_ne_bytes(buf);
    }
}

macro_rules! answer_impos { ($o:expr) => { writeln!($o, "-1").unwrap(); return; }; }

fn solve<R: BufRead, W: Write>(mut scan: Scanner<R>, mut out: W) {
    let n: u32 = scan.token();
    let m: u32 = scan.token();
    let mut adjacent: Vec<HashSet<u32, BuildHash8b>> =
        repeat_with(|| HashSet::default())
            .take(n as usize + 1).collect();
    for _ in 0..m {
        let a: u32 = scan.token();
        let b: u32 = scan.token();
        adjacent[a as usize].insert(b);
        adjacent[b as usize].insert(a);
    }
    let mut dsu = Dsu::new(n as usize);
    let mut pool: HashSet<u32, BuildHash8b> = HashSet::default();
    pool.extend(1..=n);

    let u1 = pool.take(&1).unwrap();
    dsu.make_set(u1);
    let u1_adj = &adjacent[u1 as usize];
    pool.retain(|&u| {
        dsu.make_set(u);
        if !u1_adj.contains(&u) {
            dsu.union_sets(u1, u);
            false
        } else { true }
    });

    if pool.is_empty() { answer_impos!(out); }
    let &u2 = pool.iter().next().unwrap();
    pool.remove(&u2);
    let u2_adj = &adjacent[u2 as usize];
    pool.retain(|&u| {
        if u1_adj.contains(&u) && !u2_adj.contains(&u) {
            dsu.union_sets(u2, u);
            false
        } else { true }
    });

    if pool.is_empty() { answer_impos!(out); }
    let &u3 = pool.iter().next().unwrap();
    pool.remove(&u3);
    let u3_adj = &adjacent[u3 as usize];
    pool.retain(|&u| {
        if u1_adj.contains(&u) && u2_adj.contains(&u) && !u3_adj.contains(&u) {
            dsu.union_sets(u3, u);
            false
        } else { true }
    });

    let v1 = dsu.find_set(u1) as usize;
    let v2 = dsu.find_set(u2) as usize;
    let v3 = dsu.find_set(u3) as usize;
    let s = &dsu.size;

    if !(pool.is_empty() && dsu.sets.len() == 3
        && m == s[v1] * s[v2] + s[v2] * s[v3] + s[v3] * s[v1])
    { answer_impos!(out); }

    let v: HashMap<u32, u8> = dsu.sets.iter().cloned().zip(1..).collect();
    dsu.parent[1..].iter().for_each(|s| write!(out, "{} ", v[s]).unwrap());
}

#[cfg(test)]
mod tests {
    use super::*;
    use helpers::t;

    #[test]
    fn example1() {
        let mut out = String::new();
        solve(Scanner::new("6 11
1 2
1 3
1 4
1 5
1 6
2 4
2 5
2 6
3 4
3 5
3 6
".as_bytes()), unsafe { out.as_mut_vec() });
        // "1 2 2 3 3 3 "
        let mut r: Vec<i32> = out.split_ascii_whitespace()
            .map(|s| s.parse().unwrap())
            .collect();
        println!("Expected pattern 'a b b c c c '\n found '{}'", out);
        assert_ne!(r[0], r[1]);
        assert_eq!(r[1], r[2]);
        assert_ne!(r[2], r[3]);
        assert_eq!(r[3], r[4]);
        assert_eq!(r[4], r[5]);
        assert_ne!(r[0], r[3]);
        r.sort();
        r.dedup_by_key(|&mut x| x);
        assert_eq!(r, [1, 2, 3]);
    }

    #[test]
    fn example2() {
        t!("4 6
1 2
1 3
1 4
2 3
2 4
3 4
", "-1\n");
    }

    #[test]
    fn t() {
        t!("3 0\n", "-1\n");
        t!("3 2\n1 2\n1 3\n", "-1\n");

        let mut out = String::new();
        solve(Scanner::new("3 3\n1 2\n1 3\n2 3".as_bytes()), unsafe { out.as_mut_vec() });
        let mut r = out.split_ascii_whitespace().collect::<Vec<_>>();
        r.sort();
        assert_eq!(r, ["1", "2", "3"]);
    }

    #[test]
    fn large_input() {
        use std::fmt::Write;
        // release build
        //            defHasher adjHash8b  DsuHash8b rewrite
        // 100000 199997 215 s  126 s      122 s     90 ms
        // 20000 39997   8.5 s  4.9 s      4.9 s
        // 10000 19997   2.2 s  1.2 s      1.2 s
        let mut inp = "100000 199997\n1 2\n".to_owned();
        for i in 3..=100000 {
            inp.write_fmt(format_args!("1 {0}\n2 {0}\n", i)).unwrap();
        }
        let mut out = String::new();
        solve(Scanner::new(inp.as_bytes()), unsafe { out.as_mut_vec() });
        assert_ne!(out, "-1\n");
//        assert!(false);
    }
}


mod io {
    use std::io::{self, BufRead};
    use std::str::{self, FromStr, SplitAsciiWhitespace};

    pub type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;

    pub struct Scanner<'a, R> {
        reader: R,
        buf_str: Vec<u8>,
        buf_iter: SplitAsciiWhitespace<'a>,
    }

    impl<R: BufRead> Scanner<'_, R> {
        pub fn new(reader: R) -> Self {
            Self {
                reader,
                buf_str: Vec::new(),
                buf_iter: "".split_ascii_whitespace(),
            }
        }

        pub fn try_token<T>(&mut self) -> Result<T>
            where T: FromStr,
                  <T as FromStr>::Err: 'static + std::error::Error
        {
            loop {
                if let Some(token) = self.buf_iter.next() {
                    return Ok(token.parse()?);
                }
                self.buf_str.clear();
                let bytes_read = self.reader
                    .read_until(b'\n', &mut self.buf_str)?;
                if bytes_read == 0 {
                    return Err(Box::new(io::Error::from(io::ErrorKind::UnexpectedEof)));
                }
                self.buf_iter = unsafe {
                    let slice = str::from_utf8_unchecked(&self.buf_str);
                    ::std::mem::transmute(slice.split_ascii_whitespace())
                }
            }
        }

        pub fn token<T>(&mut self) -> T
            where T: FromStr,
                  <T as FromStr>::Err: 'static + std::error::Error
        {
            self.try_token().unwrap()
        }
    }
}

mod data_struct {
    use std::collections::HashSet;
    use super::BuildHash8b;

    pub struct Dsu {
        pub parent: Vec<u32>,
        pub size: Vec<u32>,
        pub sets: HashSet<u32, BuildHash8b>,
    }

    impl Dsu {
        pub fn new(size: usize) -> Dsu {
            Dsu {
                parent: vec![0; size + 1],
                size: vec![0; size + 1],
                sets: HashSet::default(),
            }
        }

        pub fn make_set(&mut self, v: u32) {
            self.parent[v as usize] = v;
            self.size[v as usize] = 1;
            self.sets.insert(v);
        }

        pub fn find_set(&mut self, v: u32) -> u32 {
            if v == self.parent[v as usize] {
                v
            } else {
                self.parent[v as usize] = self.find_set(self.parent[v as usize]);
                self.parent[v as usize]
            }
        }

        pub fn union_sets(&mut self, mut a: u32, mut b: u32) {
            a = self.find_set(a);
            b = self.find_set(b);
            if a != b {
                if self.size[a as usize] < self.size[b as usize] {
                    std::mem::swap(&mut a, &mut b);
                }
                self.parent[b as usize] = a;
                self.size[a as usize] += self.size[b as usize];
                self.sets.remove(&b);
            }
        }
    }
}

use std::io::{stdin, stdout, BufRead, Write};
use io::Scanner;

fn main() {
    let stdin = stdin();
    solve(Scanner::new(stdin.lock()), stdout().lock());
}

use std::collections::{HashSet, HashMap};
use std::hash::{Hasher, BuildHasherDefault};
use std::iter::repeat_with;

use data_struct::Dsu;

#[derive(Default)]
pub struct Hash8b(u64);

pub type BuildHash8b = BuildHasherDefault<Hash8b>;

impl Hasher for Hash8b {
    fn finish(&self) -> u64 { self.0 }
    fn write(&mut self, bytes: &[u8]) {
        let mut buf = [0u8; 8];
        buf.as_mut().write_all(bytes).unwrap();
        self.0 = u64::from_ne_bytes(buf);
    }
}

macro_rules! answer_impos { ($o:expr) => { writeln!($o, "-1").unwrap(); return; }; }

fn solve<R: BufRead, W: Write>(mut scan: Scanner<R>, mut out: W) {
    let n: u32 = scan.token();
    let m: u32 = scan.token();
    let mut adjacent: Vec<HashSet<u32, BuildHash8b>> =
        repeat_with(|| HashSet::default())
            .take(n as usize + 1).collect();
    for _ in 0..m {
        let a: u32 = scan.token();
        let b: u32 = scan.token();
        adjacent[a as usize].insert(b);
        adjacent[b as usize].insert(a);
    }
    let mut dsu = Dsu::new(n as usize);
    let mut pool: HashSet<u32, BuildHash8b> = HashSet::default();
    pool.extend(1..=n);

    let u1 = pool.take(&1).unwrap();
    dsu.make_set(u1);
    let u1_adj = &adjacent[u1 as usize];
    pool.retain(|&u| {
        dsu.make_set(u);
        if !u1_adj.contains(&u) {
            dsu.union_sets(u1, u);
            false
        } else { true }
    });

    let u2 = u1_adj.iter().next();
    if u2.is_none() || pool.is_empty() { answer_impos!(out); }
    let &u2 = u2.unwrap();
    let u2_adj = &adjacent[u2 as usize];
    pool.retain(|&u| {
        if !u2_adj.contains(&u) {
            dsu.union_sets(u2, u);
            false
        } else { true }
    });

    if pool.is_empty() { answer_impos!(out); }
    let &u3 = pool.iter().next().unwrap();
    pool.remove(&u3);
    pool.retain(|&u| {
        if u1_adj.contains(&u) && u2_adj.contains(&u) {
            dsu.union_sets(u3, u);
            false
        } else { true }
    });

    let v1 = dsu.find_set(u1) as usize;
    let v2 = dsu.find_set(u2) as usize;
    let v3 = dsu.find_set(u3) as usize;
    let s = &dsu.size;

    if !(pool.is_empty() && dsu.sets.len() == 3
        && m == s[v1] * s[v2] + s[v2] * s[v3] + s[v3] * s[v1])
    { answer_impos!(out); }

    let v: HashMap<u32, u8> = dsu.sets.iter().cloned().zip(1..).collect();
    dsu.parent[1..].iter().for_each(|s| write!(out, "{} ", v[s]).unwrap());
}

#[cfg(test)]
mod tests {
    use super::*;
    use helpers::t;

    #[test]
    fn example1() {
        let mut out = String::new();
        solve(Scanner::new("6 11
1 2
1 3
1 4
1 5
1 6
2 4
2 5
2 6
3 4
3 5
3 6
".as_bytes()), unsafe { out.as_mut_vec() });
        // "1 2 2 3 3 3 "
        let mut r: Vec<i32> = out.split_ascii_whitespace()
            .map(|s| s.parse().unwrap())
            .collect();
        println!("Expected pattern 'a b b c c c '\n found '{}'", out);
        assert_ne!(r[0], r[1]);
        assert_eq!(r[1], r[2]);
        assert_ne!(r[2], r[3]);
        assert_eq!(r[3], r[4]);
        assert_eq!(r[4], r[5]);
        assert_ne!(r[0], r[3]);
        r.sort();
        r.dedup_by_key(|&mut x| x);
        assert_eq!(r, [1, 2, 3]);
    }

    #[test]
    fn example2() {
        t!("4 6
1 2
1 3
1 4
2 3
2 4
3 4
", "-1\n");
    }

    #[test]
    fn t() {
        t!("3 0\n", "-1\n");
        t!("3 2\n1 2\n1 3\n", "-1\n");

        let mut out = String::new();
        solve(Scanner::new("3 3\n1 2\n1 3\n2 3".as_bytes()), unsafe { out.as_mut_vec() });
        let mut r = out.split_ascii_whitespace().collect::<Vec<_>>();
        r.sort();
        assert_eq!(r, ["1", "2", "3"]);
    }

    #[test]
    fn large_input() {
        use std::fmt::Write;
        // release build
        //            defHasher adjHash8b  DsuHash8b rewrite
        // 100000 199997 215 s  126 s      122 s     90 ms
        // 20000 39997   8.5 s  4.9 s      4.9 s
        // 10000 19997   2.2 s  1.2 s      1.2 s
        let mut inp = "100000 199997\n1 2\n".to_owned();
        for i in 3..=100000 {
            inp.write_fmt(format_args!("1 {0}\n2 {0}\n", i)).unwrap();
        }
        let mut out = String::new();
        solve(Scanner::new(inp.as_bytes()), unsafe { out.as_mut_vec() });
        assert_ne!(out, "-1\n");
//        assert!(false);
    }
}


mod io {
    use std::io::{self, BufRead};
    use std::str::{self, FromStr, SplitAsciiWhitespace};

    pub type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;

    pub struct Scanner<'a, R> {
        reader: R,
        buf_str: Vec<u8>,
        buf_iter: SplitAsciiWhitespace<'a>,
    }

    impl<R: BufRead> Scanner<'_, R> {
        pub fn new(reader: R) -> Self {
            Self {
                reader,
                buf_str: Vec::new(),
                buf_iter: "".split_ascii_whitespace(),
            }
        }

        pub fn try_token<T>(&mut self) -> Result<T>
            where T: FromStr,
                  <T as FromStr>::Err: 'static + std::error::Error
        {
            loop {
                if let Some(token) = self.buf_iter.next() {
                    return Ok(token.parse()?);
                }
                self.buf_str.clear();
                let bytes_read = self.reader
                    .read_until(b'\n', &mut self.buf_str)?;
                if bytes_read == 0 {
                    return Err(Box::new(io::Error::from(io::ErrorKind::UnexpectedEof)));
                }
                self.buf_iter = unsafe {
                    let slice = str::from_utf8_unchecked(&self.buf_str);
                    ::std::mem::transmute(slice.split_ascii_whitespace())
                }
            }
        }

        pub fn token<T>(&mut self) -> T
            where T: FromStr,
                  <T as FromStr>::Err: 'static + std::error::Error
        {
            self.try_token().unwrap()
        }
    }
}

use std::io::{stdin, stdout, BufRead, Write};
use io::Scanner;

fn main() {
    let stdin = stdin();
    solve(Scanner::new(stdin.lock()), stdout().lock());
}

use std::collections::{HashSet, HashMap, hash_map::DefaultHasher};
use std::hash::{Hasher, Hash};
use std::iter::repeat_with;

fn solve<R: BufRead, W: Write>(mut scan: Scanner<R>, mut out: W) {
    let n: usize = scan.token();
    let m: u32 = scan.token();
    let mut adjacent: Vec<HashSet<u32>> =
        repeat_with(|| HashSet::default())
            .take(n + 1).collect();
    for _ in 0..m {
        let a: u32 = scan.token();
        let b: u32 = scan.token();
        adjacent[a as usize].insert(b);
        adjacent[b as usize].insert(a);
    }

    let mut sets: HashMap<u64, (u32, u32)> = HashMap::new();
    let mut set: Vec<u32> = vec![0; n + 1];
    let mut s_count: u32 = 0;
    for (u, adj) in adjacent.iter().enumerate().skip(1) {
        let h = adj.iter()
            .fold(0u64, |acc, adj_u| {
                let mut hasher = DefaultHasher::new();
                adj_u.hash(&mut hasher);
                acc ^ hasher.finish()
            });
        let s = sets.entry(h)
            .or_insert_with(|| {
                s_count += 1;
                (s_count, 0)
            });
        s.1 += 1;
        set[u] = s.0;
        if s_count > 3 { break; }
    }

    let v: Vec<u32> = sets.values().map(|&(_, size)| size).collect();
    if s_count == 3 && m == v[0] * v[1] + v[1] * v[2] + v[2] * v[0] {
        set.into_iter()
            .skip(1)
            .for_each(|s| write!(out, "{} ", s).unwrap());
    } else {
        writeln!(out, "-1").unwrap();
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use helpers::t;

    #[test]
    fn example1() {
        let mut out = String::new();
        solve(Scanner::new("6 11
1 2
1 3
1 4
1 5
1 6
2 4
2 5
2 6
3 4
3 5
3 6
".as_bytes()), unsafe { out.as_mut_vec() });
        // "1 2 2 3 3 3 "
        let mut r: Vec<i32> = out.split_ascii_whitespace()
            .map(|s| s.parse().unwrap())
            .collect();
        println!("Expected pattern 'a b b c c c '\n found '{}'", out);
        assert_ne!(r[0], r[1]);
        assert_eq!(r[1], r[2]);
        assert_ne!(r[2], r[3]);
        assert_eq!(r[3], r[4]);
        assert_eq!(r[4], r[5]);
        assert_ne!(r[0], r[3]);
        r.sort();
        r.dedup_by_key(|&mut x| x);
        assert_eq!(r, [1, 2, 3]);
    }

    #[test]
    fn example2() {
        t!("4 6
1 2
1 3
1 4
2 3
2 4
3 4
", "-1\n");
    }

    #[test]
    fn t() {
        t!("3 0\n", "-1\n");
        t!("3 2\n1 2\n1 3\n", "-1\n");

        let mut out = String::new();
        solve(Scanner::new("3 3\n1 2\n1 3\n2 3".as_bytes()), unsafe { out.as_mut_vec() });
        let mut r = out.split_ascii_whitespace().collect::<Vec<_>>();
        r.sort();
        assert_eq!(r, ["1", "2", "3"]);
    }

    #[test]
    fn large_input() {
        use std::fmt::Write;
        // release build
        //            defHasher adjHash8b  DsuHash8b rewrite
        // 100000 199997 215 s  126 s      122 s     90 ms
        // 20000 39997   8.5 s  4.9 s      4.9 s
        // 10000 19997   2.2 s  1.2 s      1.2 s
        let mut inp = "100000 199997\n1 2\n".to_owned();
        for i in 3..=100000 {
            inp.write_fmt(format_args!("1 {0}\n2 {0}\n", i)).unwrap();
        }
        let mut out = String::new();
        solve(Scanner::new(inp.as_bytes()), unsafe { out.as_mut_vec() });
        assert_ne!(out, "-1\n");
//        assert!(false);
    }
}


mod io {
    use std::io::{self, BufRead};
    use std::str::{self, FromStr, SplitAsciiWhitespace};
    use super::Result;

    pub struct Scanner<'a, R> {
        reader: R,
        buf_str: Vec<u8>,
        buf_iter: SplitAsciiWhitespace<'a>,
    }

    impl<R: BufRead> Scanner<'_, R> {
        pub fn new(reader: R) -> Self {
            Self {
                reader,
                buf_str: Vec::new(),
                buf_iter: "".split_ascii_whitespace(),
            }
        }

        pub fn token<T>(&mut self) -> Result<T>
            where T: FromStr,
                  <T as FromStr>::Err: 'static + std::error::Error
        {
            loop {
                if let Some(token) = self.buf_iter.next() {
                    return Ok(token.parse()?);
                }
                self.buf_str.clear();
                let bytes_read = self.reader
                    .read_until(b'\n', &mut self.buf_str)?;
                if bytes_read == 0 {
                    return Err(Box::new(io::Error::from(io::ErrorKind::UnexpectedEof)));
                }
                self.buf_iter = unsafe {
                    let slice = str::from_utf8_unchecked(&self.buf_str);
                    ::std::mem::transmute(slice.split_ascii_whitespace())
                }
            }
        }
    }
}

use std::io::{stdin, stdout, BufRead, Write};
use io::Scanner;

pub type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;

fn main() -> Result<()> {
    let stdin = stdin();
    solve(Scanner::new(stdin.lock()), stdout().lock())
}

use std::collections::HashMap;
use std::collections::hash_map::{DefaultHasher, Entry::{Occupied, Vacant}};
use std::hash::{Hasher, Hash};

struct Set {
    number: u8,
    size: u32,
}

fn solve<R: BufRead, W: Write>(mut scan: Scanner<R>, mut out: W) -> Result<()> {
    let n: usize = scan.token()?;
    let m: u32 = scan.token()?;
    let mut seth: Vec<u32> = vec![0; n + 1];
    for _ in 0..m {
        let a: u32 = scan.token()?;
        let b: u32 = scan.token()?;
        seth[a as usize] ^= hash(b);
        seth[b as usize] ^= hash(a);
    }

    let mut s_count: u8 = 0;
    let mut sets: HashMap<u32, Set> = HashMap::new();
    for &h in seth.iter().skip(1) {
        let s = match sets.entry(h) {
            Occupied(entry) => entry.into_mut(),
            Vacant(entry) => {
                s_count += 1;
                if s_count > 3 { break; }
                entry.insert(Set { number: s_count, size: 0 })
            }
        };
        s.size += 1;
    }

    let v: Vec<u32> = sets.values().map(|s| s.size).collect();
    if s_count == 3 && m == v[0] * v[1] + v[1] * v[2] + v[2] * v[0] {
        for h in seth.into_iter().skip(1) {
            write!(out, "{} ", sets[&h].number)?;
        }
    } else {
        writeln!(out, "-1")?;
    }
    Ok(())
}

#[inline]
fn hash(x: u32) -> u32 {
    let mut h = DefaultHasher::new();
    x.hash(&mut h);
    (h.finish() >> 31) as u32
}

#[cfg(test)]
mod tests {
    use super::*;

    macro_rules! t {
    ( $inp:expr, $expected:expr ) => {
        {
            let mut out = String::new();
            solve(Scanner::new($inp.as_bytes()), unsafe { out.as_mut_vec() }).unwrap();
            assert_eq!(out, $expected);
        }
    };
}

    #[test]
    fn example1() {
        let mut out = String::new();
        solve(Scanner::new("6 11
1 2
1 3
1 4
1 5
1 6
2 4
2 5
2 6
3 4
3 5
3 6
".as_bytes()), unsafe { out.as_mut_vec() }).unwrap();
        // "1 2 2 3 3 3 "
        let mut r: Vec<i32> = out.split_ascii_whitespace()
            .map(|s| s.parse().unwrap())
            .collect();
        println!("Expected pattern 'a b b c c c '\n found '{}'", out);
        assert_ne!(r[0], r[1]);
        assert_eq!(r[1], r[2]);
        assert_ne!(r[2], r[3]);
        assert_eq!(r[3], r[4]);
        assert_eq!(r[4], r[5]);
        assert_ne!(r[0], r[3]);
        r.sort();
        r.dedup_by_key(|&mut x| x);
        assert_eq!(r, [1, 2, 3]);
    }

    #[test]
    fn example2() {
        t!("4 6
1 2
1 3
1 4
2 3
2 4
3 4
", "-1\n");
    }

    #[test]
    fn t() {
        t!("3 0\n", "-1\n");
        t!("3 2\n1 2\n1 3\n", "-1\n");

        let mut out = String::new();
        solve(Scanner::new("3 3\n1 2\n1 3\n2 3".as_bytes()), unsafe { out.as_mut_vec() }).unwrap();
        let mut r = out.split_ascii_whitespace().collect::<Vec<_>>();
        r.sort();
        assert_eq!(r, ["1", "2", "3"]);
    }

    #[test]
    fn large_input() {
        use std::fmt::Write;
        // release build
        //            defHasher adjHash8b  DsuHash8b rewrite optimise
        // 100000 199997 215 s  126 s      122 s     90 ms   50 ms
        // 20000 39997   8.5 s  4.9 s      4.9 s
        // 10000 19997   2.2 s  1.2 s      1.2 s
        let mut inp = "100000 199997\n1 2\n".to_owned();
        for i in 3..=100000 {
            inp.write_fmt(format_args!("1 {0}\n2 {0}\n", i)).unwrap();
        }
        let mut out = String::new();
        solve(Scanner::new(inp.as_bytes()), unsafe { out.as_mut_vec() }).unwrap();
        assert_ne!(out, "-1\n");
//        assert!(false);
    }

    #[test]
    fn collision() {
        use std::collections::hash_map::Entry::{Occupied, Vacant};
        const N: u32 = 100_000;
        let mut hset: HashMap<u32, u32> = HashMap::with_capacity(N as usize);
        let mut collisions: Vec<(u32, u32)> = Vec::new();
        for i in 0..=N {
            match hset.entry(hash(i)) {
//            match hset.entry((h.finish() >> 31) as u32) {
//            match hset.entry((h.finish()) as u32) {
//            match hset.entry((h.finish() ^ (h.finish() >> 31)) as u32) {
                Vacant(e) => { e.insert(i); }
                Occupied(e) =>
                    collisions.push((*e.get(), i))
            };
        }
        collisions.iter().for_each(|x| println!("{:?}", x));
        assert_eq!(collisions.len(), 0);
    }
}


mod io {
    use std::io::{self, BufRead};
    use std::str::{self, FromStr, SplitAsciiWhitespace};

    pub type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;

    pub struct Scanner<'a, R> {
        reader: R,
        buf_str: Vec<u8>,
        buf_iter: SplitAsciiWhitespace<'a>,
    }

    impl<R: BufRead> Scanner<'_, R> {
        pub fn new(reader: R) -> Self {
            Self {
                reader,
                buf_str: Vec::new(),
                buf_iter: "".split_ascii_whitespace(),
            }
        }

        pub fn try_token<T>(&mut self) -> Result<T>
            where T: FromStr,
                  <T as FromStr>::Err: 'static + std::error::Error
        {
            loop {
                if let Some(token) = self.buf_iter.next() {
                    return Ok(token.parse()?);
                }
                self.buf_str.clear();
                let bytes_read = self.reader
                    .read_until(b'\n', &mut self.buf_str)?;
                if bytes_read == 0 {
                    return Err(Box::new(io::Error::from(io::ErrorKind::UnexpectedEof)));
                }
                self.buf_iter = unsafe {
                    let slice = str::from_utf8_unchecked(&self.buf_str);
                    ::std::mem::transmute(slice.split_ascii_whitespace())
                }
            }
        }

        pub fn token<T>(&mut self) -> T
            where T: FromStr,
                  <T as FromStr>::Err: 'static + std::error::Error
        {
            self.try_token().unwrap()
        }
    }
}

mod data_struct {
    use std::collections::HashSet;

    pub struct Dsu {
        pub parent: Vec<i32>,
        rank: Vec<i32>,
        pub sets: HashSet<i32>,
    }

    impl Dsu {
        pub fn new(size: usize) -> Dsu {
            Dsu {
                parent: vec![-1; size + 1],
                rank: vec![-1; size + 1],
                sets: HashSet::new(),
            }
        }

        pub fn make_set(&mut self, v: i32) {
            self.parent[v as usize] = v;
            self.rank[v as usize] = 0;
            self.sets.insert(v);
        }

        pub fn find_set(&mut self, v: i32) -> i32 {
            if v == self.parent[v as usize] {
                v
            } else {
                self.parent[v as usize] = self.find_set(self.parent[v as usize]);
                self.parent[v as usize]
            }
        }

        pub fn union_sets(&mut self, mut a: i32, mut b: i32) {
            a = self.find_set(a);
            b = self.find_set(b);
            if a != b {
                if self.rank[a as usize] < self.rank[b as usize] {
                    std::mem::swap(&mut a, &mut b);
                }
                self.parent[b as usize] = a;
                if self.rank[a as usize] == self.rank[b as usize] {
                    self.rank[a as usize] += 1;
                }
                self.sets.remove(&b);
            }
        }
    }
}

use std::io::{stdin, stdout, BufRead, Write};
use io::Scanner;

fn main() {
    let stdin = stdin();
    solve(Scanner::new(stdin.lock()), stdout().lock());
}

use std::iter::{repeat_with, FromIterator};
use std::collections::{HashSet, HashMap};
use data_struct::Dsu;

fn solve<R: BufRead, W: Write>(mut scan: Scanner<R>, mut output: W) {
    let n: usize = scan.token();
    let m: usize = scan.token();
    let mut adjacent: Vec<HashSet<i32>> = repeat_with(|| HashSet::new())
        .take(n + 1).collect();
    let mut dsu = Dsu::new(n);
    for _ in 0..m {
        let a: i32 = scan.token();
        let b: i32 = scan.token();
        adjacent[a as usize].insert(b);
        adjacent[b as usize].insert(a);
    }
    let n = n as i32;
    for a in 1..=n { dsu.make_set(a) }
    for (adj, a) in adjacent[1..].iter().zip(1..) {
        for b in 1..=n {
            if !adj.contains(&b) {
                dsu.union_sets(a, b);
            }
        }
    }
    if dsu.sets.len() == 3 {
        let set: HashMap<i32, i32> = HashMap::from_iter(
            dsu.sets.iter().cloned().zip(1..));
        dsu.parent[1..].iter()
            .for_each(|s| write!(output, "{} ", set[s]).unwrap());
    } else {
        writeln!(output, "-1").unwrap();
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use helpers::t;

    #[test]
    fn example1() {
        let mut out = String::new();
        solve(Scanner::new("6 11
1 2
1 3
1 4
1 5
1 6
2 4
2 5
2 6
3 4
3 5
3 6
".as_bytes()), unsafe { out.as_mut_vec() });
        // "1 2 2 3 3 3 "
        let mut r: Vec<i32> = out.split_ascii_whitespace()
            .map(|s| s.parse().unwrap())
            .collect();
        println!("Expected pattern 'a b b c c c '\n found '{}'", out);
        assert_ne!(r[0], r[1]);
        assert_eq!(r[1], r[2]);
        assert_ne!(r[2], r[3]);
        assert_eq!(r[3], r[4]);
        assert_eq!(r[4], r[5]);
        assert_ne!(r[0], r[3]);
        r.sort();
        r.dedup_by_key(|&mut x| x);
        assert_eq!(r, [1, 2, 3]);
    }

    #[test]
    fn example2() {
        t!("4 6
1 2
1 3
1 4
2 3
2 4
3 4
", "-1\n");
    }

    #[test]
    fn t() {
        t!("0 0\n", "-1\n");
        t!("3 2\n1 2\n1 3\n", "-1\n");

        let mut out = String::new();
        solve(Scanner::new("3 3\n1 2\n1 3\n2 3".as_bytes()), unsafe { out.as_mut_vec() });
        let mut r = out.split_ascii_whitespace().collect::<Vec<_>>();
        r.sort();
        assert_eq!(r, ["1", "2", "3"]);
    }
}