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::{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<Vec<u32>> =
        repeat_with(|| Vec::new())
            .take(n + 1).collect();
    for _ in 0..m {
        let a: u32 = scan.token();
        let b: u32 = scan.token();
        adjacent[a as usize].push(b);
        adjacent[b as usize].push(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};

    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::BTreeSet;

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

    impl Dsu {
        pub fn new(size: usize) -> Dsu {
            Dsu {
                parent: vec![-1; size + 1],
                rank: vec![-1; size + 1],
                sets: BTreeSet::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");
        t!("3 3\n1 2\n1 3\n2 3", "1 2 3 ");
    }
}


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())
}

struct Set {
    hash: u32,
    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: Vec<Set> = Vec::new();
    for h in seth.iter_mut().skip(1) {
        let s = match sets.binary_search_by_key(h, |s| s.hash) {
            Ok(idx) => &mut sets[idx],
            Err(idx) => {
                s_count += 1;
                if s_count > 3 { break; }
                sets.insert(idx, Set { hash: *h, number: s_count, size: 0 });
                &mut sets[idx]
            }
        };
        s.size += 1;
        *h = s.number as u32;
    }

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

#[inline]
fn hash(x: u32) -> u32 {
    x ^ std::u32::MAX
}

#[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 opt2
        // 100000 199997 215 s  126 s      122 s     90 ms   50 ms    min 24
        // 20000 39997   8.5 s  4.9 s      4.9 s                      avg 35
        // 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::HashMap;
        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(i ^ std::u32::MAX) {
//            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 a..=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"]);
    }
}


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 {
    pub struct Dsu {
        parent: Vec<i32>,
        rank: Vec<i32>,
        sets: usize,
    }

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

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

        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 -= 1;
            }
        }

        pub fn sets(&self) -> usize {
            self.sets
        }
    }
}

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;
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() == 3 {
        let mut last_used = 0;
        let mut set = HashMap::<i32, i32>::with_capacity(3);
        for a in 1..=n {
            let snum = set.entry(dsu.find_set(a)).or_insert_with(|| {
                last_used += 1;
                last_used
            });
            write!(output, "{} ", snum).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");
        t!("3 3\n1 2\n1 3\n2 3", "1 2 3 ");
    }
}


//https://qiita.com/tanakh/items/0ba42c7ca36cd29d0ac8 より
macro_rules! input {
    (source = $s:expr, $($r:tt)*) => {
        let mut iter = $s.split_whitespace();
        input_inner!{iter, $($r)*}
    };
    ($($r:tt)*) => {
        let s = {
            use std::io::Read;
            let mut s = String::new();
            std::io::stdin().read_to_string(&mut s).unwrap();
            s
        };
        let mut iter = s.split_whitespace();
        input_inner!{iter, $($r)*}
    };
}

macro_rules! input_inner {
    ($iter:expr) => {};
    ($iter:expr, ) => {};
    ($iter:expr, $var:ident : $t:tt $($r:tt)*) => {
        let $var = read_value!($iter, $t);
        input_inner!{$iter $($r)*}
    };
}

macro_rules! read_value {
    ($iter:expr, ( $($t:tt),* )) => {
        ( $(read_value!($iter, $t)),* )
    };
    ($iter:expr, [ $t:tt ; $len:expr ]) => {
        (0..$len).map(|_| read_value!($iter, $t)).collect::<Vec<_>>()
    };
    ($iter:expr, chars) => {
        read_value!($iter, String).chars().collect::<Vec<char>>()
    };
    ($iter:expr, usize1) => {
        read_value!($iter, usize) - 1
    };
    ($iter:expr, $t:ty) => {
        $iter.next().unwrap().parse::<$t>().expect("Parse error")
    };
}

// ここまで

use std::cmp::{max, min};

fn run() {
    input! {
        n: usize,
        m: usize,
        e: [(usize1, usize1); m],
    }
    let mut e = e;
    for e in &mut e {
        *e = (min(e.0, e.1), max(e.0, e.1));
    }
    e.sort();
    let mut g = vec![vec![]; n];
    for &(a, b) in &e {
        g[a].push(b);
        g[b].push(a);
    }
    let mut id = vec![1; n];
    for &u in &g[0] {
        id[u] = 0;
    }
    let cnt = id.iter().filter(|p| **p == 1).count();
    for v in 0..n {
        if id[v] == 0 {
            continue;
        }
        if g[v].len() != n - cnt {
            println!("-1");
            return;
        }
        for &u in &g[v] {
            if id[u] == 1 {
                println!("-1");
                return;
            }
        }
    }
    let mut r = 0;
    while r < n && id[r] == 1 {
        r += 1;
    }
    if r >= n {
        println!("-1");
        return;
    }
    id[r] = 2;
    let mut stack = vec![];
    stack.push(r);
    while let Some(v) = stack.pop() {
        let x = id[v];
        let y = 2 + 3 - x;
        for &u in &g[v] {
            if id[u] == x {
                println!("-1");
                return;
            }
            if id[u] == 0 {
                id[u] = y;
                stack.push(u);
            }
        }
    }
    if !id.contains(&3) {
        println!("-1");
        return;
    }
    let mut ans = String::new();
    for i in 0..n {
        ans.push_str(&format!("{} ", id[i]));
    }
    ans.pop();
    println!("{}", ans);
}

fn main() {
    run();
}


//https://qiita.com/tanakh/items/0ba42c7ca36cd29d0ac8 より
macro_rules! input {
    (source = $s:expr, $($r:tt)*) => {
        let mut iter = $s.split_whitespace();
        input_inner!{iter, $($r)*}
    };
    ($($r:tt)*) => {
        let s = {
            use std::io::Read;
            let mut s = String::new();
            std::io::stdin().read_to_string(&mut s).unwrap();
            s
        };
        let mut iter = s.split_whitespace();
        input_inner!{iter, $($r)*}
    };
}

macro_rules! input_inner {
    ($iter:expr) => {};
    ($iter:expr, ) => {};
    ($iter:expr, $var:ident : $t:tt $($r:tt)*) => {
        let $var = read_value!($iter, $t);
        input_inner!{$iter $($r)*}
    };
}

macro_rules! read_value {
    ($iter:expr, ( $($t:tt),* )) => {
        ( $(read_value!($iter, $t)),* )
    };
    ($iter:expr, [ $t:tt ; $len:expr ]) => {
        (0..$len).map(|_| read_value!($iter, $t)).collect::<Vec<_>>()
    };
    ($iter:expr, chars) => {
        read_value!($iter, String).chars().collect::<Vec<char>>()
    };
    ($iter:expr, usize1) => {
        read_value!($iter, usize) - 1
    };
    ($iter:expr, $t:ty) => {
        $iter.next().unwrap().parse::<$t>().expect("Parse error")
    };
}

// ここまで

use std::cmp::{max, min};

fn run() {
    input! {
        n: usize,
        m: usize,
        e: [(usize1, usize1); m],
    }
    let mut e = e;
    for e in &mut e {
        *e = (min(e.0, e.1), max(e.0, e.1));
    }
    e.sort();
    let mut g = vec![vec![]; n];
    for &(a, b) in &e {
        g[a].push(b);
        g[b].push(a);
    }
    let mut id = vec![1; n];
    for &u in &g[0] {
        id[u] = 0;
    }
    let cnt = id.iter().filter(|p| **p == 1).count();
    for v in 0..n {
        if id[v] == 0 {
            continue;
        }
        if g[v].len() != n - cnt {
            println!("-1");
            return;
        }
        for &u in &g[v] {
            if id[u] == 1 {
                println!("-1");
                return;
            }
        }
    }
    let mut r = 0;
    while r < n && id[r] == 1 {
        r += 1;
    }
    if r >= n {
        println!("-1");
        return;
    }
    id[r] = 2;
    let mut stack = vec![];
    stack.push(r);
    while let Some(v) = stack.pop() {
        let x = id[v];
        let y = 2 + 3 - x;
        for &u in &g[v] {
            if id[u] == x {
                println!("-1");
                return;
            }
            if id[u] == 0 {
                id[u] = y;
                stack.push(u);
            }
        }
    }
    if !id.contains(&3) {
        println!("-1");
        return;
    }
    let mut a = vec![vec![]; 4];
    for v in 0..n {
        a[id[v]].push(v);
    }
    for &x in &a[1] {
        for &y in &a[2] {
            let z = (min(x, y), max(x, y));
            if let Err(_) = e.binary_search(&z) {
                println!("-1");
                return;
            }
        }
    }
    for &x in &a[2] {
        for &y in &a[3] {
            let z = (min(x, y), max(x, y));
            if let Err(_) = e.binary_search(&z) {
                println!("-1");
                return;
            }
        }
    }
    for &x in &a[3] {
        for &y in &a[1] {
            let z = (min(x, y), max(x, y));
            if let Err(_) = e.binary_search(&z) {
                println!("-1");
                return;
            }
        }
    }
    let mut ans = String::new();
    for i in 0..n {
        ans.push_str(&format!("{} ", id[i]));
    }
    ans.pop();
    println!("{}", ans);
}

fn main() {
    run();
}


#![allow(unused_imports, unused_macros, dead_code)]

macro_rules! min {
    (.. $x:expr) => {{
        let mut it = $x.iter();
        it.next().map(|z| it.fold(z, |x, y| min!(x, y)))
    }};
    ($x:expr) => ($x);
    ($x:expr, $($ys:expr),*) => {{
        let t = min!($($ys),*);
        if $x < t { $x } else { t }
    }}
}
macro_rules! max {
    (.. $x:expr) => {{
        let mut it = $x.iter();
        it.next().map(|z| it.fold(z, |x, y| max!(x, y)))
    }};
    ($x:expr) => ($x);
    ($x:expr, $($ys:expr),*) => {{
        let t = max!($($ys),*);
        if $x > t { $x } else { t }
    }}
}

macro_rules! ewriteln {
    ($($args:expr),*) => { let _ = writeln!(&mut std::io::stderr(), $($args),*); };
}
macro_rules! trace {
    ($x:expr) => { ewriteln!(">>> {} = {:?}", stringify!($x), $x) };
    ($($xs:expr),*) => { trace!(($($xs),*)) }
}
macro_rules! put {
    (.. $x:expr) => {{
        let mut it = $x.iter();
        if let Some(x) = it.next() { print!("{}", x); }
        for x in it { print!(" {}", x); }
        println!("");
    }};
    ($x:expr) => { println!("{}", $x) };
    ($x:expr, $($xs:expr),*) => { print!("{} ", $x); put!($($xs),*) }
}

const M: i64 = 1_000_000_007;
use std::collections::HashSet;

fn main() {
    let mut sc = Scanner::new();
    let n: usize = sc.cin();
    let m: usize = sc.cin();
    let mut g = vec![vec![]; n];
    let mut es = HashSet::new();
    for _ in 0..m {
        let u: usize = sc.cin();
        let v: usize = sc.cin();
        g[u - 1].push(v - 1);
        g[v - 1].push(u - 1);
        es.insert((u - 1, v - 1));
    }

    let v0 = 0;
    let mut v1 = 0;
    let mut v2 = 0;

    { // 0 -- u -- v -- 0
        let s: HashSet<usize> = g[0].iter().map(|&x| x).collect();
        for &(u, v) in es.iter() {
            if s.contains(&u) && s.contains(&v) {
                v1 = u;
                v2 = v;
                break;
            }
        }
    }
    if v1 == 0 || v2 == 0 {
        put!(-1);
        return;
    }
    let mut s0 = HashSet::new(); s0.insert(0);
    let mut s1 = HashSet::new(); s1.insert(v1);
    let mut s2 = HashSet::new(); s2.insert(v2);

    for u in 1..n {
        if s0.contains(&u) { continue }
        if s1.contains(&u) { continue }
        if s2.contains(&u) { continue }
        let mut vs0 = false;
        let mut vs1 = false;
        let mut vs2 = false;
        for &v in g[u].iter() {
            if s0.contains(&v) { vs0 = true }
            if s1.contains(&v) { vs1 = true }
            if s2.contains(&v) { vs2 = true }
        }
        if vs0 && vs1 {
            s2.insert(u);
        } else if vs1 && vs2 {
            s0.insert(u);
        } else if vs2 && vs0 {
            s1.insert(u);
        } else {
            put!(-1);
            return;
        }
    }

    for &(u, v) in es.iter() {
        let mut ok = true;
        if s0.contains(&u) && s0.contains(&v) { ok = false; }
        if s1.contains(&u) && s1.contains(&v) { ok = false; }
        if s2.contains(&u) && s2.contains(&v) { ok = false; }
        if !ok {
            put!(-1);
            return;
        }
    }

    let ans: Vec<usize> = (0..n).map(|i| {
        if s0.contains(&i) {
            1
        } else if s1.contains(&i) {
            2
        } else {
            3
        }
    }).collect();

    put!(..ans);

}

use std::io::{self, Write};
use std::str::FromStr;
use std::collections::VecDeque;

struct Scanner { stdin: io::Stdin, buffer: VecDeque<String>, }
impl Scanner {
    fn new() -> Self { Scanner { stdin: io::stdin(), buffer: VecDeque::new() } }
    fn cin<T: FromStr>(&mut self) -> T {
        while self.buffer.len() == 0 {
            let mut line = String::new();
            let _ = self.stdin.read_line(&mut line);
            for w in line.split_whitespace() {
                self.buffer.push_back(String::from(w));
            }
        }
        self.buffer.pop_front().unwrap().parse::<T>().ok().unwrap()
    }
}


#![allow(unused_imports, unused_macros, dead_code)]

macro_rules! min {
    (.. $x:expr) => {{
        let mut it = $x.iter();
        it.next().map(|z| it.fold(z, |x, y| min!(x, y)))
    }};
    ($x:expr) => ($x);
    ($x:expr, $($ys:expr),*) => {{
        let t = min!($($ys),*);
        if $x < t { $x } else { t }
    }}
}
macro_rules! max {
    (.. $x:expr) => {{
        let mut it = $x.iter();
        it.next().map(|z| it.fold(z, |x, y| max!(x, y)))
    }};
    ($x:expr) => ($x);
    ($x:expr, $($ys:expr),*) => {{
        let t = max!($($ys),*);
        if $x > t { $x } else { t }
    }}
}

macro_rules! ewriteln {
    ($($args:expr),*) => { let _ = writeln!(&mut std::io::stderr(), $($args),*); };
}
macro_rules! trace {
    ($x:expr) => { ewriteln!(">>> {} = {:?}", stringify!($x), $x) };
    ($($xs:expr),*) => { trace!(($($xs),*)) }
}
macro_rules! put {
    (.. $x:expr) => {{
        let mut it = $x.iter();
        if let Some(x) = it.next() { print!("{}", x); }
        for x in it { print!(" {}", x); }
        println!("");
    }};
    ($x:expr) => { println!("{}", $x) };
    ($x:expr, $($xs:expr),*) => { print!("{} ", $x); put!($($xs),*) }
}

const M: i64 = 1_000_000_007;
use std::collections::HashSet;

fn main() {
    let mut sc = Scanner::new();
    let n: usize = sc.cin();
    let m: usize = sc.cin();
    let mut g = vec![vec![]; n];
    let mut es = HashSet::new();
    for _ in 0..m {
        let u: usize = sc.cin();
        let v: usize = sc.cin();
        g[u - 1].push(v - 1);
        g[v - 1].push(u - 1);
        es.insert((u - 1, v - 1));
    }

    let mut v1 = 0;
    let mut v2 = 0;

    { // 0 -- u -- v -- 0
        let s: HashSet<usize> = g[0].iter().map(|&x| x).collect();
        for &(u, v) in es.iter() {
            if u != 0 && v != 0 && s.contains(&u) && s.contains(&v) {
                v1 = u;
                v2 = v;
                break;
            }
        }
    }
    if v1 == 0 || v2 == 0 {
        put!(-1);
        return;
    }
    let mut s0 = HashSet::new(); s0.insert(0);
    let mut s1 = HashSet::new(); s1.insert(v1);
    let mut s2 = HashSet::new(); s2.insert(v2);

    for u in 1..n {
        if s0.contains(&u) { continue }
        if s1.contains(&u) { continue }
        if s2.contains(&u) { continue }
        let mut vs0 = false;
        let mut vs1 = false;
        let mut vs2 = false;
        for &v in g[u].iter() {
            if s0.contains(&v) { vs0 = true }
            if s1.contains(&v) { vs1 = true }
            if s2.contains(&v) { vs2 = true }
        }
        if vs0 && vs1 && vs2 {
            put!(-1);
            return;
        }
        if vs0 && vs1 {
            s2.insert(u);
        } else if vs1 && vs2 {
            s0.insert(u);
        } else if vs2 && vs0 {
            s1.insert(u);
        } else {
            put!(-1);
            return;
        }
    }

    let mut c01 = 0;
    let mut c12 = 0;
    let mut c20 = 0;

    for &(u, v) in es.iter() {
        let mut ok = true;
        if s0.contains(&u) && s0.contains(&v) { ok = false; }
        if s1.contains(&u) && s1.contains(&v) { ok = false; }
        if s2.contains(&u) && s2.contains(&v) { ok = false; }
        if !ok {
            put!(-1);
            return;
        }
        if s0.contains(&u) && s1.contains(&v) {
            c01 += 1;
        }
        else if s1.contains(&u) && s0.contains(&v) {
            c01 += 1;
        }
        else if s1.contains(&u) && s2.contains(&v) {
            c12 += 1;
        }
        else if s2.contains(&u) && s1.contains(&v) {
            c12 += 1;
        }
        else if s2.contains(&u) && s0.contains(&v) {
            c20 += 1;
        }
        else if s0.contains(&u) && s2.contains(&v) {
            c20 += 1;
        }
    }

    if s0.len() * s1.len() == c01 && s1.len() * s2.len() == c12 && s2.len() * s0.len() == c20 {
        ;
    } else {
        put!(-1);
        return;
    }

    let ans: Vec<usize> = (0..n).map(|i| {
        if s0.contains(&i) {
            1
        } else if s1.contains(&i) {
            2
        } else {
            3
        }
    }).collect();

    put!(..ans);

}

use std::io::{self, Write};
use std::str::FromStr;
use std::collections::VecDeque;

struct Scanner { stdin: io::Stdin, buffer: VecDeque<String>, }
impl Scanner {
    fn new() -> Self { Scanner { stdin: io::stdin(), buffer: VecDeque::new() } }
    fn cin<T: FromStr>(&mut self) -> T {
        while self.buffer.len() == 0 {
            let mut line = String::new();
            let _ = self.stdin.read_line(&mut line);
            for w in line.split_whitespace() {
                self.buffer.push_back(String::from(w));
            }
        }
        self.buffer.pop_front().unwrap().parse::<T>().ok().unwrap()
    }
}


package main

import (
	"bufio"
	"os"
	"runtime"
	"strconv"
)

/* solution */

func solve() {

	n, m := readInt(), readInt()

	e := make([][2]int, m+1)
	color := make([]int, n+1) // color
	color[1] = 0
	r, g, b := n, 0, 0
	firstG := -1
	conn := make([]int, n+1) // connections

	for i := 1; i <= m; i++ {
		u, v := readInt(), readInt()
		conn[u]++
		conn[v]++
		if u == 1 || v == 1 {
			color[u+v-1] = 1
			r--
			g++
			if firstG == -1 {
				firstG = u + v - 1
			}
		}
		e[i][0] = u
		e[i][1] = v
	}

	for i := 1; i <= m; i++ {
		u, v := e[i][0], e[i][1]
		if u == firstG || v == firstG {
			if color[u+v-firstG] == 1 {
				color[u+v-firstG] = 2
				g--
				b++
			}
		}
	}

	ok := true
	for i := 1; i <= n; i++ {
		switch color[i] {
		case 0:
			ok = ok && conn[i] == g+b
		case 1:
			ok = ok && conn[i] == r+b
		case 2:
			ok = ok && conn[i] == r+g
		}
	}

	if r <= 0 || g <= 0 || b <= 0 || !ok {
		printInt(-1)
		return
	}

	for i := 1; i <= m; i++ {
		u, v := e[i][0], e[i][1]
		if color[u] == color[v] {
			printInt(-1)
			return
		}
	}

	for i := 1; i <= n; i++ {
		printInt(color[i] + 1)
	}

}

/* stubs */

func main() {

	var f *os.File
	if runtime.GOROOT() == "C:\\Soft\\Go" {
		f, _ = os.Open("input.txt")
		defer f.Close()
	} else {
		f = os.Stdin
	}

	writer = bufio.NewWriter(os.Stdout)
	defer writer.Flush()

	reader = bufio.NewScanner(f)

	const maxCapacity = 1024 * 1024
	buf := make([]byte, maxCapacity)
	reader.Buffer(buf, maxCapacity)

	reader.Split(bufio.ScanWords)

	solve()

}

var reader *bufio.Scanner
var writer *bufio.Writer

func read() string {
	reader.Scan()
	return reader.Text()
}

func readInt() int {
	reader.Scan()
	ret, _ := strconv.Atoi(reader.Text())
	return ret
}

func print(s string) {
	writer.WriteString(s)
	writer.WriteString("\n")
}

func printInt(i int) {
	print(strconv.Itoa(i))
}

func printInt64(i int64) {
	print(strconv.FormatInt(i, 10))
}

func readInt64() int64 {
	reader.Scan()
	ret, _ := strconv.ParseInt(reader.Text(), 10, 64)
	return ret
}


package main

import (
	"bufio"
	"os"
	"runtime"
	"strconv"
)

/* solution */

func solve() {

	n, m := readInt(), readInt()

	e := make([][2]int, m+1)
	color := make([]int, n+1) // color
	color[1] = 0
	r, g, b := n, 0, 0
	firstG := -1
	conn := make([]int, n+1) // connections

	for i := 1; i <= m; i++ {
		u, v := readInt(), readInt()
		conn[u]++
		conn[v]++
		if u == 1 || v == 1 {
			color[u+v-1] = 1
			r--
			g++
			if firstG == -1 {
				firstG = u + v - 1
			}
		}
		e[i][0] = u
		e[i][1] = v
	}

	for i := 1; i <= m; i++ {
		u, v := e[i][0], e[i][1]
		if color[u] == 1 && color[v] == 1 {
			color[v] = 2
			g--
			b++
		}
	}

	ok := true
	for i := 1; i <= n; i++ {
		switch color[i] {
		case 0:
			ok = ok && conn[i] == g+b
		case 1:
			ok = ok && conn[i] == r+b
		case 2:
			ok = ok && conn[i] == r+g
		}
	}

	if r <= 0 || g <= 0 || b <= 0 || !ok {
		printInt(-1)
		return
	}

	for i := 1; i <= m; i++ {
		u, v := e[i][0], e[i][1]
		if color[u] == color[v] {
			printInt(-1)
			return
		}
	}

	for i := 1; i <= n; i++ {
		printInt(color[i] + 1)
	}

}

/* stubs */

func main() {

	var f *os.File
	if runtime.GOROOT() == "C:\\Soft\\Go" {
		f, _ = os.Open("input.txt")
		defer f.Close()
	} else {
		f = os.Stdin
	}

	writer = bufio.NewWriter(os.Stdout)
	defer writer.Flush()

	reader = bufio.NewScanner(f)

	const maxCapacity = 1024 * 1024
	buf := make([]byte, maxCapacity)
	reader.Buffer(buf, maxCapacity)

	reader.Split(bufio.ScanWords)

	solve()

}

var reader *bufio.Scanner
var writer *bufio.Writer

func read() string {
	reader.Scan()
	return reader.Text()
}

func readInt() int {
	reader.Scan()
	ret, _ := strconv.Atoi(reader.Text())
	return ret
}

func print(s string) {
	writer.WriteString(s)
	writer.WriteString("\n")
}

func printInt(i int) {
	print(strconv.Itoa(i))
}

func printInt64(i int64) {
	print(strconv.FormatInt(i, 10))
}

func readInt64() int64 {
	reader.Scan()
	ret, _ := strconv.ParseInt(reader.Text(), 10, 64)
	return ret
}


package main

import (
	"bufio"
	. "fmt"
	"io"
	"os"
)

// github.com/EndlessCheng/codeforces-go
func Sol1228D(reader io.Reader, writer io.Writer) {
	in := bufio.NewReader(reader)
	out := bufio.NewWriter(writer)
	defer out.Flush()

	var n, m int
	Fscan(in, &n, &m)
	powP := make([]uint64, n+1)
	powP[0] = 1
	const prime uint64 = 1610612741
	for i := 1; i <= n; i++ {
		powP[i] = powP[i-1] * prime
	}
	hash := make([]uint64, n+1)
	for ; m > 0; m-- {
		var x, y int
		Fscan(in, &x, &y)
		hash[x] += powP[y]
		hash[y] += powP[x]
	}

	mp := map[uint64]int{}
	idx := 0
	for i := 1; i <= n; i++ {
		if hash[i] == 0 {
			Fprint(out, -1)
			return
		}
		if mp[hash[i]] == 0 {
			idx++
			mp[hash[i]] = idx
		}
	}
	if idx != 3 {
		Fprint(out, -1)
		return
	}
	for _, h := range hash[1:] {
		Fprint(out, mp[h], " ")
	}
}

func main() {
	Sol1228D(os.Stdin, os.Stdout)
}


    import sys
    input = sys.stdin.readline

    def r1():
        return int(input())
    def rm():
        return map(int, input().split())

    n, m = rm()
    d = {}
    for i in range(m):
        x, y = rm()

        if x not in d:
            d[x] = set()
        d[x].add(y)

        if y not in d:
            d[y] = set()
        d[y].add(x)

    bc = d.get(1, [])
    if len(bc) == 0:
        print(-1)
        exit()

    ac = d.get(next(iter(bc)), [])
    if len(ac) == 0:
        print(-1)
        exit()

    c = bc & ac
    a = ac - c
    b = bc - c

    ab = a | b

    na = len(a)
    nb = len(b)
    nc = len(c)
    if na + nb + nc != n or na == 0 or nb == 0 or nc == 0:
        print(-1)
    else:
        for x in a:
            if d.get(x, []) != bc:
                print(-1)
                exit()
        for x in b:
            if d.get(x, []) != ac:
                print(-1)
                exit()
        for x in c:
            if d.get(x, []) != ab:
                print(-1)
                exit()
        r = ['' for i in range(n)]
        for x in a:
            r[x-1] = '1'
        for x in b:
            r[x-1] = '2'
        for x in c:
            r[x-1] = '3'
        print(" ".join(r))


import sys
input = sys.stdin.readline

def r1():
    return int(input())
def rm():
    return map(int, input().split())

n, m = rm()
d = {}
for i in range(m):
    x, y = rm()

    if x not in d:
        d[x] = set()
    d[x].add(y)

    if y not in d:
        d[y] = set()
    d[y].add(x)

bc = d.get(1, [])
if len(bc) == 0:
    print(-1)
    exit()

ac = d.get(next(iter(bc)), [])
if len(ac) == 0:
    print(-1)
    exit()

c = bc & ac
a = ac - c
b = bc - c

ab = a | b

na = len(a)
nb = len(b)
nc = len(c)
if na + nb + nc != n or na == 0 or nb == 0 or nc == 0:
    print(-1)
else:
    for x in a:
        if d.get(x, []) != bc:
            print(-1)
            exit()
    for x in b:
        if d.get(x, []) != ac:
            print(-1)
            exit()
    for x in c:
        if d.get(x, []) != ab:
            print(-1)
            exit()
    r = ['' for i in range(n)]
    for x in a:
        r[x-1] = '1'
    for x in b:
        r[x-1] = '2'
    for x in c:
        r[x-1] = '3'
    print(" ".join(r))


import os
import sys
from atexit import register
from io import BytesIO
sys.stdin = BytesIO(os.read(0, os.fstat(0).st_size))
sys.stdout = BytesIO()
register(lambda: os.write(1, sys.stdout.getvalue()))
input = lambda: sys.stdin.readline().rstrip('\r\n')
n,m = map(int,input().split(" "))
connected = [[]for i in range(n+1)]
edges = []
for i in range(m):
    a,b = map(int,input().split(" "))
    connected[a].append(b)
    connected[b].append(a)
    edges.append((a,b))
flag = True
labels = [0]*(n+1)
labels[1] = 1
alln = set(range(1,n+1))
if len(connected[1]) >0:
    node2 = connected[1][0]
else:
    flag = False
if flag:
    s1 = alln-set(connected[1])
    s2 = alln-set(connected[node2])
    s3 = alln-s1-s2
    n1,n2,n3 = len(s1),len(s2),len(s3)
    if n1+n2+n3!=n or min(n1,n2,n3) == 0:
        flag = False
    for i in list(s1):
        labels[i] = 1
    for i in list(s2):
        labels[i] = 2
    for i in list(s3):
        labels[i] = 3
    cnts = [0]*3
    for a,b in edges:
        if labels[a] == labels[b]:
            flag = False
            break
        cnts[labels[a]-1] += 1
        cnts[labels[b]-1] += 1
    if flag and cnts[0] == n1*(n2+n3) and cnts[1] == n2*(n1+n3) and cnts[2] == n3*(n1+n2):
        out = []
        for i in range(1,n+1):
            out.append(str(labels[i]))
        print " ".join(out)
    else:
        print -1
else:
    print -1


import os
import sys
from atexit import register
from io import BytesIO
sys.stdin = BytesIO(os.read(0, os.fstat(0).st_size))
sys.stdout = BytesIO()
register(lambda: os.write(1, sys.stdout.getvalue()))
input = lambda: sys.stdin.readline().rstrip('\r\n')
n,m = map(int,input().split(" "))
connected = [[]for i in range(n+1)]
edges = []
for i in range(m):
    a,b = map(int,input().split(" "))
    connected[a].append(b)
    connected[b].append(a)
    edges.append((a,b))
flag = True
labels = [0]*(n+1)
labels[1] = 1
alln = set(range(1,n+1))
if len(connected[1]) >0:
    node2 = connected[1][0]
else:
    flag = False
if flag:
    s1 = alln-set(connected[1])
    s2 = alln-set(connected[node2])
    s3 = alln-s1-s2
    n1,n2,n3 = len(s1),len(s2),len(s3)
    if n1+n2+n3!=n:
        flag = False
    for i in list(s1):
        labels[i] = 1
    for i in list(s2):
        labels[i] = 2
    for i in list(s3):
        labels[i] = 3
    cnts = [0]*3
    for a,b in edges:
        if labels[a] == labels[b]:
            flag = False
            break
        cnts[labels[a]-1] += 1
        cnts[labels[b]-1] += 1
    if flag and cnts[0] == n1*(n2+n3) and cnts[1] == n2*(n1+n3) and cnts[2] == n3*(n1+n2):
        out = []
        for i in range(1,n+1):
            out.append(str(labels[i]))
        print " ".join(out)
    else:
        print -1
else:
    print -1


import os
import sys
from atexit import register
from io import BytesIO
sys.stdin = BytesIO(os.read(0, os.fstat(0).st_size))
sys.stdout = BytesIO()
register(lambda: os.write(1, sys.stdout.getvalue()))
input = lambda: sys.stdin.readline().rstrip('\r\n')

n,m = map(int,input().split(" "))
connected = [[]for i in range(n+1)]
edges = []
for i in range(m):
    a,b = map(int,input().split(" "))
    connected[a].append(b)
    connected[b].append(a)
    edges.append((a,b))

flag = True
labels = [0]*(n+1)
labels[1] = 1
alln = set(range(1,n+1))
if len(connected[1]) >0:
    node2 = connected[1][0]
else:
    flag = False
if flag:
    s1 = alln-set(connected[1])
    s2 = alln-set(connected[node2])
    s3 = alln-s1-s2
    n1,n2,n3 = len(s1),len(s2),len(s3)
    for i in list(s1):
        labels[i] = 1
    for i in list(s2):
        labels[i] = 2
    for i in list(s3):
        labels[i] = 3
    cnts = [0]*3
    for a,b in edges:
        if labels[a] == labels[b]:
            flag = False
            break
        cnts[labels[a]-1] += 1
        cnts[labels[b]-1] += 1
    if flag and cnts[0] == n1*(n2+n3) and cnts[1] == n2*(n1+n3) and cnts[2] == n3*(n1+n2):
        out = []
        for i in range(1,n+1):
            out.append(str(labels[i]))
        print " ".join(out)
    else:
        print -1
else:
    print -1


from collections import Counter, defaultdict
from sys import stdin
raw_input = stdin.readline

n,m=map(int,raw_input().split())
d=defaultdict(list)
st=[[] for i in xrange(3)]
c=0
for i in xrange(m):
    u,v = map(int,raw_input().split())
    d[u].append(v)
    d[v].append(u)
vis=[0]*(n+1)
for i in xrange(1,n+1):
    if not vis[i]:
        if c>2:
            print -1
            exit()

        x=Counter(d[i])
        for j in xrange(1,n+1):

            if (not x[j]) and (not vis[j]):
                st[c].append(j)
                vis[j]=1
            elif (not x[j]) and vis[j]:
                print -1
                exit()
        c+=1

ans=[0]*(n+1)
val=1
c1=len(st[0])
c2=len(st[1])
c3=len(st[2])
if m!=(c1*c2)+(c2*c3)+(c3*c1):
    print -1
    exit()
for set in st:
    if not set:
        print -1
        exit()
    for j in set:
        ans[j]=val
    val+=1
for i in xrange(1,n+1):
    print ans[i],
print


from collections import Counter, defaultdict
from sys import stdin
raw_input = stdin.readline

n,m=map(int,raw_input().split())
d=defaultdict(list)
st=[[] for i in xrange(3)]
c=0
for i in xrange(m):
    u,v = map(int,raw_input().split())
    d[u].append(v)
    d[v].append(u)
vis=[0]*(n+1)
for i in xrange(1,n+1):
    if not vis[i]:
        if c>2:
            print -1
            exit()
        st[c].append(i)
        vis[i]=1

        x=Counter(d[i])
        for j in xrange(1,n+1):
            if j==i:
                continue
            if (not x[j]) and (not vis[j]):
                st[c].append(j)
                vis[j]=1
            elif (not x[j]) and vis[j]:
                print -1
                exit()
        c+=1

ans=[0]*(n+1)
val=1
for set in st:
    if not set:
        print -1
        exit()
    for j in set:
        ans[j]=val
    val+=1
for i in xrange(1,n+1):
    print ans[i],
print


from collections import Counter, defaultdict
from sys import stdin
raw_input = stdin.readline

n,m=map(int,raw_input().split())
d=defaultdict(list)
st=[[] for i in xrange(3)]
c=0
for i in xrange(m):
    u,v = map(int,raw_input().split())
    d[u].append(v)
    d[v].append(u)
vis=[0]*(n+1)
for i in xrange(1,n+1):
    if not vis[i]:
        if c>2:
            print -1
            exit()
        st[c].append(i)
        vis[i]=1

        x=Counter(d[i])
        for j in xrange(1,n+1):
            if j==i:
                continue
            if not x[j] and not vis[j]:
                st[c].append(j)
                vis[j]=1
            elif not x[j] and vis[j]:
                print -1
                exit()
        c+=1

ans=[0]*(n+1)
val=1
for set in st:
    if not set:
        print -1
        exit()
    for j in set:
        ans[j]=val
    val+=1
for i in xrange(1,n+1):
    print ans[i],
print


from collections import Counter, defaultdict
from sys import stdin
raw_input = stdin.readline

n,m=map(int,raw_input().split())
d=defaultdict(list)
st=[[] for i in xrange(3)]
c=0
for i in xrange(m):
    u,v = map(int,raw_input().split())
    d[u].append(v)
    d[v].append(u)
vis=[0]*(n+1)
for i in xrange(1,n+1):
    if not vis[i]:
        if c>2:
            print -1
            exit()

        x=Counter(d[i])
        for j in xrange(1,n+1):

            if (not x[j]) and (not vis[j]):
                st[c].append(j)
                vis[j]=1
                for k in d[j]:
                    x[k]=1

            elif (not x[j]) and vis[j]:
                print -1
                exit()
        c+=1

ans=[0]*(n+1)
val=1
c1=len(st[0])
c2=len(st[1])
c3=len(st[2])
if m!=(c1*c2)+(c2*c3)+(c3*c1):
    print -1
    exit()
for set in st:
    if not set:
        print -1
        exit()
    for j in set:
        ans[j]=val
    val+=1
for i in xrange(1,n+1):
    print ans[i],
print


from collections import Counter, defaultdict
from sys import stdin
raw_input = stdin.readline

n,m=map(int,raw_input().split())
d=defaultdict(list)
st=[[] for i in xrange(3)]
c=0
for i in xrange(m):
    u,v = map(int,raw_input().split())
    d[u].append(v)
    d[v].append(u)
vis=[0]*(n+1)
for i in xrange(1,n+1):
    if not vis[i]:
        if c>2:
            print -1
            exit()
        st[c].append(i)
        vis[i]=1

        x=Counter(d[i])
        for j in xrange(1,n+1):
            if j==i:
                continue
            if (not x[j]) and (not vis[j]):
                st[c].append(j)
                vis[j]=1
            elif (not x[j]) and vis[j]:
                print -1
                exit()
        c+=1

ans=[0]*(n+1)
val=1
c1=len(st[0])
c2=len(st[1])
c3=len(st[2])
if m!=(c1*c2)+(c2*c3)+(c3*c1):
    print -1
    exit()
for set in st:
    if not set:
        print -1
        exit()
    for j in set:
        ans[j]=val
    val+=1
for i in xrange(1,n+1):
    print ans[i],
print


from collections import Counter, defaultdict
from sys import stdin
raw_input = stdin.readline

n,m=map(int,raw_input().split())
d=defaultdict(list)
st=[[] for i in xrange(3)]
c=0
for i in xrange(m):
    u,v = map(int,raw_input().split())
    d[u].append(v)
    d[v].append(u)
vis=[0]*(n+1)
for i in xrange(1,n+1):
    if not vis[i]:
        if c>2:
            print -1
            exit()
        st[c].append(i)
        vis[i]=1

        x=Counter(d[i])
        for j in xrange(1,n+1):
            if j==i:
                continue
            if not x[j] and not vis[j]:
                st[c].append(j)
                vis[j]=1
            elif not x[j] and vis[j]:
                print -1
                exit()
        c+=1
ans=[0]*(n+1)
val=1
for set in st:
    for j in set:
        ans[j]=val
    val+=1
for i in xrange(1,n+1):
    print ans[i],
print


mod=10**9+7
#import resource
#resource.setrlimit(resource.RLIMIT_STACK, [0x100000000, resource.RLIM_INFINITY])
#import threading
#threading.stack_size(2**27)
#import sys
#sys.setrecursionlimit(10**7)
fact=[1]
for i in range(1,100001):
    fact.append((fact[-1]*i)%mod)
ifact=[0]*100001
ifact[100000]=pow(fact[100000],mod-2,mod)
for i in range(100000,0,-1):
    ifact[i-1]=(i*ifact[i])%mod
from sys import stdin, stdout
import bisect
from bisect import bisect_left as bl              #c++ lowerbound bl(array,element)
from bisect import bisect_right as br             #c++ upperbound
import itertools
import collections
import math
import heapq
from random import randint as rn
from Queue import Queue as Q
def modinv(n,p):
    return pow(n,p-2,p)
def ncr(n,r,p):                        #for using this uncomment the lines calculating fact and ifact
    t=((fact[n])*((ifact[r]*ifact[n-r])%p))%p
    return t
def ain():                           #takes array as input
    return list(map(int,sin().split()))
def sin():
    return input().strip()
def GCD(x,y):
    while(y):
        x, y = y, x % y
    return x
"""*******************************************************"""
def main():
    n,m=ain()
    b=[set() for i in range(n+1)]
    t1=[]
    fre=[0]*n
    for i in range(m):
        x,y=ain()
        b[x].add(y)
        b[y].add(x)
        t1.append([x,y])
    s1=set()
    t=-1
    for i in range(1,n+1):
        if(i not in b[1]):
            s1.add(i)
            fre[i-1]="1"
        else:
            t=i
    if(t==-1):
        print -1
        exit()
    s2=set()
    s3=set()
    for i in range(1,n+1):
        if(i in s1):
            continue
        if(i not in b[t]):
            s2.add(i)
            fre[i-1]="2"
        else:
            s3.add(i)
            fre[i-1]="3"
    if(len(s3)==0):
        print -1
        exit()
    fl=0
    for i in range(m):
        x,y=t1[i]
        if((x in s1) and (y in s1)):
            fl=1
            break
        if((x in s2) and (y in s2)):
            fl=1
            break
        if((x in s3) and (y in s3)):
            fl=1
            break
    if(fl==1):
        print -1
        exit()
    s=(len(s2)*len(s3))+(len(s1)*len(s3))+(len(s1)*len(s2))
    if(s==m):
        print " ".join(fre)
    else:
        print -1
######## Python 2 and 3 footer by Pajenegod and c1729
py2 = round(0.5)
if py2:
    from future_builtins import ascii, filter, hex, map, oct, zip
    range = xrange

import os, sys
from io import IOBase, BytesIO

BUFSIZE = 8192
class FastIO(BytesIO):
    newlines = 0
    def __init__(self, file):
        self._file = file
        self._fd = file.fileno()
        self.writable = "x" in file.mode or "w" in file.mode
        self.write = super(FastIO, self).write if self.writable else None

    def _fill(self):
        s = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
        self.seek((self.tell(), self.seek(0,2), super(FastIO, self).write(s))[0])
        return s
    def read(self):
        while self._fill(): pass
        return super(FastIO,self).read()

    def readline(self):
        while self.newlines == 0:
            s = self._fill(); self.newlines = s.count(b"\n") + (not s)
        self.newlines -= 1
        return super(FastIO, self).readline()

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

class IOWrapper(IOBase):
    def __init__(self, file):
        self.buffer = FastIO(file)
        self.flush = self.buffer.flush
        self.writable = self.buffer.writable
        if py2:
            self.write = self.buffer.write
            self.read = self.buffer.read
            self.readline = self.buffer.readline
        else:
            self.write = lambda s:self.buffer.write(s.encode('ascii'))
            self.read = lambda:self.buffer.read().decode('ascii')
            self.readline = lambda:self.buffer.readline().decode('ascii')

sys.stdin, sys.stdout = IOWrapper(sys.stdin), IOWrapper(sys.stdout)
input = lambda: sys.stdin.readline().rstrip('\r\n')

if __name__ == '__main__':
    main()
#threading.Thread(target=main).start()


import sys
range = xrange
input = raw_input

inp = [int(x) for x in sys.stdin.read().split()]
ii = 0

n = inp[ii]; ii += 1
m = inp[ii]; ii += 1

if m == 0:
    #print ' '.join(['1']*n)
    print -1
    sys.exit()

coupl = [[] for _ in range(n)]
a = inp[ii] - 1; ii += 1
b = inp[ii] - 1; ii += 1

ii -= 2

for _ in range(m):
    u = inp[ii] - 1; ii += 1
    v = inp[ii] - 1; ii += 1
    coupl[u].append(v)
    coupl[v].append(u)

A = set(coupl[a])
B = set(coupl[b])

C = A & B
A -= C
B -= C

if len(A) + len(B) + len(C) != n or not A or not B or not C:
    print -1
    sys.exit()

out = [0]*n
for c in range(1, 3 + 1):
    if c == 1:
        D = A
    elif  c == 2:
        D = B
    else:
        D = C

    left = n - len(D)
    for node in D:
        out[node] = c
        if len(coupl[node]) != left or any(nei in D for nei in coupl[node]):
            print -1
            sys.exit()
print ' '.join(str(x) for x in out)


import sys
range = xrange
input = raw_input

inp = [int(x) for x in sys.stdin.read().split()]
ii = 0

n = inp[ii]; ii += 1
m = inp[ii]; ii += 1

if m == 0:
    #print ' '.join(['1']*n)
    print -1
    sys.exit()

coupl = [[] for _ in range(n)]
a = inp[ii] - 1; ii += 1
b = inp[ii] - 1; ii += 1

ii -= 2

for _ in range(m):
    u = inp[ii] - 1; ii += 1
    v = inp[ii] - 1; ii += 1
    coupl[u].append(v)
    coupl[v].append(u)

A = set(coupl[a])
B = set(coupl[b])

C = A & B
A -= C
B -= C

if len(A) + len(B) + len(C) != n:
    print -1
    sys.exit()

out = [0]*n
for c in range(1, 3 + 1):
    if c == 1:
        D = A
    elif  c == 2:
        D = B
    else:
        D = C

    left = n - len(D)
    for node in D:
        out[node] = c
        if len(coupl[node]) != left or any(nei in D for nei in coupl[node]):
            print -1
            sys.exit()
print ' '.join(str(x) for x in out)


import sys
range = xrange
input = raw_input

inp = [int(x) for x in sys.stdin.read().split()]
ii = 0

n = inp[ii]; ii += 1
m = inp[ii]; ii += 1

if m == 0:
    #print ' '.join(['1']*n)
    print -1
    sys.exit()

coupl = [[] for _ in range(n)]
a = inp[ii] - 1; ii += 1
b = inp[ii] - 1; ii += 1

ii -= 2

for _ in range(m):
    u = inp[ii] - 1; ii += 1
    v = inp[ii] - 1; ii += 1
    coupl[u].append(v)
    coupl[v].append(u)

A = set(coupl[a])
B = set(coupl[b])

C = A & B
A -= C
B -= C

if len(A) + len(B) + len(C) != n:
    print -1
    sys.exit()

out = [0]*n
for c in range(1, 3 + 1):
    if c == 1:
        D = A
    elif  c == 2:
        D = B
    else:
        D = C

    left = n - len(D)
    for node in D:
        out[node] = c
        if len(coupl[node]) != left:
            print -1
            sys.exit()
print ' '.join(str(x) for x in out)


from collections import defaultdict

n, m = map(int, raw_input().split(' '))
edges = defaultdict(dict)
for i in range(m):
    a, b = map(int, raw_input().split(' '))
    edges[a][b] = True
    edges[b][a] = True

colors = {}

color = 1

for i in range(1, n + 1):
    str_color = str(color)
    if colors.get(i):
        continue
    else:
        colors[i] = str_color
    current = [i]

    for j in range(i + 1, n + 1):
        good = True

        for c in current:
            if edges[j].get(c):
                good = False
                break
        if good:
            colors[j] = str_color
            current.append(j)
    color += 1

if colors[n] == '3':
    print ' '.join(colors[i] for i in range(1, n + 1))
else:
    print -1


from collections import defaultdict

n, m = map(int, raw_input().split(' '))
edges = defaultdict(dict)
for i in range(m):
    a, b = map(int, raw_input().split(' '))
    edges[a - 1][b - 1] = True
    edges[b - 1][a - 1] = True

colors = [-1] * n

def get_vertex_set(index):
    x = 0
    for i in xrange(n):
        if colors[i] == -1:
            x = i
            break
    else:
        return []
    colors[x] = index
    s = [x]
    for i in xrange(n):
        if x != i and colors[i] == -1 and not edges[x].get(i):
            colors[i] = index
            s.append(i)
    return s

def is_intersect(s1, s2):
    for x in s1:
        for y in s2:
            if edges[x].get(y):
                return True
    return False

s1 = get_vertex_set(1)
s2 = get_vertex_set(2)
s3 = get_vertex_set(3)
s4 = get_vertex_set(4)
count1 = len(s1)
count2 = len(s2)
count3 = len(s3)
count4 = len(s4)

if count4 != 0:
    print -1
elif count3 == 0:
    print -1
elif count1 * count2 + count2 * count3 + count1 * count3 != m:
    print -1
elif not is_intersect(s1, s2) or \
        not is_intersect(s1, s3) or \
        not is_intersect(s2, s3):
    print -1
else:
    print ' '.join(map(str, colors))


from collections import defaultdict

n, m = map(int, raw_input().split(' '))
edges = defaultdict(dict)
for i in range(m):
    a, b = map(int, raw_input().split(' '))
    edges[a - 1][b - 1] = True
    edges[b - 1][a - 1] = True

colors = [-1] * n

def get_vertex_set(index):
    x = 0
    for i in xrange(n):
        if colors[i] == -1:
            x = i
            break
    else:
        return []
    colors[x] = index
    s = [x]
    for i in xrange(n):
        if x != i and colors[i] == -1 and not edges[x].get(i):
            colors[i] = index
            s.append(i)
    return s

def is_good(s1, s2):
    for x in s1:
        for y in s2:
            if not edges[x].get(y):
                return False
    return True

s1 = get_vertex_set(1)
s2 = get_vertex_set(2)
s3 = get_vertex_set(3)
s4 = get_vertex_set(4)
count1 = len(s1)
count2 = len(s2)
count3 = len(s3)
count4 = len(s4)

if count4 != 0:
    print -1
elif count3 == 0:
    print -1
elif is_good(s1, s2) and \
        is_good(s1, s3) and \
        is_good(s2, s3):
    print ' '.join(map(str, colors))
else:
    print -1


import atexit
import io
import sys

buff = io.BytesIO()
sys.stdout = buff

@atexit.register
def write():
    sys.__stdout__.write(buff.getvalue())

import sys
from collections import defaultdict

n, m = map(int, raw_input().split(' '))
edges = defaultdict(dict)
for line in sys.stdin:
    a, b = map(int, line.split(' '))
    edges[a][b] = True
    edges[b][a] = True

colors = {}

color = 1

for i in range(1, n + 1):
    str_color = str(color)
    if colors.get(i):
        continue
    else:
        colors[i] = str_color
    current = [i]

    for j in range(1, n + 1):
        good = True

        for c in current:
            if edges[j].get(c):
                good = False
        if good:
            colors[j] = str_color
            current.append(j)
    color += 1

if colors[n] == '3':
    print ' '.join(colors[i] for i in range(1, n + 1))
else:
    print -1


from collections import defaultdict

n, m = map(int, raw_input().split(' '))
edges = defaultdict(dict)
for i in range(m):
    a, b = map(int, raw_input().split(' '))
    edges[a - 1][b - 1] = True
    edges[b - 1][a - 1] = True

colors = [-1] * n

def get_vertex_set(index):
    x = 0
    for i in xrange(n):
        if colors[i] == -1:
            x = i
            break
    else:
        return []
    colors[x] = index
    s = []
    for i in xrange(n):
        if x != i and colors[i] == -1 and not edges[x].get(i):
            colors[i] = index
            s.append(i)
    return s

def is_intersect(s1, s2):
    for x in s1:
        for y in s2:
            if edges[x].get(y):
                return True
    return False

s1 = get_vertex_set(1)
s2 = get_vertex_set(2)
s3 = get_vertex_set(3)
s4 = get_vertex_set(4)
count1 = len(s1)
count2 = len(s2)
count3 = len(s3)
count4 = len(s4)

if count4 != 0:
    print -1
elif count3 == 0:
    print -1
elif count1 * count2 + count2 * count3 + count1 * count3 != m:
    print -1
elif is_intersect(s1, s2) or is_intersect(s1, s3) or is_intersect(s2, s3):
    print -1
else:
    print ' '.join(map(str, colors))


import sys
range = xrange
input = raw_input

from collections import defaultdict

inp = map(int, sys.stdin.read().split())

ii = 0
n = inp[ii]; ii += 1
m = inp[ii]; ii += 1

edges = defaultdict(dict)
for i in range(m):
    a = inp[ii]; ii+= 1
    b = inp[ii]; ii+= 1
    edges[a - 1][b - 1] = True
    edges[b - 1][a - 1] = True

colors = [-1] * n

def get_vertex_set(index):
    x = 0
    for i in xrange(n):
        if colors[i] == -1:
            x = i
            break
    else:
        return []
    colors[x] = index
    s = [x]
    for i in xrange(n):
        if x != i and colors[i] == -1 and not edges[x].get(i):
            colors[i] = index
            s.append(i)
    return s

def is_good(s1, s2):
    for x in s1:
        for y in s2:
            if not edges[x].get(y):
                return False
    return True

s1 = get_vertex_set(1)
s2 = get_vertex_set(2)
s3 = get_vertex_set(3)
s4 = get_vertex_set(4)
count1 = len(s1)
count2 = len(s2)
count3 = len(s3)
count4 = len(s4)

if count4 != 0:
    print -1
elif count3 == 0:
    print -1
elif count1 * count2 + count2 * count3 + count1 * count3 != m:
    print -1
elif is_good(s1, s2) and \
        is_good(s1, s3) and \
        is_good(s2, s3):
    print ' '.join(map(str, colors))
else:
    print -1


# import atexit
# import io
# import sys
#
# buff = io.BytesIO()
# sys.stdout = buff
#
#
# @atexit.register
# def write():
#     sys.__stdout__.write(buff.getvalue())
import sys
from collections import defaultdict

n, m = map(int, raw_input().split(' '))
edges = defaultdict(dict)
for i in range(m):
    a, b = map(int, raw_input().split(' '))
    edges[a][b] = True
    edges[b][a] = True

colors = {}

color = 1

for i in range(1, n + 1):
    str_color = str(color)
    if colors.get(i):
        continue
    else:
        colors[i] = str_color
    current = [i]

    for j in range(1, n + 1):
        good = True

        for c in current:
            if edges[j].get(c):
                good = False
        if good:
            colors[j] = str_color
            current.append(j)
    color += 1

if colors[n] == '3':
    print ' '.join(colors[i] for i in range(1, n + 1))
else:
    print -1


from collections import defaultdict

n, m = map(int, raw_input().split(' '))
edges = defaultdict(dict)
for i in range(m):
    a, b = map(int, raw_input().split(' '))
    edges[a - 1][b - 1] = True
    edges[b - 1][a - 1] = True

nodes = set(range(n))

colors = [-1] * n

def get_isle(index):
    current = []

    for node in nodes:
        if colors[node] != -1:
            continue
        for x in current:
            if edges[node].get(x):
                break
        else:
            colors[node] = index
            current.append(node)
    return len(current)

impossible = False
for i in range(1, 4):
    if get_isle(i) == 0:
        impossible = True

if get_isle(4) != 0:
    impossible = True

if impossible:
    print -1
else:
    print ' '.join(map(str, colors))


from collections import defaultdict

n, m = map(int, raw_input().split(' '))
edges = defaultdict(dict)
for i in range(m):
    a, b = map(int, raw_input().split(' '))
    edges[a - 1][b - 1] = True
    edges[b - 1][a - 1] = True

colors = [-1] * n

def get_vertex_set(index):
    x = 0
    for i in xrange(n):
        if colors[i] == -1:
            x = i
            break
    else:
        return 0
    count = 1
    colors[x] = index
    for i in xrange(n):
        if x != i and colors[i] == -1 and not edges[x].get(i):
            colors[i] = index
            count += 1
    return count

count1 = get_vertex_set(1)
count2 = get_vertex_set(2)
count3 = get_vertex_set(3)
count4 = get_vertex_set(4)
if count4 != 0:
    print -1
elif count3 == 0:
    print -1
elif count1 * count2 + count2 * count3 + count1 * count3 != m:
    print -1
else:
    print ' '.join(map(str, colors))


from collections import defaultdict

n, m = map(int, raw_input().split(' '))
edges = defaultdict(dict)
for i in range(m):
    a, b = map(int, raw_input().split(' '))
    edges[a - 1][b - 1] = True
    edges[b - 1][a - 1] = True

colors = [-1] * n

def get_vertex_set(index):
    x = 0
    for i in xrange(n):
        if colors[i] == -1:
            x = i
            break
    else:
        return []
    colors[x] = index
    s = [x]
    for i in xrange(n):
        if x != i and colors[i] == -1 and not edges[x].get(i):
            colors[i] = index
            s.append(i)
    return s

def is_intersect(s1, s2):
    for x in s1:
        for y in s2:
            if edges[x].get(y):
                return True
    return False

s1 = get_vertex_set(1)
s2 = get_vertex_set(2)
s3 = get_vertex_set(3)
s4 = get_vertex_set(4)
count1 = len(s1)
count2 = len(s2)
count3 = len(s3)
count4 = len(s4)

if count4 != 0:
    print -1
elif count3 == 0:
    print -1
elif count1 * count2 + count2 * count3 + count1 * count3 != m:
    print -1
elif not is_intersect(s1, s2) and \
        not is_intersect(s1, s3) and \
        not is_intersect(s2, s3):
    print -1
else:
    print ' '.join(map(str, colors))


from collections import defaultdict

n, m = map(int, raw_input().split(' '))
edges = defaultdict(dict)
for i in range(m):
    a, b = map(int, raw_input().split(' '))
    edges[a - 1][b - 1] = True
    edges[b - 1][a - 1] = True

colors = [-1] * n

def get_vertex_set(index):
    x = 0
    for i in xrange(n):
        if colors[i] == -1:
            x = i
            break
    else:
        return []
    colors[x] = index
    s = [x]
    for i in xrange(n):
        if x != i and colors[i] == -1 and not edges[x].get(i):
            colors[i] = index
            s.append(i)
    return s

def is_good(s1, s2):
    for x in s1:
        for y in s2:
            if not edges[x].get(y):
                return False
    return True

s1 = get_vertex_set(1)
s2 = get_vertex_set(2)
s3 = get_vertex_set(3)
s4 = get_vertex_set(4)
count1 = len(s1)
count2 = len(s2)
count3 = len(s3)
count4 = len(s4)

if count4 != 0:
    print -1
elif count3 == 0:
    print -1
elif count1 * count2 + count2 * count3 + count1 * count3 != m:
    print -1
elif is_good(s1, s2) and \
        is_good(s1, s3) and \
        is_good(s2, s3):
    print -1
else:
    print ' '.join(map(str, colors))


def main():
    from collections import defaultdict

    inp = readnumbers()

    ii = 0
    n = inp[ii];
    ii += 1
    m = inp[ii];
    ii += 1

    edges = defaultdict(dict)
    for i in range(m):
        a = inp[ii];
        ii += 1
        b = inp[ii];
        ii += 1
        edges[a - 1][b - 1] = True
        edges[b - 1][a - 1] = True

    colors = [-1] * n

    def get_vertex_set(index):
        x = 0
        for i in range(n):
            if colors[i] == -1:
                x = i
                break
        else:
            return []
        colors[x] = index
        s = [x]
        for i in range(n):
            if x != i and colors[i] == -1 and not edges[x].get(i):
                colors[i] = index
                s.append(i)
        return s

    def is_good(s1, s2):
        for x in s1:
            for y in s2:
                if not edges[x].get(y):
                    return False
        return True

    s1 = get_vertex_set(1)
    s2 = get_vertex_set(2)
    s3 = get_vertex_set(3)
    s4 = get_vertex_set(4)
    count1 = len(s1)
    count2 = len(s2)
    count3 = len(s3)
    count4 = len(s4)

    if count4 != 0:
        print -1
    elif count3 == 0:
        print -1
    elif count1 * count2 + count2 * count3 + count1 * count3 != m:
        print -1
    elif is_good(s1, s2) and \
            is_good(s1, s3) and \
            is_good(s2, s3):
        print ' '.join(map(str, colors))
    else:
        print -1

######## Python 2 and 3 footer by Pajenegod and c1729

# Note because cf runs old PyPy3 version which doesn't have the sped up
# unicode strings, PyPy3 strings will many times be slower than pypy2.
# There is a way to get around this by using binary strings in PyPy3
# but its syntax is different which makes it kind of a mess to use.

# So on cf, use PyPy2 for best string performance.

py2 = round(0.5)
if py2:
    from future_builtins import ascii, filter, hex, map, oct, zip

    range = xrange

import os, sys
from io import IOBase, BytesIO

BUFSIZE = 8192

class FastIO(BytesIO):
    newlines = 0

    def __init__(self, file):
        self._file = file
        self._fd = file.fileno()
        self.writable = "x" in file.mode or "w" in file.mode
        self.write = super(FastIO, self).write if self.writable else None

    def _fill(self):
        s = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
        self.seek(
            (self.tell(), self.seek(0, 2), super(FastIO, self).write(s))[0])
        return s

    def read(self):
        while self._fill(): pass
        return super(FastIO, self).read()

    def readline(self):
        while self.newlines == 0:
            s = self._fill();
            self.newlines = s.count(b"\n") + (not s)
        self.newlines -= 1
        return super(FastIO, self).readline()

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

class IOWrapper(IOBase):
    def __init__(self, file):
        self.buffer = FastIO(file)
        self.flush = self.buffer.flush
        self.writable = self.buffer.writable
        if py2:
            self.write = self.buffer.write
            self.read = self.buffer.read
            self.readline = self.buffer.readline
        else:
            self.write = lambda s: self.buffer.write(s.encode('ascii'))
            self.read = lambda: self.buffer.read().decode('ascii')
            self.readline = lambda: self.buffer.readline().decode('ascii')

sys.stdin, sys.stdout = IOWrapper(sys.stdin), IOWrapper(sys.stdout)
input = lambda: sys.stdin.readline().rstrip('\r\n')

# Cout implemented in Python
import sys

class ostream:
    def __lshift__(self, a):
        sys.stdout.write(str(a))
        return self

cout = ostream()
endl = '\n'

# Read all remaining integers in stdin, type is given by optional argument, this is fast
def readnumbers(zero=0):
    conv = ord if py2 else lambda x: x
    A = [];
    numb = zero;
    sign = 1;
    i = 0;
    s = sys.stdin.buffer.read()
    try:
        while True:
            if s[i] >= b'0'[0]:
                numb = 10 * numb + conv(s[i]) - 48
            elif s[i] == b'-'[0]:
                sign = -1
            elif s[i] != b'\r'[0]:
                A.append(sign * numb)
                numb = zero;
                sign = 1
            i += 1
    except:
        pass
    if s and s[-1] >= b'0'[0]:
        A.append(sign * numb)
    return A

if __name__ == "__main__":
    main()


from collections import defaultdict

n, m = map(int, raw_input().split(' '))
edges = defaultdict(dict)
for i in range(m):
    a, b = map(int, raw_input().split(' '))
    edges[a][b] = True
    edges[b][a] = True

colors = {}

color = 1

for i in range(1, n + 1):
    str_color = str(color)
    if colors.get(i):
        continue
    else:
        colors[i] = str_color
    current = [i]

    for j in range(1, n + 1):
        good = True

        for c in current:
            if edges[j].get(c):
                good = False
                break
        if good:
            colors[j] = str_color
            current.append(j)
    color += 1

if colors[n] == '3':
    print ' '.join(colors[i] for i in range(1, n + 1))
else:
    print -1


from collections import defaultdict

n, m = map(int, raw_input().split(' '))
edges = defaultdict(dict)
for i in range(m):
    a, b = map(int, raw_input().split(' '))
    edges[a - 1][b - 1] = True
    edges[b - 1][a - 1] = True

colors = [-1] * n

def get_vertex_set(index):
    x = 0
    for i in xrange(n):
        if colors[i] == -1:
            x = i
            break
    else:
        return []
    colors[x] = index
    s = [x]
    for i in xrange(n):
        if x != i and colors[i] == -1 and not edges[x].get(i):
            colors[i] = index
            s.append(i)
    return s

def is_good(s1, s2):
    for x in s1:
        for y in s2:
            if not edges[x].get(y):
                return False
    return True

s1 = get_vertex_set(1)
s2 = get_vertex_set(2)
s3 = get_vertex_set(3)
s4 = get_vertex_set(4)
count1 = len(s1)
count2 = len(s2)
count3 = len(s3)
count4 = len(s4)

if count4 != 0:
    print -1
elif count3 == 0:
    print -1
elif count1 * count2 + count2 * count3 + count1 * count3 != m:
    print -1
elif is_good(s1, s2) and \
        is_good(s1, s3) and \
        is_good(s2, s3):
    print ' '.join(map(str, colors))
else:
    print -1


import sys
readline = sys.stdin.readline

n, m = map(int, input().split())
ans = True
graph = [[] for i in range(n)]

for _ in range(m):
    a, b = map(int, readline().split())
    a-=1
    b-=1
    graph[a].append(b)
    graph[b].append(a)

s1 = set(graph[0])
s2 = set()
if len(graph[0]) != 0:
    s2 = set(graph[graph[0][0]])
else:
    ans = False
s3 = set(s1.union(s2)) - (s1 & s2)

s21 = s2 & s3
s22 = s1 & s3
s23 = s2 & s1

if len(s21) + len(s22) + len(s23) != n:
    ans = False
if len(s21) == 0 or len(s22) == 0 or len(s23) == 0:
    ans = False

if ans:
    for x in s21:
        if set(graph[x]) != s1:
            ans = False
            break
if ans:
    for x in s22:
        if set(graph[x]) != s2:
            ans = False
            break
if ans:
    for x in s23:
        if set(graph[x]) != s3:
            ans = False
            break

if not ans:
    print(-1)
else:
    for i in range(n):
        if i in s21:
            print(1, end=' ')
        elif i in s22:
            print(2, end=' ')
        else:
            print(3, end=' ')


n, m = map(int, input().split())
ans = True
graph = [[] for i in range(n)]
for _ in range(m):
    a, b = map(int, input().split())
    a-=1
    b-=1
    graph[a].append(b)
    graph[b].append(a)
s1 = set(graph[0])
s2 = set()
if len(graph[0]) != 0:
    s2 = set(graph[graph[0][0]])
else:
    ans = False
s3 = set(s1.union(s2)) - (s1 & s2)
s21 = s2 & s3
s22 = s1 & s3
s23 = s2 & s1
if len(s21) + len(s22) + len(s23) != n:
    ans = False
if len(s21) == 0 or len(s22) == 0 or len(s23) == 0:
    ans = False
if ans:
    for x in s21:
        if set(graph[x]) != s1:
            ans = False
            break
if ans:
    for x in s22:
        if set(graph[x]) != s2:
            ans = False
            break
if ans:
    for x in s23:
        if set(graph[x]) != s3:
            ans = False
            break
if not ans:
    print(-1)
else:
    for i in range(n):
        if i in s21:
            print(1, end=' ')
        elif i in s22:
            print(2, end=' ')
        else:
            print(3, end=' ')


import os, sys
from io import IOBase, BytesIO
py2 = round(0.5)
if py2:
    from future_builtins import ascii, filter, hex, map, oct, zip
    range = xrange
BUFSIZE = 8192
class FastIO(BytesIO):
    newlines = 0

    def __init__(self, file):
        self._file = file
        self._fd = file.fileno()
        self.writable = "x" in file.mode or "w" in file.mode
        self.write = super(FastIO, self).write if self.writable else None

    def _fill(self):
        s = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
        self.seek((self.tell(), self.seek(0,2), super(FastIO, self).write(s))[0])
        return s

    def read(self):
        while self._fill(): pass
        return super(FastIO,self).read()

    def readline(self):
        while self.newlines == 0:
            s = self._fill(); self.newlines = s.count(b"\n") + (not s)
        self.newlines -= 1
        return super(FastIO, self).readline()

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

class IOWrapper(IOBase):
    def __init__(self, file):
        self.buffer = FastIO(file)
        self.flush = self.buffer.flush
        self.writable = self.buffer.writable
        if py2:
            self.write = self.buffer.write
            self.read = self.buffer.read
            self.readline = self.buffer.readline
        else:
            self.write = lambda s:self.buffer.write(s.encode('ascii'))
            self.read = lambda:self.buffer.read().decode('ascii')
            self.readline = lambda:self.buffer.readline().decode('ascii')

sys.stdin, sys.stdout = IOWrapper(sys.stdin), IOWrapper(sys.stdout)
input = lambda: sys.stdin.readline().rstrip('\r\n')

# Cout implemented in Python
import sys
class ostream:
    def __lshift__(self,a):
        sys.stdout.write(str(a))
        return self
cout = ostream()
endl = '\n'

n,m=map(int,input().split())
gr=[set() for i in range(n)]

for _ in range(m):
	a,b=map(int,input().split())
	gr[a-1].add(b-1)
	gr[b-1].add(a-1)

v=[False for i in range(n)]

group=[-1 for i in range(n)]
d=1
i=0
while d<4:
	if v[i]:continue
	v[i]=True
	group[i]=d
	for j in range(n):
		if j not in gr[i]:
			group[j]=d
			v[j]=True
	d+=1
	if False in v:i=v.index(False)
	else:break
if False in v:print(-1)
else:
	a,b,c=group.count(1),group.count(2),group.count(3)
	if a*b+b*c+a*c==m and a!=0 and b!=0 and c!=0:print(*group)
	else:print(-1)


import os, sys
from io import IOBase, BytesIO
py2 = round(0.5)
if py2:
    from future_builtins import ascii, filter, hex, map, oct, zip
    range = xrange
BUFSIZE = 8192
class FastIO(BytesIO):
    newlines = 0

    def __init__(self, file):
        self._file = file
        self._fd = file.fileno()
        self.writable = "x" in file.mode or "w" in file.mode
        self.write = super(FastIO, self).write if self.writable else None

    def _fill(self):
        s = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
        self.seek((self.tell(), self.seek(0,2), super(FastIO, self).write(s))[0])
        return s

    def read(self):
        while self._fill(): pass
        return super(FastIO,self).read()

    def readline(self):
        while self.newlines == 0:
            s = self._fill(); self.newlines = s.count(b"\n") + (not s)
        self.newlines -= 1
        return super(FastIO, self).readline()

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

class IOWrapper(IOBase):
    def __init__(self, file):
        self.buffer = FastIO(file)
        self.flush = self.buffer.flush
        self.writable = self.buffer.writable
        if py2:
            self.write = self.buffer.write
            self.read = self.buffer.read
            self.readline = self.buffer.readline
        else:
            self.write = lambda s:self.buffer.write(s.encode('ascii'))
            self.read = lambda:self.buffer.read().decode('ascii')
            self.readline = lambda:self.buffer.readline().decode('ascii')

sys.stdin, sys.stdout = IOWrapper(sys.stdin), IOWrapper(sys.stdout)
input = lambda: sys.stdin.readline().rstrip('\r\n')

# Cout implemented in Python
import sys
class ostream:
    def __lshift__(self,a):
        sys.stdout.write(str(a))
        return self
cout = ostream()
endl = '\n'

n,m=map(int,input().split())
gr=[set() for i in range(n)]

for _ in range(m):
	a,b=map(int,input().split())
	gr[a-1].add(b-1)
	gr[b-1].add(a-1)

v=[False for i in range(n)]

group=[-1 for i in range(n)]
d=1
i=0
while d<4:
	if v[i]:continue
	v[i]=True
	group[i]=d
	for j in range(n):
		if j not in gr[i]:
			group[j]=d
			v[j]=True
	d+=1
	if False in v:i=v.index(False)
	else:break
if False in v:print(-1)
else:
	a,b,c=group.count(1),group.count(2),group.count(3)
	t=True
	for i in range(n):
		for j in gr[i]:
			if group[i]==group[j]:
				t=False
				break
	if t and a*b+b*c+a*c==m and a!=0 and b!=0 and c!=0:print(*group)
	else:print(-1)


import os, sys
from io import IOBase, BytesIO
py2 = round(0.5)
if py2:
    from future_builtins import ascii, filter, hex, map, oct, zip
    range = xrange
BUFSIZE = 8192
class FastIO(BytesIO):
    newlines = 0

    def __init__(self, file):
        self._file = file
        self._fd = file.fileno()
        self.writable = "x" in file.mode or "w" in file.mode
        self.write = super(FastIO, self).write if self.writable else None

    def _fill(self):
        s = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
        self.seek((self.tell(), self.seek(0,2), super(FastIO, self).write(s))[0])
        return s

    def read(self):
        while self._fill(): pass
        return super(FastIO,self).read()

    def readline(self):
        while self.newlines == 0:
            s = self._fill(); self.newlines = s.count(b"\n") + (not s)
        self.newlines -= 1
        return super(FastIO, self).readline()

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

class IOWrapper(IOBase):
    def __init__(self, file):
        self.buffer = FastIO(file)
        self.flush = self.buffer.flush
        self.writable = self.buffer.writable
        if py2:
            self.write = self.buffer.write
            self.read = self.buffer.read
            self.readline = self.buffer.readline
        else:
            self.write = lambda s:self.buffer.write(s.encode('ascii'))
            self.read = lambda:self.buffer.read().decode('ascii')
            self.readline = lambda:self.buffer.readline().decode('ascii')

sys.stdin, sys.stdout = IOWrapper(sys.stdin), IOWrapper(sys.stdout)
input = lambda: sys.stdin.readline().rstrip('\r\n')

# Cout implemented in Python
import sys
class ostream:
    def __lshift__(self,a):
        sys.stdout.write(str(a))
        return self
cout = ostream()
endl = '\n'

n,m=map(int,input().split())
gr=[set() for i in range(n)]

for _ in range(m):
	a,b=map(int,input().split())
	gr[a-1].add(b-1)
	gr[b-1].add(a-1)

v=[False for i in range(n)]

group=[-1 for i in range(n)]
d=1
i=0
while d<4:
	if v[i]:continue
	v[i]=True
	group[i]=d
	for j in range(n):
		if j not in gr[i]:
			group[j]=d
			v[j]=True
	d+=1
	if d<4:i=v.index(False)
if False in v:print(-1)
else:
	a,b,c=group.count(1),group.count(2),group.count(3)
	if a*b+b*c+a*c==m:print(*group)
	else:print(-1)


import os, sys
from io import IOBase, BytesIO
py2 = round(0.5)
if py2:
    from future_builtins import ascii, filter, hex, map, oct, zip
    range = xrange
BUFSIZE = 8192
class FastIO(BytesIO):
    newlines = 0

    def __init__(self, file):
        self._file = file
        self._fd = file.fileno()
        self.writable = "x" in file.mode or "w" in file.mode
        self.write = super(FastIO, self).write if self.writable else None

    def _fill(self):
        s = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
        self.seek((self.tell(), self.seek(0,2), super(FastIO, self).write(s))[0])
        return s

    def read(self):
        while self._fill(): pass
        return super(FastIO,self).read()

    def readline(self):
        while self.newlines == 0:
            s = self._fill(); self.newlines = s.count(b"\n") + (not s)
        self.newlines -= 1
        return super(FastIO, self).readline()

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

class IOWrapper(IOBase):
    def __init__(self, file):
        self.buffer = FastIO(file)
        self.flush = self.buffer.flush
        self.writable = self.buffer.writable
        if py2:
            self.write = self.buffer.write
            self.read = self.buffer.read
            self.readline = self.buffer.readline
        else:
            self.write = lambda s:self.buffer.write(s.encode('ascii'))
            self.read = lambda:self.buffer.read().decode('ascii')
            self.readline = lambda:self.buffer.readline().decode('ascii')

sys.stdin, sys.stdout = IOWrapper(sys.stdin), IOWrapper(sys.stdout)
input = lambda: sys.stdin.readline().rstrip('\r\n')

# Cout implemented in Python
import sys
class ostream:
    def __lshift__(self,a):
        sys.stdout.write(str(a))
        return self
cout = ostream()
endl = '\n'

n,m=map(int,input().split())
gr=[set() for i in range(n)]

for _ in range(m):
	a,b=map(int,input().split())
	gr[a-1].add(b-1)
	gr[b-1].add(a-1)

v=[False for i in range(n)]

group=[-1 for i in range(n)]
d=1
i=0
while d<4:
	if v[i]:continue
	v[i]=True
	group[i]=d
	for j in range(n):
		if j not in gr[i]:
			group[j]=d
			v[j]=True
	d+=1
	if False in v:i=v.index(False)
	else:break
if False in v:print(-1)
else:
	a,b,c=group.count(1),group.count(2),group.count(3)
	if a*b+b*c+a*c==m:print(*group)
	else:print(-1)


def fill(graph):

    dp=[[True,True,True] for _ in range(n+1)]
    l=[-1 for i in range(n+1)]
    count1,count2,count3=0,0,0
    for i in graph:
        if dp[i][0]:
            #fill
            l[i],count1=1,count1+1
            for j in graph[i]:
                dp[j][0]=False
        elif dp[i][1]:
            #fill
            l[i]=2
            count2+=1
            for j in graph[i]:
                dp[j][1]=False
        elif dp[i][2]:
            #fill
            l[i]=3
            count3+=1
            for j in graph[i]:
                dp[j][2]=False
        else:
            return [-1]

    if count1==0 or count2==0 or count3==0:
        return [-1]
    return l
n,m=map(int,input().split())
from collections import defaultdict
graph=defaultdict(list)
for i in range(m):
    a,b=map(int,input().split())
    graph[a].append(b)
    graph[b].append(a)
k=fill(graph)
print(*k)


import sys
def fill(graph):

    dp=[[True,True,True] for _ in range(n+1)]
    l=[-1 for i in range(n+1)]
    from collections import defaultdict
    vis=defaultdict(int)
    count1,count2,count3=0,0,0
    for i in graph:
        if dp[i][0]:
            #fill
            l[i],count1=1,count1+1
            vis[i]=1
            for j in graph[i]:
                dp[j][0]=False
        elif dp[i][1]:
            #fill
            l[i]=2
            count2+=1
            vis[i]=2
            for j in graph[i]:
                dp[j][1]=False
        elif dp[i][2]:
            #fill
            l[i]=3
            count3+=1
            vis[i]=3
            for j in graph[i]:
                dp[j][2]=False
        else:
            return [-1]

    if count1==0 or count2==0 or count3==0:
        return [-1]
    l.pop(0)
    for i in l:
        if i==-1:
            return [-1]
    return l
n,m=map(int,sys.stdin.readline().split())
from collections import defaultdict
graph=defaultdict(list)
for i in range(m):
    a,b=map(int,sys.stdin.readline().split())
    graph[a].append(b)
    graph[b].append(a)
k=fill(graph)
print(*k)


import sys
def fill(graph,n):

    dp=[[True,True,True] for _ in range(n+1)]
    l=[-1 for i in range(n+1)]
    from collections import defaultdict
    vis=defaultdict(int)
    count1,count2,count3=0,0,0
    for i in graph:
        if dp[i][0]:
            #fill
            l[i],count1=1,count1+1
            vis[i]=1
            for j in graph[i]:
                dp[j][0]=False
        elif dp[i][1]:
            #fill
            l[i]=2
            count2+=1
            vis[i]=2
            for j in graph[i]:
                dp[j][1]=False
        elif dp[i][2]:
            #fill
            l[i]=3
            count3+=1
            vis[i]=3
            for j in graph[i]:
                dp[j][2]=False
        else:
            return [-1]

    if count1==0 or count2==0 or count3==0:
        return [-1]
    if count1+count2+count3!=n:
        return [-1]
    l.pop(0)
    for i in l:
        if i==-1:
            return [-1]
    return l
n,m=map(int,sys.stdin.readline().split())
from collections import defaultdict
graph=defaultdict(list)
for i in range(m):
    a,b=map(int,sys.stdin.readline().split())
    graph[a].append(b)
    graph[b].append(a)
k=fill(graph,n)
print(*k)


import sys
def fill(graph):

    dp=[[True,True,True] for _ in range(n+1)]
    l=[-1 for i in range(n+1)]
    from collections import defaultdict
    vis=defaultdict(int)
    count1,count2,count3=0,0,0
    for i in graph:
        if dp[i][0]:
            #fill
            l[i],count1=1,count1+1
            vis[i]=1
            for j in graph[i]:
                if vis[j]==1:
                    return [-1]
                dp[j][0]=False
        elif dp[i][1]:
            #fill
            l[i]=2
            count2+=1
            vis[i]=2
            for j in graph[i]:
                if vis[j]==2:
                    return [-1]
                dp[j][1]=False
        elif dp[i][2]:
            #fill
            l[i]=3
            count3+=1
            vis[i]=3
            for j in graph[i]:
                if vis[j]==3:
                    return [-1]
                dp[j][2]=False
        else:
            return [-1]

    if count1==0 or count2==0 or count3==0:
        return [-1]
    return l
n,m=map(int,sys.stdin.readline().split())
from collections import defaultdict
graph=defaultdict(list)
for i in range(m):
    a,b=map(int,sys.stdin.readline().split())
    graph[a].append(b)
    graph[b].append(a)
k=fill(graph)
print(*k)