{-# OPTIONS_GHC -O #-}

import Control.Applicative
import Control.Monad
import Control.Monad.ST
import Data.List
import Control.Monad.Trans.State.Strict
import Data.Char (isSpace)
import Data.Maybe (fromJust,fromMaybe,isNothing,isJust)
import qualified Data.ByteString.Lazy.Char8 as B

import Data.Array.ST
import Data.Array.Unsafe
import Data.Array

type Parser a = StateT B.ByteString Maybe a

parseInt :: Parser Int
parseInt = StateT (B.readInt . B.dropWhile isSpace)

parseInt2 :: Parser (Int,Int)
parseInt2 = liftM2 (,) parseInt parseInt

parseInt3 :: Parser (Int,Int,Int)
parseInt3 = liftM3 (,,) parseInt parseInt parseInt

parse :: Parser ((Int,Int), [(Int,Int,Int)])
parse = do
    (n,m)<-parseInt2
    rs<-replicateM m parseInt3
    return ((n,m),rs)

main=do
    ((n,m),rs)<-fromJust.evalStateT parse <$> B.getContents
    let da = wf n rs :: Array (Int,Int) (Maybe Int)
    print $ length $ filter (\(a,b,c)-> fmap (< c) (da!(a,b)) == Just True) rs

wf::Int->[(Int,Int,Int)]->Array (Int,Int) (Maybe Int)
wf n rs = runST $ do
    d <- newArray ((1,1),(n,n)) Nothing :: ST s (STArray s (Int,Int) (Maybe Int))
    forM_ [1..n] $ \v -> writeArray d (v,v) (Just 0)
    forM_ rs $ \(u,v,l) -> writeArray d (u,v) (Just l) >> writeArray d (v,u) (Just l)
    forM_ [(k,i,j)|k<-[1..n],i<-[1..n],j<-[1..n]] $ \(k,i,j) -> do
        dc <- readArray d (i,j)
        dn <- liftA2 (+) <$> readArray d (i,k) <*> readArray d (k,j)
        when (isNothing dc || liftA2 (>) dc dn == Just True) $ writeArray d (i,j) dn
    freeze d
