/*
Copyright (c) 1993-2008, Cognitive Technologies
All rights reserved.

╨рчЁх°рхЄё  яютЄюЁэюх ЁрёяЁюёЄЁрэхэшх ш шёяюы№чютрэшх ъръ т тшфх шёїюфэюую ъюфр,
Єръ ш т фтюшўэющ ЇюЁьх, ё шчьхэхэш ьш шыш схч, яЁш ёюсы■фхэшш ёыхфє■∙шї єёыютшщ:

      * ╧Ёш яютЄюЁэюь ЁрёяЁюёЄЁрэхэшш шёїюфэюую ъюфр фюыцэ√ юёЄртрЄ№ё  єърчрээюх
        т√°х єтхфюьыхэшх юс ртЄюЁёъюь яЁртх, ¤ЄюЄ ёяшёюъ єёыютшщ ш яюёыхфє■∙шщ
        юЄърч юЄ урЁрэЄшщ.
      * ╧Ёш яютЄюЁэюь ЁрёяЁюёЄЁрэхэшш фтюшўэюую ъюфр т фюъєьхэЄрЎшш ш/шыш т
        фЁєушї ьрЄхЁшрырї, яюёЄрты хь√ї яЁш ЁрёяЁюёЄЁрэхэшш, фюыцэ√ ёюїЁрэ Є№ё 
        єърчрээр  т√°х шэЇюЁьрЎш  юс ртЄюЁёъюь яЁртх, ¤ЄюЄ ёяшёюъ єёыютшщ ш
        яюёыхфє■∙шщ юЄърч юЄ урЁрэЄшщ.
      * ═ш эрчтрэшх Cognitive Technologies, эш шьхэр хх ёюЄЁєфэшъют эх ьюуєЄ
        с√Є№ шёяюы№чютрэ√ т ърўхёЄтх ёЁхфёЄтр яюффхЁцъш ш/шыш яЁюфтшцхэш 
        яЁюфєъЄют, юёэютрээ√ї эр ¤Єюь ╧╬, схч яЁхфтрЁшЄхы№эюую яшё№ьхээюую
        ЁрчЁх°хэш .

▌╥└ ╧╨╬├╨└╠╠└ ╧╨┼─╬╤╥└┬╦┼═└ ┬╦└─┼╦▄╓└╠╚ └┬╥╬╨╤╩╚╒ ╧╨└┬ ╚/╚╦╚ ─╨╙├╚╠╚ ╦╚╓└╠╚ "╩└╩
╬═└ ┼╤╥▄" ┴┼╟ ╩└╩╬├╬-╦╚┴╬ ┬╚─└ ├└╨└═╥╚╔, ┬█╨└╞┼══█╒ ▀┬═╬ ╚╦╚ ╧╬─╨└╟╙╠┼┬└┼╠█╒,
┬╩╦▐╫└▀ ├└╨└═╥╚╚ ╩╬╠╠┼╨╫┼╤╩╬╔ ╓┼══╬╤╥╚ ╚ ╧╨╚├╬─═╬╤╥╚ ─╦▀ ╩╬═╩╨┼╥═╬╔ ╓┼╦╚, ═╬ ═┼
╬├╨└═╚╫╚┬└▀╤▄ ╚╠╚. ═╚ ┬╦└─┼╦┼╓ └┬╥╬╨╤╩╚╒ ╧╨└┬ ╚ ═╚ ╬─═╬ ─╨╙├╬┼ ╦╚╓╬, ╩╬╥╬╨╬┼
╠╬╞┼╥ ╚╟╠┼═▀╥▄ ╚/╚╦╚ ╧╬┬╥╬╨═╬ ╨└╤╧╨╬╤╥╨└═▀╥▄ ╧╨╬├╨└╠╠╙, ═╚ ┬ ╩╬┼╠ ╤╦╙╫└┼ ═┼
═┼╤и╥ ╬╥┬┼╥╤╥┬┼══╬╤╥╚, ┬╩╦▐╫└▀ ╦▐┴█┼ ╬┴┘╚┼, ╤╦╙╫└╔═█┼, ╤╧┼╓╚└╦▄═█┼ ╚╦╚
╧╬╤╦┼─╬┬└┬╪╚┼ ╙┴█╥╩╚, ╤┬▀╟└══█┼ ╤ ╚╤╧╬╦▄╟╬┬└═╚┼╠ ╚╦╚ ╧╬═┼╤┼══█┼ ┬╤╦┼─╤╥┬╚┼
═┼┬╬╟╠╬╞═╬╤╥╚ ╚╤╧╬╦▄╟╬┬└═╚▀ ╧╨╬├╨└╠╠█ (┬╩╦▐╫└▀ ╧╬╥┼╨╚ ─└══█╒, ╚╦╚ ─└══█┼,
╤╥└┬╪╚┼ ═┼├╬─═█╠╚, ╚╦╚ ╙┴█╥╩╚ ╚/╚╦╚ ╧╬╥┼╨╚ ─╬╒╬─╬┬, ╧╬═┼╤┼══█┼ ╚╟-╟└ ─┼╔╤╥┬╚╔
╥╨┼╥▄╚╒ ╦╚╓ ╚/╚╦╚ ╬╥╩└╟└ ╧╨╬├╨└╠╠█ ╨└┴╬╥└╥▄ ╤╬┬╠┼╤╥═╬ ╤ ─╨╙├╚╠╚ ╧╨╬├╨└╠╠└╠╚,
═╬ ═┼ ╬├╨└═╚╫╚┬└▀╤▄ ▌╥╚╠╚ ╤╦╙╫└▀╠╚), ═╬ ═┼ ╬├╨└═╚╫╚┬└▀╤▄ ╚╠╚, ─└╞┼ ┼╤╦╚ ╥└╩╬╔
┬╦└─┼╦┼╓ ╚╦╚ ─╨╙├╬┼ ╦╚╓╬ ┴█╦╚ ╚╟┬┼┘┼═█ ╬ ┬╬╟╠╬╞═╬╤╥╚ ╥└╩╚╒ ╙┴█╥╩╬┬ ╚ ╧╬╥┼╨▄.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright notice,
      this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright notice,
      this list of conditions and the following disclaimer in the documentation
      and/or other materials provided with the distribution.
    * Neither the name of the Cognitive Technologies nor the names of its
      contributors may be used to endorse or promote products derived from this
      software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

//базовые процедуры разрезания-склеивания
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#ifndef __MAC__
//#include <bios.h>
#endif
#include "nt_types.h"
#include "struct.h"
#include "cuthdr.h"
#include "dmconst.h"
#include "func.h"
#include "ligas.h"
#include "lang.h"
#include "linutil.h"
#include "status.h"
#include "p2libr.h"
//#include "Alik_rec.h"
#include "cut_glue.h"
#include "dpuma.h"

#include "compat_defs.h"

extern BYTE langUkr , langSer;
//#define  PROTOCOL

#define  STRRCHR  strrchr
#define  STRSPN   strspn

#define  MAX_GATE  RASTER_WIDTH  //макс. просвет внутри символа
#define  MAX_WIDTH RASTER_WIDTH  //макс. ширина символа

#define  CUT_PEN   15  //штраф за разрез
#define  PASS_PEN  45  //штраф за пропущенный dust
#define  CUT_rlI   30  //штраф отрезанным "rlI"
#define  PEN_TOP_DUST    15      //штраф для м,н,п,ц,ы, если сверху есть dust
#define  RO_DUST_OFF     80      //макс. ошибка для распознавания без dust'ов
#define  RO_COMPOSE     210      //максимальная ошибка для собранной буквы
#define  RO_BAD  MAX_RO-MINlet   //максимальная ошибка у плохой буквы
#define  RELIABLE_A     190      //надежная "а" (не может быть частью
#define  RELIABLE_9     150      //надежная "9"  другого символа)
#define  I_wo_point     150      //вероятность для "i" без точки
#define  G_to_T   100  //фора для 'т'
#define  P_to_L    35  //фора для 'л'
#define  O_to_B    60  //фора для 'б'
#define  mi_PEN    60  //штраф для 'm', если есть i
#define  m_nc_PEN  30  //штраф для 'm', если несвязная
#define  H_PEN     30  //штраф для собранной 'H'
#define  IEE_IU_PEN 40 //штраф в сочетаниях 'ыо' и  'ью'

//---------------------  types   --------------------------------------

struct cell_list_struct  //список cell'ов
{
  INT N;                 //количество
  cell *cells[MAX_SECT];
};
typedef struct cell_list_struct cell_list;

struct dubble_list_struct  //список разных cell'ов
{
  cell_list b;  //количество и список больших cell'ов
  cell_list d;  //количество и список dust'ов
};
typedef struct dubble_list_struct dubble_list;

struct grup_struct      //группа cell'ов
{
  INT n;       //количество
  INT a;       //верхний край
  INT b;       //нижний край
};
typedef struct grup_struct grup;

//-------------------  variables  -------------------------------------

 //extern CHAR accent_tab[];
 extern BYTE db_status;  // snap presence byte
 extern BYTE db_trace_flag;  // 2 - more detailed estimate (ALT-F7)
 extern BOOL pass4_in;   //флаг: второй проход по странице
 extern INT  sMALL_SIZE; //минимальная высота "большого" элемента
 Handle hSnapBLcut;

 BYTE sticks_left_to_bad[]  =  {"  1/!|l1IJ)}[]"};  // 0,1st pos reserved for liga_i, liga_exm
												    // 2nd	for turkish i_sans_accent - Nick 12.06.02
 BYTE letters_left_to_bad[] =  {" nrvtcC(u<>ьЬиИўкКлнНпПсСгГ"}; // 0th pos for sticks " nrvtcC(u<>№▄ш╚ўъ╩ыэ═я╧ё╤у├"

 static CHAR ltmp0[] = "kDPbhвВнНиИпПкКмМцЦшШщЩюЮ";
 static CHAR ltmp1[] = "m"; /* m */
 static CHAR ltmp2[] = "nm"; /* r */
 static CHAR ltmp3[] = "w"; /* v */
 static CHAR ltmp4[] = "u"; /* t */
 static CHAR ltmp5[] = "ao"; /* c */
 static CHAR ltmp6[] = "O6"; /* C */
 static CHAR ltmp7[] = "oOоО06"; /* ( */
 static CHAR ltmp8[] = "w"; /* u */
 static CHAR ltmp9[] = "odо"; /* < */
 static CHAR ltmp10[] = "xXжЖх"; /* > */
 static CHAR ltmp11[] = "ы"; /* № */
 static CHAR ltmp12[] = "Ы"; /* ▄ */
 static CHAR ltmp13[] = "мшщ"; /* ш */
 static CHAR ltmp14[] = "ШЩ"; /* ╚ */
 static CHAR ltmp15[] = "шщ"; /* ў */
 static CHAR ltmp16[] = "ю"; /* ъ */
 static CHAR ltmp17[] = "Ю";
 static CHAR ltmp18[] = "м"; /* ы */
 static CHAR ltmp19[] = "шщю"; /* э */
 static CHAR ltmp20[] = "ШЩЮ"; /* ═ */
 static CHAR ltmp21[] = "їшщ"; /* я */
 static CHAR ltmp22[] = "ШЩ"; /* ╧ */
 static CHAR ltmp23[] = "оф"; /* ё */
 static CHAR ltmp24[] = "О"; /* ╤ */
 static CHAR ltmp25[] = "п"; /* у */
 static CHAR ltmp26[] = "П"; /* ├ */

 CHAR *results_left_to_bad[] = {
         ltmp0, ltmp1, ltmp2, ltmp3, ltmp4, ltmp5, ltmp6, ltmp7, ltmp8, ltmp9,
         ltmp10, ltmp11, ltmp12, ltmp13, ltmp14, ltmp15, ltmp16, ltmp17, ltmp18, ltmp19,
         ltmp20, ltmp21, ltmp22, ltmp23, ltmp24, ltmp25, ltmp26
      };

 BYTE prob_left_to_bad[] =
      { MAX_RO, MAX_RO, MAX_RO, MAX_RO, MAX_RO, MAX_RO, MAX_RO, MAX_RO, MAX_RO,
 //     stick   n       r       v       t       c       C       (       u
        MAX_RO, MAX_RO, MAX_RO, MAX_RO, 240, 240, MAX_RO, MAX_RO, MAX_RO,
 //     <       >       №       ▄       ш    ╚    ў       ъ       ╩
        240, 240, 240, MAX_RO, 240, MAX_RO, MAX_RO, MAX_RO, MAX_RO };
 //     ы    э    ═    я       ╧    ё       ╤       у       ├

/*
	0 pos reserved for liga_i
	1st pos reserved for liga_exm,
	2-3 pos for turkish II_dot_accent, i_sans_accent 21.05.2002 E.P.
*/
 BYTE sticks_right_to_bad[] =  {"1111/!|l1I[]"};


 BYTE letters_right_to_bad[] = {" cJnNvt)u><гГсСцЦиИўнНоОпПкКл"};	// " cJnNvt)u><у├ё╤Ў╓ш╚ўэ═ю╬я╧ъ╩ы"
                                                      // 0th pos for sticks

 static CHAR tmp0[] = "dUлЛмМнНиИпПшШыЫ";
 static CHAR tmp1[] = "k"; /* c */
 static CHAR tmp2[] = "U"; /* J */
 static CHAR tmp3[] = "m"; /* n */
 static CHAR tmp4[] = "W"; /* N */
 static CHAR tmp5[] = "w"; /* v */
 static CHAR tmp6[] = "u"; /* t */
 static CHAR tmp7[] = "oOоО0"; /* ) */
 static CHAR tmp8[] = "w"; /* u */
 static CHAR tmp9[] = "bop6Dор"; /* > */
 static CHAR tmp10[] = "kxXкКжЖхХ"; /* < */
 static CHAR tmp11[] = "т"; /* г */
 static CHAR tmp12[] = "Т"; /* Г */
 static CHAR tmp13[] = "кж"; /* с */
 static CHAR tmp14[] = "КЖ"; /* С */
 static CHAR tmp15[] = "щ"; /* ц */
 static CHAR tmp16[] = "Щ"; /* ц */
 static CHAR tmp17[] = "мш"; /* ў */
 static CHAR tmp18[] = "Ш"; /* И */
 static CHAR tmp19[] = "ш"; /* ў */
 static CHAR tmp20[] = "ш"; /* н */
 static CHAR tmp21[] = "Ш"; /* Н */
 static CHAR tmp22[] = "ю"; /* о */
 static CHAR tmp23[] = "Ю"; /* О */
 static CHAR tmp24[] = "їш"; /* п */
 static CHAR tmp25[] = "Ш"; /* П */
 static CHAR tmp26[] = "ж"; /* к */
 static CHAR tmp27[] = "Ж"; /* К */
 static CHAR tmp28[] = "м"; /* л */

 CHAR *results_right_to_bad[] =
   {tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9,
    tmp10, tmp11, tmp12, tmp13 ,tmp14 ,tmp15, tmp16, tmp17, tmp18, tmp19,
    tmp20, tmp21, tmp22, tmp23 ,tmp24 ,tmp25, tmp26, tmp27, tmp28};
 //  к   К   л
 BYTE prob_right_to_bad[] =
      { MAX_RO, MAX_RO, MAX_RO, MAX_RO, MAX_RO, MAX_RO, MAX_RO, MAX_RO, MAX_RO,
 //     stick   c       J       n       N       v       t       )       u
        MAX_RO, MAX_RO, MAX_RO, MAX_RO, 240, 240, MAX_RO, MAX_RO, 240, 240,
 //     >       <       г       Г       с    С    ц       Ц       и    И
        MAX_RO, 240, 240, MAX_RO, MAX_RO, MAX_RO, 240, MAX_RO, MAX_RO, 240 };
 //     ў       н    Н    о       О       п       П    к       К       л

#define RESLEN 26 //максимальная длина пересечения results_left_to_bad и
                  // results_right_to_bad

 static BYTE  left_to_bad[] = {" /!l1IJ)}]cCnrvtгГсўСч(u<>ьЬпП|\x0"}; // 0 =>liga_exm.  10.09.2000 E.P.
 static BYTE right_to_bad[] = {"/|!l1I]nvt)u><гГўпПсСкКцЦчоО\x0"};
 static BYTE       sticks[] = {"  1lI1]!"}; // 0,1 -> liga_i,liga_exm // 08.09.2000 E.P.
											// 2 -> turkish i_sans_accent	Nick 12.06.02
 static BYTE  left_to_dust[] = {"лоч1!"};
 static BYTE right_to_dust[] = {"гГоич!"};

 static BYTE unify_width[] =   //символы "стандартной" ширины
  {"023456789абвгезиклнопрстуфхцчъьэяАБВГЕЗИКЛНОПРСТУФХЦЧЪЬЭЯЁёў°¤"};

 B_LINES my_bases; //базовые линии
 INT        blank; //ширина пробела
 INT minw;         //минимальная ширина буквы
 INT minp;         //минимальная ширина точки
 INT ps3;          // 1/3 высоты строчной буквы
 INT sym_dist=0;   //среднее расстояние между символами
 INT line_sym_width=0;  //средняя ширина символа в строке
 INT sym_width=0;       // то же в текущем слове
 INT cut_width=0;  //при ширине > cut_width можно резать

 CHAR snap_text[80]={0},*snap=snap_text;

 BYTE trs2;             // >trs2 - буква хорошая во всех отношениях
 BYTE trg;              //порог для склеивания

// CHAR bs_cut_en=0;

#ifdef PROTOCOL
 extern INT line_number;
// static INT rec_count=0;      //счетчик для статистики
// static INT t;
#endif

//---------------------  macros  --------------------------------------


#define loc_err(x) (MAX_RO-(x)->vers[0].prob) //локальная мера
#define norm(m,l)  ( (m)*(l)>>2 )             //нормировка меры

#define stick(x)   ( strchr(sticks,(x)) )

#define Alik cuts_point_methode

//------------------  functions  --------------------------------------

static CHAR get_sym_width(INT *sw, INT *mw, INT *sd);
static INT *arr_top(INT *arr, INT len);
static INT *arr_bot(INT *arr, INT len);
static INT hist_sq(INT *hist, INT *top);
static INT get_sym_width2();
static INT get_sym_width3(cell *WB, cell *WE);

static void mark_own_dust(cell *B);
//static cell *process_word (cell *WB, cell *WE);
static cell *process_frame (cell *WB, cell *WE);
static INT one_glue (INT n, cell **S, INT tol);
static cell *cut_glue (cell *LC, cell *E, CHAR ovfl);
static cell *break_cell(cell *B);
static cell *overlap_cell( cell *C, cell *D );
static INT compose_raster(raster *r, cell **clist, INT N);
static INT init_dp(struct cut_elm *cut_list, seg_vers **vers_list,
                   cell_list *list, INT rastlc, INT rastdr);
INT get_cuts (cell *C, struct cut_elm *list, INT nmax );
static void fict_sect(struct cut_elm *cut, INT x, INT px);
static void init_sect(struct cut_elm *cpnti);
static void cor_sect(cell *C, struct cut_elm *cut, INT left, INT down);
static CHAR dp_passi(cell *LC, raster *r, struct cut_elm *cut_list,
                     seg_vers **vers_list, INT ncut, INT pass);
static BYTE accept_segment(cell *C, raster *r0, struct cut_elm *cut_list,
                           seg_vers **vers_list, INT i1, INT i0, CHAR mode);
static CHAR *res_for(BYTE let, BYTE *sticks, BYTE *letters, CHAR **results);
static CHAR *unite_list(CHAR *resstr,INT i1,INT i0,struct cut_elm *cut_list,
                        seg_vers **vers_list);
static CHAR *strXcrs(CHAR *resstr, CHAR *str1, CHAR *str2);
static INT select_cells(cell *C,MN *mn1,INT pos1, INT pos2, BYTE cut_fl,
                        cell_list *left_list, cell_list *right_list);
INT recogij(cell *C, cell **org_cells, INT N, BYTE cut_fl,
                   BYTE cut_fine,INT inc, INT *roi, BYTE *gvar,
                   SVERS *vers, INT *width );
static BYTE classify (cell **cells, INT N, grup *box, grup *bottom,
                      grup *top, grup *far_bottom, grup *far_top);
static INT horiz_proj(cell **cells, INT N, BYTE *proj, INT size);
static void change(void **arr, INT i, INT j);
static INT recog_one(cell *B0, s_glue *GL, INT tol, SVERS *vers);
//static CHAR wide (cell *C);
static INT recog_set(cell *C,cell **org_cells, INT N, BYTE cut_fl,
                     BYTE cut_fine, INT inc,
                     INT tolbox, BYTE *gvar, SVERS *vers, INT *width);
static cell *unite (cell *C, cell **org_cells, INT N, s_glue *GL, BYTE *gvar);
static BYTE accept_vers(CHAR *snap, struct cut_elm *cut_list,
                        INT i1, INT i0, SVERS *vers, CHAR gvar, INT width,
                        INT cur_meas);
static BYTE not_connect_chain(INT i1, INT i0, struct cut_elm *cut_list);
static BYTE dust_chain(INT i1, INT i0, struct cut_elm *cut_list);
static cell *recover_path(void *kita, raster *r, struct cut_elm *cut_list,
                          INT N, cell *LC, cell *RC, CHAR ovfl);
static INT locate(cell *B, struct cut_elm *cut_list, INT N, INT left);
static void set_type(cell *B, SVERS *vers);
static INT is_stick(cell *B);
static void paste();
static void compose_cg_flags(cell *B, cell *L, cell *R);
//static INT full_recog (cell *B1, s_glue *gl0, INT tolbox, INT trs);
static void corr_cut();
static INT have_upper_dot(cell *c);
static INT forbid_stick_cut(cell *c, SVERS *vers, INT h, cut_pos *cpos,
                            INT edge);
static INT contain(cell *new, cell *old);
cell * create_my_cell(MN * mn, cell * ci, CHAR bdiff, CHAR dflag);

//dm2.c
INT glue_to_o (BYTE c2, BYTE c3, cell *BC, cell *EC);

void test_match_cell_word(B_LINES *my_bases, INT cut_width);

#ifdef PROTOCOL
#include "..\stats\protocol.c"
#endif

static void close_ds(struct cut_elm *, INT , INT );  //AK 23.03.97 add    ???
void set_bad_vers(SVERS *);                          //AK 23.03.97 add
//extern void det_snap(cell *, CHAR *);                //AK 23.03.97 add

/**************************************************************************/

void cuts_glues()
{
 cell *B;
 cell     *WB,*WE; //начало и конец слова
 INT      end_col; //правая граница слова

 Z=&string;

 sticks[0] = liga_i;		// 08.09.2000 E.P.
 sticks[1] = liga_exm;		// 10.09.2000 E.P.

 left_to_bad[0] = liga_exm;	// 10.09.2000 E.P.


 sticks_left_to_bad[0] =liga_i;
 sticks_left_to_bad[1] =liga_exm;
 sticks_right_to_bad[0]=liga_i;
 sticks_right_to_bad[1]=liga_exm;

 // ╥єЁхЎъшх ярыъш
 if(is_turkish_language(language))
	{
	sticks_right_to_bad[2]=II_dot_accent;
	sticks_right_to_bad[3]=i_sans_accent;

	sticks_left_to_bad[2] = i_sans_accent;  // Nick 12.06.02
	sticks[2] = i_sans_accent;			// Nick 12.06.02
	}
 else	// 16.06.2002 E.P.
	{
	sticks_right_to_bad[2]='1';
	sticks_right_to_bad[3]='1';
	sticks_left_to_bad[2] = '1';
	sticks[2] = '1';
	}

// if (pass4_in) { trs2=trg=180; }
 if (pass4_in) { trs2=trg=200; }
 else          { trs2=220; trg=190; }

 snap_newpass('a');
 get_b_lines(NULL,&my_bases);
 WB=cell_f()->next;
 if (debug_on)
 { char bb[90];
   sprintf (bb,"cuts/glues begin\n bs= %d %d %d %d, Ps=%d, nb= %d %d %d %d",
     my_bases.b1, my_bases.b2, my_bases.b3, my_bases.b4,
     my_bases.ps,
     my_bases.n1, my_bases.n2, my_bases.n3, my_bases.n4);
   glsnap('a',WB,bb);
 }

//оцениваем величину пробела и мин. ширину буквы

 blank=my_bases.ps>>1;  // 1/2 высоты строчной буквы
 ps3=my_bases.ps/3;
 minw=my_bases.ps/5;  minp=minw>>1;
 line_sym_width=my_bases.ps; cut_width=0;
 if ( language == LANG_RUSSIAN && !pass4_in )
 {
   get_sym_width(&line_sym_width,&cut_width,&sym_dist);
//   if (get_sym_width(&sym_width,&cut_width))  minw=sym_width>>2;
//   else                                       minw=my_bases.ps/5;
//   minp=minw>>1;
 }
 else
   minw=minp;
//   minw=minp=my_bases.ps/10;
// minw=MAX(minw,1); minp=max(minp,1);

//уточняем оценки и расставляем точки над 'i'

adjust_3x5(TRUE);


//еще раз пробуем оценить ширину символа
 if (line_sym_width==0)  line_sym_width=get_sym_width2();
 sprintf(snap,"dist=%d  width: aver=%d max=%d",
         sym_dist,line_sym_width,cut_width);
 WB=cell_f()->next;
 det_snap(WB,"");

#ifdef MATCH_WORD
 test_match_cell_word(&my_bases,cut_width);
#endif

 perc();  //// '╣' ш '%'

 WB=cell_f()->next;
 do  // цикл по словам
 {
   WE=WB; end_col=0;
   do
   {
     B=WE;
     WE=B->next;
     end_col=MAX(end_col,B->col+B->w);
   }
   while ( WE->next && ( WE->col - end_col ) < blank );
   WE=B;
   sym_width = (line_sym_width) ? line_sym_width : get_sym_width3(WB,WE);
   WB=process_word(WB,WE);
 }
 while ( WB->next ) ;

 if(language==LANG_RUSSIAN)
	 paste();


// if (pass4_in && bs_cut_en)
 if (pass4_in && LDPUMA_SkipEx(hSnapBLcut,FALSE,FALSE,1))
 {
   glsnap('o',B, "pass2 cut by bases");
   base_lines_cut();
   glsnap('o',B, "cuts_glues's end");
 }

#ifdef PROTOCOL
//  wr_prot ("pro",'c',(INT)'\n');
#endif

//если p>MINlet, то "хорошая"

 B=cell_f()->nextl;
 glsnap('a',B, "cut end, corr start");
 corr_cut();
 B=cell_f()->nextl;
 glsnap('a',B, "corr end");

}

/*----------------------------------------------------------------------
  get_sym_width -  вычисляет среднюю sw и максимальную mw ширину символа
                   и минимальное расстояние между символами sd;
                   если успешно, возвращает 1, иначе возвращает 0 и mw=0,
                   sd=0, sw=my_bases.ps
----------------------------------------------------------------------*/
static CHAR get_sym_width(INT *sw, INT *mw, INT *md)
{
 cell *B=cell_f()->next;
 CHAR proj[MAX_COL],*p=proj;     //проекция строки на горизонтальную ось
 INT  maxcl=0;                   //ее конец
 INT gate_hist[MAX_GATE]={0};    //гистограмма "белых" отрезков
 INT width_hist[MAX_WIDTH]={0};  //гистограмма "черных" отрезков
 INT *top1,*top2,*bot;           //вершины I и II мод и граница между ними
 INT sq_hist=0,main_sq;          //площади: общая и II моды
 INT ps2=my_bases.ps>>1;
 INT w,pick;

 memset(proj,'0',MAX_COL);

//проекция строки на горизонтальную ось
 do
 {
    INT beg=B->r_col; INT len=B->w;
    INT end=beg+len;
    if (end>=MAX_COL)  break;
    maxcl=MAX(maxcl,end);
    memset(proj+beg,'1',len);
    B=B->next;
 }
 while ( B->next ) ;
 proj[maxcl]=0;

  p += STRSPN(p,"0");          //нули в начале строки
  w=STRSPN(p,"1");             //первая проекция
  if ( w<MAX_WIDTH )  width_hist[w]++;
  p += w;
  while ( w>0 )
  {
    w=STRSPN(p,"0");
    if ( w<MAX_GATE )  gate_hist[w]++;
    p += w;
    w=STRSPN(p,"1");
    if ( w<MAX_WIDTH )
    {
      width_hist[w]++; sq_hist++;
    }
    p += w;
  }

  top1=arr_top(width_hist+1,ps2);           // I  мода
  top2=arr_top(width_hist+ps2,my_bases.ps); // II мода (ширина букв)
  bot=arr_bot(top1,(INT)(top2-top1));              //граница между ними
  main_sq=hist_sq(bot,top2);                //площадь II моды
  if ( main_sq+(main_sq<<1) <= sq_hist<<1 )
  {                         //оценка недостоверна
    *sw=0;  *mw=0;  *md=0;
    return 0;
  }
  else
  {
    *sw=top2-width_hist;            //вершина II моды
    while (*top2 != 0)  top2++;     //ее правый край+1
    *mw=top2-1-width_hist;
    top1=arr_top(gate_hist+1,ps2);
    pick=(*top1)>>1;
    while ( *top1>pick && top1>gate_hist )  top1--;
    *md=top1-gate_hist;
    return 1;
  }
}

/*----------------------------------------------------------------
  arr_top  ищет адрес максимального элемента в массиве arr длины
           len
----------------------------------------------------------------*/
static INT *arr_top(INT *arr, INT len)
{
  INT  i,topv=*arr, *topi=arr, *cur=arr+1;
  for (i=1; i<len; i++,cur++)
    if (*cur>topv)
    {
      topv=*cur; topi=cur;
    }
  return topi;
}
/*----------------------------------------------------------------
  arr_top  ищет адрес минимального элемента (если несколько, то -
           последнего из них) в массиве arr длины len
----------------------------------------------------------------*/
static INT *arr_bot(INT *arr, INT len)
{
  INT  i,botv=*arr, *boti=arr, *cur=arr+1;
  for (i=1; i<len; i++,cur++)
    if (*cur<=botv)
    {
      botv=*cur; boti=cur;
    }
  return boti;
}

/*-----------------------------------------------------------------
  mode_sq  вычисляет площадь гистограммы hist, ограниченной нулем
           справа от вершины top
 ----------------------------------------------------------------*/
static INT hist_sq(INT *hist, INT *top)
{
  INT *cur, sq=0;
  for (cur=hist; *cur != 0 || cur<top; cur++)  sq += *cur;
  return sq;
}

/*-----------------------------------------------------------------
  get_sym_width2() оценивает ширину символа по всей строке
-----------------------------------------------------------------*/
static INT get_sym_width2()
{
 cell *B;
 BYTE *ip,*ip2;
 INT ws=0;           //искомая ширина
 BYTE hist[256]={0}; //гистограмма ширины
 BYTE top=0,*itop;   //вершина гистограммы
 INT s=0,s1=0;       //площадь гистограммы: общая и в области (3/4;5/4)ws

//строим гистограмму

 B=cell_f()->nextl;
 while ( !fict(B) )
 {
   if (let(B) && strchr(unify_width,B->vers[0].let) &&
		!is_russian_baltic_conflict(B->vers[0].let)	&&  // 17.07.2001 E.P.
		!is_russian_turkish_conflict(B->vers[0].let)	// 21.05.2002 E.P.
	  )
     { hist[B->w]++; ws += B->w; s++; }
   B=B->nextl;
 }
 if (s==0)  return 0;
 ws /= s;

//ищем максимум в области средней ширины

 ip2=hist+ws+(ws>>1);
 for (ip=ip2-ws; ip<=ip2; ip++)
   if (*ip>top)  { top=*ip; itop=ip; }

//результат достоверен, если в области максимума сосредоточено больше 3/4s

 if (top>2)  ws=itop-hist;
 ip2=itop+(ws>>2);
 for (ip=itop-(ws>>2); ip<=ip2; ip++)  s1 += *ip;

 if ( s-s1 <= s>>2 )  return ws;
#ifdef PROTOCOL
  wr_prot ("multfont",'d',line_number);
#endif
 return 0;
}

/*-----------------------------------------------------------------
  get_sym_width3() оценивает ширину символа по текущему слову
-----------------------------------------------------------------*/
static INT get_sym_width3(cell *WB, cell *WE)
{
  cell *B;
  INT  s=0,ws=0;

  for (B=WB; B != WE; B=B->next)
    if (let(B) && strchr(unify_width,B->vers[0].let) &&
		 !is_russian_baltic_conflict(B->vers[0].let) &&	// 17.07.2001 E.P.
		 !is_russian_turkish_conflict(B->vers[0].let)	// 21.05.2002 E.P.
	   )
		{ ws += B->w; s++; }
  if (s>0)  return  ws/s;
  return  my_bases.ps-(my_bases.ps>>3);
}

/*-----------------06-22-95 11:12am--------------------------------
  process_word - яюшёъ ш Ёрёяючэртрэшх "ъюьяръЄэ√ї" єўрёЄъют, ьхцфє
                 cell'рьш WB ш WE; тючтЁр∙рхЄ WE->next
-----------------------------------------------------------------*/
cell *process_word (cell *WB, cell *WE)
{
  cell  *B;    //эрўрыю "ъюьяръЄэюую" єўрёЄър
  cell  *E;    //ъюэхЎ        -"-
  INT end_col; //яЁрт√щ ъЁрщ  -"-
  INT max_gate;//ьръё. "яЁюётхЄ" тэєЄЁш Ёрчтрышт°хуюё  ёшьтюыр
  cell *RW;    //cell ёяЁртр юЄ ёыютр
  cell *ST;    //ярыър яюёых "№"
  BYTE let;
  cell *C;

 RW=WE->next; B=WB; max_gate=my_bases.ps/3;

 B=WB;
 do  // цикл по "компактным" участкам
 {
   E=B; end_col=B->col+B->w; ST=NULL;

   while (1)
   {
     if ( language == LANG_RUSSIAN )
		{
        let=E->vers[0].let;

       if ( let(E) &&
				(
				let==(BYTE)'т' &&
					!is_russian_turkish_conflict(let) || // 21.05.2002 E.P.
				let==(BYTE)'Т')
				)
       {                 //"т" не всегда надежно по эвентам
         if ( !complete_recog(E) )
           if ( E != WB && !let(E->prev) || E != WE && !let(E->next) )
           {
             let_to_bad(E);
             full_recog(E,NULL,0,trs2);
           }
       }
       else
         if ( !dust(E) && E->nvers)
           if ( ST && strchr("оОсCцЦ",let) &&		// "ю╬ёCЎ╓"
				!is_russian_baltic_conflict(let) &&	// 17.07.2001 E.P.
				!is_russian_turkish_conflict(let)	// 21.05.2002 E.P.
			  )
             set_bad(ST);   //"√ю","№■","№ъ" шыш "№∙" - эрфю шёёыхфютрЄ№
           else
             if ( E->vers[0].let=='|' ) ST=E;
             else                       ST=NULL;
     }

     if ( E==WE )  break;
     if ( E==cell_l() )  break; // OLEG 08 dec 99

     C=E->next;
     if ( C->col - end_col > max_gate )
       if (pass4_in)
       {
         while ( dust(C) && C != WE )  C=C->next;
         if (dust(C))  break;
         if (!let_or_bad(C->prevl))  break;   //фиктивный в начале строки
         if (!is_stick(C) || !is_stick(C->prevl)) break;
       }
       else
         break;
     E=E->next;
     end_col=MAX(end_col,E->col+E->w);
   }
   if ( E==cell_l() )  break; // OLEG 08 dec 99
   B=process_frame(B,E);

 }
 while ( B != RW  ) ;

 return B;
}

/*-----------------02-20-95 06:41pm---------------------------------------
  process_frame - поиск и распознавание "плохих" участков, между
                  cell'ами WB и WE; возвращает WE->next
-------------------------------------------------------------------------*/
static cell *process_frame (cell *WB, cell *WE)
{
 cell *LW,*RW;//cell'ы слева и справа от слова
 cell *C;     //текущий cell
 cell *B,*E;  //начало и следующий за концом "плохого" участка
 cell *E2;    //исходное значение E, когда E сдвигается влево
 cell *E1,*E1P;//справа от конца интервала, обрабатываемого cut_glue
 INT   w,dh;  //его размеры
 INT      n;  //количество cell'ов в нем
 INT  maxcl;  //правый столбец + 1
 BYTE   let;  //лучшая версия cell'а
 INT      p;  //ее вероятность
 INT   h1,h2;

 LW=WB->prev; RW=WE->next;

 B=WB;
 do  // цикл по "плохим" участкам
 {

//   C=NULL;
   while ( let(B) || !may_glue(B) )      //ищем начало
   {
     B=B->next;
     if ( B==RW )
		 return B;
   };


   E=B->next;      //ищем конец
   h1=B->row; h2=h1+B->h;
   maxcl=B->col+B->w;
   for ( n=1; !let(E) && may_glue(E) && ( E != RW ); n++ )
   {
     h1=MIN(h1,E->row);
     h2=MAX(h2,E->row+E->h);
     maxcl=MAX(maxcl,E->col+E->w);
     w=maxcl-B->col;
     E=E->next;
   }
   dh=h2-h1;             //общая высота

   if ( n>1 )            //несколько кусков - клеим
   {
    BOOL dis=FALSE; // OLEG : SERBIAN PASS4


    if(!pass4_in)
      {
      if( language==LANG_RUSSIAN && langSer &&
        (B->flg&c_f_let) && B->nvers && (B->vers[0].let==SERB_j||B->vers[0].let=='j') &&
        ij_dot(B)==0 )
        dis = TRUE;
      }

     if( !dis )
     if ( dh<=RASTER_HEIGHT && w < dh + dh - (dh>>1) && w > minw )
       if ( (C=B->prev) == LW || !lefter(B,C->r_col+C->w) )
         if ( E == RW || lefter(E->prev,E->r_col) )
		 {
           if ( one_glue(n,&B,trs2) )
			   continue;  //дальше по слову
		 }
   }
   else   //n==1
   {
     if ( dust(B) )    //одиночный dust клеим в специальных случаях
     {
       INT bottom=B->row+B->h;        //нижняя граница dust'а
       INT middle=(B->row+bottom)>>1; //середина

       //направо
       C=B->next;
       if ( C != RW && !dust(C) && may_glue(C) )  //не конец слова
                                        //C - dust, если C - точка над "i"
         if ( (let=C->vers[0].let) != '!' && let !='?' )
         {
           INT midc=C->row+(C->h>>1);
           CHAR fl =  middle > C->row && bottom < midc &&
                      (let==(BYTE)'г' || let==(BYTE)'Г');
           if ( B->r_col+B->w > C->r_col || fl ) //C и dust перекрываются
           {                                     //или может быть "Т"("т")
             if ( !complete_recog(C) )
             {
               let_to_bad(C);
               full_recog(C,NULL,0,trs2);
             }
             p=C->vers[0].prob;
             //фора для развалившихся букв:
             if ( fl )  p -= 60; //Т,т
             else
               if ( middle < C->row &&
						let==(BYTE)'о' &&
							!is_russian_turkish_conflict(let) // 21.05.2002 E.P.
				)  p -= 50; //б
               else
                 if ( B->row > midc &&
					 let==(BYTE)'ч' &&
						!is_russian_turkish_conflict(let) // 21.05.2002 E.P.
					 )  p -= 30; //у,н

             if ( one_glue(2,&B,p) )
				 continue;  //дальше по слову
           }
         }

       //налево
       C=B->prev;
       if ( C != LW && !dust(C) && may_glue(C) )  //не начало слова
         if ( (let=C->vers[0].let) != '!' && let !='?' )
         {
           INT midc=C->row+(C->h>>1);
           CHAR fl = middle > C->row && bottom < midc && let=='1'; //может быть "Г"
           if ( fl || lefter(B,C->r_col+C->w) ) //может быть"Г"
           {                                    // или dust покрывается C
             if ( !complete_recog(C) )
             {
               let_to_bad(C);
               full_recog(C,NULL,0,trs2);
             }
             p=C->vers[0].prob;
             //фора для развалившихся букв:
             if ( fl || bottom < midc && let==(BYTE)'л'   //Г,х
                     || B->row > midc &&
						let==(BYTE)'ч' &&
							!is_russian_turkish_conflict(let) // 21.05.2002 E.P.
				) //н,у
               p -= 30;
             else
               if ( middle < C->row &&
					let==(BYTE)'о' &&
						!is_russian_turkish_conflict(let) // 21.05.2002 E.P.
				  )  p -= 50;  //б
               else
                 if ( (let==(BYTE)'г' || let==(BYTE)'Г') && B->row > C->row &&
                       middle < C->row+C->h   //п
                      ||
                      (let==(BYTE)'ь' || let==(BYTE)'Ь') && middle>C->row && middle<midc )//в
                          p -= 20;
             B=C;

             if ( one_glue(2,&B,p) )
				continue;  //дальше по слову
           }
         }
       B=E;
       continue;                 //dust не приклеился - пропускаем
     }
   }


//фюсрты хь  яю юфэюьє cell'є ёыхтр ш ёяЁртр, хёыш яхЁхъЁ√трхЄё  dust'юь
// ш (шыш) ёюфхЁцшЄё  т ёяшёъх
//ёыхтр:

   C=B->prev;  let=C->vers[0].let;
   if ( C != LW && !dust(C) && (may_glue(C) || let==(BYTE)'ь' || let==(BYTE)'Ь'))
   {
     CHAR  add=0;          //Їыру: фюсртшЄ№ ёшьтюы ёыхтр
     if ( dust(B) && lefter(B,C->r_col+C->w) )  //C яхЁхъЁ√трхЄё  dust'юь
     {
       if ( !complete_recog(C) )  //по 3x5, если не распознавалась
       {
         let_to_bad(C);
         full_recog(C,NULL,0,trs2);
       }
       if ( bad(C) || strchr(left_to_dust,let) )  add=1;
     }
     if ( !add && strchr(left_to_bad,let) &&
			!is_russian_baltic_conflict(let) &&	// 17.07.2001 E.P.
			!is_russian_turkish_conflict(let)	// 21.05.2002 E.P.
		)
     {
       if ( !complete_recog(C) )  //по 3x5, если не распознавалась
       {
         let_to_bad(C);
         full_recog(C,NULL,0,trs2);
       }
       add=1;
     }
     if (add)
     {
       B=C;  set_let(B);          //добавляем C, но резать не будем
       if ( let=='!' )
       {
         C=B->prev;
         if ( C != LW && dust(C) )
           if ( C->r_col+C->w > B->r_col )  B=C;  //точка от '!'
       }
     }
     else
       while ( dust(B) && lefter(B,C->r_col+C->w) && B != E )  B=B->next;
   }
   if (B==E)  continue;

//справа:

   E2=E; C=E->prev;
   if ( E != RW && may_glue(E) )
     if ( dust(C) && !lefter(C,E->r_col) &&
          strchr(right_to_dust,E->vers[0].let) ||
          strchr(right_to_bad,E->vers[0].let) &&
		   !is_russian_baltic_conflict(E->vers[0].let) &&	// 17.07.2001 E.P.
		   !is_russian_turkish_conflict(E->vers[0].let)		// 21.05.2002 E.P.
		)
     {
       maxcl=MAX(maxcl,E->col+E->w);
       if ( !complete_recog(E) )  //по 3x5, если не распознавалась
       {
         let_to_bad(E);
         full_recog(E,NULL,0,trs2);
       }
       set_let(E);  let=E->vers[0].let;
       E=E->next;
       if ( E != RW && let=='!' && dust(E) && E->r_col < maxcl )
         E=E->next;       //E - точка от '!'
       E2=E;
     }
     else                 //справа не добавляем
     {
       while ( dust(C) && !lefter(C,E->r_col) && C != B )  C=C->prev;
       E=C->next;
     }
   if ( E==B->next && dust(B) )  { B=E2; continue; }

//разрезание-склеивание

   E1P=NULL;
   while (1)     //по подинтервалам короче RASTER_WIDTH
   {
     while (1)
     {
       w=maxcl-B->col; E1=E;
       if ( w>RASTER_WIDTH ) //надо выделить подинтервал короче RASTER_WIDTH
       {
         INT right_col=MININT; INT right_col1=MININT;
         for ( E1=B; E1 != E; E1=E1->next )
         {
           right_col=MAX(right_col,E1->col+E1->w);
           if (right_col-B->col>RASTER_WIDTH) break;
           right_col1=right_col;
         }
         w=right_col1-B->col;
       }
       if ( E1 != E1P || B==E )  break;
     //получился тот же подинтервал, что в прошлый раз
       B=E1;                  //пропускаем его полностью
     }
     if ( w<minw || B==E ) break;
     C=B->prev;
     E1P=E1;
     B=cut_glue(C,E1,(CHAR)(E1 != E));
     if ( E1==E )  break;
     if ( B==NULL )  B=E1;
   }        //разрезание-склеивание закончено
   B=E2;
 }
 while ( B != RW ) ;
 return B;
}

/*************************************************************************/
/*    one_glue  пытается склеить n компонентов, начиная с *S, и          */
/*              распознает то, что получится:                            */
/*                если удачно (вероятность больше tol), возвращает 1     */
/*              и *S указывает на следующий за скомпонованным элементом, */
/*                если неудачно, возвращает 0 и *S указывает на          */
/*              первый нераспознанный элемент                            */
/*************************************************************************/

static INT one_glue (INT n, cell **S, INT tol)
{
  cell    *LB;   //cell слева от начала
  cell     *B;   //текущий cell
  s_glue  GL={0};//список склеиваемых компонент
  MN      *mn;   //промежуточный результат склейки
  INT     i;     //вспомогательные переменные
  INT nstick=0;  //число палок
  BYTE  fl_b=0;  //флаг "ь"
  INT  inc;      //усредненный наклон
  BYTE cg_flag=0;

  B=*S;
  glsnap('a',B,"glue begin");

  n=MIN(n,MAX_CELLS_IN_LIST-1);
  LB=B->prev;

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

  GL.ncell=(BYTE)n; GL.ngluc=1;
  for ( i=0; i<n; i++ )
  {
    cg_flag |= B->cg_flag;
    if(language == LANG_RUSSIAN && !dust(B))
      if (nstick<2)
        switch(fl_b)
        {
          BYTE let;

          case 0:       //первый раз
            if ( B->nvers && ((let=B->vers[0].let)==(BYTE)'ь' || let==(BYTE)'Ь') )
               {
			    fl_b=2;
			    break;
		       }
             else  fl_b=1;

          case 1:       //первый не "ь"
              nstick += is_stick(B);
          break;

          case 2:       //первый "ь" к "большому" не клеить
          nstick=2;
          break;
        }
      else
        nstick=3;
    GL.celist[i]=B;  GL.complist[i]=B->env;
    B=B->next;
  }
  inc = erection_compose_inc(n, GL.celist);

//клеим

  if ( nstick < 2 )
  {
    if (mn=glue(GL.complist, 2))
      if ( !mn->mnnext )
      {                              //склеилось
        B=create_my_cell(mn,LB,0,0);  //совокупный элемент
        if ( dust(B) )  goto ret;
        B->cg_flag=cg_flag;
        inter_diff(B);
        set_erection(B, inc);
        if ( full_recog(B,NULL,tol,tol) )
        {                    //принимаем результат
          B->cg_flag &= ~c_cg_cut;
          for ( i=0; i<n; i++ ) del_cell(GL.celist[i]);
          glsnap('a',B,"glued OK");
          *S=B->next;
          return 1;
        }
        goto ret;           //склейка неудачна
      }
  }

// распознавание без склейки

  B=comp_to_cell(LB,GL.complist,n,0,0);
  B->cg_flag=cg_flag;
  inter_diff(B);
  GL.var=GL.fres=GL.arg=0; GL.flarg=GFsrc;
  if ( !dust(B) && crecell(B,&GL,3) >=0 )  //совокупный растр - в BOX'е,
  {                                        //B не изменился
    B->broken_II = nstick==2;
    set_erection(B, inc);
    dmBOX(B,&GL);
    if ( B->nvers )
      if ( B->vers[0].prob>tol )
      {                      //принимаем результат
        for (i=0; i<n; i++) del_cell (GL.celist[i]);
        glsnap('a',B,"BOX OK");
        *S=B->next;
        return 1;
      }
  }

// неудача

ret:
  del_cell(B);
  glsnap('a',LB->next,"glue end");

  return 0;
}

/*-----------------02-21-95 06:28pm-----------------------------------
  cut_glue  ЁрёяючэрхЄ єўрёЄюъ ёЄЁюъш ьхцфє LC ш E (LC ш E эх тъы.);
            тючтЁр∙рхЄ E, хёыш ovfl == 0 ш єърчрЄхы№ эр cell, ъєфр эрфю
            тючтЁр∙рЄ№ё , хёыш ovfl != 0; хёыш Ёрёяючэртрэшх эх
            яЁютюфшыюё№ тючтЁр∙рхЄ NULL
---------------------------------------------------------------------*/
static cell *cut_glue (cell *LC, cell *E, CHAR ovfl)
{
  INT i,j;
  cell *C;             //рабочий cell
  cell *B=LC->next;    //начало
  cell_list org_cells; //исходные cell'ы
  raster r;       //совокупный растр
  INT st_inc;          //средний наклон
  INT ncut;            //общее число сечений
  struct cut_elm cut_list[MAX_CUTS],*cut;  //список сечений и текущее сечение
  struct dp_vers_struct vers_pool;   //куча версий dp
#define vers_list vers_pool.node[0]
  INT pass;       //номер прохода
  void *kita;     //указатель стека kit
  INT b4=my_bases.b2+(my_bases.ps<<1);

  snap_newcut();
  glsnap('a',B,"cut/glue begin");

#ifdef PROTOCOL
//  clock_on();
//  rec_count=0;
#endif

#pragma warning ( disable : 4047 4024 )

  memset(&vers_pool.node,0,MAX_SEG_VERS*sizeof(seg_vers *));
  vers_list=&vers_pool.pool;

  //наклон для всех

  for (i=0; i<MAX_SECT && B != E; i++,B=B->next)
	  org_cells.cells[i]=B;

  st_inc = erection_compose_inc(i, org_cells.cells);

  //рассыпаем составные cell'ы

  B=LC->next;
  while ( B != E )
  {
    if (!(B=break_cell(B)))
    {
      if (bad(B)) set_let(B); //не рассыпалась - не режем
      B=B->next;
    }
  }

  //склеиваем налагающиеся друг на друга

  if (!glue_overlap(LC,E))
    return NULL;

  //список org_cells

  B=LC->next;
  for ( j=0,C=B; C != E; C=C->next )
  {
    if ( dust(C) )
    {
      INT mid=C->row+(C->h>>1);
      if ( C->h>my_bases.ps>>1 && mid<my_bases.b1 || mid>b4 )  continue;            //с соседней строки
    }                                             //с соседней строки

    if ( j>=MAX_SECT )
    {
      glsnap('a',B,"too many cells");
      return NULL;
    }
    org_cells.cells[j++]=C;
  }

  if ( j==0 )
	  return NULL;
  if ( j==1 && org_cells.cells[0]->w<=cut_width )
  {
     glsnap('a',E,"cut/glue end");  return NULL;
  }
  org_cells.N=j;


//построение растра

  kita=give_kit_addr();
  if (!compose_raster(&r,&org_cells.cells,org_cells.N) || r.w>RASTER_WIDTH)
    { glsnap('a',B,"raster too large"); return NULL;}

//список сечений и начальные значения


  ncut=init_dp(cut_list,&vers_list,&org_cells,r.left,(INT)(r.top+r.h-1));

  cut_list->rv.v2=st_inc;
  for (i=0; i<org_cells.N; i++)
    org_cells.cells[i]=(cell *)del_save_cell(org_cells.cells[i]);


#ifndef MY_DEBUG
  if (!det_trace)  cg_show_rast(LC,&r,"",cut_list); //растр - на экран
#endif

  for ( i=2,cut=cut_list+1; i<ncut; i++,cut++ )
    cut->var |= NOT_CUT;  //не резать

  if (language==LANG_RUSSIAN)  dp_pass0(LC,&r,cut_list,&vers_list,ncut);
  else
    for ( pass=1; pass<=3; pass++ )
      if (dp_passi(LC,&r,cut_list,&vers_list,ncut,pass))  break;

  snap_stop_cuts();

  if (debug_on)
#ifndef MY_DEBUG
  if (det_trace)
#endif
  {
    CHAR snap[500]="DP end";
    *show_dp(snap+6,cut_list,(INT)(ncut-1))=0;
    snap_newcell(LC);
    snap_show_text(snap);
    snap_monitor();
  }
/*
  {
    INT t=clock_read();
    wr_prot ("time",'d',t);
  }
*/

#ifdef PROTOCOL
//  wr_prot ("numrecog",'d',rec_count_let(rec_count,cut_list,ncut)*10);
#endif

//восстановление cell'ов оптимального пути

  C=recover_path(kita,&r,cut_list,ncut,LC,E,ovfl);

  if ( C )
  {
//    INT p=low_prob(cut_list,ncut-1);
//    if (p != 255) wr_prot ("lowprob",'i',p);
    glsnap('a',E,"cut/glue end");
    if (ovfl && C != E)  glsnap('a',C,"return to cell displayed");
  }
  else
  {
    for (i=0; i<org_cells.N; i++)  rest_cell(org_cells.cells[i],E);
    glsnap('a',E,"path not recovered");
  }

  return C;
#undef vers_list
}

/*------------------------------------------------------------------
  break_cell - рассыпает составной cell на компоненты связности;
               возвращает указатель на следующий (до рассыпания)
-------------------------------------------------------------------*/
static cell *break_cell(cell *B)
{
  MN *mn1;
  BYTE *rp1;                   //промежуточный растр
  cell *C=NULL,*RC=B->next;    //AK C=NULL

  if (!composed(B))  return RC;

  det_snap(B,"cell fuse");
  rp1=(BYTE*)save_raster(B);
  if (!(mn1=c_locomp(rp1,(INT)((B->w+7)>>3),B->h,B->r_row,B->r_col)))
  {
    det_snap(B,"locomp error"); return NULL;
  }
  while (mn1)
  {
    C=create_my_cell(mn1,B,0,0);
    set_just(C);
    inter_diff(C);
    mn1=mn1->mnnext;
  }
  del_cell(B);
  return RC;
}

/*-----------------03-01-95 04:52pm---------------------------------
  compose_raster  по адресу raster формирует растр N cell'ов, заданных
                  списком clist, и вычисляет размеры растра w,h и ко-
                  ординаты ur,lc левого верхнего угла;
                  возвращает 1, если успешно, и 0, если нет
-------------------------------------------------------------------*/
static INT compose_raster(raster *r, cell **clist, INT N)
{
  cell *c;
  BYTE *rp1;                   //промежуточный растр
  c_comp *elist[MAX_CUTS];
  INT i,top,bot,left,right;

//объединяем все cell'ы в один
//вычисляем размеры и координаты растра

  c=new_cell();
 for (top=left=10000,bot=right=-10000,i=0; i<N && i<MAX_CUTS; i++)
  {
  if (clist[i]->r_row<top)
   top=clist[i]->r_row;
  if (clist[i]->r_row+clist[i]->h>bot)
   bot=clist[i]->r_row+clist[i]->h;
  if (clist[i]->r_col<left)
   left=clist[i]->r_col;
  if (clist[i]->r_col+clist[i]->w>right)
   right=clist[i]->r_col+clist[i]->w;
  elist[i]=clist[i]->env;
  }
 c->r_row=top;
 c->h=bot-top;
 c->r_col=left;
 c->w=right-left;
 c->row=c->r_row-(INT)((LONG)nIncline*c->r_col/2048);
 c->col=c->r_col+(INT)((LONG)nIncline*c->r_row/2048);
  insert_cell1(c,clist[0]);
  if ( c->w > 128 || c->h > 63 )
  {
    del_cell(c); return 0;
  }
 c->env=compose_comp(i,elist);

//формируем растр

  rp1=(BYTE*)save_raster(c);
  memcpy(r->pict,rp1,((c->w+7)>>3)*c->h);
  r->w=c->w; r->h=c->h; r->top=c->r_row; r->left=c->r_col;
  del_cell(c);

  return 1;

}

/*-----------------03-02-95 01:22pm----------------------------------
  init_dp  формирует список сечений для цепочки list, список включает
           левую и правую границы цепочки; положения сечений определя-
           ются относительно левой rastlc и нижней rastdr границ сово-
           купного растра; формирует начальный путь, задаваемый cell'
           ами из list;
           возвращает число сечений
---------------------------------------------------------------------*/
static INT init_dp(struct cut_elm *cut_list, seg_vers **vers_list,
                   cell_list *list, INT rastlc, INT rastdr)
{
  CHAR x;
  INT i,j,ro;
  INT  nc;        //количество сечений cell'а
  INT ncut=1;     //общее число сечений
  INT dust_sect=0;//флаг: сечение из dust'ов
  INT mincl=MAXINT,maxcl=rastlc;  //мин. и мах. оценки для правого края dust-секции
  struct cut_elm *seci=cut_list+1; //текущее сечение
  SVERS                    *versi; //его версии
  cell *C,box;    //используются только row, col, w, h - размеры коробки
  memset(&box,0,sizeof(cell));

/* dust-секция формируется, если:
 - существует dust, который не покрывается соседними "большими" компонентами
 - dust по расположению походит на точку или запятую  */

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

  fict_sect(cut_list,0,0);  //0-е фиктивное сечение
  for ( i=0; i<list->N; i++ )
  {

    C=list->cells[i];
    if ( dust(C) )
    {

      if ( C->r_col>=maxcl )
      {
        if ( dust_sect )
        {                  //закрываем старую dust-секцию
          close_ds(seci,(INT)(maxcl-rastlc),(INT)(ncut-1));
          ncut++; seci++;
          if ( ncut==MAX_CUTS ) {dust_sect=0; break;}
        }
        else
          (seci-1)->x=maxcl-rastlc;
                           //открываем новую dust-секцию
        dust_sect=1; mincl=C->r_col+C->w;
      }
      else
        if ( !dust_sect    //dust перекрывается "большим" компонентом
             && C->r_col+C->w>maxcl
             && C->row+C->h<my_bases.b3+(my_bases.ps>>1)
             && ( C->row>my_bases.bm || is_defis(C) )
           )               //может быть точка или запятая
        {                  //открываем dust-секцию
          (seci-1)->x=((seci-1)->x+(C->r_col-rastlc))>>1;
          dust_sect=1; mincl=maxcl=C->r_col+C->w;
        }
      maxcl=MAX(maxcl,C->r_col+C->w);
      mincl=MIN(mincl,C->r_col+C->w);
    }
    else  //не dust
    {

      if ( dust_sect )
      {
        dust_sect=0;
        if ( mincl<=C->r_col )          //закрываем  dust-секцию
        {
          if ( maxcl>C->r_col ) x=mincl-rastlc;
          else                  x=maxcl-rastlc;


          close_ds(seci,x,(INT)(ncut-1));
//          close_ds(seci,MAX(mincl,C->r_col-1)-rastlc,ncut-1);
//          close_ds(seci,MIN(maxcl,C->r_col-1)-rastlc,ncut-1);
          ncut++;
		  seci++;

          if ( ncut==MAX_CUTS )
			  break;
        }
        else
		{
			if (mincl==maxcl && ((seci-1)->x + rastlc + mincl)>>1 < C->r_col)
			{
				//закрываем  dust-секцию
			  close_ds(seci,(INT)(mincl-rastlc),(INT)(ncut-1));
			  ncut++;
			  seci++;

			  if ( ncut==MAX_CUTS )
				  break;
			}
//        else                            //dust-секция анулируется
//          (seci-1)->x=((seci-1)->x+C->r_col-rastlc)>>1;
		}
      }
      else  // !dust_sect
	  {

        if ( maxcl>C->r_col )           //внахлест
        {
//          x=((seci-1)->x+C->r_col-rastlc)>>1;
//          ro=middle(C)-rastlc;
//          (seci-1)->x=MIN(x,ro);
          if ( lefter(C,(seci-1)->x+rastlc) )  //перекрывается предыдущим
          {                        // "большим" - обходимся как с dust'ом
            maxcl=MAX(maxcl,C->r_col+C->w); continue;
          }
          else
            (seci-1)->x=((seci-1)->x+C->r_col-rastlc)>>1;
        }
        else
          (seci-1)->x=maxcl-rastlc;
	  }

      maxcl=MAX(maxcl,C->r_col+C->w);
      nc=0;
      if ( bad(C) &&
           ( C->w > cut_width ||
             C->r_col < (seci-1)->x+rastlc ||
             C->r_col+C->w > C->nextl->r_col ) //перекрывается с соседями
         )
      {
		  //режем
        nc=get_cuts(C,seci,(INT)(MAX_CUTS-ncut-1));

        for ( j=ncut; j<ncut+nc; j++ )         //корректируем на положение
          cor_sect(C,&cut_list[j],rastlc,rastdr);  //cell'ов в растре

        ncut+=nc;
		seci+=nc;
      }

      nc=ncut-nc-1;                     //сечение слева от C
      fict_sect(seci,(INT)(C->r_col+C->w-rastlc),nc);
      if (nc==0)
      {
        cut_list->gvarm = C->cg_flag;          //признак разреза
        cut_list->gvarr = C->cg_flag_fine;     //тип разреза
      }
      seci->lv.v2 = C->row;
      seci->lv.v3 = C->col;
      seci->rv.v2 = C->row+C->h;
      seci->rv.v3 = C->col+C->w;
      ncut++;
	  seci++;

      if ( ncut==MAX_CUTS )
		  break;
    }

  }

  if ( dust_sect )                 //последняя секция -  dust-секция
  {
    close_ds(seci,(INT)(maxcl-rastlc),(INT)(ncut-1));
    ncut++;
  }
  else
  {
    ro=maxcl-rastlc;  (seci-1)->x=MIN(ro,127);
  }
  cut_list->x=0;      //могла испортиться при открывании или аннулировании
                      //первой dust-секции

//распределяем cell'ы  по сечениям

  for ( j=0; j<list->N; j++ )
  {
    C=list->cells[j];
    for ( i=1,seci=cut_list+1; i<ncut; i++,seci++ )
      if ( seci->dh == 0 && lefter(C,seci->x+rastlc) ) break;
    if ( i==ncut ) continue;        //разрезов не хватило

    seci->duflm++;                  //число компонент в сегменте
    if (dust(C))
    {
      if ( is_defis(C) )
      {
        C->nvers=1; C->vers[0].let='-';  save_vers(C,&seci->versm);
      }
    }
    else
      if (seci->rv.v1++)  // больше 1 "большого" - аннулируем версии
      {
        seci->versm.flg=0;  set_bad_vers(&seci->versm);
      }
      else
        if (!just(C)) save_vers(C,&seci->versm);//just(C) еще не распознавался
    seci->lv.v2 = MIN(seci->lv.v2,C->row);
    seci->lv.v3 = MIN(seci->lv.v3,C->col);
    seci->rv.v2 = MAX(seci->rv.v2,C->row+C->h);
    seci->rv.v3 = MAX(seci->rv.v3,C->col+C->w);
    if (seci->px==0) cut_list->gvarm |= C->cg_flag & c_cg_cutl;
    if (i=ncut-1)    cut_list->gvarm |= C->cg_flag & c_cg_cutr;
  }

//вычисляем меры

  for ( i=1; i<ncut; i++ )
  {
    seci=cut_list+i;
    versi=&seci->versm;
    if ( seci->dh == 0 )    //граница исходного cell'а
    {
      CHAR isdust;

      C=&box;
      C->w=seci->rv.v3-seci->lv.v3;
      C->row=seci->lv.v2;
      C->col=seci->lv.v3;
      C->h=seci->rv.v2-seci->lv.v2;
      if (--seci->duflm)    //есть сопутствующие dust'ы
        isdust=C->row>my_bases.bm || C->row+C->h<my_bases.b2 ||
               if_dust(C)&0xC;
      else
        isdust=dust(versi);
      if (isdust)
        if (C->w<minp)       ro=0;
        else                 ro=norm(PASS_PEN,my_bases.ps);
      else
        if ( versi->nvers )  ro=norm(loc_err(versi),C->w);
        else                 ro=norm(MAX_RO,C->w);
      seci->rv.v1=ro;
      seci->rv.v2=C->w;
      seci->lv.v1=cut_list[seci->px].lv.v1+ro;
      if (versi->flg != 0)           //распознавался - сохраняем
      {
        (*vers_list)->px=seci->px;
        if (seci->duflm)  ro=-ro;    //чтобы распознавать с dust'ами
        (*vers_list)->ro=ro;
        (*vers_list)->gvar=0;
        (*vers_list)->width=C->w;
        memcpy(&(*vers_list)->vers,versi,sizeof(SVERS));
        (*vers_list)->next=*(vers_list+i);
        *(vers_list+i)=*vers_list;
        (*vers_list)++;
      }
      seci=cut_list+(seci->px);
      seci->rv.v3=C->col-(seci->lv.v3+seci->rv.v2); //ширина просвета
    }
    else                  //разрез исходного cell'а
    {
      seci->lv.v1=seci->rv.v1=MAXINT;  //мера сегмента (0,i)
      seci->rv.v3=0;       //ширина просвета в i-том сечении
    }
  }
  seci=cut_list+ncut;
  (seci--)->x=127;
  seci->rv.v3=MAX_COL;
  cut_list[0].rv.v3=MAX_COL;
  cut_list[0].rv.v1=seci->lv.v1;    //мера всего пути
  return ncut;
}

/*-----------------02-28-95 01:51pm------------------------------------
  fict_sect  устанавливает сечение между элементами
             (разрез фактически не производится);
---------------------------------------------------------------------*/
static void fict_sect(struct cut_elm *cut, INT x, INT px)
{
//   init_sect(cut);
//   cut->x=x; cut->dh=0; cut->h=0;  cut->px=px;
  memset(cut,0,sizeof(struct cut_elm));
  cut->x=MIN(x,127); cut->px=(CHAR)px;
  cut->lv.v2=cut->lv.v3=MAXINT;  cut->rv.v2=cut->rv.v3=MININT;
}

/*-----------------03-09-95 02:34pm----------------------------------
  init_sect  начальные значения для описателя сечения
-------------------------------------------------------------------*/
static void init_sect(struct cut_elm *cpnti)
{
  cpnti->px=0;                 //предыдущее сечение на оптимальном пути
  cpnti->lv.v1=cpnti->lv.v3=0; //накопленная мера
  cpnti->lv.v2=0;              //длина пути
  cpnti->rv.v1=0;
  cpnti->var=0; cpnti->x=127;
  cpnti->duflm=cpnti->duflr=cpnti->gvarr=cpnti->gvarm=0;
  cpnti->versm.nvers=cpnti->versm.flg=0;
}

/*-----------------05-23-95 12:28pm----------------------------------
  close_ds  закрывает dust-секцию, заполняя сечение *cut;
            x - кооордината сечения cut в растре;
            px - номер сечения - начала dust-секции;
-------------------------------------------------------------------*/
static void close_ds(struct cut_elm *cut, INT x, INT px)
{
  fict_sect(cut,x,px);
//  memset(&cut->versm,0,sizeof(SVERS));
  set_dust(&cut->versm);  set_bad_vers(&cut->versm);
  cut->duflr=1;
}

/*-----------------02-28-95 02:14pm------------------------------------
  cor_sect  корректирует положение разрезов cell'а *C относительно
            совокупного растра с координатами left и down
----------------------------------------------------------------------*/
static void cor_sect(cell *C, struct cut_elm *cut, INT left, INT down)
{
   cut->x+=C->r_col-left;
   cut->h+=down-(C->r_row+C->h-1);
}

/*-----------------02-23-95 04:13pm--------------------------------------
  get_cuts  формирует список разрезов для cell'а (максимум nmax);
            возвращает число отобранных разрезов (>=0)
-----------------------------------------------------------------------*/
INT get_cuts (cell *C, struct cut_elm *list, INT nmax )
{
  INT i;             //параметр цикла
  INT mw,mh,mrr,mrc; //размеры и координаты элемента
  BYTE *rp1;         //растр для поиска сечений
  INT  N0,N;         //количество сечений: всех и отобранных
  struct cut_elm list0[MAX_CUTS];  //полный список сечений
  struct cut_elm *cpnti,*spnti;    //исходные и отобранные сечения
  CHAR x_prev,v_prev; //координата и тип разреза предыдущего сечения
//  BYTE raster[RASTER_WIDTH*RASTER_HEIGHT/8];  //растр для разрезания
  MN *mn1;
  BYTE csv[32];       //параметры сечения
  cut_pos cpos;

 //начальные значения

 mh=C->h; mw=C->w; mrr=C->r_row; mrc=C->r_col;

  if( mw<1 )
     return 0;  // OLEG: 16-04-2003

 for (i=0,cpnti=list0; i<MAX_CUTS; i++,cpnti++) init_sect(cpnti);

//вычисление точек разреза

 rp1=(BYTE*)save_raster(C);

// clock_on();


 if( !Alik )
  N0=cut_points(mw, mh, rp1, list0);
 else
  N0=Alik_cut_points(mw, mh, rp1, list0, C->row); // ALIK : 01-31-96 05:50pm


// t=clock_read();
// wr_prot ("time",'d',t);

 N0 = MIN (N0,MAX_CUTS);

//отбираем допустимые

 x_prev=list0->x; v_prev=list0->var;
 for (i=1,                          //0-й разрез фиктивный
      N=0,spnti=list,cpnti=list0+1; i<=N0 && N<nmax; i++,cpnti++)
 {

   if ( cpnti->x >= C->w-2 )  continue;          //близко к краю
   if ( cpnti->x <= 1 )       continue;          //близко к краю
   mn1=cut_rast(rp1,mw,mh,mrr,mrc,list0,(CHAR)i,2,csv,&cpos); //режем
   if ( mn1 )
   {
     memcpy(spnti,cpnti,sizeof(struct cut_elm));
     if ( !mn1->mnnext )
     {                           //не разрезалось,
       spnti->var |= 32;         //устанавливаем полный разрез
       spnti->h=spnti->dh=(CHAR)mh;
       if ( spnti->x != x_prev || (spnti->var^v_prev) & 40 )
         { N++; spnti++; }       //не повторяет предыдущий - включаем
     }
     else
       { N++; spnti++; }
     x_prev=cpnti->x;  v_prev=cpnti->var;
   }
   cut_rast(rp1,mw,mh,mrr,mrc,list0,(CHAR)i,0,csv,&cpos); //клеим обратно
 }
 return N;
}

/*-------------------------------------------------------------------
  dp_passi  i-ый проход ДП: хорошие изолированные сегменты справа и
            слева пропускаем, на плохих - все разрезы; ncut - число
            точек в cut_list; остальные параметры для glob_measure
-------------------------------------------------------------------*/
static CHAR dp_passi(cell *LC, raster *r, struct cut_elm *cut_list,
                     seg_vers **vers_list, INT ncut, INT pass)
{
  INT i,j;
  INT ib=0,ie=ncut-1;             //начало и конец ДП

  dp_bound(cut_list,vers_list,pass,&ib,&ie);
  if (ie<=ib)  return 1;                  //ДП закончено

#ifndef MY_DEBUG
  if (det_trace)
#endif
  {
    sprintf(snap_text,"pass=%d",pass);
    cg_show_rast(LC,r,snap_text,cut_list); //растр - на экран
  }

//вычисляем меры

  for ( i=ib+1; i<=ie; i++ )
  {
    if ( !ben_cut(cut_list+i) )  //разрешен на данном проходе
    {
      snap_newpoint(i);
      for (j=i-1; j>=ib; j--)
        if ( !ben_cut(cut_list+j) )
          if (addij(LC,r,cut_list,vers_list,ncut,j,i,0) & 8)  break; //8-широкий
    }
  }
  return 0;
}

/*--------------------------------------------------------------------------
  dp_bound  юяЁхфхы хЄ ыхтє■ ib ш яЁртє■ ie уЁрэшЎ√ ─╧ ш ЁрчЁх°рхЄ ЁрчЁхч√;
            cut_list - ёяшёюъ ncut ёхўхэшщ;
            vers_list - ёяшёюъ Ёхчєы№ЄрЄют фы  ърцфюую ёхўхэш ;
            pass - эюьхЁ яЁюїюфр ─╧;
--------------------------------------------------------------------------*/
void dp_bound(struct cut_elm *cut_list,seg_vers **vers_list,
                     INT pass, INT *ib, INT *ie)
{
  INT i,ip,ibp,i1,i2,i2p,et,type,x,ie1=*ie,ib1=*ib;
  struct cut_elm *cut,*cut1;
  SVERS *versi;   //версии i-го сечения
  BYTE let,let1;
  CHAR  resstr[RESLEN],*res; //ожидаемый результат объединения сегментов

  for ( i=ib1; i<ie1; i++ )  cut_list[i].var |= NOT_CUT;  //не резать

  {
    ip=i; *ie=ibp=0;
    i2=i2p=0; //последний хороший в текущем и предыдущем сегментах
    while ( i>ib1 )
    {
      //определяем тип изолированного сегмента:
      // 0 - хороший, 1 - плохой, 2 - dust-секция
      type=0; i1=i; cut=cut_list+i;
      et=0;                      //правый край "г"
      do
      {
        seg_vers *cur_vers;
        cut->var &= ~NOT_CUT;    //разрешаем разрез по оптимальному пути
        versi=&cut->versm;
        if ( dust(versi) )
        {
          if (pass==1) type=1;
        }
        else
          if ( bad(versi)
               ||
               !( versi->nvers>0 && versi->vers[0].prob>trs2 )
               ||                //не распознавался с dust'ами
               (cur_vers=find_vers(cut->px,i,vers_list)) && cur_vers->ro<0
             )  type=1;
          else
          {
            let=versi->vers[0].let;
            cut1=cut_list+cut->px;
            switch(let)
            {
              struct cut_elm *cuti;
              CHAR weakp;

              case '|':
              case (BYTE)'ы': case (BYTE)'Ы':
              if (pass==1 && strchr("оОсCцЦ",	// "ю╬ёCЎ╓"
								(cut_list+ip)->versm.vers[0].let) &&
                                //"№■","√ю" ш Є.я.
				  !is_russian_baltic_conflict((cut_list+ip)->versm.vers[0].let) &&	// 17.07.2001 E.P.
				  !is_russian_turkish_conflict((cut_list+ip)->versm.vers[0].let)	// 21.05.2002 E.P.
				 )
              {
                if (et==0) et=ip;
                (cut_list+ip)->var &= ~NOT_CUT;       //можно резать
                if (let=='|')
                {                //разрезы вокруг "ь"
                  cut1->var &= ~NOT_CUT;
                  if (cut1 != cut_list)
                    (cut_list+cut1->px)->var &= ~NOT_CUT;
                }
                else             //резать "ы"
                  for (cuti=cut1; cuti<cut; cuti++)  cuti->var &= ~NOT_CUT;
              }
              break;

              case (BYTE)'ю':
				  if (is_russian_turkish_conflict(let)) // 21.05.2002 E.P.
					  break;
			  case (BYTE)'Ю':
              if ( cut1->dh != 0 &&
                   ((let1=cut1->versm.vers[0].let)==(BYTE)'п' || let1==(BYTE)'П') &&
                   not_connect_sect(cut->px,i,cut_list)
                 )  type=1;
              break;

              case (BYTE)'г': case (BYTE)'Г':
              if ( cut1->dh != 0 )  //"г"->"т"
              {
                if (et==0) et=i;
                cut->var &= ~NOT_CUT;       //можно резать
                (cut_list+cut1->px)->var &= ~NOT_CUT;
                x=cut->x;                   //конец "г"
                x=cut1->x-((x-cut1->x)>>1); //возможное начало "т"
                cuti=cut1-1;
                while (cuti->dh != 0 && cuti->x>x)
                {
                  if (point_fit(cuti,pass))  cuti->var &= ~NOT_CUT;
                  cuti--;
                }
              }
              i2=i;
              break;

              case (BYTE)'п': case (BYTE)'П':   //только двойные разрезы внутри -
              weakp=0;              // "п" ненадежное
              for (cuti=cut1+1; cuti<cut; cuti++)
                if (cuti->var & 40)   weakp=1;
                else                { weakp=0;  break; }
              if (weakp)  type=1;
              else        i2=i;
              break;

              default:  i2=i;
            }
          }
        ip=i;
        i=cut->px;               //следующий на оптимальном пути
        cut=cut_list+i;
      }
      while ( cut->dh != 0 && i>ib1 );
      if ( i == (cut_list+i1)->px && dust(versi) )
        type = (pass==1) ? 1 : 2;
      if (i<ib1)  { i=ib1; cut=cut_list+ib1; }

      //плохой можно резать, хороший - нельзя
      if (type)
        for ( cut1=cut_list+i1; cut1>=cut; cut1-- )
          if (point_fit(cut1,pass))  cut1->var &= ~NOT_CUT;  //можно резать

      //устанавливаем границы ДП
      switch ( type )
      {
        case 0: if (et != 0)
                {
                  if (*ie==0) *ie=et;
                  *ib=i; ibp=ip;
                }
                break;
        case 1: *ib=i; ibp=ip;
                if ( *ie==0 )
                  if ( i2p )
                  {
                    *ie=(cut_list+i2p)->px;
                    res=unite_list(resstr,(cut_list+*ie)->px,i2p,
                                   cut_list,vers_list);
                    if ( !res || strlen(res) != 0 )  *ie=i2p;
                    (cut_list+*ie)->var &= ~NOT_CUT;  //можно резать
                  }
                  else    *ie=ie1;
                break;
        case 2: if (*ib==i1)  {  *ib=i; ibp=ip;  }
      }
      i2p=i2;
    } //end while i>0
/*
    if (pass==1)
      for (cut=cut_list+(*ib+1),cut1=cut_list+*ie; cut<cut1; cut++)
        if (cut->dh==0)  cut->var &= ~NOT_CUT;
*/
    //добавляем слева хороший из списка

    if ( *ib > ib1 )
    {
      cut=cut_list+(*ib);  i1=cut->px;
      if (i1>=ib1 && strchr(letters_left_to_bad,cut->versm.vers[0].let) &&
		  !is_russian_baltic_conflict(cut->versm.vers[0].let) &&// 17.07.2001 E.P.
		  !is_russian_turkish_conflict(cut->versm.vers[0].let)	// 21.05.2002 E.P.
		  )
      {
        *ib=i1;  cut_list[*ib].var  &= ~NOT_CUT;  //можно резать
      }
    }
  }
}

/*-----------------04-30-96 11:00am----------------------------------
  addij ЁрёяючэрхЄ ёхуьхэЄ (i1,i0), хёыш х∙х эх Ёрёяючэртрыё  (яЁш
        mode=2 эхчртшёшью юЄ ьхЁ√ т i1) ш ъюЁЁхъЄшЁєхЄ эръюяыхээє■
        ьхЁє ёхўхэшщ шч ёяшёър *cut_list (ncut Єюўхъ) ЁрёЄЁр *r0;
        тючтЁр∙рхЄ ъюф чртхЁ°хэш  accept_segment
        vers_list - ьрёёшт єърчрЄхыхщ эр ёяшёюъ шьх■∙шїё  Ёхчєы№ЄрЄют фы 
        ърцфюую ёхуьхэЄр;
        *C чрфрхЄ юсЁрсрЄ√трхьє■ ёЄЁюъє
--------------------------------------------------------------------*/
BYTE addij(cell *C, raster *r0, struct cut_elm *cut_list,
           seg_vers **vers_list, INT ncut, INT i1, INT i0, CHAR mode)
{
  INT i,j,ro;
  INT ie=ncut-1;
  BYTE let1;
  seg_vers *cur_vers;
  struct cut_elm *seci0,*seci,*secj; //описатели сечений
  BYTE let0;       //текущая версия i0-го сечения
  CHAR msg[500],*s;
  BYTE err=accept_segment(C,r0,cut_list,vers_list,i1,i0,mode);

  if (err) return err;

//корректируем накопленные меры

  seci0=cut_list+i0;
  seci0->gvarr=1;                         //версии изменились
  for (seci=cut_list+ie-1; seci>seci0; seci--)  seci->gvarr=0;
  let0= (seci0->versm.nvers) ? seci0->versm.vers[0].let : 0;

  for (i=i0+1,seci=cut_list+i; i<=ie; i++,seci++)
  {
    CHAR flngp;
    INT ip=seci->px;
    seci->gvarr=(cut_list+ip)->gvarr;
    let1= (seci->versm.nvers) ? seci->versm.vers[0].let : bad_char;
    if ( ip>=i0)
    {
      if ( ip==i0 &&
           ( let0==(BYTE)'ь' && let1 != (BYTE)'|' &&
             (seci->x-seci0->x) < (seci0->x-(cut_list+seci0->px)->x)
             ||
             let1==(BYTE)'|' && let0 != (BYTE)'ь'
         ) )
      {                   //текущая versi должна быть заменена
        INT width=seci->x-seci0->x;
        width=MAX(width,my_bases.ps);
        seci->rv.v1=norm(MAX_RO,width);
        seci->lv.v1=seci0->lv.v1+seci->rv.v1;
        if (accept_segment(C,r0,cut_list,vers_list,i0,i,1)==0)
        {                            //версия заменена
          let1= (seci->versm.nvers) ? seci->versm.vers[0].let : bad_char;
        }
      }
      else
        seci->lv.v1=(cut_list+ip)->lv.v1+seci->rv.v1;
    }
    flngp = strchr("гГпП",let1) == NULL;
    for (j=i-1,secj=cut_list+j; j>=i0; j--,secj--)
    {
      INT cur_meas;
      if (!secj->versm.flg)  continue;         //из j нет пути
      if (seci->x-secj->x > r0->h<<1)  break;
      if (!secj->gvarr ||                      //версии не изменились
          !(cur_vers=find_vers(j,i,vers_list)) //не распознавался
         )  continue;
      if ( flngp || j>=ip)
        if ( secj->lv.v1>seci->lv.v1 || secj->lv.v1>cut_list->rv.v1 )
          continue;                            //оценку не улучшить

      ro=abs(cur_vers->ro);
      cur_meas=secj->lv.v1+ro;         //мера i-го сечения
      if ( accept_vers(msg,cut_list,j,i,&cur_vers->vers,cur_vers->gvar,
                       cur_vers->width,cur_meas) )
      {
        seci->lv.v1=cur_meas;
        seci->rv.v1=ro;
        seci->rv.v2=cur_vers->width;
        seci->px=(CHAR)j;
        seci->gvarm=cur_vers->gvar;
        memcpy(&seci->versm,&cur_vers->vers,sizeof(SVERS));
        seci->gvarr=1;                           //версии изменились
        let1= (seci->versm.nvers) ? seci->versm.vers[0].let : bad_char;
        flngp = strchr("гГпП",let1) == NULL;
      }
    }
  }
  cut_list->rv.v1=(cut_list+ie)->lv.v1;
  if (debug_on)
#ifndef MY_DEBUG
    if (det_trace)
#endif
    {
      s=msg+sprintf(msg,"measures corrected");
      *show_dp(s,cut_list,(INT)(MIN(i0+10,ie)))=0;
      show_and_wait(msg);
    }
  return 0;
}

/*----------------------04-30-96 02:03pm-----------------------------
  accept_segment - распознает сегмент (i1,i0) растра *r0, заданный
                   списком *cut_list;
                   mode=XX
                        │└─игнорировать прежние версии сегмента
                        └──игнорировать i1-ю меру;
                   vers_list - массив указателей для записи результатов;
                   *C задает обрабатываемую строку;
                   код завершения - сумма значений:
                   0 - версия принята             8 - слишком широкий
                   1 - уже распознан             16 - из i1 нет пути
                   2 - результата нет в списке   32 - хуже мера
                   4 - слишком узкий
-------------------------------------------------------------------*/
static BYTE accept_segment(cell *C, raster *r0, struct cut_elm *cut_list,
                           seg_vers **vers_list, INT i1, INT i0, CHAR mode)
{
  INT i,dh,fora;
  BYTE let;
  cell **CI;
  seg_vers *cur_vers;
  BYTE cut_fl;    //флаг: сегмент (i1,i0) получен разрезанием
  BYTE cut_fine;  //то же для Алика
  struct cut_elm *seci0,*seci1; //описатели i0-го и i1-го сечений
  SVERS *versi0,*versi1;        //версии i0-го,i1-го сечений
  INT x0,x1;       //координаты i0-го и i1-го сечений
  INT x0a,x1a;     //то же абсолютные
  SVERS vers;      //версии сегмента (i1,i0)
  INT width;       //его ширина
  CHAR gvar;       //способ сборки
  INT ro,cur_meas; //новые локальная и глобальная текущие меры i0-го сечения
#define ROI_LEN 4
#define ROI_SIZE ROI_LEN*sizeof(INT)
  INT roi[ROI_LEN]; //составляющие ro
  BYTE csv0[32],csv1[32];       //параметры сечения
  cut_pos    cpos0={0},cpos1={0},cposc;
  MN *mn1;         //преобразованный растр
  void *kit;       //указатель стека kit
  cell_list left_list,right_list; //списки cell'ов слева и справа от сечения i1
  INT d_row;       //разность координат реальной и повернутой строки в
                   //сечении i0
  CHAR  resstr[RESLEN],*res; //ожидаемый результат объединения сегментов
  BYTE  retv=0;    //возвращаемое значение
  CHAR msg[500],*s=msg;

  s += sprintf(s,"%d-%d ",i1,i0);
  memset(&vers,0,sizeof(SVERS));
  memset(roi,0,ROI_SIZE);
  seci0=&cut_list[i0];
  x0=seci0->x;
  versi0=&seci0->versm;
  seci1=&cut_list[i1];
  x1=seci1->x;
  if ( i1>=i0 || x0-x1-1 < minw && seci1->dh != 0 && seci0->dh != 0 )
  {
    s += sprintf(s,"too thick");
    retv=4; goto ret;    //слишком узкий
  }

//ищем среди ранее распознанных

  if ( cur_vers=find_vers(i1,i0,vers_list) )
    if (cur_vers->ro<0)      //надо распознать с dust'ами
    {
      cur_vers->ro=-cur_vers->ro;
      width=cur_vers->width; //на случай, если останется старая версия
      gvar=cur_vers->gvar;
      memcpy(&vers,&cur_vers->vers,sizeof(SVERS));
    }
    else                     //уже распознавался
      if (!(mode & 1))  return 1;

//можно ли улучшить меру?

  if (i1>0 && !(mode & 2) && seci1->versm.flg)
  {
    let= (versi0->nvers) ? versi0->vers[0].let : 0;
    if (let == (BYTE)'г' || let == (BYTE)'Г') fora=G_to_T;  //фора для 'т'
    else if ( let == (BYTE)'п' || let == (BYTE)'П') fora=P_to_L;  //фора для 'л'
         else                             fora=0;
    fora=seci1->lv.v1-norm(fora,x0-x1);
    if ( fora>seci0->lv.v1 || fora>cut_list->rv.v1 )
    {
      s += sprintf(s,"already worse");  retv=32; goto ret;
    }
  }

//если хорошо распознанный семент объединяется с другим, но объединение
//не предусмотрено списком - пропускаем

  res=unite_list(resstr,i1,i0,cut_list,vers_list);
  if (res )
    if (strlen(res)==0)
    {
       s += sprintf(s,"not registered");  { retv=2; goto ret; }
    }

  versi1=&seci1->versm;
  x1a=x1+r0->left;
  x0a=x0+r0->left;

//разрезаем

  kit=give_kit_addr();
  cut_fl= ( seci0->dh != 0 ) ? c_cg_cutr : 0; //разрез справа
  mn1=cut_rast(&r0->pict,r0->w,r0->h,r0->top,r0->left,cut_list,(CHAR)i0,1,
               csv0,&cpos0);
  d_row=(LONG)nIncline*(r0->left+x0)/2048;
  cpos0.row1 -= d_row;  cpos0.row2 -= d_row;   //для forbid_stick_cut

  if ( seci1->dh != 0 ) cut_fl |= c_cg_cutl; //разрез слева
  mn1=cut_rast(&r0->pict,r0->w,r0->h,r0->top,r0->left,cut_list,(CHAR)i1,2,
               csv1,&cpos1);
  cpos1.row1 -= d_row;  cpos1.row2 -= d_row;

//восстанавливаем растр

  cut_rast(&r0->pict,r0->w,r0->h,r0->top,r0->left,cut_list,(CHAR)i0,0,csv0,&cposc);
  cut_rast(&r0->pict,r0->w,r0->h,r0->top,r0->left,cut_list,(CHAR)i1,0,csv1,&cposc);

//выделяем cell'ы из mn1

  dh=select_cells(C,mn1,x1a,x0a,cut_fl,&left_list,&right_list);
  if ( x0-x1-1-MAX(seci1->rv.v3,0) > (dh<<1)+(dh>>2) )   //слишком широкий
  {
    for ( i=0; i<left_list.N;   i++ )  del_cell(left_list.cells[i]);
    for ( i=0; i<right_list.N;  i++ )  del_cell(right_list.cells[i]);
    take_kit_addr(kit);
    s += sprintf(s,"too wide");
    retv=8; goto ret;
  }

//чтобы распозналась палка присваиваем версию 'ь'

  let= (versi1->nvers) ? versi1->vers[0].let : 0;
  if ( let==(BYTE)'ь' )
    for ( i=0,CI=left_list.cells; i<left_list.N; i++,CI++ )
      if (!dust(*CI)) rest_vers(*CI,versi1);

//Аликов флаг

  if (cut_fl)
  {
    cell B;
    if (cut_fl & c_cg_cutr) cut_fine = (cut_fl & c_cg_cutl) ? 2 : 1;
    else                    cut_fine = 0;
    Alik_def_cut_flg(&B,seci0,seci1,r0->h,cut_fine);
    cut_fine=B.cg_flag_fine;
  }
   else
     cut_fine = 0;

//распознаем

  ro=recogij(C,right_list.cells,right_list.N,cut_fl,cut_fine,
             cut_list->rv.v2,roi,&gvar,&vers,&width);

  for ( i=0; i<left_list.N; i++ )  del_cell(left_list.cells[i]);
  take_kit_addr(kit);

  if ( vers.flg==0 )            //не распознавался
  {
    if (debug_on) snap_show_raster(NULL,0,0);   //стирание картинки
    width=x0-x1;
    if ( i1 != i0-1 )
    {
      set_bad(&vers);  set_bad_vers(&vers);  ro=MAX_RO;
    }
    else
    {
      set_dust(&vers);  set_bad_vers(&vers);
      if (width>=minp) ro=PASS_PEN;
      else             ro=0;
      if (cut_fl) roi[3] += CUT_PEN<<1;
    }
  }

  if ( x0-x1 > width+(blank<<1) )  //ошибки разрезания
  {
    width=x0-x1;
    set_bad_vers(&vers);  ro=MAX_RO;
  }

  let=vers.vers[0].let;

//результат должен быть в списке, если таковой имеется
  if (res)
    if ( vers.nvers && !strchr(res,let) )
    {
       ro=MAX_RO;
       s += sprintf (s,"not registered");
    }

  if ( language == LANG_RUSSIAN )
  {
    if ( width < my_bases.ps>>1 )  width=my_bases.ps>>1;
  }
  else
    if ( width < ps3 )  width=ps3;

  if (let=='m')
  {               //штраф для 'm', если собрана из кусков
    if ( not_connect_sect(i1,i0,cut_list) )  roi[1] = m_nc_PEN;
  }
  else            //штраф за сборку отрезанных кусков
    if (cut_fl)
    {
      i=i0;
      while( (i=not_connect_sect(i1,i,cut_list))>0 )
      {
        INT dr=(cut_list+i)->rv.v3;
        roi[1]=MAX(dr,0)*200/my_bases.ps;  //20 баллов за 1/10ps
      }
    }

//штраф для отрезанной палки
  if ( !dust(&vers) && ro<RO_BAD )
  {
    if ( cut_fl & c_cg_cutl && forbid_stick_cut(C,&vers,r0->h,&cpos1,2) )
      roi[2] += MONstick;
    if ( ro<RO_BAD &&
         cut_fl & c_cg_cutr && forbid_stick_cut(C,&vers,r0->h,&cpos0,1) )
      roi[2] += MONstick;
  }

  if ( dust(&vers) ) width=my_bases.ps;
  s += sprintf (s," ro=(%d",ro);
  for (i=1; i<ROI_LEN; i++)
  {
    INT x=roi[i];
    ro += x;  s += sprintf (s,"+%d",x);
  }
  ro = norm(ro,width);
  s += sprintf (s,")*%d/4=%d",width,ro);

//сохраняем результат для сегмента (i1,i0) на будущее

  if (!cur_vers) store_vers(NULL,vers_list,i1,i0,&vers,ro,width,gvar);

  if (!versi1->flg && i1>0)
  {
    s += sprintf (s,"no path");   //из i1 нет пути
    retv |= 16; goto ret;
  }
  cur_meas=seci1->lv.v1+ro;        //мера i0-го сечения
  if ( accept_vers(s,cut_list,i1,i0,&vers,gvar,width,cur_meas) )
  {
    if (debug_on)
#ifndef MY_DEBUG
    if (det_trace)
#endif
    {
      *show_dp(msg+strlen(msg),cut_list,i0)=0;
      show_and_wait(msg);
    }
    seci0->lv.v1=cur_meas;
    seci0->rv.v1=ro;
    seci0->rv.v2=width;
    seci0->px=(CHAR)i1;
    seci0->gvarm=gvar;
    memcpy(versi0,&vers,sizeof(SVERS));
    if (cur_vers) store_vers(cur_vers,vers_list,i1,i0,&vers,ro,width,gvar);
    return retv;
  }
  else
    retv |= 32;

ret:
  if (debug_on)
#ifndef MY_DEBUG
  if (det_trace)
#endif
  {
    *show_dp(msg+strlen(msg),cut_list,i0)=0;
    show_and_wait(msg);
  }
  return retv;
}

/*---------------------------------------------------------------------
  unite_list - ЇюЁьшЁєхЄ ёяшёюъ фюяєёЄшь√ї Ёхчєы№ЄрЄют яЁш юс·хфшэхэшш
               эхёт чэ√ї ёхуьхэЄют; хёыш ы■сющ Ёхчєы№ЄрЄ фюяєёЄшь,
               тючтЁр∙рхЄ NULL, р хёыш юс·хфшэ Є№ эхы№ч  - яєёЄющ ёяшёюъ
---------------------------------------------------------------------*/
static CHAR *unite_list(CHAR *resstr,INT i1,INT i0,struct cut_elm *cut_list,
                        seg_vers **vers_list)
{                      // for segments of the left and right
  CHAR  *res1,*res2;   // lists results
  SVERS *vers1,*vers2; // version
  INT  i=i0;           // boundary between them
  CHAR  *res;          // res1 intersection and res2
  CHAR  *res0=NULL;    // return value
  BYTE let,pt;
  CHAR  wrk1[RESLEN],wrk2[RESLEN];
  seg_vers *cur_vers;

  // 16.07.2001 E.P. conflict with a_bottom_accent_baltic 0xe0
  if (is_baltic_language(language))
	results_right_to_bad[9][6]=0;
  else
	results_right_to_bad[9][6] = (BYTE)0xe0;  // Russian letter "p" appears in the text as "a"

  // 21.05.2002 E.P. Turkish-russian conflicts
  if (is_turkish_language(language))
	{
	results_left_to_bad[0][22] = 'k';
	results_left_to_bad[0][23] = 'k';
	results_left_to_bad[7][0] = '6';
	results_left_to_bad[7][2] = '6';
	results_left_to_bad[9][2] = 'd';
	results_left_to_bad[14][1] = 'Ш';
	results_left_to_bad[16][0] = 0;
	results_left_to_bad[19][2] = 'ш';
	results_left_to_bad[20][1] = 'Ш';
	results_left_to_bad[22][1] = 'Ш';
	results_left_to_bad[23][0] = 'ф';

	results_right_to_bad[7][2]='o';
	results_right_to_bad[9][5]='b';
	results_right_to_bad[10][8]='k';
	results_right_to_bad[11][0]=0;
	results_right_to_bad[16][0]=0;
	results_right_to_bad[22][0]=0;

	}
  else
	{
	results_left_to_bad[0][22] = 'Щ';
	results_left_to_bad[0][23] = 'ю';
	results_left_to_bad[7][0] = 'o';
	results_left_to_bad[7][2] = 'о';
	results_left_to_bad[9][2] = 'о';
	results_left_to_bad[14][1] = 'Щ';
	results_left_to_bad[16][0] = 'ю';
	results_left_to_bad[19][2] = 'ю';
	results_left_to_bad[20][1] = 'Щ';
	results_left_to_bad[22][1] = 'Щ';
	results_left_to_bad[23][0] = 'о';

	results_right_to_bad[7][2]='о';
	results_right_to_bad[9][5]='о';
	results_right_to_bad[10][8]='Х';
	results_right_to_bad[11][0]='т';
	results_right_to_bad[16][0]='Щ';
	results_right_to_bad[22][0]='ю';

	}

  *resstr=0;
  while( (i=not_connect_sect(i1,i,cut_list))>0 )
  {
    res=res1=res2=NULL;
    if ( (cur_vers=find_vers(i1,i,vers_list)) )
    {
      vers1=&cur_vers->vers;
      if ( !dust(vers1) && (cur_vers=find_vers(i,i0,vers_list)) )
        if ( !dust(vers2=&cur_vers->vers) )
        {
          if (vers1->nvers)
          {
            let=vers1->vers[0].let;
            pt = (let==(BYTE)'т' &&
					!is_russian_turkish_conflict(let) // 21.05.2002 E.P.
				 ) ? trs2 : trg;
            if (vers1->vers[0].prob>=pt)
            {
              res1 = res_for(let,sticks_left_to_bad,letters_left_to_bad,
                             results_left_to_bad);
              if ( !res1 )  goto empty_list;      //хорошая, но нет в списке
            }
          }
          if (vers2->nvers)
          {
            let=vers2->vers[0].let;
            pt = (let==(BYTE)'т' &&
				!is_russian_turkish_conflict(let) // 21.05.2002 E.P.
				) ? trs2 : trg;
            if (vers2->vers[0].prob>=pt)
            {
              res2 = res_for(let,sticks_right_to_bad,letters_right_to_bad,
                             results_right_to_bad);
              if ( !res2 )  goto empty_list;
            }
          }
          if (res1 && res2)  res=strXcrs(wrk1,res1,res2);
          else   if (res1)   res=res1;
                 else        res=res2;
          if (res)
          {
		    // ╚ёъы■ўшЄ№ ъюэЇышъЄэ√х ъюф√ шч results_left_to_bad, results_right_to_bad
		    // 17.07.2001 E.P.
			if (is_baltic_language(language) ||
				is_turkish_language(language) // 21.05.2002 E.P.
				)
			  {
			  INT n1,n2;
			  for(n1=0,n2=0;res[n2];n2++)
				{
				// ╧ЁюяєёЄшЄ№ ъюэЇышъЄэ√щ ъюф
				if (is_russian_baltic_conflict(res[n2]))
					continue;

				// ╧ЁюяєёЄшЄ№ ъюэЇышъЄэ√щ ъюф 21.05.2002 E.P.
				if (is_russian_turkish_conflict(res[n2]))
					continue;

				// ╙яыюЄэшЄ№ Ёхчєы№ЄрЄ
				if(n1<n2)
					res[n1++] = res[n2];
				}
			  res[n2]=0;	// ═єы№ эр ъюэЎх
			  if (!n2)
				   goto empty_list;
			  } // ╩юэхЎ тёЄртъш 17.07.2001 E.P.

            if (strlen(resstr)>0)
            {
              if (strlen(strXcrs(wrk2,res,resstr))==0)  goto empty_list;
              strcpy(resstr,wrk2);
            }
            else
              strcpy(resstr,res);
            res0=resstr;
          }
        }
    }
  }
  return res0;

empty_list:
  *resstr=0;
  return resstr;
}

/*--------------------------------------------------------------------
  res_for  тючтЁр∙рхЄ ёЄЁюъє Ёхчєы№ЄрЄют шч ьрёёштр results фы  сєът√
           let, хёыш юэр ёюфхЁцшЄё  т ьрёёштрї sticks ш letters, шэрўх
           тючтЁр∙рхЄ NULL
--------------------------------------------------------------------*/
static CHAR *res_for(BYTE let, BYTE *sticks, BYTE *letters, CHAR **results)
{
  CHAR *pos;

  if (sticks)
  {
    pos=strchr(sticks,let);
    if (pos || let==liga_i || // 08.09.2000 E.P.
		 language == LANG_TURKISH &&  // 30.05.2002 E.P.
			(let==i_sans_accent||let==II_dot_accent)
	   )
		return results[0];
  }
  pos=strchr(letters,let);
  if (pos &&
	  !is_russian_baltic_conflict(let) &&
	  !is_russian_turkish_conflict(let) // 21.05.2002 E.P.
	  ) 	// 17.07.2001 E.P.
	  return results[pos - (CHAR*)letters];

  return NULL;
}

/*--------------------------------------------------------------------
  strXcrs  ЇюЁьшЁєхЄ ёЄЁюъє шч ёшьтюыют, ёюфхЁцр∙шїё  юфэютЁхьхээю т
           str1 ш str2 ш яюьх∙рхЄ хх т resstr; тючтЁр∙рхЄ *resstr
--------------------------------------------------------------------*/
static CHAR *strXcrs(CHAR *resstr, CHAR *str1, CHAR *str2)
{
  CHAR *res;
  BYTE let;

  for (res=resstr; *str1; str1++)
  {
    let=*str1;
    if (strchr(str2,let))  *res++=let;
  }
  *res='\0';
  return resstr;
}

/*-----------------03-02-95 06:47pm-------------------------------------
  select_cells  формирует cell'ы из структуры mn1, находящиеся левее
                pos2, помещает в строку, заданную *C, распределяет
                по спискам left_list и right_list относительно
                координаты pos1 (pos1 и pos2 - реальные), помечает
                отрезанные cell'ы в списке right_list; возвращает
                высоту списка right_list над my_bases.b3
----------------------------------------------------------------------*/
static INT select_cells(cell *C,MN *mn1,INT pos1, INT pos2, BYTE cut_fl,
                        cell_list *left_list, cell_list *right_list)
{
  INT i;
  INT minrow=my_bases.b2; //минимальная строка для right_list
  INT li=0,ri=0;          //счетчики cell'ов слева и справа
  cell *CI;

  for ( i=0; i<MAX_SECT && mn1; i++ )
  {
     CI=create_my_cell(mn1,C,0,0);
     CI->stick_inc=NO_INCLINE;
     if ( lefter(CI,pos1) )
       left_list->cells[li++]=CI;
     else
       if ( lefter(CI,pos2) )
       {
         //помечаем отрезанные
         if (cut_fl & c_cg_cutl && CI->r_col==pos1+1)
           CI->cg_flag |= c_cg_cutl;
         if (cut_fl & c_cg_cutr && CI->r_col+CI->w==pos2)
           CI->cg_flag |= c_cg_cutr;

         if (dust(CI) && cut(CI) && CI->w==1)  //Аликовы фокусы
           del_cell(CI);
         else
         {
           right_list->cells[ri++]=CI;  minrow=MIN(minrow,CI->row);
         }
       }
       else
         del_cell(CI);
     mn1=mn1->mnnext;
  }
  left_list->N=li; right_list->N=ri;
  return my_bases.b3-minrow;
}

/*-----------------04-05-95 05:35pm-----------------------------------
  recognij  образует cell из списка org_cells из N cell'ов и распознает
            его; возвращает меру, вариант объединения gvar, версии
            распознавания vers и ширину cell'а;
            cut_fl - cell'ы получены после разрезания,
            *C указывает текущую строку; cut_fl содержит биты c_cg_cutl
            и c_cg_cutr; inc - параметр наклона (курсива); *vers на
            входе должен содержать версии распознавания без dust'ов
            (если они есть) или все нули
---------------------------------------------------------------------*/
INT recogij(cell *C, cell **org_cells, INT N, BYTE cut_fl,
                   BYTE cut_fine,INT inc, INT *roi, BYTE *gvar,
                   SVERS *vers, INT *width )
{
  INT i;
  cell *B,*B0,**P; //текущий cell
  INT ro =MAX_RO-vers->vers[0].prob;  //ошибка распознавания
  INT rod=MAX_RO;  //то же с dust'ами
  BYTE  gvard;     //вариант склеивания с dust'ми
  SVERS versd;     //версии с dust'ми
  INT  widthd;     //ширина с dust'ами
  grup box,top,bottom,far_top,far_bottom;   //описатели групп cell'ов
  INT nstick=0;    //количество больших и палок в исходных cell'ах
  INT Nfar;        //количество дальних
  INT fora=0;      //фора для "б"
  BYTE up_used=0;  //флаг: верхние dust'ы уже использованы
  BYTE cut_up=0,cut_low=0,cut_far=0; //флаги: соответствующие dust'ы отрезаны

  *gvar=0;

  if ( N==0 )
  {
    if (debug_on)
#ifndef MY_DEBUG
      if (det_trace)
#endif
      {
        snap_show_raster(NULL,0,0);  //стирание картинки
        show_and_wait("no cells");
      }
    return MAX_RO;
  }
  if ( N==1 && vers->flg==0 )
  {
    B0=org_cells[0];
    B0->cg_flag_fine=cut_fine;
    B0->cg_flag |= cut_fl;
    inter_diff(B0);
    set_erection(B0, inc);
    ro=recog_one(B0,NULL,trs2,vers);
    *width=B0->w;
    goto ret;
  }

  cg_show_list(org_cells,N,"to_recog");

//распределяем cell'ы по группам

  if ( !classify(org_cells,N,&box,&bottom,&top,&far_bottom,&far_top) )
  {
    vers->flg=0;
    if (debug_on)
#ifndef MY_DEBUG
      if (det_trace)
#endif
      {
        show_and_wait("unproper cells");
      }
    goto ret;
  }

//вычисляем cut_* и удаляем далекие

  P=org_cells+box.n;
  for (i=0; i<bottom.n; i++,P++) cut_low |= cut(*P);
  for (i=0;    i<top.n; i++,P++) cut_up  |= cut(*P);
  Nfar=far_top.n+far_bottom.n;
  for (i=0;   i<Nfar; i++,P++) { cut_up |= cut(*P); del_cell(*P); }
  N -= Nfar;
  if ( box.n>MAX_CELLS_IN_LIST-1 )
  {
    if (debug_on)
#ifndef MY_DEBUG
      if (det_trace)
#endif
      {
        show_and_wait("too many cells");
      }
    goto ret;
  }

//больше одной палки клеить нельзя

  if ( language == LANG_RUSSIAN )
    for ( i=0; i<box.n; i++ )
      if( !dust(B=org_cells[i]) )
        if (nstick<2)  nstick += is_stick(B);
        else           nstick = 3;
  *gvar = gvard = (BYTE)nstick;

//распознаем

  if ( vers->flg==0 || box.n>1 )
    ro=recog_set(C,org_cells,box.n,cut_fl,cut_fine,inc,trs2,gvar,vers,width);
  if ( let_or_bad(vers) )
    switch(vers->vers[0].let)
    {
      case (BYTE)'ы':               //"м" и "ы" клеятся друг в друга
      case (BYTE)'м':
      case (BYTE)'ц':               //"ц" иногда плохо клеится
      case '%':
        if ( *gvar & glued )
        {
          BYTE  gvardd=2;     //без склеивания
          rod=recog_set(C,org_cells,box.n,cut_fl,cut_fine,inc,trs2,
                        &gvardd,&versd,width);
          if ( rod<ro )
          {
            ro=rod;
            *gvar = gvardd;
            memcpy(vers,&versd,sizeof(SVERS));
          }
        }
      case (BYTE)'н':
      case (BYTE)'п':
        if ( top.n )  ro += PEN_TOP_DUST;    //возможно "й"
        break;
      case (BYTE)'и':
        if ( ro<RO_DUST_OFF && top.b<my_bases.b2 && !bottom.n )  //"й"
          goto ret;
        break;
      case (BYTE)'о':
		  if (is_russian_turkish_conflict(vers->vers[0].let)) // 21.05.2002 E.P.
			  break;
        if ( top.n )
        {
          //меняем местами списки top и bottom
          INT nd=MIN(top.n,bottom.n);
          cell *buff[MAX_CELLS_IN_LIST];
          memcpy(buff,org_cells+box.n,sizeof(cell *)*nd);
          memcpy(org_cells+box.n,org_cells+box.n+top.n,sizeof(cell *)*nd);
          memcpy(org_cells+box.n+top.n,buff,sizeof(cell *)*nd);

          fora=O_to_B;      //фора для "б"
          up_used=1;
          //распознаем с верхним dust'ом
          rod=recog_set(C,org_cells,(INT)(box.n+top.n),cut_fl,cut_fine,
                        inc,trs2,&gvard,&versd,&widthd);
          if ( rod<ro+fora )
          {
            ro=rod;
            *gvar = gvard | up_dust;
            memcpy(vers,&versd,sizeof(SVERS));
            *width=widthd;
            if ( ro<RO_DUST_OFF )  goto ret;
          }
        }
        break;
      case '!':
      case '?':
        if ( bottom.a>my_bases.bm && bottom.b<my_bases.b3+3 )
          goto ret;
        break;
      case 'ч':
		if (is_russian_turkish_conflict(vers->vers[0].let)) // 21.05.2002 E.P.
			break;

        if ( bottom.n && !(vers->source & c_rs_BOX) )       //по 3x5
          ro=recog_set(C,org_cells,box.n,cut_fl,cut_fine,
                       inc,0,gvar,vers,width);
        break;
      case 'i':
      case 'r':
        if ( ro<110 && bottom.n==0 ) goto ret;  //чтобы не клеить в 'l'
        break;
      default:
        if ( MAX_RO-ro > trs2 )  goto ret;
    }

  //все вместе

  if ( top.n && !up_used || bottom.n )
  {
    rod=recog_set(C,org_cells,(INT)(box.n+top.n+bottom.n),cut_fl,cut_fine,inc,trs2,
                  &gvard,&versd,&widthd);
    if ( let_or_bad(&versd) )
      if ( rod<ro+fora || dust(vers) )
      {
        ro=rod;
        *gvar = gvard | all;                //все в одном cell'е
        memcpy(vers,&versd,sizeof(SVERS));
        *width=widthd;
      }
  }

  //если плохо получилось - рассыпаем

  if (ro > RO_COMPOSE) *gvar=0;
  else
    if ( ro >= RO_BAD )
    {
      INT nc=box.n;
      if (*gvar & up_dust)  nc += top.n;
      if (*gvar & low_dust) nc += bottom.n;
      if ( nc<3 ) *gvar=0;
    }

ret:
  for ( i=0; i<N; i++ )  del_cell(org_cells[i]);
  if ( cut_fl )                    //штраф за разрез
  {
    BYTE let=vers->vers[0].let;
    if ( dust(vers) && let != '-' ||
         cut_up  && !(*gvar &  up_dust) && let != 'i' &&
			!(let ='й' &&
				!is_russian_turkish_conflict(let) // 21.05.2002 E.P.
			 ) ||
         cut_low && !(*gvar & low_dust) && let != '!' && let !='?' ||
         cut_far )
                               roi[3] += CUT_PEN<<1;
    if ( cut_fl & c_cg_cutr )  roi[3] += CUT_PEN;
    if ( cut_fl & c_cg_cutl )  roi[3] += CUT_PEN;
  }
  return ro;
}

/*-----------------07-21-95 01:54pm---------------------------------
  recog_one  распознает cell B0 и возвращает ошибку распознавания и
             версии vers;
             *GL и tol - параметры для full_recog
-------------------------------------------------------------------*/
static INT recog_one (cell *B0, s_glue *GL, INT tol, SVERS *vers)
{
  INT ro =MAX_RO;  //ошибка распознавания

  if ( dust(B0) )   //все вместе - dust; не распознаем
  {
    if (B0->w<minp)
      ro=0;
    else
    {
      ro=PASS_PEN;
      if ( is_defis(B0) )
      {
        B0->nvers=1; B0->vers[0].let='-';
      }
    }
    glsnap('a',B0,"");
  }
  else
    if (B0->w>=minw && !wide(B0))
    {
      if (full_recog(B0,GL,tol,2))  ro=loc_err(B0);
    }
    else
      glsnap('a',B0,"");
  save_vers(B0,vers);
  return ro;
}

/*----------------------------------------------------------------
  wide  возвращает 1, если cell слишком широк, иначе - 0
-----------------------------------------------------------------*/
/*
static CHAR wide (cell *C)
{
  INT h=my_bases.b3-C->row;
  return ( C->w > (h<<1) + ((language==LANG_RUSSIAN) ? -(h>>3) : (h>>3)) );
}
*/

/*-----------------06-23-95 03:59pm-------------------------------
  recog_set  распознает совокупность N cell'ов org_cells как один;
             возвращает версии vers и вероятность наилучшей;
             cut_fl указывает, что cell'ы получены разрезанием;
              inc - параметр наклона (курсива); gvar=glued на входе,
             если склейка разрешена, на выходе gvar - вариант объ-
             единения, width-ширина; tolbox - параметр для full_recog;
-----------------------------------------------------------------*/
static INT recog_set(cell *C,cell **org_cells, INT N, BYTE cut_fl,
                     BYTE cut_fine, INT inc,
                     INT tolbox, BYTE *gvar, SVERS *vers, INT *width)
{
  INT i;
  cell *B0=NULL;    //cell для распознавания
  s_glue GL={0};
  INT ro =MAX_RO;   //ошибка распознавания
  BYTE broken_II=0; //флаг: две палки

  memset(vers,0,sizeof(SVERS));
  *width=0;
  if (*gvar>1)      // >1 палки
  {
    if (*gvar == 2)  broken_II=1;
    *gvar=0;        //не клеить
  }
  else
    *gvar=glued;    //клеить

  B0=unite(C,org_cells,N,&GL,gvar);
  if ( !B0 )  return ro;

  *width=B0->w;
  B0->cg_flag_fine=cut_fine;
  B0->cg_flag |= cut_fl;
  B0->broken_II=broken_II;
  set_erection(B0,inc);
  ro=recog_one(B0,&GL,tolbox,vers);
  if ( B0 != GL.celist[0] )  del_cell(B0);

  if ( *gvar & glued )
  {
    B0=GL.celist[0];
    for ( i=0; i<N; i++ )  org_cells[i]=rest_cell(org_cells[i],B0);
    for ( i=0; i<GL.ncell; i++ )  del_cell(GL.celist[i]);
  }

  return ro;
}

/*-----------------06-26-95 05:46pm--------------------------------
  unite  ЇюЁьшЁєхЄ ёютюъєяэ√щ cell, шч ёяшёър N cell'ют org_cells;
         хёыш gvar==glued, я√ЄрхЄё  ёъыхшЄ№;
         эр т√їюфх:
           gvar==glued, хёыш ўЄю-эшсєф№ ёъыхшыюё№, шэрўх - 0;
           GL - cell'√, яюыєўхээ√х яюёых ёъыхштрэш ;
           org_cells - шёїюфэ√х cell'√, хёыш эх ёъыхшыюё№, ш cell'√,
                       ёюїЁрэхээ√х т kit, хёыш ёъыхшыюё№;
         тючтЁр∙рхЄ яюыєўхээ√щ cell
------------------------------------------------------------------*/
static cell *unite (cell *C, cell **org_cells, INT N, s_glue *GL, BYTE *gvar)
{
  INT i;
  cell *B;
  MN *mn,*mn1;

  if ( N>MAX_CELLS_IN_LIST-1 )
  {
    if (debug_on)
#ifndef MY_DEBUG
      if (det_trace)
#endif
      {
        show_and_wait("too many cells");
      }
    return NULL;
  }

  for ( i=0; i<N; i++ )
  {
    B=org_cells[i];
    GL->celist[i]=B; GL->complist[i]=B->env;
  }
  GL->celist[N]=NULL; GL->complist[N]=NULL;
  GL->ncell=(BYTE)N;

//клеим

  if ( N==1 )  *gvar=0;
  if ( *gvar )
  {
    mn1=mn=glue(&GL->complist, 1);
    for ( i=0;  mn1; i++, mn1=mn1->mnnext );
    if ( i>0 && i<N )        //склеилось
    {
      for ( i=0;  mn; i++,mn=mn->mnnext )
      {
        B=create_my_cell(mn,C,0,0);
        GL->celist[i]=B; GL->complist[i]=B->env;
      }
      GL->ncell=(BYTE)i;
      GL->celist[i]=NULL; GL->complist[i]=NULL;
      for ( i=0; i<N; i++ )
        org_cells[i]=(cell *)del_save_cell(org_cells[i]);
    }
    else
      *gvar=0;
  }

//если несколько - объединяем

  B=GL->celist[0];
  if ( GL->ncell>1 )
  {
    *gvar |= united;
    B=comp_to_cell(B,GL->complist,GL->ncell,0,0);
  }

  inter_diff(B);

//  glsnap('a',B,"B");

  return B;
}

/*-----------------06-23-95 11:07am--------------------------------
  classify  реорганизует список cells из N cell'ов, образуя группы
            в порядке перечисления:
            -  образующие основной бокс (box);
            -  нижние dust'ы (bottom);
            -  верхние dust'ы (top);
            -  дальние нижние dust'ы (far_bottom);
            -  дальние верхние dust'ы (far_top);
            для каждой группы вычисляются: количество cell'ов в
            группе n и границы a и b (верхняя и нижняя);
            возвращает 1 в случае успеха и 0 в противном случае
-----------------------------------------------------------------*/
static BYTE classify (cell **cells, INT N, grup *box, grup *bottom,
                      grup *top, grup *far_bottom, grup *far_top)
{
  INT i;
  INT upper;        //верхняя строка растра
  INT gb,ge;        //начало и конец просвета
  INT gba,gea,gma;  //то же плюс середина абсолютные
  INT width;               //ширина просвета: широкий,
  INT wide=my_bases.ps>>1; // если на краю шире wide,
  INT mid=my_bases.ps/3;   // а в середине шире mid
  BYTE proj[RASTER_HEIGHT+1];  //проекция на вертикальную ось

  far_top->n=far_bottom->n=top->n=bottom->n=0;
  upper=horiz_proj(cells,N,&proj,RASTER_HEIGHT+1);

//ищем дальние

  far_top->a=far_top->b=upper;
  far_bottom->a=far_bottom->b=(STRRCHR(proj,'1')-((char*)proj)) + upper;
  ge=0;
  do                                   //цикл по просветам
  {
    gb=STRSPN(proj+ge,"1")+ge;  //начало просвета
    ge=STRSPN(proj+gb,"0")+gb;  //конец просвета + 1
    gba=gb+upper; gea=ge+upper;
    gma=(gba+gea)>>1;
    width=ge-gb;
    if ( width>wide )           //широкий
      if ( gma<my_bases.b2 )    //верхние dust'ы
      {
        i=0;
        while ( i<N )
          if ( cells[i]->row < gba )
          {
            change((void**)cells,i,(INT)(N-1)); N--; far_top->n++;
          }
          else
            i++;
        far_top->b=gba;
      }
      else
        if ( gma>my_bases.b3 )         //нижние dust'ы
        {
          i=0;
          while ( i<N )
            if ( cells[i]->row >= gea )
            {
              change((void**)cells,i,(INT)(N-1)); N--; far_bottom->n++;
            }
            else
              i++;
          far_bottom->a=gea;
          proj[gb]=0;
          break;
        }
        else                          //широкий просвет в середине
          return 0;
    else
      if ( width>=mid && gma>my_bases.b2 && gma<my_bases.b3) return 0;
  }
  while ( width>0 );

//ищем ближние

  ge=far_top->b-upper;
  ge=STRSPN(proj+ge,"0")+ge;    //первый не ноль
  top->a=top->b=ge+upper;
  bottom->a=bottom->b=(STRRCHR(proj,'1')-(char*)proj) + upper;
  do                            //цикл по просветам
  {
    gb=STRSPN(proj+ge,"1")+ge;  //начало просвета
    ge=STRSPN(proj+gb,"0")+gb;  //конец просвета + 1
    gba=gb+upper; gea=ge+upper;
    width=ge-gb;
    if ( width>0 )
      if ( gba<=my_bases.b2 )           //верхние dust'ы
      {
        i=0;
        while ( i<N )
          if ( cells[i]->row < gba )
          {
            change((void**)cells,i,(INT)(N-1)); N--; top->n++;
            top->b=gba;
          }
          else
            i++;
      }
      else
        if ( gea>=my_bases.bm )         //нижние dust'ы
        {
          i=0;
          while ( i<N )
            if ( cells[i]->row >= gea )
            {
              change((void**)cells,i,(INT)(N-1)); N--; bottom->n++;
            }
            else
              i++;
          bottom->a=gea;
          proj[gb]=0;
          break;
        }
  }
  while ( width>0 );

  ge=top->b-upper;
  ge=STRSPN(proj+ge,"0")+ge;    //первый не ноль
  box->n=N;
  box->a=ge+upper;
  box->b=(STRRCHR(proj,'1')-(char*)proj) + upper;

  far_top->b--; top->b--;

  return 1;
}

/*-----------------06-26-95 03:27pm-----------------------------
   horiz_proj вычисляет проекцию proj на вертикальную ось N
              cell'ов cells; возвращает смещение массива proj
---------------------------------------------------------------*/
static INT horiz_proj(cell **cells, INT N, BYTE *proj, INT size)
{
  INT i;
  INT upper=10000;   //верхняя граница cell'ов
  INT beg,len;       //начало и длина проекции cell'а
  cell *B;

  memset(proj,'0',size);
  proj[size-1]=0;// Piter add "-1"

//ищем верхнюю границу (смещение)

  for ( i=0; i<N; i++ )  upper=MIN(upper,(cells[i])->row);

//строим проекцию

  for ( i=0; i<N; i++ )
  {
    B=cells[i];
    beg=B->row-upper; len=B->h;
    if( beg<size )
        { // OLEG for CAM
        if ( beg+len>=size ) len=size-1-beg;
        memset(proj+beg,'1',len);
        }
  }

  proj[STRRCHR(proj,'1')-(char*)proj+1]=0;
  return upper;
}

/*-----------------06-26-95 04:18pm---------------------------------
  change  меняет местами i-ый j-ый элементы массива arr
------------------------------------------------------------------*/
static void change(void **arr, INT i, INT j)
{
  void *p;

  p=arr[i]; arr[i]=arr[j]; arr[j]=p;
}

/*-----------------07-07-95 11:39am-----------------------------
  accept_vers  тючтЁр∙рхЄ 1, хёыш тхЁёшш vers ёхуьхэЄр (i1,i0)
               яЁшэшьр■Єё , ш 0 т яЁюЄштэюь ёыєўрх;
               s - сєЇхЁ фы  т√тюфр эр ¤ъЁрэ
               res - яЁхфяюырурхь√щ Ёхчєы№ЄрЄ
               width - °шЁшэр ёхуьхэЄр (i1,i0)
               gvar - ёяюёюс ёсюЁъш
               cur_meas - эръюяыхээр  ьхЁр т ёыєўрх яЁшэ Єш 
                          тхЁёшщ vers
----------------------------------------------------------------*/
static BYTE accept_vers(CHAR *s, struct cut_elm *cut_list,
                        INT i1, INT i0, SVERS *vers, CHAR gvar, INT width,
                        INT cur_meas)
{
  BYTE let=0,p;                        //AK let=0
  struct cut_elm *seci; SVERS *versi;
  struct cut_elm *secl,*secm,*secr=cut_list+i0;  INT il,im,xl,xm,xr;
                   //левое, среднее и правое сечения и их координаты
  SVERS *vers0,*versl,*versr;  BYTE let0,letl,letr;
                   //версии сегмента (i2,i0), его левой и правой частей
  INT pen=0;       //штраф: в пользу сборки >0, в пользу разрезания <0
  INT ip=secr->px;                   //предыдущая точка оптимального пути
  static CHAR except[]="(by exception)", rule[]="";
  CHAR *reason=except;

  if (!secr->versm.flg)
  {
    reason=rule; goto ret1;   //не распознавался
  }

  if (i1==ip)
  {
    BYTE new_let=vers->vers[0].let;
    BYTE old_let=secr->versm.vers[0].let;
    switch(new_let)
    {
  //расширение 'о' до 'б'
      case (BYTE)'б':
      if ( old_let == (BYTE)'о' &&
		  !is_russian_turkish_conflict(old_let) // 21.05.2002 E.P.
		  )  pen=-O_to_B;
      break;

  //расширение 'г'до  'п'
      case (BYTE)'п':  case (BYTE)'П':
      if ( ( old_let == (BYTE)'г' || old_let == (BYTE)'Г')  &&
           !(gvar & (up_dust+low_dust))      //dust'ы только из коробки
         )  pen=-40;
      break;
    }
  }
  else
  {
    if (i1<ip)  { il=i1; im=ip; vers0=vers; versr=&secr->versm; }
    else        { il=ip; im=i1; vers0=&secr->versm; versr=vers; }
    secm=cut_list+im;  secl=cut_list+il;  versl=&secm->versm;

    let0=vers0->vers[0].let;   xl=secl->x;
    letl=versl->vers[0].let;   xm=secm->x;
    letr=versr->vers[0].let;   xr=secr->x;

    switch(let0)
    {
    //расширение 'г' до 'т'
      case (BYTE)'т':
		  if (is_russian_turkish_conflict(let0))
			  break; // 21.05.2002 E.P.
      case (BYTE)'Т':
         if ( (letr==(BYTE)'г' || letr==(BYTE)'Г') && xm-xl < (xr-xl)>>1 )
           pen = (letl==(BYTE)'у' || letl==(BYTE)'У') ? 30 : G_to_T;
         break;

  //расширение 'п' до 'л'
      case (BYTE)'л':  case (BYTE)'Л':
      if ( ( letr==(BYTE)'п' || letr==(BYTE)'П') && secm->dh != 0 && xm-xl <= (xr-xl)>>2
         )  pen=P_to_L;
      break;

  //"ї" или "тп"
      case (BYTE)'ї':
      if ( letl==(BYTE)'т' &&
		  !is_russian_turkish_conflict(letl) && // 21.05.2002 E.P.
		  letr==(BYTE)'п' )  pen=30;
      break;

  //"ыо" или "ью"
      case (BYTE)'ю':
	  case (BYTE)'Ю':
		  if (is_russian_turkish_conflict(let0)) // 21.05.2002 E.P.
			  break;
      if ( letl==(BYTE)'|' || letl==liga_exm ||
		   letl==(BYTE)'ы' || letl==(BYTE)'Ы' // √█
		 )
        if ( secl->dh==0 && secm->dh==0 )  //ярыър эшъєфр эх яЁшъыххэр
        {              //яЁшюЁшЄхЄ "ю", хёыш ярыър сышцх ъ "№"
          if ( secm->rv.v3 > secl->rv.v3<<1 )  pen=-IEE_IU_PEN; //Ёхцхь "■"
          else                                 pen= IEE_IU_PEN; //Ёхцхь "√"
        }
        else           //яЁшюЁшЄхЄ "ю", хёыш ярыър яЁшъыххэр Єюы№ъю ъ "№"
          if (secm->dh==0)  pen=-IEE_IU_PEN;
          else              pen= IEE_IU_PEN;
      if (pen>0 && vers0->source & 1)  pen = pen<<1;   //"ю" по эвентам
      break;

  //°ЄЁрЇ фы  'L', хёыш ёюсЁрэр шч ярыъш ш Єюўъш
      case 'L':
      if ( dust(versr) && secm->dh==0 && stick(letl) )  pen = -30;
      break;

  //°ЄЁрЇ фы  'U', хёыш ёюсЁрэр шч 'L' ш ярыъш
      case 'U':
      if ( secm->dh==0 && stick(letr)
           || letr=='i' && versr->vers[0].prob>I_wo_point  //'i' с точкой
         )
        if ( letl=='l' || letl=='L' )
          pen = -2*CUT_PEN;
      break;

  //°ЄЁрЇ фы  ёюсЁрээющ 'H'
      case 'H':
      if ( secm->dh==0 && stick(letl) && stick(letr) )  pen = -H_PEN;
      break;

  //°ЄЁрЇ фы  'm', хёыш ёюсЁрэр шч 'i'
      case 'm':
      if ( letl=='i' || letr=='i' )  pen = -mi_PEN;
      break;

  // 'c-l' => 'd', если склеены
      case 'd':
      p=versr->vers[0].prob;
      if ( ( stick(letr) || letr=='f' && p<180 )      // 'l'
           && memchr("c(<",letl,3)                    // 'c'
         )
        if ( secm->dh != 0 )                          //'c' и 'l' склеены
        {
          if ( vers0->vers[0].prob>MINlet ) goto retu;//принимаем 'd'
        }
        else                                          //'c' и 'l' не склеены
          if ( p>MINlet && versl->vers[0].prob>MINlet )
            goto retb;                                //оставляем 'c' и 'l'
      break;
    }
    if (i1<ip)  pen = -pen;
  }

  pen=norm(pen,width);
  if ( cur_meas+pen < secr->lv.v1 )
  {                              //новая мера лучше; проверяем исключения
    if (i1 != ip)
    {
    //несколько несвязных секций собрались в "плохой"
      if ( !dust(vers0) && vers0->vers[0].prob<MAX_RO-RO_COMPOSE
           && not_connect_chain(il,i0,cut_list) ) goto retb;

    //объединение двух сегментов
      if (il==secm->px)
      {
      //запрещается приклеивать 'i', если получается та же буква
        if ( letl==let0 &&
				(letr=='i' || letr==liga_i ||
					language == LANG_TURKISH &&  // 30.05.2002 E.P.
					(letr==i_sans_accent||letr==II_dot_accent)
				)  // X+i=X
               && versl->vers[0].prob>200 ||
             letr==let0 &&
				(letl=='i' || letl==liga_i ||
					language == LANG_TURKISH &&  // 30.05.2002 E.P.
					(letl==i_sans_accent||letl==II_dot_accent)
				)  // i+X=X
               && versr->vers[0].prob>200 )
          goto retb;
      }

      switch(let0)
      {
      //шёъы■ўхэшх "єэштхЁёры№э√ї" сєът
        case (BYTE)'м':	// 'ь'
        case (BYTE)'М': // '╠'
        case (BYTE)'ї':
        seci=secr;
        while ( seci>secl )
        {
          versi=&seci->versm;
          let=versi->vers[0].let;
          if ( ( let==(BYTE)'о' &&
			  		!is_russian_turkish_conflict(let) // 21.05.2002 E.P.
			     ||
				let==(BYTE)'О'
			   ) && versi->vers[0].prob>trs2
			 ) goto retb;

          seci=cut_list+(seci->px);
        }
        break;

      //запрещается расширять "9" до "И"
        case (BYTE)'И':
        if ( letr=='9' && versr->vers[0].prob>=RELIABLE_9 )  goto retb;
        break;
      }
    }
    reason=rule;
    goto ret1;
  }

//новая мера хуже; проверяем исключения

  if (i1==ip)
  {
  //dust-секция собралась в не-dust
    if ( dust(&secr->versm) && !dust(vers)
         && vers->vers[0].prob>=MAX_RO-RO_COMPOSE )  goto ret1;
  }
  else
  {
  //dust-секции собрались в не-dust
    if ( !dust(vers0) && vers0->vers[0].prob>=MAX_RO-RO_COMPOSE
         && dust_chain(il,i0,cut_list) ) goto retu;

  //большой элемент разрезан на 2 dust'а
    if ( let_or_bad(vers0) && dust(versr) && dust(versl) && il==secm->px
         && secm->dh != 0 )
      goto retu;
  }

  reason=rule;

  //отвергаем
ret0:
    if (debug_on)
#ifndef MY_DEBUG
    if (det_trace)
#endif
      s += sprintf(s," pen=%d => decline %s",pen,reason);
    return 0;

  //принимаем
ret1:
    if (debug_on)
#ifndef MY_DEBUG
    if (det_trace)
#endif
    {
      let = (vers->nvers) ? vers->vers[0].let : ' ';
      s += sprintf(s," pen=%d => accept %c %s",pen,let,reason);
    }
    return 1;

retu:    //объединить
   if (i1<ip)  goto ret1;
   else        goto ret0;

retb:    //разрезать
   if (i1<ip)  goto ret0;
   else        goto ret1;
}

/*-----------------06-15-95 03:19pm---------------------------------
  not_connect_chain  проверяет наличие цепочки из изолированных сек-
                     ций от i1-го до i0-го сечения;
                     cut_list - список сечений
------------------------------------------------------------------*/
static BYTE not_connect_chain(INT i1, INT i0, struct cut_elm *cut_list)
{
  struct cut_elm *cur;  //текущее сечение на оптимальном пути
  INT i;

  cur=cut_list+i0;
  i=cur->px;
  if ( (cut_list+i1)->dh != 0 || cur->dh != 0 || i<=i1)  return 0;

  while ( i>i1 )
  {
    cur=cut_list+i;
    if ( cur->dh != 0 )  return 0;  //был разрез
    i=cur->px;
  }
  if ( i==i1 ) return 1;
  else         return 0;
}

/*-----------------06-15-95 03:19pm---------------------------------
  dust_chain  проверяет наличие цепочки из dust-секций между i1-ым и
              i0-ым сечением
------------------------------------------------------------------*/
static BYTE dust_chain(INT i1, INT i0, struct cut_elm *cut_list)
{
  struct cut_elm *cur,*last;

  for ( cur=&cut_list[i0], last=&cut_list[i1]; cur>last; cur-- )
   if ( !dust(&cur->versm) )  return 0;
  return 1;
}

/*-----------------05-23-95 12:45pm---------------------------------
  recover_path  тюёёЄрэртыштрхЄ cell'√ юяЄшьры№эюую яєЄш, чрфрээюую
                ьрёёштюь cut_list шч N ёхўхэшщ ё чрфрээ√ь эръыюэюь inc;
                LC,RC - уЁрэшЎ√ шэЄхЁтрыр (эх тъы.);
                ovfl - Їыру: шёёыхфєхь√щ єўрёЄюъ эх яюьхёЄшыё  т ЁрёЄЁ;
                kita - ьхёЄю фы  эют√ї cell'ют;
                тючтЁр∙рхЄ єърчрЄхы№ эр cell, ъєфр эрфю тхЁэєЄ№ё  яЁш
                яЁюфюыцхэшш; яЁш эхєфрўх тючтЁр∙рхЄ NULL
-------------------------------------------------------------------*/
static cell *recover_path(void *kita, raster *r, struct cut_elm *cut_list,
                          INT N, cell *LC, cell *RC, CHAR ovfl)
{
  INT i,i0;
  cell *B;    // current cell
  INT ib=N-1;                       //эюьхЁ ёхўхэш , ъєфр тючтЁр∙рЄ№ё 
  cell *opt_cell[MAX_SECT]={NULL};  //cell'√ ёхўхэшщ юяЄшьры№эюую яєЄш
  cell *dcell[MAX_SECT];            //тёх cell'√ юфэюую ёхуьхэЄр
  INT    Nd;                        //шї ъюышўхёЄтю
  grup box,top,bottom,far_top,far_bottom;   //юяшёрЄхыш уЁєяя cell'ют
  struct cut_elm *seci,*seci0;      //юяшёрЄхыш i-ую ш i0-ую ёхўхэш 
  SVERS *versi0;                     //тхЁёшш ёхўхэш 
  BYTE csv[32];                     //ярЁрьхЄЁ√ ёхўхэш 
  s_glue GL;
  cut_pos    cpos;
  MN *mn1;

  if( langBul && LC && LC->nvers > 0 &&
	  ( LC->vers[0].let == 154 || LC->vers[0].let ==234 ||
	    LC->vers[0].let == 156 || LC->vers[0].let ==236
	  )
	 )     // Nick 29.09.2000
  { // ┬ сюыурЁёъюь эхЄ √ - эх юЄЁхчрЄ№ ярыъє яюёых №,·
	for( i0 = N-1; i0 > 0;i0 = seci0->px)
	{
     seci0=&cut_list[i0];
     versi0=&seci0->versm;
	 if( seci0->px <= 0 )
		 break;
    }

	if( i0 > 0 && versi0->vers[0].let == '|' )
	 return NULL;
  }


  if (ovfl)                    //надо отступить влево;  куда?
  {
    seci0=cut_list+ib;                //отступаем на dust'ы
    while (ib>0 && dust(&seci0->versm))
    {
      ib=seci0->px;  seci0=cut_list+ib;
    }
    if (seci0>cut_list)
    {
      CHAR *ilet;
      version *v0;
      versi0=&seci0->versm;  v0=&versi0->vers[0];
      if (versi0->nvers && v0->prob<trs2) set_bad(versi0);
      if (seci0->px != 0)
        if ( bad(versi0) || v0->let==(BYTE)'ы' ||
            ( (ilet=strchr(letters_left_to_bad,v0->let)) &&
			  !is_russian_baltic_conflict(v0->let) &&	// 17.07.2001 E.P.
			  !is_russian_turkish_conflict(v0->let) &&	// 21.05.2002 E.P.
              v0->prob < prob_left_to_bad[(BYTE*)ilet-letters_left_to_bad]
           ))
        {
          if (strchr("иИнНпПшШщЩ",v0->let) &&
			  !is_russian_turkish_conflict(v0->let) // 21.05.2002 E.P.
			  )	// "ш╚э═я╧°╪∙┘"
			  set_bad(versi0);
          ib=seci0->px;           //отступаем на один символ
          seci0=cut_list+ib;      //отступаем на dust'ы
          while (ib>0 && dust(&seci0->versm))
          {
            ib=seci0->px;  seci0=cut_list+ib;
          }
        }
    }
  }

//делаем все разрезы

    i = cut_list[N-1].px;    //(N-1)-ый - фиктивный
    while ( i>0 )
    {
      seci=&cut_list[i];
      mn1=cut_rast(&r->pict,r->w,r->h,r->top,r->left,cut_list,(CHAR)i,2,csv,&cpos);
      i=seci->px;            //следующий на оптимальном пути
    }
    mn1=cut_rast(&r->pict,r->w,r->h,r->top,r->left,cut_list,0,3,csv,&cpos);
    if (!mn1) return NULL;

    take_kit_addr(kita);

//создаем компоненты и распределяем по сечениям

    for ( i=0; i<MAX_SECT && mn1; i++ )
    {
       B=create_my_cell(mn1,LC,0,0);
       inter_diff(B);
       set_erection(B, cut_list->rv.v2);
       i0=locate(B,cut_list,N,r->left);
       seci0=cut_list+i0;  seci=cut_list+seci0->px;
       B->cg_flag |= c_cg_cutdone;      //сформирован программой cut_glue
       if ( seci->dh != 0 && B->r_col<=seci->x+r->left+1)
         B->cg_flag |= c_cg_cutl;
       if (seci0->dh != 0 && B->r_col+B->w>=seci0->x+r->left)
         B->cg_flag |= c_cg_cutr;
       B->complist=(void *)opt_cell[i0];
       opt_cell[i0]=B;
       mn1=mn1->mnnext;
    }

//объединяем, если нужно, и заполняем версии

    i0 = N-1;
    while ( i0>0 )
    {
      seci0=&cut_list[i0];
      versi0=&seci0->versm;
        if ( let_or_bad(versi0) && (B=opt_cell[i0]) )
        {
          if ( seci0->gvarm )           //компоненты модифицировались
          {
          //из связного списка - в dcell
            Nd=0;
            do
            {
              dcell[Nd++]=B;
              B=(cell *)B->complist;
            }
            while ( B );

          //классификация по расположению
            if ( classify(dcell,Nd,&box,&bottom,&top,&far_bottom,&far_top) )
            {
              Nd -= far_bottom.n+far_top.n;  //удаляем далекие
              if ( !(seci0->gvarm & up_dust) )
                Nd -= top.n;                 //удаляем верхние dust'ы
              if ( !(seci0->gvarm & low_dust) )
              {                            //удаляем нижние dust'ы
                memcpy(&dcell[box.n],&dcell[box.n+bottom.n],
                       sizeof(cell *)*top.n);
                Nd -= bottom.n;
              }
            }

          //совокупный элемент
            seci0->gvarm &= glued;
            B=unite(LC,dcell,Nd,&GL,&seci0->gvarm);
            if (B)
            {
              set_erection(B, cut_list->rv.v2);
              if (GL.ncell>1)         //если ncell==1, тогда celist[0]=B
                for ( i=0; i<GL.ncell; i++ )  del_cell(GL.celist[i]);
            }
          }
          else  //компоненты не модифицировались
          {
            cell *C=B;
            while ( B )
            {
              if ( !dust(B) )  break;
              B=(cell *)B->complist;
            }
            if (!B && let_or_bad(versi0) && versi0->nvers)
            {                 //все dust'ы, но есть версия
              B=C;            //ищем подходящий по размеру
              while (B != 0)
              {
                if (B->h>sMALL_SIZE) break;
                B=(cell *)B->complist;
              }
              if (B)          //преобразуем в большой
              {
                cell *LB=B->prev;
                (B->next)->prev=LB; LB->next=B->next; // DELETE
                set_bad(B);
                set_bad_cell(B);
                insert_cell1(B,LB);
              }
            }
          }
          if (B)
            if ( !dust(B) )
            {
              rest_vers(B,versi0);
              seci=cut_list+seci0->px;
              if ( seci0->dh != 0 )  B->cg_flag |= c_cg_cutr;
              if (  seci->dh != 0 )  B->cg_flag |= c_cg_cutl;
              if (seci==cut_list)
              {
                B->cg_flag = B->cg_flag | cut_list->gvarm & c_cg_cutl;
                B->cg_flag_fine = B->cg_flag_fine | cut_list->gvarr &
                  ( c_cg_cut_tl | c_cg_cut_ml | c_cg_cut_bl | c_cg_cut_fl );
              }
              if (i0==N-1)  B->cg_flag |= cut_list->gvarm & c_cg_cutr;
            }
        }
      i0=seci0->px;            //следующий на оптимальном пути
    }

  //ищем первый cell правее ib

  ib=r->left+(cut_list+ib)->x;
  for (B=LC->next; B != RC && lefter(B,ib); B=B->next);

  return B;
}

/*-----------------05-24-95 11:06am---------------------------------
  locate  определяет номер сегмента, которому принадлежит cell B;
          номер сегмента равен номеру его правого сечения из списка
          cut_list N сечений
-------------------------------------------------------------------*/
static INT locate(cell *B, struct cut_elm *cut_list, INT N, INT left)
{
  INT i,ip;  //текущее и предыдущее сечения на оптимальном пути
  struct cut_elm *seci; //описатель i-го сечения

  ip=i=N-1;
  while ( i>0 )
  {
    seci=&cut_list[i];
    if ( !lefter(B,seci->x+left) ) break;
    ip=i;
    i=seci->px;            //следующий на оптимальном пути
  }
  return ip;
}

/*--------------------------------------------------------------------
  set_type  приводит тип cell'а C (dust - не dust) к типу vers
--------------------------------------------------------------------*/
/*
static void set_type(cell * C, SVERS *vers)
{
  CHAR new_dust=dust(vers);

  if( dust(C) && !new_dust && C->h>sMALL_SIZE)   //меняем dust на не dust
  {
    cell *LC=C->prev;
    (C->next)->prev=LC; LC->next=C->next; // DELETE
    set_bad(C);
    set_bad_cell(C);
    insert_cell1(C,LC);
  }
  else
    if( !dust(C) && new_dust )         //меняем не dust на dust
    {
      set_dust(C);
      set_bad_cell(C);
      C->prevl->nextl=C->nextl;
      C->nextl->prevl=C->prevl;
      err_pnlet(C);  // AL 940318 make err links
    }
}
*/
/*--------------------------------------------------------------------
   is_stick  возвращает 1, если cell B - stick, и 0 в противном случае
--------------------------------------------------------------------*/
static INT is_stick(cell *B)
{
#define hist_len RASTER_HEIGHT*2/3+1
  INT i,j;
  BYTE hist[hist_len],*hp,*he;  //гистограмма
  CHAR emax;          //правый край гистограммы + 1
  BYTE hmax,hmax2;    //высота горба и 1/2
  INT  w;             //ширина горба
  INT s0,s1;          //площадь горба и остальной части
  c_comp *cp;         //envelope C
  lnhead *lp;         //текущая линия
  struct int_s *intp; //текущий интервал

  if ( !B->env || B->h < my_bases.ps-(my_bases.ps>>2) ||
       ( B->w+(B->w>>1) > B->h ) )  return 0;

  //строим гистограмму и вычисляем hmax

  memset(hist,0,hist_len); hmax=0; emax=0; cp=B->env;
  lp=(lnhead *)((CHAR *)cp+cp->lines+2);  //шапка первой линии
  for ( i=0; i<cp->nl; i++ )
  {
    intp=(struct int_s *)(lp+1);
    for ( j=0; j<lp->h; j++,intp++ )
    {
      he=hist+(intp->e-intp->l); emax=MAX(emax,intp->e);
      for ( hp=hist+(intp->e-1); hp>=he; hp-- )
      {
        (*hp)++;
        hmax=MAX(hmax,*hp);
      }
    }
    lp=(lnhead *)((CHAR *)lp+lp->lth);
  }

  //вычисляем площади

  if ( (hmax2=hmax>>1)==0 )  return 0;
  he=hist+emax; hp=hist; s0=s1=w=0;
  while ( *hp<=hmax2 )  s1 += *hp++;
  while ( *hp>hmax2 ) { s0 += *hp; w++; hp++; }
  while ( hp<he )       s1 += *hp++;
  if ( (w<<1)+(w>>1) > hmax )  return 0;
  s1 += (B->h*w-s0);                  //штраф за плохой горб
  if ( s1<<1 > s0 )  return 0;
  det_snap(B,"bring sticks");
  return 1;
#undef hist_len
}

/*-----------------05-04-95 05:35pm-----------------
   paste  собирает 'ы' и 'о' по всей строке
--------------------------------------------------*/
static void paste()
{
  cell *B,*BC,*EC;
  BYTE p,c2,c3;
  INT bdiff;
  CHAR dflag;
  s_glue GL;
  BYTE expect;   //ожидаемый результат
  INT  inc;      //усредненный наклон

  BC=cell_f();
  while (1)
  {
    BC=BC->nextl;
    if (!BC) break;
    EC=BC->nextl;
    if (!EC) break;                      // BC = dummy cell at end
    if (!EC->next) break;                // EC = dummy cell at end
    if (!tsimple(BC) || !may_glue(BC)) continue; //"glued" earlier or never
    if (!tsimple(EC) || !may_glue(EC)) continue; //"glued" earlier or never
    if (BC->nvers && EC->nvers)  // recovering after stick cut
    {
      c2=BC->vers[0].let;
      c3=EC->vers[0].let;
      if (
		  !is_turkish_language(language) && // 21.05.2002 E.P.
		  glue_to_o (c2,c3,BC,EC)
		  )
        expect=(BYTE)'о';
      else
        if(memchr("ьЬЪ",c2,3) &&
			(memchr("/1!()°",c3,6) || c3==liga_exm) // 10.09.2000 E.P.
			&&
           abs(BC->h - EC->h) < 4 && abs(BC->row - EC->row) < 4 &&
           EC->r_col - (BC->r_col+BC->w) < BC->h/10 + 4
          ) /*to paste ы */
          expect = is_lower(c2) ? (BYTE)'ы' : (BYTE)'Ы';
        else
          continue;

      bdiff=BC->bdiff; dflag = BC->difflg & 0xf0;
      GL.ncell=2; GL.ngluc=1;
      GL.complist[0]=BC->env;  GL.celist[0]=BC;
      GL.complist[1]=EC->env;  GL.celist[1]=EC;
      GL.complist[2]=NULL;     GL.celist[2]=NULL;
      inc = erection_compose_inc(2, GL.celist);
      B=comp_to_cell(BC,GL.complist,2,(CHAR)bdiff,dflag);
      GL.var=GL.fres=GL.arg=0; GL.flarg=GFsrc;
      if ( (B->flg&c_f_dust) || crecell(B,&GL,3)<0 )  //совокупный растр - в BOX'е, B не изменился
      {
        del_cell(B); BC=EC; continue;
      }
      set_erection(B, inc);
      compose_cg_flags(B,BC,EC);
      dmBOX(B,&GL);

      p= (B->nvers) ? B->vers[0].prob : 0;
      if (expect==(BYTE)'о')
      {
        if (!memchr("oO0оО",B->vers[0].let,5))
        {
          del_cell(B); BC=EC; continue;
        }
      }
      else    // ы
        promote('a',B,expect,60);
      if (p>70)
      {
        del_cell(BC); del_cell(EC);
        BC=B;
      }
      else
      {
        del_cell(B); BC=EC;
      }
    }
  }
}

/*-----------------------------------------------------------------------
  compose_cg_flags устанавливает cg_flag и cg_flag_fine cell'а B, в пред-
                   положении, что он сформирован из L(левый) и R(правый)
------------------------------------------------------------------------*/
static void compose_cg_flags(cell *B, cell *L, cell *R)
{
  B->cg_flag = B->cg_flag & ~(c_cg_cutl | c_cg_cutr) |
               L->cg_flag & c_cg_cutl | R->cg_flag & c_cg_cutr;
  B->cg_flag_fine = L->cg_flag_fine &
                    ( c_cg_cut_tl | c_cg_cut_ml | c_cg_cut_bl | c_cg_cut_fl )
                    |
                    R->cg_flag_fine &
                    ( c_cg_cut_tr | c_cg_cut_mr | c_cg_cut_br | c_cg_cut_fr );
}

/*-----------------02-15-95 05:29pm--------------------------------------
  full_recog распознает cell B и возвращает 1, если результат лучше
             порога tol и 0, если нет;
             распознавание по эвентам производится, если trs>0;
             распознавание по 3x5:
               - обязательно при trs==0,
               - как правило при trs>0,
               - если текущий результат хуже abs(trs) при trs<0;
             структура gl0, если задана, используется дискриминаторами;
------------------------------------------------------------------------*/
INT full_recog (cell *B1, s_glue *gl0, INT trs, INT tol)
{
  SVERS svown,svorg;
  s_glue  sg;
  s_glue  *GL=&sg;
 BYTE c, c_sacc, ct, svarg;
 INT flag_m, flg_own;
 BYTE  p1, pans, porg;
 CHAR wrk[32];
 CHAR *pmsg,madeBOX;
 extern INT best_answer_BOX;
 extern CHAR db_pass;

 static CHAR ms220ready[]={"m > 220 --> ready"};
 static CHAR msw3ready[]={"w <= 3  --> ready"};
 static CHAR msf170bef[]={"f supports i,j"};
 static CHAR msnat220[]={"native >220 --> ready"};
 static CHAR ms29500[]={"stick > 29500"};
 static CHAR msingl[]={"single nondiscrim ready"};

  if (dust(B1))  return 0;

#ifdef PROTOCOL
//  rec_count++;
#endif

  //if (pass4_in)
  //{
  //  estletter(B1,gl0); goto retp;
  //}

  if(pass4_in)
  {// OLEG : SERBIAN PASS4
  INT ret =estletter(B1,gl0);
  if (B1->vers[0].let==0)  B1->vers[0].let=bad_char;
  if( language==LANG_RUSSIAN && langSer &&
      (B1->flg&c_f_let) && B1->nvers && (B1->vers[0].let==SERB_j||B1->vers[0].let=='j') &&
      ij_dot(B1)>0 )
    ret -= 100;
  goto retp;
  //return ret;
  }

  GL->ncell=1; GL->ngluc=1;                //данные для crecell
  GL->celist[0]=B1; GL->complist[0]=B1->env;
  GL->celist[1]=NULL; GL->complist[1]=NULL;
//  if ( cut(B1) ) GL->flarg=GFcut;          //отрезанные уточняются по 3x5
//  else           GL->flarg=0;
//  GL->flarg=0; GL->ncell=n;
  GL->arg=0;  GL->flarg=0;
  if (trs==0)      GL->arg |= GABOXR;     //обязательно по 3x5
  else if (trs>0)
       {           GL->arg |= GAtigr;     //по эвентам
                   GL->flarg=GFcut;       //по 3x5, как правило,
       }
       else           trs = -trs;         //по 3x5 при необходимости
//  if (trs<0)  trs = -trs;
//  else
//  {
//    GL->arg |= GABOXR;
//    if (trs>0)  GL->arg |= GAtigr;
//  }

 flg_own = 1;   // self-made value (no 5-box estimate)
 GL->var=0;
 if ( GL->arg & GAtigr )       // request for full estimation
 {   recog_cell(B1);
  if (db_status && (db_trace_flag & 2))
    est_snap(db_pass,B1,"events");
 }
 c = B1->vers[0].let;
 p1= B1->vers[0].prob;
 if ( let(B1) && p1<trs ) let_to_bad(B1);

//   send to BOX: bad patterns;
//                miltiple version;
//                all 'm' letters   (m, n, u)
//                all cutten letters with simple structure

 if ((B1->flg & c_f_let) && (B1->nvers==1))
 {
   if (language==LANG_RUSSIAN && strchr("оЗз3",c))  goto estiBOX;
   if (
	(language != LANG_ENGLISH) &&
	(memchr ("aoeu",B1->vers[0].let,4))
      )
      { accent(B1);
	if (B1->nvers != 1)
	  goto  not_a_single;
      }
   save_vers(B1,&svown);   // save the whole vector
   abris_reset();
   GL->celist[0]=B1; GL->celist[1]=NULL; GL->ncell=1;

   // 'm' is reliable in events; just test against legs

   flag_m=0;
   c_sacc = let_sans_acc[c];
   if (c_sacc=='m') flag_m=1;	// 'm'
   if (c_sacc=='n') flag_m=2;	// 'n'
   if (c_sacc==liga_ri) flag_m=2;    // "ri"
   if (c_sacc=='u') flag_m=4;	// 'u'
   if (flag_m)
   {
     madeBOX=1;
     atlant(B1,GL,3,flag_m);
     p1=B1->vers[0].prob;
     if (db_status && (db_trace_flag & 2))
       est_snap(db_pass,B1,"atlant");
     if (c_sacc != 'm')
     {
       rest_vers(B1,&svown);              // native u,n,rt -
       goto deciBOX;
     }
     if (p1 >= trs)           // legs OK - ready answer if 'm',
     {
       B1->flg &= ~c_f_bad;  B1->flg |= c_f_let;
       save_vers(B1,&svown);
       pans = p1;
       pmsg = ms220ready;
       goto test_BOX_request;
     }
     //  'm'-legs NOT OK
     B1->flg &= ~c_f_let; B1->flg |= c_f_bad;
     save_vers(B1,&svown);
     pans = p1;
     pmsg = NULL;
     goto test_BOX_request;
   }

   if (B1->w < minw)
//   if (B1->w <= 3)
   {
     pans=B1->vers[0].prob;
     pmsg = msw3ready;
     goto test_BOX_request;
   }
   if  (c==liga_rt)    // 'rt'
   { p1 -= 82; if (p1 < 2) p1= 2; B1->vers[0].prob=p1; goto argBOX_ready; }
   if  ((c=='h') || (c=='b'))
   { p1=(BYTE)h_filt(B1,p1,GL,c);  goto deciBOX; }
      c_sacc = let_sans_acc[c];
   if ( (memchr("sSaoO0QGDMNHURdxq6<>cCkwWBEA",c_sacc,28)) ||
        ( c_sacc == ss_deaf_sound && language != LANG_RUSSIAN) ) // Vademar 2.2.93
    {  p1=(BYTE)abris(GL,B1,c_sacc,p1); goto deciBOX; }

   if ( memchr("rtfTJ()<>[]LI1il!/F7тТгГ°|",c,26) &&
   		  !is_russian_turkish_conflict(c) // 21.05.2002 E.P.
		||   // 07.01.1993 (see S_TOOLS.C)
        ( language != LANG_RUSSIAN && (c==liga_i  ||                                   // FARA REDACTION

	!is_baltic_language(language) && 	// 17.07.2001 E.P.
	!is_turkish_language(language) &&
		(	// 21.05.2002 E.P.
		c==i_right_accent ||
		c==i_roof_accent  ||
		c==II_right_accent||
		c==II_roof_accent ||

		// ╩юэЇышъЄэ√х ъюф√ 07.09.2000 E.P.
		!is_cen_language(language) && (
			c==i_left_accent  ||
			c==i_2dot_accent  ||
			c==II_left_accent ||
			c==II_2dot_accent
		) ) ||

		is_baltic_palka(c) ||	// ┴рыЄшщёъшх ярыъш. 10.07.2001 E.P.
		is_turkish_palka(c)		// ╥єЁхЎъшх ярыъш. 21.05.2002 E.P.
	) ) )
   {
     stick_center_study(B1,GL,1);      // Oleg : 07.08.92.
     p1 = B1->vers[0].prob;
   deciBOX:
     if (db_status && (db_trace_flag & 2))
     {
       sprintf (wrk,"single discrim; p=%d", p1);
       est_snap(db_pass,B1,wrk);
     }
     if (p1 <= 2) p1=2;
     if (p1 < trs)        // single version to be cut
     {
       if ((c=='f') && (p1 >= 170))
       { cell *b1n;
         ct=(b1n=B1->nextl)->vers[0].let;
         if (((ct=='i') || (ct=='j')) /*****  && (b1n->cg_flag & c_cg_rqdot) ****/)
         {
           pans=B1->vers[0].prob;   // weak 'f' provides dot to i,j
           pmsg = msf170bef;
           goto test_BOX_request;
         }
       }
       if (p1 < 170)             // BOX in full ?
       {
         B1->vers[0].prob=0;
         B1->nvers = 0;
         B1->flg = c_f_bad;
         if (db_status && (db_trace_flag & 2))
           est_snap(db_pass,B1,"p<170 to BOX FULL");
         goto estiBOX;
       }
       // not solid, but not too bad  (170 -- trs)
       rest_vers(B1,&svown);
    argBOX_ready:
       if (db_status && (db_trace_flag & 2))
         est_snap(db_pass,B1,"170 < p1 < 220 --> to BOXes");
       goto estiBOX;
     }
     // reliable single >= trs after all discrim.
     if (GL->flarg & GFcut)
     {
       rest_vers(B1,&svown);
 // PERSONAL DISCRIMINATIONS: unreliable cutten letters
       if (c==liga_rt)    // 'rt'
       {  p1-=50; if (p1<2) p1=2; B1->vers[0].prob = p1; }
       if (db_status && (db_trace_flag & 2))
         est_snap(db_pass,B1,"cutten p>220 --> to BOXes");
       goto estiBOX;        // artifact - estimate by BOX with events' value
     }
     B1->vers[0].prob=p1;
     save_vers(B1,&svown);
     pans=p1;
     pmsg = msnat220;
     goto test_BOX_request;
   }  // a stick;

   // cutten letter (stick & others) --> BOX
   if (GL->flarg & GFcut)
   {
      if (db_status && (db_trace_flag & 2))
        est_snap(db_pass,B1,"CUT/GLUE to BOX");
      goto estiBOX;    // cutten version - estimate by BOX
   }
	 if (memchr("1lI()[]{}!",c,10) || c==liga_i ||
		  language == LANG_TURKISH &&  // 30.05.2002 E.P.
			(c==i_sans_accent||c==II_dot_accent) ||
		 c==liga_exm
		)
    if (signif_line_count(B1) == 1)
    {
     svarg=GL->arg;
     if (db_status && (db_trace_flag & 2))
       est_snap(db_pass,B1,"stick, 1 big line -- test 29500");
     GL->arg |= GABOXs;         // direct request
     crepat(B1,GL,0,0);
     crecell(B1,GL,3);         // make raster, box
     dmBOX(B1,GL);
     SBOX_to_static();
     SBOX_to_GL(GL);
     GL->arg=svarg;
     if (best_answer_BOX >= 29500)  // original stick supported by BOX
     {
       rest_vers(B1,&svown);      // retain events' value
       pans=B1->vers[0].prob;
       pmsg = ms29500;
       goto test_BOX_request;
     }
     // weak stick
     B1->flg = c_f_bad;  B1->vers[0].prob=0;  B1->vers[0].let=bad_char;
     if (db_status && (db_trace_flag & 2))
       est_snap(db_pass,B1,"p < 29500 - BOX FULL");
     goto estiBOX;
   }
   // usual native letter (unknown as to be tested against some discrim. rule
   /******** ATEMPT  to confirm single by BOXes ***********  93.07.08 ***
     svarg=GL->arg;
     GL->arg |= GABOXs;         // direct request
     crepat(B1,GL,0,0);
     crecell(B1,GL,3);         // make raster, box
     dmBOX(B1,GL);
     SBOX_to_static();
     SBOX_to_GL(GL);
     GL->arg=svarg;
     rest_vers(B1,&svown);   // back to events
     if (best_answer_BOX < 29200)  // original not confirmed
     // 29200 --> 50; (50 + 254)/2 --> 152
     {
       if (db_status && (db_trace_flag & 2))
         est_snap(db_pass,B1,"p<30000 to BOXes");
       goto estiBOX;
     }
*** END OF ATTEMPT    93.07.08 ***********************************************/
   pans=B1->vers[0].prob;
   pmsg = msingl;
 test_BOX_request:
   if (pmsg && db_status && (db_trace_flag & 2))
     est_snap(db_pass,B1,pmsg);
 if ((GL->arg & GABOXR) == 0)
   goto retp;  // no direct request (for boxes ?)
 }  // let & single
 // not a single version
 not_a_single:
 if ((B1->flg & c_f_bad)      ||        // bad answer
      (B1->nvers > 1)         ||        // multiple versions
      (GL->arg & GABOXR)                // BOX explicitly requested
    )
   {
 estiBOX:
    madeBOX=1;
    if (db_status && (db_trace_flag & 2))
      est_snap(db_pass,B1,"to make all BOXes");
    flg_own = 0;   // estimated by 5-box
    if ( crecell(B1,GL,3) < 0 )            //растр в BOX'е, B не изменился
      return 0;
    if ( gl0 )  GL=gl0;
//    if ( composed(B1) )  GL->ncell=2;        //для diffrv
    save_vers(B1,&svorg);
    if ( B1->nvers ) porg=B1->vers[0].prob;
    else             porg=0;
    dmBOX(B1,GL);
    if ( B1->nvers ) pans=B1->vers[0].prob;
    else             pans=0;
    if ( !pans && porg )  rest_vers(B1,&svorg);
   }

   if ( language != LANG_RUSSIAN )
     if ( B1->nvers )
     {
/*
       if ( cut(B1) )
         if ( (c=B1->vers[0].let)=='l' || c=='I' )
         {
           INT p=(INT)B1->vers[0].prob-trs2-2;
           if ( p>0 )  discr_vers(B1,p,1);   //для всех версий -p1
         }
*/
     }
     else
       if ( bad(B1) && !cut(B1)
            && B1->env && (B1->env->nl==1) && 3*B1->w<=B1->h  )
       {
         INT d=my_bases.ps>>2, bottom=B1->row+B1->h;

         if ( is_stick(B1)
              && B1->row>=my_bases.b2-d && B1->row<=my_bases.b2+d
              &&  bottom>=my_bases.b3-d &&  bottom<=my_bases.b3+d
            )
         {                                  //'i' без точки не распознается
           B1->vers[0].prob=I_wo_point;
           B1->nvers=1; B1->vers[0].let=liga_i;
         }
         else
           set_bad_cell(B1);
       }

retp:
  if ( B1->nvers ) pans=B1->vers[0].prob;
  else             pans=0;
  if (pans >= tol)
    { B1->flg &= ~c_f_bad;  B1->flg |= c_f_let; }
  else
    { B1->flg &= ~c_f_let; B1->flg |= c_f_bad; }
  glsnap('a',B1,"");
  return pans>tol;
}

static void corr_cut()
{ cell *b1, *b2, *b3;
  BYTE c1,c2,c3;

  b2=cell_f();
  while ( (b2=b2->nextl)->nextl )
  {
    if ( !b2->nvers )
    {
      if ( language == LANG_RUSSIAN
          && (b2->env && (b2->env->nl==1) || 3*b2->w<=b2->h) )
      {                               //попытка распознать "!"
        stick_center_study(b2,NULL,1);
        det_snap(b2,"bring sticks");
      }
      continue;
    }
    if ( bad(b2) && b2->vers[0].prob>MINlet ) set_let(b2);
    if ( language != LANG_RUSSIAN )
    {
      b1=b2->prevl; b3=b2->nextl;
      c2=b2->vers[0].let;
      c1=b1->vers[0].let;
      c3=b3->vers[0].let;
       // try to recover "stick_to_something" producing 'k', 'd', 'b'
       if (memchr("lI1/J)!",c2,7) ||         // stick character ?
           c2==liga_i ||
		   language == LANG_TURKISH &&  // 30.05.2002 E.P.
			(c2==i_sans_accent||c2==II_dot_accent) ||
		   c2==liga_exm
		   )
         // in combinations  (vI) (WI) (uI)  promote  't' in place of 'I'
         if ((b2->cg_flag & c_cg_cutr) && ((c1=='v') || (c3=='w') || (c3=='u')))
         {
           promote('a',b2,'t',16);
           continue;
         }
       if ((c1=='r') && (c2=='t') && have_upper_dot(b1))
         promote ('a',b1,'i',16);
     }
  }   // while all cells
}

static INT have_upper_dot(cell *c)
{
 cell *cc;
 INT H;
 H=my_bases.ps;
 cc=c->prev;
 if ((cc->flg & c_f_dust) &&
      (c->w>4 && cc->h>=2 && cc->w>=2 &&
	(abs(cc->h-cc->w)<=H/6 || cc->h<cc->w && cc->w-cc->h<=H/4) &&
	cc->col+1>=c->col && cc->col+cc->w-5<=c->col+c->w ||
       c->w<=4 && abs(c->col-cc->col+(c->w-cc->w)/2)<=2) &&
      cc->row+cc->h-2<=my_bases.b2)
   return 1;
 cc=c->next;
 if ((cc->flg & c_f_dust) &&
      (c->w>4 && cc->h>=2 && cc->w>=2 &&
	(abs(cc->h-cc->w)<=H/6 || cc->h<cc->w && cc->w-cc->h<=H/4) &&
	cc->col+1>=c->col && cc->col+cc->w-5<=c->col+c->w ||
       c->w<=4 && abs(c->col-cc->col+(c->w-cc->w)/2)<=2) &&
      cc->row+cc->h-2<=my_bases.b2)
   return 1;
 return 0;
}

void set_bad_vers(SVERS *c)
 {
 c->nvers=0;
 c->source = 0;
 c->vers[0].let=bad_char;
 c->vers[0].prob=0;
 c->vers[1].let=0;
 if (c->flg & (c_f_let+c_f_bad)) c->flg=c_f_bad;
 }

static INT forbid_stick_cut(cell *c, SVERS *vers, INT h, cut_pos *cpos,
                            INT edge)
 {
 BYTE l, prob;
 INT  wd, bm3, bm, b3;
 INT  row1, row2;

  l = vers->vers[0].let;
  prob = vers->vers[0].prob;

  // is it a stick ?
  if ( (l!='i') && (l!='l') && (l!='1') && (l!='I') && (l!=liga_i) &&
		!(language == LANG_TURKISH &&  // 30.05.2002 E.P.
			(l==i_sans_accent||l==II_dot_accent)
		 )
	 )
	 return 0;

  wd = (row2=cpos->row2)-(row1=cpos->row1);  // cut width
  if (wd > h-2)  // full cut
    return 1;
  //93.01.28 allow the non_full cut for strong 'i' (with dot)
  if ((l=='i') && (prob >= 200)) return 0;
  b3=my_bases.b3 + c->bdiff;
  ///////////
  if (2*(b3-row2)-1 > my_bases.ps) return 0;  // above middle line (b2/b3)
  ///////////
  bm3=(my_bases.bm+my_bases.b3)/2+c->bdiff;  bm = my_bases.bm + c->bdiff;

  // first try to retain, then to abort;
  // before 01.06 the 2 lines of code were after 'thickness' check
  ///////
  ///////   LOOK ABOVE
  ///////   if (row2 <= bm) return 0;
  ///////   the cut is entirely upper than middle
  ///////
  if (row1 >= bm3) return 0;     // or lower than (middle--b3)
  if (cpos->flag & 8) return 1;  // thick nonsimple cut
  if (cpos->flag & 4) return 0;  // sophisticated cut (double, horizontal)
  if (3*wd > h)    return 1;  // simple thick
  /////// // 93/01/25 very thin cut --> not a 'k' -- NOT INVENTED
  ////////   NOT INVENTED :  if (5*wd < c->h) return 0;
  ////////   stda17/31  making  ki--> ld
  ////////   stdc8/29   dunkle  kl--> ld
  if (row2 >= (b3-2)) return 0;  //  till very bottom
  if (edge == 1)  return 1;      // at right side

  // SO BEFORE 01.06 stick cuts were:
  //    thick - anywhere
  //    starting and ending 'in the middle' AND at right side of stick
  // AFTER 01.06 stick cut is:
  //
  return 0;
//
}

cell * create_my_cell(MN * mn, cell * ci, CHAR bdiff, CHAR dflag)
{
 cell * C = create_cell1(mn,ci,bdiff,dflag);

 //меняем не dust на dust, если смещен по высоте

 if( !dust(C) && ( C->row>my_bases.bm || C->row+C->h<my_bases.b2 )
	 // Nick - 17.05.99
	 && (!p2_active || C->h <= CCOM_DUST_SIZE || C->w <= CCOM_DUST_SIZE) )
 {
   set_dust(C);  set_bad_cell(C);
   C->prevl->nextl=C->nextl;
   C->nextl->prevl=C->prevl;
   err_pnlet(C);  // AL 940318 make err links
 }

 return C;
}
