use crate::tracing::pretty_print;
use std::fs::File;

use crate::{
    cpu::CPU,
    instruction_set::instruction_data::InstructionData,
    memory_manager::{
        MemoryStorable,
        type_traits::{FromFloat, UpcastDestTraits, UpcastSlice},
    },
    tracing::Trace,
    utility::{add_offset_r1, add_offset_rd},
};
use std::io::Write;

#[cfg(feature = "tracing")]
impl Trace {
    ///////////////////////////////////////////////////////////////
    /////////////////Scalar/register Instructions//////////////////
    ///////////////////////////////////////////////////////////////

    pub fn sldi_impl(&mut self, cores: &mut CPU, data: InstructionData, prefix: &'static str) {
        let (core_indx, rd, imm) = data.get_core_rd_imm();
        let core = cores.core(core_indx);
        let file: &mut File = self
            .out_files
            .get_mut(core_indx as usize)
            .expect("File at index not found");
        if prefix == "Pre" {
            writeln!(file, "Inst: sldi {} {}", rd, imm);
        } else {
            writeln!(file);
        }
        writeln!(
            file,
            "{} Register:\n\trd({}): {}",
            prefix,
            rd,
            core.register(rd)
        );
        if prefix == "Post" {
            writeln!(file, "\n###############################################\n");
        }
    }

    pub fn pre_sldi(&mut self, cores: &mut CPU, data: InstructionData) {
        self.sldi_impl(cores, data, "Pre");
    }

    pub fn post_sldi(&mut self, cores: &mut CPU, data: InstructionData) {
        self.sldi_impl(cores, data, "Post");
    }

    pub fn pre_sld(&mut self, cores: &mut CPU, data: InstructionData) {
        let (core_indx, rd, r1) = data.get_core_rd_r1();
        let file: &mut File = self
            .out_files
            .get_mut(core_indx as usize)
            .expect("File at index not found");
        let offset_value = data.offset_value();
        let core = cores.core(core_indx);
        let r1_val = core.register(r1);
        let host = cores.host();
        let num = host.load::<i32>((r1_val + offset_value), 4).unwrap()[0][0];
        let core = cores.core(core_indx);
        writeln!(file, "Inst: sld {} {} {{ {} }}", rd, r1, offset_value);
        writeln!(file, "\tPrev Register: rd: {}", core.register(rd));
        writeln!(file, "\tPrev Register: r1: {}", core.register(r1));
        writeln!(
            file,
            "\tPrev Memory: Host[{}:{}]: {}",
            r1_val + offset_value,
            r1_val + offset_value + 4,
            num
        );
    }

    pub fn post_sld(&mut self, cores: &mut CPU, data: InstructionData) {
        let (core_indx, rd, r1) = data.get_core_rd_r1();
        let file: &mut File = self
            .out_files
            .get_mut(core_indx as usize)
            .expect("File at index not found");
        let offset_value = data.offset_value();
        let core = cores.core(core_indx);
        let r1_val = core.register(r1);
        let host = cores.host();
        let num = host.load::<i32>((r1_val + offset_value), 4).unwrap()[0][0];
        let core = cores.core(core_indx);
        writeln!(file);
        writeln!(file, "\tPost Register: rd: {}", core.register(rd));
        writeln!(file, "\tPost Register: r1: {}", core.register(r1));
        writeln!(
            file,
            "\tPrev Memory: Host[{}:{}]: {}",
            r1_val + offset_value,
            r1_val + offset_value + 4,
            num
        );
        writeln!(file, "\n###############################################\n");
    }

    pub fn pre_sadd(&mut self, cores: &mut CPU, data: InstructionData) {
        let (core_indx, rd, r1, r2) = data.get_core_rd_r1_r2();
        let file: &mut File = self
            .out_files
            .get_mut(core_indx as usize)
            .expect("File at index not found");
        //    let core = cores.core(core_indx);
        //    let r1_val = core.register(r1);
        //    let r2_val = core.register(r2);
        //    core.set_register(rd, r1_val + r2_val);
        //    Ok(InstructionStatus::Completed)
        writeln!(file, "\t\tSADD\t\t");
    }

    pub fn post_sadd(&mut self, cores: &mut CPU, data: InstructionData) {
        let (core_indx, rd, r1, r2) = data.get_core_rd_r1_r2();
        let file: &mut File = self
            .out_files
            .get_mut(core_indx as usize)
            .expect("File at index not found");
        //    let core = cores.core(core_indx);
        //    let r1_val = core.register(r1);
        //    let r2_val = core.register(r2);
        //    core.set_register(rd, r1_val + r2_val);
        //    Ok(InstructionStatus::Completed)
    }

    pub fn pre_ssub(&mut self, cores: &mut CPU, data: InstructionData) {
        let (core_indx, rd, r1, r2) = data.get_core_rd_r1_r2();
        let file: &mut File = self
            .out_files
            .get_mut(core_indx as usize)
            .expect("File at index not found");
        //    let core = cores.core(core_indx);
        //    let r1_val = core.register(r1);
        //    let r2_val = core.register(r2);
        //    core.set_register(rd, r1_val - r2_val);
        writeln!(file, "\t\tSSUB\t\t");
    }

    pub fn post_ssub(&mut self, cores: &mut CPU, data: InstructionData) {
        let (core_indx, rd, r1, r2) = data.get_core_rd_r1_r2();
        let file: &mut File = self
            .out_files
            .get_mut(core_indx as usize)
            .expect("File at index not found");
        //    let core = cores.core(core_indx);
        //    let r1_val = core.register(r1);
        //    let r2_val = core.register(r2);
        //    core.set_register(rd, r1_val - r2_val);
    }

    pub fn pre_smul(&mut self, cores: &mut CPU, data: InstructionData) {
        let (core_indx, rd, r1, r2) = data.get_core_rd_r1_r2();
        let file: &mut File = self
            .out_files
            .get_mut(core_indx as usize)
            .expect("File at index not found");
        //    let core = cores.core(core_indx);
        //    let r1_val = core.register(r1);
        //    let r2_val = core.register(r2);
        //    core.set_register(rd, r1_val * r2_val);
        //    Ok(InstructionStatus::Completed)
        writeln!(file, "\t\tSMUL\t\t");
    }

    pub fn post_smul(&mut self, cores: &mut CPU, data: InstructionData) {
        let (core_indx, rd, r1, r2) = data.get_core_rd_r1_r2();
        let file: &mut File = self
            .out_files
            .get_mut(core_indx as usize)
            .expect("File at index not found");
        //    let core = cores.core(core_indx);
        //    let r1_val = core.register(r1);
        //    let r2_val = core.register(r2);
        //    core.set_register(rd, r1_val * r2_val);
        //    Ok(InstructionStatus::Completed)
    }

    pub fn pre_saddi(&mut self, cores: &mut CPU, data: InstructionData) {
        let (core_indx, rd, r1, imm) = data.get_core_rd_r1_imm();
        let file: &mut File = self
            .out_files
            .get_mut(core_indx as usize)
            .expect("File at index not found");
        //    let core = cores.core(core_indx);
        //    let r1_val = core.register(r1);
        //    core.set_register(rd, r1_val - imm);
        //    Ok(InstructionStatus::Completed)
        writeln!(file, "\t\tSADDI\t\t");
    }

    pub fn post_saddi(&mut self, cores: &mut CPU, data: InstructionData) {
        let (core_indx, rd, r1, imm) = data.get_core_rd_r1_imm();
        let file: &mut File = self
            .out_files
            .get_mut(core_indx as usize)
            .expect("File at index not found");
        //    let core = cores.core(core_indx);
        //    let r1_val = core.register(r1);
        //    core.set_register(rd, r1_val - imm);
        //    Ok(InstructionStatus::Completed)
    }

    pub fn pre_smuli(&mut self, cores: &mut CPU, data: InstructionData) {
        let (core_indx, rd, r1, imm) = data.get_core_rd_r1_imm();
        let file: &mut File = self
            .out_files
            .get_mut(core_indx as usize)
            .expect("File at index not found");
        //    let core = cores.core(core_indx);
        //    let r1_val = core.register(r1);
        //    core.set_register(rd, r1_val * imm);
        //    Ok(InstructionStatus::Completed)
        writeln!(file, "\t\tSMULI\t\t");
    }

    pub fn post_smuli(&mut self, cores: &mut CPU, data: InstructionData) {
        let (core_indx, rd, r1, imm) = data.get_core_rd_r1_imm();
        let file: &mut File = self
            .out_files
            .get_mut(core_indx as usize)
            .expect("File at index not found");
        //    let core = cores.core(core_indx);
        //    let r1_val = core.register(r1);
        //    core.set_register(rd, r1_val * imm);
        //    Ok(InstructionStatus::Completed)
    }

    /////////////////////////////////////////////////////////////////
    ///////////////////Matrix/vector Instructions////////////////////
    /////////////////////////////////////////////////////////////////

    pub fn pre_setbw(&mut self, cores: &mut CPU, data: InstructionData) {
        let (core_indx, rd, r1, imm) = data.get_core_rd_r1_imm();
        let file: &mut File = self
            .out_files
            .get_mut(core_indx as usize)
            .expect("File at index not found");
        writeln!(file, "\t\tSETBW\t\t");
    }

    pub fn post_setbw(&mut self, cores: &mut CPU, data: InstructionData) {
        let (core_indx, rd, r1, imm) = data.get_core_rd_r1_imm();
        let file: &mut File = self
            .out_files
            .get_mut(core_indx as usize)
            .expect("File at index not found");
    }

    pub fn pre_mvm<F, M, T>(&mut self, cores: &mut CPU, data: InstructionData)
    where
        [F]: UpcastSlice<T> + UpcastSlice<M>,
        [M]: UpcastSlice<T>,
        T: UpcastDestTraits<T> + MemoryStorable,
        M: UpcastDestTraits<M> + MemoryStorable + FromFloat,
        F: UpcastDestTraits<F> + MemoryStorable,
    {
        self.mvm_impl::<F, M, T>(cores, data, "Pre");
    }

    pub fn post_mvm<F, M, T>(&mut self, cores: &mut CPU, data: InstructionData)
    where
        [F]: UpcastSlice<T> + UpcastSlice<M>,
        [M]: UpcastSlice<T>,
        T: UpcastDestTraits<T> + MemoryStorable,
        M: UpcastDestTraits<M> + MemoryStorable + FromFloat,
        F: UpcastDestTraits<F> + MemoryStorable,
    {
        self.mvm_impl::<F, M, T>(cores, data, "Post");
    }

    pub fn mvm_impl<F, M, T>(
        &mut self,
        cores: &mut CPU,
        data: InstructionData,
        prefix: &'static str,
    ) where
        [F]: UpcastSlice<T> + UpcastSlice<M>,
        [M]: UpcastSlice<T>,
        T: UpcastDestTraits<T> + MemoryStorable,
        M: UpcastDestTraits<M> + MemoryStorable + FromFloat,
        F: UpcastDestTraits<F> + MemoryStorable,
    {
        use crate::tracing::pretty_print;

        let (core_indx, rd, r1, mbiw, relu, group) = data.get_core_rd_r1_mbiw_immrelu_immgroup();
        let file: &mut File = self
            .out_files
            .get_mut(core_indx as usize)
            .expect("File at index not found");
        let group: usize = group.try_into().unwrap();
        let core = cores.core(core_indx);
        let r1_val = core.register(r1);
        let rd_val = core.register(rd);
        let (memory, crossbars) = core.get_memory_crossbar();
        let crossbar = crossbars.get_mut(group).unwrap();
        let crossbar_byte_width = crossbar.width();
        let crossbar_elem_width = crossbar_byte_width / size_of::<M>();
        let crossbar_height = crossbar.height();
        let crossbar_byte_size = crossbar_byte_width * crossbar_height;
        let loads = memory
            .reserve_load(r1_val, crossbar_height * size_of::<F>())
            .unwrap()
            .reserve_load(rd_val, crossbar_elem_width * size_of::<F>())
            .unwrap()
            .execute_load::<F>()
            .unwrap();
        let matrix = crossbar.load::<M>(crossbar_byte_size).unwrap()[0];

        if prefix == "Pre" {
            writeln!(
                file,
                "Inst: mvmul {} {} {} {} {} ",
                rd, r1, mbiw, relu, group
            );
        } else {
            writeln!(file);
        }
        writeln!(file, "{} Register:", prefix);
        writeln!(file, "\trd({}): {}", rd, rd_val);
        writeln!(file, "\trs1({}): {}", r1, r1_val);
        writeln!(file, "{} Immediate:", prefix);
        writeln!(file, "\tCrossbar size: {} bytes", crossbar_byte_size);
        writeln!(
            file,
            "\tVector size: {} bytes",
            crossbar_height * size_of::<F>()
        );
        writeln!(file, "\tmbiw: {} relu: {}", mbiw, relu);
        writeln!(file, "{} Memory:", prefix);
        write!(
            file,
            "\tLocal[{}:{}](A): ",
            r1_val,
            r1_val as usize + crossbar_height * size_of::<F>(),
        );
        pretty_print::print_slice::<_, F>(file, loads[0], 30);
        writeln!(file, "\tCrossbar[{}:{}](B): ", 0, crossbar_byte_size);
        pretty_print::print_slice::<_,F>(file, matrix, 30);
        writeln!(
            file,
            "\tLocal[{}:{}](out): ",
            rd_val,
            rd_val as usize + crossbar_elem_width * size_of::<F>()
        );
        pretty_print::print_slice::<_, F>(file, loads[1], 30);
        if prefix == "Post" {
            writeln!(file, "\n###############################################\n");
        }
    }

    pub fn vvadd_impl<F, T>(&mut self, cores: &mut CPU, data: InstructionData, prefix: &'static str)
    where
        [F]: UpcastSlice<T>,
        T: UpcastDestTraits<T> + MemoryStorable,
        F: UpcastDestTraits<F> + MemoryStorable,
    {
        use crate::{tracing::pretty_print, utility::add_offset_r2};

        let (core_indx, rd, r1, r2, imm_len, offset_select, offset_value) =
            data.get_core_rd_r1_r2_immlen_offset();
        let file: &mut File = self
            .out_files
            .get_mut(core_indx as usize)
            .expect("File at index not found");
        let core = cores.core(core_indx);
        let r1_val = core.register(r1);
        let r2_val = core.register(r2);
        let rd_val = core.register(rd);

        if prefix == "Pre" {
            writeln!(
                file,
                "Inst: vvadd {} {} {} {} {{{} {}}} ",
                rd, r1, r2, imm_len, offset_select, offset_value
            );
        } else {
            writeln!(file);
        }
        writeln!(file, "{} Register:", prefix);
        writeln!(file, "\trd({}): {}", rd, rd_val);
        writeln!(file, "\trs1({}): {}", r1, r1_val);
        writeln!(file, "\trs2({}): {}", r2, r2_val);
        writeln!(file, "{} Immediate:", prefix);
        writeln!(file, "\tLoad Length: {} bytes", imm_len);
        writeln!(
            file,
            "\toffset_select: {} offset_value: {}",
            offset_select, offset_value
        );
        let r1_final = add_offset_r1(r1_val, offset_select, offset_value);
        let r2_final = add_offset_r2(r2_val, offset_select, offset_value);
        let rd_final = add_offset_rd(rd_val, offset_select, offset_value);
        let imm_len: usize = imm_len.try_into().expect("imm_len can not be negative");
        let loads = core
            .reserve_load(r1_final, imm_len)
            .unwrap()
            .reserve_load(r2_final, imm_len)
            .unwrap()
            .reserve_load(rd_final, imm_len)
            .unwrap()
            .execute_load::<F>()
            .unwrap();
        writeln!(file, "{} Memory:", prefix);
        write!(file, "\tLocal[{}:{}](A): ", r1_final, r1_final + imm_len);
        pretty_print::print_slice::<_,f32>(file, loads[0], 30);
        write!(file, "\tLocal[{}:{}](B): ", r2_final , r2_final + imm_len);
        pretty_print::print_slice::<_,f32>(file, loads[1], 30);
        write!(file, "\tLocal[{}:{}](out): ", rd_final, rd_final+ imm_len);
        pretty_print::print_slice::<_,f32>(file, loads[2], 30);
        if prefix == "Post" {
            writeln!(file, "\n###############################################\n");
        }
    }

    pub fn pre_vvadd<F, T>(&mut self, cores: &mut CPU, data: InstructionData)
    where
        [F]: UpcastSlice<T>,
        T: UpcastDestTraits<T> + MemoryStorable,
        F: UpcastDestTraits<F> + MemoryStorable,
    {
        self.vvadd_impl::<F, T>(cores, data, "Pre");
    }

    pub fn post_vvadd<F, T>(&mut self, cores: &mut CPU, data: InstructionData)
    where
        [F]: UpcastSlice<T>,
        T: UpcastDestTraits<T> + MemoryStorable,
        F: UpcastDestTraits<F> + MemoryStorable,
    {
        self.vvadd_impl::<F, T>(cores, data, "Post");
    }

    pub fn pre_vvsub<F, T>(&mut self, cores: &mut CPU, data: InstructionData)
    where
        [F]: UpcastSlice<T>,
        T: UpcastDestTraits<T> + MemoryStorable,
        F: UpcastDestTraits<F> + MemoryStorable,
    {
        let (core_indx, rd, r1, r2, imm_len, offset_select, offset_value) =
            data.get_core_rd_r1_r2_immlen_offset();
        let file: &mut File = self
            .out_files
            .get_mut(core_indx as usize)
            .expect("File at index not found");
        //    let core = cores.core(core_indx);
        //    let r1_val = core.register(r1);
        //    let r2_val = core.register(r2);
        //    let rd_val = core.register(rd);
        //    let r1_val = add_offset_r1(r1_val, offset_select, offset_value);
        //    let r2_val = add_offset_r2(r2_val, offset_select, offset_value);
        //    let rd_val = add_offset_rd(rd_val, offset_select, offset_value);
        //    let imm_len: usize = imm_len.try_into().context("imm_len can not be negative")?;
        //
        //    let loads = core
        //        .reserve_load(r1_val, imm_len)?
        //        .reserve_load(r2_val, imm_len)?
        //        .execute_load::<F>()?;
        //    let (load1, load2) = (loads[0], loads[1]);
        //    let res: Vec<F> = load1
        //        .iter()
        //        .zip(load2.iter())
        //        .map(|(&a, &b)| a - b)
        //        .collect();
        //    ensure!(
        //        imm_len / size_of::<F>() == res.len(),
        //        "vvadd generate a vector bigger thant it's  requested elements"
        //    );
        //    let res_up: Cow<[T]> = res.as_slice().up();
        //    core.execute_store(rd_val, res_up.as_ref());
        //    Ok(InstructionStatus::Completed)
        writeln!(file, "\t\tVVSUB\t\t");
    }

    pub fn post_vvsub<F, T>(&mut self, cores: &mut CPU, data: InstructionData)
    where
        [F]: UpcastSlice<T>,
        T: UpcastDestTraits<T> + MemoryStorable,
        F: UpcastDestTraits<F> + MemoryStorable,
    {
        let (core_indx, rd, r1, r2, imm_len, offset_select, offset_value) =
            data.get_core_rd_r1_r2_immlen_offset();
        let file: &mut File = self
            .out_files
            .get_mut(core_indx as usize)
            .expect("File at index not found");
        //    let core = cores.core(core_indx);
        //    let r1_val = core.register(r1);
        //    let r2_val = core.register(r2);
        //    let rd_val = core.register(rd);
        //    let r1_val = add_offset_r1(r1_val, offset_select, offset_value);
        //    let r2_val = add_offset_r2(r2_val, offset_select, offset_value);
        //    let rd_val = add_offset_rd(rd_val, offset_select, offset_value);
        //    let imm_len: usize = imm_len.try_into().context("imm_len can not be negative")?;
        //
        //    let loads = core
        //        .reserve_load(r1_val, imm_len)?
        //        .reserve_load(r2_val, imm_len)?
        //        .execute_load::<F>()?;
        //    let (load1, load2) = (loads[0], loads[1]);
        //    let res: Vec<F> = load1
        //        .iter()
        //        .zip(load2.iter())
        //        .map(|(&a, &b)| a - b)
        //        .collect();
        //    ensure!(
        //        imm_len / size_of::<F>() == res.len(),
        //        "vvadd generate a vector bigger thant it's  requested elements"
        //    );
        //    let res_up: Cow<[T]> = res.as_slice().up();
        //    core.execute_store(rd_val, res_up.as_ref());
        //    Ok(InstructionStatus::Completed)
    }

    pub fn pre_vvmul<F, T>(&mut self, cores: &mut CPU, data: InstructionData)
    where
        [F]: UpcastSlice<T>,
        T: UpcastDestTraits<T> + MemoryStorable,
        F: UpcastDestTraits<F> + MemoryStorable,
    {
        let (core_indx, rd, r1, r2, imm_len, offset_select, offset_value) =
            data.get_core_rd_r1_r2_immlen_offset();
        let file: &mut File = self
            .out_files
            .get_mut(core_indx as usize)
            .expect("File at index not found");
        //    let core = cores.core(core_indx);
        //    let r1_val = core.register(r1);
        //    let r2_val = core.register(r2);
        //    let rd_val = core.register(rd);
        //    let r1_val = add_offset_r1(r1_val, offset_select, offset_value);
        //    let r2_val = add_offset_r2(r2_val, offset_select, offset_value);
        //    let rd_val = add_offset_rd(rd_val, offset_select, offset_value);
        //    let imm_len: usize = imm_len.try_into().context("imm_len can not be negative")?;
        //    let loads = core
        //        .reserve_load(r1_val, imm_len)?
        //        .reserve_load(r2_val, imm_len)?
        //        .execute_load::<F>()?;
        //    let (load1, load2) = (loads[0], loads[1]);
        //    let res: Vec<F> = load1
        //        .iter()
        //        .zip(load2.iter())
        //        .map(|(&a, &b)| a * b)
        //        .collect();
        //    ensure!(
        //        imm_len / size_of::<F>() == res.len(),
        //        "vvadd generate a vector bigger thant it's  requested elements"
        //    );
        //    let res_up: Cow<[T]> = res.as_slice().up();
        //    core.execute_store(rd_val, res_up.as_ref());
        //    Ok(InstructionStatus::Completed)
        writeln!(file, "\t\tVVMUL\t\t");
    }

    pub fn post_vvmul<F, T>(&mut self, cores: &mut CPU, data: InstructionData)
    where
        [F]: UpcastSlice<T>,
        T: UpcastDestTraits<T> + MemoryStorable,
        F: UpcastDestTraits<F> + MemoryStorable,
    {
        let (core_indx, rd, r1, r2, imm_len, offset_select, offset_value) =
            data.get_core_rd_r1_r2_immlen_offset();
        let file: &mut File = self
            .out_files
            .get_mut(core_indx as usize)
            .expect("File at index not found");
        //    let core = cores.core(core_indx);
        //    let r1_val = core.register(r1);
        //    let r2_val = core.register(r2);
        //    let rd_val = core.register(rd);
        //    let r1_val = add_offset_r1(r1_val, offset_select, offset_value);
        //    let r2_val = add_offset_r2(r2_val, offset_select, offset_value);
        //    let rd_val = add_offset_rd(rd_val, offset_select, offset_value);
        //    let imm_len: usize = imm_len.try_into().context("imm_len can not be negative")?;
        //    let loads = core
        //        .reserve_load(r1_val, imm_len)?
        //        .reserve_load(r2_val, imm_len)?
        //        .execute_load::<F>()?;
        //    let (load1, load2) = (loads[0], loads[1]);
        //    let res: Vec<F> = load1
        //        .iter()
        //        .zip(load2.iter())
        //        .map(|(&a, &b)| a * b)
        //        .collect();
        //    ensure!(
        //        imm_len / size_of::<F>() == res.len(),
        //        "vvadd generate a vector bigger thant it's  requested elements"
        //    );
        //    let res_up: Cow<[T]> = res.as_slice().up();
        //    core.execute_store(rd_val, res_up.as_ref());
        //    Ok(InstructionStatus::Completed)
    }

    pub fn pre_vvdmul<F, T>(&mut self, cores: &mut CPU, data: InstructionData)
    where
        [F]: UpcastSlice<T>,
        T: UpcastDestTraits<T> + MemoryStorable,
        F: UpcastDestTraits<F> + MemoryStorable,
    {
        let (core_indx, rd, r1, r2, imm_len, offset_select, offset_value) =
            data.get_core_rd_r1_r2_immlen_offset();
        let file: &mut File = self
            .out_files
            .get_mut(core_indx as usize)
            .expect("File at index not found");
        //    let core = cores.core(core_indx);
        //    let r1_val = core.register(r1);
        //    let r2_val = core.register(r2);
        //    let rd_val = core.register(rd);
        //    let r1_val = add_offset_r1(r1_val, offset_select, offset_value);
        //    let r2_val = add_offset_r2(r2_val, offset_select, offset_value);
        //    let loads = core
        //        .reserve_load(r1_val, imm_len)?
        //        .reserve_load(r2_val, imm_len)?
        //        .execute_load::<F>()?;
        //    let (load1, load2) = (loads[0], loads[1]);
        //    let res: [F; 1] = [load1
        //        .iter()
        //        .zip(load2.iter())
        //        .map(|(&a, &b)| a * b)
        //        .reduce(|a, b| a + b)
        //        .unwrap()];
        //
        //    let res_up: Cow<[T]> = res.as_slice().up();
        //    core.execute_store(rd_val, res_up.as_ref());
        //    Ok(InstructionStatus::Completed)
        writeln!(file, "\t\tVVDMUL\t\t");
    }

    pub fn post_vvdmul<F, T>(&mut self, cores: &mut CPU, data: InstructionData)
    where
        [F]: UpcastSlice<T>,
        T: UpcastDestTraits<T> + MemoryStorable,
        F: UpcastDestTraits<F> + MemoryStorable,
    {
        let (core_indx, rd, r1, r2, imm_len, offset_select, offset_value) =
            data.get_core_rd_r1_r2_immlen_offset();
        let file: &mut File = self
            .out_files
            .get_mut(core_indx as usize)
            .expect("File at index not found");
        //    let core = cores.core(core_indx);
        //    let r1_val = core.register(r1);
        //    let r2_val = core.register(r2);
        //    let rd_val = core.register(rd);
        //    let r1_val = add_offset_r1(r1_val, offset_select, offset_value);
        //    let r2_val = add_offset_r2(r2_val, offset_select, offset_value);
        //    let loads = core
        //        .reserve_load(r1_val, imm_len)?
        //        .reserve_load(r2_val, imm_len)?
        //        .execute_load::<F>()?;
        //    let (load1, load2) = (loads[0], loads[1]);
        //    let res: [F; 1] = [load1
        //        .iter()
        //        .zip(load2.iter())
        //        .map(|(&a, &b)| a * b)
        //        .reduce(|a, b| a + b)
        //        .unwrap()];
        //
        //    let res_up: Cow<[T]> = res.as_slice().up();
        //    core.execute_store(rd_val, res_up.as_ref());
        //    Ok(InstructionStatus::Completed)
    }

    pub fn pre_vvmax<F, T>(&mut self, cores: &mut CPU, data: InstructionData)
    where
        [F]: UpcastSlice<T>,
        T: UpcastDestTraits<T> + MemoryStorable,
        F: UpcastDestTraits<F> + MemoryStorable,
    {
        let (core_indx, rd, r1, r2, imm_len, offset_select, offset_value) =
            data.get_core_rd_r1_r2_immlen_offset();
        let file: &mut File = self
            .out_files
            .get_mut(core_indx as usize)
            .expect("File at index not found");
        //    let core = cores.core(core_indx);
        //    let r1_val = core.register(r1);
        //    let r2_val = core.register(r2);
        //    let rd_val = core.register(rd);
        //    let r1_val = add_offset_r1(r1_val, offset_select, offset_value);
        //    let r2_val = add_offset_r2(r2_val, offset_select, offset_value);
        //    let rd_val = add_offset_rd(rd_val, offset_select, offset_value);
        //
        //    let loads = core
        //        .reserve_load(r1_val, imm_len)?
        //        .reserve_load(r2_val, imm_len)?
        //        .execute_load::<F>()?;
        //    let (load1, load2) = (loads[0], loads[1]);
        //    let res: Vec<F> = load1
        //        .iter()
        //        .zip(load2.iter())
        //        .map(|(&a, &b)| if (a > b) { a } else { b })
        //        .collect();
        //
        //    let res_up: Cow<[T]> = res.as_slice().up();
        //    core.execute_store(rd_val, res_up.as_ref());
        //    Ok(InstructionStatus::Completed)
        writeln!(file, "\t\tVVMAX\t\t");
    }

    pub fn post_vvmax<F, T>(&mut self, cores: &mut CPU, data: InstructionData)
    where
        [F]: UpcastSlice<T>,
        T: UpcastDestTraits<T> + MemoryStorable,
        F: UpcastDestTraits<F> + MemoryStorable,
    {
        let (core_indx, rd, r1, r2, imm_len, offset_select, offset_value) =
            data.get_core_rd_r1_r2_immlen_offset();
        let file: &mut File = self
            .out_files
            .get_mut(core_indx as usize)
            .expect("File at index not found");
        //    let core = cores.core(core_indx);
        //    let r1_val = core.register(r1);
        //    let r2_val = core.register(r2);
        //    let rd_val = core.register(rd);
        //    let r1_val = add_offset_r1(r1_val, offset_select, offset_value);
        //    let r2_val = add_offset_r2(r2_val, offset_select, offset_value);
        //    let rd_val = add_offset_rd(rd_val, offset_select, offset_value);
        //
        //    let loads = core
        //        .reserve_load(r1_val, imm_len)?
        //        .reserve_load(r2_val, imm_len)?
        //        .execute_load::<F>()?;
        //    let (load1, load2) = (loads[0], loads[1]);
        //    let res: Vec<F> = load1
        //        .iter()
        //        .zip(load2.iter())
        //        .map(|(&a, &b)| if (a > b) { a } else { b })
        //        .collect();
        //
        //    let res_up: Cow<[T]> = res.as_slice().up();
        //    core.execute_store(rd_val, res_up.as_ref());
        //    Ok(InstructionStatus::Completed)
    }

    pub fn pre_vavg<F, T>(&mut self, cores: &mut CPU, data: InstructionData)
    where
        [F]: UpcastSlice<T>,
        T: UpcastDestTraits<T> + MemoryStorable,
        F: UpcastDestTraits<F> + MemoryStorable,
    {
        let (core_indx, rd, r1, r2, imm_len, offset_select, offset_value) =
            data.get_core_rd_r1_r2_immlen_offset();
        let file: &mut File = self
            .out_files
            .get_mut(core_indx as usize)
            .expect("File at index not found");
        //    let core = cores.core(core_indx);
        //    let r1_val = core.register(r1);
        //    let r2_val = r2;
        //    ensure!(r2_val == 1, "Stride different than 1 not supported");
        //    let rd_val = core.register(rd);
        //    let r1_val = add_offset_r1(r1_val, offset_select, offset_value);
        //    let loads = core.reserve_load(r1_val, imm_len)?.execute_load::<F>()?;
        //    let load1 = loads[0];
        //    let len = load1.len();
        //    let res: [F; _] =
        //        [load1.iter().copied().reduce(|a: F, b: F| a + b).unwrap() / F::from_usize(len)];
        //    let res_up: Cow<[T]> = res.as_slice().up();
        //    core.execute_store(rd_val, res_up.as_ref());
        //    Ok(InstructionStatus::Completed)
        writeln!(file, "\t\tVAVG\t\t");
    }

    pub fn post_vavg<F, T>(&mut self, cores: &mut CPU, data: InstructionData)
    where
        [F]: UpcastSlice<T>,
        T: UpcastDestTraits<T> + MemoryStorable,
        F: UpcastDestTraits<F> + MemoryStorable,
    {
        let (core_indx, rd, r1, r2, imm_len, offset_select, offset_value) =
            data.get_core_rd_r1_r2_immlen_offset();
        let file: &mut File = self
            .out_files
            .get_mut(core_indx as usize)
            .expect("File at index not found");
        //    let core = cores.core(core_indx);
        //    let r1_val = core.register(r1);
        //    let r2_val = r2;
        //    ensure!(r2_val == 1, "Stride different than 1 not supported");
        //    let rd_val = core.register(rd);
        //    let r1_val = add_offset_r1(r1_val, offset_select, offset_value);
        //    let loads = core.reserve_load(r1_val, imm_len)?.execute_load::<F>()?;
        //    let load1 = loads[0];
        //    let len = load1.len();
        //    let res: [F; _] =
        //        [load1.iter().copied().reduce(|a: F, b: F| a + b).unwrap() / F::from_usize(len)];
        //    let res_up: Cow<[T]> = res.as_slice().up();
        //    core.execute_store(rd_val, res_up.as_ref());
        //    Ok(InstructionStatus::Completed)
    }

    pub fn pre_vrelu<F, T>(&mut self, cores: &mut CPU, data: InstructionData)
    where
        [F]: UpcastSlice<T>,
        T: UpcastDestTraits<T> + MemoryStorable,
        F: UpcastDestTraits<F> + MemoryStorable + From<f32>,
    {
        let (core_indx, rd, r1, r2, imm_len, offset_select, offset_value) =
            data.get_core_rd_r1_r2_immlen_offset();
        let file: &mut File = self
            .out_files
            .get_mut(core_indx as usize)
            .expect("File at index not found");
        //    let core = cores.core(core_indx);
        //    let r1_val = core.register(r1);
        //    let rd_val = core.register(rd);
        //    let r1_val = add_offset_r1(r1_val, offset_select, offset_value);
        //    let rd_val = add_offset_rd(rd_val, offset_select, offset_value);
        //    let loads = core.reserve_load(r1_val, imm_len)?.execute_load::<F>()?;
        //    let load1 = loads[0];
        //    let res: Vec<F> = load1
        //        .iter()
        //        .map(|&a| if (a > 0.0.into()) { a } else { 0.0.into() })
        //        .collect();
        //
        //    let res_up: Cow<[T]> = res.as_slice().up();
        //    core.execute_store(rd_val, res_up.as_ref());
        //    Ok(InstructionStatus::Completed)
        writeln!(file, "\t\tVRELU\t\t");
    }

    pub fn post_vrelu<F, T>(&mut self, cores: &mut CPU, data: InstructionData)
    where
        [F]: UpcastSlice<T>,
        T: UpcastDestTraits<T> + MemoryStorable,
        F: UpcastDestTraits<F> + MemoryStorable + From<f32>,
    {
        let (core_indx, rd, r1, r2, imm_len, offset_select, offset_value) =
            data.get_core_rd_r1_r2_immlen_offset();
        //    let core = cores.core(core_indx);
        //    let r1_val = core.register(r1);
        //    let rd_val = core.register(rd);
        //    let r1_val = add_offset_r1(r1_val, offset_select, offset_value);
        //    let rd_val = add_offset_rd(rd_val, offset_select, offset_value);
        //    let loads = core.reserve_load(r1_val, imm_len)?.execute_load::<F>()?;
        //    let load1 = loads[0];
        //    let res: Vec<F> = load1
        //        .iter()
        //        .map(|&a| if (a > 0.0.into()) { a } else { 0.0.into() })
        //        .collect();
        //
        //    let res_up: Cow<[T]> = res.as_slice().up();
        //    core.execute_store(rd_val, res_up.as_ref());
        //    Ok(InstructionStatus::Completed)
    }

    pub fn pre_vtanh<F, T>(&mut self, cores: &mut CPU, data: InstructionData)
    where
        [F]: UpcastSlice<T>,
        T: UpcastDestTraits<T> + MemoryStorable,
        F: UpcastDestTraits<F> + MemoryStorable + From<f32>,
    {
        let (core_indx, rd, r1, r2, imm_len, offset_select, offset_value) =
            data.get_core_rd_r1_r2_immlen_offset();
        let file: &mut File = self
            .out_files
            .get_mut(core_indx as usize)
            .expect("File at index not found");
        //    let core = cores.core(core_indx);
        //    let r1_val = core.register(r1);
        //    let rd_val = core.register(rd);
        //    let r1_val = add_offset_r1(r1_val, offset_select, offset_value);
        //    let rd_val = add_offset_rd(rd_val, offset_select, offset_value);
        //
        //    let loads = core.reserve_load(r1_val, imm_len)?.execute_load::<F>()?;
        //    let load1 = loads[0];
        //    let res: Vec<F> = load1.iter().map(|&a| a.tanh()).collect();
        //
        //    let res_up: Cow<[T]> = res.as_slice().up();
        //    core.execute_store(rd_val, res_up.as_ref());
        //    Ok(InstructionStatus::Completed)
        writeln!(file, "\t\tVTANH\t\t");
    }

    pub fn post_vtanh<F, T>(&mut self, cores: &mut CPU, data: InstructionData)
    where
        [F]: UpcastSlice<T>,
        T: UpcastDestTraits<T> + MemoryStorable,
        F: UpcastDestTraits<F> + MemoryStorable + From<f32>,
    {
        let (core_indx, rd, r1, r2, imm_len, offset_select, offset_value) =
            data.get_core_rd_r1_r2_immlen_offset();
        let file: &mut File = self
            .out_files
            .get_mut(core_indx as usize)
            .expect("File at index not found");
        //    let core = cores.core(core_indx);
        //    let r1_val = core.register(r1);
        //    let rd_val = core.register(rd);
        //    let r1_val = add_offset_r1(r1_val, offset_select, offset_value);
        //    let rd_val = add_offset_rd(rd_val, offset_select, offset_value);
        //
        //    let loads = core.reserve_load(r1_val, imm_len)?.execute_load::<F>()?;
        //    let load1 = loads[0];
        //    let res: Vec<F> = load1.iter().map(|&a| a.tanh()).collect();
        //
        //    let res_up: Cow<[T]> = res.as_slice().up();
        //    core.execute_store(rd_val, res_up.as_ref());
        //    Ok(InstructionStatus::Completed)
    }

    pub fn pre_vsigm<F, T>(&mut self, cores: &mut CPU, data: InstructionData)
    where
        [F]: UpcastSlice<T>,
        T: UpcastDestTraits<T> + MemoryStorable,
        F: UpcastDestTraits<F> + MemoryStorable + From<f32>,
    {
        let (core_indx, rd, r1, r2, imm_len, offset_select, offset_value) =
            data.get_core_rd_r1_r2_immlen_offset();
        let file: &mut File = self
            .out_files
            .get_mut(core_indx as usize)
            .expect("File at index not found");
        //    let core = cores.core(core_indx);
        //    let r1_val = core.register(r1);
        //    let rd_val = core.register(rd);
        //    let r1_val = add_offset_r1(r1_val, offset_select, offset_value);
        //    let rd_val = add_offset_rd(rd_val, offset_select, offset_value);
        //    let loads = core.reserve_load(r1_val, imm_len)?.execute_load::<F>()?;
        //    let load1 = loads[0];
        //    let res: Vec<F> = load1.iter().map(|&a| a.sigm()).collect();
        //    let res_up: Cow<[T]> = res.as_slice().up();
        //    core.execute_store(rd_val, res_up.as_ref());
        //    Ok(InstructionStatus::Completed)
        writeln!(file, "\t\tVSIGM\t\t");
    }

    pub fn post_vsigm<F, T>(&mut self, cores: &mut CPU, data: InstructionData)
    where
        [F]: UpcastSlice<T>,
        T: UpcastDestTraits<T> + MemoryStorable,
        F: UpcastDestTraits<F> + MemoryStorable + From<f32>,
    {
        let (core_indx, rd, r1, r2, imm_len, offset_select, offset_value) =
            data.get_core_rd_r1_r2_immlen_offset();
        let file: &mut File = self
            .out_files
            .get_mut(core_indx as usize)
            .expect("File at index not found");
        //    let core = cores.core(core_indx);
        //    let r1_val = core.register(r1);
        //    let rd_val = core.register(rd);
        //    let r1_val = add_offset_r1(r1_val, offset_select, offset_value);
        //    let rd_val = add_offset_rd(rd_val, offset_select, offset_value);
        //    let loads = core.reserve_load(r1_val, imm_len)?.execute_load::<F>()?;
        //    let load1 = loads[0];
        //    let res: Vec<F> = load1.iter().map(|&a| a.sigm()).collect();
        //    let res_up: Cow<[T]> = res.as_slice().up();
        //    core.execute_store(rd_val, res_up.as_ref());
        //    Ok(InstructionStatus::Completed)
    }

    /////////////////////////////////////////////////////////////////
    /////Communication/synchronization Instructions/////////////////
    /////////////////////////////////////////////////////////////////

    pub fn ld_impl(&mut self, cores: &mut CPU, data: InstructionData, prefix: &'static str) {
        use crate::tracing::pretty_print;

        let (core, rd, r1, _, imm_len, offset_select, offset_value) =
            data.get_core_rd_r1_r2_immlen_offset();
        let file: &mut File = self
            .out_files
            .get_mut(core as usize)
            .expect("File at index not found");
        let (host, core) = cores.host_and_cores(core);
        let r1_val = core.register(r1);
        let rd_val = core.register(rd);
        if prefix == "Pre" {
            writeln!(
                file,
                "Inst: ld {} {} {} {{ {} {} }}",
                rd, r1, imm_len, offset_select, offset_value
            );
        } else {
            writeln!(file);
        }
        writeln!(file, "{} Register:", prefix);
        writeln!(file, "\trd({}): {}", rd, rd_val);
        writeln!(file, "\trs1({}): {}", r1, r1_val);
        writeln!(file, "{} Immediate:", prefix);
        writeln!(file, "\timm_size: {}", imm_len);
        writeln!(
            file,
            "\toffset_select {} offset_value {}",
            offset_select, offset_value
        );
        let r1_val = add_offset_r1(r1_val, offset_select, offset_value);
        let rd_val = add_offset_rd(rd_val, offset_select, offset_value);
        let global_memory = host.load::<u8>(r1_val, imm_len).unwrap();
        let core_memory = core.load::<u8>(rd_val, imm_len).unwrap();
        writeln!(file, "{} Memory:", prefix);
        writeln!(file, "\tHost[{}:{}]: ", r1_val, r1_val + imm_len as usize,);
        pretty_print::print_slice::<_,f32>(file, global_memory[0], 30);
        writeln!(file, "\tLocal[{}:{}]: ", rd_val, rd_val + imm_len as usize,);
        pretty_print::print_slice::<_,f32>(file, core_memory[0], 30);

        if prefix == "Post" {
            writeln!(file, "\n###############################################\n");
        }
    }

    pub fn post_ld(&mut self, cores: &mut CPU, data: InstructionData) {
        self.ld_impl(cores, data, "Post");
    }

    pub fn pre_ld(&mut self, cores: &mut CPU, data: InstructionData) {
        self.ld_impl(cores, data, "Pre");
    }

    pub fn st_impl(&mut self, cores: &mut CPU, data: InstructionData, prefix: &'static str) {
        use crate::tracing::pretty_print;

        let (core, rd, r1, _, imm_len, offset_select, offset_value) =
            data.get_core_rd_r1_r2_immlen_offset();
        let file: &mut File = self
            .out_files
            .get_mut(core as usize)
            .expect("File at index not found");
        let (host, core) = cores.host_and_cores(core);
        let r1_val = core.register(r1);
        let rd_val = core.register(rd);
        if prefix == "Pre" {
            writeln!(
                file,
                "Inst: st {} {} {} {{ {} {} }}",
                rd, r1, imm_len, offset_select, offset_value
            );
        } else {
            writeln!(file);
        }
        writeln!(file, "{} Register:", prefix);
        writeln!(file, "\trd({}): {}", rd, rd_val);
        writeln!(file, "\trs1({}): {}", r1, r1_val);
        writeln!(file, "{} Immediate:", prefix);
        writeln!(file, "\timm_size: {}", imm_len);
        writeln!(
            file,
            "\toffset_select {} offset_value {}",
            offset_select, offset_value
        );
        let r1_val = add_offset_r1(r1_val, offset_select, offset_value);
        let rd_val = add_offset_rd(rd_val, offset_select, offset_value);
        let core_memory = core.load::<u8>(r1_val, imm_len).unwrap();
        let global_memory = host.load::<u8>(rd_val, imm_len).unwrap();
        writeln!(file, "{} Memory:", prefix);
        writeln!(file, "\tLocal[{}:{}]: ", r1_val, r1_val + imm_len as usize,);
        pretty_print::print_slice::<_,f32>(file, core_memory[0], 30);
        writeln!(file, "\tHost[{}:{}]: ", rd_val, rd_val + imm_len as usize,);
        pretty_print::print_slice::<_,f32>(file, global_memory[0], 30);

        if prefix == "Post" {
            writeln!(file, "\n###############################################\n");
        }
    }

    pub fn post_st(&mut self, cores: &mut CPU, data: InstructionData) {
        self.st_impl(cores, data, "Post");
    }

    pub fn pre_st(&mut self, cores: &mut CPU, data: InstructionData) {
        self.st_impl(cores, data, "Pre");
    }


    pub fn pre_lldi(&mut self, cores: &mut CPU, data: InstructionData) {
        let (core, rd, imm) = data.get_core_rd_imm();
        let file: &mut File = self
            .out_files
            .get_mut(core as usize)
            .expect("File at index not found");
        //    let offset_value = data.offset_value();
        //    let imm_len = data.imm_len();
        //    let core = cores.core(core);
        //    let rd_val = core.register(rd);
        //    let rd_val = add_offset_rd(rd_val, 4, offset_value);
        //    core.memset(
        //        rd_val,
        //        imm_len,
        //        u8::try_from(imm).context("lldi with value bigger than u8")?,
        //    );
        //    Ok(InstructionStatus::Completed)
        writeln!(file, "\t\tLLDI\t\t");
    }

    pub fn post_lldi(&mut self, cores: &mut CPU, data: InstructionData) {
        let (core, rd, imm) = data.get_core_rd_imm();
        let file: &mut File = self
            .out_files
            .get_mut(core as usize)
            .expect("File at index not found");
        //    let offset_value = data.offset_value();
        //    let imm_len = data.imm_len();
        //    let core = cores.core(core);
        //    let rd_val = core.register(rd);
        //    let rd_val = add_offset_rd(rd_val, 4, offset_value);
        //    core.memset(
        //        rd_val,
        //        imm_len,
        //        u8::try_from(imm).context("lldi with value bigger than u8")?,
        //    );
        //    Ok(InstructionStatus::Completed)
    }

    fn lmv_impl (&mut self, cores: &mut CPU, data: InstructionData, prefix: &'static str) {
        use crate::tracing::pretty_print;

        let (core, rd, r1, _, imm_len, offset_select, offset_value) =
            data.get_core_rd_r1_r2_immlen_offset();
        let file: &mut File = self
            .out_files
            .get_mut(core as usize)
            .expect("File at index not found");
        let (host, core) = cores.host_and_cores(core);
        let r1_val = core.register(r1);
        let rd_val = core.register(rd);
        if prefix == "Pre" {
            writeln!(
                file,
                "Inst: lmv {} {} {} {{ {} {} }}",
                rd, r1, imm_len, offset_select, offset_value
            );
        } else {
            writeln!(file);
        }
        writeln!(file, "{} Register:", prefix);
        writeln!(file, "\trd({}): {}", rd, rd_val);
        writeln!(file, "\trs1({}): {}", r1, r1_val);
        writeln!(file, "{} Immediate:", prefix);
        writeln!(file, "\timm_size: {}", imm_len);
        writeln!(
            file,
            "\toffset_select {} offset_value {}",
            offset_select, offset_value
        );
        let r1_val = add_offset_r1(r1_val, offset_select, offset_value);
        let rd_val = add_offset_rd(rd_val, offset_select, offset_value);
        let core_memory = core
            .reserve_load(r1_val, imm_len).unwrap()
            .reserve_load(rd_val, imm_len).unwrap()
            .execute_load::<u8>().unwrap();
        writeln!(file, "{} Memory:", prefix);
        writeln!(file, "\tLocal[{}:{}]: ", r1_val, r1_val + imm_len as usize,);
        pretty_print::print_slice::<_,f32>(file, core_memory[0], 30);
        writeln!(file, "\tLocal[{}:{}]: ", rd_val, rd_val + imm_len as usize,);
        pretty_print::print_slice::<_,f32>(file, core_memory[1], 30);

        if prefix == "Post" {
            writeln!(file, "\n###############################################\n");
        }
    }

    pub fn pre_lmv(&mut self, cores: &mut CPU, data: InstructionData) {
        self.lmv_impl(cores, data, "Pre");
    }

    pub fn post_lmv(&mut self, cores: &mut CPU, data: InstructionData) {
        self.lmv_impl(cores, data, "Post");
    }

    pub fn pre_send(&mut self, cores: &mut CPU, data: InstructionData) {
        self.send_impl(cores, data, "Pre");
    }

    pub fn post_send(&mut self, cores: &mut CPU, data: InstructionData) {
        self.send_impl(cores, data, "Post");
    }

    pub fn send_impl(&mut self, cores: &mut CPU, data: InstructionData, prefix: &'static str) {
        let core_indx = data.core_indx();
        let r1 = data.r1();
        let imm_core = data.imm_core();
        let imm_len = data.imm_len();
        let offset_value = data.offset_value();
        let core = cores.core(core_indx);
        let file: &mut File = self
            .out_files
            .get_mut(core_indx as usize)
            .expect("File at index not found");
        let r1_val = core.register(r1);

        if prefix == "Pre" {
            writeln!(
                file,
                "Inst: send {} {} {} {} ",
                r1, imm_core, imm_len, offset_value
            );
        } else {
            writeln!(file);
        }
        writeln!(file, "{} Register:", prefix);
        writeln!(file, "\trs1({}): {}", r1, r1_val);
        writeln!(file, "{} Immediate:", prefix);
        writeln!(file, "\tSend To Core: {}", imm_core);
        writeln!(file, "\tLen: {} bytes", imm_len);
        writeln!(file, "\toffset_value: {}", offset_value);
        let r1_final = add_offset_r1(r1_val, 1, offset_value);
        let imm_len: usize = imm_len.try_into().expect("imm_len can not be negative");
        let loads = core
            .reserve_load(r1_final, imm_len)
            .unwrap()
            .execute_load::<u8>()
            .unwrap();
        writeln!(file, "{} Memory:", prefix);
        write!(file, "\tLocal[{}:{}](A): ", r1_final, r1_final + imm_len,);
        pretty_print::print_slice::<_, f32>(file, loads[0], 16);
        if prefix == "Post" {
            writeln!(file, "\n###############################################\n");
        }
    }

    pub fn pre_recv(&mut self, cores: &mut CPU, data: InstructionData) {
        self.recv_impl(cores, data, "Pre");
    }

    pub fn post_recv(&mut self, cores: &mut CPU, data: InstructionData) {
        self.recv_impl(cores, data, "Post");
    }

    pub fn recv_impl(&mut self, cores: &mut CPU, data: InstructionData, prefix: &'static str) {
        let core_indx = data.core_indx();
        let rd = data.rd();
        let imm_core = data.imm_core();
        let imm_len = data.imm_len();
        let offset_value = data.offset_value();
        let core = cores.core(core_indx);
        let file: &mut File = self
            .out_files
            .get_mut(core_indx as usize)
            .expect("File at index not found");
        let rd_val = core.register(rd);

        if prefix == "Pre" {
            writeln!(
                file,
                "Inst: recv {} {} {} {} ",
                rd, imm_core, imm_len, offset_value
            );
        } else {
            writeln!(file);
        }
        writeln!(file, "{} Register:", prefix);
        writeln!(file, "\trs1({}): {}", rd, rd_val);
        writeln!(file, "{} Immediate:", prefix);
        writeln!(file, "\tRecv from Core: {}", imm_core);
        writeln!(file, "\tLen: {} bytes", imm_len);
        writeln!(file, "\toffset_value: {}", offset_value);
        let rd_final = add_offset_r1(rd_val, 1, offset_value);
        let imm_len: usize = imm_len.try_into().expect("imm_len can not be negative");
        let loads = core
            .reserve_load(rd_final, imm_len)
            .unwrap()
            .execute_load::<u8>()
            .unwrap();
        writeln!(file, "{} Memory:", prefix);
        write!(file, "\tLocal[{}:{}](A): ", rd_final, rd_final + imm_len,);
        pretty_print::print_slice::<_, f32>(file, loads[0], 16);
        if prefix == "Post" {
            writeln!(file, "\n###############################################\n");
        }
    }
}
