#include "bits/stdc++.h"
using namespace std;
using ll = long long; using ull = unsigned long long;
using vb = vector<bool>;
using vi = vector<int>; using vvi = vector<vector<int>>;
using vl = vector<ll>; using vvl = vector<vector<ll>>;
using pii = pair<int,int>; using pll = pair<ll,ll>;
#define FOR(i,a,b) for(ll i = (a); i < (ll)(b); i++)
#define REP(i,n) FOR(i,0,n)
#define ALL(obj) (obj).begin(), (obj).end()
#define rALL(obj) (obj).rbegin(), (obj).rend()
#define eb(val) emplace_back(val)
const double PI = acos(-1);
const double EPS = 1e-10;
const ll MOD = 1e9+7;
//#define int ll
void cioacc(){//accelerate cin/cout
  cin.tie(0);
  ios::sync_with_stdio(false);
}
struct edge{
  int to,wt,from;
  bool operator<(edge right){
    return wt < right.wt;
  }
};
using ve = vector<edge>;
using vve = vector<ve>;
struct union_find{//rank,経路圧縮で高速化
  vector<long long> data;//そのノードがrootであれば-（集合のサイズ）を表し、そうでなければ親ノードidを示す。
  union_find(long long size):data(size,-1){}//コンストラクタ,size個のノードを作成し、-1で初期化
  long long root(long long a){//根を探す,経路圧縮
    return data[a] < 0 ? a : data[a]=root(data[a]);
  }
  bool find(long long a,long long b){//a,bが同じ集合に属するか調べる
    return root(a) == root(b);
  }
  bool unite(long long a,long long b){//小さい方の木の根に大きい方の根を上書きする
    a = root(a),b = root(b);
    if(a==b) return false;//根が同じなら終了,uniteが失敗したことを出力
    if(data[a]<data[b]) swap(a,b); //集合サイズの大きい方の根に低い方の根をつける
    data[a] += data[b];//集合サイズを統合
    data[a] = b;//yの親にxをつける
    return true;//unite成功を出力
  }
  long long size(long long a){//aが属する集合のサイズを出力
    return -data[root(a)];
  }
};
int kruskal(int sz,ve &edges){
  sort(rALL(edges));
  union_find uf(sz);
  int res = 0;
  while(!edges.empty()){
    edge &e = edges.back();
    edges.pop_back();
    if(!uf.unite(e.to,e.from)) continue;
    res += e.wt;
  }
  return res;
}
signed main(){
  int v,e;
  while(cin >> v >> e,v){
    ve edges(e);
    REP(i,e){
      int nd1,nd2,wt;
      cin >> nd1 >> nd2 >> wt;
      edges[i]=(edge){nd1,wt,nd2};
    }
    cout << kruskal(v,edges) << endl;
  }
}
