from __future__ import annotations

from typing import Tuple


def divstep(delta: int, f: int, g: int) -> tuple[int, int, int]:
    """
    Perform one Bernstein–Yang style divstep on (delta, f, g).

    This is the basic "divstep" transition used in safegcd-style inversion / GCD
    routines. The transition depends on the parity of g and the sign of delta.

    Rules (written in an explicit "new_*" style):
      - If g is even:
          new_f = f
          new_g = g >> 1
          new_delta = delta + 2
      - Else (g is odd) and delta < 0:
          new_f = f
          new_g = (g + f) >> 1
          new_delta = delta + 2
      - Else (g is odd) and delta >= 0:
          new_f = g
          new_g = (g - f) >> 1
          new_delta = 2 - delta

    Notes:
      - Python's right shift (>>) is an arithmetic shift, matching floor division by 2
        for signed integers.
      - This function is intentionally branch-structured for readability / tracing.

    Parameters
    ----------
    delta : int
        The divstep "delta" state.
    f : int
        Current f.
    g : int
        Current g.

    Returns
    -------
    (new_delta, new_f, new_g) : tuple[int, int, int]
        Updated state after one divstep.
    """
    if (g & 1) == 0:
        new_f = f
        new_g = g >> 1
        new_delta = delta + 2
    elif delta < 0:
        new_f = f
        new_g = (g + f) >> 1
        new_delta = delta + 2
    else:
        new_f = g
        new_g = (g - f) >> 1
        new_delta = 2 - delta

    return new_delta, new_f, new_g


def divstep_ext(
    delta: int,
    f: int,
    g: int,
    u: int,
    v: int,
    r: int,
    s: int,
    field_char: int = 0,
) -> tuple[int, int, int, int, int, int, int]:
    """
    Extended divstep that also updates the coefficient/state tuple (u, v, r, s).

    This is useful when you track the linear relations / transformation matrix
    alongside the (delta, f, g) evolution, as commonly done in extended GCD /
    modular inversion implementations.

    Update rules:

    Case A) g even:
        new_f = f
        new_g = g >> 1
        new_delta = delta + 2
        new_u = u
        new_v = v
        new_r = r/2
        new_s = s/2

    Case B) g odd and delta < 0:
        new_f = f
        new_g = (g + f) >> 1
        new_delta = delta + 2
        new_u = u
        new_v = v
        new_r = (r+u)/2
        new_s = (s+v)/2

    Case C) g odd and delta >= 0:
        new_f = g
        new_g = (g - f) >> 1
        new_delta = 2 - delta
        new_u = r
        new_v = s
        new_r = (r-u)/2
        new_s = (s-v)/2

    Division by 2:
      - If field_char == 0, division is implemented by arithmetic right shift (>> 1),
        i.e. floor division by 2 for signed ints.
        In typical safegcd invariants, these halves are exact because the numerators
        are guaranteed even.
      - If field_char != 0, values are treated modulo field_char, and "division by 2"
        means multiplying by inv2 = 2^{-1} mod field_char.

    Parameters
    ----------
    delta, f, g : int
        Core divstep state.
    u, v, r, s : int
        Extended state / coefficients.
    field_char : int, default 0
        0 means integer-domain halves using shifts.
        Non-zero means computations for (r,s,...) are done modulo field_char, using
        inv2 = pow(2, -1, field_char). field_char must be odd and > 1 (so 2 is invertible).

    Returns
    -------
    (new_delta, new_f, new_g, new_u, new_v, new_r, new_s) : tuple[int, int, int, int, int, int, int]
        Updated extended state after one divstep.
    """
    if field_char != 0:
        if field_char <= 1:
            raise ValueError("field_char must be 0 or an integer > 1.")
        # Will raise ValueError if gcd(2, field_char) != 1 (e.g., even modulus).
        inv2 = pow(2, -1, field_char)

        def half_mod(x: int) -> int:
            return (x * inv2) % field_char

        def norm(x: int) -> int:
            return x % field_char
    else:

        def half_mod(x: int) -> int:
            # arithmetic half (exact when x is even under usual invariants)
            return x >> 1

        def norm(x: int) -> int:
            return x

    if (g & 1) == 0:
        new_f = f
        new_g = g >> 1
        new_delta = delta + 2

        new_u = u
        new_v = v
        new_r = half_mod(norm(r))
        new_s = half_mod(norm(s))

    elif delta < 0:
        new_f = f
        new_g = (g + f) >> 1
        new_delta = delta + 2

        new_u = u
        new_v = v
        new_r = half_mod(norm(r + u))
        new_s = half_mod(norm(s + v))

    else:
        new_f = g
        new_g = (g - f) >> 1
        new_delta = 2 - delta

        new_u = r
        new_v = s
        new_r = half_mod(norm(r - u))
        new_s = half_mod(norm(s - v))

    return new_delta, new_f, new_g, new_u, new_v, new_r, new_s
