#include <bits/stdc++.h>
using namespace std;
inline long long gcd(long long a, long long b) { return a ? gcd(b % a, a) : b; }
struct Frac {
  long long a, b;
  inline void reduce() {
    long long g = gcd(a, b);
    if (g) a /= g, b /= g;
    if (b < 0 || (b == 0 && a < 0)) a *= -1, b *= -1;
  }
  inline Frac operator-(Frac o) { return {a * o.b - o.a * b, b * o.b}; }
  inline bool operator==(Frac o) const { return (a == o.a && b == o.b); }
  inline bool operator<(Frac o) const { return (a == o.a) ? b < o.b : a < o.a; }
};
struct Point {
  Frac x, y;
  inline void reduce() {
    x.reduce();
    y.reduce();
  }
  inline void fix() {
    reduce();
    x.a *= y.b;
    y.a *= x.b;
    x.b = y.b = 0;
    long long g = gcd(x.a, y.a);
    if (g) x.a /= g, y.a /= g;
    if (x.a < 0 || (x.a == 0 && x.b < 0)) x.a *= -1, y.a *= -1;
  }
  inline Point operator-(Point o) { return {x - o.x, y - o.y}; }
  inline bool operator==(Point o) { return (x == o.x && y == o.y); }
  inline bool operator<(Point o) const {
    return (x == o.x) ? y < o.y : x < o.x;
  }
};
int N;
vector<Point> points;
map<Point, int> has;
int main() {
  scanf("%d", &N);
  for (int i = 0; i < N; i++) {
    int a, b, c;
    scanf("%d%d%d", &a, &b, &c);
    Point cur = {{c * a, a * a + b * b}, {c * b, a * a + b * b}};
    cur.reduce();
    points.push_back(cur);
  }
  int ans = 0;
  for (int i = 0; i < N; i++) {
    has.clear();
    int same = 0;
    for (int j = i + 1; j < N; j++) {
      if (points[i] == points[j])
        same += 1;
      else {
        Point p = points[j] - points[i];
        p.fix();
        has[p] += 1;
      }
    }
    ans += same * (same - 1) / 2;
    for (auto& p : has) {
      ans += p.second * (p.second - 1) / 2;
      ans += same * p.second;
    }
  }
  printf("%d\n", ans);
  return 0;
}
