<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">% arara: pdflatex
% arara: pdflatex
% arara: pdflatex

% options:
% thesis=B bachelor's thesis
% thesis=M master's thesis
% czech thesis in Czech language
% slovak thesis in Slovak language
% english thesis in English language
% hidelinks remove colour boxes around hyperlinks

\documentclass[thesis=B,czech]{FITthesis}[2012/06/26]

\usepackage[utf8]{inputenc} % LaTeX source encoded as UTF-8

\usepackage{graphicx} %graphics files inclusion
\usepackage[]{algorithm2e}
\usepackage{listings}
\usepackage{float}
% \usepackage{amsmath} %advanced maths
% \usepackage{amssymb} %additional math symbols

\usepackage{dirtree} %directory tree visualisation

% % list of acronyms
% \usepackage[acronym,nonumberlist,toc,numberedsection=autolabel]{glossaries}
% \iflanguage{czech}{\renewcommand*{\acronymname}{Seznam pou{\v z}it{\' y}ch zkratek}}{}
% \makeglossaries

\newcommand{\tg}{\mathop{\mathrm{tg}}} %cesky tangens
\newcommand{\cotg}{\mathop{\mathrm{cotg}}} %cesky cotangens
\newcommand{\classname}[1]{\texttt{#1}}
% % % % % % % % % % % % % % % % % % % % % % % % % % % % % % 
% ODTUD DAL VSE ZMENTE
% % % % % % % % % % % % % % % % % % % % % % % % % % % % % % 

\department{Katedra softwarového inženýrství}
\title{Implementace kompresních metod LZ77, LZ78 a~LZW v~jazyce Java}
\authorGN{Ladislav} %(křestní) jméno (jména) autora
\authorFN{Zemek} %příjmení autora
\authorWithDegrees{Ladislav Zemek} %jméno autora včetně současných akademických titulů
\author{Ladislav Zemek} %jméno autora bez akademických titulů
\supervisor{Ing. Radomír Polách}
\acknowledgements{Rád bych poděkoval Ing. Radomírovi Poláchovi za odborné vedení, pomoc a vstřícnost při vedení bakalařské práce.}
\abstractCS{Tato bakalářská práce se~zabývá návrhem, analýzou, implementací a~testováním tří kompresních metod LZ77, LZ78 a~LZW do~knihovny SCT. Knihovna je koncipována tak, aby v~budoucnu nabídla uživatelům velkou škálu kompresních metod a~tak usnadnila výběr vhodné metody pro jejich projekt.  Uvnitř této práce se zabývám vhodným návrhem a~popisem daných algoritmů. U~všech metod se mi podařilo při vhodně zvolených parametrech dosáhnout zmenšení testovaných korpusů vždy alespoň o~30~\%. Přínosem této práce je přispění do knihovny, která může v budoucnu usnadnit některým vývojářům práci.}
\abstractEN{This bachelor thesis deals with the design, analysis, implementation and~testing of~three compression methods LZ77, LZ78 and~LZW in~the~SCT library. The~library is designed to~offer users a~wide range of~compression methods in~the~future to~facilitate the~choice of~the~right method for their project. Inside this thesis I deal with suitable design and~description of~given algorithms. For all methods, I managed to~reduce the tested corpuses with~appropriately selected parameters at~least by~30~\%. The benefit of~this work is contributing to a~library that can make it easier for some developers to work in the~future.}
\placeForDeclarationOfAuthenticity{V~Praze}
\declarationOfAuthenticityOption{4} %volba Prohlášení (číslo 1-6)
\keywordsCS{Komprese dat, dekomprese dat, chaining, Java, LZ77, LZ78, LZW, SCT}
\keywordsEN{Compression, decompression, chaining, LZ77, LZ78, LZW, SCT}
%\website{https://gitlab.fit.cvut.cz/zemeklad/BP} %volitelná URL práce, objeví se v tiráži - úplně odstraňte, nemáte-li URL práce

\begin{document}

%\newacronym{CVUT}{{\v C}VUT}{{\v C}esk{\' e} vysok{\' e} u{\v c}en{\' i} technick{\' e} v Praze}
%\newacronym{FIT}{FIT}{Fakulta informa{\v c}n{\' i}ch technologi{\' i}}

\begin{introduction}
	V~době, kdy se začali počítače rozšiřovat do~běžného života, vznikla potřeba ukládat čím dál větší objem dat. Z~dnešního pohledu to nevypadá jako problém, jednoduše si uložíte několika gigabytový soubor na svůj pevný disk s~kapacitou stovek gigabytů. Ovšem tehdy měli pevné disky jen desítky megabytů, což je téměř sto tisíckrát méně než dnes, paměti tedy rozhodně nebylo nazbyt. Proto bylo potřeba data nějakým způsobem zmenšit, za~předpokladu, že nedojde ke~ztrátě informací.
	  
	Právě za tímto účelem vznikla bezztrátová komprese dat, která zakóduje data pomocí důmyslných algoritmů. Nová data pak mají výrazně menší velikost než ta původní.\cite{komprese} Jsou tedy vhodnější pro uložení na~disk nebo k~odeslání přes internet. Data je následně možné dekódovat pomocí dekomprese, což je inverzní operace ke kompresi. Výstupem dekomprese jsou původní data.
	
	Jelikož v knihovně SCT nejsou naimplementovány zmíněné algoritmy, rozhodl jsem se je do knihovny doplnit.
	
	\section{Pojmy}
	V této kapitole bych rád vysvětlil a definoval některé pojmy.
	\begin{itemize}
		\item \textbf{Komprese/komprimace dat} -- Zmenšování objemu dat.
		\item \textbf{Bezztrátová komprese dat} -- Komprese dat, při které nedochází ke ztrátě informace.
		\item \textbf{Dekomprese/dekomprimace dat} -- Inverzní operace ke kompresi. Ze zmenšených dat obnovím ty původní.
		\item \textbf{Triplet} -- V této bakalářské práci je tripletem myšlena jakákoliv n-tice. Je tomu tak proto, že v knihovně SCT je každá n-tice zpracovávána pomocí třídy TripletProcessor. Neznamená to tedy vždy trojici (LZ77). V závislosti na metodě může jít i o dvojici (LZ78) nebo jednici (LZW).
		\item \textbf{Adaptivní kompresní metoda} -- Tyto metody si vytváří struktůry za běhu programu v závislosti na vstupních datech. Vytvořené struktůry pak používá ke komprimaci. Není potřeba tyto struktůry ukládat společně s komprimovanými daty. 
		\item \textbf{Slovníkové kompresní metody} -- Tyto metody používají pro komprimaci slovník, který v průběhu komprimace i dekomprimace roste přidáváním nových frází. Proto patří slovníkové metody mezi adaptivní kompresní metody. Komprimovaná data poté obsahují indexy frází ve vytvářeném slovníku.
		\item \textbf{Symetrická kompresní metoda} -- Komprimace i dekomprimace těchto metod má stejnou složitost.
		\item \textbf{Asymetrická kompresní metoda} -- Komprimace i dekomprimace těchto metod má odlišnou složitost.
		\item \textbf{Entropie} -- míra neurčitosti systému
	\end{itemize}	

\end{introduction}
	
\chapter{Cíl práce}
Cílem této bakalářské práce je seznámení se s~kompresními algoritmy LZ77, LZ78, LZW a~jejich následná implementace do knihovny SCT (Small Compression Toolkit). Součástí této práce je také jejich následné otestování z~hlediska časové a~paměťové náročnosti, případně porovnání efektivity se stávajícími kompresními algoritmy v~knihovně. Tato knihovna by pak měla ušetřit spoustu práce programátorům, kteří díky ní nebudou muset vymýšlet vlastní implementaci těchto algoritmů.

\section{Existující řešení}
Jedná se o~poměrně zastaralé metody, které vznikli v~80.~letech 20.~století a~dnes je již v~efektivitě překonávají jiné. Nicméně metodu LZ77 je možné použít například v programu CRUSH.\cite{crush} Z LZ77 bylo odvozeno mnoho dalších kompresních algoritmů, které jsou velmi často používány. LZ78 byla taktéž předlohou pro jiné algoritmy například LZW nebo BTLZ. \cite{inspire} Samotná se příliš nevyužívá, protože nedosahuje tak dobrého komprimačního poměru. Naopak metoda LZW bývala poměrně hojně využívá při komprimaci monochromatických obrázků a~obecně je velmi efektivní tam, kde se opakuje velké množství dat. LZW se používala v~dřívějších verzích formátu PDF, později byla nahrazená efektivnějším algoritmem.\cite{lzwEN} Tyto algoritmy patří mezi základní kompresní metody a~jelikož je hlavním cílem SCT poskytnout co největší spektrum kompresních algoritmů, rozhodl jsem se je do knihovny doimplementovat.




\section{Analýza požadavků}
V této části bych se rád věnoval požadavkům, které mi zadal vedoucí práce a jejich analýze. Každý softwarový projekt by měl začínat analýzou toho, co si zákazník přeje, aby program dělal nebo dodržoval. Požadavek musí být proveditelný, testovatelný a také musí byt definovaný dostatečné detailně pro účely návrhu.\cite{req} Obvykle se požadavky dělí na funkční a nefunkční.

\subsection{Funkční požadavky}
 Funkční požadavky objasňují, jaké má mít požadovaný software funkce, co má umět a jak se má chovat v daných situacích. Podle zadání a diskuse s vedoucím práce jsem určil tyto požadavky jako funkční:
 
\begin{itemize}
	\item \textbf{Dekomprese} -- Kromě výchozí funkce komprese bude možné program spustit v režimu dekomprese.
	\item \textbf{Parametry} -- Algoritmům bude možné změnit jejich parametry, jako je například veliskost bufferů, počet prvků ve slovníku nebo jak velké části souboru se budou komprimovat najednou. Parametry bude možné měnit při spoštění přes příkazovou řádku pomocí parametrů a nebo
	\item \textbf{Komprimace po částech} -- Algoritmy budou schopné rozdělit soubor na menší části a ty pak komprimovat. Dekomprimace bude probíhat nad celým souborem ne po častech, nicméně algoritmy budou schopny dekomprimovat i soubor, který byl předtím zakomprimován po částech. Velikost částí si určí uživatel pomocí parametru, případně využije výchozích nastavených hodnot.
	\item \textbf{Spustitelnost} -- Program bude možné pustit se zvolenými parametry z příkazové řadky. Nepůjde tedy pouze o implementaci metod do knihovny, ale i o jejich inteface, který bude možné prakticky využít.
	
 		
\end{itemize}		
 
\subsection{Nefunkční požadavky}
Nefunkční požadavky, jsou takové požadavky, které přímo neříkají, co má program umět. Jedná se převážně o omezující nebo zpřesňující podmínky, kterými se má návrh řídit. Tyto požadavky jsem určil jako nefunkční:

\begin{itemize}
	\item \textbf{Triplety} -- Program bude pro ukládání dat do souboru využívat již naimplementované metody třídy TripletProcesor a pro definování jednotlivých složek v tripletu třídu TripletFieldId.
	\item \textbf{Velikostí slovníků a bufferů} -- Aby nedocházelo k nekontrolovatelnému růstu slovníku a tím i k zvyšování pamětové i časové náročnosti, budou mít algorytmy LZ78 a LZW parametr, který zhora omezí počet prvků ve slovníku. U metody LZ77 bude pomocí dvou parametrů omezen vyhledávací buffer i tzv. look ahead buffer.
	\item \textbf{Velikosti prvků v tripletu} -- Toto omezení vychází z implementace třídy TripletProcessor, která dokáže zpracovávat pouze čísla, která je možné zapsat pomocí 28 bitů, tedy maximálně číslo 134 217 728.
	\item \textbf{Jazyk Java} -- Program bude napsán v jave, protože celá knihovna SCT je v tomto jazyce napsána.
	\item \textbf{Řetězení} -- Implementace metody bude dodržovat myšlenku řetězení, tedy že různé kompresní metody bude možné za sebe zapojovat a docílit tak několikanásobné komprese.  
	
\end{itemize}

\subsection{Výjimky} ??

\chapter{Analýza algoritmů}
V této kapitole pojednávám přímo o jednotlivých metodách. Na jakém principu fungují a v čem se liší. Také zde určím jejich asymptotickou složitost, náročnost na pamět, výhody a nevýhody.
\section{LZ77}
\subsection{Popis}
Tento kompresní algoritmus vynalezli v roce 1977 pánové Abraham Lempel a Jakob Ziv. Jedná se o slovníkovou, jednoprůchodovou, adaptivní a bezztrátovou metodu. Je založena na principu klouzavého okénka (floating window), které si lze představit jako vyříznutou část dat. Při běhu programu se okénko posouvá v datech pouze jedním směrem a to od začátku do konce. Je rozděleno na dvě části - prohlížecí okénko (search buffer) a aktuální okénko (look-ahead buffer). Jejich velikost je velmi zásadní pro efektivitu celého algoritmu. V zásadě se dá řict, že velikost prohlížecího okénka musí být vždy větší nebo rovna velikosti aktuálního okénka, nicméně v praxi je prohlížecí okénko obvykle tisíci násobně větší než aktuální.\cite{vohoLZ77}
\subsection{Komprese}
Hlavní myšlenka tohoto algoritmu spočívá v nalenzení co nejdelšího prefixu z nezakódovaných dat, který je zároveň obsažený i v prohlížecím okénku. Tato metoda umožňuje menší vylepšení efektivity, pokud se při vyhledávání neomezím pouze na prohlížecí okénko, ale jen na podmínku, že prefix v něm musí začínat. V některých případech je možné nalezt nejdelší prefix, který přesahuje do aktualního okénka. Je tedy možné zakódovat větší část dat. Slovník této metody je tvořen všemi podřetězci, které začínají v prohlížecím okénku.

Zde je jednoduchý pseudokód, který popisuje tuto metodu:

\IncMargin{1em}
\begin{algorithm}[H]
	prohlížecí a aktualní okénko jsou prázdná\;
	\While{na vstupu jsou data}{
	přidej na konec aktualního okénka symbol ze vstupu\;
	najdi prefix $p$ z aktualního okénka, který začíná v prohlížecím okénku\;
	\eIf{$p$ bylo nalezeno}{
		zapiš trojici $(i, j, k)$, kde $i$ je vzdálenost prvního znaku nalezeného $p$ od začátku aktualního okénka, $j$ je délka $p$ a $k$ je první nasledující symbol po $p$\;
		posun klouzavé okénko o $j+1$\;
	}{
		zapiš triplet $(0, 0, m)$, kde $m$ je první symbol v aktualním okénku\;
		posuň klouzavé okénko o $1$\;
	}
	}
\caption{Pseudokód komprese metody LZ77}
\end{algorithm}\DecMargin{1em}

\subsection{Složitost komprese}
Dle mého názoru je složitost tohoto algoritmu $O(nm)$, kde $ n $ je délka vstupních dat a $ m $ je velikost prohlížecího okénka.
Na první pohled není uplně zřejmé, jak jsem k tomuto závěru došel. Rozhodl jsem se tedy své tvrzení dokázat pomocí matematického důkazu. 

Mějme asymptotickou složitost znázorněnou sumou, která vyjadřuje nejvyšší možný počet operací potřebných pro komprimaci souboru:

$$O(\sum_{i=1}^{|P|} m p_{i})$$

kde $P$ je množina všech prefixů nalezených v datech, $m$ je velikost prohlížecího okénka a $p_{i}$ velikost $i$-tého prefixu z $P$.

Protože součet sumy nezáleží na $ m $, mohu ho vytknout před sumu:

$$O(m\sum_{i=1}^{|P|}p_{i})$$

Součet sumy je vždy rovný velikosti dat, protože procházím celá vstupní data a rozděluji je na podřetězce, které najdu v prohlížecím okénku. Nezáleží tedy na délce jednotlivých podřetězců, jejich součet velikostí bude vždy roven velikosti vstupních dat, tedy $ n $. Proto:

$$O(\sum_{i=1}^{|P|} m p_{i}) = O(mn)$$

Je třeba říci, že na vyhledání prefixu nebude vždy potřeba stejný počet operací, proto jsem se rozhodl použít tzv. omikron $ O $, který složitost omezuje shora.

\subsection{Dekomprese}

Algoritmus je poměrně jednoduchý. Nejdříve načte ze vstupu triplet, který v sobě obsahuje informaci o tom, odkud a jak dlouhou část prohlížecího okénka je potřeba zapsat na výstup. Poslední část tripletu je jeden byte, který je taktéž potřeba zapsat. Následně zapsaná data rovněž přidáme na konec prohlížecího okénka. Zde je jednoduchý pseudokód:\newline

\begin{algorithm}[H]
	prohlížecí okénko je prázdné\;
	\While{na vstupu jsou data}{
		ze vstupu načti triplet $ (i, j, k) $, kde $i$ je vzdálenost prvního znaku hledaného podřetězce $p$ od konce prohlížecího okénka, $j$ je délka $p$ a $k$ je první nasledující symbol po $p$\;
		
		\eIf{$i$ se rovná 0}{
			zapiš $k$\;
			pridej na konec prohlížecího okénka $k$\;
		}{
			$v:=$ aktuální velikost prohlížecího okénka\; 
			$z:= v - i$\;
			$p:=$ podřetězec prohlížecího okénka od indexu $z$ az do $z + j$\;
			$p:= p + k$\;
			zapiš $p$\;
			do prohlížecího okénka přidej $p$\;
		}
	}
	\caption{Pseudokód dekomprese metody LZ77}
\end{algorithm}

\subsection{Složitost dekomprese}
Oproti kompresi je složitost dekomprese vcelku trivialní. Řekněme, že $ t $ je počet tripletů, pro každý triplet bude potřeba $ p_{i} $ operací, při čtení podřetězce z prohlížecího okénka. To znamená, že celkový počet operací bude roven sumě všech délek podřetězců. Tato suma se však rovná velikosti puvodního nekomprimovaného souboru, tedy $ n $. Čímž dostáváme:  $$\sum_{i=1}^{t} p_{i} = n$$ z čehož plyne, že složitost dekomprese je $ \Theta(n) $. 

Složitosti komprese i dekomprese jsou rozdílné, jedná se tedy o asymetrickou metodu.


\subsection{Výhody}
\begin{itemize}
	\item \textbf{Paměť} -- Díky posuvnému okénu se zpracovává vždy pouze poměrně malá čast dat, proto neroste paměťová náročnost s velikost vstupu.  
	\item \textbf{Rychlost dekomprese} -- Při vhodně zvolené implementaci je algoritmus dekomprese lineárně závislý pouze na velikosti dat. Jeho složitost je tedy $\Theta(n)$, kde n je velikost původních dat. 
	
\end{itemize}
\subsection{Nevýhody}
\begin{itemize}
	\item \textbf{Náhodná data} -- Pokud mají data příliš velkou entropii, algoritmus dosahuje velmi špatných výsledků, často i komprimovaný soubor zvětší. To se týká například některých formátů obrázku (jpeg).   
	\item \textbf{Rychlost komprese} -- Složitost $O(nm)$ je oproti dekompresi $ m $ krát větší. To znamená, že algoritmus bude pomalejší, protože $ m $ je obvykle velké číslo v řádech tisíců až desetitisíců.
\end{itemize}

\section{LZ78}
\subsection{Popis}
Metoda byla vynalezena stejnými autory jako LZ77 v roce 1978. Z počátku se jednalo o velmi oblíbenou komprimační metodu až do doby než byly některé její časti patentovány.
Jedná se o paměťově velmi náročnou metodu. Je tomu tak hlavně kvůli obvykle velkému slovníku. Metoda totiž v původní verzi přidává do slovníku položku vždy, když zapisuje triplet, což se děje velmi často. Existuje mnoho způsobu, jak tento problém vyřešit. Například v případě vyčerpání paměti tzv. zamrazit slovník nebo smazat a vytvořit nový. Nejznámější modifikací je LZW, kterou se také zabývám v této práci.\cite{stringologyLZ78}

\subsection{Komprese}

Prvním krokem tohoto algoritmu je inicializace slovníku. Tento krok je velmi důležitý pro správný průběh dekomprese. Do slovníku se přidá slovo délky jedna za každé písmeno abecedy souboru. Abeceda je množina všech znaků, které se mohou v souboru objevit. Obvykle se používá množina všech bytů (0--255), pokud komprese kóduje po bytech. Dále nastaví řetězec $ str $ jako prázdný.

 Následující krok se opakuje pro každý byte ze vstupu. Pokud spojení řetězce $ str $ a načtený byte  $ b $ nejsou ve slovníku, na výstup se přidá triplet obsahující index $ str $ ve slovníku a $ b $. V opačném případě se na konec řetězce $ str $ přidá byte $ b $. Výstupem teto metody je posloupnost dvojic -- číslo, byte.

Zde je jednoduchý pseudokód, popisující kompresi:\newline

\begin{algorithm}[H]
	inicializace $slovníku$\;
	$str :=$ prázdný řetězec\;
	\While{na vstupu jsou data}{
		ze vstupu načti byte $ b $\;
		\eIf{$str + b$ není ve $slovníku$}{
			zapiš triplet $(i, b)$, kde $i$ je index $str$ ve $slovníku$\;
			přidej na konec $slovníku$ slovo $str + b$ \;
			$str :=$ prázdný řetězec\;
		}{
			$str = str + b$\;
		}
	}
	\caption{Pseudokód komprese metody LZ78}
\end{algorithm}


\subsection{Složitost komprese}
Tento algoritmus velmi ovlivňuje zvolená implementace. Pokud se slovník representuje pomocí datové struktůry strom, složitost je $ \Theta(n) $, kde n je velikost vstupních dat v bytech. Protože dokáže v konstantní složitosti $ \Theta(1) $ říci, jestli slovník již obsahuje daný řetězec či nikoliv. Pro každy vstupní symbol vykoná $ \Theta(1) $ operací. Pro uplnost doplním jednoduchou rovnici, která ukazuje, že v asymptotické složitosti se konstantní složitost neprojeví: $$ \Theta(1 * n ) = \Theta(n) $$


\subsection{Dekomprese}

Jak je vidět na pseudokódu níže, algoritmus je velmi jednoduchý. Prvním a zásadním krokem je inicializace slovníku přesně stejnou abecedou jako v kompresi. Následně se načte triplet ze vstupu, podle něj se najde slovo ve slovníku a zapiše do souboru společně s následujícím bytem. Tento zapsaný řetězec je přidán do slovníku na konec (přiřadí se mu nejvyšší index).\newline

\begin{algorithm}[H]
	inicializace $slovníku$\;
	\While{na vstupu jsou data}{
		ze vstupu načti triplet $ (i, b) $, kde $ i $ je index do $slovníku$ a $ b $ je následující byte\;
		$str := slovník[i]$ + $ b $\;
		zapiš $ str $\;
		přidej na konec $slovníku$ slovo $str$ \;
	}
	\caption{Pseudokód dekomprese metody LZ78}
\end{algorithm}

\subsection{Složitost dekomprese}
Jak bylo řečeno v popisu, jedná se o symetrickou metodu. Složitost je tedy stejná jako u komprese a tedy $ \Theta(n) $, kde $ n $ je velikost původních dat. Podobně jako u metody LZ77, máme sice menší vstup, nicméně je potřeba přičíst i režiji slovníku na zrekonstruováná slova. Jedná se o strom, to znamená, že metoda pro rekonstrukci slova udělá počet operací rovný délce slova. Pokud tedy všechny délky posčítáme, dostaneme $ n $.

\subsection{Výhody}
\begin{itemize}  
	\item \textbf{Rychlost komprese i dekomprese} -- Při vhodně zvolené implementaci je algoritmus komprese i dekomprese lineárně závislý pouze na velikosti dat. Jejich složitost je $\Theta(n)$, kde n je velikost původních dat.
	\item \textbf{Implementace} -- Metoda je poměrně jednoduchá na implementaci, především pak dekomprese.
	\item \textbf{Velikost tripletů} -- Do souboru se ukládají pouze dvojice -- číslo, byte.
\end{itemize}
\subsection{Nevýhody}
\begin{itemize}
	\item \textbf{Paměť} -- Paměťová náročnost roste s velikostí vstupu. Proto se při implementaci musí počítat s tím, že metoda vyčerpá přidělenou pamět a podle toho se zachovat.
	\item \textbf{Náhodná data} -- Podobně jako u metody LZ77, při velké entropii dat dochází k horší kompresi, někdy muže dojít až k zvětšení souboru.
	
	
\end{itemize}

\section{LZW}
Tuto slovníkovou, symetrickou, adaptivní a jednoprůchodovou metodu vytvořili Abraham Lempel, Jacob Ziv a Terry Welch v roce 1984. Jedná se o modifikaci metody LZ78. Hlavním rozdílem je, že algoritmu pro dekomprimaci dat stačí pouze indexy ve slovníku. Již tedy nepotřebuje následující byte jako jeho předchůdce. To má ale také za následek rychlejší růst počtu frází ve slovníku. Slovník tedy typicky bývá větší než u LZ78 a spotřebovává rychleji paměť. Je tedy potřeba s tím počítat při implementaci a tento problém vhodně vyřešit. Obecně platí, že čím větší slovník, tím lepší kompresní poměr. Algoritmus je navržen tak aby byl rychlý, nicméně ne vždy je optimální, protože neprovádí žádnou analýzu dat během komprese.\cite{stringologyLZW}

Metoda se stala velmi oblíbenou kolem roku 1987, protože poskytovala nejlepší kompresní poměr. Byla využita například ve formátu GIF, později například ve formátu PDF nebo v unixovém nástroji $ compress $. Stala se první široce využívanou kompresní metodou na počítačích.


\subsection{Komprese}

První krok algoritmu je inicializace, která probíhá totožně jako v metodě LZ78. Následně se načte byte $ b $ ze vstupu. Pokud je řetězec $ str + b $ ve slovníku, nastaví se jako $ str $. Pokud není, do souboru se zapíše triplet $ (i) $, kde $ i $ je index fráze $ str $. Do slovníku se pak přidá fráze $ str + b $ a jako nové $ str $ se nastaví $ b $. To je hlaní změna oproti metodě LZ78, která umožnuje právě vynechání nasledujícího bytu v tripletu. Tento postup se opakuje dokud jsou na vstupu data.\newline

\begin{algorithm}[H]
	inicializace $slovníku$\;
	$str :=$ prázdný řetězec\;
	\While{na vstupu jsou data}{
		ze vstupu načti byte $ b $\;
		\eIf{slovo $str + b$ není ve $slovníku$}{
			zapiš triplet $(i)$, kde $i$ je index $str$ ve $slovníku$\;
			přidej na konec $slovníku$ slovo $str + b$ \;
			$str := b$ \;
		}{
			$str = str + b$\;
		}
	}
	\caption{Pseudokód komprese metody LZW}
\end{algorithm}

\subsection{Složitost komprese}
Metoda obsahuje pouze drobné změny oproti LZ78, které nemají vliv na asymptotickou složitost. Je totožná se složitostí komprese LZ78, tedy $\Theta(n)$. Proto si dovolím vynechat její odvození.

\subsection{Dekomprese}

Princip dekomprese není na první pohle pochopitelný, protože nepřidávám do slovníku slovo tvořené právě zapisovaným řetězcem, ale tím který byl zapsaný v minulém běhu a pouze prvním znakem zapisovaného řetězce. Tato skutečnost je dána tím, že komprimace přidává do slovníku slovo délky $ n $, končící právě načteným bytem, ale do souboru zapisuje index předchozího slova délky $ n - 1  $, tedy bez načteného bytu. Dekomprimační algoritmus pak vždy ví, že do slovníku je potřeba přidat slovo z minulého běhu zakončené prvním znakem z fráze nalezené ve slovníku. 

Specialní případ nastane, pokud algoritmus ze vstupu načte triplet s indexem, který není ve slovníku. Toto číslo však může být pouze o jedna větší než nejvyšší obsazený index, jinak se jedná o chybu kompresního algoritmu. V tomto případě do slovníku vloží slovo, které bylo vloženo v předchozím kroku zakončené jeho prvním znakem.

K tomuto případu dochází, pokud bylo pro komprimaci použito slovo na posledním indexu (naposledy přidaná fráze do slovníku).

Zde je pseudokód, popisující tento algoritmus:\newline

\begin{algorithm}[H]
	inicializace $slovníku$\;
	$str :=$ prázdný řetězec\;
	\While{na vstupu jsou data}{
		ze vstupu načti triplet $ (i) $\;
		\eIf{$slovník$ obsahuje slovo s indexem $ i $}{
			$ fráze :=  slovník[i]$\;
			zapiš $fráze$\;
			přidej na konec $slovníku$ slovo $str $ + první znak $ fráze $ \;
		}{
			$fráze = str $ + první znak ze $str$\;
			zapiš $str$\;
			přidej na konec $slovníku$ slovo $fráze$\; 
		}
	$str := fráze$
	}
	\caption{Pseudokód komprese metody LZW}
\end{algorithm}

\subsection{Složitost dekomprese}
V závislosti na implementaci se muže složitost tohoto algoritmu lišit. Nicméně pokud struktůru slovníku representuje strom je možné dosáhnout složitosti $ \Theta(n) $, kde $ n $ je velikost původních dat před kompresí. Metoda LZW je navíc symetrická, takže složitost je stejná jako u komprese. Důvod proč jsem došel k této složitosti je analogický s určením složitosti dekomprese u LZ78. Nejvice kroků bude potreba udělat tam, kde se ze slovníku rekonstruuje řetězec, což odpovídá celkové velikosti dat. Ostatní operace mají zanedbatelnou režii a při určování asymptotické složitosti se zanedbávají. 

\subsection{Výhody}
\begin{itemize}  
	\item \textbf{Rychlost komprese i dekomprese} -- Stejně jako u LZ78 je hlavní výhodou rychlost. Při správné implementaci $ \Theta(n) $.
	\item \textbf{Velikost tripletů} -- Do souboru se ukládají pouze číslo. Na jeden triplet je tedy potřeba uložit nejmeně dat z metod kterými se zabývá tato práce.
	\item \textbf{Kompresní poměr} -- Na běžném anglickém textu dosahuje metoda velmi dobrých výsledků, typicky zmenší soubor zhruba o 50 %.
\end{itemize}
\subsection{Nevýhody}
\begin{itemize}
	\item \textbf{Paměť} -- Paměťová náročnost roste s velikostí vstupu. Roste dokonce rychleji než u LZ78. Proto se při implementaci musí počítat s tím, že metoda vyčerpá přidělenou pamět.
	\item \textbf{Náhodná data} -- Podobně jako u předchozích metod, dochází při velké entropii dat k horší kompresi. Může dojít až k zvětšení souboru.
	
	
\end{itemize}

\section{Závěr analýzy}
Hlavním přínosem těchto metod je samotný princip na jakém fungují. Především pak LZ77 z které vznikla řada algoritmů, které se dnes využívají v praxi. Největší nevýhodou těchto algoritmů je špatný kompresní poměr na datech, ve kterých se neopakují delší řetězce znaků. Další nevýhodou LZ78 a LZW je velká náročnost na paměť. Jejich kompresní poměr je obvykle závislí na velikosti slovníku. Nicméně i přes tyto nevýhody se LZW v některých programech používá dodnes, protože je velmi rychlá a vhodná na klasický text.


\chapter{Implementace}
V této kapitole popisuji realizaci metod přímo v knihovně SCT. Zásadní věcí v implementaci těchto metod je reprezentace slovníků, ať už u komprese nebo dekomprese. Efektivní přidávání a hledání ve slovníku je zásadní pro efektivitu techto metod. Vysvětluji jaké parametry bude moci nastavit uživatel a jak ovlivní efektivitu nebo paměťovou náročnost. Jakým způsobem metody načítají data ze vstupu a jak zapisují triplety do souboru.  

\section{Řetězení}
V knihovně se používá princip řetězení (chaining). To znamená, že některé metody se dají zapojovat za sebe. Zde je ukázka kódu:
\begin{lstlisting}
ChainBuilder.create(io::openParse)
.chain(lz77::compress)
.chain(t2b)
.end(bytes -&gt; io.saveObject(bytes, f2.toPath()))
.accept(f1.toPath());
\end{lstlisting}

Význam kódu není zatím relevantní, nicméně představuje použití komprese. Důležitý je princip, podle kterého je naimplementovaná metoda \classname{compress}. Hlavní podmínkou pro chainování jsou dva parametry metody. První je vstupní parametr a druhý výstupní. Respektive druhým parametrem je objekt consumer, který má za parametr datový typ výstupu. Ukázka kódu:
\begin{lstlisting}
public void compress(ByteBuffer byteBuffer,
Consumer&lt;TripletSupplier&gt; tripletSupplierConsumer) {...}
\end{lstlisting}

\newpage

Další možností je implementovat celý objekt tak, aby mohl být řetězený, pomocí implementace rozhraní \classname{Chainable&lt;x, y&gt;}, kde x je datový typ vstupního parametru a y datový typ pro výstup. Zde je hlavička třídy, kterou je možné řetězit:

\begin{lstlisting}
public abstract class TripletToByteConverter&lt;T&gt; 
   implements Chainable&lt;TripletSupplier, List&lt;byte[]&gt;&gt;
	{...}
\end{lstlisting}

Zde je vstupní parametr typu \classname{ByteBuffer}, to znamená že předchozí metoda v řetězci musela mít druhý parametr typu \classname{Consumer&lt;ByteBuffer&gt;}. Vystupním parametrem je \classname{TripletSuplier}, následující metoda tedy musí mít první parametr \classname{TripletSuplier}. V tomto připadě je to objekt \classname{t2b}, který je instancí třídy \classname{TripletToByteConverter}, která implementuje rozhraní \newline \classname{Chainable&lt;TripletSupplier, List&lt;byte[]&gt;&gt;}.

Uvnitř metody se pak volá třídní metoda consumera \classname{accept}, která posílá data do další metody. Objekt musí mít vždy implementovanou metodu \classname{setConsumer}, která nastavý consumera. Ten funguje stejne jako u metody.

\section{Načítání a zápis do souboru}
Načítání ze souboru se u komprese a dekomprese liší. Zatím co v prvním případě, načítám ze souboru  byty u dekomprese se jedná o načtení celého objektu ze souboru. Je tomu tak proto, že po kompresi se ukládá celé pole bytů jako objekt, aby bylo možné správně načítat uložené triplety. Dekomprese ukládá data do souboru po bytech, aby byl soubor stejný jako před komprimací.

O vše se stará třída \classname{FileIO}, která má jako parametr velikost, podle které rozděluje načítaný soubor na menší části a ty se následně zkomprimují a v podobě tripletů uloží do souboru. Zkomprimované soubory se načítají i dekomprimují jako celek, aby nedocházelo k narušení slovníků. Mohlo by se stát, že načteme menší nebo větší část tripletů než jaká byla vytvořena z jedné části pri kompresi. Takový případ by pak vedl k sestavení špatného slovníku, což by vedlo k chybám v dekompresi.

\section{Triplety}

Nedílnou součástí všech implementovaných kompresních metod jsou triplety. Jejich spracování a následný zápis je sprostředkován pomocí rozhraní\newline \classname{TripletSupplier}, které se vždy používá jen pro vytvoření anonymní vnitřní trídy, která implementuje metodu visit, jejímž parametrem je \classname{TripletProcessor}. Ten dokáže pomocí metody \classname{write} zapsat jednotlivé části tripletu do souboru.\newpage Zde je ukázka kódu:



\begin{lstlisting}
output.accept((TripletSupplier) new TripletSupplier() {
	@Override
	public void visit(TripletProcessor visitor) {
	visitor.write(nodeFieldId, num);
	visitor.write(characterFieldId, b);
	}
});
\end{lstlisting}

Metoda \classname{write} má dva parametry. Druhým parametrem je je hodnota zapisované složky tripletu. Prvním je objekt reprezentovaný třídou \classname{TripletFieldId}, který popisuje, kolik bitů má zapisovaná hodnota. \classname{TripletProcessor} se postará o správný zápis bitů. To je velká výhoda, protože u všech metod vždy dopředu vím, jaké největší číslo může být zapsáno.

 Například, pokud metoda pracuje se slovníkem, jehož maximálně počet frází je $ 2^{10}$. Není potřeba zapisovat indexy jako integer, který má 32 bitů. Pro zapsání jakéhokoliv indexu je potřeba nejvýše 11 bitů, zbytek jsou redundantní data. Tento postup v tomto případě ušetří na každém tripletu 22 bitů, což je velká úspora paměti, která výrazně zlepší kompresní poměr. Samotná implementace třídy \classname{TripletFieldId} umožnuje zapsat pouze číslo o velikosti 28 bitů. Tato skutečnost ale není omezující, protože slovník s takovým počtem frází ($ 2^{29} $ a více) by byl velmi neefektivní.

\section{Parametry}
Každá metoda má třídu s názvem \classname{&lt;jméno metody&gt;ProviderParametrs.java}. Tato třída reprezentuje všechny nastavitelné parametry, které ovlivňují kompresi i dekompresi. Metodě se předává jako jediný parametr v konstruktoru. Tyto parametry jsou podrobně popsány dále v realizaci metod.

\section{Spustitelný klient}
Každá metoda má třídu s názvem \classname{&lt;jméno metody&gt;Client.java}. Tato třída obsahuje funkci $main$, to znamená, že je možné jí spustit. Jedná se o spustitelný program z příkazové řádky, který komprimuje popřípadě dekomprimuje soubor podle zvoleného přepínače a parametrů. Jednotlivé klienty a jejich použití popisuji pro každou metodu zvlášť v kapitolách realizace. Klient je zatím spustitelný příkazem: \newline
 java -jar target/sct-RELEASE.jar &lt;jméno vstupního souboru&gt;\newline	&lt;jméno vytvořeného souboru&gt; &lt;případné přepínače&gt;\newline Při změně klienta je potřeba v souboru pom.xml změnit cestu k správné $main$ funkci, kterou chcete spustit a následně zkompilovat projekt.\newpage

\section{Realizace LZ77}
\subsection{Struktůra adresáře}

Na schématu adresářové struktůry můžete vidět třídu LZ77, která inicializuje kodér \classname{LZ77TripletCoder}. Ten se stará o kompresi i dekompresi a zápis tripletů. Podrobnější popis tříd a jejich funkcí naleznete dále v textu.

\begin{figure}[H]
	\dirtree{%
		.1 .
		.1 lz77.
		.2 LZ77.java.\DTcomment{třída volající metody komprese nebo dekomprese}.
		.2 LZ77Client.java.\DTcomment{klient, spustitelný přes příkazovou řádku}.
		.2 LZ77ProviderParameters.java\DTcomment{třída obsahující všechny parametry}.
		.2 LZ77Test.java\DTcomment{testovací třída}.
		.1 triplet.
		.2 coder.
		.3 LZ77TripletCoder.java\DTcomment{Třída zrpacující vstup při kompresi i dekompresi, dědí ze třídy \classname{TripletCoder}}.		
	}
	\renewcommand\figurename{Struktura}
	\caption{adresářová struktura metody LZ77}
	\label{dirtree}
\end{figure}

\subsection{Parametry}

Pro metodu LZ77 jsou důležité tři parametry. Velikost prohlížecího okna, aktualního okna a počet bytů, který udává po jak velkých částech se bude soubor komprimovat. 
Velikost prohlížecího i aktualního okna se zadává jako exponent $ e $. Následně se pak velikost oken vypočte jako $ 2^{e-1} $, $ e $ je pak nejvyšší počet bitů, potřebný pro zapsání jakehokoliv indexů do tripletu. Velikost třetího parametru se pak pohybuje obvykle od statisíců do desítek milionů (0,1 - 10 mb), nicméně není vyloučeno použít i jiné hodnoty. Na jak velké části soubor rozdělíte, ovlivní výslednou komprimaci. Jako parametr prohlížecího okna se obvykle volí čísla od 11 do 16, tj. velikost okna 1024 -- 32768 znaků. Aktualní okno je typicky mnohem menší, obvykle 64 -- 256 znaků, tomu odpovídá parametr od 7 do 9. 

\subsection{Hlavička souboru}

Na začátku komprese se uloží jako první specialní triplet, který v této práci označuji jako hlavička souboru. V případě LZ77 obsahuje informaci o tom jaký byl exponent velikosti prohlížecího a aktualního okna. Tato informace je důležitá pro načítání dat z tripletů. Udává kolik bitů je potreba prečíst abychom získali uložené číslo. Bez této hlavičky by nebylo možné soubor dekomprimovat.


 
\subsection{Implementace komprese}

Ve třídě \classname{LZ77} je implementována metoda \classname{compress}, která zajišťuje správný průběh komprese. Vstupním parametrem je \classname{byteBuffer}, což je instance třídy \classname{ByteBuffer}. Pokud je vstupní parametr null, je to signál, že je komprese u konce. Na úplném začátku algoritmu se do souboru zapíše hlavička.

Pomocí \classname{byteBufferu} a parametrů se inicializuje instance třídy \linebreak \classname{LZ77TripletCoder}, která se stará o samotnou kompresi a zápis tripletů. Komprese souboru se volá pomocí metody \classname{encode}, ta má jako parametr konsumera, do kterého se zapisují triplety.

Většina předchozího textu byla jen popis použití nástrojů knihovny, který je u všech metod podobný, proto ho dále nebudu popisovat. Z hlediska implementace je nejduležitější metoda \classname{encode}, která zajištuje celý algoritmus komprese LZ77. 

Protože načítání vstupů funguje tak, že program načte celou část souboru po bytech do pole, rozhodl jsem se toho využít při implementaci. Místo pole do kterého bych byty přidával a odebíral tak aby vždy odpovídali rozměrům prohlížecího a aktualního okénka, jsem se rozhodl, použít pouze indexy v poli bytů.

První index ukazuje, kde v poli začíná prohlížecí okno. Druhý, kde je začátek aktualního a tedy i konec prohlížecího okna a třetí, kde je konec aktualního okna. Těmito indexy si tedy ohraničím klouzavé okénko. To se pak velmi snadno posouvá daty. Vždy jen ke všem indexům přičtu velikost zakódovaného řetězce.

Na počátku algorimu jsou první dva indexy rovny nule, druhý se pak s prvním komprimovaným řetězcem začne zvětšovat. Index který ukazuje na začátek se začne měnit až když prohlížecí okénko odpovídá požadované velikosti. Poslední index, který ukazuje na konec aktualního okénka, je na začátku rovný požadované velikosti, v pruběhu komprimace se zvětšuje dokud nenarazí na konec pole.

Samotné hledání prefixu z aktualního okénka probíhá tak, že vezmu první byte v aktualním okénku a pokusím se ho najít v prohlížecím okénku. Pokud je nalezena shoda, vezmu následující byte z aktualního okénka a porovnám ho s následujícím bytem v prohlížecím okénku. Pokud jsou schodné, postup opakuji, pokud ne, zapamatuji si toto slovo jako kandidáta na nejdelší prefix. A celý postup opakuji dokud neprojdu celé prohlížecí okno a nenjdu největší možný prefix. Prohlížecí okno se postupně zmenšuje díky skutečnosti, že v poli před kandidátem na nejdelší slovo žádné delší nemůže být, protože vím, že nejdelší nalezený prefix v předchozí časti je právě kandidát. Aktualní okénko je stejné, jako na začátku, mění se až po nalezení nejdelšího prefixu.\pagebreak

Po nalezení nejelšího prefixu se všechny indexy posunou o jeho velikost zvětšenou o 1 a do konsumera se uloží triplet, který je tvořen třemi složkami, $$ (i, j, b) $$ Kde $i$ je vzdálenost začátku slova od počátku aktualního okénka. Na zapsání je potřeba počet bitů, který udává první parametr zadaný uživatelem. Další složkou je číslo $j$, což je délka prefixu. Počet bitů pro zápis čísla $j$ udává druhý parametr. Poslední složkou je byte $b$, následující po nalezeném prefixu v aktualním okně. Ten má vždy 8 bitů.

Po kompresi se všechny triplety převedou pomocí třídy \linebreak \classname{TripletToByteConverter} na byty a následně zapíšou do souboru.

\subsection{Implementace dekomprese}

Uvnitř třídy \classname{LZ77TripletCoder} je naimplementovaná metoda \classname{decode}, která má jako vstupní parametr input třídy \classname{TripletProcessor}, což je výsledek zpracování vstupu třídou \classname{ByteToTripletConverter}, která převádí vstupní byty na triplety.

Implementace dekomprese je velmi jednoduchá. Na začátku si inicializuji proměnou builder třídy  ByteBuilder, čož je třída obalující pole bytů a poskytující některé vhodné metody pro práci s polem, především pak metodu appent, která přidá byte na konec pole.

Následující krok se opakuje dokud jsou v proměnné $input$ triplety. Načtu triplet $ (i, j, b) $. Pokud jsou první i druhá složka rovny nule, pak na konec $builderu$ přidám byte $b$ z třetí složky tripletu, jinak najdu byte vzdálený $i$ bytů od konce $builderu$. Od tohoto bytu přidám $j$ znaků na konec $builderu$. Sekvence zapidaných bytů odpovídá zakódovanému prefix. Na konec $builderu$ přidám byte $b$ z tripletu.

Pokud dojdou triplety, $input$ vratí číslo $ -1 $ v následující načtené složce tripletu. 

V případě této metody není problém dekódovat i triplety, které vznikli z jiné části při kompresi. Díky skutečnosti, že při dekódování tripletu mám k dispozici vždy celé pole předchozích bytů, vždy správně najdu index od kterého kopírovat data. To je také duvod, proč nemusí být v hlavičce velikost částí na rozdíl od dalších dvou metod.

Po dekomprimaci se pole bytů zapíše do souboru. Nový soubor je totožný s tím před komprimací.\pagebreak


\subsection{Spustitelný klient}

Klient metody LZ77 může být spuštěn s těmito přepínači:

\begin{itemize}  
	\item \textbf{-h} -- vypíše nápovědu
	\item \textbf{-d} -- spustí dekompresi vstupního souboru, s tímto přepínačem není potřeba zadávat další parametry, program je bude ignorovat, protože si načte hlavičku, ve které jsou všechny potřebné parametry
	\item \textbf{-sb &lt;$e$&gt;} -- tento přepínač říká, jak velké prohlížecí okénko bude použito ($ 2^{e-1} $)
	\item \textbf{-lb &lt;$e$&gt;} -- tento přepínač říká, jak velké aktualní okénko bude použito ($ 2^{e-1} $)
	\item \textbf{-p &lt;$n$&gt;} -- tento přepínač říká, po jak velkých částech bude probíhat komprese (v bytech)
\end{itemize}
Poznámka: Pokud  při kompresi některý z posledních tří přepínačů chybí nebo je v některém chyba, metoda se zavolá s výchozími parametry, které jsou: -sb  $14 $ -lb  $8 $ -p $ 1 000 000 $.\newpage

\section{Realizace LZ78}

\subsection{Struktůra adresáře}

Na schématu adresářové struktůry můžete vidět třídu LZ78, která inicializuje kodér \classname{LZ78TripletCoder}. Ten se stará o kompresi i dekompresi a zápis tripletů. Podrobnější popis tříd a jejich funkcí naleznete dále v textu.

\begin{figure}[H]
	\dirtree{%
		.1 .
		.1 lz78.
		.2 dictionary.
		.3 DecodeDictionary.java.\DTcomment{interface pro dekompresi}.
		.3 DecodeList.java.\DTcomment{slovník dekomprese pomocí seznamu}.
		.3 DecodeTree.java.\DTcomment{slovník dekomprese pomocí stromu}.
		.3 DecodeNode.java.\DTcomment{třída reprezentující frázi/uzel pro dekomprese}.
		.3 EncodeDictionary.java.\DTcomment{slovník komprese}.
		.3 EncodeNode.java.\DTcomment{třída reprezentující uzel komprese}.
		.2 LZ78.java.\DTcomment{třída volající metody komprese nebo dekomprese}.
		.2 LZ78Client.java.\DTcomment{klient, spustitelný přes příkazovou řádku}.
		.2 LZ78ProviderParameters.java\DTcomment{třída obsahující všechny}.
		.2 LZ78Test.java\DTcomment{testovací třída}.
		.1 triplet.
		.2 coder.
		.3 LZ78TripletCoder.java\DTcomment{Třída zrpacující vstup při kompresi i dekompresi, dědí ze třídy \classname{TripletCoder}}.		
	}
	\renewcommand\figurename{Struktura}
	\caption{adresářová struktura metody LZ78}
	\label{dirtree}
\end{figure}

\subsection{Parametry}

\subsection{Hlavička souboru}

\subsection{Implementace komprese}

\subsection{Implementace dekomprese}

\subsection{Spustitelný klient}

Klient metody LZ78 může být spuštěn s těmito přepínači:

\begin{itemize}  
	\item \textbf{-h} -- vypíše nápovědu
	\item \textbf{-d} -- spustí dekompresi vstupního souboru, s tímto přepínačem není potřeba zadávat další parametry, program je bude ignorovat, protože si načte hlavičku, ve které jsou všechny potřebné parametry
	\item \textbf{-nc &lt;$e$&gt;} -- tento přepínač říká, kolik maximálně frází muže být ve slovníku ($ 2^{e-1} $)
	\item \textbf{-p &lt;$n$&gt;} -- tento přepínač říká, po jak velkých částech bude probíhat komprese (v bytech)
\end{itemize}
Poznámka: Pokud  při kompresi některý z posledních dvou přepínačů chybí nebo je v některém chyba, metoda se zavolá s výchozími parametry, které jsou: -nc  $17 $ -p $ 1 000 000 $.\newpage

\section{Realizace LZW}

\subsection{Struktůra adresáře}

Na schématu adresářové struktůry můžete vidět třídu LZW, která inicializuje kodér \classname{LZWTripletCoder}. Ten se stará o kompresi i dekompresi a zápis tripletů. Metoda LZW využívá pro reprezentaci uzlů ve slovníku stejné třídy jako LZ78. Podrobnější popis tříd a jejich funkcí naleznete dále v textu.

\begin{figure}[H]
	\dirtree{%
		.1 .
		.1 lz78.
		.2 dictionary.
		.3 DecodeNode.\DTcomment{třída reprezentující frázi/uzel pro dekomprese}.
		.3 EncodeNode.\DTcomment{třída reprezentující frázi/uzel pro kompresi}.
		.1 lzW.
		.2 dictionary.
		.3 DecodeDictionary.java.\DTcomment{slovník dekomprese}.
		.3 EncodeDictionary.java.\DTcomment{slovník komprese}.
		.2 LZW.java.\DTcomment{třída volající metody komprese nebo dekomprese}.
		.2 LZWClient.java.\DTcomment{klient, spustitelný přes příkazovou řádku}.
		.2 LZWProviderParameters.java\DTcomment{třída obsahující všechny parametry}.
		.2 LZWTest.java\DTcomment{testovací třída}.
		.1 triplet.
		.2 coder.
		.3 LZWTripletCoder.java\DTcomment{Třída zrpacující vstup při kompresi i dekompresi, dědí ze třídy \classname{TripletCoder}}.		
	}
	\renewcommand\figurename{Struktura}
	\caption{adresářová struktura metody LZW}
	\label{dirtree}
\end{figure}

\subsection{Parametry}

\subsection{Hlavička souboru}

\subsection{Implementace komprese}

\subsection{Implementace dekomprese}

\subsection{Spustitelný klient}

Klient metody LZW může být spuštěn s těmito přepínači:

\begin{itemize}  
	\item \textbf{-h} -- vypíše nápovědu
	\item \textbf{-d} -- spustí dekompresi vstupního souboru, s tímto přepínačem není potřeba zadávat další parametry, program je bude ignorovat, protože si načte hlavičku, ve které jsou všechny potřebné parametry
	\item \textbf{-nc &lt;$e$&gt;} -- tento přepínač říká, kolik maximálně frází muže být ve slovníku ($ 2^{e-1} $)
	\item \textbf{-p &lt;$n$&gt;} -- tento přepínač říká, po jak velkých částech bude probíhat komprese (v bytech)
\end{itemize}
Poznámka: Pokud  při kompresi některý z posledních dvou přepínačů chybí nebo je v některém chyba, metoda se zavolá s výchozími parametry, které jsou: -nc  $17 $ -p $ 1 000 000 $.\newpage

\chapter{Testovaní}

\begin{conclusion}
	%sem napište závěr Vaší práce
\end{conclusion}

\bibliographystyle{csn690}
\begin{thebibliography}{1}

\bibitem{komprese}
\textit{Komprese dat} [online]. [cit. 2018-04-22]. Wikipedie. Dostupné z:  \url{https://cs.wikipedia.org/wiki/Komprese_dat}

\bibitem{crush}
\textit{Crush} [online]. [cit. 2018-04-23]. Dostupné z: \url{https://sourceforge.net/projects/crush}

\bibitem{inspire}
\textit{LZ77 and LZ78} [online]. [cit. 2018-04-23]. Wikipedie. Dostupné z: \url{https://en.wikipedia.org/wiki/LZ77_and_LZ78}

\bibitem{lzwEN}
\textit{LZW} [online]. [cit. 2018-04-23]. Dostupné z: \url{https://www.prepressure.com/library/compression-algorithm/lzw}

\bibitem{req}
\textit{Analýza požadavků} [online]. [cit. 2018-04-23]. Wikipedie. Dostupné z: \url{https://cs.wikipedia.org/wiki/Anal\%C3\%BDza_po\%C5\%BEadavk\%C5\%AF}

\bibitem{vohoLZ77}
\textit{Algoritmus LZ77} [online]. [cit. 2018-04-25]. Dostupné z: \url{http://voho.eu/wiki/algoritmus-lz77/}

\bibitem{stringologyLZ78}
\textit{Algoritmus LZ78} [online]. [cit. 2018-04-28]. Dostupné z: \url{http://stringology.org/DataCompression/lz78/index_cs.html}

\bibitem{stringologyLZW}
\textit{Algoritmus LZW} [online]. [cit. 2018-04-28]. Dostupné z: \url{http://www.stringology.org/DataCompression/lzw-e/index_cs.html}

\end{thebibliography}

\appendix

\chapter{Seznam použitých zkratek}
% \printglossaries
\begin{description}
	\item[SCT] Small compression toolkit
	\item[LZ] Lempel-ziv
\end{description}


% % % % % % % % % % % % % % % % % % % % % % % % % % % % 
% % Tuto kapitolu z výsledné práce ODSTRAŇTE.
% % % % % % % % % % % % % % % % % % % % % % % % % % % % 
% 
% \chapter{Návod k~použití této šablony}
% 
% Tento dokument slouží jako základ pro napsání závěrečné práce na Fakultě informačních technologií ČVUT v~Praze.
% 
% \section{Výběr základu}
% 
% Vyberte si šablonu podle druhu práce (bakalářská, diplomová), jazyka (čeština, angličtina) a kódování (ASCII, \mbox{UTF-8}, \mbox{ISO-8859-2} neboli latin2 a nebo \mbox{Windows-1250}). 
% 
% V~české variantě naleznete šablony v~souborech pojmenovaných ve formátu práce\_kódování.tex. Typ může být:
% \begin{description}
% 	\item[BP] bakalářská práce,
% 	\item[DP] diplomová (magisterská) práce.
% \end{description}
% Kódování, ve kterém chcete psát, může být:
% \begin{description}
% 	\item[UTF-8] kódování Unicode,
% 	\item[ISO-8859-2] latin2,
% 	\item[Windows-1250] znaková sada 1250 Windows.
% \end{description}
% V~případě nejistoty ohledně kódování doporučujeme následující postup:
% \begin{enumerate}
% 	\item Otevřete šablony pro kódování UTF-8 v~editoru prostého textu, který chcete pro psaní práce použít -- pokud můžete texty s~diakritikou normálně přečíst, použijte tuto šablonu.
% 	\item V~opačném případě postupujte dále podle toho, jaký operační systém používáte:
% 	\begin{itemize}
% 		\item v~případě Windows použijte šablonu pro kódování \mbox{Windows-1250},
% 		\item jinak zkuste použít šablonu pro kódování \mbox{ISO-8859-2}.
% 	\end{itemize}
% \end{enumerate}
% 
% 
% V~anglické variantě jsou šablony pojmenované podle typu práce, možnosti jsou:
% \begin{description}
% 	\item[bachelors] bakalářská práce,
% 	\item[masters] diplomová (magisterská) práce.
% \end{description}
% 
% \section{Použití šablony}
% 
% Šablona je určena pro zpracování systémem \LaTeXe{}. Text je možné psát v~textovém editoru jako prostý text, lze však také využít specializovaný editor pro \LaTeX{}, např. Kile.
% 
% Pro získání tisknutelného výstupu z~takto vytvořeného souboru použijte příkaz \verb|pdflatex|, kterému předáte cestu k~souboru jako parametr. Vhodný editor pro \LaTeX{} toto udělá za Vás. \verb|pdfcslatex| ani \verb|cslatex| \emph{nebudou} s~těmito šablonami fungovat.
% 
% Více informací o~použití systému \LaTeX{} najdete např. v~\cite{wikilatex}.
% 
% \subsection{Typografie}
% 
% Při psaní dodržujte typografické konvence zvoleného jazyka. České \uv{uvozovky} zapisujte použitím příkazu \verb|\uv|, kterému v~parametru předáte text, jenž má být v~uvozovkách. Anglické otevírací uvozovky se v~\LaTeX{}u zadávají jako dva zpětné apostrofy, uzavírací uvozovky jako dva apostrofy. Často chybně uváděný symbol "{} (palce) nemá s~uvozovkami nic společného.
% 
% Dále je třeba zabránit zalomení řádky mezi některými slovy, v~češtině např. za jednopísmennými předložkami a spojkami (vyjma \uv{a}). To docílíte vložením pružné nezalomitelné mezery -- znakem \texttt{\textasciitilde}. V~tomto případě to není třeba dělat ručně, lze použít program \verb|vlna|.
% 
% Více o~typografii viz \cite{kobltypo}.
% 
% \subsection{Obrázky}
% 
% Pro umožnění vkládání obrázků je vhodné použít balíček \verb|graphicx|, samotné vložení se provede příkazem \verb|\includegraphics|. Takto je možné vkládat obrázky ve formátu PDF, PNG a JPEG jestliže používáte pdf\LaTeX{} nebo ve formátu EPS jestliže používáte \LaTeX{}. Doporučujeme preferovat vektorové obrázky před rastrovými (vyjma fotografií).
% 
% \subsubsection{Získání vhodného formátu}
% 
% Pro získání vektorových formátů PDF nebo EPS z~jiných lze použít některý z~vektorových grafických editorů. Pro převod rastrového obrázku na vektorový lze použít rasterizaci, kterou mnohé editory zvládají (např. Inkscape). Pro konverze lze použít též nástroje pro dávkové zpracování běžně dodávané s~\LaTeX{}em, např. \verb|epstopdf|.
% 
% \subsubsection{Plovoucí prostředí}
% 
% Příkazem \verb|\includegraphics| lze obrázky vkládat přímo, doporučujeme však použít plovoucí prostředí, konkrétně \verb|figure|. Například obrázek \ref{fig:float} byl vložen tímto způsobem. Vůbec přitom nevadí, když je obrázek umístěn jinde, než bylo původně zamýšleno -- je tomu tak hlavně kvůli dodržení typografických konvencí. Namísto vynucování konkrétní pozice obrázku doporučujeme používat odkazování z~textu (dvojice příkazů \verb|\label| a \verb|\ref|).
% 
% \begin{figure}\centering
% 	\includegraphics[width=0.5\textwidth, angle=30]{cvut-logo-bw}
% 	\caption[Příklad obrázku]{Ukázkový obrázek v~plovoucím prostředí}\label{fig:float}
% \end{figure}
% 
% \subsubsection{Verze obrázků}
% 
% % Gnuplot BW i barevně
% Může se hodit mít více verzí stejného obrázku, např. pro barevný či černobílý tisk a nebo pro prezentaci. S~pomocí některých nástrojů na generování grafiky je to snadné.
% 
% Máte-li například graf vytvořený v programu Gnuplot, můžete jeho černobílou variantu (viz obr. \ref{fig:gnuplot-bw}) vytvořit parametrem \verb|monochrome dashed| příkazu \verb|set term|. Barevnou variantu (viz obr. \ref{fig:gnuplot-col}) vhodnou na prezentace lze vytvořit parametrem \verb|colour solid|.
% 
% \begin{figure}\centering
% 	\includegraphics{gnuplot-bw}
% 	\caption{Černobílá varianta obrázku generovaného programem Gnuplot}\label{fig:gnuplot-bw}
% \end{figure}
% 
% \begin{figure}\centering
% 	\includegraphics{gnuplot-col}
% 	\caption{Barevná varianta obrázku generovaného programem Gnuplot}\label{fig:gnuplot-col}
% \end{figure}
% 
% 
% \subsection{Tabulky}
% 
% Tabulky lze zadávat různě, např. v~prostředí \verb|tabular|, avšak pro jejich vkládání platí to samé, co pro obrázky -- použijte plovoucí prostředí, v~tomto případě \verb|table|. Například tabulka \ref{tab:matematika} byla vložena tímto způsobem.
% 
% \begin{table}\centering
% 	\caption[Příklad tabulky]{Zadávání matematiky}\label{tab:matematika}
% 	\begin{tabular}{|l|l|c|c|}\hline
% 		Typ		&amp; Prostředí		&amp; \LaTeX{}ovská zkratka	&amp; \TeX{}ovská zkratka	\tabularnewline \hline \hline
% 		Text		&amp; \verb|math|		&amp; \verb|\(...\)|	&amp; \verb|$...$|		\tabularnewline \hline
% 		Displayed	&amp; \verb|displaymath|	&amp; \verb|\[...\]|	&amp; \verb|$$...$$|	\tabularnewline \hline
% 	\end{tabular}
% \end{table}
% 
% % % % % % % % % % % % % % % % % % % % % % % % % % % % 

\chapter{Obsah přiloženého CD}

%upravte podle skutecnosti

\begin{figure}
	\dirtree{%
		.1 readme.txt\DTcomment{stručný popis obsahu CD}.
		.1 exe\DTcomment{adresář se spustitelnou formou implementace}.
		.1 src.
		.2 impl\DTcomment{zdrojové kódy implementace}.
		.2 thesis\DTcomment{zdrojová forma práce ve formátu \LaTeX{}}.
		.1 text\DTcomment{text práce}.
		.2 thesis.pdf\DTcomment{text práce ve formátu PDF}.
		.2 thesis.ps\DTcomment{text práce ve formátu PS}.
	}
\end{figure}

\end{document}
</pre></body></html>