/*
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>

#include "tuner.h"
#include "nt_types.h"
//#include "struct.h"
//#include "func.h"
#include "cstr.h"

#include "linear.h"
//#include "lang.h"
#include "linutil.h"
#include "ligas.h"      // Pit 10-10-94 03:56pm
#include "minmax.h"

/*============= Func prototypes ==================*/

BYTE to_lower(BYTE c);
BYTE to_upper(BYTE c);
INT  is_lower(BYTE ch);
INT  is_upper(BYTE ch);
INT  isletter(BYTE ch);
BYTE get_homot(BYTE ch);
INT  twin(BYTE ch);
INT  count_line_hi(void);
INT  draft_cut_hyps(INT bs,INT fl);

#define MAX_HEIGHT 70  // such was...  change ?

extern Word8 language;

extern BYTE db_status    ;    // snap presence byte
extern BYTE db_trace_flag;    // snap-detail presence byte
extern BYTE db_pass      ;    // snap-pass indicator
extern BYTE fEdCode      ;    // Change code letter in module LINUTIL.C

/*============= Local function portotypes ==============*/

/*static*/ INT  h_hist(void);

/*============= Source code ============*/
BOOL is_liga_ff(BYTE c)
{
// Код лиги определен через макру в ligas.h 05.09.2000 E.P.
  return (c==liga_ff);
}
BOOL is_liga_ffl(BYTE c)
{
// Код лиги определен через макру в ligas.h 05.09.2000 E.P.
  return (c==liga_ffl);
}

/*   not used
INT is_russian(BYTE ch)
{
if( language==LANG_RUSSIAN || language==LANG_ENGLISH && multy_language )
switch(fEdCode){
  case ED_ASCII: // for ASCII
   if((ch >=(BYTE)' ' && ch <=(BYTE)'Ї') ||
      (ch >=(BYTE)'а' && ch <=(BYTE)'п') ||
      (ch >=(BYTE)'Ђ' && ch <=(BYTE)'џ') ||
       memchr("рсхчшэИА",ch,8)
     ) return 1;
  break;
  case ED_WIN: // for Windows (ANSI)
      if( (ch >=0xE0 && ch <=0xFF)||(ch >=(BYTE)'Ђ' && ch <=(BYTE)'џ'))
        return 1;
  break;
  case ED_MAC: // for Macintosh
      if(
       (ch >=0xE0 && ch <=0xFE) || ch == 0xDF || (ch >= 0xC0 && ch <= 0xDF)
        ) return 1;
  break;
        }
return 0;
}
*/

INT is_english(BYTE ch)
{
return (ch >= 'a' && ch <= 'z')||(ch >= 'A' && ch <= 'Z')||
       (
//	   ch>=ligas_beg && ch<=ligas_end &&
	   is_liga(ch) &&  // 14.09.2000 E.P.

	   ch!=liga_exm && ch!=liga_qm )
	   ;
}

INT is_serbian_special(BYTE ch)
{
return (ch == SERB_j  ||   ch == SERB_J  ||
        ch == SERB_n  ||   ch == SERB_N  ||
        ch == SERB_l  ||   ch == SERB_L  ||
        ch == SERB_h  ||   ch == SERB_H  ||
        ch == SERB_hh ||   ch == SERB_HH ||
        ch == SERB_u  ||   ch == SERB_U  );

}

INT is_polish_special(BYTE ch)
{
return (ch == POLISH_SS  ||   ch == POLISH_s  ||
        ch == POLISH_ZZD ||   ch == POLISH_zd ||
        ch == POLISH_ZZR ||   ch == POLISH_zr ||
        ch == POLISH_LL  ||   ch == POLISH_l  ||
        ch == POLISH_AA  ||   ch == POLISH_a  ||
        ch == POLISH_CC  ||   ch == POLISH_a  ||
        ch == POLISH_NN  ||   ch == POLISH_n  ||
        ch == POLISH_EE  ||   ch == POLISH_e  ||
        ch == POLISH_OO  ||   ch == POLISH_o
    );
}

INT is_czech_special(BYTE let)
{
return (
     let == AA_right_accent || let == a_right_accent	||
     let == CC_inv_roof		|| let == c_inv_roof		||
     let == DD_inv_roof		|| let == d_inv_roof		||
     let == EE_right_accent || let == e_right_accent	||
     let == EE_inv_roof		|| let == e_inv_roof		||
     let == II_right_accent || let == i_right_accent	||
     let == NN_inv_roof		|| let == n_inv_roof		||
     let == OO_right_accent || let == o_right_accent	||
     let == RR_inv_roof		|| let == r_inv_roof		||
     let == SS_inv_roof		|| let == s_inv_roof		||
     let == TT_inv_roof		|| let == t_inv_roof		||
     let == UU_right_accent || let == u_right_accent	||
     let == UU_circle_accent|| let == u_circle_accent	||
     let == YY_right_accent || let == y_right_accent	||
     let == ZZ_inv_roof		|| let == z_inv_roof
    );
}

INT is_roman_special(BYTE let)
{
return (
     let == AA_semicircle			|| 	let == a_semicircle			||
     let == AA_roof_accent			|| 	let == a_roof_accent		||
     let == II_roof_accent			|| 	let == i_roof_accent		||
     let == SS_bottom_accent_latin	|| 	let == s_bottom_accent_latin||
     let == TT_bottom_accent		|| 	let == t_bottom_accent
    );
}

INT is_hungar_special(BYTE let)
{
return (
     let == AA_right_accent || 		let == a_right_accent ||
     let == EE_right_accent || 		let == e_right_accent ||
     let == II_right_accent || 		let == i_right_accent ||
     let == OO_right_accent || 		let == o_right_accent ||
     let == OO_2dot_accent  || 		let == o_2dot_accent  ||
     let == OO_double_right || 		let == o_double_right
    );
}

INT is_lower(BYTE ch)
{

if(language==LANG_RUSSIAN)
        switch(fEdCode){
  case ED_ASCII: // for ASCII
   if((ch >=(BYTE)' ' && ch <=(BYTE)'Ї') ||
      (ch >=(BYTE)'а' && ch <=(BYTE)'п') ||
       memchr("рсхчшэА",ch,7)
     ) return 1;
  break;
  case ED_WIN: // for Windows (ANSI)
      if (ch >=0xE0 && ch <=0xFF)
        return 1;
  break;
  case ED_MAC: // for Macintosh
      if(((ch >=0xE0 && ch <=0xFE) || ch == 0xDF)
        ) return 1;
  break;
        }
 if(ch >= 'a' && ch <= 'z') return 1;
 return 0;
}
INT is_upper(BYTE ch)
{
if(language==LANG_RUSSIAN)
    switch(fEdCode){
    case ED_ASCII:
    case ED_MAC: // for ASCII and Macintosh
      if(ch >=(BYTE)'Ђ' && ch <=(BYTE)'џ'||
         ch==(BYTE)r_EE_2dot
        )  return 1;
    break;
    case ED_WIN: // for Windows (ANSI)
      if(ch >= 0xC0 && ch <= 0xDF)
        return 1;
    break;
    }
if(ch >= 'A' && ch <= 'Z') return 1;
 return 0;
}

BOOL is_digit(BYTE ch)
{
if(ch >= (BYTE)'0' && ch <= (BYTE)'9') return TRUE;
else return FALSE;
}

INT isletter(BYTE ch)
{
if(is_lower(ch) || is_upper(ch)) return 1;
else return 0;
}

BYTE get_homot(BYTE ch )
{
 if(ch == '0') return ((BYTE)'®');
 if(is_upper(ch)) return to_lower(ch);
 if(is_lower(ch)) return to_upper(ch);
 return ch;
}

static const BYTE non_twin[]=" ЂЎЃҐ…";
static const BYTE lat_twins[]="cCoOpPsSvVwWxXzZ";

INT twin(BYTE ch)
{
if(!isletter(ch)) return 0;
if( language==LANG_RUSSIAN )
 if(memchr(non_twin,ch,sizeof non_twin)) return 0;
 else                                    return 1;
if( language!=LANG_RUSSIAN && memchr(lat_twins,ch,sizeof lat_twins)) return 1;
return 0;
}

/* Function returns UPPER CASE variant of the letter.             */
BYTE to_upper( BYTE c )
{
  if ( c >= (BYTE)'a' && c <= (BYTE)'z') return c - (BYTE)'a' + (BYTE)'A';
  if(language==LANG_RUSSIAN)
       switch(fEdCode){
  case ED_ASCII:  // for ASCII
    if ( c >= (BYTE)' ' && c <= (BYTE)'Ї') return c - (BYTE)' ' + (BYTE)'Ђ';
    if ( c >= (BYTE)'а' && c <= (BYTE)'п') return c - (BYTE)'а' + (BYTE)'ђ';
  break;
  case ED_WIN: // for Windows (ANSI)
    if ( c >= 0xE0 && c <= 0xFF ) return c - 0xE0 + 0xC0;
  break;
  case ED_MAC: // for Macintosh
    if ((c >= 0xE0 && c <= 0xFE) ) return c - 0xE0 + 0x80;
    if ( c == 0xDF )               return 0x9F;
  break;
  }
    return c;
}

 /* Function returns LOWER CASE variant of the letter.             */
BYTE to_lower(BYTE c)
{
  if ( c >= (BYTE)'A' && c <= (BYTE)'Z') return c - (BYTE)'A'+ (BYTE)'a' ;
  if(language==LANG_RUSSIAN)
        switch(fEdCode){
  case ED_ASCII: // for ASCII
    if ( c >= (BYTE)'Ђ' && c <= (BYTE)'Џ') return c - (BYTE)'Ђ'+ (BYTE)' ' ;
    if ( c >= (BYTE)'ђ' && c <= (BYTE)'џ') return c - (BYTE)'ђ'+ (BYTE)'а' ;
  break;
  case ED_WIN: // for Windows
    if ( c >= 0xC0 && c <= 0xDF) return c - 0xC0 + 0xE0 ;
  break;
  case ED_MAC: // for Macintosh
    if ( c >= 0x80 && c <= 0x9E) return c - 0x80 + 0xE0 ;
    if ( c == 0x9F )             return 0xDF;
  break;
        }
    return c;
}

static struct {
INT     ns1,ns2;
BYTE    lcase,caps;
} lht[128];

#define v_bs1	1
#define v_bs2	2
#define v_bs3	4

// Nick 26.01.2001
// нужна инициализация для всех строк, т.к. в count_line_hi()
// не для всех строк можем попадать! И тогда статистика в этих строках
// останется от прежнего файла (или нечто вообще неизвестное)
void BaseLineStatisticInit(void)
{
	int i;

	for(i=0; i < arrnum(lht);i++)
		memset(&lht[i],0,sizeof(lht[0]));  //init
}
/////////////////
INT count_line_hi(void)
{
 INT d23[MAX_HEIGHT]={0},
    d13[MAX_HEIGHT]={0},
    i,max,index,
    nbcaps=0,nbsmall=0;
 CSTR_rast c;
 CSTR_rast_attr attr;
 BYTE ch;
 UniVersions vers;

	if(line_number >= arrnum(lht))
		return 0;
	memset(&lht[line_number],0,sizeof(lht[0]));  //init
	/* check validate of base 2 and base 3                       */
	if(!(ncletrs > 6 && ncletrs*2 > ncbs)) // Presentable line ?
   	                              return 0;// NO

	c = cell_f();
	//while((c=c->nextl)->nextl != NULL)
	while((c=CSTR_GetNextRaster(c,CSTR_f_let)) != NULL)
	{
    CSTR_GetAttr(c,&attr);

	if(attr.h <0 || attr.h >= MAX_HEIGHT )
		continue;

    CSTR_GetCollectionUni(c,&vers);

	if(vers.lnAltCnt<=0 || vers.Alt[0].Prob < 140)
		continue;

	ch = let_lindef[vers.Alt[0].Liga];
    if((ch & (v_bs1 | v_bs3)) == (v_bs1 | v_bs3))
	{
		d13[attr.h]++;
		nbcaps++;
	}
    if((ch & (v_bs2 | v_bs3)) == (v_bs2 | v_bs3))
	{
		d23[attr.h]++;
		nbsmall++;
	}
   }

	// test - what we got
 for(i=0,max=0,index=0;i < sizeof(d13)/sizeof(d13[0]);i++)
 {
    if(max < d13[i])
	{
		max = d13[i]; index = i;
	}
 }

 if( max > 0 )
	 lht[line_number].caps = (BYTE)index;

 for(i=0,max=0,index=0;i < sizeof(d23)/sizeof(d23[0]);i++)
 {
    if(max < d23[i])
	{
		max = d23[i]; index = i;
	}
 }

 if( max > 0 ) lht[line_number].lcase = (BYTE)index;

 lht[line_number].ns1 = nbcaps;
 lht[line_number].ns2 = nbsmall;

 return (lht[line_number].caps  != 0 ||
        lht[line_number].lcase != 0 ); /* if there some information */
}

/* Return  most usable height in line */
INT h_hist(void)
{
 CSTR_rast c;
 CSTR_rast_attr attr;
 INT h[MAX_HEIGHT]={0},th[MAX_HEIGHT]={0},
    i,max,index,
    recognized=0,total=0;
 BYTE ch;
 UniVersions vers;

 c = cell_f();
// while((c=c->nextl)->nextl != NULL)
 while((c=CSTR_GetNextRaster(c,f_letter)) != NULL)
 {
    CSTR_GetAttr(c,&attr);
	if(attr.h <0 || attr.h >= MAX_HEIGHT )
		continue;

    total++;
    th[attr.h]++; // all over

    CSTR_GetCollectionUni(c,&vers);

//	if(!(c->flg & CSTR_f_let))continue;
	if( !(attr.flg & CSTR_f_let ) ||  vers.lnAltCnt<=0 )
		continue;

	ch = let_lindef[vers.Alt[0].Liga];
    if((ch & v_bs3) && ((ch & v_bs1) || (ch & v_bs2)))
    {
	   recognized++;
	   h[attr.h]++;
	}
 }

  if(recognized > 3 || recognized*2 > total)
  {
   for(i=0,max=0,index=0;i < sizeof(h)/sizeof(h[0]);i++)
   {
    if(max < h[i])
	{
		max = h[i]; index = i;
	}
   }

   max*=2;
   max+=h[index-1]*2;
   max+=h[index+1]*2;
   max+=h[index+2];
   max+=h[index-2];
   max/=2;

   if(recognized > 4 )
   {  // letters have the same height
    if( max*4 >= recognized*3 ) return index;
   }
   else return index;
  }

  else if(recognized < 4 && total > 4 )
  {
   for(i=0,max=0,index=0;i < sizeof(th)/sizeof(th[0]);i++)
   {
    if(max < th[i])
	{
		max = th[i]; index = i;
	}
   }

   max*=2;
   max+=th[index-1]*2;
   max+=th[index+1]*2;
   max+=th[index+2];
   max+=th[index-2];
   max/=2;
   if(max*2 > total ) return index; // letters have the same height
  }

  for(i=0,max=0,index=0;i < sizeof(th)/sizeof(th[0]);i++)
  {
    if(max < th[i])
	{
		max = th[i]; index = i;
	}
  }

  if(max+th[index+1]+th[index-1] == total )
	  return index;

  return 0;
}
/*---------------------------------------
 * Return:	0 - can't set case	*
 *		1 - base line 1		*
 *		2 - base line 2		*
 *--------------------------------------*/
BYTE page_stat,stable_b3;

INT setup_let_case(INT ckH)
{
INT i,umax,lmax,lh,uprob,lprob,ret=0;
INT uc[MAX_HEIGHT]={0},
    lc[MAX_HEIGHT]={0};

 // Nick 26.01.2000  - в таблицах могут быть очень большие номера строк!
 int lastLineNumber = MIN(sizeof(lht)/sizeof(lht[0]), line_number );
 int firLineNumber = 1;  // у нас нет линии 0

      page_stat=0;
 //if(line_number < 2) return 0;
 if(ckH) lh=ckH;
 else
 if((lh = h_hist()) == 0) return 0;

 for(i=firLineNumber;i < line_number && i < sizeof(lht)/sizeof(lht[0]);i++)
 {
  uc[MIN(lht[i].caps,MAX_HEIGHT-1)]++; /* Calc histogram */
  lc[MIN(lht[i].lcase,MAX_HEIGHT-1)]++; // Nick 25.05.2001
 }

 umax  = uc[lh]+uc[lh+1]+uc[lh-1]+uc[lh+2]+uc[lh-2];
 lmax  = lc[lh]+lc[lh+1]+lc[lh-1];

 for(i=firLineNumber, lprob=0;i < lastLineNumber;i++)  // Nick 26.01.2001 - was line_number
   if( abs(lht[i].lcase-lh) < 3 )lprob += lht[i].ns2;
 for(i=firLineNumber, uprob=0;i < lastLineNumber;i++)  // Nick 26.01.2001 - was line_number
 {
   if( abs(lht[i].caps-lh) < 3 )
	   uprob += lht[i].ns1;
 }

 if(lmax >= umax && lmax > 0)
 {
	 ret=2; if(!ckH) page_stat=1;
 }
 if(umax >  lmax && umax > 0)
 {
	 ret=1; if(!ckH) page_stat=1;
 }
 if(db_status & snap_activity_rbal(db_pass) && ret!=0)
 {
  CHAR buf[120];
  sprintf(buf,"Page statistic lh=%u  caps=%u,%u lcase=%u,%u ",lh,umax,uprob,lmax,lprob);
  snap_show_text_rbal(buf); snap_monitor_rbal();
 }
return ret;
}


/********************************************************
 *                                                      *
 *      Func walk by string tries to discrim  some      *
 *      versions corresponding to base line bs          *
 *      Only for russian language.                      *
 ********************************************************/

INT draft_cut_hyps(INT bs,INT flag)
{
CSTR_rast c;
CSTR_rast_attr attr;
//version * v;
UniVersions vers;
int i;
BYTE ldef,let,gtwin=0;
INT dist,cutting_made=0,diff;

 c = cell_f();

 if( flag ) // add homotetia  versions
 {
//  while ((c=c->nextl)->nextl)
  while ((c=CSTR_GetNextRaster(c,CSTR_f_let)) )
  {
   CSTR_GetCollectionUni(c,&vers);
   let = vers.Alt[0].Liga;
//   if(!(c->flg & CSTR_f_let) || !isletter(let))
   if( vers.lnAltCnt<=0 || !isletter(let) )
	   continue;

   if(!twin(let))
	   continue;
   let = is_lower(let) ? to_upper(let) : to_lower(let);
   promote(0,c,let,0); // insvers

//   c->vers[c->nvers].let=c->vers[c->nvers].prob=0;
  }
 }

 c = cell_f();
 if(bs == 3) // cut hyps by bb3
 {
  while ((c=CSTR_GetNextRaster(c,CSTR_f_let)))
  {
//   if(!(c->flg & CSTR_f_let)) continue;
   CSTR_GetAttr(c,&attr);
   dist = 0;
   diff = 0;
   if( attr.bdiff!=127 )
	   diff=attr.bdiff;

   CSTR_GetCollectionUni(c,&vers);

//   for (v=c->vers; (let=v->let) != 0; v++)
   for(i=0;i<vers.lnAltCnt;i++)
   {
    dist = abs((attr.row + attr.h) - ( minrow + bbs3 + diff )); // Is it far from bbs3
	let = vers.Alt[i].Liga;

    ldef = let_linpos[let] & 0x0f;
    if(memchr("Љџ†",let,3))
		continue; // don't discrim
    if(memchr("ђа“г",let,4))
		gtwin = 1;

    if(ldef == 2) // Is it sunk letter
	{
     if(dist < 3)
	 {  // Yes try to discrim
      //v->prob |= 1; // Exclude from statistic by base line detection
      vers.Alt[i].Prob |= 1;
      cutting_made = 1;
	 }
	}
    if(ldef & 1 && ldef != 7) // Is it lay on bs3 don't touch ¤,ж,й
	{
     if(dist > 2)
	 { // Yes try to discrim
      //v->prob |= 1;
	  vers.Alt[i].Prob |= 1;
      cutting_made = 1;
	 }
    }

   }  // loop by hyps

   CSTR_StoreCollectionUni(c,&vers);

  }       // loop by string
 }  // end if bs == 3

 c = cell_f();
 if(bs == 2)
 {
//  while ((c=c->nextl)->nextl)
  while ((c=CSTR_GetNextRaster(c,CSTR_f_let)))
  {
// if(!(c->flg & CSTR_f_let)) continue;
   CSTR_GetAttr(c,&attr);
   CSTR_GetCollectionUni(c,&vers);

//   for (v=c->vers; (let=v->let) != 0; v++)
   for(i=0;i<vers.lnAltCnt;i++)
   {
	   let = vers.Alt[i].Liga;

	   ldef = let_linpos[let] >> 4;
	   dist = (bbs3-Ps) - (attr.row-minrow);

       if( (ldef & 2)==2 && dist>3)
	   { // Is it letter lay on bb2
        //v->prob |= 1; // Exclude from statistic by base line detection
        vers.Alt[i].Prob |= 1; // Exclude from statistic by base line detection
        cutting_made = 1;
       }

       if( ldef & 1 && dist <= 3 )  // capital
	   {
        //v->prob |= 1;
		vers.Alt[i].Prob |= 1; // Exclude from statistic by base line detection
        cutting_made = 1;
	   }

   }  // loop by hyps

   CSTR_StoreCollectionUni(c,&vers);

  }       // loop by string
 }

 if(bs == 1)
 {
  while ((c=CSTR_GetNextRaster(c,CSTR_f_let)))
  {
// if(!(c->flg & CSTR_f_let)) continue;
   CSTR_GetAttr(c,&attr);
   CSTR_GetCollectionUni(c,&vers);

//   for (v=c->vers; (let=v->let) != 0; v++)
   for(i=0;i<vers.lnAltCnt;i++)
   {
	   let = vers.Alt[i].Liga;
       ldef = let_linpos[let] >> 4;
       dist=(bbs3-Ps)-(attr.row-minrow);
       if(ldef & 1 && dist<=3) // Is it lay near bb2
	   {
        //v->prob |= 1; // Exclude from statistic by base line detection
        vers.Alt[i].Prob |= 1; // Exclude from statistic by base line detection
        cutting_made = 1;
	   }
       if( (ldef & 2)==2 && dist<=3 )  // small
	   {
        //v->prob |= 1;
        vers.Alt[i].Prob |= 1; // Exclude from statistic by base line detection
        cutting_made = 1;
	   }

   }  // loop by hyps

   CSTR_StoreCollectionUni(c,&vers);

  }       // loop by string
 }  // end if

 return (cutting_made || gtwin);
}
/////////////
extern BYTE disable_twins;
extern INT it_done;

void set_rus_difflg(CSTR_rast B1, INT filter)
{
  //CSTR_comp *env;
  CCOM_comp *env;
  CSTR_rast_attr attr;

  BYTE let, tbe, tshp;
  BYTE fl1, fl2, fl2t, flt, flnt, fl_cut, fl_retain;
//  version *v0;
  UniVersions vers;
  int i;
  BYTE cap_shape,solid,notwins;

  fl_cut = filter & f_cut;
  fl_retain = filter & f_retain;
  fl1=0xff;                           // all lines defined
  fl2=fl2t=0;
  solid=0;
  notwins=disable_twins == 1 ? 1 : 0;
  flt=0;
  flnt = 0;     // 't' only

//  env=B1->env;
  env = CSTR_GetComp(B1);

  cap_shape = 1;     // capital in shape

  CSTR_GetAttr(B1,&attr);
  CSTR_GetCollectionUni(B1,&vers);

//  for (v0=B1->vers; (let=v0->let) != 0; v0++)
  for(i=0;i<vers.lnAltCnt;i++)
  {
   /*
    if( v0->prob & 1){
     v0->prob &= 0xfe; continue;  // clear cut flag
    }
   */
    let = vers.Alt[i].Liga;

    tbe  = let_lindef[let];
    tshp = let_linshape[let];
    cap_shape &= tshp;

// Nick - check ?
    solid += tbe < 32; // is it letter without twins
    flnt = 1;

    if (env->large & CCOM_LR_UNDERLINED)
	{
      if (tshp & 2)        // sticky in shape
        { fl1=0; break; }  // no lines defined
	}

    fl2t |= tbe;  // accumulate b1/b2

    //if (v0->prob & 1)     // cutten version
	if (vers.Alt[i].Prob & 1)     // cutten version
      continue;

    if( disable_twins == 2)
	{
     if( memchr("ђа“г",let,4))
		 notwins = 1;
	}

    fl2 |= tbe;  // accumulate b1/b2

    if (tshp & 4)    // stick allows to define bs1 ( iI1l )
    {
        if (oNb2 > 3)   // base 2 defined
        {
          if (attr.h > (oPs + 3))
            { tbe |= 1; }    // allow to def bs1
        }
        else
          tbe &= 0xf4;

      // retain only b3 line for sticks while dusts not present
      #ifdef UFA
       fl1=0xff; break; // stick can't define any bases
      #endif
    }

    fl1 &= tbe;

    if( lin_pass == 3 &&
//		v0 == B1->vers && B1->nvers > 1 &&
        i == 0 && vers.lnAltCnt > 1 &&
//       (v0->prob - (v0+1)->prob) > 60 &&
        vers.Alt[i].Prob- vers.Alt[i+1].Prob > 60 &&
        !twin(let) &&
	    ( attr.recsource & CSTR_rs_BOX )
     )
    break; //
  }  // FOR versions

  if((notwins || it_done) && fl1 != 0xff )
  {
   fl1 &=(BYTE)~c_df_twins;
   fl2 &=(BYTE)~c_df_twins;
  }

  attr.difflg &= fl_retain;

  if (fl1 != 0xff)     // some bases defined
  {
     attr.basflg = flt;  // agree to 't' type
     if (fl1 & c_df_round)
       attr.basflg |= CSTR_bs_round;
     if (cap_shape)
       attr.basflg |= CSTR_bs_cap;
     if (fl2 & c_df_twins)    // a non_cutten version had a twin
     {
       if ((fl2t & (CSTR_db_b1 | CSTR_db_b2)) != (CSTR_db_b1 | CSTR_db_b2))
       //  b1 or b2  were absent in full list
       {
         fl1 &= ~(CSTR_db_b1 | CSTR_db_b2);   // forget b1/b2 as defined
         if( attr.bas_acc & CSTR_ba_chance ) // gleb
           attr.basflg |= (CSTR_bs_b1a | CSTR_bs_b2a); // agrees to be both b1. b2
       }
      if( disable_twins == 0 && fl1 & CSTR_db_b4)
		  fl1 &=~CSTR_db_b4;
     }
     if (fl2 & (CSTR_db_b1 | c_df_b1a))
       attr.basflg |= CSTR_bs_b1a;
     if (fl2 & (CSTR_db_b2 | c_df_b2a))
       attr.basflg |= CSTR_bs_b2a;
     attr.difflg = attr.difflg | (fl1 & 15);
     if (flnt==0)
		 attr.basflg |= CSTR_bs_t;  // no "not a 't'" version
  }

  CSTR_SetAttr(B1,&attr);
}

///////////////
INT smart_diff(CSTR_rast c)
{
 INT d1,d2,bm,row;
 CSTR_rast_attr attr;

 CSTR_GetAttr(c,&attr);

 row = attr.row - minrow;
 d1 = row - bbs1;
 d2 = row - bbs2;
 bm = (bbs3 + bbs2)/2;

  #ifdef UFA // Valdemar 12-05-94 08:25pm
   return attr.bdiff;
  #endif

  if( attr.h >= Ps &&
	  (abs(d1) < 2 || abs(d2) < 2) &&
	  row+attr.h >= bbs3
	)
  {
   attr.bdiff=0; // don't touch letter
   attr.difflg &= ~(CSTR_db_down | CSTR_db_up);

   CSTR_SetAttr(c, &attr);
  }  // ж,й,¤

  if(attr.h >= Ps + 2 ) // large letter
  {
   if(abs(d1) < 2 || abs(d2) < 2)
   {
    attr.bdiff=0; // don't touch letter
    attr.difflg &= ~(CSTR_db_down | CSTR_db_up);
    attr.difflg |= CSTR_db_forbid;
   }
   else
   { // letter d'not lay on base
    if( row < (bbs1+bbs2)/2 )
		return 0;    // farewell !
    if( row < bbs2 )
	{
        if( row+attr.h < bbs3 )
			return 0;   // farewell !
        else if( attr.bdiff > 0 )
               {
                attr.bdiff=0; // don't touch letter
                attr.difflg &= ~(CSTR_db_down | CSTR_db_up);
               }
	}
   }

  }

  if( attr.h < Ps-1 && (attr.flg & (CSTR_f_let | CSTR_f_bad)) )
	  // letter < Ps may be dust
  {
   if(abs(d1) < 2 || abs(d2) < 2)
   {
      attr.bdiff=0; // don't touch letter
      attr.difflg &= ~(CSTR_db_down | CSTR_db_up);
      attr.difflg |= CSTR_db_forbid;
   }
  }

  CSTR_SetAttr(c, &attr);

return attr.bdiff ;
}


INT HIST_STATISTIC=0;

extern INT hist_b3[];
extern INT hist_b3_f[];

INT GetPsFromHeights(void)
{
  CSTR_rast c;
  CSTR_rast_attr attr;
  UniVersions vers;

  INT BPs,cDefs,i,max1,max2,ind1,ind2,peak2,h_size,b_top,n_sunk,b1_or_b2_rong;
  INT Hh[RASTER_MAX_HEIGHT*2]={0};

  HIST_STATISTIC=0;

  cDefs=BPs=peak2=n_sunk=0;
  h_size=RASTER_MAX_HEIGHT*2;

  if( Ns1 && Ns2 )
	  b1_or_b2_rong = ( abs(bbs1-bbs2) > 3 ) ?   FALSE : TRUE;
  else
	  b1_or_b2_rong=FALSE;

  c=cell_f();
//  while( (c=c->nextl)->nextl )
  while ( c=CSTR_GetNextRaster(c,f_letter) )
  {
    INT df,sn;

	CSTR_GetAttr(c, &attr);
    df=attr.bdiff;
	if(df == 127 )
		df=0;
    b_top=attr.row-minrow;
    sn=(b_top+attr.h)-(df+bbs3);
    if(abs(sn) > ((attr.h+2)/5 - 2) )   // far from b3
    {
      if(sn > attr.h>>2)
		  n_sunk++;   // sunk letter
      continue;
    }

	CSTR_GetCollectionUni(c, &vers);
    if( vers.lnAltCnt>0 && BracketIn(&vers) )
		continue; // bracket present in verses,
                                                   // skip letter
    Hh[attr.h]++;
	cDefs++;   // accumulate to histogramm
  }

   // find first peak
   for(i=max1=ind1=0;i < h_size; i++)
   {
    if( max1 < Hh[i] )
	{
		max1 = Hh[i]; ind1 = i;
	}
   }

   max1*=2;
   if( ind1 > 0 )
	   max1+=Hh[ind1-1]*2;
   if( ind1 < h_size-1)
	   max1+=Hh[ind1+1]*2;
   if( ind1 < h_size-2)
	   max1+=Hh[ind1+2];
   if( ind1 >1 )
	   max1+=Hh[ind1-2];
   max1/=2;

   if(max1)
	   ind1=flood_peak(Hh,ind1);

   if( cDefs>>1 > max1 )
   {
	   multi_bas=4;
	   return 0;
   }  // flooded

   // second peak
   for(i=max2=ind2=0;i < h_size;i++)
   {
    if(i>=ind1-4 && i<=ind1+4) continue;
    if( max2 < Hh[i] )
	{
		max2 = Hh[i]; ind2 = i;
	}
   }

   max2*=2;

   if( ind2 > 0 )
	   max2+=Hh[ind2-1]*2;

   if( ind2 < h_size-1)
	   max2+=Hh[ind2+1]*2;

   if( ind2 < h_size-2)
	   max2+=Hh[ind2+2];

   if( ind2 >1 )
	   max2+=Hh[ind2-2];

   max2/=2;


   if(max2)
	   ind2=flood_peak(Hh,ind2);


   if(max2 > 0 && ( abs(ind1-ind2) >= MIN(ind1,ind2)/3) )
	   peak2=1;

   if( max2 > 3 && peak2 )
   {
	 BPs=MIN(ind1,ind2);
     bbs1=bbs3-MAX(ind1,ind2);
     goto ret;
   } // two peaks


   if( n_sunk > 2 && !b1_or_b2_rong )
   {
      INT TPs,ds;
      TPs=def_upper_side();   // check tops
      ds= ( peak2 && ind2 < ind1 ) ?  ind2 : ind1 ;
      if( TPs > 0 && abs(TPs-ds) < 3 ) { BPs=ds; goto  ret; }
   }

   if( b1_or_b2_rong == FALSE )
   {
    if(Ns2 > 0 && Ns2 >=Ns1)
    {
      INT BS2=bbs3-bbs2;
      if( abs(ind1-BS2) < 3 )
        { BPs=ind1; goto ret; }
      else if( peak2 && abs(ind2-BS2) < 3 )
        { BPs=ind2; goto ret; }
    }
    if(Ns1 > 0)
    {
      INT BS1=bbs3-bbs1;
      if( abs(ind1-BS1) < 3 )
        { BPs=-ind1; goto ret; }
      else if( peak2 && abs(ind2-BS1) < 3 )
        { BPs=-ind2; goto ret; }
    }
   } // if b1 or b2 not rong

   if( peak2 && ind1 < ind2 )
   {
	   BPs=ind1;
	   goto ret;
   }
   if( !peak2 && n_sunk > 0 )
   {
	   BPs=ind1;
	   goto ret;
   }

   if((i=setup_let_case(ind1)))
   {
      if(i==1) BPs=-ind1;
      if(i==2) BPs=ind1;
      goto ret;
   }

   if( peak2 && ind2 < ind1 )
   {
	   BPs=-ind1;
	   goto ret;
   }

    // default :
    //           H > 30 : caps letters
    //           else   : small
   HIST_STATISTIC=2;
   return ( ind1 > 30 ) ? (-ind1) : ind1 ;

   ret:
    HIST_STATISTIC=1;
    if(db_status & snap_activity_rbal(db_pass) )
	{
     CHAR buf[120];
     sprintf(buf,"Histogramms: min=%d b3=%d peak1= %d|%d peak2= %d|%d sunk=%d",
                              minrow,bbs3,    ind1,max1,   ind2,max2, n_sunk);
     snap_show_text_rbal(buf); snap_monitor_rbal();
    }
    return BPs;
}

////////////////

static BYTE BracketsList[]="<>()[]";

BYTE BracketIn(UniVersions *v)
{
  BYTE ret=0;
  int i;

  for(i=0 ; i<v->lnAltCnt; i++)
  {
   if( memchr(BracketsList,v->Alt[i].Liga,sizeof(BracketsList)-1) )
	   return 1;
  }

  return 0;
}

////////////

INT def_upper_side(void)
{
  CSTR_rast c;
  CSTR_rast_attr attr;
  INT i,max,max_sn,ind,ind_sn;
  WORD h_size,n_def,n_sn;

  INT h_top[RASTER_MAX_HEIGHT*2]={0},h_sunk[RASTER_MAX_HEIGHT*2]={0};

  h_size=RASTER_MAX_HEIGHT*2;

  c=cell_f();
  n_def=n_sn=0;
//  while( (c=c->nextl)->nextl )
  while( (c=CSTR_GetNextRaster(c, CSTR_f_let)) )
  {
    INT df,sn,b_top;

//    if(!(c->flg & CSTR_f_let) ) continue;

	CSTR_GetAttr(c, &attr);

    df=attr.bdiff;
	if(df == 127 )
		df=0;             // + diff
    b_top=attr.row-minrow;
	sn=(b_top+attr.h)-(df+bbs3);

    if( b_top-df>=RASTER_MAX_HEIGHT*2 )
        continue; // Oleg : for trash lines

    if(abs(sn) > ((attr.h+2)/5 - 2) )               // far from b3
    {
      if(sn > attr.h>>2)
	  {
		  h_sunk[b_top-df]++;
		  n_sn++;
	  }// sunk letter
      continue;
    }
    h_top[b_top-df]++;
	n_def++;
  }

   //  find peak tops
   for(i=max=ind=0;i < h_size; i++)
   {
    if( max < h_top[i] )
     {
      max = h_top[i];
      ind=i;
     }
   }

   max*=2;
   if( ind > 0 )
       max+=h_top[ind-1]*2;
   if( ind < h_size-1)
       max+=h_top[ind+1]*2;
   if( ind < h_size-2)
       max+=h_top[ind+2];
   if( ind >1 )
       max+=h_top[ind-2];
   max/=2;
   if(max)
       ind=flood_peak(h_top,ind);
   if( n_def>>1 > max )
       return FALSE;  // flooded tops

    // find peak tops of sunked
   for(i=max_sn=ind_sn=0;i < h_size; i++)
   {
    if( max_sn < h_sunk[i] )
	{
		max_sn = h_sunk[i];
		ind_sn=i;
	}
   }

   max_sn*=2;
   if( ind_sn > 0 )       max_sn+=h_sunk[ind_sn-1]*2;
   if( ind_sn < h_size-1) max_sn+=h_sunk[ind_sn+1]*2;
   if( ind_sn < h_size-2) max_sn+=h_sunk[ind_sn+2];
   if( ind_sn >1 )        max_sn+=h_sunk[ind_sn-2];
   max_sn/=2;

   if(max_sn)
	   ind_sn=flood_peak(h_sunk,ind_sn);

   if( !max_sn || n_sn>>1 > max_sn )
	   return FALSE;  // flooded sunk tops

   return ( abs(ind-ind_sn)<=3 ) ?   bbs3-ind : FALSE ;
}
//////////////

void tell_for_b3(INT hist_array[])
{
  INT i;
  DWORD NDisp,ON3;

  stable_b3=FALSE;

  if( language != LANG_RUSSIAN )
	  return;
  if( Ns3 < 5 )
	  return;

  NDisp=0;
  bbs3=(sbs3+(Ns3>>1))/Ns3;

  for(i=0; i < RASTER_MAX_HEIGHT*2 ; i++)
    NDisp+=hist_array[i]*((i-bbs3)*(i-bbs3));

  ON3 = (Ns3-1)*(Ns3-1);
  if( 25*NDisp < ON3 ) stable_b3=TRUE;
  return;
}

///////////////

INT flood_peak(INT *Hh,INT ind)
{
 WORD Num,Den,t,t1;

 if(ind > 1)
      t=Hh[ind-2]*(ind-2);
 else t=0;

 if(ind > 0)
      t1=Hh[ind-1]*(ind-1);
 else t1=0;

 Num  = Hh[ind]*ind+t1+Hh[ind+1]*(ind+1)+
        t+Hh[ind+2]*(ind+2);
 if(ind > 1)
      t=Hh[ind-2];
 else t=0;

 if(ind > 0)
      t1=Hh[ind-1];
 else t1=0;

 Den  = Hh[ind]+t1+Hh[ind+1]+t+Hh[ind+2];

 return( (2*Num+Den)/2/Den );
}

/////////
INT is_cen_bottom_accent(BYTE c)
{
// Определение нижнего акцента 12.09.2000 E.P.
	return (

	c== AA_bottom_accent		|| c== a_bottom_accent		||
	c== CC_bottom_accent		|| c== c_bottom_accent		||
	c== EE_bottom_accent		|| c== e_bottom_accent		||
	c== SS_bottom_accent_latin	|| c== s_bottom_accent_latin||
	c== TT_bottom_accent		|| c== t_bottom_accent		||

	0);
}
////////////
