commit
e3dc685891
12 changed files with 818 additions and 0 deletions
@ -0,0 +1,35 @@ |
|||||
|
# Copilot Instructions |
||||
|
|
||||
|
## Communication Preferences |
||||
|
|
||||
|
- Ich bevorzuge die Kommunikation auf Deutsch |
||||
|
- Bitte antworte auf meine Fragen auf Deutsch |
||||
|
- Schreibe technische Erklärungen, Anleitungen und Hilfestellungen auf Deutsch |
||||
|
- Der generierte Code selbst soll jedoch auf Englisch sein |
||||
|
|
||||
|
## Language for Code |
||||
|
|
||||
|
- All code comments should be written in English |
||||
|
- All console output messages should be in English |
||||
|
- All documentation (README files, inline documentation, etc.) should be written in English |
||||
|
- Variable names and function names should follow English naming conventions |
||||
|
|
||||
|
## Code Style |
||||
|
|
||||
|
- Follow idiomatic Zig style |
||||
|
- Use clear and descriptive variable and function names |
||||
|
- Provide appropriate documentation for functions and complex logic |
||||
|
- Maintain the existing project structure and organization |
||||
|
|
||||
|
## Technical Requirements |
||||
|
|
||||
|
- The project uses Zig version 0.14.1 |
||||
|
- New features and syntax introduced in Zig 0.14.1 should be leveraged when appropriate |
||||
|
- Generated code must be compatible with Zig 0.14.1 compiler |
||||
|
- Build scripts should follow Zig 0.14.1 build system conventions |
||||
|
|
||||
|
## Features |
||||
|
|
||||
|
- The Pi-Finder library is focused on simplicity and performance |
||||
|
- JSON is the standard output format |
||||
|
- Any new features should maintain backward compatibility |
||||
@ -0,0 +1,42 @@ |
|||||
|
# Zig build artifacts |
||||
|
zig-out/ |
||||
|
zig-cache/ |
||||
|
.zig-cache/ |
||||
|
|
||||
|
# Pi decimal file (may be large) |
||||
|
*.txt |
||||
|
|
||||
|
# IDE and editor files |
||||
|
.idea/ |
||||
|
.vscode/ |
||||
|
*.swp |
||||
|
*.swo |
||||
|
*~ |
||||
|
|
||||
|
# MacOS specific files |
||||
|
.DS_Store |
||||
|
.AppleDouble |
||||
|
.LSOverride |
||||
|
Icon? |
||||
|
._* |
||||
|
.Spotlight-V100 |
||||
|
.Trashes |
||||
|
|
||||
|
# Backup files |
||||
|
*.bak |
||||
|
*.tmp |
||||
|
*.backup |
||||
|
|
||||
|
# Local development environment settings |
||||
|
.env |
||||
|
.envrc |
||||
|
.direnv/ |
||||
|
|
||||
|
# Test artifacts |
||||
|
**/test-results/ |
||||
|
|
||||
|
# Dependency directories |
||||
|
/deps/ |
||||
|
|
||||
|
# Dynamic data directory |
||||
|
data/ |
||||
@ -0,0 +1,128 @@ |
|||||
|
# Pi Finder Library |
||||
|
|
||||
|
A Zig library for searching numeric sequences in the decimal places of Pi. |
||||
|
|
||||
|
## Features |
||||
|
|
||||
|
- Efficient searching of numeric sequences in Pi decimal places |
||||
|
- JSON output as standard |
||||
|
- Optional plain text output format |
||||
|
- Configurable context display with delimiters |
||||
|
- Reusable module design |
||||
|
- Performance metrics |
||||
|
|
||||
|
## Installation |
||||
|
|
||||
|
### As a Dependency in a Zig Project |
||||
|
|
||||
|
Add this library as a dependency in your `build.zig.zon`: |
||||
|
|
||||
|
```zig |
||||
|
.dependencies = .{ |
||||
|
.@"pi-finder" = .{ |
||||
|
.path = "path/to/pi-finder-lib", |
||||
|
}, |
||||
|
}, |
||||
|
``` |
||||
|
|
||||
|
### In your build.zig |
||||
|
|
||||
|
```zig |
||||
|
const pi_finder_dep = b.dependency("pi-finder", .{ |
||||
|
.target = target, |
||||
|
.optimize = optimize, |
||||
|
}); |
||||
|
|
||||
|
exe.root_module.addImport("pi-finder", pi_finder_dep.module("pi-finder")); |
||||
|
``` |
||||
|
|
||||
|
## Usage |
||||
|
|
||||
|
### Basic Usage |
||||
|
|
||||
|
```zig |
||||
|
const std = @import("std"); |
||||
|
const PiFinder = @import("pi-finder"); |
||||
|
|
||||
|
pub fn main() !void { |
||||
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){}; |
||||
|
defer _ = gpa.deinit(); |
||||
|
const allocator = gpa.allocator(); |
||||
|
|
||||
|
// Initialize Pi Finder |
||||
|
var pi_finder = try PiFinder.PiFinder.init(allocator, "pi-data.txt"); |
||||
|
defer pi_finder.deinit(); |
||||
|
|
||||
|
// Configure context |
||||
|
const context_config = PiFinder.ContextConfig{ |
||||
|
.size = 50, |
||||
|
.position = 25, |
||||
|
.delimiter = "-", |
||||
|
.delimiter_interval = 2, |
||||
|
}; |
||||
|
|
||||
|
// Search for sequence |
||||
|
const result = try pi_finder.searchSequence("123456", context_config, allocator); |
||||
|
|
||||
|
// Output as JSON (default format) |
||||
|
const json_output = try PiFinder.formatSearchResultAsJson(allocator, result); |
||||
|
defer allocator.free(json_output); |
||||
|
|
||||
|
std.debug.print("{s}\n", .{json_output}); |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
### Using Plain Text Output Format |
||||
|
|
||||
|
```zig |
||||
|
// After getting the search result |
||||
|
const plain_text_output = try PiFinder.formatSearchResultAsPlainText(allocator, result); |
||||
|
defer allocator.free(plain_text_output); |
||||
|
|
||||
|
std.debug.print("{s}\n", .{plain_text_output}); |
||||
|
``` |
||||
|
|
||||
|
## API |
||||
|
|
||||
|
### PiFinder |
||||
|
|
||||
|
- `init(allocator, file_path)` - Initializes the Pi Finder |
||||
|
- `deinit()` - Frees allocated memory |
||||
|
- `findSequenceInPi(sequence)` - Searches for a sequence |
||||
|
- `getContext(position, config, sequence, allocator)` - Generates context |
||||
|
- `searchSequence(sequence, config, allocator)` - Comprehensive search with structured result |
||||
|
|
||||
|
### Output Format Functions |
||||
|
|
||||
|
- `formatSearchResultAsJson(allocator, result)` - Formats search result as JSON |
||||
|
- `formatSearchResultAsPlainText(allocator, result)` - Formats search result as plain text |
||||
|
|
||||
|
### ContextConfig |
||||
|
|
||||
|
Configuration for context display: |
||||
|
|
||||
|
- `size` - Size of the context |
||||
|
- `position` - Position of the sequence within the context |
||||
|
- `delimiter` - Delimiter character (optional) |
||||
|
- `delimiter_interval` - Interval for delimiter insertion |
||||
|
- `prefix` - Prefix for the sequence |
||||
|
- `suffix` - Suffix for the sequence |
||||
|
|
||||
|
### SearchResult |
||||
|
|
||||
|
Structured result containing: |
||||
|
|
||||
|
- `success` - Search success status |
||||
|
- `sequence` - Searched sequence |
||||
|
- `position` - Position in Pi (if found) |
||||
|
- `context` - Context around the position |
||||
|
- `error_message` - Error message (if any) |
||||
|
- `performance` - Performance metrics |
||||
|
- `context_config` - Used context configuration |
||||
|
|
||||
|
## Examples |
||||
|
|
||||
|
See the `examples/` directory for complete examples: |
||||
|
|
||||
|
- `birth-date-finder/` - Basic example for searching birth dates |
||||
|
- `birth-date-finder/src/plain_text_example.zig` - Example using plain text output format |
||||
@ -0,0 +1,33 @@ |
|||||
|
const std = @import("std"); |
||||
|
|
||||
|
pub fn build(b: *std.Build) void { |
||||
|
const target = b.standardTargetOptions(.{}); |
||||
|
const optimize = b.standardOptimizeOption(.{}); |
||||
|
|
||||
|
// Create a module for the library |
||||
|
_ = b.addModule("pi-finder", .{ |
||||
|
.root_source_file = b.path("src/lib.zig"), |
||||
|
.target = target, |
||||
|
.optimize = optimize, |
||||
|
}); |
||||
|
|
||||
|
// Library |
||||
|
const lib = b.addStaticLibrary(.{ |
||||
|
.name = "pi-finder", |
||||
|
.root_source_file = b.path("src/lib.zig"), |
||||
|
.target = target, |
||||
|
.optimize = optimize, |
||||
|
}); |
||||
|
b.installArtifact(lib); |
||||
|
|
||||
|
// Tests |
||||
|
const lib_unit_tests = b.addTest(.{ |
||||
|
.root_source_file = b.path("src/lib.zig"), |
||||
|
.target = target, |
||||
|
.optimize = optimize, |
||||
|
}); |
||||
|
|
||||
|
const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests); |
||||
|
const test_step = b.step("test", "Run unit tests"); |
||||
|
test_step.dependOn(&run_lib_unit_tests.step); |
||||
|
} |
||||
@ -0,0 +1,4 @@ |
|||||
|
#!/bin/bash |
||||
|
|
||||
|
[ ! -d ./data ] && mkdir ./data |
||||
|
curl -o ./data/pi-decimal-10_000_000_000.txt https://einklich.net/etc/pi-decimal-10%5E9.txt |
||||
@ -0,0 +1,74 @@ |
|||||
|
# Birth Date Finder Example |
||||
|
|
||||
|
This example demonstrates how the Pi Finder Library is used to search for a birth date (01.01.02) in the decimal places of Pi. |
||||
|
|
||||
|
## Execution |
||||
|
|
||||
|
```bash |
||||
|
cd birth-date-finder |
||||
|
zig build run |
||||
|
``` |
||||
|
|
||||
|
## Additional Examples |
||||
|
|
||||
|
This project includes two additional examples: |
||||
|
|
||||
|
### 1. Multiple Test Example |
||||
|
|
||||
|
```bash |
||||
|
zig build test-multiple |
||||
|
``` |
||||
|
|
||||
|
This example tests multiple different sequences and measures the performance. |
||||
|
|
||||
|
### 2. Plain Text Format Example |
||||
|
|
||||
|
```bash |
||||
|
zig build plain-text |
||||
|
``` |
||||
|
|
||||
|
This example demonstrates using the `formatSearchResultAsPlainText` function instead of the default JSON format. |
||||
|
|
||||
|
## Features |
||||
|
|
||||
|
- Searches for the sequence "010102" in Pi |
||||
|
- Uses '-' as delimiter every 2 digits (01-01-02 format) |
||||
|
- Outputs the result as JSON |
||||
|
- Shows performance metrics |
||||
|
|
||||
|
## Example Output |
||||
|
|
||||
|
### JSON Output (Default) |
||||
|
|
||||
|
```json |
||||
|
{ |
||||
|
"success": true, |
||||
|
"sequence": "010102", |
||||
|
"position": 12345, |
||||
|
"context": "...92[01-01-02]94...", |
||||
|
"performance": { |
||||
|
"file_load_time_ms": 1250.45, |
||||
|
"search_time_ms": 23.67, |
||||
|
"file_size_mb": 95.37, |
||||
|
"load_speed_mbs": 76.28 |
||||
|
}, |
||||
|
"context_config": { |
||||
|
"size": 50, |
||||
|
"position": 25, |
||||
|
"delimiter": "-", |
||||
|
"delimiter_interval": 2, |
||||
|
"prefix": "[", |
||||
|
"suffix": "]" |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
### Plain Text Output (Using formatSearchResultAsPlainText) |
||||
|
|
||||
|
```text |
||||
|
Search Result: |
||||
|
Success: true |
||||
|
Sequence: 010102 |
||||
|
Position: 12345 |
||||
|
Context: ...92[01-01-02]94... |
||||
|
``` |
||||
@ -0,0 +1,65 @@ |
|||||
|
const std = @import("std"); |
||||
|
|
||||
|
pub fn build(b: *std.Build) void { |
||||
|
const target = b.standardTargetOptions(.{}); |
||||
|
const optimize = b.standardOptimizeOption(.{}); |
||||
|
|
||||
|
// Create the pi-finder module locally |
||||
|
const pi_finder_module = b.createModule(.{ |
||||
|
.root_source_file = b.path("../../src/lib.zig"), |
||||
|
}); |
||||
|
|
||||
|
const exe = b.addExecutable(.{ |
||||
|
.name = "birth-date-finder", |
||||
|
.root_source_file = b.path("src/main.zig"), |
||||
|
.target = target, |
||||
|
.optimize = optimize, |
||||
|
}); |
||||
|
|
||||
|
exe.root_module.addImport("pi-finder", pi_finder_module); |
||||
|
b.installArtifact(exe); |
||||
|
|
||||
|
const run_cmd = b.addRunArtifact(exe); |
||||
|
run_cmd.step.dependOn(b.getInstallStep()); |
||||
|
|
||||
|
if (b.args) |args| { |
||||
|
run_cmd.addArgs(args); |
||||
|
} |
||||
|
|
||||
|
const run_step = b.step("run", "Run the app"); |
||||
|
run_step.dependOn(&run_cmd.step); |
||||
|
|
||||
|
// Add test executable |
||||
|
const test_exe = b.addExecutable(.{ |
||||
|
.name = "test-multiple", |
||||
|
.root_source_file = b.path("src/test_multiple.zig"), |
||||
|
.target = target, |
||||
|
.optimize = optimize, |
||||
|
}); |
||||
|
|
||||
|
test_exe.root_module.addImport("pi-finder", pi_finder_module); |
||||
|
b.installArtifact(test_exe); |
||||
|
|
||||
|
const test_run_cmd = b.addRunArtifact(test_exe); |
||||
|
test_run_cmd.step.dependOn(b.getInstallStep()); |
||||
|
|
||||
|
const test_step = b.step("test-multiple", "Run multiple tests"); |
||||
|
test_step.dependOn(&test_run_cmd.step); |
||||
|
|
||||
|
// Add plain text example executable |
||||
|
const plain_text_exe = b.addExecutable(.{ |
||||
|
.name = "plain-text-example", |
||||
|
.root_source_file = b.path("src/plain_text_example.zig"), |
||||
|
.target = target, |
||||
|
.optimize = optimize, |
||||
|
}); |
||||
|
|
||||
|
plain_text_exe.root_module.addImport("pi-finder", pi_finder_module); |
||||
|
b.installArtifact(plain_text_exe); |
||||
|
|
||||
|
const plain_text_run_cmd = b.addRunArtifact(plain_text_exe); |
||||
|
plain_text_run_cmd.step.dependOn(b.getInstallStep()); |
||||
|
|
||||
|
const plain_text_step = b.step("plain-text", "Run the plain text format example"); |
||||
|
plain_text_step.dependOn(&plain_text_run_cmd.step); |
||||
|
} |
||||
@ -0,0 +1,48 @@ |
|||||
|
const std = @import("std"); |
||||
|
const PiFinder = @import("pi-finder"); |
||||
|
|
||||
|
pub fn main() !void { |
||||
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){}; |
||||
|
defer _ = gpa.deinit(); |
||||
|
const allocator = gpa.allocator(); |
||||
|
|
||||
|
// Path to the Pi file (relative to the main project) |
||||
|
const pi_file_path = "../../data/pi-decimal-10_000_000_000.txt"; |
||||
|
|
||||
|
// Initialize Pi Finder (JSON output is standard) |
||||
|
var pi_finder = PiFinder.PiFinder.init(allocator, pi_file_path) catch |err| { |
||||
|
std.debug.print("Error initializing Pi finder: {}\n", .{err}); |
||||
|
return; |
||||
|
}; |
||||
|
defer pi_finder.deinit(); |
||||
|
|
||||
|
// Configure birth date |
||||
|
const birth_date = "010102"; // 01.01.02 |
||||
|
|
||||
|
// Context configuration with delimiter '-' |
||||
|
const context_config = PiFinder.ContextConfig{ |
||||
|
.size = 50, |
||||
|
.position = 25, |
||||
|
.delimiter = "-", |
||||
|
.delimiter_interval = 2, |
||||
|
.prefix = "[", |
||||
|
.suffix = "]", |
||||
|
}; |
||||
|
|
||||
|
// Perform search |
||||
|
const result = try pi_finder.searchSequence(birth_date, context_config, allocator); |
||||
|
|
||||
|
// Output result as JSON |
||||
|
const json_output = try PiFinder.formatSearchResultAsJson(allocator, result); |
||||
|
defer allocator.free(json_output); |
||||
|
|
||||
|
const stdout = std.io.getStdOut().writer(); |
||||
|
try stdout.print("{s}\n", .{json_output}); |
||||
|
|
||||
|
// Clean up if context was allocated |
||||
|
if (result.context) |context| { |
||||
|
if (context_config.delimiter != null) { |
||||
|
allocator.free(context); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,50 @@ |
|||||
|
const std = @import("std"); |
||||
|
const PiFinder = @import("pi-finder"); |
||||
|
|
||||
|
/// This example demonstrates how to use the formatSearchResultAsPlainText function |
||||
|
/// to output search results in plain text format instead of JSON |
||||
|
pub fn main() !void { |
||||
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){}; |
||||
|
defer _ = gpa.deinit(); |
||||
|
const allocator = gpa.allocator(); |
||||
|
|
||||
|
// Path to the Pi file (relative to the main project) |
||||
|
const pi_file_path = "../../data/pi-decimal-10_000_000_000.txt"; |
||||
|
|
||||
|
// Initialize Pi Finder |
||||
|
var pi_finder = PiFinder.PiFinder.init(allocator, pi_file_path) catch |err| { |
||||
|
std.debug.print("Error initializing Pi finder: {}\n", .{err}); |
||||
|
return; |
||||
|
}; |
||||
|
defer pi_finder.deinit(); |
||||
|
|
||||
|
// Configure birth date |
||||
|
const birth_date = "010102"; // 01.01.02 |
||||
|
|
||||
|
// Context configuration with delimiter '-' |
||||
|
const context_config = PiFinder.ContextConfig{ |
||||
|
.size = 50, |
||||
|
.position = 25, |
||||
|
.delimiter = "-", |
||||
|
.delimiter_interval = 2, |
||||
|
.prefix = "[", |
||||
|
.suffix = "]", |
||||
|
}; |
||||
|
|
||||
|
// Perform search |
||||
|
const result = try pi_finder.searchSequence(birth_date, context_config, allocator); |
||||
|
|
||||
|
// Output result as plain text |
||||
|
const plain_text_output = try PiFinder.formatSearchResultAsPlainText(allocator, result); |
||||
|
defer allocator.free(plain_text_output); |
||||
|
|
||||
|
const stdout = std.io.getStdOut().writer(); |
||||
|
try stdout.print("{s}\n", .{plain_text_output}); |
||||
|
|
||||
|
// Clean up if context was allocated |
||||
|
if (result.context) |context| { |
||||
|
if (context_config.delimiter != null) { |
||||
|
allocator.free(context); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,59 @@ |
|||||
|
const std = @import("std"); |
||||
|
const PiFinder = @import("pi-finder"); |
||||
|
|
||||
|
pub fn main() !void { |
||||
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){}; |
||||
|
defer _ = gpa.deinit(); |
||||
|
const allocator = gpa.allocator(); |
||||
|
|
||||
|
// Path to the Pi file (relative to the main project) |
||||
|
const pi_file_path = "../../data/pi-decimal-10_000_000_000.txt"; |
||||
|
|
||||
|
// Initialize Pi Finder (JSON output is standard) |
||||
|
var pi_finder = PiFinder.PiFinder.init(allocator, pi_file_path) catch |err| { |
||||
|
const result = PiFinder.SearchResult{ |
||||
|
.success = false, |
||||
|
.sequence = "N/A", |
||||
|
.position = null, |
||||
|
.context = null, |
||||
|
.error_message = @errorName(err), |
||||
|
}; |
||||
|
const json_output = try PiFinder.formatSearchResultAsJson(allocator, result); |
||||
|
defer allocator.free(json_output); |
||||
|
std.debug.print("{s}\n", .{json_output}); |
||||
|
return; |
||||
|
}; |
||||
|
defer pi_finder.deinit(); |
||||
|
|
||||
|
// Test with different birth dates |
||||
|
const test_dates = [_][]const u8{ "010102", "123456", "314159" }; |
||||
|
|
||||
|
for (test_dates) |birth_date| { |
||||
|
// Context configuration with delimiter '-' for date |
||||
|
const context_config = PiFinder.ContextConfig{ |
||||
|
.size = 40, |
||||
|
.position = 20, |
||||
|
.delimiter = "-", |
||||
|
.delimiter_interval = 2, |
||||
|
.prefix = "[", |
||||
|
.suffix = "]", |
||||
|
}; |
||||
|
|
||||
|
// Perform search |
||||
|
const result = try pi_finder.searchSequence(birth_date, context_config, allocator); |
||||
|
|
||||
|
// Output result as JSON |
||||
|
const json_output = try PiFinder.formatSearchResultAsJson(allocator, result); |
||||
|
defer allocator.free(json_output); |
||||
|
|
||||
|
const stdout = std.io.getStdOut().writer(); |
||||
|
try stdout.print("Searching for {s}:\n{s}\n\n", .{ birth_date, json_output }); |
||||
|
|
||||
|
// Clean up if context was allocated |
||||
|
if (result.context) |context| { |
||||
|
if (context_config.delimiter != null) { |
||||
|
allocator.free(context); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,45 @@ |
|||||
|
const std = @import("std"); |
||||
|
|
||||
|
// Export all public types and functions |
||||
|
pub const PiFinder = @import("pi_finder.zig").PiFinder; |
||||
|
pub const ContextConfig = @import("pi_finder.zig").ContextConfig; |
||||
|
pub const SearchResult = @import("pi_finder.zig").SearchResult; |
||||
|
|
||||
|
// JSON utility functions for easier usage |
||||
|
pub fn formatSearchResultAsJson(allocator: std.mem.Allocator, result: SearchResult) ![]u8 { |
||||
|
var json_string = std.ArrayList(u8).init(allocator); |
||||
|
try std.json.stringify(result, .{}, json_string.writer()); |
||||
|
return json_string.toOwnedSlice(); |
||||
|
} |
||||
|
|
||||
|
// Plain text utility functions for easier usage |
||||
|
pub fn formatSearchResultAsPlainText(allocator: std.mem.Allocator, result: SearchResult) ![]u8 { |
||||
|
var plain_text = std.ArrayList(u8).init(allocator); |
||||
|
var writer = plain_text.writer(); |
||||
|
|
||||
|
try writer.writeAll("Success: "); |
||||
|
try writer.print("{}\n", .{result.success}); |
||||
|
try writer.writeAll("Sequence: "); |
||||
|
try writer.writeAll(result.sequence); |
||||
|
|
||||
|
if (result.position) |pos| { |
||||
|
try writer.writeAll("\nPosition: "); |
||||
|
try writer.print("{d}", .{pos}); |
||||
|
} else { |
||||
|
try writer.writeAll("\nPosition: N/A"); |
||||
|
} |
||||
|
|
||||
|
if (result.context) |context| { |
||||
|
try writer.writeAll("\nContext: "); |
||||
|
try writer.writeAll(context); |
||||
|
} else { |
||||
|
try writer.writeAll("\nContext: N/A"); |
||||
|
} |
||||
|
|
||||
|
if (result.error_message) |err_msg| { |
||||
|
try writer.writeAll("\nError Message: "); |
||||
|
try writer.writeAll(err_msg); |
||||
|
} |
||||
|
|
||||
|
return plain_text.toOwnedSlice(); |
||||
|
} |
||||
@ -0,0 +1,235 @@ |
|||||
|
const std = @import("std"); |
||||
|
|
||||
|
/// Output format configuration - REMOVED |
||||
|
// The OutputFormat enum was removed as JSON is now the default and only output format |
||||
|
|
||||
|
/// Configuration for context around a found sequence |
||||
|
pub const ContextConfig = struct { |
||||
|
size: u64 = 25, |
||||
|
position: u64 = 10, |
||||
|
delimiter: ?[]const u8 = null, |
||||
|
delimiter_interval: u64 = 2, // Default: after every 2 digits (DD-MM-YY style) |
||||
|
prefix: []const u8 = "", |
||||
|
suffix: []const u8 = "", |
||||
|
}; |
||||
|
|
||||
|
/// Result structure for JSON output |
||||
|
pub const SearchResult = struct { |
||||
|
success: bool, |
||||
|
sequence: []const u8, |
||||
|
position: ?u64, |
||||
|
context: ?[]const u8, |
||||
|
error_message: ?[]const u8 = null, |
||||
|
performance: ?PerformanceMetrics = null, |
||||
|
context_config: ?ContextConfig = null, |
||||
|
|
||||
|
pub const PerformanceMetrics = struct { |
||||
|
file_load_time_ms: f64, |
||||
|
search_time_ms: f64, |
||||
|
file_size_mb: f64, |
||||
|
load_speed_mbs: f64, |
||||
|
}; |
||||
|
}; |
||||
|
|
||||
|
/// PiFinder structure with core functionality for searching and formatting sequences |
||||
|
pub const PiFinder = struct { |
||||
|
pi_decimals: []u8, |
||||
|
allocator: std.mem.Allocator, |
||||
|
load_time_ms: f64, |
||||
|
|
||||
|
const Self = @This(); |
||||
|
|
||||
|
/// Initializes the Pi finder and loads the Pi decimal places from file |
||||
|
pub fn init(allocator: std.mem.Allocator, file_path: []const u8) !Self { |
||||
|
const start_time = std.time.nanoTimestamp(); |
||||
|
|
||||
|
const file = std.fs.cwd().openFile(file_path, .{}) catch |err| { |
||||
|
return err; |
||||
|
}; |
||||
|
defer file.close(); |
||||
|
|
||||
|
const file_size = try file.getEndPos(); |
||||
|
const pi_data = try allocator.alloc(u8, file_size); |
||||
|
_ = try file.readAll(pi_data); |
||||
|
|
||||
|
// Calculate loading time |
||||
|
const end_time = std.time.nanoTimestamp(); |
||||
|
const load_time_ns = end_time - start_time; |
||||
|
const load_time_ms = @as(f64, @floatFromInt(load_time_ns)) / 1_000_000.0; |
||||
|
|
||||
|
return Self{ |
||||
|
.pi_decimals = pi_data, |
||||
|
.allocator = allocator, |
||||
|
.load_time_ms = load_time_ms, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
/// Frees the memory |
||||
|
pub fn deinit(self: *Self) void { |
||||
|
self.allocator.free(self.pi_decimals); |
||||
|
} |
||||
|
|
||||
|
/// Searches for a number sequence in the Pi decimal places |
||||
|
pub fn findSequenceInPi(self: *const Self, sequence: []const u8) !?u64 { |
||||
|
// Input validation |
||||
|
if (sequence.len == 0) { |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
if (sequence.len > self.pi_decimals.len) { |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
// Check if all characters are digits |
||||
|
for (sequence) |char| { |
||||
|
if (char < '0' or char > '9') { |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Search for the sequence in the Pi decimal places |
||||
|
var i: usize = 0; |
||||
|
outer_loop: while (i <= self.pi_decimals.len - sequence.len) : (i += 1) { |
||||
|
// Check each character of the sequence |
||||
|
for (sequence, 0..) |seq_char, k| { |
||||
|
if (self.pi_decimals[i + k] != seq_char) { |
||||
|
continue :outer_loop; |
||||
|
} |
||||
|
} |
||||
|
return @as(u64, i); |
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
/// Generate context around a found position with optional delimiter formatting |
||||
|
pub fn getContext(self: *const Self, position: u64, context_cfg: ContextConfig, sequence: []const u8, allocator: std.mem.Allocator) ![]const u8 { |
||||
|
// Ensure context position doesn't exceed context size |
||||
|
const actual_position = if (context_cfg.position >= context_cfg.size) context_cfg.size - 1 else context_cfg.position; |
||||
|
|
||||
|
// Calculate context boundaries |
||||
|
const context_start = if (position > actual_position) position - actual_position else 0; |
||||
|
const context_end_target = context_start + context_cfg.size; |
||||
|
const context_end = if (context_end_target <= self.pi_decimals.len) context_end_target else self.pi_decimals.len; |
||||
|
|
||||
|
const raw_context = self.pi_decimals[context_start..context_end]; |
||||
|
|
||||
|
// If no delimiter is specified, return raw context |
||||
|
if (context_cfg.delimiter == null) { |
||||
|
return raw_context; |
||||
|
} |
||||
|
|
||||
|
// Apply delimiter formatting to the found sequence within the context |
||||
|
const delimiter = context_cfg.delimiter.?; |
||||
|
const sequence_start_in_context = if (position > context_start) position - context_start else 0; |
||||
|
|
||||
|
// Create a copy of the context to modify |
||||
|
const max_delimiters = (sequence.len - 1) / context_cfg.delimiter_interval; |
||||
|
var formatted_context = try allocator.alloc(u8, raw_context.len + max_delimiters * delimiter.len + context_cfg.prefix.len + context_cfg.suffix.len); |
||||
|
var write_pos: usize = 0; |
||||
|
|
||||
|
// Copy context up to the sequence |
||||
|
for (raw_context[0..sequence_start_in_context]) |char| { |
||||
|
formatted_context[write_pos] = char; |
||||
|
write_pos += 1; |
||||
|
} |
||||
|
|
||||
|
// Add prefix |
||||
|
for (context_cfg.prefix) |char| { |
||||
|
formatted_context[write_pos] = char; |
||||
|
write_pos += 1; |
||||
|
} |
||||
|
|
||||
|
// Add the sequence with delimiters |
||||
|
if (sequence_start_in_context + sequence.len <= raw_context.len) { |
||||
|
for (sequence, 0..) |_, i| { |
||||
|
// Add delimiter based on interval (but not at the very beginning) |
||||
|
if (i > 0 and i % context_cfg.delimiter_interval == 0) { |
||||
|
// Add delimiter |
||||
|
for (delimiter) |d_char| { |
||||
|
formatted_context[write_pos] = d_char; |
||||
|
write_pos += 1; |
||||
|
} |
||||
|
} |
||||
|
// Add the character from the actual context (to ensure accuracy) |
||||
|
formatted_context[write_pos] = raw_context[sequence_start_in_context + i]; |
||||
|
write_pos += 1; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Add suffix |
||||
|
for (context_cfg.suffix) |char| { |
||||
|
formatted_context[write_pos] = char; |
||||
|
write_pos += 1; |
||||
|
} |
||||
|
|
||||
|
// Copy remaining context after the sequence |
||||
|
const remaining_start = sequence_start_in_context + sequence.len; |
||||
|
if (remaining_start < raw_context.len) { |
||||
|
for (raw_context[remaining_start..]) |char| { |
||||
|
formatted_context[write_pos] = char; |
||||
|
write_pos += 1; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Resize to actual used length |
||||
|
return formatted_context[0..write_pos]; |
||||
|
} |
||||
|
|
||||
|
/// Comprehensive search function that returns a structured result |
||||
|
pub fn searchSequence(self: *const Self, sequence: []const u8, context_cfg: ContextConfig, allocator: std.mem.Allocator) !SearchResult { |
||||
|
const start_search_time = std.time.nanoTimestamp(); |
||||
|
|
||||
|
const position = self.findSequenceInPi(sequence) catch |err| { |
||||
|
return SearchResult{ |
||||
|
.success = false, |
||||
|
.sequence = sequence, |
||||
|
.position = null, |
||||
|
.context = null, |
||||
|
.error_message = @errorName(err), |
||||
|
}; |
||||
|
}; |
||||
|
|
||||
|
const end_search_time = std.time.nanoTimestamp(); |
||||
|
const search_time_ns = end_search_time - start_search_time; |
||||
|
const search_time_ms = @as(f64, @floatFromInt(search_time_ns)) / 1_000_000.0; |
||||
|
|
||||
|
if (position) |pos| { |
||||
|
const context = self.getContext(pos, context_cfg, sequence, allocator) catch |err| { |
||||
|
return SearchResult{ |
||||
|
.success = false, |
||||
|
.sequence = sequence, |
||||
|
.position = pos, |
||||
|
.context = null, |
||||
|
.error_message = @errorName(err), |
||||
|
}; |
||||
|
}; |
||||
|
|
||||
|
// Calculate performance metrics |
||||
|
const file_size_mb = @as(f64, @floatFromInt(self.pi_decimals.len)) / (1024.0 * 1024.0); |
||||
|
const load_speed_mbs = file_size_mb / (self.load_time_ms / 1000.0); |
||||
|
|
||||
|
return SearchResult{ |
||||
|
.success = true, |
||||
|
.sequence = sequence, |
||||
|
.position = pos, |
||||
|
.context = context, |
||||
|
.performance = SearchResult.PerformanceMetrics{ |
||||
|
.file_load_time_ms = self.load_time_ms, |
||||
|
.search_time_ms = search_time_ms, |
||||
|
.file_size_mb = file_size_mb, |
||||
|
.load_speed_mbs = load_speed_mbs, |
||||
|
}, |
||||
|
.context_config = context_cfg, |
||||
|
}; |
||||
|
} else { |
||||
|
return SearchResult{ |
||||
|
.success = false, |
||||
|
.sequence = sequence, |
||||
|
.position = null, |
||||
|
.context = null, |
||||
|
.error_message = "Sequence not found in Pi", |
||||
|
}; |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
Loading…
Reference in new issue