text: /**
   * Copyright (c) 2015, Jozef Stefan Institute, Quintelligence d.o.o. and contributors
   * All rights reserved.
   *
   * This source code is licensed under the FreeBSD license found in the
   * LICENSE file in the root directory of this source tree.
   */
  
  /////////////////////////////////////////////////
  // Random
  const int TRnd::RndSeed=0;
  const int TRnd::a=16807;
  const int TRnd::m=2147483647;
  const int TRnd::q=127773; // m DIV a
  const int TRnd::r=2836; // m MOD a
  
  void TRnd::LoadXml(const PXmlTok& XmlTok, const TStr& Nm){
    XLoadHd(Nm);
    Seed=TXmlObjSer::GetIntArg(XmlTok, "Seed");
  }
  
  void TRnd::SaveXml(TSOut& SOut, const TStr& Nm) const {
    XSaveBETagArg(Nm, "Seed", TInt::GetStr(Seed));
  }
  
  void TRnd::PutSeed(const int& _Seed){
    Assert(_Seed>=0);
    if (_Seed==0){
      //Seed=int(time(NULL));
      Seed=abs(int(TSysTm::GetPerfTimerTicks()));
    } else {
      Seed=_Seed;
      //Seed=abs(_Seed*100000)+1;
    }
  }
  
  void TRnd::Move(const int& Steps){
    for (int StepN=0; StepN<Steps; StepN++){GetNextSeed();}
  }
  
  bool TRnd::Check(){
    int PSeed=Seed; Seed=1;
    for (int SeedN=0; SeedN<10000; SeedN++){GetNextSeed();}
    bool Ok=Seed==1043618065; Seed=PSeed; return Ok;
  }
  
  int TRnd::GetUniDevInt(const int& Range){
    int Seed=GetNextSeed();
    if (Range==0){return Seed;}
    else {return Seed%Range;}
  }
  
  uint TRnd::GetUniDevUInt(const uint& Range){
    uint Seed=uint(GetNextSeed()%0x10000)*0x10000+uint(GetNextSeed()%0x10000);
    if (Range==0){return Seed;}
    else {return Seed%Range;}
  }
  
  int64 TRnd::GetUniDevInt64(const int64& Range){
    const int64 RndVal = int64((uint64(GetUniDevInt())<<32) | uint64(GetUniDevInt()));
    if (Range==0){return RndVal;}
    else {return RndVal%Range;}
  }
  
  uint64 TRnd::GetUniDevUInt64(const uint64& Range){
   const uint64 RndVal = uint64((uint64(GetUniDevInt())<<32) | uint64(GetUniDevInt()));
   if (Range==0){return RndVal;}
   else {return RndVal%Range;}
  }
  
  double TRnd::GetNrmDev(){
    double v1, v2, rsq;
    do {
      v1=2.0*GetUniDev()-1.0; // pick two uniform numbers in the square
      v2=2.0*GetUniDev()-1.0; // extending from -1 to +1 in each direction
      rsq=v1*v1+v2*v2; // see if they are in the unit cicrcle
    } while ((rsq>=1.0)||(rsq==0.0)); // and if they are not, try again
    double fac=sqrt(-2.0*log(rsq)/rsq); // Box-Muller transformation
    return v1*fac;
  //  return v2*fac; // second deviate
  }
  
  double TRnd::GetNrmDev(
   const double& Mean, const double& SDev, const double& Mn, const double& Mx){
    double Val=Mean+GetNrmDev()*SDev;
    if (Val<Mn){Val=Mn;}
    if (Val>Mx){Val=Mx;}
    return Val;
  }
  
  double TRnd::GetExpDev(){
    double UniDev;
    do {
      UniDev=GetUniDev();
    } while (UniDev==0.0);
    return -log(UniDev);
  }
  
  double TRnd::GetExpDev(const double& Lambda) {
    return GetExpDev()/Lambda;
  }
  
  double TRnd::GetGammaDev(const int& Order){
    int j;
    double am,e,s,v1,v2,x,y;
    if (Order<1){Fail;}
    if (Order<6) {
      x=1.0;
      for (j=1;j<=Order;j++) x *=GetUniDev();
      x = -log(x);
    } else {
      do {
        do {
          do {
            v1=2.0*GetUniDev()-1.0;
            v2=2.0*GetUniDev()-1.0;
          } while (v1*v1+v2*v2 > 1.0);
          y=v2/v1;
          am=Order-1;
          s=sqrt(2.0*am+1.0);
          x=s*y+am;
        } while (x <= 0.0);
        e=(1.0+y*y)*exp(am*log(x/am)-s*y);
      } while (GetUniDev()>e);
    }
    return x;
  }
  
  double TRnd::GetPoissonDev(const double& Mean){
    static double sq,alxm,g,oldm=(-1.0);
    double em,t,y;
    if (Mean < 12.0) {
      if (Mean != oldm) {
        oldm=Mean;
        g=exp(-Mean);
      }
      em = -1;
      t=1.0;
      do {
        ++em;
        t *= GetUniDev();
      } while (t>g);
    } else {
      if (Mean != oldm) {
        oldm=Mean;
        sq=sqrt(2.0*Mean);
        alxm=log(Mean);
        g=Mean*alxm-TSpecFunc::LnGamma(Mean+1.0);
      }
      do {
        do {
          y=tan(TMath::Pi*GetUniDev());
          em=sq*y+Mean;
        } while (em < 0.0);
        em=floor(em);
        t=0.9*(1.0+y*y)*exp(em*alxm-TSpecFunc::LnGamma(em+1.0)-g);
      } while (GetUniDev()>t);
    }
    return em;
  }
  
  double TRnd::GetBinomialDev(const double& Prb, const int& Trials){
    int j;
    static int nold=(-1);
    double am,em,g,angle,p,bnl,sq,t,y;
    static double pold=(-1.0),pc,plog,pclog,en,oldg;
  
    p=(Prb <= 0.5 ? Prb : 1.0-Prb);
    am=Trials*p;
    if (Trials < 25) {
      bnl=0.0;
      for (j=1;j<=Trials;j++)
        if (GetUniDev() < p) ++bnl;
    } else if (am < 1.0) {
      g=exp(-am);
      t=1.0;
      for (j=0;j<=Trials;j++) {
        t *= GetUniDev();
        if (t < g) break;
      }
      bnl=(j <= Trials ? j : Trials);
    } else {
      if (Trials != nold) {
        en=Trials;
        oldg=TSpecFunc::LnGamma(en+1.0);
        nold=Trials;
      } if (p != pold) {
        pc=1.0-p;
        plog=log(p);
        pclog=log(pc);
        pold=p;
      }
      sq=sqrt(2.0*am*pc);
      do {
        do {
          angle=TMath::Pi*GetUniDev();
          y=tan(angle);
          em=sq*y+am;
        } while (em < 0.0 || em >= (en+1.0));
        em=floor(em);
        t=1.2*sq*(1.0+y*y)*exp(oldg-(em+1.0)
          -TSpecFunc::LnGamma(en-em+1.0)+em*plog+(en-em)*pclog);
      } while (GetUniDev() > t);
      bnl=em;
    }
    if (p != Prb) bnl=Trials-bnl;
    return bnl;
  }
  
  // sample points from d-dimensional unit sphere
  /*void TRnd::GetSphereDev(const int& Dim, TFltV& ValV) {
    if (ValV.Len() != Dim) { ValV.Gen(Dim); }
    double Length = 0.0;
    for (int i = 0; i < Dim; i++) {
      ValV[i] = GetNrmDev();
      Length += TMath::Sqr(ValV[i]); }
    Length = 1.0 / sqrt(Length);
    for (int i = 0; i < Dim; i++) {
      ValV[i] *= Length;
    }
  }*/
  
  TRnd TRnd::LoadTxt(TILx& Lx){
    return TRnd(Lx.GetInt());
  }
  
  void TRnd::SaveTxt(TOLx& Lx) const {
    Lx.PutInt(Seed);
  }
  
  uint64 TRnd::GetMemUsed() const {
      return sizeof(TRnd) + TMemUtils::GetExtraMemberSize(Seed);
  }
  
  /////////////////////////////////////////////////
  /// Thin Input-Memory
  TThinMIn::TThinMIn() :
      TSBase(), TSIn(), Bf(NULL), BfC(0), BfL(0) { }
  
  TThinMIn::TThinMIn(const TMemBase& Mem) :
  TSBase(), TSIn(), Bf(NULL), BfC(0), BfL(0) {
  
  	Bf = (uchar*)Mem.GetBf();
  	BfL = Mem.Len();
  }
  
  TThinMIn::TThinMIn(const void* _Bf, const int& _BfL) :
  TSBase(), TSIn(),
  Bf(NULL), BfC(0), BfL(_BfL) {
  
  	Bf = (uchar*)_Bf;
  }
  
  TThinMIn::TThinMIn(const TThinMIn& min) :
  TSBase(), TSIn() {
  	Bf = min.Bf;
  	BfL = min.BfL;
  	BfC = min.BfC;
  }
  
  char TThinMIn::GetCh() {
  	IAssertR(BfC<BfL, "Reading beyond the end of stream.");
  	return Bf[BfC++];
  }
  
  char TThinMIn::PeekCh() {
  	IAssertR(BfC<BfL, "Reading beyond the end of stream.");
  	return Bf[BfC];
  }
  
  int TThinMIn::GetBf(const void* LBf, const TSize& LBfL) {
  	IAssert(TSize(BfC + LBfL) <= TSize(BfL));
  	int LBfS = 0;
  	for (TSize LBfC = 0; LBfC<LBfL; LBfC++) {
  		LBfS += (((char*)LBf)[LBfC] = Bf[BfC++]);
  	}
  	return LBfS;
  }
  
  void TThinMIn::MoveTo(int Offset) {
  	IAssertR(Offset<BfL, "Reading beyond the end of stream.");
  	BfC = Offset;
  }
  
  bool TThinMIn::GetNextLnBf(TChA& LnChA) {
  	return GetNextLn(LnChA);
  }
  
  TStr TThinMIn::GetSNm() const {
      return "Thin input memory";
  }
  /////////////////////////////////////////////////
  // Memory
  
  void TMem::Copy(const TMem& Mem) {
    if (this != &Mem) {
      if (Bf != NULL) { delete[] Bf; }
      MxBfL = Mem.MxBfL; BfL = Mem.BfL; Bf = NULL;
      if (MxBfL>0) { Bf = new char[MxBfL]; memcpy(Bf, Mem.Bf, BfL); }
    }
  }
  void TMem::Copy(const TMemBase& Mem) {
    if (Bf != NULL) { delete[] Bf; }
    MxBfL = Mem.MxBfL; BfL = Mem.BfL; Bf = NULL;
    if (MxBfL>0) { Bf = new char[MxBfL]; memcpy(Bf, Mem.Bf, BfL); }
  }
  
  void TMem::Resize(const int& _MxBfL){
    if (_MxBfL<=MxBfL){return;}
    else {if (MxBfL*2<_MxBfL){MxBfL=_MxBfL;} else {MxBfL*=2;}}
    char* NewBf=new char[MxBfL]; IAssert(NewBf!=NULL);
    if (BfL>0){memcpy(NewBf, Bf, BfL);}
    if (Bf!=NULL){delete[] Bf;}
    Bf=NewBf;
  }
  
  TMem::TMem(const TStr& Str) {
    MxBfL = Str.Len(); BfL = MxBfL; Bf = NULL;
    if (MxBfL>0){
      Bf=new char[MxBfL];
      if (BfL>0){memcpy(Bf, Str.CStr(), BfL);}
    }
  }
  
  void TMem::SaveXml(TSOut& SOut, const TStr& Nm) const {
    XSaveHdArg(Nm, "BfL", TInt::GetStr(BfL));
    SOut.PutStr(TXmlLx::GetXmlStrFromPlainMem(*this));
  }
  
  bool TMem::DoFitStr(const TStr& Str) const {
    return DoFitLen(Str.Len()+1);
  }
  
  TMem& TMem::operator+=(const char& Ch){
    if (BfL==MxBfL){Resize(BfL+1);}
    Bf[BfL]=Ch; BfL++; return *this;
  }
  
  TMem& TMem::operator+=(const TMem& Mem){
    int LBfL=Mem.Len(); if (BfL+LBfL>MxBfL){Resize(BfL+LBfL);}
    if (LBfL>0){memcpy(&Bf[BfL], Mem(), LBfL);}
    BfL+=LBfL; return *this;
  }
  
  TMem& TMem::operator+=(const TStr& Str){
    int LBfL=Str.Len(); if (BfL+LBfL>MxBfL){Resize(BfL+LBfL);}
    if (LBfL>0){memcpy(Bf+BfL, Str.CStr(), LBfL);}
    BfL+=LBfL; return *this;
  }
  
  TMem& TMem::operator+=(const PSIn& SIn){
    int LBfL=SIn->Len(); if (BfL+LBfL>MxBfL){Resize(BfL+LBfL);}
    char* LBf=new char[LBfL];
    SIn->GetBf(LBf, LBfL);
    if (LBfL>0){memcpy(Bf+BfL, LBf, LBfL);}
    delete[] LBf;
    BfL+=LBfL; return *this;
  }
  
  TMem& TMem::operator=(const TMem& Mem) {
    if (this != &Mem) {
      if (Bf != NULL) { delete[] Bf; }
      MxBfL = Mem.MxBfL; BfL = Mem.BfL; Bf = NULL;
      if (MxBfL>0) { Bf = new char[MxBfL]; memcpy(Bf, Mem.Bf, BfL); }
    }
    return *this;
  }
  TMem& TMem::operator=(TMem&& Src) {
    if (this != &Src) {
      if (Bf != NULL) { delete[] Bf; }
      MxBfL = Src.MxBfL; BfL = Src.BfL; Bf = Src.Bf;
      Src.MxBfL = Src.BfL = 0; Src.Bf = NULL;
    }
    return *this;
  }
  TMem& TMem::operator=(const TMemBase& Mem) {
    if (Bf != NULL) { delete[] Bf; }
    MxBfL = Mem.MxBfL; BfL = Mem.BfL; Bf = NULL;
    if (MxBfL>0) { Bf = new char[MxBfL]; memcpy(Bf, Mem.Bf, BfL); }
    return *this;
  }
  TMem& TMem::operator=(TMemBase&& Src) {
    if (Bf != NULL) { delete[] Bf; }
    if (Src.Owner) { // take over the buffer
      MxBfL = Src.MxBfL; BfL = Src.BfL; Bf = Src.Bf;
      Src.MxBfL = Src.BfL = 0; Src.Bf = NULL; Src.Owner = false;
    }
    else { // copy the contents
      MxBfL = Src.MxBfL; BfL = Src.BfL; Bf = NULL;
      if (MxBfL>0) { Bf = new char[MxBfL]; memcpy(Bf, Src.Bf, BfL); }
    }
    return *this;
  }
  
  void TMem::Del(const int& BChN, const int& EChN){
    if (BChN>EChN){return;}
    if ((BChN==0)&&(EChN==BfL-1)){Clr(); return;}
    IAssert((0<=BChN)&&(BChN<=EChN)&&(EChN<BfL));
    memmove(Bf+BChN, Bf+EChN+1, BfL-EChN-1);
    BfL-=(EChN-BChN+1);
  }
  
  //int TMem::AddStr(const TStr& Str){
  //  int LBfL=Str.Len()+1; Resize(BfL+LBfL);
  //  if (LBfL>0){memcpy(&Bf[BfL], Str.CStr(), LBfL);}
  //  int FChN=BfL; BfL+=LBfL; return FChN;
  //}
  
  void TMem::AddBf(const void* _Bf, const int& _BfL){
    IAssert((_BfL>=0) && (_Bf != NULL));
    Reserve(Len() + _BfL, false);
    memcpy(Bf + BfL, _Bf, _BfL);
    BfL+=_BfL;
    //char* ChBf=(char*)Bf;
    //for (int BfC=0; BfC<BfL; BfC++){
    //  char Ch=ChBf[BfC];
    //  operator+=(Ch);
    //}
  }
  
  TStr TMem::GetAsStr(const char& NewNullCh) const {
    if (NewNullCh!='\0'){
      TChA ChA(*this);
      ChA.ChangeCh('\0', NewNullCh);
      return ChA;
    } else {
      return TStr(*this);
    }
  }
  
  TStr TMem::GetHexStr() const {
  	TChA ChA;
  	for (int ChN = 0; ChN< BfL ; ChN++){
  		uchar Ch = uchar(Bf[ChN]);
  		char MshCh = TCh::GetHexCh((Ch / 16) % 16);
  		char LshCh = TCh::GetHexCh(Ch % 16);
  		ChA += MshCh; ChA += LshCh;
  	}
  	return TStr(ChA);
  }
  
  TMem TMem::GetFromHex(const TStr& Str) {
  	int StrLen = Str.Len(); IAssert(StrLen % 2 == 0);
  	TChA ChA; int ChN = 0;
  	while (ChN<StrLen){
  		char MshCh = Str.GetCh(ChN); ChN++;
  		char LshCh = Str.GetCh(ChN); ChN++;
  		uchar Ch = uchar(TCh::GetHex(MshCh) * 16 + TCh::GetHex(LshCh));
  		ChA += Ch;
  	}
  	return TMem(ChA.CStr(), ChA.Len());
  }
  
  ///////////////////////////////////////////////////////////////
  // Base64 encoding
  // Code skeleton taken from http://www.adp-gmbh.ch/cpp/common/base64.html
  // on 5.12.2015 and re-arranged to fit into glib.
  
  const TStr TStr::base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  
  TStr TStr::Base64Encode(const void* Bf, const int BfL) {
  	TStr ret;
  	int i = 0;
  	int j = 0;
  	unsigned char char_array_3[3];
  	unsigned char char_array_4[4];
  	int BfLTmp = BfL;
  	const uchar* BfTmp = (uchar*)Bf;
  	while (BfLTmp--) {
  		char_array_3[i++] = *(BfTmp++);
  		if (i == 3) {
  			char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
  			char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
  			char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
  			char_array_4[3] = char_array_3[2] & 0x3f;
  
  			for (i = 0; (i <4); i++)
  				ret += base64_chars[char_array_4[i]];
  			i = 0;
  		}
  	}
  
  	if (i) {
  		for (j = i; j < 3; j++) {
  			char_array_3[j] = '\0';
  		}
  		char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
  		char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
  		char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
  		char_array_4[3] = char_array_3[2] & 0x3f;
  
  		for (j = 0; (j < i + 1); j++) {
  			ret += base64_chars[char_array_4[j]];
  		}
  		while ((i++ < 3)) {
  			ret += '=';
  		}
  	}
  	return ret;
  }
  
  void TStr::Base64Decode(const TStr& In, TMem& Mem) {
  	int in_len = In.Len();
  	int i = 0;
  	int j = 0;
  	int in_ = 0;
  	unsigned char char_array_4[4], char_array_3[3];
  	Mem.Reserve(In.Len());
  	while (in_len-- && (In[in_] != '=') && is_base64(In[in_])) {
  		char_array_4[i++] = In[in_];
  		in_++;
  		if (i == 4) {
  			for (i = 0; i < 4; i++) {
  				char_array_4[i] = (uchar)base64_chars.SearchCh(char_array_4[i]);
  			}
  			char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
  			char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
  			char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
  
  			Mem.AddBf(&char_array_3, 3);
  			//for (i = 0; (i < 3); i++) {
  			//	Mem.AddBf(&char_array_3[i], 1);
  			//}
  			i = 0;
  		}
  	}
  
  	if (i) {
  		for (j = i; j < 4; j++) {
  			char_array_4[j] = 0;
  		}
  		for (j = 0; j < 4; j++) {
  			char_array_4[j] = (uchar)base64_chars.SearchCh(char_array_4[j]);
  		}
  		char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
  		char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
  		char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
  
  		for (j = 0; (j < i - 1); j++) {
  			Mem.AddBf(&char_array_3[j], 1);
  		}
  	}
  }
  
  /////////////////////////////////////////////////
  // Input-Memory
  TMemIn::TMemIn(const TMem& _Mem, const int& _BfC):
    TSBase(), TSIn(), Bf(_Mem()), BfC(_BfC), BfL(_Mem.Len()){}
  
  int TMemIn::GetBf(const void* LBf, const TSize& LBfL){
    Assert(TSize(BfC+LBfL)<=TSize(BfL));
    int LBfS=0;
    for (TSize LBfC=0; LBfC<LBfL; LBfC++){
      LBfS+=(((char*)LBf)[LBfC]=Bf[BfC++]);}
    return LBfS;
  }
  
  bool TMemIn::GetNextLnBf(TChA& LnChA){
    // not implemented
    FailR(TStr::Fmt("TMemIn::GetNextLnBf: not implemented").CStr());
    return false;
  }
  
  TStr TMemIn::GetSNm() const {
    return "Input-Memory";
  }
  
  /////////////////////////////////////////////////
  // Output-Memory
  TRefMemOut::TRefMemOut(TMem& _Mem): TSBase(),
      TSOut(), Mem(_Mem){}
  
  int TRefMemOut::PutBf(const void* LBf, const TSize& LBfL){
    int LBfS=0;
    for (TSize LBfC=0; LBfC<LBfL; LBfC++){
      char Ch=((char*)LBf)[LBfC];
      LBfS+=Ch; Mem+=Ch;
    }
    return LBfS;
  }
  
  TStr TRefMemOut::GetSNm() const {
    return "Output-Reference-Memory";
  }
  
  /////////////////////////////////////////////////
  // Char-Array
  void TChA::Resize(const int& _MxBfL){
    // Get max vale for MxBfL
    const auto Max = TInt::Mx - 1;
    // Make sure we got valid _MxBfL
    IAssert(0 <= _MxBfL && _MxBfL < Max);
    // Check if there is something to do
    if (_MxBfL <= MxBfL) return;
    // Check if _MxBfL at least twice larger then MxBf
    // We devide to avoid overflow when multiplying with 2
    if (_MxBfL / 2 >= MxBfL) { MxBfL = _MxBfL; }
    // if not we try to dobule the size. In case we go under 0,
    // which indicates overflow, we resort to max size
    else { MxBfL *= 2; if (MxBfL < 0 || MxBfL > Max) MxBfL = Max; }
    // Reserve new buffer
    char* NewBf=new char[MxBfL+1]; IAssert(NewBf!=NULL);
    // Copy old contents ...
    strcpy(NewBf, Bf);
    // and delete old buffer
    delete[] Bf; Bf=NewBf;
  }
  
  TChA::TChA(const TStr& Str){
    Bf=new char[(MxBfL=BfL=Str.Len())+1];
    strcpy(Bf, Str.CStr());
  }
  
  void TChA::SaveXml(TSOut& SOut, const TStr& Nm) const {
    XSaveHdArg(Nm, "BfL", TInt::GetStr(BfL));
    SOut.PutStr(TXmlLx::GetXmlStrFromPlainStr(*this));
  }
  
  TChA& TChA::operator=(const TChA& ChA){
    if (this!=&ChA){
      if (ChA.BfL>MxBfL){delete[] Bf; Bf=new char[(MxBfL=ChA.BfL)+1];}
      BfL=ChA.BfL; strcpy(Bf, ChA.CStr());
    }
    return *this;
  }
  
  TChA& TChA::operator=(const TStr& Str){
    if (Str.Len()>MxBfL){delete[] Bf; Bf=new char[(MxBfL=Str.Len())+1];}
    BfL=Str.Len(); strcpy(Bf, Str.CStr());
    return *this;
  }
  
  TChA& TChA::operator=(const char* CStr){
    int CStrLen=int(strlen(CStr));
    if (CStrLen>MxBfL){delete[] Bf; Bf=new char[(MxBfL=CStrLen)+1];}
    BfL=CStrLen; strcpy(Bf, CStr);
    return *this;
  }
  
  TChA& TChA::operator+=(const TMem& Mem) {
    Resize(BfL+Mem.Len());
    strcpy(Bf+BfL, Mem.GetBf()); BfL+=Mem.Len(); return *this;
  }
  
  TChA& TChA::operator+=(const TChA& ChA){
    Resize(BfL+ChA.Len());
    strcpy(Bf+BfL, ChA.CStr()); BfL+=ChA.Len(); return *this;
  }
  
  TChA& TChA::operator+=(const TStr& Str){
    Resize(BfL+Str.Len());
    strcpy(Bf+BfL, Str.CStr()); BfL+=Str.Len(); return *this;
  }
  
  TChA& TChA::operator+=(const char* CStr){
    int CStrLen=(int)strlen(CStr); Resize(BfL+CStrLen);
    strcpy(Bf+BfL, CStr); BfL+=CStrLen; return *this;
  }
  
  void TChA::Ins(const int& BChN, const char* CStr){
    Assert((0<=BChN)&&(BChN<=BfL)); //** ali je <= v (BChN<=BfL) upravicen?
    int CStrLen=int(strlen(CStr)); Resize(BfL+CStrLen);
    memmove(Bf+BChN+CStrLen, Bf+BChN, BfL-BChN+1);
    memmove(Bf+BChN, CStr, CStrLen); BfL+=CStrLen;
  }
  
  void TChA::Del(const int& ChN){
    Assert((0<=ChN)&&(ChN<BfL));
    memmove(Bf+ChN, Bf+ChN+1, BfL-ChN);
    BfL--;
  }
  
  void TChA::Trunc(){
    int BChN=0; while ((BChN<BfL)&&(GetCh(BChN)<=' ')){BChN++;}
    int EChN=BfL-1; while ((0<=EChN)&&(GetCh(EChN)<=' ')){EChN--;}
    if (BChN<=EChN){
      for (int ChN=BChN; ChN<=EChN; ChN++){
        PutCh(ChN-BChN, GetCh(ChN));}
      Trunc(EChN-BChN+1);
    } else {
      Clr();
    }
  /*  int BChN=0; while ((BChN<BfL)&&(Bf[BChN]<=' ')){BChN++;}
    int EChN=BfL-1; while ((0<=EChN)&&(Bf[EChN]<=' ')){EChN--;}
    if (BChN<=EChN){
      for (int ChN=BChN; ChN<=EChN; ChN++){Bf[ChN-BChN]=Bf[ChN];}
      Bf[BfL=EChN+1]=0;
    } else {
      Clr();
    }*/
  }
  
  void TChA::Reverse(){
    for (int ChN=0; ChN<BfL/2; ChN++){
      char Ch=Bf[ChN];
      Bf[ChN]=Bf[BfL-ChN-1];
      Bf[BfL-ChN-1]=Ch;
    }
  }
  
  TChA TChA::GetSubStr(const int& _BChN, const int& _EChN) const {
    int BChN=TInt::GetMx(_BChN, 0);
    int EChN=TInt::GetMn(_EChN, Len()-1);
    int Chs=EChN-BChN+1;
    if (Chs<=0){return TStr();}
    else if (Chs==Len()){return *this;}
    else {
      //char* Bf=new char[Chs+1]; strncpy(Bf, CStr()+BChN, Chs); Bf[Chs]=0;
      //TStr Str(Bf); delete[] Bf;
      //return Str;
      return TChA(CStr()+BChN, Chs);
    }
  }
  
  int TChA::CountCh(const char& Ch, const int& BChN) const {
    int ChN=TInt::GetMx(BChN, 0);
    const int ThisLen=Len();
    int Cnt = 0;
    while (ChN<ThisLen){if (Bf[ChN]==Ch){ Cnt++;} ChN++;}
    return Cnt;
  }
  
  int TChA::SearchCh(const char& Ch, const int& BChN) const {
    int ChN=TInt::GetMx(BChN, 0);
    const int ThisLen=Len();
    while (ChN<ThisLen){if (Bf[ChN]==Ch){return ChN;} ChN++;}
    return -1;
  }
  
  int TChA::SearchChBack(const char& Ch, int BChN) const {
    if (BChN >= Len() || BChN < 0) { BChN = Len()-1; }
    for (int i = BChN; i >= 0; i--) {
      if (GetCh(i) == Ch) { return i; }
    }
    return -1;
  }
  
  int TChA::SearchStr(const TChA& Str, const int& BChN) const {
    return SearchStr(Str.CStr(), BChN);
  }
  
  int TChA::SearchStr(const TStr& Str, const int& BChN) const {
    return SearchStr(Str.CStr(), BChN);
  }
  
  int TChA::SearchStr(const char* CStr, const int& BChN) const {
    const char* BegPos=strstr(Bf+BChN, CStr);
    if (BegPos==NULL){return -1;}
    else {return int(BegPos-Bf);}
  }
  
  bool TChA::IsPrefix(const char* CStr, const int& BChN) const {
    if (BChN+(int)strlen(CStr)>Len()){return false;}
    const char* B = Bf+BChN;
    const char* C = CStr;
    while (*C!=0 && *B==*C) {
      B++; C++;
    }
    if (*C==0){return true;}
    else {return false;}
  }
  
  bool TChA::IsPrefix(const TStr& Str) const {
    return IsPrefix(Str.CStr());
  }
  
  bool TChA::IsPrefix(const TChA& Str) const {
    return IsPrefix(Str.CStr());
  }
  
  bool TChA::IsPrefixLc(const char* CStr, const int& BChN) const {
    if (BChN+(int)strlen(CStr)>Len()){return false;}
    const char* B = Bf+BChN;
    const char* C = CStr;
    while (*C!=0 && tolower(*B)==tolower(*C) ) {
      B++; C++;
    }
    if (*C==0){return true;}
    else {return false;}
  }
  
  bool TChA::IsPrefixLc(const TStr& Str) const {
    return IsPrefixLc(Str.CStr());
  }
  
  bool TChA::IsPrefixLc(const TChA& Str) const {
    return IsPrefixLc(Str.CStr());
  }
  
  bool TChA::IsSuffix(const char* CStr) const {
    if ((int)strlen(CStr) > Len()) { return false; }
    const char* E = Bf+Len()-1;
    const char* C = CStr+strlen(CStr)-1;
    while (C >= CStr && *E==*C) {
      E--;  C--;
    }
    if (C+1 == CStr) { return true; }
    else { return false; }
  }
  
  bool TChA::IsSuffix(const TStr& Str) const {
    return IsSuffix(Str.CStr());
  }
  
  bool TChA::IsSuffix(const TChA& Str) const {
    return IsSuffix(Str.CStr());
  }
  
  void TChA::ChangeCh(const char& SrcCh, const char& DstCh){
    int StrLen=Len();
    for (int ChN=0; ChN<StrLen; ChN++){if (Bf[ChN]==SrcCh){Bf[ChN]=DstCh;}}
  }
  
  /*void TChA::ToUc(){
    int StrLen=Len();
    for (int ChN=0; ChN<StrLen; ChN++){Bf[ChN]=(char)toupper(Bf[ChN]);}
  }
  
  void TChA::ToLc(){
    int StrLen=Len();
    for (int ChN=0; ChN<StrLen; ChN++){Bf[ChN]=(char)tolower(Bf[ChN]);}
  }*/
  
  TChA& TChA::ToLc() {
    char *c = Bf;
    while (*c) {
      *c = (char) tolower(*c);  c++;
    }
    return *this;
  }
  
  TChA& TChA::ToUc() {
    char *c = Bf;
    while (*c) {
      *c = (char) toupper(*c); c++;
    }
    return *this;
  }
  
  TChA& TChA::ToTrunc(){
    int StrLen=Len(); int BChN=0; int EChN=StrLen-1;
    while ((BChN<StrLen)&&TCh::IsWs(GetCh(BChN))){BChN++;}
    while ((EChN>=0)&&TCh::IsWs(GetCh(EChN))){EChN--;}
    if ((BChN!=0)||(EChN!=StrLen-1)){
      int DstChN=0;
      for (int SrcChN=BChN; SrcChN<=EChN; SrcChN++){
        PutCh(DstChN, GetCh(SrcChN)); DstChN++;}
      Trunc(DstChN);
    }
    return *this;
  }
  
  void TChA::CompressWs(){
    int StrLen=Len(); int SrcChN=0; int DstChN=0;
    while ((SrcChN<StrLen)&&TCh::IsWs(GetCh(SrcChN))){SrcChN++;}
    while (SrcChN<StrLen){
      if ((TCh::IsWs(GetCh(SrcChN)))&&(DstChN>0)&&(TCh::IsWs(GetCh(DstChN-1)))){
        SrcChN++;
      } else {
        PutCh(DstChN, GetCh(SrcChN)); SrcChN++; DstChN++;
      }
    }
    if ((DstChN>0)&&(TCh::IsWs(GetCh(DstChN-1)))){DstChN--;}
    Trunc(DstChN);
  }
  
  void TChA::Swap(const int& ChN1, const int& ChN2){
    char Ch=GetCh(ChN1);
    PutCh(ChN1, GetCh(ChN2));
    PutCh(ChN2, Ch);
  }
  
  void TChA::Swap(TChA& ChA) {
    ::Swap(MxBfL, ChA.MxBfL);
    ::Swap(BfL, ChA.BfL);
    ::Swap(Bf, ChA.Bf);
  }
  
  int TChA::GetPrimHashCd() const {
    return TStrHashF_DJB::GetPrimHashCd(CStr());
  }
  
  int TChA::GetSecHashCd() const {
    return TStrHashF_DJB::GetSecHashCd(CStr());
  }
  
  
  void TChA::LoadTxt(const PSIn& SIn, TChA& ChA){
    delete[] ChA.Bf;
    ChA.Bf=new char[(ChA.MxBfL=ChA.BfL=SIn->Len())+1];
    SIn->GetBf(ChA.CStr(), SIn->Len()); ChA.Bf[ChA.BfL]=0;
  }
  
  void TChA::SaveTxt(const PSOut& SOut) const {
    SOut->SaveBf(CStr(), Len());
  }
  
  /*TChA operator+(const TChA& LStr, const TChA& RStr){
    return LStr+=RStr; }
  }
  
  TChA operator+(const TChA& LStr, const TStr& RStr){
    return LStr+=RStr.CStr();
  }
  
  TChA operator+(const TStr& LStr, const char* RCStr){
    return LStr+=RCStr;
  }*/
  
  
  /////////////////////////////////////////////////
  // Input-Char-Array
  TChAIn::TChAIn(const TChA& ChA, const int& _BfC):
    TSBase(), TSIn(), Bf(ChA.CStr()), BfC(_BfC), BfL(ChA.Len()){}
  
  int TChAIn::GetBf(const void* LBf, const TSize& LBfL){
    Assert(TSize(BfC+LBfL)<=TSize(BfL));
    int LBfS=0;
    for (TSize LBfC=0; LBfC<LBfL; LBfC++){
      LBfS+=(((char*)LBf)[LBfC]=Bf[BfC++]);}
    return LBfS;
  }
  
  bool TChAIn::GetNextLnBf(TChA& LnChA){
    // not implemented
    FailR(TStr::Fmt("TChAIn::GetNextLnBf: not implemented").CStr());
    return false;
  }
  
  TStr TChAIn::GetSNm() const {
    return "Input-Char-Array";
  }
  
  /////////////////////////////////////////////////
  // String
  const char TStr::EmptyStr = 0;
  
  TStr TStr::WrapCStr(char* _CStr) {
  	TStr NewStr;
  
  	if (_CStr != nullptr && _CStr[0] == 0) {
  		delete[] _CStr;
  	} else if (_CStr != nullptr) {
  		NewStr.Inner = _CStr;
  	}
  
  	return NewStr;
  }
  
  TStr::TStr(const char* _CStr): Inner(nullptr) {
  	if (_CStr == nullptr) { return; }
  
  	const int Len = (int)strlen(_CStr);
  	if (Len > 0) {
  		Inner = new char[Len+1];
  		strcpy(Inner, _CStr);
  	}
  }
  
  TStr::TStr(const char& Ch): Inner(nullptr) {
      if (Ch != 0) {
          Inner = new char[2];
          Inner[0] = Ch; Inner[1] = 0;
      }
  }
  
  TStr::TStr(const TStr& Str): Inner(nullptr) {
  	if (!Str.Empty()) {
  		Inner = Str.CloneCStr();
  	}
  }
  
  TStr::TStr(TStr&& Str): Inner(nullptr) {
      Inner = Str.Inner;
      // reset other
      Str.Inner=nullptr;
  }
  
  TStr::TStr(const TChA& ChA): Inner(nullptr) {
      if (!ChA.Empty()) {
          Inner = new char[ChA.Len()+1];
          strcpy(Inner, ChA.CStr());
      }
  }
  
  TStr::TStr(const TMem& Mem): Inner(nullptr) {
      if (!Mem.Empty()) {
  		const int Len = Mem.Len();
  
          Inner = new char[Len + 1];
          memcpy(Inner, Mem(), Len);
  		Inner[Len] = 0;
      }
  }
  
  TStr::TStr(const TSStr& SStr): Inner(nullptr) {
  	if (!SStr.Empty()) {
  		Inner = new char[SStr.Len()+1];
  		strcpy(Inner, SStr.CStr());
  	}
  }
  
  TStr::TStr(const PSIn& SIn): Inner(nullptr) {
  	const int SInLen = SIn->Len();
  	if (SInLen > 0) {
  		Inner = new char[SInLen + 1];
  		SIn->GetBf(Inner, SInLen);
  		Inner[SInLen] = 0;
  	}
  }
  
  TStr::TStr(TSIn& SIn, const bool& IsSmall): Inner(nullptr) {
  	if (IsSmall) {
  		if (SIn.PeekCh() == 0) {
              EAssert(SIn.GetCh() == 0);
          } else {
  			SIn.Load(Inner);
  		}
  	} else {
  		int BfL; SIn.Load(BfL);
  		if (BfL == 0) {
              EAssert(SIn.GetCh() == 0);
          } else {
  			SIn.Load(Inner, BfL, BfL);
  		}
  	}
      // delete buffer and replace with null in case it's ""
      if (Inner != nullptr && Inner[0] == 0) {
  		delete[] Inner;
          Inner = nullptr;
      }
  }
  
  void TStr::Load(TSIn& SIn, const bool& IsSmall) {
      *this = TStr(SIn, IsSmall);
  }
  
  void TStr::Save(TSOut& SOut, const bool& IsSmall) const {
      if (IsSmall){ SOut.Save(CStr()); }
      else { const int BfL = Len(); SOut.Save(BfL); SOut.Save(CStr(), BfL); }
  }
  
  void TStr::LoadXml(const PXmlTok& XmlTok, const TStr& Nm){
    XLoadHd(Nm);
    TStr TokStr=XmlTok->GetTokStr(false);
    operator=(TokStr);
  }
  
  void TStr::SaveXml(TSOut& SOut, const TStr& Nm) const {
    TStr XmlStr=TXmlLx::GetXmlStrFromPlainStr(*this);
    if (XmlStr.Empty()){XSaveBETag(Nm);}
    else {XSaveHd(Nm); SOut.PutStr(XmlStr);}
  }
  
  TStr& TStr::operator=(const TStr& Str) {
  	if (this != &Str) {
  		Clr();
  		if (!Str.Empty()) {
  			Inner = Str.CloneCStr();
  		}
  	}
      return *this;
  }
  
  TStr& TStr::operator=(TStr&& Str) {
  	if (this != &Str) {
  		std::swap(Inner, Str.Inner);
  		Str.Clr();
  	}
      return *this;
  }
  
  TStr& TStr::operator=(const TChA& ChA) {
  	Clr();
  	if (!ChA.Empty()) {
  		Inner = new char[ChA.Len() + 1];
  		strcpy(Inner, ChA.CStr());
  	}
      return *this;
  }
  
  TStr& TStr::operator=(const char* CStr) {
  	Clr();
  	const int StrLen = (int)strlen(CStr);
  	if (StrLen > 0) {
  		Inner = new char[StrLen+1];
  		strcpy(Inner, CStr);
  	}
  	return *this;
  }
  
  bool TStr::operator==(const char* _CStr) const {
  	if (_CStr == nullptr) { return false; }
  	return (CStr() == _CStr) || (strcmp(CStr(), _CStr) == 0);
  }
  
  bool TStr::operator<(const TStr& Str) const {
      return strcmp(CStr(), Str.CStr()) < 0;
  }
  
  char& TStr::operator[](const int& ChN) {
  	Assert( (0 <= ChN) && (ChN < Len()) );
  	return Inner[ChN];
  }
  
  void TStr::PutCh(const int& ChN, const char& Ch) {
      Assert((0<=ChN)&&(ChN<Len()));
      Inner[ChN] = Ch;
  }
  
  char TStr::GetCh(const int& ChN) const {
      // Assert index not negative, index not >= Length
      Assert( (0 <= ChN) && (ChN < Len()) );
      return Inner[ChN];
  }
  
  char* TStr::CloneCStr() const {
  	const int Length = Len();
  	char* Bf = new char[Length+1];
  	if (Length > 0) {
  		strcpy(Bf, Inner);
  	} else {
  		Bf[0] = 0;
  	}
  	return Bf;
  }
  
  bool TStr::Empty() const {
  	 AssertR(Inner == nullptr || Inner[0] != 0, "TStr::Empty string is not nullptr. Fix immediately!");
  	 return  Inner == nullptr;
  }
  
  void TStr::Clr() {
  	if (Inner != nullptr) {
  		delete[] Inner;
  		Inner = nullptr;
  	}
  }
  
  int TStr::GetMemUsed() const {
      return int(sizeof(TStr) + (Empty() ? 0 : (Len() + 1)));
  }
  
  int TStr::CmpI(const char* p, const char* r) {
  	if (!p){ return r ? (*r ? -1 : 0) : 0; }
  	if (!r){ return (*p ? 1 : 0); }
  	while (*p && *r){
  		int i = int(toupper(*p++)) - int(toupper(*r++));
  		if (i != 0){ return i; }
  	}
  	return int(toupper(*p++)) - int(toupper(*r++));
  }
  
  bool TStr::IsUc() const {
  	const int StrLen = Len();
  	for (int ChN = 0; ChN<StrLen; ChN++){
  		if (('a' <= Inner[ChN]) && (Inner[ChN] <= 'z')){ return false; }
  	}
  	return true;
  }
  
  TStr& TStr::ToUc() {
  	const int StrLen = Len();
  	for (int ChN = 0; ChN<StrLen; ChN++){
          Inner[ChN] = toupper(Inner[ChN]);
  	}
  	return *this;
  }
  
  TStr TStr::GetUc() const {
      return TStr(*this).ToUc();
  }
  
  bool TStr::IsLc() const {
  	const int StrLen = Len();
  	for (int ChN = 0; ChN<StrLen; ChN++){
  		if (('A' <= Inner[ChN]) && (Inner[ChN] <= 'Z')){ return false; }
  	}
  	return true;
  }
  
  TStr& TStr::ToLc() {
  	const int StrLen = Len();
  	for (int ChN = 0; ChN<StrLen; ChN++){
          Inner[ChN] = tolower(Inner[ChN]);
  	}
  	return *this;
  }
  
  TStr TStr::GetLc() const {
      return TStr(*this).ToLc();
  }
  
  TStr& TStr::ToCap() {
  	if (Empty()) { return *this; }
  	const int StrLen = Len();
  	// copy first char in uppercase
  	Inner[0] = (char)toupper(Inner[0]);
  	// copy all other chars in lowercase
  	for (int ChN = 1; ChN < StrLen; ChN++){
  		Inner[ChN] = (char)tolower(Inner[ChN]);
  	}
  	return *this;
  }
  
  TStr TStr::GetCap() const{
  	return TStr(*this).ToCap();
  }
  
  TStr& TStr::ToTrunc() {
      *this = GetTrunc();
      return *this;
  }
  
  TStr TStr::GetTrunc() const {
    int ThisLen = Len();
  
    int BChN = 0;
    int EChN = ThisLen - 1;
  
    while ((BChN < ThisLen) && TCh::IsWs(GetCh(BChN))) { BChN++; }
    while ((EChN >= 0) && TCh::IsWs(GetCh(EChN))){ EChN--; }
    if (BChN > EChN) { return TStr(); }
    return GetSubStr(BChN, EChN);
  }
  
  TStr& TStr::ToHex() {
      *this = GetHex();
      return *this;
  }
  
  TStr TStr::GetHex() const {
    TChA ChA;
    int StrLen=Len();
    for (int ChN=0; ChN<StrLen; ChN++){
      uchar Ch=uchar(GetCh(ChN));
      char MshCh=TCh::GetHexCh((Ch/16)%16);
      char LshCh=TCh::GetHexCh(Ch%16);
      ChA+=MshCh; ChA+=LshCh;
    }
    return TStr(ChA);
  }
  
  
  TStr& TStr::FromHex() {
      *this = GetFromHex();
      return *this;
  }
  
  TStr TStr::GetFromHex() const {
    int StrLen=Len(); IAssert(StrLen%2==0);
    TChA ChA; int ChN=0;
    while (ChN<StrLen){
  	char MshCh = GetCh(ChN); ChN++;
  	char LshCh = GetCh(ChN); ChN++;
      uchar Ch=uchar(TCh::GetHex(MshCh)*16+TCh::GetHex(LshCh));
  	EAssertR(Ch != 0, "TStr::GetFromHex null terminator found! ");
      ChA+=Ch;
    }
    return TStr(ChA);
  }
  
  TStr TStr::GetSubStr(const int& BChN, const int& EChN) const {
  	int StrLen = Len();
  	EAssertR(0 <= BChN && BChN <= EChN && EChN < StrLen, "TStr::GetSubStr index out of bounds");
  	int Chs = EChN - BChN + 1;
  	// initialize accordingly
  	char* Bf = nullptr;
  	if (Chs <= 0) {
  		// create empty string
  		return TStr();
  	}
  	else if (Chs == StrLen) {
  		// keep copy of everything
  		Bf = CloneCStr();//
  	}
  	else {
  		// get copy of a substring
  		Bf = new char[Chs + 1]; strncpy(Bf, CStr() + BChN, Chs); Bf[Chs] = 0;
  	}
  	return WrapCStr(Bf);
  }
  
  // safe version of GetSubStr().
  // Fixes BChN and EChN values that are outside of string's range
  // supports also negative indices (python like):
  // GetSubStrSafe(0,-1) will return all but last char
  TStr TStr::GetSubStrSafe(const int& BChN, const int& EChN) const {
  	int StrLen = Len();
  	int StartN;
  	if (BChN <= -StrLen)
  		StartN = 0;
  	else if (BChN < 0)
  		StartN = StrLen + BChN;
  	else
  		StartN = BChN;
  	int EndN;
  	if (EChN < 0)
  		EndN = StrLen + EChN - 1;
  	else
  		EndN = EChN >= StrLen ? StrLen - 1 : EChN;
  
  	if (!(0 <= StartN && StartN <= EndN && EndN < StrLen))
  		return TStr();
  	return GetSubStr(StartN, EndN);
  }
  
  void TStr::InsStr(const int& BChN, const TStr& Str) {
      const int ThisLen = Len();
      EAssert((0<=BChN)&&(BChN<=ThisLen));
      TChA ChA = TChA(ThisLen + Str.Len());
      if (BChN==0){
          ChA += Str;
          ChA += CStr();
      } else if (BChN==ThisLen){
          ChA += CStr();
          ChA += Str;
      } else {
          ChA += GetSubStr(0, BChN-1);
          ChA += Str;
          ChA += GetSubStr(BChN, ThisLen-1);
      }
      *this = ChA;
  }
  
  void TStr::DelChAll(const char& DelCh) {
      const int ThisLen = Len();
      TChA ChA = TChA(ThisLen);
      for (int ChN = 0; ChN < ThisLen; ChN++) {
          const char Ch = GetCh(ChN);
          if (Ch != DelCh) { ChA += Ch; }
      }
      *this = ChA;
  }
  
  void TStr::DelSubStr(const int& BChN, const int& EChN) {
  	int StrLen = Len();
  	EAssertR(0 <= BChN && BChN <= EChN && EChN < StrLen, "TStr::DelSubStr index out of bounds");
  
      int Chs = Len() - (EChN - BChN + 1);
      if (Chs == 0) {
          // nothing left after delete, clear it all
          Clr();
      } else if (Chs < Len()) {
          // actual substring
          char* Bf=new char[Chs + 1];
          // copy before
          strncpy(Bf, CStr(), BChN);
          // copy after
          strncpy(Bf+BChN, CStr()+EChN+1, Len()-EChN-1);
          // we are done, just replace current
          Bf[Chs]=0;
          *this = WrapCStr(Bf);
      }
  }
  
  bool TStr::DelStr(const TStr& Str) {
  	int ChN = SearchStr(Str);
  	if (ChN != -1){
  		DelSubStr(ChN, ChN + Str.Len() - 1);
          return true;
  	}
      return false;
  }
  
  int TStr::DelStrAll(const TStr& Str) {
  	return ChangeStrAll(Str, "");
  }
  
  TStr TStr::LeftOf(const char& SplitCh) const {
    int ThisLen=Len(); const char* ThisBf=CStr();
    int ChN=0;
    while ((ChN<ThisLen)&&(ThisBf[ChN]!=SplitCh)){ChN++;}
    return (ChN == 0 || ChN==ThisLen) ? "" : GetSubStr(0, ChN-1);
  }
  
  TStr TStr::LeftOfLast(const char& SplitCh) const {
    const char* ThisBf=CStr();
    int ChN=Len()-1;
    while ((ChN>=0)&&(ThisBf[ChN]!=SplitCh)){ChN--;}
    return (ChN == 0 || ChN == -1) ? "" : GetSubStr(0, ChN - 1);
  }
  
  TStr TStr::RightOf(const char& SplitCh) const {
    int ThisLen=Len(); const char* ThisBf=CStr();
    int ChN=0;
    while ((ChN<ThisLen)&&(ThisBf[ChN]!=SplitCh)){ChN++;}
    return (ChN>=ThisLen - 1) ? "" : GetSubStr(ChN+1, ThisLen-1);
  }
  
  TStr TStr::RightOfLast(const char& SplitCh) const {
    int ThisLen=Len(); const char* ThisBf=CStr();
    int ChN=Len()-1;
    while ((ChN>=0)&&(ThisBf[ChN]!=SplitCh)){ChN--;}
    return (ChN == ThisLen -1 || ChN==-1) ? "" : GetSubStr(ChN+1, ThisLen-1);
  }
  
  void TStr::SplitLeftOfRightOf(TStr& LStr, const int& LeftOfChN, const int& RightOfChN, TStr& RStr) const {
  	const int StrLen = Len();
  
  	EAssertR(LeftOfChN >= 0 && LeftOfChN <= RightOfChN && RightOfChN < StrLen, "Splitting index should be greater than 0 and less than length!");
  
  	// clear the left and right strings
  	LStr.Clr();
  	RStr.Clr();
  
  	if (Empty()) { return; }
  
  	const char* InnerPt = CStr();
  
  	const int LeftLen = LeftOfChN;
  	const int RightLen = StrLen - RightOfChN - 1;
  
  	// copy into left and right
  	// if the length of any of the strings is 0 than leave it empty
  	if (LeftLen > 0) {
  		LStr.Inner = new char[LeftLen + 1];
  		memcpy(LStr.Inner, InnerPt, LeftLen);
  		LStr.Inner[LeftLen] = 0;
  	}
  	if (RightLen > 0) {
  		RStr.Inner = new char[RightLen + 1];
  		memcpy(RStr.Inner, InnerPt + RightOfChN + 1, RightLen);
  		RStr.Inner[RightLen] = 0;
  	}
  }
  
  void TStr::SplitOnChN(TStr& LStr, const int& ChN, TStr& RStr) const {
  	SplitLeftOfRightOf(LStr, ChN, ChN, RStr);
  }
  
  void TStr::SplitOnCh(TStr& LStr, const char& SplitCh, TStr& RStr) const {
  	LStr.Clr();
  	RStr.Clr();
  
  	// check if the string is empty
  	if (Empty()) { return; }
  
  	const char* InnerPt = CStr();
  
  	// find the pointer to the delimiter
  	const char* ChPtr = strchr(InnerPt, SplitCh);
  
  	// if the character was not found than return this in the left string
  	if (ChPtr == nullptr) {
  		LStr = *this;
  		return;
  	}
  
  	// split
  	SplitOnChN(LStr, (int)(ChPtr - InnerPt), RStr);
  }
  
  void TStr::SplitOnStr(TStr& LStr, const TStr& SplitStr, TStr& RStr) const {
  	LStr.Clr();
  	RStr.Clr();
  
  	// check if the string is empty
  	if (Empty()) { return; }
  
  	const char* InnerPt = CStr();
  
  	const char* MatchPt = strstr(InnerPt, SplitStr.CStr());
  
  	if (MatchPt == nullptr || SplitStr.Empty()) {
  		LStr = *this;
  		return;
  	}
  
  	const int MatchIdx = (int)(MatchPt - InnerPt);
  
  	SplitLeftOfRightOf(LStr, MatchIdx, MatchIdx + SplitStr.Len() - 1, RStr);
  }
  
  void TStr::SplitOnLastCh(TStr& LStr, const char& SplitCh, TStr& RStr) const {
  	LStr.Clr();
  	RStr.Clr();
  	// check if the string is empty
  	if (Empty()) { return; }
  
  	const char* InnerPt = CStr();
  
  	// find the pointer to the delimiter
  	const char* ChPtr = strrchr(InnerPt, SplitCh);
  
  	// if the character was not found than return this in the right string
  	if (ChPtr == nullptr) {
  		RStr = *this;
  		return;
  	}
  
  	// split
  	SplitOnChN(LStr, (int)(ChPtr - InnerPt), RStr);
  }
  
  void TStr::SplitOnAllCh(const char& SplitCh, TStrV& StrV, const bool& SkipEmpty) const {
    StrV.Clr();
    char* Bf=new char[Len()+1];
    strcpy(Bf, CStr());
    char* CurStrBf=Bf;
    forever{
      char* BfC=CurStrBf;
      while ((*BfC!=0)&&(*BfC!=SplitCh)){BfC++;}
      bool IsEnd=(*BfC=='\0');
      *BfC=0;
      if ((BfC>CurStrBf)||(!SkipEmpty)){StrV.Add(TStr(CurStrBf));}
      if (IsEnd){break;}
      CurStrBf=BfC+1;
    }
    delete[] Bf;
  }
  
  void TStr::SplitOnAllAnyCh(
   const TStr& SplitChStr, TStrV& StrV, const bool& SkipEmpty) const {
    // reset string output-vector
    StrV.Clr();
    // prepare working-copy of string
    char* Bf=new char[Len()+1];
    strcpy(Bf, CStr());
    char* CurStrBf=Bf; // pointer to current string
    // prepare pointer to split-char-string
    const char* SplitChBf=SplitChStr.CStr();
    forever{
      char* BfC=CurStrBf; // set the counter for working-string
      while (*BfC!=0){
        const char* SplitChBfC=SplitChBf; // set counter for split-char-string
        while ((*SplitChBfC!=0)&&(*SplitChBfC!=*BfC)){SplitChBfC++;}
        if (*SplitChBfC!=0){break;} // if split-char found
        BfC++;
      }
      bool IsEnd=(*BfC==0);
      *BfC=0;
      if ((BfC>CurStrBf)||(!SkipEmpty)){StrV.Add(TStr(CurStrBf));}
      if (IsEnd){break;}
      CurStrBf=BfC+1;
    }
    // delete working-copy
    delete[] Bf;
  }
  
  void TStr::SplitOnWs(TStrV& StrV) const {
    StrV.Clr();
    char* Bf=new char[Len()+1];
    strcpy(Bf, CStr());
    char* StrBf=Bf;
    forever{
      while ((*StrBf!=0)&&(TCh::IsWs(*StrBf))){StrBf++;}
      char* BfC=StrBf;
      while ((*BfC!=0)&&(!TCh::IsWs(*BfC))){BfC++;}
      bool IsEnd=(*BfC=='\0');
      *BfC=0;
      if (BfC>StrBf){StrV.Add(TStr(StrBf));}
      if (IsEnd){break;}
      StrBf=BfC+1;
    }
    delete[] Bf;
  }
  
  void TStr::SplitOnNonAlNum(TStrV& StrV) const {
    StrV.Clr();
    char* Bf=new char[Len()+1];
    strcpy(Bf, CStr());
    char* StrBf=Bf;
    forever{
      while ((*StrBf!=0)&&(!TCh::IsAlNum(*StrBf))){StrBf++;}
      char* BfC=StrBf;
      while ((*BfC!=0)&&(TCh::IsAlNum(*BfC))){BfC++;}
      bool IsEnd=(*BfC=='\0');
      *BfC=0;
      if (BfC>StrBf){StrV.Add(TStr(StrBf));}
      if (IsEnd){break;}
      StrBf=BfC+1;
    }
    delete[] Bf;
  }
  
  void TStr::SplitOnStr(const TStr& SplitStr, TStrV& StrV) const {
    StrV.Clr();
    int SplitStrLen=SplitStr.Len();
    int PrevChN=0; int ChN=0;
    int StrLen = Len();
    // on empty string we need to end here, otherwise the GetSubStr crashes
    if (StrLen == 0) { return; }
    while (ChN < StrLen && (ChN = SearchStr(SplitStr, ChN)) != -1){
      // extract & add string
  	  if (ChN - 1 >= 0 && ChN - 1 < StrLen) {
  		  TStr SubStr = GetSubStr(PrevChN, ChN - 1);
  		  StrV.Add(SubStr);
  	  }
  	PrevChN = ChN;
  	ChN += SplitStrLen;
    }
    // add last string
    TStr LastSubStr=GetSubStr(PrevChN, StrLen-1);
    StrV.Add(LastSubStr);
  }
  
  TStr TStr::Left(const int& EChN) const {
  	if (EChN == 0) { return ""; }
  	return EChN>=0 ? GetSubStr(0, EChN-1) : GetSubStr(0, Len()+EChN-1);
  }
  
  int TStr::CountCh(const char& Ch, const int& BChN) const {
  	const int ThisLen = Len();
  	if(ThisLen == 0) { return 0; }
  	EAssertR(BChN >= 0 && BChN < ThisLen, "TStr::CountCh index BChN out of bounds!");
  
  	const char* ThisBf = CStr();
  	int Chs = 0;
  
  	for (int ChN = BChN; ChN<ThisLen; ChN++) {
      if (ThisBf[ChN]==Ch) { Chs++; }
    }
    return Chs;
  }
  
  int TStr::SearchCh(const char& Ch, const int& BChN) const {
    int ThisLen = Len();
    if(ThisLen == 0) { return -1; }
    EAssertR(BChN >= 0 && BChN < ThisLen, "TStr::SearchCh index BChN out of bounds!");
  
    const char* ThisBf = CStr();
    int ChN = BChN;
  
    while (ChN < ThisLen){
      if (ThisBf[ChN] == Ch){ return ChN; }
      ChN++;
    }
    return -1;
  }
  
  int TStr::SearchChBack(const char& Ch, int BChN) const {
    const int StrLen = Len();
    if (StrLen == 0) { return 0; }
    const char* ThisBf = CStr();
  
    if (BChN < 0 || BChN >= StrLen) { BChN = StrLen - 1; }
  
    const char* Pt = ThisBf + BChN;
  
    while (Pt >= ThisBf) {
      if (*Pt == Ch){ return (int) (Pt - ThisBf); }
      Pt--;
    }
    return -1;
  }
  
  int TStr::SearchStr(const TStr& Str, const int& BChN) const {
    const int ThisLen = Len();
    EAssertR(BChN >= 0 && BChN <= ThisLen, "TStr::SearchStr: index BChN out of bounds!");
  
    // special case for handling empty strings
    if (ThisLen == 0) { return Str.Empty() ? 0 : -1; }
  
    const int NrBChN = BChN;
    const char* StrPt = strstr((const char*) CStr() + NrBChN, Str.CStr());
    if (StrPt == nullptr) { return -1; }
    return int(StrPt - CStr());
  }
  
  bool TStr::StartsWith(const char *Str) const {
  	const int OtherLen = (int)strlen(Str);
  	const int ThisLen = Len();
  	if (OtherLen > ThisLen || ThisLen == 0) { return false; }
  
  	int Cmp = strncmp(Str, CStr(), OtherLen);
  	return Cmp == 0;
  }
  
  bool TStr::EndsWith(const char *Str) const {
  	const int OtherLen = (int)strlen(Str);
  	const int ThisLen = Len();
  	if (OtherLen > ThisLen) {
  		// too long to be a suffix anyway
  		return false;
  	} else {
  		// move to the point in the buffer where we would expect the suffix to be
  		const char *ending = CStr() + ThisLen - OtherLen;
  		int cmp = strncmp(Str, ending, OtherLen);
  		return cmp == 0;
  	}
  }
  
  int TStr::ChangeCh(const char& SrcCh, const char& DstCh, const int& BChN) {
  	AssertR(SrcCh != '\0' && DstCh != '\0', "TStr::ChangeCh: source and destination character should not be the terminator char!");
  	int ChN = SearchCh(SrcCh, BChN);
  	if (ChN != -1) { PutCh(ChN, DstCh); }
  	return ChN;
  }
  
  int TStr::ChangeChAll(const char& SrcCh, const char& DstCh) {
  	if (Empty()) { return 0; }
  	int FirstChN = SearchCh(SrcCh);
  	if (FirstChN == -1){
  		return 0;
  	} else {
  		const int StrLen = Len(); int Changes = 0;
  		for (int ChN = FirstChN; ChN < StrLen; ChN++){
              if (GetCh(ChN) == SrcCh) { PutCh(ChN, DstCh); Changes++; }
  		}
  		return Changes;
  	}
  }
  
  int TStr::ChangeStr(const TStr& SrcStr, const TStr& DstStr, const int& BChN) {
  	if (Empty() && BChN == 0) { return -1; }
  	const int ChN = SearchStr(SrcStr, BChN);
  	if (ChN != -1){
          DelSubStr(ChN, ChN + SrcStr.Len() - 1);
          InsStr(ChN, DstStr);
  	}
      return ChN;
  }
  
  int TStr::ChangeStrAll(const TStr& SrcStr, const TStr& DstStr) {
  	if (Empty() || SrcStr.Empty()) { return 0; }
  
  	const int Length = Len();
  	const int SrcLen = SrcStr.Len();
  	const int DstLen = DstStr.Len();
  
  	const char* InnerPt = CStr();
  	const char* SrcCStr = SrcStr.CStr();
  	const char* DstCStr = DstStr.CStr();
  
  	// find how many times SrcStr appears in this string
  	const char* CurrPos = Inner;
  	const char* NextHit;
  
  	int NMatches = 0;
  	while ((NextHit = strstr(CurrPos, SrcCStr)) != nullptr) {
  		NMatches++;
  		CurrPos = NextHit + SrcLen;
  	}
  
  	// create a new string
  	char* ResStr = new char[Length + NMatches*(DstLen - SrcLen) + 1];
  
  	// iterate through the string, instead of copying source copy destination
  	int i = 0;	// index in the source string
  	int j = 0;	// index in the result string
  
  	int SeqLen;
  
  	// find next hit, copy everything between the current position and the hit,
  	// then copy the dest string
  	while ((NextHit = strstr(InnerPt + i, SrcCStr)) != nullptr) {
  		SeqLen = (int)(NextHit - (InnerPt + i));
  		// copy the chars in between the hits
  		memcpy(ResStr + j, InnerPt + i, SeqLen);
  
  		// increase positions
  		i += SeqLen;
  		j += SeqLen;
  
  		// copy the dst string
  		memcpy(ResStr + j, DstCStr, DstLen);
  
  		// increase positions
  		i += SrcLen;
  		j += DstLen;
  	}
  
  	// copy what is after the last match
  	memcpy(ResStr + j, InnerPt + i, Length - i);
  
  	// insert null character
  	ResStr[Length + NMatches*(DstLen - SrcLen)] = 0;
  	// replace with the new string
  	*this = WrapCStr(ResStr);
      // return number of changes
      return NMatches;
  }
  
  TStr TStr::Reverse() const {
  	const int ThisLen = Len();
      // reserve place for reverse
  	char* Reversed = new char[ThisLen+1];
      // do the reversing
      for (int ChN = 0; ChN < ThisLen; ChN++) {
  		Reversed[ChN] = Inner[ThisLen - ChN - 1];
  	}
      // finish
  	Reversed[ThisLen] = 0;
      // create new string from it
  	return WrapCStr(Reversed);
  }
  
  int TStr::GetPrimHashCd() const {
      return TStrHashF_DJB::GetPrimHashCd(CStr());
  }
  
  int TStr::GetSecHashCd() const {
      return TStrHashF_DJB::GetSecHashCd(CStr());
  }
  
  int TStr::GetHashTrick() const {
      return TStrHashF_Murmur3::GetPrimHashCd(CStr());
  }
  
  bool TStr::IsBool(bool& Val) const {
    if (operator==("T")){Val=true; return true;}
    else if (operator==("F")){Val=false; return true;}
    else {return false;}
  }
  
  bool TStr::IsInt(const bool& Check, const int& MnVal, const int& MxVal, int& Val) const {
  	int64 _Val;
  	// assign and check for overflow
  	return IsInt64(Check, MnVal, MxVal, _Val) && (int64)(Val = (int)_Val) == _Val;
  }
  
  bool TStr::IsUInt(const bool& Check, const uint& MnVal, const uint& MxVal, uint& Val) const {
  	TChRet Ch(TStrIn::New(*this, false));
  	Ch.GetCh();
  	return IsUInt(Ch, Check, MnVal, MxVal, Val);
  }
  
  bool TStr::IsHexInt( const bool& Check, const int& MnVal, const int& MxVal, int& Val) const {
    // parsing format {ws} [+/-][0x] +{XXX}
    int _Val=0;
    bool Minus=false;
    TChRet Ch(TStrIn::New(*this, false));
    while (TCh::IsWs(Ch.GetCh())){}
    if (Ch()=='+'){Minus=false; Ch.GetCh();}
    if (Ch()=='-'){Minus=true; Ch.GetCh();}
    if (Ch()=='0'){
      Ch.GetCh();
      if (tolower(Ch())=='x' ){
        Ch.GetCh(); if (Ch.Eof()){return false;}
      }
    }
    if (!Ch.Eof() && !TCh::IsHex(Ch())){return false;}
    if (!Ch.Eof()) _Val = TCh::GetHex(Ch());
    while (TCh::IsHex(Ch.GetCh())){_Val=16*_Val+TCh::GetHex(Ch());}
    if (Minus){_Val=-_Val;}
    if (Check&&((_Val<MnVal)||(_Val>MxVal))){return false;}
    if (Ch.Eof()){Val=_Val; return true;} else {return false;}
  }
  
  bool TStr::IsInt64(const bool& Check, const int64& MnVal, const int64& MxVal, int64& Val) const {
  	EAssert(!Check || (Check && MnVal <= MxVal));
  	// parsing format {ws} [+/-] +{ddd}
  	TChRet Ch(TStrIn::New(*this, false));
  
  	while (TCh::IsWs(Ch.GetCh())) {}
  
  	bool IsInt;
  	uint64 UIntVal;
  
  	if (Ch() == '-') {
  		Ch.GetCh();
  		IsInt = IsUInt64(Ch, true, 0, 9223372036854775808ul, UIntVal);
  		Val = -1l * UIntVal;
  	} else {
  		if (Ch() == '+') { Ch.GetCh(); }
  		IsInt = IsUInt64(Ch, true, 0, 9223372036854775807ul, UIntVal);
  		Val = UIntVal;
  	}
  
  	// check
  	if (!IsInt) { return false; }
  	if (Check && (Val < MnVal || Val > MxVal)) { return false; }
  
  	return true;
  }
  
  bool TStr::IsUInt64(const bool& Check, const uint64& MnVal, const uint64& MxVal, uint64& Val) const {
  	TChRet Ch(TStrIn::New(*this, false));
  	Ch.GetCh();
  	return IsUInt64(Ch, Check, MnVal, MxVal, Val);
  }
  
  bool TStr::IsHexInt64(
   const bool& Check, const int64& MnVal, const int64& MxVal, int64& Val) const {
    // parsing format {ws} [+/-][0x] +{XXX}
    int64 _Val=0;
    bool Minus=false;
    TChRet Ch(TStrIn::New(*this, false));
    while (TCh::IsWs(Ch.GetCh())){}
    if (Ch()=='+'){Minus=false; Ch.GetCh();}
    if (Ch()=='-'){Minus=true; Ch.GetCh();}
    if (Ch()=='0'){
      Ch.GetCh();
      if (tolower(Ch())=='x' ){
        Ch.GetCh(); if (Ch.Eof()){return false;}
      }
    }
    if (!Ch.Eof()) _Val=TCh::GetHex(Ch());
    while (TCh::IsHex(Ch.GetCh())){_Val=16*_Val+TCh::GetHex(Ch());}
    if (Minus){_Val=-_Val;}
    if (Check&&((_Val<MnVal)||(_Val>MxVal))){return false;}
    if (Ch.Eof()){Val=_Val; return true;} else {return false;}
  }
  
  bool TStr::IsFlt(const bool& Check, const double& MnVal, const double& MxVal,
   double& Val, const char& DecDelimCh) const {
    // parsing format {ws} [+/-] +{d} ([.]{d}) ([E|e] [+/-] +{d})
    TChRet Ch(TStrIn::New(*this, false));
    while (TCh::IsWs(Ch.GetCh())){}
    if ((Ch()=='+')||(Ch()=='-')){Ch.GetCh();}
    if (!TCh::IsNum(Ch())&&Ch()!=DecDelimCh){return false;}
    while (TCh::IsNum(Ch.GetCh())){}
    if (Ch()==DecDelimCh){
      Ch.GetCh();
      while (TCh::IsNum(Ch.GetCh())){}
    }
    if ((Ch()=='e')||(Ch()=='E')){
      Ch.GetCh();
      if ((Ch()=='+')||(Ch()=='-')){Ch.GetCh();}
      if (!TCh::IsNum(Ch())){return false;}
      while (TCh::IsNum(Ch.GetCh())){}
    }
    if (!Ch.Eof()){return false;}
    double _Val=atof(CStr());
    if (Check&&((_Val<MnVal)||(_Val>MxVal))){
      return false;
    } else {
      Val=_Val; return true;
    }
  }
  
  bool TStr::IsWord(const bool& WsPrefixP, const bool& FirstUcAllowedP) const {
    // parsing format {ws} (A-Z,a-z) *{A-Z,a-z,0-9}
    TChRet Ch(TStrIn::New(*this, false));
    if (WsPrefixP){while (TCh::IsWs(Ch.GetCh())){}}
    else {Ch.GetCh();}
    if (!TCh::IsAlpha(Ch())){return false;}
    else if (!FirstUcAllowedP&&(TCh::IsUc(Ch()))){return false;}
    while (TCh::IsAlNum(Ch.GetCh())){}
    if (!Ch.Eof()){return false;}
    return true;
  }
  
  bool TStr::IsWs() const {
    // if string is just a bunch of whitespace chars
    TChRet Ch(TStrIn::New(*this, false));
    while (TCh::IsWs(Ch.GetCh())){}
    return Ch.Eof();
  }
  
  bool TStr::IsWcMatch(
   const int& StrBChN, const TStr& WcStr, const int& WcStrBChN, TStrV& StarStrV,
   const char& StarCh, const char& QuestCh) const {
    const int StrLen=Len(), WcStrLen=WcStr.Len();
    int StrChN=StrBChN; int WcStrChN=WcStrBChN;
    while ((StrChN<StrLen)&&(WcStrChN<WcStrLen)){
      if ((WcStr[WcStrChN]==QuestCh)||(GetCh(StrChN)==WcStr[WcStrChN])){
        StrChN++; WcStrChN++;
      } else
      if (WcStr[WcStrChN]==StarCh){
        TChA StarChA; // string substituted by star character
        for (int AfterStrChN=StrChN; AfterStrChN<=StrLen; AfterStrChN++){
          if (AfterStrChN>StrChN){
            StarChA+=GetCh(AfterStrChN-1);}
          if (IsWcMatch(AfterStrChN, WcStr, WcStrChN+1, StarStrV, StarCh, QuestCh)){
            StarStrV.Add(StarChA); return true;
          }
        }
        return false;
      } else {
        return false;
      }
    }
    if (StrChN==StrLen){
      for (int AfterWcStrChN=WcStrChN; AfterWcStrChN<WcStrLen; AfterWcStrChN++){
        if (WcStr[AfterWcStrChN]!=StarCh){return false;}}
      return true;
    } else {
      return false;
    }
  }
  
  bool TStr::IsWcMatch(
   const TStr& WcStr, TStrV& StarStrV, const char& StarCh, const char& QuestCh) const {
    bool WcMatch=IsWcMatch(0, WcStr, 0, StarStrV, StarCh, QuestCh);
    if (WcMatch){
      StarStrV.Reverse();
      return true;
    } else {
      return false;
    }
  }
  
  bool TStr::IsWcMatch(
   const TStr& WcStr, const char& StarCh, const char& QuestCh) const {
    TStrV StarStrV;
    return IsWcMatch(0, WcStr, 0, StarStrV, StarCh, QuestCh);
  }
  
  bool TStr::IsWcMatch(const TStr& WcStr, const int& StarStrN, TStr& StarStr) const {
    TStrV StarStrV;
    if (IsWcMatch(WcStr, StarStrV)){
      if (StarStrV.Len()>StarStrN){
        StarStr=StarStrV[StarStrV.Len()-StarStrN-1];
      } else {
        StarStr="";
      }
      return true;
    } else {
      return false;
    }
  }
  
  bool TStr::IsWcMatch(const TStr& WcStr) const {
    TStrV StarStrV;
    return IsWcMatch(0, WcStr, 0, StarStrV);
  }
  
  TStr TStr::GetWcMatch(const TStr& WcStr, const int& StarStrN) const {
    TStrV StarStrV;
    if (IsWcMatch(WcStr, StarStrV)&&(StarStrV.Len()>=StarStrN)){
      IAssert(StarStrN>=0);
      return StarStrV[StarStrV.Len()-StarStrN-1];
    } else {
      return "";
    }
  }
  
  TStr TStr::GetFPath() const {
    const int ThisLen=Len(); const char* ThisBf=CStr();
    int ChN=ThisLen-1;
    while ((ChN>=0)&&(ThisBf[ChN]!='/')&&(ThisBf[ChN]!='\\')){ChN--;}
    return GetSubStr(0, ChN);
  }
  
  TStr TStr::GetFBase() const {
    const int ThisLen=Len(); const char* ThisBf=CStr();
    int ChN=ThisLen-1;
    while ((ChN>=0)&&(ThisBf[ChN]!='/')&&(ThisBf[ChN]!='\\')){ChN--;}
    if (ChN + 1 > ThisLen - 1) { return TStr(); }
    return GetSubStr(ChN+1, ThisLen - 1);
  }
  
  TStr TStr::GetFMid() const {
    int ThisLen=Len(); const char* ThisBf=CStr();
    int ChN=ThisLen-1;
    while ((ChN>=0)&&(ThisBf[ChN]!='/')&&(ThisBf[ChN]!='\\')&&(ThisBf[ChN]!='.')){
      ChN--;}
    if (ChN<0){
      return *this;
    } else {
      if (ThisBf[ChN]=='.'){
        int EChN= --ChN;
        while ((ChN>=0)&&(ThisBf[ChN]!='/')&&(ThisBf[ChN]!='\\')){ChN--;}
  	  if (ChN + 1 > EChN) { return TStr(); }
        return GetSubStr(ChN+1, EChN);
      } else {
  		if (ChN + 1 > ThisLen - 1) { return TStr(); }
        return GetSubStr(ChN+1, ThisLen - 1);
      }
    }
  }
  
  TStr TStr::GetFExt() const {
    int ThisLen=Len(); const char* ThisBf=CStr();
    int ChN=ThisLen-1;
    while ((ChN>=0)&&(ThisBf[ChN]!='/')&&(ThisBf[ChN]!='\\')&&
     (ThisBf[ChN]!='.')){ChN--;}
    if ((ChN>=0)&&(ThisBf[ChN]=='.')){
  	  if (ChN > Len() - 1) { return TStr(); }
  	  return GetSubStr(ChN, Len()-1);
    } else {return TStr();}
  }
  
  TStr TStr::GetNrFPath(const TStr& FPath){
    TChA NrFPath(FPath.Len()+4); NrFPath+=FPath;
    NrFPath.ChangeCh('\\', '/');
    if (NrFPath.Empty()){NrFPath="./";}
    if ((NrFPath.Len()>=2)&&isalpha(NrFPath[0])&&(NrFPath[1]==':')){
      if (NrFPath.Len()==2){NrFPath+="./";}
      if ((NrFPath[2]!='.')&&(NrFPath[2]!='/')){NrFPath.Ins(2, "./");}
      if (NrFPath[NrFPath.Len()-1]!='/'){NrFPath+="/";}
    } else {
      if ((NrFPath[0]!='.')&&(NrFPath[0]!='/')){NrFPath.Ins(0, "./");}
      if (NrFPath[NrFPath.Len()-1]!='/'){NrFPath+="/";}
    }
    return NrFPath;
  }
  
  TStr TStr::GetNrFMid(const TStr& FMid){
    TChA NrFMid;
    int FMidLen=FMid.Len();
    for (int ChN=0; ChN<FMidLen; ChN++){
      char Ch=FMid[ChN];
      if (TCh::IsAlNum(Ch)){NrFMid+=Ch;} else {NrFMid+='_';}
    }
    return NrFMid;
  }
  
  TStr TStr::GetNrFExt(const TStr& FExt){
    if (FExt.Empty()||(FExt[0]=='.')){return FExt;}
    else {return TStr(".")+FExt;}
  }
  
  TStr TStr::GetNrNumFExt(const int& FExtN, const int& MinLen){
    TStr FExtNStr=TInt::GetStr(FExtN);
    while (FExtNStr.Len()<MinLen){
      FExtNStr=TStr("0")+FExtNStr;}
    return FExtNStr;
  }
  
  TStr TStr::GetNrFNm(const TStr& FNm){
    return GetNrFPath(FNm.GetFPath())+FNm.GetFMid()+GetNrFExt(FNm.GetFExt());
  }
  
  TStr TStr::GetNrAbsFPath(const TStr& FPath, const TStr& BaseFPath){
    TStr NrBaseFPath;
    if (BaseFPath.Empty()){
      NrBaseFPath=GetNrFPath(TDir::GetCurDir());
    } else {
      NrBaseFPath=GetNrFPath(BaseFPath);
    }
    IAssertR(IsAbsFPath(NrBaseFPath), NrBaseFPath);
    TStr NrFPath=GetNrFPath(FPath);
    TStr NrAbsFPath;
    if (IsAbsFPath(NrFPath)){
      NrAbsFPath=NrFPath;
    } else {
      NrAbsFPath=GetNrFPath(NrBaseFPath+NrFPath);
    }
    NrAbsFPath.ChangeStrAll("/./", "/");
    NrAbsFPath.ChangeStrAll("\\.\\", "\\");
    return NrAbsFPath;
  }
  
  bool TStr::IsAbsFPath(const TStr& FPath){
    if ((FPath.Len()>=3)&&isalpha(FPath[0])&&(FPath[1]==':')&&
     ((FPath[2]=='/')||(FPath[2]=='\\'))){
      return true;
    }
    if ((FPath.Len()>=2)&&(FPath[0]=='/')&&isalpha(FPath[1])){
      return true;
    }
    return false;
  }
  
  TStr TStr::PutFExt(const TStr& FNm, const TStr& FExt){
    return FNm.GetFPath()+FNm.GetFMid()+FExt;
  }
  
  TStr TStr::PutFExtIfEmpty(const TStr& FNm, const TStr& FExt){
    if (FNm.GetFExt().Empty()){
      return FNm.GetFPath()+FNm.GetFMid()+FExt;
    } else {
      return FNm;
    }
  }
  
  TStr TStr::PutFBase(const TStr& FNm, const TStr& FBase){
    return FNm.GetFPath()+FBase;
  }
  
  TStr TStr::PutFBaseIfEmpty(const TStr& FNm, const TStr& FBase){
    if (FNm.GetFBase().Empty()){
      return FNm.GetFPath()+FBase;
    } else {
      return FNm;
    }
  }
  
  TStr TStr::AddToFMid(const TStr& FNm, const TStr& ExtFMid){
    return FNm.GetFPath()+FNm.GetFMid()+ExtFMid+FNm.GetFExt();
  }
  
  TStr TStr::GetNumFNm(const TStr& FNm, const int& Num){
    return FNm.GetFPath()+FNm.GetFMid()+TInt::GetStr(Num, "%03d")+FNm.GetFExt();
  }
  
  TStr TStr::GetFNmStr(const TStr& Str, const bool& AlNumOnlyP){
    TChA FNm=Str;
    for (int ChN=0; ChN<FNm.Len(); ChN++){
      uchar Ch=FNm[ChN];
      if (AlNumOnlyP){
        if (
         (('0'<=Ch)&&(Ch<='9'))||
         (('A'<=Ch)&&(Ch<='Z'))||
         (('a'<=Ch)&&(Ch<='z'))||
         (Ch=='-')||(Ch=='_')){}
        else {Ch='_';}
      } else {
        if ((Ch<=' ')||(Ch=='/')||(Ch=='\\')||(Ch==':')||(Ch=='.')){
          Ch='_';}
      }
      FNm.PutCh(ChN, Ch);
    }
    return FNm;
  }
  
  TStr TStr::GetStr(const TStr& Str, const char* FmtStr){
    if (FmtStr==nullptr){
      return Str;
    } else {
      char Bf[1000];
      sprintf(Bf, FmtStr, Str.CStr());
      return TStr(Bf);
    }
  }
  
  TStr TStr::GetStr(const TStrV& StrV, const TStr& DelimiterStr){
    if (StrV.Empty()){return TStr();}
    TChA ResStr=StrV[0];
    for (int StrN=1; StrN<StrV.Len(); StrN++){
      ResStr+=DelimiterStr;
      ResStr+=StrV[StrN];
    }
    return ResStr;
  }
  
  TStr TStr::Fmt(const char *FmtStr, ...) {
      const int BfLen = 10 * 1024;
      char Bf[BfLen];
      va_list valist;
      va_start(valist, FmtStr);
      const int RequiredLen = vsnprintf(Bf, BfLen - 2, FmtStr, valist);
      va_end(valist);
      if (RequiredLen < 0) {
          // error
          return TStr();
      } else if (RequiredLen < BfLen - 2) {
          // fits in buffer
          return TStr(Bf);
      } else {
          // buffer was too short
          char* NewBf = new char[RequiredLen + 1];
          va_list valist;
          va_start(valist, FmtStr);
          const int RequiredLen2 = vsnprintf(NewBf, RequiredLen + 1, FmtStr, valist);
          va_end(valist);
          return WrapCStr(NewBf);
      }
  }
  
  TStr TStr::GetSpaceStr(const int& Spaces) {
      EAssert(Spaces >= 0);
      if (Spaces == 0) { return TStr(); }
      // we have more, go for it
  	char *NewBf = new char[Spaces + 1];
  	for (int SpaceN = 0; SpaceN < Spaces; SpaceN++) {
          NewBf[SpaceN] = ' ';
      }
  	NewBf[Spaces] = 0;
  	return WrapCStr(NewBf);
  }
  
  TStr operator+(const TStr& LStr, const char* RCStr) {
  	const size_t LeftLen = LStr.Len();
  	const size_t RightLen = ((RCStr == nullptr) ? 0 : strlen(RCStr));
  
  	// check if any of the strings are empty
  	if (LeftLen == 0) { return TStr(RCStr); }
  	else if (RightLen == 0) { return LStr; }
  	else {
  		const char* LCStr = LStr.CStr();
  
  		// allocate memory
  		char* ConcatStr = new char[LeftLen + RightLen + 1];
  
  		// copy the two strings into the new memory
  		memcpy(ConcatStr, LCStr, LeftLen);
  		memcpy(ConcatStr + LeftLen, RCStr, RightLen);
  
  		// finish the new string
  		ConcatStr[LeftLen + RightLen] = 0;
  
  		// return
  		return TStr::WrapCStr(ConcatStr);
  	}
  }
  
  TStr operator+(const TStr& LStr, const TStr& RStr) {
  	return operator+(LStr, RStr.CStr());
  }
  
  TStr operator+(const TStr& LStr, const char Ch) {
  	const size_t LeftLen = LStr.Len();
  	const size_t RightLen = 1;
  
  	// check if any of the strings are empty
  	if (LeftLen == 0) { return TStr(Ch); }
  	else if (RightLen == 0) { return LStr; }
  	else {
  		const char* LCStr = LStr.CStr();
  
  		// allocate memory
  		char* ConcatStr = new char[LeftLen + RightLen + 1];
  
  		// copy the two strings into the new memory
  		memcpy(ConcatStr, LCStr, LeftLen);
  		memcpy(ConcatStr + LeftLen, &Ch, RightLen);
  
  		// finish the new string
  		ConcatStr[LeftLen + RightLen] = 0;
  
  		// return
  		return TStr::WrapCStr(ConcatStr);
  	}
  }
  
  bool TStr::IsUInt(TChRet& Ch, const bool& Check, const uint& MnVal, const uint& MxVal, uint& Val) {
  	uint64 _Val;
  	// assign and check for overflow
  	return IsUInt64(Ch, Check, MnVal, MxVal, _Val) && (uint64)(Val = (uint)_Val) == _Val;
  }
  
  bool TStr::IsUInt64(TChRet& Ch, const bool& Check, const uint64& MnVal, const uint64& MxVal, uint64& Val) {
  	EAssert(!Check || (Check && MnVal <= MxVal));
  
  	uint64 _Val=0;
  	while (TCh::IsWs(Ch())) {
  		Ch.GetCh();
  	}
  
  	if (Ch()=='+'){Ch.GetCh();}
  	if (!TCh::IsNum(Ch())){return false;}
  	_Val=TCh::GetNum(Ch());
  
  	const uint64 MxBy10 = Check ? MxVal / 10 : TUInt64::Mx / 10;
  	const uint64 MxBy10Rem = Check ? MxVal % 10 : TUInt64::Mx % 10;
  
  	uint ChNum;
  	while (TCh::IsNum(Ch.GetCh())) {
  		ChNum = TCh::GetNum(Ch());
  
  		// check for overflow
  		if (_Val > MxBy10 || (_Val == MxBy10 && ChNum > MxBy10Rem)) {
  			return false;
  		}
  
  		// increase the sum
  		_Val = 10*_Val + ChNum;
  	}
  
  	if (Check && _Val < MnVal){return false;}
  	if (Ch.Eof()){Val=_Val; return true;} else {return false;}
  }
  
  /////////////////////////////////////////////////
  // Input-String
  TStrIn::TStrIn(const TStr& _Str, const bool& MakeCopyP) :
    TSBase(), TSIn(), OwnP(MakeCopyP),
    Bf(MakeCopyP ? _Str.CloneCStr() : _Str.CStr()), BfC(0), BfL(_Str.Len()){}
  
  PSIn TStrIn::New(const TStr& Str, const bool& MakeCopyP){
    return PSIn(new TStrIn(Str, MakeCopyP));
  }
  
  int TStrIn::GetBf(const void* LBf, const TSize& LBfL){
    Assert(TSize(BfC+LBfL)<=TSize(BfL));
    int LBfS=0;
    for (TSize LBfC=0; LBfC<LBfL; LBfC++){
      LBfS+=(((char*)LBf)[LBfC]=Bf[BfC++]);}
    return LBfS;
  }
  
  bool TStrIn::GetNextLnBf(TChA& LnChA){
    // not implemented
    FailR(TStr::Fmt("TStrIn::GetNextLnBf: not implemented").CStr());
    return false;
  }
  
  TStr TStrIn::GetSNm() const {
    return "Input-String";
  }
  
  /////////////////////////////////////////////////
  // String-Pool
  void TStrPool::Resize(const uint& _MxBfL) {
    uint newSize = MxBfL;
    while (newSize < _MxBfL) {
      if (newSize >= GrowBy && GrowBy > 0) newSize += GrowBy;
      else if (newSize > 0) newSize *= 2;
      else newSize = TInt::GetMn(GrowBy, 1024);
      // check for overflow at 4GB
      IAssertR(newSize >= MxBfL, TStr::Fmt("TStrPool::Resize: %u, %u [Size larger than 4Gb, which is not supported by TStrPool]", newSize, MxBfL).CStr());
    }
    if (newSize > MxBfL) {
      Bf = (char *) realloc(Bf, newSize);
      IAssertR(Bf, TStr::Fmt("old Bf size: %u, new size: %u", MxBfL, newSize).CStr());
      MxBfL = newSize;
    }
    IAssertR(MxBfL >= _MxBfL, TStr::Fmt("new size: %u, requested size: %u", MxBfL, _MxBfL).CStr());
  }
  
  TStrPool::TStrPool(const uint& MxBfLen, const uint& _GrowBy) : MxBfL(MxBfLen), BfL(0), GrowBy(_GrowBy), Bf(0) {
    //IAssert(MxBfL >= 0); IAssert(GrowBy >= 0);
    if (MxBfL > 0) { Bf = (char *) malloc(MxBfL);  IAssertR(Bf, TStr::Fmt("Can not resize buffer to %u bytes. [Program failed to allocate more memory. Solution: Get a bigger machine.]", MxBfL).CStr()); }
    AddStr(""); // add an empty string at the beginning for fast future access
  }
  
  TStrPool::TStrPool(TSIn& SIn, bool LoadCompact) : MxBfL(0), BfL(0), GrowBy(0), Bf(0) {
    SIn.Load(MxBfL);  SIn.Load(BfL);  SIn.Load(GrowBy);
    //IAssert(MxBfL >= BfL);  IAssert(BfL >= 0);  IAssert(GrowBy >= 0);
    if (LoadCompact) MxBfL = BfL;
    if (MxBfL > 0) { Bf = (char *) malloc(MxBfL); IAssertR(Bf, TStr::Fmt("Can not resize buffer to %u bytes. [Program failed to allocate more memory. Solution: Get a bigger machine.]", MxBfL).CStr()); }
    if (BfL > 0) SIn.LoadBf(Bf, BfL);
    SIn.LoadCs();
  }
  
  void TStrPool::Save(TSOut& SOut) const {
    SOut.Save(MxBfL);  SOut.Save(BfL);  SOut.Save(GrowBy);
    SOut.SaveBf(Bf, BfL);
    SOut.SaveCs();
  }
  
  TStrPool& TStrPool::operator = (const TStrPool& Pool) {
    if (this != &Pool) {
      GrowBy = Pool.GrowBy;  MxBfL = Pool.MxBfL;  BfL = Pool.BfL;
      if (Bf) free(Bf); else IAssertR(MxBfL == 0, TStr::Fmt("size: %u, expected size: 0", MxBfL).CStr());
      Bf = (char *) malloc(MxBfL);  IAssertR(Bf, TStr::Fmt("Can not resize buffer to %u bytes. [Program failed to allocate more memory. Solution: Get a bigger machine.]", MxBfL).CStr());  memcpy(Bf, Pool.Bf, BfL);
    }
    return *this;
  }
  
  // Adds Len characters to pool. To append a null
  // terminated string Len must be equal to strlen(s) + 1
  uint TStrPool::AddStr(const char *Str, const uint& Len) {
    IAssertR(Len > 0, "String too short (length includes the null character)");  //J: if (! Len) return -1;
    if (Len == 1 && BfL > 0) { return 0; } // empty string
    Assert(Str);  Assert(Len > 0);
    if (BfL + Len > MxBfL) Resize(BfL + Len);
    memcpy(Bf + BfL, Str, Len);
    uint Pos = BfL;  BfL += Len;  return Pos;
  }
  
  int TStrPool::GetPrimHashCd(const char *CStr) {
    return TStrHashF_DJB::GetPrimHashCd(CStr);
  }
  
  int TStrPool::GetSecHashCd(const char *CStr) {
    return TStrHashF_DJB::GetSecHashCd(CStr);
  }
  
  /////////////////////////////////////////////////
  // String-Pool-64bit
  void TStrPool64::Resize(const ::TSize& _MxBfL) {
    ::TSize newSize = MxBfL;
    while (newSize < _MxBfL) {
      if (newSize >= GrowBy && GrowBy > 0) newSize += GrowBy;
      else if (newSize > 0) newSize *= 2;
      else newSize = (GrowBy > ::TSize(1024)) ? ::TSize(1024) : GrowBy;
      IAssert(newSize >= MxBfL); // assert we are growing
    }
    if (newSize > MxBfL) {
      Bf = (char *) realloc(Bf, newSize);
      IAssertR(Bf, TStr::Fmt("old Bf size: %u, new size: %u", MxBfL, newSize).CStr());
      MxBfL = newSize;
    }
    IAssert(MxBfL >= _MxBfL);
  }
  
  TStrPool64::TStrPool64(::TSize _MxBfL, ::TSize _GrowBy):
      MxBfL(_MxBfL), BfL(0), GrowBy(_GrowBy), Bf(NULL) {
  
    if (MxBfL > 0) { Bf = (char*)malloc(MxBfL); IAssert(Bf != NULL); }
    AddStr("");
  }
  
  TStrPool64::TStrPool64(const TStrPool64& StrPool):
    MxBfL(StrPool.MxBfL), BfL(StrPool.BfL), GrowBy(StrPool.GrowBy) {
    if (Bf != NULL) { free(Bf); } else { IAssert(MxBfL == 0); }
    Bf = (char*)malloc(StrPool.MxBfL); IAssert(Bf != NULL);
    memcpy(Bf, StrPool.Bf, BfL);
  }
  
  TStrPool64::TStrPool64(TSIn& SIn, bool LoadCompact):
    MxBfL(0), BfL(0), GrowBy(0), Bf(0) {
    uint64 _GrowBy, _MxBfL, _BfL;
    SIn.Load(_GrowBy); SIn.Load(_MxBfL); SIn.Load(_BfL);
    GrowBy = (::TSize)_GrowBy; MxBfL = (::TSize)_MxBfL; BfL = (::TSize)_BfL;
    if (LoadCompact) { MxBfL = BfL; }
    if (MxBfL > 0) { Bf = (char*)malloc(MxBfL); IAssert(Bf != NULL); }
    for (::TSize BfN = 0; BfN < _BfL; BfN++) { Bf[BfN] = SIn.GetCh(); }
    SIn.LoadCs();
  }
  
  void TStrPool64::Save(TSOut& SOut) const {
    uint64 _GrowBy = GrowBy, _MxBfL = MxBfL, _BfL = BfL;
    SOut.Save(_GrowBy);  SOut.Save(_MxBfL);  SOut.Save(_BfL);
    for (::TSize BfN = 0; BfN < _BfL; BfN++) { SOut.PutCh(Bf[BfN]); }
    SOut.SaveCs();
  }
  
  TStrPool64& TStrPool64::operator=(const TStrPool64& StrPool) {
    if (this != &StrPool) {
    GrowBy = StrPool.GrowBy;  MxBfL = StrPool.MxBfL;  BfL = StrPool.BfL;
    if (Bf != NULL) { free(Bf); } else { IAssert(MxBfL == 0); }
    Bf = (char*)malloc(MxBfL); IAssert(Bf != NULL);
    memcpy(Bf, StrPool.Bf, BfL);
    }
    return *this;
  }
  
  void TStrPool64::Clr(bool DoDel) {
    BfL = 0;
    if (DoDel && (Bf!=NULL)) {
      free(Bf);
      Bf = NULL; MxBfL = 0;
    }
  }
  
  uint64 TStrPool64::AddStr(const TStr& Str) {
    const int Len = Str.Len() + 1;
    if (BfL + Len > MxBfL) { Resize(BfL + Len); }
    memcpy(Bf + BfL, Str.CStr(), Len);
    ::TSize Offset = BfL;  BfL += Len;
    return uint64(Offset);
  }
  
  TStr TStrPool64::GetStr(const uint64& StrId) const {
    ::TSize Offset = (::TSize)StrId;
    return TStr(Bf + Offset);
  }
  
  /////////////////////////////////////////////////
  // Void
  void TVoid::LoadXml(const PXmlTok& XmlTok, const TStr& Nm){
    XLoadHd(Nm);
  }
  
  void TVoid::SaveXml(TSOut& SOut, const TStr& Nm) const {
    XSaveBETag(Nm);
  }
  
  /////////////////////////////////////////////////
  // Boolean
  const bool TBool::Mn=0;
  const bool TBool::Mx=1;
  const int TBool::Vals=TBool::Mx-TBool::Mn+1;
  TRnd TBool::Rnd;
  
  const TStr TBool::FalseStr="F";
  const TStr TBool::TrueStr="T";
  const TStr TBool::NStr="N";
  const TStr TBool::YStr="Y";
  const TStr TBool::NoStr="No";
  const TStr TBool::YesStr="Yes";
  
  void TBool::LoadXml(const PXmlTok& XmlTok, const TStr& Nm){
    XLoadHd(Nm);
    Val=TXmlObjSer::GetBoolArg(XmlTok, "Val");
  }
  
  void TBool::SaveXml(TSOut& SOut, const TStr& Nm) const {
    XSaveBETagArg(Nm, "Val", TBool::GetStr(Val));
  }
  
  bool TBool::IsValStr(const TStr& Str){
    TStr UcStr=Str.GetUc();
    return
     (UcStr==FalseStr)||(UcStr==TrueStr)||
     (UcStr==YStr)||(UcStr==NStr)||
     (UcStr==YesStr)||(UcStr==NoStr);
  }
  
  bool TBool::GetValFromStr(const TStr& Str){
    return (Str==TrueStr)||(Str==YStr)||(Str==YesStr);
  }
  
  bool TBool::GetValFromStr(const TStr& Str, const bool& DfVal){
    TStr UcStr=Str.GetUc();
    if (IsValStr(UcStr)){
      return (UcStr==TrueStr)||(UcStr==YStr)||(UcStr==YesStr);
    } else {
      return DfVal;
    }
  }
  
  /////////////////////////////////////////////////
  // Char
  const char TCh::Mn=CHAR_MIN;
  const char TCh::Mx=CHAR_MAX;
  const int TCh::Vals=int(TCh::Mx)-int(TCh::Mn)+1;
  
  const char TCh::NullCh=char(0);
  const char TCh::TabCh=char(9);
  const char TCh::LfCh=char(10);
  const char TCh::CrCh=char(13);
  const char TCh::EofCh=char(26);
  const char TCh::HashCh='#';
  
  void TCh::LoadXml(const PXmlTok& XmlTok, const TStr& Nm){
    XLoadHd(Nm);
    Val=char(TXmlObjSer::GetIntArg(XmlTok, "Val"));
  }
  
  void TCh::SaveXml(TSOut& SOut, const TStr& Nm) const {
    XSaveBETagArg(Nm, "Val", TInt::GetStr(Val));
  }
  
  /////////////////////////////////////////////////
  // Unsigned-Char
  const uchar TUCh::Mn=0;
  const uchar TUCh::Mx=UCHAR_MAX;
  const int TUCh::Vals=int(TUCh::Mx)-int(TUCh::Mn)+1;
  
  void TUCh::LoadXml(const PXmlTok& XmlTok, const TStr& Nm){
    XLoadHd(Nm);
    Val=uchar(TXmlObjSer::GetIntArg(XmlTok, "Val"));
  }
  
  void TUCh::SaveXml(TSOut& SOut, const TStr& Nm) const {
    XSaveBETagArg(Nm, "Val", TInt::GetStr(Val));
  }
  
  /////////////////////////////////////////////////
  // Integer16
  
  const int16 TSInt::Mn = SHRT_MIN;
  const int16 TSInt::Mx = SHRT_MAX;
  
  /////////////////////////////////////////////////
  // Usigned Integer16
  
  const uint16 TUSInt::Mn = 0;
  const uint16 TUSInt::Mx = USHRT_MAX;
  
  /////////////////////////////////////////////////
  // Integer
  const int TInt::Mn=INT_MIN;
  const int TInt::Mx=INT_MAX;
  const int TInt::Kilo=1024;
  const int TInt::Mega=1024*1024;
  const int TInt::Giga=1024*1024*1024;
  TRnd TInt::Rnd;
  
  void TInt::LoadXml(const PXmlTok& XmlTok, const TStr& Nm){
    XLoadHd(Nm);
    Val=TXmlObjSer::GetIntArg(XmlTok, "Val");
  }
  
  void TInt::SaveXml(TSOut& SOut, const TStr& Nm) const {
    XSaveBETagArg(Nm, "Val", TInt::GetStr(Val));
  }
  
  TStr TInt::GetStr(const int& Val, const char* FmtStr){
    if (FmtStr==NULL){
      return GetStr(Val);
    } else {
      char Bf[255];
      sprintf(Bf, FmtStr, Val);
      return TStr(Bf);
    }
  }
  
  TStr TInt::GetSepStr(const char& Sep) const
  {
      TStr StrVal = GetStr().Reverse();
      TChA Out;
      for (int N = 0; N < StrVal.Len(); N++) {
          if (N > 0 && N % 3 == 0)
              Out += Sep;
          Out += StrVal[N];
      }
      Out.Reverse();
      return Out;
  }
  
  //-----------------------------------------------------------------------------
  // Frugal integer serialization
  //-----------------------------------------------------------------------------
  // These routines serialize integers to sequences of 1..4 bytes, with smaller
  // integers receiving shorter codes.  They do not work directly with streams
  // but rather with a user-provided buffer.  It is expected that one will want
  // to store several such integers at once, and it would be inefficient to
  // call the virtual TSOut::PutCh method once for every byte; this is why
  // SaveFrugalInt and LoadFrugalInt work with a user-provided char* buffer
  // rather than with TSIn/TSOut.  To store a vector of such integers, use the
  // SaveFrugalIntV/LoadFrugalIntV pair.
  
  char* TInt::SaveFrugalInt(char *pDest, int i){
    // <0xxx xxxx> has 128 combinations and is used to store -1..126.
    // <1xxx xxxx> <00xx xxxx> has 2^13 = 8192 combinations and is used to store 127..8318.
    // <1xxx xxxx> <01xx xxxx> has 2^13 = 8192 combinations and is used to store -2..-8193.
    // <1xxx xxxx> <1xxx xxxx> <xxxx xxxx> <0xxx xxxx> has 2^29 = 536870912 combinations and is used to store 8319..536879230.
    // <1xxx xxxx> <1xxx xxxx> <xxxx xxxx> <1xxx xxxx> has 2^29 = 536870912 combinations and is used to store -8194..-536879105.
    i++;
    if (i >= 0 && i <= 127) { *pDest++ = char(i); return pDest; }
    if (i >= 128 && i < 128 + 8192) { i -= 128; *pDest++ = char(0x80 | (i & 0x7f));
      *pDest++ = char((i >> 7) & 0x3f); return pDest; }
    if (i <= -1 && i > -1 - 8192) { i = -1 - i;  *pDest++ = char(0x80 | (i & 0x7f));
      *pDest++ = char(0x40 | ((i >> 7) & 0x3f)); return pDest; }
    if (i >= 128 + 8192 && i < 128 + 8192 + 536870912) { i -= 128 + 8192;
      *pDest++ = char(0x80 | (i & 0x7f)); *pDest++ = char(0x80 | ((i >> 7) & 0x7f));
      *pDest++ = char((i >> 14) & 0xff); *pDest++ = char((i >> 22) & 0x7f); return pDest; }
    if (i <= -1 - 8192 && i > -1 - 8192 - 536870912) { i = (-1 - 8192) - i;
      *pDest++ = char(0x80 | (i & 0x7f)); *pDest++ = char(0x80 | ((i >> 7) & 0x7f));
      *pDest++ = char((i >> 14) & 0xff); *pDest++ = char(0x80 | ((i >> 22) & 0x7f)); return pDest; }
    IAssertR(false, TInt::GetStr(i)); return 0;
  }
  
  char* TInt::LoadFrugalInt(char *pSrc, int& i){
    i = 0;
    int ch = (int) ((unsigned char) (*pSrc++));
    if ((ch & 0x80) == 0) { i = ch; i--; return pSrc; }
    i = (ch & 0x7f);
    ch = (int) ((unsigned char) (*pSrc++));
    if ((ch & 0x80) == 0)
    {
      i |= (ch & 0x3f) << 7;
      if ((ch & 0x40) == 0) i += 128; else i = -1 - i;
      i--; return pSrc;
    }
    i |= (ch & 0x7f) << 7;
    ch = (int) ((unsigned char) (*pSrc++));
    i |= ch << 14;
    ch = (int) ((unsigned char) (*pSrc++));
    i |= (ch & 0x7f) << 22;
    if ((ch & 0x80) == 0) i += 128 + 8192; else i = (-1 - 8192) - i;
    i--; return pSrc;
  }
  
  // Tests the SaveFrugalInt/LoadFrugalInt combination on all the
  // integers they can work with (about 10^9 integers).
  void TInt::TestFrugalInt(){
    char buf[10], *p = &buf[0], *r, *s;
    int i, j;
  #define __TEST(from, to, len) \
    for (i = (from); i <= (to); i++) \
      { if ((i & 0xffff) == 0) printf("%d\r", i); \
        r = SaveFrugalInt(p, i); s = LoadFrugalInt(p, j); \
        IAssert(r == s); IAssert(i == j); IAssert(r - p == len); }
  
    __TEST(-1, 126, 1);
    __TEST(127, 127 + 8191, 2);
    __TEST(-2 - 8191, -2, 2);
    __TEST(127 + 8192, 127 + 8191 + (1 << 29), 4);
    __TEST(-2 - 8191 - (1 << 29), -2 - 8192, 4);
  #undef __TEST
  }
  
  // Suppose that the contents of 'v', encoded using SaveFrugalInt,
  // occupy 'n' bytes.  SaveFrugalIntV first stores 'n' (using
  // SaveFrugalInt), then the contents.
  void TInt::SaveFrugalIntV(TSOut& SOut, const TIntV& v){
    // Prepare a large enough buffer.
    int count = v.Len();
    char *buf = new char[4 * (count + 1)], *pStart, *pEnd;
    // Encode the contents of 'v'.
    pStart = buf + 4; pEnd = pStart;
    for (int i = 0; i < count; i++)
      pEnd = SaveFrugalInt(pEnd, v[i].Val);
    // Encode the size of the encoded contents of 'v'.
    // This is stored at the beginning of 'buf' and is then
    // moved so that there is no gap between it and the
    // beginning of the stored contents (at pStart).
    int size = int(pEnd - pStart);
    char *pSizeStart = buf;
    char *pSizeEnd = SaveFrugalInt(pSizeStart, size);
    while (pSizeEnd > pSizeStart) *(--pStart) = *(--pSizeEnd);
    // Write the buffer and free the memory.
    SOut.PutBf(pStart, TSize(pEnd - pStart));
    delete[] buf;
  }
  
  // Loads an integer 'n' (using LoadFrugalInt), then loads
  // 'n' bytes, decoding them using LoadFrugalInt and adding
  // them to the vector 'v'.  If clearVec is true, 'v' is
  // cleared before anything is added to it.
  void TInt::LoadFrugalIntV(TSIn& SIn, TIntV& v, bool clearVec){
    if (clearVec) v.Clr();
    char sizeBuf[4], *p, *pEnd;
    // Load the first frugally-stored integer into the sizeBuf
    // buffer.  'count' bytes will be read.
    sizeBuf[0] = SIn.GetCh(); int count = 1;
    if (sizeBuf[0] & 0x80)
    {
      sizeBuf[1] = SIn.GetCh(); count++;
      if (sizeBuf[1] & 0x80) { sizeBuf[2] = SIn.GetCh();
        sizeBuf[3] = SIn.GetCh(); count += 2;}
    }
    // Decode the stored size.
    int size;
    pEnd = LoadFrugalInt(&sizeBuf[0], size);
    IAssert(pEnd - &sizeBuf[0] == count);
    if (size <= 0) return;
    // Allocate a buffer and read the compressed data.
    char *buf = new char[size];
    SIn.GetBf(buf, size);
    // Decode the compressed integers and add them into 'v'.
    p = buf; pEnd = buf + size;
    while (p < pEnd)
      { int i; p = LoadFrugalInt(p, i); v.Add(i); }
    IAssert(p == pEnd);
    delete[] buf;
  }
  
  /////////////////////////////////////////////////
  // Unsigned-Integer
  const uint TUInt::Mn=0;
  const uint TUInt::Mx=UINT_MAX;
  TRnd TUInt::Rnd;
  
  void TUInt::LoadXml(const PXmlTok& XmlTok, const TStr& Nm){
    XLoadHd(Nm);
    Val=TXmlObjSer::GetIntArg(XmlTok, "Val");
  }
  
  void TUInt::SaveXml(TSOut& SOut, const TStr& Nm) const {
    XSaveBETagArg(Nm, "Val", TInt::GetStr(Val));
  }
  
  TStr TUInt::GetStr(const uint& Val, const char* FmtStr){
    if (FmtStr==NULL){
      return GetStr(Val);
    } else {
      char Bf[255];
      sprintf(Bf, FmtStr, Val);
      return TStr(Bf);
    }
  }
  
  bool TUInt::IsIpStr(const TStr& IpStr, uint& Ip, const char& SplitCh) {
  	TStrV IpStrV; IpStr.SplitOnAllCh(SplitCh, IpStrV);
      Ip = 0; int Byte = 0;
  	if (IpStrV.Len() != 4) { return false; }
  	if (!IpStrV[0].IsInt(true, 0, 255, Byte)) { return false; }; Ip = (uint)Byte;
  	if (!IpStrV[1].IsInt(true, 0, 255, Byte)) { return false; }; Ip = (Ip << 8) | (uint)Byte;
  	if (!IpStrV[2].IsInt(true, 0, 255, Byte)) { return false; }; Ip = (Ip << 8) | (uint)Byte;
  	if (!IpStrV[3].IsInt(true, 0, 255, Byte)) { return false; }; Ip = (Ip << 8) | (uint)Byte;
  	return true;
  }
  
  uint TUInt::GetUIntFromIpStr(const TStr& IpStr, const char& SplitCh) {
  	TStrV IpStrV; IpStr.SplitOnAllCh(SplitCh, IpStrV);
      uint Ip = 0; int Byte = 0;
  	EAssertR(IpStrV[0].IsInt(true, 0, 255, Byte), TStr::Fmt("Bad IP: '%s;", IpStr.CStr())); Ip = (uint)Byte;
  	EAssertR(IpStrV[1].IsInt(true, 0, 255, Byte), TStr::Fmt("Bad IP: '%s;", IpStr.CStr())); Ip = (Ip << 8) | (uint)Byte;
  	EAssertR(IpStrV[2].IsInt(true, 0, 255, Byte), TStr::Fmt("Bad IP: '%s;", IpStr.CStr())); Ip = (Ip << 8) | (uint)Byte;
  	EAssertR(IpStrV[3].IsInt(true, 0, 255, Byte), TStr::Fmt("Bad IP: '%s;", IpStr.CStr())); Ip = (Ip << 8) | (uint)Byte;
  	return Ip;
  }
  
  TStr TUInt::GetStrFromIpUInt(const uint& Ip) {
    return TStr::Fmt("%d.%d.%d.%d", ((Ip>>24) & 0xFF),
     ((Ip>>16) & 0xFF), ((Ip>>8) & 0xFF), (Ip & 0xFF));
  }
  
  bool TUInt::IsIpv6Str(const TStr& IpStr, const char& SplitCh) {
  	TStrV IpStrV; IpStr.SplitOnAllCh(SplitCh, IpStrV, false);
  	// check we have 8 groups
  	if (IpStrV.Len() > 8) { return false; }
  	// each group must be in hexa and in range from 0x0000 to 0xFFFF
  	int Group = 0;
  	for (int IpStrN = 0; IpStrN < IpStrV.Len(); IpStrN++) {
  		if (IpStrV[IpStrN].Empty()) { continue; }
  		if (IpStrV[IpStrN].IsHexInt(true, 0x0000, 0xFFFF, Group)) { continue; }
  		return false;
  	}
  	// all fine
  	return true;
  }
  
  
  /////////////////////////////////////////////////
  // Signed-Integer-64Bit
  
  //#if defined (GLib_WIN)
  const int64 TInt64::Mn(INT64_MIN);
  const int64 TInt64::Mx(INT64_MAX);
  //#else
  
  //#endif
  
  /////////////////////////////////////////////////
  // Unsigned-Integer-64Bit
  
  #if defined (GLib_WIN)
  const uint64 TUInt64::Mn(uint64(0x0000000000000000i64));
  const uint64 TUInt64::Mx(uint64(0xFFFFFFFFFFFFFFFFi64));
  #elif defined (GLib_BCB)
  const uint64 TUInt64::Mn(0x0000000000000000i64);
  const uint64 TUInt64::Mx(0xFFFFFFFFFFFFFFFFi64);
  #else
  const uint64 TUInt64::Mn((uint64)0x0000000000000000LL);
  const uint64 TUInt64::Mx(0xFFFFFFFFFFFFFFFFLL);
  #endif
  
  void TUInt64::LoadXml(const PXmlTok& XmlTok, const TStr& Nm){
    XLoadHd(Nm);
    Val=TXmlObjSer::GetInt64Arg(XmlTok, "Val");
  }
  
  void TUInt64::SaveXml(TSOut& SOut, const TStr& Nm) const {
    XSaveBETagArg(Nm, "Val", TUInt64::GetStr(Val));
  }
  
  /*/////////////////////////////////////////////////
  // Unsigned-Integer-64Bit
  const TUInt64 TUInt64::Mn(0, 0);
  const TUInt64 TUInt64::Mx(UINT_MAX, UINT_MAX);
  
  void TUInt64::LoadXml(const PXmlTok& XmlTok, const TStr& Nm){
    XLoadHd(Nm); XLoad(TInt(MsVal)); XLoad(TInt(LsVal));
  }
  
  void TUInt64::SaveXml(TSOut& SOut, const TStr& Nm) const {
    XSaveHd(Nm); XSave(TInt(MsVal)); XSave(TInt(LsVal));
  }
  
  TUInt64& TUInt64::operator+=(const TUInt64& Int){
    uint CarryVal=(LsVal>TUInt::Mx-Int.LsVal) ? 1 : 0;
    LsVal+=Int.LsVal;
    MsVal+=Int.MsVal+CarryVal;
    return *this;
  }
  
  TUInt64& TUInt64::operator-=(const TUInt64& Int){
    IAssert(*this>=Int);
    uint CarryVal;
    if (LsVal>=Int.LsVal){
      LsVal-=Int.LsVal; CarryVal=0;
    } else {
      LsVal+=(TUInt::Mx-Int.LsVal)+1; CarryVal=1;
    }
    MsVal-=(Int.MsVal+CarryVal);
    return *this;
  }
  
  TUInt64 TUInt64::operator++(int){
    if (LsVal==TUInt::Mx){
      Assert(MsVal<TUInt::Mx); MsVal++; LsVal=0;}
    else {LsVal++;}
    return *this;
  }
  
  TUInt64 TUInt64::operator--(int){
    if (LsVal==0){
      Assert(MsVal>0); MsVal--; LsVal=TUInt::Mx;}
    else {LsVal--;}
    return *this;
  }
  
  TStr TUInt64::GetStr(const TUInt64& Int){
    char Bf[255]; sprintf(Bf, "%.0Lf", ldouble(Int));
    return TStr(Bf);
  }
  
  TStr TUInt64::GetHexStr(const TUInt64& Int){
    char Bf[255]; sprintf(Bf, "%08X%08X", Int.MsVal, Int.LsVal);
    return TStr(Bf);
  }*/
  
  /////////////////////////////////////////////////
  // Float
  const double TFlt::Mn=-DBL_MAX;
  const double TFlt::Mx=+DBL_MAX;
  const double TFlt::NInf=-DBL_MAX;
  const double TFlt::PInf=+DBL_MAX;
  const double TFlt::Eps=1e-16;
  const double TFlt::EpsHalf  =1e-7;
  
  TRnd TFlt::Rnd;
  
  void TFlt::LoadXml(const PXmlTok& XmlTok, const TStr& Nm){
    XLoadHd(Nm);
    Val=TXmlObjSer::GetFltArg(XmlTok, "Val");
  }
  
  void TFlt::SaveXml(TSOut& SOut, const TStr& Nm) const {
    XSaveBETagArg(Nm, "Val", TFlt::GetStr(Val));
  }
  
  TStr TFlt::GetStr(const double& Val, const int& Width, const int& Prec){
    char Bf[255];
    if ((Width==-1)&&(Prec==-1)){sprintf(Bf, "%g", Val);}
    else {sprintf(Bf, "%*.*f", Width, Prec, Val);}
    return TStr(Bf);
  }
  
  TStr TFlt::GetStr(const double& Val, const char* FmtStr){
    if (FmtStr==NULL){
      return GetStr(Val);
    } else {
      char Bf[255];
      sprintf(Bf, FmtStr, Val);
      return TStr(Bf);
    }
  }
  
  /////////////////////////////////////////////////
  // Short-Float
  const sdouble TSFlt::Mn=-FLT_MIN;
  const sdouble TSFlt::Mx=+FLT_MAX;
  
  void TSFlt::LoadXml(const PXmlTok& XmlTok, const TStr& Nm){
    XLoadHd(Nm);
    Val=sdouble(TXmlObjSer::GetFltArg(XmlTok, "Val"));
  }
  
  void TSFlt::SaveXml(TSOut& SOut, const TStr& Nm) const {
    XSaveBETagArg(Nm, "Val", TFlt::GetStr(Val));
  }
  
  /////////////////////////////////////////////////
  // Long-Float
  const ldouble TLFlt::Mn=-LDBL_MAX;
  const ldouble TLFlt::Mx=+LDBL_MAX;
  /*const ldouble TUInt64::Mn_LFlt=TUInt64::Mn;
  const ldouble TUInt64::Mx_LFlt=TUInt64::Mx;
  const ldouble TUInt64::MxP1_LFlt=ldouble(TUInt::Mx)+ldouble(1);*/
  
  void TLFlt::LoadXml(const PXmlTok& XmlTok, const TStr& Nm){
    XLoadHd(Nm);
    Val=TXmlObjSer::GetFltArg(XmlTok, "Val");
  }
  
  void TLFlt::SaveXml(TSOut& SOut, const TStr& Nm) const {
    XSaveBETagArg(Nm, "Val", TFlt::GetStr(double(Val)));
  }
  
  TStr TLFlt::GetStr(const ldouble& Val, const int& Width, const int& Prec){
    char Bf[255];
    if ((Width==-1)&&(Prec==-1)){sprintf(Bf, "%Lg", Val);}
    else {sprintf(Bf, "%*.*Lf", Width, Prec, Val);}
    return TStr(Bf);
  }
  
  TStr TLFlt::GetStr(const ldouble& Val, const char* FmtStr){
    if (FmtStr==NULL){
      return GetStr(Val);
    } else {
      char Bf[255];
      sprintf(Bf, FmtStr, Val);
      return TStr(Bf);
    }
  }
  
  /////////////////////////////////////////////////
  // Float-Rectangle
  void TFltRect::LoadXml(const PXmlTok& XmlTok, const TStr& Nm){
    XLoadHd(Nm);
    MnX=TXmlObjSer::GetFltArg(XmlTok, "MnX");
    MnY=TXmlObjSer::GetFltArg(XmlTok, "MnY");
    MxX=TXmlObjSer::GetFltArg(XmlTok, "MxX");
    MxY=TXmlObjSer::GetFltArg(XmlTok, "MxY");
  }
  
  void TFltRect::SaveXml(TSOut& SOut, const TStr& Nm) const {
    XSaveBETagArg4(Nm,
     "MnX", TFlt::GetStr(double(MnX)), "MnY", TFlt::GetStr(double(MnY)),
     "MxX", TFlt::GetStr(double(MxX)), "MxY", TFlt::GetStr(double(MxY)));
  }
  
  bool TFltRect::Intersection(const TFltRect& Rect1, const TFltRect& Rect2){
    const double MnXX = TFlt::GetMx(Rect1.GetMnX(), Rect2.GetMnX());
    const double MnYY = TFlt::GetMx(Rect1.GetMnY(), Rect2.GetMnY());
    const double MxXX = TFlt::GetMn(Rect1.GetMxX(), Rect2.GetMxX());
    const double MxYY = TFlt::GetMn(Rect1.GetMxY(), Rect2.GetMxY());
    return (MnXX < MxXX) && (MnYY < MxYY);
  }
  
  TStr TFltRect::GetStr() const {
    TChA ChA;
    ChA+='(';
    ChA+=TFlt::GetStr(MnX, "%0.2f"); ChA+=',';
    ChA+=TFlt::GetStr(MnY, "%0.2f"); ChA+=',';
    ChA+=TFlt::GetStr(MxX, "%0.2f"); ChA+=',';
    ChA+=TFlt::GetStr(MxY, "%0.2f"); ChA+=')';
    return ChA;
  }
  