5

I have been trying to find a way to do \bitwiseXor{1,2} or \bitwiseXor{1}{2} and have it return 3.

  • I tried to use pgfmath without much success (This is being used within the context of a TikZ auto-generated diagram)
  • I also tried the bitset package without a lot of success.
  • I tried using pythontex but it requires a more convoluted compilation. Since I am sharing this file, I would prefer if compilation is a one step process.
  • And finally, I see that @egreg answered something similar here, so it can be done, but I can't understand latex3 and how can I take everything out to just have the operations that returns just the decimal (or binary) number.

Thanks

1
  • 2
    It is very hard to answer this because you just say you tried to do something without success, but do not really show what you have tried. You can easily convert bitset macros to pgf functions, see e.g. here. You can also declare your own pgf functions, pgf has built in functions for converting from and to binaries. I am sure one can answer your question within pgf or pgf + bitset but this needs a bit more details from your side. I do not want to have to look up the functions from the bitset manual.
    – user194703
    Commented Apr 16, 2020 at 19:28

4 Answers 4

7

Here's a fully expandable implementation of the bitwise XOR.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewExpandableDocumentCommand{\bitwiseXor}{mm}
 {
  \recuenco_bitwise_xor:nn { #1 } { #2 }
 }

\cs_new:Nn \recuenco_bitwise_xor:nn
 {
  \int_from_bin:e
   {
    \__recuenco_bitwise_xor:ee { \int_to_bin:n { #1 } } { \int_to_bin:n { #2 } }
   }
 }
\cs_generate_variant:Nn \int_from_bin:n { e }

\cs_new:Nn \__recuenco_bitwise_xor:nn
 {
  \__recuenco_bitwise_xor_binary:ee
   {
    \prg_replicate:nn
     {
      \int_max:nn { \tl_count:n { #1 } } { \tl_count:n { #2 } } - \tl_count:n { #1 }
     }
     { 0 }
     #1
   }
   {
    \prg_replicate:nn
     {
      \int_max:nn { \tl_count:n { #1 } } { \tl_count:n { #2 } } - \tl_count:n { #2 }
     }
     { 0 }
     #2
   }
 }
\cs_generate_variant:Nn \__recuenco_bitwise_xor:nn { ee }

\cs_new:Nn \__recuenco_bitwise_xor_binary:nn
 {
  \__recuenco_bitwise_xor_binary:w #1;#2;
 }
\cs_generate_variant:Nn \__recuenco_bitwise_xor_binary:nn { ee }

\cs_new:Npn \__recuenco_bitwise_xor_binary:w #1#2;#3#4;
 {
  \int_abs:n { #1-#3 }
  \tl_if_empty:nF { #2 } { \__recuenco_bitwise_xor_binary:w #2;#4; }
 }

\ExplSyntaxOff

\begin{document}

\bitwiseXor{93}{208}

\end{document}

First the input is converted to binary. Then the two numbers are equalized in length by padding with the appropriate number of zeros the shorter one.

Then a recursive macro is called that outputs the XOR of each bit, by computing the absolute value of the difference.

The result is converted to decimal form.

You can check that the output is 141.


An extension to also cover AND and OR.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewExpandableDocumentCommand{\bitwiseAnd}{mm}
 {
  \recuenco_bitwise:nnN { #1 } { #2 } \__recuenco_bitwise_and_binary:w
 }
\NewExpandableDocumentCommand{\bitwiseOr}{mm}
 {
  \recuenco_bitwise:nnN { #1 } { #2 } \__recuenco_bitwise_or_binary:w
 }
\NewExpandableDocumentCommand{\bitwiseXor}{mm}
 {
  \recuenco_bitwise:nnN { #1 } { #2 } \__recuenco_bitwise_xor_binary:w
 }

\cs_new:Nn \recuenco_bitwise:nnN
 {
  \int_from_bin:e
   {
    \__recuenco_bitwise:eeN { \int_to_bin:n { #1 } } { \int_to_bin:n { #2 } } #3
   }
 }
\cs_generate_variant:Nn \int_from_bin:n { e }

\cs_new:Nn \__recuenco_bitwise:nnN
 {
  \__recuenco_bitwise_binary:eeN
   {
    \prg_replicate:nn
     {
      \int_max:nn { \tl_count:n { #1 } } { \tl_count:n { #2 } } - \tl_count:n { #1 }
     }
     { 0 }
     #1
   }
   {
    \prg_replicate:nn
     {
      \int_max:nn { \tl_count:n { #1 } } { \tl_count:n { #2 } } - \tl_count:n { #2 }
     }
     { 0 }
     #2
   }
   #3
 }
\cs_generate_variant:Nn \__recuenco_bitwise:nnN { ee }

\cs_new:Nn \__recuenco_bitwise_binary:nnN
 {
  #3 #1;#2;
 }
\cs_generate_variant:Nn \__recuenco_bitwise_binary:nnN { ee }

\cs_new:Npn \__recuenco_bitwise_and_binary:w #1#2;#3#4;
 {
  \int_eval:n { #1*#3 }
  \tl_if_empty:nF { #2 } { \__recuenco_bitwise_and_binary:w #2;#4; }
 }
\cs_new:Npn \__recuenco_bitwise_or_binary:w #1#2;#3#4;
 {
  \int_max:nn { #1 } { #3 }
  \tl_if_empty:nF { #2 } { \__recuenco_bitwise_or_binary:w #2;#4; }
 }
\cs_new:Npn \__recuenco_bitwise_xor_binary:w #1#2;#3#4;
 {
  \int_abs:n { #1-#3 }
  \tl_if_empty:nF { #2 } { \__recuenco_bitwise_xor_binary:w #2;#4; }
 }

\cs_new:Npn \bin #1 { \exp_args:Ne \int_to_bin:n { #1 } }

\ExplSyntaxOff

\begin{document}

$93\mathbin{\mathrm{AND}}208=\bitwiseAnd{93}{208}$\quad
\begin{tabular}[t]{r}
\bin{93} \\
\bin{208} \\
\hline
\bin{\bitwiseAnd{93}{208}}
\end{tabular}

\bigskip

$93\mathbin{\mathrm{OR}}208=\bitwiseOr{93}{208}$\quad
\begin{tabular}[t]{r}
\bin{93} \\
\bin{208} \\
\hline
\bin{\bitwiseOr{93}{208}}
\end{tabular}

\bigskip

$93\mathbin{\mathrm{XOR}}208=\bitwiseXor{93}{208}$
\begin{tabular}[t]{r}
\bin{93} \\
\bin{208} \\
\hline
\bin{\bitwiseXor{93}{208}}
\end{tabular}

\end{document}

enter image description here

2
  • Thanks! This is what I was searching for. latex3 seems a bit overwhelming many times. Commented Apr 17, 2020 at 16:39
  • 1
    @AlexRecuenco I also added AND and OR.
    – egreg
    Commented Apr 17, 2020 at 16:58
6

The following produces a 3 result, using the bitset package. The Dec in the macro names implies we are working in decimal notation. Other notations are available in the package (https://ctan.org/pkg/bitset).

The Set and Get are for setting and retrieving data. With the logical operators, such as \bitsetXor, the result is placed into the first argument.

The MWE performs 1 XOR 2 to get 3, which gets placed in the A register. Then 3 AND 2 is performed to get a result of 2.

\documentclass{article}
\usepackage{bitset}
\begin{document}
\bitsetSetDec{A}{1}
\bitsetSetDec{B}{2}
\bitsetXor{A}{B} 
\bitsetGetDec{A}

\bitsetAnd{A}{B}
\bitsetGetDec{A}
\end{document}

enter image description here

5

If you're willing to use LuaLaTeX, here's some good news: Lua5.3 (which is part of LuaTeX) features several bitwise operations. Excerpting from section 3.4.2 of Lua5.3's reference manual:

enter image description here

The only mildly tricky thing is to find a way to "smuggle" the TeX-special characters ~ to Lua. The easiest way I know of to do this is to load the luacode package and employ its \luaexec macro.

Of course, one can also create LaTeX macros that act as "wrappers" for the Lua bitwise operations. See the macro \bitwiseXOR below, which takes two arguments.

enter image description here

\documentclass{article}
\usepackage{luacode} % for '\luaexec' macro
%% Define a LaTeX "wrapper" macro:
\newcommand\bitwiseXOR[2]{\luaexec{tex.sprint((#1)~(#2))}}
\newcommand\bitwiseAND[2]{\luaexec{tex.sprint((#1)&(#2))}}
\newcommand\bitwiseOR[2]{\luaexec{tex.sprint((#1)|(#2))}}

\begin{document}
The output of \verb+\luaexec{tex.sprint(1~2)}+ is \luaexec{tex.sprint(1~2)}.

The output of \verb|\bitwiseXOR{2-1}{1+1}| is also \bitwiseXOR{2-1}{1+1}.
\end{document}
5
  • Thanks, I will see if anybody can show how to do this natively, wiyj either latex2 or latex3. Commented Apr 16, 2020 at 18:38
  • Also, could you add some examples on how to use latex macros inside the luaexec command? Commented Apr 16, 2020 at 18:56
  • @AlexRecuenco - Please see the updated answer, which sets up a LaTeX "wrapper" macro that takes two arguments.
    – Mico
    Commented Apr 16, 2020 at 19:37
  • 1
    @AlexRecuenco -- A general comment: The argument of \luaexec gets fully expanded by LaTeX before it is passed to the Lua side. Hence, if \x and \y are LaTeX macros that evaluate to integers, it's entirely ok to write \bitwiseXOR{\x}{\y} or -- if you prefer not to use the wrapper macro -- \luaexec{tex.sprint(\x~\y)}.
    – Mico
    Commented Apr 17, 2020 at 9:19
  • 1
    Thanks! That's helpful, I must have had some bug when I was quickly testing it then. Commented Apr 17, 2020 at 16:37
2

Here is a pgf only solution. It only requires pgf and the parser module thereof. Everything is done by pgf functions that can be used and parsed as usual in pgf.

\documentclass{article}
\usepackage{pgf}
\usepgfmodule{parser}
\makeatletter
\pgfparserdef{prp}{initial}{the character 0}% 
{\global\advance\pgfutil@tempcnta by1\relax
\edef\pgf@bit@list{0,\pgf@bit@list}}%
\pgfparserdef{prp}{initial}{the character 1}% 
{\global\advance\pgfutil@tempcnta by1\relax
\edef\pgf@bit@list{1,\pgf@bit@list}}%
\pgfparserdef{prp}{initial}{the character ;}% 
{\pgfparserswitch{final}}%
\pgfmathdeclarefunction{bitand}{2}{\begingroup
\pgfutil@tempcnta0\relax
\edef\pgf@bit@list{}%
\pgfparserparse{prp}#1;%
\pgfutil@tempcntb\pgfutil@tempcnta
\edef\pgfutil@tmpa{\pgf@bit@list}%
\pgfutil@tempcnta0\relax
\edef\pgf@bit@list{}%
\pgfparserparse{prp}#2;%
\edef\pgfutil@tmpb{\pgf@bit@list}%
\ifnum\pgfutil@tempcnta<\pgfutil@tempcntb\relax
\pgfutil@tempcntb\pgfutil@tempcnta
\fi
\pgfutil@tempcnta0\relax
\edef\pgfutil@tmpe{}%
\loop
\pgfmathsetmacro{\pgfutil@tmpc}{{\pgfutil@tmpa}[\pgfutil@tempcnta]}%
\pgfmathsetmacro{\pgfutil@tmpd}{{\pgfutil@tmpb}[\pgfutil@tempcnta]}%
\pgfmathparse{int(and(\pgfutil@tmpc,\pgfutil@tmpd))}%
\edef\pgfutil@tmpe{\pgfmathresult\pgfutil@tmpe}%
\advance\pgfutil@tempcnta1\relax
\ifnum\pgfutil@tempcnta<\pgfutil@tempcntb
\repeat
\edef\pgfmathresult{\pgfutil@tmpe}%
\pgfmathsmuggle\pgfmathresult
\endgroup}
\pgfmathdeclarefunction{bitor}{2}{\begingroup
\pgfutil@tempcnta0\relax
\edef\pgf@bit@list{}%
\pgfparserparse{prp}#1;%
\pgfutil@tempcntb\pgfutil@tempcnta
\edef\pgfutil@tmpf{\the\numexpr\pgfutil@tempcnta-1}%
\edef\pgfutil@tmpa{\pgf@bit@list}%
\pgfutil@tempcnta0\relax
\edef\pgf@bit@list{}%
\pgfparserparse{prp}#2;%
\edef\pgfutil@tmpb{\pgf@bit@list}%
\edef\pgfutil@tmpg{\the\numexpr\pgfutil@tempcnta-1}%
\ifnum\pgfutil@tempcnta>\pgfutil@tempcntb\relax
\pgfutil@tempcntb\pgfutil@tempcnta
\fi
\pgfutil@tempcnta0\relax
\edef\pgfutil@tmpe{}%
\loop
\ifnum\pgfutil@tempcnta>\pgfutil@tmpf
\pgfmathsetmacro{\pgfutil@tmpc}{0}%
\else
\pgfmathsetmacro{\pgfutil@tmpc}{{\pgfutil@tmpa}[\pgfutil@tempcnta]}%
\fi
\ifnum\pgfutil@tempcnta>\pgfutil@tmpg
\pgfmathsetmacro{\pgfutil@tmpd}{0}%
\else
\pgfmathsetmacro{\pgfutil@tmpd}{{\pgfutil@tmpb}[\pgfutil@tempcnta]}%
\fi
\pgfmathparse{int(or(\pgfutil@tmpc,\pgfutil@tmpd))}%
\edef\pgfutil@tmpe{\pgfmathresult\pgfutil@tmpe}%
\advance\pgfutil@tempcnta1\relax
\ifnum\pgfutil@tempcnta<\pgfutil@tempcntb
\repeat
\edef\pgfmathresult{\pgfutil@tmpe}%
\pgfmathsmuggle\pgfmathresult
\endgroup}
\pgfmathdeclarefunction{bitxor}{2}{\begingroup
\pgfutil@tempcnta0\relax
\edef\pgf@bit@list{}%
\pgfparserparse{prp}#1;%
\pgfutil@tempcntb\pgfutil@tempcnta
\edef\pgfutil@tmpf{\the\numexpr\pgfutil@tempcnta-1}%
\edef\pgfutil@tmpa{\pgf@bit@list}%
\pgfutil@tempcnta0\relax
\edef\pgf@bit@list{}%
\pgfparserparse{prp}#2;%
\edef\pgfutil@tmpb{\pgf@bit@list}%
\edef\pgfutil@tmpg{\the\numexpr\pgfutil@tempcnta-1}%
\ifnum\pgfutil@tempcnta>\pgfutil@tempcntb\relax
\pgfutil@tempcntb\pgfutil@tempcnta
\fi
\pgfutil@tempcnta0\relax
\edef\pgfutil@tmpe{}%
\loop
\ifnum\pgfutil@tempcnta>\pgfutil@tmpf
\pgfmathsetmacro{\pgfutil@tmpc}{0}%
\else
\pgfmathsetmacro{\pgfutil@tmpc}{{\pgfutil@tmpa}[\pgfutil@tempcnta]}%
\fi
\ifnum\pgfutil@tempcnta>\pgfutil@tmpg
\pgfmathsetmacro{\pgfutil@tmpd}{0}%
\else
\pgfmathsetmacro{\pgfutil@tmpd}{{\pgfutil@tmpb}[\pgfutil@tempcnta]}%
\fi
\pgfmathparse{int(mod(\pgfutil@tmpc+\pgfutil@tmpd,2))}%
\edef\pgfutil@tmpe{\pgfmathresult\pgfutil@tmpe}%
\advance\pgfutil@tempcnta1\relax
\ifnum\pgfutil@tempcnta<\pgfutil@tempcntb
\repeat
\edef\pgfmathresult{\pgfutil@tmpe}%
\pgfmathsmuggle\pgfmathresult
\endgroup}
\pgfmathdeclarefunction{bitwiseAnd}{2}{\begingroup
\pgfmathsetmacro{\pgfutil@tmpa}{bin(#1)}%
\pgfmathsetmacro{\pgfutil@tmpb}{bin(#2)}%
\pgfmathsetmacro{\pgfutil@tmpc}{bitand("\pgfutil@tmpa","\pgfutil@tmpb")}%
\pgfmathparse{0b\pgfutil@tmpc}%
\pgfmathsmuggle\pgfmathresult
\endgroup}
\pgfmathdeclarefunction{bitwiseOr}{2}{\begingroup
\pgfmathsetmacro{\pgfutil@tmpa}{bin(#1)}%
\pgfmathsetmacro{\pgfutil@tmpb}{bin(#2)}%
\pgfmathsetmacro{\pgfutil@tmpc}{bitor("\pgfutil@tmpa","\pgfutil@tmpb")}%
\pgfmathparse{0b\pgfutil@tmpc}%
\pgfmathsmuggle\pgfmathresult
\endgroup}
\pgfmathdeclarefunction{bitwiseXor}{2}{\begingroup
\pgfmathsetmacro{\pgfutil@tmpa}{bin(#1)}%
\pgfmathsetmacro{\pgfutil@tmpb}{bin(#2)}%
\pgfmathsetmacro{\pgfutil@tmpc}{bitxor("\pgfutil@tmpa","\pgfutil@tmpb")}%
\pgfmathparse{0b\pgfutil@tmpc}%
\pgfmathsmuggle\pgfmathresult
\endgroup}
\makeatother
\begin{document}
$1~\mbox{XOR}~2=\pgfmathparse{bitwiseXor(1,2)}\pgfmathresult$

$3~\mbox{AND}~5=\pgfmathparse{bitwiseAnd(3,5)}\pgfmathresult$

\medskip

\edef\bitA{0100110101}%
\edef\bitB{1010110010001}%
\pgfmathsetmacro{\bitAandB}{bitand("\bitA","\bitB")}%
\pgfmathsetmacro{\bitAorB}{bitor("\bitA","\bitB")}%
\pgfmathsetmacro{\bitAxorB}{bitxor("\bitA","\bitB")}%


\begin{tabular}{c}
\begin{tabular}{lr}
first bit sequence & \bitA \\
second bit sequence & \bitB \\
\hline
first AND second & \bitAandB \\
\end{tabular} \\[2em] 
\begin{tabular}{lr}
first bit sequence & \bitA \\
second bit sequence & \bitB \\
\hline
first OR second & \bitAorB \\
\end{tabular} \\[2em]
\begin{tabular}{lr}
first bit sequence & \bitA \\
second bit sequence & \bitB \\
\hline
first XOR second & \bitAxorB \\
\end{tabular} 
\end{tabular}
\end{document}

enter image description here

2
  • This is awesome. But it seems somewhat inscrutable. Do you think that is just my lack of experience with the pgf internals? Commented Apr 17, 2020 at 16:43
  • 1
    @AlexRecuenco The reason why this looks so complicated is that I tried to adapt the conventions of other pgf functions for the variables. It also does the things locally, i.e. does not overwrite macros you could have defined by accident. (To me personally this is still way easier to read than expl3, where there is no real manual in which one can look up things.)
    – user194703
    Commented Apr 17, 2020 at 16:48

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .