{{indexmenu_n>9}}
======== HW 9 - Řazení haldou (Heap Sort) ========
^ Termín odevzdání | 17.05.2025@23:59 PDT|
^ Povinné zadání |  3b |
^ Volitelné zadání | Není |
^ Bonusové zadání |   Není |
^ Počet uploadů |  20 | 
^ Výchozí soubory |  {{ :courses:bab36prga:hw:bab36prga-hw9.zip |}} |
Podklady pro prioritní frontu jsou v přednášce věnované prioritní frontě. [[https://cw.fel.cvut.cz/wiki/courses/bab36prga/lectures/start#|]].
=====  Povinné zadaní ===== 
Cílem úlohy je seznámit se s realizací prioritní fronty haldou a využit princip haldy v implementaci řazení v HeapSortu ([[https://cs.wikipedia.org/wiki/%C5%98azen%C3%AD_haldou|řazení haldou]]). Pro jednoduchost jsou uvažována pouze celá čísla v rozsahu 0 až 4294967294 (''UINT_MAX-1'', viz ''limits.h''), která zároveň definují priority, nižší číslo má vyšší priority (min-heap). Řešení se skládá z implementace rozhraní ''pq_heap.h'' do samostatné dynamické knihovny ''libpq_heap.so'' a programu, který načte vstupní čísla ze ''stdin'' a zapíše je vzestupně na ''stdout''. Součástí výchozích souboru jsou kromě rozhraní ''pq_heap.h'' také binární programy generující vstup, refereční řešení a testovací program dynamicky linkovaný k příslušené variantě((Dle podporovaného OS.)) knihovny ''libpq_heap.so''.
Jako refereční řešení lze použít také dostupný nástroj ''sort'' s přepínačem numerického řazení ''-n'', viz [[https://www.man7.org/linux/man-pages/man1/sort.1.html|man sort]].
Datová struktura halda (//heap//) je plný binární strom reprezentovaný v poli pevné délky splňující **vlastnost haldy**, viz [[https://cw.fel.cvut.cz/wiki/courses/bab36prga/lectures/start#abstraktni_datovy_typ_adt_-_zasobnik_fronta_prioritni_fronta|lec09]]. Pro tzv. min haldu tedy platí, že libovolný uzel strom má hodnotu nižší než jeho levý nebo pravý následník, pokud existují, a kořen stromu je tedy prvek s nejnižší hodntou. 
Implementace [[https://en.wikipedia.org/wiki/Binary_heap#Heap_implementation|řazení haldou]] je možné realizovat funkcí ''heapify'' nad existující haldou, pole ''vals''o //n// prvcích.
Z pohledu implementace prioritní fronty haldou v poli ''vals'' o //n// prvcích, můžeme implementovat i tak, že postupně zaměňuje hodnotu kořene, ''vals[0]'' s prvkem na konci pole ''vals[n-1]'' a nový kořen zaměňuje v poli o uvažované velikosti ''n-1'' podobně jako při operaci ''pq_pop()'' tak, aby platila vlastnost haldy. Postupně tak zkracujeme uvažovanou délku pole haldy a postupnou záměnou všech //n// prvků jsou hodnoty v poli uspořádány **sestupně**. Proto následně pro zachování vlastnosti haldy na původním polem o velikosti //n// obrátíme pořadí prvků.
Implementace načtení hodnot a jejich vytištění v **roustoucím** pořadí postačí načtené hodnoty vkládat do priortiní fronty funkcí ''pq_push()'' a po načtení je postupně vyjímat funkcí ''pq_pop()'' a tisknout na ''stdout''.
Součástí knihovny ''pq_heap'' jsou následující funkce pro práci s prioritní frontou.
  * Funkce ''pq_alloc()'' vytvoření datové struktury prioritní fronty.
  * Funkce ''pq_release()'' uvolnění alokované paměti struktury prioritní fronty.
  * Funkce ''pq_size()'' vrací počet prvků v prioritní frontě.
  * Funkce ''pq_is_empty()'' test, zdali je prioritní fronta prázdná.
  * Funkce ''pq_push()'' vložení prvku do prioritní fronty.
  * Funkce ''pq_pop()'' vyjmutí nejprioritnějšího (nejmenšího) prvku z prioritní fronty.
  * Funkce ''pq_peek()'' vrací hodnotu nejprioritnějšího (nejmenšího) prvku prioritní fronty.
  * Funkce ''pq_seek()'' vrací hodnotu prvku v poli priortní fronty haldy na předané pozici v poli.
  * Funkce ''pq_heapify()'' uspořádá akutální pole haldy vzestupně.
Volání ''pq_peek()'' a ''pq_seek()'' vyžaduje alespoň jeden prvek ve frontě. Rozhraní neumožňuje indikovat nevhodné volání návratovou hodnoutou. Proto v případě volání na prázdnou frontou je možné program předčasně ukončit voláním ''exit()'', např. s hodnotou ''EAGAIN'' tak, jako v referenčním řešení.
==== Rozhraní definované v souboru ''pq_heap.h'' ====
#include 
/*
 * Initialize priority queue (pq) structure using heap as full binary tree implemented in array to store up to  the capacity items.
 *
 * return pointer to the dynamically allocated pq structure or NULL on a failure
 */
void *pq_alloc(uint32_t capacity);
/*
 * Release all allocated memory of the passed
 * pq structure that is supposed to be allocated by 
 * pq_alloc()
 */
void pq_release(void *pq);
/* 
 * Return number of items in the pq
 */
uint32_t pq_size(const void *pq);
/*
 * Return true if pq is empty
 */
_Bool pq_is_empty(const void *pq);
/* 
 * Insert the passed value v into the priority queue
 * according to its value. 
 *
 * Return true on success; otherwise false, e.g., pq is full).
 */
_Bool pq_push(void *pq, uint32_t v);
/* The priority queue (pq) is supposed to be implemented by min-heap, the item with the lowest value (priority) is popped first.
 *
 * Return true on success; otherwise false, e.g., pq is empty).
 */
_Bool pq_pop(void *pq, uint32_t *v);
/* 
 * Return the top item from the pq.
 * Function fail if call on empty queue
 */ 
uint32_t pq_peek(void *pq);
/* 
 * Return the item value from the seek index of the heap array
 * Function fail if seek is out of range.
 * For seek = 0, it behaves as pq_peek()
 *
 */ 
uint32_t pq_seek(void *pq, uint32_t seek);
/* 
 * Sort the internal representation using heap property, such that 
 * the complexity can be bounded by O(n log2 n). 
 * When heapifid, pq_seek for 0..pq_size() returns sorted values 
 */ 
void pq_heapify(void *pq);
===== Odevzdávané soubory =====
Odevzdávejte pouze soubor ''pq_heap.c''s implementací prioritní fronty haldou. Soubory uložte přímo do zip archivu a **nevytvářejte žádné složky**.  Správnost implementace bude ověřena testovacím programem.
**Povinné soubory**:
  * ''pq_heap.c'' - implementace rozhraní dle výchozího souboru ''pq_heap.h''.
===== Způsob testování =====
Pro jednodušší testování jsme připravili testovací prgram ''bab36prga-hw9-test'', který linkuje  dynamickou knihovnu ''libpq_heap.so'' s implementací prioritní fronty a volá příslušené rozhraní a porovnává hodnoty ve frontě s referenčním řešením. Program zároveň testuje řazení a porovnává rychlost řazení, která je očekávána v rozsahu +/- 10 % času referečního řešení.
Referenční program ''bab36prga-hw9-genref'' lze použít pro generování náhodné posloupnosti celých čísel přepínačem ''-generate'' a uvedením počtu čísel, např. ''./bab36prga-hw9-genref -generate 1000''.
Příklad prostředí s knihovnou ''libpq_heap.so'' a testovacím programem ''binaries/bab36prga-hw9-test'' 
ldd ./binaries/bab36prga-hw9-test
	linux-vdso.so.1 (0x00007b10c68ef000)
	libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007b10c67d9000)
	libpq_heap.so => ./libpq_heap.so (0x00007b10c67d4000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007b10c6400000)
	/lib64/ld-linux-x86-64.so.2 (0x00007b10c68f1000)
Spuštění testu vůči knihovně ''libpq_heap.so'' v aktuálním pracovním adresáři může vypadat například následovně.
./bab36prga-hw9-test
Basic test ........... PASSED
Multiple queues test . PASSED
Sort basic test ...... PASSED
DEBUG: Generate 42949672 random values in 5.8 seconds
DEBUG: Solution sort time  16.9 seconds
DEBUG: Reference sort time 16.6 seconds
DEBUG: Difference is 1.8 %
Sort time test ....... PASSED
All tests passed
Programem ''bab36prga-hw9-genref'' můžeme vygenerovat větší testovací soubor, který uložíme do [[https://cs.wikipedia.org/wiki/RAMDisk|ramdisku]] v adresáři ''/tmp''. Nástrojem ''time'' můžeme změřit výkon řazení s využitím vlastní nebo výchozí prioritní fronty, například následovně.
clang -pedantic -Wall -O3 -std=gnu11 -g main.c -Wl,-rpath=. libpq_heap-fbsd.so -o main
./binaries/bab36prga-hw9-genref -generate 10000000 > /tmp/
time ./main /tmp/out2
./main < /tmp/in > /tmp/out2  3.49s user 0.08s system 99% cpu 3.574 total
time sort -n /tmp/in > /tmp/out
sort -n /tmp/in > /tmp/out  20.54s user 0.94s system 99% cpu 21.475 total
time gsort -n /tmp/in > /tmp/out
gsort -n /tmp/in > /tmp/out  10.28s user 0.79s system 392% cpu 2.821 total
Kde si můžeme všimnout, že nástroj ''gsort'' je na daném stroji výrazně rychlejší, neboť využívá téměř 4 jádra procesoru. Naopak standardní  ''sort'' je mnohem pomalejší než naše implementace.
====== Odevzdání a hodnocení ======
/* Veřejné příklady + Makefile: {{ :courses:bab36prga:hw:b3b36prg-hw08.zip |}} */
^  ^  Povinné zadání  ^
^ Název v BRUTE |   HW9  |
^ Odevzdávané soubory |  pq_heap.c  |
^ Argumenty při spuštění |  žádné  	  |
^ Kompilace  |  clang -pedantic -Wall -Werror -std=gnu99 -O2 -g |
^ Procvičované oblasti |  datové struktury \\ práce s polem  |