#!/bin/env python3
import re
class Molecule(object):
SPLIT_RE = re.compile(
'(?P<element>[A-Z][a-z]?)(?P<count>\d*)|'
'(?P<open>\()|'
'\)(?P<close>\d+)|'
'.'
)
def __init__(self, *, elements):
self._elements = elements
@classmethod
def from_string(cls, s):
molecule, remainder = cls.from_tokens(
list(cls.SPLIT_RE.finditer(s))
)
if remainder is not None:
raise ValueError
return molecule
@classmethod
def from_tokens(cls, tokens):
molecule = cls(elements=dict())
while tokens:
for ind, t in enumerate(tokens):
if t.group('element'):
molecule += cls.from_token(t)
elif t.group('open'):
next_, remainder = cls.from_tokens(tokens[ind+1:])
next_count = int(remainder[0].group('close'))
next_ *= next_count
molecule += next_
tokens = remainder[1:]
break
elif t.group('close'):
return molecule, tokens[ind:]
else:
raise ValueError
else:
tokens = False
return molecule, None
@classmethod
def from_token(cls, token):
return cls(
elements={
token.group('element'): int('0'+token.group('count')) or 1
}
)
def __mul__(self, other):
if isinstance(other, int):
return Molecule(
elements={
ele: count * other
for ele, count in self._elements.items()
}
)
else:
raise TypeError
def __add__(self, other):
if isinstance(other, Molecule):
elements = self._elements.copy()
for ele, count in other._elements.items():
elements[ele] = elements.get(ele, 0) + count
return Molecule(
elements=elements
)
elif other is None:
return self
else:
raise TypeError
def __str__(self):
return '\n'.join(
'{k} -> {v}'.format(k=k, v=v)
for k, v in self._elements.items()
)
def main():
import sys
for line in sys.stdin:
m = Molecule.from_string(line)
print(m)
if __name__ == '__main__':
main()
IyEvYmluL2VudiBweXRob24zCgppbXBvcnQgcmUKCgpjbGFzcyBNb2xlY3VsZShvYmplY3QpOgogICAgU1BMSVRfUkUgPSByZS5jb21waWxlKAogICAgICAgICcoP1A8ZWxlbWVudD5bQS1aXVthLXpdPykoP1A8Y291bnQ+XGQqKXwnCiAgICAgICAgJyg/UDxvcGVuPlwoKXwnCiAgICAgICAgJ1wpKD9QPGNsb3NlPlxkKyl8JwogICAgICAgICcuJwogICAgKQoKICAgIGRlZiBfX2luaXRfXyhzZWxmLCAqLCBlbGVtZW50cyk6CiAgICAgICAgc2VsZi5fZWxlbWVudHMgPSBlbGVtZW50cwoKICAgIEBjbGFzc21ldGhvZAogICAgZGVmIGZyb21fc3RyaW5nKGNscywgcyk6CiAgICAgICAgbW9sZWN1bGUsIHJlbWFpbmRlciA9IGNscy5mcm9tX3Rva2VucygKICAgICAgICAgICAgbGlzdChjbHMuU1BMSVRfUkUuZmluZGl0ZXIocykpCiAgICAgICAgKQogICAgICAgIGlmIHJlbWFpbmRlciBpcyBub3QgTm9uZToKICAgICAgICAgICAgcmFpc2UgVmFsdWVFcnJvcgogICAgICAgIHJldHVybiBtb2xlY3VsZQoKICAgIEBjbGFzc21ldGhvZAogICAgZGVmIGZyb21fdG9rZW5zKGNscywgdG9rZW5zKToKICAgICAgICBtb2xlY3VsZSA9IGNscyhlbGVtZW50cz1kaWN0KCkpCiAgICAgICAgd2hpbGUgdG9rZW5zOgogICAgICAgICAgICBmb3IgaW5kLCB0IGluIGVudW1lcmF0ZSh0b2tlbnMpOgogICAgICAgICAgICAgICAgaWYgdC5ncm91cCgnZWxlbWVudCcpOgogICAgICAgICAgICAgICAgICAgIG1vbGVjdWxlICs9IGNscy5mcm9tX3Rva2VuKHQpCiAgICAgICAgICAgICAgICBlbGlmIHQuZ3JvdXAoJ29wZW4nKToKICAgICAgICAgICAgICAgICAgICBuZXh0XywgcmVtYWluZGVyID0gY2xzLmZyb21fdG9rZW5zKHRva2Vuc1tpbmQrMTpdKQogICAgICAgICAgICAgICAgICAgIG5leHRfY291bnQgPSBpbnQocmVtYWluZGVyWzBdLmdyb3VwKCdjbG9zZScpKQogICAgICAgICAgICAgICAgICAgIG5leHRfICo9IG5leHRfY291bnQKICAgICAgICAgICAgICAgICAgICBtb2xlY3VsZSArPSBuZXh0XwogICAgICAgICAgICAgICAgICAgIHRva2VucyA9IHJlbWFpbmRlclsxOl0KICAgICAgICAgICAgICAgICAgICBicmVhawogICAgICAgICAgICAgICAgZWxpZiB0Lmdyb3VwKCdjbG9zZScpOgogICAgICAgICAgICAgICAgICAgIHJldHVybiBtb2xlY3VsZSwgdG9rZW5zW2luZDpdCiAgICAgICAgICAgICAgICBlbHNlOgogICAgICAgICAgICAgICAgICAgIHJhaXNlIFZhbHVlRXJyb3IKICAgICAgICAgICAgZWxzZToKICAgICAgICAgICAgICAgIHRva2VucyA9IEZhbHNlCiAgICAgICAgcmV0dXJuIG1vbGVjdWxlLCBOb25lCgogICAgQGNsYXNzbWV0aG9kCiAgICBkZWYgZnJvbV90b2tlbihjbHMsIHRva2VuKToKICAgICAgICByZXR1cm4gY2xzKAogICAgICAgICAgICBlbGVtZW50cz17CiAgICAgICAgICAgICAgICB0b2tlbi5ncm91cCgnZWxlbWVudCcpOiBpbnQoJzAnK3Rva2VuLmdyb3VwKCdjb3VudCcpKSBvciAxCiAgICAgICAgICAgIH0KICAgICAgICApCgogICAgZGVmIF9fbXVsX18oc2VsZiwgb3RoZXIpOgogICAgICAgIGlmIGlzaW5zdGFuY2Uob3RoZXIsIGludCk6CiAgICAgICAgICAgIHJldHVybiBNb2xlY3VsZSgKICAgICAgICAgICAgICAgIGVsZW1lbnRzPXsKICAgICAgICAgICAgICAgICAgICBlbGU6IGNvdW50ICogb3RoZXIKICAgICAgICAgICAgICAgICAgICBmb3IgZWxlLCBjb3VudCBpbiBzZWxmLl9lbGVtZW50cy5pdGVtcygpCiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICkKICAgICAgICBlbHNlOgogICAgICAgICAgICByYWlzZSBUeXBlRXJyb3IKCiAgICBkZWYgX19hZGRfXyhzZWxmLCBvdGhlcik6CiAgICAgICAgaWYgaXNpbnN0YW5jZShvdGhlciwgTW9sZWN1bGUpOgogICAgICAgICAgICBlbGVtZW50cyA9IHNlbGYuX2VsZW1lbnRzLmNvcHkoKQogICAgICAgICAgICBmb3IgZWxlLCBjb3VudCBpbiBvdGhlci5fZWxlbWVudHMuaXRlbXMoKToKICAgICAgICAgICAgICAgIGVsZW1lbnRzW2VsZV0gPSBlbGVtZW50cy5nZXQoZWxlLCAwKSArIGNvdW50CiAgICAgICAgICAgIHJldHVybiBNb2xlY3VsZSgKICAgICAgICAgICAgICAgIGVsZW1lbnRzPWVsZW1lbnRzCiAgICAgICAgICAgICkKICAgICAgICBlbGlmIG90aGVyIGlzIE5vbmU6CiAgICAgICAgICAgIHJldHVybiBzZWxmCiAgICAgICAgZWxzZToKICAgICAgICAgICAgcmFpc2UgVHlwZUVycm9yCgogICAgZGVmIF9fc3RyX18oc2VsZik6CiAgICAgICAgcmV0dXJuICdcbicuam9pbigKICAgICAgICAgICAgJ3trfSAtPiB7dn0nLmZvcm1hdChrPWssIHY9dikKICAgICAgICAgICAgZm9yIGssIHYgaW4gc2VsZi5fZWxlbWVudHMuaXRlbXMoKQogICAgICAgICkKCgpkZWYgbWFpbigpOgogICAgaW1wb3J0IHN5cwogICAgZm9yIGxpbmUgaW4gc3lzLnN0ZGluOgogICAgICAgIG0gPSBNb2xlY3VsZS5mcm9tX3N0cmluZyhsaW5lKQogICAgICAgIHByaW50KG0pCgoKaWYgX19uYW1lX18gPT0gJ19fbWFpbl9fJzoKICAgIG1haW4oKQ==