#include <iostream>
#include <iomanip>
#include <vector>
#include <map>
#include <set>
#include <deque>
#include <stack>
#include <queue>
#include <algorithm>
#include <numeric>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <string>
#include <cstring>
#include <ctime>
#include <cassert>
#include <climits>

using namespace std;

typedef long long lint;

//定数
const double EPS = 1e-10;
const int dx[] = {-1, 0, 1, 0};
const int dy[] = {0, 1, 0, -1};
const int INF = 1001001001;
const lint INFLL = 1001001001001001001ll;

//初期化関連
#define zclear(a) memset((a), 0 ,sizeof(a))
#define mclear(a) memset((a), -1, sizeof(a))

//デバッグ出力
#define show(x)  cerr << #x << " = " << (x) << endl;
#define debug(x) cerr << #x << " = " << (x) << " (L" << __LINE__ << ")" << " " << __FILE__ << endl;

bool eq(double a, double b) //a == b
{
    return (fabs(a - b) <= EPS);
}

bool neq(double a, double b) //a != b
{
    return (!eq(a, b));
}
  
bool lt(double a, double b) //a < b
{
    return (a - b < -EPS);
}
  
bool leq(double a, double b) //a <= b
{
    return (lt(a, b) || eq(a, b));
}
  
bool gt(double a, double b) //a > b
{
    return (a - b > +EPS);
}
  
bool geq(double a, double b) //a >= b
{
    return (gt(a, b) || eq(a, b));
}

class Point{
public:
	double x, y;
	Point(double x = 0, double y = 0): x(x), y(y){}
	Point operator + (Point p){return Point(x + p.x, y + p.y);}
	Point operator - (Point p){return Point(x - p.x, y - p.y);}
	Point operator * (double a){return Point(x * a, y * a);}
	Point operator / (double a){return Point(x / a, y / a);}
	Point operator * (const Point &a){
		return Point(x * a.x - y * a.y, x * a.y + y * a.x);
	}
	bool operator < (const Point &p) const {
		return (x != p.x ? x < p.x : y < p.y);
	}
	bool operator == (const Point &p) const {
		return (fabs(x - p.x) < EPS && fabs(y - p.y) < EPS);
	}
};

typedef Point Vector;
typedef vector<Point> Polygon;

double norm(Vector a){return (a.x * a.x + a.y * a.y);}
double abs(Vector a){return (sqrt(norm(a)));}
double abs(Vector a, Vector b){return (sqrt(norm(a - b)));}
double dot(Vector a, Vector b){return (a.x * b.x + a.y * b.y);}
double cross(Vector a, Vector b){return (a.x * b.y - a.y * b.x);}
double atan(Vector a){return atan2(a.y, a.x);}
bool sortX(Point a, Point b){return (a.x != b.x ? lt(a.x, b.x) : lt(a.y, b.y));}
bool sortY(Point a, Point b){return (a.y != b.y ? lt(a.y, b.y) : lt(a.x, b.x));}
Point baseP;
bool sortT(Point a, Point b){return (gt(cross(a - baseP, b - baseP), 0));}

int ccw(Point p0, Point p1, Point p2)
{
	Vector a = p1 - p0;
	Vector b = p2 - p0;
	if (cross(a, b) > EPS) return (+1); //p0, p1, p2 が反時計周り
	if (cross(a, b) < -EPS) return (-1);//p0, p1, p2 が時計回り
	if (dot(a, b) < -EPS) return (+2); //p2--p0--p1 の順で一直線上
	if (norm(a) < norm(b)) return (-2); //p0--p1--p2 の順で一直線上
	return (0); //p0--p2--p1 の順で一直線上
}

Polygon normalize(Polygon &v)
{
    int pt = 0;
    for (int i = 1; i < v.size(); i++){
        if (v[pt].y > v[i].y || (v[pt].y == v[i].y && v[pt].x > v[i].x)){
            pt = i;
        }
    }
    swap(v[pt], v[0]);
    baseP = v[0];
    sort(v.begin() + 1, v.end(), sortT);
    return (v);
}

Polygon conhel(Polygon& ps)
{
    sort(ps.begin(), ps.end());
    int k = 0, n = (int)ps.size();
    Polygon qs(n * 2);
      
    for (int i = 0; i < n; i++){
      while (k > 1 && cross(qs[k - 1] - qs[k - 2], ps[i] - qs[k - 1]) < EPS) k--;
      qs[k++] = ps[i];
    }
  
    for (int i = n - 2, t = k; i >= 0; i--){
      while (k > t && cross(qs[k - 1] - qs[k - 2], ps[i] - qs[k - 1]) < EPS) k--;
      qs[k++] = ps[i];
    }
  
    qs.resize(k - 1);
    return (qs);
}

double getArea(Polygon t){
    double ans = 0.0;
	t = normalize(t);
    for (unsigned int i = 0; i < t.size(); i++)
        ans += cross(t[i] , t[(i + 1) % t.size()]);
    return (fabs(ans) / 2);
}

bool isIntersectLL(Point p1, Point p2, Point p3, Point p4)
{// 同一直線の場合は交差していると判定する.
	return (abs(cross(p2 - p1, p4 - p3)) > EPS || 0);// 傾きが異なる
//		abs(cross(p2 - p1, p4 - p1)) < EPS); // 同じ直線である
}
bool isIntersectLS(Point p1, Point p2, Point p3, Point p4)
{// 同一直線上にある場合も交差と判定
	return (cross(p2 - p1, p3 - p1) * cross(p2 - p1, p4 - p1) < EPS);
}
bool isIntersectLP(Point p1, Point p2, Point p)
{
	return (neq(abs(ccw(p1, p2, p)), 1));
}
bool isIntersectSS(Point p1, Point p2, Point p3, Point p4)
{
    return (ccw(p1, p2, p3) * ccw(p1, p2, p4) <= 0 &&
             ccw(p3, p4, p1) * ccw(p3, p4, p2) <= 0);
}
bool isIntersectSP(Point p1, Point p2, Point p)
{
	return (eq(ccw(p1, p2, p), 0));
}
bool isInsideCP(Point c, double r, Point p)
{
	return (leq(abs(c - p), r));
}
bool isInsideCC(Point c1, double r1, Point c2, double r2)
{
	return (leq(abs(c1 - c2), r1 + r2) && geq(abs(c1 - c2), abs(r1 - r2)));
}
bool isInsidePolyP(Polygon gon, Point p)
{
    for (int i = 0; i < gon.size(); i++){
        if (ccw(gon[i], gon[(i + 1) % gon.size()], p) == -1) return (false);
    }
    return (true);
}

Point projection(Point p1, Point p2, Point p3) //直線p1p2 上への写像
{
	double t = dot(p3 - p1, p2 - p1) / norm(p2 - p1);
	return (p1 + (p2 - p1) * t);
}
Point reflection(Point p1, Point p2, Point p3) //直線p1p2 上への反射
{
	return (p3 + (projection(p1, p2, p3) - p3) * 2);
}
double distanceLP(Point p1, Point p2, Point p)
{
	return (abs(p - projection(p1, p2, p)));
}
double distanceLL(Point l1, Point l2, Point m1, Point m2)
{
	return (isIntersectLL(l1, l2, m1, m2) ? 0 : distanceLP(l1, l2, m1));
}
double distanceLS(Point l1, Point l2, Point s1, Point s2)
{
    if (isIntersectLS(l1, l2, s1, s2)) return (0);
	return (min(distanceLP(l1, l2, s1), distanceLP(l1, l2, s2)));
}
double distanceSP(Point s1, Point s2, Point p)
{
	Point r = projection(s1, s2, p);
	if (isIntersectSP(s1, s2, r)) return (abs(r - p));
	return (min(abs(s1 - p), abs(s2 - p)));
}
double distanceSS(Point s1, Point s2, Point t1, Point t2){
	if (isIntersectSS(s1, s2, t1, t2)) return (0);
	return (min(min(distanceSP(s1, s2, t1), distanceSP(s1, s2, t2)),
			min(distanceSP(t1, t2, s1), distanceSP(t1, t2, s2))));
}
Point interPointLL(Point a1, Point a2, Point b1, Point b2)
{
	double d = cross(a2 - a1, b2 - b1);
	if (abs(d) < EPS) throw "Segment is Paralell!";
	Point a = a2 - a1, b = b2 - b1;
    double t = cross(b, b1 - a1) / cross(b, a);
    return (a1 + a * t);
}
vector<Point> interPointCC(Point c1, double r1, Point c2, double r2)
{
	vector<Point> res;
	if (abs(c1 - c2) < EPS) return (vector<Point>()); // 交点が絶対にない
	double d = abs(c1 - c2);
	double rc = (d * d + r1 * r1 - r2 * r2) / (2 * d);
	double rs = sqrt(r1 * r1 - rc * rc);
	Point diff = (c2 - c1) / d;
	res.push_back(Point(c1 + diff * Point(rc, rs)));
	res.push_back(Point(c1 + diff * Point(rc, -rs)));
	return (res);
}
vector<Point> crosspointCL(Point a, double ra, Point b1, Point b2)
{
	vector<Point> res;
	double dist = distanceLP(b1, b2, a);
	if (leq(dist, ra)){
		Point s = projection(b1, b2, a);
		dist = sqrt(ra * ra - dist * dist);
		Point t = (b2 - b1) / abs(b2 - b1);
		res.push_back(s + t * dist);
		res.push_back(s - t * dist);
	}
	return (res);
}

Point unitVector(Vector t)
{
    double u = abs(t);
    return (Point(t.x / u , t.y / u));
}

Point normalVector(Point p, Point a, Point b)
{
    Point v = unitVector(b - a);
    v = cross(v, p - a) > 0 ? Point(v.y, -v.x) : Point(-v.y, v.x);
    return v * distanceLP(p, a, b);
}
  
double getCos(Vector a, Vector b)
{
    return (dot(a, b) / (abs(a) * abs(b)));
}
  
double getSin(Vector a, Vector b)
{
    double t = getCos(a, b);
    return (sqrt(1.0 - t * t));
}
  
double getArg(Vector v)
{
    return (atan2(v.y, v.x));
}

Point rotate(Point t, Point p, double r)
{
    double ta = cos(r) * (t.x - p.x) - sin(r) * (t.y - p.y) + p.x;
    double tb = sin(r) * (t.x - p.x) + cos(r) * (t.y - p.y) + p.y;
    return (Point(ta, tb));
}

int main()
{
	int n;
	
	scanf("%d", &n);
	
	for (int i = 0; i < n; i++){
		Point p0, p1, p2, p3;
		scanf("%lf %lf %lf %lf %lf %lf %lf %lf", &p0.x, &p0.y, &p1.x, &p1.y, &p2.x, &p2.y, &p3.x, &p3.y);
		if (!isIntersectLL(p0, p1, p2, p3)) printf("2\n");
		else {
			if (eq(dot(p1 - p0, p3 - p2), 0)) printf("1\n");
			else printf("0\n");
		}
	}
	
	return (0);
}

Measured memory_bytes: 1208