#!/usr/bin/env python3
# --------------------( LICENSE                            )--------------------
# Copyright (c) 2014-2025 Beartype authors.
# See "LICENSE" for further details.

'''
Project-wide **type-checking function utility code snippets** (i.e.,
triple-quoted pure-Python string constants formatted and concatenated together
to dynamically generate the implementations of functions type-checking arbitrary
objects against arbitrary PEP-compliant type hints).

This private submodule is *not* intended for importation by downstream callers.
'''

# ....................{ IMPORTS                            }....................
from beartype._check.checkmagic import (
    ARG_NAME_GETRANDBITS,
    VAR_NAME_RANDOM_INT,
)
from beartype._data.code.datacodeindent import CODE_INDENT_1
from collections.abc import Callable

# ....................{ CODE                               }....................
CODE_SIGNATURE_SCOPE_ARG = (
    # Indentation prefixing all wrapper parameters.
    f'{CODE_INDENT_1}'
    # Default this parameter to the current value of the module-scoped attribute
    # of the same name, passed to the make_func() function by the parent
    # @beartype decorator. While awkward, this is the optimally efficient means
    # of exposing arbitrary attributes to the body of this wrapper function.
    f'{{arg_name}}={{arg_name}},{{arg_comment}}'
    # Newline for readability.
    f'\n'
)
'''
Code snippet declaring a **hidden parameter** (i.e., parameter whose name is
prefixed by ``"__beartype_"`` and whose value is that of an external attribute
internally referenced in the body of a type-checking callable) in the signature
of that callable.
'''

# ....................{ CODE ~ init                        }....................
#FIXME: Note that NumPy provides an efficient means of generating a large
#number of pseudo-random integers all-at-once. The core issue there, of
#course, is that we then need to optionally depend upon and detect NumPy,
#which then requires us to split our random integer generation logic into two
#parallel code paths that we'll then have to maintain -- and the two will be
#rather different. In any case, here's how one generates a NumPy array
#containing 100 pseudo-random integers in the range [0, 127]:
#    random_ints = numpy.random.randint(128, size=100)
#
#To leverage that sanely, we'd need to:
#* Globally cache that array somewhere.
#* Globally cache the current index into that array.
#* When NumPy is unimportable, fallback to generating a Python list containing
#  the same number of pseudo-random integers in the same range.
#* In either case, we'd probably want to wrap that logic in a globally
#  accessible infinite generator singleton that returns another pseudo-random
#  integer every time you iterate it. This assumes, of course, that iterating
#  generators is reasonably fast in Python. (If not, just make that a getter
#  method of a standard singleton object.)
#* Replace the code snippet below with something resembling:
#      '''
#      __beartype_random_int = next(__beartype_random_int_generator)
#      '''
#Note that thread concurrency issues are probable ignorably here, but that
#there's still a great deal of maintenance and refactoring that would need to
#happen to sanely support this. In other words, ain't happenin' anytime soon.
#FIXME: To support both NumPy and non-NumPy code paths transparently, design a
#novel private data structure named "_BeartypeRNJesus" whose __next__() dunder
#method transparently returns a new random integer. The implementation of that
#method then handles all of the low-level minutiae like:
#* Storing and iterating the 0-based index of the next index into an internally
#  cached NumPy array created by calling numpy.random.randint().
#* Creating a new cached NumPy array after exhausting the prior cached array.

CODE_INIT_RANDOM_INT = f'''
    # Generate and localize a sufficiently large pseudo-random integer for
    # subsequent indexation in type-checking randomly selected container items.
    {VAR_NAME_RANDOM_INT} = {ARG_NAME_GETRANDBITS}(32)'''
'''
PEP-specific code snippet generating and localizing a pseudo-random unsigned
32-bit integer for subsequent use in type-checking randomly indexed container
items.

This bit length was intentionally chosen to correspond to the number of bits
generated by each call to Python's C-based Mersenne Twister underlying the
:func:`random.getrandbits` function called here. Exceeding this number of bits
would cause that function to inefficiently call the Twister multiple times.

This bit length produces unsigned 32-bit integers efficiently representable as
C-based atomic integers rather than **big numbers** (i.e., aggregations of
C-based atomic integers) ranging 0–``2**32 - 1`` regardless of the word size of
the active Python interpreter.

Since the cost of generating integers to this maximum bit length is
approximately the same as generating integers of much smaller bit lengths, this
maximum is preferred. Although big numbers transparently support the same
operations as non-big integers, the latter are dramatically more efficient with
respect to both space and time consumption and thus preferred.

Usage
-----
Since *most* containers are likely to contain substantially fewer items than
the maximum integer in this range, pseudo-random container indices are
efficiently selectable by simply taking the modulo of this local variable with
the lengths of those containers.

Any container containing more than this maximum number of items is typically
defined as a disk-backed data structure (e.g., Pandas dataframe) rather than an
in-memory standard object (e.g., :class:`list`). Since :mod:`beartype`
currently ignores the former with respect to deep type-checking, this local
typically suffices for real-world in-memory containers. For edge-case
containers containing more than this maximum number of items, :mod:`beartype`
will only deeply type-check items with indices in this range; all trailing
items will *not* be deeply type-checked, which we consider an acceptable
tradeoff, given the infeasibility of even storing such objects in memory.

Caveats
-------
**The only safely callable function declared by the stdlib** :mod:`random`
**module is** :func:`random.getrandbits`. While that function is efficiently
implemented in C, all other functions declared by that module are inefficiently
implemented in Python. In fact, their implementations are sufficiently
inefficient that there exist numerous online articles lamenting the fact.

See Also
--------
https://stackoverflow.com/a/11704178/2809027
    StackOverflow answer demonstrating Python's C-based Mersenne Twister
    underlying the :func:`random.getrandbits` function to generate 32 bits of
    pseudo-randomness at a time.
https://gist.github.com/terrdavis/1b23b7ff8023f55f627199b09cfa6b24#gistcomment-3237209
    Self GitHub comment introducing the core concepts embodied by this snippet.
https://eli.thegreenplace.net/2018/slow-and-fast-methods-for-generating-random-integers-in-python
    Authoritative article profiling various :mod:`random` callables.
'''

# ..................{ FORMATTERS                             }..................
# str.format() methods, globalized to avoid inefficient dot lookups elsewhere.
# This is an absurd micro-optimization. *fight me, github developer community*
CODE_SIGNATURE_SCOPE_ARG_format: Callable = (
    CODE_SIGNATURE_SCOPE_ARG.format)
