소스 검색

First usable IFS implementation

Implements borrowed if and other needed stuff + tests & benchamrk
Yann Weber 1 년 전
부모
커밋
7c335fcff0
13개의 변경된 파일724개의 추가작업 그리고 132개의 파일을 삭제
  1. 7
    3
      Makefile
  2. 32
    0
      benchplot.sh
  3. 205
    85
      python_if.c
  4. 21
    0
      python_if.h
  5. 162
    4
      python_ifs.c
  6. 9
    0
      python_ifs.h
  7. 5
    0
      rpn_if.c
  8. 2
    0
      rpn_if.h
  9. 10
    2
      rpn_ifs.c
  10. 2
    1
      rpn_ifs.h
  11. 75
    35
      tests/benchmark.py
  12. 2
    2
      tests/test_rpn.c
  13. 192
    0
      tests/tests_rpn_ifs.py

+ 7
- 3
Makefile 파일 보기

@@ -9,7 +9,7 @@ ifeq ($(DEBUG), 1)
9 9
 	#PYTHON=python3dm
10 10
 	PYTHON=python3-dbg
11 11
 else
12
-	CFLAGS=-fPIC -Wall -Werror
12
+	CFLAGS=-fPIC -Wall -Werror -DNDEBUG
13 13
 	LDFLAGS=-s
14 14
 	NASMCFLAGS=-f elf64
15 15
 	PYTHON=python3
@@ -75,8 +75,12 @@ doc/.doxygen.stamp: $(wildcard *.c) $(wildcard *.h) Doxyfile
75 75
 checks: runtest unittest
76 76
 
77 77
 benchmark: $(LIB)
78
-	PYTHONPATH=`pwd` $(PYTHON) tests/benchmark.py 0x500 0x2000;  \
79
-		PYTHONPATH=`pwd` $(PYTHON) tests/benchmark.py 0x500 0x4000;
78
+	PYTHONPATH=`pwd` $(PYTHON) tests/benchmark.py -c 0x500 -i 0x2000 -s 0x50;  \
79
+	PYTHONPATH=`pwd` $(PYTHON) tests/benchmark.py -c 0x500 -i 0x2000 -s 0xa0;  \
80
+	PYTHONPATH=`pwd` $(PYTHON) tests/benchmark.py -c 0x500 -i 0x2000 -s 0x100;  \
81
+	PYTHONPATH=`pwd` $(PYTHON) tests/benchmark.py -c 0x500 -i 0x5000 -s 0x50; \
82
+	PYTHONPATH=`pwd` $(PYTHON) tests/benchmark.py -c 0x500 -i 0x5000 -s 0xa0; \
83
+	PYTHONPATH=`pwd` $(PYTHON) tests/benchmark.py -c 0x500 -i 0x5000 -s 0x100; \
80 84
 		   
81 85
 
82 86
 unittest: $(LIB_COV)

+ 32
- 0
benchplot.sh 파일 보기

@@ -0,0 +1,32 @@
1
+#!/bin/sh
2
+
3
+gpdat=/tmp/bench.dat
4
+
5
+e=500
6
+
7
+min_i=10000
8
+max_i=100000
9
+it_i=1
10
+
11
+min_s=10
12
+max_s=10000
13
+it_s=30
14
+
15
+if [ $it_i -eq 1 ]
16
+then
17
+	inc_i=$max_i
18
+else
19
+	inc_i=$(expr $(expr $max_i - $min_i) / $(expr $it_i - 1))
20
+fi
21
+inc_s=$(expr $(expr $max_s - $min_s) / $(expr $it_s - 1))
22
+
23
+
24
+for i in $(seq $min_i $inc_i $max_i)
25
+do
26
+	for s in $(seq $min_s $inc_s $max_s)
27
+	do
28
+		echo "GO $e $i $s"
29
+		PYTHONPATH=./ python3 tests/benchmark.py -c $e -i $i -s $s -G $gpdat
30
+	done
31
+	echo "" >> $gpdat
32
+done

+ 205
- 85
python_if.c 파일 보기

@@ -190,6 +190,8 @@ PyObject* rpnif_new(PyTypeObject *subtype, PyObject *args, PyObject* kwds)
190 190
 	}
191 191
 	expr = (PyRPNIterExpr_t*)ret;
192 192
 	expr->rif = NULL;
193
+	expr->rif_params = NULL;
194
+	expr->borrowed_if = 0;
193 195
 
194 196
 	return ret;
195 197
 }
@@ -219,6 +221,7 @@ int rpnif_init(PyObject *self, PyObject *args, PyObject *kwds)
219 221
 
220 222
 	rpn_if_param_t *rif_params = _rpnif_get_params(pos_flag, res_flag,
221 223
 			lim_obj, const_values_obj, stack_size);
224
+	expr_self->rif_params = rif_params;
222 225
 
223 226
 	if(!rif_params)
224 227
 	{
@@ -228,6 +231,11 @@ int rpnif_init(PyObject *self, PyObject *args, PyObject *kwds)
228 231
 	expr_self->ndim = ((rpn_if_default_data_t*)(rif_params->data))->ndim;
229 232
 
230 233
 	const Py_ssize_t expt_sz = rif_params->mem_sz * rif_params->value_sz;
234
+	if(mmap_obj == Py_None)
235
+	{
236
+		Py_DECREF(mmap_obj);
237
+		mmap_obj = NULL;
238
+	}
231 239
 	if(!mmap_obj)
232 240
 	{
233 241
 		PyObject *fileno = PyLong_FromLong(-1);
@@ -282,6 +290,102 @@ of mmap.mmap");
282 290
 	return _rpnif_init_expr_tuple(expr_self);
283 291
 }
284 292
 
293
+
294
+PyObject* rpnif_init_borrowed(rpn_if_t *borrowed_if, PyObject *mmap_obj)
295
+{
296
+	PyObject *sz_lim, *const_val;
297
+	PyObject *args, *ret;
298
+
299
+
300
+	rpn_if_default_data_t *params;
301
+	params = (rpn_if_default_data_t*)(borrowed_if->params->data);
302
+
303
+	const size_t nsz = params->ndim + (params->pos_flag == RPN_IF_POSITION_XDIM?1:0);
304
+	sz_lim = PyTuple_New(nsz);
305
+	for(size_t i=0; i<nsz; i++)
306
+	{
307
+		PyObject *v = PyLong_FromLong(params->size_lim[i]);
308
+		PyTuple_SET_ITEM(sz_lim, i, v);
309
+	}
310
+
311
+	const size_t const_val_sz = params->res_flag == RPN_IF_RES_CONST ? 1 : \
312
+			     (params->res_flag == RPN_IF_RES_CONST_RGBA ? 4 : 0);
313
+	
314
+	if(const_val_sz)
315
+	{
316
+		const_val = PyTuple_New(const_val_sz);
317
+		for(size_t i=0;  i<const_val_sz; i++)
318
+		{
319
+			PyObject *v = PyLong_FromLong(params->const_val[i]);
320
+			PyTuple_SET_ITEM(const_val, i, v);
321
+		}
322
+	}
323
+	else
324
+	{
325
+		const_val = Py_None;
326
+		Py_INCREF(const_val);
327
+	}
328
+
329
+	// no mmap :/
330
+	// It seems there is no elegant solutions for the moment
331
+	// if we give None a new mmap is created
332
+	// if we give a dummy mmap it has to have the expected size...
333
+	// using None for the moment...
334
+	/**@todo find a solution */
335
+	PyObject *mmap_arg = Py_None;
336
+	if(mmap_obj)
337
+	{
338
+		mmap_arg = mmap_obj;
339
+	}
340
+
341
+	args = Py_BuildValue("hhOOBO", params->pos_flag, params->res_flag,
342
+			sz_lim, const_val,
343
+			borrowed_if->params->rpn_stack_sz,
344
+			mmap_arg);
345
+	Py_DECREF(sz_lim);
346
+	Py_DECREF(const_val);
347
+	if(!args || PyErr_Occurred())
348
+	{
349
+		return NULL;
350
+	}
351
+
352
+	ret = PyObject_CallObject((PyObject*)&RPNIterExprType, args);
353
+	Py_DECREF(args);
354
+	if(!ret || PyErr_Occurred())
355
+	{
356
+		return NULL;
357
+	}
358
+
359
+	PyRPNIterExpr_t* instance = (PyRPNIterExpr_t*)ret;
360
+	
361
+	// changing if instance
362
+	rpn_if_free(instance->rif);
363
+	instance->rif = borrowed_if;
364
+	// reseting expr tuple
365
+	Py_DECREF(instance->expr);
366
+	instance->expr = NULL;
367
+
368
+	// mmap update
369
+	PyBuffer_Release(&instance->mm_buff);
370
+	if(!mmap_obj)
371
+	{
372
+		Py_DECREF(instance->mmap);
373
+		instance->mmap = NULL;
374
+		instance->mm_buff.buf = NULL;
375
+		instance->_mmap = borrowed_if->mem;
376
+	}
377
+	instance->borrowed_if = 1;
378
+	// someone else will take care to free the rif params
379
+	if(instance->rif_params) { free(instance->rif_params); }
380
+	instance->rif_params = NULL;
381
+
382
+	if(_rpnif_init_expr_tuple(instance) < 0)
383
+	{
384
+		return NULL;
385
+	}
386
+	return ret;
387
+}
388
+
285 389
 /**@brief Returns the parameters in a named tuple */
286 390
 PyObject *rpnif_get_params(PyObject *self)
287 391
 {
@@ -414,47 +518,7 @@ PyObject *rpnif_to_pos(PyObject *self, PyObject** argv, Py_ssize_t argc)
414 518
 {
415 519
 	PyRPNIterExpr_t *expr_self = (PyRPNIterExpr_t*)self;
416 520
 	rpn_if_default_data_t *rif_data = (rpn_if_default_data_t*)expr_self->rif->params->data;
417
-	const short idx_off = rif_data->pos_flag == RPN_IF_POSITION_XDIM?1:0;
418
-	long long value;
419
-	
420
-	if((size_t)argc != rif_data->ndim)
421
-	{
422
-		PyErr_Format(PyExc_IndexError,
423
-				"Expression expect %lu dimentions coordinates,"
424
-				" but %ld arguments given.",
425
-				rif_data->ndim, argc);
426
-		return NULL;
427
-	}
428
-
429
-	rpn_value_t result=0;
430
-	size_t cur_dim_sz = 1;
431
-
432
-	for(size_t i=0; i<rif_data->ndim; i++)
433
-	{
434
-		if(!PyLong_Check(argv[i]))
435
-		{
436
-			PyErr_SetString(PyExc_TypeError,
437
-					"coordinates must be integers");
438
-			return NULL;
439
-		}
440
-		value = PyLong_AsLongLong(argv[i]);
441
-		if(value < 0)
442
-		{
443
-			value = (-(rpn_value_t)-value)%rif_data->size_lim[i+idx_off];
444
-		}
445
-		if((size_t)value >= rif_data->size_lim[i+idx_off])
446
-		{
447
-			PyErr_Format(PyExc_IndexError,
448
-					"Coordinate %ld overflows : %R/%lu",
449
-					i, argv[i],
450
-					rif_data->size_lim[i+idx_off]);
451
-			return NULL;
452
-		}
453
-		result += value * cur_dim_sz;
454
-		cur_dim_sz *= rif_data->size_lim[i+idx_off];
455
-	}
456
-
457
-	return PyLong_FromUnsignedLongLong(result);
521
+	return _rpnif_to_pos(rif_data, argv, argc);
458 522
 }
459 523
 
460 524
 
@@ -462,48 +526,8 @@ PyObject *rpnif_from_pos(PyObject *self, PyObject* _pos)
462 526
 {
463 527
 	PyRPNIterExpr_t *expr_self = (PyRPNIterExpr_t*)self;
464 528
 	rpn_if_default_data_t *rif_data = (rpn_if_default_data_t*)expr_self->rif->params->data;
465
-	const short idx_off = rif_data->pos_flag == RPN_IF_POSITION_XDIM?1:0;
466
-
467
-	if(!PyLong_Check(_pos))
468
-	{
469
-		PyErr_SetString(PyExc_TypeError,
470
-				"Expected position to be an integer");
471
-		return NULL;
472
-	}
473
-
474
-	size_t pos = PyLong_AsUnsignedLong(_pos);
475
-	if(PyErr_Occurred())
476
-	{
477
-		return NULL;
478
-	}
479
-
480
-	PyObject *res = PyTuple_New(rif_data->ndim);
481
-	if(PyErr_Occurred())
482
-	{
483
-		return NULL;
484
-	}
485
-	
486
-	for(size_t i=0; i<rif_data->ndim; i++)
487
-	{
488
-		size_t val;
489
-		size_t lim = rif_data->size_lim[i+idx_off];
490
-
491
-		val = pos % lim;
492
-		pos /= lim;
493
-
494
-		PyObject *elt = PyLong_FromUnsignedLong(val);
495
-		if(PyErr_Occurred())
496
-		{
497
-			goto err;
498
-		}
499
-		PyTuple_SET_ITEM(res, i,  elt);
500
-	}
501
-
502
-	return res;
503 529
 
504
-err:
505
-	Py_DECREF(res);
506
-	return NULL;
530
+	return _rpnif_from_pos(rif_data, _pos);
507 531
 }
508 532
 
509 533
 
@@ -637,7 +661,7 @@ err_loop_newtuple:
637 661
 void rpnif_del(PyObject *self)
638 662
 {
639 663
 	PyRPNIterExpr_t *expr_self = (PyRPNIterExpr_t*)self;
640
-	if(expr_self->rif)
664
+	if((!expr_self->borrowed_if) && expr_self->rif)
641 665
 	{
642 666
 		rpn_if_free(expr_self->rif);
643 667
 		expr_self->rif = NULL;
@@ -657,6 +681,7 @@ void rpnif_del(PyObject *self)
657 681
 		Py_DECREF(expr_self->expr);
658 682
 		expr_self->expr = NULL;
659 683
 	}
684
+	if(expr_self->rif_params) { free(expr_self->rif_params); }
660 685
 }
661 686
 
662 687
 
@@ -836,7 +861,7 @@ PyObject* rpnif_subscript(PyObject *self, PyObject *key)
836 861
 	if(idx < 0)
837 862
 	{
838 863
 		PyErr_Format(PyExc_IndexError,
839
-				"No expression '%s' with given parameters",
864
+				"No expression '%R' with given parameters",
840 865
 				key);
841 866
 		return NULL;
842 867
 	}
@@ -851,7 +876,7 @@ int rpnif_ass_subscript(PyObject *self, PyObject *key, PyObject *elt)
851 876
 	if(idx < 0)
852 877
 	{
853 878
 		PyErr_Format(PyExc_IndexError,
854
-				"Cannot set expression '%U' that do not exists with this parameters",
879
+				"Cannot set expression '%R' that do not exists with this parameters",
855 880
 				key);
856 881
 		return -1;
857 882
 	}
@@ -1319,11 +1344,13 @@ PyObject* rpnif_setstate(PyObject *self, PyObject *state_bytes)
1319 1344
 				"Unable to initialize rif expression");
1320 1345
 		return NULL;
1321 1346
 	}
1347
+	expr_self->rif_params = params;
1322 1348
 	expr_self->ndim = ser_data->ndim;
1323 1349
 	if(_rpnif_init_expr_tuple(expr_self) < 0)
1324 1350
 	{
1325 1351
 		return NULL;
1326 1352
 	}
1353
+	expr_self->borrowed_if = 0; // explicitly not borrowed
1327 1354
 
1328 1355
 	Py_RETURN_NONE;
1329 1356
 }
@@ -1556,6 +1583,99 @@ PyObject *_rpnif_params_to_tuple(const rpn_if_param_t *_params)
1556 1583
 }
1557 1584
 
1558 1585
 
1586
+PyObject *_rpnif_to_pos(rpn_if_default_data_t *rif_data, PyObject** argv, Py_ssize_t argc)
1587
+{
1588
+	const short idx_off = rif_data->pos_flag == RPN_IF_POSITION_XDIM?1:0;
1589
+	long long value;
1590
+	
1591
+	if((size_t)argc != rif_data->ndim)
1592
+	{
1593
+		PyErr_Format(PyExc_IndexError,
1594
+				"Expression expect %lu dimentions coordinates,"
1595
+				" but %ld arguments given.",
1596
+				rif_data->ndim, argc);
1597
+		return NULL;
1598
+	}
1599
+
1600
+	rpn_value_t result=0;
1601
+	size_t cur_dim_sz = 1;
1602
+
1603
+	for(size_t i=0; i<rif_data->ndim; i++)
1604
+	{
1605
+		if(!PyLong_Check(argv[i]))
1606
+		{
1607
+			PyErr_SetString(PyExc_TypeError,
1608
+					"coordinates must be integers");
1609
+			return NULL;
1610
+		}
1611
+		value = PyLong_AsLongLong(argv[i]);
1612
+		if(value < 0)
1613
+		{
1614
+			value = (-(rpn_value_t)-value)%rif_data->size_lim[i+idx_off];
1615
+		}
1616
+		if((size_t)value >= rif_data->size_lim[i+idx_off])
1617
+		{
1618
+			PyErr_Format(PyExc_IndexError,
1619
+					"Coordinate %ld overflows : %R/%lu",
1620
+					i, argv[i],
1621
+					rif_data->size_lim[i+idx_off]);
1622
+			return NULL;
1623
+		}
1624
+		result += value * cur_dim_sz;
1625
+		cur_dim_sz *= rif_data->size_lim[i+idx_off];
1626
+	}
1627
+
1628
+	return PyLong_FromUnsignedLongLong(result);
1629
+}
1630
+
1631
+
1632
+PyObject *_rpnif_from_pos(rpn_if_default_data_t *rif_data, PyObject* _pos)
1633
+{
1634
+	const short idx_off = rif_data->pos_flag == RPN_IF_POSITION_XDIM?1:0;
1635
+
1636
+	if(!PyLong_Check(_pos))
1637
+	{
1638
+		PyErr_SetString(PyExc_TypeError,
1639
+				"Expected position to be an integer");
1640
+		return NULL;
1641
+	}
1642
+
1643
+	size_t pos = PyLong_AsUnsignedLong(_pos);
1644
+	if(PyErr_Occurred())
1645
+	{
1646
+		return NULL;
1647
+	}
1648
+
1649
+	PyObject *res = PyTuple_New(rif_data->ndim);
1650
+	if(PyErr_Occurred())
1651
+	{
1652
+		return NULL;
1653
+	}
1654
+	
1655
+	for(size_t i=0; i<rif_data->ndim; i++)
1656
+	{
1657
+		size_t val;
1658
+		size_t lim = rif_data->size_lim[i+idx_off];
1659
+
1660
+		val = pos % lim;
1661
+		pos /= lim;
1662
+
1663
+		PyObject *elt = PyLong_FromUnsignedLong(val);
1664
+		if(PyErr_Occurred())
1665
+		{
1666
+			goto err;
1667
+		}
1668
+		PyTuple_SET_ITEM(res, i,  elt);
1669
+	}
1670
+
1671
+	return res;
1672
+
1673
+err:
1674
+	Py_DECREF(res);
1675
+	return NULL;
1676
+}
1677
+
1678
+
1559 1679
 int _rpnif_init_expr_tuple(PyRPNIterExpr_t *expr_self)
1560 1680
 {
1561 1681
 	expr_self->expr = PyTuple_New(expr_self->rif->params->rpn_sz);

+ 21
- 0
python_if.h 파일 보기

@@ -101,6 +101,13 @@ typedef struct
101 101
 	/**@brief Memory map buffer pointer */
102 102
 	void *_mmap;
103 103
 
104
+	/**@brief Pointer on params to free at del */
105
+	rpn_if_param_t *rif_params;
106
+
107
+	/**@brief If true, someone else (an ifs ?) will take care of freeing
108
+	 * the if at deletion */
109
+	short borrowed_if;
110
+
104 111
 } PyRPNIterExpr_t;
105 112
 
106 113
 /**@brief RPNIterExpr.params static method
@@ -128,6 +135,13 @@ PyObject* rpnif_new(PyTypeObject *subtype, PyObject* args, PyObject* kwds);
128 135
  */
129 136
 int rpnif_init(PyObject *self, PyObject *args, PyObject *kwds);
130 137
 
138
+/**@brief RPNIterExpr constructor with a borrowed expression
139
+ * @param borrowed_if Pointer on borrowed instance
140
+ * @param mmap Pointer on borrowed python mmap object (NULL if deserialized instance)
141
+ * @return 0 if no error else -1
142
+ */
143
+PyObject* rpnif_init_borrowed(rpn_if_t *borrowed_if, PyObject *mmap);
144
+
131 145
 /**@brief RPNIterExpr __del__ method 
132 146
  * @param self RPNExpr instance
133 147
  * @ingroup pymod_pyrpn_RPNExprIter
@@ -311,6 +325,13 @@ rpn_if_param_t *_rpnif_get_params(unsigned short pos_flag, unsigned short res_fl
311 325
  */
312 326
 PyObject *_rpnif_params_to_tuple(const rpn_if_param_t *params);
313 327
 
328
+
329
+/**@brief Implementation of both RPNIterExpr.to_pos and RPNIFS.to_pos */
330
+PyObject *_rpnif_to_pos(rpn_if_default_data_t *rif_data, PyObject** argv, Py_ssize_t argc);
331
+
332
+/**@brief Implementation of bot RPNIterExpr.to_pos and RPNIFS.from_pos */
333
+PyObject *_rpnif_from_pos(rpn_if_default_data_t *rif_data, PyObject* _pos);
334
+
314 335
 int _rpnif_init_expr_tuple(PyRPNIterExpr_t *expr_self);
315 336
 
316 337
 #endif

+ 162
- 4
python_ifs.c 파일 보기

@@ -31,11 +31,28 @@ in the system."),
31 31
 			METH_FASTCALL,
32 32
 			"self, idx, weight=None",
33 33
 			"Get or set a single weight"),
34
+	PYRPN_method("position", rpnifs_position, 
35
+			METH_FASTCALL,
36
+			"self, position=None",
37
+			"Get or set the current position"),
38
+	PYRPN_method("run", rpnifs_run,
39
+			METH_FASTCALL,
40
+			"self, steps, rand_bytes=None, /",
41
+			"Run ifs steps times"),
42
+	PYRPN_method("to_pos", rpnifs_to_pos,
43
+			METH_FASTCALL,
44
+			"self, *args, /",
45
+			"Return a position (int) from a coordinates given as"
46
+			"argument."),
47
+	PYRPN_method("from_pos", rpnifs_from_pos,
48
+			METH_O,
49
+			"self, position, /",
50
+			"Return a coordinates tuple from given position."),
34 51
 	{NULL} // Sentinel
35 52
 };
36 53
 
37 54
 static PyMemberDef RPNIFS_members[] = {
38
-	{"expressions", T_OBJECT, offsetof(PyRPNIFS_t, rifs), READONLY,
55
+	{"expressions", T_OBJECT, offsetof(PyRPNIFS_t, rpn_if), READONLY,
39 56
 		"The tuple with RPNIterExpr instances"},
40 57
 	{"mmap", T_OBJECT, offsetof(PyRPNIFS_t, mmap), READONLY,
41 58
 		"The mmap storing data"},
@@ -172,7 +189,12 @@ length %ld provided",
172 189
 			"Error initializing ifs: %s", strerror(errno));
173 190
 		return -1;
174 191
 	}
175
-
192
+	
193
+	ifs_self->rpn_if = NULL;
194
+	if(_rpnifs_update_if_tuple(ifs_self) < 0)
195
+	{
196
+		return -1;
197
+	}
176 198
 	return 0;
177 199
 }
178 200
 
@@ -182,6 +204,7 @@ void rpnifs_del(PyObject *self)
182 204
 	PyRPNIFS_t *ifs_self = (PyRPNIFS_t*)self;
183 205
 
184 206
 	rpn_ifs_free(ifs_self->ifs);
207
+	free(ifs_self->ifs);
185 208
 	if(ifs_self->mmap)
186 209
 	{
187 210
 		if(ifs_self->mm_buff.buf)
@@ -195,6 +218,23 @@ void rpnifs_del(PyObject *self)
195 218
 }
196 219
 
197 220
 
221
+PyObject *rpnifs_to_pos(PyObject *self, PyObject** argv, Py_ssize_t argc)
222
+{
223
+	PyRPNIFS_t *ifs_self = (PyRPNIFS_t*)self;
224
+	rpn_if_default_data_t *rif_data = (rpn_if_default_data_t*)ifs_self->ifs->params.data;
225
+	return _rpnif_to_pos(rif_data, argv, argc);
226
+}
227
+
228
+
229
+PyObject *rpnifs_from_pos(PyObject *self, PyObject* _pos)
230
+{
231
+	PyRPNIFS_t *ifs_self = (PyRPNIFS_t*)self;
232
+	rpn_if_default_data_t *rif_data = (rpn_if_default_data_t*)ifs_self->ifs->params.data;
233
+	return _rpnif_from_pos(rif_data, _pos);
234
+}
235
+
236
+
237
+
198 238
 PyObject *rpnifs_weights(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
199 239
 {
200 240
 	PyRPNIFS_t *ifs_self = (PyRPNIFS_t*)self;
@@ -264,6 +304,11 @@ PyObject *rpnifs_weights(PyObject *self, PyObject *const *args, Py_ssize_t nargs
264 304
 		if(PyErr_Occurred()) { return NULL; }
265 305
 		PyTuple_SET_ITEM(ret, i, item);
266 306
 	}
307
+
308
+	if(_rpnifs_update_if_tuple(ifs_self) < 0)
309
+	{
310
+		return NULL;
311
+	}
267 312
 	return ret;
268 313
 
269 314
 err_weights:
@@ -280,6 +325,85 @@ PyObject *rpnifs_weight(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
280 325
 }
281 326
 
282 327
 
328
+PyObject *rpnifs_position(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
329
+{
330
+	PyRPNIFS_t *ifs_self = (PyRPNIFS_t*)self;
331
+
332
+	if(nargs == 1 && args[0] != Py_None)
333
+	{
334
+		size_t pos = PyLong_AsSize_t(args[0]);
335
+		if(PyErr_Occurred()) { return NULL; }
336
+		if(pos > ifs_self->ifs->params.mem_sz)
337
+		{
338
+			pos %= ifs_self->ifs->params.mem_sz;
339
+#ifndef NDEBUG
340
+			PyErr_WarnFormat(PyExc_RuntimeWarning, 1,
341
+					"Given position %R overflows and stored as %lu",
342
+					args[0], pos);
343
+#endif
344
+		}
345
+		ifs_self->ifs->pos = pos;
346
+	}
347
+
348
+	return PyLong_FromSize_t(ifs_self->ifs->pos);
349
+}
350
+
351
+PyObject *rpnifs_run(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
352
+{
353
+	PyRPNIFS_t *ifs_self = (PyRPNIFS_t*)self;
354
+	if(nargs == 0 || nargs > 2)
355
+	{
356
+		PyErr_Format(PyExc_ValueError,
357
+				"Expected 1 or 2 arguments but %ld given",
358
+				nargs);
359
+		return NULL;
360
+	}
361
+
362
+	size_t steps = PyLong_AsSize_t(args[0]);
363
+	if(PyErr_Occurred())
364
+	{
365
+		return NULL;
366
+	}
367
+	else if(steps == 0)
368
+	{
369
+		PyErr_Format(PyExc_ValueError,
370
+				"Steps arguments must be > 0");
371
+		return NULL;
372
+	}
373
+
374
+	unsigned char *rnd_buf = NULL;
375
+	if(nargs == 2)
376
+	{
377
+		// Python bytes given as random source
378
+		const Py_ssize_t sz = PyBytes_Size(args[1]);
379
+		if(PyErr_Occurred()) { goto err; }
380
+		if((size_t)sz != sizeof(char)*steps)
381
+		{
382
+			PyErr_Format(PyExc_ValueError,
383
+					"Given random bytes are too small."
384
+					"Expected %ld bytes but got %ld",
385
+					sizeof(char)*steps,
386
+					sz);
387
+			goto err;
388
+		}
389
+		rnd_buf = (unsigned char*)PyBytes_AsString(args[1]);
390
+	}
391
+
392
+	const int ret = rpn_ifs_run(ifs_self->ifs, steps, rnd_buf);
393
+	if(ret < 0)
394
+	{
395
+		PyErr_Format(PyExc_RuntimeError,
396
+				"Unable to run IFS : %s",
397
+				strerror(errno));
398
+		return NULL;
399
+	}
400
+	
401
+	Py_RETURN_NONE;
402
+
403
+err:
404
+	return NULL;
405
+}
406
+
283 407
 PyObject *rpnifs_str(PyObject *self)
284 408
 {
285 409
 	PyErr_SetString(PyExc_NotImplementedError, "TODO");
@@ -303,8 +427,23 @@ Py_ssize_t rpnifs_len(PyObject *self)
303 427
 
304 428
 PyObject *rpnifs_expr_item(PyObject *self, Py_ssize_t idx)
305 429
 {
306
-	PyErr_SetString(PyExc_NotImplementedError, "TODO");
307
-	return NULL;
430
+	PyRPNIFS_t *ifs_self = (PyRPNIFS_t*)self;
431
+	Py_ssize_t _idx = idx;
432
+
433
+	if(idx < 0)
434
+	{
435
+		_idx = ifs_self->ifs->if_sz - 1 + idx;
436
+	}
437
+	if(_idx < 0 || (size_t) _idx >= ifs_self->ifs->params.rpn_sz)
438
+	{
439
+		PyErr_Format(PyExc_IndexError,
440
+			"There is %ld expressions in the system but index %ld asked",
441
+			ifs_self->ifs->params.rpn_sz, idx);
442
+		return NULL;
443
+	}
444
+	PyObject *ret = PyTuple_GET_ITEM(ifs_self->rpn_if, _idx);
445
+	Py_INCREF(ret);
446
+	return ret;
308 447
 }
309 448
 
310 449
 
@@ -326,4 +465,23 @@ void rpnifs_releasebuffer(PyObject *self, Py_buffer *view)
326 465
 	PyErr_SetString(PyExc_NotImplementedError, "TODO");
327 466
 }
328 467
 
468
+int _rpnifs_update_if_tuple(PyRPNIFS_t *ifs_self)
469
+{
470
+	if(ifs_self->rpn_if)
471
+	{
472
+		Py_DECREF(ifs_self->rpn_if);
473
+	}
474
+	ifs_self->rpn_if = PyTuple_New(ifs_self->ifs->if_sz);
475
+	for(size_t i=0; i<ifs_self->ifs->if_sz; i++)
476
+	{
477
+		PyObject *py_if = rpnif_init_borrowed(ifs_self->ifs->rpn_if[i],
478
+				ifs_self->mmap);
479
+		if((!py_if) || PyErr_Occurred())
480
+		{
481
+			return -1;
482
+		}
483
+		PyTuple_SET_ITEM(ifs_self->rpn_if, i, py_if);
484
+	}
485
+	return 0;
486
+}
329 487
 

+ 9
- 0
python_ifs.h 파일 보기

@@ -62,6 +62,7 @@ typedef struct
62 62
 	/** @brief Pointer on IF expressions in the system */
63 63
 	rpn_if_t **rifs;
64 64
 
65
+
65 66
 	/**@brief Python mmap.mmap instance representing rif memory map 
66 67
 	 * @note NULL if unpickled instance */
67 68
 	PyObject *mmap;
@@ -80,8 +81,14 @@ PyObject *rpnifs_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds);
80 81
 int rpnifs_init(PyObject *self, PyObject *args, PyObject *kwds);
81 82
 void rpnifs_del(PyObject *self);
82 83
 
84
+
85
+PyObject *rpnifs_to_pos(PyObject *self, PyObject** argv, Py_ssize_t argc);
86
+PyObject *rpnifs_from_pos(PyObject *self, PyObject* _pos);
87
+
83 88
 PyObject *rpnifs_weights(PyObject *self, PyObject *const *args, Py_ssize_t nargs);
84 89
 PyObject *rpnifs_weight(PyObject *self, PyObject *const *args, Py_ssize_t nargs);
90
+PyObject *rpnifs_position(PyObject *self, PyObject *const *args, Py_ssize_t nargs);
91
+PyObject *rpnifs_run(PyObject *self, PyObject *const *args, Py_ssize_t nargs);
85 92
 
86 93
 PyObject *rpnifs_str(PyObject *self);
87 94
 PyObject *rpnifs_repr(PyObject *self);
@@ -93,5 +100,7 @@ int rpnifs_expr_ass_item(PyObject *self, Py_ssize_t idx, PyObject *value);
93 100
 int rpnifs_getbuffer(PyObject *self, Py_buffer *view, int flags);
94 101
 void rpnifs_releasebuffer(PyObject *self, Py_buffer *view);
95 102
 
103
+int _rpnifs_update_if_tuple(PyRPNIFS_t *ifs_self);
104
+
96 105
 
97 106
 #endif

+ 5
- 0
rpn_if.c 파일 보기

@@ -176,6 +176,11 @@ void rpn_if_free(rpn_if_t* rif)
176 176
 size_t rpn_if_step(rpn_if_t *rif, size_t pos)
177 177
 {
178 178
 	size_t newpos;
179
+
180
+	assert(rif != NULL);
181
+	assert(rif->params != NULL);
182
+	assert(rif->params->getarg_f != NULL);
183
+
179 184
 	rif->params->getarg_f(rif, pos);
180 185
 	for(size_t i=0; i<rif->params->rpn_sz; i++)
181 186
 	{

+ 2
- 0
rpn_if.h 파일 보기

@@ -21,6 +21,8 @@
21 21
 
22 22
 #include "config.h"
23 23
 
24
+#include <assert.h>
25
+
24 26
 #include "rpn_jit.h"
25 27
 
26 28
 /**@file rpn_if.h

+ 10
- 2
rpn_ifs.c 파일 보기

@@ -209,6 +209,9 @@ int rpn_ifs_run(rpn_ifs_t *rifs, size_t n, unsigned char *rnd)
209 209
 		}
210 210
 		if(getrandom(_rnd, n, GRND_NONBLOCK) < 0)
211 211
 		{
212
+			int err = errno;
213
+			free(_rnd);
214
+			errno = err;
212 215
 			return -1;
213 216
 		}
214 217
 	}
@@ -220,6 +223,7 @@ int rpn_ifs_run(rpn_ifs_t *rifs, size_t n, unsigned char *rnd)
220 223
 	for(size_t i=0; i<n; i++)
221 224
 	{
222 225
 		rpn_if_t *cur_if = rifs->if_proba[_rnd[i]];
226
+		assert(cur_if != NULL);
223 227
 		rifs->pos = rpn_if_step(cur_if, rifs->pos);
224 228
 	}
225 229
 	return 0;
@@ -251,9 +255,13 @@ int rpn_ifs_weight_update(rpn_ifs_t *rifs)
251 255
 			j++;
252 256
 		}
253 257
 	}
254
-	if(j==255)
258
+	for(j=j; j<256; j++)
255 259
 	{
256
-		rifs->if_proba[255] = rifs->if_proba[254];
260
+		// dirty & quick fill with last value
261
+		/* @todo enhance the random choice resolution to match
262
+		 * more closely asked weights (uint16 vs actualt uint8 random
263
+		 * choice ? */
264
+		rifs->if_proba[j] = rifs->if_proba[j-1];
257 265
 	}
258 266
 	return 0;
259 267
 }

+ 2
- 1
rpn_ifs.h 파일 보기

@@ -21,6 +21,7 @@
21 21
 
22 22
 #include "config.h"
23 23
 
24
+#include <assert.h>
24 25
 #include <string.h>
25 26
 
26 27
 #include "rpn_jit.h"
@@ -126,7 +127,7 @@ int rpn_ifs_del_if(rpn_ifs_t *rifs, size_t if_idx);
126 127
  * @param rifs The iterated function system
127 128
  * @param n consecutive IFS calls
128 129
  * @param seed prepopulated array of random bytes (if NULL one is generated)
129
- * @return 1 if error else 0
130
+ * @return -1 if error else 0
130 131
  */
131 132
 int rpn_ifs_run(rpn_ifs_t *rifs, size_t n, unsigned char *rnd);
132 133
 

+ 75
- 35
tests/benchmark.py 파일 보기

@@ -1,5 +1,7 @@
1 1
 #!/usr/bin/python3
2 2
 
3
+import argparse
4
+import os
3 5
 import sys
4 6
 import random
5 7
 import time
@@ -16,31 +18,24 @@ except (ImportError, NameError) as e:
16 18
           file=sys.stderr)
17 19
     raise e
18 20
 
19
-if len(sys.argv) > 5:
20
-    usage()
21
-    exit(1)
22
-
23
-#expr_count = 0x200
24
-#max_iter = 0x3000
25
-#argc = 2
26
-#sz = 0x20
27
-expr_count = 0x200
28
-max_iter = 0x5000
29
-#argc = 2
30
-#sz = 0x30
31
-argc = 5
32
-sz = 0x50
21
+allint = lambda val: int(val, 0)
33 22
 
34
-try:
35
-    expr_count = int(sys.argv[1], base=0)
36
-    max_iter = int(sys.argv[2], base=0)
37
-    argc = int(sys.argv[3], base=0)
38
-    sz = int(sys.argv[4], base=0)
39
-except IndexError:
40
-    pass
41
-except Exception:
42
-    usage()
43
-    exit(2)
23
+parser = argparse.ArgumentParser(description="Benchmark RPNExpr vs IFS")
24
+parser.add_argument("-c", "--expr-count", type=allint, default=0x200)
25
+parser.add_argument("-i", "--iterations", type=allint, default=0x5000)
26
+parser.add_argument("-s", "--expr-size", type=allint, default=0x50)
27
+parser.add_argument("-G", "--gnuplot-output", type=str, default=None)
28
+
29
+args = parser.parse_args()
30
+expr_count = args.expr_count
31
+max_iter = args.iterations
32
+sz = args.expr_size
33
+
34
+argc = 1
35
+
36
+gpout=None
37
+if args.gnuplot_output is not None:
38
+    gpout = open(args.gnuplot_output, "a")
44 39
 
45 40
 try:
46 41
     from tqdm import tqdm
@@ -53,14 +48,6 @@ except (ImportError, NameError) as e:
53 48
     tqr = range
54 49
     write=True
55 50
 
56
-if len(sys.argv) > 1:
57
-    expr_count = int(sys.argv[1], 0)
58
-if len(sys.argv) > 2:
59
-    max_iter = int(sys.argv[2], 0)
60
-if len(sys.argv) > 3:
61
-    sz = int(sys.argv[4], 0)
62
-if len(sys.argv) > 4:
63
-    argc = int(sys.argv[3], 0)
64 51
 
65 52
 tot = 0
66 53
 time_op = 0
@@ -68,12 +55,15 @@ start = time.time()
68 55
 IMAX = (1<<63)-1
69 56
 samples = 8
70 57
 
58
+print("========\nIF  :", end=" ")
71 59
 print("Running %dK iter on %d expressions with %d op and %d args" % (max_iter//1000,
72 60
                                                          expr_count,
73 61
                                                          sz, argc))
74 62
 
63
+res = [0 for _ in range(512)]
75 64
 rnd_samples = [[[random.randint(0, IMAX) for _ in range(argc)] for _ in range(max_iter)]
76 65
                for _ in range(samples)]
66
+
77 67
 for i in tqr(expr_count):
78 68
     rnd_expr = pyrpn.random_expr(argc, sz)
79 69
     all_start = time.time()
@@ -82,7 +72,10 @@ for i in tqr(expr_count):
82 72
     start_op = time.time()
83 73
     rnds = random.choice(rnd_samples)
84 74
     for rnd in rnds:
85
-        expr.eval(*rnd)
75
+        r = expr.eval(*rnd)
76
+        # emulate memory access for simple if to make comparison
77
+        # consistant with IFS
78
+        res[r%512] += 1
86 79
     time_op += time.time() - start_op
87 80
     tot += time.time() - all_start
88 81
     if write:
@@ -91,8 +84,7 @@ for i in tqr(expr_count):
91 84
 if write:
92 85
     sys.stderr.write("\n")
93 86
 total = time.time() - start
94
-print("Runned %dK iter on %d expressions in %.2fs" % (max_iter//1000,
95
-                                                     expr_count, total))
87
+
96 88
 ops = max_iter*expr_count*sz/time_op/(1000*1000)
97 89
 msg = "%5.1fM op/s %6.1fK iter/s %.1fms per expr (%dK iter, %d op per expr)"
98 90
 msg %= (ops,
@@ -100,3 +92,51 @@ msg %= (ops,
100 92
         tot/expr_count*1000,
101 93
         max_iter//1000, sz)
102 94
 print(msg)
95
+print("++++++++")
96
+if_ops = ops
97
+
98
+
99
+if_sz = 5
100
+ifs_count = expr_count // if_sz
101
+ifs_sz = 3
102
+mem_sz = (512,512)
103
+expr_count = ifs_count * if_sz
104
+
105
+print("IFS :", end=" ")
106
+print("Running %dK iter on %d ifs (sz=%d with %d if choosen randomly and %d op) RGB of size %r" % \
107
+        (max_iter/1000, ifs_count , if_sz, ifs_sz, sz, mem_sz))
108
+
109
+ifs = pyrpn.RPNIFS(pyrpn.const.POS_XY, pyrpn.const.RESULT_RGB, mem_sz)
110
+runtime = 0
111
+all_start = time.time()
112
+for i in tqr(ifs_count):
113
+    weights = [random.randint(1,50) for _ in range(ifs_sz)]
114
+    ifs.weights(weights)
115
+    for i in range(ifs_sz):
116
+        for expr_id in 'RGBXY':
117
+            ifs[i][expr_id] = pyrpn.random_expr(if_sz, sz)
118
+
119
+    rnd = os.getrandom(max_iter)
120
+    start = time.time()
121
+    ifs.run(max_iter, rnd)
122
+    runtime += time.time() - start
123
+    if write:
124
+        sys.stderr.write(".")
125
+        sys.stderr.flush()
126
+if write:
127
+    sys.stderr.write("\n")
128
+
129
+total = time.time() - all_start
130
+ops = max_iter*ifs_count*if_sz*sz/(runtime*1000*1000)
131
+msg = "%5.1fM op/s %6.1fK expr_iter/s %.2fms per ifs"
132
+msg %= (ops,
133
+        max_iter*ifs_count*if_sz/runtime/1000,
134
+        total/ifs_count)
135
+ifs_ops = ops
136
+print(msg)
137
+print("========")
138
+
139
+if gpout:
140
+    line = "%d %d %f %f\n" % (max_iter, sz, if_ops, ifs_ops)
141
+    gpout.write(line)
142
+    gpout.close()

+ 2
- 2
tests/test_rpn.c 파일 보기

@@ -262,8 +262,8 @@ int test_rpn_if_default()
262 262
 		return -1;
263 263
 	}
264 264
 
265
-	free(rif_param);
266 265
 	rpn_if_free(rif);
266
+	free(rif_param);
267 267
 
268 268
 	return 0;
269 269
 }
@@ -289,8 +289,8 @@ int test_rpn_if_default2()
289 289
 		return -1;
290 290
 	}
291 291
 
292
-	free(rif_param);
293 292
 	rpn_if_free(rif);
293
+	free(rif_param);
294 294
 
295 295
 	return 0;
296 296
 }

+ 192
- 0
tests/tests_rpn_ifs.py 파일 보기

@@ -25,6 +25,7 @@ import sys
25 25
 
26 26
 import unittest
27 27
 
28
+
28 29
 try:
29 30
     import pyrpn
30 31
 except (ImportError, NameError) as e:
@@ -63,3 +64,194 @@ class TestRPNIFS(unittest.TestCase):
63 64
                 nw = ifs.weights()
64 65
                 self.assertEqual(tuple(w), nw)
65 66
 
67
+    def test_position(self):
68
+        ifs = pyrpn.RPNIFS(pyrpn.const.POS_XY,
69
+                                pyrpn.const.RESULT_COUNT,
70
+                                (640,480))
71
+        self.assertEqual(len(ifs), 0)
72
+        self.assertEqual(ifs.position(), 0)
73
+        self.assertEqual(ifs.position(10), 10)
74
+        self.assertEqual(ifs.position(), 10)
75
+        for i in range(640*480):
76
+            ifs.position(i)
77
+            self.assertEqual(ifs.position(), i)
78
+
79
+
80
+    def test_run(self):
81
+        """ Testing ifs run """
82
+        ifs = pyrpn.RPNIFS(pyrpn.const.POS_XY,
83
+                           pyrpn.const.RESULT_COUNT,
84
+                           (256,256))
85
+        ifs.weights([1])
86
+        ifs[0]['X'] = 'A0 13 +'
87
+        ifs[0]['Y'] = 'A1 2 +'
88
+        xpos = lambda steps: (steps*13)%256
89
+        ypos = lambda steps: (steps*2)%256
90
+        ifs.run(1)
91
+        self.assertEqual(ifs.position(), ifs.to_pos(xpos(1),ypos(1)))
92
+        steps = 1
93
+        for _ in range(128):
94
+            rnd = random.randint(1,128)
95
+            steps += rnd
96
+            ifs.run(rnd)
97
+            self.assertEqual(ifs.position(),
98
+                    ifs.to_pos(xpos(steps), ypos(steps)))
99
+
100
+
101
+    def test_run_rnd(self):
102
+        """ Testing ifs run with random if choice, same weights """
103
+        sz = [10000,10000]
104
+        ifs = pyrpn.RPNIFS(pyrpn.const.POS_XY,
105
+                           pyrpn.const.RESULT_COUNT,
106
+                           sz)
107
+        ifs.weights([1,1])
108
+        ifs[0]['X'] = 'A0 1 +'
109
+        ifs[0]['Y'] = 'A1 1 -'
110
+        ifs[1]['X'] = 'A0 1 -'
111
+        ifs[1]['Y'] = 'A0 1 +'
112
+        for _ in Progress(256):
113
+            ifs.position(ifs.to_pos(*[s//2 for s in sz]))
114
+            
115
+            steps = 0
116
+            steps_per_loop = 0x1000
117
+            for _ in range(4):
118
+                steps += steps_per_loop
119
+                ifs.run(steps_per_loop)
120
+                coord = ifs.from_pos(ifs.position())
121
+                
122
+                distance = [min(c, sz[i]-c) for i, c in enumerate(coord)]
123
+                dist = sum(distance)/2
124
+                percent = steps / (dist * 100)
125
+                self.assertLess(percent, 1)
126
+
127
+class TestRPNIFSCoordinates(unittest.TestCase):
128
+    """ Testing methods for coordinates <-> position convertions """
129
+
130
+    def test_position_linear(self):
131
+        """ Testing linear coordinate convertion methods """
132
+        rif = pyrpn.RPNIFS(pyrpn.const.POS_LINEAR,
133
+                    pyrpn.const.RESULT_CONST,
134
+                    (256,), (42,))
135
+
136
+        for pos in range(256):
137
+            with self.subTest(rif=rif, position=pos, expt=(pos,)):
138
+                res = rif.from_pos(pos)
139
+                self.assertEqual(res, (pos,))
140
+            with self.subTest(rif=rif, expt=pos, coord=(pos,)):
141
+                res = rif.to_pos(pos)
142
+                self.assertEqual(res, pos)
143
+
144
+    def test_position_xy(self):
145
+        """ Testing XY coordinate convertion methods """
146
+        for _ in Progress(5):
147
+            sz = (random.randint(32,128), random.randint(32,128))
148
+            rif = pyrpn.RPNIFS(pyrpn.const.POS_XY,
149
+                    pyrpn.const.RESULT_CONST,
150
+                    sz, (42,))
151
+            for pos in range(sz[0]*sz[1]):
152
+                coord = rif.from_pos(pos)
153
+                expt = (pos % sz[0], pos // sz[0])
154
+                with self.subTest(sz=sz, pos=pos, expt=expt, result=coord):
155
+                    self.assertEqual(expt, coord)
156
+                result = rif.to_pos(*coord)
157
+                with self.subTest(sz=sz, coord=coord, expt=pos, result=result):
158
+                    self.assertEqual(result, pos)
159
+
160
+    def test_position_xy_neg(self):
161
+        """ Testing XY coordinates with negative values """
162
+        sz = (100,200)
163
+        rif = pyrpn.RPNIFS(pyrpn.const.POS_XY,
164
+                pyrpn.const.RESULT_CONST,
165
+                sz, (42,))
166
+        
167
+        for _ in range(1000):
168
+            ny = random.randint(-0x1000000, sz[1])
169
+            nx = random.randint(0,sz[0]-1)
170
+            pos = rif.to_pos(nx, ny)
171
+            coord = rif.from_pos(pos)
172
+            self.assertEqual(coord, (nx, (ny%(1<<64))%sz[1]))
173
+
174
+    def test_position_xy_random(self):
175
+        """ Testing XY coordinate convertion methods (random samples)"""
176
+        for _ in Progress(256):
177
+            sz = (random.randint(32,4096), random.randint(32,4096))
178
+            rif = pyrpn.RPNIFS(pyrpn.const.POS_XY,
179
+                    pyrpn.const.RESULT_CONST,
180
+                    sz, (42,))
181
+            for _ in range(500):
182
+                pos = random.randint(0, (sz[0]*sz[1])-1)
183
+                coord = rif.from_pos(pos)
184
+                expt = (pos % sz[0], pos // sz[0])
185
+                with self.subTest(sz=sz, pos=pos, expt=expt, result=coord):
186
+                    self.assertEqual(expt, coord)
187
+                result = rif.to_pos(*coord)
188
+                with self.subTest(sz=sz, coord=coord, expt=pos, result=result):
189
+                    self.assertEqual(result, pos)
190
+
191
+    def test_position_xdim(self):
192
+        """ Testing XDIM coordinate convertion methods """
193
+        ndim = 5
194
+        resol = [8 for _ in range(ndim)]
195
+        sz = [ndim]+resol
196
+        linear_size = 1
197
+        for e in resol:
198
+            linear_size *= e
199
+        rif = pyrpn.RPNIFS(pyrpn.const.POS_XDIM,
200
+                pyrpn.const.RESULT_CONST,
201
+                sz, (42,))
202
+        for pos in range(linear_size):
203
+            coord = rif.from_pos(pos)
204
+            expt = []
205
+            cur = pos
206
+            for dim in resol:
207
+                elt = cur % dim
208
+                cur //= dim
209
+                expt.append(elt)
210
+            expt = tuple(expt)
211
+            with self.subTest(sz=sz, pos=pos, expt=expt, result=coord):
212
+                self.assertEqual(expt, coord)
213
+            result = rif.to_pos(*coord)
214
+            with self.subTest(sz=sz, coord=coord, expt=pos, result=result):
215
+                self.assertEqual(result, pos)
216
+
217
+    def test_position_xdim_neg(self):
218
+        """ Testing XDIM negative position convertion """
219
+        # TODO test negativ for other coordinate systems
220
+        ndim = 4
221
+        szlim = (ndim, 10,20,30,40)
222
+        rif = pyrpn.RPNIFS(pyrpn.const.POS_XDIM,
223
+                pyrpn.const.RESULT_CONST,
224
+                szlim, (42,))
225
+
226
+        pos = rif.to_pos(0,0,-5,0)
227
+        self.assertEqual(rif.from_pos(pos), (0,0,(-5%(1<<64))%30, 0))
228
+
229
+    def test_position_xdim(self):
230
+        """ Testing XDIM coordinate convertion methods (random sampling)"""
231
+        for _ in Progress(16):
232
+            ndim = random.randint(3,8)
233
+            resol = [random.randint(2, 16) for _ in range(ndim)]
234
+            sz = [ndim]+resol
235
+            linear_size = 1
236
+            for e in resol:
237
+                linear_size *= e
238
+            rif = pyrpn.RPNIFS(pyrpn.const.POS_XDIM,
239
+                    pyrpn.const.RESULT_CONST,
240
+                    sz, (42,))
241
+            for _ in range(2000):
242
+                pos = random.randint(0, linear_size-1)
243
+                coord = rif.from_pos(pos)
244
+                expt = []
245
+                cur = pos
246
+                for dim in resol:
247
+                    elt = cur % dim
248
+                    cur //= dim
249
+                    expt.append(elt)
250
+                expt = tuple(expt)
251
+                with self.subTest(sz=sz, pos=pos, expt=expt, result=coord):
252
+                    self.assertEqual(expt, coord)
253
+                result = rif.to_pos(*coord)
254
+                with self.subTest(sz=sz, coord=coord, expt=pos, result=result):
255
+                    self.assertEqual(result, pos)
256
+
257
+

Loading…
취소
저장