using System; using System.Numerics; namespace WinRarKeygenGui.Core; /// /// BigInteger wrapper for the keygen algorithm, replacing the C++ mpz/GMP-based class. /// public class BigInt { public BigInteger Value { get; set; } public BigInt() { Value = BigInteger.Zero; } public BigInt(BigInteger value) { Value = value; } public BigInt(BigInt other) { Value = other.Value; } /// /// Construct from hex string (with or without "0x" prefix). /// public BigInt(string hexStr) { if (hexStr.StartsWith("0x", StringComparison.OrdinalIgnoreCase)) hexStr = hexStr[2..]; Value = BigInteger.Parse("0" + hexStr, System.Globalization.NumberStyles.HexNumber); } /// /// Construct from raw bytes (big endian or little endian). /// public BigInt(bool isNegative, byte[] buffer, bool littleEndian) { byte[] data; if (littleEndian) { // BigInteger expects little-endian with a leading zero byte to ensure positive data = new byte[buffer.Length + 1]; Array.Copy(buffer, data, buffer.Length); data[buffer.Length] = 0; // ensure positive } else { // Convert big-endian to little-endian data = new byte[buffer.Length + 1]; for (int i = 0; i < buffer.Length; i++) data[buffer.Length - 1 - i] = buffer[i]; data[buffer.Length] = 0; } Value = new BigInteger(data); if (isNegative) Value = -Value; } /// /// Construct from ushort[] array (little-endian, matching C++ behavior). /// public BigInt(bool isNegative, ushort[] buffer) { var bytes = new byte[buffer.Length * 2 + 1]; for (int i = 0; i < buffer.Length; i++) { bytes[i * 2] = (byte)(buffer[i] & 0xFF); bytes[i * 2 + 1] = (byte)(buffer[i] >> 8); } bytes[^1] = 0; // ensure positive Value = new BigInteger(bytes); if (isNegative) Value = -Value; } /// /// Load from byte buffer (matching C++ Load with little-endian). /// public void Load(bool isNegative, byte[] buffer, bool littleEndian) { byte[] data; if (littleEndian) { data = new byte[buffer.Length + 1]; Array.Copy(buffer, data, buffer.Length); } else { data = new byte[buffer.Length + 1]; for (int i = 0; i < buffer.Length; i++) data[buffer.Length - 1 - i] = buffer[i]; } data[^1] = 0; Value = new BigInteger(data); if (isNegative) Value = -Value; } public bool IsZero() => Value.IsZero; public bool IsPositive() => Value.Sign > 0; public bool IsNegative() => Value.Sign < 0; public int BitLength() => Value.IsZero ? 0 : (int)Value.GetBitLength(); public bool TestBit(int i) => !Value.IsZero && (Value >> i & BigInteger.One) == BigInteger.One; public void SetBit(int i) => Value |= BigInteger.One << i; public static BigInt operator +(BigInt a, BigInt b) => new(a.Value + b.Value); public static BigInt operator -(BigInt a, BigInt b) => new(a.Value - b.Value); public static BigInt operator *(BigInt a, BigInt b) => new(a.Value * b.Value); public static BigInt operator *(BigInt a, int b) => new(a.Value * b); /// /// Floor-division modulo, matching mpz_fdiv_r behavior for negative numbers. /// public static BigInt operator %(BigInt a, BigInt b) { var r = BigInteger.Remainder(a.Value, b.Value); if (r.Sign < 0) r += BigInteger.Abs(b.Value); return new BigInt(r); } public static bool operator ==(BigInt a, BigInt b) { if (a is null && b is null) return true; if (a is null || b is null) return false; return a.Value == b.Value; } public static bool operator !=(BigInt a, BigInt b) => !(a == b); public override bool Equals(object? obj) => obj is BigInt other && Value == other.Value; public override int GetHashCode() => Value.GetHashCode(); /// /// Convert to hex string (lowercase), no "0x" prefix. /// public string ToHexString() { if (Value.IsZero) return "0"; var hex = Value.ToString("x"); // Remove leading zeros return hex.TrimStart('0'); } /// /// Export as big-endian byte array (absolute value). /// public byte[] ToByteArrayBigEndian() { var bytes = BigInteger.Abs(Value).ToByteArray(); // little-endian, unsigned // Remove trailing zero if present int len = bytes.Length; while (len > 1 && bytes[len - 1] == 0) len--; var result = new byte[len]; for (int i = 0; i < len; i++) result[len - 1 - i] = bytes[i]; return result; } }