import argparse

import torch

from diffusers import UNetaDConditionModel


def SCREAMING_SNAKE_CASE (   lowercase_:		Optional[Any]						,  lowercase_:		List[Any]						,  lowercase_:		Any						,  lowercase_:		Any=None						,  lowercase_:		int=None						,  lowercase_:		Union[str, Any]=None ):
       assert isinstance(lowercase_						,  lowercase_ ), "Paths should be a list of dicts containing 'old' and 'new' keys."

       # Splits the attention layers into three variables.
       if attention_paths_to_split is not None:
              for path, path_map in attention_paths_to_split.items():
                     _UpperCamelCase   :				Dict       =     old_checkpoint[path]
                     _UpperCamelCase   :				Dict       =     old_tensor.shape[0] // 3

                     _UpperCamelCase   :				int       =     (-1, channels) if len(old_tensor.shape ) == 3 else (-1)

                     _UpperCamelCase   :				List[Any]       =     old_tensor.shape[0] // config['num_head_channels'] // 3

                     _UpperCamelCase   :				List[Any]       =     old_tensor.reshape((num_heads, 3 * channels // num_heads) + old_tensor.shape[1:] )
                     _UpperCamelCase  ,					_UpperCamelCase  ,					_UpperCamelCase   :				Any       =     old_tensor.split(channels // num_heads						,  dim=1 )

                     _UpperCamelCase   :				Union[str, Any]       =     query.reshape(lowercase_ )
                     _UpperCamelCase   :				Optional[int]       =     key.reshape(lowercase_ )
                     _UpperCamelCase   :				Optional[Any]       =     value.reshape(lowercase_ )

       for path in paths:
              _UpperCamelCase   :				List[str]       =     path['new']

              # These have already been assigned
              if attention_paths_to_split is not None and new_path in attention_paths_to_split:
                     continue

              if additional_replacements is not None:
                     for replacement in additional_replacements:
                            _UpperCamelCase   :				Optional[Any]       =     new_path.replace(replacement['old']						,  replacement['new'] )

        # proj_attn.weight has to be converted from conv 1D to linear
              _UpperCamelCase   :				Union[str, Any]       =     old_checkpoint[path['old']]
              _UpperCamelCase   :				Dict       =     ['proj_attn.weight']
              _UpperCamelCase   :				Union[str, Any]       =     ['proj_out.weight', 'proj_in.weight']
              if any(k in new_path for k in names ):
                     _UpperCamelCase   :				int       =     weight[:, :, 0]
              elif any(k in new_path for k in names_a ) and len(weight.shape ) > 2 and ".attentions." not in new_path:
                     _UpperCamelCase   :				List[Any]       =     weight[:, :, 0]
              else:
                     _UpperCamelCase   :				List[Any]       =     weight


def SCREAMING_SNAKE_CASE (   lowercase_:		int						,  lowercase_:		Union[str, Any]=0 ):
       _UpperCamelCase   :				Tuple       =     []
       for old_item in old_list:
              _UpperCamelCase   :				Any       =     old_item

              #         new_item = new_item.replace('norm.weight', 'group_norm.weight')
              #         new_item = new_item.replace('norm.bias', 'group_norm.bias')

              #         new_item = new_item.replace('proj_out.weight', 'proj_attn.weight')
              #         new_item = new_item.replace('proj_out.bias', 'proj_attn.bias')

              #         new_item = shave_segments(new_item, n_shave_prefix_segments=n_shave_prefix_segments)

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

       return mapping


def SCREAMING_SNAKE_CASE (   lowercase_:		Any						,  lowercase_:		List[Any]=1 ):
       if n_shave_prefix_segments >= 0:
              return ".".join(path.split('.' )[n_shave_prefix_segments:] )
       else:
              return ".".join(path.split('.' )[:n_shave_prefix_segments] )


def SCREAMING_SNAKE_CASE (   lowercase_:		List[str]						,  lowercase_:		int=0 ):
       _UpperCamelCase   :				Union[str, Any]       =     []
       for old_item in old_list:
              mapping.append({'old': old_item, 'new': old_item} )

       return mapping


def SCREAMING_SNAKE_CASE (   lowercase_:		List[Any]						,  lowercase_:		Union[str, Any]=0 ):
       _UpperCamelCase   :				List[str]       =     []
       for old_item in old_list:
              _UpperCamelCase   :				List[Any]       =     old_item.replace('in_layers.0'						,  'norm1' )
              _UpperCamelCase   :				Tuple       =     new_item.replace('in_layers.2'						,  'conv1' )

              _UpperCamelCase   :				Dict       =     new_item.replace('out_layers.0'						,  'norm2' )
              _UpperCamelCase   :				Optional[Any]       =     new_item.replace('out_layers.3'						,  'conv2' )

              _UpperCamelCase   :				Dict       =     new_item.replace('emb_layers.1'						,  'time_emb_proj' )
              _UpperCamelCase   :				Tuple       =     new_item.replace('skip_connection'						,  'conv_shortcut' )

              _UpperCamelCase   :				str       =     shave_segments(lowercase_						,  n_shave_prefix_segments=lowercase_ )

              if "temopral_conv" not in old_item:
                     mapping.append({'old': old_item, 'new': new_item} )

       return mapping


def SCREAMING_SNAKE_CASE (   lowercase_:		Any						,  lowercase_:		Union[str, Any]						,  lowercase_:		Optional[int]=None						,  lowercase_:		Any=False ):
       _UpperCamelCase   :				List[str]       =     {}
       _UpperCamelCase   :				Optional[int]       =     list(checkpoint.keys() )

       _UpperCamelCase   :				Dict       =     'model.diffusion_model.'

       # at least a 100 parameters have to start with `model_ema` in order for the checkpoint to be EMA
       if sum(k.startswith('model_ema' ) for k in keys ) > 100 and extract_ema:
              print(F"Checkpoint {path} has both EMA and non-EMA weights." )
              print(
                  'In this conversion only the EMA weights are extracted. If you want to instead extract the non-EMA'
                  ' weights (useful to continue fine-tuning), please make sure to remove the `--extract_ema` flag.' )
              for key in keys:
                     if key.startswith('model.diffusion_model' ):
                            _UpperCamelCase   :				str       =     'model_ema.' + ''.join(key.split('.' )[1:] )
                            _UpperCamelCase   :				Optional[int]       =     checkpoint.pop(lowercase_ )
       else:
              if sum(k.startswith('model_ema' ) for k in keys ) > 100:
                     print(
                         'In this conversion only the non-EMA weights are extracted. If you want to instead extract the EMA'
                         ' weights (usually better for inference), please make sure to add the `--extract_ema` flag.' )

              for key in keys:
                     _UpperCamelCase   :				Any       =     checkpoint.pop(lowercase_ )

       _UpperCamelCase   :				int       =     {}

       _UpperCamelCase   :				Optional[Any]       =     unet_state_dict['time_embed.0.weight']
       _UpperCamelCase   :				List[Any]       =     unet_state_dict['time_embed.0.bias']
       _UpperCamelCase   :				List[Any]       =     unet_state_dict['time_embed.2.weight']
       _UpperCamelCase   :				Optional[int]       =     unet_state_dict['time_embed.2.bias']

       if config["class_embed_type"] is None:
              # No parameters to port
              ...
       elif config["class_embed_type"] == "timestep" or config["class_embed_type"] == "projection":
              _UpperCamelCase   :				List[Any]       =     unet_state_dict['label_emb.0.0.weight']
              _UpperCamelCase   :				Optional[Any]       =     unet_state_dict['label_emb.0.0.bias']
              _UpperCamelCase   :				Optional[int]       =     unet_state_dict['label_emb.0.2.weight']
              _UpperCamelCase   :				List[str]       =     unet_state_dict['label_emb.0.2.bias']
       else:
              raise NotImplementedError(F"Not implemented `class_embed_type`: {config['class_embed_type']}" )

       _UpperCamelCase   :				List[str]       =     unet_state_dict['input_blocks.0.0.weight']
       _UpperCamelCase   :				Dict       =     unet_state_dict['input_blocks.0.0.bias']

       _UpperCamelCase   :				List[str]       =     [v for v in unet_state_dict if v.startswith('input_blocks.0.1' )]
       _UpperCamelCase   :				Tuple       =     renew_attention_paths(lowercase_ )
       _UpperCamelCase   :				Optional[Any]       =     {'old': 'input_blocks.0.1', 'new': 'transformer_in'}
       assign_to_checkpoint(lowercase_						,  lowercase_						,  lowercase_						,  additional_replacements=[meta_path]						,  config=lowercase_ )

       _UpperCamelCase   :				Tuple       =     unet_state_dict['out.0.weight']
       _UpperCamelCase   :				Optional[Any]       =     unet_state_dict['out.0.bias']
       _UpperCamelCase   :				str       =     unet_state_dict['out.2.weight']
       _UpperCamelCase   :				List[str]       =     unet_state_dict['out.2.bias']

       # Retrieves the keys for the input blocks only
       _UpperCamelCase   :				Union[str, Any]       =     len({'.'.join(layer.split('.' )[:2] ) for layer in unet_state_dict if 'input_blocks' in layer} )
       _UpperCamelCase   :				Any       =     {
           layer_id: [key for key in unet_state_dict if F"input_blocks.{layer_id}" in key]
           for layer_id in range(lowercase_ )
       }

       # Retrieves the keys for the middle blocks only
       _UpperCamelCase   :				Optional[int]       =     len({'.'.join(layer.split('.' )[:2] ) for layer in unet_state_dict if 'middle_block' in layer} )
       _UpperCamelCase   :				Any       =     {
           layer_id: [key for key in unet_state_dict if F"middle_block.{layer_id}" in key]
           for layer_id in range(lowercase_ )
       }

       # Retrieves the keys for the output blocks only
       _UpperCamelCase   :				int       =     len({'.'.join(layer.split('.' )[:2] ) for layer in unet_state_dict if 'output_blocks' in layer} )
       _UpperCamelCase   :				Tuple       =     {
           layer_id: [key for key in unet_state_dict if F"output_blocks.{layer_id}" in key]
           for layer_id in range(lowercase_ )
       }

       for i in range(1						,  lowercase_ ):
              _UpperCamelCase   :				Any       =     (i - 1) // (config['layers_per_block'] + 1)
              _UpperCamelCase   :				Optional[int]       =     (i - 1) % (config['layers_per_block'] + 1)

              _UpperCamelCase   :				Tuple       =     [
                  key for key in input_blocks[i] if F"input_blocks.{i}.0" in key and F"input_blocks.{i}.0.op" not in key
              ]
              _UpperCamelCase   :				Optional[int]       =     [key for key in input_blocks[i] if F"input_blocks.{i}.1" in key]
              _UpperCamelCase   :				str       =     [key for key in input_blocks[i] if F"input_blocks.{i}.2" in key]

              if F"input_blocks.{i}.op.weight" in unet_state_dict:
                     _UpperCamelCase   :				Dict       =     unet_state_dict.pop(
                         F"input_blocks.{i}.op.weight" )
                     _UpperCamelCase   :				Tuple       =     unet_state_dict.pop(
                         F"input_blocks.{i}.op.bias" )

              _UpperCamelCase   :				Optional[Any]       =     renew_resnet_paths(lowercase_ )
              _UpperCamelCase   :				str       =     {'old': F"input_blocks.{i}.0", 'new': F"down_blocks.{block_id}.resnets.{layer_in_block_id}"}
              assign_to_checkpoint(
                  lowercase_						,  lowercase_						,  lowercase_						,  additional_replacements=[meta_path]						,  config=lowercase_ )

              _UpperCamelCase   :				List[Any]       =     [key for key in resnets if 'temopral_conv' in key]
              _UpperCamelCase   :				Dict       =     renew_temp_conv_paths(lowercase_ )
              _UpperCamelCase   :				Optional[Any]       =     {
                  'old': F"input_blocks.{i}.0.temopral_conv",
                  'new': F"down_blocks.{block_id}.temp_convs.{layer_in_block_id}",
              }
              assign_to_checkpoint(
                  lowercase_						,  lowercase_						,  lowercase_						,  additional_replacements=[meta_path]						,  config=lowercase_ )

              if len(lowercase_ ):
                     _UpperCamelCase   :				Tuple       =     renew_attention_paths(lowercase_ )
                     _UpperCamelCase   :				Optional[int]       =     {'old': F"input_blocks.{i}.1", 'new': F"down_blocks.{block_id}.attentions.{layer_in_block_id}"}
                     assign_to_checkpoint(
                         lowercase_						,  lowercase_						,  lowercase_						,  additional_replacements=[meta_path]						,  config=lowercase_ )

              if len(lowercase_ ):
                     _UpperCamelCase   :				List[Any]       =     renew_attention_paths(lowercase_ )
                     _UpperCamelCase   :				str       =     {
                         'old': F"input_blocks.{i}.2",
                         'new': F"down_blocks.{block_id}.temp_attentions.{layer_in_block_id}",
                     }
                     assign_to_checkpoint(
                         lowercase_						,  lowercase_						,  lowercase_						,  additional_replacements=[meta_path]						,  config=lowercase_ )

       _UpperCamelCase   :				Optional[int]       =     middle_blocks[0]
       _UpperCamelCase   :				List[Any]       =     [key for key in resnet_a if 'temopral_conv' in key]
       _UpperCamelCase   :				List[str]       =     middle_blocks[1]
       _UpperCamelCase   :				Optional[Any]       =     middle_blocks[2]
       _UpperCamelCase   :				Tuple       =     middle_blocks[3]
       _UpperCamelCase   :				Optional[int]       =     [key for key in resnet_a if 'temopral_conv' in key]

       _UpperCamelCase   :				Dict       =     renew_resnet_paths(lowercase_ )
       _UpperCamelCase   :				List[str]       =     {'old': 'middle_block.0', 'new': 'mid_block.resnets.0'}
       assign_to_checkpoint(
           lowercase_						,  lowercase_						,  lowercase_						,  config=lowercase_						,  additional_replacements=[meta_path] )

       _UpperCamelCase   :				List[str]       =     renew_temp_conv_paths(lowercase_ )
       _UpperCamelCase   :				Union[str, Any]       =     {'old': 'middle_block.0.temopral_conv', 'new': 'mid_block.temp_convs.0'}
       assign_to_checkpoint(
           lowercase_						,  lowercase_						,  lowercase_						,  config=lowercase_						,  additional_replacements=[meta_path] )

       _UpperCamelCase   :				Optional[Any]       =     renew_resnet_paths(lowercase_ )
       _UpperCamelCase   :				List[Any]       =     {'old': 'middle_block.3', 'new': 'mid_block.resnets.1'}
       assign_to_checkpoint(
           lowercase_						,  lowercase_						,  lowercase_						,  config=lowercase_						,  additional_replacements=[meta_path] )

       _UpperCamelCase   :				Optional[int]       =     renew_temp_conv_paths(lowercase_ )
       _UpperCamelCase   :				Optional[int]       =     {'old': 'middle_block.3.temopral_conv', 'new': 'mid_block.temp_convs.1'}
       assign_to_checkpoint(
           lowercase_						,  lowercase_						,  lowercase_						,  config=lowercase_						,  additional_replacements=[meta_path] )

       _UpperCamelCase   :				Tuple       =     renew_attention_paths(lowercase_ )
       _UpperCamelCase   :				List[Any]       =     {'old': 'middle_block.1', 'new': 'mid_block.attentions.0'}
       assign_to_checkpoint(
           lowercase_						,  lowercase_						,  lowercase_						,  additional_replacements=[meta_path]						,  config=lowercase_ )

       _UpperCamelCase   :				str       =     renew_attention_paths(lowercase_ )
       _UpperCamelCase   :				Dict       =     {'old': 'middle_block.2', 'new': 'mid_block.temp_attentions.0'}
       assign_to_checkpoint(
           lowercase_						,  lowercase_						,  lowercase_						,  additional_replacements=[meta_path]						,  config=lowercase_ )

       for i in range(lowercase_ ):
              _UpperCamelCase   :				str       =     i // (config['layers_per_block'] + 1)
              _UpperCamelCase   :				int       =     i % (config['layers_per_block'] + 1)
              _UpperCamelCase   :				List[Any]       =     [shave_segments(lowercase_						,  2 ) for name in output_blocks[i]]
              _UpperCamelCase   :				str       =     {}

              for layer in output_block_layers:
                     _UpperCamelCase  ,					_UpperCamelCase   :				Any       =     layer.split('.' )[0], shave_segments(lowercase_						,  1 )
                     if layer_id in output_block_list:
                            output_block_list[layer_id].append(lowercase_ )
                     else:
                            _UpperCamelCase   :				int       =     [layer_name]

              if len(lowercase_ ) > 1:
                     _UpperCamelCase   :				Optional[int]       =     [key for key in output_blocks[i] if F"output_blocks.{i}.0" in key]
                     _UpperCamelCase   :				int       =     [key for key in output_blocks[i] if F"output_blocks.{i}.1" in key]
                     _UpperCamelCase   :				int       =     [key for key in output_blocks[i] if F"output_blocks.{i}.2" in key]

                     _UpperCamelCase   :				Optional[Any]       =     renew_resnet_paths(lowercase_ )
                     _UpperCamelCase   :				Optional[Any]       =     renew_resnet_paths(lowercase_ )

                     _UpperCamelCase   :				int       =     {'old': F"output_blocks.{i}.0", 'new': F"up_blocks.{block_id}.resnets.{layer_in_block_id}"}
                     assign_to_checkpoint(
                         lowercase_						,  lowercase_						,  lowercase_						,  additional_replacements=[meta_path]						,  config=lowercase_ )

                     _UpperCamelCase   :				str       =     [key for key in resnets if 'temopral_conv' in key]
                     _UpperCamelCase   :				List[Any]       =     renew_temp_conv_paths(lowercase_ )
                     _UpperCamelCase   :				Optional[int]       =     {
                         'old': F"output_blocks.{i}.0.temopral_conv",
                         'new': F"up_blocks.{block_id}.temp_convs.{layer_in_block_id}",
                     }
                     assign_to_checkpoint(
                         lowercase_						,  lowercase_						,  lowercase_						,  additional_replacements=[meta_path]						,  config=lowercase_ )

                     _UpperCamelCase   :				List[Any]       =     {k: sorted(lowercase_ ) for k, v in output_block_list.items()}
                     if ["conv.bias", "conv.weight"] in output_block_list.values():
                            _UpperCamelCase   :				List[str]       =     list(output_block_list.values() ).index(['conv.bias', 'conv.weight'] )
                            _UpperCamelCase   :				List[str]       =     unet_state_dict[
                                F"output_blocks.{i}.{index}.conv.weight"
                            ]
                            _UpperCamelCase   :				int       =     unet_state_dict[
                                F"output_blocks.{i}.{index}.conv.bias"
                            ]

                            # Clear attentions as they have been attributed above.
                            if len(lowercase_ ) == 2:
                                   _UpperCamelCase   :				Optional[int]       =     []

                     if len(lowercase_ ):
                            _UpperCamelCase   :				int       =     renew_attention_paths(lowercase_ )
                            _UpperCamelCase   :				Union[str, Any]       =     {
                                'old': F"output_blocks.{i}.1",
                                'new': F"up_blocks.{block_id}.attentions.{layer_in_block_id}",
                            }
                            assign_to_checkpoint(
                                lowercase_						,  lowercase_						,  lowercase_						,  additional_replacements=[meta_path]						,  config=lowercase_ )

                     if len(lowercase_ ):
                            _UpperCamelCase   :				Union[str, Any]       =     renew_attention_paths(lowercase_ )
                            _UpperCamelCase   :				int       =     {
                                'old': F"output_blocks.{i}.2",
                                'new': F"up_blocks.{block_id}.temp_attentions.{layer_in_block_id}",
                            }
                            assign_to_checkpoint(
                                lowercase_						,  lowercase_						,  lowercase_						,  additional_replacements=[meta_path]						,  config=lowercase_ )
              else:
                     _UpperCamelCase   :				Union[str, Any]       =     renew_resnet_paths(lowercase_						,  n_shave_prefix_segments=1 )
                     for path in resnet_0_paths:
                            _UpperCamelCase   :				Optional[Any]       =     '.'.join(['output_blocks', str(lowercase_ ), path['old']] )
                            _UpperCamelCase   :				List[Any]       =     '.'.join(['up_blocks', str(lowercase_ ), 'resnets', str(lowercase_ ), path['new']] )
                            _UpperCamelCase   :				Tuple       =     unet_state_dict[old_path]

                     _UpperCamelCase   :				Dict       =     [l for l in output_block_layers if 'temopral_conv' in l]
                     for path in temopral_conv_paths:
                            _UpperCamelCase   :				List[str]       =     path.split('temopral_conv.' )[-1]
                            _UpperCamelCase   :				Tuple       =     '.'.join(['output_blocks', str(lowercase_ ), str(lowercase_ ), 'temopral_conv', pruned_path] )
                            _UpperCamelCase   :				Optional[Any]       =     '.'.join(['up_blocks', str(lowercase_ ), 'temp_convs', str(lowercase_ ), pruned_path] )
                            _UpperCamelCase   :				Any       =     unet_state_dict[old_path]

       return new_checkpoint


if __name__ == "__main__":
 UpperCAmelCase							= argparse.ArgumentParser()

 parser.add_argument(
     '''--checkpoint_path''', default=None, type=str, required=True, help='''Path to the checkpoint to convert.'''
 )
 parser.add_argument('''--dump_path''', default=None, type=str, required=True, help='''Path to the output model.''')
 UpperCAmelCase							= parser.parse_args()

 UpperCAmelCase							= torch.load(args.checkpoint_path, map_location='''cpu''')
 UpperCAmelCase							= UNetaDConditionModel()

 UpperCAmelCase							= convert_ldm_unet_checkpoint(unet_checkpoint, unet.config)

 UpperCAmelCase							= set(unet.state_dict().keys()) - set(converted_ckpt.keys())
 UpperCAmelCase							= set(converted_ckpt.keys()) - set(unet.state_dict().keys())

 assert len(diff_a) == len(diff_a) == 0, "Converted weights don't match"

 # load state_dict
 unet.load_state_dict(converted_ckpt)

 unet.save_pretrained(args.dump_path)

 # -- finish converting the unet --




import os
from pathlib import Path
from typing import Dict, List

import fire
import torch

from transformers import AutoModelForSeqaSeqLM, AutoTokenizer
from transformers.utils.logging import get_logger


UpperCAmelCase							= get_logger(__name__)


def SCREAMING_SNAKE_CASE (   lowercase_:		str						,  lowercase_:		str ):
       if text.startswith(lowercase_ ):
              return text[len(lowercase_ ) :]
       return text  # or whatever


def SCREAMING_SNAKE_CASE (   lowercase_:		Union[str, Any] ):
       return {remove_prefix(lowercase_						,  'model.' ): v for k, v in sd.items()}


def SCREAMING_SNAKE_CASE (   lowercase_:		List[Dict[str, torch.Tensor]] ):
       _UpperCamelCase   :				Union[str, Any]       =     {}
       for k in state_dicts[0].keys():
              _UpperCamelCase   :				Tuple       =     [sd[k] for sd in state_dicts]
              _UpperCamelCase   :				str       =     sum(lowercase_ ) / len(lowercase_ )
              assert isinstance(lowercase_						,  torch.Tensor )
              _UpperCamelCase   :				Dict       =     new_t
       return new_sd


def SCREAMING_SNAKE_CASE (   lowercase_:		str						,  lowercase_:		str						,  lowercase_:		str ):
       _UpperCamelCase   :				Optional[Any]       =     AutoModelForSeqaSeqLM.from_pretrained(lowercase_ )
       if os.path.isfile(lowercase_ ):
              _UpperCamelCase   :				Any       =     [pl_ckpt_path]
       else:
              assert os.path.isdir(lowercase_ )
              _UpperCamelCase   :				str       =     list(Path(lowercase_ ).glob('*.ckpt' ) )
              assert ckpt_files, F"could not find any ckpt files inside the {pl_ckpt_path} directory"

       if len(lowercase_ ) > 1:
              logger.info(F"averaging the weights of {ckpt_files}" )

       _UpperCamelCase   :				Tuple       =     [sanitize(torch.load(lowercase_						,  map_location='cpu' )['state_dict'] ) for x in ckpt_files]
       _UpperCamelCase   :				Optional[Any]       =     average_state_dicts(lowercase_ )

       _UpperCamelCase  ,					_UpperCamelCase   :				Tuple       =     hf_model.load_state_dict(lowercase_						,  strict=lowercase_ )
       assert not missing, F"missing keys: {missing}"
       hf_model.save_pretrained(lowercase_ )
       try:
              _UpperCamelCase   :				str       =     AutoTokenizer.from_pretrained(lowercase_ )
              tok.save_pretrained(lowercase_ )
       except Exception:
              pass
              # dont copy tokenizer if cant


if __name__ == "__main__":
 fire.Fire(convert_pl_to_hf)



