#include <bits/stdc++.h>
using namespace std;
const int maxn = 2000 + 100;
int n, a[maxn], b[maxn], c[maxn], ac[maxn], bc[maxn], a2b2[maxn];
long long res;
struct Point {
  long long x, y;
};
long long gcd(long long x, long long y) {
  if (y == 0) return x;
  return gcd(y, x % y);
}
Point getStand(Point t) {
  if (t.x == 0) {
    if (t.y == 0) return t;
    t.y = 1;
    return t;
  } else if (t.y == 0) {
    t.x = 1;
    return t;
  }
  long long d = gcd(abs(t.x), abs(t.y));
  t.x /= d;
  t.y /= d;
  if (t.x < 0 && t.y < 0 || t.x > 0 && t.y < 0) {
    t.x = -t.x;
    t.y = -t.y;
  }
  return t;
}
void solve() {
  for (int i = 1; i <= n; i++) {
    ac[i] = a[i] * c[i];
    bc[i] = b[i] * c[i];
    a2b2[i] = a[i] * a[i] + b[i] * b[i];
  }
  res = 0;
  for (int i = 1; i <= n; i++) {
    map<long long, map<long long, long long> > mp;
    int same = 0, ans = 0;
    for (int j = i + 1; j <= n; j++) {
      Point t;
      t.x = (long long)a2b2[j] * (long long)ac[i] -
            (long long)a2b2[i] * (long long)ac[j];
      t.y = (long long)a2b2[j] * (long long)bc[i] -
            (long long)a2b2[i] * (long long)bc[j];
      t = getStand(t);
      if (t.x == 0 && t.y == 0) {
        same++;
        ans += (j - i - 1);
      } else {
        ans += mp[t.x][t.y] + same;
        mp[t.x][t.y]++;
      }
    }
    res += ans;
  }
  printf("%I64d\n", res);
}
int main() {
  while (scanf("%d", &n) != EOF) {
    for (int i = 1; i <= n; i++) {
      scanf("%d%d%d", &a[i], &b[i], &c[i]);
    }
    solve();
  }
  return 0;
}
