"""
Exercise 1: Shape hierarchy with polymorphism

Implement an abstract base class Shape (use abc.ABC + abstractmethod)
with an abstract method area(self), and two concrete subclasses:

  Rectangle(width, height) -> area = width * height
  Circle(radius)            -> area = 3.14159 * radius ** 2

Then implement total_area(shapes) that takes a list of Shape instances
(any mix of Rectangle/Circle) and returns the sum of their areas —
this function must NOT check `isinstance` at all; it should rely
purely on polymorphism (calling .area() on each).

Shape itself must not be directly instantiable.
"""
from abc import ABC, abstractmethod


class Shape(ABC):
    @abstractmethod
    def area(self):
        ...


class Rectangle(Shape):
    def __init__(self, width, height):
        # TODO
        pass

    def area(self):
        # TODO
        pass


class Circle(Shape):
    def __init__(self, radius):
        # TODO
        pass

    def area(self):
        # TODO
        pass


def total_area(shapes):
    # TODO: sum shape.area() for each shape — no isinstance checks.
    pass


# --- self-check: do not modify below this line ---
shapes = [Rectangle(3, 4), Circle(2), Rectangle(1, 1)]
result = round(total_area(shapes), 2)
expected = round(12 + 3.14159 * 4 + 1, 2)

try:
    Shape()
    abstract_enforced = False
except TypeError:
    abstract_enforced = True

checks = [
    (result, expected),
    (abstract_enforced, True),
]

all_passed = all(actual == expected for actual, expected in checks)
for actual, expected in checks:
    print(f"got {actual!r}, expected {expected!r} -> {'PASS' if actual == expected else 'FAIL'}")

print("ALL PASS" if all_passed else "SOME FAILED")
