'''simple docstring'''




import argparse
import json

import torch

from diffusers import AutoencoderKL, DDPMPipeline, DDPMScheduler, UNetaDModel, VQModel




def 				lowerCamelCase      ( UpperCAmelCase__			:   List[Any] , UpperCAmelCase__			:   Any=1					)     -> int:





							if n_shave_prefix_segments >= 0:
														return ".".join(path.split("""."""					)[n_shave_prefix_segments:]					)
							else:
														return ".".join(path.split("""."""					)[:n_shave_prefix_segments]					)




def 				lowerCamelCase      ( UpperCAmelCase__			:   List[str] , UpperCAmelCase__			:   Any=0					)     -> Tuple:
							lowercase_   :     int									=			[]
							for old_item in old_list:
														lowercase_   :     Tuple									=			old_item
														lowercase_   :     Tuple									=			new_item.replace("""block.""" , """resnets."""					)
														lowercase_   :     str									=			new_item.replace("""conv_shorcut""" , """conv1"""					)
														lowercase_   :     List[Any]									=			new_item.replace("""in_shortcut""" , """conv_shortcut"""					)
														lowercase_   :     List[str]									=			new_item.replace("""temb_proj""" , """time_emb_proj"""					)

														lowercase_   :     Dict									=			shave_segments(UpperCAmelCase__ , n_shave_prefix_segments=UpperCAmelCase__					)

														mapping.append({"""old""": old_item, """new""": new_item}					)

							return mapping




def 				lowerCamelCase      ( UpperCAmelCase__			:   Union[str, Any] , UpperCAmelCase__			:   Any=0 , UpperCAmelCase__			:   Tuple=False					)     -> List[Any]:
							lowercase_   :     List[Any]									=			[]
							for old_item in old_list:
														lowercase_   :     Optional[int]									=			old_item

														# In `model.mid`, the layer is called `attn`.
														if not in_mid:
																					lowercase_   :     str									=			new_item.replace("""attn""" , """attentions"""					)
														lowercase_   :     List[str]									=			new_item.replace(""".k.""" , """.key."""					)
														lowercase_   :     int									=			new_item.replace(""".v.""" , """.value."""					)
														lowercase_   :     Optional[Any]									=			new_item.replace(""".q.""" , """.query."""					)

														lowercase_   :     Dict									=			new_item.replace("""proj_out""" , """proj_attn"""					)
														lowercase_   :     int									=			new_item.replace("""norm""" , """group_norm"""					)

														lowercase_   :     Union[str, Any]									=			shave_segments(UpperCAmelCase__ , n_shave_prefix_segments=UpperCAmelCase__					)
														mapping.append({"""old""": old_item, """new""": new_item}					)

							return mapping




def 				lowerCamelCase      ( UpperCAmelCase__			:   str , UpperCAmelCase__			:   List[Any] , UpperCAmelCase__			:   str , UpperCAmelCase__			:   Dict=None , UpperCAmelCase__			:   Dict=None , UpperCAmelCase__			:   Any=None					)     -> List[Any]:
							assert isinstance(UpperCAmelCase__ , UpperCAmelCase__					), "Paths should be a list of dicts containing 'old' and 'new' keys."

							if attention_paths_to_split is not None:
														if config is None:
																					raise ValueError("""Please specify the config if setting \'attention_paths_to_split\' to \'True\'."""					)

														for path, path_map in attention_paths_to_split.items():
																					lowercase_   :     str									=			old_checkpoint[path]
																					lowercase_   :     List[str]									=			old_tensor.shape[0] // 3

																					lowercase_   :     Dict									=			(-1, channels) if len(old_tensor.shape					) == 3 else (-1)

																					lowercase_   :     Tuple									=			old_tensor.shape[0] // config.get("""num_head_channels""" , 1					) // 3

																					lowercase_   :     Union[str, Any]									=			old_tensor.reshape((num_heads, 3 * channels // num_heads) + old_tensor.shape[1:]					)
																					lowercase_		,		lowercase_		,		lowercase_   :     Optional[Any]									=			old_tensor.split(channels // num_heads , dim=1					)

																					lowercase_   :     List[str]									=			query.reshape(UpperCAmelCase__					).squeeze()
																					lowercase_   :     Optional[int]									=			key.reshape(UpperCAmelCase__					).squeeze()
																					lowercase_   :     str									=			value.reshape(UpperCAmelCase__					).squeeze()

							for path in paths:
														lowercase_   :     List[str]									=			path["""new"""]

														if attention_paths_to_split is not None and new_path in attention_paths_to_split:
																					continue

														lowercase_   :     str									=			new_path.replace("""down.""" , """down_blocks."""					)
														lowercase_   :     Any									=			new_path.replace("""up.""" , """up_blocks."""					)

														if additional_replacements is not None:
																					for replacement in additional_replacements:
																												lowercase_   :     Optional[int]									=			new_path.replace(replacement["""old"""] , replacement["""new"""]					)

														if "attentions" in new_path:
																					lowercase_   :     Optional[Any]									=			old_checkpoint[path["""old"""]].squeeze()
														else:
																					lowercase_   :     int									=			old_checkpoint[path["""old"""]]




def 				lowerCamelCase      ( UpperCAmelCase__			:   List[str] , UpperCAmelCase__			:   Dict					)     -> List[str]:
							lowercase_   :     List[str]									=			{}

							lowercase_   :     List[str]									=			checkpoint["""temb.dense.0.weight"""]
							lowercase_   :     str									=			checkpoint["""temb.dense.0.bias"""]
							lowercase_   :     Optional[Any]									=			checkpoint["""temb.dense.1.weight"""]
							lowercase_   :     Any									=			checkpoint["""temb.dense.1.bias"""]

							lowercase_   :     Any									=			checkpoint["""norm_out.weight"""]
							lowercase_   :     Any									=			checkpoint["""norm_out.bias"""]

							lowercase_   :     List[str]									=			checkpoint["""conv_in.weight"""]
							lowercase_   :     str									=			checkpoint["""conv_in.bias"""]
							lowercase_   :     str									=			checkpoint["""conv_out.weight"""]
							lowercase_   :     Optional[Any]									=			checkpoint["""conv_out.bias"""]

							lowercase_   :     Tuple									=			len({""".""".join(layer.split("""."""					)[:2]					) for layer in checkpoint if """down""" in layer}					)
							lowercase_   :     Tuple									=			{
							    layer_id: [key for key in checkpoint if F'''down.{layer_id}''' in key] for layer_id in range(UpperCAmelCase__					)
							}

							lowercase_   :     Union[str, Any]									=			len({""".""".join(layer.split("""."""					)[:2]					) for layer in checkpoint if """up""" in layer}					)
							lowercase_   :     Optional[Any]									=			{layer_id: [key for key in checkpoint if F'''up.{layer_id}''' in key] for layer_id in range(UpperCAmelCase__					)}

							for i in range(UpperCAmelCase__					):
														lowercase_   :     List[Any]									=			(i - 1) // (config["""layers_per_block"""] + 1)

														if any("""downsample""" in layer for layer in down_blocks[i]					):
																					lowercase_   :     str									=			checkpoint[
																					    F'''down.{i}.downsample.op.weight'''
																					]
																					lowercase_   :     Optional[int]									=			checkpoint[F'''down.{i}.downsample.op.bias''']
														#            new_checkpoint[f'down_blocks.{i}.downsamplers.0.op.weight'] = checkpoint[f'down.{i}.downsample.conv.weight']
														#            new_checkpoint[f'down_blocks.{i}.downsamplers.0.op.bias'] = checkpoint[f'down.{i}.downsample.conv.bias']

														if any("""block""" in layer for layer in down_blocks[i]					):
																					lowercase_   :     Any									=			len(
																					    {""".""".join(shave_segments(UpperCAmelCase__ , 2					).split("""."""					)[:2]					) for layer in down_blocks[i] if """block""" in layer}					)
																					lowercase_   :     Dict									=			{
																					    layer_id: [key for key in down_blocks[i] if F'''block.{layer_id}''' in key]
																					    for layer_id in range(UpperCAmelCase__					)
																					}

																					if num_blocks > 0:
																												for j in range(config["""layers_per_block"""]					):
																																			lowercase_   :     List[Any]									=			renew_resnet_paths(blocks[j]					)
																																			assign_to_checkpoint(UpperCAmelCase__ , UpperCAmelCase__ , UpperCAmelCase__					)

														if any("""attn""" in layer for layer in down_blocks[i]					):
																					lowercase_   :     Any									=			len(
																					    {""".""".join(shave_segments(UpperCAmelCase__ , 2					).split("""."""					)[:2]					) for layer in down_blocks[i] if """attn""" in layer}					)
																					lowercase_   :     Dict									=			{
																					    layer_id: [key for key in down_blocks[i] if F'''attn.{layer_id}''' in key]
																					    for layer_id in range(UpperCAmelCase__					)
																					}

																					if num_attn > 0:
																												for j in range(config["""layers_per_block"""]					):
																																			lowercase_   :     Dict									=			renew_attention_paths(attns[j]					)
																																			assign_to_checkpoint(UpperCAmelCase__ , UpperCAmelCase__ , UpperCAmelCase__ , config=UpperCAmelCase__					)

							lowercase_   :     Optional[int]									=			[key for key in checkpoint if """mid.block_1""" in key]
							lowercase_   :     Optional[int]									=			[key for key in checkpoint if """mid.block_2""" in key]
							lowercase_   :     Tuple									=			[key for key in checkpoint if """mid.attn_1""" in key]

							# Mid new 2
							lowercase_   :     Union[str, Any]									=			renew_resnet_paths(UpperCAmelCase__					)
							assign_to_checkpoint(
							    UpperCAmelCase__ , UpperCAmelCase__ , UpperCAmelCase__ , additional_replacements=[{"""old""": """mid.""", """new""": """mid_new_2."""}, {"""old""": """block_1""", """new""": """resnets.0"""}] , )

							lowercase_   :     Optional[Any]									=			renew_resnet_paths(UpperCAmelCase__					)
							assign_to_checkpoint(
							    UpperCAmelCase__ , UpperCAmelCase__ , UpperCAmelCase__ , additional_replacements=[{"""old""": """mid.""", """new""": """mid_new_2."""}, {"""old""": """block_2""", """new""": """resnets.1"""}] , )

							lowercase_   :     Any									=			renew_attention_paths(UpperCAmelCase__ , in_mid=UpperCAmelCase__					)
							assign_to_checkpoint(
							    UpperCAmelCase__ , UpperCAmelCase__ , UpperCAmelCase__ , additional_replacements=[{"""old""": """mid.""", """new""": """mid_new_2."""}, {"""old""": """attn_1""", """new""": """attentions.0"""}] , )

							for i in range(UpperCAmelCase__					):
														lowercase_   :     int									=			num_up_blocks - 1 - i

														if any("""upsample""" in layer for layer in up_blocks[i]					):
																					lowercase_   :     Any									=			checkpoint[
																					    F'''up.{i}.upsample.conv.weight'''
																					]
																					lowercase_   :     Union[str, Any]									=			checkpoint[F'''up.{i}.upsample.conv.bias''']

														if any("""block""" in layer for layer in up_blocks[i]					):
																					lowercase_   :     Dict									=			len(
																					    {""".""".join(shave_segments(UpperCAmelCase__ , 2					).split("""."""					)[:2]					) for layer in up_blocks[i] if """block""" in layer}					)
																					lowercase_   :     Dict									=			{
																					    layer_id: [key for key in up_blocks[i] if F'''block.{layer_id}''' in key] for layer_id in range(UpperCAmelCase__					)
																					}

																					if num_blocks > 0:
																												for j in range(config["""layers_per_block"""] + 1					):
																																			lowercase_   :     Optional[int]									=			{"""old""": F'''up_blocks.{i}''', """new""": F'''up_blocks.{block_id}'''}
																																			lowercase_   :     Dict									=			renew_resnet_paths(blocks[j]					)
																																			assign_to_checkpoint(UpperCAmelCase__ , UpperCAmelCase__ , UpperCAmelCase__ , additional_replacements=[replace_indices]					)

														if any("""attn""" in layer for layer in up_blocks[i]					):
																					lowercase_   :     int									=			len(
																					    {""".""".join(shave_segments(UpperCAmelCase__ , 2					).split("""."""					)[:2]					) for layer in up_blocks[i] if """attn""" in layer}					)
																					lowercase_   :     Optional[Any]									=			{
																					    layer_id: [key for key in up_blocks[i] if F'''attn.{layer_id}''' in key] for layer_id in range(UpperCAmelCase__					)
																					}

																					if num_attn > 0:
																												for j in range(config["""layers_per_block"""] + 1					):
																																			lowercase_   :     Union[str, Any]									=			{"""old""": F'''up_blocks.{i}''', """new""": F'''up_blocks.{block_id}'''}
																																			lowercase_   :     Optional[Any]									=			renew_attention_paths(attns[j]					)
																																			assign_to_checkpoint(UpperCAmelCase__ , UpperCAmelCase__ , UpperCAmelCase__ , additional_replacements=[replace_indices]					)

							lowercase_   :     Any									=			{k.replace("""mid_new_2""" , """mid_block"""					): v for k, v in new_checkpoint.items()}
							return new_checkpoint




def 				lowerCamelCase      ( UpperCAmelCase__			:   str , UpperCAmelCase__			:   str					)     -> List[str]:
							lowercase_   :     List[str]									=			{}

							lowercase_   :     Tuple									=			checkpoint["""encoder.norm_out.weight"""]
							lowercase_   :     Optional[int]									=			checkpoint["""encoder.norm_out.bias"""]

							lowercase_   :     Tuple									=			checkpoint["""encoder.conv_in.weight"""]
							lowercase_   :     Optional[int]									=			checkpoint["""encoder.conv_in.bias"""]
							lowercase_   :     Union[str, Any]									=			checkpoint["""encoder.conv_out.weight"""]
							lowercase_   :     Any									=			checkpoint["""encoder.conv_out.bias"""]

							lowercase_   :     Optional[int]									=			checkpoint["""decoder.norm_out.weight"""]
							lowercase_   :     Tuple									=			checkpoint["""decoder.norm_out.bias"""]

							lowercase_   :     Union[str, Any]									=			checkpoint["""decoder.conv_in.weight"""]
							lowercase_   :     Optional[Any]									=			checkpoint["""decoder.conv_in.bias"""]
							lowercase_   :     Any									=			checkpoint["""decoder.conv_out.weight"""]
							lowercase_   :     str									=			checkpoint["""decoder.conv_out.bias"""]

							lowercase_   :     str									=			len({""".""".join(layer.split("""."""					)[:3]					) for layer in checkpoint if """down""" in layer}					)
							lowercase_   :     Union[str, Any]									=			{
							    layer_id: [key for key in checkpoint if F'''down.{layer_id}''' in key] for layer_id in range(UpperCAmelCase__					)
							}

							lowercase_   :     str									=			len({""".""".join(layer.split("""."""					)[:3]					) for layer in checkpoint if """up""" in layer}					)
							lowercase_   :     str									=			{layer_id: [key for key in checkpoint if F'''up.{layer_id}''' in key] for layer_id in range(UpperCAmelCase__					)}

							for i in range(UpperCAmelCase__					):
														lowercase_   :     Optional[Any]									=			(i - 1) // (config["""layers_per_block"""] + 1)

														if any("""downsample""" in layer for layer in down_blocks[i]					):
																					lowercase_   :     str									=			checkpoint[
																					    F'''encoder.down.{i}.downsample.conv.weight'''
																					]
																					lowercase_   :     List[Any]									=			checkpoint[
																					    F'''encoder.down.{i}.downsample.conv.bias'''
																					]

														if any("""block""" in layer for layer in down_blocks[i]					):
																					lowercase_   :     Optional[Any]									=			len(
																					    {""".""".join(shave_segments(UpperCAmelCase__ , 3					).split("""."""					)[:3]					) for layer in down_blocks[i] if """block""" in layer}					)
																					lowercase_   :     Optional[int]									=			{
																					    layer_id: [key for key in down_blocks[i] if F'''block.{layer_id}''' in key]
																					    for layer_id in range(UpperCAmelCase__					)
																					}

																					if num_blocks > 0:
																												for j in range(config["""layers_per_block"""]					):
																																			lowercase_   :     Optional[Any]									=			renew_resnet_paths(blocks[j]					)
																																			assign_to_checkpoint(UpperCAmelCase__ , UpperCAmelCase__ , UpperCAmelCase__					)

														if any("""attn""" in layer for layer in down_blocks[i]					):
																					lowercase_   :     Tuple									=			len(
																					    {""".""".join(shave_segments(UpperCAmelCase__ , 3					).split("""."""					)[:3]					) for layer in down_blocks[i] if """attn""" in layer}					)
																					lowercase_   :     Tuple									=			{
																					    layer_id: [key for key in down_blocks[i] if F'''attn.{layer_id}''' in key]
																					    for layer_id in range(UpperCAmelCase__					)
																					}

																					if num_attn > 0:
																												for j in range(config["""layers_per_block"""]					):
																																			lowercase_   :     List[str]									=			renew_attention_paths(attns[j]					)
																																			assign_to_checkpoint(UpperCAmelCase__ , UpperCAmelCase__ , UpperCAmelCase__ , config=UpperCAmelCase__					)

							lowercase_   :     Any									=			[key for key in checkpoint if """mid.block_1""" in key]
							lowercase_   :     Dict									=			[key for key in checkpoint if """mid.block_2""" in key]
							lowercase_   :     Optional[int]									=			[key for key in checkpoint if """mid.attn_1""" in key]

							# Mid new 2
							lowercase_   :     Any									=			renew_resnet_paths(UpperCAmelCase__					)
							assign_to_checkpoint(
							    UpperCAmelCase__ , UpperCAmelCase__ , UpperCAmelCase__ , additional_replacements=[{"""old""": """mid.""", """new""": """mid_new_2."""}, {"""old""": """block_1""", """new""": """resnets.0"""}] , )

							lowercase_   :     List[str]									=			renew_resnet_paths(UpperCAmelCase__					)
							assign_to_checkpoint(
							    UpperCAmelCase__ , UpperCAmelCase__ , UpperCAmelCase__ , additional_replacements=[{"""old""": """mid.""", """new""": """mid_new_2."""}, {"""old""": """block_2""", """new""": """resnets.1"""}] , )

							lowercase_   :     Optional[Any]									=			renew_attention_paths(UpperCAmelCase__ , in_mid=UpperCAmelCase__					)
							assign_to_checkpoint(
							    UpperCAmelCase__ , UpperCAmelCase__ , UpperCAmelCase__ , additional_replacements=[{"""old""": """mid.""", """new""": """mid_new_2."""}, {"""old""": """attn_1""", """new""": """attentions.0"""}] , )

							for i in range(UpperCAmelCase__					):
														lowercase_   :     Optional[Any]									=			num_up_blocks - 1 - i

														if any("""upsample""" in layer for layer in up_blocks[i]					):
																					lowercase_   :     List[str]									=			checkpoint[
																					    F'''decoder.up.{i}.upsample.conv.weight'''
																					]
																					lowercase_   :     Dict									=			checkpoint[
																					    F'''decoder.up.{i}.upsample.conv.bias'''
																					]

														if any("""block""" in layer for layer in up_blocks[i]					):
																					lowercase_   :     Optional[Any]									=			len(
																					    {""".""".join(shave_segments(UpperCAmelCase__ , 3					).split("""."""					)[:3]					) for layer in up_blocks[i] if """block""" in layer}					)
																					lowercase_   :     List[Any]									=			{
																					    layer_id: [key for key in up_blocks[i] if F'''block.{layer_id}''' in key] for layer_id in range(UpperCAmelCase__					)
																					}

																					if num_blocks > 0:
																												for j in range(config["""layers_per_block"""] + 1					):
																																			lowercase_   :     str									=			{"""old""": F'''up_blocks.{i}''', """new""": F'''up_blocks.{block_id}'''}
																																			lowercase_   :     List[Any]									=			renew_resnet_paths(blocks[j]					)
																																			assign_to_checkpoint(UpperCAmelCase__ , UpperCAmelCase__ , UpperCAmelCase__ , additional_replacements=[replace_indices]					)

														if any("""attn""" in layer for layer in up_blocks[i]					):
																					lowercase_   :     Optional[int]									=			len(
																					    {""".""".join(shave_segments(UpperCAmelCase__ , 3					).split("""."""					)[:3]					) for layer in up_blocks[i] if """attn""" in layer}					)
																					lowercase_   :     Optional[int]									=			{
																					    layer_id: [key for key in up_blocks[i] if F'''attn.{layer_id}''' in key] for layer_id in range(UpperCAmelCase__					)
																					}

																					if num_attn > 0:
																												for j in range(config["""layers_per_block"""] + 1					):
																																			lowercase_   :     Optional[int]									=			{"""old""": F'''up_blocks.{i}''', """new""": F'''up_blocks.{block_id}'''}
																																			lowercase_   :     List[str]									=			renew_attention_paths(attns[j]					)
																																			assign_to_checkpoint(UpperCAmelCase__ , UpperCAmelCase__ , UpperCAmelCase__ , additional_replacements=[replace_indices]					)

							lowercase_   :     Dict									=			{k.replace("""mid_new_2""" , """mid_block"""					): v for k, v in new_checkpoint.items()}
							lowercase_   :     Dict									=			checkpoint["""quant_conv.weight"""]
							lowercase_   :     str									=			checkpoint["""quant_conv.bias"""]
							if "quantize.embedding.weight" in checkpoint:
														lowercase_   :     Any									=			checkpoint["""quantize.embedding.weight"""]
							lowercase_   :     List[str]									=			checkpoint["""post_quant_conv.weight"""]
							lowercase_   :     Optional[int]									=			checkpoint["""post_quant_conv.bias"""]

							return new_checkpoint


if __name__ == "__main__":
						_lowercase		:					Optional[Any]													=		argparse.ArgumentParser()

						parser.add_argument(
						    "--checkpoint_path", default=None, type=str, required=True, help="Path to the checkpoint to convert."
						)

						parser.add_argument(
						    "--config_file",
						    default=None,
						    type=str,
						    required=True,
						    help="The config json file corresponding to the architecture.",
						)

						parser.add_argument("--dump_path", default=None, type=str, required=True, help="Path to the output model.")

						_lowercase		:					List[str]													=		parser.parse_args()
						_lowercase		:					Optional[int]													=		torch.load(args.checkpoint_path)

						with open(args.config_file) as f:
												_lowercase		:					Dict													=		json.loads(f.read())

						# unet case
						_lowercase		:					Any													=		{key.split(".")[0] for key in checkpoint.keys()}
						if "encoder" in key_prefix_set and "decoder" in key_prefix_set:
												_lowercase		:					Tuple													=		convert_vq_autoenc_checkpoint(checkpoint, config)
						else:
												_lowercase		:					Dict													=		convert_ddpm_checkpoint(checkpoint, config)

						if "ddpm" in config:
												del config["ddpm"]

						if config["_class_name"] == "VQModel":
												_lowercase		:					Any													=		VQModel(**config)
												model.load_state_dict(converted_checkpoint)
												model.save_pretrained(args.dump_path)
						elif config["_class_name"] == "AutoencoderKL":
												_lowercase		:					str													=		AutoencoderKL(**config)
												model.load_state_dict(converted_checkpoint)
												model.save_pretrained(args.dump_path)
						else:
												_lowercase		:					str													=		UNetaDModel(**config)
												model.load_state_dict(converted_checkpoint)

												_lowercase		:					Union[str, Any]													=		DDPMScheduler.from_config("/".join(args.checkpoint_path.split("/")[:-1]))

												_lowercase		:					Any													=		DDPMPipeline(unet=model, scheduler=scheduler)
												pipe.save_pretrained(args.dump_path)







"""simple docstring"""




__lowerCAmelCase							:  Any 				=		{
    '''0''': 1,
    '''1''': 1,
    '''2''': 2,
    '''3''': 6,
    '''4''': 24,
    '''5''': 120,
    '''6''': 720,
    '''7''': 5040,
    '''8''': 40320,
    '''9''': 362880,
}

__lowerCAmelCase							:  List[Any] 				=		{145: 145}

__lowerCAmelCase							:  Any 				=		{
    145: 0,
    169: 3,
    36301: 3,
    1454: 3,
    871: 2,
    45361: 2,
    872: 2,
}

def __snake_case (      UpperCamelCase    )       ->    int:


		"""simple docstring"""





		if n in CACHE_SUM_DIGIT_FACTORIALS:
				return CACHE_SUM_DIGIT_FACTORIALS[n]
		a__   		=     sum(DIGIT_FACTORIALS[let] for let in str(UpperCamelCase    )    )
		a__   		=     ret
		return ret

def __snake_case (      UpperCamelCase						,					UpperCamelCase = None    )       ->    int:


		"""simple docstring"""





		a__   		=     previous or set()
		if n in CHAIN_LENGTH_CACHE:
				return CHAIN_LENGTH_CACHE[n]
		a__   		=     sum_digit_factorials(UpperCamelCase    )
		if next_number in previous:
				a__   		=     0
				return 0
		else:
				previous.add(UpperCamelCase    )
				a__   		=     1 + chain_length(UpperCamelCase						,					UpperCamelCase    )
				a__   		=     ret
				return ret

def __snake_case (      UpperCamelCase = 60						,					UpperCamelCase = 1_000_000    )       ->    int:


		"""simple docstring"""





		return sum(1 for i in range(1						,					UpperCamelCase    ) if chain_length(UpperCamelCase    ) == num_terms    )


if __name__ == "__main__":
					print(f"{solution() = }")




