summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorRyan McConnell <rammcconnell@gmail.com>2023-06-16 23:42:05 -0400
committerGitHub <noreply@github.com>2023-06-17 05:42:05 +0200
commit647d9611ae81c7af2c86af5b33a14787254896b5 (patch)
tree199c5d1194e0b97736f7af7665cbf900b57d1db8
parente8d0f1c3aedd8290cc972e3d06f642e88dbe783e (diff)
downloadNim-647d9611ae81c7af2c86af5b33a14787254896b5.tar.gz
Add divmod (#22102)
* Adding divmod

* Adding support to VM

* Wrapped C structs and funcs

* Fix javascript impl

* Fixing struct compat

* Segregate tests, better compiletime defs

* Using `inline` and switch back to `func`

* Apply suggestions from code review

* Explicit structures

---------

Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
-rw-r--r--compiler/vmhooks.nim7
-rw-r--r--compiler/vmops.nim8
-rw-r--r--lib/pure/math.nim38
-rw-r--r--tests/stdlib/tmath.nim10
4 files changed, 61 insertions, 2 deletions
diff --git a/compiler/vmhooks.nim b/compiler/vmhooks.nim
index 84ecf586f..1741574b8 100644
--- a/compiler/vmhooks.nim
+++ b/compiler/vmhooks.nim
@@ -39,6 +39,13 @@ proc setResult*(a: VmArgs; v: seq[string]) =
   for x in v: n.add newStrNode(nkStrLit, x)
   a.slots[a.ra].node = n
 
+proc setResult*(a: VmArgs; v: (BiggestInt, BiggestInt)) =
+  a.slots[a.ra].ensureKind(rkNode)
+  var tuplen = newNode(nkTupleConstr)
+  tuplen.add newIntNode(nkIntLit, v[0])
+  tuplen.add newIntNode(nkIntLit, v[1]) 
+  a.slots[a.ra].node = tuplen
+
 template getReg(a, i): untyped =
   doAssert i < a.rc-1
   a.slots[i+a.rb+1].unsafeAddr
diff --git a/compiler/vmops.nim b/compiler/vmops.nim
index cf20f1825..8fb1ae901 100644
--- a/compiler/vmops.nim
+++ b/compiler/vmops.nim
@@ -12,7 +12,7 @@
 from std/math import sqrt, ln, log10, log2, exp, round, arccos, arcsin,
   arctan, arctan2, cos, cosh, hypot, sinh, sin, tan, tanh, pow, trunc,
   floor, ceil, `mod`, cbrt, arcsinh, arccosh, arctanh, erf, erfc, gamma,
-  lgamma
+  lgamma, divmod
 from std/sequtils import toSeq
 when declared(math.copySign):
   # pending bug #18762, avoid renaming math
@@ -80,6 +80,11 @@ template wrap2fMath(op) {.dirty.} =
     setResult(a, op(getFloat(a, 0), getFloat(a, 1)))
   mathop op
 
+template wrap2iMath(op) {.dirty.} =
+  proc `op Wrapper`(a: VmArgs) {.nimcall.} =
+    setResult(a, op(getInt(a, 0), getInt(a, 1)))
+  mathop op
+
 template wrap0(op, modop) {.dirty.} =
   proc `op Wrapper`(a: VmArgs) {.nimcall.} =
     setResult(a, op())
@@ -224,6 +229,7 @@ proc registerAdditionalOps*(c: PCtx) =
   wrap1fMath(erfc)
   wrap1fMath(gamma)
   wrap1fMath(lgamma)
+  wrap2iMath(divmod)
 
   when declared(copySign):
     wrap2fMath(copySign)
diff --git a/lib/pure/math.nim b/lib/pure/math.nim
index fc45a6641..7ed84fa71 100644
--- a/lib/pure/math.nim
+++ b/lib/pure/math.nim
@@ -77,6 +77,36 @@ when defined(c) or defined(cpp):
       importc: "frexpf", header: "<math.h>".}
   func c_frexp2(x: cdouble, exponent: var cint): cdouble {.
       importc: "frexp", header: "<math.h>".}
+  
+  type
+    div_t {.importc, header: "<stdlib.h>".} = object
+      quot: cint
+      rem: cint
+    ldiv_t {.importc, header: "<stdlib.h>".} = object
+      quot: clong
+      rem: clong
+    lldiv_t {.importc, header: "<stdlib.h>".} = object
+      quot: clonglong
+      rem: clonglong
+  
+  when cint isnot clong:
+    func divmod_c(x, y: cint): div_t {.importc: "div", header: "<stdlib.h>".}
+  when clong isnot clonglong:
+    func divmod_c(x, y: clonglong): lldiv_t {.importc: "lldiv", header: "<stdlib.h>".}
+  func divmod_c(x, y: clong): ldiv_t {.importc: "ldiv", header: "<stdlib.h>".}
+  func divmod*[T: SomeInteger](x, y: T): (T, T) {.inline.} = 
+    ## Specialized instructions for computing both division and modulus.
+    ## Return structure is: (quotient, remainder)
+    runnableExamples:
+      doAssert divmod(5, 2) == (2, 1)
+      doAssert divmod(5, -3) == (-1, 2)
+    when T is cint | clong | clonglong:
+      let res = divmod_c(x, y)
+      result[0] = res.quot
+      result[1] = res.rem
+    else:
+      result[0] = x div y
+      result[1] = x mod y
 
 func binom*(n, k: int): int =
   ## Computes the [binomial coefficient](https://en.wikipedia.org/wiki/Binomial_coefficient).
@@ -793,6 +823,14 @@ else: # JS
       doAssert -6.5 mod  2.5 == -1.5
       doAssert  6.5 mod -2.5 ==  1.5
       doAssert -6.5 mod -2.5 == -1.5
+  
+  func divmod*(num, denom: int): (int, int) = 
+    runnableExamples:
+      doAssert  divmod(5, 2) ==  (2, 1)
+      doAssert divmod(5, -3) == (-1, 2)
+    result[0] = num div denom
+    result[1] = num mod denom
+  
 
 func round*[T: float32|float64](x: T, places: int): T =
   ## Decimal rounding on a binary floating point number.
diff --git a/tests/stdlib/tmath.nim b/tests/stdlib/tmath.nim
index 2076a6eff..3fcc42ae8 100644
--- a/tests/stdlib/tmath.nim
+++ b/tests/stdlib/tmath.nim
@@ -186,7 +186,15 @@ template main() =
     when not defined(nimTmathCase2):
       doAssert classify(trunc(f_nan.float32)) == fcNan
     doAssert classify(trunc(0.0'f32)) == fcZero
-
+  
+  block: # divmod
+    doAssert divmod(int.high, 1) == (int.high, 0)
+    doAssert divmod(-1073741823, 17) == (-63161283, -12)
+    when not defined(js):
+      doAssert divmod(int32.high, 1.int32) == (int32.high, 0.int32)
+      doAssert divmod(1073741823.int32, 5.int32) == (214748364.int32, 3.int32)
+      doAssert divmod(4611686018427387903.int64, 5.int64) == (922337203685477580.int64, 3.int64)
+  
   block: # log
     doAssert log(4.0, 3.0) ==~ ln(4.0) / ln(3.0)
     doAssert log2(8.0'f64) == 3.0'f64