diff --git a/include/libsigrok/proto.h b/include/libsigrok/proto.h index bd725057..1a3fba8c 100644 --- a/include/libsigrok/proto.h +++ b/include/libsigrok/proto.h @@ -36,6 +36,8 @@ SR_API void sr_rational_set(struct sr_rational *r, int64_t p, uint64_t q); SR_API int sr_rational_eq(const struct sr_rational *a, const struct sr_rational *b); SR_API int sr_rational_mult(struct sr_rational *res, const struct sr_rational *a, const struct sr_rational *b); +SR_API int sr_rational_div(struct sr_rational *res, const struct sr_rational *num, + const struct sr_rational *div); /*--- backend.c -------------------------------------------------------------*/ diff --git a/src/analog.c b/src/analog.c index 7c0dde1b..7343b481 100644 --- a/src/analog.c +++ b/src/analog.c @@ -444,6 +444,8 @@ SR_API int sr_rational_eq(const struct sr_rational *a, const struct sr_rational * otherwise. If the resulting nominator/denominator are relatively prime, * this may not be possible. * + * It is save to use the same variable for result and input values + * * @retval SR_OK Success. * @retval SR_ERR_ARG Resulting value to large * @@ -506,4 +508,45 @@ SR_API int sr_rational_mult(struct sr_rational *res, const struct sr_rational *a #endif } +/** + * Divide rational a by rational b + * + * @param[in] num numerator + * @param[in] div divisor + * @param[out] res Result + * + * The resulting nominator/denominator are reduced if the result would not fit + * otherwise. If the resulting nominator/denominator are relatively prime, + * this may not be possible. + * + * It is save to use the same variable for result and input values + * + * @retval SR_OK Success. + * @retval SR_ERR_ARG Division by zero + * @retval SR_ERR_ARG Denominator of divisor to large + * @retval SR_ERR_ARG Resulting value to large + * + * @since 0.5.0 + */ +SR_API int sr_rational_div(struct sr_rational *res, const struct sr_rational *num, + const struct sr_rational *div) +{ + struct sr_rational t; + + if (div->q > INT64_MAX) + return SR_ERR_ARG; + if (div->p == 0) + return SR_ERR_ARG; + + if (div->p > 0) { + t.p = div->q; + t.q = div->p; + } else { + t.p = -div->q; + t.q = -div->p; + } + + return sr_rational_mult(res, num, &t); +} + /** @} */ diff --git a/tests/analog.c b/tests/analog.c index b5f2beb8..3e8fd494 100644 --- a/tests/analog.c +++ b/tests/analog.c @@ -266,6 +266,50 @@ START_TEST(test_mult_rational) } END_TEST +START_TEST(test_div_rational) +{ + const struct sr_rational r[][3] = { + /* a * b = c */ + { { 1, 1 }, { 1, 1 }, { 1, 1 }}, + { { 2, 1 }, { 1, 3 }, { 6, 1 }}, + { { 1, 2 }, { 1, 2 }, { 1, 1 }}, + /* Test negative numbers */ + { { -1, 2 }, { 1, 2 }, { -1, 1 }}, + { { -1, 2 }, { -1, 2 }, { 1, 1 }}, + { { -(1ll<<20), (1ll<<10) }, { -1, (1ll<<20) }, { (1ll<<30), 1 }}, + /* Test reduction */ + { { INT32_MAX, (1ll<<12) }, { 1, (1<<2) }, { INT32_MAX, (1ll<<10) }}, + { { INT64_MAX, (1ll<<63) }, { 1, (1<<3) }, { INT64_MAX, (1ll<<60) }}, + /* Test large numbers */ + { { (1ll<<40), (1ll<<10) }, { 1, (1ll<<30) }, { (1ll<<60), 1 }}, + { { -(1ll<<40), (1ll<<10) }, { -1, (1ll<<30) }, { (1ll<<60), 1 }}, + + { { 10000*3, 4 }, { 1, 80000*3 }, { 200000000*9, 1 }}, + { { 4, 10000*3 }, { 80000*3, 1 }, { 1, 200000000*9 }}, + + { { -10000*3, 4 }, { 1, 80000*3 }, { -200000000*9, 1 }}, + { { 10000*3, 4 }, { -1, 80000*3 }, { -200000000*9, 1 }}, + }; + + for (unsigned i = 0; i < ARRAY_SIZE(r); i++) { + struct sr_rational res; + + int rc = sr_rational_div(&res, &r[i][0], &r[i][1]); + fail_unless(rc == SR_OK); + fail_unless(sr_rational_eq(&res, &r[i][2]) == 1, + "sr_rational_mult() failed: [%d] %ld/%lu != %ld/%lu.", + i, res.p, res.q, r[i][2].p, r[i][2].q); + } + + { + struct sr_rational res; + int rc = sr_rational_div(&res, &r[0][0], &((struct sr_rational){ 0, 5 })); + + fail_unless(rc == SR_ERR_ARG); + } +} +END_TEST + Suite *suite_analog(void) { Suite *s; @@ -282,6 +326,7 @@ Suite *suite_analog(void) tcase_add_test(tc, test_set_rational_null); tcase_add_test(tc, test_cmp_rational); tcase_add_test(tc, test_mult_rational); + tcase_add_test(tc, test_div_rational); suite_add_tcase(s, tc); return s;