#include <amsstring4/amsstring4.hpp>

namespace ams
{

    ams_chartype tolower(ams_chartype c)
    {
      unsigned char c2 = (unsigned char) c;
      ams_chartype ret;
      if(c2>=65&&c2<=90)
      {
        ret = (ams_chartype) (c2+(97-65));
      }
      else
      {
        ret = c;
      }
      return ret;
    }
    
    ams_chartype toupper(ams_chartype c)
    {
      unsigned char c2 = (unsigned char) c;
      ams_chartype ret;
      if(c2>=97&&c2<=122)
      {
        ret = (ams_chartype) (c2-(97-65));
      }
      else
      {
        ret = c;
      }
      return ret;
    }
    
    static int amsstring_strlen(const ams_chartype *c)
    {
      int I;
      if(c!=NULL)
      {
        I = 0;
        while(c[I]!='\0')
        {
          I++;
        }
        return I;
        //return strlen((const char*) c);
      }
      else
        return 0;
    }
    
    bool amsstrneq(const ams_chartype *s1, const ams_chartype *s2, const bool casesens=1)
    {
      bool ret = 0;
      int N1 = amsstring_strlen(s1);
      int N2 = amsstring_strlen(s2);
      bool b1,b2;
    
      if(N1==N2)
      {
        int I;
        bool q = 1;
        for(I=0;I<N1;I++)
        {
          b1 = casesens&&(s1[I]!=s2[I]);
          b2 = (!casesens)&&(ams::tolower(s1[I])!=ams::tolower(s2[I]));
          if(b1 || b2)
          {
            q = 0;
            break;
          }
        }
        if(q==1)
        {
          ret = 1;
        }
        else
        {
          ret = 0;
        }
      }
      return ret;
    }
    
    
    void _amsstring_init(amsstring* str)
    {
      str->blank = (ams_chartype) '\0';
      str->cstring = NULL;
      str->length = 0;
      str->cstring = new(std::nothrow) ams_chartype[1];

      if(str->cstring==NULL)
      {
        printf("_amsstring_init error: cstring buffer failed to initialize.\n");
      }
      else
      {
        str->cstring[0] = (ams_chartype) '\0';
      }
      
      return;
    }

    amsstring::amsstring()
    {
        _amsstring_init(this);

    
        return;
    }
    
    amsstring::~amsstring()
    {
        if(cstring!=NULL)
        {
            delete[] cstring;
            cstring = NULL;
        }
        length = 0;
        blank = '\0';
        
        return;
    }
    
    int amsstring::size() const
    {
        return length;
    }

    int amsstring::resize(int _newlen)
    {
      int ret = amsstring_success;
      ams_chartype *newcstring = NULL;
      int I;

      _newlen = (_newlen<0) ? 0 : _newlen;

      newcstring = new(std::nothrow) ams_chartype[_newlen+1];
      if(newcstring==NULL)
      {
        ret = amsstring_failure;
        return ret;
      }

      if(cstring!=NULL)
      {
        for(I=0;I<(_newlen+1) && I<(length+1);I++)
        {
          newcstring[I] = cstring[I];
        }
      }
      for(I=length;I<_newlen+1;I++)
      {
        newcstring[I] = (ams_chartype) '\0';
      }

      newcstring[_newlen] = '\0';

      if(cstring!=NULL)
      {
        delete[] cstring;
        cstring=NULL;
      }

      blank = (ams_chartype) '\0';
      cstring = newcstring;
      length = _newlen;

      return ret;
    }
    
    amsstring::amsstring(const amsstring &other)
    {
      int I;
      int res;
      _amsstring_init(this);

      if(this!=&other)
      {
        res = this->resize(other.length);
        if(res==amsstring_success)
        { 
          for(I=0;I<other.length;I++)
          {
            this->cstring[I] = other.cstring[I];
          }
          this->cstring[length] = '\0';
          blank = '\0';
        }
      }

      return;
    }

    amsstring& amsstring::operator=(const amsstring& other)
    {
      int I;
      int res;

      if(this!=&other)
      {
        res = this->resize(other.length);
        if(res==amsstring_success)
        { 
          for(I=0;I<other.length;I++)
          {
            this->cstring[I] = other.cstring[I];
          }
          this->cstring[length] = '\0';
          blank = '\0';
        }
      }

      return *this;
    }

    amsstring::amsstring(amsstring &&other) noexcept
    {
      
      this->length = 0;
      this->blank = '\0';
      this->cstring = NULL;

      if(this!=&other)
      {
        this->length = other.length;
        this->blank = other.blank;
        this->cstring = other.cstring;

        other.length = 0;
        other.blank = '\0';
        other.cstring = NULL;
      }

      return;
    }

    amsstring& amsstring::operator=(amsstring &&other) noexcept
    {

      if(this!=&other)
      {
        this->length = other.length;
        this->blank = other.blank;
        this->cstring = other.cstring;

        other.length = 0;
        other.blank = '\0';
        other.cstring = NULL;
      }

      return *this;
    }

    amsstring::amsstring(const ams_chartype *other)
    {
      int len = amsstring_strlen(other);
      int res;
      int I;

      _amsstring_init(this);

      len = amsstring_strlen(other);
      res = this->resize(len);
      if(res==amsstring_success)
      {
        for(I=0;I<length;I++) cstring[I] = other[I];
        cstring[length] = '\0';
        blank = '\0';
      }

      return;
      
    }

    const amsstring& amsstring::operator=(const ams_chartype *other)
    {
      int len = amsstring_strlen(other);
      int res;
      int I;

      len = amsstring_strlen(other);
      res = this->resize(len);
      if(res==amsstring_success)
      {
        for(I=0;I<length;I++) cstring[I] = other[I];
        cstring[length] = '\0';
        blank = '\0';
      }

      return *this;
    }
    
  
    ams_chartype& amsstring::operator[](const int ind)
    {
        if(ind<0 || ind>=(length+1))
        {
          blank = (ams_chartype) '\0';
          return blank;
        }
        else
        {
          return cstring[ind]; //cstring buffer is of length length+1
        }
    
    }
    
    const ams_chartype& amsstring::operator[](const int ind) const
    {
        if(ind<0 || ind>=(length+1))
        {
          //blank = (ams_chartype) '\0';
          return blank;
        }
        else
        {
          return cstring[ind]; //cstring buffer is of length length+1
        }
    }
    
    ams_chartype& amsstring::at(const int ind)
    {
        if(ind<0 || ind>=length+1)
        {
            blank = (ams_chartype) '\0';
            return blank;
        }
        else
        {
            return cstring[ind];
        }
    }
    
    const ams_chartype& amsstring::at(const int ind) const
    {
        if(ind<0 || ind>=length+1)
        {
            //blank = (ams_chartype) '\0';
            return blank;
        }
        else
        {
            return cstring[ind];
        }
    }
    
    void amsstring::clear()
    {
        this->resize(0);
        return;
    }
    
    void amsstring::setall(const ams_chartype val, const int newlen)
    {
        int I;
        this->resize(newlen);
        for(I=0;I<newlen;I++)
        {
            cstring[I] = val;
        }
        cstring[newlen] = '\0';
        return;
    }
    
    //If, for whatever reason, the string ends up having some
    //null terminator with junk beyond the end of it, shrink to fit
    //to the null terminator (actual c string length)
    void amsstring::shrinktofit()
    {
        int N = amsstring_strlen(cstring);
        this->resize(N);
        return;
    }
    
    bool amsstring::operator==(const amsstring &other) const
    {
      bool ret = 0;
      if(this!=&other)
      {
        ret = amsstrneq(this->cstring,other.cstring);
      }
      else
      {
        ret = 1;
      }
      return ret;
    }
    
    bool amsstring::operator!=(const amsstring &other) const 
    {
      return !((*this)==other);
    }
    
    bool amsstring::operator==(const char *other) const 
    {
      bool ret = 0;
      ret = amsstrneq(this->cstring,other);
      return ret;
    }
    
    bool amsstring::operator!=(const char *other) const
    {
      return !((*this)==other);
    }
    
    static int _local_charcompare(const ams_chartype c1, const ams_chartype c2)
    {
      int ret = 0;
      //-1 - c1 < c2
      //0 - c1 == c2
      //1 c1 > c2
      unsigned char cc1 = (unsigned char) c1;
      unsigned char cc2 = (unsigned char) c2;
      if(cc1<cc2) ret = -1;
      if(cc1>cc2) ret = 1;
      return ret;
    }
    
    bool amsstring::operator<(const amsstring &other) const
    {
      bool ret = 0;
      bool gt = 0;
      int I;
      for(I=0;I<this->length&&I<other.length;I++)
      {
        if(_local_charcompare(this->cstring[I],other.cstring[I]) == -1)
        {
          ret = 1;
          break;
        }
        if(_local_charcompare(this->cstring[I],other.cstring[I]) == 1)
        {
          ret = 0;
          gt = 1;
          break;
        }
      }
      if(ret==0&&gt==0)
      {
        if(this->length>other.length)
        {
          ret = 0;
        }
        else if(this->length<other.length)
        {
          ret = 1;
        }
        else
        {
          ret = 0; //these are equal strings
        }
      }
    
      return ret;
    }
    
    bool amsstring::operator>(const amsstring &other) const
    {
      bool ret = 0;
      bool lt = 0;
      int I;
      for(I=0;I<this->length&&I<other.length;I++)
      {
        if(_local_charcompare(this->cstring[I],other.cstring[I]) == -1)
        {
          ret = 0;
          lt = 1;
          break;
        }
        if(_local_charcompare(this->cstring[I],other.cstring[I]) == 1)
        {
          ret = 1;
          break;
        }
      }
      if(ret==0&&lt==0)
      {
        if(this->length>other.length)
        {
          ret = 1;
        }
        else if(this->length<other.length)
        {
          ret = 0;
        }
        else
        {
          ret = 0;
        }
      }
    
      return ret;
    }
    
    bool amsstring::operator<(const ams_chartype *other) const
    {
      amsstring q = other;
      return (*this)<q;
    }
    
    bool amsstring::operator>(const ams_chartype *other) const
    {
      amsstring q = other;
      return (*this)>q;
    }
    
    //inserts a string into the current string at a specified location index
    //ind: [0,length] where 
    //  0 inserts the string before the first element
    //  length inserts the string after the last element [length-1]
    //  all other locations fail
    void amsstring::insert(const int ind, const amsstring other)
    {
        int I,l1,l2;
        l1 = this->length;
        l2 = other.length;
    
        I = 0;
        if(cstring==NULL) l1 = 0;
        if(other.cstring==NULL) l2 = 0;
    
        ams_chartype *newbuf=NULL;
    
        if(ind>=0 && ind<=this->length)
        {
            newbuf = new(std::nothrow) ams_chartype[l1+l2+1];
            if(newbuf==NULL)
            {
              //something went wrong with the resize
              return;
            }
    
            for(I=0;I<ind;I++)
            {
                newbuf[I] = cstring[I];
            }
            for(I=ind;I<ind+l2;I++)
            {
                newbuf[I] = other.cstring[I-ind];
            }
            for(I=ind+l2;I<l1+l2;I++)
            {
                newbuf[I] = cstring[I-l2];
            }
            newbuf[l1+l2] = '\0';
            *this = newbuf;
    
            delete[] newbuf; newbuf=NULL;
            this->shrinktofit();
        }
    
        return;
    }
    
    void amsstring::insert(const int ind, const ams_chartype *other)
    {
        amsstring q = other;
        insert(ind,q);
        return;
    }
    
    void amsstring::remove(const int ind)
    {
        if(ind>=0 && ind<length)
        {
            remove(ind,ind+1);
        }
        return;
    }
    
    
    void amsstring::remove(const int ind1, const int ind2)
    {
        int I1,I2,N,I;
        I1 = ams::max<int>(0,ind1);
        I2 = ams::min<int>(length,ind2);
        N = length-(I2-I1);
        ams_chartype *newbuf = NULL;
        if(N>0)
        {
            newbuf = new ams_chartype[N+1];
            for(I=0;I<I1;I++)
            {
              newbuf[I] = cstring[I];
            }
            for(I=I2;I<length;I++)
            {
              newbuf[I-I2+I1] = cstring[I];
            }
            newbuf[N] = '\0';
            *this = newbuf;
            delete[] newbuf;
        }
    
        return;
    }
    
    void amsstring::substring(const int ind1, const int ind2, amsstring *sout) const
    {
      int I1,I2,N,I;
      if(sout==this)
      {
        amsstring q;
        I1 = ams::max<int>(0,ind1);
        I2 = ams::min<int>(ind2,length);
        N = (I2-I1);
        if(N>0)
        {
          q.resize(N);
          for(I=I1;I<I2;I++)
          {
            q.cstring[I-I1] = cstring[I];
          }
          q.cstring[N] = '\0';
          *sout = q;
        }
        else
        {
          sout->resize(0);
        }
      }
      else
      {
        I1 = ams::max<int>(0,ind1);
        I2 = ams::min<int>(ind2,length);
        N = (I2-I1);
        if(N>0)
        {
          sout->resize(N);
          for(I=I1;I<I2;I++)
          {
            sout->cstring[I-I1] = cstring[I];
          }
          sout->cstring[N] = '\0';
        }
        else
        {
          sout->resize(0);
        }
      }
      sout->cstring[sout->length] = '\0';
    
      return;
    }
    
    void amsstring::append(const amsstring &other)
    {
      this->insert(this->length,other);
      return;
    }
    
    void amsstring::append(const ams_chartype *other)
    {
      this->insert(this->length,other);
      return;
    }
    
    void amsstring::append(const ams_chartype other)
    {
      amsstring q;
      q.resize(1);
      q.cstring[0] = other;
      this->insert(this->length,q);
      return;
    }
    
    int amsstring::find(const ams_chartype c, const int indstart, const bool casesens) const
    {
        int I;
        int ret = -1;
        bool b1,b2;
        for(I=indstart;I<length;I++)
        {
          b1 = casesens&&(cstring[I]==c);
          b2 = (!casesens)&&(ams::tolower(cstring[I])==ams::tolower(c));
          if(b1 || b2)
          {
            ret = I;
            break;
          }
        }
        return ret;
    }
    
    int amsstring::find(const amsstring substr, const int indstart, const bool casesens) const
    {
        int ret = -1;
        ret = find(substr.cstring,indstart,casesens);
        return ret;
    }
    
    int amsstring::find(const ams_chartype *str, const int indstart, const bool casesens) const
    {
        int I;
        int ret = -1;
        int J = 0;
        int N = ams::amsstring_strlen(str);
        bool cs;
        ams_chartype c1,c2;
    
        int mode=0;
        bool qf = 0;
        bool beq;
        bool b1,b2;
    
        I = 0;
        J = 0;
        while(I<length&&qf==0)
        {
          c1 = cstring[I];
          c2 = str[J];
          cs = 0;
          b1 = casesens&&(cstring[I]==str[J]);
          b2 = (!casesens)&&(ams::tolower(cstring[I])==ams::tolower(str[J]));
          beq = b1 || b2;
    
          if(mode==0&&beq)
          {
            mode = 1;
          }
          if(mode==0&&!beq)
          {
            cs = 1;
          }
          if(mode==1&&beq)
          {
            cs = 1;
            J = J + 1;
          }
          if(mode==1&&!beq)
          {
              J = 0;
              cs = 0;
              mode = 0;
          }
    
          if(J>=N)
          {
            ret = I-N+1;
            qf = 1;
            break;
          }
    
          if(cs)
          {
            I = I + 1;
          }
        }
    
        return ret;
    }
    
    void amsstring::tolower()
    {
      std::locale loc;
    
      int I;
      for(I=0;I<length;I++)
      {
        cstring[I] = (ams_chartype) ams::tolower(cstring[I]);
      }
      return;
    }
    
    void amsstring::toupper()
    {
      int I;
      for(I=0;I<length;I++)
      {
        cstring[I] = (ams_chartype) ams::toupper(cstring[I]);
      }
      return;
    }
    
    int amsstring::sprintf(int bufflen, const ams_chartype *formatstring, ...)
    {
        int ret = 0;
        ams_chartype *buff = NULL; //need to dynamically allocate with MSVC
        //ams_chartype buff[bufflen+1];
        va_list varargs;
    
        buff = new(std::nothrow) ams_chartype[bufflen+1];
        if(buff!=NULL)
        {
    
          va_start(varargs,formatstring);
          vsnprintf(buff,bufflen,formatstring,varargs);
          va_end(varargs);
          buff[bufflen] = '\0';
          //int N = ams::amsstring_strlen(buff);
          *this = buff;
    
          delete[] buff; buff=NULL;
        }
    
        return ret;
    }
    
    //also returns true for nan
    bool amsstring::isvalidnumber()
    {
      bool ret = 0;
      double d;
    
      int q;
      try
      {
        q = sscanf(this->cstring,"%lf",&d);
        if(q==0)
        {
          ret = 0;
        }
        else
        {
          ret = 1;
        }
      }
      catch(int e)
      {
        ret = 0;
      }
      return ret;
    }
    
    double amsstring::strtonum()
    {
      return amsstrtonum(this->cstring);
    }
    
    
    void splitlines(amsstring *s, std::vector<amsstring> *lns)
    {
      int I,I1,I2;
      amsstring q;
      int mode;
      ams_chartype c;
    
      lns->resize(0);
    
      I1 = 0;
      I = 0;
      mode = 0;
      while(I<s->size())
      {
        c = s->at(I);
        if(mode==0 && c==ams_char_cr)
        {
          mode=1;
          I2 = I;
        }
        else if(mode==0 && c==ams_char_lf)
        {
          mode = 0;
          I2 = I;
          s->substring(I1,I2,&q);
          lns->push_back(q);
          I1 = I+1;
        }
        else if(mode==1 && c==ams_char_lf)
        {
          mode = 0;
          s->substring(I1,I2,&q);
          lns->push_back(q);
          I1 = I+1;
        }
        else if(mode==1)
        {
          mode = 0;
        }
    
        I = I + 1;
      }
      s->substring(I1,s->length,&q);
      lns->push_back(q);
    
      return;
    }
    
    void splitlines(amsstring *s, ams::amsarray<amsstring> *lns)
    {
      int I,I1,I2;
      amsstring q;
      int mode;
      ams_chartype c;
    
      lns->resize(0);
    
      I1 = 0;
      I = 0;
      mode = 0;
      while(I<s->size())
      {
        c = s->at(I);
        if(mode==0 && c==ams_char_cr)
        {
          mode=1;
          I2 = I;
        }
        else if(mode==0 && c==ams_char_lf)
        {
          mode = 0;
          I2 = I;
          s->substring(I1,I2,&q);
          lns->push_back(q);
          I1 = I+1;
        }
        else if(mode==1 && c==ams_char_lf)
        {
          mode = 0;
          s->substring(I1,I2,&q);
          lns->push_back(q);
          I1 = I+1;
        }
        else if(mode==1)
        {
          mode = 0;
        }
    
        I = I + 1;
      }
      s->substring(I1,s->length,&q);
      lns->push_back(q);
    
      return;
    }
    
    /*
    void splitlines(amsstring *s, std::vector<amsstring> *lns)
    {
      //windows uses \r\n, linux uses \n as a line delimiter
      int I;
      amsstring q;
      lns->resize(0);
      int ind1 = 0;
      int ind2 = 0;
      int mode = 0;
      int cs;
      I = 0;
      ams_chartype c;
    
    
      while(I<s->length)
      {
        c = s->cstring[I];
        cs = 0; //has the character been 'consumed' by the state machine?
        if(c=='\n'&&mode==0&&cs==0)
        {
    
          cs = 1;
          ind2 = I-1;
          ind2 = ams::max<int>(0,ind2);
          s->substring(ind1,ind2+1,&q);
          lns->push_back(q);
          ind1 = I+1;
          ind2 = I+1;
          ind1 = ams::min<int>(s->length,ind1);
          ind2 = ams::min<int>(s->length,ind2);
        }
        if(c=='\r'&&mode==0&&cs==0)
        {
          cs = 1;
          mode = 1;
        }
        if(c=='\n'&&mode==1&&cs==0)
        {
          cs = 1;
          ind2 = I-2;
          ind2 = ams::max<int>(0,ind2);
          s->substring(ind1,ind2+1,&q);
          lns->push_back(q);
          ind1 = I+1;
          ind2 = I+1;
          ind1 = ams::min<int>(s->length,ind1);
          ind2 = ams::min<int>(s->length,ind2);
          mode = 0;
        }
        I = I + 1;
      }
      if(ind1<s->length)
      {
        s->substring(ind1,s->length+1,&q);
        lns->push_back(q);
      }
    
      return;
    }
    */
    
    void split(amsstring *s, const ams_chartype delimitchar, std::vector<amsstring> *lns)
    {
      amsstring q;
      lns->resize(0);
    
    
      int I = 0;
      int ind1;
      int ind2;
      //int cs;
      ind1 = 0;
      ams_chartype c;
      while(I<s->length)
      {
        c = s->cstring[I];
        //cs = 0;
        if(c==delimitchar)
        {
          ind2 = I;
          ind2 = max<int>(0,ind2);
          s->substring(ind1,ind2,&q);
          lns->push_back(q);
          ind1 = I+1;
          ind2 = I+1;
          ind1 = min<int>(s->length,ind1);
          ind2 = min<int>(s->length,ind2);
        }
    
        I = I + 1;
      }
      if(ind1<s->length&&s->cstring[ind1]!=delimitchar)
      {
        s->substring(ind1,s->length,&q);
        lns->push_back(q);
      }
    
      return;
    }
    
    void split(amsstring *s, const ams_chartype delimitchar, ams::amsarray<amsstring> *lns)
    {
      amsstring q;
      lns->resize(0);
    
    
      int I = 0;
      int ind1;
      int ind2;
      //int cs;
      ind1 = 0;
      ams_chartype c;
      while(I<s->length)
      {
        c = s->cstring[I];
        //cs = 0;
        if(c==delimitchar)
        {
          ind2 = I;
          ind2 = max<int>(0,ind2);
          s->substring(ind1,ind2,&q);
          lns->push_back(q);
          ind1 = I+1;
          ind2 = I+1;
          ind1 = min<int>(s->length,ind1);
          ind2 = min<int>(s->length,ind2);
        }
    
        I = I + 1;
      }
      if(ind1<s->length&&s->cstring[ind1]!=delimitchar)
      {
        s->substring(ind1,s->length,&q);
        lns->push_back(q);
      }
    
      return;
    }
    
    void split(amsstring *s, const ams_chartype *delimitstr, std::vector<amsstring> *lns)
    {
      amsstring q;
      int I;
      int ind1,ind2;
      int cs;
      int cnt;
      ams_chartype c;
      int N = amsstring_strlen(delimitstr);
      
      if(lns!=NULL && delimitstr!=NULL)
      {
        if(N>0)
        {
          lns->resize(0);
    
          ind1 = 0;
          I = 0;
          cs = 0;
          cnt = 0;
          while(I<s->size())
          {
            c = s->at(I);
            if(cs==0 && c==delimitstr[cnt])
            {
              cs=1;
              ind2 = I;
              cnt = cnt+1;
            }
            else if(cs==1 && c==delimitstr[cnt])
            {
              cs=1;
              cnt = cnt+1;
            }
            else if(cs==1 && c!=delimitstr[cnt])
            {
              cs = 0;
              ind2 = ind1;
              cnt = 0;
              I = I - 1; //step back so you can evaluate this char again
            }
    
            if(cnt>=N)
            {
              //cut string from (ind1,ind2)
              cs = 0;
    
              ind1 = min<int>(ind1,s->size());
              ind1 = max<int>(ind1,0);
              ind2 = min<int>(ind2,s->size());
              ind2 = max<int>(ind2,0);
              ind2 = max<int>(ind2,ind1);
    
              s->substring(ind1,ind2,&q);
              lns->push_back(q);
              
              ind1 = I + 1;
              ind2 = I + 1;
              cnt = 0;
            }
            I = I + 1;
          }
    
          if(ind1<s->size())
          {
            s->substring(ind1,s->size(),&q);
            lns->push_back(q);
          }
        } //N>0
        else
        {
          lns->push_back(*s);
        }
      } //lns!=NULL
    
      return;
    }
    
    void split(amsstring *s, const ams_chartype *delimitstr, ams::amsarray<amsstring> *lns)
    {
      amsstring q;
      int I;
      int ind1,ind2;
      int cs;
      int cnt;
      ams_chartype c;
      int N = amsstring_strlen(delimitstr);
      
      if(lns!=NULL && delimitstr!=NULL)
      {
        if(N>0)
        {
          lns->resize(0);
    
          ind1 = 0;
          I = 0;
          cs = 0;
          cnt = 0;
          while(I<s->size())
          {
            c = s->at(I);
            if(cs==0 && c==delimitstr[cnt])
            {
              cs=1;
              ind2 = I;
              cnt = cnt+1;
            }
            else if(cs==1 && c==delimitstr[cnt])
            {
              cs=1;
              cnt = cnt+1;
            }
            else if(cs==1 && c!=delimitstr[cnt])
            {
              cs = 0;
              ind2 = ind1;
              cnt = 0;
              I = I - 1; //step back so you can evaluate this char again
            }
    
            if(cnt>=N)
            {
              //cut string from (ind1,ind2)
              cs = 0;
    
              ind1 = min<int>(ind1,s->size());
              ind1 = max<int>(ind1,0);
              ind2 = min<int>(ind2,s->size());
              ind2 = max<int>(ind2,0);
              ind2 = max<int>(ind2,ind1);
    
              s->substring(ind1,ind2,&q);
              lns->push_back(q);
              
              ind1 = I + 1;
              ind2 = I + 1;
              cnt = 0;
            }
            I = I + 1;
          }
    
          if(ind1<s->size())
          {
            s->substring(ind1,s->size(),&q);
            lns->push_back(q);
          }
        } //N>0
        else
        {
          lns->push_back(*s);
        }
      } //lns!=NULL
    
      return;
    }
    
    void split(amsstring *s, amsstring *delimitstr, std::vector<amsstring> *lns)
    {
      split(s,delimitstr->cstring,lns);
      return;
    }
    
    void split(amsstring *s, amsstring *delimitstr, ams::amsarray<amsstring> *lns)
    {
      split(s,delimitstr->cstring,lns);
      return;
    }
    
    
    void splitwhitespace(amsstring *s, std::vector<amsstring> *lns)
    {
      int I;
      int mode;
      ams_chartype c;
      int ind1 = 0;
      int ind2 = 0;
      I = 0;
      mode = 0;
      amsstring q;
      bool qf = 0;
    
      if(s!=NULL && lns!=NULL)
      {
        lns->resize(0);
        while(qf==0)
        {
          c = s->cstring[I];
          if(!(isspace(c)||c==' '||c=='\t')&&mode==0)
          {
            mode = 1;
            ind1 = I;
            ind1 = ams::max<int>(0,ind1);
          }
          if((isspace(c)||c==' '||c=='\t'||c=='\0')&&mode==1)
          {
            mode = 0;
            ind2 = I;
            ind2 = ams::min<int>(ind2,s->length);
            s->substring(ind1,ind2,&q);
            //printf("debug: %d, %s\n",I,q.cstring);
            lns->push_back(q);
          }
          if(I>=s->length)
          {
            qf = 1;
          }
    
          I = I + 1;
        }
      } //test null pointers
      return;
    }
    
    void splitwhitespace(amsstring *s, ams::amsarray<amsstring> *lns)
    {
      int I;
      int mode;
      ams_chartype c;
      int ind1 = 0;
      int ind2 = 0;
      I = 0;
      mode = 0;
      amsstring q;
      bool qf = 0;
    
      if(s!=NULL && lns!=NULL)
      {
        lns->resize(0);
        while(qf==0)
        {
          c = s->cstring[I];
          if(!(isspace(c)||c==' '||c=='\t')&&mode==0)
          {
            mode = 1;
            ind1 = I;
            ind1 = ams::max<int>(0,ind1);
          }
          if((isspace(c)||c==' '||c=='\t'||c=='\0')&&mode==1)
          {
            mode = 0;
            ind2 = I;
            ind2 = ams::min<int>(ind2,s->length);
            s->substring(ind1,ind2,&q);
            //printf("debug: %d, %s\n",I,q.cstring);
            lns->push_back(q);
          }
          if(I>=s->length)
          {
            qf = 1;
          }
    
          I = I + 1;
        }
      } //test null pointers
      return;
    }
    
    void stripwhitespace(amsstring *s)
    {
      int I;
      amsstring q;
      int ind1;
      int ind2;
      ams_chartype c;
    
      if(s!=NULL)
      { 
        ind1 = 0;
        ind2 = 0;
        for(I=0;I<s->length;I++)
        {
          c = s->cstring[I];
          if(!(isspace(c)||c==' '||c=='\t'))
          {
            ind1 = I;
            break;
          }
        }
        for(I=s->length-1;I>=0;I--)
        {
          c = s->cstring[I];
          if(!(isspace(c)||c==' '||c=='\t'))
          {
            ind2 = I+1;
            break;
          }
        }
    
        ind1 = min<int>(ind1,s->size());
        ind1 = max<int>(ind1,0);
        ind2 = min<int>(ind2,s->size());
        ind2 = max<int>(ind2,0);
        ind2 = max<int>(ind1,ind2);
    
        q = *s; //q.copy(s);
        q.substring(ind1,ind2,s);
      } //null guard
      return;
    }
    
    void stripallwhitespace(amsstring *s)
    {
      amsstring q;
      q.resize(s->length);
      //int count;
      int I;
      int J;
      J = 0;
      ams_chartype c;
      for(I=0;I<s->length;I++)
      {
        c = s->cstring[I];
        if(!(isspace(c)||c==' '||c=='\t'||c=='\n'||c=='\r'||c=='\f'||c=='\v'||(int)c==9||(int)c==10||(int)c==11||(int)c==12))
        {
          q.cstring[J] = s->cstring[I];
          J = J + 1;
        }
      }
      q.resize(J);
      q.cstring[q.length]='\0';
      //s->copy(&q);
      *s = q;
      return;
    }
    
    ///File IO
    
    void freadline(FILE *fp,amsstring *s)
    {
      const int BUFFSZ = 8000;
      char buff[BUFFSZ];
      int N;
      if(fp!=NULL && s!=NULL)
      {
        if(!feof(fp))
        {
          fgets(buff,BUFFSZ,fp);
          N = ams::amsstring_strlen(buff);
          if((N-1)>=0&&(buff[N-1]=='\n'))
          {
            buff[N-1] = '\0';
            N = N - 1;
          }
          if((N-1)>=0&&(buff[N-1]=='\r'))
          {
            buff[N-1] = '\0';
            N = N-1;
          }
          N = ams::amsstring_strlen(buff);
          *s = buff; //s->copy(buff);
        }
        else
        {
          s->resize(0);
        }
      }//null guards
      return;
    }
    
    void freadlines(FILE *fp, std::vector<amsstring> *lines)
    {
      amsstring ln;
      //note - this does not resize the lines to zero, just adds to them
      if(fp!=NULL && lines!=NULL)
      {
        while(!feof(fp))
        {
          freadline(fp,&ln);
          lines->push_back(ln);
        }
      }
      return;
    }
    
    void fwritelines(FILE *fp, amsstring *s)
    {
      if(fp!=NULL && s!=NULL)
      {
        fputs(s->cstring,fp);
      }
      return;
    }
    
    void fwritelines(FILE *fp, std::vector<amsstring> *lines)
    {
      int I;
      if(fp!=NULL && lines!=NULL)
      {
        for(I=0;I<lines->size();I++)
        {
          fputs(lines->at(I).cstring,fp);
          if(I<lines->size()-1) fputs("\n",fp);
        }
      }
      return;
    }
    
    void freadtxtfile(FILE *fp, amsstring *s)
    {
      amsstring q;
      if(fp!=NULL&&s!=NULL)
      {
        while(!feof(fp))
        {
          freadline(fp,&q);
          if(!feof(fp))
          {
            q.append("\n");
            s->append(q);
          }
        }
      }
      return;
    }
    
    amsstring amsstring::operator+(const amsstring &other)
    {
      amsstring ret;
      amsstring q1 = *this;
      amsstring q2 = other;
      ret = "";
      ret.append(q1);
      ret.append(q2);
      return ret;
    }
    
    const amsstring amsstring::operator+(const amsstring &other) const
    {
      amsstring ret;
      amsstring q1 = *this;
      amsstring q2 = other;
      ret = "";
      ret.append(q1);
      ret.append(q2);
      return ret;
    }
    
    amsstring amsstring::operator+(const ams_chartype *other)
    {
      amsstring ret;
      amsstring q1 = *this;
      ret = "";
      ret.append(q1);
      ret.append(other);
      return ret;
    }
    
    const amsstring amsstring::operator+(const ams_chartype *other) const
    {
      amsstring ret;
      amsstring q1 = *this;
      ret = "";
      ret.append(q1);
      ret.append(other);
      return ret;
    }
    
    amsstring amsstring::operator+(const ams_chartype other)
    {
      amsstring ret;
      amsstring q1 = *this;
      ret = "";
      ret.append(q1);
      ret.append(other);
      return ret;
    }
    
    const amsstring amsstring::operator+(const ams_chartype other) const
    {
      amsstring ret;
      amsstring q1 = *this;
      ret = "";
      ret.append(q1);
      ret.append(other);
      return ret;
    }
    

};