module classes_des_reci_visitor

use, intrinsic :: iso_fortran_env, only: DP => REAL64
use data_constants, only: num_dimensions, PI
use classes_periodic_box, only: Abstract_Periodic_Box
use classes_box_size_memento, only: Abstract_Box_Size_Memento
use classes_reciprocal_lattice, only: Abstract_Reciprocal_Lattice
use types_particle_wrapper, only: Concrete_Particle
use classes_des_reci_weight, only: Abstract_DES_Reci_Weight
use classes_des_reci_structure, only: Abstract_DES_Reci_Structure
use classes_structure_visitor, only: Abstract_Structure_Visitor
use procedures_dipolar_interactions_micro, only: set_fourier, reci_number_1_sym, reci_number_2_sym

implicit none

private

    !> @note
    !> In [[classes_des_reci_visitor:Abstract_visit]],
    !> [[classes_des_reci_visitor:Abstract_visit_translation]],
    !> [[classes_des_reci_visitor:Abstract_visit_transmutation]],
    !> [[classes_des_reci_visitor:Abstract_visit_switch]],
    !> [[classes_des_reci_visitor:Abstract_visit_exchange]]
    !> only half wave vectors are visited (symmetry). Hence energy is doubled.
    type, extends(Abstract_Structure_Visitor), abstract, public :: Abstract_DES_Reci_Visitor
    private
        class(Abstract_Periodic_Box), pointer :: periodic_box => null()
        class(Abstract_Box_Size_Memento), pointer :: box_size_memento => null()
        integer :: reci_numbers(num_dimensions) = 0
        class(Abstract_DES_Reci_Weight), pointer :: weight => null()
        class(Abstract_DES_Reci_Structure), pointer :: structure => null()
    contains
        procedure :: construct => Abstract_construct
        procedure :: destroy => Abstract_destroy
        procedure :: target => Abstract_target
        procedure :: visit => Abstract_visit
        procedure :: visit_translation => Abstract_visit_translation
        procedure :: visit_transmutation => Abstract_visit_transmutation
        procedure :: visit_rotation => Abstract_visit_rotation
        procedure :: visit_add => Abstract_visit_add
        procedure :: visit_remove => Abstract_visit_remove
        procedure :: visit_switch => Abstract_visit_switch
        procedure, private :: visit_exchange => Abstract_visit_exchange
    end type Abstract_DES_Reci_Visitor

    type, extends(Abstract_DES_Reci_Visitor), public :: Concrete_DES_Reci_Visitor

    end type Concrete_DES_Reci_Visitor

    type, extends(Abstract_DES_Reci_Visitor), public :: Null_DES_Reci_Visitor
    contains
        procedure :: construct => Null_construct
        procedure :: destroy => Null_destroy
        procedure :: target => Null_target
        procedure :: visit => Null_visit
        procedure :: visit_translation => Null_visit_translation
        procedure :: visit_transmutation => Null_visit_transmutation
        procedure :: visit_switch => Null_visit_switch
        procedure, private :: visit_exchange => Null_visit_exchange
    end type Null_DES_Reci_Visitor

contains

!implementation Abstract_DES_Reci_Visitor

    subroutine Abstract_construct(this, periodic_box, box_size_memento, reciprocal_lattice, &
        weight, structure)
        class(Abstract_DES_Reci_Visitor), intent(out) :: this
        class(Abstract_Periodic_Box), target, intent(in) :: periodic_box
        class(Abstract_Box_Size_Memento), intent(in) :: box_size_memento
        class(Abstract_Reciprocal_Lattice), intent(in) :: reciprocal_lattice
        class(Abstract_DES_Reci_Weight), intent(in) :: weight
        class(Abstract_DES_Reci_Structure), intent(in) :: structure

        this%periodic_box => periodic_box
        call this%target(box_size_memento, weight, structure)
        this%reci_numbers = reciprocal_lattice%get_numbers()
    end subroutine Abstract_construct

    subroutine Abstract_destroy(this)
        class(Abstract_DES_Reci_Visitor), intent(inout) :: this

        this%structure => null()
        this%weight => null()
        this%box_size_memento => null()
        this%periodic_box => null()
    end subroutine Abstract_destroy

    subroutine Abstract_target(this, box_size_memento, weight, structure)
        class(Abstract_DES_Reci_Visitor), intent(inout) :: this
        class(Abstract_Box_Size_Memento), target, intent(in) :: box_size_memento
        class(Abstract_DES_Reci_Weight), target, intent(in) :: weight
        class(Abstract_DES_Reci_Structure), target, intent(in) :: structure

        this%box_size_memento => box_size_memento
        this%weight => weight
        this%structure => structure
    end subroutine Abstract_target

    !> \[
    !>      U = \sum_{\vec{k}} w_\alpha(\vec{k}) |S(\vec{k})|^2
    !> \]
    !> where \( w_\alpha(\vec{k}) \) is [[Abstract_DES_Reci_Weight]]
    !> and \( S(\vec{k}) \) is [[Abstract_DES_Reci_Structure]].
    pure real(DP) function Abstract_visit(this) result(energy)
        class(Abstract_DES_Reci_Visitor), intent(in) :: this

        integer :: n_1, n_2, n_3

        energy = 0._DP
        do n_3 = 0, this%reci_numbers(3)
            do n_2 = -reci_number_2_sym(this%reci_numbers, n_3), this%reci_numbers(2)
                do n_1 = -reci_number_1_sym(this%reci_numbers, n_3, n_2), this%reci_numbers(1)
                    if (n_1**2 + n_2**2 + n_3**2 > this%reci_numbers(1)**2) cycle
                    energy = energy + this%weight%get(n_1, n_2, n_3) * real(this%structure%&
                        get(n_1, n_2, n_3) * conjg(this%structure%get(n_1, n_2, n_3)), DP)
                end do
            end do
        end do
        energy = 2._DP * energy
    end function Abstract_visit

    !> Energy delta when a particle is translated:
    !> \( (\vec{x}, \vec{\mu}) \to (\vec{x}^\prime, \vec{\mu}) \).
    !> \[
    !>      \Delta U = 2 \sum_{\vec{k}} w_\alpha(\vec{k}) (\vec{k}\cdot\vec{\mu}) \left\{
    !>              \Re\left[
    !>                  \left( e^{i\vec{k}\cdot\vec{x}^\prime} - e^{i\vec{k}\cdot\vec{x}} \right)
    !>                  S^\ast(\vec{k})
    !>              \right] + (\vec{k}\cdot\vec{\mu})\left[
    !>                  1 - \Re\left(
    !>                      e^{i\vec{k}\cdot\vec{x}^\prime} e^{-i\vec{k}\cdot\vec{x}}
    !>                  \right)
    !>              \right]
    !>          \right\}
    !> \]
    pure real(DP) function Abstract_visit_translation(this, i_component, new_position, old) &
        result(delta_energy)
        class(Abstract_DES_Reci_Visitor), intent(in) :: this
        integer, intent(in) :: i_component
        real(DP), intent(in) :: new_position(:)
        type(Concrete_Particle), intent(in) :: old

        real(DP), dimension(num_dimensions) :: box_size, saved_box_size
        real(DP), dimension(num_dimensions) :: wave_vector
        integer :: n_1, n_2, n_3
        real(DP), dimension(num_dimensions) :: wave_1_x_position_new, wave_1_x_position_old
        real(DP) :: real_delta_fourier_x_conjg_structure, wave_dot_moment

        complex(DP) :: fourier_position_new
        complex(DP), dimension(-this%reci_numbers(1):this%reci_numbers(1)) :: fourier_position_new_1
        complex(DP), dimension(-this%reci_numbers(2):this%reci_numbers(2)) :: fourier_position_new_2
        complex(DP), dimension(-this%reci_numbers(3):this%reci_numbers(3)) :: fourier_position_new_3

        complex(DP) :: fourier_position_old
        complex(DP), dimension(-this%reci_numbers(1):this%reci_numbers(1)) :: fourier_position_old_1
        complex(DP), dimension(-this%reci_numbers(2):this%reci_numbers(2)) :: fourier_position_old_2
        complex(DP), dimension(-this%reci_numbers(3):this%reci_numbers(3)) :: fourier_position_old_3

        delta_energy = 0._DP
        if (.not.this%structure%is_dipolar(i_component)) return

        box_size = this%periodic_box%get_size()
        saved_box_size = this%box_size_memento%get()

        wave_1_x_position_old = 2._DP*PI * old%position / box_size
        call set_fourier(fourier_position_old_1, this%reci_numbers(1), wave_1_x_position_old(1))
        call set_fourier(fourier_position_old_2, this%reci_numbers(2), wave_1_x_position_old(2))
        call set_fourier(fourier_position_old_3, this%reci_numbers(3), wave_1_x_position_old(3))
        wave_1_x_position_new = 2._DP*PI * new_position / box_size
        call set_fourier(fourier_position_new_1, this%reci_numbers(1), wave_1_x_position_new(1))
        call set_fourier(fourier_position_new_2, this%reci_numbers(2), wave_1_x_position_new(2))
        call set_fourier(fourier_position_new_3, this%reci_numbers(3), wave_1_x_position_new(3))

        do n_3 = 0, this%reci_numbers(3)
            wave_vector(3) = 2._DP*PI * real(n_3, DP) / saved_box_size(3)
            do n_2 = -reci_number_2_sym(this%reci_numbers, n_3), this%reci_numbers(2)
                wave_vector(2) = 2._DP*PI * real(n_2, DP) / saved_box_size(2)
                do n_1 = -reci_number_1_sym(this%reci_numbers, n_3, n_2), this%reci_numbers(1)
                    wave_vector(1) = 2._DP*PI * real(n_1, DP) / saved_box_size(1)

                    if (n_1**2 + n_2**2 + n_3**2 > this%reci_numbers(1)**2) cycle

                    fourier_position_old = fourier_position_old_1(n_1) * &
                        fourier_position_old_2(n_2) * fourier_position_old_3(n_3)
                    fourier_position_new = fourier_position_new_1(n_1) * &
                        fourier_position_new_2(n_2) * fourier_position_new_3(n_3)
                    wave_dot_moment = dot_product(wave_vector, old%dipole_moment)

                    real_delta_fourier_x_conjg_structure = &
                        real((fourier_position_new - fourier_position_old) * &
                            conjg(this%structure%get(n_1, n_2, n_3)), DP)

                    delta_energy = delta_energy + this%weight%get(n_1, n_2, n_3) * &
                        wave_dot_moment * (real_delta_fourier_x_conjg_structure + &
                        wave_dot_moment * (1._DP - real(fourier_position_new * &
                            conjg(fourier_position_old), DP)))
                end do
            end do
        end do
        delta_energy = product(saved_box_size / box_size) * 4._DP * delta_energy
    end function Abstract_visit_translation

    !> Energy delta when a particle is transmuted:
    !> \( (\vec{x}, \vec{\mu}) \to (\vec{x}, \vec{\mu}^\prime) \).
    !> \[
    !>      \Delta U = \sum_{\vec{k}} w_\alpha(\vec{k}) \vec{k}\cdot(\vec{\mu}^\prime - \vec{\mu})
    !>          \left[
    !>              2\Re\left( e^{i\vec{k}\cdot\vec{x}} S^\ast(\vec{k}) \right) +
    !>              \vec{k}\cdot(\vec{\mu}^\prime - \vec{\mu})
    !>          \right]
    !> \]
    pure real(DP) function Abstract_visit_transmutation(this, ij_components, new_dipole_moment, &
        old) result(delta_energy)
        class(Abstract_DES_Reci_Visitor), intent(in) :: this
        integer, intent(in) :: ij_components(:)
        real(DP), intent(in) :: new_dipole_moment(:)
        type(Concrete_Particle), intent(in) :: old

        real(DP), dimension(num_dimensions) :: box_size, saved_box_size
        real(DP), dimension(num_dimensions) :: wave_vector
        integer :: n_1, n_2, n_3
        real(DP), dimension(num_dimensions) :: wave_1_x_position
        real(DP) :: real_fourier_x_conjg_structure, wave_dot_delta_moment

        complex(DP) :: fourier_position
        complex(DP), dimension(-this%reci_numbers(1):this%reci_numbers(1)) :: fourier_position_1
        complex(DP), dimension(-this%reci_numbers(2):this%reci_numbers(2)) :: fourier_position_2
        complex(DP), dimension(-this%reci_numbers(3):this%reci_numbers(3)) :: fourier_position_3

        delta_energy = 0._DP
        if (.not.(this%structure%is_dipolar(ij_components(1)) .or. &
            this%structure%is_dipolar(ij_components(2)))) return !shortcut?

        box_size = this%periodic_box%get_size()
        saved_box_size = this%box_size_memento%get()

        wave_1_x_position = 2._DP*PI * old%position / box_size
        call set_fourier(fourier_position_1, this%reci_numbers(1), wave_1_x_position(1))
        call set_fourier(fourier_position_2, this%reci_numbers(2), wave_1_x_position(2))
        call set_fourier(fourier_position_3, this%reci_numbers(3), wave_1_x_position(3))

        do n_3 = 0, this%reci_numbers(3)
            wave_vector(3) = 2._DP*PI * real(n_3, DP) / saved_box_size(3)
            do n_2 = -reci_number_2_sym(this%reci_numbers, n_3), this%reci_numbers(2)
                wave_vector(2) = 2._DP*PI * real(n_2, DP) / saved_box_size(2)
                do n_1 = -reci_number_1_sym(this%reci_numbers, n_3, n_2), this%reci_numbers(1)
                    wave_vector(1) = 2._DP*PI * real(n_1, DP) / saved_box_size(1)

                    if (n_1**2 + n_2**2 + n_3**2 > this%reci_numbers(1)**2) cycle

                    fourier_position = fourier_position_1(n_1) * fourier_position_2(n_2) * &
                        fourier_position_3(n_3)
                    wave_dot_delta_moment = dot_product(wave_vector, &
                        new_dipole_moment - old%dipole_moment)

                    real_fourier_x_conjg_structure = real(fourier_position * &
                        conjg(this%structure%get(n_1, n_2, n_3)), DP)

                    delta_energy = delta_energy + this%weight%get(n_1, n_2, n_3) * &
                        wave_dot_delta_moment * (2._DP*real_fourier_x_conjg_structure + &
                            wave_dot_delta_moment)
                end do
            end do
        end do
        delta_energy = product(saved_box_size / box_size) * 2._DP * delta_energy
    end function Abstract_visit_transmutation

    pure real(DP) function Abstract_visit_rotation(this, i_component, new_dipole_moment, old) &
        result(delta_energy)
        class(Abstract_DES_Reci_Visitor), intent(in) :: this
        integer, intent(in) :: i_component
        real(DP), intent(in) :: new_dipole_moment(:)
        type(Concrete_Particle), intent(in) :: old

        delta_energy = this%visit_transmutation([i_component, i_component], new_dipole_moment, old)
    end function Abstract_visit_rotation

    pure real(DP) function Abstract_visit_add(this, i_component, particle) result(delta_energy)
        class(Abstract_DES_Reci_Visitor), intent(in) :: this
        integer, intent(in) :: i_component
        type(Concrete_Particle), intent(in) :: particle

        delta_energy = this%visit_exchange(i_component, particle, +1._DP)
    end function Abstract_visit_add

    pure real(DP) function Abstract_visit_remove(this, i_component, particle) result(delta_energy)
        class(Abstract_DES_Reci_Visitor), intent(in) :: this
        integer, intent(in) :: i_component
        type(Concrete_Particle), intent(in) :: particle

        delta_energy = this%visit_exchange(i_component, particle, -1._DP)
    end function Abstract_visit_remove

    !> Energy delta when a particle of coordinates \( (\vec{x}, \vec{\mu}) \) is added (\( + \)) or
    !> removed (\( - \)).
    !> \[
    !>      \Delta U = \sum_{\vec{k}} w_\alpha(\vec{k}) (\vec{k} \cdot \vec{\mu}) \left[
    !>              \pm 2 \Re\left( e^{i\vec{k}\cdot\vec{x}} S^\ast(\vec{k}) \right) +
    !>              (\vec{k} \cdot \vec{\mu})
    !>          \right]
    !> \]
    pure real(DP) function Abstract_visit_exchange(this, i_component, particle, signed) &
        result(delta_energy)
        class(Abstract_DES_Reci_Visitor), intent(in) :: this
        integer, intent(in) :: i_component
        type(Concrete_Particle), intent(in) :: particle
        real(DP), intent(in) :: signed

        real(DP), dimension(num_dimensions) :: box_size, saved_box_size
        real(DP), dimension(num_dimensions) :: wave_vector
        integer :: n_1, n_2, n_3
        real(DP), dimension(num_dimensions) :: wave_1_x_position
        real(DP) :: real_fourier_x_conjg_structure, wave_dot_moment

        complex(DP) :: fourier_position
        complex(DP), dimension(-this%reci_numbers(1):this%reci_numbers(1)) :: fourier_position_1
        complex(DP), dimension(-this%reci_numbers(2):this%reci_numbers(2)) :: fourier_position_2
        complex(DP), dimension(-this%reci_numbers(3):this%reci_numbers(3)) :: fourier_position_3

        delta_energy = 0._DP
        if (.not.this%structure%is_dipolar(i_component)) return

        box_size = this%periodic_box%get_size()
        saved_box_size = this%box_size_memento%get()

        wave_1_x_position = 2._DP*PI * particle%position / box_size
        call set_fourier(fourier_position_1, this%reci_numbers(1), wave_1_x_position(1))
        call set_fourier(fourier_position_2, this%reci_numbers(2), wave_1_x_position(2))
        call set_fourier(fourier_position_3, this%reci_numbers(3), wave_1_x_position(3))

        do n_3 = 0, this%reci_numbers(3)
            wave_vector(3) = 2._DP*PI * real(n_3, DP) / saved_box_size(3)
            do n_2 = -reci_number_2_sym(this%reci_numbers, n_3), this%reci_numbers(2)
                wave_vector(2) = 2._DP*PI * real(n_2, DP) / saved_box_size(2)
                do n_1 = -reci_number_1_sym(this%reci_numbers, n_3, n_2), this%reci_numbers(1)
                    wave_vector(1) = 2._DP*PI * real(n_1, DP) / saved_box_size(1)

                    if (n_1**2 + n_2**2 + n_3**2 > this%reci_numbers(1)**2) cycle

                    fourier_position = fourier_position_1(n_1) * fourier_position_2(n_2) * &
                        fourier_position_3(n_3)
                    wave_dot_moment = dot_product(wave_vector, particle%dipole_moment)

                    real_fourier_x_conjg_structure = signed * real(fourier_position * &
                        conjg(this%structure%get(n_1, n_2, n_3)), DP)

                    delta_energy = delta_energy + this%weight%get(n_1, n_2, n_3) * &
                        wave_dot_moment * (2._DP * real_fourier_x_conjg_structure + &
                        wave_dot_moment)
                end do
            end do
        end do
        delta_energy = product(saved_box_size / box_size) * 2._DP * delta_energy
    end function Abstract_visit_exchange

    !> Energy delta when 2 particles of coordinates \( (\vec{x}_1, \vec{\mu}_1) \) and
    !> \( (\vec{x}_2, \vec{\mu}_2) \) switch their positions.
    !> \[
    !>      \Delta U = 2 \sum_{\vec{k}} w_\alpha(\vec{k}) \vec{k}\cdot(\vec{\mu}_1 - \vec{\mu}_2)
    !>      \left\{
    !>              \Re\left[
    !>                  \left(e^{i\vec{k}\cdot\vec{x}_2} - e^{i\vec{k}\cdot\vec{x}_1} \right)
    !>                      S^\ast(\vec{k})
    !>              \right] +
    !>              \vec{k}\cdot(\vec{\mu}_1 - \vec{\mu}_2)
    !>              \left[
    !>                  1 - \Re\left( e^{i\vec{k}\cdot\vec{x}_1} e^{-i\vec{k}\cdot\vec{x}_2} \right)
    !>              \right]
    !>      \right\}
    !> \]
    pure real(DP) function Abstract_visit_switch(this, ij_components, particles) &
        result(delta_energy)
        class(Abstract_DES_Reci_Visitor), intent(in) :: this
        integer, intent(in) :: ij_components(:)
        type(Concrete_Particle), intent(in) :: particles(:)

        real(DP), dimension(num_dimensions) :: box_size, saved_box_size
        real(DP), dimension(num_dimensions) :: wave_vector
        integer :: n_1, n_2, n_3
        real(DP), dimension(num_dimensions) :: wave_1_x_position_1, wave_1_x_position_2
        real(DP) :: real_delta_fourier_x_conjg_structure, wave_dot_delta_moment

        complex(DP) :: fourier_position_1
        complex(DP), dimension(-this%reci_numbers(1):this%reci_numbers(1)) :: fourier_position_1_1
        complex(DP), dimension(-this%reci_numbers(2):this%reci_numbers(2)) :: fourier_position_1_2
        complex(DP), dimension(-this%reci_numbers(3):this%reci_numbers(3)) :: fourier_position_1_3
        complex(DP) :: fourier_position_2
        complex(DP), dimension(-this%reci_numbers(1):this%reci_numbers(1)) :: fourier_position_2_1
        complex(DP), dimension(-this%reci_numbers(2):this%reci_numbers(2)) :: fourier_position_2_2
        complex(DP), dimension(-this%reci_numbers(3):this%reci_numbers(3)) :: fourier_position_2_3

        delta_energy = 0._DP
        if (.not.(this%structure%is_dipolar(ij_components(1)) .or. &
            this%structure%is_dipolar(ij_components(2)))) return

        box_size = this%periodic_box%get_size()
        saved_box_size = this%box_size_memento%get()

        wave_1_x_position_1 = 2._DP*PI * particles(1)%position / box_size
        call set_fourier(fourier_position_1_1, this%reci_numbers(1), wave_1_x_position_1(1))
        call set_fourier(fourier_position_1_2, this%reci_numbers(2), wave_1_x_position_1(2))
        call set_fourier(fourier_position_1_3, this%reci_numbers(3), wave_1_x_position_1(3))
        wave_1_x_position_2 = 2._DP*PI * particles(2)%position / box_size
        call set_fourier(fourier_position_2_1, this%reci_numbers(1), wave_1_x_position_2(1))
        call set_fourier(fourier_position_2_2, this%reci_numbers(2), wave_1_x_position_2(2))
        call set_fourier(fourier_position_2_3, this%reci_numbers(3), wave_1_x_position_2(3))

        do n_3 = 0, this%reci_numbers(3)
            wave_vector(3) = 2._DP*PI * real(n_3, DP) / saved_box_size(3)
            do n_2 = -reci_number_2_sym(this%reci_numbers, n_3), this%reci_numbers(2)
                wave_vector(2) = 2._DP*PI * real(n_2, DP) / saved_box_size(2)
                do n_1 = -reci_number_1_sym(this%reci_numbers, n_3, n_2), this%reci_numbers(1)
                    wave_vector(1) = 2._DP*PI * real(n_1, DP) / saved_box_size(1)

                    if (n_1**2 + n_2**2 + n_3**2 > this%reci_numbers(1)**2) cycle

                    fourier_position_1 = fourier_position_1_1(n_1) * fourier_position_1_2(n_2) * &
                        fourier_position_1_3(n_3)
                    fourier_position_2 = fourier_position_2_1(n_1) * fourier_position_2_2(n_2) * &
                        fourier_position_2_3(n_3)
                    wave_dot_delta_moment = dot_product(wave_vector, &
                        particles(1)%dipole_moment - particles(2)%dipole_moment)

                    real_delta_fourier_x_conjg_structure = &
                        real((fourier_position_2 - fourier_position_1) * &
                            conjg(this%structure%get(n_1, n_2, n_3)), DP)

                    delta_energy = delta_energy + this%weight%get(n_1, n_2, n_3) * &
                        wave_dot_delta_moment * (real_delta_fourier_x_conjg_structure + &
                            wave_dot_delta_moment * (1._DP - real(fourier_position_1 * &
                                conjg(fourier_position_2), DP)))
                end do
            end do
        end do
        delta_energy = product(saved_box_size / box_size) * 4._DP * delta_energy
    end function Abstract_visit_switch
!end implementation Abstract_DES_Reci_Visitor

!implementation Null_DES_Reci_Visitor

    subroutine Null_construct(this, periodic_box, box_size_memento, reciprocal_lattice, weight, &
        structure)
        class(Null_DES_Reci_Visitor), intent(out) :: this
        class(Abstract_Periodic_Box), target, intent(in) :: periodic_box
        class(Abstract_Box_Size_Memento), intent(in) :: box_size_memento
        class(Abstract_Reciprocal_Lattice), intent(in) :: reciprocal_lattice
        class(Abstract_DES_Reci_Weight), intent(in) :: weight
        class(Abstract_DES_Reci_Structure), intent(in) :: structure
    end subroutine Null_construct

    subroutine Null_destroy(this)
        class(Null_DES_Reci_Visitor), intent(inout) :: this
    end subroutine Null_destroy

    subroutine Null_target(this, box_size_memento, weight, structure)
        class(Null_DES_Reci_Visitor), intent(inout) :: this
        class(Abstract_Box_Size_Memento), target, intent(in) :: box_size_memento
        class(Abstract_DES_Reci_Weight), target, intent(in) :: weight
        class(Abstract_DES_Reci_Structure), target, intent(in) :: structure
    end subroutine Null_target

    pure real(DP) function Null_visit(this) result(energy)
        class(Null_DES_Reci_Visitor), intent(in) :: this
        energy = 0._DP
    end function Null_visit

    pure real(DP) function Null_visit_translation(this, i_component, new_position, old) &
        result(delta_energy)
        class(Null_DES_Reci_Visitor), intent(in) :: this
        integer, intent(in) :: i_component
        real(DP), intent(in) :: new_position(:)
        type(Concrete_Particle), intent(in) :: old
        delta_energy = 0._DP
    end function Null_visit_translation

    pure real(DP) function Null_visit_transmutation(this, ij_components, new_dipole_moment, &
        old) result(delta_energy)
        class(Null_DES_Reci_Visitor), intent(in) :: this
        integer, intent(in) :: ij_components(:)
        real(DP), intent(in) :: new_dipole_moment(:)
        type(Concrete_Particle), intent(in) :: old
        delta_energy = 0._DP
    end function Null_visit_transmutation

    pure real(DP) function Null_visit_exchange(this, i_component, particle, signed) &
        result(delta_energy)
        class(Null_DES_Reci_Visitor), intent(in) :: this
        integer, intent(in) :: i_component
        type(Concrete_Particle), intent(in) :: particle
        real(DP), intent(in) :: signed
        delta_energy = 0._DP
    end function Null_visit_exchange

    pure real(DP) function Null_visit_switch(this, ij_components, particles) result(delta_energy)
        class(Null_DES_Reci_Visitor), intent(in) :: this
        integer, intent(in) :: ij_components(:)
        type(Concrete_Particle), intent(in) :: particles(:)
        delta_energy = 0._DP
    end function Null_visit_switch

!end implementation Null_DES_Reci_Visitor

end module classes_des_reci_visitor
