import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.NoSuchElementException;
import java.util.StringTokenizer;

class ProblemG3 {

	private static final long serialVersionUID = 4106226920647527146L;
	static FileReader fileReader = null;
	static BufferedReader reader = null;
	static StringTokenizer tokenizer = null;
	static int cases;
	static int testcase;
	static double WHOLE_CIRCLE = Math.PI * 2;

	public static void main(String[] args) {
                Locale.setDefault( Locale.ENGLISH );
		try {
			reader = new BufferedReader(System.in);
			tokenizer = new StringTokenizer(reader.readLine());
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		cases = readInt();
		for (testcase = 0; testcase < cases; testcase++) {
			solve();
		}
	}
	
	public static int readInt() {
		// Kijk of er nog een token is te lezen. Zo niet -> Ga volgende regel lezen
		String number = null;
		try {
			number = tokenizer.nextToken();
		} catch (NoSuchElementException e) {
			String newLine = null;
			try {
				newLine = reader.readLine();
			} catch (IOException e1) {
				e1.printStackTrace();
			}
			if (newLine == null)
				return Integer.MAX_VALUE; // All input has been parsed

			tokenizer = new StringTokenizer(newLine);
			number = tokenizer.nextToken();
		}

		return Integer.parseInt(number);
	}

	public static double readDouble() {
		// Kijk of er nog een token is te lezen. Zo niet -> Ga volgende regel lezen
		String number = null;
		try {
			number = tokenizer.nextToken();
		} catch (NoSuchElementException e) {
			String newLine = null;
			try {
				newLine = reader.readLine();
			} catch (IOException e1) {
				e1.printStackTrace();
			}
			if (newLine == null)
				return Double.MAX_VALUE; // All input has been parsed

			tokenizer = new StringTokenizer(newLine);
			number = tokenizer.nextToken();
		}

		return Double.parseDouble(number);
	}

	public static String readString() {
		// Kijk of er nog een token is te lezen. Zo niet -> Ga volgende regel lezen
		String string = null;
		try {
			string = tokenizer.nextToken();
		} catch (NoSuchElementException e) {
			String newLine = null;
			try {
				newLine = reader.readLine();
			} catch (IOException e1) {
				e1.printStackTrace();
			}
			if (newLine == null)
				return null; // All input has been parsed

			tokenizer = new StringTokenizer(newLine);
			string = tokenizer.nextToken();
		}

		return string;
	}

	static Planet planets[] = null;
	static double bestTimeToPlanet[] = null;
	static int bestPlanetToPlanet[] = null;
	static ArrayList <Planet>nodes;
	static Planet planet;
	static int m;
	public static void solve() {
		double x = 0.0, y = 0.0, well = 0.0, speed = 0.0;

		// Lees informatie over de lichamen in (lees de graaf in)
		m = readInt(); // Lees het aantal hemellichamen binnen het stelsel
		planets = new Planet[m];
		bestTimeToPlanet = new double[m];
		bestPlanetToPlanet = new int[m];
		nodes = new ArrayList<Planet>();

		// Initialiseer de afstanden tussen de planeten
		bestTimeToPlanet[0] = 0.0;
		for (int i = 1; i < bestTimeToPlanet.length; i++) {
			bestTimeToPlanet[i] = Double.MAX_VALUE;
			bestPlanetToPlanet[i] = 0;
		}

		for (int j = 0; j < m; j++) {
			x = readDouble(); // x
			y = readDouble(); // y
			well = readDouble(); // Grootte gravity well
			speed = readDouble(); // Rotatiesnelheid in radialen per dag

			// Maak nieuwe planeet aan en zet deze in de lijst
			planets[j] = new Planet(j, x, y, well, speed);
		}
		
		// Bepaal de beste route om te reizen van begin naar eind
		int currentPlanet = 0;
		for (int i = 0; i < planets.length; i++)
			nodes.add(planets[i]);

		currentPlanet = 0; 
		do {
			/* Stap 1: Bepaal welke lichamen de Robintron kan bereiken vanaf huidig punt.
			 * De Robintron kan alle lichamen bereiken waarvan het binnen de gravity well
			 * kan komen en die een andere rotatiesnelheid heeft dan de planeet waar de
			 * Robintron op zit.
			 */
			{
				Planet p = nodes.remove(currentPlanet);
				bepaalAfstand(p.index);
			}

			/* Stap 2: We weten welke planeten we kunnen bereiken vanuit de huidige positie.
			 * Ga nu verder zoeken bij de planeet met de kleinste afstand van de huidige planeet,
			 * op zoek naar het doel.
			 */
			double smallestDistanceFromSource = Double.MAX_VALUE;
			int planetWithSmallestDistanceFromSource = 0;
			// Zoek planeet met kleinste afstand vanaf vorig punt
			for (int i = 0; i < nodes.size(); i++) {
				Planet p = nodes.get(i);
				int index = p.index;
				if (bestTimeToPlanet[index] < smallestDistanceFromSource) {
					smallestDistanceFromSource = bestTimeToPlanet[index];
					planetWithSmallestDistanceFromSource = i;
				}
			}
			currentPlanet = planetWithSmallestDistanceFromSource;
		} while (nodes.size() > 0);

		System.out.println((int)Math.ceil(bestTimeToPlanet[planets.length - 1]));
	}

	public static void bepaalAfstand(int currentPlanet) {
		for (int j = 0; j < nodes.size(); j++) {
			Planet p = nodes.get(j);
			int i = p.index;

			// Ga niet naar eigen planeet hoppen
			if (i == currentPlanet)
				continue;

			double t = 0.0;

			/* Bepaal op welk moment de planeten elkaar zouden kunnen treffen zodanig
			 * dat de Robintron kan overspringen.
			 */
			double r1 = planets[currentPlanet].distance;
			double r12 = planets[currentPlanet].distanceSquared;
			double r2 = planets[i].distance;
			double r22 = planets[i].distanceSquared;
			double theta1 = planets[currentPlanet].theta;
			double theta2 = planets[i].theta;
			double speed1 = planets[currentPlanet].speed;
			double speed2 = planets[i].speed;
			double g22 = planets[i].wellSquared;

			double currentTime = bestTimeToPlanet[currentPlanet];
			
			double x1, x2, y1, y2;
			x1 = r1 * Math.cos(currentTime * speed1 + theta1);
			y1 = r1 * Math.sin(currentTime * speed1 + theta1);
			x2 = r2 * Math.cos(currentTime * speed2 + theta2);
			y2 = r2 * Math.sin(currentTime * speed2 + theta2);

			// Is het uberhaupt mogelijk om elkaar te treffen?
			if (Math.abs(r1 - r2) > planets[i].well + 10e-6) {
				// De planeten liggen te ver uit de buurt om ooit elkaar te treffen
				continue;
			}

			/* Voordat we gaan bepalen hoe lang het duurt voordat de Robintron
			 * de cirkel snijdt die wordt bepaald door de gravity well van
			 * planeet i, gaan we kijken of de Robintron bij aankomst
			 * al binnen bereik ligt.
			 */
			if (Math.sqrt( Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2) ) <= planets[i].well + 10e-6) {
				if (bestTimeToPlanet[i] > bestTimeToPlanet[currentPlanet]) {
					bestTimeToPlanet[i] = bestTimeToPlanet[currentPlanet];
					bestPlanetToPlanet[i] = currentPlanet;
					continue; // Dat is zo, dus de tijd die het kost is 0.0
				}
			} else if (Math.abs(speed1 - speed2) < 10e-6) {
				/* De planeten bewegen even snel t.o.v. elkaar, dus
				 * kunnen ze elkaar alleen treffen als planeet1 al binnen
				 * het bereik van planeet2 ligt. We weten dat dit niet het
				 * geval is, dus kunnen de planeten elkaar nooit treffen.
				 */
				continue;
			}

			// Bepaal huidige hoeken van de planeten
			double currentTheta1 = Planet.calculateTheta(x1, y1);
			double currentTheta2 = Planet.calculateTheta(x2, y2);

			// Bepaal het verschil in hoeken tussen de planeten
			double thetaDifference = currentTheta1 - currentTheta2;
			if (thetaDifference < 0)
				thetaDifference = WHOLE_CIRCLE + thetaDifference;
			else if (thetaDifference > WHOLE_CIRCLE)
				thetaDifference %= WHOLE_CIRCLE;

			double angleDifferenceToWell = Math.abs( Math.acos( (r12 + r22 - g22) / (2 * r1 * r2) ) );
			
			/* Als de planeet net langs de ander is gegaan dan moet deze bijna weer bijna 
			 * een hele periode wachten tot ze elkaar weer ontmoeten. 
			 */
			if (speed1 > speed2) {
				/* De huidige planeet is sneller dan de ander, waardoor we een hele periode moeten
				 * wachten totdat de planeten elkaar weer kunnen ontmoeten.
				 */
				t = ((2 * Math.PI - angleDifferenceToWell) - thetaDifference) / (speed1 - speed2);
			}
			else
				t = (angleDifferenceToWell - thetaDifference) / (speed1 - speed2);
			
			if (t < 0)
				t = 0;
			
			// Cornercase die niet meer in de inputset zit
			if (Double.isNaN(t) ) {
				continue;
			}

			/* Ga bepalen hoe lang het voor iedere planeet zou duren voordat we daar zijn gegeven
			 * de huidig gevolgde route. Als blijkt dat het korter is dan dat eerder werd aangenomen
			 * dan onthouden we die tijden voor de planeten. Als dat langer heeft geduurd dan weten
			 * we dat er een andere route was die minder lang heeft geduurd en negeren we deze optie.
			 */
			if (bestTimeToPlanet[i] > bestTimeToPlanet[currentPlanet] + t) {
				bestTimeToPlanet[i] = bestTimeToPlanet[currentPlanet] + t;
				bestPlanetToPlanet[i] = currentPlanet;
			}

		}
	}

	public static class Planet {
		public double x, y, well, wellSquared, speed, theta, distance, distanceSquared;
		public int index;

		public Planet() {
			index = 0;
			x = y = well = wellSquared = speed = theta = distance = distanceSquared = 0.0;
		}

		public Planet(int index, double x, double y, double well, double speed) {
			this.index = index;
			this.x = x;
			this.y = y;
			this.well = well;
			wellSquared = Math.pow(well, 2);
			this.speed = speed;
			theta = 0.0;
			distance = Math.sqrt( (this.x * this.x) + (this.y * this.y) ); // Bepaal afstand
			distanceSquared = Math.pow(distance, 2);

			/* Nu moeten we voor dit lichaam de theta bepalen, dat is de hoek die
			 * het lichaam maakt t.o.v. het draaistartpunt [x, 0] (cos(0) = 1, sin(0) = 0)
			 */
			theta = Planet.calculateTheta(x, y);
		}

		public static double calculateTheta(double x, double y) {
			// Bepaal de hoek die het lichaam maakt t.o.v. de ster (in het kwadrant)
			return Math.atan2(y, x);
		}
	}

}
