1 /* 2 * Copyright (c) 2017 Helmut Neemann 3 * Use of this source code is governed by the GPL v3 license 4 * that can be found in the LICENSE file. 5 */ 6 package de.neemann.digital.gui.components.karnaugh; 7 8 import de.neemann.digital.analyse.expression.*; 9 import de.neemann.digital.lang.Lang; 10 11 import java.util.ArrayList; 12 import java.util.HashSet; 13 import java.util.Iterator; 14 import java.util.List; 15 16 /** 17 * Creates the covers needed to draw a kv map 18 */ 19 public class KarnaughMap implements Iterable<KarnaughMap.Cover> { 20 21 private final ArrayList<Cell> cells; 22 private final List<Variable> vars; 23 private final ArrayList<Cover> covers; 24 private final Header headerLeft; 25 private final Header headerRight; 26 private final Header headerBottom; 27 private final Header headerTop; 28 29 /** 30 * Creates a new instance 31 * 32 * @param vars the variables used 33 * @param expr the expression 34 * @throws KarnaughException KarnaughException 35 */ KarnaughMap(List<Variable> vars, Expression expr)36 public KarnaughMap(List<Variable> vars, Expression expr) throws KarnaughException { 37 this(vars, expr, new MapLayout(vars.size())); 38 } 39 40 /** 41 * Creates a new instance 42 * 43 * @param vars the variables used 44 * @param expr the expression 45 * @param mapLayout the layout mode of the map 46 * @throws KarnaughException KarnaughException 47 */ KarnaughMap(List<Variable> vars, Expression expr, MapLayout mapLayout)48 public KarnaughMap(List<Variable> vars, Expression expr, MapLayout mapLayout) throws KarnaughException { 49 this.vars = vars; 50 cells = new ArrayList<>(); 51 covers = new ArrayList<>(); 52 53 switch (vars.size()) { 54 case 2: // create the needed KV cells 55 for (int row = 0; row < 2; row++) 56 for (int col = 0; col < 2; col++) 57 cells.add(new Cell(row, col)); 58 59 boolean leftMode = mapLayout.getInvert(0); 60 headerLeft = new Header(mapLayout.get(0), !leftMode, leftMode).toRows(2, this); 61 boolean topMode = mapLayout.getInvert(1); 62 headerTop = new Header(mapLayout.get(1), !topMode, topMode).toCols(2, this); 63 headerRight = null; 64 headerBottom = null; 65 break; 66 case 3: 67 for (int row = 0; row < 2; row++) 68 for (int col = 0; col < 4; col++) 69 cells.add(new Cell(row, col)); 70 71 leftMode = mapLayout.getInvert(0); 72 headerLeft = new Header(mapLayout.get(0), !leftMode, leftMode).toRows(4, this); 73 topMode = mapLayout.getInvert(1); 74 headerTop = new Header(mapLayout.get(1), !topMode, !topMode, topMode, topMode).toCols(2, this); 75 boolean bottomMode = mapLayout.getInvert(2); 76 headerBottom = new Header(mapLayout.get(2), !bottomMode, bottomMode, bottomMode, !bottomMode).toCols(2, this); 77 headerRight = null; 78 break; 79 case 4: 80 for (int row = 0; row < 4; row++) 81 for (int col = 0; col < 4; col++) 82 cells.add(new Cell(row, col)); 83 84 leftMode = mapLayout.getInvert(0); 85 headerLeft = new Header(mapLayout.get(0), !leftMode, !leftMode, leftMode, leftMode).toRows(4, this); 86 boolean rightMode = mapLayout.getInvert(1); 87 headerRight = new Header(mapLayout.get(1), !rightMode, rightMode, rightMode, !rightMode).toRows(4, this); 88 topMode = mapLayout.getInvert(2); 89 headerTop = new Header(mapLayout.get(2), !topMode, !topMode, topMode, topMode).toCols(4, this); 90 bottomMode = mapLayout.getInvert(3); 91 headerBottom = new Header(mapLayout.get(3), !bottomMode, bottomMode, bottomMode, !bottomMode).toCols(4, this); 92 break; 93 default: 94 throw new KarnaughException(Lang.get("err_toManyVars")); 95 } 96 for (Cell c : cells) // set the row index of the bool table to the cells 97 c.createBoolTableRow(); 98 99 addExpression(expr); // create the covers 100 } 101 102 /** 103 * Returns the cell at the given position 104 * 105 * @param row the row 106 * @param col the column 107 * @return the cell at this position 108 */ getCell(int row, int col)109 public Cell getCell(int row, int col) { 110 for (Cell cell : cells) 111 if (cell.is(row, col)) return cell; 112 throw new RuntimeException("cell not found"); 113 } 114 115 /** 116 * @return all cells 117 */ getCells()118 public ArrayList<Cell> getCells() { 119 return cells; 120 } 121 addExpression(Expression expr)122 private void addExpression(Expression expr) throws KarnaughException { 123 if (expr instanceof Not || expr instanceof Variable) { 124 addCover(expr); 125 } else if (expr instanceof Operation.And) { 126 addCover(((Operation.And) expr).getExpressions()); 127 } else if (expr instanceof Operation.Or) { 128 for (Expression and : ((Operation.Or) expr).getExpressions()) 129 if (and instanceof Operation.And) 130 addCover(((Operation.And) and).getExpressions()); 131 else if (and instanceof Not || and instanceof Variable) 132 addCover(and); 133 else 134 throw new KarnaughException(Lang.get("err_invalidExpression")); 135 } else if (!(expr instanceof Constant)) 136 throw new KarnaughException(Lang.get("err_invalidExpression")); 137 } 138 addCover(Expression expr)139 private void addCover(Expression expr) throws KarnaughException { 140 addCoverToCells(new Cover().add(getVarOf(expr))); 141 } 142 addCover(ArrayList<Expression> expressions)143 private void addCover(ArrayList<Expression> expressions) throws KarnaughException { 144 Cover cover = new Cover(); 145 for (Expression expr : expressions) 146 cover.add(getVarOf(expr)); 147 addCoverToCells(cover); 148 } 149 addCoverToCells(Cover cover)150 private void addCoverToCells(Cover cover) { 151 covers.add(cover); 152 153 HashSet<Integer> insetsUsed = new HashSet<>(); 154 for (Cell cell : cells) 155 cell.addCoverToCell(cover, insetsUsed); 156 for (int i = 0; i < 8; i++) 157 if (!insetsUsed.contains(i)) { 158 cover.inset = i; 159 break; 160 } 161 } 162 getVarOf(Expression expression)163 private VarState getVarOf(Expression expression) throws KarnaughException { 164 String name = null; 165 boolean invert = false; 166 if (expression instanceof Variable) { 167 name = ((Variable) expression).getIdentifier(); 168 invert = false; 169 } else if (expression instanceof Not) { 170 Expression ex = ((Not) expression).getExpression(); 171 if (ex instanceof Variable) { 172 name = ((Variable) ex).getIdentifier(); 173 invert = true; 174 } 175 } 176 if (name == null) throw new KarnaughException(Lang.get("err_invalidExpression")); 177 178 int var = vars.indexOf(new Variable(name)); 179 if (var < 0) throw new KarnaughException(Lang.get("err_invalidExpression")); 180 181 return new VarState(var, invert); 182 } 183 184 @Override iterator()185 public Iterator<Cover> iterator() { 186 return covers.iterator(); 187 } 188 189 /** 190 * @return the number of covers 191 */ size()192 public int size() { 193 return covers.size(); 194 } 195 196 /** 197 * @return the rows of the table 198 */ getRows()199 public int getRows() { 200 return headerLeft.size(); 201 } 202 203 /** 204 * @return the cols of the table 205 */ getColumns()206 public int getColumns() { 207 return headerTop.size(); 208 } 209 210 /** 211 * @return the left header 212 */ getHeaderLeft()213 public Header getHeaderLeft() { 214 return headerLeft; 215 } 216 217 /** 218 * @return the right header 219 */ getHeaderRight()220 public Header getHeaderRight() { 221 return headerRight; 222 } 223 224 /** 225 * @return the bottom header 226 */ getHeaderBottom()227 public Header getHeaderBottom() { 228 return headerBottom; 229 } 230 231 /** 232 * @return the top header 233 */ getHeaderTop()234 public Header getHeaderTop() { 235 return headerTop; 236 } 237 238 /** 239 * a single cell in the kv map 240 */ 241 public static final class Cell { 242 private final int row; 243 private final int col; 244 private final ArrayList<VarState> minTerm; // min term of the cell 245 private final ArrayList<Cover> covers; 246 private int boolTableRow; 247 Cell(int row, int col)248 private Cell(int row, int col) { 249 this.row = row; 250 this.col = col; 251 minTerm = new ArrayList<>(); 252 covers = new ArrayList<>(); 253 } 254 add(VarState varState)255 private void add(VarState varState) { 256 minTerm.add(varState); 257 } 258 is(int row, int col)259 private boolean is(int row, int col) { 260 return (this.row == row) && (this.col == col); 261 } 262 addCoverToCell(Cover cover, HashSet<Integer> insetsUsed)263 private void addCoverToCell(Cover cover, HashSet<Integer> insetsUsed) { 264 for (VarState s : minTerm) 265 if (cover.contains(s.not())) 266 return; 267 268 for (Cover c : covers) 269 insetsUsed.add(c.inset); 270 271 covers.add(cover); 272 cover.incCellCount(); 273 } 274 contains(Cover cover)275 private boolean contains(Cover cover) { 276 return covers.contains(cover); 277 } 278 createBoolTableRow()279 private void createBoolTableRow() { 280 int tableCols = minTerm.size(); 281 boolTableRow = 0; 282 for (VarState i : minTerm) { 283 if (!i.invert) 284 boolTableRow += (1 << (tableCols - i.num - 1)); 285 } 286 } 287 288 /** 289 * @return the row 290 */ getRow()291 public int getRow() { 292 return row; 293 } 294 295 /** 296 * @return the column 297 */ getCol()298 public int getCol() { 299 return col; 300 } 301 302 /** 303 * @return the row in the truth table this cell belongs to 304 */ getBoolTableRow()305 public int getBoolTableRow() { 306 return boolTableRow; 307 } 308 isVarInMinTerm(int var, boolean invert)309 boolean isVarInMinTerm(int var, boolean invert) { 310 return minTerm.contains(new VarState(var, invert)); 311 } 312 } 313 314 private static final class VarState { 315 private final int num; // number of the variable in the vars list 316 private final boolean invert; // true if variable is inverted 317 VarState(int num, boolean invert)318 private VarState(int num, boolean invert) { 319 this.num = num; 320 this.invert = invert; 321 } 322 323 @Override equals(Object o)324 public boolean equals(Object o) { 325 if (this == o) return true; 326 if (o == null || getClass() != o.getClass()) return false; 327 328 VarState varState = (VarState) o; 329 330 if (num != varState.num) return false; 331 return invert == varState.invert; 332 } 333 334 @Override hashCode()335 public int hashCode() { 336 int result = num; 337 result = 31 * result + (invert ? 1 : 0); 338 return result; 339 } 340 not()341 private VarState not() { 342 return new VarState(num, !invert); 343 } 344 } 345 346 /** 347 * a cover in the kv table 348 */ 349 public final class Cover { 350 private final ArrayList<VarState> varStates; 351 private Pos pos; 352 private int cellCount; 353 private int inset = 0; 354 Cover()355 private Cover() { 356 varStates = new ArrayList<>(); 357 } 358 add(VarState varState)359 private Cover add(VarState varState) { 360 varStates.add(varState); 361 return this; 362 } 363 contains(VarState s)364 private boolean contains(VarState s) { 365 return varStates.contains(s); 366 } 367 368 /** 369 * @return the position of the cover. Caution: Returns a bounding box! 370 */ getPos()371 public Pos getPos() { 372 if (pos == null) { 373 int rowMin = Integer.MAX_VALUE; 374 int rowMax = Integer.MIN_VALUE; 375 int colMin = Integer.MAX_VALUE; 376 int colMax = Integer.MIN_VALUE; 377 for (Cell c : cells) { 378 if (c.contains(this)) { 379 if (c.row > rowMax) rowMax = c.row; 380 if (c.row < rowMin) rowMin = c.row; 381 if (c.col > colMax) colMax = c.col; 382 if (c.col < colMin) colMin = c.col; 383 } 384 } 385 int width = colMax - colMin + 1; 386 int height = rowMax - rowMin + 1; 387 pos = new Pos(rowMin, colMin, width, height); 388 } 389 return pos; 390 } 391 incCellCount()392 private void incCellCount() { 393 cellCount++; 394 } 395 396 /** 397 * @return the size of a cover 398 */ getSize()399 public int getSize() { 400 return cellCount; 401 } 402 403 /** 404 * @return the inset of this cover; 405 */ getInset()406 public int getInset() { 407 return inset; 408 } 409 410 /** 411 * @return true if cover is split, thus the cover is wrapping around the border 412 */ isDisconnected()413 public boolean isDisconnected() { 414 return getPos().width * getPos().height > cellCount; 415 } 416 417 /** 418 * @return covers only the edges 419 */ onlyEdges()420 public boolean onlyEdges() { 421 return getPos().width * getPos().height == 16 && cellCount == 4; 422 } 423 424 /** 425 * @return true if disconnected cover is vertical divided 426 */ isVerticalDivided()427 public boolean isVerticalDivided() { 428 Pos p = getPos(); 429 if (p.width * p.height == 16 && cellCount == 8) 430 return getCell(1, 0).contains(this); 431 else 432 return p.getWidth() > p.getHeight(); 433 } 434 } 435 436 /** 437 * The position of the cover. 438 * If a cover is wrapping around the borders the bounding box is returned! 439 */ 440 public static final class Pos { 441 private final int row; 442 private final int col; 443 private final int width; 444 private final int height; 445 Pos(int row, int col, int width, int height)446 private Pos(int row, int col, int width, int height) { 447 this.row = row; 448 this.col = col; 449 this.width = width; 450 this.height = height; 451 } 452 453 454 /** 455 * @return the row 456 */ getRow()457 public int getRow() { 458 return row; 459 } 460 461 /** 462 * @return the column 463 */ getCol()464 public int getCol() { 465 return col; 466 } 467 468 /** 469 * @return the width of the cover 470 */ getWidth()471 public int getWidth() { 472 return width; 473 } 474 475 /** 476 * @return the height of the cover 477 */ getHeight()478 public int getHeight() { 479 return height; 480 } 481 482 } 483 484 /** 485 * Defines the variables in the borders of the KV map 486 */ 487 public static final class Header { 488 private final int var; 489 private final boolean[] invert; 490 Header(int var, boolean... invert)491 private Header(int var, boolean... invert) { 492 this.var = var; 493 this.invert = invert; 494 } 495 496 /** 497 * @return the variable 498 */ getVar()499 public int getVar() { 500 return var; 501 } 502 503 /** 504 * @return the size 505 */ size()506 public int size() { 507 return invert.length; 508 } 509 510 /** 511 * Returns the variable state 512 * 513 * @param i the index of the row column 514 * @return true if inverted variable 515 */ getInvert(int i)516 public boolean getInvert(int i) { 517 return invert[i]; 518 } 519 520 /** 521 * Initializes the table according to the selected header. 522 * 523 * @param cols the number columns in the table 524 * @param kmap the k-map to use 525 * @return this for chained calls 526 */ toRows(int cols, KarnaughMap kmap)527 public Header toRows(int cols, KarnaughMap kmap) { 528 for (int row = 0; row < invert.length; row++) 529 for (int col = 0; col < cols; col++) 530 kmap.getCell(row, col).add(new VarState(var, invert[row])); 531 return this; 532 } 533 534 /** 535 * Initializes the table according to the selected header. 536 * 537 * @param rows the number rows in the table 538 * @param kmap the k-map to use 539 * @return this for chained calls 540 */ toCols(int rows, KarnaughMap kmap)541 public Header toCols(int rows, KarnaughMap kmap) { 542 for (int col = 0; col < invert.length; col++) 543 for (int row = 0; row < rows; row++) 544 kmap.getCell(row, col).add(new VarState(var, invert[col])); 545 return this; 546 } 547 548 } 549 } 550