-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implemented bitwise nand and nor; tests and docs included. #40339
Conversation
base/bool.jl
Outdated
1 | ||
``` | ||
""" | ||
nand(x::Bool, y::Bool) = ~(x & y) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have you tried this with just defining a single method as proposed in #40272? I really don't think you need to define 16 methods here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried it with just the single method but ran into issues when testing nand(missing)
and nand(x)
where x
is an integer (trying the single-arg versions caused an error). Instead of defining 16 methods, I've defined 4:
julia> methods(nand)
# 4 methods for generic function "nand":
[1] nand(::Missing) in Base at missing.jl:101
[2] nand(a::BigInt, b::BigInt) in Base.GMP at gmp.jl:551
[3] nand(x::Integer) in Base at operators.jl:579
[4] nand(x, y) in Base at bool.jl:105
This passes all the tests I've made.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would skip the one-arg ones as well and just have the two-arg version. nand(x) = x
seems questionable to me anyways.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand how nand(x) = x
seems questionable, but out of curiosity, what's your thinking on nand(missing)
? In my limited knowledge, it seems like we should include it since the majority of other operators have the same property. If not, is there a concept I'm missing / misunderstanding?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For any arbitrary set of values, we can decompose these two functions essentially just directly into their constituent parts:
nand(x...) = ~(&)(x...)
nor(x...) = ~|(x...)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My initial thought was to edge on the more conservative side here and just implement the most minimal version for now. This implementation now seems like a sensible extension to me though, so we might as well go with that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it, I appreciate your help!
base/gmp.jl
Outdated
@@ -547,6 +547,18 @@ end | |||
(-)(x::BigInt) = MPZ.neg(x) | |||
(~)(x::BigInt) = MPZ.com(x) | |||
|
|||
# nand and nor | |||
nand(a::BigInt, b::BigInt) = MPZ.com(MPZ.and(a, b)) | |||
nand(a::BigInt, b::BigInt, c::BigInt) = nand(nand(a, b), c) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not what I would expect multi-arg nand
to do. In all contexts I have encountered it, it was defined as ~(a & b & c)
. I think it would be better to just define the two-arg version for now though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it. I can remove the multi-arg versions for now.
base/promotion.jl
Outdated
nand(x::T, y::T) where {T<:Integer} = no_op_err("nand", T) | ||
nor(x::T, y::T) where {T<:Integer} = no_op_err("nor", T) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should not be an error. The idiomatic Julia solution is to use duck-typing, so that nand
/nor
just work if a type defines ~
and &
/|
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it. I'll remove these two lines.
base/gmp.jl
Outdated
@@ -547,6 +547,10 @@ end | |||
(-)(x::BigInt) = MPZ.neg(x) | |||
(~)(x::BigInt) = MPZ.com(x) | |||
|
|||
# nand and nor | |||
nand(a::BigInt, b::BigInt) = MPZ.com(MPZ.and(a, b)) | |||
nor(x::BigInt, y::BigInt) = MPZ.com(MPZ.ior(x, y)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are these definitions needed? Aren't they equivalent to the generic ~(x & y)
and ~(x | y)
fallback definitions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right, they are equivalent. I will remove them.
Seems like there should be some specialized methods in |
Addresses #40272
Features included:
nand
andnor
(for single, dual, multiple arguments) similar to the implementation of bitwisexor
:⊼
is an alias fornand
⊽
is an alias fornor
xor
(checked many of these by computing the 256-bit binary representation of a number and wrote a script to computenand
ornor
digit by digit). Checked usingmake test-all
.make docs
to confirm that it worked./base/math.md
and/manual/mathematical-operations.md
to includenand
andnor
make -C doc doctest=true
to check doctests (doctests were modeled afterxor
).\nand
and\nor
;\barwedge
and\veebar
are still functional.