#include <bits/stdc++.h>
#define REP(i,n) for(int i=0; i<(int)(n); ++i)

using namespace std;

typedef long long LL;
LL gcd(LL a, LL b){ return (a % b == 0 ? b : gcd(b, a % b)); }
LL lcm(LL a, LL b){ return a * b / gcd(a, b); }

struct Rational{
    // p : 分子 q : 分母
    LL p, q;
    void normalize(){
        if(q < 0) {
            p *= -1;
            q *= -1;
        }
        LL d = gcd(abs(p), q);
        if(d == 0){
            p = 0;
            q = 1;
        }else{
            p /= d;
            q /= d;
        }
    }
    Rational() : p(1), q(1) {}
    Rational(LL p, LL q) : p(p), q(q) {
        normalize();
    }

};

Rational operator + (const Rational& a, const Rational& b){
    return Rational(a.p * b.q + b.p * a.q, a.q * b.q);
}

Rational operator - (const Rational& a, const Rational& b){
    return Rational(a.p * b.q - b.p * a.q, a.q * b.q);
}

Rational operator * (const Rational& a, const Rational& b){
    return Rational(a.p * b.p, a.q * b.q);
}

Rational operator / (const Rational& a, const Rational& b){
    return Rational(a.p * b.q, a.q * b.p);
}

Rational abs(Rational a){
    return Rational(abs(a.p), a.q);
}

bool operator == (const Rational& a, const Rational& b){
    return (a.p == b.p) && (a.q == b.q);
}

bool operator < (const Rational& a, const Rational& b){
    // overflowを避けるためにlong doubleを用いる
    return (long double) a.p * b.q < (long double) b.p * a.q;
}
bool operator > (const Rational& a, const Rational& b){
    return b < a;
}

typedef vector<Rational> Vec;

typedef vector<Vec> Mat;

struct Result{
    map<string, int> val;
    int p;
    Result(map<string, int> val, int p) : val(val), p(p) {}
};

void pass(string s, int& p, char c){
    assert(s[p++] == c);
}

struct Parser{
    pair<Mat, Vec> parse(string s){
        int p = 0;
        vector< map<string, int> > moles;
        vector<string> elems;
        bool right = false;
        while(p < s.size() && s[p] != '.'){
            Result r = molecule(s, p);
            if(right) for(auto& p : r.val) p.second *= -1;
            moles.push_back(r.val);
            for(auto& pv : r.val) elems.push_back(pv.first);
            p = r.p;
            if(s[p] == '-'){
                right = true;
            }
            while(s[p] == '-' || s[p] == '>' || s[p] == '+') p++;
        }

        sort(elems.begin(), elems.end());
        elems.erase( unique(elems.begin(), elems.end()), elems.end() );
        // 最後の分子式の係数を1に固定
        for(auto& p : moles.back()){
            p.second *= -1;
        }

        Mat M( elems.size(), Vec(moles.size() - 1));
        Vec B( elems.size() );

        //       mole1, mole2, 
        // elem1
        // elem2
        
        for(int y = 0; y < elems.size(); y++){
            for(int x = 0; x < moles.size() - 1; x++){
                M[y][x] = Rational(moles[x][ elems[y] ], 1);
            }

            B[y] = Rational(moles.back()[ elems[y] ], 1);
        }

        return make_pair(M, B);
    }
    Result molecule(string& s, int p){
        map<string, int> val;
        while(isalpha(s[p]) || s[p] == '('){
            Result r = group(s, p);
            p = r.p;
            for(auto& p : r.val){
                val[p.first] += p.second;
            }
        }
        return Result(val, p);
    }
    Result group(string& s, int p){
        Result r = unit_group(s, p);
        if(isdigit(s[r.p])){
            int n = 0;
            while(isdigit(s[r.p])){
                n = n * 10 + s[r.p++] - '0';
            }
            for(auto& p : r.val){
                p.second *= n;
            }
        }
        return r;
    }
    Result unit_group(string& s, int p){
        if(isalpha(s[p])){
            string elem;
            elem += s[p++];
            if(islower(s[p])){
                elem += s[p++];
            }
            map<string, int> val;
            val[elem] = 1;
            return Result(val, p);
        }else if(s[p] == '('){
            Result r = molecule(s, p + 1);
            pass(s, r.p, ')');
            return r;
        }else{
            assert(false);
        }
    }
};

const double EPS = 1e-8;

Vec gauss_jordan(const Mat& A, const Vec& b){
    int W = A[0].size();
    int H = A.size();

    Mat B(H, Vec(W + 1));
    for(int y = 0; y < H; y++)
        for(int x = 0; x < W; x++)
            B[y][x] = A[y][x];
    for(int y = 0; y < H; y++)
        B[y][W] = b[y];

    bool unique = true; // 解が一意かどうか
    int cy = 0; // 現在注目している式

    // 現在注目している変数
    for(int x = 0; x < W; x++){
        int pivot = cy;
        // 注目している変数の係数の絶対値が一番大きい式を選ぶ
        for(int y = cy; y < H; y++){
            if(abs(B[y][x]) > abs(B[pivot][x])) pivot = y;
        }

        // 解が一意でないか，解が存在しない
        if(pivot >= H || B[pivot][x] == Rational(0, 1)) {
            unique = false;
            continue;
        }

        swap(B[cy], B[pivot]);

        // 注目している変数の係数を1にする
        for(int x2 = x + 1; x2 <= W; x2++) {
            B[cy][x2] = B[cy][x2] / B[cy][x];
        }

        // y番目の式からx2番目の変数を消去
        for(int y = 0; y < H; y++) if(y != cy)
            for(int x2 = x + 1; x2 <= W; x2++)
                B[y][x2] = B[y][x2] - B[y][x] * B[cy][x2];

        // 次の式に注目する
        cy++;
    }


    // 解が存在するかどうか
    for(int y = cy; y < H; y++){
        if(B[y][W] > Rational(0, 1))
            return Vec();
    }

    // 解が複数存在するかどうか
    if(!unique) {
        return Vec();
    }

    // 一意な解を返す
    Vec V(W);
    int cur_x = 0;
    for(int y = 0; y < H; y++){
        if(abs(B[y][cur_x]) > Rational(0, 1)){
            V[cur_x++] = B[y][W];
        }
    }
    return V;
}
int main(){
    string s;
    while(cin >> s){
        if(s == ".") break;
        pair<Mat, Vec> p = Parser().parse(s);
        Vec V = gauss_jordan(p.first, p.second);
        LL d = 1;
        for(int i = 0; i < V.size(); i++){
            d = lcm(d, V[i].q);
        }
        for(int i = 0; i < V.size(); i++){
            cout << (V[i] * Rational(d, 1)).p << " ";
        }
        cout << d << endl;
    }
    return 0;
}

Measured memory_bytes: 1268