//#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define CT printf("\n--------------------\n");
const int inf=1000000000;
const ll Inf=1000000000000000000ll;
char buf[1<<23],*p1=buf,*p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<23,stdin),p1==p2)?EOF:*p1++)
int read(){
	int x=0,f=0;char c=getchar();
	while(!isdigit(c))(c=='-')&&(f=1),c=getchar();
	while(isdigit(c))x=x*10+(c&15),c=getchar();
	return f?-x:x;
}
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
const int N=400005;
const int M=1000005;
struct Edge{
	int to,nxt,id;
}e[M];
int Top=1,head[N];
void addedge(int u,int v,int id){
	e[++Top].to=v;
	e[Top].nxt=head[u];
	head[u]=Top;
	e[Top].id=id;
	
//	printf("** %d %d %d\n",u,v,id);
}
map<pair<ll,ll>,int> mp;
#define fir first
#define sec second
int tot,vis[M],match[M];
vector<pair<int,int> > ans;
int getid(ll x,ll y){
	ll t=gcd(x,y);
	x/=t;y/=t;
	pair<ll,ll> p=make_pair(x,y);
	if(!mp[p]) mp[p]=++tot;
	return mp[p];
}
void dfs(int u){
//	printf("** %d\n",u);
	vis[u]=1;
	for(int i=head[u];i;i=e[i].nxt){
		int to=e[i].to,id=e[i].id;
		e[i].to=e[i^1].to=0;
		if(!to)continue;
		if(!vis[to])dfs(to);
		if(match[to]){
			ans.push_back(make_pair(id,match[to]));
			match[to]=0;
		}else
		if(match[u]){
			ans.push_back(make_pair(id,match[u]));
			match[u]=0;
		}else match[u]=id;
	}
}
int main(){
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);

	int n=read();
	for(int i=1;i<=n;++i){
		int a=read(),b=read(),c=read(),d=read();
		int id1=getid(1ll*(a+b)*d,1ll*c*b);
		int id2=getid(1ll*a*d,1ll*(c+d)*b);
		addedge(id1,id2,i);
		addedge(id2,id1,i);
	}
	
//	printf("QAQ %d\n",tot);
	
	for(int i=1;i<=tot;++i)
		if(!vis[i]) dfs(i);
	printf("%d\n",ans.size());
	for(int i=0;i<ans.size();++i){
		printf("%d %d\n",ans[i].fir,ans[i].sec);
	}

//	fclose(stdin);fclose(stdout);
	return 0;
}
