From e38279e433ca8822bc2e41f3275c38965ad6626f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Bene=C5=A1?= Date: Sat, 5 Apr 2025 16:02:32 +0200 Subject: [PATCH 1/6] Stream: add 'iterate' --- libraries/common/stream.effekt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/libraries/common/stream.effekt b/libraries/common/stream.effekt index 72b32c6ee..596770c25 100644 --- a/libraries/common/stream.effekt +++ b/libraries/common/stream.effekt @@ -173,6 +173,16 @@ def replicate[A](number: Int) { action: () => A }: Unit / emit[A] = replicate(number - 1) {action} } +/// Creates an infinite iterated stream given by an `initial` seed and a `step` function: +/// iterate(a){f} ~> a, f(a), f(f(a)), f(f(f(a))), ... +def iterate[A](initial: A) { step: A => A }: Unit / emit[A] = { + var current = initial + while (true) { + do emit(current) + current = step(current) + } +} + def sum { stream : () => Unit / emit[Int] }: Int = returning::sum[Unit]{stream}.second From 7a927768194c54aefa3fe228c29de375e3d56225 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Bene=C5=A1?= Date: Sat, 5 Apr 2025 16:02:51 +0200 Subject: [PATCH 2/6] Stream: add 'product' --- libraries/common/stream.effekt | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/libraries/common/stream.effekt b/libraries/common/stream.effekt index 596770c25..dc26eefdf 100644 --- a/libraries/common/stream.effekt +++ b/libraries/common/stream.effekt @@ -184,9 +184,12 @@ def iterate[A](initial: A) { step: A => A }: Unit / emit[A] = { } -def sum { stream : () => Unit / emit[Int] }: Int = +def sum { stream: () => Unit / emit[Int] }: Int = returning::sum[Unit]{stream}.second +def product { stream: () => Unit / emit[Int] }: Int = + returning::product[Unit]{stream}.second + def collectList[A] { stream: () => Unit / emit[A] }: List[A] = returning::collectList[A, Unit]{stream}.second @@ -471,7 +474,7 @@ def limit[A, R](number: Int) { stream: () => R / emit[A] }: R / { emit[A], stop } } -def sum[R] { stream : () => R / emit[Int] }: (R, Int) = { +def sum[R] { stream: () => R / emit[Int] }: (R, Int) = { var s = 0; try { (stream(), s) @@ -481,6 +484,16 @@ def sum[R] { stream : () => R / emit[Int] }: (R, Int) = { } } +def product[R] { stream: () => R / emit[Int] }: (R, Int) = { + var s = 1; + try { + (stream(), s) + } with emit[Int] { v => + s = s * v; + resume(()) + } +} + def collectList[A, R] { stream: () => R / emit[A] }: (R, List[A]) = try { (stream(), Nil()) From 316cb8210463f9fd36fdb861acea4512069d4c5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Bene=C5=A1?= Date: Sat, 5 Apr 2025 16:03:15 +0200 Subject: [PATCH 3/6] Stream: add 'fastexp' example --- examples/stdlib/stream/fastexp.check | 46 ++++++++++++++++ examples/stdlib/stream/fastexp.effekt | 79 +++++++++++++++++++++++++++ 2 files changed, 125 insertions(+) create mode 100644 examples/stdlib/stream/fastexp.check create mode 100644 examples/stdlib/stream/fastexp.effekt diff --git a/examples/stdlib/stream/fastexp.check b/examples/stdlib/stream/fastexp.check new file mode 100644 index 000000000..1e36e7a53 --- /dev/null +++ b/examples/stdlib/stream/fastexp.check @@ -0,0 +1,46 @@ +0 +0b0 +0b0 +1 +0b1 +0b1 +2 +0b01 +0b10 +3 +0b11 +0b11 +4 +0b001 +0b100 +123456789 +0b101010001011001111011010111 +0b111010110111100110100010101 + +2 ^ 0 +1 +1 +2 ^ 5 +32 +32 +2 ^ 20 +1048576 +1048576 +10 ^ 10 +10000000000 +10000000000 +0 ^ 1 +0 +0 +1 ^ 0 +1 +1 +0 ^ 0 +1 +1 +7 ^ 22 +3909821048582988300 +3909821048582988300 +22 ^ 7 +2494357888 +2494357888 \ No newline at end of file diff --git a/examples/stdlib/stream/fastexp.effekt b/examples/stdlib/stream/fastexp.effekt new file mode 100644 index 000000000..68d5c4896 --- /dev/null +++ b/examples/stdlib/stream/fastexp.effekt @@ -0,0 +1,79 @@ +import stream + +/// Least significant bits +def eachLSB(n: Int): Unit / emit[Bool] = { + if (n <= 0) { + do emit(false) + } else { + var tmp = n + while (tmp > 0) { + do emit(if (tmp.mod(2) == 1) true else false) + tmp = tmp / 2 + } + } +} + +/// Most significant bits +def eachMSB(n: Int): Unit / emit[Bool] = { + if (n <= 0) { + do emit(false) + } else { + // First, calculate the highest bit position + var highest = 0 + var m = n + while (m > 0) { + highest = highest + 1 + m = m / 2 + } + + // Then emit from MSB to LSB + for[Int] { range(0, highest) } { i => + val index = highest - i + val bit = n.bitwiseShr(index - 1).mod(2) == 1 + do emit(bit) + } + } +} + +def fastexp(n: Int, k: Int) = product { + stream::zip[Int, Bool] {n.iterate { x => x * x }} {k.eachLSB} { + case res, true => do emit(res) + case res, false => () + } +} + +def main() = { + def prettyBits(bits: List[Bool]): String = + "0b" ++ bits.map { b => if (b) "1" else "0"}.join("") + + def testBits(n: Int) = { + println(n) + println(collectList[Bool] {n.eachLSB}.prettyBits) + println(collectList[Bool] {n.eachMSB}.prettyBits) + } + + testBits(0) + testBits(1) + testBits(2) + testBits(3) + testBits(4) + testBits(123456789) + + println("") + + def testExp(n: Int, k: Int) = { + println(show(n) ++ " ^ " ++ show(k)) + println(n.toDouble.pow(k)) + println(n.fastexp(k)) + } + + testExp(2, 0) + testExp(2, 5) + testExp(2, 20) + testExp(10, 10) + testExp(0, 1) + testExp(1, 0) + testExp(0, 0) + testExp(7, 22) + testExp(22, 7) +} From dbd612aa1dd74b70e0d2e427b4c91011d4ba80ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Bene=C5=A1?= Date: Sat, 5 Apr 2025 16:14:12 +0200 Subject: [PATCH 4/6] Use smaller numbers for tests --- examples/stdlib/stream/fastexp.check | 12 ++++++------ examples/stdlib/stream/fastexp.effekt | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/stdlib/stream/fastexp.check b/examples/stdlib/stream/fastexp.check index 1e36e7a53..50086e64b 100644 --- a/examples/stdlib/stream/fastexp.check +++ b/examples/stdlib/stream/fastexp.check @@ -38,9 +38,9 @@ 0 ^ 0 1 1 -7 ^ 22 -3909821048582988300 -3909821048582988300 -22 ^ 7 -2494357888 -2494357888 \ No newline at end of file +7 ^ 21 +558545864083284030 +558545864083284030 +21 ^ 7 +1801088541 +1801088541 \ No newline at end of file diff --git a/examples/stdlib/stream/fastexp.effekt b/examples/stdlib/stream/fastexp.effekt index 68d5c4896..47bd1566b 100644 --- a/examples/stdlib/stream/fastexp.effekt +++ b/examples/stdlib/stream/fastexp.effekt @@ -74,6 +74,6 @@ def main() = { testExp(0, 1) testExp(1, 0) testExp(0, 0) - testExp(7, 22) - testExp(22, 7) + testExp(7, 21) + testExp(21, 7) } From edc3055b719a682c51f455535d425f894196a30a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Bene=C5=A1?= Date: Sat, 5 Apr 2025 16:18:16 +0200 Subject: [PATCH 5/6] Use even smaller numbers for tests --- examples/stdlib/stream/fastexp.check | 18 ++++++++++++------ examples/stdlib/stream/fastexp.effekt | 6 ++++-- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/examples/stdlib/stream/fastexp.check b/examples/stdlib/stream/fastexp.check index 50086e64b..44d2d4cc8 100644 --- a/examples/stdlib/stream/fastexp.check +++ b/examples/stdlib/stream/fastexp.check @@ -38,9 +38,15 @@ 0 ^ 0 1 1 -7 ^ 21 -558545864083284030 -558545864083284030 -21 ^ 7 -1801088541 -1801088541 \ No newline at end of file +200 ^ 2 +40000 +40000 +128 ^ 4 +268435456 +268435456 +7 ^ 13 +96889010407 +96889010407 +13 ^ 7 +62748517 +62748517 \ No newline at end of file diff --git a/examples/stdlib/stream/fastexp.effekt b/examples/stdlib/stream/fastexp.effekt index 47bd1566b..10caa85fe 100644 --- a/examples/stdlib/stream/fastexp.effekt +++ b/examples/stdlib/stream/fastexp.effekt @@ -74,6 +74,8 @@ def main() = { testExp(0, 1) testExp(1, 0) testExp(0, 0) - testExp(7, 21) - testExp(21, 7) + testExp(200, 2) + testExp(128, 4) + testExp(7, 13) + testExp(13, 7) } From 8bb7d80f4dd0a364f42a827b1abdbcb491d3c676 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Bene=C5=A1?= Date: Sat, 5 Apr 2025 16:29:05 +0200 Subject: [PATCH 6/6] Use 'pow' from #923 --- examples/stdlib/stream/fastexp.effekt | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/examples/stdlib/stream/fastexp.effekt b/examples/stdlib/stream/fastexp.effekt index 10caa85fe..f7d36ece8 100644 --- a/examples/stdlib/stream/fastexp.effekt +++ b/examples/stdlib/stream/fastexp.effekt @@ -43,6 +43,20 @@ def fastexp(n: Int, k: Int) = product { } def main() = { + /// Computes n^exp for integers. (from #923!) + def pow(n: Int, exp: Int): Int = { + def go(n: Int, exp: Int, acc: Int): Int = { + if (exp == 0) { + acc + } else if (mod(exp, 2) == 0) { + go(n * n, exp / 2, acc) + } else { + go(n * n, exp / 2, acc * n) + } + } + go(n, exp, 1) + } + def prettyBits(bits: List[Bool]): String = "0b" ++ bits.map { b => if (b) "1" else "0"}.join("") @@ -63,7 +77,7 @@ def main() = { def testExp(n: Int, k: Int) = { println(show(n) ++ " ^ " ++ show(k)) - println(n.toDouble.pow(k)) + println(n.pow(k)) println(n.fastexp(k)) }