From 8af81d08557a60196ca0914aec09350460c2566b Mon Sep 17 00:00:00 2001 From: datawater <86855332+datawater@users.noreply.github.com> Date: Fri, 11 Oct 2024 13:51:45 +0400 Subject: [PATCH] bug(pgn): fixed variation pointer calculation --- libcmbr/src/cmbr/pgntocmbr.rs | 3 ++- libcmbr/src/cmbr/structs.rs | 4 ++-- libcmbr/src/pgn/ast.rs | 12 ++++++---- libcmbr/src/pgn/tests.rs | 2 +- libcmbr/src/utils.rs | 44 ++++++++++++++++++++++++++++++++++- 5 files changed, 56 insertions(+), 9 deletions(-) diff --git a/libcmbr/src/cmbr/pgntocmbr.rs b/libcmbr/src/cmbr/pgntocmbr.rs index 81a4920..0ebf005 100644 --- a/libcmbr/src/cmbr/pgntocmbr.rs +++ b/libcmbr/src/cmbr/pgntocmbr.rs @@ -1,6 +1,7 @@ use super::{CmbrFile, SanToCmbrMvConvertor}; use crate::cmbr::CmbrGame; use crate::cmbr::CmbrVariation; +use crate::pgn::VariationPointerT; use crate::pgn::{PgnGame, PgnToken}; use pgn_lexer::parser::Token; @@ -143,7 +144,7 @@ impl CmbrFile { let variations = &game.variations; let variations_iter = variations.iter(); - let mut variation_pointers: HashMap = HashMap::with_capacity(1); + let mut variation_pointers: HashMap = HashMap::with_capacity(1); variation_pointers.insert(0, 0); for (id, variation) in variations_iter { diff --git a/libcmbr/src/cmbr/structs.rs b/libcmbr/src/cmbr/structs.rs index 69f34d8..b7cd6e1 100644 --- a/libcmbr/src/cmbr/structs.rs +++ b/libcmbr/src/cmbr/structs.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use super::u24; -use crate::utils::def_enum; +use crate::{pgn::VariationPointerT, utils::def_enum}; use litemap::LiteMap; def_enum! ( @@ -73,7 +73,7 @@ pub struct CmbrGame { /// 'u': Undefined. pub result: char, /// Variation pointer (main variation is 0) - pub variations: LiteMap, + pub variations: LiteMap, pub encountered_positions: HashMap, } diff --git a/libcmbr/src/pgn/ast.rs b/libcmbr/src/pgn/ast.rs index 038a18a..d82d06f 100644 --- a/libcmbr/src/pgn/ast.rs +++ b/libcmbr/src/pgn/ast.rs @@ -2,13 +2,17 @@ use litemap::LiteMap; use pgn_lexer::parser::Token; use std::collections::VecDeque; +use crate::utils::nth_prime_number; + +pub type VariationPointerT = u32; + /// An enumeration representing different types of PGN tokens. #[derive(Debug, Clone, Default, PartialEq, Eq)] pub enum PgnToken<'a> { /// Represents a token specific to the game, such as a move, header, or result. Token(Token<'a>), /// Represents a pointer to a variation. - VariationPointer(u16), + VariationPointer(VariationPointerT), /// Represents no token. This is the default variant. #[default] None, @@ -26,7 +30,7 @@ pub struct PgnVariation<'a>(pub Vec>); #[derive(Debug, Clone, Default, PartialEq, Eq)] pub struct PgnGame<'a> { pub global_tokens: Vec>, - pub variations: LiteMap>, + pub variations: LiteMap>, } /// Builds an ast (represented as `a Vec`) from the inputted Token list @@ -76,7 +80,7 @@ fn next_token<'a>( tokens: &mut VecDeque>, tree: &mut Vec>, game_number: &mut u32, - variation_depth: u16, + variation_depth: VariationPointerT, amount_of_encountered_variations: &mut u16, ) { // NOTE: I don't know if this is slow. (Like this whole approach) I'm just gonna pretend it isn't until it causes problems @@ -120,7 +124,7 @@ fn next_token<'a>( } } Token::StartVariation(_) => { - let new_variation_depth = *amount_of_encountered_variations * (variation_depth + 1); + let new_variation_depth = nth_prime_number::(*amount_of_encountered_variations as u32) * (variation_depth + 1); *amount_of_encountered_variations += 1; diff --git a/libcmbr/src/pgn/tests.rs b/libcmbr/src/pgn/tests.rs index 746fb58..848e227 100644 --- a/libcmbr/src/pgn/tests.rs +++ b/libcmbr/src/pgn/tests.rs @@ -38,7 +38,7 @@ mod pgn_tests { let ast = crate::pgn::parse_pgn(&mut mmap); - let ast_expected = "[PgnGame { global_tokens: [TagSymbol([69]), TagString([69]), TagSymbol([83]), TagString([83]), Result([42])], variations: LiteMap { values: [(0, PgnVariation([Token(MoveNumber(1, false)), Token(Move([101, 52])), VariationPointer(1), VariationPointer(2), Token(MoveNumber(1, true)), Token(Move([101, 53])), Token(MoveNumber(2, false)), Token(Move([78, 102, 51])), VariationPointer(3), VariationPointer(5), Token(MoveNumber(2, true)), Token(Move([78, 99, 54])), Token(MoveNumber(3, false)), Token(Move([66, 99, 52])), Token(Move([66, 99, 53])), Token(MoveNumber(4, false)), Token(Move([79, 45, 79]))])), (1, PgnVariation([Token(MoveNumber(1, false)), Token(Move([100, 52]))])), (2, PgnVariation([Token(MoveNumber(1, false)), Token(Move([99, 52]))])), (3, PgnVariation([Token(MoveNumber(2, false)), Token(Move([78, 99, 51])), Token(Move([100, 53])), VariationPointer(16)])), (5, PgnVariation([Token(MoveNumber(2, false)), Token(Move([100, 51]))])), (16, PgnVariation([Token(MoveNumber(2, true)), Token(Move([100, 54]))]))], _key_type: PhantomData, _value_type: PhantomData } }, PgnGame { global_tokens: [TagSymbol([69]), TagString([69]), TagSymbol([83]), TagString([83]), Result([42])], variations: LiteMap { values: [(0, PgnVariation([Token(MoveNumber(1, false)), Token(Move([101, 52])), VariationPointer(1), VariationPointer(2), Token(MoveNumber(1, true)), Token(Move([101, 53])), Token(MoveNumber(2, false)), Token(Move([78, 102, 51])), VariationPointer(3), VariationPointer(5), Token(Commentary([32, 67, 111, 109, 109, 101, 110, 116, 32]))])), (1, PgnVariation([Token(MoveNumber(1, false)), Token(Move([100, 52]))])), (2, PgnVariation([Token(MoveNumber(1, false)), Token(Move([99, 52]))])), (3, PgnVariation([Token(MoveNumber(2, false)), Token(Move([78, 99, 51])), Token(Move([100, 53])), VariationPointer(16)])), (5, PgnVariation([Token(MoveNumber(2, false)), Token(Move([100, 51]))])), (16, PgnVariation([Token(MoveNumber(2, true)), Token(Move([100, 54]))]))], _key_type: PhantomData, _value_type: PhantomData } }]"; + let ast_expected = "[PgnGame { global_tokens: [TagSymbol([69]), TagString([69]), TagSymbol([83]), TagString([83]), Result([42])], variations: LiteMap { values: [(0, PgnVariation([Token(MoveNumber(1, false)), Token(Move([101, 52])), VariationPointer(2), VariationPointer(3), Token(MoveNumber(1, true)), Token(Move([101, 53])), Token(MoveNumber(2, false)), Token(Move([78, 102, 51])), VariationPointer(5), VariationPointer(11), Token(MoveNumber(2, true)), Token(Move([78, 99, 54])), Token(MoveNumber(3, false)), Token(Move([66, 99, 52])), Token(Move([66, 99, 53])), Token(MoveNumber(4, false)), Token(Move([79, 45, 79]))])), (2, PgnVariation([Token(MoveNumber(1, false)), Token(Move([100, 52]))])), (3, PgnVariation([Token(MoveNumber(1, false)), Token(Move([99, 52]))])), (5, PgnVariation([Token(MoveNumber(2, false)), Token(Move([78, 99, 51])), Token(Move([100, 53])), VariationPointer(42)])), (11, PgnVariation([Token(MoveNumber(2, false)), Token(Move([100, 51]))])), (42, PgnVariation([Token(MoveNumber(2, true)), Token(Move([100, 54]))]))], _key_type: PhantomData, _value_type: PhantomData } }, PgnGame { global_tokens: [TagSymbol([69]), TagString([69]), TagSymbol([83]), TagString([83]), Result([42])], variations: LiteMap { values: [(0, PgnVariation([Token(MoveNumber(1, false)), Token(Move([101, 52])), VariationPointer(2), VariationPointer(3), Token(MoveNumber(1, true)), Token(Move([101, 53])), Token(MoveNumber(2, false)), Token(Move([78, 102, 51])), VariationPointer(5), VariationPointer(11), Token(Commentary([32, 67, 111, 109, 109, 101, 110, 116, 32]))])), (2, PgnVariation([Token(MoveNumber(1, false)), Token(Move([100, 52]))])), (3, PgnVariation([Token(MoveNumber(1, false)), Token(Move([99, 52]))])), (5, PgnVariation([Token(MoveNumber(2, false)), Token(Move([78, 99, 51])), Token(Move([100, 53])), VariationPointer(42)])), (11, PgnVariation([Token(MoveNumber(2, false)), Token(Move([100, 51]))])), (42, PgnVariation([Token(MoveNumber(2, true)), Token(Move([100, 54]))]))], _key_type: PhantomData, _value_type: PhantomData } }]"; assert_eq!(format!("{:?}", ast), ast_expected); } diff --git a/libcmbr/src/utils.rs b/libcmbr/src/utils.rs index 64d1a44..0aecd23 100644 --- a/libcmbr/src/utils.rs +++ b/libcmbr/src/utils.rs @@ -1,4 +1,4 @@ -use std::ops::{BitAnd, Shl, Shr, Sub}; +use std::{collections::{BTreeSet, VecDeque}, ops::{BitAnd, Shl, Shr, Sub}}; // Macro stolen from https://stackoverflow.com/a/62759540 macro_rules! def_enum { @@ -34,3 +34,45 @@ where let mask = (T::from(1u8) << num_bits) - T::from(1u8); return (number >> start_position) & mask; } + +pub(crate) fn nth_prime_number(n: u32) -> T +where T: std::cmp::Ord + std::clone::Clone + std::convert::From +{ + const PRIMES_LOOKUP: &[u32] = &[ + 1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, + 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, + 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, + 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, + 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, + 503, 509, 521, 523, 541, + ]; + + if n <= 100 { + return T::from(PRIMES_LOOKUP[n as usize]); + } + + let limit = if n < 6 { + 15 // Small value for small n + } else { + let log_n = (n as f64).ln(); + (n as f64 * (log_n + log_n.ln())).ceil() as usize + }; + + let mut isprime = VecDeque::from(vec![true; limit]); + let mut prime = BTreeSet::new(); + + isprime[0] = false; + isprime[1] = false; + + for i in 2..limit { + if isprime[i] { + prime.insert(T::from(i as u32)); + + for j in (i * i..limit).step_by(i) { + isprime[j] = false; + } + } + } + + prime.iter().nth(n as usize - 1).cloned().unwrap() +} \ No newline at end of file