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