/*! \file pistring.h
 * \ingroup Text
 * \brief
 * \~english String class
 * \~russian Класс строки
 */
/*
    PIP - Platform Independent Primitives
    String
    Ivan Pelipenko peri4ko@yandex.ru, Andrey Bychkov work.a.b@yandex.ru

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef PISTRING_H
#define PISTRING_H

#include "pibytearray.h"
#include "piconstchars.h"

#define PIStringAscii PIString::fromAscii


class PIStringList;
class PIRegularExpression;

//! \~\ingroup Text
//! \~\brief
//! \~english String class.
//! \~russian Класс строки.
class PIP_EXPORT PIString {
	BINARY_STREAM_FRIEND(PIString);

public:
	typedef PIDeque<PIChar>::iterator iterator;
	typedef PIDeque<PIChar>::const_iterator const_iterator;
	typedef PIDeque<PIChar>::reverse_iterator reverse_iterator;
	typedef PIDeque<PIChar>::const_reverse_iterator const_reverse_iterator;
	typedef PIChar value_type;
	typedef PIChar * pointer;
	typedef const PIChar * const_pointer;
	typedef PIChar & reference;
	typedef const PIChar & const_reference;
	typedef size_t size_type;

	//! \~english Constructs an empty string.
	//! \~russian Создает пустую строку.
	PIString() {}

	//! \~english Value for elide at left.
	//! \~russian Значение для пропуска слева.
	static const float ElideLeft;

	//! \~english Value for elide at center.
	//! \~russian Значение для пропуска в середине.
	static const float ElideCenter;

	//! \~english Value for elide at right.
	//! \~russian Значение для пропуска справа.
	static const float ElideRight;

	PIString & operator+=(const PIChar & c) {
		d.push_back(c);
		changed_ = true;
		return *this;
	}
	PIString & operator+=(const char c) {
		d.push_back(PIChar(c));
		changed_ = true;
		return *this;
	}
	PIString & operator+=(const char * str);
	PIString & operator+=(const wchar_t * str);
	PIString & operator+=(const char16_t * str);
	PIString & operator+=(const PIString & str);
	PIString & operator+=(const PIConstChars & str);

	PIString(uchar)               = delete;
	PIString(short)               = delete;
	PIString(ushort)              = delete;
	PIString(int)                 = delete;
	PIString(uint)                = delete;
	PIString(long)                = delete;
	PIString(ulong)               = delete;
	PIString(llong)               = delete;
	PIString(ullong)              = delete;

	PIString & operator+=(uchar)  = delete;
	PIString & operator+=(short)  = delete;
	PIString & operator+=(ushort) = delete;
	PIString & operator+=(int)    = delete;
	PIString & operator+=(uint)   = delete;
	PIString & operator+=(long)   = delete;
	PIString & operator+=(ulong)  = delete;
	PIString & operator+=(llong)  = delete;
	PIString & operator+=(ullong) = delete;

	//! \~english Constructs a copy of string.
	//! \~russian Создает копию строки.
	PIString(const PIString & o) {
		d        = o.d;
		changed_ = true;
	}

	//! \~english Move constructor.
	//! \~russian Перемещающий конструктор.
	PIString(PIString && o): d(std::move(o.d)) {
		piSwap(data_, o.data_);
		changed_ = true;
	}

	//! \~english Contructs string with single character "c".
	//! \~russian Создает строку из одного символа "c".
	PIString(const PIChar c) { d.push_back(c); }

	//! \~english Contructs string with single character "c".
	//! \~russian Создает строку из одного символа "c".
	PIString(const char c) { d.push_back(PIChar(c)); }

	//! \~english Contructs string from C-string "str" (system codepage).
	//! \~russian Создает строку из C-строки "str" (кодировка системы).
	//! \~\details
	//! \~english
	//!  "str" should be null-terminated\n
	//! \~russian
	//!  "str" должна заканчиваться нулевым байтом\n
	//! \~\code
	//! PIString s("string");
	//! \endcode
	PIString(const char * str) { *this += str; }

	//! \~english Contructs string from \c wchar_t C-string "str".
	//! \~russian Создает строку из \c wchar_t C-строки "str".
	//! \~\details
	//! \~english
	//!  "str" should be null-terminated
	//! \~russian
	//!  "str" должна заканчиваться нулевым \c wchar_t
	//! \~\code
	//! PIString s(L"string");
	//! \endcode
	PIString(const wchar_t * str) { *this += str; }

	//! \~english Contructs string from \c char16_t C-string "str".
	//! \~russian Создает строку из \c char16_t C-строки "str".
	//! \~\details
	//! \~english
	//!  "str" should be null-terminated
	//! \~russian
	//!  "str" должна заканчиваться нулевым \c char16_t
	//! \~\code
	//! PIString s(u"string");
	//! \endcode
	PIString(const char16_t * str) { *this += str; }

	//! \~english Contructs string from "len" characters of buffer "str".
	//! \~russian Создает строку из "len" символов массива "str".
	PIString(const PIChar * str, const int len): d(str, size_t(len)) {}

	//! \~english Contructs string from "len" characters of buffer "str" (system codepage).
	//! \~russian Создает строку из "len" символов массива "str" (кодировка системы).
	//! \~\details
	//! \~\code
	//! PIString s("string", 3); // s = "str"
	//! \endcode
	PIString(const char * str, const int len) { appendFromChars(str, len); }

	//! \~english Contructs string as sequence of characters "c" of buffer with length "len".
	//! \~russian Создает строку как последовательность длиной "len" символа "c".
	//! \~\details
	//! \~\code
	//! PIString s(5, 'p'); // s = "ppppp"
	//! \endcode
	PIString(const int len, const char c) {
		for (int i = 0; i < len; ++i)
			d.push_back(PIChar(c));
	}

	//! \~english Contructs string as sequence of characters "c" of buffer with length "len".
	//! \~russian Создает строку как последовательность длиной "len" символа "c".
	//! \~\details
	//! \~\code
	//! PIString s(5, "№"); // s = "№№№№№"
	//! \endcode
	PIString(const int len, const PIChar c) {
		for (int i = 0; i < len; ++i)
			d.push_back(c);
	}

	PIString(const PIConstChars & c) { *this += c; }

	~PIString();

	//! \~english Assign operator.
	//! \~russian Оператор присваивания.
	PIString & operator=(const PIString & o) {
		if (this == &o) return *this;
		d        = o.d;
		changed_ = true;
		return *this;
	}

	//! \~english Assign move operator.
	//! \~russian Оператор перемещающего присваивания.
	PIString & operator=(PIString && o) {
		d.swap(o.d);
		piSwap(data_, o.data_);
		changed_ = true;
		return *this;
	}

	//! \~english Assign operator.
	//! \~russian Оператор присваивания.
	PIString & operator=(const PIConstChars & o) {
		d.clear();
		*this += o;
		return *this;
	}

	//! \~english Assign operator.
	//! \~russian Оператор присваивания.
	PIString & operator=(const char * o) {
		d.clear();
		*this += o;
		return *this;
	}

	//! \~english Assign operator.
	//! \~russian Оператор присваивания.
	PIString & operator=(const char16_t * o) {
		d.clear();
		*this += o;
		return *this;
	}

	//! \~english Compare operator.
	//! \~russian Оператор сравнения.
	bool operator==(const PIString & str) const;

	//! \~english Compare operator.
	//! \~russian Оператор сравнения.
	bool operator==(const PIChar c) const {
		if (d.size() != 1) return false;
		return d.at(0) == c;
	}

	//! \~english Compare operator.
	//! \~russian Оператор сравнения.
	bool operator==(const char * str) const { return *this == PIString(str); }

	//! \~english Compare operator.
	//! \~russian Оператор сравнения.
	bool operator!=(const PIString & str) const;

	//! \~english Compare operator.
	//! \~russian Оператор сравнения.
	bool operator!=(const PIChar c) const {
		if (d.size() != 1) return true;
		return d.at(0) != c;
	}

	//! \~english Compare operator.
	//! \~russian Оператор сравнения.
	bool operator!=(const char * str) const { return *this != PIString(str); }

	//! \~english Compare operator.
	//! \~russian Оператор сравнения.
	bool operator<(const PIString & str) const;

	//! \~english Compare operator.
	//! \~russian Оператор сравнения.
	bool operator<(const PIChar c) const {
		if (d.size() != 1) return d.size() < 1;
		return d.at(0) < c;
	}

	//! \~english Compare operator.
	//! \~russian Оператор сравнения.
	bool operator<(const char * str) const { return *this < PIString(str); }

	//! \~english Compare operator.
	//! \~russian Оператор сравнения.
	bool operator>(const PIString & str) const;

	//! \~english Compare operator.
	//! \~russian Оператор сравнения.
	bool operator>(const PIChar c) const {
		if (d.size() != 1) return d.size() > 1;
		return d.at(0) > c;
	}

	//! \~english Compare operator.
	//! \~russian Оператор сравнения.
	bool operator>(const char * str) const { return *this > PIString(str); }

	//! \~english Compare operator.
	//! \~russian Оператор сравнения.
	bool operator<=(const PIString & str) const { return !(*this > str); }

	//! \~english Compare operator.
	//! \~russian Оператор сравнения.
	bool operator<=(const PIChar c) const { return !(*this > c); }

	//! \~english Compare operator.
	//! \~russian Оператор сравнения.
	bool operator<=(const char * str) const { return *this <= PIString(str); }

	//! \~english Compare operator.
	//! \~russian Оператор сравнения.
	bool operator>=(const PIString & str) const { return !(*this < str); }

	//! \~english Compare operator.
	//! \~russian Оператор сравнения.
	bool operator>=(const PIChar c) const { return !(*this < c); }

	//! \~english Compare operator.
	//! \~russian Оператор сравнения.
	bool operator>=(const char * str) const { return *this >= PIString(str); }

	//! \~english Iterator to the first element.
	//! \~russian Итератор на первый элемент.
	//! \~\details
	//! \~\return \ref stl_iterators
	//! \~\sa \a end(), \a rbegin(), \a rend()
	inline iterator begin() { return d.begin(); }

	//! \~english Iterator to the element following the last element.
	//! \~russian Итератор на элемент, следующий за последним элементом.
	//! \~\details
	//! \~\return \ref stl_iterators
	//! \~\sa \a begin(), \a rbegin(), \a rend()
	inline iterator end() { return d.end(); }

	inline const_iterator begin() const { return d.begin(); }
	inline const_iterator end() const { return d.end(); }

	//! \~english Returns a reverse iterator to the first element of the reversed array.
	//! \~russian Обратный итератор на первый элемент.
	//! \~\details
	//! \~english It corresponds to the last element of the non-reversed array.
	//! \~russian Итератор для прохода массива в обратном порядке.
	//! Указывает на последний элемент.
	//! \~\return \ref stl_iterators
	//! \~\sa \a rend(), \a begin(), \a end()
	inline reverse_iterator rbegin() { return d.rbegin(); }

	//! \~english Returns a reverse iterator to the element.
	//! following the last element of the reversed array.
	//! \~russian Обратный итератор на элемент, следующий за последним элементом.
	//! \~\details
	//! \~english It corresponds to the element preceding the first element of the non-reversed array.
	//! \~russian Итератор для прохода массива в обратном порядке.
	//! Указывает на элемент, предшествующий первому элементу.
	//! \~\return \ref stl_iterators
	//! \~\sa \a rbegin(), \a begin(), \a end()
	inline reverse_iterator rend() { return d.rend(); }

	inline const_reverse_iterator rbegin() const { return d.rbegin(); }
	inline const_reverse_iterator rend() const { return d.rend(); }

	//! \~english Full access to character by `index`.
	//! \~russian Полный доступ к символу по индексу `index`.
	//! \~\details
	//! \~english Сharacter index starts from `0`.
	//! Сharacter index must be in range from `0` to `size()-1`.
	//! Otherwise will be undefined behavior.
	//! \~russian Индекс элемента считается от `0`.
	//! Индекс символа должен лежать в пределах от `0` до `size()-1`.
	//! Иначе это приведёт к неопределённому поведению программы и ошибкам памяти.
	inline PIChar & operator[](size_t index) {
		changed_ = true;
		return d[index];
	}
	inline PIChar operator[](size_t index) const { return d[index]; }

	//! \~english Read only access to character by `index`.
	//! \~russian Доступ исключительно на чтение к символу по индексу `index`.
	//! \~\details
	//! \~english Сharacter index starts from `0`.
	//! Сharacter index must be in range from `0` to `size()-1`.
	//! Otherwise will be undefined behavior.
	//! \~russian Индекс символа считается от `0`.
	//! Индекс символа должен лежать в пределах от `0` до `size()-1`.
	//! Иначе это приведёт к неопределённому поведению программы и ошибкам памяти.
	inline const PIChar at(size_t index) const { return d.at(index); }

	//! \~english Returns the last character of the string.
	//! \~russian Возвращает последний символ строки.
	inline PIChar & back() {
		changed_ = true;
		return d.back();
	}
	inline PIChar back() const { return d.back(); }

	inline PIChar & front() {
		changed_ = true;
		return d.front();
	}
	inline PIChar front() const { return d.front(); }

	//! \~english Sets size of the string, new characters are copied from `c`.
	//! \~russian Устанавливает размер строки, новые символы копируются из `c`.
	//! \~\details
	//! \~english If `new_size` is greater than the current \a size(),
	//!  characters are added to the end; the new characters are initialized from `c`.
	//! If `new_size` is less than the current \a size(), characters are removed from the end.
	//! \~russian Если `new_size` больше чем текущий размер строки \a size(),
	//! новые символы добавляются в конец строки и создаются из `с`.
	//! Если `new_size` меньше чем текущий размер строки \a size(),
	//! лишние символы удаляются с конца строки.
	//! \~\sa \a size(), \a clear()
	inline PIString & resize(size_t new_size, PIChar c = PIChar()) {
		d.resize(new_size, c);
		changed_ = true;
		return *this;
	}

	//! \~english Delete one character at the end of string.
	//! \~russian Удаляет один символ с конца строки.
	inline PIString & pop_back() {
		d.pop_back();
		changed_ = true;
		return *this;
	}

	//! \~english Delete one character at the benig of string.
	//! \~russian Удаляет один символ с начала строки.
	inline PIString & pop_front() {
		d.pop_front();
		changed_ = true;
		return *this;
	}

	//! \~english Removes `count` characters from the string, starting at `index` position.
	//! \~russian Удаляет символы из строки, начиная с позиции `index` в количестве `count`.
	inline PIString & remove(size_t index, size_t count = 1) {
		d.remove(index, count);
		changed_ = true;
		return *this;
	}

	//! \~english Assigns character 'c' to all string characters.
	//! \~russian Заполняет всю строку символами `c`.
	inline PIString & fill(PIChar c = PIChar()) {
		d.fill(c);
		changed_ = true;
		return *this;
	}

	//! \~english Insert string "str" at the begin of string.
	//! \~russian Вставляет "str" в начало строки.
	PIString & prepend(const char * str) {
		insert(0, str);
		return *this;
	}

	//! \~english Insert string "str" at the begin of string.
	//! \~russian Вставляет "str" в начало строки.
	PIString & prepend(const PIString & str) {
		d.prepend(str.d);
		changed_ = true;
		return *this;
	}

	//! \~english Insert character `c` at the begin of string.
	//! \~russian Вставляет символ `c` в начало строки.
	PIString & prepend(const PIChar c) {
		d.prepend(c);
		changed_ = true;
		return *this;
	}

	//! \~english Insert character `c` at the begin of string.
	//! \~russian Вставляет символ `c` в начало строки.
	PIString & prepend(const char c) {
		d.prepend(PIChar(c));
		changed_ = true;
		return *this;
	}

	//! \~english Insert string "str" at the begin of string.
	//! \~russian Вставляет "str" в начало строки.
	PIString & push_front(const char * str) {
		insert(0, str);
		changed_ = true;
		return *this;
	}

	//! \~english Insert string "str" at the begin of string.
	//! \~russian Вставляет "str" в начало строки.
	PIString & push_front(const char16_t * str) {
		insert(0, str);
		return *this;
	}

	//! \~english Insert string "str" at the begin of string.
	//! \~russian Вставляет "str" в начало строки.
	PIString & push_front(const PIString & str) {
		d.push_front(str.d);
		changed_ = true;
		return *this;
	}

	//! \~english Insert character `c` at the begin of string.
	//! \~russian Вставляет символ `c` в начало строки.
	PIString & push_front(const PIChar c) {
		d.push_front(c);
		changed_ = true;
		return *this;
	}

	//! \~english Insert character `c` at the begin of string.
	//! \~russian Вставляет символ `c` в начало строки.
	PIString & push_front(const char c) {
		d.push_front(PIChar(c));
		changed_ = true;
		return *this;
	}

	//! \~english Insert string "str" at the end of string.
	//! \~russian Вставляет "str" в конец строки.
	PIString & append(const char * str) {
		*this += str;
		return *this;
	}

	//! \~english Insert string "str" at the end of string.
	//! \~russian Вставляет "str" в конец строки.
	PIString & append(const char16_t * str) {
		*this += str;
		return *this;
	}

	//! \~english Insert string "str" at the end of string.
	//! \~russian Вставляет "str" в конец строки.
	PIString & append(const PIString & str) {
		d.append(str.d);
		changed_ = true;
		return *this;
	}

	PIString & append(const PIConstChars & str) {
		*this += str;
		return *this;
	}

	//! \~english Insert character `c` at the end of string.
	//! \~russian Вставляет символ `c` в конец строки.
	PIString & append(const PIChar c) {
		d.append(c);
		changed_ = true;
		return *this;
	}

	//! \~english Insert character `c` at the end of string.
	//! \~russian Вставляет символ `c` в конец строки.
	PIString & append(const char c) {
		d.append(PIChar(c));
		changed_ = true;
		return *this;
	}

	//! \~english Insert string "str" at the end of string.
	//! \~russian Вставляет "str" в конец строки.
	PIString & push_back(const char * str) {
		*this += str;
		return *this;
	}

	//! \~english Insert string "str" at the end of string.
	//! \~russian Вставляет "str" в конец строки.
	PIString & push_back(const PIString & str) {
		d.push_back(str.d);
		changed_ = true;
		return *this;
	}

	PIString & push_back(const PIConstChars & str) {
		*this += str;
		return *this;
	}

	//! \~english Insert character `c` at the end of string.
	//! \~russian Вставляет символ `c` в конец строки.
	PIString & push_back(const PIChar c) {
		d.push_back(c);
		changed_ = true;
		return *this;
	}

	//! \~english Insert character `c` at the end of string.
	//! \~russian Вставляет символ `c` в конец строки.
	PIString & push_back(const char c) {
		d.push_back(PIChar(c));
		changed_ = true;
		return *this;
	}


	//! \~english Returns part of string from character at index "start" and maximum length "len".
	//! \~russian Возвращает подстроку от символа "start" и максимальной длиной "len".
	PIString mid(int start, int len = -1) const;

	//! \~english Synonym of \a mid().
	//! \~russian Аналог \a mid().
	PIString subString(int start, int len = -1) const { return mid(start, len); }

	//! \~english Returns part of string from start and maximum length "len".
	//! \~russian Возвращает подстроку от начала и максимальной длиной "len".
	//! \~\details
	//! \~\code
	//! PIString s("0123456789");
	//! piCout << s.left(-1); // s = ""
	//! piCout << s.left(1);  // s = "0"
	//! piCout << s.left(5);  // s = "01234"
	//! piCout << s.left(15); // s = "0123456789"
	//! \endcode
	//! \~\sa \a mid(), \a right()
	PIString left(int len) const { return len <= 0 ? PIString() : mid(0, len); }

	//! \~english Returns part of string at end and maximum length "len".
	//! \~russian Возвращает подстроку максимальной длиной "len" и до конца.
	//! \~\details
	//! \~\code
	//! PIString s("0123456789");
	//! piCout << s.right(-1); // s = ""
	//! piCout << s.right(1);  // s = "9"
	//! piCout << s.right(5);  // s = "56789"
	//! piCout << s.right(15); // s = "0123456789"
	//! \endcode
	//! \~\sa \a mid(), \a left()
	PIString right(int len) const { return len <= 0 ? PIString() : mid(size() - len, len); }

	//! \~english Remove part of string from character as index "start" and maximum length "len" and return this string.
	//! \~russian Удаляет часть строки от символа "start" и максимальной длины "len", возвращает эту строку.
	PIString & cutMid(int start, int len);

	//! \~english Remove part of string from start and maximum length "len" and return this string.
	//! \~russian Удаляет часть строки от начала и максимальной длины "len", возвращает эту строку.
	//! \~\details
	//! \~\code
	//! PIString s("0123456789");
	//! s.cutLeft(1);
	//! piCout << s; // s = "123456789"
	//! s.cutLeft(3);
	//! piCout << s; // s = "456789"
	//! s.cutLeft(30);
	//! piCout << s; // s = ""
	//! \endcode
	//! \~\sa \a cutMid(), \a cutRight()
	PIString & cutLeft(int len) { return len <= 0 ? *this : cutMid(0, len); }

	//! \~english Remove part of string at end and maximum length "len" and return this string.
	//! \~russian Удаляет часть строки максимальной длины "len" от конца, возвращает эту строку.
	//! \~\details
	//! \~\code
	//! PIString s("0123456789");
	//! s.cutRight(1);
	//! piCout << s; // s = "012345678"
	//! s.cutRight(3);
	//! piCout << s; // s = "012345"
	//! s.cutRight(30);
	//! piCout << s; // s = ""
	//! \endcode
	//! \~\sa \a cutMid(), \a cutLeft()
	PIString & cutRight(int len) { return len <= 0 ? *this : cutMid(size() - len, len); }

	//! \~english Remove spaces at the start and at the end of string and return this string.
	//! \~russian Удаляет пробельные символы с начала и конца строки и возвращает эту строку.
	PIString & trim();

	//! \~english Returns copy of this string without spaces at the start and at the end.
	//! \~russian Возвращает копию этой строки без пробельных символов с начала и конца.
	//! \~\details
	//! \~\code
	//! PIString s(" \t string  \n");
	//! piCout << s.trimmed(); // s = "string"
	//! piCout << s; // s = "  string  "
	//! \endcode
	//! \~\sa \a trim()
	PIString trimmed() const;

	//! \~english Replace part of string from index "from" and maximum length "len" with string "with" and return this string.
	//! \~russian Заменяет часть строки от символа "from" и максимальной длины "len" строкой "with", возвращает эту строку.
	PIString & replace(const int from, const int count, const PIString & with);

	//! \~english Replace part copy of this string from index "from" and maximum length "len" with string "with".
	//! \~russian Заменяет часть копии этой строки от символа "from" и максимальной длины "len" строкой "with".
	//! \~\details
	//! \~\code
	//! PIString s("0123456789");
	//! piCout << s.replaced(2, 3, "_cut_"); // s = "01_cut_56789"
	//! piCout << s.replaced(0, 1, "one_"); // s = "one_123456789"
	//! \endcode
	//! \~\sa \a replace(), \a replaceAll()
	PIString replaced(const int from, const int count, const PIString & with) const {
		PIString str(*this);
		str.replace(from, count, with);
		return str;
	}

	//! \~english Replace first founded substring "what" with string "with" and return this string.
	//! \~russian Заменяет первую найденную подстроку "what" строкой "with", возвращает эту строку.
	PIString & replace(const PIString & what, const PIString & with, bool * ok = 0);

	//! \~english Replace in string copy first founded substring "what" with string "with".
	//! \~russian Заменяет в копии строки первую найденную подстроку "what" строкой "with".
	//! \~\details
	//! \~english If "ok" is not null, it set to "true" if something was replaced.
	//! \~russian Если "ok" не null, то устанавливает в "true" если замена произведена.
	//! \~\code
	//! PIString s("pip string");
	//! bool ok;
	//! piCout << s.replace("string", "conf", &ok); // s = "pip conf", true
	//! piCout << s.replace("PIP", "PlInPr", &ok); // s = "pip string", false
	//! \endcode
	//! \~\sa \a replaced(), \a replaceAll()
	PIString replaced(const PIString & what, const PIString & with, bool * ok = 0) const {
		PIString str(*this);
		str.replace(what, with, ok);
		return str;
	}

	//! \~english Replace all founded substrings "what" with strings "with" and return this string.
	//! \~russian Заменяет все найденные подстроки "what" строками "with", возвращает эту строку.
	PIString & replaceAll(const PIString & what, const PIString & with);

	//! \~english Replace all founded substrings "what" with characters "with" and return this string.
	//! \~russian Заменяет все найденные подстроки "what" символами "with", возвращает эту строку.
	PIString & replaceAll(const PIString & what, const char with);

	//! \~english Replace all founded characters "what" with characters "with" and return this string.
	//! \~russian Заменяет все найденные символы "what" символами "with", возвращает эту строку.
	PIString & replaceAll(const char what, const char with);

	//! \~english Replace all founded substrings "what" with strings "with" in string copy.
	//! \~russian Заменяет в копии строки все найденные подстроки "what" строками "with".
	//! \~\sa \a replaceAll()
	PIString replacedAll(const PIString & what, const PIString & with) const {
		PIString str(*this);
		str.replaceAll(what, with);
		return str;
	}

	//! \~english Replace all founded substrings "what" with characters "with" in string copy.
	//! \~russian Заменяет в копии строки все найденные подстроки "what" символами "with".
	//! \~\sa \a replaceAll()
	PIString replacedAll(const PIString & what, const char with) const {
		PIString str(*this);
		str.replaceAll(what, with);
		return str;
	}

	//! \~english Replace all founded characters "what" with characters "with" in string copy.
	//! \~russian Заменяет в копии строки все найденные символы "what" символами "with".
	//! \~\sa \a replaceAll()
	PIString replacedAll(const char what, const char with) const {
		PIString str(*this);
		str.replaceAll(what, with);
		return str;
	}

	//! \~english Remove all founded substrings "what" and return this string.
	//! \~russian Удаляет все найденные подстроки "what", возвращает эту строку.
	PIString & removeAll(const PIString & str);

	//! \~english Remove all founded characters "what" and return this string.
	//! \~russian Удаляет все найденные символы "what", возвращает эту строку.
	PIString & removeAll(char c) {
		d.removeAll(PIChar(c));
		changed_ = true;
		return *this;
	}

	//! \~english Repeat content of string "times" times and return this string.
	//! \~russian Повторяет содержимое строки "times" раз и возвращает эту строку.
	//! \~\details
	//! \~\code
	//! PIString s(" :-) ");
	//! s.repeat(3);
	//! piCout << s; // :-)  :-)  :-)
	//! \endcode
	//! \~\sa \a repeated()
	PIString & repeat(int times) {
		PIString ss(*this);
		times--;
		piForTimes(times) * this += ss;
		changed_ = true;
		return *this;
	}

	//! \~english Returns repeated "times" times string.
	//! \~russian Возвращает повторённую "times" раз строку.
	//! \~\details
	//! \~\code
	//! PIString s(" :-) ");
	//! piCout << s.repeated(3); // :-)  :-)  :-)
	//! piCout << s; // :-)
	//! \endcode
	//! \~\sa \a repeat()
	PIString repeated(int times) const {
		PIString ss(*this);
		return ss.repeat(times);
	}

	//! \~english Insert character "c" after index "index" and return this string.
	//! \~russian Вставляет символ "c" после позиции "index" и возвращает эту строку.
	//! \~\details
	//! \~\code
	//! PIString s("pp");
	//! s.insert(1, "i");
	//! piCout << s; // s = "pip"
	//! \endcode
	PIString & insert(const int index, const PIChar c) {
		d.insert(index, c);
		changed_ = true;
		return *this;
	}

	//! \~english Insert character "c" after index "index" and return this string.
	//! \~russian Вставляет символ "c" после позиции "index" и возвращает эту строку.
	//! \~\details
	//! \~\code
	//! PIString s("pp");
	//! s.insert(1, 'i');
	//! piCout << s; // s = "pip"
	//! \endcode
	PIString & insert(const int index, const char c) { return insert(index, PIChar(c)); }

	//! \~english Insert string "str" after index "index" and return this string.
	//! \~russian Вставляет строку "str" после позиции "index" и возвращает эту строку.
	//! \~\details
	//! \~\code
	//! PIString s("stg");
	//! s.insert(2, "rin");
	//! piCout << s; // s = "string"
	//! \endcode
	PIString & insert(const int index, const PIString & str);

	//! \~english Insert string "str" after index "index" and return this string.
	//! \~russian Вставляет строку "str" после позиции "index" и возвращает эту строку.
	//! \~\details
	//! \~\code
	//! PIString s("stg");
	//! s.insert(2, "rin");
	//! piCout << s; // s = "string"
	//! \endcode
	PIString & insert(const int index, const char * c) { return insert(index, PIString(c)); }

	//! \~english Insert string "str" after index "index" and return this string.
	//! \~russian Вставляет строку "str" после позиции "index" и возвращает эту строку.
	//! \~\details
	//! \~\code
	//! PIString s("stg");
	//! s.insert(2, u"rin");
	//! piCout << s; // s = "string"
	//! \endcode
	PIString & insert(const int index, const char16_t * c) { return insert(index, PIString(c)); }

	//! \~english Enlarge string to length "len" by addition characters "c" at the end, and return this string.
	//! \~russian Увеличивает длину строки до "len" добавлением символов "c" в конец и возвращает эту строку.
	//! \~\details
	//! \~\code
	//! PIString s("str");
	//! s.expandRightTo(2, "_");
	//! piCout << s; // s = "str"
	//! s.expandRightTo(6, "_");
	//! piCout << s; // s = "str___"
	//! \endcode
	//! \~\sa \a expandLeftTo(), \a expandedRightTo(), \a expandedLeftTo()
	PIString & expandRightTo(const int len, const PIChar c) {
		if (len > d.size_s()) d.resize(len, c);
		changed_ = true;
		return *this;
	}

	//! \~english Enlarge string to length "len" by addition characters "c" at the begin, and return this string.
	//! \~russian Увеличивает длину строки до "len" добавлением символов "c" в начало и возвращает эту строку.
	//! \~\details
	//! \~\code
	//! PIString s("str");
	//! s.expandLeftTo(2, "_");
	//! piCout << s; // s = "str"
	//! s.expandLeftTo(6, "_");
	//! piCout << s; // s = "___str"
	//! \endcode
	//! \~\sa \a expandRightTo(), \a expandedRightTo(), \a expandedLeftTo()
	PIString & expandLeftTo(const int len, const PIChar c) {
		if (len > d.size_s()) insert(0, PIString(len - d.size_s(), c));
		changed_ = true;
		return *this;
	}

	//! \~english Enlarge copy of this string to length "len" by addition characters "c" at the end.
	//! \~russian Увеличивает длину копии этой строки до "len" добавлением символов "c" в конец.
	//! \~\details
	//! \~\code
	//! PIString s("str");
	//! piCouy << s.expandedRightTo(5, "_"); // s = "str__"
	//! piCout << s; // s = "str"
	//! \endcode
	//! \~\sa \a expandRightTo(), \a expandLeftTo(), \a expandedLeftTo()
	PIString expandedRightTo(const int len, const PIChar c) const { return PIString(*this).expandRightTo(len, c); }

	//! \~english Enlarge copy of this string to length "len" by addition characters "c" at the begin.
	//! \~russian Увеличивает длину копии этой строки до "len" добавлением символов "c" в начало.
	//! \~\details
	//! \~\code
	//! PIString s("str");
	//! piCouy << s.expandedLeftTo(5, "_"); // s = "__str"
	//! piCout << s; // s = "str"
	//! \endcode
	//! \~\sa \a expandRightTo(), \a expandLeftTo(), \a expandedRightTo()
	PIString expandedLeftTo(const int len, const PIChar c) const { return PIString(*this).expandLeftTo(len, c); }

	//! \~english Add "c" characters at the beginning and end, and return this string.
	//! \~russian Добавляет символ "c" в начало и конец и возвращает эту строку.
	//! \~\details
	//! \~\code
	//! PIString s("str");
	//! s.quote();
	//! piCout << s; // s = ""str""
	//! \endcode
	//! \~\sa \a quoted()
	PIString & quote(PIChar c = PIChar('"')) {
		d.prepend(c);
		d.append(c);
		changed_ = true;
		return *this;
	}

	//! \~english Returns quoted copy of this string.
	//! \~russian Возвращает копию строки с добавленным в начало и конец символом "c".
	//! \~\details
	//! \~\code
	//! PIString s("str");
	//! piCout << s.quoted(); // s = ""str""
	//! piCout << s; // s = "str"
	//! \endcode
	//! \~\sa \a quote()
	PIString quoted(PIChar c = PIChar('"')) { return PIString(*this).quote(c); }

	//! \~english Reverse string and return this string.
	//! \~russian Разворачивает и возвращает эту строку.
	//! \~\details
	//! \~\code
	//! PIString s("0123456789");
	//! s.reverse();
	//! piCout << s; // s = "9876543210"
	//! \endcode
	//! \~\sa \a reversed()
	PIString & reverse() {
		d.reverse();
		changed_ = true;
		return *this;
	}

	//! \~english Reverse copy of this string.
	//! \~russian Разворачивает копию этой строки.
	//! \~\details
	//! \~\code
	//! PIString s("0123456789");
	//! piCout << s.reversed(); // s = "9876543210"
	//! piCout << s; // s = "0123456789"
	//! \endcode
	//! \~\sa \a reverse()
	PIString reversed() const {
		PIString ret(*this);
		return ret.reverse();
	}

	//! \~english Fit string to maximum size "size" by inserting ".." at position "pos" and return this string.
	//! \~russian Уменьшает строку до размера "size", вставляя ".." в положение "pos" и возвращает эту строку.
	//! \~\sa \a elided()
	PIString & elide(int size, float pos = ElideCenter);

	//! \~english Fit copy of this string to maximum size "size" by inserting ".." at position "pos".
	//! \~russian Уменьшает копию этой строки до размера "size", вставляя ".." в положение "pos".
	//! \~\details
	//! \~\code
	//! piCout << PIString("123456789ABCDEF").elided(8, PIString::ElideLeft);   // ..ABCDEF
	//! piCout << PIString("123456789ABCDEF").elided(8, PIString::ElideCenter); // 123..DEF
	//! piCout << PIString("123456789ABCDEF").elided(8, PIString::ElideRight);  // 123456..
	//! piCout << PIString("123456789ABCDEF").elided(8, 0.25);                  // 12..CDEF
	//! \endcode
	//! \~\sa \a elide()
	PIString elided(int size, float pos = ElideCenter) const {
		PIString str(*this);
		str.elide(size, pos);
		return str;
	}


	//! \~english Take a part of string from character at index "start" and maximum length "len" and return it.
	//! \~russian Извлекает часть строки от символа "start" максимальной длины "len" и возвращает её.
	//! \~\details
	//! \~\code
	//! PIString s("0123456789");
	//! piCout << s.takeMid(1, 3); // "123"
	//! piCout << s; // s = "0456789"
	//! \endcode
	//! \~\sa \a takeLeft, \a takeRight()
	PIString takeMid(const int start, const int len = -1) {
		PIString ret(mid(start, len));
		cutMid(start, len);
		return ret;
	}

	//! \~english Take a part from the begin of string with maximum length "len" and return it.
	//! \~russian Извлекает часть строки от начала максимальной длины "len" и возвращает её.
	//! \~\details
	//! \~\code
	//! PIString s("0123456789");
	//! piCout << s.takeLeft(3); // "012"
	//! piCout << s; // s = "3456789"
	//! \endcode
	//! \~\sa \a takeMid(), \a takeRight()
	PIString takeLeft(const int len) {
		PIString ret(left(len));
		cutLeft(len);
		return ret;
	}

	//! \~english Take a part from the end of string with maximum length "len" and return it.
	//! \~russian Извлекает часть строки с конца максимальной длины "len" и возвращает её.
	//! \~\details
	//! \~\code
	//! PIString s("0123456789");
	//! piCout << s.takeRight(3); // "789"
	//! piCout << s; // s = "0123456"
	//! \endcode
	//! \~\sa \a takeMid(), \a takeLeft()
	PIString takeRight(const int len) {
		PIString ret(right(len));
		cutRight(len);
		return ret;
	}

	//! \~english Take a character from the begin of this string and return it.
	//! \~russian Извлекает символ с начала строки и возвращает его как строку.
	PIString takeSymbol();

	//! \~english Take a word from the begin of this string and return it.
	//! \~russian Извлекает слово с начала строки и возвращает его.
	PIString takeWord();

	//! \~english Take a word with letters, numbers and '_' characters from the begin of this string and return it.
	//! \~russian Извлекает слово из букв, цифр и симолов '_' с начала строки и возвращает его.
	PIString takeCWord();

	//! \~english Take a line from the begin of this string and return it.
	//! \~russian Извлекает строку текста (до новой строки) с начала строки и возвращает её.
	PIString takeLine();

	//! \~english Take a number with C-format from the begin of this string and return it.
	//! \~russian Извлекает число в C-формате с начала строки и возвращает его как строку.
	PIString takeNumber();

	//! \~english Take a digits from the begin of this string and return it.
	//! \~russian Извлекает цифры с начала строки и возвращает их как строку.
	PIString takeInteger();

	//! \~english Take a range between "start" and "end" characters from the begin of this string and return it.
	//! \~russian Извлекает диапазон между символами "start" и "end" с начала строки и возвращает его.
	PIString takeRange(const PIChar start, const PIChar end, const PIChar shield = '\\');


	//! \~english Returns string in brackets "start" and "end" characters from the beginning.
	//! \~russian Возвращает строку между символами "start" и "end" с начала строки.
	PIString inBrackets(const PIChar start, const PIChar end) const;

	//! \~english Returns \c char * representation of this string in system codepage.
	//! \~russian Возвращает \c char * представление строки в системной кодировке.
	const char * data() const;

	//! \~english Returns \c char * representation of this string in terminal codepage.
	//! \~russian Возвращает \c char * представление строки в кодировке консоли.
	const char * dataConsole() const;

	//! \~english Returns \c char * representation of this string in UTF-8.
	//! \~russian Возвращает \c char * представление строки в кодировке UTF-8.
	const char * dataUTF8() const;

	//! \~english Returns \c char * representation of this string in ASCII.
	//! \~russian Возвращает \c char * представление строки в кодировке ASCII.
	const char * dataAscii() const;

	//! \~english Returns data size of last data*() call.
	//! \~russian Возвращает размер данных последнего вызова data*().
	const size_t lastDataSize() const { return data_size_; }

	//! \~english Returns hash of string
	//! \~russian Возвращает хэш строки
	uint hash() const;

	//! \~english Same as \a toUTF8().
	//! \~russian Тоже самое, что \a toUTF8().
	PIByteArray toByteArray() const { return toUTF8(); }

	//! \~english Returns \a PIByteArray contains \a dataAscii() of this string without terminating null-char.
	//! \~russian Возвращает \a PIByteArray содержащий \a dataAscii() строки без завершающего нулевого байта.
	PIByteArray toAscii() const;

	//! \~english Returns \a PIByteArray contains \a data() of this string without terminating null-char.
	//! \~russian Возвращает \a PIByteArray содержащий \a data() строки без завершающего нулевого байта.
	PIByteArray toSystem() const;

	//! \~english Returns \a PIByteArray contains \a dataUTF8() of this string without terminating null-char.
	//! \~russian Возвращает \a PIByteArray содержащий \a dataUTF8() строки без завершающего нулевого байта.
	PIByteArray toUTF8() const;

	//! \~english Returns \a PIByteArray contains custom charset representation of this string without terminating null-char.
	//! \~russian Возвращает \a PIByteArray содержащий строку в указанной кодировке без завершающего нулевого байта.
	PIByteArray toCharset(const char * c) const;

	//! \~english Returns \a PIString with non-ASCII symbols replaced with '?'.
	//! \~russian Возвращает \a PIString с заменёнными '?' не-ASCII символами.
	PIString simplified() const;

	//! \~english Insert before any symbol from "symbols" symbol "mc" and return this string.
	//! \~russian Вставляет перед любым символом из "symbols" символ "mc" и возвращает эту строку.
	PIString & mask(const PIString & symbols, const PIChar mc = '\\');

	PIString masked(const PIString & symbols, const PIChar mc = '\\') const { return PIString(*this).mask(symbols, mc); }

	//! \~english Remove symbol "mc" before any symbol from "symbols" and return this string.
	//! \~russian Удаляет символ "mc" перед любым символом из "symbols" и возвращает эту строку.
	PIString & unmask(const PIString & symbols, const PIChar mc = '\\');

	PIString unmasked(const PIString & symbols, const PIChar mc = '\\') const { return PIString(*this).unmask(symbols, mc); }

	//! \~english Split string with delimiter "delim" to \a PIStringList.
	//! \~russian Разделяет строку в \a PIStringList через разделитель "delim".
	//! \~\details
	//! \~\code
	//! PIString s("1 2 3");
	//! piCout << s.split(" "); // {"1", "2", "3"}
	//! \endcode
	PIStringList split(const PIString & delim) const;


	//! \~english Convert each character in copied string to upper case.
	//! \~russian Преобразует каждый символ в скопированной строке в верхний регистр.
	PIString toUpperCase() const;

	//! \~english Convert each character in copied string to lower case.
	//! \~russian Преобразует каждый символ в скопированной строке в нижний регистр.
	PIString toLowerCase() const;


	//! \~english Returns if string contains character "c".
	//! \~russian Возвращает содержит ли строка символ "c".
	bool contains(const char c) const { return d.contains(PIChar(c)); }

	//! \~english Returns if string contains character "c".
	//! \~russian Возвращает содержит ли строка символ "c".
	bool contains(const PIChar c) const { return d.contains(c); }

	//! \~english Returns if string contains substring "str".
	//! \~russian Возвращает содержит ли строка подстроку "str".
	bool contains(const char * str) const { return contains(PIString(str)); }

	//! \~english Returns if string contains substring "str".
	//! \~russian Возвращает содержит ли строка подстроку "str".
	bool contains(const PIString & str) const { return find(str) >= 0; }

	//! \~english Returns if string match "regexp".
	//! \~russian Возвращает совпадает ли строка "regexp".
	bool contains(const PIRegularExpression & regexp) const;


	//! \~english Search character "c" from character at index "start" and return first occur position.
	//! \~russian Ищет символ "c" от символа "start" и возвращает первое вхождение.
	int find(const char c, const int start = 0) const;

	//! \~english Search character "c" from character at index "start" and return first occur position.
	//! \~russian Ищет символ "c" от символа "start" и возвращает первое вхождение.
	int find(PIChar c, const int start = 0) const { return d.indexOf(c, start); }

	//! \~english Search substring "str" from character at index "start" and return first occur position.
	//! \~russian Ищет подстроку "str" от символа "start" и возвращает первое вхождение.
	int find(const PIString & str, const int start = 0) const;

	//! \~english Search substring "str" from character at index "start" and return first occur position.
	//! \~russian Ищет подстроку "str" от символа "start" и возвращает первое вхождение.
	//! \~\details
	//! \~\code
	//! PIString s("012345012345");
	//! piCout << s.find("-"); // -1
	//! piCout << s.find("34"); // 3
	//! piCout << s.find("3", 4); // 9
	//! piCout << s.find("3", 10); // -1
	//! \endcode
	//! \~\sa \a findAny(), \a findLast(), \a findAnyLast(), \a findWord(), \a findCWord(), \a findRange()
	int find(const char * str, const int start = 0) const { return find(PIString(str), start); }

	//! \~english Search match of "regexp" from character at index "start" and return first occur position.
	//! \~russian Ищет совпадение с "regexp" от символа "start" и возвращает первое вхождение.
	int find(const PIRegularExpression & regexp, const int start = 0) const;

	//! \~english Search any character of "str" from character at index "start" and return first occur position.
	//! \~russian Ищет любой символ строки "str" от симола "start" и возвращает первое вхождение.
	int findAny(const PIString & str, const int start = 0) const;

	//! \~english Search any character of "str" from character at index "start" and return first occur position.
	//! \~russian Ищет любой символ строки "str" от симола "start" и возвращает первое вхождение.
	//! \~\details
	//! \~\code
	//! piCout << PIString("1.str").findAny(".,:"); // 1
	//! piCout << PIString("1,str").findAny(".,:"); // 1
	//! piCout << PIString("1:str").findAny(".,:"); // 1
	//! \endcode
	//! \~\sa \a find(), \a findLast(), \a findAnyLast(), \a findWord(), \a findCWord(), \a findRange()
	int findAny(const char * str, const int start = 0) const { return findAny(PIString(str), start); }

	//! \~english Search character "c" from character at index "start" and return last occur position.
	//! \~russian Ищет символ "c" от символа "start" и возвращает последнее вхождение.
	int findLast(const char c, const int start = 0) const;

	//! \~english Search character "c" from character at index "start" and return last occur position.
	//! \~russian Ищет символ "c" от символа "start" и возвращает последнее вхождение.
	int findLast(PIChar c, const int start = 0) const;

	//! \~english Search substring "str" from character at index "start" and return last occur position.
	//! \~russian Ищет подстроку "str" от символа "start" и возвращает последнее вхождение.
	int findLast(const PIString & str, const int start = 0) const;

	//! \~english Search substring "str" from character at index "start" and return last occur position.
	//! \~russian Ищет подстроку "str" от символа "start" и возвращает последнее вхождение.
	//! \~\details
	//! \~\code
	//! PIString s("012345012345");
	//! piCout << s.findLast("-"); // -1
	//! piCout << s.findLast("34"); // 9
	//! piCout << s.findLast("3", 4); // 9
	//! piCout << s.findLast("3", 10); // -1
	//! \endcode
	//! \~\sa \a find(), \a findAny(), \a findAnyLast(), \a findWord(), \a findCWord(), \a findRange()
	int findLast(const char * str, const int start = 0) const { return findLast(PIString(str), start); }

	//! \~english Search match of "regexp" from character at index "start" and return last occur position.
	//! \~russian Ищет совпадение с "regexp" от символа "start" и возвращает последнее вхождение.
	int findLast(const PIRegularExpression & regexp, const int start = 0) const;

	//! \~english Search any character of "str" from character at index "start" and return last occur position.
	//! \~russian Ищет любой символ строки "str" от символа "start" и возвращает последнее вхождение.
	int findAnyLast(const PIString & str, const int start = 0) const;

	//! \~english Search any character of "str" from character at index "start" and return last occur position.
	//! \~russian Ищет любой символ строки "str" от символа "start" и возвращает последнее вхождение.
	//! \~\details
	//! \~\code
	//! piCout << PIString(".str.0").findAnyLast(".,:"); // 4
	//! piCout << PIString(".str,0").findAnyLast(".,:"); // 4
	//! piCout << PIString(".str:0").findAnyLast(".,:"); // 4
	//! \endcode
	//! \~\sa \a find(), \a findAny(), \a findLast(), \a findWord(), \a findCWord(), \a findRange()
	int findAnyLast(const char * str, const int start = 0) const { return findAnyLast(PIString(str), start); }

	//! \~english Search word "word" from character at index "start" and return first occur position.
	//! \~russian Ищет слово "word" от симола "start" и возвращает первое вхождение.
	int findWord(const PIString & word, const int start = 0) const;

	//! \~english Search C-word "word" from character at index "start" and return first occur position.
	//! \~russian Ищет C-слово "word" от симола "start" и возвращает первое вхождение.
	int findCWord(const PIString & word, const int start = 0) const;

	//! \~english Search range start between "start" and "end" characters at index "start_index" and return first occur position.
	//! \~russian Ищет начало диапазона между символами "start" и "end" от симола "start" и возвращает первое вхождение.
	int findRange(const PIChar start, const PIChar end, const PIChar shield = '\\', const int start_index = 0, int * len = 0) const;

	//! \~english Returns number of occurrences of character "c".
	//! \~russian Возвращает число вхождений символа "c".
	//! \~\details
	//! \~\code
	//! piCout << PIString(".str.0").entries("."); // 2
	//! piCout << PIString(".str.0").entries("0"); // 1
	//! \endcode
	int entries(const PIChar c) const;
	int entries(char c) const { return entries(PIChar(c)); }

	//! \~english Returns number of occurrences of string "str".
	//! \~russian Возвращает число вхождений строк "str".
	//! \~\details
	//! \~\code
	//! piCout << PIString("hello locale").entries("lo"); // 2
	//! piCout << PIString("hello locale").entries("lo lo"); // 1
	//! \endcode
	int entries(const PIString & str) const;

	//! \~english Returns line number of position "pos". Lines starts from 1.
	//! \~russian Возвращает номер строки позиции "pos". Строки начинаются с 1.
	int lineNumber(int pos) const;

	//! \~english Returns if string starts with "c".
	//! \~russian Возвращает начинается ли строка с "c".
	bool startsWith(const char c) const { return startsWith(PIChar(c)); }

	//! \~english Returns if string starts with "c".
	//! \~russian Возвращает начинается ли строка с "c".
	bool startsWith(const PIChar c) const;

	//! \~english Returns if string starts with "str".
	//! \~russian Возвращает начинается ли строка со "str".
	bool startsWith(const PIString & str) const;

	//! \~english Returns if string ends with "c".
	//! \~russian Возвращает оканчивается ли строка на "c".
	bool endsWith(const char c) const { return endsWith(PIChar(c)); }

	//! \~english Returns if string ends with "c".
	//! \~russian Возвращает оканчивается ли строка на "c".
	bool endsWith(const PIChar c) const;

	//! \~english Returns if string ends with "str".
	//! \~russian Возвращает оканчивается ли строка на "str".
	bool endsWith(const PIString & str) const;

	//! \~english Returns characters length of string.
	//! \~russian Возвращает длину строки в символах.
	int length() const { return d.size_s(); }

	//! \~english Returns characters length of string.
	//! \~russian Возвращает длину строки в символах.
	size_t size() const { return d.size(); }

	//! \~english Returns characters length of string.
	//! \~russian Возвращает длину строки в символах.
	ssize_t size_s() const { return d.size_s(); }

	//! \~english Returns \c true if string is empty, i.e. length = 0.
	//! \~russian Возвращает \c true если строка пустая, т.е. длина = 0.
	bool isEmpty() const {
		if (d.isEmpty()) return true;
		if (d.at(0) == PIChar()) return true;
		return false;
	}

	//! \~english Returns \c true if string is not empty, i.e. length > 0.
	//! \~russian Возвращает \c true если строка непустая, т.е. длина > 0.
	bool isNotEmpty() const { return !isEmpty(); }

	//! \~english Clear string, will be empty string.
	//! \~russian Очищает строку, строка становится пустой.
	//! \~\details
	//! \~\note
	//! \~english Reserved memory will not be released.
	//! \~russian Зарезервированная память не освободится.
	//! \~\sa \a resize()
	void clear() {
		d.clear();
		changed_ = true;
	}

	//! \~english Returns \c true if string equal "true", "yes", "on" or positive not null numeric value.
	//! \~russian Возвращает \c true если строка равна "true", "yes", "on" или числу > 0.
	bool toBool() const;

	//! \~english Returns \c char numeric value of string.
	//! \~russian Возвращает \c char числовое значение строки.
	char toChar() const;

	//! \~english Returns \c short numeric value of string in base "base".
	//! \~russian Возвращает \c short числовое значение строки по основанию "base".
	//! \~\details
	//! \~english If "base" < 0 then base automatically select 16 if string start with "0x", therwise 10.
	//! \~russian Если "base" < 0 тогда основание автоматически принимается 16 если строка начинается с "0x", иначе 10.
	//! \~\code
	//! piCout << PIString("123").toShort(); // 123
	//! piCout << PIString("123").toShort(16); // 291
	//! piCout << PIString("0x123").toShort(); // 291
	//! piCout << PIString("1001").toShort(2); // 9
	//! \endcode
	short toShort(int base = -1, bool * ok = 0) const { return short(toNumberBase(*this, base, ok)); }

	//! \~english Returns \c ushort numeric value of string in base "base".
	//! \~russian Возвращает \c ushort числовое значение строки по основанию "base".
	//! \~\details
	//! \~english If "base" < 0 then base automatically select 16 if string start with "0x", therwise 10.
	//! \~russian Если "base" < 0 тогда основание автоматически принимается 16 если строка начинается с "0x", иначе 10.
	//! \~\code
	//! piCout << PIString("123").toUShort(); // 123
	//! piCout << PIString("123").toUShort(16); // 291
	//! piCout << PIString("0x123").toUShort(); // 291
	//! piCout << PIString("1001").toUShort(2); // 9
	//! \endcode
	ushort toUShort(int base = -1, bool * ok = 0) const { return ushort(toNumberBase(*this, base, ok)); }

	//! \~english Returns \c int numeric value of string in base "base".
	//! \~russian Возвращает \c int числовое значение строки по основанию "base".
	//! \~\details
	//! \~english If "base" < 0 then base automatically select 16 if string start with "0x", therwise 10.
	//! \~russian Если "base" < 0 тогда основание автоматически принимается 16 если строка начинается с "0x", иначе 10.
	//! \~\code
	//! piCout << PIString("123").toInt(); // 123
	//! piCout << PIString("123").toInt(16); // 291
	//! piCout << PIString("0x123").toInt(); // 291
	//! piCout << PIString("1001").toInt(2); // 9
	//! \endcode
	int toInt(int base = -1, bool * ok = 0) const { return int(toNumberBase(*this, base, ok)); }

	//! \~english Returns \c uint numeric value of string in base "base".
	//! \~russian Возвращает \c uint числовое значение строки по основанию "base".
	//! \~\details
	//! \~english If "base" < 0 then base automatically select 16 if string start with "0x", therwise 10.
	//! \~russian Если "base" < 0 тогда основание автоматически принимается 16 если строка начинается с "0x", иначе 10.
	//! \~\code
	//! piCout << PIString("123").toUInt(); // 123
	//! piCout << PIString("123").toUInt(16); // 291
	//! piCout << PIString("0x123").toUInt(); // 291
	//! piCout << PIString("1001").toUInt(2); // 9
	//! \endcode
	uint toUInt(int base = -1, bool * ok = 0) const { return uint(toNumberBase(*this, base, ok)); }

	//! \~english Returns \c long numeric value of string in base "base".
	//! \~russian Возвращает \c long числовое значение строки по основанию "base".
	//! \~\details
	//! \~english If "base" < 0 then base automatically select 16 if string start with "0x", therwise 10.
	//! \~russian Если "base" < 0 тогда основание автоматически принимается 16 если строка начинается с "0x", иначе 10.
	//! \~\code
	//! piCout << PIString("123").toLong(); // 123
	//! piCout << PIString("123").toLong(16); // 291
	//! piCout << PIString("0x123").toLong(); // 291
	//! piCout << PIString("1001").toLong(2); // 9
	//! \endcode
	long toLong(int base = -1, bool * ok = 0) const { return long(toNumberBase(*this, base, ok)); }

	//! \~english Returns \c ulong numeric value of string in base "base".
	//! \~russian Возвращает \c ulong числовое значение строки по основанию "base".
	//! \~\details
	//! \~english If "base" < 0 then base automatically select 16 if string start with "0x", therwise 10.
	//! \~russian Если "base" < 0 тогда основание автоматически принимается 16 если строка начинается с "0x", иначе 10.
	//! \~\code
	//! piCout << PIString("123").toULong(); // 123
	//! piCout << PIString("123").toULong(16); // 291
	//! piCout << PIString("0x123").toULong(); // 291
	//! piCout << PIString("1001").toULong(2); // 9
	//! \endcode
	ulong toULong(int base = -1, bool * ok = 0) const { return ulong(toNumberBase(*this, base, ok)); }

	//! \~english Returns \c llong numeric value of string in base "base".
	//! \~russian Возвращает \c llong числовое значение строки по основанию "base".
	//! \~\details
	//! \~english If "base" < 0 then base automatically select 16 if string start with "0x", therwise 10.
	//! \~russian Если "base" < 0 тогда основание автоматически принимается 16 если строка начинается с "0x", иначе 10.
	//! \~\code
	//! piCout << PIString("123").toLLong(); // 123
	//! piCout << PIString("123").toLLong(16); // 291
	//! piCout << PIString("0x123").toLLong(); // 291
	//! piCout << PIString("1001").toLLong(2); // 9
	//! \endcode
	llong toLLong(int base = -1, bool * ok = 0) const { return toNumberBase(*this, base, ok); }

	//! \~english Returns \c ullong numeric value of string in base "base".
	//! \~russian Возвращает \c ullong числовое значение строки по основанию "base".
	//! \~\details
	//! \~english If "base" < 0 then base automatically select 16 if string start with "0x", therwise 10.
	//! \~russian Если "base" < 0 тогда основание автоматически принимается 16 если строка начинается с "0x", иначе 10.
	//! \~\code
	//! piCout << PIString("123").toULLong(); // 123
	//! piCout << PIString("123").toULLong(16); // 291
	//! piCout << PIString("0x123").toULLong(); // 291
	//! piCout << PIString("1001").toULLong(2); // 9
	//! \endcode
	ullong toULLong(int base = -1, bool * ok = 0) const { return ullong(toNumberBase(*this, base, ok)); }

	//! \~english Returns \c float numeric value of string.
	//! \~russian Возвращает \c float числовое значение строки.
	//! \~\details
	//! \~\code
	//! piCout << PIString("123").toFloat(); // 123
	//! piCout << PIString("1.2E+2").toFloat(); // 120
	//! piCout << PIString("0.01").toFloat(); // 0.01
	//! \endcode
	float toFloat() const;

	//! \~english Returns \c double numeric value of string.
	//! \~russian Возвращает \c double числовое значение строки.
	//! \~\details
	//! \~\code
	//! piCout << PIString("123").toDouble(); // 123
	//! piCout << PIString("1.2E+2").toDouble(); // 120
	//! piCout << PIString("0.01").toDouble(); // 0.01
	//! \endcode
	double toDouble() const;

	//! \~english Returns \c ldouble numeric value of string.
	//! \~russian Возвращает \c ldouble числовое значение строки.
	//! \~\details
	//! \~\code
	//! piCout << PIString("123").toLDouble(); // 123
	//! piCout << PIString("1.2E+2").toLDouble(); // 120
	//! piCout << PIString("0.01").toLDouble(); // 0.01
	//! \endcode
	ldouble toLDouble() const;

	//! \~english Set string content to text representation of "value" in base "base" and return this string.
	//! \~russian Устанавливает содержимое строки в текстовое представление "value" по основанию "base" и возвращает эту строку.
	//! \~\details
	//! \~\code
	//! PIString s;
	//! s.setNumber(123);
	//! piCout << s; // 123
	//! s.setNumber(123, 16);
	//! piCout << s; // 7B
	//! \endcode
	PIString & setNumber(const short value, int base = 10, bool * ok = 0) {
		*this = PIString::fromNumber(value, base, ok);
		return *this;
	}

	//! \~english Set string content to text representation of "value" in base "base" and return this string.
	//! \~russian Устанавливает содержимое строки в текстовое представление "value" по основанию "base" и возвращает эту строку.
	//! \~\details
	//! \~\code
	//! PIString s;
	//! s.setNumber(123);
	//! piCout << s; // 123
	//! s.setNumber(123, 16);
	//! piCout << s; // 7B
	//! \endcode
	PIString & setNumber(const ushort value, int base = 10, bool * ok = 0) {
		*this = PIString::fromNumber(value, base, ok);
		return *this;
	}

	//! \~english Set string content to text representation of "value" in base "base" and return this string.
	//! \~russian Устанавливает содержимое строки в текстовое представление "value" по основанию "base" и возвращает эту строку.
	//! \~\details
	//! \~\code
	//! PIString s;
	//! s.setNumber(123);
	//! piCout << s; // 123
	//! s.setNumber(123, 16);
	//! piCout << s; // 7B
	//! \endcode
	PIString & setNumber(const int value, int base = 10, bool * ok = 0) {
		*this = PIString::fromNumber(value, base, ok);
		return *this;
	}

	//! \~english Set string content to text representation of "value" in base "base" and return this string.
	//! \~russian Устанавливает содержимое строки в текстовое представление "value" по основанию "base" и возвращает эту строку.
	//! \~\details
	//! \~\code
	//! PIString s;
	//! s.setNumber(123);
	//! piCout << s; // 123
	//! s.setNumber(123, 16);
	//! piCout << s; // 7B
	//! \endcode
	PIString & setNumber(const uint value, int base = 10, bool * ok = 0) {
		*this = PIString::fromNumber(value, base, ok);
		return *this;
	}

	//! \~english Set string content to text representation of "value" in base "base" and return this string.
	//! \~russian Устанавливает содержимое строки в текстовое представление "value" по основанию "base" и возвращает эту строку.
	//! \~\details
	//! \~\code
	//! PIString s;
	//! s.setNumber(123);
	//! piCout << s; // 123
	//! s.setNumber(123, 16);
	//! piCout << s; // 7B
	//! \endcode
	PIString & setNumber(const long value, int base = 10, bool * ok = 0) {
		*this = PIString::fromNumber(value, base, ok);
		return *this;
	}

	//! \~english Set string content to text representation of "value" in base "base" and return this string.
	//! \~russian Устанавливает содержимое строки в текстовое представление "value" по основанию "base" и возвращает эту строку.
	//! \~\details
	//! \~\code
	//! PIString s;
	//! s.setNumber(123);
	//! piCout << s; // 123
	//! s.setNumber(123, 16);
	//! piCout << s; // 7B
	//! \endcode
	PIString & setNumber(const ulong value, int base = 10, bool * ok = 0) {
		*this = PIString::fromNumber(value, base, ok);
		return *this;
	}

	//! \~english Set string content to text representation of "value" in base "base" and return this string.
	//! \~russian Устанавливает содержимое строки в текстовое представление "value" по основанию "base" и возвращает эту строку.
	//! \~\details
	//! \~\code
	//! PIString s;
	//! s.setNumber(123);
	//! piCout << s; // 123
	//! s.setNumber(123, 16);
	//! piCout << s; // 7B
	//! \endcode
	PIString & setNumber(const llong & value, int base = 10, bool * ok = 0) {
		*this = PIString::fromNumber(value, base, ok);
		return *this;
	}

	//! \~english Set string content to text representation of "value" in base "base" and return this string.
	//! \~russian Устанавливает содержимое строки в текстовое представление "value" по основанию "base" и возвращает эту строку.
	//! \~\details
	//! \~\code
	//! PIString s;
	//! s.setNumber(123);
	//! piCout << s; // 123
	//! s.setNumber(123, 16);
	//! piCout << s; // 7B
	//! \endcode
	PIString & setNumber(const ullong & value, int base = 10, bool * ok = 0) {
		*this = PIString::fromNumber(value, base, ok);
		return *this;
	}

	//! \~english Set string content to text representation of "value" with format "format" and precision "precision" and return this
	//! string.
	//! \~russian Устанавливает содержимое строки в текстовое представление "value" в формате "format" и точностью "precision" и возвращает
	//! эту строку.
	//! \~\details
	//! \~\code
	//! PIString s;
	//! s.setNumber(12.3);
	//! piCout << s; // 12.30000000
	//! s.setNumber(12.3, 'f', 3);
	//! piCout << s; // 12.300
	//! s.setNumber(12.123456, 'f', 3);
	//! piCout << s; // 12.123
	//! s.setNumber(123456789., 'g', 2);
	//! piCout << s; // 1.2e+08
	//! s.setNumber(123456789., 'f', 0);
	//! piCout << s; // 123456789
	//! \endcode
	PIString & setNumber(const float value, char format = 'f', int precision = 8) {
		*this = PIString::fromNumber(value, format, precision);
		return *this;
	}

	//! \~english Set string content to text representation of "value" with format "format" and precision "precision" and return this
	//! string.
	//! \~russian Устанавливает содержимое строки в текстовое представление "value" в формате "format" и точностью "precision" и возвращает
	//! эту строку.
	//! \~\details
	//! \~\code
	//! PIString s;
	//! s.setNumber(12.3);
	//! piCout << s; // 12.30000000
	//! s.setNumber(12.3, 'f', 3);
	//! piCout << s; // 12.300
	//! s.setNumber(12.123456, 'f', 3);
	//! piCout << s; // 12.123
	//! s.setNumber(123456789., 'g', 2);
	//! piCout << s; // 1.2e+08
	//! s.setNumber(123456789., 'f', 0);
	//! piCout << s; // 123456789
	//! \endcode
	PIString & setNumber(const double & value, char format = 'f', int precision = 8) {
		*this = PIString::fromNumber(value, format, precision);
		return *this;
	}

	//! \~english Set string content to text representation of "value" with format "format" and precision "precision" and return this
	//! string.
	//! \~russian Устанавливает содержимое строки в текстовое представление "value" в формате "format" и точностью "precision" и возвращает
	//! эту строку.
	//! \~\details
	//! \~\code
	//! PIString s;
	//! s.setNumber(12.3);
	//! piCout << s; // 12.30000000
	//! s.setNumber(12.3, 'f', 3);
	//! piCout << s; // 12.300
	//! s.setNumber(12.123456, 'f', 3);
	//! piCout << s; // 12.123
	//! s.setNumber(123456789., 'g', 2);
	//! piCout << s; // 1.2e+08
	//! s.setNumber(123456789., 'f', 0);
	//! piCout << s; // 123456789
	//! \endcode
	PIString & setNumber(const ldouble & value, char format = 'f', int precision = 8) {
		*this = PIString::fromNumber(value, format, precision);
		return *this;
	}

	//! \~english Set string content to human readable size in B/KiB/MiB/GiB/TiB/PiB.
	//! \~russian Устанавливает содержимое в строку с читаемым размером B/KiB/MiB/GiB/TiB/PiB.
	//! \~\sa PIString::readableSize()
	PIString & setReadableSize(llong bytes);


	//! \~english Returns [percentage-encoded](https://en.wikipedia.org/wiki/Percent-encoding) string.
	//! \~russian Возвращает [URL-кодированную](https://ru.wikipedia.org/wiki/URL) строку.
	PIString toPercentageEncoding() const;

	//! \~english Replace all occurances like "%1", "%2", ... with lowest value to "v" and returns this string.
	//! \~russian Заменяет все вхождения типа "%1", "%2", ... с наименьшим значением на "v" и возвращает эту строку.
	//! \~\details
	//! \~\code
	//! piCout << PIString("color %1 is not %2, but %1").arg("red").arg("green"); // color red is not green, but red
	//! \endcode
	PIString & arg(const PIString & v);

	//! \~english Replace all occurances like "%1", "%2", ... with lowest value to "v" and returns this string.
	//! \~russian Заменяет все вхождения типа "%1", "%2", ... с наименьшим значением на "v" и возвращает эту строку.
	PIString & arg(short v, int base = 10) { return arg(PIString::fromNumber(v, base)); }

	//! \~english Replace all occurances like "%1", "%2", ... with lowest value to "v" and returns this string.
	//! \~russian Заменяет все вхождения типа "%1", "%2", ... с наименьшим значением на "v" и возвращает эту строку.
	PIString & arg(ushort v, int base = 10) { return arg(PIString::fromNumber(v, base)); }

	//! \~english Replace all occurances like "%1", "%2", ... with lowest value to "v" and returns this string.
	//! \~russian Заменяет все вхождения типа "%1", "%2", ... с наименьшим значением на "v" и возвращает эту строку.
	//! \~\details
	//! \~\code
	//! piCout << PIString("ten = %1").arg(10); // ten = 10
	//! piCout << PIString("'%1' is max hex letter (%2 in dec)").arg(0xF, 16).arg(0xF); // 'F' is max hex letter (15 in dec)
	//! \endcode
	PIString & arg(int v, int base = 10) { return arg(PIString::fromNumber(v, base)); }

	//! \~english Replace all occurances like "%1", "%2", ... with lowest value to "v" and returns this string.
	//! \~russian Заменяет все вхождения типа "%1", "%2", ... с наименьшим значением на "v" и возвращает эту строку.
	PIString & arg(uint v, int base = 10) { return arg(PIString::fromNumber(v, base)); }

	//! \~english Replace all occurances like "%1", "%2", ... with lowest value to "v" and returns this string.
	//! \~russian Заменяет все вхождения типа "%1", "%2", ... с наименьшим значением на "v" и возвращает эту строку.
	PIString & arg(long v, int base = 10) { return arg(PIString::fromNumber(v, base)); }

	//! \~english Replace all occurances like "%1", "%2", ... with lowest value to "v" and returns this string.
	//! \~russian Заменяет все вхождения типа "%1", "%2", ... с наименьшим значением на "v" и возвращает эту строку.
	PIString & arg(ulong v, int base = 10) { return arg(PIString::fromNumber(v, base)); }

	//! \~english Replace all occurances like "%1", "%2", ... with lowest value to "v" and returns this string.
	//! \~russian Заменяет все вхождения типа "%1", "%2", ... с наименьшим значением на "v" и возвращает эту строку.
	PIString & arg(llong v, int base = 10) { return arg(PIString::fromNumber(v, base)); }

	//! \~english Replace all occurances like "%1", "%2", ... with lowest value to "v" and returns this string.
	//! \~russian Заменяет все вхождения типа "%1", "%2", ... с наименьшим значением на "v" и возвращает эту строку.
	PIString & arg(ullong v, int base = 10) { return arg(PIString::fromNumber(v, base)); }

	//! \~english Replace all occurances like "%1", "%2", ... with lowest value to "v" and returns this string.
	//! \~russian Заменяет все вхождения типа "%1", "%2", ... с наименьшим значением на "v" и возвращает эту строку.
	PIString & arg(float v, char format = 'f', int precision = 8) { return arg(PIString::fromNumber(v, format, precision)); }

	//! \~english Replace all occurances like "%1", "%2", ... with lowest value to "v" and returns this string.
	//! \~russian Заменяет все вхождения типа "%1", "%2", ... с наименьшим значением на "v" и возвращает эту строку.
	//! \~\details
	//! \~\code
	//! piCout << PIString("light speed = %1 %2").arg(M_LIGHT_SPEED, 'g', 4).arg("m/s"); // light speed = 2.998e+08 m/s
	//! \endcode
	PIString & arg(double v, char format = 'f', int precision = 8) { return arg(PIString::fromNumber(v, format, precision)); }

	//! \~english Replace all occurances like "%1", "%2", ... with lowest value to "v" and returns this string.
	//! \~russian Заменяет все вхождения типа "%1", "%2", ... с наименьшим значением на "v" и возвращает эту строку.
	PIString & arg(ldouble v, char format = 'f', int precision = 8) { return arg(PIString::fromNumber(v, format, precision)); }


	//! \~english Returns string contains numeric representation of "value" in base "base".
	//! \~russian Возвращает строковое представление числа "value" по основанию "base".
	//! \~\details
	//! \~\code
	//! piCout << PIString::fromNumber(123); // 123
	//! piCout << PIString::fromNumber(123, 16); // 7B
	//! \endcode
	static PIString fromNumber(const short value, int base = 10, bool * ok = 0) { return fromNumberBaseS(llong(value), base, ok); }

	//! \~english Returns string contains numeric representation of "value" in base "base".
	//! \~russian Возвращает строковое представление числа "value" по основанию "base".
	//! \~\details
	//! \~\code
	//! piCout << PIString::fromNumber(123); // 123
	//! piCout << PIString::fromNumber(123, 16); // 7B
	//! \endcode
	static PIString fromNumber(const ushort value, int base = 10, bool * ok = 0) { return fromNumberBaseU(ullong(value), base, ok); }

	//! \~english Returns string contains numeric representation of "value" in base "base".
	//! \~russian Возвращает строковое представление числа "value" по основанию "base".
	//! \~\details
	//! \~\code
	//! piCout << PIString::fromNumber(123); // 123
	//! piCout << PIString::fromNumber(123, 16); // 7B
	//! \endcode
	static PIString fromNumber(const int value, int base = 10, bool * ok = 0) { return fromNumberBaseS(llong(value), base, ok); }

	//! \~english Returns string contains numeric representation of "value" in base "base".
	//! \~russian Возвращает строковое представление числа "value" по основанию "base".
	//! \~\details
	//! \~\code
	//! piCout << PIString::fromNumber(123); // 123
	//! piCout << PIString::fromNumber(123, 16); // 7B
	//! \endcode
	static PIString fromNumber(const uint value, int base = 10, bool * ok = 0) { return fromNumberBaseU(ullong(value), base, ok); }

	//! \~english Returns string contains numeric representation of "value" in base "base".
	//! \~russian Возвращает строковое представление числа "value" по основанию "base".
	//! \~\details
	//! \~\code
	//! piCout << PIString::fromNumber(123); // 123
	//! piCout << PIString::fromNumber(123, 16); // 7B
	//! \endcode
	static PIString fromNumber(const long value, int base = 10, bool * ok = 0) { return fromNumberBaseS(llong(value), base, ok); }

	//! \~english Returns string contains numeric representation of "value" in base "base".
	//! \~russian Возвращает строковое представление числа "value" по основанию "base".
	//! \~\details
	//! \~\code
	//! piCout << PIString::fromNumber(123); // 123
	//! piCout << PIString::fromNumber(123, 16); // 7B
	//! \endcode
	static PIString fromNumber(const ulong value, int base = 10, bool * ok = 0) { return fromNumberBaseU(ullong(value), base, ok); }

	//! \~english Returns string contains numeric representation of "value" in base "base".
	//! \~russian Возвращает строковое представление числа "value" по основанию "base".
	//! \~\details
	//! \~\code
	//! piCout << PIString::fromNumber(123); // 123
	//! piCout << PIString::fromNumber(123, 16); // 7B
	//! \endcode
	static PIString fromNumber(const llong & value, int base = 10, bool * ok = 0) { return fromNumberBaseS(value, base, ok); }

	//! \~english Returns string contains numeric representation of "value" in base "base".
	//! \~russian Возвращает строковое представление числа "value" по основанию "base".
	//! \~\details
	//! \~\code
	//! piCout << PIString::fromNumber(123); // 123
	//! piCout << PIString::fromNumber(123, 16); // 7B
	//! \endcode
	static PIString fromNumber(const ullong & value, int base = 10, bool * ok = 0) { return fromNumberBaseU(value, base, ok); }

	//! \~english Returns string contains numeric representation of "value" with format "format" and precision "precision".
	//! \~russian Возвращает строковое представление числа "value" в формате "format" и точностью "precision".
	//! \~\details
	//! \~\code
	//! piCout << PIString::fromNumber(12.3); // 12.30000000
	//! piCout << PIString::fromNumber(12.3, 'f', 3); // 12.300
	//! piCout << PIString::fromNumber(12.123456, 'f', 3); // 12.123
	//! piCout << PIString::fromNumber(123456789., 'g', 2); // 1.2e+08
	//! piCout << PIString::fromNumber(123456789., 'f', 0); // 123456789
	//! \endcode
	static PIString fromNumber(const float value, char format = 'f', int precision = 8) { return dtos(value, format, precision); }

	//! \~english Returns string contains numeric representation of "value" with format "format" and precision "precision".
	//! \~russian Возвращает строковое представление числа "value" в формате "format" и точностью "precision".
	//! \~\details
	//! \~\code
	//! piCout << PIString::fromNumber(12.3); // 12.30000000
	//! piCout << PIString::fromNumber(12.3, 'f', 3); // 12.300
	//! piCout << PIString::fromNumber(12.123456, 'f', 3); // 12.123
	//! piCout << PIString::fromNumber(123456789., 'g', 2); // 1.2e+08
	//! piCout << PIString::fromNumber(123456789., 'f', 0); // 123456789
	//! \endcode
	static PIString fromNumber(const double & value, char format = 'f', int precision = 8) { return dtos(value, format, precision); }

	//! \~english Returns string contains numeric representation of "value" with format "format" and precision "precision".
	//! \~russian Возвращает строковое представление числа "value" в формате "format" и точностью "precision".
	//! \~\details
	//! \~\code
	//! piCout << PIString::fromNumber(12.3); // 12.30000000
	//! piCout << PIString::fromNumber(12.3, 'f', 3); // 12.300
	//! piCout << PIString::fromNumber(12.123456, 'f', 3); // 12.123
	//! piCout << PIString::fromNumber(123456789., 'g', 2); // 1.2e+08
	//! piCout << PIString::fromNumber(123456789., 'f', 0); // 123456789
	//! \endcode
	static PIString fromNumber(const ldouble & value, char format = 'f', int precision = 8) { return dtos(value, format, precision); }

	//! \~english Returns "true" or "false"
	//! \~russian Возвращает "true" или "false"
	static PIString fromBool(const bool value) { return PIString(value ? PIStringAscii("true") : PIStringAscii("false")); }

	//! \~english Returns string constructed from terminal codepage.
	//! \~russian Возвращает строку созданную из кодировки консоли.
	static PIString fromConsole(const PIByteArray & s);

	//! \~english Returns string constructed from terminal codepage.
	//! \~russian Возвращает строку созданную из кодировки консоли.
	static PIString fromConsole(const char * s);

	//! \~english Returns string constructed from system codepage.
	//! \~russian Возвращает строку созданную из кодировки системы.
	static PIString fromSystem(const PIByteArray & s);

	//! \~english Returns string constructed from system codepage.
	//! \~russian Возвращает строку созданную из кодировки системы.
	static PIString fromSystem(const char * s);

	//! \~english Returns string constructed from UTF-8.
	//! \~russian Возвращает строку созданную из UTF-8.
	static PIString fromUTF8(const char * s, int l = -1);

	//! \~english Returns string constructed from UTF-8.
	//! \~russian Возвращает строку созданную из UTF-8.
	static PIString fromUTF8(const PIByteArray & utf);

	//! \~english Returns string constructed from ASCII.
	//! \~russian Возвращает строку созданную из ASCII.
	static PIString fromAscii(const char * s);

	//! \~english Returns string constructed from "len" chars ASCII.
	//! \~russian Возвращает строку созданную из "len" символов ASCII.
	static PIString fromAscii(const char * s, int len);

	//! \~english Returns string constructed from ASCII.
	//! \~russian Возвращает строку созданную из ASCII.
	static PIString fromAscii(const PIByteArray & ascii);

	//! \~english Returns string constructed from "cp" codepage.
	//! \~russian Возвращает строку созданную из кодировки "cp".
	static PIString fromCodepage(const char * s, const char * cp);

	//! \~english Returns string contains human readable size in B/KiB/MiB/GiB/TiB/PiB.
	//! \~russian Возвращает строку с читаемым размером B/KiB/MiB/GiB/TiB/PiB.
	//! \~\sa PIString::setReadableSize()
	static PIString readableSize(llong bytes);

	//! \~english Returns string from [percentage-encoded](https://en.wikipedia.org/wiki/Percent-encoding) "in".
	//! \~russian Возвращает строку из [URL-кодированной](https://ru.wikipedia.org/wiki/URL)"in".
	static PIString fromPercentageEncoding(const PIString & in);

	//! \~english Swaps string `str` other with this string.
	//! \~russian Меняет строку `str` с этой строкой.
	//! \~\details
	//! \~english This operation is very fast and never fails.
	//! \~russian Эта операция выполняется мгновенно без копирования памяти и никогда не дает сбоев.
	void swap(PIString & str) {
		d.swap(str.d);
		piSwap(data_, str.data_);
	}

private:
	static const char toBaseN[];
	static const char fromBaseN[];

	static PIString itos(const int num);
	static PIString ltos(const long num);
	static PIString lltos(const llong num);
	static PIString uitos(const uint num);
	static PIString ultos(const ulong num);
	static PIString ulltos(const ullong num);
	static PIString dtos(const double num, char format = 'f', int precision = 8);
	static PIString fromNumberBaseS(const llong value, int base = 10, bool * ok = 0);
	static PIString fromNumberBaseU(const ullong value, int base = 10, bool * ok = 0);
	static llong toNumberBase(const PIString & value, int base = -1, bool * ok = 0);
	void appendFromChars(const char * c, int s, const char * cp = __syslocname__);
	void buildData(const char * cp = __syslocname__) const;
	void deleteData() const;
	void trimsubstr(int & st, int & fn) const;
	PIString minArgPlaceholder();

	PIDeque<PIChar> d;
	mutable bool changed_          = true;
	mutable const char * last_loc_ = nullptr;
	mutable char * data_           = nullptr;
	mutable size_t data_size_      = 0;
};

//! \relatesalso PIBinaryStream
//! \~english Store operator.
//! \~russian Оператор сохранения.
BINARY_STREAM_WRITE(PIString) {
	s << v.d;
	return s;
}

//! \relatesalso PIBinaryStream
//! \~english Restore operator.
//! \~russian Оператор извлечения.
BINARY_STREAM_READ(PIString) {
	s >> v.d;
	v.changed_ = true;
	if (s.wasReadError()) v.clear();
	return s;
}


//! \~english Returns concatenated string.
//! \~russian Возвращает соединение строк.
inline PIString operator+(const PIString & str, const PIString & f) {
	PIString s(str);
	s += f;
	return s;
}

//! \~english Returns concatenated string.
//! \~russian Возвращает соединение строк.
inline PIString operator+(const PIString & f, const char * str) {
	PIString s(f);
	s += str;
	return s;
}

//! \~english Returns concatenated string.
//! \~russian Возвращает соединение строк.
inline PIString operator+(const char * str, const PIString & f) {
	return PIString(str) + f;
}

//! \~english Returns concatenated string.
//! \~russian Возвращает соединение строк.
inline PIString operator+(const char c, const PIString & f) {
	return PIString(c) + f;
}

//! \~english Returns concatenated string.
//! \~russian Возвращает соединение строк.
inline PIString operator+(const PIString & f, const char c) {
	PIString s(f);
	s.push_back(c);
	return s;
}


//! \relatesalso PIString
//! \~english Compare two version strings in free notation and returns 0, -1 or 1.
//! \~russian Сравнивает две строки с версиями в произвольной форме и возвращает 0, -1 или 1.
int PIP_EXPORT versionCompare(const PIString & v0, const PIString & v1, int components = 6);

//! \relatesalso PIString
//! \~english Converts version string in free notation to classic view.
//! \~russian Преобразует строку с версией в произвольной форме к классическому виду.
PIString PIP_EXPORT versionNormalize(const PIString & v);


template<>
inline uint piHash(const PIString & s) {
	return s.hash();
}

template<>
inline void piSwap(PIString & f, PIString & s) {
	f.swap(s);
}


//! \~english Returns string representation of \"v\", using PICout operator<<(T)
//! \~russian Возвращает строковое представление \"v\", используя PICout operator<<(T)
template<typename T>
inline PIString piStringify(const T & v) {
	PIString ret;
	PICout::withExternalBuffer(&ret) << v;
	return ret;
}


#endif // PISTRING_H
