""" @brief  A module for solving the shortest non-zero vector problem (SVP). """

from .enumerate import enumerate;
from .gram_schmidt import gram_schmidt;

from ..vectors import norm2;

from ...utils.timeout import Timeout;

from gmpy2 import mpz;
from gmpy2 import mpq;
from gmpy2 import mpfr;

def solve_svp(
  B : list[list[int | mpz]],
  timeout : int | None | Timeout = None,
  gs : list[list[list[int | mpz | mpq | mpfr]]] | None = None,
  precision : int | None = None) -> list[int | mpz]:

  """ @brief  Let L be the lattice generated by the rows of the integer basis
              matrix B. Then this function computes and returns a shortest
              non-zero vector in L.

      The basis matrix B is represented as a list of lists [b_1, ..., b_n],
      where b_i = [b_i1, ..., b_id] for i = 1, ..., n are the n rows of B.

      It is assumed that the basis B is delta-LLL-reduced.

      It is required that B has integer entries of type int or mpz.

      Furthermore, it is required that B has full rank and is square since the
      enumerate() function that this function calls currently imposes these
      requirements. Said requirements may be relaxed in the future.

      It is assumed that the basis B is reduced, see the lll() function.

      @param B  The matrix B.

      @param timeout  A timeout after which a TimeoutError will be raised and
                      the computation aborted.

                      The timeout may be represented as an integer specifying
                      the timeout in seconds, or as an instance of the Timeout
                      class. May be set to None, as is the default, in which
                      case no timeout is enforced.

      @param gs   The list [Bs, M], where Bs is the Gram–Schmidt orthogonalized
                  basis for B, and M is the associated matrix of Gram–Schmidt
                  projection factors, as returned by calling

                     [Bs, M] = gram_schmidt(B, precision = precision),

                  or None, in which case this function will make the above call.

                  If you plan to call this functions repeatedly for the same
                  lattice, then time may be saved by not re-computing Bs and M
                  for each call.

                  Note that the basis B is typically LLL-reduced by calling
                  lll() before calling this function. The lll() function can
                  return not only B but also Bs and M (since Bs and M are
                  incrementally computed as a part of the LLL reduction process)
                  allowing you to directly pass them along to this function.

      @param precision  The precision to use when computing the Gram–Schmidt
                        projection factors. May be set to None, in which case
                        the projection factors are represented as exact
                        quotients.

                        Note that this parameter only has an effect if gs is
                        set to None as the Gram–Schmidt orthogonalized basis Bs
                        and the associated matrix M of Gram–Schmidt projection
                        factors are otherwise pre-computed.

      @return   A shortest non-zero vector in L, the lattice generated by the
                rows of the integer basis matrix B. """

  # Initial setup.
  timeout = Timeout.parse(timeout);
  timeout.check();

  if None == gs:
    gs = gram_schmidt(B, precision = precision);

  timeout.check();

  # Let v be the first vector in the basis.
  v = B[0];

  # Let R2 be the square norm of v.
  R2 = norm2(v);

  # Enumerate all vectors within distance R of v and see if there is a shorter
  # non-zero vector v' than v. If so, set v to v', update R, and re-start the
  # enumeration (since R is now smaller this is likely more efficient).

  # Set the proceed flag.
  proceed = True;

  while proceed:

    # Unset the proceed flag.
    proceed = False;

    # Check the timeout.
    timeout.check();

    # Start the enumeration.
    for vp in enumerate(B,
                        R2,
                        timeout = timeout,
                        gs = gs):

      tmp = norm2(vp);

      if 0 < tmp < R2:
        v = vp;
        R2 = tmp;

        # Set the proceed flag.
        proceed = True;

        break; # Stop the enumeration.

  return v;