/**
 * code generated by JHelper
 * More info: https://github.com/AlexeyDmitriev/JHelper
 * @author
 */

#include <iostream>
#include <fstream>

#include <algorithm>
#include <array>
#include <bitset>
#include <cassert>
#include <climits>
#include <cmath>
#include <complex>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <functional>
#include <iostream>
#include <map>
#include <memory>
#include <numeric>
#include <queue>
#include <random>
#include <set>
#include <stack>
#include <string>
#include <unordered_set>
#include <unordered_map>
#include <vector>
#include <ctime>

#define pb push_back
#define sz(v) ((int)(v).size())
#define all(v) (v).begin(),(v).end()
#define mp make_pair

using namespace std;

typedef long long int64;
typedef vector<int> vi;
typedef pair<int, int> ii;

int BUBEN = 30;
int BUBEN2 = 3;
int BUBEN3 = 10;

class TaskH {
 public:
  void solveOne(istream &in, ostream &out) {
    double start = clock() / (double) CLOCKS_PER_SEC;

    int n;
    in >> n;
    vector<int> x(n + 1);
    vector<int> y(n + 1);
    for (int i = 0; i <= n; ++i) {
      in >> x[i] >> y[i];
    }
    vector<double> offset;
    vector<double> len;
    vector<double> dx;
    vector<double> dy;
    offset.push_back(0);
    for (int i = 0; i < n; ++i) {
      double vx = x[i + 1] - x[i];
      double vy = y[i + 1] - y[i];
      double z = hypot(vx, vy);
      dx.push_back(vx / z);
      dy.push_back(vy / z);
      len.push_back(z);
      offset.push_back(offset.back() + z);
    }
    auto ternary = [&](const auto& func, double left, double right) {
      while (right - left > 1e-12 * max(1.0, right)) {
        double mleft = (2 * left + right) / 3;
        double mright = (left + 2 * right) / 3;
        double vleft = func(mleft);
        double vright = func(mright);
        if (vleft > vright) {
          right = mright;
        } else {
          left = mleft;
        }
      }
      return func((left + right) / 2);
    };
    double total = offset.back();

    auto where = [&](double pos) {
      int i = lower_bound(all(offset), pos) - offset.begin() - 1;
      if (i < 0) i = 0;
      if (i >= n) i = n - 1;
      return i;
    };

    auto evalBefore = [&](double pos) {
      int i = where(pos);
      double xi = x[i] + dx[i] * (pos - offset[i]);
      double yi = y[i] + dy[i] * (pos - offset[i]);

      double bestBefore = 1;
      for (int j = 0; j < i; ++j) {
        auto eval2 = [&](double t) {
          double xj = x[j] + dx[j] * t;
          double yj = y[j] + dy[j] * t;
          double direct = pos - (offset[j] + t);
          return direct / hypot(xi - xj, yi - yj);
        };
        double cur = ternary(eval2, 0, len[j]);
        bestBefore = max(bestBefore, cur);
      }
      return bestBefore;
    };

    auto evalAfter = [&](double pos) {
      int i = where(pos);
      double xi = x[i] + dx[i] * (pos - offset[i]);
      double yi = y[i] + dy[i] * (pos - offset[i]);

      double bestAfter = 1;
      for (int j = i + 1; j < n; ++j) {
        auto eval2 = [&](double t) {
          double xj = x[j] + dx[j] * t;
          double yj = y[j] + dy[j] * t;
          double direct = (offset[j] + t) - pos;
          return direct / hypot(xi - xj, yi - yj);
        };
        double cur = ternary(eval2, 0, len[j]);
        bestAfter = max(bestAfter, cur);
      }
      return bestAfter;
    };

    auto eval = [&](double pos) {
      return min(evalBefore(pos), evalAfter(pos));
    };


    double STEP = total;
    vector<tuple<double, int, double>> best;
    while (clock() / (double) CLOCKS_PER_SEC < start + 1) {
      STEP *= 0.5;
      for (double at = STEP; at <= total; at += STEP * 2) {
        best.emplace_back(eval(at), where(at), at);
      }
    }
    sort(all(best));

    auto reduce = [&]() {
      sort(all(best), [](const auto& a, const auto& b) {
        return get<0>(a) > get<0>(b);
      });
      int got = 0;
      vector<int> gotIn(n);
      for (int i = 0; i < best.size(); ++i) {
        if (got < BUBEN3 && gotIn[get<1>(best[i])] < BUBEN2) {
          best[got] = best[i];
          ++got;
          ++gotIn[get<1>(best[i])];
        }
      }
      best.resize(got);
    };

    while (clock() / (double) CLOCKS_PER_SEC < start + 3) {
      reduce();
      STEP *= 0.5;
      sort(all(best), [](const auto& a, const auto& b) {
        return get<2>(a) < get<2>(b);
      });
      int old = best.size();
      double prev = 0;
      for (int i = 0; i <= old; ++i) {
        double cur;
        if (i == old) {
          cur = total;
        } else {
          cur = get<2>(best[i]);
        }
        if (cur - prev > 3 * STEP) {
          double at = prev + STEP;
          best.emplace_back(eval(at), where(at), at);
          at = cur - STEP;
          best.emplace_back(eval(at), where(at), at);
        } else if (cur - prev > 2 * STEP) {
          double at = (2 * prev + cur) / 3;
          best.emplace_back(eval(at), where(at), at);
          at = (prev + 2 * cur) / 3;
          best.emplace_back(eval(at), where(at), at);
        } else if (cur - prev > STEP) {
          double at = (prev + cur) / 2;
          best.emplace_back(eval(at), where(at), at);
        }
        prev = cur;
      }
    }

    cerr << STEP << endl;

    double res = 0;
    for (const auto& x : best) {
      res = max(res, get<0>(x));
    }
    out << res << "\n";
  }



  void solve(std::istream &in, std::ostream &out) {
    out.precision(20);
    int nt = 1;
    for (int it = 0; it < nt; ++it) {
      solveOne(in, out);
    }
  }
};


int main() {
  ios_base::sync_with_stdio(false);
  cin.tie(NULL);
  TaskH solver;
  std::istream& in(std::cin);
  std::ostream& out(std::cout);
  solver.solve(in, out);
  return 0;
}
