mirror of
https://gitlab.uni-marburg.de/langbeid/powersort.git
synced 2025-04-01 07:26:01 +02:00
Merge remote-tracking branch 'origin/main'
This commit is contained in:
commit
f5c10d77c2
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,5 +1,6 @@
|
||||
/.idea
|
||||
/app/bin/
|
||||
/app/powersort-competition
|
||||
|
||||
# Ignore Gradle project-specific cache directory
|
||||
/.gradle
|
||||
@ -7,3 +8,4 @@
|
||||
# Ignore Gradle build output directory
|
||||
/build
|
||||
/app/build
|
||||
/app/gradle/wrapper/gradle-wrapper.properties
|
||||
|
@ -10,3 +10,16 @@ java:
|
||||
when: always
|
||||
reports:
|
||||
junit: app/build/test-results/test/**/TEST-*.xml
|
||||
expire_in: 6 month
|
||||
|
||||
jmh:
|
||||
image: alpine:latest
|
||||
stage: test
|
||||
script:
|
||||
- apk --no-cache add openjdk23 gradle --repository=https://dl-cdn.alpinelinux.org/alpine/edge/testing/
|
||||
- gradle jmh --no-daemon
|
||||
# https://docs.gitlab.com/ee/ci/jobs/job_artifacts.html
|
||||
artifacts:
|
||||
paths:
|
||||
- app/build/reports/jmh/*
|
||||
expire_in: 6 month
|
||||
|
8
.idea/.gitignore
generated
vendored
8
.idea/.gitignore
generated
vendored
@ -1,8 +0,0 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
15
.idea/compiler.xml
generated
15
.idea/compiler.xml
generated
@ -1,6 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<annotationProcessing>
|
||||
<profile default="true" name="Default" enabled="true" />
|
||||
<profile name="Gradle Imported" enabled="true">
|
||||
<outputRelativeToContentRoot value="true" />
|
||||
<processorPath useClasspath="false">
|
||||
<entry name="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjdk.jmh/jmh-generator-annprocess/1.37/da93888682df163144edf9b13d2b78e54166063a/jmh-generator-annprocess-1.37.jar" />
|
||||
<entry name="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjdk.jmh/jmh-core/1.37/896f27e49105b35ea1964319c83d12082e7a79ef/jmh-core-1.37.jar" />
|
||||
<entry name="$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.sf.jopt-simple/jopt-simple/5.0.4/4fdac2fbe92dfad86aa6e9301736f6b4342a3f5c/jopt-simple-5.0.4.jar" />
|
||||
<entry name="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-math3/3.6.1/e4ba98f1d4b3c80ec46392f25e094a6a2e58fcbf/commons-math3-3.6.1.jar" />
|
||||
</processorPath>
|
||||
<module name="powersort.app.jmh" />
|
||||
</profile>
|
||||
</annotationProcessing>
|
||||
<bytecodeTargetLevel target="23" />
|
||||
</component>
|
||||
</project>
|
||||
</project>
|
17
.idea/gradle.xml
generated
17
.idea/gradle.xml
generated
@ -1,17 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
<option value="$PROJECT_DIR$/app" />
|
||||
</set>
|
||||
</option>
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
20
.idea/jarRepositories.xml
generated
20
.idea/jarRepositories.xml
generated
@ -1,20 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RemoteRepositoriesConfiguration">
|
||||
<remote-repository>
|
||||
<option name="id" value="central" />
|
||||
<option name="name" value="Maven Central repository" />
|
||||
<option name="url" value="https://repo1.maven.org/maven2" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="jboss.community" />
|
||||
<option name="name" value="JBoss Community repository" />
|
||||
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="MavenRepo" />
|
||||
<option name="name" value="MavenRepo" />
|
||||
<option name="url" value="https://repo.maven.apache.org/maven2/" />
|
||||
</remote-repository>
|
||||
</component>
|
||||
</project>
|
7
.idea/misc.xml
generated
7
.idea/misc.xml
generated
@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="FrameworkDetectionExcludesConfiguration">
|
||||
<file type="web" url="file://$PROJECT_DIR$" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" project-jdk-name="23" project-jdk-type="JavaSDK" />
|
||||
</project>
|
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024 Daniel Langbein, Mohammed Omer, Finn Moltmann, Abhirami Siva, Ambili Jacob
|
||||
Copyright (c) 2024 Daniel Langbein <daniel@systemli.org>, Mohammed Omer, Finn Moltmann, Abhirami Siva, Ambili Jacob
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
102
README.md
102
README.md
@ -1,13 +1,33 @@
|
||||
# Bring Powersort to Java
|
||||
|
||||
## Gradle
|
||||
Implementation of Powersort with the aim of integrating it into OpenJDK. Benchmarks are used to ensure that the runtime of the new sorting algorithm is never slower than the current Timsort implementation.
|
||||
|
||||
### Setup
|
||||
<!-- TOC -->
|
||||
* [Bring Powersort to Java](#bring-powersort-to-java)
|
||||
* [Setup](#setup)
|
||||
* [Development Setup with nix](#development-setup-with-nix)
|
||||
* [Tasks](#tasks)
|
||||
* [Test Cases](#test-cases)
|
||||
* [Benchmark](#benchmark)
|
||||
* [Custom](#custom)
|
||||
* [JMH](#jmh)
|
||||
* [Run JMH with CGL and Powersort competition lists](#run-jmh-with-cgl-and-powersort-competition-lists)
|
||||
* [IntelliJ plugin](#intellij-plugin)
|
||||
* [Further notes](#further-notes)
|
||||
<!-- TOC -->
|
||||
|
||||
Dependencies (JDK23, Gradle, IntelliJ) can be installed by running `nix-shell`.
|
||||
## Setup
|
||||
|
||||
We use Gradle for dependency and build management.
|
||||
|
||||
**Commandline**
|
||||
|
||||
Update Gradle:
|
||||
|
||||
```shell
|
||||
./gradlew wrapper --gradle-version latest
|
||||
```
|
||||
|
||||
Check which Java toolchains Gradle finds:
|
||||
|
||||
```shell
|
||||
@ -26,18 +46,34 @@ This should include version >= 23, e.g.:
|
||||
| Detected by: Current JVM
|
||||
```
|
||||
|
||||
### Development Setup with nix
|
||||
|
||||
The provided [./shell.nix](./shell.nix) file can be used to set up a development environment with IntelliJ. The only prerequisite is to have a working `nix` installation for which there are multiple possibilities, e.g:
|
||||
|
||||
```shell
|
||||
# https://github.com/DeterminateSystems/nix-installer?tab=readme-ov-file#determinate-nix-installer
|
||||
curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | \
|
||||
sh -s -- install
|
||||
```
|
||||
|
||||
Then the dependencies (JDK23, Gradle, IntelliJ) can be installed with:
|
||||
|
||||
```shell
|
||||
nix-shell
|
||||
```
|
||||
|
||||
**IntelliJ**
|
||||
|
||||
When using `nix-shell`, some configuration is necessary:
|
||||
When using `nix-shell`, some manual configuration is necessary:
|
||||
|
||||
- STRG ALT SHIFT s -> SDK -> add JDK ->
|
||||
- home path: /usr/lib/openjdk
|
||||
- name: usr-lib-jdk
|
||||
- STRG ALT s -> build tools -> gradle ->
|
||||
- local installation: /usr/lib/gradle
|
||||
- Gradle JVM: usr-lib-jdk
|
||||
- `STRG` `ALT` `SHIFT` `s` -> SDK -> add JDK ->
|
||||
- home path: `/usr/lib/openjdk`
|
||||
- name: `usr-lib-jdk`
|
||||
- `STRG` `ALT` `s` -> build tools -> gradle ->
|
||||
- local installation: `/usr/lib/gradle`
|
||||
- Gradle JVM: `usr-lib-jdk`
|
||||
|
||||
### Tasks
|
||||
## Tasks
|
||||
|
||||
List all Gradle tasks which can be run:
|
||||
|
||||
@ -49,10 +85,52 @@ This can also be done graphically in IntelliJ:
|
||||
|
||||

|
||||
|
||||
### Test Cases
|
||||
## Test Cases
|
||||
|
||||
Run the task "test":
|
||||
|
||||
```shell
|
||||
./gradlew test
|
||||
```
|
||||
|
||||
## Benchmark
|
||||
|
||||
There are two different benchmarks. One is based on the JMH benchmark framework, the other one is a custom benchmark implementation.
|
||||
|
||||
### Custom
|
||||
|
||||
Run Custom Benchmark (CGM) with
|
||||
|
||||
- Custom Generated Lists (CGL):
|
||||
|
||||
```shell
|
||||
./gradlew runCbmCgl
|
||||
```
|
||||
|
||||
- Powersort competition lists:
|
||||
|
||||
```shell
|
||||
./gradlew runCbmCompetition
|
||||
```
|
||||
|
||||
### JMH
|
||||
|
||||
#### Run JMH with CGL and Powersort competition lists
|
||||
|
||||
```shell
|
||||
./gradlew jmh --rerun
|
||||
```
|
||||
|
||||
- To benchmark only one of the different list collections, see `jmh { excludes }` at the bottom of [./app/build.gradle.kts](./app/build.gradle.kts).
|
||||
|
||||
#### IntelliJ plugin
|
||||
|
||||
The [JMH Java Microbenchmark Harness](https://plugins.jetbrains.com/plugin/7529-jmh-java-microbenchmark-harness) plugin allows running benchmarks from within the IDE in a similar way as JUnit test cases. This can be used for development but yields less accurate results.
|
||||
|
||||

|
||||
|
||||
#### Further notes
|
||||
|
||||
- JHM: https://github.com/openjdk/jmh?tab=readme-ov-file#other-build-systems
|
||||
- We use the JMH Gradle plugin: https://github.com/melix/jmh-gradle-plugin
|
||||
- Sample JMH Gradle project: https://github.com/twoVersatile/jmhsample
|
||||
|
@ -6,7 +6,9 @@
|
||||
plugins {
|
||||
// Apply the application plugin to add support for building a CLI application in Java.
|
||||
application
|
||||
|
||||
// JMH Gradle Plugin
|
||||
// https://github.com/melix/jmh-gradle-plugin/blob/master/README.adoc#usage
|
||||
id("me.champeau.jmh") version "0.7.2"
|
||||
}
|
||||
|
||||
@ -15,14 +17,22 @@ repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
val latestJmhVersion = "1.37"
|
||||
val mockitoAgent = configurations.create("mockitoAgent")
|
||||
|
||||
dependencies {
|
||||
// Use JUnit Jupiter for testing.
|
||||
testImplementation(libs.junit.jupiter)
|
||||
|
||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||
|
||||
// This dependency is used by the application.
|
||||
//implementation(libs.guava)
|
||||
// Required to use the IntelliJ plugin "JMH Java Microbenchmark Harness".
|
||||
// https://stackoverflow.com/a/71286156/6334421
|
||||
jmhAnnotationProcessor("org.openjdk.jmh:jmh-generator-annprocess:$latestJmhVersion")
|
||||
|
||||
// Use Mockito for mocking.
|
||||
// https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#mockito-instrumentation
|
||||
testImplementation(libs.mockito)
|
||||
mockitoAgent(libs.mockito) { isTransitive = false }
|
||||
}
|
||||
|
||||
// Apply a specific Java toolchain to ease working on different environments.
|
||||
@ -37,24 +47,64 @@ java {
|
||||
// It is not possible to rename it.
|
||||
// Thus, we remove it and add two custom named `JavaExec` tasks instead.
|
||||
tasks.getByName("run") {
|
||||
enabled = false
|
||||
description = "This task has been disabled. It does nothing o.O"
|
||||
enabled = false
|
||||
}
|
||||
|
||||
tasks.register<JavaExec>("runCustomBenchmark") {
|
||||
tasks.register<JavaExec>("runCbmCgl") {
|
||||
description = "Run Custom Benchmark (CBM) with Custom Generated Lists (CGL)"
|
||||
group = "application"
|
||||
classpath = sourceSets["main"].runtimeClasspath
|
||||
mainClass = "de.uni_marburg.powersort.benchmark.Main"
|
||||
mainClass = "de.uni_marburg.powersort.benchmark.CbmCgl"
|
||||
// Results in `-Xmx4g` being passed to JVM
|
||||
maxHeapSize = "4g"
|
||||
}
|
||||
tasks.register<JavaExec>("runCbmCompetition") {
|
||||
description = "Run Custom Benchmark (CBM) with Powersort competition lists"
|
||||
group = "application"
|
||||
classpath = sourceSets["main"].runtimeClasspath
|
||||
mainClass = "de.uni_marburg.powersort.benchmark.CbmCompetition"
|
||||
}
|
||||
|
||||
tasks.named<Test>("test") {
|
||||
// Use JUnit Platform for unit tests.
|
||||
useJUnitPlatform()
|
||||
tasks {
|
||||
test {
|
||||
// Use JUnit Platform for unit tests.
|
||||
useJUnitPlatform()
|
||||
|
||||
reports {
|
||||
// XML reports for GitLab CI.
|
||||
junitXml.required.set(true)
|
||||
// HTML reports to view with a webbrowser.
|
||||
html.required.set(true)
|
||||
reports {
|
||||
// XML reports for GitLab CI.
|
||||
junitXml.required.set(true)
|
||||
// HTML reports to view with a webbrowser.
|
||||
html.required.set(true)
|
||||
}
|
||||
|
||||
// Mockito JDK21+ config
|
||||
// https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#mockito-instrumentation
|
||||
jvmArgs("-javaagent:${mockitoAgent.asPath}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/melix/jmh-gradle-plugin/blob/master/README.adoc#configuration-options
|
||||
jmh {
|
||||
// Use latest JMH version (1.37, https://github.com/openjdk/jmh/tags)
|
||||
// instead of default (1.36, https://github.com/melix/jmh-gradle-plugin/blob/dfdbb0cd3a363e2dbbcaab93bb1be32178f4cae4/src/main/java/me/champeau/jmh/DefaultsConfigurer.java#L24)
|
||||
jmhVersion = latestJmhVersion // Specifies JMH version
|
||||
|
||||
// Force GC (garbage collection) between iterations.
|
||||
//
|
||||
// Forcing the GC may help to lower the noise in GC-heavy benchmarks, at the expense of jeopardizing GC ergonomics decisions. Use with care.
|
||||
// https://github.com/openjdk/jmh/blob/1be779e0b4af174b67abf8080d1b76dda570d88d/jmh-core/src/main/java/org/openjdk/jmh/runner/options/CommandLineOptions.java#L148-L152
|
||||
forceGC = true
|
||||
|
||||
// If human output is saved, it won't be written to stdout while running the benchmark!
|
||||
humanOutputFile = project.file("${project.layout.buildDirectory.get()}/reports/jmh/human.txt")
|
||||
|
||||
resultsFile = project.file("${project.layout.buildDirectory.get()}/reports/jmh/results.csv")
|
||||
resultFormat = "CSV"
|
||||
|
||||
excludes = listOf(
|
||||
// To skip JmhCgl or JmhCompetition, uncomment it below.
|
||||
// "de.uni_marburg.powersort.benchmark.JmhCgl.benchmark",
|
||||
"de.uni_marburg.powersort.benchmark.JmhCompetition.benchmark",
|
||||
)
|
||||
}
|
||||
|
@ -0,0 +1,98 @@
|
||||
package de.uni_marburg.powersort.benchmark;
|
||||
|
||||
import de.uni_marburg.powersort.data.DataEnum;
|
||||
import de.uni_marburg.powersort.data.ObjectSupplier;
|
||||
import de.uni_marburg.powersort.sort.SortEnum;
|
||||
import de.uni_marburg.powersort.sort.SortImpl;
|
||||
import org.openjdk.jmh.annotations.BenchmarkMode;
|
||||
import org.openjdk.jmh.annotations.Fork;
|
||||
import org.openjdk.jmh.annotations.Level;
|
||||
import org.openjdk.jmh.annotations.Measurement;
|
||||
import org.openjdk.jmh.annotations.Mode;
|
||||
import org.openjdk.jmh.annotations.OutputTimeUnit;
|
||||
import org.openjdk.jmh.annotations.Scope;
|
||||
import org.openjdk.jmh.annotations.Setup;
|
||||
import org.openjdk.jmh.annotations.State;
|
||||
import org.openjdk.jmh.annotations.Threads;
|
||||
import org.openjdk.jmh.annotations.Timeout;
|
||||
import org.openjdk.jmh.annotations.Warmup;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/*
|
||||
* Benchmark parameters
|
||||
*/
|
||||
|
||||
@Fork(value = 1, jvmArgsAppend = {"-Xms4g", "-Xmx4g"})
|
||||
@Threads(1)
|
||||
@Timeout(time = 10, timeUnit = TimeUnit.SECONDS)
|
||||
@OutputTimeUnit(TimeUnit.MILLISECONDS)
|
||||
/*
|
||||
* Benchmark mode and related parameters
|
||||
*/
|
||||
|
||||
// AverageTime: "Average time per operation."
|
||||
// - "This mode is time-based, and it will run until the iteration time expires."
|
||||
//@BenchmarkMode(Mode.AverageTime)
|
||||
//@Warmup(iterations = 20, time = 1, timeUnit = TimeUnit.SECONDS)
|
||||
//@Measurement(iterations = 6, time = 1, timeUnit = TimeUnit.SECONDS)
|
||||
|
||||
// SingleShotTime: "Time per single operation"
|
||||
// - "More warmup/measurement iterations are generally required."
|
||||
// - "Timers overhead might be significant if benchmarks are small;"
|
||||
@BenchmarkMode(Mode.SingleShotTime)
|
||||
// During warmup iterations for ASCENDING_RUNS (with TIM_SORT and FASTER_FINN_SORT):
|
||||
// - Until the 17th spike of up to +750% (Maybe JVM optimizations happening?)
|
||||
// - After 40th constant slowdown of around +10% (Maybe CPU frequency adjustments?)
|
||||
// Thus, we need at least ~50 warmup iterations!
|
||||
@Warmup(iterations = 60)
|
||||
@Measurement(iterations = 6)
|
||||
|
||||
/*
|
||||
* State parameters
|
||||
*
|
||||
*/
|
||||
|
||||
// "State objects naturally encapsulate the state on which benchmark is working on."
|
||||
@State(Scope.Benchmark)
|
||||
|
||||
public class JmhBase {
|
||||
DataEnum getDataEnum(){
|
||||
return null;
|
||||
}
|
||||
SortEnum getSortEnum(){
|
||||
return null;
|
||||
}
|
||||
|
||||
private ObjectSupplier data = null;
|
||||
Object[] workingCopy;
|
||||
SortImpl sortImpl;
|
||||
|
||||
// TODO: This is inaccurate. How to create and use separate arrays for each warmup x iteration x sortAlgorithm ?
|
||||
@Setup(Level.Invocation)
|
||||
public void setup() {
|
||||
sortImpl = getSortEnum().getSortImpl();
|
||||
|
||||
if (Filter.isFiltered(getDataEnum(), getSortEnum())) {
|
||||
// This combination of DataEnum and SortEnum should be skipped.
|
||||
// We can't tell JMH to not run the benchmark at all.
|
||||
// Instead, we let it sort an invalid list which results in an exception.
|
||||
// This way the filtered combinations are skipped quickly
|
||||
// and not included in the benchmark results.
|
||||
data = null;
|
||||
workingCopy = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// A new JmhRunner object is created for each @Param variation.
|
||||
// Then, `data` is `null` again.
|
||||
if (data == null) {
|
||||
data = getDataEnum().getObjectSupplier();
|
||||
}
|
||||
// For all warmup and measurement iterations of one @Param variation, the JmhRunner object is reused.
|
||||
// Thus, we can't just sort `data` directly.
|
||||
// Instead, we have to create a copy of it on which the sort algorithm can work.
|
||||
// This way, all iterations sort the same input.
|
||||
workingCopy = data.getCopy();
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
package de.uni_marburg.powersort.benchmark;
|
||||
|
||||
import de.uni_marburg.powersort.data.CglEnum;
|
||||
import de.uni_marburg.powersort.data.DataEnum;
|
||||
import de.uni_marburg.powersort.sort.SortEnum;
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.Param;
|
||||
import org.openjdk.jmh.annotations.Scope;
|
||||
import org.openjdk.jmh.annotations.State;
|
||||
|
||||
/*
|
||||
* Benchmark state parameters
|
||||
*
|
||||
* Quote from JMH:
|
||||
* State objects naturally encapsulate the state on which benchmark is working on.
|
||||
*/
|
||||
@State(Scope.Benchmark)
|
||||
public class JmhCgl extends JmhBase {
|
||||
// Either all or a selection of input lists.
|
||||
//@Param()
|
||||
@Param({
|
||||
//"RANDOM_INTEGERS",
|
||||
"ASCENDING_RUNS", "ASCENDING_RUNS_WITH_OVERLAP",
|
||||
"MANY_ASCENDING_RUNS", "MANY_ASCENDING_RUNS_WITH_OVERLAP"
|
||||
})
|
||||
CglEnum dataEnum;
|
||||
|
||||
// Either all or a selection of sort implementations.
|
||||
//@Param()
|
||||
@Param({
|
||||
"TIM_SORT",
|
||||
"FASTER_FINN_SORT",
|
||||
//"IMPL_M_40",
|
||||
"IMPL_M_50",
|
||||
})
|
||||
SortEnum sortEnum;
|
||||
|
||||
@Override
|
||||
DataEnum getDataEnum(){
|
||||
return dataEnum;
|
||||
}
|
||||
@Override
|
||||
SortEnum getSortEnum(){
|
||||
return sortEnum;
|
||||
}
|
||||
|
||||
// We could move this method up to JmhBase to avoid redundancy in the subclasses.
|
||||
// But this would remove the run button of the IntelliJ plugin "JMH Java Microbenchmark Harness".
|
||||
// Furthermore, with this approach we don't need to exclude JmhBase as it has no benchmark methods.
|
||||
@Benchmark
|
||||
public void benchmark() {
|
||||
sortImpl.sort(workingCopy);
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
package de.uni_marburg.powersort.benchmark;
|
||||
|
||||
import de.uni_marburg.powersort.data.CompetitionEnum;
|
||||
import de.uni_marburg.powersort.data.DataEnum;
|
||||
import de.uni_marburg.powersort.sort.SortEnum;
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.Param;
|
||||
import org.openjdk.jmh.annotations.Scope;
|
||||
import org.openjdk.jmh.annotations.State;
|
||||
|
||||
/*
|
||||
* Benchmark state parameters
|
||||
*
|
||||
* Quote from JMH:
|
||||
* State objects naturally encapsulate the state on which benchmark is working on.
|
||||
*/
|
||||
@State(Scope.Benchmark)
|
||||
public class JmhCompetition extends JmhBase {
|
||||
//@Param()
|
||||
@Param({
|
||||
// Top 4 Heavyweight by #comparisons
|
||||
"COMPETITION_207", "COMPETITION_214", "COMPETITION_213", "COMPETITION_236",
|
||||
// Top 4 Heavyweight by #merge-cost
|
||||
"COMPETITION_198","COMPETITION_199","COMPETITION_232","COMPETITION_231",
|
||||
// Top 4 Heavyweight by combined metric
|
||||
"COMPETITION_214","COMPETITION_218","COMPETITION_236","COMPETITION_213",
|
||||
})
|
||||
CompetitionEnum dataEnum;
|
||||
//@Param()
|
||||
@Param({"TIM_SORT", "FASTER_FINN_SORT", "IMPL_M_40"})
|
||||
SortEnum sortEnum;
|
||||
|
||||
@Override
|
||||
DataEnum getDataEnum(){
|
||||
return dataEnum;
|
||||
}
|
||||
@Override
|
||||
SortEnum getSortEnum(){
|
||||
return sortEnum;
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void benchmark() {
|
||||
sortImpl.sort(workingCopy);
|
||||
}
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
package de.uni_marburg.powersort.benchmark;
|
||||
|
||||
import de.uni_marburg.powersort.data.DataEnum;
|
||||
import de.uni_marburg.powersort.data.ObjectSupplier;
|
||||
import de.uni_marburg.powersort.sort.SortEnum;
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.BenchmarkMode;
|
||||
import org.openjdk.jmh.annotations.Fork;
|
||||
import org.openjdk.jmh.annotations.Level;
|
||||
import org.openjdk.jmh.annotations.Measurement;
|
||||
import org.openjdk.jmh.annotations.Mode;
|
||||
import org.openjdk.jmh.annotations.OutputTimeUnit;
|
||||
import org.openjdk.jmh.annotations.Param;
|
||||
import org.openjdk.jmh.annotations.Scope;
|
||||
import org.openjdk.jmh.annotations.Setup;
|
||||
import org.openjdk.jmh.annotations.State;
|
||||
import org.openjdk.jmh.annotations.Warmup;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
// TODO: The parameters are way too low. Use for debugging only!
|
||||
/*
|
||||
* Benchmark parameters
|
||||
*/
|
||||
@Fork(0)
|
||||
@Warmup(iterations = 0)
|
||||
@Measurement(iterations = 1)
|
||||
@BenchmarkMode(Mode.AverageTime)
|
||||
@OutputTimeUnit(TimeUnit.MILLISECONDS)
|
||||
/*
|
||||
* Benchmark state parameters
|
||||
*
|
||||
* Quote from JMH:
|
||||
* State objects naturally encapsulate the state on which benchmark is working on.
|
||||
*/
|
||||
@State(Scope.Benchmark)
|
||||
public class MainJmh {
|
||||
@Param()
|
||||
private DataEnum dataEnum;
|
||||
@Param()
|
||||
private SortEnum sortEnum;
|
||||
|
||||
private ObjectSupplier data;
|
||||
/* package-protected */ Object[] workingCopy;
|
||||
|
||||
// TODO: This is inaccurate. How to create and use separate arrays for each warmup x iteration x sortAlgorithm ?
|
||||
@Setup(Level.Invocation)
|
||||
public void setup() {
|
||||
// A new MainJmh object is created for each @Param variation.
|
||||
// Then, `data` is `null` again.
|
||||
if (data == null) {
|
||||
data = dataEnum.getObjectSupplier();
|
||||
}
|
||||
// For all warmup and measurement iterations of one @Param variation, the MainJmh object is reused.
|
||||
// Thus, we can't just sort `data` directly.
|
||||
// Instead, we have to create a copy of it on which the sort algorithm can work.
|
||||
// This way, all iterations sort the same input.
|
||||
workingCopy = data.getCopy();
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void benchmark() {
|
||||
sortEnum.getSortImpl().sort(workingCopy);
|
||||
}
|
||||
}
|
@ -26,8 +26,11 @@ package de.uni_marburg.powersort.FinnSort;
|
||||
*/
|
||||
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
|
||||
import static java.lang.Math.pow;
|
||||
|
||||
/**
|
||||
* A stable, adaptive, iterative mergesort that requires far fewer than
|
||||
* n lg(n) comparisons when running on partially sorted arrays, while
|
||||
@ -61,30 +64,31 @@ import java.util.Comparator;
|
||||
*
|
||||
* @author Josh Bloch
|
||||
*/
|
||||
class FasterFinnSort<T> {
|
||||
public class FasterFinnSort<T> {
|
||||
/**
|
||||
* This is the minimum sized sequence that will be merged. Shorter
|
||||
* sequences will be lengthened by calling binarySort. If the entire
|
||||
* array is less than this length, no merges will be performed.
|
||||
*
|
||||
* <p>
|
||||
* This constant should be a power of two. It was 64 in Tim Peter's C
|
||||
* implementation, but 32 was empirically determined to work better in
|
||||
* this implementation. In the unlikely event that you set this constant
|
||||
* to be a number that's not a power of two, you'll need to change the
|
||||
* {@link #minRunLength} computation.
|
||||
*
|
||||
* <p>
|
||||
* If you decrease this constant, you must change the stackLen
|
||||
* computation in the TimSort constructor, or you risk an
|
||||
* ArrayOutOfBounds exception. See listsort.txt for a discussion
|
||||
* of the minimum stack length required as a function of the length
|
||||
* of the array being sorted and the minimum merge sequence length.
|
||||
*/
|
||||
private static final int MIN_MERGE = 32;
|
||||
static final int MIN_MERGE = 32;
|
||||
|
||||
/**
|
||||
* The array being sorted.
|
||||
*/
|
||||
private final T[] a;
|
||||
private final int rangeSize;
|
||||
|
||||
/**
|
||||
* The comparator for this sort.
|
||||
@ -95,7 +99,7 @@ class FasterFinnSort<T> {
|
||||
* When we get into galloping mode, we stay there until both runs win less
|
||||
* often than MIN_GALLOP consecutive times.
|
||||
*/
|
||||
private static final int MIN_GALLOP = 7;
|
||||
private static final int MIN_GALLOP = 7;
|
||||
|
||||
/**
|
||||
* This controls when we get *into* galloping mode. It is initialized
|
||||
@ -126,66 +130,52 @@ class FasterFinnSort<T> {
|
||||
* A stack of pending runs yet to be merged. Run i starts at
|
||||
* address base[i] and extends for len[i] elements. It's always
|
||||
* true (so long as the indices are in bounds) that:
|
||||
*
|
||||
* runBase[i] + runLen[i] == runBase[i + 1]
|
||||
*
|
||||
* <p>
|
||||
* runBase[i] + runLen[i] == runBase[i + 1]
|
||||
* <p>
|
||||
* so we could cut the storage for this, but it's a minor amount,
|
||||
* and keeping all the info explicit simplifies the code.
|
||||
*/
|
||||
private int stackSize = 0; // Number of pending runs on stack
|
||||
int stackSize = 0; // Number of pending runs on stack
|
||||
private final int[] runBase;
|
||||
private final int[] runLen;
|
||||
private final int[] runPower;
|
||||
|
||||
/**
|
||||
* Creates a TimSort instance to maintain the state of an ongoing sort.
|
||||
*
|
||||
* @param a the array to be sorted
|
||||
* @param c the comparator to determine the order of the sort
|
||||
* @param work a workspace array (slice)
|
||||
* @param a the array to be sorted
|
||||
* @param c the comparator to determine the order of the sort
|
||||
* @param work a workspace array (slice)
|
||||
* @param workBase origin of usable space in work array
|
||||
* @param workLen usable size of work array
|
||||
* @param workLen usable size of work array
|
||||
*/
|
||||
private FasterFinnSort(T[] a, Comparator<? super T> c, T[] work, int workBase, int workLen) {
|
||||
FasterFinnSort(T[] a, Comparator<? super T> c, T[] work, int workBase, int workLen, int rangeSize) {
|
||||
this.a = a;
|
||||
this.c = c;
|
||||
|
||||
this.rangeSize = rangeSize;
|
||||
// Allocate temp storage (which may be increased later if necessary)
|
||||
int len = a.length;
|
||||
int tlen = (len < 2 * INITIAL_TMP_STORAGE_LENGTH) ?
|
||||
len >>> 1 : INITIAL_TMP_STORAGE_LENGTH;
|
||||
if (work == null || workLen < tlen || workBase + tlen > work.length) {
|
||||
@SuppressWarnings({"unchecked", "UnnecessaryLocalVariable"})
|
||||
T[] newArray = (T[])java.lang.reflect.Array.newInstance
|
||||
T[] newArray = (T[]) java.lang.reflect.Array.newInstance
|
||||
(a.getClass().getComponentType(), tlen);
|
||||
tmp = newArray;
|
||||
tmpBase = 0;
|
||||
tmpLen = tlen;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
tmp = work;
|
||||
tmpBase = workBase;
|
||||
tmpLen = workLen;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate runs-to-be-merged stack (which cannot be expanded). The
|
||||
* stack length requirements are described in listsort.txt. The C
|
||||
* version always uses the same stack length (85), but this was
|
||||
* measured to be too expensive when sorting "mid-sized" arrays (e.g.,
|
||||
* 100 elements) in Java. Therefore, we use smaller (but sufficiently
|
||||
* large) stack lengths for smaller arrays. The "magic numbers" in the
|
||||
* computation below must be changed if MIN_MERGE is decreased. See
|
||||
* the MIN_MERGE declaration above for more information.
|
||||
* The maximum value of 49 allows for an array up to length
|
||||
* Integer.MAX_VALUE-4, if array is filled by the worst case stack size
|
||||
* increasing scenario. More explanations are given in section 4 of:
|
||||
* http://envisage-project.eu/wp-content/uploads/2015/02/sorting.pdf
|
||||
*/
|
||||
int stackLen = (len < 120 ? 5 :
|
||||
len < 1542 ? 10 :
|
||||
len < 119151 ? 24 : 49);
|
||||
// TODO: Verify if this is correct
|
||||
int stackLen = ((int) Math.ceil(Math.log(rangeSize) / Math.log(2))) + 2;
|
||||
runBase = new int[stackLen];
|
||||
runLen = new int[stackLen];
|
||||
runPower = new int[stackLen];
|
||||
}
|
||||
|
||||
/*
|
||||
@ -200,24 +190,24 @@ class FasterFinnSort<T> {
|
||||
* any necessary array bounds checks and expanding parameters into
|
||||
* the required forms.
|
||||
*
|
||||
* @param a the array to be sorted
|
||||
* @param lo the index of the first element, inclusive, to be sorted
|
||||
* @param hi the index of the last element, exclusive, to be sorted
|
||||
* @param c the comparator to use
|
||||
* @param work a workspace array (slice)
|
||||
* @param a the array to be sorted
|
||||
* @param lo the index of the first element, inclusive, to be sorted
|
||||
* @param hi the index of the last element, exclusive, to be sorted
|
||||
* @param c the comparator to use
|
||||
* @param work a workspace array (slice)
|
||||
* @param workBase origin of usable space in work array
|
||||
* @param workLen usable size of work array
|
||||
* @param workLen usable size of work array
|
||||
* @since 1.8
|
||||
*/
|
||||
static <T> void sort(T[] a, int lo, int hi, Comparator<? super T> c,
|
||||
T[] work, int workBase, int workLen) {
|
||||
public static <T> void sort(T[] a, int lo, int hi, Comparator<? super T> c,
|
||||
T[] work, int workBase, int workLen) {
|
||||
assert c != null && a != null && lo >= 0 && lo <= hi && hi <= a.length;
|
||||
|
||||
int nRemaining = hi - lo;
|
||||
int nRemaining = hi - lo;
|
||||
if (nRemaining < 2)
|
||||
return; // Arrays of size 0 and 1 are always sorted
|
||||
|
||||
// If array is small, do a "mini-TimSort" with no merges
|
||||
|
||||
if (nRemaining < MIN_MERGE) {
|
||||
int initRunLen = countRunAndMakeAscending(a, lo, hi, c);
|
||||
binarySort(a, lo, hi, lo + initRunLen, c);
|
||||
@ -229,8 +219,8 @@ class FasterFinnSort<T> {
|
||||
* extending short natural runs to minRun elements, and merging runs
|
||||
* to maintain stack invariant.
|
||||
*/
|
||||
FasterFinnSort<T> ts = new FasterFinnSort<>(a, c, work, workBase, workLen);
|
||||
int minRun = minRunLength(nRemaining);
|
||||
FasterFinnSort<T> fs = new FasterFinnSort<>(a, c, work, workBase, workLen, hi - lo);
|
||||
int minRun = fs.minRunLength(nRemaining);
|
||||
do {
|
||||
// Identify next run
|
||||
int runLen = countRunAndMakeAscending(a, lo, hi, c);
|
||||
@ -243,8 +233,10 @@ class FasterFinnSort<T> {
|
||||
}
|
||||
|
||||
// Push run onto pending-run stack, and maybe merge
|
||||
ts.pushRun(lo, runLen);
|
||||
ts.mergeCollapse();
|
||||
|
||||
int p = fs.power(fs.stackSize, runLen);
|
||||
fs.mergeCollapse(p);
|
||||
fs.pushRun(lo, runLen, p);
|
||||
|
||||
// Advance to find next run
|
||||
lo += runLen;
|
||||
@ -253,8 +245,8 @@ class FasterFinnSort<T> {
|
||||
|
||||
// Merge all remaining runs to complete sort
|
||||
assert lo == hi;
|
||||
ts.mergeForceCollapse();
|
||||
assert ts.stackSize == 1;
|
||||
fs.mergeForceCollapse();
|
||||
assert fs.stackSize == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -262,26 +254,26 @@ class FasterFinnSort<T> {
|
||||
* insertion sort. This is the best method for sorting small numbers
|
||||
* of elements. It requires O(n log n) compares, but O(n^2) data
|
||||
* movement (worst case).
|
||||
*
|
||||
* <p>
|
||||
* If the initial part of the specified range is already sorted,
|
||||
* this method can take advantage of it: the method assumes that the
|
||||
* elements from index {@code lo}, inclusive, to {@code start},
|
||||
* exclusive are already sorted.
|
||||
*
|
||||
* @param a the array in which a range is to be sorted
|
||||
* @param lo the index of the first element in the range to be sorted
|
||||
* @param hi the index after the last element in the range to be sorted
|
||||
* @param a the array in which a range is to be sorted
|
||||
* @param lo the index of the first element in the range to be sorted
|
||||
* @param hi the index after the last element in the range to be sorted
|
||||
* @param start the index of the first element in the range that is
|
||||
* not already known to be sorted ({@code lo <= start <= hi})
|
||||
* @param c comparator to used for the sort
|
||||
* not already known to be sorted ({@code lo <= start <= hi})
|
||||
* @param c comparator to used for the sort
|
||||
*/
|
||||
@SuppressWarnings("fallthrough")
|
||||
private static <T> void binarySort(T[] a, int lo, int hi, int start,
|
||||
static <T> void binarySort(T[] a, int lo, int hi, int start,
|
||||
Comparator<? super T> c) {
|
||||
assert lo <= start && start <= hi;
|
||||
if (start == lo)
|
||||
start++;
|
||||
for ( ; start < hi; start++) {
|
||||
for (; start < hi; start++) {
|
||||
T pivot = a[start];
|
||||
|
||||
// Set left (and right) to the index where a[start] (pivot) belongs
|
||||
@ -325,28 +317,28 @@ class FasterFinnSort<T> {
|
||||
* Returns the length of the run beginning at the specified position in
|
||||
* the specified array and reverses the run if it is descending (ensuring
|
||||
* that the run will always be ascending when the method returns).
|
||||
*
|
||||
* <p>
|
||||
* A run is the longest ascending sequence with:
|
||||
*
|
||||
* a[lo] <= a[lo + 1] <= a[lo + 2] <= ...
|
||||
*
|
||||
* <p>
|
||||
* a[lo] <= a[lo + 1] <= a[lo + 2] <= ...
|
||||
* <p>
|
||||
* or the longest descending sequence with:
|
||||
*
|
||||
* a[lo] > a[lo + 1] > a[lo + 2] > ...
|
||||
*
|
||||
* <p>
|
||||
* a[lo] > a[lo + 1] > a[lo + 2] > ...
|
||||
* <p>
|
||||
* For its intended use in a stable mergesort, the strictness of the
|
||||
* definition of "descending" is needed so that the call can safely
|
||||
* reverse a descending sequence without violating stability.
|
||||
*
|
||||
* @param a the array in which a run is to be counted and possibly reversed
|
||||
* @param a the array in which a run is to be counted and possibly reversed
|
||||
* @param lo index of the first element in the run
|
||||
* @param hi index after the last element that may be contained in the run.
|
||||
* It is required that {@code lo < hi}.
|
||||
* @param c the comparator to used for the sort
|
||||
* @return the length of the run beginning at the specified position in
|
||||
* the specified array
|
||||
* It is required that {@code lo < hi}.
|
||||
* @param c the comparator to used for the sort
|
||||
* @return the length of the run beginning at the specified position in
|
||||
* the specified array
|
||||
*/
|
||||
private static <T> int countRunAndMakeAscending(T[] a, int lo, int hi,
|
||||
static <T> int countRunAndMakeAscending(T[] a, int lo, int hi,
|
||||
Comparator<? super T> c) {
|
||||
assert lo < hi;
|
||||
int runHi = lo + 1;
|
||||
@ -369,7 +361,7 @@ class FasterFinnSort<T> {
|
||||
/**
|
||||
* Reverse the specified range of the specified array.
|
||||
*
|
||||
* @param a the array in which a range is to be reversed
|
||||
* @param a the array in which a range is to be reversed
|
||||
* @param lo the index of the first element in the range to be reversed
|
||||
* @param hi the index after the last element in the range to be reversed
|
||||
*/
|
||||
@ -386,20 +378,20 @@ class FasterFinnSort<T> {
|
||||
* Returns the minimum acceptable run length for an array of the specified
|
||||
* length. Natural runs shorter than this will be extended with
|
||||
* {@link #binarySort}.
|
||||
*
|
||||
* <p>
|
||||
* Roughly speaking, the computation is:
|
||||
*
|
||||
* If n < MIN_MERGE, return n (it's too small to bother with fancy stuff).
|
||||
* Else if n is an exact power of 2, return MIN_MERGE/2.
|
||||
* Else return an int k, MIN_MERGE/2 <= k <= MIN_MERGE, such that n/k
|
||||
* is close to, but strictly less than, an exact power of 2.
|
||||
*
|
||||
* <p>
|
||||
* If n < MIN_MERGE, return n (it's too small to bother with fancy stuff).
|
||||
* Else if n is an exact power of 2, return MIN_MERGE/2.
|
||||
* Else return an int k, MIN_MERGE/2 <= k <= MIN_MERGE, such that n/k
|
||||
* is close to, but strictly less than, an exact power of 2.
|
||||
* <p>
|
||||
* For the rationale, see listsort.txt.
|
||||
*
|
||||
* @param n the length of the array to be sorted
|
||||
* @return the length of the minimum run to be merged
|
||||
*/
|
||||
private static int minRunLength(int n) {
|
||||
int minRunLength(int n) {
|
||||
assert n >= 0;
|
||||
int r = 0; // Becomes 1 if any 1 bits are shifted off
|
||||
while (n >= MIN_MERGE) {
|
||||
@ -415,41 +407,115 @@ class FasterFinnSort<T> {
|
||||
* @param runBase index of the first element in the run
|
||||
* @param runLen the number of elements in the run
|
||||
*/
|
||||
private void pushRun(int runBase, int runLen) {
|
||||
void pushRun(int runBase, int runLen, int power) {
|
||||
this.runBase[stackSize] = runBase;
|
||||
this.runLen[stackSize] = runLen;
|
||||
this.runPower[stackSize] = power;
|
||||
stackSize++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Examines the stack of runs waiting to be merged and merges adjacent runs
|
||||
* until the stack invariants are reestablished:
|
||||
*
|
||||
* 1. runLen[i - 3] > runLen[i - 2] + runLen[i - 1]
|
||||
* 2. runLen[i - 2] > runLen[i - 1]
|
||||
*
|
||||
* <p>
|
||||
* 1. runLen[i - 3] > runLen[i - 2] + runLen[i - 1]
|
||||
* 2. runLen[i - 2] > runLen[i - 1]
|
||||
* <p>
|
||||
* This method is called each time a new run is pushed onto the stack,
|
||||
* so the invariants are guaranteed to hold for i < stackSize upon
|
||||
* entry to the method.
|
||||
*
|
||||
* <p>
|
||||
* Thanks to Stijn de Gouw, Jurriaan Rot, Frank S. de Boer,
|
||||
* Richard Bubel and Reiner Hahnle, this is fixed with respect to
|
||||
* the analysis in "On the Worst-Case Complexity of TimSort" by
|
||||
* Nicolas Auger, Vincent Jug, Cyril Nicaud, and Carine Pivoteau.
|
||||
*/
|
||||
private void mergeCollapse() {
|
||||
void mergeCollapse(int power) {
|
||||
while (stackSize > 1) {
|
||||
int n = stackSize - 2;
|
||||
if (n > 0 && runLen[n-1] <= runLen[n] + runLen[n+1] ||
|
||||
n > 1 && runLen[n-2] <= runLen[n] + runLen[n-1]) {
|
||||
if (runLen[n - 1] < runLen[n + 1])
|
||||
n--;
|
||||
} else if (n < 0 || runLen[n] > runLen[n + 1]) {
|
||||
if (power < runPower[stackSize - 1]) {
|
||||
mergeAt(stackSize - 2);
|
||||
} else {
|
||||
break; // Invariant is established
|
||||
}
|
||||
mergeAt(n);
|
||||
}
|
||||
}
|
||||
/*
|
||||
private int power(int stackSize) {
|
||||
if (stackSize == 0) {
|
||||
return 0;
|
||||
}
|
||||
// int = (right - left + 1); = RangeSize
|
||||
long l = runLen[stackSize - 1]; // + (long) runBase[stackSize]; // - ((long) left << 1); // 2*middleA
|
||||
long r = runLen[stackSize]; // - ((long) left << 1); // 2*middleB
|
||||
int a = (int) ((l << 30) / rangeSize); // middleA / 2n
|
||||
int b = (int) ((r << 30) / rangeSize); // middleB / 2n
|
||||
return Integer.numberOfLeadingZeros(a ^ b);
|
||||
}
|
||||
*/
|
||||
int power(int stackSize, int runLen) {
|
||||
/*
|
||||
System.out.println(Arrays.toString(runBase));
|
||||
System.out.println(Arrays.toString(runLen));
|
||||
System.out.println(Arrays.toString(runPower));
|
||||
System.out.println(stackSize);
|
||||
System.out.println(rangeSize);
|
||||
System.out.println();
|
||||
*/
|
||||
if (stackSize == 0)
|
||||
return 0;
|
||||
|
||||
int n_1 = this.runLen[stackSize - 1];
|
||||
int n_2 = runLen;
|
||||
|
||||
int a = 2 * this.runBase[stackSize - 1] + n_1;
|
||||
int b = a + n_1 + n_2;
|
||||
|
||||
int result = 0;
|
||||
|
||||
while (true) {
|
||||
++result;
|
||||
if (a >= rangeSize) {
|
||||
a -= rangeSize;
|
||||
b -= rangeSize;
|
||||
}
|
||||
if (b >= rangeSize) {
|
||||
break;
|
||||
}
|
||||
a <<= 1;
|
||||
b <<= 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
public int power(int stackSize) {
|
||||
|
||||
System.out.println(Arrays.toString(runBase));
|
||||
System.out.println(Arrays.toString(runLen));
|
||||
System.out.println(Arrays.toString(runPower));
|
||||
System.out.println(stackSize);
|
||||
System.out.println(rangeSize);
|
||||
System.out.println();
|
||||
|
||||
|
||||
if (stackSize == 0)
|
||||
return 0;
|
||||
|
||||
int n_1 = this.runLen[stackSize - 1];
|
||||
int n_2 = this.runLen[stackSize];
|
||||
|
||||
double a = ((double) this.runBase[stackSize - 1] + 0.5d * n_1 - 1d) / this.rangeSize;
|
||||
double b = ((double) this.runBase[stackSize] + 0.5d * n_2 - 1d) / this.rangeSize;
|
||||
int l = 0;
|
||||
while ((int) (a * pow(2, l)) == (int) (b * pow(2 ,l))) {
|
||||
|
||||
l++;
|
||||
}
|
||||
return l;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
Backup mergeCollapse() von TimSort:
|
||||
|
||||
@ -473,7 +539,7 @@ class FasterFinnSort<T> {
|
||||
* Merges all runs on the stack until only one remains. This method is
|
||||
* called once, to complete the sort.
|
||||
*/
|
||||
private void mergeForceCollapse() {
|
||||
void mergeForceCollapse() {
|
||||
while (stackSize > 1) {
|
||||
int n = stackSize - 2;
|
||||
if (n > 0 && runLen[n - 1] < runLen[n + 1])
|
||||
@ -492,7 +558,7 @@ class FasterFinnSort<T> {
|
||||
private void mergeAt(int i) {
|
||||
assert stackSize >= 2;
|
||||
assert i >= 0;
|
||||
assert i == stackSize - 2 || i == stackSize - 3;
|
||||
//assert i == stackSize - 3;
|
||||
|
||||
int base1 = runBase[i];
|
||||
int len1 = runLen[i];
|
||||
@ -506,12 +572,8 @@ class FasterFinnSort<T> {
|
||||
* run now, also slide over the last run (which isn't involved
|
||||
* in this merge). The current run (i+1) goes away in any case.
|
||||
*/
|
||||
runLen[i] = len1 + len2;
|
||||
if (i == stackSize - 3) {
|
||||
runBase[i + 1] = runBase[i + 2];
|
||||
runLen[i + 1] = runLen[i + 2];
|
||||
}
|
||||
stackSize--;
|
||||
runLen[i] = len1 + len2;
|
||||
|
||||
/*
|
||||
* Find where the first element of run2 goes in run1. Prior elements
|
||||
|
@ -15,7 +15,7 @@ public class FinnSort {
|
||||
int i = 0;
|
||||
int j = extendRunRight(a, i, c);
|
||||
|
||||
printList(a, c);
|
||||
//printList(a, c);
|
||||
|
||||
runs.add(new Run(i, j, 0));
|
||||
|
||||
@ -63,17 +63,59 @@ public class FinnSort {
|
||||
System.arraycopy(merge.toArray(), 0, a, min(r1.start, r2.start), merge.size());
|
||||
Run r = new Run(min(r1.start, r2.start), max(r1.end, r2.end), min(r1.power, r2.power));
|
||||
runs.add(r);
|
||||
printList(a, c);
|
||||
//printList(a, c);
|
||||
}
|
||||
|
||||
private static <T> void merge(T[] a, Run r1, Run r2, Comparator<? super T> c) {
|
||||
T[] left = java.util.Arrays.copyOfRange(a, r1.start, r1.end);
|
||||
int l = 0;
|
||||
int r = r2.start;
|
||||
int k = r1.start;
|
||||
|
||||
while (l < left.length && r < r2.end) {
|
||||
if (c.compare(left[l], a[r]) <= 0) {
|
||||
a[k++] = left[l++];
|
||||
} else {
|
||||
a[k++] = a[r++];
|
||||
}
|
||||
}
|
||||
|
||||
while (l < left.length) {
|
||||
a[k++] = left[l++];
|
||||
}
|
||||
}
|
||||
|
||||
private static <T> int extendRunRight(T[] a, int i, Comparator<? super T> c) {
|
||||
if (i >= a.length - 1)
|
||||
return i + 1;
|
||||
|
||||
|
||||
int j = i + 1;
|
||||
while (j < a.length && c.compare(a[j-1], a[j]) <= 0) {
|
||||
j++;
|
||||
if (c.compare(a[i], a[j]) < 0) { //
|
||||
while (j < a.length && c.compare(a[j - 1], a[j]) <= 0) {
|
||||
j++;
|
||||
}
|
||||
} else {
|
||||
while (j < a.length && c.compare(a[j - 1], a[j]) >= 0) {
|
||||
j++;
|
||||
}
|
||||
makeAscending(a, i, j);
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
private static <T> void makeAscending(T[] a, int i, int j) {
|
||||
for (int p = 0; p < (j - i) / 2; p++) {
|
||||
swap(a, i + p ,j - p - 1);
|
||||
}
|
||||
}
|
||||
|
||||
private static <T> void swap(T[] a, int i, int j) {
|
||||
T temp = a[i];
|
||||
a[i] = a[j];
|
||||
a[j] = temp;
|
||||
}
|
||||
|
||||
private static int power(Run run1, Run run2, int n) {
|
||||
if (run1.start == 0) {
|
||||
return 0;
|
||||
@ -89,7 +131,7 @@ public class FinnSort {
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
/*
|
||||
public static void printRuns() {
|
||||
String s = "";
|
||||
for (Run run : runs) {
|
||||
@ -111,4 +153,6 @@ public class FinnSort {
|
||||
}
|
||||
System.out.println(s);
|
||||
}
|
||||
|
||||
*/
|
||||
}
|
@ -1,14 +1,16 @@
|
||||
package de.uni_marburg.powersort.FinnSort;
|
||||
|
||||
class Run {
|
||||
int start;
|
||||
int end;
|
||||
public class Run {
|
||||
public int start;
|
||||
public int end;
|
||||
int power;
|
||||
public int len;
|
||||
|
||||
public Run(int i, int j, int p) {
|
||||
start = i;
|
||||
end = j;
|
||||
power = p;
|
||||
len = end - start;
|
||||
}
|
||||
|
||||
}
|
||||
|
271
app/src/main/java/de/uni_marburg/powersort/MSort/IMPL_M_1.java
Normal file
271
app/src/main/java/de/uni_marburg/powersort/MSort/IMPL_M_1.java
Normal file
@ -0,0 +1,271 @@
|
||||
package de.uni_marburg.powersort.MSort;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
public class IMPL_M_1 {
|
||||
|
||||
private IMPL_M_1() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts the given range, using the given workspace array slice
|
||||
* for temp storage when possible. This method is designed to be
|
||||
* invoked from public methods (in class Arrays) after performing
|
||||
* any necessary array bounds checks and expanding parameters into
|
||||
* the required forms.
|
||||
*
|
||||
* @param a the array to be sorted
|
||||
* @param lo the index of the first element, inclusive, to be sorted
|
||||
* @param hi the index of the last element, exclusive, to be sorted
|
||||
* @param c the comparator to use
|
||||
* @param work a workspace array (slice)
|
||||
* @param workBase origin of usable space in work array
|
||||
* @param workLen usable size of work array
|
||||
* @since 1.8
|
||||
*/
|
||||
protected static int MERGE_COST = 0;
|
||||
|
||||
public static void fillWithAscRunsHighToLow(Integer[] A, int[] runLengths, int runLenFactor) {
|
||||
//A has a fixed size, but it doesn't have any meaningful values
|
||||
int n = A.length;
|
||||
//This ensures that the sum of runLengths multiplied by runLenFactor equals the list size n. If not, an AssertionError is thrown.
|
||||
assert IntStream.of(runLengths).sum() * runLenFactor == n;
|
||||
|
||||
//System.out.println("IntStream Of run length output: "+IntStream.of(runLengths).sum());
|
||||
//IntStream.of(runLengths).forEach(System.out::println);
|
||||
for (int i = 0; i < n; i++) {
|
||||
//putting i in set a, while a is always the last index of n
|
||||
A[i] = n - i;
|
||||
}
|
||||
|
||||
|
||||
//For each value l in the array runLengths, do the following
|
||||
// runLengths = {2, 3, 5}, the loop will run three times, with l taking values 2, 3, and 5 respectively.
|
||||
int startIndex = 0;
|
||||
for (int l : runLengths) {
|
||||
int L = l * runLenFactor;
|
||||
// Sort the subarray from startIndex to startIndex+L manually
|
||||
for (int i = startIndex; i < startIndex + L - 1; i++) {
|
||||
for (int j = startIndex; j < startIndex + L - 1 - (i - startIndex); j++) {
|
||||
if (A[j] > A[j + 1]) {
|
||||
// Swap elements
|
||||
int temp = A[j];
|
||||
A[j] = A[j + 1];
|
||||
A[j + 1] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
startIndex += L;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static <T> T[] merge(T[] run1, T[] run2, Comparator<? super T> c) {
|
||||
/**
|
||||
* We need this because:
|
||||
* In Java, you cannot create generic arrays directly (can't write new T[size])
|
||||
* The workaround is to create an Object array and cast it to T[]
|
||||
* This casting generates a warning because the compiler can't verify the type safety at runtime due to Java's type erasure
|
||||
* **/
|
||||
@SuppressWarnings("unchecked")
|
||||
T[] result = (T[]) new Object[run1.length + run2.length];
|
||||
int i = 0, j = 0, k = 0;
|
||||
|
||||
while (i < run1.length && j < run2.length) {
|
||||
//This comparison only works if the lists are sorted
|
||||
if (c.compare(run1[i], run2[j]) <= 0) {
|
||||
result[k++] = run1[i++];
|
||||
} else {
|
||||
result[k++] = run2[j++];
|
||||
}
|
||||
}
|
||||
// can be improved by finding out which one is empty and only add the other one
|
||||
// Copy remaining elements
|
||||
while (i < run1.length) {
|
||||
result[k++] = run1[i++];
|
||||
}
|
||||
while (j < run2.length) {
|
||||
result[k++] = run2[j++];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// New helper method to handle range reversal
|
||||
private static <T> void reverseRange(T[] a, int start, int end) {
|
||||
// end is exclusive, so we need to use end-1 as the right boundary
|
||||
int left = start;
|
||||
int right = end - 1;
|
||||
while (left < right) {
|
||||
T temp = a[left];
|
||||
a[left] = a[right];
|
||||
a[right] = temp;
|
||||
left++;
|
||||
right--;
|
||||
}
|
||||
}
|
||||
|
||||
static <T> void mergeInplace(T[] a, int i, int m, int j, Comparator<? super T> c) {
|
||||
// System.out.printf("Merge(%d, %d, %d)%n", i, m, j);
|
||||
MERGE_COST += j - i;
|
||||
// Create temporary arrays for merging
|
||||
@SuppressWarnings("unchecked")
|
||||
T[] left = (T[]) new Object[m - i];
|
||||
@SuppressWarnings("unchecked")
|
||||
T[] right = (T[]) new Object[j - m];
|
||||
|
||||
// Copy data to temporary arrays
|
||||
System.arraycopy(a, i, left, 0, m - i);
|
||||
System.arraycopy(a, m, right, 0, j - m);
|
||||
|
||||
// Merge back to original array
|
||||
// Merge the runs
|
||||
T[] merged = merge(left, right, c);
|
||||
|
||||
//copy back to original array
|
||||
System.arraycopy(merged, 0, a, i,merged.length);
|
||||
}
|
||||
|
||||
static <T> int extendRun(T [] a, int i, Comparator<? super T> c) {
|
||||
// if i was the element before end so just return the last element
|
||||
if (i == a.length - 1) {
|
||||
return i + 1;
|
||||
}
|
||||
//we're looking at the element next to a[i]
|
||||
int j = i + 1;
|
||||
if (c.compare(a[i],a[j]) <=0) {
|
||||
while (j < a.length && c.compare(a[j - 1], a[j]) <= 0) {
|
||||
j++;
|
||||
}
|
||||
} else {
|
||||
while (j < a.length && c.compare(a[j - 1],a[j]) > 0) {
|
||||
j++;
|
||||
}
|
||||
// Reverse the decreasing sequence
|
||||
reverseRange(a,i,j);
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
|
||||
public static int power(int[] run1, int[] run2, int n) {
|
||||
int i1 = run1[0], n1 = run1[1];
|
||||
int i2 = run2[0], n2 = run2[1];
|
||||
|
||||
assert i1 >= 0;
|
||||
assert i2 == i1 + n1;
|
||||
assert n1 >= 1 && n2 >= 1;
|
||||
assert i2 + n2 <= n;
|
||||
|
||||
double a = ((i1 + n1 / 2.0d) / n);
|
||||
double b = ((i2 + n2 / 2.0d) / n);
|
||||
|
||||
int l = 0;
|
||||
while (Math.floor(a * Math.pow(2, l)) == Math.floor(b * Math.pow(2, l))) {
|
||||
l++;
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
public static <T> void mergeTopmost2(T[] a, List<int[]> runs, Comparator<? super T> c) {
|
||||
assert runs.size() >= 2;
|
||||
|
||||
int[] Y = runs.get(runs.size() - 2);
|
||||
int[] Z = runs.getLast();
|
||||
|
||||
assert Z[0] == Y[0] + Y[1];
|
||||
|
||||
mergeInplace(a, Y[0], Z[0], Z[0] + Z[1], c);
|
||||
|
||||
runs.set(runs.size() - 2, new int[] {Y[0], Y[1] + Z[1], Y[2]});
|
||||
runs.removeLast();
|
||||
}
|
||||
|
||||
public static <T> void powerSort(T[] a, Comparator<? super T> c) {
|
||||
int n = a.length;
|
||||
int i = 0;
|
||||
List<int[]> runs = new ArrayList<>();
|
||||
|
||||
int j = extendRun(a, i, c);
|
||||
runs.add(new int[] {i, j - i, 0});
|
||||
i = j;
|
||||
|
||||
while (i < n) {
|
||||
j = extendRun(a, i, c);
|
||||
int p = power(runs.getLast(), new int[] {i, j - i}, n);
|
||||
|
||||
while (p <= runs.getLast()[2]) {
|
||||
mergeTopmost2(a, runs, c);
|
||||
}
|
||||
|
||||
runs.add(new int[] {i, j - i, p});
|
||||
i = j;
|
||||
}
|
||||
|
||||
while (runs.size() >= 2) {
|
||||
mergeTopmost2(a, runs, c);
|
||||
}
|
||||
}
|
||||
|
||||
public static int extendRunIncreasingOnly(List<Integer> a, int i) {
|
||||
if (i == a.size() - 1) {
|
||||
return i + 1;
|
||||
}
|
||||
int j = i + 1;
|
||||
while (j < a.size() && a.get(j - 1) <= a.get(j)) {
|
||||
j++;
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
public static int powerFast(int[] run1, int[] run2, int n) {
|
||||
int i1 = run1[0], n1 = run1[1];
|
||||
int i2 = run2[0], n2 = run2[1];
|
||||
|
||||
int a = 2 * i1 + n1;
|
||||
int b = a + n1 + n2;
|
||||
|
||||
int l = 0;
|
||||
while (true) {
|
||||
l++;
|
||||
if (a >= n) {
|
||||
assert b >= a;
|
||||
a -= n;
|
||||
b -= n;
|
||||
} else if (b >= n) {
|
||||
break;
|
||||
}
|
||||
assert a < b && b < n;
|
||||
a <<= 1;
|
||||
b <<= 1;
|
||||
}
|
||||
return l;
|
||||
}
|
||||
//
|
||||
// ,
|
||||
// new SortImpl("MSort") {
|
||||
// @Override
|
||||
// @SuppressWarnings("unchecked")
|
||||
// public void sort(Object[] a) {
|
||||
// // Create a list of type that matches powerSort's requirements
|
||||
// List<Comparable<Object>> list = new ArrayList<>();
|
||||
// for (Object obj : a) {
|
||||
// // Since we know the input will be Integer objects in the benchmark
|
||||
// list.add((Comparable<Object>) obj);
|
||||
// }
|
||||
//
|
||||
// // Call powerSort with the proper type
|
||||
// IMPL_M_1.<Comparable<Object>>powerSort(list);
|
||||
//
|
||||
// // Copy back to array
|
||||
// for (int i = 0; i < a.length; i++) {
|
||||
// a[i] = list.get(i);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
493
app/src/main/java/de/uni_marburg/powersort/MSort/IMPL_M_2.java
Normal file
493
app/src/main/java/de/uni_marburg/powersort/MSort/IMPL_M_2.java
Normal file
@ -0,0 +1,493 @@
|
||||
package de.uni_marburg.powersort.MSort;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
public class IMPL_M_2 {
|
||||
|
||||
private IMPL_M_2() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts the given range, using the given workspace array slice
|
||||
* for temp storage when possible. This method is designed to be
|
||||
* invoked from public methods (in class Arrays) after performing
|
||||
* any necessary array bounds checks and expanding parameters into
|
||||
* the required forms.
|
||||
*
|
||||
* @param a the array to be sorted
|
||||
* @param lo the index of the first element, inclusive, to be sorted
|
||||
* @param hi the index of the last element, exclusive, to be sorted
|
||||
* @param c the comparator to use
|
||||
* @param work a workspace array (slice)
|
||||
* @param workBase origin of usable space in work array
|
||||
* @param workLen usable size of work array
|
||||
* @since 1.8
|
||||
*/
|
||||
protected static int MERGE_COST = 0;
|
||||
|
||||
public static void fillWithAscRunsHighToLow(Integer[] A, int[] runLengths, int runLenFactor) {
|
||||
//A has a fixed size, but it doesn't have any meaningful values
|
||||
int n = A.length;
|
||||
//This ensures that the sum of runLengths multiplied by runLenFactor equals the list size n. If not, an AssertionError is thrown.
|
||||
assert IntStream.of(runLengths).sum() * runLenFactor == n;
|
||||
|
||||
//System.out.println("IntStream Of run length output: "+IntStream.of(runLengths).sum());
|
||||
//IntStream.of(runLengths).forEach(System.out::println);
|
||||
for (int i = 0; i < n; i++) {
|
||||
//putting i in set a, while a is always the last index of n
|
||||
A[i] = n - i;
|
||||
}
|
||||
|
||||
//For each value l in the array runLengths, do the following
|
||||
// runLengths = {2, 3, 5}, the loop will run three times, with l taking values 2, 3, and 5 respectively.
|
||||
int startIndex = 0;
|
||||
for (int l : runLengths) {
|
||||
int L = l * runLenFactor;
|
||||
// Sort the subarray from startIndex to startIndex+L manually
|
||||
for (int i = startIndex; i < startIndex + L - 1; i++) {
|
||||
for (int j = startIndex; j < startIndex + L - 1 - (i - startIndex); j++) {
|
||||
if (A[j] > A[j + 1]) {
|
||||
// Swap elements
|
||||
int temp = A[j];
|
||||
A[j] = A[j + 1];
|
||||
A[j + 1] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
startIndex += L;
|
||||
}
|
||||
}
|
||||
|
||||
static <T> void mergeWithGalloping(T[] a, int i, int m, int j, Comparator<? super T> c) {
|
||||
@SuppressWarnings("unchecked")
|
||||
T[] left = (T[]) new Object[m - i];
|
||||
@SuppressWarnings("unchecked")
|
||||
T[] right = (T[]) new Object[j - m];
|
||||
|
||||
System.arraycopy(a, i, left, 0, m - i);
|
||||
System.arraycopy(a, m, right, 0, j - m);
|
||||
|
||||
int k = i, l = 0, r = 0;
|
||||
int leftGallop = 1, rightGallop = 1;
|
||||
|
||||
while (l < left.length && r < right.length) {
|
||||
if (c.compare(left[l], right[r]) <= 0) {
|
||||
a[k++] = left[l++];
|
||||
rightGallop = 1;
|
||||
leftGallop--;
|
||||
} else {
|
||||
a[k++] = right[r++];
|
||||
leftGallop = 1;
|
||||
rightGallop--;
|
||||
}
|
||||
|
||||
// Enable galloping mode
|
||||
if (leftGallop <= 0 || rightGallop <= 0) {
|
||||
k += performGallopingMerge(a, left, right, k, l, r, c);
|
||||
leftGallop = rightGallop = 7;
|
||||
}
|
||||
}
|
||||
|
||||
// Copy remaining elements
|
||||
while (l < left.length) a[k++] = left[l++];
|
||||
while (r < right.length) a[k++] = right[r++];
|
||||
}
|
||||
|
||||
static <T> int performGallopingMerge(T[] a, T[] left, T[] right, int k, int l, int r, Comparator<? super T> c) {
|
||||
// Implement exponential search for faster merging
|
||||
int gallopAdded = 0;
|
||||
while (l < left.length && r < right.length) {
|
||||
int leftWins = exponentialBinarySearch(right, r, left[l], c);
|
||||
if (leftWins > 0) {
|
||||
System.arraycopy(left, l, a, k, leftWins);
|
||||
k += leftWins;
|
||||
l += leftWins;
|
||||
gallopAdded += leftWins;
|
||||
}
|
||||
|
||||
if (l < left.length) {
|
||||
int rightWins = exponentialBinarySearch(left, l, right[r], c);
|
||||
if (rightWins > 0) {
|
||||
System.arraycopy(right, r, a, k, rightWins);
|
||||
k += rightWins;
|
||||
r += rightWins;
|
||||
gallopAdded += rightWins;
|
||||
}
|
||||
}
|
||||
}
|
||||
return gallopAdded;
|
||||
}
|
||||
|
||||
static <T> int exponentialBinarySearch(T[] arr, int start, T key, Comparator<? super T> c) {
|
||||
// Implement exponential search with binary search for finding consecutive winning elements
|
||||
int index = start;
|
||||
int jump = 1;
|
||||
while (index + jump < arr.length && c.compare(arr[index + jump], key) <= 0) {
|
||||
index += jump;
|
||||
jump *= 2;
|
||||
}
|
||||
|
||||
int right = Math.min(index + jump, arr.length);
|
||||
return binarySearchRange(arr, start, right, key, c);
|
||||
}
|
||||
|
||||
static <T> int binarySearchRange(T[] arr, int left, int right, T key, Comparator<? super T> c) {
|
||||
while (left < right) {
|
||||
int mid = (left + right) >>> 1;
|
||||
if (c.compare(arr[mid], key) <= 0) {
|
||||
left = mid + 1;
|
||||
} else {
|
||||
right = mid;
|
||||
}
|
||||
}
|
||||
return left - left;
|
||||
}
|
||||
|
||||
static <T> T[] merge(T[] run1, T[] run2, Comparator<? super T> c) {
|
||||
/**
|
||||
* We need this because:
|
||||
* In Java, you cannot create generic arrays directly (can't write new T[size])
|
||||
* The workaround is to create an Object array and cast it to T[]
|
||||
* This casting generates a warning because the compiler can't verify the type safety at runtime due to Java's type erasure
|
||||
* **/
|
||||
@SuppressWarnings("unchecked")
|
||||
T[] result = (T[]) new Object[run1.length + run2.length];
|
||||
int i = 0, j = 0, k = 0;
|
||||
|
||||
while (i < run1.length && j < run2.length) {
|
||||
//This comparison only works if the lists are sorted
|
||||
if (c.compare(run1[i], run2[j]) <= 0) {
|
||||
result[k++] = run1[i++];
|
||||
} else {
|
||||
result[k++] = run2[j++];
|
||||
}
|
||||
}
|
||||
// can be improved by finding out which one is empty and only add the other one
|
||||
// Copy remaining elements
|
||||
while (i < run1.length) {
|
||||
result[k++] = run1[i++];
|
||||
}
|
||||
while (j < run2.length) {
|
||||
result[k++] = run2[j++];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// New helper method to handle range reversal
|
||||
private static <T> void reverseRange(T[] a, int start, int end) {
|
||||
// end is exclusive, so we need to use end-1 as the right boundary
|
||||
int left = start;
|
||||
int right = end - 1;
|
||||
while (left < right) {
|
||||
T temp = a[left];
|
||||
a[left] = a[right];
|
||||
a[right] = temp;
|
||||
left++;
|
||||
right--;
|
||||
}
|
||||
}
|
||||
|
||||
static <T> void mergeInplace(T[] a, int i, int m, int j, Comparator<? super T> c) {
|
||||
// Minimize memory allocation and copying
|
||||
if (j - i <= 16) {
|
||||
insertionSortRange(a, i, j, c);
|
||||
return;
|
||||
}
|
||||
|
||||
// Reduce temporary array allocations
|
||||
T[] merged = merge(Arrays.copyOfRange(a, i, m),
|
||||
Arrays.copyOfRange(a, m, j), c);
|
||||
System.arraycopy(merged, 0, a, i, merged.length);
|
||||
}
|
||||
|
||||
|
||||
static <T> int extendRun(T[] a, int i, Comparator<? super T> c) {
|
||||
// Faster, more aggressive run detection
|
||||
int j = i + 1;
|
||||
while (j < a.length && c.compare(a[j-1], a[j]) <= 0) {
|
||||
j++;
|
||||
}
|
||||
|
||||
// Quick reversal for descending runs
|
||||
if (j < a.length && c.compare(a[j-1], a[j]) > 0) {
|
||||
reverseRange(a, i, j);
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
|
||||
static <T> int determineRunType(T[] a, int start, Comparator<? super T> c) {
|
||||
if (start >= a.length - 1) return 0;
|
||||
int result = c.compare(a[start], a[start + 1]);
|
||||
return Integer.signum(result);
|
||||
}
|
||||
|
||||
static <T> int findRunEnd(T[] a, int start, int runType, Comparator<? super T> c) {
|
||||
int j = start + 1;
|
||||
while (j < a.length) {
|
||||
int comparison = c.compare(a[j - 1], a[j]);
|
||||
if (runType == 0 && comparison > 0) break;
|
||||
if (runType < 0 && comparison > 0) break;
|
||||
if (runType > 0 && comparison < 0) break;
|
||||
j++;
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
public static int power(int[] run1, int[] run2, int n) {
|
||||
int i1 = run1[0], n1 = run1[1];
|
||||
int i2 = run2[0], n2 = run2[1];
|
||||
|
||||
assert i1 >= 0;
|
||||
assert i2 == i1 + n1;
|
||||
assert n1 >= 1 && n2 >= 1;
|
||||
assert i2 + n2 <= n;
|
||||
|
||||
double a = ((i1 + n1 / 2.0d) / n);
|
||||
double b = ((i2 + n2 / 2.0d) / n);
|
||||
|
||||
int l = 0;
|
||||
while (Math.floor(a * Math.pow(2, l)) == Math.floor(b * Math.pow(2, l))) {
|
||||
l++;
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
|
||||
static <T> void inPlaceMerge(T[] a, int start, int mid, int end, Comparator<? super T> c) {
|
||||
if (end - start <= 16) {
|
||||
insertionSortRange(a, start, end, c);
|
||||
return;
|
||||
}
|
||||
|
||||
T[] temp = Arrays.copyOfRange(a, start, end);
|
||||
int i = 0, j = mid - start, k = start;
|
||||
|
||||
while (i < mid - start && j < temp.length) {
|
||||
if (c.compare(temp[i], temp[j]) <= 0) {
|
||||
a[k++] = temp[i++];
|
||||
} else {
|
||||
a[k++] = temp[j++];
|
||||
}
|
||||
}
|
||||
|
||||
while (i < mid - start) a[k++] = temp[i++];
|
||||
while (j < temp.length) a[k++] = temp[j++];
|
||||
}
|
||||
|
||||
private static final int MIN_RUN_LENGTH = 32;
|
||||
|
||||
|
||||
public static <T> void powerSort(T[] a, Comparator<? super T> c) {
|
||||
int n = a.length;
|
||||
if (n < 64) {
|
||||
Arrays.sort(a, c);
|
||||
return;
|
||||
}
|
||||
|
||||
List<int[]> runs = new ArrayList<>();
|
||||
int i = 0;
|
||||
int iterationLimit = n * 10; // Increased limit
|
||||
int iterations = 0;
|
||||
|
||||
while (i < n) {
|
||||
if (iterations++ > iterationLimit) {
|
||||
System.err.println("Iteration limit reached, falling back to standard sort");
|
||||
Arrays.sort(a, c);
|
||||
return;
|
||||
}
|
||||
|
||||
int j = fastExtendRun(a, i, c);
|
||||
int p = calculateEffectivePower(runs, i, j - i, n);
|
||||
|
||||
int mergeCycles = 0;
|
||||
while (runs.size() >= 2 && shouldMerge(runs, p)) {
|
||||
if (mergeCycles++ > 100) {
|
||||
System.err.println("Merge cycle limit reached");
|
||||
break;
|
||||
}
|
||||
mergeRuns(a, runs, c);
|
||||
}
|
||||
|
||||
runs.add(new int[]{i, j - i, p});
|
||||
i = j;
|
||||
|
||||
// Periodic debug output
|
||||
if (iterations % 100 == 0) {
|
||||
System.out.printf("Iteration %d: runs=%d, current index=%d%n",
|
||||
iterations, runs.size(), i);
|
||||
}
|
||||
}
|
||||
|
||||
while (runs.size() > 1) {
|
||||
mergeRuns(a, runs, c);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean shouldMerge(List<int[]> runs, int newPower) {
|
||||
return runs.size() >= 2 &&
|
||||
runs.getLast()[2] >= newPower;
|
||||
}
|
||||
|
||||
static <T> void mergeRuns(T[] a, List<int[]> runs, Comparator<? super T> c) {
|
||||
if (runs.size() < 2) return;
|
||||
|
||||
int[] Y = runs.get(runs.size() - 2);
|
||||
int[] Z = runs.getLast();
|
||||
|
||||
mergeInplaceFast(a, Y[0], Z[0], Z[0] + Z[1], c);
|
||||
|
||||
runs.set(runs.size() - 2, new int[]{Y[0], Y[1] + Z[1], Y[2]});
|
||||
runs.removeLast();
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static boolean shouldAggressiveMerge(List<int[]> runs, int newPower) {
|
||||
return runs.size() >= 2 && runs.get(runs.size() - 1)[2] >= newPower;
|
||||
}
|
||||
|
||||
static <T> void mergeOptimized(T[] a, List<int[]> runs, Comparator<? super T> c) {
|
||||
if (runs.size() < 2) return;
|
||||
|
||||
int[] Y = runs.get(runs.size() - 2);
|
||||
int[] Z = runs.getLast();
|
||||
|
||||
mergeInplaceFast(a, Y[0], Z[0], Z[0] + Z[1], c);
|
||||
|
||||
runs.set(runs.size() - 2, new int[]{Y[0], Y[1] + Z[1], Y[2]});
|
||||
runs.removeLast();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private static <T> int fastExtendRun(T[] a, int start, Comparator<? super T> c) {
|
||||
int j = start + 1;
|
||||
while (j < a.length && c.compare(a[j-1], a[j]) <= 0) {
|
||||
j++;
|
||||
}
|
||||
|
||||
if (j < a.length && c.compare(a[j-1], a[j]) > 0) {
|
||||
reverseRange(a, start, j);
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
private static int calculateEffectivePower(List<int[]> runs, int start, int length, int n) {
|
||||
if (runs.isEmpty()) return 0;
|
||||
|
||||
int[] lastRun = runs.getLast();
|
||||
return (start + length/2) / (n/2);
|
||||
}
|
||||
|
||||
|
||||
static <T> void mergeInplaceFast(T[] a, int start, int mid, int end, Comparator<? super T> c) {
|
||||
if (end - start <= 16) {
|
||||
insertionSortRange(a, start, end, c);
|
||||
return;
|
||||
}
|
||||
|
||||
T[] temp = Arrays.copyOfRange(a, start, end);
|
||||
int i = 0, j = mid - start, k = start;
|
||||
|
||||
while (i < mid - start && j < temp.length) {
|
||||
if (c.compare(temp[i], temp[j]) <= 0) {
|
||||
a[k++] = temp[i++];
|
||||
} else {
|
||||
a[k++] = temp[j++];
|
||||
}
|
||||
}
|
||||
|
||||
while (i < mid - start) a[k++] = temp[i++];
|
||||
while (j < temp.length) a[k++] = temp[j++];
|
||||
}
|
||||
|
||||
static <T> void insertionSortRange(T[] a, int start, int end, Comparator<? super T> c) {
|
||||
for (int i = start + 1; i < end; i++) {
|
||||
T key = a[i];
|
||||
int j = i - 1;
|
||||
while (j >= start && c.compare(a[j], key) > 0) {
|
||||
a[j + 1] = a[j];
|
||||
j--;
|
||||
}
|
||||
a[j + 1] = key;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private static int calculateOptimizedPower(List<int[]> runs, int start, int length, int n) {
|
||||
if (runs.isEmpty()) return 0;
|
||||
|
||||
int[] lastRun = runs.getLast();
|
||||
int l = 0;
|
||||
double a = (start + length / 2.0) / n;
|
||||
double b = (lastRun[0] + lastRun[1] / 2.0) / n;
|
||||
|
||||
while (Math.floor(a * Math.pow(2, l)) == Math.floor(b * Math.pow(2, l))) {
|
||||
l++;
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
|
||||
static <T> void mergeTopmost2(T[] a, List<int[]> runs, Comparator<? super T> c) {
|
||||
int[] Y = runs.get(runs.size() - 2);
|
||||
int[] Z = runs.getLast();
|
||||
|
||||
mergeInplace(a, Y[0], Z[0], Z[0] + Z[1], c);
|
||||
|
||||
runs.set(runs.size() - 2, new int[]{Y[0], Y[1] + Z[1], Y[2]});
|
||||
runs.removeLast();
|
||||
}
|
||||
public static int extendRunIncreasingOnly(List<Integer> a, int i) {
|
||||
if (i == a.size() - 1) {
|
||||
return i + 1;
|
||||
}
|
||||
int j = i + 1;
|
||||
while (j < a.size() && a.get(j - 1) <= a.get(j)) {
|
||||
j++;
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
public static int powerFast(int[] run1, int[] run2, int n) {
|
||||
// Bitwise optimizations
|
||||
int a = (run1[0] << 1) + run1[1];
|
||||
int b = a + run1[1] + run2[1];
|
||||
|
||||
int l = 0;
|
||||
while (a < n) {
|
||||
a <<= 1;
|
||||
b <<= 1;
|
||||
l++;
|
||||
if (b >= n) break;
|
||||
}
|
||||
return l;
|
||||
}
|
||||
}
|
178
app/src/main/java/de/uni_marburg/powersort/MSort/IMPL_M_3.java
Normal file
178
app/src/main/java/de/uni_marburg/powersort/MSort/IMPL_M_3.java
Normal file
@ -0,0 +1,178 @@
|
||||
package de.uni_marburg.powersort.MSort;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
|
||||
public class IMPL_M_3 {
|
||||
|
||||
private static final int MIN_MERGE = 32;
|
||||
private static final int MIN_GALLOP = 7;
|
||||
|
||||
private IMPL_M_3() {
|
||||
}
|
||||
|
||||
public static void fillWithAscRunsHighToLow(Integer[] A, int[] runLengths, int runLenFactor) {
|
||||
int n = A.length;
|
||||
assert Arrays.stream(runLengths).sum() * runLenFactor == n;
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
A[i] = n - i;
|
||||
}
|
||||
|
||||
int startIndex = 0;
|
||||
for (int l : runLengths) {
|
||||
int L = l * runLenFactor;
|
||||
Arrays.sort(A, startIndex, startIndex + L);
|
||||
startIndex += L;
|
||||
}
|
||||
}
|
||||
|
||||
private static <T> int extendRun(T[] a, int i, Comparator<? super T> c) {
|
||||
if (i >= a.length - 1) {
|
||||
return a.length; // Return the end of the array
|
||||
}
|
||||
|
||||
int j = i + 1;
|
||||
boolean ascending = c.compare(a[i], a[j]) <= 0;
|
||||
|
||||
while (j < a.length && c.compare(a[j - 1], a[j]) == (ascending ? -1 : 1)) {
|
||||
j++;
|
||||
}
|
||||
|
||||
if (!ascending) {
|
||||
reverseRange(a, i, j);
|
||||
}
|
||||
|
||||
return j;
|
||||
}
|
||||
|
||||
private static <T> void reverseRange(T[] a, int start, int end) {
|
||||
end--;
|
||||
while (start < end) {
|
||||
T temp = a[start];
|
||||
a[start++] = a[end];
|
||||
a[end--] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
private static <T> void mergeInplace(T[] a, int i, int m, int j, Comparator<? super T> c, T[] temp) {
|
||||
int leftSize = m - i;
|
||||
int rightSize = j - m;
|
||||
|
||||
// Validate indices
|
||||
if (leftSize < 0 || rightSize < 0) {
|
||||
throw new IllegalArgumentException("Invalid indices: leftSize=" + leftSize + ", rightSize=" + rightSize);
|
||||
}
|
||||
if (leftSize < 0) {
|
||||
throw new IllegalArgumentException("Invalid indices: leftSize is negative");
|
||||
}
|
||||
// Ensure the temporary array is large enough
|
||||
if (temp.length < leftSize) {
|
||||
temp = Arrays.copyOf(temp, leftSize);
|
||||
}
|
||||
|
||||
System.arraycopy(a, i, temp, 0, leftSize);
|
||||
|
||||
int li = 0, ri = m, k = i;
|
||||
int gallopCount = 0;
|
||||
|
||||
while (li < leftSize && ri < j) {
|
||||
if (c.compare(temp[li], a[ri]) <= 0) {
|
||||
a[k++] = temp[li++];
|
||||
gallopCount++;
|
||||
} else {
|
||||
a[k++] = a[ri++];
|
||||
gallopCount = 0;
|
||||
}
|
||||
|
||||
if (gallopCount >= MIN_GALLOP) {
|
||||
gallopCount = 0;
|
||||
while (li < leftSize && ri < j) {
|
||||
if (c.compare(temp[li], a[ri]) <= 0) {
|
||||
a[k++] = temp[li++];
|
||||
} else {
|
||||
a[k++] = a[ri++];
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while (li < leftSize) a[k++] = temp[li++];
|
||||
while (ri < j) a[k++] = a[ri++];
|
||||
}
|
||||
|
||||
public static <T> void powerSort(T[] a, Comparator<? super T> c) {
|
||||
int n = a.length;
|
||||
if (n < MIN_MERGE) {
|
||||
Arrays.sort(a, c);
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize temporary array with a reasonable size
|
||||
T[] temp = (T[]) new Object[Math.min(n, MIN_MERGE)];
|
||||
int[] runStack = new int[40];
|
||||
int stackSize = 0;
|
||||
|
||||
int i = 0;
|
||||
while (i < n) {
|
||||
int j = extendRun(a, i, c);
|
||||
|
||||
// Ensure j > i
|
||||
if (j <= i) {
|
||||
throw new IllegalStateException("Invalid run: j <= i, i=" + i + ", j=" + j);
|
||||
}
|
||||
|
||||
int[] newRun = new int[]{i, j - i};
|
||||
|
||||
// Validate new run
|
||||
if (newRun[0] >= newRun[1]) {
|
||||
throw new IllegalArgumentException("Invalid run: start index >= length, i=" + i + ", j=" + j);
|
||||
}
|
||||
|
||||
i = j;
|
||||
|
||||
if (stackSize > 0) {
|
||||
int[] prevRun = new int[]{runStack[stackSize - 2], runStack[stackSize - 1]};
|
||||
int p = power(prevRun, newRun, n);
|
||||
|
||||
while (stackSize > 0 && p <= runStack[stackSize - 1]) {
|
||||
mergeInplace(a, runStack[stackSize - 2], runStack[stackSize - 1], i, c, temp);
|
||||
stackSize -= 2;
|
||||
}
|
||||
}
|
||||
|
||||
runStack[stackSize++] = newRun[0];
|
||||
runStack[stackSize++] = newRun[1];
|
||||
}
|
||||
|
||||
while (stackSize > 2) {
|
||||
mergeInplace(a, runStack[stackSize - 4], runStack[stackSize - 3], n, c, temp);
|
||||
stackSize -= 2;
|
||||
}
|
||||
}
|
||||
|
||||
private static int power(int[] run1, int[] run2, int n) {
|
||||
int i1 = run1[0], n1 = run1[1];
|
||||
int i2 = run2[0], n2 = run2[1];
|
||||
|
||||
int a = 2 * i1 + n1;
|
||||
int b = a + n1 + n2;
|
||||
|
||||
int l = 0;
|
||||
while (true) {
|
||||
l++;
|
||||
if (a >= n) {
|
||||
a -= n;
|
||||
b -= n;
|
||||
} else if (b >= n) {
|
||||
break;
|
||||
}
|
||||
a <<= 1;
|
||||
b <<= 1;
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
|
||||
}
|
1018
app/src/main/java/de/uni_marburg/powersort/MSort/IMPL_M_4.java
Normal file
1018
app/src/main/java/de/uni_marburg/powersort/MSort/IMPL_M_4.java
Normal file
File diff suppressed because it is too large
Load Diff
996
app/src/main/java/de/uni_marburg/powersort/MSort/IMPL_M_5.java
Normal file
996
app/src/main/java/de/uni_marburg/powersort/MSort/IMPL_M_5.java
Normal file
@ -0,0 +1,996 @@
|
||||
package de.uni_marburg.powersort.MSort;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
/*
|
||||
* Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright 2009 Google Inc. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Imported from OpenJDK git repo TimSort.java
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* A stable, adaptive, iterative mergesort that requires far fewer than
|
||||
* n lg(n) comparisons when running on partially sorted arrays, while
|
||||
* offering performance comparable to a traditional mergesort when run
|
||||
* on random arrays. Like all proper mergesorts, this sort is stable and
|
||||
* runs O(n log n) time (worst case). In the worst case, this sort requires
|
||||
* temporary storage space for n/2 object references; in the best case,
|
||||
* it requires only a small constant amount of space.
|
||||
*
|
||||
* This implementation was adapted from Tim Peters's list sort for
|
||||
* Python, which is described in detail here:
|
||||
*
|
||||
* http://svn.python.org/projects/python/trunk/Objects/listsort.txt
|
||||
*
|
||||
* Tim's C code may be found here:
|
||||
*
|
||||
* http://svn.python.org/projects/python/trunk/Objects/listobject.c
|
||||
*
|
||||
* The underlying techniques are described in this paper (and may have
|
||||
* even earlier origins):
|
||||
*
|
||||
* "Optimistic Sorting and Information Theoretic Complexity"
|
||||
* Peter McIlroy
|
||||
* SODA (Fourth Annual ACM-SIAM Symposium on Discrete Algorithms),
|
||||
* pp 467-474, Austin, Texas, 25-27 January 1993.
|
||||
*
|
||||
* While the API to this class consists solely of static methods, it is
|
||||
* (privately) instantiable; a TimSort instance holds the state of an ongoing
|
||||
* sort, assuming the input array is large enough to warrant the full-blown
|
||||
* TimSort. Small arrays are sorted in place, using a binary insertion sort.
|
||||
*
|
||||
* @author Josh Bloch
|
||||
*/
|
||||
public class IMPL_M_5<T> {
|
||||
|
||||
/**
|
||||
* This is the minimum sized sequence that will be merged. Shorter
|
||||
* sequences will be lengthened by calling binarySort. If the entire
|
||||
* array is less than this length, no merges will be performed.
|
||||
*
|
||||
* This constant should be a power of two. It was 64 in Tim Peter's C
|
||||
* implementation, but 32 was empirically determined to work better in
|
||||
* this implementation. In the unlikely event that you set this constant
|
||||
* to be a number that's not a power of two, you'll need to change the
|
||||
* {@link #minRunLength} computation.
|
||||
*
|
||||
* If you decrease this constant, you must change the stackLen
|
||||
* computation in the TimSort constructor, or you risk an
|
||||
* ArrayOutOfBounds exception. See listsort.txt for a discussion
|
||||
* of the minimum stack length required as a function of the length
|
||||
* of the array being sorted and the minimum merge sequence length.
|
||||
*/
|
||||
private static final int MIN_MERGE = 31;
|
||||
|
||||
|
||||
/**
|
||||
* The array being sorted.
|
||||
*/
|
||||
private final T[] a;
|
||||
|
||||
/**
|
||||
* The comparator for this sort.
|
||||
*/
|
||||
private final Comparator<? super T> c;
|
||||
|
||||
/**
|
||||
* When we get into galloping mode, we stay there until both runs win less
|
||||
* often than MIN_GALLOP consecutive times.
|
||||
*/
|
||||
private static final int MIN_GALLOP = 7;
|
||||
|
||||
/**
|
||||
* This controls when we get *into* galloping mode. It is initialized
|
||||
* to MIN_GALLOP. The mergeLo and mergeHi methods nudge it higher for
|
||||
* random data, and lower for highly structured data.
|
||||
*/
|
||||
private int minGallop = MIN_GALLOP;
|
||||
|
||||
/**
|
||||
* Maximum initial size of tmp array, which is used for merging. The array
|
||||
* can grow to accommodate demand.
|
||||
*
|
||||
* Unlike Tim's original C version, we do not allocate this much storage
|
||||
* when sorting smaller arrays. This change was required for performance.
|
||||
*/
|
||||
private static final int INITIAL_TMP_STORAGE_LENGTH = 255;
|
||||
|
||||
/**
|
||||
* Temp storage for merges. A workspace array may optionally be
|
||||
* provided in constructor, and if so will be used as long as it
|
||||
* is big enough.
|
||||
*/
|
||||
private T[] tmp;
|
||||
private int tmpBase; // base of tmp array slice
|
||||
private int tmpLen; // length of tmp array slice
|
||||
|
||||
/**
|
||||
* A stack of pending runs yet to be merged. Run i starts at
|
||||
* address base[i] and extends for len[i] elements. It's always
|
||||
* true (so long as the indices are in bounds) that:
|
||||
*
|
||||
* runBase[i] + runLen[i] == runBase[i + 1]
|
||||
*
|
||||
* so we could cut the storage for this, but it's a minor amount,
|
||||
* and keeping all the info explicit simplifies the code.
|
||||
*/
|
||||
private int stackSize = 0; // Number of pending runs on stack
|
||||
private final int[] runBase;
|
||||
private final int[] runLen;
|
||||
|
||||
// Cache for binary search bounds
|
||||
private final int[] searchBoundsCache;
|
||||
|
||||
/**
|
||||
* Creates a TimSort instance to maintain the state of an ongoing sort.
|
||||
*
|
||||
* @param a the array to be sorted
|
||||
* @param c the comparator to determine the order of the sort
|
||||
* @param work a workspace array (slice)
|
||||
* @param workBase origin of usable space in work array
|
||||
* @param workLen usable size of work array
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
MINE
|
||||
**/
|
||||
|
||||
|
||||
private final int[] runPower; // Added to track power of each run
|
||||
private static final int PARALLEL_THRESHOLD = 1 << 16; // 65,536 elements
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private IMPL_M_5(T[] a, Comparator<? super T> c, T[] work, int workBase, int workLen) {
|
||||
this.a = a;
|
||||
this.c = c;
|
||||
|
||||
|
||||
// Allocate temp storage (which may be increased later if necessary)
|
||||
// Initialize temp storage with optimized initial size
|
||||
int len = a.length;
|
||||
int tlen = (len < 2 * INITIAL_TMP_STORAGE_LENGTH) ?
|
||||
len >>> 1 : INITIAL_TMP_STORAGE_LENGTH;
|
||||
|
||||
if (work == null || workLen < tlen || workBase + tlen > work.length) {
|
||||
@SuppressWarnings({"unchecked", "UnnecessaryLocalVariable"})
|
||||
T[] newArray = (T[])java.lang.reflect.Array.newInstance(
|
||||
a.getClass().getComponentType(), tlen);
|
||||
tmp = newArray;
|
||||
tmpBase = 0;
|
||||
tmpLen = tlen;
|
||||
} else {
|
||||
tmp = work;
|
||||
tmpBase = workBase;
|
||||
tmpLen = workLen;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate runs-to-be-merged stack (which cannot be expanded). The
|
||||
* stack length requirements are described in listsort.txt. The C
|
||||
* version always uses the same stack length (85), but this was
|
||||
* measured to be too expensive when sorting "mid-sized" arrays (e.g.,
|
||||
* 100 elements) in Java. Therefore, we use smaller (but sufficiently
|
||||
* large) stack lengths for smaller arrays. The "magic numbers" in the
|
||||
* computation below must be changed if MIN_MERGE is decreased. See
|
||||
* the MIN_MERGE declaration above for more information.
|
||||
* The maximum value of 49 allows for an array up to length
|
||||
* Integer.MAX_VALUE-4, if array is filled by the worst case stack size
|
||||
* increasing scenario. More explanations are given in section 4 of:
|
||||
* http://envisage-project.eu/wp-content/uploads/2015/02/sorting.pdf
|
||||
*/
|
||||
// Optimize stack size based on array length
|
||||
int stackLen = (len < 120 ? 5 :
|
||||
len < 1542 ? 10 :
|
||||
len < 119151 ? 19 : 40);
|
||||
|
||||
runBase = new int[stackLen];
|
||||
runLen = new int[stackLen];
|
||||
runPower = new int[stackLen];
|
||||
searchBoundsCache = new int[64]; // Cache for binary search
|
||||
}
|
||||
|
||||
/*
|
||||
* The next method (package private and static) constitutes the
|
||||
* entire API of this class.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Sorts the given range, using the given workspace array slice
|
||||
* for temp storage when possible. This method is designed to be
|
||||
* invoked from public methods (in class Arrays) after performing
|
||||
* any necessary array bounds checks and expanding parameters into
|
||||
* the required forms.
|
||||
*
|
||||
* @param a the array to be sorted
|
||||
* @param lo the index of the first element, inclusive, to be sorted
|
||||
* @param hi the index of the last element, exclusive, to be sorted
|
||||
* @param c the comparator to use
|
||||
* @param work a workspace array (slice)
|
||||
* @param workBase origin of usable space in work array
|
||||
* @param workLen usable size of work array
|
||||
* @since 1.8
|
||||
*/
|
||||
public static <T> void sort(T[] a, int lo, int hi, Comparator<? super T> c,
|
||||
T[] work, int workBase, int workLen) {
|
||||
if (hi - lo < 2) return;
|
||||
|
||||
IMPL_M_5<T> sorter = new IMPL_M_5<>(a, c, work, workBase, workLen);
|
||||
sorter.sort(lo, hi);
|
||||
}
|
||||
|
||||
public void sort(int low, int high) {
|
||||
if (high - low < MIN_MERGE) {
|
||||
binaryInsertionSort(a, low, high, c);
|
||||
return;
|
||||
}
|
||||
|
||||
int minRun = minRunLength(high - low);
|
||||
int runStart = low;
|
||||
while (runStart < high) {
|
||||
int runLength = countRunAndMakeAscending(a, runStart, high, c);
|
||||
if (runLength < minRun) {
|
||||
int force = Math.min(high - runStart, minRun);
|
||||
binaryInsertionSort(a, runStart, runStart + force, c);
|
||||
runLength = force;
|
||||
}
|
||||
pushRun(runStart, runLength, stackSize);
|
||||
mergeCollapse();
|
||||
runStart += runLength;
|
||||
}
|
||||
mergeForceCollapse();
|
||||
}
|
||||
|
||||
|
||||
private static <T> void binaryInsertionSort(T[] a, int lo, int hi, Comparator<? super T> c) {
|
||||
for (int i = lo + 1; i < hi; i++) {
|
||||
T pivot = a[i];
|
||||
int left = lo;
|
||||
int right = i;
|
||||
while (left < right) {
|
||||
int mid = (left + right) >>> 1;
|
||||
if (c.compare(pivot, a[mid]) < 0)
|
||||
right = mid;
|
||||
else
|
||||
left = mid + 1;
|
||||
}
|
||||
System.arraycopy(a, left, a, left + 1, i - left);
|
||||
a[left] = pivot;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts the specified portion of the specified array using a binary
|
||||
* insertion sort. This is the best method for sorting small numbers
|
||||
* of elements. It requires O(n log n) compares, but O(n^2) data
|
||||
* movement (worst case).
|
||||
*
|
||||
* If the initial part of the specified range is already sorted,
|
||||
* this method can take advantage of it: the method assumes that the
|
||||
* elements from index {@code lo}, inclusive, to {@code start},
|
||||
* exclusive are already sorted.
|
||||
*
|
||||
* @param a the array in which a range is to be sorted
|
||||
* @param lo the index of the first element in the range to be sorted
|
||||
* @param hi the index after the last element in the range to be sorted
|
||||
* @param start the index of the first element in the range that is
|
||||
* not already known to be sorted ({@code lo <= start <= hi})
|
||||
* @param c comparator to used for the sort
|
||||
*/
|
||||
@SuppressWarnings("fallthrough")
|
||||
private static <T> void binarySort(T[] a, int lo, int hi, int start,
|
||||
Comparator<? super T> c) {
|
||||
assert lo <= start && start <= hi;
|
||||
if (start == lo)
|
||||
start++;
|
||||
for ( ; start < hi; start++) {
|
||||
T pivot = a[start];
|
||||
|
||||
// Set left (and right) to the index where a[start] (pivot) belongs
|
||||
int left = lo;
|
||||
int right = start;
|
||||
assert left <= right;
|
||||
/*
|
||||
* Invariants:
|
||||
* pivot >= all in [lo, left).
|
||||
* pivot < all in [right, start).
|
||||
*/
|
||||
while (left < right) {
|
||||
int mid = (left + right) >>> 1;
|
||||
if (c.compare(pivot, a[mid]) < 0)
|
||||
right = mid;
|
||||
else
|
||||
left = mid + 1;
|
||||
}
|
||||
assert left == right;
|
||||
|
||||
/*
|
||||
* The invariants still hold: pivot >= all in [lo, left) and
|
||||
* pivot < all in [left, start), so pivot belongs at left. Note
|
||||
* that if there are elements equal to pivot, left points to the
|
||||
* first slot after them -- that's why this sort is stable.
|
||||
* Slide elements over to make room for pivot.
|
||||
*/
|
||||
int n = start - left; // The number of elements to move
|
||||
// Switch is just an optimization for arraycopy in default case
|
||||
switch (n) {
|
||||
case 2: a[left + 2] = a[left + 1];
|
||||
case 1: a[left + 1] = a[left];
|
||||
break;
|
||||
default: System.arraycopy(a, left, a, left + 1, n);
|
||||
}
|
||||
a[left] = pivot;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the length of the run beginning at the specified position in
|
||||
* the specified array and reverses the run if it is descending (ensuring
|
||||
* that the run will always be ascending when the method returns).
|
||||
*
|
||||
* A run is the longest ascending sequence with:
|
||||
*
|
||||
* a[lo] <= a[lo + 1] <= a[lo + 2] <= ...
|
||||
*
|
||||
* or the longest descending sequence with:
|
||||
*
|
||||
* a[lo] > a[lo + 1] > a[lo + 2] > ...
|
||||
*
|
||||
* For its intended use in a stable mergesort, the strictness of the
|
||||
* definition of "descending" is needed so that the call can safely
|
||||
* reverse a descending sequence without violating stability.
|
||||
*
|
||||
* @param a the array in which a run is to be counted and possibly reversed
|
||||
* @param lo index of the first element in the run
|
||||
* @param hi index after the last element that may be contained in the run.
|
||||
* It is required that {@code lo < hi}.
|
||||
* @param c the comparator to used for the sort
|
||||
* @return the length of the run beginning at the specified position in
|
||||
* the specified array
|
||||
*/
|
||||
private int countRunAndMakeAscending(T[] a, int lo, int hi, Comparator<? super T> c) {
|
||||
int runHi = lo + 1;
|
||||
if (runHi == hi) return 1;
|
||||
if (c.compare(a[runHi++], a[lo]) < 0) {
|
||||
while (runHi < hi && c.compare(a[runHi], a[runHi - 1]) < 0)
|
||||
runHi++;
|
||||
reverseRange(a, lo, runHi);
|
||||
} else {
|
||||
while (runHi < hi && c.compare(a[runHi], a[runHi - 1]) >= 0)
|
||||
runHi++;
|
||||
}
|
||||
return runHi - lo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the specified range of the specified array.
|
||||
*
|
||||
* @param a the array in which a range is to be reversed
|
||||
* @param lo the index of the first element in the range to be reversed
|
||||
* @param hi the index after the last element in the range to be reversed
|
||||
*/
|
||||
private void reverseRange(T[] a, int lo, int hi) {
|
||||
hi--;
|
||||
while (lo < hi) {
|
||||
T t = a[lo];
|
||||
a[lo++] = a[hi];
|
||||
a[hi--] = t;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the minimum acceptable run length for an array of the specified
|
||||
* length. Natural runs shorter than this will be extended with
|
||||
* {@link #binarySort}.
|
||||
*
|
||||
* Roughly speaking, the computation is:
|
||||
*
|
||||
* If n < MIN_MERGE, return n (it's too small to bother with fancy stuff).
|
||||
* Else if n is an exact power of 2, return MIN_MERGE/2.
|
||||
* Else return an int k, MIN_MERGE/2 <= k <= MIN_MERGE, such that n/k
|
||||
* is close to, but strictly less than, an exact power of 2.
|
||||
*
|
||||
* For the rationale, see listsort.txt.
|
||||
*
|
||||
* @param n the length of the array to be sorted
|
||||
* @return the length of the minimum run to be merged
|
||||
*/
|
||||
private int minRunLength(int n) {
|
||||
int r = 0;
|
||||
while (n >= MIN_MERGE) {
|
||||
r |= (n & 1);
|
||||
n >>= 1;
|
||||
}
|
||||
return n + r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pushes the specified run onto the pending-run stack.
|
||||
*
|
||||
* @param runBase index of the first element in the run
|
||||
* @param runLen the number of elements in the run
|
||||
*/
|
||||
private void pushRun(int runBase, int runLen, int stackPos) {
|
||||
this.runBase[stackPos] = runBase;
|
||||
this.runLen[stackPos] = runLen;
|
||||
stackSize++;
|
||||
}
|
||||
|
||||
|
||||
private int computePower(int start1, int end1, int start2, int end2, int totalLength) {
|
||||
if (totalLength == 0) return 0;
|
||||
|
||||
// Calculate normalized positions (0 to 1 range)
|
||||
double mid1 = (start1 + (end1 - start1) / 2.0) / totalLength;
|
||||
double mid2 = (start2 + (end2 - start2) / 2.0) / totalLength;
|
||||
|
||||
// Fast path for equal midpoints
|
||||
if (Math.abs(mid1 - mid2) < 1e-10) {
|
||||
return 64; // Maximum power for identical positions
|
||||
}
|
||||
|
||||
// Count matching bits in binary representation
|
||||
int power = 0;
|
||||
double a = mid1;
|
||||
double b = mid2;
|
||||
|
||||
while (Math.floor(a) == Math.floor(b) && power < 64) {
|
||||
a = (a - Math.floor(a)) * 2;
|
||||
b = (b - Math.floor(b)) * 2;
|
||||
power++;
|
||||
}
|
||||
|
||||
return power;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Examines the stack of runs waiting to be merged and merges adjacent runs
|
||||
* until the stack invariants are reestablished:
|
||||
*
|
||||
* 1. runLen[i - 3] > runLen[i - 2] + runLen[i - 1]
|
||||
* 2. runLen[i - 2] > runLen[i - 1]
|
||||
*
|
||||
* This method is called each time a new run is pushed onto the stack,
|
||||
* so the invariants are guaranteed to hold for i < stackSize upon
|
||||
* entry to the method.
|
||||
*
|
||||
* Thanks to Stijn de Gouw, Jurriaan Rot, Frank S. de Boer,
|
||||
* Richard Bubel and Reiner Hahnle, this is fixed with respect to
|
||||
* the analysis in "On the Worst-Case Complexity of TimSort" by
|
||||
* Nicolas Auger, Vincent Jug, Cyril Nicaud, and Carine Pivoteau.
|
||||
*/
|
||||
private void mergeCollapse() {
|
||||
while (stackSize > 1) {
|
||||
int n = stackSize - 2;
|
||||
if (n > 0 && runLen[n - 1] <= runLen[n] + runLen[n + 1]) {
|
||||
if (runLen[n - 1] < runLen[n + 1]) {
|
||||
mergeAt(n - 1);
|
||||
} else {
|
||||
mergeAt(n);
|
||||
}
|
||||
} else if (runLen[n] <= runLen[n + 1]) {
|
||||
mergeAt(n);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Merges all runs on the stack until only one remains. This method is
|
||||
* called once, to complete the sort.
|
||||
*/
|
||||
private void mergeForceCollapse() {
|
||||
while (stackSize > 1) {
|
||||
int n = stackSize - 2;
|
||||
if (n > 0 && runLen[n - 1] < runLen[n + 1]) {
|
||||
n--;
|
||||
}
|
||||
mergeAt(n);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Merges the two runs at stack indices i and i+1. Run i must be
|
||||
* the penultimate or antepenultimate run on the stack. In other words,
|
||||
* i must be equal to stackSize-2 or stackSize-3.
|
||||
*
|
||||
* @param i stack index of the first of the two runs to merge
|
||||
*/
|
||||
private void mergeAt(int i) {
|
||||
assert stackSize >= 2;
|
||||
assert i >= 0;
|
||||
assert i == stackSize - 2 || i == stackSize - 3;
|
||||
|
||||
int base1 = runBase[i];
|
||||
int len1 = runLen[i];
|
||||
int base2 = runBase[i + 1];
|
||||
int len2 = runLen[i + 1];
|
||||
assert len1 > 0 && len2 > 0;
|
||||
assert base1 + len1 == base2;
|
||||
|
||||
// if (i < 0 || i >= stackSize - 1) {
|
||||
// throw new IllegalStateException("mergeAt called with invalid index: " + i);
|
||||
// }
|
||||
// System.out.println("Merging at index: " + i + " with stackSize: " + stackSize);
|
||||
|
||||
/*
|
||||
* Record the length of the combined runs; if i is the 3rd-last
|
||||
* run now, also slide over the last run (which isn't involved
|
||||
* in this merge). The current run (i+1) goes away in any case.
|
||||
*/
|
||||
runLen[i] = len1 + len2;
|
||||
|
||||
// Update runPower before modifying the stack structure
|
||||
runPower[i] = runPower[i + 1]; // Transfer power from the absorbed run
|
||||
|
||||
if (i == stackSize - 3) {
|
||||
runBase[i + 1] = runBase[i + 2];
|
||||
runLen[i + 1] = runLen[i + 2];
|
||||
runPower[i + 1] = runPower[i + 2]; // Also slide the power value
|
||||
}
|
||||
stackSize--;
|
||||
|
||||
/*
|
||||
* Find where the first element of run2 goes in run1. Prior elements
|
||||
* in run1 can be ignored (because they're already in place).
|
||||
*/
|
||||
int k = gallopRight(a[base2], a, base1, len1, 0, c);
|
||||
assert k >= 0;
|
||||
base1 += k;
|
||||
len1 -= k;
|
||||
if (len1 == 0)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Find where the last element of run1 goes in run2. Subsequent elements
|
||||
* in run2 can be ignored (because they're already in place).
|
||||
*/
|
||||
len2 = gallopLeft(a[base1 + len1 - 1], a, base2, len2, len2 - 1, c);
|
||||
assert len2 >= 0;
|
||||
if (len2 == 0)
|
||||
return;
|
||||
|
||||
// Merge remaining runs, using tmp array with min(len1, len2) elements
|
||||
if (len1 <= len2)
|
||||
mergeLo(base1, len1, base2, len2);
|
||||
else
|
||||
mergeHi(base1, len1, base2, len2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Locates the position at which to insert the specified key into the
|
||||
* specified sorted range; if the range contains an element equal to key,
|
||||
* returns the index of the leftmost equal element.
|
||||
*
|
||||
* @param key the key whose insertion point to search for
|
||||
* @param a the array in which to search
|
||||
* @param base the index of the first element in the range
|
||||
* @param len the length of the range; must be > 0
|
||||
* @param hint the index at which to begin the search, 0 <= hint < n.
|
||||
* The closer hint is to the result, the faster this method will run.
|
||||
* @param c the comparator used to order the range, and to search
|
||||
* @return the int k, 0 <= k <= n such that a[b + k - 1] < key <= a[b + k],
|
||||
* pretending that a[b - 1] is minus infinity and a[b + n] is infinity.
|
||||
* In other words, key belongs at index b + k; or in other words,
|
||||
* the first k elements of a should precede key, and the last n - k
|
||||
* should follow it.
|
||||
*/
|
||||
private static <T> int gallopLeft(T key, T[] a, int base, int len, int hint,
|
||||
Comparator<? super T> c) {
|
||||
int lastOfs = 0;
|
||||
int ofs = 1;
|
||||
|
||||
if (c.compare(key, a[base + hint]) > 0) {
|
||||
int maxOfs = len - hint;
|
||||
while (ofs < maxOfs && c.compare(key, a[base + hint + ofs]) > 0) {
|
||||
lastOfs = ofs;
|
||||
ofs = (ofs << 1) + 1;
|
||||
if (ofs <= 0) ofs = maxOfs;
|
||||
}
|
||||
if (ofs > maxOfs) ofs = maxOfs;
|
||||
|
||||
lastOfs += hint;
|
||||
ofs += hint;
|
||||
} else {
|
||||
int maxOfs = hint + 1;
|
||||
while (ofs < maxOfs && c.compare(key, a[base + hint - ofs]) <= 0) {
|
||||
lastOfs = ofs;
|
||||
ofs = (ofs << 1) + 1;
|
||||
if (ofs <= 0) ofs = maxOfs;
|
||||
}
|
||||
if (ofs > maxOfs) ofs = maxOfs;
|
||||
|
||||
int tmp = lastOfs;
|
||||
lastOfs = hint - ofs;
|
||||
ofs = hint - tmp;
|
||||
}
|
||||
|
||||
lastOfs++;
|
||||
while (lastOfs < ofs) {
|
||||
int m = lastOfs + ((ofs - lastOfs) >>> 1);
|
||||
if (c.compare(key, a[base + m]) > 0) {
|
||||
lastOfs = m + 1;
|
||||
} else {
|
||||
ofs = m;
|
||||
}
|
||||
}
|
||||
return ofs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Like gallopLeft, except that if the range contains an element equal to
|
||||
* key, gallopRight returns the index after the rightmost equal element.
|
||||
*
|
||||
* @param key the key whose insertion point to search for
|
||||
* @param a the array in which to search
|
||||
* @param base the index of the first element in the range
|
||||
* @param len the length of the range; must be > 0
|
||||
* @param hint the index at which to begin the search, 0 <= hint < n.
|
||||
* The closer hint is to the result, the faster this method will run.
|
||||
* @param c the comparator used to order the range, and to search
|
||||
* @return the int k, 0 <= k <= n such that a[b + k - 1] <= key < a[b + k]
|
||||
*/
|
||||
private static <T> int gallopRight(T key, T[] a, int base, int len,
|
||||
int hint, Comparator<? super T> c) {
|
||||
assert len > 0 && hint >= 0 && hint < len;
|
||||
|
||||
int ofs = 1;
|
||||
int lastOfs = 0;
|
||||
if (c.compare(key, a[base + hint]) < 0) {
|
||||
// Gallop left until a[b+hint - ofs] <= key < a[b+hint - lastOfs]
|
||||
int maxOfs = hint + 1;
|
||||
while (ofs < maxOfs && c.compare(key, a[base + hint - ofs]) < 0) {
|
||||
lastOfs = ofs;
|
||||
ofs = (ofs << 1) + 1;
|
||||
if (ofs <= 0) // int overflow
|
||||
ofs = maxOfs;
|
||||
}
|
||||
if (ofs > maxOfs)
|
||||
ofs = maxOfs;
|
||||
|
||||
// Make offsets relative to b
|
||||
int tmp = lastOfs;
|
||||
lastOfs = hint - ofs;
|
||||
ofs = hint - tmp;
|
||||
} else { // a[b + hint] <= key
|
||||
// Gallop right until a[b+hint + lastOfs] <= key < a[b+hint + ofs]
|
||||
int maxOfs = len - hint;
|
||||
while (ofs < maxOfs && c.compare(key, a[base + hint + ofs]) >= 0) {
|
||||
lastOfs = ofs;
|
||||
ofs = (ofs << 1) + 1;
|
||||
if (ofs <= 0) // int overflow
|
||||
ofs = maxOfs;
|
||||
}
|
||||
if (ofs > maxOfs)
|
||||
ofs = maxOfs;
|
||||
|
||||
// Make offsets relative to b
|
||||
lastOfs += hint;
|
||||
ofs += hint;
|
||||
}
|
||||
assert -1 <= lastOfs && lastOfs < ofs && ofs <= len;
|
||||
|
||||
/*
|
||||
* Now a[b + lastOfs] <= key < a[b + ofs], so key belongs somewhere to
|
||||
* the right of lastOfs but no farther right than ofs. Do a binary
|
||||
* search, with invariant a[b + lastOfs - 1] <= key < a[b + ofs].
|
||||
*/
|
||||
lastOfs++;
|
||||
while (lastOfs < ofs) {
|
||||
int m = lastOfs + ((ofs - lastOfs) >>> 1);
|
||||
|
||||
if (c.compare(key, a[base + m]) < 0)
|
||||
ofs = m; // key < a[b + m]
|
||||
else
|
||||
lastOfs = m + 1; // a[b + m] <= key
|
||||
}
|
||||
assert lastOfs == ofs; // so a[b + ofs - 1] <= key < a[b + ofs]
|
||||
return ofs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges two adjacent runs in place, in a stable fashion. The first
|
||||
* element of the first run must be greater than the first element of the
|
||||
* second run (a[base1] > a[base2]), and the last element of the first run
|
||||
* (a[base1 + len1-1]) must be greater than all elements of the second run.
|
||||
*
|
||||
* For performance, this method should be called only when len1 <= len2;
|
||||
* its twin, mergeHi should be called if len1 >= len2. (Either method
|
||||
* may be called if len1 == len2.)
|
||||
*
|
||||
* @param base1 index of first element in first run to be merged
|
||||
* @param len1 length of first run to be merged (must be > 0)
|
||||
* @param base2 index of first element in second run to be merged
|
||||
* (must be aBase + aLen)
|
||||
* @param len2 length of second run to be merged (must be > 0)
|
||||
*/
|
||||
private void mergeLo(int base1, int len1, int base2, int len2) {
|
||||
assert len1 > 0 && len2 > 0 && base1 + len1 == base2;
|
||||
|
||||
// Copy first run into temp array
|
||||
T[] a = this.a; // For performance
|
||||
T[] tmp = ensureCapacity(len1);
|
||||
int cursor1 = tmpBase; // Indexes into tmp array
|
||||
int cursor2 = base2; // Indexes int a
|
||||
int dest = base1; // Indexes int a
|
||||
System.arraycopy(a, base1, tmp, cursor1, len1);
|
||||
|
||||
// Move first element of second run and deal with degenerate cases
|
||||
a[dest++] = a[cursor2++];
|
||||
if (--len2 == 0) {
|
||||
System.arraycopy(tmp, cursor1, a, dest, len1);
|
||||
return;
|
||||
}
|
||||
if (len1 == 1) {
|
||||
System.arraycopy(a, cursor2, a, dest, len2);
|
||||
a[dest + len2] = tmp[cursor1]; // Last elt of run 1 to end of merge
|
||||
return;
|
||||
}
|
||||
|
||||
Comparator<? super T> c = this.c; // Use local variable for performance
|
||||
int minGallop = this.minGallop; // " " " " "
|
||||
outer:
|
||||
while (true) {
|
||||
int count1 = 0; // Number of times in a row that first run won
|
||||
int count2 = 0; // Number of times in a row that second run won
|
||||
|
||||
/*
|
||||
* Do the straightforward thing until (if ever) one run starts
|
||||
* winning consistently.
|
||||
*/
|
||||
do {
|
||||
assert len1 > 1 && len2 > 0;
|
||||
if (c.compare(a[cursor2], tmp[cursor1]) < 0) {
|
||||
a[dest++] = a[cursor2++];
|
||||
count2++;
|
||||
count1 = 0;
|
||||
if (--len2 == 0)
|
||||
break outer;
|
||||
} else {
|
||||
a[dest++] = tmp[cursor1++];
|
||||
count1++;
|
||||
count2 = 0;
|
||||
if (--len1 == 1)
|
||||
break outer;
|
||||
}
|
||||
} while ((count1 | count2) < minGallop);
|
||||
|
||||
/*
|
||||
* One run is winning so consistently that galloping may be a
|
||||
* huge win. So try that, and continue galloping until (if ever)
|
||||
* neither run appears to be winning consistently anymore.
|
||||
*/
|
||||
do {
|
||||
assert len1 > 1 && len2 > 0;
|
||||
count1 = gallopRight(a[cursor2], tmp, cursor1, len1, 0, c);
|
||||
if (count1 != 0) {
|
||||
System.arraycopy(tmp, cursor1, a, dest, count1);
|
||||
dest += count1;
|
||||
cursor1 += count1;
|
||||
len1 -= count1;
|
||||
if (len1 <= 1) // len1 == 1 || len1 == 0
|
||||
break outer;
|
||||
}
|
||||
a[dest++] = a[cursor2++];
|
||||
if (--len2 == 0)
|
||||
break outer;
|
||||
|
||||
count2 = gallopLeft(tmp[cursor1], a, cursor2, len2, 0, c);
|
||||
if (count2 != 0) {
|
||||
System.arraycopy(a, cursor2, a, dest, count2);
|
||||
dest += count2;
|
||||
cursor2 += count2;
|
||||
len2 -= count2;
|
||||
if (len2 == 0)
|
||||
break outer;
|
||||
}
|
||||
a[dest++] = tmp[cursor1++];
|
||||
if (--len1 == 1)
|
||||
break outer;
|
||||
minGallop--;
|
||||
} while (count1 >= MIN_GALLOP | count2 >= MIN_GALLOP);
|
||||
if (minGallop < 0)
|
||||
minGallop = 0;
|
||||
minGallop += 2; // Penalize for leaving gallop mode
|
||||
} // End of "outer" loop
|
||||
this.minGallop = minGallop < 1 ? 1 : minGallop; // Write back to field
|
||||
|
||||
if (len1 == 1) {
|
||||
assert len2 > 0;
|
||||
System.arraycopy(a, cursor2, a, dest, len2);
|
||||
a[dest + len2] = tmp[cursor1]; // Last elt of run 1 to end of merge
|
||||
} else if (len1 == 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"Comparison method violates its general contract!");
|
||||
} else {
|
||||
assert len2 == 0;
|
||||
assert len1 > 1;
|
||||
System.arraycopy(tmp, cursor1, a, dest, len1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Like mergeLo, except that this method should be called only if
|
||||
* len1 >= len2; mergeLo should be called if len1 <= len2. (Either method
|
||||
* may be called if len1 == len2.)
|
||||
*
|
||||
* @param base1 index of first element in first run to be merged
|
||||
* @param len1 length of first run to be merged (must be > 0)
|
||||
* @param base2 index of first element in second run to be merged
|
||||
* (must be aBase + aLen)
|
||||
* @param len2 length of second run to be merged (must be > 0)
|
||||
*/
|
||||
private void mergeHi(int base1, int len1, int base2, int len2) {
|
||||
assert len1 > 0 && len2 > 0 && base1 + len1 == base2;
|
||||
|
||||
// Copy second run into temp array
|
||||
T[] a = this.a; // For performance
|
||||
T[] tmp = ensureCapacity(len2);
|
||||
int tmpBase = this.tmpBase;
|
||||
System.arraycopy(a, base2, tmp, tmpBase, len2);
|
||||
|
||||
int cursor1 = base1 + len1 - 1; // Indexes into a
|
||||
int cursor2 = tmpBase + len2 - 1; // Indexes into tmp array
|
||||
int dest = base2 + len2 - 1; // Indexes into a
|
||||
|
||||
// Move last element of first run and deal with degenerate cases
|
||||
a[dest--] = a[cursor1--];
|
||||
if (--len1 == 0) {
|
||||
System.arraycopy(tmp, tmpBase, a, dest - (len2 - 1), len2);
|
||||
return;
|
||||
}
|
||||
if (len2 == 1) {
|
||||
dest -= len1;
|
||||
cursor1 -= len1;
|
||||
System.arraycopy(a, cursor1 + 1, a, dest + 1, len1);
|
||||
a[dest] = tmp[cursor2];
|
||||
return;
|
||||
}
|
||||
|
||||
Comparator<? super T> c = this.c; // Use local variable for performance
|
||||
int minGallop = this.minGallop; // " " " " "
|
||||
outer:
|
||||
while (true) {
|
||||
int count1 = 0; // Number of times in a row that first run won
|
||||
int count2 = 0; // Number of times in a row that second run won
|
||||
|
||||
/*
|
||||
* Do the straightforward thing until (if ever) one run
|
||||
* appears to win consistently.
|
||||
*/
|
||||
do {
|
||||
assert len1 > 0 && len2 > 1;
|
||||
if (c.compare(tmp[cursor2], a[cursor1]) < 0) {
|
||||
a[dest--] = a[cursor1--];
|
||||
count1++;
|
||||
count2 = 0;
|
||||
if (--len1 == 0)
|
||||
break outer;
|
||||
} else {
|
||||
a[dest--] = tmp[cursor2--];
|
||||
count2++;
|
||||
count1 = 0;
|
||||
if (--len2 == 1)
|
||||
break outer;
|
||||
}
|
||||
} while ((count1 | count2) < minGallop);
|
||||
|
||||
/*
|
||||
* One run is winning so consistently that galloping may be a
|
||||
* huge win. So try that, and continue galloping until (if ever)
|
||||
* neither run appears to be winning consistently anymore.
|
||||
*/
|
||||
do {
|
||||
assert len1 > 0 && len2 > 1;
|
||||
count1 = len1 - gallopRight(tmp[cursor2], a, base1, len1, len1 - 1, c);
|
||||
if (count1 != 0) {
|
||||
dest -= count1;
|
||||
cursor1 -= count1;
|
||||
len1 -= count1;
|
||||
System.arraycopy(a, cursor1 + 1, a, dest + 1, count1);
|
||||
if (len1 == 0)
|
||||
break outer;
|
||||
}
|
||||
a[dest--] = tmp[cursor2--];
|
||||
if (--len2 == 1)
|
||||
break outer;
|
||||
|
||||
count2 = len2 - gallopLeft(a[cursor1], tmp, tmpBase, len2, len2 - 1, c);
|
||||
if (count2 != 0) {
|
||||
dest -= count2;
|
||||
cursor2 -= count2;
|
||||
len2 -= count2;
|
||||
System.arraycopy(tmp, cursor2 + 1, a, dest + 1, count2);
|
||||
if (len2 <= 1) // len2 == 1 || len2 == 0
|
||||
break outer;
|
||||
}
|
||||
a[dest--] = a[cursor1--];
|
||||
if (--len1 == 0)
|
||||
break outer;
|
||||
minGallop--;
|
||||
} while (count1 >= MIN_GALLOP | count2 >= MIN_GALLOP);
|
||||
if (minGallop < 0)
|
||||
minGallop = 0;
|
||||
minGallop += 2; // Penalize for leaving gallop mode
|
||||
} // End of "outer" loop
|
||||
this.minGallop = minGallop < 1 ? 1 : minGallop; // Write back to field
|
||||
|
||||
if (len2 == 1) {
|
||||
assert len1 > 0;
|
||||
dest -= len1;
|
||||
cursor1 -= len1;
|
||||
System.arraycopy(a, cursor1 + 1, a, dest + 1, len1);
|
||||
a[dest] = tmp[cursor2]; // Move first elt of run2 to front of merge
|
||||
} else if (len2 == 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"Comparison method violates its general contract!");
|
||||
} else {
|
||||
assert len1 == 0;
|
||||
assert len2 > 0;
|
||||
System.arraycopy(tmp, tmpBase, a, dest - (len2 - 1), len2);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the external array tmp has at least the specified
|
||||
* number of elements, increasing its size if necessary. The size
|
||||
* increases exponentially to ensure amortized linear time complexity.
|
||||
*
|
||||
* @param minCapacity the minimum required capacity of the tmp array
|
||||
* @return tmp, whether or not it grew
|
||||
*/
|
||||
private T[] ensureCapacity(int minCapacity) {
|
||||
if (tmpLen < minCapacity) {
|
||||
// Compute smallest power of 2 > minCapacity
|
||||
int newSize = -1 >>> Integer.numberOfLeadingZeros(minCapacity);
|
||||
newSize++;
|
||||
|
||||
if (newSize < 0) // Not bloody likely!
|
||||
newSize = minCapacity;
|
||||
else
|
||||
newSize = Math.min(newSize, a.length >>> 1);
|
||||
|
||||
@SuppressWarnings({"unchecked", "UnnecessaryLocalVariable"})
|
||||
T[] newArray = (T[])java.lang.reflect.Array.newInstance
|
||||
(a.getClass().getComponentType(), newSize);
|
||||
tmp = newArray;
|
||||
tmpLen = newSize;
|
||||
tmpBase = 0;
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
package de.uni_marburg.powersort.benchmark;
|
||||
|
||||
import de.uni_marburg.powersort.data.DataEnum;
|
||||
import de.uni_marburg.powersort.sort.SortEnum;
|
||||
import de.uni_marburg.powersort.data.ObjectSupplier;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Custom Benchmark (CBM).
|
||||
*/
|
||||
public class CbmBase implements Runnable {
|
||||
private final SortEnum[] sortImplementations;
|
||||
private final DataEnum[] dataEnums;
|
||||
private final int maxDataEnumStrLen;
|
||||
private final int maxSortStrLen;
|
||||
|
||||
public CbmBase(SortEnum[] sortEnums, DataEnum[] dataEnums) {
|
||||
this.sortImplementations = sortEnums;
|
||||
this.dataEnums = dataEnums;
|
||||
|
||||
maxDataEnumStrLen = Arrays.stream(dataEnums).map(e -> e.toString().length()).max(Integer::compareTo).orElse(0);
|
||||
maxSortStrLen = Arrays.stream(sortEnums).map(e -> e.toString().length()).max(Integer::compareTo).orElse(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
System.out.println();
|
||||
|
||||
for (DataEnum dataEnum : dataEnums) {
|
||||
ObjectSupplier objectSupplier = null;
|
||||
for (SortEnum sortImplementation : sortImplementations) {
|
||||
if (!Filter.isFiltered(dataEnum, sortImplementation)) {
|
||||
if (objectSupplier == null) {
|
||||
System.out.println("Creating object supplier " + dataEnum);
|
||||
System.out.println();
|
||||
objectSupplier = dataEnum.getObjectSupplier();
|
||||
}
|
||||
|
||||
String dataEnumStr = dataEnum.toString();
|
||||
dataEnumStr += " ".repeat(maxDataEnumStrLen - dataEnumStr.length());
|
||||
String sortImplStr = sortImplementation.toString();
|
||||
sortImplStr += " ".repeat(maxSortStrLen - sortImplStr.length());
|
||||
|
||||
CbmBench cbmBench = new CbmBench(dataEnumStr, sortImplStr, objectSupplier, sortImplementation);
|
||||
cbmBench.run();
|
||||
|
||||
System.out.println();
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
package de.uni_marburg.powersort.benchmark;
|
||||
|
||||
import de.uni_marburg.powersort.data.ObjectSupplier;
|
||||
import de.uni_marburg.powersort.sort.SortEnum;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class CbmBench implements Runnable{
|
||||
private final String dataEnumStr;
|
||||
private final String sortImplStr;
|
||||
private final ObjectSupplier objectSupplier;
|
||||
private final SortEnum sortImplementation;
|
||||
|
||||
public CbmBench(String dataEnumStr, String sortImplStr, ObjectSupplier objectSupplier, SortEnum sortImplementation) {
|
||||
this.dataEnumStr = dataEnumStr;
|
||||
this.sortImplStr = sortImplStr;
|
||||
this.objectSupplier = objectSupplier;
|
||||
this.sortImplementation = sortImplementation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
// Warmup
|
||||
for (int i = 0; i < 3; i++) {
|
||||
benchmark(true);
|
||||
}
|
||||
// Measure
|
||||
for (int i = 0; i < 3; i++) {
|
||||
benchmark(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void benchmark(boolean warmup) {
|
||||
System.out.print(dataEnumStr + "," + sortImplStr + ",");
|
||||
if (warmup) {
|
||||
System.out.print("WARMUP ,");
|
||||
} else {
|
||||
System.out.print("MEASURE,");
|
||||
}
|
||||
System.out.flush();
|
||||
|
||||
Object[] sortInput = objectSupplier.getCopy();
|
||||
|
||||
final long startNanos = System.nanoTime();
|
||||
sortImplementation.getSortImpl().sort(sortInput);
|
||||
final long stopNanos = System.nanoTime();
|
||||
|
||||
final long durNanos = stopNanos - startNanos;
|
||||
final long durMillis = TimeUnit.NANOSECONDS.toMillis(durNanos);
|
||||
final String durFormatted = LongFormatter.formatUnderscore(durMillis);
|
||||
|
||||
System.out.println(durFormatted);
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package de.uni_marburg.powersort.benchmark;
|
||||
|
||||
import de.uni_marburg.powersort.data.CglEnum;
|
||||
import de.uni_marburg.powersort.sort.SortEnum;
|
||||
|
||||
public class CbmCgl {
|
||||
public static void main(String[] args) {
|
||||
CbmBase cbmBase = new CbmBase(
|
||||
SortEnum.values(),
|
||||
CglEnum.values()
|
||||
);
|
||||
cbmBase.run();
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package de.uni_marburg.powersort.benchmark;
|
||||
|
||||
import de.uni_marburg.powersort.data.CompetitionEnum;
|
||||
import de.uni_marburg.powersort.sort.SortEnum;
|
||||
|
||||
public class CbmCompetition {
|
||||
public static void main(String[] args) {
|
||||
CbmBase cbmBase = new CbmBase(
|
||||
SortEnum.values(),
|
||||
CompetitionEnum.values()
|
||||
);
|
||||
cbmBase.run();
|
||||
}
|
||||
}
|
186
app/src/main/java/de/uni_marburg/powersort/benchmark/Filter.java
Normal file
186
app/src/main/java/de/uni_marburg/powersort/benchmark/Filter.java
Normal file
@ -0,0 +1,186 @@
|
||||
package de.uni_marburg.powersort.benchmark;
|
||||
|
||||
import de.uni_marburg.powersort.data.CglEnum;
|
||||
import de.uni_marburg.powersort.data.CompetitionEnum;
|
||||
import de.uni_marburg.powersort.data.DataEnum;
|
||||
import de.uni_marburg.powersort.data.IntegerSerializer;
|
||||
import de.uni_marburg.powersort.sort.SortEnum;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.util.List;
|
||||
|
||||
import static de.uni_marburg.powersort.data.CompetitionEnum.isCompetitionInput;
|
||||
|
||||
public class Filter {
|
||||
/* Utility Class */
|
||||
private Filter() {
|
||||
}
|
||||
|
||||
public static boolean isFiltered(DataEnum d, SortEnum s) {
|
||||
// To skip some of the inputs for all sort algorithms, uncomment them here.
|
||||
// if (List.of(
|
||||
// CglEnum.RANDOM_INTEGERS,
|
||||
// CglEnum.ASCENDING_INTEGERS,
|
||||
// CglEnum.DESCENDING_INTEGERS,
|
||||
// CglEnum.ASCENDING_RUNS,
|
||||
// CglEnum.ASCENDING_RUNS_WITH_OVERLAP
|
||||
// ).contains(d)) {
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// To skip all competition inputs, uncomment the next line.
|
||||
// if(isCompetitionInput(d)){
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// Skip some sort algorithms for all competition inputs.
|
||||
if(isCompetitionInput(d)){
|
||||
if(List.of(
|
||||
SortEnum.BUBBLE_SORT,
|
||||
SortEnum.MERGE_SORT
|
||||
).contains(s)){
|
||||
return true;
|
||||
}
|
||||
|
||||
if(!Files.exists(IntegerSerializer.DATA_DIR)){
|
||||
System.err.println("Folder with inputs of competition missing: " + IntegerSerializer.DATA_DIR.toAbsolutePath());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Filter based on SortEnum values.
|
||||
//
|
||||
|
||||
if (s == SortEnum.QUICK_SORT) {
|
||||
return List.of(
|
||||
CglEnum.RANDOM_INTEGERS,
|
||||
CglEnum.ASCENDING_INTEGERS,
|
||||
CglEnum.DESCENDING_INTEGERS,
|
||||
CglEnum.ASCENDING_RUNS,
|
||||
CglEnum.ASCENDING_RUNS_WITH_OVERLAP,
|
||||
|
||||
CompetitionEnum.COMPETITION_34,
|
||||
CompetitionEnum.COMPETITION_35,
|
||||
CompetitionEnum.COMPETITION_36,
|
||||
CompetitionEnum.COMPETITION_37,
|
||||
CompetitionEnum.COMPETITION_38,
|
||||
CompetitionEnum.COMPETITION_39,
|
||||
CompetitionEnum.COMPETITION_83,
|
||||
CompetitionEnum.COMPETITION_85,
|
||||
CompetitionEnum.COMPETITION_153,
|
||||
CompetitionEnum.COMPETITION_163,
|
||||
CompetitionEnum.COMPETITION_164,
|
||||
CompetitionEnum.COMPETITION_165,
|
||||
CompetitionEnum.COMPETITION_166,
|
||||
CompetitionEnum.COMPETITION_167,
|
||||
CompetitionEnum.COMPETITION_168,
|
||||
CompetitionEnum.COMPETITION_169,
|
||||
CompetitionEnum.COMPETITION_170,
|
||||
CompetitionEnum.COMPETITION_171,
|
||||
CompetitionEnum.COMPETITION_172,
|
||||
CompetitionEnum.COMPETITION_173,
|
||||
CompetitionEnum.COMPETITION_174,
|
||||
CompetitionEnum.COMPETITION_181,
|
||||
CompetitionEnum.COMPETITION_182,
|
||||
CompetitionEnum.COMPETITION_183,
|
||||
CompetitionEnum.COMPETITION_184,
|
||||
CompetitionEnum.COMPETITION_185,
|
||||
CompetitionEnum.COMPETITION_186,
|
||||
CompetitionEnum.COMPETITION_187,
|
||||
CompetitionEnum.COMPETITION_205,
|
||||
CompetitionEnum.COMPETITION_206,
|
||||
CompetitionEnum.COMPETITION_207,
|
||||
CompetitionEnum.COMPETITION_213,
|
||||
CompetitionEnum.COMPETITION_214,
|
||||
CompetitionEnum.COMPETITION_216,
|
||||
CompetitionEnum.COMPETITION_218,
|
||||
CompetitionEnum.COMPETITION_220,
|
||||
CompetitionEnum.COMPETITION_222,
|
||||
CompetitionEnum.COMPETITION_225,
|
||||
CompetitionEnum.COMPETITION_226,
|
||||
CompetitionEnum.COMPETITION_228,
|
||||
CompetitionEnum.COMPETITION_231,
|
||||
CompetitionEnum.COMPETITION_232,
|
||||
CompetitionEnum.COMPETITION_235,
|
||||
CompetitionEnum.COMPETITION_236
|
||||
).contains(d);
|
||||
}
|
||||
|
||||
if (s == SortEnum.MERGE_SORT) {
|
||||
return List.of(
|
||||
CglEnum.RANDOM_INTEGERS,
|
||||
CglEnum.ASCENDING_INTEGERS,
|
||||
CglEnum.DESCENDING_INTEGERS
|
||||
).contains(d);
|
||||
}
|
||||
|
||||
if (s == SortEnum.BUBBLE_SORT) {
|
||||
return List.of(
|
||||
CglEnum.DESCENDING_INTEGERS,
|
||||
CglEnum.ASCENDING_RUNS,
|
||||
CglEnum.ASCENDING_RUNS_WITH_OVERLAP
|
||||
).contains(d);
|
||||
}
|
||||
|
||||
// TODO: Remove this once performance of FinnSort improved
|
||||
if (s == SortEnum.FINN_SORT) {
|
||||
return List.of(
|
||||
CglEnum.DESCENDING_INTEGERS,
|
||||
CglEnum.ASCENDING_RUNS,
|
||||
CglEnum.ASCENDING_RUNS_WITH_OVERLAP,
|
||||
|
||||
CompetitionEnum.COMPETITION_83,
|
||||
CompetitionEnum.COMPETITION_85,
|
||||
CompetitionEnum.COMPETITION_153,
|
||||
CompetitionEnum.COMPETITION_163,
|
||||
CompetitionEnum.COMPETITION_164,
|
||||
CompetitionEnum.COMPETITION_165,
|
||||
CompetitionEnum.COMPETITION_166,
|
||||
CompetitionEnum.COMPETITION_167,
|
||||
CompetitionEnum.COMPETITION_168,
|
||||
CompetitionEnum.COMPETITION_169,
|
||||
CompetitionEnum.COMPETITION_170,
|
||||
CompetitionEnum.COMPETITION_171,
|
||||
CompetitionEnum.COMPETITION_172,
|
||||
CompetitionEnum.COMPETITION_173,
|
||||
CompetitionEnum.COMPETITION_174,
|
||||
CompetitionEnum.COMPETITION_181,
|
||||
CompetitionEnum.COMPETITION_182,
|
||||
CompetitionEnum.COMPETITION_183,
|
||||
CompetitionEnum.COMPETITION_184,
|
||||
CompetitionEnum.COMPETITION_185,
|
||||
CompetitionEnum.COMPETITION_186,
|
||||
CompetitionEnum.COMPETITION_187,
|
||||
CompetitionEnum.COMPETITION_198,
|
||||
CompetitionEnum.COMPETITION_199,
|
||||
CompetitionEnum.COMPETITION_205,
|
||||
CompetitionEnum.COMPETITION_206,
|
||||
CompetitionEnum.COMPETITION_207,
|
||||
CompetitionEnum.COMPETITION_213,
|
||||
CompetitionEnum.COMPETITION_214,
|
||||
CompetitionEnum.COMPETITION_216,
|
||||
CompetitionEnum.COMPETITION_218,
|
||||
CompetitionEnum.COMPETITION_220,
|
||||
CompetitionEnum.COMPETITION_222,
|
||||
CompetitionEnum.COMPETITION_226,
|
||||
CompetitionEnum.COMPETITION_228,
|
||||
CompetitionEnum.COMPETITION_231,
|
||||
CompetitionEnum.COMPETITION_232,
|
||||
CompetitionEnum.COMPETITION_236
|
||||
).contains(d);
|
||||
}
|
||||
|
||||
// TODO: Remove this once performance of ASort improved
|
||||
if (s == SortEnum.ASORT) {
|
||||
return List.of(
|
||||
CglEnum.ASCENDING_RUNS,
|
||||
CglEnum.ASCENDING_RUNS_WITH_OVERLAP,
|
||||
CompetitionEnum.COMPETITION_173,
|
||||
CompetitionEnum.COMPETITION_174
|
||||
).contains(d);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
package de.uni_marburg.powersort.benchmark;
|
||||
|
||||
import de.uni_marburg.powersort.data.DataEnum;
|
||||
import de.uni_marburg.powersort.sort.SortEnum;
|
||||
import de.uni_marburg.powersort.data.ObjectSupplier;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Custom benchmark.
|
||||
*/
|
||||
public class Main {
|
||||
public static void main(final String[] args) {
|
||||
final EnumSet<SortEnum> sortImplementations = getSortImplementations();
|
||||
final EnumSet<DataEnum> dataEnums = getSortInputSuppliers();
|
||||
|
||||
for (DataEnum dataEnum : dataEnums) {
|
||||
ObjectSupplier objectSupplier = dataEnum.getObjectSupplier();
|
||||
System.out.println(dataEnum);
|
||||
|
||||
for (SortEnum sortImplementation : sortImplementations) {
|
||||
Object[] sortInput = objectSupplier.getCopy();
|
||||
|
||||
// TODO: JVM warmup!
|
||||
final long startNanos = System.nanoTime();
|
||||
sortImplementation.getSortImpl().sort(sortInput);
|
||||
final long stopNanos = System.nanoTime();
|
||||
|
||||
final long durNanos = stopNanos - startNanos;
|
||||
final long durMillis = TimeUnit.NANOSECONDS.toMillis(durNanos);
|
||||
final String durFormatted = LongFormatter.formatUnderscore(durMillis);
|
||||
System.out.println(durFormatted + "," + sortImplementation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static EnumSet<SortEnum> getSortImplementations() {
|
||||
return EnumSet.allOf(SortEnum.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* The returned ObjectSupplier objects are wrapped by DataEnum objects.
|
||||
* This way they are lazily created on their first access with DataEnum.get().
|
||||
* This saves memory if we work with large lists.
|
||||
*/
|
||||
static EnumSet<DataEnum> getSortInputSuppliers() {
|
||||
return EnumSet.allOf(DataEnum.class);
|
||||
}
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
package de.uni_marburg.powersort.benchmark;
|
||||
|
||||
public abstract class SortImpl {
|
||||
final String title;
|
||||
|
||||
public SortImpl(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the sort algorithm.
|
||||
*/
|
||||
public abstract void sort(Object[] a);
|
||||
}
|
46
app/src/main/java/de/uni_marburg/powersort/data/CglEnum.java
Normal file
46
app/src/main/java/de/uni_marburg/powersort/data/CglEnum.java
Normal file
@ -0,0 +1,46 @@
|
||||
package de.uni_marburg.powersort.data;
|
||||
|
||||
/**
|
||||
* Enumeration of Custom Generated Lists (CGL).
|
||||
*/
|
||||
public enum CglEnum implements DataEnum {
|
||||
RANDOM_INTEGERS,
|
||||
ASCENDING_INTEGERS,
|
||||
DESCENDING_INTEGERS,
|
||||
ASCENDING_RUNS,
|
||||
ASCENDING_RUNS_WITH_OVERLAP,
|
||||
MANY_ASCENDING_RUNS,
|
||||
MANY_ASCENDING_RUNS_WITH_OVERLAP;
|
||||
|
||||
@Override
|
||||
public ObjectSupplier getObjectSupplier() {
|
||||
// We use a seed to get the same random list every time -> Repeatable benchmarks on same input data!
|
||||
// final long seed = 3651660232967549736L; // System.nanoTime() ++ Math.random()
|
||||
final long seed = 140506881906827520L; // (long) 'P' * (long) 'O' *(long) 'W' * (long) 'E' * (long) 'R' * (long) 'S' * (long) 'O' * (long) 'R' * (long) 'T';
|
||||
|
||||
int listSize = 66_000_000;
|
||||
int runs = 3_010;
|
||||
int runLength = 3_010;
|
||||
|
||||
int manyRuns = 120_000;
|
||||
int shortRunLength = 50;
|
||||
|
||||
// Constant factors
|
||||
double a = 0.96;
|
||||
double b = 0.25;
|
||||
double c = 0.81;
|
||||
double d = 0.85;
|
||||
|
||||
return switch (this) {
|
||||
case RANDOM_INTEGERS -> new RandomIntegers(listSize, seed);
|
||||
case ASCENDING_INTEGERS -> new AscendingIntegers((int) (a * listSize));
|
||||
case DESCENDING_INTEGERS -> new DescendingIntegers((int) (b * listSize));
|
||||
case ASCENDING_RUNS -> AscendingRuns.newAscendingRuns(runs, runLength, -1 * runLength);
|
||||
case ASCENDING_RUNS_WITH_OVERLAP ->
|
||||
AscendingRuns.newAscendingRuns((int) (c * runs), (int) (c * runLength), (int) (-0.5 * c * runLength));
|
||||
case MANY_ASCENDING_RUNS -> AscendingRuns.newAscendingRuns(manyRuns, shortRunLength, -1 * shortRunLength);
|
||||
case MANY_ASCENDING_RUNS_WITH_OVERLAP ->
|
||||
AscendingRuns.newAscendingRuns((int) (d * manyRuns), shortRunLength, (int) (-0.5 * shortRunLength));
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,500 @@
|
||||
package de.uni_marburg.powersort.data;
|
||||
|
||||
/**
|
||||
* Enumeration of integer lists from the Powersort competition.
|
||||
*/
|
||||
public enum CompetitionEnum implements DataEnum {
|
||||
COMPETITION_1,
|
||||
COMPETITION_2,
|
||||
COMPETITION_3,
|
||||
// COMPETITION_4, // Invalid input file.
|
||||
COMPETITION_5,
|
||||
COMPETITION_6,
|
||||
COMPETITION_7,
|
||||
COMPETITION_8,
|
||||
COMPETITION_9,
|
||||
COMPETITION_10,
|
||||
COMPETITION_11,
|
||||
COMPETITION_12,
|
||||
COMPETITION_13,
|
||||
// COMPETITION_14, // Invalid input file.
|
||||
COMPETITION_15,
|
||||
COMPETITION_16,
|
||||
COMPETITION_17,
|
||||
COMPETITION_18,
|
||||
COMPETITION_19,
|
||||
COMPETITION_20,
|
||||
COMPETITION_21,
|
||||
COMPETITION_22,
|
||||
COMPETITION_23,
|
||||
COMPETITION_24,
|
||||
COMPETITION_25,
|
||||
COMPETITION_26,
|
||||
COMPETITION_27,
|
||||
COMPETITION_28,
|
||||
COMPETITION_29,
|
||||
COMPETITION_30,
|
||||
COMPETITION_31,
|
||||
COMPETITION_32,
|
||||
COMPETITION_33,
|
||||
COMPETITION_34,
|
||||
COMPETITION_35,
|
||||
COMPETITION_36,
|
||||
COMPETITION_37,
|
||||
COMPETITION_38,
|
||||
COMPETITION_39,
|
||||
COMPETITION_40,
|
||||
COMPETITION_41,
|
||||
COMPETITION_42,
|
||||
COMPETITION_43,
|
||||
COMPETITION_44,
|
||||
COMPETITION_45,
|
||||
COMPETITION_46,
|
||||
COMPETITION_47,
|
||||
COMPETITION_48,
|
||||
COMPETITION_49,
|
||||
COMPETITION_50,
|
||||
COMPETITION_51,
|
||||
COMPETITION_52,
|
||||
COMPETITION_53,
|
||||
COMPETITION_54,
|
||||
COMPETITION_55,
|
||||
COMPETITION_56,
|
||||
COMPETITION_57,
|
||||
COMPETITION_58,
|
||||
COMPETITION_59,
|
||||
COMPETITION_60,
|
||||
COMPETITION_61,
|
||||
COMPETITION_62,
|
||||
COMPETITION_63,
|
||||
COMPETITION_64,
|
||||
COMPETITION_65,
|
||||
COMPETITION_66,
|
||||
COMPETITION_67,
|
||||
COMPETITION_68,
|
||||
COMPETITION_69,
|
||||
COMPETITION_70,
|
||||
COMPETITION_71,
|
||||
COMPETITION_72,
|
||||
COMPETITION_73,
|
||||
COMPETITION_74,
|
||||
COMPETITION_75,
|
||||
COMPETITION_76,
|
||||
COMPETITION_77,
|
||||
COMPETITION_78,
|
||||
COMPETITION_79,
|
||||
COMPETITION_80,
|
||||
COMPETITION_81,
|
||||
COMPETITION_82,
|
||||
COMPETITION_83,
|
||||
COMPETITION_84,
|
||||
COMPETITION_85,
|
||||
COMPETITION_86,
|
||||
COMPETITION_87,
|
||||
COMPETITION_88,
|
||||
COMPETITION_89,
|
||||
COMPETITION_90,
|
||||
COMPETITION_91,
|
||||
COMPETITION_92,
|
||||
COMPETITION_93,
|
||||
COMPETITION_94,
|
||||
COMPETITION_95,
|
||||
COMPETITION_96,
|
||||
COMPETITION_97,
|
||||
// COMPETITION_98, // Invalid input file (floats not integers).
|
||||
COMPETITION_99,
|
||||
COMPETITION_100,
|
||||
COMPETITION_101,
|
||||
COMPETITION_102,
|
||||
COMPETITION_103,
|
||||
COMPETITION_104,
|
||||
COMPETITION_105,
|
||||
COMPETITION_106,
|
||||
COMPETITION_107,
|
||||
COMPETITION_108,
|
||||
COMPETITION_109,
|
||||
COMPETITION_110,
|
||||
COMPETITION_111,
|
||||
COMPETITION_112,
|
||||
COMPETITION_113,
|
||||
COMPETITION_114,
|
||||
COMPETITION_115,
|
||||
COMPETITION_116,
|
||||
COMPETITION_117,
|
||||
COMPETITION_118,
|
||||
COMPETITION_119,
|
||||
COMPETITION_120,
|
||||
COMPETITION_121,
|
||||
COMPETITION_122,
|
||||
COMPETITION_123,
|
||||
COMPETITION_124,
|
||||
COMPETITION_125,
|
||||
COMPETITION_126,
|
||||
COMPETITION_127,
|
||||
COMPETITION_128,
|
||||
COMPETITION_129,
|
||||
COMPETITION_130,
|
||||
COMPETITION_131,
|
||||
COMPETITION_132,
|
||||
COMPETITION_133,
|
||||
COMPETITION_134,
|
||||
COMPETITION_135,
|
||||
COMPETITION_136,
|
||||
COMPETITION_137,
|
||||
COMPETITION_138,
|
||||
COMPETITION_139,
|
||||
COMPETITION_140,
|
||||
COMPETITION_141,
|
||||
COMPETITION_142,
|
||||
COMPETITION_143,
|
||||
COMPETITION_144,
|
||||
COMPETITION_145,
|
||||
COMPETITION_146,
|
||||
COMPETITION_147,
|
||||
COMPETITION_148,
|
||||
COMPETITION_149,
|
||||
COMPETITION_150,
|
||||
COMPETITION_151,
|
||||
COMPETITION_152,
|
||||
COMPETITION_153,
|
||||
COMPETITION_154,
|
||||
COMPETITION_155,
|
||||
COMPETITION_156,
|
||||
COMPETITION_157,
|
||||
COMPETITION_158,
|
||||
COMPETITION_159,
|
||||
COMPETITION_160,
|
||||
COMPETITION_161,
|
||||
COMPETITION_162,
|
||||
COMPETITION_163,
|
||||
COMPETITION_164,
|
||||
COMPETITION_165,
|
||||
COMPETITION_166,
|
||||
COMPETITION_167,
|
||||
COMPETITION_168,
|
||||
COMPETITION_169,
|
||||
COMPETITION_170,
|
||||
COMPETITION_171,
|
||||
COMPETITION_172,
|
||||
COMPETITION_173,
|
||||
COMPETITION_174,
|
||||
COMPETITION_175,
|
||||
COMPETITION_176,
|
||||
COMPETITION_177,
|
||||
COMPETITION_178,
|
||||
COMPETITION_179,
|
||||
COMPETITION_180,
|
||||
COMPETITION_181,
|
||||
COMPETITION_182,
|
||||
COMPETITION_183,
|
||||
COMPETITION_184,
|
||||
COMPETITION_185,
|
||||
COMPETITION_186,
|
||||
COMPETITION_187,
|
||||
COMPETITION_188,
|
||||
COMPETITION_189,
|
||||
COMPETITION_190,
|
||||
COMPETITION_191,
|
||||
COMPETITION_192,
|
||||
COMPETITION_193,
|
||||
COMPETITION_194,
|
||||
COMPETITION_195,
|
||||
COMPETITION_196,
|
||||
COMPETITION_197,
|
||||
COMPETITION_198,
|
||||
COMPETITION_199,
|
||||
COMPETITION_200,
|
||||
COMPETITION_201,
|
||||
COMPETITION_202,
|
||||
COMPETITION_203,
|
||||
COMPETITION_204,
|
||||
COMPETITION_205,
|
||||
COMPETITION_206,
|
||||
COMPETITION_207,
|
||||
COMPETITION_208,
|
||||
COMPETITION_209,
|
||||
COMPETITION_210,
|
||||
COMPETITION_211,
|
||||
COMPETITION_212,
|
||||
COMPETITION_213,
|
||||
COMPETITION_214,
|
||||
COMPETITION_215,
|
||||
COMPETITION_216,
|
||||
COMPETITION_217,
|
||||
COMPETITION_218,
|
||||
COMPETITION_219,
|
||||
COMPETITION_220,
|
||||
COMPETITION_221,
|
||||
COMPETITION_222,
|
||||
COMPETITION_223,
|
||||
COMPETITION_224,
|
||||
COMPETITION_225,
|
||||
COMPETITION_226,
|
||||
COMPETITION_227,
|
||||
COMPETITION_228,
|
||||
COMPETITION_229,
|
||||
COMPETITION_230,
|
||||
COMPETITION_231,
|
||||
COMPETITION_232,
|
||||
COMPETITION_233,
|
||||
COMPETITION_234,
|
||||
COMPETITION_235,
|
||||
COMPETITION_236,
|
||||
COMPETITION_237,
|
||||
COMPETITION_238;
|
||||
|
||||
public static final String COMPETITION_PREFIX = "COMPETITION_";
|
||||
|
||||
public static boolean isCompetitionInput(DataEnum dataEnum) {
|
||||
if (dataEnum instanceof CompetitionEnum) {
|
||||
return ((CompetitionEnum) dataEnum).isCompetitionInput();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isCompetitionInput() {
|
||||
return this.toString().startsWith(COMPETITION_PREFIX);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectSupplier getObjectSupplier() {
|
||||
return switch (this) {
|
||||
case COMPETITION_1 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_2 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_3 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_5 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_6 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_7 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_8 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_9 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_10 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_11 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_12 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_13 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_15 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_16 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_17 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_18 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_19 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_20 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_21 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_22 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_23 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_24 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_25 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_26 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_27 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_28 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_29 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_30 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_31 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_32 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_33 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_34 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_35 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_36 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_37 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_38 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_39 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_40 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_41 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_42 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_43 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_44 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_45 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_46 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_47 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_48 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_49 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_50 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_51 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_52 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_53 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_54 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_55 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_56 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_57 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_58 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_59 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_60 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_61 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_62 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_63 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_64 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_65 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_66 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_67 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_68 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_69 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_70 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_71 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_72 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_73 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_74 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_75 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_76 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_77 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_78 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_79 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_80 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_81 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_82 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_83 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_84 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_85 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_86 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_87 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_88 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_89 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_90 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_91 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_92 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_93 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_94 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_95 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_96 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_97 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_99 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_100 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_101 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_102 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_103 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_104 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_105 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_106 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_107 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_108 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_109 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_110 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_111 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_112 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_113 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_114 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_115 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_116 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_117 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_118 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_119 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_120 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_121 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_122 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_123 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_124 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_125 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_126 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_127 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_128 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_129 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_130 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_131 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_132 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_133 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_134 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_135 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_136 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_137 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_138 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_139 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_140 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_141 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_142 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_143 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_144 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_145 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_146 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_147 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_148 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_149 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_150 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_151 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_152 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_153 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_154 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_155 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_156 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_157 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_158 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_159 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_160 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_161 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_162 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_163 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_164 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_165 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_166 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_167 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_168 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_169 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_170 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_171 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_172 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_173 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_174 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_175 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_176 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_177 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_178 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_179 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_180 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_181 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_182 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_183 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_184 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_185 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_186 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_187 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_188 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_189 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_190 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_191 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_192 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_193 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_194 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_195 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_196 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_197 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_198 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_199 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_200 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_201 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_202 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_203 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_204 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_205 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_206 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_207 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_208 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_209 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_210 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_211 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_212 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_213 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_214 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_215 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_216 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_217 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_218 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_219 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_220 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_221 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_222 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_223 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_224 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_225 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_226 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_227 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_228 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_229 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_230 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_231 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_232 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_233 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_234 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_235 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_236 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_237 -> IntegerSerializer.fromEnum(this);
|
||||
case COMPETITION_238 -> IntegerSerializer.fromEnum(this);
|
||||
};
|
||||
}
|
||||
}
|
@ -1,26 +1,8 @@
|
||||
package de.uni_marburg.powersort.data;
|
||||
|
||||
public enum DataEnum {
|
||||
RANDOM_INTEGERS,
|
||||
ASCENDING_INTEGERS,
|
||||
DESCENDING_INTEGERS,
|
||||
ASCENDING_RUNS,
|
||||
ASCENDING_RUNS_WITH_OVERLAP;
|
||||
|
||||
public ObjectSupplier getObjectSupplier() {
|
||||
// We use a seed to get the same random list every time -> Repeatable benchmarks on same input data!
|
||||
// final long seed = 3651660232967549736L; // System.nanoTime() ++ Math.random()
|
||||
final long seed = 140506881906827520L; // (long) 'P' * (long) 'O' *(long) 'W' * (long) 'E' * (long) 'R' * (long) 'S' * (long) 'O' * (long) 'R' * (long) 'T';
|
||||
|
||||
int longListSize = 50_000_000;
|
||||
|
||||
return switch (this) {
|
||||
case RANDOM_INTEGERS -> new RandomIntegers(longListSize, seed);
|
||||
case ASCENDING_INTEGERS -> new AscendingIntegers(longListSize);
|
||||
case DESCENDING_INTEGERS -> new DescendingIntegers(longListSize);
|
||||
|
||||
case ASCENDING_RUNS -> AscendingRuns.newAscendingRuns(10_000, 10_000, -10_000);
|
||||
case ASCENDING_RUNS_WITH_OVERLAP -> AscendingRuns.newAscendingRuns(10_000, 10_000, -5_000);
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Interface for generic usage of different concrete Enum classes.
|
||||
*/
|
||||
public interface DataEnum {
|
||||
ObjectSupplier getObjectSupplier();
|
||||
}
|
||||
|
@ -0,0 +1,89 @@
|
||||
package de.uni_marburg.powersort.data;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class IntegerSerializer {
|
||||
public static final Path DATA_DIR = Paths.get("powersort-competition");
|
||||
|
||||
private final int id;
|
||||
|
||||
public static ObjectSupplier fromEnum(CompetitionEnum dataEnum) {
|
||||
if (!dataEnum.isCompetitionInput()) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
String prefixedId = dataEnum.toString();
|
||||
IntegerSerializer serializer = new IntegerSerializer(Integer.parseInt(prefixedId.substring(CompetitionEnum.COMPETITION_PREFIX.length())));
|
||||
return serializer.getObjectSupplier();
|
||||
}
|
||||
|
||||
public IntegerSerializer(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public ObjectSupplier getObjectSupplier() {
|
||||
Integer[] list = read();
|
||||
return new IntegerSupplier(list) {
|
||||
@Override
|
||||
public Integer[] getCopy() {
|
||||
return super.getCopy();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public Integer[] read() {
|
||||
Path path = getPath();
|
||||
String content;
|
||||
try {
|
||||
content = Files.readString(path);
|
||||
} catch (IOException e) {
|
||||
System.err.println(this + ": Could not read file " + path.toAbsolutePath());
|
||||
System.exit(1);
|
||||
return null;
|
||||
}
|
||||
|
||||
content = content.strip();
|
||||
if (content.charAt(0) != '[' || content.charAt(content.length() - 1) != ']') {
|
||||
System.err.println(this + ": Illegal data format: Expected list of integers surrounded by square brackets.");
|
||||
System.exit(1);
|
||||
return null;
|
||||
}
|
||||
content = content.substring(1, content.length() - 1);
|
||||
|
||||
// Remove spaces
|
||||
content = content.replaceAll(" ", "");
|
||||
|
||||
String[] elements = content.split(",");
|
||||
try {
|
||||
return Arrays.stream(elements).map(Integer::valueOf).toArray(Integer[]::new);
|
||||
} catch (NumberFormatException e) {
|
||||
System.err.println(this + ": Number format exception: " + e.getMessage());
|
||||
System.exit(1);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void write(Integer[] list) throws IOException {
|
||||
Path path = getPath();
|
||||
if (Files.exists(path)) {
|
||||
throw new IOException(this + ": Target file does already exist");
|
||||
}
|
||||
|
||||
String[] elements = Arrays.stream(list).map(Object::toString).toArray(String[]::new);
|
||||
String content = "[" + String.join(",", elements) + "]";
|
||||
Files.writeString(path, content);
|
||||
}
|
||||
|
||||
private Path getPath() {
|
||||
return DATA_DIR.resolve(Integer.toString(id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "IntegerSerializer{" + id + "}";
|
||||
}
|
||||
}
|
@ -3,7 +3,7 @@ package de.uni_marburg.powersort.data;
|
||||
import java.util.Arrays;
|
||||
|
||||
public abstract class ObjectSupplier {
|
||||
/* package-protected */ final Object[] readOnly;
|
||||
final Object[] readOnly;
|
||||
|
||||
ObjectSupplier(Object[] readOnly) {
|
||||
this.readOnly = readOnly;
|
||||
@ -15,4 +15,9 @@ public abstract class ObjectSupplier {
|
||||
public Object[] getCopy(){
|
||||
return Arrays.copyOf(readOnly, readOnly.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ObjectSupplier{" + readOnly.length + "}";
|
||||
}
|
||||
}
|
||||
|
@ -1,298 +0,0 @@
|
||||
package de.uni_marburg.powersort.msort;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
public class IMPL_M_1 {
|
||||
|
||||
|
||||
|
||||
private IMPL_M_1() {}
|
||||
|
||||
/**
|
||||
* Sorts the given range, using the given workspace array slice
|
||||
* for temp storage when possible. This method is designed to be
|
||||
* invoked from public methods (in class Arrays) after performing
|
||||
* any necessary array bounds checks and expanding parameters into
|
||||
* the required forms.
|
||||
*
|
||||
* @param a the array to be sorted
|
||||
* @param lo the index of the first element, inclusive, to be sorted
|
||||
* @param hi the index of the last element, exclusive, to be sorted
|
||||
* @param c the comparator to use
|
||||
* @param work a workspace array (slice)
|
||||
* @param workBase origin of usable space in work array
|
||||
* @param workLen usable size of work array
|
||||
* @since 1.8
|
||||
*/
|
||||
protected static int MERGE_COST = 0;
|
||||
|
||||
// Example usage
|
||||
// int[] runs = new int[] {5, 3, 3, 14, 1, 2}; // example from slides
|
||||
// //runs = new int[]{9, 16, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7};
|
||||
//
|
||||
// ArrayList<Integer> a = new ArrayList<>(IntStream.range(0, Arrays.stream(runs).sum()).boxed().collect(Collectors.toList()));
|
||||
//
|
||||
// System.out.println();
|
||||
// fillWithAscRunsHighToLow(a, runs);
|
||||
// MERGE_COST = 0;
|
||||
// System.out.println("Sorting with Powersort:");
|
||||
// powersort(a, this::extendRunIncreasingOnly);
|
||||
// System.out.println("Merge cost: " + MERGE_COST);
|
||||
|
||||
// runs = [5,3,3,14,1,2];
|
||||
// runs = [9,16,7,7,7,7,7,7,7,7,7,7];
|
||||
//
|
||||
// a = list(range(sum(runs)));
|
||||
// fill_with_asc_runs_high_to_low(a, runs);
|
||||
// MERGE_COST = 0;
|
||||
// System.out.println("Sorting with Powersort:");
|
||||
// powersort(a, extendRunIncreasingOnly);
|
||||
// System.out.println("Merge cost: " + MERGE_COST);
|
||||
|
||||
|
||||
public static void fillWithAscRunsHighToLow(List<Integer> A, int[] runLengths, int runLenFactor) {
|
||||
int n = A.size();
|
||||
assert IntStream.of(runLengths).sum() * runLenFactor == n;
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
A.set(i, n - i);
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
for (int l : runLengths) {
|
||||
int L = l * runLenFactor;
|
||||
List<Integer> sublist = A.subList(i, i + L);
|
||||
Collections.sort(sublist);
|
||||
i += L;
|
||||
}
|
||||
}
|
||||
|
||||
private static List<Integer> merge(List<Integer> run1, List<Integer> run2) {
|
||||
List<Integer> result = new ArrayList<>();
|
||||
while (!run1.isEmpty() && !run2.isEmpty()) {
|
||||
if (run1.get(0) < run2.get(0)) {
|
||||
result.add(run1.remove(0));
|
||||
} else {
|
||||
result.add(run2.remove(0));
|
||||
}
|
||||
}
|
||||
result.addAll(run1);
|
||||
result.addAll(run2);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void mergeInplace(List<Integer> a, int i, int m, int j) {
|
||||
System.out.printf("Merge(%d, %d, %d)%n", i, m, j);
|
||||
MERGE_COST += j - i;
|
||||
List<Integer> sublist = merge(
|
||||
new ArrayList<>(a.subList(i, m)),
|
||||
new ArrayList<>(a.subList(m, j))
|
||||
);
|
||||
for (int k = 0; k < sublist.size(); k++) {
|
||||
a.set(i + k, sublist.get(k));
|
||||
}
|
||||
}
|
||||
|
||||
static int extendRun(List<Integer> a, int i) {
|
||||
if (i == a.size() - 1) {
|
||||
return i + 1;
|
||||
}
|
||||
int j = i + 1;
|
||||
if (a.get(i) <= a.get(j)) {
|
||||
while (j < a.size() && a.get(j - 1) <= a.get(j)) {
|
||||
j++;
|
||||
}
|
||||
} else {
|
||||
while (j < a.size() && a.get(j - 1) > a.get(j)) {
|
||||
j++;
|
||||
}
|
||||
List<Integer> sublist = a.subList(i, j);
|
||||
Collections.reverse(sublist);
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
private static int extendRunIncreasingOnly(List<Integer> a, int i) {
|
||||
if (i == a.size() - 1) {
|
||||
return i + 1;
|
||||
}
|
||||
int j = i + 1;
|
||||
while (j < a.size() && a.get(j - 1) <= a.get(j)) {
|
||||
j++;
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
public static int power(int[] run1, int[] run2, int n) {
|
||||
int i1 = run1[0], n1 = run1[1];
|
||||
int i2 = run2[0], n2 = run2[1];
|
||||
|
||||
assert i1 >= 0;
|
||||
assert i2 == i1 + n1;
|
||||
assert n1 >= 1 && n2 >= 1;
|
||||
assert i2 + n2 <= n;
|
||||
|
||||
double a = ((i1 + n1 / 2.0d) / n);
|
||||
double b = ((i2 + n2 / 2.0d) / n);
|
||||
|
||||
int l = 0;
|
||||
while (Math.floor(a * Math.pow(2, l)) == Math.floor(b * Math.pow(2, l))) {
|
||||
l++;
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
public static int powerFast(int[] run1, int[] run2, int n) {
|
||||
int i1 = run1[0], n1 = run1[1];
|
||||
int i2 = run2[0], n2 = run2[1];
|
||||
|
||||
int a = 2 * i1 + n1;
|
||||
int b = a + n1 + n2;
|
||||
|
||||
int l = 0;
|
||||
while (true) {
|
||||
l++;
|
||||
if (a >= n) {
|
||||
assert b >= a;
|
||||
a -= n;
|
||||
b -= n;
|
||||
} else if (b >= n) {
|
||||
break;
|
||||
}
|
||||
assert a < b && b < n;
|
||||
a <<= 1;
|
||||
b <<= 1;
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
public static void mergeTopmost2(List<Integer> a, List<int[]> runs) {
|
||||
assert runs.size() >= 2;
|
||||
|
||||
int[] Y = runs.get(runs.size() - 2);
|
||||
int[] Z = runs.get(runs.size() - 1);
|
||||
|
||||
assert Z[0] == Y[0] + Y[1];
|
||||
|
||||
mergeInplace(a, Y[0], Z[0], Z[0] + Z[1]);
|
||||
|
||||
runs.set(runs.size() - 2, new int[] {Y[0], Y[1] + Z[1], Y[2]});
|
||||
runs.remove(runs.size() - 1);
|
||||
}
|
||||
|
||||
public static void powerSort(List<Integer> a) {
|
||||
int n = a.size();
|
||||
int i = 0;
|
||||
List<int[]> runs = new ArrayList<>();
|
||||
|
||||
int j = extendRun(a, i);
|
||||
runs.add(new int[] {i, j - i, 0});
|
||||
i = j;
|
||||
|
||||
while (i < n) {
|
||||
j = extendRun(a, i);
|
||||
int p = power(runs.get(runs.size() - 1), new int[] {i, j - i}, n);
|
||||
|
||||
while (p <= runs.get(runs.size() - 1)[2]) {
|
||||
mergeTopmost2(a, runs);
|
||||
}
|
||||
|
||||
runs.add(new int[] {i, j - i, p});
|
||||
i = j;
|
||||
}
|
||||
|
||||
while (runs.size() >= 2) {
|
||||
mergeTopmost2(a, runs);
|
||||
}
|
||||
}
|
||||
|
||||
/* """Fills the given array A with ascending runs of the given list of run
|
||||
lengths.
|
||||
More precisely, the array is first filled n, n-1, ..., 1
|
||||
and then for i=0..l-1 segments of runLengths.get(i) * runLenFactor
|
||||
are sorted ascending.
|
||||
The sum of all lengths in runLengths times runLenFactor should be equal to the
|
||||
length of A.
|
||||
"""*/
|
||||
|
||||
|
||||
/* static <T> void sort(T[] a, int lo, int hi, Comparator<? super T> c,
|
||||
T[] work, int workBase, int workLen) {
|
||||
assert c != null && a != null && lo >= 0 && lo <= hi && hi <= a.length;
|
||||
}*/
|
||||
/*
|
||||
public static final int MIN_MERGE=24;
|
||||
public int mergeCost=0;
|
||||
private final T []sortedArray;
|
||||
|
||||
public PowerSort(T[] sortedArray) {
|
||||
super();
|
||||
this.sortedArray = sortedArray;
|
||||
}
|
||||
|
||||
ArrayList<Integer> run1 = new ArrayList<>();
|
||||
ArrayList<Integer> run2 = new ArrayList<>();
|
||||
|
||||
private AbstractList<Integer> merge(ArrayList <Integer> run1, ArrayList<Integer> run2) {
|
||||
ArrayList<Integer> result = new ArrayList<>();
|
||||
|
||||
while(run1.size() > 0 && run2.size() >0) {
|
||||
if (run1.getFirst()<run2.getFirst()){
|
||||
result.add(run1.getFirst());
|
||||
run1.removeFirst();
|
||||
}else {
|
||||
result.add(run2.getFirst());
|
||||
run2.removeFirst();
|
||||
}
|
||||
result.addAll(run1);
|
||||
result.addAll(run2);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public void mergeInplace(int[] a, int[] i, int[] m, int[] j) {
|
||||
//System.out.println("merge(" + i + "," + m + "," + j + ")");
|
||||
|
||||
System.out.printf("Merge(%d, %d, %d)%n", i, m, j);
|
||||
// this.mergeCost += j - i;
|
||||
|
||||
|
||||
for(int s =0; s < i.length && s< j.length ; s++) {
|
||||
|
||||
|
||||
// int[] leftSubarray = copyOfRange(a, i, m);
|
||||
// int[] rightSubarray = copyOfRange(a, m, j);
|
||||
// int[] mergedSubarray = merge(leftSubarray, rightSubarray);
|
||||
// System.arraycopy(mergedSubarray, 0, a, i, mergedSubarray.length);
|
||||
//// mergeCost += j[s] - i[s];
|
||||
// System.arraycopy(merge(Arrays.copyOfRange(a, i, m), Arrays.copyOfRange(a, m, j)), 0, a, i, j - i);
|
||||
// a[i:j]=merge(a[i:m],a[m:j]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void power(int run1,int run2, int n) {
|
||||
int i = run1;
|
||||
int n1 = run1;
|
||||
int j = run2;
|
||||
int n2 = run2;
|
||||
int a=(i + n1/2) / n;
|
||||
int b=(j + n2/2) / n;
|
||||
int l =0;
|
||||
//while( Math.floor(a * 2**1)){
|
||||
// Math.floor(b * );
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
public void sorting(final int[] Array, final int left, final int right) {
|
||||
|
||||
|
||||
}*/
|
||||
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
package de.uni_marburg.powersort.sort;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
public class QuickSort {
|
||||
/**
|
||||
* Based on https://www.baeldung.com/java-quicksort
|
||||
*/
|
||||
public static <T> void sort(final T[] arr, Comparator<T> c) {
|
||||
quickSort(arr, c, 0, arr.length - 1);
|
||||
}
|
||||
|
||||
private static <T> void quickSort(T[] arr, Comparator<T> c, int begin, int end) {
|
||||
if (begin < end) {
|
||||
int partitionIndex = partition(arr, c, begin, end);
|
||||
|
||||
quickSort(arr, c, begin, partitionIndex - 1);
|
||||
quickSort(arr, c, partitionIndex + 1, end);
|
||||
}
|
||||
}
|
||||
|
||||
private static <T> int partition(T[] arr, Comparator<T> c, int begin, int end) {
|
||||
T pivot = arr[end];
|
||||
int i = (begin - 1);
|
||||
|
||||
for (int j = begin; j < end; j++) {
|
||||
if (c.compare(arr[j], pivot) <= 0) {
|
||||
i++;
|
||||
|
||||
T swapTemp = arr[i];
|
||||
arr[i] = arr[j];
|
||||
arr[j] = swapTemp;
|
||||
}
|
||||
}
|
||||
|
||||
T swapTemp = arr[i + 1];
|
||||
arr[i + 1] = arr[end];
|
||||
arr[end] = swapTemp;
|
||||
|
||||
return i + 1;
|
||||
}
|
||||
}
|
@ -1,21 +1,45 @@
|
||||
package de.uni_marburg.powersort.sort;
|
||||
|
||||
import de.uni_marburg.powersort.FinnSort.FinnSort;
|
||||
import de.uni_marburg.powersort.FinnSort.FasterFinnSort;
|
||||
import de.uni_marburg.powersort.MSort.IMPL_M_1;
|
||||
import de.uni_marburg.powersort.MSort.IMPL_M_2;
|
||||
import de.uni_marburg.powersort.MSort.IMPL_M_3;
|
||||
import de.uni_marburg.powersort.MSort.IMPL_M_4;
|
||||
import de.uni_marburg.powersort.MSort.IMPL_M_5;
|
||||
import de.uni_marburg.powersort.benchmark.NaturalOrder;
|
||||
import de.uni_marburg.powersort.sort.dpqs.DualPivotQuicksort;
|
||||
|
||||
public enum SortEnum {
|
||||
// BUBBLE_SORT,
|
||||
MERGE_SORT,
|
||||
TIM_SORT,
|
||||
FASTER_FINN_SORT,
|
||||
ASORT,
|
||||
FINN_SORT,
|
||||
ASORT;
|
||||
IMPL_M_10,
|
||||
IMPL_M_20,
|
||||
IMPL_M_30,
|
||||
IMPL_M_40,
|
||||
IMPL_M_50,
|
||||
DPQS,
|
||||
QUICK_SORT,
|
||||
MERGE_SORT,
|
||||
BUBBLE_SORT;
|
||||
|
||||
|
||||
public SortImpl getSortImpl() {
|
||||
return switch (this) {
|
||||
// case BUBBLE_SORT -> array -> BubbleSort.sort(array, NaturalOrder.INSTANCE);
|
||||
case BUBBLE_SORT -> array -> BubbleSort.sort(array, NaturalOrder.INSTANCE);
|
||||
case QUICK_SORT -> array -> QuickSort.sort(array, NaturalOrder.INSTANCE);
|
||||
case DPQS -> array -> DualPivotQuicksort.sort(array, 0, array.length);
|
||||
case MERGE_SORT -> array -> MergeSort.legacyMergeSort(array, NaturalOrder.INSTANCE);
|
||||
case TIM_SORT -> array -> TimSort.sort(array, 0, array.length, NaturalOrder.INSTANCE, null, 0, 0);
|
||||
case FINN_SORT -> array -> FinnSort.sort(array, NaturalOrder.INSTANCE);
|
||||
case IMPL_M_10 -> array -> IMPL_M_1.powerSort(array,NaturalOrder.INSTANCE);
|
||||
case IMPL_M_20 -> array -> IMPL_M_2.powerSort(array,NaturalOrder.INSTANCE);
|
||||
case IMPL_M_30 -> array -> IMPL_M_3.powerSort(array,NaturalOrder.INSTANCE);
|
||||
case IMPL_M_40 -> array -> IMPL_M_4.sort(array, 0, array.length, NaturalOrder.INSTANCE, null, 0, 0);
|
||||
case IMPL_M_50 -> array -> IMPL_M_5.sort(array, 0, array.length, NaturalOrder.INSTANCE, null, 0, 0);
|
||||
case FASTER_FINN_SORT -> array -> FasterFinnSort.sort(array, 0, array.length, NaturalOrder.INSTANCE, null, 0, 0);
|
||||
case ASORT -> array -> ASort.sort(array, NaturalOrder.INSTANCE);
|
||||
};
|
||||
}
|
||||
|
@ -0,0 +1,879 @@
|
||||
// Imported from JDK23 DualPivotQuicksort.java
|
||||
// Removed parallel parts
|
||||
// Changed from int[] to Object[]
|
||||
|
||||
package de.uni_marburg.powersort.sort.dpqs;
|
||||
|
||||
/*
|
||||
* Copyright (c) 2009, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import de.uni_marburg.powersort.benchmark.NaturalOrder;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* This class implements powerful and fully optimized versions, both
|
||||
* sequential and parallel, of the Dual-Pivot Quicksort algorithm by
|
||||
* Vladimir Yaroslavskiy, Jon Bentley and Josh Bloch. This algorithm
|
||||
* offers O(n log(n)) performance on all data sets, and is typically
|
||||
* faster than traditional (one-pivot) Quicksort implementations.
|
||||
* <p>
|
||||
* There are also additional algorithms, invoked from the Dual-Pivot
|
||||
* Quicksort, such as mixed insertion sort, merging of runs and heap
|
||||
* sort, counting sort and parallel merge sort.
|
||||
*
|
||||
* @author Vladimir Yaroslavskiy
|
||||
* @author Jon Bentley
|
||||
* @author Josh Bloch
|
||||
* @author Doug Lea
|
||||
* @version 2018.08.18
|
||||
* @since 1.7 * 14
|
||||
*/
|
||||
public final class DualPivotQuicksort {
|
||||
|
||||
/**
|
||||
* Prevents instantiation.
|
||||
*/
|
||||
private DualPivotQuicksort() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Max array size to use mixed insertion sort.
|
||||
*/
|
||||
private static final int MAX_MIXED_INSERTION_SORT_SIZE = 65;
|
||||
|
||||
/**
|
||||
* Max array size to use insertion sort.
|
||||
*/
|
||||
private static final int MAX_INSERTION_SORT_SIZE = 44;
|
||||
|
||||
/**
|
||||
* Min array size to try merging of runs.
|
||||
*/
|
||||
private static final int MIN_TRY_MERGE_SIZE = 4 << 10;
|
||||
|
||||
/**
|
||||
* Min size of the first run to continue with scanning.
|
||||
*/
|
||||
private static final int MIN_FIRST_RUN_SIZE = 16;
|
||||
|
||||
/**
|
||||
* Min factor for the first runs to continue scanning.
|
||||
*/
|
||||
private static final int MIN_FIRST_RUNS_FACTOR = 7;
|
||||
|
||||
/**
|
||||
* Max capacity of the index array for tracking runs.
|
||||
*/
|
||||
private static final int MAX_RUN_CAPACITY = 5 << 10;
|
||||
|
||||
/**
|
||||
* Threshold of mixed insertion sort is incremented by this value.
|
||||
*/
|
||||
private static final int DELTA = 3 << 1;
|
||||
|
||||
/**
|
||||
* Max recursive partitioning depth before using heap sort.
|
||||
*/
|
||||
private static final int MAX_RECURSION_DEPTH = 64 * DELTA;
|
||||
|
||||
|
||||
/**
|
||||
* Represents a function that accepts the array and partitions the specified range
|
||||
* of the array using the pivots provided.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
interface PartitionOperation<A> {
|
||||
/**
|
||||
* Partitions the specified range of the array using the given pivots.
|
||||
*
|
||||
* @param a the array to be partitioned
|
||||
* @param low the index of the first element, inclusive, to be partitioned
|
||||
* @param high the index of the last element, exclusive, to be partitioned
|
||||
* @param pivotIndex1 the index of pivot1, the first pivot
|
||||
* @param pivotIndex2 the index of pivot2, the second pivot
|
||||
*/
|
||||
int[] partition(A a, int low, int high, int pivotIndex1, int pivotIndex2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Partitions the specified range of the array using the two pivots provided.
|
||||
*
|
||||
* @param array the array to be partitioned
|
||||
* @param high the index of the last element, exclusive, to be partitioned
|
||||
* @param pivotIndex1 the index of pivot1, the first pivot
|
||||
* @param pivotIndex2 the index of pivot2, the second pivot
|
||||
* @param po the method reference for the fallback implementation
|
||||
*/
|
||||
private static <A> int[] partition(A array, int low, int high, int pivotIndex1, int pivotIndex2, PartitionOperation<A> po) {
|
||||
return po.partition(array, low, high, pivotIndex1, pivotIndex2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts the specified range of the array Dual-Pivot Quicksort.
|
||||
*
|
||||
* @param a the array to be sorted
|
||||
* @param low the index of the first element, inclusive, to be sorted
|
||||
* @param high the index of the last element, exclusive, to be sorted
|
||||
*/
|
||||
public static void sort(Object[] a, int low, int high) {
|
||||
sort(a, 0, low, high);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts the specified array using the Dual-Pivot Quicksort and/or
|
||||
* other sorts in special-cases, possibly with parallel partitions.
|
||||
*
|
||||
* @param a the array to be sorted
|
||||
* @param bits the combination of recursion depth and bit flag, where
|
||||
* the right bit "0" indicates that array is the leftmost part
|
||||
* @param low the index of the first element, inclusive, to be sorted
|
||||
* @param high the index of the last element, exclusive, to be sorted
|
||||
*/
|
||||
static void sort(Object[] a, int bits, int low, int high) {
|
||||
while (true) {
|
||||
int end = high - 1, size = high - low;
|
||||
/*
|
||||
* Run mixed insertion sort on small non-leftmost parts.
|
||||
*/
|
||||
if (size < MAX_MIXED_INSERTION_SORT_SIZE + bits && (bits & 1) > 0) {
|
||||
mixedInsertionSort(a, low, high);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Invoke insertion sort on small leftmost part.
|
||||
*/
|
||||
if (size < MAX_INSERTION_SORT_SIZE) {
|
||||
insertionSort(a, low, high);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the whole array or large non-leftmost
|
||||
* parts are nearly sorted and then merge runs.
|
||||
*/
|
||||
if ((bits == 0 || size > MIN_TRY_MERGE_SIZE && (bits & 1) > 0)
|
||||
&& tryMergeRuns(a, low, size)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Switch to heap sort if execution
|
||||
* time is becoming quadratic.
|
||||
*/
|
||||
if ((bits += DELTA) > MAX_RECURSION_DEPTH) {
|
||||
heapSort(a, low, high);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Use an inexpensive approximation of the golden ratio
|
||||
* to select five sample elements and determine pivots.
|
||||
*/
|
||||
int step = (size >> 3) * 3 + 3;
|
||||
|
||||
/*
|
||||
* Five elements around (and including) the central element
|
||||
* will be used for pivot selection as described below. The
|
||||
* unequal choice of spacing these elements was empirically
|
||||
* determined to work well on a wide variety of inputs.
|
||||
*/
|
||||
int e1 = low + step;
|
||||
int e5 = end - step;
|
||||
int e3 = (e1 + e5) >>> 1;
|
||||
int e2 = (e1 + e3) >>> 1;
|
||||
int e4 = (e3 + e5) >>> 1;
|
||||
Object a3 = a[e3];
|
||||
|
||||
/*
|
||||
* Sort these elements in place by the combination
|
||||
* of 4-element sorting network and insertion sort.
|
||||
*
|
||||
* 5 ------o-----------o------------
|
||||
* | |
|
||||
* 4 ------|-----o-----o-----o------
|
||||
* | | |
|
||||
* 2 ------o-----|-----o-----o------
|
||||
* | |
|
||||
* 1 ------------o-----o------------
|
||||
*/
|
||||
if (NaturalOrder.INSTANCE.compare(a[e5] , a[e2])<0) {
|
||||
Object t = a[e5];
|
||||
a[e5] = a[e2];
|
||||
a[e2] = t;
|
||||
}
|
||||
if (NaturalOrder.INSTANCE.compare(a[e4] , a[e1])<0) {
|
||||
Object t = a[e4];
|
||||
a[e4] = a[e1];
|
||||
a[e1] = t;
|
||||
}
|
||||
if (NaturalOrder.INSTANCE.compare(a[e5] , a[e4])<0) {
|
||||
Object t = a[e5];
|
||||
a[e5] = a[e4];
|
||||
a[e4] = t;
|
||||
}
|
||||
if (NaturalOrder.INSTANCE.compare(a[e2] , a[e1])<0) {
|
||||
Object t = a[e2];
|
||||
a[e2] = a[e1];
|
||||
a[e1] = t;
|
||||
}
|
||||
if (NaturalOrder.INSTANCE.compare(a[e4] , a[e2])<0) {
|
||||
Object t = a[e4];
|
||||
a[e4] = a[e2];
|
||||
a[e2] = t;
|
||||
}
|
||||
|
||||
if (NaturalOrder.INSTANCE.compare(a3 , a[e2])<0) {
|
||||
if (NaturalOrder.INSTANCE.compare(a3 , a[e1])<0) {
|
||||
a[e3] = a[e2];
|
||||
a[e2] = a[e1];
|
||||
a[e1] = a3;
|
||||
} else {
|
||||
a[e3] = a[e2];
|
||||
a[e2] = a3;
|
||||
}
|
||||
} else if (NaturalOrder.INSTANCE.compare(a3 , a[e4])>0) {
|
||||
if (NaturalOrder.INSTANCE.compare(a3 , a[e5])>0) {
|
||||
a[e3] = a[e4];
|
||||
a[e4] = a[e5];
|
||||
a[e5] = a3;
|
||||
} else {
|
||||
a[e3] = a[e4];
|
||||
a[e4] = a3;
|
||||
}
|
||||
}
|
||||
|
||||
// Pointers
|
||||
int lower; // The index of the last element of the left part
|
||||
int upper; // The index of the first element of the right part
|
||||
|
||||
/*
|
||||
* Partitioning with 2 pivots in case of different elements.
|
||||
*/
|
||||
if (NaturalOrder.INSTANCE.compare(a[e1] , a[e2])<0 && NaturalOrder.INSTANCE.compare(a[e2] , a[e3])<0 && NaturalOrder.INSTANCE.compare(a[e3] , a[e4])<0 && NaturalOrder.INSTANCE.compare(a[e4] , a[e5])<0) {
|
||||
/*
|
||||
* Use the first and fifth of the five sorted elements as
|
||||
* the pivots. These values are inexpensive approximation
|
||||
* of tertiles. Note, that pivot1 < pivot2.
|
||||
*/
|
||||
int[] pivotIndices = partition(a, low, high, e1, e5, DualPivotQuicksort::partitionDualPivot);
|
||||
lower = pivotIndices[0];
|
||||
upper = pivotIndices[1];
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Sort non-left parts recursively,
|
||||
* excluding known pivots.
|
||||
*/
|
||||
sort(a, bits | 1, lower + 1, upper);
|
||||
sort(a, bits | 1, upper + 1, high);
|
||||
|
||||
} else { // Use single pivot in case of many equal elements
|
||||
|
||||
/*
|
||||
* Use the third of the five sorted elements as the pivot.
|
||||
* This value is inexpensive approximation of the median.
|
||||
*/
|
||||
int[] pivotIndices = partition(a, low, high, e3, e3, DualPivotQuicksort::partitionSinglePivot);
|
||||
lower = pivotIndices[0];
|
||||
upper = pivotIndices[1];
|
||||
/*
|
||||
* Sort the right part, excluding
|
||||
* known pivot. All elements from the central part are
|
||||
* equal and therefore already sorted.
|
||||
*/
|
||||
sort(a, bits | 1, upper, high);
|
||||
}
|
||||
high = lower; // Iterate along the left part
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Partitions the specified range of the array using the two pivots provided.
|
||||
*
|
||||
* @param a the array to be partitioned
|
||||
* @param low the index of the first element, inclusive, for partitioning
|
||||
* @param high the index of the last element, exclusive, for partitioning
|
||||
* @param pivotIndex1 the index of pivot1, the first pivot
|
||||
* @param pivotIndex2 the index of pivot2, the second pivot
|
||||
*/
|
||||
private static int[] partitionDualPivot(Object[] a, int low, int high, int pivotIndex1, int pivotIndex2) {
|
||||
int end = high - 1;
|
||||
int lower = low;
|
||||
int upper = end;
|
||||
|
||||
int e1 = pivotIndex1;
|
||||
int e5 = pivotIndex2;
|
||||
Object pivot1 = a[e1];
|
||||
Object pivot2 = a[e5];
|
||||
|
||||
/*
|
||||
* The first and the last elements to be sorted are moved
|
||||
* to the locations formerly occupied by the pivots. When
|
||||
* partitioning is completed, the pivots are swapped back
|
||||
* into their final positions, and excluded from the next
|
||||
* subsequent sorting.
|
||||
*/
|
||||
a[e1] = a[lower];
|
||||
a[e5] = a[upper];
|
||||
|
||||
/*
|
||||
* Skip elements, which are less or greater than the pivots.
|
||||
*/
|
||||
while (NaturalOrder.INSTANCE.compare(a[++lower] , pivot1)<0) ;
|
||||
while (NaturalOrder.INSTANCE.compare(a[--upper] , pivot2)>0) ;
|
||||
|
||||
/*
|
||||
* Backward 3-interval partitioning
|
||||
*
|
||||
* left part central part right part
|
||||
* +------------------------------------------------------------+
|
||||
* | < pivot1 | ? | pivot1 <= && <= pivot2 | > pivot2 |
|
||||
* +------------------------------------------------------------+
|
||||
* ^ ^ ^
|
||||
* | | |
|
||||
* lower k upper
|
||||
*
|
||||
* Invariants:
|
||||
*
|
||||
* all in (low, lower] < pivot1
|
||||
* pivot1 <= all in (k, upper) <= pivot2
|
||||
* all in [upper, end) > pivot2
|
||||
*
|
||||
* Pointer k is the last index of ?-part
|
||||
*/
|
||||
for (int unused = --lower, k = ++upper; --k > lower; ) {
|
||||
Object ak = a[k];
|
||||
|
||||
if (NaturalOrder.INSTANCE.compare(ak , pivot1)<0) { // Move a[k] to the left side
|
||||
while (lower < k) {
|
||||
if (NaturalOrder.INSTANCE.compare(a[++lower] , pivot1)>=0) {
|
||||
if (NaturalOrder.INSTANCE.compare(a[lower] , pivot2)>0) {
|
||||
a[k] = a[--upper];
|
||||
a[upper] = a[lower];
|
||||
} else {
|
||||
a[k] = a[lower];
|
||||
}
|
||||
a[lower] = ak;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (NaturalOrder.INSTANCE.compare(ak , pivot2)>0) { // Move a[k] to the right side
|
||||
a[k] = a[--upper];
|
||||
a[upper] = ak;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Swap the pivots into their final positions.
|
||||
*/
|
||||
a[low] = a[lower];
|
||||
a[lower] = pivot1;
|
||||
a[end] = a[upper];
|
||||
a[upper] = pivot2;
|
||||
|
||||
return new int[]{lower, upper};
|
||||
}
|
||||
|
||||
/**
|
||||
* Partitions the specified range of the array using a single pivot provided.
|
||||
*
|
||||
* @param a the array to be partitioned
|
||||
* @param low the index of the first element, inclusive, for partitioning
|
||||
* @param high the index of the last element, exclusive, for partitioning
|
||||
* @param pivotIndex1 the index of pivot1, the first pivot
|
||||
* @param pivotIndex2 the index of pivot2, the second pivot
|
||||
*/
|
||||
private static int[] partitionSinglePivot(Object[] a, int low, int high, int pivotIndex1, int pivotIndex2) {
|
||||
|
||||
int end = high - 1;
|
||||
int lower = low;
|
||||
int upper = end;
|
||||
int e3 = pivotIndex1;
|
||||
Object pivot = a[e3];
|
||||
|
||||
/*
|
||||
* The first element to be sorted is moved to the
|
||||
* location formerly occupied by the pivot. After
|
||||
* completion of partitioning the pivot is swapped
|
||||
* back into its final position, and excluded from
|
||||
* the next subsequent sorting.
|
||||
*/
|
||||
a[e3] = a[lower];
|
||||
|
||||
/*
|
||||
* Traditional 3-way (Dutch National Flag) partitioning
|
||||
*
|
||||
* left part central part right part
|
||||
* +------------------------------------------------------+
|
||||
* | < pivot | ? | == pivot | > pivot |
|
||||
* +------------------------------------------------------+
|
||||
* ^ ^ ^
|
||||
* | | |
|
||||
* lower k upper
|
||||
*
|
||||
* Invariants:
|
||||
*
|
||||
* all in (low, lower] < pivot
|
||||
* all in (k, upper) == pivot
|
||||
* all in [upper, end] > pivot
|
||||
*
|
||||
* Pointer k is the last index of ?-part
|
||||
*/
|
||||
for (int k = ++upper; --k > lower; ) {
|
||||
Object ak = a[k];
|
||||
|
||||
if (NaturalOrder.INSTANCE.compare(ak , pivot)!=0) {
|
||||
a[k] = pivot;
|
||||
|
||||
if (NaturalOrder.INSTANCE.compare(ak , pivot)<0) { // Move a[k] to the left side
|
||||
while (NaturalOrder.INSTANCE.compare(a[++lower] , pivot)<0) ;
|
||||
|
||||
if (NaturalOrder.INSTANCE.compare(a[lower] , pivot)>0) {
|
||||
a[--upper] = a[lower];
|
||||
}
|
||||
a[lower] = ak;
|
||||
} else { // ak > pivot - Move a[k] to the right side
|
||||
a[--upper] = ak;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Swap the pivot into its final position.
|
||||
*/
|
||||
a[low] = a[lower];
|
||||
a[lower] = pivot;
|
||||
return new int[]{lower, upper};
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts the specified range of the array using mixed insertion sort.
|
||||
* <p>
|
||||
* Mixed insertion sort is combination of simple insertion sort,
|
||||
* pin insertion sort and pair insertion sort.
|
||||
* <p>
|
||||
* In the context of Dual-Pivot Quicksort, the pivot element
|
||||
* from the left part plays the role of sentinel, because it
|
||||
* is less than any elements from the given part. Therefore,
|
||||
* expensive check of the left range can be skipped on each
|
||||
* iteration unless it is the leftmost call.
|
||||
*
|
||||
* @param a the array to be sorted
|
||||
* @param low the index of the first element, inclusive, to be sorted
|
||||
* @param high the index of the last element, exclusive, to be sorted
|
||||
*/
|
||||
private static void mixedInsertionSort(Object[] a, int low, int high) {
|
||||
int size = high - low;
|
||||
int end = high - 3 * ((size >> 5) << 3);
|
||||
if (end == high) {
|
||||
|
||||
/*
|
||||
* Invoke simple insertion sort on tiny array.
|
||||
*/
|
||||
for (int i; ++low < end; ) {
|
||||
Object ai = a[i = low];
|
||||
|
||||
while (NaturalOrder.INSTANCE.compare(ai,a[--i])<0) {
|
||||
a[i + 1] = a[i];
|
||||
}
|
||||
a[i + 1] = ai;
|
||||
}
|
||||
} else {
|
||||
|
||||
/*
|
||||
* Start with pin insertion sort on small part.
|
||||
*
|
||||
* Pin insertion sort is extended simple insertion sort.
|
||||
* The main idea of this sort is to put elements larger
|
||||
* than an element called pin to the end of array (the
|
||||
* proper area for such elements). It avoids expensive
|
||||
* movements of these elements through the whole array.
|
||||
*/
|
||||
Object pin = a[end];
|
||||
|
||||
for (int i, p = high; ++low < end; ) {
|
||||
Object ai = a[i = low];
|
||||
|
||||
if (NaturalOrder.INSTANCE.compare(ai,a[i - 1])<0) { // Small element
|
||||
|
||||
/*
|
||||
* Insert small element into sorted part.
|
||||
*/
|
||||
a[i] = a[--i];
|
||||
|
||||
while (NaturalOrder.INSTANCE.compare(ai,a[--i])<0) {
|
||||
a[i + 1] = a[i];
|
||||
}
|
||||
a[i + 1] = ai;
|
||||
|
||||
} else if (p > i && NaturalOrder.INSTANCE.compare(ai,pin)>0) { // Large element
|
||||
|
||||
/*
|
||||
* Find element smaller than pin.
|
||||
*/
|
||||
while (NaturalOrder.INSTANCE.compare(a[--p] , pin)>0) ;
|
||||
|
||||
/*
|
||||
* Swap it with large element.
|
||||
*/
|
||||
if (p > i) {
|
||||
ai = a[p];
|
||||
a[p] = a[i];
|
||||
}
|
||||
|
||||
/*
|
||||
* Insert small element into sorted part.
|
||||
*/
|
||||
while (NaturalOrder.INSTANCE.compare(ai , a[--i])<0) {
|
||||
a[i + 1] = a[i];
|
||||
}
|
||||
a[i + 1] = ai;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Continue with pair insertion sort on remain part.
|
||||
*/
|
||||
for (int i; low < high; ++low) {
|
||||
Object a1 = a[i = low], a2 = a[++low];
|
||||
|
||||
/*
|
||||
* Insert two elements per iteration: at first, insert the
|
||||
* larger element and then insert the smaller element, but
|
||||
* from the position where the larger element was inserted.
|
||||
*/
|
||||
if (NaturalOrder.INSTANCE.compare(a1 , a2)>0) {
|
||||
|
||||
while (NaturalOrder.INSTANCE.compare(a1 , a[--i])<0) {
|
||||
a[i + 2] = a[i];
|
||||
}
|
||||
a[++i + 1] = a1;
|
||||
|
||||
while (NaturalOrder.INSTANCE.compare(a2 , a[--i])<0) {
|
||||
a[i + 1] = a[i];
|
||||
}
|
||||
a[i + 1] = a2;
|
||||
|
||||
} else if (NaturalOrder.INSTANCE.compare(a1 , a[i - 1])<0) {
|
||||
|
||||
while (NaturalOrder.INSTANCE.compare(a2 , a[--i])<0) {
|
||||
a[i + 2] = a[i];
|
||||
}
|
||||
a[++i + 1] = a2;
|
||||
|
||||
while (NaturalOrder.INSTANCE.compare(a1 , a[--i])<0) {
|
||||
a[i + 1] = a[i];
|
||||
}
|
||||
a[i + 1] = a1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts the specified range of the array using insertion sort.
|
||||
*
|
||||
* @param a the array to be sorted
|
||||
* @param low the index of the first element, inclusive, to be sorted
|
||||
* @param high the index of the last element, exclusive, to be sorted
|
||||
*/
|
||||
private static void insertionSort(Object[] a, int low, int high) {
|
||||
for (int i, k = low; ++k < high; ) {
|
||||
Object ai = a[i = k];
|
||||
|
||||
if (NaturalOrder.INSTANCE.compare(ai , a[i - 1])<0) {
|
||||
while (--i >= low && NaturalOrder.INSTANCE.compare(ai , a[i])<0) {
|
||||
a[i + 1] = a[i];
|
||||
}
|
||||
a[i + 1] = ai;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts the specified range of the array using heap sort.
|
||||
*
|
||||
* @param a the array to be sorted
|
||||
* @param low the index of the first element, inclusive, to be sorted
|
||||
* @param high the index of the last element, exclusive, to be sorted
|
||||
*/
|
||||
private static void heapSort(Object[] a, int low, int high) {
|
||||
for (int k = (low + high) >>> 1; k > low; ) {
|
||||
pushDown(a, --k, a[k], low, high);
|
||||
}
|
||||
while (--high > low) {
|
||||
Object max = a[low];
|
||||
pushDown(a, low, a[high], low, high);
|
||||
a[high] = max;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pushes specified element down during heap sort.
|
||||
*
|
||||
* @param a the given array
|
||||
* @param p the start index
|
||||
* @param value the given element
|
||||
* @param low the index of the first element, inclusive, to be sorted
|
||||
* @param high the index of the last element, exclusive, to be sorted
|
||||
*/
|
||||
private static void pushDown(Object[] a, int p, Object value, int low, int high) {
|
||||
for (int k; ; a[p] = a[p = k]) {
|
||||
k = (p << 1) - low + 2; // Index of the right child
|
||||
|
||||
if (k > high) {
|
||||
break;
|
||||
}
|
||||
if (k == high || NaturalOrder.INSTANCE.compare(a[k] , a[k - 1])<0) {
|
||||
--k;
|
||||
}
|
||||
if (NaturalOrder.INSTANCE.compare(a[k] , value)<=0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
a[p] = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to sort the specified range of the array.
|
||||
*
|
||||
* @param a the array to be sorted
|
||||
* @param low the index of the first element to be sorted
|
||||
* @param size the array size
|
||||
* @return true if finally sorted, false otherwise
|
||||
*/
|
||||
private static boolean tryMergeRuns(Object[] a, int low, int size) {
|
||||
|
||||
/*
|
||||
* The run array is constructed only if initial runs are
|
||||
* long enough to continue, run[i] then holds start index
|
||||
* of the i-th sequence of elements in non-descending order.
|
||||
*/
|
||||
int[] run = null;
|
||||
int high = low + size;
|
||||
int count = 1, last = low;
|
||||
|
||||
/*
|
||||
* Identify all possible runs.
|
||||
*/
|
||||
for (int k = low + 1; k < high; ) {
|
||||
|
||||
/*
|
||||
* Find the end index of the current run.
|
||||
*/
|
||||
if (NaturalOrder.INSTANCE.compare(a[k - 1] , a[k])<0) {
|
||||
|
||||
// Identify ascending sequence
|
||||
while (++k < high && NaturalOrder.INSTANCE.compare(a[k - 1] , a[k])<=0) ;
|
||||
|
||||
} else if (NaturalOrder.INSTANCE.compare(a[k - 1] , a[k])>0) {
|
||||
|
||||
// Identify descending sequence
|
||||
while (++k < high && NaturalOrder.INSTANCE.compare(a[k - 1] , a[k])>=0) ;
|
||||
|
||||
// Reverse into ascending order
|
||||
for (int i = last - 1, j = k; ++i < --j && NaturalOrder.INSTANCE.compare(a[i] , a[j])>0; ) {
|
||||
Object ai = a[i];
|
||||
a[i] = a[j];
|
||||
a[j] = ai;
|
||||
}
|
||||
} else { // Identify constant sequence
|
||||
for (Object ak = a[k]; ++k < high && NaturalOrder.INSTANCE.compare(ak , a[k])==0; ) ;
|
||||
|
||||
if (k < high) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check special cases.
|
||||
*/
|
||||
if (run == null) {
|
||||
if (k == high) {
|
||||
|
||||
/*
|
||||
* The array is monotonous sequence,
|
||||
* and therefore already sorted.
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
if (k - low < MIN_FIRST_RUN_SIZE) {
|
||||
|
||||
/*
|
||||
* The first run is too small
|
||||
* to proceed with scanning.
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
|
||||
run = new int[((size >> 10) | 0x7F) & 0x3FF];
|
||||
run[0] = low;
|
||||
|
||||
} else if (NaturalOrder.INSTANCE.compare(a[last - 1] , a[last])>0) {
|
||||
|
||||
if (count > (k - low) >> MIN_FIRST_RUNS_FACTOR) {
|
||||
|
||||
/*
|
||||
* The first runs are not long
|
||||
* enough to continue scanning.
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
|
||||
if (++count == MAX_RUN_CAPACITY) {
|
||||
|
||||
/*
|
||||
* Array is not highly structured.
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
|
||||
if (count == run.length) {
|
||||
|
||||
/*
|
||||
* Increase capacity of index array.
|
||||
*/
|
||||
run = Arrays.copyOf(run, count << 1);
|
||||
}
|
||||
}
|
||||
run[count] = (last = k);
|
||||
}
|
||||
|
||||
/*
|
||||
* Merge runs of highly structured array.
|
||||
*/
|
||||
if (count > 1) {
|
||||
Object[] b;
|
||||
int offset = low;
|
||||
|
||||
b = new Object[size];
|
||||
mergeRuns(a, b, offset, 1, run, 0, count);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges the specified runs.
|
||||
*
|
||||
* @param a the source array
|
||||
* @param b the temporary buffer used in merging
|
||||
* @param offset the start index in the source, inclusive
|
||||
* @param aim specifies merging: to source ( > 0), buffer ( < 0) or any ( == 0)
|
||||
* @param run the start indexes of the runs, inclusive
|
||||
* @param lo the start index of the first run, inclusive
|
||||
* @param hi the start index of the last run, inclusive
|
||||
* @return the destination where runs are merged
|
||||
*/
|
||||
private static Object[] mergeRuns(Object[] a, Object[] b, int offset,
|
||||
int aim, int[] run, int lo, int hi) {
|
||||
|
||||
if (hi - lo == 1) {
|
||||
if (aim >= 0) {
|
||||
return a;
|
||||
}
|
||||
for (int i = run[hi], j = i - offset, low = run[lo]; i > low;
|
||||
b[--j] = a[--i]
|
||||
)
|
||||
;
|
||||
return b;
|
||||
}
|
||||
|
||||
/*
|
||||
* Split into approximately equal parts.
|
||||
*/
|
||||
int mi = lo, rmi = (run[lo] + run[hi]) >>> 1;
|
||||
while (run[++mi + 1] <= rmi) ;
|
||||
|
||||
/*
|
||||
* Merge the left and right parts.
|
||||
*/
|
||||
Object[] a1, a2;
|
||||
|
||||
a1 = mergeRuns(a, b, offset, -aim, run, lo, mi);
|
||||
a2 = mergeRuns(a, b, offset, 0, run, mi, hi);
|
||||
|
||||
Object[] dst = a1 == a ? b : a;
|
||||
|
||||
int k = a1 == a ? run[lo] - offset : run[lo];
|
||||
int lo1 = a1 == b ? run[lo] - offset : run[lo];
|
||||
int hi1 = a1 == b ? run[mi] - offset : run[mi];
|
||||
int lo2 = a2 == b ? run[mi] - offset : run[mi];
|
||||
int hi2 = a2 == b ? run[hi] - offset : run[hi];
|
||||
|
||||
mergeParts(dst, k, a1, lo1, hi1, a2, lo2, hi2);
|
||||
return dst;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges the sorted parts.
|
||||
*
|
||||
* @param dst the destination where parts are merged
|
||||
* @param k the start index of the destination, inclusive
|
||||
* @param a1 the first part
|
||||
* @param lo1 the start index of the first part, inclusive
|
||||
* @param hi1 the end index of the first part, exclusive
|
||||
* @param a2 the second part
|
||||
* @param lo2 the start index of the second part, inclusive
|
||||
* @param hi2 the end index of the second part, exclusive
|
||||
*/
|
||||
private static void mergeParts(Object[] dst, int k,
|
||||
Object[] a1, int lo1, int hi1, Object[] a2, int lo2, int hi2) {
|
||||
|
||||
|
||||
/*
|
||||
* Merge small parts sequentially.
|
||||
*/
|
||||
while (lo1 < hi1 && lo2 < hi2) {
|
||||
dst[k++] = NaturalOrder.INSTANCE.compare(a1[lo1] , a2[lo2])<0 ? a1[lo1++] : a2[lo2++];
|
||||
}
|
||||
if (dst != a1 || k < lo1) {
|
||||
while (lo1 < hi1) {
|
||||
dst[k++] = a1[lo1++];
|
||||
}
|
||||
}
|
||||
if (dst != a2 || k < lo2) {
|
||||
while (lo2 < hi2) {
|
||||
dst[k++] = a2[lo2++];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// [long]
|
||||
|
||||
// [byte]
|
||||
|
||||
// [char]
|
||||
|
||||
// [short]
|
||||
|
||||
// [float]
|
||||
|
||||
// [double]
|
||||
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
package de.uni_marburg.powersort.FinnSort;
|
||||
|
||||
import de.uni_marburg.powersort.JUnitUtil;
|
||||
import de.uni_marburg.powersort.benchmark.NaturalOrder;
|
||||
import org.junit.jupiter.api.RepeatedTest;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.converter.ConvertWith;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
public class FasterFinnSortMockitoTest {
|
||||
@ParameterizedTest
|
||||
@CsvSource({
|
||||
"4|5|6|1|2|3",
|
||||
"100|10|11|1|2|3",
|
||||
"100|11|10|3|2|1",
|
||||
"24|25|26|27|28|21|22|23|18|19|20|4|5|6|7|8|9|10|11|12|13|14|15|16|17|3|1|2",
|
||||
})
|
||||
void foo(@ConvertWith(JUnitUtil.IntegerArrayConverter.class) Integer[] a) {
|
||||
printPowers(a);
|
||||
}
|
||||
|
||||
void printPowers(Object[] a) {
|
||||
FasterFinnSort<Object> ffs = new FasterFinnSort<>(a, NaturalOrder.INSTANCE, null, 0, 0, a.length);
|
||||
FasterFinnSort<Object> spiedFfs = spy(ffs);
|
||||
|
||||
// Don't extend short runs with `binarySort()`
|
||||
when(spiedFfs.minRunLength(anyInt())).thenReturn(0);
|
||||
|
||||
// Capture calculated power values.
|
||||
final ResultCaptor<Integer> resultCaptor = new ResultCaptor<>();
|
||||
doAnswer(resultCaptor).when(spiedFfs).power(anyInt(), anyInt());
|
||||
|
||||
|
||||
sort(a, spiedFfs);
|
||||
|
||||
|
||||
List<Integer> calculatedPowers = resultCaptor.getAllValues();
|
||||
System.out.println(Arrays.toString(calculatedPowers.toArray()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts the given array using FasterFinnSort.
|
||||
*/
|
||||
void sort(Object[] a, FasterFinnSort<Object> ffs) {
|
||||
// Don't sort short array with `binarySort()`
|
||||
boolean forcePowersortShortArray = true;
|
||||
|
||||
//FasterFinnSortWrapper.sort(a, 0, a.length, NaturalOrder.INSTANCE, null, 0, 0, ffs, forcePowersortShortArray);
|
||||
}
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
/*
|
||||
package de.uni_marburg.powersort.FinnSort;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
import static de.uni_marburg.powersort.FinnSort.FasterFinnSort.MIN_MERGE;
|
||||
import static de.uni_marburg.powersort.FinnSort.FasterFinnSort.binarySort;
|
||||
import static de.uni_marburg.powersort.FinnSort.FasterFinnSort.countRunAndMakeAscending;
|
||||
|
||||
public class FasterFinnSortWrapper {
|
||||
/**
|
||||
* Copy of `FasterFinnSort.java`. Param `ts` has been added to allow dependency injection for testing.
|
||||
*
|
||||
* @param fs: If non-null, the creation of `ts` with `new FasterFinnSort<>(a, c, work, workBase, workLen)` in this methods body is skipped. Instead, the given `ts` is used.
|
||||
*
|
||||
public static <T> void sort(T[] a, int lo, int hi, Comparator<? super T> c,
|
||||
T[] work, int workBase, int workLen,
|
||||
FasterFinnSort<T> fs, boolean forcePowersortShortArray) {
|
||||
assert c != null && a != null && lo >= 0 && lo <= hi && hi <= a.length;
|
||||
int nRemaining = hi - lo;
|
||||
if (nRemaining < 2)
|
||||
return; // Arrays of size 0 and 1 are always sorted
|
||||
|
||||
// If array is small, do a "mini-TimSort" with no merges
|
||||
|
||||
if (!forcePowersortShortArray && nRemaining < MIN_MERGE) {
|
||||
int initRunLen = countRunAndMakeAscending(a, lo, hi, c);
|
||||
binarySort(a, lo, hi, lo + initRunLen, c);
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* March over the array once, left to right, finding natural runs,
|
||||
* extending short natural runs to minRun elements, and merging runs
|
||||
* to maintain stack invariant.
|
||||
*
|
||||
if (fs == null) fs = new FasterFinnSort<>(a, c, work, workBase, workLen, hi - lo);
|
||||
int minRun = fs.minRunLength(nRemaining);
|
||||
do {
|
||||
// Identify next run
|
||||
int runLen = countRunAndMakeAscending(a, lo, hi, c);
|
||||
|
||||
// If run is short, extend to min(minRun, nRemaining)
|
||||
if (runLen < minRun) {
|
||||
int force = nRemaining <= minRun ? nRemaining : minRun;
|
||||
binarySort(a, lo, lo + force, lo + runLen, c);
|
||||
runLen = force;
|
||||
}
|
||||
|
||||
// Push run onto pending-run stack, and maybe merge
|
||||
fs.pushRun(lo, runLen);
|
||||
fs.mergeCollapse();
|
||||
|
||||
// Advance to find next run
|
||||
lo += runLen;
|
||||
nRemaining -= runLen;
|
||||
} while (nRemaining != 0);
|
||||
|
||||
// Merge all remaining runs to complete sort
|
||||
assert lo == hi;
|
||||
fs.mergeForceCollapse();
|
||||
assert fs.stackSize == 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
*/
|
@ -0,0 +1,31 @@
|
||||
package de.uni_marburg.powersort.FinnSort;
|
||||
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* Source: https://stackoverflow.com/a/25694142/6334421
|
||||
* <p>
|
||||
* Alternative which didn't work: https://github.com/mockito/mockito/issues/2422
|
||||
*/
|
||||
public class ResultCaptor<T> implements Answer {
|
||||
private final List<T> results = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Returns all captured values.
|
||||
*/
|
||||
public List<T> getAllValues() {
|
||||
return results;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T answer(InvocationOnMock invocationOnMock) throws Throwable {
|
||||
T result = (T) invocationOnMock.callRealMethod();
|
||||
results.add(result);
|
||||
return result;
|
||||
}
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
package de.uni_marburg.powersort.MSort;
|
||||
|
||||
import static de.uni_marburg.powersort.MSort.IMPL_M_3.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import de.uni_marburg.powersort.benchmark.NaturalOrder;
|
||||
|
||||
public class PowerSortT {
|
||||
|
||||
public static void main(String[] args) {
|
||||
testFillWithAscRunsHighToLow();
|
||||
//testMerge();
|
||||
// testMergeInplace();
|
||||
testExtendRun();
|
||||
testPower();
|
||||
testPowerFast();
|
||||
testMergeTopmost2();
|
||||
testPowerSort();
|
||||
}
|
||||
|
||||
// Test for fillWithAscRunsHighToLow
|
||||
public static void testFillWithAscRunsHighToLow() {
|
||||
Integer[] A = new Integer[10];
|
||||
int[] runLengths = {2, 3, 5};
|
||||
int runLenFactor = 1;
|
||||
fillWithAscRunsHighToLow(A, runLengths, runLenFactor);
|
||||
System.out.println("Test fillWithAscRunsHighToLow: " + A);
|
||||
}
|
||||
|
||||
// Test for merge
|
||||
// public static void testMerge() {
|
||||
// Integer[] run1 ={1,4,6};
|
||||
// Integer []run2 = {2, 3, 5};
|
||||
// Integer[] result = merge(run1, run2, NaturalOrder.INSTANCE);
|
||||
// System.out.println("Test merge: " + result);
|
||||
// }
|
||||
|
||||
// Test for mergeInplace
|
||||
// public static void testMergeInplace() {
|
||||
// Integer[] A = {1,4,6,2,3,5};
|
||||
// mergeInplace(A, 0, 3, 6,NaturalOrder.INSTANCE);
|
||||
// System.out.println("Test mergeInplace: " + A);
|
||||
// }
|
||||
|
||||
// Test for extendRun
|
||||
public static void testExtendRun() {
|
||||
Integer [] A = {1, 2, 3, 6, 5, 4};
|
||||
// int endIndex = extendRun(A, 0,NaturalOrder.INSTANCE);
|
||||
// System.out.println("Test extendRun (from 0): " + endIndex);
|
||||
System.out.println("Modified List: " + A);
|
||||
}
|
||||
|
||||
// Test for power
|
||||
public static void testPower() {
|
||||
int[] run1 = {0, 3};
|
||||
int[] run2 = {3, 3};
|
||||
int n = 6;
|
||||
// int powerValue = power(run1, run2, n);
|
||||
// System.out.println("Test power: " + powerValue);
|
||||
}
|
||||
|
||||
// Test for powerFast
|
||||
public static void testPowerFast() {
|
||||
int[] run1 = {0, 3};
|
||||
int[] run2 = {3, 3};
|
||||
int n = 6;
|
||||
// int powerFastValue = powerFast(run1, run2, n);
|
||||
// System.out.println("Test powerFast: " + powerFastValue);
|
||||
}
|
||||
|
||||
// Test for mergeTopmost2
|
||||
public static void testMergeTopmost2() {
|
||||
Integer[] A = {1, 3, 5, 2, 4, 6};
|
||||
List<int[]> runs = new ArrayList<>();
|
||||
runs.add(new int[]{0, 3, 1});
|
||||
runs.add(new int[]{3, 3, 1});
|
||||
// mergeTopmost2(A, runs,NaturalOrder.INSTANCE);
|
||||
System.out.println("Test mergeTopmost2: " + A);
|
||||
}
|
||||
|
||||
// Test for powerSort
|
||||
public static void testPowerSort() {
|
||||
Integer [] A = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
|
||||
powerSort(A, NaturalOrder.INSTANCE);
|
||||
System.out.println("Test powerSort: " + A);
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
package de.uni_marburg.powersort.MSort;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import de.uni_marburg.powersort.benchmark.NaturalOrder;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
//import static de.uni_marburg.powersort.MSort.IMPL_M_3.MERGE_COST;
|
||||
import static de.uni_marburg.powersort.MSort.IMPL_M_3.fillWithAscRunsHighToLow;
|
||||
import static de.uni_marburg.powersort.MSort.IMPL_M_3.powerSort;
|
||||
|
||||
class PowerSortTest {
|
||||
@Test
|
||||
public void testWithOneInputList() {
|
||||
// List<Integer> list = new ArrayList<>(List.of(5, 2, 8, 12, 1, 6));
|
||||
// extendRun(list, 0);
|
||||
//System.out.println(list);
|
||||
// example from slides he wrote this
|
||||
// int[] runs = {5, 3, 3, 14, 1, 2};
|
||||
int [] runs = new int[]{9, 16, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7};
|
||||
int totalSize = Arrays.stream(runs).sum();
|
||||
|
||||
Integer[] a = new Integer[totalSize];
|
||||
|
||||
System.out.println();
|
||||
fillWithAscRunsHighToLow(a, runs, 1);
|
||||
//MERGE_COST = 0;
|
||||
System.out.println("Sorting with Powersort:");
|
||||
powerSort(a,NaturalOrder.INSTANCE);
|
||||
System.out.println("Sorted Array"+Arrays.toString(a));
|
||||
// System.out.println("Merge cost: " + MERGE_COST);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWithFinnInputList() {
|
||||
Integer [] numbers = new Integer[] {24, 25, 26, 27, 28, 21, 22, 23, 18, 19, 20, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 3, 1, 2};
|
||||
|
||||
System.out.println("Original array"+Arrays.toString(numbers));
|
||||
powerSort(numbers, NaturalOrder.INSTANCE);
|
||||
System.out.println("Result: "+Arrays.toString(numbers));
|
||||
// System.out.println(new ArrayList<>(List.of(numbers)));
|
||||
}
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
package de.uni_marburg.powersort.msort;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static de.uni_marburg.powersort.msort.IMPL_M_1.MERGE_COST;
|
||||
import static de.uni_marburg.powersort.msort.IMPL_M_1.extendRun;
|
||||
import static de.uni_marburg.powersort.msort.IMPL_M_1.fillWithAscRunsHighToLow;
|
||||
import static de.uni_marburg.powersort.msort.IMPL_M_1.powerSort;
|
||||
|
||||
class PowerSortTest {
|
||||
@Test
|
||||
|
||||
public void testWithOneInputList() {
|
||||
List<Integer> list = new ArrayList<>(List.of(5, 2, 8, 12, 1, 6));
|
||||
extendRun(list, 0);
|
||||
System.out.println(list);
|
||||
// example from slides he wrote this
|
||||
int[] runs = {5, 3, 3, 14, 1, 2};
|
||||
// runs = new int[]{9, 16, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7};
|
||||
|
||||
List<Integer> a = new ArrayList<>(IntStream.range(0, Arrays.stream(runs).sum()).boxed().collect(Collectors.toList()));
|
||||
|
||||
System.out.println();
|
||||
fillWithAscRunsHighToLow(a, runs, 1);
|
||||
MERGE_COST = 0;
|
||||
System.out.println("Sorting with Powersort:");
|
||||
powerSort(a);
|
||||
System.out.println("Merge cost: " + MERGE_COST);
|
||||
}
|
||||
|
||||
}
|
@ -4,4 +4,4 @@ public class ASortTest extends AbstractSortTest {
|
||||
ASortTest() {
|
||||
sortAlg = SortEnum.ASORT;
|
||||
}
|
||||
}
|
||||
}
|
@ -30,11 +30,15 @@ public abstract class AbstractSortTest {
|
||||
@CsvSource({
|
||||
"3,7,-13",
|
||||
"3,7,-3",
|
||||
"10,10,-5",
|
||||
"17,17,-17",
|
||||
"2,128,-64", // Large runs to trigger function `mergeRuns` of DualPivotQuicksort
|
||||
})
|
||||
void test2(int numOfRuns, int runLength, int decreaseBetweenRuns) {
|
||||
Integer[] array = AscendingRuns.newAscendingRuns(numOfRuns, runLength, decreaseBetweenRuns).getCopy();
|
||||
sortAndCheckResult(array);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@CsvSource({
|
||||
"2",
|
||||
@ -42,16 +46,16 @@ public abstract class AbstractSortTest {
|
||||
"13",
|
||||
"1337",
|
||||
})
|
||||
void test2(int size) {
|
||||
void test3(int size) {
|
||||
Integer[] array = new DescendingIntegers(size).getCopy();
|
||||
sortAndCheckResult(array);
|
||||
}
|
||||
|
||||
void sortAndCheckResult(Integer[] array){
|
||||
void sortAndCheckResult(Integer[] array) {
|
||||
Integer[] expected = Arrays.copyOf(array, array.length);
|
||||
Arrays.sort(expected);
|
||||
|
||||
sortAlg.getSortImpl().sort(array);
|
||||
assertArrayEquals(expected, array);
|
||||
assertArrayEquals(expected, array, Arrays.toString(array) + "\n\n");
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,7 @@
|
||||
package de.uni_marburg.powersort.sort;
|
||||
|
||||
public class BubbleSortTest extends AbstractSortTest {
|
||||
BubbleSortTest() {
|
||||
sortAlg = SortEnum.BUBBLE_SORT;
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package de.uni_marburg.powersort.sort;
|
||||
|
||||
public class DualPivotQuicksortTest extends AbstractSortTest {
|
||||
DualPivotQuicksortTest() {
|
||||
sortAlg = SortEnum.DPQS;
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package de.uni_marburg.powersort.sort;
|
||||
|
||||
import de.uni_marburg.powersort.FinnSort.FasterFinnSort;
|
||||
import de.uni_marburg.powersort.FinnSort.Run;
|
||||
import de.uni_marburg.powersort.benchmark.NaturalOrder;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import static java.lang.Math.pow;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class FasterFinnSortTest extends AbstractSortTest {
|
||||
FasterFinnSortTest() {
|
||||
sortAlg = SortEnum.FASTER_FINN_SORT;
|
||||
}
|
||||
|
||||
}
|
@ -4,4 +4,4 @@ public class FinnSortTest extends AbstractSortTest {
|
||||
FinnSortTest() {
|
||||
sortAlg = SortEnum.FINN_SORT;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package de.uni_marburg.powersort.sort;
|
||||
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
|
||||
@Disabled("Disabled in favor of IMPL_M_2")
|
||||
public class IMPL_M_1Test extends AbstractSortTest {
|
||||
IMPL_M_1Test() {
|
||||
sortAlg = SortEnum.IMPL_M_10;
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package de.uni_marburg.powersort.sort;
|
||||
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
|
||||
@Disabled("Disabled in favor of IMPL_M_3")
|
||||
public class IMPL_M_2Test extends AbstractSortTest {
|
||||
IMPL_M_2Test() {
|
||||
sortAlg = SortEnum.IMPL_M_20;
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package de.uni_marburg.powersort.sort;
|
||||
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
|
||||
@Disabled("Disabled in favor of IMPL_M_4")
|
||||
public class IMPL_M_3Test extends AbstractSortTest {
|
||||
IMPL_M_3Test() {
|
||||
sortAlg = SortEnum.IMPL_M_30;
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package de.uni_marburg.powersort.sort;
|
||||
|
||||
public class IMPL_M_4Test extends AbstractSortTest {
|
||||
IMPL_M_4Test() {
|
||||
sortAlg = SortEnum.IMPL_M_40;
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package de.uni_marburg.powersort.sort;
|
||||
|
||||
public class QuickSortTest extends AbstractSortTest {
|
||||
QuickSortTest() {
|
||||
sortAlg = SortEnum.ASORT;
|
||||
}
|
||||
}
|
@ -2,9 +2,9 @@
|
||||
# https://docs.gradle.org/current/userguide/platforms.html#sub::toml-dependencies-format
|
||||
|
||||
[versions]
|
||||
guava = "32.1.3-jre"
|
||||
junit-jupiter = "5.10.1"
|
||||
junit-jupiter = "5.10.3"
|
||||
mockito = "5.14.0"
|
||||
|
||||
[libraries]
|
||||
guava = { module = "com.google.guava:guava", version.ref = "guava" }
|
||||
junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit-jupiter" }
|
||||
mockito = { module = "org.mockito:mockito-core", version.ref = "mockito" }
|
||||
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
7
gradlew
vendored
7
gradlew
vendored
@ -15,6 +15,8 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
@ -55,7 +57,7 @@
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
@ -84,7 +86,8 @@ done
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
|
||||
' "$PWD" ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
|
2
gradlew.bat
vendored
2
gradlew.bat
vendored
@ -13,6 +13,8 @@
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
@rem SPDX-License-Identifier: Apache-2.0
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
|
BIN
intellij-jmh.png
Normal file
BIN
intellij-jmh.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
26
nix/sources.json
Normal file
26
nix/sources.json
Normal file
@ -0,0 +1,26 @@
|
||||
{
|
||||
"nixpkgs": {
|
||||
"branch": "nixos-24.11",
|
||||
"description": "Nix Packages collection & NixOS",
|
||||
"homepage": "",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "cbd8ec4de4469333c82ff40d057350c30e9f7d36",
|
||||
"sha256": "1bcgis62aj9slin2cm2h7ywqq4qswh4kzdlx0zgdwz80g4z10f8f",
|
||||
"type": "tarball",
|
||||
"url": "https://github.com/NixOS/nixpkgs/archive/cbd8ec4de4469333c82ff40d057350c30e9f7d36.tar.gz",
|
||||
"url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
|
||||
},
|
||||
"unstable": {
|
||||
"branch": "nixpkgs-unstable",
|
||||
"description": "Nix Packages collection & NixOS",
|
||||
"homepage": "",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "3df3c47c19dc90fec35359e89ffb52b34d2b0e94",
|
||||
"sha256": "1lhlm7czhwwys5ak6ngb5li6bxddilb9479k9nkss502kw8hwjyz",
|
||||
"type": "tarball",
|
||||
"url": "https://github.com/NixOS/nixpkgs/archive/3df3c47c19dc90fec35359e89ffb52b34d2b0e94.tar.gz",
|
||||
"url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
|
||||
}
|
||||
}
|
198
nix/sources.nix
Normal file
198
nix/sources.nix
Normal file
@ -0,0 +1,198 @@
|
||||
# This file has been generated by Niv.
|
||||
|
||||
let
|
||||
|
||||
#
|
||||
# The fetchers. fetch_<type> fetches specs of type <type>.
|
||||
#
|
||||
|
||||
fetch_file = pkgs: name: spec:
|
||||
let
|
||||
name' = sanitizeName name + "-src";
|
||||
in
|
||||
if spec.builtin or true then
|
||||
builtins_fetchurl { inherit (spec) url sha256; name = name'; }
|
||||
else
|
||||
pkgs.fetchurl { inherit (spec) url sha256; name = name'; };
|
||||
|
||||
fetch_tarball = pkgs: name: spec:
|
||||
let
|
||||
name' = sanitizeName name + "-src";
|
||||
in
|
||||
if spec.builtin or true then
|
||||
builtins_fetchTarball { name = name'; inherit (spec) url sha256; }
|
||||
else
|
||||
pkgs.fetchzip { name = name'; inherit (spec) url sha256; };
|
||||
|
||||
fetch_git = name: spec:
|
||||
let
|
||||
ref =
|
||||
spec.ref or (
|
||||
if spec ? branch then "refs/heads/${spec.branch}" else
|
||||
if spec ? tag then "refs/tags/${spec.tag}" else
|
||||
abort "In git source '${name}': Please specify `ref`, `tag` or `branch`!"
|
||||
);
|
||||
submodules = spec.submodules or false;
|
||||
submoduleArg =
|
||||
let
|
||||
nixSupportsSubmodules = builtins.compareVersions builtins.nixVersion "2.4" >= 0;
|
||||
emptyArgWithWarning =
|
||||
if submodules
|
||||
then
|
||||
builtins.trace
|
||||
(
|
||||
"The niv input \"${name}\" uses submodules "
|
||||
+ "but your nix's (${builtins.nixVersion}) builtins.fetchGit "
|
||||
+ "does not support them"
|
||||
)
|
||||
{ }
|
||||
else { };
|
||||
in
|
||||
if nixSupportsSubmodules
|
||||
then { inherit submodules; }
|
||||
else emptyArgWithWarning;
|
||||
in
|
||||
builtins.fetchGit
|
||||
({ url = spec.repo; inherit (spec) rev; inherit ref; } // submoduleArg);
|
||||
|
||||
fetch_local = spec: spec.path;
|
||||
|
||||
fetch_builtin-tarball = name: throw
|
||||
''[${name}] The niv type "builtin-tarball" is deprecated. You should instead use `builtin = true`.
|
||||
$ niv modify ${name} -a type=tarball -a builtin=true'';
|
||||
|
||||
fetch_builtin-url = name: throw
|
||||
''[${name}] The niv type "builtin-url" will soon be deprecated. You should instead use `builtin = true`.
|
||||
$ niv modify ${name} -a type=file -a builtin=true'';
|
||||
|
||||
#
|
||||
# Various helpers
|
||||
#
|
||||
|
||||
# https://github.com/NixOS/nixpkgs/pull/83241/files#diff-c6f540a4f3bfa4b0e8b6bafd4cd54e8bR695
|
||||
sanitizeName = name:
|
||||
(
|
||||
concatMapStrings (s: if builtins.isList s then "-" else s)
|
||||
(
|
||||
builtins.split "[^[:alnum:]+._?=-]+"
|
||||
((x: builtins.elemAt (builtins.match "\\.*(.*)" x) 0) name)
|
||||
)
|
||||
);
|
||||
|
||||
# The set of packages used when specs are fetched using non-builtins.
|
||||
mkPkgs = sources: system:
|
||||
let
|
||||
sourcesNixpkgs =
|
||||
import (builtins_fetchTarball { inherit (sources.nixpkgs) url sha256; }) { inherit system; };
|
||||
hasNixpkgsPath = builtins.any (x: x.prefix == "nixpkgs") builtins.nixPath;
|
||||
hasThisAsNixpkgsPath = <nixpkgs> == ./.;
|
||||
in
|
||||
if builtins.hasAttr "nixpkgs" sources
|
||||
then sourcesNixpkgs
|
||||
else if hasNixpkgsPath && ! hasThisAsNixpkgsPath then
|
||||
import <nixpkgs> { }
|
||||
else
|
||||
abort
|
||||
''
|
||||
Please specify either <nixpkgs> (through -I or NIX_PATH=nixpkgs=...) or
|
||||
add a package called "nixpkgs" to your sources.json.
|
||||
'';
|
||||
|
||||
# The actual fetching function.
|
||||
fetch = pkgs: name: spec:
|
||||
|
||||
if ! builtins.hasAttr "type" spec then
|
||||
abort "ERROR: niv spec ${name} does not have a 'type' attribute"
|
||||
else if spec.type == "file" then fetch_file pkgs name spec
|
||||
else if spec.type == "tarball" then fetch_tarball pkgs name spec
|
||||
else if spec.type == "git" then fetch_git name spec
|
||||
else if spec.type == "local" then fetch_local spec
|
||||
else if spec.type == "builtin-tarball" then fetch_builtin-tarball name
|
||||
else if spec.type == "builtin-url" then fetch_builtin-url name
|
||||
else
|
||||
abort "ERROR: niv spec ${name} has unknown type ${builtins.toJSON spec.type}";
|
||||
|
||||
# If the environment variable NIV_OVERRIDE_${name} is set, then use
|
||||
# the path directly as opposed to the fetched source.
|
||||
replace = name: drv:
|
||||
let
|
||||
saneName = stringAsChars (c: if (builtins.match "[a-zA-Z0-9]" c) == null then "_" else c) name;
|
||||
ersatz = builtins.getEnv "NIV_OVERRIDE_${saneName}";
|
||||
in
|
||||
if ersatz == "" then drv else
|
||||
# this turns the string into an actual Nix path (for both absolute and
|
||||
# relative paths)
|
||||
if builtins.substring 0 1 ersatz == "/" then /. + ersatz else /. + builtins.getEnv "PWD" + "/${ersatz}";
|
||||
|
||||
# Ports of functions for older nix versions
|
||||
|
||||
# a Nix version of mapAttrs if the built-in doesn't exist
|
||||
mapAttrs = builtins.mapAttrs or (
|
||||
f: set: with builtins;
|
||||
listToAttrs (map (attr: { name = attr; value = f attr set.${attr}; }) (attrNames set))
|
||||
);
|
||||
|
||||
# https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/lists.nix#L295
|
||||
range = first: last: if first > last then [ ] else builtins.genList (n: first + n) (last - first + 1);
|
||||
|
||||
# https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L257
|
||||
stringToCharacters = s: map (p: builtins.substring p 1 s) (range 0 (builtins.stringLength s - 1));
|
||||
|
||||
# https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L269
|
||||
stringAsChars = f: s: concatStrings (map f (stringToCharacters s));
|
||||
concatMapStrings = f: list: concatStrings (map f list);
|
||||
concatStrings = builtins.concatStringsSep "";
|
||||
|
||||
# https://github.com/NixOS/nixpkgs/blob/8a9f58a375c401b96da862d969f66429def1d118/lib/attrsets.nix#L331
|
||||
optionalAttrs = cond: as: if cond then as else { };
|
||||
|
||||
# fetchTarball version that is compatible between all the versions of Nix
|
||||
builtins_fetchTarball = { url, name ? null, sha256 }@attrs:
|
||||
let
|
||||
inherit (builtins) lessThan nixVersion fetchTarball;
|
||||
in
|
||||
if lessThan nixVersion "1.12" then
|
||||
fetchTarball ({ inherit url; } // (optionalAttrs (name != null) { inherit name; }))
|
||||
else
|
||||
fetchTarball attrs;
|
||||
|
||||
# fetchurl version that is compatible between all the versions of Nix
|
||||
builtins_fetchurl = { url, name ? null, sha256 }@attrs:
|
||||
let
|
||||
inherit (builtins) lessThan nixVersion fetchurl;
|
||||
in
|
||||
if lessThan nixVersion "1.12" then
|
||||
fetchurl ({ inherit url; } // (optionalAttrs (name != null) { inherit name; }))
|
||||
else
|
||||
fetchurl attrs;
|
||||
|
||||
# Create the final "sources" from the config
|
||||
mkSources = config:
|
||||
mapAttrs
|
||||
(
|
||||
name: spec:
|
||||
if builtins.hasAttr "outPath" spec
|
||||
then
|
||||
abort
|
||||
"The values in sources.json should not have an 'outPath' attribute"
|
||||
else
|
||||
spec // { outPath = replace name (fetch config.pkgs name spec); }
|
||||
)
|
||||
config.sources;
|
||||
|
||||
# The "config" used by the fetchers
|
||||
mkConfig =
|
||||
{ sourcesFile ? if builtins.pathExists ./sources.json then ./sources.json else null
|
||||
, sources ? if sourcesFile == null then { } else builtins.fromJSON (builtins.readFile sourcesFile)
|
||||
, system ? builtins.currentSystem
|
||||
, pkgs ? mkPkgs sources system
|
||||
}: rec {
|
||||
# The sources, i.e. the attribute set of spec name to spec
|
||||
inherit sources;
|
||||
|
||||
# The "pkgs" (evaluated nixpkgs) to use for e.g. non-builtin fetchers
|
||||
inherit pkgs;
|
||||
};
|
||||
|
||||
in
|
||||
mkSources (mkConfig { }) // { __functor = _: settings: mkSources (mkConfig settings); }
|
40
shell.nix
40
shell.nix
@ -1,17 +1,39 @@
|
||||
# Gradle's toolchain support does not work with IntelliJ, thus we have to use buildFHSUserEnv
|
||||
|
||||
# https://discourse.nixos.org/t/how-to-create-a-development-environment-with-intellij-idea-and-openjdk/10153
|
||||
|
||||
# How to use `config.allowUnfree` in shell.nix
|
||||
# https://github.com/NixOS/nixpkgs/issues/166220#issuecomment-1745803058
|
||||
|
||||
{
|
||||
pkgs ? import <nixpkgs> {
|
||||
# https://github.com/NixOS/nixpkgs/issues/166220#issuecomment-1745803058
|
||||
config.allowUnfree = true;
|
||||
}
|
||||
### Import nixpkgs as channels ###
|
||||
#
|
||||
# nixpkgs stable
|
||||
#pkgs ? import <nixpkgs> {
|
||||
# # Required for IntelliJ Ultimate
|
||||
# config.allowUnfree = true;
|
||||
#},
|
||||
# nixpkgs unstable
|
||||
#unstable ? import <unstable> { }
|
||||
}:
|
||||
|
||||
let
|
||||
unstable = import (fetchTarball https://github.com/NixOS/nixpkgs/archive/nixos-unstable.tar.gz) { };
|
||||
### Import nixpkgs with Niv ###
|
||||
#
|
||||
sources = import ./nix/sources.nix;
|
||||
# nixpkgs stable
|
||||
pkgs = import sources.nixpkgs {
|
||||
# Required for IntelliJ Ultimate
|
||||
config.allowUnfree = true;
|
||||
};
|
||||
# nixpkgs unstable
|
||||
unstable = import sources.unstable { };
|
||||
|
||||
jdk = unstable.jdk23;
|
||||
gradle = unstable.gradle;
|
||||
gradle = unstable.gradle.override {
|
||||
java = jdk;
|
||||
# Required for `gradle javaToolchains` to list JDK23
|
||||
javaToolchains = [ jdk ];
|
||||
};
|
||||
in
|
||||
|
||||
(
|
||||
@ -20,6 +42,10 @@ in
|
||||
name = "intellij-gradle-jdk23";
|
||||
targetPkgs = pkgs_: [
|
||||
pkgs_.jetbrains.idea-ultimate
|
||||
# IntelliJ needs at least one font installed.
|
||||
# If this shell.nix is run on a non-NixOS system, that might not be the case.
|
||||
# Thus, we manually add one font.
|
||||
pkgs_.noto-fonts
|
||||
jdk
|
||||
gradle
|
||||
];
|
||||
|
Loading…
x
Reference in New Issue
Block a user