Lib-DVM - описание интерфейса (оглавление) Часть
1(1-5)
Часть 2
(6-7)
Часть 3
(8-11)
Часть 4
(12-13)
Часть 5
(14-15)
Часть 6
(16-18)
Часть 7
(19)
создан: февраль, 2001 - последнее обновление 03.05.01 -

16 Ввод/вывод

16.1 Аналоги библиотеки языка C

Перечисленные ниже функции предназначены для ввода/вывода переменных, каждая из которых в момент вывода имеет одно и то же значение во всех процессорах решающего поля или принимает его после ввода. Их (функций) реализация основана на выполнении соответствующей функции библиотеки языка C только одним процессором, называемым процессором ввода/вывода, с последующей рассылкой результатов выполнения, если таковые имеются, всем остальным процессорам решающего поля. Например, применение функции dvm_printf приведет к однократному выводу, осуществленному процессором ввода/вывода, а применение функции printf – к многократному, произведенному каждым процессором решающего поля. Определить, является ли текущий процессор процессором ввода/вывода, при необходимости можно с помощью функции tstio_, описанной в п.17.6.

Функции ввода/вывода стандартной библиотеки языка C (ANSI), отсутствующие в нижеприведенном списке функций-аналогов (т.е. не имеющие нежелательных эффектов многократного выполнения), могут использоваться программой пользователя без всяких ограничений.

16.1.1 Функции ввода/вывода высокого уровня

ANSI:

void dvm_clearerr (DVMFILE *StreamPtr);
int dvm_fclose (DVMFILE *StreamPtr);
int dvm_feof (DVMFILE *StreamPtr);
int dvm_ferror (DVMFILE *StreamPtr);
int dvm_fflush (DVMFILE *StreamPtr);
int dvm_fgetc (DVMFILE *StreamPtr);
int dvm_fgetpos (DVMFILE *StreamPtr, fpos_t *PosPtr);
char *dvm_fgets (char *StringPtr, int n, DVMFILE *StreamPtr);
DVMFILE *dvm_fopen (const char *FileNamePtr, const char *TypePtr);
int dvm_fprintf (DVMFILE *StreamPtr, const char *FormatPtr [, Argument, ... ]);
void dvm_void_fprintf (DVMFILE *StreamPtr, const char *FormatPtr [,Argument, ... ]);
int dvm_fputc (int c, DVMFILE *StreamPtr);
int dvm_fputs (const char *StringPtr, DVMFILE *StreamPtr);
int dvm_fread (void *BufferPtr, size_t Size, size_t Count, DVMFILE *StreamPtr);
DVMFILE *dvm_freopen (const char *FileNamePtr, const char *TypePtr, DVMFILE *StreamPtr);
int dvm_fscanf (DVMFILE *StreamPtr, const char *FormatPtr [, ArgumentPtr, ... ]);
int dvm_fseek (DVMFILE *StreamPtr, long Offset, int Origin);
int dvm_fsetpos (DVMFILE *StreamPtr, const fpos_t *PosPtr);
long dvm_ftell (DVMFILE *StreamPtr);
int dvm_fwrite (const void *BufferPtr, size_t Size, size_t Count, DVMFILE *StreamPtr);
int dvm_getc (DVM_FILE *StreamPtr);
int dvm_getchar (void);
char *dvm_gets (char *BufferPtr);
int dvm_printf (const char *FormatPtr, ... );
void dvm_void_printf (const char *FormatPtr, ... );
int dvm_putc (int c, DVMFILE *StreamPtr);
int dvm_putchar (int c);
int dvm_puts (char const *StringPtr);
void dvm_rewind (DVMFILE *StreamPtr);
int dvm_scanf (const char *FormatPtr [, ArgumentPtr, ... ]);
void dvm_setbuf (DVMFILE *StreamPtr, char *BufferPtr);
int dvm_setvbuf (DVMFILE *StreamPtr, char *BufferPtr,
int Type, int Size);
DVMFILE *dvm_tmpfile (void);
int dvm_ungetc (int c, DVMFILE *StreamPtr);
int dvm_vfprintf (DVMFILE *StreamPtr, const char *FormatPtr,
va_list ArgList);
void dvm_void_vfprintf (DVMFILE *StreamPtr, const char *FormatPtr,
va_list ArgList);
int dvm_vprintf (const char *FormatPtr, va_list ArgList);
void dvm_void_vprintf (const char *FormatPtr, va_list ArgList);

BORLAND-C & MICROSOFT-C:

int dvm_fgetchar (void);
int dvm_fputchar (int c);

BORLAND-C:

int dvm_vfscanf (DVMFILE *StreamPtr, const char FormatPtr,
va_list ArgList);
int dvm_vscanf (const char *FormatPtr, va_list ArgList);

В вышеперечисленных функциях для задания стандартных потоков ввода/вывода могут быть использованы следующие указатели:

DVMFILE *DVMSTDIN - стандартный ввод;
DVMFILE *DVMSTDOUT - стандартный вывод;
DVMFILE *DVMSTDERR - стандартный вывод сообщений об ошибках;
DVMFILE *DVMSTDAUX - стандартный последовательный порт;
DVMFILE *DVMSTDPRN - стандартное устройство печати.

Замечание 1. Функции dvm_void_printf, dvm_void_fprintf, dvm_void_vprintf и dvm_void_vfprintf отличаются (в своей реализации) от функций dvm_printf, dvm_fprintf, dvm_vprintf и dvm_vfprintf тем, что процессор ввода/вывода не рассылает всем остальным процессорам значения, возвращаемые ему функциями printf, fprintf, vprintf и vfprintf, т.е. более быстрым выполнением.

Замечание 2. Функции dvm_fread и dvm_fwrite могут быть использованы для чтения и записи распределённых массивов. Для этого BufferPtr должен указывать на заголовок распределённого массива. В этом случае размер элемента массива полагается равным не Size, а *TypeSizePtr (тому значению, которое было задано при создании массива с помощью функции crtda_). Число читаемых или записываемых элементов полагается равным минимуму между Count*Size/(*TypeSizePtr) и числом элементов распределённого массива (см. также описание функций dvm_dfread и dvm_dfwrite в п.п.16.2 и 16.3).

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

Замечание 3. Доступ к стандартному потоку stdaux (stdprn) через указатель DVMSTDAUX (DVMSTDPRN) возможен лишь при компиляции системы поддержки с определённой переменной компиляции _DVM_STDAUX_ (_DVM_STDPRN_).

16.1.2 Функции ввода/вывода нижнего уровня

BORLAND-C & MICROSOFT-C & PORTLAND GROUP-C:

int dvm_close (DVMHANDLE *HandlePtr);
int dvm_fstat (DVMHANDLE *HandlePtr, struct stat *BufferPtr);
long dvm_lseek (DVMHANDLE *HandlePtr, long Offset, int Origin);
DVMHANDLE *dvm_open (const char *FileNamePtr, int OFlag, int PMode);
int dvm_read (DVMHANDLE *HandlePtr, char *BufferPtr,
unsigned int Count);
int dvm_write (DVMHANDLE *HandlePtr, const void *BufferPtr, unsigned int Count);

Для доступа к стандартным потокам ввода/вывода в функциях ввода/вывода нижнего уровня могут использоваться следующие указатели:

DVMSTREAM0 - стандартный ввод;
DVMSTREAM1 - стандартный вывод;
DVMSTREAM2 - стандартный вывод сообщений об ошибках;
DVMSTREAM3 - стандартный последовательный порт;
DVMSTREAM4 - стандартное устройство печати.

16.1.3 Операции над каталогами и файлами

ANSI:

int dvm_remove (const char *FileNamePtr);
int dvm_rename (const char *OldNamePtr, const char *NewNamePtr);
char *dvm_tmpnam (char *FileNamePtr);

BORLAND-C & MICROSOFT-C & PORTLAND GROUP-C:

int dvm_access (const char *FileNamePtr, int Mode);
int dvm_stat (const char *FileName, struct stat *BufferPtr);
int dvm_unlink (const char *FileNamePtr);

Замечание. Функции ввода/вывода нижнего уровня, а также функции dvm_access и dvm_stat доступны пользовательской программе лишь в том случае, если библиотека используемого компилятора языка C содержит соответствующие им функции. Для использования функций ввода/вывода нижнего уровня система поддержки и программа пользователя должны быть скомпилированы с определённой переменной компиляции _DVM_LLIO_. Подключение функции dvm_access (dvm_stat) достигается компиляцией с определённой переменной _ACCESS_FUN_ (_STRUCT_STAT_).

При работе в среде WINDOWS+MPI+MICROSOFT_C (WINDOWS+MICROSOFT_C) подключение всех перечисленных выше функций осуществляется компиляцией системы поддержки и программы пользователя с определённой переменной компиляции _WIN_MPI_ (_DVM_MSC_).

16.2 Чтение файла в подмассив распределенного массива

long DisArrRead ( DVMFILE
long
long
long
long
*StreamPtr,
ArrayHeader[],
InitIndexArray[],
LastIndexArray[],
StepArray[] );
     
*StreamPtr - дескриптор файла ввода.
ArrayHeader - заголовок распределённого массива.
InitIndexArray - массив, в i-м элементе которого задается начальное значение индексной переменной (i+1)-го измерения распределённого массива.
LastIndexArray - массив, в i-м элементе которого задается конечное значение индексной переменной (i+1)-го измерения распределённого массива.
StepArray - массив, в i-м элементе которого задается значение шага для индексной переменной (i+1)-го измерения распределённого массива.

Функция DisArrRead осуществляет чтение подмассива распределённого массива, определяемого множеством индексных кортежей вида

{ I1 О M1: I1 = InitIndexArray[0]      + StepArray[0] *P1     } x
        . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
{ I
k
О Mk: Ik = InitIndexArray[k-1] + StepArray[k-1]*Pk  } x
        . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
{ I
n
О Mn: In = InitIndexArray[n-1]  + StepArray[n-1]*Pn  } ,

где:

x - символ прямого произведения множеств;
n - размерность распределённого массива;
Ik - индексная переменная k-го измерения распределённого массива.

0 <= Pk <= (LastIndexArray[k-1] - InitIndexArray[k-1] + 1) / StepArray[k-1].

Читаемый подмассив является блоком (разреженным, если хотя бы один StepArray[i] не равен единице).

Чтение происходит до исчерпания читаемых элементов по принятой в языке C дисциплине непрерывного размещения массивов в памяти: правый индекс меняется быстрее левого. Если начальное значение индекса для какого-либо измерения читаемого массива задано большим или равным его конечному значению, то индекс по этому измерению изменяться при копировании не будет. Отметим, что конечное значение индекса для любого измерения рассматривается системой поддержки как минимум между заданным в параметрах обращения к функции и размером массива по этому измерению минус 1.

Для полного охвата измерения читаемого массива без использования опроса размера объекта по заданному измерению (см.п.17.2) система поддержки предусматривает задание начального значения индекса равным -1. В этом случае начальное значение индекса будет считаться равным нулю, шаг - единице, а конечное значение - размеру массива по рассматриваемому измерению минус 1.

Функция возвращает число прочитанных элементов.

long dvm_dfread ( long
long
DVMFILE
ArrayHeader[],
Count,
*StreamPtr );
     
ArrayHeader - заголовок распределённого массива.
Count - число читаемых элементов.
*StreamPtr - дескриптор файла ввода.

Функция dvm_dfread осуществляет чтение первых по размещению в памяти Count элементов распределённого массива. При положительном Count число читаемых элементов полагается равным минимуму значения Count и числа элементов распределённого массива. При неположительном значении Count производится чтение всего распределённого массива.

Возвращается число прочитанных элементов.

16.3 Запись подмассива распределенного массива в файл

long DisArrWrite ( DVMFILE
long
long
long
long
*StreamPtr,
ArrayHeader[],
InitIndexArray[],
LastIndexArray[],
StepArray[] );
     
*StreamPtr - дескриптор файла вывода.
ArrayHeader - заголовок распределённого массива.
InitIndexArray - массив, в i-м элементе которого задается начальное значение индексной переменной (i+1)-го измерения распределённого массива.
LastIndexArray - массив, в i-м элементе которого задается конечное значение индексной переменной (i+1)-го измерения распределённого массива.
StepArray - массив, в i-м элементе которого задается значение шага для индексной переменной (i+1)-го измерения распределённого массива.

Функция DisArrWrite осуществляет запись блока распределённого массива, определяемого множеством индексных кортежей вида

{ I1 О M1: I1 = InitIndexArray[0]      + StepArray[0] *P1     } x
        . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
{ I
k
О Mk: Ik = InitIndexArray[k-1] + StepArray[k-1]*Pk  } x
        . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
{ I
n
О Mn: In = InitIndexArray[n-1]  + StepArray[n-1]*Pn  } ,

где:

x - символ прямого произведения множеств;
n - размерность распределённого массива;
Ik - индексная переменная k-го измерения распределённого массива.

0 <= Pk <= (LastIndexArray[k-1] - InitIndexArray[k-1] + 1) / StepArray[k-1].

Записываемый подмассив является блоком (разреженным, если хотя бы один StepArray[i] не равен единице).

Запись происходит до исчерпания записываемых элементов по принятой в языке C дисциплине непрерывного размещения массивов в памяти: правый индекс меняется быстрее левого. Если начальное значение индекса для какого-либо измерения записываемого массива задано большим или равным его конечному значению, то индекс по этому измерению изменяться при копировании не будет. Отметим, что конечное значение индекса для любого измерения рассматривается системой поддержки как минимум между заданным в параметрах обращения к функции и размером массива по этому измерению минус 1.

Для полного охвата измерения записываемого массива без использования опроса размера объекта по заданному измерению (см.п.17.2) система поддержки предусматривает задание начального значения индекса равным -1. В этом случае начальное значение индекса будет считаться равным нулю, шаг - единице, а конечное значение - размеру массива по рассматриваемому измерению минус 1.

Функция возвращает число записанных элементов.

long dvm_dfwrite ( long
long
DVMFILE
ArrayHeader[],
Count,
*StreamPtr );
     
ArrayHeader - заголовок распределённого массива.
Count - число записываемых элементов.
*StreamPtr - дескриптор файла вывода.

Функция dvm_dfwrite осуществляет запись первых по размещению в памяти Count элементов распределённого массива. При положительном Count число записываемых элементов полагается равным минимуму значения Count и числа элементов распределённого массива. При неположительном значении Count производится запись всего распределённого массива.

Возвращается число записанных элементов.

Замечание 1. Распределённый массив, задаваемый при обращении к функциям dvm_dfread, DisArrRead, dvm_dfwrite и DisArrWrite, должен быть отображён в процессорную систему, каждый элемент которой обязан принадлежать текущей процессорной системе.

Замечание 2. Первые по расположению в памяти Count элементов распределённого массива не являются, в общем случае, блоком и могут быть описаны следующим образом. Пусть разложение числа Count по весам измерений распределённого массива есть

где:

n - размерность распределённого массива;
Ki - коэффициент разложения для i-го измерения (0 <= Ki <= Sizei -1 , Sizei размер i-го измерения);
Wi - вес i-го измерения.

Тогда первые Count элементов могут быть представлены в виде следующего объединения блоков:

Поэтому чтение (запись) первых Count элементов распределённого массива с помощью функции dvm_dfread (dvm_dfwrite) эквивалентно последовательному чтению (записи) существующих блоков B1, ... , Bn с помощью функции DisArrRead (DisArrWrite) (блок Bm существует, если Km не равно нулю).

17 Вспомогательные функции

17.1 Опрос размерности заданного объекта

long getrnk_ (ObjectRef *ObjectRefPtr);

Функция getrnk_ возвращает размерность объекта, заданного ссылкой *ObjectRefPtr.

Объектом может быть:

Если *ObjectRefPtr не является ссылкой ни однин из вышеперечисленных объектов, то функция возвращает нулевое значение.

Для карты отображения (массива или представления абстрактной машины) возвращается размерность объекта, являющегося источником этой карты.

17.2 Опрос размера заданного измерения заданного объекта

long getsiz_ ( ObjectRef
long
*ObjectRefPtr,
*AxisPtr );

Функция getsiz_ возвращает размер объекта, заданного ссылкой *ObjectRefPtr, по измерению *AxisPtr.

Объектом может быть:

Размер измерения параллельного цикла равен

ceil((LastIndex - InitIndex + 1) / Step) ,

где:

InitIndex - начальное значение индексной переменной параллельного цикла для измерения *AxisPtr;
LastIndex - конечное значение индексной переменной параллельного цикла для измерения *AxisPtr;
Step - шаг изменения индексной переменной параллельного цикла для измерения *AxisPtr;
сeil - функция нахождения наименьшего целого, не меньшего значения своего аргумента.

При нулевом значении *AxisPtr возвращается размер всего объекта (произведение размеров всех измерений).

Если *ObjectRefPtr не является ссылкой ни на один из вышеперечисленных объектов, то функция возвращает нулевое значение.

long locsiz_ ( ObjectRef
long
*ObjectRefPtr,
*AxisPtr );

Функция locsiz_ аналогична рассмотренной выше функции getsiz_, но возвращает локальный размер измерения *AxisPtr (или локальный размер всего объекта), т.е. размер части объекта, отображённой на текущий процессор. Если объектом является многопроцессорная система, то значения, возвращаемые функциями getsiz_ и locsiz_, совпадают.

Отметим, что локальный размер всего объекта, не имеющего локальной части на текущем процессоре, равен нулю.

17.3 Опрос признака распределенного массива

long tstda_ (ObjectRef *ObjectRefPtr);

Функция tstda_ позволяет установить, является ли объект, заданный ссылкой *ObjectRefPtr, распределённым массивом.

Возвращается значение:

0 - объект не является распределённым массивом;
1 - объект является распределённым массивом;
2 - объект является полностью размноженнымраспределённым массивом.

17.4 Опрос длины элемента распределенного массива

long getlen_ (long ArrayHeader[]);

ArrayHeader - заголовок распределённого массива.

Функция getlen_ возвращает длину элемента заданного распределённого массива в байтах.

17.5 Уничтожение заданного объекта

long delobj_ (ObjectRef *ObjectRefPtr);

*ObjectRefPtr - ссылка на уничтожаемый объект.

Уничтожаемым объектом может быть:

Объект быть уничтожена функцией delobj_ только в том случае, если он был создан в текущей подзадаче и в текущем программном блоке (или его подблоке) (см. п. 8 и п. 10).

Возвращается нулевое значение.

17.6 Опрос признака процессора ввода/вывода

long tstio_ (void);

Функция возвращает единицу, если текущий процессор является процессором ввода/вывода, и нуль - в противном случае.

17.7 Рассылка областей памяти процессора ввода/вывода

long srmem_ ( long
AddrType
long
*MemoryCountPtr,
StartAddrArray[],
LengthArray[] );
*MemoryCountPtr - число областей рассылаемой памяти.
StartAddrArray - массив, i-й элемент которого содержит начальный адрес (i+1)-й области, приведенный к типу AddrType.
LengthArray - массив, i-й элемент которого содержит длину в байтах (i+1)-й области.

Функция srmem_ передает заданные области памяти процессора ввода/вывода всем остальным процессорам решающего поля. При выполнении функции процессором ввода/вывода осуществляется передача, любым другим процессором - приём. Число параметров, задаваемых в массивах LengthArray и StartAddrArray, должно быть равно *MemoryCountPtr. Длина каждой рассылаемой области не должна превосходить максимального положительного целого числа (LengthArray[i] ? (unsigned int)-1 >> 1).

Возвращается нулевое значение.

Для преобразования адреса переменной к виду, пригодному для использования в функции srmem_ , в системе поддержки предусмотрена функция

AddrType dvmadr_(void *VarPtr); ,

предназначенная для использования в Фортране.

Для различных типов переменных Фортрана могут быть использованы также функции

AddrType getash_ ( void *VarPtr );
AddrType getai_ ( void *VarPtr );
AddrType getal_ ( void *VarPtr );
AddrType getaf_ ( void *VarPtr );
AddrType getad_ ( void *VarPtr );
AddrType getac_ ( void *VarPtr );
AddrType getach_ ( void *VarPtr, long StrLength );

аналогичные функции dvmadr_ (параметр VarPtr – адрес текстовой строки, а StrLength – её длина, формируемая Фортран-компилятором).

Функции srmem_ и dvmadr_ (getash_, getai_, getal_, getaf_, getad_, getac_, getach_) могут быть использованы для реализации операций ввода/вывода.

18 Об использовании функций системы поддержки в Фортране

Для согласования размещения в памяти массивов по столбцам, принятом в Фортране, с построчным их размещением, предполагаемым в языке C, требуется инверсный порядок задания индексов элементов массивов (по сравнению с их заданием в языке C). Кроме того, предполагаемые в данном документе начальные значения индексов массивов - нулевые, что соответствует языку C. В Фортране - единичные.

Типы данных (кроме базовых типов языка C, DVMFILE* и DVMHANDLE*), используемые при работе с функциями системы поддержки, определены как "unsigned long". Для них допустимы лишь операции присваивания и указания в качестве фактических аргументов при обращении к функциям и подпрограммам.

Каждый базовый тип данных языка C, используемый при обращении к функциям системы поддержки, должен иметь эквивалентное представление в виде некоторой конструкции языка Фортран. Контроль правильности представления базового типа данных языка C в Фортране осуществляется по длине этого представления с помощью функции

void tpcntr_( long
AddrType AddrType
long
long
*ElmNumberPtr,
FirstAddrArray[],
NextAddrArray[],
TypeLengthArray[],
TypeCodeArray[] );
     
*ElmNumberPtr - число элементов, заданных в массивах FirstAddrArray, NextAddrArray, TypeLengthArray и TypeCodeArray.
FirstAddrArray, NextAddrArray - массивы, в i-х элементах которых содержатся адреса двух соседних элементов типа с номером TypeCodeArray[i] (в представлении языка Фортран).
TypeLengthArray - массив, в i-м элементе которого задана предполагаемая длина типа с номером TypeCodeArray[i].
TypeCodeArray - массив, содержащий номера (коды) контролируемых типов данных языка C.

Функция tpcntr_ предполагает следующую нумерацию типов языка C:

  1. "int";
  2. "long";
  3. "float";
  4. "double";
  5. "char";
  6. "short".

Если при выполнении функции tpcntr_ будет установлено, что длина данных типа с номером TypeCodeArray[i] не равна разности (NextAddrArray[i] – FirstAddrArray[i]) или значению TypeLengthArray[i], то программа пользователя будет завершена с выводом диагностики, содержащей:

Обращение к функции tpcntr_ должно предшествовать инициализации системы поддержки из Фортран-программы с помощью функции linit_ (см. п. 2).

Пример.

integer                   getac, getash, getai, getal, getaf, getad, linit
integer                   dvm
integer                   faddr(8), naddr(8), tlen(8), tcode(8)
integer                   tint(2)
logical                   tlog(2)
character*1            tchar (2)
real                        tfloat (2)
double precision   tdouble (2)
integer*2               tint2 (2)
integer*4               tint4 (2)
integer*8               tint8 (2)

C Инициализация массивов с адресами соседних элементов одинакового типа

faddr(1) = getai (tint(1))
naddr(1) = getai (tint(2))
faddr(2) = getal (tlog(1))
naddr(2) = getal (tlog(2))
faddr(3) = getaf (tfloat(1))
naddr(3) = getaf (tfloat(2))
faddr(4) = getad (tdouble(1))
naddr(4) = getad (tdouble(2))
faddr(5) = getach (tchar(1))
naddr(5) = getach (tchar(2))
faddr(6) = getash (tint2(1))
naddr(6) = getash (tint2(2))
faddr(7) = getai (tint4(1))
naddr(7) = getai (tint4(2))
faddr(8) = getai (tint8(1))
naddr(8) = getai (tint8(2))

С Инициализация массива с предполагаемыми длинами контролируемых типов

tlen(1) = 4
tlen(2) = 4
tlen(3) = 4
tlen(4) = 8
tlen(5) = 1
tlen(6) = 2
tlen(7) = 4
tlen(8) = 8

С Инициализация массивов с номерами контролируемых типов

tcode(1) = 1
tcode(2) = 1
tcode(3) = 3
tcode(4) = 4
tcode(5) = 5
tcode(6) = 6
tcode(7) = 1
tcode(8) = 2

C Контроль типов данных

call tpcntr (8, faddr, naddr, tlen, tcode)

C Инициализация системы поддержки

dvm = linit (0)


Lib-DVM - описание интерфейса (оглавление) Часть
1(1-5)
Часть 2
(6-7)
Часть 3
(8-11)
Часть 4
(12-13)
Часть 5
(14-15)
Часть 6
(16-18)
Часть 7
(19)