#include <bits/stdc++.h>
using namespace std;
template <class T>
inline void RST(T &A, char x = 0) {
  memset(A, x, sizeof(A));
}
template <class T>
inline void SRT(T &A) {
  sort(A.begin(), A.end());
}
template <class T, class C>
inline void SRT(T &A, C B) {
  sort(A.begin(), A.end(), B);
}
const int DX[4] = {1, -1, 0, 0};
const int DY[4] = {0, 0, -1, 1};
const int inf = 0x3f3f3f3f;
const long long linf = 1LL << 60;
const double eps = 1e-9;
const double oo = 1e70;
const double pi = acos(-1.0);
inline double dcmp(const double &x) { return (x > eps) - (x < -eps); }
inline double dcmp(const double &x, const double &y) { return dcmp(x - y); }
template <class T>
inline void RD(T &x) {
  char c = getchar();
  x = 0;
  int flg = 0;
  while (c < '0' || c > '9') flg |= c == '-', c = getchar();
  while (c >= '0' && c <= '9') x = (x << 1) + (x << 3) + c - '0', c = getchar();
  if (flg) x = -x;
}
template <class T1, class T2>
inline void RD(T1 &x, T2 &y) {
  RD(x);
  RD(y);
}
template <class T1, class T2, class T3>
inline void RD(T1 &x, T2 &y, T3 &z) {
  RD(x);
  RD(y);
  RD(z);
}
template <class T1, class T2, class T3, class T4>
inline void RD(T1 &x, T2 &y, T3 &z, T4 &w) {
  RD(x);
  RD(y);
  RD(z);
  RD(w);
}
inline long long RD() {
  long long x;
  RD(x);
  return x;
}
inline void OT(int x) { printf("%d\n", x); }
inline void OT(long long x) { printf("%I64d\n", x); }
inline void OT(int x, int y) { printf("%d %d\n", x, y); }
inline void OT(long long x, long long y) { printf("%I64d %I64d\n", x, y); }
inline void setIO(char *in, char *out) {
  if (in) freopen(in, "r", stdin);
  if (out) freopen(out, "w", stdout);
}
template <class T>
inline void upmax(T &ans, const T &x) {
  ans = max(ans, x);
}
template <class T>
inline void upmin(T &ans, const T &x) {
  ans = min(ans, x);
}
inline char getAlpha() {
  char c = getchar();
  while (c < 'A' || c > 'Z') c = getchar();
  return c;
}
inline char getalpha() {
  char c = getchar();
  while (c < 'a' || c > 'z') c = getchar();
  return c;
}
const int maxn = 1000 + 5;
const int maxm = 500000 + 5;
const int mod = 1000000007;
struct Point {
  double x, y;
  Point() {}
  Point(double x, double y) : x(x), y(y) {}
  inline Point operator+(const Point &b) const {
    return Point(x + b.x, y + b.y);
  }
  inline Point operator-(const Point &b) const {
    return Point(x - b.x, y - b.y);
  }
  inline Point operator/(const double &b) const { return Point(x / b, y / b); }
  inline Point operator*(const double &b) const { return Point(x * b, y * b); }
  inline double operator^(const Point &b) const { return x * b.x + y * b.y; }
  inline bool operator==(const Point &b) const {
    return !dcmp(x - b.x) && !dcmp(y - b.y);
  }
  inline bool operator<(const Point &b) const {
    return dcmp(x - b.x) < 0 || (!dcmp(x - b.x) && dcmp(y - b.y) < 0);
  }
} p[maxn];
struct Info {
  Point p;
  double rad;
  Info() {}
  Info(const Point &p, const double &rad) : p(p), rad(rad) {}
  inline bool operator<(const Info &b) const {
    return p < b.p || (p == b.p && dcmp(rad - b.rad) < 0);
  }
} c[maxm];
int N, tot;
int main() {
  RD(N);
  for (int i = 1; i <= int(N); i++) {
    int a, b, c, d;
    RD(a, b, c, d);
    p[i] = Point(double(a) / b, double(c) / d);
    p[i] = p[i] / (p[i] ^ p[i]);
  }
  for (int i = 1; i <= int(N); i++)
    for (int j = int(i + 1); j <= int(N); j++) {
      Point mid = (p[i] + p[j]) / 2, v = (p[i] - p[j]) / 2;
      double ang = atan2(v.y, v.x);
      if (dcmp(ang) < 0) ang += pi;
      if (dcmp(ang - pi) >= 0) ang -= pi;
      c[++tot] = Info(mid, ang);
    }
  sort(c + 1, c + 1 + tot);
  long long ans = 0;
  for (int i = 1, j; i <= tot; i = j) {
    long long s1 = 0, s2 = 0;
    for (j = i; j <= tot && c[j].p == c[i].p;) {
      int k = j;
      while (j <= tot && !(c[k] < c[j])) ++j;
      s2 = (s2 + (s1 + s2) * (j - k)) % mod, s1 += j - k;
    }
    ans = (ans + s2) % mod;
  }
  OT(ans);
  return 0;
}
