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: mod.rs
    path of file: ./repos/neuronika/neuronika-optim/src/adam
    
    the code of the file until where you have to start completion: use std::{cell::Cell, rc::Rc};

use ndarray::{Array, Dimension, Zip};

use neuronika_variable::VarDiff;

use super::{IntoParam, Optimize, Optimizer, OptimizerStatus, Penalty};

/// Adam optimizer.
///
/// It has been proposed in
/// [Adam: A Method for Stochastic Optimization](https://arxiv.org/abs/1412.6980).
pub struct Adam<T>
where
    T: Penalty,
{
    lr: Cell<f32>,
    penalty: T,
    beta1: Cell<f32>,
    beta2: Cell<f32>,
    eps: Cell<f32>,
}

impl<T> OptimizerStatus for Adam<T>
where
    T: Penalty,
{
    fn get_lr(&self) -> f32 {
        self.lr.get()
    }

    fn set_lr(&self, lr: f32) {
        self.lr.set(lr)
    }
}

impl<T> Adam<T>
where
    T: Penalty,
{
    /// Creates a new Adam optimizer.
    ///
    /// # Arguments
    ///
    /// * `lr` - learning rate.
    ///
    /// * `beta1`Â - coefficient for computing running average of the gradient. A good default is 0.9.
    ///
    /// * `beta2` - coefficient for computing running average of the squared gradient. A good
    /// default is 0.999.
    ///
    /// * `penalty` - penalty regularization.
    ///
    /
    
    fn optimize(&mut self) {
        self.step += 1;

        let beta1 = self.status.beta1.get();
        let beta2 = self.status.beta2.get();
        let lr = self.status.lr.get();
        let eps = self.status.eps.get();
        let penalty = self.status.penalty;

        let bias_correction1 = 1.0 - beta1.powi(self.step as i32);
        let bias_correction2 = 1.0 - beta2.powi(self.step as i32);

        let mut data = self.variable.data_mut();
        let mut grad = self.variable.grad_mut();

        Zip::from(&mut *grad)
            .and(&*data)
            .for_each(|grad_el, data_el| *grad_el += penalty.penalize(data_el));

        Zip::from(&mut self.exp_avg)
            .and(&*grad)
            .for_each(|exp_avg_el, grad_el| {
                *exp_avg_el = *exp_avg_el * beta1 + grad_el * (1.0 - beta1)
            });

        Zip::from(&mut self.exp_avg_sq)
            .and(&*grad)
            .for_each(|exp_avg_sq_el, grad_el| {
                *exp_avg_sq_el = *exp_avg_sq_el * beta2 + grad_el * grad_el * (1.0 - beta2)
            });

        Zip::from(&mut *data)
            .and(&self.exp_avg)
            .and(&self.exp_avg_sq)
            .for_each(|data_el, exp_avg_el, exp_avg_sq_el| {
                *data_el -= exp_avg_el / ((exp_avg_sq_el.sqrt() / bias_correction2.sqrt()) + eps)
                    * (lr / bias_correction1)
            })
    }