building block classes an angle in degrees unit of measurement angle angledegrees90 angle45 5 angledegrees45 5 angle1 traceback most recent call last typeerror degrees must be a numeric value between 0 and 360 angle361 traceback most recent call last typeerror degrees must be a numeric value between 0 and 360 a side of a two dimensional shape such as polygon etc adjacentsides a list of sides which are adjacent to the current side angle the angle in degrees between each adjacent side length the length of the current side in meters side5 sidelength5 angleangledegrees90 nextsidenone side5 angle45 6 sidelength5 angleangledegrees45 6 nextsidenone side5 angle45 6 side1 angle2 doctest ellipsis sidelength5 angleangledegrees45 6 nextsidesidelength1 angleangled a geometric ellipse on a 2d surface ellipse5 10 ellipsemajorradius5 minorradius10 ellipse5 10 is ellipse5 10 false ellipse5 10 ellipse5 10 true ellipse5 10 area 157 07963267948966 ellipse5 10 perimeter 47 12388980384689 a geometric circle on a 2d surface circle5 circleradius5 circle5 is circle5 false circle5 circle5 true circle5 area 78 53981633974483 circle5 perimeter 31 41592653589793 circle5 diameter 10 return the maximum number of parts that circle can be divided into if cut numcuts times circle circle5 circle maxparts0 1 0 circle maxparts7 29 0 circle maxparts54 1486 0 circle maxparts22 5 265 375 circle maxparts222 traceback most recent call last typeerror numcuts must be a positive numeric value circle maxparts222 traceback most recent call last typeerror numcuts must be a positive numeric value an abstract class which represents polygon on a 2d surface polygon polygonsides polygon addsideside5 polygonsidessidelength5 angleangledegrees90 nextsidenone polygon getside0 traceback most recent call last indexerror list index out of range polygon addsideside5 getside1 sidelength5 angleangledegrees90 nextsidenone polygon setside0 side5 traceback most recent call last indexerror list assignment index out of range polygon addsideside5 setside0 side10 polygonsidessidelength10 angleangledegrees90 nextsidenone a geometric rectangle on a 2d surface rectangleone rectangle5 10 rectangleone perimeter 30 rectangleone area 50 rectangle5 10 doctest normalizewhitespace rectanglesidessidelength5 angleangledegrees90 nextsidenone sidelength10 angleangledegrees90 nextsidenone a structure which represents a geometrical square on a 2d surface squareone square5 squareone perimeter 20 squareone area 25 building block classes an angle in degrees unit of measurement angle angle degrees 90 angle 45 5 angle degrees 45 5 angle 1 traceback most recent call last typeerror degrees must be a numeric value between 0 and 360 angle 361 traceback most recent call last typeerror degrees must be a numeric value between 0 and 360 a side of a two dimensional shape such as polygon etc adjacent_sides a list of sides which are adjacent to the current side angle the angle in degrees between each adjacent side length the length of the current side in meters side 5 side length 5 angle angle degrees 90 next_side none side 5 angle 45 6 side length 5 angle angle degrees 45 6 next_side none side 5 angle 45 6 side 1 angle 2 doctest ellipsis side length 5 angle angle degrees 45 6 next_side side length 1 angle angle d a geometric ellipse on a 2d surface ellipse 5 10 ellipse major_radius 5 minor_radius 10 ellipse 5 10 is ellipse 5 10 false ellipse 5 10 ellipse 5 10 true ellipse 5 10 area 157 07963267948966 ellipse 5 10 perimeter 47 12388980384689 a geometric circle on a 2d surface circle 5 circle radius 5 circle 5 is circle 5 false circle 5 circle 5 true circle 5 area 78 53981633974483 circle 5 perimeter 31 41592653589793 circle 5 diameter 10 return the maximum number of parts that circle can be divided into if cut num_cuts times circle circle 5 circle max_parts 0 1 0 circle max_parts 7 29 0 circle max_parts 54 1486 0 circle max_parts 22 5 265 375 circle max_parts 222 traceback most recent call last typeerror num_cuts must be a positive numeric value circle max_parts 222 traceback most recent call last typeerror num_cuts must be a positive numeric value an abstract class which represents polygon on a 2d surface polygon polygon sides polygon add_side side 5 polygon sides side length 5 angle angle degrees 90 next_side none polygon get_side 0 traceback most recent call last indexerror list index out of range polygon add_side side 5 get_side 1 side length 5 angle angle degrees 90 next_side none polygon set_side 0 side 5 traceback most recent call last indexerror list assignment index out of range polygon add_side side 5 set_side 0 side 10 polygon sides side length 10 angle angle degrees 90 next_side none a geometric rectangle on a 2d surface rectangle_one rectangle 5 10 rectangle_one perimeter 30 rectangle_one area 50 rectangle 5 10 doctest normalize_whitespace rectangle sides side length 5 angle angle degrees 90 next_side none side length 10 angle angle degrees 90 next_side none a structure which represents a geometrical square on a 2d surface square_one square 5 square_one perimeter 20 square_one area 25
from __future__ import annotations

import math
from dataclasses import dataclass, field
from types import NoneType
from typing import Self




@dataclass
class Angle:
    

    degrees: float = 90

    def __post_init__(self) -> None:
        if not isinstance(self.degrees, (int, float)) or not 0 <= self.degrees <= 360:
            raise TypeError("degrees must be a numeric value between 0 and 360.")


@dataclass
class Side:
    

    length: float
    angle: Angle = field(default_factory=Angle)
    next_side: Side | None = None

    def __post_init__(self) -> None:
        if not isinstance(self.length, (int, float)) or self.length <= 0:
            raise TypeError("length must be a positive numeric value.")
        if not isinstance(self.angle, Angle):
            raise TypeError("angle must be an Angle object.")
        if not isinstance(self.next_side, (Side, NoneType)):
            raise TypeError("next_side must be a Side or None.")


@dataclass
class Ellipse:
    

    major_radius: float
    minor_radius: float

    @property
    def area(self) -> float:
        
        return math.pi * self.major_radius * self.minor_radius

    @property
    def perimeter(self) -> float:
        
        return math.pi * (self.major_radius + self.minor_radius)


class Circle(Ellipse):
    

    def __init__(self, radius: float) -> None:
        super().__init__(radius, radius)
        self.radius = radius

    def __repr__(self) -> str:
        return f"Circle(radius={self.radius})"

    @property
    def diameter(self) -> float:
        
        return self.radius * 2

    def max_parts(self, num_cuts: float) -> float:
        
        if not isinstance(num_cuts, (int, float)) or num_cuts < 0:
            raise TypeError("num_cuts must be a positive numeric value.")
        return (num_cuts + 2 + num_cuts**2) * 0.5


@dataclass
class Polygon:
    

    sides: list[Side] = field(default_factory=list)

    def add_side(self, side: Side) -> Self:
        
        self.sides.append(side)
        return self

    def get_side(self, index: int) -> Side:
        
        return self.sides[index]

    def set_side(self, index: int, side: Side) -> Self:
        
        self.sides[index] = side
        return self


class Rectangle(Polygon):
    

    def __init__(self, short_side_length: float, long_side_length: float) -> None:
        super().__init__()
        self.short_side_length = short_side_length
        self.long_side_length = long_side_length
        self.post_init()

    def post_init(self) -> None:
        
        self.short_side = Side(self.short_side_length)
        self.long_side = Side(self.long_side_length)
        super().add_side(self.short_side)
        super().add_side(self.long_side)

    def perimeter(self) -> float:
        return (self.short_side.length + self.long_side.length) * 2

    def area(self) -> float:
        return self.short_side.length * self.long_side.length


@dataclass
class Square(Rectangle):
    

    def __init__(self, side_length: float) -> None:
        super().__init__(side_length, side_length)

    def perimeter(self) -> float:
        return super().perimeter()

    def area(self) -> float:
        return super().area()


if __name__ == "__main__":
    __import__("doctest").testmod()