use std::{io::{stdin, Read}, str::Chars, iter::Peekable, cmp::Ordering};

fn main() {
    let mut stdin = stdin();
    let mut buf = String::new();
    stdin.read_to_string(&mut buf).unwrap();

    // let mut c = 0; // part 1
    // let mut total = 0;

    // for i in buf.split("\n\n") {
    //     c += 1;
    //     let mut sp2 = i.split("\n");
    //     let left = parse(&mut sp2.next().unwrap().chars().peekable());
    //     let right = parse(&mut sp2.next().unwrap().chars().peekable());
    //     let comp = compare(&left, &right).unwrap();
    //     if comp { total += c; }
    // }

    // println!("{}", total);

    let mut split: Vec<&str> = buf.split("\n").collect(); // part 2. even uglier
    split.push("[[2]]");
    split.push("[[6]]");
    let mut items: Vec<Elem> = split.iter().map(|s| parse(&mut s.chars().peekable())).collect();
    items.sort_by(|a, b| match compare(&a, &b) {
        None => Ordering::Equal, 
        Some(false) => Ordering::Greater, 
        Some(true) => Ordering::Less
    });
    let s1 = items.iter().position(|n| is_sep(n, 2)).unwrap() + 1;
    let s2 = items.iter().position(|n| is_sep(n, 6)).unwrap() + 1;
    println!("{}*{} = {}", s1, s2, s1 * s2);
}

fn parse(a: &mut Peekable<Chars>) -> Elem { // i do not know how parser work
    let mut c = a.next().unwrap();
    if c == '[' {
        let mut vec: Vec<Elem> = vec![];
        if *a.peek().unwrap() == ']' { // this is really ugly but i don't know how to make it better without accidentally eating some input
            a.next().unwrap();
        } else {
            while c != ']' {
                let n = parse(a);
                vec.push(n);
                c = a.next().unwrap();
            }
        }
        Elem::L(Box::new(vec))
    } else {
        let mut result: u32 = c.to_digit(10).unwrap();
        while match a.peek() {
            Some(c) => c.is_digit(10),
            None => false
        } {
            c = a.next().unwrap();
            result *= 10;
            result += c.to_digit(10).unwrap();
        }
        Elem::N(result)
    }
}

fn compare(l: &Elem, r: &Elem) -> Option<bool> {
    use Elem::*;
    match (l, r) { // fairly simple casework, badly written
        (N(a), N(b)) => {
            if a > b {
                Some(false)
            } else if a == b {
                None
            } else {
                Some(true)
            }
        }
        (L(a), L(b)) => {
            let mut i = 0;
            loop {
                if i >= a.len() && i >= b.len() {
                    return None;
                } else if i >= a.len() {
                    return Some(true);
                } else if i >= b.len() {
                    return Some(false);
                } else if let Some(b) = compare(&a[i], &b[i]) {
                    return Some(b);
                }
                i += 1;
            }           
        }
        (N(a), L(_)) => {
            compare(&Elem::L(Box::new(vec![Elem::N(*a)])), r)
        }
        (L(_), N(b)) => {
            compare(l, &Elem::L(Box::new(vec![Elem::N(*b)])))
        }
    }
}

fn is_sep(e: &Elem, n: u32) -> bool { // suffering,
    if let Elem::L(a) = e {
        if a.len() == 1 {
            let f = &a[0];
            if let Elem::L(b) = f {
                if b.len() == 1 {
                    let ff = &b[0];
                    if let Elem::N(nn) = ff { n == *nn } else { false } // i don't think it's possible to do this as an && chain cause of the use of if let. kinda sucks that rust does this
                } else { false }
            } else { false }
        } else { false }
    } else { false }
}

enum Elem {
    N(u32),
    L(Box<Vec<Elem>>) // i hate boxes
}