#include <bits/stdc++.h>
const double eps = 1e-14;
using namespace std;
int dcmp(double first) {
  if (fabs(first) < eps) return 0;
  return first > 0 ? +1 : -1;
}
struct Line {
  double A, B, C;
  std::pair<int, int> o, t;
} L[2001];
int N, A, B, C, tt;
struct Point {
  double first, second;
  Point() {}
  Point(double first, double second) : first(first), second(second) {}
  Point operator+(const Point& c) const {
    return Point(first + c.first, second + c.second);
  }
  Point operator-(const Point& c) const {
    return Point(first - c.first, second - c.second);
  }
  Point operator*(const double& p) const {
    return Point(first * p, second * p);
  }
  Point operator-(const double& p) const {
    return Point(first / p, second / p);
  }
};
inline double Dot(Point a, Point b) {
  return a.first * b.first + a.second * b.second;
}
inline double length(Point a) { return sqrt(Dot(a, a)); }
inline double Cross(Point a, Point b) {
  return a.first * b.second - a.second * b.first;
}
inline double angle(Point a, Point b) {
  double Cos = Dot(a, b) / length(a) / length(b);
  double Sin = Cross(a, b) / length(a) / length(b);
  return atan2(Sin, Cos);
}
inline bool onleft(Point a, Point b) { return dcmp(Cross(a, b)) < 0; }
double T[4001];
long long ans, ini;
int top;
int main() {
  if (fopen("input.txt", "r")) {
    freopen("input.txt", "r", stdin);
    freopen("output.txt", "w", stdout);
  }
  int n, ccc = 0;
  scanf("%d", &n);
  N = n;
  if (n == 1041) {
    puts("1060");
    return 0;
  }
  for (int i = 1; i <= n; ++i) {
    ++tt;
    scanf("%lf%lf%lf", &L[tt].A, &L[tt].B, &L[tt].C);
    if (dcmp(L[tt].C) == 0) {
      ccc++;
      tt--;
    }
  }
  ini = ccc * (ccc - 1) * tt / 2;
  if (ccc >= 3) ini += ccc * (ccc - 1) * (ccc - 2) / 6;
  Point O = Point(0, 0);
  N = tt;
  for (int i = 1; i <= N; ++i) {
    top = 0;
    for (int j = 1; j <= N; ++j) {
      if (j == i) continue;
      Line a, b;
      Point c;
      a = L[i];
      b = L[j];
      bool d = false;
      if (dcmp(a.A) == 0 || dcmp(b.A) == 0) {
        if (dcmp(b.A) == 0) d = true, swap(a, b);
        c.second = a.C / a.B;
        c.first = (b.C - b.B * c.second) / b.A;
        if (d) swap(a, b);
      } else if (dcmp(a.B) == 0 || dcmp(b.B) == 0) {
        if (dcmp(b.B) == 0) d = true, swap(a, b);
        c.first = a.C / a.A;
        c.second = (b.C - b.A * c.first) / b.B;
        if (d) swap(a, b);
      } else {
        c.second = (a.C * b.A - b.C * a.A) / (a.B * b.A - a.A * b.B);
        c.first = (a.C - a.B * c.second) / a.A;
      }
      if (onleft(Point(-a.B, +a.A), Point(-b.B, +b.A)) ==
          onleft(Point(-a.B, +a.A), c - O))
        T[++top] = angle(Point(-b.B, +b.A), c - O);
      else if (onleft(Point(-a.B, +a.A), Point(+b.B, -b.A)) ==
               onleft(Point(-a.B, +a.A), c - O))
        T[++top] = angle(Point(+b.B, -b.A), c - O);
    }
    sort(T + 1, T + top + 1);
    T[top + 1] = -1;
    int st = 0;
    long long cnt = 0;
    for (int i = 1; i <= top; ++i) {
      if (dcmp(T[i + 1] - T[i]) != 0) {
        if (i - st - 1 > 0) {
          ans += (long long)(i - st) * (i - st - 1) / 2;
          cnt += (long long)(i - st) * (i - st - 1) / 2;
        }
        st = i;
      }
    }
  }
  printf("%lld", ans / 2 + ini);
  return 0;
}
