P1 2021/22 - 10 Kalibracija laserja - resitev Aljaz S.¶
from math import pi, pow
import inspect
import sys
import unittest
import contextlib
import random
import ast
# Ne kopiraj tega slovarja ven iz funkcije!
# Vedno samo kliči funkcijo!
def r(koord):
return {(0, 10): 7.5,
(0, 6): 2.5,
(3.75, 12.5): 2.5,
(3.75, 11.25): 1.25,
(-3.75, 12.5): 2.5,
(-5, 12.5): 1.25,
(20, 10): 7.5, (20, 6): 2.5,
(23.75, 12.5): 2.5, (23.75, 11.25): 1.25,
(16.25, 12.5): 2.5, (15, 12.5): 1.25,
(20, 10.625): 0.625,
(-2.5, -8.75): 8.75,
(-8.125, -4.375): 0.625,
(-1.875, -9.375): 6.875,
(-1.25, -10): 5,
(-1.25, -9.25): 3.75,
(-0.625, -10.625): 1.875,
(21.25, -16.25): 11.25,
(21.25, -7.5): 2.5,
(22.5, -7.5): 1.25,
(25.625, -13.125): 3.125,
(26.875, -11.875): 0.625,
(26.25, -21.25): 3.75,
(16.25, -21.25): 3.75,
(16.25, -22.25): 2.5,
(16.125, -22.875): 0.875,
(16.25, -12.5): 3.75
}[koord]
# Ne kopiraj tega slovarja ven iz funkcije!
# Vedno samo kliči funkcijo!
def notranji(koord):
noter = {(0, 10): [(0, 6), (3.75, 12.5), (-3.75, 12.5)],
(0, 6): [],
(3.75, 12.5): [(3.75, 11.25)],
(3.75, 11.25): [],
(-3.75, 12.5): [(-5, 12.5)],
(-5, 12.5): [],
(20, 10): [(20, 6), (23.75, 12.5), (16.25, 12.5), (20, 10.625)],
(20, 6): [],
(23.75, 12.5): [(23.75, 11.25)],
(23.75, 11.25): [],
(16.25, 12.5): [(15, 12.5)],
(15, 12.5): [],
(20, 10.625): [],
(-2.5, -8.75): [(-8.125, -4.375), (-1.875, -9.375)],
(-8.125, -4.375): [],
(-1.875, -9.375): [(-1.25, -10)],
(-1.25, -10): [(-1.25, -9.25)],
(-1.25, -9.25): [(-0.625, -10.625)],
(-0.625, -10.625): [],
(21.25, -16.25): [(21.25, -7.5), (25.625, -13.125),
(26.25, -21.25), (16.25, -21.25),
(16.25, -12.5)],
(21.25, -7.5): [(22.5, -7.5)],
(22.5, -7.5): [],
(25.625, -13.125): [(26.875, -11.875)],
(26.875, -11.875): [],
(26.25, -21.25): [],
(16.25, -21.25): [(16.25, -22.25)],
(16.25, -22.25): [(16.125, -22.875)],
(16.125, -22.875): [],
(16.25, -12.5): []}[koord]
random.shuffle(noter)
return noter
#
# NALOGA
#
def povrsina(koord):
return pi * pow(r(koord), 2) + sum( map(povrsina, notranji(koord)) )
def najmanjsi(koord):
# return min([ *map(najmanjsi, notranji(koord)), koord ], key=r)
return min( [ najmanjsi(k) for k in notranji(koord) ] + [koord], key=r)
#
# TEST
#
class Test(unittest.TestCase):
with open(__file__, "r", encoding="utf-8") as f:
functions = {
elm.name: elm
for elm in ast.parse(f.read()).body
if isinstance(elm, ast.FunctionDef)}
def assert_proper_shape(self, func):
code = func.__code__
self.assertTrue(
code.co_argcount + code.co_kwonlyargcount == 1
and not code.co_flags & (inspect.CO_VARARGS | inspect.CO_VARKEYWORDS),
f"Funkcija '{func.__name__}' naj sprejme en argument"
)
class TestObvezna(Test):
def setUp(cls):
sys.setrecursionlimit(10000)
@staticmethod
def r0(koord):
x, _ = koord
return 2 ** f"{x:b}"[::-1].index("1")
@classmethod
def notranji0(cls, koord):
x, y = koord
r0 = cls.r0(koord) // 2
return r0 and [(x - r0, y), (y + r0, y)] or []
@staticmethod
def r1(koord):
x, _ = koord
return 1000 - x
@staticmethod
def notranji1(koord):
x, _ = koord
return [(x + 1, 0)] if x < 999 else []
@classmethod
@contextlib.contextmanager
def patched_with(cls, new_r, new_notranji):
global r, notranji
try:
r, orig_r = new_r, r
notranji, orig_notranji = new_notranji, notranji
yield
finally:
r = orig_r
notranji = orig_notranji
def test_00_oblika_funkcij(self):
for func in self.functions:
self.assertIn(func, {"povrsina", "najmanjsi", "r", "notranji"},
f"\nFunkcija {func} ni dovoljena.")
def test_01_povrsina(self):
self.assert_proper_shape(povrsina)
self.assertAlmostEqual(pi * 78.125, povrsina((0, 10)))
self.assertAlmostEqual(pi * 78.515625, povrsina((20, 10)))
self.assertAlmostEqual(pi * 166.796875, povrsina((-2.5, -8.75)))
self.assertAlmostEqual(pi * 193.734375, povrsina((21.25, -16.25)))
with self.patched_with(self.r0, self.notranji0):
self.assertAlmostEqual(
sum(pi * 2 ** i * (2 ** (10 - i)) ** 2 for i in range(11)),
povrsina((1024, 0)), places=3)
self.assertAlmostEqual(
sum(pi * 2 ** i * (2 ** (8 - i)) ** 2 for i in range(9)),
povrsina((256, 0)), places=3)
with self.patched_with(self.r1, self.notranji1):
self.assertAlmostEqual(
sum(pi * i ** 2 for i in range(1001)),
povrsina((0, 0)), places=3)
sys.setrecursionlimit(1000)
# Če pade ta test, to pomeni, da funkcija ne preiskuje krogov
# rekurzivno. Piši profesorju ali asistentom
self.assertRaises(RecursionError, povrsina, (0, 0))
def test_02_najmanjsi(self):
self.assert_proper_shape(najmanjsi)
self.assertEqual((20, 10.625), najmanjsi((20, 10)))
self.assertEqual((-8.125, -4.375), najmanjsi((-2.5, -8.75)))
self.assertEqual((26.875, -11.875), najmanjsi((21.25, -16.25)))
self.assertIn(najmanjsi((0, 10)), {(3.75, 11.25), (-5, 12.5)})
with self.patched_with(self.r0, self.notranji0):
for x0 in (64, 256, 1024):
x, y = najmanjsi((x0, 0))
self.assertEqual(0, y)
self.assertEqual(1, x % 2)
with self.patched_with(self.r1, self.notranji1):
self.assertEqual((999, 0), najmanjsi((0, 0)))
sys.setrecursionlimit(1000)
# Če pade ta test, to pomeni, da funkcija ne preiskuje krogov
# rekurzivno. Piši profesorju ali asistentom
self.assertRaises(RecursionError, najmanjsi, (0, 0))
class TestDodatna(Test):
def test_oneline(self):
for funcname in {"povrsina", "najmanjsi"}:
body = self.functions[funcname].body
self.assertEqual(len(body), 1, "\nFunkcija ni dolga le eno vrstico")
self.assertIsInstance(body[0], ast.Return, "\nFunkcija naj bi vsebovala le return")
if __name__ == "__main__":
unittest.main()
Zadnja posodobitev:
May 7, 2022