#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
typedef long long ll ;
#define rep(i, a, b) for (int i = a; i <= b; ++ i) 
const int N = 405, M = 1e5 + 5, inf = 1e9 + 7 ;
using namespace std ;
bool vis[N] ; int opt[M << 3] ;
int n, m, e, ter[M << 1], nxt[M << 1], lnk[N], A[N], G[N][N] ;
struct poi { int x, y ; } a[M] ;
void add(int x, int y) {
	ter[++ e] = y, nxt[e] = lnk[x], lnk[x] = e ;
}
inline bool chk(int u, int y) {
	if (A[u] <= m) {
		if (y <= m) return (A[u] == y) ; else {
			if (y == inf) return false ;
			return (y - m >= A[u]) ;
		}
	} else {
		if (A[u] == inf) {
			A[u] = y ; 
			return true ;
		}
		if (y <= m) {
			if (A[u] - m < y) return false ;
			A[u] = y ;
			return true ;
		} else {
			if (y == inf) return false ;
			A[u] = min(A[u], y) ;
			return true ;
		}
	}
}
bool dfs(int u) {
	if (A[u] <= m) {
		for (int i = lnk[u] ; i; i = nxt[i]) if (ter[i] != A[u]) {
			int x = ter[i], v = (u == a[x].x ? a[x].y : a[x].x) ;
			int tmp = A[v] ;
			if (x < A[u]) {
				if (!chk(v, x)) return false ;
				if (A[v] != tmp) {
					if (!dfs(v)) return false ;
				}
			} else {
				if (!chk(v, x + m)) return false ;
				if (A[v] != tmp) {
					if (!dfs(v)) return false ;
				}
			}
		}
	} else {
		for (int i = lnk[u] ; i; i = nxt[i]) if (ter[i] > A[u] - m) {
			int x = ter[i], v = (u == a[x].x ? a[x].y : a[x].x) ;
			int tmp = A[v] ;
			if (!chk(v, x + m)) return false ;
			if (A[v] != tmp) {
				if (!dfs(v)) return false ;
			}
		}
	}
	return true ;
}
bool check(int u) {
	rep(i, 1, n) vis[i] = true ;
	int he = 0, ta = 0 ;
	for (int i = lnk[u] ; i; i = nxt[i]) {
		int x = ter[i], v = (u == a[x].x ? a[x].y : a[x].x) ;
		if (!chk(u, inf) || !chk(v, x)) return false ;
		if (vis[v]) vis[v] = false, opt[++ ta] = v ;
	}
	for ( ; he != ta ; ) {
		int u = opt[++ he] ;
		if (A[u] <= m) {
			for (int i = lnk[u] ; i; i = nxt[i]) if (ter[i] != A[u]) {
				int x = ter[i], v = (u == a[x].x ? a[x].y : a[x].x) ;
				int tmp = A[v] ;
				if (x < A[u]) {
					if (!chk(v, x)) return false ;
					if (A[v] != tmp) {
						if (vis[v]) vis[v] = false, opt[++ ta] = v ;
					}
				} else {
					if (!chk(v, x + m)) return false ;
					if (A[v] != tmp) {
						if (vis[v]) vis[v] = false, opt[++ ta] = v ;
					}
				}
			}
		} else {
			for (int i = lnk[u] ; i; i = nxt[i]) if (ter[i] > A[u] - m) {
				int x = ter[i], v = (u == a[x].x ? a[x].y : a[x].x) ;
				int tmp = A[v] ;
				if (!chk(v, x + m)) return false ;
				if (A[v] != tmp) {
					if (vis[v]) vis[v] = false, opt[++ ta] = v ;
				}
			}
		}	
		vis[u] = true ;
	}
	return chk(u, inf) ;
}
inline bool ok(int u, int v) {
	if (G[u][v] != inf || G[v][u] != inf || G[u][u] != inf || G[v][v] != inf) return false ;
	rep(i, 1, n) if (i != u && i != v) {
		int x = G[u][i], y = G[v][i] ;
		if (x > y) swap(x, y) ;
		if (x == inf) continue ;
		if (x <= m && y <= m) {
			if (x == y) continue ; else return false ;
		} else 
		if (x <= m && y > m) {
			if (x <= y - m) continue ; else return false ;
		} 
	}
	return true ;
}
int main() {
	scanf("%d%d", &n, &m) ;
	rep(i, 1, m) scanf("%d%d", &a[i].x, &a[i].y), add(a[i].x, i), add(a[i].y, i) ;
	rep(u, 1, n) {
		rep(i, 1, n) A[i] = inf ;
		if (!check(u)) A[u] = 0 ; 
		rep(i, 1, n) G[u][i] = A[i] ;
	}
	int ans = 0 ;
	rep(i, 1, n) rep(j, i + 1, n) if (ok(i, j)) ans ++ ;
	printf("%d\n", ans) ;
	return 0 ; 	
}

Measured memory_bytes: 4352