#include <bits/stdc++.h>
using namespace std;
int N, M, S, D;
vector<pair<int, int> > G[105], GT[105];
vector<int> GG[105];
vector<int> Path[105], Path2[105];
pair<int, int> E[1005];
bool P[1005];
bool Full[1005];
bool Use[105];
int Cost[1005], MaxFlow, top;
int Stack[1005];
int C[105][105];
int F[105][105], TT[105];
queue<int> Q;
void Read() {
  scanf("%d%d%d%d", &N, &M, &S, &D);
  for (int i = 1; i <= M; i++) {
    int x, y, g;
    scanf("%d%d%d", &x, &y, &g);
    G[x].push_back(make_pair(y, i));
    GT[y].push_back(make_pair(x, i));
    GG[x].push_back(y);
    GG[y].push_back(x);
    if (g == 1) {
      C[x][y] = 1;
      C[y][x] = 1000000000;
    } else
      C[x][y] = 1000000000;
    E[i] = make_pair(x, y);
    P[i] = g;
  }
}
void DFS(int node, int edge) {
  Use[node] = 1;
  if (node != S) Stack[++top] = edge;
  for (int i = 1; i <= top; i++) Path[node].push_back(Stack[i]);
  for (int i = 0; i < G[node].size(); i++) {
    int neighb = G[node][i].first, e = G[node][i].second;
    if (P[e] == 0) continue;
    if (Use[neighb] == 0) {
      DFS(neighb, e);
    }
  }
  if (node != S) --top;
}
void DFS2(int node, int edge) {
  Use[node] = 1;
  if (node != D) Stack[++top] = edge;
  for (int i = 1; i <= top; i++) Path2[node].push_back(Stack[i]);
  for (int i = 0; i < GT[node].size(); i++) {
    int neighb = GT[node][i].first, e = GT[node][i].second;
    if (P[e] == 0) continue;
    if (Use[neighb] == 0) {
      DFS2(neighb, e);
    }
  }
  if (node != D) --top;
}
void findPath(int u, int v, int edge) {
  memset(Full, 0, sizeof(Full));
  Full[edge] = 1;
  for (int j = 0; j < Path[u].size(); j++) {
    int e = Path[u][j];
    Cost[e]++;
    Full[e] = 1;
  }
  for (int j = Path2[v].size() - 1; j >= 0; j--) {
    int e = Path2[v][j];
    if (Full[e] == 1) break;
    Full[e] = 1;
    Cost[e]++;
  }
  Cost[edge]++;
}
int BFS(int ind) {
  memset(Use, 0, sizeof(Use));
  Q.push(S);
  Use[S] = 1;
  while (!Q.empty()) {
    int Node = Q.front();
    Q.pop();
    if (ind == 0 && Node == D) continue;
    for (unsigned int i = 0; i < GG[Node].size(); ++i) {
      int Neighbour = GG[Node][i];
      if (!Use[Neighbour] && C[Node][Neighbour] - F[Node][Neighbour] > 0) {
        TT[Neighbour] = Node;
        Q.push(Neighbour);
        Use[Neighbour] = 1;
      }
    }
  }
  return Use[D];
}
void Solve() {
  while (BFS(0)) {
    for (unsigned int k = 0; k < GG[D].size(); ++k) {
      int Vecin = GG[D][k];
      if (!Use[Vecin] || C[Vecin][D] - F[Vecin][D] == 0) continue;
      TT[D] = Vecin;
      int Fmin = 1000000000;
      for (int i = D; i != S; i = TT[i])
        Fmin = min(Fmin, C[TT[i]][i] - F[TT[i]][i]);
      MaxFlow += Fmin;
      for (int i = D; i != S; i = TT[i]) {
        F[TT[i]][i] += Fmin;
        F[i][TT[i]] -= Fmin;
      }
    }
  }
  BFS(1);
}
int main() {
  Read();
  DFS(S, 0);
  memset(Use, 0, sizeof(Use));
  DFS2(D, 0);
  for (int i = 1; i <= M; i++) {
    if (P[i] == 1) {
      findPath(E[i].first, E[i].second, i);
    }
  }
  Solve();
  printf("%d\n", MaxFlow);
  for (int i = 1; i <= M; i++) {
    if (P[i] == 1 && F[E[i].first][E[i].second] == 1 &&
        Use[E[i].first] != Use[E[i].second]) {
      printf("%d %d\n", Cost[i], Cost[i]);
    } else {
      printf("%d 1000000000\n", Cost[i]);
    }
  }
  return 0;
}
