#include<bits/stdc++.h>
using namespace std;
#include <ext/pb_ds/assoc_container.hpp>
using namespace __gnu_pbds;
#define int long long
#define endl "\n"
#define Endl "\n"
#define tests int t1234;cin>>t1234;for(int i1234=1;i1234<=t1234;i1234++)
#define ll long long
#define fastio ios_base::sync_with_stdio(false);cin.tie(nullptr);std::cout << std::fixed;std::cout << setprecision(20);
#define takearr(a,n) ll a[n];for(ll i=0;i<n;i++)cin>>a[i]
#define pii pair<int,int>
#define F first
#define S second
#define rep(i,n) for( int i=0;i<(int)n;i++)
#define f(i,a,b) for( int i=(int)a;i<=(int)b;i++)
#define mods ((1000000007))
#define init1d(arr,n,val) for(int i=0;i<n;i++)arr[i]=val;
#define init2d(arr,x,y,val) for(int i=0;i<x;i++)for(int j=0;j<y;j++)arr[i][j]=val;
#define pb push_back
#define mass(vec) (int)vec.size()
#define lb(vec,n,val) ((int)(lower_bound(vec,vec+n,val)-vec))
#define ub(arr,n,val) ((int)(upper_bound(arr,arr+n,val)-arr))


typedef tree<int,null_type, less<>, rb_tree_tag,tree_order_statistics_node_update> oset;
//order_of_key   *find_by_order
typedef tree<int,int, less<>, rb_tree_tag,tree_order_statistics_node_update> omap;
#define dbg(...) cerr << "[" << #__VA_ARGS__ << "]: ", cerr << to_string(__VA_ARGS__) << endl<<endl
template <typename T, size_t N> int SIZE(const T (&t)[N]){ return N; } template<typename T> int SIZE(const T &t){ return t.size(); }string to_string(string &s, int x1=0, int x2=1e9){ return '"' + ((x1 < s.size()) ? s.substr(x1, x2-x1+1) : "") + '"'; }string to_string(const char* s) { string tmp(s); return to_string(tmp); } string to_string(bool b) { return (b ? "true" : "false"); } string to_string(char c){ return string({c}); }template<size_t N> string to_string(bitset<N> &b, int x1=0, int x2=1e9){ string t = ((x1 < b.size()) ? (b.to_string()).substr(x1, x2-x1+1) : "");reverse(begin(t), end(t)); return '"' + t + '"'; }template <typename A, typename... C> string to_string(A (&v), int x1=0, int x2=1e9, C... coords);int l_v_l_v_l = 0, t_a_b_s = 0;template <typename A, typename B> string to_string(pair<A, B> &p) { l_v_l_v_l++; string res = "(" + to_string(p.first) + ", " + to_string(p.second) + ")";l_v_l_v_l--; return res; } template <typename A, typename... C> string to_string(A (&v), int x1, int x2, C... coords) { int rnk = rank<A>::value; string tab(t_a_b_s, ' ');string res = ""; bool first = true; if(l_v_l_v_l == 0) res += endl; res += tab + "["; x1 = min(x1, SIZE(v)), x2 = min(x2, SIZE(v)); auto l = begin(v); advance(l, x1);auto r = l; advance(r, (x2-x1) + (x2 < SIZE(v))); for (auto e = l; e != r; e = next(e)) { if (!first) { res += ", "; } first = false; l_v_l_v_l++;if(e != l){ if(rnk > 1) { res += endl; t_a_b_s = l_v_l_v_l; }; } else{ t_a_b_s = 0; } res += to_string(*e, coords...); l_v_l_v_l--; } res += "]";if(l_v_l_v_l == 0) res += endl; return res; }
const int N=5*(1<<17);

int32_t main()
{
  fastio;
  int n,d;cin>>n>>d;
  vector<int>vec[n];
  rep(i,n)
    {
    rep(j,d){int x;cin>>x;vec[i].pb(x);}
    }
    int anss=0;
  rep(i,n)
    {

    f(j,i+1,n-1)
      {
        int ans=0;
      rep(kk,d)ans+=((vec[i][kk]-vec[j][kk])*(vec[i][kk]-vec[j][kk]));
      int ans1=ans-1;ans1=ans&ans1;int aa=sqrt(ans);
        if(ans==aa*aa)anss++;
        //cout<<ans<<endl;
      }

    }
    cout<<anss<<endl;
}




Measured memory_bytes: 256