1import { useState, useEffect } from 'react';
2import { createTheme, CssBaseline, ThemeProvider, Typography, Button, Box, CircularProgress, Select, MenuItem, FormControl, InputLabel, FormControlLabel, Checkbox, Alert } from '@mui/material';
3import { GCDTable } from './components/GCDTable';
4import { getNextInteger, getPreviousInteger, getInitialIntegerForViewport, INTEGERS } from './utils/integerUtils';
6const theme = createTheme({
7 palette: {
8 mode: 'light',
9 primary: {
10 main: '#1976d2',
11 },
12 },
13});
15function App() {
16 const [currentInteger, setCurrentInteger] = useState(() => getInitialIntegerForViewport());
17 const [isComputing, setIsComputing] = useState(true);
18 const [showColorBar, setShowColorBar] = useState(false);
19 const [isMobile, setIsMobile] = useState(false);
21 // Detect mobile device
22 useEffect(() => {
23 const checkMobile = () => {
24 const isMobileDevice = window.innerWidth < 768; // Standard tablet/mobile breakpoint
25 setIsMobile(isMobileDevice);
26 };
28 checkMobile();
29 window.addEventListener('resize', checkMobile);
31 return () => window.removeEventListener('resize', checkMobile);
32 }, []);
34 const handleNextInteger = () => {
35 setCurrentInteger(prev => getNextInteger(prev));
36 };
38 const handlePreviousInteger = () => {
39 setCurrentInteger(prev => getPreviousInteger(prev));
40 };
42 return (
43 <ThemeProvider theme={theme}>
44 <CssBaseline />
45 <Box sx={{
46 display: 'flex',
47 flexDirection: { xs: 'column', md: 'row' },
48 height: '100vh',
49 overflow: 'hidden'
50 }}>
51 {/* Sidebar with controls */}
52 <Box sx={{
53 width: { xs: '100%', md: 300 },
54 maxHeight: { xs: 'auto', md: '100vh' },
55 p: { xs: 2, md: 3 },
56 borderRight: { xs: 0, md: 1 },
57 borderBottom: { xs: 1, md: 0 },
58 borderColor: 'divider',
59 display: 'flex',
60 flexDirection: 'column',
61 gap: 2,
62 overflowY: { xs: 'visible', md: 'auto' }
63 }}>
64 <Typography variant="h5" component="h1" gutterBottom>
65 GCD Table
66 </Typography>
68 <Box>
69 <Box sx={{ display: 'flex', alignItems: 'center', gap: 2, mb: 1 }}>
70 {isComputing && <CircularProgress size={20} />}
71 </Box>
72 <Typography variant="body2" color="text.secondary" paragraph>
73 Log GCD table for integers 1 to {currentInteger}
74 </Typography>
75 <Typography variant="body2" color="text.secondary" paragraph>
76 Showing ({currentInteger} × {currentInteger}) grid
77 </Typography>
78 <FormControl fullWidth sx={{ mb: 2 }}>
79 <InputLabel id="integer-select-label">Select Size</InputLabel>
80 <Select
81 labelId="integer-select-label"
82 id="integer-select"
83 value={currentInteger}
84 label="Select Integer"
85 onChange={(e) => setCurrentInteger(e.target.value as number)}
86 >
87 {INTEGERS.map((integer) => (
88 <MenuItem key={integer} value={integer}>
89 {integer}
90 </MenuItem>
91 ))}
92 </Select>
93 </FormControl>
94 <Box sx={{ display: 'flex', gap: 1 }}>
95 <Button
96 variant="outlined"
97 onClick={handlePreviousInteger}
98 fullWidth
99 disabled={currentInteger === INTEGERS[0]}
100 >
101 Previous
102 </Button>
103 <Button
104 variant="contained"
105 onClick={handleNextInteger}
106 fullWidth
107 disabled={currentInteger === INTEGERS[INTEGERS.length - 1]}
108 >
109 Next
110 </Button>
111 </Box>
112 </Box>
114 <Box sx={{ mt: 2 }}>
115 <Typography variant="body2" color="text.secondary">
116 Each cell (i, j) shows the value of log(gcd(i, j))
117 </Typography>
118 </Box>
120 {isMobile ? (
121 <Box sx={{ mt: 2 }}>
122 <Alert severity="info" sx={{ fontSize: '0.875rem' }}>
123 This visualization is best viewed on a desktop.
124 </Alert>
125 </Box>
126 ) : (
127 <Box sx={{ mt: 2 }}>
128 <FormControlLabel
129 control={
130 <Checkbox
131 checked={showColorBar}
132 onChange={(e) => setShowColorBar(e.target.checked)}
133 />
134 }
135 label="Show Color Bar"
136 />
137 </Box>
138 )}
139 </Box>
141 {/* Main content area */}
142 <Box sx={{
143 flex: 1,
144 display: 'flex',
145 alignItems: 'center',
146 justifyContent: 'center',
147 p: { xs: 1, md: 2 },
148 overflow: 'hidden',
149 minHeight: 0
150 }}>
151 <GCDTable
152 n={currentInteger}
153 onLoadingChange={setIsComputing}
154 showColorBar={showColorBar}
155 />
156 </Box>
157 </Box>
158 </ThemeProvider>
159 );
160}
162export default App;