logicalOperator
Configurable logical AND/OR/NAND/NOR/XOR/XNOR/NOT of the inputs.
📝Syntax
Block type: logicalOperator
📥Input Arguments
Parameter Description
input ports 2 input port(s) declared.
📤Output Arguments
Parameter Description
output ports 1 output port(s) declared.
📄Description

Configurable logical AND/OR/NAND/NOR/XOR/XNOR/NOT of the inputs.

Module nflow_blocks
Library Logic / Bit Operations
Type logicalOperator
Label Logical Operator

Description

A single dispatchable logical block: Operator selects AND, OR, NAND, NOR, XOR, XNOR or NOT. It reduces the N inputs element-wise (a nonzero input is true); NOT takes a single input and negates it. Complements the fixed and / or / xor / not blocks with one configurable block.

Ports

Input(s)

Port Role Side Position
Port_1 Numeric signal read by the block. left x=0, y=26
Port_2 Numeric signal read by the block. left x=0, y=54

Output(s)

Port Role Side Position
Port_1 Numeric signal produced by the block. right x=80, y=40

Parameters

Parameter Default value
Operator AND

Block Characteristics

Block type logicalOperator
Family Logic / Bit Operations
Rendered size 80 x 80
Phases ALGEBRAIC
Internal state or history no
Signal data type double numeric values

Algorithms

Extended Capabilities

Code generation: supported for C and Rust.

Implementation Sources

Manifestmodules/nflow_blocks/libraries/logic/library.json
{
  "id": "builtin.logic",
  "title": "Logic / Bit Operations",
  "version": "0.1.0",
  "format": "nflow-2",
  "builtin": true,
  "blocks": [
    {
      "type": "and",
      "label": "AND",
      "icon": "and.svg",
      "phases": [
        "ALGEBRAIC"
      ],
      "width": 80,
      "height": 80,
      "inputs": [
        {
          "x": 0,
          "y": 20,
          "side": "left"
        },
        {
          "x": 0,
          "y": 60,
          "side": "left"
        }
      ],
      "outputs": [
        {
          "x": 80,
          "y": 40,
          "side": "right"
        }
      ],
      "render": {
        "type": "math",
        "bodyClass": "block-body",
        "mathGroupClass": "and-math",
        "formula": "\\text{AND}",
        "textSize": "16px"
      }
    },
    {
      "type": "or",
      "label": "OR",
      "icon": "or.svg",
      "phases": [
        "ALGEBRAIC"
      ],
      "width": 80,
      "height": 80,
      "inputs": [
        {
          "x": 0,
          "y": 20,
          "side": "left"
        },
        {
          "x": 0,
          "y": 60,
          "side": "left"
        }
      ],
      "outputs": [
        {
          "x": 80,
          "y": 40,
          "side": "right"
        }
      ],
      "render": {
        "type": "math",
        "bodyClass": "block-body",
        "mathGroupClass": "or-math",
        "formula": "\\text{OR}",
        "textSize": "16px"
      }
    },
    {
      "type": "xor",
      "label": "XOR",
      "icon": "xor.svg",
      "phases": [
        "ALGEBRAIC"
      ],
      "width": 80,
      "height": 80,
      "inputs": [
        {
          "x": 0,
          "y": 20,
          "side": "left"
        },
        {
          "x": 0,
          "y": 60,
          "side": "left"
        }
      ],
      "outputs": [
        {
          "x": 80,
          "y": 40,
          "side": "right"
        }
      ],
      "render": {
        "type": "math",
        "bodyClass": "block-body",
        "mathGroupClass": "xor-math",
        "formula": "\\text{XOR}",
        "textSize": "16px"
      }
    },
    {
      "type": "not",
      "label": "NOT",
      "icon": "not.svg",
      "phases": [
        "ALGEBRAIC"
      ],
      "width": 80,
      "height": 80,
      "inputs": [
        {
          "x": 0,
          "y": 40,
          "side": "left"
        }
      ],
      "outputs": [
        {
          "x": 80,
          "y": 40,
          "side": "right"
        }
      ],
      "render": {
        "type": "math",
        "bodyClass": "block-body",
        "mathGroupClass": "not-math",
        "formula": "\\text{NOT}",
        "textSize": "16px"
      }
    },
    {
      "type": "compareToConstant",
      "label": "Compare Const",
      "icon": "compareToConstant.svg",
      "phases": [
        "ALGEBRAIC"
      ],
      "width": 100,
      "height": 80,
      "inputs": [
        {
          "x": 0,
          "y": 40,
          "side": "left"
        }
      ],
      "outputs": [
        {
          "x": 100,
          "y": 40,
          "side": "right"
        }
      ],
      "defaultParams": {
        "relop": "ge",
        "const": 0
      },
      "render": {
        "type": "image",
        "src": "exports/compareToConstant.svg",
        "svgMode": "element",
        "preserveAspectRatio": "none",
        "x": 0,
        "y": 0,
        "width": 100,
        "height": 80
      }
    },
    {
      "type": "intervalTest",
      "label": "Interval Test",
      "icon": "intervalTest.svg",
      "phases": [
        "ALGEBRAIC"
      ],
      "width": 100,
      "height": 80,
      "inputs": [
        {
          "x": 0,
          "y": 40,
          "side": "left"
        }
      ],
      "outputs": [
        {
          "x": 100,
          "y": 40,
          "side": "right"
        }
      ],
      "defaultParams": {
        "LowerLimit": 0,
        "UpperLimit": 1,
        "IntervalClosedLeft": 1,
        "IntervalClosedRight": 1
      },
      "render": {
        "type": "image",
        "src": "exports/intervalTest.svg",
        "svgMode": "element",
        "preserveAspectRatio": "none",
        "x": 0,
        "y": 0,
        "width": 100,
        "height": 80
      }
    },
    {
      "type": "intervalTestDynamic",
      "label": "Interval Test Dynamic",
      "icon": "intervalTestDynamic.svg",
      "phases": [
        "ALGEBRAIC"
      ],
      "width": 100,
      "height": 80,
      "inputs": [
        {
          "x": 0,
          "y": 20,
          "side": "left"
        },
        {
          "x": 0,
          "y": 40,
          "side": "left"
        },
        {
          "x": 0,
          "y": 60,
          "side": "left"
        }
      ],
      "outputs": [
        {
          "x": 100,
          "y": 40,
          "side": "right"
        }
      ],
      "defaultParams": {
        "IntervalClosedLeft": 1,
        "IntervalClosedRight": 1
      },
      "render": {
        "type": "image",
        "src": "exports/intervalTestDynamic.svg",
        "svgMode": "element",
        "preserveAspectRatio": "none",
        "x": 0,
        "y": 0,
        "width": 100,
        "height": 80
      }
    },
    {
      "type": "bitwiseOperator",
      "label": "Bitwise Operator",
      "icon": "bitwiseOperator.svg",
      "phases": [
        "ALGEBRAIC"
      ],
      "width": 90,
      "height": 80,
      "inputs": [
        {
          "x": 0,
          "y": 40,
          "side": "left"
        }
      ],
      "outputs": [
        {
          "x": 90,
          "y": 40,
          "side": "right"
        }
      ],
      "defaultParams": {
        "Operation": "AND",
        "BitMask": 0,
        "NumBits": 32
      },
      "render": {
        "type": "image",
        "src": "exports/bitwiseOperator.svg",
        "svgMode": "element",
        "preserveAspectRatio": "none",
        "x": 0,
        "y": 0,
        "width": 90,
        "height": 80
      }
    },
    {
      "type": "bitSet",
      "label": "Bit Set",
      "icon": "bitSet.svg",
      "phases": [
        "ALGEBRAIC"
      ],
      "width": 80,
      "height": 80,
      "inputs": [
        {
          "x": 0,
          "y": 40,
          "side": "left"
        }
      ],
      "outputs": [
        {
          "x": 80,
          "y": 40,
          "side": "right"
        }
      ],
      "defaultParams": {
        "BitIndex": 0,
        "NumBits": 32
      },
      "render": {
        "type": "image",
        "src": "exports/bitSet.svg",
        "svgMode": "element",
        "preserveAspectRatio": "none",
        "x": 0,
        "y": 0,
        "width": 80,
        "height": 80
      }
    },
    {
      "type": "bitClear",
      "label": "Bit Clear",
      "icon": "bitClear.svg",
      "phases": [
        "ALGEBRAIC"
      ],
      "width": 80,
      "height": 80,
      "inputs": [
        {
          "x": 0,
          "y": 40,
          "side": "left"
        }
      ],
      "outputs": [
        {
          "x": 80,
          "y": 40,
          "side": "right"
        }
      ],
      "defaultParams": {
        "BitIndex": 0,
        "NumBits": 32
      },
      "render": {
        "type": "image",
        "src": "exports/bitClear.svg",
        "svgMode": "element",
        "preserveAspectRatio": "none",
        "x": 0,
        "y": 0,
        "width": 80,
        "height": 80
      }
    },
    {
      "type": "extractBits",
      "label": "Extract Bits",
      "icon": "extractBits.svg",
      "phases": [
        "ALGEBRAIC"
      ],
      "width": 90,
      "height": 80,
      "inputs": [
        {
          "x": 0,
          "y": 40,
          "side": "left"
        }
      ],
      "outputs": [
        {
          "x": 90,
          "y": 40,
          "side": "right"
        }
      ],
      "defaultParams": {
        "StartBit": 0,
        "NumBitsToExtract": 8,
        "NumBits": 32,
        "OutputScaling": "keepWeight"
      },
      "render": {
        "type": "image",
        "src": "exports/extractBits.svg",
        "svgMode": "element",
        "preserveAspectRatio": "none",
        "x": 0,
        "y": 0,
        "width": 90,
        "height": 80
      }
    },
    {
      "type": "shiftArithmetic",
      "label": "Shift Arithmetic",
      "icon": "shiftArithmetic.svg",
      "phases": [
        "ALGEBRAIC"
      ],
      "width": 90,
      "height": 80,
      "inputs": [
        {
          "x": 0,
          "y": 40,
          "side": "left"
        }
      ],
      "outputs": [
        {
          "x": 90,
          "y": 40,
          "side": "right"
        }
      ],
      "defaultParams": {
        "ShiftDirection": "Left",
        "ShiftNumber": 1
      },
      "render": {
        "type": "image",
        "src": "exports/shiftArithmetic.svg",
        "svgMode": "element",
        "preserveAspectRatio": "none",
        "x": 0,
        "y": 0,
        "width": 90,
        "height": 80
      }
    },
    {
      "type": "combinatorialLogic",
      "label": "Combinatorial Logic",
      "icon": "combinatorialLogic.svg",
      "phases": [
        "ALGEBRAIC"
      ],
      "width": 90,
      "height": 80,
      "inputs": [
        {
          "x": 0,
          "y": 40,
          "side": "left"
        }
      ],
      "outputs": [
        {
          "x": 90,
          "y": 40,
          "side": "right"
        }
      ],
      "defaultParams": {
        "TruthTable": [
          0,
          1,
          1,
          0
        ]
      },
      "render": {
        "type": "image",
        "src": "exports/combinatorialLogic.svg",
        "svgMode": "element",
        "preserveAspectRatio": "none",
        "x": 0,
        "y": 0,
        "width": 90,
        "height": 80
      }
    },
    {
      "type": "compareToZero",
      "label": "Compare Zero",
      "icon": "compareToZero.svg",
      "phases": [
        "ALGEBRAIC"
      ],
      "width": 100,
      "height": 80,
      "inputs": [
        {
          "x": 0,
          "y": 40,
          "side": "left"
        }
      ],
      "outputs": [
        {
          "x": 100,
          "y": 40,
          "side": "right"
        }
      ],
      "defaultParams": {
        "relop": "ne"
      },
      "render": {
        "type": "image",
        "src": "exports/compareToZero.svg",
        "svgMode": "element",
        "preserveAspectRatio": "none",
        "x": 0,
        "y": 0,
        "width": 100,
        "height": 80
      }
    },
    {
      "type": "relationalOperator",
      "label": "Relational",
      "icon": "relationalOperator.svg",
      "phases": [
        "ALGEBRAIC"
      ],
      "width": 100,
      "height": 80,
      "inputs": [
        {
          "x": 0,
          "y": 30,
          "side": "left"
        },
        {
          "x": 0,
          "y": 50,
          "side": "left"
        }
      ],
      "outputs": [
        {
          "x": 100,
          "y": 40,
          "side": "right"
        }
      ],
      "defaultParams": {
        "Operator": "ge"
      },
      "render": {
        "type": "image",
        "src": "exports/relationalOperator.svg",
        "svgMode": "element",
        "preserveAspectRatio": "none",
        "x": 0,
        "y": 0,
        "width": 100,
        "height": 80
      }
    },
    {
      "type": "switchCase",
      "label": "Switch Case",
      "icon": "switchCase.svg",
      "phases": [
        "ALGEBRAIC"
      ],
      "width": 100,
      "height": 80,
      "inputs": [
        {
          "x": 0,
          "y": 40,
          "side": "left"
        }
      ],
      "outputs": [
        {
          "x": 100,
          "y": 30,
          "side": "right"
        },
        {
          "x": 100,
          "y": 50,
          "side": "right"
        }
      ],
      "defaultParams": {
        "CaseConditions": "{1}",
        "ShowDefaultCase": "on"
      },
      "render": {
        "type": "image",
        "src": "exports/switchCase.svg",
        "svgMode": "element",
        "preserveAspectRatio": "none",
        "x": 0,
        "y": 0,
        "width": 100,
        "height": 80
      }
    },
    {
      "type": "if",
      "label": "If",
      "icon": "if.svg",
      "phases": [
        "ALGEBRAIC"
      ],
      "width": 100,
      "height": 80,
      "inputs": [
        {
          "x": 0,
          "y": 30,
          "side": "left"
        },
        {
          "x": 0,
          "y": 50,
          "side": "left"
        }
      ],
      "outputs": [
        {
          "x": 100,
          "y": 30,
          "side": "right"
        },
        {
          "x": 100,
          "y": 50,
          "side": "right"
        }
      ],
      "defaultParams": {
        "IfExpression": "u1 > 0",
        "ElseIfExpressions": "",
        "ShowElse": "on"
      },
      "render": {
        "type": "image",
        "src": "exports/if.svg",
        "svgMode": "element",
        "preserveAspectRatio": "none",
        "x": 0,
        "y": 0,
        "width": 100,
        "height": 80
      }
    },
    {
      "type": "logicalOperator",
      "label": "Logical Operator",
      "icon": "logicalOperator.svg",
      "phases": [
        "ALGEBRAIC"
      ],
      "width": 80,
      "height": 80,
      "inputs": [
        {
          "x": 0,
          "y": 30,
          "side": "left"
        },
        {
          "x": 0,
          "y": 50,
          "side": "left"
        }
      ],
      "outputs": [
        {
          "x": 80,
          "y": 40,
          "side": "right"
        }
      ],
      "defaultParams": {
        "Operator": "AND"
      },
      "render": {
        "type": "image",
        "src": "exports/logicalOperator.svg",
        "svgMode": "element",
        "preserveAspectRatio": "none",
        "x": 0,
        "y": 0,
        "width": 80,
        "height": 80
      }
    }
  ]
}
Runtimemodules/nflow_blocks/src/cpp/logic/logicalOperator.cpp
//=============================================================================
// Copyright (c) 2016-present Allan CORNET (Nelson)
//=============================================================================
// This file is part of Nelson.
//=============================================================================
// LICENCE_BLOCK_BEGIN
// SPDX-License-Identifier: LGPL-3.0-or-later
// LICENCE_BLOCK_END
//=============================================================================
// logicalOperator: a single configurable logical block (Logical Operator).
// Operator is AND / OR / NAND / NOR / XOR / XNOR / NOT; it reduces the N boolean
// inputs element-wise (a nonzero input is true). NOT takes a single input and
// negates it. Pure algebraic feedthrough over the input width; C / Rust code
// generation. Complements the fixed and / or / xor / not blocks with one
// dispatchable block (matching the standard Logical Operator).
//=============================================================================
#include "SimEngineTypes.hpp"
#include "BlockRegistry.hpp"
#include "FieldNames.hpp"
#include "NFlowBlockDescriptor.hpp"
#include "NFlowCodegenHelpers.hpp"
#include <cmath>
#include <string>
#include <vector>
#include "logic_blocks.hpp"
//=============================================================================
namespace Nelson {
namespace NFlow {
    //=============================================================================
    // Operator codes: 0 AND, 1 OR, 2 NAND, 3 NOR, 4 XOR, 5 XNOR, 6 NOT.
    static int
    logOp(const nflow::BlockDescriptor& bd)
    {
        const std::string s = bd.paramStr("Operator", "AND");
        if (s == "OR") {
            return 1;
        }
        if (s == "NAND") {
            return 2;
        }
        if (s == "NOR") {
            return 3;
        }
        if (s == "XOR") {
            return 4;
        }
        if (s == "XNOR") {
            return 5;
        }
        if (s == "NOT") {
            return 6;
        }
        return 0;
    }
    //=============================================================================
    bool
    handleLogicalOperator(SimCtx& ctx, const Block& b, Phase phase)
    {
        if (phase != Phase::ALGEBRAIC) {
            return false;
        }
        nflow::BlockDescriptor bd(b, ctx.variables);
        const int op = logOp(bd);
        const int n = numInputs(ctx, b.nid);
        std::vector<SigView> ins(n);
        for (int i = 0; i < n; ++i) {
            ins[i] = getInputSig(ctx, b.nid, i);
        }
        return emitElementwise(ctx, b.nid, [&](int k) -> double {
            if (op == 6) { // NOT (single input)
                const double v = (n > 0) ? sigAt(ins[0], k) : 0.0;
                return (v == 0.0) ? 1.0 : 0.0;
            }
            int trueCount = 0;
            bool anyTrue = false;
            bool allTrue = true;
            for (int i = 0; i < n; ++i) {
                const double v = sigAt(ins[i], k);
                const bool t = !(std::isnan(v) || v == 0.0);
                anyTrue = anyTrue || t;
                allTrue = allTrue && t;
                if (t) {
                    ++trueCount;
                }
            }
            switch (op) {
            case 1:
                return anyTrue ? 1.0 : 0.0; // OR
            case 2:
                return allTrue ? 0.0 : 1.0; // NAND
            case 3:
                return anyTrue ? 0.0 : 1.0; // NOR
            case 4:
                return (trueCount & 1) ? 1.0 : 0.0; // XOR
            case 5:
                return (trueCount & 1) ? 0.0 : 1.0; // XNOR
            case 0:
            default:
                return allTrue ? 1.0 : 0.0; // AND
            }
        });
    }
    //=============================================================================
    static std::string
    logExprC(int op, const std::vector<std::string>& in)
    {
        const int n = (int)in.size();
        if (op == 6) {
            const std::string x = n > 0 ? in[0] : std::string("0.0");
            return "(" + x + " == 0.0) ? 1.0 : 0.0";
        }
        std::string joined;
        for (int i = 0; i < n; ++i) {
            const std::string b = "(" + in[i] + " != 0.0)";
            if (op == 0 || op == 2) { // AND / NAND
                joined += (i ? " && " : "") + b;
            } else if (op == 1 || op == 3) { // OR / NOR
                joined += (i ? " || " : "") + b;
            } else { // XOR / XNOR: parity of the boolean count
                joined += (i ? " ^ " : "") + std::string("(int)") + b;
            }
        }
        if (op == 0) {
            return "(" + joined + ") ? 1.0 : 0.0";
        }
        if (op == 1) {
            return "(" + joined + ") ? 1.0 : 0.0";
        }
        if (op == 2) {
            return "(" + joined + ") ? 0.0 : 1.0";
        }
        if (op == 3) {
            return "(" + joined + ") ? 0.0 : 1.0";
        }
        if (op == 4) {
            return "((" + joined + ") & 1) ? 1.0 : 0.0";
        }
        return "((" + joined + ") & 1) ? 0.0 : 1.0"; // XNOR
    }
    //=============================================================================
    static std::string
    logExprRust(int op, const std::vector<std::string>& in)
    {
        const int n = (int)in.size();
        if (op == 6) {
            const std::string x = n > 0 ? in[0] : std::string("0.0_f64");
            return "if " + x + " == 0.0_f64 { 1.0_f64 } else { 0.0_f64 }";
        }
        std::string joined;
        for (int i = 0; i < n; ++i) {
            const std::string b = "(" + in[i] + " != 0.0_f64)";
            if (op == 0 || op == 2) {
                joined += (i ? " && " : "") + b;
            } else if (op == 1 || op == 3) {
                joined += (i ? " || " : "") + b;
            } else {
                joined += (i ? " ^ " : "") + std::string("(") + b + " as i64)";
            }
        }
        if (op == 0 || op == 1) {
            return "if " + joined + " { 1.0_f64 } else { 0.0_f64 }";
        }
        if (op == 2 || op == 3) {
            return "if " + joined + " { 0.0_f64 } else { 1.0_f64 }";
        }
        if (op == 4) {
            return "if ((" + joined + ") & 1) != 0 { 1.0_f64 } else { 0.0_f64 }";
        }
        return "if ((" + joined + ") & 1) != 0 { 0.0_f64 } else { 1.0_f64 }";
    }
    //=============================================================================
    BlockCodegenTemplate
    getCodeGenCLogicalOperator()
    {
        BlockCodegenTemplate t;
        t.emitStep = [](const BlockCodegenArgs& a) {
            nflow::BlockDescriptor bd(*a.block, *a.variables);
            a.line("out_" + a.id + " = " + logExprC(logOp(bd), a.in) + ";");
        };
        return t;
    }
    //=============================================================================
    BlockCodegenTemplate
    getCodeGenRustLogicalOperator()
    {
        BlockCodegenTemplate t;
        t.emitStep = [](const BlockCodegenArgs& a) {
            nflow::BlockDescriptor bd(*a.block, *a.variables);
            a.line("out_" + a.id + " = " + logExprRust(logOp(bd), a.in) + ";");
        };
        return t;
    }
    //=============================================================================
} // namespace NFlow
} // namespace Nelson
//=============================================================================
💡Examples
NAND of two constants: NAND(1, 1) = 0.
d.blocks={ struct('id','a','type','constant','inputs',0,'outputs',1,'params',struct('Value',1)), struct('id','b','type','constant','inputs',0,'outputs',1,'params',struct('Value',1)), struct('id','g','type','logicalOperator','inputs',2,'outputs',1,'params',struct('Operator','NAND')), struct('id','w','type','toWorkspace','inputs',1,'outputs',0,'params',struct('VariableName','y','SaveFormat','Array')) };
d.connections={ struct('from','a','to','g','fromIndex',0,'toIndex',0), struct('from','b','to','g','fromIndex',0,'toIndex',1), struct('from','g','to','w','fromIndex',0,'toIndex',0) };
d.sampleTime=0.1; d.duration=0.2; d.solver='discrete'; d.variables=struct();
r=jsondecode(__nflow_simulate__(jsonencode(d)));
🔗See Also
andorrelationalOperator
🕔Version History
Version Description
1.0.0 initial version
Edit this page on GitHub