Problem:
Don't Cross the Circles!
There are one or more circles on a plane.
Any two circles have different center positions and/or different radiuses.
A circle may intersect with another circle,
but no three or more circles have areas nor points shared by all of them.
A circle may completely contain another circle or two circles may intersect at two separate points,
but you can assume that the circumferences of two circles never
touch at a single point.
Your task is to judge
whether there exists a path that connects the given two points,
P
and
Q
,
without crossing the circumferences of the circles.
You are given one or more point pairs for each layout of circles.
In the case of Figure G-1,
we can connect
P
and
Q
1
without crossing the circumferences of the circles, but we cannot connect
P
with
Q
2
,
Q
3
, or
Q
4
without crossing the circumferences of the circles.
Figure G-1: Sample layout of circles and points
Input
The input consists of multiple datasets, each in the following format.
n
m
Cx
1
Cy
1
r
1
...
Cx
n
Cy
n
r
n
Px
1
Py
1
Qx
1
Qy
1
...
Px
m
Py
m
Qx
m
Qy
m
The first line of a dataset contains two integers
n
and
m
separated by a space.
n
represents the number of circles, and
you can assume 1 ≤
n
≤ 100.
m
represents the number of point pairs, and
you can assume 1 ≤
m
≤ 10.
Each of the following
n
lines contains three integers separated by a single space.
(
Cx
i
,
Cy
i
) and
r
i
represent the center position and the radius of the
i
-th circle, respectively.
Each of the following
m
lines contains four integers separated by a single space.
These four integers represent coordinates of two separate points
P
j
= (
Px
j
,
Py
j
) and
Q
j
=(
Qx
j
,
Qy
j
).
These two points
P
j
and
Q
j
form the
j
-th point pair.
You can assume
0 ≤
Cx
i
≤ 10000,
0 ≤
Cy
i
≤ 10000,
1 ≤
r
i
≤ 1000,
0 ≤
Px
j
≤ 10000,
0 ≤
Py
j
≤ 10000,
0 ≤
Qx
j
≤ 10000,
0 ≤
Qy
j
≤ 10000.
In addition,
you can assume
P
j
or
Q
j
are not located on the circumference of any circle.
The end of the input is indicated by a line containing two zeros separated by a space.
Figure G-1 shows the layout of the circles and the points in the first dataset of the sample input below.
Figure G-2 shows the layouts of the subsequent datasets in the sample input.
Figure G-2: Sample layout of circles and points
Output
For each dataset,
output a single line containing the
m
results separated by a space.
The
j
-th result should be "YES" if there exists a path connecting
P
j
and
Q
j
, and "NO" otherwise.
Sample Input
5 3
0 0 1000
1399 1331 931
0 1331 500
1398 0 400
2000 360 340
450 950 1600 380
450 950 1399 1331
450 950 450 2000
1 2
50 50 50
0 10 100 90
0 10 50 50
2 2
50 50 50
100 50 50
40 50 110 50
40 50 0 0
4 1
25 100 26
75 100 26
50 40 40
50 160 40
50 81 50 119
6 1
100 50 40
0 50 40
50 0 48
50 50 3
55 55 4
55 105 48
50 55 55 50
20 6
270 180 50
360 170 50
0 0 50
10 0 10
0 90 50
0 180 50
90 180 50
180 180 50
205 90 50
180 0 50
65 0 20
75 30 16
90 78 36
105 30 16
115 0 20
128 48 15
128 100 15
280 0 30
330 0 30
305 65 42
0 2
...(truncated)

Submitted solutions:

code_snippet: #include <bits/stdc++.h>
using namespace std;

using Real = long double;
using Point = complex<Real>;
const Real EPS = 1e-9, PI = acos(-1);

inline bool eq(Real a, Real b) {
  return fabs(b - a) < EPS;
}

Point operator*(const Point &p, const Real &d) {
  return Point(real(p) * d, imag(p) * d);
}

istream &operator>>(istream &is, Point &p) {
  Real a, b;
  is >> a >> b;
  p = Point(a, b);
  return is;
}

ostream &operator<<(ostream &os, Point &p) {
  return os << fixed << setprecision(10) << p.real() << " " << p.imag();
}

// 点 p を反時計回りに theta 回転
Point rotate(Real theta, const Point &p) {
  return Point(
    cos(theta) * p.real() - sin(theta) * p.imag(), sin(theta) * p.real() + cos(theta) * p.imag());
}

Real radian_to_degree(Real r) {
  return (r * 180.0 / PI);
}

Real degree_to_radian(Real d) {
  return (d * PI / 180.0);
}

// a-b-c の角度のうち小さい方を返す
Real get_angle(const Point &a, const Point &b, const Point &c) {
  const Point v(b - a), w(c - b);
  Real alpha = atan2(v.imag(), v.real()), beta = atan2(w.imag(), w.real());
  if (alpha > beta) swap(alpha, beta);
  Real theta = (beta - alpha);
  return min(theta, 2 * acos(-1) - theta);
}

namespace std {
bool operator<(const Point &a, const Point &b) {
  return a.real() != b.real() ? a.real() < b.real() : a.imag() < b.imag();
}
}  // namespace std

struct Line {
  Point a, b;

  Line() = default;

  Line(Point a, Point b) : a(a), b(b) {}

  Line(Real A, Real B, Real C)  // Ax + By = C
  {
    if (eq(A, 0)) a = Point(0, C / B), b = Point(1, C / B);
    else if (eq(B, 0)) b = Point(C / A, 0), b = Point(C / A, 1);
    else a = Point(0, C / B), b = Point(C / A, 0);
  }

  friend ostream &operator<<(ostream &os, Line &p) { return os << p.a << " to " << p.b; }

  friend istream &operator>>(istream &is, Line &a) { return is >> a.a >> a.b; }
};

struct Segment : Line {
  Segment() = default;

  Segment(Point a, Point b) : Line(a, b) {}
};

struct Circle {
  Point p;
  Real r;

  Circle() = default;

  Circle(Point p, Real r) : p(p), r(r) {}
};

using Points = vector<Point>;
using Polygon = vector<Point>;
using Segments = vector<Segment>;
using Lines = vector<Line>;
using Circles = vector<Circle>;

Real cross(const Point &a, const Point &b) {
  return real(a) * imag(b) - imag(a) * real(b);
}

Real dot(const Point &a, const Point &b) {
  return real(a) * real(b) + imag(a) * imag(b);
}

// 点の回転方向
int ccw(const Point &a, Point b, Point c) {
  b = b - a, c = c - a;
  if (cross(b, c) > EPS) return +1;   // "COUNTER_CLOCKWISE"
  if (cross(b, c) < -EPS) return -1;  // "CLOCKWISE"
  if (dot(b, c) < 0) return +2;       // "ONLINE_BACK"
  if (norm(b) < norm(c)) return -2;   // "ONLINE_FRONT"
  return 0;                           // "ON_SEGMENT"
}

// 平行判定
bool parallel(const Line &a, const Line &b) {
  return eq(cross(a.b - a.a, b.b - b.a), 0.0);
}

// 垂直判定
bool orthogonal(const Line &a, const Line &b) {
  return eq(dot(a.a - a.b, b.a - b.b), 0.0);
}

// 射影
// 直線 l に p から垂線を引いた交点を求める
Point projection(const Line &l, const Point &p) {
  double t = dot(p - l.a, l.a - l.b) / norm(l.a - l.b);
  return l.a + (l.a - l.b) * t;
}
Point projection(const Segment &l, const Point &p) {
  double t = dot(p - l.a, l.a - l.b) / norm(l.a - l.b);
  return l.a + (l.a - l.b) * t;
}

// 反射
// 直線 l を対称軸として点 p  と線対称にある点を求める
Point reflection(const Line &l, const Point &p) {
  return p + (projection(l, p) - p) * 2.0;
}

bool intersect(const Line &l, const Point &p) {
  return abs(ccw(l.a, l.b, p)) != 1;
}
bool intersect(const Line &l, const Line &m) {
  return abs(cross(l.b - l.a, m.b - m.a)) > EPS || abs(cross(l.b - l.a, m.b - l.a)) < EPS;
}
bool intersect(const Segment &s, const Point &p) {
  return ccw(s.a, s.b, p) == 0;
}
bool intersect(const Line &l, const Segment &s) {
  return cross(l.b - l.a, s.a - l.a) * cross(l.b - l.a, s.b - l.a) < EPS;
}

Real distance(const Line &l, const Point &p);

bool intersect(const Circle &c, const Line &l) {
  return distance(l, c.p) <= c.r + EPS;
}
bool intersect(const Circle &c, const Point &p) {
  return abs(abs(p - c.p) - c.r) < EPS;
}
bool intersect(const Segment &s, const Segment &t) {
  return ccw(s.a, s.b, t.a) * ccw(s.a, s.b, t.b) <= 0 &&
         ccw(t.a, t.b, s.a) * ccw(t.a, t.b, s.b) <= 0;
}

int intersect(const Circle &c, const Segment &l) {
  if (norm(projection(l, c.p) - c.p) - c.r * c.r > EPS) return 0;
  auto d1 = abs(c.p - l.a), d2 = abs(c.p - l.b);
  if (d1 < c.r + EPS && d2 < c.r + EPS) return 0;
  if (d1 < c.r - EPS && d2 > c.r + EPS || d1 > c.r + EPS && d2 < c.r - EPS) return 1;
  const Point h = projection(l, c.p);
  if (dot(l.a - h, l.b - h) < 0) return 2;
  return 0;
}
// 0 -> 包含, 1 -> 内接, 2 -> 交差, 3 -> 外接, 4 -> 分離
int intersect(Circle c1, Circle c2) {
  if (c1.r < c2.r) swap(c1, c2);
  Real d = abs(c1.p - c2.p);
  if (c1.r + c2.r < d) return 4;
  if (eq(c1.r + c2.r, d)) return 3;
  if (c1.r - c2.r < d) return 2;
  if (eq(c1.r - c2.r, d)) return 1;
  return 0;
}

Real distance(const Point &a, const Point &b) {
  return abs(a - b);
}
Real distance(const Line &l, const Point &p) {
  return abs(p - projection(l, p));
}
Real distance(const Line &l, const Line &m) {
  return intersect(l, m) ? 0 : distance(l, m.a);
}
Real distance(const Segment &s, const Point &p) {
  Point r = projection(s, p);
  if (intersect(s, r)) return abs(r - p);
  return min(abs(s.a - p), abs(s.b - p));
}
Real distance(const Segment &a, const Segment &b) {
  if (intersect(a, b)) return 0;
  return min({distance(a, b.a), distance(a, b.b), distance(b, a.a), distance(b, a.b)});
}
Real distance(const Line &l, const Segment &s) {
  if (intersect(l, s)) return 0;
  return min(distance(l, s.a), distance(l, s.b));
}

Point crosspoint(const Line &l, const Line &m) {
  Real A = cross(l.b - l.a, m.b - m.a);
  Real B = cross(l.b - l.a, l.b - m.a);
  if (eq(abs(A), 0.0) && eq(abs(B), 0.0)) return m.a;
  return m.a + (m.b - m.a) * B / A;
}
Point crosspoint(const Segment &l, const Segment &m) {
  return crosspoint(Line(l), Line(m));
}
pair<Point, Point> crosspoint(const Circle &c, const Line l) {
  Point pr = projection(l, c.p);
  Point e = (l.b - l.a) / abs(l.b - l.a);
  if (eq(distance(l, c.p), c.r)) return {pr, pr};
  double base = sqrt(c.r * c.r - norm(pr - c.p));
  return {pr - e * base, pr + e * base};
}
pair<Point, Point> crosspoint(const Circle &c, const Segment &l) {
  Line aa = Line(l.a, l.b);
  if (intersect(c, l) == 2) return crosspoint(c, aa);
  auto ret = crosspoint(c, aa);
  if (dot(l.a - ret.first, l.b - ret.first) < 0) ret.second = ret.first;
  else ret.first = ret.second;
  return ret;
}
pair<Point, Point> crosspoint(const Circle &c1, const Circle &c2) {
  Real d = abs(c1.p - c2.p);
  Real a = acos((c1.r * c1.r + d * d - c2.r * c2.r) / (2 * c1.r * d));
  Real t = atan2(c2.p.imag() - c1.p.imag(), c2.p.real() - c1.p.real());
  Point p1 = c1.p + Point(cos(t + a) * c1.r, sin(t + a) * c1.r);
  Point p2 = c1.p + Point(cos(t - a) * c1.r, sin(t - a) * c1.r);
  return {p1, p2};
}

// 点 p を通る円 c の接線
pair<Point, Point> tangent(const Circle &c1, const Point &p2) {
  return crosspoint(c1, Circle(p2, sqrt(norm(c1.p - p2) - c1.r * c1.r)));
}

// 円 c1, c2 の共通接線
Lines tangent(Circle c1, Circle c2) {
  Lines ret;
  if (c1.r < c2.r) swap(c1, c2);
  Real g = norm(c1.p - c2.p);
  if (eq(g, 0)) return ret;
  Point u = (c2.p - c1.p) / sqrt(g);
  Point v = rotate(PI * 0.5, u);
  for (int s : {-1, 1}) {
    Real h = (c1.r + s * c2.r) / sqrt(g);
    if (eq(1 - h * h, 0)) {
      ret.emplace_back(c1.p + u * c1.r, c1.p + (u + v) * c1.r);
    } else if (1 - h * h > 0) {
      Point uu = u * h, vv = v * sqrt(1 - h * h);
      ret.emplace_back(c1.p + (uu + vv) * c1.r, c2.p - (uu + vv) * c2.r * s);
      ret.emplace_back(c1.p + (uu - vv) * c1.r, c2.p - (uu - vv) * c2.r * s);
    }
  }
  return ret;
}

// 凸性判定
bool is_convex(const Polygon &p) {
  int n = (int)p.size();
  for (int i = 0; i < n; i++) {
    if (ccw(p[(i + n - 1) % n], p[i], p[(i + 1) % n]) == -1) return false;
  }
  return true;
}

// 凸包
Polygon convex_hull(Polygon &p) {
  int n = (int)p.size(), k = 0;
  if (n <= 2) return p;
  sort(p.begin(), p.end());
  vector<Point> ch(2 * n);
  for (int i = 0; i < n; ch[k++] = p[i++]) {
    while (k >= 2 && cross(ch[k - 1] - ch[k - 2], p[i] - ch[k - 1]) < EPS) --k;
  }
  for (int i = n - 2, t = k + 1; i >= 0; ch[k++] = p[i--]) {
    while (k >= t && cross(ch[k - 1] - ch[k - 2], p[i] - ch[k - 1]) < EPS) --k;
  }
  ch.resize(k - 1);
  return ch;
}

// 多角形と点の包含判定
enum { OUT, ON, IN };

int contains(const Polygon &Q, const Point &p) {
  bool in = false;
  for (int i = 0; i < Q.size(); i++) {
    Point a = Q[i] - p, b = Q[(i + 1) % Q.size()] - p;
    if (a.imag() > b.imag()) swap(a, b);
    if (a.imag() <= 0 && 0 < b.imag() && cross(a, b) < 0) in = !in;
    if (cross(a, b) == 0 && dot(a, b) <= 0) return ON;
  }
  return in ? IN : OUT;
}

// 線分の重複除去
void merge_segments(vector<Segment> &segs) {
  auto merge_if_able = [](Segment &s1, const Segment &s2) {
    if (abs(cross(s1.b - s1.a, s2.b - s2.a)) > EPS) return false;
    if (ccw(s1.a, s2.a, s1.b) == 1 || ccw(s1.a, s2.a, s1.b) == -1) return false;
    if (ccw(s1.a, s1.b, s2.a) == -2 || ccw(s2.a, s2.b, s1.a) == -2) return false;
    s1 = Segment(min(s1.a, s2.a), max(s1.b, s2.b));
    return true;
  };

  for (int i = 0; i < segs.size(); i++) {
    if (segs[i].b < segs[i].a) swap(segs[i].a, segs[i].b);
  }
  for (int i = 0; i < segs.size(); i++) {
    for (int j = i + 1; j < segs.size(); j++) {
      if (merge_if_able(segs[i], segs[j])) {
        segs[j--] = segs.back(), segs.pop_back();
      }
    }
  }
}

// 線分アレンジメント
// 任意の2線分の交点を頂点としたグラフを構築する
vector<vector<int>> segment_arrangement(vector<Segment> &segs, vector<Point> &ps) {
  vector<vector<int>> g;
  int N = (int)segs.size();
  for (int i = 0; i < N; i++) {
    ps.emplace_back(segs[i].a);
    ps.emplace_back(segs[i].b);
    for (int j = i + 1; j < N; j++) {
      const Point p1 = segs[i].b - segs[i].a;
      const Point p2 = segs[j].b - segs[j].a;
      if (cross(p1, p2) == 0) continue;
      if (intersect(segs[i], segs[j])) {
        ps.emplace_back(crosspoint(segs[i], segs[j]));
      }
    }
  }
  sort(begin(ps), end(ps));
  ps.erase(unique(begin(ps), end(ps)), end(ps));

  int M = (int)ps.size();
  g.resize(M);
  for (int i = 0; i < N; i++) {
    vector<int> vec;
    for (int j = 0; j < M; j++) {
      if (intersect(segs[i], ps[j])) {
        vec.emplace_back(j);
      }
    }
    for (int j = 1; j < vec.size(); j++) {
      g[vec[j - 1]].push_back(vec[j]);
      g[vec[j]].push_back(vec[j - 1]);
    }
  }
  return (g);
}

// 凸多角形の切断
// 直線 l.a-l.b で切断しその左側にできる凸多角形を返す
Polygon convex_cut(const Polygon &U, Line l) {
  Polygon ret;
  for (int i = 0; i < U.size(); i++) {
    Point now = U[i], nxt = U[(i + 1) % U.size()];
    if (ccw(l.a, l.b, now) != -1) ret.push_back(now);
    if (ccw(l.a, l.b, now) * ccw(l.a, l.b, nxt) < 0) {
      ret.push_back(crosspoint(Line(now, nxt), l));
    }
  }
  return (ret);
}

// 多角形の面積
Real area(const Polygon &p) {
  Real A = 0;
  for (int i = 0; i < p.size(); ++i) {
    A += cross(p[i], p[(i + 1) % p.size()]);
  }
  return A * 0.5;
}

// 円と多角形の共通部分の面積
Real area(const Polygon &p, const Circle &c) {
  if (p.size() < 3) return 0.0;
  function<Real(Circle, Point, Point)> cross_area = [&](const Circle &c, const Point &a,
                                                      const Point &b) {
    Point va = c.p - a, vb = c.p - b;
    Real f = cross(va, vb), ret = 0.0;
    if (eq(f, 0.0)) return ret;
    if (max(abs(va), abs(vb)) < c.r + EPS) return f;
    if (distance(Segment(a, b), c.p) > c.r - EPS) return c.r * c.r * arg(vb * conj(va));
    auto u = crosspoint(c, Segment(a, b));
    vector<Point> tot{a, u.first, u.second, b};
    for (int i = 0; i + 1 < tot.size(); i++) {
      ret += cross_area(c, tot[i], tot[i + 1]);
    }
    return ret;
  };
  Real A = 0;
  for (int i = 0; i < p.size(); i++) {
    A += cross_area(c, p[i], p[(i + 1) % p.size()]);
  }
  return A;
}

// 凸多角形の直径(最遠頂点対間距離)
Real convex_diameter(const Polygon &p) {
  int N = (int)p.size();
  int is = 0, js = 0;
  for (int i = 1; i < N; i++) {
    if (p[i].imag() > p[is].imag()) is = i;
    if (p[i].imag() < p[js].imag()) js = i;
  }
  Real maxdis = norm(p[is] - p[js]);

  int maxi, maxj, i, j;
  i = maxi = is;
  j = maxj = js;
  do {
    if (cross(p[(i + 1) % N] - p[i], p[(j + 1) % N] - p[j]) >= 0) {
      j = (j + 1) % N;
    } else {
      i = (i + 1) % N;
    }
    if (norm(p[i] - p[j]) > maxdis) {
      maxdis = norm(p[i] - p[j]);
      maxi = i;
      maxj = j;
    }
  } while (i != is || j != js);
  return sqrt(maxdis);
}

// 最近点対
Real closest_pair(Points ps) {
  if (ps.size() <= 1) throw(0);
  sort(begin(ps), end(ps));

  auto compare_y = [&](const Point &a, const Point &b) { return imag(a) < imag(b); };
  vector<Point> beet(ps.size());
  const Real INF = 1e18;

  function<Real(int, int)> rec = [&](int left, int right) {
    if (right - left <= 1) return INF;
    int mid = (left + right) >> 1;
    auto x = real(ps[mid]);
    auto ret = min(rec(left, mid), rec(mid, right));
    inplace_merge(begin(ps) + left, begin(ps) + mid, begin(ps) + right, compare_y);
    int ptr = 0;
    for (int i = left; i < right; i++) {
      if (abs(real(ps[i]) - x) >= ret) continue;
      for (int j = 0; j < ptr; j++) {
        auto luz = ps[i] - beet[ptr - j - 1];
        if (imag(luz) >= ret) break;
        ret = min(ret, abs(luz));
      }
      beet[ptr++] = ps[i];
    }
    return ret;
  };
  return rec(0, (int)ps.size());
}

// 平面グラフを多角形に分割する (基本的に反時計回り、外平面のみ時計回り)
// graph は無向グラフを想定 (u-v <=> v-u)。変なグラフを入れると壊れるはず
vector<vector<int>> decompose_to_cycle(const vector<vector<int>> &graph, const vector<Point> &pos) {
  assert(graph.size() == pos.size());
  int n = graph.size();
  int m = 0;
  map<array<int, 2>, int> mp;
  vector<array<int, 2>> edge;
  for (int i = 0; i < n; ++i) {
    for (auto ni : graph[i]) {
      mp[{i, ni}] = m++;
      edge.push_back({i, ni});
    }
  }
  
  vector<int> nex(m, -1);
  for (int i = 0; i < n; ++i) {
    if (graph[i].empty()) continue;
    
    vector<pair<long double, int>> adj;
    for (int ni : graph[i]) {
      Point diff = pos[ni] - pos[i];
      assert(pos[ni] != pos[i]);
      adj.push_back({arg(diff), ni});
    }
    
    sort(adj.begin(), adj.end());
    int siz = adj.size();
    adj.emplace_back(adj[0]);
    adj[siz].first += 2 * PI;
    
    for (int j = 0; j < siz; ++j) {
      auto [arg0, to] = adj[j];
      auto [arg1, from] = adj[j + 1];
      int ein = mp[{from, i}];
      int eout = mp[{i, to}];
      
      nex[ein] = eout;
    }
  }
  
  vector<vector<int>> ret;
  vector<bool> used(m, false);
  for (int i = 0; i < m; ++i) {
    if (used[i]) continue;
    
    vector<int> tmp;
    int now = i;
    while (!used[now]) {
      used[now] = true;
      tmp.emplace_back(edge[now][0]);
      now = nex[now];
    }
    ret.emplace_back(tmp);
  }
  
  return ret;
}

/*-- main --*/

bool is_end = false;

void solve()
{
  int N, Q; cin >> N >> Q;
  if (N == 0 && Q == 0)
  {
    is_end = true;
    return;
  }
  vector<Circle> circles(N);
  vector<Point> centers(N);
  for (int i = 0; i < N; ++i)
  {
    Real x, y, r; cin >> x >> y >> r;
    centers[i] = Point(x, y);
    circles[i] = Circle(Point(x, y), r);
  }
  vector<array<Point, 2>> query(Q);
  for (int i = 0; i < Q; ++i)
  {
    int px, py, qx, qy; cin >> px >> py >> qx >> qy;
    query[i][0] = Point(px, py);
    query[i][1] = Point(qx, qy);
  }
  
  
  vector<vector<int>> graph(N);
  for (int i = 0; i < N; ++i)
  {
    for (int j = 0; j < N; ++j)
    {
      if (i == j) continue;
      if (intersect(circles[i], circles[j]) == 2) graph[i].emplace_back(j);
    }
  }
  vector<vector<int>> tmp = decompose_to_cycle(graph, centers);
  vector<Polygon> polygons(tmp.size());
  for (int i = 0; i < tmp.size(); ++i)
  {
    for (auto v : tmp[i])
    {
      polygons[i].emplace_back(centers[v]);
    }
  }
  
  for (int i = 0; i < Q; ++i)
  {
    auto [p, q] = query[i];
    
    vector<bool> isinp(N, false), isinq(N, false);
    int cntp = 0, cntq = 0;
    for (int j = 0; j < N; ++j)
    {
      Point center = circles[j].p;
      Real radius = circles[j].r;
      if (abs(center - p) < radius) isinp[j] = true, cntp++;
      if (abs(center - q) < radius) isinq[j] = true, cntq++;
    }
    
    bool flg = false;
    if (isinp != isinq) flg = false;
    else
    {
      flg = true;
      
      if (cntp == 0)
      {
        int siz = polygons.size();
        for (auto poly : polygons)
        {
          auto pp = contains(poly, p);
          auto qq = contains(poly, q);
          if (pp != qq) flg = false;
        }
      }
    }
    
    cout << (flg ? "YES" : "NO") << " \n"[i+1 == Q];
  }
  
  return;
}

int main()
{
  while (!is_end)
  {
    solve();
  }
  
  return 0;
}
accuracy: 1
time_ms: 30
memory_kb: 3672
final_rank: 11

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;
using P = pair<ll, ll>;
#define rep(i, a, b) for(ll i = a; i < b; ++i)
#define rrep(i, a, b) for(ll i = a; i >= b; --i)
constexpr ll inf = 4e18;
struct SetupIO {
    SetupIO() {
        ios::sync_with_stdio(0);
        cin.tie(0);
        cout << fixed << setprecision(30);
    }
} setup_io;
vector<int> cycle_detection(const vector<vector<int>>& g) {
    int n = g.size();
    vector<int> r(n, -1), p(n, -1), d(n, 0);
    auto dfs = [&](auto& dfs, int r2, int cur) -> void {
        r[cur] = r2;
        for(int to : g[cur]) {
            if(r[to] == -1) {
                d[to] = d[cur] + 1;
                p[to] = cur;
                dfs(dfs, r2, to);
            }
        }
    };
    rep(i, 0, n) {
        if(p[i] == -1) {
            dfs(dfs, i, i);
        }
    }
    rep(i, 0, n) {
        for(int j : g[i]) {
            if(r[i] == r[j] and d[j] - d[i] > 1) {
                vector<int> cycle;
                for(int v = j; v != i; v = p[v]) {
                    cycle.push_back(v);
                }
                cycle.push_back(i);
                return cycle;
            }
        }
    }
    return {};
}
using Real = long double;
const Real EPS = 1e-6, PI = acos(Real(-1.0));
int sign(const Real& r) {
    if(r <= -EPS) return -1;
    if(r >= +EPS) return +1;
    return 0;
}
bool eq(const Real& a, const Real& b) {
    return sign(a - b) == 0;
}
using Point = complex<Real>;
istream& operator>>(istream& is, Point& p) {
    Real a, b;
    is >> a >> b;
    p = Point(a, b);
    return is;
}
Real dot(const Point& p1, const Point& p2) {
    return (conj(p1) * p2).real();
}
Real cross(const Point& p1, const Point& p2) {
    return (conj(p1) * p2).imag();
}
int ccw(const Point& a, Point b, Point c) {
    b -= a;
    c -= a;
    if(sign(cross(b, c)) == 1) return 1;
    if(sign(cross(b, c)) == -1) return -1;
    if(sign(dot(b, c)) == -1) return +2;
    if(norm(b) < norm(c)) return -2;
    return 0;
}
struct Line {
    Point a, b;
    Line() = default;
    Line(const Point& a, const Point& b)
        : a(a), b(b) {}
};
using Segment = Line;
bool is_intersect_ss(const Segment& s1, const Segment& s2) {
    if(ccw(s1.a, s1.b, s2.a) * ccw(s1.a, s1.b, s2.b) > 0) return false;
    return ccw(s2.a, s2.b, s1.a) * ccw(s2.a, s2.b, s1.b) <= 0;
}
bool in_polygon(const vector<Point>& ps, const Point& p) {
    int n = ps.size();
    Segment seg = Segment(p, Point(p.real() + 1000000, p.imag() + 58));
    int cnt = 0;
    rep(i, 0, n) {
        Point dir = ps[(i + 1) % n] - ps[i];
        if(is_intersect_ss(seg, Segment(ps[i], ps[(i + 1) % n] - EPS * dir))) {
            cnt++;
        }
    }
    return cnt % 2 == 1;
}
struct Circle {
    Point p;
    Real r;
    Circle() = default;
    Circle(const Point& p, const Real& r)
        : p(p), r(r) {}
};
int is_intersect_cc(Circle c1, Circle c2) {
    if(c1.r < c2.r) swap(c1, c2);
    Real d = abs(c1.p - c2.p);
    int a = sign(d - c1.r - c2.r);
    if(a >= 0) return a + 3;
    return sign(d - c1.r + c2.r) + 1;
}
bool is_contain_cp(Circle c, Point p) {
    return abs(p - c.p) < c.r;
}
int main(void) {
    while(1) {
        int n, m;
        cin >> n >> m;
        if(n == 0 and m == 0) break;
        vector<Circle> c(n);
        rep(i, 0, n) {
            cin >> c[i].p >> c[i].r;
        }
        rep(tc, 0, m) {
            Point p, q;
            cin >> p >> q;
            set<int> st1, st2;
            rep(i, 0, n) {
                if(is_contain_cp(c[i], p)) {
                    st1.insert(i);
                }
                if(is_contain_cp(c[i], q)) {
                    st2.insert(i);
                }
            }
            if(!st1.empty() or !st2.empty()) {
                if(st1 == st2) {
                    cout << "YES";
                } else {
                    cout << "NO";
                }
                if(tc + 1 < m) cout << ' ';
                continue;
            }
            vector<vector<int>> g(n);
            rep(i, 0, n) {
                rep(j, 0, i) {
                    if(is_intersect_cc(c[i], c[j]) == 2) {
                        g[i].push_back(j);
                        g[j].push_back(i);
                    }
                }
            }
            bool ans = true;
            while(1) {
                vector<int> cycle = cycle_detection(g);
                if(cycle.empty()) break;
                vector<Point> ps;
                rep(i, 0, (int)cycle.size()) {
                    ps.push_back(c[cycle[i]].p);
                }
                if(in_polygon(ps, p) != in_polygon(ps, q)) {
                    ans = false;
                    break;
                }
                rep(i, 0, (int)g[cycle[0]].size()) {
                    if(g[cycle[0]][i] == cycle[1]) {
                        g[cycle[0]].erase(g[cycle[0]].begin() + i);
                        break;
                    }
                }
                rep(i, 0, (int)g[cycle[1]].size()) {
                    if(g[cycle[1]][i] == cycle[0]) {
                        g[cycle[1]].erase(g[cycle[1]].begin() + i);
                        break;
                    }
                }
            }
            if(ans) {
                cout << "YES";
            } else {
                cout << "NO";
            }
            if(tc < m - 1) cout << ' ';
        }
        cout << '\n';
    }
}
accuracy: 1
time_ms: 50
memory_kb: 3480
final_rank: 9

code_snippet: #line 1 "test/geometry/aoj1198.test.cpp"
#define PROBLEM "https://onlinejudge.u-aizu.ac.jp/problems/1198"

#line 2 "template/template.hpp"

#include<bits/stdc++.h>

#define rep(i,n) for (int i = 0; i < (int)(n); i++)
#define reb(i,n) for (int i = (int)(n-1); i >= 0; i--)
#define all(v) v.begin(),v.end()

using ll = long long;
using ld = long double;
using ull = unsigned long long;

using namespace std;

namespace noya2{

/*　~ (. _________ . /)　*/

}

using namespace noya2;

template <typename T> bool chmin(T &a, const T &b) {
    if (a <= b) return false;
    a = b;
    return true;
}
template <typename T> bool chmax(T &a, const T &b) {
    if (a >= b) return false;
    a = b;
    return true;
}

template<class T>istream &operator>>(istream &is, vector<T> &v){
    for (auto &e : v) is >> e;
    return is;
}

void fast_io(){
    cin.tie(0); ios::sync_with_stdio(0); cout << fixed << setprecision(15);
}

const int iinf = 1'000'000'007;
const ll linf = 2e18;
#line 4 "test/geometry/aoj1198.test.cpp"

#line 2 "geometry/partition_by_circle.hpp"

#line 2 "geometry/base_ld.hpp"

#line 4 "geometry/base_ld.hpp"

namespace noya2 {

using vec = complex<ld>;

const ld PI = acos(-1);

void ldout(int len = 20) {
    cout << fixed << setprecision(len);
}

int sgn(ld a, const ld eps = 1e-7) {
    return (a < -eps) ? -1 : (a > eps) ? 1 : 0;
}

bool same_vec(vec a, vec b) {
    a -= b;
    return sgn(a.real()) == 0 && sgn(a.imag()) == 0;
}

ld dot(const vec &a, const vec &b) {
    return (conj(a) * b).real();
}

ld cross(const vec &a, const vec &b) {
    return (conj(a) * b).imag();
}

int isp(const vec &a, const vec &b, const vec &c) {
    int cross_sgn = sgn(cross(b - a, c - a));
    if (cross_sgn == 0) {
        if (sgn(dot(b - a, c - a)) < 0) return -2;
        if (sgn(dot(a - b, c - b)) < 0) return 2;
    }
    return cross_sgn;
}

vec rot90(const vec &a) {
    return {-a.imag(), a.real()};
}

vec rot(const vec &a, ld rad) {
    return a * vec(cosl(rad), sinl(rad));
}


}  // namespace lib
#line 2 "data_structure/dsu.hpp"

#line 4 "data_structure/dsu.hpp"

namespace noya2{

struct dsu {
  public:
    dsu() : _n(0) {}
    dsu(int n) : _n(n), parent_or_size(n, -1) {}

    int merge(int a, int b) {
        assert(0 <= a && a < _n);
        assert(0 <= b && b < _n);
        int x = leader(a), y = leader(b);
        if (x == y) return x;
        if (-parent_or_size[x] < -parent_or_size[y]) std::swap(x, y);
        parent_or_size[x] += parent_or_size[y];
        parent_or_size[y] = x;
        return x;
    }

    bool same(int a, int b) {
        assert(0 <= a && a < _n);
        assert(0 <= b && b < _n);
        return leader(a) == leader(b);
    }

    int leader(int a) {
        assert(0 <= a && a < _n);
        if (parent_or_size[a] < 0) return a;
        return parent_or_size[a] = leader(parent_or_size[a]);
    }

    int size(int a) {
        assert(0 <= a && a < _n);
        return -parent_or_size[leader(a)];
    }

    std::vector<std::vector<int>> groups() {
        std::vector<int> leader_buf(_n), group_size(_n);
        for (int i = 0; i < _n; i++) {
            leader_buf[i] = leader(i);
            group_size[leader_buf[i]]++;
        }
        std::vector<std::vector<int>> result(_n);
        for (int i = 0; i < _n; i++) {
            result[i].reserve(group_size[i]);
        }
        for (int i = 0; i < _n; i++) {
            result[leader_buf[i]].push_back(i);
        }
        result.erase(
            std::remove_if(result.begin(), result.end(),
                           [&](const std::vector<int>& v) { return v.empty(); }),
            result.end());
        return result;
    }

  private:
    int _n;
    // root node: -1 * component size
    // otherwise: parent
    std::vector<int> parent_or_size;
};

} // namespace noya2
#line 6 "geometry/partition_by_circle.hpp"

namespace noya2{

using vec = complex<ld>;

struct circle {
    vec c;
    ld r;
};

// c1 != c2
vector<ld> cross_point_x(const circle &c1, const circle &c2){
    assert(sgn(abs(c1.r - c2.r)) != 0 || sgn(abs(c1.c - c2.c)) != 0);
    ld d = abs(c1.c - c2.c);
    // 円が離れすぎている
    if (sgn(d - c1.r - c2.r) > 0) return {};
    // 円が近すぎる
    if (sgn(abs(c1.r - c2.r) - d) > 0) return {};
    // 外接している
    if (sgn(d - c1.r - c2.r) == 0){
        return {(c1.r*c2.c.real() + c2.r*c1.c.real()) / (c1.r + c2.r)};
    }
    // 内接している
    if (sgn(abs(c1.r - c2.r) - d) == 0){
        return {(c1.r * c2.c.real() - c2.r * c1.c.real()) / (c1.r - c2.r)};
    }
    // 2 点を共有する
    ld e = (d * d + c1.r * c1.r - c2.r * c2.r) / (2 * d);
    vec p = c1.c + (c2.c - c1.c) * e / d;
    vec v((c1.c - c2.c).imag(),(c2.c - c1.c).real());
    v *= sqrtl(max(c1.r * c1.r - e * e, ld(0))) / abs(v);
    return {(p+v).real(),(p-v).real()};
}

vector<ld> cross_point_y(const circle &c, const ld &x){
    int cond = sgn(abs(c.c.real() - x) - c.r); 
    if (cond > 0) return {};
    if (cond == 0) return {c.c.imag()};
    ld ydiff = sqrtl(max(c.r*c.r - (x - c.c.real()) * (x - c.c.real()),ld(0)));
    return {c.c.imag()+ydiff,c.c.imag()-ydiff};
}

struct Partition_by_Circles {
    vector<circle> a;
    // 同じ円は与えられない
    void add_circle(circle c){
        // x 座標で区切る場所が重ならないように 1 rad 回転しておく
        c.c = rot(c.c,1);
        a.emplace_back(c);
    }
    ld coordinate_width(){
        ld res = 0.0;
        for (auto &c : a){
            chmax(res,abs(c.c)+c.r);
        }
        return res;
    }
    vector<ld> x_coordinates(){
        vector<ld> xs;
        for (auto &c : a){
            xs.emplace_back(c.c.real()-c.r);
            xs.emplace_back(c.c.real()+c.r);
        }
        int siza = a.size();
        for (int i = 0; i < siza-1; i++) for (int j = i+1; j < siza; j++){
            for (auto x : cross_point_x(a[i],a[j])){
                bool yet = true;
                for (auto testx : xs){
                    if (sgn(abs(x - testx)) == 0){
                        yet = false;
                        break;
                    }
                }
                if (yet) xs.emplace_back(x);
            }
        }
        sort(all(xs));
        return xs;
    }
    using ylr = tuple<ld,int,int>;
    vector<ylr> y_coordinates(ld x){
        vector<ylr> ys;
        auto add = [&](ld y, int l, int r){
            bool yet = true;
            for (auto &[testy, testl, testr] : ys){
                if (sgn(abs(y - testy)) == 0){
                    yet = false;
                    testl += l;
                    testr += r;
                    break;
                }
            }
            if (yet) ys.push_back({y,l,r});
        };
        for (auto &c : a){
            auto vecy = cross_point_y(c,x);
            if (vecy.empty()) continue;
            if ((int)(vecy.size()) == 2){
                for (auto y : vecy) add(y,1,1);
            }
            else {
                if (c.c.real() < x){
                    add(vecy[0],2,0);
                }
                else {
                    add(vecy[0],0,2);
                }
            }
        }
        sort(all(ys),[&](ylr lhs, ylr rhs){
            return get<0>(lhs) < get<0>(rhs);
        });
        return ys;
    }
    dsu d;
    vector<ld> xs;
    vector<int> lower_idx;
    map<int,int> mp;
    vector<vector<int>> build_graph(ld max_width = 0){
        // 大きな円で全体を囲ってしまう
        if (sgn(max_width) == 0) max_width = coordinate_width()*2;
        a.push_back({vec(0,0),max_width});
        // x 座標の列挙
        xs = x_coordinates();
        // 各 x について y 座標の列挙
        vector<vector<ylr>> yss; yss.reserve(xs.size());
        for (auto &x : xs) yss.push_back(y_coordinates(x));
        // x で切って領域を舐める
        int idx = 0;
        vector<pair<int,int>> es, merge;
        vector<int> cur;
        for (int itr = 0; itr < (int)(xs.size())-1; itr++){
            lower_idx.emplace_back(idx);
            vector<int> lid, rid;
            for (int l = 0; l < (int)(yss[itr].size()); l++){
                for (int t = 0; t < get<2>(yss[itr][l]); t++){
                    lid.emplace_back(l);
                }
            }
            for (int r = 0; r < (int)(yss[itr+1].size()); r++){
                for (int t = 0; t < get<1>(yss[itr+1][r]); t++){
                    rid.emplace_back(r);
                }
            }
            assert(lid.size() == rid.size());
            int pre = 0;
            vector<int> nxt;
            for (int i = 0; i < (int)(lid.size())-1; i++){
                for (int t = 0; t < rid[i+1]-rid[i]; t++){
                    nxt.emplace_back(idx);
                }
                if (i != 0){
                    es.emplace_back(idx-1,idx);
                }
                if (lid[i+1] - lid[i] != 0){
                    merge.emplace_back(cur[pre++],idx);
                }
                if (lid[i+1] - lid[i] == 2){
                    merge.emplace_back(cur[pre++],idx);
                }
                idx++;
            }
            swap(cur,nxt);
        }
        // グラフを構築
        d = dsu(idx);
        for (auto &[u, v] : merge) d.merge(u,v);
        set<int> st;
        for (int i = 0; i < idx; i++) st.insert(d.leader(i));
        int vid = 0;
        for (auto v : st) mp[v] = vid++;
        vector<vector<int>> graph(vid);
        for (auto [u, v] : es){
            u = mp[d.leader(u)];
            v = mp[d.leader(v)];
            graph[u].emplace_back(v);
            graph[v].emplace_back(u);
        }
        return graph;
    }
    int get_area_idx(vec p){
        p = rot(p,1);
        int xid = int(lower_bound(all(xs),p.real()) - xs.begin()) - 1;
        vector<ld> ys;
        for (auto &[y, l, r] : y_coordinates(p.real())) ys.emplace_back(y);
        int yid = int(lower_bound(all(ys),p.imag()) - ys.begin()) - 1;
        return mp[d.leader(lower_idx[xid] + yid)];
    }
};

} //namespace noya2
#line 6 "test/geometry/aoj1198.test.cpp"

int main(){
    while (true){
        int n, m; cin >> n >> m;
        if (n == 0 && m == 0) break;
        Partition_by_Circles pc;
        while (n--){
            ld x, y, r; cin >> x >> y >> r;
            pc.add_circle(circle({vec(x,y),r}));
        }
        pc.build_graph(100000);
        string ans = "";
        while (m--){
            ld px, py, qx, qy; cin >> px >> py >> qx >> qy;
            int ip = pc.get_area_idx(vec(px,py));
            int iq = pc.get_area_idx(vec(qx,qy));
            ans += (ip == iq ? "YES" : "NO");
            ans += (m == 0 ? '\n' : ' ');
        }
        cout << ans;
    }
}
accuracy: 1
time_ms: 80
memory_kb: 4640
final_rank: 16

code_snippet: #line 1 "test/geometry/aoj1198.test.cpp"
#define PROBLEM "https://onlinejudge.u-aizu.ac.jp/problems/1198"

#line 2 "template/template.hpp"

#include<bits/stdc++.h>

#define rep(i,n) for (int i = 0; i < (int)(n); i++)
#define reb(i,n) for (int i = (int)(n-1); i >= 0; i--)
#define all(v) v.begin(),v.end()

using ll = long long;
using ld = long double;
using ull = unsigned long long;

using namespace std;

namespace noya2{

/*　~ (. _________ . /)　*/

}

using namespace noya2;

template <typename T> bool chmin(T &a, const T &b) {
    if (a <= b) return false;
    a = b;
    return true;
}
template <typename T> bool chmax(T &a, const T &b) {
    if (a >= b) return false;
    a = b;
    return true;
}

template<class T>istream &operator>>(istream &is, vector<T> &v){
    for (auto &e : v) is >> e;
    return is;
}

void fast_io(){
    cin.tie(0); ios::sync_with_stdio(0); cout << fixed << setprecision(15);
}

const int iinf = 1'000'000'007;
const ll linf = 2e18;
#line 4 "test/geometry/aoj1198.test.cpp"

#line 2 "geometry/partition_by_circle.hpp"

#line 2 "geometry/base_ld.hpp"

#line 4 "geometry/base_ld.hpp"

namespace noya2 {

using vec = complex<ld>;

const ld PI = acos(-1);

void ldout(int len = 20) {
    cout << fixed << setprecision(len);
}

int sgn(ld a, const ld eps = 1e-7) {
    return (a < -eps) ? -1 : (a > eps) ? 1 : 0;
}

bool same_vec(vec a, vec b) {
    a -= b;
    return sgn(a.real()) == 0 && sgn(a.imag()) == 0;
}

ld dot(const vec &a, const vec &b) {
    return (conj(a) * b).real();
}

ld cross(const vec &a, const vec &b) {
    return (conj(a) * b).imag();
}

int isp(const vec &a, const vec &b, const vec &c) {
    int cross_sgn = sgn(cross(b - a, c - a));
    if (cross_sgn == 0) {
        if (sgn(dot(b - a, c - a)) < 0) return -2;
        if (sgn(dot(a - b, c - b)) < 0) return 2;
    }
    return cross_sgn;
}

vec rot90(const vec &a) {
    return {-a.imag(), a.real()};
}

vec rot(const vec &a, ld rad) {
    return a * vec(cosl(rad), sinl(rad));
}

bool comp_for_argument_sort(const vec &lhs, const vec &rhs) {
    // if (abs(arg(lhs)-arg(rhs)) < eps) return false; // need ?
    return arg(lhs) < arg(rhs);
}

}  // namespace lib
#line 5 "geometry/partition_by_circle.hpp"

namespace noya2{

struct dsu {
    dsu(int n = 0) : _n(n), pos(n, -1) {}

    int merge(int a, int b) {
        int x = leader(a), y = leader(b);
        if (x == y) return x;
        if (-pos[x] < -pos[y]) swap(x, y);
        pos[x] += pos[y];
        pos[y] = x;
        return x;
    }

    bool same(int a, int b) {
        return leader(a) == leader(b);
    }

    int leader(int a) {
        if (pos[a] < 0) return a;
        return pos[a] = leader(pos[a]);
    }
  private:
    int _n;
    vector<int> pos;
};

using vec = complex<ld>;

struct circle {
    vec c;
    ld r;
};

// c1 != c2
vector<ld> cross_point_x(const circle &c1, const circle &c2){
    assert(sgn(abs(c1.r - c2.r)) != 0 || sgn(abs(c1.c - c2.c)) != 0);
    ld d = abs(c1.c - c2.c);
    // 円が離れすぎている
    if (sgn(d - c1.r - c2.r) > 0) return {};
    // 円が近すぎる
    if (sgn(abs(c1.r - c2.r) - d) > 0) return {};
    // 外接している
    if (sgn(d - c1.r - c2.r) == 0){
        return {(c1.r*c2.c.real() + c2.r*c1.c.real()) / (c1.r + c2.r)};
    }
    // 内接している
    if (sgn(abs(c1.r - c2.r) - d) == 0){
        return {(c1.r * c2.c.real() - c2.r * c1.c.real()) / (c1.r - c2.r)};
    }
    // 2 点を共有する
    ld e = (d * d + c1.r * c1.r - c2.r * c2.r) / (2 * d);
    vec p = c1.c + (c2.c - c1.c) * e / d;
    vec v((c1.c - c2.c).imag(),(c2.c - c1.c).real());
    v *= sqrtl(max(c1.r * c1.r - e * e, ld(0))) / abs(v);
    return {(p+v).real(),(p-v).real()};
}

vector<ld> cross_point_y(const circle &c, const ld &x){
    int cond = sgn(abs(c.c.real() - x) - c.r); 
    if (cond > 0) return {};
    if (cond == 0) return {c.c.imag()};
    ld ydiff = sqrtl(max(c.r*c.r - (x - c.c.real()) * (x - c.c.real()),ld(0)));
    return {c.c.imag()+ydiff,c.c.imag()-ydiff};
}

struct Partition_by_Circles {
    vector<circle> a;
    // 同じ円は与えられない
    void add_circle(circle c){
        // x 座標で区切る場所が重ならないように 1 rad 回転しておく
        c.c = rot(c.c,1);
        a.emplace_back(c);
    }
    ld coordinate_width(){
        ld res = 0.0;
        for (auto &c : a){
            chmax(res,abs(c.c)+c.r);
        }
        return res;
    }
    vector<ld> x_coordinates(){
        vector<ld> xs;
        for (auto &c : a){
            xs.emplace_back(c.c.real()-c.r);
            xs.emplace_back(c.c.real()+c.r);
        }
        int siza = a.size();
        for (int i = 0; i < siza-1; i++) for (int j = i+1; j < siza; j++){
            for (auto x : cross_point_x(a[i],a[j])){
                bool yet = true;
                for (auto testx : xs){
                    if (sgn(abs(x - testx)) == 0){
                        yet = false;
                        break;
                    }
                }
                if (yet) xs.emplace_back(x);
            }
        }
        sort(all(xs));
        return xs;
    }
    using ylr = tuple<ld,int,int>;
    vector<ylr> y_coordinates(ld x){
        vector<ylr> ys;
        auto add = [&](ld y, int l, int r){
            bool yet = true;
            for (auto &[testy, testl, testr] : ys){
                if (sgn(abs(y - testy)) == 0){
                    yet = false;
                    testl += l;
                    testr += r;
                    break;
                }
            }
            if (yet) ys.push_back({y,l,r});
        };
        for (auto &c : a){
            auto vecy = cross_point_y(c,x);
            if (vecy.empty()) continue;
            if ((int)(vecy.size()) == 2){
                for (auto y : vecy) add(y,1,1);
            }
            else {
                if (c.c.real() < x){
                    add(vecy[0],2,0);
                }
                else {
                    add(vecy[0],0,2);
                }
            }
        }
        sort(all(ys),[&](ylr lhs, ylr rhs){
            return get<0>(lhs) < get<0>(rhs);
        });
        return ys;
    }
    dsu d;
    vector<ld> xs;
    vector<int> lower_idx;
    map<int,int> mp;
    vector<vector<int>> build_graph(){
        // 大きな円で全体を囲ってしまう
        a.push_back({vec(0,0),coordinate_width()*2});
        // x 座標の列挙
        xs = x_coordinates();
        // 各 x について y 座標の列挙
        vector<vector<ylr>> yss; yss.reserve(xs.size());
        for (auto &x : xs) yss.push_back(y_coordinates(x));
        // x で切って領域を舐める
        int idx = 0;
        vector<pair<int,int>> es, merge;
        vector<int> cur;
        for (int itr = 0; itr < (int)(xs.size())-1; itr++){
            lower_idx.emplace_back(idx);
            vector<int> lid, rid;
            for (int l = 0; l < (int)(yss[itr].size()); l++){
                for (int t = 0; t < get<2>(yss[itr][l]); t++){
                    lid.emplace_back(l);
                }
            }
            for (int r = 0; r < (int)(yss[itr+1].size()); r++){
                for (int t = 0; t < get<1>(yss[itr+1][r]); t++){
                    rid.emplace_back(r);
                }
            }
            assert(lid.size() == rid.size());
            int pre = 0;
            vector<int> nxt;
            for (int i = 0; i < (int)(lid.size())-1; i++){
                for (int t = 0; t < rid[i+1]-rid[i]; t++){
                    nxt.emplace_back(idx);
                }
                if (i != 0){
                    es.emplace_back(idx-1,idx);
                }
                if (lid[i+1] - lid[i] != 0){
                    merge.emplace_back(cur[pre++],idx);
                }
                if (lid[i+1] - lid[i] == 2){
                    merge.emplace_back(cur[pre++],idx);
                }
                idx++;
            }
            swap(cur,nxt);
        }
        // グラフを構築
        d = dsu(idx);
        for (auto &[u, v] : merge) d.merge(u,v);
        set<int> st;
        for (int i = 0; i < idx; i++) st.insert(d.leader(i));
        int vid = 0;
        for (auto v : st) mp[v] = vid++;
        vector<vector<int>> graph(vid);
        for (auto [u, v] : es){
            u = mp[d.leader(u)];
            v = mp[d.leader(v)];
            graph[u].emplace_back(v);
            graph[v].emplace_back(u);
        }
        return graph;
    }
    int get_area_idx(vec p){
        p = rot(p,1);
        int xid = int(lower_bound(all(xs),p.real()) - xs.begin()) - 1;
        vector<ld> ys;
        for (auto &[y, l, r] : y_coordinates(p.real())) ys.emplace_back(y);
        int yid = int(lower_bound(all(ys),p.imag()) - ys.begin()) - 1;
        return mp[d.leader(lower_idx[xid] + yid)];
    }
};

} //namespace noya2
#line 6 "test/geometry/aoj1198.test.cpp"

int main(){
    while (true){
        int n, m; cin >> n >> m;
        if (n == 0 && m == 0) break;
        Partition_by_Circles pc;
        while (n--){
            ld x, y, r; cin >> x >> y >> r;
            pc.add_circle(circle({vec(x,y),r}));
        }
        pc.build_graph();
        string ans = "";
        while (m--){
            ld px, py, qx, qy; cin >> px >> py >> qx >> qy;
            int ip = pc.get_area_idx(vec(px,py));
            int iq = pc.get_area_idx(vec(qx,qy));
            ans += (ip == iq ? "YES" : "NO");
            ans += (m == 0 ? '\n' : ' ');
        }
        cout << ans;
    }
}
accuracy: 1
time_ms: 80
memory_kb: 4724
final_rank: 17

code_snippet: #include <cmath>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;

class edge {
public:
	int to; bool vis;
	edge() : to(-1), vis(false) {}
	edge(int to_, bool vis_) : to(to_), vis(vis_) {}
};

int main() {
	while (true) {
		int N, M;
		cin >> N >> M;
		if (N == 0 && M == 0) {
			break;
		}
		vector<int> IX(N), IY(N), IR(N);
		for (int i = 0; i < N; i++) {
			cin >> IX[i] >> IY[i] >> IR[i];
		}
		auto sqr = [](int x) {
			return x * x;
		};
		vector<bool> answers(M, true);
		for (int id = 0; id < M; id++) {
			int PX, PY, QX, QY;
			cin >> PX >> PY >> QX >> QY;
			for (int i = 0; i < N; i++) {
				if ((sqr(PX - IX[i]) + sqr(PY - IY[i]) < sqr(IR[i])) != (sqr(QX - IX[i]) + sqr(QY - IY[i]) < sqr(IR[i]))) {
					answers[id] = false;
				}
			}
			if (answers[id]) {
				// step #1. find all necessary circles
				vector<int> X, Y, R;
				for (int i = 0; i < N; i++) {
					if (sqr(PX - IX[i]) + sqr(PY - IY[i]) >= sqr(IR[i])) {
						X.push_back(IX[i]);
						Y.push_back(IY[i]);
						R.push_back(IR[i]);
					}
				}
				int K = X.size();
				
				// step #2. make graph
				vector<vector<edge> > G(K);
				for (int i = 0; i < K; i++) {
					for (int j = i + 1; j < K; j++) {
						int dist = sqr(X[j] - X[i]) + sqr(Y[j] - Y[i]);
						if (sqr(R[i] - R[j]) < dist && dist < sqr(R[i] + R[j])) {
							G[i].push_back(edge(j, false));
							G[j].push_back(edge(i, false));
						}
					}
				}
				for (int i = 0; i < K; i++) {
					sort(G[i].begin(), G[i].end(), [&](const edge& ea, const edge& eb) {
						return atan2(Y[ea.to] - Y[i], X[ea.to] - X[i]) < atan2(Y[eb.to] - Y[i], X[eb.to] - X[i]);
					});
				}
				
				// step #3. compute polygons
				vector<vector<int> > polygons;
				for (int i = 0; i < K; i++) {
					for (edge &e : G[i]) {
						if (e.vis == false) {
							vector<int> subpolygon;
							int pre = i, pos = e.to;
							do {
								subpolygon.push_back(pos);
								for (int j = 0; j < int(G[pos].size()); j++) {
									if (G[pos][j].to == pre) {
										pre = pos;
										pos = G[pos][(j + 1) % G[pos].size()].to;
										G[pos][(j + 1) % G[pos].size()].vis = true;
										break;
									}
								}
							} while(pos != e.to);
							polygons.push_back(subpolygon);
						}
					}
				}
				
				// step #4. check if a point is inside polygon
				auto is_inside = [&](const vector<int>& poly, int x, int y) {
					int crosses = 0;
					for (int i = 0; i < int(poly.size()); i++) {
						int xa = X[poly[i]], ya = Y[poly[i]];
						int xb = X[poly[(i + 1) % poly.size()]], yb = Y[poly[(i + 1) % poly.size()]];
						if (ya > yb) {
							swap(xa, xb);
							swap(ya, yb);
						}
						if (ya <= y && y < yb && (xa - x) * (yb - y) - (xb - x) * (ya - y) >= 0) {
							crosses += 1;
						}
					}
					return (crosses % 2 == 1);
				};
				
				// step #5. check for all polygons
				for (vector<int> poly : polygons) {
					if (is_inside(poly, PX, PY) != is_inside(poly, QX, QY)) {
						answers[id] = false;
						break;
					}
				}
			}
		}
		for (int i = 0; i < M; i++) {
			if (i != 0) cout << ' ';
			cout << (answers[i] ? "YES" : "NO");
		}
		cout << endl;
	}
	return 0;
}
accuracy: 1
time_ms: 20
memory_kb: 3748
final_rank: 12

code_snippet: #include <bits/stdc++.h>

using namespace std;

using ld = long double;
using point = complex<ld>;
using polygon = vector<point>;

const ld pi = acosl(-1);

constexpr ld eps = 1e-9;

bool is_zero(ld x) {
    return abs(x) < eps;
}
int compare(ld x, ld y) {
    return is_zero(x - y) ? 0 : x < y ? -1 : +1;
}

struct circle {
    point p;
    ld r;
    circle() : p(0), r(0) {}
    circle(point p, ld r) : p(p), r(r) {}

    bool contains(point q) {
        return compare(abs(p - q), r) <= 0;
    }
};

bool is_in_polygon(polygon poly, point p) {
    int n = poly.size();
    ld sum = 0;
    for (int i = 0; i < n; ++i) {
        point p1 = poly[i], p2 = poly[(i + 1) % n];
        sum += arg((p2 - p) / (p1 - p));
    }
    return abs(sum) >= pi / 2;
}

void solve(int n, int m, vector<circle> cs) {
    vector<vector<int>> g = [&] {
        vector<set<int>> g(n);
        for (int i = 0; i < n; ++i) for (int j = 0; j < i; ++j) {
            ld d = abs(cs[i].p - cs[j].p);
            ld dl = abs(cs[i].r - cs[j].r), dr = cs[i].r + cs[j].r;
            if (compare(dl, d) < 0 and compare(d, dr) < 0) {
                g[i].insert(j);
                g[j].insert(i);
            }
        }

        auto deg_comp = [&](int i, int j) { return g[i].size() == g[j].size() ? i < j : g[i].size() < g[j].size(); };
        set<int, decltype(deg_comp)> st{ deg_comp };

        for (int i = 0; i < n; ++i) st.insert(i);

        while (st.size()) {
            int u = *st.begin();
            st.erase(st.begin());
            if (g[u].size() == 0) continue;
            if (g[u].size() >= 2) break;
            int v = *g[u].begin();
            g[u].clear();
            st.erase(v);
            g[v].erase(u);
            st.insert(v);
        }

        vector<vector<int>> res(n);
        for (int i = 0; i < n; ++i) for (int j : g[i]) res[i].push_back(j);
        return res;
    }();

    vector<map<int, int>> idx(n);

    for (int i = 0; i < n; ++i) {
        sort(
            g[i].begin(), g[i].end(),
            [&](int x, int y) {
                point p = cs[x].p - cs[i].p;
                point q = cs[y].p - cs[i].p;
                return arg(p) < arg(q);
            }
        );
        int siz = g[i].size();
        for (int j = 0; j < siz; ++j) {
            idx[i][g[i][j]] = j;
        }
    }

    vector<vector<int8_t>> seen(n);
    for (int i = 0; i < n; ++i) {
        seen[i].resize(g[i].size(), false);
    }

    vector<polygon> polygons;

    for (int i = 0; i < n; ++i) for (int j = 0; j < int(g[i].size()); ++j) if (not seen[i][j]) {
        polygon poly;
        for (int u = i, k = j;;) {
            poly.push_back(cs[u].p);
            int v = g[u][k];
            int rev_k = idx[v][u];
            assert(not seen[u][k]);
            seen[u][k] = true;
            int nk = (rev_k + g[v].size() - 1) % g[v].size();
            u = v, k = nk;
            if (u == i and k == j) {
                break;
            }
        }
        polygons.push_back(poly);
        // for (auto p : poly) {
        //     cerr << "(" << real(p) << ", " << imag(p) << "), ";
        // }
        // cerr << endl;
    }

    for (int i = 0; i < m; ++i) {
        point p, q;

        {
            ld x1, y1, x2, y2;
            cin >> x1 >> y1 >> x2 >> y2;
            p = { x1, y1 };
            q = { x2, y2 };
        }

        vector<int> cp, cq;
        for (int i = 0; i < n; ++i) {
            if (cs[i].contains(p)) {
                cp.push_back(i);
            }
            if (cs[i].contains(q)) {
                cq.push_back(i);
            }
        }

        string ans = "YES";

        if (cp.size() or cq.size()) {
            if (cp != cq) {
                ans = "NO";
            }
        } else {
            for (const auto& poly : polygons) {
                if (is_in_polygon(poly, p) xor is_in_polygon(poly, q)) {
                    ans = "NO";
                    break;
                }
            }
        }

        cout << ans << " \n"[i == m - 1];
    }
}

int main() {
    while (true) {
        int n, m;
        cin >> n >> m;

        if (n == 0 and m == 0) break;

        vector<circle> cs(n);
        for (int i = 0; i < n; ++i) {
            ld x, y, r;
            cin >> x >> y >> r;
            cs[i] = { point(x, y), r };
        }

        solve(n, m, cs);
    }
}
accuracy: 1
time_ms: 20
memory_kb: 3512
final_rank: 7

code_snippet: /*
正規版
メモ
norm(P) Pの絶対値の2乗 abs(P)の2乗
abs(P) Pの絶対値
arg(P)の戻り値の範囲は[-π, π]
polar(長さ,角度(ラジアン))でPointができる
int(-2.2+0.5)=-1
*/

#include <bits/stdc++.h>
using namespace std;
template<class T,class U>constexpr bool chmin(T&a,const U b){if(a<=b)return false;a=b;return true;}
template<class T,class U>constexpr bool chmax(T&a,const U b){if(a>=b)return false;a=b;return true;}
template<class T,class U>constexpr bool bitUP(const T n,const U k){return (n>>k)&1;}

////////////////////////////
// マクロや型
////////////////////////////

using DD=long double;

const DD EPS=1e-9;
const DD INF=1e50;
const DD PI=acosl(-1.0);
#define EQ(a,b) (abs( (a) - (b) )<EPS) //a==b
#define LS(a,b) ( (a)+EPS<(b) ) //a<b
#define GR(a,b) ( (a)>(b)+EPS ) //a>b
#define LE(a,b) ( (a)<(b)+EPS ) //a<=b
#define GE(a,b) ( (a)>(b)-EPS ) //a>=b

#define X real()
#define Y imag()


//点
using Point=complex<DD>;
istream &operator>>(istream &is, Point &p) {
  DD x,y;
  is>>x>>y;
  p=Point(x,y);
  return is;
}
void debug(Point p,string name=""){
    cout<<p.X<<" "<<p.Y<<" "<<name<<endl;
}

//点a→点bの線分
//aとbが等しい時、色々バグるから注意!
struct Segment{
    Point a,b;
    Segment()=default;
    Segment(Point a,Point b) :a(a),b(b){}
    Segment(DD ax,DD ay,DD bx,DD by):a(ax,ay),b(bx,by){}
    Segment(DD r,Point a) :a(a),b(a+polar((DD)1.0,r)){}
    void debug(bool isLine=false,string name=""){
        string type="Segment ";
        if(isLine) type="Line ";
        cout<<type<<a.X<<" "<<a.Y<<" "<<b.X<<" "<<b.Y<<" "<<name<<endl;
    }
};
using Line=Segment;

//円
struct Circle{
    Point p;
    DD r;
    Circle()=default;
    Circle(Point p,DD r):p(p),r(r){}
    void debug(string name=""){
        cout<<p.X<<" "<<p.Y<<" "<<"circle"<<"_"<<name<<endl;
        cout<<"Circle "<<p.X<<" "<<p.Y<<" "<<r<<endl;
    }
};

using Polygon=vector<Point>;
void debug(Polygon pol,string name=""){
    cout<<"Polygon"<<" "<<name<<endl;
    for(auto p:pol){
        cout<<p.X<<" "<<p.Y<<endl;
    }
    cout<<"..."<<endl;
}

////////////////////////////
// 基本計算
////////////////////////////

//度→ラジアン
inline DD torad(const DD deg){return deg*PI/180;}
//ラジアン→度
inline DD todeg(const DD rad){return rad*180/PI;}
//内積 |a||b|cosθ
inline DD dot(const Point &a,const Point &b){return (conj(a)*b).X;}
//外積 |a||b|sinθ
inline DD cross(const Point &a,const Point &b){return (conj(a)*b).Y;}
//ベクトルpを反時計回りにtheta(ラジアン)回転
Point rotate(const Point &p,const DD theta){return p*Point(cos(theta),sin(theta));}

//余弦定理 cos(角度abc(ラジアン))を返す
//長さの対応に注意 ab:辺abの長さ
//verify:https://codeforces.com/gym/102056/problem/F
DD cosine_formula(const DD ab,const DD bc,const DD ca){
    return (ab*ab+bc*bc-ca*ca)/(2*ab*bc);
}

inline bool xy_sort(const Point &a,const Point &b){
    if(a.X+EPS<b.X) return true;
    if(EQ(a.X,b.X) && a.Y+EPS<b.Y) return true;
    return false;
}
inline bool yx_sort(const Point &a,const Point &b){
    if(a.Y+EPS<b.Y) return true;
    if(EQ(a.Y,b.Y) && a.X+EPS<b.X) return true;
    return false;
}
namespace std{
    inline bool operator<(const Point &a,const Point &b)noexcept{
        return xy_sort(a,b);
    }
}
//DDの比較関数
//map<DD,vector<pii>,DDcom>
struct DDcom{
    bool operator()(const DD a, const DD b) const noexcept {
        return a+EPS<b;
    }
};


////////////////////////////
// 平行や直交
////////////////////////////

bool isOrthogonal(const Point &a,const Point &b){
    return EQ(dot(a,b),0.0);
}
//verify:http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_2_A
bool isOrthogonal(const Segment &s,const Segment &t){
    return EQ(dot(s.a-s.b,t.a-t.b),0.0);
}
bool isParallel(const Point &a,const Point &b){
    return EQ(cross(a,b),0.0);
}
//verify:http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_2_A
bool isParallel(const Segment &s,const Segment &t){
    return EQ(cross(s.a-s.b,t.a-t.b),0);
}
//線分a→bに対して点cがどの位置にあるか
//反時計回り:1　時計回り:-1 直線上(a,b,c:-2 a,c,b:0 c,a,b:2)
//verify:http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_1_C
//線分a→bの端点にcがあるなら0と判定されるはず
int ccw(const Point &a,Point b,Point c){
    if(EQ(abs(b-c),0) or EQ(abs(a-c),0)) return 0;
    b-=a;c-=a;
    if(cross(b,c)>EPS) return 1;
    if(cross(b,c)<-EPS) return -1;
    if(dot(b,c)<-EPS) return 2;
    if(LS(norm(b),norm(c))) return -2;
    return 0;
}

////////////////////////////
// 射影と反射
////////////////////////////

//射影
//直線lに点pから垂線を引いたときの交点
//verify:http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_1_A
Point project(const Point &p,const Line &l){
    Point base=l.b-l.a;
    DD r=dot(p-l.a,base)/norm(base);
    return l.a+base*r;
}
//反射
//直線lに対して点pと対称な点
//verify:http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_1_B
Point reflect(const Point &p,const Line &l){
    return p+(project(p,l)-p)*(DD)2.0;
}
//反射
//直線lに対して線分sと対称な線分(直線でも使える)
//verify:https://onlinejudge.u-aizu.ac.jp/solutions/problem/1171/review/6009447/bin101/C++17
Segment reflect(const Segment &s,const Line &l){
    return Segment(reflect(s.a,l) , reflect(s.b,l));
}

////////////////////////////
// 点との距離
////////////////////////////

//点と直線の距離
DD DistPL(const Point &p,const Line &l){
    return abs(cross(l.b-l.a,p-l.a))/abs(l.b-l.a);
}
//点と線分の距離
DD DistPS(const Point &p,const Segment &s){
    if( dot(s.b-s.a,p-s.a)<0.0 ) return abs(p-s.a);
    if( dot(s.a-s.b,p-s.b)<0.0 ) return abs(p-s.b);
    return DistPL(p,s);
}

////////////////////////////
// 線分と直線
////////////////////////////

//線分a-b,c-dは交差するか?
//接する場合も交差すると判定
//接する場合は交差しないとするなら<=を<に変換
bool intersect(const Point &a,const Point &b,const Point &c,const Point &d){
    return(ccw(a,b,c)*ccw(a,b,d)<=0 && ccw(c,d,a)*ccw(c,d,b)<=0);
}
//線分s,tは交差するか？
//接する場合も交差すると判定
//verify:http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_2_B&lang=ja
//接する場合は交差しないとするならintersectの<=を<に変換
bool intersect(const Segment &s,const Segment &t){
    return intersect(s.a,s.b,t.a,t.b);
}
// 2直線の交点
// 線分の場合はintersectをしてから使う
//verify:http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_2_C&lang=ja
Point crossPoint(const Line &s,const Line &t){
    Point base=t.b-t.a;
    DD d1=cross(base,t.a-s.a);
    DD d2=cross(base,s.b-s.a);
    if(EQ(d1,0) and EQ(d2,0)) return s.a; //同じ直線
    if(EQ(d2,0)) assert(false); //交点がない
    return s.a+d1/d2*(s.b-s.a);
}
//線分と線分の距離
//verify:http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_2_D&lang=ja
DD Dist(const Segment &s,const Segment &t){
    if(intersect(s,t)) return 0.0;
    return min(min(DistPS(t.a,s),DistPS(t.b,s)),min(DistPS(s.a,t),DistPS(s.b,t)) );
}
bool issame(const Line &l,const Line &m){
    if (GR(abs(cross(m.a - l.a, l.b - l.a)),0) ) return false;
    if (GR(abs(cross(m.b - l.a, l.b - l.a)),0) ) return false;
    return true;
}

////////////////////////////
// 円
////////////////////////////
//直線lと円Cの交点
//l.aにより近い方がindexが小さい
//veirfy:http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_7_D
vector<Point> crossPointLC(const Line &l,const Circle &c){
    vector<Point> ret;
    if(GR(DistPL(c.p,l),c.r)) return ret; //交点がないとき、空ベクトルを返す
    Point pr=project(c.p,l);
    Point e=(l.b-l.a)/(abs(l.b-l.a));
    DD base=sqrt(c.r*c.r-norm(pr-c.p));
    ret.push_back(pr-e*base);
    ret.push_back(pr+e*base);
    if(EQ(DistPL(c.p,l),c.r)) ret.pop_back(); //接するとき
    return ret;
}
//線分sと円cの交点
//交点がないときは空ベクトルを返す
//線分の端が交わる場合も交点とみなす
//s.aにより近い方がindexが小さい
vector<Point> crossPointSC(const Segment &s,const Circle &c){
    vector<Point> ret;
    if(DistPS(c.p,s)>c.r+EPS) return ret;
    auto koho=crossPointLC(s,c);
    for(auto p:koho){
        if(ccw(s.a,s.b,p)==0 or EQ(abs(p-s.a),0) or EQ(abs(p-s.b),0)) ret.push_back(p);
    }
    return ret;
}

//円aと円bの共通接線の数
//離れている:4 外接:3 交わる:2 内接:1 内包:0
//verify:http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_7_A
int intersect(const Circle &a,const Circle &b){
    DD d=abs(a.p-b.p);
    if(GR(d,a.r+b.r)) return 4;
    if(EQ(d,a.r+b.r)) return 3;
    if(EQ(d,abs(a.r-b.r))) return 1;
    if(LS(d,abs(a.r-b.r))) return 0;
    return 2;
}

//円aと円bの交点
//交点がないときは空ベクトルを返す
//事前にintersectをしたほうがいいかも
//verify:http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_7_E
vector<Point> crossPoint(const Circle &a,const Circle &b){
    vector<Point> ret;
    if(GR(abs(a.p-b.p),a.r+b.r)) return ret;
    DD d=abs(a.p-b.p);
    DD s=acosl((a.r*a.r+d*d-b.r*b.r)/(2*a.r*d)); //0~π
    DD t=arg(b.p-a.p);
    if(EQ(a.r+b.r,d)) ret.push_back(a.p+polar(a.r,t));
    else ret.push_back(a.p+polar(a.r,t+s)),ret.push_back(a.p+polar(a.r,t-s));
    return ret;
}

//点pから円cに接線を引いたときの接点
//verify:http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_7_F&lang=ja
vector<Point> TanLine(const Point &p,const Circle &c){
    vector<Point> ret;
    DD d=abs(p-c.p);
    if(LS(d,c.r)) return ret; //pがcの内部にある場合
    if(EQ(d,c.r)){
        ret.push_back(p);
        return ret; //円の線上にある場合
    } 
    return crossPoint(c,Circle(p,sqrt(d*d-c.r*c.r)));
}

//円a,bの共通接線
//https://www.slideshare.net/kujira16/ss-35861169
//Line.aは円aと接線の交点
//(接線と円bの交点)と(接線と円aの交点)が違う場合はLine.bは(接線と円bの交点)
//一致する場合は円aの中心からLine.aに向かうベクトルを反時計回りに90度回転したものを
//Line.aに足したもの
//verify:http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_7_G
//verify:http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=2201
vector<Line> TanLine(const Circle &a,const Circle &b){
    DD d=abs(a.p-b.p);
    vector<Line> ret;
    //外接線
    if(intersect(a,b)>=2){
        if(EQ(a.r,b.r)){
            Point up=polar(a.r,arg(b.p-a.p)+PI/2);
            ret.emplace_back(a.p+up,b.p+up);
            ret.emplace_back(a.p-up,b.p-up);
        }else{
            Point q=(a.p*b.r-b.p*a.r)/(b.r-a.r);
            auto as=TanLine(q,a);
            auto bs=TanLine(q,b);
            assert(as.size()==2 and bs.size()==2);
            for(int i=0;i<2;i++){
                ret.emplace_back(as[i],bs[i]);
            }
        }
    }else if(intersect(a,b)==1){ //内接する場合
        Point dir;
        if(a.r>b.r) dir=polar(a.r,arg(b.p-a.p));
        else dir=polar(a.r,arg(a.p-b.p));
        ret.emplace_back(a.p+dir,a.p+dir+rotate(dir,PI/2));
    }
    //内接線
    if(GR(abs(a.p-b.p),a.r+b.r)){ //円が離れている場合
        Point q=a.p+(b.p-a.p)*a.r/(a.r+b.r);
        auto as=TanLine(q,a);
        auto bs=TanLine(q,b);
        assert(as.size()==2 and bs.size()==2);
        for(int i=0;i<2;i++){
            ret.emplace_back(as[i],bs[i]);
        }
    }else if(EQ(d,a.r+b.r)){ //円が接している場合
        Point dir=polar(a.r,arg(b.p-a.p));
        ret.emplace_back(a.p+dir,a.p+dir+rotate(dir,PI/2));
    }
    return ret;
}
//２つの円の共通部分の面積
//参考: https://tjkendev.github.io/procon-library/python/geometry/circles_intersection_area.html
//verify: https://judge.u-aizu.ac.jp/onlinejudge/review.jsp?rid=6005736#1
DD Area(const Circle &c1,const Circle &c2){
    DD cd=abs(c1.p-c2.p);
    if(LE(c1.r+c2.r,cd)) return 0; //円が離れている

    if(LE(cd,abs(c1.r-c2.r))) //片方の円が他方を包んでいる
        return min(c1.r,c2.r)*min(c1.r,c2.r)*PI;

    DD theta1=acosl(cosine_formula(c1.r,cd,c2.r));
    DD theta2=acosl(cosine_formula(c2.r,cd,c1.r));

    return c1.r*c1.r*theta1+c2.r*c2.r*theta2-c1.r*cd*sin(theta1);
}


////////////////////////////
// 三角形
////////////////////////////

//ヘロンの公式　三角形の面積を返す
//三角形が成立するか(平行ではないか)を忘れずに
DD Heron(const DD ad,const DD bd,const DD cd){
    DD s=(ad+bd+cd)/2;
    return sqrt(s*(s-ad)*(s-bd)*(s-cd));
}
//三角形の外接円
//isParallel()を使って三角形が成立するか(平行ではないか)を忘れずに
//verify:http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_7_C
Circle CircumscribedCircle(const Point &a,const Point &b,const Point &c){
    Point bc=(b+c)/(DD)2.0;
    Line aa(bc,bc+rotate(c-b,PI/2));
    Point ab=(a+b)/(DD)2.0;
    Line cc(ab,ab+rotate(b-a,PI/2));
    Point p=crossPoint(aa,cc);
    return Circle(p,abs(a-p));
}
//三角形の内接円
//isParallel()を使って三角形が成立するか(平行ではないか)を忘れずに
//verify:http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_7_B
Circle InCircle(const Point &a,const Point &b,const Point &c){
    Line aa(a,a+polar((DD)1.0,(arg(b-a)+arg(c-a))/(DD)2.0));
    Line bb(b,b+polar((DD)1.0,(arg(a-b)+arg(c-b))/(DD)2.0));
    Point p=crossPoint(aa,bb);
    return Circle(p,DistPL(p,Line(a,b)));
}

////////////////////////////
// 多角形
////////////////////////////

//点pが多角形gに対してどの位置にあるか
//中:2 辺上:1 外:0
//凸多角形である必要ない
//verify:http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_3_C
int contains(const Point &p,const vector<Point> &g){
    int n=(int)g.size();
    int x=false;
    for(int i=0;i<n;i++){
        Point a=g[i]-p,b=g[(i+1)%n]-p;
        if(EQ(cross(a,b),0) && dot(a,b)<EPS) return 1;
        if(a.Y>b.Y) swap(a,b);
        if(a.Y<EPS && EPS<b.Y && cross(a,b)>EPS) x=!x;
    }
    return (x?2:0);
}
//凸性判定
//多角形gは凸か？
//gは反時計回りでも時計回りでもいい
//verify:http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_3_B
bool isConvex(const vector<Point> &g){
    int n=(int)g.size();
    if(n<=3) return true;
    int flag=0;
    int t;
    for(int i=0;i<n;i++){
        Point a(g[(i+1)%n]-g[i]),b(g[(i+2)%n]-g[i]);
        if(cross(a,b)>EPS) t=1;
        else if(cross(a,b)<-EPS) t=-1;
        else continue;
        if(flag==-t) return false;
        flag=t;
    }
    return true;
}

//凸包　アンドリューのアルゴリズム
//O(NlogN)
//反時計回りの多角形を返す
//verify:http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_4_A
vector<Point> ConvexHull(vector<Point> s,const bool on_edge){
    int j;
    if(on_edge) j=-1;
    else j=1;
    int sz=(int)s.size();
    if(sz<3) return s;
    sort(s.begin(),s.end(),yx_sort);

    int n=0;
    vector<Point> res(2*sz);
    for(int i=0;i<sz;i++){
        while(n>=2 && cross(res[n-1]-res[n-2],s[i]-res[n-2])<EPS*j){
            n--;
        }
        res[n]=s[i];
        n++;
    }
    int t=n+1;
    for(int i=sz-2;i>=0;i--){
        while(n>=t && cross(res[n-1]-res[n-2],s[i]-res[n-2])<EPS*j){
            n--;
        }
        res[n]=s[i];
        n++;
    }
    res.resize(n-1);
    return res;
}

//多角形g(凸でなくてもいい)の符号付き面積
//反時計回りの多角形なら正の値を返す　時計回りなら負
//O(N)
//https://imagingsolution.net/math/calc_n_point_area/
//verify:http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_3_A
DD Area(const vector<Point> &g){
    DD ret=0.0;
    int n=(int)g.size();
    for(int i=0;i<n;i++){
        ret+=cross(g[i],g[(i+1)%n]);
    }
    return ret/2.0;
}

//凸多角形gを直線lで切断
//直線の左側(向きを考慮)にできる凸多角形を返す
//多角形gが反時計回りならば、反時計回りで返す(時計回りなら時計回り)
//直線上の頂点を含む線分の交点は含めてない
//verify:http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_4_C
vector<Point> ConvexCut(const vector<Point> &g,const Line &l){
    vector<Point> ret;
    int gz=(int)g.size();
    for(int i=0;i<gz;i++){
        Point now=g[i],next=g[(i+1)%gz];
        if(ccw(l.a,l.b,now)!=-1) ret.push_back(now);
        if(EQ(DistPL(now,l),0) or EQ(DistPL(next,l),0)) continue;
        if(ccw(l.a,l.b,now)*ccw(l.a,l.b,next)<0){
            ret.push_back(crossPoint(Line(now,next),l));
        }
    }
    return ret;
}
//部品
inline DD calc(const Point &a,const Point &b,const DD &r,const bool triangle){
    if(triangle) return cross(a,b);
    else return r*r*arg(b/a);
}
//部品
DD calcArea(const DD &r,const Point &a,const Point &b){
    if(EQ(abs(a-b),0)) return 0;
    bool ina=(abs(a)<r+EPS);
    bool inb=(abs(b)<r+EPS);
    if(ina && inb) return cross(a,b);
    auto cr=crossPointSC(Segment(a,b),Circle(Point(0,0),r));
    if(cr.empty()) return calc(a,b,r,false);
    auto s=cr[0],t=cr.back();
    return calc(s,t,r,true)+calc(a,s,r,ina)+calc(t,b,r,inb);
}
//円と多角形の共通部分の面積
//多角形が反時計回りならば正の値を返す
//凸多角形でなくても大丈夫
//http://drken1215.hatenablog.com/entry/2020/02/02/091000
//verify:http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_7_H&lang=ja
DD Area(const Circle &c,const vector<Point> &g){
    DD ret=0.0;
    int gz=g.size();
    if(gz<=2) return ret;
    for(int i=0;i<gz;i++){
        Point a=g[i]-c.p,b=g[(i+1)%gz]-c.p;
        ret+=calcArea(c.r,g[i]-c.p,g[(i+1)%gz]-c.p);
    }
    return ret/2.0;
}

//点と多角形の距離
//点が多角形の中にあるなら0
DD Dist(const Point &a,const vector<Point> &b){
	if(b.size()==1) return abs(a-b[0]);
	if(contains(a,b)>=1) return 0.0L;
	DD ret=INF;
	for(int i=0;i<b.size();i++){
		chmin(ret,DistPS(a,Segment(b[i],b[(i+1)%b.size()])));
	}
	return ret;
}

//多角形と多角形の距離
//どちらかが内側に入っていたら0
//全ての線分の組み合わせの距離を求めて、その最小が答え
//O(size(a)*size(b))
//sizeが1の時は点との距離になる
//verify:http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=2827
DD Dist(const vector<Point> &a,const vector<Point> &b){
	DD ret=INF;
	if(a.size()==1) return Dist(a[0],b);
	if(b.size()==1) return Dist(b[0],a);

	if(contains(a[0],b)>=1) return 0.0L;
	if(contains(b[0],a)>=1) return 0.0L;

	for(int i=0;i<a.size();i++){
		for(int j=0;j<b.size();j++){
			Segment sa(a[i],a[(i+1)%a.size()]);
			Segment sb(b[j],b[(j+1)%b.size()]);
			chmin(ret,Dist(sa,sb));
		}
	}
	return ret;
}

////////////////////////////
// 最近(遠)点間距離
////////////////////////////
//部品
DD RecClosetPair(const vector<Point>::iterator it,const int n){
    if(n<=1) return INF;
    int m=n/2;
    DD x=it[m].X;
    DD d=min(RecClosetPair(it,m),RecClosetPair(it+m,n-m));
    inplace_merge(it,it+m,it+n,yx_sort);
    vector<Point> v;
    for(int i=0;i<n;i++){
        if(abs(it[i].X-x)>=d) continue;
        for(int j=0;j<v.size();j++){
            DD dy=it[i].Y-v[(int)v.size()-1-j].Y;
            if(dy>=d) break;
            DD dx=it[i].X-v[(int)v.size()-1-j].X;
            d=min(d,sqrt(dx*dx+dy*dy));
        }
        v.push_back(it[i]);
    }
    return d;
}
//最近点対の距離
//点が1つのときINFを返す
//O(NlogN)
//verify:http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_5_A
DD ClosetPair(vector<Point> g){
    sort(g.begin(),g.end(),xy_sort);
    return RecClosetPair(g.begin(),g.size());
}

//最遠頂点間
//verify:http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_4_B
DD Diameter(vector<Point> g){
    g=ConvexHull(g,1);
    int gz=g.size();
    int m=0,M=0;
    for(int i=1;i<gz;i++){
        if(g[i].Y<g[m].Y) m=i;
        if(g[i].Y>g[M].Y) M=i;
    }
    DD ret=0;
    int sm=m,sM=M;
    while(m!=sM || M!=sm){
        ret=max(ret,norm(g[m]-g[M]));
        if(cross(g[(m+1)%gz]-g[m],g[(M+1)%gz]-g[M])<0) m=(m+1)%gz;
        else M=(M+1)%gz;
    }
    return sqrt(ret);
}
//動的凸包
//辺上にある点は含めない
//verify:https://atcoder.jp/contests/geocon2013/tasks/geocon2013_c
struct DynamicConvex{
	set<Point> up,down;
	DynamicConvex(){}
 
	bool isContain(set<Point> &pol,Point p){
		if(pol.size()==0) return false;
		if(pol.size()==1){
			Point q=*begin(pol);
			return EQ(q.X,p.X) and GE(q.Y,p.Y);
		}
		assert(pol.size()>=2);
        auto left=begin(pol);
        auto right=(--end(pol));
        if(LS(p.X,left->X) or GR(p.X,right->X)) return false;
        
        
        auto q1=pol.upper_bound(Point(p.X,INF));
        if(q1==end(pol)){
            return GE(right->Y,p.Y);
        }
        auto q2=q1--; //q1が範囲外になるのは最初に弾いてる
        return GE(cross(p-*q1,*q2-*q1),0.0);
	}
    bool isContain(Point p){
        if(not isContain(up,p)) return false;
        if(not isContain(down,Point(p.X,-p.Y))) return false;
        return true;
    }
 
	void add(set<Point> &pol,Point p){
		if(pol.size()==0){
			pol.insert(p);
			return;
		}
		if(pol.size()==1){
            Point q=*begin(pol);
			if(EQ(q.X,p.X)){
                pol.clear();
                pol.insert(max(p,q));
			}else{
				pol.insert(p);
			}
            return;
		}
        assert(pol.size()>=2);
        if(isContain(pol,p)) return;
        //left
        auto q1=pol.upper_bound(Point(p.X,INF));
        if(q1!=begin(pol)){
            q1--;
            while(pol.size()>=1 and q1!=begin(pol)){
                auto q2=q1--;
                if(GR(cross(*q2-p,*q1-p),0)) break;
                pol.erase(q2);
            }
        }
        //right
        q1=pol.lower_bound(Point(p.X,-INF));
        if(q1!=end(pol)){
            while(pol.size()>1 and q1!=--end(pol)){
                auto q2=q1++;
                if(GR(cross(*q1-p,*q2-p),0.0)) break;
                pol.erase(q2);
            }
        }
        pol.insert(p);
	}
 
	void add(Point p){
		add(up,p);
		add(down,Point(p.X,-p.Y)); //upと同じように処理をしたいから
	}
};

//垂直二等分線
//p-↑-q こんなかんじ
//voronoiで使う
Line bisector(const Point &p, const Point &q) {
    Point c=(p+q)/DD(2.0);
    Point v=rotate(q-p,PI/2);
    v/=abs(v);
    return Line(c-v,c+v);
}

//ボロノイ図
//pol:全体 ps:点の集合 id:中心となる点の添字 pp:中心となる点
//計算量: pol.size()*ps.size()
//verify: https://judge.u-aizu.ac.jp/onlinejudge/review.jsp?rid=6005648#1
vector<Point> voronoi(Polygon pol,const vector<Point> &ps,int id=-1,const Point pp=Point()){
    Point p;
    if(id!=-1) p=ps[id];
    else p=pp;
    for(size_t i=0;i<ps.size();i++){
        if(i==id) continue;
        Line l=bisector(p,ps[i]);
        pol=ConvexCut(pol,l);
    }
    return pol;
}

vector<Polygon>  Pol;
vector<int> vp;
vector<Circle> C;
using Graph=vector<vector<int>>;
Graph g;
vector<bool> used;

void dfs(int idx,int par){
    if(used[idx]) return;
    used[idx]=true;
    vp.push_back(idx);

    for(int to:g[idx]){
        if(to==par) continue;
        int start=-1;
        for(int i=0;i<vp.size();i++){
            if(vp[i]==to) start=i;
        }
        if(start==-1){
            dfs(to,idx);
        }else{
            Polygon pol;
            for(int i=start;i<vp.size();i++){
                pol.push_back(C[vp[i]].p);
            }
            Pol.push_back(pol);
        }
    }
    vp.pop_back();
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout << fixed << setprecision(20);

while(true){
    int N,M;
    cin>>N>>M;
    if(N==0 and M==0) return 0;
    C.clear();
    C.resize(N);
    for(int i=0;i<N;i++) cin>>C[i].p>>C[i].r/*,C[i].debug({char('a'+i)})*/;

    g.clear();
    g.resize(N);
    for(int i=0;i<N;i++){
        for(int j=i+1;j<N;j++){
            if(intersect(C[i],C[j])==2){
                g[i].push_back(j);
                g[j].push_back(i);
            }
        }
    }
    used.clear();
    used.resize(N);
    Pol.clear();

    dfs(0,-1);
    for(int i=0;i<N;i++){
        dfs(i,-1);
    }
    vector<bool> ans;
    for(int i=0;i<M;i++){
        Point p,q; cin>>p>>q;
        /*string s;
        s+='a'+i;
        debug(p,s); debug(q,s);*/
        vector<bool> cp(N),cq(N);
        bool ok=false;
        for(int j=0;j<N;j++){
            if(LE(abs(p-C[j].p),C[j].r)){
                ok=true;
                cp[j]=true;
            }
            if(LE(abs(q-C[j].p),C[j].r)){
                cq[j]=true;
            }
        }
        if(cp==cq and ok){
            ans.push_back(true);
        }else if(cp!=cq){
            ans.push_back(false);
        }else{
            cp.clear(); cq.clear();
            cp.resize(Pol.size()); cq.resize(Pol.size());
            for(int j=0;j<Pol.size();j++){
                cp[j]=contains(p,Pol[j]);
                cq[j]=contains(q,Pol[j]);
            }
            if(cp==cq) ans.push_back(true);
            else ans.push_back(false);
        }
    }
    for(int i=0;i<ans.size();i++){
        if(i) cout<<" ";
        if(ans[i]) cout<<"YES";
        else cout<<"NO";
    }
    cout<<endl;
}
}
accuracy: 1
time_ms: 10
memory_kb: 3664
final_rank: 10

code_snippet: #include <bits/stdc++.h>
using namespace std;

namespace geometry{
  constexpr double eps = 1e-9;
  bool eq(double a, double b){ return fabs(a - b) < eps; }
  template<typename T>  bool eq(T a, T b){ return a == b; }
  bool is_zero(double a){ return fabs(a) < eps; }
  template<typename T> bool is_zero(T a){ return a == 0; }
  bool le(double a, double b){ return a <= b + eps; }
  template<typename T> bool le(T a, T b){ return a <= b; }
  bool lt(double a, double b){ return a < b - eps; }
  template<typename T> bool lt(T a, T b){ return a < b; }
};


template<class T> struct Point{
  T x,y;
  Point(){}
  Point(T x, T y) : x(x), y(y) {}
  Point(const pair<T,T> &p) : x(p.first), y(p.second) {}

  Point operator*(const T b) const { return Point(x * b, y * b); }
  Point operator/(const T b) const { return Point(x / b, y / b); }
  Point operator+(const Point &b) const { return Point(x + b.x, y + b.y); }
  Point operator-(const Point &b) const { return Point(x - b.x, y - b.y); }
  Point operator*(const Point &b) const { return Point(x*b.x - y*b.y, x*b.y + y*b.x); }
  Point operator/(const Point &b) const { return Point(x*b.x + y*b.y, y*b.x - x*b.y)/(b.x*b.x + b.y*b.y); }

  Point &operator*=(const T b){ return (*this) = (*this) * b; }
  Point &operator/=(const T b){ return (*this) = (*this) / b; }
  Point &operator+=(const Point &b){ return (*this) = (*this) + b; }
  Point &operator-=(const Point &b){ return (*this) = (*this) - b; }
  Point &operator*=(const Point &b){ return (*this) = (*this) * b; }
  Point &operator/=(const Point &b){ return (*this) = (*this) / b; }
  
  bool operator==(const Point &b) const {
    return geometry::eq(x, b.x) && geometry::eq(y, b.y);
  }
  bool operator<(const Point &b) const { 
    if(geometry::eq(x, b.x)) return y < b.y;
    return x < b.x;
  }
  T Norm() const { return x * x + y * y; }
  double Abs() const { return hypot<double>(x, y); }
  double dist(const Point &b) const { return hypot<double>(x - b.x, y - b.y); }
  double arg() const { return atan2<double>(y, x); }
  
  Point ArgVec() const {
    if((*this) == Point(0, 0)) return (*this);
    if(geometry::is_zero(x)) return Point(0, -1);
    return (*this) / gcd(x, y) * (x < 0 ? -1 : 1);
  }

  int ort() const {
    if(geometry::is_zero(x) && geometry::is_zero(y)) return 0;
    if(geometry::is_zero(y)) return x > 0 ? 1 : 3;
    if(geometry::is_zero(x)) return y > 0 ? 2 : 4;
    if (y > 0) return x > 0 ? 1 : 2;
    else return x > 0 ? 4 : 3;
  }
  Point rotate90() const { return Point(-y, x);}
  Point rotate180() const { return Point(-x, -y);}
  Point rotate270() const { return Point(y, -x);}

  Point flip_x(T p = 0) const { return Point(p*2 - x, y);}
  Point flip_y(T p = 0) const { return Point(x, p*2 - y);}

  Point rotate(double theta) const {
    return Point(cos(theta)*x - sin(theta)*y, sin(theta)*x + cos(theta)*y);
  }

  Point flip(double theta) const {
    return (*this).rotate(-theta).flip_y().rotate(theta);
  }

  friend ostream &operator<<(ostream &os, const Point &p) {
    return os << p.x << " " << p.y;
  }
  friend istream &operator>>(istream &is, Point &p) {
    T a, b; is >> a >> b;
    p = Point<T>(a, b);
    return (is);
  }
};

template<typename T> Point<T> Polar(const T& rho, const T& theta = 0){
  return Point<T>(rho * cos(theta), rho * sin(theta));
}


template<typename T> using Polygon = vector<Point<T>>;

template<class T> T Cross(const Point<T> &a, const Point<T> &b) { return a.x * b.y - a.y * b.x; }

template<class T> T Dot(const Point<T> &a, const Point<T> &b) { return a.x * b.x + a.y * b.y; }

template<class T> bool Intersect(const Point<T> &a, const Point<T> &b, const Point<T> &c, const Point<T> &d) {
  return (iSP(a, b, c)*iSP(a, b, d) <= 0) && (iSP(c, d, a)*iSP(c, d, b) <= 0);
}

template<class T> int iSP(const Point<T> &a, const Point<T> &b, const Point<T> &c){
  T fl = Cross(b-a, c-a);
  if(geometry::lt(T(0), fl)) return 1; // CCW
  if(geometry::lt(fl, T(0))) return -1; // CW
  if(geometry::lt(T(0), Dot(b-a, c-b))) return 2; //abc
  if(geometry::lt(T(0), Dot(a-b, c-a))) return -2; //bac
  return 0; // acb
}

template<class T> int angletype(const Point<T> &a, const Point<T> &b, const Point<T> &c){
  T v = Dot(a-b, c-a);
  if(geometry::is_zero(v)) return 0; // right angle
  if(v > 0) return 1; // acute angle
  return -1; // obtuse angle
}

template<typename T>
bool cmp_y(const Point<T>& p1, const Point<T> &p2){
  if(p1.y == p2.y) return p1.x < p2.x;
  return p1.y < p2.y;
}

template<typename T>
bool cmp_arg(const Point<T>& p1, const Point<T> &p2){
  if(p1.ort() != p2.ort()) return p1.ort() < p2.ort();
  return iSP(Point<T>(0,0), p1, p2) > 0;
}

template<typename T>
void ArgSort(Polygon<T> &ps){
  sort(begin(ps), end(ps), cmp_arg<T>);
}

template<class T> struct Line{
  Point<T> a, b;
  Line() {}
  Line(const Point<T> &a, const Point<T> &b): a(a), b(b) {}
  Line(T A, T B, T C){ // Ax + By = C
    if(A == 0){ a = Point<T>(0, C/B); b = Point<T>(1, C/B); }
    else if(B == 0){ a = Point<T>(C/A, 0); b = Point<T>(C/A, 1); }
    else{ a = Point<T>(0, C/B); b = Point<T>(C/A, 0); }
  }
  //Line(const Segment<T> &s): a(s.a), b(s.b) {}
  Point<T> projection(const Point<T> &p) const {
    return a + (b - a)*Dot(p - a, b - a)/(b - a).Norm();
  }
  Point<T> reflection(const Point<T> &p) const {
    return p + (projection(p) - p)*2;
  }
  int angletype(const Line &l) const {
    if(geometry::is_zero(Cross(b-a, l.b-l.a))) return 1;
    if(geometry::is_zero(Dot(a-b, l.b-l.a))) return -1;
    return 0;
  }
  bool Intersect(const Line &l) const {
    return angletype(l) != 1;
  }
  Point<T> Cross_Point(const Line &l) const {
    if(geometry::is_zero(Cross(b-a,l.b-l.a))) return l.a;
    return (b - a)*Cross(l.a - a, l.b - l.a)/Cross(b - a,l.b - l.a) + a;
  }
  double dist(const Point<T> &p) const {
    return abs(Cross(p - a, b - a)/(b - a).Abs());
  }
  double dist(const Line &l) const {
    if(Intersect(l)) return 0;
    return dist(l.a);
  }
};

template<class T> struct Segment{
  Point<T> a, b;
  Segment() {}
  Segment(const Point<T> &a, const Point<T> &b): a(a), b(b) {}

  double length() const { return a.dist(b); }
  bool Intersect(const Segment &s) const {
    return iSP(a, b, s.a)*iSP(a, b, s.b) <= 0 && iSP(s.a, s.b, a)*iSP(s.a, s.b, b) <= 0;
  }
  Point<T> Center() const { return (a + b) / 2; }
  Line<T> PerpBisector() const {
    Point<T> center = this->Center();
    return Line<T>((a - center).rotate90() + center, center);
  }
  int angletype(const Segment &s) const {
    if(geometry::is_zero(Cross(b-a, s.b-s.a))) return 1;
    if(geometry::is_zero(Dot(a-b, s.b-s.a))) return -1;
    return 0;
  }
  Point<T> Cross_Point(const Segment &s) const {
    if(geometry::is_zero(Cross(b-a,s.b-s.a))) return s.a;
    return (b - a)*Cross(s.a - a, s.b - s.a)/Cross(b - a, s.b - s.a) + a;
  }
  double dist(const Point<T> &p) const {
    if(Dot(b-a, p-a) < 0) return p.dist(a);
    if(Dot(a-b, p-b) < 0) return p.dist(b);
    return abs(Cross(p-a, b-a)/ (b-a).Abs());
  }
  double dist(const Segment &s) const {
    if(Intersect(s)) return 0;
    return min({dist(s.a), dist(s.b), s.dist(a), s.dist(b)});
  }
};

template<typename T>
struct Triangle{
  Point<T> a, b, c;
  Triangle() {}
  Triangle(const Point<T> &a, const Point<T> &b, const Point<T> &c): a(a), b(b), c(c){}

  double angle_a() const {
    double ab = a.dist(b), bc = b.dist(c), ca = c.dist(a);
    return acos((ca*ca + ab*ab - bc*bc)/(2*ca*ab));
  }
  double angle_b() const {
    double ab = a.dist(b), bc = b.dist(c), ca = c.dist(a);
    return acos((ab*ab + bc*bc - ca*ca)/(2*ab*bc));
  }
  double angle_c() const {
    double ab = a.dist(b), bc = b.dist(c), ca = c.dist(a);
    return acos((bc*bc + ca*ca - ab*ab)/(2*bc*ca));
  }
  Point<T> divpoint(double p, double q, double r) const {
    return (a*p + b*q + c*r)/(p + q + r);
  }

  Point<T> centerofgravity() const {
    return (a + b + c)/3;
  }
  Point<T> circumcenter() const {
    Segment<T> s1(a, b), s2(a, c);
    return s1.PerpBisector().Cross_Point(s2.PerpBisector());
  }
  Point<T> orthocenter() const {
    return centerofgravity()*3 - circumcenter()*2;
  }
  Point<T> innercenter() const {
    return divpoint(b.dist(c), c.dist(a), a.dist(b));
  }
  Point<T> excenterA() const {
    return divpoint(-b.dist(c), c.dist(a), a.dist(b));
  }
  Point<T> excenterB() const {
    return divpoint(b.dist(c), -c.dist(a), a.dist(b));
  }
  Point<T> excenterC() const {
    return divpoint(b.dist(c), c.dist(a), -a.dist(b));
  }
};

template<class T> struct Circle{
  Point<T> o;
  T r;
  Circle() {}
  Circle(const Point<T> &o, const T &r): o(o), r(r) {}
  Circle(const Point<T> &a, const Point<T> &b): o((a + b)/2), r(a.dist(b)/2) {}
  Circle(const Point<T> &a, const Point<T> &b, const Point<T> &c)
    : o(Triangle(a,b,c).circumcenter()), r(o.dist(a)) {}

  int Intersect(const Circle&c) const {
    if(r < c.r) return c.Intersect(*this);
    auto od = (o - c.o).Norm();
    auto ra = (r + c.r)*(r + c.r), rs = (r - c.r)*(r - c.r);
    if(geometry::lt(ra, od)) return 4;
    if(geometry::eq(ra, od)) return 3;
    if(geometry::lt(rs, od)) return 2;
    if(geometry::eq(rs, od)) return 1;
    return 0;
  }

  int Intersect(const Line<T> &l) const {
    double d = l.dist(o);
    if(geometry::lt<double>(d, r)) return 2;
    return geometry::eq(d, r);
  }

  int Contains(const Point<T> &p) const {
    auto od = (o - p).Norm();
    if(geometry::eq(od, r*r)) return 1; // on
    return (od > r*r) ? 2 : 0; // out/in
  }

  Polygon<T> Cross_Points(const Line<T> &l) const {
    int k = this->Intersect(l);
    if(k == 0) return {};
    Point<T> p = l.projection(o);
    if(k == 1) return {p};
    Point<T> d = (l.b - l.a)/l.a.dist(l.b);
    double base = sqrt(r*r - (p - o).Norm());
    return {p + d*base, p - d*base};
  }

  Polygon<T> Cross_Points(const Circle &c) const {
    int k = this->Intersect(c);
    if(k == 0 || k == 4) return {};
    double t = (c.o - o).arg();
    if(k == 1 || k == 3) return {o + Polar<T>(r, t)};
    double d = o.dist(c.o), a = acos((r*r + d*d - c.r*c.r) / (r * d * 2));
    return {o + Polar<T>(r, t + a), o + Polar<T>(r, t - a)};
  }

  friend ostream &operator<<(ostream &os, const Circle &c) {
    return os << c.o << " " << c.r;
  }
  friend istream &operator>>(istream &is, Circle &c) {
    Point<T> p; T l; is >> p >> l;
    c = Circle<T>(p, l);
    return (is);
  }
};


template<typename T>
int Contains(const Polygon<T> &ps, const Point<T>& p){
  bool in = false;
  int n = ps.size();
  for(int i = 0; i < n; i++){
    Point<T> a = ps[i] - p, b = ps[(i+1)%n] - p;
    if(a.y > b.y) swap(a, b);
    if(a.y <= 0 && 0 < b.y && Cross(a,b) < 0) in = !in;
    if(geometry::is_zero(Cross(a, b)) && geometry::le(Dot(a, b), T(0))) return 1; // on
  }
  return in? 2 : 0; // out/in
}

template<typename T = int>
struct Edge{
  int src, to; T cost; int idx;
  Edge(){}
  Edge(int src, int to, T cost = -1, int idx = -1): src(src), to(to), cost(cost), idx(idx) {}
  operator int() const { return to; }
};
template<typename T>
using Edges = vector<Edge<T>>;

template<typename T = bool>
struct Graph {
  vector<vector<Edge<T>>> g;
  size_t es;
  Graph() {};
  Graph(size_t n): g(n), es(0) {}
  size_t size() const { return g.size(); }
  int deg(int v) const { return int(g[v].size()); }

  vector<Edge<T>> &operator[](int k) { return g.at(k); }
  const vector<Edge<T>> &operator[](int k) const { return g.at(k); }
  vector<Edge<T>> &at(int k) { return g.at(k); }
  const vector<Edge<T>> &at(int k) const { return g.at(k); }

  void add_edge(int src, int to, T cost = 1){
    g[src].emplace_back(src, to, cost, es);
    g[to].emplace_back(to, src, cost, es++);
  }
  void add_directed_edge(int src, int to, T cost = 1){
    g[src].emplace_back(src, to, cost, es++);
  }

  void read(int m, int base = 1, bool weighted = false, bool directed = false, const T &id = 1){
    for (int i = 0; i < m; i++){
      int u, v; cin >> u >> v; u -= base; v -= base;
      T c = id;
      if(weighted) cin >> c;
      if(directed) add_directed_edge(u, v, c);
      else add_edge(u, v, c);
    }
  }
};

template<typename T>
struct CycleDetection{
  Graph<T> g;
  bool directed;
  vector<int> used;
  vector<Edge<T>> pre, cycle;
  CycleDetection(const Graph<T> &g, bool directed = true)
    : g(g), directed(directed), used(g.size()), pre(g.size()) {}

  vector<Edge<T>> find(){
    for(int i = 0; i < (int)g.size(); i++){
      if(used[i]==0 && dfs(i)){
        reverse(cycle.begin(), cycle.end());
        return cycle;
      }
    }
    return {};
  }

private:
  bool dfs(int v, int q = -1){
    used[v] = 1;
    for(auto&&e : g[v]){
      if(e == q && !directed) continue;
      if(used[e] == 0){
        pre[e] = e;
        if(dfs(e, v)) return true;
      }else if(used[e] == 1){
        int p = v;
        while(p != e){
          cycle.emplace_back(pre[p]);
          p = pre[p].src;
        }
        cycle.emplace_back(e);
        return true;
      }
    }
    used[v] = 2;
    return false;
  }
};

template<typename T> T Area(const Polygon<T> &ps, bool harf = true){
  if((int)ps.size() < 3) return 0;
  T res = Cross(ps.back(), ps.front());
  for(int i = 0; i < (int)ps.size()-1; i++){
    res += Cross(ps[i], ps[i+1]);
  }
  if(harf) res /= 2;
  return res;
}


struct Solver{
  Solver(int n, int m): n(n), m(m) {}
  using ll = long long;

  int n, m;
  vector<Circle<ll>> cs;
  void solve(){
    cs.resize(n);
    for(auto&c : cs) cin >> c;

    Edges<int> es;
    int t = 0;
    for(int j = 0; j < n; ++j){
      for(int i = 0; i < j; ++i){
        if(cs[i].Intersect(cs[j]) < 4){
          es.emplace_back(i, j, 0, t++);
        }
      }
    }

    string ans;
    while(m--) ans += (check(es) ? "YES "s : "NO "s);
    ans.pop_back();
    cout << ans << "\n";
  }

  void ci(int i){ //cout << i << ":"; }
  }

  bool check(Edges<int> es){
    Point<ll> p, q; cin >> p >> q;
    bool fl = false;
    for(auto&c : cs){
      if((c.Contains(p) < 2) != (c.Contains(q) < 2)){
        ci(0);
        return false;
      }
      fl |= c.Contains(p) < 2;
    }
    if(fl){
      ci(1);
      return true;
    }

    while(true){
      Graph<int> g(n);
      for(auto&e : es) g.add_edge(e.src, e.to);

      CycleDetection cd(g, false);
      auto cy = cd.find();
      if(cy.empty()) break;

      int l = int(cy.size());
      Polygon<ll> ps(l);
      for(int i = 0; i < l; ++i) ps[i] = cs[cy[i].src].o;

      if(Area(ps, false) < 0) reverse(begin(ps), end(ps));

      if(Contains(ps, p) != Contains(ps, q)){
        ci(2);
        return false;
      }

      l = int(es.size());
      auto e = cy[0];
      fl = false;
      for(int i = 0; i < l; ++i){
        if((e.src == es[i].src && e.to == es[i].to) || (e.src == es[i].to && e.to == es[i].src)){
          es.erase(begin(es) + i);
          fl = true;
          break;
        }
      }
      assert(fl);
    }
    ci(3);
    return true;
  }
};

int main() {
  while(true){
    int n, m; cin >> n >> m;
    if(n == 0) return 0;
    Solver solver(n, m);
    solver.solve();
  }
}
accuracy: 1
time_ms: 30
memory_kb: 3400
final_rank: 2

code_snippet: #include <bits/stdc++.h>
#define rep(i,n) for(int i = 0; i < (n); i++)
using namespace std;
typedef long long ll;
using Real = double;
using Point = complex< Real >;
using Polygon = vector< Point >;
const Real EPS = 1e-6, PI = acos(-1);
constexpr int COUNTER_CLOCKWISE = +1;
constexpr int CLOCKWISE = -1;
constexpr int ONLINE_BACK = +2;  // c-a-b
constexpr int ONLINE_FRONT = -2; // a-b-c
constexpr int ON_SEGMENT = 0;    // a-c-b

Point operator*(const Point &p, const Real &d) {
    return Point(real(p) * d, imag(p) * d);
}

Point operator/(const Point &p, const Real &d) {
    return Point(real(p) / d, imag(p) / d);
}

istream &operator>>(istream &is, Point &p) {
    Real a, b; is >> a >> b;
    p = Point(a, b);
    return is;
}

ostream &operator<<(ostream &os, const Point &p) {
    return os << fixed << setprecision(10) << real(p) << " " << imag(p);
}

namespace std {
    bool operator<(const Point &a, const Point &b) {
        return a.real() != b.real() ? a.real() < b.real() : a.imag() < b.imag();
    }
}

// 符号
inline int sign(const Real &r) {
    return r <= -EPS ? -1 : r >= EPS ? 1 : 0;
}

// a = b ? 
inline bool equals(const Real &a, const Real &b) {
    return sign(a - b) == 0;
}


// 単位ベクトル
Point unitVec(const Point &a){ return a / abs(a); }

// 法線ベクトル 半時計周り
Point normVec(const Point &a){ return a * Point(0, 1); }
//Point normVec(const Point &a){ return a * Point(0,-1); }

// 内積
Real dot(const Point &a, const Point &b) {
    return (a.real() * b.real() + a.imag() * b.imag());
}

// 外積
Real cross(const Point &a, const Point &b) {
    return (a.real() * b.imag() - a.imag() * b.real());
}

Point rotate(const Point &p, const Real &theta) {
    return Point(cos(theta) * p.real() - sin(theta) * p.imag(),
                 sin(theta) * p.real() + cos(theta) * p.imag());
}

Real Radian_To_Degree(const Real &radian){ return radian * 180.0 / PI; }
Real Degree_To_Radian(const Real &degree){ return degree * PI / 180.0; }

struct Line {
    Point a, b;
    Line() = default;
    Line(Point a, Point b) : a(a), b(b) {}

    // Ax + By = C
    Line(Real A, Real B, Real C) {
        if(equals(A, 0)) {
            a = Point(0, C / B), b = Point(1, C / B);
        }else if(equals(B,0)) {
            b = Point(C / A, 0), b = Point(C / A, 1);
        }else{
            a = Point(0, C / B), b = Point(C / A, 0);
        }
    }

    friend ostream &operator<<(ostream &os, Line &p) {
        return os << p.a << " " << p.b;
    }

    friend istream &operator>>(istream &is, Line &a) {
        return is >> a.a >> a.b;
    }
};

struct Segment : Line {
    Segment() = default;
    Segment(Point a, Point b) : Line(a, b) {}
};

struct Circle {
    Point p;
    Real r;
    Circle() = default;
    Circle(Point p, Real r) : p(p), r(r) {}

    friend ostream &operator<<(ostream &os, Circle &c) {
        return os << c.p << " " << c.r;
    }

    friend istream &operator>>(istream &is, Circle &c) {
        return is >> c.p >> c.r;
    }
};

// 直線lに点pから引いた垂線の足を求める
Point projection(const Line &l, const Point &p) {
    Real t = dot(p - l.a, l.a - l.b) / norm(l.a - l.b);
    return l.a + (l.a - l.b) * t;
}

Point projection(const Segment &l, const Point &p) {
    Real t = dot(p - l.a, l.a - l.b) / norm(l.a - l.b);
    return l.a + (l.a - l.b) * t;
}

// 直線lに関して点pと対称な点を求める
Point reflection(const Line &l, const Point &p) {
    return p + (projection(l, p) - p) * 2.0;
}

// 点の回転方向
// 点aを基準に点b,cの位置関係
int ccw(const Point &a, Point b, Point c) {
    b = b - a, c = c - a;
    if(sign(cross(b, c)) == +1) return COUNTER_CLOCKWISE; // 反時計回り
    if(sign(cross(b, c)) == -1) return CLOCKWISE;         // 時計回り
    if(sign(dot(b, c)) == -1) return ONLINE_BACK;         // c - a - b
    if(norm(b) < norm(c)) return ONLINE_FRONT;            // a - b - c
    return ON_SEGMENT;                                    // a - c - b
}

// 2直線の直交判定
bool is_orthogonal(const Line &a, const Line &b) {
    return equals(dot(a.b - a.a, b.b - b.a), 0);
}

// 2直線の平行判定
bool is_parallel(const Line &a, const Line &b) {
    return equals(cross(a.b - a.a, b.b - b.a), 0);
}

// 線分の交差判定
bool is_intersect_ss(const Segment &s, const Segment &t) {
    return ccw(s.a, s.b, t.a) * ccw(s.a, s.b, t.b) <= 0 &&
           ccw(t.a, t.b, s.a) * ccw(t.a, t.b, s.b) <= 0;
}

// 直線の交差判定
bool is_intersect_ll(const Line &l, const Line &m) {
    Real A = cross(l.b - l.a, m.b - m.a);
    Real B = cross(l.b - l.a, l.b - m.a);
    if(equals(abs(A), 0) && equals(abs(B), 0)) return true;
    return !is_parallel(l, m);
}

// 線分に点が乗っているか
bool is_intersect_sp(const Segment &s, const Point &p) {
    return ccw(s.a, s.b, p) == ON_SEGMENT;
}

// 点と点の距離
Real distance_pp(const Point &p1, const Point &p2) {
    return sqrt(pow(real(p1)-real(p2), 2) + pow(imag(p1)-imag(p2), 2));
}

// 点と直線の距離
Real distance_lp(const Line &l, const Point &p) {
    return abs(p - projection(l, p));
}

// 直線と直線の距離
Real distance_ll(const Line &l, const Line &m) {
    return is_intersect_ll(l, m) ? 0 : distance_lp(l, m.a);
}

// 点と線分の距離
Real distance_sp(const Segment &s, const Point &p) {
    Point r = projection(s, p);
    if(is_intersect_sp(s, r)) return abs(r - p);
    return min(abs(s.a - p), abs(s.b - p));
}

// 線分と線分の距離
Real distance_ss(const Segment &a, const Segment &b) {
    if(is_intersect_ss(a, b)) return 0;
    return min({distance_sp(a, b.a), distance_sp(a, b.b), distance_sp(b, a.a), distance_sp(b, a.b)});
}

// 交点
Point cross_point_ll(const Line &l, const Line &m) {
    Real A = cross(l.b - l.a, m.b - m.a);
    Real B = cross(l.b - l.a, l.b - m.a);
    if(equals(abs(A), 0) && equals(abs(B), 0)) return m.a;
    return m.a + (m.b - m.a) * B / A;
}

// 円と円の位置関係
int intersect(Circle c1, Circle c2) {
    Real d = abs(c1.p - c2.p);
    return abs(c1.r - c2.r) - EPS < d && d < c1.r + c2.r + EPS;
}

Real area(const Polygon &p){
    Real A = 0;
    for(int i = 0; i < p.size(); i++){
        A += cross(p[i], p[(i + 1) % p.size()]);
    }
    return A * 0.5;
}

bool is_convex(const Polygon &p){
    for(int i = 0; i < p.size(); i++){
        if(ccw(p[(i + p.size() - 1) % p.size()], p[i], p[(i + 1) % p.size()]) == -1) return false;
    }
    return true;
}

enum { OUT, ON, IN };
int contains(const Polygon &Q, const Point &p){
    bool in = false;
    for(int i = 0; i < Q.size(); i++){
        Point a = Q[i] - p, b = Q[(i + 1) % Q.size()] - p;
        if(a.imag() > b.imag()) swap(a, b);
        if(a.imag() <= 0 && 0 < b.imag() && cross(a, b) < 0) in = !in;
        if(cross(a, b) == 0 && dot(a, b) <= 0) return ON;
    }
    return in ? IN : OUT;
}

Polygon convex_hull(Polygon &p, bool strict = true){
    int n = (int)p.size(), k = 0;
    if(n <= 2) return p;
    sort(p.begin(), p.end());
    Polygon ch(2 * n);
    for(int i = 0; i < n; ch[k++] = p[i++]){
        while(k >= 2 && cross(ch[k - 1] - ch[k - 2], p[i] - ch[k - 1]) < 0) --k;
    }
    for(int i = n - 2, t = k + 1; i >= 0; ch[k++] = p[i--]){
        while(k >= t && cross(ch[k - 1] - ch[k - 2], p[i] - ch[k - 1]) < 0) --k;
    }
    ch.resize(k - 1);
    return ch;
}

Real convex_diameter(const Polygon &p){
    int n = (int)p.size();
    int is = 0, js = 0;
    for(int i = 1; i < n; i++){
        if(p[i].imag() > p[is].imag()) is = i;
        if(p[i].imag() < p[js].imag()) js = i;
    }
    Real maxdis = norm(p[is] - p[js]);
    int maxi, maxj, i, j;
    i = maxi = is;
    j = maxj = js;
    do {
        if(cross(p[(i + 1) % n] - p[i], p[(j + 1) % n] - p[j]) >= 0){
            j = (j + 1) % n;
        }else{
            i = (i + 1) % n;
        }

        if(norm(p[i] - p[j]) > maxdis){
            maxdis = norm(p[i] - p[j]);
            maxi = i;
            maxj = j;
        }

    }while(i != is || j != js);
    return sqrt(maxdis);
}

Polygon convex_cut(const Polygon &U, Line l){
    Polygon ret;
    for(int i = 0; i < U.size(); i++){
        Point now = U[i], nxt = U[(i + 1) % U.size()];
        if(ccw(l.a, l.b, now) != -1) ret.push_back(now);
        if(ccw(l.a, l.b, now) * ccw(l.a, l.b, nxt) < 0){
            ret.push_back(cross_point_ll(Line(now, nxt), l));
        }
    }
    return ret;
}

Real ClosetPair(Polygon& ps){
    if(ps.size() <= 1) throw (0);
    sort(begin(ps), end(ps));

    auto compare_y = [&](const Point &a, const Point &b) {
        return a.imag() < b.imag();
    };
    Polygon beet(ps.size());
    const double INF = 1e18;

    function< double(int, int) > rec = [&](int left, int right) {
        if(right - left <= 1) return INF;
        int mid = (left + right) >> 1;
        auto x = ps[mid].real();
        auto ret = min(rec(left, mid), rec(mid, right));
        inplace_merge(begin(ps) + left, begin(ps) + mid, begin(ps) + right, compare_y);
        int ptr = 0;
        for(int i = left; i < right; i++) {
            if(fabs((ps[i].real()) - x) >= ret) continue;
            for(int j = 0; j < ptr; j++) {
                auto luz = ps[i] - beet[ptr - j - 1];
                if(luz.imag() >= ret) break;
                ret = min(ret, abs(luz));
            }
            beet[ptr++] = ps[i];
        }
        return ret;
    };
    return rec(0, (int) ps.size());
}

pair< Point, Point > cross_point_cc(const Circle &c1, const Circle &c2) {
    Real d = abs(c1.p - c2.p);
    Real a = acos((c1.r * c1.r + d * d - c2.r * c2.r) / (2 * c1.r * d));
    Real t = atan2(c2.p.imag() - c1.p.imag(), c2.p.real() - c1.p.real());
    Point p1 = c1.p + Point(cos(t + a) * c1.r, sin(t + a) * c1.r);
    Point p2 = c1.p + Point(cos(t - a) * c1.r, sin(t - a) * c1.r);
    return {p1, p2};
}

const int BOTTOM = 0;
const int LEFT   = 1;
const int RIGHT  = 2;
const int TOP    = 3;

class endPoint {
    public:
        Point p;
        int seg; // id of Point
        int st; // kind of Point
        endPoint(){}
        endPoint(Point inp, int inseg, int inst):p(inp),seg(inseg),st(inst){}
        bool operator<(const endPoint &ep)const {
            if(p.imag() == ep.p.imag()) return st < ep.st;
            else return p.imag() < ep.p.imag();
        }
};

endPoint EP[220000];
int Manhattan_Intersections(vector< Segment > s){
    int n = s.size();
    double hoge;

    for(int i = 0, k = 0; i < n; i++){
        if(s[i].a.imag() == s[i].b.imag()){
            if(s[i].a.real() > s[i].b.real()){
                hoge = s[i].a.real();
                s[i].a.real(s[i].b.real());
                s[i].b.real(hoge);
            }
        }else if(s[i].a.imag() > s[i].b.imag()){
            hoge = s[i].a.imag();
            s[i].a.imag(s[i].b.imag());
            s[i].b.imag(hoge);
        }

        if(s[i].a.imag() == s[i].b.imag()){
            EP[k++] = endPoint(s[i].a, i, LEFT);
            EP[k++] = endPoint(s[i].b, i, RIGHT);
        }else{
            EP[k++] = endPoint(s[i].a, i, BOTTOM);
            EP[k++] = endPoint(s[i].b, i, TOP);
        }
    }

    sort(EP, EP + 2 * n);
    set<int> BT;
    BT.insert(1000000001);
    int cnt = 0;
    for(int i = 0; i < 2 * n; i++){
        if(EP[i].st == TOP) BT.erase(EP[i].p.real());
        else if(EP[i].st == BOTTOM) BT.insert(EP[i].p.real());
        else if(EP[i].st == LEFT){
            set<int>::iterator b = lower_bound(BT.begin(), BT.end(), s[EP[i].seg].a.real());
            set<int>::iterator e = upper_bound(BT.begin(), BT.end(), s[EP[i].seg].b.real());
            cnt += distance(b, e);
        }
    }

    return cnt;
}

// 内接円
Circle Inscribed_Circle(const Point& a, const Point& b, const Point& c){
    double A = abs(b - c), B = abs(c - a), C = abs(a - b);
    Point x = Point((a * A + b * B + c * C) / (A + B + C));
    double r = distance_sp(Segment(a,b),x);
    return Circle(x, r);
}

// 外接円
Circle Circumscribed_Circle(const Point& a, const Point& b, const Point& c){
    Point m1((a+b)/2.0), m2((b+c)/2.0);
    Point v((b-a).imag(), (a-b).real()), w((b-c).imag(), (c-b).real());
    Line s(m1, Point(m1+v)), t(m2, Point(m2+w));
    const Point x = cross_point_ll(s, t);
    return Circle(x, abs(a-x));
}

pair< Point, Point > cross_point_cl(const Circle &c, const Line &l) {
    Point pr = projection(l, c.p);
    if(equals(abs(pr - c.p), c.r)) return {pr, pr};
    Point e = (l.b - l.a) / abs(l.b - l.a);
    auto k = sqrt(norm(c.r) - norm(pr - c.p));
    return {pr - e * k, pr + e * k};
}

// 点pを通る円の接線
pair<Point,Point> tangent_cp(const Circle &c, const Point &p) {
    return cross_point_cc(c, Circle(p, sqrt(norm(c.p - p) - c.r * c.r)));
}

// 二つの円の共通接線
vector< Line > tangent_cc(Circle c1, Circle c2){
    vector< Line > ret;
    if(c1.r < c2.r) swap(c1,c2);
    double g = norm(c1.p-c2.p);
    if(equals(g,0.0)) return ret;
    Point u = (c2.p-c1.p)/sqrt(g);
    Point v = rotate(u, PI * 0.5);
    for(int s:{-1,1}){
        double h = (c1.r+ s*c2.r)/sqrt(g);
        if(equals(1- h*h, 0)){
            ret.emplace_back(c1.p+u*c1.r, c1.p+(u+v)*c1.r);
        }else if(1-h*h > 0){
            Point uu = u*h, vv = v*sqrt(1-h*h);
            ret.emplace_back(c1.p+(uu+vv)*c1.r, c2.p-(uu+vv)*c2.r*s);
            ret.emplace_back(c1.p+(uu-vv)*c1.r, c2.p-(uu-vv)*c2.r*s);
        }
    }
    return ret;
}

Real area_poly_c(const Polygon &p, const Circle &c) {
    if(p.size() < 3) return 0.0;
    function< Real(Circle, Point, Point) > cross_area = [&](const Circle &c, const Point &a, const Point &b) {
        Point va = c.p - a, vb = c.p - b;
        Real f = cross(va, vb), ret = 0.0;
        if(equals(f, 0.0)) return ret;
        if(max(abs(va), abs(vb)) < c.r + EPS) return f;
        if(distance_sp(Segment(a, b), c.p) > c.r - EPS) return c.r * c.r * arg(vb * conj(va));
        auto u = cross_point_cl(c, Segment(a, b));
        vector< Point > tot{a, u.first, u.second, b};
        for(int i = 0; i + 1 < tot.size(); i++) {
            ret += cross_area(c, tot[i], tot[i + 1]);
        }
        return ret;
    };
    Real A = 0;
    for(int i = 0; i < p.size(); i++) {
        A += cross_area(c, p[i], p[(i + 1) % p.size()]);
    }
    return A;
}

Real area_cc(const Circle &c1, const Circle &c2) {
    Real d = abs(c1.p - c2.p);
    if(c1.r + c2.r <= d + EPS) return 0.0;
    if(d <= fabs(c1.r - c2.r) + EPS) {
        Real r = min(c1.r, c2.r);
        return r * r * PI;
    }
    Real rc = (d * d + c1.r * c1.r - c2.r * c2.r) / (2 * d);
    Real theta = acos(rc / c1.r);
    Real phi = acos((d - rc) / c2.r);
    return (c1.r * c1.r*theta + c2.r*c2.r*phi - d*c1.r*sin(theta));
}

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

    while(true) {
        int n,m; cin >> n >> m; if(n == 0 && m == 0) break;
        vector< Circle > Cs(n);
        rep(i,n) cin >> Cs[i];

        vector<string> ans;
        auto push = [&](string s){ ans.push_back(s); };

        rep(_,m) {
            Point p,q; cin >> p >> q;
            vector<int> id_p, id_q;
            rep(i,n) {
                if(abs(Cs[i].p - p) <= Cs[i].r) id_p.push_back(i);
                if(abs(Cs[i].p - q) <= Cs[i].r) id_q.push_back(i);
            }
            sort(id_p.begin(), id_p.end());
            sort(id_q.begin(), id_q.end());
            if(id_p.size() > 0 || id_q.size() > 0) {
                push(id_p == id_q ? "YES" : "NO"); continue;
            }
            vector<vector<int>> G(n);
            rep(i,n)rep(j,n)if(i < j) {
                if(intersect(Cs[i], Cs[j])) {
                    G[i].push_back(j);
                    G[j].push_back(i);
                }
            }

            bool ok = true;
            while(true) {
                vector<int> r(n, -1), f(n, -1), d(n, 0);
                auto search = [&](void) {
                    function<void(int,int)> dfs = [&](int x, int v) -> void {
                        r[v] = x;
                        for(int to : G[v]) if(r[to] == -1) {
                            d[to] = d[v] + 1;
                            f[to] = v;
                            dfs(x, to);
                        }  
                    };

                    rep(i,n) if(f[i] == -1) dfs(i, i);
                    rep(i,n) {
                        for(int to : G[i]) {
                            if(r[i] == r[to] && d[to] - d[i] > 1) {
                                vector<int> res;
                                for(int v = to; v != i; v = f[v]) res.push_back(v);
                                res.push_back(i);
                                return res;
                            }
                        }
                    }
                    return vector<int>();
                };

                auto c = search();
                if(c.size() == 0) break;
                vector< Point > poly(c.size());
                rep(i,c.size()) poly[i] = Cs[c[i]].p;
                if(contains(poly, p) != contains(poly, q)) ok = false;

                rep(i,G[c[0]].size()) {
                    if(G[c[0]][i] == c[1]) {
                        G[c[0]].erase(G[c[0]].begin() + i); break;
                    }
                }
                rep(i,G[c[1]].size()) {
                    if(G[c[1]][i] == c[0]) {
                        G[c[1]].erase(G[c[1]].begin() + i); break;
                    }
                }
            }
            push(ok ? "YES" : "NO");
        }
        rep(i,m) { if(i) cout << " "; cout << ans[i]; } cout << endl;
    }
}
accuracy: 1
time_ms: 30
memory_kb: 8652
final_rank: 18

code_snippet: #include <bits/stdc++.h>
using namespace std;

//geometry library by maine

//basic
using Real = double;
using Point = complex<Real>;
#define x real()
#define y imag()
const Real EPS = 1e-8, PI = acos(-1), INF = 1e10;

istream& operator>>(istream& is, Point& p) {
    Real a, b;
    is >> a >> b;
    p = Point(a, b);
    return is;
}
ostream& operator<<(ostream& os, Point p) {
    return os << fixed << setprecision(10) << p.x << " " << p.y;
}

Point operator*(const Point& p, const Real& d) { return Point(p.x * d, p.y * d); }

bool eq(const Real& r, const Real& s) { return abs(r - s) < EPS; }
bool eq(const Point& p, const Point& q) { return abs(p - q) < EPS; }

namespace std {
bool operator<(const Point& a, const Point& b) {
    return !eq(a.x, b.x) ? a.x < b.x : a.y < b.y;
}
}  // namespace std

Real get_angle(const Point& a, const Point& b, const Point& c) {
    return arg((c - a) / (b - a));
}

inline int inc(int i, int N) {
    return (i == N - 1 ? 0 : i + 1);
}
inline int dec(int i, int N) {
    return (i == 0 ? N - 1 : i - 1);
}

//unit vector
Point e(const Point& p) { return p / abs(p); }

//angle transformation
Real radian_to_degree(Real r) { return r * 180.0 / PI; }
Real degree_to_radian(Real d) { return d * PI / 180.0; }

//basic functions
Real dot(const Point& p, const Point& q) { return p.x * q.x + p.y * q.y; }
Real cross(const Point& p, const Point& q) { return p.x * q.y - p.y * q.x; }
Point rotate(const Real& theta, const Point& p) {
    return {cos(theta) * p.x - sin(theta) * p.y, sin(theta) * p.x + cos(theta) * p.y};
}
Point rotate90(const Point& p) {
    return {-p.y, p.x};
}

//structs : Line, Segment, Circle
struct Line {
    Point a, b;
    Line() = default;
    Line(Point a, Point b) : a(a), b(b) {}
    Point d() const { return e(b - a); }  //direction vector
    Point n() const {
        Point dd = d();
        return {dd.y, -dd.x};
    }  //normal vector
};
struct Segment : Line {
    Segment() = default;
    Segment(Point a, Point b) : Line(a, b) {}
};
struct Circle {
    Point p;
    Real r;
    Circle() = default;
    Circle(Point p, Real r) : p(p), r(r){};
};
using Points = vector<Point>;
using Polygon = vector<Point>;
using Polygons = vector<Polygon>;
using Lines = vector<Line>;
using Segments = vector<Segment>;
using Circles = vector<Circle>;

//happy functions
//https://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_1_C
int ccw(const Point& a, const Point& bb, const Point& cc) {
    Point b = bb - a, c = cc - a;
    if (cross(b, c) > EPS)
        return 1;  //COUNTER_CLOCKWISE
    if (cross(b, c) < -EPS)
        return -1;  //CLOCKWISE
    if (dot(b, c) < 0)
        return 2;  //ONLINE_BACK  c-a-b
    if (norm(b) < norm(c))
        return -2;  //ONLINE_FRONT a-b-c
    return 0;       //ON_SEGMENT   a-c-b
}

bool parallel(const Line& l, const Line& m) { return eq(cross(l.d(), m.d()), 0.0); }
bool orthogonal(const Line& l, const Line& m) { return eq(dot(l.d(), m.d()), 0.0); }
Point projection(const Line& l, const Point& p) { return l.a + (l.a - l.b) * (dot(p - l.a, l.a - l.b) / norm(l.a - l.b)); }
Point reflection(const Line& l, const Point& p) { return p + (projection(l, p) - p) * 2.0; }

//intersect
bool intersect(const Line& l, const Point& p) { return abs(ccw(l.a, l.b, p)) != 1; }
bool intersect(const Line& l, const Line& m) { return !parallel(l, m) || (parallel(l, m) && intersect(l, m.a)); }
bool intersect(const Segment& s, const Point& p) { return ccw(s.a, s.b, p) == 0; }
bool intersect(const Line& l, const Segment& s) { return cross(l.d(), s.a - l.a) * cross(l.d(), s.b - l.a) < EPS; }
bool intersect(const Segment& s, const Segment& t) {
    return ccw(s.a, s.b, t.a) * ccw(s.a, s.b, t.b) <= 0 && ccw(t.a, t.b, s.a) * ccw(t.a, t.b, s.b) <= 0;
}
bool intersect_onsegment(const Segment& s, const Point& p) { return intersect(s, p) && !eq(s.a, p) && !eq(s.b, p); }

//distance
Real distance(const Point& p, const Point& q) { return abs(p - q); }
Real distance(const Line& l, const Point& p) { return distance(p, projection(l, p)); }
Real distance(const Line& l, const Line& m) { return intersect(l, m) ? 0 : distance(l, m.a); }
Real distance(const Segment& s, const Point& p) {
    Point q = projection(s, p);
    return intersect(s, q) ? distance(p, q) : min(distance(s.a, p), distance(s.b, p));
}
Real distance(const Segment& s, const Segment& t) { return intersect(s, t) ? 0 : min({distance(s, t.a), distance(s, t.b), distance(t, s.a), distance(t, s.b)}); }
Real distance(const Line& l, const Segment& s) { return intersect(l, s) ? 0 : min(distance(l, s.a), distance(l, s.b)); }

//intersect circle
bool intersect(const Circle& c, const Point& p) { return eq(distance(c.p, p), c.r); }
bool intersect(const Circle& c, const Line& l) { return distance(l, c.p) <= c.r + EPS; }
int intersect(const Circle& c, const Segment& s) {
    if (!intersect(c, Line(s)))
        return 0;
    Real da = distance(c.p, s.a), db = distance(c.p, s.b);
    if (da < c.r + EPS && db < c.r + EPS)
        return 0;
    if (da > db)
        swap(da, db);
    if (da < c.r - EPS && db > c.r + EPS)
        return 1;
    if (intersect(s, projection(s, c.p)))
        return 2;
    return 0;
}
int intersect(Circle c1, Circle c2) {
    if (c1.r < c2.r)
        swap(c1, c2);
    Real d = distance(c1.p, c2.p);
    if (c1.r + c2.r < d)
        return 4;
    if (eq(c1.r + c2.r, d))
        return 3;
    if (c1.r - c2.r < d)
        return 2;
    if (eq(c1.r - c2.r, d))
        return 1;
    return 0;
}

//crosspoint
Point crosspoint(const Line& l, const Line& m) {
    Real A = cross(l.b - l.a, m.b - m.a);
    Real B = cross(l.b - l.a, l.b - m.a);
    if (eq(abs(A), 0.0) && eq(abs(B), 0.0))
        return m.a;
    return m.a + (m.b - m.a) * B / A;
}
Point crosspoint(const Segment& s, const Segment& t) { return crosspoint(Line(s), Line(t)); }
pair<Point, Point> crosspoint(const Circle& c, const Line& l) {
    Point p = projection(l, c.p);
    if (eq(distance(l, c.p), c.r))
        return {p, p};
    Real len = sqrt(c.r * c.r - norm(p - c.p));
    return {p - len * l.d(), p + len * l.d()};
}
pair<Point, Point> crosspoint(const Circle& c, const Segment& s) {
    Line l(s);
    if (intersect(c, s) == 2)
        return crosspoint(c, l);
    auto ret = crosspoint(c, l);
    if (dot(s.a - ret.first, s.b - ret.first) < 0)
        ret.second = ret.first;
    else
        ret.first = ret.second;
    return ret;
}
pair<Point, Point> crosspoint(const Circle& c1, const Circle& c2) {
    Real d = distance(c1.p, c2.p);
    Real a = acos((c1.r * c1.r + d * d - c2.r * c2.r) / (2 * c1.r * d));
    Real t = atan2(c2.p.y - c1.p.y, c2.p.x - c1.p.x);
    Point p1 = c1.p + Point(cos(t + a) * c1.r, sin(t + a) * c1.r);
    Point p2 = c1.p + Point(cos(t - a) * c1.r, sin(t - a) * c1.r);
    return {p1, p2};
}

//tangent
pair<Point, Point> tangent(const Circle& c, const Point& p) {
    return crosspoint(c, Circle(p, sqrt(norm(c.p - p) - c.r * c.r)));
}
Lines tangent(Circle c1, Circle c2) {
    Lines ret;
    if (c1.r < c2.r)
        swap(c1, c2);
    if (eq(c1.p, c2.p))
        return ret;
    Real g = norm(c1.p - c2.p);
    Point u = (c2.p - c1.p) / sqrt(g);
    Point v = rotate(PI / 2, u);
    for (int s : {-1, 1}) {
        Real h = (c1.r + s * c2.r) / sqrt(g);
        if (eq(1 - h * h, 0)) {
            ret.emplace_back(c1.p + u * c1.r, c1.p + (u + v) * c1.r);
        } else if (1 - h * h > 0) {
            Point uu = u * h, vv = v * sqrt(1 - h * h);
            ret.emplace_back(c1.p + (uu + vv) * c1.r, c2.p - (uu + vv) * c2.r * s);
            ret.emplace_back(c1.p + (uu - vv) * c1.r, c2.p - (uu - vv) * c2.r * s);
        }
    }
    return ret;
}

//convex
//https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/3/CGL_3_B
bool is_convex(const Polygon& P) {
    int N = (int)P.size();
    for (int i = 0; i < N; i++) {
        if (ccw(P[i], P[(i + 1) % N], P[(i + 2) % N]) == -1)
            return false;
    }
    return true;
}
//https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/4/CGL_4_A
Polygon convex_hull(Points P) {
    int N = (int)P.size(), k = 0;
    if (N <= 2)
        return P;
    sort(begin(P), end(P));
    Points ret(2 * N);
    for (int i = 0; i < N; ret[k++] = P[i++]) {
        while (k >= 2 && cross(ret[k - 1] - ret[k - 2], P[i] - ret[k - 1]) < EPS)
            --k;
    }
    for (int i = N - 2, t = k + 1; i >= 0; ret[k++] = P[i--]) {
        while (k >= t && cross(ret[k - 1] - ret[k - 2], P[i] - ret[k - 1]) < EPS)
            --k;
    }
    ret.resize(k - 1);
    return ret;
}
//https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/4/CGL_4_B
Real convex_diameter(const Polygon& P) {
    int N = (int)P.size();
    int is = 0, js = 0;
    for (int i = 1; i < N; i++) {
        if (P[i].y < P[is].y)
            is = i;
        if (P[i].y > P[js].y)
            js = i;
    }
    Real maxdist = norm(P[is] - P[js]);
    int maxi, maxj, i, j;
    maxi = i = is;
    maxj = j = js;
    do {
        if (cross(P[inc(i, N)] - P[i], P[inc(j, N)] - P[j]) >= 0)
            j = inc(j, N);
        else
            i = inc(i, N);
        if (norm(P[i] - P[j]) > maxdist) {
            maxdist = norm(P[i] - P[j]);
            maxi = i;
            maxj = j;
        }
    } while (i != is || j != js);
    maxi = maxi;
    maxj = maxj;
    return sqrt(maxdist);
}
//https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/4/CGL_4_C
Polygon convex_cut(const Polygon& P, const Line& l) {
    Polygon ret;
    int N = (int)P.size();
    for (int i = 0; i < N; i++) {
        Point now = P[i], nxt = P[(inc(i, N))];
        if (ccw(l.a, l.b, now) != -1)
            ret.emplace_back(now);
        if (ccw(l.a, l.b, now) * ccw(l.a, l.b, nxt) == -1)
            ret.emplace_back(crosspoint(Line(now, nxt), l));
    }
    return ret;
}

//other
enum
{
    OUT,
    ON,
    IN
};

//https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/3/CGL_3_C
int contains(const Polygon& Q, const Point& p) {
    bool in = false;
    int N = (int)Q.size();
    for (int i = 0; i < N; i++) {
        Point a = Q[i] - p, b = Q[(i + 1) % N] - p;
        if (a.y > b.y)
            swap(a, b);
        if (a.y <= 0 && 0 < b.y && cross(a, b) < 0)
            in = !in;
        if (cross(a, b) == 0 && dot(a, b) <= 0)
            return ON;
    }
    return in ? IN : OUT;
}

//https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/3/CGL_3_A
Real area(const Polygon& P) {
    Real A = 0;
    for (int i = 0; i < (int)P.size(); i++) {
        A += cross(P[i], P[(i + 1) % P.size()]);
    }
    return A * 0.5;
}

//https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/7/CGL_7_H
Real cross_area(const Circle& c, const Point& a, const Point& b) {
    Point va = c.p - a, vb = c.p - b;
    Real f = cross(va, vb), ret = 0.0;
    if (eq(f, 0.0))
        return ret;
    if (max(abs(va), abs(vb)) < c.r + EPS)
        return f;
    if (distance(Segment(a, b), c.p) > c.r - EPS)
        return c.r * c.r * arg(vb * conj(va));
    auto u = crosspoint(c, Segment(a, b));
    vector<Point> V{a, u.first, u.second, b};
    for (int i = 0; i < (int)V.size() - 1; i++) {
        ret += cross_area(c, V[i], V[i + 1]);
    }
    return ret;
}
Real area(const Polygon& p, const Circle& c) {
    int N = (int)p.size();
    if (N < 3)
        return 0.0;
    Real ret = 0;
    for (int i = 0; i < N; i++) {
        ret += cross_area(c, p[i], p[inc(i, N)]);
    }
    return ret / 2.0;
}

//https://onlinejudge.u-aizu.ac.jp/problems/CGL_5_A
Real closest_pair(Points& P, int left, int right) {
    if (right - left <= 1)
        return 1e18;
    int mid = (left + right) / 2;
    Real xx = P[mid].x;
    Real dist = min(closest_pair(P, left, mid), closest_pair(P, mid, right));
    inplace_merge(P.begin() + left, P.begin() + mid, P.begin() + right, [&](const Point& a, const Point& b) { return a.y < b.y; });
    Points memo;
    memo.reserve(right - left);
    for (int i = left; i < right; i++) {
        if (abs(P[i].x - xx) >= dist)
            continue;
        for (int j = (int)memo.size() - 1; j >= 0; j--) {
            if (P[i].y - memo[j].y >= dist)
                break;
            dist = min(dist, distance(P[i], memo[j]));
        }
        memo.emplace_back(P[i]);
    }
    return dist;
}
Real closest_pair(Points P) {
    sort(begin(P), end(P));
    return closest_pair(P, 0, (int)P.size());
}

//https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/7/CGL_7_B
Circle incircle(Polygon P) {
    assert((int)P.size() == 3);
    Real l0 = distance(P[1], P[2]), l1 = distance(P[0], P[2]), l2 = distance(P[0], P[1]);
    Circle c;
    c.p = crosspoint(Line(P[0], (P[1] * l1 + P[2] * l2) / (l1 + l2)), Line(P[1], (P[0] * l0 + P[2] * l2) / (l0 + l2)));
    c.r = distance(Line(P[0], P[1]), c.p);
    return c;
}

//https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/7/CGL_7_C
Circle circumcircle(Polygon P) {
    assert((int)P.size() == 3);
    Circle c;
    c.p = crosspoint(Line((P[0] + P[1]) / 2.0, (P[0] + P[1]) / 2.0 + rotate90(P[0] - P[1])),
        Line((P[0] + P[2]) / 2.0, (P[0] + P[2]) / 2.0 + rotate90(P[0] - P[2])));
    c.r = distance(c.p, P[0]);
    return c;
}

#undef x
#undef y

struct UF {
    vector<int> data;
    UF(int n) : data(n, -1) {}
    int root(int k) {
        if (data[k] < 0)
            return k;
        return data[k] = root(data[k]);
    }
    bool unite(int x, int y) {
        x = root(x), y = root(y);
        if (x == y)
            return false;
        if (data[x] > data[y])
            swap(x, y);
        data[x] += data[y];
        data[y] = x;
        return true;
    }
    bool same(int x, int y) {
        return root(x) == root(y);
    }
    int size(int k) { return -data[root(k)]; }
};


int main() {
    int N, M;
    while (cin >> N >> M, N) {
        Circles C(N);
        for (auto&& c : C) {
            cin >> c.p >> c.r;
        }
        vector<vector<double>> ang(N);
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < i; j++) {
                auto &c1 = C[i], &c2 = C[j];
                if (intersect(c1, c2) == 2) {
                    auto [p1, p2] = crosspoint(c1, c2);
                    ang[i].emplace_back(arg(p1 - c1.p));
                    ang[i].emplace_back(arg(p2 - c1.p));
                    ang[j].emplace_back(arg(p1 - c2.p));
                    ang[j].emplace_back(arg(p2 - c2.p));
                }
            }
        }
        int n = 0;
        vector<int> Vidx;
        for (auto&& V : ang) {
            for (auto&& x : V) {
                if (eq(-PI, x))
                    x = PI;
            }
            if (V.size() == 0)
                V.emplace_back(0);
            sort(begin(V), end(V));
            Vidx.emplace_back(n);
            n += (int)V.size();
        }
        UF uf(n * 2);

        auto f = [&](int i, int j, bool outer) {
            j += (int)ang[i].size();
            if (j >= (int)ang[i].size())
                j %= (int)ang[i].size();
            return (Vidx[i] + j) * 2 + (int)outer;
        };

        vector<bool> canconnectin(n * 2, true);
        vector<bool> canconnectout(n * 2, true);

        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                if (i == j)
                    continue;
                auto c1 = C[i], c2 = C[j];
                if (intersect(c1, c2) == 2) {
                    auto p = crosspoint(c1, c2).first;
                    double a1 = arg(p - c1.p), a2 = arg(p - c2.p);
                    if (eq(a1, -PI))
                        a1 = PI;
                    if (eq(a2, -PI))
                        a2 = PI;
                    int idx1 = lower_bound(cbegin(ang[i]), cend(ang[i]), a1 - EPS) - cbegin(ang[i]);
                    int idx2 = lower_bound(cbegin(ang[j]), cend(ang[j]), a2 - EPS) - cbegin(ang[j]);
                    uf.unite(f(i, idx1, true), f(j, idx2, false));
                    uf.unite(f(i, idx1 + 1, true), f(j, idx2, true));
                    uf.unite(f(i, idx1 + 1, false), f(j, idx2 + 1, true));
                    uf.unite(f(i, idx1, false), f(j, idx2 + 1, false));
                    canconnectin[f(i, idx1, false)] = false;
                    canconnectout[f(i, idx1, true)] = false;
                    canconnectin[f(j, idx2 + 1, false)] = false;
                    canconnectin[f(j, idx2 + 1, true)] = false;
                }
            }
        }

        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                if (i == j)
                    continue;
                auto c1 = C[i], c2 = C[j];
                if (intersect(c1, c2) == 0 && c1.r > c2.r) {
                    for (int idx1 = 0; idx1 < (int)ang[i].size(); idx1++) {
                        for (int idx2 = 0; idx2 < (int)ang[j].size(); idx2++) {
                            int f1 = f(i, idx1, false);
                            int f2 = f(j, idx2, true);
                            if (canconnectin[f1] && canconnectin[f2])
                                uf.unite(f1, f2);
                        }
                    }
                }

                if (intersect(c1, c2) == 4) {
                    Line L{c1.p, c2.p};
                    Point p1 = crosspoint(c1, L).second;
                    Point p2 = crosspoint(c2, L).first;
                    Segment S{p1, p2};
                    int cnt = 0;
                    for (auto&& c : C) {
                        cnt += intersect(c, S);
                    }
                    if (cnt == 0) {
                        double a1 = arg(p1 - c1.p), a2 = arg(p2 - c2.p);
                        if (eq(a1, -PI))
                            a1 = PI;
                        if (eq(a2, -PI))
                            a2 = PI;
                        int idx1 = lower_bound(cbegin(ang[i]), cend(ang[i]), a1 - EPS) - cbegin(ang[i]);
                        int idx2 = lower_bound(cbegin(ang[j]), cend(ang[j]), a2 - EPS) - cbegin(ang[j]);
                        uf.unite(f(i, idx1, true), f(j, idx2, true));
                    }
                }
            }
        }

        vector<bool> ans;

        while (M--) {
            Point p, q;
            cin >> p >> q;

            auto find = [&](Point p) {
                vector<pair<double, int>> V;
                for (int i = 0; i < N; i++) {
                    auto c = C[i];
                    V.emplace_back(abs(distance(p, c.p) - c.r), i);
                }
                auto [d, i] = *min_element(cbegin(V), cend(V));
                auto c = C[i];
                bool outer = distance(p, c.p) >= c.r;
                double a = arg(p - c.p);
                if (eq(a, -PI))
                    a = PI;
                int idx = lower_bound(cbegin(ang[i]), cend(ang[i]), a - EPS) - cbegin(ang[i]);
                return f(i, idx, outer);
            };

            ans.emplace_back(uf.same(find(p), find(q)));
        }

        for (int i = 0; i < ans.size(); i++) {
            cout << (ans[i] ? "YES" : "NO") << " \n"[i == ans.size() - 1];
        }
    }
}
accuracy: 1
time_ms: 1460
memory_kb: 3900
final_rank: 19

code_snippet: #include <bits/stdc++.h>
using namespace std;
const double EPS = 0.0001;
struct point{
  double x, y;
  point(){
  }
  point(double x, double y): x(x), y(y){
  }
  point operator -(point P){
    return point(x - P.x, y - P.y);
  }
};
double abs(point P){
  return sqrt(P.x * P.x + P.y * P.y);
}
double dist(point P, point Q){
  return abs(Q - P);
}
double cross(point P, point Q){
  return P.x * Q.y - P.y * Q.x;
}
struct line{
  point A, B;
  line(point A, point B): A(A), B(B){
  }
};
point vec(line L){
  return L.B - L.A;
}
bool segment_intersect(line L1, line L2){
  if (cross(L1.A - L2.A, vec(L2)) * cross(L1.B - L2.A, vec(L2)) > 0){
    return false;
  }
  if (cross(L2.A - L1.A, vec(L1)) * cross(L2.B - L1.A, vec(L1)) > 0){
    return false;
  }
  return true;
}
bool point_in_polygon(point P, vector<point> Q){
  int N = Q.size();
  point P2 = point(P.x + 100000, P.y + 123456);
  int cnt = 0;
  for (int i = 0; i < N; i++){
    line L1(Q[i], Q[(i + 1) % N]);
    line L2(P, P2);
    if (segment_intersect(L1, L2)){
      cnt++;
    }
  }
  if (cnt % 2 == 1){
    return true;
  } else {
    return false;
  }
}
struct circle{
  point C;
  double r;
  circle(){
  }
};
bool point_in_circle(point P, circle C){
  return dist(P, C.C) < C.r;
}
bool circle_intersect(circle C1, circle C2){
  double d = dist(C1.C, C2.C);
  return abs(C1.r - C2.r) - EPS < d && d < C1.r + C2.r + EPS;
}
void dfs(vector<vector<int>> &E, vector<int> &r, vector<int> &p, vector<int> &d, int r2, int v){
  r[v] = r2;
  for (int w : E[v]){
    if (r[w] == -1){
      d[w] = d[v] + 1;
      p[w] = v;
      dfs(E, r, p, d, r2, w);
    }
  }
}
vector<int> detect_cycle(vector<vector<int>> E){
  int N = E.size();
  vector<int> r(N, -1);
  vector<int> p(N, -1);
  vector<int> d(N, 0);
  for (int i = 0; i < N; i++){
    if (p[i] == -1){
      dfs(E, r, p, d, i, i);
    }
  }
  for (int i = 0; i < N; i++){
    for (int j : E[i]){
      if (r[i] == r[j] && d[j] - d[i] > 1){
        vector<int> c;
        for (int v = j; v != i; v = p[v]){
          c.push_back(v);
        }
        c.push_back(i);
        return c;
      }
    }
  }
  return vector<int>();
}
int main(){
  while (true){
    int n, m;
    cin >> n >> m;
    if (n == 0 && m == 0){
      break;
    }
    vector<circle> C(n);
    for (int i = 0; i < n; i++){
      cin >> C[i].C.x >> C[i].C.y >> C[i].r;
    }
    for (int i = 0; i < m; i++){
      point P, Q;
      cin >> P.x >> P.y >> Q.x >> Q.y;
      set<int> st1, st2;
      for (int j = 0; j < n; j++){
        if (point_in_circle(P, C[j])){
          st1.insert(j);
        }
        if (point_in_circle(Q, C[j])){
          st2.insert(j);
        }
      }
      if (!st1.empty() || !st2.empty()){
        if (st1 == st2){
          cout << "YES";
        } else {
          cout << "NO";
        }
      } else {
        vector<vector<int>> E(n);
        for (int j = 0; j < n; j++){
          for (int k = j + 1; k < n; k++){
            if (circle_intersect(C[j], C[k])){
              E[j].push_back(k);
              E[k].push_back(j);
            }
          }
        }
        bool ok = true;
        while (true){
          vector<int> c = detect_cycle(E);
          if (c.empty()){
            break;
          }
          int cnt = c.size();
          vector<point> p(cnt);
          for (int j = 0; j < cnt; j++){
            p[j] = C[c[j]].C;
          }
          if (point_in_polygon(P, p) != point_in_polygon(Q, p)){
            ok = false;
          }
          for (int j = 0; j < E[c[0]].size(); j++){
            if (E[c[0]][j] == c[1]){
              E[c[0]].erase(E[c[0]].begin() + j);
              break;
            }
          }
          for (int j = 0; j < E[c[1]].size(); j++){
            if (E[c[1]][j] == c[0]){
              E[c[1]].erase(E[c[1]].begin() + j);
              break;
            }
          }
        }
        if (ok){
          cout << "YES";
        } else {
          cout << "NO";
        }
      }
      if (i < m - 1){
        cout << ' ';
      }
    }
    cout << endl;
  }
}
accuracy: 1
time_ms: 20
memory_kb: 3508
final_rank: 6

code_snippet: #include <bits/stdc++.h>
using namespace std;
const double EPS = 0.0001;
struct point{
  double x, y;
  point(){
  }
  point(double x, double y): 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 k){
    return point(x * k, y * k);
  }
  point operator /(double k){
    return point(x / k, y / k);
  }
};
double abs(point P){
  return sqrt(P.x * P.x + P.y * P.y);
}
double dist(point P, point Q){
  return abs(Q - P);
}
double cross(point P, point Q){
  return P.x * Q.y - P.y * Q.x;
}
struct line{
  point A, B;
  line(point A, point B): A(A), B(B){
  }
};
point vec(line L){
  return L.B - L.A;
}
bool segment_intersect(line L1, line L2){
  if (cross(L1.A - L2.A, vec(L2)) * cross(L1.B - L2.A, vec(L2)) > 0){
    return false;
  }
  if (cross(L2.A - L1.A, vec(L1)) * cross(L2.B - L1.A, vec(L1)) > 0){
    return false;
  }
  return true;
}
struct circle{
  point C;
  double r;
  circle(){
  }
};
bool point_in_circle(point P, circle C){
  return dist(P, C.C) < C.r;
}
bool circle_intersects(circle C1, circle C2){
  double d = dist(C1.C, C2.C);
  return abs(C1.r - C2.r) - EPS < d && d < C1.r + C2.r + EPS;
}
bool point_in_polygon(point P, vector<point> Q){
  int N = Q.size();
  point P2 = point(P.x + 100000, P.y + 123456);
  int cnt = 0;
  for (int i = 0; i < N; i++){
    line L1(Q[i], Q[(i + 1) % N]);
    line L2(P, P2);
    if (segment_intersect(L1, L2)){
      cnt++;
    }
  }
  if (cnt % 2 == 1){
    return true;
  } else {
    return false;
  }
}
void dfs(vector<vector<int>> &E, vector<int> &r, vector<int> &p, vector<int> &d, int r2, int v){
  r[v] = r2;
  for (int w : E[v]){
    if (r[w] == -1){
      d[w] = d[v] + 1;
      p[w] = v;
      dfs(E, r, p, d, r2, w);
    }
  }
}
vector<int> detect_cycle(vector<vector<int>> E){
  int N = E.size();
  vector<int> r(N, -1);
  vector<int> p(N, -1);
  vector<int> d(N, 0);
  for (int i = 0; i < N; i++){
    if (p[i] == -1){
      dfs(E, r, p, d, i, i);
    }
  }
  for (int i = 0; i < N; i++){
    for (int j : E[i]){
      if (r[i] == r[j] && d[j] - d[i] > 1){
        vector<int> c;
        for (int v = j; v != i; v = p[v]){
          c.push_back(v);
        }
        c.push_back(i);
        return c;
      }
    }
  }
  return vector<int>();
}
int main(){
  while (true){
    int n, m;
    cin >> n >> m;
    if (n == 0 && m == 0){
      break;
    }
    vector<circle> C(n);
    for (int i = 0; i < n; i++){
      cin >> C[i].C.x >> C[i].C.y >> C[i].r;
    }
    for (int i = 0; i < m; i++){
      point P, Q;
      cin >> P.x >> P.y >> Q.x >> Q.y;
      set<int> st1, st2;
      for (int j = 0; j < n; j++){
        if (point_in_circle(P, C[j])){
          st1.insert(j);
        }
        if (point_in_circle(Q, C[j])){
          st2.insert(j);
        }
      }
      if (!st1.empty() || !st2.empty()){
        if (st1 == st2){
          cout << "YES";
        } else {
          cout << "NO";
        }
      } else {
        vector<vector<int>> E(n);
        for (int j = 0; j < n; j++){
          for (int k = j + 1; k < n; k++){
            if (circle_intersects(C[j], C[k])){
              E[j].push_back(k);
              E[k].push_back(j);
            }
          }
        }
        bool ok = true;
        while (true){
          vector<int> c = detect_cycle(E);
          if (c.empty()){
            break;
          }
          int cnt = c.size();
          vector<point> p(cnt);
          for (int j = 0; j < cnt; j++){
            p[j] = C[c[j]].C;
          }
          if (point_in_polygon(P, p) != point_in_polygon(Q, p)){
            ok = false;
          }
          for (int j = 0; j < E[c[0]].size(); j++){
            if (E[c[0]][j] == c[1]){
              E[c[0]].erase(E[c[0]].begin() + j);
              break;
            }
          }
          for (int j = 0; j < E[c[1]].size(); j++){
            if (E[c[1]][j] == c[0]){
              E[c[1]].erase(E[c[1]].begin() + j);
              break;
            }
          }
        }
        if (ok){
          cout << "YES";
        } else {
          cout << "NO";
        }
      }
      if (i < m - 1){
        cout << ' ';
      }
    }
    cout << endl;
  }
}
accuracy: 1
time_ms: 20
memory_kb: 3500
final_rank: 5

code_snippet: /*	author:  Kite_kuma
	created: 2021.06.29 20:45:02 */

// #ifdef LOCAL
// #define _GLIBCXX_DEBUG
// #endif
#include <bits/stdc++.h>
using namespace std;

#pragma region macros

#define foa(s, v) for(auto &s : v)
#define all(v) (v).begin(), (v).end()
#define rall(v) (v).rbegin(), (v).rend()
#define popcnt(n) __builtin_popcountll((long long)n)

#define REPname(a, b, c, d, e, ...) e
#define rep(...) REPname(__VA_ARGS__, REP3, REP2, REP1, REP0)(__VA_ARGS__)
#define REP0(x) for(int Counter_in_rep_macro = 0; Counter_in_rep_macro < (x); ++Counter_in_rep_macro)
#define REP1(i, x) for(int i = 0; i < (x); ++i)
#define REP2(i, l, r) for(int i = (l); i < (r); ++i)
#define REP3(i, l, r, c) for(int i = (l); i < (r); i += (c))

#define DREPname(a, b, c, d, e, ...) e
#define drep(...) DREPname(__VA_ARGS__, DREP3, DREP2, DREP1)(__VA_ARGS__)
#define DREP1(i, x) for(int i = (x)-1; i >= 0; --i)
#define DREP2(i, l, r) for(int i = (r)-1; i >= (l); --i)
#define DREP3(i, l, r, c) for(int i = (r)-1; i >= (l); i -= (c))

#pragma endregion

#pragma region aliases

using ll = long long;
using ld = long double;
using ull = unsigned long long;
using vi = std::vector<int>;
using vvi = std::vector<std::vector<int>>;
using vvvi = std::vector<std::vector<std::vector<int>>>;
using vll = std::vector<ll>;
using vvll = std::vector<vll>;
using vvvll = std::vector<vvll>;
using pii = std::pair<int, int>;
using pll = std::pair<long long, long long>;
template <class T = ll>
using V = std::vector<T>;
template <class T = ll>
using VV = V<V<T>>;
template <class T = ll>
using VVV = V<V<V<T>>>;
template <class T = ll>
using pqup = std::priority_queue<T, std::vector<T>, std::greater<T>>;
template <class T = ll>
using pqdn = std::priority_queue<T>;

#pragma endregion

#pragma region constants

const int inf = 1e9;
const long long INF = 1e18;
const long double pi = acos(-1);
const char dl = '\n';
const char sp = ' ';
int dx[8] = {1, 0, -1, 0, 1, -1, -1, 1};
int dy[8] = {0, 1, 0, -1, 1, 1, -1, -1};

const int mod_1000000007 = 1000000007;
const int mod_998244353 = 998244353;

#pragma endregion

#pragma region basic_operation

template <class T0, class T1, class T2>
inline bool in_range(T0 x, T1 lef, T2 rig) {
	return ((lef <= x) && (x < rig));
}

template <class T>
inline bool chmin(T &a, T b) {
	if(a > b) {
		a = b;
		return true;
	}
	return false;
}
template <class T>
inline bool chmax(T &a, T b) {
	if(a < b) {
		a = b;
		return true;
	}
	return false;
}

void Yes(bool f = 1) { std::cout << (f ? "Yes" : "No") << '\n'; }
void No() { std::cout << "No\n"; }
void YES(bool f = 1) { std::cout << (f ? "YES" : "NO") << '\n'; }
void NO() { std::cout << "NO\n"; }

template <class T>
void drop(T answer) {
	std::cout << answer << '\n';
	exit(0);
}

void err(bool flag = true) {
	if(!flag) return;
	std::cout << -1 << '\n';
	exit(0);
}

template <class T>
void vout(std::vector<T> const &v, bool vertically = 0) {
	if(vertically) {
		for(auto const &a : v) {
			std::cout << a << '\n';
		}
		return;
	}
	for(auto it = v.begin(); it != v.end(); it++) {
		std::cout << (*it);
		if(it != v.end() - 1) {
			std::cout << ' ';
		}
	}
	std::cout << '\n';
	return;
}

inline void print() { std::cout << '\n'; }
template <class T>
inline void print(T x) {
	std::cout << x << '\n';
	return;
}
template <typename Head, typename... Tail>
void print(Head H, Tail... T) {
	std::cout << H << " ";
	print(T...);
}

template <class T>
void add(std::vector<T> &v, T val) {
	for(auto &a : v) a += val;
	return;
}

template <class T>
T dup(T a, T b) {
	assert(b != 0);
	return (a + b - 1) / b;
}

template <class T>
T greatest_lower_multiple(T x, T d) {
	if(d == 0) return 0;
	if(d < 0) d *= -1;
	T y = x % d;
	if(y < 0) y += d;
	return x - y;
}

template <class T>
T least_upper_multiple(T x, T d) {
	return -greatest_lower_multiple(-x, d);
}

long long POW(long long a, long long n) {
	long long res = 1;
	while(n > 0) {
		if(n & 1) res = res * a;
		a = a * a;
		n >>= 1;
	}
	return res;
}

long long modpow(long long a, long long n, long long mod) {	 // a^n mod
	assert(n >= 0 && mod);
	if(mod == 1) return 0LL;
	long long res = 1;
	while(n > 0) {
		if(n & 1) res = res * a % mod;
		a = a * a % mod;
		n >>= 1;
	}
	return res < 0 ? res + mod : res;
}

// return x which satisfies a * x % mod == gcd(a, mod)
// not (mod | a)
long long modinv(long long a, long long mod) {
	long long b = mod, u = 1, v = 0;
	while(b) {
		long long t = a / b;
		a -= t * b;
		std::swap(a, b);
		u -= t * v;
		std::swap(u, v);
	}
	u %= mod;
	if(u < 0) u += mod;
	return u;
}

template <class T>
int lb(const std::vector<T> &a, const T x) {
	return std::distance(a.begin(), std::lower_bound(a.begin(), a.end(), x));
}
template <class T>
int ub(const std::vector<T> &a, const T x) {
	return std::distance(a.begin(), std::upper_bound(a.begin(), a.end(), x));
}
template <class T>
void unq_sort(std::vector<T> &a) {
	std::sort(a.begin(), a.end());
	a.erase(std::unique(a.begin(), a.end()), a.end());
}
template <class T>
std::vector<int> press(std::vector<T> &a) {
	auto vec = a;
	unq_sort(vec);
	std::vector<int> ret;
	for(auto &v : a) ret.push_back(lb(vec, v));
	return ret;
}

#pragma endregion

#pragma region input
#define VEC(type, name, size) \
	vector<type> name(size);  \
	scanner::INPUT(name)
#define VVEC(type, name, h, w)                     \
	vector<vector<type>> name(h, vector<type>(w)); \
	scanner::INPUT(name)
#define INT(...)     \
	int __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define LL(...)            \
	long long __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define STR(...)        \
	string __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define CHR(...)      \
	char __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define DBL(...)        \
	double __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define LD(...)              \
	long double __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define TPL3(type0, type1, type2, name)   \
	std::tuple<type0, type1, type2> name; \
	scanner::INPUT(name);
#define TPL4(type0, type1, type2, type3, name)   \
	std::tuple<type0, type1, type2, type3> name; \
	scanner::INPUT(name);

namespace scanner {
template <class T>
void scan(T &a) {
	std::cin >> a;
}

template <class T, class L>
void scan(std::pair<T, L> &p) {
	scan(p.first);
	scan(p.second);
}

template <class T0, class T1, class T2>
void scan(std::tuple<T0, T1, T2> &p) {
	T0 t0;
	T1 t1;
	T2 t2;
	scan(t0);
	scan(t1);
	scan(t2);
	p = std::make_tuple(t0, t1, t2);
}

template <class T0, class T1, class T2, class T3>
void scan(std::tuple<T0, T1, T2, T3> &p) {
	T0 t0;
	T1 t1;
	T2 t2;
	T3 t3;
	scan(t0);
	scan(t1);
	scan(t2);
	scan(t3);
	p = std::make_tuple(t0, t1, t2, t3);
}

template <class T>
void scan(std::vector<T> &a) {
	for(auto &i : a) scan(i);
}

void INPUT() {}
template <class Head, class... Tail>
void INPUT(Head &head, Tail &... tail) {
	scan(head);
	INPUT(tail...);
}
}  // namespace scanner

template <typename T1, typename T2>
std::istream &operator>>(std::istream &is, std::pair<T1, T2> &p) {
	is >> p.first >> p.second;
	return is;
}
#pragma endregion

#pragma region debug

#pragma region output
template <typename T1, typename T2>
std::ostream &std::operator<<(std::ostream &os, const std::pair<T1, T2> &p) {
	os << p.first << " " << p.second;
	return os;
}
template <class T>
std::ostream &std::operator<<(std::ostream &os, const std::vector<T> &v) {
	for(int i = 0; i < (int)v.size(); i++) {
		if(i) os << " ";
		os << v[i];
	}
	return os;
}
#pragma endregion

#pragma region view

namespace viewer {
void view(const long long e) {
	if(e == INF)
		std::cerr << "INF";
	else if(e == -INF)
		std::cerr << "-INF";
	else
		std::cerr << e;
}

void view(const int e) {
	if(e == inf)
		std::cerr << "inf";
	else if(e == -inf)
		std::cerr << "-inf";
	else
		std::cerr << e;
}

template <typename T>
void view(const T e) {
	std::cerr << e;
}

template <typename T, typename U>
void view(const std::pair<T, U> &p) {
	std::cerr << "(";
	view(p.first);
	std::cerr << ", ";
	view(p.second);
	std::cerr << ")";
}

template <class T0, class T1, class T2>
void view(const std::tuple<T0, T1, T2> &p) {
	std::cerr << "(";
	view(std::get<0>(p));
	std::cerr << ", ";
	view(std::get<1>(p));
	std::cerr << ", ";
	view(std::get<2>(p));
	std::cerr << ")";
}

template <class T0, class T1, class T2, class T3>
void view(const std::tuple<T0, T1, T2, T3> &p) {
	std::cerr << "(";
	view(std::get<0>(p));
	std::cerr << ", ";
	view(std::get<1>(p));
	std::cerr << ", ";
	view(std::get<2>(p));
	std::cerr << ", ";
	view(std::get<3>(p));
	std::cerr << ")";
}

template <typename T>
void view(const std::set<T> &s) {
	if(s.empty()) {
		std::cerr << "{ }";
		return;
	}
	std::cerr << "{ ";
	for(auto &t : s) {
		view(t);
		std::cerr << ", ";
	}
	std::cerr << "\b\b }";
}

template <typename T>
void view(const std::unordered_set<T> &s) {
	if(s.empty()) {
		std::cerr << "{ }";
		return;
	}
	std::cerr << "{ ";
	for(auto &t : s) {
		view(t);
		std::cerr << ", ";
	}
	std::cerr << "\b\b }";
}

template <typename T>
void view(const std::vector<T> &v) {
	if(v.empty()) {
		std::cerr << "{ }";
		return;
	}
	std::cerr << "{ ";
	for(const auto &e : v) {
		view(e);
		std::cerr << ", ";
	}
	std::cerr << "\b\b }";
}

template <typename T>
void view(const std::vector<std::vector<T>> &vv) {
	std::cerr << "{\n";
	for(const auto &v : vv) {
		std::cerr << "\t";
		view(v);
		std::cerr << '\n';
	}
	std::cerr << "}";
}

template <typename T, typename U>
void view(const std::vector<std::pair<T, U>> &v) {
	std::cerr << "{\n";
	for(const auto &c : v) {
		std::cerr << "\t(";
		view(c.first);
		std::cerr << ", ";
		view(c.second);
		std::cerr << ")\n";
	}
	std::cerr << "}";
}

template <class T0, class T1, class T2>
void view(const std::vector<std::tuple<T0, T1, T2>> &v) {
	if(v.empty()) {
		std::cerr << "{ }";
		return;
	}
	std::cerr << '{';
	for(const auto &t : v) {
		std::cerr << "\n\t";
		view(t);
		std::cerr << ",";
	}
	std::cerr << "\n}";
}

template <class T0, class T1, class T2, class T3>
void view(const std::vector<std::tuple<T0, T1, T2, T3>> &v) {
	if(v.empty()) {
		std::cerr << "{ }";
		return;
	}
	std::cerr << '{';
	for(const auto &t : v) {
		std::cerr << "\n\t";
		view(t);
		std::cerr << ",";
	}
	std::cerr << "\n}";
}

template <typename T, typename U>
void view(const std::map<T, U> &m) {
	std::cerr << "{\n";
	for(const auto &t : m) {
		std::cerr << "\t[";
		view(t.first);
		std::cerr << "] : ";
		view(t.second);
		std::cerr << '\n';
	}
	std::cerr << "}";
}

template <typename T, typename U>
void view(const std::unordered_map<T, U> &m) {
	std::cerr << "{\n";
	for(const auto &t : m) {
		std::cerr << "\t[";
		view(t.first);
		std::cerr << "] : ";
		view(t.second);
		std::cerr << '\n';
	}
	std::cerr << "}";
}
}  // namespace viewer

#pragma endregion

// when debugging : g++ foo.cpp -DLOCAL
#ifdef LOCAL
void debug_out() {}
template <typename Head, typename... Tail>
void debug_out(Head H, Tail... T) {
	viewer::view(H);
	std::cerr << ", ";
	debug_out(T...);
}
#define debug(...)                                                \
	do {                                                          \
		std::cerr << __LINE__ << " [" << #__VA_ARGS__ << "] : ["; \
		debug_out(__VA_ARGS__);                                   \
		std::cerr << "\b\b]\n";                                   \
	} while(0)
#define dump(x)                                      \
	do {                                             \
		std::cerr << __LINE__ << " " << #x << " : "; \
		viewer::view(x);                             \
		std::cerr << '\n';                           \
	} while(0)

#else
#define debug(...) (void(0))
#define dump(x) (void(0))
#endif

#pragma endregion

#pragma region geometry

using Real = long double;  //時間がかかる割にdoubleと大差ない
using Point = complex<Real>;
const Real EPS = 1e-10, /*1e-8はたまに落ちる*/ PI = acos(-1);

inline bool eq(Real a, Real b) { return fabs(b - a) < EPS; }  //誤差無視の等式

//実スカラー倍
Point operator*(const Point &p, const Real &d) { return Point(real(p) * d, imag(p) * d); }

// point入力受け取り
istream &operator>>(istream &is, Point &p) {
	Real a, b;
	is >> a >> b;
	p = Point(a, b);
	return is;
}
// point出力
ostream &operator<<(ostream &os, Point &p) { return os << fixed << setprecision(10) << p.real() << " " << p.imag(); }

// 点 p を反時計回りに theta 回転
Point rotate(Real theta, const Point &p) {
	return Point(cos(theta) * p.real() - sin(theta) * p.imag(), sin(theta) * p.real() + cos(theta) * p.imag());
}

Real radian_to_degree(Real r) { return (r * 180.0 / PI); }

Real degree_to_radian(Real d) { return (d * PI / 180.0); }

// a-b-c の角度のうち小さい方を返す
Real get_angle(const Point &a, const Point &b, const Point &c) {
	const Point v(a - b), /* もとはv(b-a)になってたので修正 */ w(c - b);
	Real alpha = atan2(v.imag(), v.real()), beta = atan2(w.imag(), w.real());
	if(alpha > beta) swap(alpha, beta);
	Real theta = (beta - alpha);
	return min(theta, 2 * acos(-1) - theta);
}

namespace std {
bool operator<(const Point &a, const Point &b) { return a.real() != b.real() ? a.real() < b.real() : a.imag() < b.imag(); }
}  // namespace std

struct Line {
	Point a, b;

	Line() = default;

	Line(Point a, Point b) : a(a), b(b) {}

	Line(Real A, Real B, Real C)  // Ax + By = C
	{
		if(eq(A, 0))
			a = Point(0, C / B), b = Point(1, C / B);
		else if(eq(B, 0))
			b = Point(C / A, 0), b = Point(C / A, 1);
		else
			a = Point(0, C / B), b = Point(C / A, 0);
	}

	friend ostream &operator<<(ostream &os, Line &p) { return os << p.a << " to " << p.b; }

	friend istream &operator>>(istream &is, Line &a) { return is >> a.a >> a.b; }
};

struct Segment : Line {
	Segment() = default;

	Segment(Point a, Point b) : Line(a, b) {}
};

struct Circle {
	Point p;
	Real r;

	Circle() = default;

	Circle(Point p, Real r) : p(p), r(r) {}
};

using Points = vector<Point>;
using Polygon = vector<Point>;
using Segments = vector<Segment>;
using Lines = vector<Line>;
using Circles = vector<Circle>;

//外積
Real cross(const Point &a, const Point &b) { return real(a) * imag(b) - imag(a) * real(b); }
//内積
Real dot(const Point &a, const Point &b) { return real(a) * real(b) + imag(a) * imag(b); }

// http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_1_C
// 点の回転方向
int ccw(const Point &a, Point b, Point c) {
	b = b - a, c = c - a;
	if(cross(b, c) > EPS) return +1;   // "COUNTER_CLOCKWISE"
	if(cross(b, c) < -EPS) return -1;  // "CLOCKWISE"
	if(dot(b, c) < 0) return +2;	   // "ONLINE_BACK"
	if(norm(b) < norm(c)) return -2;   // "ONLINE_FRONT"
	return 0;						   // "ON_SEGMENT"
}

// http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_2_A
// 平行判定
bool parallel(const Line &a, const Line &b) { return eq(cross(a.b - a.a, b.b - b.a), 0.0); }

// http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_2_A
// 垂直判定
bool orthogonal(const Line &a, const Line &b) { return eq(dot(a.a - a.b, b.a - b.b), 0.0); }

// http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_1_A
// 射影
// 直線 l に p から垂線を引いた交点を求める
Point projection(const Line &l, const Point &p) {
	double t = dot(p - l.a, l.a - l.b) / norm(l.a - l.b);
	return l.a + (l.a - l.b) * t;
}

Point projection(const Segment &l, const Point &p) {
	double t = dot(p - l.a, l.a - l.b) / norm(l.a - l.b);
	return l.a + (l.a - l.b) * t;
}

// http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_1_B
// 反射
// 直線 l を対称軸として点 p  と線対称にある点を求める
Point reflection(const Line &l, const Point &p) { return p + (projection(l, p) - p) * 2.0; }

bool intersect(const Line &l, const Point &p) { return abs(ccw(l.a, l.b, p)) != 1; }

bool intersect(const Line &l, const Line &m) { return abs(cross(l.b - l.a, m.b - m.a)) > EPS || abs(cross(l.b - l.a, m.b - l.a)) < EPS; }

bool intersect(const Segment &s, const Point &p) { return ccw(s.a, s.b, p) == 0; }

bool intersect(const Line &l, const Segment &s) { return cross(l.b - l.a, s.a - l.a) * cross(l.b - l.a, s.b - l.a) < EPS; }

Real distance(const Line &l, const Point &p);

bool intersect(const Circle &c, const Line &l) { return distance(l, c.p) <= c.r + EPS; }

bool intersect(const Circle &c, const Point &p) { return abs(abs(p - c.p) - c.r) < EPS; }

// http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_2_B
// 線分同士の交差判定　(端点が他方の線分上にあるならば交差とみなす)
bool intersect(const Segment &s, const Segment &t) {
	return ccw(s.a, s.b, t.a) * ccw(s.a, s.b, t.b) <= 0 && ccw(t.a, t.b, s.a) * ccw(t.a, t.b, s.b) <= 0;
}

int intersect(const Circle &c, const Segment &l) {
	if(norm(projection(l, c.p) - c.p) - c.r * c.r > EPS) return 0;
	auto d1 = abs(c.p - l.a), d2 = abs(c.p - l.b);
	if(d1 < c.r + EPS && d2 < c.r + EPS) return 0;
	if(d1 < c.r - EPS && d2 > c.r + EPS || d1 > c.r + EPS && d2 < c.r - EPS) return 1;
	const Point h = projection(l, c.p);
	if(dot(l.a - h, l.b - h) < 0) return 2;
	return 0;
}

// http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_7_A&lang=jp
int intersect(Circle c1, Circle c2) {
	if(c1.r < c2.r) swap(c1, c2);
	Real d = abs(c1.p - c2.p);
	if(c1.r + c2.r < d) return 4;	  // 外に離れている
	if(eq(c1.r + c2.r, d)) return 3;  // 外接
	if(c1.r - c2.r < d) return 2;	  // 交差
	if(eq(c1.r - c2.r, d)) return 1;  // 内接
	return 0;						  // 内包
}

Real distance(const Point &a, const Point &b) { return abs(a - b); }

Real distance(const Line &l, const Point &p) { return abs(p - projection(l, p)); }

Real distance(const Line &l, const Line &m) { return intersect(l, m) ? 0 : distance(l, m.a); }

Real distance(const Segment &s, const Point &p) {
	Point r = projection(s, p);
	if(intersect(s, r)) return abs(r - p);
	return min(abs(s.a - p), abs(s.b - p));
}

// http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_2_D
Real distance(const Segment &a, const Segment &b) {
	if(intersect(a, b)) return 0;
	return min({distance(a, b.a), distance(a, b.b), distance(b, a.a), distance(b, a.b)});
}

Real distance(const Line &l, const Segment &s) {
	if(intersect(l, s)) return 0;
	return min(distance(l, s.a), distance(l, s.b));
}

Point crosspoint(const Line &l, const Line &m) {
	Real A = cross(l.b - l.a, m.b - m.a);
	Real B = cross(l.b - l.a, l.b - m.a);
	if(eq(abs(A), 0.0) && eq(abs(B), 0.0)) return m.a;
	return m.a + (m.b - m.a) * B / A;
}

// http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_2_C
Point crosspoint(const Segment &l, const Segment &m) { return crosspoint(Line(l), Line(m)); }

// http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_7_D
pair<Point, Point> crosspoint(const Circle &c, const Line l) {
	Point pr = projection(l, c.p);
	Point e = (l.b - l.a) / abs(l.b - l.a);
	if(eq(distance(l, c.p), c.r)) return {pr, pr};
	double base = sqrt(c.r * c.r - norm(pr - c.p));
	return {pr - e * base, pr + e * base};
}

pair<Point, Point> crosspoint(const Circle &c, const Segment &l) {
	Line aa = Line(l.a, l.b);
	if(intersect(c, l) == 2) return crosspoint(c, aa);
	auto ret = crosspoint(c, aa);
	if(dot(l.a - ret.first, l.b - ret.first) < 0)
		ret.second = ret.first;
	else
		ret.first = ret.second;
	return ret;
}

// http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_7_E
pair<Point, Point> crosspoint(const Circle &c1, const Circle &c2) {
	Real d = abs(c1.p - c2.p);
	Real a = acos((c1.r * c1.r + d * d - c2.r * c2.r) / (2 * c1.r * d));  // 余弦定理
	Real t = atan2(c2.p.imag() - c1.p.imag(), c2.p.real() - c1.p.real());
	Point p1 = c1.p + Point(cos(t + a) * c1.r, sin(t + a) * c1.r);
	Point p2 = c1.p + Point(cos(t - a) * c1.r, sin(t - a) * c1.r);
	return {p1, p2};
}

// http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_7_F
// "点 p を通る円 c の接線"の接点2つ
pair<Point, Point> tangent(const Circle &c1, const Point &p2) { return crosspoint(c1, Circle(p2, sqrt(norm(c1.p - p2) - c1.r * c1.r))); }

// http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_7_G
// 円 c1, c2 の共通接線
Lines tangent(Circle c1, Circle c2) {
	Lines ret;
	if(c1.r < c2.r) swap(c1, c2);
	Real g = norm(c1.p - c2.p);
	if(eq(g, 0)) return ret;
	Point u = (c2.p - c1.p) / sqrt(g);
	Point v = rotate(PI * 0.5, u);
	for(int s : {-1, 1}) {
		Real h = (c1.r + s * c2.r) / sqrt(g);
		if(eq(1 - h * h, 0)) {
			ret.emplace_back(c1.p + u * c1.r, c1.p + (u + v) * c1.r);
		} else if(1 - h * h > 0) {
			Point uu = u * h, vv = v * sqrt(1 - h * h);
			ret.emplace_back(c1.p + (uu + vv) * c1.r, c2.p - (uu + vv) * c2.r * s);
			ret.emplace_back(c1.p + (uu - vv) * c1.r, c2.p - (uu - vv) * c2.r * s);
		}
	}
	return ret;
}

// http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_3_B
// 凸性判定 (反時計回り)
bool is_convex(const Polygon &p) {
	int n = (int)p.size();
	for(int i = 0; i < n; i++) {
		if(ccw(p[(i + n - 1) % n], p[i], p[(i + 1) % n]) == -1) return false;
	}
	return true;
}

// http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_4_A
// 凸包
Polygon convex_hull(Polygon &p) {
	int n = (int)p.size(), k = 0;
	if(n <= 2) return p;
	sort(p.begin(), p.end());  // x -> yの順にソート
	vector<Point> ch(2 * n);
	for(int i = 0; i < n; ch[k++] = p[i++]) {  //下半分を構成
		while(k >= 2 && cross(ch[k - 1] - ch[k - 2], p[i] - ch[k - 1]) < EPS) --k;
	}
	for(int i = n - 2, t = k + 1; i >= 0; ch[k++] = p[i--]) {  //上半分を構成
		while(k >= t && cross(ch[k - 1] - ch[k - 2], p[i] - ch[k - 1]) < EPS) --k;
	}
	ch.resize(k - 1);
	return ch;
}

// http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_3_C
// 多角形と点の包含判定
enum { OUT, ON, IN };

int contains(const Polygon &Q, const Point &p) {
	bool in = false;
	for(int i = 0; i < Q.size(); i++) {
		Point a = Q[i] - p, b = Q[(i + 1) % Q.size()] - p;
		if(a.imag() > b.imag()) swap(a, b);
		if(a.imag() <= 0 && 0 < b.imag() && cross(a, b) < 0) in = !in;
		if(cross(a, b) == 0 && dot(a, b) <= 0) return ON;
	}
	return in ? IN : OUT;
}

// http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_7_H
// 円と多角形の共通部分の面積
Real area(const Polygon &p, const Circle &c) {
	if(p.size() < 3) return 0.0;
	function<Real(Circle, Point, Point)> cross_area = [&](const Circle &c, const Point &a, const Point &b) {
		Point va = c.p - a, vb = c.p - b;
		Real f = cross(va, vb), ret = 0.0;
		if(eq(f, 0.0)) return ret;
		if(max(abs(va), abs(vb)) < c.r + EPS) return f;
		if(distance(Segment(a, b), c.p) > c.r - EPS) return c.r * c.r * arg(vb * conj(va));	 // 0.5倍してなくない？
		auto u = crosspoint(c, Segment(a, b));
		vector<Point> tot{a, u.first, u.second, b};
		for(int i = 0; i + 1 < tot.size(); i++) {
			ret += cross_area(c, tot[i], tot[i + 1]);
		}
		return ret;
	};
	Real A = 0;
	for(int i = 0; i < p.size(); i++) {
		A += cross_area(c, p[i], p[(i + 1) % p.size()]);
	}
	return A * 0.5;	 // 0.5倍が抜けていたので修正;
}

// http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_4_B
// 凸多角形の直径(最遠頂点対間距離)
Real convex_diameter(const Polygon &p) {
	int N = (int)p.size();
	int is = 0, js = 0;
	for(int i = 1; i < N; i++) {
		if(p[i].imag() > p[is].imag()) is = i;
		if(p[i].imag() < p[js].imag()) js = i;
	}
	Real maxdis = norm(p[is] - p[js]);

	int maxi, maxj, i, j;
	i = maxi = is;
	j = maxj = js;
	do {
		if(cross(p[(i + 1) % N] - p[i], p[(j + 1) % N] - p[j]) >= 0) {
			j = (j + 1) % N;
		} else {
			i = (i + 1) % N;
		}
		if(norm(p[i] - p[j]) > maxdis) {
			maxdis = norm(p[i] - p[j]);
			maxi = i;
			maxj = j;
		}
	} while(i != is || j != js);
	return sqrt(maxdis);
}

// http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_5_A
// 最近点対
Real closest_pair(Points ps) {
	if(ps.size() <= 1) throw(0);
	sort(begin(ps), end(ps));  // xでソート

	auto compare_y = [&](const Point &a, const Point &b) { return imag(a) < imag(b); };
	vector<Point> beet(ps.size());
	const Real INF = 1e18;

	function<Real(int, int)> rec = [&](int left, int right) {
		if(right - left <= 1) return INF;
		int mid = (left + right) >> 1;
		auto x = real(ps[mid]);	 // 領域内でx座標真中の点
		auto ret = min(rec(left, mid), rec(mid, right));
		// y座標でソートされた2つの区間をマージ
		inplace_merge(begin(ps) + left, begin(ps) + mid, begin(ps) + right, compare_y);
		int ptr = 0;
		for(int i = left; i < right; i++) {
			if(abs(real(ps[i]) - x) >= ret) continue;
			for(int j = 0; j < ptr; j++) {
				auto luz = ps[i] - beet[ptr - j - 1];
				if(imag(luz) >= ret) break;
				ret = min(ret, abs(luz));
			}
			beet[ptr++] = ps[i];
		}
		return ret;
	};
	return rec(0, (int)ps.size());
}

// (aからの距離):(bからの距離) = dist_a : dist_b となる点の集合(円)を返す
Circle Apollonius_Circle(Point a, Point b, Real dist_a, Real dist_b) {
	assert(!eq(dist_a, dist_b));  //同じ場合はbisectionで
	Real asq = dist_a * dist_a, bsq = dist_b * dist_b;
	Point p = (a * bsq - b * asq) * (1 / (bsq - asq));
	Real r = abs(a - b) * dist_a * dist_b / abs(asq - bsq);
	return Circle(p, r);
}

// 二等分線
Line bisection(Point a, Point b) {
	a = (a + b) * 0.5;
	b = rotate(PI / 2, b - a) + a;
	return Line(a, b);
}

#pragma endregion

void solve() {
	INT(n, m);
	if(n + m == 0) exit(0);

	Circles c;
	rep(n) {
		Real x, y, r;
		cin >> x >> y >> r;
		c.emplace_back(Point(x, y), r);
	}

	VV<pair<Real, int>> g(n, V<pair<Real, int>>(n, pair(-1., 0)));

	rep(i, n) rep(j, n) {
		if(i == j) continue;
		if(intersect(c[i], c[j]) == 2) {
			// g[i].emplace_back(arg(c[j].p - c[i].p), j);
			g[i][j] = pair(arg(c[j].p - c[i].p), 1);
		}
	}


	vector<vector<int>> convexes;
	vi visited(n);
	rep(first, n) {
		while(1) {
			bool b = true;
			vi cycle;
			Real direction = 0.;
			int now = first;

			do {
				int nxt = -1;
				Real angle = -INF;
				auto nxt_dir = direction;
				rep(j, n) {
					if(g[now][j].second == 0) continue;
					Real diff = g[now][j].first - direction;
					while(diff < -pi + EPS * 2) diff += pi * 2;
					while(diff >= pi - EPS) diff -= pi * 2;

					if(chmax(angle, diff)) {
						nxt = j;
						nxt_dir = g[now][j].first;
					}
				}

				if(nxt == -1) {
					b = false;
					break;
				}
				g[now][nxt].second = 0;
				direction = nxt_dir;
				now = nxt;
				cycle.push_back(nxt);
			} while(now != first);
			if(!b) break;
			convexes.push_back(cycle);
		}
	}
	// int cs = convexes.size();

	V<Polygon> plgns;

	foa(cvx, convexes) {
		Polygon plgn;
		foa(ver, cvx) plgn.push_back(c[ver].p);
		plgns.push_back(plgn);
	}

	rep(loop, m) {
		Point pee, kyu;	 // P, Q
		// Real x, y;
		// cin >> x >> y;
		// pee.emplace_back(x, y);
		// cin >> x >> y;
		// kyu.emplace_back(x, y);
		cin >> pee >> kyu;

		vector<int> pee_in;
		vector<int> kyu_in;

		rep(i, n) {
			if(distance(c[i].p, pee) < c[i].r) {
				pee_in.push_back(i);
			}
		}

		rep(i, n) {
			if(distance(c[i].p, kyu) < c[i].r) {
				kyu_in.push_back(i);
			}
		}

		if(!pee_in.empty()) {
			cout << (pee_in == kyu_in ? "YES" : "NO") << (loop == m - 1 ? '\n' : ' ');
			continue;
		}

		rep(i, plgns.size()) {
			// foa(plgn, plgns) {
			auto plgn = plgns[i];
			if(contains(plgn, pee) == IN) {
				pee_in.push_back(i);
			}
		}

		rep(i, plgns.size()) {
			// foa(plgn, plgns) {
			auto plgn = plgns[i];
			if(contains(plgn, kyu) == IN) {
				kyu_in.push_back(i);
			}
		}

		cout << (pee_in == kyu_in ? "YES" : "NO") << (loop == m - 1 ? '\n' : ' ');
	}

	debug(convexes);
	return;
}
int main() {
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);
	std::cout << std::fixed << std::setprecision(15);
	srand((unsigned)time(NULL));

	while(1) solve();

	return 0;
}
accuracy: 1
time_ms: 30
memory_kb: 4008
final_rank: 15

code_snippet: #include"bits/stdc++.h"
using namespace std;

#define REP(i,n) for(int i=0;i<(n);i++)
#define FOR(i,m,n) for(int i=(m);i<(n);i++)
#define REPR(i,n) for(int i=(n)-1;i>=0;i--)
const bool DEBUG = false;
const double EPS = 1e-8;
typedef complex<double>point;
point operator*(const point &p, const double &d) {
	return point(real(p)*d, imag(p)*d);
}
namespace std {
	bool operator<(const point &a, const point &b) {
		return real(a) != real(b) ? real(a)<real(b) : imag(a)<imag(b);
	}
}
double dist(const point &a, const point &b) {
	return abs(a - b);
}
struct Line :public vector<point> {
	Line(const point &a, const point &b) {
		push_back(a); push_back(b);
	}
};
typedef vector<Line> Lines;
double cross(const point &a, const point &b) {
	return real(a)*imag(b) - imag(a)*real(b);
}
double dot(const point &a, const point &b) {
	return real(a)*real(b) + imag(a)*imag(b);
}
int ccw(point a, point b, point c) {
	b = b - a, c = c - a;
	if (cross(b, c)>EPS)return +1;
	if (cross(b, c)<-EPS)return -1;
	if (dot(b, c)<-EPS)return +2;
	if (norm(b)<norm(c))return -2;
	return 0;
}

bool intersectSP(const Line &s, const point &p) {
	return abs(s[0] - p) + abs(s[1] - p) - abs(s[1] - s[0])<EPS;
}
bool isSamePoint(const point &p1, const point &p2) {
	return p1==p2||abs(p1 - p2)<=EPS;
}
bool intersectSS(const Line &s, const Line &t) {
	return ccw(s[0], s[1], t[0])*ccw(s[0], s[1], t[1]) <= 0 &&
		ccw(t[0], t[1], s[0])*ccw(t[0], t[1], s[1]) <= 0;
}
bool connectSS(const Line &s, const Line &t) {
	REP(i, 2)REP(j, 2) {
		if (isSamePoint(s[i], t[j]))return true;
		//if (s[i] == t[j])return true;
	}
	return false;
}
point crosspoint(const Line &l, const Line &m) {
	double A = cross(l[1] - l[0], m[1] - m[0]);
	double B = cross(l[1] - l[0], l[1] - m[0]);
	if (abs(A)<EPS&&abs(B)<EPS)return m[0];
	if (abs(A)<EPS)assert(false);
	return m[0] + B / A * (m[1] - m[0]);
}
struct Circle {
	point c; double r;
	int id;
	Circle(const point &c_, double r_, int id_) :c(c_), r(r_), id(id_) {}
	bool contains(const point &p) {
		if (dist(c, p)<r + EPS)return true;
		else return false;
	}
};

struct Polygon :public vector<point> {
	//0->OUT,1->ON,2->IN
	int contains(const point &p) {
		bool in = false;
		int N = this->size();
		for (int i = 0; i<N; i++) {
			point a = this->at(i) - p;
			point b = this->at((i + 1) % N) - p;
			if (imag(a)>imag(b))std::swap(a, b);
			if (imag(a) <= 0 && 0<imag(b))if (cross(a, b)<0)in = !in;
			if (cross(a, b) == 0 && dot(a, b) <= 0)return 1;
		}
		return in ? 2 : 0;
	}
	void print() {
		if (DEBUG) {
			cerr << "number of points:" << this->size() << endl;
            for(int i=0;i<(int)this->size();i++){
				cerr << i + 1 << ":" << this->at(i) << endl;
			}
		}

	}
};
typedef vector<Circle> Circles;
set<int> get_enclosing_circle(const point &p, const Circles &Cs) {
	set<int> ret;
	for (auto c : Cs) {
		if (c.contains(p))ret.insert(c.id);
	}
	return ret;
}

struct Equiv {
	vector<int>links;
	Equiv(int n) :links(n) {
		REP(i, n)links[i] = i;
	}
	int find(int x) {
		int xx = links[x];
		if (xx == x)return x;
		return links[x] = find(xx);
	}
	void unify(int x, int y) {
		int xx = find(x), yy = find(y);
		if (xx <= yy)links[yy] = xx;
		else links[xx] = yy;
	}
};
typedef vector<Lines> vLines;
vLines connectedComponent(const Lines &ls) {
	vLines ret;
	Equiv equiv(ls.size());
	REP(i, (int)ls.size()) {
		REP(j, i) {
			if (connectSS(ls[i], ls[j])) {
				equiv.unify(i, j);
			}
		}
	}
	REP(i, (int)ls.size()) {
		if (equiv.find(i) == i) {
			Lines newLs;
			newLs.push_back(ls[i]);
			FOR(j, i + 1, (int)ls.size()) {
				if (equiv.find(j) == i)newLs.push_back(ls[j]);
			}
			ret.push_back(newLs);
		}
	}
	return ret;
}
typedef pair<double, point>doublePoint;
typedef vector<doublePoint>vDoublePoint;
typedef map<point, vDoublePoint>adjPoint;
typedef vector<point>Points;

point findNext(const vDoublePoint &vdp, const point &p) {
	//隣接点のリスト(偏角ソート済み)から移動前の点を探してその次
	int n = vdp.size();
	REP(i, n) {
		if (p == vdp[i].second) {
			return vdp[(i + 1) % n].second;
		}
	}
	return vdp[0].second;
}
bool used[101][101];
void make_polys(point minPoint, vector<Polygon>&polys,adjPoint &aPoint,map<point, int>&p2id,
map<int, point>&id2p,int id) {
	Polygon ret;
	REP(i, id)REP(j, id)used[i][j] = false;
	ret.push_back(minPoint);
	point curP = aPoint[minPoint][0].second;
	used[p2id[minPoint]][p2id[curP]] = true;
	point lastP = minPoint;
	while (curP != minPoint) {
		ret.push_back(curP);
		point nextP = findNext(aPoint[curP], lastP);
		used[p2id[curP]][p2id[nextP]] = true;
		lastP = curP;
		curP = nextP;
	}

	
	polys.push_back(ret);

    bool all_used=false;
	while (!all_used) {
        all_used=true;
		bool found = false;
		point st, nx;
        st={1e9,1e9};
		REP(i, id) {
            if(found)break;
			for (auto nxt : aPoint[id2p[i]]) {
				if (!used[i][p2id[nxt.second]]) {
                    all_used=false;
					found = true;
                    if(id2p[i]<st){
                        st=id2p[i];
                        nx=nxt.second;
                    }
				}
			}
		}
        if(all_used)break;
		Polygon res;
		res.push_back(st);
		res.push_back(nx);
		used[p2id[st]][p2id[nx]] = true;
		point cur = nx;
		point las = st;
		while (cur != st) {
			vDoublePoint v = aPoint[cur];
			int n = v.size();
			int s_idx = -1;
			bool found = false;
			REP(i, n) {
				if (isSamePoint(v[i].second,las)) {
					s_idx = i; break;
				}
			}
            if(s_idx<0){
                cerr<<cur<<endl;
                cerr<<las<<endl;
            }
            assert(s_idx>=0);
			for (int i = s_idx + 1; i < s_idx + n + 1; i++) {
				int idx = i % n;
				if (v[idx].second == cur)continue;
				int cur_id = p2id[cur];
				int nxt_id = p2id[v[idx].second];
				if (!used[cur_id][nxt_id]) {
					found = true;
					las = cur;
					cur = v[idx].second;
					used[cur_id][nxt_id] = true;
					res.push_back(cur);
					break;
				}
			}
			//if(!found)break;
			if (cur == st)break;
			if (!found)goto END2;
		}
		polys.push_back(res);
	END2:;
	}

}
vector<Polygon> trace(const Lines &ls) {
	//隣接点(偏角付き)のリストを作成
	map<point, vDoublePoint> aPoint;
	//頂点に番号を付けて辺の使用を記録
	map<point, int>p2id;
	map<int, point>id2p;
	int id = 0;

	REP(i, (int)ls.size()) {
		if (!p2id.count(ls[i][0])) {
			p2id[ls[i][0]] = id;
			id2p[id] = ls[i][0];
			id++;
		}
		if (!p2id.count(ls[i][1])) {
			p2id[ls[i][1]] = id;
			id2p[id] = ls[i][1];
			id++;
		}
		aPoint[ls[i][0]].push_back(doublePoint(arg(ls[i][1] - ls[i][0]), ls[i][1]));
		aPoint[ls[i][1]].push_back(doublePoint(arg(ls[i][0] - ls[i][1]), ls[i][0]));
	}
	vector<point>minPoints;
    REP(i,id)REP(j,id)used[i][j]=false;
	//x座標最小の点の計算と隣接点リストのソート
	point minPoint(INT_MAX, INT_MAX);
	adjPoint::iterator it;
	for (it = aPoint.begin(); it != aPoint.end(); ++it) {
		point p = it->first;
		minPoints.push_back(p);
		minPoint = min(minPoint, p);
		vDoublePoint &vdp = it->second;
		sort(vdp.begin(), vdp.end());
	}
	vector<Polygon>polys;
	make_polys(minPoint, polys, aPoint, p2id, id2p, id);
	return polys;
}
bool intersectCC(const Circle &a, const Circle &b) {
	double d = dist(a.c, b.c);
	return abs(a.r - b.r)<d + EPS && d - EPS<a.r + b.r;
}
bool unuse[101];
bool canConnect(point p, point q, const Circles &circles) {
	set<int> p_enclose = get_enclosing_circle(p, circles);
	set<int> q_enclose = get_enclosing_circle(q, circles);
	if (p_enclose.size()>2) {
		cout << p << endl;
		for (auto id : p_enclose) {
			cout << circles[id].c << " " << circles[id].r << endl;
		}
	}
	assert(p_enclose.size() <= 2);
	assert(q_enclose.size() <= 2);
	if (p_enclose != q_enclose) {
		return false;
	}
    REP(i,100)unuse[i]=false;
	for (auto id : p_enclose) {
		unuse[id] = true;
	}
	int n = circles.size();
	vector<Line>segments;
	REP(i, n) {
		if (unuse[i])continue;
		FOR(j, i, n) {
			if (i == j)continue;
			if (unuse[j])continue;
			if (intersectCC(circles[i], circles[j])) {
				segments.push_back(Line(circles[i].c, circles[j].c));
			}
		}
	}
	vLines cc = connectedComponent(segments);
	for (auto ls : cc) {
		if (ls.size() <= 2)continue;
		vector<Polygon>polys = trace(ls);
		for (auto poly : polys) {
			if (poly.contains(p) != poly.contains(q))return false;
		}
	}

	return true;
}

void solve() {
	int N, M; cin >> N >> M;
	//cerr << N << " " << M << endl;
	if (N == 0)exit(0);
	Circles circles;
	REP(i, N) {
		unuse[i] = false;
		double x, y, r; cin >> x >> y >> r;
		point c(x, y);
		circles.push_back({ c,r ,i });
	}
	while (M--) {
		double x1, y1, x2, y2; cin >> x1 >> y1 >> x2 >> y2;
		point p(x1, y1), q(x2, y2);
		if (canConnect(p, q, circles))cout << "YES";
		else cout << "NO";
		if (M)cout << " ";
		else cout << endl;
	}
}
signed main() {
	while (true)solve();
}
accuracy: 1
time_ms: 110
memory_kb: 3580
final_rank: 13

code_snippet: #include"bits/stdc++.h"
using namespace std;

#define REP(i,n) for(int i=0;i<(n);i++)
#define FOR(i,m,n) for(int i=(m);i<(n);i++)
#define REPR(i,n) for(int i=(n)-1;i>=0;i--)
const bool DEBUG = false;
const double EPS = 1e-8;
typedef complex<double>point;
point operator*(const point &p, const double &d) {
	return point(real(p)*d, imag(p)*d);
}
namespace std {
	bool operator<(const point &a, const point &b) {
		return real(a) != real(b) ? real(a)<real(b) : imag(a)<imag(b);
	}
}
double dist(const point &a, const point &b) {
	return abs(a - b);
}
struct Line :public vector<point> {
	Line(const point &a, const point &b) {
		push_back(a); push_back(b);
	}
};
typedef vector<Line> Lines;
double cross(const point &a, const point &b) {
	return real(a)*imag(b) - imag(a)*real(b);
}
double dot(const point &a, const point &b) {
	return real(a)*real(b) + imag(a)*imag(b);
}
int ccw(point a, point b, point c) {
	b = b - a, c = c - a;
	if (cross(b, c)>EPS)return +1;
	if (cross(b, c)<-EPS)return -1;
	if (dot(b, c)<-EPS)return +2;
	if (norm(b)<norm(c))return -2;
	return 0;
}

bool intersectSP(const Line &s, const point &p) {
	return abs(s[0] - p) + abs(s[1] - p) - abs(s[1] - s[0])<EPS;
}
bool isSamePoint(const point &p1, const point &p2) {
	return p1==p2||abs(p1 - p2)<=EPS;
}
bool intersectSS(const Line &s, const Line &t) {
	return ccw(s[0], s[1], t[0])*ccw(s[0], s[1], t[1]) <= 0 &&
		ccw(t[0], t[1], s[0])*ccw(t[0], t[1], s[1]) <= 0;
}
bool connectSS(const Line &s, const Line &t) {
	REP(i, 2)REP(j, 2) {
		if (isSamePoint(s[i], t[j]))return true;
		//if (s[i] == t[j])return true;
	}
	return false;
}
point crosspoint(const Line &l, const Line &m) {
	double A = cross(l[1] - l[0], m[1] - m[0]);
	double B = cross(l[1] - l[0], l[1] - m[0]);
	if (abs(A)<EPS&&abs(B)<EPS)return m[0];
	if (abs(A)<EPS)assert(false);
	return m[0] + B / A * (m[1] - m[0]);
}
struct Circle {
	point c; double r;
	int id;
	Circle(const point &c_, double r_, int id_) :c(c_), r(r_), id(id_) {}
	bool contains(const point &p) {
		if (dist(c, p)<r + EPS)return true;
		else return false;
	}
};

struct Polygon :public vector<point> {
	//0->OUT,1->ON,2->IN
	int contains(const point &p) {
		bool in = false;
		int N = this->size();
		for (int i = 0; i<N; i++) {
			point a = this->at(i) - p;
			point b = this->at((i + 1) % N) - p;
			if (imag(a)>imag(b))std::swap(a, b);
			if (imag(a) <= 0 && 0<imag(b))if (cross(a, b)<0)in = !in;
			if (cross(a, b) == 0 && dot(a, b) <= 0)return 1;
		}
		return in ? 2 : 0;
	}
	void print() {
		if (DEBUG) {
			cerr << "number of points:" << this->size() << endl;
            for(int i=0;i<(int)this->size();i++){
				cerr << i + 1 << ":" << this->at(i) << endl;
			}
		}

	}
};
typedef vector<Circle> Circles;
set<int> get_enclosing_circle(const point &p, const Circles &Cs) {
	set<int> ret;
	for (auto c : Cs) {
		if (c.contains(p))ret.insert(c.id);
	}
	return ret;
}

struct Equiv {
	vector<int>links;
	Equiv(int n) :links(n) {
		REP(i, n)links[i] = i;
	}
	int find(int x) {
		int xx = links[x];
		if (xx == x)return x;
		return links[x] = find(xx);
	}
	void unify(int x, int y) {
		int xx = find(x), yy = find(y);
		if (xx <= yy)links[yy] = xx;
		else links[xx] = yy;
	}
};
typedef vector<Lines> vLines;
vLines connectedComponent(const Lines &ls) {
	vLines ret;
	Equiv equiv(ls.size());
	REP(i, (int)ls.size()) {
		REP(j, i) {
			if (connectSS(ls[i], ls[j])) {
				equiv.unify(i, j);
			}
		}
	}
	REP(i, (int)ls.size()) {
		if (equiv.find(i) == i) {
			Lines newLs;
			newLs.push_back(ls[i]);
			FOR(j, i + 1, (int)ls.size()) {
				if (equiv.find(j) == i)newLs.push_back(ls[j]);
			}
			ret.push_back(newLs);
		}
	}
	return ret;
}
typedef pair<double, point>doublePoint;
typedef vector<doublePoint>vDoublePoint;
typedef map<point, vDoublePoint>adjPoint;
typedef vector<point>Points;

point findNext(const vDoublePoint &vdp, const point &p) {
	//隣接点のリスト(偏角ソート済み)から移動前の点を探してその次
	int n = vdp.size();
	REP(i, n) {
		if (p == vdp[i].second) {
			return vdp[(i + 1) % n].second;
		}
	}
	return vdp[0].second;
}
bool used[101][101];
void make_polys(point minPoint, vector<Polygon>&polys,adjPoint &aPoint,map<point, int>&p2id,
map<int, point>&id2p,int id) {
	Polygon ret;
	REP(i, id)REP(j, id)used[i][j] = false;
	ret.push_back(minPoint);
	point curP = aPoint[minPoint][0].second;
	used[p2id[minPoint]][p2id[curP]] = true;
	point lastP = minPoint;
	while (curP != minPoint) {
		ret.push_back(curP);
		point nextP = findNext(aPoint[curP], lastP);
		used[p2id[curP]][p2id[nextP]] = true;
		lastP = curP;
		curP = nextP;
	}
	//ret.push_back(minPoint);

	
	polys.push_back(ret);

    bool all_used=false;
	while (!all_used) {
        all_used=true;
		bool found = false;
		point st, nx;
        st={1e9,1e9};
		REP(i, id) {
            if(found)break;
			for (auto nxt : aPoint[id2p[i]]) {
				if (!used[i][p2id[nxt.second]]) {
                    all_used=false;
					found = true;
                    if(id2p[i]<st){
                        st=id2p[i];
                        nx=nxt.second;
                    }
				}
			}
		}
        if(all_used)break;
		//if (!found)break;
		Polygon res;
		res.push_back(st);
		res.push_back(nx);
		used[p2id[st]][p2id[nx]] = true;
		point cur = nx;
		point las = st;
		while (cur != st) {
			vDoublePoint v = aPoint[cur];
			int n = v.size();
			int s_idx = -1;
			bool found = false;
			REP(i, n) {
				if (isSamePoint(v[i].second,las)) {
					s_idx = i; break;
				}
			}
            if(s_idx<0){
                cerr<<cur<<endl;
                cerr<<las<<endl;
            }
            assert(s_idx>=0);
			for (int i = s_idx + 1; i < s_idx + n + 1; i++) {
				int idx = i % n;
				if (v[idx].second == cur)continue;
				int cur_id = p2id[cur];
				int nxt_id = p2id[v[idx].second];
				if (!used[cur_id][nxt_id]) {
					found = true;
					las = cur;
					cur = v[idx].second;
					used[cur_id][nxt_id] = true;
					res.push_back(cur);
					break;
				}
			}
			//if(!found)break;
			if (cur == st)break;
			if (!found)goto END2;
		}
		polys.push_back(res);
	END2:;
	}

}
vector<Polygon> trace(const Lines &ls) {
	//隣接点(偏角付き)のリストを作成
	map<point, vDoublePoint> aPoint;
	//頂点に番号を付けて辺の使用を記録
	map<point, int>p2id;
	map<int, point>id2p;
	int id = 0;

	REP(i, (int)ls.size()) {
		if (!p2id.count(ls[i][0])) {
			p2id[ls[i][0]] = id;
			id2p[id] = ls[i][0];
			id++;
		}
		if (!p2id.count(ls[i][1])) {
			p2id[ls[i][1]] = id;
			id2p[id] = ls[i][1];
			id++;
		}
		aPoint[ls[i][0]].push_back(doublePoint(arg(ls[i][1] - ls[i][0]), ls[i][1]));
		aPoint[ls[i][1]].push_back(doublePoint(arg(ls[i][0] - ls[i][1]), ls[i][0]));
	}
	vector<point>minPoints;
    REP(i,id)REP(j,id)used[i][j]=false;
	//vector<vector<bool>>used(id, vector<bool>(id));
	//REP(i,id)used[i][i]=true;
	//x座標最小の点の計算と隣接点リストのソート
	point minPoint(INT_MAX, INT_MAX);
	adjPoint::iterator it;
	for (it = aPoint.begin(); it != aPoint.end(); ++it) {
		point p = it->first;
		minPoints.push_back(p);
		minPoint = min(minPoint, p);
		vDoublePoint &vdp = it->second;
		sort(vdp.begin(), vdp.end());
	}
	vector<Polygon>polys;
	//for (auto minpoint : minPoints) {
	//	make_polys(minpoint, polys, aPoint, p2id, id2p, id);
	//}
	make_polys(minPoint, polys, aPoint, p2id, id2p, id);
	return polys;
}
bool intersectCC(const Circle &a, const Circle &b) {
	double d = dist(a.c, b.c);
	return abs(a.r - b.r)<d + EPS && d - EPS<a.r + b.r;
}
bool unuse[101];
bool canConnect(point p, point q, const Circles &circles) {
	set<int> p_enclose = get_enclosing_circle(p, circles);
	set<int> q_enclose = get_enclosing_circle(q, circles);
	if (p_enclose.size()>2) {
		cout << p << endl;
		for (auto id : p_enclose) {
			cout << circles[id].c << " " << circles[id].r << endl;
		}
	}
	assert(p_enclose.size() <= 2);
	assert(q_enclose.size() <= 2);
	if (p_enclose != q_enclose) {
		return false;
	}
    REP(i,100)unuse[i]=false;
	for (auto id : p_enclose) {
		unuse[id] = true;
	}
	int n = circles.size();
	vector<Line>segments;
	REP(i, n) {
		if (unuse[i])continue;
		FOR(j, i, n) {
			if (i == j)continue;
			if (unuse[j])continue;
			if (intersectCC(circles[i], circles[j])) {
				segments.push_back(Line(circles[i].c, circles[j].c));
			}
		}
	}
	vLines cc = connectedComponent(segments);
	for (auto ls : cc) {
		if (ls.size() <= 2)continue;
		vector<Polygon>polys = trace(ls);
		for (auto poly : polys) {
			// for (auto pp : poly) {
			// 	cout << pp << endl;
			// }cout << endl;
			if (poly.contains(p) != poly.contains(q))return false;
		}
	}

	return true;
}

void solve() {
	int N, M; cin >> N >> M;
	//cerr << N << " " << M << endl;
	if (N == 0)exit(0);
	Circles circles;
	REP(i, N) {
		unuse[i] = false;
		double x, y, r; cin >> x >> y >> r;
		point c(x, y);
		circles.push_back({ c,r ,i });
	}
	while (M--) {
		double x1, y1, x2, y2; cin >> x1 >> y1 >> x2 >> y2;
		point p(x1, y1), q(x2, y2);
		if (canConnect(p, q, circles))cout << "YES";
		else cout << "NO";
		if (M)cout << " ";
		else cout << endl;
	}
}
signed main() {
	while (true)solve();
}
accuracy: 1
time_ms: 110
memory_kb: 3584
final_rank: 14

code_snippet: #include<iostream>
#include<string>
#include<algorithm>
#include<vector>
#include<iomanip>
#include<math.h>
#include<complex>
#include<queue>
#include<deque>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<functional>
#include<assert.h>
#include<numeric>
using namespace std;
#define REP(i,m,n) for(int i=(int)(m) ; i < (int) (n) ; ++i )
#define rep(i,n) REP(i,0,n)
using ll = long long;
const int inf=1e9+7;
const ll longinf=1LL<<60 ;
const ll mod=1e9+7 ;

using ld = double;
using Point =  complex<ld>;
const ld eps = 1e-9;
const ld pi = acos(-1.0);

bool eq(ld a, ld b) {
	return (abs(a - b) < eps);
}

bool cmp(Point x,Point y){
	if(eq(x.real(),y.real()))return x.imag()<y.imag();
	return x.real()<y.real();
}

bool eqq(Point x,Point y){
	return eq(x.real(),y.real())&&eq(x.imag(),y.imag());
}
//内積
ld dot(Point a, Point b) {
	return real(conj(a)*b);
}
//外積
ld cross(Point a, Point b) {
	return imag(conj(a)*b);
}


//線分
//直線にするなら十分通い２点を端点とすればよい
class Line {
public:
	Point a, b;
};
//円
class Circle {
public:
	Point p;
	ld r;
};
//3点の位置関係
int ccw(Point a, Point b, Point c) {
	b -= a; c -= a;
	if (cross(b, c) > eps)return 1;//a,b,cが反時計回り
	if (cross(b, c) < -eps)return -1;//a,b,cが時計回り
	if (dot(b, c) < 0)return 2;//c,a,bの順に一直線
	if (norm(b) < norm(c))return -2;//a,b,cの順に一直線
	return 0;//a,c,bの順に一直線
}
//2直線の交差判定
bool isis_ll(Line l, Line m) {
	return !eq(cross(l.b - l.a, m.b - m.a), 0);
}
//直線と線分の交差判定
bool isis_ls(Line l, Line s) {
	return (cross(l.b - l.a, s.a - l.a)*cross(l.b - l.a, s.b - l.a) < eps);
}
//点が直線上に存在するか
bool isis_lp(Line l, Point p) {
	return (abs(cross(l.b - p, l.a - p)) < eps);
}
//点が線分上に存在するか
bool isis_sp(Line s, Point p) {
	return (abs(s.a - p) + abs(s.b - p) - abs(s.b - s.a) < eps);
}
//線分と線分の交差判定
bool isis_ss(Line s, Line t) {
	if (isis_sp(s, t.a) || isis_sp(s, t.b) || isis_sp(t, s.a) || isis_sp(t, s.b))return true;
	return(cross(s.b - s.a, t.a - s.a)*cross(s.b - s.a, t.b - s.a) < -eps && cross(t.b - t.a, s.a - t.a)*cross(t.b - t.a, s.b - t.a) < -eps);
}
//点から直線への垂線の足
Point proj(Line l, Point p) {
	ld t = dot(p - l.a, l.a - l.b) / norm(l.a - l.b);
	return l.a + t * (l.a - l.b);
}
//直線と直線の交点
//平行な２直線に対しては使うな！！！！
Point is_ll(Line s, Line t) {
	Point sv = s.b - s.a; Point tv = t.b - t.a;
	return s.a + sv * cross(tv, t.a - s.a) / cross(tv, sv);
}
//直線と点の距離
ld dist_lp(Line l, Point p) {
	return abs(p - proj(l, p));
}
//直線と直線の距離
ld dist_ll(Line l, Line m) {
	return isis_ll(l, m) ? 0 : dist_lp(l, m.a);
}
//線分と直線の距離
ld dist_ls(Line l, Line s) {
	return isis_ls(l, s) ? 0 : min(dist_lp(l, s.a), dist_lp(l, s.b));
}
//線分と点の距離
ld dist_sp(Line s, Point p) {
	Point r = proj(s, p);
	return isis_sp(s, r) ? abs(p - r) : min(abs(p - s.a), abs(p - s.b));
}
//線分と線分の距離
ld dist_ss(Line s, Line t) {
	if (isis_ss(s, t))return 0;
	return min({ dist_sp(s,t.a),dist_sp(s,t.b),dist_sp(t,s.a),dist_sp(t,s.b) });
}

bool included(Point p,vector<Point>& pol){
    int n=pol.size();
    ld area=0;
    rep(i,n){
        area+=arg((pol[i]-p)/(pol[(i+1)%n]-p));
    }
    return abs(area) > eps;
}

void solve(int n,int Q){
    Circle c[n];
    Point p[n];
    rep(i,n){
        double x,y,r;
        cin>>x>>y>>r;
        p[i]=Point(x,y);
        c[i]={p[i],r};
    }

    vector<int> in(n);
    rep(i,n)rep(j,n){
        if(i==j)continue;
        if(c[i].r>c[j].r+abs(c[i].p-c[j].p))in[j]=true;
    }
    vector<vector<int>> v(n);
    vector<vector<int>> used(n);
    rep(i,n){
        if(in[i])continue;
        rep(j,n){
            if(i==j||in[j])continue;
            if(c[i].r+c[j].r>abs(c[i].p-c[j].p)){
                v[i].push_back(j);
            }
        }
        used[i].resize(v[i].size());
        sort(v[i].begin(),v[i].end(),[&](int x,int y){
            return arg(c[x].p-c[i].p)<arg(c[y].p-c[i].p);
        });
    }
    vector<vector<Point>> pols;
    rep(i,n){
        rep(j,v[i].size()){
            if(used[i][j])continue;
            used[i][j]=true;
            vector<Point> pol;
            int pre=i, cur=v[i][j];
            pol.push_back(p[pre]);
            ld area=cross(p[cur],p[pre]);
            while(cur!=i){
                int nxt;
                rep(k,v[cur].size()){
                    if(v[cur][k]==pre){
                        used[cur][(k+1)%v[cur].size()]=true;
                        nxt=v[cur][(k+1)%v[cur].size()];
                        break;
                    }
                }
                pre=cur;cur=nxt;
                area+=cross(p[cur],p[pre]);
                pol.push_back(p[pre]);
            }
            if(area>eps)pols.push_back(pol);
        }
    }
    rep(aaa,Q){
        Point q[2];
        rep(i,2){
            int x,y;
            cin>>x>>y;
            q[i]=Point(x,y);
        }
        vector<int> inc[2];
        rep(i,2){
            rep(j,n){
                if(abs(q[i]-p[j])<c[j].r)inc[i].push_back(j);
            }
        }
        if(inc[0]!=inc[1]){
            cout<<"NO";
            if(aaa!=Q-1)cout<<" ";
            continue;
        }
        if(inc[0].size()&&inc[0]==inc[1]){
            cout<<"YES";
            if(aaa!=Q-1)cout<<" ";
            continue;
        }
        rep(i,2){
            rep(j,pols.size()){
                if(included(q[i],pols[j]))inc[i].push_back(j);
            }
        }
        if(inc[0]==inc[1])cout<<"YES";
        else cout<<"NO";
        if(aaa!=Q-1)cout<<" ";
    }
    cout<<endl;

}
int main(){
    int n,q;
    while(cin>>n>>q,n!=0)solve(n,q);
    return 0;
}
accuracy: 1
time_ms: 20
memory_kb: 3564
final_rank: 8

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define rep(i,n) for(int (i)=0;(i)<(int)(n);++(i))
#define all(x) (x).begin(),(x).end()
#define pb push_back
#define fi first
#define se second
#define dbg(x) cout<<#x" = "<<((x))<<endl
template<class T,class U> ostream& operator<<(ostream& o, const pair<T,U> &p){o<<"("<<p.fi<<","<<p.se<<")";return o;}
template<class T> ostream& operator<<(ostream& o, const vector<T> &v){o<<"[";for(T t:v){o<<t<<",";}o<<"]";return o;}

/* 基本要素 */
typedef complex<double> Point;
typedef pair<Point, Point> Line;
typedef vector<Point> VP;
const double PI = acos(-1);
const double EPS = 1e-8; // 許容誤差^2
const double INF = 1e9;
#define X real()
#define Y imag()
// #define LE(n,m) ((n) < (m) + EPS)
#define LE(n,m) ((n) - (m) < EPS)
// #define GE(n,m) ((n) + EPS > (m))
#define GE(n,m) (EPS > (m) - (n))
#define EQ(n,m) (abs((n)-(m)) < EPS)

// 頂点の順序（sortやmax_elementに必要）
namespace std {
  bool operator<(const Point a, const Point b) {
    return a.X != b.X ? a.X < b.X : a.Y < b.Y;
  }
}

// 内積　dot(a,b) = |a||b|cosθ
double dot(Point a, Point b) {
  return a.X*b.X + a.Y*b.Y;
}

// 外積　cross(a,b) = |a||b|sinθ
double cross(Point a, Point b) {
  return a.X*b.Y - a.Y*b.X;
}

// 点の進行方向
// !!! 誤差に注意 !!! (掛け算したものとかなり小さいものを比べているので)
int ccw(Point a, Point b, Point c) {
  b -= a;  c -= a;
  if (cross(b,c) >  EPS) return +1;  // counter clockwise
  if (cross(b,c) < -EPS) return -1;  // clockwise
  if (dot(b,c)   < -EPS) return +2;  // c--a--b on line
  if (norm(b) < norm(c)) return -2;  // a--b--c on line or a==b
  return 0;                          // a--c--b on line or a==c or b==c
}

// 多角形の内部判定
// 点が領域内部なら1、境界上なら2、外部なら0を返す
int inPolygon(const VP& ps, Point p) {
  int n = ps.size();
  bool in = false;
  rep (i, n) {
    Point a = ps[i] - p;
    Point b = ps[(i + 1) % n] - p;
    if (EQ(cross(a,b), 0) && LE(dot(a,b), 0)) return 2;
    if (a.Y > b.Y) swap(a,b);
    if ((a.Y*b.Y < 0 || (a.Y*b.Y < EPS && b.Y > EPS)) && LE(cross(a, b), 0)) in = !in;
  }
  return in;
}


Point READ(){
    int x,y;
    cin >>x >>y;
    return Point(x,y);
}

int main(){
    int n,m;
    while(cin >>n >>m,n){
        VP c(n);
        vector<double> r(n);
        rep(i,n){
            c[i] = READ();
            cin >>r[i];
        }

        auto isecCC = [&](int a, int b){
            if(r[a] > r[b]) swap(a,b);
            double d = abs(c[a] - c[b]);

            if( r[a]+r[b] < d+EPS ) return false;
            if( d+r[a] < r[b]+EPS ) return false;
            return true;
        };

        using D = pair<double,int>;
        vector<vector<D>> g(n);
        rep(i,n){
            rep(j,n)if(j!=i && isecCC(i,j)){
                Point pp = c[j] - c[i];
                double theta = atan2(pp.Y,pp.X);
                g[i].pb({theta,j});
            }
            sort(all(g[i]));
            // dbg(g[i]);
        }

        // 領域
        vector<VP> pls;

        // (円i, 次に向かう方向)
        vector<vector<bool>> vis(n);
        rep(i,n) vis[i] = vector<bool>(g[i].size());

        using pi = pair<int,int>;
        rep(i,n){
            int GG = g[i].size();
            if(GG==0) continue;
            rep(ii,GG){
                if(vis[i][ii]) continue;

                queue<pi> que;
                stack<int> c_idx;
                vector<bool> in(n);

                in[i] = true;
                c_idx.push(i);
                vis[i][ii] = true;
                que.push({i,ii});
                while(!que.empty()){
                    pi cur = que.front();
                    que.pop();

                    // dbg(cur);

                    int idx = cur.fi;
                    int dir = cur.se;

                    int nidx = g[idx][dir].se;
                    int ndir = -1;
                    int sz = g[nidx].size();
                    rep(j,sz){
                        if(g[nidx][j].se == idx){
                            ndir = (j+1)%sz;
                            break;
                        }
                    }

                    assert(ndir >= 0);
                    if(!vis[nidx][ndir]){
                        if(in[nidx]){
                            VP poly;
                            while(in[nidx]){
                                assert(!c_idx.empty());
                                int st_idx = c_idx.top();
                                c_idx.pop();

                                in[st_idx] = false;
                                poly.pb(c[st_idx]);
                            }

                            pls.pb(poly);
                        }

                        in[nidx] = true;
                        c_idx.push(nidx);
                        vis[nidx][ndir] = true;
                        que.push({nidx,ndir});
                    }
                }

                VP poly;
                while(!c_idx.empty()){
                    int st_idx = c_idx.top();
                    c_idx.pop();

                    in[st_idx] = false;
                    poly.pb(c[st_idx]);
                }
                pls.pb(poly);
            }
        }

        // for(VP poly:pls) dbg(poly);

        auto IN = [&](int idx, Point p){
            double d = abs(p - c[idx]);
            return d < r[idx];
        };

        vector<bool> ans(m,true);
        rep(i,m){
            Point start = READ(), goal = READ();
            bool flg = false;
            rep(j,n){
                if(IN(j,start) || IN(j,goal)) flg = true;
                if( IN(j,start) != IN(j,goal) ) ans[i] = false;
            }

            if(!flg){
                for(VP poly:pls)if(poly.size()>=3){
                    int vs = inPolygon(poly, start);
                    int vg = inPolygon(poly, goal);
                    if( vs != vg ) ans[i] = false;
                }
            }
        }

        rep(i,m){
            if(i) cout << " ";
            cout << (ans[i]?"YES":"NO");
        }
        cout << "\n";
    }
    return 0;
}
accuracy: 1
time_ms: 10
memory_kb: 3492
final_rank: 4

code_snippet: #include "bits/stdc++.h"
#include<unordered_map>
#include<unordered_set>
#pragma warning(disable:4996)
using namespace std;
using ld = long double;
const ld eps = 1e-9;


/* 幾何の基本 */

#include <complex>

typedef complex<ld> Point;


const ld pi = acos(-1.0);
const ld dtop = pi / 180.;
const ld ptod = 1. / dtop;

namespace std {
	bool operator<(const Point &lhs, const Point &rhs) {
		if (lhs.real() < rhs.real() - eps) return true;
		if (lhs.real() > rhs.real() + eps) return false;
		return lhs.imag() < rhs.imag();
	}
}

// 点の入力
Point input_Point() {
	ld x, y;
	cin >> x >> y;
	return Point(x, y);
}

// 誤差つき等号判定
bool eq(const ld a, const ld b) {
	return (abs(a - b) < eps);
}

// 内積
ld dot(const Point& a, const Point& b) {
	return real(conj(a) * b);
}

// 外積
ld cross(const Point& a, const Point& b) {
	return imag(conj(a) * b);
}

// 直線の定義
class Line {
public:
	Point a, b;
	Line() : a(Point(0, 0)), b(Point(0, 0)) {}
	Line(Point a, Point b) : a(a), b(b) {}
	Point operator[](const int _num)const {
		if (_num == 0)return a;
		else if (_num == 1)return b;
		else {
			assert(false);
			return Point();
		}
	}
};

// 円の定義
class Circle {
public:
	Point p;
	ld r;
	Circle() : p(Point(0, 0)), r(0) {}
	Circle(Point p, ld r) : p(p), r(r) {}
};

// ccw
// 1: a,b,cが反時計周りの順に並ぶ
//-1: a,b,cが時計周りの順に並ぶ
// 2: c,a,bの順に直線に並ぶ
//-2: a,b,cの順に直線に並ぶ
// 0: a,c,bの順に直線に並ぶ
int ccw(const Point& a, const Point &b, const Point &c) {
	const Point nb(b - a);
	const Point nc(c - a);
	if (cross(nb, nc) > eps) return 1;   // a,b,cが反時計周りの順に並ぶ
	if (cross(nb, nc) < -eps) return -1; // a,b,cが時計周りの順に並ぶ
	if (dot(nb, nc) < 0) return 2;       // c,a,bの順に直線に並ぶ
	if (norm(nb) < norm(nc)) return -2;  // a,b,cの順に直線に並ぶ
	return 0;                          // a,c,bの順に直線に並ぶ
}


/* 交差判定 */

// 直線と直線の交差判定
bool isis_ll(const Line& l, const Line& m) {
	return !eq(cross(l.b - l.a, m.b - m.a), 0);
}

// 直線と線分の交差判定
bool isis_ls(const Line& l, const Line& s) {
	return isis_ll(l, s) &&
		(cross(l.b - l.a, s.a - l.a) * cross(l.b - l.a, s.b - l.a) < eps);
}

// 線分と線分の交差判定
bool isis_ss(const Line& s, const Line& t) {
	return ccw(s.a, s.b, t.a) * ccw(s.a, s.b, t.b) <= 0 &&
		ccw(t.a, t.b, s.a) * ccw(t.a, t.b, s.b) <= 0;
}

// 点の直線上判定
bool isis_lp(const Line& l, const Point& p) {
	return (abs(cross(l.b - p, l.a - p)) < eps);
}

// 点の線分上判定
bool isis_sp(const Line& s, const Point& p) {
	return (abs(s.a - p) + abs(s.b - p) - abs(s.b - s.a) < eps);
}

// 垂線の足
Point proj(const Line &l, const Point& p) {
	ld t = dot(p - l.a, l.b - l.a) / norm(l.a - l.b);
	return l.a + t * (l.b - l.a);
}

//線対象の位置にある点
Point reflect(const Line &l, const Point &p) {
	Point pr = proj(l, p);
	return pr * 2.l - p;
}

// 直線と直線の交点
Point is_ll(const Line &s, const Line& t) {
	Point sv = s.b - s.a, tv = t.b - t.a;
	assert(cross(sv, tv) != 0);
	return s.a + sv * cross(tv, t.a - s.a) / cross(tv, sv);
}
// 直線と直線の交点
vector<Point> is_ll2(const Line &s, const Line& t) {
	Point sv = s.b - s.a, tv = t.b - t.a;
	if (cross(sv, tv) != 0)return vector<Point>(1, is_ll(s, t));
	else {
		vector<Point>ans;
		for (int k = 0; k < 2; ++k) {
			if (isis_sp(s, t[k]) && find(ans.begin(), ans.end(), t[k]) == ans.end())ans.push_back(t[k]);
			if (isis_sp(t, s[k]) && find(ans.begin(), ans.end(), s[k]) == ans.end())ans.push_back(s[k]);
		}
		return ans;
	}
}
// 線分と線分の交点
//　重なってる部分あるとassert(false)
Point is_ss(const Line &s, const Line& t) {
	if (isis_ss(s, t)) {
		for (int k = 0; k < 2; ++k) {
			for (int l = 0; l < 2; ++l) {
				if (s[k] == t[l])return s[k];
			}
		}
		return is_ll(s, t);
	}
	else {
		//先にisis_ssしてね
		assert(false);
		return Point(0, 0);
	}
}
// 線分と線分の交点
vector<Point> is_ss2(const Line &s, const Line& t) {
	vector<Point> kouho(is_ll2(s, t));
	vector<Point>ans;
	for (auto p : kouho) {
		if (isis_sp(s, p) && isis_sp(t, p))ans.emplace_back(p);
	}
	return ans;
}
// 直線と点の距離
ld dist_lp(const Line& l, const Point& p) {
	return abs(p - proj(l, p));
}

//直線と直線の距離
ld dist_ll(const Line& l, const Line& m) {
	return isis_ll(l, m) ? 0 : dist_lp(l, m.a);
}

// 直線と線分の距離
ld dist_ls(const Line& l, const Line& s) {
	return isis_ls(l, s) ? 0 : min(dist_lp(l, s.a), dist_lp(l, s.b));
}

// 線分と点の距離
ld dist_sp(const Line& s, const Point& p) {
	Point r = proj(s, p);
	return isis_sp(s, r) ? abs(r - p) : min(abs(s.a - p), abs(s.b - p));
}

// 線分と線分の距離
ld dist_ss(const Line& s, const Line& t) {
	if (isis_ss(s, t)) return 0;
	return min({ dist_sp(s, t.a), dist_sp(s, t.b), dist_sp(t, s.a), dist_sp(t, s.b) });
}


//直線と直線の二等分線のベクトル
Line line_bisection(const Line &s, const Line &t) {
	const Point laglanju(is_ll(s, t));
	const Point avec = !(abs(laglanju - s[0])<eps) ? s[0] - laglanju : s[1] - laglanju;
	const Point bvec = !(abs(laglanju - t[0])<eps) ? t[0] - laglanju : t[1] - laglanju;

	return Line(laglanju, laglanju + (abs(bvec)*avec + abs(avec)*bvec) / (abs(avec) + abs(bvec)));
}
//点と点の垂直二等分線　aを左に見ながら
Line point_bisection(const Point&a, const Point&b) {
	const Point cen((a + b) / 2.l);
	const Point vec = (b - a)*Point(0, 1);
	return Line(cen, cen + vec);
}

//三つの点からなる外心
Point outer_center(const vector<Point>&ps) {
	assert(ps.size() == 3);
	Line l1 = point_bisection(ps[0], ps[1]);
	Line l2 = point_bisection(ps[1], ps[2]);
	return is_ll(l1, l2);
}


//三つの直線からなる内心
//三つの直線が並行でないことは確かめといてね
Point  inner_center(const vector<Line>&ls) {
	vector<Point>vertics;
	for (int i = 0; i <static_cast<int>(ls.size()); ++i) {
		vertics.push_back(is_ll(ls[i], ls[(i + 1) % 3]));
	}
	if (vertics[0] == vertics[1] || vertics[1] == vertics[2] || vertics[2] == vertics[0])return vertics[0];
	Line bi1(line_bisection(Line(vertics[0], vertics[1]), Line(vertics[0], vertics[2])));
	Line bi2(line_bisection(Line(vertics[1], vertics[2]), Line(vertics[1], vertics[0])));
	if (bi1[0] == bi2[0])return bi1[0];
	else {
		return is_ll(bi1, bi2);
	}
}

//三つの直線からなる傍心
//三つの直線が並行でないことは確かめといてね
vector<Point>  ex_center(const vector<Line>&ls) {
	vector<Point>vertics;
	for (int i = 0; i < static_cast<int>(ls.size()); ++i) {
		vertics.push_back(is_ll(ls[i], ls[(i + 1) % 3]));
	}
	if (abs(vertics[0] - vertics[1])<eps || abs(vertics[1] - vertics[2])<eps || (abs(vertics[2] - vertics[0])<eps))return vector<Point>();
	vector<Point>ecs;
	for (int i = 0; i < 3; ++i) {
		Line bi1(line_bisection(Line(vertics[i], vertics[i] * 2.0l - vertics[(i + 2) % 3]), Line(vertics[i], vertics[(i + 1) % 3])));
		Line bi2(line_bisection(Line(vertics[(i + 1) % 3], vertics[(i + 1) % 3] * 2.0l - vertics[(i + 2) % 3]), Line(vertics[(i + 1) % 3], vertics[i])));
		ecs.push_back(is_ll(bi1, bi2));
	}
	return ecs;
}


//a,b:並行
//c:並行でない
//三つの直線から同距離の位置を求める。
vector<Point>  same_dis(const vector<Line>&ls) {
	vector<Point>vertics;
	vertics.push_back(is_ll(ls[0], ls[2]));
	vertics.push_back(is_ll(ls[1], ls[2]));

	if (abs(vertics[0] - vertics[1]) < eps)return vector<Point>{vertics[0]};
	Line bis(line_bisection(ls[0], ls[1]));
	vector<Point>ecs;

	Line abi(line_bisection(Line(vertics[0], vertics[1]), ls[0]));
	ecs.push_back(is_ll(bis, abi));


	Line bbi(line_bisection(Line(vertics[0], 2.l*vertics[0] - vertics[1]), ls[0]));
	ecs.push_back(is_ll(bis, bbi));

	return ecs;
}
/* 円 */

// 円と円の交点
vector<Point> is_cc(const Circle& c1, const Circle& c2) {
	vector<Point> res;
	ld d = abs(c1.p - c2.p);
	ld rc = (d * d + c1.r * c1.r - c2.r * c2.r) / (2 * d);
	ld dfr = c1.r * c1.r - rc * rc;
	if (abs(dfr) < eps) dfr = 0.0;
	else if (dfr < 0.0) return res; // no intersection
	ld rs = sqrt(dfr);
	Point diff = (c2.p - c1.p) / d;
	res.push_back(c1.p + diff * Point(rc, rs));
	if (dfr != 0.0) res.push_back(c1.p + diff * Point(rc, -rs));
	return res;
}

/*
点が円の中にいるか
0 => out
1 => on
2 => in*/
int is_in_Circle(const  Point& p, const Circle &cir) {
	ld dis = abs(cir.p - p);
	if (dis > cir.r + eps)return 0;
	else if (dis < cir.r - eps)return 2;
	else return 1;
}
int is_in_Circle(const Circle &cir, const  Point& p) {
	ld dis = abs(cir.p - p);
	if (dis > cir.r + eps)return 0;
	else if (dis < cir.r - eps)return 2;
	else return 1;
}
/*
円lcが円rcの中にいるか
0 => out
1 => on
2 => in*/
int Circle_in_Circle(const Circle &lc, const  Circle&rc) {
	ld dis = abs(lc.p - rc.p);
	if (dis < rc.r - lc.r - eps)return 2;
	else if (dis>rc.r - lc.r + eps)return 0;
	else return 1;
}

// 円と直線の交点
vector<Point> is_lc(const Circle& c, const Line& l) {
	vector<Point> res;
	ld d = dist_lp(l, c.p);
	if (d < c.r + eps) {
		ld len = (d > c.r) ? 0.0 : sqrt(c.r * c.r - d * d); //safety;
		Point nor = (l.a - l.b) / abs(l.a - l.b);
		res.push_back(proj(l, c.p) + len * nor);
		res.push_back(proj(l, c.p) - len * nor);
	}
	return res;
}

// 円と線分の距離
vector<Point> is_sc(const Circle& c, const Line& l) {
	vector<Point> v = is_lc(c, l), res;
	for (Point p : v)
		if (isis_sp(l, p)) res.push_back(p);
	return res;
}

// 円と点の接線
vector<Line> tangent_cp(const Circle& c, const Point& p) {
	vector<Line> ret;
	Point v = c.p - p;
	ld d = abs(v);
	ld l = sqrt(norm(v) - c.r * c.r);
	if (isnan(l)) { return ret; }
	Point v1 = v * Point(l / d, c.r / d);
	Point v2 = v * Point(l / d, -c.r / d);
	ret.push_back(Line(p, p + v1));
	if (l < eps) return ret;
	ret.push_back(Line(p, p + v2));
	return ret;
}

// 円と円の接線
vector<Line> tangent_cc(const Circle& c1, const Circle& c2) {
	vector<Line> ret;
	if (abs(c1.p - c2.p) - (c1.r + c2.r) > -eps) {
		Point center = (c1.p * c2.r + c2.p * c1.r) / (c1.r + c2.r);
		ret = tangent_cp(c1, center);
	}
	if (abs(c1.r - c2.r) > eps) {
		Point out = (-c1.p * c2.r + c2.p * c1.r) / (c1.r - c2.r);
		vector<Line> nret = tangent_cp(c1, out);
		ret.insert(ret.end(), nret.begin(), nret.end());
	}
	else {
		Point v = c2.p - c1.p;
		v /= abs(v);
		Point q1 = c1.p + v * Point(0, 1) * c1.r;
		Point q2 = c1.p + v * Point(0, -1) * c1.r;
		ret.push_back(Line(q1, q1 + v));
		ret.push_back(Line(q2, q2 + v));
	}
	return ret;
}
//二つの円の重なり面積
ld two_Circle_area(const Circle&l, const Circle&r) {
	ld dis = abs(l.p - r.p);
	if (dis > l.r + r.r)return 0;
	else if (dis + r.r < l.r) {
		return r.r*r.r*pi;
	}
	else if (dis + l.r < r.r) {
		return l.r*l.r*pi;
	}
	else {
		ld ans = (l.r)*(l.r)*acos((dis*dis + l.r*l.r - r.r*r.r) / (2 * dis*l.r)) +
			(r.r)*(r.r)*acos((dis*dis + r.r*r.r - l.r*l.r) / (2 * dis*r.r)) -
			sqrt(4 * dis*dis*l.r*l.r - (dis*dis + l.r*l.r - r.r*r.r)*(dis*dis + l.r*l.r - r.r*r.r)) / 2;
		return ans;
	}

}

/* 多角形 */

typedef vector<Point> Polygon;


// 面積
ld get_area(const Polygon &p) {
	ld res = 0;
	int n = p.size();
	for (int j = 0; j<n; ++j) res += cross(p[j], p[(j + 1) % n]);
	return res / 2;
}

// 円の内外判定
/*0 => out
1 => on
2 => in*/
int convex_contains(const Polygon &poly, const  Point& p) {
	ld angle = 0;
	int n = poly.size();
	for(int i=0;i<n;++i) {
		Point a = poly[i], b = poly[(i + 1) % n];
		if (isis_sp(Line(a, b), p)) return 1;
		angle += arg((b - p) / (a - p));
	}
	return eq(angle, 0) ? 0 : 2;
}


struct Edge {
	int src;
	int dest;
};
using Graph=vector<vector<Edge>>;

//線分アレンジメント後に、
//閉路の面積の和を求める。
void dfs(set<pair<int,int>>&real_set,set<pair<int ,int>>&aset,const int now, const int from, vector<int>&prevs, vector<Polygon>&ans_polys, const vector<Point>&ps, const Graph&g) {
	vector<pair<ld, int>>next_points;

	Point from_p = from == -1 ? ps[now] + Point(-3e6, 1) : ps[from];
	Point now_p = ps[now];
	for (auto e : g[now]) {
		if (e.dest == from)continue;
		Point next_p = ps[e.dest];

		ld costheta = dot(from_p - now_p, next_p - now_p) / (abs(from_p - now_p)*abs(next_p - now_p));
		costheta *= 0.3;
		ld d = acos(costheta);
		if (ccw(from_p, now_p, next_p) != 1) {
			d = 2 * pi - d;
		}
		next_points.push_back(make_pair(d, e.dest));
	}
	sort(next_points.begin(), next_points.end());

	for (auto n_p : next_points) {
		if (aset.find(make_pair(now, n_p.second))!=aset.end()) {
			continue;
		}
		else {
			if (real_set.find(make_pair(now, n_p.second)) != real_set.end()) {
				continue;
			}
			aset.emplace(now,n_p.second);
		}

		if (prevs[n_p.second] != -1) {
			int k=now;
			Polygon poly(1,ps[k]);
			real_set.emplace(k,n_p.second);
			while (k != n_p.second) {
				int a = k;
				k=prevs[k];
				real_set.emplace(k,a);
				poly.push_back(ps[k]);
			}
			
			ld plus = get_area(poly);
			if (plus<eps) {
				reverse(poly.begin(),poly.end());
				ans_polys.push_back(poly);
			}
			throw(false);
		}
		else {

			prevs[n_p.second] = now;
			dfs(real_set,aset, n_p.second, now, prevs, ans_polys, ps, g);
			prevs[n_p.second] = -1;
		}

	}
	return ;
}

int main() {
	int t=0;
	while (true) {
		int N,M;cin>>N>>M;
		t++;

		if(!N)break;
		vector<Circle>cs;
		for (int i = 0; i < N; ++i) {
			int x,y,r;cin>>x>>y>>r;
			cs.push_back(Circle(Point(x,y),r));
		}
		vector<vector<Edge>>edges(N);
		Graph g(N);
		int sum=0;
		for (int i = 0; i < N; ++i) {
			for (int j = i + 1; j < N; ++j) {
				
				ld dis=abs(cs[i].p-cs[j].p);
				if (Circle_in_Circle(cs[i], cs[j])<=1&&Circle_in_Circle(cs[j],cs[i])<=1&&dis<cs[i].r+cs[j].r) {
					edges[i].push_back(Edge{ i,j });
					edges[j].push_back(Edge{ j,i });
					sum++;
				}
			}
		}
		vector<Point>ps(N);
		for(int i=0;i<N;++i)ps[i]=cs[i].p;

		vector<Polygon>polys;
		set<pair<int,int>>aset;

		for(int i=0;i<N;++i) {
			for (int j = 0; j < edges[i].size();++j) {
				if (aset.find(make_pair(i, j)) == aset.end()) {
					try {
						vector<int>prevs(N, -1);
						prevs[i] = -2;
						set<pair<int,int>>nset;
						dfs(aset,nset, i, -1, prevs, polys, ps, edges);
					}
					catch (...) {
					}
				}
			}
		}

		for (int i = 0; i < M; ++i) {
			vector<Point>query_points;
			{
				int a,b,c,d;cin>>a>>b>>c>>d;
				Point p,q;
				p=Point(a,b);
				q=Point(c,d);
				query_points = vector<Point>{ p,q };
			}
			vector<vector<int>>in_circles(2);
			for (int j = 0; j < 2; ++j) {
				for (int k = 0; k < N; ++k) {
					if (is_in_Circle(cs[k], query_points[j])) {
						in_circles[j].push_back(true);
					}
					else {
						in_circles[j].push_back(false);
					}
				}
			}
			bool ok;
			if (in_circles[0] != in_circles[1]) {
				ok=false;
			}
			else {
				if (all_of(in_circles[0].begin(), in_circles[0].end(), [](const int a) {return a == 0; })) {
					vector<vector<int>>in_polygons(2,vector<int>(polys.size()));
					for (int j = 0; j < 2; ++j) {
						for (int k = 0; k < polys.size(); ++k) {
							if (convex_contains(polys[k], query_points[j])) {
								in_polygons[j][k] = true;
							}
						}
					}
					if (in_polygons[0] == in_polygons[1]) {
						ok=true;
					}
					else {
						ok=false;
					}
				}
				else {
					ok=true;
				}

			}
			if(ok)cout<<"YES";
			else cout<<"NO";
			if(i==M-1)cout<<endl;
			else cout<<" ";
			
		}
	}
	return 0;
}
accuracy: 1
time_ms: 60
memory_kb: 3280
final_rank: 1

code_snippet: #include "bits/stdc++.h"
#include<unordered_map>
#include<unordered_set>
#pragma warning(disable:4996)
using namespace std;
using ld = long double;
const ld eps = 1e-9;


/* 幾何の基本 */

#include <complex>

typedef complex<ld> Point;


const ld pi = acos(-1.0);
const ld dtop = pi / 180.;
const ld ptod = 1. / dtop;

namespace std {
	bool operator<(const Point &lhs, const Point &rhs) {
		if (lhs.real() < rhs.real() - eps) return true;
		if (lhs.real() > rhs.real() + eps) return false;
		return lhs.imag() < rhs.imag();
	}
}

// 点の入力
Point input_Point() {
	ld x, y;
	cin >> x >> y;
	return Point(x, y);
}

// 誤差つき等号判定
bool eq(const ld a, const ld b) {
	return (abs(a - b) < eps);
}

// 内積
ld dot(const Point& a, const Point& b) {
	return real(conj(a) * b);
}

// 外積
ld cross(const Point& a, const Point& b) {
	return imag(conj(a) * b);
}

// 直線の定義
class Line {
public:
	Point a, b;
	Line() : a(Point(0, 0)), b(Point(0, 0)) {}
	Line(Point a, Point b) : a(a), b(b) {}
	Point operator[](const int _num)const {
		if (_num == 0)return a;
		else if (_num == 1)return b;
		else {
			assert(false);
			return Point();
		}
	}
};

// 円の定義
class Circle {
public:
	Point p;
	ld r;
	Circle() : p(Point(0, 0)), r(0) {}
	Circle(Point p, ld r) : p(p), r(r) {}
};

// ccw
// 1: a,b,cが反時計周りの順に並ぶ
//-1: a,b,cが時計周りの順に並ぶ
// 2: c,a,bの順に直線に並ぶ
//-2: a,b,cの順に直線に並ぶ
// 0: a,c,bの順に直線に並ぶ
int ccw(const Point& a, const Point &b, const Point &c) {
	const Point nb(b - a);
	const Point nc(c - a);
	if (cross(nb, nc) > eps) return 1;   // a,b,cが反時計周りの順に並ぶ
	if (cross(nb, nc) < -eps) return -1; // a,b,cが時計周りの順に並ぶ
	if (dot(nb, nc) < 0) return 2;       // c,a,bの順に直線に並ぶ
	if (norm(nb) < norm(nc)) return -2;  // a,b,cの順に直線に並ぶ
	return 0;                          // a,c,bの順に直線に並ぶ
}


/* 交差判定 */

// 直線と直線の交差判定
bool isis_ll(const Line& l, const Line& m) {
	return !eq(cross(l.b - l.a, m.b - m.a), 0);
}

// 直線と線分の交差判定
bool isis_ls(const Line& l, const Line& s) {
	return isis_ll(l, s) &&
		(cross(l.b - l.a, s.a - l.a) * cross(l.b - l.a, s.b - l.a) < eps);
}

// 線分と線分の交差判定
bool isis_ss(const Line& s, const Line& t) {
	return ccw(s.a, s.b, t.a) * ccw(s.a, s.b, t.b) <= 0 &&
		ccw(t.a, t.b, s.a) * ccw(t.a, t.b, s.b) <= 0;
}

// 点の直線上判定
bool isis_lp(const Line& l, const Point& p) {
	return (abs(cross(l.b - p, l.a - p)) < eps);
}

// 点の線分上判定
bool isis_sp(const Line& s, const Point& p) {
	return (abs(s.a - p) + abs(s.b - p) - abs(s.b - s.a) < eps);
}

// 垂線の足
Point proj(const Line &l, const Point& p) {
	ld t = dot(p - l.a, l.b - l.a) / norm(l.a - l.b);
	return l.a + t * (l.b - l.a);
}

//線対象の位置にある点
Point reflect(const Line &l, const Point &p) {
	Point pr = proj(l, p);
	return pr * 2.l - p;
}

// 直線と直線の交点
Point is_ll(const Line &s, const Line& t) {
	Point sv = s.b - s.a, tv = t.b - t.a;
	assert(cross(sv, tv) != 0);
	return s.a + sv * cross(tv, t.a - s.a) / cross(tv, sv);
}
// 直線と直線の交点
vector<Point> is_ll2(const Line &s, const Line& t) {
	Point sv = s.b - s.a, tv = t.b - t.a;
	if (cross(sv, tv) != 0)return vector<Point>(1, is_ll(s, t));
	else {
		vector<Point>ans;
		for (int k = 0; k < 2; ++k) {
			if (isis_sp(s, t[k]) && find(ans.begin(), ans.end(), t[k]) == ans.end())ans.push_back(t[k]);
			if (isis_sp(t, s[k]) && find(ans.begin(), ans.end(), s[k]) == ans.end())ans.push_back(s[k]);
		}
		return ans;
	}
}
// 線分と線分の交点
//　重なってる部分あるとassert(false)
Point is_ss(const Line &s, const Line& t) {
	if (isis_ss(s, t)) {
		for (int k = 0; k < 2; ++k) {
			for (int l = 0; l < 2; ++l) {
				if (s[k] == t[l])return s[k];
			}
		}
		return is_ll(s, t);
	}
	else {
		//先にisis_ssしてね
		assert(false);
		return Point(0, 0);
	}
}
// 線分と線分の交点
vector<Point> is_ss2(const Line &s, const Line& t) {
	vector<Point> kouho(is_ll2(s, t));
	vector<Point>ans;
	for (auto p : kouho) {
		if (isis_sp(s, p) && isis_sp(t, p))ans.emplace_back(p);
	}
	return ans;
}
// 直線と点の距離
ld dist_lp(const Line& l, const Point& p) {
	return abs(p - proj(l, p));
}

//直線と直線の距離
ld dist_ll(const Line& l, const Line& m) {
	return isis_ll(l, m) ? 0 : dist_lp(l, m.a);
}

// 直線と線分の距離
ld dist_ls(const Line& l, const Line& s) {
	return isis_ls(l, s) ? 0 : min(dist_lp(l, s.a), dist_lp(l, s.b));
}

// 線分と点の距離
ld dist_sp(const Line& s, const Point& p) {
	Point r = proj(s, p);
	return isis_sp(s, r) ? abs(r - p) : min(abs(s.a - p), abs(s.b - p));
}

// 線分と線分の距離
ld dist_ss(const Line& s, const Line& t) {
	if (isis_ss(s, t)) return 0;
	return min({ dist_sp(s, t.a), dist_sp(s, t.b), dist_sp(t, s.a), dist_sp(t, s.b) });
}


//直線と直線の二等分線のベクトル
Line line_bisection(const Line &s, const Line &t) {
	const Point laglanju(is_ll(s, t));
	const Point avec = !(abs(laglanju - s[0])<eps) ? s[0] - laglanju : s[1] - laglanju;
	const Point bvec = !(abs(laglanju - t[0])<eps) ? t[0] - laglanju : t[1] - laglanju;

	return Line(laglanju, laglanju + (abs(bvec)*avec + abs(avec)*bvec) / (abs(avec) + abs(bvec)));
}
//点と点の垂直二等分線　aを左に見ながら
Line point_bisection(const Point&a, const Point&b) {
	const Point cen((a + b) / 2.l);
	const Point vec = (b - a)*Point(0, 1);
	return Line(cen, cen + vec);
}

//三つの点からなる外心
Point outer_center(const vector<Point>&ps) {
	assert(ps.size() == 3);
	Line l1 = point_bisection(ps[0], ps[1]);
	Line l2 = point_bisection(ps[1], ps[2]);
	return is_ll(l1, l2);
}


//三つの直線からなる内心
//三つの直線が並行でないことは確かめといてね
Point  inner_center(const vector<Line>&ls) {
	vector<Point>vertics;
	for (int i = 0; i <static_cast<int>(ls.size()); ++i) {
		vertics.push_back(is_ll(ls[i], ls[(i + 1) % 3]));
	}
	if (vertics[0] == vertics[1] || vertics[1] == vertics[2] || vertics[2] == vertics[0])return vertics[0];
	Line bi1(line_bisection(Line(vertics[0], vertics[1]), Line(vertics[0], vertics[2])));
	Line bi2(line_bisection(Line(vertics[1], vertics[2]), Line(vertics[1], vertics[0])));
	if (bi1[0] == bi2[0])return bi1[0];
	else {
		return is_ll(bi1, bi2);
	}
}

//三つの直線からなる傍心
//三つの直線が並行でないことは確かめといてね
vector<Point>  ex_center(const vector<Line>&ls) {
	vector<Point>vertics;
	for (int i = 0; i < static_cast<int>(ls.size()); ++i) {
		vertics.push_back(is_ll(ls[i], ls[(i + 1) % 3]));
	}
	if (abs(vertics[0] - vertics[1])<eps || abs(vertics[1] - vertics[2])<eps || (abs(vertics[2] - vertics[0])<eps))return vector<Point>();
	vector<Point>ecs;
	for (int i = 0; i < 3; ++i) {
		Line bi1(line_bisection(Line(vertics[i], vertics[i] * 2.0l - vertics[(i + 2) % 3]), Line(vertics[i], vertics[(i + 1) % 3])));
		Line bi2(line_bisection(Line(vertics[(i + 1) % 3], vertics[(i + 1) % 3] * 2.0l - vertics[(i + 2) % 3]), Line(vertics[(i + 1) % 3], vertics[i])));
		ecs.push_back(is_ll(bi1, bi2));
	}
	return ecs;
}


//a,b:並行
//c:並行でない
//三つの直線から同距離の位置を求める。
vector<Point>  same_dis(const vector<Line>&ls) {
	vector<Point>vertics;
	vertics.push_back(is_ll(ls[0], ls[2]));
	vertics.push_back(is_ll(ls[1], ls[2]));

	if (abs(vertics[0] - vertics[1]) < eps)return vector<Point>{vertics[0]};
	Line bis(line_bisection(ls[0], ls[1]));
	vector<Point>ecs;

	Line abi(line_bisection(Line(vertics[0], vertics[1]), ls[0]));
	ecs.push_back(is_ll(bis, abi));


	Line bbi(line_bisection(Line(vertics[0], 2.l*vertics[0] - vertics[1]), ls[0]));
	ecs.push_back(is_ll(bis, bbi));

	return ecs;
}
/* 円 */

// 円と円の交点
vector<Point> is_cc(const Circle& c1, const Circle& c2) {
	vector<Point> res;
	ld d = abs(c1.p - c2.p);
	ld rc = (d * d + c1.r * c1.r - c2.r * c2.r) / (2 * d);
	ld dfr = c1.r * c1.r - rc * rc;
	if (abs(dfr) < eps) dfr = 0.0;
	else if (dfr < 0.0) return res; // no intersection
	ld rs = sqrt(dfr);
	Point diff = (c2.p - c1.p) / d;
	res.push_back(c1.p + diff * Point(rc, rs));
	if (dfr != 0.0) res.push_back(c1.p + diff * Point(rc, -rs));
	return res;
}

/*
点が円の中にいるか
0 => out
1 => on
2 => in*/
int is_in_Circle(const  Point& p, const Circle &cir) {
	ld dis = abs(cir.p - p);
	if (dis > cir.r + eps)return 0;
	else if (dis < cir.r - eps)return 2;
	else return 1;
}
int is_in_Circle(const Circle &cir, const  Point& p) {
	ld dis = abs(cir.p - p);
	if (dis > cir.r + eps)return 0;
	else if (dis < cir.r - eps)return 2;
	else return 1;
}
/*
円lcが円rcの中にいるか
0 => out
1 => on
2 => in*/
int Circle_in_Circle(const Circle &lc, const  Circle&rc) {
	ld dis = abs(lc.p - rc.p);
	if (dis < rc.r - lc.r - eps)return 2;
	else if (dis>rc.r - lc.r + eps)return 0;
	else return 1;
}

// 円と直線の交点
vector<Point> is_lc(const Circle& c, const Line& l) {
	vector<Point> res;
	ld d = dist_lp(l, c.p);
	if (d < c.r + eps) {
		ld len = (d > c.r) ? 0.0 : sqrt(c.r * c.r - d * d); //safety;
		Point nor = (l.a - l.b) / abs(l.a - l.b);
		res.push_back(proj(l, c.p) + len * nor);
		res.push_back(proj(l, c.p) - len * nor);
	}
	return res;
}

// 円と線分の距離
vector<Point> is_sc(const Circle& c, const Line& l) {
	vector<Point> v = is_lc(c, l), res;
	for (Point p : v)
		if (isis_sp(l, p)) res.push_back(p);
	return res;
}

// 円と点の接線
vector<Line> tangent_cp(const Circle& c, const Point& p) {
	vector<Line> ret;
	Point v = c.p - p;
	ld d = abs(v);
	ld l = sqrt(norm(v) - c.r * c.r);
	if (isnan(l)) { return ret; }
	Point v1 = v * Point(l / d, c.r / d);
	Point v2 = v * Point(l / d, -c.r / d);
	ret.push_back(Line(p, p + v1));
	if (l < eps) return ret;
	ret.push_back(Line(p, p + v2));
	return ret;
}

// 円と円の接線
vector<Line> tangent_cc(const Circle& c1, const Circle& c2) {
	vector<Line> ret;
	if (abs(c1.p - c2.p) - (c1.r + c2.r) > -eps) {
		Point center = (c1.p * c2.r + c2.p * c1.r) / (c1.r + c2.r);
		ret = tangent_cp(c1, center);
	}
	if (abs(c1.r - c2.r) > eps) {
		Point out = (-c1.p * c2.r + c2.p * c1.r) / (c1.r - c2.r);
		vector<Line> nret = tangent_cp(c1, out);
		ret.insert(ret.end(), nret.begin(), nret.end());
	}
	else {
		Point v = c2.p - c1.p;
		v /= abs(v);
		Point q1 = c1.p + v * Point(0, 1) * c1.r;
		Point q2 = c1.p + v * Point(0, -1) * c1.r;
		ret.push_back(Line(q1, q1 + v));
		ret.push_back(Line(q2, q2 + v));
	}
	return ret;
}
//二つの円の重なり面積
ld two_Circle_area(const Circle&l, const Circle&r) {
	ld dis = abs(l.p - r.p);
	if (dis > l.r + r.r)return 0;
	else if (dis + r.r < l.r) {
		return r.r*r.r*pi;
	}
	else if (dis + l.r < r.r) {
		return l.r*l.r*pi;
	}
	else {
		ld ans = (l.r)*(l.r)*acos((dis*dis + l.r*l.r - r.r*r.r) / (2 * dis*l.r)) +
			(r.r)*(r.r)*acos((dis*dis + r.r*r.r - l.r*l.r) / (2 * dis*r.r)) -
			sqrt(4 * dis*dis*l.r*l.r - (dis*dis + l.r*l.r - r.r*r.r)*(dis*dis + l.r*l.r - r.r*r.r)) / 2;
		return ans;
	}

}

/* 多角形 */

typedef vector<Point> Polygon;


// 面積
ld get_area(const Polygon &p) {
	ld res = 0;
	int n = p.size();
	for (int j = 0; j<n; ++j) res += cross(p[j], p[(j + 1) % n]);
	return res / 2;
}

// 円の内外判定
/*0 => out
1 => on
2 => in*/
int convex_contains(const Polygon &poly, const  Point& p) {
	ld angle = 0;
	int n = poly.size();
	for(int i=0;i<n;++i) {
		Point a = poly[i], b = poly[(i + 1) % n];
		if (isis_sp(Line(a, b), p)) return 1;
		angle += arg((b - p) / (a - p));
	}
	return eq(angle, 0) ? 0 : 2;
}


struct Edge {
	int src;
	int dest;
};
using Graph=vector<vector<Edge>>;

//線分アレンジメント後に、
//閉路の面積の和を求める。
void dfs(set<pair<int ,int>>&aset,const int now, const int from, vector<int>&prevs, vector<Polygon>&ans_polys, const vector<Point>&ps, const Graph&g) {
	vector<pair<ld, int>>next_points;

	Point from_p = from == -1 ? ps[now] + Point(-3e6, 0) : ps[from];
	Point now_p = ps[now];
	for (auto e : g[now]) {
		if (e.dest == from)continue;
		Point next_p = ps[e.dest];

		ld costheta = dot(from_p - now_p, next_p - now_p) / (abs(from_p - now_p)*abs(next_p - now_p));
		costheta *= 0.3;
		ld d = acos(costheta);
		if (ccw(from_p, now_p, next_p) != 1) {
			d = 2 * pi - d;
		}
		next_points.push_back(make_pair(d, e.dest));
	}
	sort(next_points.begin(), next_points.end());

	for (auto n_p : next_points) {
		if (aset.find(make_pair(now, n_p.second))!=aset.end()) {
			continue;
		}
		else {
			aset.emplace(now,n_p.second);
		}

		if (prevs[n_p.second] != -1) {
			int k=now;
			Polygon poly(1,ps[k]);
			while (k != n_p.second) {
				k=prevs[k];
				poly.push_back(ps[k]);
			}
			
			ld plus = get_area(poly);
			if (plus<eps) {
				reverse(poly.begin(),poly.end());
				ans_polys.push_back(poly);
			}
			throw(false);
		}
		else {

			prevs[n_p.second] = now;
			dfs(aset, n_p.second, now, prevs, ans_polys, ps, g);
			prevs[n_p.second] = -1;
		}

	}
	return ;
}

int main() {
	while (true) {
		int N,M;cin>>N>>M;
		if(!N)break;
		vector<Circle>cs;
		for (int i = 0; i < N; ++i) {
			int x,y,r;cin>>x>>y>>r;
			cs.push_back(Circle(Point(x,y),r));
		}
		vector<vector<Edge>>edges(N);
		Graph g(N);
		for (int i = 0; i < N; ++i) {
			for (int j = i + 1; j < N; ++j) {
				
				ld dis=abs(cs[i].p-cs[j].p);
				if (Circle_in_Circle(cs[i], cs[j])==0&&Circle_in_Circle(cs[j],cs[i])==0&&dis<cs[i].r+cs[j].r) {
					edges[i].push_back(Edge{ i,j });
					edges[j].push_back(Edge{ j,i });
				}
			}
		}
		vector<Point>ps(N);
		for(int i=0;i<N;++i)ps[i]=cs[i].p;

		vector<Polygon>polys;
		set<pair<int,int>>aset;

		for(int i=0;i<N;++i) {
			for (int j = 0; j < edges[i].size();++j) {
				if (aset.find(make_pair(i, j)) == aset.end()) {
					try {
						vector<int>prevs(N, -1);
						prevs[i] = -2;
						dfs(aset, i, -1, prevs, polys, ps, edges);
					}
					catch (...) {
					}
				}
			}
		}

		for (int i = 0; i < M; ++i) {
			vector<Point>query_points;
			{
				int a,b,c,d;cin>>a>>b>>c>>d;
				Point p,q;
				p=Point(a,b);
				q=Point(c,d);
				query_points = vector<Point>{ p,q };
			}
			vector<vector<int>>in_circles(2);
			for (int j = 0; j < 2; ++j) {
				for (int k = 0; k < N; ++k) {
					if (is_in_Circle(cs[k], query_points[j])) {
						in_circles[j].push_back(true);
					}
					else {
						in_circles[j].push_back(false);
					}
				}
			}
			bool ok;
			if (in_circles[0] != in_circles[1]) {
				ok=false;
			}
			else {
				if (all_of(in_circles[0].begin(), in_circles[0].end(), [](const int a) {return a == 0; })) {
					vector<vector<int>>in_polygons(2,vector<int>(polys.size()));
					for (int j = 0; j < 2; ++j) {
						for (int k = 0; k < polys.size(); ++k) {
							if (convex_contains(polys[k], query_points[j])) {
								in_polygons[j][k] = true;
							}
						}
					}
					if (in_polygons[0] == in_polygons[1]) {
						ok=true;
					}
					else {
						ok=false;
					}
				}
				else {
					ok=true;
				}

			}
			if(ok)cout<<"YES";
			else cout<<"NO";
			if(i==M-1)cout<<endl;
			else cout<<" ";
			
		}
	}
	return 0;
}
accuracy: 0.5
time_ms: 70
memory_kb: 3360
final_rank: 20

code_snippet: #include <cstdio>
#include <cstring>
#include <iostream>
#include <string>
#include <cmath>
#include <bitset>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <deque>
#include <algorithm>
#include <fstream>
#define EPS 1e-10
   
using namespace std;
typedef long long int ll;
 
const double PI=acos(-1.0);
   
double add(double a, double b){
    if(abs(a+b)<EPS*(abs(a)+abs(b))) return 0;
    return a+b;
}
   
struct P{
    double x, y;
    P() {}
    P(double x, double y): x(x), y(y){
    }
    P operator+ (P p){
        return P(add(x, p.x), add(y, p.y));
    }
    P operator- (P p){
        return P(add(x, -p.x), add(y, -p.y));
    }
    P operator* (double d){
        return P(x*d, y*d);
    }
    double dot(P p){
        return add(x*p.x, y*p.y);
    }
    double det(P p){
        return add(x*p.y, -y*p.x);
    }
    bool operator< (const P& p) const{
        if(abs(x-p.x)>EPS){
            return x<p.x;
        }else{
            return y<p.y-EPS;
        }
    }
};
   
bool on_seg(P p1, P p2, P q){
    return (p1-q).det(p2-q)==0 && (p1-q).dot(p2-q)<=0;
}
   
P intersection(P p1, P p2, P q1, P q2){
    return p1+(p2-p1)*((q2-q1).det(q1-p1) / (q2-q1).det(p2-p1));
}
 
double dist(P p1, P p2){
  return sqrt((p1-p2).dot(p1-p2));
}
 
bool in_polygon(vector<P> poly, P p){ //内ならtrue、多角形は反時計回り
  int ct=0;
  P inf=P(cos(1.0)*20000.0, sin(1.0)*20000.0);
  int N=poly.size();
  for(int i=0; i<N-1; i++){
    P q=intersection(poly[i], poly[i+1], p, inf);
    if(on_seg(poly[i], poly[i+1], q) && on_seg(p, inf, q)) ct++;
  }
  P q=intersection(poly[N-1], poly[0], p, inf);
  if(on_seg(poly[N-1], poly[0], q) && on_seg(p, inf, q)) ct++;
  if(ct%2==0){
    return false;
  }else{
    return true;
  }
}
 
int n;
P c[102];
bool nn[102][102];
vector<int> v[102];
 
vector<int> min_polygon0(P p, int j, int i){
  bool vec=0;
  if((c[i]-c[j]).det(p-c[j])>0) vec=1;
  vector<int> ppoly;
  ppoly.push_back(i);
  int i1=i, j1=j;
  int ct=1;
  while(ct<=n+3){
    int i2;
    double ang0;
    ang0=3.0*PI;
    for(int l=0; l<v[i1].size(); l++){
      int x=v[i1][l];
      if(x==j1) continue;
      if(nn[i1][x]==1) continue;
      double cs=((c[j1]-c[i1]).dot(c[x]-c[i1]))/(dist(c[j1], c[i1])*dist(c[x], c[i1]));
      double ang;
      if(abs(cs+1.0)<EPS){
        ang=PI;
      }else if(abs(cs-1.0)<EPS){
        ang=0;
      }else{
        ang=acos(cs);
	  }
      if((c[j1]-c[i1]).det(c[x]-c[i1])<0){
        ang=2.0*PI-ang;
      }
	  if(vec==1){
		  ang=2.0*PI-ang;
	  }
      if(ang0>ang){
        ang0=ang;
        i2=x;
      }
    }
    j1=i1;
    i1=i2;
    ppoly.push_back(i2);
    ct++;
    if(i1==j){
      break;
    }
  }
  vector<P> ppoly1;
  for(int i=0; i<ppoly.size(); i++){
    ppoly1.push_back(c[ppoly[i]]);
  }
  if(ct>=n+3 || in_polygon(ppoly1, p)==false){
    vector<int> emp;
    return emp;
  }else{
    return ppoly;
  }
}
 
vector<int> min_polygon(P p){
  for(int i=0; i<n; i++){
    for(int j=0; j<v[i].size(); j++){
      int x=v[i][j];
      if(nn[i][x]==1) continue;
      P mid=(c[i]+c[x])*0.5;
      bool e0=0;
      for(int k=0; k<n; k++){
        for(int l=0; l<v[k].size(); l++){
          int y=v[k][l];
          if(nn[k][y]==1) continue;
          if((k==i && y==x) || (k==x && y==i)) continue;
          if((mid-p).det(c[k]-c[y])==0){
            if(on_seg(mid, p, c[k]) || on_seg(mid, p, c[y]) || on_seg(c[k], c[y], mid) || on_seg(c[k], c[y], p)){
              e0=1;
              break;
            }
          }else{
            P r=intersection(mid, p, c[k], c[y]);
            if(on_seg(mid, p, r) && on_seg(c[k], c[y], r)){
              e0=1;
              break;
            }
          }
        }
        if(e0==1) break;
      }
      if(e0==0){
        vector<int> ppoly=min_polygon0(p, i, x);
        if(!ppoly.empty()){
          return ppoly;
        }
      }
    }
  }
  vector<int> emp;
  return emp;
}
  
int main()
{
    while(1){
      int m;
      cin>>n>>m;
      if(n==0 && m==0) return 0;
      double r[100];
      for(int i=0; i<n; i++){
        double cx, cy;
        cin>>cx>>cy>>r[i];
        c[i]=P(cx, cy);
      }
      for(int i=0; i<100; i++){
        v[i].clear();
      }
      for(int i=0; i<n; i++){
        for(int j=i+1; j<n; j++){
          double d=dist(c[i], c[j]);
          if(abs(r[i]-r[j])<d && d<(r[i]+r[j])){
            v[i].push_back(j);
            v[j].push_back(i);
          }
        }
      }

	  for(int i=0; i<n; i++){
          for(int j=0; j<n; j++){
            nn[i][j]=0;
		  }
      }
	  for(int t=0; t<3*n; t++){
        for(int i=0; i<n; i++){
			int ct=0;
			  for(int j=0; j<v[i].size(); j++){
				if(nn[i][v[i][j]]==1) continue;
				ct++;
			  }
          if(ct==1){
            int j2;
            for(int j=0; j<v[i].size(); j++){
				if(nn[i][v[i][j]]==1) continue;
				j2=v[i][j];
			}
            nn[i][j2]=1;
            nn[j2][i]=1;
            
          }
        }
	  }
      for(int k=0; k<m; k++){
        P p, q;
        double px, py, qx, qy;
        cin>>px>>py>>qx>>qy;
        p=P(px, py), q=P(qx, qy);
        bool ep[100], eq[100];
        bool e=0, e0=0;
        for(int i=0; i<n; i++){
          if(dist(p, c[i])<r[i]){
            ep[i]=1;
            e0=1;
          }else{
            ep[i]=0;
          }
          if(dist(q, c[i])<r[i]){
            eq[i]=1;
            e0=1;
          }else{
            eq[i]=0;
          }
          if(ep[i]!=eq[i]){
            if(k==m-1){
              cout<<"NO";
            }else{
              cout<<"NO ";
            }
            e=1;
            break;
          }
        }
        if(e==1) continue;
        if(e0==1){
          if(k==m-1){
            cout<<"YES";
          }else{
            cout<<"YES ";
          }
          continue;
        }
        
        vector<int> ppoly, qpoly;
        ppoly=min_polygon(p);
        qpoly=min_polygon(q);
        if(ppoly.empty() && qpoly.empty()){
          if(k==m-1){
            cout<<"YES";
          }else{
            cout<<"YES ";
          }
          continue;
        }
        if(ppoly.empty() || qpoly.empty()){
          if(k==m-1){
              cout<<"NO";
            }else{
              cout<<"NO ";
            }
          continue;
        }
        if(ppoly.size()!=qpoly.size()){
          if(k==m-1){
              cout<<"NO";
            }else{
              cout<<"NO ";
            }
          continue;
        }
        sort(ppoly.begin(), ppoly.end());
        sort(qpoly.begin(), qpoly.end());
        bool ok=0;
        for(int i=0; i<ppoly.size(); i++){
          if(ppoly[i]!=qpoly[i]){
            ok=1;
            break;
          }
        }
        if(ok==0){
          if(k==m-1){
            cout<<"YES";
          }else{
            cout<<"YES ";
          }
        }else{
          if(k==m-1){
              cout<<"NO";
            }else{
              cout<<"NO ";
            }
        }
      }
      cout<<"\n";
    }
    return 0;
}
accuracy: 1
time_ms: 40
memory_kb: 3372
final_rank: 3