Skip to content
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

Merged
merged 6 commits into from
Apr 9, 2021
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions base/bool.jl
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,74 @@ julia> [true; true; false] .⊻ [true; false; false]
"""
xor(x::Bool, y::Bool) = (x != y)

"""
nand(x, y)
⊼(x, y)

Bitwise nand (not and) of `x` and `y`. Implements
[three-valued logic](https://en.wikipedia.org/wiki/Three-valued_logic),
returning [`missing`](@ref) if one of the arguments is `missing`.

The infix operation `a ⊼ b` is a synonym for `nand(a,b)`, and
`⊼` can be typed by tab-completing `\\nand` or `\\barwedge` in the Julia REPL.

# Examples
```jldoctest
julia> nand(true, false)
true

julia> nand(true, true)
false

julia> nand(true, missing)
missing

julia> false ⊼ false
true

julia> [true; true; false] .⊼ [true; false; false]
3-element BitVector:
0
1
1
```
"""
nand(x, y) = ~(x & y)

"""
nor(x, y)
⊽(x, y)

Bitwise nor (not or) of `x` and `y`. Implements
[three-valued logic](https://en.wikipedia.org/wiki/Three-valued_logic),
returning [`missing`](@ref) if one of the arguments is `missing`.

The infix operation `a ⊽ b` is a synonym for `nor(a,b)`, and
`⊽` can be typed by tab-completing `\\nor` or `\\veebar` in the Julia REPL.

# Examples
```jldoctest
julia> nor(true, false)
false

julia> nor(true, true)
false

julia> nor(true, missing)
false

julia> false ⊽ false
true

julia> [true; true; false] .⊽ [true; false; false]
3-element BitVector:
0
0
1
```
"""
nor(x, y) = ~(x | y)

>>(x::Bool, c::UInt) = Int(x) >> c
<<(x::Bool, c::UInt) = Int(x) << c
>>>(x::Bool, c::UInt) = Int(x) >>> c
Expand Down
4 changes: 4 additions & 0 deletions base/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,10 @@ export
≢,
xor,
⊻,
nand,
nor,
⊼,
⊽,
%,
÷,
&,
Expand Down
6 changes: 5 additions & 1 deletion base/gmp.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ module GMP

export BigInt

import .Base: *, +, -, /, <, <<, >>, >>>, <=, ==, >, >=, ^, (~), (&), (|), xor,
import .Base: *, +, -, /, <, <<, >>, >>>, <=, ==, >, >=, ^, (~), (&), (|), xor, nand, nor,
binomial, cmp, convert, div, divrem, factorial, cld, fld, gcd, gcdx, lcm, mod,
ndigits, promote_rule, rem, show, isqrt, string, powermod,
sum, trailing_zeros, trailing_ones, count_ones, tryparse_internal,
Expand Down Expand Up @@ -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))
Copy link
Member

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?

Copy link
Contributor Author

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.


<<(x::BigInt, c::UInt) = c == 0 ? x : MPZ.mul_2exp(x, c)
>>(x::BigInt, c::UInt) = c == 0 ? x : MPZ.fdiv_q_2exp(x, c)
>>>(x::BigInt, c::UInt) = x >> c
Expand Down
2 changes: 1 addition & 1 deletion base/missing.jl
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ isapprox(::Missing, ::Any; kwargs...) = missing
isapprox(::Any, ::Missing; kwargs...) = missing

# Unary operators/functions
for f in (:(!), :(~), :(+), :(-), :(*), :(&), :(|), :(xor),
for f in (:(!), :(~), :(+), :(-), :(*), :(&), :(|), :(xor), :(nand), :(nor),
:(zero), :(one), :(oneunit),
:(isfinite), :(isinf), :(isodd),
:(isinteger), :(isreal), :(isnan),
Expand Down
4 changes: 4 additions & 0 deletions base/operators.jl
Original file line number Diff line number Diff line change
Expand Up @@ -576,8 +576,12 @@ identity(x) = x
(&)(x::Integer) = x
(|)(x::Integer) = x
xor(x::Integer) = x
nand(x::Integer) = x
nor(x::Integer) = x

const ⊻ = xor
const ⊼ = nand
const ⊽ = nor

# foldl for argument lists. expand fully up to a point, then
# switch to a loop. this allows small cases like `a+b+c+d` to be managed
Expand Down
2 changes: 2 additions & 0 deletions doc/src/base/math.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ Base.:(~)
Base.:(&)
Base.:(|)
Base.xor
Base.nand
Base.nor
Base.:(!)
&&
||
Expand Down
14 changes: 14 additions & 0 deletions doc/src/manual/mathematical-operations.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ are supported on all primitive integer types:
| `x & y` | bitwise and |
| `x \| y` | bitwise or |
| `x ⊻ y` | bitwise xor (exclusive or) |
| `x ⊼ y` | bitwise nand (not and) |
| `x ⊽ y` | bitwise nor (not or) |
| `x >>> y` | [logical shift](https://en.wikipedia.org/wiki/Logical_shift) right |
| `x >> y` | [arithmetic shift](https://en.wikipedia.org/wiki/Arithmetic_shift) right |
| `x << y` | logical/arithmetic shift left |
Expand All @@ -104,6 +106,18 @@ julia> 123 ⊻ 234
julia> xor(123, 234)
145

julia> nand(123, 123)
-124

julia> 123 ⊼ 123
-124

julia> nor(123, 124)
-128

julia> 123 ⊽ 124
-128

julia> ~UInt32(123)
0xffffff84

Expand Down
4 changes: 4 additions & 0 deletions stdlib/REPL/src/latex_symbols.jl
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ const latex_symbols = Dict(
"\\backpprime" => "‶",
"\\backppprime" => "‷",
"\\xor" => "⊻",
"\\nand" => "⊼",
"\\nor" => "⊽",
"\\iff" => "⟺",
"\\implies" => "⟹",
"\\impliedby" => "⟸",
Expand Down Expand Up @@ -2651,4 +2653,6 @@ const symbols_latex_canonical = Dict(
"→" => "\\to",
"ε" => "\\varepsilon",
"⊻" => "\\xor",
"⊼" => "\\nand",
"⊽" => "\\nor",
)
36 changes: 36 additions & 0 deletions test/bitarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -832,6 +832,8 @@ timesofar("unary arithmetic")
@check_bit_operation broadcast(&, b1, b2) BitMatrix
@check_bit_operation broadcast(|, b1, b2) BitMatrix
@check_bit_operation broadcast(xor, b1, b2) BitMatrix
@check_bit_operation broadcast(nand, b1, b2) BitMatrix
@check_bit_operation broadcast(nor, b1, b2) BitMatrix
@check_bit_operation (+)(b1, b2) Matrix{Int}
@check_bit_operation (-)(b1, b2) Matrix{Int}
@check_bit_operation broadcast(*, b1, b2) BitMatrix
Expand Down Expand Up @@ -861,6 +863,8 @@ timesofar("unary arithmetic")
@check_bit_operation broadcast(&, b0, b0) BitVector
@check_bit_operation broadcast(|, b0, b0) BitVector
@check_bit_operation broadcast(xor, b0, b0) BitVector
@check_bit_operation broadcast(nand, b0, b0) BitVector
@check_bit_operation broadcast(nor, b0, b0) BitVector
@check_bit_operation broadcast(*, b0, b0) BitVector
@check_bit_operation (*)(b0, b0') BitMatrix
end
Expand All @@ -871,6 +875,8 @@ timesofar("unary arithmetic")
@check_bit_operation broadcast(&, b1, i2) Matrix{Int}
@check_bit_operation broadcast(|, b1, i2) Matrix{Int}
@check_bit_operation broadcast(xor, b1, i2) Matrix{Int}
@check_bit_operation broadcast(nand, b1, i2) Matrix{Int}
@check_bit_operation broadcast(nor, b1, i2) Matrix{Int}
@check_bit_operation (+)(b1, i2) Matrix{Int}
@check_bit_operation (-)(b1, i2) Matrix{Int}
@check_bit_operation broadcast(*, b1, i2) Matrix{Int}
Expand Down Expand Up @@ -902,13 +908,17 @@ timesofar("unary arithmetic")
@check_bit_operation broadcast(&, i1, b2) Matrix{Int}
@check_bit_operation broadcast(|, i1, b2) Matrix{Int}
@check_bit_operation broadcast(xor, i1, b2) Matrix{Int}
@check_bit_operation broadcast(nand, i1, b2) Matrix{Int}
@check_bit_operation broadcast(nor, i1, b2) Matrix{Int}
@check_bit_operation broadcast(+, i1, b2) Matrix{Int}
@check_bit_operation broadcast(-, i1, b2) Matrix{Int}
@check_bit_operation broadcast(*, i1, b2) Matrix{Int}

@check_bit_operation broadcast(&, u1, b2) Matrix{UInt8}
@check_bit_operation broadcast(|, u1, b2) Matrix{UInt8}
@check_bit_operation broadcast(xor, u1, b2) Matrix{UInt8}
@check_bit_operation broadcast(nand, u1, b2) Matrix{UInt8}
@check_bit_operation broadcast(nor, u1, b2) Matrix{UInt8}
@check_bit_operation broadcast(+, u1, b2) Matrix{UInt8}
@check_bit_operation broadcast(-, u1, b2) Matrix{UInt8}
@check_bit_operation broadcast(*, u1, b2) Matrix{UInt8}
Expand Down Expand Up @@ -986,6 +996,14 @@ timesofar("unary arithmetic")
@check_bit_operation broadcast(xor, b1, false) BitMatrix
@check_bit_operation broadcast(xor, true, b1) BitMatrix
@check_bit_operation broadcast(xor, false, b1) BitMatrix
@check_bit_operation broadcast(nand, b1, true) BitMatrix
@check_bit_operation broadcast(nand, b1, false) BitMatrix
@check_bit_operation broadcast(nand, true, b1) BitMatrix
@check_bit_operation broadcast(nand, false, b1) BitMatrix
@check_bit_operation broadcast(nor, b1, true) BitMatrix
@check_bit_operation broadcast(nor, b1, false) BitMatrix
@check_bit_operation broadcast(nor, true, b1) BitMatrix
@check_bit_operation broadcast(nor, false, b1) BitMatrix
@check_bit_operation broadcast(+, b1, true) Matrix{Int}
@check_bit_operation broadcast(+, b1, false) Matrix{Int}
@check_bit_operation broadcast(-, b1, true) Matrix{Int}
Expand All @@ -1002,12 +1020,18 @@ timesofar("unary arithmetic")
@check_bit_operation broadcast(&, b1, b2) BitMatrix
@check_bit_operation broadcast(|, b1, b2) BitMatrix
@check_bit_operation broadcast(xor, b1, b2) BitMatrix
@check_bit_operation broadcast(nand, b1, b2) BitMatrix
@check_bit_operation broadcast(nor, b1, b2) BitMatrix
@check_bit_operation broadcast(&, b2, b1) BitMatrix
@check_bit_operation broadcast(|, b2, b1) BitMatrix
@check_bit_operation broadcast(xor, b2, b1) BitMatrix
@check_bit_operation broadcast(nand, b2, b1) BitMatrix
@check_bit_operation broadcast(nor, b2, b1) BitMatrix
@check_bit_operation broadcast(&, b1, i2) Matrix{Int}
@check_bit_operation broadcast(|, b1, i2) Matrix{Int}
@check_bit_operation broadcast(xor, b1, i2) Matrix{Int}
@check_bit_operation broadcast(nand, b1, i2) Matrix{Int}
@check_bit_operation broadcast(nor, b1, i2) Matrix{Int}
@check_bit_operation broadcast(+, b1, i2) Matrix{Int}
@check_bit_operation broadcast(-, b1, i2) Matrix{Int}
@check_bit_operation broadcast(*, b1, i2) Matrix{Int}
Expand All @@ -1018,6 +1042,8 @@ timesofar("unary arithmetic")
@check_bit_operation broadcast(&, b1, u2) Matrix{UInt8}
@check_bit_operation broadcast(|, b1, u2) Matrix{UInt8}
@check_bit_operation broadcast(xor, b1, u2) Matrix{UInt8}
@check_bit_operation broadcast(nand, b1, u2) Matrix{UInt8}
@check_bit_operation broadcast(nor, b1, u2) Matrix{UInt8}
@check_bit_operation broadcast(+, b1, u2) Matrix{UInt8}
@check_bit_operation broadcast(-, b1, u2) Matrix{UInt8}
@check_bit_operation broadcast(*, b1, u2) Matrix{UInt8}
Expand Down Expand Up @@ -1086,6 +1112,14 @@ timesofar("unary arithmetic")
@check_bit_operation broadcast(xor, b1, transpose(b3)) BitMatrix
@check_bit_operation broadcast(xor, b2, b1) BitMatrix
@check_bit_operation broadcast(xor, transpose(b3), b1) BitMatrix
@check_bit_operation broadcast(nand, b1, b2) BitMatrix
@check_bit_operation broadcast(nand, b1, transpose(b3)) BitMatrix
@check_bit_operation broadcast(nand, b2, b1) BitMatrix
@check_bit_operation broadcast(nand, transpose(b3), b1) BitMatrix
@check_bit_operation broadcast(nor, b1, b2) BitMatrix
@check_bit_operation broadcast(nor, b1, transpose(b3)) BitMatrix
@check_bit_operation broadcast(nor, b2, b1) BitMatrix
@check_bit_operation broadcast(nor, transpose(b3), b1) BitMatrix
@check_bit_operation broadcast(+, b1, b2) Matrix{Int}
@check_bit_operation broadcast(+, b1, transpose(b3)) Matrix{Int}
@check_bit_operation broadcast(+, b2, b1) Matrix{Int}
Expand Down Expand Up @@ -1390,6 +1424,8 @@ timesofar("reductions")
@test map(&, b1, b2) == map((x,y)->x&y, b1, b2) == broadcast(&, b1, b2)
@test map(|, b1, b2) == map((x,y)->x|y, b1, b2) == broadcast(|, b1, b2)
@test map(⊻, b1, b2) == map((x,y)->x⊻y, b1, b2) == broadcast(⊻, b1, b2) == broadcast(xor, b1, b2)
@test map(⊼, b1, b2) == map((x,y)->x⊼y, b1, b2) == broadcast(⊼, b1, b2) == broadcast(nand, b1, b2)
@test map(⊽, b1, b2) == map((x,y)->x⊽y, b1, b2) == broadcast(⊽, b1, b2) == broadcast(nor, b1, b2)

@test map(^, b1, b2) == map((x,y)->x^y, b1, b2) == b1 .^ b2
@test map(*, b1, b2) == map((x,y)->x*y, b1, b2) == b1 .* b2
Expand Down
6 changes: 6 additions & 0 deletions test/gmp.jl
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,12 @@ end
@test xor(a, b, c, d, f) == parse(BigInt,"-2413804710837418037418307081437316711364709261074607933698")
@test xor(a, b, c, d, f, g) == parse(BigInt,"2413804710837418037418307081437316711364709261074607933697")

@test nand(a, b) == parse(BigInt,"-125")
@test ⊼(a, b) == parse(BigInt,"-125")

@test nor(a, b) == parse(BigInt,"-327424")
@test ⊽(a, b) == parse(BigInt,"-327424")

@test (&)(a, b) == parse(BigInt,"124")
@test (&)(a, b, c) == parse(BigInt,"72")
@test (&)(a, b, c, d) == parse(BigInt,"8")
Expand Down
26 changes: 25 additions & 1 deletion test/missing.jl
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ end
arithmetic_operators = [+, -, *, /, ^, Base.div, Base.mod, Base.fld, Base.rem]

# All unary operators return missing when evaluating missing
for f in [!, ~, +, -, *, &, |, xor]
for f in [!, ~, +, -, *, &, |, xor, nand, nor]
@test ismissing(f(missing))
end

Expand Down Expand Up @@ -128,13 +128,37 @@ end
@test ismissing(xor(true, missing))
@test ismissing(xor(missing, false))
@test ismissing(xor(false, missing))
@test ismissing(nand(missing, true))
@test ismissing(nand(true, missing))
@test nand(missing, false) == true
@test nand(false, missing) == true
@test ismissing(⊼(missing, true))
@test ismissing(⊼(true, missing))
@test ⊼(missing, false) == true
@test ⊼(false, missing) == true
@test nor(missing, true) == false
@test nor(true, missing) == false
@test ismissing(nor(missing, false))
@test ismissing(nor(false, missing))
@test ⊽(missing, true) == false
@test ⊽(true, missing) == false
@test ismissing(⊽(missing, false))
@test ismissing(⊽(false, missing))

@test ismissing(missing & 1)
@test ismissing(1 & missing)
@test ismissing(missing | 1)
@test ismissing(1 | missing)
@test ismissing(xor(missing, 1))
@test ismissing(xor(1, missing))
@test ismissing(nand(missing, 1))
@test ismissing(nand(1, missing))
@test ismissing(⊼(missing, 1))
@test ismissing(⊼(1, missing))
@test ismissing(nor(missing, 1))
@test ismissing(nor(1, missing))
@test ismissing(⊽(missing, 1))
@test ismissing(⊽(1, missing))
end

@testset "* string concatenation" begin
Expand Down
18 changes: 18 additions & 0 deletions test/numbers.jl
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,24 @@ const ≣ = isequal # convenient for comparing NaNs
@test xor(true, false) == true
@test xor(false, true) == true
@test xor(true, true) == false

@test false ⊼ false == true
@test true ⊼ false == true
@test false ⊼ true == true
@test true ⊼ true == false
@test nand(false, false) == true
@test nand(true, false) == true
@test nand(false, true) == true
@test nand(true, true) == false

@test false ⊽ false == true
@test true ⊽ false == false
@test false ⊽ true == false
@test true ⊽ true == false
@test nor(false, false) == true
@test nor(true, false) == false
@test nor(false, true) == false
@test nor(true, true) == false
end
@testset "bool operator" begin
@test Bool(false) == false
Expand Down
4 changes: 4 additions & 0 deletions test/operators.jl
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ end
@test (|)(2) == 2
@test xor(2) == 2
@test (⊻)(2) == 2
@test nand(2) == 2
@test (⊼)(2) == 2
@test nor(2) == 2
@test (⊽)(2) == 2

@test_throws MethodError min(Set([1]), Set([2]))
@test_throws MethodError max(Set([1]), Set([2]))
Expand Down