Skip to content

Commit 6c5b3be

Browse files
committed
Various optimizations, README update
1 parent 60e8741 commit 6c5b3be

File tree

6 files changed

+86
-66
lines changed

6 files changed

+86
-66
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "Mods"
22
uuid = "7475f97c-0381-53b1-977b-4c60186c8d62"
33
author = ["Edward Scheinerman <[email protected]>"]
4-
version = "2.2.3"
4+
version = "2.2.4"
55

66
[compat]
77
julia = "1"

README.md

Lines changed: 62 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -2,57 +2,6 @@
22

33
Modular arithmetic for Julia.
44

5-
## New in Version 2
6-
7-
8-
9-
With this new version the modulus of a `Mod` number must be of type `Int`.
10-
If a `Mod` number is constructed with any other typeof `Integer`, the
11-
constructor will (try to) convert it to type `Int`.
12-
13-
The old style `Mod{N,T}(v)` no longer works.
14-
15-
### Why this change?
16-
17-
There were various issues in the earlier version of `Mods` that are
18-
resolved by requiring `N` to be of type `Int`.
19-
20-
* Previously `Mod` numbers created with different sorts of integer parameters would
21-
be different. So if `N = 17` and `M = 0x11`, then `Mod{N}(1)` would not be interoperable with `Mod{M}(1).`
22-
23-
* The internal storage of the value of the `Mod` numbers could be different.
24-
For example, `Mod{17}(-1)` would store the
25-
value internally as `-1` whereas `Mod{17}(16)` would store the value as `16`.
26-
27-
* Finally, if the modulus were a large `Int128` number, then arithmetic
28-
operations could silently fail.
29-
30-
We believe that the dominant use case for this module will be with moduli between
31-
`2` and `2^63-1` and so we do not expect this change to affect
32-
users. Further, since `Mod` numbers that required `Int128` moduli were
33-
likely to give incorrect results, version 1 of this module was buggy.
34-
35-
Users who require *smaller* integer (e.g., `Int8`) types should use the latest version 1 of `Mods`.
36-
37-
> **NEW!** For small moduli (between 2 and 255) see the [MiniMods](https://github.com/scheinerman/MiniMods.jl) module.
38-
39-
In addition, some functionality has been moved to the `extras` folder.
40-
See the `README` there.
41-
42-
### New in 2.2.3
43-
44-
The values of `Mod` numbers are held in 64-bit integers. If the moduli and values are
45-
large enough, integer arithmetic might overflow and yield incorrect results. To deal
46-
with this, integer values are expanded to 128 bits in order to ensure correctness,
47-
and then reduced by the modulus.
48-
49-
In prior versions, we always expanded values to 128 bits before arithmetic.
50-
51-
Starting in version 2.2.3, expansion to 128 bits only happens for moduli above
52-
`typemax(Int32)` which equals `2^31 - 1 = 2,147,483,647`. This results in a
53-
roughly 4 or 5 times speedup compared to prior versions.
54-
55-
565

576
## Quick Overview
587
This module supports modular values and arithmetic. The moduli are integers (at least 2)
@@ -247,7 +196,11 @@ julia> value(a)
247196
8
248197
```
249198

199+
## Limitations
250200

201+
The modulus and value of a `Mod` number are of type `Int` (or `Complex{Int}` for `GaussMod` numbers). This implies that the largest possible modulus is `typemax(Int)` which equals `2^63-1`
202+
(assuming a 64-bit system).
203+
251204
### Overflow safety
252205

253206
Integer operations on 64-bit numbers can give results requiring more than
@@ -270,6 +223,14 @@ julia> Mod{N}(a) * Mod{N}(a) # but this is correct!
270223
Mod{1000000000000000000}(0)
271224
```
272225

226+
This safety comes at a cost. If the modulus is large then operations may
227+
require enlarging the values to 128-bits before reducing mod `N`. For multipication,
228+
this widening occurs when the modulus exceeds `2^32-1`; for addition, widening occurs
229+
when the modulus exceeds `2^62-1`.
230+
231+
232+
233+
273234
## Extras
274235

275236
### Zeros and ones
@@ -432,4 +393,54 @@ GaussMod{13}(2 + 8im) + GaussMod{13}(11 + 2im)*x + GaussMod{13}(8 + 2im)*x^2 + G
432393
```
433394

434395

396+
## Version 2 vs Version 1 of `Mods`
397+
398+
399+
400+
In version 2 the modulus of a `Mod` number must be of type `Int`.
401+
If a `Mod` number is constructed with any other typeof `Integer`, the
402+
constructor will (try to) convert it to type `Int`.
403+
404+
The old style `Mod{N,T}(v)` no longer works.
405+
406+
### Why this change?
407+
408+
There were various issues in the earlier version of `Mods` that are
409+
resolved by requiring `N` to be of type `Int`.
410+
411+
* Previously `Mod` numbers created with different sorts of integer parameters would
412+
be different. So if `N = 17` and `M = 0x11`, then `Mod{N}(1)` would not be interoperable with `Mod{M}(1).`
413+
414+
* The internal storage of the value of the `Mod` numbers could be different.
415+
For example, `Mod{17}(-1)` would store the
416+
value internally as `-1` whereas `Mod{17}(16)` would store the value as `16`.
417+
418+
* Finally, if the modulus were a large `Int128` number, then arithmetic
419+
operations could silently fail.
420+
421+
We believe that the dominant use case for this module will be with moduli between
422+
`2` and `2^63-1` and so we do not expect this change to affect
423+
users. Further, since `Mod` numbers that required `Int128` moduli were
424+
likely to give incorrect results, version 1 of this module was buggy.
425+
426+
427+
In addition, some functionality has been moved to the `extras` folder.
428+
See the `README` there. In particular, the `CRT` function is no longer part of the `Mods`
429+
module but resides in `extras/CRT.jl`.
430+
431+
432+
### Different modulus types
433+
434+
Since moduli are of type `Int`, a `Mod` numbers uses 8 bytes (and a `GaussMod` uses 16 bytes). A
435+
large matrix of `Mod` numbers could unnecessarily use a lot of memory, especially if the moduli
436+
are small.
437+
438+
Some solutions to this problem:
435439

440+
* Use the latest Version 1 of `Mods`.
441+
* Create a cloned copy of the `Mods` package and edit this line in the file `Mods.jl`:
442+
```julia
443+
const value_type = Int # storage type for the value in a Mod
444+
```
445+
to set `value_type` to a smaller type of integer (say, `Int16`).
446+
* For very small moduli (between 2 and 255) see the [MiniMods](https://github.com/scheinerman/MiniMods.jl) module.

src/Mods.jl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,14 @@ CZQ = Union{Complex{<:Integer},Integer,Complex{<:Rational},Rational}
1414

1515
abstract type AbstractMod <: Number end
1616

17+
18+
const value_type = Int # storage type for the value in a Mod
19+
20+
const max_mod = typemax(value_type) # maximum modulus allowed
21+
const max_mul = isqrt(max_mod) # maximum before widening may be necessary for multiplication
22+
const max_add = max_mod ÷ 2 # maximum before widening may be necessary for addition
23+
24+
1725
include("constructors.jl")
1826
include("basic_functions.jl")
1927
include("promotion.jl")

src/arithmetic.jl

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22
# (+)(a::GaussMod{N}, b::GaussMod{N}) where {N} = Mod{N}(widen(a.val) + widen(b.val))
33

44
function (+)(a::Mod{N}, b::Mod{N}) where {N}
5-
N <= typemax(Int32) ? Mod{N}(a.val + b.val) : Mod{N}(widen(a.val) + widen(b.val))
5+
N <= max_add ? Mod{N}(a.val + b.val) : Mod{N}(widen(a.val) + widen(b.val))
66
end
7+
78
function (+)(a::GaussMod{N}, b::GaussMod{N}) where {N}
8-
N <= typemax(Int32) ? Mod{N}((a.val) + (b.val)) :
9+
N <= max_add ? Mod{N}((a.val) + (b.val)) :
910
GaussMod{N}(widen(a.val) + widen(b.val))
1011
end
1112

@@ -21,10 +22,10 @@ end
2122

2223

2324
function (*)(a::Mod{N}, b::Mod{N}) where {N}
24-
N <= typemax(Int32) ? Mod{N}(a.val * b.val) : Mod{N}(widemul(a.val, b.val))
25+
N <= max_mul ? Mod{N}(a.val * b.val) : Mod{N}(widemul(a.val, b.val))
2526
end
2627
function (*)(a::GaussMod{N}, b::GaussMod{N}) where {N}
27-
N <= typemax(Int32) ? GaussMod{N}(a.val * b.val) : GaussMod{N}(widemul(a.val, b.val))
28+
N <= max_mul ? GaussMod{N}(a.val * b.val) : GaussMod{N}(widemul(a.val, b.val))
2829
end
2930

3031
"""

src/basic_functions.jl

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
value(a::AbstractMod) = a.val
2-
modulus(a::Mod{N}) where N = N
3-
modulus(a::GaussMod{N}) where N = N
2+
modulus(a::Mod{N}) where {N} = N
3+
modulus(a::GaussMod{N}) where {N} = N
44

55
abs(a::AbstractMod) = abs(value(a))
66
conj(x::Mod{N}) where {N} = x
7-
conj(x::GaussMod{N}) where N = GaussMod{N}(value(x)')
7+
conj(x::GaussMod{N}) where {N} = GaussMod{N}(value(x)')
88

99
# real & imaginary parts
10-
real(x::GaussMod{N}) where N = Mod{N}(real(value(x)))
11-
imag(x::GaussMod{N}) where N = Mod{N}(imag(value(x)))
10+
real(x::GaussMod{N}) where {N} = Mod{N}(real(value(x)))
11+
imag(x::GaussMod{N}) where {N} = Mod{N}(imag(value(x)))
1212

1313

1414
function hash(x::AbstractMod, h::UInt64 = UInt64(0))

src/constructors.jl

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11

22
struct Mod{N} <: AbstractMod
3-
val::Int
3+
val::value_type
44
function Mod{N}(x::T) where {N,T<:Integer}
55
@assert N isa Integer && N > 1 "Modulus must be an integer greater than 1"
6-
@assert N <= typemax(Int) "modulus is too large"
7-
new{Int(N)}(mod(x, N))
6+
@assert N <= max_mod "modulus is too large"
7+
new{value_type(N)}(mod(x, N))
88
end
99
end
1010

@@ -25,10 +25,10 @@ mod(z::Complex{<:Integer}, n::Integer) = Complex(mod(real(z), n), mod(imag(z), n
2525

2626

2727
struct GaussMod{N} <: AbstractMod
28-
val::Complex{Int}
28+
val::Complex{value_type}
2929
function GaussMod{N}(x::Complex{T}) where {N,T<:Integer}
3030
@assert N isa Integer && N > 1 "Modulus must be an integer greater than 1"
31-
@assert N <= typemax(Int) "modulus is too large"
31+
@assert N <= max_mod "modulus is too large"
3232
new{Int(N)}(mod(x, N))
3333
end
3434
end

0 commit comments

Comments
 (0)