diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml
index 9b51665..ead51db 100644
--- a/.github/workflows/benchmarks.yml
+++ b/.github/workflows/benchmarks.yml
@@ -11,6 +11,9 @@ on:
- 'src/main/**'
- 'src/jmh/**'
- 'build.gradle'
+ - 'gradle.properties'
+ - 'gradle.lockfile'
+ - 'settings.gradle'
- 'gradle/**'
- 'gradlew'
- 'gradlew.bat'
@@ -38,6 +41,14 @@ jobs:
- name: Make Gradle executable
run: chmod +x ./gradlew
+ - name: Verify reproducibility inputs
+ shell: bash
+ run: |
+ set -euo pipefail
+ test -f gradle.lockfile
+ test -f gradle.properties
+ test -f gradle/verification-metadata.xml
+
- name: Run JMH benchmarks
run: ./gradlew clean jmh --no-daemon
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 7d35cd0..db8bd6c 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -42,6 +42,14 @@ jobs:
- name: Set up Gradle caching and instrumentation
uses: gradle/actions/setup-gradle@v4
+ - name: Verify reproducibility inputs
+ shell: bash
+ run: |
+ set -euo pipefail
+ test -f gradle.lockfile
+ test -f gradle.properties
+ test -f gradle/verification-metadata.xml
+
- name: Execute build, tests, PMD, coverage, Javadoc, distribution packaging, and SBOM generation
run: ./gradlew --no-daemon clean build pmdMain javadoc jacocoTestReport distZip cyclonedxBom
@@ -140,6 +148,14 @@ jobs:
- name: Set up Gradle caching and instrumentation
uses: gradle/actions/setup-gradle@v4
+ - name: Verify reproducibility inputs
+ shell: bash
+ run: |
+ set -euo pipefail
+ test -f gradle.lockfile
+ test -f gradle.properties
+ test -f gradle/verification-metadata.xml
+
- name: Build release distribution and SBOM
run: ./gradlew --no-daemon clean build pmdMain javadoc jacocoTestReport distZip cyclonedxBom
diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml
index a3be564..f493e7d 100644
--- a/.github/workflows/pages.yml
+++ b/.github/workflows/pages.yml
@@ -9,6 +9,8 @@ on:
- 'src/test/**'
- 'src/jmh/**'
- 'build.gradle'
+ - 'gradle.properties'
+ - 'gradle.lockfile'
- 'settings.gradle'
- 'gradle/**'
- 'dependency-suppression.xml'
@@ -46,6 +48,14 @@ jobs:
- name: Set up Gradle caching and instrumentation
uses: gradle/actions/setup-gradle@v4
+
+ - name: Verify reproducibility inputs
+ shell: bash
+ run: |
+ set -euo pipefail
+ test -f gradle.lockfile
+ test -f gradle.properties
+ test -f gradle/verification-metadata.xml
- name: Build reports for publication
run: ./gradlew --no-daemon clean build pmdMain javadoc jacocoTestReport pitest jmh cyclonedxBom
diff --git a/build.gradle b/build.gradle
index 304f04d..994c7c7 100644
--- a/build.gradle
+++ b/build.gradle
@@ -41,8 +41,10 @@ tasks.withType(JavaCompile).configureEach {
options.release = 21
}
-repositories {
- mavenCentral()
+dependencyLocking {
+ lockAllConfigurations()
+
+ lockMode = LockMode.STRICT
}
dependencies {
@@ -94,10 +96,13 @@ dependencyCheck {
tasks.withType(Test).configureEach {
useJUnitPlatform()
- jvmArgs += "-javaagent:${configurations.mockitoAgent.singleFile}"
-
+
+ doFirst {
+ jvmArgs "-javaagent:${configurations.mockitoAgent.singleFile}"
+ }
+
finalizedBy(tasks.named('jacocoTestReport'))
-
+
reports {
junitXml.required = true
html.required = true
diff --git a/gradle.lockfile b/gradle.lockfile
new file mode 100644
index 0000000..11566a3
--- /dev/null
+++ b/gradle.lockfile
@@ -0,0 +1,47 @@
+# This is a Gradle generated file for dependency locking.
+# Manual edits can break the build and are not advised.
+# This file is expected to be part of source control.
+com.github.oowekyala.ooxml:nice-xml-messages:3.1=pmd
+com.google.code.gson:gson:2.13.2=pmd
+com.google.errorprone:error_prone_annotations:2.41.0=pmd
+net.bytebuddy:byte-buddy-agent:1.17.7=jmhRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
+net.bytebuddy:byte-buddy:1.17.7=jmhRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
+net.sf.jopt-simple:jopt-simple:5.0.4=jmh,jmhCompileClasspath,jmhRuntimeClasspath
+net.sf.saxon:Saxon-HE:12.9=pmd
+net.sourceforge.pmd:pmd-ant:7.20.0=pmd
+net.sourceforge.pmd:pmd-core:7.20.0=pmd
+net.sourceforge.pmd:pmd-java:7.20.0=pmd
+org.antlr:antlr4-runtime:4.9.3=pmd
+org.apache.commons:commons-lang3:3.20.0=pmd
+org.apache.commons:commons-math3:3.6.1=jmh,jmhCompileClasspath,jmhRuntimeClasspath
+org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath
+org.checkerframework:checker-qual:3.52.1=pmd
+org.jacoco:org.jacoco.agent:0.8.14=jacocoAgent,jacocoAnt
+org.jacoco:org.jacoco.ant:0.8.14=jacocoAnt
+org.jacoco:org.jacoco.core:0.8.14=jacocoAnt
+org.jacoco:org.jacoco.report:0.8.14=jacocoAnt
+org.junit.jupiter:junit-jupiter-api:5.14.3=jmhRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.junit.jupiter:junit-jupiter-engine:5.14.3=jmhRuntimeClasspath,testRuntimeClasspath
+org.junit.jupiter:junit-jupiter-params:5.14.3=jmhRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.junit.jupiter:junit-jupiter:5.14.3=jmhRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.junit.platform:junit-platform-commons:1.14.3=jmhRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.junit.platform:junit-platform-engine:1.14.3=jmhRuntimeClasspath,testRuntimeClasspath
+org.junit.platform:junit-platform-launcher:1.14.3=jmhRuntimeClasspath,testRuntimeClasspath
+org.junit:junit-bom:5.14.3=jmhRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.mockito:mockito-core:5.23.0=jmhRuntimeClasspath,mockitoAgent,testCompileClasspath,testRuntimeClasspath
+org.mockito:mockito-junit-jupiter:5.23.0=jmhRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.objenesis:objenesis:3.3=jmhRuntimeClasspath,testRuntimeClasspath
+org.openjdk.jmh:jmh-core:1.37=jmh,jmhCompileClasspath,jmhRuntimeClasspath
+org.openjdk.jmh:jmh-generator-asm:1.37=jmh,jmhCompileClasspath,jmhRuntimeClasspath
+org.openjdk.jmh:jmh-generator-bytecode:1.37=jmh,jmhCompileClasspath,jmhRuntimeClasspath
+org.openjdk.jmh:jmh-generator-reflection:1.37=jmh,jmhCompileClasspath,jmhRuntimeClasspath
+org.opentest4j:opentest4j:1.3.0=jmhRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.ow2.asm:asm-commons:9.9=jacocoAnt
+org.ow2.asm:asm-tree:9.9=jacocoAnt
+org.ow2.asm:asm:9.0=jmh,jmhCompileClasspath,jmhRuntimeClasspath
+org.ow2.asm:asm:9.9=jacocoAnt
+org.ow2.asm:asm:9.9.1=pmd
+org.pcollections:pcollections:4.0.2=pmd
+org.slf4j:jul-to-slf4j:1.7.36=pmd
+org.xmlresolver:xmlresolver:5.3.3=pmd
+empty=annotationProcessor,compileClasspath,cyclonedxBom,jmhAnnotationProcessor,mainPmdAuxClasspath,runtimeClasspath,testAnnotationProcessor
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 0000000..a47b4eb
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1 @@
+org.gradle.dependency.verification=strict
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index b8ce83a..7ec0bc2 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -1,3 +1,23 @@
+#
+# After changing dependency versions:
+#
+# run:
+# ./gradlew --write-locks classes testClasses jmh distZip cyclonedxBom
+#
+# if needed, refresh verification metadata:
+# ./gradlew --write-verification-metadata sha256 test jmh distZip cyclonedxBom
+#
+# (optional - for Eclipse IDE)
+# insert trusted-artifacts into gradle/verification-metadata.xml/verification-metadata/configuration:
+#
+#
+#
+#
+#
+# commit:
+# gradle.lockfile
+# gradle/verification-metadata.xml
+#
[versions]
junit = "5.14.3"
mockito = "5.23.0"
@@ -9,4 +29,3 @@ junit-platform-launcher = { module = "org.junit.platform:junit-platform-launcher
mockito-core = { module = "org.mockito:mockito-core", version.ref = "mockito" }
mockito-junit-jupiter = { module = "org.mockito:mockito-junit-jupiter", version.ref = "mockito" }
-
diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml
new file mode 100644
index 0000000..dd76ef0
--- /dev/null
+++ b/gradle/verification-metadata.xml
@@ -0,0 +1,1704 @@
+
+
+
+ true
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/settings-gradle.lockfile b/settings-gradle.lockfile
new file mode 100644
index 0000000..709a43f
--- /dev/null
+++ b/settings-gradle.lockfile
@@ -0,0 +1,4 @@
+# This is a Gradle generated file for dependency locking.
+# Manual edits can break the build and are not advised.
+# This file is expected to be part of source control.
+empty=incomingCatalogForLibs0
diff --git a/settings.gradle b/settings.gradle
index f9e6be3..c3d5c2a 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1 +1,8 @@
rootProject.name = 'Radixor'
+
+dependencyResolutionManagement {
+ repositoriesMode = RepositoriesMode.FAIL_ON_PROJECT_REPOS
+ repositories {
+ mavenCentral()
+ }
+}