Little Dima misbehaved during a math lesson a lot and the nasty teacher Mr. Pickles gave him the following problem as a punishment. Find all integer solutions x (0 < x < 109) of the equation:x = b·s(x)a + c,  where a, b, c are some predetermined constant values and function s(x) determines the sum of all digits in the decimal representation of number x.The teacher gives this problem to Dima for each lesson. He changes only the parameters of the equation: a, b, c. Dima got sick of getting bad marks and he asks you to help him solve this challenging problem.

The first line contains three space-separated integers: a, b, c (1 ≤ a ≤ 5; 1 ≤ b ≤ 10000;  - 10000 ≤ c ≤ 10000).

Print integer n — the number of the solutions that you've found. Next print n integers in the increasing order — the solutions of the given equation. Print only integer solutions that are larger than zero and strictly less than 109.

package main

import (
	"fmt"
	"sort"
)

func sum(x int64) int64 {
	res := int64(0)
	for x > 0 {
		res += x % 10
		x /= 10
	}
	return res
}

func main() {
	var b, c int64
	var a int
	fmt.Scan(&a, &b, &c)
	var res []int
	for s := int64(1); s <= 9*9; s++ {
		r := s
		for i := 1; i < a; i++ {
			r *= s
		}
		r = b*r + c
		if sum(r) == s && r < 1000000000 {
			res = append(res, int(r))
		}
	}

	sort.Ints(res)
	fmt.Println(len(res))
	for i, r := range res {
		fmt.Print(r)
		if i == len(res)-1 {
			fmt.Print("\n")
		} else {
			fmt.Print(" ")
		}
	}
}

package main

import (
	"fmt"
	"strconv"
	"strings"
)

// x = b·s(x)^a + c

func main() {
	const max_x int64 = 1000000000
	const max_digits int64 = 9

	var a, b, c int64
	if _, err := fmt.Scanf("%d %d %d\n", &a, &b, &c); err != nil {
		return
	}

	cnt := 0
	var all_res []string

	for i := int64(1); i <= 9*max_digits; i++ {
		y := b*power(i, a) + c
		if y < max_x && s(y) == i {
			cnt++
			all_res = append(all_res, strconv.FormatInt(y, 10))
		}
	}

	fmt.Println(cnt)
	fmt.Println(strings.Join(all_res, " "))
}

func s(x int64) int64 {
	res := int64(0)
	for x > 0 {
		res += x % 10
		x /= 10
	}
	return res
}

func power(sx, n int64) int64 {
	res := sx
	for i := int64(1); i < n; i++ {
		res *= sx
	}
	return res
}

package main

import "bufio"
import "os"
import "fmt"
import "math"

func Read(s *bufio.Reader) string {
    line, _ := s.ReadString('\n')
    return line
}

func Write(s *bufio.Writer, a ...interface{}) {
    fmt.Fprintln(s, a...)
    s.Flush()
}

func Sum(x int64) int64 {
    var result int64
    for x != 0 {
        result += int64(math.Mod(float64(x), 10.0))
        x = x / 10
    }

    return result
}

func main() {
    reader := bufio.NewReader(os.Stdin)

    var a, b, c int64
    line, _ := reader.ReadString(' ')
    fmt.Sscanln(line, &a)
    line, _ = reader.ReadString(' ')
    fmt.Sscanln(line, &b)
    fmt.Sscanln(Read(reader), &c)

    writer := bufio.NewWriter(os.Stdout)
    // s(x) can be from 1 to 81
    var result []int64
    for i := 1; i <= 81; i++ {
        right := b * int64(math.Pow(float64(i), float64(a))) + c
        if right < 1000000000 {
            left := Sum(right)

            if left == int64(i) {
                result = append(result, right)
            }
        }
    }

    Write(writer, len(result))
    for i := range result {
        fmt.Fprint(writer, result[i])
        fmt.Fprint(writer, " ")
    }

    writer.Flush()
}

package main

import (
	"bufio"
	"fmt"
	"os"
)

func S(x int64) int64 {
	var res int64 = 0
	for x != 0 {
		res += x % 10
		x /= 10
	}
	return res
}

func Pow(x, a int64) int64 {
	var res int64 = 1
	for i := 0; int64(i) < a; i++ {
		res *= x
	}
	return res
}

func main() {
	reader := bufio.NewReader(os.Stdin)
	writer := bufio.NewWriter(os.Stdout)
	var a, b, c int64
	fmt.Fscan(reader, &a, &b, &c)
	var num_res = 0
	var res []int64 = make([]int64, 40)
	for i := 1; i < 82; i++ {
		var x int64 = b*Pow(int64(i), a) + c
		if x < 1000000000 && S(x) == int64(i) {
			res[num_res] = x
			num_res++
		}
	}
	fmt.Fprintln(writer, num_res)
	for i := 0; i < num_res; i++ {
		fmt.Fprintf(writer, "%d ", res[i])
	}
	fmt.Fprintln(writer)
	writer.Flush()
}

package main

import (
	"fmt"
)

func pow(x, y int) uint64 {
	var ans uint64
	ans = 1
	for i := 0; i < y; i++ {
		ans *= uint64(x)
	}
	return ans
}

func calc_s(x uint64) uint64 {
	var ans uint64
	ans = 0
	for x != 0 {
		ans += x % 10
		x /= 10
	}
	return ans
}

func main() {
	var a, b, c int
	fmt.Scan(&a, &b, &c)

	ans := make([]int, 81, 81)
	i := 0
	for s := 1; s <= 81; s++ {
		var x uint64
		x = uint64(b)*pow(s, a) + uint64(c)
		if x < uint64(1000000000) && uint64(s) == calc_s(x) {
			ans[i] = int(x)
			i++
		}
	}

	fmt.Println(i)
	if i > 0 {
		for j := 0; j < i; j++ {
			fmt.Print(ans[j], " ")
		}
		fmt.Println()
	}
}

package main

import (
	"fmt"
	"sort"
)

func sum(x int64) int64 {
	res := int64(0)
	for x > 0 {
		res += x % 10
		x /= 10
	}
	return res
}

func main() {
	var b, c int64
	var a int
	fmt.Scan(&a, &b, &c)
	var res []int
	for s := int64(1); s <= 9*9; s++ {
		r := s
		for i := 1; i < a; i++ {
			r *= s
		}
		r = b*r + c
		if sum(r) == s {
			res = append(res, int(r))
		}
	}

	sort.Ints(res)
	fmt.Println(len(res))
	for i, r := range res {
		fmt.Print(r)
		if i == len(res)-1 {
			fmt.Print("\n")
		} else {
			fmt.Print(" ")
		}
	}
}

package main

import (
	"fmt"
	"strconv"
	"strings"
)

// x = b·s(x)^a + c

func main() {
	const max_digits int64 = 9

	var a, b, c int64
	if _, err := fmt.Scanf("%d %d %d\n", &a, &b, &c); err != nil {
		return
	}

	cnt := 0
	var all_res []string

	for i := int64(1); i < max_digits*max_digits; i++ {
		y := b*power(i, a) + c
		if s(y) == i {
			cnt++
			all_res = append(all_res, strconv.FormatInt(y, 10))
		}
	}

	fmt.Println(cnt)
	fmt.Println(strings.Join(all_res, " "))
}

func s(x int64) int64 {
	res := int64(0)
	for x > 0 {
		res += x % 10
		x /= 10
	}
	return res
}

func power(sx, n int64) int64 {
	res := sx
	for i := int64(1); i < n; i++ {
		res *= sx
	}
	return res
}

package main

import "bufio"
import "os"
import "fmt"
import "math"

func Read(s *bufio.Reader) string {
    line, _ := s.ReadString('\n')
    return line
}

func Write(s *bufio.Writer, a ...interface{}) {
    fmt.Fprintln(s, a...)
    s.Flush()
}

func Sum(x int) int {
    var result int
    for x != 0 {
        result += int(math.Mod(float64(x), 10.0))
        x = x / 10
    }

    return result
}

func main() {
    reader := bufio.NewReader(os.Stdin)

    var a, b, c int
    line, _ := reader.ReadString(' ')
    fmt.Sscanln(line, &a)
    line, _ = reader.ReadString(' ')
    fmt.Sscanln(line, &b)
    fmt.Sscanln(Read(reader), &c)

    // s(x) can be from 1 to 81
    var result []int
    for i := 1; i <= 81; i++ {
        right := b * int(math.Pow(float64(i), float64(a))) + c
        left := Sum(right)

        if left == i {
            result = append(result, right)
        }
    }

    writer := bufio.NewWriter(os.Stdout)
    Write(writer, len(result))
    for i := range result {
        fmt.Fprint(writer, result[i])
        fmt.Fprint(writer, " ")
    }

    writer.Flush()
}

package main

import "bufio"
import "os"
import "fmt"
import "math"

func Read(s *bufio.Reader) string {
    line, _ := s.ReadString('\n')
    return line
}

func Write(s *bufio.Writer, a ...interface{}) {
    fmt.Fprintln(s, a...)
    s.Flush()
}

func Sum(x int) int {
    var result int
    for x != 0 {
        result += int(math.Mod(float64(x), 10.0))
        x = x / 10
    }

    return result
}

func main() {
    reader := bufio.NewReader(os.Stdin)

    var a, b, c int
    line, _ := reader.ReadString(' ')
    fmt.Sscanln(line, &a)
    line, _ = reader.ReadString(' ')
    fmt.Sscanln(line, &b)
    fmt.Sscanln(Read(reader), &c)

    // s(x) can be from 1 to 81
    var result []int
    for i := 1; i <= 81; i++ {
        right := b * int(math.Pow(float64(i), float64(a))) + c
        if right < 1000000000 {
            left := Sum(right)

            if left == i {
                result = append(result, right)
            }
        }
    }

    writer := bufio.NewWriter(os.Stdout)
    Write(writer, len(result))
    for i := range result {
        fmt.Fprint(writer, result[i])
        fmt.Fprint(writer, " ")
    }

    writer.Flush()
}

package main

import "bufio"
import "os"
import "fmt"
import "math"

func Read(s *bufio.Reader) string {
    line, _ := s.ReadString('\n')
    return line
}

func Write(s *bufio.Writer, a ...interface{}) {
    fmt.Fprintln(s, a...)
    s.Flush()
}

func Sum(x int64) int64 {
    var result int64
    for x != 0 {
        result += int64(math.Mod(float64(x), 10.0))
        x = x / 10
    }

    return result
}

func main() {
    reader := bufio.NewReader(os.Stdin)

    var a, b, c int64
    line, _ := reader.ReadString(' ')
    fmt.Sscanln(line, &a)
    line, _ = reader.ReadString(' ')
    fmt.Sscanln(line, &b)
    fmt.Sscanln(Read(reader), &c)

    writer := bufio.NewWriter(os.Stdout)
    // s(x) can be from 1 to 81
    var result []int64
    for i := 1; i <= 81; i++ {
        right := b * int64(math.Pow(float64(i), float64(a))) + c
        if right < 1000000000 {
            left := Sum(right)

            if left == int64(i) {
                Write(writer, left, right)
                result = append(result, right)
            }
        }
    }

    Write(writer, len(result))
    for i := range result {
        fmt.Fprint(writer, result[i])
        fmt.Fprint(writer, " ")
    }

    writer.Flush()
}

package main

import (
	"bufio"
	"fmt"
	"os"
)

func S(x int64) int64 {
	var res int64 = 0
	for x != 0 {
		res += x % 10
		x /= 10
	}
	return res
}

func Pow(x, a int64) int64 {
	var res int64 = 1
	for i := 0; int64(i) < a; i++ {
		res *= x
	}
	return res
}

func main() {
	reader := bufio.NewReader(os.Stdin)
	writer := bufio.NewWriter(os.Stdout)
	var a, b, c int64
	fmt.Fscan(reader, &a, &b, &c)
	var num_res = 0
	var res []int64 = make([]int64, 40)
	for i := 0; i < 40; i++ {
		var x int64 = b*Pow(int64(i), a) + c
		if x < 1000000000 && S(x) == int64(i) {
			res[num_res] = x
			num_res++
		}
	}
	fmt.Fprintln(writer, num_res)
	for i := 0; i < num_res; i++ {
		fmt.Fprintf(writer, "%d ", res[i])
	}
	fmt.Fprintln(writer)
	writer.Flush()
}

package main

import (
	"bufio"
	"fmt"
	"os"
)

func S(x int64) int64 {
	var res int64 = 0
	for x != 0 {
		res += x % 10
		x /= 10
	}
	return res
}

func Pow(x, a int64) int64 {
	var res int64 = 1
	for i := 0; int64(i) < a; i++ {
		res *= x
	}
	return res
}

func main() {
	reader := bufio.NewReader(os.Stdin)
	writer := bufio.NewWriter(os.Stdout)
	var a, b, c int64
	fmt.Fscan(reader, &a, &b, &c)
	var num_res = 0
	var res []int64 = make([]int64, 40)
	for i := 1; i < 40; i++ {
		var x int64 = b*Pow(int64(i), a) + c
		if x < 1000000000 && S(x) == int64(i) {
			res[num_res] = x
			num_res++
		}
	}
	fmt.Fprintln(writer, num_res)
	for i := 0; i < num_res; i++ {
		fmt.Fprintf(writer, "%d ", res[i])
	}
	fmt.Fprintln(writer)
	writer.Flush()
}


Anadi has a set of dominoes. Every domino has two parts, and each part contains some dots. For every $$$a$$$ and $$$b$$$ such that $$$1 \leq a \leq b \leq 6$$$, there is exactly one domino with $$$a$$$ dots on one half and $$$b$$$ dots on the other half. The set contains exactly $$$21$$$ dominoes. Here is an exact illustration of his set:  Also, Anadi has an undirected graph without self-loops and multiple edges. He wants to choose some dominoes and place them on the edges of this graph. He can use at most one domino of each type. Each edge can fit at most one domino. It's not necessary to place a domino on each edge of the graph.When placing a domino on an edge, he also chooses its direction. In other words, one half of any placed domino must be directed toward one of the endpoints of the edge and the other half must be directed toward the other endpoint. There's a catch: if there are multiple halves of dominoes directed toward the same vertex, each of these halves must contain the same number of dots.How many dominoes at most can Anadi place on the edges of his graph?

The first line contains two integers $$$n$$$ and $$$m$$$ ($$$1 \leq n \leq 7$$$, $$$0 \leq m \leq \frac{n\cdot(n-1)}{2}$$$) — the number of vertices and the number of edges in the graph. The next $$$m$$$ lines contain two integers each. Integers in the $$$i$$$-th line are $$$a_i$$$ and $$$b_i$$$ ($$$1 \leq a, b \leq n$$$, $$$a \neq b$$$) and denote that there is an edge which connects vertices $$$a_i$$$ and $$$b_i$$$. The graph might be disconnected. It's however guaranteed that the graph doesn't contain any self-loops, and that there is at most one edge between any pair of vertices.

Output one integer which denotes the maximum number of dominoes which Anadi can place on the edges of the graph.

NoteHere is an illustration of Anadi's graph from the first sample test:  And here is one of the ways to place a domino on each of its edges:  Note that each vertex is faced by the halves of dominoes with the same number of dots. For instance, all halves directed toward vertex $$$1$$$ have three dots.

package main

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"math"
	"os"
	"strconv"
)

/*********** I/O ***********/

var (
	// ReadString returns a WORD string.
	ReadString func() string
	stdout     *bufio.Writer
)

func init() {
	ReadString = newReadString(os.Stdin)
	stdout = bufio.NewWriter(os.Stdout)
}

func newReadString(ior io.Reader) func() string {
	r := bufio.NewScanner(ior)
	// r.Buffer(make([]byte, 1024), int(1e+11)) // for AtCoder
	r.Buffer(make([]byte, 1024), int(1e+9)) // for Codeforces
	// Split sets the split function for the Scanner. The default split function is ScanLines.
	// Split panics if it is called after scanning has started.
	r.Split(bufio.ScanWords)

	return func() string {
		if !r.Scan() {
			panic("Scan failed")
		}
		return r.Text()
	}
}

// ReadInt returns an integer.
func ReadInt() int {
	return int(readInt64())
}
func ReadInt2() (int, int) {
	return int(readInt64()), int(readInt64())
}
func ReadInt3() (int, int, int) {
	return int(readInt64()), int(readInt64()), int(readInt64())
}
func ReadInt4() (int, int, int, int) {
	return int(readInt64()), int(readInt64()), int(readInt64()), int(readInt64())
}

// ReadInt64 returns as integer as int64.
func ReadInt64() int64 {
	return readInt64()
}
func ReadInt64_2() (int64, int64) {
	return readInt64(), readInt64()
}
func ReadInt64_3() (int64, int64, int64) {
	return readInt64(), readInt64(), readInt64()
}
func ReadInt64_4() (int64, int64, int64, int64) {
	return readInt64(), readInt64(), readInt64(), readInt64()
}

func readInt64() int64 {
	i, err := strconv.ParseInt(ReadString(), 0, 64)
	if err != nil {
		panic(err.Error())
	}
	return i
}

// ReadIntSlice returns an integer slice that has n integers.
func ReadIntSlice(n int) []int {
	b := make([]int, n)
	for i := 0; i < n; i++ {
		b[i] = ReadInt()
	}
	return b
}

// ReadInt64Slice returns as int64 slice that has n integers.
func ReadInt64Slice(n int) []int64 {
	b := make([]int64, n)
	for i := 0; i < n; i++ {
		b[i] = ReadInt64()
	}
	return b
}

// ReadFloat64 returns an float64.
func ReadFloat64() float64 {
	return float64(readFloat64())
}

func readFloat64() float64 {
	f, err := strconv.ParseFloat(ReadString(), 64)
	if err != nil {
		panic(err.Error())
	}
	return f
}

// ReadFloatSlice returns an float64 slice that has n float64.
func ReadFloat64Slice(n int) []float64 {
	b := make([]float64, n)
	for i := 0; i < n; i++ {
		b[i] = ReadFloat64()
	}
	return b
}

// ReadRuneSlice returns a rune slice.
func ReadRuneSlice() []rune {
	return []rune(ReadString())
}

/*********** Debugging ***********/

// ZeroPaddingRuneSlice returns binary expressions of integer n with zero padding.
// For debugging use.
func ZeroPaddingRuneSlice(n, digitsNum int) []rune {
	sn := fmt.Sprintf("%b", n)

	residualLength := digitsNum - len(sn)
	if residualLength <= 0 {
		return []rune(sn)
	}

	zeros := make([]rune, residualLength)
	for i := 0; i < len(zeros); i++ {
		zeros[i] = '0'
	}

	res := []rune{}
	res = append(res, zeros...)
	res = append(res, []rune(sn)...)

	return res
}

/*********** DP sub-functions ***********/

// ChMin accepts a pointer of integer and a target value.
// If target value is SMALLER than the first argument,
//	then the first argument will be updated by the second argument.
func ChMin(updatedValue *int, target int) bool {
	if *updatedValue > target {
		*updatedValue = target
		return true
	}
	return false
}

// ChMax accepts a pointer of integer and a target value.
// If target value is LARGER than the first argument,
//	then the first argument will be updated by the second argument.
func ChMax(updatedValue *int, target int) bool {
	if *updatedValue < target {
		*updatedValue = target
		return true
	}
	return false
}

// NthBit returns nth bit value of an argument.
// n starts from 0.
func NthBit(num, nth int) int {
	return num >> uint(nth) & 1
}

// OnBit returns the integer that has nth ON bit.
// If an argument has nth ON bit, OnBit returns the argument.
func OnBit(num, nth int) int {
	return num | (1 << uint(nth))
}

// OffBit returns the integer that has nth OFF bit.
// If an argument has nth OFF bit, OffBit returns the argument.
func OffBit(num, nth int) int {
	return num & ^(1 << uint(nth))
}

// PopCount returns the number of ON bit of an argument.
func PopCount(num int) int {
	res := 0

	for i := 0; i < 70; i++ {
		if ((num >> uint(i)) & 1) == 1 {
			res++
		}
	}

	return res
}

/*********** Arithmetic ***********/

// Max returns the max integer among input set.
// This function needs at least 1 argument (no argument causes panic).
func Max(integers ...int) int {
	m := integers[0]
	for i, integer := range integers {
		if i == 0 {
			continue
		}
		if m < integer {
			m = integer
		}
	}
	return m
}

// Min returns the min integer among input set.
// This function needs at least 1 argument (no argument causes panic).
func Min(integers ...int) int {
	m := integers[0]
	for i, integer := range integers {
		if i == 0 {
			continue
		}
		if m > integer {
			m = integer
		}
	}
	return m
}

// DigitSum returns digit sum of a decimal number.
// DigitSum only accept a positive integer.
func DigitSum(n int) int {
	if n < 0 {
		return -1
	}

	res := 0

	for n > 0 {
		res += n % 10
		n /= 10
	}

	return res
}

// DigitNumOfDecimal returns digits number of n.
// n is non negative number.
func DigitNumOfDecimal(n int) int {
	res := 0

	for n > 0 {
		n /= 10
		res++
	}

	return res
}

// Sum returns multiple integers sum.
func Sum(integers ...int) int {
	s := 0

	for _, i := range integers {
		s += i
	}

	return s
}

// Kiriage returns Ceil(a/b)
// a >= 0, b > 0
func Kiriage(a, b int) int {
	return (a + (b - 1)) / b
}

// PowInt is integer version of math.Pow
// PowInt calculate a power by Binary Power (二分累乗法(O(log e))).
func PowInt(a, e int) int {
	if a < 0 || e < 0 {
		panic(errors.New("[argument error]: PowInt does not accept negative integers"))
	}

	if e == 0 {
		return 1
	}

	if e%2 == 0 {
		halfE := e / 2
		half := PowInt(a, halfE)
		return half * half
	}

	return a * PowInt(a, e-1)
}

// AbsInt is integer version of math.Abs
func AbsInt(a int) int {
	if a < 0 {
		return -a
	}
	return a
}

// Gcd returns the Greatest Common Divisor of two natural numbers.
// Gcd only accepts two natural numbers (a, b >= 1).
// 0 or negative number causes panic.
// Gcd uses the Euclidean Algorithm.
func Gcd(a, b int) int {
	if a <= 0 || b <= 0 {
		panic(errors.New("[argument error]: Gcd only accepts two NATURAL numbers"))
	}
	if a < b {
		a, b = b, a
	}

	// Euclidean Algorithm
	for b > 0 {
		div := a % b
		a, b = b, div
	}

	return a
}

// Lcm returns the Least Common Multiple of two natural numbers.
// Lcd only accepts two natural numbers (a, b >= 1).
// 0 or negative number causes panic.
// Lcd uses the Euclidean Algorithm indirectly.
func Lcm(a, b int) int {
	if a <= 0 || b <= 0 {
		panic(errors.New("[argument error]: Gcd only accepts two NATURAL numbers"))
	}

	// a = a'*gcd, b = b'*gcd, a*b = a'*b'*gcd^2
	// a' and b' are relatively prime numbers
	// gcd consists of prime numbers, that are included in a and b
	gcd := Gcd(a, b)

	// not (a * b / gcd), because of reducing a probability of overflow
	return (a / gcd) * b
}

// Strtoi is a wrapper of `strconv.Atoi()`.
// If `strconv.Atoi()` returns an error, Strtoi calls panic.
func Strtoi(s string) int {
	if i, err := strconv.Atoi(s); err != nil {
		panic(errors.New("[argument error]: Strtoi only accepts integer string"))
	} else {
		return i
	}
}

// PrintIntsLine returns integers string delimited by a space.
func PrintIntsLine(A ...int) string {
	res := []rune{}

	for i := 0; i < len(A); i++ {
		str := strconv.Itoa(A[i])
		res = append(res, []rune(str)...)

		if i != len(A)-1 {
			res = append(res, ' ')
		}
	}

	return string(res)
}

/********** I/O usage **********/

//str := ReadString()
//i := ReadInt()
//X := ReadIntSlice(n)
//S := ReadRuneSlice()
//a := ReadFloat64()
//A := ReadFloat64Slice(n)

//str := ZeroPaddingRuneSlice(num, 32)
//str := PrintIntsLine(X...)

/*******************************************************************/

const MOD = 1000000000 + 7
const ALPHABET_NUM = 26
const INF_INT64 = math.MaxInt64
const INF_BIT60 = 1 << 60

var n, m int

var adjMatrix [10][10]int
var dominoMatrix [10][10]int

func main() {
	n, m = ReadInt2()
	for i := 0; i < m; i++ {
		a, b := ReadInt2()
		// 常にaをbより小さくする
		if a > b {
			a, b = b, a
		}
		adjMatrix[a][b] = 1
		adjMatrix[b][a] = 1
	}

	if n <= 6 {
		fmt.Println(m)
	} else {
		fmt.Println(sub())
	}
}

func sub() int {
	res := 0

	// i, jを同じ数字とする
	for i := 1; i <= n; i++ {
		for j := i + 1; j <= n; j++ {
			// ノードにサイコロの目を割り振る
			memo := make(map[int]int)
			dice := 1
			for k := 1; k <= n; k++ {
				if k == j {
					continue
				}
				memo[k] = dice
				dice++
			}
			memo[j] = memo[i]

			tmp := 0
			initialize()
			for l := 1; l <= n; l++ {
				for m := l + 1; m <= n; m++ {
					ll, mm := memo[l], memo[m]
					if ll > mm {
						ll, mm = mm, ll
					}
					if adjMatrix[l][m] == 1 && dominoMatrix[ll][mm] == 1 {
						tmp++
						dominoMatrix[ll][mm] = 0
					}
				}
			}

			ChMax(&res, tmp)
		}
	}

	return res
}

func initialize() {
	for i := 1; i <= 6; i++ {
		dominoMatrix[i][i] = 1
		for j := i + 1; j <= 6; j++ {
			dominoMatrix[i][j] = 1
		}
	}
}

// MODはとったか？
// 遷移だけじゃなくて最後の最後でちゃんと取れよ？

/*******************************************************************/

package main

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"math"
	"os"
	"strconv"
)

/*********** I/O ***********/

var (
	// ReadString returns a WORD string.
	ReadString func() string
	stdout     *bufio.Writer
)

func init() {
	ReadString = newReadString(os.Stdin)
	stdout = bufio.NewWriter(os.Stdout)
}

func newReadString(ior io.Reader) func() string {
	r := bufio.NewScanner(ior)
	// r.Buffer(make([]byte, 1024), int(1e+11)) // for AtCoder
	r.Buffer(make([]byte, 1024), int(1e+9)) // for Codeforces
	// Split sets the split function for the Scanner. The default split function is ScanLines.
	// Split panics if it is called after scanning has started.
	r.Split(bufio.ScanWords)

	return func() string {
		if !r.Scan() {
			panic("Scan failed")
		}
		return r.Text()
	}
}

// ReadInt returns an integer.
func ReadInt() int {
	return int(readInt64())
}
func ReadInt2() (int, int) {
	return int(readInt64()), int(readInt64())
}
func ReadInt3() (int, int, int) {
	return int(readInt64()), int(readInt64()), int(readInt64())
}
func ReadInt4() (int, int, int, int) {
	return int(readInt64()), int(readInt64()), int(readInt64()), int(readInt64())
}

// ReadInt64 returns as integer as int64.
func ReadInt64() int64 {
	return readInt64()
}
func ReadInt64_2() (int64, int64) {
	return readInt64(), readInt64()
}
func ReadInt64_3() (int64, int64, int64) {
	return readInt64(), readInt64(), readInt64()
}
func ReadInt64_4() (int64, int64, int64, int64) {
	return readInt64(), readInt64(), readInt64(), readInt64()
}

func readInt64() int64 {
	i, err := strconv.ParseInt(ReadString(), 0, 64)
	if err != nil {
		panic(err.Error())
	}
	return i
}

// ReadIntSlice returns an integer slice that has n integers.
func ReadIntSlice(n int) []int {
	b := make([]int, n)
	for i := 0; i < n; i++ {
		b[i] = ReadInt()
	}
	return b
}

// ReadInt64Slice returns as int64 slice that has n integers.
func ReadInt64Slice(n int) []int64 {
	b := make([]int64, n)
	for i := 0; i < n; i++ {
		b[i] = ReadInt64()
	}
	return b
}

// ReadFloat64 returns an float64.
func ReadFloat64() float64 {
	return float64(readFloat64())
}

func readFloat64() float64 {
	f, err := strconv.ParseFloat(ReadString(), 64)
	if err != nil {
		panic(err.Error())
	}
	return f
}

// ReadFloatSlice returns an float64 slice that has n float64.
func ReadFloat64Slice(n int) []float64 {
	b := make([]float64, n)
	for i := 0; i < n; i++ {
		b[i] = ReadFloat64()
	}
	return b
}

// ReadRuneSlice returns a rune slice.
func ReadRuneSlice() []rune {
	return []rune(ReadString())
}

/*********** Debugging ***********/

// ZeroPaddingRuneSlice returns binary expressions of integer n with zero padding.
// For debugging use.
func ZeroPaddingRuneSlice(n, digitsNum int) []rune {
	sn := fmt.Sprintf("%b", n)

	residualLength := digitsNum - len(sn)
	if residualLength <= 0 {
		return []rune(sn)
	}

	zeros := make([]rune, residualLength)
	for i := 0; i < len(zeros); i++ {
		zeros[i] = '0'
	}

	res := []rune{}
	res = append(res, zeros...)
	res = append(res, []rune(sn)...)

	return res
}

/*********** DP sub-functions ***********/

// ChMin accepts a pointer of integer and a target value.
// If target value is SMALLER than the first argument,
//	then the first argument will be updated by the second argument.
func ChMin(updatedValue *int, target int) bool {
	if *updatedValue > target {
		*updatedValue = target
		return true
	}
	return false
}

// ChMax accepts a pointer of integer and a target value.
// If target value is LARGER than the first argument,
//	then the first argument will be updated by the second argument.
func ChMax(updatedValue *int, target int) bool {
	if *updatedValue < target {
		*updatedValue = target
		return true
	}
	return false
}

// NthBit returns nth bit value of an argument.
// n starts from 0.
func NthBit(num, nth int) int {
	return num >> uint(nth) & 1
}

// OnBit returns the integer that has nth ON bit.
// If an argument has nth ON bit, OnBit returns the argument.
func OnBit(num, nth int) int {
	return num | (1 << uint(nth))
}

// OffBit returns the integer that has nth OFF bit.
// If an argument has nth OFF bit, OffBit returns the argument.
func OffBit(num, nth int) int {
	return num & ^(1 << uint(nth))
}

// PopCount returns the number of ON bit of an argument.
func PopCount(num int) int {
	res := 0

	for i := 0; i < 70; i++ {
		if ((num >> uint(i)) & 1) == 1 {
			res++
		}
	}

	return res
}

/*********** Arithmetic ***********/

// Max returns the max integer among input set.
// This function needs at least 1 argument (no argument causes panic).
func Max(integers ...int) int {
	m := integers[0]
	for i, integer := range integers {
		if i == 0 {
			continue
		}
		if m < integer {
			m = integer
		}
	}
	return m
}

// Min returns the min integer among input set.
// This function needs at least 1 argument (no argument causes panic).
func Min(integers ...int) int {
	m := integers[0]
	for i, integer := range integers {
		if i == 0 {
			continue
		}
		if m > integer {
			m = integer
		}
	}
	return m
}

// DigitSum returns digit sum of a decimal number.
// DigitSum only accept a positive integer.
func DigitSum(n int) int {
	if n < 0 {
		return -1
	}

	res := 0

	for n > 0 {
		res += n % 10
		n /= 10
	}

	return res
}

// DigitNumOfDecimal returns digits number of n.
// n is non negative number.
func DigitNumOfDecimal(n int) int {
	res := 0

	for n > 0 {
		n /= 10
		res++
	}

	return res
}

// Sum returns multiple integers sum.
func Sum(integers ...int) int {
	s := 0

	for _, i := range integers {
		s += i
	}

	return s
}

// Kiriage returns Ceil(a/b)
// a >= 0, b > 0
func Kiriage(a, b int) int {
	return (a + (b - 1)) / b
}

// PowInt is integer version of math.Pow
// PowInt calculate a power by Binary Power (二分累乗法(O(log e))).
func PowInt(a, e int) int {
	if a < 0 || e < 0 {
		panic(errors.New("[argument error]: PowInt does not accept negative integers"))
	}

	if e == 0 {
		return 1
	}

	if e%2 == 0 {
		halfE := e / 2
		half := PowInt(a, halfE)
		return half * half
	}

	return a * PowInt(a, e-1)
}

// AbsInt is integer version of math.Abs
func AbsInt(a int) int {
	if a < 0 {
		return -a
	}
	return a
}

// Gcd returns the Greatest Common Divisor of two natural numbers.
// Gcd only accepts two natural numbers (a, b >= 1).
// 0 or negative number causes panic.
// Gcd uses the Euclidean Algorithm.
func Gcd(a, b int) int {
	if a <= 0 || b <= 0 {
		panic(errors.New("[argument error]: Gcd only accepts two NATURAL numbers"))
	}
	if a < b {
		a, b = b, a
	}

	// Euclidean Algorithm
	for b > 0 {
		div := a % b
		a, b = b, div
	}

	return a
}

// Lcm returns the Least Common Multiple of two natural numbers.
// Lcd only accepts two natural numbers (a, b >= 1).
// 0 or negative number causes panic.
// Lcd uses the Euclidean Algorithm indirectly.
func Lcm(a, b int) int {
	if a <= 0 || b <= 0 {
		panic(errors.New("[argument error]: Gcd only accepts two NATURAL numbers"))
	}

	// a = a'*gcd, b = b'*gcd, a*b = a'*b'*gcd^2
	// a' and b' are relatively prime numbers
	// gcd consists of prime numbers, that are included in a and b
	gcd := Gcd(a, b)

	// not (a * b / gcd), because of reducing a probability of overflow
	return (a / gcd) * b
}

// Strtoi is a wrapper of `strconv.Atoi()`.
// If `strconv.Atoi()` returns an error, Strtoi calls panic.
func Strtoi(s string) int {
	if i, err := strconv.Atoi(s); err != nil {
		panic(errors.New("[argument error]: Strtoi only accepts integer string"))
	} else {
		return i
	}
}

// PrintIntsLine returns integers string delimited by a space.
func PrintIntsLine(A ...int) string {
	res := []rune{}

	for i := 0; i < len(A); i++ {
		str := strconv.Itoa(A[i])
		// str := strconv.FormatInt(A[i], 10)  // 64bit int version
		res = append(res, []rune(str)...)

		if i != len(A)-1 {
			res = append(res, ' ')
		}
	}

	return string(res)
}

/********** FAU standard libraries **********/

//fmt.Sprintf("%b\n", 255) 	// binary expression

/********** I/O usage **********/

//str := ReadString()
//i := ReadInt()
//X := ReadIntSlice(n)
//S := ReadRuneSlice()
//a := ReadFloat64()
//A := ReadFloat64Slice(n)

//str := ZeroPaddingRuneSlice(num, 32)
//str := PrintIntsLine(X...)

/*******************************************************************/

const MOD = 1000000000 + 7
const ALPHABET_NUM = 26
const INF_INT64 = math.MaxInt64
const INF_BIT60 = 1 << 60

var n, m int

var ans int
var edges []Edge

type Edge struct {
	s, t int
}

func main() {
	n, m = ReadInt2()
	for i := 0; i < m; i++ {
		s, t := ReadInt2()
		s--
		t--
		edges = append(edges, Edge{s, t})
	}

	v := []int{}
	dfs(v)
	fmt.Println(ans)
}

func dfs(v []int) {
	if len(v) == n {
		A := [6][6]int{}
		for i := 0; i < 6; i++ {
			for j := 0; j < 6; j++ {
				A[i][j] = 1
			}
		}

		cnt := 0
		for _, e := range edges {
			l, r := v[e.s], v[e.t]
			if A[l][r] == 1 {
				A[l][r], A[r][l] = 0, 0
				cnt++
			}
		}
		ChMax(&ans, cnt)
	} else {
		for i := 0; i <= 5; i++ {
			tmp := CopySlice(v)
			tmp = append(tmp, i)
			dfs(tmp)
		}
	}
}

func CopySlice(v []int) []int {
	res := make([]int, len(v))
	for i := 0; i < len(v); i++ {
		res[i] = v[i]
	}
	return res
}

// MODはとったか？
// 遷移だけじゃなくて最後の最後でちゃんと取れよ？

/*******************************************************************/

package main

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"math"
	"os"
	"strconv"
)

/*********** I/O ***********/

var (
	// ReadString returns a WORD string.
	ReadString func() string
	stdout     *bufio.Writer
)

func init() {
	ReadString = newReadString(os.Stdin)
	stdout = bufio.NewWriter(os.Stdout)
}

func newReadString(ior io.Reader) func() string {
	r := bufio.NewScanner(ior)
	// r.Buffer(make([]byte, 1024), int(1e+11)) // for AtCoder
	r.Buffer(make([]byte, 1024), int(1e+9)) // for Codeforces
	// Split sets the split function for the Scanner. The default split function is ScanLines.
	// Split panics if it is called after scanning has started.
	r.Split(bufio.ScanWords)

	return func() string {
		if !r.Scan() {
			panic("Scan failed")
		}
		return r.Text()
	}
}

// ReadInt returns an integer.
func ReadInt() int {
	return int(readInt64())
}
func ReadInt2() (int, int) {
	return int(readInt64()), int(readInt64())
}
func ReadInt3() (int, int, int) {
	return int(readInt64()), int(readInt64()), int(readInt64())
}
func ReadInt4() (int, int, int, int) {
	return int(readInt64()), int(readInt64()), int(readInt64()), int(readInt64())
}

// ReadInt64 returns as integer as int64.
func ReadInt64() int64 {
	return readInt64()
}
func ReadInt64_2() (int64, int64) {
	return readInt64(), readInt64()
}
func ReadInt64_3() (int64, int64, int64) {
	return readInt64(), readInt64(), readInt64()
}
func ReadInt64_4() (int64, int64, int64, int64) {
	return readInt64(), readInt64(), readInt64(), readInt64()
}

func readInt64() int64 {
	i, err := strconv.ParseInt(ReadString(), 0, 64)
	if err != nil {
		panic(err.Error())
	}
	return i
}

// ReadIntSlice returns an integer slice that has n integers.
func ReadIntSlice(n int) []int {
	b := make([]int, n)
	for i := 0; i < n; i++ {
		b[i] = ReadInt()
	}
	return b
}

// ReadInt64Slice returns as int64 slice that has n integers.
func ReadInt64Slice(n int) []int64 {
	b := make([]int64, n)
	for i := 0; i < n; i++ {
		b[i] = ReadInt64()
	}
	return b
}

// ReadFloat64 returns an float64.
func ReadFloat64() float64 {
	return float64(readFloat64())
}

func readFloat64() float64 {
	f, err := strconv.ParseFloat(ReadString(), 64)
	if err != nil {
		panic(err.Error())
	}
	return f
}

// ReadFloatSlice returns an float64 slice that has n float64.
func ReadFloat64Slice(n int) []float64 {
	b := make([]float64, n)
	for i := 0; i < n; i++ {
		b[i] = ReadFloat64()
	}
	return b
}

// ReadRuneSlice returns a rune slice.
func ReadRuneSlice() []rune {
	return []rune(ReadString())
}

/*********** Debugging ***********/

// ZeroPaddingRuneSlice returns binary expressions of integer n with zero padding.
// For debugging use.
func ZeroPaddingRuneSlice(n, digitsNum int) []rune {
	sn := fmt.Sprintf("%b", n)

	residualLength := digitsNum - len(sn)
	if residualLength <= 0 {
		return []rune(sn)
	}

	zeros := make([]rune, residualLength)
	for i := 0; i < len(zeros); i++ {
		zeros[i] = '0'
	}

	res := []rune{}
	res = append(res, zeros...)
	res = append(res, []rune(sn)...)

	return res
}

/*********** DP sub-functions ***********/

// ChMin accepts a pointer of integer and a target value.
// If target value is SMALLER than the first argument,
//	then the first argument will be updated by the second argument.
func ChMin(updatedValue *int, target int) bool {
	if *updatedValue > target {
		*updatedValue = target
		return true
	}
	return false
}

// ChMax accepts a pointer of integer and a target value.
// If target value is LARGER than the first argument,
//	then the first argument will be updated by the second argument.
func ChMax(updatedValue *int, target int) bool {
	if *updatedValue < target {
		*updatedValue = target
		return true
	}
	return false
}

// NthBit returns nth bit value of an argument.
// n starts from 0.
func NthBit(num, nth int) int {
	return num >> uint(nth) & 1
}

// OnBit returns the integer that has nth ON bit.
// If an argument has nth ON bit, OnBit returns the argument.
func OnBit(num, nth int) int {
	return num | (1 << uint(nth))
}

// OffBit returns the integer that has nth OFF bit.
// If an argument has nth OFF bit, OffBit returns the argument.
func OffBit(num, nth int) int {
	return num & ^(1 << uint(nth))
}

// PopCount returns the number of ON bit of an argument.
func PopCount(num int) int {
	res := 0

	for i := 0; i < 70; i++ {
		if ((num >> uint(i)) & 1) == 1 {
			res++
		}
	}

	return res
}

/*********** Arithmetic ***********/

// Max returns the max integer among input set.
// This function needs at least 1 argument (no argument causes panic).
func Max(integers ...int) int {
	m := integers[0]
	for i, integer := range integers {
		if i == 0 {
			continue
		}
		if m < integer {
			m = integer
		}
	}
	return m
}

// Min returns the min integer among input set.
// This function needs at least 1 argument (no argument causes panic).
func Min(integers ...int) int {
	m := integers[0]
	for i, integer := range integers {
		if i == 0 {
			continue
		}
		if m > integer {
			m = integer
		}
	}
	return m
}

// DigitSum returns digit sum of a decimal number.
// DigitSum only accept a positive integer.
func DigitSum(n int) int {
	if n < 0 {
		return -1
	}

	res := 0

	for n > 0 {
		res += n % 10
		n /= 10
	}

	return res
}

// DigitNumOfDecimal returns digits number of n.
// n is non negative number.
func DigitNumOfDecimal(n int) int {
	res := 0

	for n > 0 {
		n /= 10
		res++
	}

	return res
}

// Sum returns multiple integers sum.
func Sum(integers ...int) int {
	s := 0

	for _, i := range integers {
		s += i
	}

	return s
}

// Kiriage returns Ceil(a/b)
// a >= 0, b > 0
func Kiriage(a, b int) int {
	return (a + (b - 1)) / b
}

// PowInt is integer version of math.Pow
// PowInt calculate a power by Binary Power (二分累乗法(O(log e))).
func PowInt(a, e int) int {
	if a < 0 || e < 0 {
		panic(errors.New("[argument error]: PowInt does not accept negative integers"))
	}

	if e == 0 {
		return 1
	}

	if e%2 == 0 {
		halfE := e / 2
		half := PowInt(a, halfE)
		return half * half
	}

	return a * PowInt(a, e-1)
}

// AbsInt is integer version of math.Abs
func AbsInt(a int) int {
	if a < 0 {
		return -a
	}
	return a
}

// Gcd returns the Greatest Common Divisor of two natural numbers.
// Gcd only accepts two natural numbers (a, b >= 1).
// 0 or negative number causes panic.
// Gcd uses the Euclidean Algorithm.
func Gcd(a, b int) int {
	if a <= 0 || b <= 0 {
		panic(errors.New("[argument error]: Gcd only accepts two NATURAL numbers"))
	}
	if a < b {
		a, b = b, a
	}

	// Euclidean Algorithm
	for b > 0 {
		div := a % b
		a, b = b, div
	}

	return a
}

// Lcm returns the Least Common Multiple of two natural numbers.
// Lcd only accepts two natural numbers (a, b >= 1).
// 0 or negative number causes panic.
// Lcd uses the Euclidean Algorithm indirectly.
func Lcm(a, b int) int {
	if a <= 0 || b <= 0 {
		panic(errors.New("[argument error]: Gcd only accepts two NATURAL numbers"))
	}

	// a = a'*gcd, b = b'*gcd, a*b = a'*b'*gcd^2
	// a' and b' are relatively prime numbers
	// gcd consists of prime numbers, that are included in a and b
	gcd := Gcd(a, b)

	// not (a * b / gcd), because of reducing a probability of overflow
	return (a / gcd) * b
}

// Strtoi is a wrapper of `strconv.Atoi()`.
// If `strconv.Atoi()` returns an error, Strtoi calls panic.
func Strtoi(s string) int {
	if i, err := strconv.Atoi(s); err != nil {
		panic(errors.New("[argument error]: Strtoi only accepts integer string"))
	} else {
		return i
	}
}

// PrintIntsLine returns integers string delimited by a space.
func PrintIntsLine(A ...int) string {
	res := []rune{}

	for i := 0; i < len(A); i++ {
		str := strconv.Itoa(A[i])
		// str := strconv.FormatInt(A[i], 10)  // 64bit int version
		res = append(res, []rune(str)...)

		if i != len(A)-1 {
			res = append(res, ' ')
		}
	}

	return string(res)
}

/********** FAU standard libraries **********/

//fmt.Sprintf("%b\n", 255) 	// binary expression

/********** I/O usage **********/

//str := ReadString()
//i := ReadInt()
//X := ReadIntSlice(n)
//S := ReadRuneSlice()
//a := ReadFloat64()
//A := ReadFloat64Slice(n)

//str := ZeroPaddingRuneSlice(num, 32)
//str := PrintIntsLine(X...)

/*******************************************************************/

const MOD = 1000000000 + 7
const ALPHABET_NUM = 26
const INF_INT64 = math.MaxInt64
const INF_BIT60 = 1 << 60

var n, m int

var edges []Edge

type Edge struct {
	s, t int
}

func main() {
	n, m = ReadInt2()
	for i := 0; i < m; i++ {
		s, t := ReadInt2()
		s--
		t--
		edges = append(edges, Edge{s, t})
	}

	ans := 0
	patterns := DuplicatePatterns([]int{0, 1, 2, 3, 4, 5}, n)
	for _, p := range patterns {
		A := [6][6]int{}
		for i := 0; i < 6; i++ {
			for j := 0; j < 6; j++ {
				A[i][j] = 1
			}
		}

		cnt := 0
		for _, e := range edges {
			l, r := p[e.s], p[e.t]
			if A[l][r] == 1 {
				A[l][r], A[r][l] = 0, 0
				cnt++
			}
		}
		ChMax(&ans, cnt)
	}

	fmt.Println(ans)
}

// DuplicatePatterns returns all patterns of n^k of elems([]int).
func DuplicatePatterns(elems []int, k int) [][]int {
	return dupliRec([]int{}, elems, k)
}

// DFS function for DuplicatePatterns.
func dupliRec(pattern, elems []int, k int) [][]int {
	if len(pattern) == k {
		return [][]int{pattern}
	}

	res := [][]int{}
	for _, e := range elems {
		newPattern := make([]int, len(pattern))
		copy(newPattern, pattern)
		newPattern = append(newPattern, e)

		res = append(res, dupliRec(newPattern, elems, k)...)
	}

	return res
}

// func dfs(v []int) {
// 	if len(v) == n {
// 		A := [6][6]int{}
// 		for i := 0; i < 6; i++ {
// 			for j := 0; j < 6; j++ {
// 				A[i][j] = 1
// 			}
// 		}

// 		cnt := 0
// 		for _, e := range edges {
// 			l, r := v[e.s], v[e.t]
// 			if A[l][r] == 1 {
// 				A[l][r], A[r][l] = 0, 0
// 				cnt++
// 			}
// 		}
// 		ChMax(&ans, cnt)
// 	} else {
// 		for i := 0; i <= 5; i++ {
// 			tmp := CopySlice(v)
// 			tmp = append(tmp, i)
// 			dfs(tmp)
// 		}
// 	}
// }

// func CopySlice(v []int) []int {
// 	res := make([]int, len(v))
// 	for i := 0; i < len(v); i++ {
// 		res[i] = v[i]
// 	}
// 	return res
// }

// MODはとったか？
// 遷移だけじゃなくて最後の最後でちゃんと取れよ？

/*******************************************************************/

package main

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

// github.com/EndlessCheng/codeforces-go
func Sol1230C(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)
	edges := make([][]int, n+1)
	shown := map[int]int{}
	for i := 0; i < m; i++ {
		var v, w int
		Fscan(in, &v, &w)
		edges[v] = append(edges[v], w)
		edges[w] = append(edges[w], v)
		shown[v] = 1
		shown[w] = 1
	}
	if len(shown) <= 6 {
		Fprintln(out, m)
		return
	}

	ans := 0
	for v, e := range edges {
		if len(e) == 0 {
			continue
		}
		for w, e2 := range edges {
			if w == v || len(e2) == 0 {
				continue
			}
			delta := 0
			for _, u := range e2 {
				if u == v {
					delta = 1
					break
				}
			}
			mp := map[int]int{}
			for _, u := range append(e, e2...) {
				if u == v {
					u = w
				}
				mp[u] = 1
			}
			delta = len(e) + len(e2) - delta - len(mp)
			if newAns := m - delta; newAns > ans {
				ans = newAns
			}
		}
	}
	Fprintln(out, ans)
}

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

// Author: sighduck
// URL: https://codeforces.com/contest/1230/problem/C

package main

import (
	"bufio"
	"fmt"
	"os"
)

type Edge struct {
	A int
	B int
}

func createMatrix(n int, m int, edges []Edge) [][]bool {
	matrix := make([][]bool, n)
	for i := 0; i < n; i++ {
		matrix[i] = make([]bool, n)
	}
	for _, edge := range edges {
		matrix[edge.A-1][edge.B-1] = true
		matrix[edge.B-1][edge.A-1] = true
	}
	return matrix
}

func Solve(n int, m int, edges []Edge) int {
	if n < 7 {
		return m
	}
	matrix := createMatrix(n, m, edges)
	minMutualNeighbors := n + 1
	for i := 0; i < n; i++ {
		for j := i + 1; j < n; j++ {
			mutualNeighbors := 0
			for z := 0; z < n; z++ {
				if z != i && z != j && matrix[i][z] && matrix[j][z] {
					mutualNeighbors += 1
				}
			}
			if mutualNeighbors < minMutualNeighbors {
				minMutualNeighbors = mutualNeighbors
			}
		}
	}
	return m - minMutualNeighbors
}

func main() {
	reader := bufio.NewReader(os.Stdin)
	writer := bufio.NewWriter(os.Stdout)
	defer writer.Flush()

	var n, m int
	fmt.Fscanf(reader, "%d %d\n", &n, &m)
	edges := make([]Edge, m)
	for i := 0; i < m; i++ {
		fmt.Fscanf(reader, "%d %d\n", &edges[i].A, &edges[i].B)
	}

	fmt.Fprintf(writer, "%d\n", Solve(n, m, edges))
}

package main

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"math"
	"os"
	"strconv"
)

/*********** I/O ***********/

var (
	// ReadString returns a WORD string.
	ReadString func() string
	stdout     *bufio.Writer
)

func init() {
	ReadString = newReadString(os.Stdin)
	stdout = bufio.NewWriter(os.Stdout)
}

func newReadString(ior io.Reader) func() string {
	r := bufio.NewScanner(ior)
	// r.Buffer(make([]byte, 1024), int(1e+11)) // for AtCoder
	r.Buffer(make([]byte, 1024), int(1e+9)) // for Codeforces
	// Split sets the split function for the Scanner. The default split function is ScanLines.
	// Split panics if it is called after scanning has started.
	r.Split(bufio.ScanWords)

	return func() string {
		if !r.Scan() {
			panic("Scan failed")
		}
		return r.Text()
	}
}

// ReadInt returns an integer.
func ReadInt() int {
	return int(readInt64())
}
func ReadInt2() (int, int) {
	return int(readInt64()), int(readInt64())
}
func ReadInt3() (int, int, int) {
	return int(readInt64()), int(readInt64()), int(readInt64())
}
func ReadInt4() (int, int, int, int) {
	return int(readInt64()), int(readInt64()), int(readInt64()), int(readInt64())
}

// ReadInt64 returns as integer as int64.
func ReadInt64() int64 {
	return readInt64()
}
func ReadInt64_2() (int64, int64) {
	return readInt64(), readInt64()
}
func ReadInt64_3() (int64, int64, int64) {
	return readInt64(), readInt64(), readInt64()
}
func ReadInt64_4() (int64, int64, int64, int64) {
	return readInt64(), readInt64(), readInt64(), readInt64()
}

func readInt64() int64 {
	i, err := strconv.ParseInt(ReadString(), 0, 64)
	if err != nil {
		panic(err.Error())
	}
	return i
}

// ReadIntSlice returns an integer slice that has n integers.
func ReadIntSlice(n int) []int {
	b := make([]int, n)
	for i := 0; i < n; i++ {
		b[i] = ReadInt()
	}
	return b
}

// ReadInt64Slice returns as int64 slice that has n integers.
func ReadInt64Slice(n int) []int64 {
	b := make([]int64, n)
	for i := 0; i < n; i++ {
		b[i] = ReadInt64()
	}
	return b
}

// ReadFloat64 returns an float64.
func ReadFloat64() float64 {
	return float64(readFloat64())
}

func readFloat64() float64 {
	f, err := strconv.ParseFloat(ReadString(), 64)
	if err != nil {
		panic(err.Error())
	}
	return f
}

// ReadFloatSlice returns an float64 slice that has n float64.
func ReadFloat64Slice(n int) []float64 {
	b := make([]float64, n)
	for i := 0; i < n; i++ {
		b[i] = ReadFloat64()
	}
	return b
}

// ReadRuneSlice returns a rune slice.
func ReadRuneSlice() []rune {
	return []rune(ReadString())
}

/*********** Debugging ***********/

// ZeroPaddingRuneSlice returns binary expressions of integer n with zero padding.
// For debugging use.
func ZeroPaddingRuneSlice(n, digitsNum int) []rune {
	sn := fmt.Sprintf("%b", n)

	residualLength := digitsNum - len(sn)
	if residualLength <= 0 {
		return []rune(sn)
	}

	zeros := make([]rune, residualLength)
	for i := 0; i < len(zeros); i++ {
		zeros[i] = '0'
	}

	res := []rune{}
	res = append(res, zeros...)
	res = append(res, []rune(sn)...)

	return res
}

/*********** DP sub-functions ***********/

// ChMin accepts a pointer of integer and a target value.
// If target value is SMALLER than the first argument,
//	then the first argument will be updated by the second argument.
func ChMin(updatedValue *int, target int) bool {
	if *updatedValue > target {
		*updatedValue = target
		return true
	}
	return false
}

// ChMax accepts a pointer of integer and a target value.
// If target value is LARGER than the first argument,
//	then the first argument will be updated by the second argument.
func ChMax(updatedValue *int, target int) bool {
	if *updatedValue < target {
		*updatedValue = target
		return true
	}
	return false
}

// NthBit returns nth bit value of an argument.
// n starts from 0.
func NthBit(num, nth int) int {
	return num >> uint(nth) & 1
}

// OnBit returns the integer that has nth ON bit.
// If an argument has nth ON bit, OnBit returns the argument.
func OnBit(num, nth int) int {
	return num | (1 << uint(nth))
}

// OffBit returns the integer that has nth OFF bit.
// If an argument has nth OFF bit, OffBit returns the argument.
func OffBit(num, nth int) int {
	return num & ^(1 << uint(nth))
}

// PopCount returns the number of ON bit of an argument.
func PopCount(num int) int {
	res := 0

	for i := 0; i < 70; i++ {
		if ((num >> uint(i)) & 1) == 1 {
			res++
		}
	}

	return res
}

/*********** Arithmetic ***********/

// Max returns the max integer among input set.
// This function needs at least 1 argument (no argument causes panic).
func Max(integers ...int) int {
	m := integers[0]
	for i, integer := range integers {
		if i == 0 {
			continue
		}
		if m < integer {
			m = integer
		}
	}
	return m
}

// Min returns the min integer among input set.
// This function needs at least 1 argument (no argument causes panic).
func Min(integers ...int) int {
	m := integers[0]
	for i, integer := range integers {
		if i == 0 {
			continue
		}
		if m > integer {
			m = integer
		}
	}
	return m
}

// DigitSum returns digit sum of a decimal number.
// DigitSum only accept a positive integer.
func DigitSum(n int) int {
	if n < 0 {
		return -1
	}

	res := 0

	for n > 0 {
		res += n % 10
		n /= 10
	}

	return res
}

// DigitNumOfDecimal returns digits number of n.
// n is non negative number.
func DigitNumOfDecimal(n int) int {
	res := 0

	for n > 0 {
		n /= 10
		res++
	}

	return res
}

// Sum returns multiple integers sum.
func Sum(integers ...int) int {
	s := 0

	for _, i := range integers {
		s += i
	}

	return s
}

// Kiriage returns Ceil(a/b)
// a >= 0, b > 0
func Kiriage(a, b int) int {
	return (a + (b - 1)) / b
}

// PowInt is integer version of math.Pow
// PowInt calculate a power by Binary Power (二分累乗法(O(log e))).
func PowInt(a, e int) int {
	if a < 0 || e < 0 {
		panic(errors.New("[argument error]: PowInt does not accept negative integers"))
	}

	if e == 0 {
		return 1
	}

	if e%2 == 0 {
		halfE := e / 2
		half := PowInt(a, halfE)
		return half * half
	}

	return a * PowInt(a, e-1)
}

// AbsInt is integer version of math.Abs
func AbsInt(a int) int {
	if a < 0 {
		return -a
	}
	return a
}

// Gcd returns the Greatest Common Divisor of two natural numbers.
// Gcd only accepts two natural numbers (a, b >= 1).
// 0 or negative number causes panic.
// Gcd uses the Euclidean Algorithm.
func Gcd(a, b int) int {
	if a <= 0 || b <= 0 {
		panic(errors.New("[argument error]: Gcd only accepts two NATURAL numbers"))
	}
	if a < b {
		a, b = b, a
	}

	// Euclidean Algorithm
	for b > 0 {
		div := a % b
		a, b = b, div
	}

	return a
}

// Lcm returns the Least Common Multiple of two natural numbers.
// Lcd only accepts two natural numbers (a, b >= 1).
// 0 or negative number causes panic.
// Lcd uses the Euclidean Algorithm indirectly.
func Lcm(a, b int) int {
	if a <= 0 || b <= 0 {
		panic(errors.New("[argument error]: Gcd only accepts two NATURAL numbers"))
	}

	// a = a'*gcd, b = b'*gcd, a*b = a'*b'*gcd^2
	// a' and b' are relatively prime numbers
	// gcd consists of prime numbers, that are included in a and b
	gcd := Gcd(a, b)

	// not (a * b / gcd), because of reducing a probability of overflow
	return (a / gcd) * b
}

// Strtoi is a wrapper of `strconv.Atoi()`.
// If `strconv.Atoi()` returns an error, Strtoi calls panic.
func Strtoi(s string) int {
	if i, err := strconv.Atoi(s); err != nil {
		panic(errors.New("[argument error]: Strtoi only accepts integer string"))
	} else {
		return i
	}
}

// PrintIntsLine returns integers string delimited by a space.
func PrintIntsLine(A ...int) string {
	res := []rune{}

	for i := 0; i < len(A); i++ {
		str := strconv.Itoa(A[i])
		res = append(res, []rune(str)...)

		if i != len(A)-1 {
			res = append(res, ' ')
		}
	}

	return string(res)
}

/********** I/O usage **********/

//str := ReadString()
//i := ReadInt()
//X := ReadIntSlice(n)
//S := ReadRuneSlice()
//a := ReadFloat64()
//A := ReadFloat64Slice(n)

//str := ZeroPaddingRuneSlice(num, 32)
//str := PrintIntsLine(X...)

/*******************************************************************/

const MOD = 1000000000 + 7
const ALPHABET_NUM = 26
const INF_INT64 = math.MaxInt64
const INF_BIT60 = 1 << 60

var n, m int

var adjMatrix [10][10]int
var dominoMatrix [10][10]int

func main() {
	n, m = ReadInt2()
	for i := 0; i < m; i++ {
		a, b := ReadInt2()
		// 常にaをbより小さくする
		if a > b {
			a, b = b, a
		}
		adjMatrix[a][b] = 1
		adjMatrix[b][a] = 1
	}

	if n <= 6 {
		fmt.Println(m)
	} else {
		fmt.Println(sub())
	}
}

func sub() int {
	res := 0

	// i, jを同じ数字とする
	for i := 1; i <= n; i++ {
		for j := i + 1; j <= n; j++ {
			// ノードにサイコロの目を割り振る
			memo := make(map[int]int)
			dice := 1
			for k := 1; k <= n; k++ {
				if k == j {
					continue
				}
				memo[k] = dice
				dice++
			}
			memo[j] = memo[i]

			tmp := 0
			initialize()
			for l := 1; l <= n; l++ {
				for m := l + 1; m <= n; m++ {
					ll, mm := memo[l], memo[m]
					if adjMatrix[l][m] == 1 && dominoMatrix[ll][mm] == 1 {
						tmp++
						dominoMatrix[ll][mm] = 0
					}
				}
			}

			ChMax(&res, tmp)
		}
	}

	return res
}

func initialize() {
	for i := 1; i <= 6; i++ {
		dominoMatrix[i][i] = 1
		for j := i + 1; j <= 6; j++ {
			dominoMatrix[i][j] = 1
		}
	}
}

// MODはとったか？
// 遷移だけじゃなくて最後の最後でちゃんと取れよ？

/*******************************************************************/

package main

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"math"
	"os"
	"strconv"
)

/*********** I/O ***********/

var (
	// ReadString returns a WORD string.
	ReadString func() string
	stdout     *bufio.Writer
)

func init() {
	ReadString = newReadString(os.Stdin)
	stdout = bufio.NewWriter(os.Stdout)
}

func newReadString(ior io.Reader) func() string {
	r := bufio.NewScanner(ior)
	// r.Buffer(make([]byte, 1024), int(1e+11)) // for AtCoder
	r.Buffer(make([]byte, 1024), int(1e+9)) // for Codeforces
	// Split sets the split function for the Scanner. The default split function is ScanLines.
	// Split panics if it is called after scanning has started.
	r.Split(bufio.ScanWords)

	return func() string {
		if !r.Scan() {
			panic("Scan failed")
		}
		return r.Text()
	}
}

// ReadInt returns an integer.
func ReadInt() int {
	return int(readInt64())
}
func ReadInt2() (int, int) {
	return int(readInt64()), int(readInt64())
}
func ReadInt3() (int, int, int) {
	return int(readInt64()), int(readInt64()), int(readInt64())
}
func ReadInt4() (int, int, int, int) {
	return int(readInt64()), int(readInt64()), int(readInt64()), int(readInt64())
}

func readInt64() int64 {
	i, err := strconv.ParseInt(ReadString(), 0, 64)
	if err != nil {
		panic(err.Error())
	}
	return i
}

// ReadIntSlice returns an integer slice that has n integers.
func ReadIntSlice(n int) []int {
	b := make([]int, n)
	for i := 0; i < n; i++ {
		b[i] = ReadInt()
	}
	return b
}

// ReadFloat64 returns an float64.
func ReadFloat64() float64 {
	return float64(readFloat64())
}

func readFloat64() float64 {
	f, err := strconv.ParseFloat(ReadString(), 64)
	if err != nil {
		panic(err.Error())
	}
	return f
}

// ReadFloatSlice returns an float64 slice that has n float64.
func ReadFloat64Slice(n int) []float64 {
	b := make([]float64, n)
	for i := 0; i < n; i++ {
		b[i] = ReadFloat64()
	}
	return b
}

// ReadRuneSlice returns a rune slice.
func ReadRuneSlice() []rune {
	return []rune(ReadString())
}

/*********** Debugging ***********/

// ZeroPaddingRuneSlice returns binary expressions of integer n with zero padding.
// For debugging use.
func ZeroPaddingRuneSlice(n, digitsNum int) []rune {
	sn := fmt.Sprintf("%b", n)

	residualLength := digitsNum - len(sn)
	if residualLength <= 0 {
		return []rune(sn)
	}

	zeros := make([]rune, residualLength)
	for i := 0; i < len(zeros); i++ {
		zeros[i] = '0'
	}

	res := []rune{}
	res = append(res, zeros...)
	res = append(res, []rune(sn)...)

	return res
}

/*********** DP sub-functions ***********/

// ChMin accepts a pointer of integer and a target value.
// If target value is SMALLER than the first argument,
//	then the first argument will be updated by the second argument.
func ChMin(updatedValue *int, target int) bool {
	if *updatedValue > target {
		*updatedValue = target
		return true
	}
	return false
}

// ChMax accepts a pointer of integer and a target value.
// If target value is LARGER than the first argument,
//	then the first argument will be updated by the second argument.
func ChMax(updatedValue *int, target int) bool {
	if *updatedValue < target {
		*updatedValue = target
		return true
	}
	return false
}

// NthBit returns nth bit value of an argument.
// n starts from 0.
func NthBit(num, nth int) int {
	return num >> uint(nth) & 1
}

// OnBit returns the integer that has nth ON bit.
// If an argument has nth ON bit, OnBit returns the argument.
func OnBit(num, nth int) int {
	return num | (1 << uint(nth))
}

// OffBit returns the integer that has nth OFF bit.
// If an argument has nth OFF bit, OffBit returns the argument.
func OffBit(num, nth int) int {
	return num & ^(1 << uint(nth))
}

// PopCount returns the number of ON bit of an argument.
func PopCount(num int) int {
	res := 0

	for i := 0; i < 70; i++ {
		if ((num >> uint(i)) & 1) == 1 {
			res++
		}
	}

	return res
}

/*********** Arithmetic ***********/

// Max returns the max integer among input set.
// This function needs at least 1 argument (no argument causes panic).
func Max(integers ...int) int {
	m := integers[0]
	for i, integer := range integers {
		if i == 0 {
			continue
		}
		if m < integer {
			m = integer
		}
	}
	return m
}

// Min returns the min integer among input set.
// This function needs at least 1 argument (no argument causes panic).
func Min(integers ...int) int {
	m := integers[0]
	for i, integer := range integers {
		if i == 0 {
			continue
		}
		if m > integer {
			m = integer
		}
	}
	return m
}

// DigitSum returns digit sum of a decimal number.
// DigitSum only accept a positive integer.
func DigitSum(n int) int {
	if n < 0 {
		return -1
	}

	res := 0

	for n > 0 {
		res += n % 10
		n /= 10
	}

	return res
}

// DigitNumOfDecimal returns digits number of n.
// n is non negative number.
func DigitNumOfDecimal(n int) int {
	res := 0

	for n > 0 {
		n /= 10
		res++
	}

	return res
}

// Sum returns multiple integers sum.
func Sum(integers ...int) int {
	s := 0

	for _, i := range integers {
		s += i
	}

	return s
}

// Kiriage returns Ceil(a/b)
// a >= 0, b > 0
func Kiriage(a, b int) int {
	return (a + (b - 1)) / b
}

// PowInt is integer version of math.Pow
// PowInt calculate a power by Binary Power (二分累乗法(O(log e))).
func PowInt(a, e int) int {
	if a < 0 || e < 0 {
		panic(errors.New("[argument error]: PowInt does not accept negative integers"))
	}

	if e == 0 {
		return 1
	}

	if e%2 == 0 {
		halfE := e / 2
		half := PowInt(a, halfE)
		return half * half
	}

	return a * PowInt(a, e-1)
}

// AbsInt is integer version of math.Abs
func AbsInt(a int) int {
	if a < 0 {
		return -a
	}
	return a
}

// Gcd returns the Greatest Common Divisor of two natural numbers.
// Gcd only accepts two natural numbers (a, b >= 1).
// 0 or negative number causes panic.
// Gcd uses the Euclidean Algorithm.
func Gcd(a, b int) int {
	if a <= 0 || b <= 0 {
		panic(errors.New("[argument error]: Gcd only accepts two NATURAL numbers"))
	}
	if a < b {
		a, b = b, a
	}

	// Euclidean Algorithm
	for b > 0 {
		div := a % b
		a, b = b, div
	}

	return a
}

// Lcm returns the Least Common Multiple of two natural numbers.
// Lcd only accepts two natural numbers (a, b >= 1).
// 0 or negative number causes panic.
// Lcd uses the Euclidean Algorithm indirectly.
func Lcm(a, b int) int {
	if a <= 0 || b <= 0 {
		panic(errors.New("[argument error]: Gcd only accepts two NATURAL numbers"))
	}

	// a = a'*gcd, b = b'*gcd, a*b = a'*b'*gcd^2
	// a' and b' are relatively prime numbers
	// gcd consists of prime numbers, that are included in a and b
	gcd := Gcd(a, b)

	// not (a * b / gcd), because of reducing a probability of overflow
	return (a / gcd) * b
}

// Strtoi is a wrapper of `strconv.Atoi()`.
// If `strconv.Atoi()` returns an error, Strtoi calls panic.
func Strtoi(s string) int {
	if i, err := strconv.Atoi(s); err != nil {
		panic(errors.New("[argument error]: Strtoi only accepts integer string"))
	} else {
		return i
	}
}

// PrintIntsLine returns integers string delimited by a space.
func PrintIntsLine(A ...int) string {
	res := []rune{}

	for i := 0; i < len(A); i++ {
		str := strconv.Itoa(A[i])
		res = append(res, []rune(str)...)

		if i != len(A)-1 {
			res = append(res, ' ')
		}
	}

	return string(res)
}

/********** I/O usage **********/

//str := ReadString()
//i := ReadInt()
//X := ReadIntSlice(n)
//S := ReadRuneSlice()
//a := ReadFloat64()
//A := ReadFloat64Slice(n)

//str := ZeroPaddingRuneSlice(num, 32)
//str := PrintIntsLine(X...)

/*******************************************************************/

const MOD = 1000000000 + 7
const ALPHABET_NUM = 26
const INF_INT64 = math.MaxInt64
const INF_BIT60 = 1 << 60

var n, m int

var adjMatrix [10][10]int
var dominoMatrix [7][7]int

func main() {
	n, m = ReadInt2()
	for i := 0; i < m; i++ {
		a, b := ReadInt2()
		// 常にaをbより小さくする
		if a > b {
			a, b = b, a
		}
		adjMatrix[a][b] = 1
		adjMatrix[b][a] = 1
	}
	// for i := 1; i <= n; i++ {
	// 	fmt.Println(adjMatrix[i])
	// }
	// initialize()
	// for i := 1; i <= 6; i++ {
	// 	fmt.Println(dominoMatrix[i])
	// }

	if n == 7 {
		ans := 0
		for i := 1; i <= n; i++ {
			initialize()
			tmp := sub(i)
			ChMax(&ans, tmp)
		}
		fmt.Println(ans)
	} else {
		fmt.Println(subsub())
	}
}

func subsub() int {
	res := 0
	for i := 1; i <= n; i++ {
		for j := i + 1; j <= n; j++ {
			if adjMatrix[i][j] == 1 {
				res++
			}
		}
	}
	return res
}

// sevenId を 7としたときの置けるドミノの数
func sub(sevenId int) int {
	res := 0

	// memo: ノードIDからドミノの数へのマップ
	memo := make(map[int]int)
	dominoNum := 1
	for i := 1; i <= n; i++ {
		if i == sevenId {
			continue
		}
		memo[i] = dominoNum
		dominoNum++
	}
	memo[sevenId] = dominoNum
	// memoの逆引き
	revMemo := make(map[int]int)
	for k, v := range memo {
		revMemo[v] = k
	}

	// sevenId以外のノードについて、割り当てられたドミノ数に基づいてドミノをおいていく
	for i := 1; i <= n; i++ {
		for j := i + 1; j <= n; j++ {
			// i, jは通常のノード番号
			if i == sevenId || j == sevenId {
				continue
			}

			if adjMatrix[i][j] == 1 {
				res++

				// i, jをドミノ数に変換
				// diのほうを常に小さく
				di := memo[i]
				dj := memo[j]
				if di > dj {
					di, dj = dj, di
				}
				dominoMatrix[di][dj] = 0 // ドミノを消費
			}
		}
	}

	// i: sevenId のドミノ数をiとする
	resres := 0
	for i := 1; i <= 6; i++ {
		di := i
		tmpRes := 0

		// 他方のノードすべてを調べる
		for j := 1; j <= 6; j++ {
			dj := j
			ni, nj := sevenId, revMemo[dj]
			if di > dj {
				di, dj = dj, di
			}

			// ドミノが未使用かつエッジがアレばドミノを設置できる
			if dominoMatrix[di][dj] == 1 && adjMatrix[ni][nj] == 1 {
				tmpRes++
			}
		}

		ChMax(&resres, tmpRes)
	}

	return res + resres
}

func initialize() {
	for i := 1; i <= 6; i++ {
		for j := i; j <= 6; j++ {
			dominoMatrix[i][j] = 1
		}
	}
}

// MODはとったか？
// 遷移だけじゃなくて最後の最後でちゃんと取れよ？

/*******************************************************************/

package main

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"math"
	"os"
	"strconv"
)

/*********** I/O ***********/

var (
	// ReadString returns a WORD string.
	ReadString func() string
	stdout     *bufio.Writer
)

func init() {
	ReadString = newReadString(os.Stdin)
	stdout = bufio.NewWriter(os.Stdout)
}

func newReadString(ior io.Reader) func() string {
	r := bufio.NewScanner(ior)
	// r.Buffer(make([]byte, 1024), int(1e+11)) // for AtCoder
	r.Buffer(make([]byte, 1024), int(1e+9)) // for Codeforces
	// Split sets the split function for the Scanner. The default split function is ScanLines.
	// Split panics if it is called after scanning has started.
	r.Split(bufio.ScanWords)

	return func() string {
		if !r.Scan() {
			panic("Scan failed")
		}
		return r.Text()
	}
}

// ReadInt returns an integer.
func ReadInt() int {
	return int(readInt64())
}
func ReadInt2() (int, int) {
	return int(readInt64()), int(readInt64())
}
func ReadInt3() (int, int, int) {
	return int(readInt64()), int(readInt64()), int(readInt64())
}
func ReadInt4() (int, int, int, int) {
	return int(readInt64()), int(readInt64()), int(readInt64()), int(readInt64())
}

// ReadInt64 returns as integer as int64.
func ReadInt64() int64 {
	return readInt64()
}
func ReadInt64_2() (int64, int64) {
	return readInt64(), readInt64()
}
func ReadInt64_3() (int64, int64, int64) {
	return readInt64(), readInt64(), readInt64()
}
func ReadInt64_4() (int64, int64, int64, int64) {
	return readInt64(), readInt64(), readInt64(), readInt64()
}

func readInt64() int64 {
	i, err := strconv.ParseInt(ReadString(), 0, 64)
	if err != nil {
		panic(err.Error())
	}
	return i
}

// ReadIntSlice returns an integer slice that has n integers.
func ReadIntSlice(n int) []int {
	b := make([]int, n)
	for i := 0; i < n; i++ {
		b[i] = ReadInt()
	}
	return b
}

// ReadInt64Slice returns as int64 slice that has n integers.
func ReadInt64Slice(n int) []int64 {
	b := make([]int64, n)
	for i := 0; i < n; i++ {
		b[i] = ReadInt64()
	}
	return b
}

// ReadFloat64 returns an float64.
func ReadFloat64() float64 {
	return float64(readFloat64())
}

func readFloat64() float64 {
	f, err := strconv.ParseFloat(ReadString(), 64)
	if err != nil {
		panic(err.Error())
	}
	return f
}

// ReadFloatSlice returns an float64 slice that has n float64.
func ReadFloat64Slice(n int) []float64 {
	b := make([]float64, n)
	for i := 0; i < n; i++ {
		b[i] = ReadFloat64()
	}
	return b
}

// ReadRuneSlice returns a rune slice.
func ReadRuneSlice() []rune {
	return []rune(ReadString())
}

/*********** Debugging ***********/

// ZeroPaddingRuneSlice returns binary expressions of integer n with zero padding.
// For debugging use.
func ZeroPaddingRuneSlice(n, digitsNum int) []rune {
	sn := fmt.Sprintf("%b", n)

	residualLength := digitsNum - len(sn)
	if residualLength <= 0 {
		return []rune(sn)
	}

	zeros := make([]rune, residualLength)
	for i := 0; i < len(zeros); i++ {
		zeros[i] = '0'
	}

	res := []rune{}
	res = append(res, zeros...)
	res = append(res, []rune(sn)...)

	return res
}

/*********** DP sub-functions ***********/

// ChMin accepts a pointer of integer and a target value.
// If target value is SMALLER than the first argument,
//	then the first argument will be updated by the second argument.
func ChMin(updatedValue *int, target int) bool {
	if *updatedValue > target {
		*updatedValue = target
		return true
	}
	return false
}

// ChMax accepts a pointer of integer and a target value.
// If target value is LARGER than the first argument,
//	then the first argument will be updated by the second argument.
func ChMax(updatedValue *int, target int) bool {
	if *updatedValue < target {
		*updatedValue = target
		return true
	}
	return false
}

// NthBit returns nth bit value of an argument.
// n starts from 0.
func NthBit(num, nth int) int {
	return num >> uint(nth) & 1
}

// OnBit returns the integer that has nth ON bit.
// If an argument has nth ON bit, OnBit returns the argument.
func OnBit(num, nth int) int {
	return num | (1 << uint(nth))
}

// OffBit returns the integer that has nth OFF bit.
// If an argument has nth OFF bit, OffBit returns the argument.
func OffBit(num, nth int) int {
	return num & ^(1 << uint(nth))
}

// PopCount returns the number of ON bit of an argument.
func PopCount(num int) int {
	res := 0

	for i := 0; i < 70; i++ {
		if ((num >> uint(i)) & 1) == 1 {
			res++
		}
	}

	return res
}

/*********** Arithmetic ***********/

// Max returns the max integer among input set.
// This function needs at least 1 argument (no argument causes panic).
func Max(integers ...int) int {
	m := integers[0]
	for i, integer := range integers {
		if i == 0 {
			continue
		}
		if m < integer {
			m = integer
		}
	}
	return m
}

// Min returns the min integer among input set.
// This function needs at least 1 argument (no argument causes panic).
func Min(integers ...int) int {
	m := integers[0]
	for i, integer := range integers {
		if i == 0 {
			continue
		}
		if m > integer {
			m = integer
		}
	}
	return m
}

// DigitSum returns digit sum of a decimal number.
// DigitSum only accept a positive integer.
func DigitSum(n int) int {
	if n < 0 {
		return -1
	}

	res := 0

	for n > 0 {
		res += n % 10
		n /= 10
	}

	return res
}

// DigitNumOfDecimal returns digits number of n.
// n is non negative number.
func DigitNumOfDecimal(n int) int {
	res := 0

	for n > 0 {
		n /= 10
		res++
	}

	return res
}

// Sum returns multiple integers sum.
func Sum(integers ...int) int {
	s := 0

	for _, i := range integers {
		s += i
	}

	return s
}

// Kiriage returns Ceil(a/b)
// a >= 0, b > 0
func Kiriage(a, b int) int {
	return (a + (b - 1)) / b
}

// PowInt is integer version of math.Pow
// PowInt calculate a power by Binary Power (二分累乗法(O(log e))).
func PowInt(a, e int) int {
	if a < 0 || e < 0 {
		panic(errors.New("[argument error]: PowInt does not accept negative integers"))
	}

	if e == 0 {
		return 1
	}

	if e%2 == 0 {
		halfE := e / 2
		half := PowInt(a, halfE)
		return half * half
	}

	return a * PowInt(a, e-1)
}

// AbsInt is integer version of math.Abs
func AbsInt(a int) int {
	if a < 0 {
		return -a
	}
	return a
}

// Gcd returns the Greatest Common Divisor of two natural numbers.
// Gcd only accepts two natural numbers (a, b >= 1).
// 0 or negative number causes panic.
// Gcd uses the Euclidean Algorithm.
func Gcd(a, b int) int {
	if a <= 0 || b <= 0 {
		panic(errors.New("[argument error]: Gcd only accepts two NATURAL numbers"))
	}
	if a < b {
		a, b = b, a
	}

	// Euclidean Algorithm
	for b > 0 {
		div := a % b
		a, b = b, div
	}

	return a
}

// Lcm returns the Least Common Multiple of two natural numbers.
// Lcd only accepts two natural numbers (a, b >= 1).
// 0 or negative number causes panic.
// Lcd uses the Euclidean Algorithm indirectly.
func Lcm(a, b int) int {
	if a <= 0 || b <= 0 {
		panic(errors.New("[argument error]: Gcd only accepts two NATURAL numbers"))
	}

	// a = a'*gcd, b = b'*gcd, a*b = a'*b'*gcd^2
	// a' and b' are relatively prime numbers
	// gcd consists of prime numbers, that are included in a and b
	gcd := Gcd(a, b)

	// not (a * b / gcd), because of reducing a probability of overflow
	return (a / gcd) * b
}

// Strtoi is a wrapper of `strconv.Atoi()`.
// If `strconv.Atoi()` returns an error, Strtoi calls panic.
func Strtoi(s string) int {
	if i, err := strconv.Atoi(s); err != nil {
		panic(errors.New("[argument error]: Strtoi only accepts integer string"))
	} else {
		return i
	}
}

// PrintIntsLine returns integers string delimited by a space.
func PrintIntsLine(A ...int) string {
	res := []rune{}

	for i := 0; i < len(A); i++ {
		str := strconv.Itoa(A[i])
		res = append(res, []rune(str)...)

		if i != len(A)-1 {
			res = append(res, ' ')
		}
	}

	return string(res)
}

/********** I/O usage **********/

//str := ReadString()
//i := ReadInt()
//X := ReadIntSlice(n)
//S := ReadRuneSlice()
//a := ReadFloat64()
//A := ReadFloat64Slice(n)

//str := ZeroPaddingRuneSlice(num, 32)
//str := PrintIntsLine(X...)

/*******************************************************************/

const MOD = 1000000000 + 7
const ALPHABET_NUM = 26
const INF_INT64 = math.MaxInt64
const INF_BIT60 = 1 << 60

var n, m int

var adjMatrix [10][10]int
var dominoMatrix [7][7]int

func main() {
	n, m = ReadInt2()
	for i := 0; i < m; i++ {
		a, b := ReadInt2()
		// 常にaをbより小さくする
		if a > b {
			a, b = b, a
		}
		adjMatrix[a][b] = 1
		adjMatrix[b][a] = 1
	}

	if n <= 6 {
		fmt.Println(m)
	} else {
		fmt.Println(sub())
	}
}

func sub() int {
	res := 0

	// i, jを同じ数字とする
	for i := 1; i <= n; i++ {
		for j := i + 1; j <= n; j++ {
			// j は除外して置けるドミノの数をカウントし、最後にi, jに辺があれば一つ加算する
			tmp := 0
			for l := 1; l <= n; l++ {
				for m := l + 1; m <= n; m++ {
					if l == j || m == j {
						continue
					}

					if adjMatrix[l][m] == 1 {
						tmp++
					}
				}
			}
			if adjMatrix[i][j] == 1 {
				tmp++
			}

			ChMax(&res, tmp)
		}
	}

	return res
}

// MODはとったか？
// 遷移だけじゃなくて最後の最後でちゃんと取れよ？

/*******************************************************************/

package main

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"math"
	"os"
	"strconv"
)

/*********** I/O ***********/

var (
	// ReadString returns a WORD string.
	ReadString func() string
	stdout     *bufio.Writer
)

func init() {
	ReadString = newReadString(os.Stdin)
	stdout = bufio.NewWriter(os.Stdout)
}

func newReadString(ior io.Reader) func() string {
	r := bufio.NewScanner(ior)
	// r.Buffer(make([]byte, 1024), int(1e+11)) // for AtCoder
	r.Buffer(make([]byte, 1024), int(1e+9)) // for Codeforces
	// Split sets the split function for the Scanner. The default split function is ScanLines.
	// Split panics if it is called after scanning has started.
	r.Split(bufio.ScanWords)

	return func() string {
		if !r.Scan() {
			panic("Scan failed")
		}
		return r.Text()
	}
}

// ReadInt returns an integer.
func ReadInt() int {
	return int(readInt64())
}
func ReadInt2() (int, int) {
	return int(readInt64()), int(readInt64())
}
func ReadInt3() (int, int, int) {
	return int(readInt64()), int(readInt64()), int(readInt64())
}
func ReadInt4() (int, int, int, int) {
	return int(readInt64()), int(readInt64()), int(readInt64()), int(readInt64())
}

// ReadInt64 returns as integer as int64.
func ReadInt64() int64 {
	return readInt64()
}
func ReadInt64_2() (int64, int64) {
	return readInt64(), readInt64()
}
func ReadInt64_3() (int64, int64, int64) {
	return readInt64(), readInt64(), readInt64()
}
func ReadInt64_4() (int64, int64, int64, int64) {
	return readInt64(), readInt64(), readInt64(), readInt64()
}

func readInt64() int64 {
	i, err := strconv.ParseInt(ReadString(), 0, 64)
	if err != nil {
		panic(err.Error())
	}
	return i
}

// ReadIntSlice returns an integer slice that has n integers.
func ReadIntSlice(n int) []int {
	b := make([]int, n)
	for i := 0; i < n; i++ {
		b[i] = ReadInt()
	}
	return b
}

// ReadInt64Slice returns as int64 slice that has n integers.
func ReadInt64Slice(n int) []int64 {
	b := make([]int64, n)
	for i := 0; i < n; i++ {
		b[i] = ReadInt64()
	}
	return b
}

// ReadFloat64 returns an float64.
func ReadFloat64() float64 {
	return float64(readFloat64())
}

func readFloat64() float64 {
	f, err := strconv.ParseFloat(ReadString(), 64)
	if err != nil {
		panic(err.Error())
	}
	return f
}

// ReadFloatSlice returns an float64 slice that has n float64.
func ReadFloat64Slice(n int) []float64 {
	b := make([]float64, n)
	for i := 0; i < n; i++ {
		b[i] = ReadFloat64()
	}
	return b
}

// ReadRuneSlice returns a rune slice.
func ReadRuneSlice() []rune {
	return []rune(ReadString())
}

/*********** Debugging ***********/

// ZeroPaddingRuneSlice returns binary expressions of integer n with zero padding.
// For debugging use.
func ZeroPaddingRuneSlice(n, digitsNum int) []rune {
	sn := fmt.Sprintf("%b", n)

	residualLength := digitsNum - len(sn)
	if residualLength <= 0 {
		return []rune(sn)
	}

	zeros := make([]rune, residualLength)
	for i := 0; i < len(zeros); i++ {
		zeros[i] = '0'
	}

	res := []rune{}
	res = append(res, zeros...)
	res = append(res, []rune(sn)...)

	return res
}

/*********** DP sub-functions ***********/

// ChMin accepts a pointer of integer and a target value.
// If target value is SMALLER than the first argument,
//	then the first argument will be updated by the second argument.
func ChMin(updatedValue *int, target int) bool {
	if *updatedValue > target {
		*updatedValue = target
		return true
	}
	return false
}

// ChMax accepts a pointer of integer and a target value.
// If target value is LARGER than the first argument,
//	then the first argument will be updated by the second argument.
func ChMax(updatedValue *int, target int) bool {
	if *updatedValue < target {
		*updatedValue = target
		return true
	}
	return false
}

// NthBit returns nth bit value of an argument.
// n starts from 0.
func NthBit(num, nth int) int {
	return num >> uint(nth) & 1
}

// OnBit returns the integer that has nth ON bit.
// If an argument has nth ON bit, OnBit returns the argument.
func OnBit(num, nth int) int {
	return num | (1 << uint(nth))
}

// OffBit returns the integer that has nth OFF bit.
// If an argument has nth OFF bit, OffBit returns the argument.
func OffBit(num, nth int) int {
	return num & ^(1 << uint(nth))
}

// PopCount returns the number of ON bit of an argument.
func PopCount(num int) int {
	res := 0

	for i := 0; i < 70; i++ {
		if ((num >> uint(i)) & 1) == 1 {
			res++
		}
	}

	return res
}

/*********** Arithmetic ***********/

// Max returns the max integer among input set.
// This function needs at least 1 argument (no argument causes panic).
func Max(integers ...int) int {
	m := integers[0]
	for i, integer := range integers {
		if i == 0 {
			continue
		}
		if m < integer {
			m = integer
		}
	}
	return m
}

// Min returns the min integer among input set.
// This function needs at least 1 argument (no argument causes panic).
func Min(integers ...int) int {
	m := integers[0]
	for i, integer := range integers {
		if i == 0 {
			continue
		}
		if m > integer {
			m = integer
		}
	}
	return m
}

// DigitSum returns digit sum of a decimal number.
// DigitSum only accept a positive integer.
func DigitSum(n int) int {
	if n < 0 {
		return -1
	}

	res := 0

	for n > 0 {
		res += n % 10
		n /= 10
	}

	return res
}

// DigitNumOfDecimal returns digits number of n.
// n is non negative number.
func DigitNumOfDecimal(n int) int {
	res := 0

	for n > 0 {
		n /= 10
		res++
	}

	return res
}

// Sum returns multiple integers sum.
func Sum(integers ...int) int {
	s := 0

	for _, i := range integers {
		s += i
	}

	return s
}

// Kiriage returns Ceil(a/b)
// a >= 0, b > 0
func Kiriage(a, b int) int {
	return (a + (b - 1)) / b
}

// PowInt is integer version of math.Pow
// PowInt calculate a power by Binary Power (二分累乗法(O(log e))).
func PowInt(a, e int) int {
	if a < 0 || e < 0 {
		panic(errors.New("[argument error]: PowInt does not accept negative integers"))
	}

	if e == 0 {
		return 1
	}

	if e%2 == 0 {
		halfE := e / 2
		half := PowInt(a, halfE)
		return half * half
	}

	return a * PowInt(a, e-1)
}

// AbsInt is integer version of math.Abs
func AbsInt(a int) int {
	if a < 0 {
		return -a
	}
	return a
}

// Gcd returns the Greatest Common Divisor of two natural numbers.
// Gcd only accepts two natural numbers (a, b >= 1).
// 0 or negative number causes panic.
// Gcd uses the Euclidean Algorithm.
func Gcd(a, b int) int {
	if a <= 0 || b <= 0 {
		panic(errors.New("[argument error]: Gcd only accepts two NATURAL numbers"))
	}
	if a < b {
		a, b = b, a
	}

	// Euclidean Algorithm
	for b > 0 {
		div := a % b
		a, b = b, div
	}

	return a
}

// Lcm returns the Least Common Multiple of two natural numbers.
// Lcd only accepts two natural numbers (a, b >= 1).
// 0 or negative number causes panic.
// Lcd uses the Euclidean Algorithm indirectly.
func Lcm(a, b int) int {
	if a <= 0 || b <= 0 {
		panic(errors.New("[argument error]: Gcd only accepts two NATURAL numbers"))
	}

	// a = a'*gcd, b = b'*gcd, a*b = a'*b'*gcd^2
	// a' and b' are relatively prime numbers
	// gcd consists of prime numbers, that are included in a and b
	gcd := Gcd(a, b)

	// not (a * b / gcd), because of reducing a probability of overflow
	return (a / gcd) * b
}

// Strtoi is a wrapper of `strconv.Atoi()`.
// If `strconv.Atoi()` returns an error, Strtoi calls panic.
func Strtoi(s string) int {
	if i, err := strconv.Atoi(s); err != nil {
		panic(errors.New("[argument error]: Strtoi only accepts integer string"))
	} else {
		return i
	}
}

// PrintIntsLine returns integers string delimited by a space.
func PrintIntsLine(A ...int) string {
	res := []rune{}

	for i := 0; i < len(A); i++ {
		str := strconv.Itoa(A[i])
		res = append(res, []rune(str)...)

		if i != len(A)-1 {
			res = append(res, ' ')
		}
	}

	return string(res)
}

/********** I/O usage **********/

//str := ReadString()
//i := ReadInt()
//X := ReadIntSlice(n)
//S := ReadRuneSlice()
//a := ReadFloat64()
//A := ReadFloat64Slice(n)

//str := ZeroPaddingRuneSlice(num, 32)
//str := PrintIntsLine(X...)

/*******************************************************************/

const MOD = 1000000000 + 7
const ALPHABET_NUM = 26
const INF_INT64 = math.MaxInt64
const INF_BIT60 = 1 << 60

var n, m int

var adjMatrix [10][10]int
var dominoMatrix [10][10]int

func main() {
	n, m = ReadInt2()
	for i := 0; i < m; i++ {
		a, b := ReadInt2()
		// 常にaをbより小さくする
		if a > b {
			a, b = b, a
		}
		adjMatrix[a][b] = 1
		adjMatrix[b][a] = 1
	}

	if n <= 6 {
		fmt.Println(m)
	} else {
		fmt.Println(sub())
	}
}

func sub() int {
	res := 0

	// i, jを同じ数字とする
	for i := 1; i <= n; i++ {
		for j := i + 1; j <= n; j++ {
			memo := make(map[int]int)
			dice := 1
			for k := 1; k <= n; k++ {
				if k == j {
					continue
				}
				memo[k] = dice
				dice++
			}
			memo[j] = memo[i]

			tmp := 0
			initialize()
			for l := 1; l <= n; l++ {
				for m := l + 1; m <= n; m++ {
					ll, mm := memo[l], memo[m]
					if adjMatrix[l][m] == 1 && dominoMatrix[ll][mm] == 1 {
						tmp++
						dominoMatrix[ll][mm] = 0
					}
				}
			}
			if adjMatrix[i][j] == 1 {
				tmp++
			}

			ChMax(&res, tmp)
		}
	}

	return res
}

func initialize() {
	for i := 1; i <= 6; i++ {
		for j := i + 1; j <= 6; j++ {
			dominoMatrix[i][j] = 1
		}
	}
}

// MODはとったか？
// 遷移だけじゃなくて最後の最後でちゃんと取れよ？

/*******************************************************************/

package main

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"math"
	"os"
	"strconv"
)

/*********** I/O ***********/

var (
	// ReadString returns a WORD string.
	ReadString func() string
	stdout     *bufio.Writer
)

func init() {
	ReadString = newReadString(os.Stdin)
	stdout = bufio.NewWriter(os.Stdout)
}

func newReadString(ior io.Reader) func() string {
	r := bufio.NewScanner(ior)
	// r.Buffer(make([]byte, 1024), int(1e+11)) // for AtCoder
	r.Buffer(make([]byte, 1024), int(1e+9)) // for Codeforces
	// Split sets the split function for the Scanner. The default split function is ScanLines.
	// Split panics if it is called after scanning has started.
	r.Split(bufio.ScanWords)

	return func() string {
		if !r.Scan() {
			panic("Scan failed")
		}
		return r.Text()
	}
}

// ReadInt returns an integer.
func ReadInt() int {
	return int(readInt64())
}
func ReadInt2() (int, int) {
	return int(readInt64()), int(readInt64())
}
func ReadInt3() (int, int, int) {
	return int(readInt64()), int(readInt64()), int(readInt64())
}
func ReadInt4() (int, int, int, int) {
	return int(readInt64()), int(readInt64()), int(readInt64()), int(readInt64())
}

func readInt64() int64 {
	i, err := strconv.ParseInt(ReadString(), 0, 64)
	if err != nil {
		panic(err.Error())
	}
	return i
}

// ReadIntSlice returns an integer slice that has n integers.
func ReadIntSlice(n int) []int {
	b := make([]int, n)
	for i := 0; i < n; i++ {
		b[i] = ReadInt()
	}
	return b
}

// ReadFloat64 returns an float64.
func ReadFloat64() float64 {
	return float64(readFloat64())
}

func readFloat64() float64 {
	f, err := strconv.ParseFloat(ReadString(), 64)
	if err != nil {
		panic(err.Error())
	}
	return f
}

// ReadFloatSlice returns an float64 slice that has n float64.
func ReadFloat64Slice(n int) []float64 {
	b := make([]float64, n)
	for i := 0; i < n; i++ {
		b[i] = ReadFloat64()
	}
	return b
}

// ReadRuneSlice returns a rune slice.
func ReadRuneSlice() []rune {
	return []rune(ReadString())
}

/*********** Debugging ***********/

// ZeroPaddingRuneSlice returns binary expressions of integer n with zero padding.
// For debugging use.
func ZeroPaddingRuneSlice(n, digitsNum int) []rune {
	sn := fmt.Sprintf("%b", n)

	residualLength := digitsNum - len(sn)
	if residualLength <= 0 {
		return []rune(sn)
	}

	zeros := make([]rune, residualLength)
	for i := 0; i < len(zeros); i++ {
		zeros[i] = '0'
	}

	res := []rune{}
	res = append(res, zeros...)
	res = append(res, []rune(sn)...)

	return res
}

/*********** DP sub-functions ***********/

// ChMin accepts a pointer of integer and a target value.
// If target value is SMALLER than the first argument,
//	then the first argument will be updated by the second argument.
func ChMin(updatedValue *int, target int) bool {
	if *updatedValue > target {
		*updatedValue = target
		return true
	}
	return false
}

// ChMax accepts a pointer of integer and a target value.
// If target value is LARGER than the first argument,
//	then the first argument will be updated by the second argument.
func ChMax(updatedValue *int, target int) bool {
	if *updatedValue < target {
		*updatedValue = target
		return true
	}
	return false
}

// NthBit returns nth bit value of an argument.
// n starts from 0.
func NthBit(num, nth int) int {
	return num >> uint(nth) & 1
}

// OnBit returns the integer that has nth ON bit.
// If an argument has nth ON bit, OnBit returns the argument.
func OnBit(num, nth int) int {
	return num | (1 << uint(nth))
}

// OffBit returns the integer that has nth OFF bit.
// If an argument has nth OFF bit, OffBit returns the argument.
func OffBit(num, nth int) int {
	return num & ^(1 << uint(nth))
}

// PopCount returns the number of ON bit of an argument.
func PopCount(num int) int {
	res := 0

	for i := 0; i < 70; i++ {
		if ((num >> uint(i)) & 1) == 1 {
			res++
		}
	}

	return res
}

/*********** Arithmetic ***********/

// Max returns the max integer among input set.
// This function needs at least 1 argument (no argument causes panic).
func Max(integers ...int) int {
	m := integers[0]
	for i, integer := range integers {
		if i == 0 {
			continue
		}
		if m < integer {
			m = integer
		}
	}
	return m
}

// Min returns the min integer among input set.
// This function needs at least 1 argument (no argument causes panic).
func Min(integers ...int) int {
	m := integers[0]
	for i, integer := range integers {
		if i == 0 {
			continue
		}
		if m > integer {
			m = integer
		}
	}
	return m
}

// DigitSum returns digit sum of a decimal number.
// DigitSum only accept a positive integer.
func DigitSum(n int) int {
	if n < 0 {
		return -1
	}

	res := 0

	for n > 0 {
		res += n % 10
		n /= 10
	}

	return res
}

// DigitNumOfDecimal returns digits number of n.
// n is non negative number.
func DigitNumOfDecimal(n int) int {
	res := 0

	for n > 0 {
		n /= 10
		res++
	}

	return res
}

// Sum returns multiple integers sum.
func Sum(integers ...int) int {
	s := 0

	for _, i := range integers {
		s += i
	}

	return s
}

// Kiriage returns Ceil(a/b)
// a >= 0, b > 0
func Kiriage(a, b int) int {
	return (a + (b - 1)) / b
}

// PowInt is integer version of math.Pow
// PowInt calculate a power by Binary Power (二分累乗法(O(log e))).
func PowInt(a, e int) int {
	if a < 0 || e < 0 {
		panic(errors.New("[argument error]: PowInt does not accept negative integers"))
	}

	if e == 0 {
		return 1
	}

	if e%2 == 0 {
		halfE := e / 2
		half := PowInt(a, halfE)
		return half * half
	}

	return a * PowInt(a, e-1)
}

// AbsInt is integer version of math.Abs
func AbsInt(a int) int {
	if a < 0 {
		return -a
	}
	return a
}

// Gcd returns the Greatest Common Divisor of two natural numbers.
// Gcd only accepts two natural numbers (a, b >= 1).
// 0 or negative number causes panic.
// Gcd uses the Euclidean Algorithm.
func Gcd(a, b int) int {
	if a <= 0 || b <= 0 {
		panic(errors.New("[argument error]: Gcd only accepts two NATURAL numbers"))
	}
	if a < b {
		a, b = b, a
	}

	// Euclidean Algorithm
	for b > 0 {
		div := a % b
		a, b = b, div
	}

	return a
}

// Lcm returns the Least Common Multiple of two natural numbers.
// Lcd only accepts two natural numbers (a, b >= 1).
// 0 or negative number causes panic.
// Lcd uses the Euclidean Algorithm indirectly.
func Lcm(a, b int) int {
	if a <= 0 || b <= 0 {
		panic(errors.New("[argument error]: Gcd only accepts two NATURAL numbers"))
	}

	// a = a'*gcd, b = b'*gcd, a*b = a'*b'*gcd^2
	// a' and b' are relatively prime numbers
	// gcd consists of prime numbers, that are included in a and b
	gcd := Gcd(a, b)

	// not (a * b / gcd), because of reducing a probability of overflow
	return (a / gcd) * b
}

// Strtoi is a wrapper of `strconv.Atoi()`.
// If `strconv.Atoi()` returns an error, Strtoi calls panic.
func Strtoi(s string) int {
	if i, err := strconv.Atoi(s); err != nil {
		panic(errors.New("[argument error]: Strtoi only accepts integer string"))
	} else {
		return i
	}
}

// PrintIntsLine returns integers string delimited by a space.
func PrintIntsLine(A ...int) string {
	res := []rune{}

	for i := 0; i < len(A); i++ {
		str := strconv.Itoa(A[i])
		res = append(res, []rune(str)...)

		if i != len(A)-1 {
			res = append(res, ' ')
		}
	}

	return string(res)
}

/********** I/O usage **********/

//str := ReadString()
//i := ReadInt()
//X := ReadIntSlice(n)
//S := ReadRuneSlice()
//a := ReadFloat64()
//A := ReadFloat64Slice(n)

//str := ZeroPaddingRuneSlice(num, 32)
//str := PrintIntsLine(X...)

/*******************************************************************/

const MOD = 1000000000 + 7
const ALPHABET_NUM = 26
const INF_INT64 = math.MaxInt64
const INF_BIT60 = 1 << 60

var n, m int

var adjMatrix [10][10]int
var dominoMatrix [7][7]int

func main() {
	n, m = ReadInt2()
	for i := 0; i < m; i++ {
		a, b := ReadInt2()
		// 常にaをbより小さくする
		if a > b {
			a, b = b, a
		}
		adjMatrix[a][b] = 1
		adjMatrix[b][a] = 1
	}
	// for i := 1; i <= n; i++ {
	// 	fmt.Println(adjMatrix[i])
	// }
	// initialize()
	// for i := 1; i <= 6; i++ {
	// 	fmt.Println(dominoMatrix[i])
	// }

	ans := 0
	for i := 1; i <= n; i++ {
		initialize()
		tmp := sub(i)
		ChMax(&ans, tmp)
	}

	fmt.Println(ans)
}

// sevenId を 7としたときの置けるドミノの数
func sub(sevenId int) int {
	res := 0

	// memo: ノードIDからドミノの数へのマップ
	memo := make(map[int]int)
	dominoNum := 1
	for i := 1; i <= n; i++ {
		if i == sevenId {
			continue
		}
		memo[i] = dominoNum
		dominoNum++
	}
	memo[sevenId] = dominoNum
	// memoの逆引き
	revMemo := make(map[int]int)
	for k, v := range memo {
		revMemo[v] = k
	}

	// sevenId以外のノードについて、割り当てられたドミノ数に基づいてドミノをおいていく
	for i := 1; i <= n; i++ {
		for j := i + 1; j <= n; j++ {
			// i, jは通常のノード番号
			if i == sevenId || j == sevenId {
				continue
			}

			if adjMatrix[i][j] == 1 {
				res++

				// i, jをドミノ数に変換
				// diのほうを常に小さく
				di := memo[i]
				dj := memo[j]
				if di > dj {
					di, dj = dj, di
				}
				dominoMatrix[di][dj] = 0 // ドミノを消費
			}
		}
	}

	// i: sevenId のドミノ数をiとする
	resres := 0
	for i := 1; i <= 6; i++ {
		di := i
		tmpRes := 0

		// 他方のノードすべてを調べる
		for j := 1; j <= 6; j++ {
			dj := j
			ni, nj := sevenId, revMemo[dj]
			if di > dj {
				di, dj = dj, di
			}

			// ドミノが未使用かつエッジがアレばドミノを設置できる
			if dominoMatrix[di][dj] == 1 && adjMatrix[ni][nj] == 1 {
				tmpRes++
			}
		}

		ChMax(&resres, tmpRes)
	}

	return res + resres
}

func initialize() {
	for i := 1; i <= 6; i++ {
		for j := i; j <= 6; j++ {
			dominoMatrix[i][j] = 1
		}
	}
}

// MODはとったか？
// 遷移だけじゃなくて最後の最後でちゃんと取れよ？

/*******************************************************************/

package main

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

// github.com/EndlessCheng/codeforces-go
func Sol1230C(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)
	edges := make([][]int, n+1)
	shown := map[int]int{}
	for i := 0; i < m; i++ {
		var v, w int
		Fscan(in, &v, &w)
		edges[v] = append(edges[v], w)
		edges[w] = append(edges[w], v)
		shown[v] = 1
		shown[w] = 1
	}
	if len(shown) <= 6 {
		Fprintln(out, m)
		return
	}
	minDeg := 100
	for _, e := range edges {
		if len(e) > 0 && len(e) < minDeg {
			minDeg = len(e)
		}
	}
	ans := m - minDeg + 1
	for v, e := range edges {
		if len(e) != minDeg {
			continue
		}
		for w, e2 := range edges {
			if len(e2) <= minDeg {
				continue
			}
			delta := 0
			for _, ee := range e2 {
				if ee == v {
					delta = 1
					break
				}
			}
			mp := map[int]int{}
			for _, ee := range append(e, e2...) {
				if ee == v {
					ee = w
				}
				mp[ee] = 1
			}
			if newAns := m - (len(e) + len(e2) - delta - len(mp)); newAns > ans {
				ans = newAns
			}
		}
	}
	Fprintln(out, ans)
}

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

package main

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

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

	var n, m, v, w int
	Fscan(in, &n, &m)
	edges := make([][]int, n+1)
	shown := map[int]int{}
	for i := 0; i < m; i++ {
		Fscan(in, &v, &w)
		edges[v] = append(edges[v], w)
		edges[w] = append(edges[w], v)
		shown[v] = 1
		shown[w] = 1
	}
	if len(shown) <= 6 {
		Fprintln(out, m)
		return
	}
	minDeg := 100
	//var idx int
	for _, e := range edges {
		if len(e) > 0 && len(e) < minDeg {
			minDeg = len(e)
			//idx = i + 1
		}
	}
	ans := m - minDeg + 1
	for _, e := range edges {
		if len(e) != minDeg {
			continue
		}
		for _, e2 := range edges {
			if len(e2) <= minDeg {
				continue
			}
			mp := map[int]int{}
			for _, ee := range append(e, e2...) {
				mp[ee] = 1
			}
			if newAns := m - (len(e) + len(e2) - len(mp)); newAns > ans {
				ans = newAns
			}
		}
	}
	Fprintln(out, ans)
}

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

package main

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

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

	var n, m, v, w int
	Fscan(in, &n, &m)
	edges := make([][]int, n+1)
	shown := map[int]int{}
	for i := 0; i < m; i++ {
		Fscan(in, &v, &w)
		edges[v] = append(edges[v], w)
		edges[w] = append(edges[w], v)
		shown[v] = 1
		shown[w] = 1
	}
	if len(shown) <= 6 {
		Fprintln(out, m)
		return
	}
	minDeg := m
	for _, e := range edges {
		if len(e) > 0 && len(e) < minDeg {
			minDeg = len(e)
		}
	}
	Fprintln(out, m-minDeg+1)
}

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

package main

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

// github.com/EndlessCheng/codeforces-go
func Sol1230C(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)
	edges := make([][]int, n+1)
	shown := map[int]int{}
	for i := 0; i < m; i++ {
		var v, w int
		Fscan(in, &v, &w)
		edges[v] = append(edges[v], w)
		edges[w] = append(edges[w], v)
		shown[v] = 1
		shown[w] = 1
	}
	if len(shown) <= 6 {
		Fprintln(out, m)
		return
	}

	minDeg := 7
	for _, e := range edges {
		if len(e) > 0 && len(e) < minDeg {
			minDeg = len(e)
		}
	}
	ans := 0
	for v, e := range edges {
		if len(e) != minDeg {
			continue
		}
		for w, e2 := range edges {
			if w == v || len(e2) == 0 {
				continue
			}
			delta := 0
			for _, ee := range e2 {
				if ee == v {
					delta = 1
					break
				}
			}
			mp := map[int]int{}
			for _, ee := range append(e, e2...) {
				if ee == v {
					ee = w
				}
				mp[ee] = 1
			}
			delta = len(e) + len(e2) - delta - len(mp)
			if newAns := m - delta; newAns > ans {
				ans = newAns
			}
		}
	}
	Fprintln(out, ans)
}

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

package main

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

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

	var n, m, v, w int
	Fscan(in, &n, &m)
	edges := make([][]int, n+1)
	shown := map[int]int{}
	for i := 0; i < m; i++ {
		Fscan(in, &v, &w)
		edges[v] = append(edges[v], w)
		edges[w] = append(edges[w], v)
		shown[v] = 1
		shown[w] = 1
	}
	if len(shown) <= 6 {
		Fprintln(out, m)
		return
	}
	minDeg := 100
	var idx int
	for i, e := range edges {
		if len(e) > 0 && len(e) < minDeg {
			minDeg = len(e)
			idx = i + 1
		}
	}
	ans := m - minDeg + 1
	for i, e := range edges {
		if i == idx || len(e) != minDeg {
			continue
		}
		for _, e2 := range edges {
			if len(e2) <= minDeg {
				continue
			}
			mp := map[int]int{}
			for _, ee := range append(e, e2...) {
				mp[ee] = 1
			}
			if newAns := m - (len(e) + len(e2) - len(mp)); newAns > ans {
				ans = newAns
			}
		}
	}
	Fprintln(out, ans)
}

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

// Author: sighduck
// URL: https://codeforces.com/contest/1230/problem/C

package main

import (
	"bufio"
	"fmt"
	"os"
)

type Edge struct {
	A int
	B int
}

func min(x int, y int) int {
	if x < y {
		return x
	}
	return y
}

func max(x int, y int) int {
	if x > y {
		return x
	}
	return y
}

func Solve(n int, m int, edges []Edge) int {
	if n < 7 {
		return m
	}
	degreeCount := make([]int, n)
	for _, edge := range edges {
		degreeCount[edge.A-1] += 1
		degreeCount[edge.B-1] += 1
	}
	maxDominoes := 0
	for _, degree := range degreeCount {
		maxDominoes = max(maxDominoes, m-degree+min(degree, 1))
	}
	return min(maxDominoes, 16)
}

func main() {
	reader := bufio.NewReader(os.Stdin)
	writer := bufio.NewWriter(os.Stdout)
	defer writer.Flush()

	var n, m int
	fmt.Fscanf(reader, "%d %d\n", &n, &m)
	edges := make([]Edge, m)
	for i := 0; i < m; i++ {
		fmt.Fscanf(reader, "%d %d\n", &edges[i].A, &edges[i].B)
	}

	fmt.Fprintf(writer, "%d\n", Solve(n, m, edges))
}


One day Vasya came across three Berland coins. They didn't have any numbers that's why Vasya didn't understand how their denominations differ. He supposed that if one coin is heavier than the other one, then it should be worth more. Vasya weighed all the three pairs of coins on pan balance scales and told you the results. Find out how the deminations of the coins differ or if Vasya has a mistake in the weighting results. No two coins are equal.

The input data contains the results of all the weighting, one result on each line. It is guaranteed that every coin pair was weighted exactly once. Vasya labelled the coins with letters «A», «B» and «C». Each result is a line that appears as (letter)(> or < sign)(letter). For example, if coin "A" proved lighter than coin "B", the result of the weighting is A<B.

It the results are contradictory, print Impossible. Otherwise, print without spaces the rearrangement of letters «A», «B» and «C» which represent the coins in the increasing order of their weights.