32 Commits

Author SHA1 Message Date
1c46307798 Formatting & commented out do_count_digits_decimal() 2022-03-18 13:34:25 +01:00
b7159c3606 Merge pull request 'fix/zero_length_array' (#10) from fix/zero_length_array into master
Reviewed-on: http://git.mercurial-manifold.eu/an.tsouchlos/const_fmt/pulls/10
2022-03-18 00:24:51 +00:00
2be63fda5a Changed the way recursion ends in const_fmt::format_args<>() 2022-03-18 01:23:56 +01:00
036b17fca9 Formatting 2022-03-18 01:15:32 +01:00
045363c733 Merge pull request 'feature/no_std_lib' (#9) from feature/no_std_lib into master
Reviewed-on: http://git.mercurial-manifold.eu/an.tsouchlos/const_fmt/pulls/9
2022-03-17 22:45:07 +00:00
4d05a9a8fa Added const versions of std::array::begin() and std::array::end() 2022-03-17 22:44:17 +01:00
ee1153c393 Changed copy constructor signature of std::array implementation 2022-03-17 22:11:30 +01:00
b29695e148 Replaced memcpy with manual while loop 2022-03-17 18:04:06 +01:00
86930fd8b0 Added conepts; Fixed bug in const_fmt::std::true_type 2022-02-24 16:14:38 +01:00
7796be7cd4 Reimplemented is_integral and is_floating_point with different approach; Made functions constexpr inline 2022-02-23 23:12:34 +01:00
73a50cae0f Added is_integral and is_floating_point 2022-02-23 20:42:56 +01:00
91398ef23d Added remove_cv, remove_const and remove_volatile 2022-02-23 19:51:55 +01:00
b5ecd73b18 Formatting 2022-02-23 19:47:06 +01:00
826f3a54b3 Changed comment 2022-02-23 19:46:33 +01:00
ea722ec5bf Changed includes; Implemented std::pair and std::make_unsigned; changed usage of std::memcpy to memcpy 2022-02-23 19:45:17 +01:00
5ba1723295 Made array a structural type 2022-02-20 14:29:58 +01:00
8e51facd10 fixed wrong clang-format comments 2022-02-20 00:27:45 +01:00
9bd1104e5f Code compiles and passes tests without NO_STDLIB define 2022-02-20 00:26:48 +01:00
6ba83fd582 Minor changes to make sure some stuff works with no stl headers 2022-02-20 00:15:31 +01:00
8f7b1cd4e3 Implemented std::array and a bunch of other stuff 2022-02-20 00:14:33 +01:00
ec70a5bba1 Merge branch 'master' into feature/no_std_lib 2022-02-19 19:21:34 +01:00
1263dfee98 Merge pull request 'Added cmake-build-relwithdebinfo to gitignore' (#8) from fix/gitignore_builddir into master
Reviewed-on: http://git.mercurial-manifold.eu/an.tsouchlos/const_fmt/pulls/8
2022-02-19 18:18:51 +00:00
52992e255e Added cmake-build-relwithdebinfo to gitignore 2022-02-19 19:13:54 +01:00
8526ae1d91 First version of std::array 2022-02-18 23:31:15 +01:00
b8f59710d3 Merge pull request 'Updated README.md' (#7) from fix/README_wording into master
Reviewed-on: http://git.mercurial-manifold.eu/an.tsouchlos/const_fmt/pulls/7
2022-02-15 13:54:04 +00:00
184815c75b Updated README.md 2022-02-15 13:53:47 +00:00
74c4a4d582 Merge pull request 'feature/int_hex' (#6) from feature/int_hex into master
Reviewed-on: http://git.mercurial-manifold.eu/an.tsouchlos/const_fmt/pulls/6
2022-02-15 10:35:17 +00:00
2933676ea2 Fixed bug in hex formatting, wrote tests for hex int formatting 2022-02-15 11:34:49 +01:00
7115e09aca Implemented count_digits for hex and started implementing digits2_base 2022-02-15 11:13:15 +01:00
f9099ce6ee Added tests for count_digits_base, decimal, binary and hex 2022-02-15 11:12:19 +01:00
4c3024e978 Merge pull request 'Reintroduced previously erroneously removed memcpy' (#5) from fix/memcpy into master
Reviewed-on: http://git.mercurial-manifold.eu/an.tsouchlos/const_fmt/pulls/5
2022-02-15 09:29:55 +00:00
a921676b77 Reintroduced previously erroneously removed memcpy 2022-02-15 10:29:23 +01:00
12 changed files with 487 additions and 35 deletions

1
.gitignore vendored
View File

@@ -1,4 +1,5 @@
build
cmake-build-debug
cmake-build-release
cmake-build-relwithdebinfo
.idea

View File

@@ -11,8 +11,7 @@ During compile-time, the string to be formatted is preprocessed to the point onl
have to be written (If they are not available at compile time).
For example `One number: {:03}; And another one: {:05.3}` is preprocessed into `One number: 000; And another one: 00.000`.
This is returned as a `std::array<char, N>`, where `N` is automatically evaluated. The only code executed at compile
time then formats the numbers and writes them into their place in the array.
This is returned as a `std::array<char, N>`, where `N` is automatically evaluated. The only code executed at runtime then formats the numbers and writes them into their places in the array.
Disclaimer: The actual formatting code is largely shamelessly stolen from `fmtlib`.
@@ -48,8 +47,11 @@ $ ctest --test-dir build/
## Limitations
For the compile time preprocessing of format strings non non-type template parameters are heavily relied upon,
which means C++20 is required.
Only a relatively limited subset of the `fmtlib` syntax is recognized (for now anyway). In particular,
there is no support for positional arguments, alignment, chrono format specs and custom const_format specifications.
float formatting in decimal and integer formatting in decimal, binary and hexadecimal are supported.
By nature of the library design, which forces compile-time preprocessing of the const_format string, no dynamic width or
dynamic precision can be implemented.

View File

@@ -57,7 +57,7 @@ constexpr inline void format_arg(char* dest, const char* arg) {
dest = dest + fmt_data.length - len;
if (!std::is_constant_evaluated()) {
std::memcpy(dest, arg, len);
memcpy(dest, arg, len);
return;
}
for (std::size_t i = 0; i < len; ++i) {
@@ -66,16 +66,13 @@ constexpr inline void format_arg(char* dest, const char* arg) {
};
// End of recursion
template <auto ast>
constexpr inline void format_args(char*) {
}
template <auto fmt_data_array, typename first_arg_t, typename... args_t>
constexpr inline void format_args(char* dest, first_arg_t first_arg,
args_t... args) {
format_arg<fmt_data_array[0]>(dest + fmt_data_array[0].position, first_arg);
format_args<drop_first(fmt_data_array)>(dest, args...);
if constexpr(fmt_data_array.size() > 1)
format_args<drop_first(fmt_data_array)>(dest, args...);
}

View File

@@ -12,6 +12,7 @@
#include <stdint.h>
#include "stdlib.h"
#include "utility.h"
@@ -50,7 +51,9 @@ inline int do_count_digits_decimal(uint64_t n) {
6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10,
10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 15, 15,
15, 16, 16, 16, 16, 17, 17, 17, 18, 18, 18, 19, 19, 19, 19, 20};
auto t = bsr2log10[__builtin_clzll(n | 1) ^ 63];
auto t = bsr2log10[__builtin_clzll(n | 1) ^ 63];
static constexpr const uint64_t zero_or_powers_of_10[] = {
0, 0, FMT_POWERS_OF_10(1U), FMT_POWERS_OF_10(1000000000ULL),
10000000000000000000ULL};
@@ -69,11 +72,21 @@ constexpr inline auto count_digits_base(uint64_t n) -> int {
return result;
} else {
if (!std::is_constant_evaluated()) {
return do_count_digits_decimal(n);
}
if constexpr (t_format_type == FormatType::x) {
int result = 0;
return count_digits_decimal_fallback(n);
while (n) {
n = n >> 4;
result += 1;
}
return (result + count_digits_base<FormatType::b>(n));
} else {
// if (!std::is_constant_evaluated()) {
// return do_count_digits_decimal(n);
// }
return count_digits_decimal_fallback(n);
}
}
}
@@ -85,25 +98,53 @@ constexpr inline const char* digits2_base(size_t value) {
if constexpr (t_format_type == FormatType::b) {
return &"00011011"[value * 2];
} else {
return &"0001020304050607080910111213141516171819"
"2021222324252627282930313233343536373839"
"4041424344454647484950515253545556575859"
"6061626364656667686970717273747576777879"
"8081828384858687888990919293949596979899"[value * 2];
if constexpr (t_format_type == FormatType::x) {
// clang-format off
return &"000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F"
"202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F"
"404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F"
"606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F"
"808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9F"
"A0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF"
"C0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDF"
"E0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF"[value * 2];
// clang-format on
} else {
return &"0001020304050607080910111213141516171819"
"2021222324252627282930313233343536373839"
"4041424344454647484950515253545556575859"
"6061626364656667686970717273747576777879"
"8081828384858687888990919293949596979899"[value * 2];
}
}
}
constexpr inline void copy2(char* dst, const char* src) {
if (!std::is_constant_evaluated()) {
memcpy(dst, src, 2);
return;
}
*dst++ = static_cast<char>(*src++);
*dst = static_cast<char>(*src);
}
template <FormatType t_format_type>
consteval inline unsigned get_base_divisor() {
switch (t_format_type) {
case FormatType::b:
return 2;
case FormatType::x:
return 16;
default:
return 10;
}
}
template <FormatType t_format_type, typename uint_t>
constexpr inline void format_base(char* out, uint_t value, int n_digits,
int size) {
constexpr unsigned divisor = (t_format_type == FormatType::b) ? 2 : 10;
constexpr unsigned divisor = get_base_divisor<t_format_type>();
constexpr unsigned square_divisor = const_pow(divisor, 2);
if (n_digits > size) {
@@ -122,7 +163,7 @@ constexpr inline void format_base(char* out, uint_t value, int n_digits,
}
if (value < divisor) {
*--out = static_cast<char>('0' + value);
*--out = digits2_base<t_format_type>(value * divisor)[0];
return;
}
@@ -160,7 +201,6 @@ constexpr std::pair<int_t, bool> get_abs_value(int_t value) {
template <fmt_data_t t_fmt_node, std::unsigned_integral uint_t>
constexpr inline void format_int(char* out, uint_t value) {
// format_decimal(out, value, count_digits(value), t_fmt_node.length);
format_base<t_fmt_node.type>(out, value,
count_digits_base<t_fmt_node.type>(value),
t_fmt_node.length);

View File

@@ -5,7 +5,7 @@
#include "types.h"
// clang-const_format off
// clang-format off
/*
*
@@ -34,7 +34,7 @@
*
*/
// clang-const_format on
// clang-format on
namespace const_fmt { namespace const_fmt_detail {

304
const_fmt/stdlib.h Normal file
View File

@@ -0,0 +1,304 @@
#ifndef CONST_FMT_STDLIB_H
#define CONST_FMT_STDLIB_H
/*
*
* Disclaimer: Very bad (partially at least) implementation of some features of
* the C++ Standard Library. Not meant as a full-on stdlib implementation, only
* for usage in this project (Underlined by the fact, that in this case the
* namespace std is actually const_fmt::std)
*
*/
#ifndef CONST_FMT_NO_CPP_STDLIB
#include <array>
#include <cstring>
#else
#include <stdint.h>
#include <string.h>
namespace const_fmt { namespace std {
using size_t = uint16_t;
/*
*
* type_traits
*
*/
// various
constexpr inline bool is_constant_evaluated() noexcept {
return __builtin_is_constant_evaluated();
}
struct true_type {
constexpr static bool value = true;
};
struct false_type {
constexpr static bool value = false;
};
// is_same
template <typename fist_t, typename second_t>
struct is_same : public false_type {};
template <typename type_t>
struct is_same<type_t, type_t> : public true_type {};
// is_one_of
template <typename...>
struct is_one_of;
template <typename type_t>
struct is_one_of<type_t> {
constexpr static bool value = false;
};
template <typename type_t, typename first_t, typename... rest_t>
struct is_one_of<type_t, first_t, rest_t...> {
constexpr static bool value = std::is_same<type_t, first_t>::value ||
is_one_of<type_t, rest_t...>::value;
};
// remove_x
// clang-format off
template <typename type_t> struct remove_cv { typedef type_t type; };
template <typename type_t> struct remove_cv<const type_t> { typedef type_t type; };
template <typename type_t> struct remove_cv<volatile type_t> { typedef type_t type; };
template <typename type_t> struct remove_cv<const volatile type_t> { typedef type_t type; };
template <typename type_t> struct remove_const { typedef type_t type; };
template <typename type_t> struct remove_const<const type_t> { typedef type_t type; };
template <typename type_t> struct remove_volatile { typedef type_t type; };
template <typename type_t> struct remove_volatile<volatile type_t> { typedef type_t type; };
template <typename type_t> struct remove_reference { using type = type_t; };
template <typename type_t> struct remove_reference<type_t &> { using type = type_t; };
template <typename type_t> struct remove_reference<type_t &&> { using type = type_t; };
template <typename type_t> using remove_reference_t = typename std::remove_reference<type_t>::type;
template <typename type_t> using remove_cv_t = typename std::remove_cv<type_t>::type;
template <typename type_t> using remove_const_t = typename std::remove_const<type_t>::type;
template <typename type_t> using remove_volatile_t = typename std::remove_volatile<type_t>::type;
// clang-format on
// is_integral
template <typename type_t>
using is_integral =
is_one_of<remove_cv_t<type_t>, bool, char, signed char, unsigned char,
wchar_t, char16_t, char32_t, short, unsigned short, int,
unsigned int, long, unsigned long, long long, unsigned long long>;
// is_signed
template <typename _Tp>
using is_signed_integer = is_one_of<remove_cv_t<_Tp>, signed char, signed short,
signed int, signed long, signed long long>;
// is_floating_point
template <typename type_t>
using is_floating_point =
is_one_of<remove_cv_t<type_t>, float, double, long double>;
// make_unsigned
// clang-format off
template <typename type_t> struct make_unsigned { using type = type_t; };
template <> struct make_unsigned<signed char> { using type = char; };
template <> struct make_unsigned<unsigned short> { using type = unsigned short; };
template <> struct make_unsigned<signed int> { using type = unsigned int; };
template <> struct make_unsigned<signed long> { using type = unsigned long; };
template <> struct make_unsigned<signed long long> { using type = unsigned long long; };
// clang-format on
// value definitions
template <typename type_t>
inline constexpr bool is_integral_v = is_integral<type_t>::value;
template <typename type_t>
inline constexpr bool is_signed_integer_v = is_signed_integer<type_t>::value;
template <typename type_t>
inline constexpr bool is_floating_point_v = is_floating_point<type_t>::value;
/*
*
* concepts
*
*/
template <typename type_t>
concept integral = is_integral_v<type_t>;
template <typename type_t>
concept signed_integral = is_signed_integer_v<type_t>;
template <typename type_t>
concept unsigned_integral = integral<type_t> && !signed_integral<type_t>;
template <typename type_t>
concept floating_point = is_floating_point_v<type_t>;
/*
*
* algorithm
*
*/
template <typename input_t, typename output_t>
constexpr inline void copy(const input_t* start, const input_t* end,
output_t* dest_start) {
auto temp = start;
while (temp != end)
*(dest_start++) = *(temp++);
}
/*
*
* utility
*
*/
template <typename T>
std::remove_reference_t<T>&& move(T&& arg) noexcept {
return reinterpret_cast<std::remove_reference_t<T>&&>(arg);
}
template <typename T>
constexpr inline void swap(T& t1, T& t2) {
T temp = std::move(t1);
t1 = std::move(t2);
t2 = std::move(temp);
}
template <typename first_t, typename second_t>
struct pair {
first_t first;
second_t second;
};
/*
*
* array
*
*/
// TODO: Is std::size_t really the best bet here?
template <typename data_t, std::size_t t_size>
class array {
public:
template <typename... args_t>
constexpr array(args_t... args) noexcept : m_data{args...} {
static_assert(sizeof...(args) == t_size, "Invalid number of arguments");
}
constexpr array() noexcept = default;
constexpr array(const array&) = default;
constexpr array(array&&) = default;
constexpr array& operator=(const array& other) = default;
constexpr array& operator=(array&& other) = default;
constexpr void swap(array<data_t, t_size>& other) noexcept {
for (int i = 0; i < t_size; ++i) {
using std::swap;
swap(m_data[i], other.m_data[i]);
}
}
constexpr std::size_t size() const noexcept {
return t_size;
}
constexpr data_t& operator[](std::size_t index) noexcept {
return m_data[index];
}
constexpr const data_t& operator[](std::size_t index) const noexcept {
return m_data[index];
}
using iterator = data_t*;
using const_iterator = const data_t*;
constexpr iterator begin() noexcept {
return &(m_data[0]);
}
constexpr iterator end() noexcept {
return (&(m_data[t_size - 1]) + 1);
}
constexpr const_iterator begin() const noexcept {
return &(m_data[0]);
}
constexpr const_iterator end() const noexcept {
return (&(m_data[t_size - 1]) + 1);
}
constexpr const_iterator cbegin() const noexcept {
return &(m_data[0]);
}
constexpr const_iterator cend() const noexcept {
return (&(m_data[t_size - 1]) + 1);
}
data_t m_data[t_size];
};
}} // namespace const_fmt::std
#endif
#endif // CONST_FMT_STDLIB_H

View File

@@ -2,7 +2,8 @@
#define LOGGER_TYPES_H
#include <array>
#include "stdlib.h"
namespace const_fmt { namespace const_fmt_detail {
@@ -19,8 +20,7 @@ template <std::size_t N>
class ConstString {
public:
constexpr ConstString(const char (&content)[N]) noexcept {
std::copy(std::begin(content), std::end(content),
std::begin(m_content));
std::copy(&content[0], (&content[N - 1] + 1), m_content.begin());
}
constexpr char operator[](std::size_t index) const noexcept {

View File

@@ -2,8 +2,9 @@
#define LOGGER_UTILITY_H
#include <cstring>
#include <string.h>
#include "stdlib.h"
#include "types.h"

View File

@@ -1,17 +1,19 @@
#define CONST_FMT_NO_CPP_STDLIB
#include <const_fmt/format.h>
#include <iostream>
#include <string_view>
//#include <iostream>
//#include <string_view>
using namespace const_fmt;
int main() {
constexpr auto s = "This is an integer: {:08b}, and this is a float: {:09.4b}"_const_fmt(125u, -86.2);
constexpr auto s = "This is an integer: {:08x}, and this is a float: {:09.4b}"_const_fmt(122u, -86.2);
// Convert s (with a type of 'std::array<char, N>') into something
// writable to std::cout
std::string_view sv{&s[0], s.size()};
std::cout << sv << std::endl;
// std::string_view sv{&s[0], s.size()};
//
// std::cout << sv << std::endl;
return 0;
}

View File

@@ -25,6 +25,8 @@ endmacro()
package_add_test(utility_test src/utility.cpp)
package_add_test(parse_test src/parse.cpp)
package_add_test(format_utility_test src/format_utility.cpp)
package_add_test(format_decimal_test src/format_decimal.cpp)
package_add_test(format_binary_test src/format_binary.cpp)
package_add_test(format_hex_test src/format_hex.cpp)

59
test/src/format_hex.cpp Normal file
View File

@@ -0,0 +1,59 @@
#include <const_fmt/format.h>
#include <gtest/gtest.h>
using namespace const_fmt;
using namespace const_fmt::const_fmt_detail;
TEST(FormatHex, positive_int) {
constexpr std::array<char, 8> control1 = {'0', '0', '0', '0',
'0', '0', '1', '0'};
constexpr std::array<char, 8> formatted1 = const_format<"{:08x}">(0x10);
constexpr std::array<char, 8> control2 = {' ', ' ', ' ', 'F',
'F', 'A', '7', '6'};
constexpr std::array<char, 8> formatted2 = const_format<"{:8x}">(0xffa76);
constexpr std::array<char, 8> control3 = {'0', '0', '0', '0',
'B', 'C', 'E', 'F'};
constexpr std::array<char, 8> formatted3 = const_format<"{:08.4x}">(0xbcef);
constexpr std::array<char, 4> control4 = {'A', 'D', '0', '1'};
constexpr std::array<char, 4> formatted4 = const_format<"{:4x}">(0xad01);
constexpr std::array<char, 4> control5 = {'f', 'f', 'f', 'f'};
constexpr std::array<char, 4> formatted5 = const_format<"{:4x}">(0x12345);
EXPECT_EQ(control1, formatted1);
EXPECT_EQ(control2, formatted2);
EXPECT_EQ(control3, formatted3);
EXPECT_EQ(control4, formatted4);
EXPECT_EQ(control5, formatted5);
}
TEST(FormatHex, negative_int) {
constexpr std::array<char, 8> control1 = {'-', '0', '0', '0',
'0', '0', '1', '0'};
constexpr std::array<char, 8> formatted1 = const_format<"{:08x}">(-0x10);
constexpr std::array<char, 8> control2 = {' ', ' ', '-', 'F',
'F', 'A', '7', '6'};
constexpr std::array<char, 8> formatted2 = const_format<"{:8x}">(-0xffa76);
constexpr std::array<char, 8> control3 = {'-', '0', '0', '0',
'B', 'C', 'E', 'F'};
constexpr std::array<char, 8> formatted3 = const_format<"{:08.4x}">(-0xbcef);
constexpr std::array<char, 4> control4 = {'-', 'A', 'D', '1'};
constexpr std::array<char, 4> formatted4 = const_format<"{:4x}">(-0xad1);
constexpr std::array<char, 4> control5 = {'-', 'f', 'f', 'f'};
constexpr std::array<char, 4> formatted5 = const_format<"{:04x}">(-0x1234);
EXPECT_EQ(control1, formatted1);
EXPECT_EQ(control2, formatted2);
EXPECT_EQ(control3, formatted3);
EXPECT_EQ(control4, formatted4);
EXPECT_EQ(control5, formatted5);
}

View File

@@ -0,0 +1,44 @@
#include <const_fmt/format_impl.h>
#include <limits>
#include <gtest/gtest.h>
using namespace const_fmt;
using namespace const_fmt::const_fmt_detail;
TEST(FormatUtility, count_digits_base_decimal) {
constexpr unsigned length1 = count_digits_base<FormatType::d>(123);
constexpr unsigned length2 = count_digits_base<FormatType::d>(std::numeric_limits<uint64_t>::max());
constexpr unsigned length3 = count_digits_base<FormatType::d>(10000011);
constexpr unsigned length4 = count_digits_base<FormatType::d>(1);
EXPECT_EQ(length1, 3);
EXPECT_EQ(length2, 20);
EXPECT_EQ(length3, 8);
EXPECT_EQ(length4, 1);
}
TEST(FormatUtility, count_digits_base_binary) {
constexpr unsigned length1 = count_digits_base<FormatType::b>(0b1001);
constexpr unsigned length2 = count_digits_base<FormatType::b>(std::numeric_limits<uint64_t>::max());
constexpr unsigned length3 = count_digits_base<FormatType::b>(0b10000001);
constexpr unsigned length4 = count_digits_base<FormatType::b>(0b01);
EXPECT_EQ(length1, 4);
EXPECT_EQ(length2, 64);
EXPECT_EQ(length3, 8);
EXPECT_EQ(length4, 1);
}
TEST(FormatUtility, count_digits_base_hex) {
constexpr unsigned length1 = count_digits_base<FormatType::x>(0x123);
constexpr unsigned length2 = count_digits_base<FormatType::x>(std::numeric_limits<uint64_t>::max());
constexpr unsigned length3 = count_digits_base<FormatType::x>(0x1000000f);
constexpr unsigned length4 = count_digits_base<FormatType::x>(0x01);
EXPECT_EQ(length1, 3);
EXPECT_EQ(length2, 16);
EXPECT_EQ(length3, 8);
EXPECT_EQ(length4, 1);
}