//! returns: 0

/// Keep eight distinct values live across branch and loop edges.
fn run(limit: u32) -> u32 {
    let mut a: u32 = 1;
    let mut b: u32 = 2;
    let mut c: u32 = 3;
    let mut d: u32 = 4;
    let mut e: u32 = 5;
    let mut f: u32 = 6;
    let mut g: u32 = 7;
    let mut h: u32 = 8;
    let mut marker: u32 = 17;
    for index in 0..limit {
        let mut step: u32 = 0;
        match index % 3 {
            case 0 => { set step = 1; set marker = 17; }
            case 1 => { set step = 3; set marker = 17; }
            else => { set step = 5; set marker = 17; }
        }
        if index % 2 == 0 {
            set a += step;
            set c += step * 3;
            set e += step * 5;
            set g += step * 7;
        } else {
            set b += step * 2;
            set d += step * 4;
            set f += step * 6;
            set h += step * 8;
        }
        assert marker == 17;
    }
    assert marker == 17;
    return a + 2*b + 3*c + 4*d + 5*e + 6*f + 7*g + 8*h;
}

/// Compute the weighted sum without carrying the eight separate values.
fn expected(limit: u32) -> u32 {
    let mut result: u32 = 204;
    for index in 0..limit {
        let step = 1 + (index % 3) * 2;
        set result += step * (84 if index % 2 == 0 else 120);
    }
    return result;
}

/// First function value carried through a control-flow edge.
fn addOne(value: u32) -> u32 {
    return value + 1;
}

/// Second function value carried through a control-flow edge.
fn addThree(value: u32) -> u32 {
    return value + 3;
}

/// Preserve symbol-valued arguments alongside loop-carried results.
fn dispatch(limit: u32) -> u32 {
    let mut operation = addOne;
    let mut result: u32 = 0;
    for index in 0..limit {
        if index % 2 == 0 {
            set operation = addOne;
        } else {
            set operation = addThree;
        }
        set result += operation(index);
    }
    return result;
}

/// Check empty, single-iteration, and repeatedly merged control flow.
@default fn main() -> u32 {
    for limit in 0..20 {
        assert run(limit) == expected(limit);
        assert dispatch(limit) == limit * (limit + 1) / 2 + 2 * (limit / 2);
    }
    return 0;
}
