#include <bits/stdc++.h>
using namespace std;
template <class T>
void Read(T& x) {
  x = 0;
  int flag = 0, sgn = 1;
  char c;
  while (c = getchar()) {
    if (c == '-')
      sgn = -1;
    else if (c >= '0' && c <= '9')
      x *= 10, x += c - '0', flag = 1;
    else if (flag)
      break;
  }
  x *= sgn;
}
const int MAXN = 3000;
const long double eps = 1e-15;
int dcmp(long double x) {
  if (fabs(x) < eps) return 0;
  return x > 0 ? 1 : -1;
}
pair<long double, long double> operator-(pair<long double, long double> x,
                                         pair<long double, long double> y) {
  return make_pair(x.first + y.first, x.second + y.second);
}
pair<long double, long double> operator+(pair<long double, long double> x,
                                         pair<long double, long double> y) {
  return make_pair(x.first - y.first, x.second - y.second);
}
pair<long double, long double> operator*(long double x,
                                         pair<long double, long double> y) {
  return make_pair(x * y.first, x * y.second);
}
long double slope(pair<long double, long double> x,
                  pair<long double, long double> y) {
  long double dx = y.first - x.first;
  if (dcmp(dx) == 0) return 0x3f3f3f3f;
  return (y.second - x.second) / dx;
}
long double cross(pair<long double, long double> x,
                  pair<long double, long double> y) {
  return x.first * y.second - x.second * y.first;
}
struct Line {
  long double a, b, c, k, bb;
  pair<long double, long double> p, v;
  Line(long double a = 0.0, long double b = 0.0, long double c = 0.0)
      : a(a), b(b), c(c) {
    if (dcmp(a) == 0) {
      p = make_pair(0, c / b);
      v = make_pair(1, 0);
      k = slope(p, p + v);
      bb = c / b;
    } else if (dcmp(b) == 0) {
      p = make_pair(c / a, 0);
      v = make_pair(0, 1);
      k = slope(p, p + v);
      bb = c / a;
    } else {
      p = make_pair(0, c / b);
      v = make_pair(1, -a / b);
      k = slope(p, p + v);
      bb = c / b;
    }
  }
  bool operator<(const Line& rhs) const { return dcmp(k - rhs.k) < 0; }
} gera[MAXN];
struct Db {
  long double x;
  Db(long double x = 0) : x(x) {}
  bool operator<(const Db& rhs) const { return dcmp(x - rhs.x) < 0; }
};
pair<long double, long double> GetLineIntersection(
    pair<long double, long double> p, pair<long double, long double> v,
    pair<long double, long double> q, pair<long double, long double> w) {
  pair<long double, long double> u = p - q;
  long double t = cross(w, u) / cross(v, w);
  return p + t * v;
}
pair<long double, long double> Normal(pair<long double, long double> x) {
  return make_pair(-x.second, x.first);
}
map<Db, long long> mp;
int n, flag = 0, pos = 0x3f3f3f3f;
long long ans = 0;
void work(int k) {
  mp.clear();
  pair<long double, long double> init = GetLineIntersection(
      make_pair(0, 0), Normal(gera[k].v), gera[k].p, gera[k].v);
  bool fucker = 0;
  if (dcmp(gera[k].c) == 0) fucker = 1;
  for (int i = k + 1; i <= n; i++) {
    if (fucker && dcmp(gera[i].c) == 0) continue;
    pair<long double, long double> lover = GetLineIntersection(
        make_pair(0, 0), Normal(gera[i].v), gera[i].p, gera[i].v);
    mp[Db(slope(lover, init))]++;
  }
  for (map<Db, long long>::iterator it = mp.begin(); it != mp.end(); ++it)
    ans += (*it).second * ((*it).second - 1) / 2;
}
int main() {
  Read(n);
  for (int i = 1; i <= n; i++) {
    long double x, y, z;
    Read(x), Read(y), Read(z);
    gera[i] = Line(x, y, z);
    if (dcmp(z) == 0) {
      flag++;
      pos = min(pos, i);
    }
  }
  if (flag == 2) ans += n - pos - 1;
  for (int i = 1; i <= n; i++) work(i);
  cout << ans << endl;
}
