You are an expert in writing code in many different languages. Your goal is to perform code completion for the following code, keeping in mind the rest of the code and the file meta data.
    Metadata details:
    filename: const_propagation.rs
    path of file: ./repos/logicsim/src/graph/optimizations
    
    the code of the file until where you have to start completion: use super::super::{gate::*, graph_builder::GateGraphBuilder};
use GateType::*;

fn find_replacement(
    g: &mut GateGraphBuilder,
    idx: GateIndex,
    on: bool,
    short_circuit: GateIndex,
    negated: bool,
) -> Option<GateIndex> {
    let short_circuit_output = if negated {
        short_circuit
            .opposite_if_const()
            .expect("short_circuit should be const")
    } else {
        short_circuit
    };

    if on == short_circuit.is_on() {
        return Some(short_circuit_output);
    }

    let dependencies_len = g.get(idx).dependencies.len();
    if dependencies_len == 1 {
        return Some(if negated ^ on { ON } else { OFF });
    }

    let mut non_const_dependency = None;
    for (i, dependency) in g.get(idx).dependencies.iter().copied().enumerate() {
        if dependency == short_circuit {
            return Some(short_circuit_output);
        }
        if !dependency.is_const() {
            non_const_dependency = Some((dependency, i))
        }
    }
    if let Some((non_const_dependency, i)) = non_const_dependency {
        if dependencies_len == 2 {
            if negated {
                let removed_dep_idx = if i == 0 { 1 } else { 0 };
                let removed_dep = g.get(idx).dependencies[removed_dep_idx];
                g.get_mut(removed_dep).dependents.remove(&idx);

                let gate = g.get_mut(idx);
                gate.ty = Not;
                gate.dependencies.remove(removed_dep_idx);
                return None;
            } else {
                return Some(non_const_dependency);
    
pub fn const_propagation_pass(g: &mut GateGraphBuilder) {
    // Allocated outside main loop.
    let mut temp_dependents = Vec::new();
    let mut temp_dependencies = Vec::new();

    struct WorkItem {
        idx: GateIndex,
        on: bool,
    }

    let off = g.get_mut(OFF);

    let mut work: Vec<_> = off
        .dependents
        .drain(0..off.dependents.len())
        .map(|idx| WorkItem { idx, on: false })
        .collect();

    let on = g.get_mut(ON);

    work.extend(
        on.dependents
            .drain(0..on.dependents.len())
            .map(|idx| WorkItem { idx, on: true }),
    );

    for (i, gate) in g.nodes.iter() {
        if !GateIndex::from(i).is_const() && !gate.ty.is_lever() && gate.dependencies.is_empty() {
            work.extend(
                gate.dependents
                    .iter()
                    .copied()
                    .map(|idx| WorkItem { idx, on: false }),
            )
        }
    }

    while let Some(WorkItem { idx, on }) = work.pop() {
        if g.nodes.get(idx.into()).is_none() {
            continue;
        }
        if g.is_observable(idx) {
            continue;
        }

        let gate_type = &g.get(idx).ty;
        let replacement = match gate_type {
            Off | On | Lever => unreachable!("Off, On, and lever nodes have no dependencies"),
            Not => Some(if on { OFF } else { ON }),
            And => find_replacement(g, idx, on, OFF, false),
            Nand => find_replacement(g, idx, on, OFF, true),
            Or => find_replacement(g, idx, on, ON, false),
            Nor => find_replacement(g, idx, on, ON, true),
            Xor => find_replacement_xor(g, idx, on, false),
            Xnor => find_replacement_xor(g, idx, on, true),
        };
        if let Some(replacement) = replacement {
            temp_dependents.extend(&g.get(idx).dependents);
            temp_dependencies.extend_from_slice(&g.get(idx).dependencies);

            for dependency in temp_dependencies.drain(0..temp_dependencies.len()) {
                let dependency_dependents = &mut g.get_mut(dependency).dependents;
                dependency_dependents.remove(&idx);
            }

            if replacement.is_const() {
                work.extend(temp_dependents.iter().copied().map(|idx| WorkItem {
                    idx,
                    on: replacement.is_on(),
                }))
            }

            for dependent in temp_dependents.drain(0..temp_dependents.len()) {
                // A gate can have the same dependency many times in different dependency indexes.
                g.get_mut(dependent).swap_dependency(idx, replacement);
                g.nodes
                    .get_mut(replacement.into())
                    .unwrap()
                    .dependents
                    .insert(dependent);
            }

            g.nodes.remove(idx.into());
        }
    }
}