package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	var input []string
	scanner := bufio.NewScanner(os.Stdin)
	for scanner.Scan() {
		input = append(input, scanner.Text())
	}

	transforms := make(map[string][]string)
	var stMolecule string

	for i := range input {
		if strings.Contains(input[i], "=>") {
			parts := strings.Split(input[i], " => ")
			transforms[parts[0]] = append(transforms[parts[0]], parts[1])
		} else if len(input[i]) > 0 {
			// Must be the calibration molecule
			stMolecule = input[i]
		}
	}

	fmt.Println("*** CALIBRATION ***")
	res := calibrate(stMolecule, transforms)
	fmt.Println("Found " + strconv.Itoa(len(res)) + " combinations")
	fmt.Println("\n")
	fmt.Println("*** MANUFACTURING ***")
	backTrans := reverseTransforms(transforms)
	manufacture(stMolecule, backTrans, 0)
}

// stepBack takes an ending string and a reversed list of the transformations
func manufacture(molecule string, transforms map[string][]string, steps int) {
	fmt.Println(molecule)

	if molecule == "e" {
		fmt.Println("Found in " + strconv.Itoa(steps) + " steps")
		os.Exit(0)
	}

	for k, v := range transforms {
		if strings.Contains(molecule, k) {
			for i := range v {
				tstVal := strings.Replace(molecule, k, v[i], 1)
				manufacture(tstVal, transforms, steps+1)
			}
		}
	}
}

func reverseTransforms(t map[string][]string) map[string][]string {
	ret := make(map[string][]string)
	for v := range t {
		for j := range t[v] {
			ret[t[v][j]] = append(ret[t[v][j]], v)
		}
	}
	return ret
}

func calibrate(molecule string, transforms map[string][]string) []string {
	var ret []string
	atoms := getAtomsFromMolecule(molecule)

	for i := range atoms {
		// Build a string of atoms before i
		start := strings.Join(atoms[:i], "")
		// And all after i too
		end := strings.Join(atoms[i+1:], "")
		// Find all transforms for atoms[i]
		if v, ok := transforms[atoms[i]]; ok {
			for j := range v {
				ret = uniqueInsert(ret, start+v[j]+end)
				//ret = append(ret, start+v[j]+end)
			}
		}
	}
	return ret
}

func getAtomsFromMolecule(molecule string) []string {
	var atoms []string
	var bldAtom string

	for i := range molecule {
		if molecule[i] >= 'A' && molecule[i] <= 'Z' {
			if len(bldAtom) > 0 {
				atoms = append(atoms, bldAtom)
			}
			bldAtom = string(molecule[i])
			continue
		}
		bldAtom = bldAtom + string(molecule[i])
	}
	return append(atoms, bldAtom)
}

func uniqueInsert(curr []string, ins string) []string {
	for i := range curr {
		if curr[i] == ins {
			return curr
		}
	}
	return append(curr, ins)
}

func mustAtoi(s string) int {
	var i int
	var err error
	if i, err = strconv.Atoi(s); err != nil {
		fmt.Println("Tried to atoi " + s)
		os.Exit(1)
	}
	return i
}
