diff --git a/PKHeX.Core/Legality/RNG/Algorithms/LCRNG.cs b/PKHeX.Core/Legality/RNG/Algorithms/LCRNG.cs
index 751dac675..70d652ced 100644
--- a/PKHeX.Core/Legality/RNG/Algorithms/LCRNG.cs
+++ b/PKHeX.Core/Legality/RNG/Algorithms/LCRNG.cs
@@ -1,3 +1,4 @@
+using System;
using System.Runtime.CompilerServices;
namespace PKHeX.Core;
@@ -123,4 +124,63 @@ public static class LCRNG
seed = Prev(seed);
return seed;
}
+
+ ///
+ /// Multiplication constants for jumping 2^(index) frames forward.
+ ///
+ private static ReadOnlySpan JumpMult =>
+ [
+ 0x41C64E6D, 0xC2A29A69, 0xEE067F11, 0xCFDDDF21, 0x5F748241, 0x8B2E1481, 0x76006901, 0x1711D201,
+ 0xBE67A401, 0xDDDF4801, 0x3FFE9001, 0x90FD2001, 0x65FA4001, 0xDBF48001, 0xF7E90001, 0xEFD20001,
+ 0xDFA40001, 0xBF480001, 0x7E900001, 0xFD200001, 0xFA400001, 0xF4800001, 0xE9000001, 0xD2000001,
+ 0xA4000001, 0x48000001, 0x90000001, 0x20000001, 0x40000001, 0x80000001, 0x00000001, 0x00000001,
+ ];
+
+ ///
+ /// Addition constants for jumping 2^(index) frames forward.
+ ///
+ private static ReadOnlySpan JumpAdd =>
+ [
+ 0x00006073, 0xE97E7B6A, 0x31B0DDE4, 0x67DBB608, 0xCBA72510, 0x1D29AE20, 0xBA84EC40, 0x79F01880,
+ 0x08793100, 0x6B566200, 0x803CC400, 0xA6B98800, 0xE6731000, 0x30E62000, 0xF1CC4000, 0x23988000,
+ 0x47310000, 0x8E620000, 0x1CC40000, 0x39880000, 0x73100000, 0xE6200000, 0xCC400000, 0x98800000,
+ 0x31000000, 0x62000000, 0xC4000000, 0x88000000, 0x10000000, 0x20000000, 0x40000000, 0x80000000,
+ ];
+
+ ///
+ /// Computes the amount of advances (distance) between two seeds.
+ ///
+ /// Initial seed
+ /// Final seed
+ /// Count of advances from to arrive at .
+ ///
+ /// To compute the distance, we abuse the fact that a given state bit at index `i` has a periodicity of `2^i`.
+ /// If the bit is present in the state, we must include that bit in our distance result.
+ /// The algorithmic complexity is O(log(n)) for finding n advancements.
+ /// We store a precomputed table of multiply & addition constants (skip 2^n) to avoid computing them on the fly.
+ ///
+ public static uint GetDistance(in uint start, in uint end)
+ {
+ int i = 0;
+ uint bit = 1u;
+
+ uint distance = 0u;
+ uint seed = start;
+
+ // Instead of doing a for loop which always does 32 iterations, check to see if we end up at the end seed.
+ // If we do, we can return after [0..31] jumps.
+ // Due to the inputs, we normally have low distance, so normally this won't take more than a few loops.
+ while (seed != end)
+ {
+ // 50:50 odds of this being true.
+ if (((seed ^ end) & bit) != 0)
+ {
+ seed = (seed * JumpMult[i]) + JumpAdd[i];
+ distance |= bit;
+ }
+ i++;
+ bit <<= 1;
+ }
+ return distance;
+ }
}
diff --git a/PKHeX.Core/Legality/RNG/Algorithms/XDRNG.cs b/PKHeX.Core/Legality/RNG/Algorithms/XDRNG.cs
index 2caff8c0d..568085752 100644
--- a/PKHeX.Core/Legality/RNG/Algorithms/XDRNG.cs
+++ b/PKHeX.Core/Legality/RNG/Algorithms/XDRNG.cs
@@ -284,4 +284,63 @@ public static class XDRNG
}
return ctr;
}
+
+ ///
+ /// Multiplication constants for jumping 2^(index) frames forward.
+ ///
+ private static ReadOnlySpan JumpMult =>
+ [
+ 0x000343FD, 0xA9FC6809, 0xDDFF5051, 0xF490B9A1, 0x43BA1741, 0xD290BE81, 0x82E3BD01, 0xBF507A01,
+ 0xF8C4F401, 0x7A19E801, 0x1673D001, 0xB5E7A001, 0x8FCF4001, 0xAF9E8001, 0x9F3D0001, 0x3E7A0001,
+ 0x7CF40001, 0xF9E80001, 0xF3D00001, 0xE7A00001, 0xCF400001, 0x9E800001, 0x3D000001, 0x7A000001,
+ 0xF4000001, 0xE8000001, 0xD0000001, 0xA0000001, 0x40000001, 0x80000001, 0x00000001, 0x00000001,
+ ];
+
+ ///
+ /// Addition constants for jumping 2^(index) frames forward.
+ ///
+ private static ReadOnlySpan JumpAdd =>
+ [
+ 0x00269EC3, 0x1E278E7A, 0x098520C4, 0x7E1DBEC8, 0x3E314290, 0x824E1920, 0x844E8240, 0xFD864480,
+ 0xDFB18900, 0xD9F71200, 0x5E3E2400, 0x65BC4800, 0x70789000, 0x74F12000, 0x39E24000, 0xB3C48000,
+ 0x67890000, 0xCF120000, 0x9E240000, 0x3C480000, 0x78900000, 0xF1200000, 0xE2400000, 0xC4800000,
+ 0x89000000, 0x12000000, 0x24000000, 0x48000000, 0x90000000, 0x20000000, 0x40000000, 0x80000000,
+ ];
+
+ ///
+ /// Computes the amount of advances (distance) between two seeds.
+ ///
+ /// Initial seed
+ /// Final seed
+ /// Count of advances from to arrive at .
+ ///
+ /// To compute the distance, we abuse the fact that a given state bit at index `i` has a periodicity of `2^i`.
+ /// If the bit is present in the state, we must include that bit in our distance result.
+ /// The algorithmic complexity is O(log(n)) for finding n advancements.
+ /// We store a precomputed table of multiply & addition constants (skip 2^n) to avoid computing them on the fly.
+ ///
+ public static uint GetDistance(in uint start, in uint end)
+ {
+ int i = 0;
+ uint bit = 1u;
+
+ uint distance = 0u;
+ uint seed = start;
+
+ // Instead of doing a for loop which always does 32 iterations, check to see if we end up at the end seed.
+ // If we do, we can return after [0..31] jumps.
+ // Due to the inputs, we normally have low distance, so normally this won't take more than a few loops.
+ while (seed != end)
+ {
+ // 50:50 odds of this being true.
+ if (((seed ^ end) & bit) != 0)
+ {
+ seed = (seed * JumpMult[i]) + JumpAdd[i];
+ distance |= bit;
+ }
+ i++;
+ bit <<= 1;
+ }
+ return distance;
+ }
}
diff --git a/Tests/PKHeX.Core.Tests/PKM/LCRNGTest.cs b/Tests/PKHeX.Core.Tests/PKM/LCRNGTest.cs
new file mode 100644
index 000000000..9bbe568d4
--- /dev/null
+++ b/Tests/PKHeX.Core.Tests/PKM/LCRNGTest.cs
@@ -0,0 +1,29 @@
+using FluentAssertions;
+using Xunit;
+
+namespace PKHeX.Core.Tests.PKM;
+
+public class LCRNGTest
+{
+ [Theory]
+ [InlineData(0x12345u, 0xAEA0DF8C, 12345u)]
+ [InlineData(0xBADC0DED, 0xBADC0DED, 0u)]
+ [InlineData(0, 0x0A3561A1, uint.MaxValue)]
+ [InlineData(0x0A3561A1, 0, 1u)]
+ public void FindFrame(uint start, uint end, uint expect)
+ {
+ var distance = LCRNG.GetDistance(start, end);
+ distance.Should().Be(expect);
+ }
+
+ [Theory]
+ [InlineData(8675309, 0x75C29428, 8675309)]
+ [InlineData(0xBADC0DED, 0xBADC0DED, 0u)]
+ [InlineData(0, 0xA170F641, uint.MaxValue)]
+ [InlineData(0xA170F641, 0, 1u)]
+ public void FindFrameXDRNG(uint start, uint end, uint expect)
+ {
+ var distance = XDRNG.GetDistance(start, end);
+ distance.Should().Be(expect);
+ }
+}