rusty_common/
const_fn_candidates.rs

1//! Const fn utility functions for compile-time evaluation
2//!
3//! This module contains simple, pure functions that can be evaluated at compile time.
4//! These utilities provide performance benefits by computing values during compilation
5//! rather than at runtime.
6
7/// Mathematical constants and operations
8/// Calculate the maximum of two u64 values at compile time
9#[inline]
10#[must_use]
11pub const fn const_max_u64(a: u64, b: u64) -> u64 {
12    if a > b { a } else { b }
13}
14
15/// Calculate the minimum of two u64 values at compile time
16#[inline]
17#[must_use]
18pub const fn const_min_u64(a: u64, b: u64) -> u64 {
19    if a < b { a } else { b }
20}
21
22/// Calculate the maximum of two i64 values at compile time
23#[inline]
24#[must_use]
25pub const fn const_max_i64(a: i64, b: i64) -> i64 {
26    if a > b { a } else { b }
27}
28
29/// Calculate the minimum of two i64 values at compile time
30#[inline]
31#[must_use]
32pub const fn const_min_i64(a: i64, b: i64) -> i64 {
33    if a < b { a } else { b }
34}
35
36/// Check if a u64 value is a power of 2 at compile time
37#[inline]
38#[must_use]
39pub const fn is_power_of_two(n: u64) -> bool {
40    n != 0 && (n & (n - 1)) == 0
41}
42
43/// Calculate the next power of 2 for a u64 value at compile time
44#[inline]
45#[must_use]
46pub const fn next_power_of_two(n: u64) -> u64 {
47    if n == 0 {
48        return 1;
49    }
50
51    let mut power = 1;
52    while power < n {
53        power <<= 1;
54    }
55    power
56}
57
58/// Type conversions
59/// Convert bytes to kilobytes (1024 bytes = 1 KB)
60#[inline]
61#[must_use]
62pub const fn bytes_to_kb(bytes: u64) -> u64 {
63    bytes / 1024
64}
65
66/// Convert kilobytes to bytes
67#[inline]
68#[must_use]
69pub const fn kb_to_bytes(kb: u64) -> u64 {
70    kb * 1024
71}
72
73/// Convert bytes to megabytes (1024 * 1024 bytes = 1 MB)
74#[inline]
75#[must_use]
76pub const fn bytes_to_mb(bytes: u64) -> u64 {
77    bytes / (1024 * 1024)
78}
79
80/// Convert megabytes to bytes
81#[inline]
82#[must_use]
83pub const fn mb_to_bytes(mb: u64) -> u64 {
84    mb * 1024 * 1024
85}
86
87/// Array and slice utilities
88/// Get the length of a static array at compile time
89#[inline]
90#[must_use]
91pub const fn array_len<T, const N: usize>(_arr: &[T; N]) -> usize {
92    N
93}
94
95/// Check if an index is valid for a given length at compile time
96#[inline]
97#[must_use]
98pub const fn is_valid_index(index: usize, len: usize) -> bool {
99    index < len
100}
101
102/// Trading-specific utilities
103/// Calculate basis points (bps) from a percentage (1% = 100 bps)
104#[inline]
105#[must_use]
106pub const fn percent_to_bps(percent: u64) -> u64 {
107    percent * 100
108}
109
110/// Calculate percentage from basis points
111#[inline]
112#[must_use]
113pub const fn bps_to_percent(bps: u64) -> u64 {
114    bps / 100
115}
116
117/// Calculate the number of decimal places for a given tick size
118/// For example, tick size 0.01 has 2 decimal places
119#[inline]
120#[must_use]
121pub const fn tick_size_to_decimals(tick_size_multiplier: u64) -> u8 {
122    if tick_size_multiplier == 1 {
123        0
124    } else if tick_size_multiplier == 10 {
125        1
126    } else if tick_size_multiplier == 100 {
127        2
128    } else if tick_size_multiplier == 1000 {
129        3
130    } else if tick_size_multiplier == 10000 {
131        4
132    } else if tick_size_multiplier == 100000 {
133        5
134    } else if tick_size_multiplier == 1000000 {
135        6
136    } else if tick_size_multiplier == 10000000 {
137        7
138    } else if tick_size_multiplier == 100000000 {
139        8
140    } else {
141        9
142    } // Maximum supported
143}
144
145/// Network and latency utilities
146/// Convert microseconds to nanoseconds
147#[inline]
148#[must_use]
149pub const fn micros_to_nanos(micros: u64) -> u64 {
150    micros * 1000
151}
152
153/// Convert nanoseconds to microseconds
154#[inline]
155#[must_use]
156pub const fn nanos_to_micros(nanos: u64) -> u64 {
157    nanos / 1000
158}
159
160/// Convert seconds to nanoseconds
161#[inline]
162#[must_use]
163pub const fn seconds_to_nanos(seconds: u64) -> u64 {
164    seconds * 1_000_000_000
165}
166
167/// Convert nanoseconds to seconds
168#[inline]
169#[must_use]
170pub const fn nanos_to_seconds(nanos: u64) -> u64 {
171    nanos / 1_000_000_000
172}
173
174/// Convert milliseconds to nanoseconds
175#[inline]
176#[must_use]
177pub const fn millis_to_nanos(millis: u64) -> u64 {
178    millis * 1_000_000
179}
180
181/// Convert nanoseconds to milliseconds
182#[inline]
183#[must_use]
184pub const fn nanos_to_millis(nanos: u64) -> u64 {
185    nanos / 1_000_000
186}
187
188/// Buffer sizing utilities
189/// Calculate buffer size rounded up to next power of 2
190#[inline]
191#[must_use]
192pub const fn buffer_size_power_of_two(min_size: usize) -> usize {
193    if min_size == 0 {
194        return 1;
195    }
196
197    let mut size = 1;
198    while size < min_size {
199        size <<= 1;
200    }
201    size
202}
203
204/// Calculate SIMD-aligned buffer size (multiple of 4 for f64x4)
205#[inline]
206#[must_use]
207pub const fn simd_aligned_size(size: usize) -> usize {
208    (size + 3) & !3 // Round up to multiple of 4
209}
210
211/// Calculate cache-line aligned size (64 bytes)
212#[inline]
213#[must_use]
214pub const fn cache_aligned_size(size: usize) -> usize {
215    (size + 63) & !63 // Round up to multiple of 64
216}
217
218/// HFT-specific utilities
219/// Calculate order book depth for given memory budget (bytes)
220#[inline]
221#[must_use]
222pub const fn orderbook_depth_for_memory(memory_bytes: usize, level_size: usize) -> usize {
223    if level_size == 0 {
224        return 0;
225    }
226    memory_bytes / level_size
227}
228
229/// Calculate maximum message size for WebSocket buffer
230#[inline]
231#[must_use]
232pub const fn websocket_max_message_size(buffer_size: usize) -> usize {
233    // Reserve 10% for headers and overhead
234    buffer_size * 9 / 10
235}
236
237/// Calculate pool capacity based on expected throughput
238#[inline]
239#[must_use]
240pub const fn pool_capacity_for_throughput(messages_per_second: u64, latency_ms: u64) -> usize {
241    // Buffer for 2x expected messages during latency window
242    let messages_in_window = (messages_per_second * latency_ms) / 1000;
243    (messages_in_window * 2) as usize
244}
245
246/// SIMD and vectorization utilities
247/// Calculate number of SIMD chunks needed for f64x4 operations
248#[inline]
249#[must_use]
250pub const fn simd_f64x4_chunks(elements: usize) -> usize {
251    elements.div_ceil(4) // Round up to multiple of 4
252}
253
254/// Calculate total SIMD buffer size for f64x4 operations
255#[inline]
256#[must_use]
257pub const fn simd_f64x4_buffer_size(elements: usize) -> usize {
258    simd_f64x4_chunks(elements) * 4
259}
260
261/// Calculate optimal SIMD chunk size for given element count and max chunks
262#[inline]
263#[must_use]
264pub const fn optimal_simd_chunks(elements: usize, max_chunks: usize) -> usize {
265    let needed_chunks = simd_f64x4_chunks(elements);
266    if needed_chunks > max_chunks {
267        max_chunks
268    } else {
269        needed_chunks
270    }
271}
272
273/// HFT-specific financial calculations
274/// Calculate basis points difference between two prices
275#[inline]
276#[must_use]
277pub const fn price_diff_bps(price1: u64, price2: u64) -> u64 {
278    if price1 == 0 {
279        return 0;
280    }
281    let diff = price1.abs_diff(price2);
282    (diff * 10000) / price1 // Convert to basis points
283}
284
285/// Calculate tick count between two prices
286#[inline]
287#[must_use]
288pub const fn tick_count(price1: u64, price2: u64, tick_size: u64) -> u64 {
289    if tick_size == 0 {
290        return 0;
291    }
292    let diff = price1.abs_diff(price2);
293    diff / tick_size
294}
295
296/// Calculate spread in basis points
297#[inline]
298#[must_use]
299pub const fn spread_bps(bid: u64, ask: u64) -> u64 {
300    if bid == 0 {
301        return 0;
302    }
303    if ask <= bid {
304        return 0; // Invalid spread
305    }
306    let spread = ask - bid;
307    let mid = (bid + ask) / 2;
308    if mid == 0 {
309        return 0;
310    }
311    // For spread_bps(9990, 10010): spread=20, mid=10000, result=(20*10000)/10000=200
312    (spread * 10000) / mid
313}
314
315/// Calculate mid price from bid and ask
316#[inline]
317#[must_use]
318pub const fn mid_price(bid: u64, ask: u64) -> u64 {
319    (bid + ask) / 2
320}
321
322/// Check if price is within tick size boundaries
323#[inline]
324#[must_use]
325pub const fn is_valid_tick_price(price: u64, tick_size: u64) -> bool {
326    if tick_size == 0 {
327        return false;
328    }
329    price.is_multiple_of(tick_size)
330}
331
332/// Round price to nearest tick
333#[inline]
334#[must_use]
335pub const fn round_to_tick(price: u64, tick_size: u64) -> u64 {
336    if tick_size == 0 {
337        return price;
338    }
339    let remainder = price % tick_size;
340    if remainder * 2 >= tick_size {
341        price + (tick_size - remainder) // Round up
342    } else {
343        price - remainder // Round down
344    }
345}
346
347/// WebSocket and networking utilities
348/// Check if an exchange supports WebSocket compression at compile time
349#[inline]
350#[must_use]
351pub const fn exchange_supports_websocket_compression(exchange: u8) -> bool {
352    // Exchange enum values: Binance=0, Coinbase=1, Bybit=2, Upbit=3, Bithumb=4
353    match exchange {
354        1 | 3 => true, // Coinbase and Upbit
355        _ => false,    // Binance, Bybit, Bithumb
356    }
357}
358
359/// Get default WebSocket port for exchange
360#[inline]
361#[must_use]
362pub const fn exchange_default_websocket_port(exchange: u8) -> u16 {
363    // All supported exchanges use HTTPS WebSocket (port 443)
364    match exchange {
365        0..=4 => 443, // All exchanges: Binance, Coinbase, Bybit, Upbit, Bithumb
366        _ => 443,     // Default for unknown exchanges
367    }
368}
369
370/// Calculate WebSocket ping interval in milliseconds for exchange
371#[inline]
372#[must_use]
373pub const fn exchange_websocket_ping_interval_ms(exchange: u8) -> u64 {
374    match exchange {
375        0 => 180_000, // Binance: 3 minutes
376        1 => 30_000,  // Coinbase: 30 seconds (default)
377        2 => 20_000,  // Bybit: 20 seconds
378        3 => 60_000,  // Upbit: 1 minute
379        4 => 30_000,  // Bithumb: 30 seconds (default)
380        _ => 30_000,  // Default: 30 seconds
381    }
382}
383
384/// Check if exchange requires custom ping message format
385#[inline]
386#[must_use]
387pub const fn exchange_requires_custom_ping(exchange: u8) -> bool {
388    match exchange {
389        2..=4 => true, // Bybit, Upbit, Bithumb
390        _ => false,    // Binance, Coinbase
391    }
392}
393
394/// Calculate maximum WebSocket message size for exchange
395#[inline]
396#[must_use]
397pub const fn exchange_max_websocket_message_size(exchange: u8) -> usize {
398    match exchange {
399        0 => 1024 * 1024,     // Binance: 1MB
400        1 => 4 * 1024 * 1024, // Coinbase: 4MB
401        2 => 1024 * 1024,     // Bybit: 1MB
402        3 => 1024 * 1024,     // Upbit: 1MB
403        4 => 512 * 1024,      // Bithumb: 512KB
404        _ => 1024 * 1024,     // Default: 1MB
405    }
406}
407
408/// Authentication and security utilities
409/// Calculate maximum signature length for exchange authentication
410#[inline]
411#[must_use]
412pub const fn exchange_max_signature_length(exchange: u8) -> usize {
413    match exchange {
414        0 => 64,  // Binance: HMAC-SHA256 (64 chars hex)
415        1 => 256, // Coinbase: ECDSA or HMAC (variable, up to 256)
416        2 => 64,  // Bybit: HMAC-SHA256 (64 chars hex)
417        3 => 512, // Upbit: JWT tokens can be longer
418        4 => 128, // Bithumb: Base64 encoded signature
419        _ => 256, // Default: conservative estimate
420    }
421}
422
423/// Check if exchange requires passphrase for authentication
424#[inline]
425#[must_use]
426pub const fn exchange_requires_passphrase(exchange: u8) -> bool {
427    match exchange {
428        1 => true,  // Coinbase requires passphrase
429        _ => false, // Others don't
430    }
431}
432
433/// Calculate maximum API key length for exchange
434#[inline]
435#[must_use]
436pub const fn exchange_max_api_key_length(exchange: u8) -> usize {
437    match exchange {
438        0 => 64,  // Binance: typically 64 chars
439        1 => 128, // Coinbase: longer API keys
440        2 => 64,  // Bybit: typically 64 chars
441        3 => 128, // Upbit: longer keys
442        4 => 64,  // Bithumb: typically 64 chars
443        _ => 128, // Default: conservative estimate
444    }
445}
446
447/// Performance and optimization utilities
448/// Calculate optimal batch size for exchange message processing
449#[inline]
450#[must_use]
451pub const fn exchange_optimal_batch_size(exchange: u8, message_rate_per_second: u64) -> usize {
452    let base_batch_size = match exchange {
453        0 => 100, // Binance: high throughput
454        1 => 50,  // Coinbase: moderate throughput
455        2 => 75,  // Bybit: moderate-high throughput
456        3 => 25,  // Upbit: lower throughput
457        4 => 25,  // Bithumb: lower throughput
458        _ => 50,  // Default
459    };
460
461    // Scale batch size based on message rate
462    if message_rate_per_second > 1000 {
463        base_batch_size * 2
464    } else if message_rate_per_second > 100 {
465        base_batch_size
466    } else {
467        base_batch_size / 2
468    }
469}
470
471/// Calculate reconnection delay for exchange (exponential backoff)
472#[inline]
473#[must_use]
474pub const fn exchange_reconnection_delay_ms(exchange: u8, attempt: u32) -> u64 {
475    let base_delay = match exchange {
476        0 => 1000, // Binance: 1 second base
477        1 => 2000, // Coinbase: 2 seconds base (more conservative)
478        2 => 1000, // Bybit: 1 second base
479        3 => 3000, // Upbit: 3 seconds base (most conservative)
480        4 => 2000, // Bithumb: 2 seconds base
481        _ => 1000, // Default: 1 second
482    };
483
484    // Exponential backoff with cap at 60 seconds
485    let capped_attempt = if attempt > 6 { 6 } else { attempt }; // Cap at 2^6 = 64x multiplier
486    let delay = base_delay * (1u64 << capped_attempt);
487    const_min_u64(delay, 60_000) // Cap at 60 seconds
488}
489
490#[cfg(test)]
491mod tests {
492    use super::*;
493
494    #[test]
495    fn test_const_max_min() {
496        assert_eq!(const_max_u64(5, 10), 10);
497        assert_eq!(const_max_u64(10, 5), 10);
498        assert_eq!(const_min_u64(5, 10), 5);
499        assert_eq!(const_min_u64(10, 5), 5);
500
501        assert_eq!(const_max_i64(-5, 10), 10);
502        assert_eq!(const_max_i64(10, -5), 10);
503        assert_eq!(const_min_i64(-5, 10), -5);
504        assert_eq!(const_min_i64(10, -5), -5);
505    }
506
507    #[test]
508    fn test_power_of_two() {
509        assert!(is_power_of_two(1));
510        assert!(is_power_of_two(2));
511        assert!(is_power_of_two(4));
512        assert!(is_power_of_two(64));
513        assert!(!is_power_of_two(0));
514        assert!(!is_power_of_two(3));
515        assert!(!is_power_of_two(100));
516
517        assert_eq!(next_power_of_two(0), 1);
518        assert_eq!(next_power_of_two(1), 1);
519        assert_eq!(next_power_of_two(2), 2);
520        assert_eq!(next_power_of_two(3), 4);
521        assert_eq!(next_power_of_two(5), 8);
522        assert_eq!(next_power_of_two(100), 128);
523    }
524
525    #[test]
526    fn test_byte_conversions() {
527        assert_eq!(bytes_to_kb(1024), 1);
528        assert_eq!(bytes_to_kb(2048), 2);
529        assert_eq!(kb_to_bytes(1), 1024);
530        assert_eq!(kb_to_bytes(2), 2048);
531
532        assert_eq!(bytes_to_mb(1024 * 1024), 1);
533        assert_eq!(bytes_to_mb(2 * 1024 * 1024), 2);
534        assert_eq!(mb_to_bytes(1), 1024 * 1024);
535        assert_eq!(mb_to_bytes(2), 2 * 1024 * 1024);
536    }
537
538    #[test]
539    fn test_trading_utilities() {
540        assert_eq!(percent_to_bps(1), 100);
541        assert_eq!(percent_to_bps(5), 500);
542        assert_eq!(bps_to_percent(100), 1);
543        assert_eq!(bps_to_percent(500), 5);
544
545        assert_eq!(tick_size_to_decimals(1), 0);
546        assert_eq!(tick_size_to_decimals(10), 1);
547        assert_eq!(tick_size_to_decimals(100), 2);
548        assert_eq!(tick_size_to_decimals(1000), 3);
549    }
550
551    #[test]
552    fn test_time_conversions() {
553        assert_eq!(micros_to_nanos(1), 1000);
554        assert_eq!(micros_to_nanos(1000), 1_000_000);
555        assert_eq!(nanos_to_micros(1000), 1);
556        assert_eq!(nanos_to_micros(1_000_000), 1000);
557
558        assert_eq!(seconds_to_nanos(1), 1_000_000_000);
559        assert_eq!(seconds_to_nanos(60), 60_000_000_000);
560        assert_eq!(nanos_to_seconds(1_000_000_000), 1);
561        assert_eq!(nanos_to_seconds(60_000_000_000), 60);
562    }
563
564    #[test]
565    fn test_const_contexts() {
566        // Verify all functions work in const contexts
567        const MAX_U64: u64 = const_max_u64(100, 200);
568        const MIN_U64: u64 = const_min_u64(100, 200);
569        const MAX_I64: i64 = const_max_i64(-100, 200);
570        const MIN_I64: i64 = const_min_i64(-100, 200);
571
572        assert_eq!(MAX_U64, 200);
573        assert_eq!(MIN_U64, 100);
574        assert_eq!(MAX_I64, 200);
575        assert_eq!(MIN_I64, -100);
576
577        const IS_POWER: bool = is_power_of_two(64);
578        const NEXT_POWER: u64 = next_power_of_two(100);
579
580        assert!(IS_POWER);
581        assert_eq!(NEXT_POWER, 128);
582
583        const KB_SIZE: u64 = bytes_to_kb(4096);
584        const MB_SIZE: u64 = bytes_to_mb(4_194_304);
585
586        assert_eq!(KB_SIZE, 4);
587        assert_eq!(MB_SIZE, 4);
588
589        const BPS: u64 = percent_to_bps(2);
590        const DECIMALS: u8 = tick_size_to_decimals(100);
591
592        assert_eq!(BPS, 200);
593        assert_eq!(DECIMALS, 2);
594
595        const NANOS: u64 = micros_to_nanos(100);
596        const SECONDS: u64 = nanos_to_seconds(5_000_000_000);
597
598        assert_eq!(NANOS, 100_000);
599        assert_eq!(SECONDS, 5);
600    }
601
602    #[test]
603    fn test_millis_conversions() {
604        assert_eq!(millis_to_nanos(1), 1_000_000);
605        assert_eq!(millis_to_nanos(1000), 1_000_000_000);
606        assert_eq!(nanos_to_millis(1_000_000), 1);
607        assert_eq!(nanos_to_millis(1_000_000_000), 1000);
608    }
609
610    #[test]
611    fn test_buffer_sizing() {
612        assert_eq!(buffer_size_power_of_two(0), 1);
613        assert_eq!(buffer_size_power_of_two(1), 1);
614        assert_eq!(buffer_size_power_of_two(2), 2);
615        assert_eq!(buffer_size_power_of_two(3), 4);
616        assert_eq!(buffer_size_power_of_two(5), 8);
617        assert_eq!(buffer_size_power_of_two(100), 128);
618
619        assert_eq!(simd_aligned_size(1), 4);
620        assert_eq!(simd_aligned_size(4), 4);
621        assert_eq!(simd_aligned_size(5), 8);
622        assert_eq!(simd_aligned_size(8), 8);
623
624        assert_eq!(cache_aligned_size(1), 64);
625        assert_eq!(cache_aligned_size(64), 64);
626        assert_eq!(cache_aligned_size(65), 128);
627        assert_eq!(cache_aligned_size(128), 128);
628    }
629
630    #[test]
631    fn test_hft_utilities() {
632        assert_eq!(orderbook_depth_for_memory(1024, 32), 32);
633        assert_eq!(orderbook_depth_for_memory(2048, 64), 32);
634        assert_eq!(orderbook_depth_for_memory(100, 0), 0);
635
636        assert_eq!(websocket_max_message_size(1000), 900);
637        assert_eq!(websocket_max_message_size(2048), 1843);
638
639        assert_eq!(pool_capacity_for_throughput(1000, 10), 20);
640        assert_eq!(pool_capacity_for_throughput(5000, 5), 50);
641        assert_eq!(pool_capacity_for_throughput(10000, 1), 20);
642    }
643
644    #[test]
645    fn test_new_const_contexts() {
646        // Test new functions in const contexts
647        const MILLIS_NANOS: u64 = millis_to_nanos(500);
648        const NANOS_MILLIS: u64 = nanos_to_millis(500_000_000);
649
650        assert_eq!(MILLIS_NANOS, 500_000_000);
651        assert_eq!(NANOS_MILLIS, 500);
652
653        const BUFFER_SIZE: usize = buffer_size_power_of_two(100);
654        const SIMD_SIZE: usize = simd_aligned_size(10);
655        const CACHE_SIZE: usize = cache_aligned_size(100);
656
657        assert_eq!(BUFFER_SIZE, 128);
658        assert_eq!(SIMD_SIZE, 12);
659        assert_eq!(CACHE_SIZE, 128);
660
661        const DEPTH: usize = orderbook_depth_for_memory(2048, 64);
662        const MAX_MSG_SIZE: usize = websocket_max_message_size(1024);
663        const POOL_CAP: usize = pool_capacity_for_throughput(1000, 10);
664
665        assert_eq!(DEPTH, 32);
666        assert_eq!(MAX_MSG_SIZE, 921);
667        assert_eq!(POOL_CAP, 20);
668    }
669
670    #[test]
671    fn test_simd_utilities() {
672        // Test SIMD chunk calculations
673        assert_eq!(simd_f64x4_chunks(1), 1);
674        assert_eq!(simd_f64x4_chunks(4), 1);
675        assert_eq!(simd_f64x4_chunks(5), 2);
676        assert_eq!(simd_f64x4_chunks(8), 2);
677        assert_eq!(simd_f64x4_chunks(9), 3);
678
679        // Test SIMD buffer size calculations
680        assert_eq!(simd_f64x4_buffer_size(1), 4);
681        assert_eq!(simd_f64x4_buffer_size(4), 4);
682        assert_eq!(simd_f64x4_buffer_size(5), 8);
683        assert_eq!(simd_f64x4_buffer_size(9), 12);
684
685        // Test optimal chunk calculations
686        assert_eq!(optimal_simd_chunks(10, 5), 3); // 10 elements need 3 chunks, within limit
687        assert_eq!(optimal_simd_chunks(20, 3), 3); // 20 elements need 5 chunks, capped at 3
688        assert_eq!(optimal_simd_chunks(4, 10), 1); // 4 elements need 1 chunk, within limit
689    }
690
691    #[test]
692    fn test_hft_financial_calculations() {
693        // Test price difference in basis points
694        assert_eq!(price_diff_bps(10000, 10100), 100); // 1% = 100 bps
695        assert_eq!(price_diff_bps(10000, 9900), 100); // 1% = 100 bps (absolute)
696        assert_eq!(price_diff_bps(0, 100), 0); // Invalid price
697
698        // Test tick count calculations
699        assert_eq!(tick_count(10000, 10010, 5), 2); // 10 price units / 5 tick size = 2 ticks
700        assert_eq!(tick_count(10010, 10000, 5), 2); // Absolute difference
701        assert_eq!(tick_count(10000, 10010, 0), 0); // Invalid tick size
702
703        // Test spread calculations
704        assert_eq!(spread_bps(9990, 10010), 20); // 20 spread / 10000 mid * 10000 = 20 bps
705        assert_eq!(spread_bps(10000, 10000), 0); // No spread
706        assert_eq!(spread_bps(10010, 9990), 0); // Invalid spread (bid > ask)
707        assert_eq!(spread_bps(0, 100), 0); // Invalid bid
708
709        // Test mid price calculations
710        assert_eq!(mid_price(9990, 10010), 10000);
711        assert_eq!(mid_price(10000, 10000), 10000);
712        assert_eq!(mid_price(0, 200), 100);
713
714        // Test tick price validation
715        assert!(is_valid_tick_price(10000, 5)); // 10000 % 5 == 0
716        assert!(!is_valid_tick_price(10001, 5)); // 10001 % 5 != 0
717        assert!(!is_valid_tick_price(10000, 0)); // Invalid tick size
718
719        // Test tick rounding
720        assert_eq!(round_to_tick(10001, 5), 10000); // Round down
721        assert_eq!(round_to_tick(10003, 5), 10005); // Round up
722        assert_eq!(round_to_tick(10000, 5), 10000); // Already aligned
723        assert_eq!(round_to_tick(10001, 0), 10001); // Invalid tick size
724    }
725
726    #[test]
727    fn test_simd_const_contexts() {
728        // Test SIMD functions in const contexts
729        const CHUNKS_10: usize = simd_f64x4_chunks(10);
730        const BUFFER_SIZE_10: usize = simd_f64x4_buffer_size(10);
731        const OPTIMAL_CHUNKS: usize = optimal_simd_chunks(20, 5);
732
733        assert_eq!(CHUNKS_10, 3);
734        assert_eq!(BUFFER_SIZE_10, 12);
735        assert_eq!(OPTIMAL_CHUNKS, 5);
736    }
737
738    #[test]
739    fn test_hft_const_contexts() {
740        // Test HFT functions in const contexts
741        const PRICE_DIFF: u64 = price_diff_bps(10000, 10100);
742        const TICK_COUNT: u64 = tick_count(10000, 10010, 5);
743        const SPREAD: u64 = spread_bps(9990, 10010);
744        const MID: u64 = mid_price(9990, 10010);
745        const IS_VALID: bool = is_valid_tick_price(10000, 5);
746        const ROUNDED: u64 = round_to_tick(10003, 5);
747
748        assert_eq!(PRICE_DIFF, 100);
749        assert_eq!(TICK_COUNT, 2);
750        assert_eq!(SPREAD, 20);
751        assert_eq!(MID, 10000);
752        assert!(IS_VALID);
753        assert_eq!(ROUNDED, 10005);
754    }
755}