An important design decision in Oscar.jl is to use Julia as the user language by default. This means that integers typed at the REPL are Julia integers. However, for performance reasons, OSCAR has its own integer format.

Julia has a number of different integer types, but the two that are most relevant here are Int and BigInt. All the Julia integer types belong to Integer.

The Int type is for machine integers which are highly efficient, but can only represent integers up to a certain size, and most basic arithmetic operations are performed unchecked, that is, they can silently overflow. The Int type is the type of literal input such as 12, and should be used for loop control flow, array indices, and other situations where the overflow can be provably avoided.

The BigInt type is backed by GMP multiprecision integers and can represent integers whose size is usually only limited by available memory. While the BigInt type avoids overflow problems, it can be relatively slow in the Int range.

OSCAR currently has the integer type ZZRingElem, which for performance reasons scales internally from machine integers to GMP multiprecision integers.

The ring of integers

Every object in OSCAR representing a mathematical element has a parent. This is an object encoding information about where that element belongs.

The parent of an OSCAR integer is the ring of integers ZZ.

julia> ZZ
Integer ring

Integer constructors

OSCAR integers are created using ZZ:

julia> ZZ(2)^100

julia> ZZ(618970019642690137449562111)

One can also construct the integer $0$ with the empty constructor:

julia> ZZ()

The following special constructors are also provided:

  • zero(ZZ)
  • one(ZZ)
julia> zero(ZZ)

julia> one(ZZ)

Note that ZZ is not a Julia type, but the above methods of constructing OSCAR integers are similar to the way that Julia integer types can be used to construct Julia integers.

julia> Int(123)

julia> BigInt(123456343567843598776327698374259876295438725)

julia> zero(BigInt)

julia> one(Int)


OSCAR integers have the same limitations as GMP multiprecision integers, namely that they are limited by the available memory on the machine and in any case to signed integers whose absolute value does not exceed $2^{37}$ bits.


The Julia Int type is either a 32 or 64 bit integer, depending on the machine architecture (usually 64 bits on most modern machines). The range of values is machine dependent, but can be found by typing typemin(Int) and typemax(Int) in Julia.

Julia integers in OSCAR functions

For convenience, all basic arithmetic and exact division functions in OSCAR also accept Julia integers. If all of the arguments to an OSCAR function are julia integers, the resulting integers should be julia integers. However, once at least one of the arguments is an ZZRingElem, the function will generally behave as if all integer arguments were promoted to the type ZZRingElem, and the integers in the return generally should also be of type ZZRingElem. For example:

julia> divexact(ZZ(234), 2)

julia> typeof(gcd(4, 6))

julia> typeof(gcdx(4, 6))
Tuple{Int64, Int64, Int64}

julia> typeof(gcd(4, ZZ(6)))

julia> typeof(gcdx(4, ZZ(6)))
Tuple{ZZRingElem, ZZRingElem, ZZRingElem}

julia> typeof(jacobi_symbol(ZZ(2), ZZ(3)))

In the first example, 2 is a Julia integer but is still valid in the call to the OSCAR function divexact. In the last example, the exceptional function jacobi_symbol returns an Int as this will always be able to hold the three possible return values of -1, 0, or 1.


  • iszero(n::ZZRingElem) -> Bool
  • isone(n::ZZRingElem) -> Bool
  • is_unit(n::ZZRingElem) -> Bool
  • isodd(n::ZZRingElem) -> Bool
  • iseven(n::ZZRingElem) -> Bool
  • is_square(n::ZZRingElem) -> Bool
  • is_prime(n::ZZRingElem) -> Bool
  • is_probable_prime(n::ZZRingElem) -> Bool

The is_prime predicate will prove primality, whereas is_probable_prime may declare a composite number to be prime with very low probability.

Negative numbers, $0$ and $1$ are not considered prime by is_prime and is_probable_prime.

julia> isone(ZZ(1))

julia> is_unit(ZZ(-1))

julia> is_square(ZZ(16))

julia> is_probable_prime(ZZ(23))


  • sign(n::ZZRingElem) -> ZZRingElem

Return the sign of n, i.e. $n/|n|$ if $n \neq 0$, or $0$ otherwise.

julia> sign(ZZ(23))

julia> sign(ZZ(0))

julia> sign(ZZ(-1))
  • abs(n::ZZRingElem) -> ZZRingElem

Return the absolute value of $n$, i.e. $n$ if $n \geq 0$ and $-n$ otherwise

julia> abs(ZZ(-3))

Basic arithmetic

OSCAR provides the basic arithmetic operations +, - and * and comparison operators ==, !=, <, <=, >, >=, including mixed operations between Julia and OSCAR integers. It also provides division and powering as described below.

Division in OSCAR

OSCAR distinguishes a number of different kinds of division:

These choices have been made for maximum parsimony with the Julia language.


It is a common error to enter 1/2 for the fraction 'one half' in Julia. This expression is reserved for floating point division. Instead, the double slash operator // should be used for fractions.

Exact Division

  • divexact(a::ZZRingElem, b::ZZRingElem) -> ZZRingElem

Return the quotient of $a$ by $b$. The result of the exact division of two integers will always be another integer. Exact division raises an exception if the division is not exact, or if division by zero is attempted.

julia> divexact(ZZ(6), ZZ(3))

julia> divexact(ZZ(6), ZZ(0))
ERROR: DivideError: integer division error

julia> divexact(ZZ(6), ZZ(5))
ERROR: ArgumentError: Not an exact division

julia> divexact(ZZ(6), 2)


  • ^(a::ZZRingElem, b::Int) -> ZZRingElem

Return the result of powering $a$ by $b$.

julia> ZZ(37)^37

julia> ZZ(1)^(-2)

An exception will be raised if an integer other than $-1$ or $1$ is raised to a negative exponent.


In Julia 2^-2 is called a literal power. The value returned is a floating point value. To get behavior that agrees with OSCAR, one can write 2^Int(-2).

The following is allowed for convenience.

julia> ZZ(0)^0

In Julia, 2^64 will return zero, as the Julia integer $2$ is a machine integer. In OSCAR, the expression ZZ(2)^64 will return the expected result, just as the Julia equivalent BigInt(2)^64 does.

Euclidean division

The ring of integers is a Euclidean domain and OSCAR provides Euclidean division through the functions divrem, div and rem.

Integer Euclidean division of $a$ by $b$ computes a quotient and remainder such that

a = qb + r

with $|r| < |b|$.

Division with remainder

  • divrem(a::ZZRingElem, b::ZZRingElem) -> (ZZRingElem, ZZRingElem) : division with remainder
  • div(a::ZZRingElem, b::ZZRingElem) -> ZZRingElem : quotient only
  • rem(a::ZZRingElem, b::ZZRingElem) -> ZZRingElem : remainder only

Both rem and divrem compute the remainder $r$ such that when $r \neq 0$ the sign of $r$ is the same as the sign of $a$.

All three functions raise an exception if the modulus $b$ is zero.

julia> divrem(ZZ(5), ZZ(3))
(1, 2)

julia> div(ZZ(7), ZZ(2))

julia> rem(ZZ(4), ZZ(3))

julia> div(ZZ(2), ZZ(0))
ERROR: DivideError: integer division error

The rem function does not provide a minimal set of representatives, e.g. rem(-2, 3) = -2 but rem(1, 3) = 1.

Modular arithmetic

Modular reduction

  • mod(a::ZZRingElem, b::ZZRingElem) -> ZZRingElem : remainder only

The mod function computes a remainder $r$ such that when $r \neq 0$ the sign of $r$ is the same as the sign of $b$. Thus, if $b > 0$ then mod(a, b) will be in the range $[0, b)$. An exception is raised if the modulus $b$ is zero. This is summarised in the following table.

remdiv/divremsame as dividendtowards zero
modsame as divisortowards $-\infty$

There is no function implemented to compute the quotient corresponding to the remainder given by mod.

julia> mod(ZZ(4), ZZ(3))

julia> mod(ZZ(2), ZZ(0))
ERROR: DivideError: integer division error

Divisibility testing

  • divides(a::ZZRingElem, b::ZZRingElem) -> (Bool, ZZRingElem)

In OSCAR, we say that $b$ divides $a$ if there exists $c$ in the same ring such that $a = bc$.

The call divides(a, b) returns a tuple (flag, q) where flag is either true if b divides a in which case q will be a quotient, or flag is false if b does not divide a in which case q will be an integer whose value is not defined.

julia> divides(ZZ(6), ZZ(3))
(true, 2)

julia> divides(ZZ(5), ZZ(2))
(false, 0)

Note that for convenience we define:

julia> divides(ZZ(0), ZZ(0))
(true, 0)

Greatest common divisor

Greatest common divisor

  • gcd(a::ZZRingElem, b::ZZRingElem) -> ZZRingElem

Return the greatest common divisor of its inputs, which is by definition the largest integer dividing the two inputs, unless both inputs are zero in which case it returns zero. The result will always be non-negative and will only be zero if both inputs are zero.

julia> gcd(ZZ(34), ZZ(17))

julia> gcd(ZZ(3), ZZ(0))

Extended GCD

  • gcdx(a::ZZRingElem, b::ZZRingElem) -> (ZZRingElem, ZZRingElem, ZZRingElem)

Return a tuple $(g, s, t)$ such that $g$ is the greatest common divisor of $a$ and $b$ and $g = as + bt$. Normally $s$ and $t$ are chosen so that $|s| < |b|/(2g)$ and $|t| < |a|/(2g)$, where this uniquely defines $s$ and $t$. The following cases are handled specially:

  1. if $|a| = |b|$ then $t = b/|b|$
  2. if $b = 0$ or $|b| = 2g$ then $s = a/|a|$
  3. if $a = 0$ or $|a| = 2g$ then $t = b/|b|$

Least common multiple

  • lcm(a::ZZRingElem, b::ZZRingElem) -> ZZRingElem

Return the least common multiple of $a$ and $b$. This is the least positive multiple of $a$ and $b$, unless $a = 0$ or $b = 0$ which case we define the least common multiple to be zero.

julia> lcm(ZZ(6), ZZ(21))

julia> lcm(ZZ(0), ZZ(0))


Square roots

Julia and OSCAR distinguish two kinds of square root:

  • Integer square root (isqrt)
  • Floating point square root (sqrt)

We describe only the first of these here.

  • isqrt(n::ZZRingElem) -> ZZRingElem

Return the floor of the square root of its argument, i.e. the largest integer whose square does not exceed its input. An exception is raised if a negative input is passed.

julia> isqrt(ZZ(16))

julia> isqrt(ZZ(0))

julia> isqrt(ZZ(5))

julia> isqrt(ZZ(-3))
ERROR: DomainError with -3:
Argument must be non-negative
  • isqrtrem(n::ZZRingElem) -> (ZZRingElem, ZZRingElem)

Return the tuple (s, r) such that $s$ is equal to isqrt(n) and $n = s^2 + r$.

julia> isqrtrem(ZZ(16))
(4, 0)

julia> isqrtrem(ZZ(5))
(2, 1)

General roots

  • root(a::ZZRingElem, n::Int) -> ZZRingElem

Return an $n$-th root of $a$ or throw an error if it does not exist.

When $n$ is even, the non-negative root is always returned. An exception is raised if $n \leq 0$ or if $n$ is even and $a < 0$.

julia> root(ZZ(16), 4)

julia> root(ZZ(-5), 2)
ERROR: DomainError with (-5, 2):
Argument `x` must be positive if exponent `n` is even

julia> root(ZZ(12), -2)
ERROR: DomainError with -2:
Exponent must be positive


  • Int(n::ZZRingElem) -> Int
  • BigInt(n::ZZRingElem) -> BigInt

Convert the OSCAR integer to the respective Julia integer.

julia> n = ZZ(123)

julia> Int(n)

julia> BigInt(n)

In the case of Int, if the OSCAR integer is too large to fit, an exception is raised.

julia> Int(ZZ(12348732648732648763274868732687324))
ERROR: InexactError: convert(Int64, 12348732648732648763274868732687324)
  • fits(::Type{Int}, n::ZZRingElem) -> Bool

Return true if the OSCAR integer will fit in an Int.

julia> fits(Int, ZZ(123))

julia> fits(Int, ZZ(12348732648732648763274868732687324))


  • factor(n::ZZRingElem) -> Fac{ZZRingElem}

Return a factorisation of the given integer. The return value is a special factorisation struct which can be manipulated using the functions below.

julia> factor(ZZ(-6000361807272228723606))
-1 * 2 * 229^3 * 43669^3 * 3

julia> factor(ZZ(0))
ERROR: ArgumentError: Argument is not non-zero
  • unit(F::Fac) -> ZZRingElem
julia> F = factor(ZZ(-12))
-1 * 2^2 * 3

julia> unit(F)

Factorisation are iterable

Once created, a factorisation is iterable:

julia> F = factor(ZZ(-60))
-1 * 5 * 2^2 * 3

julia> for (p, e) in F; println("$p^$e"); end

The pairs (p, e) in a factorisation represent the prime power factors $p^e$ of the non-unit part of the factorisation. They can be placed in an array using collect:

julia> F = factor(ZZ(-60))
-1 * 5 * 2^2 * 3

julia> collect(F)
3-element Vector{Pair{ZZRingElem, Int64}}:
 5 => 1
 2 => 2
 3 => 1

Accessing exponents in a factorisation

One can also determine whether a given prime is in the non-unit part of a factorisation and if so return its exponent. If the exponent of a prime that is not in a factorisation is requested, an exception is raised.

For convenience, a Int can be used instead of an OSCAR integer for this functionality.

julia> F = factor(ZZ(-60))
-1 * 5 * 2^2 * 3

julia> 5 in F

julia> ZZ(3) in F

julia> 7 in F

julia> F[3]

julia> F[ZZ(7)]
ERROR: 7 is not a factor of -1 * 5 * 2^2 * 3

Combinatorial functions


The functions in this section that take Int arguments will return an Int, which may overflow or throw an error. Use the ZZRingElem versions if this is not the desired behavior.


  • factorial(n::ZZRingElem) -> ZZRingElem

Return the factorial of $n$, i.e. $n!$. An exception is raised if $n < 0$. We define $0! = 1$.

  • rising_factorial(x::Int, n::Int) -> Int
  • rising_factorial(x::ZZRingElem, n::Int) -> ZZRingElem
  • rising_factorial(x::ZZRingElem, n::ZZRingElem) -> ZZRingElem

Return $x(x + 1)(x + 2)\ldots(x + n - 1)$. An exception is raised if $n < 0$. We define rising_factorial(x, 0) to be $1$.

julia> factorial(ZZ(30))

julia> rising_factorial(ZZ(-30), 3)


  • primorial(n::Int) -> Int
  • primorial(n::ZZRingElem) -> ZZRingElem

Return the primorial $P(n)$, i.e. the product of all primes less than or equal to $n$. An exception is raised if $n < 0$. We define $P(0) = P(1) = 1$.

julia> primorial(ZZ(100))

Bell numbers

  • bell(n::Int) -> Int
  • bell(n::ZZRingElem) -> ZZRingElem

Return the $n$-th Bell number $B(n)$, i.e. the number of ways of partitioning a set of $n$ elements. An exception is raised if $n < 0$.

julia> bell(ZZ(20))

Binomial coefficients

  • binomial(n::ZZRingElem, k::ZZRingElem) -> ZZRingElem

Return the binomial coefficient $\frac{n (n-1) \cdots (n-k+1)}{k!}$ for $k \ge 0$ and returns 0 for k < 0.


Julia already defines the binomial function for Int, which throws an error on overflow.

julia> binomial(ZZ(72), ZZ(15))

Integer partitions

  • number_of_partitions(n::Int) -> ZZRingElem
  • number_of_partitions(n::ZZRingElem) -> ZZRingElem

Return the number of integer partitions $p(n)$ of $n$, i.e. the number of distinct ways to write $n$ as a sum of positive integers. Note that $p(0) = 1$, as the empty sum is counted. For $n < 0$ we return zero.

julia> number_of_partitions(ZZ(10^6))

Fibonacci sequence

  • fibonacci(n::Int) -> Int
  • fibonacci(n::ZZRingElem) -> ZZRingElem

Return the $n$-th Fibonacci number $F(n)$, defined by the recurrence relation $F(1) = 1$, $F(2) = 1$ and $F(n) = F(n - 1) + F(n - 2)$ for $n \geq 3$. We define $F(0) = 0$ and for $n > 0$ we have $F(-n) = (-1)^{n+1}F(n)$.

julia> fibonacci(ZZ(100))

julia> fibonacci(-2)

Number theoretic functionality


The functions in this section that take Int arguments will return a Int, which may overflow or throw an error. Use the ZZRingElem versions if this is not the desired behavior.

Moebius mu function

  • moebius_mu(n::Int) -> Int
  • moebius_mu(n::ZZRingElem) -> Int

Return the Moebius function $\mu(n)$, which is defined to be $0$ if $n$ is not squarefree and otherwise is defined to be $+1$ or $-1$ if $n$ has an even or odd number of prime factors, respectively. Alternatively, $\mu(n)$ can be defined to be the sum of the primitive $n$-th roots of unity. An exception is raised if $n \leq 0$.

julia> moebius_mu(30)

Jacobi symbols

  • jacobi_symbol(m::Int, n::Int) -> Int
  • jacobi_symbol(m::ZZRingElem, n::ZZRingElem) -> Int

Return the Jacobi symbol $\left(\frac{m}{n}\right)$, which is defined for integers $m$ and odd, positive integers $n$. If the factorisation of $n$ is $n = p_1^{i_1}p_2^{i_2}\ldots p_r^{i_r}$ then we define

\[\left(\frac{m}{n}\right) = \left(\frac{m}{p_1}\right)^{i_1}\left(\frac{m}{p_2}\right)^{i_2}\ldots \left(\frac{m}{p_r}\right)^{i_r}\]

where $\left(\frac{m}{p}\right)$ on the right hand side is the Legendre symbol, which is defined for an odd prime number $p$ to be $0$ if $p$ divides $m$ and otherwise $+1$ or $-1$ depending on whether $m$ is a square modulo $p$ or not. An exception is raised if $n$ is even or if $n \leq 0$.

julia> jacobi_symbol(3, 37)

Sigma function

  • divisor_sigma(m::Int, n::Int) -> Int
  • divisor_sigma(m::ZZRingElem, n::Int) -> ZZRingElem
  • divisor_sigma(m::ZZRingElem, n::ZZRingElem) -> ZZRingElem

Return the sum of the $n$-th powers of the divisors of $m$

\[\sigma(m, n) = \sum_{d\;|\;m} d^n.\]

If $m \leq 0$ or $n < 0$ we raise an exception.

julia> divisor_sigma(60, 5)

Euler totient function

  • euler_phi(n::Int) -> Int
  • euler_phi(n::ZZRingElem) -> ZZRingElem

Return the Euler totient function $\varphi(n)$, i.e. the number of positive integers $1 \leq x \leq n$ which are coprime to $n$. Note that $\varphi(1) = 1$. We raise an exception if $n \leq 0$.

julia> euler_phi(200)