





import contextlib
from multiprocessing import Pool, RLock

from tqdm.auto import tqdm

from ..utils import experimental, logging


__lowerCamelCase			:	Union[str, Any]					    =	logging.get_logger(__name__)



class    A__  :
      _UpperCAmelCase      :List[str]		       =  None



@experimental
def 						A_							(	_lowerCAmelCase				,  _lowerCAmelCase				,  _lowerCAmelCase				,  _lowerCAmelCase				,  _lowerCAmelCase				,  _lowerCAmelCase				,  _lowerCAmelCase	)		->					Optional[Any]:
 if ParallelBackendConfig.backend_name is None:
  return _map_with_multiprocessing_pool(
      _lowerCAmelCase				,  _lowerCAmelCase				,  _lowerCAmelCase				,  _lowerCAmelCase				,  _lowerCAmelCase				,  _lowerCAmelCase				,  _lowerCAmelCase	)

 return _map_with_joblib(_lowerCAmelCase				,  _lowerCAmelCase				,  _lowerCAmelCase				,  _lowerCAmelCase				,  _lowerCAmelCase				,  _lowerCAmelCase				,  _lowerCAmelCase	)



def 						A_							(	_lowerCAmelCase				,  _lowerCAmelCase				,  _lowerCAmelCase				,  _lowerCAmelCase				,  _lowerCAmelCase				,  _lowerCAmelCase				,  _lowerCAmelCase	)		->					Optional[int]:
 UpperCamelCase      :   int			      =  num_proc if num_proc <= len(_lowerCAmelCase	) else len(_lowerCAmelCase	)
 UpperCamelCase      :   Tuple			      =  []  # We organize the splits ourselve (contiguous splits)
 for index in range(_lowerCAmelCase	):
  UpperCamelCase      :   Union[str, Any]			      =  len(_lowerCAmelCase	) // num_proc
  UpperCamelCase      :   Optional[Any]			      =  len(_lowerCAmelCase	) % num_proc
  UpperCamelCase      :   int			      =  div * index + min(_lowerCAmelCase				,  _lowerCAmelCase	)
  UpperCamelCase      :   Union[str, Any]			      =  start + div + (1 if index < mod else 0)
  split_kwds.append((function, iterable[start:end], types, index, disable_tqdm, desc)	)

 if len(_lowerCAmelCase	) != sum(len(i[1]	) for i in split_kwds	):
  raise ValueError(
      F"""Error dividing inputs iterable among processes. """
      F"""Total number of objects {len(_lowerCAmelCase	)}, """
      F"""length: {sum(len(i[1]	) for i in split_kwds	)}"""	)

 logger.info(
     F"""Spawning {num_proc} processes for {len(_lowerCAmelCase	)} objects in slices of {[len(i[1]	) for i in split_kwds]}"""	)
 UpperCamelCase      ,       UpperCamelCase      :   int			      =  None, None
 if not disable_tqdm:
  UpperCamelCase      ,       UpperCamelCase      :   Dict			      =  (RLock(),), tqdm.set_lock
 with Pool(_lowerCAmelCase				,  initargs=_lowerCAmelCase				,  initializer=_lowerCAmelCase	) as pool:
  UpperCamelCase      :   str			      =  pool.map(_lowerCAmelCase				,  _lowerCAmelCase	)
 logger.info(F"""Finished {num_proc} processes"""	)
 UpperCamelCase      :   List[Any]			      =  [obj for proc_res in mapped for obj in proc_res]
 logger.info(F"""Unpacked {len(_lowerCAmelCase	)} objects"""	)

 return mapped



def 						A_							(	_lowerCAmelCase				,  _lowerCAmelCase				,  _lowerCAmelCase				,  _lowerCAmelCase				,  _lowerCAmelCase				,  _lowerCAmelCase				,  _lowerCAmelCase	)		->					Optional[Any]:
 # progress bar is not yet supported for _map_with_joblib, because tqdm couldn't accurately be applied to joblib,
 # and it requires monkey-patching joblib internal classes which is subject to change
 import joblib

 with joblib.parallel_backend(ParallelBackendConfig.backend_name				,  n_jobs=_lowerCAmelCase	):
  return joblib.Parallel()(
      joblib.delayed(_lowerCAmelCase	)((function, obj, types, None, True, None)	) for obj in iterable	)



@experimental
@contextlib.contextmanager
def 						A_							(	_lowerCAmelCase	)		->					int:
 UpperCamelCase      :   Optional[int]			      =  backend_name

 if backend_name == "spark":
  from joblibspark import register_spark

  register_spark()

  # TODO: call create_cache_and_write_probe if "download" in steps
  # TODO: raise NotImplementedError when Dataset.map etc is called

 try:
  yield
 finally:
  UpperCamelCase      :   List[Any]			      =  None







import numpy as np
import qiskit



def 						A_							(	_lowerCAmelCase = 8				,  _lowerCAmelCase = None	)		->					str:
 UpperCamelCase      :   Tuple			      =  np.random.default_rng(seed=_lowerCAmelCase	)

 # Roughly 25% of the qubits will contribute to the key.
 # So we take more than we need.
 UpperCamelCase      :   List[str]			      =  6 * key_len
 # Measurement basis for Alice's qubits.
 UpperCamelCase      :   List[Any]			      =  rng.integers(2				,  size=_lowerCAmelCase	)
 # The set of states Alice will prepare.
 UpperCamelCase      :   List[Any]			      =  rng.integers(2				,  size=_lowerCAmelCase	)
 # Measurement basis for Bob's qubits.
 UpperCamelCase      :   Optional[int]			      =  rng.integers(2				,  size=_lowerCAmelCase	)

 # Quantum Circuit to simulate BB84
 UpperCamelCase      :   List[Any]			      =  qiskit.QuantumCircuit(_lowerCAmelCase				,  name="BB84"	)

 # Alice prepares her qubits according to rules above.
 for index, _ in enumerate(_lowerCAmelCase	):
  if alice_state[index] == 1:
   bbaa_circ.x(_lowerCAmelCase	)
  if alice_basis[index] == 1:
   bbaa_circ.h(_lowerCAmelCase	)
 bbaa_circ.barrier()

 # Bob measures the received qubits according to rules above.
 for index, _ in enumerate(_lowerCAmelCase	):
  if bob_basis[index] == 1:
   bbaa_circ.h(_lowerCAmelCase	)

 bbaa_circ.barrier()
 bbaa_circ.measure_all()

 # Simulate the quantum circuit.
 UpperCamelCase      :   Union[str, Any]			      =  qiskit.Aer.get_backend("aer_simulator"	)
 # We only need to run one shot because the key is unique.
 # Multiple shots will produce the same key.
 UpperCamelCase      :   Tuple			      =  qiskit.execute(_lowerCAmelCase				,  _lowerCAmelCase				,  shots=1				,  seed_simulator=_lowerCAmelCase	)
 # Returns the result of measurement.
 UpperCamelCase      :   Optional[Any]			      =  job.result().get_counts(_lowerCAmelCase	).most_frequent()

 # Extracting the generated key from the simulation results.
 # Only keep measurement results where Alice and Bob chose the same basis.
 UpperCamelCase      :   Tuple			      =  "".join(
     [
         result_bit
         for alice_basis_bit, bob_basis_bit, result_bit in zip(
             _lowerCAmelCase				,  _lowerCAmelCase				,  _lowerCAmelCase	)
         if alice_basis_bit == bob_basis_bit
     ]	)

 # Get final key. Pad with 0 if too short, otherwise truncate.
 UpperCamelCase      :   Tuple			      =  gen_key[:key_len] if len(_lowerCAmelCase	) >= key_len else gen_key.ljust(_lowerCAmelCase				,  "0"	)
 return key


if __name__ == "__main__":
    print(f"""The generated key is : {bbaa(8, seed=0)}""")
    from doctest import testmod

    testmod()
