#include <bits/stdc++.h>
using namespace std;
const double eps = 1e-9;
inline bool eq(double x, double y) { return fabs(x - y) < eps; }
inline bool lt(double x, double y) { return x < y - eps; }
inline bool gt(double x, double y) { return x > y + eps; }
inline bool le(double x, double y) { return x < y + eps; }
inline bool ge(double x, double y) { return x > y - eps; }
struct pt {
  double x, y;
  inline pt operator-(const pt& p) const { return pt{x - p.x, y - p.y}; }
  inline pt operator+(const pt& p) const { return pt{x + p.x, y + p.y}; }
  inline pt operator*(double a) const { return pt{x * a, y * a}; }
  inline double cross(const pt& p) const { return x * p.y - y * p.x; }
  inline double dot(const pt& p) const { return x * p.x + y * p.y; }
  inline bool operator==(const pt& p) const { return eq(x, p.x) && eq(y, p.y); }
};
struct Line {
  pt p[2];
  Line() {}
  Line(pt a, pt b) : p{a, b} {}
  pt vec() const { return p[1] - p[0]; }
  pt& operator[](size_t i) { return p[i]; }
};
inline bool lexComp(const pt& l, const pt& r) {
  if (fabs(l.x - r.x) > eps) {
    return l.x < r.x;
  } else
    return l.y < r.y;
}
inline double sqr(double x) { return x * x; }
vector<pt> interSegSeg(Line l1, Line l2) {
  if (eq(l1.vec().cross(l2.vec()), 0)) {
    if (!eq(l1.vec().cross(l2[0] - l1[0]), 0)) return {};
    if (!lexComp(l1[0], l1[1])) swap(l1[0], l1[1]);
    if (!lexComp(l2[0], l2[1])) swap(l2[0], l2[1]);
    pt l = lexComp(l1[0], l2[0]) ? l2[0] : l1[0];
    pt r = lexComp(l1[1], l2[1]) ? l1[1] : l2[1];
    if (l == r)
      return {l};
    else
      return lexComp(l, r) ? vector<pt>{l, r} : vector<pt>();
  } else {
    double s = (l2[0] - l1[0]).cross(l2.vec()) / l1.vec().cross(l2.vec());
    pt inter = l1[0] + l1.vec() * s;
    if (ge(s, 0) && le(s, 1) && le((l2[0] - inter).dot(l2[1] - inter), 0))
      return {inter};
    else
      return {};
  }
}
inline char get_segtype(Line segment, pt other_point) {
  if (eq(segment[0].x, segment[1].x)) return 0;
  if (!lexComp(segment[0], segment[1])) swap(segment[0], segment[1]);
  return (segment[1] - segment[0]).cross(other_point - segment[0]) > 0 ? 1 : -1;
}
double union_perimeter(vector<tuple<pt, pt, pt> > triangles) {
  vector<Line> segments(3 * triangles.size());
  vector<char> segtype(segments.size());
  for (size_t i = 0; i < triangles.size(); i++) {
    pt a, b, c;
    tie(a, b, c) = triangles[i];
    segments[3 * i] = lexComp(a, b) ? Line(a, b) : Line(b, a);
    segtype[3 * i] = get_segtype(segments[3 * i], c);
    segments[3 * i + 1] = lexComp(b, c) ? Line(b, c) : Line(c, b);
    segtype[3 * i + 1] = get_segtype(segments[3 * i + 1], a);
    segments[3 * i + 2] = lexComp(c, a) ? Line(c, a) : Line(a, c);
    segtype[3 * i + 2] = get_segtype(segments[3 * i + 2], b);
  }
  vector<double> k(segments.size()), b(segments.size());
  for (size_t i = 0; i < segments.size(); i++) {
    if (segtype[i]) {
      k[i] = (segments[i][1].y - segments[i][0].y) /
             (segments[i][1].x - segments[i][0].x);
      b[i] = segments[i][0].y - k[i] * segments[i][0].x;
    }
  }
  double ans = 0;
  for (size_t i = 0; i < segments.size(); i++) {
    if (!segtype[i]) continue;
    double l = segments[i][0].x, r = segments[i][1].x;
    vector<pair<double, int> > evts;
    for (size_t j = 0; j < segments.size(); j++) {
      if (!segtype[j] || i == j) continue;
      double l1 = segments[j][0].x, r1 = segments[j][1].x;
      if (ge(l1, r) || ge(l, r1)) continue;
      double common_l = max(l, l1), common_r = min(r, r1);
      auto pts = interSegSeg(segments[i], segments[j]);
      if (pts.empty()) {
        double yl1 = k[j] * common_l + b[j];
        double yl = k[i] * common_l + b[i];
        if (lt(yl1, yl) == (segtype[i] == 1)) {
          int evt_type = -segtype[i] * segtype[j];
          evts.emplace_back(common_l, evt_type);
          evts.emplace_back(common_r, -evt_type);
        }
      } else if (pts.size() == 1u) {
        double yl = k[i] * common_l + b[i], yl1 = k[j] * common_l + b[j];
        int evt_type = -segtype[i] * segtype[j];
        if (lt(yl1, yl) == (segtype[i] == 1)) {
          evts.emplace_back(common_l, evt_type);
          evts.emplace_back(pts[0].x, -evt_type);
        }
        yl = k[i] * common_r + b[i], yl1 = k[j] * common_r + b[j];
        if (lt(yl1, yl) == (segtype[i] == 1)) {
          evts.emplace_back(pts[0].x, evt_type);
          evts.emplace_back(common_r, -evt_type);
        }
      } else {
        if (segtype[j] != segtype[i] || j > i) {
          evts.emplace_back(common_l, -2);
          evts.emplace_back(common_r, 2);
        }
      }
    }
    evts.emplace_back(l, 0);
    sort(evts.begin(), evts.end());
    size_t j = 0;
    int balance = 0;
    while (j < evts.size()) {
      size_t ptr = j;
      while (ptr < evts.size() && eq(evts[j].first, evts[ptr].first)) {
        balance += evts[ptr].second;
        ++ptr;
      }
      if (!balance && !eq(evts[j].first, r)) {
        double next_x = ptr == evts.size() ? r : evts[ptr].first;
        ans += sqrt(sqr(k[i] * (next_x - evts[j].first)) +
                    sqr(next_x - evts[j].first));
      }
      j = ptr;
    }
  }
  return ans;
}
void solve() {
  mt19937 gen(1488);
  uniform_real_distribution<double> U;
  double angle = U(gen) * acos(0);
  double ca = cos(angle), sa = sin(angle);
  int n;
  cin >> n;
  vector<tuple<pt, pt, pt> > tr;
  for (int i = 0; i < n; i++) {
    pt a[3];
    for (int j = 0; j < 3; j++) {
      cin >> a[j].x >> a[j].y;
      double x = a[j].x * ca - a[j].y * sa;
      double y = a[j].x * sa + a[j].y * ca;
      a[j].x = x;
      a[j].y = y;
    }
    tr.emplace_back(a[0], a[1], a[2]);
  }
  cout << fixed << union_perimeter(tr) << endl;
}
signed main() {
  ios_base::sync_with_stdio(false);
  cin.tie(0);
  cout.precision(11);
  int T = 1;
  while (T--) solve();
  return 0;
}
