diff --git a/src/Simulation/QSharpFoundation/Convert/Convert.cs b/src/Simulation/QSharpFoundation/Convert/Convert.cs index ffcc86d8..15084b79 100644 --- a/src/Simulation/QSharpFoundation/Convert/Convert.cs +++ b/src/Simulation/QSharpFoundation/Convert/Convert.cs @@ -26,94 +26,6 @@ namespace Microsoft.Quantum.Convert } - public partial class MaybeBigIntAsInt - { - public class Native : MaybeBigIntAsInt - { - static (long, bool) MaybeBigIntAsIntFunc(BigInteger x) - { - if (x > long.MaxValue || x < long.MinValue) - { - return (0, false); - } - else - { - return ((long)x, true); - } - } - - public Native(IOperationFactory m) : base(m) { } - public override Func __Body__ => MaybeBigIntAsIntFunc; - } - } - - - public partial class BigIntAsBoolArray - { - public class Native : BigIntAsBoolArray - { - static IQArray BigIntAsBoolArrayFunc(BigInteger x) - { - var bytes = x.ToByteArray(); - long n = bytes.LongLength * 8; - var array = new bool[n]; - for (int i = 0; i < bytes.Length; i++) - { - var b = bytes[i]; - array[(8 * i) + 0] = (b & 0x01) != 0; - array[(8 * i) + 1] = (b & 0x02) != 0; - array[(8 * i) + 2] = (b & 0x04) != 0; - array[(8 * i) + 3] = (b & 0x08) != 0; - array[(8 * i) + 4] = (b & 0x10) != 0; - array[(8 * i) + 5] = (b & 0x20) != 0; - array[(8 * i) + 6] = (b & 0x40) != 0; - array[(8 * i) + 7] = (b & 0x80) != 0; - } - return new QArray(array); - } - - public Native(IOperationFactory m) : base(m) { } - public override Func> __Body__ => BigIntAsBoolArrayFunc; - } - } - - public partial class BoolArrayAsBigInt - { - public class Native : BoolArrayAsBigInt - { - static BigInteger BoolArrayAsBigIntFunc(IQArray x) - { - long fullBytes = x.Length / 8; - long leftOver = x.Length % 8; - long totalBytes = fullBytes + (leftOver > 0 ? 1 : 0); - var array = new byte[totalBytes]; - for (int i = 0; i < fullBytes; i++) - { - array[i] += (byte)(x[(8 * i) + 0] ? 0x01 : 0); - array[i] += (byte)(x[(8 * i) + 1] ? 0x02 : 0); - array[i] += (byte)(x[(8 * i) + 2] ? 0x04 : 0); - array[i] += (byte)(x[(8 * i) + 3] ? 0x08 : 0); - array[i] += (byte)(x[(8 * i) + 4] ? 0x10 : 0); - array[i] += (byte)(x[(8 * i) + 5] ? 0x20 : 0); - array[i] += (byte)(x[(8 * i) + 6] ? 0x40 : 0); - array[i] += (byte)(x[(8 * i) + 7] ? 0x80 : 0); - } - long last = fullBytes * 8; - if (leftOver > 0) array[fullBytes] += (byte)(x[last + 0] ? 0x01 : 0); - if (leftOver > 1) array[fullBytes] += (byte)(x[last + 1] ? 0x02 : 0); - if (leftOver > 2) array[fullBytes] += (byte)(x[last + 2] ? 0x04 : 0); - if (leftOver > 3) array[fullBytes] += (byte)(x[last + 3] ? 0x08 : 0); - if (leftOver > 4) array[fullBytes] += (byte)(x[last + 4] ? 0x10 : 0); - if (leftOver > 5) array[fullBytes] += (byte)(x[last + 5] ? 0x20 : 0); - if (leftOver > 6) array[fullBytes] += (byte)(x[last + 6] ? 0x40 : 0); - return new BigInteger(array); - } - - public Native(IOperationFactory m) : base(m) { } - public override Func, BigInteger> __Body__ => BoolArrayAsBigIntFunc; - } - } - public partial class BoolAsString { public class Native : BoolAsString diff --git a/src/Simulation/QSharpFoundation/Convert/Convert.qs b/src/Simulation/QSharpFoundation/Convert/Convert.qs index ac3ad6dd..2a9fbb89 100644 --- a/src/Simulation/QSharpFoundation/Convert/Convert.qs +++ b/src/Simulation/QSharpFoundation/Convert/Convert.qs @@ -63,20 +63,68 @@ namespace Microsoft.Quantum.Convert { /// Converts a given big integer to an equivalent integer, if possible. /// The function returns a pair of the resulting integer and a Boolean flag /// which is true, if and only if the conversion was possible. - /// # Remarks - /// See [C# BigInteger constructor](https://docs.microsoft.com/dotnet/api/system.numerics.biginteger.-ctor?view=netframework-4.7.2#System_Numerics_BigInteger__ctor_System_Int64_) for more details. function MaybeBigIntAsInt(a : BigInt) : (Int, Bool) { - body intrinsic; + if a > (1L <<< 63) - 1L or a < (-1L * (1L <<< 63)) { + return (0, false); + } + let arr = BigIntAsBoolArray(a); + let len = Length(arr); + + // BigIntAsBoolArray always returns padded results with minimum length 8, so the below + // logic can assume the last entry is the sign bit for two's complement. + mutable val = 0; + for i in 0..(len - 2) { + set val += arr[i] ? 2 ^ i | 0; + } + if arr[len - 1] { + set val -= 2 ^ (len - 1); + } + + return (val, true); } /// # Summary /// Converts a given big integer to an array of Booleans. /// The 0 element of the array is the least significant bit of the big integer. - /// # Remarks - /// See [C# BigInteger constructor](https://docs.microsoft.com/dotnet/api/system.numerics.biginteger.-ctor?view=netframework-4.7.2#System_Numerics_BigInteger__ctor_System_Int64_) for more details. function BigIntAsBoolArray(a : BigInt) : Bool[] { - body intrinsic; + // To use two's complement, little endian representation of the integer, we fisrt need to track if the input + // is a negative number. If so, flip it back to positive and start tracking a carry bit. + let isNegative = a < 0L; + mutable carry = isNegative; + mutable val = isNegative ? -a | a; + + mutable arr = []; + while val != 0L { + let newBit = val % 2L == 1L; + if isNegative { + // For negative numbers we must invert the calculated bit, so treat "true" as "0" + // and "false" as "1". This means when the carry bit is set, we want to record the + // calculated new bit and set the carry to the opposite, otherwise record the opposite + // of the calculate bit. + if carry { + set arr += [newBit]; + set carry = not newBit; + } + else { + set arr += [not newBit]; + } + } + else { + // For positive numbers just accumulate the calculated bits into the array. + set arr += [newBit]; + } + + set val /= 2L; + } + + // Pad to the next higher byte length (size 8) if the length is not a non-zero multiple of 8 or + // if the last bit does not agree with the sign bit. + let len = Length(arr); + if len == 0 or len % 8 != 0 or arr[len - 1] != isNegative { + set arr += [isNegative, size = 8 - (len % 8)]; + } + return arr; } @@ -84,8 +132,7 @@ namespace Microsoft.Quantum.Convert { /// Converts a given array of Booleans to an equivalent big integer. /// The 0 element of the array is the least significant bit of the big integer. /// # Remarks - /// See [C# BigInteger constructor](https://docs.microsoft.com/dotnet/api/system.numerics.biginteger.-ctor?view=netframework-4.7.2#System_Numerics_BigInteger__ctor_System_Int64_) for more details. - /// Note that the Boolean array is padded of the right with `false` values to a length that is a multiple of 8, + /// Note that the Boolean array is padded on the right with `false` values to a length that is a multiple of 8, /// and then treated as a little-endian notation of a positive or negative number following two's complement semantics. /// /// # Example @@ -94,7 +141,30 @@ namespace Microsoft.Quantum.Convert { /// let bi2 = BoolArrayAsBigInt([false, false, false, false, false, false, false, true]); // Not padded -> -128 /// ``` function BoolArrayAsBigInt(a : Bool[]) : BigInt { - body intrinsic; + mutable val = 0L; + + if Length(a) > 0 { + mutable arr = a; + + if Length(arr) % 8 != 0 { + // Padding is needed when the array is not evenly divisible by 8 (byte size). + // Always pad with false to treat the input number as positive. + set arr += [false, size = 8 - (Length(arr) % 8)]; + } + + let len = Length(arr); + for i in 0..(len - 2) { + set val += arr[i] ? 2L ^ i | 0L; + } + if arr[len - 1] { + // In two's complement the final bit is a sign bit, meaning it corresponds to + // -1 * 2^i, where i is the index of the most significant bit in the nearest length + // evenly divisible by 8. + set val -= 2L ^ (len - 1); + } + } + + return val; } @@ -144,6 +214,7 @@ namespace Microsoft.Quantum.Convert { /// /// # Remarks /// See [C# Int64.ToString](https://docs.microsoft.com/dotnet/api/system.int64.tostring?view=netframework-4.7.1#System_Int64_ToString_System_String_) for more details. + @Deprecated("") function IntAsStringWithFormat(a : Int, fmt : String) : String { body intrinsic; } diff --git a/src/Simulation/QSharpFoundation/Math/Math.cs b/src/Simulation/QSharpFoundation/Math/Math.cs index 0d9fef67..fbc884a0 100644 --- a/src/Simulation/QSharpFoundation/Math/Math.cs +++ b/src/Simulation/QSharpFoundation/Math/Math.cs @@ -62,22 +62,6 @@ namespace Microsoft.Quantum.Math } } - public partial class DivRemL - { - public class Native : DivRemL - { - public Native(IOperationFactory m) : base(m) { } - - private static (BigInteger, BigInteger) Impl((BigInteger, BigInteger) arg) - { - BigInteger rem; - var div = BigInteger.DivRem(arg.Item1, arg.Item2, out rem); - return (div, rem); - } - public override Func<(BigInteger, BigInteger), (BigInteger, BigInteger)> __Body__ => Impl; - } - } - public partial class ExpD { public class Native : ExpD diff --git a/src/Simulation/QSharpFoundation/Math/Math.qs b/src/Simulation/QSharpFoundation/Math/Math.qs index 28831f74..bd4dce5c 100644 --- a/src/Simulation/QSharpFoundation/Math/Math.qs +++ b/src/Simulation/QSharpFoundation/Math/Math.qs @@ -79,11 +79,8 @@ namespace Microsoft.Quantum.Math { /// # Summary /// Divides one BigInteger value by another, returns the result and the remainder as a tuple. - /// - /// # Remarks - /// See [System.Numerics.BigInteger.DivRem](https://docs.microsoft.com/dotnet/api/system.numerics.biginteger.divrem) for more details. function DivRemL(dividend : BigInt, divisor : BigInt) : (BigInt, BigInt) { - body intrinsic; + return (dividend / divisor, dividend % divisor); } /// # Summary diff --git a/src/Simulation/Simulators.Tests/Circuits/CoreOperations.qs b/src/Simulation/Simulators.Tests/Circuits/CoreOperations.qs index 12d4e4de..20281cfd 100644 --- a/src/Simulation/Simulators.Tests/Circuits/CoreOperations.qs +++ b/src/Simulation/Simulators.Tests/Circuits/CoreOperations.qs @@ -441,7 +441,11 @@ namespace Microsoft.Quantum.Simulation.Simulators.Tests.Circuits { let arr2 = [true, true, true, false, true, false, false, false, true, false, false, false, false, false, false, false]; // Exactly 2 bytes AssertEqual(279L, BoolArrayAsBigInt(arr2)); + let arr3 = [true, true, true, false, true, false, false, false, + true, false, false, false, false, false, false, true]; // Exactly 2 bytes, negative + AssertEqual(-32489L, BoolArrayAsBigInt(arr3)); AssertEqual(37L, BoolArrayAsBigInt(BigIntAsBoolArray(37L))); + AssertEqual(-37L, BoolArrayAsBigInt(BigIntAsBoolArray(-37L))); let (div, rem) = DivRemL(16L, 5L); AssertEqual(3L, div); AssertEqual(1L, rem);