Files
StegoHackathonConsole/Program.cs

748 lines
30 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using BitVectorLib;
namespace StegoHackathonConsole
{
class Program
{
// Настройки по умолчанию
private static int _threads = Environment.ProcessorCount;
private static bool _verbose = false;
static async Task Main(string[] args)
{
// Настройка кодировки для корректного вывода кириллицы
Console.OutputEncoding = Encoding.UTF8;
PrintBanner();
if (args.Length == 0)
{
PrintHelp();
return;
}
var arguments = ParseArguments(args);
string mode = arguments.GetValueOrDefault("mode") ?? string.Empty;
mode = mode.ToLower();
// Глобальные настройки
if (arguments.ContainsKey("threads"))
int.TryParse(arguments["threads"], out _threads);
if (arguments.ContainsKey("verbose"))
_verbose = true;
try
{
switch (mode)
{
case "hack":
await RunHackModeFast(arguments);
break;
case "encrypt":
await RunEncryptMode(arguments);
break;
case "verify":
await RunVerifyMode(arguments);
break;
default:
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("ОШИБКА: Неизвестный режим работы.");
Console.ResetColor();
PrintHelp();
break;
}
}
catch (Exception ex)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine($"\nКРИТИЧЕСКАЯ ОШИБКА: {ex.Message}");
if (_verbose) Console.WriteLine(ex.StackTrace);
Console.ResetColor();
}
}
static void PrintBanner()
{
var process = Process.GetCurrentProcess();
var gcInfo = GC.GetGCMemoryInfo();
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine("================================================================================");
Console.WriteLine(" STEGOHACKATHON CLI v1.0 ");
Console.WriteLine("================================================================================");
Console.ResetColor();
Console.WriteLine($" [SYSTEM] OS Description : {System.Runtime.InteropServices.RuntimeInformation.OSDescription}");
Console.WriteLine($" [SYSTEM] Architecture : {System.Runtime.InteropServices.RuntimeInformation.OSArchitecture}");
string cpuName = GetCpuModelName();
Console.WriteLine($" [CPU] Model : {cpuName}");
Console.WriteLine($" [CPU] Logical Cores : {Environment.ProcessorCount}");
long totalRamMb = gcInfo.TotalAvailableMemoryBytes / 1024 / 1024;
long appMemMb = process.WorkingSet64 / 1024 / 1024;
Console.WriteLine($" [RAM] System Total : {totalRamMb:N0} MB");
Console.WriteLine($" [RAM] App Current : {appMemMb:N0} MB");
Console.WriteLine($" [PROC] Threads Limit : {_threads}");
Console.WriteLine($" [PROC] GC Mode : {(System.Runtime.GCSettings.IsServerGC ? "Server" : "Workstation")}");
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine("================================================================================\n");
Console.ResetColor();
}
static string GetCpuModelName()
{
try
{
if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Linux))
{
if (File.Exists("/proc/cpuinfo"))
{
var lines = File.ReadAllLines("/proc/cpuinfo");
foreach (var line in lines)
{
if (line.StartsWith("model name", StringComparison.OrdinalIgnoreCase) ||
line.StartsWith("Model", StringComparison.OrdinalIgnoreCase))
{
var parts = line.Split(':');
if (parts.Length > 1) return parts[1].Trim();
}
}
}
}
else if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows))
{
string? name = Environment.GetEnvironmentVariable("PROCESSOR_IDENTIFIER");
if (!string.IsNullOrEmpty(name)) return name;
}
}
catch { }
return "Unknown / Generic";
}
static void PrintHelp()
{
Console.WriteLine("Использование:");
Console.WriteLine(" --mode hack --data <файл.bin> --book <книга.txt> [--threads <N>]");
Console.WriteLine(" --mode encrypt --book <книга.txt> --out <вывод.bin>");
Console.WriteLine(" --mode verify --data <файл.bin> --key <ключ.key.bin>");
Console.WriteLine("\nПример:");
Console.WriteLine(" dotnet run -- --mode hack --data task1.bin --book WarAndPeace.txt --threads 8");
}
static Dictionary<string, string> ParseArguments(string[] args)
{
var dict = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
for (int i = 0; i < args.Length; i++)
{
if (args[i].StartsWith("--") || args[i].StartsWith("-"))
{
string key = args[i].TrimStart('-');
string val = "true";
if (i + 1 < args.Length && !args[i + 1].StartsWith("-"))
{
val = args[i + 1];
i++;
}
dict[key] = val;
}
}
return dict;
}
// --- РЕЖИМ 1: ОПТИМИЗИРОВАННЫЙ ВЗЛОМ ---
static async Task RunHackModeFast(Dictionary<string, string> args)
{
string stegoPath = args.GetValueOrDefault("data") ?? string.Empty;
string bookPath = args.GetValueOrDefault("book") ?? string.Empty;
if (string.IsNullOrEmpty(stegoPath) || !File.Exists(stegoPath))
{
Console.WriteLine("[ERR] Файл данных не найден.");
return;
}
if (string.IsNullOrEmpty(bookPath) || !File.Exists(bookPath))
{
Console.WriteLine("[ERR] Файл книги не найден.");
return;
}
Console.WriteLine($"[INIT] Чтение файлов...");
byte[] stegoBytes = await File.ReadAllBytesAsync(stegoPath);
string bookText = await File.ReadAllTextAsync(bookPath, Encoding.UTF8);
byte[] bookBytes = Encoding.UTF8.GetBytes(bookText);
Console.WriteLine($"[DATA] Стего-контейнер: {stegoBytes.Length} байт");
Console.WriteLine($"[BOOK] Книга: {bookBytes.Length} байт");
string[] etalonStrings = AssocStego.GetDefaultEtalons10();
int etalonLen = etalonStrings[0].Length;
int blockByteSize = (etalonLen + 7) / 8;
byte[][] etalonsRaw = new byte[10][];
for (int i = 0; i < 10; i++)
{
etalonsRaw[i] = new BitVector(etalonStrings[i]).ToByteArray();
}
int bytesEncrypted = stegoBytes.Length / (3 * blockByteSize);
Console.WriteLine($"[INFO] Длина блока: {blockByteSize} байт. Скрыто символов: {bytesEncrypted}");
List<byte[]> stegoChunks = new List<byte[]>(bytesEncrypted * 3);
for (int i = 0; i < stegoBytes.Length; i += blockByteSize)
{
byte[] chunk = new byte[blockByteSize];
Array.Copy(stegoBytes, i, chunk, 0, blockByteSize);
stegoChunks.Add(chunk);
}
int maxOffset = bookBytes.Length - bytesEncrypted;
if (maxOffset <= 0)
{
Console.WriteLine("[FAIL] Размер книги меньше данных.");
return;
}
Stopwatch sw = Stopwatch.StartNew();
long processed = 0;
bool found = false;
object lockObj = new object();
Console.WriteLine($"[RUN ] СТАРТ ПЕРЕБОРА (TURBO MODE) на {_threads} потоках...");
using var timer = new Timer(_ =>
{
long current = Interlocked.Read(ref processed);
double progress = (double)current / maxOffset * 100.0;
double speed = current / sw.Elapsed.TotalSeconds;
long ram = Process.GetCurrentProcess().WorkingSet64 / 1024 / 1024;
Console.Write($"\r[PROG] {progress:F2}% | Speed: {speed:N0} ops/s | RAM: {ram} MB | Time: {sw.Elapsed:mm\\:ss} ");
}, null, 1000, 1000);
var pOptions = new ParallelOptions { MaxDegreeOfParallelism = _threads };
Parallel.For(0, maxOffset + 1, pOptions, (i, state) =>
{
if (found) { state.Stop(); return; }
if ((i & 4095) == 0) Interlocked.Add(ref processed, 4096);
byte[][] currentMasks = new byte[10][];
for (int k = 0; k < 10; k++)
{
currentMasks[k] = new byte[blockByteSize];
for (int b = 0; b < blockByteSize; b++) currentMasks[k][b] = 0xFF;
}
bool[] digitUsed = new bool[10];
bool possible = true;
int quickCheckLen = (bytesEncrypted < 15) ? bytesEncrypted : 15;
for (int j = 0; j < quickCheckLen; j++)
{
byte b = bookBytes[i + j];
int d2 = b / 100;
int d1 = (b % 100) / 10;
int d0 = b % 10;
int idx = j * 3;
FastAndNotXor(currentMasks[d2], stegoChunks[idx], etalonsRaw[d2]);
FastAndNotXor(currentMasks[d1], stegoChunks[idx + 1], etalonsRaw[d1]);
FastAndNotXor(currentMasks[d0], stegoChunks[idx + 2], etalonsRaw[d0]);
digitUsed[d2] = true;
digitUsed[d1] = true;
digitUsed[d0] = true;
}
for (int k = 0; k < 10; k++)
{
if (digitUsed[k])
{
if (FastPopCount(currentMasks[k]) < 2)
{
possible = false;
break;
}
}
}
if (!possible) return;
for (int k = 0; k < 10; k++)
{
for (int b = 0; b < blockByteSize; b++) currentMasks[k][b] = 0xFF;
digitUsed[k] = false;
}
for (int j = 0; j < bytesEncrypted; j++)
{
byte b = bookBytes[i + j];
int d2 = b / 100;
int d1 = (b % 100) / 10;
int d0 = b % 10;
int idx = j * 3;
FastAndNotXor(currentMasks[d2], stegoChunks[idx], etalonsRaw[d2]);
FastAndNotXor(currentMasks[d1], stegoChunks[idx + 1], etalonsRaw[d1]);
FastAndNotXor(currentMasks[d0], stegoChunks[idx + 2], etalonsRaw[d0]);
digitUsed[d2] = true; digitUsed[d1] = true; digitUsed[d0] = true;
}
int totalBits = 0;
for (int k = 0; k < 10; k++)
{
if (digitUsed[k])
totalBits += FastPopCount(currentMasks[k]);
}
if (totalBits > 5)
{
lock (lockObj)
{
if (!found)
{
found = true;
sw.Stop();
Console.WriteLine($"\n\n[SUCCESS] Взлом успешен! Offset: {i}");
Console.WriteLine($"[TIME] Время: {sw.ElapsedMilliseconds} мс");
SaveFoundKey(stegoPath, bookBytes, i, bytesEncrypted, currentMasks, etalonStrings, etalonLen);
state.Stop();
}
}
}
});
if (!found) Console.WriteLine("\n[FAIL] Совпадений не найдено.");
}
static void FastAndNotXor(byte[] target, byte[] stego, byte[] etalon)
{
for (int i = 0; i < target.Length; i++)
{
target[i] = (byte)(target[i] & ~(stego[i] ^ etalon[i]));
}
}
static int FastPopCount(byte[] data)
{
int count = 0;
for (int i = 0; i < data.Length; i++)
{
int n = data[i];
while (n != 0)
{
count++;
n &= (n - 1);
}
}
return count;
}
static void SaveFoundKey(string stegoPath, byte[] bookBytes, int offset, int len, byte[][] masksRaw, string[] etalonsStr, int etalonLen)
{
byte[] decrypted = new byte[len];
Array.Copy(bookBytes, offset, decrypted, 0, len);
string text = Encoding.UTF8.GetString(decrypted);
Console.WriteLine("--- НАЧАЛО ТЕКСТА ---");
Console.WriteLine(text.Substring(0, Math.Min(100, text.Length)) + "...");
Console.WriteLine("---------------------");
BitVector[] keyVectors = new BitVector[10];
for (int k = 0; k < 10; k++)
{
keyVectors[k] = new BitVector(masksRaw[k], etalonLen);
}
var sys = new AssocStego(etalonsStr);
string keyPath = Path.ChangeExtension(stegoPath, ".recovered.key.bin");
string txtPath = Path.ChangeExtension(stegoPath, ".txt");
sys.SaveKeyToFile(keyPath, keyVectors);
File.WriteAllText(txtPath, text);
Console.WriteLine($"[SAVE] Ключ: {keyPath}");
Console.WriteLine($"[SAVE] Текст: {txtPath}");
}
// --- РЕЖИМ 2: ШИФРОВАНИЕ ---
static async Task RunEncryptMode(Dictionary<string, string> args)
{
string bookPath = args.GetValueOrDefault("book") ?? string.Empty;
string outFile = args.GetValueOrDefault("out") ?? "output.bin";
if (string.IsNullOrEmpty(bookPath) || !File.Exists(bookPath)) throw new FileNotFoundException("Книга не найдена.");
string text = await File.ReadAllTextAsync(bookPath);
Random rnd = new Random();
int len = rnd.Next(200, 600);
int start = rnd.Next(0, text.Length - len);
string secretText = text.Substring(start, len);
Console.WriteLine($"[ENCRYPT] Фрагмент: \"{secretText.Substring(0, Math.Min(30, secretText.Length))}...\"");
var etalons = AssocStego.GetDefaultEtalons10();
var system = new AssocStego(etalons);
Console.WriteLine("[KEY] Генерация масок...");
var keys = system.CreateKey();
string keyFile = Path.ChangeExtension(outFile, ".key.bin");
system.SaveKeyToFile(keyFile, keys);
Console.WriteLine($"[WRITE] Запись в {outFile}...");
byte[] data = Encoding.UTF8.GetBytes(secretText);
using (var fs = new FileStream(outFile, FileMode.Create))
using (var ss = new AssocStegoStream(fs, system))
{
ss.Write(data, 0, data.Length);
}
Console.WriteLine("[DONE] Успешно.");
}
// --- РЕЖИМ 3: ПРОВЕРКА ---
static async Task RunVerifyMode(Dictionary<string, string> args)
{
string dataPath = args.GetValueOrDefault("data") ?? string.Empty;
string keyPath = args.GetValueOrDefault("key") ?? string.Empty;
if (!File.Exists(dataPath) || !File.Exists(keyPath)) throw new FileNotFoundException("Файлы не найдены.");
var etalons = AssocStego.GetDefaultEtalons10();
var system = new AssocStego(etalons);
Console.WriteLine($"[LOAD] Ключ: {keyPath}");
system.Key = system.LoadKeyFromFile(keyPath);
byte[] encrypted = await File.ReadAllBytesAsync(dataPath);
int containerByteLen = (system.etalonLength + 7) / 8;
int count = encrypted.Length / (containerByteLen * 3);
byte[] buffer = new byte[count];
Console.WriteLine($"[DECRYPT] Расшифровка...");
using (var ms = new MemoryStream(encrypted))
using (var ss = new AssocStegoStream(ms, system))
{
ss.Read(buffer, 0, count);
}
Console.WriteLine($"[TEXT] {Encoding.UTF8.GetString(buffer)}");
}
}
// --- КЛАССЫ ЛОГИКИ (ИСПОЛЬЗУЮТ BitVectorLib) ---
public class AssocStegoStream : Stream
{
private readonly Stream _stream;
private readonly AssocStego _stegoAlg;
private readonly int _containerByteLen;
private readonly int _hiddenByteLen;
public AssocStegoStream(Stream stream, AssocStego stegoAlg)
{
_stream = stream;
_stegoAlg = stegoAlg;
_containerByteLen = (_stegoAlg.etalonLength + 7) / 8;
_hiddenByteLen = 3 * _containerByteLen;
}
public override bool CanRead => _stream.CanRead;
public override bool CanSeek => false;
public override bool CanWrite => _stream.CanWrite;
public override long Length => _stream.Length;
public override long Position { get => _stream.Position; set => throw new NotSupportedException(); }
public override void Flush() => _stream.Flush();
public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();
public override void SetLength(long value) => throw new NotSupportedException();
public override void Write(byte[] buffer, int offset, int count)
{
for (int i = offset; i < offset + count; i++)
{
byte[] hidden = HideByte(buffer[i]);
_stream.Write(hidden, 0, hidden.Length);
}
}
public override int Read(byte[] buffer, int offset, int count)
{
int readCount = 0;
for (int i = offset; i < offset + count; i++)
{
var hidden = new byte[_hiddenByteLen];
int totalRead = 0;
while (totalRead < _hiddenByteLen)
{
int r = _stream.Read(hidden, totalRead, _hiddenByteLen - totalRead);
if (r == 0) return readCount;
totalRead += r;
}
buffer[i] = DiscloseByte(hidden);
readCount++;
}
return readCount;
}
private byte[] HideByte(byte value)
{
int v = value;
int d2 = v / 100;
int rem = v % 100;
int d1 = rem / 10;
int d0 = rem % 10;
BitVector c2 = _stegoAlg.HideEtalon(d2);
BitVector c1 = _stegoAlg.HideEtalon(d1);
BitVector c0 = _stegoAlg.HideEtalon(d0);
byte[] b2 = c2.ToByteArray();
byte[] b1 = c1.ToByteArray();
byte[] b0 = c0.ToByteArray();
byte[] result = new byte[b2.Length + b1.Length + b0.Length];
int pos = 0;
Buffer.BlockCopy(b2, 0, result, pos, b2.Length); pos += b2.Length;
Buffer.BlockCopy(b1, 0, result, pos, b1.Length); pos += b1.Length;
Buffer.BlockCopy(b0, 0, result, pos, b0.Length);
return result;
}
public byte DiscloseByte(byte[] hidden)
{
byte[] b2 = new byte[_containerByteLen];
byte[] b1 = new byte[_containerByteLen];
byte[] b0 = new byte[_containerByteLen];
int pos = 0;
Buffer.BlockCopy(hidden, pos, b2, 0, _containerByteLen); pos += _containerByteLen;
Buffer.BlockCopy(hidden, pos, b1, 0, _containerByteLen); pos += _containerByteLen;
Buffer.BlockCopy(hidden, pos, b0, 0, _containerByteLen);
int d2 = _stegoAlg.DiscloseEtalon(new BitVector(b2, _stegoAlg.etalonLength));
int d1 = _stegoAlg.DiscloseEtalon(new BitVector(b1, _stegoAlg.etalonLength));
int d0 = _stegoAlg.DiscloseEtalon(new BitVector(b0, _stegoAlg.etalonLength));
if (d2 < 0 || d1 < 0 || d0 < 0) return 63;
return (byte)(d2 * 100 + d1 * 10 + d0);
}
}
public class AssocStego
{
public readonly BitVector[] etalons;
public readonly int etalonLength;
private readonly List<int>[] maskUnitPositions;
private BitVector[]? key;
private readonly BitVector zeroVector;
// Свойство выбрасывает исключение, если ключ не задан
public BitVector[] Key
{
get => key ?? throw new InvalidOperationException("Ключ не сгенерирован!");
set => key = value;
}
public AssocStego(params string[] e)
{
etalonLength = e[0].Length;
etalons = new BitVector[e.Length];
maskUnitPositions = new List<int>[e.Length];
zeroVector = new BitVector(new byte[(etalonLength + 7) / 8], etalonLength);
for (int i = 0; i < e.Length; i++)
{
etalons[i] = new BitVector(e[i]);
maskUnitPositions[i] = new List<int>();
}
}
public BitVector[] CreateKey()
{
var eIndexes = Enumerable.Range(0, etalons.Length).ToList();
ResetMaskUnitPositions();
CreateKeyRec(eIndexes, true);
var masks = new BitVector[etalons.Length];
for (int i = 0; i < etalons.Length; i++)
masks[i] = CreateVector(etalonLength, maskUnitPositions[i].ToArray());
Key = masks;
return masks;
}
private void CreateKeyRec(List<int> eIndexes, bool firstPerm)
{
if (eIndexes.Count < 2) return;
var c = firstPerm ? Shuffle(eIndexes) : eIndexes;
var d1 = new List<int> { c[0] };
BitVector a0 = etalons[c[0]] ^ etalons[c[1]];
for (int i = 0; i < c.Count - 2; i++)
{
BitVector a1 = etalons[c[0]] ^ etalons[c[i + 2]];
BitVector a3 = a0 & a1;
if (IsZero(a3)) d1.Add(c[i + 2]);
else a0 = a3;
}
int rndBit = GetRandomOneBitIndex(a0);
foreach (int idx in c) maskUnitPositions[idx].Add(rndBit);
var d2 = c.Except(d1).ToList();
Parallel.Invoke(() => CreateKeyRec(d1, false), () => CreateKeyRec(d2, false));
}
private bool IsZero(BitVector v)
{
foreach (byte b in v.ToByteArray()) if (b != 0) return false;
return true;
}
private List<int> Shuffle(List<int> list)
{
var res = new List<int>(list);
var rng = new Random();
int n = res.Count;
while (n > 1) { n--; int k = rng.Next(n + 1); (res[k], res[n]) = (res[n], res[k]); }
return res;
}
private void ResetMaskUnitPositions() { foreach (var l in maskUnitPositions) l.Clear(); }
private int GetRandomOneBitIndex(BitVector v)
{
var bytes = v.ToByteArray();
var ones = new List<int>();
for (int i = 0; i < bytes.Length * 8; i++)
{
int byteIdx = i / 8;
if (byteIdx >= bytes.Length) break;
int bitIdx = 7 - (i % 8);
if ((bytes[byteIdx] & (1 << bitIdx)) != 0) ones.Add(i);
}
if (ones.Count == 0) return 0;
return ones[new Random().Next(ones.Count)];
}
private BitVector CreateVector(int len, int[] indexes)
{
int byteLen = (len + 7) / 8;
byte[] arr = new byte[byteLen];
foreach (int idx in indexes)
{
int byteIndex = idx / 8;
int bitIndex = 7 - (idx % 8);
if (byteIndex < arr.Length) arr[byteIndex] |= (byte)(1 << bitIndex);
}
return new BitVector(arr, len);
}
public BitVector HideEtalon(int idx)
{
byte[] randomBytes = new byte[(etalonLength + 7) / 8];
RandomNumberGenerator.Fill(randomBytes);
BitVector container = new BitVector(randomBytes, etalonLength);
return container ^ ((container ^ etalons[idx]) & Key[idx]);
}
public int DiscloseEtalon(BitVector stego)
{
for (int i = 0; i < etalons.Length; i++)
{
BitVector a = stego & Key[i];
BitVector b = etalons[i] & Key[i];
if (a.ToByteArray().SequenceEqual(b.ToByteArray())) return i;
}
return -1;
}
public void SaveKeyToFile(string path, BitVector[] k)
{
using var bw = new BinaryWriter(File.Create(path));
bw.Write(0x41534B59);
bw.Write(k.Length);
bw.Write(etalonLength);
foreach (var vec in k)
{
byte[] b = vec.ToByteArray();
bw.Write(b.Length);
bw.Write(b);
}
}
public BitVector[] LoadKeyFromFile(string path)
{
using var br = new BinaryReader(File.OpenRead(path));
if (br.ReadInt32() != 0x41534B59) throw new Exception("Bad magic");
int count = br.ReadInt32();
int len = br.ReadInt32();
var res = new BitVector[count];
for (int i = 0; i < count; i++)
{
int blen = br.ReadInt32();
byte[] b = br.ReadBytes(blen);
res[i] = new BitVector(b, len);
}
return res;
}
public static string[] GetDefaultEtalons10() => new[] {
"111111111111111111111111111111111111111111111111111111000000000000000000000000",
"000000000100000000000000000111111111111111111100000000000000000000000011111111",
"100000000000000000111111111111111111100000000111111111111111110000000000000000",
"100000000100000000111111111100000000100000000000000000111111111111111111111111",
"000000000111111111100000000111111111111111111100000000000000001111111100000000",
"100000000111111111111111111100000000111111111111111111000000001111111100000000",
"111111111100000000000000000100000000111111111111111111000000001111111111111111",
"111111111100000000111111111100000000000000000000000000000000000000000011111111",
"111111111111111111111111111111111111111111111111111111000000001111111100000000",
"100000000111111111111111111111111111100000000000000000111111111111111100000000"
}.Select(s => new string(s.Reverse().ToArray())).ToArray();
}
public static class BitVectorHelpers
{
public static BitVector CreateFullMask(int bitLength)
{
int byteLen = (bitLength + 7) / 8;
byte[] b = new byte[byteLen];
for (int i = 0; i < byteLen; i++) b[i] = 0xFF;
return new BitVector(b, bitLength);
}
public static BitVector CreateZeroMask(int bitLength) => new BitVector(new byte[(bitLength + 7) / 8], bitLength);
public static BitVector AndNot(BitVector a, BitVector b)
{
byte[] ba = a.ToByteArray();
byte[] bb = b.ToByteArray();
int len = Math.Min(ba.Length, bb.Length);
byte[] result = new byte[ba.Length];
for (int i = 0; i < len; i++) result[i] = (byte)(ba[i] & ~bb[i]);
return new BitVector(result, a.ToByteArray().Length * 8);
}
public static int PopCount(BitVector v)
{
byte[] bytes = v.ToByteArray();
int count = 0;
foreach (byte b in bytes)
{
int n = b;
while (n > 0) { if ((n & 1) == 1) count++; n >>= 1; }
}
return count;
}
}
}