Skip to content
Snippets Groups Projects
Commit cf412c1a authored by Dahmane Lynda's avatar Dahmane Lynda
Browse files

tri-fusion

parent 12b52a7b
No related branches found
No related tags found
No related merge requests found
#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
:module: ap_decorators
:author: FIL - Faculté des Sciences et Technologies - Univ. Lille <http://portail.fil.univ-lille1.fr>_
:date: 2018, september
"""
from functools import wraps
def trace(fct):
'''
Decorator for tracing every call to fct.
Recursive calls are indented.
:Example:
>>> @trace
... def fact(n):
... if n == 0:
... return 1
... else:
... return n * fact(n - 1)
>>> fact(5)
-> fact((5,), {})
... -> fact((4,), {})
...... -> fact((3,), {})
......... -> fact((2,), {})
............ -> fact((1,), {})
............... -> fact((0,), {})
............... <- 1
............ <- 1
......... <- 2
...... <- 6
... <- 24
<- 120
120
'''
@wraps(fct)
def wrapper(*args, **kwargs):
dots = '...' * wrapper.__depth
print('{:s} -> {:s}{:s}'.format(dots, wrapper.__name__, repr((args, kwargs))))
wrapper.__depth += 1
y = fct(*args, **kwargs)
wrapper.__depth -= 1
print('{:s} <- {:s}'.format(dots, repr(y)))
return y
wrapper.__depth = 0
return wrapper
def count(fct):
'''
decorator for counting calls to function fct
:Example:
>>> @count
... def fact(n):
... if n == 0:
... return 1
... else:
... return n * fact(n - 1)
>>> fact.counter
0
>>> fact(5)
120
>>> fact.counter
6
'''
@wraps(fct)
def wrapper(*args, **kwargs):
y = fct(*args, **kwargs)
wrapper.counter += 1
return y
wrapper.counter = 0
return wrapper
def memoize(fct):
'''
decorator for memoizing computed values of function fct
:Example:
>>> @count
... @memoize
... def fact(n):
... if n == 0:
... return 1
... else:
... return n * fact(n - 1)
>>> fact.counter
0
>>> fact(5)
120
>>> fact.counter
6
>>> fact.counter = 0
>>> fact(5)
120
>>> fact.counter
1
'''
cache = dict()
@wraps(fct)
def wrapper(*args, **kwargs):
key = repr((args, kwargs))
if key in cache:
return cache[key]
else:
y = fct(*args, **kwargs)
cache[key] = y
return y
return wrapper
if __name__ == '__main__':
import doctest
doctest.testmod(optionflags=doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS, verbose=False)
import thonnycontrib
from thonnycontrib.backend.evaluator import Evaluator
import thonnycontrib.backend.l1test_backend
from thonny.plugins.cpython_backend.cp_back import MainCPythonBackend
import thonnycontrib.backend.doctest_parser
from thonnycontrib.backend.doctest_parser import ExampleWithExpected, ExampleWithoutExpected
import thonnycontrib.backend.ast_parser
from thonnycontrib.backend.ast_parser import L1DocTest
import thonnycontrib.backend.verdicts
from thonnycontrib.backend.verdicts.ExceptionVerdict import ExceptionVerdict
import inspect
import tempfile
import os
import sys
class MockBackend(MainCPythonBackend):
"""
Fake backend.
"""
def __init__(self):
...
def send_message(self, msg) -> None:
...
# register backend
thonnycontrib.backend.l1test_backend.BACKEND = MockBackend()
def l1test_to_org(filename: str, source: str=""):
"""
Return an org abstract of the tests presents in `filename` file.
"""
abstract = {'total': 0,
'success': 0,
'failures': 0,
'errors': 0,
'empty': 0}
if source == "":
with open(filename, 'rt') as fin:
source = fin.read()
evaluator = Evaluator(filename=filename,
source=source)
tests = evaluator.evaluate()
n = len(tests)
abstract['total'] = n
res = ""
for test in tests:
examples = test.get_examples()
res_examples = ""
nb_test, nb_test_ok = 0, 0
empty = True
for example in examples:
verdict = test.get_verdict_from_example(example)
if isinstance(example, ExampleWithExpected):
nb_test += 1
if verdict.isSuccess():
nb_test_ok += 1
abstract['success'] += 1
else:
abstract['failures'] += 1
empty = False
if isinstance(verdict, ExceptionVerdict):
abstract['errors'] += 1
empty = False
res_examples += f"** {verdict}\n\n"
if not verdict.isSuccess():
res_examples += f" {verdict.get_details()}\n\n"
if not empty:
res += f"* {test.get_name()} ~ {nb_test_ok}/{nb_test} réussis\n\n"
else:
abstract['empty'] += 1
res += f"* {test.get_name()}\n\n Aucun test trouvé !\n\n"
res += res_examples
res = f"Tests exécutés : {abstract['total']}\nSuccès: {abstract['success']}, \
Echecs: {abstract['failures']}, Erreurs: {abstract['errors']}, \
Vide: {abstract['empty']}\n\n" + res
return res
def testmod(modulename: str):
"""
mimic the doctest.testmod function
for `modulename` module
"""
print(l1test_to_org(modulename))
#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
:mod:`list` module
:author: FIL - Faculté des Sciences et Technologies -
Univ. Lille <http://portail.fil.univ-lille1.fr>_
:date: 2022, march
:last revision: 2024, march
Provides
- a class :class:`ApLst` for non mutable lists
- an exception :class:`ApLstError`
ApLsts are either empty, either essentially objects with two composants :
#. a *head* composant which represent the head value of the list,
#. and a *tail* composant which is represent the tail of the list
"""
class ApLstError(Exception):
"""
Exception used by methods
* ``__init__``
* ``head``
* ``tail``
of class :class:`ApLst`.
"""
def __init__(self, msg):
self.message = msg
class ApLst():
"""
$$$ list = ApLst()
$$$ list.is_empty()
True
$$e list.head()
ApLstError
$$$ list2 = ApLst(1, list)
$$$ list2.is_empty()
False
$$$ list2.head()
1
$$$ list2.tail().is_empty()
True
$$$ l = ApLst(2, list2)
$$$ repr(l)
'ApLst(2, ApLst(1, ApLst()))'
$$$ str(l)
'[2, 1]'
$$$ repr(list2)
'ApLst(1, ApLst())'
"""
def __init__(self, *args):
"""
build a new empty list if args is empty,
or a list whose head is first element of args,
and tail list is second element of args.
précondition: len(args) in {0, 2}
and if len(args) == 2, args[1] must be a ApLst
raise: `ApLstError` if précondition is not satisfied
"""
if len(args) == 0:
self.content = ()
elif len(args) == 2:
if isinstance(args[1], ApLst):
self.content = (args[0], args[1])
else:
raise ApLstError('bad type for second argument')
else:
raise ApLstError('bad number of arguments')
def is_empty(self) -> bool:
"""
return
- True if self is empty
- False otherwise
précondition: none
"""
return len(self.content) == 0
def head(self):
"""
return: head element of self
raise: `ApLstError` if self is empty
"""
if self.is_empty():
raise ApLstError('head: empty list')
else:
return self.content[0]
def tail(self) -> "ApLst":
"""
return: tail list of self
raise: `ApLstError` if self is empty
"""
if self.is_empty():
raise ApLstError('head: empty list')
else:
return self.content[1]
def __str__(self) -> str:
"""
return: a string representation of list self
précondition: none
"""
def str_content(self, item_number=0):
if self.is_empty():
return ''
elif item_number == 50:
return ', ...'
else:
comma = '' if item_number == 0 else ', '
return (comma + str(self.head()) +
str_content(self.tail(), item_number + 1))
return f'[{str_content(self)}]'
def __repr__(self) -> str:
if self.is_empty():
content = ""
else:
content = f"{repr(self.head())}, {repr(self.tail())}"
return f"ApLst({content})"
if (__name__ == '__main__'):
import apl1test
apl1test.testmod("aplst.py")
#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
:mod:`recursive sorts`
:author: `FIL - FST - Univ. Lille.fr <http://portail.fil.univ-lille1.fr>`_
:date: 2016, september. Last revised: 2018, september
Some recursive sorting algorithms:
- quicksort
- mergesort
"""
from typing import Callable, TypeVar
from aplst import ApLst
T = TypeVar('T')
def compare(a: T, b: T) -> int:
"""
return:
- -1 if a < b
- 1 if a > b
- 0 if a = b
precondition: a and b must be comparable with <
exemples:
$$$ compare(0, 1)
-1
$$$ compare('a', 'a')
0
$$$ compare((2, 1), (1, 2))
1
"""
if a < b:
return -1
elif a > b:
return 1
else:
return 0
def length(li: ApLst) -> int:
"""
return the length of li.
precondition: none
examples:
$$$ length(ApLst())
0
$$$ length(ApLst(3, ApLst(1, ApLst(4, ApLst()))))
3
"""
if li.is_empty():
return 0
else:
return 1+length(li.tail())
def native_to_list(li: list[T]) -> ApLst:
"""
return a recursive list containing the same element of li.
precondition: none
examples:
$$$ native_to_list([]).is_empty()
True
$$$ rec_lst = native_to_list([3, 1, 4, 1, 5])
$$$ length(rec_lst)
5
$$$ rec_lst.head()
3
$$$ l = rec_lst.tail()
$$$ l.head()
1
$$$ l = l.tail()
$$$ l.head()
4
"""
if len(li)==0:
return ApLst()
else:
return ApLst(li[0],native_to_list(li[1:]))
def list_to_native(li: ApLst) -> list[T]:
"""
return a native python list containing the same element of li.
precondition: none
examples:
$$$ list_to_native(ApLst())
[]
$$$ list_to_native(ApLst(3, ApLst(1, ApLst(4, ApLst(1, ApLst(5, ApLst()))))))
[3, 1, 4, 1, 5]
"""
if li.is_empty():
return[]
else:
return[li.head()]+list_to_native(li.tail())
def is_sorted(l: ApLst, comp: Callable[[T, T], int]=compare) -> bool:
"""
return True if list l is sorted by ascending order
and False otherwise.
precondition: elements of l must be comparable
exemples:
$$$ is_sorted(native_to_list([1, 2, 3, 4]))
True
$$$ is_sorted(native_to_list([1, 2, 4, 3]))
False
"""
...
def split(l: ApLst) -> tuple[ApLst, ApLst]:
"""
return a couple (l1,l2) of lists of equal length
exemples:
$$$ l = [3, 1, 4, 1, 5, 9, 2]
$$$ l1, l2 = split(native_to_list(l))
$$$ abs(length(l1) - length(l2)) <= 1
True
$$$ l3 = list_to_native(l1) + list_to_native(l2)
$$$ len(l3) == len(l)
True
$$$ all(k in l for k in l3)
True
"""
...
def merge(l1: ApLst, l2: ApLst,
comp: Callable[[T, T], int]=compare) -> ApLst:
"""
return a list containing all elements de l1 and l2.
If l1 and l2 are sorted, so is the returned list.
precondition: elements of l1 and l2 are comparable
exemples:
$$$ list_to_native(merge(native_to_list([1, 3, 4, 9]), native_to_list([1, 2, 5])))
[1, 1, 2, 3, 4, 5, 9]
"""
...
def mergesort(l: ApLst, comp: Callable[[T, T], int]=compare) -> ApLst:
"""
return a new list containing elements of l sorted by ascending order.
precondition: elements of l are comparable
exemples:
$$$ list_to_native(mergesort(native_to_list([3, 1, 4, 1, 5, 9, 2])))
[1, 1, 2, 3, 4, 5, 9]
$$$ import random
$$$ n = random.randrange(20)
$$$ l = native_to_list([random.randrange(20) for k in range(n)])
$$$ l1 = mergesort(l)
$$$ length(l1) == length(l)
True
$$$ is_sorted(l1)
True
"""
...
if (__name__ == '__main__'):
import apl1test
apl1test.testmod("merge_sort.py")
#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
:mod:`mergesort_analysis` module
:author: FIL - FST - Univ. Lille <http://portail.fil.univ-lille.fr>_
:date: 2024, April.
Experimental analysis of mergesort algorithm
"""
from aplst import ApLst
from merge_sort import compare, mergesort, native_to_list
from ap_decorators import count
import random
import matplotlib.pyplot as plt
from math import log
def random_list(size: int) -> ApLst:
'''
:param size: (int)
:return: (list) list of size size containing all natural numbers from 0 to n-1 in random order
:CU: n >= 0
'''
l = list(range(size))
random.shuffle(l)
return native_to_list(l)
def myplot(listX: list[float], listY: list[float],
title: str='', xlabel: str ='', ylabel: str=''):
'''
plot the data in listX and listY
'''
plt.plot(listX, listY)
plt.title(title)
plt.xlabel(xlabel)
plt.ylabel(ylabel)
plt.show()
def build_comp_number_list(sort, max_size: int, sample_size: int) -> list[float]:
'''
return: list of average number of comparisons for sorting lists of size up to max_size
'''
comp = count(compare)
nb_comps = []
for size in range(1, max_size):
comp.counter = 0
for _ in range(sample_size):
sort(random_list(size), comp=comp)
nb_comps.append(comp.counter / sample_size)
return nb_comps
def usage():
print('Usage: {:s} <max size> <sample size>'.format(sys.argv[0]),
file=sys.stderr)
print('\t<max size> = max size of lists to sort', file=sys.stderr)
print('\t<sample size> = size of samples.', file=sys.stderr)
exit(1)
if __name__ == '__main__':
import sys
if len(sys.argv) != 3:
print('Bad number of arguments!', file=sys.stderr)
usage()
try:
MAX_SIZE = int(sys.argv[1])
except ValueError:
print('Max size must be an integer!', file=sys.stderr)
usage()
SIZES = list(range(1, MAX_SIZE))
try:
SAMPLE_SIZE = int(sys.argv[2])
except ValueError:
print('Sample size must be an integer!', file=sys.stderr)
usage()
# all is OK!
NB_COMPS = build_comp_number_list(mergesort, MAX_SIZE, SAMPLE_SIZE)
myplot(SIZES, NB_COMPS,
title=f'Nbre de comparaisons du tri fusion (mesuré sur échantillon de taille {SAMPLE_SIZE})',
xlabel='taille des listes',
ylabel='nbre de comparaisons')
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment