From 8d782d8bf27f1b4646909b684a57e8afdc359581 Mon Sep 17 00:00:00 2001 From: logicalmechanism Date: Tue, 18 Feb 2025 18:48:07 -0800 Subject: [PATCH 1/7] its a start, would need a lot of work and use of fuzzy wuzzy --- aiken.toml | 2 +- lib/aiken/math/bitwise.ak | 135 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 lib/aiken/math/bitwise.ak diff --git a/aiken.toml b/aiken.toml index 4d5f84f..8333752 100644 --- a/aiken.toml +++ b/aiken.toml @@ -1,6 +1,6 @@ name = "aiken-lang/stdlib" version = "main" -compiler = "v1.1.9" +compiler = "v1.1.11" plutus = "v3" description = "The Aiken Standard Library" diff --git a/lib/aiken/math/bitwise.ak b/lib/aiken/math/bitwise.ak new file mode 100644 index 0000000..8f0d19c --- /dev/null +++ b/lib/aiken/math/bitwise.ak @@ -0,0 +1,135 @@ +use aiken/builtin + +pub fn add(a: ByteArray, b: ByteArray) -> ByteArray { + if builtin.count_set_bits(b) == 0 { + a + } else { + let carry: ByteArray = + builtin.and_bytearray(True, a, b) |> builtin.shift_bytearray(1) + let sum_without_carry: ByteArray = builtin.xor_bytearray(True, a, b) + add(sum_without_carry, carry) + } +} + +test one_plus_one() { + let one = builtin.integer_to_bytearray(True, 0, 1) + let two = builtin.integer_to_bytearray(True, 0, 2) + add(one, one) == two +} + +test add_to_zero() { + // add(#"00", #"acab") == #"acab" + add(#"acab", #"00") == #"acab" +} + +test mix_addition() { + let a = builtin.integer_to_bytearray(True, 0, 12) + let b = builtin.integer_to_bytearray(True, 0, 34151) + add(a, b) == add(b, a) +} + +test small_addition() { + let a = builtin.integer_to_bytearray(True, 0, 31) + let b = builtin.integer_to_bytearray(True, 0, 51) + let result = builtin.integer_to_bytearray(True, 0, 82) + add(a, b) == result +} + +test medium_addition() { + let a = builtin.integer_to_bytearray(True, 0, 1313) + let b = builtin.integer_to_bytearray(True, 0, 1514) + let result = builtin.integer_to_bytearray(True, 0, 2827) + add(a, b) == result +} + +test large_addition() { + let a = builtin.integer_to_bytearray(True, 0, 123456789) + let b = builtin.integer_to_bytearray(True, 0, 987654321) + let result = builtin.integer_to_bytearray(True, 0, 1111111110) + add(a, b) == result +} + +test xlarge_addition() { + let a = + builtin.integer_to_bytearray(True, 0, 123456789123456789123456789123456789) + let b = + builtin.integer_to_bytearray(True, 0, 987654321987654321987654321987654321) + let result = + builtin.integer_to_bytearray(True, 0, 1111111111111111111111111111111111110) + add(a, b) == result +} + +pub fn subtract(a: ByteArray, b: ByteArray) -> ByteArray { + if builtin.count_set_bits(b) == 0 { + a + } else { + let borrow: ByteArray = + builtin.and_bytearray(True, builtin.complement_bytearray(a), b) + |> builtin.shift_bytearray(1) + let diff_without_borrow: ByteArray = builtin.xor_bytearray(True, a, b) + subtract(diff_without_borrow, borrow) + } +} + +test one_minus_one() { + let one = builtin.integer_to_bytearray(True, 0, 1) + subtract(one, one) == #"00" +} + +test small_subtraction1() { + let a = builtin.integer_to_bytearray(True, 0, 31) + let b = builtin.integer_to_bytearray(True, 0, 51) + let result = builtin.integer_to_bytearray(True, 0, 256 - 20) + subtract(a, b) == result +} + +test small_subtraction2() { + let a = builtin.integer_to_bytearray(True, 0, 31) + let b = builtin.integer_to_bytearray(True, 0, 51) + let result = builtin.integer_to_bytearray(True, 0, 20) + subtract(b, a) == result +} + +pub fn multiply(a: ByteArray, b: ByteArray) -> ByteArray { + do_multiply(a, b, #"00") +} + +pub fn do_multiply(a: ByteArray, b: ByteArray, acc: ByteArray) -> ByteArray { + trace acc + if builtin.count_set_bits(b) == 0 { + acc + } else { + // Check the least significant bit of b. + // If it is set, then add the current value of a to the accumulator. + let bit_set = builtin.and_bytearray(True, b, #"01") + let new_acc = + if builtin.count_set_bits(bit_set) > 0 { + add(a, acc) + } else { + acc + } + // trace new_acc + // Shift a left by 1 (multiply a by 2). + let new_a = builtin.shift_bytearray(a, 1) + // trace new_a + // Shift b right by 1 (divide b by 2). + let new_b = builtin.shift_bytearray(b, -1) + + // trace new_b + do_multiply(new_a, new_b, new_acc) + } +} + +test small_multiply() { + let a = builtin.integer_to_bytearray(True, 0, 2) + let b = builtin.integer_to_bytearray(True, 0, 3) + let result = builtin.integer_to_bytearray(True, 0, 6) + multiply(a, b) == result +} + +test medium_multiply() { + let a = builtin.integer_to_bytearray(True, 0, 21) + let b = builtin.integer_to_bytearray(True, 0, 31) + let result = builtin.integer_to_bytearray(True, 0, 651) + multiply(a, b) == result +} From 0c9f4435b4633834c6f54cf8f0e9362bae92e628 Mon Sep 17 00:00:00 2001 From: logicalmechanism Date: Tue, 18 Feb 2025 20:19:33 -0800 Subject: [PATCH 2/7] good start --- lib/aiken/math/bitwise.ak | 128 +--------------------- lib/aiken/math/bitwise.tests.ak | 30 +++++ lib/cardano/transaction/script_purpose.ak | 10 +- 3 files changed, 40 insertions(+), 128 deletions(-) create mode 100644 lib/aiken/math/bitwise.tests.ak diff --git a/lib/aiken/math/bitwise.ak b/lib/aiken/math/bitwise.ak index 8f0d19c..7bd84bf 100644 --- a/lib/aiken/math/bitwise.ak +++ b/lib/aiken/math/bitwise.ak @@ -1,5 +1,10 @@ use aiken/builtin +/// Adds two ByteArrays together using bitwise operations. +/// +/// ```aiken +/// bitwise.add(#"00acab", #"00cafe") == #"0177A9" +/// `` pub fn add(a: ByteArray, b: ByteArray) -> ByteArray { if builtin.count_set_bits(b) == 0 { a @@ -10,126 +15,3 @@ pub fn add(a: ByteArray, b: ByteArray) -> ByteArray { add(sum_without_carry, carry) } } - -test one_plus_one() { - let one = builtin.integer_to_bytearray(True, 0, 1) - let two = builtin.integer_to_bytearray(True, 0, 2) - add(one, one) == two -} - -test add_to_zero() { - // add(#"00", #"acab") == #"acab" - add(#"acab", #"00") == #"acab" -} - -test mix_addition() { - let a = builtin.integer_to_bytearray(True, 0, 12) - let b = builtin.integer_to_bytearray(True, 0, 34151) - add(a, b) == add(b, a) -} - -test small_addition() { - let a = builtin.integer_to_bytearray(True, 0, 31) - let b = builtin.integer_to_bytearray(True, 0, 51) - let result = builtin.integer_to_bytearray(True, 0, 82) - add(a, b) == result -} - -test medium_addition() { - let a = builtin.integer_to_bytearray(True, 0, 1313) - let b = builtin.integer_to_bytearray(True, 0, 1514) - let result = builtin.integer_to_bytearray(True, 0, 2827) - add(a, b) == result -} - -test large_addition() { - let a = builtin.integer_to_bytearray(True, 0, 123456789) - let b = builtin.integer_to_bytearray(True, 0, 987654321) - let result = builtin.integer_to_bytearray(True, 0, 1111111110) - add(a, b) == result -} - -test xlarge_addition() { - let a = - builtin.integer_to_bytearray(True, 0, 123456789123456789123456789123456789) - let b = - builtin.integer_to_bytearray(True, 0, 987654321987654321987654321987654321) - let result = - builtin.integer_to_bytearray(True, 0, 1111111111111111111111111111111111110) - add(a, b) == result -} - -pub fn subtract(a: ByteArray, b: ByteArray) -> ByteArray { - if builtin.count_set_bits(b) == 0 { - a - } else { - let borrow: ByteArray = - builtin.and_bytearray(True, builtin.complement_bytearray(a), b) - |> builtin.shift_bytearray(1) - let diff_without_borrow: ByteArray = builtin.xor_bytearray(True, a, b) - subtract(diff_without_borrow, borrow) - } -} - -test one_minus_one() { - let one = builtin.integer_to_bytearray(True, 0, 1) - subtract(one, one) == #"00" -} - -test small_subtraction1() { - let a = builtin.integer_to_bytearray(True, 0, 31) - let b = builtin.integer_to_bytearray(True, 0, 51) - let result = builtin.integer_to_bytearray(True, 0, 256 - 20) - subtract(a, b) == result -} - -test small_subtraction2() { - let a = builtin.integer_to_bytearray(True, 0, 31) - let b = builtin.integer_to_bytearray(True, 0, 51) - let result = builtin.integer_to_bytearray(True, 0, 20) - subtract(b, a) == result -} - -pub fn multiply(a: ByteArray, b: ByteArray) -> ByteArray { - do_multiply(a, b, #"00") -} - -pub fn do_multiply(a: ByteArray, b: ByteArray, acc: ByteArray) -> ByteArray { - trace acc - if builtin.count_set_bits(b) == 0 { - acc - } else { - // Check the least significant bit of b. - // If it is set, then add the current value of a to the accumulator. - let bit_set = builtin.and_bytearray(True, b, #"01") - let new_acc = - if builtin.count_set_bits(bit_set) > 0 { - add(a, acc) - } else { - acc - } - // trace new_acc - // Shift a left by 1 (multiply a by 2). - let new_a = builtin.shift_bytearray(a, 1) - // trace new_a - // Shift b right by 1 (divide b by 2). - let new_b = builtin.shift_bytearray(b, -1) - - // trace new_b - do_multiply(new_a, new_b, new_acc) - } -} - -test small_multiply() { - let a = builtin.integer_to_bytearray(True, 0, 2) - let b = builtin.integer_to_bytearray(True, 0, 3) - let result = builtin.integer_to_bytearray(True, 0, 6) - multiply(a, b) == result -} - -test medium_multiply() { - let a = builtin.integer_to_bytearray(True, 0, 21) - let b = builtin.integer_to_bytearray(True, 0, 31) - let result = builtin.integer_to_bytearray(True, 0, 651) - multiply(a, b) == result -} diff --git a/lib/aiken/math/bitwise.tests.ak b/lib/aiken/math/bitwise.tests.ak new file mode 100644 index 0000000..209b6fc --- /dev/null +++ b/lib/aiken/math/bitwise.tests.ak @@ -0,0 +1,30 @@ +use aiken/builtin +use aiken/math/bitwise.{add} + +test one_plus_one() { + let one = builtin.integer_to_bytearray(True, 0, 1) + let two = builtin.integer_to_bytearray(True, 0, 2) + add(one, one) == two +} + +test emptiness_is_empty() { + add(#"", #"") == #"" +} + +test not_equal_length_does_not_work() fail { + add(#"00", #"acab") == #"acab" +} + +test communitive() { + add(#"acab", #"0000") == add(#"0000", #"acab") +} + +test associativity() { + ( add(#"0101", #"0202") |> add(#"0303") ) == ( + add(#"0202", #"0303") |> add(#"0101") + ) +} + +test identity() { + add(#"00", #"01") == #"01" +} diff --git a/lib/cardano/transaction/script_purpose.ak b/lib/cardano/transaction/script_purpose.ak index 4fef2cb..13c7783 100644 --- a/lib/cardano/transaction/script_purpose.ak +++ b/lib/cardano/transaction/script_purpose.ak @@ -32,9 +32,9 @@ pub fn compare(left: ScriptPurpose, right: ScriptPurpose) -> Ordering { _ -> Less } - Publish(left, _) -> + Publish { at: left, .. } -> when right is { - Publish(right, _) -> int.compare(left, right) + Publish { at: right, .. } -> int.compare(left, right) Spend(_) | Mint(_) | Withdraw(_) -> Greater _ -> Less } @@ -42,13 +42,13 @@ pub fn compare(left: ScriptPurpose, right: ScriptPurpose) -> Ordering { Vote(left) -> when right is { Vote(right) -> voter.compare(left, right) - Propose(..) -> Less + Propose { .. } -> Less _ -> Greater } - Propose(left, _) -> + Propose { at: left, .. } -> when right is { - Propose(right, _) -> int.compare(left, right) + Propose { at: right, .. } -> int.compare(left, right) _ -> Greater } } From e6a82ae2b4c147c418eb86bf52b29d8add4a611e Mon Sep 17 00:00:00 2001 From: logicalmechanism Date: Tue, 18 Feb 2025 20:59:19 -0800 Subject: [PATCH 3/7] adding a pad addition function to prepad bytearrays if required --- lib/aiken/math/bitwise.ak | 36 ++++++++++++++++++++++++-- lib/aiken/math/bitwise.tests.ak | 45 ++++++++++++++++++++++++++++----- 2 files changed, 73 insertions(+), 8 deletions(-) diff --git a/lib/aiken/math/bitwise.ak b/lib/aiken/math/bitwise.ak index 7bd84bf..926e998 100644 --- a/lib/aiken/math/bitwise.ak +++ b/lib/aiken/math/bitwise.ak @@ -1,10 +1,42 @@ use aiken/builtin -/// Adds two ByteArrays together using bitwise operations. +/// Addition will at most add a single byte to a bytearray. This will prepad the +/// bytearrays to the correct length for addition and it will correct for mismatched +/// lengths. +/// +/// ```aiken +/// bitwise.pad_addition(#"acab", #"cafe") == (#"00acab", #"00cafe") +/// ``` +pub fn pad_addition(a: ByteArray, b: ByteArray) -> (ByteArray, ByteArray) { + let length_a: Int = builtin.length_of_bytearray(a) + let length_b: Int = builtin.length_of_bytearray(b) + if length_a >= length_b { + let diff: Int = length_a - length_b + ( + builtin.cons_bytearray(0, a), + builtin.append_bytearray(builtin.replicate_byte(diff + 1, 0), b), + ) + } else { + let diff: Int = length_b - length_a + ( + builtin.append_bytearray(builtin.replicate_byte(diff + 1, 0), a), + builtin.cons_bytearray(0, b), + ) + } +} + +/// Add two ByteArrays together using bitwise operations. The function assumes +/// fixed-width (modular) arithmetic. +/// +/// -- pad inputs for arbitrary precision /// /// ```aiken /// bitwise.add(#"00acab", #"00cafe") == #"0177A9" -/// `` +/// ``` +/// +/// ```aiken +/// bitwise.add(#"acab", #"cafe") == #"77A9" +/// ``` pub fn add(a: ByteArray, b: ByteArray) -> ByteArray { if builtin.count_set_bits(b) == 0 { a diff --git a/lib/aiken/math/bitwise.tests.ak b/lib/aiken/math/bitwise.tests.ak index 209b6fc..ce807a7 100644 --- a/lib/aiken/math/bitwise.tests.ak +++ b/lib/aiken/math/bitwise.tests.ak @@ -1,10 +1,43 @@ -use aiken/builtin -use aiken/math/bitwise.{add} +use aiken/math/bitwise.{add, pad_addition} -test one_plus_one() { - let one = builtin.integer_to_bytearray(True, 0, 1) - let two = builtin.integer_to_bytearray(True, 0, 2) - add(one, one) == two +test equal_pad_addition() { + let a: ByteArray = #"acab" + let b: ByteArray = #"cafe" + let (new_a, new_b) = pad_addition(a, b) + and { + new_a == #"00acab", + new_b == #"00cafe", + } +} + +test unequal_pad_addition1() { + let a: ByteArray = #"acabbeefface" + let b: ByteArray = #"cafe" + let (new_a, new_b) = pad_addition(a, b) + and { + new_a == #"00acabbeefface", + new_b == #"0000000000cafe", + } +} + +test unequal_pad_addition2() { + let b: ByteArray = #"acabbeefface" + let a: ByteArray = #"cafe" + let (new_a, new_b) = pad_addition(a, b) + and { + new_a == #"0000000000cafe", + new_b == #"00acabbeefface", + } +} + +test pad_for_addition() { + let b: ByteArray = #"acab" + let a: ByteArray = #"cafe" + let (new_a, new_b) = pad_addition(a, b) + and { + add(a, b) == #"77a9", + add(new_a, new_b) == #"0177a9", + } } test emptiness_is_empty() { From 56dad6dfc55e2375f0ef0e1cbf56debf5ad9c8c2 Mon Sep 17 00:00:00 2001 From: logicalmechanism Date: Tue, 18 Feb 2025 21:07:28 -0800 Subject: [PATCH 4/7] adding comments and adjusting naming --- lib/aiken/math/bitwise.ak | 11 +++++++---- lib/aiken/math/bitwise.tests.ak | 18 +++++++++--------- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/lib/aiken/math/bitwise.ak b/lib/aiken/math/bitwise.ak index 926e998..f5c145b 100644 --- a/lib/aiken/math/bitwise.ak +++ b/lib/aiken/math/bitwise.ak @@ -5,9 +5,9 @@ use aiken/builtin /// lengths. /// /// ```aiken -/// bitwise.pad_addition(#"acab", #"cafe") == (#"00acab", #"00cafe") +/// bitwise.pad_for_addition(#"acab", #"cafe") == (#"00acab", #"00cafe") /// ``` -pub fn pad_addition(a: ByteArray, b: ByteArray) -> (ByteArray, ByteArray) { +pub fn pad_for_addition(a: ByteArray, b: ByteArray) -> (ByteArray, ByteArray) { let length_a: Int = builtin.length_of_bytearray(a) let length_b: Int = builtin.length_of_bytearray(b) if length_a >= length_b { @@ -25,8 +25,9 @@ pub fn pad_addition(a: ByteArray, b: ByteArray) -> (ByteArray, ByteArray) { } } -/// Add two ByteArrays together using bitwise operations. The function assumes -/// fixed-width (modular) arithmetic. +/// Add two ByteArrays together, a + b, using bitwise operations. The function +/// assumes fixed-width (modular) arithmetic. If arbitrary precision is required +/// use `pad_for_addition`. /// /// -- pad inputs for arbitrary precision /// @@ -34,6 +35,8 @@ pub fn pad_addition(a: ByteArray, b: ByteArray) -> (ByteArray, ByteArray) { /// bitwise.add(#"00acab", #"00cafe") == #"0177A9" /// ``` /// +/// -- otherwise +/// /// ```aiken /// bitwise.add(#"acab", #"cafe") == #"77A9" /// ``` diff --git a/lib/aiken/math/bitwise.tests.ak b/lib/aiken/math/bitwise.tests.ak index ce807a7..675d3fe 100644 --- a/lib/aiken/math/bitwise.tests.ak +++ b/lib/aiken/math/bitwise.tests.ak @@ -1,39 +1,39 @@ -use aiken/math/bitwise.{add, pad_addition} +use aiken/math/bitwise.{add, pad_for_addition} -test equal_pad_addition() { +test equal_pad_for_addition() { let a: ByteArray = #"acab" let b: ByteArray = #"cafe" - let (new_a, new_b) = pad_addition(a, b) + let (new_a, new_b) = pad_for_addition(a, b) and { new_a == #"00acab", new_b == #"00cafe", } } -test unequal_pad_addition1() { +test unequal_pad_for_addition1() { let a: ByteArray = #"acabbeefface" let b: ByteArray = #"cafe" - let (new_a, new_b) = pad_addition(a, b) + let (new_a, new_b) = pad_for_addition(a, b) and { new_a == #"00acabbeefface", new_b == #"0000000000cafe", } } -test unequal_pad_addition2() { +test unequal_pad_for_addition2() { let b: ByteArray = #"acabbeefface" let a: ByteArray = #"cafe" - let (new_a, new_b) = pad_addition(a, b) + let (new_a, new_b) = pad_for_addition(a, b) and { new_a == #"0000000000cafe", new_b == #"00acabbeefface", } } -test pad_for_addition() { +test pad_for_addition_works() { let b: ByteArray = #"acab" let a: ByteArray = #"cafe" - let (new_a, new_b) = pad_addition(a, b) + let (new_a, new_b) = pad_for_addition(a, b) and { add(a, b) == #"77a9", add(new_a, new_b) == #"0177a9", From 938a86108aa6b9bf7bd110843fc163280257ce3d Mon Sep 17 00:00:00 2001 From: logicalmechanism Date: Tue, 18 Feb 2025 21:18:08 -0800 Subject: [PATCH 5/7] subtraction using the borrow method instead of 2s --- lib/aiken/math/bitwise.ak | 19 +++++++++++++++++- lib/aiken/math/bitwise.tests.ak | 34 ++++++++++++++++++++++++++------- 2 files changed, 45 insertions(+), 8 deletions(-) diff --git a/lib/aiken/math/bitwise.ak b/lib/aiken/math/bitwise.ak index f5c145b..8861d39 100644 --- a/lib/aiken/math/bitwise.ak +++ b/lib/aiken/math/bitwise.ak @@ -25,7 +25,7 @@ pub fn pad_for_addition(a: ByteArray, b: ByteArray) -> (ByteArray, ByteArray) { } } -/// Add two ByteArrays together, a + b, using bitwise operations. The function +/// Add two ByteArrays, a + b, using bitwise operations. The function /// assumes fixed-width (modular) arithmetic. If arbitrary precision is required /// use `pad_for_addition`. /// @@ -50,3 +50,20 @@ pub fn add(a: ByteArray, b: ByteArray) -> ByteArray { add(sum_without_carry, carry) } } + +/// Subtracts two ByteArrays, a - b, using bitwise operations. +/// +/// ```aiken +/// bitwise.subtract(#"77a9", #"cafe") == #"acab" +/// ``` +pub fn subtract(a: ByteArray, b: ByteArray) -> ByteArray { + if builtin.count_set_bits(b) == 0 { + a + } else { + let borrow: ByteArray = + builtin.and_bytearray(True, builtin.complement_bytearray(a), b) + |> builtin.shift_bytearray(1) + let diff_without_borrow: ByteArray = builtin.xor_bytearray(True, a, b) + subtract(diff_without_borrow, borrow) + } +} diff --git a/lib/aiken/math/bitwise.tests.ak b/lib/aiken/math/bitwise.tests.ak index 675d3fe..c742207 100644 --- a/lib/aiken/math/bitwise.tests.ak +++ b/lib/aiken/math/bitwise.tests.ak @@ -1,4 +1,4 @@ -use aiken/math/bitwise.{add, pad_for_addition} +use aiken/math/bitwise.{add, pad_for_addition, subtract} test equal_pad_for_addition() { let a: ByteArray = #"acab" @@ -30,7 +30,7 @@ test unequal_pad_for_addition2() { } } -test pad_for_addition_works() { +test pad_for_addition_does_works() { let b: ByteArray = #"acab" let a: ByteArray = #"cafe" let (new_a, new_b) = pad_for_addition(a, b) @@ -44,20 +44,40 @@ test emptiness_is_empty() { add(#"", #"") == #"" } -test not_equal_length_does_not_work() fail { +test adding_not_equal_length_does_not_work() fail { add(#"00", #"acab") == #"acab" } -test communitive() { - add(#"acab", #"0000") == add(#"0000", #"acab") +test add_communitive() { + add(#"acab", #"cafe") == add(#"cafe", #"acab") } -test associativity() { +test add_associativity() { ( add(#"0101", #"0202") |> add(#"0303") ) == ( add(#"0202", #"0303") |> add(#"0101") ) } -test identity() { +test add_identity() { add(#"00", #"01") == #"01" } + +test subtracting_does_work1() { + subtract(#"0177a9", #"00cafe") == #"00acab" +} + +test subtracting_does_work2() { + subtract(#"77a9", #"cafe") == #"acab" +} + +test subtracting_not_equal_length_does_not_work() fail { + subtract(#"0177a9", #"cafe") == #"acab" +} + +test subtract_is_not_communitive() fail { + subtract(#"acab", #"cafe") == add(#"cafe", #"acab") +} + +test subtract_identity() { + subtract(#"10", #"00") == #"10" +} From 2536f4960bed08b39c4907c704dfc409c5b34f8d Mon Sep 17 00:00:00 2001 From: logicalmechanism Date: Tue, 18 Feb 2025 21:36:35 -0800 Subject: [PATCH 6/7] multiplication is at most l1 + l2 bytes --- lib/aiken/math/bitwise.ak | 25 +++++++++++++++++++++++++ lib/aiken/math/bitwise.tests.ak | 32 +++++++++++++++++++++++++++++++- 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/lib/aiken/math/bitwise.ak b/lib/aiken/math/bitwise.ak index 8861d39..1372586 100644 --- a/lib/aiken/math/bitwise.ak +++ b/lib/aiken/math/bitwise.ak @@ -67,3 +67,28 @@ pub fn subtract(a: ByteArray, b: ByteArray) -> ByteArray { subtract(diff_without_borrow, borrow) } } + +/// Multiplication will at most add N bytes to a bytearray for a bytearray of size N. +/// This will prepad the bytearrays to the correct length for multiplication and it +/// will correct for mismatched lengths. +/// +/// ```aiken +/// bitwise.pad_for_multiply(#"acab", #"cafe") == (#"0000acab", #"0000cafe") +/// ``` +pub fn pad_for_multiply(a: ByteArray, b: ByteArray) -> (ByteArray, ByteArray) { + let length_a: Int = builtin.length_of_bytearray(a) + let length_b: Int = builtin.length_of_bytearray(b) + if length_a >= length_b { + let diff: Int = length_a - length_b + ( + builtin.append_bytearray(builtin.replicate_byte(length_a, 0), a), + builtin.append_bytearray(builtin.replicate_byte(diff + length_a, 0), b), + ) + } else { + let diff: Int = length_b - length_a + ( + builtin.append_bytearray(builtin.replicate_byte(diff + length_b, 0), a), + builtin.append_bytearray(builtin.replicate_byte(length_b, 0), b), + ) + } +} diff --git a/lib/aiken/math/bitwise.tests.ak b/lib/aiken/math/bitwise.tests.ak index c742207..db5bf7d 100644 --- a/lib/aiken/math/bitwise.tests.ak +++ b/lib/aiken/math/bitwise.tests.ak @@ -1,4 +1,4 @@ -use aiken/math/bitwise.{add, pad_for_addition, subtract} +use aiken/math/bitwise.{add, pad_for_addition, pad_for_multiply, subtract} test equal_pad_for_addition() { let a: ByteArray = #"acab" @@ -81,3 +81,33 @@ test subtract_is_not_communitive() fail { test subtract_identity() { subtract(#"10", #"00") == #"10" } + +test equal_pad_for_multiply() { + let a: ByteArray = #"acab" + let b: ByteArray = #"cafe" + let (new_a, new_b) = pad_for_multiply(a, b) + and { + new_a == #"0000acab", + new_b == #"0000cafe", + } +} + +test unequal_pad_for_multiply1() { + let a: ByteArray = #"acabbeefface" + let b: ByteArray = #"cafe" + let (new_a, new_b) = pad_for_multiply(a, b) + and { + new_a == #"000000000000acabbeefface", + new_b == #"00000000000000000000cafe", + } +} + +test unequal_pad_for_multiply2() { + let b: ByteArray = #"acabbeefface" + let a: ByteArray = #"cafe" + let (new_a, new_b) = pad_for_multiply(a, b) + and { + new_a == #"00000000000000000000cafe", + new_b == #"000000000000acabbeefface", + } +} From dab43a8b4b616eab322e4fd432e8a5a94865bb0e Mon Sep 17 00:00:00 2001 From: logicalmechanism Date: Tue, 18 Feb 2025 21:57:05 -0800 Subject: [PATCH 7/7] probably some faster way to do muliplication but this does do arbitrary values --- lib/aiken/math/bitwise.ak | 33 +++++++++++++++++++++++++++++++++ lib/aiken/math/bitwise.tests.ak | 12 +++++++++++- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/lib/aiken/math/bitwise.ak b/lib/aiken/math/bitwise.ak index 1372586..c2a1ae5 100644 --- a/lib/aiken/math/bitwise.ak +++ b/lib/aiken/math/bitwise.ak @@ -92,3 +92,36 @@ pub fn pad_for_multiply(a: ByteArray, b: ByteArray) -> (ByteArray, ByteArray) { ) } } + +pub fn multiply(a: ByteArray, b: ByteArray) -> ByteArray { + let length_a: Int = builtin.length_of_bytearray(a) + let zero: ByteArray = builtin.replicate_byte(length_a, 0) + let one: ByteArray = + builtin.append_bytearray(builtin.replicate_byte(length_a, 0), #"01") + do_multiply(a, b, zero, one) +} + +fn do_multiply( + a: ByteArray, + b: ByteArray, + acc: ByteArray, + one: ByteArray, +) -> ByteArray { + if builtin.count_set_bits(b) == 0 { + acc + } else { + let bit_set = builtin.and_bytearray(True, b, #"00000001") + let new_acc = + if builtin.count_set_bits(bit_set) > 0 { + add(acc, a) + } else { + acc + } + + let new_a = builtin.shift_bytearray(a, 1) + // left shift: multiply a by 2 + let new_b = builtin.shift_bytearray(b, -1) + // right shift: divide b by 2 + do_multiply(new_a, new_b, new_acc, one) + } +} diff --git a/lib/aiken/math/bitwise.tests.ak b/lib/aiken/math/bitwise.tests.ak index db5bf7d..2a79f85 100644 --- a/lib/aiken/math/bitwise.tests.ak +++ b/lib/aiken/math/bitwise.tests.ak @@ -1,4 +1,6 @@ -use aiken/math/bitwise.{add, pad_for_addition, pad_for_multiply, subtract} +use aiken/math/bitwise.{ + add, multiply, pad_for_addition, pad_for_multiply, subtract, +} test equal_pad_for_addition() { let a: ByteArray = #"acab" @@ -111,3 +113,11 @@ test unequal_pad_for_multiply2() { new_b == #"000000000000acabbeefface", } } + +test simple_multiply1() { + multiply(#"0000ffff", #"0000ffff") == #"fffe0001" +} + +test multiply_communitive() { + multiply(#"0000acab", #"0000cafe") == multiply(#"0000cafe", #"0000acab") +}