File size: 4,672 Bytes
1295969
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
//! Master Pattern Protocol β€” mod-9 supersingular prime band classification.
//!
//! 15 supersingular primes in three bands:
//!   Band 0 (Common,    7 primes): {2,3,5,7,11,13,17}   digit_root(sum)=4
//!   Band 1 (Rare,      4 primes): {19,23,29,31}          digit_root(sum)=3
//!   Band 2 (Legendary, 4 primes): {41,47,59,71}          digit_root(sum)=2
//!
//! Closure invariant: 4+3+2=9 ≑ 0 (mod 9)  β€” verified at compile time.
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};

// ── Compile-time invariant checks ────────────────────────────────────────
const fn digit_root_const(n: u64) -> u64 {
    if n == 0 {
        return 0;
    }
    let r = n % 9;
    if r == 0 {
        9
    } else {
        r
    }
}

const BAND0: [u64; 7] = [2, 3, 5, 7, 11, 13, 17];
const BAND1: [u64; 4] = [19, 23, 29, 31];
const BAND2: [u64; 4] = [41, 47, 59, 71];

const fn sum_arr7(a: [u64; 7]) -> u64 {
    a[0] + a[1] + a[2] + a[3] + a[4] + a[5] + a[6]
}
const fn sum_arr4(a: [u64; 4]) -> u64 {
    a[0] + a[1] + a[2] + a[3]
}

const _: () = assert!(digit_root_const(sum_arr7(BAND0)) == 4, "Band0 DR must be 4");
const _: () = assert!(digit_root_const(sum_arr4(BAND1)) == 3, "Band1 DR must be 3");
const _: () = assert!(digit_root_const(sum_arr4(BAND2)) == 2, "Band2 DR must be 2");
const CLOSURE_SUM: u64 = digit_root_const(sum_arr7(BAND0))
    + digit_root_const(sum_arr4(BAND1))
    + digit_root_const(sum_arr4(BAND2));
const _: () = assert!(CLOSURE_SUM % 9 == 0, "Closure: 4+3+2 mod 9 must be 0");

// ── Runtime API ───────────────────────────────────────────────────────────
/// Mod-9 digit root (9β†’9, not 0).
pub fn digit_root(n: u64) -> u64 {
    if n == 0 {
        return 0;
    }
    let r = n % 9;
    if r == 0 {
        9
    } else {
        r
    }
}

/// Rarity tier based on band.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum RarityTier {
    Common,
    Rare,
    Legendary,
}

impl RarityTier {
    pub fn from_band(band: u8) -> Self {
        match band {
            0 => Self::Common,
            1 => Self::Rare,
            _ => Self::Legendary,
        }
    }
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Common => "Common",
            Self::Rare => "Rare",
            Self::Legendary => "Legendary",
        }
    }
}

/// Classify a prime into its band.
pub fn classify_prime(p: u64) -> Option<u8> {
    if BAND0.contains(&p) {
        return Some(0);
    }
    if BAND1.contains(&p) {
        return Some(1);
    }
    if BAND2.contains(&p) {
        return Some(2);
    }
    None
}

/// Map a band to its first (lowest) prime.
pub fn map_to_band_prime(band: u8) -> u64 {
    match band {
        0 => 2,
        1 => 19,
        _ => 41,
    }
}

/// Determine band from digit root.
pub fn band_from_digit_root(dr: u64) -> u8 {
    match dr {
        4 => 0,
        3 => 1,
        2 => 2,
        _ => 0,
    }
}

/// Full fingerprint of a track.
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct PatternFingerprint {
    pub hash_u64: u64,
    pub cycle_position: u8,
    pub digit_root: u64,
    pub expanded_root: u64,
    pub band: u8,
    pub band_residue: u64,
    pub mapped_prime: u64,
    pub parity: bool,
    pub parity_inverted: bool,
    pub closure_verified: bool,
}

/// Compute fingerprint from ISRC bytes + audio SHA-256 hash.
pub fn pattern_fingerprint(isrc_bytes: &[u8], audio_hash: &[u8; 32]) -> PatternFingerprint {
    let mut combined = isrc_bytes.to_vec();
    combined.extend_from_slice(audio_hash);
    let hash_bytes: [u8; 32] = Sha256::digest(&combined).into();
    let hash_u64 = u64::from_le_bytes(hash_bytes[..8].try_into().unwrap_or_default());

    let dr = digit_root(hash_u64);
    let band = band_from_digit_root(dr);
    let residue = (4u64 + 3 + 2).wrapping_sub(band as u64) % 9;
    let prime = map_to_band_prime(band);
    let expanded = {
        let s: u64 = hash_u64
            .to_string()
            .chars()
            .filter_map(|c| c.to_digit(10))
            .map(|d| d as u64)
            .sum();
        digit_root(s)
    };

    PatternFingerprint {
        hash_u64,
        cycle_position: (hash_u64 % 256) as u8,
        digit_root: dr,
        expanded_root: expanded,
        band,
        band_residue: residue,
        mapped_prime: prime,
        parity: hash_u64 % 2 == 1,
        parity_inverted: hash_u64 % 2 == 0,
        closure_verified: CLOSURE_SUM % 9 == 0,
    }
}