#include <bits/stdc++.h>
using namespace std;
#define repr(i,a,b) for(int i=a;i<b;i++)
#define rep(i,n) for(int i=0;i<n;i++)
#define reprrev(i,a,b) for(int i=b-1;i>=a;i--) // [a, b)
#define reprev(i,n) reprrev(i,0,n)
typedef long long ll;
typedef unsigned long long ull;
template<class T> inline bool chmax(T& a, T b) { if (a < b) { a = b; return true; } return false; }
template<class T> inline bool chmin(T& a, T b) { if (a > b) { a = b; return true; } return false; }

/* attention
    long longのシフト演算には気をつけよう
    タイポした時のデバッグが死ぬほどきつくなるので変数名は最低3字くらい使った方がいいかも
    sizeは(int)とキャストしよう
    cin.tie(0);
    ios::sync_with_stdio(false);<- これら、printfとかと併用しない方が良さそう

*/

const ll mod = 1e9+7;

void chmod(ll &M){
    if(M >= mod) M %= mod;
    else if(M < 0){
        M += (abs(M)/mod + 1)*mod;
        M %= mod;
    }
}

ll modpow(ll x, ll n){
    if(n==0) return 1;
    ll res=modpow(x, n/2);

    if(n%2==0) return res*res%mod;
    else return res*res%mod*x%mod;
}

int getl(int i, int N) { return i==0? N-1:i-1; };
int getr(int i, int N) { return i==N-1? 0:i+1; };


// 線分 ab の偏角 返り値は[-π, π]
double argument(const pair<double, double> &a, const pair<double, double> &b){
    double ax=a.first, ay=a.second, bx=b.first, by=b.second;
    return atan2(by-ay, bx-ax);
}

/* <-----------------------------------------------------------------------------------> */
/* <-----------------------------------------------------------------------------------> */
/* <-----------------------------------------------------------------------------------> */
/* <-----------------------------------------------------------------------------------> */


long double const EPS = (1e-10);
bool equal(long double const a, long double const b) { return fabs(a-b) < EPS; }

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

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

    long double norm() { return x*x + y*y; }
    long double abs() { return sqrt(norm()); }
    
    bool operator < (Point const & p) const { return x != p.x ? x < p.x : y < p.y; }
    bool operator == (Point const & p) const { return equal(x, p.x) && equal(y, p.y); }

    void print() { cout << x << ' ' << y << "\n"; }
};
using Vector = Point;

struct Segment{
    Point p1, p2;
    Segment(Point _p1, Point _p2)
        : p1(_p1), p2(_p2) {}
};
using Line = Segment;

struct Circle{
    Point c;
    double r;
    Circle(Point _c, double _r)
        : c(_c), r(_r) {}
};
using Polygon = vector<Point>;

// 内積 外積 外積は大きさを返す
// https://mathtrain.jp/gaiseki
long double dot(Vector a, Vector b) { return a.x*b.x + a.y*b.y; }
long double cross(Vector a, Vector b) { return a.x*b.y - a.y*b.x; }

// ベクトルの直交判定　平行判定
bool isOrthogonal(Vector const & a, Vector const & b) { return equal(dot(a, b), 0.0); }
bool isOrthogonal(Point const & a1, Point const & a2, Point const & b1, Point const & b2) { return isOrthogonal(a1-a2, b1-b2); }
bool isOrthogonal(Segment const & s1, Segment const & s2) { return isOrthogonal(s1.p1 - s1.p2, s2.p1 - s2.p2); }
bool isParallel(Vector const & a, Vector const & b) { return equal(cross(a, b), 0); }
bool isParallel(Point const & a1, Point const & a2, Point const & b1, Point const & b2) { return isParallel(a1-a2, b1-b2); }
bool isParallel(Segment const & s1, Segment const & s2) { return isParallel(s1.p1-s1.p2, s2.p1-s2.p2); }

// 線分 s に対する点 p の射影
Point project(Segment const & s, Point const & p){
    Vector base = s.p2 - s.p1;
    double r = dot(p - s.p1, base) / base.norm();
    return s.p1 + base * r;
}
// 線分 s を対称軸とした点 p の線対称点
Point reflect(Segment const & s, Point const & p) { return p + (project(s, p) - p) * 2.0; }

// 線分p0-p1 に対して線分 p0-p2 が反時計回りなのか直線上なのか線分上なのか...
static const int COUNTER_CLOCKWISE = 1;
static const int CLOCKWISE = -1;
static const int ONLINE_BACK = 2;   // p2-p0-p1
static const int ONLINE_FRONT = -2; // p0-p1-p2
static const int ON_SEGMENT = 0;    // p0-p2-p1
int ccw(Point const & p0, Point const & p1, Point const & p2){
    Vector a = p1 - p0, b = p2 - p0;
    if(cross(a, b) > EPS) return COUNTER_CLOCKWISE;
    if(cross(a, b) < -EPS) return CLOCKWISE;
    if(dot(a, b) < -EPS) return ONLINE_BACK;
    if(a.norm() < b.norm()) return ONLINE_FRONT;
    return ON_SEGMENT;
}

// 線分 p1-p2 と線分 p3-p4 の交差判定（交点を持てば true）
bool intersect(Point const & p1, Point const & p2, Point const & p3, Point const & p4){
    return (ccw(p1, p2, p3) * ccw(p1, p2, p4) <= 0 && ccw(p3, p4, p1) * ccw(p3, p4, p2) <= 0);
}
bool intersect(Segment const & s1, Segment const & s2) { return intersect(s1.p1, s1.p2, s2.p1, s2.p2); }

// 点と直線の距離
long double getDistanceLP(Line const & l, Point const & p) {
    return abs(cross(l.p2-l.p1, p-l.p1) / (l.p2-l.p1).abs());
}

// 点と線分の距離
long double getDistanceSP(Segment s, Point p) {
    if (dot(s.p2-s.p1, p-s.p1) < 0.0) return (p-s.p1).abs();
    if (dot(s.p1-s.p2, p-s.p2) < 0.0) return (p-s.p2).abs();
    return getDistanceLP(s, p);
}

// 線分と線分の距離
long double getDistance(Segment s1, Segment s2) {
    if (intersect(s1, s2)) return 0.0;
    return min(min(getDistanceSP(s1, s2.p1), getDistanceSP(s1, s2.p2)), min(getDistanceSP(s2, s1.p1), getDistanceSP(s2, s1.p2)));
}

int main(){
    cin.tie(nullptr);
    ios::sync_with_stdio(false);

    int q; cin >> q;
    while(q--) {
        int a, b, c, d, e, f, g, h; cin >> a >> b >> c >> d >> e >> f >> g >> h;
        Point p1(a, b), p2(c, d), p3(e, f), p4(g, h);
        Segment s1(p1, p2), s2(p3, p4);
        cout << fixed << setprecision(15) << getDistance(s1, s2) << endl;
    }


    return 0;
}



Measured memory_bytes: 3320