From 87952fdb8ed56fcf926bea81e3247ff3c6395e31 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 29 Oct 2021 12:40:54 +0100 Subject: [PATCH] `big.Rat` (Experimental) --- core/math/big/api.odin | 14 + core/math/big/helpers.odin | 36 ++- core/math/big/internal.odin | 17 +- core/math/big/public.odin | 21 +- core/math/big/rat.odin | 540 ++++++++++++++++++++++++++++++++++++ 5 files changed, 614 insertions(+), 14 deletions(-) create mode 100644 core/math/big/rat.odin diff --git a/core/math/big/api.odin b/core/math/big/api.odin index 8c9d2e799..c9be04da0 100644 --- a/core/math/big/api.odin +++ b/core/math/big/api.odin @@ -32,6 +32,9 @@ add :: proc { int_add_digit :: proc(dest, a: ^Int, digit: DIGIT, allocator := context.allocator) -> (err: Error) */ int_add_digit, + rat_add_rat, + rat_add_int, + int_add_rat, } /* @@ -46,6 +49,9 @@ sub :: proc { int_sub_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) */ int_sub_digit, + rat_sub_rat, + rat_sub_int, + int_sub_rat, } /* @@ -67,6 +73,10 @@ is_zero :: proc { int_is_zero :: proc(a: ^Int) -> bool */ int_is_zero, + /* + rat_is_zero :: proc(a: ^Rat) -> bool + */ + rat_is_zero, } is_positive :: proc { @@ -74,6 +84,7 @@ is_positive :: proc { int_is_positive :: proc(a: ^Int) -> bool */ int_is_positive, + rat_is_positive, } is_pos :: is_positive @@ -82,6 +93,7 @@ is_negative :: proc { int_is_negative :: proc(a: ^Int) -> bool */ int_is_negative, + rat_is_negative, } is_neg :: is_negative @@ -90,6 +102,7 @@ is_even :: proc { int_is_even :: proc(a: ^Int) -> bool */ int_is_even, + rat_is_even, } is_odd :: proc { @@ -97,6 +110,7 @@ is_odd :: proc { int_is_odd :: proc(a: ^Int) -> bool */ int_is_odd, + rat_is_odd, } is_power_of_two :: proc { diff --git a/core/math/big/helpers.odin b/core/math/big/helpers.odin index ceb3fcf60..6d13d32bb 100644 --- a/core/math/big/helpers.odin +++ b/core/math/big/helpers.odin @@ -44,7 +44,20 @@ int_set_from_integer :: proc(dest: ^Int, src: $T, minimize := false, allocator : return #force_inline internal_int_set_from_integer(dest, src, minimize) } -set :: proc { int_set_from_integer, int_copy, int_atoi, } +set :: proc { + int_set_from_integer, + int_copy, + int_atoi, + + rat_set_f64, + rat_set_f32, + rat_set_f16, + rat_set_u64, + rat_set_i64, + rat_set_int, + rat_set_digit, + rat_set_rat, +} /* Copy one `Int` to another. @@ -66,7 +79,10 @@ int_copy :: proc(dest, src: ^Int, minimize := false, allocator := context.alloca return #force_inline internal_int_copy(dest, src, minimize) } -copy :: proc { int_copy, } +copy :: proc { + int_copy, + rat_copy, +} /* In normal code, you can also write `a, b = b, a`. @@ -77,7 +93,7 @@ int_swap :: proc(a, b: ^Int) { assert_if_nil(a, b) #force_inline internal_swap(a, b) } -swap :: proc { int_swap, } +swap :: proc { int_swap, rat_swap } /* Set `dest` to |`src`|. @@ -98,7 +114,7 @@ int_abs :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) platform_abs :: proc(n: $T) -> T where intrinsics.type_is_integer(T) { return n if n >= 0 else -n } -abs :: proc{ int_abs, platform_abs, } +abs :: proc{ int_abs, platform_abs, rat_abs } /* Set `dest` to `-src`. @@ -115,7 +131,7 @@ int_neg :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) return #force_inline internal_int_neg(dest, src) } -neg :: proc { int_neg, } +neg :: proc { int_neg, rat_neg } /* Helpers to extract values from the `Int`. @@ -788,7 +804,10 @@ destroy_constants :: proc() { } -assert_if_nil :: proc{assert_if_nil_int} +assert_if_nil :: proc{ + assert_if_nil_int, + assert_if_nil_rat, +} assert_if_nil_int :: #force_inline proc(integers: ..^Int, loc := #caller_location) { for i in integers { @@ -796,3 +815,8 @@ assert_if_nil_int :: #force_inline proc(integers: ..^Int, loc := #caller_locatio } } +assert_if_nil_rat :: #force_inline proc(rationals: ..^Rat, loc := #caller_location) { + for r in rationals { + assert(r != nil, "(nil)", loc) + } +} diff --git a/core/math/big/internal.odin b/core/math/big/internal.odin index d8e5157a4..4702e76a3 100644 --- a/core/math/big/internal.odin +++ b/core/math/big/internal.odin @@ -1042,7 +1042,10 @@ internal_is_initialized :: proc { internal_int_is_initialized, } internal_int_is_zero :: #force_inline proc(a: ^Int) -> (zero: bool) { return a.used == 0 } -internal_is_zero :: proc { internal_int_is_zero } +internal_is_zero :: proc { + internal_rat_is_zero, + internal_int_is_zero, +} /* This procedure will return `true` if the `Int` is positive, `false` if not. @@ -1865,7 +1868,10 @@ internal_int_destroy :: proc(integers: ..^Int) { a = &Int{} } } -internal_destroy :: proc{ internal_int_destroy, } +internal_destroy :: proc{ + internal_int_destroy, + internal_rat_destroy, +} /* Helpers to set an `Int` to a specific value. @@ -1950,13 +1956,14 @@ internal_copy :: proc { internal_int_copy, } This helper swaps completely. */ internal_int_swap :: #force_inline proc(a, b: ^Int) { - a := a; b := b - a.used, b.used = b.used, a.used a.sign, b.sign = b.sign, a.sign a.digit, b.digit = b.digit, a.digit } -internal_swap :: proc { internal_int_swap, } +internal_swap :: proc { + internal_int_swap, + internal_rat_swap, +} /* Set `dest` to |`src`|. diff --git a/core/math/big/public.odin b/core/math/big/public.odin index f0fc16f36..2673a262f 100644 --- a/core/math/big/public.odin +++ b/core/math/big/public.odin @@ -152,9 +152,18 @@ int_mul :: proc(dest, src, multiplier: ^Int, allocator := context.allocator) -> return #force_inline internal_int_mul(dest, src, multiplier) } -mul :: proc { int_mul, int_mul_digit, } +mul :: proc { + int_mul, + int_mul_digit, + rat_mul_rat, + rat_mul_int, + int_mul_rat, +} + +int_sqr :: proc(dest, src: ^Int) -> (err: Error) { return mul(dest, src, src) } +rat_sqr :: proc(dest, src: ^Rat) -> (err: Error) { return mul(dest, src, src) } +sqr :: proc { int_sqr, rat_sqr } -sqr :: proc(dest, src: ^Int) -> (err: Error) { return mul(dest, src, src) } /* divmod. @@ -200,7 +209,13 @@ int_div_digit :: proc(quotient, numerator: ^Int, denominator: DIGIT, allocator : _ = #force_inline internal_divmod(quotient, numerator, denominator) or_return return } -div :: proc { int_div, int_div_digit, } +div :: proc { + int_div, + int_div_digit, + rat_div_rat, + rat_div_int, + int_div_rat, +} /* remainder = numerator % denominator. diff --git a/core/math/big/rat.odin b/core/math/big/rat.odin new file mode 100644 index 000000000..8acd8c2c6 --- /dev/null +++ b/core/math/big/rat.odin @@ -0,0 +1,540 @@ +package math_big + +import "core:builtin" +import "core:intrinsics" +import "core:math" + +Rat :: struct { + a, b: Int, +} + +rat_set_f64 :: proc(dst: ^Rat, f: f64, allocator := context.allocator) -> (err: Error) { + assert_if_nil(dst) + context.allocator = allocator + + EXP_MASK :: 1<<11 - 1 + + bits := transmute(u64)f + mantissa := bits & (1<<52 - 1) + exp := int((bits>>52) & EXP_MASK) + + int_set_from_integer(&dst.b, 1) or_return + + switch exp { + case EXP_MASK: + dst.a.flags += {.Inf} + return + case 0: + exp -= 1022 + case: + mantissa |= 1<<52 + exp -= 1023 + } + + shift := 52 - exp + + for mantissa&1 == 0 && shift > 0 { + mantissa >>= 1 + shift -= 1 + } + + int_set_from_integer(&dst.a, mantissa) or_return + dst.a.sign = .Negative if f < 0 else .Zero_or_Positive + + if shift > 0 { + internal_int_shl_digit(&dst.b, shift) or_return + } else { + internal_int_shl_digit(&dst.a, -shift) or_return + } + + return internal_rat_norm(dst) +} + +rat_set_f32 :: proc(dst: ^Rat, f: f32, allocator := context.allocator) -> (err: Error) { + return rat_set_f64(dst, f64(f), allocator) +} +rat_set_f16 :: proc(dst: ^Rat, f: f16, allocator := context.allocator) -> (err: Error) { + return rat_set_f64(dst, f64(f), allocator) +} + + +rat_set_frac :: proc{rat_set_frac_digit, rat_set_frac_int} + +rat_set_frac_digit :: proc(dst: ^Rat, a, b: DIGIT, allocator := context.allocator) -> (err: Error) { + assert_if_nil(dst) + if b == 0 { + return .Division_by_Zero + } + context.allocator = allocator + internal_set(&dst.a, a) or_return + internal_set(&dst.b, b) or_return + return internal_rat_norm(dst) +} + +rat_set_frac_int :: proc(dst: ^Rat, a, b: ^Int, allocator := context.allocator) -> (err: Error) { + assert_if_nil(dst) + assert_if_nil(a, b) + if internal_is_zero(b) { + return .Division_by_Zero + } + context.allocator = allocator + internal_set(&dst.a, a) or_return + internal_set(&dst.b, b) or_return + return internal_rat_norm(dst) +} + +rat_set_int :: proc(dst: ^Rat, a: ^Int, allocator := context.allocator) -> (err: Error) { + assert_if_nil(dst) + assert_if_nil(a) + context.allocator = allocator + internal_set(&dst.a, a) or_return + internal_set(&dst.b, 1) or_return + return +} + +rat_set_digit :: proc(dst: ^Rat, a: DIGIT, allocator := context.allocator) -> (err: Error) { + assert_if_nil(dst) + context.allocator = allocator + internal_set(&dst.a, a) or_return + internal_set(&dst.b, 1) or_return + return +} + +rat_set_rat :: proc(dst, x: ^Rat, allocator := context.allocator) -> (err: Error) { + assert_if_nil(dst, x) + context.allocator = allocator + internal_set(&dst.a, &x.a) or_return + internal_set(&dst.b, &x.b) or_return + return +} + +rat_set_u64 :: proc(dst: ^Rat, x: u64, allocator := context.allocator) -> (err: Error) { + assert_if_nil(dst) + context.allocator = allocator + internal_set(&dst.a, x) or_return + internal_set(&dst.a, 1) or_return + return +} +rat_set_i64 :: proc(dst: ^Rat, x: i64, allocator := context.allocator) -> (err: Error) { + assert_if_nil(dst) + context.allocator = allocator + internal_set(&dst.a, x) or_return + internal_set(&dst.a, 1) or_return + return +} + +rat_copy :: proc(dst, src: ^Rat, minimize := false, allocator := context.allocator) -> (err: Error) { + if (dst == src) { return nil } + + assert_if_nil(dst, src) + context.allocator = allocator + int_copy(&dst.a, &src.a, minimize, allocator) or_return + int_copy(&dst.b, &src.b, minimize, allocator) or_return + internal_rat_norm(dst) or_return + return nil +} + +internal_rat_destroy :: proc(rationals: ..^Rat) { + rationals := rationals + + for z in &rationals { + internal_int_destroy(&z.a, &z.b) + } +} + +internal_rat_norm :: proc(z: ^Rat, allocator := context.allocator) -> (err: Error) { + assert_if_nil(z) + context.allocator = allocator + switch { + case internal_is_zero(&z.a): + z.a.sign = .Zero_or_Positive + fallthrough + case internal_is_zero(&z.b): + int_set_from_integer(&z.b, 1) or_return + case: + sign := z.a.sign + z.a.sign = .Zero_or_Positive + z.b.sign = .Zero_or_Positive + + f := &Int{} + internal_int_gcd(f, &z.a, &z.b) or_return + if !internal_int_equals_digit(f, 1) { + f.sign = .Zero_or_Positive + internal_int_div(&z.a, &z.a, f) or_return + internal_int_div(&z.b, &z.b, f) or_return + } + z.a.sign = sign + } + return +} + +rat_swap :: proc(a, b: ^Rat) { + assert_if_nil(a, b) + #force_inline internal_swap(a, b) +} + +internal_rat_swap :: #force_inline proc(a, b: ^Rat) { + internal_int_swap(&a.a, &b.a) + internal_int_swap(&a.b, &b.b) +} + +rat_sign :: proc(z: ^Rat) -> Sign { + if z == nil { + return .Zero_or_Positive + } + return z.a.sign +} + +rat_is_int :: proc(z: ^Rat) -> bool { + assert_if_nil(z) + return internal_is_zero(&z.a) || internal_int_equals_digit(&z.b, 1) +} + +rat_is_zero :: proc(z: ^Rat) -> bool { + return internal_rat_is_zero(z) +} +internal_rat_is_zero :: #force_inline proc(z: ^Rat) -> bool { + assert_if_nil(z) + return internal_is_zero(&z.a) +} + +internal_int_mul_denom :: proc(dst, x, y: ^Int, allocator := context.allocator) -> (err: Error) { + assert_if_nil(dst, x, y) + context.allocator = allocator + switch { + case internal_is_zero(x) && internal_is_zero(y): + return internal_set(dst, 1) + case internal_is_zero(x): + return internal_set(dst, y) + case internal_is_zero(y): + return internal_set(dst, x) + } + return int_mul(dst, x, y) +} + +internal_int_scale_denom :: proc(dst, x, y: ^Int, allocator := context.allocator) -> (err: Error) { + assert_if_nil(dst, x, y) + if internal_is_zero(y) { + return internal_set(dst, x) + } + int_mul(dst, x, y) or_return + dst.sign = x.sign + return +} + + +rat_add_rat :: proc(dst, x, y: ^Rat, allocator := context.allocator) -> (err: Error) { + assert_if_nil(dst, x, y) + context.allocator = allocator + + a1, a2: Int + defer internal_destroy(&a1, &a2) + + internal_int_scale_denom(&a1, &x.a, &y.b) or_return + internal_int_scale_denom(&a2, &y.a, &x.b) or_return + int_add(&dst.a, &a1, &a2) or_return + internal_int_mul_denom(&dst.b, &x.b, &y.b) or_return + return internal_rat_norm(dst) +} + +rat_sub_rat :: proc(dst, x, y: ^Rat, allocator := context.allocator) -> (err: Error) { + assert_if_nil(dst, x, y) + context.allocator = allocator + + a1, a2 := &Int{}, &Int{} + defer internal_destroy(a1, a2) + + internal_int_scale_denom(a1, &x.a, &y.b) or_return + internal_int_scale_denom(a2, &y.a, &x.b) or_return + int_sub(&dst.a, a1, a2) or_return + internal_int_mul_denom(&dst.b, &x.b, &y.b) or_return + return internal_rat_norm(dst) +} + +rat_mul_rat :: proc(dst, x, y: ^Rat, allocator := context.allocator) -> (err: Error) { + assert_if_nil(dst, x, y) + context.allocator = allocator + + if x == y { + internal_sqr(&dst.a, &x.a) or_return + if internal_is_zero(&x.b) { + internal_set(&dst.b, 1) or_return + } else { + internal_sqr(&dst.a, &x.b) or_return + } + return + } + + int_sub(&dst.a, &x.a, &y.a) or_return + internal_int_mul_denom(&dst.b, &x.b, &y.b) or_return + return internal_rat_norm(dst) +} + +rat_div_rat :: proc(dst, x, y: ^Rat, allocator := context.allocator) -> (err: Error) { + if internal_rat_is_zero(y) { + return .Division_by_Zero + } + context.allocator = allocator + + a, b := &Int{}, &Int{} + defer internal_destroy(a, b) + + internal_int_scale_denom(a, &x.a, &y.b) or_return + internal_int_scale_denom(b, &y.a, &x.b) or_return + internal_set(&dst.a, a) or_return + internal_set(&dst.b, b) or_return + internal_int_abs(&dst.a, &dst.a) + internal_int_abs(&dst.b, &dst.b) + dst.a.sign = .Negative if a.sign != b.sign else .Zero_or_Positive + return internal_rat_norm(dst) +} + + +rat_abs :: proc(dst, x: ^Rat, allocator := context.allocator) -> (err: Error) { + rat_set_rat(dst, x, allocator) or_return + internal_abs(&dst.a, &dst.a, allocator) or_return + return +} +rat_neg :: proc(dst, x: ^Rat, allocator := context.allocator) -> (err: Error) { + rat_set_rat(dst, x, allocator) or_return + internal_neg(&dst.a, &dst.a, allocator) or_return + return +} + + +rat_is_positive :: proc(z: ^Rat, allocator := context.allocator) -> (ok: bool, err: Error) { + assert_if_nil(z) + a := int_is_positive(&z.a, allocator) or_return + b := int_is_positive(&z.b, allocator) or_return + return !(a ~ b), nil +} +rat_is_negative :: proc(z: ^Rat, allocator := context.allocator) -> (ok: bool, err: Error) { + assert_if_nil(z) + a := int_is_positive(&z.a, allocator) or_return + b := int_is_positive(&z.b, allocator) or_return + return (a ~ b), nil +} + +rat_is_even :: proc(z: ^Rat, allocator := context.allocator) -> (ok: bool, err: Error) { + assert_if_nil(z) + if rat_is_int(z) { + return int_is_even(&z.a, allocator) + } + return false, nil +} +rat_is_odd :: proc(z: ^Rat, allocator := context.allocator) -> (ok: bool, err: Error) { + assert_if_nil(z) + if rat_is_int(z) { + return int_is_odd(&z.a, allocator) + } + return false, nil +} + +rat_to_f16 :: proc(z: ^Rat, allocator := context.allocator) -> (f: f16, exact: bool, err: Error) { + assert_if_nil(z) + return internal_rat_to_float(f16, z, allocator) +} +rat_to_f32 :: proc(z: ^Rat, allocator := context.allocator) -> (f: f32, exact: bool, err: Error) { + assert_if_nil(z) + return internal_rat_to_float(f32, z, allocator) +} +rat_to_f64 :: proc(z: ^Rat, allocator := context.allocator) -> (f: f64, exact: bool, err: Error) { + assert_if_nil(z) + return internal_rat_to_float(f64, z, allocator) +} + +internal_rat_to_float :: proc($T: typeid, z: ^Rat, allocator := context.allocator) -> (f: T, exact: bool, err: Error) where intrinsics.type_is_float(T) { + FSIZE :: 8*size_of(T) + when FSIZE == 16 { + MSIZE :: 10 + } else when FSIZE == 32 { + MSIZE :: 23 + } else when FSIZE == 64 { + MSIZE :: 52 + } else { + #panic("unsupported float type") + } + + MSIZE1 :: MSIZE+1 + MSIZE2 :: MSIZE+2 + + ESIZE :: FSIZE - MSIZE1 + EBIAS :: 1<<(ESIZE-1) - 1 + EMIN :: 1 - EBIAS + EMAX :: EBIAS + + assert_if_nil(z) + a, b := &z.a, &z.b + + context.allocator = allocator + + alen := internal_count_bits(a) + if alen == 0 { + return 0, true, nil + } + blen := internal_count_bits(b) + if blen == 0 { + return T(math.nan_f64()), false, .Division_by_Zero + } + + has_sign := a.sign != b.sign + defer if has_sign { + f = -builtin.abs(f) + } + + exp := alen - blen + a2, b2 := &Int{}, &Int{} + defer internal_destroy(a2, b2) + internal_int_abs(a2, a) or_return + internal_int_abs(b2, b) or_return + + if shift := MSIZE2 - exp; shift > 0 { + internal_int_shl_digit(a2, shift) or_return + } else { + internal_int_shl_digit(b2, -shift) or_return + } + + q, r := &Int{}, &Int{} + defer internal_destroy(q, r) + + internal_int_divmod(q, r, a2, b2) or_return + + has_rem := !internal_is_zero(r) + mantissa := internal_int_get_u64(q) or_return + + if mantissa>>MSIZE2 == 1 { + if mantissa&1 == 1 { + has_rem = true + } + mantissa >>= 1 + exp += 1 + } + + assert(mantissa>>MSIZE1 == 1, "invalid bit result") + + + if EMIN-MSIZE <= exp && exp <= EMIN { + shift := uint(EMIN - (exp - 1)) + lost_bits := mantissa & (1<>= shift + exp = 2 - EBIAS // exp + shift + } + + + exact = !has_rem + if mantissa&1 != 0 { + exact = false + if has_rem || mantissa&2 != 0 { + mantissa += 1 + if mantissa >= 1<>= 1 + exp += 1 + } + } + } + + mantissa >>= 1 + + f = T(math.ldexp(f64(mantissa), i32(exp-MSIZE1))) + if math.is_inf(f, 0) { + exact = false + } + return +} + + +rat_compare :: proc(x, y: ^Rat, allocator := context.allocator) -> (comparison: int, error: Error) { + assert_if_nil(x, y) + context.allocator = allocator + + a, b: Int + internal_init_multi(&a, &b) or_return + defer internal_destroy(&a, &b) + internal_int_scale_denom(&a, &x.a, &y.b) or_return + internal_int_scale_denom(&b, &y.a, &x.b) or_return + return int_compare(&a, &b) +} + + + +rat_add_int :: proc(dst, x: ^Rat, y: ^Int, allocator := context.allocator) -> (err: Error) { + assert_if_nil(dst, x) + assert_if_nil(y) + + z: Rat + rat_set_int(&z, y, allocator) or_return + defer internal_destroy(&z) + return rat_add_rat(dst, x, &z, allocator) +} + +rat_sub_int :: proc(dst, x: ^Rat, y: ^Int, allocator := context.allocator) -> (err: Error) { + assert_if_nil(dst, x) + assert_if_nil(y) + + z: Rat + rat_set_int(&z, y, allocator) or_return + defer internal_destroy(&z) + return rat_sub_rat(dst, x, &z, allocator) +} + +rat_mul_int :: proc(dst, x: ^Rat, y: ^Int, allocator := context.allocator) -> (err: Error) { + assert_if_nil(dst, x) + assert_if_nil(y) + + z: Rat + rat_set_int(&z, y, allocator) or_return + defer internal_destroy(&z) + return rat_mul_rat(dst, x, &z, allocator) +} + +rat_div_int :: proc(dst, x: ^Rat, y: ^Int, allocator := context.allocator) -> (err: Error) { + if internal_is_zero(y) { + return .Division_by_Zero + } + z: Rat + rat_set_int(&z, y, allocator) or_return + defer internal_destroy(&z) + return rat_div_rat(dst, x, &z, allocator) +} + + +int_add_rat :: proc(dst: ^Rat, x: ^Int, y: ^Rat, allocator := context.allocator) -> (err: Error) { + assert_if_nil(x) + assert_if_nil(dst, y) + + w: Rat + rat_set_int(&w, x, allocator) or_return + defer internal_destroy(&w) + return rat_add_rat(dst, &w, y, allocator) +} + +int_sub_rat :: proc(dst: ^Rat, x: ^Int, y: ^Rat, allocator := context.allocator) -> (err: Error) { + assert_if_nil(x) + assert_if_nil(dst, y) + + w: Rat + rat_set_int(&w, x, allocator) or_return + defer internal_destroy(&w) + return rat_sub_rat(dst, &w, y, allocator) +} + +int_mul_rat :: proc(dst: ^Rat, x: ^Int, y: ^Rat, allocator := context.allocator) -> (err: Error) { + assert_if_nil(x) + assert_if_nil(dst, y) + + w: Rat + rat_set_int(&w, x, allocator) or_return + defer internal_destroy(&w) + return rat_mul_rat(dst, &w, y, allocator) +} + +int_div_rat :: proc(dst: ^Rat, x: ^Int, y: ^Rat, allocator := context.allocator) -> (err: Error) { + if internal_is_zero(y) { + return .Division_by_Zero + } + w: Rat + rat_set_int(&w, x, allocator) or_return + defer internal_destroy(&w) + return rat_div_rat(dst, &w, y, allocator) +} \ No newline at end of file