array2d.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. #include "pocketpy/array2d.h"
  2. namespace pkpy{
  3. struct Array2d{
  4. PK_ALWAYS_PASS_BY_POINTER(Array2d)
  5. PyObject** data;
  6. int n_cols;
  7. int n_rows;
  8. int numel;
  9. Array2d(){
  10. data = nullptr;
  11. n_cols = 0;
  12. n_rows = 0;
  13. numel = 0;
  14. }
  15. void init(int n_cols, int n_rows){
  16. this->n_cols = n_cols;
  17. this->n_rows = n_rows;
  18. this->numel = n_cols * n_rows;
  19. this->data = new PyObject*[numel];
  20. }
  21. bool is_valid(int col, int row) const{
  22. return 0 <= col && col < n_cols && 0 <= row && row < n_rows;
  23. }
  24. PyObject* _get(int col, int row){
  25. return data[row * n_cols + col];
  26. }
  27. void _set(int col, int row, PyObject* value){
  28. data[row * n_cols + col] = value;
  29. }
  30. static void _register(VM* vm, PyObject* mod, PyObject* type){
  31. vm->bind(type, "__new__(cls, *args, **kwargs)", [](VM* vm, ArgsView args){
  32. Type cls = PK_OBJ_GET(Type, args[0]);
  33. return vm->heap.gcnew<Array2d>(cls);
  34. });
  35. vm->bind(type, "__init__(self, n_cols: int, n_rows: int, default=None)", [](VM* vm, ArgsView args){
  36. Array2d& self = PK_OBJ_GET(Array2d, args[0]);
  37. int n_cols = CAST(int, args[1]);
  38. int n_rows = CAST(int, args[2]);
  39. if(n_cols <= 0 || n_rows <= 0){
  40. vm->ValueError("n_cols and n_rows must be positive integers");
  41. }
  42. self.init(n_cols, n_rows);
  43. if(vm->py_callable(args[3])){
  44. for(int i = 0; i < self.numel; i++) self.data[i] = vm->call(args[3]);
  45. }else{
  46. for(int i = 0; i < self.numel; i++) self.data[i] = args[3];
  47. }
  48. return vm->None;
  49. });
  50. PY_READONLY_FIELD(Array2d, "n_cols", n_cols);
  51. PY_READONLY_FIELD(Array2d, "n_rows", n_rows);
  52. PY_READONLY_FIELD(Array2d, "width", n_cols);
  53. PY_READONLY_FIELD(Array2d, "height", n_rows);
  54. PY_READONLY_FIELD(Array2d, "numel", numel);
  55. vm->bind(type, "is_valid(self, col: int, row: int)", [](VM* vm, ArgsView args){
  56. Array2d& self = PK_OBJ_GET(Array2d, args[0]);
  57. int col = CAST(int, args[1]);
  58. int row = CAST(int, args[2]);
  59. return VAR(self.is_valid(col, row));
  60. });
  61. vm->bind(type, "get(self, col: int, row: int, default=None)", [](VM* vm, ArgsView args){
  62. Array2d& self = PK_OBJ_GET(Array2d, args[0]);
  63. int col = CAST(int, args[1]);
  64. int row = CAST(int, args[2]);
  65. if(!self.is_valid(col, row)) return args[3];
  66. return self._get(col, row);
  67. });
  68. #define HANDLE_SLICE() \
  69. int start_col, stop_col, step_col; \
  70. int start_row, stop_row, step_row; \
  71. vm->parse_int_slice(PK_OBJ_GET(Slice, xy[0]), self.n_cols, start_col, stop_col, step_col); \
  72. vm->parse_int_slice(PK_OBJ_GET(Slice, xy[1]), self.n_rows, start_row, stop_row, step_row); \
  73. if(step_col != 1 || step_row != 1) vm->ValueError("slice step must be 1"); \
  74. int slice_width = stop_col - start_col; \
  75. int slice_height = stop_row - start_row; \
  76. if(slice_width <= 0 || slice_height <= 0) vm->ValueError("slice width and height must be positive");
  77. vm->bind__getitem__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* _0, PyObject* _1){
  78. Array2d& self = PK_OBJ_GET(Array2d, _0);
  79. const Tuple& xy = CAST(Tuple&, _1);
  80. i64 col, row;
  81. if(try_cast_int(xy[0], &col) && try_cast_int(xy[1], &row)){
  82. if(!self.is_valid(col, row)){
  83. vm->IndexError(_S('(', col, ", ", row, ')', " is not a valid index for array2d(", self.n_cols, ", ", self.n_rows, ')'));
  84. }
  85. return self._get(col, row);
  86. }
  87. if(is_type(xy[0], VM::tp_slice) && is_type(xy[1], VM::tp_slice)){
  88. HANDLE_SLICE();
  89. PyObject* new_array_obj = vm->new_user_object<Array2d>();
  90. Array2d& new_array = PK_OBJ_GET(Array2d, new_array_obj);
  91. new_array.init(stop_col - start_col, stop_row - start_row);
  92. for(int j = start_row; j < stop_row; j++){
  93. for(int i = start_col; i < stop_col; i++){
  94. new_array._set(i - start_col, j - start_row, self._get(i, j));
  95. }
  96. }
  97. return new_array_obj;
  98. }
  99. vm->TypeError("expected `tuple[int, int]` or `tuple[slice, slice]` as index");
  100. PK_UNREACHABLE();
  101. });
  102. vm->bind__setitem__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* _0, PyObject* _1, PyObject* _2){
  103. Array2d& self = PK_OBJ_GET(Array2d, _0);
  104. const Tuple& xy = CAST(Tuple&, _1);
  105. i64 col, row;
  106. if(try_cast_int(xy[0], &col) && try_cast_int(xy[1], &row)){
  107. if(!self.is_valid(col, row)){
  108. vm->IndexError(_S('(', col, ", ", row, ')', " is not a valid index for array2d(", self.n_cols, ", ", self.n_rows, ')'));
  109. }
  110. self._set(col, row, _2);
  111. return;
  112. }
  113. if(is_type(xy[0], VM::tp_slice) && is_type(xy[1], VM::tp_slice)){
  114. HANDLE_SLICE();
  115. bool is_basic_type = false;
  116. switch(vm->_tp(_2).index){
  117. case VM::tp_int.index: is_basic_type = true; break;
  118. case VM::tp_float.index: is_basic_type = true; break;
  119. case VM::tp_str.index: is_basic_type = true; break;
  120. case VM::tp_bool.index: is_basic_type = true; break;
  121. default: is_basic_type = _2 == vm->None;
  122. }
  123. if(is_basic_type){
  124. for(int j = 0; j < slice_height; j++)
  125. for(int i = 0; i < slice_width; i++)
  126. self._set(i + start_col, j + start_row, _2);
  127. return;
  128. }
  129. if(!vm->is_user_type<Array2d>(_2)){
  130. vm->TypeError(_S("expected int/float/str/bool/None or an array2d instance"));
  131. }
  132. Array2d& other = PK_OBJ_GET(Array2d, _2);
  133. if(slice_width != other.n_cols || slice_height != other.n_rows){
  134. vm->ValueError("array2d size does not match the slice size");
  135. }
  136. for(int j = 0; j < slice_height; j++)
  137. for(int i = 0; i < slice_width; i++)
  138. self._set(i + start_col, j + start_row, other._get(i, j));
  139. return;
  140. }
  141. vm->TypeError("expected `tuple[int, int]` or `tuple[slice, slice]` as index");
  142. });
  143. #undef HANDLE_SLICE
  144. vm->bind(type, "tolist(self)", [](VM* vm, ArgsView args){
  145. Array2d& self = PK_OBJ_GET(Array2d, args[0]);
  146. List t(self.n_rows);
  147. for(int j = 0; j < self.n_rows; j++){
  148. List row(self.n_cols);
  149. for(int i = 0; i < self.n_cols; i++) row[i] = self._get(i, j);
  150. t[j] = VAR(std::move(row));
  151. }
  152. return VAR(std::move(t));
  153. });
  154. vm->bind__len__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* _0){
  155. Array2d& self = PK_OBJ_GET(Array2d, _0);
  156. return (i64)self.numel;
  157. });
  158. vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* _0){
  159. Array2d& self = PK_OBJ_GET(Array2d, _0);
  160. return VAR(_S("array2d(", self.n_cols, ", ", self.n_rows, ')'));
  161. });
  162. vm->bind(type, "map(self, f)", [](VM* vm, ArgsView args){
  163. Array2d& self = PK_OBJ_GET(Array2d, args[0]);
  164. PyObject* f = args[1];
  165. PyObject* new_array_obj = vm->new_user_object<Array2d>();
  166. Array2d& new_array = PK_OBJ_GET(Array2d, new_array_obj);
  167. new_array.init(self.n_cols, self.n_rows);
  168. for(int i = 0; i < new_array.numel; i++){
  169. new_array.data[i] = vm->call(f, self.data[i]);
  170. }
  171. return new_array_obj;
  172. });
  173. vm->bind(type, "copy(self)", [](VM* vm, ArgsView args){
  174. Array2d& self = PK_OBJ_GET(Array2d, args[0]);
  175. PyObject* new_array_obj = vm->new_user_object<Array2d>();
  176. Array2d& new_array = PK_OBJ_GET(Array2d, new_array_obj);
  177. new_array.init(self.n_cols, self.n_rows);
  178. for(int i = 0; i < new_array.numel; i++){
  179. new_array.data[i] = self.data[i];
  180. }
  181. return new_array_obj;
  182. });
  183. vm->bind(type, "fill_(self, value)", [](VM* vm, ArgsView args){
  184. Array2d& self = PK_OBJ_GET(Array2d, args[0]);
  185. for(int i = 0; i < self.numel; i++){
  186. self.data[i] = args[1];
  187. }
  188. return vm->None;
  189. });
  190. vm->bind(type, "apply_(self, f)", [](VM* vm, ArgsView args){
  191. Array2d& self = PK_OBJ_GET(Array2d, args[0]);
  192. PyObject* f = args[1];
  193. for(int i = 0; i < self.numel; i++){
  194. self.data[i] = vm->call(f, self.data[i]);
  195. }
  196. return vm->None;
  197. });
  198. vm->bind(type, "copy_(self, other)", [](VM* vm, ArgsView args){
  199. Array2d& self = PK_OBJ_GET(Array2d, args[0]);
  200. if(is_type(args[1], VM::tp_list)){
  201. const List& list = PK_OBJ_GET(List, args[1]);
  202. if(list.size() != self.numel){
  203. vm->ValueError("list size must be equal to the number of elements in the array2d");
  204. }
  205. for(int i = 0; i < self.numel; i++){
  206. self.data[i] = list[i];
  207. }
  208. return vm->None;
  209. }
  210. Array2d& other = CAST(Array2d&, args[1]);
  211. // if self and other have different sizes, re-initialize self
  212. if(self.n_cols != other.n_cols || self.n_rows != other.n_rows){
  213. delete self.data;
  214. self.init(other.n_cols, other.n_rows);
  215. }
  216. for(int i = 0; i < self.numel; i++){
  217. self.data[i] = other.data[i];
  218. }
  219. return vm->None;
  220. });
  221. vm->bind__eq__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* _0, PyObject* _1){
  222. Array2d& self = PK_OBJ_GET(Array2d, _0);
  223. if(!vm->is_user_type<Array2d>(_1)) return vm->NotImplemented;
  224. Array2d& other = PK_OBJ_GET(Array2d, _1);
  225. if(self.n_cols != other.n_cols || self.n_rows != other.n_rows) return vm->False;
  226. for(int i = 0; i < self.numel; i++){
  227. if(vm->py_ne(self.data[i], other.data[i])) return vm->False;
  228. }
  229. return vm->True;
  230. });
  231. vm->bind(type, "count_neighbors(self, value, neighborhood='Moore') -> array2d[int]", [](VM* vm, ArgsView args){
  232. Array2d& self = PK_OBJ_GET(Array2d, args[0]);
  233. PyObject* new_array_obj = vm->new_user_object<Array2d>();
  234. Array2d& new_array = PK_OBJ_GET(Array2d, new_array_obj);
  235. new_array.init(self.n_cols, self.n_rows);
  236. PyObject* value = args[1];
  237. const Str& neighborhood = CAST(Str&, args[2]);
  238. if(neighborhood == "Moore"){
  239. for(int j = 0; j < new_array.n_rows; j++){
  240. for(int i = 0; i < new_array.n_cols; i++){
  241. int count = 0;
  242. count += self.is_valid(i-1, j-1) && vm->py_eq(self._get(i-1, j-1), value);
  243. count += self.is_valid(i, j-1) && vm->py_eq(self._get(i, j-1), value);
  244. count += self.is_valid(i+1, j-1) && vm->py_eq(self._get(i+1, j-1), value);
  245. count += self.is_valid(i-1, j) && vm->py_eq(self._get(i-1, j), value);
  246. count += self.is_valid(i+1, j) && vm->py_eq(self._get(i+1, j), value);
  247. count += self.is_valid(i-1, j+1) && vm->py_eq(self._get(i-1, j+1), value);
  248. count += self.is_valid(i, j+1) && vm->py_eq(self._get(i, j+1), value);
  249. count += self.is_valid(i+1, j+1) && vm->py_eq(self._get(i+1, j+1), value);
  250. new_array._set(i, j, VAR(count));
  251. }
  252. }
  253. }else if(neighborhood == "von Neumann"){
  254. for(int j = 0; j < new_array.n_rows; j++){
  255. for(int i = 0; i < new_array.n_cols; i++){
  256. int count = 0;
  257. count += self.is_valid(i, j-1) && vm->py_eq(self._get(i, j-1), value);
  258. count += self.is_valid(i-1, j) && vm->py_eq(self._get(i-1, j), value);
  259. count += self.is_valid(i+1, j) && vm->py_eq(self._get(i+1, j), value);
  260. count += self.is_valid(i, j+1) && vm->py_eq(self._get(i, j+1), value);
  261. new_array._set(i, j, VAR(count));
  262. }
  263. }
  264. }else{
  265. vm->ValueError("neighborhood must be 'Moore' or 'von Neumann'");
  266. }
  267. return new_array_obj;
  268. });
  269. vm->bind(type, "count(self, value) -> int", [](VM* vm, ArgsView args){
  270. Array2d& self = PK_OBJ_GET(Array2d, args[0]);
  271. PyObject* value = args[1];
  272. int count = 0;
  273. for(int i = 0; i < self.numel; i++) count += vm->py_eq(self.data[i], value);
  274. return VAR(count);
  275. });
  276. vm->bind(type, "find_bounding_rect(self, value)", [](VM* vm, ArgsView args){
  277. Array2d& self = PK_OBJ_GET(Array2d, args[0]);
  278. PyObject* value = args[1];
  279. int left = self.n_cols;
  280. int top = self.n_rows;
  281. int right = 0;
  282. int bottom = 0;
  283. for(int j = 0; j < self.n_rows; j++){
  284. for(int i = 0; i < self.n_cols; i++){
  285. if(vm->py_eq(self._get(i, j), value)){
  286. left = std::min(left, i);
  287. top = std::min(top, j);
  288. right = std::max(right, i);
  289. bottom = std::max(bottom, j);
  290. }
  291. }
  292. }
  293. int width = right - left + 1;
  294. int height = bottom - top + 1;
  295. if(width <= 0 || height <= 0) return vm->None;
  296. return VAR(Tuple(VAR(left), VAR(top), VAR(width), VAR(height)));
  297. });
  298. }
  299. void _gc_mark() const{
  300. for(int i = 0; i < numel; i++) PK_OBJ_MARK(data[i]);
  301. }
  302. ~Array2d(){
  303. delete[] data;
  304. }
  305. };
  306. struct Array2dIter{
  307. PK_ALWAYS_PASS_BY_POINTER(Array2dIter)
  308. PyObject* ref;
  309. int i;
  310. Array2dIter(PyObject* ref) : ref(ref), i(0) {}
  311. void _gc_mark() const{ PK_OBJ_MARK(ref); }
  312. static void _register(VM* vm, PyObject* mod, PyObject* type){
  313. vm->_all_types[PK_OBJ_GET(Type, type)].subclass_enabled = false;
  314. vm->bind_notimplemented_constructor<Array2dIter>(type);
  315. vm->bind__iter__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* _0) { return _0; });
  316. vm->bind__next__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* _0) -> unsigned{
  317. Array2dIter& self = PK_OBJ_GET(Array2dIter, _0);
  318. Array2d& a = PK_OBJ_GET(Array2d, self.ref);
  319. if(self.i == a.numel) return 0;
  320. std::div_t res = std::div(self.i, a.n_cols);
  321. vm->s_data.push(VAR(res.rem));
  322. vm->s_data.push(VAR(res.quot));
  323. vm->s_data.push(a.data[self.i++]);
  324. return 3;
  325. });
  326. }
  327. };
  328. void add_module_array2d(VM* vm){
  329. PyObject* mod = vm->new_module("array2d");
  330. vm->register_user_class<Array2d>(mod, "array2d", true);
  331. vm->register_user_class<Array2dIter>(mod, "_array2d_iter");
  332. vm->bind__iter__(vm->_tp_user<Array2d>(), [](VM* vm, PyObject* _0){
  333. return vm->new_user_object<Array2dIter>(_0);
  334. });
  335. }
  336. } // namespace pkpy