diff --git a/README.md b/README.md index 839d25c72..695360142 100644 --- a/README.md +++ b/README.md @@ -254,6 +254,7 @@ a_xor_b <= a ^ b; // xor and_a <= a.and(); // unary and or_a <= a.or(); // unary or xor_a <= a.xor(); // unary xor +a_pow_b <= a.pow(b);// power a_plus_b <= a + b; // addition a_sub_b <= a - b; // subtraction a_times_b <= a * b; // multiplication diff --git a/doc/tutorials/chapter_2/00_basic_logic.md b/doc/tutorials/chapter_2/00_basic_logic.md index 704bfab0b..ec04b222d 100644 --- a/doc/tutorials/chapter_2/00_basic_logic.md +++ b/doc/tutorials/chapter_2/00_basic_logic.md @@ -149,6 +149,7 @@ a_xor_b <= a ^ b; // xor and_a <= a.and(); // unary and or_a <= a.or(); // unary or xor_a <= a.xor(); // unary xor +a_pow_b <= a.pow(b);// power a_plus_b <= a + b; // addition a_sub_b <= a - b; // subtraction a_times_b <= a * b; // multiplication diff --git a/lib/src/logic.dart b/lib/src/logic.dart index 0bdc182dc..08d9ced0e 100644 --- a/lib/src/logic.dart +++ b/lib/src/logic.dart @@ -530,6 +530,9 @@ class Logic { /// Logical bitwise XOR. Logic operator ^(Logic other) => Xor2Gate(this, other).out; + /// Power operation + Logic pow(dynamic other) => Power(this, other).out; + /// Addition. /// /// WARNING: Signed math is not fully tested. diff --git a/lib/src/modules/gates.dart b/lib/src/modules/gates.dart index b385f7f41..25c61a1ba 100644 --- a/lib/src/modules/gates.dart +++ b/lib/src/modules/gates.dart @@ -424,6 +424,15 @@ class Xor2Gate extends _TwoInputBitwiseGate { : super((a, b) => a ^ b, '^', in0, in1, name: name); } +/// A two-input power module. +class Power extends _TwoInputBitwiseGate { + /// Calculates [in0] raise to power of [in1]. + /// + /// [in1] can be either a [Logic] or [int]. + Power(Logic in0, dynamic in1, {String name = 'power'}) + : super((a, b) => a.pow(b), '**', in0, in1, name: name); +} + /// A two-input addition module. class Add extends _TwoInputBitwiseGate { /// Calculates the sum of [in0] and [in1]. diff --git a/lib/src/values/logic_value.dart b/lib/src/values/logic_value.dart index 4fab13bb2..c05f61dd1 100644 --- a/lib/src/values/logic_value.dart +++ b/lib/src/values/logic_value.dart @@ -733,6 +733,13 @@ abstract class LogicValue { /// invalid, [neq] will return `x`. LogicValue neq(dynamic other) => _doCompare(other, (a, b) => a != b); + /// Power operation. + /// + /// This will return a [LogicValue] of some input 'base' to the power of other + /// input 'exponent'. If one of the two input values is invalid, [pow] will + /// return ‘x’ of input size width. + LogicValue pow(dynamic other) => _doMath(other, _binPower); + /// Less-than operation. /// /// WARNING: Signed math is not fully tested. @@ -761,6 +768,35 @@ abstract class LogicValue { // ignore: avoid_dynamic_calls _doCompare(other, (a, b) => (a >= b) as bool); + /// Binary Power + /// + /// Both inputs [a] and [b] are either of type [int] or [BigInt]. + /// Returns [a] raise to the power [b] of input type. + dynamic _binPower(dynamic a, dynamic b) { + dynamic x = a; + dynamic y = b; + dynamic zero = 0; + dynamic res = 1; + if (x is BigInt) { + res = BigInt.from(1); + zero = BigInt.zero; + } + + // ignore: avoid_dynamic_calls + while ((y > zero) as bool) { + // ignore: avoid_dynamic_calls + if (y.isOdd as bool) { + // ignore: avoid_dynamic_calls + res = res * x; + } + // ignore: avoid_dynamic_calls + x = x * x; + // ignore: avoid_dynamic_calls + y = y >> 1; + } + return res; + } + /// Executes comparison operations between two [LogicValue]s /// /// Handles width and bounds checks as well as proper conversion between diff --git a/test/logic_value_test.dart b/test/logic_value_test.dart index 5052809c7..87813eb2b 100644 --- a/test/logic_value_test.dart +++ b/test/logic_value_test.dart @@ -551,6 +551,43 @@ void main() { }); }); group('arithmetic operations', () { + test('power', () { + expect( + // test ofInt + LogicValue.ofInt(2, 32).pow(LogicValue.ofInt(0, 32)), + equals(LogicValue.ofInt(1, 32))); + expect( + // test ofInt + LogicValue.ofInt(2, 32).pow(LogicValue.ofInt(12, 32)), + equals(LogicValue.ofInt(4096, 32))); + expect( + // test ofInt + LogicValue.ofInt(3, 32).pow(LogicValue.ofInt(5, 32)), + equals(LogicValue.ofInt(243, 32))); + expect( + // test ofBigInt + LogicValue.ofBigInt(BigInt.from(31), 128) + .pow(LogicValue.ofBigInt(BigInt.from(21), 128)), + equals(LogicValue.ofBigInt( + BigInt.parse('20825506393391550743120420649631'), 128))); + expect( + // test ofBigInt + LogicValue.ofBigInt(BigInt.parse('111234234231234523412665554'), 256) + .pow(LogicValue.ofBigInt(BigInt.from(2), 256)), + equals(LogicValue.ofBigInt( + BigInt.parse( + '12373054865009146225795242412633846245734343458126916'), + 256))); + expect( + //test string + LogicValue.ofString('000010').pow(LogicValue.ofString('000100')), + equals(LogicValue.ofString('010000'))); + expect( + //test invalid input + LogicValue.ofString('0001').pow(LogicValue.ofString('000x')), + equals(LogicValue.filled(4, LogicValue.x))); + }); + test('addsub', () { expect( // + normal diff --git a/test/math_test.dart b/test/math_test.dart index 20ff96b3f..71b65e969 100644 --- a/test/math_test.dart +++ b/test/math_test.dart @@ -23,6 +23,7 @@ class MathTestModule extends Module { a = addInput('a', a, width: a.width); b = addInput('b', b, width: b.width); + final aPowerB = addOutput('a_power_b', width: a.width); final aPlusB = addOutput('a_plus_b', width: a.width); final aMinusB = addOutput('a_minus_b', width: a.width); final aTimesB = addOutput('a_times_b', width: a.width); @@ -30,6 +31,7 @@ class MathTestModule extends Module { final aModuloB = addOutput('a_modulo_b', width: a.width); final aModuloConst = addOutput('a_modulo_const', width: a.width); + final aPowerConst = addOutput('a_power_const', width: a.width); final aPlusConst = addOutput('a_plus_const', width: a.width); final aMinusConst = addOutput('a_minus_const', width: a.width); final aTimesConst = addOutput('a_times_const', width: a.width); @@ -43,6 +45,7 @@ class MathTestModule extends Module { final aSrlConst = addOutput('a_srl_const', width: a.width); final aSraConst = addOutput('a_sra_const', width: a.width); + aPowerB <= a.pow(b); aPlusB <= a + b; aMinusB <= a - b; aTimesB <= a * b; @@ -50,6 +53,7 @@ class MathTestModule extends Module { aModuloB <= a % b; aModuloConst <= a % c; + aPowerConst <= a.pow(c); aPlusConst <= a + c; aMinusConst <= a - c; aTimesConst <= a * c; @@ -79,6 +83,19 @@ void main() { expect(simResult, equals(true)); } + test('power', () async { + await runMathVectors([ + Vector({'a': 1, 'b': 100}, {'a_power_b': 1}), + Vector({'a': 1043, 'b': 0}, {'a_power_b': 1}), + Vector({'a': 3, 'b': 3}, {'a_power_b': 27}), + Vector({'a': 2, 'b': 1}, {'a_power_b': 2}), + Vector({'a': 2, 'b': 7}, {'a_power_b': 128}), + Vector({'a': 2}, {'a_power_const': 32}), + Vector({'a': 5, 'b': 3}, {'a_power_b': 125}), + Vector({'a': 7, 'b': 2}, {'a_power_b': 49}), + ]); + }); + test('addition', () async { await runMathVectors([ Vector({'a': 0, 'b': 0}, {'a_plus_b': 0}),