//在init的时候 我传了N进去....WA 传了n AC了..............
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <stdlib.h>
#include <vector>
#include <queue>
#include <cmath>
#include <stack>
#include <map>
#include <set>
using namespace std;
typedef long long LL;
const int inf = 0x3f3f3f3f;
const LL N = 110;
int n, m;
struct Edge {
int u, v, w;
} edge[N * N];
int MAX[N][N]; //i->j的最大权值
bool vis[N*N]; //改变是否已经在MST中
vector<int> G[N]; //存储MST
int pre[N];
void init(int n)
{
memset(vis, 0, sizeof(vis));
memset(MAX, 0, sizeof(MAX));
for(int i = 0; i <= n; i++)
pre[i] = i, G[i].clear(), G[i].push_back(i);
}
int Find(int x)
{
return (x == pre[x]) ? x : pre[x] = Find(pre[x]);
}
bool Union(int x, int y)
{
if(x != y){
pre[x] = y;
return true;
}
return false;
}
bool cmp(const Edge &a, const Edge &b)
{
return a.w < b.w;
}
void Kruskal()
{
sort(edge+1, edge+1+m, cmp);
init(n);
int sum = 0;
for(int i = 1; i <= m; i++) {
int u = Find(edge[i].u), v = Find(edge[i].v), w = edge[i].w;
if(Union(u, v)) {
sum += w, vis[i] = true;
for(int j = 0; j < G[u].size(); ++j)
for(int k = 0; k < G[v].size(); ++k)
MAX[G[u][j]][G[v][k]] = MAX[G[v][k]][G[u][j]] = w;
for(int j = 0; j < G[u].size(); ++j)
G[v].push_back(G[u][j]); //u为v的 子树
}
}
int Ssum = inf;
for(int i = 1; i <= m; ++i)
if(!vis[i])
Ssum = min(Ssum, sum+edge[i].w-MAX[edge[i].u][edge[i].v]);
cout << sum << " " << Ssum << endl;
}
int main()
{
int T;
cin >> T;
while(T--){
cin >> n >> m;
for(int i = 1; i <= m; ++i)
cin >> edge[i].u >> edge[i].v >> edge[i].w;
Kruskal();
}
return 0;
}