#include <fstream>
#include <cstring>
#include <vector>
#include <cmath>
#include <algorithm>
#include <queue>
using namespace std;
ifstream F("nowhere-zero.in");
ofstream G("nowhere-zero.out");
/**
Construiesc regiunile si trag muchie intre pentru laturile comune din regiuni diferite.
Muchiile initiale le orientez. Intai fac un vector nxt care as ma trimita la urmatorul
punct. ( punctele vor fi sortea in ordine trigonometrica ) Cand ajung in acelasi punct
am epuziat o regiune.
Colorez graful regiunilor si apoi contruiesc reteaua de circulatie. Colorarea se face cu
6 culori , deci pornesc de la nodul cu grad minim si il elimin din graf.
Complexitatea: O(N log N)
*/
const int debuging = 0;
#define IT(type) vector<type>::iterator
#define parc(type,it,vect) for (vector< type >::iterator it=vect.begin();it!=vect.end();++it)
#define x first
#define y second
const int Nmax = 100010;
int N,M,K;
vector< pair<double,double> > point;
vector< pair<int,int> > edges;
vector<int> V[Nmax];
vector< vector<int> > regions;
vector<int> r_index;
vector< vector<int> > graph;
vector<int> nxt;
vector<double> angle;
int reverse_index(int x)
{
if ( x > M )
return x-M;
return x+M;
}
void print_vector(vector<int> A)
{
if ( !debuging ) return;
G<<"debug: ";
for (size_t i=0;i<A.size();++i)
G<<A[i]<<' ';
G<<'\n';
}
void read_data()
{
F>>N>>M;
point.resize(N+5);
for (int i=1;i<=N;++i)
F>>point[i].x>>point[i].y;
edges.resize(2*M+5);
for (int i=1,x,y;i<=M;++i)
{
F>>x>>y;
edges[i] = make_pair(x,y);
edges[reverse_index(i)] = make_pair(y,x);
V[x].push_back( i );
V[y].push_back( reverse_index(i) );
}
}
bool angle_cmp(int x,int y)
{
return angle[x] < angle[y];
}
void compute_nxt()
{
angle.resize(2*M+5);
nxt.resize(2*M+5);
for (int i=1;i<=N;++i)
{
parc(int,e,V[i])
angle[*e] = atan2(point[edges[*e].y].y-point[i].y,point[edges[*e].y].x-point[i].x);
sort(V[i].begin(),V[i].end(),angle_cmp);
for (size_t j=0;j<V[i].size();++j)
nxt[reverse_index(V[i][j])] = V[i][(j+1)%(int(V[i].size()))];
}
}
vector<bool> used;
void build_regions()
{
used.resize(2*M+5);
r_index.resize(2*M+5);
for (int x=1;x<=N;++x)
parc(int,nowe,V[x])
{
int e = *nowe;
vector<int> region;
for (;used[e] == 0 && edges[e].y != x ; e = nxt[e])
{
region.push_back(e);
r_index[e] = int(regions.size());
used[e] = 1;
}
if ( !used[e] )
{
region.push_back(e);
r_index[e] = int(regions.size());
used[e] = 1;
}
print_vector( region );
if ( region.size() )
regions.push_back( region );
}
K = regions.size();
}
void place_edges()
{
graph.resize(K+5);
for (int i=1;i<=M;++i)
{
graph[ r_index[i] ].push_back( r_index[reverse_index(i)] );
graph[ r_index[reverse_index(i)] ].push_back( r_index[i] );
}
for (int i=0;i<K;++i)
{
sort(graph[i].begin(),graph[i].end());
graph[i].erase( unique(graph[i].begin(),graph[i].end()) , graph[i].end() );
print_vector(graph[i]);
}
}
const int Colors = 6;
queue<int> q;
vector<int> degree;
vector<int> order;
vector<int> color;
vector<int> mark;
int get_color(int node)
{
int X[Colors+5];
memset(X,0,sizeof(X));
print_vector(graph[node]);
parc(int,it,graph[node])
X[color[*it]] = 1;
for (int i=1;i<=Colors;++i)
if ( X[i] == 0 )
return i;
return 0;
}
void color_graph()
{
degree.resize(K+5);
mark.resize(K+5);
color.resize(K+5);
for (int i=0;i<K;++i)
degree[i] = int(graph[i].size());
for (int i=0;i<K;++i)
if ( degree[i] < Colors )
{
mark[i] = 1;
q.push( i );
}
while ( q.size() )
{
int now = q.front();
order.push_back( now );
q.pop();
parc(int,it,graph[now])
{
--degree[*it];
if ( degree[*it] < Colors && mark[*it] == 0 )
{
mark[*it] = 1;
q.push(*it);
}
}
}
for (int i=int(order.size())-1;i>=0;--i)
color[ order[i] ] = get_color(order[i]);
}
void print_data()
{
for (int i=1;i<=(M<<1);++i)
{
int flow = color[ r_index[i] ] - color[ r_index[reverse_index(i)] ];
if ( flow < 0 ) continue;
G<<edges[i].x<<' '<<edges[i].y<<' '<<flow<<'\n';
}
}
int main()
{
read_data();
compute_nxt();
build_regions();
place_edges();
color_graph();
print_data();
}