Skip to content

Commit 1edd903

Browse files
authored
Merge branch 'master' into mschauer-stirling
2 parents 55725ed + e005ee0 commit 1edd903

22 files changed

+1305
-479
lines changed

.github/dependabot.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
2+
version: 2
3+
updates:
4+
- package-ecosystem: "github-actions"
5+
directory: "/" # Location of package manifests
6+
schedule:
7+
interval: "weekly"

.github/workflows/CI.yml

Lines changed: 66 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,90 @@
11
name: CI
2+
23
on:
34
pull_request:
45
push:
56
branches:
67
- master
78
- main
8-
tags: '*'
9+
tags: ['*']
10+
pull_request:
11+
workflow_dispatch:
12+
13+
concurrency:
14+
# Skip intermediate builds: always.
15+
# Cancel intermediate builds: only if it is a pull request build.
16+
group: ${{ github.workflow }}-${{ github.ref }}
17+
cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }}
18+
919
jobs:
1020
test:
11-
name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }}
21+
name: Julia ${{ matrix.version }} - ${{ matrix.os }}
1222
runs-on: ${{ matrix.os }}
23+
timeout-minutes: 60
24+
permissions: # needed to allow julia-actions/cache to proactively delete old caches that it has created
25+
actions: write
26+
contents: read
1327
strategy:
1428
fail-fast: false
1529
matrix:
1630
version:
17-
- '1.0'
18-
- '1.6'
19-
# - 'nightly'
31+
- '1.0' # compat
32+
- 'lts'
33+
- 'nightly'
2034
os:
2135
- ubuntu-latest
22-
- macOS-latest
36+
- macOS-latest # Apple silicon
2337
- windows-latest
24-
arch:
25-
- x64
38+
exclude:
39+
# Apple Silicon need julia >= v1.8
40+
- os: macos-latest
41+
version: '1.0'
42+
include:
43+
- os: macos-13 # Intel
44+
version: '1.0'
2645
steps:
27-
- uses: actions/checkout@v2
28-
- uses: julia-actions/setup-julia@v1
46+
- uses: actions/checkout@v4
47+
- uses: julia-actions/setup-julia@v2
2948
with:
3049
version: ${{ matrix.version }}
31-
arch: ${{ matrix.arch }}
32-
- uses: actions/cache@v1
33-
env:
34-
cache-name: cache-artifacts
35-
with:
36-
path: ~/.julia/artifacts
37-
key: ${{ runner.os }}-test-${{ env.cache-name }}-${{ hashFiles('**/Project.toml') }}
38-
restore-keys: |
39-
${{ runner.os }}-test-${{ env.cache-name }}-
40-
${{ runner.os }}-test-
41-
${{ runner.os }}-
50+
- uses: julia-actions/cache@v2
4251
- uses: julia-actions/julia-buildpkg@v1
4352
- uses: julia-actions/julia-runtest@v1
4453
- uses: julia-actions/julia-processcoverage@v1
45-
- uses: codecov/codecov-action@v1
54+
- uses: codecov/codecov-action@v5
55+
with:
56+
token: ${{ secrets.CODECOV_TOKEN }} # required
57+
fail_ci_if_error: true
58+
files: lcov.info
59+
60+
docs:
61+
name: Documentation
62+
runs-on: ubuntu-latest
63+
permissions:
64+
actions: write # needed to allow julia-actions/cache to proactively delete old caches that it has created
65+
contents: write
66+
statuses: write
67+
steps:
68+
- uses: actions/checkout@v4
69+
- uses: julia-actions/setup-julia@v2
4670
with:
47-
file: lcov.info
71+
version: '1'
72+
show-versioninfo: true
73+
- uses: julia-actions/cache@v2
74+
- name: Configure doc environment
75+
shell: julia --project=docs --color=yes {0}
76+
run: |
77+
using Pkg
78+
Pkg.develop(PackageSpec(path=pwd()))
79+
Pkg.instantiate()
80+
- uses: julia-actions/julia-buildpkg@v1
81+
- name: Run doctests
82+
shell: julia --project=docs --color=yes {0}
83+
run: |
84+
using Documenter: DocMeta, doctest
85+
using Combinatorics
86+
DocMeta.setdocmeta!(Combinatorics, :DocTestSetup, :(using Combinatorics); recursive=true)
87+
doctest(Combinatorics)
88+
- uses: julia-actions/julia-docdeploy@v1
89+
env:
90+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.github/workflows/CompatHelper.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
name: CompatHelper
2+
3+
on:
4+
schedule:
5+
- cron: 0 0 * * *
6+
workflow_dispatch:
7+
8+
jobs:
9+
CompatHelper:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- name: Pkg.add("CompatHelper")
13+
run: julia -e 'using Pkg; Pkg.add("CompatHelper")'
14+
- name: CompatHelper.main()
15+
env:
16+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
17+
COMPATHELPER_PRIV: ${{ secrets.DOCUMENTER_KEY }}
18+
run: julia -e 'using CompatHelper; CompatHelper.main()'

.github/workflows/TagBot.yml

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,32 @@
11
name: TagBot
22
on:
3-
schedule:
4-
- cron: 0 * * * *
3+
issue_comment:
4+
types:
5+
- created
6+
workflow_dispatch:
7+
inputs:
8+
lookback:
9+
default: 3
10+
# Workaround for https://github.com/JuliaRegistries/TagBot/issues/388
11+
# permissions:
12+
# actions: read
13+
# checks: read
14+
# contents: write
15+
# deployments: read
16+
# issues: read
17+
# discussions: read
18+
# packages: read
19+
# pages: read
20+
# pull-requests: read
21+
# repository-projects: read
22+
# security-events: read
23+
# statuses: read
524
jobs:
625
TagBot:
26+
if: github.event_name == 'workflow_dispatch' || github.actor == 'JuliaTagBot'
727
runs-on: ubuntu-latest
828
steps:
929
- uses: JuliaRegistries/TagBot@v1
1030
with:
1131
token: ${{ secrets.GITHUB_TOKEN }}
32+
ssh: ${{ secrets.DOCUMENTER_KEY }}

.github/workflows/docsCI.yml

Lines changed: 0 additions & 63 deletions
This file was deleted.

Project.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
name = "Combinatorics"
22
uuid = "861a8166-3701-5b0c-9a16-15d98fcdc6aa"
3-
version = "1.0.2"
3+
version = "1.0.3"
44

55
[compat]
66
julia = "1"
77

88
[extras]
99
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
10+
OffsetArrays = "6fe1bfb0-de20-5000-8ca7-80f57d26f881"
1011
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
1112

1213
[targets]
13-
test = ["LinearAlgebra", "Test"]
14+
test = ["LinearAlgebra", "OffsetArrays", "Test"]

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
# Combinatorics
22

3-
[![Build Status](https://travis-ci.org/JuliaMath/Combinatorics.jl.svg?branch=master)](https://travis-ci.org/JuliaMath/Combinatorics.jl)
43
[![Dev](https://img.shields.io/badge/docs-dev-blue.svg)](http://juliamath.github.io/Combinatorics.jl/)
4+
[![deps](https://juliahub.com/docs/General/Combinatorics/stable/deps.svg)](https://juliahub.com/ui/Packages/General/Combinatorics?t=2)
5+
[![CI](https://github.com/JuliaMath/Combinatorics.jl/actions/workflows/CI.yml/badge.svg)](https://github.com/JuliaMath/Combinatorics.jl/actions/workflows/CI.yml)
56
[![Coverage Status](https://coveralls.io/repos/github/JuliaMath/Combinatorics.jl/badge.svg?branch=master)](https://coveralls.io/github/JuliaMath/Combinatorics.jl?branch=master)
6-
[![Codecov](https://codecov.io/gh/JuliaMath/Combinatorics.jl/branch/master/graph/badge.svg)](https://codecov.io/gh/JuliaMath/Combinatorics.jl)
7+
[![codecov](https://codecov.io/gh/JuliaMath/Combinatorics.jl/graph/badge.svg?token=ov65P61lvZ)](https://codecov.io/gh/JuliaMath/Combinatorics.jl)
78

89
A combinatorics library for Julia, focusing mostly (as of now) on enumerative
910
combinatorics and permutations. As overflows are expected even for low values,

docs/src/README_old.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# Combinatorics
22

3-
[![Build Status](https://travis-ci.org/JuliaMath/Combinatorics.jl.svg?branch=master)](https://travis-ci.org/JuliaMath/Combinatorics.jl)
3+
[![CI](https://github.com/JuliaMath/Combinatorics.jl/actions/workflows/CI.yml/badge.svg)](https://github.com/JuliaMath/Combinatorics.jl/actions/workflows/CI.yml)
44
[![Coverage Status](https://coveralls.io/repos/github/JuliaMath/Combinatorics.jl/badge.svg?branch=master)](https://coveralls.io/github/JuliaMath/Combinatorics.jl?branch=master)
5-
[![Codecov](https://codecov.io/gh/JuliaMath/Combinatorics.jl/branch/master/graph/badge.svg)](https://codecov.io/gh/JuliaMath/Combinatorics.jl)
5+
[![codecov](https://codecov.io/gh/JuliaMath/Combinatorics.jl/graph/badge.svg?token=ov65P61lvZ)](https://codecov.io/gh/JuliaMath/Combinatorics.jl)
66

77
A combinatorics library for Julia, focusing mostly (as of now) on enumerative
88
combinatorics and permutations. As overflows are expected even for low values,

src/combinations.jl

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ struct Combinations
1010
t::Int
1111
end
1212

13-
function Base.iterate(c::Combinations, s = [min(c.t - 1, i) for i in 1:c.t])
13+
@inline function Base.iterate(c::Combinations, s = [min(c.t - 1, i) for i in 1:c.t])
1414
if c.t == 0 # special case to generate 1 result for t==0
1515
isempty(s) && return (s, [1])
1616
return
@@ -56,9 +56,7 @@ end
5656
Generate combinations of the elements of `a` of all orders. Chaining of order iterators
5757
is eager, but the sequence at each order is lazy.
5858
"""
59-
combinations(a) = Iterators.flatten([combinations(a, k) for k = 1:length(a)])
60-
61-
59+
combinations(a) = Iterators.flatten([combinations(a, k) for k = 0:length(a)])
6260

6361
# cool-lex combinations iterator
6462

src/factorials.jl

Lines changed: 59 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ export
88
doublefactorial,
99
hyperfactorial,
1010
multifactorial,
11-
gamma,
1211
primorial,
1312
multinomial
1413

@@ -29,6 +28,18 @@ function Base.factorial(n::T, k::T) where T<:Integer
2928
end
3029
return f
3130
end
31+
function Base.factorial(n::BigInt, k::BigInt)
32+
if k < 0 || n < 0 || k > n
33+
throw(DomainError((n, k), "n and k must be nonnegative with k ≤ n"))
34+
end
35+
f = BigInt(1)
36+
n = deepcopy(n) # avoid mutating input
37+
while n > k
38+
Base.GMP.MPZ.mul!(f, n)
39+
Base.GMP.MPZ.sub_ui!(n, 1)
40+
end
41+
return f
42+
end
3243
Base.factorial(n::Integer, k::Integer) = factorial(promote(n, k)...)
3344

3445

@@ -38,9 +49,23 @@ Base.factorial(n::Integer, k::Integer) = factorial(promote(n, k)...)
3849
Compute the number of permutations of `n` with no fixed points, also known as the
3950
subfactorial. An alias `subfactorial` for this function is provided for convenience.
4051
"""
41-
function derangement(sn::Integer)
42-
n = BigInt(sn)
43-
return numerator(factorial(n) * sum([(-1)^k // factorial(k) for k = 0:n]))
52+
function derangement(n::Integer)
53+
if n < 0
54+
throw(DomainError(n, "n must be nonnegative"))
55+
elseif n <= 1
56+
return BigInt(1-n)
57+
end
58+
d = BigInt(0)
59+
for i in 2:n
60+
# d = i * d + (iseven(i) ? 1 : -1)
61+
Base.GMP.MPZ.mul_ui!(d, i)
62+
if iseven(i)
63+
Base.GMP.MPZ.add_ui!(d, 1)
64+
else
65+
Base.GMP.MPZ.sub_ui!(d, 1)
66+
end
67+
end
68+
return d
4469
end
4570
const subfactorial = derangement
4671

@@ -95,14 +120,42 @@ end
95120
"""
96121
multinomial(k...)
97122
98-
Multinomial coefficient where `n = sum(k)`.
123+
Compute the multinomial coefficient
124+
``\\binom{n}{k_1,k_2,...,k_i} = \\frac{n!}{k_1!k_2! \\cdots k_i!}, n = \\sum{k_i}``.
125+
Throws an `OverflowError` when the input is too large.
126+
127+
See Also: `binomial`.
128+
129+
# Examples
130+
```jldoctest
131+
julia> # (x+y)^2 = x^2 + 2xy + y^2
132+
133+
julia> multinomial(2, 0)
134+
1
135+
136+
julia> multinomial(1, 1)
137+
2
138+
139+
julia> multinomial(0, 2)
140+
1
141+
142+
julia> multinomial(10, 10, 10, 10)
143+
ERROR: OverflowError: 5550996791340 * 847660528 overflowed for type Int64
144+
Stacktrace:
145+
[...]
146+
```
147+
148+
# External links
149+
- [Definitions](https://dlmf.nist.gov/26.4.2) on DLMF
150+
- [Multinomial theorem](https://en.wikipedia.org/wiki/Multinomial_theorem) on Wikipedia
99151
"""
100152
function multinomial(k...)
101153
s = 0
102154
result = 1
103155
@inbounds for i in k
104156
s += i
105-
result *= binomial(s, i)
157+
bi = binomial(s, i)
158+
result = Base.Checked.checked_mul(result, bi)
106159
end
107160
result
108161
end

0 commit comments

Comments
 (0)