[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [pdf-devel] Type 4 functions
From: |
Johannes Tax |
Subject: |
Re: [pdf-devel] Type 4 functions |
Date: |
Sat, 9 Jan 2010 13:21:57 +0100 |
User-agent: |
Mutt/1.5.19 (2009-01-05) |
Hi,
On [Wed, 06.01.2010 23:04], address@hidden wrote:
> [...]
>
> could work, yielding the boolean values in the stack. But it is not
> clear what would happen with a function like:
>
> { true true and }
>
> With the current implementation and the above hack, the result on the
> stack would be something like 1.202057, that is the float
> interpretation of the internal representation of 'true'
> (PDF_FP_FUNC_TYPE4_TRUE).
>
> On the other hand, the spec is clear in that the range of the pdf
> functions is a real interval, so I suppose that the above function
> shall be considered invalid.
I think so too. Furthermore the current implementation allows something
like { true true and 1 add }, which is clearly invalid (the ghostscript
interpreter raises a type exception).
It seems that the PDF spec requires a stricter type checking than is
currently implemented. The mod operator for example is defined to take
two integer arguments. So { 10.0 3.0 mod } should be invalid (and is
treated as such by gs) but it is not treated as such by the current
implementation.
Treating this correctly would require attaching a type to every element
on the stack, I think something like
struct stack_element_t {
enum type { BOOL, INT, REAL } t;
double v;
} stack[NSTACK+2];
and a set of suitable predicates/accessors would do the job, some
additional checks for numerical constants and return values would be
required too. I don't know if this is considered neccessary. As far as
I can see the current approach works well for all valid type 4 functions.
However, it also treats obviously invalid type 4 functions as valid ones.
I don't think that fixing this requires too much effort.
> [...]
>
> Actually what we need are systematic tests on every allowed operator.
> For each operator, would be good to have:
>
> - A couple of positive tests (with valid values in the domain).
> - A couple of negative tests, if applicable (with invalid values in
> the domain).
> - A couple of extreme/interesting cases tests, if applicable.
Please find attached some first tests I have written
(torture/unit/base/fp/pdf-fp-func-eval.c) and the required changes to
pass them (src/base/pdf-fp-func.c). The changes concern the following
issues:
1) Input values are not clipped to the domain.
2) The second operand to mod must not be 0. I suppose in this case
setting PDF_EMATH as appropriate.
3) It is a range error to pass a negative value to the log functions. I
think passing 0 should cause a range error too.
I will go on writing tests in this manner.
Regards,
Johannes
--
Johannes Tax
address@hidden
=== modified file 'src/base/pdf-fp-func.c'
--- src/base/pdf-fp-func.c 2009-06-17 19:42:48 +0000
+++ src/base/pdf-fp-func.c 2010-01-09 10:45:37 +0000
@@ -1439,7 +1439,7 @@
for (sp = 0; sp < t->m; sp++)
{
- stack[sp] = in[sp];
+ stack[sp] = clip(in[sp], t->domain + 2*sp);
}
sp--;
for (pc = 0; pc < n; pc++)
@@ -1515,7 +1515,7 @@
break;
case OPC_log:
if (sp < 0) goto stack_underflow;
- if (stack[sp] < 0) goto range_error;
+ if (stack[sp] <= 0) goto range_error;
stack[sp] = pdf_fp_log10 (stack[sp]);
break;
case OPC_ln:
@@ -1642,6 +1642,8 @@
if (sp < 1) goto stack_underflow;
if (!INT_P(stack[sp]) || !INT_P(stack[sp-1]))
goto type_error;
+ if (stack[sp-1] == 0)
+ goto math_error;
stack[sp-1] = INT(stack[sp]) % INT(stack[sp-1]);
sp--;
break;
=== modified file 'torture/unit/base/fp/pdf-fp-func-eval.c'
--- torture/unit/base/fp/pdf-fp-func-eval.c 2009-08-05 20:32:56 +0000
+++ torture/unit/base/fp/pdf-fp-func-eval.c 2010-01-09 10:47:46 +0000
@@ -1848,6 +1848,171 @@
END_TEST
+/*
+ * Test: pdf_fp_func_eval_025
+ * Description:
+ * Evaluate a type 4 function that uses the mod operator.
+ * Success condition:
+ * The result of the evaluations should be correct.
+ */
+static pdf_real_t
+mod_f (pdf_real_t x, pdf_real_t y)
+{
+ return (int)x % (int)y;
+}
+START_TEST(pdf_fp_func_eval_025)
+{
+ pdf_fp_func_t func;
+ pdf_fp_func_debug_t debug;
+ pdf_size_t prog_size;
+ pdf_char_t *prog =
+ "{ "
+ " mod"
+ "}" ;
+
+ pdf_real_t domain[4] = {-10.0, 10.0, -10.0, 10.0};
+ pdf_real_t range[2] = {-10.0, 10.0};
+ pdf_real_t in[2];
+ pdf_real_t out[1];
+
+ prog_size = strlen (prog);
+
+ pdf_init();
+
+ /* Create the function */
+ fail_if(pdf_fp_func_4_new (2, 1,
+ domain,
+ range,
+ prog,
+ prog_size,
+ NULL,
+ &func) != PDF_OK);
+
+ /*
+ * Eval for some values
+ */
+
+ /* x = 1, y = 0
+ * math error - division by 0 */
+ in[0] = 0;
+ in[1] = 1;
+ fail_if(pdf_fp_func_eval (func, in, out, &debug) != PDF_ETYPE4);
+ fail_if(debug.type4.status != PDF_EMATH);
+
+ /* x = 5, y = 3.5
+ * type error - real number argument to mod */
+ in[0] = 3.5;
+ in[1] = 5;
+ fail_if(pdf_fp_func_eval (func, in, out, &debug) != PDF_ETYPE4);
+ fail_if(debug.type4.status != PDF_EBADTYPE);
+
+ /* x = 9.3, y = 3
+ * type error - real number argument to mod */
+ in[0] = 3;
+ in[1] = 9.3;
+ fail_if(pdf_fp_func_eval (func, in, out, &debug) != PDF_ETYPE4);
+ fail_if(debug.type4.status != PDF_EBADTYPE);
+
+ /* x = 0, y = 1 */
+ in[0] = 1;
+ in[1] = 0;
+ fail_if(pdf_fp_func_eval (func, in, out, NULL) != PDF_OK);
+ fail_if(pdf_fp_abs(out[0] - mod_f(in[1], in[0])) > ABS_ERROR);
+
+ /* x = 5, y = 3 */
+ in[0] = 3;
+ in[1] = 5;
+ fail_if(pdf_fp_func_eval (func, in, out, NULL) != PDF_OK);
+ fail_if(pdf_fp_abs(out[0] - mod_f(in[1], in[0])) > ABS_ERROR);
+
+ /* x = 12, y = 7
+ * x out of (positive) domain */
+ in[0] = 7;
+ in[1] = 12;
+ fail_if(pdf_fp_func_eval (func, in, out, NULL) != PDF_OK);
+ fail_if(pdf_fp_abs(out[0] - mod_f(domain[1], in[0])) > ABS_ERROR);
+}
+END_TEST
+
+
+/*
+ * Test: pdf_fp_func_eval_026
+ * Description:
+ * Evaluate a type 4 function that uses the log operator.
+ * Success condition:
+ * The result of the evaluations should be correct.
+ */
+static pdf_real_t
+log_f (pdf_real_t x)
+{
+ return pdf_fp_log10(x);
+}
+START_TEST(pdf_fp_func_eval_026)
+{
+ pdf_fp_func_t func;
+ pdf_fp_func_debug_t debug;
+ pdf_size_t prog_size;
+ pdf_char_t *prog =
+ "{ "
+ " log"
+ "}" ;
+
+ pdf_real_t domain[2] = {-20.0, 20.0};
+ pdf_real_t range[2] = {-1.0, 1.0};
+ pdf_real_t in[1];
+ pdf_real_t out[1];
+
+ prog_size = strlen (prog);
+
+ pdf_init();
+
+ /* Create the function */
+ fail_if(pdf_fp_func_4_new (1, 1,
+ domain,
+ range,
+ prog,
+ prog_size,
+ NULL,
+ &func) != PDF_OK);
+
+ /*
+ * Eval for some values
+ */
+
+ /* x = -1
+ * invalid range error - log of negative number */
+ in[0] = -1;
+ fail_if(pdf_fp_func_eval (func, in, out, &debug) != PDF_ETYPE4);
+ fail_if(debug.type4.status != PDF_EINVRANGE);
+
+ /* x = 0 *
+ * invalid range error - log of 0 */
+ in[0] = 0;
+ fail_if(pdf_fp_func_eval (func, in, out, &debug) != PDF_ETYPE4);
+ fail_if(debug.type4.status != PDF_EINVRANGE);
+
+ /* x = 1 */
+ in[0] = 1;
+ fail_if(pdf_fp_func_eval (func, in, out, NULL) != PDF_OK);
+ fail_if(pdf_fp_abs(out[0] - log_f(in[0])) > ABS_ERROR);
+
+ /* x = 1.533 */
+ in[0] = 1.533;
+ fail_if(pdf_fp_func_eval (func, in, out, NULL) != PDF_OK);
+ fail_if(pdf_fp_abs(out[0] - log_f(in[0])) > ABS_ERROR);
+
+ /* x = 9 */
+ in[0] = 9;
+ fail_if(pdf_fp_func_eval (func, in, out, NULL) != PDF_OK);
+ fail_if(pdf_fp_abs(out[0] - log_f(in[0])) > ABS_ERROR);
+
+ /* x = 14
+ * result out of (positive) range */
+ in[0] = 14;
+ fail_if(pdf_fp_func_eval (func, in, out, NULL) != PDF_OK);
+ fail_if(pdf_fp_abs(out[0] - range[1]) > ABS_ERROR);
+}
+END_TEST
@@ -1883,6 +2048,8 @@
tcase_add_test(tc, pdf_fp_func_eval_022);
tcase_add_test(tc, pdf_fp_func_eval_023);
tcase_add_test(tc, pdf_fp_func_eval_024);
+ tcase_add_test(tc, pdf_fp_func_eval_025);
+ tcase_add_test(tc, pdf_fp_func_eval_026);
return tc;
}