#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

// Union-Find
struct union_find{
	vector<int> par, rank;
	
	// コンストラクタで初期化
	union_find(int n){
		vector<int> v(n);
		par = v;
		rank = v;
		for(int i=0 ; i < n ; i++ ){
			par[i] = i;
			rank[i] = 0;
		}
	}
	// x の root を返す
	int find(int x){
		return ( par[x] == x )? x : (par[x] = find(par[x]) ) ;
	}
	// x と y の属する集合を併合する
	void unite(int x, int y){
		x = find(x);
		y = find(y);
		if( x == y ) return;
		
		if( rank[x] < rank[y] ){
			par[x] = y;
		}else{
			par[y] = x;
			if( rank[x] == rank[y] )
				rank[x]++;
		}
	}
	// x と y が同じ集合に属するかどうか
	bool same(int x, int y){
		return ( find(x) == find(y) );
	}
};

struct edge{
	int u,v,cost;
	edge(int u_, int v_, int cost_){
		u = u_;
		v = v_;
		cost = cost_;
	}
};

bool comp(const edge& e1, const edge& e2){
	return e1.cost < e2.cost;
}

// 辺集合
vector<edge> es;
// 頂点数
int V;

// 初期化
void init(int n){
	V = n;
	es.clear();
}

// 最小全域木を求める (クラスカル法)
int kruskal(){
	// コストの小さい順にソート
	sort( es.begin() , es.end() , comp );
	// 頂点数で初期化
	union_find uf( V );
	int res = 0;
	for(int i = 0 ; i < es.size() ; i++ ){
		edge e = es[i];
		if( !uf.same( e.u , e.v ) ){
			uf.unite( e.u , e.v );
			res += e.cost;
		}
	}
	return res;
}

// 重み cost の辺(u,v)の追加
void add_edge(int u, int v, int cost){
	edge e(u,v,cost);
	es.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 << kruskal() << endl;
	}
}