From cf041e4ec8bb9f8745d8679bb4bf2927c5e729b8 Mon Sep 17 00:00:00 2001 From: tsotchke Date: Tue, 14 Jul 2026 01:20:13 -0400 Subject: [PATCH] feat: migrate API to quantum_rng v3 --- .gitattributes | 1 + .github/workflows/ci.yml | 28 + CHANGELOG.md | 16 + README.md | 327 +-- binding.gyp | 97 +- package-lock.json | 2006 +++++++---------- package.json | 42 +- server.js | 831 ++++--- src/binding.cc | 388 ++-- src/qrng_v3_bridge.c | 163 ++ src/qrng_v3_bridge.h | 70 + test/native.test.js | 77 + test/server.test.js | 114 + vendor/quantum_rng/LICENSE | 21 + vendor/quantum_rng/UPSTREAM.md | 28 + .../quantum_rng/src}/common/constants.h | 0 vendor/quantum_rng/src/common/secure_memory.h | 91 + vendor/quantum_rng/src/common/validation.h | 251 +++ vendor/quantum_rng/src/entropy/entropy_pool.c | 499 ++++ vendor/quantum_rng/src/entropy/entropy_pool.h | 208 ++ .../src/entropy/hardware_entropy.c | 765 +++++++ .../src/entropy/hardware_entropy.h | 270 +++ vendor/quantum_rng/src/health/health_tests.c | 396 ++++ vendor/quantum_rng/src/health/health_tests.h | 323 +++ .../src/profiling/performance_monitor.c | 280 +++ .../src/profiling/performance_monitor.h | 178 ++ .../src/quantum_rng/accelerate_ops.c | 735 ++++++ .../src/quantum_rng/accelerate_ops.h | 398 ++++ .../quantum_rng/src/quantum_rng/bell_test.c | 514 +++++ .../quantum_rng/src/quantum_rng/bell_test.h | 288 +++ .../src/quantum_rng/cuda/cuda_bridge.cu | 668 ++++++ .../src/quantum_rng/cuda/cuda_bridge.h | 296 +++ vendor/quantum_rng/src/quantum_rng/grover.c | 610 +++++ vendor/quantum_rng/src/quantum_rng/grover.h | 278 +++ .../src/quantum_rng/grover_parallel.c | 491 ++++ .../src/quantum_rng/grover_parallel.h | 185 ++ .../quantum_rng/src/quantum_rng/matrix_math.c | 275 +++ .../quantum_rng/src/quantum_rng/matrix_math.h | 100 + .../src/quantum_rng/metal/metal_bridge.h | 417 ++++ .../src/quantum_rng/metal/metal_bridge.mm | 837 +++++++ .../quantum_rng/metal/quantum_kernels.metal | 510 +++++ .../metal/quantum_kernels_batch.metal | 291 +++ .../src/quantum_rng/quantum_constants.h | 125 + .../src/quantum_rng/quantum_entropy.h | 117 + .../src/quantum_rng/quantum_gates.c | 1022 +++++++++ .../src/quantum_rng/quantum_gates.h | 377 ++++ .../src}/quantum_rng/quantum_rng.c | 123 +- .../src}/quantum_rng/quantum_rng.h | 21 +- .../src/quantum_rng/quantum_rng_v3.c | 1176 ++++++++++ .../src/quantum_rng/quantum_rng_v3.h | 474 ++++ .../src/quantum_rng/quantum_state.c | 551 +++++ .../src/quantum_rng/quantum_state.h | 281 +++ vendor/quantum_rng/src/quantum_rng/simd_ops.c | 726 ++++++ vendor/quantum_rng/src/quantum_rng/simd_ops.h | 254 +++ 54 files changed, 17639 insertions(+), 1971 deletions(-) create mode 100644 .gitattributes create mode 100644 .github/workflows/ci.yml create mode 100644 CHANGELOG.md create mode 100644 src/qrng_v3_bridge.c create mode 100644 src/qrng_v3_bridge.h create mode 100644 test/native.test.js create mode 100644 test/server.test.js create mode 100644 vendor/quantum_rng/LICENSE create mode 100644 vendor/quantum_rng/UPSTREAM.md rename {src => vendor/quantum_rng/src}/common/constants.h (100%) create mode 100644 vendor/quantum_rng/src/common/secure_memory.h create mode 100644 vendor/quantum_rng/src/common/validation.h create mode 100644 vendor/quantum_rng/src/entropy/entropy_pool.c create mode 100644 vendor/quantum_rng/src/entropy/entropy_pool.h create mode 100644 vendor/quantum_rng/src/entropy/hardware_entropy.c create mode 100644 vendor/quantum_rng/src/entropy/hardware_entropy.h create mode 100644 vendor/quantum_rng/src/health/health_tests.c create mode 100644 vendor/quantum_rng/src/health/health_tests.h create mode 100644 vendor/quantum_rng/src/profiling/performance_monitor.c create mode 100644 vendor/quantum_rng/src/profiling/performance_monitor.h create mode 100644 vendor/quantum_rng/src/quantum_rng/accelerate_ops.c create mode 100644 vendor/quantum_rng/src/quantum_rng/accelerate_ops.h create mode 100644 vendor/quantum_rng/src/quantum_rng/bell_test.c create mode 100644 vendor/quantum_rng/src/quantum_rng/bell_test.h create mode 100644 vendor/quantum_rng/src/quantum_rng/cuda/cuda_bridge.cu create mode 100644 vendor/quantum_rng/src/quantum_rng/cuda/cuda_bridge.h create mode 100644 vendor/quantum_rng/src/quantum_rng/grover.c create mode 100644 vendor/quantum_rng/src/quantum_rng/grover.h create mode 100644 vendor/quantum_rng/src/quantum_rng/grover_parallel.c create mode 100644 vendor/quantum_rng/src/quantum_rng/grover_parallel.h create mode 100644 vendor/quantum_rng/src/quantum_rng/matrix_math.c create mode 100644 vendor/quantum_rng/src/quantum_rng/matrix_math.h create mode 100644 vendor/quantum_rng/src/quantum_rng/metal/metal_bridge.h create mode 100644 vendor/quantum_rng/src/quantum_rng/metal/metal_bridge.mm create mode 100644 vendor/quantum_rng/src/quantum_rng/metal/quantum_kernels.metal create mode 100644 vendor/quantum_rng/src/quantum_rng/metal/quantum_kernels_batch.metal create mode 100644 vendor/quantum_rng/src/quantum_rng/quantum_constants.h create mode 100644 vendor/quantum_rng/src/quantum_rng/quantum_entropy.h create mode 100644 vendor/quantum_rng/src/quantum_rng/quantum_gates.c create mode 100644 vendor/quantum_rng/src/quantum_rng/quantum_gates.h rename {src => vendor/quantum_rng/src}/quantum_rng/quantum_rng.c (79%) rename {src => vendor/quantum_rng/src}/quantum_rng/quantum_rng.h (85%) create mode 100644 vendor/quantum_rng/src/quantum_rng/quantum_rng_v3.c create mode 100644 vendor/quantum_rng/src/quantum_rng/quantum_rng_v3.h create mode 100644 vendor/quantum_rng/src/quantum_rng/quantum_state.c create mode 100644 vendor/quantum_rng/src/quantum_rng/quantum_state.h create mode 100644 vendor/quantum_rng/src/quantum_rng/simd_ops.c create mode 100644 vendor/quantum_rng/src/quantum_rng/simd_ops.h diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..95c800f --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +vendor/quantum_rng/** -whitespace diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..27bcb4e --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,28 @@ +name: CI + +on: + push: + branches: [master] + pull_request: + +permissions: + contents: read + +jobs: + build-and-test: + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest] + node: [20, 22] + runs-on: ${{ matrix.os }} + timeout-minutes: 20 + + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node }} + cache: npm + - run: npm ci --ignore-scripts + - run: npm test diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..1782659 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,16 @@ +# Changelog + +## 2.0.0 — 2026-07-14 + +- Replaced the embedded legacy v1.1 generator with the vendored + `quantum_rng` v3 state-vector engine. +- Pinned upstream revision `1a77e77`, including the post-v3.0.1 full-width + output-conditioning fix. +- Added startup CHSH verification with explicit simulation-only scope. +- Added engine provenance, statistics, verification, and unsigned 64-bit + range endpoints. +- Fixed signed full-span range overflow, unsigned full-span constant output, + and `probability=0` handling. +- Added native and HTTP regression tests plus Linux/macOS CI. +- Rewrote public documentation to remove physical-hardware, FIPS, and + deployment-security claims not established by the repository. diff --git a/README.md b/README.md index 9ecd904..923dcd2 100644 --- a/README.md +++ b/README.md @@ -1,181 +1,190 @@ -# Quantum RNG API - -A high-performance REST API for true random number generation using quantum mechanical processes. This API provides cryptographically secure random numbers suitable for various applications including cryptography, scientific simulations, gaming, and statistical sampling. - -## Features - -- True random number generation using quantum mechanical processes -- Multiple output formats (bytes, integers, floats, booleans) -- Range-based random number generation -- Random element selection from arrays -- Real-time entropy monitoring -- Comprehensive API documentation with Swagger UI -- Production-ready with Railway deployment support - -## API Documentation - -The API comes with a built-in Swagger UI that provides interactive documentation and testing capabilities. Access the Swagger UI at: - -``` -https://api.tsotchke.net/v1/docs -``` - -This interface allows you to: -- Explore all available endpoints -- Test API calls directly from the browser -- View request/response schemas -- Understand parameter requirements and constraints - -## Deployment to Railway - -1. Create a new project on [Railway](https://railway.app) - -2. Connect your GitHub repository - -3. Deploy the service - -The API will automatically build and deploy. Railway will handle building the native module in the container environment. - -## API Endpoints - -### Base URL -``` -https://api.tsotchke.net -``` - -### Root Endpoint -``` -GET / -``` -Returns a welcome message and API information. - -### API Version 1 - -All v1 endpoints are prefixed with `/v1` - -#### Health Check -``` -GET /v1/health -``` - -#### Random Number Generation - -All random number generation endpoints are prefixed with `/v1/qrng/` - -##### Random Bytes -``` -GET /v1/qrng/bytes/:count -``` -Parameters: -- count: Number of random bytes (1-1024) - -##### Random Number -``` -GET /v1/qrng/number?type=float|uint64 -``` -Parameters: -- type: "float" (0-1) or "uint64" (optional, defaults to float) - -##### Random Range -``` -GET /v1/qrng/range?min=0&max=100 -``` -Parameters: -- min: Minimum value (inclusive) -- max: Maximum value (inclusive) - -##### Random Boolean -``` -GET /v1/qrng/boolean?probability=0.5 -``` -Parameters: -- probability: Chance of true (0-1, optional, defaults to 0.5) - -##### Random Choice -``` -POST /v1/qrng/choice -Content-Type: application/json - -{ - "array": ["apple", "banana", "orange"] -} -``` -Parameters: -- array: Array of items to choose from - -## Local Development - -### Prerequisites - -- Node.js >= 18.0.0 -- npm or yarn -- C++ build tools (for native module compilation) - -### Setup - -1. Install dependencies: -```bash -npm install -``` - -2. Build the native module: -```bash -npm run build -``` - -3. Start the server: -```bash +# Tsotchke Quantum RNG API + +[![CI](https://github.com/Tsotchke-Corporation/Quantum-RNG-API/actions/workflows/ci.yml/badge.svg)](https://github.com/Tsotchke-Corporation/Quantum-RNG-API/actions/workflows/ci.yml) +[![License: MIT](https://img.shields.io/badge/License-MIT-111111.svg)](LICENSE) +[![Engine](https://img.shields.io/badge/engine-quantum__rng_v3-6f42c1.svg)](https://github.com/tsotchke/quantum_rng) + +A production-oriented HTTP interface to the +[`quantum_rng`](https://github.com/tsotchke/quantum_rng) v3 state-vector +simulation engine. The service exposes random bytes, numbers, bounded draws, +health information, engine statistics, and the startup CHSH verification +result through a versioned REST API. + +The native engine is pinned to `v3.0.1+1a77e77`: the v3.0.1 release line plus +the critical full-width output-conditioning fix now present on upstream +`master`. + +## Scope of the claim + +This project does **not** claim access to physical quantum hardware. + +The engine performs real state-vector evolution, unitary gates, Born-rule +measurement, and wavefunction collapse in software. Measurement and final +output are driven and conditioned by operating-system and CPU entropy sources +that pass continuous health tests. The built-in CHSH test checks that the +simulated quantum model produces the expected non-classical correlations; it +is not device-independent certification of a physical entropy source. + +The repository is not FIPS validated and the public server should not be +treated as a drop-in key-generation service without deployment-specific TLS, +authentication, authorization, rate limiting, monitoring, and independent +security review. + +## What changed in API 2.0 + +- The separate legacy v1.1 generator has been removed. +- The Node-API addon now wraps the actual QRNG v3 engine. +- Upstream source and revision provenance are explicit and reproducible. +- Startup runs a CHSH verification of the simulation model by default. +- Full signed 32-bit and unsigned 64-bit ranges use unbiased, overflow-safe + sampling. +- `probability=0` and `probability=1` have exact boolean semantics. +- Health responses expose the engine mode, revision, verification scope, and + generation counters. +- Native and HTTP regression tests run on Linux and macOS in CI. + +## Architecture + +```text +HTTP request + │ + ▼ +Express API + │ + ▼ +Node-API C++ binding + │ + ▼ +quantum_rng v3 + ├── health-tested OS / CPU entropy pool + ├── state-vector evolution and Born-rule measurement + ├── direct, Grover, and Bell-verified modes + └── conditioned full-width output +``` + +The upstream C source is vendored under `vendor/quantum_rng` so builds do not +depend on mutable network state. See +[UPSTREAM.md](vendor/quantum_rng/UPSTREAM.md) for the pinned revision and update +procedure. + +## API + +The default local base URL is `http://localhost:3000`. The documented +production URL is `https://api.tsotchke.net`. + +| Method | Endpoint | Result | +| --- | --- | --- | +| `GET` | `/` | API and engine identity | +| `GET` | `/v1/health` | Operational status, provenance, verification, and counters | +| `GET` | `/v1/openapi.json` | OpenAPI 3.0 document | +| `GET` | `/v1/docs` | Swagger UI | +| `GET` | `/v1/qrng/bytes/:count` | 1–4096 random bytes in hex, Base64, and array form | +| `GET` | `/v1/qrng/number?type=float\|uint64` | Unit-interval float or decimal uint64 string | +| `GET` | `/v1/qrng/range?min=…&max=…` | Inclusive signed 32-bit bounded draw | +| `GET` | `/v1/qrng/range64?min=…&max=…` | Inclusive unsigned 64-bit bounded draw | +| `GET` | `/v1/qrng/boolean?probability=0.5` | Boolean with the requested true probability | +| `POST` | `/v1/qrng/choice` | Unbiased index and element from a JSON array | +| `GET` | `/v1/qrng/stats` | Native engine counters | +| `GET` | `/v1/qrng/verification` | Cached startup CHSH result and scope | + +Unsigned 64-bit values are returned as decimal strings because JSON numbers +cannot represent the full range exactly. + +### Examples + +```sh +curl https://api.tsotchke.net/v1/qrng/bytes/32 +curl 'https://api.tsotchke.net/v1/qrng/number?type=uint64' +curl 'https://api.tsotchke.net/v1/qrng/range?min=-100&max=100' +curl 'https://api.tsotchke.net/v1/qrng/range64?min=0&max=18446744073709551615' +curl 'https://api.tsotchke.net/v1/qrng/boolean?probability=0.25' +curl -X POST https://api.tsotchke.net/v1/qrng/choice \ + -H 'content-type: application/json' \ + -d '{"array":["alpha","beta","gamma"]}' +``` + +## Runtime configuration + +| Variable | Default | Meaning | +| --- | --- | --- | +| `PORT` | `3000` | HTTP listen port | +| `HOST` | `0.0.0.0` | HTTP listen address | +| `QRNG_MODE` | `direct` | `direct`, `grover`, or `bell-verified` | +| `QRNG_VERIFY_ON_START` | `1` | Set to `0` to skip startup CHSH verification | +| `QRNG_VERIFY_MEASUREMENTS` | `4000` | CHSH sample count, from 1000 to 100000 | + +`direct` is the default because it is the simplest and fastest state-vector +measurement path. Enabling `bell-verified` additionally activates periodic +verification in the upstream engine; it has materially higher latency. + +## Build and test + +Requirements: + +- Node.js 20.17 or newer +- Python 3 for `node-gyp` +- A C11 and C++17 compiler +- `make` + +```sh +git clone https://github.com/Tsotchke-Corporation/Quantum-RNG-API.git +cd Quantum-RNG-API +npm ci +npm test npm start ``` -The server will run on port 3000 by default. Set the PORT environment variable to change this. +The build is tested with Node.js 20 and 22 on Ubuntu and macOS. On Apple +platforms the native engine links against Accelerate; other platforms use its +portable SIMD/scalar paths. -## Technical Details +## Deployment -### Quantum Random Number Generation +`railway.toml` supplies the Railway build packages, start command, and +`/v1/health` probe. A production deployment must place an authenticated, +rate-limited TLS gateway in front of this process when the output is used by +untrusted clients or security-sensitive systems. -This API uses hardware-level quantum mechanical processes to generate true random numbers. The implementation: +The service starts only after the native engine initializes. Unless disabled, +it also requires the startup CHSH simulation check to exceed the classical +bound. -- Leverages quantum mechanical noise as a source of entropy -- Provides cryptographically secure random numbers -- Continuously monitors entropy levels -- Implements various post-processing techniques to ensure uniform distribution +## Compatibility -### Performance and Security +The original v1 endpoint paths remain available. Responses now include more +provenance and encoding fields, while `uint64` values remain decimal strings. +The old native-only `entangleStates`, `measureState`, seed, and manual reseed +methods were not part of the HTTP contract and have been removed with the +legacy engine. -- High-performance C implementation with Node.js bindings -- Continuous entropy monitoring and quality assurance -- Input validation and error handling -- Rate limiting and security best practices +## Verification -## Error Handling - -The API uses standard HTTP status codes and returns detailed error messages in JSON format: - -```json -{ - "error": "Error description" -} +```sh +npm test ``` -Common status codes: -- 200: Successful request -- 400: Invalid input parameters -- 500: Internal server error +The suite checks: -## Contributing +- full-width v3 byte and uint64 output; +- CHSH violation by the simulated quantum-state model; +- unit-interval floating-point output; +- full-span signed and unsigned range safety; +- exact probability boundary behavior; +- API validation, encodings, provenance, and OpenAPI publication. -Contributions are welcome! Please feel free to submit a Pull Request. +## License and citation -## Citation +This API and the vendored `quantum_rng` source are available under the MIT +License. The vendored copy retains its upstream license and exact revision +record. -If you use this software in your research, please cite it as: +If the engine is used in research, cite the upstream project: ```bibtex @software{quantum_rng, - title = {Semi-Classical Quantum Random Number Generator With Examples}, + title = {Quantum RNG}, author = {tsotchke}, - year = {2024}, - url = {https://github.com/tsotchke/quantum_rng} + year = {2026}, + url = {https://github.com/tsotchke/quantum_rng} } ``` diff --git a/binding.gyp b/binding.gyp index 24b6fc5..2fb3ec8 100644 --- a/binding.gyp +++ b/binding.gyp @@ -1,37 +1,64 @@ { - "targets": [{ - "target_name": "quantum_rng", - "sources": [ - "src/binding.cc", - "src/quantum_rng/quantum_rng.c" - ], - "include_dirs": [ - "=18.0.0" + "node": ">=20.17.0" } }, "node_modules/@apidevtools/json-schema-ref-parser": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.1.2.tgz", - "integrity": "sha512-r1w81DpR+KyRWd3f+rk6TNqMgedmAxZP5v5KWlXQWlgMUUtyEJch0DKEci1SorPMiSeM8XPl7MZ3miJ60JIpQg==", + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-14.0.1.tgz", + "integrity": "sha512-Oc96zvmxx1fqoSEdUmfmvvb59/KDOnUoJ7s2t7bISyAn0XEz57LCCw8k2Y4Pf3mwKaZLMciESALORLgfe2frCw==", + "license": "MIT", "dependencies": { - "@jsdevtools/ono": "^7.1.3", - "@types/json-schema": "^7.0.6", - "call-me-maybe": "^1.0.1", + "@types/json-schema": "^7.0.15", "js-yaml": "^4.1.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/philsturgeon" } }, "node_modules/@apidevtools/openapi-schemas": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@apidevtools/openapi-schemas/-/openapi-schemas-2.1.0.tgz", "integrity": "sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==", + "license": "MIT", "engines": { "node": ">=10" } @@ -42,57 +48,45 @@ "node_modules/@apidevtools/swagger-methods": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/@apidevtools/swagger-methods/-/swagger-methods-3.0.2.tgz", - "integrity": "sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==" + "integrity": "sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==", + "license": "MIT" }, "node_modules/@apidevtools/swagger-parser": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/@apidevtools/swagger-parser/-/swagger-parser-10.0.3.tgz", - "integrity": "sha512-sNiLY51vZOmSPFZA5TF35KZ2HbgYklQnTSDnkghamzLb3EkNtcQnrBQEj5AOCxHpTtXpqMCRM1CrmV2rG6nw4g==", + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/@apidevtools/swagger-parser/-/swagger-parser-12.1.0.tgz", + "integrity": "sha512-e5mJoswsnAX0jG+J09xHFYQXb/bUc5S3pLpMxUuRUA2H8T2kni3yEoyz2R3Dltw5f4A6j6rPNMpWTK+iVDFlng==", + "license": "MIT", "dependencies": { - "@apidevtools/json-schema-ref-parser": "^9.0.6", - "@apidevtools/openapi-schemas": "^2.0.4", + "@apidevtools/json-schema-ref-parser": "14.0.1", + "@apidevtools/openapi-schemas": "^2.1.0", "@apidevtools/swagger-methods": "^3.0.2", - "@jsdevtools/ono": "^7.1.3", - "call-me-maybe": "^1.0.1", - "z-schema": "^5.0.1" + "ajv": "^8.17.1", + "ajv-draft-04": "^1.0.0", + "call-me-maybe": "^1.0.2" }, "peerDependencies": { "openapi-types": ">=7" } }, - "node_modules/@gar/promisify": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", - "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==" - }, - "node_modules/@jsdevtools/ono": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", - "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==" - }, - "node_modules/@npmcli/fs": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-2.1.2.tgz", - "integrity": "sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==", - "dependencies": { - "@gar/promisify": "^1.1.3", - "semver": "^7.3.5" - }, + "node_modules/@isaacs/cliui": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-9.0.0.tgz", + "integrity": "sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==", + "license": "BlueOak-1.0.0", "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=18" } }, - "node_modules/@npmcli/move-file": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-2.0.1.tgz", - "integrity": "sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==", - "deprecated": "This functionality has been moved to @npmcli/fs", + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "license": "ISC", "dependencies": { - "mkdirp": "^1.0.4", - "rimraf": "^3.0.2" + "minipass": "^7.0.4" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@scarf/scarf": { @@ -101,242 +95,143 @@ "integrity": "sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==", "hasInstallScript": true }, - "node_modules/@tootallnate/once": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", - "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", - "engines": { - "node": ">= 10" - } - }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "license": "MIT" }, "node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-4.0.0.tgz", + "integrity": "sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA==", + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } }, "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" }, "engines": { "node": ">= 0.6" } }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "license": "MIT", + "peer": true, "dependencies": { - "debug": "4" + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" }, - "engines": { - "node": ">= 6.0.0" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/agent-base/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" + "node_modules/ajv-draft-04": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", + "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", + "license": "MIT", + "peerDependencies": { + "ajv": "^8.5.0" }, "peerDependenciesMeta": { - "supports-color": { + "ajv": { "optional": true } } }, - "node_modules/agent-base/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" }, - "node_modules/agentkeepalive": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz", - "integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==", - "dependencies": { - "humanize-ms": "^1.2.1" - }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "license": "MIT", "engines": { - "node": ">= 8.0.0" + "node": "18 || 20 || >=22" } }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "node_modules/body-parser": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz", + "integrity": "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==", + "license": "MIT", "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" + "bytes": "^3.1.2", + "content-type": "^2.0.0", + "debug": "^4.4.3", + "http-errors": "^2.0.1", + "iconv-lite": "^0.7.2", + "on-finished": "^2.4.1", + "qs": "^6.15.2", + "raw-body": "^3.0.2", + "type-is": "^2.1.0" }, "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "engines": { - "node": ">=8" + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/aproba": { + "node_modules/body-parser/node_modules/content-type": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", - "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" - }, - "node_modules/are-we-there-yet": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", - "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", - "deprecated": "This package is no longer supported.", - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" - }, + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", + "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", + "license": "MIT", "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "node_modules/body-parser": { - "version": "1.20.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", - "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.13.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" + "node": ">=18" }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz", + "integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==", + "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" } }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", "engines": { "node": ">= 0.8" } }, - "node_modules/cacache": { - "version": "16.1.3", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-16.1.3.tgz", - "integrity": "sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==", - "dependencies": { - "@npmcli/fs": "^2.1.0", - "@npmcli/move-file": "^2.0.0", - "chownr": "^2.0.0", - "fs-minipass": "^2.1.0", - "glob": "^8.0.1", - "infer-owner": "^1.0.4", - "lru-cache": "^7.7.1", - "minipass": "^3.1.6", - "minipass-collect": "^1.0.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "mkdirp": "^1.0.4", - "p-map": "^4.0.0", - "promise-inflight": "^1.0.1", - "rimraf": "^3.0.2", - "ssri": "^9.0.0", - "tar": "^6.1.11", - "unique-filename": "^2.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/cacache/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/cacache/node_modules/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/cacache/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/call-bind-apply-helpers": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", - "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" @@ -346,12 +241,13 @@ } }, "node_modules/call-bound": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", - "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "get-intrinsic": "^1.2.6" + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" }, "engines": { "node": ">= 0.4" @@ -363,30 +259,16 @@ "node_modules/call-me-maybe": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz", - "integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==" + "integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==", + "license": "MIT" }, "node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "engines": { - "node": ">=10" - } - }, - "node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "license": "BlueOak-1.0.0", "engines": { - "node": ">=6" - } - }, - "node_modules/color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "bin": { - "color-support": "bin.js" + "node": ">=18" } }, "node_modules/commander": { @@ -397,31 +279,24 @@ "node": ">= 6" } }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" - }, - "node_modules/console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" - }, "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dependencies": { - "safe-buffer": "5.2.1" - }, + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", + "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==", + "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/content-type": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -435,40 +310,75 @@ } }, "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } }, - "node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", "dependencies": { - "ms": "2.0.0" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" } }, - "node_modules/delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" + "node_modules/cross-spawn/node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", "engines": { "node": ">= 0.8" } }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -484,6 +394,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", @@ -496,42 +407,18 @@ "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" }, "node_modules/encodeurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", "engines": { "node": ">= 0.8" } }, - "node_modules/encoding": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", - "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", - "optional": true, - "dependencies": { - "iconv-lite": "^0.6.2" - } - }, - "node_modules/encoding/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "optional": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/env-paths": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", @@ -540,15 +427,11 @@ "node": ">=6" } }, - "node_modules/err-code": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", - "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==" - }, "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", "engines": { "node": ">= 0.4" } @@ -557,14 +440,16 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", "engines": { "node": ">= 0.4" } }, "node_modules/es-object-atoms": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", - "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "license": "MIT", "dependencies": { "es-errors": "^1.3.0" }, @@ -575,7 +460,8 @@ "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" }, "node_modules/esutils": { "version": "2.0.3", @@ -589,6 +475,7 @@ "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -599,65 +486,123 @@ "integrity": "sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==" }, "node_modules/express": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", - "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.3", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.7.1", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.3.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.3", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.12", - "proxy-addr": "~2.0.7", - "qs": "6.13.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.19.0", - "serve-static": "1.16.2", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "license": "MIT", + "peer": true, + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/express" } }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.3.tgz", + "integrity": "sha512-i70LwGWUduXqzicKXWshooq+sWL1K3WUU5rKZNG/0i3a1OSoX3HqhH5WbWwTmqWfor4urUakGPiRQcleRZTwOg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, "node_modules/finalhandler": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", - "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "license": "MIT", "dependencies": { - "debug": "2.6.9", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" }, "engines": { - "node": ">= 0.8" + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/forwarded": { @@ -669,71 +614,39 @@ } }, "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "dependencies": { - "minipass": "^3.0.0" - }, + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", "engines": { - "node": ">= 8" + "node": ">= 0.8" } }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" - }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/gauge": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", - "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", - "deprecated": "This package is no longer supported.", - "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.3", - "console-control-strings": "^1.1.0", - "has-unicode": "^2.0.1", - "signal-exit": "^3.0.7", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.5" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, "node_modules/get-intrinsic": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.6.tgz", - "integrity": "sha512-qxsEs+9A+u85HhllWJJFicJfPDhRmjzoYdl64aMWW9yRIJmSyxdn8IEkuIM530/7T+lv0TIHd8L6Q/ra0tEoeA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "dunder-proto": "^1.0.0", + "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", + "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", + "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", - "math-intrinsics": "^1.0.0" + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -742,21 +655,38 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/glob": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz", + "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "license": "BlueOak-1.0.0", + "dependencies": { + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.1.1", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" }, "engines": { - "node": "*" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -766,6 +696,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -782,6 +713,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -789,15 +721,11 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" - }, "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "license": "MIT", "dependencies": { "function-bind": "^1.1.2" }, @@ -805,159 +733,47 @@ "node": ">= 0.4" } }, - "node_modules/http-cache-semantics": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", - "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==" - }, "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" }, "engines": { "node": ">= 0.8" - } - }, - "node_modules/http-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", - "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", - "dependencies": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/http-proxy-agent/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/http-proxy-agent/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dependencies": { - "agent-base": "6", - "debug": "4" }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/https-proxy-agent/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/https-proxy-agent/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/humanize-ms": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", - "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", - "dependencies": { - "ms": "^2.0.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz", + "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==", + "license": "MIT", "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" + "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { "node": ">=0.10.0" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/infer-owner": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", - "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==" - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/ip-address": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", - "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", - "dependencies": { - "jsbn": "1.1.0", - "sprintf-js": "^1.1.3" - }, - "engines": { - "node": ">= 12" - } + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" }, "node_modules/ipaddr.js": { "version": "1.9.1", @@ -967,28 +783,51 @@ "node": ">= 0.10" } }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz", + "integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==", + "license": "BlueOak-1.0.0", "engines": { - "node": ">=8" + "node": ">=20" } }, - "node_modules/is-lambda": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", - "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==" - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + "node_modules/jackspeak": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.2.3.tgz", + "integrity": "sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^9.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", + "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -996,20 +835,11 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/jsbn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", - "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==" - }, - "node_modules/lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==" - }, - "node_modules/lodash.isequal": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", - "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" }, "node_modules/lodash.mergewith": { "version": "4.6.2", @@ -1017,281 +847,173 @@ "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==" }, "node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "version": "11.5.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", + "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", + "license": "BlueOak-1.0.0", "engines": { - "node": ">=12" - } - }, - "node_modules/make-fetch-happen": { - "version": "10.2.1", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz", - "integrity": "sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==", - "dependencies": { - "agentkeepalive": "^4.2.1", - "cacache": "^16.1.0", - "http-cache-semantics": "^4.1.0", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", - "is-lambda": "^1.0.1", - "lru-cache": "^7.7.1", - "minipass": "^3.1.6", - "minipass-collect": "^1.0.2", - "minipass-fetch": "^2.0.3", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.3", - "promise-retry": "^2.0.1", - "socks-proxy-agent": "^7.0.0", - "ssri": "^9.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "20 || >=22" } }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", "engines": { "node": ">= 0.4" } }, "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">= 0.8" } }, "node_modules/merge-descriptors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "bin": { - "mime": "cli.js" + "node": ">=18" }, - "engines": { - "node": ">=4" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-collect": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", - "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minipass-fetch": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-2.1.2.tgz", - "integrity": "sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", "dependencies": { - "minipass": "^3.1.6", - "minipass-sized": "^1.0.3", - "minizlib": "^2.1.2" + "mime-db": "^1.54.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=18" }, - "optionalDependencies": { - "encoding": "^0.1.13" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/minipass-flush": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", - "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "license": "BlueOak-1.0.0", "dependencies": { - "minipass": "^3.0.0" + "brace-expansion": "^5.0.5" }, "engines": { - "node": ">= 8" - } - }, - "node_modules/minipass-pipeline": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", - "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", - "dependencies": { - "minipass": "^3.0.0" + "node": "18 || 20 || >=22" }, - "engines": { - "node": ">=8" + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/minipass-sized": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", - "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", - "dependencies": { - "minipass": "^3.0.0" - }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "license": "BlueOak-1.0.0", "engines": { - "node": ">=8" + "node": ">=16 || 14 >=14.17" } }, "node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", + "license": "MIT", "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" + "minipass": "^7.1.2" }, "engines": { - "node": ">= 8" - } - }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" + "node": ">= 18" } }, "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" }, "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/node-addon-api": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", - "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==" + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.9.0.tgz", + "integrity": "sha512-ekZMeaaIzSQTSpr7X2X3iJM7lTzgnx8ahAG9pJfT/7+14mlEM8ZYQ9cgCDvSSRbReFK0oHli3WrZdCiRsgAT9Q==", + "license": "MIT", + "engines": { + "node": "^18 || ^20 || >= 21" + } }, "node_modules/node-gyp": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-9.4.1.tgz", - "integrity": "sha512-OQkWKbjQKbGkMf/xqI1jjy3oCTgMKJac58G2+bjZb3fza6gW2YrCSdMQYaoTb70crvE//Gngr4f0AgVHmqHvBQ==", + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-12.4.0.tgz", + "integrity": "sha512-OMcPNvqTCFUnNaBlmdgq+lfNqY7gTiSmNRDjY3uAXRyudeKZEZxu3CLtjMQrx4zZxCX2b/mpNqTtwuCJgXhHkw==", + "license": "MIT", "dependencies": { "env-paths": "^2.2.0", "exponential-backoff": "^3.1.1", - "glob": "^7.1.4", "graceful-fs": "^4.2.6", - "make-fetch-happen": "^10.0.3", - "nopt": "^6.0.0", - "npmlog": "^6.0.0", - "rimraf": "^3.0.2", + "nopt": "^9.0.0", + "proc-log": "^6.0.0", "semver": "^7.3.5", - "tar": "^6.1.2", - "which": "^2.0.2" + "tar": "^7.5.4", + "tinyglobby": "^0.2.12", + "undici": "^6.25.0", + "which": "^6.0.0" }, "bin": { "node-gyp": "bin/node-gyp.js" }, "engines": { - "node": "^12.13 || ^14.13 || >=16" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/nopt": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-6.0.0.tgz", - "integrity": "sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-9.0.0.tgz", + "integrity": "sha512-Zhq3a+yFKrYwSBluL4H9XP3m3y5uvQkB/09CwDruCiRmR/UJYnn9W4R48ry0uGC70aeTPKLynBtscP9efFFcPw==", + "license": "ISC", "dependencies": { - "abbrev": "^1.0.0" + "abbrev": "^4.0.0" }, "bin": { "nopt": "bin/nopt.js" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npmlog": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", - "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", - "deprecated": "This package is no longer supported.", - "dependencies": { - "are-we-there-yet": "^3.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^4.0.3", - "set-blocking": "^2.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/object-inspect": { - "version": "1.13.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", - "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -1303,6 +1025,7 @@ "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", "dependencies": { "ee-first": "1.1.1" }, @@ -1322,58 +1045,79 @@ "version": "12.1.3", "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz", "integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==", + "license": "MIT", "peer": true }, - "node_modules/p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", "engines": { "node": ">= 0.8" } }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/path-to-regexp": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", - "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==" + "node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, - "node_modules/promise-inflight": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==" + "node_modules/path-to-regexp": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", + "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } }, - "node_modules/promise-retry": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", - "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", - "dependencies": { - "err-code": "^2.0.2", - "retry": "^0.12.0" + "node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/proc-log": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", + "license": "ISC", "engines": { - "node": ">=10" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/proxy-addr": { @@ -1389,11 +1133,13 @@ } }, "node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "version": "6.15.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz", + "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==", + "license": "BSD-3-Clause", "dependencies": { - "side-channel": "^1.0.6" + "es-define-property": "^1.0.1", + "side-channel": "^1.1.1" }, "engines": { "node": ">=0.6" @@ -1403,91 +1149,69 @@ } }, "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.3.0.tgz", + "integrity": "sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==", + "license": "MIT", "engines": { "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" }, - "engines": { - "node": ">= 0.8" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "license": "MIT", "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" }, "engines": { - "node": ">= 6" + "node": ">= 0.10" } }, - "node_modules/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", "engines": { - "node": ">= 4" + "node": ">=0.10.0" } }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "engines": { + "node": ">= 18" } }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" }, "node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -1496,73 +1220,86 @@ } }, "node_modules/send": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", - "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "license": "MIT", "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" }, "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "engines": { - "node": ">= 0.8" + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, "node_modules/serve-static": { - "version": "1.16.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", - "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "license": "MIT", "dependencies": { - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.19.0" + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" }, "engines": { - "node": ">= 0.8.0" + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" - }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } }, "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", + "license": "MIT", "dependencies": { "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" }, @@ -1574,12 +1311,13 @@ } }, "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "license": "MIT", "dependencies": { "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" + "object-inspect": "^1.13.4" }, "engines": { "node": ">= 0.4" @@ -1592,6 +1330,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -1609,6 +1348,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -1624,170 +1364,44 @@ } }, "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" - }, - "node_modules/smart-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", - "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socks": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", - "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", - "dependencies": { - "ip-address": "^9.0.5", - "smart-buffer": "^4.2.0" - }, - "engines": { - "node": ">= 10.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socks-proxy-agent": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz", - "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==", - "dependencies": { - "agent-base": "^6.0.2", - "debug": "^4.3.3", - "socks": "^2.6.2" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/socks-proxy-agent/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "dependencies": { - "ms": "^2.1.3" - }, + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", "engines": { - "node": ">=6.0" + "node": ">=14" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/socks-proxy-agent/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/sprintf-js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", - "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==" - }, - "node_modules/ssri": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-9.0.1.tgz", - "integrity": "sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==", - "dependencies": { - "minipass": "^3.1.1" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", "engines": { "node": ">= 0.8" } }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/swagger-jsdoc": { - "version": "6.2.8", - "resolved": "https://registry.npmjs.org/swagger-jsdoc/-/swagger-jsdoc-6.2.8.tgz", - "integrity": "sha512-VPvil1+JRpmJ55CgAtn8DIcpBs0bL5L3q5bVQvF4tAW/k/9JYSj7dCpaYCAv5rufe0vcCbBRQXGvzpkWjvLklQ==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/swagger-jsdoc/-/swagger-jsdoc-6.3.0.tgz", + "integrity": "sha512-I+iQjVGV3t28pOkQUJv2MncthvOtkEactOn8R76SvSYhxgtIn7FoqfDHwQaN+GBnQdXQLrhgDXseKitmJcHMsA==", + "license": "MIT", "dependencies": { + "@apidevtools/swagger-parser": "^12.1.0", "commander": "6.2.0", "doctrine": "3.0.0", - "glob": "7.1.6", + "glob": "11.1.0", "lodash.mergewith": "^4.6.2", - "swagger-parser": "^10.0.3", "yaml": "2.0.0-1" }, "bin": { "swagger-jsdoc": "bin/swagger-jsdoc.js" }, "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/swagger-jsdoc/node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/swagger-parser": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/swagger-parser/-/swagger-parser-10.0.3.tgz", - "integrity": "sha512-nF7oMeL4KypldrQhac8RyHerJeGPD1p2xDh900GPvc+Nk7nWP6jX2FcC7WmkinMoAmoO774+AFXcWsW8gMWEIg==", - "dependencies": { - "@apidevtools/swagger-parser": "10.0.3" - }, - "engines": { - "node": ">=10" + "node": ">=20.0.0" } }, "node_modules/swagger-ui-dist": { @@ -1813,100 +1427,95 @@ } }, "node_modules/tar": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", - "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "version": "7.5.20", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.20.tgz", + "integrity": "sha512-9FcyK4PA6+WbzlTM9WhQm6vB5W7cP7dUiPsv1g7YDwEQnQ1CGpK3MGlKk/ITVWMk05kHZuBhmVhiv8LZoy/PFQ==", + "license": "BlueOak-1.0.0", "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" }, "engines": { - "node": ">=10" + "node": ">=18" } }, - "node_modules/tar/node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, "engines": { - "node": ">=8" + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" } }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", "engines": { "node": ">=0.6" } }, "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", + "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==", + "license": "MIT", "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" + "content-type": "^2.0.0", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" }, "engines": { - "node": ">= 0.6" + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/unique-filename": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-2.0.1.tgz", - "integrity": "sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==", - "dependencies": { - "unique-slug": "^3.0.0" - }, + "node_modules/type-is/node_modules/content-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", + "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", + "license": "MIT", "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/unique-slug": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-3.0.0.tgz", - "integrity": "sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w==", - "dependencies": { - "imurmurhash": "^0.1.4" - }, + "node_modules/undici": { + "version": "6.27.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.27.0.tgz", + "integrity": "sha512-YmfV3YnEDzXRC5lZ2jWtWWHKGUm1zIt8AhesR1tens+HTNv+YZlN/dp6G727LOvMJ8xjP9Be7Y2Sdr96LDm+pg==", + "license": "MIT", "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=18.17" } }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", "engines": { "node": ">= 0.8" } }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/validator": { - "version": "13.12.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.12.0.tgz", - "integrity": "sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==", - "engines": { - "node": ">= 0.10" - } - }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -1916,25 +1525,18 @@ } }, "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/which/-/which-6.0.1.tgz", + "integrity": "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==", + "license": "ISC", "dependencies": { - "isexe": "^2.0.0" + "isexe": "^4.0.0" }, "bin": { - "node-which": "bin/node-which" + "node-which": "bin/which.js" }, "engines": { - "node": ">= 8" - } - }, - "node_modules/wide-align": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", - "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "dependencies": { - "string-width": "^1.0.2 || 2 || 3 || 4" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/wrappy": { @@ -1943,9 +1545,13 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } }, "node_modules/yaml": { "version": "2.0.0-1", @@ -1954,34 +1560,6 @@ "engines": { "node": ">= 6" } - }, - "node_modules/z-schema": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.5.tgz", - "integrity": "sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==", - "dependencies": { - "lodash.get": "^4.4.2", - "lodash.isequal": "^4.5.0", - "validator": "^13.7.0" - }, - "bin": { - "z-schema": "bin/z-schema" - }, - "engines": { - "node": ">=8.0.0" - }, - "optionalDependencies": { - "commander": "^9.4.1" - } - }, - "node_modules/z-schema/node_modules/commander": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", - "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", - "optional": true, - "engines": { - "node": "^12.20.0 || >=14" - } } } } diff --git a/package.json b/package.json index 076f937..0d2532a 100644 --- a/package.json +++ b/package.json @@ -1,26 +1,42 @@ { "name": "quantum-rng-api", - "version": "1.0.0", + "version": "2.0.0", + "private": true, + "description": "REST API for the Bell-verified quantum_rng v3 simulation engine", "main": "server.js", "scripts": { + "build": "node-gyp rebuild", + "clean": "node-gyp clean", "start": "node server.js", - "build": "node-gyp configure && node-gyp rebuild", - "postinstall": "npm run build", - "prestart": "npm run build" + "test": "npm run build && node --test", + "postinstall": "npm run build" }, "gypfile": true, - "keywords": [], - "author": "", - "license": "ISC", - "description": "Quantum RNG REST API", + "keywords": [ + "random-number-generator", + "quantum-simulation", + "entropy", + "node-api", + "rest-api" + ], + "author": "Tsotchke Corporation", + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/Tsotchke-Corporation/Quantum-RNG-API.git" + }, + "bugs": { + "url": "https://github.com/Tsotchke-Corporation/Quantum-RNG-API/issues" + }, + "homepage": "https://github.com/Tsotchke-Corporation/Quantum-RNG-API#readme", "dependencies": { - "express": "^4.18.2", - "node-addon-api": "^6.1.0", - "node-gyp": "^9.4.0", - "swagger-jsdoc": "^6.2.8", + "express": "^5.2.1", + "node-addon-api": "^8.9.0", + "node-gyp": "^12.4.0", + "swagger-jsdoc": "^6.3.0", "swagger-ui-express": "^5.0.1" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.17.0" } } diff --git a/server.js b/server.js index 6c5b691..dcc7b96 100644 --- a/server.js +++ b/server.js @@ -1,403 +1,514 @@ +'use strict'; + const express = require('express'); -const swaggerUi = require('swagger-ui-express'); const swaggerJsdoc = require('swagger-jsdoc'); +const swaggerUi = require('swagger-ui-express'); +const packageMetadata = require('./package.json'); + +const { QuantumRNG } = require('./build/Release/quantum_rng.node'); + +const API_VERSION = packageMetadata.version; +const MAX_BYTES_PER_REQUEST = 4096; +const MAX_CHOICE_ITEMS = 100000; +const UINT64_MAX = (1n << 64n) - 1n; +const PORT = parsePort(process.env.PORT || '3000'); +const HOST = process.env.HOST || '0.0.0.0'; +const QRNG_MODE = parseMode(process.env.QRNG_MODE || 'direct'); +const VERIFY_ON_START = process.env.QRNG_VERIFY_ON_START !== '0'; +const VERIFY_MEASUREMENTS = parseVerificationMeasurements( + process.env.QRNG_VERIFY_MEASUREMENTS || '4000' +); + +class ClientError extends Error { + constructor(message, status = 400) { + super(message); + this.name = 'ClientError'; + this.status = status; + } +} + +function parsePort(value) { + const parsed = Number(value); + if (!Number.isInteger(parsed) || parsed < 0 || parsed > 65535) { + throw new Error('PORT must be an integer between 0 and 65535'); + } + return parsed; +} + +function parseMode(value) { + if (!['direct', 'grover', 'bell-verified'].includes(value)) { + throw new Error('QRNG_MODE must be direct, grover, or bell-verified'); + } + return value; +} + +function parseVerificationMeasurements(value) { + const parsed = Number(value); + if (!Number.isInteger(parsed) || parsed < 1000 || parsed > 100000) { + throw new Error( + 'QRNG_VERIFY_MEASUREMENTS must be an integer between 1000 and 100000' + ); + } + return parsed; +} + +function parseInteger(value, name, min, max) { + if (typeof value !== 'string' || !/^-?\d+$/.test(value)) { + throw new ClientError(`${name} must be an integer`); + } + const parsed = Number(value); + if (!Number.isSafeInteger(parsed) || parsed < min || parsed > max) { + throw new ClientError(`${name} must be between ${min} and ${max}`); + } + return parsed; +} -let QuantumRNG; +function parseUInt64(value, name) { + if (typeof value !== 'string' || !/^\d+$/.test(value)) { + throw new ClientError(`${name} must be an unsigned decimal integer`); + } + const parsed = BigInt(value); + if (parsed > UINT64_MAX) { + throw new ClientError(`${name} must be at most 18446744073709551615`); + } + return parsed; +} + +function parseProbability(value) { + if (value === undefined) return 0.5; + if (typeof value !== 'string' || value.trim() === '') { + throw new ClientError('probability must be a number between 0 and 1'); + } + const parsed = Number(value); + if (!Number.isFinite(parsed) || parsed < 0 || parsed > 1) { + throw new ClientError('probability must be a number between 0 and 1'); + } + return parsed; +} + +function serializeStats(stats) { + return Object.fromEntries( + Object.entries(stats).map(([key, value]) => [ + key, + typeof value === 'bigint' ? value.toString() : value, + ]) + ); +} + +function engineMetadata(rngInstance) { + return { + name: 'quantum_rng_v3', + release: QuantumRNG.getVersion(), + engineVersion: QuantumRNG.getEngineVersion(), + revision: QuantumRNG.getRevision(), + mode: rngInstance.getMode(), + }; +} + +function runStartupVerification(rngInstance) { + if (!VERIFY_ON_START) { + return { + performed: false, + scope: 'quantum-state simulation model', + reason: 'disabled by QRNG_VERIFY_ON_START=0', + }; + } + + const result = rngInstance.verifyQuantum(VERIFY_MEASUREMENTS); + if (!result.violatesClassical) { + throw new Error( + `QRNG v3 startup CHSH verification failed: S=${result.chsh}` + ); + } + + return { + performed: true, + scope: 'quantum-state simulation model', + ...result, + }; +} + +const rng = new QuantumRNG({ + mode: QRNG_MODE, + bellMonitoring: QRNG_MODE === 'bell-verified', +}); +const startupVerification = runStartupVerification(rng); -// Swagger definition const swaggerOptions = { definition: { - openapi: '3.0.0', + openapi: '3.0.3', info: { - title: 'Quantum RNG API', - version: '1.0.0', - description: 'A REST API for generating true random numbers using quantum mechanics', + title: 'Tsotchke Quantum RNG API', + version: API_VERSION, + description: + 'HTTP interface to the quantum_rng v3 state-vector simulation engine. ' + + 'Output is conditioned with health-tested operating-system and CPU entropy. ' + + 'CHSH verification validates the simulation model; it is not a claim of physical quantum hardware.', license: { - name: 'ISC', - url: 'https://opensource.org/licenses/ISC', + name: 'MIT', + url: 'https://opensource.org/license/mit', }, }, servers: [ { url: 'https://api.tsotchke.net', - description: 'Production server', + description: 'Production', + }, + { + url: 'http://localhost:3000', + description: 'Local development', }, ], + tags: [ + { name: 'System', description: 'Runtime status and provenance' }, + { name: 'Random', description: 'Random data generation' }, + ], }, - apis: ['./server.js'], // files containing annotations + apis: ['./server.js'], }; -const swaggerDocs = swaggerJsdoc(swaggerOptions); - -try { - console.log('Current directory:', process.cwd()); - console.log('Attempting to load quantum_rng module...'); - const quantum_rng = require('./build/Release/quantum_rng.node'); - console.log('Module loaded:', quantum_rng); - QuantumRNG = quantum_rng.QuantumRNG; - console.log('QuantumRNG constructor:', QuantumRNG); -} catch (err) { - console.error('Failed to load quantum_rng module:', err); - process.exit(1); -} +const swaggerDocument = swaggerJsdoc(swaggerOptions); -const app = express(); -app.use(express.json()); +function createApp({ rngInstance = rng, verification = startupVerification } = {}) { + const app = express(); + app.disable('x-powered-by'); + app.use(express.json({ limit: '256kb' })); -// Initialize quantum RNG -console.log('Initializing QuantumRNG instance...'); -const rng = new QuantumRNG(); -console.log('QuantumRNG instance created successfully'); + app.get('/', (req, res) => { + res.json({ + name: 'Tsotchke Quantum RNG API', + apiVersion: API_VERSION, + engine: engineMetadata(rngInstance), + documentation: '/v1/docs', + source: 'https://github.com/Tsotchke-Corporation/Quantum-RNG-API', + }); + }); -// Add boolean and choice functions to rng object -rng.boolean = function(probability = 0.5) { - return this.getDouble() < probability; -}; + const v1 = express.Router(); + v1.get('/openapi.json', (req, res) => res.json(swaggerDocument)); + v1.use('/docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument)); -rng.choice = function(array) { - if (!Array.isArray(array) || array.length === 0) { - throw new Error('Array must be non-empty'); - } - const index = Math.floor(this.getDouble() * array.length); - return array[index]; -}; + /** + * @swagger + * /v1/health: + * get: + * summary: Get runtime health, provenance, verification, and counters + * tags: [System] + * responses: + * 200: + * description: The generator is initialized and operational + */ + v1.get('/health', (req, res) => { + res.json({ + status: 'ok', + apiVersion: API_VERSION, + engine: engineMetadata(rngInstance), + verification, + stats: serializeStats(rngInstance.getStats()), + }); + }); -// Welcome message -app.get('/', (req, res) => { - res.json({ - message: 'Welcome to the Quantum RNG API', - version: '1.0.0', - documentation: 'https://api.tsotchke.net/v1/docs' + /** + * @swagger + * /v1/qrng/stats: + * get: + * summary: Get QRNG v3 generation and simulation counters + * tags: [System] + * responses: + * 200: + * description: Current engine counters + */ + v1.get('/qrng/stats', (req, res) => { + res.json({ + engine: engineMetadata(rngInstance), + stats: serializeStats(rngInstance.getStats()), + }); }); -}); -// API v1 router -const v1Router = express.Router(); - -// Swagger UI -v1Router.use('/docs', swaggerUi.serve, swaggerUi.setup(swaggerDocs)); - -/** - * @swagger - * /v1/health: - * get: - * summary: Health check endpoint - * description: Returns the API status, version, and current entropy estimate - * tags: [System] - * responses: - * 200: - * description: System health information - * content: - * application/json: - * schema: - * type: object - * properties: - * status: - * type: string - * example: ok - * version: - * type: string - * example: 1.0.0 - * entropy: - * type: number - * example: 8.2 - */ -v1Router.get('/health', (req, res) => { - res.json({ - status: 'ok', - version: QuantumRNG.getVersion(), - entropy: rng.getEntropyEstimate() + /** + * @swagger + * /v1/qrng/verification: + * get: + * summary: Get the startup CHSH simulation-verification result + * tags: [System] + * responses: + * 200: + * description: Startup verification result and its scope + */ + v1.get('/qrng/verification', (req, res) => { + res.json(verification); }); -}); -/** - * @swagger - * /v1/qrng/bytes/{count}: - * get: - * summary: Get random bytes - * description: Returns the specified number of random bytes - * tags: [Random] - * parameters: - * - in: path - * name: count - * required: true - * schema: - * type: integer - * minimum: 1 - * maximum: 1024 - * description: Number of random bytes to generate - * responses: - * 200: - * description: Random bytes in hex and raw format - * content: - * application/json: - * schema: - * type: object - * properties: - * bytes: - * type: string - * description: Hex string representation of random bytes - * raw: - * type: array - * items: - * type: integer - * 400: - * description: Invalid count parameter - * 500: - * description: Server error - */ -v1Router.get('/qrng/bytes/:count', (req, res) => { - try { - const count = parseInt(req.params.count); - if (isNaN(count) || count <= 0 || count > 1024) { - return res.status(400).json({ - error: 'Count must be a number between 1 and 1024' + /** + * @swagger + * /v1/qrng/bytes/{count}: + * get: + * summary: Generate random bytes + * tags: [Random] + * parameters: + * - in: path + * name: count + * required: true + * schema: + * type: integer + * minimum: 1 + * maximum: 4096 + * responses: + * 200: + * description: Hex, Base64, and byte-array encodings of one draw + * 400: + * description: Invalid count + */ + v1.get('/qrng/bytes/:count', (req, res, next) => { + try { + const count = parseInteger( + req.params.count, + 'count', + 1, + MAX_BYTES_PER_REQUEST + ); + const bytes = rngInstance.getBytes(count); + res.json({ + length: bytes.length, + bytes: bytes.toString('hex'), + base64: bytes.toString('base64'), + raw: Array.from(bytes), }); + } catch (error) { + next(error); } - - const bytes = rng.getBytes(count); - res.json({ - bytes: bytes.toString('hex'), - raw: Array.from(bytes) - }); - } catch (err) { - res.status(500).json({ error: err.message }); - } -}); + }); -/** - * @swagger - * /v1/qrng/number: - * get: - * summary: Get random number - * description: Returns a random number in either float or uint64 format - * tags: [Random] - * parameters: - * - in: query - * name: type - * schema: - * type: string - * enum: [float, uint64] - * default: float - * description: Type of random number to generate - * responses: - * 200: - * description: Random number - * content: - * application/json: - * schema: - * type: object - * properties: - * value: - * oneOf: - * - type: number - * - type: string - * 400: - * description: Invalid type parameter - * 500: - * description: Server error - */ -v1Router.get('/qrng/number', (req, res) => { - try { - const { type = 'float' } = req.query; - - let result; - switch (type) { - case 'float': - result = rng.getDouble(); - break; - case 'uint64': - // Convert BigInt to string for JSON serialization - result = rng.getUInt64().toString(); - break; - default: - return res.status(400).json({ - error: 'Type must be either "float" or "uint64"' - }); + /** + * @swagger + * /v1/qrng/number: + * get: + * summary: Generate a floating-point or unsigned 64-bit value + * tags: [Random] + * parameters: + * - in: query + * name: type + * schema: + * type: string + * enum: [float, uint64] + * default: float + * responses: + * 200: + * description: Generated value; uint64 values are decimal strings + * 400: + * description: Invalid type + */ + v1.get('/qrng/number', (req, res, next) => { + try { + const type = req.query.type || 'float'; + if (type === 'float') { + return res.json({ type, value: rngInstance.getDouble() }); + } + if (type === 'uint64') { + return res.json({ type, value: rngInstance.getUInt64().toString() }); + } + throw new ClientError('type must be float or uint64'); + } catch (error) { + next(error); } - - res.json({ value: result }); - } catch (err) { - res.status(500).json({ error: err.message }); - } -}); + }); -/** - * @swagger - * /v1/qrng/range: - * get: - * summary: Get random number in range - * description: Returns a random integer within the specified range - * tags: [Random] - * parameters: - * - in: query - * name: min - * required: true - * schema: - * type: integer - * description: Minimum value (inclusive) - * - in: query - * name: max - * required: true - * schema: - * type: integer - * description: Maximum value (inclusive) - * responses: - * 200: - * description: Random number within range - * content: - * application/json: - * schema: - * type: object - * properties: - * value: - * type: integer - * 400: - * description: Invalid parameters - * 500: - * description: Server error - */ -v1Router.get('/qrng/range', (req, res) => { - try { - const min = parseInt(req.query.min); - const max = parseInt(req.query.max); - - if (isNaN(min) || isNaN(max)) { - return res.status(400).json({ - error: 'Min and max must be numbers' - }); + /** + * @swagger + * /v1/qrng/range: + * get: + * summary: Generate an unbiased signed 32-bit integer in an inclusive range + * tags: [Random] + * parameters: + * - in: query + * name: min + * required: true + * schema: { type: integer, minimum: -2147483648, maximum: 2147483647 } + * - in: query + * name: max + * required: true + * schema: { type: integer, minimum: -2147483648, maximum: 2147483647 } + * responses: + * 200: + * description: Generated signed integer + * 400: + * description: Invalid range + */ + v1.get('/qrng/range', (req, res, next) => { + try { + const min = parseInteger( + req.query.min, + 'min', + -2147483648, + 2147483647 + ); + const max = parseInteger( + req.query.max, + 'max', + -2147483648, + 2147483647 + ); + if (min > max) throw new ClientError('min must not exceed max'); + res.json({ min, max, value: rngInstance.getRange32(min, max) }); + } catch (error) { + next(error); } - - if (min > max) { - return res.status(400).json({ - error: 'Min must be less than or equal to max' + }); + + /** + * @swagger + * /v1/qrng/range64: + * get: + * summary: Generate an unbiased unsigned 64-bit integer in an inclusive range + * tags: [Random] + * parameters: + * - in: query + * name: min + * required: true + * schema: { type: string, pattern: '^\\d+$' } + * - in: query + * name: max + * required: true + * schema: { type: string, pattern: '^\\d+$' } + * responses: + * 200: + * description: Generated unsigned integer as a decimal string + * 400: + * description: Invalid range + */ + v1.get('/qrng/range64', (req, res, next) => { + try { + const min = parseUInt64(req.query.min, 'min'); + const max = parseUInt64(req.query.max, 'max'); + if (min > max) throw new ClientError('min must not exceed max'); + res.json({ + min: min.toString(), + max: max.toString(), + value: rngInstance.getRange64(min, max).toString(), }); + } catch (error) { + next(error); } - - const value = rng.getRange32(min, max); - res.json({ value }); - } catch (err) { - res.status(500).json({ error: err.message }); - } -}); + }); -/** - * @swagger - * /v1/qrng/boolean: - * get: - * summary: Get random boolean - * description: Returns a random boolean with specified probability of being true - * tags: [Random] - * parameters: - * - in: query - * name: probability - * schema: - * type: number - * minimum: 0 - * maximum: 1 - * default: 0.5 - * description: Probability of returning true - * responses: - * 200: - * description: Random boolean - * content: - * application/json: - * schema: - * type: object - * properties: - * value: - * type: boolean - * 400: - * description: Invalid probability parameter - * 500: - * description: Server error - */ -v1Router.get('/qrng/boolean', (req, res) => { - try { - const probability = parseFloat(req.query.probability) || 0.5; - - if (probability < 0 || probability > 1) { - return res.status(400).json({ - error: 'Probability must be between 0 and 1' - }); + /** + * @swagger + * /v1/qrng/boolean: + * get: + * summary: Generate a boolean with a requested true probability + * tags: [Random] + * parameters: + * - in: query + * name: probability + * schema: { type: number, minimum: 0, maximum: 1, default: 0.5 } + * responses: + * 200: + * description: Generated boolean + * 400: + * description: Invalid probability + */ + v1.get('/qrng/boolean', (req, res, next) => { + try { + const probability = parseProbability(req.query.probability); + const value = rngInstance.getDouble() < probability; + res.json({ probability, value }); + } catch (error) { + next(error); } - - const value = rng.boolean(probability); - res.json({ value }); - } catch (err) { - res.status(500).json({ error: err.message }); - } -}); + }); -/** - * @swagger - * /v1/qrng/choice: - * post: - * summary: Get random array element - * description: Returns a random element from the provided array - * tags: [Random] - * requestBody: - * required: true - * content: - * application/json: - * schema: - * type: object - * required: - * - array - * properties: - * array: - * type: array - * items: - * type: string - * minItems: 1 - * example: ["apple", "banana", "orange"] - * responses: - * 200: - * description: Random element from array - * content: - * application/json: - * schema: - * type: object - * properties: - * value: - * type: any - * 400: - * description: Invalid request body - * 500: - * description: Server error - */ -v1Router.post('/qrng/choice', (req, res) => { - try { - const { array } = req.body; - - if (!Array.isArray(array) || array.length === 0) { - return res.status(400).json({ - error: 'Request body must contain a non-empty array' - }); + /** + * @swagger + * /v1/qrng/choice: + * post: + * summary: Select one element from a non-empty JSON array + * tags: [Random] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: [array] + * properties: + * array: + * type: array + * minItems: 1 + * maxItems: 100000 + * items: {} + * responses: + * 200: + * description: Selected element and its exact unbiased index + * 400: + * description: Invalid request body + */ + v1.post('/qrng/choice', (req, res, next) => { + try { + const { array } = req.body || {}; + if (!Array.isArray(array) || array.length === 0) { + throw new ClientError('array must be non-empty'); + } + if (array.length > MAX_CHOICE_ITEMS) { + throw new ClientError(`array must contain at most ${MAX_CHOICE_ITEMS} items`); + } + const index = Number( + rngInstance.getRange64(0n, BigInt(array.length - 1)) + ); + res.json({ index, value: array[index] }); + } catch (error) { + next(error); } - - const value = rng.choice(array); - res.json({ value }); - } catch (err) { - res.status(500).json({ error: err.message }); - } -}); + }); -// Mount v1 router -app.use('/v1', v1Router); + app.use('/v1', v1); -// Error handler -app.use((err, req, res, next) => { - console.error(err.stack); - res.status(500).json({ - error: 'Internal server error', - message: err.message + app.use((req, res) => { + res.status(404).json({ error: 'Not found' }); }); -}); -const PORT = process.env.PORT || 3000; -app.listen(PORT, () => { - console.log(`Quantum RNG API server running on port ${PORT}`); - console.log(`Version: ${QuantumRNG.getVersion()}`); - console.log(`Current entropy estimate: ${rng.getEntropyEstimate()}`); -}); + app.use((error, req, res, next) => { + if (res.headersSent) return next(error); + if (error instanceof ClientError) { + return res.status(error.status).json({ error: error.message }); + } + console.error(error); + return res.status(500).json({ error: 'Internal server error' }); + }); + + return app; +} + +const app = createApp(); + +function startServer(port = PORT, host = HOST) { + const server = app.listen(port, host, () => { + const address = server.address(); + const boundPort = typeof address === 'object' && address ? address.port : port; + console.log(`Tsotchke Quantum RNG API ${API_VERSION} listening on ${host}:${boundPort}`); + console.log( + `Engine ${QuantumRNG.getVersion()} (${QuantumRNG.getRevision().slice(0, 7)}), mode ${rng.getMode()}` + ); + if (startupVerification.performed) { + console.log( + `Startup CHSH simulation verification: S=${startupVerification.chsh.toFixed(4)}` + ); + } + }); + return server; +} + +if (require.main === module) { + startServer(); +} + +module.exports = { + API_VERSION, + MAX_BYTES_PER_REQUEST, + app, + createApp, + engineMetadata, + rng, + serializeStats, + startServer, + startupVerification, +}; diff --git a/src/binding.cc b/src/binding.cc index 6a82803..12f4465 100644 --- a/src/binding.cc +++ b/src/binding.cc @@ -1,243 +1,337 @@ #include -// Declare C linkage for quantum_rng functions -extern "C" { -#include "quantum_rng.h" +#include +#include +#include +#include + +#include "qrng_v3_bridge.h" + +namespace { + +constexpr const char *kUpstreamRelease = "v3.0.1+1a77e77"; +constexpr const char *kUpstreamRevision = + "1a77e77f803c63883349b658361c06401cd8ceb7"; +constexpr uint32_t kMaxNativeRequestBytes = 1024 * 1024; + +bool CheckError(Napi::Env env, int error) { + if (error == 0) return true; + Napi::Error::New(env, qrng_api_error_string(error)) + .ThrowAsJavaScriptException(); + return false; +} + +bool ParseInt32(Napi::Env env, const Napi::Value &input, + const char *name, int32_t *output) { + if (!input.IsNumber()) { + Napi::TypeError::New(env, std::string(name) + " must be a number") + .ThrowAsJavaScriptException(); + return false; + } + + const double value = input.As().DoubleValue(); + if (!std::isfinite(value) || std::trunc(value) != value || + value < std::numeric_limits::min() || + value > std::numeric_limits::max()) { + Napi::RangeError::New( + env, std::string(name) + " must be a signed 32-bit integer") + .ThrowAsJavaScriptException(); + return false; + } + + *output = static_cast(value); + return true; +} + +qrng_api_mode_t ParseMode(Napi::Env env, const Napi::Value &input, + bool *valid) { + *valid = false; + if (!input.IsString()) { + Napi::TypeError::New(env, "mode must be a string") + .ThrowAsJavaScriptException(); + return QRNG_API_MODE_DIRECT; + } + + const std::string mode = input.As().Utf8Value(); + *valid = true; + if (mode == "direct") return QRNG_API_MODE_DIRECT; + if (mode == "grover") return QRNG_API_MODE_GROVER; + if (mode == "bell-verified") return QRNG_API_MODE_BELL_VERIFIED; + + *valid = false; + Napi::RangeError::New( + env, "mode must be direct, grover, or bell-verified") + .ThrowAsJavaScriptException(); + return QRNG_API_MODE_DIRECT; } +} // namespace + class QuantumRNG : public Napi::ObjectWrap { public: static Napi::Object Init(Napi::Env env, Napi::Object exports); - QuantumRNG(const Napi::CallbackInfo& info); - ~QuantumRNG(); + explicit QuantumRNG(const Napi::CallbackInfo &info); + ~QuantumRNG() override; private: static Napi::FunctionReference constructor; - qrng_ctx* ctx; - - // Wrapped methods - Napi::Value GetBytes(const Napi::CallbackInfo& info); - Napi::Value GetUInt64(const Napi::CallbackInfo& info); - Napi::Value GetDouble(const Napi::CallbackInfo& info); - Napi::Value GetRange32(const Napi::CallbackInfo& info); - Napi::Value GetRange64(const Napi::CallbackInfo& info); - Napi::Value GetEntropyEstimate(const Napi::CallbackInfo& info); - Napi::Value Reseed(const Napi::CallbackInfo& info); - Napi::Value EntangleStates(const Napi::CallbackInfo& info); - Napi::Value MeasureState(const Napi::CallbackInfo& info); - static Napi::Value GetVersion(const Napi::CallbackInfo& info); + qrng_api_ctx *ctx_ = nullptr; + + Napi::Value GetBytes(const Napi::CallbackInfo &info); + Napi::Value GetUInt64(const Napi::CallbackInfo &info); + Napi::Value GetDouble(const Napi::CallbackInfo &info); + Napi::Value GetRange32(const Napi::CallbackInfo &info); + Napi::Value GetRange64(const Napi::CallbackInfo &info); + Napi::Value GetStats(const Napi::CallbackInfo &info); + Napi::Value VerifyQuantum(const Napi::CallbackInfo &info); + Napi::Value GetEntanglementEntropy(const Napi::CallbackInfo &info); + Napi::Value GetMode(const Napi::CallbackInfo &info); + + static Napi::Value GetVersion(const Napi::CallbackInfo &info); + static Napi::Value GetEngineVersion(const Napi::CallbackInfo &info); + static Napi::Value GetRevision(const Napi::CallbackInfo &info); }; Napi::FunctionReference QuantumRNG::constructor; Napi::Object QuantumRNG::Init(Napi::Env env, Napi::Object exports) { - Napi::HandleScope scope(env); - - Napi::Function func = DefineClass(env, "QuantumRNG", { + Napi::Function function = DefineClass(env, "QuantumRNG", { InstanceMethod("getBytes", &QuantumRNG::GetBytes), InstanceMethod("getUInt64", &QuantumRNG::GetUInt64), InstanceMethod("getDouble", &QuantumRNG::GetDouble), InstanceMethod("getRange32", &QuantumRNG::GetRange32), InstanceMethod("getRange64", &QuantumRNG::GetRange64), - InstanceMethod("getEntropyEstimate", &QuantumRNG::GetEntropyEstimate), - InstanceMethod("reseed", &QuantumRNG::Reseed), - InstanceMethod("entangleStates", &QuantumRNG::EntangleStates), - InstanceMethod("measureState", &QuantumRNG::MeasureState), - StaticMethod("getVersion", &QuantumRNG::GetVersion) + InstanceMethod("getStats", &QuantumRNG::GetStats), + InstanceMethod("verifyQuantum", &QuantumRNG::VerifyQuantum), + InstanceMethod("getEntanglementEntropy", &QuantumRNG::GetEntanglementEntropy), + InstanceMethod("getMode", &QuantumRNG::GetMode), + StaticMethod("getVersion", &QuantumRNG::GetVersion), + StaticMethod("getEngineVersion", &QuantumRNG::GetEngineVersion), + StaticMethod("getRevision", &QuantumRNG::GetRevision), }); - constructor = Napi::Persistent(func); + constructor = Napi::Persistent(function); constructor.SuppressDestruct(); - - exports.Set("QuantumRNG", func); + exports.Set("QuantumRNG", function); return exports; } -QuantumRNG::QuantumRNG(const Napi::CallbackInfo& info) - : Napi::ObjectWrap(info), ctx(nullptr) { +QuantumRNG::QuantumRNG(const Napi::CallbackInfo &info) + : Napi::ObjectWrap(info) { Napi::Env env = info.Env(); - try { - fprintf(stderr, "QuantumRNG constructor start\n"); - - if (info.Length() > 0 && !info[0].IsBuffer()) { - fprintf(stderr, "Invalid seed type\n"); - throw Napi::TypeError::New(env, "Seed must be a Buffer"); + qrng_api_mode_t mode = QRNG_API_MODE_DIRECT; + int bell_monitoring = 0; + + if (info.Length() > 0) { + if (!info[0].IsObject()) { + Napi::TypeError::New(env, "options must be an object") + .ThrowAsJavaScriptException(); + return; } - const uint8_t* seed = nullptr; - size_t seed_len = 0; - - if (info.Length() > 0) { - fprintf(stderr, "Processing seed buffer\n"); - Napi::Buffer seedBuffer = info[0].As>(); - seed = seedBuffer.Data(); - seed_len = seedBuffer.Length(); + const Napi::Object options = info[0].As(); + if (options.Has("mode")) { + bool valid = false; + mode = ParseMode(env, options.Get("mode"), &valid); + if (!valid) return; } - - fprintf(stderr, "Calling qrng_init\n"); - qrng_error err = qrng_init(&ctx, seed, seed_len); - fprintf(stderr, "qrng_init returned: %d\n", err); - if (err != QRNG_SUCCESS) { - if (ctx) { - free(ctx); - ctx = nullptr; + if (options.Has("bellMonitoring")) { + const Napi::Value value = options.Get("bellMonitoring"); + if (!value.IsBoolean()) { + Napi::TypeError::New(env, "bellMonitoring must be a boolean") + .ThrowAsJavaScriptException(); + return; } - throw Napi::Error::New(env, qrng_error_string(err)); + bell_monitoring = value.As().Value() ? 1 : 0; } - } catch (const Napi::Error& e) { - e.ThrowAsJavaScriptException(); + } + + const int error = qrng_api_init(&ctx_, mode, bell_monitoring); + if (error != 0) { + ctx_ = nullptr; + Napi::Error::New(env, qrng_api_error_string(error)) + .ThrowAsJavaScriptException(); } } QuantumRNG::~QuantumRNG() { - if (ctx) { - qrng_free(ctx); - ctx = nullptr; - } + qrng_api_free(ctx_); + ctx_ = nullptr; } -Napi::Value QuantumRNG::GetBytes(const Napi::CallbackInfo& info) { +Napi::Value QuantumRNG::GetBytes(const Napi::CallbackInfo &info) { Napi::Env env = info.Env(); + if (info.Length() != 1 || !info[0].IsNumber()) { + Napi::TypeError::New(env, "length must be a number") + .ThrowAsJavaScriptException(); + return env.Null(); + } - if (info.Length() < 1 || !info[0].IsNumber()) { - Napi::TypeError::New(env, "Number of bytes required").ThrowAsJavaScriptException(); + const double raw_length = info[0].As().DoubleValue(); + if (!std::isfinite(raw_length) || std::trunc(raw_length) != raw_length || + raw_length < 1 || raw_length > kMaxNativeRequestBytes) { + Napi::RangeError::New( + env, "length must be an integer between 1 and 1048576") + .ThrowAsJavaScriptException(); return env.Null(); } - size_t length = info[0].As().Uint32Value(); + const size_t length = static_cast(raw_length); Napi::Buffer buffer = Napi::Buffer::New(env, length); - - qrng_error err = qrng_bytes(ctx, buffer.Data(), length); - if (err != QRNG_SUCCESS) { - Napi::Error::New(env, qrng_error_string(err)).ThrowAsJavaScriptException(); + if (!CheckError(env, qrng_api_bytes(ctx_, buffer.Data(), length))) { return env.Null(); } - return buffer; } -Napi::Value QuantumRNG::GetUInt64(const Napi::CallbackInfo& info) { +Napi::Value QuantumRNG::GetUInt64(const Napi::CallbackInfo &info) { Napi::Env env = info.Env(); - uint64_t value = qrng_uint64(ctx); + uint64_t value = 0; + if (!CheckError(env, qrng_api_uint64(ctx_, &value))) return env.Null(); return Napi::BigInt::New(env, value); } -Napi::Value QuantumRNG::GetDouble(const Napi::CallbackInfo& info) { +Napi::Value QuantumRNG::GetDouble(const Napi::CallbackInfo &info) { Napi::Env env = info.Env(); - double value = qrng_double(ctx); + double value = 0.0; + if (!CheckError(env, qrng_api_double(ctx_, &value))) return env.Null(); return Napi::Number::New(env, value); } -Napi::Value QuantumRNG::GetRange32(const Napi::CallbackInfo& info) { +Napi::Value QuantumRNG::GetRange32(const Napi::CallbackInfo &info) { Napi::Env env = info.Env(); - - if (info.Length() < 2 || !info[0].IsNumber() || !info[1].IsNumber()) { - Napi::TypeError::New(env, "Min and max values required").ThrowAsJavaScriptException(); + if (info.Length() != 2) { + Napi::TypeError::New(env, "min and max are required") + .ThrowAsJavaScriptException(); return env.Null(); } - int32_t min = info[0].As().Int32Value(); - int32_t max = info[1].As().Int32Value(); + int32_t min = 0; + int32_t max = 0; + if (!ParseInt32(env, info[0], "min", &min) || + !ParseInt32(env, info[1], "max", &max)) { + return env.Null(); + } + if (min > max) { + Napi::RangeError::New(env, "min must be less than or equal to max") + .ThrowAsJavaScriptException(); + return env.Null(); + } - int32_t value = qrng_range32(ctx, min, max); + int32_t value = 0; + if (!CheckError(env, qrng_api_range32(ctx_, min, max, &value))) { + return env.Null(); + } return Napi::Number::New(env, value); } -Napi::Value QuantumRNG::GetRange64(const Napi::CallbackInfo& info) { +Napi::Value QuantumRNG::GetRange64(const Napi::CallbackInfo &info) { Napi::Env env = info.Env(); - - if (info.Length() < 2) { - Napi::TypeError::New(env, "Min and max values required").ThrowAsJavaScriptException(); + if (info.Length() != 2 || !info[0].IsBigInt() || !info[1].IsBigInt()) { + Napi::TypeError::New(env, "min and max must be unsigned BigInt values") + .ThrowAsJavaScriptException(); return env.Null(); } - bool lossless; - uint64_t min = info[0].As().Uint64Value(&lossless); - if (!lossless) { - Napi::Error::New(env, "Loss of precision in min value").ThrowAsJavaScriptException(); + bool min_lossless = false; + bool max_lossless = false; + const uint64_t min = info[0].As().Uint64Value(&min_lossless); + const uint64_t max = info[1].As().Uint64Value(&max_lossless); + if (!min_lossless || !max_lossless || min > max) { + Napi::RangeError::New( + env, "min and max must satisfy 0 <= min <= max <= 2^64-1") + .ThrowAsJavaScriptException(); return env.Null(); } - uint64_t max = info[1].As().Uint64Value(&lossless); - if (!lossless) { - Napi::Error::New(env, "Loss of precision in max value").ThrowAsJavaScriptException(); + + uint64_t value = 0; + if (!CheckError(env, qrng_api_range64(ctx_, min, max, &value))) { return env.Null(); } - - uint64_t value = qrng_range64(ctx, min, max); return Napi::BigInt::New(env, value); } -Napi::Value QuantumRNG::GetEntropyEstimate(const Napi::CallbackInfo& info) { +Napi::Value QuantumRNG::GetStats(const Napi::CallbackInfo &info) { Napi::Env env = info.Env(); - double entropy = qrng_get_entropy_estimate(ctx); - return Napi::Number::New(env, entropy); + qrng_api_stats_t stats{}; + if (!CheckError(env, qrng_api_get_stats(ctx_, &stats))) return env.Null(); + + Napi::Object result = Napi::Object::New(env); + result.Set("bytesGenerated", Napi::BigInt::New(env, stats.bytes_generated)); + result.Set("quantumMeasurements", Napi::BigInt::New(env, stats.quantum_measurements)); + result.Set("groverSearches", Napi::BigInt::New(env, stats.grover_searches)); + result.Set("bellTestsPerformed", Napi::BigInt::New(env, stats.bell_tests_performed)); + result.Set("bellTestsPassed", Napi::BigInt::New(env, stats.bell_tests_passed)); + result.Set("averageChsh", Napi::Number::New(env, stats.average_chsh)); + result.Set("minChsh", Napi::Number::New(env, stats.min_chsh)); + result.Set("maxChsh", Napi::Number::New(env, stats.max_chsh)); + result.Set("hardwareEntropyConsumed", + Napi::BigInt::New(env, stats.hardware_entropy_consumed)); + result.Set("entanglementEntropy", + Napi::Number::New(env, stats.entanglement_entropy)); + result.Set("throughputMbps", Napi::Number::New(env, stats.throughput_mbps)); + result.Set("mode", Napi::String::New(env, qrng_api_mode(ctx_))); + return result; } -Napi::Value QuantumRNG::Reseed(const Napi::CallbackInfo& info) { +Napi::Value QuantumRNG::VerifyQuantum(const Napi::CallbackInfo &info) { Napi::Env env = info.Env(); - - if (info.Length() < 1 || !info[0].IsBuffer()) { - Napi::TypeError::New(env, "Seed buffer required").ThrowAsJavaScriptException(); - return env.Null(); + size_t measurements = 4000; + if (info.Length() > 0) { + if (!info[0].IsNumber()) { + Napi::TypeError::New(env, "measurements must be a number") + .ThrowAsJavaScriptException(); + return env.Null(); + } + const double value = info[0].As().DoubleValue(); + if (!std::isfinite(value) || std::trunc(value) != value || + value < 1000 || value > 100000) { + Napi::RangeError::New( + env, "measurements must be an integer between 1000 and 100000") + .ThrowAsJavaScriptException(); + return env.Null(); + } + measurements = static_cast(value); } - Napi::Buffer seedBuffer = info[0].As>(); - qrng_error err = qrng_reseed(ctx, seedBuffer.Data(), seedBuffer.Length()); - - if (err != QRNG_SUCCESS) { - Napi::Error::New(env, qrng_error_string(err)).ThrowAsJavaScriptException(); + qrng_api_verification_t result{}; + if (!CheckError(env, qrng_api_verify(ctx_, measurements, &result))) { return env.Null(); } - return env.Undefined(); + Napi::Object output = Napi::Object::New(env); + output.Set("chsh", Napi::Number::New(env, result.chsh)); + output.Set("classicalBound", Napi::Number::New(env, result.classical_bound)); + output.Set("quantumBound", Napi::Number::New(env, result.quantum_bound)); + output.Set("pValue", Napi::Number::New(env, result.p_value)); + output.Set("standardError", Napi::Number::New(env, result.standard_error)); + output.Set("measurements", Napi::Number::New(env, result.measurements)); + output.Set("violatesClassical", Napi::Boolean::New(env, result.violates_classical != 0)); + output.Set("confirmsQuantumModel", + Napi::Boolean::New(env, result.confirms_quantum_model != 0)); + output.Set("statisticallySignificant", + Napi::Boolean::New(env, result.statistically_significant != 0)); + return output; } -Napi::Value QuantumRNG::EntangleStates(const Napi::CallbackInfo& info) { - Napi::Env env = info.Env(); - - if (info.Length() < 2 || !info[0].IsBuffer() || !info[1].IsBuffer()) { - Napi::TypeError::New(env, "Two state buffers required").ThrowAsJavaScriptException(); - return env.Null(); - } - - Napi::Buffer state1 = info[0].As>(); - Napi::Buffer state2 = info[1].As>(); - - if (state1.Length() != state2.Length()) { - Napi::Error::New(env, "State buffers must be the same length").ThrowAsJavaScriptException(); - return env.Null(); - } - - qrng_error err = qrng_entangle_states(ctx, state1.Data(), state2.Data(), state1.Length()); - if (err != QRNG_SUCCESS) { - Napi::Error::New(env, qrng_error_string(err)).ThrowAsJavaScriptException(); - return env.Null(); - } - - return env.Undefined(); +Napi::Value QuantumRNG::GetEntanglementEntropy(const Napi::CallbackInfo &info) { + return Napi::Number::New(info.Env(), qrng_api_entanglement_entropy(ctx_)); } -Napi::Value QuantumRNG::MeasureState(const Napi::CallbackInfo& info) { - Napi::Env env = info.Env(); - - if (info.Length() < 1 || !info[0].IsBuffer()) { - Napi::TypeError::New(env, "State buffer required").ThrowAsJavaScriptException(); - return env.Null(); - } - - Napi::Buffer state = info[0].As>(); - qrng_error err = qrng_measure_state(ctx, state.Data(), state.Length()); +Napi::Value QuantumRNG::GetMode(const Napi::CallbackInfo &info) { + return Napi::String::New(info.Env(), qrng_api_mode(ctx_)); +} - if (err != QRNG_SUCCESS) { - Napi::Error::New(env, qrng_error_string(err)).ThrowAsJavaScriptException(); - return env.Null(); - } +Napi::Value QuantumRNG::GetVersion(const Napi::CallbackInfo &info) { + return Napi::String::New(info.Env(), kUpstreamRelease); +} - return env.Undefined(); +Napi::Value QuantumRNG::GetEngineVersion(const Napi::CallbackInfo &info) { + return Napi::String::New(info.Env(), qrng_api_engine_version()); } -Napi::Value QuantumRNG::GetVersion(const Napi::CallbackInfo& info) { - return Napi::String::New(info.Env(), qrng_version()); +Napi::Value QuantumRNG::GetRevision(const Napi::CallbackInfo &info) { + return Napi::String::New(info.Env(), kUpstreamRevision); } Napi::Object Init(Napi::Env env, Napi::Object exports) { diff --git a/src/qrng_v3_bridge.c b/src/qrng_v3_bridge.c new file mode 100644 index 0000000..5fdcc44 --- /dev/null +++ b/src/qrng_v3_bridge.c @@ -0,0 +1,163 @@ +#include "qrng_v3_bridge.h" + +#include "quantum_rng_v3.h" + +#include + +struct qrng_api_ctx { + qrng_v3_ctx_t *engine; + uint64_t manual_bell_tests_passed; +}; + +int qrng_api_init(qrng_api_ctx **ctx_out, qrng_api_mode_t mode, + int bell_monitoring) { + if (!ctx_out || mode < QRNG_API_MODE_DIRECT || + mode > QRNG_API_MODE_BELL_VERIFIED) { + return QRNG_V3_ERROR_INVALID_PARAM; + } + + *ctx_out = NULL; + qrng_api_ctx *ctx = calloc(1, sizeof(*ctx)); + if (!ctx) return QRNG_V3_ERROR_OUT_OF_MEMORY; + + qrng_v3_config_t config; + qrng_v3_get_default_config(&config); + config.mode = (qrng_v3_mode_t)mode; + config.enable_bell_monitoring = bell_monitoring ? 1 : 0; + + qrng_v3_error_t error = qrng_v3_init_with_config(&ctx->engine, &config); + if (error != QRNG_V3_SUCCESS) { + free(ctx); + return error; + } + + *ctx_out = ctx; + return QRNG_V3_SUCCESS; +} + +void qrng_api_free(qrng_api_ctx *ctx) { + if (!ctx) return; + qrng_v3_free(ctx->engine); + ctx->engine = NULL; + free(ctx); +} + +int qrng_api_bytes(qrng_api_ctx *ctx, uint8_t *buffer, size_t size) { + if (!ctx) return QRNG_V3_ERROR_NULL_CONTEXT; + return qrng_v3_bytes(ctx->engine, buffer, size); +} + +int qrng_api_uint64(qrng_api_ctx *ctx, uint64_t *value) { + if (!ctx) return QRNG_V3_ERROR_NULL_CONTEXT; + return qrng_v3_uint64(ctx->engine, value); +} + +int qrng_api_double(qrng_api_ctx *ctx, double *value) { + if (!ctx) return QRNG_V3_ERROR_NULL_CONTEXT; + return qrng_v3_double(ctx->engine, value); +} + +int qrng_api_range32(qrng_api_ctx *ctx, int32_t min, int32_t max, + int32_t *value) { + if (!ctx) return QRNG_V3_ERROR_NULL_CONTEXT; + if (!value) return QRNG_V3_ERROR_NULL_BUFFER; + if (min > max) return QRNG_V3_ERROR_INVALID_PARAM; + + const uint64_t span = + (uint64_t)((int64_t)max - (int64_t)min) + UINT64_C(1); + const uint64_t threshold = (UINT64_C(0) - span) % span; + uint64_t random = 0; + int error; + do { + error = qrng_v3_uint64(ctx->engine, &random); + if (error != QRNG_V3_SUCCESS) return error; + } while (random < threshold); + + *value = (int32_t)((int64_t)min + (int64_t)(random % span)); + return QRNG_V3_SUCCESS; +} + +int qrng_api_range64(qrng_api_ctx *ctx, uint64_t min, uint64_t max, + uint64_t *value) { + if (!ctx) return QRNG_V3_ERROR_NULL_CONTEXT; + return qrng_v3_range(ctx->engine, min, max, value); +} + +int qrng_api_get_stats(qrng_api_ctx *ctx, qrng_api_stats_t *stats_out) { + if (!ctx) return QRNG_V3_ERROR_NULL_CONTEXT; + if (!stats_out) return QRNG_V3_ERROR_NULL_BUFFER; + + qrng_v3_stats_t stats; + const int error = qrng_v3_get_stats(ctx->engine, &stats); + if (error != QRNG_V3_SUCCESS) return error; + + stats_out->bytes_generated = stats.bytes_generated; + stats_out->quantum_measurements = stats.quantum_measurements; + stats_out->grover_searches = stats.grover_searches; + stats_out->bell_tests_performed = stats.bell_tests_performed; + /* + * The pinned engine increments bell_tests_performed for explicit + * qrng_v3_verify_quantum() calls but only increments bell_tests_passed in + * its automatic monitoring path. Account for successful explicit checks + * here so the API's public counters describe what actually happened. + */ + stats_out->bell_tests_passed = + stats.bell_tests_passed + ctx->manual_bell_tests_passed; + stats_out->average_chsh = stats.average_chsh; + stats_out->min_chsh = stats.min_chsh; + stats_out->max_chsh = stats.max_chsh; + stats_out->hardware_entropy_consumed = stats.hardware_entropy_consumed; + stats_out->entanglement_entropy = + qrng_v3_get_entanglement_entropy(ctx->engine); + stats_out->throughput_mbps = stats.throughput_mbps; + return QRNG_V3_SUCCESS; +} + +int qrng_api_verify(qrng_api_ctx *ctx, size_t measurements, + qrng_api_verification_t *verification) { + if (!ctx) return QRNG_V3_ERROR_NULL_CONTEXT; + if (!verification) return QRNG_V3_ERROR_NULL_BUFFER; + + const bell_test_result_t result = + qrng_v3_verify_quantum(ctx->engine, measurements); + verification->chsh = result.chsh_value; + verification->classical_bound = result.classical_bound; + verification->quantum_bound = result.quantum_bound; + verification->p_value = result.p_value; + verification->standard_error = result.standard_error; + verification->measurements = result.measurements; + verification->violates_classical = result.violates_classical; + verification->confirms_quantum_model = result.confirms_quantum; + verification->statistically_significant = result.statistically_significant; + if (result.confirms_quantum) { + ctx->manual_bell_tests_passed++; + } + return QRNG_V3_SUCCESS; +} + +double qrng_api_entanglement_entropy(qrng_api_ctx *ctx) { + if (!ctx) return 0.0; + return qrng_v3_get_entanglement_entropy(ctx->engine); +} + +const char *qrng_api_mode(qrng_api_ctx *ctx) { + if (!ctx) return "UNKNOWN"; + switch (qrng_v3_get_mode(ctx->engine)) { + case QRNG_V3_MODE_DIRECT: + return "direct"; + case QRNG_V3_MODE_GROVER: + return "grover"; + case QRNG_V3_MODE_BELL_VERIFIED: + return "bell-verified"; + default: + return "unknown"; + } +} + +const char *qrng_api_engine_version(void) { + return qrng_v3_version(); +} + +const char *qrng_api_error_string(int error) { + return qrng_v3_error_string((qrng_v3_error_t)error); +} diff --git a/src/qrng_v3_bridge.h b/src/qrng_v3_bridge.h new file mode 100644 index 0000000..7b40d91 --- /dev/null +++ b/src/qrng_v3_bridge.h @@ -0,0 +1,70 @@ +#ifndef TSOTCHKE_QRNG_V3_BRIDGE_H +#define TSOTCHKE_QRNG_V3_BRIDGE_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct qrng_api_ctx qrng_api_ctx; + +typedef enum { + QRNG_API_MODE_DIRECT = 0, + QRNG_API_MODE_GROVER = 1, + QRNG_API_MODE_BELL_VERIFIED = 2 +} qrng_api_mode_t; + +typedef struct { + uint64_t bytes_generated; + uint64_t quantum_measurements; + uint64_t grover_searches; + uint64_t bell_tests_performed; + uint64_t bell_tests_passed; + double average_chsh; + double min_chsh; + double max_chsh; + uint64_t hardware_entropy_consumed; + double entanglement_entropy; + double throughput_mbps; +} qrng_api_stats_t; + +typedef struct { + double chsh; + double classical_bound; + double quantum_bound; + double p_value; + double standard_error; + size_t measurements; + int violates_classical; + int confirms_quantum_model; + int statistically_significant; +} qrng_api_verification_t; + +int qrng_api_init(qrng_api_ctx **ctx, qrng_api_mode_t mode, + int bell_monitoring); +void qrng_api_free(qrng_api_ctx *ctx); + +int qrng_api_bytes(qrng_api_ctx *ctx, uint8_t *buffer, size_t size); +int qrng_api_uint64(qrng_api_ctx *ctx, uint64_t *value); +int qrng_api_double(qrng_api_ctx *ctx, double *value); +int qrng_api_range32(qrng_api_ctx *ctx, int32_t min, int32_t max, + int32_t *value); +int qrng_api_range64(qrng_api_ctx *ctx, uint64_t min, uint64_t max, + uint64_t *value); + +int qrng_api_get_stats(qrng_api_ctx *ctx, qrng_api_stats_t *stats); +int qrng_api_verify(qrng_api_ctx *ctx, size_t measurements, + qrng_api_verification_t *verification); +double qrng_api_entanglement_entropy(qrng_api_ctx *ctx); +const char *qrng_api_mode(qrng_api_ctx *ctx); + +const char *qrng_api_engine_version(void); +const char *qrng_api_error_string(int error); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/test/native.test.js b/test/native.test.js new file mode 100644 index 0000000..bd8ff97 --- /dev/null +++ b/test/native.test.js @@ -0,0 +1,77 @@ +'use strict'; + +const assert = require('node:assert/strict'); +const { before, test } = require('node:test'); + +const { QuantumRNG } = require('../build/Release/quantum_rng.node'); + +let rng; + +before(() => { + rng = new QuantumRNG({ mode: 'direct', bellMonitoring: false }); +}); + +test('reports exact upstream provenance', () => { + assert.equal(QuantumRNG.getVersion(), 'v3.0.1+1a77e77'); + assert.equal( + QuantumRNG.getRevision(), + '1a77e77f803c63883349b658361c06401cd8ceb7' + ); + assert.equal(rng.getMode(), 'direct'); +}); + +test('generates full-width byte and uint64 output', () => { + const bytes = rng.getBytes(256); + assert.equal(bytes.length, 256); + assert.notEqual(bytes.equals(Buffer.alloc(bytes.length)), true); + + const values = Array.from({ length: 256 }, () => rng.getUInt64()); + assert.ok(new Set(values.map(String)).size > 240); + assert.ok(values.some((value) => (value >> 32n) !== 0n)); + assert.ok(values.every((value) => value >= 0n && value <= 0xffffffffffffffffn)); +}); + +test('generates doubles in the half-open unit interval', () => { + for (let i = 0; i < 256; i += 1) { + const value = rng.getDouble(); + assert.ok(value >= 0 && value < 1); + } +}); + +test('handles the full signed 32-bit span without overflow or SIGFPE', () => { + const values = Array.from({ length: 256 }, () => + rng.getRange32(-2147483648, 2147483647) + ); + assert.ok(values.every((value) => Number.isInteger(value))); + assert.ok(values.every((value) => value >= -2147483648 && value <= 2147483647)); + assert.ok(values.some((value) => value < 0)); + assert.ok(values.some((value) => value >= 0)); +}); + +test('handles the full unsigned 64-bit span without a constant result', () => { + const values = Array.from({ length: 128 }, () => + rng.getRange64(0n, 0xffffffffffffffffn) + ); + assert.ok(new Set(values.map(String)).size > 120); + assert.ok(values.some((value) => value !== 0xffffffffffffffffn)); +}); + +test('Bell verification validates the simulated quantum-state model', () => { + const before = rng.getStats(); + const result = rng.verifyQuantum(4000); + const after = rng.getStats(); + assert.equal(result.measurements, 4000); + assert.equal(result.violatesClassical, true); + assert.ok(result.chsh > result.classicalBound); + assert.ok(result.chsh <= result.quantumBound + 0.2); + assert.equal(after.bellTestsPerformed, before.bellTestsPerformed + 1n); + assert.equal(after.bellTestsPassed, before.bellTestsPassed + 1n); +}); + +test('exposes precise generation counters', () => { + const stats = rng.getStats(); + assert.equal(typeof stats.bytesGenerated, 'bigint'); + assert.equal(typeof stats.quantumMeasurements, 'bigint'); + assert.ok(stats.bytesGenerated > 0n); + assert.ok(stats.quantumMeasurements > 0n); +}); diff --git a/test/server.test.js b/test/server.test.js new file mode 100644 index 0000000..0eaa4b4 --- /dev/null +++ b/test/server.test.js @@ -0,0 +1,114 @@ +'use strict'; + +const assert = require('node:assert/strict'); +const { once } = require('node:events'); +const { after, before, test } = require('node:test'); + +const { API_VERSION, app, startupVerification } = require('../server'); + +let server; +let baseUrl; + +before(async () => { + server = app.listen(0, '127.0.0.1'); + await once(server, 'listening'); + const address = server.address(); + baseUrl = `http://127.0.0.1:${address.port}`; +}); + +after(async () => { + if (!server || !server.listening) return; + await new Promise((resolve, reject) => { + server.close((error) => (error ? reject(error) : resolve())); + }); +}); + +async function request(path, options) { + const response = await fetch(`${baseUrl}${path}`, options); + return { response, body: await response.json() }; +} + +test('root and health disclose API and engine provenance', async () => { + const root = await request('/'); + assert.equal(root.response.status, 200); + assert.equal(root.body.apiVersion, API_VERSION); + assert.equal(root.body.engine.name, 'quantum_rng_v3'); + assert.match(root.body.engine.revision, /^[0-9a-f]{40}$/); + + const health = await request('/v1/health'); + assert.equal(health.response.status, 200); + assert.equal(health.body.status, 'ok'); + assert.equal(health.body.verification.scope, 'quantum-state simulation model'); + assert.equal(health.body.verification.performed, startupVerification.performed); + assert.equal(typeof health.body.stats.bytesGenerated, 'string'); +}); + +test('returns bytes in stable encodings', async () => { + const { response, body } = await request('/v1/qrng/bytes/32'); + assert.equal(response.status, 200); + assert.equal(body.length, 32); + assert.match(body.bytes, /^[0-9a-f]{64}$/); + assert.equal(Buffer.from(body.base64, 'base64').length, 32); + assert.equal(body.raw.length, 32); +}); + +test('rejects invalid byte counts', async () => { + assert.equal((await request('/v1/qrng/bytes/0')).response.status, 400); + assert.equal((await request('/v1/qrng/bytes/4097')).response.status, 400); + assert.equal((await request('/v1/qrng/bytes/not-a-number')).response.status, 400); +}); + +test('returns float and full-width uint64 values', async () => { + const floating = await request('/v1/qrng/number?type=float'); + assert.equal(floating.response.status, 200); + assert.ok(floating.body.value >= 0 && floating.body.value < 1); + + const integer = await request('/v1/qrng/number?type=uint64'); + assert.equal(integer.response.status, 200); + assert.match(integer.body.value, /^\d+$/); + assert.ok(BigInt(integer.body.value) <= 0xffffffffffffffffn); +}); + +test('range endpoints safely cover full-width inputs', async () => { + const signed = await request( + '/v1/qrng/range?min=-2147483648&max=2147483647' + ); + assert.equal(signed.response.status, 200); + assert.ok(signed.body.value >= -2147483648); + assert.ok(signed.body.value <= 2147483647); + + const unsigned = await request( + '/v1/qrng/range64?min=0&max=18446744073709551615' + ); + assert.equal(unsigned.response.status, 200); + assert.match(unsigned.body.value, /^\d+$/); + assert.ok(BigInt(unsigned.body.value) <= 0xffffffffffffffffn); +}); + +test('probability endpoints preserve exact zero and one semantics', async () => { + const never = await request('/v1/qrng/boolean?probability=0'); + const always = await request('/v1/qrng/boolean?probability=1'); + assert.deepEqual(never.body, { probability: 0, value: false }); + assert.deepEqual(always.body, { probability: 1, value: true }); +}); + +test('choice uses an exact bounded integer draw', async () => { + const { response, body } = await request('/v1/qrng/choice', { + method: 'POST', + headers: { 'content-type': 'application/json' }, + body: JSON.stringify({ array: ['alpha', 'beta', 'gamma'] }), + }); + assert.equal(response.status, 200); + assert.ok(Number.isInteger(body.index)); + assert.equal(body.value, ['alpha', 'beta', 'gamma'][body.index]); +}); + +test('exposes OpenAPI and returns structured 404 errors', async () => { + const openapi = await request('/v1/openapi.json'); + assert.equal(openapi.response.status, 200); + assert.equal(openapi.body.openapi, '3.0.3'); + + const missing = await request('/does-not-exist'); + assert.equal(missing.response.status, 404); + assert.deepEqual(missing.body, { error: 'Not found' }); +}); diff --git a/vendor/quantum_rng/LICENSE b/vendor/quantum_rng/LICENSE new file mode 100644 index 0000000..e42af98 --- /dev/null +++ b/vendor/quantum_rng/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Tsotchke + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/quantum_rng/UPSTREAM.md b/vendor/quantum_rng/UPSTREAM.md new file mode 100644 index 0000000..608f8b7 --- /dev/null +++ b/vendor/quantum_rng/UPSTREAM.md @@ -0,0 +1,28 @@ +# Vendored quantum_rng provenance + +The native engine in this directory is vendored from +[`tsotchke/quantum_rng`](https://github.com/tsotchke/quantum_rng). + +| Field | Value | +| --- | --- | +| Release line | `v3.0.1` | +| Pinned revision | `1a77e77f803c63883349b658361c06401cd8ceb7` | +| Pinned date | 2026-07-11 | +| License | MIT; see `LICENSE` in this directory | + +The pinned revision is one commit newer than the `v3.0.1` tag. It contains the +full-width output-conditioning fix that prevents the 8-qubit basis index from +being serialized as if it were an unconditioned 64-bit value. This API reports +the engine provenance as `v3.0.1+1a77e77`. + +The vendored source files are copied without modification. API-specific code +lives in `src/binding.cc` and `server.js`. + +## Updating + +1. Review the new upstream tag and every commit since the pinned revision. +2. Replace the vendored source directories and `LICENSE` from the selected + upstream commit. +3. Update the revision constants in `src/binding.cc` and this document. +4. Run `npm ci --ignore-scripts && npm test` on macOS and Linux. +5. Verify `/v1/health` reports the expected release and revision. diff --git a/src/common/constants.h b/vendor/quantum_rng/src/common/constants.h similarity index 100% rename from src/common/constants.h rename to vendor/quantum_rng/src/common/constants.h diff --git a/vendor/quantum_rng/src/common/secure_memory.h b/vendor/quantum_rng/src/common/secure_memory.h new file mode 100644 index 0000000..ade22b3 --- /dev/null +++ b/vendor/quantum_rng/src/common/secure_memory.h @@ -0,0 +1,91 @@ +#ifndef SECURE_MEMORY_H +#define SECURE_MEMORY_H + +#include +#include + +/** + * @file secure_memory.h + * @brief Cryptographically secure memory operations + * + * Provides platform-independent secure memory zeroing that cannot be + * optimized away by compilers. Essential for clearing sensitive data + * like cryptographic keys, quantum states, and entropy buffers. + * + * Uses platform-specific implementations when available: + * - C11 memset_s() (Annex K) + * - OpenBSD/glibc explicit_bzero() + * - Portable fallback with volatile writes + a compiler memory barrier + * (used on Windows/MinGW and anywhere the above are unavailable) + */ + +/** + * @brief Securely zero memory + * + * Guarantees that memory is zeroed and the operation is not optimized + * away by the compiler. Critical for security-sensitive applications. + * + * This function: + * - Cannot be optimized away (compiler-independent) + * - Works across all platforms + * - Has minimal performance overhead + * - Is safe for use in destructors/cleanup code + * + * @param ptr Pointer to memory to zero + * @param size Number of bytes to zero + */ +static inline void secure_memzero(void *ptr, size_t size) { + if (!ptr || size == 0) return; + +#if defined(__STDC_LIB_EXT1__) && defined(__STDC_WANT_LIB_EXT1__) + // C11 Annex K - memset_s + memset_s(ptr, size, 0, size); + +#elif defined(__OpenBSD__) || (defined(__GLIBC__) && \ + (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 25))) + // OpenBSD or glibc >= 2.25 - explicit_bzero + explicit_bzero(ptr, size); + +#else + // Portable fallback with volatile and memory barrier + volatile unsigned char *p = (volatile unsigned char *)ptr; + while (size--) { + *p++ = 0; + } + + // Memory barrier to prevent reordering + #if defined(__GNUC__) || defined(__clang__) + __asm__ __volatile__("" ::: "memory"); + #elif defined(_MSC_VER) + _ReadWriteBarrier(); + #endif +#endif +} + +/** + * @brief Securely compare two memory regions in constant time + * + * Prevents timing attacks by ensuring comparison time is independent + * of data content. Use for comparing cryptographic keys, MACs, etc. + * + * @param a First memory region + * @param b Second memory region + * @param size Number of bytes to compare + * @return 0 if equal, non-zero if different + */ +static inline int secure_memcmp(const void *a, const void *b, size_t size) { + if (!a || !b) return -1; + + const volatile unsigned char *pa = (const volatile unsigned char *)a; + const volatile unsigned char *pb = (const volatile unsigned char *)b; + unsigned char diff = 0; + + // Constant-time comparison - always checks all bytes + for (size_t i = 0; i < size; i++) { + diff |= pa[i] ^ pb[i]; + } + + return diff; +} + +#endif /* SECURE_MEMORY_H */ \ No newline at end of file diff --git a/vendor/quantum_rng/src/common/validation.h b/vendor/quantum_rng/src/common/validation.h new file mode 100644 index 0000000..b5f99a3 --- /dev/null +++ b/vendor/quantum_rng/src/common/validation.h @@ -0,0 +1,251 @@ +#ifndef VALIDATION_H +#define VALIDATION_H + +#include +#include + +/** + * @file validation.h + * @brief Input validation macros and utilities + * + * Provides comprehensive, consistent input validation across all modules. + * Helps prevent security vulnerabilities, crashes, and undefined behavior + * caused by invalid inputs. + * + * Key principles: + * - Validate all inputs at API boundaries + * - Provide clear error messages + * - Fail safely with proper error codes + * - Zero performance overhead in release builds (optional) + */ + +// ============================================================================ +// VALIDATION MACROS +// ============================================================================ + +/** + * @brief Validate pointer is not NULL + */ +#define VALIDATE_NOT_NULL(ptr, error_code) \ + do { \ + if (!(ptr)) { \ + return (error_code); \ + } \ + } while (0) + +/** + * @brief Validate pointer with custom error message (for debugging builds) + */ +#ifdef ENABLE_VALIDATION_MESSAGES + #define VALIDATE_NOT_NULL_MSG(ptr, error_code, msg) \ + do { \ + if (!(ptr)) { \ + fprintf(stderr, "Validation error: %s (NULL pointer)\n", msg); \ + return (error_code); \ + } \ + } while (0) +#else + #define VALIDATE_NOT_NULL_MSG(ptr, error_code, msg) \ + VALIDATE_NOT_NULL(ptr, error_code) +#endif + +/** + * @brief Validate value is within range [min, max] + */ +#define VALIDATE_RANGE(val, min, max, error_code) \ + do { \ + if ((val) < (min) || (val) > (max)) { \ + return (error_code); \ + } \ + } while (0) + +/** + * @brief Validate unsigned value is within range [min, max] + */ +#define VALIDATE_RANGE_U(val, min, max, error_code) \ + do { \ + if ((uint64_t)(val) < (uint64_t)(min) || (uint64_t)(val) > (uint64_t)(max)) { \ + return (error_code); \ + } \ + } while (0) + +/** + * @brief Validate condition is true + */ +#define VALIDATE_CONDITION(condition, error_code) \ + do { \ + if (!(condition)) { \ + return (error_code); \ + } \ + } while (0) + +/** + * @brief Validate buffer and size are valid + */ +#define VALIDATE_BUFFER(buf, size, error_code) \ + do { \ + if (!(buf) || (size) == 0) { \ + return (error_code); \ + } \ + } while (0) + +// ============================================================================ +// QUANTUM-SPECIFIC VALIDATION +// ============================================================================ + +/** + * @brief Validate qubit index is valid for quantum state + */ +#define VALIDATE_QUBIT(state, qubit, error_code) \ + do { \ + if (!state || (qubit) < 0 || (qubit) >= (int)((state)->num_qubits)) { \ + return (error_code); \ + } \ + } while (0) + +/** + * @brief Validate multiple qubits are valid and unique + */ +#define VALIDATE_QUBITS_UNIQUE(state, qubits, num_qubits, error_code) \ + do { \ + if (!(state) || !(qubits) || (num_qubits) == 0) { \ + return (error_code); \ + } \ + for (size_t _i = 0; _i < (num_qubits); _i++) { \ + if ((qubits)[_i] < 0 || (qubits)[_i] >= (int)((state)->num_qubits)) { \ + return (error_code); \ + } \ + for (size_t _j = _i + 1; _j < (num_qubits); _j++) { \ + if ((qubits)[_i] == (qubits)[_j]) { \ + return (error_code); \ + } \ + } \ + } \ + } while (0) + +/** + * @brief Validate quantum state is initialized + */ +#define VALIDATE_QUANTUM_STATE(state, error_code) \ + do { \ + if (!(state) || !(state)->amplitudes || (state)->state_dim == 0) { \ + return (error_code); \ + } \ + } while (0) + +/** + * @brief Validate basis state index is within state dimension + */ +#define VALIDATE_BASIS_INDEX(state, index, error_code) \ + do { \ + if (!(state) || (index) >= (state)->state_dim) { \ + return (error_code); \ + } \ + } while (0) + +// ============================================================================ +// ENTROPY VALIDATION +// ============================================================================ + +/** + * @brief Validate entropy context is initialized + */ +#define VALIDATE_ENTROPY_CTX(ctx, error_code) \ + do { \ + if (!(ctx) || !(ctx)->get_bytes) { \ + return (error_code); \ + } \ + } while (0) + +/** + * @brief Validate entropy quality estimate + */ +#define VALIDATE_ENTROPY_QUALITY(min_entropy, error_code) \ + do { \ + if ((min_entropy) <= 0.0 || (min_entropy) > 8.0) { \ + return (error_code); \ + } \ + } while (0) + +// ============================================================================ +// CONFIGURATION VALIDATION +// ============================================================================ + +/** + * @brief Validate health test configuration + */ +static inline int validate_health_config( + uint32_t rct_cutoff, + uint32_t apt_cutoff, + uint32_t apt_window_size, + double min_entropy +) { + if (rct_cutoff < 2 || rct_cutoff > 10000) return 0; + if (apt_cutoff < 10 || apt_cutoff > 100000) return 0; + if (apt_window_size < 16 || apt_window_size > 65536) return 0; + if (min_entropy <= 0.0 || min_entropy > 8.0) return 0; + return 1; +} + +/** + * @brief Validate matrix dimensions + */ +static inline int validate_matrix_dims(size_t m, size_t n, size_t k) { + if (m == 0 || n == 0 || k == 0) return 0; + if (m > 65536 || n > 65536 || k > 65536) return 0; // Reasonable limits + return 1; +} + +/** + * @brief Validate number of qubits + */ +static inline int validate_num_qubits(size_t num_qubits, size_t max_qubits) { + return (num_qubits > 0 && num_qubits <= max_qubits); +} + +// ============================================================================ +// UTILITY VALIDATION +// ============================================================================ + +/** + * @brief Check if value is power of 2 + */ +static inline int is_power_of_2(uint64_t x) { + return x != 0 && (x & (x - 1)) == 0; +} + +/** + * @brief Validate array indices + */ +static inline int validate_array_indices( + const int *indices, + size_t num_indices, + size_t max_index +) { + if (!indices || num_indices == 0) return 0; + + for (size_t i = 0; i < num_indices; i++) { + if (indices[i] < 0 || (size_t)indices[i] >= max_index) { + return 0; + } + } + return 1; +} + +/** + * @brief Validate no duplicate indices in array + */ +static inline int validate_unique_indices(const int *indices, size_t num_indices) { + if (!indices || num_indices == 0) return 0; + + for (size_t i = 0; i < num_indices; i++) { + for (size_t j = i + 1; j < num_indices; j++) { + if (indices[i] == indices[j]) { + return 0; + } + } + } + return 1; +} + +#endif /* VALIDATION_H */ \ No newline at end of file diff --git a/vendor/quantum_rng/src/entropy/entropy_pool.c b/vendor/quantum_rng/src/entropy/entropy_pool.c new file mode 100644 index 0000000..3141568 --- /dev/null +++ b/vendor/quantum_rng/src/entropy/entropy_pool.c @@ -0,0 +1,499 @@ +#include "entropy_pool.h" +#include "../common/secure_memory.h" +#include "../common/validation.h" +#include +#include +#include +#include + +/** + * @file entropy_pool.c + * @brief High-performance entropy pool with background pre-generation + * + * Implements a thread-safe entropy pool that continuously generates and + * tests entropy in the background, providing near-zero latency for requests. + */ + +// ============================================================================ +// BACKGROUND WORKER THREAD +// ============================================================================ + +/** + * @brief Background thread function for continuous entropy generation + */ +static void* entropy_worker_thread(void *arg) { + entropy_pool_ctx_t *pool = (entropy_pool_ctx_t *)arg; + + uint8_t chunk[ENTROPY_POOL_CHUNK_SIZE]; + + while (1) { + // Check for shutdown signal + pthread_mutex_lock(&pool->pool_mutex); + if (pool->shutdown_requested) { + pthread_mutex_unlock(&pool->pool_mutex); + break; + } + + // Wait if pool is full + while (pool->pool_available >= pool->config.refill_threshold && + !pool->shutdown_requested) { + pthread_cond_wait(&pool->refill_cond, &pool->pool_mutex); + } + + if (pool->shutdown_requested) { + pthread_mutex_unlock(&pool->pool_mutex); + break; + } + + pthread_mutex_unlock(&pool->pool_mutex); + + // Generate entropy outside lock for better concurrency + entropy_error_t err = entropy_get_bytes(pool->entropy_ctx, chunk, sizeof(chunk)); + if (err != ENTROPY_SUCCESS) { + usleep(1000); // Back off on error + continue; + } + + // Run health tests on generated entropy. The health context is shared + // with the on-demand generation path, so serialize access to it. + pthread_mutex_lock(&pool->health_mutex); + health_error_t health_err = health_tests_run_batch( + pool->health_ctx, chunk, sizeof(chunk)); + pthread_mutex_unlock(&pool->health_mutex); + + if (health_err != HEALTH_SUCCESS) { + // Health test failed - discard this chunk + secure_memzero(chunk, sizeof(chunk)); + usleep(10000); // Back off more on health failure + continue; + } + + // Add tested entropy to pool + pthread_mutex_lock(&pool->pool_mutex); + + // Calculate space available + size_t space_available = pool->pool_size - pool->pool_available; + size_t bytes_to_add = (sizeof(chunk) < space_available) ? + sizeof(chunk) : space_available; + + if (bytes_to_add > 0) { + // Calculate write position with wraparound + size_t write_pos = pool->pool_used + pool->pool_available; + if (write_pos >= pool->pool_size) { + write_pos -= pool->pool_size; + } + + // Handle wraparound + if (write_pos + bytes_to_add <= pool->pool_size) { + memcpy(pool->pool_buffer + write_pos, chunk, bytes_to_add); + } else { + // Split copy + size_t first_part = pool->pool_size - write_pos; + memcpy(pool->pool_buffer + write_pos, chunk, first_part); + memcpy(pool->pool_buffer, chunk + first_part, bytes_to_add - first_part); + } + + pool->pool_available += bytes_to_add; + pool->stats.background_chunks++; + } + + pthread_mutex_unlock(&pool->pool_mutex); + + // Small delay to prevent spinning + usleep(100); + } + + secure_memzero(chunk, sizeof(chunk)); + return NULL; +} + +// ============================================================================ +// INITIALIZATION & CLEANUP +// ============================================================================ + +int entropy_pool_init(entropy_pool_ctx_t **ctx) { + entropy_pool_config_t config = { + .pool_size = ENTROPY_POOL_DEFAULT_SIZE, + .refill_threshold = ENTROPY_POOL_REFILL_THRESHOLD, + .chunk_size = ENTROPY_POOL_CHUNK_SIZE, + .enable_background_thread = 1, + .min_entropy = 4.0 + }; + + return entropy_pool_init_with_config(ctx, &config); +} + +int entropy_pool_init_with_config( + entropy_pool_ctx_t **ctx_out, + const entropy_pool_config_t *config +) { + VALIDATE_NOT_NULL(ctx_out, -1); + VALIDATE_NOT_NULL(config, -1); + + // Validate configuration + if (config->pool_size == 0 || config->pool_size > (100 * 1024 * 1024)) { + return -1; // Max 100MB pool + } + if (config->chunk_size == 0 || config->chunk_size > config->pool_size) { + return -1; + } + + // Allocate context + entropy_pool_ctx_t *ctx = calloc(1, sizeof(entropy_pool_ctx_t)); + if (!ctx) return -1; + + // Copy configuration + memcpy(&ctx->config, config, sizeof(entropy_pool_config_t)); + ctx->pool_size = config->pool_size; + + // Allocate pool buffer + ctx->pool_buffer = calloc(1, ctx->pool_size); + if (!ctx->pool_buffer) { + free(ctx); + return -1; + } + + // Initialize hardware entropy + ctx->entropy_ctx = calloc(1, sizeof(entropy_ctx_t)); + if (!ctx->entropy_ctx) { + secure_memzero(ctx->pool_buffer, ctx->pool_size); + free(ctx->pool_buffer); + free(ctx); + return -1; + } + + entropy_error_t err = entropy_init(ctx->entropy_ctx); + if (err != ENTROPY_SUCCESS) { + free(ctx->entropy_ctx); + secure_memzero(ctx->pool_buffer, ctx->pool_size); + free(ctx->pool_buffer); + free(ctx); + return -1; + } + + // Initialize health tests + ctx->health_ctx = calloc(1, sizeof(health_test_ctx_t)); + if (!ctx->health_ctx) { + entropy_free(ctx->entropy_ctx); + free(ctx->entropy_ctx); + secure_memzero(ctx->pool_buffer, ctx->pool_size); + free(ctx->pool_buffer); + free(ctx); + return -1; + } + + health_test_config_t health_config; + health_get_recommended_config(config->min_entropy, &health_config); + + health_error_t health_err = health_tests_init_custom(ctx->health_ctx, &health_config); + if (health_err != HEALTH_SUCCESS) { + free(ctx->health_ctx); + entropy_free(ctx->entropy_ctx); + free(ctx->entropy_ctx); + secure_memzero(ctx->pool_buffer, ctx->pool_size); + free(ctx->pool_buffer); + free(ctx); + return -1; + } + + // Initialize thread safety + if (pthread_mutex_init(&ctx->pool_mutex, NULL) != 0) { + health_tests_free(ctx->health_ctx); + free(ctx->health_ctx); + entropy_free(ctx->entropy_ctx); + free(ctx->entropy_ctx); + secure_memzero(ctx->pool_buffer, ctx->pool_size); + free(ctx->pool_buffer); + free(ctx); + return -1; + } + + if (pthread_mutex_init(&ctx->health_mutex, NULL) != 0) { + pthread_mutex_destroy(&ctx->pool_mutex); + health_tests_free(ctx->health_ctx); + free(ctx->health_ctx); + entropy_free(ctx->entropy_ctx); + free(ctx->entropy_ctx); + secure_memzero(ctx->pool_buffer, ctx->pool_size); + free(ctx->pool_buffer); + free(ctx); + return -1; + } + + if (pthread_cond_init(&ctx->refill_cond, NULL) != 0) { + pthread_mutex_destroy(&ctx->health_mutex); + pthread_mutex_destroy(&ctx->pool_mutex); + health_tests_free(ctx->health_ctx); + free(ctx->health_ctx); + entropy_free(ctx->entropy_ctx); + free(ctx->entropy_ctx); + secure_memzero(ctx->pool_buffer, ctx->pool_size); + free(ctx->pool_buffer); + free(ctx); + return -1; + } + + // Pre-fill pool with tested entropy + uint8_t startup_entropy[4096]; + err = entropy_get_bytes(ctx->entropy_ctx, startup_entropy, sizeof(startup_entropy)); + if (err == ENTROPY_SUCCESS) { + pthread_mutex_lock(&ctx->health_mutex); + health_err = health_tests_run_batch(ctx->health_ctx, startup_entropy, sizeof(startup_entropy)); + pthread_mutex_unlock(&ctx->health_mutex); + if (health_err == HEALTH_SUCCESS) { + memcpy(ctx->pool_buffer, startup_entropy, sizeof(startup_entropy)); + ctx->pool_available = sizeof(startup_entropy); + } + } + secure_memzero(startup_entropy, sizeof(startup_entropy)); + + // Start background thread if enabled + if (config->enable_background_thread) { + if (entropy_pool_start_background(ctx) != 0) { + pthread_cond_destroy(&ctx->refill_cond); + pthread_mutex_destroy(&ctx->health_mutex); + pthread_mutex_destroy(&ctx->pool_mutex); + health_tests_free(ctx->health_ctx); + free(ctx->health_ctx); + entropy_free(ctx->entropy_ctx); + free(ctx->entropy_ctx); + secure_memzero(ctx->pool_buffer, ctx->pool_size); + free(ctx->pool_buffer); + free(ctx); + return -1; + } + } + + *ctx_out = ctx; + return 0; +} + +void entropy_pool_free(entropy_pool_ctx_t *ctx) { + if (!ctx) return; + + // Stop background thread if running + if (ctx->background_running) { + entropy_pool_stop_background(ctx); + } + + // Destroy synchronization primitives + pthread_cond_destroy(&ctx->refill_cond); + pthread_mutex_destroy(&ctx->health_mutex); + pthread_mutex_destroy(&ctx->pool_mutex); + + // Free components + if (ctx->health_ctx) { + health_tests_free(ctx->health_ctx); + free(ctx->health_ctx); + } + + if (ctx->entropy_ctx) { + entropy_free(ctx->entropy_ctx); + free(ctx->entropy_ctx); + } + + // Securely zero and free pool buffer + if (ctx->pool_buffer) { + secure_memzero(ctx->pool_buffer, ctx->pool_size); + free(ctx->pool_buffer); + } + + // Zero context + secure_memzero(ctx, sizeof(*ctx)); + free(ctx); +} + +int entropy_pool_start_background(entropy_pool_ctx_t *ctx) { + VALIDATE_NOT_NULL(ctx, -1); + + if (ctx->background_running) { + return 0; // Already running + } + + ctx->shutdown_requested = 0; + + if (pthread_create(&ctx->background_thread, NULL, entropy_worker_thread, ctx) != 0) { + return -1; + } + + ctx->background_running = 1; + ctx->stats.background_active = 1; + + return 0; +} + +void entropy_pool_stop_background(entropy_pool_ctx_t *ctx) { + if (!ctx || !ctx->background_running) return; + + // Signal shutdown + pthread_mutex_lock(&ctx->pool_mutex); + ctx->shutdown_requested = 1; + pthread_cond_broadcast(&ctx->refill_cond); + pthread_mutex_unlock(&ctx->pool_mutex); + + // Wait for thread to exit + pthread_join(ctx->background_thread, NULL); + + ctx->background_running = 0; + ctx->stats.background_active = 0; +} + +// ============================================================================ +// ENTROPY RETRIEVAL +// ============================================================================ + +int entropy_pool_get_bytes( + entropy_pool_ctx_t *ctx, + uint8_t *buffer, + size_t size +) { + VALIDATE_NOT_NULL(ctx, -1); + VALIDATE_BUFFER(buffer, size, -1); + + pthread_mutex_lock(&ctx->pool_mutex); + + // Try to serve from pool first (cache hit) + if (ctx->pool_available >= size) { + // Copy from pool + size_t read_pos = ctx->pool_used; + + if (read_pos + size <= ctx->pool_size) { + // Simple copy + memcpy(buffer, ctx->pool_buffer + read_pos, size); + } else { + // Wraparound copy + size_t first_part = ctx->pool_size - read_pos; + memcpy(buffer, ctx->pool_buffer + read_pos, first_part); + memcpy(buffer + first_part, ctx->pool_buffer, size - first_part); + } + + ctx->pool_used += size; + if (ctx->pool_used >= ctx->pool_size) { + ctx->pool_used -= ctx->pool_size; + } + + ctx->pool_available -= size; + ctx->stats.cache_hits++; + ctx->stats.bytes_generated += size; + + // Signal refill if needed + if (ctx->pool_available < ctx->config.refill_threshold) { + pthread_cond_signal(&ctx->refill_cond); + } + + pthread_mutex_unlock(&ctx->pool_mutex); + return 0; + } + + // Cache miss - need to generate fresh entropy + ctx->stats.cache_misses++; + pthread_mutex_unlock(&ctx->pool_mutex); + + // Generate directly (bypassing pool for large requests) + entropy_error_t err = entropy_get_bytes(ctx->entropy_ctx, buffer, size); + if (err != ENTROPY_SUCCESS) { + return -1; + } + + // Test generated entropy. Serialize health_ctx access — the background + // worker thread runs the same tests on the shared context concurrently. + pthread_mutex_lock(&ctx->health_mutex); + health_error_t health_err = health_tests_run_batch(ctx->health_ctx, buffer, size); + pthread_mutex_unlock(&ctx->health_mutex); + if (health_err != HEALTH_SUCCESS) { + secure_memzero(buffer, size); + return -1; + } + + pthread_mutex_lock(&ctx->pool_mutex); + ctx->stats.bytes_generated += size; + ctx->stats.cache_misses++; + pthread_mutex_unlock(&ctx->pool_mutex); + + return 0; +} + +int entropy_pool_refill(entropy_pool_ctx_t *ctx) { + VALIDATE_NOT_NULL(ctx, -1); + + pthread_mutex_lock(&ctx->pool_mutex); + pthread_cond_signal(&ctx->refill_cond); + ctx->stats.refills_triggered++; + pthread_mutex_unlock(&ctx->pool_mutex); + + return 0; +} + +// ============================================================================ +// MONITORING +// ============================================================================ + +int entropy_pool_get_stats( + const entropy_pool_ctx_t *ctx, + entropy_pool_stats_t *stats +) { + VALIDATE_NOT_NULL(ctx, -1); + VALIDATE_NOT_NULL(stats, -1); + + pthread_mutex_lock((pthread_mutex_t*)&ctx->pool_mutex); + memcpy(stats, &ctx->stats, sizeof(*stats)); + stats->current_fill_level = ctx->pool_available; + pthread_mutex_unlock((pthread_mutex_t*)&ctx->pool_mutex); + + return 0; +} + +size_t entropy_pool_get_fill_level(const entropy_pool_ctx_t *ctx) { + if (!ctx) return 0; + + pthread_mutex_lock((pthread_mutex_t*)&ctx->pool_mutex); + size_t level = ctx->pool_available; + pthread_mutex_unlock((pthread_mutex_t*)&ctx->pool_mutex); + + return level; +} + +int entropy_pool_needs_refill(const entropy_pool_ctx_t *ctx) { + if (!ctx) return 0; + + pthread_mutex_lock((pthread_mutex_t*)&ctx->pool_mutex); + int needs = (ctx->pool_available < ctx->config.refill_threshold); + pthread_mutex_unlock((pthread_mutex_t*)&ctx->pool_mutex); + + return needs; +} + +void entropy_pool_print_stats(const entropy_pool_ctx_t *ctx) { + if (!ctx) return; + + entropy_pool_stats_t stats; + if (entropy_pool_get_stats(ctx, &stats) != 0) { + return; + } + + printf("=== Entropy Pool Statistics ===\n"); + printf("Configuration:\n"); + printf(" Pool size: %zu bytes\n", ctx->pool_size); + printf(" Refill threshold: %zu bytes\n", ctx->config.refill_threshold); + printf(" Chunk size: %zu bytes\n", ctx->config.chunk_size); + printf("\n"); + printf("Status:\n"); + printf(" Current fill level: %zu bytes (%.1f%%)\n", + stats.current_fill_level, + 100.0 * stats.current_fill_level / ctx->pool_size); + printf(" Background active: %s\n", stats.background_active ? "Yes" : "No"); + printf("\n"); + printf("Performance:\n"); + printf(" Bytes generated: %llu\n", (unsigned long long)stats.bytes_generated); + printf(" Cache hits: %llu\n", (unsigned long long)stats.cache_hits); + printf(" Cache misses: %llu\n", (unsigned long long)stats.cache_misses); + + if (stats.cache_hits + stats.cache_misses > 0) { + double hit_rate = 100.0 * stats.cache_hits / (stats.cache_hits + stats.cache_misses); + printf(" Cache hit rate: %.1f%%\n", hit_rate); + } + + printf(" Refills triggered: %llu\n", (unsigned long long)stats.refills_triggered); + printf(" Background chunks: %llu\n", (unsigned long long)stats.background_chunks); + printf("\n"); +} \ No newline at end of file diff --git a/vendor/quantum_rng/src/entropy/entropy_pool.h b/vendor/quantum_rng/src/entropy/entropy_pool.h new file mode 100644 index 0000000..5c74066 --- /dev/null +++ b/vendor/quantum_rng/src/entropy/entropy_pool.h @@ -0,0 +1,208 @@ +#ifndef ENTROPY_POOL_H +#define ENTROPY_POOL_H + +#include +#include +#include +#include "hardware_entropy.h" +#include "../health/health_tests.h" + +/** + * @file entropy_pool.h + * @brief Advanced entropy pooling with background pre-generation + * + * Provides a high-performance entropy pool that: + * - Pre-generates tested entropy in background thread + * - Reduces latency for entropy requests + * - Maintains continuous health testing + * - Thread-safe access with lock-free reads when possible + * + * Performance benefits: + * - Near-zero latency for cached entropy + * - Continuous background collection + * - Automatic refilling + * - Burst request handling + */ + +// ============================================================================ +// CONFIGURATION +// ============================================================================ + +#define ENTROPY_POOL_DEFAULT_SIZE (64 * 1024) // 64KB pool +#define ENTROPY_POOL_REFILL_THRESHOLD (16 * 1024) // Refill at 25% +#define ENTROPY_POOL_CHUNK_SIZE 4096 // Generate 4KB chunks + +/** + * @brief Entropy pool configuration + */ +typedef struct { + size_t pool_size; /**< Total pool size in bytes */ + size_t refill_threshold; /**< Trigger refill when below this */ + size_t chunk_size; /**< Size of generation chunks */ + int enable_background_thread; /**< Enable background generation */ + double min_entropy; /**< Min-entropy for health tests */ +} entropy_pool_config_t; + +/** + * @brief Entropy pool statistics + */ +typedef struct { + uint64_t bytes_generated; /**< Total bytes generated */ + uint64_t cache_hits; /**< Requests served from cache */ + uint64_t cache_misses; /**< Requests requiring generation */ + uint64_t refills_triggered; /**< Number of refill operations */ + uint64_t background_chunks; /**< Chunks generated in background */ + size_t current_fill_level; /**< Current pool fill level */ + int background_active; /**< Background thread status */ +} entropy_pool_stats_t; + +/** + * @brief Entropy pool context + */ +typedef struct { + // Configuration + entropy_pool_config_t config; + + // Pool storage + uint8_t *pool_buffer; /**< Entropy pool buffer */ + size_t pool_size; /**< Total pool size */ + size_t pool_used; /**< Bytes consumed from pool */ + size_t pool_available; /**< Bytes available in pool */ + + // Thread safety + pthread_mutex_t pool_mutex; /**< Pool access mutex */ + pthread_mutex_t health_mutex; /**< Serializes health_ctx access across the worker + on-demand paths */ + pthread_cond_t refill_cond; /**< Refill condition variable */ + pthread_t background_thread; /**< Background generation thread */ + int background_running; /**< Background thread running flag */ + int shutdown_requested; /**< Shutdown flag */ + + // Components + entropy_ctx_t *entropy_ctx; /**< Hardware entropy context */ + health_test_ctx_t *health_ctx; /**< Health test context */ + + // Statistics + entropy_pool_stats_t stats; +} entropy_pool_ctx_t; + +// ============================================================================ +// POOL MANAGEMENT +// ============================================================================ + +/** + * @brief Initialize entropy pool with default configuration + * + * @param ctx Output context pointer + * @return 0 on success, -1 on error + */ +int entropy_pool_init(entropy_pool_ctx_t **ctx); + +/** + * @brief Initialize entropy pool with custom configuration + * + * @param ctx Output context pointer + * @param config Custom configuration + * @return 0 on success, -1 on error + */ +int entropy_pool_init_with_config( + entropy_pool_ctx_t **ctx, + const entropy_pool_config_t *config +); + +/** + * @brief Free entropy pool context + * + * Stops background thread and securely erases pool. + * + * @param ctx Pool context + */ +void entropy_pool_free(entropy_pool_ctx_t *ctx); + +/** + * @brief Start background entropy generation + * + * Launches background thread that continuously refills the pool. + * + * @param ctx Pool context + * @return 0 on success, -1 on error + */ +int entropy_pool_start_background(entropy_pool_ctx_t *ctx); + +/** + * @brief Stop background entropy generation + * + * @param ctx Pool context + */ +void entropy_pool_stop_background(entropy_pool_ctx_t *ctx); + +// ============================================================================ +// ENTROPY RETRIEVAL +// ============================================================================ + +/** + * @brief Get entropy from pool + * + * Retrieves health-tested entropy from pool. If pool is empty, + * generates fresh entropy immediately. + * + * @param ctx Pool context + * @param buffer Output buffer + * @param size Number of bytes requested + * @return 0 on success, -1 on error + */ +int entropy_pool_get_bytes( + entropy_pool_ctx_t *ctx, + uint8_t *buffer, + size_t size +); + +/** + * @brief Refill entropy pool + * + * Manually triggers pool refill with fresh tested entropy. + * + * @param ctx Pool context + * @return 0 on success, -1 on error + */ +int entropy_pool_refill(entropy_pool_ctx_t *ctx); + +// ============================================================================ +// MONITORING +// ============================================================================ + +/** + * @brief Get pool statistics + * + * @param ctx Pool context + * @param stats Output statistics structure + * @return 0 on success, -1 on error + */ +int entropy_pool_get_stats( + const entropy_pool_ctx_t *ctx, + entropy_pool_stats_t *stats +); + +/** + * @brief Get current fill level + * + * @param ctx Pool context + * @return Number of bytes available in pool + */ +size_t entropy_pool_get_fill_level(const entropy_pool_ctx_t *ctx); + +/** + * @brief Check if pool needs refill + * + * @param ctx Pool context + * @return 1 if refill needed, 0 otherwise + */ +int entropy_pool_needs_refill(const entropy_pool_ctx_t *ctx); + +/** + * @brief Print pool statistics + * + * @param ctx Pool context + */ +void entropy_pool_print_stats(const entropy_pool_ctx_t *ctx); + +#endif /* ENTROPY_POOL_H */ \ No newline at end of file diff --git a/vendor/quantum_rng/src/entropy/hardware_entropy.c b/vendor/quantum_rng/src/entropy/hardware_entropy.c new file mode 100644 index 0000000..986f06e --- /dev/null +++ b/vendor/quantum_rng/src/entropy/hardware_entropy.c @@ -0,0 +1,765 @@ +#if defined(_WIN32) +/* Enable rand_s() (backed by the OS CSPRNG / RtlGenRandom); must be defined + * before . */ +#define _CRT_RAND_S +#endif + +#include "hardware_entropy.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Platform-specific includes +#ifdef __linux__ +#include +#endif + +// CPU feature detection +#ifdef __x86_64__ +#include +#elif defined(__aarch64__) + // ARM feature detection - platform specific + #ifdef __linux__ + #include + #ifndef HWCAP_RNG + #define HWCAP_RNG (1 << 16) // ARMv8.5-A Random Number + #endif + #elif defined(__APPLE__) + #include + #endif +#endif + +// ============================================================================ +// CPU INSTRUCTION DETECTION +// ============================================================================ + +int rdrand_available(void) { +#ifdef __x86_64__ + unsigned int eax, ebx, ecx, edx; + if (__get_cpuid(1, &eax, &ebx, &ecx, &edx)) { + return (ecx & (1 << 30)) != 0; // RDRAND is bit 30 of ECX + } +#endif + return 0; +} + +int rdseed_available(void) { +#ifdef __x86_64__ + unsigned int eax, ebx, ecx, edx; + if (__get_cpuid_count(7, 0, &eax, &ebx, &ecx, &edx)) { + return (ebx & (1 << 18)) != 0; // RDSEED is bit 18 of EBX + } +#endif + return 0; +} + +// ============================================================================ +// ARM HARDWARE INSTRUCTIONS (ARMv8.5-A+) +// ============================================================================ + +#ifdef __aarch64__ + +/** + * @brief Check if ARM RNDR instruction is available + */ +int rndr_available(void) { + #ifdef __linux__ + // Linux: Use getauxval + unsigned long hwcaps = getauxval(AT_HWCAP); + return (hwcaps & HWCAP_RNG) != 0; + #elif defined(__APPLE__) + // macOS: Feature detection via sysctl + // Apple Silicon M1/M2 have RNDR support + int has_feature = 0; + size_t size = sizeof(has_feature); + + // Try to detect via hw.optional.armv8_5_a + if (sysctlbyname("hw.optional.arm.FEAT_RNG", &has_feature, &size, NULL, 0) == 0) { + return has_feature; + } + + // Fallback: Assume available on Apple Silicon (M1+) + // This is safe as the instruction will fail gracefully if not supported + return 1; + #else + // Other ARM platforms: Assume not available, fail gracefully + return 0; + #endif +} + +/** + * @brief Get entropy via ARM RNDR instruction + * + * RNDR (Random Number) - reseeded from hardware TRNG + * Returns conditioned random number + */ +int rndr_get_uint64(uint64_t *value) { + if (!rndr_available()) return 0; + + uint64_t result; + int ok; + + // Retry up to 10 times as recommended + for (int i = 0; i < 10; i++) { + __asm__ volatile( + "mrs %0, s3_3_c2_c4_0\n" // RNDR register + "cset %w1, ne" + : "=r"(result), "=r"(ok) + : + : "cc" + ); + + if (ok) { + *value = result; + return 1; + } + } + + return 0; +} + +/** + * @brief Get entropy via ARM RNDRRS instruction + * + * RNDRRS (Random Number Reseeded) - guaranteed reseed from TRNG + * Higher quality but slower than RNDR + */ +int rndrrs_get_uint64(uint64_t *value) { + if (!rndr_available()) return 0; + + uint64_t result; + int ok; + + // RNDRRS may take longer, retry up to 100 times + for (int i = 0; i < 100; i++) { + __asm__ volatile( + "mrs %0, s3_3_c2_c4_1\n" // RNDRRS register + "cset %w1, ne" + : "=r"(result), "=r"(ok) + : + : "cc" + ); + + if (ok) { + *value = result; + return 1; + } + } + + return 0; +} + +#else +// Stub implementations for non-ARM platforms +int rndr_available(void) { return 0; } +int rndr_get_uint64(uint64_t *value) { (void)value; return 0; } +int rndrrs_get_uint64(uint64_t *value) { (void)value; return 0; } +#endif + +// ============================================================================ +// HARDWARE INSTRUCTIONS +// ============================================================================ + +int rdrand_get_uint64(uint64_t *value) { +#ifdef __x86_64__ + if (!rdrand_available()) return 0; + + unsigned char ok; + // Retry up to 10 times as recommended by Intel + for (int i = 0; i < 10; i++) { + __asm__ volatile( + "rdrand %0; setc %1" + : "=r" (*value), "=qm" (ok) + : + : "cc" + ); + if (ok) return 1; + } +#endif + return 0; +} + +int rdseed_get_uint64(uint64_t *value) { +#ifdef __x86_64__ + if (!rdseed_available()) return 0; + + unsigned char ok; + // RDSEED may take longer, retry up to 100 times + for (int i = 0; i < 100; i++) { + __asm__ volatile( + "rdseed %0; setc %1" + : "=r" (*value), "=qm" (ok) + : + : "cc" + ); + if (ok) return 1; + } +#endif + return 0; +} + +// ============================================================================ +// SYSTEM ENTROPY (getrandom) +// ============================================================================ + +ssize_t entropy_getrandom(uint8_t *buffer, size_t size, unsigned int flags) { +#if defined(_WIN32) + // Windows: rand_s() draws from the OS cryptographic RNG (RtlGenRandom). + (void)flags; + size_t i = 0; + while (i < size) { + unsigned int r; + if (rand_s(&r) != 0) { errno = EIO; return -1; } + size_t chunk = (size - i < sizeof(r)) ? (size - i) : sizeof(r); + memcpy(buffer + i, &r, chunk); + i += chunk; + } + return (ssize_t)size; +#elif defined(__linux__) + #ifdef SYS_getrandom + return syscall(SYS_getrandom, buffer, size, flags); + #else + errno = ENOSYS; + return -1; + #endif +#else + errno = ENOSYS; + return -1; +#endif +} + +// ============================================================================ +// JITTER ENTROPY +// ============================================================================ + +// High-resolution timer for jitter collection +static inline uint64_t get_timer_cycles(void) { +#ifdef __x86_64__ + unsigned int lo, hi; + __asm__ volatile("rdtsc" : "=a"(lo), "=d"(hi)); + return ((uint64_t)hi << 32) | lo; +#else + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return (uint64_t)ts.tv_sec * 1000000000ULL + ts.tv_nsec; +#endif +} + +// Cryptographic-quality mixing based on SipHash-inspired design +static inline uint64_t sipround(uint64_t v0, uint64_t v1, uint64_t v2, uint64_t v3) { + v0 += v1; v2 += v3; + v1 = (v1 << 13) | (v1 >> 51); + v3 = (v3 << 16) | (v3 >> 48); + v1 ^= v0; v3 ^= v2; + v0 = (v0 << 32) | (v0 >> 32); + v2 += v1; v0 += v3; + v1 = (v1 << 17) | (v1 >> 47); + v3 = (v3 << 21) | (v3 >> 43); + v1 ^= v2; v3 ^= v0; + v2 = (v2 << 32) | (v2 >> 32); + return v0 ^ v1 ^ v2 ^ v3; +} + +// Enhanced cryptographic mixing with multiple rounds +static inline uint64_t crypto_mix(uint64_t x, uint64_t key) { + uint64_t v0 = 0x736f6d6570736575ULL ^ key; + uint64_t v1 = 0x646f72616e646f6dULL ^ key; + uint64_t v2 = 0x6c7967656e657261ULL ^ x; + uint64_t v3 = 0x7465646279746573ULL ^ x; + + // Multiple mixing rounds for thorough diffusion + for (int i = 0; i < 4; i++) { + v0 = sipround(v0, v1, v2, v3); + v1 = sipround(v1, v2, v3, v0); + v2 = sipround(v2, v3, v0, v1); + v3 = sipround(v3, v0, v1, v2); + } + + return v0 ^ v1 ^ v2 ^ v3; +} + +// Von Neumann debiasing - removes bias from binary stream +static inline int debias_bit_pair(uint8_t b1, uint8_t b2) { + if (b1 != b2) { + return b1; // Return first bit if different (removes bias) + } + return -1; // Skip if same (no information) +} + +// Extract entropy from timing delta using multiple techniques +static inline uint64_t extract_timing_entropy(uint64_t delta, uint64_t prev_delta, uint64_t state) { + // Use multiple entropy sources from timing: + // 1. Absolute value (timing uncertainty) + uint64_t abs_entropy = delta; + + // 2. Second derivative (timing jitter acceleration) + uint64_t jitter = delta ^ prev_delta; + + // 3. LSBs (most uncertain bits) + uint64_t lsb_entropy = delta & 0xFFULL; + + // 4. XOR fold to concentrate entropy + uint64_t folded = delta ^ (delta >> 32); + folded ^= (folded >> 16); + folded ^= (folded >> 8); + + // Combine all sources with cryptographic mixing + uint64_t combined = crypto_mix(abs_entropy, state) ^ + crypto_mix(jitter, state + 1) ^ + crypto_mix(lsb_entropy, state + 2) ^ + crypto_mix(folded, state + 3); + + return combined; +} + +entropy_error_t entropy_jitter(uint8_t *buffer, size_t size) { + if (!buffer || size == 0) { + return ENTROPY_ERROR_INVALID_PARAM; + } + + // Initialize state with high-resolution time + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + uint64_t state = ((uint64_t)ts.tv_sec << 32) | ts.tv_nsec; + + uint64_t accumulator = 0; + uint64_t prev_delta = 0; + size_t bytes_generated = 0; + int bit_buffer = 0; + int bits_in_buffer = 0; + + while (bytes_generated < size) { + // Collect timing measurements with memory barrier + uint64_t t1 = get_timer_cycles(); + + // CPU-intensive operation with unpredictable timing + volatile uint64_t dummy = state; + for (int i = 0; i < 50; i++) { + dummy = crypto_mix(dummy, i); + } + state ^= dummy; // Feed back to prevent optimization + + uint64_t t2 = get_timer_cycles(); + + // Additional timing source: system call + clock_gettime(CLOCK_MONOTONIC, &ts); + uint64_t sys_time = ((uint64_t)ts.tv_nsec) ^ get_timer_cycles(); + + uint64_t delta = t2 - t1; + + // Extract entropy from timing delta + uint64_t entropy = extract_timing_entropy(delta, prev_delta, state); + entropy ^= crypto_mix(sys_time, state + delta); + + prev_delta = delta; + state = crypto_mix(state, entropy); + + // Apply von Neumann debiasing to LSBs + for (int i = 0; i < 8 && bytes_generated < size; i++) { + uint8_t b1 = (entropy >> (i * 8)) & 1; + uint8_t b2 = (entropy >> (i * 8 + 1)) & 1; + + int debiased = debias_bit_pair(b1, b2); + if (debiased >= 0) { + bit_buffer = (bit_buffer << 1) | debiased; + bits_in_buffer++; + + if (bits_in_buffer >= 8) { + buffer[bytes_generated++] = (uint8_t)bit_buffer; + bit_buffer = 0; + bits_in_buffer = 0; + } + } + } + + // If debiasing is too slow, fall back to direct extraction + if (bytes_generated < size && bits_in_buffer == 0) { + accumulator ^= entropy; + accumulator = crypto_mix(accumulator, state++); + + for (int i = 0; i < 8 && bytes_generated < size; i++) { + buffer[bytes_generated++] = (accumulator >> (i * 8)) & 0xFF; + } + } + } + + // Secure cleanup + state = 0; + accumulator = 0; + prev_delta = 0; + + return ENTROPY_SUCCESS; +} + +// ============================================================================ +// INITIALIZATION +// ============================================================================ + +entropy_error_t entropy_init(entropy_ctx_t *ctx) { + if (!ctx) return ENTROPY_ERROR_INVALID_PARAM; + + memset(ctx, 0, sizeof(*ctx)); + ctx->dev_random_fd = -1; + ctx->dev_urandom_fd = -1; + + // Detect available entropy sources + ctx->caps.has_rdrand = rdrand_available(); + ctx->caps.has_rdseed = rdseed_available(); + + // ARM entropy support: DISABLED by default for compatibility + // The RNDR system register encoding is not stable across all macOS versions + // and Apple Silicon variants, causing illegal instruction faults. + // + // PRODUCTION RECOMMENDATION: Use /dev/random on ARM - it's: + // - Hardware-backed (Apple's TRNG feeds kernel entropy pool) + // - Guaranteed available + // - No illegal instruction risk + // - Excellent entropy quality (8.0 bits/byte) + // + // If you need RNDR specifically: + // 1. Test on your exact macOS version and chip (M1/M2/M3) + // 2. Add signal handler for SIGILL to catch illegal instruction + // 3. Enable conditionally based on successful runtime test + // + // For now, /dev/random provides excellent entropy on Apple Silicon + #if 0 && defined(__aarch64__) + if (rndr_available()) { + // Code intentionally disabled - see comment above + ctx->caps.has_rdrand = 1; + ctx->caps.has_rdseed = 1; + } + #endif + + ctx->caps.has_jitter = 1; // Always available + + // Test getrandom availability + uint8_t test_byte; + ctx->caps.has_getrandom = (entropy_getrandom(&test_byte, 1, 0) > 0); + + // Try to open the POSIX random devices. These do not exist on native + // Windows, where getrandom() (rand_s) is the OS entropy source instead. +#ifndef _WIN32 + ctx->dev_random_fd = open("/dev/random", O_RDONLY | O_NONBLOCK); + ctx->caps.has_dev_random = (ctx->dev_random_fd >= 0); + + ctx->dev_urandom_fd = open("/dev/urandom", O_RDONLY); + ctx->caps.has_dev_urandom = (ctx->dev_urandom_fd >= 0); +#else + ctx->dev_random_fd = -1; + ctx->dev_urandom_fd = -1; + ctx->caps.has_dev_random = 0; + ctx->caps.has_dev_urandom = 0; +#endif + + // Determine preferred source (best quality first) + if (ctx->caps.has_rdseed) { + ctx->caps.preferred_source = ENTROPY_SOURCE_RDSEED; + } else if (ctx->caps.has_rdrand) { + ctx->caps.preferred_source = ENTROPY_SOURCE_RDRAND; + } else if (ctx->caps.has_getrandom) { + ctx->caps.preferred_source = ENTROPY_SOURCE_GETRANDOM; + } else if (ctx->caps.has_dev_random) { + ctx->caps.preferred_source = ENTROPY_SOURCE_DEV_RANDOM; + } else if (ctx->caps.has_dev_urandom) { + ctx->caps.preferred_source = ENTROPY_SOURCE_DEV_URANDOM; + } else if (ctx->caps.has_jitter) { + ctx->caps.preferred_source = ENTROPY_SOURCE_JITTER; + } else { + ctx->caps.preferred_source = ENTROPY_SOURCE_NONE; + return ENTROPY_ERROR_NO_SOURCE; + } + + return ENTROPY_SUCCESS; +} + +void entropy_free(entropy_ctx_t *ctx) { + if (!ctx) return; + + if (ctx->dev_random_fd >= 0) { + close(ctx->dev_random_fd); + ctx->dev_random_fd = -1; + } + + if (ctx->dev_urandom_fd >= 0) { + close(ctx->dev_urandom_fd); + ctx->dev_urandom_fd = -1; + } + + // Zero sensitive data + memset(ctx, 0, sizeof(*ctx)); +} + +entropy_capabilities_t entropy_get_capabilities(const entropy_ctx_t *ctx) { + if (!ctx) { + entropy_capabilities_t empty = {0}; + return empty; + } + return ctx->caps; +} + +// ============================================================================ +// DEVICE ENTROPY (/dev/random, /dev/urandom) +// ============================================================================ + +ssize_t entropy_dev_random(entropy_ctx_t *ctx, uint8_t *buffer, size_t size, int blocking) { + if (!ctx || !buffer || size == 0) return -1; + if (ctx->dev_random_fd < 0) return -1; + + // Set blocking mode if requested (POSIX only; this path is unreachable on + // Windows, where dev_random_fd is always -1). +#ifndef _WIN32 + if (blocking) { + int flags = fcntl(ctx->dev_random_fd, F_GETFL, 0); + fcntl(ctx->dev_random_fd, F_SETFL, flags & ~O_NONBLOCK); + } +#else + (void)blocking; +#endif + + ssize_t total = 0; + while (total < (ssize_t)size) { + ssize_t n = read(ctx->dev_random_fd, buffer + total, size - total); + if (n < 0) { + if (errno == EINTR) continue; + if (errno == EAGAIN && !blocking) break; + return -1; + } + if (n == 0) break; + total += n; + } + + return total; +} + +ssize_t entropy_dev_urandom(entropy_ctx_t *ctx, uint8_t *buffer, size_t size) { + if (!ctx || !buffer || size == 0) return -1; + if (ctx->dev_urandom_fd < 0) return -1; + + ssize_t total = 0; + while (total < (ssize_t)size) { + ssize_t n = read(ctx->dev_urandom_fd, buffer + total, size - total); + if (n < 0) { + if (errno == EINTR) continue; + return -1; + } + if (n == 0) break; + total += n; + } + + return total; +} + +// ============================================================================ +// ENTROPY COLLECTION +// ============================================================================ + +entropy_error_t entropy_get_bytes_from_source( + entropy_ctx_t *ctx, + uint8_t *buffer, + size_t size, + entropy_source_type_t source +) { + if (!ctx || !buffer || size == 0) { + return ENTROPY_ERROR_INVALID_PARAM; + } + + switch (source) { + case ENTROPY_SOURCE_RDSEED: { + if (!ctx->caps.has_rdseed) return ENTROPY_ERROR_NO_SOURCE; + + size_t offset = 0; + while (offset < size) { + uint64_t value; + int success = 0; + + #ifdef __aarch64__ + // Use ARM RNDRRS (equivalent to RDSEED) + success = rndrrs_get_uint64(&value); + #else + // Use x86 RDSEED + success = rdseed_get_uint64(&value); + #endif + + if (!success) { + ctx->rdseed_failures++; + return ENTROPY_ERROR_INSUFFICIENT; + } + + size_t copy_size = (size - offset) < 8 ? (size - offset) : 8; + memcpy(buffer + offset, &value, copy_size); + offset += copy_size; + } + ctx->last_source = ENTROPY_SOURCE_RDSEED; + return ENTROPY_SUCCESS; + } + + case ENTROPY_SOURCE_RDRAND: { + if (!ctx->caps.has_rdrand) return ENTROPY_ERROR_NO_SOURCE; + + size_t offset = 0; + while (offset < size) { + uint64_t value; + int success = 0; + + #ifdef __aarch64__ + // Use ARM RNDR (equivalent to RDRAND) + success = rndr_get_uint64(&value); + #else + // Use x86 RDRAND + success = rdrand_get_uint64(&value); + #endif + + if (!success) { + ctx->rdrand_failures++; + return ENTROPY_ERROR_INSUFFICIENT; + } + + size_t copy_size = (size - offset) < 8 ? (size - offset) : 8; + memcpy(buffer + offset, &value, copy_size); + offset += copy_size; + } + ctx->last_source = ENTROPY_SOURCE_RDRAND; + return ENTROPY_SUCCESS; + } + + case ENTROPY_SOURCE_GETRANDOM: { + if (!ctx->caps.has_getrandom) return ENTROPY_ERROR_NO_SOURCE; + + ssize_t n = entropy_getrandom(buffer, size, 0); + if (n < 0 || (size_t)n < size) { + return ENTROPY_ERROR_SYSCALL; + } + ctx->last_source = ENTROPY_SOURCE_GETRANDOM; + return ENTROPY_SUCCESS; + } + + case ENTROPY_SOURCE_DEV_RANDOM: { + if (!ctx->caps.has_dev_random) return ENTROPY_ERROR_NO_SOURCE; + + ssize_t n = entropy_dev_random(ctx, buffer, size, 0); + if (n < 0 || (size_t)n < size) { + return ENTROPY_ERROR_INSUFFICIENT; + } + ctx->last_source = ENTROPY_SOURCE_DEV_RANDOM; + return ENTROPY_SUCCESS; + } + + case ENTROPY_SOURCE_DEV_URANDOM: { + if (!ctx->caps.has_dev_urandom) return ENTROPY_ERROR_NO_SOURCE; + + ssize_t n = entropy_dev_urandom(ctx, buffer, size); + if (n < 0 || (size_t)n < size) { + return ENTROPY_ERROR_SYSCALL; + } + ctx->last_source = ENTROPY_SOURCE_DEV_URANDOM; + return ENTROPY_SUCCESS; + } + + case ENTROPY_SOURCE_JITTER: { + entropy_error_t err = entropy_jitter(buffer, size); + if (err != ENTROPY_SUCCESS) return err; + ctx->last_source = ENTROPY_SOURCE_JITTER; + return ENTROPY_SUCCESS; + } + + default: + return ENTROPY_ERROR_NO_SOURCE; + } +} + +entropy_error_t entropy_get_bytes(entropy_ctx_t *ctx, uint8_t *buffer, size_t size) { + if (!ctx || !buffer || size == 0) { + return ENTROPY_ERROR_INVALID_PARAM; + } + + // Try sources in priority order + entropy_source_type_t sources[] = { + ENTROPY_SOURCE_RDSEED, + ENTROPY_SOURCE_RDRAND, + ENTROPY_SOURCE_GETRANDOM, + ENTROPY_SOURCE_DEV_RANDOM, + ENTROPY_SOURCE_DEV_URANDOM, + ENTROPY_SOURCE_JITTER + }; + + for (size_t i = 0; i < sizeof(sources) / sizeof(sources[0]); i++) { + entropy_error_t err = entropy_get_bytes_from_source(ctx, buffer, size, sources[i]); + if (err == ENTROPY_SUCCESS) { + ctx->total_bytes += size; + return ENTROPY_SUCCESS; + } + } + + return ENTROPY_ERROR_NO_SOURCE; +} + +entropy_error_t entropy_get_uint64(entropy_ctx_t *ctx, uint64_t *value) { + if (!ctx || !value) return ENTROPY_ERROR_INVALID_PARAM; + + return entropy_get_bytes(ctx, (uint8_t*)value, sizeof(*value)); +} + +// ============================================================================ +// QUALITY ASSESSMENT +// ============================================================================ + +double entropy_quality_estimate(entropy_source_type_t source) { + switch (source) { + case ENTROPY_SOURCE_RDSEED: + return 8.0; // Full entropy (conditioned by hardware) + case ENTROPY_SOURCE_RDRAND: + return 8.0; // Full entropy (hardware TRNG) + case ENTROPY_SOURCE_GETRANDOM: + return 8.0; // Kernel certified + case ENTROPY_SOURCE_DEV_RANDOM: + return 8.0; // Kernel entropy pool + case ENTROPY_SOURCE_DEV_URANDOM: + return 7.8; // High quality + case ENTROPY_SOURCE_JITTER: + return 5.0; // Variable, conservative estimate + default: + return 0.0; + } +} + +const char* entropy_source_name(entropy_source_type_t source) { + switch (source) { + case ENTROPY_SOURCE_RDSEED: return "RDSEED"; + case ENTROPY_SOURCE_RDRAND: return "RDRAND"; + case ENTROPY_SOURCE_GETRANDOM: return "getrandom()"; + case ENTROPY_SOURCE_DEV_RANDOM: return "/dev/random"; + case ENTROPY_SOURCE_DEV_URANDOM: return "/dev/urandom"; + case ENTROPY_SOURCE_JITTER: return "CPU Jitter"; + case ENTROPY_SOURCE_FALLBACK: return "Fallback"; + case ENTROPY_SOURCE_NONE: return "None"; + default: return "Unknown"; + } +} + +void entropy_print_stats(const entropy_ctx_t *ctx) { + if (!ctx) return; + + printf("=== Entropy Source Statistics ===\n"); + printf("Available sources:\n"); + printf(" RDSEED: %s\n", ctx->caps.has_rdseed ? "Yes" : "No"); + printf(" RDRAND: %s\n", ctx->caps.has_rdrand ? "Yes" : "No"); + printf(" getrandom(): %s\n", ctx->caps.has_getrandom ? "Yes" : "No"); + printf(" /dev/random: %s\n", ctx->caps.has_dev_random ? "Yes" : "No"); + printf(" /dev/urandom: %s\n", ctx->caps.has_dev_urandom ? "Yes" : "No"); + printf(" CPU Jitter: %s\n", ctx->caps.has_jitter ? "Yes" : "No"); + printf("\n"); + printf("Preferred source: %s\n", entropy_source_name(ctx->caps.preferred_source)); + printf("Last used source: %s\n", entropy_source_name(ctx->last_source)); + printf("Total bytes collected: %lu\n", ctx->total_bytes); + printf("RDRAND failures: %lu\n", ctx->rdrand_failures); + printf("RDSEED failures: %lu\n", ctx->rdseed_failures); + printf("Quality estimate: %.1f bits/byte\n", + entropy_quality_estimate(ctx->caps.preferred_source)); +} \ No newline at end of file diff --git a/vendor/quantum_rng/src/entropy/hardware_entropy.h b/vendor/quantum_rng/src/entropy/hardware_entropy.h new file mode 100644 index 0000000..f72bb68 --- /dev/null +++ b/vendor/quantum_rng/src/entropy/hardware_entropy.h @@ -0,0 +1,270 @@ +#ifndef HARDWARE_ENTROPY_H +#define HARDWARE_ENTROPY_H + +#include +#include +#include + +/** + * @file hardware_entropy.h + * @brief Hardware entropy source abstraction layer + * + * Provides unified interface to multiple hardware entropy sources: + * - Intel RDRAND (CPU-based TRNG) + * - Intel RDSEED (Enhanced TRNG with conditioning) + * - /dev/random (kernel entropy pool) + * - /dev/urandom (non-blocking kernel entropy) + * - getrandom() syscall (modern Linux) + * - CPU jitter entropy (timing uncertainty) + * + * Implements fallback chain for maximum portability and security. + */ + +/** + * @brief Entropy source types + */ +typedef enum { + ENTROPY_SOURCE_RDSEED, // Intel RDSEED instruction + ENTROPY_SOURCE_RDRAND, // Intel RDRAND instruction + ENTROPY_SOURCE_GETRANDOM, // getrandom() syscall + ENTROPY_SOURCE_DEV_RANDOM, // /dev/random device + ENTROPY_SOURCE_DEV_URANDOM, // /dev/urandom device + ENTROPY_SOURCE_JITTER, // CPU jitter timing + ENTROPY_SOURCE_FALLBACK, // System fallback + ENTROPY_SOURCE_NONE // No source available +} entropy_source_type_t; + +/** + * @brief Entropy source capabilities + */ +typedef struct { + int has_rdrand; // RDRAND available + int has_rdseed; // RDSEED available + int has_getrandom; // getrandom() available + int has_dev_random; // /dev/random available + int has_dev_urandom; // /dev/urandom available + int has_jitter; // Jitter source available + entropy_source_type_t preferred_source; +} entropy_capabilities_t; + +/** + * @brief Entropy context for state management + */ +typedef struct { + entropy_capabilities_t caps; + int dev_random_fd; // /dev/random file descriptor + int dev_urandom_fd; // /dev/urandom file descriptor + uint64_t rdrand_failures; // RDRAND failure count + uint64_t rdseed_failures; // RDSEED failure count + uint64_t total_bytes; // Total entropy collected + entropy_source_type_t last_source; // Last successful source +} entropy_ctx_t; + +/** + * @brief Error codes + */ +typedef enum { + ENTROPY_SUCCESS = 0, + ENTROPY_ERROR_NO_SOURCE = -1, + ENTROPY_ERROR_INSUFFICIENT = -2, + ENTROPY_ERROR_SYSCALL = -3, + ENTROPY_ERROR_TIMEOUT = -4, + ENTROPY_ERROR_INVALID_PARAM = -5 +} entropy_error_t; + +// ============================================================================ +// INITIALIZATION +// ============================================================================ + +/** + * @brief Initialize entropy context + * + * Detects available entropy sources and initializes state. + * + * @param ctx Entropy context to initialize + * @return ENTROPY_SUCCESS or error code + */ +entropy_error_t entropy_init(entropy_ctx_t *ctx); + +/** + * @brief Free entropy context resources + * + * @param ctx Entropy context to free + */ +void entropy_free(entropy_ctx_t *ctx); + +/** + * @brief Get entropy capabilities + * + * @param ctx Entropy context + * @return Capabilities structure + */ +entropy_capabilities_t entropy_get_capabilities(const entropy_ctx_t *ctx); + +// ============================================================================ +// ENTROPY COLLECTION +// ============================================================================ + +/** + * @brief Collect entropy from best available source + * + * Tries sources in priority order: + * 1. RDSEED (highest quality) + * 2. RDRAND (high quality) + * 3. getrandom() (kernel quality) + * 4. /dev/random (blocking, highest quality) + * 5. /dev/urandom (non-blocking) + * 6. Jitter (timing-based) + * + * @param ctx Entropy context + * @param buffer Output buffer + * @param size Number of bytes to collect + * @return ENTROPY_SUCCESS or error code + */ +entropy_error_t entropy_get_bytes(entropy_ctx_t *ctx, uint8_t *buffer, size_t size); + +/** + * @brief Collect entropy from specific source + * + * @param ctx Entropy context + * @param buffer Output buffer + * @param size Number of bytes to collect + * @param source Specific entropy source to use + * @return ENTROPY_SUCCESS or error code + */ +entropy_error_t entropy_get_bytes_from_source( + entropy_ctx_t *ctx, + uint8_t *buffer, + size_t size, + entropy_source_type_t source +); + +/** + * @brief Get 64-bit entropy value + * + * @param ctx Entropy context + * @param value Output value + * @return ENTROPY_SUCCESS or error code + */ +entropy_error_t entropy_get_uint64(entropy_ctx_t *ctx, uint64_t *value); + +// ============================================================================ +// HARDWARE INSTRUCTIONS +// ============================================================================ + +/** + * @brief Get entropy via RDRAND instruction + * + * @param value Output value + * @return 1 on success, 0 on failure + */ +int rdrand_get_uint64(uint64_t *value); + +/** + * @brief Get entropy via RDSEED instruction + * + * @param value Output value + * @return 1 on success, 0 on failure + */ +int rdseed_get_uint64(uint64_t *value); + +/** + * @brief Check if RDRAND is available + * + * @return 1 if available, 0 otherwise + */ +int rdrand_available(void); + +/** + * @brief Check if RDSEED is available + * + * @return 1 if available, 0 otherwise + */ +int rdseed_available(void); + +// ============================================================================ +// SYSTEM ENTROPY +// ============================================================================ + +/** + * @brief Get entropy via getrandom() syscall + * + * @param buffer Output buffer + * @param size Number of bytes + * @param flags getrandom flags + * @return Number of bytes read, or -1 on error + */ +ssize_t entropy_getrandom(uint8_t *buffer, size_t size, unsigned int flags); + +/** + * @brief Read from /dev/random + * + * @param ctx Entropy context + * @param buffer Output buffer + * @param size Number of bytes + * @param blocking 1 for blocking, 0 for non-blocking + * @return Number of bytes read, or -1 on error + */ +ssize_t entropy_dev_random(entropy_ctx_t *ctx, uint8_t *buffer, size_t size, int blocking); + +/** + * @brief Read from /dev/urandom + * + * @param ctx Entropy context + * @param buffer Output buffer + * @param size Number of bytes + * @return Number of bytes read, or -1 on error + */ +ssize_t entropy_dev_urandom(entropy_ctx_t *ctx, uint8_t *buffer, size_t size); + +// ============================================================================ +// JITTER ENTROPY +// ============================================================================ + +/** + * @brief Get entropy from CPU timing jitter + * + * Measures timing variations in CPU execution to extract entropy. + * Based on CPU timing uncertainty principle. + * + * @param buffer Output buffer + * @param size Number of bytes to generate + * @return ENTROPY_SUCCESS or error code + */ +entropy_error_t entropy_jitter(uint8_t *buffer, size_t size); + +// ============================================================================ +// QUALITY ASSESSMENT +// ============================================================================ + +/** + * @brief Estimate entropy quality from source + * + * Returns estimated bits of entropy per byte: + * - RDSEED: 8.0 bits/byte (full entropy) + * - RDRAND: 8.0 bits/byte (full entropy) + * - /dev/random: 8.0 bits/byte (kernel certified) + * - /dev/urandom: 7.5-8.0 bits/byte (high quality) + * - Jitter: 4.0-6.0 bits/byte (variable quality) + * + * @param source Entropy source type + * @return Estimated bits per byte + */ +double entropy_quality_estimate(entropy_source_type_t source); + +/** + * @brief Get source name string + * + * @param source Entropy source type + * @return Human-readable source name + */ +const char* entropy_source_name(entropy_source_type_t source); + +/** + * @brief Print entropy statistics + * + * @param ctx Entropy context + */ +void entropy_print_stats(const entropy_ctx_t *ctx); + +#endif /* HARDWARE_ENTROPY_H */ \ No newline at end of file diff --git a/vendor/quantum_rng/src/health/health_tests.c b/vendor/quantum_rng/src/health/health_tests.c new file mode 100644 index 0000000..2214940 --- /dev/null +++ b/vendor/quantum_rng/src/health/health_tests.c @@ -0,0 +1,396 @@ +#include "health_tests.h" +#include "../common/secure_memory.h" +#include +#include +#include +#include + +// ============================================================================ +// CONFIGURATION CALCULATIONS +// ============================================================================ + +uint32_t health_calculate_rct_cutoff(double min_entropy) { + // NIST SP 800-90B formula: C = ceil(1 + (-log2(2^-30) / H)) + // Simplified: C = ceil(1 + (30 / H)) + if (min_entropy <= 0.0 || min_entropy > 8.0) { + return 31; // Conservative default for H >= 4 bits + } + + double cutoff = 1.0 + (30.0 / min_entropy); + return (uint32_t)ceil(cutoff); +} + +uint32_t health_calculate_apt_cutoff(double min_entropy, uint32_t window_size) { + // Critical value from binomial distribution + // For window W and probability p = 2^-H, find C such that: + // P(X > C) < 2^-30 where X ~ Binomial(W, p) + + if (min_entropy <= 0.0 || min_entropy > 8.0 || window_size == 0) { + return 354; // Conservative default for H=4, W=512 + } + + double p = pow(2.0, -min_entropy); + double mean = window_size * p; + double stddev = sqrt(window_size * p * (1.0 - p)); + + // Use normal approximation with continuity correction + // For 2^-30 probability, need ~6.9 standard deviations + double z = 6.9; // Critical value for 2^-30 confidence + double cutoff = mean + z * stddev + 0.5; // +0.5 for continuity correction + + return (uint32_t)ceil(cutoff); +} + +void health_get_recommended_config(double min_entropy, health_test_config_t *config) { + if (!config) return; + + // Ensure min_entropy is in valid range + if (min_entropy < 1.0) min_entropy = 4.0; // Conservative default + if (min_entropy > 8.0) min_entropy = 8.0; + + config->min_entropy_estimate = min_entropy; + config->rct_cutoff = health_calculate_rct_cutoff(min_entropy); + config->apt_window_size = 512; // NIST recommended + config->apt_cutoff = health_calculate_apt_cutoff(min_entropy, config->apt_window_size); + config->startup_test_samples = 1024; // NIST minimum +} + +int health_validate_config(const health_test_config_t *config) { + if (!config) return 0; + + // Check reasonable bounds + if (config->rct_cutoff < 2 || config->rct_cutoff > 1000) return 0; + if (config->apt_cutoff < 10 || config->apt_cutoff > 10000) return 0; + if (config->apt_window_size < 16 || config->apt_window_size > 65536) return 0; + if (config->startup_test_samples < 100) return 0; + if (config->min_entropy_estimate <= 0.0 || config->min_entropy_estimate > 8.0) return 0; + + return 1; +} + +// ============================================================================ +// INITIALIZATION +// ============================================================================ + +health_error_t health_tests_init(health_test_ctx_t *ctx) { + if (!ctx) return HEALTH_ERROR_INVALID_PARAM; + + memset(ctx, 0, sizeof(*ctx)); + + // Set conservative defaults (H_min = 4 bits/sample) + health_get_recommended_config(4.0, &ctx->config); + + // Allocate APT window buffer + ctx->stats.apt_window_buffer = calloc(ctx->config.apt_window_size, sizeof(uint8_t)); + if (!ctx->stats.apt_window_buffer) { + return HEALTH_ERROR_INVALID_PARAM; + } + + ctx->stats.tests_enabled = 1; + + return HEALTH_SUCCESS; +} + +health_error_t health_tests_init_custom(health_test_ctx_t *ctx, const health_test_config_t *config) { + if (!ctx || !config) return HEALTH_ERROR_INVALID_PARAM; + if (!health_validate_config(config)) return HEALTH_ERROR_INVALID_PARAM; + + memset(ctx, 0, sizeof(*ctx)); + ctx->config = *config; + + // Allocate APT window buffer + ctx->stats.apt_window_buffer = calloc(ctx->config.apt_window_size, sizeof(uint8_t)); + if (!ctx->stats.apt_window_buffer) { + return HEALTH_ERROR_INVALID_PARAM; + } + + ctx->stats.tests_enabled = 1; + + return HEALTH_SUCCESS; +} + +void health_tests_free(health_test_ctx_t *ctx) { + if (!ctx) return; + + if (ctx->stats.apt_window_buffer) { + // Secure erase before freeing + secure_memzero(ctx->stats.apt_window_buffer, ctx->config.apt_window_size); + free(ctx->stats.apt_window_buffer); + ctx->stats.apt_window_buffer = NULL; + } + + secure_memzero(ctx, sizeof(*ctx)); +} + +void health_tests_reset(health_test_ctx_t *ctx) { + if (!ctx) return; + + // Save configuration and buffer + health_test_config_t saved_config = ctx->config; + uint8_t *saved_buffer = ctx->stats.apt_window_buffer; + void (*saved_callback)(health_error_t, void*) = ctx->failure_callback; + void *saved_user_data = ctx->callback_user_data; + + // Clear statistics + memset(&ctx->stats, 0, sizeof(ctx->stats)); + + // Restore + ctx->config = saved_config; + ctx->stats.apt_window_buffer = saved_buffer; + ctx->failure_callback = saved_callback; + ctx->callback_user_data = saved_user_data; + ctx->stats.tests_enabled = 1; + + // Clear APT buffer + if (ctx->stats.apt_window_buffer) { + secure_memzero(ctx->stats.apt_window_buffer, ctx->config.apt_window_size); + } +} + +// ============================================================================ +// INDIVIDUAL TESTS +// ============================================================================ + +health_error_t health_test_rct(health_test_ctx_t *ctx, uint8_t sample) { + if (!ctx) return HEALTH_ERROR_INVALID_PARAM; + if (!ctx->stats.tests_enabled) return HEALTH_SUCCESS; + + // First sample initializes the test + if (ctx->stats.samples_tested == 0) { + ctx->stats.rct_last_sample = sample; + ctx->stats.rct_current_count = 1; + return HEALTH_SUCCESS; + } + + // Check if sample repeats + if (sample == ctx->stats.rct_last_sample) { + ctx->stats.rct_current_count++; + + // Test failure if count exceeds cutoff + if (ctx->stats.rct_current_count >= ctx->config.rct_cutoff) { + ctx->stats.rct_failures++; + ctx->stats.total_failures++; + + // Invoke callback if set + if (ctx->failure_callback) { + ctx->failure_callback(HEALTH_ERROR_RCT_FAILURE, ctx->callback_user_data); + } + + // Reset counter but keep last sample + ctx->stats.rct_current_count = 1; + + return HEALTH_ERROR_RCT_FAILURE; + } + } else { + // Different sample, reset counter + ctx->stats.rct_last_sample = sample; + ctx->stats.rct_current_count = 1; + } + + return HEALTH_SUCCESS; +} + +health_error_t health_test_apt(health_test_ctx_t *ctx, uint8_t sample) { + if (!ctx) return HEALTH_ERROR_INVALID_PARAM; + if (!ctx->stats.tests_enabled) return HEALTH_SUCCESS; + if (!ctx->stats.apt_window_buffer) return HEALTH_ERROR_NOT_INITIALIZED; + + // First sample in window + if (ctx->stats.apt_window_pos == 0) { + ctx->stats.apt_first_sample = sample; + ctx->stats.apt_current_count = 1; + ctx->stats.apt_window_buffer[0] = sample; + ctx->stats.apt_window_pos = 1; + return HEALTH_SUCCESS; + } + + // Add sample to window. Bounds-guarded: apt_window_pos is always in + // [0, apt_window_size) under correct single-threaded use, but guarding the + // write makes an out-of-bounds store impossible even if a caller feeds this + // context from more than one thread. + if (ctx->stats.apt_window_pos < ctx->config.apt_window_size) { + ctx->stats.apt_window_buffer[ctx->stats.apt_window_pos] = sample; + } + + // Count occurrences of first sample + if (sample == ctx->stats.apt_first_sample) { + ctx->stats.apt_current_count++; + } + + ctx->stats.apt_window_pos++; + + // Window full - perform test + if (ctx->stats.apt_window_pos >= ctx->config.apt_window_size) { + // Check if count exceeds cutoff + if (ctx->stats.apt_current_count >= ctx->config.apt_cutoff) { + ctx->stats.apt_failures++; + ctx->stats.total_failures++; + + // Invoke callback if set + if (ctx->failure_callback) { + ctx->failure_callback(HEALTH_ERROR_APT_FAILURE, ctx->callback_user_data); + } + + // Reset window + ctx->stats.apt_window_pos = 0; + ctx->stats.apt_current_count = 0; + + return HEALTH_ERROR_APT_FAILURE; + } + + // Test passed, reset window + ctx->stats.apt_window_pos = 0; + ctx->stats.apt_current_count = 0; + } + + return HEALTH_SUCCESS; +} + +// ============================================================================ +// TEST EXECUTION +// ============================================================================ + +health_error_t health_tests_run(health_test_ctx_t *ctx, uint8_t sample) { + if (!ctx) return HEALTH_ERROR_INVALID_PARAM; + if (!ctx->stats.tests_enabled) return HEALTH_SUCCESS; + + ctx->stats.samples_tested++; + + // Run RCT + health_error_t rct_result = health_test_rct(ctx, sample); + if (rct_result != HEALTH_SUCCESS) { + return rct_result; + } + + // Run APT + health_error_t apt_result = health_test_apt(ctx, sample); + if (apt_result != HEALTH_SUCCESS) { + return apt_result; + } + + return HEALTH_SUCCESS; +} + +health_error_t health_tests_run_batch(health_test_ctx_t *ctx, const uint8_t *samples, size_t num_samples) { + if (!ctx || !samples) return HEALTH_ERROR_INVALID_PARAM; + if (!ctx->stats.tests_enabled) return HEALTH_SUCCESS; + + for (size_t i = 0; i < num_samples; i++) { + health_error_t result = health_tests_run(ctx, samples[i]); + if (result != HEALTH_SUCCESS) { + return result; // Fail on first error + } + } + + return HEALTH_SUCCESS; +} + +health_error_t health_tests_startup(health_test_ctx_t *ctx, const uint8_t *samples, size_t num_samples) { + if (!ctx || !samples) return HEALTH_ERROR_INVALID_PARAM; + if (num_samples < ctx->config.startup_test_samples) return HEALTH_ERROR_INVALID_PARAM; + + // Reset tests for startup + health_tests_reset(ctx); + + // Run tests on startup samples + health_error_t result = health_tests_run_batch(ctx, samples, num_samples); + + if (result == HEALTH_SUCCESS) { + ctx->stats.startup_complete = 1; + } else { + ctx->stats.startup_failures++; + ctx->stats.startup_complete = 0; + } + + return result; +} + +// ============================================================================ +// STATISTICS & MONITORING +// ============================================================================ + +health_test_stats_t health_tests_get_stats(const health_test_ctx_t *ctx) { + health_test_stats_t empty = {0}; + if (!ctx) return empty; + return ctx->stats; +} + +int health_tests_startup_complete(const health_test_ctx_t *ctx) { + if (!ctx) return 0; + return ctx->stats.startup_complete; +} + +void health_tests_set_callback( + health_test_ctx_t *ctx, + void (*callback)(health_error_t error, void *user_data), + void *user_data +) { + if (!ctx) return; + ctx->failure_callback = callback; + ctx->callback_user_data = user_data; +} + +void health_tests_set_enabled(health_test_ctx_t *ctx, int enabled) { + if (!ctx) return; + ctx->stats.tests_enabled = enabled ? 1 : 0; +} + +void health_tests_print_stats(const health_test_ctx_t *ctx) { + if (!ctx) return; + + printf("=== Health Test Statistics ===\n"); + printf("Configuration:\n"); + printf(" Min-entropy: %.2f bits/sample\n", ctx->config.min_entropy_estimate); + printf(" RCT cutoff: %u\n", ctx->config.rct_cutoff); + printf(" APT cutoff: %u\n", ctx->config.apt_cutoff); + printf(" APT window: %u samples\n", ctx->config.apt_window_size); + printf("\n"); + printf("Status:\n"); + printf(" Tests enabled: %s\n", ctx->stats.tests_enabled ? "Yes" : "No"); + printf(" Startup complete: %s\n", ctx->stats.startup_complete ? "Yes" : "No"); + printf("\n"); + printf("Statistics:\n"); + printf(" Samples tested: %lu\n", ctx->stats.samples_tested); + printf(" Total failures: %lu\n", ctx->stats.total_failures); + printf(" RCT failures: %lu\n", ctx->stats.rct_failures); + printf(" APT failures: %lu\n", ctx->stats.apt_failures); + printf(" Startup failures: %lu\n", ctx->stats.startup_failures); + + if (ctx->stats.samples_tested > 0) { + double failure_rate = (double)ctx->stats.total_failures / ctx->stats.samples_tested * 100.0; + printf(" Failure rate: %.6f%%\n", failure_rate); + } + + printf("\n"); + printf("Current RCT state:\n"); + printf(" Last sample: 0x%02X\n", ctx->stats.rct_last_sample); + printf(" Current count: %u / %u\n", ctx->stats.rct_current_count, ctx->config.rct_cutoff); + printf("\n"); + printf("Current APT state:\n"); + printf(" First sample: 0x%02X\n", ctx->stats.apt_first_sample); + printf(" Current count: %u / %u\n", ctx->stats.apt_current_count, ctx->config.apt_cutoff); + printf(" Window position: %u / %u\n", ctx->stats.apt_window_pos, ctx->config.apt_window_size); +} + +// ============================================================================ +// UTILITY FUNCTIONS +// ============================================================================ + +const char* health_error_string(health_error_t error) { + switch (error) { + case HEALTH_SUCCESS: + return "Success"; + case HEALTH_ERROR_RCT_FAILURE: + return "Repetition Count Test failure - entropy source may be stuck"; + case HEALTH_ERROR_APT_FAILURE: + return "Adaptive Proportion Test failure - loss of entropy detected"; + case HEALTH_ERROR_STARTUP_FAILURE: + return "Startup test failure - entropy source not functioning correctly"; + case HEALTH_ERROR_INVALID_PARAM: + return "Invalid parameter"; + case HEALTH_ERROR_NOT_INITIALIZED: + return "Health tests not initialized"; + default: + return "Unknown error"; + } +} \ No newline at end of file diff --git a/vendor/quantum_rng/src/health/health_tests.h b/vendor/quantum_rng/src/health/health_tests.h new file mode 100644 index 0000000..9a1ba36 --- /dev/null +++ b/vendor/quantum_rng/src/health/health_tests.h @@ -0,0 +1,323 @@ +#ifndef HEALTH_TESTS_H +#define HEALTH_TESTS_H + +#include +#include + +/** + * @file health_tests.h + * @brief NIST SP 800-90B health tests for entropy sources + * + * Implements continuous health testing as required by NIST SP 800-90B + * for cryptographic random number generators: + * + * 1. Repetition Count Test (RCT) - detects stuck-at faults + * 2. Adaptive Proportion Test (APT) - detects loss of entropy + * 3. Startup Tests - validates initial entropy source behavior + * + * These tests are mandatory for FIPS 140-3 compliance and ensure + * the entropy source is functioning correctly. + */ + +/** + * @brief Health test error codes + */ +typedef enum { + HEALTH_SUCCESS = 0, + HEALTH_ERROR_RCT_FAILURE = -1, // Repetition count test failed + HEALTH_ERROR_APT_FAILURE = -2, // Adaptive proportion test failed + HEALTH_ERROR_STARTUP_FAILURE = -3, // Startup test failed + HEALTH_ERROR_INVALID_PARAM = -4, // Invalid parameters + HEALTH_ERROR_NOT_INITIALIZED = -5 // Context not initialized +} health_error_t; + +/** + * @brief Health test configuration + * + * Parameters are derived from NIST SP 800-90B Section 4. + * Values depend on entropy source min-entropy estimate. + */ +typedef struct { + // Repetition Count Test (RCT) + uint32_t rct_cutoff; // Maximum allowed repetitions + + // Adaptive Proportion Test (APT) + uint32_t apt_cutoff; // Maximum allowed count in window + uint32_t apt_window_size; // Window size (typically 512 or 1024) + + // Startup test + uint32_t startup_test_samples; // Samples for startup test + + // Min-entropy estimate (bits per sample) + double min_entropy_estimate; // Conservative entropy estimate +} health_test_config_t; + +/** + * @brief Health test statistics + */ +typedef struct { + // Test results + uint64_t rct_failures; // RCT failure count + uint64_t apt_failures; // APT failure count + uint64_t startup_failures; // Startup failure count + + // Current state + uint64_t samples_tested; // Total samples tested + uint64_t total_failures; // Total test failures + + // RCT state + uint8_t rct_last_sample; // Last sample for RCT + uint32_t rct_current_count; // Current repetition count + + // APT state + uint8_t apt_first_sample; // First sample in window + uint32_t apt_current_count; // Count of first_sample in window + uint32_t apt_window_pos; // Position in window + uint8_t *apt_window_buffer; // Window buffer for APT + + // Status + int startup_complete; // 1 if startup tests passed + int tests_enabled; // 1 if tests are active +} health_test_stats_t; + +/** + * @brief Health test context + */ +typedef struct { + health_test_config_t config; + health_test_stats_t stats; + + // Callback for test failures + void (*failure_callback)(health_error_t error, void *user_data); + void *callback_user_data; +} health_test_ctx_t; + +// ============================================================================ +// INITIALIZATION +// ============================================================================ + +/** + * @brief Initialize health test context with default parameters + * + * Sets conservative defaults based on NIST SP 800-90B recommendations: + * - RCT cutoff: 31 (for H_min ≥ 4 bits/sample) + * - APT cutoff: 354 (for H_min ≥ 4 bits/sample, W=512) + * - APT window: 512 samples + * + * @param ctx Health test context to initialize + * @return HEALTH_SUCCESS or error code + */ +health_error_t health_tests_init(health_test_ctx_t *ctx); + +/** + * @brief Initialize with custom configuration + * + * @param ctx Health test context + * @param config Custom configuration + * @return HEALTH_SUCCESS or error code + */ +health_error_t health_tests_init_custom(health_test_ctx_t *ctx, const health_test_config_t *config); + +/** + * @brief Free health test resources + * + * @param ctx Health test context + */ +void health_tests_free(health_test_ctx_t *ctx); + +/** + * @brief Reset health test statistics + * + * @param ctx Health test context + */ +void health_tests_reset(health_test_ctx_t *ctx); + +// ============================================================================ +// TEST EXECUTION +// ============================================================================ + +/** + * @brief Run startup health tests + * + * Performs initial testing of entropy source with larger sample size. + * Must pass before allowing normal operation. + * + * @param ctx Health test context + * @param samples Sample data + * @param num_samples Number of samples (typically ≥1024) + * @return HEALTH_SUCCESS if tests pass, error code if failure + */ +health_error_t health_tests_startup(health_test_ctx_t *ctx, const uint8_t *samples, size_t num_samples); + +/** + * @brief Test single sample (continuous testing) + * + * Runs RCT and APT on each sample during normal operation. + * This function should be called for every entropy sample. + * + * @param ctx Health test context + * @param sample Sample to test + * @return HEALTH_SUCCESS if tests pass, error code if failure + */ +health_error_t health_tests_run(health_test_ctx_t *ctx, uint8_t sample); + +/** + * @brief Test multiple samples (batch mode) + * + * More efficient for testing multiple samples at once. + * + * @param ctx Health test context + * @param samples Array of samples + * @param num_samples Number of samples + * @return HEALTH_SUCCESS if all tests pass, error code on first failure + */ +health_error_t health_tests_run_batch(health_test_ctx_t *ctx, const uint8_t *samples, size_t num_samples); + +// ============================================================================ +// INDIVIDUAL TESTS +// ============================================================================ + +/** + * @brief Repetition Count Test (RCT) + * + * Detects stuck-at faults by counting consecutive identical samples. + * Fails if count exceeds cutoff (typically 31 for H_min ≥ 4). + * + * Formula: C = ceil(1 + (-log₂(2⁻ᴴ) / H)) + * where H is min-entropy per sample. + * + * @param ctx Health test context + * @param sample Current sample + * @return HEALTH_SUCCESS or HEALTH_ERROR_RCT_FAILURE + */ +health_error_t health_test_rct(health_test_ctx_t *ctx, uint8_t sample); + +/** + * @brief Adaptive Proportion Test (APT) + * + * Detects loss of entropy by counting occurrences of first sample + * in a sliding window. Fails if count exceeds cutoff. + * + * Formula: C = critbinom(W, 2⁻ᴴ, 2⁻³⁰) + * where W is window size, H is min-entropy. + * + * @param ctx Health test context + * @param sample Current sample + * @return HEALTH_SUCCESS or HEALTH_ERROR_APT_FAILURE + */ +health_error_t health_test_apt(health_test_ctx_t *ctx, uint8_t sample); + +// ============================================================================ +// CONFIGURATION HELPERS +// ============================================================================ + +/** + * @brief Calculate RCT cutoff from min-entropy + * + * Uses NIST SP 800-90B formula: + * C = ceil(1 + (-log₂(2⁻ᴴ) / H)) + * + * @param min_entropy Min-entropy per sample in bits + * @return RCT cutoff value + */ +uint32_t health_calculate_rct_cutoff(double min_entropy); + +/** + * @brief Calculate APT cutoff from min-entropy + * + * Uses critical value from binomial distribution with + * probability 2⁻ᴴ and confidence level 2⁻³⁰. + * + * @param min_entropy Min-entropy per sample in bits + * @param window_size APT window size + * @return APT cutoff value + */ +uint32_t health_calculate_apt_cutoff(double min_entropy, uint32_t window_size); + +/** + * @brief Get recommended configuration for entropy estimate + * + * Returns NIST-compliant configuration for given min-entropy. + * + * @param min_entropy Estimated min-entropy (bits/sample) + * @param config Output configuration + */ +void health_get_recommended_config(double min_entropy, health_test_config_t *config); + +// ============================================================================ +// STATISTICS & MONITORING +// ============================================================================ + +/** + * @brief Get test statistics + * + * @param ctx Health test context + * @return Statistics structure + */ +health_test_stats_t health_tests_get_stats(const health_test_ctx_t *ctx); + +/** + * @brief Check if startup tests passed + * + * @param ctx Health test context + * @return 1 if passed, 0 otherwise + */ +int health_tests_startup_complete(const health_test_ctx_t *ctx); + +/** + * @brief Print health test statistics + * + * @param ctx Health test context + */ +void health_tests_print_stats(const health_test_ctx_t *ctx); + +/** + * @brief Set failure callback + * + * Callback is invoked whenever a test fails, allowing custom + * handling (logging, alerts, shutdown, etc.) + * + * @param ctx Health test context + * @param callback Callback function + * @param user_data User data passed to callback + */ +void health_tests_set_callback( + health_test_ctx_t *ctx, + void (*callback)(health_error_t error, void *user_data), + void *user_data +); + +/** + * @brief Enable/disable tests + * + * Allows temporary disabling of tests (e.g., during initialization). + * Tests should be enabled for normal operation. + * + * @param ctx Health test context + * @param enabled 1 to enable, 0 to disable + */ +void health_tests_set_enabled(health_test_ctx_t *ctx, int enabled); + +// ============================================================================ +// UTILITY FUNCTIONS +// ============================================================================ + +/** + * @brief Get error string + * + * @param error Health error code + * @return Human-readable error description + */ +const char* health_error_string(health_error_t error); + +/** + * @brief Validate configuration parameters + * + * Checks if configuration values are reasonable and NIST-compliant. + * + * @param config Configuration to validate + * @return 1 if valid, 0 otherwise + */ +int health_validate_config(const health_test_config_t *config); + +#endif /* HEALTH_TESTS_H */ \ No newline at end of file diff --git a/vendor/quantum_rng/src/profiling/performance_monitor.c b/vendor/quantum_rng/src/profiling/performance_monitor.c new file mode 100644 index 0000000..682fd43 --- /dev/null +++ b/vendor/quantum_rng/src/profiling/performance_monitor.c @@ -0,0 +1,280 @@ +#include "performance_monitor.h" +#include "../common/secure_memory.h" +#include +#include +#include +#include + +// Platform-specific high-resolution timing +#ifdef __x86_64__ + static inline uint64_t get_cycles(void) { + return __builtin_ia32_rdtsc(); + } +#elif defined(__aarch64__) + static inline uint64_t get_cycles(void) { + uint64_t val; + __asm__ volatile("mrs %0, cntvct_el0" : "=r"(val)); + return val; + } +#else + #include + static inline uint64_t get_cycles(void) { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return ts.tv_sec * 1000000000ULL + ts.tv_nsec; + } +#endif + +/** + * @file performance_monitor.c + * @brief Real-time performance monitoring for Quantum RNG + * + * Provides low-overhead performance tracking with: + * - Sub-microsecond timing precision + * - Operation breakdowns + * - Throughput measurement + * - Latency histograms + */ + +// ============================================================================ +// CONTEXT MANAGEMENT +// ============================================================================ + +int perf_monitor_init(perf_monitor_ctx_t **ctx_out) { + if (!ctx_out) return -1; + + perf_monitor_ctx_t *ctx = calloc(1, sizeof(perf_monitor_ctx_t)); + if (!ctx) return -1; + + // Initialize timing + ctx->start_time = get_cycles(); + ctx->min_latency_cycles = UINT64_MAX; + + // Get CPU frequency for time conversion (approximate) + #ifdef __linux__ + // Try to read from /proc/cpuinfo + FILE *f = fopen("/proc/cpuinfo", "r"); + if (f) { + char line[256]; + while (fgets(line, sizeof(line), f)) { + if (strncmp(line, "cpu MHz", 7) == 0) { + sscanf(line, "cpu MHz : %lf", &ctx->cpu_mhz); + break; + } + } + fclose(f); + } + #endif + + // Fallback: Assume 2.5 GHz (will be calibrated during runtime) + if (ctx->cpu_mhz == 0.0) { + ctx->cpu_mhz = 2500.0; + } + + *ctx_out = ctx; + return 0; +} + +void perf_monitor_free(perf_monitor_ctx_t *ctx) { + if (!ctx) return; + secure_memzero(ctx, sizeof(*ctx)); + free(ctx); +} + +void perf_monitor_reset(perf_monitor_ctx_t *ctx) { + if (!ctx) return; + + // Save CPU frequency + double saved_mhz = ctx->cpu_mhz; + + // Reset all stats + secure_memzero(ctx, sizeof(*ctx)); + + // Restore + ctx->cpu_mhz = saved_mhz; + ctx->start_time = get_cycles(); + ctx->min_latency_cycles = UINT64_MAX; +} + +// ============================================================================ +// TIMING OPERATIONS +// ============================================================================ + +void perf_monitor_start_operation(perf_monitor_ctx_t *ctx, perf_operation_t op) { + if (!ctx || op >= PERF_OP_MAX) return; + + ctx->current_op = op; + ctx->op_start_cycles = get_cycles(); +} + +void perf_monitor_end_operation(perf_monitor_ctx_t *ctx) { + if (!ctx) return; + + uint64_t end_cycles = get_cycles(); + uint64_t elapsed = end_cycles - ctx->op_start_cycles; + + // Update totals + ctx->total_operations++; + ctx->total_cycles += elapsed; + + // Update operation-specific counters + switch (ctx->current_op) { + case PERF_OP_ENTROPY_COLLECTION: + ctx->entropy_collection_cycles += elapsed; + ctx->entropy_operations++; + break; + case PERF_OP_HEALTH_TEST: + ctx->health_test_cycles += elapsed; + ctx->health_operations++; + break; + case PERF_OP_QUANTUM_MIXING: + ctx->quantum_mixing_cycles += elapsed; + ctx->quantum_operations++; + break; + case PERF_OP_OUTPUT_GENERATION: + ctx->output_generation_cycles += elapsed; + ctx->output_operations++; + break; + default: + break; + } + + // Update min/max latency + if (elapsed < ctx->min_latency_cycles) { + ctx->min_latency_cycles = elapsed; + } + if (elapsed > ctx->max_latency_cycles) { + ctx->max_latency_cycles = elapsed; + } + + // Update latency histogram (20 buckets, logarithmic scale) + int bucket = 0; + uint64_t threshold = 1000; // Start at 1000 cycles + while (bucket < 19 && elapsed >= threshold) { + bucket++; + threshold *= 2; // Logarithmic buckets + } + ctx->latency_histogram[bucket]++; +} + +void perf_monitor_record_bytes(perf_monitor_ctx_t *ctx, size_t bytes) { + if (!ctx) return; + + ctx->bytes_processed += bytes; + + // Calculate throughput + uint64_t elapsed_cycles = get_cycles() - ctx->start_time; + if (elapsed_cycles > 0) { + // Convert cycles to seconds + double elapsed_seconds = elapsed_cycles / (ctx->cpu_mhz * 1000000.0); + if (elapsed_seconds > 0.0) { + double mbytes = ctx->bytes_processed / (1024.0 * 1024.0); + ctx->current_throughput_mbps = mbytes / elapsed_seconds; + + if (ctx->current_throughput_mbps > ctx->peak_throughput_mbps) { + ctx->peak_throughput_mbps = ctx->current_throughput_mbps; + } + } + } +} + +// ============================================================================ +// STATISTICS +// ============================================================================ + +void perf_monitor_get_stats(const perf_monitor_ctx_t *ctx, perf_stats_t *stats) { + if (!ctx || !stats) return; + + memset(stats, 0, sizeof(*stats)); + + // Basic stats + stats->total_operations = ctx->total_operations; + stats->bytes_processed = ctx->bytes_processed; + + // Calculate average latency + if (ctx->total_operations > 0) { + stats->avg_latency_cycles = ctx->total_cycles / ctx->total_operations; + stats->avg_latency_ns = (double)stats->avg_latency_cycles / ctx->cpu_mhz * 1000.0; + } + + // Min/max latency + stats->min_latency_cycles = ctx->min_latency_cycles; + stats->max_latency_cycles = ctx->max_latency_cycles; + stats->min_latency_ns = (double)stats->min_latency_cycles / ctx->cpu_mhz * 1000.0; + stats->max_latency_ns = (double)stats->max_latency_cycles / ctx->cpu_mhz * 1000.0; + + // Throughput + stats->current_throughput_mbps = ctx->current_throughput_mbps; + stats->peak_throughput_mbps = ctx->peak_throughput_mbps; + + // Operation breakdowns + stats->entropy_percent = ctx->total_cycles > 0 ? + 100.0 * ctx->entropy_collection_cycles / ctx->total_cycles : 0.0; + stats->health_percent = ctx->total_cycles > 0 ? + 100.0 * ctx->health_test_cycles / ctx->total_cycles : 0.0; + stats->quantum_percent = ctx->total_cycles > 0 ? + 100.0 * ctx->quantum_mixing_cycles / ctx->total_cycles : 0.0; + stats->output_percent = ctx->total_cycles > 0 ? + 100.0 * ctx->output_generation_cycles / ctx->total_cycles : 0.0; +} + +void perf_monitor_print_stats(const perf_monitor_ctx_t *ctx) { + if (!ctx) return; + + perf_stats_t stats; + perf_monitor_get_stats(ctx, &stats); + + printf("\n"); + printf("╔═══════════════════════════════════════════════════════════╗\n"); + printf("║ PERFORMANCE MONITORING STATISTICS ║\n"); + printf("╠═══════════════════════════════════════════════════════════╣\n"); + printf("║ ║\n"); + printf("║ Operations: ║\n"); + printf("║ Total operations: %10llu ║\n", + (unsigned long long)stats.total_operations); + printf("║ Bytes processed: %10llu ║\n", + (unsigned long long)stats.bytes_processed); + printf("║ ║\n"); + printf("║ Latency (per operation): ║\n"); + printf("║ Average: %10.2f ns ║\n", stats.avg_latency_ns); + printf("║ Minimum: %10.2f ns ║\n", stats.min_latency_ns); + printf("║ Maximum: %10.2f ns ║\n", stats.max_latency_ns); + printf("║ ║\n"); + printf("║ Throughput: ║\n"); + printf("║ Current: %10.2f MB/s ║\n", stats.current_throughput_mbps); + printf("║ Peak: %10.2f MB/s ║\n", stats.peak_throughput_mbps); + printf("║ ║\n"); + printf("║ Time Distribution: ║\n"); + printf("║ Entropy collection: %10.1f%% ║\n", stats.entropy_percent); + printf("║ Health testing: %10.1f%% ║\n", stats.health_percent); + printf("║ Quantum mixing: %10.1f%% ║\n", stats.quantum_percent); + printf("║ Output generation: %10.1f%% ║\n", stats.output_percent); + printf("║ ║\n"); + printf("╚═══════════════════════════════════════════════════════════╝\n"); + printf("\n"); + + // Print latency histogram + printf("Latency Distribution:\n"); + printf("─────────────────────────────────────────────────────────\n"); + + uint64_t threshold = 1000; + for (int i = 0; i < 20; i++) { + if (ctx->latency_histogram[i] > 0) { + double ns = threshold / ctx->cpu_mhz * 1000.0; + printf(" <%8.1f ns: %10llu ops\n", + ns, (unsigned long long)ctx->latency_histogram[i]); + } + threshold *= 2; + } + printf("\n"); +} + +double perf_monitor_get_overhead_percent(const perf_monitor_ctx_t *ctx) { + if (!ctx || ctx->total_cycles == 0) return 0.0; + + // Estimate monitoring overhead (timing calls, bookkeeping) + // Typically <1% for production code + uint64_t monitoring_cycles = ctx->total_operations * 50; // ~50 cycles per operation + + return 100.0 * monitoring_cycles / ctx->total_cycles; +} \ No newline at end of file diff --git a/vendor/quantum_rng/src/profiling/performance_monitor.h b/vendor/quantum_rng/src/profiling/performance_monitor.h new file mode 100644 index 0000000..893dd78 --- /dev/null +++ b/vendor/quantum_rng/src/profiling/performance_monitor.h @@ -0,0 +1,178 @@ +#ifndef PERFORMANCE_MONITOR_H +#define PERFORMANCE_MONITOR_H + +#include +#include + +/** + * @file performance_monitor.h + * @brief Real-time performance monitoring for Quantum RNG + * + * Provides low-overhead (<1%) performance tracking with: + * - High-resolution timing (CPU cycles or nanoseconds) + * - Operation breakdowns + * - Throughput measurement + * - Latency distribution histograms + */ + +// ============================================================================ +// OPERATION TYPES +// ============================================================================ + +typedef enum { + PERF_OP_ENTROPY_COLLECTION, + PERF_OP_HEALTH_TEST, + PERF_OP_QUANTUM_MIXING, + PERF_OP_OUTPUT_GENERATION, + PERF_OP_MAX +} perf_operation_t; + +// ============================================================================ +// CONTEXT +// ============================================================================ + +/** + * @brief Performance monitoring context + */ +typedef struct { + // Timing + uint64_t start_time; /**< Start timestamp (cycles) */ + uint64_t op_start_cycles; /**< Current operation start */ + perf_operation_t current_op; /**< Current operation type */ + + // Totals + uint64_t total_operations; /**< Total operations monitored */ + uint64_t total_cycles; /**< Total cycles elapsed */ + uint64_t bytes_processed; /**< Total bytes processed */ + + // Latency statistics + uint64_t min_latency_cycles; /**< Minimum latency */ + uint64_t max_latency_cycles; /**< Maximum latency */ + + // Per-operation breakdowns + uint64_t entropy_collection_cycles; + uint64_t health_test_cycles; + uint64_t quantum_mixing_cycles; + uint64_t output_generation_cycles; + + uint64_t entropy_operations; + uint64_t health_operations; + uint64_t quantum_operations; + uint64_t output_operations; + + // Throughput + double current_throughput_mbps; + double peak_throughput_mbps; + + // Latency histogram (20 buckets, logarithmic) + uint64_t latency_histogram[20]; + + // CPU info + double cpu_mhz; /**< CPU frequency in MHz */ +} perf_monitor_ctx_t; + +/** + * @brief Performance statistics structure + */ +typedef struct { + uint64_t total_operations; + uint64_t bytes_processed; + + uint64_t avg_latency_cycles; + uint64_t min_latency_cycles; + uint64_t max_latency_cycles; + + double avg_latency_ns; + double min_latency_ns; + double max_latency_ns; + + double current_throughput_mbps; + double peak_throughput_mbps; + + double entropy_percent; + double health_percent; + double quantum_percent; + double output_percent; +} perf_stats_t; + +// ============================================================================ +// INITIALIZATION +// ============================================================================ + +/** + * @brief Initialize performance monitor + * + * @param ctx Output context pointer + * @return 0 on success, -1 on error + */ +int perf_monitor_init(perf_monitor_ctx_t **ctx); + +/** + * @brief Free performance monitor + * + * @param ctx Monitor context + */ +void perf_monitor_free(perf_monitor_ctx_t *ctx); + +/** + * @brief Reset performance statistics + * + * @param ctx Monitor context + */ +void perf_monitor_reset(perf_monitor_ctx_t *ctx); + +// ============================================================================ +// TIMING OPERATIONS +// ============================================================================ + +/** + * @brief Start timing an operation + * + * @param ctx Monitor context + * @param op Operation type + */ +void perf_monitor_start_operation(perf_monitor_ctx_t *ctx, perf_operation_t op); + +/** + * @brief End timing current operation + * + * @param ctx Monitor context + */ +void perf_monitor_end_operation(perf_monitor_ctx_t *ctx); + +/** + * @brief Record bytes processed (for throughput calculation) + * + * @param ctx Monitor context + * @param bytes Number of bytes + */ +void perf_monitor_record_bytes(perf_monitor_ctx_t *ctx, size_t bytes); + +// ============================================================================ +// STATISTICS +// ============================================================================ + +/** + * @brief Get performance statistics + * + * @param ctx Monitor context + * @param stats Output statistics structure + */ +void perf_monitor_get_stats(const perf_monitor_ctx_t *ctx, perf_stats_t *stats); + +/** + * @brief Print detailed performance statistics + * + * @param ctx Monitor context + */ +void perf_monitor_print_stats(const perf_monitor_ctx_t *ctx); + +/** + * @brief Get monitoring overhead percentage + * + * @param ctx Monitor context + * @return Estimated overhead as percentage + */ +double perf_monitor_get_overhead_percent(const perf_monitor_ctx_t *ctx); + +#endif /* PERFORMANCE_MONITOR_H */ \ No newline at end of file diff --git a/vendor/quantum_rng/src/quantum_rng/accelerate_ops.c b/vendor/quantum_rng/src/quantum_rng/accelerate_ops.c new file mode 100644 index 0000000..65aa03f --- /dev/null +++ b/vendor/quantum_rng/src/quantum_rng/accelerate_ops.c @@ -0,0 +1,735 @@ +#include "accelerate_ops.h" +#include +#include +#include +#include + +#ifdef __APPLE__ +#include +#endif + +// ============================================================================ +// EFFICIENT SPLIT-COMPLEX CONVERSION +// ============================================================================ + +#if HAS_ACCELERATE + +/** + * @brief Convert C99 interleaved complex to vDSP split-complex + * + * Optimized conversion that prepares data for AMX-accelerated vDSP operations. + * Allocates and populates split-complex structure. + */ +static inline void to_split_complex( + const complex_t* interleaved, + double** real_out, + double** imag_out, + size_t n +) { + *real_out = (double*)accelerate_aligned_alloc(n * sizeof(double)); + *imag_out = (double*)accelerate_aligned_alloc(n * sizeof(double)); + + if (!*real_out || !*imag_out) { + accelerate_aligned_free(*real_out); + accelerate_aligned_free(*imag_out); + *real_out = *imag_out = NULL; + return; + } + + // Deinterleave: optimized with loop unrolling + for (size_t i = 0; i < n; i++) { + (*real_out)[i] = creal(interleaved[i]); + (*imag_out)[i] = cimag(interleaved[i]); + } +} + +/** + * @brief Convert vDSP split-complex back to C99 interleaved + */ +static inline void from_split_complex( + const double* real_in, + const double* imag_in, + complex_t* interleaved, + size_t n +) { + // Reinterleave: optimized with loop unrolling + for (size_t i = 0; i < n; i++) { + interleaved[i] = real_in[i] + I * imag_in[i]; + } +} + +#endif + +// ============================================================================ +// CAPABILITY DETECTION +// ============================================================================ + +int accelerate_is_available(void) { +#if HAS_ACCELERATE + return 1; +#else + return 0; +#endif +} + +const char* accelerate_get_capabilities(void) { +#if HAS_ACCELERATE + #ifdef __arm64__ + return "Apple Accelerate (ARM64 + AMX): vDSP, BLAS Level 1-3, 64-byte aligned"; + #else + return "Apple Accelerate (x86_64): vDSP, BLAS Level 1-3"; + #endif +#else + return "Accelerate framework not available"; +#endif +} + +// ============================================================================ +// MEMORY MANAGEMENT (AMX-OPTIMIZED) +// ============================================================================ + +void* accelerate_aligned_alloc(size_t size) { + void* ptr = NULL; + +#ifdef __APPLE__ + if (posix_memalign(&ptr, 64, size) != 0) { + return NULL; + } +#else + ptr = malloc(size); +#endif + + return ptr; +} + +void accelerate_aligned_free(void* ptr) { + if (ptr) { + free(ptr); + } +} + +complex_t* accelerate_alloc_complex_array(size_t num_elements) { + size_t size = num_elements * sizeof(complex_t); + return (complex_t*)accelerate_aligned_alloc(size); +} + +void accelerate_free_complex_array(complex_t* ptr) { + accelerate_aligned_free(ptr); +} + +// ============================================================================ +// VECTORIZED COMPLEX OPERATIONS (vDSP with proper conversion) +// ============================================================================ + +void accelerate_complex_multiply( + const complex_t* a, + const complex_t* b, + complex_t* result, + size_t n +) { +#if HAS_ACCELERATE + double *a_real, *a_imag, *b_real, *b_imag, *r_real, *r_imag; + + to_split_complex(a, &a_real, &a_imag, n); + to_split_complex(b, &b_real, &b_imag, n); + r_real = (double*)accelerate_aligned_alloc(n * sizeof(double)); + r_imag = (double*)accelerate_aligned_alloc(n * sizeof(double)); + + if (!a_real || !b_real || !r_real) { + // Fallback + accelerate_aligned_free(a_real); accelerate_aligned_free(a_imag); + accelerate_aligned_free(b_real); accelerate_aligned_free(b_imag); + accelerate_aligned_free(r_real); accelerate_aligned_free(r_imag); + for (size_t i = 0; i < n; i++) result[i] = a[i] * b[i]; + return; + } + + DSPDoubleSplitComplex a_s = {a_real, a_imag}; + DSPDoubleSplitComplex b_s = {b_real, b_imag}; + DSPDoubleSplitComplex r_s = {r_real, r_imag}; + + // AMX-accelerated complex multiply + vDSP_zvmulD(&a_s, 1, &b_s, 1, &r_s, 1, n, 1); + + from_split_complex(r_real, r_imag, result, n); + + accelerate_aligned_free(a_real); accelerate_aligned_free(a_imag); + accelerate_aligned_free(b_real); accelerate_aligned_free(b_imag); + accelerate_aligned_free(r_real); accelerate_aligned_free(r_imag); +#else + for (size_t i = 0; i < n; i++) { + result[i] = a[i] * b[i]; + } +#endif +} + +void accelerate_complex_add( + const complex_t* a, + const complex_t* b, + complex_t* result, + size_t n +) { +#if HAS_ACCELERATE + double *a_real, *a_imag, *b_real, *b_imag, *r_real, *r_imag; + + to_split_complex(a, &a_real, &a_imag, n); + to_split_complex(b, &b_real, &b_imag, n); + r_real = (double*)accelerate_aligned_alloc(n * sizeof(double)); + r_imag = (double*)accelerate_aligned_alloc(n * sizeof(double)); + + if (!a_real || !b_real || !r_real) { + accelerate_aligned_free(a_real); accelerate_aligned_free(a_imag); + accelerate_aligned_free(b_real); accelerate_aligned_free(b_imag); + accelerate_aligned_free(r_real); accelerate_aligned_free(r_imag); + for (size_t i = 0; i < n; i++) result[i] = a[i] + b[i]; + return; + } + + DSPDoubleSplitComplex a_s = {a_real, a_imag}; + DSPDoubleSplitComplex b_s = {b_real, b_imag}; + DSPDoubleSplitComplex r_s = {r_real, r_imag}; + + vDSP_zvaddD(&a_s, 1, &b_s, 1, &r_s, 1, n); + + from_split_complex(r_real, r_imag, result, n); + + accelerate_aligned_free(a_real); accelerate_aligned_free(a_imag); + accelerate_aligned_free(b_real); accelerate_aligned_free(b_imag); + accelerate_aligned_free(r_real); accelerate_aligned_free(r_imag); +#else + for (size_t i = 0; i < n; i++) { + result[i] = a[i] + b[i]; + } +#endif +} + +void accelerate_complex_subtract( + const complex_t* a, + const complex_t* b, + complex_t* result, + size_t n +) { +#if HAS_ACCELERATE + double *a_real, *a_imag, *b_real, *b_imag, *r_real, *r_imag; + + to_split_complex(a, &a_real, &a_imag, n); + to_split_complex(b, &b_real, &b_imag, n); + r_real = (double*)accelerate_aligned_alloc(n * sizeof(double)); + r_imag = (double*)accelerate_aligned_alloc(n * sizeof(double)); + + if (!a_real || !b_real || !r_real) { + accelerate_aligned_free(a_real); accelerate_aligned_free(a_imag); + accelerate_aligned_free(b_real); accelerate_aligned_free(b_imag); + accelerate_aligned_free(r_real); accelerate_aligned_free(r_imag); + for (size_t i = 0; i < n; i++) result[i] = a[i] - b[i]; + return; + } + + DSPDoubleSplitComplex a_s = {a_real, a_imag}; + DSPDoubleSplitComplex b_s = {b_real, b_imag}; + DSPDoubleSplitComplex r_s = {r_real, r_imag}; + + vDSP_zvsubD(&b_s, 1, &a_s, 1, &r_s, 1, n); + + from_split_complex(r_real, r_imag, result, n); + + accelerate_aligned_free(a_real); accelerate_aligned_free(a_imag); + accelerate_aligned_free(b_real); accelerate_aligned_free(b_imag); + accelerate_aligned_free(r_real); accelerate_aligned_free(r_imag); +#else + for (size_t i = 0; i < n; i++) { + result[i] = a[i] - b[i]; + } +#endif +} + +void accelerate_complex_magnitude( + const complex_t* a, + double* result, + size_t n +) { +#if HAS_ACCELERATE + double *a_real, *a_imag; + to_split_complex(a, &a_real, &a_imag, n); + + if (!a_real) { + for (size_t i = 0; i < n; i++) result[i] = cabs(a[i]); + return; + } + + DSPDoubleSplitComplex a_s = {a_real, a_imag}; + vDSP_zvabsD(&a_s, 1, result, 1, n); + + accelerate_aligned_free(a_real); + accelerate_aligned_free(a_imag); +#else + for (size_t i = 0; i < n; i++) { + result[i] = cabs(a[i]); + } +#endif +} + +void accelerate_complex_magnitude_squared( + const complex_t* a, + double* result, + size_t n +) { +#if HAS_ACCELERATE + double *a_real, *a_imag; + to_split_complex(a, &a_real, &a_imag, n); + + if (!a_real) { + for (size_t i = 0; i < n; i++) { + double r = creal(a[i]); + double im = cimag(a[i]); + result[i] = r * r + im * im; + } + return; + } + + DSPDoubleSplitComplex a_s = {a_real, a_imag}; + + // AMX-accelerated magnitude squared + vDSP_zvmagsD(&a_s, 1, result, 1, n); + + accelerate_aligned_free(a_real); + accelerate_aligned_free(a_imag); +#else + for (size_t i = 0; i < n; i++) { + double r = creal(a[i]); + double im = cimag(a[i]); + result[i] = r * r + im * im; + } +#endif +} + +void accelerate_complex_scale( + const complex_t* a, + complex_t scalar, + complex_t* result, + size_t n +) { + // For real scalar (normalization case), use optimized path + double scalar_real = creal(scalar); + double scalar_imag = cimag(scalar); + +#if HAS_ACCELERATE + if (fabs(scalar_imag) < 1e-15) { + // Pure real scalar - use vDSP_vsmulD (no complex conversion needed!) + double *a_real, *a_imag; + to_split_complex(a, &a_real, &a_imag, n); + + if (!a_real) { + for (size_t i = 0; i < n; i++) result[i] = a[i] * scalar; + return; + } + + // Multiply real and imag parts by scalar separately + vDSP_vsmulD(a_real, 1, &scalar_real, a_real, 1, n); + vDSP_vsmulD(a_imag, 1, &scalar_real, a_imag, 1, n); + + from_split_complex(a_real, a_imag, result, n); + + accelerate_aligned_free(a_real); + accelerate_aligned_free(a_imag); + } else { + // Complex scalar - use scalar (rare case) + for (size_t i = 0; i < n; i++) { + result[i] = a[i] * scalar; + } + } +#else + (void)scalar_real; + (void)scalar_imag; + for (size_t i = 0; i < n; i++) { + result[i] = a[i] * scalar; + } +#endif +} + +// ============================================================================ +// BLAS OPERATIONS (AMX-ACCELERATED) - PRIMARY PERFORMANCE WIN +// ============================================================================ + +void accelerate_matrix_vector_multiply( + size_t m, + size_t n, + complex_t alpha, + const complex_t* A, + const complex_t* x, + complex_t beta, + complex_t* y +) { +#if HAS_ACCELERATE + // CRITICAL: BLAS accepts interleaved complex directly via void* cast! + // This IS AMX-accelerated on M2 Ultra - no conversion needed! + + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated-declarations" + cblas_zgemv( + CblasColMajor, + CblasNoTrans, + (int)m, (int)n, + &alpha, + A, (int)m, + x, 1, + &beta, + y, 1 + ); + #pragma clang diagnostic pop +#else + for (size_t i = 0; i < m; i++) { + complex_t sum = 0.0; + for (size_t j = 0; j < n; j++) { + sum += A[j * m + i] * x[j]; + } + y[i] = alpha * sum + beta * y[i]; + } +#endif +} + +void accelerate_matrix_multiply( + size_t m, + size_t n, + size_t k, + complex_t alpha, + const complex_t* A, + const complex_t* B, + complex_t beta, + complex_t* C +) { +#if HAS_ACCELERATE + // BLAS Level 3 - fully AMX-accelerated! + // Direct interleaved complex support - this is the key performance win! + + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated-declarations" + cblas_zgemm( + CblasColMajor, + CblasNoTrans, CblasNoTrans, + (int)m, (int)n, (int)k, + &alpha, + A, (int)m, + B, (int)k, + &beta, + C, (int)m + ); + #pragma clang diagnostic pop +#else + for (size_t i = 0; i < m; i++) { + for (size_t j = 0; j < n; j++) { + complex_t sum = 0.0; + for (size_t l = 0; l < k; l++) { + sum += A[l * m + i] * B[j * k + l]; + } + C[j * m + i] = alpha * sum + beta * C[j * m + i]; + } + } +#endif +} + +// ============================================================================ +// QUANTUM-SPECIFIC OPTIMIZATIONS +// ============================================================================ + +double accelerate_sum_squared_magnitudes( + const complex_t* amplitudes, + size_t n +) { +#if HAS_ACCELERATE + double *a_real, *a_imag; + to_split_complex(amplitudes, &a_real, &a_imag, n); + + if (!a_real) { + double sum = 0.0; + for (size_t i = 0; i < n; i++) { + double r = creal(amplitudes[i]); + double im = cimag(amplitudes[i]); + sum += r * r + im * im; + } + return sum; + } + + double* mags_sq = (double*)accelerate_aligned_alloc(n * sizeof(double)); + if (!mags_sq) { + accelerate_aligned_free(a_real); + accelerate_aligned_free(a_imag); + double sum = 0.0; + for (size_t i = 0; i < n; i++) { + double r = creal(amplitudes[i]); + double im = cimag(amplitudes[i]); + sum += r * r + im * im; + } + return sum; + } + + DSPDoubleSplitComplex a_s = {a_real, a_imag}; + + // AMX-accelerated magnitude squared + vDSP_zvmagsD(&a_s, 1, mags_sq, 1, n); + + // AMX-accelerated sum + double sum = 0.0; + vDSP_sveD(mags_sq, 1, &sum, n); + + accelerate_aligned_free(a_real); + accelerate_aligned_free(a_imag); + accelerate_aligned_free(mags_sq); + + return sum; +#else + double sum = 0.0; + for (size_t i = 0; i < n; i++) { + double r = creal(amplitudes[i]); + double im = cimag(amplitudes[i]); + sum += r * r + im * im; + } + return sum; +#endif +} + +void accelerate_normalize_amplitudes( + complex_t* amplitudes, + size_t n, + double norm_factor +) { + if (norm_factor == 0.0 || norm_factor == 1.0) { + return; + } + + double inv_norm = 1.0 / norm_factor; + +#if HAS_ACCELERATE + double *a_real, *a_imag; + to_split_complex(amplitudes, &a_real, &a_imag, n); + + if (!a_real) { + for (size_t i = 0; i < n; i++) amplitudes[i] *= inv_norm; + return; + } + + // AMX-accelerated real scalar multiply (normalization is always real) + vDSP_vsmulD(a_real, 1, &inv_norm, a_real, 1, n); + vDSP_vsmulD(a_imag, 1, &inv_norm, a_imag, 1, n); + + from_split_complex(a_real, a_imag, amplitudes, n); + + accelerate_aligned_free(a_real); + accelerate_aligned_free(a_imag); +#else + for (size_t i = 0; i < n; i++) { + amplitudes[i] *= inv_norm; + } +#endif +} + +void accelerate_apply_2x2_gate( + const complex_t matrix[2][2], + complex_t* amplitudes, + const uint64_t* indices, + size_t num_pairs +) { + // Scalar is better for small 2×2 - BLAS overhead too high + for (size_t i = 0; i < num_pairs; i++) { + uint64_t idx0 = indices[i * 2]; + uint64_t idx1 = indices[i * 2 + 1]; + + complex_t amp0 = amplitudes[idx0]; + complex_t amp1 = amplitudes[idx1]; + + amplitudes[idx0] = matrix[0][0] * amp0 + matrix[0][1] * amp1; + amplitudes[idx1] = matrix[1][0] * amp0 + matrix[1][1] * amp1; + } +} + +uint64_t accelerate_cumulative_probability_search( + const complex_t* amplitudes, + size_t n, + double threshold +) { +#if HAS_ACCELERATE + double *a_real, *a_imag; + to_split_complex(amplitudes, &a_real, &a_imag, n); + + if (!a_real) { + double cumulative = 0.0; + for (uint64_t i = 0; i < n; i++) { + double r = creal(amplitudes[i]); + double im = cimag(amplitudes[i]); + cumulative += r * r + im * im; + if (threshold < cumulative) return i; + } + return n - 1; + } + + double* probs = (double*)accelerate_aligned_alloc(n * sizeof(double)); + if (!probs) { + accelerate_aligned_free(a_real); + accelerate_aligned_free(a_imag); + double cumulative = 0.0; + for (uint64_t i = 0; i < n; i++) { + double r = creal(amplitudes[i]); + double im = cimag(amplitudes[i]); + cumulative += r * r + im * im; + if (threshold < cumulative) return i; + } + return n - 1; + } + + DSPDoubleSplitComplex a_s = {a_real, a_imag}; + + // AMX-accelerated magnitude squared + vDSP_zvmagsD(&a_s, 1, probs, 1, n); + + // Cumulative search + double cumulative = 0.0; + uint64_t result = 0; + for (uint64_t i = 0; i < n; i++) { + cumulative += probs[i]; + if (threshold < cumulative) { + result = i; + break; + } + } + + accelerate_aligned_free(a_real); + accelerate_aligned_free(a_imag); + accelerate_aligned_free(probs); + + return result; +#else + double cumulative = 0.0; + for (uint64_t i = 0; i < n; i++) { + double r = creal(amplitudes[i]); + double im = cimag(amplitudes[i]); + double prob = r * r + im * im; + cumulative += prob; + + if (threshold < cumulative) { + return i; + } + } + return n - 1; +#endif +} + +// ============================================================================ +// CONVERSION UTILITIES +// ============================================================================ + +void accelerate_convert_to_column_major( + const complex_t* row_major, + complex_t* col_major, + size_t rows, + size_t cols +) { + for (size_t i = 0; i < rows; i++) { + for (size_t j = 0; j < cols; j++) { + col_major[j * rows + i] = row_major[i * cols + j]; + } + } +} + +void accelerate_convert_to_row_major( + const complex_t* col_major, + complex_t* row_major, + size_t rows, + size_t cols +) { + for (size_t i = 0; i < rows; i++) { + for (size_t j = 0; j < cols; j++) { + row_major[i * cols + j] = col_major[j * rows + i]; + } + } +} + +// ============================================================================ +// PERFORMANCE UTILITIES +// ============================================================================ + +#include + +static double get_time_seconds(void) { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return ts.tv_sec + ts.tv_nsec / 1e9; +} + +double accelerate_benchmark_vs_simd(size_t num_qubits) { + if (!accelerate_is_available()) { + return 1.0; + } + + size_t n = 1ULL << num_qubits; + + complex_t* a = accelerate_alloc_complex_array(n); + if (!a) return 1.0; + + // Initialize + for (size_t i = 0; i < n; i++) { + a[i] = (double)rand() / RAND_MAX + I * (double)rand() / RAND_MAX; + } + + // Benchmark Accelerate sum of squared magnitudes + const int iterations = 100; + + double start = get_time_seconds(); + for (int iter = 0; iter < iterations; iter++) { + double sum = accelerate_sum_squared_magnitudes(a, n); + (void)sum; + } + double accelerate_time = get_time_seconds() - start; + + // Benchmark scalar + start = get_time_seconds(); + for (int iter = 0; iter < iterations; iter++) { + double sum = 0.0; + for (size_t i = 0; i < n; i++) { + double r = creal(a[i]); + double im = cimag(a[i]); + sum += r * r + im * im; + } + (void)sum; + } + double scalar_time = get_time_seconds() - start; + + accelerate_free_complex_array(a); + + if (accelerate_time > 0.0) { + return scalar_time / accelerate_time; + } + return 1.0; +} + +void accelerate_print_performance_stats(void) { + printf("=== Apple Accelerate Performance ===\n"); + printf("Framework: %s\n", accelerate_get_capabilities()); + printf("Available: %s\n", accelerate_is_available() ? "YES" : "NO"); + +#ifdef __APPLE__ + #ifdef __arm64__ + printf("Architecture: ARM64 (Apple Silicon)\n"); + printf("AMX Support: YES (2× 512-bit matrix units)\n"); + printf("Memory Alignment: 64-byte (AMX-optimized)\n"); + printf("\nKEY OPTIMIZATIONS:\n"); + printf(" • BLAS (cblas_zgemv, cblas_zgemm): Direct AMX acceleration\n"); + printf(" • vDSP (magnitude, sums): AMX-accelerated with format conversion\n"); + printf(" • Expected 5-10x for matrix operations, 2-3x for element-wise\n"); + #else + printf("Architecture: x86_64 (Intel)\n"); + printf("AMX Support: NO (ARM-only)\n"); + #endif +#else + printf("Platform: Non-Apple\n"); +#endif + + if (accelerate_is_available()) { + printf("\nBenchmarking speedup vs scalar:\n"); + for (size_t qubits = 8; qubits <= 16; qubits += 2) { + double speedup = accelerate_benchmark_vs_simd(qubits); + printf(" %2zu qubits: %.2fx speedup\n", qubits, speedup); + } + } + + printf("====================================\n"); +} \ No newline at end of file diff --git a/vendor/quantum_rng/src/quantum_rng/accelerate_ops.h b/vendor/quantum_rng/src/quantum_rng/accelerate_ops.h new file mode 100644 index 0000000..83d9cef --- /dev/null +++ b/vendor/quantum_rng/src/quantum_rng/accelerate_ops.h @@ -0,0 +1,398 @@ +#ifndef ACCELERATE_OPS_H +#define ACCELERATE_OPS_H + +#include +#include +#include + +#ifdef __APPLE__ +#include +#define HAS_ACCELERATE 1 +#else +#define HAS_ACCELERATE 0 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file accelerate_ops.h + * @brief Apple Accelerate framework integration for M2 Ultra quantum RNG + * + * Phase 3 Optimization: AMX-Accelerated Matrix Operations + * + * Leverages Apple's Accelerate framework to achieve 5-10x additional speedup + * through AMX (Apple Matrix coprocessor) hardware acceleration. + * + * Key Features: + * - vDSP vectorized complex operations (AMX-accelerated) + * - BLAS Level 2/3 operations for matrix math + * - 64-byte memory alignment for optimal AMX performance + * - Automatic fallback to SIMD when Accelerate unavailable + * + * M2 Ultra AMX Capabilities: + * - 2× 512-bit matrix units + * - 8×8 complex matrix operations in ~4 cycles + * - Integrated with vDSP for automatic engagement + * - Expected 5-10x speedup for matrix-heavy quantum gates + */ + +// Complex type compatible with quantum_state.h and Accelerate framework +typedef double _Complex complex_t; + +// ============================================================================ +// ACCELERATE CAPABILITY DETECTION +// ============================================================================ + +/** + * @brief Check if Accelerate framework is available + * @return 1 if available, 0 otherwise + */ +int accelerate_is_available(void); + +/** + * @brief Get hardware capabilities string + * @return Human-readable description of acceleration capabilities + */ +const char* accelerate_get_capabilities(void); + +// ============================================================================ +// MEMORY MANAGEMENT (AMX-OPTIMIZED) +// ============================================================================ + +/** + * @brief Allocate AMX-aligned memory (64-byte boundary) + * + * AMX requires 64-byte alignment for optimal performance. + * This ensures data structures are properly aligned. + * + * @param size Number of bytes to allocate + * @return Pointer to aligned memory, or NULL on failure + */ +void* accelerate_aligned_alloc(size_t size); + +/** + * @brief Free AMX-aligned memory + * @param ptr Pointer to free (must be from accelerate_aligned_alloc) + */ +void accelerate_aligned_free(void* ptr); + +/** + * @brief Allocate complex amplitude array with AMX alignment + * @param num_elements Number of complex elements + * @return Pointer to aligned complex array + */ +complex_t* accelerate_alloc_complex_array(size_t num_elements); + +/** + * @brief Free complex amplitude array + * @param ptr Pointer to complex array + */ +void accelerate_free_complex_array(complex_t* ptr); + +// ============================================================================ +// VECTORIZED COMPLEX OPERATIONS (vDSP) +// ============================================================================ + +/** + * @brief Complex vector multiplication (element-wise) + * + * Computes: result[i] = a[i] * b[i] + * Uses vDSP_zvmul for AMX acceleration + * + * @param a First complex vector + * @param b Second complex vector + * @param result Output vector + * @param n Number of elements + */ +void accelerate_complex_multiply( + const complex_t* a, + const complex_t* b, + complex_t* result, + size_t n +); + +/** + * @brief Complex vector addition + * + * Computes: result[i] = a[i] + b[i] + * Uses vDSP_zvadd for AMX acceleration + * + * @param a First complex vector + * @param b Second complex vector + * @param result Output vector + * @param n Number of elements + */ +void accelerate_complex_add( + const complex_t* a, + const complex_t* b, + complex_t* result, + size_t n +); + +/** + * @brief Complex vector subtraction + * + * Computes: result[i] = a[i] - b[i] + * Uses vDSP_zvsub for AMX acceleration + * + * @param a First complex vector + * @param b Second complex vector + * @param result Output vector + * @param n Number of elements + */ +void accelerate_complex_subtract( + const complex_t* a, + const complex_t* b, + complex_t* result, + size_t n +); + +/** + * @brief Complex vector magnitude (absolute value) + * + * Computes: result[i] = |a[i]| = sqrt(real² + imag²) + * Uses vDSP_zvabs for AMX acceleration + * + * @param a Complex input vector + * @param result Real output vector (magnitudes) + * @param n Number of elements + */ +void accelerate_complex_magnitude( + const complex_t* a, + double* result, + size_t n +); + +/** + * @brief Complex vector magnitude squared + * + * Computes: result[i] = |a[i]|² = real² + imag² + * Uses vDSP_zvmags for AMX acceleration (faster than zvabs + square) + * + * @param a Complex input vector + * @param result Real output vector (squared magnitudes) + * @param n Number of elements + */ +void accelerate_complex_magnitude_squared( + const complex_t* a, + double* result, + size_t n +); + +/** + * @brief Complex vector scale (multiply by scalar) + * + * Computes: result[i] = a[i] * scalar + * Uses vDSP_zvsmul for AMX acceleration + * + * @param a Complex input vector + * @param scalar Complex scalar value + * @param result Complex output vector + * @param n Number of elements + */ +void accelerate_complex_scale( + const complex_t* a, + complex_t scalar, + complex_t* result, + size_t n +); + +// ============================================================================ +// BLAS OPERATIONS (AMX-ACCELERATED) +// ============================================================================ + +/** + * @brief Complex matrix-vector multiply (BLAS Level 2) + * + * Computes: y = alpha * A * x + beta * y + * Uses cblas_zgemv with AMX acceleration for M2 Ultra + * + * This is CRITICAL for quantum gate operations - up to 10x faster + * than scalar implementations due to AMX matrix units. + * + * @param m Number of rows in matrix A + * @param n Number of columns in matrix A + * @param alpha Scalar multiplier for A*x + * @param A Complex matrix (m×n, column-major for BLAS) + * @param x Complex input vector (length n) + * @param beta Scalar multiplier for y + * @param y Complex output vector (length m, modified in-place) + */ +void accelerate_matrix_vector_multiply( + size_t m, + size_t n, + complex_t alpha, + const complex_t* A, + const complex_t* x, + complex_t beta, + complex_t* y +); + +/** + * @brief Complex matrix-matrix multiply (BLAS Level 3) + * + * Computes: C = alpha * A * B + beta * C + * Uses cblas_zgemm with AMX acceleration + * + * @param m Rows in A and C + * @param n Columns in B and C + * @param k Columns in A, rows in B + * @param alpha Scalar multiplier for A*B + * @param A Complex matrix (m×k, column-major) + * @param B Complex matrix (k×n, column-major) + * @param beta Scalar multiplier for C + * @param C Complex output matrix (m×n, column-major, modified in-place) + */ +void accelerate_matrix_multiply( + size_t m, + size_t n, + size_t k, + complex_t alpha, + const complex_t* A, + const complex_t* B, + complex_t beta, + complex_t* C +); + +// ============================================================================ +// QUANTUM-SPECIFIC OPTIMIZATIONS +// ============================================================================ + +/** + * @brief Sum of squared magnitudes (for normalization) + * + * Computes: Σ|a[i]|² + * Uses vDSP_zvmags + vDSP_sve for AMX-accelerated reduction + * + * Critical for quantum state normalization checks. + * + * @param amplitudes Complex amplitude array + * @param n Number of amplitudes + * @return Sum of squared magnitudes + */ +double accelerate_sum_squared_magnitudes( + const complex_t* amplitudes, + size_t n +); + +/** + * @brief Normalize complex amplitude array + * + * Divides all amplitudes by normalization factor. + * Uses vDSP_zvsmul for vectorized scaling. + * + * @param amplitudes Complex amplitude array (modified in-place) + * @param n Number of amplitudes + * @param norm_factor Normalization factor (typically sqrt(Σ|a|²)) + */ +void accelerate_normalize_amplitudes( + complex_t* amplitudes, + size_t n, + double norm_factor +); + +/** + * @brief Apply 2×2 quantum gate to amplitude pairs + * + * For each pair (amp0, amp1), computes: + * new_amp0 = matrix[0][0] * amp0 + matrix[0][1] * amp1 + * new_amp1 = matrix[1][0] * amp0 + matrix[1][1] * amp1 + * + * Uses vDSP operations with AMX acceleration. + * Optimized for single-qubit quantum gates (H, X, Y, Z, RY, etc.) + * + * @param matrix 2×2 complex gate matrix + * @param amplitudes Amplitude array (modified in-place) + * @param indices Array of amplitude pair indices + * @param num_pairs Number of pairs to process + */ +void accelerate_apply_2x2_gate( + const complex_t matrix[2][2], + complex_t* amplitudes, + const uint64_t* indices, + size_t num_pairs +); + +/** + * @brief Cumulative probability search (for quantum measurement) + * + * Finds index where cumulative probability exceeds threshold. + * Uses vDSP_zvmags for fast magnitude computation. + * + * @param amplitudes Complex amplitude array + * @param n Number of amplitudes + * @param threshold Random threshold value [0, 1) + * @return Index where cumulative probability exceeds threshold + */ +uint64_t accelerate_cumulative_probability_search( + const complex_t* amplitudes, + size_t n, + double threshold +); + +// ============================================================================ +// CONVERSION UTILITIES +// ============================================================================ + +/** + * @brief Convert row-major matrix to column-major (for BLAS) + * + * BLAS expects column-major matrices, but our code uses row-major. + * This performs efficient transpose conversion. + * + * @param row_major Input matrix (row-major) + * @param col_major Output matrix (column-major) + * @param rows Number of rows + * @param cols Number of columns + */ +void accelerate_convert_to_column_major( + const complex_t* row_major, + complex_t* col_major, + size_t rows, + size_t cols +); + +/** + * @brief Convert column-major matrix to row-major (from BLAS) + * + * @param col_major Input matrix (column-major) + * @param row_major Output matrix (row-major) + * @param rows Number of rows + * @param cols Number of columns + */ +void accelerate_convert_to_row_major( + const complex_t* col_major, + complex_t* row_major, + size_t rows, + size_t cols +); + +// ============================================================================ +// PERFORMANCE UTILITIES +// ============================================================================ + +/** + * @brief Benchmark Accelerate vs SIMD performance + * + * Compares performance of Accelerate framework operations + * against current SIMD implementations. + * + * @param num_qubits Number of qubits to test + * @return Speedup factor (>1.0 means Accelerate is faster) + */ +double accelerate_benchmark_vs_simd(size_t num_qubits); + +/** + * @brief Print AMX performance statistics + * + * Displays information about AMX utilization and performance. + */ +void accelerate_print_performance_stats(void); + +#ifdef __cplusplus +} +#endif + +#endif /* ACCELERATE_OPS_H */ \ No newline at end of file diff --git a/vendor/quantum_rng/src/quantum_rng/bell_test.c b/vendor/quantum_rng/src/quantum_rng/bell_test.c new file mode 100644 index 0000000..fbf02de --- /dev/null +++ b/vendor/quantum_rng/src/quantum_rng/bell_test.c @@ -0,0 +1,514 @@ +#include "bell_test.h" +#include "quantum_gates.h" +#include "quantum_constants.h" +#include "quantum_entropy.h" +#include +#include +#include +#include + +#define M_PI QC_PI +#define M_SQRT2 QC_SQRT2 +#define M_PI_4 QC_PI_4 +#define M_PI_2 QC_PI_2 + +// Optimal CHSH value for maximally entangled states: 2√2 (Tsirelson bound) +#define CHSH_QUANTUM_MAX QC_TSIRELSON_BOUND + +// Helper function +static inline int get_bit(uint64_t n, int bit_pos) { + return (n >> bit_pos) & 1ULL; +} + +// ============================================================================ +// BELL STATE CREATION +// ============================================================================ + +qs_error_t create_bell_state_phi_plus(quantum_state_t *state, int qubit1, int qubit2) { + if (!state) return QS_ERROR_INVALID_STATE; + + // |Φ⁺⟩ = (|00⟩ + |11⟩)/√2 + // Circuit: H on qubit1, then CNOT(qubit1, qubit2) + + qs_error_t err = gate_hadamard(state, qubit1); + if (err != QS_SUCCESS) return err; + + return gate_cnot(state, qubit1, qubit2); +} + +qs_error_t create_bell_state_phi_minus(quantum_state_t *state, int qubit1, int qubit2) { + if (!state) return QS_ERROR_INVALID_STATE; + + // |Φ⁻⟩ = (|00⟩ - |11⟩)/√2 + // Circuit: H on qubit1, Z on qubit1, then CNOT(qubit1, qubit2) + + qs_error_t err = gate_hadamard(state, qubit1); + if (err != QS_SUCCESS) return err; + + err = gate_pauli_z(state, qubit1); + if (err != QS_SUCCESS) return err; + + return gate_cnot(state, qubit1, qubit2); +} + +qs_error_t create_bell_state_psi_plus(quantum_state_t *state, int qubit1, int qubit2) { + if (!state) return QS_ERROR_INVALID_STATE; + + // |Ψ⁺⟩ = (|01⟩ + |10⟩)/√2 + // Circuit: H on qubit1, X on qubit2, then CNOT(qubit1, qubit2) + + qs_error_t err = gate_hadamard(state, qubit1); + if (err != QS_SUCCESS) return err; + + err = gate_pauli_x(state, qubit2); + if (err != QS_SUCCESS) return err; + + return gate_cnot(state, qubit1, qubit2); +} + +qs_error_t create_bell_state_psi_minus(quantum_state_t *state, int qubit1, int qubit2) { + if (!state) return QS_ERROR_INVALID_STATE; + + // |Ψ⁻⟩ = (|01⟩ - |10⟩)/√2 + // Circuit: H on qubit1, X on qubit2, Z on qubit1, then CNOT(qubit1, qubit2) + + qs_error_t err = gate_hadamard(state, qubit1); + if (err != QS_SUCCESS) return err; + + err = gate_pauli_x(state, qubit2); + if (err != QS_SUCCESS) return err; + + err = gate_pauli_z(state, qubit1); + if (err != QS_SUCCESS) return err; + + return gate_cnot(state, qubit1, qubit2); +} + +qs_error_t create_bell_state(quantum_state_t *state, int qubit1, int qubit2, bell_state_type_t type) { + switch (type) { + case BELL_PHI_PLUS: + return create_bell_state_phi_plus(state, qubit1, qubit2); + case BELL_PHI_MINUS: + return create_bell_state_phi_minus(state, qubit1, qubit2); + case BELL_PSI_PLUS: + return create_bell_state_psi_plus(state, qubit1, qubit2); + case BELL_PSI_MINUS: + return create_bell_state_psi_minus(state, qubit1, qubit2); + default: + return QS_ERROR_INVALID_STATE; + } +} + +// ============================================================================ +// CORRELATION MEASUREMENT +// ============================================================================ + +double measure_correlation( + quantum_state_t *state, + int qubit_a, + int qubit_b, + double angle_a, + double angle_b, + size_t num_samples, + quantum_entropy_ctx_t *entropy +) { + if (!state || num_samples == 0 || !entropy) return 0.0; + + /** + * OPTIMIZED BELL TEST CORRELATION MEASUREMENT (10-20x faster!) + * + * Key optimization: Create ONE rotated state, compute probabilities ONCE, + * then sample multiple times. Eliminates num_samples state clones. + * + * Old approach: Clone → Rotate → Measure (repeated num_samples times) + * New approach: Clone ONCE → Rotate ONCE → Compute probs → Sample many times + */ + + // Step 1: Clone state ONCE (not num_samples times!) + quantum_state_t measurement_state; + qs_error_t err = quantum_state_clone(&measurement_state, state); + if (err != QS_SUCCESS) return 0.0; + + // Step 2: Rotate to measurement basis ONCE + err = gate_ry(&measurement_state, qubit_a, -angle_a); + if (err != QS_SUCCESS) { + quantum_state_free(&measurement_state); + return 0.0; + } + + err = gate_ry(&measurement_state, qubit_b, -angle_b); + if (err != QS_SUCCESS) { + quantum_state_free(&measurement_state); + return 0.0; + } + + // Step 3: Compute joint probabilities ONCE + // P(00), P(01), P(10), P(11) for the two qubits + double prob_00 = 0.0; + double prob_01 = 0.0; + double prob_10 = 0.0; + double prob_11 = 0.0; + + for (uint64_t basis = 0; basis < measurement_state.state_dim; basis++) { + double prob = quantum_state_get_probability(&measurement_state, basis); + + int bit_a = (basis >> qubit_a) & 1; + int bit_b = (basis >> qubit_b) & 1; + + if (bit_a == 0 && bit_b == 0) prob_00 += prob; + else if (bit_a == 0 && bit_b == 1) prob_01 += prob; + else if (bit_a == 1 && bit_b == 0) prob_10 += prob; + else prob_11 += prob; + } + + // Step 4: Sample num_samples times from the SAME probability distribution + int64_t correlation_sum = 0; + + for (size_t sample = 0; sample < num_samples; sample++) { + // Get random number for sampling + double rand; + if (quantum_entropy_get_double(entropy, &rand) != 0) { + quantum_state_free(&measurement_state); + return 0.0; + } + + // Sample from joint distribution + int outcome_a, outcome_b; + + if (rand < prob_00) { + outcome_a = 0; outcome_b = 0; + } else if (rand < prob_00 + prob_01) { + outcome_a = 0; outcome_b = 1; + } else if (rand < prob_00 + prob_01 + prob_10) { + outcome_a = 1; outcome_b = 0; + } else { + outcome_a = 1; outcome_b = 1; + } + + // Convert to ±1 for correlation: 0→+1, 1→-1 + int sign_a = outcome_a ? -1 : +1; + int sign_b = outcome_b ? -1 : +1; + + correlation_sum += sign_a * sign_b; + } + + // Step 5: Clean up (single free instead of num_samples frees!) + quantum_state_free(&measurement_state); + + // Return average correlation E(a,b) = ⟨A(a) ⊗ B(b)⟩ + // Statistical noise: σ ~ 1/√num_samples (same as original 1/√N) + return (double)correlation_sum / num_samples; +} + +double calculate_chsh_parameter(const double correlations[4]) { + // CHSH S = |E(a,b) - E(a,b') + E(a',b) + E(a',b')| + double s = fabs( + correlations[0] - correlations[1] + + correlations[2] + correlations[3] + ); + return s; +} + +// ============================================================================ +// FULL BELL TEST +// ============================================================================ + +bell_test_result_t bell_test_chsh( + quantum_state_t *state, + int qubit_a, + int qubit_b, + size_t num_measurements, + const bell_measurement_settings_t *settings, + quantum_entropy_ctx_t *entropy +) { + bell_test_result_t result = {0}; + + if (!state || num_measurements == 0 || !entropy) { + return result; + } + + // Use optimal settings if none provided + bell_measurement_settings_t optimal_settings; + if (!settings) { + bell_get_optimal_settings(&optimal_settings); + settings = &optimal_settings; + } + + result.measurements = num_measurements; + result.classical_bound = 2.0; + result.quantum_bound = CHSH_QUANTUM_MAX; + + // Create maximally entangled Bell state |Φ⁺⟩ + quantum_state_t test_state; + qs_error_t err = quantum_state_clone(&test_state, state); + if (err != QS_SUCCESS) { + return result; + } + + // Reset to |00⟩ and create Bell state + quantum_state_reset(&test_state); + create_bell_state_phi_plus(&test_state, qubit_a, qubit_b); + + // Measure four correlations + // Divide measurements equally among the four settings + size_t samples_per_setting = num_measurements / 4; + + printf("Measuring correlations (this may take a moment)...\n"); + + // E(a, b) + printf(" E(a,b)..."); + fflush(stdout); + result.correlation_ab = measure_correlation( + &test_state, qubit_a, qubit_b, + settings->angle_a1, settings->angle_b1, + samples_per_setting, entropy + ); + printf(" %.6f\n", result.correlation_ab); + + // E(a, b') + printf(" E(a,b')..."); + fflush(stdout); + result.correlation_ab_prime = measure_correlation( + &test_state, qubit_a, qubit_b, + settings->angle_a1, settings->angle_b2, + samples_per_setting, entropy + ); + printf(" %.6f\n", result.correlation_ab_prime); + + // E(a', b) + printf(" E(a',b)..."); + fflush(stdout); + result.correlation_a_prime_b = measure_correlation( + &test_state, qubit_a, qubit_b, + settings->angle_a2, settings->angle_b1, + samples_per_setting, entropy + ); + printf(" %.6f\n", result.correlation_a_prime_b); + + // E(a', b') + printf(" E(a',b')..."); + fflush(stdout); + result.correlation_a_prime_b_prime = measure_correlation( + &test_state, qubit_a, qubit_b, + settings->angle_a2, settings->angle_b2, + samples_per_setting, entropy + ); + printf(" %.6f\n", result.correlation_a_prime_b_prime); + + // Calculate CHSH parameter + double correlations[4] = { + result.correlation_ab, + result.correlation_ab_prime, + result.correlation_a_prime_b, + result.correlation_a_prime_b_prime + }; + + result.chsh_value = calculate_chsh_parameter(correlations); + + // Calculate statistical significance + // Standard error ≈ 2/√N for CHSH measurement + result.standard_error = 2.0 / sqrt((double)num_measurements); + + // Z-score for violation of classical bound + double z_score = (result.chsh_value - result.classical_bound) / result.standard_error; + + // P-value (one-tailed test) + result.p_value = 0.5 * erfc(z_score / M_SQRT2); + + // Determine test results + result.violates_classical = (result.chsh_value > result.classical_bound); + result.confirms_quantum = (result.chsh_value > 2.4); // Conservative threshold + result.statistically_significant = (result.p_value < 0.01); + + quantum_state_free(&test_state); + + return result; +} + +void bell_get_optimal_settings(bell_measurement_settings_t *settings) { + if (!settings) return; + + // Optimal CHSH angles for maximal violation + // For Bell state |Φ⁺⟩, E(θ_a, θ_b) = cos(θ_a - θ_b) + // Standard optimal settings give CHSH = 2√2 + settings->angle_a1 = 0.0; + settings->angle_a2 = QC_PI_2; // π/2 + settings->angle_b1 = QC_PI_4; // π/4 + settings->angle_b2 = QC_3PI_4; // 3π/4 +} + +int bell_test_confirms_quantum(const bell_test_result_t *result) { + if (!result) return 0; + + // Quantum behavior confirmed if: + // 1. Violates classical bound (CHSH > 2) + // 2. Statistically significant (p < 0.01) + // 3. Close to theoretical maximum (within 10%) + + int violates_classical = result->violates_classical; + int significant = result->statistically_significant; + int near_maximum = (result->chsh_value > 0.9 * result->quantum_bound); + + return violates_classical && significant && near_maximum; +} + +void bell_test_print_results(const bell_test_result_t *result) { + if (!result) return; + + printf("\n"); + printf("╔═══════════════════════════════════════════════════════════╗\n"); + printf("║ BELL INEQUALITY TEST RESULTS (CHSH) ║\n"); + printf("╠═══════════════════════════════════════════════════════════╣\n"); + printf("║ ║\n"); + printf("║ CHSH Parameter (S): %6.4f ║\n", result->chsh_value); + printf("║ Classical Bound: %6.4f ║\n", result->classical_bound); + printf("║ Quantum Bound (2√2): %6.4f ║\n", result->quantum_bound); + printf("║ ║\n"); + printf("║ Correlations: ║\n"); + printf("║ E(a,b): %7.4f ║\n", result->correlation_ab); + printf("║ E(a,b'): %7.4f ║\n", result->correlation_ab_prime); + printf("║ E(a',b): %7.4f ║\n", result->correlation_a_prime_b); + printf("║ E(a',b'): %7.4f ║\n", result->correlation_a_prime_b_prime); + printf("║ ║\n"); + printf("║ Statistical Analysis: ║\n"); + printf("║ Measurements: %6zu ║\n", result->measurements); + printf("║ Standard Error: %6.4f ║\n", result->standard_error); + printf("║ P-value: %6.4f ║\n", result->p_value); + printf("║ ║\n"); + printf("║ Test Results: ║\n"); + printf("║ Violates Classical: %s ║\n", + result->violates_classical ? "✓ YES" : "✗ NO "); + printf("║ Confirms Quantum: %s ║\n", + result->confirms_quantum ? "✓ YES" : "✗ NO "); + printf("║ Statistically Significant: %s ║\n", + result->statistically_significant ? "✓ YES" : "✗ NO "); + printf("║ ║\n"); + + // Overall verdict + if (bell_test_confirms_quantum(result)) { + printf("║ ┌─────────────────────────────────────────────────────┐ ║\n"); + printf("║ │ ✓ QUANTUM BEHAVIOR CONFIRMED │ ║\n"); + printf("║ │ System exhibits genuine quantum entanglement │ ║\n"); + printf("║ └─────────────────────────────────────────────────────┘ ║\n"); + } else if (result->violates_classical) { + printf("║ ┌─────────────────────────────────────────────────────┐ ║\n"); + printf("║ │ ⚠ CLASSICAL BOUND VIOLATED │ ║\n"); + printf("║ │ Non-classical behavior detected │ ║\n"); + printf("║ └─────────────────────────────────────────────────────┘ ║\n"); + } else { + printf("║ ┌─────────────────────────────────────────────────────┐ ║\n"); + printf("║ │ ✗ CLASSICAL BEHAVIOR │ ║\n"); + printf("║ │ No Bell inequality violation detected │ ║\n"); + printf("║ └─────────────────────────────────────────────────────┘ ║\n"); + } + + printf("╚═══════════════════════════════════════════════════════════╝\n"); + printf("\n"); +} + +double bell_theoretical_chsh(bell_state_type_t state_type) { + // All maximally entangled Bell states achieve maximum CHSH = 2√2 + (void)state_type; // Unused for maximally entangled states + return CHSH_QUANTUM_MAX; +} + +// ============================================================================ +// CONTINUOUS VERIFICATION +// ============================================================================ + +int bell_monitor_init(bell_test_monitor_t *monitor, size_t capacity) { + if (!monitor || capacity == 0) return -1; + + memset(monitor, 0, sizeof(bell_test_monitor_t)); + + monitor->test_history = (bell_test_result_t *)calloc(capacity, sizeof(bell_test_result_t)); + if (!monitor->test_history) return -1; + + monitor->capacity = capacity; + monitor->num_tests = 0; + monitor->all_tests_quantum = 1; + monitor->min_chsh = CHSH_QUANTUM_MAX; + monitor->max_chsh = 0.0; + + return 0; +} + +void bell_monitor_add_result(bell_test_monitor_t *monitor, const bell_test_result_t *result) { + if (!monitor || !result) return; + + // Add result (circular buffer if full) + size_t index = monitor->num_tests % monitor->capacity; + memcpy(&monitor->test_history[index], result, sizeof(bell_test_result_t)); + + if (monitor->num_tests < monitor->capacity) { + monitor->num_tests++; + } + + // Update statistics + double chsh = result->chsh_value; + + if (chsh < monitor->min_chsh) monitor->min_chsh = chsh; + if (chsh > monitor->max_chsh) monitor->max_chsh = chsh; + + // Recalculate average + double sum = 0.0; + double sum_sq = 0.0; + size_t quantum_count = 0; + + for (size_t i = 0; i < monitor->num_tests; i++) { + double val = monitor->test_history[i].chsh_value; + sum += val; + sum_sq += val * val; + + if (bell_test_confirms_quantum(&monitor->test_history[i])) { + quantum_count++; + } + } + + monitor->average_chsh = sum / monitor->num_tests; + monitor->variance_chsh = (sum_sq / monitor->num_tests) - (monitor->average_chsh * monitor->average_chsh); + monitor->all_tests_quantum = (quantum_count == monitor->num_tests); + monitor->num_classical_violations = monitor->num_tests - quantum_count; +} + +void bell_monitor_get_statistics(const bell_test_monitor_t *monitor) { + if (!monitor) return; + + printf("\n"); + printf("╔═══════════════════════════════════════════════════════════╗\n"); + printf("║ BELL TEST MONITORING STATISTICS ║\n"); + printf("╠═══════════════════════════════════════════════════════════╣\n"); + printf("║ ║\n"); + printf("║ Total Tests: %6zu ║\n", monitor->num_tests); + printf("║ Average CHSH: %6.4f ║\n", monitor->average_chsh); + printf("║ Min CHSH: %6.4f ║\n", monitor->min_chsh); + printf("║ Max CHSH: %6.4f ║\n", monitor->max_chsh); + printf("║ Variance: %6.4f ║\n", monitor->variance_chsh); + printf("║ ║\n"); + printf("║ Quantum Tests: %6zu ║\n", + monitor->num_tests - monitor->num_classical_violations); + printf("║ Classical Violations: %6zu ║\n", + monitor->num_classical_violations); + printf("║ Quantum Success Rate: %5.1f%% ║\n", + 100.0 * (monitor->num_tests - monitor->num_classical_violations) / monitor->num_tests); + printf("║ ║\n"); + + if (monitor->all_tests_quantum) { + printf("║ ✓ ALL TESTS CONFIRM QUANTUM BEHAVIOR ║\n"); + } else { + printf("║ ⚠ SOME TESTS SHOW CLASSICAL BEHAVIOR ║\n"); + } + + printf("╚═══════════════════════════════════════════════════════════╝\n"); + printf("\n"); +} + +void bell_monitor_free(bell_test_monitor_t *monitor) { + if (!monitor) return; + + if (monitor->test_history) { + free(monitor->test_history); + } + + memset(monitor, 0, sizeof(bell_test_monitor_t)); +} \ No newline at end of file diff --git a/vendor/quantum_rng/src/quantum_rng/bell_test.h b/vendor/quantum_rng/src/quantum_rng/bell_test.h new file mode 100644 index 0000000..54d111a --- /dev/null +++ b/vendor/quantum_rng/src/quantum_rng/bell_test.h @@ -0,0 +1,288 @@ +#ifndef BELL_TEST_H +#define BELL_TEST_H + +#include "quantum_state.h" +#include "quantum_entropy.h" +#include + +/** + * @file bell_test.h + * @brief Bell inequality testing for quantum behavior verification + * + * Implements CHSH (Clauser-Horne-Shimony-Holt) inequality test to prove + * genuine quantum properties. Classical systems obey CHSH ≤ 2, while + * quantum systems can violate this up to 2√2 ≈ 2.828. + * + * This is the gold standard for verifying quantum behavior and proving + * the system exhibits genuine quantum entanglement. + */ + +/** + * @brief Bell measurement settings for CHSH test + * + * Angles specify measurement bases for Alice and Bob: + * - Alice measures at angles a1, a2 + * - Bob measures at angles b1, b2 + * + * Optimal CHSH settings: a1=0, a2=π/2, b1=π/4, b2=-π/4 + */ +typedef struct { + double angle_a1; // Alice's first measurement angle + double angle_a2; // Alice's second measurement angle + double angle_b1; // Bob's first measurement angle + double angle_b2; // Bob's second measurement angle +} bell_measurement_settings_t; + +/** + * @brief CHSH test result + * + * Contains complete statistical analysis of Bell inequality test + */ +typedef struct { + // CHSH S-parameter: S = |E(a,b) - E(a,b') + E(a',b) + E(a',b')| + double chsh_value; + + // Individual correlation measurements + double correlation_ab; // E(a,b) = ⟨A(a) ⊗ B(b)⟩ + double correlation_ab_prime; // E(a,b') + double correlation_a_prime_b; // E(a',b) + double correlation_a_prime_b_prime; // E(a',b') + + // Statistical bounds + double classical_bound; // 2.0 for classical systems + double quantum_bound; // 2√2 ≈ 2.828 for quantum systems + + // Statistical significance + double p_value; // Statistical significance (< 0.01 = significant) + double standard_error; // Standard error of S measurement + size_t measurements; // Number of measurement pairs + + // Test results + int violates_classical; // 1 if CHSH > 2 (proves non-classical) + int confirms_quantum; // 1 if CHSH close to 2√2 + int statistically_significant; // 1 if p-value < 0.01 + + // Detailed statistics + uint64_t counts_00; // Number of (0,0) outcomes + uint64_t counts_01; // Number of (0,1) outcomes + uint64_t counts_10; // Number of (1,0) outcomes + uint64_t counts_11; // Number of (1,1) outcomes +} bell_test_result_t; + +/** + * @brief Bell state types + */ +typedef enum { + BELL_PHI_PLUS, // |Φ⁺⟩ = (|00⟩ + |11⟩)/√2 + BELL_PHI_MINUS, // |Φ⁻⟩ = (|00⟩ - |11⟩)/√2 + BELL_PSI_PLUS, // |Ψ⁺⟩ = (|01⟩ + |10⟩)/√2 + BELL_PSI_MINUS // |Ψ⁻⟩ = (|01⟩ - |10⟩)/√2 +} bell_state_type_t; + +// ============================================================================ +// BELL STATE CREATION +// ============================================================================ + +/** + * @brief Create Bell state |Φ⁺⟩ = (|00⟩ + |11⟩)/√2 + * + * Maximally entangled state. Most common Bell state. + * + * @param state Quantum state (must have at least 2 qubits) + * @param qubit1 First qubit index + * @param qubit2 Second qubit index + * @return QS_SUCCESS or error code + */ +qs_error_t create_bell_state_phi_plus(quantum_state_t *state, int qubit1, int qubit2); + +/** + * @brief Create Bell state |Φ⁻⟩ = (|00⟩ - |11⟩)/√2 + */ +qs_error_t create_bell_state_phi_minus(quantum_state_t *state, int qubit1, int qubit2); + +/** + * @brief Create Bell state |Ψ⁺⟩ = (|01⟩ + |10⟩)/√2 + */ +qs_error_t create_bell_state_psi_plus(quantum_state_t *state, int qubit1, int qubit2); + +/** + * @brief Create Bell state |Ψ⁻⟩ = (|01⟩ - |10⟩)/√2 + */ +qs_error_t create_bell_state_psi_minus(quantum_state_t *state, int qubit1, int qubit2); + +/** + * @brief Create arbitrary Bell state + * @param state Quantum state + * @param qubit1 First qubit + * @param qubit2 Second qubit + * @param type Bell state type + * @return QS_SUCCESS or error code + */ +qs_error_t create_bell_state(quantum_state_t *state, int qubit1, int qubit2, bell_state_type_t type); + +// ============================================================================ +// CORRELATION MEASUREMENT +// ============================================================================ + +/** + * @brief Measure correlation E(θ_a, θ_b) = ⟨A(θ_a) ⊗ B(θ_b)⟩ + * + * Measures correlation between two qubits at specified angles. + * Returns value between -1 and +1. + * + * SECURITY: Uses cryptographically secure entropy for measurement sampling. + * + * @param state Quantum state + * @param qubit_a Alice's qubit + * @param qubit_b Bob's qubit + * @param angle_a Alice's measurement angle + * @param angle_b Bob's measurement angle + * @param num_samples Number of measurements to perform + * @param entropy Secure entropy source + * @return Correlation value between -1 and +1 + */ +double measure_correlation( + quantum_state_t *state, + int qubit_a, + int qubit_b, + double angle_a, + double angle_b, + size_t num_samples, + quantum_entropy_ctx_t *entropy +); + +/** + * @brief Calculate CHSH S-parameter from correlations + * + * S = |E(a,b) - E(a,b') + E(a',b) + E(a',b')| + * + * Classical bound: S ≤ 2 + * Quantum bound: S ≤ 2√2 ≈ 2.828 + * + * @param correlations Array of 4 correlations [E(a,b), E(a,b'), E(a',b), E(a',b')] + * @return CHSH S-parameter + */ +double calculate_chsh_parameter(const double correlations[4]); + +// ============================================================================ +// FULL BELL TEST +// ============================================================================ + +/** + * @brief Perform complete CHSH inequality test + * + * Creates maximally entangled state and measures violations of + * Bell's inequality. This is the definitive test for quantum behavior. + * + * SECURITY: Uses cryptographically secure entropy for all measurements. + * + * @param state Quantum state (will be modified) + * @param qubit_a Alice's qubit index + * @param qubit_b Bob's qubit index + * @param num_measurements Number of measurement pairs (recommend ≥10000) + * @param settings Measurement angles (NULL for optimal settings) + * @param entropy Secure entropy source + * @return Complete Bell test results with statistical analysis + */ +bell_test_result_t bell_test_chsh( + quantum_state_t *state, + int qubit_a, + int qubit_b, + size_t num_measurements, + const bell_measurement_settings_t *settings, + quantum_entropy_ctx_t *entropy +); + +/** + * @brief Get optimal CHSH measurement settings + * + * Returns settings that maximize quantum violation: + * a1=0, a2=π/2, b1=π/4, b2=-π/4 + * + * These angles give CHSH = 2√2 for maximally entangled states. + * + * @param settings Output: optimal measurement settings + */ +void bell_get_optimal_settings(bell_measurement_settings_t *settings); + +/** + * @brief Verify Bell test results meet quantum criteria + * + * Checks if results demonstrate genuine quantum behavior: + * - CHSH > 2 (violates classical bound) + * - p-value < 0.01 (statistically significant) + * - CHSH close to theoretical maximum (2√2) + * + * @param result Bell test result + * @return 1 if quantum behavior confirmed, 0 otherwise + */ +int bell_test_confirms_quantum(const bell_test_result_t *result); + +/** + * @brief Print Bell test results + * @param result Bell test result + */ +void bell_test_print_results(const bell_test_result_t *result); + +/** + * @brief Calculate theoretical CHSH for given Bell state + * + * Returns theoretical maximum CHSH value for the state. + * For maximally entangled states: 2√2 ≈ 2.828 + * + * @param state_type Bell state type + * @return Theoretical CHSH value + */ +double bell_theoretical_chsh(bell_state_type_t state_type); + +// ============================================================================ +// CONTINUOUS VERIFICATION +// ============================================================================ + +/** + * @brief Continuous Bell test monitor + * + * Tracks Bell test results over time for continuous quantum verification + */ +typedef struct { + bell_test_result_t *test_history; + size_t num_tests; + size_t capacity; + + double average_chsh; + double min_chsh; + double max_chsh; + double variance_chsh; + + int all_tests_quantum; + size_t num_classical_violations; +} bell_test_monitor_t; + +/** + * @brief Initialize Bell test monitor + * @param monitor Monitor structure + * @param capacity Maximum number of tests to track + * @return 0 on success, -1 on error + */ +int bell_monitor_init(bell_test_monitor_t *monitor, size_t capacity); + +/** + * @brief Add test result to monitor + * @param monitor Monitor structure + * @param result Bell test result + */ +void bell_monitor_add_result(bell_test_monitor_t *monitor, const bell_test_result_t *result); + +/** + * @brief Get monitor statistics + * @param monitor Monitor structure + */ +void bell_monitor_get_statistics(const bell_test_monitor_t *monitor); + +/** + * @brief Free monitor resources + * @param monitor Monitor structure + */ +void bell_monitor_free(bell_test_monitor_t *monitor); + +#endif /* BELL_TEST_H */ \ No newline at end of file diff --git a/vendor/quantum_rng/src/quantum_rng/cuda/cuda_bridge.cu b/vendor/quantum_rng/src/quantum_rng/cuda/cuda_bridge.cu new file mode 100644 index 0000000..b46b881 --- /dev/null +++ b/vendor/quantum_rng/src/quantum_rng/cuda/cuda_bridge.cu @@ -0,0 +1,668 @@ +/** + * @file cuda_bridge.cu + * @brief NVIDIA CUDA implementation of the quantum compute backend. + * + * Mirrors the Apple Metal backend (src/quantum_rng/metal/). Amplitudes are + * stored as interleaved single-precision complex (cuda_complex_t, 8 bytes), + * identical to Metal's Complex layout. Buffers use CUDA unified memory + * (cudaMallocManaged) so the host can read results after cuda_wait_completion. + * + * Build (RTX 3090 = sm_86): + * nvcc -O3 -arch=sm_86 -Isrc -c cuda_bridge.cu + */ + +#include "cuda_bridge.h" + +#include +#include +#include +#include + +// 1/sqrt(2) +#define SQRT2_INV 0.7071067811865476f + +// ============================================================================ +// CONTEXT & BUFFER STRUCTS +// ============================================================================ + +struct cuda_compute_ctx { + int device_id; + cudaDeviceProp props; + cudaEvent_t ev_start; + cudaEvent_t ev_stop; + double last_execution_time; // seconds + int perf_monitoring; + char error[256]; +}; + +struct cuda_buffer { + void* ptr; // unified-memory pointer (host + device usable) + size_t size; // bytes +}; + +// Default launch geometry +#define THREADS_PER_BLOCK 256 + +static inline int div_up(uint32_t a, uint32_t b) { + return (int)((a + b - 1u) / b); +} + +static void set_error(cuda_compute_ctx_t* ctx, const char* msg) { + if (!ctx) return; + snprintf(ctx->error, sizeof(ctx->error), "%s", msg); +} + +static int check_cuda(cuda_compute_ctx_t* ctx, cudaError_t err, const char* where) { + if (err != cudaSuccess) { + if (ctx) { + snprintf(ctx->error, sizeof(ctx->error), "%s: %s", + where, cudaGetErrorString(err)); + } + return -1; + } + return 0; +} + +// ============================================================================ +// TIMING HELPERS +// ============================================================================ + +static inline void timing_begin(cuda_compute_ctx_t* ctx) { + if (ctx && ctx->perf_monitoring) { + cudaEventRecord(ctx->ev_start, 0); + } +} + +static inline void timing_end(cuda_compute_ctx_t* ctx) { + if (ctx && ctx->perf_monitoring) { + cudaEventRecord(ctx->ev_stop, 0); + } +} + +// ============================================================================ +// KERNELS +// ============================================================================ + +// Hadamard on a single qubit. Launched with state_dim/2 threads; each thread +// owns one amplitude pair (idx0, idx1) with bit `qubit_index` cleared/set. +__global__ void k_hadamard(cuda_complex_t* amp, uint32_t qubit_index, uint32_t state_dim) { + uint32_t gid = blockIdx.x * blockDim.x + threadIdx.x; + uint32_t half = state_dim >> 1; + if (gid >= half) return; + + uint32_t stride = 1u << qubit_index; + uint32_t block = gid / stride; + uint32_t pos = gid % stride; + uint32_t idx0 = block * (stride << 1) + pos; + uint32_t idx1 = idx0 + stride; + + cuda_complex_t a0 = amp[idx0]; + cuda_complex_t a1 = amp[idx1]; + amp[idx0].real = (a0.real + a1.real) * SQRT2_INV; + amp[idx0].imag = (a0.imag + a1.imag) * SQRT2_INV; + amp[idx1].real = (a0.real - a1.real) * SQRT2_INV; + amp[idx1].imag = (a0.imag - a1.imag) * SQRT2_INV; +} + +// Oracle: phase-flip a single target state. +__global__ void k_oracle_single(cuda_complex_t* amp, uint32_t target) { + // Launched with a single thread; trivial but keeps everything on GPU. + amp[target].real = -amp[target].real; + amp[target].imag = -amp[target].imag; +} + +// Oracle: phase-flip any of a set of marked states. +__global__ void k_oracle_multi(cuda_complex_t* amp, const uint32_t* marked, + uint32_t num_marked, uint32_t state_dim) { + uint32_t gid = blockIdx.x * blockDim.x + threadIdx.x; + if (gid >= state_dim) return; + for (uint32_t i = 0; i < num_marked; i++) { + if (gid == marked[i]) { + amp[gid].real = -amp[gid].real; + amp[gid].imag = -amp[gid].imag; + return; + } + } +} + +// Reduction pass: accumulate real/imag sums into a managed float[2] via atomics. +__global__ void k_sum_reduce(const cuda_complex_t* amp, uint32_t state_dim, + float* sum_out /* [0]=real [1]=imag */) { + __shared__ float s_real[THREADS_PER_BLOCK]; + __shared__ float s_imag[THREADS_PER_BLOCK]; + + uint32_t tid = threadIdx.x; + uint32_t gid = blockIdx.x * blockDim.x + tid; + uint32_t grid_stride = blockDim.x * gridDim.x; + + float sr = 0.0f, si = 0.0f; + for (uint32_t i = gid; i < state_dim; i += grid_stride) { + sr += amp[i].real; + si += amp[i].imag; + } + s_real[tid] = sr; + s_imag[tid] = si; + __syncthreads(); + + for (uint32_t s = blockDim.x >> 1; s > 0; s >>= 1) { + if (tid < s) { + s_real[tid] += s_real[tid + s]; + s_imag[tid] += s_imag[tid + s]; + } + __syncthreads(); + } + if (tid == 0) { + atomicAdd(&sum_out[0], s_real[0]); + atomicAdd(&sum_out[1], s_imag[0]); + } +} + +// Inversion about the mean: amp = 2*mean - amp. +__global__ void k_inversion(cuda_complex_t* amp, uint32_t state_dim, + const float* sum_in /* real,imag */) { + uint32_t gid = blockIdx.x * blockDim.x + threadIdx.x; + if (gid >= state_dim) return; + float mean_r = sum_in[0] / (float)state_dim; + float mean_i = sum_in[1] / (float)state_dim; + amp[gid].real = 2.0f * mean_r - amp[gid].real; + amp[gid].imag = 2.0f * mean_i - amp[gid].imag; +} + +// Pauli X: swap amplitude pairs across qubit bit. +__global__ void k_pauli_x(cuda_complex_t* amp, uint32_t qubit_index, uint32_t state_dim) { + uint32_t gid = blockIdx.x * blockDim.x + threadIdx.x; + uint32_t half = state_dim >> 1; + if (gid >= half) return; + + uint32_t stride = 1u << qubit_index; + uint32_t block = gid / stride; + uint32_t pos = gid % stride; + uint32_t idx0 = block * (stride << 1) + pos; + uint32_t idx1 = idx0 + stride; + + cuda_complex_t tmp = amp[idx0]; + amp[idx0] = amp[idx1]; + amp[idx1] = tmp; +} + +// Pauli Z: negate amplitudes where qubit bit is set. +__global__ void k_pauli_z(cuda_complex_t* amp, uint32_t qubit_index, uint32_t state_dim) { + uint32_t gid = blockIdx.x * blockDim.x + threadIdx.x; + if (gid >= state_dim) return; + if (gid & (1u << qubit_index)) { + amp[gid].real = -amp[gid].real; + amp[gid].imag = -amp[gid].imag; + } +} + +// Probabilities: prob[i] = real^2 + imag^2. +__global__ void k_probabilities(const cuda_complex_t* amp, float* prob, uint32_t state_dim) { + uint32_t gid = blockIdx.x * blockDim.x + threadIdx.x; + if (gid >= state_dim) return; + float re = amp[gid].real; + float im = amp[gid].imag; + prob[gid] = re * re + im * im; +} + +// Normalize: divide each amplitude by norm. +__global__ void k_normalize(cuda_complex_t* amp, float norm, uint32_t state_dim) { + uint32_t gid = blockIdx.x * blockDim.x + threadIdx.x; + if (gid >= state_dim) return; + float inv = 1.0f / norm; + amp[gid].real *= inv; + amp[gid].imag *= inv; +} + +// Set state to computational basis |0...0>. +__global__ void k_init_zero(cuda_complex_t* amp, uint32_t state_dim) { + uint32_t gid = blockIdx.x * blockDim.x + threadIdx.x; + if (gid >= state_dim) return; + amp[gid].real = (gid == 0) ? 1.0f : 0.0f; + amp[gid].imag = 0.0f; +} + +// ---------------------------------------------------------------------------- +// BATCH GROVER: one block per independent search. +// Each block owns one state of `state_dim` amplitudes and runs a full search: +// uniform superposition (Hadamard on all qubits) -> N * (oracle, diffusion) +// -> argmax of |amp|^2 written to results[block]. +// Diffusion is inversion about the mean (matches single-search path). +// ---------------------------------------------------------------------------- +__global__ void k_grover_batch(cuda_complex_t* batch_states, + const uint32_t* targets, + uint32_t* results, + uint32_t num_searches, + uint32_t num_qubits, + uint32_t num_iterations) { + uint32_t search = blockIdx.x; + if (search >= num_searches) return; + + const uint32_t state_dim = 1u << num_qubits; + cuda_complex_t* amp = &batch_states[(size_t)search * state_dim]; + const uint32_t target = targets[search]; + + uint32_t tid = threadIdx.x; + uint32_t nthreads = blockDim.x; + + __shared__ float s_real[THREADS_PER_BLOCK]; + __shared__ float s_imag[THREADS_PER_BLOCK]; + + // ---- Initialize to |0...0> ---- + for (uint32_t i = tid; i < state_dim; i += nthreads) { + amp[i].real = (i == 0) ? 1.0f : 0.0f; + amp[i].imag = 0.0f; + } + __syncthreads(); + + // ---- Hadamard on all qubits (uniform superposition) ---- + for (uint32_t q = 0; q < num_qubits; q++) { + uint32_t stride = 1u << q; + for (uint32_t idx = tid; idx < state_dim; idx += nthreads) { + if ((idx & stride) == 0) { + uint32_t j = idx | stride; + cuda_complex_t a0 = amp[idx]; + cuda_complex_t a1 = amp[j]; + amp[idx].real = (a0.real + a1.real) * SQRT2_INV; + amp[idx].imag = (a0.imag + a1.imag) * SQRT2_INV; + amp[j].real = (a0.real - a1.real) * SQRT2_INV; + amp[j].imag = (a0.imag - a1.imag) * SQRT2_INV; + } + } + __syncthreads(); + } + + // ---- Grover iterations ---- + for (uint32_t iter = 0; iter < num_iterations; iter++) { + // Oracle: phase flip target + if (tid == 0) { + amp[target].real = -amp[target].real; + amp[target].imag = -amp[target].imag; + } + __syncthreads(); + + // Diffusion: inversion about the mean + float sr = 0.0f, si = 0.0f; + for (uint32_t i = tid; i < state_dim; i += nthreads) { + sr += amp[i].real; + si += amp[i].imag; + } + s_real[tid] = sr; + s_imag[tid] = si; + __syncthreads(); + for (uint32_t s = nthreads >> 1; s > 0; s >>= 1) { + if (tid < s) { + s_real[tid] += s_real[tid + s]; + s_imag[tid] += s_imag[tid + s]; + } + __syncthreads(); + } + float mean_r = s_real[0] / (float)state_dim; + float mean_i = s_imag[0] / (float)state_dim; + __syncthreads(); + + for (uint32_t i = tid; i < state_dim; i += nthreads) { + amp[i].real = 2.0f * mean_r - amp[i].real; + amp[i].imag = 2.0f * mean_i - amp[i].imag; + } + __syncthreads(); + } + + // ---- Measurement: argmax of |amp|^2 ---- + float max_prob = -1.0f; + uint32_t max_idx = 0; + for (uint32_t i = tid; i < state_dim; i += nthreads) { + float p = amp[i].real * amp[i].real + amp[i].imag * amp[i].imag; + if (p > max_prob) { + max_prob = p; + max_idx = i; + } + } + s_real[tid] = max_prob; + s_imag[tid] = (float)max_idx; + __syncthreads(); + for (uint32_t s = nthreads >> 1; s > 0; s >>= 1) { + if (tid < s) { + if (s_real[tid + s] > s_real[tid]) { + s_real[tid] = s_real[tid + s]; + s_imag[tid] = s_imag[tid + s]; + } + } + __syncthreads(); + } + if (tid == 0) { + results[search] = (uint32_t)s_imag[0]; + } +} + +// ============================================================================ +// CONTEXT LIFECYCLE +// ============================================================================ + +extern "C" cuda_compute_ctx_t* cuda_compute_init(void) { + int count = 0; + if (cudaGetDeviceCount(&count) != cudaSuccess || count <= 0) { + return NULL; + } + cuda_compute_ctx_t* ctx = (cuda_compute_ctx_t*)calloc(1, sizeof(cuda_compute_ctx_t)); + if (!ctx) return NULL; + + ctx->device_id = 0; + if (check_cuda(ctx, cudaSetDevice(ctx->device_id), "cudaSetDevice") != 0) { + free(ctx); + return NULL; + } + if (check_cuda(ctx, cudaGetDeviceProperties(&ctx->props, ctx->device_id), + "cudaGetDeviceProperties") != 0) { + free(ctx); + return NULL; + } + cudaEventCreate(&ctx->ev_start); + cudaEventCreate(&ctx->ev_stop); + ctx->last_execution_time = 0.0; + ctx->perf_monitoring = 1; + set_error(ctx, "no error"); + return ctx; +} + +extern "C" void cuda_compute_free(cuda_compute_ctx_t* ctx) { + if (!ctx) return; + cudaEventDestroy(ctx->ev_start); + cudaEventDestroy(ctx->ev_stop); + free(ctx); +} + +extern "C" int cuda_is_available(void) { + int count = 0; + if (cudaGetDeviceCount(&count) != cudaSuccess) return 0; + return count > 0 ? 1 : 0; +} + +extern "C" void cuda_get_device_info(cuda_compute_ctx_t* ctx, char* name, + uint32_t* max_threads, uint32_t* num_cores) { + if (!ctx) return; + if (name) snprintf(name, 256, "%s", ctx->props.name); + if (max_threads) *max_threads = (uint32_t)ctx->props.maxThreadsPerBlock; + if (num_cores) *num_cores = (uint32_t)ctx->props.multiProcessorCount; +} + +// ============================================================================ +// MEMORY +// ============================================================================ + +extern "C" cuda_buffer_t* cuda_buffer_create(cuda_compute_ctx_t* ctx, size_t size) { + cuda_buffer_t* buf = (cuda_buffer_t*)calloc(1, sizeof(cuda_buffer_t)); + if (!buf) return NULL; + if (check_cuda(ctx, cudaMallocManaged(&buf->ptr, size), "cudaMallocManaged") != 0) { + free(buf); + return NULL; + } + buf->size = size; + return buf; +} + +extern "C" cuda_buffer_t* cuda_buffer_create_from_data(cuda_compute_ctx_t* ctx, + void* data, size_t size) { + cuda_buffer_t* buf = cuda_buffer_create(ctx, size); + if (!buf) return NULL; + if (data) memcpy(buf->ptr, data, size); + return buf; +} + +extern "C" void* cuda_buffer_contents(cuda_buffer_t* buffer) { + return buffer ? buffer->ptr : NULL; +} + +extern "C" void cuda_buffer_free(cuda_buffer_t* buffer) { + if (!buffer) return; + if (buffer->ptr) cudaFree(buffer->ptr); + free(buffer); +} + +// ============================================================================ +// GATE OPERATIONS +// ============================================================================ + +extern "C" int cuda_hadamard(cuda_compute_ctx_t* ctx, cuda_buffer_t* amplitudes, + uint32_t qubit_index, uint32_t state_dim) { + if (!ctx || !amplitudes) return -1; + cuda_complex_t* amp = (cuda_complex_t*)amplitudes->ptr; + uint32_t half = state_dim >> 1; + timing_begin(ctx); + k_hadamard<<>>(amp, qubit_index, state_dim); + timing_end(ctx); + return check_cuda(ctx, cudaGetLastError(), "cuda_hadamard"); +} + +extern "C" int cuda_hadamard_all(cuda_compute_ctx_t* ctx, cuda_buffer_t* amplitudes, + uint32_t num_qubits, uint32_t state_dim) { + if (!ctx || !amplitudes) return -1; + cuda_complex_t* amp = (cuda_complex_t*)amplitudes->ptr; + uint32_t half = state_dim >> 1; + int blocks = div_up(half, THREADS_PER_BLOCK); + timing_begin(ctx); + // Kernel boundaries provide the global synchronization needed between qubits. + for (uint32_t q = 0; q < num_qubits; q++) { + k_hadamard<<>>(amp, q, state_dim); + } + timing_end(ctx); + return check_cuda(ctx, cudaGetLastError(), "cuda_hadamard_all"); +} + +extern "C" int cuda_oracle(cuda_compute_ctx_t* ctx, cuda_buffer_t* amplitudes, + uint32_t target_state, uint32_t state_dim) { + if (!ctx || !amplitudes) return -1; + (void)state_dim; + cuda_complex_t* amp = (cuda_complex_t*)amplitudes->ptr; + timing_begin(ctx); + k_oracle_single<<<1, 1>>>(amp, target_state); + timing_end(ctx); + return check_cuda(ctx, cudaGetLastError(), "cuda_oracle"); +} + +extern "C" int cuda_oracle_multi(cuda_compute_ctx_t* ctx, cuda_buffer_t* amplitudes, + const uint32_t* marked_states, uint32_t num_marked, + uint32_t state_dim) { + if (!ctx || !amplitudes) return -1; + cuda_complex_t* amp = (cuda_complex_t*)amplitudes->ptr; + + // Copy marked list into unified memory the kernel can read. + uint32_t* d_marked = NULL; + if (check_cuda(ctx, cudaMallocManaged(&d_marked, num_marked * sizeof(uint32_t)), + "cuda_oracle_multi alloc") != 0) { + return -1; + } + memcpy(d_marked, marked_states, num_marked * sizeof(uint32_t)); + + timing_begin(ctx); + k_oracle_multi<<>>( + amp, d_marked, num_marked, state_dim); + timing_end(ctx); + int rc = check_cuda(ctx, cudaGetLastError(), "cuda_oracle_multi"); + cudaDeviceSynchronize(); + cudaFree(d_marked); + return rc; +} + +extern "C" int cuda_grover_diffusion(cuda_compute_ctx_t* ctx, cuda_buffer_t* amplitudes, + uint32_t num_qubits, uint32_t state_dim) { + if (!ctx || !amplitudes) return -1; + (void)num_qubits; + cuda_complex_t* amp = (cuda_complex_t*)amplitudes->ptr; + + float* sum = NULL; + if (check_cuda(ctx, cudaMallocManaged(&sum, 2 * sizeof(float)), + "cuda_grover_diffusion alloc") != 0) { + return -1; + } + sum[0] = 0.0f; + sum[1] = 0.0f; + + int blocks = div_up(state_dim, THREADS_PER_BLOCK); + int reduce_blocks = blocks < 256 ? blocks : 256; // cap grid for reduction + + timing_begin(ctx); + k_sum_reduce<<>>(amp, state_dim, sum); + k_inversion<<>>(amp, state_dim, sum); + timing_end(ctx); + int rc = check_cuda(ctx, cudaGetLastError(), "cuda_grover_diffusion"); + cudaDeviceSynchronize(); + cudaFree(sum); + return rc; +} + +extern "C" int cuda_pauli_x(cuda_compute_ctx_t* ctx, cuda_buffer_t* amplitudes, + uint32_t qubit_index, uint32_t state_dim) { + if (!ctx || !amplitudes) return -1; + cuda_complex_t* amp = (cuda_complex_t*)amplitudes->ptr; + uint32_t half = state_dim >> 1; + timing_begin(ctx); + k_pauli_x<<>>(amp, qubit_index, state_dim); + timing_end(ctx); + return check_cuda(ctx, cudaGetLastError(), "cuda_pauli_x"); +} + +extern "C" int cuda_pauli_z(cuda_compute_ctx_t* ctx, cuda_buffer_t* amplitudes, + uint32_t qubit_index, uint32_t state_dim) { + if (!ctx || !amplitudes) return -1; + cuda_complex_t* amp = (cuda_complex_t*)amplitudes->ptr; + timing_begin(ctx); + k_pauli_z<<>>(amp, qubit_index, state_dim); + timing_end(ctx); + return check_cuda(ctx, cudaGetLastError(), "cuda_pauli_z"); +} + +// ============================================================================ +// PROBABILITY & MEASUREMENT +// ============================================================================ + +extern "C" int cuda_compute_probabilities(cuda_compute_ctx_t* ctx, + cuda_buffer_t* amplitudes, + cuda_buffer_t* probabilities, + uint32_t state_dim) { + if (!ctx || !amplitudes || !probabilities) return -1; + cuda_complex_t* amp = (cuda_complex_t*)amplitudes->ptr; + float* prob = (float*)probabilities->ptr; + timing_begin(ctx); + k_probabilities<<>>(amp, prob, state_dim); + timing_end(ctx); + return check_cuda(ctx, cudaGetLastError(), "cuda_compute_probabilities"); +} + +extern "C" int cuda_normalize(cuda_compute_ctx_t* ctx, cuda_buffer_t* amplitudes, + float norm, uint32_t state_dim) { + if (!ctx || !amplitudes) return -1; + cuda_complex_t* amp = (cuda_complex_t*)amplitudes->ptr; + timing_begin(ctx); + k_normalize<<>>(amp, norm, state_dim); + timing_end(ctx); + return check_cuda(ctx, cudaGetLastError(), "cuda_normalize"); +} + +// ============================================================================ +// GROVER (SINGLE) +// ============================================================================ + +extern "C" int cuda_grover_iteration(cuda_compute_ctx_t* ctx, cuda_buffer_t* amplitudes, + uint32_t target_state, uint32_t num_qubits, + uint32_t state_dim) { + if (cuda_oracle(ctx, amplitudes, target_state, state_dim) != 0) return -1; + if (cuda_grover_diffusion(ctx, amplitudes, num_qubits, state_dim) != 0) return -1; + return 0; +} + +extern "C" int cuda_grover_search(cuda_compute_ctx_t* ctx, cuda_buffer_t* amplitudes, + uint32_t target_state, uint32_t num_qubits, + uint32_t state_dim, uint32_t num_iterations) { + if (!ctx || !amplitudes) return -1; + cuda_complex_t* amp = (cuda_complex_t*)amplitudes->ptr; + + timing_begin(ctx); + // Initialize to |0...0>, then build uniform superposition. + k_init_zero<<>>(amp, state_dim); + if (cuda_hadamard_all(ctx, amplitudes, num_qubits, state_dim) != 0) return -1; + + for (uint32_t iter = 0; iter < num_iterations; iter++) { + if (cuda_grover_iteration(ctx, amplitudes, target_state, num_qubits, state_dim) != 0) { + return -1; + } + } + timing_end(ctx); + return check_cuda(ctx, cudaGetLastError(), "cuda_grover_search"); +} + +extern "C" int cuda_grover_batch_search(cuda_compute_ctx_t* ctx, + cuda_buffer_t* batch_states, + const uint32_t* targets, + uint32_t* results, + uint32_t num_searches, + uint32_t num_qubits, + uint32_t num_iterations) { + if (!ctx || !batch_states) return -1; + cuda_complex_t* amp = (cuda_complex_t*)batch_states->ptr; + + uint32_t* d_targets = NULL; + uint32_t* d_results = NULL; + if (check_cuda(ctx, cudaMallocManaged(&d_targets, num_searches * sizeof(uint32_t)), + "batch targets alloc") != 0) return -1; + if (check_cuda(ctx, cudaMallocManaged(&d_results, num_searches * sizeof(uint32_t)), + "batch results alloc") != 0) { + cudaFree(d_targets); + return -1; + } + memcpy(d_targets, targets, num_searches * sizeof(uint32_t)); + + // One block per independent search. + timing_begin(ctx); + k_grover_batch<<>>( + amp, d_targets, d_results, num_searches, num_qubits, num_iterations); + timing_end(ctx); + int rc = check_cuda(ctx, cudaGetLastError(), "cuda_grover_batch_search"); + cudaDeviceSynchronize(); + + if (rc == 0 && results) { + memcpy(results, d_results, num_searches * sizeof(uint32_t)); + } + cudaFree(d_targets); + cudaFree(d_results); + return rc; +} + +// ============================================================================ +// SYNC & UTILITIES +// ============================================================================ + +extern "C" void cuda_wait_completion(cuda_compute_ctx_t* ctx) { + if (!ctx) return; + cudaDeviceSynchronize(); + if (ctx->perf_monitoring) { + float ms = 0.0f; + if (cudaEventElapsedTime(&ms, ctx->ev_start, ctx->ev_stop) == cudaSuccess) { + ctx->last_execution_time = (double)ms / 1000.0; + } + } +} + +extern "C" double cuda_get_last_execution_time(cuda_compute_ctx_t* ctx) { + return ctx ? ctx->last_execution_time : 0.0; +} + +extern "C" void cuda_set_performance_monitoring(cuda_compute_ctx_t* ctx, int enable) { + if (ctx) ctx->perf_monitoring = enable ? 1 : 0; +} + +extern "C" void cuda_print_device_info(cuda_compute_ctx_t* ctx) { + if (!ctx) return; + const cudaDeviceProp* p = &ctx->props; + printf("CUDA Device: %s\n", p->name); + printf(" Compute capability: %d.%d\n", p->major, p->minor); + printf(" SMs (multiprocessors): %d\n", p->multiProcessorCount); + printf(" Max threads/block: %d\n", p->maxThreadsPerBlock); + printf(" Global memory: %.1f GB\n", (double)p->totalGlobalMem / (1024.0 * 1024.0 * 1024.0)); + printf(" Memory clock: %.0f MHz, bus width: %d-bit\n", + p->memoryClockRate / 1000.0, p->memoryBusWidth); + printf(" Managed memory (unified): %s\n", p->managedMemory ? "yes" : "no"); +} + +extern "C" const char* cuda_get_error(cuda_compute_ctx_t* ctx) { + return ctx ? ctx->error : "no context"; +} diff --git a/vendor/quantum_rng/src/quantum_rng/cuda/cuda_bridge.h b/vendor/quantum_rng/src/quantum_rng/cuda/cuda_bridge.h new file mode 100644 index 0000000..d1cd8f4 --- /dev/null +++ b/vendor/quantum_rng/src/quantum_rng/cuda/cuda_bridge.h @@ -0,0 +1,296 @@ +#ifndef CUDA_BRIDGE_H +#define CUDA_BRIDGE_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file cuda_bridge.h + * @brief C API for NVIDIA CUDA GPU acceleration of quantum operations + * + * Provides a C interface to a CUDA compute pipeline for quantum operations, + * mirroring the Apple Metal backend in src/quantum_rng/metal/. The data layout + * (interleaved single-precision complex amplitudes) is identical to Metal's so + * that the two backends are drop-in interchangeable. + * + * Memory model: buffers are allocated with cudaMallocManaged (CUDA unified + * memory), so cuda_buffer_contents() returns a host-usable pointer, mirroring + * Metal's zero-copy shared storage. Call cuda_wait_completion() before reading + * results back on the host. + */ + +// Interleaved single-precision complex amplitude (8 bytes, matches Metal Complex) +typedef struct { + float real; + float imag; +} cuda_complex_t; + +// Opaque handle to CUDA compute context +typedef struct cuda_compute_ctx cuda_compute_ctx_t; + +// CUDA buffer handle (unified memory) +typedef struct cuda_buffer cuda_buffer_t; + +// ============================================================================ +// INITIALIZATION & CLEANUP +// ============================================================================ + +/** + * @brief Initialize CUDA compute context. + * @return CUDA compute context or NULL on failure. + */ +cuda_compute_ctx_t* cuda_compute_init(void); + +/** + * @brief Free CUDA compute context. + */ +void cuda_compute_free(cuda_compute_ctx_t* ctx); + +/** + * @brief Check if a CUDA device is available on this system. + * @return 1 if available, 0 otherwise. + */ +int cuda_is_available(void); + +/** + * @brief Get GPU device information. + * @param ctx CUDA compute context + * @param name Output buffer for device name (min 256 bytes) + * @param max_threads Output: max threads per block + * @param num_cores Output: number of streaming multiprocessors (SMs) + */ +void cuda_get_device_info( + cuda_compute_ctx_t* ctx, + char* name, + uint32_t* max_threads, + uint32_t* num_cores +); + +// ============================================================================ +// MEMORY MANAGEMENT (UNIFIED MEMORY) +// ============================================================================ + +/** + * @brief Allocate a CUDA unified-memory buffer. + */ +cuda_buffer_t* cuda_buffer_create(cuda_compute_ctx_t* ctx, size_t size); + +/** + * @brief Allocate a CUDA unified-memory buffer and copy existing host data in. + */ +cuda_buffer_t* cuda_buffer_create_from_data( + cuda_compute_ctx_t* ctx, + void* data, + size_t size +); + +/** + * @brief Get a host-usable pointer to a CUDA buffer. + */ +void* cuda_buffer_contents(cuda_buffer_t* buffer); + +/** + * @brief Free a CUDA buffer. + */ +void cuda_buffer_free(cuda_buffer_t* buffer); + +// ============================================================================ +// QUANTUM GATE OPERATIONS (GPU-ACCELERATED) +// ============================================================================ + +/** + * @brief GPU Hadamard gate on a single qubit. + * @param qubit_index Index of qubit to apply gate to + * @param state_dim Number of amplitudes (2^num_qubits) + * @return 0 on success, -1 on error + */ +int cuda_hadamard( + cuda_compute_ctx_t* ctx, + cuda_buffer_t* amplitudes, + uint32_t qubit_index, + uint32_t state_dim +); + +/** + * @brief GPU Hadamard applied to all qubits (uniform superposition from |0..0>). + */ +int cuda_hadamard_all( + cuda_compute_ctx_t* ctx, + cuda_buffer_t* amplitudes, + uint32_t num_qubits, + uint32_t state_dim +); + +/** + * @brief GPU oracle (phase flip of a single marked state). + */ +int cuda_oracle( + cuda_compute_ctx_t* ctx, + cuda_buffer_t* amplitudes, + uint32_t target_state, + uint32_t state_dim +); + +/** + * @brief GPU oracle with multiple marked states. + */ +int cuda_oracle_multi( + cuda_compute_ctx_t* ctx, + cuda_buffer_t* amplitudes, + const uint32_t* marked_states, + uint32_t num_marked, + uint32_t state_dim +); + +/** + * @brief GPU Grover diffusion (inversion about the mean amplitude). + * mean = sum(amp)/N ; amp = 2*mean - amp. + */ +int cuda_grover_diffusion( + cuda_compute_ctx_t* ctx, + cuda_buffer_t* amplitudes, + uint32_t num_qubits, + uint32_t state_dim +); + +/** + * @brief GPU Pauli X (bit flip on qubit). + */ +int cuda_pauli_x( + cuda_compute_ctx_t* ctx, + cuda_buffer_t* amplitudes, + uint32_t qubit_index, + uint32_t state_dim +); + +/** + * @brief GPU Pauli Z (phase flip where qubit bit is set). + */ +int cuda_pauli_z( + cuda_compute_ctx_t* ctx, + cuda_buffer_t* amplitudes, + uint32_t qubit_index, + uint32_t state_dim +); + +// ============================================================================ +// PROBABILITY & MEASUREMENT +// ============================================================================ + +/** + * @brief Compute probabilities from amplitudes: prob[i] = real^2 + imag^2. + * @param probabilities Output buffer of state_dim floats + */ +int cuda_compute_probabilities( + cuda_compute_ctx_t* ctx, + cuda_buffer_t* amplitudes, + cuda_buffer_t* probabilities, + uint32_t state_dim +); + +/** + * @brief Normalize a quantum state: divide each amplitude by norm. + */ +int cuda_normalize( + cuda_compute_ctx_t* ctx, + cuda_buffer_t* amplitudes, + float norm, + uint32_t state_dim +); + +// ============================================================================ +// GROVER SEARCH (SINGLE + BATCH) +// ============================================================================ + +/** + * @brief One Grover iteration on GPU: oracle(target) then diffusion. + */ +int cuda_grover_iteration( + cuda_compute_ctx_t* ctx, + cuda_buffer_t* amplitudes, + uint32_t target_state, + uint32_t num_qubits, + uint32_t state_dim +); + +/** + * @brief Complete Grover search on GPU. + * + * Initializes to a uniform superposition then runs num_iterations of + * (oracle -> diffusion). The amplitude of target_state should dominate. + */ +int cuda_grover_search( + cuda_compute_ctx_t* ctx, + cuda_buffer_t* amplitudes, + uint32_t target_state, + uint32_t num_qubits, + uint32_t state_dim, + uint32_t num_iterations +); + +/** + * @brief Execute MULTIPLE complete Grover searches in parallel. + * + * One CUDA block per independent search. Each block runs a full Grover search + * on its own state (num_searches states of state_dim each) and writes the + * argmax (most probable state index) to results[search]. + * + * @param batch_states Buffer for all quantum states (num_searches * state_dim) + * @param targets Array of target states (one per search) + * @param results Output array of found states (one per search) + * @param num_searches Number of parallel searches + * @param num_qubits Qubits per search + * @param num_iterations Grover iterations per search + */ +int cuda_grover_batch_search( + cuda_compute_ctx_t* ctx, + cuda_buffer_t* batch_states, + const uint32_t* targets, + uint32_t* results, + uint32_t num_searches, + uint32_t num_qubits, + uint32_t num_iterations +); + +// ============================================================================ +// SYNCHRONIZATION & UTILITIES +// ============================================================================ + +/** + * @brief Wait for all queued GPU operations to complete. + */ +void cuda_wait_completion(cuda_compute_ctx_t* ctx); + +/** + * @brief Get GPU execution time (seconds) of the last measured operation. + */ +double cuda_get_last_execution_time(cuda_compute_ctx_t* ctx); + +/** + * @brief Enable/disable performance monitoring (event-based timing). + */ +void cuda_set_performance_monitoring(cuda_compute_ctx_t* ctx, int enable); + +// ============================================================================ +// DIAGNOSTICS +// ============================================================================ + +/** + * @brief Print CUDA device capabilities. + */ +void cuda_print_device_info(cuda_compute_ctx_t* ctx); + +/** + * @brief Get error message for the last error. + */ +const char* cuda_get_error(cuda_compute_ctx_t* ctx); + +#ifdef __cplusplus +} +#endif + +#endif /* CUDA_BRIDGE_H */ diff --git a/vendor/quantum_rng/src/quantum_rng/grover.c b/vendor/quantum_rng/src/quantum_rng/grover.c new file mode 100644 index 0000000..b434bbe --- /dev/null +++ b/vendor/quantum_rng/src/quantum_rng/grover.c @@ -0,0 +1,610 @@ +#include "grover.h" +#include "quantum_gates.h" +#include "quantum_constants.h" +#include "quantum_entropy.h" +#include "simd_ops.h" +#include "../entropy/hardware_entropy.h" +#include +#include +#include +#include +#include + +// ============================================================================ +// GROVER OPERATORS +// ============================================================================ + +qs_error_t grover_oracle(quantum_state_t *state, uint64_t marked_state) { + if (!state || !state->amplitudes) return QS_ERROR_INVALID_STATE; + if (marked_state >= state->state_dim) return QS_ERROR_INVALID_QUBIT; + + // Oracle: flip phase of marked state + // O|x⟩ = (-1)^f(x)|x⟩ where f(marked) = 1, else 0 + state->amplitudes[marked_state] = -state->amplitudes[marked_state]; + + return QS_SUCCESS; +} + +qs_error_t grover_diffusion(quantum_state_t *state) { + if (!state || !state->amplitudes) return QS_ERROR_INVALID_STATE; + + // Diffusion operator: D = 2|s⟩⟨s| - I + // where |s⟩ = H⊗ⁿ|0⟩ⁿ (equal superposition) + // + // Implementation: + // 1. H⊗ⁿ (transform to |0⟩ⁿ basis) + // 2. Phase flip all states except |0⟩ⁿ + // 3. H⊗ⁿ (transform back) + + // Step 1: Apply Hadamard to all qubits + for (size_t i = 0; i < state->num_qubits; i++) { + qs_error_t err = gate_hadamard(state, i); + if (err != QS_SUCCESS) return err; + } + + // Step 2: Phase flip all states except |0...0⟩ + // This is equivalent to: (2|0⟩⟨0| - I) + // SIMD OPTIMIZED: Use vectorized negation (8-16x faster on ARM NEON) + if (state->state_dim > 1) { + simd_negate(&state->amplitudes[1], state->state_dim - 1); + } + + // Step 3: Apply Hadamard to all qubits again + for (size_t i = 0; i < state->num_qubits; i++) { + qs_error_t err = gate_hadamard(state, i); + if (err != QS_SUCCESS) return err; + } + + return QS_SUCCESS; +} + +qs_error_t grover_iteration(quantum_state_t *state, uint64_t marked_state) { + if (!state) return QS_ERROR_INVALID_STATE; + + // One Grover iteration: G = D · O + + // Apply oracle + qs_error_t err = grover_oracle(state, marked_state); + if (err != QS_SUCCESS) return err; + + // Apply diffusion + return grover_diffusion(state); +} + +// ============================================================================ +// ITERATION CALCULATION +// ============================================================================ + +size_t grover_optimal_iterations(size_t num_qubits) { + // Optimal iterations = ⌊π/4 × √N⌋ where N = 2^n + // This gives success probability ≈ 1 - 1/N + + if (num_qubits == 0) return 0; + + double N = pow(2.0, (double)num_qubits); + double optimal = (QC_PI_4) * sqrt(N); + + return (size_t)floor(optimal); +} + +// ============================================================================ +// MAIN ALGORITHM +// ============================================================================ + +grover_result_t grover_search(quantum_state_t *state, const grover_config_t *config, quantum_entropy_ctx_t *entropy) { + grover_result_t result = {0}; + + if (!state || !config || !entropy) return result; + if (config->num_qubits != state->num_qubits) return result; + if (config->marked_state >= state->state_dim) return result; + + // Calculate iterations + size_t iterations; + if (config->use_optimal_iterations) { + iterations = grover_optimal_iterations(config->num_qubits); + } else { + iterations = config->num_iterations; + } + + result.iterations_performed = iterations; + + // Step 1: Initialize to equal superposition |s⟩ = H⊗ⁿ|0⟩ⁿ + quantum_state_reset(state); + for (size_t i = 0; i < state->num_qubits; i++) { + qs_error_t err = gate_hadamard(state, i); + if (err != QS_SUCCESS) return result; + } + + // Step 2: Apply Grover iterations + for (size_t iter = 0; iter < iterations; iter++) { + qs_error_t err = grover_iteration(state, config->marked_state); + if (err != QS_SUCCESS) return result; + result.oracle_calls++; + } + + // Step 3: Calculate success probability before measurement + result.success_probability = quantum_state_get_probability(state, config->marked_state); + + // Step 4: Measure to find the state using secure entropy + // SIMD OPTIMIZED: Use vectorized probability search (4-8x faster!) + double random; + if (quantum_entropy_get_double(entropy, &random) != 0) { + return result; // Entropy failure + } + + // Use SIMD cumulative probability search + result.found_state = simd_cumulative_probability_search( + state->amplitudes, + state->state_dim, + random + ); + + // Check if we found the marked state + result.found_marked_state = (result.found_state == config->marked_state); + + // Calculate fidelity (how close we got to pure |marked⟩ state) + result.fidelity = result.success_probability; + + return result; +} + +// ============================================================================ +// RANDOM SAMPLING +// ============================================================================ + +uint64_t grover_random_sample(quantum_state_t *state, size_t num_qubits, quantum_entropy_ctx_t *entropy) { + if (!state || num_qubits != state->num_qubits || !entropy) return 0; + + // Use Grover with random marked state for quantum sampling + uint64_t search_space = 1ULL << num_qubits; + + // Get secure random target state + uint64_t random_target; + if (quantum_entropy_get_uint64(entropy, &random_target) != 0) { + return 0; // Entropy failure + } + random_target = random_target % search_space; + + grover_config_t config = { + .num_qubits = num_qubits, + .marked_state = random_target, + .num_iterations = 0, + .use_optimal_iterations = 1 + }; + + grover_result_t result = grover_search(state, &config, entropy); + return result.found_state; +} + +qs_error_t grover_random_samples( + quantum_state_t *state, + size_t num_qubits, + uint64_t *samples, + size_t num_samples, + quantum_entropy_ctx_t *entropy +) { + if (!state || !samples || num_qubits != state->num_qubits || !entropy) { + return QS_ERROR_INVALID_STATE; + } + + for (size_t i = 0; i < num_samples; i++) { + samples[i] = grover_random_sample(state, num_qubits, entropy); + } + + return QS_SUCCESS; +} + +// ============================================================================ +// ANALYSIS +// ============================================================================ + +grover_analysis_t grover_analyze_performance(size_t num_qubits, size_t num_trials, quantum_entropy_ctx_t *entropy) { + grover_analysis_t analysis = {0}; + + if (num_qubits == 0 || num_qubits > 12 || num_trials == 0 || !entropy) { + return analysis; + } + + quantum_state_t state; + qs_error_t err = quantum_state_init(&state, num_qubits); + if (err != QS_SUCCESS) return analysis; + + uint64_t search_space = 1ULL << num_qubits; + size_t successes = 0; + size_t total_iterations = 0; + size_t total_oracle_calls = 0; + + // Run trials + for (size_t trial = 0; trial < num_trials; trial++) { + // Pick random marked state using secure entropy + uint64_t marked; + if (quantum_entropy_get_uint64(entropy, &marked) != 0) { + break; // Entropy failure + } + marked = marked % search_space; + + grover_config_t config = { + .num_qubits = num_qubits, + .marked_state = marked, + .num_iterations = 0, + .use_optimal_iterations = 1 + }; + + grover_result_t result = grover_search(&state, &config, entropy); + + if (result.found_marked_state) { + successes++; + } + + total_iterations += result.iterations_performed; + total_oracle_calls += result.oracle_calls; + } + + // Calculate statistics + analysis.success_rate = (double)successes / num_trials; + analysis.avg_iterations = (double)total_iterations / num_trials; + analysis.total_oracle_calls = total_oracle_calls; + + // Theoretical quantum speedup + analysis.quantum_speedup = sqrt((double)search_space); + + // Measured speedup (oracle calls vs classical N/2 average) + double classical_avg = (double)search_space / 2.0; + analysis.measured_speedup = classical_avg / analysis.avg_iterations; + + quantum_state_free(&state); + return analysis; +} + +void grover_print_result(const grover_result_t *result, const grover_config_t *config) { + if (!result || !config) return; + + printf("\n"); + printf("╔═══════════════════════════════════════════════════════════╗\n"); + printf("║ GROVER'S ALGORITHM RESULTS ║\n"); + printf("╠═══════════════════════════════════════════════════════════╣\n"); + printf("║ ║\n"); + printf("║ Search Configuration: ║\n"); + printf("║ Qubits: %6zu ║\n", config->num_qubits); + printf("║ Search Space: %6llu (2^%zu) ║\n", + 1ULL << config->num_qubits, config->num_qubits); + printf("║ Target State: %6llu ║\n", config->marked_state); + printf("║ Iterations: %6zu ║\n", result->iterations_performed); + printf("║ ║\n"); + printf("║ Results: ║\n"); + printf("║ Found State: %6llu ║\n", result->found_state); + printf("║ Success: %s ║\n", + result->found_marked_state ? "✓ YES" : "✗ NO "); + printf("║ Success Probability: %6.4f ║\n", result->success_probability); + printf("║ Fidelity: %6.4f ║\n", result->fidelity); + printf("║ Oracle Calls: %6zu ║\n", result->oracle_calls); + printf("║ ║\n"); + + // Speedup analysis + uint64_t N = 1ULL << config->num_qubits; + double classical_avg = (double)N / 2.0; + double speedup = classical_avg / result->oracle_calls; + double theoretical_speedup = sqrt((double)N); + + printf("║ Quantum Advantage: ║\n"); + printf("║ Classical (avg): %6.0f queries ║\n", classical_avg); + printf("║ Quantum (actual): %6zu queries ║\n", result->oracle_calls); + printf("║ Measured Speedup: %6.2fx ║\n", speedup); + printf("║ Theoretical: %6.2fx (√N) ║\n", theoretical_speedup); + printf("║ Efficiency: %5.1f%% ║\n", + 100.0 * speedup / theoretical_speedup); + printf("║ ║\n"); + + if (result->found_marked_state) { + printf("║ ┌─────────────────────────────────────────────────────┐ ║\n"); + printf("║ │ ✓ QUANTUM SEARCH SUCCESSFUL │ ║\n"); + printf("║ │ Demonstrates genuine quantum speedup │ ║\n"); + printf("║ └─────────────────────────────────────────────────────┘ ║\n"); + } else { + printf("║ ┌─────────────────────────────────────────────────────┐ ║\n"); + printf("║ │ ⚠ TARGET NOT FOUND │ ║\n"); + printf("║ │ May need more iterations or different parameters │ ║\n"); + printf("║ └─────────────────────────────────────────────────────┘ ║\n"); + } + + printf("╚═══════════════════════════════════════════════════════════╝\n"); + printf("\n"); +} + +// ============================================================================ +// ADVANCED GROVER OPERATIONS (V3.0 ENHANCEMENTS) +// ============================================================================ + +grover_result_t grover_adaptive_search( + quantum_state_t *state, + uint64_t marked_state, + quantum_entropy_ctx_t *entropy +) { + grover_result_t result = {0}; + + if (!state || !entropy) return result; + if (marked_state >= state->state_dim) return result; + + /** + * ADAPTIVE GROVER SEARCH + * + * Automatically finds optimal number of iterations by monitoring + * success probability and adjusting dynamically. + * + * Algorithm: + * 1. Start with theoretical optimal iterations + * 2. Monitor success probability after each iteration + * 3. Stop when probability starts decreasing (over-rotation detection) + */ + + size_t num_qubits = state->num_qubits; + size_t theoretical_optimal = grover_optimal_iterations(num_qubits); + + // Initialize to superposition + quantum_state_reset(state); + for (size_t i = 0; i < num_qubits; i++) { + qs_error_t err = gate_hadamard(state, i); + if (err != QS_SUCCESS) return result; + } + + double prev_prob = quantum_state_get_probability(state, marked_state); + double max_prob = prev_prob; + size_t best_iteration = 0; + + // Apply iterations and track success probability + for (size_t iter = 0; iter < theoretical_optimal * 2; iter++) { + // Apply one Grover iteration + qs_error_t err = grover_iteration(state, marked_state); + if (err != QS_SUCCESS) break; + + result.oracle_calls++; + + // Check success probability + double curr_prob = quantum_state_get_probability(state, marked_state); + + if (curr_prob > max_prob) { + max_prob = curr_prob; + best_iteration = iter + 1; + } + + // Stop if probability is decreasing (over-rotation detected) + if (curr_prob < prev_prob * 0.95) { + break; + } + + prev_prob = curr_prob; + } + + result.iterations_performed = best_iteration; + result.success_probability = max_prob; + result.fidelity = max_prob; + + // Measure to get final result + // SIMD OPTIMIZED: Use vectorized probability search (4-8x faster!) + double rand; + if (quantum_entropy_get_double(entropy, &rand) != 0) { + return result; + } + + // Use SIMD cumulative probability search + result.found_state = simd_cumulative_probability_search( + state->amplitudes, + state->state_dim, + rand + ); + + result.found_marked_state = (result.found_state == marked_state); + + return result; +} + +qs_error_t grover_oracle_multi_phase( + quantum_state_t *state, + const uint64_t *marked_states, + const double *phases, + size_t num_marked +) { + if (!state || !marked_states || !phases) return QS_ERROR_INVALID_STATE; + if (num_marked == 0) return QS_ERROR_INVALID_STATE; + + /** + * MULTI-PHASE ORACLE + * + * Marks multiple states with individual phase controls. + * Enables fine-grained amplitude manipulation for custom distributions. + * + * O|x⟩ = e^(iφ_x)|x⟩ where φ_x is the phase for marked state x + */ + + for (size_t i = 0; i < num_marked; i++) { + if (marked_states[i] >= state->state_dim) { + return QS_ERROR_INVALID_STATE; + } + + // Apply phase to marked state + complex_t phase_factor = cexp(I * phases[i]); + state->amplitudes[marked_states[i]] *= phase_factor; + } + + return QS_SUCCESS; +} + +qs_error_t grover_amplitude_amplification( + quantum_state_t *state, + const double *target_amplitudes, + size_t num_iterations +) { + if (!state || !target_amplitudes) return QS_ERROR_INVALID_STATE; + + /** + * GENERALIZED AMPLITUDE AMPLIFICATION + * + * Amplifies amplitudes toward target distribution using + * repeated reflection operators. This is the foundation + * for sampling from arbitrary distributions. + * + * Based on: Brassard et al., "Quantum Amplitude Amplification + * and Estimation" (2000) + */ + + // Step 1: Encode target amplitudes into quantum state + double norm = 0.0; + for (uint64_t i = 0; i < state->state_dim; i++) { + double target = target_amplitudes[i]; + if (target < 0.0) target = 0.0; + + // Preserve phase, modify magnitude + complex_t current = state->amplitudes[i]; + double current_mag = cabs(current); + + if (current_mag > 1e-15) { + complex_t phase = current / current_mag; + state->amplitudes[i] = target * phase; + } else { + state->amplitudes[i] = target + 0.0 * I; + } + + norm += target * target; + } + + // Normalize + if (norm > 1e-15) { + norm = sqrt(norm); + for (uint64_t i = 0; i < state->state_dim; i++) { + state->amplitudes[i] /= norm; + } + } + + // Step 2: Apply amplitude amplification iterations + for (size_t iter = 0; iter < num_iterations; iter++) { + // Diffusion operator amplifies above-average amplitudes + qs_error_t err = grover_diffusion(state); + if (err != QS_SUCCESS) return err; + } + + return QS_SUCCESS; +} + +qs_error_t grover_importance_sampling( + quantum_state_t *state, + double (*importance_function)(uint64_t), + size_t num_samples, + uint64_t *samples, + quantum_entropy_ctx_t *entropy +) { + if (!state || !importance_function || !samples || !entropy) { + return QS_ERROR_INVALID_STATE; + } + if (num_samples == 0) return QS_ERROR_INVALID_STATE; + + /** + * QUANTUM IMPORTANCE SAMPLING + * + * Uses Grover amplitude amplification to sample from importance + * distribution, providing quadratic speedup over classical methods + * for certain function classes. + * + * Key insight: Amplitude amplification naturally implements + * importance sampling by concentrating probability mass. + */ + + uint64_t state_dim = state->state_dim; + + for (size_t sample_idx = 0; sample_idx < num_samples; sample_idx++) { + // Reset to uniform superposition + quantum_state_reset(state); + for (size_t q = 0; q < state->num_qubits; q++) { + gate_hadamard(state, q); + } + + // Encode importance weights as amplitudes + double norm = 0.0; + for (uint64_t i = 0; i < state_dim; i++) { + double weight = importance_function(i); + if (weight < 0.0) weight = 0.0; + + // amplitude ∝ sqrt(weight) for Born rule + double amplitude = sqrt(weight); + state->amplitudes[i] = amplitude + 0.0 * I; + norm += amplitude * amplitude; + } + + // Normalize + if (norm > 1e-15) { + norm = sqrt(norm); + for (uint64_t i = 0; i < state_dim; i++) { + state->amplitudes[i] /= norm; + } + } + + // Apply Grover amplification + size_t iterations = (size_t)(QC_PI_4 * sqrt((double)state_dim / 4.0)); + if (iterations > 20) iterations = 20; + + for (size_t iter = 0; iter < iterations; iter++) { + grover_diffusion(state); + } + + // Measure sample + // SIMD OPTIMIZED: Use vectorized probability search + double rand; + if (quantum_entropy_get_double(entropy, &rand) != 0) { + return QS_ERROR_INVALID_STATE; + } + + // Use SIMD cumulative probability search + samples[sample_idx] = simd_cumulative_probability_search( + state->amplitudes, + state_dim, + rand + ); + } + + return QS_SUCCESS; +} + +uint64_t grover_mcmc_step( + quantum_state_t *state, + double (*target_distribution)(uint64_t), + uint64_t current_state, + quantum_entropy_ctx_t *entropy +) { + if (!state || !target_distribution || !entropy) { + return current_state; + } + + /** + * QUANTUM METROPOLIS-HASTINGS + * + * Uses Grover search to generate proposal states with quantum enhancement. + * Acceptance/rejection follows standard Metropolis-Hastings criterion. + * + * Quantum advantage: Faster mixing for certain target distributions. + */ + + // Generate proposal using Grover-enhanced sampling + uint64_t proposal = grover_random_sample(state, state->num_qubits, entropy); + + // Metropolis-Hastings acceptance criterion + double current_prob = target_distribution(current_state); + double proposal_prob = target_distribution(proposal); + + if (proposal_prob >= current_prob) { + // Always accept if probability increases + return proposal; + } + + // Accept with probability proposal_prob / current_prob + double acceptance_ratio = proposal_prob / current_prob; + + double rand; + if (quantum_entropy_get_double(entropy, &rand) != 0) { + return current_state; + } + + if (rand < acceptance_ratio) { + return proposal; + } + + return current_state; +} \ No newline at end of file diff --git a/vendor/quantum_rng/src/quantum_rng/grover.h b/vendor/quantum_rng/src/quantum_rng/grover.h new file mode 100644 index 0000000..e132209 --- /dev/null +++ b/vendor/quantum_rng/src/quantum_rng/grover.h @@ -0,0 +1,278 @@ +#ifndef GROVER_H +#define GROVER_H + +#include "quantum_state.h" +#include "quantum_entropy.h" +#include + +/** + * @file grover.h + * @brief Grover's search algorithm for quantum database search + * + * Implements Grover's algorithm, which provides quadratic speedup + * for unstructured search problems. This demonstrates genuine + * quantum advantage: O(√N) vs classical O(N). + * + * For random number generation, Grover's algorithm can be used to: + * - Sample from quantum superpositions + * - Demonstrate quantum speedup + * - Generate quantum-enhanced random distributions + */ + +/** + * @brief Grover algorithm configuration + */ +typedef struct { + size_t num_qubits; // Number of qubits (search space = 2^n) + uint64_t marked_state; // State to search for + size_t num_iterations; // Number of Grover iterations (≈π√N/4) + int use_optimal_iterations; // Auto-calculate optimal iterations +} grover_config_t; + +/** + * @brief Grover algorithm result + */ +typedef struct { + uint64_t found_state; // Measured state + double success_probability; // P(measuring marked state) + size_t oracle_calls; // Number of oracle queries + size_t iterations_performed; // Actual iterations + double fidelity; // |⟨target|final⟩|² + int found_marked_state; // 1 if found, 0 if not +} grover_result_t; + +// ============================================================================ +// GROVER'S ALGORITHM +// ============================================================================ + +/** + * @brief Execute Grover's search algorithm + * + * Searches for marked state in O(√N) queries (classical requires O(N)). + * + * Algorithm: + * 1. Initialize: |s⟩ = H⊗ⁿ|0⟩ⁿ (equal superposition) + * 2. Repeat √N times: + * a. Oracle: O|x⟩ = (-1)^f(x)|x⟩ where f(marked) = 1 + * b. Diffusion: D = 2|s⟩⟨s| - I (amplitude amplification) + * 3. Measure + * + * SECURITY: Uses cryptographically secure entropy for final measurement. + * + * @param state Quantum state (will be modified) + * @param config Algorithm configuration + * @param entropy Secure entropy source for measurement + * @return Result including found state and statistics + */ +grover_result_t grover_search(quantum_state_t *state, const grover_config_t *config, quantum_entropy_ctx_t *entropy); + +/** + * @brief Calculate optimal number of Grover iterations + * + * For N items, optimal = ⌊π√N/4⌋ + * Gives maximum success probability ≈ 1 - 1/N + * + * @param num_qubits Number of qubits + * @return Optimal number of iterations + */ +size_t grover_optimal_iterations(size_t num_qubits); + +/** + * @brief Oracle operator for Grover's algorithm + * + * Marks target state with phase flip: |target⟩ → -|target⟩ + * Implements: O|x⟩ = (-1)^f(x)|x⟩ where f(marked) = 1, else 0 + * + * @param state Quantum state + * @param marked_state Index of state to mark + * @return QS_SUCCESS or error + */ +qs_error_t grover_oracle(quantum_state_t *state, uint64_t marked_state); + +/** + * @brief Diffusion operator (inversion about average) + * + * Implements: D = 2|s⟩⟨s| - I where |s⟩ = H⊗ⁿ|0⟩ⁿ + * This amplifies amplitude of marked state. + * + * @param state Quantum state + * @return QS_SUCCESS or error + */ +qs_error_t grover_diffusion(quantum_state_t *state); + +/** + * @brief Single Grover iteration (oracle + diffusion) + * + * One complete Grover iteration: G = D·O + * + * @param state Quantum state + * @param marked_state State to amplify + * @return QS_SUCCESS or error + */ +qs_error_t grover_iteration(quantum_state_t *state, uint64_t marked_state); + +// ============================================================================ +// RANDOM SAMPLING USING GROVER +// ============================================================================ + +/** + * @brief Generate random number using Grover's algorithm + * + * Uses quantum superposition and Grover iterations to sample + * from quantum distribution. Demonstrates quantum enhancement. + * + * @param state Quantum state + * @param num_qubits Number of qubits (determines range: 0 to 2^n-1) + * @param entropy Secure entropy source + * @return Sampled random number + */ +uint64_t grover_random_sample(quantum_state_t *state, size_t num_qubits, quantum_entropy_ctx_t *entropy); + +/** + * @brief Generate multiple random samples using Grover + * + * @param state Quantum state + * @param num_qubits Number of qubits + * @param samples Output array + * @param num_samples Number of samples to generate + * @param entropy Secure entropy source + * @return QS_SUCCESS or error + */ +qs_error_t grover_random_samples( + quantum_state_t *state, + size_t num_qubits, + uint64_t *samples, + size_t num_samples, + quantum_entropy_ctx_t *entropy +); + +// ============================================================================ +// ANALYSIS & VERIFICATION +// ============================================================================ + +/** + * @brief Analyze Grover's algorithm performance + * + * Runs multiple Grover searches and analyzes: + * - Success rate + * - Average iterations to success + * - Quantum speedup vs classical + * + * @param num_qubits Number of qubits + * @param num_trials Number of trials to run + * @param entropy Secure entropy source for measurements + */ +typedef struct { + double success_rate; // Fraction of successful searches + double avg_iterations; // Average iterations used + double quantum_speedup; // √N (theoretical) + double measured_speedup; // Observed speedup + size_t total_oracle_calls; // Total oracle queries +} grover_analysis_t; + +grover_analysis_t grover_analyze_performance(size_t num_qubits, size_t num_trials, quantum_entropy_ctx_t *entropy); + +/** + * @brief Print Grover result + * @param result Grover result + */ +void grover_print_result(const grover_result_t *result, const grover_config_t *config); + +// ============================================================================ +// ADVANCED GROVER OPERATIONS (V3.0 ENHANCEMENTS) +// ============================================================================ + +/** + * @brief Adaptive Grover search with automatic iteration optimization + * + * Automatically determines optimal number of iterations and adjusts + * based on success probability feedback. + * + * @param state Quantum state + * @param marked_state Target to find + * @param entropy Secure entropy source + * @return Grover result with adaptive optimization stats + */ +grover_result_t grover_adaptive_search( + quantum_state_t *state, + uint64_t marked_state, + quantum_entropy_ctx_t *entropy +); + +/** + * @brief Multi-oracle Grover with individual phase controls + * + * Advanced oracle that marks multiple states with configurable phases. + * Allows fine-grained control over amplitude distribution. + * + * @param state Quantum state + * @param marked_states Array of states to mark + * @param phases Phase angle for each marked state (in radians) + * @param num_marked Number of marked states + * @return QS_SUCCESS or error + */ +qs_error_t grover_oracle_multi_phase( + quantum_state_t *state, + const uint64_t *marked_states, + const double *phases, + size_t num_marked +); + +/** + * @brief Amplitude amplification for arbitrary target state + * + * Generalized amplitude amplification that can amplify any + * set of basis states with custom amplitude targets. + * + * @param state Quantum state + * @param target_amplitudes Desired amplitude for each basis state + * @param num_iterations Number of amplification iterations + * @return QS_SUCCESS or error + */ +qs_error_t grover_amplitude_amplification( + quantum_state_t *state, + const double *target_amplitudes, + size_t num_iterations +); + +/** + * @brief Quantum sampling with importance sampling + * + * Uses Grover's algorithm to implement quantum importance sampling, + * which provides quadratic speedup for certain distribution classes. + * + * @param state Quantum state + * @param importance_function Weight function for importance sampling + * @param num_samples Number of samples to generate + * @param samples Output array for samples + * @param entropy Secure entropy source + * @return QS_SUCCESS or error + */ +qs_error_t grover_importance_sampling( + quantum_state_t *state, + double (*importance_function)(uint64_t), + size_t num_samples, + uint64_t *samples, + quantum_entropy_ctx_t *entropy +); + +/** + * @brief Quantum Metropolis-Hastings using Grover + * + * Implements quantum-enhanced MCMC sampling using Grover's algorithm + * for proposal distribution. Provides speedup for peaked target distributions. + * + * @param state Quantum state + * @param target_distribution Target distribution to sample from + * @param current_state Current MCMC state + * @param entropy Secure entropy source + * @return Next MCMC state + */ +uint64_t grover_mcmc_step( + quantum_state_t *state, + double (*target_distribution)(uint64_t), + uint64_t current_state, + quantum_entropy_ctx_t *entropy +); + +#endif /* GROVER_H */ \ No newline at end of file diff --git a/vendor/quantum_rng/src/quantum_rng/grover_parallel.c b/vendor/quantum_rng/src/quantum_rng/grover_parallel.c new file mode 100644 index 0000000..881acab --- /dev/null +++ b/vendor/quantum_rng/src/quantum_rng/grover_parallel.c @@ -0,0 +1,491 @@ +#include "grover_parallel.h" +#include "grover.h" +#include "quantum_constants.h" +#include "../entropy/hardware_entropy.h" +#include +#include +#include +#include +#include + +#ifdef _OPENMP +#include +#endif + +#ifdef __APPLE__ +#include +#include +#endif + +// ============================================================================ +// HARDWARE DETECTION +// ============================================================================ + +size_t grover_parallel_get_optimal_batch_size(void) { +#ifdef _OPENMP + int num_threads = omp_get_max_threads(); + return (size_t)num_threads; +#else + // Fallback: try to detect cores +#ifdef __APPLE__ + int num_cores = 0; + size_t size = sizeof(num_cores); + if (sysctlbyname("hw.ncpu", &num_cores, &size, NULL, 0) == 0) { + return (size_t)num_cores; + } +#endif + return 1; // Sequential fallback +#endif +} + +// ============================================================================ +// PARALLEL BATCH PROCESSING +// ============================================================================ + +grover_parallel_result_t grover_parallel_batch( + const grover_parallel_config_t *config, + const uint64_t *marked_states, + quantum_entropy_ctx_t *entropy_pools +) { + grover_parallel_result_t result = {0}; + + if (!config || !marked_states || !entropy_pools) { + return result; + } + + size_t num_searches = config->num_parallel_searches; + if (num_searches == 0) { + return result; + } + + // Allocate results array + result.results = calloc(num_searches, sizeof(grover_result_t)); + if (!result.results) { + return result; + } + result.num_results = num_searches; + + // Allocate quantum states (one per thread) + quantum_state_t *states = calloc(num_searches, sizeof(quantum_state_t)); + if (!states) { + free(result.results); + memset(&result, 0, sizeof(result)); + return result; + } + + // Initialize quantum states + for (size_t i = 0; i < num_searches; i++) { + if (quantum_state_init(&states[i], config->num_qubits) != QS_SUCCESS) { + // Cleanup on failure + for (size_t j = 0; j < i; j++) { + quantum_state_free(&states[j]); + } + free(states); + free(result.results); + memset(&result, 0, sizeof(result)); + return result; + } + } + + // Start timing + struct timespec start_time, end_time; + clock_gettime(CLOCK_MONOTONIC, &start_time); + + // PARALLEL EXECUTION - M2 ULTRA 24-CORE OPTIMIZATION +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic) num_threads(num_searches) +#endif + for (size_t i = 0; i < num_searches; i++) { + // Each thread runs independent Grover search + grover_config_t search_config = { + .num_qubits = config->num_qubits, + .marked_state = marked_states[i], + .num_iterations = 0, + .use_optimal_iterations = config->use_optimal_iterations + }; + + // Thread-safe: each thread has its own state and entropy context + result.results[i] = grover_search( + &states[i], + &search_config, + &entropy_pools[i] + ); + } + + // End timing + clock_gettime(CLOCK_MONOTONIC, &end_time); + + // Calculate execution time + result.total_time_seconds = + (end_time.tv_sec - start_time.tv_sec) + + (end_time.tv_nsec - start_time.tv_nsec) / 1e9; + + result.searches_per_second = num_searches / result.total_time_seconds; + + // Find best result (highest success probability) + result.best_result = result.results[0]; + for (size_t i = 1; i < num_searches; i++) { + if (result.results[i].success_probability > result.best_result.success_probability) { + result.best_result = result.results[i]; + } + } + + // Cleanup quantum states + for (size_t i = 0; i < num_searches; i++) { + quantum_state_free(&states[i]); + } + free(states); + + return result; +} + +grover_parallel_result_t grover_parallel_random_batch( + size_t num_searches, + size_t num_qubits, + quantum_entropy_ctx_t *entropy_pool +) { + grover_parallel_result_t result = {0}; + + if (num_searches == 0 || num_qubits == 0 || !entropy_pool) { + return result; + } + + // Generate random target states + uint64_t *marked_states = calloc(num_searches, sizeof(uint64_t)); + if (!marked_states) { + return result; + } + + uint64_t search_space = 1ULL << num_qubits; + for (size_t i = 0; i < num_searches; i++) { + uint64_t random_target; + if (quantum_entropy_get_uint64(entropy_pool, &random_target) != 0) { + free(marked_states); + return result; + } + marked_states[i] = random_target % search_space; + } + + // Create entropy contexts for each thread + quantum_entropy_ctx_t *entropy_pools = calloc(num_searches, sizeof(quantum_entropy_ctx_t)); + if (!entropy_pools) { + free(marked_states); + return result; + } + + // Each thread gets its own entropy context (thread-safe) + for (size_t i = 0; i < num_searches; i++) { + // Copy entropy context structure (each thread will have independent state) + memcpy(&entropy_pools[i], entropy_pool, sizeof(quantum_entropy_ctx_t)); + } + + // Configure parallel execution + grover_parallel_config_t config = { + .num_parallel_searches = num_searches, + .num_qubits = num_qubits, + .use_optimal_iterations = 1, + .pin_to_performance_cores = 0 + }; + + // Execute parallel batch + result = grover_parallel_batch(&config, marked_states, entropy_pools); + + // Cleanup + free(marked_states); + free(entropy_pools); + + return result; +} + +int grover_parallel_random_samples( + size_t num_qubits, + uint64_t *samples, + size_t num_samples, + quantum_entropy_ctx_t *entropy_pool +) { + if (!samples || num_samples == 0 || !entropy_pool) { + return -1; + } + + // Determine optimal batch size + size_t batch_size = grover_parallel_get_optimal_batch_size(); + if (batch_size > num_samples) { + batch_size = num_samples; + } + + size_t samples_generated = 0; + + while (samples_generated < num_samples) { + size_t current_batch = batch_size; + if (samples_generated + current_batch > num_samples) { + current_batch = num_samples - samples_generated; + } + + // Run parallel batch + grover_parallel_result_t batch_result = grover_parallel_random_batch( + current_batch, + num_qubits, + entropy_pool + ); + + if (!batch_result.results) { + return -1; + } + + // Extract found states + for (size_t i = 0; i < current_batch; i++) { + samples[samples_generated + i] = batch_result.results[i].found_state; + } + + samples_generated += current_batch; + + grover_parallel_free_result(&batch_result); + } + + return 0; +} + +// ============================================================================ +// PARTITIONED SEARCH (Experimental) +// ============================================================================ + +grover_result_t grover_parallel_partitioned_search( + size_t num_qubits, + uint64_t marked_state, + size_t num_partitions, + quantum_entropy_ctx_t *entropy_pool +) { + grover_result_t result = {0}; + + if (num_partitions == 0 || !entropy_pool) { + return result; + } + + // Note: Partitioned quantum search is less effective than batch processing + // because quantum parallelism already searches superposition of all states. + // This is included mainly for comparison with classical approaches. + + // For now, just run a single standard Grover search + // (True partitioned quantum search requires advanced quantum circuit design) + + quantum_state_t state; + if (quantum_state_init(&state, num_qubits) != QS_SUCCESS) { + return result; + } + + grover_config_t config = { + .num_qubits = num_qubits, + .marked_state = marked_state, + .num_iterations = 0, + .use_optimal_iterations = 1 + }; + + result = grover_search(&state, &config, entropy_pool); + + quantum_state_free(&state); + + return result; +} + +// ============================================================================ +// PERFORMANCE BENCHMARKING +// ============================================================================ + +grover_parallel_benchmark_t grover_parallel_benchmark( + size_t num_searches, + size_t num_qubits, + quantum_entropy_ctx_t *entropy_pool +) { + grover_parallel_benchmark_t benchmark = {0}; + + if (num_searches == 0 || num_qubits == 0 || !entropy_pool) { + return benchmark; + } + + printf("\n"); + printf("╔═══════════════════════════════════════════════════════════╗\n"); + printf("║ M2 ULTRA PARALLEL GROVER BENCHMARK ║\n"); + printf("╠═══════════════════════════════════════════════════════════╣\n"); + printf("║ ║\n"); + printf("║ Configuration: ║\n"); + printf("║ Searches: %6zu ║\n", num_searches); + printf("║ Qubits per search: %6zu ║\n", num_qubits); + printf("║ Search space: %6llu (2^%zu) ║\n", + 1ULL << num_qubits, num_qubits); + +#ifdef _OPENMP + printf("║ OpenMP threads: %6d ║\n", omp_get_max_threads()); +#else + printf("║ OpenMP: NOT ENABLED ║\n"); +#endif + + printf("║ ║\n"); + printf("╚═══════════════════════════════════════════════════════════╝\n"); + printf("\n"); + + // ======================================== + // SEQUENTIAL BENCHMARK + // ======================================== + + printf("Running SEQUENTIAL benchmark (%zu searches)...\n", num_searches); + fflush(stdout); + + struct timespec seq_start, seq_end; + clock_gettime(CLOCK_MONOTONIC, &seq_start); + + quantum_state_t state; + quantum_state_init(&state, num_qubits); + + uint64_t search_space = 1ULL << num_qubits; + + for (size_t i = 0; i < num_searches; i++) { + uint64_t target; + quantum_entropy_get_uint64(entropy_pool, &target); + target = target % search_space; + + grover_config_t config = { + .num_qubits = num_qubits, + .marked_state = target, + .num_iterations = 0, + .use_optimal_iterations = 1 + }; + + grover_search(&state, &config, entropy_pool); + } + + clock_gettime(CLOCK_MONOTONIC, &seq_end); + quantum_state_free(&state); + + benchmark.sequential_time = + (seq_end.tv_sec - seq_start.tv_sec) + + (seq_end.tv_nsec - seq_start.tv_nsec) / 1e9; + + printf("Sequential time: %.6f seconds\n", benchmark.sequential_time); + printf("Throughput: %.2f searches/second\n\n", num_searches / benchmark.sequential_time); + + // ======================================== + // PARALLEL BENCHMARK + // ======================================== + + printf("Running PARALLEL benchmark (%zu searches on %zu cores)...\n", + num_searches, grover_parallel_get_optimal_batch_size()); + fflush(stdout); + + struct timespec par_start, par_end; + clock_gettime(CLOCK_MONOTONIC, &par_start); + + grover_parallel_result_t par_result = grover_parallel_random_batch( + num_searches, + num_qubits, + entropy_pool + ); + + clock_gettime(CLOCK_MONOTONIC, &par_end); + + benchmark.parallel_time = + (par_end.tv_sec - par_start.tv_sec) + + (par_end.tv_nsec - par_start.tv_nsec) / 1e9; + + printf("Parallel time: %.6f seconds\n", benchmark.parallel_time); + printf("Throughput: %.2f searches/second\n\n", num_searches / benchmark.parallel_time); + + grover_parallel_free_result(&par_result); + + // ======================================== + // ANALYSIS + // ======================================== + + benchmark.speedup = benchmark.sequential_time / benchmark.parallel_time; + benchmark.num_cores_used = grover_parallel_get_optimal_batch_size(); + benchmark.efficiency = benchmark.speedup / benchmark.num_cores_used; + + return benchmark; +} + +void grover_parallel_print_benchmark(const grover_parallel_benchmark_t *benchmark) { + if (!benchmark) return; + + printf("\n"); + printf("╔═══════════════════════════════════════════════════════════╗\n"); + printf("║ PARALLEL PERFORMANCE RESULTS ║\n"); + printf("╠═══════════════════════════════════════════════════════════╣\n"); + printf("║ ║\n"); + printf("║ Execution Time: ║\n"); + printf("║ Sequential: %10.6f seconds ║\n", benchmark->sequential_time); + printf("║ Parallel: %10.6f seconds ║\n", benchmark->parallel_time); + printf("║ ║\n"); + printf("║ Performance: ║\n"); + printf("║ Speedup: %10.2fx ║\n", benchmark->speedup); + printf("║ Cores used: %10zu ║\n", benchmark->num_cores_used); + printf("║ Efficiency: %9.1f%% ║\n", + benchmark->efficiency * 100.0); + printf("║ ║\n"); + + // Evaluation + if (benchmark->speedup >= benchmark->num_cores_used * 0.8) { + printf("║ ┌─────────────────────────────────────────────────────┐ ║\n"); + printf("║ │ ✓ EXCELLENT SCALING │ ║\n"); + printf("║ │ Near-linear speedup achieved! │ ║\n"); + printf("║ └─────────────────────────────────────────────────────┘ ║\n"); + } else if (benchmark->speedup >= benchmark->num_cores_used * 0.5) { + printf("║ ┌─────────────────────────────────────────────────────┐ ║\n"); + printf("║ │ ✓ GOOD SCALING │ ║\n"); + printf("║ │ Effective multi-core utilization │ ║\n"); + printf("║ └─────────────────────────────────────────────────────┘ ║\n"); + } else { + printf("║ ┌─────────────────────────────────────────────────────┐ ║\n"); + printf("║ │ ⚠ SUBOPTIMAL SCALING │ ║\n"); + printf("║ │ Consider larger batch sizes or tune OpenMP │ ║\n"); + printf("║ └─────────────────────────────────────────────────────┘ ║\n"); + } + + printf("╚═══════════════════════════════════════════════════════════╝\n"); + printf("\n"); +} + +// ============================================================================ +// UTILITIES +// ============================================================================ + +void grover_parallel_free_result(grover_parallel_result_t *result) { + if (!result) return; + + if (result->results) { + free(result->results); + result->results = NULL; + } + + result->num_results = 0; +} + +void grover_parallel_print_config(const grover_parallel_config_t *config) { + if (!config) return; + + printf("\n"); + printf("╔═══════════════════════════════════════════════════════════╗\n"); + printf("║ PARALLEL GROVER CONFIGURATION ║\n"); + printf("╠═══════════════════════════════════════════════════════════╣\n"); + printf("║ ║\n"); + printf("║ Parallel searches: %6zu ║\n", + config->num_parallel_searches); + printf("║ Qubits per search: %6zu ║\n", + config->num_qubits); + printf("║ Search space each: %6llu (2^%zu) ║\n", + 1ULL << config->num_qubits, config->num_qubits); + printf("║ Optimal iterations: %s ║\n", + config->use_optimal_iterations ? "YES" : "NO "); + printf("║ Pin to P-cores: %s ║\n", + config->pin_to_performance_cores ? "YES" : "NO "); + printf("║ ║\n"); + +#ifdef _OPENMP + printf("║ OpenMP Status: ENABLED ║\n"); + printf("║ Max threads: %6d ║\n", omp_get_max_threads()); +#else + printf("║ OpenMP Status: DISABLED ║\n"); + printf("║ Note: Compile with -fopenmp for parallel execution ║\n"); +#endif + + printf("╚═══════════════════════════════════════════════════════════╝\n"); + printf("\n"); +} \ No newline at end of file diff --git a/vendor/quantum_rng/src/quantum_rng/grover_parallel.h b/vendor/quantum_rng/src/quantum_rng/grover_parallel.h new file mode 100644 index 0000000..c85ed29 --- /dev/null +++ b/vendor/quantum_rng/src/quantum_rng/grover_parallel.h @@ -0,0 +1,185 @@ +#ifndef GROVER_PARALLEL_H +#define GROVER_PARALLEL_H + +#include "grover.h" +#include "quantum_state.h" +#include "quantum_entropy.h" +#include + +/** + * @file grover_parallel.h + * @brief Parallel Grover's algorithm implementation for M2 Ultra (24 cores) + * + * PERFORMANCE OPTIMIZATION - Phase 1: Multi-Core Parallelization + * + * This module provides parallel implementations of Grover's search algorithm + * optimized for the M2 Ultra's 24-core architecture: + * - 16 performance cores + * - 8 efficiency cores + * + * Target: 20-30x speedup through parallel batch processing + */ + +/** + * @brief Configuration for parallel Grover execution + */ +typedef struct { + size_t num_parallel_searches; // Number of parallel searches (default: 24) + size_t num_qubits; // Qubits per search + int use_optimal_iterations; // Auto-calculate iterations + int pin_to_performance_cores; // Pin threads to P-cores (macOS) +} grover_parallel_config_t; + +/** + * @brief Result from parallel batch of Grover searches + */ +typedef struct { + grover_result_t *results; // Array of individual results + size_t num_results; // Number of results + grover_result_t best_result; // Best result (highest probability) + double total_time_seconds; // Total execution time + double searches_per_second; // Throughput metric +} grover_parallel_result_t; + +// ============================================================================ +// PARALLEL BATCH PROCESSING +// ============================================================================ + +/** + * @brief Execute multiple Grover searches in parallel (OpenMP) + * + * Runs N independent Grover searches simultaneously across all available cores. + * Each search has its own quantum state and targets a different marked state. + * + * M2 Ultra advantage: 24 searches complete in the time of 1! + * + * @param config Parallel execution configuration + * @param marked_states Array of target states (one per search) + * @param entropy_pools Array of entropy contexts (one per thread) + * @return Parallel batch results + */ +grover_parallel_result_t grover_parallel_batch( + const grover_parallel_config_t *config, + const uint64_t *marked_states, + quantum_entropy_ctx_t *entropy_pools +); + +/** + * @brief Execute parallel Grover searches with random targets + * + * Convenient wrapper that generates random target states automatically. + * Perfect for benchmarking and RNG applications. + * + * @param num_searches Number of parallel searches + * @param num_qubits Number of qubits per search + * @param entropy_pool Entropy source for random targets + * @return Parallel batch results + */ +grover_parallel_result_t grover_parallel_random_batch( + size_t num_searches, + size_t num_qubits, + quantum_entropy_ctx_t *entropy_pool +); + +/** + * @brief Parallel random sampling using Grover (optimized for throughput) + * + * Generates multiple random samples in parallel. Much faster than + * sequential sampling when you need many samples. + * + * Speedup: ~24x on M2 Ultra for batch sizes >= 24 + * + * @param num_qubits Number of qubits for sampling + * @param samples Output array for samples + * @param num_samples Number of samples to generate + * @param entropy_pool Entropy source + * @return 0 on success, -1 on error + */ +int grover_parallel_random_samples( + size_t num_qubits, + uint64_t *samples, + size_t num_samples, + quantum_entropy_ctx_t *entropy_pool +); + +// ============================================================================ +// PARTITIONED SEARCH (Single large search split across cores) +// ============================================================================ + +/** + * @brief Parallel search space partitioning (experimental) + * + * Splits a single large search space across multiple cores. + * Each core searches its partition in parallel. + * + * Note: Less effective for quantum search due to quantum parallelism, + * but included for classical comparison benchmarking. + * + * @param num_qubits Number of qubits (search space = 2^n) + * @param marked_state Target to find + * @param num_partitions Number of parallel partitions + * @param entropy_pool Entropy source + * @return Search result + */ +grover_result_t grover_parallel_partitioned_search( + size_t num_qubits, + uint64_t marked_state, + size_t num_partitions, + quantum_entropy_ctx_t *entropy_pool +); + +// ============================================================================ +// PERFORMANCE ANALYSIS +// ============================================================================ + +/** + * @brief Benchmark parallel vs sequential Grover search + * + * Runs identical searches sequentially and in parallel to measure + * actual speedup on your hardware. + * + * @param num_searches Number of searches to run + * @param num_qubits Number of qubits per search + * @param entropy_pool Entropy source + */ +typedef struct { + double sequential_time; // Time for sequential execution + double parallel_time; // Time for parallel execution + double speedup; // Actual speedup ratio + double efficiency; // Parallel efficiency (speedup / cores) + size_t num_cores_used; // Number of cores utilized +} grover_parallel_benchmark_t; + +grover_parallel_benchmark_t grover_parallel_benchmark( + size_t num_searches, + size_t num_qubits, + quantum_entropy_ctx_t *entropy_pool +); + +/** + * @brief Print parallel benchmark results + */ +void grover_parallel_print_benchmark(const grover_parallel_benchmark_t *benchmark); + +// ============================================================================ +// UTILITIES +// ============================================================================ + +/** + * @brief Free parallel result resources + */ +void grover_parallel_free_result(grover_parallel_result_t *result); + +/** + * @brief Get optimal number of parallel searches for current hardware + * + * Auto-detects number of cores and returns recommended parallel batch size. + */ +size_t grover_parallel_get_optimal_batch_size(void); + +/** + * @brief Print parallel execution configuration + */ +void grover_parallel_print_config(const grover_parallel_config_t *config); + +#endif /* GROVER_PARALLEL_H */ \ No newline at end of file diff --git a/vendor/quantum_rng/src/quantum_rng/matrix_math.c b/vendor/quantum_rng/src/quantum_rng/matrix_math.c new file mode 100644 index 0000000..6667871 --- /dev/null +++ b/vendor/quantum_rng/src/quantum_rng/matrix_math.c @@ -0,0 +1,275 @@ +#include "matrix_math.h" +#include +#include +#include +#include + +#define JACOBI_TOLERANCE 1e-12 +#define SMALL_NUMBER 1e-15 + +// ============================================================================ +// HELPER FUNCTIONS +// ============================================================================ + +static inline double complex_abs_squared(complex_t z) { + double r = creal(z); + double i = cimag(z); + return r * r + i * i; +} + +static inline void givens_rotation( + complex_t *matrix, + size_t n, + size_t p, + size_t q, + double *c, + double *s +) { + // Compute Givens rotation to zero out matrix[p][q] + complex_t app = matrix[p * n + p]; + complex_t aqq = matrix[q * n + q]; + complex_t apq = matrix[p * n + q]; + + double theta; + if (fabs(creal(apq)) < SMALL_NUMBER && fabs(cimag(apq)) < SMALL_NUMBER) { + *c = 1.0; + *s = 0.0; + return; + } + + double tau = (creal(aqq) - creal(app)) / (2.0 * creal(apq)); + double t; + + if (tau >= 0) { + t = 1.0 / (tau + sqrt(1.0 + tau * tau)); + } else { + t = -1.0 / (-tau + sqrt(1.0 + tau * tau)); + } + + *c = 1.0 / sqrt(1.0 + t * t); + *s = t * (*c); +} + +// ============================================================================ +// EIGENVALUE DECOMPOSITION +// ============================================================================ + +int hermitian_eigen_decomposition( + const complex_t *matrix, + size_t n, + double *eigenvalues, + complex_t *eigenvectors, + int max_iterations, + double tolerance +) { + if (!matrix || !eigenvalues || !eigenvectors) return -1; + if (n == 0) return -1; + + if (max_iterations <= 0) { + max_iterations = 50 * n * n; + } + if (tolerance <= 0) { + tolerance = JACOBI_TOLERANCE; + } + + // Verify matrix is Hermitian + if (!matrix_is_hermitian(matrix, n, tolerance)) { + return -1; + } + + // Copy matrix to working array (will be diagonalized) + complex_t *A = (complex_t *)malloc(n * n * sizeof(complex_t)); + if (!A) return -1; + memcpy(A, matrix, n * n * sizeof(complex_t)); + + // Initialize eigenvectors to identity + memset(eigenvectors, 0, n * n * sizeof(complex_t)); + for (size_t i = 0; i < n; i++) { + eigenvectors[i * n + i] = 1.0; + } + + // Jacobi rotation algorithm + int iteration = 0; + int converged = 0; + + while (iteration < max_iterations && !converged) { + // Find largest off-diagonal element + double max_off_diag = 0.0; + size_t max_p = 0, max_q = 1; + + for (size_t p = 0; p < n - 1; p++) { + for (size_t q = p + 1; q < n; q++) { + double abs_sq = complex_abs_squared(A[p * n + q]); + if (abs_sq > max_off_diag) { + max_off_diag = abs_sq; + max_p = p; + max_q = q; + } + } + } + + // Check convergence + if (sqrt(max_off_diag) < tolerance) { + converged = 1; + break; + } + + // Apply Givens rotation + double c, s; + givens_rotation(A, n, max_p, max_q, &c, &s); + + // Update matrix A + for (size_t i = 0; i < n; i++) { + if (i != max_p && i != max_q) { + complex_t aip = A[i * n + max_p]; + complex_t aiq = A[i * n + max_q]; + + A[i * n + max_p] = c * aip - s * aiq; + A[i * n + max_q] = s * aip + c * aiq; + A[max_p * n + i] = conj(A[i * n + max_p]); + A[max_q * n + i] = conj(A[i * n + max_q]); + } + } + + // Update diagonal elements + complex_t app = A[max_p * n + max_p]; + complex_t aqq = A[max_q * n + max_q]; + complex_t apq = A[max_p * n + max_q]; + + A[max_p * n + max_p] = c * c * app - 2.0 * c * s * apq + s * s * aqq; + A[max_q * n + max_q] = s * s * app + 2.0 * c * s * apq + c * c * aqq; + A[max_p * n + max_q] = 0.0; + A[max_q * n + max_p] = 0.0; + + // Update eigenvectors + for (size_t i = 0; i < n; i++) { + complex_t vip = eigenvectors[i * n + max_p]; + complex_t viq = eigenvectors[i * n + max_q]; + + eigenvectors[i * n + max_p] = c * vip - s * viq; + eigenvectors[i * n + max_q] = s * vip + c * viq; + } + + iteration++; + } + + if (!converged) { + free(A); + return -1; // Failed to converge + } + + // Extract eigenvalues (diagonal elements) + for (size_t i = 0; i < n; i++) { + eigenvalues[i] = creal(A[i * n + i]); + } + + // Sort eigenvalues in descending order with eigenvectors + for (size_t i = 0; i < n - 1; i++) { + for (size_t j = i + 1; j < n; j++) { + if (eigenvalues[j] > eigenvalues[i]) { + // Swap eigenvalues + double temp_val = eigenvalues[i]; + eigenvalues[i] = eigenvalues[j]; + eigenvalues[j] = temp_val; + + // Swap eigenvector columns + for (size_t k = 0; k < n; k++) { + complex_t temp = eigenvectors[k * n + i]; + eigenvectors[k * n + i] = eigenvectors[k * n + j]; + eigenvectors[k * n + j] = temp; + } + } + } + } + + free(A); + return 0; +} + +// ============================================================================ +// MATRIX OPERATIONS +// ============================================================================ + +void matrix_multiply( + const complex_t *a, + const complex_t *b, + complex_t *c, + size_t m, + size_t k, + size_t n +) { + if (!a || !b || !c) return; + + // C[m×n] = A[m×k] × B[k×n] + for (size_t i = 0; i < m; i++) { + for (size_t j = 0; j < n; j++) { + complex_t sum = 0.0; + for (size_t l = 0; l < k; l++) { + sum += a[i * k + l] * b[l * n + j]; + } + c[i * n + j] = sum; + } + } +} + +complex_t matrix_trace(const complex_t *matrix, size_t n) { + if (!matrix) return 0.0; + + complex_t trace = 0.0; + for (size_t i = 0; i < n; i++) { + trace += matrix[i * n + i]; + } + return trace; +} + +int matrix_is_hermitian(const complex_t *matrix, size_t n, double tolerance) { + if (!matrix) return 0; + + for (size_t i = 0; i < n; i++) { + // Diagonal elements must be real + if (fabs(cimag(matrix[i * n + i])) > tolerance) { + return 0; + } + + for (size_t j = i + 1; j < n; j++) { + complex_t aij = matrix[i * n + j]; + complex_t aji = matrix[j * n + i]; + + // Check if A[i,j] = conj(A[j,i]) + double diff_real = fabs(creal(aij) - creal(aji)); + double diff_imag = fabs(cimag(aij) + cimag(aji)); + + if (diff_real > tolerance || diff_imag > tolerance) { + return 0; + } + } + } + + return 1; +} + +void matrix_conjugate_transpose( + const complex_t *matrix, + complex_t *result, + size_t m, + size_t n +) { + if (!matrix || !result) return; + + // Result[n×m] = Matrix[m×n]† + for (size_t i = 0; i < m; i++) { + for (size_t j = 0; j < n; j++) { + result[j * m + i] = conj(matrix[i * n + j]); + } + } +} + +double matrix_frobenius_norm(const complex_t *matrix, size_t m, size_t n) { + if (!matrix) return 0.0; + + double sum = 0.0; + for (size_t i = 0; i < m * n; i++) { + sum += complex_abs_squared(matrix[i]); + } + return sqrt(sum); +} \ No newline at end of file diff --git a/vendor/quantum_rng/src/quantum_rng/matrix_math.h b/vendor/quantum_rng/src/quantum_rng/matrix_math.h new file mode 100644 index 0000000..921fd33 --- /dev/null +++ b/vendor/quantum_rng/src/quantum_rng/matrix_math.h @@ -0,0 +1,100 @@ +#ifndef MATRIX_MATH_H +#define MATRIX_MATH_H + +#include +#include + +typedef double _Complex complex_t; + +/** + * @file matrix_math.h + * @brief Production-quality matrix mathematics for quantum operations + * + * Implements: + * - Eigenvalue decomposition for Hermitian matrices + * - Matrix operations (multiply, transpose, trace) + * - Numerical linear algebra utilities + */ + +/** + * @brief Compute eigenvalues and eigenvectors of Hermitian matrix + * + * Uses Jacobi algorithm for symmetric/Hermitian matrices. + * Guaranteed to converge for Hermitian matrices. + * + * @param matrix Input Hermitian matrix (n×n), row-major order + * @param n Dimension of matrix + * @param eigenvalues Output: array of n real eigenvalues (sorted descending) + * @param eigenvectors Output: matrix of eigenvectors (n×n, column-major) + * @param max_iterations Maximum iterations (default 50*n²) + * @param tolerance Convergence tolerance (default 1e-10) + * @return 0 on success, -1 on error + */ +int hermitian_eigen_decomposition( + const complex_t *matrix, + size_t n, + double *eigenvalues, + complex_t *eigenvectors, + int max_iterations, + double tolerance +); + +/** + * @brief Matrix multiplication: C = A × B + * @param a First matrix (m×k) + * @param b Second matrix (k×n) + * @param c Output matrix (m×n) + * @param m Rows in A + * @param k Cols in A, rows in B + * @param n Cols in B + */ +void matrix_multiply( + const complex_t *a, + const complex_t *b, + complex_t *c, + size_t m, + size_t k, + size_t n +); + +/** + * @brief Matrix trace: Tr(A) = Σ Aᵢᵢ + * @param matrix Square matrix (n×n) + * @param n Dimension + * @return Trace value + */ +complex_t matrix_trace(const complex_t *matrix, size_t n); + +/** + * @brief Check if matrix is Hermitian: A = A† + * @param matrix Matrix to check (n×n) + * @param n Dimension + * @param tolerance Tolerance for comparison + * @return 1 if Hermitian, 0 otherwise + */ +int matrix_is_hermitian(const complex_t *matrix, size_t n, double tolerance); + +/** + * @brief Conjugate transpose: A† (dagger) + * @param matrix Input matrix (m×n) + * @param result Output matrix (n×m) + * @param m Rows in input + * @param n Cols in input + */ +void matrix_conjugate_transpose( + const complex_t *matrix, + complex_t *result, + size_t m, + size_t n +); + +/** + * @brief Matrix norm (Frobenius): ||A|| = √(Σ|Aᵢⱼ|²) + * @param matrix Matrix (m×n) + * @param m Rows + * @param n Cols + * @return Frobenius norm + */ +double matrix_frobenius_norm(const complex_t *matrix, size_t m, size_t n); + +#endif /* MATRIX_MATH_H */ \ No newline at end of file diff --git a/vendor/quantum_rng/src/quantum_rng/metal/metal_bridge.h b/vendor/quantum_rng/src/quantum_rng/metal/metal_bridge.h new file mode 100644 index 0000000..d9128e3 --- /dev/null +++ b/vendor/quantum_rng/src/quantum_rng/metal/metal_bridge.h @@ -0,0 +1,417 @@ +#ifndef METAL_BRIDGE_H +#define METAL_BRIDGE_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file metal_bridge.h + * @brief C API for Metal GPU acceleration on M2 Ultra + * + * Provides C interface to Metal compute pipeline for quantum operations. + * Zero-copy unified memory architecture for maximum performance. + * + * PERFORMANCE TARGET: 100-200x speedup over CPU + * - 76 GPU cores on M2 Ultra (top configuration) + * - 192GB unified memory with 800 GB/s bandwidth + * - MTLResourceStorageModeShared for zero-copy + */ + +// Complex type (compatible with C) +typedef double _Complex complex_t; + +// Opaque handle to Metal compute context +typedef struct metal_compute_ctx metal_compute_ctx_t; + +// Metal buffer handle +typedef struct metal_buffer metal_buffer_t; + +// ============================================================================ +// INITIALIZATION & CLEANUP +// ============================================================================ + +/** + * @brief Initialize Metal compute context + * + * Creates Metal device, command queue, and compiles compute pipeline. + * + * @return Metal compute context or NULL on failure + */ +metal_compute_ctx_t* metal_compute_init(void); + +/** + * @brief Free Metal compute context + * + * @param ctx Metal compute context + */ +void metal_compute_free(metal_compute_ctx_t* ctx); + +/** + * @brief Check if Metal is available on this system + * + * @return 1 if Metal is available, 0 otherwise + */ +int metal_is_available(void); + +/** + * @brief Get GPU device information + * + * @param ctx Metal compute context + * @param name Output buffer for device name (min 256 bytes) + * @param max_threads Output: max threads per threadgroup + * @param num_cores Output: number of GPU cores + */ +void metal_get_device_info( + metal_compute_ctx_t* ctx, + char* name, + uint32_t* max_threads, + uint32_t* num_cores +); + +// ============================================================================ +// MEMORY MANAGEMENT (ZERO-COPY UNIFIED MEMORY) +// ============================================================================ + +/** + * @brief Allocate Metal buffer with zero-copy shared storage + * + * Uses MTLResourceStorageModeShared for unified memory access. + * CPU and GPU can access the same memory without copying. + * + * @param ctx Metal compute context + * @param size Buffer size in bytes + * @return Metal buffer handle or NULL on failure + */ +metal_buffer_t* metal_buffer_create(metal_compute_ctx_t* ctx, size_t size); + +/** + * @brief Create Metal buffer from existing CPU memory + * + * Wraps existing memory as shared Metal buffer (zero-copy). + * + * @param ctx Metal compute context + * @param data Existing CPU memory pointer + * @param size Buffer size in bytes + * @return Metal buffer handle or NULL on failure + */ +metal_buffer_t* metal_buffer_create_from_data( + metal_compute_ctx_t* ctx, + void* data, + size_t size +); + +/** + * @brief Get CPU-accessible pointer to Metal buffer + * + * @param buffer Metal buffer + * @return Pointer to buffer data + */ +void* metal_buffer_contents(metal_buffer_t* buffer); + +/** + * @brief Free Metal buffer + * + * @param buffer Metal buffer + */ +void metal_buffer_free(metal_buffer_t* buffer); + +// ============================================================================ +// QUANTUM GATE OPERATIONS (GPU-ACCELERATED) +// ============================================================================ + +/** + * @brief GPU-accelerated Hadamard gate + * + * PERFORMANCE: 20-40x faster than CPU + * + * @param ctx Metal compute context + * @param amplitudes Amplitude buffer (MTLResourceStorageModeShared) + * @param qubit_index Index of qubit to apply gate to + * @param state_dim Number of amplitudes (2^num_qubits) + * @return 0 on success, -1 on error + */ +int metal_hadamard( + metal_compute_ctx_t* ctx, + metal_buffer_t* amplitudes, + uint32_t qubit_index, + uint32_t state_dim +); + +/** + * @brief GPU-accelerated Hadamard on all qubits + * + * Applies Hadamard to all qubits in single GPU dispatch. + * + * @param ctx Metal compute context + * @param amplitudes Amplitude buffer + * @param num_qubits Number of qubits + * @param state_dim Number of amplitudes + * @return 0 on success, -1 on error + */ +int metal_hadamard_all( + metal_compute_ctx_t* ctx, + metal_buffer_t* amplitudes, + uint32_t num_qubits, + uint32_t state_dim +); + +/** + * @brief GPU-accelerated oracle (phase flip) + * + * PERFORMANCE: 50-100x faster than CPU + * + * @param ctx Metal compute context + * @param amplitudes Amplitude buffer + * @param target_state State to flip phase of + * @param state_dim Number of amplitudes + * @return 0 on success, -1 on error + */ +int metal_oracle( + metal_compute_ctx_t* ctx, + metal_buffer_t* amplitudes, + uint32_t target_state, + uint32_t state_dim +); + +/** + * @brief GPU-accelerated oracle with multiple marked states + * + * @param ctx Metal compute context + * @param amplitudes Amplitude buffer + * @param marked_states Array of marked states + * @param num_marked Number of marked states + * @param state_dim Number of amplitudes + * @return 0 on success, -1 on error + */ +int metal_oracle_multi( + metal_compute_ctx_t* ctx, + metal_buffer_t* amplitudes, + const uint32_t* marked_states, + uint32_t num_marked, + uint32_t state_dim +); + +/** + * @brief GPU-accelerated Grover diffusion operator + * + * PERFORMANCE: 15-30x faster than CPU + * Fused implementation: Hadamard → Inversion → Hadamard + * + * @param ctx Metal compute context + * @param amplitudes Amplitude buffer + * @param num_qubits Number of qubits + * @param state_dim Number of amplitudes + * @return 0 on success, -1 on error + */ +int metal_grover_diffusion( + metal_compute_ctx_t* ctx, + metal_buffer_t* amplitudes, + uint32_t num_qubits, + uint32_t state_dim +); + +/** + * @brief GPU-accelerated Pauli X gate + * + * @param ctx Metal compute context + * @param amplitudes Amplitude buffer + * @param qubit_index Index of qubit + * @param state_dim Number of amplitudes + * @return 0 on success, -1 on error + */ +int metal_pauli_x( + metal_compute_ctx_t* ctx, + metal_buffer_t* amplitudes, + uint32_t qubit_index, + uint32_t state_dim +); + +/** + * @brief GPU-accelerated Pauli Z gate + * + * @param ctx Metal compute context + * @param amplitudes Amplitude buffer + * @param qubit_index Index of qubit + * @param state_dim Number of amplitudes + * @return 0 on success, -1 on error + */ +int metal_pauli_z( + metal_compute_ctx_t* ctx, + metal_buffer_t* amplitudes, + uint32_t qubit_index, + uint32_t state_dim +); + +// ============================================================================ +// PROBABILITY & MEASUREMENT +// ============================================================================ + +/** + * @brief Compute probabilities from amplitudes (GPU) + * + * PERFORMANCE: 30-50x faster than CPU + * + * @param ctx Metal compute context + * @param amplitudes Amplitude buffer + * @param probabilities Output probability buffer + * @param state_dim Number of amplitudes + * @return 0 on success, -1 on error + */ +int metal_compute_probabilities( + metal_compute_ctx_t* ctx, + metal_buffer_t* amplitudes, + metal_buffer_t* probabilities, + uint32_t state_dim +); + +/** + * @brief Normalize quantum state (GPU) + * + * @param ctx Metal compute context + * @param amplitudes Amplitude buffer + * @param norm Normalization factor + * @param state_dim Number of amplitudes + * @return 0 on success, -1 on error + */ +int metal_normalize( + metal_compute_ctx_t* ctx, + metal_buffer_t* amplitudes, + float norm, + uint32_t state_dim +); +/** + * @brief BREAKTHROUGH: Execute MULTIPLE complete Grover searches in parallel! + * + * THIS IS THE RIGHT WAY TO USE GPU! + * - Processes N searches simultaneously (one per threadgroup) + * - 76 searches optimal for M2 Ultra (76 GPU cores) + * - Single kernel launch for ALL searches + * - Amortizes overhead across batch + * + * EXPECTED PERFORMANCE: + * - 76 searches in ~150ms (vs 14,972ms on CPU) + * - Speedup: 100x+ for batch workloads! + * + * @param ctx Metal compute context + * @param batch_states Buffer for all quantum states (num_searches × state_dim) + * @param targets Array of target states (one per search) + * @param results Output array of found states (one per search) + * @param num_searches Number of parallel searches (≤76 optimal) + * @param num_qubits Qubits per search + * @param num_iterations Grover iterations per search + * @return 0 on success, -1 on error + */ +int metal_grover_batch_search( + metal_compute_ctx_t* ctx, + metal_buffer_t* batch_states, + const uint32_t* targets, + uint32_t* results, + uint32_t num_searches, + uint32_t num_qubits, + uint32_t num_iterations +); + + +// ============================================================================ +// BATCH OPERATIONS +// ============================================================================ + +/** + * @brief Execute complete Grover iteration on GPU + * + * Fuses: Oracle → Diffusion into single GPU dispatch + * Minimizes CPU↔GPU synchronization + * + * @param ctx Metal compute context + * @param amplitudes Amplitude buffer + * @param target_state Marked state + * @param num_qubits Number of qubits + * @param state_dim Number of amplitudes + * @return 0 on success, -1 on error + */ +int metal_grover_iteration( + metal_compute_ctx_t* ctx, + metal_buffer_t* amplitudes, + uint32_t target_state, + uint32_t num_qubits, + uint32_t state_dim +); + +/** + * @brief Execute multiple Grover iterations on GPU + * + * Keeps all computation on GPU with minimal CPU interaction. + * + * @param ctx Metal compute context + * @param amplitudes Amplitude buffer + * @param target_state Marked state + * @param num_qubits Number of qubits + * @param state_dim Number of amplitudes + * @param num_iterations Number of Grover iterations + * @return 0 on success, -1 on error + */ +int metal_grover_search( + metal_compute_ctx_t* ctx, + metal_buffer_t* amplitudes, + uint32_t target_state, + uint32_t num_qubits, + uint32_t state_dim, + uint32_t num_iterations +); + +// ============================================================================ +// SYNCHRONIZATION & UTILITIES +// ============================================================================ + +/** + * @brief Wait for GPU operations to complete + * + * @param ctx Metal compute context + */ +void metal_wait_completion(metal_compute_ctx_t* ctx); + +/** + * @brief Get GPU execution time for last operation + * + * @param ctx Metal compute context + * @return Execution time in seconds + */ +double metal_get_last_execution_time(metal_compute_ctx_t* ctx); + +/** + * @brief Enable/disable performance monitoring + * + * @param ctx Metal compute context + * @param enable 1 to enable, 0 to disable + */ +void metal_set_performance_monitoring(metal_compute_ctx_t* ctx, int enable); + +// ============================================================================ +// DIAGNOSTICS +// ============================================================================ + +/** + * @brief Print Metal device capabilities + * + * @param ctx Metal compute context + */ +void metal_print_device_info(metal_compute_ctx_t* ctx); + +/** + * @brief Get error message for last error + * + * @param ctx Metal compute context + * @return Error message string + */ +const char* metal_get_error(metal_compute_ctx_t* ctx); + +#ifdef __cplusplus +} +#endif + +#endif /* METAL_BRIDGE_H */ \ No newline at end of file diff --git a/vendor/quantum_rng/src/quantum_rng/metal/metal_bridge.mm b/vendor/quantum_rng/src/quantum_rng/metal/metal_bridge.mm new file mode 100644 index 0000000..9a14c45 --- /dev/null +++ b/vendor/quantum_rng/src/quantum_rng/metal/metal_bridge.mm @@ -0,0 +1,837 @@ +#import +#import +#include "metal_bridge.h" +#include +#include +#include + +/** + * @file metal_bridge.mm + * @brief Objective-C++ implementation of Metal GPU acceleration + * + * Implements zero-copy unified memory architecture for M2 Ultra. + * Optimized for 76 GPU cores with 192GB unified memory. + */ + +// ============================================================================ +// INTERNAL STRUCTURES +// ============================================================================ + +struct metal_compute_ctx { + id device; + id commandQueue; + id library; + + // Compiled compute pipelines (cached for performance) + id hadamardPipeline; + id hadamardAllPipeline; + id oraclePipeline; + id oracleMultiPipeline; + id diffusionPipeline; + id batchSearchPipeline; + id batchHadamardPipeline; + + id probabilitiesPipeline; + id normalizePipeline; + id pauliXPipeline; + id pauliZPipeline; + + // Performance monitoring + int performance_monitoring; + double last_execution_time; + + // Error tracking + NSString* lastError; +}; + +struct metal_buffer { + id buffer; + size_t size; +}; + +// ============================================================================ +// UTILITY FUNCTIONS +// ============================================================================ + +static double get_time_seconds() { + static mach_timebase_info_data_t timebase_info; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + mach_timebase_info(&timebase_info); + }); + + uint64_t time = mach_absolute_time(); + return (double)time * timebase_info.numer / timebase_info.denom / 1e9; +} + +static void set_error(metal_compute_ctx_t* ctx, NSString* error) { + if (ctx) { + ctx->lastError = error; + } +} + +// ============================================================================ +// INITIALIZATION +// ============================================================================ + +metal_compute_ctx_t* metal_compute_init(void) { + @autoreleasepool { + metal_compute_ctx_t* ctx = (metal_compute_ctx_t*)calloc(1, sizeof(metal_compute_ctx_t)); + if (!ctx) return NULL; + + // Get Metal device (should be M2 Ultra) + ctx->device = MTLCreateSystemDefaultDevice(); + if (!ctx->device) { + fprintf(stderr, "Metal: Failed to create device\n"); + free(ctx); + return NULL; + } + + // Print device info + NSString* deviceName = [ctx->device name]; + printf("Metal: Initialized device: %s\n", [deviceName UTF8String]); + printf("Metal: Unified memory: %llu MB\n", + [ctx->device recommendedMaxWorkingSetSize] / (1024 * 1024)); + + // Create command queue + ctx->commandQueue = [ctx->device newCommandQueue]; + if (!ctx->commandQueue) { + fprintf(stderr, "Metal: Failed to create command queue\n"); + free(ctx); + return NULL; + } + + // Load shader library + NSError* error = nil; + NSString* shaderPath = @"src/quantum_rng/metal/quantum_kernels.metal"; + + // Try to compile from source + NSString* shaderSource = [NSString stringWithContentsOfFile:shaderPath + encoding:NSUTF8StringEncoding + error:&error]; + + if (shaderSource) { + ctx->library = [ctx->device newLibraryWithSource:shaderSource + options:nil + error:&error]; + } + + if (!ctx->library) { + // Try default library (pre-compiled) + ctx->library = [ctx->device newDefaultLibrary]; + } + + if (!ctx->library) { + fprintf(stderr, "Metal: Failed to load shader library: %s\n", + [[error localizedDescription] UTF8String]); + free(ctx); + return NULL; + } + + // Load batch kernels + NSError* batchError = nil; + NSString* batchPath = @"src/quantum_rng/metal/quantum_kernels_batch.metal"; + NSString* batchSource = [NSString stringWithContentsOfFile:batchPath + encoding:NSUTF8StringEncoding + error:&batchError]; + + if (batchSource) { + id batchLibrary = [ctx->device newLibraryWithSource:batchSource + options:nil + error:&batchError]; + if (batchLibrary) { + id batchFunc = [batchLibrary newFunctionWithName:@"grover_batch_search"]; + if (batchFunc) { + ctx->batchSearchPipeline = [ctx->device newComputePipelineStateWithFunction:batchFunc + error:&batchError]; + if (!ctx->batchSearchPipeline) { + fprintf(stderr, "Metal: Failed to compile batch search pipeline: %s\n", + [[batchError localizedDescription] UTF8String]); + } else { + printf("Metal: Batch search pipeline compiled successfully\n"); + printf(" Max threads per threadgroup: %lu\n", + (unsigned long)[ctx->batchSearchPipeline maxTotalThreadsPerThreadgroup]); + printf(" Threadgroup memory: %lu bytes\n", + (unsigned long)[ctx->batchSearchPipeline threadExecutionWidth]); + } + } else { + fprintf(stderr, "Metal: Failed to find grover_batch_search function\n"); + } + + id batchHadamard = [batchLibrary newFunctionWithName:@"batch_hadamard_init"]; + if (batchHadamard) { + ctx->batchHadamardPipeline = [ctx->device newComputePipelineStateWithFunction:batchHadamard + error:&batchError]; + if (!ctx->batchHadamardPipeline) { + fprintf(stderr, "Metal: Failed to compile batch Hadamard pipeline: %s\n", + [[batchError localizedDescription] UTF8String]); + } else { + printf("Metal: Batch Hadamard pipeline compiled successfully\n"); + } + } else { + fprintf(stderr, "Metal: Failed to find batch_hadamard_init function\n"); + } + } else if (batchError) { + fprintf(stderr, "Metal: Failed to compile batch library: %s\n", + [[batchError localizedDescription] UTF8String]); + } + } + + // Compile compute pipelines + NSArray* kernelNames = @[ + @"hadamard_transform", + @"hadamard_all_qubits", + @"oracle_single_target", + @"sparse_oracle", + @"grover_diffusion_fused", + @"compute_probabilities", + @"normalize_state", + @"pauli_x", + @"pauli_z" + ]; + + id* pipelines[] = { + &ctx->hadamardPipeline, + &ctx->hadamardAllPipeline, + &ctx->oraclePipeline, + &ctx->oracleMultiPipeline, + &ctx->diffusionPipeline, + &ctx->probabilitiesPipeline, + &ctx->normalizePipeline, + &ctx->pauliXPipeline, + &ctx->pauliZPipeline + }; + + for (size_t i = 0; i < [kernelNames count]; i++) { + id function = [ctx->library newFunctionWithName:kernelNames[i]]; + if (!function) { + fprintf(stderr, "Metal: Failed to load function: %s\n", + [kernelNames[i] UTF8String]); + continue; + } + + *pipelines[i] = [ctx->device newComputePipelineStateWithFunction:function + error:&error]; + if (!*pipelines[i]) { + fprintf(stderr, "Metal: Failed to create pipeline for %s: %s\n", + [kernelNames[i] UTF8String], + [[error localizedDescription] UTF8String]); + } + } + + ctx->performance_monitoring = 0; + ctx->last_execution_time = 0.0; + + // Detect GPU cores + uint32_t num_cores = 0; + metal_get_device_info(ctx, NULL, NULL, &num_cores); + + printf("Metal: Compute pipelines compiled successfully\n"); + printf("Metal: Ready for GPU acceleration (%u cores)\n", num_cores); + + return ctx; + } +} + +void metal_compute_free(metal_compute_ctx_t* ctx) { + if (!ctx) return; + + @autoreleasepool { + // ARC will handle cleanup of Metal objects + free(ctx); + } +} + +int metal_is_available(void) { + @autoreleasepool { + id device = MTLCreateSystemDefaultDevice(); + return device != nil; + } +} + +void metal_get_device_info( + metal_compute_ctx_t* ctx, + char* name, + uint32_t* max_threads, + uint32_t* num_cores +) { + if (!ctx || !ctx->device) return; + + @autoreleasepool { + if (name) { + NSString* deviceName = [ctx->device name]; + strncpy(name, [deviceName UTF8String], 255); + name[255] = '\0'; + } + + if (max_threads) { + *max_threads = (uint32_t)[ctx->device maxThreadsPerThreadgroup].width; + } + + if (num_cores) { + // Estimate GPU cores based on device capabilities + // This is a heuristic since Metal doesn't expose core count directly + + NSString* deviceName = [ctx->device name]; + uint64_t memorySize = [ctx->device recommendedMaxWorkingSetSize]; + + // M-series GPU core estimates based on device configuration + if ([deviceName containsString:@"M2 Ultra"]) { + // M2 Ultra: 60 or 76 cores depending on configuration + *num_cores = (memorySize > 100ULL * 1024 * 1024 * 1024) ? 76 : 60; + } else if ([deviceName containsString:@"M2 Max"]) { + // M2 Max: 30 or 38 cores + *num_cores = (memorySize > 50ULL * 1024 * 1024 * 1024) ? 38 : 30; + } else if ([deviceName containsString:@"M2 Pro"]) { + // M2 Pro: 16 or 19 cores + *num_cores = 19; + } else if ([deviceName containsString:@"M2"]) { + // Base M2: 8 or 10 cores + *num_cores = 10; + } else if ([deviceName containsString:@"M1 Ultra"]) { + // M1 Ultra: 48 or 64 cores + *num_cores = (memorySize > 100ULL * 1024 * 1024 * 1024) ? 64 : 48; + } else if ([deviceName containsString:@"M1 Max"]) { + // M1 Max: 24 or 32 cores + *num_cores = 32; + } else if ([deviceName containsString:@"M1 Pro"]) { + // M1 Pro: 14 or 16 cores + *num_cores = 16; + } else if ([deviceName containsString:@"M1"]) { + // Base M1: 7 or 8 cores + *num_cores = 8; + } else { + // Fallback: estimate from recommended working set + // Rough heuristic: ~2GB RAM per GPU core + *num_cores = (uint32_t)(memorySize / (2ULL * 1024 * 1024 * 1024)); + if (*num_cores < 4) *num_cores = 4; // Minimum reasonable estimate + if (*num_cores > 128) *num_cores = 128; // Maximum reasonable estimate + } + } + } +} + +// ============================================================================ +// MEMORY MANAGEMENT +// ============================================================================ + +metal_buffer_t* metal_buffer_create(metal_compute_ctx_t* ctx, size_t size) { + if (!ctx || !ctx->device || size == 0) return NULL; + + @autoreleasepool { + metal_buffer_t* buffer = (metal_buffer_t*)malloc(sizeof(metal_buffer_t)); + if (!buffer) return NULL; + + // Create buffer with shared storage (zero-copy) + buffer->buffer = [ctx->device newBufferWithLength:size + options:MTLResourceStorageModeShared]; + + if (!buffer->buffer) { + free(buffer); + return NULL; + } + + buffer->size = size; + return buffer; + } +} + +metal_buffer_t* metal_buffer_create_from_data( + metal_compute_ctx_t* ctx, + void* data, + size_t size +) { + if (!ctx || !ctx->device || !data || size == 0) return NULL; + + @autoreleasepool { + metal_buffer_t* buffer = (metal_buffer_t*)malloc(sizeof(metal_buffer_t)); + if (!buffer) return NULL; + + // Create buffer with existing data (zero-copy when possible) + buffer->buffer = [ctx->device newBufferWithBytesNoCopy:data + length:size + options:MTLResourceStorageModeShared + deallocator:nil]; + + if (!buffer->buffer) { + // Fallback: copy data + buffer->buffer = [ctx->device newBufferWithBytes:data + length:size + options:MTLResourceStorageModeShared]; + } + + if (!buffer->buffer) { + free(buffer); + return NULL; + } + + buffer->size = size; + return buffer; + } +} + +void* metal_buffer_contents(metal_buffer_t* buffer) { + if (!buffer || !buffer->buffer) return NULL; + + @autoreleasepool { + return [buffer->buffer contents]; + } +} + +void metal_buffer_free(metal_buffer_t* buffer) { + if (!buffer) return; + + @autoreleasepool { + // ARC will handle buffer cleanup + free(buffer); + } +} + +// ============================================================================ +// KERNEL DISPATCH HELPER +// ============================================================================ + +static int dispatch_kernel( + metal_compute_ctx_t* ctx, + id pipeline, + metal_buffer_t** buffers, + size_t num_buffers, + uint32_t* constants, + size_t num_constants, + uint32_t grid_size +) { + if (!ctx || !pipeline) return -1; + + @autoreleasepool { + double start_time = ctx->performance_monitoring ? get_time_seconds() : 0.0; + + id commandBuffer = [ctx->commandQueue commandBuffer]; + id encoder = [commandBuffer computeCommandEncoder]; + + [encoder setComputePipelineState:pipeline]; + + // Bind buffers + for (size_t i = 0; i < num_buffers; i++) { + if (buffers[i] && buffers[i]->buffer) { + [encoder setBuffer:buffers[i]->buffer offset:0 atIndex:i]; + } + } + + // Bind constants + for (size_t i = 0; i < num_constants; i++) { + [encoder setBytes:&constants[i] length:sizeof(uint32_t) atIndex:num_buffers + i]; + } + + // Calculate threadgroup size and count + NSUInteger maxThreads = [pipeline maxTotalThreadsPerThreadgroup]; + NSUInteger threadgroupSize = MIN(1024, maxThreads); // Optimal for M2 Ultra + NSUInteger threadgroups = (grid_size + threadgroupSize - 1) / threadgroupSize; + + MTLSize threadsPerThreadgroup = MTLSizeMake(threadgroupSize, 1, 1); + MTLSize threadgroupCount = MTLSizeMake(threadgroups, 1, 1); + + [encoder dispatchThreadgroups:threadgroupCount + threadsPerThreadgroup:threadsPerThreadgroup]; + + [encoder endEncoding]; + [commandBuffer commit]; + [commandBuffer waitUntilCompleted]; + + if (ctx->performance_monitoring) { + ctx->last_execution_time = get_time_seconds() - start_time; + } + + // Check for errors + if ([commandBuffer status] == MTLCommandBufferStatusError) { + set_error(ctx, @"Command buffer execution failed"); + return -1; + } + + return 0; + } +} + +// ============================================================================ +// QUANTUM GATE OPERATIONS +// ============================================================================ + +int metal_hadamard( + metal_compute_ctx_t* ctx, + metal_buffer_t* amplitudes, + uint32_t qubit_index, + uint32_t state_dim +) { + if (!ctx || !amplitudes) return -1; + + metal_buffer_t* buffers[] = {amplitudes}; + uint32_t constants[] = {qubit_index, state_dim}; + + uint32_t stride = 1u << qubit_index; + uint32_t num_pairs = state_dim / 2; + + return dispatch_kernel(ctx, ctx->hadamardPipeline, + buffers, 1, + constants, 2, + num_pairs); +} + +int metal_hadamard_all( + metal_compute_ctx_t* ctx, + metal_buffer_t* amplitudes, + uint32_t num_qubits, + uint32_t state_dim +) { + if (!ctx || !amplitudes) return -1; + + metal_buffer_t* buffers[] = {amplitudes}; + uint32_t constants[] = {num_qubits, state_dim}; + + return dispatch_kernel(ctx, ctx->hadamardAllPipeline, + buffers, 1, + constants, 2, + state_dim); +} + +int metal_oracle( + metal_compute_ctx_t* ctx, + metal_buffer_t* amplitudes, + uint32_t target_state, + uint32_t state_dim +) { + if (!ctx || !amplitudes) return -1; + + metal_buffer_t* buffers[] = {amplitudes}; + uint32_t constants[] = {target_state}; + + return dispatch_kernel(ctx, ctx->oraclePipeline, + buffers, 1, + constants, 1, + state_dim); +} + +int metal_oracle_multi( + metal_compute_ctx_t* ctx, + metal_buffer_t* amplitudes, + const uint32_t* marked_states, + uint32_t num_marked, + uint32_t state_dim +) { + if (!ctx || !amplitudes || !marked_states) return -1; + + // Create buffer for marked states + metal_buffer_t* marked_buffer = metal_buffer_create_from_data( + ctx, (void*)marked_states, num_marked * sizeof(uint32_t)); + + if (!marked_buffer) return -1; + + metal_buffer_t* buffers[] = {amplitudes, marked_buffer}; + uint32_t constants[] = {num_marked, state_dim}; + + int result = dispatch_kernel(ctx, ctx->oracleMultiPipeline, + buffers, 2, + constants, 2, + state_dim); + + metal_buffer_free(marked_buffer); + return result; +} + +int metal_grover_diffusion( + metal_compute_ctx_t* ctx, + metal_buffer_t* amplitudes, + uint32_t num_qubits, + uint32_t state_dim +) { + if (!ctx || !amplitudes) return -1; + + // Create scratch buffer for average computation + metal_buffer_t* scratch = metal_buffer_create(ctx, 8); // 2 floats + if (!scratch) return -1; + + metal_buffer_t* buffers[] = {amplitudes, scratch}; + uint32_t constants[] = {num_qubits, state_dim}; + + int result = dispatch_kernel(ctx, ctx->diffusionPipeline, + buffers, 2, + constants, 2, + state_dim); + + metal_buffer_free(scratch); + return result; +} + +int metal_pauli_x( + metal_compute_ctx_t* ctx, + metal_buffer_t* amplitudes, + uint32_t qubit_index, + uint32_t state_dim +) { + if (!ctx || !amplitudes) return -1; + + metal_buffer_t* buffers[] = {amplitudes}; + uint32_t constants[] = {qubit_index, state_dim}; + + uint32_t num_pairs = state_dim / 2; + return dispatch_kernel(ctx, ctx->pauliXPipeline, + buffers, 1, + constants, 2, + num_pairs); +} + +int metal_pauli_z( + metal_compute_ctx_t* ctx, + metal_buffer_t* amplitudes, + uint32_t qubit_index, + uint32_t state_dim +) { + if (!ctx || !amplitudes) return -1; + + metal_buffer_t* buffers[] = {amplitudes}; + uint32_t constants[] = {qubit_index, state_dim}; + + return dispatch_kernel(ctx, ctx->pauliZPipeline, + buffers, 1, + constants, 2, + state_dim); +} + +// ============================================================================ +// PROBABILITY & MEASUREMENT +// ============================================================================ + +int metal_compute_probabilities( + metal_compute_ctx_t* ctx, + metal_buffer_t* amplitudes, + metal_buffer_t* probabilities, + uint32_t state_dim +) { + if (!ctx || !amplitudes || !probabilities) return -1; + + metal_buffer_t* buffers[] = {amplitudes, probabilities}; + uint32_t constants[] = {state_dim}; + + return dispatch_kernel(ctx, ctx->probabilitiesPipeline, + buffers, 2, + constants, 1, + state_dim); +} + +int metal_normalize( + metal_compute_ctx_t* ctx, + metal_buffer_t* amplitudes, + float norm, + uint32_t state_dim +) { + if (!ctx || !amplitudes) return -1; + + metal_buffer_t* buffers[] = {amplitudes}; + uint32_t constants[] = {*(uint32_t*)&norm, state_dim}; // Cast float to uint32 for passing + + return dispatch_kernel(ctx, ctx->normalizePipeline, + buffers, 1, + constants, 2, + state_dim); +} + +// ============================================================================ +// BATCH OPERATIONS +// ============================================================================ + +int metal_grover_iteration( + metal_compute_ctx_t* ctx, + metal_buffer_t* amplitudes, + uint32_t target_state, + uint32_t num_qubits, + uint32_t state_dim +) { + // Oracle + if (metal_oracle(ctx, amplitudes, target_state, state_dim) != 0) { + return -1; + } + + // Diffusion + return metal_grover_diffusion(ctx, amplitudes, num_qubits, state_dim); +} + +int metal_grover_search( + metal_compute_ctx_t* ctx, + metal_buffer_t* amplitudes, + uint32_t target_state, + uint32_t num_qubits, + uint32_t state_dim, + uint32_t num_iterations +) { + // Initialize: Apply Hadamard to all qubits + if (metal_hadamard_all(ctx, amplitudes, num_qubits, state_dim) != 0) { + return -1; + } + + // Run Grover iterations + for (uint32_t i = 0; i < num_iterations; i++) { + if (metal_grover_iteration(ctx, amplitudes, target_state, num_qubits, state_dim) != 0) { + return -1; + } + } + + return 0; +} + +// ============================================================================ + +// ============================================================================ +// BATCH PROCESSING (THE BREAKTHROUGH!) +// ============================================================================ + +int metal_grover_batch_search( + metal_compute_ctx_t* ctx, + metal_buffer_t* batch_states, + const uint32_t* targets, + uint32_t* results, + uint32_t num_searches, + uint32_t num_qubits, + uint32_t num_iterations +) { + if (!ctx || !batch_states || !targets || !results) return -1; + if (num_searches == 0 || num_qubits == 0) return -1; + + @autoreleasepool { + // Load batch kernel if not already loaded + if (!ctx->batchSearchPipeline) { + // Try to load from batch kernel file + NSError* error = nil; + NSString* batchPath = @"src/quantum_rng/metal/quantum_kernels_batch.metal"; + NSString* batchSource = [NSString stringWithContentsOfFile:batchPath + encoding:NSUTF8StringEncoding + error:&error]; + + if (batchSource) { + id batchLibrary = [ctx->device newLibraryWithSource:batchSource + options:nil + error:&error]; + if (batchLibrary) { + id function = [batchLibrary newFunctionWithName:@"grover_batch_search"]; + if (function) { + ctx->batchSearchPipeline = [ctx->device newComputePipelineStateWithFunction:function + error:&error]; + } + } + } + + if (!ctx->batchSearchPipeline) { + fprintf(stderr, "Metal: Failed to load batch search kernel\n"); + return -1; + } + } + + double start_time = ctx->performance_monitoring ? get_time_seconds() : 0.0; + + // Create Metal buffers for targets and results + metal_buffer_t* targets_buf = metal_buffer_create_from_data( + ctx, (void*)targets, num_searches * sizeof(uint32_t)); + metal_buffer_t* results_buf = metal_buffer_create( + ctx, num_searches * sizeof(uint32_t)); + + if (!targets_buf || !results_buf) { + if (targets_buf) metal_buffer_free(targets_buf); + if (results_buf) metal_buffer_free(results_buf); + return -1; + } + + // Dispatch batch kernel + id commandBuffer = [ctx->commandQueue commandBuffer]; + id encoder = [commandBuffer computeCommandEncoder]; + + [encoder setComputePipelineState:ctx->batchSearchPipeline]; + [encoder setBuffer:batch_states->buffer offset:0 atIndex:0]; + [encoder setBuffer:targets_buf->buffer offset:0 atIndex:1]; + [encoder setBuffer:results_buf->buffer offset:0 atIndex:2]; + [encoder setBytes:&num_searches length:sizeof(uint32_t) atIndex:3]; + [encoder setBytes:&num_qubits length:sizeof(uint32_t) atIndex:4]; + [encoder setBytes:&num_iterations length:sizeof(uint32_t) atIndex:5]; + + // CRITICAL: One threadgroup per search! + // Each threadgroup has 1024 threads to process its quantum state + MTLSize threadsPerThreadgroup = MTLSizeMake(1024, 1, 1); + MTLSize threadgroupCount = MTLSizeMake(num_searches, 1, 1); + + [encoder dispatchThreadgroups:threadgroupCount + threadsPerThreadgroup:threadsPerThreadgroup]; + + [encoder endEncoding]; + [commandBuffer commit]; + [commandBuffer waitUntilCompleted]; + + if (ctx->performance_monitoring) { + ctx->last_execution_time = get_time_seconds() - start_time; + } + + // Copy results back + uint32_t* results_ptr = (uint32_t*)metal_buffer_contents(results_buf); + memcpy(results, results_ptr, num_searches * sizeof(uint32_t)); + + metal_buffer_free(targets_buf); + metal_buffer_free(results_buf); + + return ([commandBuffer status] == MTLCommandBufferStatusCompleted) ? 0 : -1; + } +} +// SYNCHRONIZATION & UTILITIES +// ============================================================================ + +void metal_wait_completion(metal_compute_ctx_t* ctx) { + if (!ctx || !ctx->commandQueue) return; + + @autoreleasepool { + // Create a barrier to wait for all pending operations + id commandBuffer = [ctx->commandQueue commandBuffer]; + [commandBuffer commit]; + [commandBuffer waitUntilCompleted]; + } +} + +double metal_get_last_execution_time(metal_compute_ctx_t* ctx) { + return ctx ? ctx->last_execution_time : 0.0; +} + +void metal_set_performance_monitoring(metal_compute_ctx_t* ctx, int enable) { + if (ctx) { + ctx->performance_monitoring = enable; + } +} + +// ============================================================================ +// DIAGNOSTICS +// ============================================================================ + +void metal_print_device_info(metal_compute_ctx_t* ctx) { + if (!ctx || !ctx->device) return; + + @autoreleasepool { + printf("\n"); + printf("╔═══════════════════════════════════════════════════════════╗\n"); + printf("║ METAL GPU DEVICE INFORMATION ║\n"); + printf("╠═══════════════════════════════════════════════════════════╣\n"); + printf("║ ║\n"); + + NSString* name = [ctx->device name]; + printf("║ Device: %-48s ║\n", [name UTF8String]); + printf("║ Unified Memory: %7llu MB ║\n", + [ctx->device recommendedMaxWorkingSetSize] / (1024 * 1024)); + printf("║ Max Threadgroup Size: %7lu ║\n", + (unsigned long)[ctx->device maxThreadsPerThreadgroup].width); + printf("║ GPU Cores (M2 Ultra): 76 ║\n"); + printf("║ Memory Bandwidth: ~800 GB/s ║\n"); + printf("║ Storage Mode: MTLResourceStorageModeShared (zero-copy) ║\n"); + printf("║ ║\n"); + printf("╚═══════════════════════════════════════════════════════════╝\n"); + printf("\n"); + } +} + +const char* metal_get_error(metal_compute_ctx_t* ctx) { + if (!ctx || !ctx->lastError) return "No error"; + + @autoreleasepool { + return [ctx->lastError UTF8String]; + } +} \ No newline at end of file diff --git a/vendor/quantum_rng/src/quantum_rng/metal/quantum_kernels.metal b/vendor/quantum_rng/src/quantum_rng/metal/quantum_kernels.metal new file mode 100644 index 0000000..a79e043 --- /dev/null +++ b/vendor/quantum_rng/src/quantum_rng/metal/quantum_kernels.metal @@ -0,0 +1,510 @@ +#include +using namespace metal; + +/** + * @file quantum_kernels.metal + * @brief Metal GPU kernels for quantum operations on M2 Ultra + * + * PERFORMANCE TARGET: 100-200x speedup over CPU + * - 76 GPU cores × 1024 threads = 77,824 parallel threads + * - Threadgroup size: 256-1024 threads + * - Zero-copy unified memory (MTLResourceStorageModeShared) + */ + +// ============================================================================ +// COMPLEX NUMBER OPERATIONS +// ============================================================================ + +struct Complex { + float real; + float imag; + + Complex(float r = 0.0f, float i = 0.0f) : real(r), imag(i) {} + + // Operators for thread address space (default) + Complex operator+(thread const Complex& other) const { + return Complex(real + other.real, imag + other.imag); + } + + Complex operator-(thread const Complex& other) const { + return Complex(real - other.real, imag - other.imag); + } + + Complex operator*(thread const Complex& other) const { + return Complex( + real * other.real - imag * other.imag, + real * other.imag + imag * other.real + ); + } + + Complex operator*(float scalar) const { + return Complex(real * scalar, imag * scalar); + } + + float magnitude_squared() const { + return real * real + imag * imag; + } +}; + +// Helper functions for device memory operations +static Complex complex_add(Complex a, device Complex& b) { + return Complex(a.real + b.real, a.imag + b.imag); +} + +static Complex complex_sub(Complex a, device Complex& b) { + return Complex(a.real - b.real, a.imag - b.imag); +} + +static Complex complex_mul_scalar(device Complex& a, float s) { + return Complex(a.real * s, a.imag * s); +} + +// Constants +constant float SQRT2_INV = 0.7071067811865476f; // 1/√2 + +// ============================================================================ +// HADAMARD TRANSFORM KERNEL (HIGHEST PRIORITY - 40% OF RUNTIME) +// ============================================================================ + +/** + * @brief GPU-accelerated Hadamard gate + * + * H = 1/√2 * [1 1] + * [1 -1] + * + * OPTIMIZATION STRATEGY: + * - Stride-based access pattern for coalesced memory + * - Each thread processes one amplitude pair + * - Full GPU utilization: 76 threadgroups × 1024 threads + * + * Expected speedup: 20-40x vs CPU (memory bandwidth limited) + */ +kernel void hadamard_transform( + device Complex* amplitudes [[buffer(0)]], + constant uint& qubit_index [[buffer(1)]], + constant uint& state_dim [[buffer(2)]], + uint gid [[thread_position_in_grid]] +) { + const uint stride = 1u << qubit_index; + const uint block_size = stride << 1; + + // Calculate which block and position within block + const uint block = gid / stride; + const uint pos = gid % stride; + const uint base = block * block_size; + + if (base + pos + stride >= state_dim) return; + + const uint idx0 = base + pos; + const uint idx1 = idx0 + stride; + + // Load amplitudes + Complex amp0 = amplitudes[idx0]; + Complex amp1 = amplitudes[idx1]; + + // Hadamard transformation + // |0⟩ → (|0⟩ + |1⟩)/√2 + // |1⟩ → (|0⟩ - |1⟩)/√2 + amplitudes[idx0] = (amp0 + amp1) * SQRT2_INV; + amplitudes[idx1] = (amp0 - amp1) * SQRT2_INV; +} + +// ============================================================================ +// MULTI-HADAMARD KERNEL (BATCH PROCESSING) +// ============================================================================ + +/** + * @brief Apply Hadamard to all qubits simultaneously + * + * OPTIMIZATION: Processes all Hadamard operations in single GPU dispatch + * Eliminates CPU↔GPU round-trips between gate applications + */ +kernel void hadamard_all_qubits( + device Complex* amplitudes [[buffer(0)]], + constant uint& num_qubits [[buffer(1)]], + constant uint& state_dim [[buffer(2)]], + uint gid [[thread_position_in_grid]] +) { + if (gid >= state_dim) return; + + // Apply Hadamard to each qubit sequentially + // (In-place transformation) + for (uint qubit = 0; qubit < num_qubits; qubit++) { + const uint stride = 1u << qubit; + const uint block_size = stride << 1; + const uint block = gid / block_size; + const uint pos = gid % stride; + + if ((gid & stride) == 0) { // Only process first half of each block + const uint idx0 = gid; + const uint idx1 = gid | stride; + + Complex amp0 = amplitudes[idx0]; + Complex amp1 = amplitudes[idx1]; + + amplitudes[idx0] = (amp0 + amp1) * SQRT2_INV; + amplitudes[idx1] = (amp0 - amp1) * SQRT2_INV; + + threadgroup_barrier(mem_flags::mem_device); + } + } +} + +// ============================================================================ +// SPARSE ORACLE KERNEL (GROVER PHASE FLIP) +// ============================================================================ + +/** + * @brief GPU-accelerated oracle for Grover's algorithm + * + * Flips phase of target state: |target⟩ → -|target⟩ + * + * OPTIMIZATION: + * - Embarrassingly parallel (no dependencies) + * - Expected speedup: 50-100x vs CPU + * - Each thread checks one amplitude independently + */ +kernel void sparse_oracle( + device Complex* amplitudes [[buffer(0)]], + constant uint* marked_states [[buffer(1)]], + constant uint& num_marked [[buffer(2)]], + constant uint& state_dim [[buffer(3)]], + uint gid [[thread_position_in_grid]] +) { + if (gid >= state_dim) return; + + // Check if this state is marked + for (uint i = 0; i < num_marked; i++) { + if (gid == marked_states[i]) { + // Phase flip: multiply by -1 + amplitudes[gid].real = -amplitudes[gid].real; + amplitudes[gid].imag = -amplitudes[gid].imag; + return; + } + } +} + +// ============================================================================ +// OPTIMIZED ORACLE (SINGLE TARGET) +// ============================================================================ + +/** + * @brief Optimized oracle for single marked state + * + * Most common case in Grover's algorithm + */ +kernel void oracle_single_target( + device Complex* amplitudes [[buffer(0)]], + constant uint& target_state [[buffer(1)]], + uint gid [[thread_position_in_grid]] +) { + if (gid == target_state) { + amplitudes[gid].real = -amplitudes[gid].real; + amplitudes[gid].imag = -amplitudes[gid].imag; + } +} + +// ============================================================================ +// INVERSION ABOUT AVERAGE (GROVER DIFFUSION) +// ============================================================================ + +/** + * @brief GPU-accelerated diffusion operator + * + * D = 2|ψ⟩⟨ψ| - I = H^⊗n (2|0⟩⟨0| - I) H^⊗n + * + * OPTIMIZATION STRATEGY: + * - Two-pass algorithm: + * Pass 1: Compute average amplitude using parallel reduction + * Pass 2: Apply inversion about average + * - Uses threadgroup shared memory for reduction + * + * Expected speedup: 15-30x vs CPU + */ + +// Pass 1: Compute average using parallel reduction +kernel void compute_average_amplitude( + device const Complex* amplitudes [[buffer(0)]], + device float* partial_sums_real [[buffer(1)]], + device float* partial_sums_imag [[buffer(2)]], + constant uint& state_dim [[buffer(3)]], + uint gid [[thread_position_in_grid]], + uint lid [[thread_position_in_threadgroup]], + uint threadgroup_size [[threads_per_threadgroup]] +) { + // Shared memory for reduction within threadgroup + threadgroup float shared_real[1024]; + threadgroup float shared_imag[1024]; + + // Load and accumulate + float sum_real = 0.0f; + float sum_imag = 0.0f; + + for (uint i = gid; i < state_dim; i += threadgroup_size * 76) { // 76 threadgroups + sum_real += amplitudes[i].real; + sum_imag += amplitudes[i].imag; + } + + shared_real[lid] = sum_real; + shared_imag[lid] = sum_imag; + + threadgroup_barrier(mem_flags::mem_threadgroup); + + // Parallel reduction within threadgroup + for (uint stride = threadgroup_size / 2; stride > 0; stride >>= 1) { + if (lid < stride) { + shared_real[lid] += shared_real[lid + stride]; + shared_imag[lid] += shared_imag[lid + stride]; + } + threadgroup_barrier(mem_flags::mem_threadgroup); + } + + // Write threadgroup result + if (lid == 0) { + uint threadgroup_id = gid / threadgroup_size; + partial_sums_real[threadgroup_id] = shared_real[0]; + partial_sums_imag[threadgroup_id] = shared_imag[0]; + } +} + +// Pass 2: Apply inversion about average +kernel void inversion_about_average( + device Complex* amplitudes [[buffer(0)]], + constant float& avg_real [[buffer(1)]], + constant float& avg_imag [[buffer(2)]], + constant uint& state_dim [[buffer(3)]], + uint gid [[thread_position_in_grid]] +) { + if (gid >= state_dim) return; + + // Inversion: amp' = 2*avg - amp + float two_avg_real = avg_real * 2.0f; + float two_avg_imag = avg_imag * 2.0f; + amplitudes[gid].real = two_avg_real - amplitudes[gid].real; + amplitudes[gid].imag = two_avg_imag - amplitudes[gid].imag; +} + +// ============================================================================ +// FUSED GROVER DIFFUSION (SINGLE KERNEL) +// ============================================================================ + +/** + * @brief Fused Hadamard + Inversion + Hadamard for diffusion + * + * OPTIMIZATION: Combines three operations into one kernel + * - Eliminates 2 CPU↔GPU round-trips + * - Keeps intermediate results in GPU memory + */ +kernel void grover_diffusion_fused( + device Complex* amplitudes [[buffer(0)]], + constant uint& num_qubits [[buffer(1)]], + constant uint& state_dim [[buffer(2)]], + device float* scratch_avg [[buffer(3)]], // Scratch space for average + uint gid [[thread_position_in_grid]], + uint lid [[thread_position_in_threadgroup]], + uint threadgroup_size [[threads_per_threadgroup]] +) { + // Step 1: Apply Hadamard to all qubits + if (gid < state_dim) { + for (uint qubit = 0; qubit < num_qubits; qubit++) { + const uint stride = 1u << qubit; + if ((gid & stride) == 0) { + const uint idx1 = gid | stride; + + Complex amp0 = amplitudes[gid]; + Complex amp1 = amplitudes[idx1]; + + amplitudes[gid] = (amp0 + amp1) * SQRT2_INV; + amplitudes[idx1] = (amp0 - amp1) * SQRT2_INV; + + threadgroup_barrier(mem_flags::mem_device); + } + } + } + + threadgroup_barrier(mem_flags::mem_device); + + // Step 2: Compute average (local reduction) + threadgroup float shared_real[1024]; + threadgroup float shared_imag[1024]; + + float sum_real = 0.0f; + float sum_imag = 0.0f; + + if (gid < state_dim) { + sum_real = amplitudes[gid].real; + sum_imag = amplitudes[gid].imag; + } + + shared_real[lid] = sum_real; + shared_imag[lid] = sum_imag; + threadgroup_barrier(mem_flags::mem_threadgroup); + + for (uint stride = threadgroup_size / 2; stride > 0; stride >>= 1) { + if (lid < stride && lid + stride < threadgroup_size) { + shared_real[lid] += shared_real[lid + stride]; + shared_imag[lid] += shared_imag[lid + stride]; + } + threadgroup_barrier(mem_flags::mem_threadgroup); + } + + // Broadcast average to all threads + float avg_real = shared_real[0] / float(state_dim); + float avg_imag = shared_imag[0] / float(state_dim); + + threadgroup_barrier(mem_flags::mem_threadgroup); + + // Step 3: Inversion about average + if (gid < state_dim) { + float two_avg_real = avg_real * 2.0f; + float two_avg_imag = avg_imag * 2.0f; + amplitudes[gid].real = two_avg_real - amplitudes[gid].real; + amplitudes[gid].imag = two_avg_imag - amplitudes[gid].imag; + } + + threadgroup_barrier(mem_flags::mem_device); + + // Step 4: Apply Hadamard again + if (gid < state_dim) { + for (uint qubit = 0; qubit < num_qubits; qubit++) { + const uint stride = 1u << qubit; + if ((gid & stride) == 0) { + const uint idx1 = gid | stride; + + Complex amp0 = amplitudes[gid]; + Complex amp1 = amplitudes[idx1]; + + amplitudes[gid] = (amp0 + amp1) * SQRT2_INV; + amplitudes[idx1] = (amp0 - amp1) * SQRT2_INV; + + threadgroup_barrier(mem_flags::mem_device); + } + } + } +} + +// ============================================================================ +// PROBABILITY CALCULATION KERNEL +// ============================================================================ + +/** + * @brief Compute probabilities from amplitudes + * + * P(i) = |α_i|² = real² + imag² + * + * OPTIMIZATION: Embarrassingly parallel + * Expected speedup: 30-50x vs CPU + */ +kernel void compute_probabilities( + device const Complex* amplitudes [[buffer(0)]], + device float* probabilities [[buffer(1)]], + constant uint& state_dim [[buffer(2)]], + uint gid [[thread_position_in_grid]] +) { + if (gid >= state_dim) return; + + float re = amplitudes[gid].real; + float im = amplitudes[gid].imag; + probabilities[gid] = re * re + im * im; +} + +// ============================================================================ +// CUMULATIVE PROBABILITY (FOR MEASUREMENT) +// ============================================================================ + +/** + * @brief Parallel prefix sum for cumulative probabilities + * + * Used for quantum measurement sampling + * Implements Blelloch scan algorithm + */ +kernel void cumulative_sum_upsweep( + device float* data [[buffer(0)]], + constant uint& n [[buffer(1)]], + constant uint& stride [[buffer(2)]], + uint gid [[thread_position_in_grid]] +) { + uint i = (gid + 1) * stride * 2 - 1; + if (i < n) { + data[i] += data[i - stride]; + } +} + +kernel void cumulative_sum_downsweep( + device float* data [[buffer(0)]], + constant uint& n [[buffer(1)]], + constant uint& stride [[buffer(2)]], + uint gid [[thread_position_in_grid]] +) { + uint i = (gid + 1) * stride * 2 - 1; + if (i < n) { + float temp = data[i - stride]; + data[i - stride] = data[i]; + data[i] += temp; + } +} + +// ============================================================================ +// NORMALIZATION KERNEL +// ============================================================================ + +/** + * @brief Normalize quantum state + * + * Divides all amplitudes by sqrt(Σ|α_i|²) + */ +kernel void normalize_state( + device Complex* amplitudes [[buffer(0)]], + constant float& norm [[buffer(1)]], + constant uint& state_dim [[buffer(2)]], + uint gid [[thread_position_in_grid]] +) { + if (gid >= state_dim) return; + + float inv_norm = 1.0f / norm; + amplitudes[gid].real *= inv_norm; + amplitudes[gid].imag *= inv_norm; +} + +// ============================================================================ +// PAULI GATES (BONUS OPTIMIZATIONS) +// ============================================================================ + +// Pauli X (bit flip) +kernel void pauli_x( + device Complex* amplitudes [[buffer(0)]], + constant uint& qubit_index [[buffer(1)]], + constant uint& state_dim [[buffer(2)]], + uint gid [[thread_position_in_grid]] +) { + const uint stride = 1u << qubit_index; + const uint block_size = stride << 1; + const uint block = gid / stride; + const uint pos = gid % stride; + const uint base = block * block_size; + + if (base + pos + stride >= state_dim) return; + + const uint idx0 = base + pos; + const uint idx1 = idx0 + stride; + + Complex temp = amplitudes[idx0]; + amplitudes[idx0] = amplitudes[idx1]; + amplitudes[idx1] = temp; +} + +// Pauli Z (phase flip) +kernel void pauli_z( + device Complex* amplitudes [[buffer(0)]], + constant uint& qubit_index [[buffer(1)]], + constant uint& state_dim [[buffer(2)]], + uint gid [[thread_position_in_grid]] +) { + if (gid >= state_dim) return; + + const uint stride = 1u << qubit_index; + if (gid & stride) { // If qubit is |1⟩ + amplitudes[gid].real = -amplitudes[gid].real; + amplitudes[gid].imag = -amplitudes[gid].imag; + } +} \ No newline at end of file diff --git a/vendor/quantum_rng/src/quantum_rng/metal/quantum_kernels_batch.metal b/vendor/quantum_rng/src/quantum_rng/metal/quantum_kernels_batch.metal new file mode 100644 index 0000000..5ee1578 --- /dev/null +++ b/vendor/quantum_rng/src/quantum_rng/metal/quantum_kernels_batch.metal @@ -0,0 +1,291 @@ +#include +using namespace metal; + +/** + * @file quantum_kernels_batch.metal + * @brief BATCH GPU kernels - The RIGHT way to use GPU! + * + * BREAKTHROUGH: Process MANY Grover searches in parallel! + * - 76 threadgroups = 76 simultaneous Grover searches + * - Each threadgroup (1024 threads) handles ONE complete search + * - Amortizes kernel launch overhead across all searches + * + * EXPECTED: 100-1000x speedup over CPU for batch workloads! + */ + +// ============================================================================ +// COMPLEX NUMBER (same as before) +// ============================================================================ + +struct Complex { + float real; + float imag; + + Complex(float r = 0.0f, float i = 0.0f) : real(r), imag(i) {} + + Complex operator+(thread const Complex& other) const { + return Complex(real + other.real, imag + other.imag); + } + + Complex operator-(thread const Complex& other) const { + return Complex(real - other.real, imag - other.imag); + } + + Complex operator*(thread const Complex& other) const { + return Complex( + real * other.real - imag * other.imag, + real * other.imag + imag * other.real + ); + } + + Complex operator*(float scalar) const { + return Complex(real * scalar, imag * scalar); + } + + float magnitude_squared() const { + return real * real + imag * imag; + } +}; + +constant float SQRT2_INV = 0.7071067811865476f; + +// ============================================================================ +// BATCH GROVER SEARCH KERNEL (THE GAME CHANGER!) +// ============================================================================ + +/** + * @brief Execute COMPLETE Grover search for multiple states in parallel + * + * ARCHITECTURE: + * - Each threadgroup = ONE complete Grover search + * - 76 threadgroups = 76 parallel searches + * - Each search: Initialize → N iterations → Result + * - ALL in ONE kernel launch! + * + * EXPECTED PERFORMANCE: + * - 76 searches in ~150ms (vs 197ms × 76 = 14,972ms on CPU) + * - Speedup: ~100x! + * + * @param batch_states All quantum states (concatenated) + * @param targets Target state for each search + * @param results Output: found states + * @param num_searches Number of parallel searches (≤ 76 optimal) + * @param num_qubits Qubits per search + * @param num_iterations Grover iterations + */ +kernel void grover_batch_search( + device Complex* batch_states [[buffer(0)]], + constant uint* targets [[buffer(1)]], + device uint* results [[buffer(2)]], + constant uint& num_searches [[buffer(3)]], + constant uint& num_qubits [[buffer(4)]], + constant uint& num_iterations [[buffer(5)]], + uint threadgroup_id [[threadgroup_position_in_grid]], + uint thread_id [[thread_position_in_threadgroup]], + uint threadgroup_size [[threads_per_threadgroup]] +) { + // Bounds check + if (threadgroup_id >= num_searches) return; + + const uint state_dim = 1u << num_qubits; + const uint search_offset = threadgroup_id * state_dim; + + // This threadgroup's quantum state + device Complex* amplitudes = &batch_states[search_offset]; + const uint target = targets[threadgroup_id]; + + // Shared memory for reductions + threadgroup float shared_real[1024]; + threadgroup float shared_imag[1024]; + + // ======================================== + // INITIALIZATION: Apply Hadamard to all qubits + // ======================================== + + for (uint qubit = 0; qubit < num_qubits; qubit++) { + const uint stride = 1u << qubit; + + // Each thread processes amplitude pairs + for (uint i = thread_id; i < stride; i += threadgroup_size) { + const uint idx0 = i; + const uint idx1 = i + stride; + + Complex amp0 = amplitudes[idx0]; + Complex amp1 = amplitudes[idx1]; + + amplitudes[idx0] = (amp0 + amp1) * SQRT2_INV; + amplitudes[idx1] = (amp0 - amp1) * SQRT2_INV; + } + + threadgroup_barrier(mem_flags::mem_device); + } + + // ======================================== + // GROVER ITERATIONS + // ======================================== + + for (uint iter = 0; iter < num_iterations; iter++) { + // ORACLE: Flip phase of target state + if (thread_id == 0) { + amplitudes[target].real = -amplitudes[target].real; + amplitudes[target].imag = -amplitudes[target].imag; + } + threadgroup_barrier(mem_flags::mem_device); + + // DIFFUSION OPERATOR + + // Step 1: Apply Hadamard to all qubits + for (uint qubit = 0; qubit < num_qubits; qubit++) { + const uint stride = 1u << qubit; + + for (uint i = thread_id; i < stride; i += threadgroup_size) { + const uint idx0 = i; + const uint idx1 = i + stride; + + Complex amp0 = amplitudes[idx0]; + Complex amp1 = amplitudes[idx1]; + + amplitudes[idx0] = (amp0 + amp1) * SQRT2_INV; + amplitudes[idx1] = (amp0 - amp1) * SQRT2_INV; + } + + threadgroup_barrier(mem_flags::mem_device); + } + + // Step 2: Compute average amplitude (parallel reduction) + float sum_real = 0.0f; + float sum_imag = 0.0f; + + for (uint i = thread_id; i < state_dim; i += threadgroup_size) { + sum_real += amplitudes[i].real; + sum_imag += amplitudes[i].imag; + } + + shared_real[thread_id] = sum_real; + shared_imag[thread_id] = sum_imag; + threadgroup_barrier(mem_flags::mem_threadgroup); + + // Reduction tree + for (uint s = threadgroup_size / 2; s > 0; s >>= 1) { + if (thread_id < s) { + shared_real[thread_id] += shared_real[thread_id + s]; + shared_imag[thread_id] += shared_imag[thread_id + s]; + } + threadgroup_barrier(mem_flags::mem_threadgroup); + } + + float avg_real = shared_real[0] / float(state_dim); + float avg_imag = shared_imag[0] / float(state_dim); + + threadgroup_barrier(mem_flags::mem_threadgroup); + + // Step 3: Inversion about average + for (uint i = thread_id; i < state_dim; i += threadgroup_size) { + float two_avg_real = avg_real * 2.0f; + float two_avg_imag = avg_imag * 2.0f; + amplitudes[i].real = two_avg_real - amplitudes[i].real; + amplitudes[i].imag = two_avg_imag - amplitudes[i].imag; + } + + threadgroup_barrier(mem_flags::mem_device); + + // Step 4: Apply Hadamard again + for (uint qubit = 0; qubit < num_qubits; qubit++) { + const uint stride = 1u << qubit; + + for (uint i = thread_id; i < stride; i += threadgroup_size) { + const uint idx0 = i; + const uint idx1 = i + stride; + + Complex amp0 = amplitudes[idx0]; + Complex amp1 = amplitudes[idx1]; + + amplitudes[idx0] = (amp0 + amp1) * SQRT2_INV; + amplitudes[idx1] = (amp0 - amp1) * SQRT2_INV; + } + + threadgroup_barrier(mem_flags::mem_device); + } + } + + // ======================================== + // MEASUREMENT: Find state with highest probability + // ======================================== + + // Each thread finds max in its partition + uint max_idx = thread_id; + float max_prob = 0.0f; + + for (uint i = thread_id; i < state_dim; i += threadgroup_size) { + // Compute magnitude_squared inline (avoids device->thread copy) + float prob = amplitudes[i].real * amplitudes[i].real + + amplitudes[i].imag * amplitudes[i].imag; + if (prob > max_prob) { + max_prob = prob; + max_idx = i; + } + } + + shared_real[thread_id] = max_prob; + shared_imag[thread_id] = (float)max_idx; // Store index in imag + threadgroup_barrier(mem_flags::mem_threadgroup); + + // Reduction to find global max + for (uint s = threadgroup_size / 2; s > 0; s >>= 1) { + if (thread_id < s) { + if (shared_real[thread_id + s] > shared_real[thread_id]) { + shared_real[thread_id] = shared_real[thread_id + s]; + shared_imag[thread_id] = shared_imag[thread_id + s]; + } + } + threadgroup_barrier(mem_flags::mem_threadgroup); + } + + // Thread 0 writes result + if (thread_id == 0) { + results[threadgroup_id] = (uint)shared_imag[0]; + } +} + +// ============================================================================ +// OPTIMIZED BATCH HADAMARD (ALL SEARCHES, ALL QUBITS) +// ============================================================================ + +/** + * @brief Initialize ALL searches with Hadamard in ONE kernel + * + * Each threadgroup handles initialization for ONE search. + * Processes all qubits for that search. + */ +kernel void batch_hadamard_init( + device Complex* batch_states [[buffer(0)]], + constant uint& num_searches [[buffer(1)]], + constant uint& num_qubits [[buffer(2)]], + uint threadgroup_id [[threadgroup_position_in_grid]], + uint thread_id [[thread_position_in_threadgroup]], + uint threadgroup_size [[threads_per_threadgroup]] +) { + if (threadgroup_id >= num_searches) return; + + const uint state_dim = 1u << num_qubits; + const uint search_offset = threadgroup_id * state_dim; + device Complex* amplitudes = &batch_states[search_offset]; + + // Apply Hadamard to all qubits + for (uint qubit = 0; qubit < num_qubits; qubit++) { + const uint stride = 1u << qubit; + + for (uint i = thread_id; i < stride; i += threadgroup_size) { + const uint idx0 = i; + const uint idx1 = i + stride; + + Complex amp0 = amplitudes[idx0]; + Complex amp1 = amplitudes[idx1]; + + amplitudes[idx0] = (amp0 + amp1) * SQRT2_INV; + amplitudes[idx1] = (amp0 - amp1) * SQRT2_INV; + } + + threadgroup_barrier(mem_flags::mem_device); + } +} \ No newline at end of file diff --git a/vendor/quantum_rng/src/quantum_rng/quantum_constants.h b/vendor/quantum_rng/src/quantum_rng/quantum_constants.h new file mode 100644 index 0000000..bc9707b --- /dev/null +++ b/vendor/quantum_rng/src/quantum_rng/quantum_constants.h @@ -0,0 +1,125 @@ +#ifndef QUANTUM_CONSTANTS_H +#define QUANTUM_CONSTANTS_H + +#include + +/** + * @file quantum_constants.h + * @brief High-precision mathematical and physical constants + * + * All constants in IEEE 754 double precision hexadecimal format + * for maximum precision and reproducibility across platforms. + */ + +// ============================================================================ +// MATHEMATICAL CONSTANTS +// ============================================================================ + +// π and related +#define QC_PI_HEX 0x400921FB54442D18ULL // π +#define QC_PI_2_HEX 0x3FF921FB54442D18ULL // π/2 +#define QC_PI_4_HEX 0x3FE921FB54442D18ULL // π/4 +#define QC_2PI_HEX 0x401921FB54442D18ULL // 2π +#define QC_3PI_4_HEX 0x4002D97C7F3321D2ULL // 3π/4 + +// √2 and related +#define QC_SQRT2_HEX 0x3FF6A09E667F3BCDULL // √2 +#define QC_SQRT2_INV_HEX 0x3FE6A09E667F3BCDULL // 1/√2 +#define QC_2SQRT2_HEX 0x4006A09E667F3BCDULL // 2√2 ≈ 2.8284271 (Tsirelson bound) + +// √3 and related +#define QC_SQRT3_HEX 0x3FFBB67AE8584CAAULL // √3 +#define QC_SQRT3_INV_HEX 0x3FE279A74590331CULL // 1/√3 + +// e and related +#define QC_E_HEX 0x4005BF0A8B145769ULL // e +#define QC_E_INV_HEX 0x3FD78B56362CEF38ULL // 1/e +#define QC_LN2_HEX 0x3FE62E42FEFA39EFULL // ln(2) + +// ============================================================================ +// QUANTUM PHYSICS CONSTANTS (in atomic units) +// ============================================================================ + +// Fine structure constant α ≈ 1/137 +#define QC_FINE_STRUCTURE_HEX 0x3F747AE147AE147BULL +// Planck constant ħ (in reduced units) +#define QC_PLANCK_HEX 0x3FF0000000000000ULL +// Rydberg constant +#define QC_RYDBERG_HEX 0x9E3779B97F4A7C15ULL +// Electron g-factor +#define QC_ELECTRON_G_HEX 0x2B992DDFA232945ULL +// Golden ratio φ +#define QC_GOLDEN_RATIO_HEX 0x3FF9E3779B97F4A8ULL + +// Quantum mixing constants (derived from physical constants) +#define QC_HEISENBERG_HEX 0xC13FA9A902A6328FULL +#define QC_SCHRODINGER_HEX 0x91E10DA5C79E7B1DULL +#define QC_PAULI_X_HEX 0x4C957F2D8A1E6B3CULL +#define QC_PAULI_Y_HEX 0xD3E99E3B6C1A4F78ULL +#define QC_PAULI_Z_HEX 0x8F142FC07892A5B6ULL + +// ============================================================================ +// CONVERSION UTILITIES +// ============================================================================ + +/** + * @brief Convert hexadecimal to double (strict aliasing safe) + * @param hex 64-bit hexadecimal value + * @return Double precision floating point value + */ +static inline double qc_hex_to_double(uint64_t hex) { + union { uint64_t u; double d; } converter; + converter.u = hex; + return converter.d; +} + +// ============================================================================ +// CONSTANT ACCESSORS +// ============================================================================ + +// Mathematical constants +#define QC_PI (qc_hex_to_double(QC_PI_HEX)) +#define QC_PI_2 (qc_hex_to_double(QC_PI_2_HEX)) +#define QC_PI_4 (qc_hex_to_double(QC_PI_4_HEX)) +#define QC_2PI (qc_hex_to_double(QC_2PI_HEX)) +#define QC_3PI_4 (qc_hex_to_double(QC_3PI_4_HEX)) + +#define QC_SQRT2 (qc_hex_to_double(QC_SQRT2_HEX)) +#define QC_SQRT2_INV (qc_hex_to_double(QC_SQRT2_INV_HEX)) +#define QC_2SQRT2 (qc_hex_to_double(QC_2SQRT2_HEX)) + +#define QC_SQRT3 (qc_hex_to_double(QC_SQRT3_HEX)) +#define QC_SQRT3_INV (qc_hex_to_double(QC_SQRT3_INV_HEX)) + +#define QC_E (qc_hex_to_double(QC_E_HEX)) +#define QC_E_INV (qc_hex_to_double(QC_E_INV_HEX)) +#define QC_LN2 (qc_hex_to_double(QC_LN2_HEX)) + +// Physical constants +#define QC_FINE_STRUCTURE (qc_hex_to_double(QC_FINE_STRUCTURE_HEX)) +#define QC_PLANCK (qc_hex_to_double(QC_PLANCK_HEX)) +#define QC_RYDBERG (qc_hex_to_double(QC_RYDBERG_HEX)) +#define QC_ELECTRON_G (qc_hex_to_double(QC_ELECTRON_G_HEX)) +#define QC_GOLDEN_RATIO (qc_hex_to_double(QC_GOLDEN_RATIO_HEX)) + +#define QC_HEISENBERG (qc_hex_to_double(QC_HEISENBERG_HEX)) +#define QC_SCHRODINGER (qc_hex_to_double(QC_SCHRODINGER_HEX)) +#define QC_PAULI_X (qc_hex_to_double(QC_PAULI_X_HEX)) +#define QC_PAULI_Y (qc_hex_to_double(QC_PAULI_Y_HEX)) +#define QC_PAULI_Z (qc_hex_to_double(QC_PAULI_Z_HEX)) + +// ============================================================================ +// COMMON QUANTUM CIRCUIT CONSTANTS +// ============================================================================ + +// Common rotation angles +#define QC_PI_8 (QC_PI / 8.0) // π/8 (T gate angle) +#define QC_PI_16 (QC_PI / 16.0) // π/16 + +// Tsirelson bound for CHSH inequality +#define QC_TSIRELSON_BOUND QC_2SQRT2 // 2√2 ≈ 2.828 + +// Maximum entanglement entropy (1 ebit) +#define QC_MAX_EBIT 1.0 + +#endif /* QUANTUM_CONSTANTS_H */ \ No newline at end of file diff --git a/vendor/quantum_rng/src/quantum_rng/quantum_entropy.h b/vendor/quantum_rng/src/quantum_rng/quantum_entropy.h new file mode 100644 index 0000000..900f1e1 --- /dev/null +++ b/vendor/quantum_rng/src/quantum_rng/quantum_entropy.h @@ -0,0 +1,117 @@ +#ifndef QUANTUM_ENTROPY_H +#define QUANTUM_ENTROPY_H + +#include +#include + +/** + * @file quantum_entropy.h + * @brief Cryptographically secure entropy interface for quantum operations + * + * Provides secure random number generation for quantum measurement simulation + * and sampling operations. This interface allows quantum modules to access + * cryptographic-quality randomness without creating circular dependencies. + * + * SECURITY CRITICAL: Quantum measurements must use cryptographically secure + * randomness. Using predictable sources like stdlib rand() completely + * undermines the security of the quantum RNG system. + */ + +/** + * @brief Entropy source function pointer type + * + * Function that provides cryptographically secure random bytes. + * Must be thread-safe if used in concurrent contexts. + * + * @param user_data Opaque pointer to entropy context + * @param buffer Output buffer for random bytes + * @param size Number of bytes to generate + * @return 0 on success, non-zero on error + */ +typedef int (*quantum_entropy_fn)(void *user_data, uint8_t *buffer, size_t size); + +/** + * @brief Quantum entropy context + * + * Encapsulates entropy source for quantum operations. + * Can be backed by secure_rng, hardware entropy, or other sources. + */ +typedef struct { + quantum_entropy_fn get_bytes; /**< Entropy generation function */ + void *user_data; /**< Opaque context for entropy source */ +} quantum_entropy_ctx_t; + +/** + * @brief Initialize quantum entropy context + * + * @param ctx Entropy context to initialize + * @param get_bytes Entropy function + * @param user_data User data for entropy function + */ +static inline void quantum_entropy_init( + quantum_entropy_ctx_t *ctx, + quantum_entropy_fn get_bytes, + void *user_data +) { + if (!ctx) return; + ctx->get_bytes = get_bytes; + ctx->user_data = user_data; +} + +/** + * @brief Get cryptographically secure random bytes + * + * @param ctx Entropy context + * @param buffer Output buffer + * @param size Number of bytes to generate + * @return 0 on success, non-zero on error + */ +static inline int quantum_entropy_get_bytes( + quantum_entropy_ctx_t *ctx, + uint8_t *buffer, + size_t size +) { + if (!ctx || !ctx->get_bytes || !buffer || size == 0) { + return -1; + } + return ctx->get_bytes(ctx->user_data, buffer, size); +} + +/** + * @brief Get cryptographically secure random double in [0, 1) + * + * @param ctx Entropy context + * @param value Output pointer for random double + * @return 0 on success, non-zero on error + */ +static inline int quantum_entropy_get_double( + quantum_entropy_ctx_t *ctx, + double *value +) { + if (!ctx || !value) return -1; + + uint64_t random_bits; + int err = quantum_entropy_get_bytes(ctx, (uint8_t*)&random_bits, sizeof(random_bits)); + if (err != 0) return err; + + // Convert to double in [0, 1) using 53 bits of precision + *value = (double)(random_bits >> 11) * 0x1.0p-53; + return 0; +} + +/** + * @brief Get cryptographically secure random uint64 + * + * @param ctx Entropy context + * @param value Output pointer for random uint64 + * @return 0 on success, non-zero on error + */ +static inline int quantum_entropy_get_uint64( + quantum_entropy_ctx_t *ctx, + uint64_t *value +) { + if (!ctx || !value) return -1; + return quantum_entropy_get_bytes(ctx, (uint8_t*)value, sizeof(*value)); +} + +#endif /* QUANTUM_ENTROPY_H */ \ No newline at end of file diff --git a/vendor/quantum_rng/src/quantum_rng/quantum_gates.c b/vendor/quantum_rng/src/quantum_rng/quantum_gates.c new file mode 100644 index 0000000..63cb642 --- /dev/null +++ b/vendor/quantum_rng/src/quantum_rng/quantum_gates.c @@ -0,0 +1,1022 @@ +#include "quantum_gates.h" +#include "quantum_entropy.h" +#include "quantum_constants.h" +#include "simd_ops.h" +#include +#include +#include + +#ifdef __ARM_NEON +#include +#endif + +// Use constants from quantum_constants.h +#define M_PI QC_PI +#define SQRT2_INV QC_SQRT2_INV +#define M_PI_4 QC_PI_4 +#define M_PI_2 QC_PI_2 + +// ============================================================================ +// HELPER FUNCTIONS +// ============================================================================ + +static inline int check_qubit_valid(const quantum_state_t *state, int qubit) { + return (qubit >= 0 && qubit < (int)state->num_qubits); +} + +static inline uint64_t flip_bit(uint64_t n, int bit_pos) { + return n ^ (1ULL << bit_pos); +} + +static inline int get_bit(uint64_t n, int bit_pos) { + return (n >> bit_pos) & 1ULL; +} + +// ============================================================================ +// SINGLE-QUBIT GATES +// ============================================================================ + +qs_error_t gate_pauli_x(quantum_state_t *state, int qubit) { + if (!state || !state->amplitudes) return QS_ERROR_INVALID_STATE; + if (!check_qubit_valid(state, qubit)) return QS_ERROR_INVALID_QUBIT; + + // OPTIMIZED X gate: Stride-based indexing, no get_bit() + // X gate: |0⟩ ↔ |1⟩ + const uint64_t stride = 1ULL << qubit; + const uint64_t block_size = stride << 1; + + for (uint64_t base = 0; base < state->state_dim; base += block_size) { + for (uint64_t i = 0; i < stride; i++) { + const uint64_t idx0 = base + i; + const uint64_t idx1 = idx0 + stride; + + // Swap amplitudes + const complex_t temp = state->amplitudes[idx0]; + state->amplitudes[idx0] = state->amplitudes[idx1]; + state->amplitudes[idx1] = temp; + } + } + + return QS_SUCCESS; +} + +qs_error_t gate_pauli_y(quantum_state_t *state, int qubit) { + if (!state || !state->amplitudes) return QS_ERROR_INVALID_STATE; + if (!check_qubit_valid(state, qubit)) return QS_ERROR_INVALID_QUBIT; + + // OPTIMIZED Y gate: Stride-based indexing, no get_bit() + // Y gate: |0⟩ → i|1⟩, |1⟩ → -i|0⟩ + const uint64_t stride = 1ULL << qubit; + const uint64_t block_size = stride << 1; + + for (uint64_t base = 0; base < state->state_dim; base += block_size) { + for (uint64_t i = 0; i < stride; i++) { + const uint64_t idx0 = base + i; + const uint64_t idx1 = idx0 + stride; + + const complex_t amp_0 = state->amplitudes[idx0]; + const complex_t amp_1 = state->amplitudes[idx1]; + + state->amplitudes[idx0] = -I * amp_1; // |0⟩ component + state->amplitudes[idx1] = I * amp_0; // |1⟩ component + } + } + + return QS_SUCCESS; +} + +qs_error_t gate_pauli_z(quantum_state_t *state, int qubit) { + if (!state || !state->amplitudes) return QS_ERROR_INVALID_STATE; + if (!check_qubit_valid(state, qubit)) return QS_ERROR_INVALID_QUBIT; + + // SIMD-OPTIMIZED Z gate: Vectorized negation of |1⟩ states + // Z gate: |0⟩ → |0⟩, |1⟩ → -|1⟩ + const uint64_t stride = 1ULL << qubit; + const uint64_t block_size = stride << 1; + + for (uint64_t base = 0; base < state->state_dim; base += block_size) { + // SIMD-negate all |1⟩ states in this block at once + simd_negate(&state->amplitudes[base + stride], stride); + } + + return QS_SUCCESS; +} + +qs_error_t gate_hadamard(quantum_state_t *state, int qubit) { + if (!state || !state->amplitudes) return QS_ERROR_INVALID_STATE; + if (!check_qubit_valid(state, qubit)) return QS_ERROR_INVALID_QUBIT; + + /** + * ULTRA-OPTIMIZED HADAMARD GATE (Stride-Based + Full SIMD) + * + * H gate: |0⟩ → (|0⟩ + |1⟩)/√2, |1⟩ → (|0⟩ - |1⟩)/√2 + * + * KEY OPTIMIZATION: Stride-based access eliminates bit-checking overhead + * and enables full vectorization across entire amplitude blocks. + */ + + const uint64_t stride = 1ULL << qubit; + const uint64_t block_size = stride << 1; + +#if defined(__ARM_NEON) || defined(__aarch64__) + // ARM NEON: Vectorized processing + for (uint64_t base = 0; base < state->state_dim; base += block_size) { + // Process in stride-sized chunks + for (uint64_t i = 0; i < stride; i++) { + const uint64_t idx0 = base + i; + const uint64_t idx1 = idx0 + stride; + + const complex_t amp0 = state->amplitudes[idx0]; + const complex_t amp1 = state->amplitudes[idx1]; + + state->amplitudes[idx0] = (amp0 + amp1) * QC_SQRT2_INV; + state->amplitudes[idx1] = (amp0 - amp1) * QC_SQRT2_INV; + } + } +#else + // Scalar fallback: Stride-based (still much faster than bit-checking) + for (uint64_t base = 0; base < state->state_dim; base += block_size) { + for (uint64_t i = 0; i < stride; i++) { + const uint64_t idx0 = base + i; + const uint64_t idx1 = idx0 + stride; + + const complex_t amp0 = state->amplitudes[idx0]; + const complex_t amp1 = state->amplitudes[idx1]; + + state->amplitudes[idx0] = (amp0 + amp1) * QC_SQRT2_INV; + state->amplitudes[idx1] = (amp0 - amp1) * QC_SQRT2_INV; + } + } +#endif + + return QS_SUCCESS; +} + +qs_error_t gate_s(quantum_state_t *state, int qubit) { + if (!state || !state->amplitudes) return QS_ERROR_INVALID_STATE; + if (!check_qubit_valid(state, qubit)) return QS_ERROR_INVALID_QUBIT; + + // SIMD-OPTIMIZED S gate: Vectorized multiply by i + // S gate: |0⟩ → |0⟩, |1⟩ → i|1⟩ + const uint64_t stride = 1ULL << qubit; + const uint64_t block_size = stride << 1; + + for (uint64_t base = 0; base < state->state_dim; base += block_size) { + // SIMD-multiply all |1⟩ states by i at once + simd_multiply_by_i(&state->amplitudes[base + stride], stride, 0); + } + + return QS_SUCCESS; +} + +qs_error_t gate_s_dagger(quantum_state_t *state, int qubit) { + if (!state || !state->amplitudes) return QS_ERROR_INVALID_STATE; + if (!check_qubit_valid(state, qubit)) return QS_ERROR_INVALID_QUBIT; + + // SIMD-OPTIMIZED S† gate: Vectorized multiply by -i + // S† gate: |0⟩ → |0⟩, |1⟩ → -i|1⟩ + const uint64_t stride = 1ULL << qubit; + const uint64_t block_size = stride << 1; + + for (uint64_t base = 0; base < state->state_dim; base += block_size) { + // SIMD-multiply all |1⟩ states by -i at once + simd_multiply_by_i(&state->amplitudes[base + stride], stride, 1); + } + + return QS_SUCCESS; +} + +qs_error_t gate_t(quantum_state_t *state, int qubit) { + if (!state || !state->amplitudes) return QS_ERROR_INVALID_STATE; + if (!check_qubit_valid(state, qubit)) return QS_ERROR_INVALID_QUBIT; + + // SIMD-OPTIMIZED T gate: Vectorized phase multiplication + // T gate: |0⟩ → |0⟩, |1⟩ → e^(iπ/4)|1⟩ + const complex_t phase = cexp(I * M_PI / 4.0); + const uint64_t stride = 1ULL << qubit; + const uint64_t block_size = stride << 1; + + for (uint64_t base = 0; base < state->state_dim; base += block_size) { + // SIMD-apply phase to all |1⟩ states at once + simd_apply_phase(&state->amplitudes[base + stride], phase, stride); + } + + return QS_SUCCESS; +} + +qs_error_t gate_t_dagger(quantum_state_t *state, int qubit) { + if (!state || !state->amplitudes) return QS_ERROR_INVALID_STATE; + if (!check_qubit_valid(state, qubit)) return QS_ERROR_INVALID_QUBIT; + + // SIMD-OPTIMIZED T† gate: Vectorized phase multiplication + // T† gate: |0⟩ → |0⟩, |1⟩ → e^(-iπ/4)|1⟩ + const complex_t phase = cexp(-I * M_PI / 4.0); + const uint64_t stride = 1ULL << qubit; + const uint64_t block_size = stride << 1; + + for (uint64_t base = 0; base < state->state_dim; base += block_size) { + // SIMD-apply phase to all |1⟩ states at once + simd_apply_phase(&state->amplitudes[base + stride], phase, stride); + } + + return QS_SUCCESS; +} + +qs_error_t gate_phase(quantum_state_t *state, int qubit, double theta) { + if (!state || !state->amplitudes) return QS_ERROR_INVALID_STATE; + if (!check_qubit_valid(state, qubit)) return QS_ERROR_INVALID_QUBIT; + + // SIMD-OPTIMIZED Phase gate: Vectorized phase multiplication + // Phase gate: |0⟩ → |0⟩, |1⟩ → e^(iθ)|1⟩ + const complex_t phase = cexp(I * theta); + const uint64_t stride = 1ULL << qubit; + const uint64_t block_size = stride << 1; + + for (uint64_t base = 0; base < state->state_dim; base += block_size) { + // SIMD-apply phase to all |1⟩ states at once + simd_apply_phase(&state->amplitudes[base + stride], phase, stride); + } + + return QS_SUCCESS; +} + +qs_error_t gate_rx(quantum_state_t *state, int qubit, double theta) { + if (!state || !state->amplitudes) return QS_ERROR_INVALID_STATE; + if (!check_qubit_valid(state, qubit)) return QS_ERROR_INVALID_QUBIT; + + // OPTIMIZED R_X(θ): Stride-based indexing, no get_bit() + // R_X(θ) = cos(θ/2)I - i*sin(θ/2)X + const double cos_half = cos(theta / 2.0); + const double sin_half = sin(theta / 2.0); + const uint64_t stride = 1ULL << qubit; + const uint64_t block_size = stride << 1; + + for (uint64_t base = 0; base < state->state_dim; base += block_size) { + for (uint64_t i = 0; i < stride; i++) { + const uint64_t idx0 = base + i; + const uint64_t idx1 = idx0 + stride; + + const complex_t amp_0 = state->amplitudes[idx0]; + const complex_t amp_1 = state->amplitudes[idx1]; + + state->amplitudes[idx0] = cos_half * amp_0 - I * sin_half * amp_1; + state->amplitudes[idx1] = cos_half * amp_1 - I * sin_half * amp_0; + } + } + + return QS_SUCCESS; +} + +qs_error_t gate_ry(quantum_state_t *state, int qubit, double theta) { + if (!state || !state->amplitudes) return QS_ERROR_INVALID_STATE; + if (!check_qubit_valid(state, qubit)) return QS_ERROR_INVALID_QUBIT; + + /** + * OPTIMIZED RY ROTATION (SIMD) + * RY(θ) = cos(θ/2)I - i*sin(θ/2)Y + */ + + double cos_half = cos(theta / 2.0); + double sin_half = sin(theta / 2.0); + + complex_t RY_matrix[4] = { + cos_half, -sin_half, + sin_half, cos_half + }; + + for (uint64_t i = 0; i < state->state_dim; i++) { + if (!get_bit(i, qubit)) { + uint64_t j = flip_bit(i, qubit); + + complex_t input[2] = {state->amplitudes[i], state->amplitudes[j]}; + complex_t output[2]; + + simd_matrix2x2_vec_multiply(RY_matrix, input, output); + + state->amplitudes[i] = output[0]; + state->amplitudes[j] = output[1]; + } + } + + return QS_SUCCESS; +} + +qs_error_t gate_rz(quantum_state_t *state, int qubit, double theta) { + if (!state || !state->amplitudes) return QS_ERROR_INVALID_STATE; + if (!check_qubit_valid(state, qubit)) return QS_ERROR_INVALID_QUBIT; + + // OPTIMIZED R_Z(θ): Stride-based indexing, no get_bit() + // R_Z(θ) = [e^(-iθ/2) 0; 0 e^(iθ/2)] + const complex_t phase_0 = cexp(-I * theta / 2.0); + const complex_t phase_1 = cexp(I * theta / 2.0); + const uint64_t stride = 1ULL << qubit; + const uint64_t block_size = stride << 1; + + for (uint64_t base = 0; base < state->state_dim; base += block_size) { + for (uint64_t i = 0; i < stride; i++) { + const uint64_t idx0 = base + i; + const uint64_t idx1 = idx0 + stride; + + state->amplitudes[idx0] *= phase_0; // |0⟩ component + state->amplitudes[idx1] *= phase_1; // |1⟩ component + } + } + + return QS_SUCCESS; +} + +qs_error_t gate_u3(quantum_state_t *state, int qubit, double theta, double phi, double lambda) { + if (!state || !state->amplitudes) return QS_ERROR_INVALID_STATE; + if (!check_qubit_valid(state, qubit)) return QS_ERROR_INVALID_QUBIT; + + // OPTIMIZED U3(θ,φ,λ): Stride-based indexing, no get_bit() + // U3(θ,φ,λ) - most general single-qubit unitary + // Matrix: [cos(θ/2), -e^(iλ)sin(θ/2); e^(iφ)sin(θ/2), e^(i(φ+λ))cos(θ/2)] + const double cos_half = cos(theta / 2.0); + const double sin_half = sin(theta / 2.0); + const complex_t e_phi = cexp(I * phi); + const complex_t e_lambda = cexp(I * lambda); + const complex_t e_phi_lambda = cexp(I * (phi + lambda)); + const uint64_t stride = 1ULL << qubit; + const uint64_t block_size = stride << 1; + + for (uint64_t base = 0; base < state->state_dim; base += block_size) { + for (uint64_t i = 0; i < stride; i++) { + const uint64_t idx0 = base + i; + const uint64_t idx1 = idx0 + stride; + + const complex_t amp_0 = state->amplitudes[idx0]; + const complex_t amp_1 = state->amplitudes[idx1]; + + state->amplitudes[idx0] = cos_half * amp_0 - e_lambda * sin_half * amp_1; + state->amplitudes[idx1] = e_phi * sin_half * amp_0 + e_phi_lambda * cos_half * amp_1; + } + } + + return QS_SUCCESS; +} + +// ============================================================================ +// TWO-QUBIT GATES +// ============================================================================ + +qs_error_t gate_cnot(quantum_state_t *state, int control, int target) { + if (!state || !state->amplitudes) return QS_ERROR_INVALID_STATE; + if (!check_qubit_valid(state, control)) return QS_ERROR_INVALID_QUBIT; + if (!check_qubit_valid(state, target)) return QS_ERROR_INVALID_QUBIT; + if (control == target) return QS_ERROR_INVALID_QUBIT; + + // CNOT: flip target if control is |1⟩ + for (uint64_t i = 0; i < state->state_dim; i++) { + if (get_bit(i, control) && !get_bit(i, target)) { + uint64_t j = flip_bit(i, target); + complex_t temp = state->amplitudes[i]; + state->amplitudes[i] = state->amplitudes[j]; + state->amplitudes[j] = temp; + } + } + + return QS_SUCCESS; +} + +qs_error_t gate_cz(quantum_state_t *state, int control, int target) { + if (!state || !state->amplitudes) return QS_ERROR_INVALID_STATE; + if (!check_qubit_valid(state, control)) return QS_ERROR_INVALID_QUBIT; + if (!check_qubit_valid(state, target)) return QS_ERROR_INVALID_QUBIT; + if (control == target) return QS_ERROR_INVALID_QUBIT; + + // CZ: apply phase -1 if both qubits are |1⟩ + for (uint64_t i = 0; i < state->state_dim; i++) { + if (get_bit(i, control) && get_bit(i, target)) { + state->amplitudes[i] = -state->amplitudes[i]; + } + } + + return QS_SUCCESS; +} + +qs_error_t gate_cy(quantum_state_t *state, int control, int target) { + if (!state || !state->amplitudes) return QS_ERROR_INVALID_STATE; + if (!check_qubit_valid(state, control)) return QS_ERROR_INVALID_QUBIT; + if (!check_qubit_valid(state, target)) return QS_ERROR_INVALID_QUBIT; + if (control == target) return QS_ERROR_INVALID_QUBIT; + + // CY: apply Y gate to target if control is |1⟩ + for (uint64_t i = 0; i < state->state_dim; i++) { + if (get_bit(i, control) && !get_bit(i, target)) { + uint64_t j = flip_bit(i, target); + complex_t amp_0 = state->amplitudes[i]; + complex_t amp_1 = state->amplitudes[j]; + + state->amplitudes[i] = -I * amp_1; + state->amplitudes[j] = I * amp_0; + } + } + + return QS_SUCCESS; +} + +qs_error_t gate_swap(quantum_state_t *state, int qubit1, int qubit2) { + if (!state || !state->amplitudes) return QS_ERROR_INVALID_STATE; + if (!check_qubit_valid(state, qubit1)) return QS_ERROR_INVALID_QUBIT; + if (!check_qubit_valid(state, qubit2)) return QS_ERROR_INVALID_QUBIT; + if (qubit1 == qubit2) return QS_SUCCESS; + + // SWAP: exchange states of two qubits + for (uint64_t i = 0; i < state->state_dim; i++) { + int bit1 = get_bit(i, qubit1); + int bit2 = get_bit(i, qubit2); + + if (bit1 != bit2) { + // Only process each pair once + if (bit1 > bit2) continue; + + // Swap amplitudes + uint64_t j = flip_bit(flip_bit(i, qubit1), qubit2); + complex_t temp = state->amplitudes[i]; + state->amplitudes[i] = state->amplitudes[j]; + state->amplitudes[j] = temp; + } + } + + return QS_SUCCESS; +} + +qs_error_t gate_cphase(quantum_state_t *state, int control, int target, double theta) { + if (!state || !state->amplitudes) return QS_ERROR_INVALID_STATE; + if (!check_qubit_valid(state, control)) return QS_ERROR_INVALID_QUBIT; + if (!check_qubit_valid(state, target)) return QS_ERROR_INVALID_QUBIT; + if (control == target) return QS_ERROR_INVALID_QUBIT; + + // Controlled-Phase: apply phase if control is |1⟩ + complex_t phase = cexp(I * theta); + + for (uint64_t i = 0; i < state->state_dim; i++) { + if (get_bit(i, control) && get_bit(i, target)) { + state->amplitudes[i] *= phase; + } + } + + return QS_SUCCESS; +} + +qs_error_t gate_crx(quantum_state_t *state, int control, int target, double theta) { + if (!state || !state->amplitudes) return QS_ERROR_INVALID_STATE; + if (!check_qubit_valid(state, control)) return QS_ERROR_INVALID_QUBIT; + if (!check_qubit_valid(state, target)) return QS_ERROR_INVALID_QUBIT; + if (control == target) return QS_ERROR_INVALID_QUBIT; + + // Controlled-RX: apply RX to target if control is |1⟩ + double cos_half = cos(theta / 2.0); + double sin_half = sin(theta / 2.0); + + for (uint64_t i = 0; i < state->state_dim; i++) { + if (get_bit(i, control) && !get_bit(i, target)) { + uint64_t j = flip_bit(i, target); + complex_t amp_0 = state->amplitudes[i]; + complex_t amp_1 = state->amplitudes[j]; + + state->amplitudes[i] = cos_half * amp_0 - I * sin_half * amp_1; + state->amplitudes[j] = cos_half * amp_1 - I * sin_half * amp_0; + } + } + + return QS_SUCCESS; +} + +qs_error_t gate_cry(quantum_state_t *state, int control, int target, double theta) { + if (!state || !state->amplitudes) return QS_ERROR_INVALID_STATE; + if (!check_qubit_valid(state, control)) return QS_ERROR_INVALID_QUBIT; + if (!check_qubit_valid(state, target)) return QS_ERROR_INVALID_QUBIT; + if (control == target) return QS_ERROR_INVALID_QUBIT; + + // Controlled-RY + double cos_half = cos(theta / 2.0); + double sin_half = sin(theta / 2.0); + + for (uint64_t i = 0; i < state->state_dim; i++) { + if (get_bit(i, control) && !get_bit(i, target)) { + uint64_t j = flip_bit(i, target); + complex_t amp_0 = state->amplitudes[i]; + complex_t amp_1 = state->amplitudes[j]; + + state->amplitudes[i] = cos_half * amp_0 - sin_half * amp_1; + state->amplitudes[j] = cos_half * amp_1 + sin_half * amp_0; + } + } + + return QS_SUCCESS; +} + +qs_error_t gate_crz(quantum_state_t *state, int control, int target, double theta) { + if (!state || !state->amplitudes) return QS_ERROR_INVALID_STATE; + if (!check_qubit_valid(state, control)) return QS_ERROR_INVALID_QUBIT; + if (!check_qubit_valid(state, target)) return QS_ERROR_INVALID_QUBIT; + if (control == target) return QS_ERROR_INVALID_QUBIT; + + // Controlled-RZ + complex_t phase_0 = cexp(-I * theta / 2.0); + complex_t phase_1 = cexp(I * theta / 2.0); + + for (uint64_t i = 0; i < state->state_dim; i++) { + if (get_bit(i, control)) { + if (get_bit(i, target)) { + state->amplitudes[i] *= phase_1; + } else { + state->amplitudes[i] *= phase_0; + } + } + } + + return QS_SUCCESS; +} + +// ============================================================================ +// THREE-QUBIT GATES +// ============================================================================ + +qs_error_t gate_toffoli(quantum_state_t *state, int control1, int control2, int target) { + if (!state || !state->amplitudes) return QS_ERROR_INVALID_STATE; + if (!check_qubit_valid(state, control1)) return QS_ERROR_INVALID_QUBIT; + if (!check_qubit_valid(state, control2)) return QS_ERROR_INVALID_QUBIT; + if (!check_qubit_valid(state, target)) return QS_ERROR_INVALID_QUBIT; + if (control1 == control2 || control1 == target || control2 == target) { + return QS_ERROR_INVALID_QUBIT; + } + + // Toffoli (CCNOT): flip target if both controls are |1⟩ + for (uint64_t i = 0; i < state->state_dim; i++) { + if (get_bit(i, control1) && get_bit(i, control2) && !get_bit(i, target)) { + uint64_t j = flip_bit(i, target); + complex_t temp = state->amplitudes[i]; + state->amplitudes[i] = state->amplitudes[j]; + state->amplitudes[j] = temp; + } + } + + return QS_SUCCESS; +} + +qs_error_t gate_fredkin(quantum_state_t *state, int control, int target1, int target2) { + if (!state || !state->amplitudes) return QS_ERROR_INVALID_STATE; + if (!check_qubit_valid(state, control)) return QS_ERROR_INVALID_QUBIT; + if (!check_qubit_valid(state, target1)) return QS_ERROR_INVALID_QUBIT; + if (!check_qubit_valid(state, target2)) return QS_ERROR_INVALID_QUBIT; + if (control == target1 || control == target2 || target1 == target2) { + return QS_ERROR_INVALID_QUBIT; + } + + // Fredkin (CSWAP): swap two targets if control is |1⟩ + for (uint64_t i = 0; i < state->state_dim; i++) { + if (get_bit(i, control)) { + int bit1 = get_bit(i, target1); + int bit2 = get_bit(i, target2); + + if (bit1 != bit2) { + if (bit1 > bit2) continue; // Process each pair once + + uint64_t j = flip_bit(flip_bit(i, target1), target2); + complex_t temp = state->amplitudes[i]; + state->amplitudes[i] = state->amplitudes[j]; + state->amplitudes[j] = temp; + } + } + } + + return QS_SUCCESS; +} + +// ============================================================================ +// MULTI-QUBIT GATES +// ============================================================================ + +qs_error_t gate_mcx(quantum_state_t *state, const int *controls, size_t num_controls, int target) { + if (!state || !state->amplitudes) return QS_ERROR_INVALID_STATE; + if (!controls || num_controls == 0) return QS_ERROR_INVALID_QUBIT; + if (!check_qubit_valid(state, target)) return QS_ERROR_INVALID_QUBIT; + + // Verify all control qubits are valid and unique + for (size_t i = 0; i < num_controls; i++) { + if (!check_qubit_valid(state, controls[i])) return QS_ERROR_INVALID_QUBIT; + if (controls[i] == target) return QS_ERROR_INVALID_QUBIT; + + for (size_t j = i + 1; j < num_controls; j++) { + if (controls[i] == controls[j]) return QS_ERROR_INVALID_QUBIT; + } + } + + // Multi-controlled X: flip target if all controls are |1⟩ + for (uint64_t i = 0; i < state->state_dim; i++) { + // Check if all control qubits are |1⟩ + int all_controls_one = 1; + for (size_t c = 0; c < num_controls; c++) { + if (!get_bit(i, controls[c])) { + all_controls_one = 0; + break; + } + } + + if (all_controls_one && !get_bit(i, target)) { + uint64_t j = flip_bit(i, target); + complex_t temp = state->amplitudes[i]; + state->amplitudes[i] = state->amplitudes[j]; + state->amplitudes[j] = temp; + } + } + + return QS_SUCCESS; +} + +qs_error_t gate_mcz(quantum_state_t *state, const int *controls, size_t num_controls, int target) { + if (!state || !state->amplitudes) return QS_ERROR_INVALID_STATE; + if (!controls || num_controls == 0) return QS_ERROR_INVALID_QUBIT; + if (!check_qubit_valid(state, target)) return QS_ERROR_INVALID_QUBIT; + + // Multi-controlled Z: apply phase -1 if all controls and target are |1⟩ + for (uint64_t i = 0; i < state->state_dim; i++) { + int all_one = get_bit(i, target); + + for (size_t c = 0; c < num_controls && all_one; c++) { + if (!get_bit(i, controls[c])) { + all_one = 0; + } + } + + if (all_one) { + state->amplitudes[i] = -state->amplitudes[i]; + } + } + + return QS_SUCCESS; +} + +qs_error_t gate_qft(quantum_state_t *state, const int *qubits, size_t num_qubits) { + if (!state || !state->amplitudes) return QS_ERROR_INVALID_STATE; + if (!qubits || num_qubits == 0) return QS_ERROR_INVALID_QUBIT; + + // Quantum Fourier Transform + for (size_t i = 0; i < num_qubits; i++) { + int qubit = qubits[i]; + + // Hadamard on qubit i + qs_error_t err = gate_hadamard(state, qubit); + if (err != QS_SUCCESS) return err; + + // Controlled phase rotations + for (size_t j = i + 1; j < num_qubits; j++) { + int control = qubits[j]; + double theta = M_PI / (1ULL << (j - i)); + + err = gate_cphase(state, control, qubit, theta); + if (err != QS_SUCCESS) return err; + } + } + + // Reverse qubit order (swap pairs) + for (size_t i = 0; i < num_qubits / 2; i++) { + qs_error_t err = gate_swap(state, qubits[i], qubits[num_qubits - 1 - i]); + if (err != QS_SUCCESS) return err; + } + + return QS_SUCCESS; +} + +qs_error_t gate_iqft(quantum_state_t *state, const int *qubits, size_t num_qubits) { + if (!state || !state->amplitudes) return QS_ERROR_INVALID_STATE; + if (!qubits || num_qubits == 0) return QS_ERROR_INVALID_QUBIT; + + // Inverse QFT: reverse of QFT + // Swap qubits first + for (size_t i = 0; i < num_qubits / 2; i++) { + qs_error_t err = gate_swap(state, qubits[i], qubits[num_qubits - 1 - i]); + if (err != QS_SUCCESS) return err; + } + + // Apply inverse operations in reverse order + for (int i = (int)num_qubits - 1; i >= 0; i--) { + int qubit = qubits[i]; + + // Inverse controlled phase rotations + for (int j = (int)num_qubits - 1; j > i; j--) { + int control = qubits[j]; + double theta = -M_PI / (1ULL << (j - i)); + + qs_error_t err = gate_cphase(state, control, qubit, theta); + if (err != QS_SUCCESS) return err; + } + + // Hadamard (self-inverse) + qs_error_t err = gate_hadamard(state, qubit); + if (err != QS_SUCCESS) return err; + } + + return QS_SUCCESS; +} + +// ============================================================================ +// MEASUREMENT +// ============================================================================ + +measurement_result_t quantum_measure( + quantum_state_t *state, + int qubit, + measurement_basis_t basis, + quantum_entropy_ctx_t *entropy +) { + measurement_result_t result = {0, 0.0, 0.0}; + + if (!state || !state->amplitudes || !check_qubit_valid(state, qubit)) { + return result; + } + + if (!entropy) { + // CRITICAL ERROR: No entropy source provided + return result; + } + + // Transform to measurement basis if needed + if (basis == MEASURE_HADAMARD) { + gate_hadamard(state, qubit); // Transform to X-basis + } else if (basis == MEASURE_CIRCULAR) { + gate_s_dagger(state, qubit); // Transform to Y-basis + gate_hadamard(state, qubit); + } + + // SIMD-OPTIMIZED probability calculation: Stride-based summation + double prob_0 = 0.0; + double prob_1 = 0.0; + + const uint64_t stride = 1ULL << qubit; + const uint64_t block_size = stride << 1; + + for (uint64_t base = 0; base < state->state_dim; base += block_size) { + // Sum probabilities for |0⟩ states in this block + for (uint64_t i = 0; i < stride; i++) { + const double mag = cabs(state->amplitudes[base + i]); + prob_0 += mag * mag; + } + + // Sum probabilities for |1⟩ states in this block + for (uint64_t i = 0; i < stride; i++) { + const double mag = cabs(state->amplitudes[base + stride + i]); + prob_1 += mag * mag; + } + } + + // Determine outcome using cryptographically secure random number + double random; + if (quantum_entropy_get_double(entropy, &random) != 0) { + // Entropy failure - cannot perform measurement + return result; + } + result.outcome = (random < prob_0) ? 0 : 1; + result.probability = (result.outcome == 0) ? prob_0 : prob_1; + + // Calculate measurement entropy + if (prob_0 > 1e-15) result.entropy -= prob_0 * log2(prob_0); + if (prob_1 > 1e-15) result.entropy -= prob_1 * log2(prob_1); + + // SIMD-OPTIMIZED wavefunction collapse: Stride-based zeroing + double norm = 0.0; + + const uint64_t collapse_stride = 1ULL << qubit; + const uint64_t collapse_block = collapse_stride << 1; + + if (result.outcome == 0) { + // Zero all |1⟩ states, keep |0⟩ states + for (uint64_t base = 0; base < state->state_dim; base += collapse_block) { + // Zero |1⟩ components + memset(&state->amplitudes[base + collapse_stride], 0, collapse_stride * sizeof(complex_t)); + + // Calculate norm from |0⟩ components + for (uint64_t i = 0; i < collapse_stride; i++) { + const double mag = cabs(state->amplitudes[base + i]); + norm += mag * mag; + } + } + } else { + // Zero all |0⟩ states, keep |1⟩ states + for (uint64_t base = 0; base < state->state_dim; base += collapse_block) { + // Zero |0⟩ components + memset(&state->amplitudes[base], 0, collapse_stride * sizeof(complex_t)); + + // Calculate norm from |1⟩ components + for (uint64_t i = 0; i < collapse_stride; i++) { + const double mag = cabs(state->amplitudes[base + collapse_stride + i]); + norm += mag * mag; + } + } + } + + // Renormalize + if (norm > 1e-15) { + norm = sqrt(norm); + for (uint64_t i = 0; i < state->state_dim; i++) { + state->amplitudes[i] /= norm; + } + } + + // Transform back if needed + if (basis == MEASURE_HADAMARD) { + gate_hadamard(state, qubit); + } else if (basis == MEASURE_CIRCULAR) { + gate_hadamard(state, qubit); + gate_s(state, qubit); + } + + // Record measurement + quantum_state_record_measurement(state, result.outcome); + + return result; +} + +qs_error_t quantum_measure_multi( + quantum_state_t *state, + const int *qubits, + size_t num_qubits, + int *outcomes, + quantum_entropy_ctx_t *entropy +) { + if (!state || !qubits || !outcomes) return QS_ERROR_INVALID_STATE; + if (!entropy) return QS_ERROR_INVALID_STATE; + + for (size_t i = 0; i < num_qubits; i++) { + measurement_result_t result = quantum_measure(state, qubits[i], MEASURE_COMPUTATIONAL, entropy); + outcomes[i] = result.outcome; + } + + return QS_SUCCESS; +} + +double quantum_peek_probability( + const quantum_state_t *state, + int qubit, + int outcome +) { + if (!state || !state->amplitudes || !check_qubit_valid(state, qubit)) { + return 0.0; + } + + double prob = 0.0; + for (uint64_t i = 0; i < state->state_dim; i++) { + if (get_bit(i, qubit) == outcome) { + double amp = cabs(state->amplitudes[i]); + prob += amp * amp; + } + } + + return prob; +} + +/** + * @brief PERFORMANCE-CRITICAL: Fast batch measurement of all qubits + * + * Instead of measuring each qubit separately (8 scans of 256 elements = 2048 accesses), + * sample the complete basis state in ONE pass (256 accesses). + * + * This gives 8x speedup for measurement-heavy workloads! + */ +uint64_t quantum_measure_all_fast( + quantum_state_t *state, + quantum_entropy_ctx_t *entropy +) { + if (!state || !state->amplitudes || !entropy) { + return 0; + } + + // Get random number for sampling + double random; + if (quantum_entropy_get_double(entropy, &random) != 0) { + return 0; + } + + // Sample from probability distribution in ONE pass + double cumulative = 0.0; + for (uint64_t i = 0; i < state->state_dim; i++) { + const double mag = cabs(state->amplitudes[i]); + const double prob = mag * mag; + cumulative += prob; + + if (random < cumulative) { + // Collapse to this basis state + // Zero all other amplitudes + memset(state->amplitudes, 0, state->state_dim * sizeof(complex_t)); + state->amplitudes[i] = 1.0; // Collapsed state + + // Record measurement + quantum_state_record_measurement(state, i); + + return i; // Return the measured basis state + } + } + + // Numerical precision fallback + uint64_t final_state = state->state_dim - 1; + memset(state->amplitudes, 0, state->state_dim * sizeof(complex_t)); + state->amplitudes[final_state] = 1.0; + quantum_state_record_measurement(state, final_state); + + return final_state; +} + +// ============================================================================ +// UTILITY FUNCTIONS +// ============================================================================ + +qs_error_t apply_single_qubit_gate( + quantum_state_t *state, + int qubit, + const complex_t matrix[2][2] +) { + if (!state || !state->amplitudes) return QS_ERROR_INVALID_STATE; + if (!check_qubit_valid(state, qubit)) return QS_ERROR_INVALID_QUBIT; + + for (uint64_t i = 0; i < state->state_dim; i++) { + if (!get_bit(i, qubit)) { + uint64_t j = flip_bit(i, qubit); + complex_t amp_0 = state->amplitudes[i]; + complex_t amp_1 = state->amplitudes[j]; + + state->amplitudes[i] = matrix[0][0] * amp_0 + matrix[0][1] * amp_1; + state->amplitudes[j] = matrix[1][0] * amp_0 + matrix[1][1] * amp_1; + } + } + + return QS_SUCCESS; +} + +qs_error_t apply_two_qubit_gate( + quantum_state_t *state, + int qubit1, + int qubit2, + const complex_t matrix[4][4] +) { + if (!state || !state->amplitudes) return QS_ERROR_INVALID_STATE; + if (!check_qubit_valid(state, qubit1)) return QS_ERROR_INVALID_QUBIT; + if (!check_qubit_valid(state, qubit2)) return QS_ERROR_INVALID_QUBIT; + if (qubit1 == qubit2) return QS_ERROR_INVALID_QUBIT; + + // Full production implementation of arbitrary two-qubit unitary + // The matrix operates on the joint state of qubits in basis: |00⟩, |01⟩, |10⟩, |11⟩ + + int low_qubit = (qubit1 < qubit2) ? qubit1 : qubit2; + int high_qubit = (qubit1 < qubit2) ? qubit2 : qubit1; + int swapped = (qubit1 > qubit2); + + for (uint64_t i = 0; i < state->state_dim; i++) { + int bit_low = get_bit(i, low_qubit); + int bit_high = get_bit(i, high_qubit); + + // Only process states where we're at the "first" of the four related states + if (bit_low == 0 && bit_high == 0) { + // Get the four basis state indices: |00⟩, |01⟩, |10⟩, |11⟩ + uint64_t idx_00 = i; + uint64_t idx_01 = flip_bit(i, low_qubit); + uint64_t idx_10 = flip_bit(i, high_qubit); + uint64_t idx_11 = flip_bit(flip_bit(i, low_qubit), high_qubit); + + // Get current amplitudes + complex_t amp[4]; + if (swapped) { + // If qubits were swapped, adjust indexing + amp[0] = state->amplitudes[idx_00]; // |00⟩ + amp[1] = state->amplitudes[idx_10]; // |10⟩ (swapped to |01⟩) + amp[2] = state->amplitudes[idx_01]; // |01⟩ (swapped to |10⟩) + amp[3] = state->amplitudes[idx_11]; // |11⟩ + } else { + amp[0] = state->amplitudes[idx_00]; // |00⟩ + amp[1] = state->amplitudes[idx_01]; // |01⟩ + amp[2] = state->amplitudes[idx_10]; // |10⟩ + amp[3] = state->amplitudes[idx_11]; // |11⟩ + } + + // Apply the unitary transformation + complex_t new_amp[4]; + for (int row = 0; row < 4; row++) { + new_amp[row] = 0.0; + for (int col = 0; col < 4; col++) { + new_amp[row] += matrix[row][col] * amp[col]; + } + } + + // Write back the transformed amplitudes + if (swapped) { + state->amplitudes[idx_00] = new_amp[0]; + state->amplitudes[idx_10] = new_amp[1]; + state->amplitudes[idx_01] = new_amp[2]; + state->amplitudes[idx_11] = new_amp[3]; + } else { + state->amplitudes[idx_00] = new_amp[0]; + state->amplitudes[idx_01] = new_amp[1]; + state->amplitudes[idx_10] = new_amp[2]; + state->amplitudes[idx_11] = new_amp[3]; + } + } + } + + return QS_SUCCESS; +} + +int verify_gate_normalization(const quantum_state_t *state) { + return quantum_state_is_normalized(state, 1e-10); +} \ No newline at end of file diff --git a/vendor/quantum_rng/src/quantum_rng/quantum_gates.h b/vendor/quantum_rng/src/quantum_rng/quantum_gates.h new file mode 100644 index 0000000..f04a424 --- /dev/null +++ b/vendor/quantum_rng/src/quantum_rng/quantum_gates.h @@ -0,0 +1,377 @@ +#ifndef QUANTUM_GATES_H +#define QUANTUM_GATES_H + +#include "quantum_state.h" +#include "quantum_entropy.h" +#include + +/** + * @file quantum_gates.h + * @brief Complete universal quantum gate set + * + * Implements all gates needed for universal quantum computation: + * - Single-qubit gates: Pauli, Hadamard, Phase, Rotation + * - Two-qubit gates: CNOT, CZ, SWAP, Controlled-Phase + * - Three-qubit gates: Toffoli, Fredkin + * - Multi-qubit gates: Multi-controlled gates, QFT + * + * All gates preserve state normalization and properly handle + * quantum superposition, entanglement, and phase. + */ + +// ============================================================================ +// SINGLE-QUBIT GATES +// ============================================================================ + +/** + * @brief Pauli-X gate (NOT gate, bit flip) + * |0⟩ → |1⟩, |1⟩ → |0⟩ + * Matrix: [0 1; 1 0] + */ +qs_error_t gate_pauli_x(quantum_state_t *state, int qubit); + +/** + * @brief Pauli-Y gate (bit and phase flip) + * |0⟩ → i|1⟩, |1⟩ → -i|0⟩ + * Matrix: [0 -i; i 0] + */ +qs_error_t gate_pauli_y(quantum_state_t *state, int qubit); + +/** + * @brief Pauli-Z gate (phase flip) + * |0⟩ → |0⟩, |1⟩ → -|1⟩ + * Matrix: [1 0; 0 -1] + */ +qs_error_t gate_pauli_z(quantum_state_t *state, int qubit); + +/** + * @brief Hadamard gate (creates superposition) + * |0⟩ → (|0⟩ + |1⟩)/√2 + * |1⟩ → (|0⟩ - |1⟩)/√2 + * Matrix: [1 1; 1 -1]/√2 + */ +qs_error_t gate_hadamard(quantum_state_t *state, int qubit); + +/** + * @brief S gate (Phase gate, √Z) + * |0⟩ → |0⟩, |1⟩ → i|1⟩ + * Matrix: [1 0; 0 i] + */ +qs_error_t gate_s(quantum_state_t *state, int qubit); + +/** + * @brief S† gate (Inverse S gate) + * |0⟩ → |0⟩, |1⟩ → -i|1⟩ + * Matrix: [1 0; 0 -i] + */ +qs_error_t gate_s_dagger(quantum_state_t *state, int qubit); + +/** + * @brief T gate (π/8 gate, √S) + * |0⟩ → |0⟩, |1⟩ → e^(iπ/4)|1⟩ + * Matrix: [1 0; 0 e^(iπ/4)] + */ +qs_error_t gate_t(quantum_state_t *state, int qubit); + +/** + * @brief T† gate (Inverse T gate) + * |0⟩ → |0⟩, |1⟩ → e^(-iπ/4)|1⟩ + * Matrix: [1 0; 0 e^(-iπ/4)] + */ +qs_error_t gate_t_dagger(quantum_state_t *state, int qubit); + +/** + * @brief Arbitrary phase gate + * |0⟩ → |0⟩, |1⟩ → e^(iθ)|1⟩ + * @param theta Phase angle in radians + */ +qs_error_t gate_phase(quantum_state_t *state, int qubit, double theta); + +/** + * @brief Rotation around X axis + * R_X(θ) = exp(-iθX/2) = cos(θ/2)I - i*sin(θ/2)X + * @param theta Rotation angle in radians + */ +qs_error_t gate_rx(quantum_state_t *state, int qubit, double theta); + +/** + * @brief Rotation around Y axis + * R_Y(θ) = exp(-iθY/2) = cos(θ/2)I - i*sin(θ/2)Y + * @param theta Rotation angle in radians + */ +qs_error_t gate_ry(quantum_state_t *state, int qubit, double theta); + +/** + * @brief Rotation around Z axis + * R_Z(θ) = exp(-iθZ/2) = [e^(-iθ/2) 0; 0 e^(iθ/2)] + * @param theta Rotation angle in radians + */ +qs_error_t gate_rz(quantum_state_t *state, int qubit, double theta); + +/** + * @brief Arbitrary single-qubit unitary U3 gate + * U3(θ,φ,λ) - most general single-qubit gate + * @param theta Rotation angle + * @param phi Phase angle + * @param lambda Global phase + */ +qs_error_t gate_u3(quantum_state_t *state, int qubit, double theta, double phi, double lambda); + +// ============================================================================ +// TWO-QUBIT GATES +// ============================================================================ + +/** + * @brief CNOT gate (Controlled-NOT) + * Flips target if control is |1⟩ + * Creates entanglement + * @param control Control qubit index + * @param target Target qubit index + */ +qs_error_t gate_cnot(quantum_state_t *state, int control, int target); + +/** + * @brief CZ gate (Controlled-Z) + * Applies Z to target if control is |1⟩ + * @param control Control qubit index + * @param target Target qubit index + */ +qs_error_t gate_cz(quantum_state_t *state, int control, int target); + +/** + * @brief CY gate (Controlled-Y) + * Applies Y to target if control is |1⟩ + * @param control Control qubit index + * @param target Target qubit index + */ +qs_error_t gate_cy(quantum_state_t *state, int control, int target); + +/** + * @brief SWAP gate + * Swaps the states of two qubits + * @param qubit1 First qubit index + * @param qubit2 Second qubit index + */ +qs_error_t gate_swap(quantum_state_t *state, int qubit1, int qubit2); + +/** + * @brief Controlled-Phase gate + * Applies phase if control is |1⟩ + * @param control Control qubit index + * @param target Target qubit index + * @param theta Phase angle + */ +qs_error_t gate_cphase(quantum_state_t *state, int control, int target, double theta); + +/** + * @brief Controlled-Rotation X gate + * @param control Control qubit index + * @param target Target qubit index + * @param theta Rotation angle + */ +qs_error_t gate_crx(quantum_state_t *state, int control, int target, double theta); + +/** + * @brief Controlled-Rotation Y gate + * @param control Control qubit index + * @param target Target qubit index + * @param theta Rotation angle + */ +qs_error_t gate_cry(quantum_state_t *state, int control, int target, double theta); + +/** + * @brief Controlled-Rotation Z gate + * @param control Control qubit index + * @param target Target qubit index + * @param theta Rotation angle + */ +qs_error_t gate_crz(quantum_state_t *state, int control, int target, double theta); + +// ============================================================================ +// THREE-QUBIT GATES +// ============================================================================ + +/** + * @brief Toffoli gate (CCNOT, Controlled-Controlled-NOT) + * Flips target if both controls are |1⟩ + * Universal for classical computation + * @param control1 First control qubit + * @param control2 Second control qubit + * @param target Target qubit + */ +qs_error_t gate_toffoli(quantum_state_t *state, int control1, int control2, int target); + +/** + * @brief Fredkin gate (CSWAP, Controlled-SWAP) + * Swaps two targets if control is |1⟩ + * @param control Control qubit + * @param target1 First target qubit + * @param target2 Second target qubit + */ +qs_error_t gate_fredkin(quantum_state_t *state, int control, int target1, int target2); + +// ============================================================================ +// MULTI-QUBIT GATES +// ============================================================================ + +/** + * @brief Multi-controlled X gate (generalized Toffoli) + * Applies X to target if all controls are |1⟩ + * @param controls Array of control qubit indices + * @param num_controls Number of control qubits + * @param target Target qubit index + */ +qs_error_t gate_mcx(quantum_state_t *state, const int *controls, size_t num_controls, int target); + +/** + * @brief Multi-controlled Z gate + * Applies Z to target if all controls are |1⟩ + * @param controls Array of control qubit indices + * @param num_controls Number of control qubits + * @param target Target qubit index + */ +qs_error_t gate_mcz(quantum_state_t *state, const int *controls, size_t num_controls, int target); + +/** + * @brief Quantum Fourier Transform + * Implements QFT on specified qubits + * Critical for Shor's algorithm and phase estimation + * @param qubits Array of qubit indices to transform + * @param num_qubits Number of qubits + */ +qs_error_t gate_qft(quantum_state_t *state, const int *qubits, size_t num_qubits); + +/** + * @brief Inverse Quantum Fourier Transform + * @param qubits Array of qubit indices + * @param num_qubits Number of qubits + */ +qs_error_t gate_iqft(quantum_state_t *state, const int *qubits, size_t num_qubits); + +// ============================================================================ +// MEASUREMENT +// ============================================================================ + +/** + * @brief Measurement basis types + */ +typedef enum { + MEASURE_COMPUTATIONAL, // Z-basis: |0⟩, |1⟩ + MEASURE_HADAMARD, // X-basis: |+⟩, |-⟩ + MEASURE_CIRCULAR, // Y-basis: |↻⟩, |↺⟩ + MEASURE_CUSTOM // Custom basis +} measurement_basis_t; + +/** + * @brief Measurement result + */ +typedef struct { + int outcome; // 0 or 1 + double probability; // P(outcome) + double entropy; // Measurement entropy +} measurement_result_t; + +/** + * @brief Measure single qubit + * Collapses wavefunction to measurement outcome + * + * SECURITY: Uses cryptographically secure entropy for measurement simulation. + * The entropy source must be unpredictable to maintain security properties. + * + * @param state Quantum state + * @param qubit Qubit to measure + * @param basis Measurement basis + * @param entropy Secure entropy source for measurement sampling + * @return Measurement result + */ +measurement_result_t quantum_measure( + quantum_state_t *state, + int qubit, + measurement_basis_t basis, + quantum_entropy_ctx_t *entropy +); + +/** + * @brief Measure multiple qubits + * @param state Quantum state + * @param qubits Array of qubit indices + * @param num_qubits Number of qubits to measure + * @param outcomes Output array for outcomes + * @param entropy Secure entropy source + * @return QS_SUCCESS or error + */ +qs_error_t quantum_measure_multi( + quantum_state_t *state, + const int *qubits, + size_t num_qubits, + int *outcomes, + quantum_entropy_ctx_t *entropy +); + +/** + * @brief Non-destructive measurement (peek) + * Gets probability without collapsing state + * @param state Quantum state + * @param qubit Qubit to peek + * @param outcome Outcome to check (0 or 1) + * @return Probability of outcome + */ +double quantum_peek_probability( + const quantum_state_t *state, + int qubit, + int outcome +); + +/** + * @brief PERFORMANCE-CRITICAL: Fast batch measurement of all qubits + * + * Measures entire basis state in ONE pass instead of measuring qubits separately. + * 8x faster for 8-qubit systems! + * + * @param state Quantum state (will be collapsed) + * @param entropy Secure entropy source for sampling + * @return Measured basis state index (contains all qubit outcomes) + */ +uint64_t quantum_measure_all_fast( + quantum_state_t *state, + quantum_entropy_ctx_t *entropy +); + +// ============================================================================ +// UTILITY FUNCTIONS +// ============================================================================ + +/** + * @brief Apply arbitrary single-qubit unitary + * @param state Quantum state + * @param qubit Target qubit + * @param matrix 2x2 unitary matrix + */ +qs_error_t apply_single_qubit_gate( + quantum_state_t *state, + int qubit, + const complex_t matrix[2][2] +); + +/** + * @brief Apply arbitrary two-qubit unitary + * @param state Quantum state + * @param qubit1 First qubit + * @param qubit2 Second qubit + * @param matrix 4x4 unitary matrix + */ +qs_error_t apply_two_qubit_gate( + quantum_state_t *state, + int qubit1, + int qubit2, + const complex_t matrix[4][4] +); + +/** + * @brief Check if gate preserves normalization + * @param state Quantum state (after gate application) + * @return 1 if normalized, 0 otherwise + */ +int verify_gate_normalization(const quantum_state_t *state); + +#endif /* QUANTUM_GATES_H */ \ No newline at end of file diff --git a/src/quantum_rng/quantum_rng.c b/vendor/quantum_rng/src/quantum_rng/quantum_rng.c similarity index 79% rename from src/quantum_rng/quantum_rng.c rename to vendor/quantum_rng/src/quantum_rng/quantum_rng.c index d711a9e..6146323 100644 --- a/src/quantum_rng/quantum_rng.c +++ b/vendor/quantum_rng/src/quantum_rng/quantum_rng.c @@ -1,4 +1,5 @@ #include "quantum_rng.h" +#include "../common/secure_memory.h" #include #include #include @@ -28,6 +29,7 @@ static inline uint64_t splitmix64(uint64_t x); static inline uint64_t hadamard_mix(uint64_t x); static uint64_t get_system_entropy(void); static uint64_t get_runtime_entropy(qrng_ctx *ctx); +static uint64_t absorb_seed(const uint8_t *seed, size_t seed_len); static inline uint64_t hadamard_gate(uint64_t x); static inline uint64_t phase_gate(uint64_t x, uint64_t angle); static uint64_t measure_state(qrng_ctx *ctx, double quantum_state, uint64_t last); @@ -101,6 +103,22 @@ static uint64_t get_system_entropy(void) { return entropy; } +// Deterministic seed absorption. +// +// Folds the ENTIRE seed (every byte) together with the seed length into a +// single 64-bit value with good diffusion. Used to derive the seeded-mode +// state purely from the caller-provided seed, so that identical seeds yield +// byte-identical streams while different seeds (including seeds that differ +// only in trailing bytes or in length) yield different streams. +static uint64_t absorb_seed(const uint8_t *seed, size_t seed_len) { + uint64_t h = QRNG_SQRT2 ^ splitmix64((uint64_t)seed_len + QRNG_GOLDEN_RATIO); + for (size_t i = 0; i < seed_len; i++) { + h ^= (uint64_t)seed[i] + QRNG_GOLDEN_RATIO + (h << 6) + (h >> 2); + h = hadamard_mix(h); + } + return h ^ splitmix64((uint64_t)seed_len); +} + // Enhanced runtime entropy collection static uint64_t get_runtime_entropy(qrng_ctx *ctx) { struct timeval tv; @@ -153,8 +171,8 @@ static inline uint64_t phase_gate(uint64_t x, uint64_t angle) { // Enhanced measurement function with improved entropy collection static uint64_t measure_state(qrng_ctx *ctx, double quantum_state, uint64_t last) { - // Update runtime entropy - ctx->runtime_entropy = get_runtime_entropy(ctx); + // Update runtime entropy (skipped in seeded mode for a reproducible stream) + if (!ctx->seeded) ctx->runtime_entropy = get_runtime_entropy(ctx); volatile double collapsed = quantum_noise(quantum_state + (double)ctx->runtime_entropy / UINT64_MAX); @@ -190,10 +208,10 @@ static void quantum_step(qrng_ctx *ctx) { ctx->counter++; uint64_t mixer = splitmix64(ctx->counter * QRNG_GOLDEN_RATIO); - - // Update runtime entropy - ctx->runtime_entropy = get_runtime_entropy(ctx); - + + // Update runtime entropy (skipped in seeded mode for a reproducible stream) + if (!ctx->seeded) ctx->runtime_entropy = get_runtime_entropy(ctx); + // Enhanced mixing rounds with improved quantum gates for (int round = 0; round < QRNG_MIXING_ROUNDS; round++) { mixer = hadamard_mix(mixer ^ ctx->pool_mixer ^ ctx->runtime_entropy); @@ -260,14 +278,29 @@ qrng_error qrng_init(qrng_ctx **ctx, const uint8_t *seed, size_t seed_len) { *ctx = calloc(1, sizeof(qrng_ctx)); if (!*ctx) return QRNG_ERROR_NULL_CONTEXT; - - // Initialize context with system-specific entropy - gettimeofday(&(*ctx)->init_time, NULL); - (*ctx)->pid = getpid(); - (*ctx)->system_entropy = get_system_entropy(); + + // A caller-provided seed selects deterministic (reproducible) mode: the + // entire output stream becomes a pure function of the seed, with NO + // per-call or per-init wall-clock/pid/rdtsc entropy folded in. Without a + // seed we retain the original non-deterministic behavior, drawing + // system/runtime entropy once here (and refreshing it per call below). + (*ctx)->seeded = (seed != NULL && seed_len >= 1) ? 1 : 0; + + if ((*ctx)->seeded) { + // Deterministic: derive all base entropy from the full seed only. + // init_time and pid remain zero (from calloc) so they contribute + // nothing non-deterministic; runtime_entropy stays zero throughout. + (*ctx)->system_entropy = absorb_seed(seed, seed_len); + } else { + // Non-deterministic: seed the state from OS/runtime entropy once. + gettimeofday(&(*ctx)->init_time, NULL); + (*ctx)->pid = getpid(); + (*ctx)->system_entropy = get_system_entropy(); + } (*ctx)->unique_id = splitmix64((*ctx)->system_entropy); (*ctx)->pool_mixer = QRNG_HEISENBERG ^ (*ctx)->unique_id; - (*ctx)->runtime_entropy = get_runtime_entropy(*ctx); + // runtime_entropy stays 0 in seeded mode (reproducible); drawn once here otherwise. + if (!(*ctx)->seeded) (*ctx)->runtime_entropy = get_runtime_entropy(*ctx); // Initialize entropy pool with multiple sources for (int i = 0; i < 16; i++) { @@ -317,7 +350,7 @@ qrng_error qrng_init(qrng_ctx **ctx, const uint8_t *seed, size_t seed_len) { void qrng_free(qrng_ctx *ctx) { if (ctx) { - memset(ctx, 0, sizeof(*ctx)); + secure_memzero(ctx, sizeof(*ctx)); free(ctx); } } @@ -327,10 +360,14 @@ qrng_error qrng_reseed(qrng_ctx *ctx, const uint8_t *seed, size_t seed_len) { if (!seed && seed_len > 0) return QRNG_ERROR_NULL_BUFFER; if (seed_len == 0) return QRNG_ERROR_INVALID_LENGTH; - // Update runtime entropy - ctx->runtime_entropy = get_runtime_entropy(ctx); - - uint64_t mixer = QRNG_GOLDEN_RATIO ^ ctx->runtime_entropy; + // Update runtime entropy (skipped in seeded mode for a reproducible stream) + if (!ctx->seeded) ctx->runtime_entropy = get_runtime_entropy(ctx); + + // Fold the ENTIRE seed (all bytes + length) into the mixer so the reseed + // depends on every seed byte, not just the first QRNG_NUM_QUBITS. Reseed + // stays a deterministic function of (prior state, seed). + uint64_t seed_digest = absorb_seed(seed, seed_len); + uint64_t mixer = QRNG_GOLDEN_RATIO ^ ctx->runtime_entropy ^ seed_digest; for (size_t i = 0; i < seed_len && i < QRNG_NUM_QUBITS; i++) { mixer = splitmix64(mixer ^ seed[i] ^ ctx->runtime_entropy); ctx->phase[i] = hadamard_gate(ctx->phase[i] ^ seed[i] ^ mixer ^ @@ -380,8 +417,8 @@ uint64_t qrng_uint64(qrng_ctx *ctx) { uint64_t result; qrng_bytes(ctx, (uint8_t*)&result, sizeof(result)); - // Enhanced output mixing with runtime entropy - ctx->runtime_entropy = get_runtime_entropy(ctx); + // Enhanced output mixing with runtime entropy (skipped in seeded mode) + if (!ctx->seeded) ctx->runtime_entropy = get_runtime_entropy(ctx); result = splitmix64(result ^ ctx->runtime_entropy); result ^= QRNG_PAULI_X * (result >> 27); result *= QRNG_HEISENBERG; @@ -402,19 +439,27 @@ int32_t qrng_range32(qrng_ctx *ctx, int32_t min, int32_t max) { return max; } - uint32_t range = (uint32_t)(max - min + 1); - if (range == 0) { - return max; + /* Span in [1, 2^32]. Compute in 64-bit signed so (max - min + 1) can never + overflow: the naive int32 form is signed-overflow UB at the full span, + and at -O2 the compiler then proves range != 0 and deletes the + (range == 0) guard below, leaving r % 0 (SIGFPE). */ + uint64_t span = (uint64_t)((int64_t)max - (int64_t)min) + 1; + + if (span > 0xFFFFFFFFULL) { + /* Full 32-bit span: every uint32 is valid; no rejection or modulo. */ + return (int32_t)((uint32_t)min + (uint32_t)qrng_uint64(ctx)); } - - uint32_t threshold = (uint32_t)-range % range; + + uint32_t range = (uint32_t)span; /* guaranteed nonzero */ + uint32_t threshold = (0u - range) % range; /* == 2^32 % range, unbiased */ uint32_t r; - + do { r = (uint32_t)qrng_uint64(ctx); } while (r < threshold); - - return min + (r % range); + + /* Unsigned add avoids signed-overflow UB when min < 0. */ + return (int32_t)((uint32_t)min + (r % range)); } uint64_t qrng_range64(qrng_ctx *ctx, uint64_t min, uint64_t max) { @@ -428,16 +473,18 @@ uint64_t qrng_range64(qrng_ctx *ctx, uint64_t min, uint64_t max) { uint64_t range = max - min + 1; if (range == 0) { - return max; + /* range wrapped => full 2^64 span (min==0, max==UINT64_MAX): every + uint64 is valid. Return a raw draw, not the constant max. */ + return qrng_uint64(ctx); } - - uint64_t threshold = -range % range; + + uint64_t threshold = (0uLL - range) % range; uint64_t r; - + do { r = qrng_uint64(ctx); } while (r < threshold); - + return min + (r % range); } @@ -450,8 +497,8 @@ double qrng_get_entropy_estimate(qrng_ctx *ctx) { entropy += -log2(ctx->entropy_pool[i] + 1e-10); } - // Include runtime entropy in the estimate - ctx->runtime_entropy = get_runtime_entropy(ctx); + // Include runtime entropy in the estimate (skipped in seeded mode) + if (!ctx->seeded) ctx->runtime_entropy = get_runtime_entropy(ctx); entropy += -log2((double)(ctx->runtime_entropy & 0xFF) / 256.0 + 1e-10); return entropy / 17.0; // Average over all sources @@ -462,8 +509,8 @@ qrng_error qrng_entangle_states(qrng_ctx *ctx, uint8_t *state1, uint8_t *state2, if (!state1 || !state2) return QRNG_ERROR_NULL_BUFFER; if (len == 0) return QRNG_ERROR_INVALID_LENGTH; - // Update runtime entropy - ctx->runtime_entropy = get_runtime_entropy(ctx); + // Update runtime entropy (skipped in seeded mode for a reproducible stream) + if (!ctx->seeded) ctx->runtime_entropy = get_runtime_entropy(ctx); // Create entanglement between states using quantum gates uint64_t mixer = splitmix64(ctx->counter * QRNG_GOLDEN_RATIO); @@ -500,8 +547,8 @@ qrng_error qrng_measure_state(qrng_ctx *ctx, uint8_t *state, size_t len) { if (!state) return QRNG_ERROR_NULL_BUFFER; if (len == 0) return QRNG_ERROR_INVALID_LENGTH; - // Update runtime entropy - ctx->runtime_entropy = get_runtime_entropy(ctx); + // Update runtime entropy (skipped in seeded mode for a reproducible stream) + if (!ctx->seeded) ctx->runtime_entropy = get_runtime_entropy(ctx); // Measure each byte of the state uint64_t mixer = splitmix64(ctx->counter * QRNG_GOLDEN_RATIO); diff --git a/src/quantum_rng/quantum_rng.h b/vendor/quantum_rng/src/quantum_rng/quantum_rng.h similarity index 85% rename from src/quantum_rng/quantum_rng.h rename to vendor/quantum_rng/src/quantum_rng/quantum_rng.h index 54b90d6..62535c7 100644 --- a/src/quantum_rng/quantum_rng.h +++ b/vendor/quantum_rng/src/quantum_rng/quantum_rng.h @@ -8,12 +8,22 @@ /** * @file quantum_rng.h - * @brief Quantum-inspired random number generator + * @brief Quantum-mixing random number generator core * - * This library implements a high-performance random number generator that leverages - * quantum computing principles on classical hardware. It uses quantum circuit simulation, - * floating-point uncertainty, and quantum entanglement to achieve high-quality - * non-deterministic random numbers. + * A high-performance random number generator that mixes state through + * quantum-circuit-inspired transformations on classical hardware. It is the + * low-level core beneath the secure_rng layer. + * + * Determinism contract: + * - qrng_init with a seed (seed != NULL, seed_len >= 1): output is a pure, + * reproducible function of the seed. The same seed always produces the same + * stream. No wall-clock/PID/cycle-counter entropy is mixed in. + * - qrng_init unseeded (seed == NULL): the state is seeded from system entropy + * at initialization, producing a non-deterministic stream. + * + * For genuine quantum-state-vector simulation and CHSH Bell verification see + * quantum_state.h / bell_test.h; for the production CSPRNG (hardware entropy, + * NIST SP 800-90B health tests, reseeding) see secure_rng.h. */ /** @@ -59,6 +69,7 @@ typedef struct qrng_ctx_t { uint64_t unique_id; uint64_t system_entropy; uint64_t runtime_entropy; + int seeded; /**< 1 if initialized from a caller-provided seed (deterministic stream) */ } qrng_ctx; /** diff --git a/vendor/quantum_rng/src/quantum_rng/quantum_rng_v3.c b/vendor/quantum_rng/src/quantum_rng/quantum_rng_v3.c new file mode 100644 index 0000000..52b76cd --- /dev/null +++ b/vendor/quantum_rng/src/quantum_rng/quantum_rng_v3.c @@ -0,0 +1,1176 @@ +#include "quantum_rng_v3.h" +#include "quantum_constants.h" +#include "simd_ops.h" +#include "../common/secure_memory.h" +#include "../common/validation.h" +#include +#include +#include +#include +#include + +/** + * @file quantum_rng_v3.c + * @brief Unified Quantum RNG v3.0 - Production implementation + * + * CRITICAL ARCHITECTURE: + * This resolves the entropy circular dependency through layered design: + * + * Layer 1 (Base): Hardware entropy pool + * - RDSEED, RDRAND, /dev/random, jitter + * - Health tested (NIST SP 800-90B) + * - Background generation thread + * + * Layer 2 (Quantum): Quantum simulation + * - Uses Layer 1 for measurement sampling + * - No circular dependency! + * - Bell-verified quantum evolution + * + * Layer 3 (Output): Conditioned random output + * - Quantum-evolved entropy + * - Final mixing and conditioning + * - Cryptographic quality + */ + +// ============================================================================ +// CONFIGURATION +// ============================================================================ + +void qrng_v3_get_default_config(qrng_v3_config_t *config) { + if (!config) return; + + // Quantum simulation defaults + config->num_qubits = 8; // 256-dimensional state space + config->mode = QRNG_V3_MODE_DIRECT; + + // Bell test monitoring (disable for benchmarks, enable for production) + config->enable_bell_monitoring = 0; // Disabled by default for performance + config->bell_test_interval = 1024 * 1024; // Test every 1MB when enabled + // Alert threshold for the monitored CHSH value. A healthy source produces + // ~2.83 (Tsirelson) and the classical bound is 2.0, so 2.1 flags a genuine + // collapse toward classical while leaving ample margin for the sampling + // noise of a finite-measurement CHSH estimate (avoids spurious failures). + config->min_acceptable_chsh = 2.1; + + // Grover optimization + config->enable_grover_cache = 1; + config->grover_cache_size = 256; + + // Entropy pool + config->entropy_pool_size = 64 * 1024; // 64KB pool + config->enable_background_entropy = 1; + + // Output buffer (larger = fewer refills = better performance) + config->output_buffer_size = 64 * 1024; // 64KB buffer + + // Performance + config->enable_simd = 1; + config->enable_performance_monitoring = 0; // Disabled by default (overhead) +} + +// ============================================================================ +// ENTROPY CALLBACK (Resolves circular dependency!) +// ============================================================================ + +/** + * @brief Entropy callback for quantum measurements + * + * This function provides hardware entropy to the quantum simulation layer. + * Key: Uses entropy_pool (Layer 1) for quantum measurements (Layer 2). + * No circular dependency because entropy_pool doesn't use quantum simulation! + */ +static int entropy_pool_callback(void *user_data, uint8_t *buffer, size_t size) { + entropy_pool_ctx_t *pool = (entropy_pool_ctx_t *)user_data; + if (!pool) return -1; + + return entropy_pool_get_bytes(pool, buffer, size); +} + +// ============================================================================ +// QUANTUM STATE EVOLUTION +// ============================================================================ + +/** + * @brief Evolve quantum state through random circuit + * + * Applies random quantum gates to create complex quantum evolution. + * Uses hardware entropy to select gates (no circular dependency). + */ +static qs_error_t evolve_quantum_state( + quantum_state_t *state, + quantum_entropy_ctx_t *entropy_ctx, + size_t num_gates +) { + if (!state || !entropy_ctx) return QS_ERROR_INVALID_STATE; + + for (size_t i = 0; i < num_gates; i++) { + // Get random gate selection using hardware entropy + uint64_t gate_selector; + if (quantum_entropy_get_uint64(entropy_ctx, &gate_selector) != 0) { + return QS_ERROR_INVALID_STATE; + } + + int qubit1 = gate_selector % state->num_qubits; + int qubit2 = (gate_selector >> 8) % state->num_qubits; + + // Select random gate type + int gate_type = (gate_selector >> 16) % 8; + + switch (gate_type) { + case 0: + gate_hadamard(state, qubit1); + break; + case 1: + if (qubit1 != qubit2) { + gate_cnot(state, qubit1, qubit2); + } + break; + case 2: { + double angle = ((gate_selector >> 24) & 0xFF) * QC_2PI / 256.0; + gate_ry(state, qubit1, angle); + break; + } + case 3: + gate_pauli_x(state, qubit1); + break; + case 4: + gate_pauli_z(state, qubit1); + break; + case 5: + gate_s(state, qubit1); + break; + case 6: + if (qubit1 != qubit2) { + gate_cz(state, qubit1, qubit2); + } + break; + case 7: { + double angle = ((gate_selector >> 32) & 0xFF) * QC_2PI / 256.0; + gate_rz(state, qubit1, angle); + break; + } + } + } + + return QS_SUCCESS; +} + +/** + * @brief Extract randomness from quantum measurements (OPTIMIZED) + * + * PERFORMANCE CRITICAL OPTIMIZATION: + * Instead of evolving for every 8 bytes, we: + * 1. Evolve state ONCE with deep circuit + * 2. Extract MULTIPLE measurements without re-evolving + * 3. Only re-evolve when entropy pool depleted + * + * This gives 10-50x speedup while maintaining quantum properties! + */ +static int extract_quantum_entropy( + qrng_v3_ctx_t *ctx, + uint8_t *buffer, + size_t size +) { + if (!ctx || !buffer || size == 0) return -1; + + size_t bytes_generated = 0; + + // GUARANTEED UNIQUENESS: Always evolve between measurements + // Balance: Batch for performance, but ensure diversity + const size_t measurements_per_full_evolution = 64; // Smaller batch = more diversity + size_t measurements_since_evolution = measurements_per_full_evolution; // Force first evolution + + while (bytes_generated < size) { + // Full evolution circuit periodically + if (measurements_since_evolution >= measurements_per_full_evolution) { + evolve_quantum_state( + ctx->quantum_state, + &ctx->entropy_ctx, + ctx->config.num_qubits * 2 // 2 gates per qubit + ); + measurements_since_evolution = 0; + } + + // CRITICAL: Apply 4 gates between EVERY measurement for guaranteed uniqueness + // More gates = more entropy mixing = better diversity + uint64_t gate_selector; + if (quantum_entropy_get_uint64(&ctx->entropy_ctx, &gate_selector) == 0) { + int qubit1 = gate_selector % ctx->quantum_state->num_qubits; + int qubit2 = (gate_selector >> 8) % ctx->quantum_state->num_qubits; + int qubit3 = (gate_selector >> 16) % ctx->quantum_state->num_qubits; + + // Apply 4 different gates for stronger mixing + gate_hadamard(ctx->quantum_state, qubit1); + gate_pauli_z(ctx->quantum_state, qubit2); + if (qubit1 != qubit3) { + gate_cnot(ctx->quantum_state, qubit1, qubit3); + } + gate_pauli_x(ctx->quantum_state, (qubit2 + 1) % ctx->quantum_state->num_qubits); + } + + // PERFORMANCE CRITICAL: Batch measure all qubits in ONE pass (8x faster!) + uint64_t measurement = quantum_measure_all_fast( + ctx->quantum_state, + &ctx->entropy_ctx + ); + ctx->stats.quantum_measurements += ctx->config.num_qubits; + + // A basis-state index contains only num_qubits bits (8 by default). + // Condition it with an independent full-width entropy word before + // serializing it, so repeated Born outcomes do not become repeated + // 8-byte output patterns. + uint64_t conditioner; + if (quantum_entropy_get_uint64(&ctx->entropy_ctx, &conditioner) != 0) { + return -1; + } + measurement ^= conditioner; + + measurements_since_evolution++; + + // Extract bytes from measurement + size_t bytes_to_copy = sizeof(measurement); + if (bytes_to_copy > size - bytes_generated) { + bytes_to_copy = size - bytes_generated; + } + + memcpy(buffer + bytes_generated, &measurement, bytes_to_copy); + bytes_generated += bytes_to_copy; + } + + return 0; +} + +/** + * @brief Extract randomness using Grover-enhanced quantum state (OPTIMIZED) + * + * KEY INSIGHT: Running full Grover search per byte is wasteful! + * Instead: Use Grover to prepare interesting quantum distributions, + * then measure multiple times from that distribution. + * + * This gives quantum enhancement WITHOUT the overhead of repeated searches. + */ +static int extract_grover_entropy( + qrng_v3_ctx_t *ctx, + uint8_t *buffer, + size_t size +) { + if (!ctx || !buffer || size == 0) return -1; + + size_t bytes_generated = 0; + + // OPTIMIZATION: Batch extraction - run Grover ONCE, measure MANY times + const size_t measurements_per_grover = 128; // 128 measurements per Grover prep + size_t measurements_extracted = measurements_per_grover; // Force first Grover + + while (bytes_generated < size) { + // Run Grover preparation only when batch depleted + if (measurements_extracted >= measurements_per_grover) { + // Use Grover to prepare quantum state with interesting distribution + // Pick random target to create non-uniform but quantum-evolved state + uint64_t random_target; + if (quantum_entropy_get_uint64(&ctx->entropy_ctx, &random_target) != 0) { + return -1; + } + random_target = random_target % ctx->quantum_state->state_dim; + + // Run simplified Grover preparation (fewer iterations for speed) + quantum_state_reset(ctx->quantum_state); + for (size_t q = 0; q < ctx->config.num_qubits; q++) { + gate_hadamard(ctx->quantum_state, q); + } + + // Apply just 2-3 Grover iterations (enough for enhancement, not full search) + for (int iter = 0; iter < 2; iter++) { + grover_oracle(ctx->quantum_state, random_target); + grover_diffusion(ctx->quantum_state); + } + + ctx->stats.grover_searches++; + measurements_extracted = 0; + } + + // PERFORMANCE CRITICAL: Batch measure all qubits in ONE pass (8x faster!) + uint64_t measurement = quantum_measure_all_fast( + ctx->quantum_state, + &ctx->entropy_ctx + ); + ctx->stats.quantum_measurements += ctx->config.num_qubits; + + // quantum_measure_all_fast returns a basis index, not 64 bits of + // entropy. Apply the same full-width conditioning as direct mode. + uint64_t conditioner; + if (quantum_entropy_get_uint64(&ctx->entropy_ctx, &conditioner) != 0) { + return -1; + } + measurement ^= conditioner; + + measurements_extracted++; + + // Extract bytes + size_t bytes_to_copy = sizeof(measurement); + if (bytes_to_copy > size - bytes_generated) { + bytes_to_copy = size - bytes_generated; + } + + memcpy(buffer + bytes_generated, &measurement, bytes_to_copy); + bytes_generated += bytes_to_copy; + } + + return 0; +} + +// ============================================================================ +// INITIALIZATION & CLEANUP +// ============================================================================ + +qrng_v3_error_t qrng_v3_init(qrng_v3_ctx_t **ctx_out) { + qrng_v3_config_t config; + qrng_v3_get_default_config(&config); + return qrng_v3_init_with_config(ctx_out, &config); +} + +qrng_v3_error_t qrng_v3_init_with_config( + qrng_v3_ctx_t **ctx_out, + const qrng_v3_config_t *config +) { + VALIDATE_NOT_NULL(ctx_out, QRNG_V3_ERROR_NULL_CONTEXT); + VALIDATE_NOT_NULL(config, QRNG_V3_ERROR_INVALID_PARAM); + + // Validate configuration + if (config->num_qubits < 2 || config->num_qubits > MAX_QUBITS) { + return QRNG_V3_ERROR_INVALID_PARAM; + } + + // Allocate context + qrng_v3_ctx_t *ctx = calloc(1, sizeof(qrng_v3_ctx_t)); + if (!ctx) return QRNG_V3_ERROR_OUT_OF_MEMORY; + + // Copy configuration + memcpy(&ctx->config, config, sizeof(qrng_v3_config_t)); + + // LAYER 1: Initialize hardware entropy pool (base layer) + entropy_pool_config_t pool_config = { + .pool_size = config->entropy_pool_size, + .refill_threshold = config->entropy_pool_size / 4, + .chunk_size = 4096, + .enable_background_thread = config->enable_background_entropy, + .min_entropy = 4.0 + }; + + int pool_err = entropy_pool_init_with_config(&ctx->entropy_pool, &pool_config); + if (pool_err != 0) { + free(ctx); + return QRNG_V3_ERROR_ENTROPY_FAILURE; + } + + // LAYER 2: Initialize quantum simulation engine + ctx->quantum_state = calloc(1, sizeof(quantum_state_t)); + if (!ctx->quantum_state) { + entropy_pool_free(ctx->entropy_pool); + free(ctx); + return QRNG_V3_ERROR_OUT_OF_MEMORY; + } + + qs_error_t qs_err = quantum_state_init(ctx->quantum_state, config->num_qubits); + if (qs_err != QS_SUCCESS) { + free(ctx->quantum_state); + entropy_pool_free(ctx->entropy_pool); + free(ctx); + return QRNG_V3_ERROR_QUANTUM_INIT; + } + + // Connect entropy pool to quantum measurements (resolves circular dependency!) + quantum_entropy_init( + &ctx->entropy_ctx, + entropy_pool_callback, + ctx->entropy_pool + ); + + // LAYER 3: Initialize output buffer (larger = better performance) + ctx->output_buffer_size = config->output_buffer_size > 0 ? + config->output_buffer_size : 65536; // Default 64KB + ctx->output_buffer = calloc(1, ctx->output_buffer_size); + if (!ctx->output_buffer) { + quantum_state_free(ctx->quantum_state); + free(ctx->quantum_state); + entropy_pool_free(ctx->entropy_pool); + free(ctx); + return QRNG_V3_ERROR_OUT_OF_MEMORY; + } + ctx->buffer_pos = ctx->output_buffer_size; // Force initial fill + + // Initialize Bell test monitoring + if (config->enable_bell_monitoring) { + ctx->bell_monitor = calloc(1, sizeof(bell_test_monitor_t)); + if (ctx->bell_monitor) { + bell_monitor_init(ctx->bell_monitor, 100); // Track last 100 tests + } + } + + // Initialize Grover cache + if (config->enable_grover_cache && config->grover_cache_size > 0) { + ctx->grover_cache = calloc(config->grover_cache_size, sizeof(uint64_t)); + } + + // Initialize performance monitoring + if (config->enable_performance_monitoring) { + perf_monitor_init(&ctx->perf_monitor); + } + + ctx->initialized = 1; + *ctx_out = ctx; + + return QRNG_V3_SUCCESS; +} + +void qrng_v3_free(qrng_v3_ctx_t *ctx) { + if (!ctx) return; + + // Free quantum state + if (ctx->quantum_state) { + quantum_state_free(ctx->quantum_state); + free(ctx->quantum_state); + } + + // Free entropy pool + if (ctx->entropy_pool) { + entropy_pool_free(ctx->entropy_pool); + } + + // Free Bell monitor + if (ctx->bell_monitor) { + bell_monitor_free(ctx->bell_monitor); + free(ctx->bell_monitor); + } + + // Free output buffer + if (ctx->output_buffer) { + secure_memzero(ctx->output_buffer, ctx->output_buffer_size); + free(ctx->output_buffer); + } + + // Free Grover cache + if (ctx->grover_cache) { + secure_memzero(ctx->grover_cache, ctx->config.grover_cache_size * sizeof(uint64_t)); + free(ctx->grover_cache); + } + + // Free performance monitor + if (ctx->perf_monitor) { + perf_monitor_free(ctx->perf_monitor); + } + + // Zero context + secure_memzero(ctx, sizeof(*ctx)); + free(ctx); +} + +// ============================================================================ +// RANDOM NUMBER GENERATION +// ============================================================================ + +qrng_v3_error_t qrng_v3_bytes( + qrng_v3_ctx_t *ctx, + uint8_t *buffer, + size_t size +) { + VALIDATE_NOT_NULL(ctx, QRNG_V3_ERROR_NULL_CONTEXT); + VALIDATE_BUFFER(buffer, size, QRNG_V3_ERROR_NULL_BUFFER); + + if (!ctx->initialized) { + return QRNG_V3_ERROR_NOT_INITIALIZED; + } + + // Performance monitoring + if (ctx->perf_monitor) { + perf_monitor_start_operation(ctx->perf_monitor, PERF_OP_OUTPUT_GENERATION); + } + + size_t bytes_copied = 0; + + while (bytes_copied < size) { + // Refill buffer if needed + if (ctx->buffer_pos >= ctx->output_buffer_size) { + // Generate fresh quantum entropy + int err; + + switch (ctx->config.mode) { + case QRNG_V3_MODE_DIRECT: + err = extract_quantum_entropy(ctx, ctx->output_buffer, ctx->output_buffer_size); + break; + + case QRNG_V3_MODE_GROVER: + // Grover-based entropy extraction + err = extract_grover_entropy(ctx, ctx->output_buffer, ctx->output_buffer_size); + break; + + case QRNG_V3_MODE_BELL_VERIFIED: + err = extract_quantum_entropy(ctx, ctx->output_buffer, ctx->output_buffer_size); + break; + + default: + return QRNG_V3_ERROR_INVALID_PARAM; + } + + if (err != 0) { + if (ctx->perf_monitor) { + perf_monitor_end_operation(ctx->perf_monitor); + } + return QRNG_V3_ERROR_ENTROPY_FAILURE; + } + + ctx->buffer_pos = 0; + } + + // Copy from buffer + size_t copy_size = ctx->output_buffer_size - ctx->buffer_pos; + if (copy_size > size - bytes_copied) { + copy_size = size - bytes_copied; + } + + memcpy(buffer + bytes_copied, ctx->output_buffer + ctx->buffer_pos, copy_size); + ctx->buffer_pos += copy_size; + bytes_copied += copy_size; + } + + // Update statistics + ctx->stats.bytes_generated += size; + ctx->bytes_since_bell_test += size; + + // Bell test monitoring + if (ctx->config.enable_bell_monitoring && + ctx->config.bell_test_interval > 0 && + ctx->bytes_since_bell_test >= ctx->config.bell_test_interval) { + + // Use enough measurements that the CHSH estimate is statistically + // tight (SE ~ 2/sqrt(N)); a small N made monitored generation + // spuriously trip the min-CHSH check on an unlucky draw. + bell_test_result_t result = qrng_v3_verify_quantum(ctx, 4000); + + if (ctx->bell_monitor) { + bell_monitor_add_result(ctx->bell_monitor, &result); + } + + ctx->stats.bell_tests_performed++; + if (bell_test_confirms_quantum(&result)) { + ctx->stats.bell_tests_passed++; + } + + // Check if quantum behavior is maintained + if (result.chsh_value < ctx->config.min_acceptable_chsh) { + // Quantum behavior degraded - this shouldn't happen in simulation + // but good to check + return QRNG_V3_ERROR_BELL_TEST_FAILED; + } + + ctx->bytes_since_bell_test = 0; + } + + if (ctx->perf_monitor) { + perf_monitor_end_operation(ctx->perf_monitor); + perf_monitor_record_bytes(ctx->perf_monitor, size); + } + + return QRNG_V3_SUCCESS; +} + +qrng_v3_error_t qrng_v3_uint64(qrng_v3_ctx_t *ctx, uint64_t *value) { + VALIDATE_NOT_NULL(ctx, QRNG_V3_ERROR_NULL_CONTEXT); + VALIDATE_NOT_NULL(value, QRNG_V3_ERROR_NULL_BUFFER); + + return qrng_v3_bytes(ctx, (uint8_t*)value, sizeof(*value)); +} + +qrng_v3_error_t qrng_v3_double(qrng_v3_ctx_t *ctx, double *value) { + VALIDATE_NOT_NULL(ctx, QRNG_V3_ERROR_NULL_CONTEXT); + VALIDATE_NOT_NULL(value, QRNG_V3_ERROR_NULL_BUFFER); + + uint64_t random_bits; + qrng_v3_error_t err = qrng_v3_uint64(ctx, &random_bits); + if (err != QRNG_V3_SUCCESS) return err; + + // Convert to double in [0, 1) using 53 bits of precision + *value = (double)(random_bits >> 11) * 0x1.0p-53; + + return QRNG_V3_SUCCESS; +} + +qrng_v3_error_t qrng_v3_range( + qrng_v3_ctx_t *ctx, + uint64_t min, + uint64_t max, + uint64_t *value +) { + VALIDATE_NOT_NULL(ctx, QRNG_V3_ERROR_NULL_CONTEXT); + VALIDATE_NOT_NULL(value, QRNG_V3_ERROR_NULL_BUFFER); + + if (min > max) return QRNG_V3_ERROR_INVALID_PARAM; + if (min == max) { + *value = min; + return QRNG_V3_SUCCESS; + } + + uint64_t range = max - min + 1; + if (range == 0) { // Overflow case + qrng_v3_uint64(ctx, value); + return QRNG_V3_SUCCESS; + } + + // Unbiased sampling + uint64_t threshold = (UINT64_MAX - range + 1) % range; + uint64_t r; + + do { + qrng_v3_error_t err = qrng_v3_uint64(ctx, &r); + if (err != QRNG_V3_SUCCESS) return err; + } while (r < threshold); + + *value = min + (r % range); + return QRNG_V3_SUCCESS; +} + +// ============================================================================ +// GROVER-ENHANCED SAMPLING +// ============================================================================ + +qrng_v3_error_t qrng_v3_grover_sample( + qrng_v3_ctx_t *ctx, + uint64_t *value +) { + VALIDATE_NOT_NULL(ctx, QRNG_V3_ERROR_NULL_CONTEXT); + VALIDATE_NOT_NULL(value, QRNG_V3_ERROR_NULL_BUFFER); + + if (!ctx->initialized) { + return QRNG_V3_ERROR_NOT_INITIALIZED; + } + + // Use Grover's algorithm for quantum sampling + *value = grover_random_sample( + ctx->quantum_state, + ctx->config.num_qubits, + &ctx->entropy_ctx + ); + + ctx->stats.grover_searches++; + + return QRNG_V3_SUCCESS; +} + +qrng_v3_error_t qrng_v3_grover_sample_distribution( + qrng_v3_ctx_t *ctx, + double (*target_distribution)(uint64_t), + uint64_t *value +) { + VALIDATE_NOT_NULL(ctx, QRNG_V3_ERROR_NULL_CONTEXT); + VALIDATE_NOT_NULL(target_distribution, QRNG_V3_ERROR_NULL_BUFFER); + VALIDATE_NOT_NULL(value, QRNG_V3_ERROR_NULL_BUFFER); + + /** + * FULL PRODUCTION AMPLITUDE AMPLIFICATION + * + * This implements quantum amplitude amplification to sample from + * arbitrary probability distributions. This is a generalization of + * Grover's algorithm for non-uniform distributions. + * + * Algorithm (Brassard et al., "Quantum Amplitude Amplification"): + * 1. Prepare state with amplitudes ∝ √P(x) where P is target distribution + * 2. Apply amplitude amplification operator iteratively + * 3. Measure to sample from P(x) + * + * Quantum advantage: Fewer iterations than rejection sampling for + * peaked distributions. + */ + + quantum_state_t *state = ctx->quantum_state; + uint64_t state_dim = state->state_dim; + + // Step 1: Initialize to uniform superposition + quantum_state_reset(state); + for (size_t q = 0; q < ctx->config.num_qubits; q++) { + gate_hadamard(state, q); + } + + // Step 2: Encode target distribution as amplitude modulation + // We set amplitude[i] ∝ sqrt(target_distribution(i)) + double normalization = 0.0; + + for (uint64_t i = 0; i < state_dim; i++) { + double target_prob = target_distribution(i); + + // Ensure valid probability + if (target_prob < 0.0) target_prob = 0.0; + if (target_prob > 1.0) target_prob = 1.0; + + // Born rule: |amplitude|^2 = probability + // Therefore: amplitude = sqrt(probability) * phase_factor + double target_amplitude = sqrt(target_prob); + + // Preserve existing phase from uniform superposition + complex_t current = state->amplitudes[i]; + double current_mag = cabs(current); + + if (current_mag > 1e-15) { + // Scale amplitude while preserving phase + complex_t phase_factor = current / current_mag; + state->amplitudes[i] = target_amplitude * phase_factor; + } else { + // No phase information, use real amplitude + state->amplitudes[i] = target_amplitude + 0.0 * I; + } + + normalization += target_amplitude * target_amplitude; + } + + // Step 3: Renormalize state (required for valid quantum state) + if (normalization > 1e-15) { + double norm = sqrt(normalization); + for (uint64_t i = 0; i < state_dim; i++) { + state->amplitudes[i] /= norm; + } + } + + // Step 4: Apply Grover diffusion to amplify target amplitudes + // Number of iterations for amplitude amplification: + // optimal = π/4 * sqrt(N/M) where N=state_dim, M=effective good states + + // Estimate M from distribution (effective number of states with high probability) + double effective_good_states = 0.0; + double max_prob = 0.0; + + for (uint64_t i = 0; i < state_dim; i++) { + double prob = target_distribution(i); + if (prob > max_prob) max_prob = prob; + } + + for (uint64_t i = 0; i < state_dim; i++) { + if (target_distribution(i) > max_prob * 0.1) { // 10% threshold + effective_good_states += 1.0; + } + } + + if (effective_good_states < 1.0) effective_good_states = 1.0; + + // Calculate optimal iterations + size_t num_iterations = (size_t)(QC_PI_4 * sqrt((double)state_dim / effective_good_states)); + + // Practical limits + if (num_iterations < 1) num_iterations = 1; + if (num_iterations > 50) num_iterations = 50; + + // Apply Grover diffusion operators + for (size_t iter = 0; iter < num_iterations; iter++) { + // Grover diffusion: inverts amplitudes about their average + // This amplifies amplitudes above average, suppresses below average + qs_error_t err = grover_diffusion(state); + if (err != QS_SUCCESS) { + return QRNG_V3_ERROR_QUANTUM_INIT; + } + } + + // Step 5: Measure to sample from amplified distribution + // Use hardware entropy for measurement sampling + double random; + if (quantum_entropy_get_double(&ctx->entropy_ctx, &random) != 0) { + return QRNG_V3_ERROR_ENTROPY_FAILURE; + } + + // Sample from final probability distribution + double cumulative = 0.0; + for (uint64_t i = 0; i < state_dim; i++) { + double prob = quantum_state_get_probability(state, i); + cumulative += prob; + + if (random < cumulative) { + *value = i; + ctx->stats.grover_searches++; + return QRNG_V3_SUCCESS; + } + } + + // Fallback (numerical precision edge case) + *value = state_dim - 1; + ctx->stats.grover_searches++; + + return QRNG_V3_SUCCESS; +} + +qrng_v3_error_t qrng_v3_grover_multi_target( + qrng_v3_ctx_t *ctx, + const uint64_t *targets, + size_t num_targets, + size_t *found_index, + uint64_t *value +) { + VALIDATE_NOT_NULL(ctx, QRNG_V3_ERROR_NULL_CONTEXT); + VALIDATE_NOT_NULL(targets, QRNG_V3_ERROR_NULL_BUFFER); + VALIDATE_NOT_NULL(found_index, QRNG_V3_ERROR_NULL_BUFFER); + VALIDATE_NOT_NULL(value, QRNG_V3_ERROR_NULL_BUFFER); + + if (num_targets == 0) return QRNG_V3_ERROR_INVALID_PARAM; + + /** + * MULTI-TARGET GROVER SEARCH (Production Implementation) + * + * Uses Grover's algorithm to search for multiple targets simultaneously. + * Oracle marks ALL targets, diffusion amplifies them collectively. + * + * Speedup: √(N/M) where N=search space, M=number of targets + */ + + quantum_state_t *state = ctx->quantum_state; + uint64_t state_dim = state->state_dim; + + // Validate all targets are within state space + for (size_t i = 0; i < num_targets; i++) { + if (targets[i] >= state_dim) { + return QRNG_V3_ERROR_INVALID_PARAM; + } + } + + // Step 1: Initialize to uniform superposition + quantum_state_reset(state); + for (size_t q = 0; q < ctx->config.num_qubits; q++) { + gate_hadamard(state, q); + } + + // Step 2: Calculate optimal iterations for multi-target search + // optimal_iterations = π/4 * sqrt(N/M) + double ratio = (double)state_dim / (double)num_targets; + size_t iterations = (size_t)(QC_PI_4 * sqrt(ratio)); + + if (iterations < 1) iterations = 1; + if (iterations > 100) iterations = 100; // Safety limit + + // Step 3: Apply Grover iterations + for (size_t iter = 0; iter < iterations; iter++) { + // Oracle: Mark all target states with phase flip + for (size_t t = 0; t < num_targets; t++) { + qs_error_t err = grover_oracle(state, targets[t]); + if (err != QS_SUCCESS) { + return QRNG_V3_ERROR_QUANTUM_INIT; + } + } + + // Diffusion: Amplitude amplification + qs_error_t err = grover_diffusion(state); + if (err != QS_SUCCESS) { + return QRNG_V3_ERROR_QUANTUM_INIT; + } + } + + // Step 4: Measure to find one of the targets + double random; + if (quantum_entropy_get_double(&ctx->entropy_ctx, &random) != 0) { + return QRNG_V3_ERROR_ENTROPY_FAILURE; + } + + // Sample from amplified probability distribution + double cumulative = 0.0; + uint64_t measured_state = 0; + + for (uint64_t i = 0; i < state_dim; i++) { + double prob = quantum_state_get_probability(state, i); + cumulative += prob; + + if (random < cumulative) { + measured_state = i; + break; + } + } + + // Step 5: Determine which target was found + for (size_t t = 0; t < num_targets; t++) { + if (measured_state == targets[t]) { + *found_index = t; + *value = targets[t]; + ctx->stats.grover_searches++; + return QRNG_V3_SUCCESS; + } + } + + // If we didn't find an exact target (rare but possible), return closest + // Find target with highest probability + size_t best_target = 0; + double best_prob = 0.0; + + for (size_t t = 0; t < num_targets; t++) { + double prob = quantum_state_get_probability(state, targets[t]); + if (prob > best_prob) { + best_prob = prob; + best_target = t; + } + } + + *found_index = best_target; + *value = targets[best_target]; + ctx->stats.grover_searches++; + + return QRNG_V3_SUCCESS; +} + +// ============================================================================ +// QUANTUM VERIFICATION +// ============================================================================ + +bell_test_result_t qrng_v3_verify_quantum( + qrng_v3_ctx_t *ctx, + size_t num_measurements +) { + bell_test_result_t result = {0}; + + if (!ctx || !ctx->initialized || !ctx->quantum_state) { + return result; + } + + // Run Bell test on current quantum state + result = bell_test_chsh( + ctx->quantum_state, + 0, // Qubit A + 1, // Qubit B + num_measurements, + NULL, // Use optimal settings + &ctx->entropy_ctx + ); + + // Update statistics + if (result.chsh_value > ctx->stats.max_chsh) { + ctx->stats.max_chsh = result.chsh_value; + } + if (ctx->stats.min_chsh == 0.0 || result.chsh_value < ctx->stats.min_chsh) { + ctx->stats.min_chsh = result.chsh_value; + } + + // Update running average + double total = ctx->stats.average_chsh * ctx->stats.bell_tests_performed; + total += result.chsh_value; + ctx->stats.bell_tests_performed++; + ctx->stats.average_chsh = total / ctx->stats.bell_tests_performed; + + return result; +} + +double qrng_v3_get_entanglement_entropy(const qrng_v3_ctx_t *ctx) { + if (!ctx || !ctx->quantum_state) return 0.0; + + // Calculate entanglement between first half and second half of qubits + size_t half = ctx->config.num_qubits / 2; + if (half == 0) return 0.0; + + int *subsystem_a = calloc(half, sizeof(int)); + if (!subsystem_a) return 0.0; + + for (size_t i = 0; i < half; i++) { + subsystem_a[i] = i; + } + + double entropy = quantum_state_entanglement_entropy( + ctx->quantum_state, + subsystem_a, + half + ); + + free(subsystem_a); + + // Numerical safety: entanglement entropy must be non-negative + // Negative values from numerical precision issues should be clamped + if (entropy < 0.0 || isnan(entropy)) { + entropy = 0.0; // Physically valid lower bound + } + + return entropy; +} + +int qrng_v3_is_quantum_verified(const qrng_v3_ctx_t *ctx) { + if (!ctx || !ctx->bell_monitor) return 0; + + // Check if recent Bell tests confirm quantum behavior + if (ctx->stats.bell_tests_performed == 0) return 0; + + // At least 80% of tests should pass + double pass_rate = (double)ctx->stats.bell_tests_passed / ctx->stats.bell_tests_performed; + if (pass_rate < 0.8) return 0; + + // Average CHSH should be above classical bound + if (ctx->stats.average_chsh <= 2.0) return 0; + + return 1; +} + +// ============================================================================ +// MODE CONTROL +// ============================================================================ + +qrng_v3_error_t qrng_v3_set_mode(qrng_v3_ctx_t *ctx, qrng_v3_mode_t mode) { + VALIDATE_NOT_NULL(ctx, QRNG_V3_ERROR_NULL_CONTEXT); + + ctx->config.mode = mode; + return QRNG_V3_SUCCESS; +} + +qrng_v3_mode_t qrng_v3_get_mode(const qrng_v3_ctx_t *ctx) { + if (!ctx) return QRNG_V3_MODE_DIRECT; + return ctx->config.mode; +} + +// ============================================================================ +// STATISTICS & MONITORING +// ============================================================================ + +qrng_v3_error_t qrng_v3_get_stats( + const qrng_v3_ctx_t *ctx, + qrng_v3_stats_t *stats +) { + VALIDATE_NOT_NULL(ctx, QRNG_V3_ERROR_NULL_CONTEXT); + VALIDATE_NOT_NULL(stats, QRNG_V3_ERROR_NULL_BUFFER); + + memcpy(stats, &ctx->stats, sizeof(*stats)); + + // Calculate current entanglement + stats->current_entanglement_entropy = qrng_v3_get_entanglement_entropy(ctx); + + return QRNG_V3_SUCCESS; +} + +void qrng_v3_print_stats(const qrng_v3_ctx_t *ctx) { + if (!ctx) return; + + printf("\n"); + printf("╔═══════════════════════════════════════════════════════════╗\n"); + printf("║ QUANTUM RNG v3.0 STATISTICS ║\n"); + printf("╠═══════════════════════════════════════════════════════════╣\n"); + printf("║ ║\n"); + printf("║ Generation: ║\n"); + printf("║ Bytes generated: %10llu ║\n", + (unsigned long long)ctx->stats.bytes_generated); + printf("║ Quantum measurements:%10llu ║\n", + (unsigned long long)ctx->stats.quantum_measurements); + printf("║ Grover searches: %10llu ║\n", + (unsigned long long)ctx->stats.grover_searches); + printf("║ ║\n"); + printf("║ Quantum Verification: ║\n"); + printf("║ Bell tests performed:%10llu ║\n", + (unsigned long long)ctx->stats.bell_tests_performed); + printf("║ Bell tests passed: %10llu ║\n", + (unsigned long long)ctx->stats.bell_tests_passed); + + if (ctx->stats.bell_tests_performed > 0) { + printf("║ Average CHSH: %10.4f ║\n", + ctx->stats.average_chsh); + printf("║ Min CHSH: %10.4f ║\n", + ctx->stats.min_chsh); + printf("║ Max CHSH: %10.4f ║\n", + ctx->stats.max_chsh); + + double pass_rate = 100.0 * ctx->stats.bell_tests_passed / ctx->stats.bell_tests_performed; + printf("║ Pass rate: %9.1f%% ║\n", pass_rate); + } + + printf("║ ║\n"); + printf("║ Quantum Properties: ║\n"); + + double ent_entropy = qrng_v3_get_entanglement_entropy(ctx); + printf("║ Entanglement entropy:%10.4f bits ║\n", ent_entropy); + printf("║ State entropy: %10.4f bits ║\n", + quantum_state_entropy(ctx->quantum_state)); + printf("║ Purity: %10.4f ║\n", + quantum_state_purity(ctx->quantum_state)); + + printf("║ ║\n"); + printf("║ Mode: %-50s ║\n", qrng_v3_mode_string(ctx->config.mode)); + printf("║ Quantum Verified: %-42s ║\n", + qrng_v3_is_quantum_verified(ctx) ? "✓ YES" : "✗ NO"); + + printf("╚═══════════════════════════════════════════════════════════╝\n"); + printf("\n"); + + // Print entropy pool stats + if (ctx->entropy_pool) { + entropy_pool_print_stats(ctx->entropy_pool); + } + + // Print performance stats + if (ctx->perf_monitor) { + perf_monitor_print_stats(ctx->perf_monitor); + } +} + +const bell_test_monitor_t* qrng_v3_get_bell_history(const qrng_v3_ctx_t *ctx) { + if (!ctx) return NULL; + return ctx->bell_monitor; +} + +// ============================================================================ +// UTILITY FUNCTIONS +// ============================================================================ + +const char* qrng_v3_version(void) { + static char version[32]; + snprintf(version, sizeof(version), "%d.%d.%d", + QUANTUM_RNG_V3_VERSION_MAJOR, + QUANTUM_RNG_V3_VERSION_MINOR, + QUANTUM_RNG_V3_VERSION_PATCH); + return version; +} + +const char* qrng_v3_error_string(qrng_v3_error_t error) { + switch (error) { + case QRNG_V3_SUCCESS: + return "Success"; + case QRNG_V3_ERROR_NULL_CONTEXT: + return "NULL context provided"; + case QRNG_V3_ERROR_NULL_BUFFER: + return "NULL buffer provided"; + case QRNG_V3_ERROR_INVALID_PARAM: + return "Invalid parameter"; + case QRNG_V3_ERROR_ENTROPY_FAILURE: + return "Entropy generation failed"; + case QRNG_V3_ERROR_QUANTUM_INIT: + return "Quantum state initialization failed"; + case QRNG_V3_ERROR_BELL_TEST_FAILED: + return "Bell test verification failed - quantum behavior not confirmed"; + case QRNG_V3_ERROR_OUT_OF_MEMORY: + return "Out of memory"; + case QRNG_V3_ERROR_NOT_INITIALIZED: + return "Context not initialized"; + default: + return "Unknown error"; + } +} + +const char* qrng_v3_mode_string(qrng_v3_mode_t mode) { + switch (mode) { + case QRNG_V3_MODE_DIRECT: + return "DIRECT (quantum measurements)"; + case QRNG_V3_MODE_GROVER: + return "GROVER (quantum search sampling)"; + case QRNG_V3_MODE_BELL_VERIFIED: + return "BELL_VERIFIED (continuous verification)"; + default: + return "Unknown mode"; + } +} + +// ============================================================================ +// BACKWARD COMPATIBILITY +// ============================================================================ + +qrng_v3_error_t qrng_v3_init_from_seed( + qrng_v3_ctx_t **ctx, + const uint8_t *seed, + size_t seed_len +) { + // Initialize with default config + qrng_v3_error_t err = qrng_v3_init(ctx); + if (err != QRNG_V3_SUCCESS) return err; + + // Use seed to initialize quantum state if provided + if (seed && seed_len > 0) { + // Apply seed-based gates to quantum state + for (size_t i = 0; i < seed_len && i < (*ctx)->config.num_qubits; i++) { + double angle = (seed[i] / 255.0) * QC_2PI; + gate_ry((*ctx)->quantum_state, i % (*ctx)->config.num_qubits, angle); + } + } + + return QRNG_V3_SUCCESS; +} diff --git a/vendor/quantum_rng/src/quantum_rng/quantum_rng_v3.h b/vendor/quantum_rng/src/quantum_rng/quantum_rng_v3.h new file mode 100644 index 0000000..4c4318b --- /dev/null +++ b/vendor/quantum_rng/src/quantum_rng/quantum_rng_v3.h @@ -0,0 +1,474 @@ +#ifndef QUANTUM_RNG_V3_H +#define QUANTUM_RNG_V3_H + +#include +#include +#include "quantum_state.h" +#include "quantum_gates.h" +#include "quantum_entropy.h" +#include "bell_test.h" +#include "grover.h" +#include "../entropy/entropy_pool.h" +#include "../profiling/performance_monitor.h" + +/** + * @file quantum_rng_v3.h + * @brief Unified Quantum RNG v3.0 - Production quantum engine integration + * + * This is the NEW unified quantum RNG that integrates: + * - Proven quantum simulation engine (Bell verified CHSH=2.828) + * - Hardware entropy pool with background generation + * - Continuous Bell test monitoring + * - Advanced Grover-based sampling + * - SIMD-optimized operations + * + * REPLACES: quantum_rng.c (legacy quantum-inspired implementation) + * INTEGRATES: quantum_state.c + quantum_gates.c + bell_test.c + grover.c + * + * Key improvements over v1.0: + * - Actually uses the Bell-verified quantum engine + * - Resolves entropy circular dependency (layered architecture) + * - 10-20x faster Bell tests + * - Advanced Grover sampling modes + * - Runtime quantum verification + */ + +// ============================================================================ +// VERSION INFORMATION +// ============================================================================ + +#define QUANTUM_RNG_V3_VERSION_MAJOR 3 +#define QUANTUM_RNG_V3_VERSION_MINOR 0 +#define QUANTUM_RNG_V3_VERSION_PATCH 0 + +// ============================================================================ +// CONFIGURATION +// ============================================================================ + +/** + * @brief Quantum RNG operation modes + */ +typedef enum { + QRNG_V3_MODE_DIRECT, /**< Direct quantum measurement (fastest) */ + QRNG_V3_MODE_GROVER, /**< Grover-enhanced sampling */ + QRNG_V3_MODE_BELL_VERIFIED /**< With continuous Bell verification */ +} qrng_v3_mode_t; + +/** + * @brief Configuration for quantum RNG v3 + */ +typedef struct { + // Quantum simulation + size_t num_qubits; /**< Qubits in quantum state (4-16) */ + qrng_v3_mode_t mode; /**< Operation mode */ + + // Bell test verification + int enable_bell_monitoring; /**< Enable continuous Bell tests */ + uint64_t bell_test_interval; /**< Test every N bytes (0=disabled) */ + double min_acceptable_chsh; /**< Minimum CHSH value (2.4 recommended) */ + + // Grover optimization + int enable_grover_cache; /**< Cache Grover iterations */ + size_t grover_cache_size; /**< Grover result cache size */ + + // Entropy pool + size_t entropy_pool_size; /**< Hardware entropy pool size */ + int enable_background_entropy; /**< Background entropy generation */ + + // Output buffering + size_t output_buffer_size; /**< Output buffer size (larger = fewer refills) */ + + // Performance + int enable_simd; /**< Use SIMD optimizations */ + int enable_performance_monitoring; /**< Track performance metrics */ +} qrng_v3_config_t; + +/** + * @brief Error codes + */ +typedef enum { + QRNG_V3_SUCCESS = 0, + QRNG_V3_ERROR_NULL_CONTEXT = -1, + QRNG_V3_ERROR_NULL_BUFFER = -2, + QRNG_V3_ERROR_INVALID_PARAM = -3, + QRNG_V3_ERROR_ENTROPY_FAILURE = -4, + QRNG_V3_ERROR_QUANTUM_INIT = -5, + QRNG_V3_ERROR_BELL_TEST_FAILED = -6, + QRNG_V3_ERROR_OUT_OF_MEMORY = -7, + QRNG_V3_ERROR_NOT_INITIALIZED = -8 +} qrng_v3_error_t; + +/** + * @brief Statistics structure + */ +typedef struct { + // Generation stats + uint64_t bytes_generated; + uint64_t quantum_measurements; + uint64_t grover_searches; + + // Bell test stats + uint64_t bell_tests_performed; + uint64_t bell_tests_passed; + double average_chsh; + double min_chsh; + double max_chsh; + + // Entropy stats + uint64_t hardware_entropy_consumed; + double current_entanglement_entropy; + + // Performance + double avg_measurement_latency_ns; + double throughput_mbps; +} qrng_v3_stats_t; + +/** + * @brief Quantum RNG v3 context + */ +typedef struct { + // Configuration + qrng_v3_config_t config; + + // Quantum simulation engine + quantum_state_t *quantum_state; + quantum_entropy_ctx_t entropy_ctx; // For quantum measurements + + // Hardware entropy (base layer - no circular dependency!) + entropy_pool_ctx_t *entropy_pool; + + // Bell test monitoring + bell_test_monitor_t *bell_monitor; + uint64_t bytes_since_bell_test; + + // Output buffering + uint8_t *output_buffer; + size_t output_buffer_size; + size_t buffer_pos; + + // Grover caching + uint64_t *grover_cache; + size_t grover_cache_pos; + + // Performance monitoring + perf_monitor_ctx_t *perf_monitor; + + // Statistics + qrng_v3_stats_t stats; + + // State + int initialized; +} qrng_v3_ctx_t; + +// ============================================================================ +// INITIALIZATION & CLEANUP +// ============================================================================ + +/** + * @brief Get default configuration + * + * Returns recommended settings: + * - 8 qubits (256-dimensional state space) + * - DIRECT mode (fastest) + * - Bell monitoring every 1MB + * - Background entropy enabled + * + * @param config Output configuration + */ +void qrng_v3_get_default_config(qrng_v3_config_t *config); + +/** + * @brief Initialize quantum RNG v3 with default configuration + * + * Creates unified quantum RNG using proven Bell-verified engine. + * Resolves entropy circular dependency through layered architecture: + * + * Layer 1: Hardware entropy pool (RDSEED, /dev/random, etc.) + * ↓ + * Layer 2: Quantum simulation (measurements use Layer 1) + * ↓ + * Layer 3: RNG output (quantum-evolved, conditioned) + * + * @param ctx Output context pointer + * @return QRNG_V3_SUCCESS or error code + */ +qrng_v3_error_t qrng_v3_init(qrng_v3_ctx_t **ctx); + +/** + * @brief Initialize with custom configuration + * + * @param ctx Output context pointer + * @param config Custom configuration + * @return QRNG_V3_SUCCESS or error code + */ +qrng_v3_error_t qrng_v3_init_with_config( + qrng_v3_ctx_t **ctx, + const qrng_v3_config_t *config +); + +/** + * @brief Free quantum RNG context + * + * Securely erases all quantum states and entropy buffers. + * + * @param ctx Context to free + */ +void qrng_v3_free(qrng_v3_ctx_t *ctx); + +// ============================================================================ +// RANDOM NUMBER GENERATION +// ============================================================================ + +/** + * @brief Generate random bytes using quantum simulation + * + * Process: + * 1. Apply quantum gates to evolve state + * 2. Measure qubits (using hardware entropy) + * 3. Extract randomness from measurements + * 4. Condition output through mixing + * 5. Optional: Verify with Bell test + * + * @param ctx Quantum RNG context + * @param buffer Output buffer + * @param size Number of bytes to generate + * @return QRNG_V3_SUCCESS or error code + */ +qrng_v3_error_t qrng_v3_bytes( + qrng_v3_ctx_t *ctx, + uint8_t *buffer, + size_t size +); + +/** + * @brief Generate uint64 using quantum simulation + * + * @param ctx Quantum RNG context + * @param value Output value pointer + * @return QRNG_V3_SUCCESS or error code + */ +qrng_v3_error_t qrng_v3_uint64(qrng_v3_ctx_t *ctx, uint64_t *value); + +/** + * @brief Generate double in [0, 1) using quantum simulation + * + * @param ctx Quantum RNG context + * @param value Output value pointer + * @return QRNG_V3_SUCCESS or error code + */ +qrng_v3_error_t qrng_v3_double(qrng_v3_ctx_t *ctx, double *value); + +/** + * @brief Generate integer in range using quantum simulation + * + * @param ctx Quantum RNG context + * @param min Minimum value (inclusive) + * @param max Maximum value (inclusive) + * @param value Output value pointer + * @return QRNG_V3_SUCCESS or error code + */ +qrng_v3_error_t qrng_v3_range( + qrng_v3_ctx_t *ctx, + uint64_t min, + uint64_t max, + uint64_t *value +); + +// ============================================================================ +// GROVER-ENHANCED SAMPLING (NEW!) +// ============================================================================ + +/** + * @brief Generate sample using Grover's algorithm + * + * Uses Grover search for quantum-enhanced sampling. + * Provides √N speedup for certain distributions. + * + * @param ctx Quantum RNG context + * @param value Output value (0 to 2^num_qubits - 1) + * @return QRNG_V3_SUCCESS or error code + */ +qrng_v3_error_t qrng_v3_grover_sample( + qrng_v3_ctx_t *ctx, + uint64_t *value +); + +/** + * @brief Generate samples from target distribution using Grover + * + * Uses amplitude amplification to sample from arbitrary distributions. + * + * @param ctx Quantum RNG context + * @param target_distribution Probability distribution function + * @param value Output sample + * @return QRNG_V3_SUCCESS or error code + */ +qrng_v3_error_t qrng_v3_grover_sample_distribution( + qrng_v3_ctx_t *ctx, + double (*target_distribution)(uint64_t), + uint64_t *value +); + +/** + * @brief Multi-target Grover search + * + * Searches for multiple targets simultaneously. + * + * @param ctx Quantum RNG context + * @param targets Array of target states + * @param num_targets Number of targets + * @param found_index Output: which target was found + * @param value Output: found value + * @return QRNG_V3_SUCCESS or error code + */ +qrng_v3_error_t qrng_v3_grover_multi_target( + qrng_v3_ctx_t *ctx, + const uint64_t *targets, + size_t num_targets, + size_t *found_index, + uint64_t *value +); + +// ============================================================================ +// QUANTUM VERIFICATION +// ============================================================================ + +/** + * @brief Run Bell test to verify quantum behavior + * + * Performs CHSH inequality test to prove genuine quantum properties. + * Should be called periodically or on-demand. + * + * @param ctx Quantum RNG context + * @param num_measurements Number of measurements (recommend 1000+) + * @return Bell test result + */ +bell_test_result_t qrng_v3_verify_quantum( + qrng_v3_ctx_t *ctx, + size_t num_measurements +); + +/** + * @brief Get entanglement entropy of current quantum state + * + * @param ctx Quantum RNG context + * @return Entanglement entropy in bits + */ +double qrng_v3_get_entanglement_entropy(const qrng_v3_ctx_t *ctx); + +/** + * @brief Check if quantum behavior is maintained + * + * Returns 1 if recent Bell tests confirm quantum behavior, + * 0 if quantum properties have degraded. + * + * @param ctx Quantum RNG context + * @return 1 if quantum, 0 otherwise + */ +int qrng_v3_is_quantum_verified(const qrng_v3_ctx_t *ctx); + +// ============================================================================ +// MODE CONTROL +// ============================================================================ + +/** + * @brief Set operation mode + * + * Modes: + * - DIRECT: Direct quantum measurements (fastest) + * - GROVER: Grover-enhanced sampling (quantum advantage) + * - BELL_VERIFIED: Continuous Bell test monitoring (slowest, max assurance) + * + * @param ctx Quantum RNG context + * @param mode New operation mode + * @return QRNG_V3_SUCCESS or error code + */ +qrng_v3_error_t qrng_v3_set_mode(qrng_v3_ctx_t *ctx, qrng_v3_mode_t mode); + +/** + * @brief Get current operation mode + * + * @param ctx Quantum RNG context + * @return Current mode + */ +qrng_v3_mode_t qrng_v3_get_mode(const qrng_v3_ctx_t *ctx); + +// ============================================================================ +// STATISTICS & MONITORING +// ============================================================================ + +/** + * @brief Get statistics + * + * @param ctx Quantum RNG context + * @param stats Output statistics structure + * @return QRNG_V3_SUCCESS or error code + */ +qrng_v3_error_t qrng_v3_get_stats( + const qrng_v3_ctx_t *ctx, + qrng_v3_stats_t *stats +); + +/** + * @brief Print detailed statistics + * + * @param ctx Quantum RNG context + */ +void qrng_v3_print_stats(const qrng_v3_ctx_t *ctx); + +/** + * @brief Get Bell test history + * + * @param ctx Quantum RNG context + * @return Bell test monitor (read-only) + */ +const bell_test_monitor_t* qrng_v3_get_bell_history(const qrng_v3_ctx_t *ctx); + +// ============================================================================ +// UTILITY FUNCTIONS +// ============================================================================ + +/** + * @brief Get version string + * + * @return Version string "major.minor.patch" + */ +const char* qrng_v3_version(void); + +/** + * @brief Get error string + * + * @param error Error code + * @return Human-readable error description + */ +const char* qrng_v3_error_string(qrng_v3_error_t error); + +/** + * @brief Get mode string + * + * @param mode Operation mode + * @return Mode name + */ +const char* qrng_v3_mode_string(qrng_v3_mode_t mode); + +// ============================================================================ +// BACKWARD COMPATIBILITY (with legacy quantum_rng.h) +// ============================================================================ + +/** + * @brief Create v3 context from legacy seed (backward compatible) + * + * Maintains compatibility with old qrng_init() API. + * + * @param ctx Output context + * @param seed Seed bytes (can be NULL) + * @param seed_len Seed length + * @return QRNG_V3_SUCCESS or error code + */ +qrng_v3_error_t qrng_v3_init_from_seed( + qrng_v3_ctx_t **ctx, + const uint8_t *seed, + size_t seed_len +); + +#endif /* QUANTUM_RNG_V3_H */ \ No newline at end of file diff --git a/vendor/quantum_rng/src/quantum_rng/quantum_state.c b/vendor/quantum_rng/src/quantum_rng/quantum_state.c new file mode 100644 index 0000000..ee7dd68 --- /dev/null +++ b/vendor/quantum_rng/src/quantum_rng/quantum_state.c @@ -0,0 +1,551 @@ +#include "quantum_state.h" +#include "matrix_math.h" +#include "simd_ops.h" +#include "accelerate_ops.h" // Phase 3: Apple Accelerate + AMX +#include "../common/secure_memory.h" +#include "../common/validation.h" +#include +#include +#include +#include + +#define NORMALIZATION_TOLERANCE 1e-10 +#define SMALL_PROBABILITY 1e-15 + +// ============================================================================ +// STATE MANAGEMENT +// ============================================================================ + +qs_error_t quantum_state_init(quantum_state_t *state, size_t num_qubits) { + if (!state) return QS_ERROR_INVALID_STATE; + if (num_qubits == 0 || num_qubits > MAX_QUBITS) return QS_ERROR_INVALID_DIMENSION; + + memset(state, 0, sizeof(quantum_state_t)); + + state->num_qubits = num_qubits; + state->state_dim = 1ULL << num_qubits; + + // Phase 4: Allocate AMX-aligned memory for optimal M2 Ultra performance + // Uses 64-byte alignment for Apple Silicon AMX hardware acceleration + #if HAS_ACCELERATE + state->amplitudes = accelerate_alloc_complex_array(state->state_dim); + #else + state->amplitudes = (complex_t *)calloc(state->state_dim, sizeof(complex_t)); + #endif + + if (!state->amplitudes) { + return QS_ERROR_OUT_OF_MEMORY; + } + + // Zero-initialize (AMX-aligned alloc doesn't guarantee zeroing) + memset(state->amplitudes, 0, state->state_dim * sizeof(complex_t)); + + // Initialize to |0...0⟩ state + state->amplitudes[0] = 1.0 + 0.0*I; + + // Allocate measurement history + state->max_measurements = 1024; + state->measurement_outcomes = (uint64_t *)calloc(state->max_measurements, sizeof(uint64_t)); + if (!state->measurement_outcomes) { + free(state->amplitudes); + return QS_ERROR_OUT_OF_MEMORY; + } + + state->owns_memory = 1; + state->global_phase = 0.0; + state->purity = 1.0; // Pure state + state->fidelity = 1.0; + state->num_measurements = 0; + + return QS_SUCCESS; +} + +void quantum_state_free(quantum_state_t *state) { + if (!state) return; + + if (state->owns_memory) { + if (state->amplitudes) { + // Secure zero before freeing (prevent memory dumps) + secure_memzero(state->amplitudes, state->state_dim * sizeof(complex_t)); + + // Phase 3: Use Accelerate-aware deallocation + #if HAS_ACCELERATE + accelerate_free_complex_array(state->amplitudes); + #else + free(state->amplitudes); + #endif + } + + if (state->measurement_outcomes) { + // Secure zero measurement history + secure_memzero(state->measurement_outcomes, state->max_measurements * sizeof(uint64_t)); + free(state->measurement_outcomes); + } + } + + secure_memzero(state, sizeof(quantum_state_t)); +} + +qs_error_t quantum_state_from_amplitudes( + quantum_state_t *state, + const complex_t *amplitudes, + size_t dim +) { + if (!state || !amplitudes) return QS_ERROR_INVALID_STATE; + + // Determine number of qubits from dimension + size_t num_qubits = 0; + size_t check_dim = dim; + while (check_dim > 1) { + if (check_dim & 1) return QS_ERROR_INVALID_DIMENSION; // Not power of 2 + check_dim >>= 1; + num_qubits++; + } + + if (num_qubits > MAX_QUBITS) return QS_ERROR_INVALID_DIMENSION; + + // Initialize state + qs_error_t err = quantum_state_init(state, num_qubits); + if (err != QS_SUCCESS) return err; + + // Copy amplitudes + memcpy(state->amplitudes, amplitudes, dim * sizeof(complex_t)); + + // Verify and normalize + if (!quantum_state_is_normalized(state, NORMALIZATION_TOLERANCE)) { + quantum_state_normalize(state); + } + + return QS_SUCCESS; +} + +qs_error_t quantum_state_clone(quantum_state_t *dest, const quantum_state_t *src) { + if (!dest || !src) return QS_ERROR_INVALID_STATE; + + qs_error_t err = quantum_state_init(dest, src->num_qubits); + if (err != QS_SUCCESS) return err; + + // Copy amplitudes + memcpy(dest->amplitudes, src->amplitudes, src->state_dim * sizeof(complex_t)); + + // Copy properties + dest->global_phase = src->global_phase; + dest->entanglement_entropy = src->entanglement_entropy; + dest->purity = src->purity; + dest->fidelity = src->fidelity; + + // Copy measurement history + size_t measurements_to_copy = src->num_measurements; + if (measurements_to_copy > dest->max_measurements) { + measurements_to_copy = dest->max_measurements; + } + memcpy(dest->measurement_outcomes, src->measurement_outcomes, + measurements_to_copy * sizeof(uint64_t)); + dest->num_measurements = measurements_to_copy; + + return QS_SUCCESS; +} + +void quantum_state_reset(quantum_state_t *state) { + if (!state || !state->amplitudes) return; + + // Reset to |0...0⟩ + memset(state->amplitudes, 0, state->state_dim * sizeof(complex_t)); + state->amplitudes[0] = 1.0 + 0.0*I; + + state->global_phase = 0.0; + state->purity = 1.0; + state->fidelity = 1.0; + state->num_measurements = 0; +} + +// ============================================================================ +// STATE PROPERTIES +// ============================================================================ + +int quantum_state_is_normalized(const quantum_state_t *state, double tolerance) { + if (!state || !state->amplitudes) return 0; + + // Phase 3: Use Accelerate framework for AMX acceleration (5-10x additional speedup) + #if HAS_ACCELERATE + double sum = accelerate_sum_squared_magnitudes(state->amplitudes, state->state_dim); + #else + // Fallback: Use SIMD for fast normalization check (8-16x faster with AVX2) + double sum = simd_sum_squared_magnitudes(state->amplitudes, state->state_dim); + #endif + + return fabs(sum - 1.0) < tolerance; +} + +qs_error_t quantum_state_normalize(quantum_state_t *state) { + VALIDATE_QUANTUM_STATE(state, QS_ERROR_INVALID_STATE); + + // Phase 3: Calculate norm using Accelerate framework (AMX-accelerated) + #if HAS_ACCELERATE + double norm_squared = accelerate_sum_squared_magnitudes(state->amplitudes, state->state_dim); + #else + // Fallback: Calculate norm using SIMD (8-16x faster with AVX2) + double norm_squared = simd_sum_squared_magnitudes(state->amplitudes, state->state_dim); + #endif + + if (norm_squared < SMALL_PROBABILITY) { + return QS_ERROR_NOT_NORMALIZED; + } + + double norm = sqrt(norm_squared); + + // Phase 3: Normalize using Accelerate framework (AMX-accelerated) + #if HAS_ACCELERATE + accelerate_normalize_amplitudes(state->amplitudes, state->state_dim, norm); + #else + // Fallback: Normalize using SIMD (4-8x faster with AVX2) + simd_normalize_amplitudes(state->amplitudes, state->state_dim, norm); + #endif + + return QS_SUCCESS; +} + +double quantum_state_entropy(const quantum_state_t *state) { + if (!state || !state->amplitudes) return 0.0; + + double entropy = 0.0; + + for (size_t i = 0; i < state->state_dim; i++) { + double prob = cabs(state->amplitudes[i]); + prob = prob * prob; // |α|² + + if (prob > SMALL_PROBABILITY) { + entropy -= prob * log2(prob); + } + } + + return entropy; +} + +double quantum_state_purity(const quantum_state_t *state) { + if (!state || !state->amplitudes) return 0.0; + + // For pure state: Tr(ρ²) = (Σ|αᵢ|²)² = 1 + // For mixed state: Tr(ρ²) < 1 + double purity = 0.0; + + for (size_t i = 0; i < state->state_dim; i++) { + double prob = cabs(state->amplitudes[i]); + prob = prob * prob; + purity += prob * prob; + } + + return purity; +} + +double quantum_state_fidelity(const quantum_state_t *state1, const quantum_state_t *state2) { + if (!state1 || !state2 || !state1->amplitudes || !state2->amplitudes) return 0.0; + if (state1->num_qubits != state2->num_qubits) return 0.0; + + // Fidelity F = |⟨ψ|φ⟩|² + complex_t inner_product = 0.0; + + for (size_t i = 0; i < state1->state_dim; i++) { + // ⟨ψ|φ⟩ = Σ ψᵢ* φᵢ + inner_product += conj(state1->amplitudes[i]) * state2->amplitudes[i]; + } + + double fidelity = cabs(inner_product); + return fidelity * fidelity; +} + +complex_t quantum_state_get_amplitude(const quantum_state_t *state, uint64_t basis_index) { + if (!state || !state->amplitudes || basis_index >= state->state_dim) { + return 0.0; + } + return state->amplitudes[basis_index]; +} + +double quantum_state_get_probability(const quantum_state_t *state, uint64_t basis_index) { + if (!state || !state->amplitudes || basis_index >= state->state_dim) { + return 0.0; + } + + complex_t amplitude = state->amplitudes[basis_index]; + double prob = cabs(amplitude); + return prob * prob; +} + +// ============================================================================ +// ENTANGLEMENT MEASURES +// ============================================================================ + +double quantum_state_entanglement_entropy( + const quantum_state_t *state, + const int *qubits_subsystem_a, + size_t num_qubits_a +) { + // Validate inputs + if (!state || !qubits_subsystem_a || num_qubits_a == 0) return 0.0; + if (num_qubits_a >= state->num_qubits) return 0.0; + + // Calculate dimensions + size_t num_qubits_b = state->num_qubits - num_qubits_a; + size_t dim_a = 1ULL << num_qubits_a; + + // Initialize all pointers to NULL for cleanup + int *qubits_b = NULL; + int *in_a = NULL; + complex_t *rho_a = NULL; + double *eigenvalues = NULL; + complex_t *eigenvectors = NULL; + double entropy = 0.0; + + // Allocate memory with goto cleanup pattern + qubits_b = (int *)calloc(num_qubits_b, sizeof(int)); + if (!qubits_b) goto cleanup; + + in_a = (int *)calloc(state->num_qubits, sizeof(int)); + if (!in_a) goto cleanup; + + // Mark which qubits are in A + for (size_t i = 0; i < num_qubits_a; i++) { + if (qubits_subsystem_a[i] >= 0 && qubits_subsystem_a[i] < (int)state->num_qubits) { + in_a[qubits_subsystem_a[i]] = 1; + } + } + + // Build list of qubits in B + size_t b_idx = 0; + for (size_t i = 0; i < state->num_qubits; i++) { + if (!in_a[i]) { + qubits_b[b_idx++] = i; + } + } + + // Get reduced density matrix + rho_a = (complex_t *)calloc(dim_a * dim_a, sizeof(complex_t)); + if (!rho_a) goto cleanup; + + qs_error_t err = quantum_state_partial_trace(state, qubits_b, num_qubits_b, rho_a); + if (err != QS_SUCCESS) goto cleanup; + + // Allocate for eigenvalue decomposition + eigenvalues = (double *)calloc(dim_a, sizeof(double)); + if (!eigenvalues) goto cleanup; + + eigenvectors = (complex_t *)calloc(dim_a * dim_a, sizeof(complex_t)); + if (!eigenvectors) goto cleanup; + + // Compute eigenvalues + int status = hermitian_eigen_decomposition( + rho_a, dim_a, eigenvalues, eigenvectors, 0, 0); + + if (status != 0) goto cleanup; + + // Calculate von Neumann entropy: S = -Σᵢ λᵢ log₂(λᵢ) + for (size_t i = 0; i < dim_a; i++) { + double lambda = eigenvalues[i]; + if (lambda > SMALL_PROBABILITY) { + entropy -= lambda * log2(lambda); + } + } + +cleanup: + // Cleanup all allocations (safe even if NULL) + if (qubits_b) free(qubits_b); + if (in_a) free(in_a); + if (rho_a) free(rho_a); + if (eigenvalues) free(eigenvalues); + if (eigenvectors) free(eigenvectors); + + return entropy; +} + +qs_error_t quantum_state_partial_trace( + const quantum_state_t *state, + const int *qubits_to_trace, + size_t num_traced, + complex_t *reduced_density +) { + if (!state || !qubits_to_trace || !reduced_density) { + return QS_ERROR_INVALID_STATE; + } + + if (num_traced == 0 || num_traced >= state->num_qubits) { + return QS_ERROR_INVALID_DIMENSION; + } + + // Calculate dimensions + size_t num_kept = state->num_qubits - num_traced; + size_t dim_kept = 1ULL << num_kept; + size_t dim_traced = 1ULL << num_traced; + + // Create mask for kept qubits + uint64_t kept_mask = 0; + uint64_t traced_mask = 0; + + // Build masks + int *is_traced = (int *)calloc(state->num_qubits, sizeof(int)); + if (!is_traced) return QS_ERROR_OUT_OF_MEMORY; + + for (size_t i = 0; i < num_traced; i++) { + if (qubits_to_trace[i] < 0 || qubits_to_trace[i] >= (int)state->num_qubits) { + free(is_traced); + return QS_ERROR_INVALID_QUBIT; + } + is_traced[qubits_to_trace[i]] = 1; + traced_mask |= (1ULL << qubits_to_trace[i]); + } + + for (size_t i = 0; i < state->num_qubits; i++) { + if (!is_traced[i]) { + kept_mask |= (1ULL << i); + } + } + + // Initialize reduced density matrix to zero + memset(reduced_density, 0, dim_kept * dim_kept * sizeof(complex_t)); + + // Compute partial trace: ρ_A = Tr_B(|ψ⟩⟨ψ|) + // ρ_A(i,j) = Σ_k ⟨i,k|ψ⟩⟨ψ|j,k⟩ = Σ_k ψ*_{i,k} ψ_{j,k} + + for (uint64_t kept_i = 0; kept_i < dim_kept; kept_i++) { + for (uint64_t kept_j = 0; kept_j < dim_kept; kept_j++) { + complex_t sum = 0.0; + + // Sum over all traced qubits + for (uint64_t traced = 0; traced < dim_traced; traced++) { + // Construct full basis indices + uint64_t basis_i = 0; + uint64_t basis_j = 0; + + // Interleave kept and traced qubit indices + size_t kept_bit_i = 0; + size_t kept_bit_j = 0; + size_t traced_bit = 0; + + for (size_t qubit = 0; qubit < state->num_qubits; qubit++) { + if (is_traced[qubit]) { + // Use traced index + if (traced & (1ULL << traced_bit)) { + basis_i |= (1ULL << qubit); + basis_j |= (1ULL << qubit); + } + traced_bit++; + } else { + // Use kept index + if (kept_i & (1ULL << kept_bit_i)) { + basis_i |= (1ULL << qubit); + } + if (kept_j & (1ULL << kept_bit_j)) { + basis_j |= (1ULL << qubit); + } + kept_bit_i++; + kept_bit_j++; + } + } + + // Add contribution: ψ*_i × ψ_j + sum += conj(state->amplitudes[basis_i]) * state->amplitudes[basis_j]; + } + + // Store in reduced density matrix + reduced_density[kept_i * dim_kept + kept_j] = sum; + } + } + + free(is_traced); + return QS_SUCCESS; +} + +// ============================================================================ +// MEASUREMENT HISTORY +// ============================================================================ + +void quantum_state_record_measurement(quantum_state_t *state, uint64_t outcome) { + if (!state || !state->measurement_outcomes) return; + + if (state->num_measurements < state->max_measurements) { + state->measurement_outcomes[state->num_measurements] = outcome; + state->num_measurements++; + } else { + // Circular buffer: overwrite oldest + memmove(state->measurement_outcomes, + state->measurement_outcomes + 1, + (state->max_measurements - 1) * sizeof(uint64_t)); + state->measurement_outcomes[state->max_measurements - 1] = outcome; + } +} + +size_t quantum_state_get_measurement_history( + const quantum_state_t *state, + uint64_t *outcomes, + size_t max_outcomes +) { + if (!state || !outcomes) return 0; + + size_t count = state->num_measurements < max_outcomes ? + state->num_measurements : max_outcomes; + + memcpy(outcomes, state->measurement_outcomes, count * sizeof(uint64_t)); + return count; +} + +void quantum_state_clear_measurements(quantum_state_t *state) { + if (!state) return; + state->num_measurements = 0; +} + +// ============================================================================ +// UTILITY FUNCTIONS +// ============================================================================ + +void quantum_state_print(const quantum_state_t *state, size_t max_terms) { + if (!state || !state->amplitudes) return; + + printf("Quantum State ("); + printf("%zu qubits, dim=%zu):\n", state->num_qubits, state->state_dim); + + size_t terms_printed = 0; + for (size_t i = 0; i < state->state_dim && terms_printed < max_terms; i++) { + complex_t amp = state->amplitudes[i]; + double prob = cabs(amp); + prob = prob * prob; + + if (prob > SMALL_PROBABILITY) { + char basis_str[MAX_QUBITS + 1]; + quantum_basis_state_string(i, state->num_qubits, basis_str, sizeof(basis_str)); + + printf(" "); + if (creal(amp) != 0.0) printf("%.6f", creal(amp)); + if (cimag(amp) > 0.0) { + printf("+%.6fi", cimag(amp)); + } else if (cimag(amp) < 0.0) { + printf("%.6fi", cimag(amp)); + } + printf(" |%s⟩ (p=%.6f)\n", basis_str, prob); + + terms_printed++; + } + } + + if (terms_printed < state->state_dim) { + printf(" ... (%zu more terms)\n", state->state_dim - terms_printed); + } + + printf("Properties:\n"); + printf(" Entropy: %.6f bits\n", quantum_state_entropy(state)); + printf(" Purity: %.6f\n", quantum_state_purity(state)); + printf(" Measurements: %zu\n", state->num_measurements); +} + +void quantum_basis_state_string( + uint64_t basis_index, + size_t num_qubits, + char *buffer, + size_t buffer_size +) { + if (!buffer || buffer_size < num_qubits + 1) return; + + for (size_t i = 0; i < num_qubits; i++) { + size_t bit_pos = num_qubits - 1 - i; + buffer[i] = (basis_index & (1ULL << bit_pos)) ? '1' : '0'; + } + buffer[num_qubits] = '\0'; +} \ No newline at end of file diff --git a/vendor/quantum_rng/src/quantum_rng/quantum_state.h b/vendor/quantum_rng/src/quantum_rng/quantum_state.h new file mode 100644 index 0000000..25b014f --- /dev/null +++ b/vendor/quantum_rng/src/quantum_rng/quantum_state.h @@ -0,0 +1,281 @@ +#ifndef QUANTUM_STATE_H +#define QUANTUM_STATE_H + +#include +#include +#include +#include + +// Use C99 complex types +typedef double _Complex complex_t; + +/** + * @file quantum_state.h + * @brief Advanced quantum state vector simulation engine + * + * Implements full quantum circuit simulation with: + * - Up to 32 qubits (4.3B dimensional state space) - Phase 4 scaling + * - Universal gate set (Pauli, Hadamard, Phase, CNOT, Toffoli) + * - Wavefunction collapse on measurement + * - Quantum entanglement and superposition + * - Bell inequality violation capability + * + * M2 Ultra with 192GB RAM can handle: + * - 32 qubits: 4.3B states = 68.7GB (comfortable) + * - 30 qubits: 1.07B states = 17.2GB (easy) + * - 28 qubits: 268M states = 4.3GB (trivial) + */ + +#define MAX_QUBITS 32 // Phase 4: Scale to 32 qubits (4.3B states, 68.7GB) +#define MAX_STATE_DIM (1ULL << MAX_QUBITS) // 2^32 = 4,294,967,296 +#define RECOMMENDED_MAX_QUBITS 28 // Sweet spot: 268M states = 4.3GB (fast + safe) + +// Error codes specific to quantum simulation +typedef enum { + QS_SUCCESS = 0, + QS_ERROR_INVALID_QUBIT = -1, + QS_ERROR_INVALID_STATE = -2, + QS_ERROR_NOT_NORMALIZED = -3, + QS_ERROR_OUT_OF_MEMORY = -4, + QS_ERROR_INVALID_DIMENSION = -5 +} qs_error_t; + +/** + * @brief Quantum state representation + * + * Represents a pure quantum state |ψ⟩ = Σ αᵢ|i⟩ where: + * - αᵢ are complex amplitudes + * - Σ|αᵢ|² = 1 (normalization) + * - |i⟩ are computational basis states + */ +typedef struct { + size_t num_qubits; // Number of qubits + size_t state_dim; // 2^num_qubits + complex_t *amplitudes; // State vector coefficients + + // Quantum properties + double global_phase; // Global phase factor + double entanglement_entropy; // Von Neumann entropy + double purity; // Tr(ρ²) + double fidelity; // State fidelity + + // Measurement history for verification + uint64_t *measurement_outcomes; + size_t num_measurements; + size_t max_measurements; + + // Memory management + int owns_memory; // 1 if we allocated amplitudes +} quantum_state_t; + +// ============================================================================ +// STATE MANAGEMENT +// ============================================================================ + +/** + * @brief Initialize quantum state in |0...0⟩ + * + * Phase 4: Scales to 32 qubits (4.3B states, 68.7GB with 192GB RAM) + * Uses Accelerate framework with AMX-aligned memory (64-byte boundaries) + * for optimal M2 Ultra performance. + * + * Memory Requirements: + * - 20 qubits: 1M states = 16MB + * - 22 qubits: 4.2M states = 67MB + * - 24 qubits: 16.8M states = 268MB + * - 26 qubits: 67M states = 1.1GB + * - 28 qubits: 268M states = 4.3GB (recommended max for speed) + * - 30 qubits: 1.07B states = 17.2GB + * - 32 qubits: 4.3B states = 68.7GB (feasible with 192GB RAM!) + * + * @param state Pointer to quantum state structure + * @param num_qubits Number of qubits (1-32, recommended 28 for speed/memory balance) + * @return QS_SUCCESS or error code + */ +qs_error_t quantum_state_init(quantum_state_t *state, size_t num_qubits); + +/** + * @brief Free quantum state resources + * @param state Quantum state to free + */ +void quantum_state_free(quantum_state_t *state); + +/** + * @brief Create arbitrary quantum state from amplitudes + * @param state Quantum state to initialize + * @param amplitudes Array of complex amplitudes + * @param dim Dimension of state space + * @return QS_SUCCESS or error code + */ +qs_error_t quantum_state_from_amplitudes( + quantum_state_t *state, + const complex_t *amplitudes, + size_t dim +); + +/** + * @brief Clone quantum state (deep copy) + * @param dest Destination state + * @param src Source state + * @return QS_SUCCESS or error code + */ +qs_error_t quantum_state_clone(quantum_state_t *dest, const quantum_state_t *src); + +/** + * @brief Reset state to |0...0⟩ + * @param state Quantum state to reset + */ +void quantum_state_reset(quantum_state_t *state); + +// ============================================================================ +// STATE PROPERTIES +// ============================================================================ + +/** + * @brief Check if state is normalized (Σ|αᵢ|² = 1) + * @param state Quantum state + * @param tolerance Tolerance for normalization check + * @return 1 if normalized, 0 otherwise + */ +int quantum_state_is_normalized(const quantum_state_t *state, double tolerance); + +/** + * @brief Normalize quantum state + * @param state Quantum state to normalize + * @return QS_SUCCESS or error code + */ +qs_error_t quantum_state_normalize(quantum_state_t *state); + +/** + * @brief Calculate von Neumann entropy S = -Tr(ρ log₂ ρ) + * @param state Quantum state + * @return Entropy in bits + */ +double quantum_state_entropy(const quantum_state_t *state); + +/** + * @brief Calculate state purity Tr(ρ²) + * @param state Quantum state + * @return Purity (1 for pure state, <1 for mixed) + */ +double quantum_state_purity(const quantum_state_t *state); + +/** + * @brief Calculate fidelity F = |⟨ψ|φ⟩|² + * @param state1 First quantum state + * @param state2 Second quantum state + * @return Fidelity between 0 and 1 + */ +double quantum_state_fidelity(const quantum_state_t *state1, const quantum_state_t *state2); + +/** + * @brief Get probability amplitude for basis state |i⟩ + * @param state Quantum state + * @param basis_index Index of basis state + * @return Complex amplitude αᵢ + */ +complex_t quantum_state_get_amplitude(const quantum_state_t *state, uint64_t basis_index); + +/** + * @brief Get probability for measuring basis state |i⟩ + * @param state Quantum state + * @param basis_index Index of basis state + * @return Probability |αᵢ|² + */ +double quantum_state_get_probability(const quantum_state_t *state, uint64_t basis_index); + +// ============================================================================ +// ENTANGLEMENT MEASURES +// ============================================================================ + +/** + * @brief Calculate entanglement entropy between subsystems + * + * Computes von Neumann entropy of reduced density matrix. + * For pure bipartite states: S(ρ_A) = S(ρ_B) quantifies entanglement. + * + * @param state Full quantum state + * @param qubits_subsystem_a Qubits in subsystem A + * @param num_qubits_a Number of qubits in A + * @return Entanglement entropy in bits + */ +double quantum_state_entanglement_entropy( + const quantum_state_t *state, + const int *qubits_subsystem_a, + size_t num_qubits_a +); + +/** + * @brief Compute reduced density matrix for subsystem + * + * Partial trace: ρ_A = Tr_B(|ψ⟩⟨ψ|) + * + * @param state Full quantum state + * @param qubits_to_trace Qubits to trace out + * @param num_traced Number of qubits to trace + * @param reduced_density Output: reduced density matrix + * @return QS_SUCCESS or error code + */ +qs_error_t quantum_state_partial_trace( + const quantum_state_t *state, + const int *qubits_to_trace, + size_t num_traced, + complex_t *reduced_density +); + +// ============================================================================ +// MEASUREMENT HISTORY +// ============================================================================ + +/** + * @brief Record measurement outcome + * @param state Quantum state + * @param outcome Measurement outcome (basis index) + */ +void quantum_state_record_measurement(quantum_state_t *state, uint64_t outcome); + +/** + * @brief Get measurement history + * @param state Quantum state + * @param outcomes Output array for outcomes + * @param max_outcomes Maximum outcomes to retrieve + * @return Number of measurements retrieved + */ +size_t quantum_state_get_measurement_history( + const quantum_state_t *state, + uint64_t *outcomes, + size_t max_outcomes +); + +/** + * @brief Clear measurement history + * @param state Quantum state + */ +void quantum_state_clear_measurements(quantum_state_t *state); + +// ============================================================================ +// UTILITY FUNCTIONS +// ============================================================================ + +/** + * @brief Print quantum state (debug) + * @param state Quantum state + * @param max_terms Maximum number of terms to print + */ +void quantum_state_print(const quantum_state_t *state, size_t max_terms); + +/** + * @brief Get string representation of basis state + * @param basis_index Basis state index + * @param num_qubits Number of qubits + * @param buffer Output buffer + * @param buffer_size Size of buffer + */ +void quantum_basis_state_string( + uint64_t basis_index, + size_t num_qubits, + char *buffer, + size_t buffer_size +); + +#endif /* QUANTUM_STATE_H */ \ No newline at end of file diff --git a/vendor/quantum_rng/src/quantum_rng/simd_ops.c b/vendor/quantum_rng/src/quantum_rng/simd_ops.c new file mode 100644 index 0000000..af080fe --- /dev/null +++ b/vendor/quantum_rng/src/quantum_rng/simd_ops.c @@ -0,0 +1,726 @@ +#include "simd_ops.h" +#include +#include +#include +#include + +// Platform detection +#ifdef __x86_64__ + #include + #ifdef __SSE2__ + #include // SSE2 + #endif + #ifdef __SSE3__ + #include // SSE3: _mm_hadd_pd, _mm_addsub_pd (used below) + #endif + #ifdef __AVX2__ + #include // AVX2 + #endif +#endif + +#if defined(__ARM_NEON) || defined(__ARM_NEON__) + #include + #define NEON_AVAILABLE 1 +#else + #define NEON_AVAILABLE 0 +#endif + +/** + * @file simd_ops.c + * @brief SIMD-optimized operations for quantum RNG + * + * Provides vectorized implementations with automatic fallback to scalar code. + * Expected speedups: + * - SSE2: 4-8x for complex operations + * - AVX2: 8-16x for large operations + * - ARM NEON: 4-8x on ARM processors + */ + +// ============================================================================ +// CPU FEATURE DETECTION +// ============================================================================ + +simd_capabilities_t simd_detect_capabilities(void) { + simd_capabilities_t caps = {0}; + +#ifdef __x86_64__ + unsigned int eax, ebx, ecx, edx; + + // Basic features (CPUID function 1) + if (__get_cpuid(1, &eax, &ebx, &ecx, &edx)) { + caps.has_sse2 = (edx & (1 << 26)) != 0; + caps.has_sse3 = (ecx & (1 << 0)) != 0; + caps.has_ssse3 = (ecx & (1 << 9)) != 0; + caps.has_sse4_1 = (ecx & (1 << 19)) != 0; + caps.has_avx = (ecx & (1 << 28)) != 0; + caps.has_fma = (ecx & (1 << 12)) != 0; + } + + // Extended features (CPUID function 7) + if (__get_cpuid_count(7, 0, &eax, &ebx, &ecx, &edx)) { + caps.has_avx2 = (ebx & (1 << 5)) != 0; + } +#elif defined(__aarch64__) + // ARM NEON is mandatory on AArch64 + caps.has_sse2 = 1; // Map to NEON capability +#endif + + return caps; +} + +const char* simd_capabilities_string(const simd_capabilities_t *caps) { + if (!caps) return "Unknown"; + + static char buffer[256]; + buffer[0] = '\0'; + +#ifdef __x86_64__ + if (caps->has_avx2) strcat(buffer, "AVX2 "); + if (caps->has_avx) strcat(buffer, "AVX "); + if (caps->has_fma) strcat(buffer, "FMA "); + if (caps->has_sse4_1) strcat(buffer, "SSE4.1 "); + if (caps->has_ssse3) strcat(buffer, "SSSE3 "); + if (caps->has_sse3) strcat(buffer, "SSE3 "); + if (caps->has_sse2) strcat(buffer, "SSE2 "); +#elif defined(__aarch64__) + if (caps->has_sse2) strcat(buffer, "ARM_NEON "); // sse2 flag used for NEON +#endif + + if (buffer[0] == '\0') { + return "Scalar only"; + } + + return buffer; +} + +// ============================================================================ +// VECTORIZED OPERATIONS +// ============================================================================ + +double simd_sum_squared_magnitudes(const complex_t *amplitudes, size_t n) { + if (!amplitudes || n == 0) return 0.0; + +#if defined(__AVX2__) && defined(__x86_64__) && !defined(__aarch64__) + // AVX2 implementation (4 doubles at once) + __m256d sum_vec = _mm256_setzero_pd(); + + size_t i = 0; + // Process 2 complex numbers (4 doubles) at a time + for (; i + 1 < n; i += 2) { + __m256d data = _mm256_loadu_pd((const double*)&litudes[i]); + __m256d sq = _mm256_mul_pd(data, data); + sum_vec = _mm256_add_pd(sum_vec, sq); + } + + // Horizontal sum of 4 doubles + __m128d sum_high = _mm256_extractf128_pd(sum_vec, 1); + __m128d sum_low = _mm256_castpd256_pd128(sum_vec); + __m128d sum128 = _mm_add_pd(sum_low, sum_high); + __m128d sum_final = _mm_hadd_pd(sum128, sum128); + + double sum = _mm_cvtsd_f64(sum_final); + + // Handle remaining elements + for (; i < n; i++) { + double re = creal(amplitudes[i]); + double im = cimag(amplitudes[i]); + sum += re * re + im * im; + } + + return sum; + +#elif defined(__SSE2__) && defined(__x86_64__) && !defined(__aarch64__) + // SSE2 implementation (2 doubles at once) + __m128d sum_vec = _mm_setzero_pd(); + + size_t i = 0; + // Process 1 complex number (2 doubles) at a time + for (; i < n; i++) { + __m128d data = _mm_loadu_pd((const double*)&litudes[i]); + __m128d sq = _mm_mul_pd(data, data); + sum_vec = _mm_add_pd(sum_vec, sq); + } + + // Horizontal sum + __m128d sum_final = _mm_hadd_pd(sum_vec, sum_vec); + return _mm_cvtsd_f64(sum_final); + +#elif NEON_AVAILABLE + // ARM NEON implementation (2 doubles per complex number) + float64x2_t sum_vec = vdupq_n_f64(0.0); + + for (size_t i = 0; i < n; i++) { + float64x2_t data = vld1q_f64((const double*)&litudes[i]); + float64x2_t sq = vmulq_f64(data, data); + sum_vec = vaddq_f64(sum_vec, sq); + } + + // Extract and sum the two lanes + return vgetq_lane_f64(sum_vec, 0) + vgetq_lane_f64(sum_vec, 1); + +#else + // Scalar fallback + double sum = 0.0; + for (size_t i = 0; i < n; i++) { + double re = creal(amplitudes[i]); + double im = cimag(amplitudes[i]); + sum += re * re + im * im; + } + return sum; +#endif +} + +void simd_normalize_amplitudes(complex_t *amplitudes, size_t n, double norm) { + if (!amplitudes || n == 0 || norm == 0.0) return; + +#if defined(__AVX2__) && defined(__x86_64__) && !defined(__aarch64__) + // AVX2: Process 4 doubles (2 complex) at a time + __m256d norm_vec = _mm256_set1_pd(norm); + + size_t i = 0; + for (; i + 1 < n; i += 2) { + __m256d data = _mm256_loadu_pd((double*)&litudes[i]); + __m256d normalized = _mm256_div_pd(data, norm_vec); + _mm256_storeu_pd((double*)&litudes[i], normalized); + } + + // Handle remaining elements + for (; i < n; i++) { + amplitudes[i] /= norm; + } + +#elif defined(__SSE2__) && defined(__x86_64__) && !defined(__aarch64__) + // SSE2: Process 2 doubles (1 complex) at a time + __m128d norm_vec = _mm_set1_pd(norm); + + for (size_t i = 0; i < n; i++) { + __m128d data = _mm_loadu_pd((double*)&litudes[i]); + __m128d normalized = _mm_div_pd(data, norm_vec); + _mm_storeu_pd((double*)&litudes[i], normalized); + } + +#elif NEON_AVAILABLE + // ARM NEON implementation (2 doubles per complex number) + float64x2_t norm_vec = vdupq_n_f64(norm); + + for (size_t i = 0; i < n; i++) { + float64x2_t data = vld1q_f64((double*)&litudes[i]); + float64x2_t normalized = vdivq_f64(data, norm_vec); + vst1q_f64((double*)&litudes[i], normalized); + } + +#else + // Scalar fallback + for (size_t i = 0; i < n; i++) { + amplitudes[i] /= norm; + } +#endif +} + +void simd_matrix2x2_vec_multiply( + const complex_t matrix[4], + const complex_t input[2], + complex_t output[2] +) { + if (!matrix || !input || !output) return; + +#if defined(__SSE2__) && defined(__x86_64__) && !defined(__aarch64__) + // Load matrix elements + __m128d m00 = _mm_loadu_pd((const double*)&matrix[0]); // a + bi + __m128d m01 = _mm_loadu_pd((const double*)&matrix[1]); // c + di + __m128d m10 = _mm_loadu_pd((const double*)&matrix[2]); // e + fi + __m128d m11 = _mm_loadu_pd((const double*)&matrix[3]); // g + hi + + // Load input + __m128d v0 = _mm_loadu_pd((const double*)&input[0]); + __m128d v1 = _mm_loadu_pd((const double*)&input[1]); + + // Complex multiplication: (a+bi)(c+di) = (ac-bd) + (ad+bc)i + // For matrix-vector: out[0] = m00*v0 + m01*v1 + + // m00 * v0 + __m128d v0_flip = _mm_shuffle_pd(v0, v0, 1); // Swap real/imag + __m128d m00_real = _mm_shuffle_pd(m00, m00, 0); // Broadcast real + __m128d m00_imag = _mm_shuffle_pd(m00, m00, 3); // Broadcast imag + + __m128d prod00_real = _mm_mul_pd(m00_real, v0); + __m128d prod00_imag = _mm_mul_pd(m00_imag, v0_flip); + + __m128d result0 = _mm_addsub_pd(prod00_real, prod00_imag); + + // m01 * v1 (similar) + __m128d v1_flip = _mm_shuffle_pd(v1, v1, 1); + __m128d m01_real = _mm_shuffle_pd(m01, m01, 0); + __m128d m01_imag = _mm_shuffle_pd(m01, m01, 3); + + __m128d prod01_real = _mm_mul_pd(m01_real, v1); + __m128d prod01_imag = _mm_mul_pd(m01_imag, v1_flip); + + result0 = _mm_add_pd(result0, _mm_addsub_pd(prod01_real, prod01_imag)); + + _mm_storeu_pd((double*)&output[0], result0); + + // out[1] = m10*v0 + m11*v1 + __m128d m10_real = _mm_shuffle_pd(m10, m10, 0); + __m128d m10_imag = _mm_shuffle_pd(m10, m10, 3); + + __m128d prod10_real = _mm_mul_pd(m10_real, v0); + __m128d prod10_imag = _mm_mul_pd(m10_imag, v0_flip); + + __m128d result1 = _mm_addsub_pd(prod10_real, prod10_imag); + + __m128d m11_real = _mm_shuffle_pd(m11, m11, 0); + __m128d m11_imag = _mm_shuffle_pd(m11, m11, 3); + + __m128d prod11_real = _mm_mul_pd(m11_real, v1); + __m128d prod11_imag = _mm_mul_pd(m11_imag, v1_flip); + + result1 = _mm_add_pd(result1, _mm_addsub_pd(prod11_real, prod11_imag)); + + _mm_storeu_pd((double*)&output[1], result1); + +#else + // Scalar fallback - standard complex multiplication + output[0] = matrix[0] * input[0] + matrix[1] * input[1]; + output[1] = matrix[2] * input[0] + matrix[3] * input[1]; +#endif +} + +complex_t simd_complex_multiply(complex_t z1, complex_t z2) { +#if defined(__SSE2__) && defined(__x86_64__) && !defined(__aarch64__) + __m128d a = _mm_loadu_pd((const double*)&z1); + __m128d b = _mm_loadu_pd((const double*)&z2); + + // (a+bi)(c+di) = (ac-bd) + (ad+bc)i + __m128d b_flip = _mm_shuffle_pd(b, b, 1); // di, c + __m128d a_real = _mm_shuffle_pd(a, a, 0); // a, a + __m128d a_imag = _mm_shuffle_pd(a, a, 3); // b, b + + __m128d prod_real = _mm_mul_pd(a_real, b); // ac, ad + __m128d prod_imag = _mm_mul_pd(a_imag, b_flip); // bd, bc + + __m128d result = _mm_addsub_pd(prod_real, prod_imag); // ac-bd, ad+bc + + complex_t output; + _mm_storeu_pd((double*)&output, result); + return output; + +#else + return z1 * z2; +#endif +} + +void simd_compute_probabilities( + const complex_t *amplitudes, + double *probabilities, + size_t n +) { + if (!amplitudes || !probabilities || n == 0) return; + +#if defined(__AVX2__) && defined(__x86_64__) && !defined(__aarch64__) + // AVX2: Process 2 complex (4 doubles) at once + size_t i = 0; + for (; i + 1 < n; i += 2) { + __m256d data = _mm256_loadu_pd((const double*)&litudes[i]); + __m256d sq = _mm256_mul_pd(data, data); + + // Horizontal add within each complex number + __m256d sum = _mm256_hadd_pd(sq, sq); + + // Extract probabilities + double probs[4]; + _mm256_storeu_pd(probs, sum); + probabilities[i] = probs[0]; + probabilities[i+1] = probs[2]; + } + + // Handle remaining + for (; i < n; i++) { + double re = creal(amplitudes[i]); + double im = cimag(amplitudes[i]); + probabilities[i] = re * re + im * im; + } + +#elif defined(__SSE2__) && defined(__x86_64__) && !defined(__aarch64__) + // SSE2: Process 1 complex (2 doubles) at once + for (size_t i = 0; i < n; i++) { + __m128d data = _mm_loadu_pd((const double*)&litudes[i]); + __m128d sq = _mm_mul_pd(data, data); + __m128d sum = _mm_hadd_pd(sq, sq); + probabilities[i] = _mm_cvtsd_f64(sum); + } + +#else + // Scalar fallback + for (size_t i = 0; i < n; i++) { + double re = creal(amplitudes[i]); + double im = cimag(amplitudes[i]); + probabilities[i] = re * re + im * im; + } +#endif +} + +// ============================================================================ +// ENTROPY MIXING OPERATIONS +// ============================================================================ + +void simd_xor_bytes(uint8_t *dest, const uint8_t *src, size_t n) { + if (!dest || !src || n == 0) return; + +#if defined(__AVX2__) && defined(__x86_64__) && !defined(__aarch64__) + // AVX2: Process 32 bytes at once + size_t i = 0; + for (; i + 31 < n; i += 32) { + __m256i d = _mm256_loadu_si256((const __m256i*)(dest + i)); + __m256i s = _mm256_loadu_si256((const __m256i*)(src + i)); + __m256i result = _mm256_xor_si256(d, s); + _mm256_storeu_si256((__m256i*)(dest + i), result); + } + + // Handle remaining bytes + for (; i < n; i++) { + dest[i] ^= src[i]; + } + +#elif defined(__SSE2__) && defined(__x86_64__) && !defined(__aarch64__) + // SSE2: Process 16 bytes at once + size_t i = 0; + for (; i + 15 < n; i += 16) { + __m128i d = _mm_loadu_si128((const __m128i*)(dest + i)); + __m128i s = _mm_loadu_si128((const __m128i*)(src + i)); + __m128i result = _mm_xor_si128(d, s); + _mm_storeu_si128((__m128i*)(dest + i), result); + } + + // Handle remaining bytes + for (; i < n; i++) { + dest[i] ^= src[i]; + } + +#else + // Scalar fallback + for (size_t i = 0; i < n; i++) { + dest[i] ^= src[i]; + } +#endif +} + +void simd_mix_entropy( + const uint8_t *state, + const uint8_t *entropy, + uint8_t *output, + size_t size +) { + if (!state || !entropy || !output || size == 0) return; + + // Copy state to output, then XOR with entropy + memcpy(output, state, size); + simd_xor_bytes(output, entropy, size); + +#if defined(__AVX2__) && !defined(__aarch64__) + // Additional mixing with bit rotation (AVX2) + size_t i = 0; + for (; i + 31 < size; i += 32) { + __m256i data = _mm256_loadu_si256((const __m256i*)(output + i)); + + // Rotate left by 3 bits + __m256i rotated = _mm256_or_si256( + _mm256_slli_epi32(data, 3), + _mm256_srli_epi32(data, 29) + ); + + // XOR with rotation + __m256i mixed = _mm256_xor_si256(data, rotated); + _mm256_storeu_si256((__m256i*)(output + i), mixed); + } + + // Handle remaining with scalar + for (; i < size; i++) { + output[i] ^= (output[i] << 3) | (output[i] >> 5); + } + +#elif defined(__SSE2__) && defined(__x86_64__) && !defined(__aarch64__) + // SSE2 version + size_t i = 0; + for (; i + 15 < size; i += 16) { + __m128i data = _mm_loadu_si128((const __m128i*)(output + i)); + __m128i rotated = _mm_or_si128( + _mm_slli_epi32(data, 3), + _mm_srli_epi32(data, 29) + ); + __m128i mixed = _mm_xor_si128(data, rotated); + _mm_storeu_si128((__m128i*)(output + i), mixed); + } + + for (; i < size; i++) { + output[i] ^= (output[i] << 3) | (output[i] >> 5); + } + +#else + // Scalar mixing with rotation + for (size_t i = 0; i < size; i++) { + output[i] ^= (output[i] << 3) | (output[i] >> 5); + } +#endif +} + +// ============================================================================ +// QUANTUM GATE PRIMITIVES (SIMD-OPTIMIZED) +// ============================================================================ + +void simd_complex_swap(complex_t *amp0, complex_t *amp1, size_t n) { + if (!amp0 || !amp1 || n == 0) return; + +#if NEON_AVAILABLE + // ARM NEON: Process 1 complex number (2 doubles) at a time + for (size_t i = 0; i < n; i++) { + float64x2_t a = vld1q_f64((double*)&0[i]); + float64x2_t b = vld1q_f64((double*)&1[i]); + + vst1q_f64((double*)&0[i], b); + vst1q_f64((double*)&1[i], a); + } + +#elif defined(__SSE2__) && defined(__x86_64__) + // SSE2: Process 1 complex number (2 doubles) at a time + for (size_t i = 0; i < n; i++) { + __m128d a = _mm_loadu_pd((double*)&0[i]); + __m128d b = _mm_loadu_pd((double*)&1[i]); + + _mm_storeu_pd((double*)&0[i], b); + _mm_storeu_pd((double*)&1[i], a); + } + +#else + // Scalar fallback + for (size_t i = 0; i < n; i++) { + complex_t temp = amp0[i]; + amp0[i] = amp1[i]; + amp1[i] = temp; + } +#endif +} + +void simd_multiply_by_i(complex_t *amplitudes, size_t n, int negate) { + if (!amplitudes || n == 0) return; + +#if NEON_AVAILABLE + // ARM NEON: Multiply by ±i = swap real/imag and negate real + // (a + bi) * i = -b + ai + // (a + bi) * (-i) = b - ai + + for (size_t i = 0; i < n; i++) { + float64x2_t data = vld1q_f64((double*)&litudes[i]); + + // Swap real and imaginary parts: [re, im] -> [im, re] + float64x2_t swapped = vextq_f64(data, data, 1); + + // Negate first lane (was real, now im position) + float64x2_t neg_mask = {-1.0, 1.0}; + if (negate) { + neg_mask = (float64x2_t){1.0, -1.0}; + } + + float64x2_t result = vmulq_f64(swapped, neg_mask); + vst1q_f64((double*)&litudes[i], result); + } + +#elif defined(__SSE2__) && defined(__x86_64__) + // SSE2: Similar approach + for (size_t i = 0; i < n; i++) { + __m128d data = _mm_loadu_pd((double*)&litudes[i]); + + // Swap real and imaginary + __m128d swapped = _mm_shuffle_pd(data, data, 1); + + // Negate first element + __m128d neg_mask = negate ? + _mm_set_pd(-1.0, 1.0) : + _mm_set_pd(1.0, -1.0); + + __m128d result = _mm_mul_pd(swapped, neg_mask); + _mm_storeu_pd((double*)&litudes[i], result); + } + +#else + // Scalar fallback + complex_t factor = negate ? -I : I; + for (size_t i = 0; i < n; i++) { + amplitudes[i] *= factor; + } +#endif +} + +void simd_negate(complex_t *amplitudes, size_t n) { + if (!amplitudes || n == 0) return; + +#if NEON_AVAILABLE + // ARM NEON: Negate both real and imaginary parts + float64x2_t neg_one = vdupq_n_f64(-1.0); + + for (size_t i = 0; i < n; i++) { + float64x2_t data = vld1q_f64((double*)&litudes[i]); + float64x2_t result = vmulq_f64(data, neg_one); + vst1q_f64((double*)&litudes[i], result); + } + +#elif defined(__SSE2__) && defined(__x86_64__) + // SSE2: Negate both components + __m128d neg_one = _mm_set1_pd(-1.0); + + for (size_t i = 0; i < n; i++) { + __m128d data = _mm_loadu_pd((double*)&litudes[i]); + __m128d result = _mm_mul_pd(data, neg_one); + _mm_storeu_pd((double*)&litudes[i], result); + } + +#else + // Scalar fallback + for (size_t i = 0; i < n; i++) { + amplitudes[i] = -amplitudes[i]; + } +#endif +} + +void simd_apply_phase(complex_t *amplitudes, complex_t phase, size_t n) { + if (!amplitudes || n == 0) return; + +#if NEON_AVAILABLE + // ARM NEON: Complex multiplication by phase + // (a+bi)(c+di) = (ac-bd) + (ad+bc)i + + double phase_re = creal(phase); + double phase_im = cimag(phase); + + float64x2_t phase_real_vec = vdupq_n_f64(phase_re); // [c, c] + float64x2_t phase_imag_vec = vdupq_n_f64(phase_im); // [d, d] + + for (size_t i = 0; i < n; i++) { + float64x2_t amp = vld1q_f64((double*)&litudes[i]); // [a, b] + + // Compute ac and ad + float64x2_t prod_real = vmulq_f64(amp, phase_real_vec); // [ac, bc] + + // Swap amplitude components for second product: [b, a] + float64x2_t amp_swap = vextq_f64(amp, amp, 1); + + // Compute bd and ad + float64x2_t prod_imag = vmulq_f64(amp_swap, phase_imag_vec); // [bd, ad] + + // Result: [ac-bd, bc+ad] = [ac-bd, ad+bc] + // Need to negate the bd term in first position + float64x2_t neg_mask = {-1.0, 1.0}; + prod_imag = vmulq_f64(prod_imag, neg_mask); // [-bd, ad] + + float64x2_t result = vaddq_f64(prod_real, prod_imag); // [ac-bd, bc+ad] + vst1q_f64((double*)&litudes[i], result); + } + +#elif defined(__SSE2__) && defined(__x86_64__) + // SSE2: Use existing simd_complex_multiply logic + for (size_t i = 0; i < n; i++) { + amplitudes[i] = simd_complex_multiply(amplitudes[i], phase); + } + +#else + // Scalar fallback + for (size_t i = 0; i < n; i++) { + amplitudes[i] *= phase; + } +#endif +} + +// ============================================================================ +// QUANTUM MEASUREMENT OPTIMIZATIONS (CRITICAL FOR GROVER) +// ============================================================================ + +uint64_t simd_cumulative_probability_search( + const complex_t *amplitudes, + size_t n, + double random_threshold +) { + if (!amplitudes || n == 0) return 0; + + double cumulative = 0.0; + +#if defined(__ARM_NEON) || defined(__aarch64__) + // ARM NEON: Process 2 complex numbers (4 doubles) at once + size_t i = 0; + + for (; i + 1 < n; i += 2) { + // Load 2 complex numbers + float64x2_t amp0 = vld1q_f64((const double*)&litudes[i]); + float64x2_t amp1 = vld1q_f64((const double*)&litudes[i+1]); + + // Compute |amp|^2 for both + float64x2_t mag0_sq = vmulq_f64(amp0, amp0); + float64x2_t mag1_sq = vmulq_f64(amp1, amp1); + + // Horizontal add to get probabilities + double prob0 = vgetq_lane_f64(mag0_sq, 0) + vgetq_lane_f64(mag0_sq, 1); + double prob1 = vgetq_lane_f64(mag1_sq, 0) + vgetq_lane_f64(mag1_sq, 1); + + cumulative += prob0; + if (random_threshold < cumulative) return i; + + cumulative += prob1; + if (random_threshold < cumulative) return i + 1; + } + + // Handle remainder + for (; i < n; i++) { + double re = creal(amplitudes[i]); + double im = cimag(amplitudes[i]); + cumulative += re * re + im * im; + if (random_threshold < cumulative) return i; + } + +#elif defined(__AVX2__) && defined(__x86_64__) + // AVX2: Process 2 complex numbers at once + size_t i = 0; + + for (; i + 1 < n; i += 2) { + __m256d data = _mm256_loadu_pd((const double*)&litudes[i]); + __m256d sq = _mm256_mul_pd(data, data); + __m256d sum = _mm256_hadd_pd(sq, sq); + + double probs[4]; + _mm256_storeu_pd(probs, sum); + + cumulative += probs[0]; + if (random_threshold < cumulative) return i; + + cumulative += probs[2]; + if (random_threshold < cumulative) return i + 1; + } + + // Handle remainder + for (; i < n; i++) { + double re = creal(amplitudes[i]); + double im = cimag(amplitudes[i]); + cumulative += re * re + im * im; + if (random_threshold < cumulative) return i; + } + +#else + // Scalar fallback + for (size_t i = 0; i < n; i++) { + double re = creal(amplitudes[i]); + double im = cimag(amplitudes[i]); + cumulative += re * re + im * im; + if (random_threshold < cumulative) return i; + } +#endif + + return n - 1; // Fallback +} + +uint64_t simd_fast_measurement_sample( + const complex_t *amplitudes, + size_t n, + double random_threshold +) { + // For now, use the cumulative search (already optimized) + // Could be further optimized with probability pre-computation if needed + return simd_cumulative_probability_search(amplitudes, n, random_threshold); +} \ No newline at end of file diff --git a/vendor/quantum_rng/src/quantum_rng/simd_ops.h b/vendor/quantum_rng/src/quantum_rng/simd_ops.h new file mode 100644 index 0000000..ba18085 --- /dev/null +++ b/vendor/quantum_rng/src/quantum_rng/simd_ops.h @@ -0,0 +1,254 @@ +#ifndef SIMD_OPS_H +#define SIMD_OPS_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file simd_ops.h + * @brief SIMD-optimized operations for quantum RNG + * + * Provides SSE2/AVX2 vectorized implementations of performance-critical + * operations with automatic fallback to scalar implementations. + * + * Key optimizations: + * - Complex number arithmetic (SSE2) + * - Matrix operations (AVX2 if available) + * - Normalization calculations + * - Probability computations + * + * Runtime CPU feature detection ensures optimal code path selection. + */ + +// Complex type (compatible with quantum_state.h) +typedef double _Complex complex_t; + +// ============================================================================ +// CPU FEATURE DETECTION +// ============================================================================ + +/** + * @brief CPU SIMD capabilities + */ +typedef struct { + int has_sse2; /**< SSE2 support (2001+) */ + int has_sse3; /**< SSE3 support (2004+) */ + int has_ssse3; /**< SSSE3 support (2006+) */ + int has_sse4_1; /**< SSE4.1 support (2007+) */ + int has_avx; /**< AVX support (2011+) */ + int has_avx2; /**< AVX2 support (2013+) */ + int has_fma; /**< FMA3 support */ +} simd_capabilities_t; + +/** + * @brief Detect CPU SIMD capabilities + * + * @return SIMD capabilities structure + */ +simd_capabilities_t simd_detect_capabilities(void); + +/** + * @brief Get CPU capabilities as string + * + * @param caps Capabilities structure + * @return Human-readable string + */ +const char* simd_capabilities_string(const simd_capabilities_t *caps); + +// ============================================================================ +// VECTORIZED OPERATIONS +// ============================================================================ + +/** + * @brief Compute sum of squared magnitudes (vectorized) + * + * Calculates Σ|αᵢ|² for normalization checks. + * Uses SSE2/AVX2 when available. + * + * @param amplitudes Complex amplitude array + * @param n Number of amplitudes + * @return Sum of squared magnitudes + */ +double simd_sum_squared_magnitudes(const complex_t *amplitudes, size_t n); + +/** + * @brief Normalize complex amplitude array (vectorized) + * + * Divides all amplitudes by sqrt(Σ|αᵢ|²). + * Uses SSE2/AVX2 when available. + * + * @param amplitudes Complex amplitude array (modified in-place) + * @param n Number of amplitudes + * @param norm Normalization factor (sqrt of sum squared magnitudes) + */ +void simd_normalize_amplitudes(complex_t *amplitudes, size_t n, double norm); + +/** + * @brief Matrix-vector multiply for 2x2 complex matrices (vectorized) + * + * Optimized for quantum gate operations. + * Uses SSE2 for complex arithmetic. + * + * @param matrix 2x2 complex matrix (row-major) + * @param input 2-element complex vector + * @param output 2-element complex vector (output) + */ +void simd_matrix2x2_vec_multiply( + const complex_t matrix[4], + const complex_t input[2], + complex_t output[2] +); + +/** + * @brief Complex multiplication (vectorized) + * + * Computes (a + bi) * (c + di) using SSE2. + * + * @param z1 First complex number + * @param z2 Second complex number + * @return Product z1 * z2 + */ +complex_t simd_complex_multiply(complex_t z1, complex_t z2); + +/** + * @brief Batch compute probabilities from amplitudes (vectorized) + * + * Computes |α|² for array of amplitudes. + * Uses SSE2/AVX2 when available. + * + * @param amplitudes Input amplitudes + * @param probabilities Output probabilities + * @param n Number of elements + */ +void simd_compute_probabilities( + const complex_t *amplitudes, + double *probabilities, + size_t n +); + +// ============================================================================ +// QUANTUM GATE PRIMITIVES (SIMD-OPTIMIZED) +// ============================================================================ + +/** + * @brief Vectorized complex swap for Pauli X gate + * + * Efficiently swaps pairs of complex amplitudes using SIMD. + * Used by Pauli X and CNOT gates. + * + * @param amp0 First amplitude array + * @param amp1 Second amplitude array + * @param n Number of pairs to swap + */ +void simd_complex_swap(complex_t *amp0, complex_t *amp1, size_t n); + +/** + * @brief Vectorized multiply by i for Pauli Y components + * + * Efficiently multiplies complex numbers by ±i using SIMD. + * + * @param amplitudes Amplitude array to modify + * @param n Number of amplitudes + * @param negate If true, multiply by -i; otherwise by +i + */ +void simd_multiply_by_i(complex_t *amplitudes, size_t n, int negate); + +/** + * @brief Vectorized negate for Pauli Z gate + * + * Efficiently negates complex amplitudes using SIMD. + * + * @param amplitudes Amplitude array to negate + * @param n Number of amplitudes + */ +void simd_negate(complex_t *amplitudes, size_t n); + +/** + * @brief Vectorized phase multiplication + * + * Efficiently multiplies amplitudes by a phase factor e^(iθ). + * Used by S, T, and Phase gates. + * + * @param amplitudes Amplitude array to modify + * @param phase Complex phase factor + * @param n Number of amplitudes + */ +void simd_apply_phase(complex_t *amplitudes, complex_t phase, size_t n); + +// ============================================================================ +// ENTROPY MIXING OPERATIONS +// ============================================================================ + +/** + * @brief XOR mixing of byte arrays (vectorized) + * + * Performs dest[i] ^= src[i] using SSE2/AVX2. + * + * @param dest Destination array (modified in-place) + * @param src Source array + * @param n Number of bytes + */ +void simd_xor_bytes(uint8_t *dest, const uint8_t *src, size_t n); + +/** + * @brief Fast entropy mixing using SIMD + * + * Mixes entropy buffers with cryptographic quality using + * vectorized operations. + * + * @param state Current state buffer + * @param entropy New entropy to mix + * @param output Mixed output + * @param size Buffer size + */ +void simd_mix_entropy( + const uint8_t *state, + const uint8_t *entropy, + uint8_t *output, + size_t size +); + +/** + * @brief SIMD-optimized cumulative probability search + * + * Finds the index where cumulative probability exceeds threshold. + * Critical for fast quantum measurement sampling. + * + * @param amplitudes Complex amplitude array + * @param n Number of amplitudes + * @param random_threshold Random value in [0,1) for sampling + * @return Index where cumulative probability exceeds threshold + */ +uint64_t simd_cumulative_probability_search( + const complex_t *amplitudes, + size_t n, + double random_threshold +); + +/** + * @brief Batch compute probabilities and find cumulative threshold + * + * Optimized version that pre-computes all probabilities with SIMD, + * then does fast cumulative sum. + * + * @param amplitudes Complex amplitude array + * @param n Number of amplitudes + * @param random_threshold Random value for sampling + * @return Sampled index + */ +uint64_t simd_fast_measurement_sample( + const complex_t *amplitudes, + size_t n, + double random_threshold +); + +#ifdef __cplusplus +} +#endif + +#endif /* SIMD_OPS_H */ \ No newline at end of file