## DBsubject(Computer science) ## DBchapter(Digital logic) ## DBsection(Boolean expressions) ## Date(2025) ## Institution(University of Lethbridge) ## Author(Nicole Wilson and Andrew Parker) ## MO(1) ## KEYWORDS('digital logic', 'boolean', 'truth tables', 'kmaps') DOCUMENT(); loadMacros( "PGstandard.pl", "PGML.pl", "contextBoolean.pl", "niceTables.pl", "parserPopUp.pl", "parserMultiAnswer.pl" ); ############################################ # To create a questions # 1. Go to UPDATE DETAILS ############################################ ## PGMLTable helper functions sub wrapTeX { return '[`' . shift . '`]' } sub PGMLopts { my $opts = shift; return '{' . join(', ', map {"$_ => $opts->{$_}"} keys %$opts) . '}'; } sub wrapCell { my $cell = shift; my $opts; if (ref $cell eq 'ARRAY') { my $data = shift @$cell; $opts = {%$cell}; $cell = $data; } my $string = "[.$cell.]"; $string .= PGMLopts($opts) if $opts; return $string; } sub convertRow { my $row = shift; return join('', map { wrapCell($_) } @$row) . '* '; } sub PGMLTable { my $rows = shift; my %opts = @_; my $table = '[#' . join(' ', map { convertRow($_) } @$rows) . '#]'; if ($opts{layout}) { $table .= '*'; delete $opts{layout}; } $table .= PGMLopts(~~%opts) if %opts; return $table; } ## End PGMLTable helper functions ## K-map helper functions sub KmapMaker { @kmapOrder = ([0, 1, 3, 2], [4, 5, 7, 6]); my $label = shift; my $nVar = 3; my $nRow = 2; my $nCol = 4; @cLabels; push @cLabels, wrapTeX( $label ); push @cLabels, "0"; push @cLabels, "1"; @rLabels; push @rLabels, "00"; push @rLabels, "01"; push @rLabels, "11"; push @rLabels, "10"; my @kmap = ($cLabels); for $r (0 .. $nRow - 1) { my @row; push @row, $rlabels[$i]; for $c (0..$nCol - 1) { warn("not found at $r, $c") if !defined($kmapOrder[$r][$c]); push @row, $_[$kmapOrder[$r][$c]]; } push @{ $kmap[ $r + 1 ] }, ~~@row; } return @kmap; } ## End K-map helper functions ## Customize here Context("Boolean"); Context()->constants->set(T => { string => '1' }, F => { string => '0' }); Context()->operators->set( 'not' => { TeX => '\neg ' }, 'and' => { TeX => '\cdot ' }, 'or' => { TeX => '+' }, 'xor' => { TeX => '\oplus' } ); Context()->setPrecedence('oxan'); Context() ->strings->add(X => { caseSensitive => 1 }, i => { caseSensitive => 1 }); ################################################################################ # 1. UPDATE DETAILS ################################################################################ # Update the formulae labels. # A comma delimited list of strings, in double quotes. # The strings may contain latex formatting # These labels will appear at the top of the output columns of the truth table # In the formula table with the corresponding formulae defined below # and anywhere the output/formulae needs to be referenced. @fLabel = ("O_1", "O_2"); # Update the variables used in this problem # Declare a comma delimited list of boolean variables # each declaration has the format A => 'Boolean' where A is the # name of the variable. # The variables must be single letters, they are used in the formulae # in the truth table as the labels for the input columns # and anywhere else the inputs need to be regerenced. Context()->variables->are(A => 'Boolean', B => 'Boolean', C => 'Boolean'); # Formulae corresponding to the labels listed in fLabel @f = (Formula("(A+C)(B+C')"), Formula("(A'C')+(B'C)"),); # For the answers --- indicate the rows that contain don't cares for each label # If there are no don't cares for a label then put []. @dontcare = ([6], [],); # Indicate whether or not to ask students for the don't care in min/maxterms question # Default: true $showDontCareFields = 1; # For the answers --- indicate the rows that contain indeterminants for each label # If there are no indeterminants for a label then put []. @indeterm = ([], [3, 4],); ################################################################################ # End Problem details ################################################################################ ## End customization @vars = Context()->variables->names; $T = Compute('1'); $F = Compute('0'); @dontcareHash; for my $i (0 .. $#dontcare) { my %dcHash = map { $_ => 1 } @{ $dontcare[$i] }; push @dontcareHash, ~~%dcHash; } @indetermHash; for my $i (0 .. $#indeterm) { my %idHash = map { $_ => 1 } @{ $indeterm[$i] }; push @indetermHash, ~~%idHash; } # custom checker is required for MultiAnswer # can be modified to return partial credit -- currently all or nothing $columnChecker = sub { my ($c, $s, $ma, $ansHash) = @_; my $incorrect = 0; my $size = @$c - 1; for (0 .. $size) { $incorrect++ unless ($c->[$_] == $s->[$_]); } Value->Error("You have $incorrect incorrect entr" . ($incorrect == 1 ? 'y' : 'ies') . " in this column") if $incorrect; return 1; }; # custom checker is required for MultiAnswer, a list of sets # can be modified to return partial credit -- currently all or nothing $setsChecker = sub { my ($c, $s, $ma, $ansHash) = @_; my $incorrect = 0; my $size = @$c - 1; for (0 .. $size) { $incorrect++ unless ($c->[$_] == $s->[$_]); } Value->Error("You have $incorrect incorrect " . ($incorrect == 1 ? 'set' : 'sets') . " of terms.") if $incorrect; return 1; }; ############################# # Problem Text # build tables @varheader = map { wrapTeX($_) } @vars; @fheader = map { wrapTeX($_) } @fLabel; $header = [ @varheader, @fheader ]; @table = ($header); @ans; @ansTable = ($header); $n = @vars - 1; $numrows = 2**@vars - 1; for my $i (0 .. $numrows) { my @coords = map { $i & 2**$_ ? 1 : 0 } (0 .. $n); my %point = map { $vars[$_] => $coords[ $n - $_ ] } (0 .. $n); my @row = map { $point{ $vars[$_] } ? $T : $F } (0 .. $n); my @rowans = map { exists($dontcareHash[$_]->{$i}) ? 'X' : ( exists($indetermHash[$_]->{$i}) ? 'i' : $f[$_]->eval(%point)->value) } (0 .. $#f); push @ans, ~~@rowans; push @table, ~~@row; my @ansRow = (@row, @rowans); push @ansTable, ~~@ansRow; } # build one MA per expression to evaluate (in @f) @columnMA; @fLabelTable; for my $j (0 .. $#f) { $columnMA[$j] = MultiAnswer(map { $ans[$_][$j] } (0 .. $numrows))->with( singleResult => 1, allowBlankAnswers => 0, checker => $columnChecker ); # while we're at it, build a table for the expressions my @row = map { wrapTeX($_) } ($fLabel[$j], '=', $f[$j]->TeX); push @fLabelTable, ~~@row; } # push the MA answer blanks onto the end of each row for my $i (0 .. $numrows) { push @{ $table[ $i + 1 ] }, map {"[__]{~~$columnMA[$_]}"} (0 .. $#f); } $PGMLLabelTable = PGMLTable(~~@fLabelTable); $PGMLTable = PGMLTable(~~@table); $PGMLAnsTable = PGMLTable(~~@ansTable); @kmapAns; for my $i (0 .. $#f) { my @column; push @column, map {$ans[$i][$j]} (0 .. $numRows-1); @oneKmap = KmapMaker($fLabel[$i], @column); foreach my $kRow (@oneKmap) { push @kmapAns, $kRow; } } $PGMLAnsKMaps = PGMLTable(~~@kmapAns); BEGIN_PGML Let [$PGMLLabelTable]** Complete the truth table. [$PGMLTable]** Valid table values include [`0`], [`1`], [`X`] (don't care), and [`i`] (indeterminant). Then use the information above to complete the Kmap for each output column. [$PGMLAnsKMaps]** END_PGML BEGIN_PGML_SOLUTION Truth table [$PGMLAnsTable]** Kmaps [$PGMLAnsKMaps]** END_PGML_SOLUTION ENDDOCUMENT();