Skip to content

Conversation

@Kryonn
Copy link

@Kryonn Kryonn commented Oct 21, 2025

I was taking a look at logical operations and noticed that there is no function for NAND and NOR operations. This type of operation is most commonly used in projects related to digital systems, but since we have the logic category, I thought it would be a good idea to add these two to make it more complete. For now, I've only done this for number variables, but the idea is to expand the types of variables with subsequent PRs.

@josdejong
Copy link
Owner

Thanks @Kryonn! It makes sense to add two two variations too.

For now, I've only done this for number variables, but the idea is to expand the types of variables with subsequent PRs.

I indeed think though that we should not only support them for the plain numbers, but also for mathjs in general: add these two new functions to /src/function/logical.

@josdejong
Copy link
Owner

@Kryonn I see you've now implemented nor for all datatypes now. I guess implementing nand is on your todo list too.

Just to be sure: when implementing functions, there are a couple of extra things that need to be taken care of. This is documented here and may be of help: https://github.com/josdejong/mathjs/blob/develop/README.md#implementing-a-new-function

@Kryonn
Copy link
Author

Kryonn commented Oct 30, 2025

Just to be sure: when implementing functions, there are a couple of extra things that need to be taken care of. This is documented here and may be of help: https://github.com/josdejong/mathjs/blob/develop/README.md#implementing-a-new-function

I decided to start with the NOR function, and then do the NAND function. So, for the NOR function, I checked and applied the steps mentioned. However, when I went to test the function, I got two errors that I can't seem to resolve. The first error was related to the return of the toTex function. I didn't quite understand how we define the return for a given function. The second problem was related to the operation between two sparse matrices. I couldn't quite understand the error message. Could you help me?

error message
26 passing (27ms)
  2 failing

  1) nor
       should LaTeX nor:

      AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
actual expected

'\\mathrm{nor}\\left(1,\\2\\right)'

      + expected - actual

      -\mathrm{nor}\left(1,2\right)
      +\left(1\2\right)

      at Context.<anonymous> (file:///C:/Users/Kryoj/OneDrive/Documents/GitHub/mathjs/test/unit-tests/function/logical/nor.test.js:234:12)
      at process.processImmediate (node:internal/timers:485:21)

  2) nor
       SparseMatrix
         should nor sparse matrix - sparse matrix:

      AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
+ actual - expected

  SparseMatrix {
    _datatype: undefined,
+   _index: [],
-   _index: [
-     0
-   ],
    _ptr: [
      0,
+     0,
+     0
-     1,
-     1
    ],
    _size: [
      2,
      2
    ],
+   _values: []
-   _values: [
-     true
-   ]
  }

      + expected - actual

       {
         "_datatype": [undefined]
      -  "_index": []
      +  "_index": [
      +    0
      +  ]
         "_ptr": [
           0
      -    0
      -    0
      +    1
      +    1
         ]
         "_size": [
           2
           2
         ]
      -  "_values": []
      +  "_values": [
      +    true
      +  ]
       }

@josdejong
Copy link
Owner

@Kryonn do you still need help? I did run the tests on the latest version of this PR but do not get the errors you report above (only two errors related to documentation).

@Kryonn
Copy link
Author

Kryonn commented Nov 1, 2025

@Kryonn do you still need help? I did run the tests on the latest version of this PR but do not get the errors you report above

In the end, I managed to solve it. Now, all that's left is to implement the NAND function.

(only two errors related to documentation).

Okay! I'll check it carefully and correct it.

@Kryonn
Copy link
Author

Kryonn commented Nov 5, 2025

I finished the NOR function, it’s now working correctly and without any documentation issues.

As for the NAND function, I tried to implement it, but I wasn’t able to fix the test errors. From what I can tell, it would be necessary to create new matrix function application algorithms (matAlgoXXXX), which seems a bit complicated.

So, I’ll wrap up my contribution here. I hope I was able to help, and thank you for your support!

@gwhitney
Copy link
Collaborator

gwhitney commented Nov 6, 2025

I have no objection to adding nand and nor functions to mathjs, but is there any motivation for writing new internal code as opposed to just implementing them internally as not(and(x, y)) and not(or(x, y))? is it the inefficiency of passing over array arguments twice, or something else? If the former is the only real concern, then I'd recommend implementing only the matrix algorithms directly, with or without the matAlgo functions as appropriate, and forwarding all other arguments to not(and(x,y)) or not(or(x,y)). In particular, since x nor y === (not x) and (not y) the "nor" matrix by scalar operations should short-circuit when the scalar is true like the "and" ones do when the scalar is false. (And similarly, the matrix implementation for "or" is actually your best template when implementing L nand M === (not L) or (not M).

Finally, my writing those expressions above makes me wonder whether nand and nor should be added as named infix operators like or and and? Again, I don't see any reason why they should be treated differently from the other logical operations, so I would recommend doing so.

@Kryonn, looking forward to your feedback on these points, and whether based on them you want to restart your efforts to get these operations into mathjs? If not, please be aware that due to limited time on the part of the core mathjs team, this PR would likely be converted to a discussion on adding "nand" and "nor" to mathjs, with a pointer to the closed PR as a starting place for anyone who is motivated to take up the effort to add these operations. It's just unfortunate that this PR as it stands isn't quite ready for merging (and in particular I have a tough time imagining merging nor without nand).

@Kryonn
Copy link
Author

Kryonn commented Nov 6, 2025

I have no objection to adding nand and nor functions to mathjs, but is there any motivation for writing new internal code as opposed to just implementing them internally as not(and(x, y)) and not(or(x, y))? is it the inefficiency of passing over array arguments twice, or something else? If the former is the only real concern, then I'd recommend implementing only the matrix algorithms directly, with or without the matAlgo functions as appropriate, and forwarding all other arguments to not(and(x,y)) or not(or(x,y)). In particular, since x nor y === (not x) and (not y) the "nor" matrix by scalar operations should short-circuit when the scalar is true like the "and" ones do when the scalar is false. (And similarly, the matrix implementation for "or" is actually your best template when implementing L nand M === (not L) or (not M).

I actually thought about doing it that way, but it seemed a bit too easy to be true. Anyway, I rewrote the NOR function and implemented the NAND function, both built by applying the OR and AND functions followed by the NOT function.

@gwhitney
Copy link
Collaborator

gwhitney commented Nov 6, 2025

Excellent. With this, we will definitely get this PR over the finish line. Here is a checklist of items that remain before final review:

  • Add nor/nand to test/typescript-tests/testTypes.ts
  • Add nor/nand to the list of named infix logical operators in the mathjs expression language, parallel to or/and
  • Add parsing tests for the new infix operators
  • Avoid temporary matrices in the matrix versions of nor/nand (i.e., just for matrices, implement nor roughly the way you had, but with short-circuiting when the scalar is true in the matrix-by-scalar case, and implement nand on matrices directly - the problem with not-and for matrices is the "and" matrix gets made, then a copy with every entry negated gets made, and then the intermediate "and" matrix is just immediately thrown away. For large matrices that's a lot of extra work/memory thrashing.)
  • Include nand in your new, and much appreciated, test/unit-tests/plain/number/logical.test.js

Please let us know which of the above you can tackle and plan to do (the more of them you do, the quicker this will get in, but we will be able to get around to all of them eventually now that the PR is this far along). Thanks so much!

@Kryonn
Copy link
Author

Kryonn commented Nov 7, 2025

I will do these tasks

  • Add nor/nand to test/typescript-tests/testTypes.ts
  • Add nor/nand to the list of named infix logical operators in the mathjs expression language, parallel to or/and
  • Add parsing tests for the new infix operators
  • Avoid temporary matrices in the matrix versions of nor/nand (i.e., just for matrices, implement nor roughly the way you had, but with short-circuiting when the scalar is true in the matrix-by-scalar case, and implement nand on matrices directly - the problem with not-and for matrices is the "and" matrix gets made, then a copy with every entry negated gets made, and then the intermediate "and" matrix is just immediately thrown away. For large matrices that's a lot of extra work/memory thrashing.)
  • Include nand in your new, and much appreciated, test/unit-tests/plain/number/logical.test.js

Copy link
Author

@Kryonn Kryonn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I finished the tasks. \o/

Copy link
Collaborator

@gwhitney gwhitney left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the further updates. I take it from your previous post that you plan to leave the matrix optimizations, and extending the plain number logical tests to nand, to the mathjs maintainer crew? (Just checking for definiteness, I wasn't clear whether maybe you made your post a checkbox list too so you could check them off as they were done?)

In any case, let's get these points finished off and move this PR ever closer to merging. Thanks!

/**
* logical and, 'x and y'
* logical nand, 'x nand y'
* @return {Node} node
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I mentioned in earlier review, nand is actually a form of or (since A nand B == (not A) or (not B)). So nand should go in the logical or precedence group (maybe rename it parseLogicalOrNand?? that's optional). And conversely A nor B == (not A) and (not B) is a form of and so should go in this precedence group, which you could optionally rename parseLogicalAndNor. Thanks!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solved. I reversed the group in which the NAND and NOR functions were inserted (NAND -> OR group, NOR -> AND group) and changed the names of the functions.

assert.strictEqual(parseAndEval('(true or true) and false'), false)
})

it('should respect precedence between logical nand and or', function () {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice range of tests! This precedence test, though, needs adjusting because the cases you happened to choose don't distinguish the precedence options. For example, both of these expressions are true: (false nand true) or true and false nand (true or true). For testing precedence, you need an expression for which the result depends on how it is parenthesized. For example, for a nand - or combo like the first one, you could use true nand false or true, since (true nand false) or true is true, but true nand (false or true) is false. You should find operands that show the same contrast for the other combos you test. Overall, those combos should include at least nand - and, nand-or, nor - and, and nor - or. Thanks!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solved. Now, in the last commit, I added a test for the NAND-OR, NAND-AND, NOR-OR, and NOR-AND operations, and this time I made sure to give different results in order to validate the order of precedence.

@Kryonn
Copy link
Author

Kryonn commented Nov 12, 2025

Thanks for the further updates. I take it from your previous post that you plan to leave the matrix optimizations, and extending the plain number logical tests to nand, to the mathjs maintainer crew? (Just checking for definiteness, I wasn't clear whether maybe you made your post a checkbox list too so you could check them off as they were done?)

In any case, let's get these points finished off and move this PR ever closer to merging. Thanks!

True, I hadn't thought about it that way. But I really only intend to do the tasks that are marked.

@gwhitney
Copy link
Collaborator

That's fine, just looking for clarity. If you're able to handle the comments on what you've done so far, we should be able to get this PR merged before long.

@Kryonn
Copy link
Author

Kryonn commented Nov 12, 2025

That's fine, just looking for clarity. If you're able to handle the comments on what you've done so far, we should be able to get this PR merged before long.

I have finished correcting all the problems mentioned in the comments. I think everything is fine now.

@gwhitney
Copy link
Collaborator

Excellent, thanks! Your parsing tests now show all of the grouping distinctions. However, they don't yet test the precedence, in that it just so happens in every one of the tests the higher-precedence operator, when there was a difference, happened to end up on the left. The unit tests need examples where the precedence causes grouping on the right, e.g. A or B nor C parses as A or (B nor C) (and so on for the other three combos where the higher-precedence operator is on the left).

Or actually, it would be simpler and better to just have tests that show that nand is lower-precedence than xor and nor is higher-precedence than xor:

  • Every operator should be tested against the ones immediately higher and lower precedence than it. Note that by this principle, now that the precedences of nand and nor have been corrected, please change the other precedence tests you have to be between nand and conditional, and between nor and bitwise or, since those are now the adjacent pairs.
  • Checking nand vs xor and then xor vs nor eliminates need to check nand vs nor, or vs nor, etc. etc., so actually fewer tests.

Note you can leave in all the tests you have so far -- there's no problem with them -- but tests that demonstrate the proper precedence of each vs xor are needed, since right now there don't happen to be any tests that prove proper precedence in the whole nand - xor - nor range. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants