@@ -1252,115 +1252,167 @@ class Visualizer {
12521252 }
12531253
12541254 /**
1255- * Render Karnaugh Map (K-map)
1256- * Supports 2, 3, and 4 variable expressions
1255+ * Generate n-bit Gray code sequence
1256+ * e.g. grayCode(2) → ['00','01','11','10']
12571257 */
1258- renderKMap ( containerElement ) {
1259- containerElement . innerHTML = '' ;
1260-
1261- const numVars = this . variables . length ;
1258+ grayCode ( n ) {
1259+ if ( n === 0 ) return [ '' ] ;
1260+ if ( n === 1 ) return [ '0' , '1' ] ;
1261+ const prev = this . grayCode ( n - 1 ) ;
1262+ return [
1263+ ...prev . map ( code => '0' + code ) ,
1264+ ...[ ...prev ] . reverse ( ) . map ( code => '1' + code )
1265+ ] ;
1266+ }
12621267
1263- if ( numVars < 2 || numVars > 4 ) {
1264- containerElement . innerHTML = `<p>K-maps are only supported for 2–4 variables (you have ${ numVars } )</p>` ;
1265- return ;
1266- }
1268+ /**
1269+ * Build a single K-map table for given row/col variables and a prefix key
1270+ * rowVars: array of variable names for the row axis
1271+ * colVars: array of variable names for the column axis
1272+ * prefix: fixed bit string prepended to each lookup key
1273+ * valueMap: full truth table lookup { binaryKey → 0|1 }
1274+ */
1275+ buildKMapTable ( rowVars , colVars , prefix , valueMap ) {
1276+ const rowGray = this . grayCode ( rowVars . length ) ;
1277+ const colGray = this . grayCode ( colVars . length ) ;
12671278
1268- // Create a lookup: binary-key → output value
1269- const valueMap = { } ;
1270- this . truthTable . forEach ( row => {
1271- const key = this . variables . map ( v => row [ v ] ) . join ( '' ) ;
1272- valueMap [ key ] = row . output ;
1273- } ) ;
1279+ const rowLabel = rowVars . map ( v => v . toUpperCase ( ) ) . join ( '' ) ;
1280+ const colLabel = colVars . map ( v => v . toUpperCase ( ) ) . join ( '' ) ;
12741281
12751282 const table = document . createElement ( 'table' ) ;
12761283 table . className = 'kmap-table' ;
12771284
1278- if ( numVars === 2 ) {
1279- this . renderKMap2Var ( table , valueMap ) ;
1280- } else if ( numVars === 3 ) {
1281- this . renderKMap3Var ( table , valueMap ) ;
1282- } else if ( numVars === 4 ) {
1283- this . renderKMap4Var ( table , valueMap ) ;
1284- }
1285-
1286- containerElement . appendChild ( table ) ;
1287- }
1288-
1289- renderKMap2Var ( table , valueMap ) {
1290- const [ v0 , v1 ] = this . variables . map ( v => v . toUpperCase ( ) ) ;
1291-
1285+ // Header row
12921286 const headerRow = document . createElement ( 'tr' ) ;
1293- headerRow . innerHTML = `<th class="kmap-corner">${ v0 } \\ ${ v1 } </th><th>0</th><th>1</th>` ;
1287+ const corner = document . createElement ( 'th' ) ;
1288+ corner . className = 'kmap-corner' ;
1289+ corner . textContent = rowLabel + ' \\ ' + colLabel ;
1290+ headerRow . appendChild ( corner ) ;
1291+ colGray . forEach ( c => {
1292+ const th = document . createElement ( 'th' ) ;
1293+ th . textContent = c ;
1294+ headerRow . appendChild ( th ) ;
1295+ } ) ;
12941296 table . appendChild ( headerRow ) ;
12951297
1296- [ '0' , '1' ] . forEach ( r => {
1298+ // Data rows
1299+ rowGray . forEach ( r => {
12971300 const row = document . createElement ( 'tr' ) ;
1298- const rowLabel = document . createElement ( 'th' ) ;
1299- rowLabel . textContent = r ;
1300- row . appendChild ( rowLabel ) ;
1301-
1302- [ '0' , '1' ] . forEach ( c => {
1301+ const rowTh = document . createElement ( 'th' ) ;
1302+ rowTh . textContent = r ;
1303+ row . appendChild ( rowTh ) ;
1304+ colGray . forEach ( c => {
13031305 const td = document . createElement ( 'td' ) ;
1304- const key = r + c ;
1306+ const key = prefix + r + c ;
13051307 td . textContent = valueMap [ key ] ;
13061308 td . className = valueMap [ key ] === 1 ? 'high' : 'low' ;
13071309 row . appendChild ( td ) ;
13081310 } ) ;
1309-
13101311 table . appendChild ( row ) ;
13111312 } ) ;
1312- }
13131313
1314- renderKMap3Var ( table , valueMap ) {
1315- const [ v0 , v1 , v2 ] = this . variables . map ( v => v . toUpperCase ( ) ) ;
1316- const colOrder = [ '00' , '01' , '11' , '10' ] ;
1314+ return table ;
1315+ }
13171316
1318- const headerRow = document . createElement ( 'tr' ) ;
1319- headerRow . innerHTML = `<th class="kmap-corner">${ v0 } \\ ${ v1 } ${ v2 } </th><th>00</th><th>01</th><th>11</th><th>10</th>` ;
1320- table . appendChild ( headerRow ) ;
1317+ /**
1318+ * Render Karnaugh Map (K-map)
1319+ * 2-4 vars: single table
1320+ * 5+ vars: grid of labeled 4×4 sub-maps (standard textbook layout)
1321+ */
1322+ renderKMap ( containerElement ) {
1323+ containerElement . innerHTML = '' ;
13211324
1322- [ '0' , '1' ] . forEach ( r => {
1323- const row = document . createElement ( 'tr' ) ;
1324- const rowLabel = document . createElement ( 'th' ) ;
1325- rowLabel . textContent = r ;
1326- row . appendChild ( rowLabel ) ;
1325+ const numVars = this . variables . length ;
1326+ const vars = this . variables ; // already sorted
13271327
1328- colOrder . forEach ( c => {
1329- const td = document . createElement ( 'td' ) ;
1330- const key = r + c ;
1331- td . textContent = valueMap [ key ] ;
1332- td . className = valueMap [ key ] === 1 ? 'high' : 'low' ;
1333- row . appendChild ( td ) ;
1334- } ) ;
1328+ if ( numVars < 2 ) {
1329+ containerElement . innerHTML = '<p>K-maps require at least 2 variables.</p>' ;
1330+ return ;
1331+ }
13351332
1336- table . appendChild ( row ) ;
1333+ // Create a lookup: binary-key → output value
1334+ const valueMap = { } ;
1335+ this . truthTable . forEach ( row => {
1336+ const key = vars . map ( v => row [ v ] ) . join ( '' ) ;
1337+ valueMap [ key ] = row . output ;
13371338 } ) ;
1338- }
1339-
1340- renderKMap4Var ( table , valueMap ) {
1341- const [ v0 , v1 , v2 , v3 ] = this . variables . map ( v => v . toUpperCase ( ) ) ;
1342- const grayOrder = [ '00' , '01' , '11' , '10' ] ;
1343-
1344- const headerRow = document . createElement ( 'tr' ) ;
1345- headerRow . innerHTML = `<th class="kmap-corner">${ v0 } ${ v1 } \\ ${ v2 } ${ v3 } </th><th>00</th><th>01</th><th>11</th><th>10</th>` ;
1346- table . appendChild ( headerRow ) ;
13471339
1348- grayOrder . forEach ( r => {
1349- const row = document . createElement ( 'tr' ) ;
1350- const rowLabel = document . createElement ( 'th' ) ;
1351- rowLabel . textContent = r ;
1352- row . appendChild ( rowLabel ) ;
1340+ // Decide variable split:
1341+ // Row vars: first floor(innerVars/2) bits (max 2 for ≤4 total inner)
1342+ // Col vars: remaining inner bits (max 2 for ≤4 total inner)
1343+ // Outer vars (5+): extra variables that select which sub-map
1344+ //
1345+ // Standard textbook: inner map is always 4 vars (2 row + 2 col)
1346+ // except for 2 vars (1+1) and 3 vars (1+2)
1347+ let rowVars , colVars , outerVars ;
1348+
1349+ if ( numVars <= 4 ) {
1350+ // Single table, no outer vars
1351+ outerVars = [ ] ;
1352+ const innerCount = numVars ;
1353+ const rowCount = Math . floor ( innerCount / 2 ) ;
1354+ const colCount = innerCount - rowCount ;
1355+ rowVars = vars . slice ( 0 , rowCount ) ;
1356+ colVars = vars . slice ( rowCount , rowCount + colCount ) ;
1357+ } else {
1358+ // 5+ vars: first 2 → rows, next 2 → cols, rest → outer (sub-map selectors)
1359+ rowVars = vars . slice ( 0 , 2 ) ;
1360+ colVars = vars . slice ( 2 , 4 ) ;
1361+ outerVars = vars . slice ( 4 ) ;
1362+ }
13531363
1354- grayOrder . forEach ( c => {
1355- const td = document . createElement ( 'td' ) ;
1356- const key = r + c ;
1357- td . textContent = valueMap [ key ] ;
1358- td . className = valueMap [ key ] === 1 ? 'high' : 'low' ;
1359- row . appendChild ( td ) ;
1364+ if ( outerVars . length === 0 ) {
1365+ // Simple single table (2-4 vars)
1366+ const table = this . buildKMapTable ( rowVars , colVars , '' , valueMap ) ;
1367+ containerElement . appendChild ( table ) ;
1368+ } else {
1369+ // Multiple sub-maps in a grid
1370+ const outerGray = this . grayCode ( outerVars . length ) ;
1371+ const outerLabel = outerVars . map ( v => v . toUpperCase ( ) ) . join ( '' ) ;
1372+
1373+ // Determine grid layout (standard textbook):
1374+ // outerVars=1 → 1 row × 2 cols
1375+ // outerVars=2 → 2 rows × 2 cols (Gray-coded)
1376+ // outerVars=3 → 2 rows × 4 cols
1377+ // outerVars=4 → 4 rows × 4 cols
1378+ // General: gridRows = 2^floor(outerVars/2), gridCols = 2^ceil(outerVars/2)
1379+ const gridRowBits = Math . floor ( outerVars . length / 2 ) ;
1380+ const gridColBits = outerVars . length - gridRowBits ;
1381+ const gridRowGray = this . grayCode ( gridRowBits ) ;
1382+ const gridColGray = this . grayCode ( gridColBits ) ;
1383+
1384+ const gridRowVars = outerVars . slice ( 0 , gridRowBits ) ;
1385+ const gridColVars = outerVars . slice ( gridRowBits ) ;
1386+
1387+ const wrapper = document . createElement ( 'div' ) ;
1388+ wrapper . className = 'kmap-grid' ;
1389+ // CSS grid dimensions
1390+ wrapper . style . gridTemplateColumns = `repeat(${ gridColGray . length } , auto)` ;
1391+
1392+ gridRowGray . forEach ( gr => {
1393+ gridColGray . forEach ( gc => {
1394+ const prefix = gr + gc ;
1395+ const subDiv = document . createElement ( 'div' ) ;
1396+ subDiv . className = 'kmap-submap' ;
1397+
1398+ // Build label for this sub-map
1399+ const label = document . createElement ( 'div' ) ;
1400+ label . className = 'kmap-submap-label' ;
1401+ const parts = [ ] ;
1402+ for ( let i = 0 ; i < outerVars . length ; i ++ ) {
1403+ parts . push ( outerVars [ i ] . toUpperCase ( ) + '=' + prefix [ i ] ) ;
1404+ }
1405+ label . textContent = parts . join ( ', ' ) ;
1406+ subDiv . appendChild ( label ) ;
1407+
1408+ const table = this . buildKMapTable ( rowVars , colVars , prefix , valueMap ) ;
1409+ subDiv . appendChild ( table ) ;
1410+ wrapper . appendChild ( subDiv ) ;
1411+ } ) ;
13601412 } ) ;
13611413
1362- table . appendChild ( row ) ;
1363- } ) ;
1414+ containerElement . appendChild ( wrapper ) ;
1415+ }
13641416 }
13651417}
13661418
0 commit comments