#include<cmath>
#include<iostream>
#include<algorithm>
#include<iomanip>
#include<vector>
#include<cassert>

using namespace std;

namespace geometry2d {

	//eps　浮動小数点誤差
	const double EPS = 1e-10;

	//a > 0ならば+1, a == 0ならば0, a < 0ならば-1　を返す。　基本的にEPS込みの評価はこれで行う。
	//不等式は、加減算に直してこれに適用する。
	int sgn(const double a) {
		return (a < -EPS ? -1 : (a > EPS ? +1 : 0));
	}

	struct Point {
		//2次元ベクトルのクラス

		double x, y;
		Point(double _x, double _y) {
			x = _x, y = _y;
		}
		Point() {
			x = 0, y = 0;
		}

		Point operator+() const {
			return *this;
		}
		Point operator-() const {
			return{ -x, -y };
		}
		Point operator+ (const Point& b) const {
			return{ x + b.x, y + b.y };
		}
		Point operator- (const Point& b) const {
			return{ x - b.x, y - b.y };
		}
		Point operator* (const double b) const {
			return{ x * b, y * b };
		}
		Point operator/ (const double b) const {
			return{ x / b, y / b };
		}
		Point operator+= (const Point& b) {
			x += b.x, y += b.y;
			return *this;
		}
		Point operator-= (const Point& b) {
			x -= b.x, y -= b.y;
			return *this;
		}
		Point operator*= (const double b) {
			x *= b, y *= b;
			return *this;
		}
		Point operator/= (const double b) {
			x /= b, y /= b;
			return *this;
		}
		bool operator== (const Point& b) {
			return b.x == x && b.y == y;
		}

		double lengthSquare() const {
			return (x * x + y * y);
		}
		double length() const {
			return std::sqrt(lengthSquare());
		}
		double dot(const Point& b) const {
			return x * b.x + y * b.y;
		}
		double cross(const Point& b) const {
			//Generally, cross product is vector, but in 2D, cross product is also scalar.
			return x * b.y - y * b.x;
		}
		double distanceFrom(const Point& b) const {
			return std::sqrt((x - b.x) * (x - b.x) + (y - b.y) * (y - b.y));
		}
		Point normalized() const {
			return{ x / length(), y / length() };
		}
		bool isZero() const {
			return sgn(x) == 0 && sgn(y) == 0;
		}
		//単位法線ベクトル
		Point normalUnitVector() const {
			return{ -normalized().y, normalized().x };
		}
		//(0, 0)中心にarg(弧度法)回転した座標
		Point rotation(double arg) const {
			double cs = cos(arg), sn = sin(arg);
			return Point(x * cs - y * sn, x * sn + y * cs);
		}
		//(0, 0)中心の円上に乗ってるとしたときの、偏角
		double angle() const {
			return atan2(y, x);
		}
	};

	inline Point operator*(double a, const Point& b) {
		return{ b.x * a, b.y * a };
	}

	template <class Char>
	inline std::basic_ostream<Char>& operator <<(std::basic_ostream<Char>& os, const Point& v)
	{
		return os << Char('(') << v.x << Char(',') << v.y << Char(')');
	}

	template <class Char>
	inline std::basic_istream<Char>& operator >> (std::basic_istream<Char>& is, Point& v)
	{
		return is >> v.x >> v.y;
	}

	//幾何ライブラリにおいて、解なしのような状態の時に返される点の値
	const Point error_val = { 114514.0, -191981.0 };

	inline bool operator==(const Point& a, const Point& b) {
		return (sgn(a.x - b.x) == 0 && sgn(a.y - b.y) == 0);
	}

	//第一にx, 第二にyを小さい順にソートするためのオーバーロード
	inline bool operator<(const Point& a, const Point& b) {
		if (sgn(a.x - b.x) != 0)return sgn(a.x - b.x) < 0;
		else sgn(a.y - b.y) < 0;
	}

	/*
	3点A, B, Cの位置関係を返す関数 A, Bがすべて異なった点であるのが前提
	//ABから見てBCは左に曲がるのなら +1
	//ABから見てBCは右に曲がるのなら -1
	//ABC(CBA)の順番で一直線上に並ぶなら +2
	//ACB(BCA)の順番で一直線上に並ぶなら 0
	BAC(CAB)の順番で一直線上に並ぶなら -2
	*/
	int iSP(const Point& a, const Point& b, const Point& c) {
		int flg = sgn((b - a).cross(c - a));
		if (flg == 1) {
			return +1;
		}
		else if (flg == -1) {
			return -1;
		}
		else {
			//ABC(CBA)
			if (sgn((b - a).dot(c - b)) > 0)
				return +2;
			//BAC(CAB)
			else if (sgn((a - b).dot(c - a)) > 0)
				return -2;
			//ACB(BCA)　CがA or Bと一致しても、こっちに含まれる。
			else
				return 0;
		}
	}

	//直線に関する操作や判定
	namespace line2d {

		//向きつき直線を想定。Segment
		struct Line {
			
			//直線の通る二点。有向直線でないのならば、beginやendに違いはない。
			Point begin, end;

			Line() {
				begin = Point(), end = Point();
			}

			Line(const Point& b, const Point& e) {
				begin = b, end = e;
			}

			//ax+by+c=0
			Line(const double a, const double b, const double c) {
				if (sgn(a) == 0 && sgn(b) == 0) {
					assert(-1);
				}

				if (sgn(b) == 0) {
					//ax+c=0になる。
					begin = Point(-c / a, 0.0);
					end = Point(-c / a, 1.0);
				}
				else {
					//y=-(ax+c)/b　傾きは-a/bで、y切片が-c/b
					begin = Point(0, -c / b);
					end = Point(1.0, -(a + c) / b);
				}
			}

			Point vec() const {
				return end - begin;
			}

		};


		//線分の場合、Lineと同じものになるが、はっきりと区別する
		typedef Line Segment;


		//直線の交点を返す。交わってなければ、error_valを返す。
		Point lineIntersection(const Line& l1, const Line& l2) {
			if (sgn(l1.vec().cross(l2.vec())) == 0)return error_val;

			Point ret;
			ret = l1.begin + l1.vec() * 
				abs((l2.end - l1.begin).cross(l2.vec()) / l1.vec().cross(l2.vec()));
			return ret;
		}

		//線分が共通部分を持つかどうか？と線分の交点を返す。共通部分がない、もしくは交点が一意ではないなら、error_valを返す。
		//trueなら、共通部分を持つ。falseなら、共通部分を持たない。
		pair<bool, Point> segmentIntersection(const Segment& s1, const Segment& s2) {

			if (iSP(s1.begin, s1.end, s2.begin) * iSP(s1.begin, s1.end, s2.end) <= 0 &&
				iSP(s2.begin, s2.end, s1.begin) * iSP(s2.begin, s2.end, s1.end) <= 0) {

				//平行ならば、交点は定まらない。(完全に重なってるので)
				if (s1.vec().cross(s2.vec()) == 0)
					return make_pair(true, error_val);
				else //そうでないのなら、lineIntersection()で交点を返しておく。
					return make_pair(true, lineIntersection(s1, s2));

			}
			return make_pair(false, error_val);
		}


		//点と直線の距離。引数は、点、直線上の2点
		double distanceBetweenPointAndLine(const Point& p, const Line& l) {
			return abs(l.vec().cross(p - l.begin) / l.vec().length());
		}

		//点と線分の距離。引数は、点、線分の両端
		double distanceBetweenPointAndSegment(const Point& p, const Segment& s) {
			if (sgn(s.vec().dot(p - s.begin)) < 0 || sgn(s.vec().dot(p - s.end)) < 0) {
				//下した垂線は線分の上にはない
				return min(p.distanceFrom(s.begin), p.distanceFrom(s.end));
			}
			return abs(s.vec().cross(p - s.begin) / s.vec().length());
		}

		//二線分間の距離
		double distanceBetweenSegmentAndSegment(const Segment& s1, const Segment& s2) {
			if (segmentIntersection(s1, s2).first)return 0;//交点を持つ

			double ans = distanceBetweenPointAndSegment(s1.begin, s2);
			ans = min(ans, distanceBetweenPointAndSegment(s1.end, s2));
			ans = min(ans, distanceBetweenPointAndSegment(s2.begin, s1));
			ans = min(ans, distanceBetweenPointAndSegment(s2.end, s1));
			return ans;
		}

		//正射影
		//引数は点A, B, Cで、BAベクトルの直線BC上の正射影を求める。
		Point projection(const Point& a, const Line& l) {
			Point ret;
			ret = l.begin + 
				l.vec().normalized() * (a - l.begin).dot(l.vec()) / l.vec().length();
			return ret;
		}

		//鏡映変換。引数は点A, B, Cで、直線BCにおいて、Aと線対称な点を求める。
		Point reflection(const Point& a, const Line& l) {
			Point ret;
			ret = a + 2 * (projection(a, l) - a);
			return ret;
		}
	}

}

int main() {
	int q;
	cin >> q;
	while (q--) {
		geometry2d::line2d::Line s1, s2;
		cin >> s1.begin >> s1.end >> s2.begin >> s2.end;
		if (geometry2d::sgn(s1.vec().dot(s2.vec())) == 0) {
			cout << 1 << endl;
		}
		else if (geometry2d::sgn(s1.vec().cross(s2.vec())) == 0) {
			cout << 2 << endl;
		}
		else
			cout << 0 << endl;
	}
	return 0;
}


Measured memory_bytes: 3236