Всё сдал! - помощь студентам онлайн Всё сдал! - помощь студентам онлайн

Реальная база готовых
студенческих работ

Узнайте стоимость индивидуальной работы!

Вы нашли то, что искали?

Вы нашли то, что искали?

Да, спасибо!

0%

Нет, пока не нашел

0%

Узнайте стоимость индивидуальной работы

это быстро и бесплатно

Получите скидку

Оформите заказ сейчас и получите скидку 100 руб.!


Разработка программы сжатия и восстановления файлов с помощью фиксированного блочного кода постоянного смещения

Тип Реферат
Предмет Информатика и программирование
Просмотров
358
Размер файла
52 б
Поделиться

Ознакомительный фрагмент работы:

Разработка программы сжатия и восстановления файлов с помощью фиксированного блочного кода постоянного смещения

Содержание

Краткое описание работы программы

Код программы

Результаты тестирования приложения

Краткое описание работы программы

Используемые компоненты среды Delphi:

1. SaveDialog, OpenDialog. Копомненты необходимые для чтения и сохранения файлов, а так же получения названия файла и его пути до него.

2. Button - компонент кнопка.

3. ListBox - компонент, состоящий из строк, хранит кодовые слова.

4. ProgressBar - компонент, необходимый для отслеживания этапов выполнения программы.

5. Label - компонент для вывода строковых данных.

Краткое описание работы приложения:

1. При нажатии кнопки Button1 “Считать для сжатия” происходит получение имени считываемого файла и пути до него. Измеряется размер файла (функция FileSize). Полностью очищаются Label.

2. При нажатии кнопки Button2 “Проверить” происходит обнуление всех необходимых для работы переменных. Запускается процесс считывания (по-байтово) и анализа сжимаемого файла, в результате которого высчитывается средняя длинна кодового слова. Она необходима для определения возможности сжатия. В зависимости от полученного результата выводится сообщение о том, что файл можно сжимать, в противном случае, что нельзя. Так же формируется массив частот повторений символов в файле, необходимый для соотнесения кодовых слов и символов в файле.

3. При нажатии Button3 “Сжать” происходит побайтовое считывание сжимаемого файла с помощью команды BlockRead. Далее происходит соотнесение считанного байта кодовому слову. Алгоритм соотнесения основан на частоте появлений символов в файле. Чем чаще данный символ встречается в файле, тем меньшей длинны ему присваивается кодовое слово. Запись нового файла производится с помощью команды BlockWriteтак же по-байтово. Новому файлу присваивается новое расширение. Когда файл полностью считан, выполняется проверка на наличие оставшихся битов. Если таковые имеются, то пустое пространство забивается нулями до тех пор, пока количество битов не будет равно 8.

4. При нажатии Button4 “Считать для восстановления” происходит получение имени сжатого файла, пути до него.

5. При нажатии Button5 “Восстановить" происходит побайтового считывание файла. Алгоритм разжатия состоит из двух этапов. На первом этапе происходит считывание файла блоками размером в 1 байт. Производится запись считанных байтов в специальную переменную. Когда длина переменной составляет 16 символов или более, начинается анализ считанной информации. Производится проверка на соответствие кодовым словам и последующая запись полученных значений в новый файл. На втором этапе производится проверка на остаток и удаление лишних данных (случай, когда было выполнено дополнение нулями при сжатии).

Кодпрограммы:

unit Unit1;

interface

uses

Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

Dialogs, StdCtrls, ComCtrls;

type

TForm1 = class (TForm)

ListBox1: TListBox;

OpenDialog1: TOpenDialog;

SaveDialog1: TSaveDialog;

Button1: TButton;

Button2: TButton;

Button3: TButton;

Label1: TLabel;

ProgressBar1: TProgressBar;

Button4: TButton;

Button5: TButton;

Label3: TLabel;

Label4: TLabel;

Label2: TLabel;

Label5: TLabel;

Label6: TLabel;

Label7: TLabel;

Label8: TLabel;

SaveDialog2: TSaveDialog;

OpenDialog2: TOpenDialog;

procedure Button1Click (Sender: TObject);

procedure FormCreate (Sender: TObject);

procedure Button2Click (Sender: TObject);

procedure Button3Click (Sender: TObject);

procedure Button4Click (Sender: TObject);

procedure Button5Click (Sender: TObject);

private

{ Private declarations }

public

{ Public declarations }

end;

var

Form1: TForm1;

f,f1,f2,f3: file;

buff: byte;

conR,conW: string;

a: array [0.255] of real; // массив вероятностей

word: array [0.255] of string; // массив кодовых слов

l: array [0.255] ofbyte; // массив длин кодовых слов

e: array [0.255] ofreal; // массив номеров элементов

inf: array [0.255] of integer; // массив служебной информации

i,j,k,size,prog,buff1,buff3,check,dop: integer;

sl,sum,sort,sort1,buff2: real;

implementation

{$R *.dfm}

procedure TForm1.Button1Click (Sender: TObject);

begin

if OpenDialog1.Execute then

AssignFile (f,OpenDialog1.FileName); // считываем имя файла и путь до него

Reset (f,1); // открываем файл для чтения и изменения

size: =FileSize (f); // измеряем размер файла

Label4.Caption: =IntToStr (size) + ' байт';

// выводим размер файла в Label4

Label2.Caption: ='';

Label6.Caption: ='';

Label8.Caption: ='';

end;

procedure TForm1.Button2Click (Sender: TObject);

begin

ProgressBar1.Position: =0; // обнуляем ProgressBar

prog: =0; // обнуляем счетчик для Progress Bar

sl: =0; // обнуляем переменную средней длинны

sum: =0; // обнуляем счетчик повторений символов

sort: =0; // обнуляем переменные для сортировки массива номеров элементов

sort1: =0;

buff: =0;

for i: =0 to 255 do

begin

e [i]: =i; // производим обнуление элементов массивов кодовых слов, длин кодовых слов, вероятностей и номеров элементов.

l [i]: =0;

word [i]: ='';

a [i]: =0;

end;

whilenotEOF (f) do // считываем файл до его окончания

begin

BlockRead (f,buff,1); // считываем блоки в 1 байт

a [buff]: =a [buff] +1; // записываем этот байт в массив

prog: =prog+1;

ProgressBar1.Position: =round ( (prog/size) *100);

end;

for i: =0 to 255 do

begin

word [i]: =ListBox1. Items.Strings [i]; // записываем кодовые слова из ListBox1 в массив кодовых слов

ifa [i] <>0 then // проверяем наличие байта в массиве

begin

sum: = sum+a [i]; // считаем количество повторений данного байта

end;

end;

CloseFile (f); // закрываем файл после чтения

for i: =0 to 255 do

begin

for j: =0 to 254 do

begin

if (a [j] < a [j+1]) then

begin

sort: =a [j]; // производим сортировку массива номеров элементов

sort1: =e [j];

a [j]: =a [j+1];

a [j+1]: =sort;

e [j]: =e [j+1];

e [j+1]: =sort1;

end;

end;

end;

for k: =0 to 255 do

begin

ifa [k] <>0 then // проверяем наличие элементов в массиве

begin

a [k]: =a [k] /sum; // считаем у появления символа

l [k]: =length (word [k]); // высчитываем длину кодовых слов

sl: =sl+l [k] *a [k]; // получаем значение средней длинны

end;

end;

Label2.Caption: =FloatToStr (sl); // выводим значение средней длины

ifsl< 8 then // проверяем значение средней длины

begin

Button3.Enabled: =true; // активируем кнопку “Сжать”

showmessage ('Сжатие возможно');

end;

if sl > 8 then

begin

showmessage ('Сжатие невозможно');

end;

end;

procedure TForm1.Button3Click (Sender: TObject);

begin

SaveDialog1.FileName: =OpenDialog1.FileName+'.gop';

// задаем новое расширение для сжатого файла

SaveDialog1.DefaultExt: ='gop';

if SaveDialog1.Execute then

begin

AssignFile (f1, SaveDialog1.FileName);

Rewrite (f1,1); // открываем файл для записи

end;

buff1: =0;

i: =0;

whilei<> 256 do // записываем служебную информацию в новый файл.

begin

buff1: =StrToInt (FloatToStr (e [i]));

BlockWrite (f1,buff1,1);

Reset (f,1);

i: =i+1;

end;

buff1: =0;

seek (f1,256); // осуществляем переход на 256-ой байт в файле

ProgressBar1.Position: =0;

prog: =0;

while not EOF (f) do // считываем файл до его окончания

begin

BlockRead (f,buff,1); // считываем блоки размером 1 байт

buff1: =buff1+1;

prog: =prog+1;

for i: =0 to 255 do

begin

if buff=e [i] then // проверяем совпадения

begin

conR: =conR+word [i]; // записываем соответствующее кодовое слово

iflength (conR) >=8 then // проверяем длину переменной

begin

conW: =copy (conR,1,8); // копируем первые 8 символов

buff2: = ( (strtoint (conW [1])) *128) + ( (strtoint (conW [2])) *64) + ( (strtoint (conW [3])) *32) + ( (strtoint (conW [4])) *16) + ( (strtoint (conW [5])) *8) + ( (strtoint (conW [6])) *4) + ( (strtoint (conW [7])) *2) + (strtoint (conW [8]));

// переводим скопрированную информацию в десятичное число

buff3: =strtoint (floattostr (buff2));

BlockWrite (f1,buff3,1); // записываем результат в новый файл

Delete (conR,1,8); // удаляем первые 8 символов

end;

if (EOF (f) =true) and (conR<>'') and (length (conR) <8) then

// проверяем наличие остатка

begin

k: =0;

check: =length (conR); // вычисляем длину остатка

dop: =8-check; // вычисляем количество необходимых для заполнения битов

whilek<>dopdo // цикл дополнения нулями

begin

conR: =conR+'0'; // дописываем нули

k: =k+1;

end;

conW: =copy (conR,1,8); // копируем данные

buff2: = ( (strtoint (conW [1])) *128) + ( (strtoint (conW [2])) *64) + ( (strtoint (conW [3])) *32) + ( (strtoint (conW [4])) *16) + ( (strtoint (conW [5])) *8) + ( (strtoint (conW [6])) *4) + ( (strtoint (conW [7])) *2) + (strtoint (conW [8]));

// переводим скопированную информацию в десятичное число

buff3: =strtoint (floattostr (buff2));

BlockWrite (f1,buff3,1); // записываем полученные значения

end;

end;

end;

ProgressBar1.Position: =Round ( (prog/size) *100);

end;

Label6.Caption: =Inttostr (FileSize (f1)) + ' байт';

// выводим размер полученного файла

Label8.Caption: =IntToStr (Round (100- (FileSize (f1) *100/size))) + ' %';

// считаем процент сжатия файла

ShowMessage ('Файл успешно сжат');

CloseFile (f); // закрываем файлы

CloseFile (f1);

conR: =''; // обнуляем переменные

conW: ='';

Button3.Enabled: =false;

end;

procedure TForm1.Button4Click (Sender: TObject);

begin

if OpenDialog2.Execute then

AssignFile (f2,OpenDialog2.FileName); // считываем имя файла и путь до него

Reset (f2,1); // открываем файл для чтения и записи

size: =FileSize (f2); // запоминаем размер файла

Label4.Caption: =IntToStr (size) + ' байт';

Label2.Caption: ='';

Label6.Caption: ='';

Label8.Caption: ='';

Button5.Enabled: =true;

for i: =0 to 255 do

begin

BlockRead (f2,buff,1);

inf [i]: =buff; // задаем соответствие по служебной информации

word [i]: =ListBox1. Items.Strings [i]; // считываем массив кодовых слов

end;

end;

functionIntToBin (n: Integer): String; // перевод из десятичного значения в двоичное

var

m: integer;

begin

Result: ='';

while n<>0 do

begin

if n and 1=0 then Result: ='0'+Result else Result: ='1'+Result;

n: =n shr 1;

end;

if length (result) <8 then

begin

for m: =1 to 8-length (result) do result: ='0'+result;

end;

end;

procedure TForm1.Button5Click (Sender: TObject);

begin

if SaveDialog2.Execute then

begin

ProgressBar1.Position: =0;

prog: =0;

AssignFile (f3, SaveDialog2.FileName); // считываем имя файла и путь до него

ReWrite (f3,1); // открываем файл для записи

conR: =''; // обнуляем рабочие переменные

conW: ='';

seek (f2,256); // переходим на 256-ой байт

form1.Refresh;

WhilenotEOF (f2) do // считываем файл до его окончания

begin

iflength (conR) <16 then // проверяем длину рабочей переменной

begin

BlockRead (f2,buff,1); // считываем файл по 1 байту

conR: =conR+IntToBin (buff); // переводим считанный байт в двоичное число

prog: =prog+1;

end;

if length (conR) >=16 then // проверяем длину переменной

Дальнейший код основан на проверке определенных элементов кодовых слов. Так как кодовые слова имеющие смещение отличаются от предыдущих кодовых слов без смещения появлением в начале кодового слова дополнительных нулей, то необходимо проверять всего два элемента в кодовом слове. Поэтому массив разбивается на промежутки, что ускоряет поиск подходящих кодовых слов. Для первых 6 элементов массива кодовых слов достаточно одного условия проверки.

begin

if conR [1] ='1' then

begin

conW: =copy (conR,1,2); // копируем кодовое слово

fori: =0 to1 do

begin

ifword [i] =conWthen // сравниваем массив кодовых слов в заданном промежутке с выделенным кодовым словом

begin

BlockWrite (f3, inf [i],1); // записываем полученный элемент в файл

Delete (conR,1,2); // удаляем кодовое слово

break; // прерываем цикл

end;

end;

end

else

if conR [2] ='1' then

begin

conW: =copy (conR,1,4);

for i: =2 to 5 do

begin

if word [i] =conW then

begin

BlockWrite (f3, inf [i],1);

Delete (conR,1,4);

Break;

end;

end;

end

else

if (conR [2] ='0') and (conR [3] ='1') then

begin

conW: =copy (conR,1,6);

for i: =6 to 13 do

begin

if word [i] =conW then

begin

BlockWrite (f3, inf [i],1);

Delete (conR,1,6);

Break;

end;

end;

end

else

if (conR [3] ='0') and (conR [4] ='1') then

begin

conW: =copy (conR,1,8);

for i: =14 to 29 do

begin

if word [i] =conW then

begin

BlockWrite (f3, inf [i],1);

Delete (conR,1,8);

Break;

end;

end;

end

else

if (conR [4] ='0') and (conR [5] ='1') then

begin

conW: =copy (conR,1,10);

for i: =30 to 61 do

begin

if word [i] =conW then

begin

BlockWrite (f3, inf [i],1);

Delete (conR,1,10);

Break;

end;

end;

end

else

if (conR [5] ='0') and (conR [6] ='1') then

begin

conW: =copy (conR,1,12);

for i: =62 to 125 do

begin

if word [i] =conW then

begin

BlockWrite (f3, inf [i],1);

Delete (conR,1,12);

Break;

end;

end;

end

else

if (conR [6] ='0') and (conR [7] ='1') then

begin

conW: =copy (conR,1,14);

for i: =126 to 253 do

begin

if word [i] =conW then

begin

BlockWrite (f3, inf [i],1);

Delete (conR,1,14);

Break;

end;

end;

end

else

if (conR [7] ='0') and (conR [8] ='1') then

begin

conW: =copy (conR,1,16);

for i: =254 to 255 do

begin

if word [i] =conW then

begin

BlockWrite (f3, inf [i],1);

Delete (conR,1,16);

Break;

end;

end;

end;

if (EOF (f2) = true) and (length (conR) >1) then

// проверяем остаток

begin

for j: =1 to length (conR) do // считаем длину остатка

begin

if conR='' then break;

Проверка на остаток производится аналогично предыдущей процедуре. Поиск кодовых слов в остатке производится путем проверки определенных элементов кодовых слов. Сравнение и запись идентичны.

ifconR [1] ='1' then

begin

conW: =copy (conR,1,2);

for i: =0 to 1 do

begin

if word [i] =conW then

begin

BlockWrite (f3, inf [i],1);

delete (conR,1,2);

break;

end;

end;

end

else

if (conR [1] ='0') and (conR [2] ='1') then

begin

conW: =copy (conR,1,4);

for i: =2 to 5 do

begin

if word [i] =conW then

begin

blockWrite (f3, inf [i],1);

delete (conR,1,4);

break;

end;

end;

end

else

if (conR [2] ='0') and (conR [3] ='1') then

begin

conW: =copy (conR,1,6);

for i: =6 to 13 do

begin

if word [i] =conW then

begin

BlockWrite (f3, inf [i],1);

Delete (conR,1,6);

break;

end;

end;

end

else

if (conR [3] ='0') and (conR [4] ='1') then

begin

conW: =copy (conR,1,8);

for i: =14 to 29 do

begin

if word [i] =conW then

begin

BlockWrite (f3, inf [i],1);

Delete (conR,1,8);

break;

end;

end;

end

else

if (conR [4] ='0') and (conR [5] ='1') then

begin

conW: =copy (conR,1,10);

for i: =30 to 61 do

begin

if word [i] =conW then

begin

BlockWrite (f3, inf [i],1);

Delete (conR,1,10);

break;

end;

end;

end

else

if (conR [5] ='0') and (conR [6] ='1') then

begin

conW: =copy (conR,1,12);

for i: =62 to 125 do

begin

if word [i] =conW then

begin

BlockWrite (f3, inf [i],1);

Delete (conR,1,12);

break;

end;

end;

end

else

if (conR [6] ='0') and (conR [7] ='1') then

begin

conW: =copy (conR,1,14);

for i: =126 to 253 do

begin

if word [i] =conW then

begin

BlockWrite (f3, inf [i],1);

Delete (conR,1,14);

break;

end;

end;

end

else

if (conR [7] ='0') and (conR [8] ='1') then

begin

conW: =copy (conR,1,16);

for i: =254 to 255 do

begin

if word [i] =conW then

begin

BlockWrite (f3, inf [i],1);

Delete (conR,1,16);

break;

end;

end;

end;

end;

end;

end;

ProgressBar1.Position: =Round ( (prog/size) *100);

end;

Label6.Caption: =IntToStr (FileSize (f3)) + ' байт';

ShowMessage ('Процедура завершена');

end;

CloseFile (f3);

CloseFile (f2);

end;

procedure TForm1.FormCreate (Sender: TObject);

begin

for i: =0 to 255 do

a [i]: =0;

e [i]: =i;

end;

end.

Рис.1. Интерфейс программы

Результаты тестирования приложения:

Работа приложения тестировалась на различных типах файлов. Использовались файлы графического, текстового, мультимедийного и других форматов. Все данные о процессах сжатия изложены в следующей таблице.

Имятип файлаРазмер до сжатияИмя сжатого файлаРазмер после сжатияСжатие
8bit org.bmp68,7 КБ8bit org.bmp.gop18,1 КБ73%
24bit org.bmp203 КБ24bitorg.bmp.gop54,1 КБ73%
DOC org.doc1516 КБDOC org.doc.gop843 КБ45%
RTF org.rtf711 КБRTF org.rtf.gop539 КБ24%
TXT org.txt 1 619 байтTXT org.txt.gop1 392 байт14%
midi org.mid40 075 байтmidi org.mid.gop36 551 байт9%
Unit1 org.pas15 721 байтUnit1 org.pas.gop9 068 байт42%

Максимальный размер сжатия составляет 73-75%. В данной таблице отображены форматы файлов, которые можно было сжать. Однако некоторые форматы в связи со своей спецификой несут в себе определенные методы сжатия данных, что не позволяет приложению производить операции над ними. К таким форматам относятся: *.tiff, *.gif, *.wav, *.jpeg, *.avi, *.mp3, *.3gp, *.odt.

Процент сжатия характеризуется длиной кодовых слов и смещением. В данном случае максимальный процент сжатия будет 75%, так как минимальная длина кодового слова равна 2. Сжатие файла происходит побайтово, следовательно заменяем кодовым словом из 2 бит блоки из 8 бит.

Лучше всего подвергаются сжатию текстовые документы, менее сжимаемы и плохо сжимаемы мультимедия файлы.


Нет нужной работы в каталоге?

Сделайте индивидуальный заказ на нашем сервисе. Там эксперты помогают с учебой без посредников Разместите задание – сайт бесплатно отправит его исполнителя, и они предложат цены.

Цены ниже, чем в агентствах и у конкурентов

Вы работаете с экспертами напрямую. Поэтому стоимость работ приятно вас удивит

Бесплатные доработки и консультации

Исполнитель внесет нужные правки в работу по вашему требованию без доплат. Корректировки в максимально короткие сроки

Гарантируем возврат

Если работа вас не устроит – мы вернем 100% суммы заказа

Техподдержка 7 дней в неделю

Наши менеджеры всегда на связи и оперативно решат любую проблему

Строгий отбор экспертов

К работе допускаются только проверенные специалисты с высшим образованием. Проверяем диплом на оценки «хорошо» и «отлично»

1 000 +
Новых работ ежедневно
computer

Требуются доработки?
Они включены в стоимость работы

Работы выполняют эксперты в своём деле. Они ценят свою репутацию, поэтому результат выполненной работы гарантирован

avatar
Математика
История
Экономика
icon
159599
рейтинг
icon
3275
работ сдано
icon
1404
отзывов
avatar
Математика
Физика
История
icon
156450
рейтинг
icon
6068
работ сдано
icon
2737
отзывов
avatar
Химия
Экономика
Биология
icon
105734
рейтинг
icon
2110
работ сдано
icon
1318
отзывов
avatar
Высшая математика
Информатика
Геодезия
icon
62710
рейтинг
icon
1046
работ сдано
icon
598
отзывов
Отзывы студентов о нашей работе
63 457 оценок star star star star star
среднее 4.9 из 5
Филиал государственного бюджетного образовательного учреждения высшего образования Московской област
Спасибо Елизавете за оперативность. Так как это было важно для нас! Замечаний особых не бы...
star star star star star
РУТ
Огромное спасибо за уважительное отношение к заказчикам, быстроту и качество работы
star star star star star
ТГПУ
спасибо за помощь, работа сделана в срок и без замечаний, в полном объеме!
star star star star star

Последние размещённые задания

Ежедневно эксперты готовы работать над 1000 заданиями. Контролируйте процесс написания работы в режиме онлайн

решить 6 практических

Решение задач, Спортивные сооружения

Срок сдачи к 17 дек.

только что

Задание в microsoft project

Лабораторная, Программирование

Срок сдачи к 14 дек.

только что

Решить две задачи №13 и №23

Решение задач, Теоретические основы электротехники

Срок сдачи к 15 дек.

только что

Решить 4задачи

Решение задач, Прикладная механика

Срок сдачи к 31 дек.

только что

Выполнить 2 задачи

Контрольная, Конституционное право

Срок сдачи к 12 дек.

2 минуты назад

6 заданий

Контрольная, Ветеринарная вирусология и иммунология

Срок сдачи к 6 дек.

4 минуты назад

Требуется разобрать ст. 135 Налогового кодекса по составу напогового...

Решение задач, Налоговое право

Срок сдачи к 5 дек.

4 минуты назад

ТЭД, теории кислот и оснований

Решение задач, Химия

Срок сдачи к 5 дек.

5 минут назад

Решить задание в эксель

Решение задач, Эконометрика

Срок сдачи к 6 дек.

5 минут назад

Нужно проходить тесты на сайте

Тест дистанционно, Детская психология

Срок сдачи к 31 янв.

6 минут назад

Решить 7 лабораторных

Решение задач, визуализация данных в экономике

Срок сдачи к 6 дек.

7 минут назад

Вариационные ряды

Другое, Статистика

Срок сдачи к 9 дек.

8 минут назад

Школьный кабинет химии и его роль в химико-образовательном процессе

Курсовая, Методика преподавания химии

Срок сдачи к 26 дек.

8 минут назад

Вариант 9

Решение задач, Теоретическая механика

Срок сдачи к 7 дек.

8 минут назад

9 задач по тех меху ,к 16:20

Решение задач, Техническая механика

Срок сдачи к 5 дек.

9 минут назад
9 минут назад
10 минут назад
planes planes
Закажи индивидуальную работу за 1 минуту!

Размещенные на сайт контрольные, курсовые и иные категории работ (далее — Работы) и их содержимое предназначены исключительно для ознакомления, без целей коммерческого использования. Все права в отношении Работ и их содержимого принадлежат их законным правообладателям. Любое их использование возможно лишь с согласия законных правообладателей. Администрация сайта не несет ответственности за возможный вред и/или убытки, возникшие в связи с использованием Работ и их содержимого.

«Всё сдал!» — безопасный онлайн-сервис с проверенными экспертами

Используя «Свежую базу РГСР», вы принимаете пользовательское соглашение
и политику обработки персональных данных
Сайт работает по московскому времени:

Вход
Регистрация или
Не нашли, что искали?

Заполните форму и узнайте цену на индивидуальную работу!

Файлы (при наличии)

    это быстро и бесплатно