



import os
import time
from dataclasses import dataclass, field
from enum import Enum
from typing import Dict, List, Optional, Union

import torch
from filelock import FileLock
from torch.utils.data import Dataset

from ...models.auto.modeling_auto import MODEL_FOR_QUESTION_ANSWERING_MAPPING
from ...tokenization_utils import PreTrainedTokenizer
from ...utils import logging
from ..processors.squad import SquadFeatures, SquadVaProcessor, SquadVaProcessor, squad_convert_examples_to_features


__snake_case     :Optional[Any]			       =logging.get_logger(__name__)

__snake_case     :int			       =list(MODEL_FOR_QUESTION_ANSWERING_MAPPING.keys())
__snake_case     :Optional[int]			       =tuple(conf.model_type for conf in MODEL_CONFIG_CLASSES)



@dataclass
class        lowerCAmelCase__					:
				A_   :     str								=     field(
				    default=_lowerCamelCase ,				metadata={'help': 'Model type selected in the list: ' + ', '.join(_lowerCamelCase       )}       )
				A_   :     str								=     field(
				    default=_lowerCamelCase ,				metadata={'help': 'The input data dir. Should contain the .json files for the SQuAD task.'}       )
				A_   :     int								=     field(
				    default=1_2_8 ,				metadata={
				        'help': (
				            'The maximum total input sequence length after tokenization. Sequences longer '
				            'than this will be truncated, sequences shorter will be padded.'
				        )
				    } ,				)
				A_   :     int								=     field(
				    default=1_2_8 ,				metadata={'help': 'When splitting up a long document into chunks, how much stride to take between chunks.'} ,				)
				A_   :     int								=     field(
				    default=6_4 ,				metadata={
				        'help': (
				            'The maximum number of tokens for the question. Questions longer than this will '
				            'be truncated to this length.'
				        )
				    } ,				)
				A_   :     int								=     field(
				    default=3_0 ,				metadata={
				        'help': (
				            'The maximum length of an answer that can be generated. This is needed because the start '
				            'and end predictions are not conditioned on one another.'
				        )
				    } ,				)
				A_   :     bool								=     field(
				    default=_lowerCamelCase ,				metadata={'help': 'Overwrite the cached training and evaluation sets'}       )
				A_   :     bool								=     field(
				    default=_lowerCamelCase ,				metadata={'help': 'If true, the SQuAD examples contain some that do not have an answer.'}       )
				A_   :     float								=     field(
				    default=0.0 ,				metadata={'help': 'If null_score - best_non_null is greater than the threshold predict null.'}       )
				A_   :     int								=     field(
				    default=2_0 ,				metadata={'help': 'If null_score - best_non_null is greater than the threshold predict null.'}       )
				A_   :     int								=     field(
				    default=0 ,				metadata={
				        'help': (
				            'language id of input for language-specific xlm models (see'
				            ' tokenization_xlm.PRETRAINED_INIT_CONFIGURATION)'
				        )
				    } ,				)
				A_   :     int								=     field(default=1 ,				metadata={'help': 'multiple threads for converting example to features'}       )





class        lowerCAmelCase__					(  _lowerCamelCase       ):
				A_   :     str								=     'train'
				A_   :     str								=     'dev'





class        lowerCAmelCase__					(  _lowerCamelCase       ):
				A_   :     SquadDataTrainingArguments
				A_   :     List[SquadFeatures]
				A_   :     Split
				A_   :     bool


				def __init__(							self      :  Optional[int]						, __UpperCamelCase      :  SquadDataTrainingArguments						, __UpperCamelCase      :  PreTrainedTokenizer						, __UpperCamelCase      :  Optional[int] = None						, __UpperCamelCase      :  Union[str, Split] = Split.train						, __UpperCamelCase      :  Optional[bool] = False						, __UpperCamelCase      :  Optional[str] = None						, __UpperCamelCase      :  Optional[str] = "pt"						, )		->     Any:
											A      						=    args
											A      						=    is_language_sensitive
											A      						=    SquadVaProcessor() if args.version_2_with_negative else SquadVaProcessor()
											if isinstance(__UpperCamelCase						, __UpperCamelCase       ):
																		try:
																									A      						=    Split[mode]
																		except KeyError:
																									raise KeyError('mode is not a valid split name'       )
											A      						=    mode
											# Load data features from cache or dataset file
											A      						=    'v2' if args.version_2_with_negative else 'v1'
											A      						=    os.path.join(
											    cache_dir if cache_dir is not None else args.data_dir						, f'''cached_{mode.value}_{tokenizer.__class__.__name__}_{args.max_seq_length}_{version_tag}'''						, )

											# Make sure only the first process in distributed training processes the dataset,
											# and the others will use the cache.
											A      						=    cached_features_file + '.lock'
											with FileLock(__UpperCamelCase       ):
																		if os.path.exists(__UpperCamelCase       ) and not args.overwrite_cache:
																									A      						=    time.time()
																									A      						=    torch.load(__UpperCamelCase       )

																									# Legacy cache files have only features, while new cache files
																									# will have dataset and examples also.
																									A      						=    self.old_features['features']
																									A      						=    self.old_features.get('dataset'						, __UpperCamelCase       )
																									A      						=    self.old_features.get('examples'						, __UpperCamelCase       )
																									logger.info(
																									    f'''Loading features from cached file {cached_features_file} [took %.3f s]'''						, time.time() - start       )

																									if self.dataset is None or self.examples is None:
																																logger.warning(
																																    f'''Deleting cached file {cached_features_file} will allow dataset and examples to be cached in'''
																																    ' future run'       )
																		else:
																									if mode == Split.dev:
																																A      						=    self.processor.get_dev_examples(args.data_dir       )
																									else:
																																A      						=    self.processor.get_train_examples(args.data_dir       )

																									A						,   A      						=    squad_convert_examples_to_features(
																									    examples=self.examples						, tokenizer=__UpperCamelCase						, max_seq_length=args.max_seq_length						, doc_stride=args.doc_stride						, max_query_length=args.max_query_length						, is_training=mode == Split.train						, threads=args.threads						, return_dataset=__UpperCamelCase						, )

																									A      						=    time.time()
																									torch.save(
																									    {'features': self.features, 'dataset': self.dataset, 'examples': self.examples}						, __UpperCamelCase						, )
																									# ^ This seems to take a lot of time so I want to investigate why and how we can improve.
																									logger.info(
																									    f'''Saving features into cached file {cached_features_file} [took {time.time() - start:.3f} s]'''       )


				def __len__(							self      :  Optional[Any]       )		->     Tuple:
											return len(self.features       )


				def __getitem__(							self      :  Tuple						, __UpperCamelCase      :  List[Any]       )		->     Dict[str, torch.Tensor]:
											# Convert to Tensors and build dataset
											A      						=    self.features[i]

											A      						=    torch.tensor(feature.input_ids						, dtype=torch.long       )
											A      						=    torch.tensor(feature.attention_mask						, dtype=torch.long       )
											A      						=    torch.tensor(feature.token_type_ids						, dtype=torch.long       )
											A      						=    torch.tensor(feature.cls_index						, dtype=torch.long       )
											A      						=    torch.tensor(feature.p_mask						, dtype=torch.float       )
											A      						=    torch.tensor(feature.is_impossible						, dtype=torch.float       )

											A      						=    {
											    'input_ids': input_ids,
											    'attention_mask': attention_mask,
											    'token_type_ids': token_type_ids,
											}

											if self.args.model_type in ["xlm", "roberta", "distilbert", "camembert"]:
																		del inputs["token_type_ids"]

											if self.args.model_type in ["xlnet", "xlm"]:
																		inputs.update({'cls_index': cls_index, 'p_mask': p_mask}       )
																		if self.args.version_2_with_negative:
																									inputs.update({'is_impossible': is_impossible}       )
																		if self.is_language_sensitive:
																									inputs.update({'langs': (torch.ones(input_ids.shape						, dtype=torch.intaa       ) * self.args.lang_id)}       )

											if self.mode == Split.train:
																		A      						=    torch.tensor(feature.start_position						, dtype=torch.long       )
																		A      						=    torch.tensor(feature.end_position						, dtype=torch.long       )
																		inputs.update({'start_positions': start_positions, 'end_positions': end_positions}       )

											return inputs



import json
import multiprocessing as mp
import re
from collections import defaultdict
from functools import partial
from typing import Dict, List, Optional, Set, Tuple, Type

from datasets import Dataset
from datasketch import MinHash, MinHashLSH
from dpu_utils.utils.iterators import ThreadedIterator
from tqdm import tqdm


__a	 =      re.compile("""[^A-Za-z_0-9]""")
# parameters used in DuplicationIndex
__a	 =      10
__a	 =      256
def 	_UpperCamelCase					(    lowerCAmelCase_ )    ->Optional[MinHash]:
   if len(lowerCAmelCase_ ) < MIN_NUM_TOKENS:
      return None
   UpperCAmelCase      					=  MinHash(num_perm=lowerCAmelCase_ )
   for token in set(lowerCAmelCase_ ):
      min_hash.update(token.encode() )
   return min_hash
def 	_UpperCamelCase					(    lowerCAmelCase_ )    ->Set[str]:
   return {t for t in NON_ALPHA.split(lowerCAmelCase_ ) if len(t.strip() ) > 0}






class 	__lowercase					:





      def __init__(		self    :       List[str]       ,							*,
          __lowerCamelCase    :       float = 0.85       ,							)     -> Any:






         """simple docstring"""







         UpperCAmelCase      					=  duplication_jaccard_threshold
         UpperCAmelCase      					=  NUM_PERM
         UpperCAmelCase      					=  MinHashLSH(threshold=self._duplication_jaccard_threshold       ,							num_perm=self._num_perm							)

         UpperCAmelCase      					=  defaultdict(__lowerCamelCase							)





      def 							_lowercase					(		self    :       Tuple       ,							__lowerCamelCase    :       Tuple       ,							__lowerCamelCase    :       MinHash							)     -> None:






         """simple docstring"""







         UpperCAmelCase      					=  self._index.query(__lowerCamelCase							)
         if code_key in self._index.keys:
            print(F"""Duplicate key {code_key}"""							)
            return

         self._index.insert(__lowerCamelCase       ,							__lowerCamelCase							)
         if len(__lowerCamelCase							) > 0:
            for base_duplicate in close_duplicates:
               if base_duplicate in self._duplicate_clusters:
                  self._duplicate_clusters[base_duplicate].add(__lowerCamelCase							)
                  break
            else:
               self._duplicate_clusters[close_duplicates[0]].add(__lowerCamelCase							)





      def 							_lowercase					(		self    :       Union[str, Any]							)     -> List[List[Dict]]:






         """simple docstring"""







         UpperCAmelCase      					=  []
         for base, duplicates in self._duplicate_clusters.items():
            UpperCAmelCase      					=  [base] + list(__lowerCamelCase							)
            # reformat the cluster to be a list of dict
            UpperCAmelCase      					=  [{"""base_index""": el[0], """repo_name""": el[1], """path""": el[2]} for el in cluster]
            duplicate_clusters.append(__lowerCamelCase							)
         return duplicate_clusters





      def 							_lowercase					(		self    :       Tuple       ,							__lowerCamelCase    :       Optional[int]							)     -> None:






         """simple docstring"""







         UpperCAmelCase      					=  self.get_duplicate_clusters()
         with open(__lowerCamelCase       ,							"""w"""							) as f:
            json.dump(__lowerCamelCase       ,							__lowerCamelCase							)

def 	_UpperCamelCase					(    lowerCAmelCase_ )    ->Tuple:
   UpperCAmelCase					,	UpperCAmelCase      					=  element
   UpperCAmelCase      					=  get_min_hash([t for t in NON_ALPHA.split(data["""content"""] ) if len(t.strip() ) > 0] )
   if min_hash is not None:
      return (index, data["repo_name"], data["path"]), min_hash
def 	_UpperCamelCase					(    lowerCAmelCase_ )    ->int:
   with mp.Pool() as pool:
      for data in pool.imap_unordered(
          _compute_min_hash	,    ThreadedIterator(lowerCAmelCase_	,    max_queue_size=1_0_0_0_0 )	,    chunksize=1_0_0	,    ):
         if data is not None:
            yield data
def 	_UpperCamelCase					(    lowerCAmelCase_	,    lowerCAmelCase_ )    ->Any:
   UpperCAmelCase      					=  DuplicationIndex(duplication_jaccard_threshold=lowerCAmelCase_ )

   for filename, min_hash in tqdm(ThreadedIterator(minhash_iter(enumerate(lowerCAmelCase_ ) )	,    max_queue_size=1_0_0 ) ):
      di.add(lowerCAmelCase_	,    lowerCAmelCase_ )

   # Returns a List[Cluster] where Cluster is List[str] with the filenames.
   return di.get_duplicate_clusters()
def 	_UpperCamelCase					(    lowerCAmelCase_	,    lowerCAmelCase_ )    ->float:
   UpperCAmelCase      					=  get_tokens(lowerCAmelCase_ )
   UpperCAmelCase      					=  get_tokens(lowerCAmelCase_ )
   return len(tokensa & tokensa ) / len(tokensa | tokensa )


__a	 =      None
def 	_UpperCamelCase					(    lowerCAmelCase_	,    lowerCAmelCase_ )    ->Dict:
   UpperCAmelCase      					=  []
   for elementa in cluster:
      UpperCAmelCase      					=  _shared_dataset[elementa["""base_index"""]]["""content"""]
      for elementa in extremes:
         UpperCAmelCase      					=  _shared_dataset[elementa["""base_index"""]]["""content"""]
         if jaccard_similarity(lowerCAmelCase_	,    lowerCAmelCase_ ) >= jaccard_threshold:
            elementa["copies"] += 1
            break
      else:
         UpperCAmelCase      					=  1
         extremes.append(lowerCAmelCase_ )
   return extremes
def 	_UpperCamelCase					(    lowerCAmelCase_	,    lowerCAmelCase_	,    lowerCAmelCase_ )    ->Optional[int]:
   global _shared_dataset
   UpperCAmelCase      					=  dataset
   UpperCAmelCase      					=  []
   UpperCAmelCase      					=  partial(_find_cluster_extremes_shared	,    jaccard_threshold=lowerCAmelCase_ )
   with mp.Pool() as pool:
      for extremes in tqdm(
          pool.imap_unordered(
              lowerCAmelCase_	,    lowerCAmelCase_	,    )	,    total=len(lowerCAmelCase_ )	,    ):
         extremes_list.append(lowerCAmelCase_ )
   return extremes_list







def 	_UpperCamelCase					(    lowerCAmelCase_	,    lowerCAmelCase_ = 0.85 )    ->Tuple[Type[Dataset], List[List[Dict]]]:
   UpperCAmelCase      					=  make_duplicate_clusters(lowerCAmelCase_	,    lowerCAmelCase_ )
   UpperCAmelCase      					=  {x["""base_index"""] for cluster in duplicate_clusters for x in cluster}
   UpperCAmelCase      					=  {}
   UpperCAmelCase      					=  find_extremes(lowerCAmelCase_	,    lowerCAmelCase_	,    lowerCAmelCase_ )
   for extremes in extremes_clusters:
      for element in extremes:
         UpperCAmelCase      					=  element
   UpperCAmelCase      					=  duplicate_indices - set(extreme_dict.keys() )
   UpperCAmelCase      					=  dataset.filter(lambda lowerCAmelCase_	,    lowerCAmelCase_ : idx not in remove_indices	,    with_indices=lowerCAmelCase_ )

   # update duplicate_clusters
   for cluster in duplicate_clusters:
      for element in cluster:
         UpperCAmelCase      					=  element["""base_index"""] in extreme_dict
         if element["is_extreme"]:
            UpperCAmelCase      					=  extreme_dict[element["""base_index"""]]["""copies"""]

   print(F"""Original dataset size: {len(lowerCAmelCase_ )}""" )
   print(F"""Number of duplicate clusters: {len(lowerCAmelCase_ )}""" )
   print(F"""Files in duplicate cluster: {len(lowerCAmelCase_ )}""" )
   print(F"""Unique files in duplicate cluster: {len(lowerCAmelCase_ )}""" )
   print(F"""Filtered dataset size: {len(lowerCAmelCase_ )}""" )

   return ds_filter, duplicate_clusters



