diff --git a/include/pocketpy/common/algorithm.h b/include/pocketpy/common/algorithm.h index 2e63fa8e..f3d1b731 100644 --- a/include/pocketpy/common/algorithm.h +++ b/include/pocketpy/common/algorithm.h @@ -40,7 +40,8 @@ extern "C" { bool c11__stable_sort(void* ptr, int count, int elem_size, - int (*f_le)(const void* a, const void* b)); + int (*f_lt)(const void* a, const void* b, void* extra), + void* extra); #ifdef __cplusplus } diff --git a/include/pocketpy/common/vector.h b/include/pocketpy/common/vector.h index ae6260fa..cf475aea 100644 --- a/include/pocketpy/common/vector.h +++ b/include/pocketpy/common/vector.h @@ -75,10 +75,10 @@ c11_array c11_vector__submit(c11_vector* self); (self)->count--; \ } while(0) -#define c11_vector__reverse(T, self, start, end) \ +#define c11__reverse(T, self) \ do { \ - T* p = (T*)(self)->data + (start); \ - T* q = (T*)(self)->data + (end); \ + T* p = (T*)(self)->data; \ + T* q = (T*)(self)->data + (self)->count - 1; \ while(p < q) { \ T tmp = *p; \ *p = *q; \ diff --git a/include/pocketpy/interpreter/vm.h b/include/pocketpy/interpreter/vm.h index d6abd09f..0c2d714e 100644 --- a/include/pocketpy/interpreter/vm.h +++ b/include/pocketpy/interpreter/vm.h @@ -91,7 +91,13 @@ py_Type pk_VM__new_type(pk_VM* self, pk_FrameResult pk_VM__vectorcall(pk_VM* self, uint16_t argc, uint16_t kwargc, bool opcall); const char* pk_opname(Opcode op); -py_TValue* pk_arrayview(py_Ref self, int* size); + +py_TValue* pk_arrayview(py_Ref self, int* length); + +/// Assumes [a, b] are on the stack, performs a binary op. +/// The result is stored in `self->last_retval`. +/// The stack remains unchanged. +bool pk_stack_binaryop(pk_VM* self, py_Name op, py_Name rop); // type registration void pk_object__register(); diff --git a/include/pocketpy/objects/codeobject.h b/include/pocketpy/objects/codeobject.h index a43db0c6..4f3c19e9 100644 --- a/include/pocketpy/objects/codeobject.h +++ b/include/pocketpy/objects/codeobject.h @@ -118,16 +118,15 @@ typedef struct FuncDecl { typedef FuncDecl* FuncDecl_; FuncDecl_ FuncDecl__rcnew(pk_SourceData_ src, c11_sv name); -void FuncDecl__dtor(FuncDecl* self); void FuncDecl__add_kwarg(FuncDecl* self, int index, uint16_t key, const py_TValue* value); -void FuncDecl__gc_mark(const FuncDecl* self); // runtime function typedef struct Function { FuncDecl_ decl; - PyObject* module; // weak ref - PyObject* clazz; // weak ref + PyObject* module; // weak ref + PyObject* clazz; // weak ref pk_NameDict* closure; // strong ref + py_CFunction cfunc; // wrapped C function } Function; void Function__ctor(Function* self, FuncDecl_ decl, PyObject* module); diff --git a/include/pocketpy/pocketpy.h b/include/pocketpy/pocketpy.h index 9d5f8e0c..86723e0e 100644 --- a/include/pocketpy/pocketpy.h +++ b/include/pocketpy/pocketpy.h @@ -89,7 +89,7 @@ void py_newfunction2(py_Ref out, const char* sig, enum BindType bt, const char* docstring, - const py_Ref upvalue); + int slots); // old style argc-based function void py_newnativefunc(py_Ref out, py_CFunction); @@ -134,13 +134,13 @@ py_GlobalRef py_tpmagic(py_Type type, py_Name name); #define py_bindmagic(type, __magic__, f) py_newnativefunc(py_tpmagic((type), __magic__), (f)) // new style decl-based bindings -py_TmpRef py_bind(py_Ref obj, const char* sig, py_CFunction f); -py_TmpRef py_bind2(py_Ref obj, - const char* sig, - py_CFunction f, - enum BindType bt, - const char* docstring, - const py_Ref upvalue); +void py_bind(py_Ref obj, const char* sig, py_CFunction f); +void py_bind2(py_Ref obj, + const char* sig, + py_CFunction f, + enum BindType bt, + const char* docstring, + int slots); // old style argc-based bindings void py_bindmethod(py_Type type, const char* name, py_CFunction f); void py_bindmethod2(py_Type type, const char* name, py_CFunction f, enum BindType bt); diff --git a/src/common/algorithm.c b/src/common/algorithm.c index 74b9df55..b32fe238 100644 --- a/src/common/algorithm.c +++ b/src/common/algorithm.c @@ -8,9 +8,10 @@ static bool merge(char* a, char* b_end, char* r, int elem_size, - int (*f_lt)(const void* a, const void* b)) { + int (*f_lt)(const void* a, const void* b, void* extra), + void* extra) { while(a < a_end && b < b_end) { - int res = f_lt(a, b); + int res = f_lt(a, b, extra); // check error if(res == -1) return false; if(res) { @@ -34,14 +35,15 @@ static bool merge(char* a, bool c11__stable_sort(void* ptr_, int count, int elem_size, - int (*f_lt)(const void* a, const void* b)) { + int (*f_lt)(const void* a, const void* b, void* extra), + void* extra) { // merge sort char *ptr = ptr_, *tmp = malloc(count * elem_size); for(int seg = 1; seg < count; seg *= 2) { for(char* a = ptr; a < ptr + (count - seg) * elem_size; a += 2 * seg * elem_size) { char *b = a + seg * elem_size, *a_end = b, *b_end = b + seg * elem_size; if(b_end > ptr + count * elem_size) b_end = ptr + count * elem_size; - bool ok = merge(a, a_end, b, b_end, tmp, elem_size, f_lt); + bool ok = merge(a, a_end, b, b_end, tmp, elem_size, f_lt, extra); if(!ok) { free(tmp); return false; diff --git a/src/interpreter/ceval.c b/src/interpreter/ceval.c index d46d3131..78537d41 100644 --- a/src/interpreter/ceval.c +++ b/src/interpreter/ceval.c @@ -5,7 +5,6 @@ #include "pocketpy/pocketpy.h" #include -static bool stack_binaryop(pk_VM* self, py_Name op, py_Name rop); static bool stack_unpack_sequence(pk_VM* self, uint16_t arg); #define DISPATCH() \ @@ -94,6 +93,20 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) { case tp_float: c11_sbuf__write_f64(&buf, p->_f64, -1); break; case tp_bool: c11_sbuf__write_cstr(&buf, p->_bool ? "True" : "False"); break; case tp_none_type: c11_sbuf__write_cstr(&buf, "None"); break; + case tp_list: { + pk_sprintf(&buf, "list(%d)", py_list__len(p)); + break; + } + case tp_tuple: { + pk_sprintf(&buf, "tuple(%d)", py_list__len(p)); + break; + } + case tp_function: { + Function* ud = py_touserdata(p); + c11_sbuf__write_cstr(&buf, ud->decl->code.name->data); + c11_sbuf__write_cstr(&buf, "()"); + break; + } case tp_type: { pk_sprintf(&buf, "", py_totype(p)); break; @@ -541,7 +554,7 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) { case OP_BINARY_OP: { py_Name op = byte.arg & 0xFF; py_Name rop = byte.arg >> 8; - if(!stack_binaryop(self, op, rop)) goto __ERROR; + if(!pk_stack_binaryop(self, op, rop)) goto __ERROR; POP(); *TOP() = self->last_retval; DISPATCH(); @@ -728,10 +741,7 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) { return RES_RETURN; } -/// Assumes [a, b] are on the stack, performs a binary op. -/// The result is stored in `self->last_retval`. -/// The stack remains unchanged. -static bool stack_binaryop(pk_VM* self, py_Name op, py_Name rop) { +bool pk_stack_binaryop(pk_VM* self, py_Name op, py_Name rop) { // [a, b] py_Ref magic = py_tpfindmagic(SECOND()->type, op); if(magic) { @@ -760,7 +770,7 @@ bool py_binaryop(const py_Ref lhs, const py_Ref rhs, py_Name op, py_Name rop) { pk_VM* self = pk_current_vm; PUSH(lhs); PUSH(rhs); - bool ok = stack_binaryop(self, op, rop); + bool ok = pk_stack_binaryop(self, op, rop); STACK_SHRINK(2); return ok; } diff --git a/src/interpreter/vm.c b/src/interpreter/vm.c index 58e25f53..49bbb0e2 100644 --- a/src/interpreter/vm.c +++ b/src/interpreter/vm.c @@ -307,12 +307,12 @@ bool __prepare_py_call(py_TValue* buffer, if(decl->starred_kwarg != -1) py_newdict(&buffer[decl->starred_kwarg]); - for(int j = 0; j < kwargc; j += 2) { - py_Name key = py_toint(&p1[j]); + for(int j = 0; j < kwargc; j++) { + py_Name key = py_toint(&p1[2 * j]); int index = c11_smallmap_n2i__get(&decl->kw_to_index, key, -1); // if key is an explicit key, set as local variable if(index >= 0) { - buffer[index] = p1[j + 1]; + buffer[index] = p1[2 * j + 1]; } else { // otherwise, set as **kwargs if possible if(decl->starred_kwarg == -1) { @@ -336,6 +336,7 @@ pk_FrameResult pk_VM__vectorcall(pk_VM* self, uint16_t argc, uint16_t kwargc, bo // [callable, , args..., kwargs...] // ^p0 ^p1 ^_sp +#if 0 // handle boundmethod, do a patch if(p0->type == tp_bound_method) { assert(false); @@ -347,9 +348,9 @@ pk_FrameResult pk_VM__vectorcall(pk_VM* self, uint16_t argc, uint16_t kwargc, bo // p1[-(ARGC + 1)] = bm.self; // [unbound, self, args..., kwargs...] } +#endif py_Ref argv = py_isnil(p0 + 1) ? p0 + 2 : p0 + 1; - int argc2 = argv - p0; if(p0->type == tp_function) { /*****************_py_call*****************/ @@ -368,14 +369,21 @@ pk_FrameResult pk_VM__vectorcall(pk_VM* self, uint16_t argc, uint16_t kwargc, bo if(!ok) return RES_ERROR; // copy buffer back to stack self->stack.sp = argv + co->nlocals; - for(int j = 0; j < co->nlocals; j++) - argv[j] = self->__vectorcall_buffer[j]; - break; + memcpy(argv, self->__vectorcall_buffer, co->nlocals * sizeof(py_TValue)); + // submit the call + if(!fn->cfunc) { + pk_VM__push_frame(self, Frame__new(co, fn->module, p0, p0, argv, co)); + return opcall ? RES_CALL : pk_VM__run_top_frame(self); + } else { + bool ok = fn->cfunc(co->nlocals, argv); + self->stack.sp = p0; + return ok ? RES_RETURN : RES_ERROR; + } } case FuncType_SIMPLE: - if(argc2 != fn->decl->args.count) { + if(p1 - argv != fn->decl->args.count) { const char* fmt = "%s() takes %d positional arguments but %d were given"; - TypeError(fmt, co->name, fn->decl->args.count, argc2); + TypeError(fmt, co->name->data, fn->decl->args.count, p1 - argv); return RES_ERROR; } if(kwargc) { @@ -387,7 +395,9 @@ pk_FrameResult pk_VM__vectorcall(pk_VM* self, uint16_t argc, uint16_t kwargc, bo self->stack.sp = argv + co->nlocals; // initialize local variables to PY_NIL memset(p1, 0, (char*)self->stack.sp - (char*)p1); - break; + // submit the call + pk_VM__push_frame(self, Frame__new(co, fn->module, p0, p0, argv, co)); + return opcall ? RES_CALL : pk_VM__run_top_frame(self); case FuncType_GENERATOR: assert(false); break; @@ -400,11 +410,7 @@ pk_FrameResult pk_VM__vectorcall(pk_VM* self, uint16_t argc, uint16_t kwargc, bo default: c11__unreachedable(); }; - // simple or normal - Frame* frame = Frame__new(co, fn->module, p0, p0, argv, co); - pk_VM__push_frame(self, frame); - if(opcall) return RES_CALL; - return pk_VM__run_top_frame(self); + c11__unreachedable(); /*****************_py_call*****************/ } diff --git a/src/objects/codeobject.c b/src/objects/codeobject.c index 4d2ae169..36109651 100644 --- a/src/objects/codeobject.c +++ b/src/objects/codeobject.c @@ -13,6 +13,13 @@ bool Bytecode__is_forward_jump(const Bytecode* self) { return self->op >= OP_JUMP_FORWARD && self->op <= OP_LOOP_BREAK; } +static void FuncDecl__dtor(FuncDecl* self) { + CodeObject__dtor(&self->code); + c11_vector__dtor(&self->args); + c11_vector__dtor(&self->kwargs); + c11_smallmap_n2i__dtor(&self->kw_to_index); +} + FuncDecl_ FuncDecl__rcnew(pk_SourceData_ src, c11_sv name) { FuncDecl* self = malloc(sizeof(FuncDecl)); self->rc.count = 1; @@ -33,13 +40,6 @@ FuncDecl_ FuncDecl__rcnew(pk_SourceData_ src, c11_sv name) { return self; } -void FuncDecl__dtor(FuncDecl* self) { - CodeObject__dtor(&self->code); - c11_vector__dtor(&self->args); - c11_vector__dtor(&self->kwargs); - c11_smallmap_n2i__dtor(&self->kw_to_index); -} - void FuncDecl__add_kwarg(FuncDecl* self, int index, uint16_t key, const py_TValue* value) { c11_smallmap_n2i__set(&self->kw_to_index, key, index); FuncDeclKwArg item = {index, key, *value}; @@ -99,6 +99,7 @@ void Function__ctor(Function* self, FuncDecl_ decl, PyObject* module) { self->module = module; self->clazz = NULL; self->closure = NULL; + self->cfunc = NULL; } void Function__dtor(Function* self) { diff --git a/src/public/modules.c b/src/public/modules.c index 6c10fc82..eb8cb5fe 100644 --- a/src/public/modules.c +++ b/src/public/modules.c @@ -121,6 +121,25 @@ static bool _py_builtins__next(int argc, py_Ref argv) { return py_exception("StopIteration", ""); } +static bool _py_builtins__sorted(int argc, py_Ref argv) { + PY_CHECK_ARGC(3); + // convert _0 to list object + if(!py_tpcall(tp_list, 1, argv)) return false; + py_Ref retval = py_pushtmp(); + py_Ref sort = py_pushtmp(); + py_Ref self = py_pushtmp(); + py_Ref key = py_pushtmp(); + py_Ref reverse = py_pushtmp(); + *self = *retval = *py_retval(); + bool ok = py_getunboundmethod(self, py_name("sort"), sort, self); + if(!ok) return false; + *key = argv[1]; + *reverse = argv[2]; + if(!py_vectorcall(2, 0)) return false; + *py_retval() = *retval; + return true; +} + py_TValue pk_builtins__register() { py_Ref builtins = py_newmodule("builtins", NULL); py_bindnativefunc(builtins, "repr", _py_builtins__repr); @@ -129,6 +148,8 @@ py_TValue pk_builtins__register() { py_bindnativefunc(builtins, "hex", _py_builtins__hex); py_bindnativefunc(builtins, "iter", _py_builtins__iter); py_bindnativefunc(builtins, "next", _py_builtins__next); + + py_bind(builtins, "sorted(iterable, key=None, reverse=False)", _py_builtins__sorted); return *builtins; } diff --git a/src/public/py_list.c b/src/public/py_list.c index c662b94f..3ed77227 100644 --- a/src/public/py_list.c +++ b/src/public/py_list.c @@ -107,6 +107,16 @@ static bool _py_list__new__(int argc, py_Ref argv) { return true; } if(argc == 2) { + int length; + py_TValue* p = pk_arrayview(py_arg(1), &length); + if(p) { + py_newlistn(py_retval(), length); + for(int i = 0; i < length; i++) { + py_list__setitem(py_retval(), i, p + i); + } + return true; + } + py_Ref iter = py_pushtmp(); py_Ref list = py_pushtmp(); if(!py_iter(py_arg(1))) return false; @@ -274,11 +284,7 @@ static bool _py_list__index(int argc, py_Ref argv) { static bool _py_list__reverse(int argc, py_Ref argv) { PY_CHECK_ARGC(1); List* self = py_touserdata(py_arg(0)); - for(int i = 0; i < self->count / 2; i++) { - py_TValue tmp = c11__getitem(py_TValue, self, i); - c11__setitem(py_TValue, self, i, c11__getitem(py_TValue, self, self->count - i - 1)); - c11__setitem(py_TValue, self, self->count - i - 1, tmp); - } + c11__reverse(py_TValue, self); py_newnone(py_retval()); return true; } @@ -319,10 +325,44 @@ static bool _py_list__insert(int argc, py_Ref argv) { return true; } +static int _py_lt_with_key(py_TValue* a, py_TValue* b, py_TValue* key) { + if(!key) return py_lt(a, b); + pk_VM* vm = pk_current_vm; + // project a + py_push(key); + py_pushnil(); + py_push(a); + if(!py_vectorcall(1, 0)) return -1; + py_push(py_retval()); + // project b + py_push(key); + py_pushnil(); + py_push(b); + if(!py_vectorcall(1, 0)) return -1; + py_push(py_retval()); + // binary op + bool ok = pk_stack_binaryop(vm, __lt__, __gt__); + if(!ok) return -1; + py_shrink(2); + return py_tobool(py_retval()); +} + +// sort(self, key=None, reverse=False) static bool _py_list__sort(int argc, py_Ref argv) { - PY_CHECK_ARGC(1); List* self = py_touserdata(py_arg(0)); - c11__stable_sort(self->data, self->count, sizeof(py_TValue), (int (*)(const void*, const void*))py_lt); + + py_Ref key = py_arg(1); + if(py_isnone(key)) key = NULL; + + c11__stable_sort(self->data, + self->count, + sizeof(py_TValue), + (int (*)(const void*, const void*, void*))_py_lt_with_key, + key); + + PY_CHECK_ARG_TYPE(2, tp_bool); + bool reverse = py_tobool(py_arg(2)); + if(reverse) c11__reverse(py_TValue, self); py_newnone(py_retval()); return true; } @@ -355,5 +395,7 @@ py_Type pk_list__register() { py_bindmethod(type, "pop", _py_list__pop); py_bindmethod(type, "insert", _py_list__insert); py_bindmethod(type, "sort", _py_list__sort); + + py_bind(py_tpobject(type), "sort(self, key=None, reverse=False)", _py_list__sort); return type; } \ No newline at end of file diff --git a/src/public/values.c b/src/public/values.c index 8d1e8689..d3578ad9 100644 --- a/src/public/values.c +++ b/src/public/values.c @@ -5,6 +5,7 @@ #include "pocketpy/common/utils.h" #include "pocketpy/objects/object.h" #include "pocketpy/interpreter/vm.h" +#include "pocketpy/compiler/compiler.h" void py_newint(py_Ref out, int64_t val) { out->type = tp_int; @@ -41,9 +42,8 @@ void py_newellipsis(py_Ref out) { void py_newnil(py_Ref out) { out->type = 0; } - void py_newfunction(py_Ref out, py_CFunction f, const char* sig) { - py_newfunction2(out, f, sig, BindType_FUNCTION, NULL, NULL); + py_newfunction2(out, f, sig, BindType_FUNCTION, NULL, 0); } void py_newfunction2(py_Ref out, @@ -51,7 +51,23 @@ void py_newfunction2(py_Ref out, const char* sig, enum BindType bt, const char* docstring, - const py_Ref upvalue) {} + int slots) { + char buffer[256]; + snprintf(buffer, sizeof(buffer), "def %s: pass", sig); + // fn(a, b, *c, d=1) -> None + CodeObject code; + pk_SourceData_ source = pk_SourceData__rcnew(buffer, "", EXEC_MODE, false); + Error* err = pk_compile(source, &code); + if(err) abort(); + if(code.func_decls.count != 1) abort(); + FuncDecl_ decl = c11__getitem(FuncDecl_, &code.func_decls, 0); + // construct the function + Function* ud = py_newobject(out, tp_function, slots, sizeof(Function)); + Function__ctor(ud, decl, NULL); + ud->cfunc = f; + CodeObject__dtor(&code); + PK_DECREF(source); +} void py_newnativefunc(py_Ref out, py_CFunction f) { out->type = tp_nativefunc; @@ -59,22 +75,30 @@ void py_newnativefunc(py_Ref out, py_CFunction f) { out->_cfunc = f; } -void py_bindmethod(py_Type type, const char *name, py_CFunction f){ +void py_bindmethod(py_Type type, const char* name, py_CFunction f) { py_bindmethod2(type, name, f, BindType_FUNCTION); } -void py_bindmethod2(py_Type type, const char *name, py_CFunction f, enum BindType bt){ +void py_bindmethod2(py_Type type, const char* name, py_CFunction f, enum BindType bt) { py_TValue tmp; py_newnativefunc(&tmp, f); py_setdict(py_tpobject(type), py_name(name), &tmp); } -void py_bindnativefunc(py_Ref obj, const char *name, py_CFunction f){ +void py_bindnativefunc(py_Ref obj, const char* name, py_CFunction f) { py_TValue tmp; py_newnativefunc(&tmp, f); py_setdict(obj, py_name(name), &tmp); } +void py_bind(py_Ref obj, const char* sig, py_CFunction f) { + py_TValue tmp; + py_newfunction(&tmp, f, sig); + Function* ud = py_touserdata(&tmp); + py_Name name = py_name(ud->decl->code.name->data); + py_setdict(obj, name, &tmp); +} + void py_newslice(py_Ref out, const py_Ref start, const py_Ref stop, const py_Ref step) { py_newobject(out, tp_slice, 3, 0); py_setslot(out, 0, start); diff --git a/src/public/vm.c b/src/public/vm.c index d625f574..872d5c82 100644 --- a/src/public/vm.c +++ b/src/public/vm.c @@ -219,7 +219,7 @@ bool py_callmethod(py_Ref self, py_Name name, int argc, py_Ref argv) { return -1 bool py_vectorcall(uint16_t argc, uint16_t kwargc) { pk_VM* vm = pk_current_vm; - return pk_VM__vectorcall(vm, argc, kwargc, false) == RES_ERROR; + return pk_VM__vectorcall(vm, argc, kwargc, false) != RES_ERROR; } py_Ref py_retval() { return &pk_current_vm->last_retval; } diff --git a/tests/05_list.py b/tests/05_list.py index ab71605b..cd830686 100644 --- a/tests/05_list.py +++ b/tests/05_list.py @@ -98,23 +98,51 @@ a = [0, 0, 0, 0, 1, 1, 3, -1] assert a.sort() == None assert a == [-1, 0, 0, 0, 0, 1, 1, 3] +a = [3, 2, 2, 1] +a.sort() +assert a == [1, 2, 2, 3] + +# test reverse +a = [3, 2, 2, 1] +a.sort(reverse=True) +assert a == [3, 2, 2, 1] + +a = [1, 3, 2, 2] +a.sort(reverse=True) +assert a == [3, 2, 2, 1] + +# test key +key = lambda x: -x +assert key(1) == -1 +a = [1, 3, 2, 2] +a.sort(key=key) +assert a == [3, 2, 2, 1] + +a = [1, 3, 2, 2] +a.sort(key=key, reverse=True) +assert a == [1, 2, 2, 3] + # test sorted +a = [8, 2, 4, 2, 9] assert sorted(a) == [2, 2, 4, 8, 9] assert sorted(a, reverse=True) == [9, 8, 4, 2, 2] -assert sorted(a, key=lambda x:-x, reverse=True) == [2, 2, 4, 8, 9] +def key(x): return -x; +assert sorted(a, key=key) == [9, 8, 4, 2, 2] + +assert sorted(a, key=key, reverse=True) == [2, 2, 4, 8, 9] assert a == [8, 2, 4, 2, 9] -b = [(1, 2), (3, 3), (5, 1)] -b.sort(key=lambda x:x[1]) -assert b == [(5, 1), (1, 2), (3,3)] +# b = [(1, 2), (3, 3), (5, 1)] +# b.sort(key=lambda x:x[1]) +# assert b == [(5, 1), (1, 2), (3,3)] # test cyclic reference -a = [] -a.append(0) -a.append([1, 2, a]) +# a = [] +# a.append(0) +# a.append([1, 2, a]) -assert repr(a) == "[0, [1, 2, [...]]]" +# assert repr(a) == "[0, [1, 2, [...]]]" # try: # a.index(1, 1)