#cython: boundscheck=False, wraparound=False, initializedcheck=False

"""Low-level calculations for Gaussian beam properties.

This module provides functions for computing properties of Gaussian beams
and transforming Gaussian beam parameters via the ABCD ray-matrix framework.

.. note::
    These are low-level functions intended for use in Cython extensions. Users
    should use the functions and classes given in :mod:`finesse.gaussian` instead
    of those defined here.
"""

cpdef complex_t transform_q(
    const double[:, ::1] M,
    complex_t q1,
    double nr1,
    double nr2
) nogil:
    cdef:
        complex_t q1_factor = q1 / nr1

    return nr2 * (M[0][0] * q1_factor + M[0][1]) / (M[1][0] * q1_factor + M[1][1])

cdef complex_t c_transform_q(
    const double* M,
    complex_t q1,
    double nr1,
    double nr2
) nogil:
    """Assumes M is a C-contiguous array of size 2x2"""
    cdef:
        complex_t q1_factor = q1 / nr1

    return nr2 * (M[0] * q1_factor + M[1]) / (M[2] * q1_factor + M[3])

cpdef complex_t inv_transform_q(
    const double[:, ::1] M,
    complex_t q2,
    double nr1,
    double nr2
) nogil:
    cdef:
        complex_t q2_factor = q2 / nr2

    return nr1 * (M[0][1] - M[1][1] * q2_factor) / (M[1][0] * q2_factor - M[0][0])


cdef void abcd_multiply(
    const double[:, ::1] m1,
    const double[:, ::1] m2,
    double[:, ::1] out,
    bint m2_refl_transform=False
) nogil:
    cdef:
        double A1 = m1[0][0]
        double B1 = m1[0][1]
        double C1 = m1[1][0]
        double D1 = m1[1][1]

        double A2 = m2[0][0]
        double B2 = m2[0][1]
        double C2 = m2[1][0]
        double D2 = m2[1][1]

    if m2_refl_transform:
        A2 *= -1.0
        B2 *= -1.0
        C2 *= -1.0
        D2 *= -1.0

    out[0][0] = A1 * A2 + B1 * C2
    out[0][1] = A1 * B2 + B1 * D2
    out[1][0] = C1 * A2 + D1 * C2
    out[1][1] = C1 * B2 + D1 * D2


cdef void sym_abcd_multiply(
    object[:, ::1] m1,
    object[:, ::1] m2,
    object[:, ::1] out,
    bint m2_refl_transform=False
):
    cdef:
        object A1 = m1[0][0]
        object B1 = m1[0][1]
        object C1 = m1[1][0]
        object D1 = m1[1][1]

        object A2 = m2[0][0]
        object B2 = m2[0][1]
        object C2 = m2[1][0]
        object D2 = m2[1][1]

    if m2_refl_transform:
        A2 *= -1.0
        B2 *= -1.0
        C2 *= -1.0
        D2 *= -1.0

    out[0][0] = A1 * A2 + B1 * C2
    out[0][1] = A1 * B2 + B1 * D2
    out[1][0] = C1 * A2 + D1 * C2
    out[1][1] = C1 * B2 + D1 * D2
