import Data.List hiding (find)
import Data.Tree
--import Data.Array
import Data.Array.Unboxed
import Data.Array.MArray
import Data.Array.ST
--import Data.Vector as V
import Control.Monad
import Control.Monad.ST
import Control.Monad.State
import Debug.Trace

type Str = UArray Int Char
type Edge = (Int,Int)
type Table = UArray (Int,Int) Int --sl*tl matrix
type DP s = STUArray s (Int,Int) Int

dp_i :: DP s -> Edge -> Int -> ST s ()
dp_i dparr (x,y) n = do
   dpxy <- readArray dparr (x,y)
   -- 閉路はないので以下のことができる
   forM_ [1..n] $ \i -> do
      l  <- readArray dparr (y,i)
      l' <- readArray dparr (x,i)
      if l /= 0 then writeArray dparr (x,i) (max (dpxy+l) l') else return ()
   forM_ [1..n] $ \i -> do
      l  <- readArray dparr (i,x)
      l' <- readArray dparr (i,y)
      if l /= 0 then writeArray dparr (i,y) (max (dpxy+l) l') else return ()

findans :: Table -> Int -> Int -> Int -> Int
findans table n i j | j > n = 0
                    | i > n = findans table n 1 (j+1)
                    | otherwise = max ((table!(i,j))) (findans table n (i+1) j)

solve :: [Edge] -> Int -> Int -> ST s Int
solve ls n m = do
  dparr <- newArray ((1,1),(n,n)) 0
  forM_ ls $ \e -> writeArray dparr e 1
  forM_ ls $ \e -> dp_i dparr e n
  table <- freeze dparr
  let ans = findans table n 1 1
  return ans

main :: IO ()
main = do
  [n,m] <- map read.words <$> getLine
  ls <- replicateM m $ do
    (x:y:e) <- map read.words <$> getLine
    return (x,y)
  putStrLn $ show $ runST $ solve ls n m
