#include<vector>
#include<queue>
#include<tuple>
#include<functional>
#include<cfloat>
#include<algorithm>

const int NIL = -1;
const double INF = DBL_MAX / 3.0;

// Edge
class Edge {
public:
    Edge(const int f, const int t, const double w) :from_(f), to_(t), weight_(w), color_(NIL) {}
    int from() const {return from_;}
    int to() const {return to_;}
    double weight() const {return weight_;}
    int color() const {return color_;}
    int color(const int c) {return color_ = c;}
    bool operator<(const Edge& e) const {
        if(from() != e.from()) return from() < e.from();
        if(to() != e.to()) return to() < e.to();
        return weight() < e.weight();
    }
private:
    int from_, to_;
    double weight_;
    int color_;
};

// Graph
class Graph {
public:
    explicit Graph(int s) :size_(s) {edge_.resize(s);}

    int size() const;
    const std::vector<Edge>& edge(const int v) const;

    void add(const Edge& e);
    void add(const int from, const int to, const double weight);

    const std::vector<double> Dijkstra_all(const int start) const;

private:
    int size_;
    std::vector<std::vector<Edge>> edge_;
};

// implements
int Graph::size() const {return size_;}
const std::vector<Edge>& Graph::edge(const int v) const {return edge_[v];}
void Graph::add(const Edge& e) {edge_[e.from()].push_back(e);}
void Graph::add(const int from, const int to, const double weight) {edge_[from].push_back(Edge(from, to, weight));}
const std::vector<double> Graph::Dijkstra_all(const int start) const {
    enum {COST, VERTEX};
    typedef std::tuple<double, int> Node;
    std::vector<double> result(size(), INF);
    std::priority_queue<Node, std::vector<Node>, std::greater<Node>> q;
    q.push(Node(0, start));
    while(!q.empty()) {
        double cost = std::get<COST>(q.top());
        int from = std::get<VERTEX>(q.top());
        q.pop();
        if(result[from] <= cost) continue;
        result[from] = cost;
        for(const auto& e: edge(from)) q.push(Node(cost + e.weight(), e.to()));
    }
    return result;
}

////////////////////////////////////////////////////////////////////////////////

#include<iostream>
#include<set>
using namespace std;

struct Point {
    double x, y;
    Point() {}
    Point(const double x, const double y) :x(x), y(y) {}
};
bool operator<(const Point& lhs, const Point& rhs) {return lhs.x != rhs.x ? lhs.x < rhs.x : lhs.y < rhs.y;}
const Point operator-(const Point& lhs, const Point& rhs) {return Point(lhs.x - rhs.x, lhs.y - rhs.y);}
double cross(const Point& lhs, const Point& rhs) {return lhs.x * rhs.y - lhs.y * rhs.x;}
enum {CCW = -1, ONLINE, CW};
int ccw(const Point& a, const Point& b, const Point& c) {
    if(cross(b - a, c - a) > 0.0) return CCW;
    if(cross(b - a, c - a) < 0.0) return CCW;
    return ONLINE;
}

int color_size;

void dfs(vector<vector<Edge>>& edge, const int previous, const int current) {
    for(int step = 0; step < 2 * edge[current].size(); ++step) {
        int i = step % edge[current].size();
        if(edge[current][i].to() != previous) continue;

        i = (i + 1) % edge[current].size();
        if(edge[current][i].color() != NIL) return;
        edge[current][i].color(color_size);
        dfs(edge, edge[current][i].from(), edge[current][i].to());
        break;
    }
}

int main() {
    int C, W;
    while(cin >> C >> W, C | W) {
        vector<Point> pillar(C);
        for(auto& p: pillar) cin >> p.x >> p.y;
        vector<vector<Edge>> edge(C);
        for(int i = 0; i < W; ++i) {
            int s, t;
            cin >> s >> t;
            --s; --t;
            edge[s].push_back(Edge(s, t, atan2(pillar[t].y - pillar[s].y, pillar[t].x - pillar[s].x)));
            edge[t].push_back(Edge(t, s, atan2(pillar[s].y - pillar[t].y, pillar[s].x - pillar[t].x)));
        }

        // bubble sort
        for(auto& e: edge) for(int i = 0; i < e.size(); ++i) for(int j = e.size() - 1; j > i; --j)
            if(e[i].weight() > e[j].weight()) swap(e[i], e[j]);

        // coloring
        color_size = 0;
        for(int v = 0; v < C; ++v) for(auto& e: edge[v]) {
            if(e.color() == NIL) {
                e.color(color_size);
                dfs(edge, e.from(), e.to());
                ++color_size;
            }
        }

        // construct the dual graph
        vector<set<int>> new_edge(color_size);
        for(int v = 0; v < C; ++v) for(const auto& e: edge[v]) {
            for(const auto& reverse: edge[e.to()]) {
                if(reverse.to() != e.from()) continue;
                new_edge[e.color()].insert(reverse.color());
                break;
            }
        }
        Graph g(color_size);
        for(int from = 0; from < new_edge.size(); ++from) for(int to: new_edge[from])
            g.add(from, to, 1);

        // solve
        int leftmost = 0;
        for(int i = 1; i < C; ++i) if(pillar[i] < pillar[leftmost]) leftmost = i;
        int outside = edge[leftmost].front().color();
        auto list = g.Dijkstra_all(outside);
        cout << *max_element(list.begin(), list.end()) << endl;
    }
}

Measured memory_bytes: 1392