diff --git a/test/perf/ok/sha224.drun-run-opt.ok b/test/perf/ok/sha224.drun-run-opt.ok new file mode 100644 index 00000000000..e2fab5497ac --- /dev/null +++ b/test/perf/ok/sha224.drun-run-opt.ok @@ -0,0 +1,4 @@ +ingress Completed: Reply: 0x4449444c016c01b3c4b1f204680100010a00000000000000000101 +ingress Completed: Reply: 0x4449444c0000 +debug.print: (+5_767_028, 393_806_202) +ingress Completed: Reply: 0x4449444c0000 diff --git a/test/perf/ok/sha224.drun-run.ok b/test/perf/ok/sha224.drun-run.ok new file mode 100644 index 00000000000..5af0b9acc99 --- /dev/null +++ b/test/perf/ok/sha224.drun-run.ok @@ -0,0 +1,4 @@ +ingress Completed: Reply: 0x4449444c016c01b3c4b1f204680100010a00000000000000000101 +ingress Completed: Reply: 0x4449444c0000 +debug.print: (+5_767_028, 450_179_112) +ingress Completed: Reply: 0x4449444c0000 diff --git a/test/perf/ok/sha224.tc.ok b/test/perf/ok/sha224.tc.ok new file mode 100644 index 00000000000..822ffa0b6b4 --- /dev/null +++ b/test/perf/ok/sha224.tc.ok @@ -0,0 +1,2 @@ +sha224/Array.mo:864.40-864.54: warning [M0155], operator may trap for inferred type + Nat diff --git a/test/perf/sha224.mo b/test/perf/sha224.mo new file mode 100644 index 00000000000..b462c7ad63e --- /dev/null +++ b/test/perf/sha224.mo @@ -0,0 +1,950 @@ +import Blob "./sha224/Blob"; +import Array "./sha224/Array"; +import Nat8 "./sha224/Nat8"; +import Nat32 "./sha224/Nat32"; +import Nat64 "./sha224/Nat64"; +import { Array_tabulate; performanceCounter; debugPrint; rts_heap_size } = "mo:⛔"; + +actor { + + func counters() : (Int, Nat64) = (rts_heap_size(), performanceCounter(0)); + + public func go() : async () { + let (m0, n0) = counters(); + let sha224 = SHA224(); + let data = Array.tabulate(1024*1024, Nat8.fromIntWrap); + sha224.writeArray(data); + let hashSum = sha224.sum(); + let (m1, n1) = counters(); + debugPrint(debug_show (m1 - m0, n1 - n0)); + }; + + + /** + * SHA224 Utilities used in toAccount(). + * Utilities are not exposed as public functions. + * Taken with permission from https://github.com/research-ag/sha2 + **/ + let K00 : Nat32 = 0x428a2f98; + let K01 : Nat32 = 0x71374491; + let K02 : Nat32 = 0xb5c0fbcf; + let K03 : Nat32 = 0xe9b5dba5; + let K04 : Nat32 = 0x3956c25b; + let K05 : Nat32 = 0x59f111f1; + let K06 : Nat32 = 0x923f82a4; + let K07 : Nat32 = 0xab1c5ed5; + let K08 : Nat32 = 0xd807aa98; + let K09 : Nat32 = 0x12835b01; + let K10 : Nat32 = 0x243185be; + let K11 : Nat32 = 0x550c7dc3; + let K12 : Nat32 = 0x72be5d74; + let K13 : Nat32 = 0x80deb1fe; + let K14 : Nat32 = 0x9bdc06a7; + let K15 : Nat32 = 0xc19bf174; + let K16 : Nat32 = 0xe49b69c1; + let K17 : Nat32 = 0xefbe4786; + let K18 : Nat32 = 0x0fc19dc6; + let K19 : Nat32 = 0x240ca1cc; + let K20 : Nat32 = 0x2de92c6f; + let K21 : Nat32 = 0x4a7484aa; + let K22 : Nat32 = 0x5cb0a9dc; + let K23 : Nat32 = 0x76f988da; + let K24 : Nat32 = 0x983e5152; + let K25 : Nat32 = 0xa831c66d; + let K26 : Nat32 = 0xb00327c8; + let K27 : Nat32 = 0xbf597fc7; + let K28 : Nat32 = 0xc6e00bf3; + let K29 : Nat32 = 0xd5a79147; + let K30 : Nat32 = 0x06ca6351; + let K31 : Nat32 = 0x14292967; + let K32 : Nat32 = 0x27b70a85; + let K33 : Nat32 = 0x2e1b2138; + let K34 : Nat32 = 0x4d2c6dfc; + let K35 : Nat32 = 0x53380d13; + let K36 : Nat32 = 0x650a7354; + let K37 : Nat32 = 0x766a0abb; + let K38 : Nat32 = 0x81c2c92e; + let K39 : Nat32 = 0x92722c85; + let K40 : Nat32 = 0xa2bfe8a1; + let K41 : Nat32 = 0xa81a664b; + let K42 : Nat32 = 0xc24b8b70; + let K43 : Nat32 = 0xc76c51a3; + let K44 : Nat32 = 0xd192e819; + let K45 : Nat32 = 0xd6990624; + let K46 : Nat32 = 0xf40e3585; + let K47 : Nat32 = 0x106aa070; + let K48 : Nat32 = 0x19a4c116; + let K49 : Nat32 = 0x1e376c08; + let K50 : Nat32 = 0x2748774c; + let K51 : Nat32 = 0x34b0bcb5; + let K52 : Nat32 = 0x391c0cb3; + let K53 : Nat32 = 0x4ed8aa4a; + let K54 : Nat32 = 0x5b9cca4f; + let K55 : Nat32 = 0x682e6ff3; + let K56 : Nat32 = 0x748f82ee; + let K57 : Nat32 = 0x78a5636f; + let K58 : Nat32 = 0x84c87814; + let K59 : Nat32 = 0x8cc70208; + let K60 : Nat32 = 0x90befffa; + let K61 : Nat32 = 0xa4506ceb; + let K62 : Nat32 = 0xbef9a3f7; + let K63 : Nat32 = 0xc67178f2; + + let ivs : [[Nat32]] = [ + [ + // 224 + 0xc1059ed8, + 0x367cd507, + 0x3070dd17, + 0xf70e5939, + 0xffc00b31, + 0x68581511, + 0x64f98fa7, + 0xbefa4fa4 + ], + [ + // 256 + 0x6a09e667, + 0xbb67ae85, + 0x3c6ef372, + 0xa54ff53a, + 0x510e527f, + 0x9b05688c, + 0x1f83d9ab, + 0x5be0cd19 + ] + ]; + + let rot = Nat32.bitrotRight; + + class SHA224() { + let (sum_bytes, iv) = (28, 0); + + var s0 : Nat32 = 0; + var s1 : Nat32 = 0; + var s2 : Nat32 = 0; + var s3 : Nat32 = 0; + var s4 : Nat32 = 0; + var s5 : Nat32 = 0; + var s6 : Nat32 = 0; + var s7 : Nat32 = 0; + + let msg : [var Nat32] = Array.init(16, 0); + let digest = Array.init(sum_bytes, 0); + var word : Nat32 = 0; + + var i_msg : Nat8 = 0; + var i_byte : Nat8 = 4; + var i_block : Nat64 = 0; + + public func reset() { + i_msg := 0; + i_byte := 4; + i_block := 0; + s0 := ivs[iv][0]; + s1 := ivs[iv][1]; + s2 := ivs[iv][2]; + s3 := ivs[iv][3]; + s4 := ivs[iv][4]; + s5 := ivs[iv][5]; + s6 := ivs[iv][6]; + s7 := ivs[iv][7] + }; + + reset(); + + private func writeByte(val : Nat8) : () { + word := (word << 8) ^ Nat32.fromIntWrap(Nat8.toNat(val)); + i_byte -%= 1; + if (i_byte == 0) { + msg[Nat8.toNat(i_msg)] := word; + word := 0; + i_byte := 4; + i_msg +%= 1; + if (i_msg == 16) { + process_block(); + i_msg := 0; + i_block +%= 1 + } + } + }; + + private func process_block() : () { + let w00 = msg[0]; + let w01 = msg[1]; + let w02 = msg[2]; + let w03 = msg[3]; + let w04 = msg[4]; + let w05 = msg[5]; + let w06 = msg[6]; + let w07 = msg[7]; + let w08 = msg[8]; + let w09 = msg[9]; + let w10 = msg[10]; + let w11 = msg[11]; + let w12 = msg[12]; + let w13 = msg[13]; + let w14 = msg[14]; + let w15 = msg[15]; + let w16 = w00 +% rot(w01, 07) ^ rot(w01, 18) ^ (w01 >> 03) +% w09 +% rot(w14, 17) ^ rot(w14, 19) ^ (w14 >> 10); + let w17 = w01 +% rot(w02, 07) ^ rot(w02, 18) ^ (w02 >> 03) +% w10 +% rot(w15, 17) ^ rot(w15, 19) ^ (w15 >> 10); + let w18 = w02 +% rot(w03, 07) ^ rot(w03, 18) ^ (w03 >> 03) +% w11 +% rot(w16, 17) ^ rot(w16, 19) ^ (w16 >> 10); + let w19 = w03 +% rot(w04, 07) ^ rot(w04, 18) ^ (w04 >> 03) +% w12 +% rot(w17, 17) ^ rot(w17, 19) ^ (w17 >> 10); + let w20 = w04 +% rot(w05, 07) ^ rot(w05, 18) ^ (w05 >> 03) +% w13 +% rot(w18, 17) ^ rot(w18, 19) ^ (w18 >> 10); + let w21 = w05 +% rot(w06, 07) ^ rot(w06, 18) ^ (w06 >> 03) +% w14 +% rot(w19, 17) ^ rot(w19, 19) ^ (w19 >> 10); + let w22 = w06 +% rot(w07, 07) ^ rot(w07, 18) ^ (w07 >> 03) +% w15 +% rot(w20, 17) ^ rot(w20, 19) ^ (w20 >> 10); + let w23 = w07 +% rot(w08, 07) ^ rot(w08, 18) ^ (w08 >> 03) +% w16 +% rot(w21, 17) ^ rot(w21, 19) ^ (w21 >> 10); + let w24 = w08 +% rot(w09, 07) ^ rot(w09, 18) ^ (w09 >> 03) +% w17 +% rot(w22, 17) ^ rot(w22, 19) ^ (w22 >> 10); + let w25 = w09 +% rot(w10, 07) ^ rot(w10, 18) ^ (w10 >> 03) +% w18 +% rot(w23, 17) ^ rot(w23, 19) ^ (w23 >> 10); + let w26 = w10 +% rot(w11, 07) ^ rot(w11, 18) ^ (w11 >> 03) +% w19 +% rot(w24, 17) ^ rot(w24, 19) ^ (w24 >> 10); + let w27 = w11 +% rot(w12, 07) ^ rot(w12, 18) ^ (w12 >> 03) +% w20 +% rot(w25, 17) ^ rot(w25, 19) ^ (w25 >> 10); + let w28 = w12 +% rot(w13, 07) ^ rot(w13, 18) ^ (w13 >> 03) +% w21 +% rot(w26, 17) ^ rot(w26, 19) ^ (w26 >> 10); + let w29 = w13 +% rot(w14, 07) ^ rot(w14, 18) ^ (w14 >> 03) +% w22 +% rot(w27, 17) ^ rot(w27, 19) ^ (w27 >> 10); + let w30 = w14 +% rot(w15, 07) ^ rot(w15, 18) ^ (w15 >> 03) +% w23 +% rot(w28, 17) ^ rot(w28, 19) ^ (w28 >> 10); + let w31 = w15 +% rot(w16, 07) ^ rot(w16, 18) ^ (w16 >> 03) +% w24 +% rot(w29, 17) ^ rot(w29, 19) ^ (w29 >> 10); + let w32 = w16 +% rot(w17, 07) ^ rot(w17, 18) ^ (w17 >> 03) +% w25 +% rot(w30, 17) ^ rot(w30, 19) ^ (w30 >> 10); + let w33 = w17 +% rot(w18, 07) ^ rot(w18, 18) ^ (w18 >> 03) +% w26 +% rot(w31, 17) ^ rot(w31, 19) ^ (w31 >> 10); + let w34 = w18 +% rot(w19, 07) ^ rot(w19, 18) ^ (w19 >> 03) +% w27 +% rot(w32, 17) ^ rot(w32, 19) ^ (w32 >> 10); + let w35 = w19 +% rot(w20, 07) ^ rot(w20, 18) ^ (w20 >> 03) +% w28 +% rot(w33, 17) ^ rot(w33, 19) ^ (w33 >> 10); + let w36 = w20 +% rot(w21, 07) ^ rot(w21, 18) ^ (w21 >> 03) +% w29 +% rot(w34, 17) ^ rot(w34, 19) ^ (w34 >> 10); + let w37 = w21 +% rot(w22, 07) ^ rot(w22, 18) ^ (w22 >> 03) +% w30 +% rot(w35, 17) ^ rot(w35, 19) ^ (w35 >> 10); + let w38 = w22 +% rot(w23, 07) ^ rot(w23, 18) ^ (w23 >> 03) +% w31 +% rot(w36, 17) ^ rot(w36, 19) ^ (w36 >> 10); + let w39 = w23 +% rot(w24, 07) ^ rot(w24, 18) ^ (w24 >> 03) +% w32 +% rot(w37, 17) ^ rot(w37, 19) ^ (w37 >> 10); + let w40 = w24 +% rot(w25, 07) ^ rot(w25, 18) ^ (w25 >> 03) +% w33 +% rot(w38, 17) ^ rot(w38, 19) ^ (w38 >> 10); + let w41 = w25 +% rot(w26, 07) ^ rot(w26, 18) ^ (w26 >> 03) +% w34 +% rot(w39, 17) ^ rot(w39, 19) ^ (w39 >> 10); + let w42 = w26 +% rot(w27, 07) ^ rot(w27, 18) ^ (w27 >> 03) +% w35 +% rot(w40, 17) ^ rot(w40, 19) ^ (w40 >> 10); + let w43 = w27 +% rot(w28, 07) ^ rot(w28, 18) ^ (w28 >> 03) +% w36 +% rot(w41, 17) ^ rot(w41, 19) ^ (w41 >> 10); + let w44 = w28 +% rot(w29, 07) ^ rot(w29, 18) ^ (w29 >> 03) +% w37 +% rot(w42, 17) ^ rot(w42, 19) ^ (w42 >> 10); + let w45 = w29 +% rot(w30, 07) ^ rot(w30, 18) ^ (w30 >> 03) +% w38 +% rot(w43, 17) ^ rot(w43, 19) ^ (w43 >> 10); + let w46 = w30 +% rot(w31, 07) ^ rot(w31, 18) ^ (w31 >> 03) +% w39 +% rot(w44, 17) ^ rot(w44, 19) ^ (w44 >> 10); + let w47 = w31 +% rot(w32, 07) ^ rot(w32, 18) ^ (w32 >> 03) +% w40 +% rot(w45, 17) ^ rot(w45, 19) ^ (w45 >> 10); + let w48 = w32 +% rot(w33, 07) ^ rot(w33, 18) ^ (w33 >> 03) +% w41 +% rot(w46, 17) ^ rot(w46, 19) ^ (w46 >> 10); + let w49 = w33 +% rot(w34, 07) ^ rot(w34, 18) ^ (w34 >> 03) +% w42 +% rot(w47, 17) ^ rot(w47, 19) ^ (w47 >> 10); + let w50 = w34 +% rot(w35, 07) ^ rot(w35, 18) ^ (w35 >> 03) +% w43 +% rot(w48, 17) ^ rot(w48, 19) ^ (w48 >> 10); + let w51 = w35 +% rot(w36, 07) ^ rot(w36, 18) ^ (w36 >> 03) +% w44 +% rot(w49, 17) ^ rot(w49, 19) ^ (w49 >> 10); + let w52 = w36 +% rot(w37, 07) ^ rot(w37, 18) ^ (w37 >> 03) +% w45 +% rot(w50, 17) ^ rot(w50, 19) ^ (w50 >> 10); + let w53 = w37 +% rot(w38, 07) ^ rot(w38, 18) ^ (w38 >> 03) +% w46 +% rot(w51, 17) ^ rot(w51, 19) ^ (w51 >> 10); + let w54 = w38 +% rot(w39, 07) ^ rot(w39, 18) ^ (w39 >> 03) +% w47 +% rot(w52, 17) ^ rot(w52, 19) ^ (w52 >> 10); + let w55 = w39 +% rot(w40, 07) ^ rot(w40, 18) ^ (w40 >> 03) +% w48 +% rot(w53, 17) ^ rot(w53, 19) ^ (w53 >> 10); + let w56 = w40 +% rot(w41, 07) ^ rot(w41, 18) ^ (w41 >> 03) +% w49 +% rot(w54, 17) ^ rot(w54, 19) ^ (w54 >> 10); + let w57 = w41 +% rot(w42, 07) ^ rot(w42, 18) ^ (w42 >> 03) +% w50 +% rot(w55, 17) ^ rot(w55, 19) ^ (w55 >> 10); + let w58 = w42 +% rot(w43, 07) ^ rot(w43, 18) ^ (w43 >> 03) +% w51 +% rot(w56, 17) ^ rot(w56, 19) ^ (w56 >> 10); + let w59 = w43 +% rot(w44, 07) ^ rot(w44, 18) ^ (w44 >> 03) +% w52 +% rot(w57, 17) ^ rot(w57, 19) ^ (w57 >> 10); + let w60 = w44 +% rot(w45, 07) ^ rot(w45, 18) ^ (w45 >> 03) +% w53 +% rot(w58, 17) ^ rot(w58, 19) ^ (w58 >> 10); + let w61 = w45 +% rot(w46, 07) ^ rot(w46, 18) ^ (w46 >> 03) +% w54 +% rot(w59, 17) ^ rot(w59, 19) ^ (w59 >> 10); + let w62 = w46 +% rot(w47, 07) ^ rot(w47, 18) ^ (w47 >> 03) +% w55 +% rot(w60, 17) ^ rot(w60, 19) ^ (w60 >> 10); + let w63 = w47 +% rot(w48, 07) ^ rot(w48, 18) ^ (w48 >> 03) +% w56 +% rot(w61, 17) ^ rot(w61, 19) ^ (w61 >> 10); + + /* + for ((i, j, k, l, m) in expansion_rounds.vals()) { + // (j,k,l,m) = (i+1,i+9,i+14,i+16) + let (v0, v1) = (msg[j], msg[l]); + let s0 = rot(v0, 07) ^ rot(v0, 18) ^ (v0 >> 03); + let s1 = rot(v1, 17) ^ rot(v1, 19) ^ (v1 >> 10); + msg[m] := msg[i] +% s0 +% msg[k] +% s1; + }; + */ + // compress + var a = s0; + var b = s1; + var c = s2; + var d = s3; + var e = s4; + var f = s5; + var g = s6; + var h = s7; + var t = 0 : Nat32; + + t := h +% K00 +% w00 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K01 +% w01 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K02 +% w02 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K03 +% w03 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K04 +% w04 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K05 +% w05 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K06 +% w06 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K07 +% w07 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K08 +% w08 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K09 +% w09 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K10 +% w10 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K11 +% w11 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K12 +% w12 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K13 +% w13 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K14 +% w14 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K15 +% w15 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K16 +% w16 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K17 +% w17 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K18 +% w18 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K19 +% w19 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K20 +% w20 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K21 +% w21 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K22 +% w22 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K23 +% w23 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K24 +% w24 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K25 +% w25 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K26 +% w26 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K27 +% w27 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K28 +% w28 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K29 +% w29 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K30 +% w30 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K31 +% w31 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K32 +% w32 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K33 +% w33 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K34 +% w34 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K35 +% w35 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K36 +% w36 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K37 +% w37 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K38 +% w38 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K39 +% w39 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K40 +% w40 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K41 +% w41 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K42 +% w42 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K43 +% w43 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K44 +% w44 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K45 +% w45 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K46 +% w46 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K47 +% w47 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K48 +% w48 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K49 +% w49 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K50 +% w50 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K51 +% w51 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K52 +% w52 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K53 +% w53 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K54 +% w54 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K55 +% w55 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K56 +% w56 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K57 +% w57 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K58 +% w58 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K59 +% w59 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K60 +% w60 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K61 +% w61 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K62 +% w62 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + t := h +% K63 +% w63 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + + /* + for (i in compression_rounds.keys()) { + let ch = (e & f) ^ (^ e & g); + let maj = (a & b) ^ (a & c) ^ (b & c); + let sigma0 = rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); + let sigma1 = rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); + let t = h +% K[i] +% msg[i] +% ch +% sigma1; + h := g; + g := f; + f := e; + e := d +% t; + d := c; + c := b; + b := a; + a := t +% maj +% sigma0; + }; + */ + // final addition + s0 +%= a; + s1 +%= b; + s2 +%= c; + s3 +%= d; + s4 +%= e; + s5 +%= f; + s6 +%= g; + s7 +%= h + }; + + public func writeIter(iter : { next() : ?Nat8 }) : () { + label reading loop { + switch (iter.next()) { + case (?val) { + writeByte(val); + continue reading + }; + case (null) { + break reading + } + } + } + }; + + public func writeArray(arr : [Nat8]) : () = writeIter(arr.vals()); + public func writeBlob(blob : Blob) : () = writeIter(blob.vals()); + + public func sum() : Blob { + // calculate padding + // t = bytes in the last incomplete block (0-63) + let t : Nat8 = (i_msg << 2) +% 4 -% i_byte; + // p = length of padding (1-64) + var p : Nat8 = if (t < 56) (56 -% t) else (120 -% t); + // n_bits = length of message in bits + let n_bits : Nat64 = ((i_block << 6) +% Nat64.fromIntWrap(Nat8.toNat(t))) << 3; + + // write padding + writeByte(0x80); + p -%= 1; + while (p != 0) { + writeByte(0x00); + p -%= 1 + }; + + // write length (8 bytes) + // Note: this exactly fills the block buffer, hence process_block will get + // triggered by the last writeByte + writeByte(Nat8.fromIntWrap(Nat64.toNat((n_bits >> 56) & 0xff))); + writeByte(Nat8.fromIntWrap(Nat64.toNat((n_bits >> 48) & 0xff))); + writeByte(Nat8.fromIntWrap(Nat64.toNat((n_bits >> 40) & 0xff))); + writeByte(Nat8.fromIntWrap(Nat64.toNat((n_bits >> 32) & 0xff))); + writeByte(Nat8.fromIntWrap(Nat64.toNat((n_bits >> 24) & 0xff))); + writeByte(Nat8.fromIntWrap(Nat64.toNat((n_bits >> 16) & 0xff))); + writeByte(Nat8.fromIntWrap(Nat64.toNat((n_bits >> 8) & 0xff))); + writeByte(Nat8.fromIntWrap(Nat64.toNat(n_bits & 0xff))); + + // retrieve sum + digest[0] := Nat8.fromIntWrap(Nat32.toNat((s0 >> 24) & 0xff)); + digest[1] := Nat8.fromIntWrap(Nat32.toNat((s0 >> 16) & 0xff)); + digest[2] := Nat8.fromIntWrap(Nat32.toNat((s0 >> 8) & 0xff)); + digest[3] := Nat8.fromIntWrap(Nat32.toNat(s0 & 0xff)); + digest[4] := Nat8.fromIntWrap(Nat32.toNat((s1 >> 24) & 0xff)); + digest[5] := Nat8.fromIntWrap(Nat32.toNat((s1 >> 16) & 0xff)); + digest[6] := Nat8.fromIntWrap(Nat32.toNat((s1 >> 8) & 0xff)); + digest[7] := Nat8.fromIntWrap(Nat32.toNat(s1 & 0xff)); + digest[8] := Nat8.fromIntWrap(Nat32.toNat((s2 >> 24) & 0xff)); + digest[9] := Nat8.fromIntWrap(Nat32.toNat((s2 >> 16) & 0xff)); + digest[10] := Nat8.fromIntWrap(Nat32.toNat((s2 >> 8) & 0xff)); + digest[11] := Nat8.fromIntWrap(Nat32.toNat(s2 & 0xff)); + digest[12] := Nat8.fromIntWrap(Nat32.toNat((s3 >> 24) & 0xff)); + digest[13] := Nat8.fromIntWrap(Nat32.toNat((s3 >> 16) & 0xff)); + digest[14] := Nat8.fromIntWrap(Nat32.toNat((s3 >> 8) & 0xff)); + digest[15] := Nat8.fromIntWrap(Nat32.toNat(s3 & 0xff)); + digest[16] := Nat8.fromIntWrap(Nat32.toNat((s4 >> 24) & 0xff)); + digest[17] := Nat8.fromIntWrap(Nat32.toNat((s4 >> 16) & 0xff)); + digest[18] := Nat8.fromIntWrap(Nat32.toNat((s4 >> 8) & 0xff)); + digest[19] := Nat8.fromIntWrap(Nat32.toNat(s4 & 0xff)); + digest[20] := Nat8.fromIntWrap(Nat32.toNat((s5 >> 24) & 0xff)); + digest[21] := Nat8.fromIntWrap(Nat32.toNat((s5 >> 16) & 0xff)); + digest[22] := Nat8.fromIntWrap(Nat32.toNat((s5 >> 8) & 0xff)); + digest[23] := Nat8.fromIntWrap(Nat32.toNat(s5 & 0xff)); + digest[24] := Nat8.fromIntWrap(Nat32.toNat((s6 >> 24) & 0xff)); + digest[25] := Nat8.fromIntWrap(Nat32.toNat((s6 >> 16) & 0xff)); + digest[26] := Nat8.fromIntWrap(Nat32.toNat((s6 >> 8) & 0xff)); + digest[27] := Nat8.fromIntWrap(Nat32.toNat(s6 & 0xff)); + + return Blob.fromArrayMut(digest) + } + }; // class SHA224 + + func nat32ToByteArray(n : Nat32) : [Nat8] { + func byte(n : Nat32) : Nat8 { + Nat8.fromNat(Nat32.toNat(n & 0xff)) + }; + [byte(n >> 24), byte(n >> 16), byte(n >> 8), byte(n)] + } +} +//CALL ingress go 0x4449444C0000 \ No newline at end of file diff --git a/test/perf/sha224/Array.mo b/test/perf/sha224/Array.mo new file mode 100644 index 00000000000..9acd0186970 --- /dev/null +++ b/test/perf/sha224/Array.mo @@ -0,0 +1,867 @@ +/// Provides extended utility functions on Arrays. +/// +/// Note the difference between mutable and non-mutable arrays below. +/// +/// WARNING: If you are looking for a list that can grow and shrink in size, +/// it is recommended you use either the Buffer class or the List class for +/// those purposes. Arrays must be created with a fixed size. +/// +/// Import from the base library to use this module. +/// ```motoko name=import +/// import Array "mo:base/Array"; +/// ``` + +import I "IterType"; +import Option "Option"; +import Order "Order"; +import Prim "mo:⛔"; +import Result "Result"; + +module { + /// Create a mutable array with `size` copies of the initial value. + /// + /// ```motoko include=import + /// let array = Array.init(4, 2); + /// ``` + /// + /// Runtime: O(size) + /// Space: O(size) + public func init(size : Nat, initValue : X) : [var X] = Prim.Array_init(size, initValue); + + /// Create an immutable array of size `size`. Each element at index i + /// is created by applying `generator` to i. + /// + /// ```motoko include=import + /// let array : [Nat] = Array.tabulate(4, func i = i * 2); + /// ``` + /// + /// Runtime: O(size) + /// Space: O(size) + /// + /// *Runtime and space assumes that `generator` runs in O(1) time and space. + public func tabulate(size : Nat, generator : Nat -> X) : [X] = Prim.Array_tabulate(size, generator); + + /// Create a mutable array of size `size`. Each element at index i + /// is created by applying `generator` to i. + /// + /// ```motoko include=import + /// let array : [var Nat] = Array.tabulateVar(4, func i = i * 2); + /// array[2] := 0; + /// array + /// ``` + /// + /// Runtime: O(size) + /// Space: O(size) + /// + /// *Runtime and space assumes that `generator` runs in O(1) time and space. + public func tabulateVar(size : Nat, generator : Nat -> X) : [var X] { + // FIXME add this as a primitive in the RTS + if (size == 0) { return [var] }; + let array = Prim.Array_init(size, generator 0); + var i = 1; + while (i < size) { + array[i] := generator i; + i += 1 + }; + array + }; + + /// Transforms a mutable array into an immutable array. + /// + /// ```motoko include=import + /// + /// let varArray = [var 0, 1, 2]; + /// varArray[2] := 3; + /// let array = Array.freeze(varArray); + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(1) + public func freeze(varArray : [var X]) : [X] = Prim.Array_tabulate(varArray.size(), func i = varArray[i]); + + /// Transforms an immutable array into a mutable array. + /// + /// ```motoko include=import + /// + /// let array = [0, 1, 2]; + /// let varArray = Array.thaw(array); + /// varArray[2] := 3; + /// varArray + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(1) + public func thaw(array : [A]) : [var A] { + let size = array.size(); + if (size == 0) { + return [var] + }; + let newArray = Prim.Array_init(size, array[0]); + var i = 0; + while (i < size) { + newArray[i] := array[i]; + i += 1 + }; + newArray + }; + + /// Tests if two arrays contain equal values (i.e. they represent the same + /// list of elements). Uses `equal` to compare elements in the arrays. + /// + /// ```motoko include=import + /// // Use the equal function from the Nat module to compare Nats + /// import {equal} "mo:base/Nat"; + /// + /// let array1 = [0, 1, 2, 3]; + /// let array2 = [0, 1, 2, 3]; + /// Array.equal(array1, array2, equal) + /// ``` + /// + /// Runtime: O(size1 + size2) + /// + /// Space: O(1) + /// + /// *Runtime and space assumes that `equal` runs in O(1) time and space. + public func equal(array1 : [X], array2 : [X], equal : (X, X) -> Bool) : Bool { + let size1 = array1.size(); + let size2 = array2.size(); + if (size1 != size2) { + return false + }; + var i = 0; + while (i < size1) { + if (not equal(array1[i], array2[i])) { + return false + }; + i += 1 + }; + return true + }; + + /// Returns the first value in `array` for which `predicate` returns true. + /// If no element satisfies the predicate, returns null. + /// + /// ```motoko include=import + /// let array = [1, 9, 4, 8]; + /// Array.find(array, func x = x > 8) + /// ``` + /// Runtime: O(size) + /// + /// Space: O(1) + /// + /// *Runtime and space assumes that `predicate` runs in O(1) time and space. + public func find(array : [X], predicate : X -> Bool) : ?X { + for (element in array.vals()) { + if (predicate element) { + return ?element + } + }; + return null + }; + + /// Create a new array by appending the values of `array1` and `array2`. + /// @deprecated `Array.append` copies its arguments and has linear complexity; + /// when used in a loop, consider using a `Buffer`, and `Buffer.append`, instead. + /// + /// ```motoko include=import + /// let array1 = [1, 2, 3]; + /// let array2 = [4, 5, 6]; + /// Array.append(array1, array2) + /// ``` + /// Runtime: O(size1 + size2) + /// + /// Space: O(size1 + size2) + public func append(array1 : [X], array2 : [X]) : [X] { + let size1 = array1.size(); + let size2 = array2.size(); + Prim.Array_tabulate( + size1 + size2, + func i { + if (i < size1) { + array1[i] + } else { + array2[i - size1] + } + } + ) + }; + + // FIXME this example stack overflows. Should test with new implementation of sortInPlace + /// Sorts the elements in the array according to `compare`. + /// Sort is deterministic and stable. + /// + /// ```motoko include=import + /// import Nat "mo:base/Nat"; + /// + /// let array = [4, 2, 6]; + /// Array.sort(array, Nat.compare) + /// ``` + /// Runtime: O(size * log(size)) + /// + /// Space: O(size) + /// *Runtime and space assumes that `compare` runs in O(1) time and space. + public func sort(array : [X], compare : (X, X) -> Order.Order) : [X] { + let temp : [var X] = thaw(array); + sortInPlace(temp, compare); + freeze(temp) + }; + + /// Sorts the elements in the array, __in place__, according to `compare`. + /// Sort is deterministic, stable, and in-place. + /// + /// ```motoko include=import + /// + /// import {compare} "mo:base/Nat"; + /// + /// let array = [var 4, 2, 6]; + /// Array.sortInPlace(array, compare); + /// array + /// ``` + /// Runtime: O(size * log(size)) + /// + /// Space: O(size) + /// *Runtime and space assumes that `compare` runs in O(1) time and space. + public func sortInPlace(array : [var X], compare : (X, X) -> Order.Order) { + // Stable merge sort in a bottom-up iterative style. Same algorithm as the sort in Buffer. + let size = array.size(); + if (size == 0) { + return + }; + let scratchSpace = Prim.Array_init(size, array[0]); + + let sizeDec = size - 1 : Nat; + var currSize = 1; // current size of the subarrays being merged + // when the current size == size, the array has been merged into a single sorted array + while (currSize < size) { + var leftStart = 0; // selects the current left subarray being merged + while (leftStart < sizeDec) { + let mid : Nat = if (leftStart + currSize - 1 : Nat < sizeDec) { + leftStart + currSize - 1 + } else { sizeDec }; + let rightEnd : Nat = if (leftStart + (2 * currSize) - 1 : Nat < sizeDec) { + leftStart + (2 * currSize) - 1 + } else { sizeDec }; + + // Merge subarrays elements[leftStart...mid] and elements[mid+1...rightEnd] + var left = leftStart; + var right = mid + 1; + var nextSorted = leftStart; + while (left < mid + 1 and right < rightEnd + 1) { + let leftElement = array[left]; + let rightElement = array[right]; + switch (compare(leftElement, rightElement)) { + case (#less or #equal) { + scratchSpace[nextSorted] := leftElement; + left += 1 + }; + case (#greater) { + scratchSpace[nextSorted] := rightElement; + right += 1 + } + }; + nextSorted += 1 + }; + while (left < mid + 1) { + scratchSpace[nextSorted] := array[left]; + nextSorted += 1; + left += 1 + }; + while (right < rightEnd + 1) { + scratchSpace[nextSorted] := array[right]; + nextSorted += 1; + right += 1 + }; + + // Copy over merged elements + var i = leftStart; + while (i < rightEnd + 1) { + array[i] := scratchSpace[i]; + i += 1 + }; + + leftStart += 2 * currSize + }; + currSize *= 2 + } + }; + + /// Creates a new array by reversing the order of elements in `array`. + /// + /// ```motoko include=import + /// + /// let array = [10, 11, 12]; + /// + /// Array.reverse(array) + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(1) + public func reverse(array : [X]) : [X] { + let size = array.size(); + Prim.Array_tabulate(size, func i = array[size - i - 1]) + }; + + /// Creates a new array by applying `f` to each element in `array`. `f` "maps" + /// each element it is applied to of type `X` to an element of type `Y`. + /// Retains original ordering of elements. + /// + /// ```motoko include=import + /// + /// let array = [0, 1, 2, 3]; + /// Array.map(array, func x = x * 3) + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(size) + /// + /// *Runtime and space assumes that `f` runs in O(1) time and space. + public func map(array : [X], f : X -> Y) : [Y] = Prim.Array_tabulate(array.size(), func i = f(array[i])); + + /// Creates a new array by applying `predicate` to every element + /// in `array`, retaining the elements for which `predicate` returns true. + /// + /// ```motoko include=import + /// let array = [4, 2, 6, 1, 5]; + /// let evenElements = Array.filter(array, func x = x % 2 == 0); + /// ``` + /// Runtime: O(size) + /// + /// Space: O(size) + /// *Runtime and space assumes that `predicate` runs in O(1) time and space. + public func filter(array : [X], predicate : X -> Bool) : [X] { + var count = 0; + let keep = Prim.Array_tabulate( + array.size(), + func i { + if (predicate(array[i])) { + count += 1; + true + } else { + false + } + } + ); + var nextKeep = 0; + Prim.Array_tabulate( + count, + func _ { + while (not keep[nextKeep]) { + nextKeep += 1 + }; + nextKeep += 1; + array[nextKeep - 1] + } + ) + }; + + // FIXME the arguments ordering to the higher order function are flipped + // between this and the buffer class + // probably can't avoid breaking changes at some point + /// Creates a new array by applying `f` to each element in `array` and its index. + /// Retains original ordering of elements. + /// + /// ```motoko include=import + /// + /// let array = [10, 10, 10, 10]; + /// Array.mapEntries(array, func (x, i) = i * x) + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(size) + /// + /// *Runtime and space assumes that `f` runs in O(1) time and space. + public func mapEntries(array : [X], f : (X, Nat) -> Y) : [Y] = Prim.Array_tabulate(array.size(), func i = f(array[i], i)); + + /// Creates a new array by applying `f` to each element in `array`, + /// and keeping all non-null elements. The ordering is retained. + /// + /// ```motoko include=import + /// import {toText} "mo:base/Nat"; + /// + /// let array = [4, 2, 0, 1]; + /// let newArray = + /// Array.mapFilter( // mapping from Nat to Text values + /// array, + /// func x = if (x == 0) { null } else { ?toText(100 / x) } // can't divide by 0, so return null + /// ); + /// ``` + /// Runtime: O(size) + /// + /// Space: O(size) + /// *Runtime and space assumes that `f` runs in O(1) time and space. + public func mapFilter(array : [X], f : X -> ?Y) : [Y] { + var count = 0; + let options = Prim.Array_tabulate( + array.size(), + func i { + let result = f(array[i]); + switch (result) { + case (?element) { + count += 1; + result + }; + case null { + null + } + } + } + ); + + var nextSome = 0; + Prim.Array_tabulate( + count, + func _ { + while (Option.isNull(options[nextSome])) { + nextSome += 1 + }; + nextSome += 1; + switch (options[nextSome - 1]) { + case (?element) element; + case null { + Prim.trap "Malformed array in mapFilter" + } + } + } + ) + }; + + /// Creates a new array by applying `f` to each element in `array`. + /// If any invocation of `f` produces an `#err`, returns an `#err`. Otherwise + /// returns an `#ok` containing the new array. + /// + /// ```motoko include=import + /// let array = [4, 3, 2, 1, 0]; + /// // divide 100 by every element in the array + /// Array.mapResult(array, func x { + /// if (x > 0) { + /// #ok(100 / x) + /// } else { + /// #err "Cannot divide by zero" + /// } + /// }) + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(size) + /// + /// *Runtime and space assumes that `f` runs in O(1) time and space. + public func mapResult(array : [X], f : X -> Result.Result) : Result.Result<[Y], E> { + let size = array.size(); + var target : [var Y] = [var]; + var isInit = false; + + var error : ?Result.Result<[Y], E> = null; + let results = Prim.Array_tabulate( + size, + func i { + switch (f(array[i])) { + case (#ok element) { + ?element + }; + case (#err e) { + switch (error) { + case null { + // only take the first error + error := ?(#err e) + }; + case _ {} + }; + null + } + } + } + ); + + switch error { + case null { + // unpack the option + #ok( + map( + results, + func element { + switch element { + case (?element) { + element + }; + case null { + Prim.trap "Malformed array in mapResults" + } + } + } + ) + ) + }; + case (?error) { + error + } + } + }; + + /// Creates a new array by applying `k` to each element in `array`, + /// and concatenating the resulting arrays in order. This operation + /// is similar to what in other functional languages is known as monadic bind. + /// + /// ```motoko include=import + /// import Nat "mo:base/Nat"; + /// + /// let array = [1, 2, 3, 4]; + /// Array.chain(array, func x = [x, -x]) + /// + /// ``` + /// Runtime: O(size) + /// + /// Space: O(size) + /// *Runtime and space assumes that `k` runs in O(1) time and space. + public func chain(array : [X], k : X -> [Y]) : [Y] { + var flatSize = 0; + let arrays = Prim.Array_tabulate<[Y]>( + array.size(), + func i { + let subArray = k(array[i]); + flatSize += subArray.size(); + subArray + } + ); + + // could replace with a call to flatten, + // but it would require an extra pass (to compute `flatSize`) + var outer = 0; + var inner = 0; + Prim.Array_tabulate( + flatSize, + func _ { + while (inner == arrays[outer].size()) { + inner := 0; + outer += 1 + }; + let element = arrays[outer][inner]; + inner += 1; + element + } + ) + }; + + /// Collapses the elements in `array` into a single value by starting with `base` + /// and progessively combining elements into `base` with `combine`. Iteration runs + /// left to right. + /// + /// ```motoko include=import + /// import {add} "mo:base/Nat"; + /// + /// let array = [4, 2, 0, 1]; + /// let sum = + /// Array.foldLeft( + /// array, + /// 0, // start the sum at 0 + /// func(sumSoFar, x) = sumSoFar + x // this entire function can be replaced with `add`! + /// ); + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(1) + /// + /// *Runtime and space assumes that `combine` runs in O(1) time and space. + public func foldLeft(array : [X], base : A, combine : (A, X) -> A) : A { + var accumulation = base; + + for (element in array.vals()) { + accumulation := combine(accumulation, element) + }; + + accumulation + }; + + // FIXME the type arguments are reverse order from Buffer + /// Collapses the elements in `array` into a single value by starting with `base` + /// and progessively combining elements into `base` with `combine`. Iteration runs + /// right to left. + /// + /// ```motoko include=import + /// import {toText} "mo:base/Nat"; + /// + /// let array = [1, 9, 4, 8]; + /// let bookTitle = Array.foldRight(array, "", func(x, acc) = toText(x) # acc); + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(1) + /// + /// *Runtime and space assumes that `combine` runs in O(1) time and space. + public func foldRight(array : [X], base : A, combine : (X, A) -> A) : A { + var accumulation = base; + let size = array.size(); + + var i = size; + while (i > 0) { + i -= 1; + accumulation := combine(array[i], accumulation) + }; + + accumulation + }; + + /// Flattens the array of arrays into a single array. Retains the original + /// ordering of the elements. + /// + /// ```motoko include=import + /// + /// let arrays = [[0, 1, 2], [2, 3], [], [4]]; + /// Array.flatten(arrays) + /// ``` + /// + /// Runtime: O(number of elements in array) + /// + /// Space: O(number of elements in array) + public func flatten(arrays : [[X]]) : [X] { + var flatSize = 0; + for (subArray in arrays.vals()) { + flatSize += subArray.size() + }; + + var outer = 0; + var inner = 0; + Prim.Array_tabulate( + flatSize, + func _ { + while (inner == arrays[outer].size()) { + inner := 0; + outer += 1 + }; + let element = arrays[outer][inner]; + inner += 1; + element + } + ) + }; + + /// Create an array containing a single value. + /// + /// ```motoko include=import + /// Array.make(2) + /// ``` + /// + /// Runtime: O(1) + /// + /// Space: O(1) + public func make(element : X) : [X] = [element]; + + /// Returns an Iterator (`Iter`) over the elements of `array`. + /// Iterator provides a single method `next()`, which returns + /// elements in order, or `null` when out of elements to iterate over. + /// + /// NOTE: You can also use `array.vals()` instead of this function. See example + /// below. + /// + /// ```motoko include=import + /// + /// let array = [10, 11, 12]; + /// + /// var sum = 0; + /// for (element in array.vals()) { + /// sum += element; + /// }; + /// sum + /// ``` + /// + /// Runtime: O(1) + /// + /// Space: O(1) + public func vals(array : [X]) : I.Iter = array.vals(); + + /// Returns an Iterator (`Iter`) over the indices of `array`. + /// Iterator provides a single method `next()`, which returns + /// indices in order, or `null` when out of index to iterate over. + /// + /// NOTE: You can also use `array.keys()` instead of this function. See example + /// below. + /// + /// ```motoko include=import + /// + /// let array = [10, 11, 12]; + /// + /// var sum = 0; + /// for (element in array.keys()) { + /// sum += element; + /// }; + /// sum + /// ``` + /// + /// Runtime: O(1) + /// + /// Space: O(1) + public func keys(array : [X]) : I.Iter = array.keys(); + + /// Returns the size of `array`. + /// + /// NOTE: You can also use `array.size()` instead of this function. See example + /// below. + /// + /// ```motoko include=import + /// + /// let array = [10, 11, 12]; + /// let size = Array.size(array); + /// ``` + /// + /// Runtime: O(1) + /// + /// Space: O(1) + public func size(array : [X]) : Nat = array.size(); + + /// Returns a new subarray from the given array provided the start index and length of elements in the subarray + /// + /// Limitations: Traps if the start index + length is greater than the size of the array + /// + /// ```motoko include=import + /// + /// let array = [1,2,3,4,5]; + /// let subArray = Array.subArray(array, 2, 3); + /// ``` + /// Runtime: O(length); + /// Space: O(length); + public func subArray(array : [X], start : Nat, length : Nat) : [X] { + if (start + length > array.size()) { Prim.trap("Array.subArray") }; + tabulate( + length, + func(i) { + array[start + i] + } + ) + }; + + /// Returns the index of the first `element` in the `array`. + /// + /// ```motoko include=import + /// import Char "mo:base/Char"; + /// let array = ['c', 'o', 'f', 'f', 'e', 'e']; + /// assert Array.indexOf('c', array, Char.equal) == ?0; + /// assert Array.indexOf('f', array, Char.equal) == ?2; + /// assert Array.indexOf('g', array, Char.equal) == null; + /// ``` + /// + /// Runtime: O(array.size()); + /// Space: O(1); + public func indexOf(element : X, array : [X], equal : (X, X) -> Bool) : ?Nat = nextIndexOf(element, array, 0, equal); + + /// Returns the index of the next occurence of `element` in the `array` starting from the `from` index (inclusive). + /// + /// ```motoko include=import + /// import Char "mo:base/Char"; + /// let array = ['c', 'o', 'f', 'f', 'e', 'e']; + /// assert Array.nextIndexOf('c', array, 0, Char.equal) == ?0; + /// assert Array.nextIndexOf('f', array, 0, Char.equal) == ?2; + /// assert Array.nextIndexOf('f', array, 2, Char.equal) == ?2; + /// assert Array.nextIndexOf('f', array, 3, Char.equal) == ?3; + /// assert Array.nextIndexOf('f', array, 4, Char.equal) == null; + /// ``` + /// + /// Runtime: O(array.size()); + /// Space: O(1); + public func nextIndexOf(element : X, array : [X], fromInclusive : Nat, equal : (X, X) -> Bool) : ?Nat { + var i = fromInclusive; + let n = array.size(); + while (i < n) { + if (equal(array[i], element)) { + return ?i + } else { + i += 1 + } + }; + null + }; + + /// Returns the index of the last `element` in the `array`. + /// + /// ```motoko include=import + /// import Char "mo:base/Char"; + /// let array = ['c', 'o', 'f', 'f', 'e', 'e']; + /// assert Array.lastIndexOf('c', array, Char.equal) == ?0; + /// assert Array.lastIndexOf('f', array, Char.equal) == ?3; + /// assert Array.lastIndexOf('e', array, Char.equal) == ?5; + /// assert Array.lastIndexOf('g', array, Char.equal) == null; + /// ``` + /// + /// Runtime: O(array.size()); + /// Space: O(1); + public func lastIndexOf(element : X, array : [X], equal : (X, X) -> Bool) : ?Nat = prevIndexOf(element, array, array.size(), equal); + + /// Returns the index of the previous occurance of `element` in the `array` starting from the `from` index (exclusive). + /// + /// ```motoko include=import + /// import Char "mo:base/Char"; + /// let array = ['c', 'o', 'f', 'f', 'e', 'e']; + /// assert Array.prevIndexOf('c', array, array.size(), Char.equal) == ?0; + /// assert Array.prevIndexOf('e', array, array.size(), Char.equal) == ?5; + /// assert Array.prevIndexOf('e', array, 5, Char.equal) == ?4; + /// assert Array.prevIndexOf('e', array, 4, Char.equal) == null; + /// ``` + /// + /// Runtime: O(array.size()); + /// Space: O(1); + public func prevIndexOf(element : T, array : [T], fromExclusive : Nat, equal : (T, T) -> Bool) : ?Nat { + var i = fromExclusive; + while (i > 0) { + i -= 1; + if (equal(array[i], element)) { + return ?i + } + }; + null + }; + + /// Returns an iterator over a slice of the given array. + /// + /// ```motoko include=import + /// let array = [1, 2, 3, 4, 5]; + /// let s = Array.slice(array, 3, array.size()); + /// assert s.next() == ?4; + /// assert s.next() == ?5; + /// assert s.next() == null; + /// + /// let s = Array.slice(array, 0, 0); + /// assert s.next() == null; + /// ``` + /// + /// Runtime: O(1) + /// Space: O(1) + public func slice(array : [X], fromInclusive : Nat, toExclusive : Nat) : I.Iter = object { + var i = fromInclusive; + + public func next() : ?X { + if (i >= toExclusive) { + return null + }; + let result = array[i]; + i += 1; + return ?result + } + }; + + /// Returns a new subarray of given length from the beginning or end of the given array + /// + /// Returns the entire array if the length is greater than the size of the array + /// + /// ```motoko include=import + /// let array = [1, 2, 3, 4, 5]; + /// assert Array.take(array, 2) == [1, 2]; + /// assert Array.take(array, -2) == [4, 5]; + /// assert Array.take(array, 10) == [1, 2, 3, 4, 5]; + /// assert Array.take(array, -99) == [1, 2, 3, 4, 5]; + /// ``` + /// Runtime: O(length); + /// Space: O(length); + public func take(array : [T], length : Int) : [T] { + let len = Prim.abs(length); + let size = array.size(); + let resSize = if (len < size) { len } else { size }; + let start = if (length > 0) 0 else size - resSize; + subArray(array, start, resSize) + } +} diff --git a/test/perf/sha224/Blob.mo b/test/perf/sha224/Blob.mo new file mode 100644 index 00000000000..3b6ccd17d6c --- /dev/null +++ b/test/perf/sha224/Blob.mo @@ -0,0 +1,207 @@ +/// Module for working with Blobs: immutable sequence of bytes. +/// +/// Blobs represent sequences of bytes. They are immutable, iterable, but not indexable and can be empty. +/// +/// Byte sequences are also often represented as `[Nat8]`, i.e. an array of bytes, but this representation is currently much less compact than `Blob`, taking 4 physical bytes to represent each logical byte in the sequence. +/// If you would like to manipulate Blobs, it is recommended that you convert +/// Blobs to `[var Nat8]` or `Buffer`, do the manipulation, then convert back. +/// +/// Import from the base library to use this module. +/// ```motoko name=import +/// import Blob "mo:base/Blob"; +/// ``` +/// +/// Some built in features not listed in this module: +/// +/// * You can create a `Blob` literal from a `Text` literal, provided the context expects an expression of type `Blob`. +/// * `b.size() : Nat` returns the number of bytes in the blob `b`; +/// * `b.vals() : Iter.Iter` returns an iterator to enumerate the bytes of the blob `b`. +/// +/// For example: +/// ```motoko include=import +/// import Debug "mo:base/Debug"; +/// import Nat8 "mo:base/Nat8"; +/// +/// let blob = "\00\00\00\ff" : Blob; // blob literals, where each byte is delimited by a back-slash and represented in hex +/// let blob2 = "charsもあり" : Blob; // you can also use characters in the literals +/// let numBytes = blob.size(); // => 4 (returns the number of bytes in the Blob) +/// for (byte : Nat8 in blob.vals()) { // iterator over the Blob +/// Debug.print(Nat8.toText(byte)) +/// } +/// ``` +import Prim "mo:⛔"; +module { + public type Blob = Prim.Types.Blob; + /// Creates a `Blob` from an array of bytes (`[Nat8]`), by copying each element. + /// + /// Example: + /// ```motoko include=import + /// let bytes : [Nat8] = [0, 255, 0]; + /// let blob = Blob.fromArray(bytes); // => "\00\FF\00" + /// ``` + public func fromArray(bytes : [Nat8]) : Blob = Prim.arrayToBlob bytes; + + /// Creates a `Blob` from a mutable array of bytes (`[var Nat8]`), by copying each element. + /// + /// Example: + /// ```motoko include=import + /// let bytes : [var Nat8] = [var 0, 255, 0]; + /// let blob = Blob.fromArrayMut(bytes); // => "\00\FF\00" + /// ``` + public func fromArrayMut(bytes : [var Nat8]) : Blob = Prim.arrayMutToBlob bytes; + + /// Converts a `Blob` to an array of bytes (`[Nat8]`), by copying each element. + /// + /// Example: + /// ```motoko include=import + /// let blob = "\00\FF\00" : Blob; + /// let bytes = Blob.toArray(blob); // => [0, 255, 0] + /// ``` + public func toArray(blob : Blob) : [Nat8] = Prim.blobToArray blob; + + /// Converts a `Blob` to a mutable array of bytes (`[var Nat8]`), by copying each element. + /// + /// Example: + /// ```motoko include=import + /// let blob = "\00\FF\00" : Blob; + /// let bytes = Blob.toArrayMut(blob); // => [var 0, 255, 0] + /// ``` + public func toArrayMut(blob : Blob) : [var Nat8] = Prim.blobToArrayMut blob; + + /// Returns the (non-cryptographic) hash of `blob`. + /// + /// Example: + /// ```motoko include=import + /// let blob = "\00\FF\00" : Blob; + /// Blob.hash(blob) // => 1_818_567_776 + /// ``` + public func hash(blob : Blob) : Nat32 = Prim.hashBlob blob; + + /// General purpose comparison function for `Blob` by comparing the value of + /// the bytes. Returns the `Order` (either `#less`, `#equal`, or `#greater`) + /// by comparing `blob1` with `blob2`. + /// + /// Example: + /// ```motoko include=import + /// let blob1 = "\00\00\00" : Blob; + /// let blob2 = "\00\FF\00" : Blob; + /// Blob.compare(blob1, blob2) // => #less + /// ``` + public func compare(b1 : Blob, b2 : Blob) : { #less; #equal; #greater } { + let c = Prim.blobCompare(b1, b2); + if (c < 0) #less else if (c == 0) #equal else #greater + }; + + /// Equality function for `Blob` types. + /// This is equivalent to `blob1 == blob2`. + /// + /// Example: + /// ```motoko include=import + /// let blob1 = "\00\FF\00" : Blob; + /// let blob2 = "\00\FF\00" : Blob; + /// ignore Blob.equal(blob1, blob2); + /// blob1 == blob2 // => true + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `==` operator) is so that you can use it as a function value + /// to pass to a higher order function. It is not possible to use `==` as a + /// function value at the moment. + /// + /// Example: + /// ```motoko include=import + /// import Buffer "mo:base/Buffer"; + /// + /// let buffer1 = Buffer.Buffer(3); + /// let buffer2 = Buffer.Buffer(3); + /// Buffer.equal(buffer1, buffer2, Blob.equal) // => true + /// ``` + public func equal(blob1 : Blob, blob2 : Blob) : Bool { blob1 == blob2 }; + + /// Inequality function for `Blob` types. + /// This is equivalent to `blob1 != blob2`. + /// + /// Example: + /// ```motoko include=import + /// let blob1 = "\00\AA\AA" : Blob; + /// let blob2 = "\00\FF\00" : Blob; + /// ignore Blob.notEqual(blob1, blob2); + /// blob1 != blob2 // => true + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `!=` operator) is so that you can use it as a function value + /// to pass to a higher order function. It is not possible to use `!=` as a + /// function value at the moment. + public func notEqual(blob1 : Blob, blob2 : Blob) : Bool { blob1 != blob2 }; + + /// "Less than" function for `Blob` types. + /// This is equivalent to `blob1 < blob2`. + /// + /// Example: + /// ```motoko include=import + /// let blob1 = "\00\AA\AA" : Blob; + /// let blob2 = "\00\FF\00" : Blob; + /// ignore Blob.less(blob1, blob2); + /// blob1 < blob2 // => true + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `<` operator) is so that you can use it as a function value + /// to pass to a higher order function. It is not possible to use `<` as a + /// function value at the moment. + public func less(blob1 : Blob, blob2 : Blob) : Bool { blob1 < blob2 }; + + /// "Less than or equal to" function for `Blob` types. + /// This is equivalent to `blob1 <= blob2`. + /// + /// Example: + /// ```motoko include=import + /// let blob1 = "\00\AA\AA" : Blob; + /// let blob2 = "\00\FF\00" : Blob; + /// ignore Blob.lessOrEqual(blob1, blob2); + /// blob1 <= blob2 // => true + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `<=` operator) is so that you can use it as a function value + /// to pass to a higher order function. It is not possible to use `<=` as a + /// function value at the moment. + public func lessOrEqual(blob1 : Blob, blob2 : Blob) : Bool { blob1 <= blob2 }; + + /// "Greater than" function for `Blob` types. + /// This is equivalent to `blob1 > blob2`. + /// + /// Example: + /// ```motoko include=import + /// let blob1 = "\BB\AA\AA" : Blob; + /// let blob2 = "\00\00\00" : Blob; + /// ignore Blob.greater(blob1, blob2); + /// blob1 > blob2 // => true + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `>` operator) is so that you can use it as a function value + /// to pass to a higher order function. It is not possible to use `>` as a + /// function value at the moment. + public func greater(blob1 : Blob, blob2 : Blob) : Bool { blob1 > blob2 }; + + /// "Greater than or equal to" function for `Blob` types. + /// This is equivalent to `blob1 >= blob2`. + /// + /// Example: + /// ```motoko include=import + /// let blob1 = "\BB\AA\AA" : Blob; + /// let blob2 = "\00\00\00" : Blob; + /// ignore Blob.greaterOrEqual(blob1, blob2); + /// blob1 >= blob2 // => true + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `>=` operator) is so that you can use it as a function value + /// to pass to a higher order function. It is not possible to use `>=` as a + /// function value at the moment. + public func greaterOrEqual(blob1 : Blob, blob2 : Blob) : Bool { + blob1 >= blob2 + } +} diff --git a/test/perf/sha224/Buffer.mo b/test/perf/sha224/Buffer.mo new file mode 100644 index 00000000000..219ac78abf1 --- /dev/null +++ b/test/perf/sha224/Buffer.mo @@ -0,0 +1,2660 @@ +/// Class `Buffer` provides a mutable list of elements of type `X`. +/// The class wraps and resizes an underyling array that holds the elements, +/// and thus is comparable to ArrayLists or Vectors in other languages. +/// +/// When required, the current state of a buffer object can be converted to a fixed-size array of its elements. +/// This is recommended for example when storing a buffer to a stable variable. +/// +/// Throughout this documentation, two terms come up that can be confused: `size` +/// and `capacity`. `size` is the length of the list that the buffer represents. +/// `capacity` is the length of the underyling array that backs this list. +/// `capacity` >= `size` is an invariant for this class. +/// +/// Like arrays, elements in the buffer are ordered by indices from 0 to `size`-1. +/// +/// WARNING: Certain operations are amortized O(1) time, such as `add`, but run +/// in worst case O(n) time. These worst case runtimes may exceed the cycles limit +/// per message if the size of the buffer is large enough. Grow these structures +/// with discretion. All amortized operations below also list the worst case runtime. +/// +/// Constructor: +/// The argument `initCapacity` determines the initial capacity of the array. +/// The underlying array grows by a factor of 1.5 when its current capacity is +/// exceeded. Further, when the size of the buffer shrinks to be less than 1/4th +/// of the capacity, the underyling array is shrunk by a factor of 2. +/// +/// Example: +/// ```motoko name=initialize +/// import Buffer "mo:base/Buffer"; +/// +/// let buffer = Buffer.Buffer(3); // Creates a new Buffer +/// ``` +/// +/// Runtime: O(initCapacity) +/// +/// Space: O(initCapacity) + +import Prim "mo:⛔"; +import Result "Result"; +import Order "Order"; +import Array "Array"; + +module { + type Order = Order.Order; + + // The following constants are used to manage the capacity. + // The length of `elements` is increased by `INCREASE_FACTOR` when capacity is reached. + // The length of `elements` is decreased by `DECREASE_FACTOR` when capacity is strictly less than + // `DECREASE_THRESHOLD`. + + // INCREASE_FACTOR = INCREASE_FACTOR_NUME / INCREASE_FACTOR_DENOM (with floating point division) + // Keep INCREASE_FACTOR low to minimize cycle limit problem + private let INCREASE_FACTOR_NUME = 3; + private let INCREASE_FACTOR_DENOM = 2; + private let DECREASE_THRESHOLD = 4; // Don't decrease capacity too early to avoid thrashing + private let DECREASE_FACTOR = 2; + private let DEFAULT_CAPACITY = 8; + + private func newCapacity(oldCapacity : Nat) : Nat { + if (oldCapacity == 0) { + 1 + } else { + // calculates ceil(oldCapacity * INCREASE_FACTOR) without floats + ((oldCapacity * INCREASE_FACTOR_NUME) + INCREASE_FACTOR_DENOM - 1) / INCREASE_FACTOR_DENOM + } + }; + + public class Buffer(initCapacity : Nat) = this { + var _size : Nat = 0; // avoid name clash with `size()` method + var elements : [var ?X] = Prim.Array_init(initCapacity, null); + + /// Returns the current number of elements in the buffer. + /// + /// Example: + /// ```motoko include=initialize + /// buffer.size() // => 0 + /// ``` + /// + /// Runtime: O(1) + /// + /// Space: O(1) + public func size() : Nat = _size; + + /// Adds a single element to the end of the buffer, doubling + /// the size of the array if capacity is exceeded. + /// + /// Example: + /// ```motoko include=initialize + /// + /// buffer.add(0); // add 0 to buffer + /// buffer.add(1); + /// buffer.add(2); + /// buffer.add(3); // causes underlying array to increase in capacity + /// Buffer.toArray(buffer) // => [0, 1, 2, 3] + /// ``` + /// + /// Amortized Runtime: O(1), Worst Case Runtime: O(size) + /// + /// Amortized Space: O(1), Worst Case Space: O(size) + public func add(element : X) { + if (_size == elements.size()) { + reserve(newCapacity(elements.size())) + }; + elements[_size] := ?element; + _size += 1 + }; + + /// Returns the element at index `index`. Traps if `index >= size`. Indexing is zero-based. + /// + /// Example: + /// ```motoko include=initialize + /// + /// buffer.add(10); + /// buffer.add(11); + /// buffer.get(0); // => 10 + /// ``` + /// + /// Runtime: O(1) + /// + /// Space: O(1) + public func get(index : Nat) : X { + switch (elements[index]) { + case (?element) element; + case null Prim.trap("Buffer index out of bounds in get") + } + }; + + /// Returns the element at index `index` as an option. + /// Returns `null` when `index >= size`. Indexing is zero-based. + /// + /// Example: + /// ```motoko include=initialize + /// + /// buffer.add(10); + /// buffer.add(11); + /// let x = buffer.getOpt(0); // => ?10 + /// let y = buffer.getOpt(2); // => null + /// ``` + /// + /// Runtime: O(1) + /// + /// Space: O(1) + public func getOpt(index : Nat) : ?X { + if (index < _size) { + elements[index] + } else { + null + } + }; + + /// Overwrites the current element at `index` with `element`. Traps if + /// `index` >= size. Indexing is zero-based. + /// + /// Example: + /// ```motoko include=initialize + /// + /// buffer.add(10); + /// buffer.put(0, 20); // overwrites 10 at index 0 with 20 + /// Buffer.toArray(buffer) // => [20] + /// ``` + /// + /// Runtime: O(1) + /// + /// Space: O(1) + public func put(index : Nat, element : X) { + if (index >= _size) { + Prim.trap "Buffer index out of bounds in put" + }; + elements[index] := ?element + }; + + /// Removes and returns the last item in the buffer or `null` if + /// the buffer is empty. + /// + /// Example: + /// ```motoko include=initialize + /// + /// buffer.add(10); + /// buffer.add(11); + /// buffer.removeLast(); // => ?11 + /// ``` + /// + /// Amortized Runtime: O(1), Worst Case Runtime: O(size) + /// + /// Amortized Space: O(1), Worst Case Space: O(size) + public func removeLast() : ?X { + if (_size == 0) { + return null + }; + + _size -= 1; + let lastElement = elements[_size]; + elements[_size] := null; + + if (_size < elements.size() / DECREASE_THRESHOLD) { + // FIXME should this new capacity be a function of _size + // instead of the current capacity? E.g. _size * INCREASE_FACTOR + reserve(elements.size() / DECREASE_FACTOR) + }; + + lastElement + }; + + /// Removes and returns the element at `index` from the buffer. + /// All elements with index > `index` are shifted one position to the left. + /// This may cause a downsizing of the array. + /// + /// Traps if index >= size. + /// + /// WARNING: Repeated removal of elements using this method is ineffecient + /// and might be a sign that you should consider a different data-structure + /// for your use case. + /// + /// Example: + /// ```motoko include=initialize + /// + /// buffer.add(10); + /// buffer.add(11); + /// buffer.add(12); + /// let x = buffer.remove(1); // evaluates to 11. 11 no longer in list. + /// Buffer.toArray(buffer) // => [10, 12] + /// ``` + /// + /// Runtime: O(size) + /// + /// Amortized Space: O(1), Worst Case Space: O(size) + public func remove(index : Nat) : X { + if (index >= _size) { + Prim.trap "Buffer index out of bounds in remove" + }; + + let element = elements[index]; + + // copy elements to new array and shift over in one pass + if ((_size - 1) : Nat < elements.size() / DECREASE_THRESHOLD) { + let elements2 = Prim.Array_init(elements.size() / DECREASE_FACTOR, null); + + var i = 0; + var j = 0; + label l while (i < _size) { + if (i == index) { + i += 1; + continue l + }; + + elements2[j] := elements[i]; + i += 1; + j += 1 + }; + elements := elements2 + } else { + // just shift over elements + var i = index; + while (i < (_size - 1 : Nat)) { + elements[i] := elements[i + 1]; + i += 1 + }; + elements[_size - 1] := null + }; + + _size -= 1; + + switch (element) { + case (?element) { + element + }; + case null { + Prim.trap "Malformed buffer in remove" + } + } + }; + + /// Resets the buffer. Capacity is set to 8. + /// + /// Example: + /// ```motoko include=initialize + /// + /// buffer.add(10); + /// buffer.add(11); + /// buffer.add(12); + /// buffer.clear(); // buffer is now empty + /// Buffer.toArray(buffer) // => [] + /// ``` + /// + /// Runtime: O(1) + /// + /// Space: O(1) + public func clear() { + _size := 0; + reserve(DEFAULT_CAPACITY) + }; + + /// Removes all elements from the buffer for which the predicate returns false. + /// The predicate is given both the index of the element and the element itself. + /// This may cause a downsizing of the array. + /// + /// Example: + /// ```motoko include=initialize + /// + /// buffer.add(10); + /// buffer.add(11); + /// buffer.add(12); + /// buffer.filterEntries(func(_, x) = x % 2 == 0); // only keep even elements + /// Buffer.toArray(buffer) // => [10, 12] + /// ``` + /// + /// Runtime: O(size) + /// + /// Amortized Space: O(1), Worst Case Space: O(size) + public func filterEntries(predicate : (Nat, X) -> Bool) { + var numRemoved = 0; + let keep = Prim.Array_tabulate( + _size, + func i { + switch (elements[i]) { + case (?element) { + if (predicate(i, element)) { + true + } else { + numRemoved += 1; + false + } + }; + case null { + Prim.trap "Malformed buffer in filter()" + } + } + } + ); + + let capacity = elements.size(); + + if ((_size - numRemoved : Nat) < capacity / DECREASE_THRESHOLD) { + let elements2 = Prim.Array_init(capacity / DECREASE_FACTOR, null); + + var i = 0; + var j = 0; + while (i < _size) { + if (keep[i]) { + elements2[j] := elements[i]; + i += 1; + j += 1 + } else { + i += 1 + } + }; + + elements := elements2 + } else { + var i = 0; + var j = 0; + while (i < _size) { + if (keep[i]) { + elements[j] := elements[i]; + i += 1; + j += 1 + } else { + i += 1 + } + }; + + while (j < _size) { + elements[j] := null; + j += 1 + } + }; + + _size -= numRemoved + }; + + /// Returns the capacity of the buffer (the length of the underlying array). + /// + /// Example: + /// ```motoko include=initialize + /// + /// let buffer = Buffer.Buffer(2); // underlying array has capacity 2 + /// buffer.add(10); + /// let c1 = buffer.capacity(); // => 2 + /// buffer.add(11); + /// buffer.add(12); // causes capacity to increase by factor of 1.5 + /// let c2 = buffer.capacity(); // => 3 + /// ``` + /// + /// Runtime: O(1) + /// + /// Space: O(1) + public func capacity() : Nat = elements.size(); + + /// Changes the capacity to `capacity`. Traps if `capacity` < `size`. + /// + /// ```motoko include=initialize + /// + /// buffer.reserve(4); + /// buffer.add(10); + /// buffer.add(11); + /// buffer.capacity(); // => 4 + /// ``` + /// + /// Runtime: O(capacity) + /// + /// Space: O(capacity) + public func reserve(capacity : Nat) { + if (capacity < _size) { + Prim.trap "capacity must be >= size in reserve" + }; + + let elements2 = Prim.Array_init(capacity, null); + + var i = 0; + while (i < _size) { + elements2[i] := elements[i]; + i += 1 + }; + elements := elements2 + }; + + /// Adds all elements in buffer `b` to this buffer. + /// + /// ```motoko include=initialize + /// let buffer1 = Buffer.Buffer(2); + /// let buffer2 = Buffer.Buffer(2); + /// buffer1.add(10); + /// buffer1.add(11); + /// buffer2.add(12); + /// buffer2.add(13); + /// buffer1.append(buffer2); // adds elements from buffer2 to buffer1 + /// Buffer.toArray(buffer1) // => [10, 11, 12, 13] + /// ``` + /// + /// Amortized Runtime: O(size2), Worst Case Runtime: O(size1 + size2) + /// + /// Amortized Space: O(1), Worst Case Space: O(size1 + size2) + public func append(buffer2 : Buffer) { + let size2 = buffer2.size(); + // Make sure you only allocate a new array at most once + if (_size + size2 > elements.size()) { + // FIXME would be nice to have a tabulate for var arrays here + reserve(newCapacity(_size + size2)) + }; + var i = 0; + while (i < size2) { + elements[_size + i] := buffer2.getOpt i; + i += 1 + }; + + _size += size2 + }; + + /// Inserts `element` at `index`, shifts all elements to the right of + /// `index` over by one index. Traps if `index` is greater than size. + /// + /// ```motoko include=initialize + /// let buffer1 = Buffer.Buffer(2); + /// let buffer2 = Buffer.Buffer(2); + /// buffer.add(10); + /// buffer.add(11); + /// buffer.insert(1, 9); + /// Buffer.toArray(buffer) // => [10, 9, 11] + /// ``` + /// + /// Runtime: O(size) + /// + /// Amortized Space: O(1), Worst Case Space: O(size) + public func insert(index : Nat, element : X) { + if (index > _size) { + Prim.trap "Buffer index out of bounds in insert" + }; + let capacity = elements.size(); + + if (_size + 1 > capacity) { + let capacity = elements.size(); + let elements2 = Prim.Array_init(newCapacity capacity, null); + var i = 0; + while (i < _size + 1) { + if (i < index) { + elements2[i] := elements[i] + } else if (i == index) { + elements2[i] := ?element + } else { + elements2[i] := elements[i - 1] + }; + + i += 1 + }; + elements := elements2 + } else { + var i : Nat = _size; + while (i > index) { + elements[i] := elements[i - 1]; + i -= 1 + }; + elements[index] := ?element + }; + + _size += 1 + }; + + /// Inserts `buffer2` at `index`, and shifts all elements to the right of + /// `index` over by size2. Traps if `index` is greater than size. + /// + /// ```motoko include=initialize + /// let buffer1 = Buffer.Buffer(2); + /// let buffer2 = Buffer.Buffer(2); + /// buffer1.add(10); + /// buffer1.add(11); + /// buffer2.add(12); + /// buffer2.add(13); + /// buffer1.insertBuffer(1, buffer2); + /// Buffer.toArray(buffer1) // => [10, 12, 13, 11] + /// ``` + /// + /// Runtime: O(size) + /// + /// Amortized Space: O(1), Worst Case Space: O(size1 + size2) + public func insertBuffer(index : Nat, buffer2 : Buffer) { + if (index > _size) { + Prim.trap "Buffer index out of bounds in insertBuffer" + }; + + let size2 = buffer2.size(); + let capacity = elements.size(); + + // copy elements to new array and shift over in one pass + if (_size + size2 > capacity) { + let elements2 = Prim.Array_init(newCapacity(_size + size2), null); + var i = 0; + for (element in elements.vals()) { + if (i == index) { + i += size2 + }; + elements2[i] := element; + i += 1 + }; + + i := 0; + while (i < size2) { + elements2[i + index] := buffer2.getOpt(i); + i += 1 + }; + elements := elements2 + } // just insert + else { + var i = index; + while (i < index + size2) { + if (i < _size) { + elements[i + size2] := elements[i] + }; + elements[i] := buffer2.getOpt(i - index); + + i += 1 + } + }; + + _size += size2 + }; + + /// Sorts the elements in the buffer according to `compare`. + /// Sort is deterministic, stable, and in-place. + /// + /// ```motoko include=initialize + /// + /// import Nat "mo:base/Nat"; + /// + /// buffer.add(11); + /// buffer.add(12); + /// buffer.add(10); + /// buffer.sort(Nat.compare); + /// Buffer.toArray(buffer) // => [10, 11, 12] + /// ``` + /// + /// Runtime: O(size * log(size)) + /// + /// Space: O(size) + public func sort(compare : (X, X) -> Order.Order) { + // Stable merge sort in a bottom-up iterative style + if (_size == 0) { + return + }; + let scratchSpace = Prim.Array_init(_size, null); + + let sizeDec = _size - 1 : Nat; + var currSize = 1; // current size of the subarrays being merged + // when the current size == size, the array has been merged into a single sorted array + while (currSize < _size) { + var leftStart = 0; // selects the current left subarray being merged + while (leftStart < sizeDec) { + let mid : Nat = if (leftStart + currSize - 1 : Nat < sizeDec) { + leftStart + currSize - 1 + } else { sizeDec }; + let rightEnd : Nat = if (leftStart + (2 * currSize) - 1 : Nat < sizeDec) { + leftStart + (2 * currSize) - 1 + } else { sizeDec }; + + // Merge subarrays elements[leftStart...mid] and elements[mid+1...rightEnd] + var left = leftStart; + var right = mid + 1; + var nextSorted = leftStart; + while (left < mid + 1 and right < rightEnd + 1) { + let leftOpt = elements[left]; + let rightOpt = elements[right]; + switch (leftOpt, rightOpt) { + case (?leftElement, ?rightElement) { + switch (compare(leftElement, rightElement)) { + case (#less or #equal) { + scratchSpace[nextSorted] := leftOpt; + left += 1 + }; + case (#greater) { + scratchSpace[nextSorted] := rightOpt; + right += 1 + } + } + }; + case (_, _) { + // only sorting non-null items + Prim.trap "Malformed buffer in sort" + } + }; + nextSorted += 1 + }; + while (left < mid + 1) { + scratchSpace[nextSorted] := elements[left]; + nextSorted += 1; + left += 1 + }; + while (right < rightEnd + 1) { + scratchSpace[nextSorted] := elements[right]; + nextSorted += 1; + right += 1 + }; + + // Copy over merged elements + var i = leftStart; + while (i < rightEnd + 1) { + elements[i] := scratchSpace[i]; + i += 1 + }; + + leftStart += 2 * currSize + }; + currSize *= 2 + } + }; + + /// Returns an Iterator (`Iter`) over the elements of this buffer. + /// Iterator provides a single method `next()`, which returns + /// elements in order, or `null` when out of elements to iterate over. + /// + /// ```motoko include=initialize + /// + /// buffer.add(10); + /// buffer.add(11); + /// buffer.add(12); + /// + /// var sum = 0; + /// for (element in buffer.vals()) { + /// sum += element; + /// }; + /// sum // => 33 + /// ``` + /// + /// Runtime: O(1) + /// + /// Space: O(1) + public func vals() : { next : () -> ?X } = object { + // FIXME either handle modification to underlying list + // or explicitly warn users in documentation + var nextIndex = 0; + public func next() : ?X { + if (nextIndex >= _size) { + return null + }; + let nextElement = elements[nextIndex]; + nextIndex += 1; + nextElement + } + }; + + // FOLLOWING METHODS ARE DEPRECATED + + /// @deprecated Use static library function instead. + public func clone() : Buffer { + let newBuffer = Buffer(elements.size()); + for (element in vals()) { + newBuffer.add(element) + }; + newBuffer + }; + + /// @deprecated Use static library function instead. + public func toArray() : [X] = + // immutable clone of array + Prim.Array_tabulate( + _size, + func(i : Nat) : X { get i } + ); + + /// @deprecated Use static library function instead. + public func toVarArray() : [var X] { + if (_size == 0) { [var] } else { + let newArray = Prim.Array_init(_size, get 0); + var i = 0; + for (element in vals()) { + newArray[i] := element; + i += 1 + }; + newArray + } + } + }; + + /// Returns true if and only if the buffer is empty. + /// + /// Example: + /// ```motoko include=initialize + /// buffer.add(2); + /// buffer.add(0); + /// buffer.add(3); + /// Buffer.isEmpty(buffer); // => false + /// ``` + /// + /// ```motoko include=initialize + /// Buffer.isEmpty(buffer); // => true + /// ``` + /// + /// Runtime: O(1) + /// + /// Space: O(1) + public func isEmpty(buffer : Buffer) : Bool = buffer.size() == 0; + + /// Returns true iff `buffer` contains `element` with respect to equality + /// defined by `equal`. + /// + /// + /// Example: + /// ```motoko include=initialize + /// import Nat "mo:base/Nat"; + /// + /// buffer.add(2); + /// buffer.add(0); + /// buffer.add(3); + /// Buffer.contains(buffer, 2, Nat.equal); // => true + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(1) + /// + /// *Runtime and space assumes that `equal` runs in O(1) time and space. + public func contains(buffer : Buffer, element : X, equal : (X, X) -> Bool) : Bool { + for (current in buffer.vals()) { + if (equal(current, element)) { + return true + } + }; + + false + }; + + /// Returns a copy of `buffer`, with the same capacity. + /// + /// + /// Example: + /// ```motoko include=initialize + /// + /// buffer.add(1); + /// + /// let clone = Buffer.clone(buffer); + /// Buffer.toArray(clone); // => [1] + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(size) + public func clone(buffer : Buffer) : Buffer { + let newBuffer = Buffer(buffer.capacity()); + for (element in buffer.vals()) { + newBuffer.add(element) + }; + newBuffer + }; + + /// Finds the greatest element in `buffer` defined by `compare`. + /// Returns `null` if `buffer` is empty. + /// + /// + /// Example: + /// ```motoko include=initialize + /// import Nat "mo:base/Nat"; + /// + /// buffer.add(1); + /// buffer.add(2); + /// + /// Buffer.max(buffer, Nat.compare); // => ?2 + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(1) + /// + /// *Runtime and space assumes that `compare` runs in O(1) time and space. + public func max(buffer : Buffer, compare : (X, X) -> Order) : ?X { + if (buffer.size() == 0) { + return null + }; + + var maxSoFar = buffer.get(0); + for (current in buffer.vals()) { + switch (compare(current, maxSoFar)) { + case (#greater) { + maxSoFar := current + }; + case _ {} + } + }; + + ?maxSoFar + }; + + /// Finds the least element in `buffer` defined by `compare`. + /// Returns `null` if `buffer` is empty. + /// + /// Example: + /// ```motoko include=initialize + /// import Nat "mo:base/Nat"; + /// + /// buffer.add(1); + /// buffer.add(2); + /// + /// Buffer.min(buffer, Nat.compare); // => ?1 + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(1) + /// + /// *Runtime and space assumes that `compare` runs in O(1) time and space. + public func min(buffer : Buffer, compare : (X, X) -> Order) : ?X { + if (buffer.size() == 0) { + return null + }; + + var minSoFar = buffer.get(0); + for (current in buffer.vals()) { + switch (compare(current, minSoFar)) { + case (#less) { + minSoFar := current + }; + case _ {} + } + }; + + ?minSoFar + }; + + /// Defines equality for two buffers, using `equal` to recursively compare elements in the + /// buffers. Returns true iff the two buffers are of the same size, and `equal` + /// evaluates to true for every pair of elements in the two buffers of the same + /// index. + /// + /// + /// Example: + /// ```motoko include=initialize + /// import Nat "mo:base/Nat"; + /// + /// let buffer1 = Buffer.Buffer(2); + /// buffer1.add(1); + /// buffer1.add(2); + /// + /// let buffer2 = Buffer.Buffer(5); + /// buffer2.add(1); + /// buffer2.add(2); + /// + /// Buffer.equal(buffer1, buffer2, Nat.equal); // => true + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(1) + /// + /// *Runtime and space assumes that `equal` runs in O(1) time and space. + public func equal(buffer1 : Buffer, buffer2 : Buffer, equal : (X, X) -> Bool) : Bool { + let size1 = buffer1.size(); + + if (size1 != buffer2.size()) { + return false + }; + + var i = 0; + while (i < size1) { + if (not equal(buffer1.get(i), buffer2.get(i))) { + return false + }; + i += 1 + }; + + true + }; + + /// Defines comparison for two buffers, using `compare` to recursively compare elements in the + /// buffers. Comparison is defined lexicographically. + /// + /// + /// Example: + /// ```motoko include=initialize + /// import Nat "mo:base/Nat"; + /// + /// let buffer1 = Buffer.Buffer(2); + /// buffer1.add(1); + /// buffer1.add(2); + /// + /// let buffer2 = Buffer.Buffer(3); + /// buffer2.add(3); + /// buffer2.add(4); + /// + /// Buffer.compare(buffer1, buffer2, Nat.compare); // => #less + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(1) + /// + /// *Runtime and space assumes that `compare` runs in O(1) time and space. + public func compare(buffer1 : Buffer, buffer2 : Buffer, compare : (X, X) -> Order.Order) : Order.Order { + let size1 = buffer1.size(); + let size2 = buffer2.size(); + let minSize = if (size1 < size2) { size1 } else { size2 }; + + var i = 0; + while (i < minSize) { + switch (compare(buffer1.get(i), buffer2.get(i))) { + case (#less) { + return #less + }; + case (#greater) { + return #greater + }; + case _ {} + }; + i += 1 + }; + + if (size1 < size2) { + #less + } else if (size1 == size2) { + #equal + } else { + #greater + } + }; + + /// Creates a textual representation of `buffer`, using `toText` to recursively + /// convert the elements into Text. + /// + /// Example: + /// ```motoko include=initialize + /// import Nat "mo:base/Nat"; + /// + /// buffer.add(1); + /// buffer.add(2); + /// buffer.add(3); + /// buffer.add(4); + /// + /// Buffer.toText(buffer, Nat.toText); // => "[1, 2, 3, 4]" + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(size) + /// + /// *Runtime and space assumes that `toText` runs in O(1) time and space. + public func toText(buffer : Buffer, toText : X -> Text) : Text { + let size : Int = buffer.size(); + var i = 0; + var text = ""; + while (i < size - 1) { + text := text # toText(buffer.get(i)) # ", "; // Text implemented as rope + i += 1 + }; + if (size > 0) { + // avoid the trailing comma + text := text # toText(buffer.get(i)) + }; + + "[" # text # "]" + }; + + /// Hashes `buffer` using `hash` to hash the underlying elements. + /// The deterministic hash function is a function of the elements in the Buffer, as well + /// as their ordering. + /// + /// Example: + /// ```motoko include=initialize + /// import Hash "mo:base/Hash"; + /// + /// buffer.add(1); + /// buffer.add(2); + /// buffer.add(3); + /// buffer.add(1000); + /// + /// Buffer.hash(buffer, Hash.hash); // => 2_872_640_342 + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(1) + /// + /// *Runtime and space assumes that `hash` runs in O(1) time and space. + public func hash(buffer : Buffer, hash : X -> Nat32) : Nat32 { + let size = buffer.size(); + var i = 0; + var accHash : Nat32 = 0; + + while (i < size) { + accHash := Prim.intToNat32Wrap(i) ^ accHash ^ hash(buffer.get(i)); + i += 1 + }; + + accHash + }; + + /// Finds the first index of `element` in `buffer` using equality of elements defined + /// by `equal`. Returns `null` if `element` is not found. + /// + /// Example: + /// ```motoko include=initialize + /// import Nat "mo:base/Nat"; + /// + /// buffer.add(1); + /// buffer.add(2); + /// buffer.add(3); + /// buffer.add(4); + /// + /// Buffer.indexOf(3, buffer, Nat.equal); // => ?2 + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(size) + /// + /// *Runtime and space assumes that `equal` runs in O(1) time and space. + public func indexOf(element : X, buffer : Buffer, equal : (X, X) -> Bool) : ?Nat { + let size = buffer.size(); + var i = 0; + while (i < size) { + if (equal(buffer.get(i), element)) { + return ?i + }; + i += 1 + }; + + null + }; + + /// Finds the last index of `element` in `buffer` using equality of elements defined + /// by `equal`. Returns `null` if `element` is not found. + /// + /// Example: + /// ```motoko include=initialize + /// import Nat "mo:base/Nat"; + /// + /// buffer.add(1); + /// buffer.add(2); + /// buffer.add(3); + /// buffer.add(4); + /// buffer.add(2); + /// buffer.add(2); + /// + /// Buffer.lastIndexOf(2, buffer, Nat.equal); // => ?5 + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(size) + /// + /// *Runtime and space assumes that `equal` runs in O(1) time and space. + public func lastIndexOf(element : X, buffer : Buffer, equal : (X, X) -> Bool) : ?Nat { + let size = buffer.size(); + if (size == 0) { + return null + }; + var i = size; + while (i >= 1) { + i -= 1; + if (equal(buffer.get(i), element)) { + return ?i + } + }; + + null + }; + + /// Searches for `subBuffer` in `buffer`, and returns the starting index if it is found. + /// + /// Example: + /// ```motoko include=initialize + /// import Nat "mo:base/Nat"; + /// + /// buffer.add(1); + /// buffer.add(2); + /// buffer.add(3); + /// buffer.add(4); + /// buffer.add(5); + /// buffer.add(6); + /// + /// let sub = Buffer.Buffer(2); + /// sub.add(4); + /// sub.add(5); + /// sub.add(6); + /// + /// Buffer.indexOfBuffer(sub, buffer, Nat.equal); // => ?3 + /// ``` + /// + /// Runtime: O(size of buffer + size of subBuffer) + /// + /// Space: O(size of subBuffer) + /// + /// *Runtime and space assumes that `equal` runs in O(1) time and space. + public func indexOfBuffer(subBuffer : Buffer, buffer : Buffer, equal : (X, X) -> Bool) : ?Nat { + // Uses the KMP substring search algorithm + // Implementation from: https://www.educative.io/answers/what-is-the-knuth-morris-pratt-algorithm + let size = buffer.size(); + let subSize = subBuffer.size(); + if (subSize > size or subSize == 0) { + return null + }; + + // precompute lps + let lps = Prim.Array_init(subSize, 0); + var i = 0; + var j = 1; + + while (j < subSize) { + if (equal(subBuffer.get(i), subBuffer.get(j))) { + i += 1; + lps[j] := i; + j += 1 + } else if (i == 0) { + lps[j] := 0; + j += 1 + } else { + i := lps[i - 1] + } + }; + + // start search + i := 0; + j := 0; + let subSizeDec = subSize - 1 : Nat; // hoisting loop invariant + while (i < subSize and j < size) { + if (equal(subBuffer.get(i), buffer.get(j)) and i == subSizeDec) { + return ?(j - i) + } else if (equal(subBuffer.get(i), buffer.get(j))) { + i += 1; + j += 1 + } else { + if (i != 0) { + i := lps[i - 1] + } else { + j += 1 + } + } + }; + + null + }; + + /// Similar to indexOf, but runs in logarithmic time. Assumes that `buffer` is sorted. + /// Behavior is undefined if `buffer` is not sorted. Uses `compare` to + /// perform the search. Returns an index of `element` if it is found. + /// + /// Example: + /// ```motoko include=initialize + /// import Nat "mo:base/Nat"; + /// + /// buffer.add(1); + /// buffer.add(4); + /// buffer.add(5); + /// buffer.add(6); + /// + /// Buffer.binarySearch(5, buffer, Nat.compare); // => ?2 + /// ``` + /// + /// Runtime: O(log(size)) + /// + /// Space: O(1) + /// + /// *Runtime and space assumes that `compare` runs in O(1) time and space. + public func binarySearch(element : X, buffer : Buffer, compare : (X, X) -> Order.Order) : ?Nat { + var low = 0; + var high = buffer.size(); + + while (low < high) { + let mid = (low + high) / 2; + let current = buffer.get(mid); + switch (compare(element, current)) { + case (#equal) { + return ?mid + }; + case (#less) { + high := mid + }; + case (#greater) { + low := mid + 1 + } + } + }; + + null + }; + + /// Returns the sub-buffer of `buffer` starting at index `start` + /// of length `length`. Traps if `start` is out of bounds, or `start + length` + /// is greater than the size of `buffer`. + /// + /// Example: + /// ```motoko include=initialize + /// import Nat "mo:base/Nat"; + /// + /// buffer.add(1); + /// buffer.add(2); + /// buffer.add(3); + /// buffer.add(4); + /// buffer.add(5); + /// buffer.add(6); + /// + /// let sub = Buffer.subBuffer(buffer, 3, 2); + /// Buffer.toText(sub, Nat.toText); // => [4, 5] + /// ``` + /// + /// Runtime: O(length) + /// + /// Space: O(length) + public func subBuffer(buffer : Buffer, start : Nat, length : Nat) : Buffer { + let size = buffer.size(); + let end = start + length; // exclusive + if (start >= size or end > size) { + Prim.trap "Buffer index out of bounds in subBuffer" + }; + + let newBuffer = Buffer(newCapacity length); + + var i = start; + while (i < end) { + newBuffer.add(buffer.get(i)); + + i += 1 + }; + + newBuffer + }; + + /// Checks if `subBuffer` is a sub-Buffer of `buffer`. Uses `equal` to + /// compare elements. + /// + /// Example: + /// ```motoko include=initialize + /// import Nat "mo:base/Nat"; + /// + /// buffer.add(1); + /// buffer.add(2); + /// buffer.add(3); + /// buffer.add(4); + /// buffer.add(5); + /// buffer.add(6); + /// + /// let sub = Buffer.Buffer(2); + /// sub.add(2); + /// sub.add(3); + /// Buffer.isSubBufferOf(sub, buffer, Nat.equal); // => true + /// ``` + /// + /// Runtime: O(size of subBuffer + size of buffer) + /// + /// Space: O(size of subBuffer) + /// + /// *Runtime and space assumes that `equal` runs in O(1) time and space. + public func isSubBufferOf(subBuffer : Buffer, buffer : Buffer, equal : (X, X) -> Bool) : Bool { + switch (indexOfBuffer(subBuffer, buffer, equal)) { + case null subBuffer.size() == 0; + case _ true + } + }; + + /// Checks if `subBuffer` is a strict subBuffer of `buffer`, i.e. `subBuffer` must be + /// strictly contained inside both the first and last indices of `buffer`. + /// Uses `equal` to compare elements. + /// + /// Example: + /// ```motoko include=initialize + /// import Nat "mo:base/Nat"; + /// + /// buffer.add(1); + /// buffer.add(2); + /// buffer.add(3); + /// buffer.add(4); + /// + /// let sub = Buffer.Buffer(2); + /// sub.add(2); + /// sub.add(3); + /// Buffer.isStrictSubBufferOf(sub, buffer, Nat.equal); // => true + /// ``` + /// + /// Runtime: O(size of subBuffer + size of buffer) + /// + /// Space: O(size of subBuffer) + /// + /// *Runtime and space assumes that `equal` runs in O(1) time and space. + public func isStrictSubBufferOf(subBuffer : Buffer, buffer : Buffer, equal : (X, X) -> Bool) : Bool { + let subBufferSize = subBuffer.size(); + + switch (indexOfBuffer(subBuffer, buffer, equal)) { + case (?index) { + index != 0 and index != (buffer.size() - subBufferSize : Nat) // enforce strictness + }; + case null { + subBufferSize == 0 and subBufferSize != buffer.size() + } + } + }; + + /// Returns the prefix of `buffer` of length `length`. Traps if `length` + /// is greater than the size of `buffer`. + /// + /// Example: + /// ```motoko include=initialize + /// import Nat "mo:base/Nat"; + /// + /// buffer.add(1); + /// buffer.add(2); + /// buffer.add(3); + /// buffer.add(4); + /// + /// let pre = Buffer.prefix(buffer, 3); // => [1, 2, 3] + /// Buffer.toText(pre, Nat.toText); + /// ``` + /// + /// Runtime: O(length) + /// + /// Space: O(length) + public func prefix(buffer : Buffer, length : Nat) : Buffer { + let size = buffer.size(); + if (length > size) { + Prim.trap "Buffer index out of bounds in prefix" + }; + + let newBuffer = Buffer(newCapacity length); + + var i = 0; + while (i < length) { + newBuffer.add(buffer.get(i)); + i += 1 + }; + + newBuffer + }; + + /// Checks if `prefix` is a prefix of `buffer`. Uses `equal` to + /// compare elements. + /// + /// Example: + /// ```motoko include=initialize + /// import Nat "mo:base/Nat"; + /// + /// buffer.add(1); + /// buffer.add(2); + /// buffer.add(3); + /// buffer.add(4); + /// + /// let pre = Buffer.Buffer(2); + /// pre.add(1); + /// pre.add(2); + /// Buffer.isPrefixOf(pre, buffer, Nat.equal); // => true + /// ``` + /// + /// Runtime: O(size of prefix) + /// + /// Space: O(size of prefix) + /// + /// *Runtime and space assumes that `equal` runs in O(1) time and space. + public func isPrefixOf(prefix : Buffer, buffer : Buffer, equal : (X, X) -> Bool) : Bool { + let sizePrefix = prefix.size(); + if (buffer.size() < sizePrefix) { + return false + }; + + var i = 0; + while (i < sizePrefix) { + if (not equal(buffer.get(i), prefix.get(i))) { + return false + }; + + i += 1 + }; + + return true + }; + + /// Checks if `prefix` is a strict prefix of `buffer`. Uses `equal` to + /// compare elements. + /// + /// Example: + /// ```motoko include=initialize + /// import Nat "mo:base/Nat"; + /// + /// buffer.add(1); + /// buffer.add(2); + /// buffer.add(3); + /// buffer.add(4); + /// + /// let pre = Buffer.Buffer(3); + /// pre.add(1); + /// pre.add(2); + /// pre.add(3); + /// Buffer.isStrictPrefixOf(pre, buffer, Nat.equal); // => true + /// ``` + /// + /// Runtime: O(size of prefix) + /// + /// Space: O(size of prefix) + /// + /// *Runtime and space assumes that `equal` runs in O(1) time and space. + public func isStrictPrefixOf(prefix : Buffer, buffer : Buffer, equal : (X, X) -> Bool) : Bool { + if (buffer.size() <= prefix.size()) { + return false + }; + isPrefixOf(prefix, buffer, equal) + }; + + /// Returns the suffix of `buffer` of length `length`. + /// Traps if `length`is greater than the size of `buffer`. + /// + /// Example: + /// ```motoko include=initialize + /// import Nat "mo:base/Nat"; + /// + /// buffer.add(1); + /// buffer.add(2); + /// buffer.add(3); + /// buffer.add(4); + /// + /// let suf = Buffer.suffix(buffer, 3); // => [2, 3, 4] + /// Buffer.toText(suf, Nat.toText); + /// ``` + /// + /// Runtime: O(length) + /// + /// Space: O(length) + public func suffix(buffer : Buffer, length : Nat) : Buffer { + let size = buffer.size(); + + if (length > size) { + Prim.trap "Buffer index out of bounds in suffix" + }; + + let newBuffer = Buffer(newCapacity length); + + var i = size - length : Nat; + while (i < size) { + newBuffer.add(buffer.get(i)); + + i += 1 + }; + + newBuffer + }; + + /// Checks if `suffix` is a suffix of `buffer`. Uses `equal` to compare + /// elements. + /// + /// Example: + /// ```motoko include=initialize + /// import Nat "mo:base/Nat"; + /// + /// buffer.add(1); + /// buffer.add(2); + /// buffer.add(3); + /// buffer.add(4); + /// + /// let suf = Buffer.Buffer(3); + /// suf.add(2); + /// suf.add(3); + /// suf.add(4); + /// Buffer.isSuffixOf(suf, buffer, Nat.equal); // => true + /// ``` + /// + /// Runtime: O(length of suffix) + /// + /// Space: O(length of suffix) + /// + /// *Runtime and space assumes that `equal` runs in O(1) time and space. + public func isSuffixOf(suffix : Buffer, buffer : Buffer, equal : (X, X) -> Bool) : Bool { + let suffixSize = suffix.size(); + let bufferSize = buffer.size(); + if (bufferSize < suffixSize) { + return false + }; + + var i = bufferSize; + var j = suffixSize; + while (i >= 1 and j >= 1) { + i -= 1; + j -= 1; + if (not equal(buffer.get(i), suffix.get(j))) { + return false + } + }; + + return true + }; + + /// Checks if `suffix` is a strict suffix of `buffer`. Uses `equal` to compare + /// elements. + /// + /// Example: + /// ```motoko include=initialize + /// import Nat "mo:base/Nat"; + /// + /// buffer.add(1); + /// buffer.add(2); + /// buffer.add(3); + /// buffer.add(4); + /// + /// let suf = Buffer.Buffer(3); + /// suf.add(2); + /// suf.add(3); + /// suf.add(4); + /// Buffer.isStrictSuffixOf(suf, buffer, Nat.equal); // => true + /// ``` + /// + /// Runtime: O(length of suffix) + /// + /// Space: O(length of suffix) + /// + /// *Runtime and space assumes that `equal` runs in O(1) time and space. + public func isStrictSuffixOf(suffix : Buffer, buffer : Buffer, equal : (X, X) -> Bool) : Bool { + if (buffer.size() <= suffix.size()) { + return false + }; + isSuffixOf(suffix, buffer, equal) + }; + + /// Returns true iff every element in `buffer` satisfies `predicate`. + /// + /// Example: + /// ```motoko include=initialize + /// + /// buffer.add(2); + /// buffer.add(3); + /// buffer.add(4); + /// + /// Buffer.forAll(buffer, func x { x > 1 }); // => true + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(1) + /// + /// *Runtime and space assumes that `predicate` runs in O(1) time and space. + public func forAll(buffer : Buffer, predicate : X -> Bool) : Bool { + for (element in buffer.vals()) { + if (not predicate element) { + return false + } + }; + + true + }; + + /// Returns true iff some element in `buffer` satisfies `predicate`. + /// + /// Example: + /// ```motoko include=initialize + /// + /// buffer.add(2); + /// buffer.add(3); + /// buffer.add(4); + /// + /// Buffer.forSome(buffer, func x { x > 3 }); // => true + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(1) + /// + /// *Runtime and space assumes that `predicate` runs in O(1) time and space. + public func forSome(buffer : Buffer, predicate : X -> Bool) : Bool { + for (element in buffer.vals()) { + if (predicate element) { + return true + } + }; + + false + }; + + /// Returns true iff no element in `buffer` satisfies `predicate`. + /// + /// Example: + /// ```motoko include=initialize + /// + /// buffer.add(2); + /// buffer.add(3); + /// buffer.add(4); + /// + /// Buffer.forNone(buffer, func x { x == 0 }); // => true + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(1) + /// + /// *Runtime and space assumes that `predicate` runs in O(1) time and space. + public func forNone(buffer : Buffer, predicate : X -> Bool) : Bool { + for (element in buffer.vals()) { + if (predicate element) { + return false + } + }; + + true + }; + + /// Creates an array containing elements from `buffer`. + /// + /// Example: + /// ```motoko include=initialize + /// + /// buffer.add(1); + /// buffer.add(2); + /// buffer.add(3); + /// + /// Buffer.toArray(buffer); // => [1, 2, 3] + /// + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(size) + public func toArray(buffer : Buffer) : [X] = + // immutable clone of array + Prim.Array_tabulate( + buffer.size(), + func(i : Nat) : X { buffer.get(i) } + ); + + /// Creates a mutable array containing elements from `buffer`. + /// + /// Example: + /// ```motoko include=initialize + /// + /// buffer.add(1); + /// buffer.add(2); + /// buffer.add(3); + /// + /// Buffer.toVarArray(buffer); // => [1, 2, 3] + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(size) + public func toVarArray(buffer : Buffer) : [var X] { + let size = buffer.size(); + if (size == 0) { [var] } else { + let newArray = Prim.Array_init(size, buffer.get(0)); + var i = 1; + while (i < size) { + newArray[i] := buffer.get(i); + i += 1 + }; + newArray + } + }; + + /// Creates a buffer containing elements from `array`. + /// + /// Example: + /// ```motoko include=initialize + /// import Nat "mo:base/Nat"; + /// + /// let array = [2, 3]; + /// + /// let buf = Buffer.fromArray(array); // => [2, 3] + /// Buffer.toText(buf, Nat.toText); + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(size) + public func fromArray(array : [X]) : Buffer { + // When returning new buffer, if possible, set the capacity + // to the capacity of the old buffer. Otherwise, return them + // at 2/3 capacity (like in this case). Alternative is to + // calculate what the size would be if the elements were + // sequentially added using `add`. This current strategy (2/3) + // is the upper bound of that calculation (if the last element + // added caused a capacity increase). + let newBuffer = Buffer(newCapacity(array.size())); + + for (element in array.vals()) { + newBuffer.add(element) + }; + + newBuffer + }; + + /// Creates a buffer containing elements from `array`. + /// + /// Example: + /// ```motoko include=initialize + /// import Nat "mo:base/Nat"; + /// + /// let array = [var 1, 2, 3]; + /// + /// let buf = Buffer.fromVarArray(array); // => [1, 2, 3] + /// Buffer.toText(buf, Nat.toText); + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(size) + public func fromVarArray(array : [var X]) : Buffer { + let newBuffer = Buffer(newCapacity(array.size())); + + for (element in array.vals()) { + newBuffer.add(element) + }; + + newBuffer + }; + + /// Creates a buffer containing elements from `iter`. + /// + /// Example: + /// ```motoko include=initialize + /// import Nat "mo:base/Nat"; + /// + /// let array = [1, 1, 1]; + /// let iter = array.vals(); + /// + /// let buf = Buffer.fromIter(iter); // => [1, 1, 1] + /// Buffer.toText(buf, Nat.toText); + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(size) + public func fromIter(iter : { next : () -> ?X }) : Buffer { + let newBuffer = Buffer(DEFAULT_CAPACITY); // can't get size from `iter` + + for (element in iter) { + newBuffer.add(element) + }; + + newBuffer + }; + + /// Reallocates the array underlying `buffer` such that capacity == size. + /// + /// Example: + /// ```motoko include=initialize + /// + /// let buffer = Buffer.Buffer(10); + /// buffer.add(1); + /// buffer.add(2); + /// buffer.add(3); + /// + /// Buffer.trimToSize(buffer); + /// buffer.capacity(); // => 3 + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(size) + public func trimToSize(buffer : Buffer) { + let size = buffer.size(); + if (size < buffer.capacity()) { + buffer.reserve(size) + } + }; + + /// Creates a new buffer by applying `f` to each element in `buffer`. + /// + /// Example: + /// ```motoko include=initialize + /// import Nat "mo:base/Nat"; + /// + /// buffer.add(1); + /// buffer.add(2); + /// buffer.add(3); + /// + /// let newBuf = Buffer.map(buffer, func (x) { x + 1 }); + /// Buffer.toText(newBuf, Nat.toText); // => [2, 3, 4] + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(size) + /// + /// *Runtime and space assumes that `f` runs in O(1) time and space. + public func map(buffer : Buffer, f : X -> Y) : Buffer { + let newBuffer = Buffer(buffer.capacity()); + + for (element in buffer.vals()) { + newBuffer.add(f element) + }; + + newBuffer + }; + + /// Applies `f` to each element in `buffer`. + /// + /// Example: + /// ```motoko include=initialize + /// import Nat "mo:base/Nat"; + /// import Debug "mo:base/Debug"; + /// + /// buffer.add(1); + /// buffer.add(2); + /// buffer.add(3); + /// + /// Buffer.iterate(buffer, func (x) { + /// Debug.print(Nat.toText(x)); // prints each element in buffer + /// }); + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(size) + /// + /// *Runtime and space assumes that `f` runs in O(1) time and space. + public func iterate(buffer : Buffer, f : X -> ()) { + for (element in buffer.vals()) { + f element + } + }; + + /// Applies `f` to each element in `buffer` and its index. + /// + /// Example: + /// ```motoko include=initialize + /// import Nat "mo:base/Nat"; + /// + /// buffer.add(1); + /// buffer.add(2); + /// buffer.add(3); + /// + /// let newBuf = Buffer.mapEntries(buffer, func (x, i) { x + i + 1 }); + /// Buffer.toText(newBuf, Nat.toText); // => [2, 4, 6] + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(size) + /// + /// *Runtime and space assumes that `f` runs in O(1) time and space. + public func mapEntries(buffer : Buffer, f : (Nat, X) -> Y) : Buffer { + let newBuffer = Buffer(buffer.capacity()); + + var i = 0; + let size = buffer.size(); + while (i < size) { + newBuffer.add(f(i, buffer.get(i))); + i += 1 + }; + + newBuffer + }; + + /// Creates a new buffer by applying `f` to each element in `buffer`, + /// and keeping all non-null elements. + /// + /// Example: + /// ```motoko include=initialize + /// import Nat "mo:base/Nat"; + /// + /// buffer.add(1); + /// buffer.add(2); + /// buffer.add(3); + /// + /// let newBuf = Buffer.mapFilter(buffer, func (x) { + /// if (x > 1) { + /// ?(x * 2); + /// } else { + /// null; + /// } + /// }); + /// Buffer.toText(newBuf, Nat.toText); // => [4, 6] + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(size) + /// + /// *Runtime and space assumes that `f` runs in O(1) time and space. + public func mapFilter(buffer : Buffer, f : X -> ?Y) : Buffer { + let newBuffer = Buffer(buffer.capacity()); + + for (element in buffer.vals()) { + switch (f element) { + case (?element) { + newBuffer.add(element) + }; + case _ {} + } + }; + + newBuffer + }; + + /// Creates a new buffer by applying `f` to each element in `buffer`. + /// If any invocation of `f` produces an `#err`, returns an `#err`. Otherwise + /// Returns an `#ok` containing the new buffer. + /// + /// Example: + /// ```motoko include=initialize + /// import Result "mo:base/Result"; + /// + /// buffer.add(1); + /// buffer.add(2); + /// buffer.add(3); + /// + /// let result = Buffer.mapResult(buffer, func (k) { + /// if (k > 0) { + /// #ok(k); + /// } else { + /// #err("One or more elements are zero."); + /// } + /// }); + /// + /// Result.mapOk, [Nat], Text>(result, func buffer = Buffer.toArray(buffer)) // => #ok([1, 2, 3]) + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(size) + /// + /// *Runtime and space assumes that `f` runs in O(1) time and space. + public func mapResult(buffer : Buffer, f : X -> Result.Result) : Result.Result, E> { + let newBuffer = Buffer(buffer.capacity()); + + for (element in buffer.vals()) { + switch (f element) { + case (#ok result) { + newBuffer.add(result) + }; + case (#err e) { + return #err e + } + } + }; + + #ok newBuffer + }; + + /// Creates a new buffer by applying `k` to each element in `buffer`, + /// and concatenating the resulting buffers in order. This operation + /// is similar to what in other functional languages is known as monadic bind. + /// + /// Example: + /// ```motoko include=initialize + /// import Nat "mo:base/Nat"; + /// + /// buffer.add(1); + /// buffer.add(2); + /// buffer.add(3); + /// + /// let chain = Buffer.chain(buffer, func (x) { + /// let b = Buffer.Buffer(2); + /// b.add(x); + /// b.add(x * 2); + /// return b; + /// }); + /// Buffer.toText(chain, Nat.toText); // => [1, 2, 2, 4, 3, 6] + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(size) + /// + /// *Runtime and space assumes that `k` runs in O(1) time and space. + public func chain(buffer : Buffer, k : X -> Buffer) : Buffer { + let newBuffer = Buffer(buffer.size() * 4); + + for (element in buffer.vals()) { + newBuffer.append(k element) + }; + + newBuffer + }; + + /// Collapses the elements in `buffer` into a single value by starting with `base` + /// and progessively combining elements into `base` with `combine`. Iteration runs + /// left to right. + /// + /// Example: + /// ```motoko include=initialize + /// import Nat "mo:base/Nat"; + /// + /// buffer.add(1); + /// buffer.add(2); + /// buffer.add(3); + /// + /// Buffer.foldLeft(buffer, "", func (acc, x) { acc # Nat.toText(x)}); // => "123" + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(1) + /// + /// *Runtime and space assumes that `combine` runs in O(1) time and space. + public func foldLeft(buffer : Buffer, base : A, combine : (A, X) -> A) : A { + var accumulation = base; + + for (element in buffer.vals()) { + accumulation := combine(accumulation, element) + }; + + accumulation + }; + + /// Collapses the elements in `buffer` into a single value by starting with `base` + /// and progessively combining elements into `base` with `combine`. Iteration runs + /// right to left. + /// + /// Example: + /// ```motoko include=initialize + /// import Nat "mo:base/Nat"; + /// + /// buffer.add(1); + /// buffer.add(2); + /// buffer.add(3); + /// + /// Buffer.foldRight(buffer, "", func (x, acc) { Nat.toText(x) # acc }); // => "123" + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(1) + /// + /// *Runtime and space assumes that `combine` runs in O(1) time and space. + public func foldRight(buffer : Buffer, base : A, combine : (X, A) -> A) : A { + let size = buffer.size(); + if (size == 0) { + return base + }; + var accumulation = base; + + var i = size; + while (i >= 1) { + i -= 1; // to avoid Nat underflow, subtract first and stop iteration at 1 + accumulation := combine(buffer.get(i), accumulation) + }; + + accumulation + }; + + /// Returns the first element of `buffer`. Traps if `buffer` is empty. + /// + /// Example: + /// ```motoko include=initialize + /// + /// buffer.add(1); + /// buffer.add(2); + /// buffer.add(3); + /// + /// Buffer.first(buffer); // => 1 + /// ``` + /// + /// Runtime: O(1) + /// + /// Space: O(1) + public func first(buffer : Buffer) : X = buffer.get(0); + + /// Returns the last element of `buffer`. Traps if `buffer` is empty. + /// + /// Example: + /// ```motoko include=initialize + /// + /// buffer.add(1); + /// buffer.add(2); + /// buffer.add(3); + /// + /// Buffer.last(buffer); // => 3 + /// ``` + /// + /// Runtime: O(1) + /// + /// Space: O(1) + public func last(buffer : Buffer) : X = buffer.get(buffer.size() - 1); + + /// Returns a new buffer with capacity and size 1, containing `element`. + /// + /// Example: + /// ```motoko include=initialize + /// import Nat "mo:base/Nat"; + /// + /// let buffer = Buffer.make(1); + /// Buffer.toText(buffer, Nat.toText); // => [1] + /// ``` + /// + /// Runtime: O(1) + /// + /// Space: O(1) + public func make(element : X) : Buffer { + let newBuffer = Buffer(1); + newBuffer.add(element); + newBuffer + }; + + /// Reverses the order of elements in `buffer`. + /// + /// Example: + /// ```motoko include=initialize + /// import Nat "mo:base/Nat"; + /// + /// buffer.add(1); + /// buffer.add(2); + /// buffer.add(3); + /// + /// Buffer.reverse(buffer); + /// Buffer.toText(buffer, Nat.toText); // => [3, 2, 1] + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(1) + public func reverse(buffer : Buffer) { + let size = buffer.size(); + if (size == 0) { + return + }; + + var i = 0; + var j = size - 1 : Nat; + var temp = buffer.get(0); + while (i < size / 2) { + temp := buffer.get(j); + buffer.put(j, buffer.get(i)); + buffer.put(i, temp); + i += 1; + j -= 1 + } + }; + + /// Merges two sorted buffers into a single sorted buffer, using `compare` to define + /// the ordering. The final ordering is stable. Behavior is undefined if either + /// `buffer1` or `buffer2` is not sorted. + /// + /// Example: + /// ```motoko include=initialize + /// import Nat "mo:base/Nat"; + /// + /// let buffer1 = Buffer.Buffer(2); + /// buffer1.add(1); + /// buffer1.add(2); + /// buffer1.add(4); + /// + /// let buffer2 = Buffer.Buffer(2); + /// buffer2.add(2); + /// buffer2.add(4); + /// buffer2.add(6); + /// + /// let merged = Buffer.merge(buffer1, buffer2, Nat.compare); + /// Buffer.toText(merged, Nat.toText); // => [1, 2, 2, 4, 4, 6] + /// ``` + /// + /// Runtime: O(size1 + size2) + /// + /// Space: O(size1 + size2) + /// + /// *Runtime and space assumes that `compare` runs in O(1) time and space. + public func merge(buffer1 : Buffer, buffer2 : Buffer, compare : (X, X) -> Order) : Buffer { + let size1 = buffer1.size(); + let size2 = buffer2.size(); + + let newBuffer = Buffer(newCapacity(size1 + size2)); + + var pointer1 = 0; + var pointer2 = 0; + + while (pointer1 < size1 and pointer2 < size2) { + let current1 = buffer1.get(pointer1); + let current2 = buffer2.get(pointer2); + + switch (compare(current1, current2)) { + case (#less) { + newBuffer.add(current1); + pointer1 += 1 + }; + case _ { + newBuffer.add(current2); + pointer2 += 1 + } + } + }; + + while (pointer1 < size1) { + newBuffer.add(buffer1.get(pointer1)); + pointer1 += 1 + }; + + while (pointer2 < size2) { + newBuffer.add(buffer2.get(pointer2)); + pointer2 += 1 + }; + + newBuffer + }; + + /// Eliminates all duplicate elements in `buffer` as defined by `compare`. + /// Elimination is stable with respect to the original ordering of the elements. + /// + /// Example: + /// ```motoko include=initialize + /// import Nat "mo:base/Nat"; + /// + /// buffer.add(1); + /// buffer.add(2); + /// buffer.add(3); + /// buffer.add(1); + /// buffer.add(2); + /// buffer.add(3); + /// + /// Buffer.removeDuplicates(buffer, Nat.compare); + /// Buffer.toText(buffer, Nat.toText); // => [1, 2, 3] + /// ``` + /// + /// Runtime: O(size * log(size)) + /// + /// Space: O(size) + public func removeDuplicates(buffer : Buffer, compare : (X, X) -> Order) { + let size = buffer.size(); + let indices = Prim.Array_tabulate<(Nat, X)>(size, func i = (i, buffer.get(i))); + // Sort based on element, while carrying original index information + // This groups together the duplicate elements + let sorted = Array.sort<(Nat, X)>(indices, func(pair1, pair2) = compare(pair1.1, pair2.1)); + let uniques = Buffer<(Nat, X)>(size); + + // Iterate over elements + var i = 0; + while (i < size) { + var j = i; + // Iterate over duplicate elements, and find the smallest index among them (for stability) + var minIndex = sorted[j]; + label duplicates while (j < (size - 1 : Nat)) { + let pair1 = sorted[j]; + let pair2 = sorted[j + 1]; + switch (compare(pair1.1, pair2.1)) { + case (#equal) { + if (pair2.0 < pair1.0) { + minIndex := pair2 + }; + j += 1 + }; + case _ { + break duplicates + } + } + }; + + uniques.add(minIndex); + i := j + 1 + }; + + // resort based on original ordering and place back in buffer + uniques.sort( + func(pair1, pair2) { + if (pair1.0 < pair2.0) { + #less + } else if (pair1.0 == pair2.0) { + #equal + } else { + #greater + } + } + ); + + buffer.clear(); + buffer.reserve(uniques.size()); + for (element in uniques.vals()) { + buffer.add(element.1) + } + }; + + /// Splits `buffer` into a pair of buffers where all elements in the left + /// buffer satisfy `predicate` and all elements in the right buffer do not. + /// + /// Example: + /// ```motoko include=initialize + /// import Nat "mo:base/Nat"; + /// + /// buffer.add(1); + /// buffer.add(2); + /// buffer.add(3); + /// buffer.add(4); + /// buffer.add(5); + /// buffer.add(6); + /// + /// let partitions = Buffer.partition(buffer, func (x) { x % 2 == 0 }); + /// (Buffer.toArray(partitions.0), Buffer.toArray(partitions.1)) // => ([2, 4, 6], [1, 3, 5]) + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(size) + /// + /// *Runtime and space assumes that `predicate` runs in O(1) time and space. + public func partition(buffer : Buffer, predicate : X -> Bool) : (Buffer, Buffer) { + let size = buffer.size(); + let trueBuffer = Buffer(size); + let falseBuffer = Buffer(size); + + for (element in buffer.vals()) { + if (predicate element) { + trueBuffer.add(element) + } else { + falseBuffer.add(element) + } + }; + + (trueBuffer, falseBuffer) + }; + + /// Splits the buffer into two buffers at `index`, where the left buffer contains + /// all elements with indices less than `index`, and the right buffer contains all + /// elements with indices greater than or equal to `index`. Traps if `index` is out + /// of bounds. + /// + /// Example: + /// ```motoko include=initialize + /// import Nat "mo:base/Nat"; + /// + /// buffer.add(1); + /// buffer.add(2); + /// buffer.add(3); + /// buffer.add(4); + /// buffer.add(5); + /// buffer.add(6); + /// + /// let split = Buffer.split(buffer, 3); + /// (Buffer.toArray(split.0), Buffer.toArray(split.1)) // => ([1, 2, 3], [4, 5, 6]) + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(size) + /// + /// *Runtime and space assumes that `compare` runs in O(1) time and space. + public func split(buffer : Buffer, index : Nat) : (Buffer, Buffer) { + let size = buffer.size(); + + if (index < 0 or index > size) { + Prim.trap "Index out of bounds in split" + }; + + let buffer1 = Buffer(newCapacity index); + let buffer2 = Buffer(newCapacity(size - index)); + + var i = 0; + while (i < index) { + buffer1.add(buffer.get(i)); + i += 1 + }; + while (i < size) { + buffer2.add(buffer.get(i)); + i += 1 + }; + + (buffer1, buffer2) + }; + + /// Breaks up `buffer` into buffers of size `size`. The last chunk may + /// have less than `size` elements if the number of elements is not divisible + /// by the chunk size. + /// + /// Example: + /// ```motoko include=initialize + /// import Nat "mo:base/Nat"; + /// + /// buffer.add(1); + /// buffer.add(2); + /// buffer.add(3); + /// buffer.add(4); + /// buffer.add(5); + /// buffer.add(6); + /// + /// let chunks = Buffer.chunk(buffer, 3); + /// Buffer.toText>(chunks, func buf = Buffer.toText(buf, Nat.toText)); // => [[1, 2, 3], [4, 5, 6]] + /// ``` + /// + /// Runtime: O(number of elements in buffer) + /// + /// Space: O(number of elements in buffer) + public func chunk(buffer : Buffer, size : Nat) : Buffer> { + if (size == 0) { + Prim.trap "Chunk size must be non-zero in chunk" + }; + + // ceil(buffer.size() / size) + let newBuffer = Buffer>((buffer.size() + size - 1) / size); + + var newInnerBuffer = Buffer(newCapacity size); + var innerSize = 0; + for (element in buffer.vals()) { + if (innerSize == size) { + newBuffer.add(newInnerBuffer); + newInnerBuffer := Buffer(newCapacity size); + innerSize := 0 + }; + newInnerBuffer.add(element); + innerSize += 1 + }; + if (innerSize > 0) { + newBuffer.add(newInnerBuffer) + }; + + newBuffer + }; + + /// Groups equal and adjacent elements in the list into sub lists. + /// + /// Example: + /// ```motoko include=initialize + /// import Nat "mo:base/Nat"; + /// + /// buffer.add(1); + /// buffer.add(2); + /// buffer.add(2); + /// buffer.add(4); + /// buffer.add(5); + /// buffer.add(5); + /// + /// let grouped = Buffer.groupBy(buffer, func (x, y) { x == y }); + /// Buffer.toText>(grouped, func buf = Buffer.toText(buf, Nat.toText)); // => [[1], [2, 2], [4], [5, 5]] + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(size) + /// + /// *Runtime and space assumes that `equal` runs in O(1) time and space. + public func groupBy(buffer : Buffer, equal : (X, X) -> Bool) : Buffer> { + let size = buffer.size(); + let newBuffer = Buffer>(size); + if (size == 0) { + return newBuffer + }; + + var i = 0; + var baseElement = buffer.get(0); + var newInnerBuffer = Buffer(size); + while (i < size) { + let element = buffer.get(i); + + if (equal(baseElement, element)) { + newInnerBuffer.add(element) + } else { + newBuffer.add(newInnerBuffer); + baseElement := element; + newInnerBuffer := Buffer(size - i); + newInnerBuffer.add(element) + }; + i += 1 + }; + if (newInnerBuffer.size() > 0) { + newBuffer.add(newInnerBuffer) + }; + + newBuffer + }; + + /// Flattens the buffer of buffers into a single buffer. + /// + /// Example: + /// ```motoko include=initialize + /// import Nat "mo:base/Nat"; + /// + /// let buffer = Buffer.Buffer>(1); + /// + /// let inner1 = Buffer.Buffer(2); + /// inner1.add(1); + /// inner1.add(2); + /// + /// let inner2 = Buffer.Buffer(2); + /// inner2.add(3); + /// inner2.add(4); + /// + /// buffer.add(inner1); + /// buffer.add(inner2); + /// // buffer = [[1, 2], [3, 4]] + /// + /// let flat = Buffer.flatten(buffer); + /// Buffer.toText(flat, Nat.toText); // => [1, 2, 3, 4] + /// ``` + /// + /// Runtime: O(number of elements in buffer) + /// + /// Space: O(number of elements in buffer) + public func flatten(buffer : Buffer>) : Buffer { + let size = buffer.size(); + if (size == 0) { + return Buffer(0) + }; + + let newBuffer = Buffer( + if (buffer.get(0).size() != 0) { + newCapacity(buffer.get(0).size() * size) + } else { + newCapacity(size) + } + ); + + for (innerBuffer in buffer.vals()) { + for (innerElement in innerBuffer.vals()) { + newBuffer.add(innerElement) + } + }; + + newBuffer + }; + + /// Combines the two buffers into a single buffer of pairs, pairing together + /// elements with the same index. If one buffer is longer than the other, the + /// remaining elements from the longer buffer are not included. + /// + /// Example: + /// ```motoko include=initialize + /// + /// let buffer1 = Buffer.Buffer(2); + /// buffer1.add(1); + /// buffer1.add(2); + /// buffer1.add(3); + /// + /// let buffer2 = Buffer.Buffer(2); + /// buffer2.add(4); + /// buffer2.add(5); + /// + /// let zipped = Buffer.zip(buffer1, buffer2); + /// Buffer.toArray(zipped); // => [(1, 4), (2, 5)] + /// ``` + /// + /// Runtime: O(min(size1, size2)) + /// + /// Space: O(min(size1, size2)) + public func zip(buffer1 : Buffer, buffer2 : Buffer) : Buffer<(X, Y)> { + // compiler should pull lamda out as a static function since it is fully closed + zipWith(buffer1, buffer2, func(x, y) = (x, y)) + }; + + /// Combines the two buffers into a single buffer, pairing together + /// elements with the same index and combining them using `zip`. If + /// one buffer is longer than the other, the remaining elements from + /// the longer buffer are not included. + /// + /// Example: + /// ```motoko include=initialize + /// + /// let buffer1 = Buffer.Buffer(2); + /// buffer1.add(1); + /// buffer1.add(2); + /// buffer1.add(3); + /// + /// let buffer2 = Buffer.Buffer(2); + /// buffer2.add(4); + /// buffer2.add(5); + /// buffer2.add(6); + /// + /// let zipped = Buffer.zipWith(buffer1, buffer2, func (x, y) { x + y }); + /// Buffer.toArray(zipped) // => [5, 7, 9] + /// ``` + /// + /// Runtime: O(min(size1, size2)) + /// + /// Space: O(min(size1, size2)) + /// + /// *Runtime and space assumes that `zip` runs in O(1) time and space. + public func zipWith(buffer1 : Buffer, buffer2 : Buffer, zip : (X, Y) -> Z) : Buffer { + let size1 = buffer1.size(); + let size2 = buffer2.size(); + let minSize = if (size1 < size2) { size1 } else { size2 }; + + var i = 0; + let newBuffer = Buffer(newCapacity minSize); + while (i < minSize) { + newBuffer.add(zip(buffer1.get(i), buffer2.get(i))); + i += 1 + }; + newBuffer + }; + + /// Creates a new buffer taking elements in order from `buffer` until predicate + /// returns false. + /// + /// Example: + /// ```motoko include=initialize + /// import Nat "mo:base/Nat"; + /// + /// buffer.add(1); + /// buffer.add(2); + /// buffer.add(3); + /// + /// let newBuf = Buffer.takeWhile(buffer, func (x) { x < 3 }); + /// Buffer.toText(newBuf, Nat.toText); // => [1, 2] + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(size) + /// + /// *Runtime and space assumes that `predicate` runs in O(1) time and space. + public func takeWhile(buffer : Buffer, predicate : X -> Bool) : Buffer { + let newBuffer = Buffer(buffer.size()); + + for (element in buffer.vals()) { + if (not predicate element) { + return newBuffer + }; + newBuffer.add(element) + }; + + newBuffer + }; + + /// Creates a new buffer excluding elements in order from `buffer` until predicate + /// returns false. + /// + /// Example: + /// ```motoko include=initialize + /// import Nat "mo:base/Nat"; + /// + /// buffer.add(1); + /// buffer.add(2); + /// buffer.add(3); + /// + /// let newBuf = Buffer.dropWhile(buffer, func x { x < 3 }); // => [3] + /// Buffer.toText(newBuf, Nat.toText); + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(size) + /// + /// *Runtime and space assumes that `predicate` runs in O(1) time and space. + public func dropWhile(buffer : Buffer, predicate : X -> Bool) : Buffer { + let size = buffer.size(); + let newBuffer = Buffer(size); + + var i = 0; + var take = false; + label iter for (element in buffer.vals()) { + if (not (take or predicate element)) { + take := true + }; + if (take) { + newBuffer.add(element) + } + }; + newBuffer + } +} diff --git a/test/perf/sha224/Char.mo b/test/perf/sha224/Char.mo new file mode 100644 index 00000000000..d4ef02fcd26 --- /dev/null +++ b/test/perf/sha224/Char.mo @@ -0,0 +1,65 @@ +/// Characters +import Prim "mo:⛔"; +module { + + /// Characters represented as Unicode code points. + public type Char = Prim.Types.Char; + + /// Convert character `c` to a word containing its Unicode scalar value. + public let toNat32 : (c : Char) -> Nat32 = Prim.charToNat32; + + /// Convert `w` to a character. + /// Traps if `w` is not a valid Unicode scalar value. + /// Value `w` is valid if, and only if, `w < 0xD800 or (0xE000 <= w and w <= 0x10FFFF)`. + public let fromNat32 : (w : Nat32) -> Char = Prim.nat32ToChar; + + /// Convert character `c` to single character text. + public let toText : (c : Char) -> Text = Prim.charToText; + + // Not exposed pending multi-char implementation. + private let toUpper : (c : Char) -> Char = Prim.charToUpper; + + // Not exposed pending multi-char implementation. + private let toLower : (c : Char) -> Char = Prim.charToLower; + + /// Returns `true` when `c` is a decimal digit between `0` and `9`, otherwise `false`. + public func isDigit(c : Char) : Bool { + Prim.charToNat32(c) -% Prim.charToNat32('0') <= (9 : Nat32) + }; + + /// Returns the Unicode _White_Space_ property of `c`. + public let isWhitespace : (c : Char) -> Bool = Prim.charIsWhitespace; + + /// Returns the Unicode _Lowercase_ property of `c`. + public let isLowercase : (c : Char) -> Bool = Prim.charIsLowercase; + + /// Returns the Unicode _Uppercase_ property of `c`. + public let isUppercase : (c : Char) -> Bool = Prim.charIsUppercase; + + /// Returns the Unicode _Alphabetic_ property of `c`. + public let isAlphabetic : (c : Char) -> Bool = Prim.charIsAlphabetic; + + /// Returns `x == y`. + public func equal(x : Char, y : Char) : Bool { x == y }; + + /// Returns `x != y`. + public func notEqual(x : Char, y : Char) : Bool { x != y }; + + /// Returns `x < y`. + public func less(x : Char, y : Char) : Bool { x < y }; + + /// Returns `x <= y`. + public func lessOrEqual(x : Char, y : Char) : Bool { x <= y }; + + /// Returns `x > y`. + public func greater(x : Char, y : Char) : Bool { x > y }; + + /// Returns `x >= y`. + public func greaterOrEqual(x : Char, y : Char) : Bool { x >= y }; + + /// Returns the order of `x` and `y`. + public func compare(x : Char, y : Char) : { #less; #equal; #greater } { + if (x < y) { #less } else if (x == y) { #equal } else { #greater } + }; + +} diff --git a/test/perf/sha224/Debug.mo b/test/perf/sha224/Debug.mo new file mode 100644 index 00000000000..3e915f5c357 --- /dev/null +++ b/test/perf/sha224/Debug.mo @@ -0,0 +1,56 @@ +/// Utility functions for debugging. +/// +/// Import from the base library to use this module. +/// ```motoko name=import +/// import Debug "mo:base/Debug"; +/// ``` + +import Prim "mo:⛔"; +module { + /// Prints `text` to output stream. + /// + /// NOTE: The output is placed in the replica log. When running on mainnet, + /// this function has no effect. + /// + /// ```motoko include=import + /// Debug.print "Hello New World!"; + /// Debug.print(debug_show(4)) // Often used with `debug_show` to convert values to Text + /// ``` + public func print(text : Text) { + Prim.debugPrint text + }; + + /// `trap(t)` traps execution with a user-provided diagnostic message. + /// + /// The caller of a future whose execution called `trap(t)` will + /// observe the trap as an `Error` value, thrown at `await`, with code + /// `#canister_error` and message `m`. Here `m` is a more descriptive `Text` + /// message derived from the provided `t`. See example for more details. + /// + /// NOTE: Other execution environments that cannot handle traps may only + /// propagate the trap and terminate execution, with or without some + /// descriptive message. + /// + /// ```motoko + /// import Debug "mo:base/Debug"; + /// import Error "mo:base/Error"; + /// + /// actor { + /// func fail() : async () { + /// Debug.trap("user provided error message"); + /// }; + /// + /// public func foo() : async () { + /// try { + /// await fail(); + /// } catch e { + /// let code = Error.code(e); // evaluates to #canister_error + /// let message = Error.message(e); // contains user provided error message + /// } + /// }; + /// } + /// ``` + public func trap(errorMessage : Text) : None { + Prim.trap errorMessage + } +} diff --git a/test/perf/sha224/Hash.mo b/test/perf/sha224/Hash.mo new file mode 100644 index 00000000000..285c02b6b98 --- /dev/null +++ b/test/perf/sha224/Hash.mo @@ -0,0 +1,82 @@ +/// Hash values + +import Prim "mo:⛔"; +import Iter "Iter"; + +module { + + /// Hash values represent a string of _hash bits_, packed into a `Nat32`. + public type Hash = Nat32; + + /// The hash length, always 31. + public let length : Nat = 31; // Why not 32? + + /// Project a given bit from the bit vector. + public func bit(h : Hash, pos : Nat) : Bool { + assert (pos <= length); + (h & (Prim.natToNat32(1) << Prim.natToNat32(pos))) != Prim.natToNat32(0) + }; + + /// Test if two hashes are equal + public func equal(ha : Hash, hb : Hash) : Bool { + ha == hb + }; + + /// Computes a hash from the least significant 32-bits of `n`, ignoring other bits. + /// @deprecated For large `Nat` values consider using a bespoke hash function that considers all of the argument's bits. + public func hash(n : Nat) : Hash { + let j = Prim.intToNat32Wrap(n); + hashNat8([ + j & (255 << 0), + j & (255 << 8), + j & (255 << 16), + j & (255 << 24) + ]) + }; + + /// @deprecated This function will be removed in future. + public func debugPrintBits(bits : Hash) { + for (j in Iter.range(0, length - 1)) { + if (bit(bits, j)) { + Prim.debugPrint("1") + } else { + Prim.debugPrint("0") + } + } + }; + + /// @deprecated This function will be removed in future. + public func debugPrintBitsRev(bits : Hash) { + for (j in Iter.revRange(length - 1, 0)) { + if (bit(bits, Prim.abs(j))) { + Prim.debugPrint("1") + } else { + Prim.debugPrint("0") + } + } + }; + + /// Jenkin's one at a time: + /// + /// https://en.wikipedia.org/wiki/Jenkins_hash_function#one_at_a_time + /// + /// The input type should actually be `[Nat8]`. + /// Note: Be sure to explode each `Nat8` of a `Nat32` into its own `Nat32`, and to shift into lower 8 bits. + + // should this really be public? + // NB: Int.mo contains a local copy of hashNat8 (redefined to suppress the deprecation warning). + /// @deprecated This function may be removed or changed in future. + public func hashNat8(key : [Hash]) : Hash { + var hash : Nat32 = 0; + for (natOfKey in key.vals()) { + hash := hash +% natOfKey; + hash := hash +% hash << 10; + hash := hash ^ (hash >> 6) + }; + hash := hash +% hash << 3; + hash := hash ^ (hash >> 11); + hash := hash +% hash << 15; + return hash + }; + +} diff --git a/test/perf/sha224/Int.mo b/test/perf/sha224/Int.mo new file mode 100644 index 00000000000..0dd479cb40c --- /dev/null +++ b/test/perf/sha224/Int.mo @@ -0,0 +1,370 @@ +/// Signed integer numbers with infinite precision (also called big integers). +/// +/// Most operations on integer numbers (e.g. addition) are available as built-in operators (e.g. `-1 + 1`). +/// This module provides equivalent functions and `Text` conversion. +/// +/// Import from the base library to use this module. +/// ```motoko name=import +/// import Int "mo:base/Int"; +/// ``` + +import Prim "mo:⛔"; +import Prelude "Prelude"; +import Hash "Hash"; + +module { + + /// Infinite precision signed integers. + public type Int = Prim.Types.Int; + + /// Returns the absolute value of `x`. + /// + /// Example: + /// ```motoko include=import + /// Int.abs(-12) // => 12 + /// ``` + public func abs(x : Int) : Nat { + Prim.abs(x) + }; + + /// Converts an integer number to its textual representation. Textual + /// representation _do not_ contain underscores to represent commas. + /// + /// Example: + /// ```motoko include=import + /// Int.toText(-1234) // => "-1234" + /// ``` + public func toText(x : Int) : Text { + if (x == 0) { + return "0" + }; + + let isNegative = x < 0; + var int = if isNegative { -x } else { x }; + + var text = ""; + let base = 10; + + while (int > 0) { + let rem = int % base; + text := ( + switch (rem) { + case 0 { "0" }; + case 1 { "1" }; + case 2 { "2" }; + case 3 { "3" }; + case 4 { "4" }; + case 5 { "5" }; + case 6 { "6" }; + case 7 { "7" }; + case 8 { "8" }; + case 9 { "9" }; + case _ { Prelude.unreachable() } + } + ) # text; + int := int / base + }; + + return if isNegative { "-" # text } else { text } + }; + + /// Returns the minimum of `x` and `y`. + /// + /// Example: + /// ```motoko include=import + /// Int.min(2, -3) // => -3 + /// ``` + public func min(x : Int, y : Int) : Int { + if (x < y) { x } else { y } + }; + + /// Returns the maximum of `x` and `y`. + /// + /// Example: + /// ```motoko include=import + /// Int.max(2, -3) // => 2 + /// ``` + public func max(x : Int, y : Int) : Int { + if (x < y) { y } else { x } + }; + + // this is a local copy of deprecated Hash.hashNat8 (redefined to suppress the warning) + private func hashNat8(key : [Nat32]) : Hash.Hash { + var hash : Nat32 = 0; + for (natOfKey in key.vals()) { + hash := hash +% natOfKey; + hash := hash +% hash << 10; + hash := hash ^ (hash >> 6) + }; + hash := hash +% hash << 3; + hash := hash ^ (hash >> 11); + hash := hash +% hash << 15; + return hash + }; + + /// Computes a hash from the least significant 32-bits of `i`, ignoring other bits. + /// @deprecated For large `Int` values consider using a bespoke hash function that considers all of the argument's bits. + public func hash(i : Int) : Hash.Hash { + // CAUTION: This removes the high bits! + let j = Prim.int32ToNat32(Prim.intToInt32Wrap(i)); + hashNat8([ + j & (255 << 0), + j & (255 << 8), + j & (255 << 16), + j & (255 << 24) + ]) + }; + + /// Computes an accumulated hash from `h1` and the least significant 32-bits of `i`, ignoring other bits in `i`. + /// @deprecated For large `Int` values consider using a bespoke hash function that considers all of the argument's bits. + public func hashAcc(h1 : Hash.Hash, i : Int) : Hash.Hash { + // CAUTION: This removes the high bits! + let j = Prim.int32ToNat32(Prim.intToInt32Wrap(i)); + hashNat8([ + h1, + j & (255 << 0), + j & (255 << 8), + j & (255 << 16), + j & (255 << 24) + ]) + }; + + /// Equality function for Int types. + /// This is equivalent to `x == y`. + /// + /// Example: + /// ```motoko include=import + /// Int.equal(-1, -1); // => true + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `==` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `==` + /// as a function value at the moment. + /// + /// Example: + /// ```motoko include=import + /// import Buffer "mo:base/Buffer"; + /// + /// let buffer1 = Buffer.Buffer(1); + /// buffer1.add(-3); + /// let buffer2 = Buffer.Buffer(1); + /// buffer2.add(-3); + /// Buffer.equal(buffer1, buffer2, Int.equal) // => true + /// ``` + public func equal(x : Int, y : Int) : Bool { x == y }; + + /// Inequality function for Int types. + /// This is equivalent to `x != y`. + /// + /// Example: + /// ```motoko include=import + /// Int.notEqual(-1, -2); // => true + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `!=` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `!=` + /// as a function value at the moment. + public func notEqual(x : Int, y : Int) : Bool { x != y }; + + /// "Less than" function for Int types. + /// This is equivalent to `x < y`. + /// + /// Example: + /// ```motoko include=import + /// Int.less(-2, 1); // => true + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `<` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `<` + /// as a function value at the moment. + public func less(x : Int, y : Int) : Bool { x < y }; + + /// "Less than or equal" function for Int types. + /// This is equivalent to `x <= y`. + /// + /// Example: + /// ```motoko include=import + /// Int.lessOrEqual(-2, 1); // => true + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `<=` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `<=` + /// as a function value at the moment. + public func lessOrEqual(x : Int, y : Int) : Bool { x <= y }; + + /// "Greater than" function for Int types. + /// This is equivalent to `x > y`. + /// + /// Example: + /// ```motoko include=import + /// Int.greater(1, -2); // => true + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `>` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `>` + /// as a function value at the moment. + public func greater(x : Int, y : Int) : Bool { x > y }; + + /// "Greater than or equal" function for Int types. + /// This is equivalent to `x >= y`. + /// + /// Example: + /// ```motoko include=import + /// Int.greaterOrEqual(1, -2); // => true + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `>=` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `>=` + /// as a function value at the moment. + public func greaterOrEqual(x : Int, y : Int) : Bool { x >= y }; + + /// General-purpose comparison function for `Int`. Returns the `Order` ( + /// either `#less`, `#equal`, or `#greater`) of comparing `x` with `y`. + /// + /// Example: + /// ```motoko include=import + /// Int.compare(-3, 2) // => #less + /// ``` + /// + /// This function can be used as value for a high order function, such as a sort function. + /// + /// Example: + /// ```motoko include=import + /// import Array "mo:base/Array"; + /// Array.sort([1, -2, -3], Int.compare) // => [-3, -2, 1] + /// ``` + public func compare(x : Int, y : Int) : { #less; #equal; #greater } { + if (x < y) { #less } else if (x == y) { #equal } else { #greater } + }; + + /// Returns the negation of `x`, `-x` . + /// + /// Example: + /// ```motoko include=import + /// Int.neg(123) // => -123 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `-` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `-` + /// as a function value at the moment. + public func neg(x : Int) : Int { -x }; + + /// Returns the sum of `x` and `y`, `x + y`. + /// + /// No overflow since `Int` has infinite precision. + /// + /// Example: + /// ```motoko include=import + /// Int.add(1, -2); // => -1 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `+` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `+` + /// as a function value at the moment. + /// + /// Example: + /// ```motoko include=import + /// import Array "mo:base/Array"; + /// Array.foldLeft([1, -2, -3], 0, Int.add) // => -4 + /// ``` + public func add(x : Int, y : Int) : Int { x + y }; + + /// Returns the difference of `x` and `y`, `x - y`. + /// + /// No overflow since `Int` has infinite precision. + /// + /// Example: + /// ```motoko include=import + /// Int.sub(1, 2); // => -1 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `-` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `-` + /// as a function value at the moment. + /// + /// Example: + /// ```motoko include=import + /// import Array "mo:base/Array"; + /// Array.foldLeft([1, -2, -3], 0, Int.sub) // => 4 + /// ``` + public func sub(x : Int, y : Int) : Int { x - y }; + + /// Returns the product of `x` and `y`, `x * y`. + /// + /// No overflow since `Int` has infinite precision. + /// + /// Example: + /// ```motoko include=import + /// Int.mul(-2, 3); // => -6 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `*` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `*` + /// as a function value at the moment. + /// + /// Example: + /// ```motoko include=import + /// import Array "mo:base/Array"; + /// Array.foldLeft([1, -2, -3], 1, Int.mul) // => 6 + /// ``` + public func mul(x : Int, y : Int) : Int { x * y }; + + /// Returns the signed integer division of `x` by `y`, `x / y`. + /// Rounds the quotient towards zero, which is the same as truncating the decimal places of the quotient. + /// + /// Traps when `y` is zero. + /// + /// Example: + /// ```motoko include=import + /// Int.div(6, -2); // => -3 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `/` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `/` + /// as a function value at the moment. + public func div(x : Int, y : Int) : Int { x / y }; + + /// Returns the remainder of the signed integer division of `x` by `y`, `x % y`, + /// which is defined as `x - x / y * y`. + /// + /// Traps when `y` is zero. + /// + /// Example: + /// ```motoko include=import + /// Int.rem(6, -4); // => 2 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `%` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `%` + /// as a function value at the moment. + public func rem(x : Int, y : Int) : Int { x % y }; + + /// Returns `x` to the power of `y`, `x ** y`. + /// + /// Traps when `y` is negative or `y > 2 ** 32 - 1`. + /// No overflow since `Int` has infinite precision. + /// + /// Example: + /// ```motoko include=import + /// Int.pow(-2, 3); // => -8 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `**` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `**` + /// as a function value at the moment. + public func pow(x : Int, y : Int) : Int { x ** y }; + +} diff --git a/test/perf/sha224/Iter.mo b/test/perf/sha224/Iter.mo new file mode 100644 index 00000000000..2d18c9b30ff --- /dev/null +++ b/test/perf/sha224/Iter.mo @@ -0,0 +1,227 @@ +/// Iterators + +import Array "Array"; +import Buffer "Buffer"; +import List "List"; +import Order "Order"; + +module { + + /// An iterator that produces values of type `T`. Calling `next` returns + /// `null` when iteration is finished. + /// + /// Iterators are inherently stateful. Calling `next` "consumes" a value from + /// the Iterator that cannot be put back, so keep that in mind when sharing + /// iterators between consumers. + /// + /// An iterater `i` can be iterated over using + /// ``` + /// for (x in i) { + /// …do something with x… + /// } + /// ``` + public type Iter = { next : () -> ?T }; + + /// Creates an iterator that produces all `Nat`s from `x` to `y` including + /// both of the bounds. + /// ```motoko + /// import Iter "mo:base/Iter"; + /// let iter = Iter.range(1, 3); + /// assert(?1 == iter.next()); + /// assert(?2 == iter.next()); + /// assert(?3 == iter.next()); + /// assert(null == iter.next()); + /// ``` + public class range(x : Nat, y : Int) { + var i = x; + public func next() : ?Nat { + if (i > y) { null } else { let j = i; i += 1; ?j } + } + }; + + /// Like `range` but produces the values in the opposite + /// order. + public class revRange(x : Int, y : Int) { + var i = x; + public func next() : ?Int { + if (i < y) { null } else { let j = i; i -= 1; ?j } + } + }; + + /// Calls a function `f` on every value produced by an iterator and discards + /// the results. If you're looking to keep these results use `map` instead. + /// + /// ```motoko + /// import Iter "mo:base/Iter"; + /// var sum = 0; + /// Iter.iterate(Iter.range(1, 3), func(x, _index) { + /// sum += x; + /// }); + /// assert(6 == sum) + /// ``` + public func iterate( + xs : Iter, + f : (A, Nat) -> () + ) { + var i = 0; + label l loop { + switch (xs.next()) { + case (?next) { + f(next, i) + }; + case (null) { + break l + } + }; + i += 1; + continue l + } + }; + + /// Consumes an iterator and counts how many elements were produced + /// (discarding them in the process). + public func size(xs : Iter) : Nat { + var len = 0; + iterate(xs, func(x, i) { len += 1 }); + len + }; + + /// Takes a function and an iterator and returns a new iterator that lazily applies + /// the function to every element produced by the argument iterator. + /// ```motoko + /// import Iter "mo:base/Iter"; + /// let iter = Iter.range(1, 3); + /// let mappedIter = Iter.map(iter, func (x : Nat) : Nat { x * 2 }); + /// assert(?2 == mappedIter.next()); + /// assert(?4 == mappedIter.next()); + /// assert(?6 == mappedIter.next()); + /// assert(null == mappedIter.next()); + /// ``` + public func map(xs : Iter, f : A -> B) : Iter = object { + public func next() : ?B { + switch (xs.next()) { + case (?next) { + ?f(next) + }; + case (null) { + null + } + } + } + }; + + /// Takes a function and an iterator and returns a new iterator that produces + /// elements from the original iterator if and only if the predicate is true. + /// ```motoko + /// import Iter "o:base/Iter"; + /// let iter = Iter.range(1, 3); + /// let mappedIter = Iter.filter(iter, func (x : Nat) : Bool { x % 2 == 1 }); + /// assert(?1 == mappedIter.next()); + /// assert(?3 == mappedIter.next()); + /// assert(null == mappedIter.next()); + /// ``` + public func filter(xs : Iter, f : A -> Bool) : Iter = object { + public func next() : ?A { + loop { + switch (xs.next()) { + case (null) { + return null + }; + case (?x) { + if (f(x)) { + return ?x + } + } + } + }; + null + } + }; + + /// Creates an iterator that produces an infinite sequence of `x`. + /// ```motoko + /// import Iter "mo:base/Iter"; + /// let iter = Iter.make(10); + /// assert(?10 == iter.next()); + /// assert(?10 == iter.next()); + /// assert(?10 == iter.next()); + /// // ... + /// ``` + public func make(x : A) : Iter = object { + public func next() : ?A { + ?x + } + }; + + /// Creates an iterator that produces the elements of an Array in ascending index order. + /// ```motoko + /// import Iter "mo:base/Iter"; + /// let iter = Iter.fromArray([1, 2, 3]); + /// assert(?1 == iter.next()); + /// assert(?2 == iter.next()); + /// assert(?3 == iter.next()); + /// assert(null == iter.next()); + /// ``` + public func fromArray(xs : [A]) : Iter { + var ix : Nat = 0; + let size = xs.size(); + object { + public func next() : ?A { + if (ix >= size) { + return null + } else { + let res = ?(xs[ix]); + ix += 1; + return res + } + } + } + }; + + /// Like `fromArray` but for Arrays with mutable elements. Captures + /// the elements of the Array at the time the iterator is created, so + /// further modifications won't be reflected in the iterator. + public func fromArrayMut(xs : [var A]) : Iter { + fromArray(Array.freeze(xs)) + }; + + /// Like `fromArray` but for Lists. + public let fromList = List.toIter; + + /// Consumes an iterator and collects its produced elements in an Array. + /// ```motoko + /// import Iter "mo:base/Iter"; + /// let iter = Iter.range(1, 3); + /// assert([1, 2, 3] == Iter.toArray(iter)); + /// ``` + public func toArray(xs : Iter) : [A] { + let buffer = Buffer.Buffer(8); + iterate(xs, func(x : A, ix : Nat) { buffer.add(x) }); + return Buffer.toArray(buffer) + }; + + /// Like `toArray` but for Arrays with mutable elements. + public func toArrayMut(xs : Iter) : [var A] { + Array.thaw(toArray(xs)) + }; + + /// Like `toArray` but for Lists. + public func toList(xs : Iter) : List.List { + var result = List.nil(); + iterate( + xs, + func(x, _i) { + result := List.push(x, result) + } + ); + List.reverse(result) + }; + + /// Sorted iterator. Will iterate over *all* elements to sort them, necessarily. + public func sort(xs : Iter, compare : (A, A) -> Order.Order) : Iter { + let a = toArrayMut(xs); + Array.sortInPlace(a, compare); + fromArrayMut(a) + }; + +} diff --git a/test/perf/sha224/IterType.mo b/test/perf/sha224/IterType.mo new file mode 100644 index 00000000000..1c258ff4e75 --- /dev/null +++ b/test/perf/sha224/IterType.mo @@ -0,0 +1,7 @@ +/// The Iterator type + +// Just here to break cyclic module definitions + +module { + public type Iter = { next : () -> ?T } +} diff --git a/test/perf/sha224/List.mo b/test/perf/sha224/List.mo new file mode 100644 index 00000000000..de200f227db --- /dev/null +++ b/test/perf/sha224/List.mo @@ -0,0 +1,930 @@ +/// Purely-functional, singly-linked lists. + +/// A list of type `List` is either `null` or an optional pair of a value of type `T` and a tail, itself of type `List`. +/// +/// To use this library, import it using: +/// +/// ```motoko name=initialize +/// import List "mo:base/List"; +/// ``` + +import Array "Array"; +import Iter "IterType"; +import Option "Option"; +import Order "Order"; +import Result "Result"; + +module { + + // A singly-linked list consists of zero or more _cons cells_, wherein + // each cell contains a single list element (the cell's _head_), and a pointer to the + // remainder of the list (the cell's _tail_). + public type List = ?(T, List); + + /// Create an empty list. + /// + /// Example: + /// ```motoko include=initialize + /// List.nil() // => null + /// ``` + /// + /// Runtime: O(1) + /// + /// Space: O(1) + public func nil() : List = null; + + /// Check whether a list is empty and return true if the list is empty. + /// + /// Example: + /// ```motoko include=initialize + /// List.isNil(null) // => true + /// ``` + /// + /// Runtime: O(1) + /// + /// Space: O(1) + public func isNil(l : List) : Bool { + switch l { + case null { true }; + case _ { false } + } + }; + + /// Add `x` to the head of `list`, and return the new list. + /// + /// Example: + /// ```motoko include=initialize + /// List.push(0, null) // => ?(0, null); + /// ``` + /// + /// Runtime: O(1) + /// + /// Space: O(1) + public func push(x : T, l : List) : List = ?(x, l); + + /// Return the last element of the list, if present. + /// Example: + /// ```motoko include=initialize + /// List.last(?(0, ?(1, null))) // => ?1 + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(1) + public func last(l : List) : ?T { + switch l { + case null { null }; + case (?(x, null)) { ?x }; + case (?(_, t)) { last(t) } + } + }; + + /// Remove the head of the list, returning the optioned head and the tail of the list in a pair. + /// Returns `(null, null)` if the list is empty. + /// + /// Example: + /// ```motoko include=initialize + /// List.pop(?(0, ?(1, null))) // => (?0, ?(1, null)) + /// ``` + /// + /// Runtime: O(1) + /// + /// Space: O(1) + public func pop(l : List) : (?T, List) { + switch l { + case null { (null, null) }; + case (?(h, t)) { (?h, t) } + } + }; + + /// Return the length of the list. + /// + /// Example: + /// ```motoko include=initialize + /// List.size(?(0, ?(1, null))) // => 2 + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(1) + public func size(l : List) : Nat { + func rec(l : List, n : Nat) : Nat { + switch l { + case null { n }; + case (?(_, t)) { rec(t, n + 1) } + } + }; + rec(l, 0) + }; + /// Access any item in a list, zero-based. + /// + /// NOTE: Indexing into a list is a linear operation, and usually an + /// indication that a list might not be the best data structure + /// to use. + /// + /// Example: + /// ```motoko include=initialize + /// List.get(?(0, ?(1, null)), 1) // => ?1 + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(1) + public func get(l : List, n : Nat) : ?T { + switch (n, l) { + case (_, null) { null }; + case (0, (?(h, t))) { ?h }; + case (_, (?(_, t))) { get(t, n - 1) } + } + }; + + /// Reverses the list. + /// + /// Example: + /// ```motoko include=initialize + /// List.reverse(?(0, ?(1, ?(2, null)))) // => ?(2, ?(1, ?(0, null))) + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(size) + public func reverse(l : List) : List { + func rec(l : List, r : List) : List { + switch l { + case null { r }; + case (?(h, t)) { rec(t, ?(h, r)) } + } + }; + rec(l, null) + }; + + /// Call the given function for its side effect, with each list element in turn. + /// + /// Example: + /// ```motoko include=initialize + /// var sum = 0; + /// List.iterate(?(0, ?(1, ?(2, null))), func n { sum += n }); + /// sum // => 3 + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(1) + public func iterate(l : List, f : T -> ()) { + switch l { + case null { () }; + case (?(h, t)) { f(h); iterate(t, f) } + } + }; + + /// Call the given function `f` on each list element and collect the results + /// in a new list. + /// + /// Example: + /// ```motoko include=initialize + /// import Nat = "mo:base/Nat" + /// List.map(?(0, ?(1, ?(2, null))), Nat.toText) // => ?("0", ?("1", ?("2", null)) + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(size) + /// *Runtime and space assumes that `f` runs in O(1) time and space. + public func map(l : List, f : T -> U) : List { + switch l { + case null { null }; + case (?(h, t)) { ?(f(h), map(t, f)) } + } + }; + + /// Create a new list with only those elements of the original list for which + /// the given function (often called the _predicate_) returns true. + /// + /// Example: + /// ```motoko include=initialize + /// List.filter(?(0, ?(1, ?(2, null))), func n { n != 1 }) // => ?(0, ?(2, null)) + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(size) + public func filter(l : List, f : T -> Bool) : List { + switch l { + case null { null }; + case (?(h, t)) { + if (f(h)) { + ?(h, filter(t, f)) + } else { + filter(t, f) + } + } + } + }; + + /// Create two new lists from the results of a given function (`f`). + /// The first list only includes the elements for which the given + /// function `f` returns true and the second list only includes + /// the elements for which the function returns false. + /// + /// Example: + /// ```motoko include=initialize + /// List.partition(?(0, ?(1, ?(2, null))), func n { n != 1 }) // => (?(0, ?(2, null)), ?(1, null)) + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(size) + public func partition(l : List, f : T -> Bool) : (List, List) { + switch l { + case null { (null, null) }; + case (?(h, t)) { + if (f(h)) { + // call f in-order + let (l, r) = partition(t, f); + (?(h, l), r) + } else { + let (l, r) = partition(t, f); + (l, ?(h, r)) + } + } + } + }; + + /// Call the given function on each list element, and collect the non-null results + /// in a new list. + /// + /// Example: + /// ```motoko include=initialize + /// List.mapFilter( + /// ?(1, ?(2, ?(3, null))), + /// func n { + /// if (n > 1) { + /// ?(n * 2); + /// } else { + /// null + /// } + /// } + /// ) // => ?(4, ?(6, null)) + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(size) + public func mapFilter(l : List, f : T -> ?U) : List { + switch l { + case null { null }; + case (?(h, t)) { + switch (f(h)) { + case null { mapFilter(t, f) }; + case (?h_) { ?(h_, mapFilter(t, f)) } + } + } + } + }; + + /// Maps a Result-returning function `f` over a List and returns either + /// the first error or a list of successful values. + /// + /// Example: + /// ```motoko include=initialize + /// List.mapResult( + /// ?(1, ?(2, ?(3, null))), + /// func n { + /// if (n > 0) { + /// #ok(n * 2); + /// } else { + /// #err("Some element is zero") + /// } + /// } + /// ); // => #ok ?(2, ?(4, ?(6, null)) + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(size) + /// + /// *Runtime and space assumes that `f` runs in O(1) time and space. + public func mapResult(xs : List, f : T -> Result.Result) : Result.Result, E> { + func go(xs : List, acc : List) : Result.Result, E> { + switch xs { + case null { #ok(acc) }; + case (?(head, tail)) { + switch (f(head)) { + case (#err(err)) { #err(err) }; + case (#ok(ok)) { go(tail, ?(ok, acc)) } + } + } + } + }; + Result.mapOk(go(xs, null), func(xs : List) : List = reverse(xs)) + }; + + /// Append the elements from the reverse of one list, 'l', to another list, 'm'. + /// + /// Example: + /// ```motoko include=initialize + /// List.revAppend( + /// ?(2, ?(1, ?(0, null))), + /// ?(3, ?(4, ?(5, null))) + /// ); // => ?(0, ?(1, ?(2, ?(3, ?(4, ?(5, null)))))) + /// ``` + /// + /// Runtime: O(size(l)) + /// + /// Space: O(size(l)) + func revAppend(l : List, m : List) : List { + switch l { + case null { m }; + case (?(h, t)) { revAppend(t, ?(h, m)) } + } + }; + + /// Append the elements from one list to another list. + /// + /// Example: + /// ```motoko include=initialize + /// List.append( + /// ?(0, ?(1, ?(2, null))), + /// ?(3, ?(4, ?(5, null))) + /// ) // => ?(0, ?(1, ?(2, ?(3, ?(4, ?(5, null)))))) + /// ``` + /// + /// Runtime: O(size(l)) + /// + /// Space: O(size(l)) + public func append(l : List, m : List) : List { + revAppend(reverse(l), m) + }; + + /// Flatten, or concatenate, a list of lists as a list. + /// + /// Example: + /// ```motoko include=initialize + /// List.flatten( + /// ?(?(0, ?(1, ?(2, null))), + /// ?(?(3, ?(4, ?(5, null))), + /// null)) + /// ); // => ?(0, ?(1, ?(2, ?(3, ?(4, ?(5, null)))))) + /// ``` + /// + /// Runtime: O(size*size) + /// + /// Space: O(size*size) + public func flatten(l : List>) : List { + //FIXME: this is quadratic, not linear https://github.com/dfinity/motoko-base/issues/459 + foldLeft, List>(l, null, func(a, b) { append(a, b) }) + }; + + /// Returns the first `n` elements of the given list. + /// If the given list has fewer than `n` elements, this function returns + /// a copy of the full input list. + /// + /// Example: + /// ```motoko include=initialize + /// List.take( + /// ?(0, ?(1, ?(2, null))), + /// 2 + /// ); // => ?(0, ?(1, null)) + /// ``` + /// + /// Runtime: O(n) + /// + /// Space: O(n) + public func take(l : List, n : Nat) : List { + switch (l, n) { + case (_, 0) { null }; + case (null, _) { null }; + case (?(h, t), m) { ?(h, take(t, m - 1)) } + } + }; + + /// Drop the first `n` elements from the given list. + /// + /// Example: + /// ```motoko include=initialize + /// List.drop( + /// ?(0, ?(1, ?(2, null))), + /// 2 + /// ); // => ?(2, null) + /// ``` + /// + /// Runtime: O(n) + /// + /// Space: O(1) + public func drop(l : List, n : Nat) : List { + switch (l, n) { + case (l_, 0) { l_ }; + case (null, _) { null }; + case ((?(h, t)), m) { drop(t, m - 1) } + } + }; + + /// Collapses the elements in `list` into a single value by starting with `base` + /// and progessively combining elements into `base` with `combine`. Iteration runs + /// left to right. + /// + /// Example: + /// ```motoko include=initialize + /// import Nat "mo:base/Nat"; + /// + /// List.foldLeft( + /// ?(1, ?(2, ?(3, null))), + /// "", + /// func (acc, x) { acc # Nat.toText(x)} + /// ) // => "123" + /// ``` + /// + /// Runtime: O(size(list)) + /// + /// Space: O(1) heap, O(1) stack + /// + /// *Runtime and space assumes that `combine` runs in O(1) time and space. + public func foldLeft(list : List, base : S, combine : (S, T) -> S) : S { + switch list { + case null { base }; + case (?(h, t)) { foldLeft(t, combine(base, h), combine) } + } + }; + + /// Collapses the elements in `buffer` into a single value by starting with `base` + /// and progessively combining elements into `base` with `combine`. Iteration runs + /// right to left. + /// + /// Example: + /// ```motoko include=initialize + /// import Nat "mo:base/Nat"; + /// + /// List.foldRight( + /// ?(1, ?(2, ?(3, null))), + /// "", + /// func (x, acc) { Nat.toText(x) # acc} + /// ) // => "123" + /// ``` + /// + /// Runtime: O(size(list)) + /// + /// Space: O(1) heap, O(size(list)) stack + /// + /// *Runtime and space assumes that `combine` runs in O(1) time and space. + public func foldRight(list : List, base : S, combine : (T, S) -> S) : S { + switch list { + case null { base }; + case (?(h, t)) { combine(h, foldRight(t, base, combine)) } + } + }; + + /// Return the first element for which the given predicate `f` is true, + /// if such an element exists. + /// + /// Example: + /// ```motoko include=initialize + /// + /// List.find( + /// ?(1, ?(2, ?(3, null))), + /// func n { n > 1 } + /// ); // => ?2 + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(1) + /// + /// *Runtime and space assumes that `f` runs in O(1) time and space. + public func find(l : List, f : T -> Bool) : ?T { + switch l { + case null { null }; + case (?(h, t)) { if (f(h)) { ?h } else { find(t, f) } } + } + }; + + /// Return true if there exists a list element for which + /// the given predicate `f` is true. + /// + /// Example: + /// ```motoko include=initialize + /// + /// List.some( + /// ?(1, ?(2, ?(3, null))), + /// func n { n > 1 } + /// ) // => true + /// ``` + /// + /// Runtime: O(size(list)) + /// + /// Space: O(1) + /// + /// *Runtime and space assumes that `f` runs in O(1) time and space. + public func some(l : List, f : T -> Bool) : Bool { + switch l { + case null { false }; + case (?(h, t)) { f(h) or some(t, f) } + } + }; + + /// Return true if the given predicate `f` is true for all list + /// elements. + /// + /// Example: + /// ```motoko include=initialize + /// + /// List.all( + /// ?(1, ?(2, ?(3, null))), + /// func n { n > 1 } + /// ); // => false + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(1) + /// + /// *Runtime and space assumes that `f` runs in O(1) time and space. + public func all(l : List, f : T -> Bool) : Bool { + switch l { + case null { true }; + case (?(h, t)) { f(h) and all(t, f) } + } + }; + + /// Merge two ordered lists into a single ordered list. + /// This function requires both list to be ordered as specified + /// by the given relation `lessThanOrEqual`. + /// + /// Example: + /// ```motoko include=initialize + /// + /// List.merge( + /// ?(1, ?(2, ?(4, null))), + /// ?(2, ?(4, ?(6, null))), + /// func (n1, n2) { n1 <= n2 } + /// ); // => ?(1, ?(2, ?(2, ?(4, ?(4, ?(6, null))))))), + /// ``` + /// + /// Runtime: O(size(l1) + size(l2)) + /// + /// Space: O(size(l1) + size(l2)) + /// + /// *Runtime and space assumes that `lessThanOrEqual` runs in O(1) time and space. + // TODO: replace by merge taking a compare : (T, T) -> Order.Order function? + public func merge(l1 : List, l2 : List, lessThanOrEqual : (T, T) -> Bool) : List { + switch (l1, l2) { + case (null, _) { l2 }; + case (_, null) { l1 }; + case (?(h1, t1), ?(h2, t2)) { + if (lessThanOrEqual(h1, h2)) { + ?(h1, merge(t1, l2, lessThanOrEqual)) + } else { + ?(h2, merge(l1, t2, lessThanOrEqual)) + } + } + } + }; + + private func compareAux(l1 : List, l2 : List, compare : (T, T) -> Order.Order) : Order.Order { + switch (l1, l2) { + case (null, null) { #equal }; + case (null, _) { #less }; + case (_, null) { #greater }; + case (?(h1, t1), ?(h2, t2)) { + switch (compare(h1, h2)) { + case (#equal) { compareAux(t1, t2, compare) }; + case other { other } + } + } + } + }; + + /// Compare two lists using lexicographic ordering specified by argument function `compare`. + /// + /// Example: + /// ```motoko include=initialize + /// import Nat "mo:base/Nat"; + /// + /// List.compare( + /// ?(1, ?(2, null)), + /// ?(3, ?(4, null)), + /// Nat.compare + /// ) // => #less + /// ``` + /// + /// Runtime: O(size(l1)) + /// + /// Space: O(1) + /// + /// *Runtime and space assumes that argument `compare` runs in O(1) time and space. + public func compare(l1 : List, l2 : List, compare : (T, T) -> Order.Order) : Order.Order { + compareAux(l1, l2, compare); + }; + + private func equalAux(l1 : List, l2 : List, equal : (T, T) -> Bool) : Bool { + switch (l1, l2) { + case (?(h1, t1), ?(h2, t2)) { + equal(h1, h2) and equalAux(t1, t2, equal) + }; + case (null, null) { true }; + case _ { false }; + } + }; + /// Compare two lists for equality using the argument function `equal` to determine equality of their elements. + /// + /// Example: + /// ```motoko include=initialize + /// import Nat "mo:base/Nat"; + /// + /// List.equal( + /// ?(1, ?(2, null)), + /// ?(3, ?(4, null)), + /// Nat.equal + /// ); // => false + /// ``` + /// + /// Runtime: O(size(l1)) + /// + /// Space: O(1) + /// + /// *Runtime and space assumes that argument `equal` runs in O(1) time and space. + public func equal(l1 : List, l2 : List, equal : (T, T) -> Bool) : Bool { + equalAux(l1, l2, equal); + }; + + /// Generate a list based on a length and a function that maps from + /// a list index to a list element. + /// + /// Example: + /// ```motoko include=initialize + /// List.tabulate( + /// 3, + /// func n { n * 2 } + /// ) // => ?(0, ?(2, (?4, null))) + /// ``` + /// + /// Runtime: O(n) + /// + /// Space: O(n) + /// + /// *Runtime and space assumes that `f` runs in O(1) time and space. + public func tabulate(n : Nat, f : Nat -> T) : List { + var i = 0; + var l : List = null; + while (i < n) { + l := ?(f(i), l); + i += 1 + }; + reverse(l) + }; + + /// Create a list with exactly one element. + /// + /// Example: + /// ```motoko include=initialize + /// List.make( + /// 0 + /// ) // => ?(0, null) + /// ``` + /// + /// Runtime: O(1) + /// + /// Space: O(1) + public func make(x : T) : List = ?(x, null); + + /// Create a list of the given length with the same value in each position. + /// + /// Example: + /// ```motoko include=initialize + /// List.replicate( + /// 3, + /// 0 + /// ) // => ?(0, ?(0, ?(0, null))) + /// ``` + /// + /// Runtime: O(n) + /// + /// Space: O(n) + public func replicate(n : Nat, x : T) : List { + var i = 0; + var l : List = null; + while (i < n) { + l := ?(x, l); + i += 1 + }; + l + }; + + /// Create a list of pairs from a pair of lists. + /// + /// If the given lists have different lengths, then the created list will have a + /// length equal to the length of the smaller list. + /// + /// Example: + /// ```motoko include=initialize + /// List.zip( + /// ?(0, ?(1, ?(2, null))), + /// ?("0", ?("1", null)), + /// ) // => ?((0, "0"), ?((1, "1"), null)) + /// ``` + /// + /// Runtime: O(min(size(xs), size(ys))) + /// + /// Space: O(min(size(xs), size(ys))) + public func zip(xs : List, ys : List) : List<(T, U)> = zipWith(xs, ys, func(x, y) { (x, y) }); + + /// Create a list in which elements are created by applying function `f` to each pair `(x, y)` of elements + /// occuring at the same position in list `xs` and list `ys`. + /// + /// If the given lists have different lengths, then the created list will have a + /// length equal to the length of the smaller list. + /// + /// Example: + /// ```motoko include=initialize + /// import Nat = "mo:base/Nat"; + /// import Char = "mo:base/Char"; + /// + /// List.zipWith( + /// ?(0, ?(1, ?(2, null))), + /// ?('a', ?('b', null)), + /// func (n, c) { Nat.toText(n) # Char.toText(c) } + /// ) // => ?("0a", ?("1b", null)) + /// ``` + /// + /// Runtime: O(min(size(xs), size(ys))) + /// + /// Space: O(min(size(xs), size(ys))) + /// + /// *Runtime and space assumes that `zip` runs in O(1) time and space. + public func zipWith( + xs : List, + ys : List, + f : (T, U) -> V + ) : List { + switch (pop(xs)) { + case (null, _) { null }; + case (?x, xt) { + switch (pop(ys)) { + case (null, _) { null }; + case (?y, yt) { + push(f(x, y), zipWith(xt, yt, f)) + } + } + } + } + }; + + /// Split the given list at the given zero-based index. + /// + /// Example: + /// ```motoko include=initialize + /// List.split( + /// 2, + /// ?(0, ?(1, ?(2, null))) + /// ) // => (?(0, ?(1, null)), ?(2, null)) + /// ``` + /// + /// Runtime: O(n) + /// + /// Space: O(n) + /// + /// *Runtime and space assumes that `zip` runs in O(1) time and space. + public func split(n : Nat, xs : List) : (List, List) { + if (n == 0) { (null, xs) } else { + func rec(n : Nat, xs : List) : (List, List) { + switch (pop(xs)) { + case (null, _) { (null, null) }; + case (?h, t) { + if (n == 1) { (make(h), t) } else { + let (l, r) = rec(n - 1, t); + (push(h, l), r) + } + } + } + }; + rec(n, xs) + } + }; + + /// Split the given list into chunks of length `n`. + /// The last chunk will be shorter if the length of the given list + /// does not divide by `n` evenly. + /// + /// Example: + /// ```motoko include=initialize + /// List.chunks( + /// 2, + /// ?(0, ?(1, ?(2, ?(3, ?(4, null))))) + /// ) + /// /* => ?(?(0, ?(1, null)), + /// ?(?(2, ?(3, null)), + /// ?(?(4, null), + /// null))) + /// */ + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(size) + /// + /// *Runtime and space assumes that `zip` runs in O(1) time and space. + public func chunks(n : Nat, xs : List) : List> { + let (l, r) = split(n, xs); + if (isNil(l)) { + null + } else { + push>(l, chunks(n, r)) + } + }; + + /// Convert an array into a list. + /// + /// Example: + /// ```motoko include=initialize + /// List.fromArray([ 0, 1, 2, 3, 4]) + /// // => ?(0, ?(1, ?(2, ?(3, ?(4, null))))) + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(size) + public func fromArray(xs : [T]) : List { + Array.foldRight>( + xs, + null, + func(x : T, ys : List) : List { + push(x, ys) + } + ) + }; + + /// Convert a mutable array into a list. + /// + /// Example: + /// ```motoko include=initialize + /// List.fromVarArray([var 0, 1, 2, 3, 4]) + /// // => ?(0, ?(1, ?(2, ?(3, ?(4, null))))) + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(size) + public func fromVarArray(xs : [var T]) : List = fromArray(Array.freeze(xs)); + + /// Create an array from a list. + /// Example: + /// ```motoko include=initialize + /// List.toArray(?(0, ?(1, ?(2, ?(3, ?(4, null)))))) + /// // => [0, 1, 2, 3, 4] + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(size) + public func toArray(xs : List) : [T] { + let length = size(xs); + var list = xs; + Array.tabulate( + length, + func(i) { + let popped = pop(list); + list := popped.1; + switch (popped.0) { + case null { loop { assert false } }; + case (?x) x + } + } + ) + }; + + /// Create a mutable array from a list. + /// Example: + /// ```motoko include=initialize + /// List.toVarArray(?(0, ?(1, ?(2, ?(3, ?(4, null)))))) + /// // => [var 0, 1, 2, 3, 4] + /// ``` + /// + /// Runtime: O(size) + /// + /// Space: O(size) + public func toVarArray(xs : List) : [var T] = Array.thaw(toArray(xs)); + + /// Create an iterator from a list. + /// Example: + /// ```motoko include=initialize + /// var sum = 0; + /// for (n in List.toIter(?(0, ?(1, ?(2, ?(3, ?(4, null))))))) { + /// sum += n; + /// }; + /// sum + /// // => 10 + /// ``` + /// + /// Runtime: O(1) + /// + /// Space: O(1) + public func toIter(xs : List) : Iter.Iter { + var state = xs; + object { + public func next() : ?T = switch state { + case (?(hd, tl)) { state := tl; ?hd }; + case _ null + } + } + } + +} diff --git a/test/perf/sha224/Nat.mo b/test/perf/sha224/Nat.mo new file mode 100644 index 00000000000..bfab55387b2 --- /dev/null +++ b/test/perf/sha224/Nat.mo @@ -0,0 +1,305 @@ +/// Natural numbers with infinite precision. +/// +/// Most operations on natural numbers (e.g. addition) are available as built-in operators (e.g. `1 + 1`). +/// This module provides equivalent functions and `Text` conversion. +/// +/// Import from the base library to use this module. +/// ```motoko name=import +/// import Nat "mo:base/Nat"; +/// ``` + +import Int "Int"; +import Order "Order"; +import Prim "mo:⛔"; +import Char "Char"; + +module { + + /// Infinite precision natural numbers. + public type Nat = Prim.Types.Nat; + + /// Converts a natural number to its textual representation. Textual + /// representation _do not_ contain underscores to represent commas. + /// + /// Example: + /// ```motoko include=import + /// Nat.toText 1234 // => "1234" + /// ``` + public func toText(n : Nat) : Text = Int.toText n; + + /// Creates a natural number from its textual representation. Returns `null` + /// if the input is not a valid natural number. + /// + /// Note: The textual representation _must not_ contain underscores. + /// + /// Example: + /// ```motoko include=import + /// Nat.fromText "1234" // => ?1234 + /// ``` + public func fromText(text : Text) : ?Nat { + if (text == "") { + return null + }; + var n = 0; + for (c in text.chars()) { + if (Char.isDigit(c)) { + let charAsNat = Prim.nat32ToNat(Prim.charToNat32(c) -% Prim.charToNat32('0')); + n := n * 10 + charAsNat + } else { + return null + } + }; + ?n + }; + + /// Returns the minimum of `x` and `y`. + /// + /// Example: + /// ```motoko include=import + /// Nat.min(1, 2) // => 1 + /// ``` + public func min(x : Nat, y : Nat) : Nat { + if (x < y) { x } else { y } + }; + + /// Returns the maximum of `x` and `y`. + /// + /// Example: + /// ```motoko include=import + /// Nat.max(1, 2) // => 2 + /// ``` + public func max(x : Nat, y : Nat) : Nat { + if (x < y) { y } else { x } + }; + + /// Equality function for Nat types. + /// This is equivalent to `x == y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat.equal(1, 1); // => true + /// 1 == 1 // => true + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `==` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `==` + /// as a function value at the moment. + /// + /// Example: + /// ```motoko include=import + /// import Buffer "mo:base/Buffer"; + /// + /// let buffer1 = Buffer.Buffer(3); + /// let buffer2 = Buffer.Buffer(3); + /// Buffer.equal(buffer1, buffer2, Nat.equal) // => true + /// ``` + public func equal(x : Nat, y : Nat) : Bool { x == y }; + + /// Inequality function for Nat types. + /// This is equivalent to `x != y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat.notEqual(1, 2); // => true + /// 1 != 2 // => true + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `!=` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `!=` + /// as a function value at the moment. + public func notEqual(x : Nat, y : Nat) : Bool { x != y }; + + /// "Less than" function for Nat types. + /// This is equivalent to `x < y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat.less(1, 2); // => true + /// 1 < 2 // => true + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `<` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `<` + /// as a function value at the moment. + public func less(x : Nat, y : Nat) : Bool { x < y }; + + /// "Less than or equal" function for Nat types. + /// This is equivalent to `x <= y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat.lessOrEqual(1, 2); // => true + /// 1 <= 2 // => true + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `<=` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `<=` + /// as a function value at the moment. + public func lessOrEqual(x : Nat, y : Nat) : Bool { x <= y }; + + /// "Greater than" function for Nat types. + /// This is equivalent to `x > y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat.greater(2, 1); // => true + /// 2 > 1 // => true + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `>` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `>` + /// as a function value at the moment. + public func greater(x : Nat, y : Nat) : Bool { x > y }; + + /// "Greater than or equal" function for Nat types. + /// This is equivalent to `x >= y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat.greaterOrEqual(2, 1); // => true + /// 2 >= 1 // => true + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `>=` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `>=` + /// as a function value at the moment. + public func greaterOrEqual(x : Nat, y : Nat) : Bool { x >= y }; + + /// General purpose comparison function for `Nat`. Returns the `Order` ( + /// either `#less`, `#equal`, or `#greater`) of comparing `x` with `y`. + /// + /// Example: + /// ```motoko include=import + /// Nat.compare(2, 3) // => #less + /// ``` + /// + /// This function can be used as value for a high order function, such as a sort function. + /// + /// Example: + /// ```motoko include=import + /// import Array "mo:base/Array"; + /// Array.sort([2, 3, 1], Nat.compare) // => [1, 2, 3] + /// ``` + public func compare(x : Nat, y : Nat) : { #less; #equal; #greater } { + if (x < y) { #less } else if (x == y) { #equal } else { #greater } + }; + + /// Returns the sum of `x` and `y`, `x + y`. This operator will never overflow + /// because `Nat` is infinite precision. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat.add(1, 2); // => 3 + /// 1 + 2 // => 3 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `+` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `+` + /// as a function value at the moment. + /// + /// Example: + /// ```motoko include=import + /// import Array "mo:base/Array"; + /// Array.foldLeft([2, 3, 1], 0, Nat.add) // => 6 + /// ``` + public func add(x : Nat, y : Nat) : Nat { x + y }; + + /// Returns the difference of `x` and `y`, `x - y`. + /// Traps on underflow below `0`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat.sub(2, 1); // => 1 + /// // Add a type annotation to avoid a warning about the subtraction + /// 2 - 1 : Nat // => 1 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `-` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `-` + /// as a function value at the moment. + /// + /// Example: + /// ```motoko include=import + /// import Array "mo:base/Array"; + /// Array.foldLeft([2, 3, 1], 10, Nat.sub) // => 4 + /// ``` + public func sub(x : Nat, y : Nat) : Nat { x - y }; + + /// Returns the product of `x` and `y`, `x * y`. This operator will never + /// overflow because `Nat` is infinite precision. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat.mul(2, 3); // => 6 + /// 2 * 3 // => 6 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `*` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `*` + /// as a function value at the moment. + /// + /// Example: + /// ```motoko include=import + /// import Array "mo:base/Array"; + /// Array.foldLeft([2, 3, 1], 1, Nat.mul) // => 6 + /// ``` + public func mul(x : Nat, y : Nat) : Nat { x * y }; + + /// Returns the unsigned integer division of `x` by `y`, `x / y`. + /// Traps when `y` is zero. + /// + /// The quotient is rounded down, which is equivalent to truncating the + /// decimal places of the quotient. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat.div(6, 2); // => 3 + /// 6 / 2 // => 3 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `/` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `/` + /// as a function value at the moment. + public func div(x : Nat, y : Nat) : Nat { x / y }; + + /// Returns the remainder of unsigned integer division of `x` by `y`, `x % y`. + /// Traps when `y` is zero. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat.rem(6, 4); // => 2 + /// 6 % 4 // => 2 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `%` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `%` + /// as a function value at the moment. + public func rem(x : Nat, y : Nat) : Nat { x % y }; + + /// Returns `x` to the power of `y`, `x ** y`. Traps when `y > 2^32`. This operator + /// will never overflow because `Nat` is infinite precision. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat.pow(2, 3); // => 8 + /// 2 ** 3 // => 8 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `**` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `**` + /// as a function value at the moment. + public func pow(x : Nat, y : Nat) : Nat { x ** y }; + +} diff --git a/test/perf/sha224/Nat16.mo b/test/perf/sha224/Nat16.mo new file mode 100644 index 00000000000..f8ca1a289f1 --- /dev/null +++ b/test/perf/sha224/Nat16.mo @@ -0,0 +1,577 @@ +/// Provides utility functions on 16-bit unsigned integers. +/// +/// Note that most operations are available as built-in operators (e.g. `1 + 1`). +/// +/// Import from the base library to use this module. +/// ```motoko name=import +/// import Nat16 "mo:base/Nat16"; +/// ``` +import Nat "Nat"; +import Prim "mo:⛔"; + +module { + + /// 16-bit natural numbers. + public type Nat16 = Prim.Types.Nat16; + + /// Maximum 16-bit natural number. `2 ** 16 - 1`. + /// + /// Example: + /// ```motoko include=import + /// Nat16.maximumValue; // => 65536 : Nat16 + /// ``` + public let maximumValue = 65535 : Nat16; + + /// Converts a 16-bit unsigned integer to an unsigned integer with infinite precision. + /// + /// Example: + /// ```motoko include=import + /// Nat16.toNat(123); // => 123 : Nat + /// ``` + public let toNat : Nat16 -> Nat = Prim.nat16ToNat; + + /// Converts an unsigned integer with infinite precision to a 16-bit unsigned integer. + /// + /// Traps on overflow. + /// + /// Example: + /// ```motoko include=import + /// Nat16.fromNat(123); // => 123 : Nat16 + /// ``` + public let fromNat : Nat -> Nat16 = Prim.natToNat16; + + /// Converts an 8-bit unsigned integer to a 16-bit unsigned integer. + /// + /// Example: + /// ```motoko include=import + /// Nat16.fromNat8(123); // => 123 : Nat16 + /// ``` + public func fromNat8(x : Nat8) : Nat16 { + Prim.nat8ToNat16(x) + }; + + /// Converts a 16-bit unsigned integer to an 8-bit unsigned integer. + /// + /// Traps on overflow. + /// + /// Example: + /// ```motoko include=import + /// Nat16.toNat8(123); // => 123 : Nat8 + /// ``` + public func toNat8(x : Nat16) : Nat8 { + Prim.nat16ToNat8(x) + }; + + /// Converts a 32-bit unsigned integer to a 16-bit unsigned integer. + /// + /// Traps on overflow. + /// + /// Example: + /// ```motoko include=import + /// Nat16.fromNat32(123); // => 123 : Nat16 + /// ``` + public func fromNat32(x : Nat32) : Nat16 { + Prim.nat32ToNat16(x) + }; + + /// Converts a 16-bit unsigned integer to a 32-bit unsigned integer. + /// + /// Example: + /// ```motoko include=import + /// Nat16.toNat32(123); // => 123 : Nat32 + /// ``` + public func toNat32(x : Nat16) : Nat32 { + Prim.nat16ToNat32(x) + }; + + /// Converts a signed integer with infinite precision to a 16-bit unsigned integer. + /// + /// Wraps on overflow/underflow. + /// + /// Example: + /// ```motoko include=import + /// Nat16.fromIntWrap(123 : Int); // => 123 : Nat16 + /// ``` + public let fromIntWrap : Int -> Nat16 = Prim.intToNat16Wrap; + + /// Converts `x` to its textual representation. Textual representation _do not_ + /// contain underscores to represent commas. + /// + /// Example: + /// ```motoko include=import + /// Nat16.toText(1234); // => "1234" : Text + /// ``` + public func toText(x : Nat16) : Text { + Nat.toText(toNat(x)) + }; + + /// Returns the minimum of `x` and `y`. + /// + /// Example: + /// ```motoko include=import + /// Nat16.min(123, 200); // => 123 : Nat16 + /// ``` + public func min(x : Nat16, y : Nat16) : Nat16 { + if (x < y) { x } else { y } + }; + + /// Returns the maximum of `x` and `y`. + /// + /// Example: + /// ```motoko include=import + /// Nat16.max(123, 200); // => 200 : Nat16 + /// ``` + public func max(x : Nat16, y : Nat16) : Nat16 { + if (x < y) { y } else { x } + }; + + /// Equality function for Nat16 types. + /// This is equivalent to `x == y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat16.equal(1, 1); // => true + /// (1 : Nat16) == (1 : Nat16) // => true + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `==` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `==` + /// as a function value at the moment. + /// + /// Example: + /// ```motoko include=import + /// import Buffer "mo:base/Buffer"; + /// + /// let buffer1 = Buffer.Buffer(3); + /// let buffer2 = Buffer.Buffer(3); + /// Buffer.equal(buffer1, buffer2, Nat16.equal) // => true + /// ``` + public func equal(x : Nat16, y : Nat16) : Bool { x == y }; + + /// Inequality function for Nat16 types. + /// This is equivalent to `x != y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat16.notEqual(1, 2); // => true + /// (1 : Nat16) != (2 : Nat16) // => true + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `!=` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `!=` + /// as a function value at the moment. + public func notEqual(x : Nat16, y : Nat16) : Bool { x != y }; + + /// "Less than" function for Nat16 types. + /// This is equivalent to `x < y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat16.less(1, 2); // => true + /// (1 : Nat16) < (2 : Nat16) // => true + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `<` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `<` + /// as a function value at the moment. + public func less(x : Nat16, y : Nat16) : Bool { x < y }; + + /// "Less than or equal" function for Nat16 types. + /// This is equivalent to `x <= y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat16.lessOrEqual(1, 2); // => true + /// (1 : Nat16) <= (2 : Nat16) // => true + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `<=` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `<=` + /// as a function value at the moment. + public func lessOrEqual(x : Nat16, y : Nat16) : Bool { x <= y }; + + /// "Greater than" function for Nat16 types. + /// This is equivalent to `x > y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat16.greater(2, 1); // => true + /// (2 : Nat16) > (1 : Nat16) // => true + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `>` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `>` + /// as a function value at the moment. + public func greater(x : Nat16, y : Nat16) : Bool { x > y }; + + /// "Greater than or equal" function for Nat16 types. + /// This is equivalent to `x >= y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat16.greaterOrEqual(2, 1); // => true + /// (2 : Nat16) >= (1 : Nat16) // => true + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `>=` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `>=` + /// as a function value at the moment. + public func greaterOrEqual(x : Nat16, y : Nat16) : Bool { x >= y }; + + /// General purpose comparison function for `Nat16`. Returns the `Order` ( + /// either `#less`, `#equal`, or `#greater`) of comparing `x` with `y`. + /// + /// Example: + /// ```motoko include=import + /// Nat16.compare(2, 3) // => #less + /// ``` + /// + /// This function can be used as value for a high order function, such as a sort function. + /// + /// Example: + /// ```motoko include=import + /// import Array "mo:base/Array"; + /// Array.sort([2, 3, 1] : [Nat16], Nat16.compare) // => [1, 2, 3] + /// ``` + public func compare(x : Nat16, y : Nat16) : { #less; #equal; #greater } { + if (x < y) { #less } else if (x == y) { #equal } else { #greater } + }; + + /// Returns the sum of `x` and `y`, `x + y`. + /// Traps on overflow. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat16.add(1, 2); // => 3 + /// (1 : Nat16) + (2 : Nat16) // => 3 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `+` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `+` + /// as a function value at the moment. + /// + /// Example: + /// ```motoko include=import + /// import Array "mo:base/Array"; + /// Array.foldLeft([2, 3, 1], 0, Nat16.add) // => 6 + /// ``` + public func add(x : Nat16, y : Nat16) : Nat16 { x + y }; + + /// Returns the difference of `x` and `y`, `x - y`. + /// Traps on underflow. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat16.sub(2, 1); // => 1 + /// (2 : Nat16) - (1 : Nat16) // => 1 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `-` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `-` + /// as a function value at the moment. + /// + /// Example: + /// ```motoko include=import + /// import Array "mo:base/Array"; + /// Array.foldLeft([2, 3, 1], 20, Nat16.sub) // => 14 + /// ``` + public func sub(x : Nat16, y : Nat16) : Nat16 { x - y }; + + /// Returns the product of `x` and `y`, `x * y`. + /// Traps on overflow. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat16.mul(2, 3); // => 6 + /// (2 : Nat16) * (3 : Nat16) // => 6 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `*` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `*` + /// as a function value at the moment. + /// + /// Example: + /// ```motoko include=import + /// import Array "mo:base/Array"; + /// Array.foldLeft([2, 3, 1], 1, Nat16.mul) // => 6 + /// ``` + public func mul(x : Nat16, y : Nat16) : Nat16 { x * y }; + + /// Returns the quotient of `x` divided by `y`, `x / y`. + /// Traps when `y` is zero. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat16.div(6, 2); // => 3 + /// (6 : Nat16) / (2 : Nat16) // => 3 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `/` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `/` + /// as a function value at the moment. + public func div(x : Nat16, y : Nat16) : Nat16 { x / y }; + + /// Returns the remainder of `x` divided by `y`, `x % y`. + /// Traps when `y` is zero. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat16.rem(6, 4); // => 2 + /// (6 : Nat16) % (4 : Nat16) // => 2 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `%` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `%` + /// as a function value at the moment. + public func rem(x : Nat16, y : Nat16) : Nat16 { x % y }; + + /// Returns the power of `x` to `y`, `x ** y`. + /// Traps on overflow. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat16.pow(2, 3); // => 8 + /// (2 : Nat16) ** (3 : Nat16) // => 8 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `**` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `**` + /// as a function value at the moment. + public func pow(x : Nat16, y : Nat16) : Nat16 { x ** y }; + + /// Returns the bitwise negation of `x`, `^x`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat16.bitnot(0); // => 65535 + /// ^(0 : Nat16) // => 65535 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `^` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `^` + /// as a function value at the moment. + public func bitnot(x : Nat16) : Nat16 { ^x }; + + /// Returns the bitwise and of `x` and `y`, `x & y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat16.bitand(0, 1); // => 0 + /// (0 : Nat16) & (1 : Nat16) // => 0 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `&` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `&` + /// as a function value at the moment. + public func bitand(x : Nat16, y : Nat16) : Nat16 { x & y }; + + /// Returns the bitwise or of `x` and `y`, `x | y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat16.bitor(0, 1); // => 1 + /// (0 : Nat16) | (1 : Nat16) // => 1 + /// ``` + public func bitor(x : Nat16, y : Nat16) : Nat16 { x | y }; + + /// Returns the bitwise exclusive or of `x` and `y`, `x ^ y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat16.bitxor(0, 1); // => 1 + /// (0 : Nat16) ^ (1 : Nat16) // => 1 + /// ``` + public func bitxor(x : Nat16, y : Nat16) : Nat16 { x ^ y }; + + /// Returns the bitwise shift left of `x` by `y`, `x << y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat16.bitshiftLeft(1, 3); // => 8 + /// (1 : Nat16) << (3 : Nat16) // => 8 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `<<` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `<<` + /// as a function value at the moment. + public func bitshiftLeft(x : Nat16, y : Nat16) : Nat16 { x << y }; + + /// Returns the bitwise shift right of `x` by `y`, `x >> y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat16.bitshiftRight(8, 3); // => 1 + /// (8 : Nat16) >> (3 : Nat16) // => 1 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `>>` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `>>` + /// as a function value at the moment. + public func bitshiftRight(x : Nat16, y : Nat16) : Nat16 { x >> y }; + + /// Returns the bitwise rotate left of `x` by `y`, `x <<> y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat16.bitrotLeft(2, 1); // => 4 + /// (2 : Nat16) <<> (1 : Nat16) // => 4 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `<<>` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `<<>` + /// as a function value at the moment. + public func bitrotLeft(x : Nat16, y : Nat16) : Nat16 { x <<> y }; + + /// Returns the bitwise rotate right of `x` by `y`, `x <>> y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat16.bitrotRight(1, 1); // => 32768 + /// (1 : Nat16) <>> (1 : Nat16) // => 32768 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `<>>` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `<>>` + /// as a function value at the moment. + public func bitrotRight(x : Nat16, y : Nat16) : Nat16 { x <>> y }; + + /// Returns the value of bit `p mod 16` in `x`, `(x & 2^(p mod 16)) == 2^(p mod 16)`. + /// This is equivalent to checking if the `p`-th bit is set in `x`, using 0 indexing. + /// + /// Example: + /// ```motoko include=import + /// Nat16.bittest(5, 2); // => true + /// ``` + public func bittest(x : Nat16, p : Nat) : Bool { + Prim.btstNat16(x, Prim.natToNat16(p)) + }; + + /// Returns the value of setting bit `p mod 16` in `x` to `1`. + /// + /// Example: + /// ```motoko include=import + /// Nat16.bitset(0, 2); // => 4 + /// ``` + public func bitset(x : Nat16, p : Nat) : Nat16 { + x | (1 << Prim.natToNat16(p)) + }; + + /// Returns the value of clearing bit `p mod 16` in `x` to `0`. + /// + /// Example: + /// ```motoko include=import + /// Nat16.bitclear(5, 2); // => 1 + /// ``` + public func bitclear(x : Nat16, p : Nat) : Nat16 { + x & ^(1 << Prim.natToNat16(p)) + }; + + /// Returns the value of flipping bit `p mod 16` in `x`. + /// + /// Example: + /// ```motoko include=import + /// Nat16.bitflip(5, 2); // => 1 + /// ``` + public func bitflip(x : Nat16, p : Nat) : Nat16 { + x ^ (1 << Prim.natToNat16(p)) + }; + + /// Returns the count of non-zero bits in `x`. + /// + /// Example: + /// ```motoko include=import + /// Nat16.bitcountNonZero(5); // => 2 + /// ``` + public let bitcountNonZero : (x : Nat16) -> Nat16 = Prim.popcntNat16; + + /// Returns the count of leading zero bits in `x`. + /// + /// Example: + /// ```motoko include=import + /// Nat16.bitcountLeadingZero(5); // => 13 + /// ``` + public let bitcountLeadingZero : (x : Nat16) -> Nat16 = Prim.clzNat16; + + /// Returns the count of trailing zero bits in `x`. + /// + /// Example: + /// ```motoko include=import + /// Nat16.bitcountTrailingZero(5); // => 0 + /// ``` + public let bitcountTrailingZero : (x : Nat16) -> Nat16 = Prim.ctzNat16; + + /// Returns the sum of `x` and `y`, `x +% y`. Wraps on overflow. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat16.addWrap(65532, 5); // => 1 + /// (65532 : Nat16) +% (5 : Nat16) // => 1 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `+%` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `+%` + /// as a function value at the moment. + public func addWrap(x : Nat16, y : Nat16) : Nat16 { x +% y }; + + /// Returns the difference of `x` and `y`, `x -% y`. Wraps on underflow. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat16.subWrap(1, 2); // => 65535 + /// (1 : Nat16) -% (2 : Nat16) // => 65535 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `-%` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `-%` + /// as a function value at the moment. + public func subWrap(x : Nat16, y : Nat16) : Nat16 { x -% y }; + + /// Returns the product of `x` and `y`, `x *% y`. Wraps on overflow. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat16.mulWrap(655, 101); // => 619 + /// (655 : Nat16) *% (101 : Nat16) // => 619 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `*%` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `*%` + /// as a function value at the moment. + public func mulWrap(x : Nat16, y : Nat16) : Nat16 { x *% y }; + + /// Returns `x` to the power of `y`, `x **% y`. Wraps on overflow. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat16.powWrap(2, 16); // => 0 + /// (2 : Nat16) **% (16 : Nat16) // => 0 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `**%` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `**%` + /// as a function value at the moment. + public func powWrap(x : Nat16, y : Nat16) : Nat16 { x **% y }; + +} diff --git a/test/perf/sha224/Nat32.mo b/test/perf/sha224/Nat32.mo new file mode 100644 index 00000000000..1c92ae52444 --- /dev/null +++ b/test/perf/sha224/Nat32.mo @@ -0,0 +1,586 @@ +/// Provides utility functions on 32-bit unsigned integers. +/// +/// Note that most operations are available as built-in operators (e.g. `1 + 1`). +/// +/// Import from the base library to use this module. +/// ```motoko name=import +/// import Nat32 "mo:base/Nat32"; +/// ``` +import Nat "Nat"; +import Prim "mo:⛔"; + +module { + + /// 32-bit natural numbers. + public type Nat32 = Prim.Types.Nat32; + + /// Maximum 32-bit natural number. `2 ** 32 - 1`. + /// + /// Example: + /// ```motoko include=import + /// Nat32.maximumValue; // => 4294967295 : Nat32 + /// ``` + public let maximumValue = 4294967295 : Nat32; + + /// Converts a 32-bit unsigned integer to an unsigned integer with infinite precision. + /// + /// Example: + /// ```motoko include=import + /// Nat32.toNat(123); // => 123 : Nat + /// ``` + public let toNat : Nat32 -> Nat = Prim.nat32ToNat; + + /// Converts an unsigned integer with infinite precision to a 32-bit unsigned integer. + /// + /// Traps on overflow. + /// + /// Example: + /// ```motoko include=import + /// Nat32.fromNat(123); // => 123 : Nat32 + /// ``` + public let fromNat : Nat -> Nat32 = Prim.natToNat32; + + /// Converts a 16-bit unsigned integer to a 32-bit unsigned integer. + /// + /// Example: + /// ```motoko include=import + /// Nat32.fromNat16(123); // => 123 : Nat32 + /// ``` + public func fromNat16(x : Nat16) : Nat32 { + Prim.nat16ToNat32(x) + }; + + /// Converts a 32-bit unsigned integer to a 16-bit unsigned integer. + /// + /// Traps on overflow. + /// + /// Example: + /// ```motoko include=import + /// Nat32.toNat16(123); // => 123 : Nat16 + /// ``` + public func toNat16(x : Nat32) : Nat16 { + Prim.nat32ToNat16(x) + }; + + /// Converts a 64-bit unsigned integer to a 32-bit unsigned integer. + /// + /// Traps on overflow. + /// + /// Example: + /// ```motoko include=import + /// Nat32.fromNat64(123); // => 123 : Nat32 + /// ``` + public func fromNat64(x : Nat64) : Nat32 { + Prim.nat64ToNat32(x) + }; + + /// Converts a 32-bit unsigned integer to a 64-bit unsigned integer. + /// + /// Example: + /// ```motoko include=import + /// Nat32.toNat64(123); // => 123 : Nat64 + /// ``` + public func toNat64(x : Nat32) : Nat64 { + Prim.nat32ToNat64(x) + }; + + /// Converts a signed integer with infinite precision to a 32-bit unsigned integer. + /// + /// Traps on overflow/underflow. + /// + /// Example: + /// ```motoko include=import + /// Nat32.fromIntWrap(123); // => 123 : Nat32 + /// ``` + public let fromIntWrap : Int -> Nat32 = Prim.intToNat32Wrap; + + /// Converts `x` to its textual representation. Textual representation _do not_ + /// contain underscores to represent commas. + /// + /// Example: + /// ```motoko include=import + /// Nat32.toText(1234); // => "1234" : Text + /// ``` + public func toText(x : Nat32) : Text { + Nat.toText(toNat(x)) + }; + + /// Returns the minimum of `x` and `y`. + /// + /// Example: + /// ```motoko include=import + /// Nat32.min(123, 456); // => 123 : Nat32 + /// ``` + public func min(x : Nat32, y : Nat32) : Nat32 { + if (x < y) { x } else { y } + }; + + /// Returns the maximum of `x` and `y`. + /// + /// Example: + /// ```motoko include=import + /// Nat32.max(123, 456); // => 456 : Nat32 + /// ``` + public func max(x : Nat32, y : Nat32) : Nat32 { + if (x < y) { y } else { x } + }; + + /// Equality function for Nat32 types. + /// This is equivalent to `x == y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat32.equal(1, 1); // => true + /// (1 : Nat32) == (1 : Nat32) // => true + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `==` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `==` + /// as a function value at the moment. + /// + /// Example: + /// ```motoko include=import + /// import Buffer "mo:base/Buffer"; + /// + /// let buffer1 = Buffer.Buffer(3); + /// let buffer2 = Buffer.Buffer(3); + /// Buffer.equal(buffer1, buffer2, Nat32.equal) // => true + /// ``` + public func equal(x : Nat32, y : Nat32) : Bool { x == y }; + + /// Inequality function for Nat32 types. + /// This is equivalent to `x != y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat32.notEqual(1, 2); // => true + /// (1 : Nat32) != (2 : Nat32) // => true + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `!=` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `!=` + /// as a function value at the moment. + public func notEqual(x : Nat32, y : Nat32) : Bool { x != y }; + + /// "Less than" function for Nat32 types. + /// This is equivalent to `x < y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat32.less(1, 2); // => true + /// (1 : Nat32) < (2 : Nat32) // => true + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `<` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `<` + /// as a function value at the moment. + public func less(x : Nat32, y : Nat32) : Bool { x < y }; + + /// "Less than or equal" function for Nat32 types. + /// This is equivalent to `x <= y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat32.lessOrEqual(1, 2); // => true + /// (1 : Nat32) <= (2 : Nat32) // => true + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `<=` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `<=` + /// as a function value at the moment. + public func lessOrEqual(x : Nat32, y : Nat32) : Bool { x <= y }; + + /// "Greater than" function for Nat32 types. + /// This is equivalent to `x > y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat32.greater(2, 1); // => true + /// (2 : Nat32) > (1 : Nat32) // => true + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `>` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `>` + /// as a function value at the moment. + public func greater(x : Nat32, y : Nat32) : Bool { x > y }; + + /// "Greater than or equal" function for Nat32 types. + /// This is equivalent to `x >= y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat32.greaterOrEqual(2, 1); // => true + /// (2 : Nat32) >= (1 : Nat32) // => true + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `>=` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `>=` + /// as a function value at the moment. + public func greaterOrEqual(x : Nat32, y : Nat32) : Bool { x >= y }; + + /// General purpose comparison function for `Nat32`. Returns the `Order` ( + /// either `#less`, `#equal`, or `#greater`) of comparing `x` with `y`. + /// + /// Example: + /// ```motoko include=import + /// Nat32.compare(2, 3) // => #less + /// ``` + /// + /// This function can be used as value for a high order function, such as a sort function. + /// + /// Example: + /// ```motoko include=import + /// import Array "mo:base/Array"; + /// Array.sort([2, 3, 1] : [Nat32], Nat32.compare) // => [1, 2, 3] + /// ``` + public func compare(x : Nat32, y : Nat32) : { #less; #equal; #greater } { + if (x < y) { #less } else if (x == y) { #equal } else { #greater } + }; + + /// Returns the sum of `x` and `y`, `x + y`. + /// Traps on overflow. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat32.add(1, 2); // => 3 + /// (1 : Nat32) + (2 : Nat32) // => 3 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `+` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `+` + /// as a function value at the moment. + /// + /// Example: + /// ```motoko include=import + /// import Array "mo:base/Array"; + /// Array.foldLeft([2, 3, 1], 0, Nat32.add) // => 6 + /// ``` + public func add(x : Nat32, y : Nat32) : Nat32 { x + y }; + + /// Returns the difference of `x` and `y`, `x - y`. + /// Traps on underflow. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat32.sub(2, 1); // => 1 + /// (2 : Nat32) - (1 : Nat32) // => 1 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `-` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `-` + /// as a function value at the moment. + /// + /// Example: + /// ```motoko include=import + /// import Array "mo:base/Array"; + /// Array.foldLeft([2, 3, 1], 20, Nat32.sub) // => 14 + /// ``` + public func sub(x : Nat32, y : Nat32) : Nat32 { x - y }; + + /// Returns the product of `x` and `y`, `x * y`. + /// Traps on overflow. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat32.mul(2, 3); // => 6 + /// (2 : Nat32) * (3 : Nat32) // => 6 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `*` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `*` + /// as a function value at the moment. + /// + /// Example: + /// ```motoko include=import + /// import Array "mo:base/Array"; + /// Array.foldLeft([2, 3, 1], 1, Nat32.mul) // => 6 + /// ``` + public func mul(x : Nat32, y : Nat32) : Nat32 { x * y }; + + /// Returns the division of `x by y`, `x / y`. + /// Traps when `y` is zero. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat32.div(6, 2); // => 3 + /// (6 : Nat32) / (2 : Nat32) // => 3 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `/` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `/` + /// as a function value at the moment. + public func div(x : Nat32, y : Nat32) : Nat32 { x / y }; + + /// Returns the remainder of `x` divided by `y`, `x % y`. + /// Traps when `y` is zero. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat32.rem(6, 4); // => 2 + /// (6 : Nat32) % (4 : Nat32) // => 2 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `%` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `%` + /// as a function value at the moment. + public func rem(x : Nat32, y : Nat32) : Nat32 { x % y }; + + /// Returns `x` to the power of `y`, `x ** y`. Traps on overflow. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat32.pow(2, 3); // => 8 + /// (2 : Nat32) ** (3 : Nat32) // => 8 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `**` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `**` + /// as a function value at the moment. + public func pow(x : Nat32, y : Nat32) : Nat32 { x ** y }; + + /// Returns the bitwise negation of `x`, `^x`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat32.bitnot(0) // => 4294967295 + /// ^(0 : Nat32) // => 4294967295 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `^` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `^` + /// as a function value at the moment. + public func bitnot(x : Nat32) : Nat32 { ^x }; + + /// Returns the bitwise and of `x` and `y`, `x & y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat32.bitand(1, 3); // => 1 + /// (1 : Nat32) & (3 : Nat32) // => 1 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `&` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `&` + /// as a function value at the moment. + public func bitand(x : Nat32, y : Nat32) : Nat32 { x & y }; + + /// Returns the bitwise or of `x` and `y`, `x | y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat32.bitor(1, 3); // => 3 + /// (1 : Nat32) | (3 : Nat32) // => 3 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `|` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `|` + /// as a function value at the moment. + public func bitor(x : Nat32, y : Nat32) : Nat32 { x | y }; + + /// Returns the bitwise exclusive or of `x` and `y`, `x ^ y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat32.bitxor(1, 3); // => 2 + /// (1 : Nat32) ^ (3 : Nat32) // => 2 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `^` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `^` + /// as a function value at the moment. + public func bitxor(x : Nat32, y : Nat32) : Nat32 { x ^ y }; + + /// Returns the bitwise shift left of `x` by `y`, `x << y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat32.bitshiftLeft(1, 3); // => 8 + /// (1 : Nat32) << (3 : Nat32) // => 8 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `<<` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `<<` + /// as a function value at the moment. + public func bitshiftLeft(x : Nat32, y : Nat32) : Nat32 { x << y }; + + /// Returns the bitwise shift right of `x` by `y`, `x >> y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat32.bitshiftRight(8, 3); // => 1 + /// (8 : Nat32) >> (3 : Nat32) // => 1 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `>>` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `>>` + /// as a function value at the moment. + public func bitshiftRight(x : Nat32, y : Nat32) : Nat32 { x >> y }; + + /// Returns the bitwise rotate left of `x` by `y`, `x <<> y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat32.bitrotLeft(1, 3); // => 8 + /// (1 : Nat32) <<> (3 : Nat32) // => 8 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `<<>` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `<<>` + /// as a function value at the moment. + public func bitrotLeft(x : Nat32, y : Nat32) : Nat32 { x <<> y }; + + /// Returns the bitwise rotate right of `x` by `y`, `x <>> y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat32.bitrotRight(1, 1); // => 2147483648 + /// (1 : Nat32) <>> (1 : Nat32) // => 2147483648 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `<>>` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `<>>` + /// as a function value at the moment. + public func bitrotRight(x : Nat32, y : Nat32) : Nat32 { x <>> y }; + + /// Returns the value of bit `p mod 32` in `x`, `(x & 2^(p mod 32)) == 2^(p mod 32)`. + /// This is equivalent to checking if the `p`-th bit is set in `x`, using 0 indexing. + /// + /// Example: + /// ```motoko include=import + /// Nat32.bittest(5, 2); // => true + /// ``` + public func bittest(x : Nat32, p : Nat) : Bool { + Prim.btstNat32(x, Prim.natToNat32(p)) + }; + + /// Returns the value of setting bit `p mod 32` in `x` to `1`. + /// + /// Example: + /// ```motoko include=import + /// Nat32.bitset(5, 1); // => 7 + /// ``` + public func bitset(x : Nat32, p : Nat) : Nat32 { + x | (1 << Prim.natToNat32(p)) + }; + + /// Returns the value of clearing bit `p mod 32` in `x` to `0`. + /// + /// Example: + /// ```motoko include=import + /// Nat32.bitclear(5, 2); // => 1 + /// ``` + public func bitclear(x : Nat32, p : Nat) : Nat32 { + x & ^(1 << Prim.natToNat32(p)) + }; + + /// Returns the value of flipping bit `p mod 32` in `x`. + /// + /// Example: + /// ```motoko include=import + /// Nat32.bitflip(5, 2); // => 1 + /// ``` + public func bitflip(x : Nat32, p : Nat) : Nat32 { + x ^ (1 << Prim.natToNat32(p)) + }; + + /// Returns the count of non-zero bits in `x`. + /// + /// Example: + /// ```motoko include=import + /// Nat32.bitcountNonZero(5); // => 2 + /// ``` + public let bitcountNonZero : (x : Nat32) -> Nat32 = Prim.popcntNat32; + + /// Returns the count of leading zero bits in `x`. + /// + /// Example: + /// ```motoko include=import + /// Nat32.bitcountLeadingZero(5); // => 29 + /// ``` + public let bitcountLeadingZero : (x : Nat32) -> Nat32 = Prim.clzNat32; + + /// Returns the count of trailing zero bits in `x`. + /// + /// Example: + /// ```motoko include=import + /// Nat32.bitcountTrailingZero(16); // => 4 + /// ``` + public let bitcountTrailingZero : (x : Nat32) -> Nat32 = Prim.ctzNat32; + + /// Returns the sum of `x` and `y`, `x +% y`. Wraps on overflow. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat32.addWrap(4294967295, 1); // => 0 + /// (4294967295 : Nat32) +% (1 : Nat32) // => 0 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `+%` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `+%` + /// as a function value at the moment. + public func addWrap(x : Nat32, y : Nat32) : Nat32 { x +% y }; + + /// Returns the difference of `x` and `y`, `x -% y`. Wraps on underflow. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat32.subWrap(0, 1); // => 4294967295 + /// (0 : Nat32) -% (1 : Nat32) // => 4294967295 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `-%` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `-%` + /// as a function value at the moment. + public func subWrap(x : Nat32, y : Nat32) : Nat32 { x -% y }; + + /// Returns the product of `x` and `y`, `x *% y`. Wraps on overflow. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat32.mulWrap(2147483648, 2); // => 0 + /// (2147483648 : Nat32) *% (2 : Nat32) // => 0 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `*%` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `*%` + /// as a function value at the moment. + public func mulWrap(x : Nat32, y : Nat32) : Nat32 { x *% y }; + + /// Returns `x` to the power of `y`, `x **% y`. Wraps on overflow. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat32.powWrap(2, 32); // => 0 + /// (2 : Nat32) **% (32 : Nat32) // => 0 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `**%` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `**%` + /// as a function value at the moment. + public func powWrap(x : Nat32, y : Nat32) : Nat32 { x **% y }; + +} diff --git a/test/perf/sha224/Nat64.mo b/test/perf/sha224/Nat64.mo new file mode 100644 index 00000000000..3724fce48c2 --- /dev/null +++ b/test/perf/sha224/Nat64.mo @@ -0,0 +1,565 @@ +/// Provides utility functions on 64-bit unsigned integers. +/// +/// Note that most operations are available as built-in operators (e.g. `1 + 1`). +/// +/// Import from the base library to use this module. +/// ```motoko name=import +/// import Nat64 "mo:base/Nat64"; +/// ``` +import Nat "Nat"; +import Prim "mo:⛔"; + +module { + + /// 64-bit natural numbers. + public type Nat64 = Prim.Types.Nat64; + + /// Maximum 64-bit natural number. `2 ** 64 - 1`. + /// + /// Example: + /// ```motoko include=import + /// Nat64.maximumValue; // => 18446744073709551615 : Nat64 + /// ``` + + public let maximumValue = 18446744073709551615 : Nat64; + + /// Converts a 64-bit unsigned integer to an unsigned integer with infinite precision. + /// + /// Example: + /// ```motoko include=import + /// Nat64.toNat(123); // => 123 : Nat + /// ``` + public let toNat : Nat64 -> Nat = Prim.nat64ToNat; + + /// Converts an unsigned integer with infinite precision to a 64-bit unsigned integer. + /// + /// Traps on overflow. + /// + /// Example: + /// ```motoko include=import + /// Nat64.fromNat(123); // => 123 : Nat64 + /// ``` + public let fromNat : Nat -> Nat64 = Prim.natToNat64; + + /// Converts a 32-bit unsigned integer to a 64-bit unsigned integer. + /// + /// Example: + /// ```motoko include=import + /// Nat64.fromNat32(123); // => 123 : Nat64 + /// ``` + public func fromNat32(x : Nat32) : Nat64 { + Prim.nat32ToNat64(x) + }; + + /// Converts a 64-bit unsigned integer to a 32-bit unsigned integer. + /// + /// Traps on overflow. + /// + /// Example: + /// ```motoko include=import + /// Nat64.toNat32(123); // => 123 : Nat32 + /// ``` + public func toNat32(x : Nat64) : Nat32 { + Prim.nat64ToNat32(x) + }; + + /// Converts a signed integer with infinite precision to a 64-bit unsigned integer. + /// + /// Traps on overflow/underflow. + /// + /// Example: + /// ```motoko include=import + /// Nat64.fromIntWrap(123); // => 123 : Nat64 + /// ``` + public let fromIntWrap : Int -> Nat64 = Prim.intToNat64Wrap; + + /// Converts `x` to its textual representation. Textual representation _do not_ + /// contain underscores to represent commas. + /// + /// Example: + /// ```motoko include=import + /// Nat64.toText(1234); // => "1234" : Text + /// ``` + public func toText(x : Nat64) : Text { + Nat.toText(toNat(x)) + }; + + /// Returns the minimum of `x` and `y`. + /// + /// Example: + /// ```motoko include=import + /// Nat64.min(123, 456); // => 123 : Nat64 + /// ``` + public func min(x : Nat64, y : Nat64) : Nat64 { + if (x < y) { x } else { y } + }; + + /// Returns the maximum of `x` and `y`. + /// + /// Example: + /// ```motoko include=import + /// Nat64.max(123, 456); // => 456 : Nat64 + /// ``` + public func max(x : Nat64, y : Nat64) : Nat64 { + if (x < y) { y } else { x } + }; + + /// Equality function for Nat64 types. + /// This is equivalent to `x == y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat64.equal(1, 1); // => true + /// (1 : Nat64) == (1 : Nat64) // => true + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `==` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `==` + /// as a function value at the moment. + /// + /// Example: + /// ```motoko include=import + /// import Buffer "mo:base/Buffer"; + /// + /// let buffer1 = Buffer.Buffer(3); + /// let buffer2 = Buffer.Buffer(3); + /// Buffer.equal(buffer1, buffer2, Nat64.equal) // => true + /// ``` + public func equal(x : Nat64, y : Nat64) : Bool { x == y }; + + /// Inequality function for Nat64 types. + /// This is equivalent to `x != y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat64.notEqual(1, 2); // => true + /// (1 : Nat64) != (2 : Nat64) // => true + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `!=` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `!=` + /// as a function value at the moment. + public func notEqual(x : Nat64, y : Nat64) : Bool { x != y }; + + /// "Less than" function for Nat64 types. + /// This is equivalent to `x < y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat64.less(1, 2); // => true + /// (1 : Nat64) < (2 : Nat64) // => true + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `<` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `<` + /// as a function value at the moment. + public func less(x : Nat64, y : Nat64) : Bool { x < y }; + + /// "Less than or equal" function for Nat64 types. + /// This is equivalent to `x <= y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat64.lessOrEqual(1, 2); // => true + /// (1 : Nat64) <= (2 : Nat64) // => true + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `<=` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `<=` + /// as a function value at the moment. + public func lessOrEqual(x : Nat64, y : Nat64) : Bool { x <= y }; + + /// "Greater than" function for Nat64 types. + /// This is equivalent to `x > y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat64.greater(2, 1); // => true + /// (2 : Nat64) > (1 : Nat64) // => true + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `>` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `>` + /// as a function value at the moment. + public func greater(x : Nat64, y : Nat64) : Bool { x > y }; + + /// "Greater than or equal" function for Nat64 types. + /// This is equivalent to `x >= y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat64.greaterOrEqual(2, 1); // => true + /// (2 : Nat64) >= (1 : Nat64) // => true + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `>=` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `>=` + /// as a function value at the moment. + public func greaterOrEqual(x : Nat64, y : Nat64) : Bool { x >= y }; + + /// General purpose comparison function for `Nat64`. Returns the `Order` ( + /// either `#less`, `#equal`, or `#greater`) of comparing `x` with `y`. + /// + /// Example: + /// ```motoko include=import + /// Nat64.compare(2, 3) // => #less + /// ``` + /// + /// This function can be used as value for a high order function, such as a sort function. + /// + /// Example: + /// ```motoko include=import + /// import Array "mo:base/Array"; + /// Array.sort([2, 3, 1] : [Nat64], Nat64.compare) // => [1, 2, 3] + /// ``` + public func compare(x : Nat64, y : Nat64) : { #less; #equal; #greater } { + if (x < y) { #less } else if (x == y) { #equal } else { #greater } + }; + + /// Returns the sum of `x` and `y`, `x + y`. + /// Traps on overflow. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat64.add(1, 2); // => 3 + /// (1 : Nat64) + (2 : Nat64) // => 3 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `+` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `+` + /// as a function value at the moment. + /// + /// Example: + /// ```motoko include=import + /// import Array "mo:base/Array"; + /// Array.foldLeft([2, 3, 1], 0, Nat64.add) // => 6 + /// ``` + public func add(x : Nat64, y : Nat64) : Nat64 { x + y }; + + /// Returns the difference of `x` and `y`, `x - y`. + /// Traps on underflow. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat64.sub(3, 1); // => 2 + /// (3 : Nat64) - (1 : Nat64) // => 2 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `-` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `-` + /// as a function value at the moment. + /// + /// Example: + /// ```motoko include=import + /// import Array "mo:base/Array"; + /// Array.foldLeft([2, 3, 1], 10, Nat64.sub) // => 4 + /// ``` + public func sub(x : Nat64, y : Nat64) : Nat64 { x - y }; + + /// Returns the product of `x` and `y`, `x * y`. + /// Traps on overflow. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat64.mul(2, 3); // => 6 + /// (2 : Nat64) * (3 : Nat64) // => 6 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `*` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `*` + /// as a function value at the moment. + /// + /// Example: + /// ```motoko include=import + /// import Array "mo:base/Array"; + /// Array.foldLeft([2, 3, 1], 1, Nat64.mul) // => 6 + /// ``` + public func mul(x : Nat64, y : Nat64) : Nat64 { x * y }; + + /// Returns the quotient of `x` divided by `y`, `x / y`. + /// Traps when `y` is zero. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat64.div(6, 2); // => 3 + /// (6 : Nat64) / (2 : Nat64) // => 3 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `/` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `/` + /// as a function value at the moment. + public func div(x : Nat64, y : Nat64) : Nat64 { x / y }; + + /// Returns the remainder of `x` divided by `y`, `x % y`. + /// Traps when `y` is zero. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat64.rem(6, 4); // => 2 + /// (6 : Nat64) % (4 : Nat64) // => 2 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `%` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `%` + /// as a function value at the moment. + public func rem(x : Nat64, y : Nat64) : Nat64 { x % y }; + + /// Returns `x` to the power of `y`, `x ** y`. Traps on overflow. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat64.pow(2, 3); // => 8 + /// (2 : Nat64) ** (3 : Nat64) // => 8 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `**` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `**` + /// as a function value at the moment. + public func pow(x : Nat64, y : Nat64) : Nat64 { x ** y }; + + /// Returns the bitwise negation of `x`, `^x`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat64.bitnot(0); // => 18446744073709551615 + /// ^(0 : Nat64) // => 18446744073709551615 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `^` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `^` + /// as a function value at the moment. + public func bitnot(x : Nat64) : Nat64 { ^x }; + + /// Returns the bitwise and of `x` and `y`, `x & y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat64.bitand(1, 3); // => 1 + /// (1 : Nat64) & (3 : Nat64) // => 1 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `&` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `&` + /// as a function value at the moment. + public func bitand(x : Nat64, y : Nat64) : Nat64 { x & y }; + + /// Returns the bitwise or of `x` and `y`, `x | y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat64.bitor(1, 3); // => 3 + /// (1 : Nat64) | (3 : Nat64) // => 3 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `|` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `|` + /// as a function value at the moment. + public func bitor(x : Nat64, y : Nat64) : Nat64 { x | y }; + + /// Returns the bitwise exclusive or of `x` and `y`, `x ^ y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat64.bitxor(1, 3); // => 2 + /// (1 : Nat64) ^ (3 : Nat64) // => 2 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `^` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `^` + /// as a function value at the moment. + public func bitxor(x : Nat64, y : Nat64) : Nat64 { x ^ y }; + + /// Returns the bitwise shift left of `x` by `y`, `x << y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat64.bitshiftLeft(1, 3); // => 8 + /// (1 : Nat64) << (3 : Nat64) // => 8 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `<<` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `<<` + /// as a function value at the moment. + public func bitshiftLeft(x : Nat64, y : Nat64) : Nat64 { x << y }; + + /// Returns the bitwise shift right of `x` by `y`, `x >> y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat64.bitshiftRight(8, 3); // => 1 + /// (8 : Nat64) >> (3 : Nat64) // => 1 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `>>` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `>>` + /// as a function value at the moment. + public func bitshiftRight(x : Nat64, y : Nat64) : Nat64 { x >> y }; + + /// Returns the bitwise rotate left of `x` by `y`, `x <<> y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat64.bitrotLeft(1, 3); // => 8 + /// (1 : Nat64) <<> (3 : Nat64) // => 8 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `<<>` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `<<>` + /// as a function value at the moment. + public func bitrotLeft(x : Nat64, y : Nat64) : Nat64 { x <<> y }; + + /// Returns the bitwise rotate right of `x` by `y`, `x <>> y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat64.bitrotRight(8, 3); // => 1 + /// (8 : Nat64) <>> (3 : Nat64) // => 1 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `<>>` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `<>>` + /// as a function value at the moment. + public func bitrotRight(x : Nat64, y : Nat64) : Nat64 { x <>> y }; + + /// Returns the value of bit `p mod 64` in `x`, `(x & 2^(p mod 64)) == 2^(p mod 64)`. + /// This is equivalent to checking if the `p`-th bit is set in `x`, using 0 indexing. + /// + /// Example: + /// ```motoko include=import + /// Nat64.bittest(5, 2); // => true + /// ``` + public func bittest(x : Nat64, p : Nat) : Bool { + Prim.btstNat64(x, Prim.natToNat64(p)) + }; + + /// Returns the value of setting bit `p mod 64` in `x` to `1`. + /// + /// Example: + /// ```motoko include=import + /// Nat64.bitset(5, 1); // => 7 + /// ``` + public func bitset(x : Nat64, p : Nat) : Nat64 { + x | (1 << Prim.natToNat64(p)) + }; + + /// Returns the value of clearing bit `p mod 64` in `x` to `0`. + /// + /// Example: + /// ```motoko include=import + /// Nat64.bitclear(5, 2); // => 1 + /// ``` + public func bitclear(x : Nat64, p : Nat) : Nat64 { + x & ^(1 << Prim.natToNat64(p)) + }; + + /// Returns the value of flipping bit `p mod 64` in `x`. + /// + /// Example: + /// ```motoko include=import + /// Nat64.bitflip(5, 2); // => 1 + /// ``` + public func bitflip(x : Nat64, p : Nat) : Nat64 { + x ^ (1 << Prim.natToNat64(p)) + }; + + /// Returns the count of non-zero bits in `x`. + /// + /// Example: + /// ```motoko include=import + /// Nat64.bitcountNonZero(5); // => 2 + /// ``` + public let bitcountNonZero : (x : Nat64) -> Nat64 = Prim.popcntNat64; + + /// Returns the count of leading zero bits in `x`. + /// + /// Example: + /// ```motoko include=import + /// Nat64.bitcountLeadingZero(5); // => 61 + /// ``` + public let bitcountLeadingZero : (x : Nat64) -> Nat64 = Prim.clzNat64; + + /// Returns the count of trailing zero bits in `x`. + /// + /// Example: + /// ```motoko include=import + /// Nat64.bitcountTrailingZero(16); // => 4 + /// ``` + public let bitcountTrailingZero : (x : Nat64) -> Nat64 = Prim.ctzNat64; + + /// Returns the sum of `x` and `y`, `x +% y`. Wraps on overflow. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat64.addWrap(Nat64.maximumValue, 1); // => 0 + /// Nat64.maximumValue +% (1 : Nat64) // => 0 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `+%` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `+%` + /// as a function value at the moment. + public func addWrap(x : Nat64, y : Nat64) : Nat64 { x +% y }; + + /// Returns the difference of `x` and `y`, `x -% y`. Wraps on underflow. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat64.subWrap(0, 1); // => 18446744073709551615 + /// (0 : Nat64) -% (1 : Nat64) // => 18446744073709551615 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `-%` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `-%` + /// as a function value at the moment. + public func subWrap(x : Nat64, y : Nat64) : Nat64 { x -% y }; + + /// Returns the product of `x` and `y`, `x *% y`. Wraps on overflow. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat64.mulWrap(4294967296, 4294967296); // => 0 + /// (4294967296 : Nat64) *% (4294967296 : Nat64) // => 0 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `*%` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `*%` + /// as a function value at the moment. + public func mulWrap(x : Nat64, y : Nat64) : Nat64 { x *% y }; + + /// Returns `x` to the power of `y`, `x **% y`. Wraps on overflow. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat64.powWrap(2, 64); // => 0 + /// (2 : Nat64) **% (64 : Nat64) // => 0 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `**%` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `**%` + /// as a function value at the moment. + public func powWrap(x : Nat64, y : Nat64) : Nat64 { x **% y }; + +} diff --git a/test/perf/sha224/Nat8.mo b/test/perf/sha224/Nat8.mo new file mode 100644 index 00000000000..540991d17dc --- /dev/null +++ b/test/perf/sha224/Nat8.mo @@ -0,0 +1,559 @@ +/// Provides utility functions on 8-bit unsigned integers. +/// +/// Note that most operations are available as built-in operators (e.g. `1 + 1`). +/// +/// Import from the base library to use this module. +/// ```motoko name=import +/// import Nat8 "mo:base/Nat8"; +/// ``` +import Nat "Nat"; +import Prim "mo:⛔"; + +module { + + /// 8-bit natural numbers. + public type Nat8 = Prim.Types.Nat8; + + /// Maximum 8-bit natural number. `2 ** 8 - 1`. + /// + /// Example: + /// ```motoko include=import + /// Nat8.maximumValue; // => 255 : Nat8 + /// ``` + public let maximumValue = 255 : Nat8; + + /// Converts an 8-bit unsigned integer to an unsigned integer with infinite precision. + /// + /// Example: + /// ```motoko include=import + /// Nat8.toNat(123); // => 123 : Nat + /// ``` + public let toNat : Nat8 -> Nat = Prim.nat8ToNat; + + /// Converts an unsigned integer with infinite precision to an 8-bit unsigned integer. + /// + /// Traps on overflow. + /// + /// Example: + /// ```motoko include=import + /// Nat8.fromNat(123); // => 123 : Nat8 + /// ``` + public let fromNat : Nat -> Nat8 = Prim.natToNat8; + + /// Converts a 16-bit unsigned integer to a 8-bit unsigned integer. + /// + /// Traps on overflow. + /// + /// Example: + /// ```motoko include=import + /// Nat8.fromNat16(123); // => 123 : Nat8 + /// ``` + public let fromNat16 : Nat16 -> Nat8 = Prim.nat16ToNat8; + + /// Converts an 8-bit unsigned integer to a 16-bit unsigned integer. + /// + /// Example: + /// ```motoko include=import + /// Nat8.toNat16(123); // => 123 : Nat16 + /// ``` + public let toNat16 : Nat8 -> Nat16 = Prim.nat8ToNat16; + + /// Converts a signed integer with infinite precision to an 8-bit unsigned integer. + /// + /// Wraps on overflow/underflow. + /// + /// Example: + /// ```motoko include=import + /// Nat8.fromIntWrap(123); // => 123 : Nat8 + /// ``` + public let fromIntWrap : Int -> Nat8 = Prim.intToNat8Wrap; + + /// Converts `x` to its textual representation. + /// + /// Example: + /// ```motoko include=import + /// Nat8.toText(123); // => "123" : Text + /// ``` + public func toText(x : Nat8) : Text { + Nat.toText(toNat(x)) + }; + + /// Returns the minimum of `x` and `y`. + /// + /// Example: + /// ```motoko include=import + /// Nat8.min(123, 200); // => 123 : Nat8 + /// ``` + public func min(x : Nat8, y : Nat8) : Nat8 { + if (x < y) { x } else { y } + }; + + /// Returns the maximum of `x` and `y`. + /// + /// Example: + /// ```motoko include=import + /// Nat8.max(123, 200); // => 200 : Nat8 + /// ``` + public func max(x : Nat8, y : Nat8) : Nat8 { + if (x < y) { y } else { x } + }; + + /// Equality function for Nat8 types. + /// This is equivalent to `x == y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat8.equal(1, 1); // => true + /// (1 : Nat8) == (1 : Nat8) // => true + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `==` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `==` + /// as a function value at the moment. + /// + /// Example: + /// ```motoko include=import + /// import Buffer "mo:base/Buffer"; + /// + /// let buffer1 = Buffer.Buffer(3); + /// let buffer2 = Buffer.Buffer(3); + /// Buffer.equal(buffer1, buffer2, Nat8.equal) // => true + /// ``` + public func equal(x : Nat8, y : Nat8) : Bool { x == y }; + + /// Inequality function for Nat8 types. + /// This is equivalent to `x != y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat8.notEqual(1, 2); // => true + /// (1 : Nat8) != (2 : Nat8) // => true + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `!=` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `!=` + /// as a function value at the moment. + public func notEqual(x : Nat8, y : Nat8) : Bool { x != y }; + + /// "Less than" function for Nat8 types. + /// This is equivalent to `x < y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat8.less(1, 2); // => true + /// (1 : Nat8) < (2 : Nat8) // => true + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `<` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `<` + /// as a function value at the moment. + public func less(x : Nat8, y : Nat8) : Bool { x < y }; + + /// "Less than or equal" function for Nat8 types. + /// This is equivalent to `x <= y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat.lessOrEqual(1, 2); // => true + /// 1 <= 2 // => true + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `<=` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `<=` + /// as a function value at the moment. + public func lessOrEqual(x : Nat8, y : Nat8) : Bool { x <= y }; + + /// "Greater than" function for Nat8 types. + /// This is equivalent to `x > y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat8.greater(2, 1); // => true + /// (2 : Nat8) > (1 : Nat8) // => true + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `>` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `>` + /// as a function value at the moment. + public func greater(x : Nat8, y : Nat8) : Bool { x > y }; + + /// "Greater than or equal" function for Nat8 types. + /// This is equivalent to `x >= y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat8.greaterOrEqual(2, 1); // => true + /// (2 : Nat8) >= (1 : Nat8) // => true + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `>=` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `>=` + /// as a function value at the moment. + public func greaterOrEqual(x : Nat8, y : Nat8) : Bool { x >= y }; + + /// General purpose comparison function for `Nat8`. Returns the `Order` ( + /// either `#less`, `#equal`, or `#greater`) of comparing `x` with `y`. + /// + /// Example: + /// ```motoko include=import + /// Nat8.compare(2, 3) // => #less + /// ``` + /// + /// This function can be used as value for a high order function, such as a sort function. + /// + /// Example: + /// ```motoko include=import + /// import Array "mo:base/Array"; + /// Array.sort([2, 3, 1] : [Nat8], Nat8.compare) // => [1, 2, 3] + /// ``` + public func compare(x : Nat8, y : Nat8) : { #less; #equal; #greater } { + if (x < y) { #less } else if (x == y) { #equal } else { #greater } + }; + + /// Returns the sum of `x` and `y`, `x + y`. + /// Traps on overflow. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat8.add(1, 2); // => 3 + /// (1 : Nat8) + (2 : Nat8) // => 3 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `+` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `+` + /// as a function value at the moment. + /// + /// Example: + /// ```motoko include=import + /// import Array "mo:base/Array"; + /// Array.foldLeft([2, 3, 1], 0, Nat8.add) // => 6 + /// ``` + public func add(x : Nat8, y : Nat8) : Nat8 { x + y }; + + /// Returns the difference of `x` and `y`, `x - y`. + /// Traps on underflow. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat8.sub(2, 1); // => 1 + /// (2 : Nat8) - (1 : Nat8) // => 1 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `-` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `-` + /// as a function value at the moment. + /// + /// Example: + /// ```motoko include=import + /// import Array "mo:base/Array"; + /// Array.foldLeft([2, 3, 1], 20, Nat8.sub) // => 14 + /// ``` + public func sub(x : Nat8, y : Nat8) : Nat8 { x - y }; + + /// Returns the product of `x` and `y`, `x * y`. + /// Traps on overflow. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat8.mul(2, 3); // => 6 + /// (2 : Nat8) * (3 : Nat8) // => 6 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `*` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `*` + /// as a function value at the moment. + /// + /// Example: + /// ```motoko include=import + /// import Array "mo:base/Array"; + /// Array.foldLeft([2, 3, 1], 1, Nat8.mul) // => 6 + /// ``` + public func mul(x : Nat8, y : Nat8) : Nat8 { x * y }; + + /// Returns the quotient of `x` divided by `y`, `x / y`. + /// Traps when `y` is zero. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat8.div(6, 2); // => 3 + /// (6 : Nat8) / (2 : Nat8) // => 3 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `/` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `/` + /// as a function value at the moment. + public func div(x : Nat8, y : Nat8) : Nat8 { x / y }; + + /// Returns the remainder of `x` divided by `y`, `x % y`. + /// Traps when `y` is zero. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat8.rem(6, 4); // => 2 + /// (6 : Nat8) % (4 : Nat8) // => 2 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `%` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `%` + /// as a function value at the moment. + public func rem(x : Nat8, y : Nat8) : Nat8 { x % y }; + + /// Returns `x` to the power of `y`, `x ** y`. + /// Traps on overflow. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat8.pow(2, 3); // => 8 + /// (2 : Nat8) ** (3 : Nat8) // => 8 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `**` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `**` + /// as a function value at the moment. + public func pow(x : Nat8, y : Nat8) : Nat8 { x ** y }; + + /// Returns the bitwise negation of `x`, `^x`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat8.bitnot(0); // => 255 + /// ^(0 : Nat8) // => 255 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `^` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `^` + /// as a function value at the moment. + public func bitnot(x : Nat8) : Nat8 { ^x }; + + /// Returns the bitwise and of `x` and `y`, `x & y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat8.bitand(3, 2); // => 2 + /// (3 : Nat8) & (2 : Nat8) // => 2 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `&` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `&` + /// as a function value at the moment. + public func bitand(x : Nat8, y : Nat8) : Nat8 { x & y }; + + /// Returns the bitwise or of `x` and `y`, `x | y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat8.bitor(3, 2); // => 3 + /// (3 : Nat8) | (2 : Nat8) // => 3 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `|` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `|` + /// as a function value at the moment. + public func bitor(x : Nat8, y : Nat8) : Nat8 { x | y }; + + /// Returns the bitwise exclusive or of `x` and `y`, `x ^ y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat8.bitxor(3, 2); // => 1 + /// (3 : Nat8) ^ (2 : Nat8) // => 1 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `^` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `^` + /// as a function value at the moment. + public func bitxor(x : Nat8, y : Nat8) : Nat8 { x ^ y }; + + /// Returns the bitwise shift left of `x` by `y`, `x << y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat8.bitshiftLeft(1, 2); // => 4 + /// (1 : Nat8) << (2 : Nat8) // => 4 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `<<` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `<<` + /// as a function value at the moment. + public func bitshiftLeft(x : Nat8, y : Nat8) : Nat8 { x << y }; + + /// Returns the bitwise shift right of `x` by `y`, `x >> y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat8.bitshiftRight(4, 2); // => 1 + /// (4 : Nat8) >> (2 : Nat8) // => 1 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `>>` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `>>` + /// as a function value at the moment. + public func bitshiftRight(x : Nat8, y : Nat8) : Nat8 { x >> y }; + + /// Returns the bitwise rotate left of `x` by `y`, `x <<> y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat8.bitrotLeft(128, 1); // => 1 + /// (128 : Nat8) <<> (1 : Nat8) // => 1 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `<<>` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `<<>` + /// as a function value at the moment. + public func bitrotLeft(x : Nat8, y : Nat8) : Nat8 { x <<> y }; + + /// Returns the bitwise rotate right of `x` by `y`, `x <>> y`. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat8.bitrotRight(1, 1); // => 128 + /// (1 : Nat8) <>> (1 : Nat8) // => 128 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `<>>` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `<>>` + /// as a function value at the moment. + public func bitrotRight(x : Nat8, y : Nat8) : Nat8 { x <>> y }; + + /// Returns the value of bit `p mod 8` in `x`, `(x & 2^(p mod 8)) == 2^(p mod 8)`. + /// This is equivalent to checking if the `p`-th bit is set in `x`, using 0 indexing. + /// + /// Example: + /// ```motoko include=import + /// Nat8.bittest(5, 2); // => true + /// ``` + public func bittest(x : Nat8, p : Nat) : Bool { + Prim.btstNat8(x, Prim.natToNat8(p)) + }; + + /// Returns the value of setting bit `p mod 8` in `x` to `1`. + /// + /// Example: + /// ```motoko include=import + /// Nat8.bitset(5, 1); // => 7 + /// ``` + public func bitset(x : Nat8, p : Nat) : Nat8 { + x | (1 << Prim.natToNat8(p)) + }; + + /// Returns the value of clearing bit `p mod 8` in `x` to `0`. + /// + /// Example: + /// ```motoko include=import + /// Nat8.bitclear(5, 2); // => 1 + /// ``` + public func bitclear(x : Nat8, p : Nat) : Nat8 { + x & ^(1 << Prim.natToNat8(p)) + }; + + /// Returns the value of flipping bit `p mod 8` in `x`. + /// + /// Example: + /// ```motoko include=import + /// Nat8.bitflip(5, 2); // => 1 + /// ``` + public func bitflip(x : Nat8, p : Nat) : Nat8 { + x ^ (1 << Prim.natToNat8(p)) + }; + + /// Returns the count of non-zero bits in `x`. + /// + /// Example: + /// ```motoko include=import + /// Nat8.bitcountNonZero(5); // => 2 + /// ``` + public let bitcountNonZero : (x : Nat8) -> Nat8 = Prim.popcntNat8; + + /// Returns the count of leading zero bits in `x`. + /// + /// Example: + /// ```motoko include=import + /// Nat8.bitcountLeadingZero(5); // => 5 + /// ``` + public let bitcountLeadingZero : (x : Nat8) -> Nat8 = Prim.clzNat8; + + /// Returns the count of trailing zero bits in `x`. + /// + /// Example: + /// ```motoko include=import + /// Nat8.bitcountTrailingZero(6); // => 1 + /// ``` + public let bitcountTrailingZero : (x : Nat8) -> Nat8 = Prim.ctzNat8; + + /// Returns the sum of `x` and `y`, `x +% y`. Wraps on overflow. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat8.addWrap(230, 26); // => 0 + /// (230 : Nat8) +% (26 : Nat8) // => 0 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `+%` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `+%` + /// as a function value at the moment. + public func addWrap(x : Nat8, y : Nat8) : Nat8 { x +% y }; + + /// Returns the difference of `x` and `y`, `x -% y`. Wraps on underflow. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat8.subWrap(0, 1); // => 255 + /// (0 : Nat8) -% (1 : Nat8) // => 255 + /// ``` + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `-%` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `-%` + /// as a function value at the moment. + public func subWrap(x : Nat8, y : Nat8) : Nat8 { x -% y }; + + /// Returns the product of `x` and `y`, `x *% y`. Wraps on overflow. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat8.mulWrap(230, 26); // => 92 + /// (230 : Nat8) *% (26 : Nat8) // => 92 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `*%` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `*%` + /// as a function value at the moment. + public func mulWrap(x : Nat8, y : Nat8) : Nat8 { x *% y }; + + /// Returns `x` to the power of `y`, `x **% y`. Wraps on overflow. + /// + /// Example: + /// ```motoko include=import + /// ignore Nat8.powWrap(2, 8); // => 0 + /// (2 : Nat8) **% (8 : Nat8) // => 0 + /// ``` + /// + /// Note: The reason why this function is defined in this library (in addition + /// to the existing `**%` operator) is so that you can use it as a function + /// value to pass to a higher order function. It is not possible to use `**%` + /// as a function value at the moment. + public func powWrap(x : Nat8, y : Nat8) : Nat8 { x **% y }; + +} diff --git a/test/perf/sha224/Option.mo b/test/perf/sha224/Option.mo new file mode 100644 index 00000000000..1af5d4eddff --- /dev/null +++ b/test/perf/sha224/Option.mo @@ -0,0 +1,161 @@ +/// Typesafe nulls +/// +/// Optional values can be seen as a typesafe `null`. A value of type `?Int` can +/// be constructed with either `null` or `?42`. The simplest way to get at the +/// contents of an optional is to use pattern matching: +/// +/// ```motoko +/// let optionalInt1 : ?Int = ?42; +/// let optionalInt2 : ?Int = null; +/// +/// let int1orZero : Int = switch optionalInt1 { +/// case null 0; +/// case (?int) int; +/// }; +/// assert int1orZero == 42; +/// +/// let int2orZero : Int = switch optionalInt2 { +/// case null 0; +/// case (?int) int; +/// }; +/// assert int2orZero == 0; +/// ``` +/// +/// The functions in this module capture some common operations when working +/// with optionals that can be more succinct than using pattern matching. + +import P "Prelude"; + +module { + + /// Unwraps an optional value, with a default value, i.e. `get(?x, d) = x` and + /// `get(null, d) = d`. + public func get(x : ?T, default : T) : T = switch x { + case null { default }; + case (?x_) { x_ } + }; + + /// Unwraps an optional value using a function, or returns the default, i.e. + /// `option(?x, f, d) = f x` and `option(null, f, d) = d`. + public func getMapped(x : ?A, f : A -> B, default : B) : B = switch x { + case null { default }; + case (?x_) { f(x_) } + }; + + /// Applies a function to the wrapped value. `null`'s are left untouched. + /// ```motoko + /// import Option "mo:base/Option"; + /// assert Option.map(?42, func x = x + 1) == ?43; + /// assert Option.map(null, func x = x + 1) == null; + /// ``` + public func map(x : ?A, f : A -> B) : ?B = switch x { + case null { null }; + case (?x_) { ?f(x_) } + }; + + /// Applies a function to the wrapped value, but discards the result. Use + /// `iterate` if you're only interested in the side effect `f` produces. + /// + /// ```motoko + /// import Option "mo:base/Option"; + /// var counter : Nat = 0; + /// Option.iterate(?5, func (x : Nat) { counter += x }); + /// assert counter == 5; + /// Option.iterate(null, func (x : Nat) { counter += x }); + /// assert counter == 5; + /// ``` + public func iterate(x : ?A, f : A -> ()) = switch x { + case null {}; + case (?x_) { f(x_) } + }; + + /// Applies an optional function to an optional value. Returns `null` if at + /// least one of the arguments is `null`. + public func apply(x : ?A, f : ?(A -> B)) : ?B { + switch (f, x) { + case (?f_, ?x_) { + ?f_(x_) + }; + case (_, _) { + null + } + } + }; + + /// Applies a function to an optional value. Returns `null` if the argument is + /// `null`, or the function returns `null`. + public func chain(x : ?A, f : A -> ?B) : ?B { + switch (x) { + case (?x_) { + f(x_) + }; + case (null) { + null + } + } + }; + + /// Given an optional optional value, removes one layer of optionality. + /// ```motoko + /// import Option "mo:base/Option"; + /// assert Option.flatten(?(?(42))) == ?42; + /// assert Option.flatten(?(null)) == null; + /// assert Option.flatten(null) == null; + /// ``` + public func flatten(x : ??A) : ?A { + chain( + x, + func(x_ : ?A) : ?A { + x_ + } + ) + }; + + /// Creates an optional value from a definite value. + /// ```motoko + /// import Option "mo:base/Option"; + /// assert Option.make(42) == ?42; + /// ``` + public func make(x : A) : ?A = ?x; + + /// Returns true if the argument is not `null`, otherwise returns false. + public func isSome(x : ?Any) : Bool = switch x { + case null { false }; + case _ { true } + }; + + /// Returns true if the argument is `null`, otherwise returns false. + public func isNull(x : ?Any) : Bool = switch x { + case null { true }; + case _ { false } + }; + + /// Returns true if the optional arguments are equal according to the equality function provided, otherwise returns false. + public func equal(x : ?A, y : ?A, eq : (A, A) -> Bool) : Bool = switch (x, y) { + case (null, null) { true }; + case (?x_, ?y_) { eq(x_, y_) }; + case (_, _) { false } + }; + + /// Asserts that the value is not `null`; fails otherwise. + /// @deprecated Option.assertSome will be removed soon; use an assert expression instead + public func assertSome(x : ?Any) = switch x { + case null { P.unreachable() }; + case _ {} + }; + + /// Asserts that the value _is_ `null`; fails otherwise. + /// @deprecated Option.assertNull will be removed soon; use an assert expression instead + public func assertNull(x : ?Any) = switch x { + case null {}; + case _ { P.unreachable() } + }; + + /// Unwraps an optional value, i.e. `unwrap(?x) = x`. + /// + /// @deprecated Option.unwrap is unsafe and fails if the argument is null; it will be removed soon; use a `switch` or `do?` expression instead + public func unwrap(x : ?T) : T = switch x { + case null { P.unreachable() }; + case (?x_) { x_ } + } +} diff --git a/test/perf/sha224/Order.mo b/test/perf/sha224/Order.mo new file mode 100644 index 00000000000..da271ed8d58 --- /dev/null +++ b/test/perf/sha224/Order.mo @@ -0,0 +1,46 @@ +/// Order + +module { + + /// A type to represent an order. + public type Order = { + #less; + #equal; + #greater + }; + + /// Check if an order is #less. + public func isLess(order : Order) : Bool { + switch order { + case (#less) { true }; + case _ { false } + } + }; + + /// Check if an order is #equal. + public func isEqual(order : Order) : Bool { + switch order { + case (#equal) { true }; + case _ { false } + } + }; + + /// Check if an order is #greater. + public func isGreater(order : Order) : Bool { + switch order { + case (#greater) { true }; + case _ { false } + } + }; + + /// Returns true if only if `o1` and `o2` are the same ordering. + public func equal(o1 : Order, o2 : Order) : Bool { + switch (o1, o2) { + case (#less, #less) { true }; + case (#equal, #equal) { true }; + case (#greater, #greater) { true }; + case _ { false } + } + }; + +} diff --git a/test/perf/sha224/Prelude.mo b/test/perf/sha224/Prelude.mo new file mode 100644 index 00000000000..f16f35d653a --- /dev/null +++ b/test/perf/sha224/Prelude.mo @@ -0,0 +1,33 @@ +/// General utilities +/// +/// This prelude file proposes standard library features that _may_ +/// belong in the _language_ (compiler-internal) prelude sometime, after +/// some further experience and discussion. Until then, they live here. + +import Debug "Debug"; + +module { + + /// Not yet implemented + /// + /// Mark incomplete code with the `nyi` and `xxx` functions. + /// + /// Each have calls are well-typed in all typing contexts, which + /// trap in all execution contexts. + public func nyi() : None { + Debug.trap("Prelude.nyi()") + }; + + public func xxx() : None { + Debug.trap("Prelude.xxx()") + }; + + /// Mark unreachable code with the `unreachable` function. + /// + /// Calls are well-typed in all typing contexts, and they + /// trap in all execution contexts. + public func unreachable() : None { + Debug.trap("Prelude.unreachable()") + }; + +} diff --git a/test/perf/sha224/Result.mo b/test/perf/sha224/Result.mo new file mode 100644 index 00000000000..68ff6f0be08 --- /dev/null +++ b/test/perf/sha224/Result.mo @@ -0,0 +1,209 @@ +/// Error handling with the Result type. + +import Prim "mo:⛔"; +import P "Prelude"; +import Order "Order"; + +module { + + /// `Result` is the type used for returning and propagating errors. It + /// is a type with the variants, `#ok(Ok)`, representing success and containing + /// a value, and `#err(Err)`, representing error and containing an error value. + /// + /// The simplest way of working with `Result`s is to pattern match on them: + /// + /// For example, given a function `createUser(user : User) : Result` + /// where `String` is an error message we could use it like so: + /// ```motoko no-repl + /// switch(createUser(myUser)) { + /// case (#ok(id)) { Debug.print("Created new user with id: " # id) }; + /// case (#err(msg)) { Debug.print("Failed to create user with the error: " # msg) }; + /// } + /// ``` + public type Result = { + #ok : Ok; + #err : Err + }; + + // Compares two Result's for equality. + public func equal( + eqOk : (Ok, Ok) -> Bool, + eqErr : (Err, Err) -> Bool, + r1 : Result, + r2 : Result + ) : Bool { + switch (r1, r2) { + case (#ok(ok1), #ok(ok2)) { + eqOk(ok1, ok2) + }; + case (#err(err1), #err(err2)) { + eqErr(err1, err2) + }; + case _ { false } + } + }; + + // Compares two Results. `#ok` is larger than `#err`. This ordering is + // arbitrary, but it lets you for example use Results as keys in ordered maps. + public func compare( + compareOk : (Ok, Ok) -> Order.Order, + compareErr : (Err, Err) -> Order.Order, + r1 : Result, + r2 : Result + ) : Order.Order { + switch (r1, r2) { + case (#ok(ok1), #ok(ok2)) { + compareOk(ok1, ok2) + }; + case (#err(err1), #err(err2)) { + compareErr(err1, err2) + }; + case (#ok(_), _) { #greater }; + case (#err(_), _) { #less } + } + }; + + /// Allows sequencing of `Result` values and functions that return + /// `Result`'s themselves. + /// ```motoko + /// import Result "mo:base/Result"; + /// type Result = Result.Result; + /// func largerThan10(x : Nat) : Result = + /// if (x > 10) { #ok(x) } else { #err("Not larger than 10.") }; + /// + /// func smallerThan20(x : Nat) : Result = + /// if (x < 20) { #ok(x) } else { #err("Not smaller than 20.") }; + /// + /// func between10And20(x : Nat) : Result = + /// Result.chain(largerThan10(x), smallerThan20); + /// + /// assert(between10And20(15) == #ok(15)); + /// assert(between10And20(9) == #err("Not larger than 10.")); + /// assert(between10And20(21) == #err("Not smaller than 20.")); + /// ``` + public func chain( + x : Result, + y : R1 -> Result + ) : Result { + switch x { + case (#err(e)) { #err(e) }; + case (#ok(r)) { y(r) } + } + }; + + /// Flattens a nested Result. + /// + /// ```motoko + /// import Result "mo:base/Result"; + /// assert(Result.flatten(#ok(#ok(10))) == #ok(10)); + /// assert(Result.flatten(#err("Wrong")) == #err("Wrong")); + /// assert(Result.flatten(#ok(#err("Wrong"))) == #err("Wrong")); + /// ``` + public func flatten( + result : Result, Error> + ) : Result { + switch result { + case (#ok(ok)) { ok }; + case (#err(err)) { #err(err) } + } + }; + + /// Maps the `Ok` type/value, leaving any `Error` type/value unchanged. + public func mapOk( + x : Result, + f : Ok1 -> Ok2 + ) : Result { + switch x { + case (#err(e)) { #err(e) }; + case (#ok(r)) { #ok(f(r)) } + } + }; + + /// Maps the `Err` type/value, leaving any `Ok` type/value unchanged. + public func mapErr( + x : Result, + f : Error1 -> Error2 + ) : Result { + switch x { + case (#err(e)) { #err(f(e)) }; + case (#ok(r)) { #ok(r) } + } + }; + + /// Create a result from an option, including an error value to handle the `null` case. + /// ```motoko + /// import Result "mo:base/Result"; + /// assert(Result.fromOption(?42, "err") == #ok(42)); + /// assert(Result.fromOption(null, "err") == #err("err")); + /// ``` + public func fromOption(x : ?R, err : E) : Result { + switch x { + case (?x) { #ok(x) }; + case null { #err(err) } + } + }; + + /// Create an option from a result, turning all #err into `null`. + /// ```motoko + /// import Result "mo:base/Result"; + /// assert(Result.toOption(#ok(42)) == ?42); + /// assert(Result.toOption(#err("err")) == null); + /// ``` + public func toOption(r : Result) : ?R { + switch r { + case (#ok(x)) { ?x }; + case (#err(_)) { null } + } + }; + + /// Applies a function to a successful value, but discards the result. Use + /// `iterate` if you're only interested in the side effect `f` produces. + /// + /// ```motoko + /// import Result "mo:base/Result"; + /// var counter : Nat = 0; + /// Result.iterate(#ok(5), func (x : Nat) { counter += x }); + /// assert(counter == 5); + /// Result.iterate(#err("Wrong"), func (x : Nat) { counter += x }); + /// assert(counter == 5); + /// ``` + public func iterate(res : Result, f : Ok -> ()) { + switch res { + case (#ok(ok)) { f(ok) }; + case _ {} + } + }; + + // Whether this Result is an `#ok` + public func isOk(r : Result) : Bool { + switch r { + case (#ok(_)) { true }; + case (#err(_)) { false } + } + }; + + // Whether this Result is an `#err` + public func isErr(r : Result) : Bool { + switch r { + case (#ok(_)) { false }; + case (#err(_)) { true } + } + }; + + /// Asserts that its argument is an `#ok` result, traps otherwise. + public func assertOk(r : Result) { + switch (r) { + case (#err(_)) { assert false }; + case (#ok(_)) {} + } + }; + + /// Asserts that its argument is an `#err` result, traps otherwise. + public func assertErr(r : Result) { + switch (r) { + case (#err(_)) {}; + case (#ok(_)) assert false + } + }; + +}