






"""simple docstring"""


import argparse
import logging
import os
from pathlib import Path
from typing import Any, Dict

import pytorch_lightning as pl
from pytorch_lightning.utilities import rank_zero_info

from transformers import (
    AdamW,
    AutoConfig,
    AutoModel,
    AutoModelForPreTraining,
    AutoModelForQuestionAnswering,
    AutoModelForSeqaSeqLM,
    AutoModelForSequenceClassification,
    AutoModelForTokenClassification,
    AutoModelWithLMHead,
    AutoTokenizer,
    PretrainedConfig,
    PreTrainedTokenizer,
)
from transformers.optimization import (
    Adafactor,
    get_cosine_schedule_with_warmup,
    get_cosine_with_hard_restarts_schedule_with_warmup,
    get_linear_schedule_with_warmup,
    get_polynomial_decay_schedule_with_warmup,
)
from transformers.utils.versions import require_version


a__   :       List[Any]   						=			logging.getLogger(__name__)

require_version("""pytorch_lightning>=1.0.4""")

a__   :       List[Any]   						=			{
    """base""": AutoModel,
    """sequence-classification""": AutoModelForSequenceClassification,
    """question-answering""": AutoModelForQuestionAnswering,
    """pretraining""": AutoModelForPreTraining,
    """token-classification""": AutoModelForTokenClassification,
    """language-modeling""": AutoModelWithLMHead,
    """summarization""": AutoModelForSeqaSeqLM,
    """translation""": AutoModelForSeqaSeqLM,
}


# update this and the import above to support new schedulers from transformers.optimization
a__   :       Tuple   						=			{
    """linear""": get_linear_schedule_with_warmup,
    """cosine""": get_cosine_schedule_with_warmup,
    """cosine_w_restarts""": get_cosine_with_hard_restarts_schedule_with_warmup,
    """polynomial""": get_polynomial_decay_schedule_with_warmup,
    # '': get_constant_schedule,             # not supported for now
    # '': get_constant_schedule_with_warmup, # not supported for now
}
a__   :       Optional[Any]   						=			sorted(arg_to_scheduler.keys())
a__   :       Dict   						=			"""{""" + """, """.join(arg_to_scheduler_choices) + """}"""



class        lowercase_			(       pl.LightningModule							):

       def __init__(     self				,   __SCREAMING_SNAKE_CASE				,   __SCREAMING_SNAKE_CASE=None				,   __SCREAMING_SNAKE_CASE="base"				,   __SCREAMING_SNAKE_CASE=None				,   __SCREAMING_SNAKE_CASE=None				,   __SCREAMING_SNAKE_CASE=None				,   **__SCREAMING_SNAKE_CASE				,   ):






        '''simple docstring'''

        super().__init__()
        # TODO: move to self.save_hyperparameters()
        # self.save_hyperparameters()
        # can also expand arguments into trainer signature for easier reading

        self.save_hyperparameters(lowercase__       )
        snake_case__      							=			0
        snake_case__      							=			Path(self.hparams.output_dir       )
        snake_case__      							=			self.hparams.cache_dir if self.hparams.cache_dir else None
        if config is None:
         snake_case__      							=			AutoConfig.from_pretrained(
             self.hparams.config_name if self.hparams.config_name else self.hparams.model_name_or_path				,   **({"num_labels": num_labels} if num_labels is not None else {})				,   cache_dir=lowercase__				,   **lowercase__				,   )
        else:
         snake_case__      							=			config

        snake_case__      							=			("encoder_layerdrop", "decoder_layerdrop", "dropout", "attention_dropout")
        for p in extra_model_params:
         if getattr(self.hparams				,   lowercase__				,   lowercase__       ):
          assert hasattr(self.config				,   lowercase__       ), F"model config doesn't have a `{p}` attribute"
          setattr(self.config				,   lowercase__				,   getattr(self.hparams				,   lowercase__       )       )

        if tokenizer is None:
         snake_case__      							=			AutoTokenizer.from_pretrained(
             self.hparams.tokenizer_name if self.hparams.tokenizer_name else self.hparams.model_name_or_path				,   cache_dir=lowercase__				,   )
        else:
         snake_case__      							=			tokenizer
        snake_case__      							=			MODEL_MODES[mode]
        if model is None:
         snake_case__      							=			self.model_type.from_pretrained(
             self.hparams.model_name_or_path				,   from_tf=bool(".ckpt" in self.hparams.model_name_or_path       )				,   config=self.config				,   cache_dir=lowercase__				,   )
        else:
         snake_case__      							=			model

       def 						_SCREAMING_SNAKE_CASE(     self				,   *__SCREAMING_SNAKE_CASE				,   **__SCREAMING_SNAKE_CASE       ):






        '''simple docstring'''

        snake_case__      							=			self.model_type.from_pretrained(*lowercase__				,   **lowercase__       )

       def 						_SCREAMING_SNAKE_CASE(     self       ):






        '''simple docstring'''

        snake_case__      							=			arg_to_scheduler[self.hparams.lr_scheduler]
        snake_case__      							=			get_schedule_func(
            self.opt				,   num_warmup_steps=self.hparams.warmup_steps				,   num_training_steps=self.total_steps()       )
        snake_case__      							=			{"scheduler": scheduler, "interval": "step", "frequency": 1}
        return scheduler

       def 						_SCREAMING_SNAKE_CASE(     self       ):






        '''simple docstring'''

        snake_case__      							=			self.model
        snake_case__      							=			["bias", "LayerNorm.weight"]
        snake_case__      							=			[
            {
                "params": [p for n, p in model.named_parameters() if not any(nd in n for nd in no_decay       )],
                "weight_decay": self.hparams.weight_decay,
            },
            {
                "params": [p for n, p in model.named_parameters() if any(nd in n for nd in no_decay       )],
                "weight_decay": 0.0,
            },
        ]
        if self.hparams.adafactor:
         snake_case__      							=			Adafactor(
             lowercase__				,   lr=self.hparams.learning_rate				,   scale_parameter=lowercase__				,   relative_step=lowercase__       )

        else:
         snake_case__      							=			AdamW(
             lowercase__				,   lr=self.hparams.learning_rate				,   eps=self.hparams.adam_epsilon       )
        snake_case__      							=			optimizer

        snake_case__      							=			self.get_lr_scheduler()

        return [optimizer], [scheduler]

       def 						_SCREAMING_SNAKE_CASE(     self				,   __SCREAMING_SNAKE_CASE				,   __SCREAMING_SNAKE_CASE       ):






        '''simple docstring'''

        return self.validation_step(lowercase__				,   lowercase__       )

       def 						_SCREAMING_SNAKE_CASE(     self				,   __SCREAMING_SNAKE_CASE       ):






        '''simple docstring'''

        return self.validation_end(lowercase__       )

       def 						_SCREAMING_SNAKE_CASE(     self       ):






        '''simple docstring'''

        snake_case__      							=			max(1				,   self.hparams.gpus       )  # TODO: consider num_tpu_cores
        snake_case__      							=			self.hparams.train_batch_size * self.hparams.accumulate_grad_batches * num_devices
        return (self.dataset_size / effective_batch_size) * self.hparams.max_epochs

       def 						_SCREAMING_SNAKE_CASE(     self				,   __SCREAMING_SNAKE_CASE       ):






        '''simple docstring'''

        if mode == "test":
         snake_case__      							=			len(self.test_dataloader().dataset       )
        else:
         snake_case__      							=			self.get_dataloader("train"				,   self.hparams.train_batch_size				,   shuffle=lowercase__       )
         snake_case__      							=			len(self.train_dataloader().dataset       )

       def 						_SCREAMING_SNAKE_CASE(     self				,   __SCREAMING_SNAKE_CASE				,   __SCREAMING_SNAKE_CASE				,   __SCREAMING_SNAKE_CASE = False       ):






        '''simple docstring'''

        raise NotImplementedError("You must implement this for your task"       )

       def 						_SCREAMING_SNAKE_CASE(     self       ):






        '''simple docstring'''

        return self.train_loader

       def 						_SCREAMING_SNAKE_CASE(     self       ):






        '''simple docstring'''

        return self.get_dataloader("dev"				,   self.hparams.eval_batch_size				,   shuffle=lowercase__       )

       def 						_SCREAMING_SNAKE_CASE(     self       ):






        '''simple docstring'''

        return self.get_dataloader("test"				,   self.hparams.eval_batch_size				,   shuffle=lowercase__       )

       def 						_SCREAMING_SNAKE_CASE(     self				,   __SCREAMING_SNAKE_CASE       ):






        '''simple docstring'''

        return os.path.join(
            self.hparams.data_dir				,   "cached_{}_{}_{}".format(
                lowercase__				,   list(filter(lowercase__				,   self.hparams.model_name_or_path.split("/"       )       )       ).pop()				,   str(self.hparams.max_seq_length       )				,   )				,   )

       @pl.utilities.rank_zero_only
       def 						_SCREAMING_SNAKE_CASE(     self				,   __SCREAMING_SNAKE_CASE       ):






        '''simple docstring'''

        snake_case__      							=			self.output_dir.joinpath("best_tfmr"       )
        snake_case__      							=			self.step_count
        self.model.save_pretrained(lowercase__       )
        self.tokenizer.save_pretrained(lowercase__       )

       @staticmethod
       def 						_SCREAMING_SNAKE_CASE(     __SCREAMING_SNAKE_CASE				,   __SCREAMING_SNAKE_CASE       ):






        '''simple docstring'''

        parser.add_argument(
            "--model_name_or_path"				,   default=lowercase__				,   type=lowercase__				,   required=lowercase__				,   help="Path to pretrained model or model identifier from huggingface.co/models"				,   )
        parser.add_argument(
            "--config_name"				,   default=""				,   type=lowercase__				,   help="Pretrained config name or path if not the same as model_name"       )
        parser.add_argument(
            "--tokenizer_name"				,   default=lowercase__				,   type=lowercase__				,   help="Pretrained tokenizer name or path if not the same as model_name"				,   )
        parser.add_argument(
            "--cache_dir"				,   default=""				,   type=lowercase__				,   help="Where do you want to store the pre-trained models downloaded from huggingface.co"				,   )
        parser.add_argument(
            "--encoder_layerdrop"				,   type=lowercase__				,   help="Encoder layer dropout probability (Optional). Goes into model.config"				,   )
        parser.add_argument(
            "--decoder_layerdrop"				,   type=lowercase__				,   help="Decoder layer dropout probability (Optional). Goes into model.config"				,   )
        parser.add_argument(
            "--dropout"				,   type=lowercase__				,   help="Dropout probability (Optional). Goes into model.config"				,   )
        parser.add_argument(
            "--attention_dropout"				,   type=lowercase__				,   help="Attention dropout probability (Optional). Goes into model.config"				,   )
        parser.add_argument("--learning_rate"				,   default=5e-5				,   type=lowercase__				,   help="The initial learning rate for Adam."       )
        parser.add_argument(
            "--lr_scheduler"				,   default="linear"				,   choices=lowercase__				,   metavar=lowercase__				,   type=lowercase__				,   help="Learning rate scheduler"				,   )
        parser.add_argument("--weight_decay"				,   default=0.0				,   type=lowercase__				,   help="Weight decay if we apply some."       )
        parser.add_argument("--adam_epsilon"				,   default=1e-8				,   type=lowercase__				,   help="Epsilon for Adam optimizer."       )
        parser.add_argument("--warmup_steps"				,   default=0				,   type=lowercase__				,   help="Linear warmup over warmup_steps."       )
        parser.add_argument("--num_workers"				,   default=4				,   type=lowercase__				,   help="kwarg passed to DataLoader"       )
        parser.add_argument("--num_train_epochs"				,   dest="max_epochs"				,   default=3				,   type=lowercase__       )
        parser.add_argument("--train_batch_size"				,   default=32				,   type=lowercase__       )
        parser.add_argument("--eval_batch_size"				,   default=32				,   type=lowercase__       )
        parser.add_argument("--adafactor"				,   action="store_true"       )



class        lowercase_			(       pl.Callback							):

       def 						_SCREAMING_SNAKE_CASE(     self				,   __SCREAMING_SNAKE_CASE				,   __SCREAMING_SNAKE_CASE       ):






        '''simple docstring'''

        snake_case__      							=			trainer.lr_schedulers[0]["scheduler"]
        snake_case__      							=			{F"lr_group_{i}": lr for i, lr in enumerate(lr_scheduler.get_lr()       )}
        pl_module.logger.log_metrics(lowercase__       )

       def 						_SCREAMING_SNAKE_CASE(     self				,   __SCREAMING_SNAKE_CASE				,   __SCREAMING_SNAKE_CASE       ):






        '''simple docstring'''

        rank_zero_info("***** Validation results *****"       )
        snake_case__      							=			trainer.callback_metrics
        # Log results
        for key in sorted(lowercase__       ):
         if key not in ["log", "progress_bar"]:
          rank_zero_info("{} = {}\n".format(lowercase__				,   str(metrics[key]       )       )       )

       def 						_SCREAMING_SNAKE_CASE(     self				,   __SCREAMING_SNAKE_CASE				,   __SCREAMING_SNAKE_CASE       ):






        '''simple docstring'''

        rank_zero_info("***** Test results *****"       )
        snake_case__      							=			trainer.callback_metrics
        # Log and save results to file
        snake_case__      							=			os.path.join(pl_module.hparams.output_dir				,   "test_results.txt"       )
        with open(lowercase__				,   "w"       ) as writer:
         for key in sorted(lowercase__       ):
          if key not in ["log", "progress_bar"]:
           rank_zero_info("{} = {}\n".format(lowercase__				,   str(metrics[key]       )       )       )
           writer.write("{} = {}\n".format(lowercase__				,   str(metrics[key]       )       )       )





def 							a__     (					_UpperCAmelCase		,       _UpperCAmelCase		)      ->					None:

 '''simple docstring'''






 parser.add_argument(
     "--output_dir"		,       default=SCREAMING_SNAKE_CASE_		,       type=SCREAMING_SNAKE_CASE_		,       required=SCREAMING_SNAKE_CASE_		,       help="The output directory where the model predictions and checkpoints will be written."		,       )
 parser.add_argument(
     "--fp16"		,       action="store_true"		,       help="Whether to use 16-bit (mixed) precision (through NVIDIA apex) instead of 32-bit"		,       )

 parser.add_argument(
     "--fp16_opt_level"		,       type=SCREAMING_SNAKE_CASE_		,       default="O2"		,       help=(
         "For fp16: Apex AMP optimization level selected in ['O0', 'O1', 'O2', and 'O3']."
         "See details at https://nvidia.github.io/apex/amp.html"
     )		,       )
 parser.add_argument("--n_tpu_cores"		,       dest="tpu_cores"		,       type=SCREAMING_SNAKE_CASE_		)
 parser.add_argument("--max_grad_norm"		,       dest="gradient_clip_val"		,       default=1.0		,       type=SCREAMING_SNAKE_CASE_		,       help="Max gradient norm"		)
 parser.add_argument("--do_train"		,       action="store_true"		,       help="Whether to run training."		)
 parser.add_argument("--do_predict"		,       action="store_true"		,       help="Whether to run predictions on the test set."		)
 parser.add_argument(
     "--gradient_accumulation_steps"		,       dest="accumulate_grad_batches"		,       type=SCREAMING_SNAKE_CASE_		,       default=1		,       help="Number of updates steps to accumulate before performing a backward/update pass."		,       )
 parser.add_argument("--seed"		,       type=SCREAMING_SNAKE_CASE_		,       default=4_2		,       help="random seed for initialization"		)
 parser.add_argument(
     "--data_dir"		,       default=SCREAMING_SNAKE_CASE_		,       type=SCREAMING_SNAKE_CASE_		,       required=SCREAMING_SNAKE_CASE_		,       help="The input data dir. Should contain the training files for the CoNLL-2003 NER task."		,       )





def 							a__     (					_UpperCAmelCase		,       _UpperCAmelCase		,       _UpperCAmelCase=None		,       _UpperCAmelCase=True		,       _UpperCAmelCase=[]		,       _UpperCAmelCase=None		,       _UpperCAmelCase=None		,       **_UpperCAmelCase		,       )      ->					Optional[int]:

 '''simple docstring'''






 pl.seed_everything(args.seed		)

 # init model
 snake_case__      							=			Path(model.hparams.output_dir		)
 odir.mkdir(exist_ok=SCREAMING_SNAKE_CASE_		)

 # add custom checkpoints
 if checkpoint_callback is None:
  snake_case__      							=			pl.callbacks.ModelCheckpoint(
      filepath=args.output_dir		,       prefix="checkpoint"		,       monitor="val_loss"		,       mode="min"		,       save_top_k=1		)
 if early_stopping_callback:
  extra_callbacks.append(SCREAMING_SNAKE_CASE_		)
 if logging_callback is None:
  snake_case__      							=			LoggingCallback()

 snake_case__      							=			{}

 # TODO: remove with PyTorch 1.6 since pl uses native amp
 if args.fpaa:
  snake_case__      							=			1_6
  snake_case__      							=			args.fpaa_opt_level

 if args.gpus > 1:
  snake_case__      							=			"ddp"

 snake_case__      							=			args.accumulate_grad_batches
 snake_case__      							=			extra_train_kwargs.get("accelerator"		,       SCREAMING_SNAKE_CASE_		)
 snake_case__      							=			extra_train_kwargs.get("profiler"		,       SCREAMING_SNAKE_CASE_		)

 snake_case__      							=			pl.Trainer.from_argparse_args(
     SCREAMING_SNAKE_CASE_		,       weights_summary=SCREAMING_SNAKE_CASE_		,       callbacks=[logging_callback] + extra_callbacks		,       logger=SCREAMING_SNAKE_CASE_		,       checkpoint_callback=SCREAMING_SNAKE_CASE_		,       **SCREAMING_SNAKE_CASE_		,       )

 if args.do_train:
  trainer.fit(SCREAMING_SNAKE_CASE_		)

 return trainer








"""simple docstring"""


from __future__ import annotations

from math import pi, pow





def 							a__     (					_UpperCAmelCase		)      ->					float:

 '''simple docstring'''






 if side_length < 0:
  raise ValueError("vol_cube() only accepts non-negative values"		)
 return pow(_UpperCAmelCase		,       3		)





def 							a__     (					_UpperCAmelCase		,       _UpperCAmelCase		)      ->					float:

 '''simple docstring'''






 if height < 0 or radius < 0:
  raise ValueError("vol_spherical_cap() only accepts non-negative values"		)
 # Volume is 1/3 pi * height squared * (3 * radius - height)
 return 1 / 3 * pi * pow(_UpperCAmelCase		,       2		) * (3 * radius - height)





def 							a__     (					_UpperCAmelCase		,       _UpperCAmelCase		,       _UpperCAmelCase		)      ->					float:

 '''simple docstring'''






 if radius_a < 0 or radius_a < 0 or centers_distance < 0:
  raise ValueError("vol_spheres_intersect() only accepts non-negative values"		)
 if centers_distance == 0:
  return vol_sphere(min(_UpperCAmelCase		,       _UpperCAmelCase		)		)

 snake_case__      							=			(
     (radius_a - radius_a + centers_distance)
     * (radius_a + radius_a - centers_distance)
     / (2 * centers_distance)
 )
 snake_case__      							=			(
     (radius_a - radius_a + centers_distance)
     * (radius_a + radius_a - centers_distance)
     / (2 * centers_distance)
 )

 return vol_spherical_cap(_UpperCAmelCase		,       _UpperCAmelCase		) + vol_spherical_cap(_UpperCAmelCase		,       _UpperCAmelCase		)





def 							a__     (					_UpperCAmelCase		,       _UpperCAmelCase		,       _UpperCAmelCase		)      ->					float:

 '''simple docstring'''






 if radius_a <= 0 or radius_a <= 0 or centers_distance < 0:
  raise ValueError(
      "vol_spheres_union() only accepts non-negative values, non-zero radius"		)

 if centers_distance == 0:
  return vol_sphere(max(_UpperCAmelCase		,       _UpperCAmelCase		)		)

 return (
     vol_sphere(_UpperCAmelCase		)
     + vol_sphere(_UpperCAmelCase		)
     - vol_spheres_intersect(_UpperCAmelCase		,       _UpperCAmelCase		,       _UpperCAmelCase		)
 )





def 							a__     (					_UpperCAmelCase		,       _UpperCAmelCase		,       _UpperCAmelCase		)      ->					float:

 '''simple docstring'''






 if width < 0 or height < 0 or length < 0:
  raise ValueError("vol_cuboid() only accepts non-negative values"		)
 return float(width * height * length		)





def 							a__     (					_UpperCAmelCase		,       _UpperCAmelCase		)      ->					float:

 '''simple docstring'''






 if height < 0 or area_of_base < 0:
  raise ValueError("vol_cone() only accepts non-negative values"		)
 return area_of_base * height / 3.0





def 							a__     (					_UpperCAmelCase		,       _UpperCAmelCase		)      ->					float:

 '''simple docstring'''






 if height < 0 or radius < 0:
  raise ValueError("vol_right_circ_cone() only accepts non-negative values"		)
 return pi * pow(_UpperCAmelCase		,       2		) * height / 3.0





def 							a__     (					_UpperCAmelCase		,       _UpperCAmelCase		)      ->					float:

 '''simple docstring'''






 if height < 0 or area_of_base < 0:
  raise ValueError("vol_prism() only accepts non-negative values"		)
 return float(area_of_base * height		)





def 							a__     (					_UpperCAmelCase		,       _UpperCAmelCase		)      ->					float:

 '''simple docstring'''






 if height < 0 or area_of_base < 0:
  raise ValueError("vol_pyramid() only accepts non-negative values"		)
 return area_of_base * height / 3.0





def 							a__     (					_UpperCAmelCase		)      ->					float:

 '''simple docstring'''






 if radius < 0:
  raise ValueError("vol_sphere() only accepts non-negative values"		)
 # Volume is 4/3 * pi * radius cubed
 return 4 / 3 * pi * pow(_UpperCAmelCase		,       3		)





def 							a__     (					_UpperCAmelCase		)      ->					float:

 '''simple docstring'''






 if radius < 0:
  raise ValueError("vol_hemisphere() only accepts non-negative values"		)
 # Volume is radius cubed * pi * 2/3
 return pow(_UpperCAmelCase		,       3		) * pi * 2 / 3





def 							a__     (					_UpperCAmelCase		,       _UpperCAmelCase		)      ->					float:

 '''simple docstring'''






 if height < 0 or radius < 0:
  raise ValueError("vol_circular_cylinder() only accepts non-negative values"		)
 # Volume is radius squared * height * pi
 return pow(_UpperCAmelCase		,       2		) * height * pi





def 							a__     (					_UpperCAmelCase		,       _UpperCAmelCase		,       _UpperCAmelCase		)      ->					float:

 '''simple docstring'''






 if inner_radius < 0 or outer_radius < 0 or height < 0:
  raise ValueError(
      "vol_hollow_circular_cylinder() only accepts non-negative values"		)
 if outer_radius <= inner_radius:
  raise ValueError("outer_radius must be greater than inner_radius"		)
 return pi * (pow(_UpperCAmelCase		,       2		) - pow(_UpperCAmelCase		,       2		)) * height





def 							a__     (					_UpperCAmelCase		,       _UpperCAmelCase		,       _UpperCAmelCase		)      ->					float:

 '''simple docstring'''






 if radius_a < 0 or radius_a < 0 or height < 0:
  raise ValueError("vol_conical_frustum() only accepts non-negative values"		)
 return (
     1
     / 3
     * pi
     * height
     * (pow(_UpperCAmelCase		,       2		) + pow(_UpperCAmelCase		,       2		) + radius_a * radius_a)
 )





def 							a__     (					_UpperCAmelCase		,       _UpperCAmelCase		)      ->					float:

 '''simple docstring'''






 if torus_radius < 0 or tube_radius < 0:
  raise ValueError("vol_torus() only accepts non-negative values"		)
 return 2 * pow(_UpperCAmelCase		,       2		) * torus_radius * pow(_UpperCAmelCase		,       2		)





def 							a__     (					)      ->					Union[str, Any]:

 '''simple docstring'''






 print("Volumes:"		)
 print(f"Cube: {vol_cube(2		) = }"		)  # = 8
 print(f"Cuboid: {vol_cuboid(2		,       2		,       2		) = }"		)  # = 8
 print(f"Cone: {vol_cone(2		,       2		) = }"		)  # ~= 1.33
 print(f"Right Circular Cone: {vol_right_circ_cone(2		,       2		) = }"		)  # ~= 8.38
 print(f"Prism: {vol_prism(2		,       2		) = }"		)  # = 4
 print(f"Pyramid: {vol_pyramid(2		,       2		) = }"		)  # ~= 1.33
 print(f"Sphere: {vol_sphere(2		) = }"		)  # ~= 33.5
 print(f"Hemisphere: {vol_hemisphere(2		) = }"		)  # ~= 16.75
 print(f"Circular Cylinder: {vol_circular_cylinder(2		,       2		) = }"		)  # ~= 25.1
 print(f"Torus: {vol_torus(2		,       2		) = }"		)  # ~= 157.9
 print(f"Conical Frustum: {vol_conical_frustum(2		,       2		,       4		) = }"		)  # ~= 58.6
 print(f"Spherical cap: {vol_spherical_cap(1		,       2		) = }"		)  # ~= 5.24
 print(f"Spheres intersetion: {vol_spheres_intersect(2		,       2		,       1		) = }"		)  # ~= 21.21
 print(f"Spheres union: {vol_spheres_union(2		,       2		,       1		) = }"		)  # ~= 45.81
 print(
     f"Hollow Circular Cylinder: {vol_hollow_circular_cylinder(1		,       2		,       3		) = }"		)  # ~= 28.3


if __name__ == "__main__":
   main()

