#include <fstream>
#include <iostream>
#include <algorithm>
#include <vector>
#include <cmath>
#include <queue>
#include <cstring>
#include <set>
using namespace std;
const int N = 1000005, M = N, cMax = 7;
template<class T>
class Stiva{
T v[N];
int size;
public:
Stiva(){
size = 0;
}
inline void push(T x){
v[++size] = x;
}
inline T top(){
return v[size];
}
inline void pop(){
--size;
}
inline bool empty(){
return size == 0;
}
};
struct Per{
int x, y;
Per(){
x = y = 0;
}
Per(int _x, int _y){
x = _x;
y = _y;
}
inline bool operator<(const Per& N) const {
return x > N.x;
}
};
struct Muchie{
int ind, next;
double alpha;
Muchie(int _ind, int _next, double _alpha){
ind = _ind;
next =_next;
alpha = _alpha;
}
inline bool operator<(const Muchie& M) const {
return alpha < M.alpha;
}
};
int color[N], nrV, nrE, nrC;
double X[N], Y[N];
bool use[N];
vector<int> area[M], comp[M], areaGraph[M];
vector<Muchie> graph[N];
Per edge[M];
Muchie minusUnu = Muchie(-1, 0, 0);
ifstream in("nowhere-zero.in");
ofstream out("nowhere-zero.out");
inline double unghi(int x, int y){
return atan2(Y[y] - Y[x], X[y] - X[x]);
}
inline void addToGraph(int x, int y, int ind){
graph[x].push_back(Muchie(ind, y, unghi(x, y)));
}
void read(){
int x, y;
in >> nrV >> nrE;
for (int i = 1 ; i <= nrV ; i++)
in >> X[i] >> Y[i];
for (int i = 1 ; i <= nrE ; i++){
in >> x >> y;
edge[i] = Per(x, y);
addToGraph(x, y, i);
addToGraph(y, x, i);
}
for (int i = 1 ; i <= nrV ; i++)
if (!graph[i].empty())
sort(graph[i].begin(), graph[i].end());
}
int mex(vector<int>& v, int val[]){
for (int i = 0 ; i < cMax ; i++)
use[i] = false;
for (vector<int> :: iterator it = v.begin() ; it != v.end() ; it++)
use[ val[*it] ] = true;
for (int i = 1 ; i < cMax ; i++)
if (!use[i])
return i;
return -1;
}
void colorare(vector<int> graph[], int color[], int n){
int grad[n + 1];
priority_queue<Per> Q;
Stiva<int> S;
for (int i = 1 ; i <= n ; i++){
grad[i] = graph[i].size();
Q.push(Per(grad[i], i));
use[i] = false;
}
while (!Q.empty()){
int x = Q.top().y; Q.pop();
if (use[x])
continue;
use[x] = true;
S.push(x);
for (vector<int> :: iterator it = graph[x].begin() ; it != graph[x].end() ; it++){
--grad[*it];
Q.push(Per(grad[*it], *it));
}
}
while (!S.empty()){
color[S.top()] = mex(graph[S.top()], color);
S.pop();
}
}
Muchie& find(vector<Muchie>& v, double alpha){
if (v.size() == 1 && v[0].alpha == alpha)
return minusUnu;
if (v.back().alpha <= alpha)
return v[0];
int rez = 0;
for (int step = 1 << 16 ; step ; step >>= 1)
if (rez + step < v.size() && v[rez + step].alpha <= alpha)
rez += step;
return v[rez + 1];
}
inline int semn(bool b){
return b ? 1 : -1;
}
void expand(int src, Muchie& M){
if (M.ind == -1)
return;
area[nrC].push_back(M.ind);
comp[ M.ind ].push_back(nrC * semn(edge[ M.ind ].y == M.next));
M.ind = -1;
if (use[M.next])
return;
use[M.next] = true;
expand(M.next, find(graph[M.next], unghi(M.next, src)));
use[M.next] = false;
}
void adauga(int x, int y){
x = abs(x);
y = abs(y);
areaGraph[x].push_back(y);
areaGraph[y].push_back(x);
}
void buildGraph(vector<Muchie> graph[]){
memset(use, false, sizeof(use));
for (int i = 1 ; i <= nrV ; i++){
use[i] = true;
for (size_t j = 0 ; j < graph[i].size() ; j++)
if (graph[i][j].ind != -1){
++nrC;
expand(i, graph[i][j]);
}
use[i] = false;
}
for (int i = 1 ; i <= nrE ; i++)
if (comp[i].size() > 1)
adauga(comp[i][0], comp[i][1]);
}
void print(Per P, int x, int y){
int val = 0;
if (x > 0)
val += color[x];
else
val -= color[-x];
if (y > 0)
val += color[y];
else
val -= color[-y];
if (val > 0)
out << P.x << " " << P.y << " " << val << "\n";
else
out << P.y << " " << P.x << " " << -val << "\n";
}
void cleanup(vector<int>& v){
sort(v.begin(), v.end());
int n = 1;
for (size_t i = 1 ; i < v.size() ; i++)
if (v[i] != v[i - 1])
v[n++] = v[i];
v.resize(n);
}
int main(){
read();
buildGraph(graph);
for (int i = 1 ; i <= nrC ; i++)
cleanup(areaGraph[i]);
colorare(areaGraph, color, nrC);
for (int i = 1 ; i <= nrE ; i++)
print(edge[i], comp[i][0], comp[i][1]);
return 0;
}