\def\packagedate{13 February 2025}
\def\packageversion{2.2}
\def\packageshortdate{2025/02/13}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Defines various relational algebra symbols.
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Version History
% v1.0    Initial version.
% v2.0    Complete rewrite to support Unicode OpenType fonts and LaTeX3.
% v2.1    Plays nicely with other packages that \setmathfont, and only
%         interpolates join symbols if they aren’t in the current font.
% v2.2    Added text versions of logical operators (AND, OR, NOT), and
%         "parameters" of the form "<x>". Fixed missing \fi.
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\NeedsTeXFormat{LaTeX2e}[2001/06/01]
\ProvidesPackage{relalg}%
        [\packageshortdate\space v\packageversion\space Relational algebra notation]


\RequirePackage{amsmath}
\RequirePackage{expl3}
\RequirePackage{iftex}


\ExplSyntaxOn
    \bool_new:N \g__ra_unicode_engine

    \ifLuaTeX
        \PackageInfo{relalg}{LuaTeX\space engine\space detected}
        \bool_set_true:N \g__ra_unicode_engine
    \fi

    \ifXeTeX
        \PackageInfo{relalg}{XeTeX\space engine\space detected}
        \bool_set_true:N \g__ra_unicode_engine
    \fi

    \bool_if:NTF \g__ra_unicode_engine {
        \RequirePackage[no-math]{fontspec}
        \RequirePackage{unicode-math}

        % Stolen from unicode-math.sty as the fallback in unicode-math doesn't
        % seem to work? Weirdly it seems to work in a bare-bones MWE.
        \bool_if:NF \g__um_main_font_defined_bool \__um_load_lm:

        % The various join symbols aren't covered very well by many maths fonts
        % (including Latin Modern Math, annoyingly), so if necessary let's
        % Frankenstein them in from fonts that do have them. We need to hack
        % the scale to get roughly proportional looking sizes.
        % \iffontchar trick from <https://tex.stackexchange.com/questions/642080/newunicodechar-only-if-undeclared>
        % Outer joins (left, right, full):
        \iffontchar\font "27D5\relax\else\setmathfont{STIXTwoMath-Regular.otf}[range="027D5, Scale=0.875]\fi
        \iffontchar\font "27D6\relax\else\setmathfont{STIXTwoMath-Regular.otf}[range="27D6, Scale=0.875]\fi
        \iffontchar\font "27D7\relax\else\setmathfont{STIXTwoMath-Regular.otf}[range="27D7, Scale=0.875]\fi
        % \setmathfont{STIXTwoMath-Regular.otf}[range={"027D5-"027D7}, Scale=0.875]
        % Inner join: (1.143 = 1 / 0.875)
        \iffontchar\font "2A1D\relax\else\setmathfont{KpMath-Light.otf}[range="2A1D, Scale=1.143]\fi
    }{
        \PackageInfo{relalg}{non-Unicode\space engine\space detected}
        \RequirePackage{latexsym} % for \Join
    }
\ExplSyntaxOff


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% IDENTIFIERS
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% General identifiers.
\NewDocumentCommand{\RelIdentifier}{m}{\ensuremath{\mathit{#1}}}

% Attribute names.
\let\RelAttribute\RelIdentifier
\let\RelAttr\RelIdentifier

% Relation variable names.
\let\RelVariable\RelIdentifier
\let\RelVar\RelIdentifier

% Attribute sets: \RelAttributeSet{foo,bar,baz}, \RelAttrSet{foo,bar,baz}
% <https://tex.stackexchange.com/a/159132>, expl3 manual Section 23
\ExplSyntaxOn
    \NewDocumentCommand{\RelAttributeSet}{m}{
        \clist_clear:N \l_tmpa_clist
        \clist_map_inline:nn{#1}{
            \clist_put_right:Nn \l_tmpa_clist {\RelAttribute{##1}}
        }
        \ensuremath{\{\clist_use:Nn \l_tmpa_clist {,}\}}
    }
    \let\RelAttrSet\RelAttributeSet
\ExplSyntaxOff

% "Parameters" of the form "<x>".
\NewDocumentCommand{\RelParameter}{m}{\ensuremath{\langle\RelIdentifier{#1}\rangle}}


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% BASIC OPERATORS
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Restrict (select) operator.
\NewDocumentCommand{\RelRestrict}{}{\ensuremath{\sigma}}
\let\RelSelect\RelRestrict

% Project operator.
\NewDocumentCommand{\RelProject}{}{\ensuremath{\pi}}

% Set union operator.
\NewDocumentCommand{\RelUnion}{}{\ensuremath{\cup}}

% Set difference operator.
\NewDocumentCommand{\RelDifference}{}{\ensuremath{-}}
\let\RelMinus\RelDifference

% Cartesian product operator.
\NewDocumentCommand{\RelCartesianProduct}{}{\ensuremath{\times}}
\let\RelProduct\RelCartesianProduct
\let\RelTimes\RelCartesianProduct

% Rename operator.
\NewDocumentCommand{\RelRename}{}{\ensuremath{\rho}}

% Extend operator.
\NewDocumentCommand{\RelExtend}{}{\ensuremath{\epsilon}}


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% ADDITIONAL OPERATORS
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Set intersection operator.
\NewDocumentCommand{\RelIntersection}{}{\ensuremath{\cap}}
\let\RelIntersect\RelIntersection

% Join operator (including natural join).
% For some reason XeTeX puts less space in front of the operator than LuaTeX.
% Adding +2.5mu for XeTeX looks close enough to LuaTeX's output. This also
% applies to the outer join operators below.
\NewDocumentCommand{\RelJoin}{}{\ensuremath{\ifXeTeX\mkern2.5mu\fi\mathbin{\Join}}}
\let\RelNaturalJoin\RelJoin
\let\RelNatJoin\RelJoin
\let\RelNJoin\RelJoin

% Division operator (two alternatives).
\NewDocumentCommand{\RelDivide}{}{\ensuremath{/}}
\NewDocumentCommand{\RelAltDivide}{}{\ensuremath{\div}}

% Assignment operator.
\NewDocumentCommand{\RelAssign}{}{\ensuremath{\leftarrow}}


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% EXTENDED OPERATORS
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Aggregate functions.
\DeclareMathOperator{\AggAvg}{avg}
\DeclareMathOperator{\AggCount}{count}
\DeclareMathOperator{\AggMin}{min}
\DeclareMathOperator{\AggMax}{max}
\DeclareMathOperator{\AggSum}{sum}

\NewDocumentCommand{\RelAggregrate}{m m}{\ensuremath{\mathcal{G}_{#1}(#2)}}

\NewDocumentCommand{\RelAverage}{m}{\RelAggregrate{\AggAverage}{#1}}
\let\RelAvg\RelAverage

\NewDocumentCommand{\RelCount}{m}{\RelAggregrate{\AggCount}{#1}}

\NewDocumentCommand{\RelMaximum}{m}{\RelAggregrate{\AggMax}{#1}}
\let\RelMax\RelMaximum

\NewDocumentCommand{\RelMinimum}{m}{\RelAggregrate{\AggMin}{#1}}
\let\RelMin\RelMinimum

\NewDocumentCommand{\RelSum}{m}{\RelAggregrate{\AggSum}{#1}}

% Outer join operators.
\ExplSyntaxOn
    \bool_if:NTF \g__ra_unicode_engine {
        \NewDocumentCommand{\RelLeftOuterJoin}{}{\ensuremath{\ifXeTeX\mkern2.5mu\fi\mathbin{\leftouterjoin}}}
        \NewDocumentCommand{\RelRightOuterJoin}{}{\ensuremath{\ifXeTeX\mkern2.5mu\fi\mathbin{\rightouterjoin}}}
        \NewDocumentCommand{\RelFullOuterJoin}{}{\ensuremath{\ifXeTeX\mkern2.5mu\fi\mathbin{\fullouterjoin}}}
    }{
        % Hand-tweaked for non-Unicode engines based on answers at
        % <https://tex.stackexchange.com/questions/20740/symbols-for-outer-joins>
        % Weirdly the height of \Join is fractionally shorter than \bowtie, so
        % we can't use the more sensible solution of measuring off the line
        % height. Use only font-relative units to ensure proper scaling.
        \def\__ra_outer_join{\rule[0.11ex]{0.25em}{0.1ex}\llap{\rule[1.41ex]{0.25em}{0.1ex}}}
        \NewDocumentCommand{\RelLeftOuterJoin}{}{\ensuremath{\mathbin{\__ra_outer_join\mkern-6.4mu\Join}}}
        \NewDocumentCommand{\RelRightOuterJoin}{}{\ensuremath{\mathbin{\Join\mkern-6.4mu\__ra_outer_join}}}
        \NewDocumentCommand{\RelFullOuterJoin}{}{\ensuremath{\mathbin{\__ra_outer_join\mkern-6.4mu\Join\mkern-6.4mu\__ra_outer_join}}}
    }
\ExplSyntaxOff

\let\RelLOuterJoin\RelLeftOuterJoin
\let\RelLeftOuter\RelLeftOuterJoin
\let\RelLOJ\RelLeftOuterJoin

\let\RelROuterJoin\RelRightOuterJoin
\let\RelRightOuter\RelRightOuterJoin
\let\RelROJ\RelRightOuterJoin

\let\RelFOuterJoin\RelFullOuterJoin
\let\RelFullOuter\RelFullOuterJoin
\let\RelFOJ\RelFullOuterJoin

% Semijoin operator.
\NewDocumentCommand{\RelLeftSemiJoin}{}{\ensuremath{\ltimes}}
\let\RelSemiJoin\RelLeftSemiJoin

\NewDocumentCommand{\RelRightSemiJoin}{}{\ensuremath{\rtimes}}
\let\RelReverseSemiJoin\RelRightSemiJoin

% Antijoin operator.
\NewDocumentCommand{\RelLeftAntiJoin}{}{\ensuremath{\triangleright}}
\let\RelAntiJoin\RelLeftAntiJoin

\NewDocumentCommand{\RelRightAntiJoin}{}{\ensuremath{\triangleleft}}
\let\RelReverseAntiJoin\RelRightAntiJoin


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% MISCELLANEOUS
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Logical operators.
\DeclareMathOperator{\AndText}{AND}
\DeclareMathOperator{\OrText}{OR}
\DeclareMathOperator{\NotText}{NOT}

\ExplSyntaxOn
    \NewDocumentCommand{\RelLogicalAnd}{O{symbol}}{
        \tl_if_eq:nnTF {#1} {text}
            {\ensuremath{\AndText}}
            {\ensuremath{\wedge}}
    }
    \let\RelLogAnd\RelLogicalAnd
    \let\RelAnd\RelLogicalAnd

    \NewDocumentCommand{\RelLogicalOr}{O{symbol}}{
        \tl_if_eq:nnTF {#1} {text}
            {\ensuremath{\OrText}}
            {\ensuremath{\vee}}
    }
    \let\RelLogOr\RelLogicalOr
    \let\RelOr\RelLogicalOr

    \NewDocumentCommand{\RelLogicalNot}{O{symbol}}{
        \tl_if_eq:nnTF {#1} {text}
            {\ensuremath{\NotText}}
            {\ensuremath{\neg}}
    }
    \let\RelLogNot\RelLogicalNot
    \let\RelNot\RelLogicalNot
\ExplSyntaxOff

\endinput
