import numbers
import math

#all interior are less than 180 degree
#all sides have equal length

#apothem: goes from the center of the polygon to the middle of the edge
#circumradius: goes from the midpoint of the polygon to the edge of the circle

#n edges (= n vettices)
#R circumradius

#formula
#interior angle: (n - 2)*180/n
#edge length: S = 2R sin(pi/n)
#apothem: a = R cos(pi/n)
#area: 1/2nsa
#perimeter: ns

#Goal 1: Create a Polygon class
#Initializer: number of edges/vertices, circumradius
#Properties: edges, vertices; calculated properties: interior angle, edge length, apothem, area, perimeter
#Functionality: a proper representation (__repr__), implements equality (==) based on vertices and circumradius(__eq__), 
#				implements > based on number of vertices only (__gt__)

#Goal 2: Implement a Polygon sequence type
#Initializer: number of vertices for largest polygon in the sequence
#			  common circumradius for all polygons
#Properties: max efficiency polygon: returns the polygon with the highest area:perimeter ration
#Functionality: functions as a sequence type(__getitem__), supports the len() function(__len__)

class Polygon:
	def __init__(self, n, R):
		if isinstance(n, numbers.Real) and isinstance(R, numbers.Real):
			self._R = R
			self._n = n
		

	def __repr__(self):
		return f'Polygon(n={self._n}, R={self._R})'
	
	@property
	def count_vertices(self):
		return self._n

	@property
	def count_edges(self):
		return self._n

	@property
	def circumradius(self):
		return self._R

	@property
	def interior_angle(self):
		return (self._n - 2) * 180 / self._n

	@property
	def side_length(self):
		return 2* self._R * math.sin(math.pi / self._n)

	@property
	def apothem(self):
		return self._R * math.cos(math.pi / self._n)
	
	@property
	def area(self):
		return self._n / 2 * self.side_length * self.apothem

	@property
	def perimeter(self):
		return self._n * self.side_length
	
	
def test_polygon():
	n = 4
	R = 1
	p = Polygon(n, R)

	assert str(p) == f'Polygon(n=4, R=1)', f'actual: {str(p)}'
	assert p.count_vertices == n, (f'actual: {p.count_vertices}, '
								   f'expected: {n}'	)
	assert p.count_edges == n
	assert p.circumradius == R
	assert p.interior_angle == 90
	assert p.area == 2.0, (f'actual: {p.area}, '
							f'expexted: {2.0}')

test_polygon()

don’t ever compare floats.

1.0 + 1.0 + 1.0 == 3.0
#False
#The resolution: math.iscolse

!!! Be careful for Polygon at least needs 3 sides as a triangle.

p = Polygon(1, 10)
# No Error!!!

Untitled

Goal 2

import math

class Polygon:
	def __init__(self, n, R):
		if n < 3:
			raise ValueError('Polygon must have at lease 3 sides.')
		self._R = R
		self._n = n
		

	def __repr__(self):
		return f'Polygon(n={self._n}, R={self._R})'
	
	@property
	def count_vertices(self):
		return self._n

	@property
	def count_edges(self):
		return self._n

	@property
	def circumradius(self):
		return self._R

	@property
	def interior_angle(self):
		return (self._n - 2) * 180 / self._n

	@property
	def side_length(self):
		return 2 * self._R * math.sin(math.pi / self._n)

	@property
	def apothem(self):
		return self._R * math.cos(math.pi / self._n)
	
	@property
	def area(self):
		return self._n / 2 * self.side_length * self.apothem

	@property
	def perimeter(self):
		return self._n * self.side_length

	def __eq__(self, other):
		if isinstance(other, self.__class__): #if isinstance(other, Polygon) this way works fine
			return (self.count_edges == other.count_edges
					and self.circumradius == other.circumradius) #self._n == other._n is this way works fine
		else:
			return NotImplemented

	def __gt__(self, other):
		if isinstance(other, Polygon):
			return self.count_vertices > other.count_vertices
		else:
			return NotImplemented

	#---------------------Goal 2 start line----------------------------
class Polygons:
	def __init__(self, m, R):
		if m < 3:
			raise ValueError('m must be greater than 3')
		self._m = m
		self._R = R
		self._polygons = [Polygon(i, R) for i in range(3, m+1)]

	def __len__(self):
		return self._m - 2

	def __repr__(self):
		return f'Polygons(m={self._m}, R={self._R})'

	def __getitem__(self, s):
		return self._polygons[s]

	@property
	def max_efficiency_polygon(self):
		sorted_polygons = sorted(self._polygons,
									key=lambda p: p.area / p.perimeter,
									reverse=True)
		return sorted_polygons[0]
	

# for p in polygons:
# 	print(p)

# for p in polygons[2:5]:
# 	print(p)

# for p in polygons[::-1]:
# 	print(p)

# print(polygons.max_efficiency_polygon)

# print([(p, p.area/p.perimeter) for p in polygons])

polygons = Polygons(500, 1)
p = polygons.max_efficiency_polygon
print(p.area)
assert math.isclose(p.area, math.pi, rel_tol= 0.001, abs_tol=0.001)
#True