signvec/lib.rs
1//! # SignVec
2//!
3//! 
4//! 
5//! 
6//! 
7//!
8//! `SignVec` extends the capabilities of the traditional `Vec` by providing additional functionalities
9//! to efficiently track and manipulate elements based on their sign (positive or negative) using the `Signable` trait.
10//!
11//! ## Features
12//!
13//! - **Sign-Aware Element Tracking**: Tracks the sign of elements for optimized sign-specific operations.
14//! - **Efficient Updates**: Maintains high performance even with frequent modifications.
15//! - **Versatile Operations**: Provides methods for element counting, access, and manipulation based on sign.
16//! - **Flexible Interface**: Offers both safe and unsafe methods to accommodate various requirements.
17//! - **Custom Type Support**: Seamlessly integrates with user-defined types via the `Signable` trait.
18//!
19//! ## Usage: Basic operations
20//!
21//! ```rust
22//! use nanorand::{Rng, WyRand};
23//! use signvec::{svec, Sign, SignVec};
24//!
25//! let mut vector = svec![1, -2, 3, -4];
26//!
27//! assert_eq!(vector.len(), 4);
28//! assert_eq!(vector[0], 1);
29//! assert_eq!(vector[1], -2);
30//!
31//! // Count positive and negative elements
32//! assert_eq!(vector.count(Sign::Plus), 2);
33//! assert_eq!(vector.count(Sign::Minus), 2);
34//!
35//! // Get indices of positive and negative elements
36//! assert_eq!(
37//! vector.indices(Sign::Plus).iter().collect::<Vec<_>>(),
38//! vec![&0, &2]
39//! );
40//! assert_eq!(
41//! vector.indices(Sign::Minus).iter().collect::<Vec<_>>(),
42//! vec![&1, &3]
43//! );
44//!
45//! // Retrieve values based on their sign
46//! assert_eq!(vector.values(Sign::Plus).collect::<Vec<_>>(), vec![&1, &3]);
47//! assert_eq!(
48//! vector.values(Sign::Minus).collect::<Vec<_>>(),
49//! vec![&-2, &-4]
50//! );
51//!
52//! // Modify individual elements
53//! vector.set(1, 5);
54//! assert_eq!(vector[1], 5);
55//!
56//! // Randomly select an element based on its sign
57//! let mut rng = WyRand::new();
58//! if let Some(random_positive) = vector.random(Sign::Plus, &mut rng) {
59//! println!("Random positive value: {}", random_positive);
60//! }
61//! ```
62//!
63//! ## Usage: Monte Carlo simulations
64//!
65//! This demonstrates a simple Monte Carlo simulation where site energies in a
66//! `SignVec` are updated based on simulated dynamics and system energy distribution.
67//!
68//! ```rust
69//! use signvec::{SignVec, svec, Sign, Signable};
70//! use nanorand::{WyRand, Rng};
71//!
72//! let mut energies = svec![1.0, -1.0, 1.5, -1.5, 0.5, -0.5];
73//! let mut rng = WyRand::new();
74//!
75//! // Simulation loop for multiple Monte Carlo steps
76//! for _step in 0..100 {
77//! let site = rng.generate_range(0..energies.len());
78//! let dE = rng.generate::<f64>() - 0.5; // Change in energy
79//!
80//! let new_energy = energies[site] + dE; // Update site energy
81//!
82//! // Make decisions based on system's energy distribution
83//! if energies.count(Sign::Minus) > energies.count(Sign::Plus) {
84//! if energies[site].sign() == Sign::Minus && rng.generate::<f64>() < 0.5 {
85//! energies.set(site, -new_energy); // Flip energy sign
86//! } else {
87//! energies.set(site, new_energy);
88//! }
89//! } else {
90//! energies.set(site, new_energy); // Balanced distribution
91//! }
92//! }
93//!
94//! println!("Final energy distribution: {:?}", energies);
95//! ```
96//!
97//! ## Usage: Portfolio management
98//!
99//! Demonstrates how `SignVec` can be used for managing a financial portfolio, simulating
100//! market conditions, and making decisions based on the sign-aware characteristics of
101//! assets and liabilities.
102//!
103//! ```rust
104//! use signvec::{SignVec, Sign, svec};
105//! use nanorand::WyRand;
106//!
107//! let mut portfolio = svec![150.0, -200.0, 300.0, -50.0, 400.0];
108//! let market_conditions = vec![1.05, 0.95, 1.10, 1.00, 1.03];
109//!
110//! // Apply market conditions to adjust portfolio balances
111//! for (index, &factor) in market_conditions.iter().enumerate() {
112//! portfolio.set(index, portfolio[index] * factor);
113//! }
114//!
115//! // Decision making based on portfolio's sign-aware characteristics
116//! if portfolio.count(Sign::Minus) > 2 {
117//! println!("Consider rebalancing your portfolio to manage risk.");
118//! } else {
119//! println!("Your portfolio is well-balanced and diversified.");
120//! }
121//!
122//! // Calculate 10% of total liabilities for debt reduction
123//! let debt_reduction = portfolio.values(Sign::Minus).sum::<f64>() * 0.1;
124//! println!("Plan for a debt reduction of ${:.2} to strengthen your financial position.", debt_reduction.abs());
125//!
126//! // Identify a high-performing asset for potential investment
127//! let mut rng = WyRand::new();
128//! if let Some(lucky_asset) = portfolio.random(Sign::Plus, &mut rng) {
129//! println!("Consider investing more in an asset valued at ${:.2}.", lucky_asset);
130//! } else {
131//! println!("No standout assets for additional investment at the moment.");
132//! }
133//! ```
134//!
135//! ## Benchmarks
136//!
137//! The table below is a summary of benchmark results for the specialized functionality of `SignVec`.
138//!
139//! | Operation | `SignVec` | `Vec` | Speedup |
140//! |-------------------|----------------|-----------|----------------|
141//! | `set` | 1.3922 ns | - | - |
142//! | `set_unchecked` | 1.3873 ns | - | - |
143//! | `random` (Plus) | 822.90 ps | - | - |
144//! | `random` (Minus) | 829.92 ps | - | - |
145//! | `random_pos` | 652.96 ps | - | - |
146//! | `random_neg` | 687.77 ps | - | - |
147//! | `count` (Plus) | 453.21 ps | - | - |
148//! | `count` (Minus) | 458.70 ps | - | - |
149//! | `count_pos` | 229.73 ps | - | - |
150//! | `count_neg` | 228.44 ps | - | - |
151//! | `indices` (Plus) | 465.04 ps | - | - |
152//! | `indices` (Minus) | 461.85 ps | - | - |
153//! | `indices_pos` | 226.99 ps | - | - |
154//! | `indices_neg` | 225.83 ps | - | - |
155//! | `sync` | 61.208 µs | - | - |
156//! | `count`[^1] | 225.74 ps | 153.38 ns | ~679x faster |
157//! | `indices`[^2] | 86.42 ns | 1.11 µs | ~12.8x faster |
158//! | `values`[^3] | 579.37 ns | 1.13 µs | ~1.95x faster |
159//! | `random`[^4] | 857.86 ps | 950.84 ns | ~1106x faster |
160//!
161//! [^1]: The `count_pos` and `count_neg` benchmarks are used here for comparison as they represent the optimized paths for counting elements by sign.
162//! [^2]: The `indices_pos` and `indices_neg` benchmarks are presented as the optimized methods for retrieving indices by sign.
163//! [^3]: The `values` operation does not have a direct counterpart in the provided benchmarks but is included for context.
164//! [^4]: The `random_pos` and `random_neg` benchmarks provide context for the `random` operation's performance when the sign is predetermined.
165//!
166//! Benchmarks were conducted on a machine with the following specifications:
167//! - Processor: AMD Ryzen™ 5 5600G with Radeon™ Graphics x 12
168//! - Memory: 58.8 GiB
169//! - Operating System: Guix System
170//! - OS Type: 64-bit
171
172mod signvec;
173pub use signvec::SignVec;
174
175/// Enum representing the sign of a number.
176#[derive(Debug, PartialEq, Copy, Clone)]
177pub enum Sign {
178 Plus,
179 Minus,
180}
181
182/// Trait for types that can be classified by a sign.
183pub trait Signable {
184 fn sign(&self) -> Sign;
185}
186
187impl Sign {
188 pub fn flip(&self) -> Self {
189 match self {
190 Sign::Plus => Sign::Minus,
191 Sign::Minus => Sign::Plus,
192 }
193 }
194}
195
196macro_rules! signfrom {
197 ($($t:ty),*) => {$(
198 impl From<$t> for Sign {
199 fn from(num: $t) -> Self {
200 if num >= 0 as $t { Sign::Plus } else { Sign::Minus }
201 }
202 }
203 )*};
204}
205
206macro_rules! signable {
207 ($($t:ty),*) => {$(
208 impl Signable for $t {
209 fn sign(&self) -> Sign {
210 if *self >= 0 as $t { Sign::Plus } else { Sign::Minus }
211 }
212 }
213 )*};
214}
215
216#[macro_export]
217macro_rules! svec {
218 ($($x:expr),* $(,)?) => {{
219 let temp_slice = &[$($x),*];
220 SignVec::from(temp_slice)
221 }};
222}
223
224signfrom!(i8, i16, i32, i64, i128, isize, f32, f64);
225signable!(i8, i16, i32, i64, i128, isize, f32, f64);