/*
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 "func.h"
#include "cut_glue.h"
#include "dmconst.h"
#include "linutil.h"

 extern BYTE db_status;  // snap presence byte
 extern BYTE db_trace_flag;  // 2 - more detailed estimate (ALT-F7)

//юс∙шх ярЁрьхЄЁ√ ёъыхщъш
 static cell *LC;                  //cell ёыхтр юЄ ёъыхщъш
 static raster *r;                 //ЁрёЄЁ
 static INT ncut;
 static struct cut_elm *cut_list;  //ёяшёюъ ncut ёхўхэшщ
 static seg_vers **vers_list;      //ЄрсышЎр Ёрёяючэртрт°шїё  ёхуьхэЄют

//Єхъє∙шх ярЁрьхЄЁ√ ЁрчЁхчрэш 
 static INT ib2;  //яюёыхфэ   эрфхцэр  уЁрэшЎр яЁш фтшцхэшш ёыхтр
 static INT ib1;  // Єю цх, хёыш яюёыхфэ   сєътр шч ёяшёър
 static INT il;   //яюёыхфэ   тхЁю Єэр  уЁрэшЎр яЁш фтшцхэшш ёыхтр
 static INT iec;  //уЁрэшЎр яЁш фтшцхэшш яю ёт чэ√ь ъюьяюэхэЄрь
 static INT ie2;  //яюёыхфэ   эрфхцэр  уЁрэшЎр яЁш фтшцхэшш ёяЁртр
 static INT ie1;  // Єю цх, хёыш яюёыхфэ   сєътр шч ёяшёър
 static INT ir;   //яюёыхфэ   тхЁю Єэр  уЁрэшЎр яЁш фтшцхэшш ёяЁртр
 static INT ibc;  //уЁрэшЎр яЁш фтшцхэшш яю ёт чэ√ь ъюьяюэхэЄрь
 static INT iemax;       //яЁртр  уЁрэшЎр
 static CHAR right_dust; //Їыру: ёяЁртр dust (тюьюцэю Єюўър шыш чря Єр )
 static CHAR fl_b;       //Їыру: яЁш фтшцхэшш ёыхтр тёЄЁхЄшыё  "№"
 static BYTE connect;    //Їыру Ёхцшьр яю ёт чэ√ь ъюьяюэхэЄрь

 static INT w1,w2,h2;

static CHAR l2r(INT *ib1, INT *ib2, INT *il);
static CHAR lcut_out(INT ib, INT ii, INT ie, INT wmin, INT wmax,
                     INT set, INT tol, INT *imax, INT *pmax);
static CHAR r2l(INT *ir, INT *ie2, INT *ie1, INT mode);
static CHAR rcut_out(INT ib, INT ii, INT ie, INT wmin, INT wmax,
                     INT set, INT tol, INT *imax, INT *pmax, INT mode);
static INT inc(struct cut_elm **cutp, INT i, INT ie, INT pass, INT x);
static INT dec(struct cut_elm **cutp, INT i, INT ie, INT pass, INT x);
static CHAR one_cut(INT i1, INT ib, INT ie, INT i2);
static CHAR ladjust_cut(INT ib, INT *ib0, INT *il, INT ie, INT d, INT *pmax);
static CHAR radjust_cut(INT ib, INT *ir, INT *ie0, INT ie, INT d,
                        INT *pmax, INT mode);
static CHAR adjust_cut(cell *LC, raster *r, struct cut_elm *cut_list,
       seg_vers **vers_list, INT ncut, INT ib, INT *i0, INT ie, INT d);
static void spec_pairs();
static INT right_bound(raster *r, INT x1, INT x2, INT y1, INT y2);
static CHAR glue_right_dust();
static good_path(struct cut_elm *cut_list, INT ncut);

/*------------------------------------------------------------------
  dp_pass0  линейный проход в растре r на множестве ncut
            точек cut_list; vers_list - результаты для отдельных
            сегментов; LC - cell слева от растра
------------------------------------------------------------------*/
void dp_pass0(cell *CP, raster *rp, struct cut_elm *cutp,
              seg_vers **versp, INT n)
{
  INT i,j,x;
  INT i1,i2;
  BYTE let;
  INT cc;
  struct cut_elm *cut;
  SVERS *vers;
  INT pass;
//  seg_vers *cur_vers;

  if ((ncut=n)<2)  return;
  LC=CP; r=rp; cut_list=cutp; vers_list=versp;
  iemax=ncut-1;  right_dust=0;  fl_b=0;  connect=1;
  ie1=iemax;  vers=&cut_list[ie1].versm;
  ie2=ir=(let(vers) || dust(vers)) ? ie1-1 : ie1;
  ib1=ib2=0;      vers=&cut_list[1].versm;
  if (let(vers))  ib2=1;
  else
    if (cut_list->gvarm & c_cg_cutdone)  ib2=on_path(0,ie2,cut_list);
  iec=ib2;  ibc=ie2;
  w1=sym_width-(sym_width>>2);  w2=sym_width+(sym_width>>2);  h2=r->h<<1;
  cut=cut_list+ie1;
/*
//отщепляем правые dust'ы
  if (dust(&cut->versm) && cut->duflm==0)
  {
    right_dust=1;  iemax--;  cut--;
//    if (dust(&cut->versm) && cut->duflm==0)  iemax--;
    ir=ie1=ie2=iemax;
  }
*/
//крайние хорошие распознаем с сопутствующими dust'ами
  if (ie1 != ie2 && cut->duflm)
    addij(LC,r,cut_list,vers_list,ncut,ie2,ie1,0);
  if (ib1 != ib2)
  {
    cut=cut_list+ib2;
    if (cut->duflm)
		addij(LC,r,cut_list,vers_list,ncut,ib1,ib2,0);
    if ((let=cut->versm.vers[0].let)==(BYTE)'ь' &&
			!is_russian_baltic_conflict(let) ||		// 17.07.2001 E.P.
			let==(BYTE)'Ь'
	   )
      fl_b=1;
  }

  if (ib2>=ie2)
  {
    addij(LC,r,cut_list,vers_list,ncut,ib1,ie1,0);
    goto test_right_dust;
  }

  do
  {
    if (!fl_b && iec != iemax)
    {
#ifndef MY_DEBUG
    if (det_trace)
#endif
      cg_show_rast(LC,r,"left to right",cut_list); //растр - на экран

      cc=l2r(&ib1,&ib2,&il);
      if (cc)  goto finish;

      if (il<iemax && iec>il &&
          (cut=cut_list+il)->dh==0 &&
          (x=cut->x-cut_list->x) < h2 && x > sym_width>>1 &&
          cut->versm.vers[0].prob>=190 &&
          !not_connect_sect(0,il,cut_list))
      {                 //оставим, как есть, и попробуем дальше
        i1=i2=il;
        cc=l2r(&i1,&i2,&i);
        if (cc)         //дальше - хорошо, вернемся к подозрительному участку
        {
  if (debug_on)
#ifndef MY_DEBUG
    if (det_trace)
#endif
    {
      sprintf(snap_text,"right side reached, return %d-%d",0,il);
      cg_show_rast(LC,r,snap_text,cut_list); //растр - на экран
    }
          ib1=i1;  ib2=i2;  i1=i2=il;  il=i;
          cc=r2l(&i,&i2,&i1,0);
          goto finish;
        }
      }
    }
    if (ibc != 0 && abs(cut_list[ie2].x-cut_list[ib2].x)>minw)
    {
    //слева не вышло, пробуем справа налево

  if (debug_on)
#ifndef MY_DEBUG
    if (det_trace)
#endif
    {
      sprintf(snap_text,"left: reliable=%d advance=%d; try right",ib2,il);
      cg_show_rast(LC,r,snap_text,cut_list); //растр - на экран
    }

      cc=r2l(&ir,&ie2,&ie1,2);
      if (cc)  goto finish;

      if (ir>0 && ibc<ir &&
          (cut=cut_list+ir)->dh==0 &&
          (x=cut_list[iemax].x-cut->x) < h2 && x > sym_width>>1 &&
          cut_list[iemax].versm.vers[0].prob>=190 &&
          !not_connect_sect(ir,iemax,cut_list))
      {                 //оставим, как есть, и попробуем дальше
        i1=i2=ie2;
        cc=r2l(&i,&i2,&i1,2);
        if (cc)         //дальше - хорошо, вернемся к подозрительному участку
        {
  if (debug_on)
#ifndef MY_DEBUG
    if (det_trace)
#endif
    {
      sprintf(snap_text,"left side reached, return %d-%d",ir,iemax);
      cg_show_rast(LC,r,snap_text,cut_list); //растр - на экран
    }
          ie1=i1;  ie2=i2;  i1=i2=ir;  ir=i;
          cc=l2r(&i1,&i2,&i);
          goto finish;
        }
      }
    }
    if (abs(cut_list[ie2].x-cut_list[ib2].x)<=minw)
    {
      j=on_path(ie2,iemax,cut_list);
      if (j==0)  break;
      if (ib2==0)  addij(LC,r,cut_list,vers_list,ncut,0,j,0);
      else         one_cut(cut_list[ib2].px,ib2,ie2,j);
      goto test_right_dust;
    }

    if (il==ie2 && (iec>il || iec==iemax) &&
        ir==ib2 && (ibc<ir || ibc==0)     &&
        cut_list[ie2].versm.vers[0].prob>MINlet)
      goto finish;
    if (il==ie2 && (iec>il || iec==iemax) || ir==ib2 && (ibc<ir || ibc==0))
      if (cut_list[ie2].versm.vers[0].prob>=190)  goto finish;
    if (il==ir)
    {
      version *vers0;
      CHAR *ilet;
      if (connect && cut_list[il].dh==0)
      {
        addij(LC,r,cut_list,vers_list,ncut,ib2,ie2,0);
        if (cut_list[ie2].versm.vers[0].prob>trs2)  goto finish;
      }
      if (ir != ie2 && cut_list[ie2].x-cut_list[ir].x > w2)
      {
        i1=i2=ir;
        if ( l2r(&i1,&i2,&i) || i>0 && on_path(i,iemax,cut_list) &&
             cut_list[i].versm.vers[0].prob>=190)
        {
          j=on_path(ir,iemax,cut_list);
          vers0=&cut_list[j].versm.vers[0];
          if ( (let=vers0->let)==(BYTE)'т' &&
			  !is_russian_baltic_conflict(let) && 	// 17.07.2001 E.P.
			  !is_russian_turkish_conflict(let) 	// 21.05.2002 E.P.
			  )
            ie1=j;
          else
            if ( (ilet=strchr(letters_right_to_bad,let))!=0 &&
			    !is_russian_baltic_conflict(let) // 17.07.2001 E.P.
			   )
              if (vers0->prob < prob_right_to_bad[(BYTE*)ilet-letters_right_to_bad])
                ie1=j;          //может быть частью буквы
          ie2=ir;
        }
      }
      if (il != ib2 && cut_list[il].x-cut_list[ib2].x > w2)
      {
        i2=il;  i1 = (ie2==il) ? ie1 : i2;
        if (r2l(&i,&i2,&i1,0) || on_path(i,ib2,cut_list) &&
            cut_list[i2].versm.vers[0].prob>=190)
        {
          cut=cut_list+il;
          vers0=&cut->versm.vers[0];
          if ((let=vers0->let)==(BYTE)'т' &&
			  !is_russian_baltic_conflict(let) && 	// 17.07.2001 E.P.
			  !is_russian_turkish_conflict(let) 	// 21.05.2002 E.P.
			  )
            ib1=cut->px;
          else
            if ( (ilet=strchr(letters_right_to_bad,let)) !=0 &&
			    !is_russian_baltic_conflict(let) // 17.07.2001 E.P.
			   )
              if (vers0->prob < prob_right_to_bad[(BYTE*)ilet-letters_right_to_bad])
                ie1=cut->px;          //может быть частью буквы
          ib2=il;
        }
      }
      if (ib2==ie2)  goto finish;
    }
//    if (il>ir)  break;
//    if (iec==iemax || ibc==0 || iec<ibc)  break;
    if (iec==iemax && ibc==0)  break;
    connect = 1-connect;
  }
  while (!connect);

  if (right_dust)
    if (glue_right_dust())
    {
      spec_pairs();  return;
    }

  if (debug_on)
#ifndef MY_DEBUG
    if (det_trace)
#endif
    {
      sprintf(snap_text,"right: reliable=%d advance=%d; ",ie2,ir);
      cg_show_rast(LC,r,snap_text,cut_list); //растр - на экран
    }

  //не сошлись, запускаем ДП между ними

  if (ie1-ib1 <= 2)
    if (!good_path(cut_list,ncut))  { ib1=ib2=0; ie2=ie1=ncut-1; }

DP:
  if (ie1-ib1 <= 2)
  {
    spec_pairs();  return;
  }

  il=ib1; ir=ie1;

  for (pass=1; pass<=4; pass++)
  {
    dp_bound(cut_list,vers_list,pass,&il,&ir);
    if (ir<=il)
    {
      if (ib1 != 0 || ie1 != ncut-1)  spec_pairs();   //неполное ДП
      return;
    }
#ifndef MY_DEBUG
    if (det_trace)
#endif
    {
      sprintf(snap_text,"try DP %d-%d pass=%d",il,ir,pass);
      cg_show_rast(LC,r,snap_text,cut_list); //растр - на экран
    }
    for ( i=il+1; i<=ir; i++ )
    {
      if ( !ben_cut(cut_list+i) )  //разрешен на данном проходе
      {
        snap_newpoint(i);
        for (j=i-1; j>=il; j--)
          if ( !ben_cut(cut_list+j) )
            if (addij(LC,r,cut_list,vers_list,ncut,j,i,0) & 8)  break                                                               ;
      }                                                     //8-широк                                                               ий
    }
  }  //pass

  if (ib1==0 && ie1==ncut-1)  return;   //полное ДП - больше нечего делать

  if (good_path(cut_list,ncut))  { spec_pairs();  return; }

//ДП по всему участку

  il=0;  ir=ncut-1;
  for (pass=1; pass<=4; pass++)
  {
    dp_bound(cut_list,vers_list,pass,&il,&ir);
    if (ir<=il)  return;
    if (il>=ib1 && ir<=ie1)  return; //на этом интервале уже считали
/*
    {
      struct cut_elm *cute=cut_list+ir;
      for (cut=cut_list+(il+1); cut<cute; cut++)
        if ((cut->var & 0x7F)==40)  break;
      if (cut==cute)  return;  //новых точек не будет
    }
*/
#ifndef MY_DEBUG
    if (det_trace)
#endif
    {
      sprintf(snap_text,"try DP %d-%d pass=%d",il,ir,pass);
      cg_show_rast(LC,r,snap_text,cut_list); //растр - на экран
    }
    for ( i=il+1; i<=ir; i++ )
    {
      if ( !ben_cut(cut_list+i) )  //разрешен на данном проходе
      {
        snap_newpoint(i);
        for (j=i-1; j>=il; j--)
          if ( !ben_cut(cut_list+j) )
            if (addij(LC,r,cut_list,vers_list,ncut,j,i,0) & 8)  break                                                               ;
      }                                                     //8-широк                                                               ий
    }
  }  //pass
  return;

test_right_dust:
  if (right_dust)  glue_right_dust();
  if (!good_path(cut_list,ncut))  { ib1=ib2=0; ie2=ie1=ncut-1; goto DP; }
  spec_pairs();
  return;

finish:
  if (right_dust)  glue_right_dust();
  spec_pairs();
  return;
}

/*---------------------------------------------------------------------
  l2r процесс последовательного отрезания букв слева на интервале
      (*b2,ie2) (или (*b1,ie2), если слева буква из списка);
      заканчивается, если весь интервал хорошо разрезался или очередная
      граница совпала с вершиной оптимального пути справа;
      вычисляет новые значения ib1, ib2, il;
---------------------------------------------------------------------*/
static CHAR l2r(INT *ib1, INT *ib2, INT *il)
{
  INT i,x0;
  BYTE let;
  struct cut_elm *cut;
  CHAR cc;
  INT ib2p;       //предыдущая ib2
  struct cut_elm *cute;//граница при движении по связным компонентам
  INT pmax;       //наилучший результат
  INT set;               //индекс множества точек разрезания
  INT ib0;
  INT wmin,wmax;  //допустимая ширина буквы: мин. и макс.

  ib2p=*ib1;  *il=iec=*ib2;  cute=cut_list+iec;
  while (iec < iemax)
  {
    if (connect)
    {
    //выбираем компоненту, добавляя узкие куски, лежащие недалеко
      x0=cut_list[*ib2].x;
      for (i=iec+1,cut=cute+1; i<=iemax; i++,cut++)
      {
        if (cut->dh != 0)  continue;
        if (cut->rv.v3 > minp)  break;   //широкий просвет
        if (cut->x-cute->x > sym_width &&
            cute->x-x0 > sym_width)  break;
        iec=i; cute=cut;                 //кандидат на конец
      }
      if (i>iemax)  { i--; cut--; }
      if (cut->x-cute->x <= sym_width || cute->x-x0 <= sym_width)
        { iec=i; cute=cut; }
    }
    else
      iec=iemax;

    //режем
    *il=*ib2;
    while (*il<iec)
    {
      version *vers0;
      CHAR *ilet;
      INT ib1p=*ib1;
      if (*ib2<ie2 && ie2<=iec && cut_list[ie2].x-cut_list[*ib2].x <= w2)
      {
        ib0=*ib2;
        if ((addij(LC,r,cut_list,vers_list,ncut,ib0,ie2,0) & (2+4+8)) == 0)
          if (cut_list[ie2].versm.vers[0].prob>trs2)
            { *ib1=*il=ie2;  goto accept; }
      }
      pmax=-1;
      wmin=w1;  wmax=w2;
      for (set=1; set<=3; set++)
      {
        ib0=*ib2;                                   //пробуем следующую
        cc=lcut_out(ib0,*ib2,iec,wmin,wmax,set,trs2,il,&pmax);
        if (cc)  { *ib1=*il; goto accept; }
      }
      wmin=minw; wmax=h2;
      for (set=1; set<=4; set++)
      {
        ib0=*ib2;
        cc=lcut_out(ib0,*ib2,iec,wmin,wmax,set,trs2,il,&pmax);
                                                    //пробуем следующую
        if (cc)  { *ib1=*il; goto accept; }
        if (*ib1 != *ib2)
        {             //слева буква может быть частью более широкой
          ib0=*ib1;
          cc=lcut_out(ib0,*ib2,iec,minw,h2,set,trs2,il,&pmax);
                                                    //расширяем предыдущую
          if (cc) { *ib1=*ib2; goto accept; }
        }
      }
      if (*ib2>ib2p)
      {
        ib0=*ib2;
        cc=ladjust_cut(ib2p,&ib0,il,iec,minw,&pmax);
        if (cc) { *ib1=*il; goto accept; }
      }
      return 0;

accept:
      *ib2=*il; ib2p=ib0; let=0;
      vers0=&cut_list[*il].versm.vers[0];  let=vers0->let;
      if (let=='-')
        *ib1=ib1p;
      else
        if ( (let==(BYTE)'т' || let==(BYTE)'ш' || let==(BYTE)'Ш' ||
              strchr(sticks_left_to_bad,let)
			 ) &&
			 !is_russian_baltic_conflict(let) &&// 17.07.2001 E.P.
			 !is_russian_turkish_conflict(let) 	// 21.05.2002 E.P.
		   )
          *ib1=ib0;
        else
          if ( (ilet=strchr(letters_left_to_bad,let))!=0 &&
			  !is_russian_baltic_conflict(let)	// 17.07.2001 E.P.
			  )
            if (vers0->prob < prob_left_to_bad[(BYTE*)ilet-letters_left_to_bad])
              *ib1=ib0;            //может быть частью буквы

      if (debug_on)
#ifndef MY_DEBUG
        if (det_trace)
#endif
        {
          sprintf(snap_text,"keep: (%d,%d) %c",ib0,*il,let);
          show_and_wait(snap_text);
        }
      if (*ib2>=ie2)
        if (*ib2==ie2 || on_path(*ib2,iemax,cut_list))
		  return 1;
      if ( (let==(BYTE)'ь' || let==(BYTE)'Ь') &&
		  !is_russian_baltic_conflict(let) 	// 17.07.2001 E.P.
		 )
      {
        fl_b=1; return 0;
      }
    }
  }
  return 1;
}

/*------------------------------------------------------------------
  lcut_out  отрезает одну букву от сегмента (ib,ie) слева (причем
            шире сегмента (ib,ii) ): первую  попавшуюся лучше trs2,
            а если все хуже - наилучшую; set определяет множество
            допустимых точек (всего ncut), если set<0 - поиск по
            всем точкам;
            возвращает 1, если лучше tol, и 0, если хуже;
            imax и pmax - найденная правая граница буквы и ее
            вероятность
------------------------------------------------------------------*/
static CHAR lcut_out(INT ib, INT ii, INT ie, INT wmin, INT wmax,
                     INT set, INT tol, INT *imax, INT *pmax)
{
  struct cut_elm  *cut; INT i;
  struct cut_elm *cute=cut_list+ie;
  INT xb=cut_list[ib].x;
  INT x0=xb+sym_width;      //начальное сечение
  INT xe=xb+wmax;           //правая граница
  struct cut_elm  *cutl,*cutr; INT il,ir;   //сечения слева и справа от x0
  BYTE p;
  INT sete=set;
  BYTE cc;
  seg_vers *cur_vers;

  xb += wmin;
  if (set<0)  { set=1; sete=4; }
  for ( ; set<=sete; set++)
  {
  //первый разрез на расстоянии около sym_width, далее смещаемся в обе стороны
    i=ii+1; cut=cut_list+i; il=ir=-128;
    while (cut<=cute && cut->x <= xe)
    {
      CHAR var=cut->var & 0x7F;
      if (cut->x > xb && in_set(var,set))
        if(cut->x <= x0)  { il=i;  cutl=cut; }
        else              { ir=i;  cutr=cut; break; }
      cut++; i++;
    }

    while (il>=0 || ir>=0)
    {
      if (il<0)    goto right;
      else
        if (ir<0)  goto left;
        else
          if (cutr->x-x0 < x0-cutl->x)  goto right;
left:
      cc=addij(LC,r,cut_list,vers_list,ncut,ib,il,0);
      if (cc & 4)                     //узкий
        il=-128;
      else
      {
        if ((cc & (2+4+8)) == 0)
          if (cur_vers=find_vers(ib,il,vers_list))
          {
            p=0;
            if (let_or_bad(&cur_vers->vers))
            {
              p=cur_vers->vers.vers[0].prob;
              if (p>*pmax) { *pmax=p; *imax=il; }
              if (p>tol)  return 1;
            }
            else
              if (cur_vers->vers.vers[0].let=='-')
              {
                *imax=il; return 1;
              }
          }
        il=dec(&cutl,il,ii,set,xb);
        if (il==ii) il=-128;
      }
      continue;
right:
      cc=addij(LC,r,cut_list,vers_list,ncut,ib,ir,0);
      if (cc & 8)                     //широкий
        ir=-128;
      else
      {
        if ((cc & (2+4+8)) == 0)
          if (cur_vers=find_vers(ib,ir,vers_list))
          {
            p=0;
            if (let_or_bad(&cur_vers->vers))
            {
              p=cur_vers->vers.vers[0].prob;
              if (p>*pmax) { *pmax=p; *imax=ir; }
              if (p>tol)  return 1;
            }
            else
              if (cur_vers->vers.vers[0].let=='-')
              {
                *imax=ir; return 1;
              }
          }
        ir=inc(&cutr,ir,ie,set,xe);
      }
    }
  }
  return 0;
}

/*---------------------------------------------------------------------
  r2l процесс последовательного отрезания букв справа на интервале
      (ib2,ie2) (или (ib1,ie2), если справа буква из списка);
      заканчивается, если весь интервал хорошо разрезался или очередная
      граница совпала с вершиной оптимального пути слева;
      вычисляет новые значения ie1, ie2, ir;
---------------------------------------------------------------------*/
static CHAR r2l(INT *ir, INT *ie2, INT *ie1, INT mode)
{
  INT i,x0;
  BYTE let;
  seg_vers *cur_vers;
  struct cut_elm *cut;
  CHAR cc;
  INT ie2p;   //предыдущая ie2
  struct cut_elm *cutb;  //граница при движении по связным компонентам
  INT pmax;       //наилучший результат
  INT set;        //индекс множества точек разрезания
  INT ie0;
  INT wmin,wmax;  //допустимая ширина буквы: мин. и макс.

  ie2p=*ie1;  *ir=ibc=*ie2;  cutb=cut_list+ibc;
  while (ibc > 0)
  {
    if (connect)
    {
    //выбираем компоненту, добавляя узкие куски, лежащие недалеко
      x0=cut_list[*ie2].x;
      for (i=ibc-1,cut=cutb-1; i>=0; i--,cut--)
      {
        if (cut->dh != 0)  continue;
        if (cut->rv.v3 > minp)  break;   //широкий просвет
        if (cutb->x-cut->x > sym_width &&
            x0-cutb->x > sym_width)  break;
        ibc=i; cutb=cut;                 //кандидат на конец
      }
      if (i<0)  { i++; cut++; }
      if (cutb->x-cut->x <= sym_width || x0-cutb->x <= sym_width)
        { ibc=i;  cutb=cut; }
    }
    else
      ibc=0;

    //режем
    *ir=*ie2;
    while (*ir > ibc)
    {
      version *vers0;
      CHAR *ilet;
      INT ie1p=*ie1;
      if (ib2<*ie2 && ibc<=ib2 && cut_list[*ie2].x-cut_list[ib2].x <= w2)
      {
        ie0=*ie2;
        if ((addij(LC,r,cut_list,vers_list,ncut,ib2,ie0,0) & (2+4+8)) == 0)
          if (cut_list[*ie2].versm.vers[0].prob>trs2)
            { *ie1=*ir=ib2;  goto accept; }
      }
      pmax=-1;
      wmin=w1;  wmax=w2;
      for (set=1; set<=3; set++)
      {
        ie0=*ie2;                                   //пробуем следующую
        cc=rcut_out(ibc,ie0,ie0,wmin,wmax,set,trs2,ir,&pmax,mode);
        if (cc)  { *ie1=*ir; goto accept; }
      }
      wmin=minw; wmax=h2;
      for (set=1; set<=4; set++)
      {
        ie0=*ie2;
        cc=rcut_out(ibc,ie0,ie0,wmin,wmax,set,trs2,ir,&pmax,mode);
                                                    //пробуем следующую
        if (cc)  { *ie1=*ir; goto accept; }
        if (*ie1 != *ie2)
        {                 //справа буква может быть частью более широкой
          ie0=*ie1;
          cc=rcut_out(ibc,*ie2,ie0,minw,h2,set,trs2,ir,&pmax,mode);
                                                  //расширяем предыдущую
          if (cc) { *ie1=*ie2; goto accept; }
        }
      }
      if (*ie2<ie2p)
      {
        ie0=*ie2;
        cc=radjust_cut(ibc,ir,&ie0,ie2p,minw,&pmax,mode);
        if (cc) { *ie1=*ir; goto accept; }
      }
      return 0;

accept:
      *ie2=*ir; ie2p=ie0; let=0;
      if (cur_vers=find_vers(*ir,ie0,vers_list))
      {
        vers0=&cur_vers->vers.vers[0];  let=vers0->let;
        if (let=='-')
          *ie1=ie1p;
        else
          if ( (let==(BYTE)'т' || strchr(sticks_right_to_bad,let) ) &&
			   !is_russian_baltic_conflict(let) &&	// 17.07.2001 E.P.
			   !is_russian_turkish_conflict(let) 	// 21.05.2002 E.P.
			 )
            *ie1=ie0;
          else
            if ( (ilet=strchr(letters_right_to_bad,let))!=0 &&
			    !is_russian_baltic_conflict(let) // 17.07.2001 E.P.
			   )
              if (vers0->prob < prob_right_to_bad[(BYTE*)ilet-letters_right_to_bad])
                *ie1=ie0;          //может быть частью буквы
      }

      if (debug_on)
#ifndef MY_DEBUG
        if (det_trace)
#endif
        {
          sprintf(snap_text,"keep: (%d,%d) %c",*ir,ie0,let);
          show_and_wait(snap_text);
        }
      if (ib2>=*ie2)
        if (ib2==*ie2 || on_path(*ie2,ib2,cut_list))  return 1;
    }
  }
  return 1;
}

/*------------------------------------------------------------------
  rcut_out  отрезает одну букву от сегмента (ib,ie) справа (причем
            шире сегмента (ii,ie) ): первую попавшуюся лучше trs2,
            а если все хуже - наилучшую;
            set определяет множество допустимых точек (всего ncut),
            если set<0 - поиск по всем точкам;
            возвращает 1, если лучше tol, и 0, если хуже;
            imax и pmax - найденная правая граница буквы и ее
            вероятность
------------------------------------------------------------------*/
static CHAR rcut_out(INT ib, INT ii, INT ie, INT wmin, INT wmax,
                     INT set, INT tol, INT *imax, INT *pmax, INT mode)
{
  struct cut_elm  *cut; INT i;
  struct cut_elm *cutb=cut_list+ib;
  INT xe=cut_list[ie].x;
  INT x0=xe-sym_width;      //начальное сечение
  INT xb=xe-wmax;           //левая граница
  struct cut_elm  *cutl,*cutr; INT il,ir;   //сечения слева и справа от x0
  BYTE p;
  INT sete=set;
  BYTE cc;
  seg_vers *cur_vers;

  xe -= wmin;
  if (set<0)  { set=1; sete=4; }
  for ( ; set<=sete; set++)
  {
  //первый разрез на расстоянии около sym_width, далее смещаемся в обе стороны
    i=ii-1; cut=cut_list+i; il=ir=-128;
    while (cut>=cutb && cut->x >= xb)
    {
      CHAR var=cut->var & 0x7F;
      if (cut->x < xe && in_set(var,set))
        if(cut->x >= x0)  { ir=i;  cutr=cut; }
        else              { il=i;  cutl=cut; break; }
      cut--; i--;
    }

    while (il>=0 || ir>=0)
    {
      if (il<0)    goto right;
      else
        if (ir<0)  goto left;
        else
          if (cutr->x-x0 < x0-cutl->x)  goto right;
left:
      cc=addij(LC,r,cut_list,vers_list,ncut,il,ie,(CHAR)mode);
      if (cc & 8)                     //широкий
        il=-128;
      else
      {
        if ((cc & (2+4+8)) == 0)
          if (cur_vers=find_vers(il,ie,vers_list))
          {
            p=0;
            if (let_or_bad(&cur_vers->vers))
            {
              p=cur_vers->vers.vers[0].prob;
              if (p>*pmax) { *pmax=p; *imax=il; }
              if (p>tol)  return 1;
            }
            else
              if (cur_vers->vers.vers[0].let=='-')
              {
                *imax=il; return 1;
              }
          }
        il=dec(&cutl,il,ib,set,xb);
      }
      continue;
right:
      cc=addij(LC,r,cut_list,vers_list,ncut,ir,ie,(CHAR)mode);
      if (cc & 4)                     //узкий
        ir=-128;
      else
      {
        if ((cc & (2+4+8)) == 0)
          if (cur_vers=find_vers(ir,ie,vers_list))
          {
            p=0;
            if (let_or_bad(&cur_vers->vers))
            {
              p=cur_vers->vers.vers[0].prob;
              if (p>*pmax) { *pmax=p; *imax=ir; }
              if (p>tol)  return 1;
            }
            else
              if (cur_vers->vers.vers[0].let=='-')
              {
                *imax=ir; return 1;
              }
          }
        ir=inc(&cutr,ir,ii,set,xe);
        if (ir==ii) ir=-128;
      }
    }
  }
  return 0;
}
/*-------------------------------------------------------------------
  inc возвращает номер допустимой на множестве set точки между i-ой
      и ie-ой и не правее x
-------------------------------------------------------------------*/
static INT inc(struct cut_elm **cutp, INT i, INT ie, INT set, INT x)
{
  if (i>=ie) return -128;
  (*cutp)++; i++;
  while ((*cutp)->x < x)
  {
    CHAR var=(*cutp)->var & 0x7F;
    if (i==ie || in_set(var,set))  return i;
    (*cutp)++; i++;
  }
  return -128;
}

/*-------------------------------------------------------------------
  dec возвращает номер допустимой на множестве set точки между ie-ой
      и i-ой  и не левее x
-------------------------------------------------------------------*/
static INT dec(struct cut_elm **cutp, INT i, INT ie, INT set, INT x)
{
  if (i<=ie) return -128;
  (*cutp)--; i--;
  while ((*cutp)->x > x)
  {
    CHAR var=(*cutp)->var & 0x7F;
    if (i==ie || in_set(var,set))  return i;
    (*cutp)--; i--;
  }
  return -128;
}

/*-------------------------------------------------------------------
  one_cut  находит разрез сегмента (i1,i2) в пределах (ib,ie), чтобы
           хотя бы один кусок был хороший;
           возвращает 1, если разрезался на две хорошие части, иначе - 0
-------------------------------------------------------------------*/
static CHAR one_cut(INT i1, INT ib, INT ie, INT i2)
{
  struct cut_elm  *cut; INT i;
  struct cut_elm *cutb=cut_list+ib;
  struct cut_elm *cute=cut_list+ie;
  INT xb=cutb->x;
  INT xe=cute->x;
  INT x0=(cutb->x+cute->x)>>1;  //начальное сечение
  struct cut_elm  *cutl,*cutr; INT il,ir;   //сечения слева и справа от x0
  CHAR fll,flr;
  SVERS *vers;
  INT set;

  if (debug_on)
#ifndef MY_DEBUG
    if (det_trace)
#endif
    {
      sprintf(snap_text,"one cut: %d-%d",ib,ie);
      cg_show_rast(LC,r,snap_text,cut_list); //растр - на экран
    }

  if (cut_list[i2].x-cut_list[i1].x<=sym_width)  return 0;
  xb=x0-(sym_width>>2); xe=x0+(sym_width>>2);
  for (set=1; set<=4; set++)
  {
  //первый разрез примерно на середине, далее смещаемся в обе стороны
    i=ib+1; cut=cutb+1; il=ir=-128;
    while (cut<=cute && cut->x < xe)
    {
      CHAR var=cut->var & 0x7F;
      if (cut->x > xb && in_set(var,set))
        if(cut->x <= x0)  { il=i;  cutl=cut; }
        else              { ir=i;  cutr=cut; break; }
      cut++; i++;
    }

    while (il>=0 || ir>=0)
    {
      if (il<0)    goto right;
      else
        if (ir<0)  goto left;
        else
          if (cutr->x-x0 < x0-cutl->x)  goto right;
left:
      i=il; cut=cutl; il=dec(&cutl,il,ib,set,xb);
      if (il==i1) il=-128;  goto rec;
right:
      i=ir; cut=cutr; ir=inc(&cutr,ir,ie,set,xe);
      if (ir==i2) ir=-128;
rec:
      fll=flr=0;
      addij(LC,r,cut_list,vers_list,ncut,i1,i,0);
      addij(LC,r,cut_list,vers_list,ncut,i,i2,0);
      if (let_or_bad(vers=&cut->versm))   fll=vers->vers[0].prob>trs2;
      if (let_or_bad(vers=&cute->versm))  flr=vers->vers[0].prob>trs2;
      if (fll && flr)  return 1;
      if (fll || flr)  return 0;
    }
    xb=cutb->x; xe=cute->x;
  }
  return 0;
}

/*-------------------------------------------------------------------
  ladjust_cut  уточняет положение разреза ib0 в пределах +-d пикселов;
               возвращает 1, если результат улучшен, иначе - 0;
               ib,ie - предельные границы слева и справа
               il и pmax - наилучшая правая граница и вероятность
-------------------------------------------------------------------*/
static CHAR ladjust_cut(INT ib, INT *ib0, INT *il, INT ie, INT d, INT *pmax)
{
  INT i0=*ib0;
  INT i=i0; struct cut_elm  *cut=cut_list+i; //текущая середина
  INT iln;                                   //текущий правый край
  INT xb=cut->x-d, xe=cut->x+d;              //границы поиска
  INT pl,pr;    //вероятности версий слева и справа от i
  BYTE good;    //наилучшая вероятность слева
  INT dir=((cut+1)->x-cut->x < cut->x-(cut-1)->x) ? 1 : -1;
                                              // направление смещения
  INT set;
  SVERS *vers;
  INT idir;
  INT wmin,wmax;  //допустимая ширина буквы: мин. и макс.
  CHAR cc;

  if (i==ib || i==ie || cut->dh==0)  return 0;

  if (debug_on)
#ifndef MY_DEBUG
    if (det_trace)
#endif
    {
      sprintf(snap_text,"adjust cut: %d-%d-%d",ib,i,*il);
      cg_show_rast(LC,r,snap_text,cut_list); //растр - на экран
    }

  good=(let_or_bad(vers=&cut_list[i].versm)) ? vers->vers[0].prob : 0;

  //смещаемся в одну сторону, пока pmin улучшается
  for (idir=0; idir<2; idir++)
  {
    i=i0+dir;  cut=cut_list+i;
    while ( (dir>0) ? cut->x <= xe && i<=ie : cut->x >= xb && i>ib )
    {
      addij(LC,r,cut_list,vers_list,ncut,ib,i,0);
      pl=(let_or_bad(vers=&cut->versm)) ? vers->vers[0].prob : 0;
      if (pl<=trs2)  break;   //хорошая испортилась
      if (i==ie)  { *ib0=*il=i; *pmax=pl; return 1; }
      wmin=w1;  wmax=w2;  pr=-1;  iln=i;
      for (set=1; set<=3; set++)
      {
        cc=lcut_out(i,i,ie,wmin,wmax,set,trs2,&iln,&pr);
                                             //от новой границы вправо
        if (cc) { *ib0=i; *il=iln; *pmax=pr; return 1; }
      }
      wmin=minw; wmax=h2;
      for (set=1; set<=4; set++)
      {
        cc=lcut_out(i,i,ie,wmin,wmax,set,trs2,&iln,&pr);
                                             //от новой границы вправо
        if (cc) { *ib0=i; *il=iln; *pmax=pr; return 1; }
      }
      if (pl<=good) break;    //лучше не стало
      good=(BYTE)pl; *ib0=i; *il=iln; *pmax=pr;
      cut += dir;  i += dir;
    }
    dir= -dir;
  }
  return 0;
}

/*-------------------------------------------------------------------
  radjust_cut  уточняет положение разреза ie0 в пределах +-d пикселов;
               возвращает 1, если результат улучшен, иначе - 0;
               ib,ie - предельные границы слева и справа
               ir и pmax - наилучшая левая граница и вероятность
-------------------------------------------------------------------*/
static CHAR radjust_cut(INT ib, INT *ir, INT *ie0, INT ie, INT d,
                        INT *pmax, INT mode)
{
  INT i0=*ie0;
  INT i=i0; struct cut_elm  *cut=cut_list+i; //текущая середина
  INT irn;                                   //текущий левый край
  INT xb=cut->x-d, xe=cut->x+d;              //границы поиска
  INT pl,pr;   //вероятности версий слева и справа от i
  BYTE good;   //наилучшая вероятность справа
  INT dir=((cut+1)->x-cut->x < cut->x-(cut-1)->x) ? 1 : -1;
                                              // направление смещения
  INT set;
  seg_vers *cur_vers;
  SVERS *vers;
  INT idir;
  INT wmin,wmax;  //допустимая ширина буквы: мин. и макс.
  CHAR cc;

  if (i==ib || i==ie || cut->dh==0)  return 0;

  if (debug_on)
#ifndef MY_DEBUG
    if (det_trace)
#endif
    {
      sprintf(snap_text,"adjust cut: %d-%d-%d",*ir,i,ie);
      cg_show_rast(LC,r,snap_text,cut_list); //растр - на экран
    }

  good=0;
  if (cur_vers=find_vers(i,ie,vers_list))
    if(let_or_bad(vers=&cur_vers->vers))  good=vers->vers[0].prob;

  //смещаемся в одну сторону, пока pmin улучшается
  for (idir=0; idir<2; idir++)
  {
    i=i0+dir;  cut=cut_list+i;
    while ( (dir>0) ? cut->x <= xe && i<ie : cut->x >= xb && i>=ib )
    {
      addij(LC,r,cut_list,vers_list,ncut,i,ie,(CHAR)mode);
      pr=0;
      if (cur_vers=find_vers(i,ie,vers_list))
        if (let_or_bad(vers=&cur_vers->vers)) pr=vers->vers[0].prob;
      if (pr<=trs2)  break;   //хорошая испортилась
      if (i==ib)  { *ie0=*ir=i; *pmax=pr; return 1; }
      wmin=w1;  wmax=w2;  pl=-1;  irn=i;
      for (set=1; set<=3; set++)
      {
        cc=rcut_out(ib,i,i,wmin,wmax,set,trs2,&irn,&pl,mode);
                              //от новой границы влево
        if (cc) { *ie0=i; *ir=irn; *pmax=pl; return 1; }
      }
      wmin=minw; wmax=h2;
      for (set=1; set<=4; set++)
      {
        cc=rcut_out(ib,i,i,wmin,wmax,set,trs2,&irn,&pl,mode);
                              //от новой границы влево
        if (cc) { *ie0=i; *ir=irn; *pmax=pl; return 1; }
      }
      if (pr<=good) break;    //лучше не стало
      good=(BYTE)pr; *ie0=i; *ir=irn; *pmax=pl;
      cut += dir;  i += dir;
    }
    dir= -dir;
  }
  return 0;
}

/*---------------------------------------------------------------
  spec_pairs "ст", "сл" и др.
----------------------------------------------------------------*/
static void spec_pairs()
{
  struct cut_elm *cut;
  INT i,ip; //текущее и предыдущее сечения
  SVERS *vers;                   //текущая версия
  BYTE let,letp;                 //текущая и предыдущая буквы
  struct cut_elm *cuts;
  INT is,x0;  //правая граница "с"
  INT m_row;                     //средняя базовая линия

  det_snap(LC, "special pairs");

  i=ncut-1;
  letp=0;
  ip=i;
  while (i>0)
    {
     cut=cut_list+i;
	 vers=&cut->versm;
	 let=vers->vers[0].let;
     if (dust(vers))
       {
        cuts=cut_list+cut->px;  //левый край dust'а
        if (cuts->dh != 0 &&      //dust отрезан слева
            addij(LC,r,cut_list,vers_list,ncut,cuts->px,i,0)==0 &&
            cut->versm.vers[0].prob > cuts->versm.vers[0].prob
           ) ;                     //приклеили
          else
            if (cut->dh != 0)       //dust отрезан справа
              addij(LC,r,cut_list,vers_list,ncut,i,ip,0);
       }
      else
        if ( cut->dh != 0 && strchr("кКтТуУ",let) &&
		    !is_russian_baltic_conflict(let) && // 17.07.2001 E.P.
			!is_russian_turkish_conflict(let)&&	// 21.05.2002 E.P.
		     (letp==(BYTE)'г' || letp==(BYTE)'Г')
		   )
          {
           INT  j,il=cut->px;
           INT x0=cut->x-((cut_list[ip].x-cut->x)>>1);
           BYTE *letr=&cut_list[ip].versm.vers[0].let;
           for (j=i-1,cuts=cut-1; j>il && cuts->x >= x0; j--,cuts--)
             {
              if (addij(LC,r,cut_list,vers_list,ncut,il,j,0)>1)
				 break;
//              if (cut_list[j].versm.vers[0].prob<trs2)
//			       break;
              if (addij(LC,r,cut_list,vers_list,ncut,j,ip,0)>1)
				 continue;
              if ( (*letr==(BYTE)'т' || *letr==(BYTE)'Т')  &&
					!is_russian_baltic_conflict(*letr) && // 17.07.2001 E.P.
					!is_russian_turkish_conflict(*letr)	// 21.05.2002 E.P.
				 )
				 break;
             }
          }
         else
            {
             if (cut->dh != 0 &&
				 strchr("еиИнНпПоОсС",let) &&			// "хш╚э═я╧ю╬ё╤"
			     !is_russian_baltic_conflict(let) &&	// 17.07.2001 E.P.
				 !is_russian_turkish_conflict(let) &&	// 21.05.2002 E.P.
				 (letp==(BYTE)'г' || letp==(BYTE)'Г'))	// у├
               {
                m_row=my_bases.bm+(INT)((LONG)nIncline*(r->left+cut->x)/2048);
                x0=right_bound(r,0,cut->x,(INT)(m_row-r->top+1),(INT)(r->h-1));
               }
              else
                if (cut->dh != 0 && strchr("есС",let) &&
					!is_russian_baltic_conflict(let) && // 17.07.2001 E.P.
				    (letp==(BYTE)'п' || letp==(BYTE)'П'))
                  {
                   m_row=my_bases.bm+(INT)((LONG)nIncline*(r->left+cut->x)/2048);
                   x0=right_bound(r,0,cut->x,0,(INT)(m_row-r->top));
                  }
                 else
                   if (cut->dh != 0 && (letp==(BYTE)'ь' || letp==(BYTE)'Ь') &&
					   !is_russian_baltic_conflict(letp) // 17.07.2001 E.P.
					  )
                     {
                      x0=cut->x-((cut_list[ip].x-cut->x)>>2);
                     }
                    else
                      if ((let==(BYTE)'ы' || let==(BYTE)'Ы') &&
					      strchr(letters_right_to_bad,letp) &&
			              letp != (BYTE)'к' && letp != (BYTE)'К' &&
						  !is_russian_baltic_conflict(let)	&&	// 17.07.2001 E.P.
						  !is_russian_baltic_conflict(letp)		// 17.07.2001 E.P.
						 )
                        x0=(cut_list[cut->px].x + (cut->x<<1))/3;
                       else
                         if (let=='|' && strchr(letters_right_to_bad,letp) &&
							 !is_russian_baltic_conflict(letp) && // 17.07.2001 E.P.
                             letp != (BYTE)'к' && letp != (BYTE)'К')
                           {
                            i=cut->px;
                            if (i==0)
							  break;
                            cut=cut_list+i;
							x0=cut->x;
                           }
                          else
                            x0=0;
             if (x0 != 0)
               {
                //ищем ближайший к x0 разрез
                is=i;
		        cuts=cut;
                while (cuts->x > x0)
		          {
		           is--;
		           cuts--;
		          };
                if ( (cuts+1)->x-x0 < x0-cuts->x )
			      is++;
                //распознаем
                addij(LC,r,cut_list,vers_list,ncut,cut->px,is,0);
                addij(LC,r,cut_list,vers_list,ncut,is,ip,0);
               }
            }
     letp=let; ip=i;
     i=cut->px;
    }
}

/*--------------------------------------------------------------
  right_bound  находит координату x правой границы изображения
               в области (x1,y1) (x2,y2) растра r; левый верхний
               угол растра - (0,0)
--------------------------------------------------------------*/
static INT right_bound(raster *r, INT x1, INT x2, INT y1, INT y2)
{
  INT i;
  INT wb=(r->w+7)/8;    //ширина растра в байтах;
  INT h1=y2-y1+1;       //высота области
  BYTE *b0=r->pict + wb*y1;  //начало горизонтали
  BYTE *b1=b0+x1/8;          //верхние левый
  BYTE *b2=b0+x2/8;          // и правый байты области
  CHAR p;               //проекция столбика
  BYTE *bx=b2;          //верхний байт столбика
  BYTE *ib;             //текущий  -"-
  BYTE m1=0xff>>(x1%8); //интересующие биты в проекции
  BYTE m2=0xff<<(7-x2%8);

  do
  {
    for (p=0,i=0,ib=bx; i<h1; i++,ib += wb)  p |= *ib;  //проекция
    if (bx==b2) p &= m2;
    if (bx==b1) p &= m1;
    i=0;
    while (p<0)  { p = p<<1;  i++; }        //число единиц слева
    if (i>0)  return ((bx-b0)<<3)+i-1;
    bx--;                                   //следующий байт слева
  }
  while (bx>=b1);
  return 0;
}

/*--------------------------------------------------------------
  glue_right_dust присоединяет правые dust'ы, если нужно
--------------------------------------------------------------*/
static CHAR glue_right_dust()
{
  SVERS *vers;
  if (let_or_bad(vers=&cut_list[iemax].versm))
    if (vers->vers[0].prob>trs2)  return 1;
  ie1=ie2=ncut-1;
  addij(LC,r,cut_list,vers_list,ncut,ib2,ie1,0);
  if (let_or_bad(vers=&cut_list[ie1].versm))
    if (vers->vers[0].prob>trs2)  return 1;
  addij(LC,r,cut_list,vers_list,ncut,il,ie1,0);
  if (let_or_bad(vers=&cut_list[ie1].versm))
    if (vers->vers[0].prob>trs2)  return 1;
  return 0;
}

/*-------------------------------------------------------------
  good_path возвращает 1, если путь в списке ncut точек не содержит
            отрезанных dust'ов в середине и все вероятности >190
-------------------------------------------------------------*/
static good_path(struct cut_elm *cut_list, INT ncut)
{
  INT ncut1=ncut-1;
  INT i=ncut1;  struct cut_elm *cut=cut_list+i;
  INT in;  struct cut_elm *cutn;
  SVERS *vers;

  while (i>0)
  {
    in=cut->px;  cutn=cut_list+in; vers=&cut->versm;
    if (dust(vers))
    {
      if (i<ncut1 && in>0 && (cut->dh != 0 || cutn->dh != 0))  return 0;
    }
    else
      if (vers->vers[0].prob<190)  return 0;
    i=in; cut=cutn;
  }
  return 1;
}
