Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Fable.Cli/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Fixed

* [Python] Add `Math.DivRem` support for int, int64, and bigint (by @dbrattli)
* [Python] Fix modulo with negative numbers using Python floored semantics instead of .NET truncated semantics for bigint (fixes #4462) (by @dbrattli)
* [Beam] Fix `System.String.Concat` with 4+ arguments not being supported (by @dbrattli)
* [TS/Python] Fix invalid `this` argument type in structs (#4453) (by @ncave)
Expand Down
1 change: 1 addition & 0 deletions src/Fable.Compiler/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Fixed

* [Python] Add `Math.DivRem` support for int, int64, and bigint (by @dbrattli)
* [Python] Fix modulo with negative numbers using Python floored semantics instead of .NET truncated semantics for bigint (fixes #4462) (by @dbrattli)
* [Beam] Fix `System.String.Concat` with 4+ arguments not being supported (by @dbrattli)
* [TS/Python] Fix invalid `this` argument type in structs (#4453) (by @ncave)
Expand Down
12 changes: 11 additions & 1 deletion src/Fable.Transforms/Python/Replacements.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1267,6 +1267,16 @@ let operators (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr o
| _ ->
Helper.ImportedCall("math", "trunc", t, args, i.SignatureArgTypes, ?loc = r)
|> Some
| "DivRem", _ ->
let modName =
match i.SignatureArgTypes with
| Number(BigInt, _) :: _ -> "big_int"
| Number(Int64, _) :: _
| Number(UInt64, _) :: _ -> "long"
| _ -> "int32"

Helper.LibCall(com, modName, "div_rem", t, args, i.SignatureArgTypes, ?loc = r)
|> Some
| "Sign", _ ->
match args with
| ExprType(Number(Decimal, _)) :: _ ->
Expand Down Expand Up @@ -2412,7 +2422,7 @@ let bigints (com: ICompiler) (ctx: Context) r (t: Type) (i: CallInfo) (thisArg:
Helper.LibCall(com, "BigInt", methodName, t, args, i.SignatureArgTypes, ?loc = r)
|> Some
| None, "DivRem" ->
Helper.LibCall(com, "big_int", "divRem", t, args, i.SignatureArgTypes, ?loc = r)
Helper.LibCall(com, "big_int", "div_rem", t, args, i.SignatureArgTypes, ?loc = r)
|> Some
| None, meth when meth.StartsWith("get_", StringComparison.Ordinal) ->
Helper.LibValue(com, "big_int", meth, t) |> Some
Expand Down
22 changes: 21 additions & 1 deletion src/fable-library-py/fable_library/big_int.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from decimal import Decimal
from typing import Any, SupportsInt
from typing import Any, SupportsInt, overload

from .core import FSharpRef

Expand Down Expand Up @@ -89,6 +89,25 @@ def op_modulus(a: int, b: int) -> int:
return r


@overload
def div_rem(x: int, y: int) -> tuple[int, int]: ...


@overload
def div_rem(x: int, y: int, out: FSharpRef[int]) -> int: ...


def div_rem(x: int, y: int, out: FSharpRef[int] | None = None) -> int | tuple[int, int]:
# .NET uses truncated division; Python // floors toward -inf.
# Adjust when signs differ to get truncation toward zero.
q = -((-x) // y) if (x < 0) != (y < 0) else x // y
r = x - q * y
if out is None:
return (q, r)
out.contents = r
return q


def op_right_shift(a: int, num_bits: int) -> int:
return a >> num_bits

Expand Down Expand Up @@ -272,6 +291,7 @@ def try_parse(string: str, def_value: FSharpRef[int]) -> bool:
"abs",
"add",
"compare",
"div_rem",
"divide",
"equals",
"from_int32",
Expand Down
23 changes: 22 additions & 1 deletion src/fable-library-py/fable_library/int32.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
from .core import int8, int16, int32
from typing import SupportsInt, overload

from .core import FSharpRef, int8, int16, int32
from .core._core import get_range
from .core._core import parse_int32 as parse
from .core._core import try_parse_int32 as try_parse
Expand All @@ -12,6 +14,24 @@ def sign(x: int32) -> int32:
return int32.NEG_ONE if x < int32.ZERO else int32.ONE if x > int32.ZERO else int32.ZERO


@overload
def div_rem[T: SupportsInt](x: T, y: T) -> tuple[T, T]: ...


@overload
def div_rem[T: SupportsInt](x: T, y: T, out: FSharpRef[T]) -> T: ...


def div_rem[T: SupportsInt](x: T, y: T, out: FSharpRef[T] | None = None) -> T | tuple[T, T]:
# Rust wrapper types already use truncated division and remainder
q = x // y # type: ignore[operator]
r = x % y # type: ignore[operator]
if out is None:
return (q, r)
out.contents = r
return q


def op_unary_negation_int8(x: int8) -> int8:
return -x

Expand All @@ -26,6 +46,7 @@ def op_unary_negation_int32(x: int32) -> int32:

__all__ = [
"AllowHexSpecifier",
"div_rem",
"get_range",
"op_unary_negation_int8",
"op_unary_negation_int16",
Expand Down
21 changes: 20 additions & 1 deletion src/fable-library-py/fable_library/long.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from typing import Any, Literal, SupportsFloat, SupportsInt, overload

from .core import float64, int32, int64, uint64
from .core import FSharpRef, float64, int32, int64, uint64
from .core._core import get_range_64 as get_range
from .core._core import parse_int64 as parse
from .core._core import try_parse_int64 as try_parse
Expand Down Expand Up @@ -127,6 +127,24 @@ def op_modulus(a: int64 | uint64, b: int64 | uint64) -> int64 | uint64:
return a % b


@overload
def div_rem(x: int64, y: int64) -> tuple[int64, int64]: ...


@overload
def div_rem(x: int64, y: int64, out: FSharpRef[int64]) -> int64: ...


def div_rem(x: int64, y: int64, out: FSharpRef[int64] | None = None) -> int64 | tuple[int64, int64]:
# Rust wrapper types already use truncated division and remainder
q = x // y
r = x % y
if out is None:
return (q, r)
out.contents = r
return q


@overload
def op_right_shift(a: int64, b: int32) -> int64: ...

Expand Down Expand Up @@ -278,6 +296,7 @@ def to_string(x: int64 | uint64) -> str:

__all__ = [
"compare",
"div_rem",
"from_bits",
"from_int",
"from_integer",
Expand Down
62 changes: 31 additions & 31 deletions tests/Python/TestArithmetic.fs
Original file line number Diff line number Diff line change
Expand Up @@ -66,36 +66,36 @@ let ``test Infix modulo with negative numbers`` () =
5 % -3 |> equal 2
-5 % -3 |> equal -2

// [<Fact>]
// let ``test Math.DivRem works with bytes`` () =
// Math.DivRem(5y, 2y) |> equal struct (2y, 1y)
// Math.DivRem(4y, 2y) |> equal struct (2y, 0y)
[<Fact>]
let ``test Math.DivRem works with bytes`` () =
Math.DivRem(5y, 2y) |> equal struct (2y, 1y)
Math.DivRem(4y, 2y) |> equal struct (2y, 0y)

// [<Fact>]
// let ``test Math.DivRem works with ints`` () =
// Math.DivRem(5, 2) |> equal struct (2, 1)
// Math.DivRem(4, 2) |> equal struct (2, 0)
[<Fact>]
let ``test Math.DivRem works with ints`` () =
Math.DivRem(5, 2) |> equal struct (2, 1)
Math.DivRem(4, 2) |> equal struct (2, 0)

// [<Fact>]
// let ``test Math.DivRem works with longs`` () =
// Math.DivRem(5L, 2L) |> equal struct (2L, 1L)
// Math.DivRem(4L, 2L) |> equal struct (2L, 0L)
[<Fact>]
let ``test Math.DivRem works with longs`` () =
Math.DivRem(5L, 2L) |> equal struct (2L, 1L)
Math.DivRem(4L, 2L) |> equal struct (2L, 0L)

// [<Fact>]
// let ``test Math.DivRem works with ints and outref`` () =
// let mutable rem = -1
// Math.DivRem(5, 2, &rem) |> equal 2
// rem |> equal 1
// Math.DivRem(4, 2, &rem) |> equal 2
// rem |> equal 0
[<Fact>]
let ``test Math.DivRem works with ints and outref`` () =
let mutable rem = -1
Math.DivRem(5, 2, &rem) |> equal 2
rem |> equal 1
Math.DivRem(4, 2, &rem) |> equal 2
rem |> equal 0

// [<Fact>]
// let ``test Math.DivRem works with longs and outref`` () =
// let mutable rem = -1L
// Math.DivRem(5L, 2L, &rem) |> equal 2L
// rem |> equal 1L
// Math.DivRem(4L, 2L, &rem) |> equal 2L
// rem |> equal 0L
[<Fact>]
let ``test Math.DivRem works with longs and outref`` () =
let mutable rem = -1L
Math.DivRem(5L, 2L, &rem) |> equal 2L
rem |> equal 1L
Math.DivRem(4L, 2L, &rem) |> equal 2L
rem |> equal 0L

[<Fact>]
let ``test Evaluation order is preserved by generated code`` () =
Expand Down Expand Up @@ -464,11 +464,11 @@ let ``test BigInt Infix modulo with negative numbers`` () =
5I % -3I |> equal 2I
-5I % -3I |> equal -2I

// [<Fact>]
// let ``test BigInt.DivRem works`` () = // See #1744
// let quotient,remainder = bigint.DivRem(5I,2I)
// 2I |> equal quotient
// 1I |> equal remainder
[<Fact>]
let ``test BigInt.DivRem works`` () = // See #1744
let quotient,remainder = bigint.DivRem(5I,2I)
2I |> equal quotient
1I |> equal remainder

[<Fact>]
let ``test BigInt Evaluation order is preserved by generated code`` () =
Expand Down
Loading