/ concept-collection / finite-field-visualizer
concept-collection / finite-field-visualizer
finite-field-visualizer / src / utils / primeUtils.ts
124 lines · 3.4 KBCodeBlameHistory
56938f5initialJeremy Magland 1/**
2 * Check if a number is prime
3 */
4function isPrime(n: number): boolean {
5 if (n < 2) return false;
6 if (n === 2) return true;
7 if (n % 2 === 0) return false;
8
9 const sqrt = Math.sqrt(n);
10 for (let i = 3; i <= sqrt; i += 2) {
11 if (n % i === 0) return false;
12 }
13 return true;
16/**
17 * Generate the first n prime numbers
18 */
19function generatePrimes(count: number): number[] {
20 const primes: number[] = [];
21 let candidate = 2;
23 while (primes.length < count) {
24 if (isPrime(candidate)) {
25 primes.push(candidate);
26 }
27 candidate++;
28 }
30 return primes;
33/**
34 * Precomputed list primes
35 */
36export const PRIMES = generatePrimes(2000);
38/**
39 * Get the index of a prime in the precomputed list, or -1 if not found
40 */
41export function getPrimeIndex(prime: number): number {
42 return PRIMES.indexOf(prime);
45/**
46 * Get the prime at a specific index in the precomputed list
47 */
48export function getPrimeAtIndex(index: number): number | undefined {
49 return PRIMES[index];
52/**
53 * Get the next prime in the precomputed list
54 */
55export function getNextPrime(currentPrime: number): number {
56 const index = getPrimeIndex(currentPrime);
57 if (index === -1 || index === PRIMES.length - 1) {
58 return currentPrime; // Stay at current if not found or at end
59 }
60 return PRIMES[index + 1];
63/**
64 * Get the previous prime in the precomputed list
65 */
66export function getPreviousPrime(currentPrime: number): number {
67 const index = getPrimeIndex(currentPrime);
68 if (index === -1 || index === 0) {
69 return currentPrime; // Stay at current if not found or at start
70 }
71 return PRIMES[index - 1];
74/**
75 * Calculate the best initial prime based on viewport dimensions
76 * The grid size will be (prime - 1) × (prime - 1) pixels
77 *
78 * @param maxPrime - Maximum allowed prime (default 2000)
79 * @returns The largest prime that will fit in the viewport without scrolling
80 */
81export function getInitialPrimeForViewport(maxPrime: number = 2000): number {
82 // MUI's 'md' breakpoint is 900px
83 const isMobile = window.innerWidth < 900;
85 let availableWidth: number;
86 let availableHeight: number;
88 if (isMobile) {
89 // Mobile layout: controls at top, full width available
90 // Account for left/right padding (~20px total on mobile)
91 availableWidth = window.innerWidth - 20;
93 // Account for controls section at top (approximately 400-450px) and padding
94 // Being conservative with 500px to ensure it fits
95 availableHeight = window.innerHeight - 500;
96 } else {
97 // Desktop layout: sidebar on left
98 // Account for sidebar (300px) and padding/margins (~50px total)
99 availableWidth = window.innerWidth - 350;
101 // Account for top/bottom padding (~50px total)
102 availableHeight = window.innerHeight - 50;
103 }
105 // Use the smaller dimension to ensure it fits both ways
106 const availableSize = Math.min(availableWidth, availableHeight);
108 // Find the largest prime where (prime - 1) fits in the available space
109 // The target grid size is availableSize, so target prime is availableSize + 1
110 const targetPrime = availableSize + 1;
112 // Find the largest prime that doesn't exceed targetPrime or maxPrime
113 const maxAllowedPrime = Math.min(targetPrime, maxPrime);
115 // Find the largest prime in our list that doesn't exceed the max
116 for (let i = PRIMES.length - 1; i >= 0; i--) {
117 if (PRIMES[i] <= maxAllowedPrime) {
118 return PRIMES[i];
119 }
120 }
122 // Fallback to smallest prime if viewport is too small
123 return PRIMES[0];