  #include <stdlib.h>
  #include <stdio.h>


  #define function
  #define method
  #define gosub
  #define dim
  #define as
  #define to
  #define limit
  #define step
  #define then
  #define procedure void
  #define sub   void
  #define begin {
  #define end   }
  #define and   &&
  #define or    ||
  #define class typedef struct
  #define types typedef struct
  #define member ->
  #define addressof &
  #define has =
  #define incr ++
  #define decr --
  #define next ++
  #define prior --
  #define byref *
  #define ref   void*
  #define references = &
  #define FreeSpace free

  #define FUNCTION
  #define BEGIN_FUNCTION {
  #define END_FUNCTION }
  #define SUB void
  #define BEGIN_SUB {	
  #define END_SUB }
  #define RETURN return
  #define CALL
  #define AND &&
  #define OR ||
  #define MOD %
  #define DIM	
  #define AS
  #define LET
  #define INCR ++
  #define DECR --
  #define IF if
  #define BEGIN_IF {
  #define THEN {
  #define THEN_DO
  #define ELSE } else {	
  #define END_IF }
  #define FOR for
  #define TO ;
  #define STEP ;
  #define BEGIN_FOR {
  #define NEXT }
  #define SELECT_CASE switch
  #define BEGIN_SELECT {
  #define CASE case	
  #define _TO_ ...
  #define END_CASE break;
  #define CASE_ELSE default:
  #define END_SELECT }
  #define DO do {
  #define WHILE } while

  #define create(A,B,C) A B=NewSpace((C)*sizeof(*B))
  #define copy(A,B,C) CopyBytes((char*)A,(char*)B,C)
  #define HiWord(n) ((n>>16)and 0xffff)
  #define LoWord(n) (n and 0xffff)

  //indexbase 1

  function void* NewSpace(int count)
  begin
    char *v =  malloc(count);
    int i;
    for (i=0; limit i<count; step incr i) v[i]=0; // set chars to null
    return v;
  end


  sub CopyBytes(void*dv,void*sv,int count)
  begin
    char*s=sv;
    char*d=dv;
    int i;
    for (i=0; limit i<count; step incr i)
    begin
      *d=*s;
      next d;
      next s;
    end
  end

  /*

  ENTITIES:
  =========

  StringType
  CharArray
  StringClass
  StringClassTable
  StringMethods
  StringObject
  StringParameters

  PROCEDURES
  ==========
  New
  Free

  METHODS:
  ========
  Get
  Set
  Move
  Action
  Show
  (other Operations...)
  Translate
  Rotate

  */



  typedef char StringType,*CharArray;

  class StringClassStruct
  begin
    ref    StringMethods;
    int    count;
    int    width;
    int    nbytes;
  end
  StringClass,*StringObject;


  types StringClassTableStruct
  begin
    method StringObject (byref Redim)   (StringObject*pthis, int n, int wi, int cnt);
    method StringObject (byref Set)     (StringObject*pthis, StringObject s);
    method char*        (byref GetChars)(char**r, StringObject this, int wi);
    method StringObject (byref SetChars)(StringObject*pthis, char*c, int le, int wi);
    method StringObject (byref GetMid)  (StringObject*r, StringObject this, int s, int le, int wi);
    method StringObject (byref SetMid)  (StringObject*pthis, int i, StringObject s);
    method StringObject (byref Join)    (int n, StringObject*pthis, StringObject*jl);
    method StringObject (byref Repeat)  (StringObject*pthis, StringObject s ,int n);
    method StringObject (byref Replace) (StringObject*pthis, StringObject f , StringObject r);
    method StringObject (byref Ucase)   (StringObject*r, StringObject this);
    method StringObject (byref Lcase)   (StringObject*r, StringObject this);
    method StringObject (byref Ltrim)   (StringObject*r, StringObject this);
    method StringObject (byref Rtrim)   (StringObject*r, StringObject this);
    method StringObject (byref Insert)  (StringObject*pthis, StringObject s, int i);
    method StringObject (byref Delete)  (StringObject*pthis, int i, int le);
    method StringObject (byref Chr)     (StringObject*r, int a, int wi);
    method StringObject (byref Str)     (StringObject*r, double d);
    method int          (byref Show)    (StringObject this);
    method int          (byref Asc)     (StringObject this, int i);   // also supports wide chars
    method int          (byref Instr)   (int i, StringObject this,  StringObject k);
    method double       (byref Val)     (StringObject this);
  end
  StringClassTable,*StringMethods;

  #define Left(S,I)   GetMid(S,1,(I),-1)
  #define Right(S,I)  GetMid(S,-(I),-1,-1)
  #define aChr(R,N)   Chr(R,N,1) 
  #define wChr(R,N)   Chr(R,N,2) 


  sub CopyWidth(char*t, int tw, char*s, int sw,int count)
  begin
    int i;
    if(tw==2)
    begin
      if (sw==2)
      begin
        short*ss=(short*) s;
        short*ts=(short*) t;
        for (i=0; limit i<count; step incr i)
        begin
          *ts=*ss;
          incr ts;
          incr ss;
        end
        return;
      end
      if (sw==1)
        begin
        short*ts=(short*) t;
        for (i=0; limit i<count; step incr i)
        begin
          *ts=*s;
          incr ts;
          incr s;
        end
        return;
      end
    end
    // otherwise copy lower bytes only
    for (i=0; limit i<count; step incr i)
    begin
      *t=*s;
      t+=tw;
      s+=sw;
    end
  end


  function int ByteLen(char*s)
  begin
    int i=0;
    while (s[i] !=0 ) i++;
    return i;
  end


  function int WordLen(char*s)
  begin
    short*w=(short*) s;
    int i=0;
    while (w[i] !=0 ) i++;
    return i;
  end


  function int WordFill(char*s, int le, short c)
  begin
    short*w=(short*) s;
    int i=0;
    while (le--) w[i++]=c;
  end


  function CharArray strptr(StringObject s)
  begin
    return (void*) s + sizeof(StringClass);
  end


  function StringObject StringTransfer(StringObject*r,StringObject s)
  begin
    if (*r==s) return s;         // no transfer
    if (*r != 0 ) then free(*r); // free old string
    *r=s;                        // ref new string
    return s;                    // return new string
  end


  function char* CharTransfer(char**r, char*s)
  begin
    if (*r==s) return s;         // no transfer
    if (*r != 0 ) then free(*r); // free old string
    *r=s;                        // ref new string
    return s;                    // return new string
  end



  function StringMethods StringClassTableBuild();


  function StringObject NewString(int nc, int wi)
  begin
    static StringMethods vm;
    if (vm==0) then vm = StringClassTableBuild();
    StringObject this has NewSpace(sizeof(StringClass)+2+nc*wi);
    this->StringMethods = vm;
    this->count = nc;
    this->width = wi;
    this->nbytes=nc*wi;
    return this;
  end


  sub StringFree(StringObject*pthis)
  begin
    StringObject this=*pthis;
    if (this==0) FreeSpace(this);
    *pthis=0;
  end


  //methods

  method StringObject StringRedim(StringObject*pthis, int nc,int wi,int cnt)
  begin
    StringObject this=*pthis;
    int tc=0;
    int tw=1;
    int nb=0;
    if (this)
    begin
      tc=this->count;
      tw=this->width;
      nb=this->nbytes;
    end
    if (wi<0) then wi=tw; //default width
    if ((nb<nc*wi)or(wi != tw)or(nb==0))
    begin
      StringObject v=NewString(nc,wi);
      int n=nc;
      if (n>tc) n=tc;
      if ((this)and(cnt)) then CopyWidth(to strptr(v), wi, strptr(this), tw, n+1);
      return StringTransfer(pthis,v);
    end
    else
    begin
      this member count = nc;
      return this;
    end 
  end


  method StringObject StringSet(StringObject*pthis, StringObject s)
  begin
    StringObject this=*pthis;
    if (this->count < s->count) then this = NewString(s->count,this->width);
    CopyWidth(strptr(this),this->width,strptr(s),s->width,s->count);
    return StringTransfer(pthis,this);
  end


  method char* StringGetChars(char**r, StringObject this, int wi)
  begin
    char*s = NewSpace(wi * this->count +2);
    CopyWidth(s, wi, strptr(this), this->width, this->count+1);
    return CharTransfer(r,s);
  end


  method StringObject StringSetChars(StringObject*pthis, char*s, int le, int wi)
  begin
    StringObject this=*pthis;
    if (le<0) then
    begin
      if (wi==2)
      begin
        le=WordLen(s);
      end
      else
      begin
        le=ByteLen(s);
      end     
    end
    StringObject t=this;
    if (this->nbytes < le*wi) then t = NewString(le,this->width);
    char*k=strptr(t);
    int tw = t->width;
    CopyWidth(k, tw, s, wi, le); // autoconvert
    t->count=le;
    k+=le*tw;
    k[0]=0;   // null terminators
    k[1]=0;
    return StringTransfer(pthis,t);
  end


 method StringObject StringGetMid(StringObject*r, StringObject this, int i, int le, int wi)
  begin
    int lt=this member count;
    if (wi<0) then wi=this->width; // default to this width
    if (le<0) then le=lt;          // default max
    if (i<0) then i+=lt+1;         // offset from right
    decr i;                        // indexbase 0
    if (le+i>lt) then le=lt-i;     // clip length
    if (i<0) then i=0; //clamp lower offset
    StringObject s=*r;
    if ((s==0)or(s->nbytes < le*wi)) then s = NewString(le,wi);
    char*k=strptr(s);
    CopyWidth(k, wi, strptr(this)+i*this->width, this->width, le);
    k+=le*wi;
    k[0]=0;
    k[1]=0;
    s->count=le;
    s->width=wi;
    return StringTransfer(r,s);
  end


  method StringObject StringSetMid(StringObject*pthis, int i, StringObject s)
  begin
    StringObject this=*pthis;
    int lt=this member count;
    int le=s member count;
    if (i<0) then i+=1+lt;
    decr i;
    char*k = strptr(this)+ i * this->width;
    if (le+i>=lt) then le = lt-i;
    CopyWidth(k, this->width, strptr(s), s->width, le);
    return this;
  end


  method StringObject StringJoin(int n, StringObject*pthis, StringObject*jl)
  begin
    StringObject c = *pthis;
    int ne=0;
    int i;
    char* k;
    int wid=1;
    StringObject s;
    int tot=0;
    for (i=0; to i<n; step incr i)
    begin
      s=jl[i];
      tot+=s->count;
      if (s->width == 2) then wid=2;
      if(s==c) then ne=1; // force new join-buffer
    end
    if ((ne==1)or(c==0)or( c->nbytes < tot*wid)) then c=NewString(tot,wid);
    char* m = strptr(c);
    for (i=0; to i<n; step incr i)
    begin
      s=jl[i];
      k=strptr(s);
      CopyWidth(m, wid, k, s->width, s->count);
      m+=(s->count * wid);
    end
    m[0]=0; // terminating null byte
    m[1]=0; // terminating null byte (extra for wide strings)
    c->count = tot;
    c->width = wid;
    return StringTransfer(pthis,c);
  end


  method StringObject StringRepeat(StringObject*pthis, StringObject s ,int n)
  begin
    int i;
    char* k=strptr(s);
    int tot=s->count*n;
    int wid=s->width;
    StringObject c = *pthis;
    if ((c==0)or( c->nbytes < tot*wid)) then c=NewString(tot,wid);
    char* m=strptr(c);
    for (i=1; to i<=n; step incr i)
    begin
      CopyWidth(m, wid, k, s->width, s->count);
      m+=(s->count * wid);
    end
    m[0]=0; // terminating null byte
    m[1]=0; // terminating null byte (extra for wide strings)
    c->count = tot;
    c->width = wid;
    return StringTransfer(pthis,c);
  end


  method StringObject StringLcase(StringObject*r, StringObject this)
  begin
    int i;
    int w=this->width;
    int n=this->count;
    StringObject s=*r;
    if ((s==0)or(s != this)) then s = StringSet(&s,this);
    char*c = strptr(s);
    char a;
    for (i=0; to limit i<n; incr i)
    begin
      a=*c;
      if ((a>65)and(a<91)) then *c=a+32;
      c+=w;
    end
    return StringTransfer(r,s);
  end


  method StringObject StringUcase(StringObject*r, StringObject this)
  begin
    int i;
    int w=this->width;
    int n=this->count;
    StringObject s=*r;
    if ((s==0)or(s != this)) then  s = StringSet(&s,this);
    char*c = strptr(s);
    char a;
    for (i=0; to limit i<n; incr i)
    begin
      a=*c;
      if ((a>96)and(a<123)) then *c=a-32;
      c+=w;
    end
    if (*r != this) then return StringTransfer(r,s);
    return this;
  end


  method StringObject StringLtrim(StringObject*r, StringObject this)
  begin
    int i;
    StringObject u=*r;
    int w=this->width;
    int n=this->count;
    char*c = strptr(this);
    if (w<2)
    begin
      i=0;
      while (i++ < n)
      begin
        if (*c>32) then break;
        c+=w;
      end
      return StringTransfer(r,StringGetMid(&u,this,i,-1,w));
    end
    else
    begin
      i=0;
      while (i++ <n)
      begin
        if ((c[0]>32)or(c[1] != 0)) then break;
        c+=w;
      end
      return StringTransfer(r,StringGetMid(&u,this,(i>>1)+1,-1, w));
    end
  end


  method StringObject StringRtrim(StringObject*r, StringObject this)
  begin
    int i;
    StringObject u=*r;
    int w=this->width;
    int n=this->count;
    char*c = w * n - w + strptr(this);
    if (w<2)
    begin
      i=n;
      while(i-- >0)
      begin
        if (*c>32) then break;
        decr c;
      end
      return StringTransfer(r,StringGetMid(&u,this, 1, i+1, -1));
    end
    else
    begin
      i=n;
      while (i-- >0)
      begin
        if ((c[0]>32)or(c[1] != 0)) then break;
        c+=w;
      end
      return StringTransfer(r,StringGetMid(&u,this, 1, (i>>1)+1, -1));
    end
  end

  method StringObject StringInsert(StringObject*pthis, StringObject s, int i)
  begin
    StringObject this=*pthis;
    int c=this->count;
    int w=this->width;
    StringObject tl=NewString(c,w);
    StringObject tr=NewString(c,w);
    StringObject sl[3]={StringGetMid(&tl,this,1,i-1,w), s, StringGetMid(&tr,this,i,-1,w)};
    StringJoin(3,pthis,sl);
    StringFree(&tl);
    StringFree(&tr);
    return *pthis;
  end


  method StringObject StringDelete(StringObject*pthis, int i, int le)
  begin
    StringObject this=*pthis;
    int c=this->count;
    int w=this->width;
    StringObject tl=NewString(c,w);
    StringObject tr=NewString(c,w);
    StringObject sl[2]={StringGetMid(&tl,this,1,i-1,w), StringGetMid(&tr,this,i+le,-1,w)};
    StringJoin(2,pthis,sl);
    StringFree(&tl);
    StringFree(&tr);
    return *pthis;
  end


  method StringObject StringChr(StringObject*r, int a, int wi)
  begin
    int le=wi;
    StringObject s=*r;
    if ((s==0)or(s->nbytes < le*wi)) then s = NewString(le,wi);
    s->count=le;
    s->width=wi;
    short*k=(short*) strptr(s);
    *k=a;
    return StringTransfer(r,s);
  end


  method StringObject StringStr(StringObject*r, double d)
  begin
    int wi=1;
    int le=32;
    StringObject s=*r;
    if ((s==0)or(s->nbytes < le*wi)) then s = NewString(le,wi);
    char*k=strptr(s);
    sprintf(k,"%f",d);
    s->count=ByteLen(k);
    s->width=wi;
    return StringTransfer(r,s);
  end


  method int StringShow(StringObject this)
  begin
    CharArray w=strptr(this);
    printf("%s\n", w); // itr wide chars
  end


  method int StringAsc(StringObject this, int i)   // also supports wide chars
  begin
    if (i<0) then i+=this->count+1;            // offset from right
    if ((i>this->count)or(i<1)) then return 0; // out of range
    decr i;                                    // indexbase 0
    int wi=this->width;
    if (wi==1) then return *(strptr(this)+i);
    if (wi==2)
    begin
      short *a = (short*)  strptr(this)+i*2;
      return *a;
    end
  end


  method int StringInstr(int i, StringObject this,  StringObject k)
  //MATCHING MIXED WIDTHS BUT ONLY ON THE LOWER BYTE
  begin
    char*tb = strptr(this);
    char*tc=tb;
    char*kc=strptr(k);
    char*te=tc+this->count*this->width;
    char*ke=kc+k->count*k->width;
    if (i > this->count) then return 0;  // offset past end
    if (this->count==0) then return 0;  // null search string
    if (k->count==0) then return i;     // null keyword
    if (i<0) then i+=this->count+1;     // offset from right
    decr i;                             //indexbase 0
    tc+=i*this->width;                  // offset
    char*td;
    char*kd;
    int tw=this->width;
    int kw=k->width;
    while (tc<te)
    begin
      if (*tc==*kc)
      begin
        td=tc;
        kd=kc;     
        while (*td==*kd)
        begin
          td+=tw;
          kd+=kw;
          if (kd==ke) then return ((tc-tb)>>(tw-1))+1; // match complete
          if (td==te) then return 0;                   // end of main string
          if (*td==*tc) then tc=td-tw;                 // possible next match
        end
      end
      tc+=tw;
    end
  end

  method double StringVal(StringObject this)
  begin
  double d;
  sscanf(strptr(this),"%lf ",&d);
  return d;
  end


  method StringObject StringReplace(StringObject*pthis, StringObject f , StringObject r)
  begin
    StringObject this=*pthis;
    if ((this==0)or(f==0)or(r==0)) then return this;
    if ((this->count==0)or(f->count==0)) then return this;
    int n=0;
    int i=1;
    while (i)
    begin
      i=StringInstr(i,this,f);
      if (i)
      begin
        n++;
        i+=f->count;
      end
    end
    n *= (r->count - f->count);
    int tot = n + this->count;
    StringObject c = NewString(tot, this->width);
    char* m = strptr(c);
    int b=1,j=1;
    while (j)
    begin
      j=StringInstr(b,this,f);
      if (j)
      begin
        if (j>b) then CopyWidth(m, c->width,strptr(this)+b-1,this->width,j-b);
        m += c->width * (j-b);
        CopyWidth(m,c->width,strptr(r),r->width,r->count);
        m += c->width * r->count;
        b=j + f->count; // skip by keyword length
      end
    end
    // remainder
    j = this->count + 1;
    if (j>b)
    begin
      CopyWidth(m,c->width,strptr(this)+b-1,this->width,j-b);
      m += c->width * (j-b);
    end
    m[0]=0; // terminating null byte
    m[1]=0; // terminating null byte (extra for wide strings)
    return StringTransfer(pthis,c);
  end


  function StringMethods StringClassTableBuild()
  begin
    static StringClassTable t;
    StringMethods vm references t;
    vm->Redim    references method StringRedim;
    vm->Set      references method StringSet;
    vm->GetChars references method StringGetChars;
    vm->SetChars references method StringSetChars;
    vm->GetMid   references method StringGetMid;
    vm->SetMid   references method StringSetMid;
    vm->Join     references method StringJoin;
    vm->Repeat   references method StringRepeat;
    vm->Replace  references method StringReplace;
    vm->Ucase    references method StringUcase;
    vm->Lcase    references method StringLcase;
    vm->Ltrim    references method StringLtrim;
    vm->Rtrim    references method StringRtrim;
    vm->Insert   references method StringInsert;
    vm->Delete   references method StringDelete;
    vm->Chr      references method StringChr;
    vm->Str      references method StringStr;
    vm->Show     references method StringShow;
    vm->Asc      references method StringAsc;
    vm->Instr    references method StringInstr;
    vm->Val      references method StringVal;
    return vm;
  end

  function TestStrings()
  begin
    dim as int           char8=1, char16=2, all=-1;
    dim as StringObject  uo=NewString(0,char8);
    dim as StringObject  vo=NewString(0,char8);
    dim as StringObject  wo=NewString(100,char8);
    dim as StringMethods sm=vo->StringMethods;
    
    sm->Show(vo);
    sm->SetChars(&uo,"LO",all,char8);
    sm->SetChars(&vo," HellO ",all,char8);
    sm->Ltrim(&vo,vo);
    sm->Rtrim(&vo,vo);
    sm->Ucase(&vo,vo);
    StringObject c1[]={vo,vo,vo};
    sm->Join(3,&wo,c1);
    sm->Show(uo);
    sm->Show(vo);
    sm->Show(wo);
    printf("%i\n",sm->Instr(6,wo,uo));
    sm->Lcase(&wo,wo);
    sm->Insert(&wo,uo,6);
    sm->Show(wo);
    sm->Delete(&wo,6,2);
    sm->Show(wo);
    sm->Str(&vo,42.0);
    sm->Show(vo);
    double v=sm->Val(vo);
    printf("%f\n",v);
    sm->SetChars(&vo,"Abc",all,char8);
    sm->Repeat(&wo,vo,7);
    sm->Show(wo);
    sm->Replace(&wo,vo,uo);
    sm->Show(wo);
    sm->Replace(&wo,uo,vo);
    sm->Show(wo);
    sm->SetChars(&uo,"<",all,char8);
    sm->SetChars(&vo,">",all,char8);
    sm->SetChars(&wo,"  Hello World! ",all,char8);
    sm->Show(uo);
    sm->Show(vo);
    sm->Show(wo);
    StringObject c2[]={uo,sm->Ltrim(&wo,sm->Rtrim(&wo,sm->Ucase(&wo,wo))),vo};
    sm->Join(3,&wo,c2);
    sm->Show(wo);
    StringObject c3[3]={uo,wo,vo};
    sm->Join(3,&wo,c3);
    sm->Set(&vo,wo);
    sm->Show(vo);
    sm->GetMid(&vo,wo,3,6,1);
    sm->Show(vo);
    sm->SetMid(&vo,6,vo);
    sm->Show(vo);


    StringFree(&uo);
    StringFree(&vo);
    StringFree(&wo);
  end

  function int main()
  begin
    TestStrings();
  end
