/*
 * 隣接リスト表現のグラフのプリム法 (優先度付きキューを使用)
 * 計算量は O(|E| log |V|)
 *
 */
#include <iostream>
#include <vector>
#include <map>
#include <queue>
#include <algorithm>
using namespace std;

const int MAX_V = 10000;
const int INF = 1e+8;
// first : コスト, second : 頂点番号
typedef pair<int,int> P;

struct edge{
	int to, cost;
	edge(int v, int cost_){
		to = v;
		cost = cost_;
	}
};

// 頂点数
int V;
// G[u][i] = ノード u に接続している i 番目の edge が返ってくる
vector<edge> G[MAX_V];
// 頂点の集合 X からの辺の最小コスト
int mincost[MAX_V];
// used[i] := 頂点 i が集合に含まれているか
bool used[MAX_V];

// 初期化
void init(int n){
	V = n;
	for(int i = 0 ; i < MAX_V ; i++ ){
		G[i].clear();
	}
}

// 最小全域木のコストの総和を返す (プリム法)
int prim(){
	// greater<P> を指定することで first が小さい順に取り出せる
	priority_queue<P, vector<P>, greater<P> > q;
	// 初期化
	fill( mincost , mincost+V , INF );
	fill( used , used+V , false );
	mincost[0] = 0;
	int res = 0;
	q.push( P(0,0) );
	
	while( !q.empty() ){
		P p = q.top(); q.pop();
		int v = p.second;
		
		if( mincost[v] < p.first ) continue;
		used[v] = true;
		res += mincost[v];
		
		for(int i=0 ; i < G[v].size() ; i++ ){
			edge e = G[v][i];
			if( !used[e.to] && mincost[e.to] > e.cost ){
				mincost[e.to] = e.cost;
				q.push( P(e.cost,e.to) );
			}
		}
	}
	return res;
}

// 重み cost の辺 (u,v) の追加
void add_edge(int u, int v, int cost){
	edge e( v , cost );
	G[u].push_back( e );
	// 有向グラフなら以下の処理は不要
	e.to = u;
	G[v].push_back( e );
}

int main(){
	int n,m;
	while( cin >> n >> m , n || m ){
		// 初期化
		init( n );
		for(int i = 0 ; i < m ; i++ ){
			int u, v, c ;
			cin >> u >> v >> c;
			add_edge( u , v , c );
		}
		cout << prim() << endl;
	}
}