Init
This commit is contained in:
35
.github/copilot-instructions.md
vendored
Normal file
35
.github/copilot-instructions.md
vendored
Normal file
@@ -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
|
||||
42
.gitignore
vendored
Normal file
42
.gitignore
vendored
Normal file
@@ -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/
|
||||
128
README.md
Normal file
128
README.md
Normal file
@@ -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
|
||||
33
build.zig
Normal file
33
build.zig
Normal file
@@ -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);
|
||||
}
|
||||
4
download-pi-data.sh
Executable file
4
download-pi-data.sh
Executable file
@@ -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
|
||||
74
examples/birth-date-finder/README.md
Normal file
74
examples/birth-date-finder/README.md
Normal file
@@ -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...
|
||||
```
|
||||
65
examples/birth-date-finder/build.zig
Normal file
65
examples/birth-date-finder/build.zig
Normal file
@@ -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);
|
||||
}
|
||||
48
examples/birth-date-finder/src/main.zig
Normal file
48
examples/birth-date-finder/src/main.zig
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
50
examples/birth-date-finder/src/plain_text_example.zig
Normal file
50
examples/birth-date-finder/src/plain_text_example.zig
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
59
examples/birth-date-finder/src/test_multiple.zig
Normal file
59
examples/birth-date-finder/src/test_multiple.zig
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
45
src/lib.zig
Normal file
45
src/lib.zig
Normal file
@@ -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();
|
||||
}
|
||||
235
src/pi_finder.zig
Normal file
235
src/pi_finder.zig
Normal file
@@ -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",
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user