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: as_matmul.rs
    path of file: ./repos/tract/core/src/ops/einsum
    
    the code of the file until where you have to start completion: use tract_data::itertools::Itertools;
use tract_linalg::Scaler;
use tract_ndarray::Ix2;
use tract_num_traits::One;

use super::codegen::*;
use super::EinSum;
use crate::internal::*;

pub fn rewrite_einsums_as_matmul(model: &mut TypedModel) -> TractResult<()> {
    let rules = Rewriter::default().with_rul
    
    fn eval(&self, inputs: TVec<TValue>) -> TractResult<TVec<TValue>> {
        let (a, b) = args_2!(inputs);
        let output_shape = self.output_shape(a.shape(), b.shape());
        if let Some(qp) = self.quantize_output {
            let mut acc = Tensor::zero_dt(i32::datum_type(), &output_shape)?;
            let mut a_i32 = a.cast_to::<i32>()?.into_owned();
            a_i32.as_slice_mut::<i32>()?.iter_mut().for_each(|x| *x -= a.datum_type().zp_scale().0);
            let mut b_i32 = b.cast_to::<i32>()?.into_owned();
            b_i32.as_slice_mut::<i32>()?.iter_mut().for_each(|x| *x -= b.datum_type().zp_scale().0);
            self.mm::<i32>(&mut acc, &a_i32, &b_i32)?;
            let scale = a.datum_type().zp_scale().1 * b.datum_type().zp_scale().1 / qp.zp_scale().1;
            let scaler = Scaler::new(scale, tract_linalg::mmm::RoundingPolicy::Even);
            acc.to_array_view_mut::<i32>()?.iter_mut().for_each(|x| *x = *x * scaler);
            let mut c: Tensor = acc.cast_to_dt(qp.unquantized())?.into_owned();
            unsafe { c.set_datum_type(qp) };
            Ok(tvec!(c.into_tvalue()))
        } else {
            let mut c = Tensor::zero_dt(a.datum_type(), &output_shape)?;
            dispatch_floatlike!(Self::mm(c.datum_type())(self, &mut c, &a, &b))?;
            Ok(tvec!(c.into_tvalue()))
        }
    }