diff -Nru ddogleg-0.18+ds/benchmark/src/org/ddogleg/clustering/BenchmarkStabilityInitialization.java ddogleg-0.22+ds/benchmark/src/org/ddogleg/clustering/BenchmarkStabilityInitialization.java --- ddogleg-0.18+ds/benchmark/src/org/ddogleg/clustering/BenchmarkStabilityInitialization.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/benchmark/src/org/ddogleg/clustering/BenchmarkStabilityInitialization.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -18,7 +18,8 @@ package org.ddogleg.clustering; -import org.ddogleg.struct.GrowQueue_I32; +import org.ddogleg.clustering.misc.ListAccessor; +import org.ddogleg.struct.DogArray_I32; import java.util.ArrayList; import java.util.List; @@ -29,18 +30,19 @@ */ public class BenchmarkStabilityInitialization { + int DOF = 2; Random rand = new Random(234); - List points = new ArrayList(); - List centers = new ArrayList(); - GrowQueue_I32 membership = new GrowQueue_I32(); - GrowQueue_I32 clusterSize = new GrowQueue_I32(); + List points = new ArrayList<>(); + List centers = new ArrayList<>(); + DogArray_I32 membership = new DogArray_I32(); + DogArray_I32 clusterSize = new DogArray_I32(); int totalClusters = 0; public void addCluster( double x , double y , double sigmaX , double sigmaY , int N ) { centers.add( new double[]{x,y}); for (int i = 0; i < N; i++) { - double p[] = new double[2]; + double[] p = new double[2]; p[0] = x + rand.nextGaussian()*sigmaX; p[1] = y + rand.nextGaussian()*sigmaY; @@ -57,22 +59,32 @@ addCluster(-1,5,0.5,0.2,500); addCluster(5,7,0.3,0.3,100); + ConfigKMeans config = new ConfigKMeans(); + config.reseedAfterIterations = 1000; + config.maxIterations = 1000; + config.convergeTol = 1e-8; + System.out.println("Lower errors the better....\n"); - evaluate(FactoryClustering.kMeans_F64(KMeansInitializers.STANDARD, 1000,1000, 1e-8)); - evaluate(FactoryClustering.kMeans_F64(KMeansInitializers.PLUS_PLUS,1000,1000, 1e-8)); - evaluate(FactoryClustering.gaussianMixtureModelEM_F64(1000,1000,1e-8)); + config.initializer = KMeansInitializers.STANDARD; + evaluate(FactoryClustering.kMeans(config,DOF, double[].class)); + config.initializer = KMeansInitializers.PLUS_PLUS; + evaluate(FactoryClustering.kMeans(config,DOF, double[].class)); + evaluate(FactoryClustering.gaussianMixtureModelEM_F64(1000,1000,1e-8,DOF)); } public void evaluate( ComputeClusters clusterer ) { - clusterer.init(2,32454325); + clusterer.initialize(32454325); + + ListAccessor accessor = new ListAccessor<>(points, + (src, dst) -> System.arraycopy(src, 0, dst, 0, DOF), double[].class); int numTrials = 500; double totalSizeError = 0; for (int i = 0; i < numTrials; i++) { - clusterer.process(points,3); + clusterer.process(accessor,3); AssignCluster assign = clusterer.getAssignment(); - int counts[] = new int[totalClusters]; + int[] counts = new int[totalClusters]; for (int j = 0; j < points.size(); j++) { int found = assign.assign(points.get(j)); @@ -85,7 +97,7 @@ System.out.println("average size error = "+(totalSizeError/numTrials)); } - private double computeSizeError( AssignCluster assign , int counts[] ) { + private double computeSizeError(AssignCluster assign , int[] counts) { double error = 0; for (int i = 0; i < totalClusters; i++) { int closest = assign.assign(centers.get(i)); diff -Nru ddogleg-0.18+ds/benchmark/src/org/ddogleg/nn/BenchmarkNearestNeighborCorrect.java ddogleg-0.22+ds/benchmark/src/org/ddogleg/nn/BenchmarkNearestNeighborCorrect.java --- ddogleg-0.18+ds/benchmark/src/org/ddogleg/nn/BenchmarkNearestNeighborCorrect.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/benchmark/src/org/ddogleg/nn/BenchmarkNearestNeighborCorrect.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -34,7 +34,7 @@ int dimen; List cloud; List searchSet; - double[] solutions[]; + double[][] solutions; double maxDistance; NnData result = new NnData<>(); @@ -113,8 +113,9 @@ // TODO have a search set // TODO Compute correct solution using exhaustive - public static void main( String args[] ) { - BenchmarkNearestNeighborCorrect app = new BenchmarkNearestNeighborCorrect(); + @SuppressWarnings("EmptyCatch") + public static void main(String[] args) { + final BenchmarkNearestNeighborCorrect app = new BenchmarkNearestNeighborCorrect(); // app.evaluateDataSet(3,30); // app.evaluateDataSet(3,300); @@ -126,9 +127,7 @@ app.evaluateDataSet(120,100000,1000); try { - synchronized ( app ) { - Thread.sleep(100); - } - } catch (InterruptedException e) {} + Thread.sleep(100); + } catch (InterruptedException ignore) {} } } diff -Nru ddogleg-0.18+ds/benchmark/src/org/ddogleg/optimization/funcs/EvalFuncBadlyScaledBrown_DDRM.java ddogleg-0.22+ds/benchmark/src/org/ddogleg/optimization/funcs/EvalFuncBadlyScaledBrown_DDRM.java --- ddogleg-0.18+ds/benchmark/src/org/ddogleg/optimization/funcs/EvalFuncBadlyScaledBrown_DDRM.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/benchmark/src/org/ddogleg/optimization/funcs/EvalFuncBadlyScaledBrown_DDRM.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -53,7 +53,7 @@ return new double[]{1e6,2e-6}; } - public class Func implements FunctionNtoM + public static class Func implements FunctionNtoM { @Override public int getNumOfInputsN() {return 2;} @@ -72,7 +72,7 @@ } } - public class Deriv implements FunctionNtoMxN + public static class Deriv implements FunctionNtoMxN { @Override public int getNumOfInputsN() {return 2;} diff -Nru ddogleg-0.18+ds/benchmark/src/org/ddogleg/optimization/funcs/EvalFuncBadlyScaledBrown_DSCC.java ddogleg-0.22+ds/benchmark/src/org/ddogleg/optimization/funcs/EvalFuncBadlyScaledBrown_DSCC.java --- ddogleg-0.18+ds/benchmark/src/org/ddogleg/optimization/funcs/EvalFuncBadlyScaledBrown_DSCC.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/benchmark/src/org/ddogleg/optimization/funcs/EvalFuncBadlyScaledBrown_DSCC.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -53,7 +53,7 @@ return new double[]{1e6,2e-6}; } - public class Func implements FunctionNtoM + public static class Func implements FunctionNtoM { @Override public int getNumOfInputsN() {return 2;} @@ -72,7 +72,7 @@ } } - public class Deriv implements FunctionNtoMxN + public static class Deriv implements FunctionNtoMxN { @Override public int getNumOfInputsN() {return 2;} diff -Nru ddogleg-0.18+ds/benchmark/src/org/ddogleg/optimization/funcs/EvalFunctionBundle2D_DSCC.java ddogleg-0.22+ds/benchmark/src/org/ddogleg/optimization/funcs/EvalFunctionBundle2D_DSCC.java --- ddogleg-0.18+ds/benchmark/src/org/ddogleg/optimization/funcs/EvalFunctionBundle2D_DSCC.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/benchmark/src/org/ddogleg/optimization/funcs/EvalFunctionBundle2D_DSCC.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -23,7 +23,7 @@ import org.ddogleg.optimization.wrap.SchurJacobian_to_NtoMxN; import org.ejml.data.DMatrixSparseCSC; import org.ejml.data.DMatrixSparseTriplet; -import org.ejml.ops.ConvertDMatrixStruct; +import org.ejml.ops.DConvertMatrixStruct; import java.util.ArrayList; import java.util.List; @@ -102,8 +102,8 @@ } } - ConvertDMatrixStruct.convert(tripletLeft,left); - ConvertDMatrixStruct.convert(tripletRight,right); + DConvertMatrixStruct.convert(tripletLeft,left); + DConvertMatrixStruct.convert(tripletRight,right); } @Override diff -Nru ddogleg-0.18+ds/benchmark/src/org/ddogleg/optimization/funcs/EvalFunctionBundle2D.java ddogleg-0.22+ds/benchmark/src/org/ddogleg/optimization/funcs/EvalFunctionBundle2D.java --- ddogleg-0.18+ds/benchmark/src/org/ddogleg/optimization/funcs/EvalFunctionBundle2D.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/benchmark/src/org/ddogleg/optimization/funcs/EvalFunctionBundle2D.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -41,17 +41,17 @@ double length,depth; int numCamera,numLandmarks; - double observations[]; + double[] observations; - double optimal[]; - double initial[]; + double[] optimal; + double[] initial; - public EvalFunctionBundle2D() { + protected EvalFunctionBundle2D() { this(0xDEADBEF,20,10,20,10); } - public EvalFunctionBundle2D(long seed , double length, double depth, - int numCamera, int numLandmarks) + protected EvalFunctionBundle2D(long seed , double length, double depth, + int numCamera, int numLandmarks) { this.length = length; this.depth = depth; diff -Nru ddogleg-0.18+ds/benchmark/test/org.ddogleg/optimization/funcs/TestEvalFunctionBundle2D_DDRM.java ddogleg-0.22+ds/benchmark/test/org.ddogleg/optimization/funcs/TestEvalFunctionBundle2D_DDRM.java --- ddogleg-0.18+ds/benchmark/test/org.ddogleg/optimization/funcs/TestEvalFunctionBundle2D_DDRM.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/benchmark/test/org.ddogleg/optimization/funcs/TestEvalFunctionBundle2D_DDRM.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -19,7 +19,6 @@ package org.ddogleg.optimization.funcs; import org.ddogleg.optimization.DerivativeChecker; -import org.ddogleg.optimization.functions.FunctionNtoM; import org.ddogleg.optimization.functions.FunctionNtoMxN; import org.ddogleg.optimization.wrap.SchurJacobian_to_NtoMxN; import org.ejml.data.DMatrixRMaj; @@ -36,7 +35,7 @@ EvalFunctionBundle2D_DDRM eval = new EvalFunctionBundle2D_DDRM(234,20,10,2,3); FunctionNtoMxN jac = new SchurJacobian_to_NtoMxN.DDRM(eval.getJacobianSchur()); - FunctionNtoM func = eval.getFunction(); + eval.getFunction(); double[] params = eval.initial; // DerivativeChecker.jacobianPrintR(func, jac, params, 1e-3); diff -Nru ddogleg-0.18+ds/benchmark/test/org.ddogleg/optimization/funcs/TestEvalFunctionBundle2D_DSCC.java ddogleg-0.22+ds/benchmark/test/org.ddogleg/optimization/funcs/TestEvalFunctionBundle2D_DSCC.java --- ddogleg-0.18+ds/benchmark/test/org.ddogleg/optimization/funcs/TestEvalFunctionBundle2D_DSCC.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/benchmark/test/org.ddogleg/optimization/funcs/TestEvalFunctionBundle2D_DSCC.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -19,7 +19,6 @@ package org.ddogleg.optimization.funcs; import org.ddogleg.optimization.DerivativeChecker; -import org.ddogleg.optimization.functions.FunctionNtoM; import org.ddogleg.optimization.functions.FunctionNtoMxN; import org.ddogleg.optimization.wrap.SchurJacobian_to_NtoMxN; import org.ejml.data.DMatrixSparseCSC; @@ -36,7 +35,7 @@ EvalFunctionBundle2D_DSCC eval = new EvalFunctionBundle2D_DSCC(234,20,10,2,3); FunctionNtoMxN jac = new SchurJacobian_to_NtoMxN.DSCC(eval.getJacobianSchur()); - FunctionNtoM func = eval.getFunction(); + eval.getFunction(); double[] params = eval.initial; // DerivativeChecker.jacobianPrintR(func, jac, params, 1e-3); diff -Nru ddogleg-0.18+ds/build.gradle ddogleg-0.22+ds/build.gradle --- ddogleg-0.18+ds/build.gradle 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/build.gradle 2022-09-01 02:18:09.000000000 +0000 @@ -1,25 +1,80 @@ +import net.ltgt.gradle.errorprone.CheckSeverity +import org.gradle.api.tasks.testing.logging.TestExceptionFormat plugins { - id "com.peterabeles.gversion" version "1.7.0" + id "com.peterabeles.gversion" version "1.10" + id "net.ltgt.errorprone" version "2.0.2" } -apply plugin: 'idea' -apply plugin: 'eclipse' -apply plugin: 'java' -apply plugin: 'maven' +apply plugin: 'java-library' +apply plugin: 'maven-publish' apply plugin: 'signing' group = 'org.ddogleg' -version = '0.18' +version = '0.22' project.archivesBaseName = 'ddogleg' -sourceCompatibility = 1.8 -targetCompatibility = 1.8 +project.ext.lombok_version = '1.18.20' +project.ext.errorprone_version = '2.11.0' +project.ext.nullaway_version = '0.9.5' +project.ext.guava_version = '31.0.1-jre' +project.ext.jabel_version = '0.4.2' +project.ext.jetnull_version = '23.0.0' gversion { srcDir = "src" classPackage = "org.ddogleg" className = "DDoglegVersion" + annotate = true +} + +java { + withJavadocJar() + withSourcesJar() + toolchain { languageVersion = JavaLanguageVersion.of(17) } +} + +// Prevents tons of errors if someone is using ASCII +tasks.withType(JavaCompile).configureEach { options.encoding = "UTF-8" } + +// Creates Java 11 byte code but Java 17 syntax +tasks.withType(JavaCompile).configureEach { + sourceCompatibility = 17 + options.release = 11 +} + +// Enable incremental compile. Should make single file changes faster +tasks.withType(JavaCompile) { options.incremental = true } + +// Fail on jar conflict +configurations.all { resolutionStrategy { failOnVersionConflict() } } + +// Force the release build to fail if it depends on a SNAPSHOT +project.jar.dependsOn project.checkDependsOnSNAPSHOT + +// Force publish to fail if trying to upload a stable release and git is dirty +project.publish.dependsOn failDirtyNotSnapshot + +// To make ErrorProne and Kotlin plugins happy +configurations.all { + resolutionStrategy { + force "org.jetbrains:annotations:$project.jetnull_version" + force "com.google.guava:guava:$project.guava_version" + force "com.google.errorprone:error_prone_annotations:$project.errorprone_version" + force 'com.google.code.findbugs:jsr305:3.0.2' + force 'org.checkerframework:checker-qual:2.10.0' + } +} + +test { + useJUnitPlatform() + reports.html.enabled = false + // Make the error logging verbose to make debugging on CI easier + testLogging.showStandardStreams = true + testLogging.exceptionFormat TestExceptionFormat.FULL + testLogging.showCauses true + testLogging.showExceptions true + testLogging.showStackTraces true } repositories { @@ -27,147 +82,176 @@ mavenLocal() maven { url = "https://oss.sonatype.org/content/repositories/snapshots/" } + maven { url = 'https://jitpack.io' } // Allows annotations past Java 8 to be used } sourceSets { + generate { java { srcDir 'generate' } } benchmark { - java { - srcDir 'benchmark/src' - } - resources { - srcDir 'benchmark/resources' - } + java { srcDir 'benchmark/src' } + resources { srcDir 'benchmark/resources' } } + examples { java { srcDir 'examples/src' } } + main { - java { - srcDir 'src' - } - resources { - srcDir 'resources/src' - } + java { srcDir 'src' } + resources { srcDir 'resources/src' } } test { java { - srcDir 'examples/src' srcDir 'test' - srcDir 'generate' srcDir 'benchmark/test' } - resources { - srcDir 'resources/test' - } + resources { srcDir 'resources/test' } } - } dependencies { ['ejml-core','ejml-fdense','ejml-ddense','ejml-simple','ejml-dsparse'].each { String a -> - compile group: 'org.ejml', name: a, version: '0.39'} + api group: 'org.ejml', name: a, version: '0.41'} + + compileOnly "org.projectlombok:lombok:$project.lombok_version" + compileOnly 'org.jetbrains:annotations:20.0.0' // @Nullable + compileOnly 'javax.annotation:jsr250-api:1.0' // @Generated + testImplementation project.sourceSets.main.compileClasspath - compile 'com.google.code.findbugs:jsr305:3.0.2' // @Nullable + examplesImplementation project.sourceSets.main.compileClasspath + examplesImplementation project.sourceSets.main.runtimeClasspath testImplementation( 'org.junit.jupiter:junit-jupiter-api:5.4.0') + testImplementation( 'org.junit.jupiter:junit-jupiter-params:5.4.0' ) testRuntimeOnly( 'org.junit.jupiter:junit-jupiter-engine:5.4.0') - testCompile project.sourceSets.benchmark.output + generateImplementation group: 'commons-io', name: 'commons-io', version: '2.6' + testImplementation project.sourceSets.benchmark.output - benchmarkCompile project.sourceSets.main.output - benchmarkCompile project.sourceSets.main.runtimeClasspath - benchmarkCompile project.sourceSets.main.compileClasspath + benchmarkImplementation project.sourceSets.main.output + benchmarkImplementation project.sourceSets.main.runtimeClasspath + benchmarkImplementation project.sourceSets.main.compileClasspath + examplesImplementation project.sourceSets.main.output + + // needed to use Java 17 syntax with Java 11 byte code + annotationProcessor("com.github.bsideup.jabel:jabel-javac-plugin:$project.jabel_version") + testAnnotationProcessor("com.github.bsideup.jabel:jabel-javac-plugin:$project.jabel_version") + benchmarkAnnotationProcessor("com.github.bsideup.jabel:jabel-javac-plugin:$project.jabel_version") + + errorprone("com.google.errorprone:error_prone_core:$project.errorprone_version") + + // even if it's not used you still need to include the dependency + annotationProcessor "com.uber.nullaway:nullaway:${project.nullaway_version}" + testAnnotationProcessor "com.uber.nullaway:nullaway:${project.nullaway_version}" + benchmarkAnnotationProcessor "com.uber.nullaway:nullaway:${project.nullaway_version}" + examplesAnnotationProcessor "com.uber.nullaway:nullaway:${project.nullaway_version}" + + annotationProcessor "org.projectlombok:lombok:$project.lombok_version" // @Getter @Setter + testAnnotationProcessor "org.projectlombok:lombok:$project.lombok_version" +} + +tasks.withType(JavaCompile).configureEach { + options.errorprone.enabled = false + if( path.contains("compileBenchmark") ) + return + if( path.contains("example") ) + return + + options.errorprone.enabled = true + options.errorprone.disableWarningsInGeneratedCode = true + options.errorprone.disable("TypeParameterUnusedInFormals","StringSplitter","InconsistentCapitalization", + "HidingField", // this is sometimes done when the specific type is known by child. Clean up later. + "ClassNewInstance", // yes it's deprecated, but new version is more verbose with ignored errors + "FloatingPointLiteralPrecision", // too many false positives in test code + "MissingSummary","UnescapedEntity","EmptyBlockTag") + options.errorprone.error("MissingOverride","MissingCasesInEnumSwitch","BadInstanceof", + "PublicConstructorForAbstractClass","EmptyCatch","NarrowingCompoundAssignment","JdkObsolete") + + if( name.startsWith("compileTest") ) { + options.errorprone.disable("ReferenceEquality","IntLongMath","ClassCanBeStatic") + } + + options.errorprone { + check("NullAway", CheckSeverity.ERROR) + option("NullAway:TreatGeneratedAsUnannotated", true) + option("NullAway:AnnotatedPackages", "org.ddogleg") + } } javadoc { configure(options) { - links = [ 'http://docs.oracle.com/javase/8/docs/api/', - 'http://ejml.org/javadoc/'] + links = ['http://docs.oracle.com/javase/8/docs/api/', + 'http://ejml.org/javadoc/'] failOnError = false + enabled = !project.version.contains("SNAPSHOT") // disable to stop it from spamming stdout } -} - -test { - ignoreFailures true -} - -task javadocJar(type: Jar) { - classifier = 'javadoc' - from javadoc -} - -task sourcesJar(type: Jar) { - classifier = 'sources' - from sourceSets.main.allSource -} -artifacts { - archives javadocJar, sourcesJar -} - -// Creates Java 8 byte code on Java 9+ -if(JavaVersion.current() != JavaVersion.VERSION_1_8) { - compileJava { - options.compilerArgs = ["--release", "8"] + // https://github.com/gradle/gradle/issues/11182 Error introduced in JDK 11 + if (JavaVersion.current() >= JavaVersion.VERSION_11) { + options.addStringOption("-release", "11") } -} -// if Maven central isn't setup in gradle.properties skip all of this -if( project.hasProperty('ossrhUsername') ) { - signing { - sign configurations.archives + if(JavaVersion.current().isJava9Compatible()) { + options.addBooleanOption('html5', true) } +} - uploadArchives { - repositories { - mavenDeployer { - beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } - - repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") { - authentication(userName: ossrhUsername, password: ossrhPassword) - } - - snapshotRepository(url: "https://oss.sonatype.org/content/repositories/snapshots/") { - authentication(userName: ossrhUsername, password: ossrhPassword) - } - - pom.project { - name 'DDogleg' - packaging 'pom' - // optionally artifactId can be defined here - description 'DDogleg Numerics is a high performance Java library for non-linear optimization, robust model fitting, polynomial root finding, sorting, and more.' - url 'http://ddogleg.org' - - scm { - connection 'scm:git:git@github.com:lessthanoptimal/ddogleg.git' - developerConnection 'scm:git:git@github.com:lessthanoptimal/ddogleg.git' - url 'https://github.com/lessthanoptimal/ddogleg' - } - - licenses { - license { - name 'The Apache Software License, Version 2.0' - url 'http://www.apache.org/licenses/LICENSE-2.0.txt' - } +// if Maven central isn't setup in ~/.gradle/gradle.properties fill in these variables to make it happy +if( !project.hasProperty('ossrhUsername') ) { + ext.ossrhUsername = "dummy" + ext.ossrhPassword = "dummy" +} + +publishing { + publications { + mavenJava(MavenPublication) { + from components.java + pom { + name = 'DDogleg' + description = 'DDogleg Numerics is a high performance Java library for non-linear optimization, robust model fitting, polynomial root finding, sorting, and more.' + url = 'http://ddogleg.org' + licenses { + license { + name = 'The Apache License, Version 2.0' + url = 'http://www.apache.org/licenses/LICENSE-2.0.txt' } - - developers { - developer { - id 'pabeles' - name 'Peter Abeles' - email 'peter.abeles@gmail.com' - } + } + developers { + developer { + id = 'pabeles' + name = 'Peter Abeles' + email = 'peter.abeles@gmail.com' } } + scm { + connection = 'scm:git:git://github.com/lessthanoptimal/ddogleg.git' + developerConnection = 'scm:git:git://github.com/lessthanoptimal/ddogleg.git' + url = 'https://github.com/lessthanoptimal/ddogleg' + } + } + } + } + repositories { + maven { + def releasesRepoUrl = "https://oss.sonatype.org/service/local/staging/deploy/maven2/" + def snapshotsRepoUrl = "https://oss.sonatype.org/content/repositories/snapshots/" + url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl + credentials { + username ossrhUsername + password ossrhPassword } } } } +if (ext.ossrhPassword != "dummy") { + signing { sign publishing.publications.mavenJava } +} + // Creates a directory with all the compiled jars and the dependencies task createLibraryDirectory( dependsOn: ['jar','sourcesJar'] ) { doLast { - ext.listExternal = files(project.configurations.compile) + ext.listExternal = files(project.configurations.runtimeClasspath) ext.listInternal = files(project.tasks.jar.archivePath) ext.listSource = files(project.tasks.sourcesJar.archivePath) @@ -198,7 +282,11 @@ // Hack for Java 8u121 and beyond. Comment out if running an earlier version of Java options.addBooleanOption("-allow-script-in-comments", true) - options.addBooleanOption("-no-module-directories", true) + + // Flag is no longer around in later versions of Java but required before + if (JavaVersion.current().ordinal() < JavaVersion.VERSION_13.ordinal()) { + options.addBooleanOption("-no-module-directories", true) + } // Add a list of uses of a class to javadoc options.use = true @@ -216,5 +304,5 @@ wrapper { distributionType = Wrapper.DistributionType.BIN - gradleVersion = '6.3' + gradleVersion = '7.3.3' } diff -Nru ddogleg-0.18+ds/change.txt ddogleg-0.22+ds/change.txt --- ddogleg-0.18+ds/change.txt 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/change.txt 2022-09-01 02:18:09.000000000 +0000 @@ -3,6 +3,101 @@ Date Format: YEAR-MONTH-DAY ------------------------------------------------------ +Version: 0.22 +Date: 2022/Aug/31 + +- Build + * Generates Java 11 bytecode but compiles with Java 17 +- Added DogArray_I16 +- BigDogArrayBase + * Fixed excessive growth of internal array +- DogLinkedList + * Fixed reset for cyclical lists +- FastAccess + * Added count(), filter() +- DogArray + * Added ability to chain some commands. + * Added shuffle(rand) + * Deprecated resetResize() +- Ransac_MT and LeastMedianOfSquares_MT + * Added way to initialize generators and distance functions for each thread + +------------------------------------------------------ +Version: 0.21 +Date: 2022-01-17 + +- FastArray + * Added resetReserve() +- K-Means Clustering + * Fixed serialization + * Checks contract + +------------------------------------------------------ +Version: 0.20 +Date: 2021-07-07 + +- Updated for changes in EJML's API +- K-Means + * Updated to be data type agnostic + * Designed to handle potentially very large sets of data +- DogArray_** + * getTail(), setTail(idx,value) +- DogArray + * Added an initializer function +- BigDogArray_** + * For when a regular dog isn't sufficient + * Does not use a single very large array internally + * This scales much better as array size gets very very large +- PrimitiveArrays + * Added minIdx(), maxIdx(), sumD(), intersection(), union() + * Fixed how min/max was being computed. Thanks the-widmore for reporting and showing the fix! +- ModelMatcher + * Created ModelMatcherPost that let's you specify the model after creation + * Concurrent implementations of RANSAC and LeastMedianOfSquares +- Added QuickSortComparable +- QuickSort + * Now can have an offset + +------------------------------------------------------ +Version: 0.19 +Date: 2020-12-19 + +- Updated to Java 11 syntax but generates Java 8 byte code +- Concurrency + * Added DDoglegConcurrency for controlling threads and turning it on and off + * Default is off + * Only HessianSchurComplement_DSCC is threaded at the moment +- GrowQueue -> renamed to DogArray_TYPE + * Added resize( size , defaultValue ) + * Added count( value ) + * Added forIdx( idx, value ) + * Added applyIdx( idx, value ) -> value + * Added removeSwap( idx ) + * Added setTo(...) + * Added toArray() + * Added isEquals(...) + * Renamed setMaxSize() to reserve(). This matches more commonly used terms and tweaked behavior to match. +- FastArray + * added remove(object) + * added forEach(lambda) + * Renamed growArray() to reserve() +- FastAccess + * Moved functions into this that should have been here originally + * Added find(), findIdx(), findAll(), findAllIdx() +- FastQueue is now DogArray + * GrowArray was the first choice but that was too common and everyone likes dogs +- RANSAC + * When selecting an inlier set it will abort if there's no possible way it can do better than the best set +- SchurComplement + * Swapped out inefficient sparse matrix multiplication functions greatly increasing the speed +- QuickSortComparator + * Added function to sort List +- DogLinkedList + * Fixed generics issue + * Added functions for getting the value of first and last elements + * previous is now prev to make formatting code easier as it's the same length as next. + +------------------------------------------------------ Version: 0.18 Date: 2020/May/15 @@ -18,6 +113,8 @@ * added removeSwap() - ModelMatcher * Added reset() function to return it into its initial state +- DistanceFromModel + * computeDistance() to distance() and distances() to make less verbose - GrowQueue * Added getTail() diff -Nru ddogleg-0.18+ds/debian/changelog ddogleg-0.22+ds/debian/changelog --- ddogleg-0.18+ds/debian/changelog 2022-09-14 08:34:56.000000000 +0000 +++ ddogleg-0.22+ds/debian/changelog 2022-11-24 12:25:47.000000000 +0000 @@ -1,3 +1,14 @@ +ddogleg (0.22+ds-1) unstable; urgency=medium + + * Run tests. + * New upstream version 0.22+ds (Closes: #964010) + * Adjust source version for javac. + * Set upstream metadata fields: Repository-Browse. + * Update standards version to 4.6.1, no changes needed. + * Refresh copyright years. + + -- Andrius Merkys Thu, 24 Nov 2022 07:25:47 -0500 + ddogleg (0.18+ds-1) unstable; urgency=medium * New upstream version 0.18+ds diff -Nru ddogleg-0.18+ds/debian/clean ddogleg-0.22+ds/debian/clean --- ddogleg-0.18+ds/debian/clean 2022-09-14 07:19:33.000000000 +0000 +++ ddogleg-0.22+ds/debian/clean 2022-09-20 06:48:25.000000000 +0000 @@ -1 +1,2 @@ ddogleg*.jar +junk.txt diff -Nru ddogleg-0.18+ds/debian/control ddogleg-0.22+ds/debian/control --- ddogleg-0.18+ds/debian/control 2022-09-14 08:29:58.000000000 +0000 +++ ddogleg-0.22+ds/debian/control 2022-11-24 12:24:01.000000000 +0000 @@ -11,7 +11,7 @@ libejml-java (>= 0.39), libjsr305-java, liblombok-java, -Standards-Version: 4.6.0 +Standards-Version: 4.6.1 Homepage: http://ddogleg.org Vcs-Browser: https://salsa.debian.org/java-team/ddogleg Vcs-Git: https://salsa.debian.org/java-team/ddogleg.git diff -Nru ddogleg-0.18+ds/debian/copyright ddogleg-0.22+ds/debian/copyright --- ddogleg-0.18+ds/debian/copyright 2022-09-14 07:22:44.000000000 +0000 +++ ddogleg-0.22+ds/debian/copyright 2022-11-24 12:25:20.000000000 +0000 @@ -7,7 +7,7 @@ gradlew.bat Files: * -Copyright: 2012-2020, Peter Abeles +Copyright: 2012-2022, Peter Abeles License: Apache-2.0 Files: debian/* diff -Nru ddogleg-0.18+ds/debian/install ddogleg-0.22+ds/debian/install --- ddogleg-0.18+ds/debian/install 2022-09-14 07:20:12.000000000 +0000 +++ ddogleg-0.22+ds/debian/install 2022-09-20 06:48:25.000000000 +0000 @@ -1 +1,2 @@ -ddogleg*.jar usr/share/java +ddogleg.jar usr/share/java +ddogleg-0*.jar usr/share/java diff -Nru ddogleg-0.18+ds/debian/rules ddogleg-0.22+ds/debian/rules --- ddogleg-0.18+ds/debian/rules 2022-09-14 08:30:09.000000000 +0000 +++ ddogleg-0.22+ds/debian/rules 2022-11-24 12:23:23.000000000 +0000 @@ -2,7 +2,7 @@ include /usr/share/dpkg/pkg-info.mk -export CLASSPATH = /usr/share/java/jsr305.jar:/usr/share/java/lombok.jar:/usr/share/java/ejml-core.jar:/usr/share/java/ejml-ddense.jar:/usr/share/java/ejml-dsparse.jar:/usr/share/java/ejml-cdense.jar +export CLASSPATH = /usr/share/java/jsr305.jar:/usr/share/java/lombok.jar:/usr/share/java/ejml-core.jar:/usr/share/java/ejml-ddense.jar:/usr/share/java/ejml-dsparse.jar:/usr/share/java/ejml-cdense.jar:/usr/share/java/ejml-simple.jar:ddogleg.jar:ddogleg-benchmark.jar UPSTREAM_VERSION = $(DEB_VERSION_UPSTREAM:%+ds=%) @@ -10,5 +10,10 @@ dh $@ --with javahelper override_dh_auto_build: - jh_build --no-javadoc ddogleg-$(UPSTREAM_VERSION).jar --javacopts '-source 1.8' src/ + jh_build --no-javadoc ddogleg-$(UPSTREAM_VERSION).jar --javacopts '-source 11' src/ ln -s ddogleg-$(UPSTREAM_VERSION).jar ddogleg.jar + jh_build --no-javadoc ddogleg-benchmark.jar --javacopts '-source 11' benchmark/ + jh_build --no-javadoc ddogleg-test.jar --javacopts '-source 11' test/ + +override_dh_auto_test: + java -jar /usr/share/java/junit-platform-console-standalone.jar -cp ddogleg.jar:ddogleg-test.jar --select-package org.ddogleg diff -Nru ddogleg-0.18+ds/debian/upstream/metadata ddogleg-0.22+ds/debian/upstream/metadata --- ddogleg-0.18+ds/debian/upstream/metadata 2022-09-13 14:52:08.000000000 +0000 +++ ddogleg-0.22+ds/debian/upstream/metadata 2022-11-24 12:23:57.000000000 +0000 @@ -1,3 +1,4 @@ --- Bug-Database: https://github.com/lessthanoptimal/ddogleg/issues Bug-Submit: https://github.com/lessthanoptimal/ddogleg/issues/new +Repository-Browse: https://github.com/lessthanoptimal/ddogleg diff -Nru ddogleg-0.18+ds/docs/website/BigDogArray.rst ddogleg-0.22+ds/docs/website/BigDogArray.rst --- ddogleg-0.18+ds/docs/website/BigDogArray.rst 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/docs/website/BigDogArray.rst 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,17 @@ +BigDogArray +####################### + +BigDogArrays are designed to store extremely large arrays. This is accomplished by internally using an array-of-arrays +as a net results they are more complex to work with than DogArrays. In fact, it would be trivial to modify them to +exceed the 32-bit limit of Java arrays, but this capability isn't enabled for backwards compatibility. At least +not yet. + +:gitexample:`ExampleBigDogArray.java` + +.. literalinclude:: ../../examples/src/org/ddogleg/example/ExampleBigDogArray.java + :language: java + :linenos: + :start-after: public class + :tab-width: 4 + :dedent: 4 + diff -Nru ddogleg-0.18+ds/docs/website/conf.py ddogleg-0.22+ds/docs/website/conf.py --- ddogleg-0.18+ds/docs/website/conf.py 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/docs/website/conf.py 2022-09-01 02:18:09.000000000 +0000 @@ -48,7 +48,7 @@ # General information about the project. project = u'DDogleg Numerics' -copyright = u'2020, Peter Abeles' +copyright = u'2022, Peter Abeles' author = u'Peter Abeles' # The version info for the project you're documenting, acts as replacement for @@ -56,9 +56,9 @@ # built documents. # # The short X.Y version. -version = '0.18' +version = '0.22' # The full version, including alpha/beta/rc tags. -release = '0.18' +release = '0.22' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff -Nru ddogleg-0.18+ds/docs/website/Documentation.rst ddogleg-0.22+ds/docs/website/Documentation.rst --- ddogleg-0.18+ds/docs/website/Documentation.rst 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/docs/website/Documentation.rst 2022-09-01 02:18:09.000000000 +0000 @@ -17,8 +17,10 @@ List of Example Code: +* :doc:`/BigDogArray` * :doc:`/Clustering` * :doc:`/Combinatorics` +* :doc:`/DogArray` * :doc:`/optimization/NonLinearLeastSquares` * :doc:`/optimization/NonLinearMinimization` * :doc:`/optimization/NonLinearSparseSchurComplement` diff -Nru ddogleg-0.18+ds/docs/website/DogArray.rst ddogleg-0.22+ds/docs/website/DogArray.rst --- ddogleg-0.18+ds/docs/website/DogArray.rst 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/docs/website/DogArray.rst 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,16 @@ +DogArray +####################### + +DogArrays are re-sizable arrays with implementations available for primitive types and generic objects. Internally +it uses an array that can be directly accessed. The name DogArray comes from the library name and was selected since +just about every permutation on growable array conflicted with an existing library. Plus who doesn't like dogs? + +:gitexample:`ExampleDogArray.java` + +.. literalinclude:: ../../examples/src/org/ddogleg/example/ExampleDogArray.java + :language: java + :linenos: + :start-after: public class + :tab-width: 4 + :dedent: 4 + diff -Nru ddogleg-0.18+ds/docs/website/index.rst ddogleg-0.22+ds/docs/website/index.rst --- ddogleg-0.18+ds/docs/website/index.rst 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/docs/website/index.rst 2022-09-01 02:18:09.000000000 +0000 @@ -5,7 +5,7 @@ =================== ===================== **Latest Version** v\ |ddogleg_version| -**Released** May 15, 2020 +**Released** Auguest 31, 2022 **Source Code** `GitHub `_ **License** `Apache 2.0 `_ =================== ===================== @@ -39,7 +39,9 @@ * **Combinatorics:** Set combinations and permutations. * **Clustering:** K-Means and Gaussian Mixture Models (GMM) * **Arithmetic:** Complex and Polynomial. +* **Data Structures:** Dynamic arrays for small and large data. +Single and multi-threaded implementations of several algorithms, such as model fitting variants. Indices and tables ================== diff -Nru ddogleg-0.18+ds/examples/src/org/ddogleg/example/DistanceFromLine.java ddogleg-0.22+ds/examples/src/org/ddogleg/example/DistanceFromLine.java --- ddogleg-0.18+ds/examples/src/org/ddogleg/example/DistanceFromLine.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/examples/src/org/ddogleg/example/DistanceFromLine.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2016, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -28,14 +28,14 @@ * * @author Peter Abeles */ -public class DistanceFromLine implements DistanceFromModel { +public class DistanceFromLine implements DistanceFromModel { // parametric line equation double x0, y0; - double slopeX,slopeY; + double slopeX, slopeY; @Override - public void setModel(Line2D param) { + public void setModel( Line2D param ) { x0 = param.x; y0 = param.y; @@ -44,11 +44,11 @@ } @Override - public double computeDistance(Point2D p) { + public double distance( Point2D p ) { // find the closest point on the line to the point - double t = slopeX * ( p.x - x0) + slopeY * ( p.y - y0); - t /= slopeX * slopeX + slopeY * slopeY; + double t = slopeX*(p.x - x0) + slopeY*(p.y - y0); + t /= slopeX*slopeX + slopeY*slopeY; double closestX = x0 + t*slopeX; double closestY = y0 + t*slopeY; @@ -65,9 +65,9 @@ * This is not one of them. */ @Override - public void computeDistance(List obs, double[] distance) { - for( int i = 0; i < obs.size(); i++ ) { - distance[i] = computeDistance(obs.get(i)); + public void distances( List obs, double[] distance ) { + for (int i = 0; i < obs.size(); i++) { + distance[i] = distance(obs.get(i)); } } diff -Nru ddogleg-0.18+ds/examples/src/org/ddogleg/example/ExampleBigDogArray.java ddogleg-0.22+ds/examples/src/org/ddogleg/example/ExampleBigDogArray.java --- ddogleg-0.18+ds/examples/src/org/ddogleg/example/ExampleBigDogArray.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/examples/src/org/ddogleg/example/ExampleBigDogArray.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.example; + +import org.ddogleg.struct.BigDogArray_I32; + +/** + * BigDogArrays are designed to handle very large arrays at the cost of additional complexity and a small hit + * on read/write performance. As you start to hit the limits of a java array, growing a DogArray can get expensive + * as it will need to allocate a new array, then copy the old one over. This could mean two very large + * chunks of continuous memory. BigDogArrays are broken up into multiple chunks/blocks and at worst the cost + * to grow the array is limited by the size of a chunk. + * + * It's recommended that you first look at ExampleDogArray, as we will only cover new topics here. + * + * @author Peter Abeles + */ +public class ExampleBigDogArray { + public static void main( String[] args ) { + // Unless you really know what you are doing, use the default constructor + var array = new BigDogArray_I32(); + + // It is possible to customize the block size and how the array is grown. +// var array = new BigDogArray_I32(10_000, 50_0000, BigDogGrowth.GROW_FIRST); + + // Let's initialize it after pre-allocating memory + array.reserve(50); + for (int i = 0; i < 50; i++) { + // Set is actually making two array access calls + array.set(i, i*2); + } + + // Here's an alternative to do the same thing. It will be smart enough toe process it by blocks + // reducing the number of array accesses + array.applyIdx(0, 50, ( i, value ) -> i*2); + + // if you need to process a range of values it's recommended you use forEach or forIdx and it will + // handle the internal complexity for you + array.forEach(10, 15, v -> System.out.print(v + ",")); + System.out.println(); + + // If you for some reason need to process a range of values (10 to 20) but need to access the raw + // block array, then processByBlock is your friend + array.processByBlock(10,20, (block, idx0, idx1, offset)->{ + // block is the raw array that composes the block + // idx0 is the first element in the block that you should process + // idx1 is the upper extent, exclusive + // offset is the offset to the array's indexing. So block[idx0 + 1] = array[offset + 1] + for (int i = idx0; i < idx1; i++) { + block[i] = offset + i; + } + }); + + // Let's see what happened + array.forEach(0, 20, v -> System.out.print(v + ",")); + System.out.println(); + } +} diff -Nru ddogleg-0.18+ds/examples/src/org/ddogleg/example/ExampleClustering.java ddogleg-0.22+ds/examples/src/org/ddogleg/example/ExampleClustering.java --- ddogleg-0.18+ds/examples/src/org/ddogleg/example/ExampleClustering.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/examples/src/org/ddogleg/example/ExampleClustering.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -21,6 +21,7 @@ import org.ddogleg.clustering.AssignCluster; import org.ddogleg.clustering.ComputeClusters; import org.ddogleg.clustering.FactoryClustering; +import org.ddogleg.clustering.misc.ListAccessor; import javax.swing.*; import java.awt.*; @@ -35,7 +36,7 @@ * Demonstration of how to cluster a N-dimensional points into clusters. The high level interface provides * the capability to do hard and soft assignments. Hard assignments select a single best match for a point * and soft assignments compute a weight for each cluster, high weights mean better matches. - * + *

* Results are visualized in a window. Points in different clusters are assigned different colors. If you * click the inside of the window the algorithm is run again and a new set of clusters is shown. * @@ -50,59 +51,64 @@ public static boolean clicked = false; public static void main(String[] args) { - List points = new ArrayList(); + int dof = 2; // degree of freedom of the 2D point + List points = new ArrayList<>(); + + // Accessor is used instead of a list directly because it becomes more efficient in very large datasets + ListAccessor accessor = new ListAccessor<>(points, + (src, dst) -> System.arraycopy(src, 0, dst, 0, dof), double[].class); // create 3 clusters drawn from a uniform square distribution - points.addAll( createCluster(5,7,2,100) ); - points.addAll( createCluster(1,2,1,120) ); - points.addAll( createCluster(4,5,1.5,300) ); + points.addAll(createCluster(5, 7, 2, 100)); + points.addAll(createCluster(1, 2, 1, 120)); + points.addAll(createCluster(4, 5, 1.5, 300)); // remove any structure from the point's ordering Collections.shuffle(points); - ComputeClusters cluster = FactoryClustering.kMeans_F64(null,1000,100, 1e-8); - // ComputeClusters cluster = FactoryClustering.gaussianMixtureModelEM_F64(1000, 1e-8); + ComputeClusters cluster = FactoryClustering.kMeans(null, dof, double[].class); +// ComputeClusters cluster = FactoryClustering.gaussianMixtureModelEM_F64(1000, 500, 1e-8, dof); - cluster.init(2, rand.nextLong()); + cluster.initialize(rand.nextLong()); // visualization stuff Gui gui = new Gui(points); JFrame frame = new JFrame(); - frame.add(gui,BorderLayout.CENTER); + frame.add(gui, BorderLayout.CENTER); frame.pack(); frame.setVisible(true); frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); // Run the cluster algorithm again each time the user clicks the window // This allows you to see how stable the clusters are - while( true ) { - - cluster.process(points, 3); + while (true) { + int numClusters = 3; + cluster.process(accessor, numClusters); AssignCluster assignment = cluster.getAssignment(); gui.update(assignment); - while( !clicked ) { + while (!clicked) { Thread.yield(); } clicked = false; } } - public static List createCluster( double x , double y , double width , int N ) { + public static List createCluster(double x, double y, double width, int N) { - List points = new ArrayList(); + List points = new ArrayList<>(); for (int i = 0; i < N; i++) { double[] p = new double[2]; - if ( gaussian ) { - p[0] = rand.nextGaussian()*width/3+x; - p[1] = rand.nextGaussian()*width/3+y; + if (gaussian) { + p[0] = rand.nextGaussian() * width / 3 + x; + p[1] = rand.nextGaussian() * width / 3 + y; } else { - p[0] = rand.nextDouble()*width-width/2+x; - p[1] = rand.nextDouble()*width-width/2+y; + p[0] = rand.nextDouble() * width - width / 2 + x; + p[1] = rand.nextDouble() * width - width / 2 + y; } points.add(p); @@ -117,21 +123,21 @@ public static class Gui extends JPanel implements MouseListener { AssignCluster assignment; List points; - Color colors[]; + Color[] colors; - public Gui( List points) { + public Gui(List points) { this.points = points; - setPreferredSize(new Dimension(300,300)); + setPreferredSize(new Dimension(300, 300)); setBackground(Color.WHITE); addMouseListener(this); } - public synchronized void update( AssignCluster assignment ) { + public synchronized void update(AssignCluster assignment) { this.assignment = assignment; - colors = new Color[ assignment.getNumberOfClusters() ]; + colors = new Color[assignment.getNumberOfClusters()]; for (int i = 0; i < colors.length; i++) { colors[i] = new Color(rand.nextInt() | 0x080808); } @@ -140,24 +146,23 @@ @Override public synchronized void paintComponent(Graphics g) { - if( assignment == null ) + if (assignment == null) return; super.paintComponent(g); - Graphics2D g2 = (Graphics2D)g; - - double scaleX = getWidth()/10.0; - double scaleY = getHeight()/10.0; + Graphics2D g2 = (Graphics2D) g; + double scaleX = getWidth() / 10.0; + double scaleY = getHeight() / 10.0; for (int i = 0; i < points.size(); i++) { double[] p = points.get(i); - int x = (int)(p[0]*scaleX+0.5); - int y = (int)(p[1]*scaleY+0.5); + int x = (int) (p[0] * scaleX + 0.5); + int y = (int) (p[1] * scaleY + 0.5); g2.setColor(colors[assignment.assign(p)]); - g2.fillOval(x-2,y-2,5,5); + g2.fillOval(x - 2, y - 2, 5, 5); } } @@ -166,9 +171,20 @@ clicked = true; } - @Override public void mousePressed(MouseEvent e) {} - @Override public void mouseReleased(MouseEvent e) {} - @Override public void mouseEntered(MouseEvent e) {} - @Override public void mouseExited(MouseEvent e) {} + @Override + public void mousePressed(MouseEvent e) { + } + + @Override + public void mouseReleased(MouseEvent e) { + } + + @Override + public void mouseEntered(MouseEvent e) { + } + + @Override + public void mouseExited(MouseEvent e) { + } } } diff -Nru ddogleg-0.18+ds/examples/src/org/ddogleg/example/ExampleDogArray.java ddogleg-0.22+ds/examples/src/org/ddogleg/example/ExampleDogArray.java --- ddogleg-0.18+ds/examples/src/org/ddogleg/example/ExampleDogArray.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/examples/src/org/ddogleg/example/ExampleDogArray.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.example; + +import org.ddogleg.struct.DogArray; +import org.ddogleg.struct.DogArray_F32; + +/** + * DogArrays and BigDogArrays are a dynamic array that will automatically grow and add new instances. They are + * designed to make recycling memory easy by managing the life cycle entirely from creating to re-initializing + * the data structures. Many convenience functions are also provided and it they support functional APIs. Access + * to the raw underlying internal data structures is also provided as this is intended for high performance + * applications. Implementations for object and primitive data structures are provided. + * + * The main difference between DogArrays and BigDogArrays is that DogArrays are a continuous chunk of memory + * while BigDogArrays are composed of multiple arrays, enabling it to scale to very very large arrays. + * + * While not covered here, FastArrays are dynamic object arrays that do not manage the creation or recycling + * of memory that are also available. + * + * @author Peter Abeles + */ +public class ExampleDogArray { + public static void main( String[] args ) { + // This will create an array of Point2D. New instances are created using the default constructor + // and when recycled they will be assigned values of zero. + // The second argument (reset lambda) is optional. If not provided then the state is not changed + // when recycled + var arrayObject = new DogArray<>(Point2D::new, ( p ) -> p.setTo(0, 0)); + + // Resize the array so that it has 5 elements + arrayObject.resize(2); + // Let's resize it to make it larger, but provide a lambda to initialize these values instead of the default + // (idx = the index, p = an element in the array) + arrayObject.resize(6, ( idx, p ) -> p.setTo(idx, 1)); + // Print so we can see what it looks like. Instead of the traditional for loop, let's use the functional API + arrayObject.forEach(p -> System.out.println(p.x + " " + p.y)); + + // When the order doesn't matter, you can "remove" elements with remove swap. This has a O(1) complexity + // but will swap the first for the last objects + arrayObject.removeSwap(1); + System.out.println("After removeSwap"); + arrayObject.forIdx(( idx, p ) -> System.out.println("p[" + idx + "] = " + p.x + " " + p.y)); + + // While remove will shift every element, maintaining their order but in O(N) time + arrayObject.remove(0); + System.out.println("After remove"); + arrayObject.forIdx(( idx, p ) -> System.out.println("p[" + idx + "] = " + p.x + " " + p.y)); + + // It's also possible to treat it like a list. Note that toList() does not declare any memory and recycles + // the returned object. This is very important in threaded applications. + for (Point2D p : arrayObject.toList()) { + p.y += 10; + } + + // Make it easy to see which output belongs to the code below + System.out.println("------------ Primitive Array\n"); + + // There are primitive versions that should (in theory) support the same API when it makes sense + var arrayPrimitive = new DogArray_F32(); + + // This will fill the first 5 elements with (0.5, 1.5, 2.5, ... ) + arrayPrimitive.resize(5, ( idx ) -> idx + 0.5f); + // This will fill the new elements (5 to 7) with -1 + arrayPrimitive.resize(8, -1); + // If you shrink the array size then no values are changed + arrayPrimitive.resize(7); + + // Let's print it out + System.out.print("[ "); + arrayPrimitive.forEach(v -> System.out.print(v + ", ")); + System.out.println(" ]"); + + // If you want to access elements in the reverse order, then getTail() can do that + System.out.println("tail[0]=" + arrayPrimitive.getTail(0) + " tail[2]=" + arrayPrimitive.getTail(2)); + + // There are also a few different variants of indexOf() for when you need to search and get the index + System.out.println("2.5 is at " + arrayPrimitive.indexOf(2.5f)); + + // Not previously discussed, but reserve will ensure that there is enough memory preallocate to support + // an array of the specified size before it needs to declare a new array internally + arrayPrimitive.reserve(20); + System.out.println("Reserve: array.size=" + arrayPrimitive.size + " data.length=" + arrayPrimitive.data.length); + + // If you are dealing with errors then its easy to get percentile errors by sorting then using get fraction + arrayPrimitive.sort(); + System.out.println("35% = " + arrayPrimitive.getFraction(0.35)); + } +} diff -Nru ddogleg-0.18+ds/examples/src/org/ddogleg/example/ExampleRobustModelFit.java ddogleg-0.22+ds/examples/src/org/ddogleg/example/ExampleRobustModelFit.java --- ddogleg-0.18+ds/examples/src/org/ddogleg/example/ExampleRobustModelFit.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/examples/src/org/ddogleg/example/ExampleRobustModelFit.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -18,10 +18,8 @@ package org.ddogleg.example; -import org.ddogleg.fitting.modelset.DistanceFromModel; -import org.ddogleg.fitting.modelset.ModelGenerator; import org.ddogleg.fitting.modelset.ModelManager; -import org.ddogleg.fitting.modelset.ModelMatcher; +import org.ddogleg.fitting.modelset.ModelMatcherPost; import org.ddogleg.fitting.modelset.ransac.Ransac; import java.util.ArrayList; @@ -48,12 +46,11 @@ //------------------------ Compute the solution // Let it know how to compute the model and fit errors ModelManager manager = new LineManager(); - ModelGenerator generator = new LineGenerator(); - DistanceFromModel distance = new DistanceFromLine(); // RANSAC or LMedS work well here - ModelMatcher alg = - new Ransac<>(234234, manager, generator, distance, 500, 0.01); + ModelMatcherPost alg = new Ransac<>(234234, 500, 0.01, manager, Point2D.class); + alg.setModel(LineGenerator::new, DistanceFromLine::new); + // ModelMatcher alg = // new LeastMedianOfSquares(234234,100,0.1,0.5,generator,distance); diff -Nru ddogleg-0.18+ds/examples/src/org/ddogleg/example/ExampleSchurComplementLeastSquares.java ddogleg-0.22+ds/examples/src/org/ddogleg/example/ExampleSchurComplementLeastSquares.java --- ddogleg-0.18+ds/examples/src/org/ddogleg/example/ExampleSchurComplementLeastSquares.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/examples/src/org/ddogleg/example/ExampleSchurComplementLeastSquares.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -26,7 +26,7 @@ import org.ejml.UtilEjml; import org.ejml.data.DMatrixSparseCSC; import org.ejml.data.DMatrixSparseTriplet; -import org.ejml.ops.ConvertDMatrixStruct; +import org.ejml.ops.DConvertMatrixStruct; import java.util.ArrayList; import java.util.List; @@ -244,8 +244,8 @@ } } - ConvertDMatrixStruct.convert(tripletLeft,left); - ConvertDMatrixStruct.convert(tripletRight,right); + DConvertMatrixStruct.convert(tripletLeft,left); + DConvertMatrixStruct.convert(tripletRight,right); } } diff -Nru ddogleg-0.18+ds/examples/src/org/ddogleg/example/Line2D.java ddogleg-0.22+ds/examples/src/org/ddogleg/example/Line2D.java --- ddogleg-0.18+ds/examples/src/org/ddogleg/example/Line2D.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/examples/src/org/ddogleg/example/Line2D.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -40,6 +40,7 @@ */ double x,y; + @Override public String toString() { return "Line2D( x="+x+" y="+y+" )"; } diff -Nru ddogleg-0.18+ds/examples/src/org/ddogleg/example/LineGenerator.java ddogleg-0.22+ds/examples/src/org/ddogleg/example/LineGenerator.java --- ddogleg-0.18+ds/examples/src/org/ddogleg/example/LineGenerator.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/examples/src/org/ddogleg/example/LineGenerator.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -27,13 +27,13 @@ * * @author Peter Abeles */ -public class LineGenerator implements ModelGenerator { +public class LineGenerator implements ModelGenerator { // a point at the origin (0,0) Point2D origin = new Point2D(); @Override - public boolean generate(List dataSet, Line2D output) { + public boolean generate( List dataSet, Line2D output ) { Point2D p1 = dataSet.get(0); Point2D p2 = dataSet.get(1); @@ -43,8 +43,8 @@ // Now that we have the slope, all we need is a line on the point (we pick p1) to find // the closest point on the line to the origin. This closest point is the parametrization. - double t = slopeX * ( origin.x - p1.x) + slopeY * ( origin.y - p1.y); - t /= slopeX * slopeX + slopeY * slopeY; + double t = slopeX*(origin.x - p1.x) + slopeY*(origin.y - p1.y); + t /= slopeX*slopeX + slopeY*slopeY; output.x = p1.x + t*slopeX; output.y = p1.y + t*slopeY; diff -Nru ddogleg-0.18+ds/examples/src/org/ddogleg/example/Point2D.java ddogleg-0.22+ds/examples/src/org/ddogleg/example/Point2D.java --- ddogleg-0.18+ds/examples/src/org/ddogleg/example/Point2D.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/examples/src/org/ddogleg/example/Point2D.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2013, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -30,6 +30,10 @@ this.y = y; } - public Point2D() { + public Point2D() {} + + public void setTo(double x, double y) { + this.x = x; + this.y = y; } } diff -Nru ddogleg-0.18+ds/generate/org/ddogleg/struct/GenerateBigDogArray.java ddogleg-0.22+ds/generate/org/ddogleg/struct/GenerateBigDogArray.java --- ddogleg-0.18+ds/generate/org/ddogleg/struct/GenerateBigDogArray.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/generate/org/ddogleg/struct/GenerateBigDogArray.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.struct; + +import org.apache.commons.io.FileUtils; + +import java.io.File; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; + +/** + * Uses the double[] implementation as a template to create all the others + * + * @author Peter Abeles + */ +public class GenerateBigDogArray { + public static String[] templates = new String[]{ + "src/org/ddogleg/struct/BigDogArray_F64.java", + "test/org/ddogleg/struct/TestBigDogArray_F64.java"}; + + public static class WordSwaps { + public String dataType; + public String suffix; + + public WordSwaps( String dataType, String suffix ) { + this.dataType = dataType; + this.suffix = suffix; + } + } + + public static void main( String[] args ) { + List swaps = new ArrayList<>(); + swaps.add(new WordSwaps("float", "_F32")); + swaps.add(new WordSwaps("byte", "_I8")); + swaps.add(new WordSwaps("int", "_I32")); + swaps.add(new WordSwaps("long", "_I64")); + swaps.add(new WordSwaps("boolean", "_B")); + + for (String template : templates) { + File templateFile = new File(template); + + try { + String templateString = FileUtils.readFileToString(templateFile, StandardCharsets.UTF_8); + for (WordSwaps swap : swaps) { + String modified = templateString.replace("double", swap.dataType); + modified = modified.replace("_F64", swap.suffix); + + String fileName = templateFile.getName().replace("_F64", swap.suffix); + + if (!fileName.contains("Test")) { + // Splice in a comment about it being generated + int warningLocation = modified.indexOf("\tpublic BigDogArray" + swap.suffix + "() {"); + modified = modified.substring(0, warningLocation) + + "\t// WARNING: Autogenerated from TupleDesc_F64. Do not modify.\n\n" + + modified.substring(warningLocation); + } + + FileUtils.write(new File(templateFile.getParent(), fileName), modified, StandardCharsets.UTF_8); + } + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + } +} diff -Nru ddogleg-0.18+ds/generate/org/ddogleg/struct/GenerateDogArray.java ddogleg-0.22+ds/generate/org/ddogleg/struct/GenerateDogArray.java --- ddogleg-0.18+ds/generate/org/ddogleg/struct/GenerateDogArray.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/generate/org/ddogleg/struct/GenerateDogArray.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.struct; + +import org.apache.commons.io.FileUtils; + +import java.io.File; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; + +/** + * Uses the double[] implementation as a template to create all the others + * + * @author Peter Abeles + */ +public class GenerateDogArray { + public static String[] templates = new String[]{ + "src/org/ddogleg/struct/DogArray_F64.java", + "test/org/ddogleg/struct/TestDogArray_F64.java"}; + + + public static class WordSwaps { + public String dataType; + public String suffix; + + public WordSwaps( String dataType, String suffix ) { + this.dataType = dataType; + this.suffix = suffix; + } + } + + public static void main( String[] args ) { + List swaps = new ArrayList<>(); + swaps.add( new WordSwaps("float","_F32")); + swaps.add( new WordSwaps("byte","_I8")); + swaps.add( new WordSwaps("int","_I32")); + swaps.add( new WordSwaps("long","_I64")); + swaps.add( new WordSwaps("boolean","_B")); + + for (String template : templates ) { + File templateFile = new File(template); + + try { + String templateString = FileUtils.readFileToString(templateFile, StandardCharsets.UTF_8); + for( WordSwaps swap : swaps) { + String modified = templateString.replace("double",swap.dataType); + modified = modified.replace("_F64",swap.suffix); + + String fileName = templateFile.getName().replace("_F64",swap.suffix); + + FileUtils.write(new File(templateFile.getParent(),fileName),modified,StandardCharsets.UTF_8); + } + } catch( IOException e ) { + throw new UncheckedIOException(e); + } + } + } +} diff -Nru ddogleg-0.18+ds/.github/workflows/gradle.yml ddogleg-0.22+ds/.github/workflows/gradle.yml --- ddogleg-0.18+ds/.github/workflows/gradle.yml 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/.github/workflows/gradle.yml 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,31 @@ +# This workflow will build a Java project with Gradle +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle + +name: Java CI with Gradle + +on: + push: + branches: [ SNAPSHOT ] + pull_request: + branches: [ SNAPSHOT ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + submodules: true + - name: Set up JDK + uses: actions/setup-java@v1 + with: + java-version: 11 + - name: Grant execute permission for gradlew + run: chmod +x gradlew + - name: Build with Gradle + run: ./gradlew assemble + - name: Unit Tests + run: ./gradlew test diff -Nru ddogleg-0.18+ds/.idea/codeStyles/codeStyleConfig.xml ddogleg-0.22+ds/.idea/codeStyles/codeStyleConfig.xml --- ddogleg-0.18+ds/.idea/codeStyles/codeStyleConfig.xml 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/.idea/codeStyles/codeStyleConfig.xml 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff -Nru ddogleg-0.18+ds/.idea/codeStyles/Project.xml ddogleg-0.22+ds/.idea/codeStyles/Project.xml --- ddogleg-0.18+ds/.idea/codeStyles/Project.xml 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/.idea/codeStyles/Project.xml 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,48 @@ + + + + \ No newline at end of file diff -Nru ddogleg-0.18+ds/.idea/copyright/ddogleg.xml ddogleg-0.22+ds/.idea/copyright/ddogleg.xml --- ddogleg-0.18+ds/.idea/copyright/ddogleg.xml 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/.idea/copyright/ddogleg.xml 2022-09-01 02:18:09.000000000 +0000 @@ -1,6 +1,6 @@ - \ No newline at end of file diff -Nru ddogleg-0.18+ds/misc/release-checklist.txt ddogleg-0.22+ds/misc/release-checklist.txt --- ddogleg-0.18+ds/misc/release-checklist.txt 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/misc/release-checklist.txt 2022-09-01 02:18:09.000000000 +0000 @@ -10,7 +10,7 @@ - update version in build.gradle - update change.txt - ./gradlew test -- ./gradlew install +- ./gradlew PublishToMavenLocal - Creating Files for Source Forge git clone git@github.com:lessthanoptimal/ddogleg.git VERSION=v0.15 @@ -24,10 +24,13 @@ ---- -Gradle +Releasing code to Maven Central -To post a SNAPSHOT or full release: -gradle uploadArchives +To post a SNAPSHOT: +./gradlew publish + +To post a full release: +./gradlew assemble;./gradlew publish -Dorg.gradle.parallel=false A SNAPSHOT is created if SNAPSHOT is in its name. diff -Nru ddogleg-0.18+ds/src/org/ddogleg/clustering/AssignCluster.java ddogleg-0.22+ds/src/org/ddogleg/clustering/AssignCluster.java --- ddogleg-0.18+ds/src/org/ddogleg/clustering/AssignCluster.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/clustering/AssignCluster.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2015, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -18,42 +18,34 @@ package org.ddogleg.clustering; -import java.io.Serializable; - /** * Used to assign a point to set of clusters. Clusters are given labels from 0 to N-1, where N is the number of * clusters. * * @author Peter Abeles */ -public interface AssignCluster extends Serializable { - +public interface AssignCluster { /** * Assigns the point to cluster which is the best fit. * * @param point Point which is to be assigned * @return Index of the cluster from 0 to N-1 */ - public int assign( D point ); + int assign(D point); /** * Performs a soft assignment of a point to all the clusters. Clusters with a better fit will have * a larger value in 'fit'. The sum of fit is equal to 1, unless everything is zero. Then it is zero. * * @param point Point which is to be assigned - * @param fit Storage for relative fit quality of each cluster. Length must be at least the number of clusters. + * @param fit Storage for relative fit quality of each cluster. Length must be at least the number of clusters. */ - public void assign( D point , double fit[] ); + void assign(D point, double[] fit); /** * Total number of clusters. - * @return The total number of clusters. - */ - public int getNumberOfClusters(); - - /** - * Creates an exact copy of this class. - * @return Copy of class + * + * @return The total number of clusters. */ - public AssignCluster copy(); + int getNumberOfClusters(); } diff -Nru ddogleg-0.18+ds/src/org/ddogleg/clustering/ComputeClusters.java ddogleg-0.22+ds/src/org/ddogleg/clustering/ComputeClusters.java --- ddogleg-0.18+ds/src/org/ddogleg/clustering/ComputeClusters.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/clustering/ComputeClusters.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2015, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -18,7 +18,7 @@ package org.ddogleg.clustering; -import java.util.List; +import org.ddogleg.struct.LArrayAccessor; /** * Given a set of points in N-dimensional space, compute a set of unique assignment for each point to a cluster. @@ -27,23 +27,23 @@ * * @author Peter Abeles */ -public interface ComputeClusters { +public interface ComputeClusters

{ /** * Must be called first to initializes internal data structures. Only needs to be called once. * - * @param pointDimension Number of degrees of freedom in each point. * @param randomSeed Seed for any random number generators used internally. */ - public void init( int pointDimension , long randomSeed ); + void initialize(long randomSeed); /** - * Computes a set of clusters which segment the points into numCluster sets. + * Computes a set of clusters which segment the points into numCluster sets. The number + * of clusters and points must be 1 or more. If this is not true then the behavior is undefined. * * @param points Set of points which are to be clustered. Not modified. * @param numCluster Number of clusters it will use to split the points. */ - public void process( List points , int numCluster ); + void process( LArrayAccessor

points, int numCluster); /** *

Returns a class which is used to assign a point to a cluster. Only invoked after @@ -56,7 +56,7 @@ * * @return Instance of {@link org.ddogleg.clustering.AssignCluster}. */ - public AssignCluster getAssignment(); + AssignCluster

getAssignment(); /** *

@@ -69,11 +69,17 @@ * * @return sum of distance between each point and their respective clusters. */ - public double getDistanceMeasure(); + double getDistanceMeasure(); /** * If set to true then information about status will be printed to standard out. By default verbose is off * @param verbose true for versbose mode. False for quite mode. */ - public void setVerbose( boolean verbose ); + void setVerbose(boolean verbose); + + /** + * Creates a new instance which has the same configuration and can be run in parallel. Some components + * can be shared as long as they are read only and thread safe. + */ + ComputeClusters

newInstanceThread(); } diff -Nru ddogleg-0.18+ds/src/org/ddogleg/clustering/ComputeMeanClusters.java ddogleg-0.22+ds/src/org/ddogleg/clustering/ComputeMeanClusters.java --- ddogleg-0.18+ds/src/org/ddogleg/clustering/ComputeMeanClusters.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/clustering/ComputeMeanClusters.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.clustering; + +import org.ddogleg.struct.DogArray_I32; +import org.ddogleg.struct.FastAccess; +import org.ddogleg.struct.LArrayAccessor; + +/** + * Abstract way to update the cluster mean values from a set of points which have been assigned to a single + * cluster. + * + * @author Peter Abeles + */ +public interface ComputeMeanClusters

{ + + /** + * Updates cluster means + * + * @param points (Input) access to point values + * @param assignments (Input) which cluster each point has been assigned to + * @param clusters (Output) Cluster means which are to be updated. + */ + void process( LArrayAccessor

points, DogArray_I32 assignments, FastAccess

clusters); + + /** + * Creates a new instance which has the same configuration and can be run in parallel. Some components + * can be shared as long as they are read only and thread safe. + */ + ComputeMeanClusters

newInstanceThread(); +} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/clustering/ConfigKMeans.java ddogleg-0.22+ds/src/org/ddogleg/clustering/ConfigKMeans.java --- ddogleg-0.18+ds/src/org/ddogleg/clustering/ConfigKMeans.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/clustering/ConfigKMeans.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.clustering; + +/** + * Configuration for K-Means clustering + * + * @author Peter Abeles + */ +public class ConfigKMeans { + /** Which initialization algorithm */ + public KMeansInitializers initializer = KMeansInitializers.PLUS_PLUS; + + /** Maximum number of iterations, across all seeds combined */ + public int maxIterations = 500; + + /** If it doesn't converge within this many iterations a new seed is created */ + public int reseedAfterIterations = 50; + + /** Maximum number of times it will reseed before stopping. If %le; then there is no limit. */ + public int maxReSeed = 10; + + /** Change in distance criteria when testing for convergence */ + public double convergeTol = 1e-8; + + public void setTo( ConfigKMeans src ) { + this.initializer = src.initializer; + this.maxIterations = src.maxIterations; + this.reseedAfterIterations = src.reseedAfterIterations; + this.maxReSeed = src.maxReSeed; + this.convergeTol = src.convergeTol; + } + + public void checkValidity() { + if (maxIterations < 0) + throw new IllegalArgumentException("maxIterations can't be negative"); + if (reseedAfterIterations < 0) + throw new IllegalArgumentException("maxConverge can't be negative"); + if (convergeTol < 0.0) + throw new IllegalArgumentException("convergeTol can't be negative"); + } +} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/clustering/FactoryClustering.java ddogleg-0.22+ds/src/org/ddogleg/clustering/FactoryClustering.java --- ddogleg-0.18+ds/src/org/ddogleg/clustering/FactoryClustering.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/clustering/FactoryClustering.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2015, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -20,17 +20,18 @@ import org.ddogleg.clustering.gmm.ExpectationMaximizationGmm_F64; import org.ddogleg.clustering.gmm.SeedFromKMeans_F64; -import org.ddogleg.clustering.kmeans.InitializeKMeans_F64; -import org.ddogleg.clustering.kmeans.InitializePlusPlus; -import org.ddogleg.clustering.kmeans.InitializeStandard_F64; -import org.ddogleg.clustering.kmeans.StandardKMeans_F64; +import org.ddogleg.clustering.kmeans.*; +import org.ddogleg.clustering.misc.EuclideanSqArrayF64; +import org.ddogleg.clustering.misc.MeanArrayF64; +import org.ddogleg.struct.DogLambdas; +import org.jetbrains.annotations.Nullable; /** * Factory for creating clustering algorithms. * * @author Peter Abeles */ -public class FactoryClustering { +@SuppressWarnings("unchecked") public class FactoryClustering { /** *

@@ -46,44 +47,125 @@ * @return ExpectationMaximizationGmm_F64 */ public static ExpectationMaximizationGmm_F64 gaussianMixtureModelEM_F64( - int maxIterations, int maxConverge , double convergeTol) { + int maxIterations, int maxConverge, double convergeTol, int pointDimension ) { - StandardKMeans_F64 kmeans = kMeans_F64(null,maxIterations,maxConverge,convergeTol); + ConfigKMeans configKMeans = new ConfigKMeans(); + configKMeans.reseedAfterIterations = maxConverge; + configKMeans.maxIterations = maxIterations; + configKMeans.convergeTol = convergeTol; + + StandardKMeans kmeans = kMeans(configKMeans, pointDimension, double[].class); SeedFromKMeans_F64 seeds = new SeedFromKMeans_F64(kmeans); - return new ExpectationMaximizationGmm_F64(maxIterations,convergeTol,seeds); + return new ExpectationMaximizationGmm_F64(maxIterations, convergeTol, pointDimension, seeds); + } + + /** + * K-Means using a primitive array, e.g. double[]. + * + * @param pointDimension Length of the array + * @param dataType Specifies the data type, e.g. double[].class + */ + public static StandardKMeans kMeans( @Nullable ConfigKMeans config, int pointDimension, Class dataType ) { + if (dataType != double[].class) + throw new IllegalArgumentException("Only double[] supported at this time."); + + return (StandardKMeans)kMeans(config, + new MeanArrayF64(pointDimension), + new EuclideanSqArrayF64(pointDimension), + () -> new double[pointDimension]); + } + + /** + * + * @param minimumForThreads The minimum number of points required for it to use concurrent code + */ + public static StandardKMeans kMeans_MT( @Nullable ConfigKMeans config, int pointDimension, + int minimumForThreads, Class dataType ) { + if (dataType != double[].class) + throw new IllegalArgumentException("Only double[] supported at this time."); + + return (StandardKMeans)kMeans_MT(config,minimumForThreads, + new MeanArrayF64(pointDimension), + new EuclideanSqArrayF64(pointDimension), + () -> new double[pointDimension]); } /** * High level interface for creating k-means cluster. If more flexibility is needed (e.g. custom seeds) - * then create and instance of {@link org.ddogleg.clustering.kmeans.StandardKMeans_F64} directly + * then create and instance of {@link StandardKMeans} directly * - * @param initializer Specify which method should be used to select the initial seeds for the clusters. null means default. - * @param maxIterations Maximum number of iterations it will perform. - * @param maxConverge Maximum iterations allowed before convergence. Re-seeded if it doesn't converge. - * @param convergeTol Distance based convergence tolerance. Try 1e-8 + * @param config Configuration for tuning parameters + * @param updateMeans Used to compute the means given point assignments + * @param factory Creates a new instance of a point * @return StandardKMeans_F64 */ - public static StandardKMeans_F64 kMeans_F64( KMeansInitializers initializer, - int maxIterations, int maxConverge , double convergeTol) { - InitializeKMeans_F64 seed; - - if( initializer == null ) { - seed = new InitializePlusPlus(); - } else { - switch (initializer) { - case PLUS_PLUS: - seed = new InitializePlusPlus(); - break; - - case STANDARD: - seed = new InitializeStandard_F64(); - break; - - default: - throw new RuntimeException("Unknown initializer " + initializer); - } + public static

StandardKMeans

kMeans( @Nullable ConfigKMeans config, + ComputeMeanClusters

updateMeans, + PointDistance

pointDistance, + DogLambdas.NewInstance

factory ) { + if (config == null) + config = new ConfigKMeans(); + + InitializeKMeans

seed; + + switch (config.initializer) { + case PLUS_PLUS: + seed = new InitializePlusPlus<>(); + break; + + case STANDARD: + seed = new InitializeStandard<>(); + break; + + default: + throw new RuntimeException("Unknown initializer " + config.initializer); } - return new StandardKMeans_F64(maxIterations,maxConverge,convergeTol,seed); + + StandardKMeans

alg = new StandardKMeans<>(updateMeans, seed, pointDistance, factory); + alg.convergeTol = config.convergeTol; + alg.maxIterations = config.maxIterations; + alg.reseedAfterIterations = config.reseedAfterIterations; + alg.maxReSeed = config.maxReSeed; + + return alg; + } + + /** + * + * @param minimumForThreads The minimum number of points required for it to use concurrent code + */ + public static

StandardKMeans

kMeans_MT( @Nullable ConfigKMeans config, + int minimumForThreads, + ComputeMeanClusters

updateMeans, + PointDistance

pointDistance, + DogLambdas.NewInstance

factory ) { + if (config == null) + config = new ConfigKMeans(); + + InitializeKMeans

seed; + + switch (config.initializer) { + case PLUS_PLUS: { + seed = new InitializePlusPlus_MT<>(factory); + ((InitializePlusPlus_MT)seed).setMinimumConcurrent(minimumForThreads); + } break; + + case STANDARD: + seed = new InitializeStandard<>(); // TODO make concurrent + break; + + default: + throw new RuntimeException("Unknown initializer " + config.initializer); + } + + var alg = new StandardKMeans_MT<>(updateMeans, seed, pointDistance, factory); + alg.convergeTol = config.convergeTol; + alg.maxIterations = config.maxIterations; + alg.reseedAfterIterations = config.reseedAfterIterations; + alg.maxReSeed = config.maxReSeed; + alg.setMinimumForConcurrent(minimumForThreads); + + return alg; } } diff -Nru ddogleg-0.18+ds/src/org/ddogleg/clustering/gmm/AssignGmm_F64.java ddogleg-0.22+ds/src/org/ddogleg/clustering/gmm/AssignGmm_F64.java --- ddogleg-0.18+ds/src/org/ddogleg/clustering/gmm/AssignGmm_F64.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/clustering/gmm/AssignGmm_F64.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2015, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -18,6 +18,8 @@ package org.ddogleg.clustering.gmm; +import lombok.Getter; +import lombok.Setter; import org.ddogleg.clustering.AssignCluster; import java.util.ArrayList; @@ -30,8 +32,8 @@ */ public class AssignGmm_F64 implements AssignCluster { - protected List mixture; - volatile GaussianLikelihoodManager glm; + protected @Getter @Setter List mixture; + protected GaussianLikelihoodManager glm; /** * Use reference to provided mixtures @@ -47,7 +49,7 @@ * Copy constructor */ public AssignGmm_F64( AssignGmm_F64 original ) { - mixture = new ArrayList(); + mixture = new ArrayList<>(); for (int i = 0; i < original.mixture.size(); i++) { GaussianGmm_F64 o = original.mixture.get(i); @@ -91,17 +93,4 @@ public int getNumberOfClusters() { return mixture.size(); } - - @Override - public AssignCluster copy() { - return new AssignGmm_F64(this); - } - - public List getMixture() { - return mixture; - } - - public void setMixture(List mixture) { - this.mixture = mixture; - } } diff -Nru ddogleg-0.18+ds/src/org/ddogleg/clustering/gmm/ExpectationMaximizationGmm_F64.java ddogleg-0.22+ds/src/org/ddogleg/clustering/gmm/ExpectationMaximizationGmm_F64.java --- ddogleg-0.18+ds/src/org/ddogleg/clustering/gmm/ExpectationMaximizationGmm_F64.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/clustering/gmm/ExpectationMaximizationGmm_F64.java 2022-09-01 02:18:09.000000000 +0000 @@ -20,12 +20,11 @@ import org.ddogleg.clustering.AssignCluster; import org.ddogleg.clustering.ComputeClusters; -import org.ddogleg.struct.FastQueue; -import org.ddogleg.struct.GrowQueue_F64; +import org.ddogleg.struct.DogArray; +import org.ddogleg.struct.DogArray_F64; +import org.ddogleg.struct.LArrayAccessor; import org.ejml.dense.row.CommonOps_DDRM; -import java.util.List; - /** * Standard expectation maximization based approach to fitting mixture-of-Gaussian models to a set of data. * A locally optimal maximum likelihood estimate is found. The full covariance is found. Some other @@ -41,20 +40,25 @@ // TODO Unconstrained covariance // TODO just diagonal covariance // TODO added shared and tied covariance? +@SuppressWarnings("NullAway.Init") public class ExpectationMaximizationGmm_F64 implements ComputeClusters { // Used to select initial parameters InitializeGmm_F64 selectInitial; // storage for mixture models - FastQueue mixture; + DogArray mixture; // info for each points - FastQueue info = new FastQueue<>(PointInfo::new); + @SuppressWarnings("NullAway") + DogArray info; - // Maximum number of iterations\ + // Maximum number of iterations int maxIterations; + // Number of elements in a point + int pointDimension; + // If the fractional change in score is less or equal to this value then it has converged. // ||prev-curr||/prev double convergeTol; @@ -63,7 +67,7 @@ GaussianLikelihoodManager likelihoodManager; // internal work space for computing the difference between the mean and point - double dx[] = new double[1]; + double[] dx = new double[1]; // compute chi-square error double errorChiSquare; @@ -80,18 +84,22 @@ */ public ExpectationMaximizationGmm_F64(int maxIterations, double convergeTol, + int pointDimension, InitializeGmm_F64 selectInitial) { this.maxIterations = maxIterations; this.convergeTol = convergeTol; this.selectInitial = selectInitial; + this.pointDimension = pointDimension; + + info = new DogArray<>(()->new PointInfo(pointDimension)); + mixture = new DogArray<>(()->new GaussianGmm_F64(pointDimension)); System.err.println("WARNING: GMM-EM is a work in progress! Might not work in your situation"); } @Override - public void init(final int pointDimension, long randomSeed) { - mixture = new FastQueue<>(()->new GaussianGmm_F64(pointDimension)); - selectInitial.init(pointDimension,randomSeed); + public void initialize(long randomSeed) { + selectInitial.init(pointDimension, randomSeed); if( dx.length < pointDimension ) dx = new double[pointDimension]; @@ -99,16 +107,19 @@ } @Override - public void process(List points, int numCluster) { + public void process( LArrayAccessor points, int numCluster) { // setup data structures mixture.resize(numCluster); + info.resize(points.size()); + for (int i = 0; i < points.size(); i++) { - PointInfo p = info.grow(); - p.point = points.get(i); + PointInfo p = info.get(i); + points.getCopy(i,p.point); p.weights.resize(numCluster); } if( verbose ) System.out.println("GMM-EM: Selecting initial seeds"); + // Select initial distributions selectInitial.selectSeeds(points,mixture.toList()); likelihoodManager.precomputeAll(); @@ -134,11 +145,6 @@ maximization(); likelihoodManager.precomputeAll(); } - - // clean up - for (int i = 0; i < info.size; i++) { - info.data[i].point = null; // de-reference so the memory could be freed - } info.reset(); } @@ -254,9 +260,17 @@ this.verbose = verbose; } + @Override public ComputeClusters newInstanceThread() { + throw new RuntimeException("Not yet implemented"); + } + public static class PointInfo { - public double[] point; // reference to the original input point - public GrowQueue_F64 weights = new GrowQueue_F64(); + public double[] point; + public DogArray_F64 weights = new DogArray_F64(); + + public PointInfo(int pointDimension) { + point = new double[pointDimension]; + } } } diff -Nru ddogleg-0.18+ds/src/org/ddogleg/clustering/gmm/GaussianGmm_F64.java ddogleg-0.22+ds/src/org/ddogleg/clustering/gmm/GaussianGmm_F64.java --- ddogleg-0.18+ds/src/org/ddogleg/clustering/gmm/GaussianGmm_F64.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/clustering/gmm/GaussianGmm_F64.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2017, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -29,6 +29,7 @@ * * @author Peter Abeles */ +@SuppressWarnings("NullAway.Init") public class GaussianGmm_F64 implements Serializable { // These specify the parameters of the Gaussian in the mixture public DMatrixRMaj mean; @@ -95,8 +96,8 @@ public GaussianGmm_F64 copy() { GaussianGmm_F64 out = new GaussianGmm_F64(mean.getNumElements()); - out.mean.set(mean); - out.covariance.set(covariance); + out.mean.setTo(mean); + out.covariance.setTo(covariance); out.weight = weight; return out; diff -Nru ddogleg-0.18+ds/src/org/ddogleg/clustering/gmm/GaussianLikelihoodManager.java ddogleg-0.22+ds/src/org/ddogleg/clustering/gmm/GaussianLikelihoodManager.java --- ddogleg-0.18+ds/src/org/ddogleg/clustering/gmm/GaussianLikelihoodManager.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/clustering/gmm/GaussianLikelihoodManager.java 2022-09-01 02:18:09.000000000 +0000 @@ -18,7 +18,7 @@ package org.ddogleg.clustering.gmm; -import org.ddogleg.struct.FastQueue; +import org.ddogleg.struct.DogArray; import org.ejml.LinearSolverSafe; import org.ejml.data.DMatrixRMaj; import org.ejml.dense.row.factory.LinearSolverFactory_DDRM; @@ -38,7 +38,7 @@ // Set of Gaussians which describe the mixture List mixtures; // Storage for precomputed likelihood functions - FastQueue precomputes; + DogArray precomputes; // used to compute likelihood LinearSolverDense solver; @@ -53,7 +53,7 @@ solver = LinearSolverFactory_DDRM.symmPosDef(pointDimension); solver = new LinearSolverSafe<>(solver); - precomputes = new FastQueue<>(()->new Likelihood(pointDimension)); + precomputes = new DogArray<>(()->new Likelihood(pointDimension)); diff = new DMatrixRMaj(pointDimension,1); } @@ -78,6 +78,7 @@ /** * Likelihood for a specific Gaussian */ + @SuppressWarnings("NullAway.Init") public class Likelihood { public GaussianGmm_F64 gaussian; diff -Nru ddogleg-0.18+ds/src/org/ddogleg/clustering/gmm/InitializeGmm_F64.java ddogleg-0.22+ds/src/org/ddogleg/clustering/gmm/InitializeGmm_F64.java --- ddogleg-0.18+ds/src/org/ddogleg/clustering/gmm/InitializeGmm_F64.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/clustering/gmm/InitializeGmm_F64.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2015, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -18,6 +18,8 @@ package org.ddogleg.clustering.gmm; +import org.ddogleg.struct.LArrayAccessor; + import java.util.List; /** @@ -32,14 +34,14 @@ * @param pointDimension Number of degrees of freedom in each point. * @param randomSeed Seed for any random number generators used internally. */ - public void init( int pointDimension, long randomSeed ); + void init(int pointDimension, long randomSeed); /** * * @param points (input) Set of points which is to be clustered. * @param seeds (output) List containing storage for the initial Gaussians. */ - public void selectSeeds( List points, List seeds ); + void selectSeeds( LArrayAccessor points, List seeds); /** * Turn on verbose output to standard out diff -Nru ddogleg-0.18+ds/src/org/ddogleg/clustering/gmm/SeedFromKMeans_F64.java ddogleg-0.22+ds/src/org/ddogleg/clustering/gmm/SeedFromKMeans_F64.java --- ddogleg-0.18+ds/src/org/ddogleg/clustering/gmm/SeedFromKMeans_F64.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/clustering/gmm/SeedFromKMeans_F64.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2017, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -18,9 +18,10 @@ package org.ddogleg.clustering.gmm; -import org.ddogleg.clustering.kmeans.StandardKMeans_F64; -import org.ddogleg.struct.FastQueue; -import org.ddogleg.struct.GrowQueue_I32; +import org.ddogleg.clustering.kmeans.StandardKMeans; +import org.ddogleg.struct.DogArray; +import org.ddogleg.struct.DogArray_I32; +import org.ddogleg.struct.LArrayAccessor; import org.ejml.data.DMatrixRMaj; import org.ejml.dense.row.CommonOps_DDRM; @@ -35,36 +36,35 @@ */ public class SeedFromKMeans_F64 implements InitializeGmm_F64 { - StandardKMeans_F64 kmeans; - GrowQueue_I32 totals = new GrowQueue_I32(); + StandardKMeans kmeans; + DogArray_I32 totals = new DogArray_I32(); - double dx[] = new double[1]; - int N; + double[] dx = new double[1]; + // degrees-of-freedom in the points + int dof; - public SeedFromKMeans_F64(StandardKMeans_F64 kmeans) { + public SeedFromKMeans_F64(StandardKMeans kmeans) { this.kmeans = kmeans; } @Override public void init(int pointDimension, long randomSeed) { - this.N = pointDimension; - kmeans.init(N,randomSeed); - if( dx.length < N ) { - dx = new double[N]; + this.dof = pointDimension; + kmeans.initialize(randomSeed); + if( dx.length < dof) { + dx = new double[dof]; } } @Override - public void selectSeeds(List points, List seeds) { - - totals.resize(seeds.size()); - totals.fill(0); + public void selectSeeds( LArrayAccessor points, List seeds) { + totals.resetResize(seeds.size(), 0); // initial cluster kmeans.process(points,seeds.size()); - GrowQueue_I32 labels = kmeans.getPointLabels(); - FastQueue means = kmeans.getClusterMeans(); + DogArray_I32 labels = kmeans.getAssignments(); + DogArray means = kmeans.getBestClusters(); // compute mixture models for (int i = 0; i < seeds.size(); i++) { @@ -76,23 +76,23 @@ // Perform the summation part of the covariance calculation and tally how many points are // in each cluster for (int i = 0; i < points.size(); i++) { - double[] p = points.get(i); + double[] point = points.getTemp(i); int label = labels.get(i); totals.data[label]++; double[] m = means.get(label); // compute the difference between the mean and the point - for (int j = 0; j < N; j++) { - dx[j] = m[j]-p[j]; + for (int j = 0; j < dof; j++) { + dx[j] = m[j]-point[j]; } // add to the covariance while taking advantage of symmetry DMatrixRMaj cov = seeds.get(label).covariance; - for (int j = 0; j < N; j++) { - for (int k = j; k < N; k++) { - cov.data[k*N+j] += dx[j]*dx[k]; + for (int j = 0; j < dof; j++) { + for (int k = j; k < dof; k++) { + cov.data[k*dof +j] += dx[j]*dx[k]; } } } @@ -100,9 +100,9 @@ // fill in the lower half for (int i = 0; i < seeds.size(); i++) { DMatrixRMaj cov = seeds.get(i).covariance; - for (int j = 0; j < N; j++) { + for (int j = 0; j < dof; j++) { for (int k = 0; k < j; k++) { - cov.data[k*N+j] = cov.data[j*N+k]; + cov.data[k*dof +j] = cov.data[j*dof +k]; } } } diff -Nru ddogleg-0.18+ds/src/org/ddogleg/clustering/kmeans/AssignKMeans_F64.java ddogleg-0.22+ds/src/org/ddogleg/clustering/kmeans/AssignKMeans_F64.java --- ddogleg-0.18+ds/src/org/ddogleg/clustering/kmeans/AssignKMeans_F64.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/clustering/kmeans/AssignKMeans_F64.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,129 +0,0 @@ -/* - * Copyright (c) 2012-2015, Peter Abeles. All Rights Reserved. - * - * This file is part of DDogleg (http://ddogleg.org). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ddogleg.clustering.kmeans; - -import org.ddogleg.clustering.AssignCluster; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * Implementation of {@link org.ddogleg.clustering.AssignCluster} for K-Means. Euclidean distance squared is - * used to select the best fit clusters to a point. This distance metric works well for hard assignment but can - * produce undesirable results for soft assignment, see JavaDoc. - * - * @author Peter Abeles - */ -public class AssignKMeans_F64 implements AssignCluster { - - List clusters; - - public AssignKMeans_F64( List clusters ) { - this.clusters = clusters; - } - - public AssignKMeans_F64( AssignKMeans_F64 original ) { - clusters = new ArrayList(); - for (int i = 0; i < original.clusters.size(); i++) { - clusters.add( original.clusters.get(i).clone()); - } - } - - @Override - public int assign(double[] point) { - - int best = -1; - double bestScore = Double.MAX_VALUE; - - for (int i = 0; i < clusters.size(); i++) { - double score = StandardKMeans_F64.distanceSq(point,clusters.get(i)); - if( score < bestScore ) { - bestScore = score; - best = i; - } - } - - return best; - } - - /** - *

Soft assignment is done by summing the total distance of the point from each cluster. Then for each cluster - * its value is set to total minus its distance. The output array is then normalized by dividing each element - * by the sum.

- * - *

- * When all clusters are approximately the same distance or one is clearly the closest this produces reasonable - * results. When multiple clusters are much closer than at least on other cluster then it effectively ignores - * the relative difference in distances between the closest points. There are several obvious heuristic "fixes" - * to this issue, but the best way to solve it is to simply use {@link org.ddogleg.clustering.gmm.AssignGmm_F64} - * instead. - *

- */ - @Override - public void assign(double[] point, double[] fit) { - Arrays.fill(fit,0); - - // compute and save distance to each cluster - double max = 0; - for (int i = 0; i < clusters.size(); i++) { - double d = StandardKMeans_F64.distanceSq(point,clusters.get(i)); - fit[i] = d; - if( d > max ) { - max = d; - } - } - - // normalize to reduce overflow - double total = 0; - for (int i = 0; i < clusters.size(); i++) { - total += fit[i] /= max; - } - - // ensure that the closer clusters are weighted more - double total2 = 0; - for (int i = 0; i < clusters.size(); i++) { - total2 += fit[i] = total - fit[i]; - } - - // normalize so that the sum is 1 - for (int i = 0; i < clusters.size(); i++) { - fit[i] /= total2; - } - - } - - @Override - public int getNumberOfClusters() { - return clusters.size(); - } - - @Override - public AssignCluster copy() { - return new AssignKMeans_F64(this); - } - - public List getClusters() { - return clusters; - } - - public void setClusters(List clusters) { - this.clusters = clusters; - } -} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/clustering/kmeans/AssignKMeans.java ddogleg-0.22+ds/src/org/ddogleg/clustering/kmeans/AssignKMeans.java --- ddogleg-0.18+ds/src/org/ddogleg/clustering/kmeans/AssignKMeans.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/clustering/kmeans/AssignKMeans.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.clustering.kmeans; + +import lombok.Getter; +import lombok.Setter; +import org.ddogleg.clustering.AssignCluster; +import org.ddogleg.clustering.PointDistance; +import org.ddogleg.struct.FastAccess; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Implementation of {@link org.ddogleg.clustering.AssignCluster} for K-Means. Euclidean distance squared is + * used to select the best fit clusters to a point. This distance metric works well for hard assignment but can + * produce undesirable results for soft assignment, see JavaDoc. + * + * @author Peter Abeles + */ +public class AssignKMeans

implements AssignCluster

, Serializable{ + + /** Each point is the mean of a cluster */ + @Getter @Setter List

clusters; + + /** Computes the distance two points are apart */ + @Getter @Setter PointDistance

distancer; + + public AssignKMeans(List

clusters, PointDistance

distancer) { + // Copy to make sure there are no serialization issues. + this.clusters = new ArrayList<>(clusters); + this.distancer = distancer; + } + + @Override + public int assign(P point) { + int best = -1; + double bestScore = Double.MAX_VALUE; + + for (int i = 0; i < clusters.size(); i++) { + double score = distancer.distance(point,clusters.get(i)); + if( score < bestScore ) { + bestScore = score; + best = i; + } + } + + return best; + } + + /** + *

Soft assignment is done by summing the total distance of the point from each cluster. Then for each cluster + * its value is set to total minus its distance. The output array is then normalized by dividing each element + * by the sum.

+ * + *

+ * When all clusters are approximately the same distance or one is clearly the closest this produces reasonable + * results. When multiple clusters are much closer than at least on other cluster then it effectively ignores + * the relative difference in distances between the closest points. There are several obvious heuristic "fixes" + * to this issue, but the best way to solve it is to simply use {@link org.ddogleg.clustering.gmm.AssignGmm_F64} + * instead. + *

+ */ + @Override + public void assign(P point, double[] fit) { + Arrays.fill(fit,0); + + // compute and save distance to each cluster + double max = 0; + for (int i = 0; i < clusters.size(); i++) { + double d = distancer.distance(point,clusters.get(i)); + fit[i] = d; + if( d > max ) { + max = d; + } + } + + // normalize to reduce overflow + double total = 0; + for (int i = 0; i < clusters.size(); i++) { + total += fit[i] /= max; + } + + // ensure that the closer clusters are weighted more + double total2 = 0; + for (int i = 0; i < clusters.size(); i++) { + total2 += fit[i] = total - fit[i]; + } + + // normalize so that the sum is 1 + for (int i = 0; i < clusters.size(); i++) { + fit[i] /= total2; + } + + } + + @Override + public int getNumberOfClusters() { + return clusters.size(); + } +} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/clustering/kmeans/InitializeKMeans_F64.java ddogleg-0.22+ds/src/org/ddogleg/clustering/kmeans/InitializeKMeans_F64.java --- ddogleg-0.18+ds/src/org/ddogleg/clustering/kmeans/InitializeKMeans_F64.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/clustering/kmeans/InitializeKMeans_F64.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2012-2015, Peter Abeles. All Rights Reserved. - * - * This file is part of DDogleg (http://ddogleg.org). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ddogleg.clustering.kmeans; - -import java.util.List; - -/** - * Selects the initial cluster positions for k-means - * - * @author Peter Abeles - */ -public interface InitializeKMeans_F64 { - - /** - * Initializes internal data structures. Must be called first. - * @param pointDimension NUmber of degrees of freedom in each point. - * @param randomSeed Seed for any random number generators used internally. - */ - void init( int pointDimension, long randomSeed ); - - /** - *

Given the set of points select reasonable seeds.

- * - * Duplicate Points: If there duplicate points in the input list it should not crash. This - * is true even if the number of unique points is less than the number of requested seeds. All the - * seeds will be filled but they do not need to be unique. - * - * - * @param points (Input) Set of points which is to be clustered. - * @param seeds (Output) List full of points which will act as the initial seed for k-means. Results - * are copied into this set. Must be filled initially. - */ - void selectSeeds( List points, List seeds ); -} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/clustering/kmeans/InitializeKMeans.java ddogleg-0.22+ds/src/org/ddogleg/clustering/kmeans/InitializeKMeans.java --- ddogleg-0.18+ds/src/org/ddogleg/clustering/kmeans/InitializeKMeans.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/clustering/kmeans/InitializeKMeans.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.clustering.kmeans; + +import org.ddogleg.clustering.PointDistance; +import org.ddogleg.struct.DogArray; +import org.ddogleg.struct.LArrayAccessor; + +/** + * Selects the initial cluster positions for k-means + * + * @author Peter Abeles + */ +public interface InitializeKMeans

{ + + /** + * Initializes internal data structures. Must be called first. + * + * @param distance Distance function between two points + * @param randomSeed Seed for any random number generators used internally. + */ + void initialize( PointDistance

distance, long randomSeed ); + + /** + *

Given the a set of points, select a set of seeds to initialize k-means from.

+ * + *
    + *
  • How duplicate points are handled isn't specified. It could result in two seeds having the same value or + * the number of selected seeds being less that the requested amount
  • + *
  • If the number of points is less than the number of seeds requested it will at most select one + * seed for each point
  • + *
* + * + * @param points (Input) Set of points which is to be clustered. + * @param requestedSeeds (Input) Number of seeds it will attempt to select. See above for exceptions. + * @param selectedSeeds (Output) Storage for selected seeds. They will be copied into it. + */ + void selectSeeds( LArrayAccessor

points, int requestedSeeds, DogArray

selectedSeeds ); + + /** + * Creates a new instance which has the same configuration and can be run in parallel. Some components + * can be shared as long as they are read only and thread safe. + */ + InitializeKMeans

newInstanceThread(); +} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/clustering/kmeans/InitializePlusPlus.java ddogleg-0.22+ds/src/org/ddogleg/clustering/kmeans/InitializePlusPlus.java --- ddogleg-0.18+ds/src/org/ddogleg/clustering/kmeans/InitializePlusPlus.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/clustering/kmeans/InitializePlusPlus.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2015, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -18,9 +18,11 @@ package org.ddogleg.clustering.kmeans; -import org.ddogleg.struct.GrowQueue_F64; +import org.ddogleg.clustering.PointDistance; +import org.ddogleg.struct.DogArray; +import org.ddogleg.struct.DogArray_F64; +import org.ddogleg.struct.LArrayAccessor; -import java.util.List; import java.util.Random; /** @@ -38,48 +40,72 @@ * * @author Peter Abeles */ -public class InitializePlusPlus implements InitializeKMeans_F64{ - +@SuppressWarnings("NullAway.Init") +public class InitializePlusPlus

implements InitializeKMeans

{ Random rand; - // the distance of each point to the cluster it is closest to - GrowQueue_F64 distance = new GrowQueue_F64(1); - double totalDistance; + + PointDistance

computeDistance; + + DogArray_F64 distances = new DogArray_F64(); + + double sumDistances; @Override - public void init(int pointDimension, long randomSeed) { + public void initialize( PointDistance

distance, long randomSeed ) { + this.computeDistance = distance; rand = new Random(randomSeed); } @Override - public void selectSeeds(List points, List seeds) { - if( seeds.size() > points.size() ) - throw new IllegalArgumentException("More seeds requested than points!"); - - distance.resize(points.size()); + public void selectSeeds( LArrayAccessor

points, int requestedSeeds, DogArray

selectedSeeds ) { + // Pre-allocate memory and reset the output + selectedSeeds.reserve(requestedSeeds); + selectedSeeds.reset(); + + // Handle edge cases here. There is nothing that can be done. + if (points.size() == 0 || requestedSeeds == 0) + return; // the first seed is randomly selected from the list of points - double[] seed = points.get( rand.nextInt(points.size()) ); - copyInto(seed,seeds.get(0)); - // compute the distance each points is from the seed - totalDistance = 0; - for (int i = 0; i < points.size(); i++) { - double[] p = points.get(i); - double d = StandardKMeans_F64.distanceSq(p,seed); - distance.data[i] = d; - totalDistance += d; + points.getCopy(rand.nextInt(points.size()), selectedSeeds.grow()); + + // Initialize the sum of distance from seeds to 0 + distances.resetResize(points.size(), Double.MAX_VALUE); + + // Update with information from the first seed + updateDistanceWithNewSeed(points, selectedSeeds.get(0)); + + // Select the remaining seeds probabilistically based on distance from prior seeds + for (int seedIdx = 1; seedIdx < requestedSeeds; seedIdx++) { + int selected = selectPointForNextSeed(rand.nextDouble()); + if (selected == -1) + break; + P seed = selectedSeeds.grow(); + points.getCopy(selected, seed); + updateDistanceWithNewSeed(points, seed); } + } + + @Override public InitializeKMeans

newInstanceThread() { + return new InitializePlusPlus<>(); + } - // iteratively select the next seed and update the list of point distances - for (int i = 1; i < seeds.size(); i++) { - if( totalDistance == 0 ) { - // if the total distance is zero that means there are duplicate points and that - // all the unique points have already been added as seeds. just select a point - // and copy it into rest of the seeds - copyInto(seed, seeds.get(i)); + /** + * A new seed has been added and the distance from the seeds needs to be updated + */ + protected void updateDistanceWithNewSeed( LArrayAccessor

points, P seed ) { + sumDistances = 0; + for (int pointIdx = 0; pointIdx < points.size(); pointIdx++) { + P point = points.getTemp(pointIdx); + + // Set the distance ot be the distance of th closest seed + double d = computeDistance.distance(point, seed); + double prevD = distances.data[pointIdx]; + if (d < prevD) { + distances.data[pointIdx] = d; + sumDistances += d; } else { - double target = rand.nextDouble(); - copyInto(selectNextSeed(points, target), seeds.get(i)); - updateDistances(points, seeds.get(i)); + sumDistances += prevD; } } } @@ -87,41 +113,23 @@ /** * Randomly selects the next seed. The chance of a seed is based upon its distance * from the closest cluster. Larger distances mean more likely. - * @param points List of all the points - * @param target Number from 0 to 1, inclusive - * @return Index of the selected seed + * + * @param targetFraction Number from 0 to 1, inclusive + * @return Index of the selected seed. Return -1 is no valid seeds left */ - protected final double[] selectNextSeed( List points , double target ) { + protected int selectPointForNextSeed( double targetFraction ) { // this won't select previously selected points because the distance will be zero // If the distance is zero it will simply skip over it double sum = 0; - for (int i = 0; i < distance.size(); i++) { - sum += distance.get(i); - double fraction = sum/totalDistance; - if( fraction >= target ) - return points.get(i); - } - throw new RuntimeException("This shouldn't happen"); - } - - /** - * Updates the list of distances from a point to the closest cluster. Update list of total distances - */ - protected final void updateDistances( List points , double []clusterNew ) { - totalDistance = 0; - for (int i = 0; i < distance.size(); i++) { - double dOld = distance.get(i); - double dNew = StandardKMeans_F64.distanceSq(points.get(i),clusterNew); - if( dNew < dOld ) { - distance.data[i] = dNew; - totalDistance += dNew; - } else { - totalDistance += dOld; - } + double targetValue = sumDistances*targetFraction; + for (int pointIdx = 0; pointIdx < distances.size(); pointIdx++) { + double d = distances.get(pointIdx); + sum += d; + if (sum >= targetValue && d != 0.0) + return pointIdx; } - } - private static void copyInto( double[] src, double[] dst) { - System.arraycopy(src,0,dst,0,src.length); + // If every single point has already been matched to a seed then they will all have zero distance + return -1; } } diff -Nru ddogleg-0.18+ds/src/org/ddogleg/clustering/kmeans/InitializePlusPlus_MT.java ddogleg-0.22+ds/src/org/ddogleg/clustering/kmeans/InitializePlusPlus_MT.java --- ddogleg-0.18+ds/src/org/ddogleg/clustering/kmeans/InitializePlusPlus_MT.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/clustering/kmeans/InitializePlusPlus_MT.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.clustering.kmeans; + +import lombok.Getter; +import lombok.Setter; +import org.ddogleg.DDoglegConcurrency; +import org.ddogleg.struct.DogLambdas; +import org.ddogleg.struct.LArrayAccessor; +import pabeles.concurrency.GrowArray; + +/** + *

Concurrent implementation of {@link InitializePlusPlus}

+ * + * @author Peter Abeles + */ +@SuppressWarnings("NullAway.Init") +public class InitializePlusPlus_MT

extends InitializePlusPlus

{ + + // Creates new points. Used as work space inside of threads + DogLambdas.NewInstance

factoryPoint; + + // Stores the sum computed in each thread + GrowArray threadsData; + + /** + * Minimum list size for it to use concurrent code. If a list is small it will run slower than the single + * thread version. By default this is zero since the optimal value is use case specific. + */ + @Getter @Setter int minimumConcurrent = 0; + + public InitializePlusPlus_MT( DogLambdas.NewInstance

factoryPoint ) { + this.factoryPoint = factoryPoint; + this.threadsData = new GrowArray<>(DistanceWork::new, DistanceWork::reset); + } + + @Override public InitializeKMeans

newInstanceThread() { + return new InitializePlusPlus_MT<>(factoryPoint); + } + + /** + * A new seed has been added and the distance from the seeds needs to be updated + */ + @Override protected void updateDistanceWithNewSeed( LArrayAccessor

points, P seed ) { + // see if it should run the single thread version instead + if (points.size() < minimumConcurrent) { + super.updateDistanceWithNewSeed(points, seed); + return; + } + + DDoglegConcurrency.loopBlocks(0, points.size(), threadsData, ( work, idx0, idx1 ) -> { + final P point = work.point; + double sum = 0.0; + for (int pointIdx = idx0; pointIdx < idx1; pointIdx++) { + points.getCopy(pointIdx, point); + + // Set the distance ot be the distance of th closest seed + double d = computeDistance.distance(point, seed); + double prevD = distances.data[pointIdx]; + if (d < prevD) { + distances.data[pointIdx] = d; + sum += d; + } else { + sum += prevD; + } + } + work.sum = sum; + }); + + sumDistances = 0; + for (int i = 0; i < threadsData.size(); i++) { + sumDistances += threadsData.get(i).sum; + } + } + + private class DistanceWork { + public double sum; + public P point = factoryPoint.newInstance(); + + public void reset() { + sum = 0; + } + } +} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/clustering/kmeans/InitializeStandard_F64.java ddogleg-0.22+ds/src/org/ddogleg/clustering/kmeans/InitializeStandard_F64.java --- ddogleg-0.18+ds/src/org/ddogleg/clustering/kmeans/InitializeStandard_F64.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/clustering/kmeans/InitializeStandard_F64.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,85 +0,0 @@ -/* - * Copyright (c) 2012-2015, Peter Abeles. All Rights Reserved. - * - * This file is part of DDogleg (http://ddogleg.org). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ddogleg.clustering.kmeans; - -import org.ddogleg.struct.GrowQueue_B; - -import java.util.ArrayList; -import java.util.List; -import java.util.Random; - -/** - * Seeds are selects by randomly picking points. This is the standard way to initialize k-means - * - * @author Peter Abeles - */ -public class InitializeStandard_F64 implements InitializeKMeans_F64 { - - Random rand; - - // used to ensure that the same point isn't returned twice - GrowQueue_B marked = new GrowQueue_B(); - List unused = new ArrayList(); - - @Override - public void init(int pointDimension, long randomSeed) { - rand = new Random(randomSeed); - } - - @Override - public void selectSeeds(List points, List seeds) { - - if( seeds.size() > points.size() ) - throw new IllegalArgumentException("More seeds requested than points!"); - - if( seeds.size()*2 > points.size() ) { - // If the probability of a collision is too great use a list which is modified - // this way each time a draw is made its going to not be used. However removing items - // from a list is an expensive opeartion - - unused.addAll(points); - for (int i = 0; i < seeds.size(); i++) { - double[] s = seeds.get(i); - double[] p = unused.remove(rand.nextInt(unused.size())); - - System.arraycopy(p, 0, s, 0, s.length); - } - unused.clear(); - } else { - // mark which points have been used. If it draws the same one twice just do another random draw - marked.resize(points.size()); - marked.fill(false); - int numSelected = 0; - while( numSelected < seeds.size() ) { - int drawn = rand.nextInt(points.size()); - - // see if has already been selected - if( marked.get(drawn)) - continue; - - marked.set(drawn,true); - double[] s = seeds.get(numSelected); - double[] p = points.get(drawn); - - System.arraycopy(p, 0, s, 0, s.length); - numSelected++; - } - } - } -} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/clustering/kmeans/InitializeStandard.java ddogleg-0.22+ds/src/org/ddogleg/clustering/kmeans/InitializeStandard.java --- ddogleg-0.18+ds/src/org/ddogleg/clustering/kmeans/InitializeStandard.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/clustering/kmeans/InitializeStandard.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.clustering.kmeans; + +import org.ddogleg.clustering.PointDistance; +import org.ddogleg.struct.DogArray; +import org.ddogleg.struct.DogArray_I32; +import org.ddogleg.struct.LArrayAccessor; + +import java.util.HashSet; +import java.util.Random; +import java.util.Set; + +/** + * Seeds are selects by randomly picking points. This is the standard way to initialize k-means + * + * @author Peter Abeles + */ +@SuppressWarnings("NullAway.Init") +public class InitializeStandard

implements InitializeKMeans

{ + + protected Random rand; + + // List of unselected indexes + protected DogArray_I32 unused = new DogArray_I32(); + // Set indicating which indexes have been used + protected Set used = new HashSet<>(); + + @Override + public void initialize( PointDistance

distance, long randomSeed ) { + rand = new Random(randomSeed); + } + + @Override + public void selectSeeds( LArrayAccessor

points, int requestedSeeds, DogArray

selectedSeeds ) { + selectedSeeds.reserve(requestedSeeds); + selectedSeeds.resize(0); + + // Handle edge cases here. There is nothing that can be done. + if (points.size() == 0 || requestedSeeds == 0) + return; + + // use different sampling approaches depending on the number of points/probability of selecting a valid seed + if (points.size() <= requestedSeeds) { + // Every point will be a seed in this situation. If there are duplicate points then there will be + // duplicate seeds + for (int i = 0; i < points.size(); i++) { + points.getCopy(i, selectedSeeds.grow()); + } + } else if (requestedSeeds*2 > points.size()) { + // The number of seeds is small relative to points, then select the seeds with an opt-out strategy + unused.resize(points.size()); + for (int i = 0; i < points.size(); i++) { + unused.set(i, i); + } + + for (int i = 0; i < requestedSeeds; i++) { + int index = unused.removeSwap(rand.nextInt(unused.size)); + points.getCopy(index, selectedSeeds.grow()); + } + } else { + // clear the used list since nothing is used yet + used.clear(); + + // randomly select indexes until the desired number have been selected + while (selectedSeeds.size() < requestedSeeds) { + int index = rand.nextInt(points.size()); + if (used.contains(index)) + continue; + // copy the point into the output set + points.getCopy(index, selectedSeeds.grow()); + + // mark this index as being used + used.add(index); + } + } + } + + @Override public InitializeKMeans

newInstanceThread() { + return new InitializeStandard<>(); + } +} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/clustering/kmeans/StandardKMeans_F64.java ddogleg-0.22+ds/src/org/ddogleg/clustering/kmeans/StandardKMeans_F64.java --- ddogleg-0.18+ds/src/org/ddogleg/clustering/kmeans/StandardKMeans_F64.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/clustering/kmeans/StandardKMeans_F64.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,295 +0,0 @@ -/* - * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. - * - * This file is part of DDogleg (http://ddogleg.org). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ddogleg.clustering.kmeans; - -import org.ddogleg.clustering.AssignCluster; -import org.ddogleg.clustering.ComputeClusters; -import org.ddogleg.struct.FastQueue; -import org.ddogleg.struct.GrowQueue_I32; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - *

- * Standard implementation of k-means [1], summary is provided below: - *

- *
    - *
  1. The initial seeds for each cluster is selected by the provided - * {@link InitializeKMeans_F64}. - *
  2. Each point is assigned to a cluster which minimizes the euclidean distance squared. - *
  3. New cluster centers are computed from the average of all points assigned to it. - *
- *

- * This will find a locally optimal solution which minimizes the sum of the distance-squared of each point - * to the cluster they are assigned to. - *

- * - *

- * Converged if, {@code(D[i] - D[i-1])/D[i] <= tol}, where D is the sum of point from cluster distance at iteration 'i', - * and tol is the convergence tolerance threshold. - *

- * - *

- * [1] Lloyd, S. P. (1957). "Least square quantization in PCM". Bell Telephone Laboratories Paper. - * Published in journal much later: Lloyd., S. P. (1982) - *

- * - * @author Peter Abeles - */ -public class StandardKMeans_F64 implements ComputeClusters { - - // number of elements in each point - int N; - - // flag for verbose mode - boolean verbose = false; - - // maximum number of iterations - int maxIterations; - // max iterations before convergence - int maxConverge; - - // It is considered to be converged when the change in sum score is <= than this amount. - double convergeTol; - - // selects the initial locations of each seed - InitializeKMeans_F64 seedSelector; - // Storage for the seeds - FastQueue clusters; - - // labels for all the points - GrowQueue_I32 labels = new GrowQueue_I32(); - - // work space for computing the new cluster centers. The sum for points in a cluster is computed on the fly - // instead of labeling each point and computing it later. Should save memory and maybe slightly faster. - FastQueue workClusters; - GrowQueue_I32 memberCount = new GrowQueue_I32(); - - // distance of the best match to the point - double bestDistance; - // sum of distances for all the points - double sumDistance; - // the best cluster centers - FastQueue bestClusters; - double bestClusterScore; - - /** - * Configures k-means parameters - * - * @param maxIterations Maximum number of iterations - * @param maxConverge Maximum iterations before it converges. It is reseeded if it doesn't converge. - * @param convergeTol Clusters have converged if the change in score is ≤ to this amount. - * @param seedSelector Used to select initial seeds for the clusters - */ - public StandardKMeans_F64(int maxIterations, int maxConverge, double convergeTol, - InitializeKMeans_F64 seedSelector) { - this.maxIterations = maxIterations; - this.maxConverge = maxConverge; - this.convergeTol = convergeTol; - this.seedSelector = seedSelector; - } - - @Override - public void init(final int pointDimension, long randomSeed) { - seedSelector.init(pointDimension,randomSeed); - this.N = pointDimension; - - clusters = createQueue(pointDimension); - workClusters = createQueue(pointDimension); - bestClusters = createQueue(pointDimension); - memberCount.resize(pointDimension); - } - - private FastQueue createQueue( final int pointDimension ) { - return new FastQueue<>(() -> new double[pointDimension]); - } - - @Override - public void process(List points, int numCluster) { - if( verbose ) - System.out.println("ENTER standard kmeans process"); - // declare data - clusters.resize(numCluster); - workClusters.resize(numCluster); - bestClusters.resize(numCluster); - memberCount.resize(numCluster); - labels.resize(points.size()); - - // select the initial seeds - seedSelector.selectSeeds(points, clusters.toList()); - - // run standard k-means - bestClusterScore = Double.MAX_VALUE; - double previousSum = Double.MAX_VALUE; - int lastConverge = 0; - for (int iteration = 0; iteration < maxIterations; iteration++) { - // zero the work seeds. These will be used - for (int i = 0; i < workClusters.size(); i++) { - Arrays.fill(workClusters.data[i],0); - } - memberCount.fill(0); - - // match points to the means and compute the score - matchPointsToClusters(points); - - // see if a better solution has been found and save it - if( sumDistance < bestClusterScore) { - bestClusterScore = sumDistance; - for (int i = 0; i < clusters.size(); i++) { - System.arraycopy(clusters.data[i], 0,bestClusters.data[i],0,N); - } - if( verbose ) - System.out.println(iteration+" better clusters score: "+bestClusterScore); - } - - // see if its taking too long - boolean reseed = iteration-lastConverge>=maxConverge; - - // check for convergence - double fractionalChange = 1.0-sumDistance/previousSum; - reseed |= fractionalChange >= 0 && fractionalChange <= convergeTol; - - - if( reseed) { - if( verbose ) - System.out.println(iteration+" Reseeding: "+sumDistance); - // try from a new random seed - seedSelector.selectSeeds(points, clusters.toList()); - previousSum = Double.MAX_VALUE; - lastConverge = iteration; - } else { - if( verbose && previousSum == Double.MAX_VALUE ) { - System.out.println(iteration+" first iteration: "+sumDistance); - } - previousSum = sumDistance; - updateClusterCenters(); - } - } - - if( verbose ) - System.out.println("EXIT standard kmeans process"); - } - - /** - * Finds the cluster which is the closest to each point. The point is the added to the sum for the cluster - * and its member count incremented - */ - protected void matchPointsToClusters(List points) { - sumDistance = 0; - for (int i = 0; i < points.size(); i++) { - double[]p = points.get(i); - - // find the cluster which is closest to the point - int bestCluster = findBestMatch(p); - - // sum up all the points which are members of this cluster - double[] c = workClusters.get(bestCluster); - for (int j = 0; j < c.length; j++) { - c[j] += p[j]; - } - memberCount.data[bestCluster]++; - labels.data[i] = bestCluster; - sumDistance += bestDistance; - } - } - - /** - * Searches for this cluster which is the closest to p - */ - protected int findBestMatch(double[] p) { - int bestCluster = -1; - bestDistance = Double.MAX_VALUE; - - for (int j = 0; j < clusters.size; j++) { - double d = distanceSq(p,clusters.get(j)); - if( d < bestDistance ) { - bestDistance = d; - bestCluster = j; - } - } - return bestCluster; - } - - /** - * Sets the location of each cluster to the average location of all its members. - */ - protected void updateClusterCenters() { - // compute the new centers of each cluster - for (int i = 0; i < clusters.size; i++) { - double mc = memberCount.get(i); - double[] w = workClusters.get(i); - double[] c = clusters.get(i); - - for (int j = 0; j < w.length; j++) { - c[j] = w[j] / mc; - } - } - } - - /** - * Returns the euclidean distance squared between the two poits - */ - protected static double distanceSq(double[] a, double[] b) { - double sum = 0; - for (int i = 0; i < a.length; i++) { - double d = a[i]-b[i]; - sum += d*d; - } - return sum; - } - - /** - * Returns the labels assigned to each point - */ - public GrowQueue_I32 getPointLabels() { - return labels; - } - - /** - * Returns the mean of each cluster - */ - public FastQueue getClusterMeans() { - return bestClusters; - } - - @Override - public AssignCluster getAssignment() { - - // creating a new list here to make serialization easier - List list = new ArrayList(); - list.addAll( bestClusters.toList() ); - - return new AssignKMeans_F64(list); - } - - /** - * Computes the potential function. The sum of distance for each point from their cluster centers.\ - */ - @Override - public double getDistanceMeasure() { - return sumDistance; - } - - @Override - public void setVerbose(boolean verbose) { - this.verbose = verbose; - } -} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/clustering/kmeans/StandardKMeans.java ddogleg-0.22+ds/src/org/ddogleg/clustering/kmeans/StandardKMeans.java --- ddogleg-0.18+ds/src/org/ddogleg/clustering/kmeans/StandardKMeans.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/clustering/kmeans/StandardKMeans.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.clustering.kmeans; + +import lombok.Getter; +import lombok.Setter; +import org.ddogleg.clustering.AssignCluster; +import org.ddogleg.clustering.ComputeClusters; +import org.ddogleg.clustering.ComputeMeanClusters; +import org.ddogleg.clustering.PointDistance; +import org.ddogleg.struct.DogArray; +import org.ddogleg.struct.DogArray_I32; +import org.ddogleg.struct.DogLambdas; +import org.ddogleg.struct.LArrayAccessor; + +/** + *

+ * Standard implementation of k-means [1], summary is provided below: + *

+ *
    + *
  1. The initial seeds for each cluster is selected by the provided + * {@link InitializeKMeans}. + *
  2. Each point is assigned to a cluster which minimizes the euclidean distance squared. + *
  3. New cluster centers are computed from the average of all points assigned to it. + *
+ *

+ * This will find a locally optimal solution which minimizes the sum of the distance-squared of each point + * to the cluster they are assigned to. + *

+ * + *

+ * Converged if, {@code(D[i] - D[i-1])/D[i] <= tol}, where D is the sum of point from cluster distance at iteration 'i', + * and tol is the convergence tolerance threshold. + *

+ * + *

+ * [1] Lloyd, S. P. (1957). "Least square quantization in PCM". Bell Telephone Laboratories Paper. + * Published in journal much later: Lloyd., S. P. (1982) + *

+ * + * @author Peter Abeles + */ +@SuppressWarnings("NullAway.Init") +public class StandardKMeans

implements ComputeClusters

{ + + // flag for verbose mode + boolean verbose = false; + + /** maximum number of iterations */ + public @Getter @Setter int maxIterations = 100; + /** max iterations before it will reseed */ + public @Getter @Setter int reseedAfterIterations = 20; + /** max number of times it will re-seed */ + public @Getter @Setter int maxReSeed = 5; + + /** It is considered to be converged when the change in sum score is <= than this amount. */ + public @Getter @Setter double convergeTol = 1e-8; + + // Used to update the mean after assignments have been made + public ComputeMeanClusters

updateMeans; + // selects the initial locations of each seed + public InitializeKMeans

seedSelector; + // Storage for the seeds + DogArray

workClusters; + + // Computes the distance between two points + PointDistance

distancer; + + // Creates new instances of points + DogLambdas.NewInstance

factory; + + /** labels for all the points */ + final @Getter DogArray_I32 assignments = new DogArray_I32(); + + /** Number of points assigned to each cluster */ + final @Getter DogArray_I32 memberCount = new DogArray_I32(); + + // sum of distances for all the points + double sumDistance; + + // the best cluster centers + final @Getter DogArray

bestClusters; + /** Distance from cluster score for best cluster */ + @Getter double bestClusterScore; + final DogArray_I32 bestMemberCount = new DogArray_I32(); + + + /** + * Configures k-means parameters + * + * @param seedSelector Used to select initial seeds for the clusters + */ + public StandardKMeans( ComputeMeanClusters

updateMeans, + InitializeKMeans

seedSelector, + PointDistance

distancer, + DogLambdas.NewInstance

factory) { + this.updateMeans = updateMeans; + this.seedSelector = seedSelector; + this.distancer = distancer; + this.factory = factory; + + workClusters = new DogArray<>(factory::newInstance); + bestClusters = new DogArray<>(factory::newInstance); + } + + @Override + public void initialize( long randomSeed ) { + if (convergeTol >= 1.0 || convergeTol < 0.0) + throw new IllegalArgumentException("convergeTol must be 0 <= tol < 1.0, not "+convergeTol); + seedSelector.initialize(distancer, randomSeed); + } + + @Override + public void process( LArrayAccessor

points, int numCluster ) { + if (numCluster <= 0) + throw new IllegalArgumentException("There must be at least one cluster"); + if (points.size() == 0) + throw new IllegalArgumentException("There must be at least one point"); + + if (verbose) + System.out.println("ENTER standard kmeans process"); + // declare data + workClusters.resize(numCluster); + bestClusters.resize(numCluster); + + // select the initial seeds + seedSelector.selectSeeds(points, numCluster, workClusters); + + // un standard k-means + bestClusterScore = Double.MAX_VALUE; + sumDistance = Double.MAX_VALUE; + double previousSum = Double.MAX_VALUE; + int lastConverge = 0; + + // Abort if it re-seeded many times. + int numReSeeded = 0; + int maxReSeed = this.maxReSeed <= 0 ? Integer.MAX_VALUE : this.maxReSeed; + for (int iteration = 0; iteration < maxIterations && numReSeeded < maxReSeed; iteration++) { + // match points to the means + matchPointsToClusters(points, workClusters); + + // see if its taking too long + boolean reseed = iteration - lastConverge >= reseedAfterIterations; + + // check for convergence + double fractionalChange = 1.0 - sumDistance/previousSum; + reseed |= fractionalChange >= 0 && fractionalChange <= convergeTol; + + if (reseed) { + // see if a better solution has been found and save it + if (sumDistance < bestClusterScore) { + saveBestCluster(points); + } + if (verbose) + System.out.println(iteration + " Reseeding, distance = " + sumDistance); + // try from a new random seed + seedSelector.selectSeeds(points, numCluster, workClusters); + previousSum = Double.MAX_VALUE; + lastConverge = iteration; + numReSeeded++; + } else { + if (verbose && previousSum == Double.MAX_VALUE) { + System.out.println(iteration + " first iteration: " + sumDistance); + } + previousSum = sumDistance; + + // Given the current assignments, update the cluster centers + updateMeans.process(points, assignments, workClusters); + } + } + + // see if a better solution has been found and save it + if (sumDistance < bestClusterScore) { + saveBestCluster(points); + } + + // Make sure the points are assigned to the best cluster + matchPointsToClusters(points, bestClusters); + + // copy the best into the output member count + memberCount.setTo(bestMemberCount); + + if (verbose) + System.out.println("EXIT standard kmeans process"); + } + + /** + * Copies the current cluster into the best cluster + */ + private void saveBestCluster( LArrayAccessor

points ) { + bestClusterScore = sumDistance; + + // copy current cluster into best cluster + bestClusters.reserve(workClusters.size); + bestClusters.reset(); + for (int i = 0; i < workClusters.size; i++) { + points.copy(workClusters.get(i), bestClusters.grow()); + } + bestMemberCount.setTo(memberCount); + + if (verbose) + System.out.println(" better clusters score: " + bestClusterScore); + } + + @Override + public AssignCluster

getAssignment() { + return new AssignKMeans<>(bestClusters.toList(), distancer); + } + + /** + * Finds the cluster which is the closest to each point. The point is the added to the sum for the cluster + * and its member count incremented + */ + protected void matchPointsToClusters( LArrayAccessor

points, DogArray

clusters ) { + // reset the member counts to zero for each cluster + memberCount.resetResize(clusters.size, 0); + + // updated inside the call to findBestMatch + sumDistance = 0; + + // NOTE: This is a good candidate for optimizing + // Maybe reverse loop order by having outer loop go through clusters + // instead of doing points.getTemp() compute the distance directly on the internal array + + // Assign each point a single cluster + assignments.resize(points.size()); + for (int i = 0; i < points.size(); i++) { + P point = points.getTemp(i); + + // find the cluster which is closest to the point + int assignment = findBestMatch(point, clusters); + assignments.set(i, assignment); + // increment the number of points assigned to this cluster + memberCount.data[assignment]++; + } + } + + /** + * Searches for this cluster which is the closest to p + */ + protected int findBestMatch( final P p, final DogArray

clusters ) { + int bestCluster = -1; + double bestDistance = Double.MAX_VALUE; + + for (int clusterIdx = 0; clusterIdx < clusters.size; clusterIdx++) { + double d = distancer.distance(p, clusters.get(clusterIdx)); + if (d < bestDistance) { + bestDistance = d; + bestCluster = clusterIdx; + } + } + sumDistance += bestDistance; + return bestCluster; + } + + /** + * Computes the potential function. The sum of distance for each point from their cluster centers.\ + */ + @Override + public double getDistanceMeasure() { + return sumDistance; + } + + @Override + public void setVerbose( boolean verbose ) { + this.verbose = verbose; + } + + @Override public ComputeClusters

newInstanceThread() { + var spawn = new StandardKMeans<>( + updateMeans.newInstanceThread(), + seedSelector.newInstanceThread(), + distancer.newInstanceThread(),factory); + + spawn.convergeTol = convergeTol; + spawn.maxIterations = maxIterations; + spawn.reseedAfterIterations = reseedAfterIterations; + spawn.verbose = verbose; + + return spawn; + } +} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/clustering/kmeans/StandardKMeans_MT.java ddogleg-0.22+ds/src/org/ddogleg/clustering/kmeans/StandardKMeans_MT.java --- ddogleg-0.18+ds/src/org/ddogleg/clustering/kmeans/StandardKMeans_MT.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/clustering/kmeans/StandardKMeans_MT.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.clustering.kmeans; + +import lombok.Getter; +import lombok.Setter; +import org.ddogleg.DDoglegConcurrency; +import org.ddogleg.clustering.ComputeClusters; +import org.ddogleg.clustering.ComputeMeanClusters; +import org.ddogleg.clustering.PointDistance; +import org.ddogleg.struct.DogArray; +import org.ddogleg.struct.DogArray_I32; +import org.ddogleg.struct.DogLambdas; +import org.ddogleg.struct.LArrayAccessor; +import pabeles.concurrency.GrowArray; + +/** + *

Concurrent implementation of {@link StandardKMeans}

+ * + * @author Peter Abeles + */ +@SuppressWarnings("NullAway.Init") +public class StandardKMeans_MT

extends StandardKMeans

{ + GrowArray workspace; + + /** + * Minimum list size for it to use concurrent code. If a list is small it will run slower than the single + * thread version. By default this is zero since the optimal value is use case specific. + */ + @Getter @Setter int minimumForConcurrent = 0; + + /** + * Configures k-means parameters + * + * @param seedSelector Used to select initial seeds for the clusters + */ + public StandardKMeans_MT( ComputeMeanClusters

updateMeans, + InitializeKMeans

seedSelector, + PointDistance

distancer, + DogLambdas.NewInstance

factory ) { + super(updateMeans, seedSelector, distancer, factory); + + workspace = new GrowArray<>(MatchData::new, MatchData::reset); + } + + /** + * Finds the cluster which is the closest to each point. The point is the added to the sum for the cluster + * and its member count incremented + */ + @Override protected void matchPointsToClusters( LArrayAccessor

points, DogArray

clusters ) { + // see if it should run the single thread version instead + if (points.size() < minimumForConcurrent) { + super.matchPointsToClusters(points, clusters); + return; + } + assignments.resize(points.size()); + + DDoglegConcurrency.loopBlocks(0, points.size(), workspace, ( work, idx0, idx1 ) -> { + final DogArray_I32 memberCount = work.memberCount; + // reset the member counts to zero for each cluster + memberCount.resetResize(clusters.size, 0); + final P point = work.point; + + // Assign each point a single cluster + for (int i = idx0; i < idx1; i++) { + points.getCopy(i, point); + + // find the cluster which is closest to the point + int assignment = findBestMatch(point, clusters, work); + assignments.set(i, assignment); // threads won't modify the same elements + // increment the number of points assigned to this cluster + memberCount.data[assignment]++; + } + }); + + // Stitch results back together from the threads + memberCount.resetResize(clusters.size, 0); + sumDistance = 0; + + for (int i = 0; i < workspace.size(); i++) { + MatchData md = workspace.get(i); + sumDistance += md.sumDistance; + for (int clusterIdx = 0; clusterIdx < clusters.size; clusterIdx++) { + memberCount.data[clusterIdx] += md.memberCount.data[clusterIdx]; + } + } + } + + /** + * Searches for this cluster which is the closest to p + */ + protected int findBestMatch( P p, DogArray

clusters, MatchData match ) { + int bestCluster = -1; + double bestDistance = Double.MAX_VALUE; + + for (int clusterIdx = 0; clusterIdx < clusters.size; clusterIdx++) { + double d = distancer.distance(p, clusters.get(clusterIdx)); + if (d < bestDistance) { + bestDistance = d; + bestCluster = clusterIdx; + } + } + match.sumDistance += bestDistance; + return bestCluster; + } + + @Override public ComputeClusters

newInstanceThread() { + var spawn = new StandardKMeans_MT<>( + updateMeans.newInstanceThread(), + seedSelector.newInstanceThread(), + distancer.newInstanceThread(), factory); + + spawn.convergeTol = convergeTol; + spawn.maxIterations = maxIterations; + spawn.reseedAfterIterations = reseedAfterIterations; + spawn.verbose = verbose; + + return spawn; + } + + private class MatchData { + public double sumDistance; + public P point; + DogArray_I32 memberCount = new DogArray_I32(); + + public MatchData() { + point = factory.newInstance(); + } + + public void reset() { + sumDistance = 0; + memberCount.reset(); + } + } +} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/clustering/misc/EuclideanSqArrayF64.java ddogleg-0.22+ds/src/org/ddogleg/clustering/misc/EuclideanSqArrayF64.java --- ddogleg-0.18+ds/src/org/ddogleg/clustering/misc/EuclideanSqArrayF64.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/clustering/misc/EuclideanSqArrayF64.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.clustering.misc; + +import org.ddogleg.clustering.PointDistance; + +import java.io.Serializable; + +/** + * Returns Euclidean distance squared for double[] points. + * + * @author Peter Abeles + */ +public class EuclideanSqArrayF64 implements PointDistance, Serializable { + // Number of elements in the array + final int arrayLength; + + public EuclideanSqArrayF64(int arrayLength ) { + this.arrayLength = arrayLength; + } + + @Override + public double distance(double[] a, double[] b) { + double sum = 0.0; + for (int i = 0; i < arrayLength; i++) { + double d = a[i]-b[i]; + sum += d*d; + } + return sum; + } + + @Override public PointDistance newInstanceThread() { + return this; + } +} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/clustering/misc/ListAccessor.java ddogleg-0.22+ds/src/org/ddogleg/clustering/misc/ListAccessor.java --- ddogleg-0.18+ds/src/org/ddogleg/clustering/misc/ListAccessor.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/clustering/misc/ListAccessor.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.clustering.misc; + +import org.ddogleg.struct.DogLambdas; +import org.ddogleg.struct.LArrayAccessor; + +import java.util.List; + +/** + * Wrapper around {@link List} for {@link LArrayAccessor}. + * + * @author Peter Abeles + */ +public class ListAccessor

implements LArrayAccessor

{ + List

list; + DogLambdas.Copy

copier; + Class

type; + + public ListAccessor(List

list, DogLambdas.Copy

copier, Class

type ) { + this.list = list; + this.copier = copier; + this.type = type; + } + + @Override public P getTemp( int index ) { + return list.get(index); + } + + @Override public void getCopy( int index, P dst ) { + copier.copy(list.get(index), dst); + } + + @Override public void copy( P src, P dst ) { + copier.copy(src, dst); + } + + @Override + public int size() { + return list.size(); + } + + @Override public Class

getElementType() { + return type; + } +} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/clustering/misc/MeanArrayF64.java ddogleg-0.22+ds/src/org/ddogleg/clustering/misc/MeanArrayF64.java --- ddogleg-0.18+ds/src/org/ddogleg/clustering/misc/MeanArrayF64.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/clustering/misc/MeanArrayF64.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.clustering.misc; + +import org.ddogleg.clustering.ComputeMeanClusters; +import org.ddogleg.struct.DogArray_I32; +import org.ddogleg.struct.FastAccess; +import org.ddogleg.struct.LArrayAccessor; + +import java.util.Arrays; + +/** + * Computes the mean for points composed of double[] + * + * @author Peter Abeles + */ +public class MeanArrayF64 implements ComputeMeanClusters { + + final int length; + + DogArray_I32 counts = new DogArray_I32(); + + public MeanArrayF64(int length) { + this.length = length; + } + + @Override + public void process( LArrayAccessor points, + DogArray_I32 assignments, + FastAccess clusters) { + + if (assignments.size != points.size()) + throw new IllegalArgumentException("Points and assignments need to be the same size"); + + // set the number of points in each cluster to zero and zero the clusters + counts.resetResize(clusters.size, 0); + for (int i = 0; i < clusters.size; i++) { + Arrays.fill(clusters.get(i),0,length,0.0); + } + + // Compute the sum of all points in each cluster + for (int pointIdx = 0; pointIdx < points.size(); pointIdx++) { + double[] point = points.getTemp(pointIdx); + + int clusterIdx = assignments.get(pointIdx); + counts.data[clusterIdx]++; + double[] cluster = clusters.get(clusterIdx); + for (int i = 0; i < length; i++) { + cluster[i] += point[i]; + } + } + + // Divide to get the average value in each cluster + for (int clusterIdx = 0; clusterIdx < clusters.size; clusterIdx++) { + double[] cluster = clusters.get(clusterIdx); + double divisor = counts.get(clusterIdx); + for (int i = 0; i < length; i++) { + cluster[i] /= divisor; + } + } + } + + @Override public ComputeMeanClusters newInstanceThread() { + return new MeanArrayF64(length); + } +} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/clustering/PointDistance.java ddogleg-0.22+ds/src/org/ddogleg/clustering/PointDistance.java --- ddogleg-0.18+ds/src/org/ddogleg/clustering/PointDistance.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/clustering/PointDistance.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.clustering; + +/** + * Computes the distance between two points. Each point is a tuple. + * + * @author Peter Abeles + */ +public interface PointDistance

{ + /** + * Computes the distance between the two input points + * @param a point + * @param b point + * @return distance + */ + double distance(P a , P b ); + + /** + * Creates a new instance which has the same configuration and can be run in parallel. Some components + * can be shared as long as they are read only and thread safe. + */ + PointDistance

newInstanceThread(); +} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/combinatorics/Combinations.java ddogleg-0.22+ds/src/org/ddogleg/combinatorics/Combinations.java --- ddogleg-0.18+ds/src/org/ddogleg/combinatorics/Combinations.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/combinatorics/Combinations.java 2022-09-01 02:18:09.000000000 +0000 @@ -18,6 +18,8 @@ package org.ddogleg.combinatorics; +import org.jetbrains.annotations.Nullable; + import java.util.ArrayList; import java.util.List; @@ -60,6 +62,7 @@ * 345 * **/ +@SuppressWarnings({"NullAway.Init"}) public class Combinations< T > { protected List a; // the original unmolested list @@ -83,8 +86,7 @@ init( a , bucketSize ); } - public Combinations(){ - } + public Combinations(){} /** * Initialize with a new list and bucket size @@ -92,7 +94,7 @@ * @param list List which is to be symbols * @param bucketSize Size of the bucket */ - public void init( List list , int bucketSize ) { + public final void init( List list , int bucketSize ) { if( list.size() < bucketSize ) { throw new RuntimeException("There needs to be more than or equal to elements in the 'list' that there are in the bucket"); } @@ -236,7 +238,7 @@ * @param storage Optional storage. If null a list is created. clear() is automatically called. * @return List containing the bucket */ - public List getBucket(List storage) { + public List getBucket( @Nullable List storage) { if( storage == null ) storage = new ArrayList(); else @@ -256,7 +258,7 @@ * @param storage Optional storage. If null a list is created. clear() is automatically called. * @return List containing the bucket */ - public List getOutside( List storage ) + public List getOutside( @Nullable List storage ) { if( storage == null ) { storage = new ArrayList(); diff -Nru ddogleg-0.18+ds/src/org/ddogleg/combinatorics/Permute.java ddogleg-0.22+ds/src/org/ddogleg/combinatorics/Permute.java --- ddogleg-0.18+ds/src/org/ddogleg/combinatorics/Permute.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/combinatorics/Permute.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2015, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -18,6 +18,8 @@ package org.ddogleg.combinatorics; +import org.jetbrains.annotations.Nullable; + import java.util.ArrayList; import java.util.List; @@ -64,8 +66,8 @@ public class Permute< T > { protected List list; - private int indexes[]; - private int counters[]; + private int[] indexes; + private int[] counters; private int total; private int permutation; @@ -77,9 +79,6 @@ init( list ); } - public Permute() { - } - /** * Initializes the permutation for a new list * @@ -198,7 +197,7 @@ * @param storage Optional storage. If null a new list will be declared. * @return Current permutation */ - public List getPermutation(List storage) { + public List getPermutation( @Nullable List storage) { if( storage == null ) storage = new ArrayList(); else diff -Nru ddogleg-0.18+ds/src/org/ddogleg/DDoglegConcurrency.java ddogleg-0.22+ds/src/org/ddogleg/DDoglegConcurrency.java --- ddogleg-0.18+ds/src/org/ddogleg/DDoglegConcurrency.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/DDoglegConcurrency.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg; + +import pabeles.concurrency.ConcurrencyOps; + +/** + * Used to turn on and off functions/classes which can automatically switch between concurrent operations + * + * @author Peter Abeles + */ +public class DDoglegConcurrency extends ConcurrencyOps { + /** Used to toggle auto matic switching to concurrent algorithms */ + public static boolean USE_CONCURRENT = false; + + /** + * Sets the maximum number of threads available in the thread pool and adjusts USE_CONCURRENT. If + * the number of threads is less than 2 then USE_CONCURRENT will be set to false and the single thread + * version of code will be called. Otherwise USE_CONCURRENT will be true and the max threads in the pool + * set to the specified number. + * + * @param maxThreads Maximum number of threads. ≤ 1 means it will not be threaded. + */ + public static void setMaxThreads( int maxThreads ) { + ConcurrencyOps.setMaxThreads(maxThreads); + USE_CONCURRENT = maxThreads > 1; + } + + public static boolean isUseConcurrent() { + return USE_CONCURRENT; + } +} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/fitting/modelset/distance/FitByMeanStatistics.java ddogleg-0.22+ds/src/org/ddogleg/fitting/modelset/distance/FitByMeanStatistics.java --- ddogleg-0.18+ds/src/org/ddogleg/fitting/modelset/distance/FitByMeanStatistics.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/fitting/modelset/distance/FitByMeanStatistics.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2013, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -20,8 +20,8 @@ import org.ddogleg.fitting.modelset.DistanceFromModel; +import java.util.ArrayDeque; import java.util.Iterator; -import java.util.LinkedList; /** @@ -29,13 +29,14 @@ * * @author Peter Abeles */ +@SuppressWarnings("NullAway.Init") public class FitByMeanStatistics implements StatisticalFit { protected DistanceFromModel modelError; - protected LinkedList> allPoints = new LinkedList>(); + protected ArrayDeque> allPoints = new ArrayDeque<>(); // the number of standard deviations away that points are pruned - private double pruneThreshold; + private final double pruneThreshold; // the mean error private double meanError; @@ -50,7 +51,7 @@ } @Override - public void init(DistanceFromModel modelError, LinkedList> allPoints ) { + public void init(DistanceFromModel modelError, ArrayDeque> allPoints ) { this.modelError = modelError; this.allPoints = allPoints; @@ -71,7 +72,7 @@ Point pt = iter.next().data; // only prune points which are less accurate than the mean - if (modelError.computeDistance(pt) - meanError > thresh) { + if (modelError.distance(pt) - meanError > thresh) { iter.remove(); } } @@ -92,7 +93,7 @@ for (PointIndex inlier : allPoints) { Point pt = inlier.data; - meanError += modelError.computeDistance(pt); + meanError += modelError.distance(pt); } meanError /= size; @@ -105,7 +106,7 @@ for (PointIndex inlier : allPoints) { Point pt = inlier.data; - double e = modelError.computeDistance(pt) - meanError; + double e = modelError.distance(pt) - meanError; stdError += e * e; } diff -Nru ddogleg-0.18+ds/src/org/ddogleg/fitting/modelset/distance/FitByMedianStatistics.java ddogleg-0.22+ds/src/org/ddogleg/fitting/modelset/distance/FitByMedianStatistics.java --- ddogleg-0.18+ds/src/org/ddogleg/fitting/modelset/distance/FitByMedianStatistics.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/fitting/modelset/distance/FitByMedianStatistics.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2013, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -21,8 +21,8 @@ import org.ddogleg.fitting.modelset.DistanceFromModel; import org.ddogleg.sorting.QuickSort_F64; +import java.util.ArrayDeque; import java.util.Iterator; -import java.util.LinkedList; /** @@ -31,11 +31,12 @@ * * @author Peter Abeles */ +@SuppressWarnings("NullAway.Init") public class FitByMedianStatistics implements StatisticalFit { private DistanceFromModel modelError; // set of points which contains all the inliers - private LinkedList> allPoints; + private ArrayDeque> allPoints; // The fraction of samples that are not pruned private double pruneThreshold; @@ -64,7 +65,7 @@ } @Override - public void init(DistanceFromModel modelError, LinkedList> allPoints ) { + public void init(DistanceFromModel modelError, ArrayDeque> allPoints ) { this.modelError = modelError; this.allPoints = allPoints; } @@ -82,7 +83,7 @@ int index = 0; while( iter.hasNext() ) { Point pt = iter.next().data; - errors[index++] = modelError.computeDistance(pt); + errors[index++] = modelError.distance(pt); } System.arraycopy(errors, 0, origErrors, 0, size); diff -Nru ddogleg-0.18+ds/src/org/ddogleg/fitting/modelset/distance/StatisticalDistanceModelMatcher.java ddogleg-0.22+ds/src/org/ddogleg/fitting/modelset/distance/StatisticalDistanceModelMatcher.java --- ddogleg-0.18+ds/src/org/ddogleg/fitting/modelset/distance/StatisticalDistanceModelMatcher.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/fitting/modelset/distance/StatisticalDistanceModelMatcher.java 2022-09-01 02:18:09.000000000 +0000 @@ -20,8 +20,8 @@ import org.ddogleg.fitting.modelset.*; +import java.util.ArrayDeque; import java.util.ArrayList; -import java.util.LinkedList; import java.util.List; @@ -42,40 +42,40 @@ public class StatisticalDistanceModelMatcher implements ModelMatcher { // maximum number of times it will perform the pruning process - private int maxIterations; + private final int maxIterations; // if the error changes by less than this amount it stops iterating - private double minChange; + private final double minChange; // current best fit parameters protected Model param; protected Model currParam; // what computes the error metrics - private StatisticalFit errorAlg; + private final StatisticalFit errorAlg; // error in previous iteration protected double oldCenter; // the error for the current fit parameters protected double centerError; // if the center error is less than this value it stops iterating immediately - private double exitCenterError; + private final double exitCenterError; // if the error is more than this amount it failed - private double failError; + private final double failError; // the minimum number of points that can be left over before it is considered a failure - private int minFitPoints; + private final int minFitPoints; // computes a set of model parameters from a list of points - private ModelFitter modelFitter; + private final ModelGenerator modelFitter; // computes the difference between the model and a point - private DistanceFromModel modelError; + private final DistanceFromModel modelError; // converts the model into an array parameter format - private ModelCodec codec; + private final ModelCodec codec; // list containing points that are to be pruned - LinkedList> pruneList = new LinkedList>(); + ArrayDeque> pruneList = new ArrayDeque<>(); // set of points which fit the model - private List inliers = new ArrayList(); + private final List inliers = new ArrayList<>(); // list of indexes converting it from match set to input list private int []matchToInput = new int[1]; @@ -103,7 +103,7 @@ StatisticalDistance statistics, double pruneThreshold, ModelManager modelManager , - ModelFitter modelFitter, + ModelGenerator modelFitter, DistanceFromModel modelError, ModelCodec codec ) { this.maxIterations = maxIterations; @@ -120,11 +120,11 @@ switch (statistics) { case MEAN: - errorAlg = new FitByMeanStatistics(pruneThreshold); + errorAlg = new FitByMeanStatistics<>(pruneThreshold); break; case PERCENTILE: - errorAlg = new FitByMedianStatistics(pruneThreshold); + errorAlg = new FitByMedianStatistics<>(pruneThreshold); break; default: @@ -144,7 +144,7 @@ pruneList.clear(); inliers.clear(); for( int i = 0; i < dataSet.size(); i++ ) - pruneList.add( new PointIndex(dataSet.get(i),i)); + pruneList.add(new PointIndex<>(dataSet.get(i), i)); inliers.clear(); errorAlg.init(modelError, pruneList); @@ -161,7 +161,7 @@ inliers.add(p.data); } - if (!modelFitter.fitModel(inliers, null, currParam)) { + if (!modelFitter.generate(inliers, currParam)) { // failed to fit the model, so stop before it screws things up break; } @@ -206,8 +206,8 @@ */ protected double computeDiff(Model modelA, Model modelB ) { - double paramA[] = new double[ codec.getParamLength() ]; - double paramB[] = new double[ codec.getParamLength() ]; + double[] paramA = new double[ codec.getParamLength() ]; + double[] paramB = new double[ codec.getParamLength() ]; codec.encode(modelA,paramA); codec.encode(modelB,paramB); diff -Nru ddogleg-0.18+ds/src/org/ddogleg/fitting/modelset/distance/StatisticalFit.java ddogleg-0.22+ds/src/org/ddogleg/fitting/modelset/distance/StatisticalFit.java --- ddogleg-0.18+ds/src/org/ddogleg/fitting/modelset/distance/StatisticalFit.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/fitting/modelset/distance/StatisticalFit.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2013, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -21,7 +21,7 @@ import org.ddogleg.fitting.modelset.DistanceFromModel; -import java.util.LinkedList; +import java.util.ArrayDeque; /** @@ -37,7 +37,7 @@ * @param modelDistance Computes the error between a point and the model * @param allPoints Contains all the points which are currently considered part of the model. */ - void init(DistanceFromModel modelDistance, LinkedList> allPoints ); + void init(DistanceFromModel modelDistance, ArrayDeque> allPoints ); /** * Returns the computed statistical error. diff -Nru ddogleg-0.18+ds/src/org/ddogleg/fitting/modelset/DistanceFromModel.java ddogleg-0.22+ds/src/org/ddogleg/fitting/modelset/DistanceFromModel.java --- ddogleg-0.18+ds/src/org/ddogleg/fitting/modelset/DistanceFromModel.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/fitting/modelset/DistanceFromModel.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -20,7 +20,6 @@ import java.util.List; - /** *

* Computes the distance a sample point is from the provided model. @@ -32,16 +31,16 @@ * in 3D space before the transform has been applied and the observed location after the transform * has been applied. *

+ * * @author Peter Abeles */ public interface DistanceFromModel { - /** * Sets the model parameters. * * @param model Model parameters. */ - void setModel(Model model); + void setModel( Model model ); /** * Computes the distance the point is from the model. @@ -49,16 +48,16 @@ * @param pt Point being evaluated. Not modified. * @return Distance the point is from the model. */ - double computeDistance(Point pt); + double distance( Point pt ); /** * Computes the distance a set of points is from the model and saves the results * in the provided array. * - * @param points Set of points which are to be evaluated. + * @param points Set of points which are to be evaluated. * @param distance Where model distance is stored. */ - void computeDistance(List points, double distance[]); + void distances( List points, double[] distance ); /** * Returns a class for the input point object diff -Nru ddogleg-0.18+ds/src/org/ddogleg/fitting/modelset/InlierFraction.java ddogleg-0.22+ds/src/org/ddogleg/fitting/modelset/InlierFraction.java --- ddogleg-0.18+ds/src/org/ddogleg/fitting/modelset/InlierFraction.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/fitting/modelset/InlierFraction.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.fitting.modelset; + +/** + * Specifies inlier set as a fraction + */ +public interface InlierFraction { + /** + * Specifies the inlier set as a fraction + * + * @param threshold inlier threshold. Must be from 0 to 1, inclusive. + */ + void setErrorFraction(double threshold); + + /** + * Gets the inlier set's fractional value + */ + double getErrorFraction(); +} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/fitting/modelset/InlierThreshold.java ddogleg-0.22+ds/src/org/ddogleg/fitting/modelset/InlierThreshold.java --- ddogleg-0.18+ds/src/org/ddogleg/fitting/modelset/InlierThreshold.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/fitting/modelset/InlierThreshold.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.fitting.modelset; + +/** + * Specifies a fit threshold for an inlier set based upon a maximum allowed error + */ +public interface InlierThreshold { + /** + * Specifies the "fit" criteria for inlier set as being equal to or less than this + * @param threshold inlier threshold + */ + void setThresholdFit(double threshold); + + /** + * Returns the inlier threshold + */ + double getThresholdFit(); +} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/fitting/modelset/lmeds/LeastMedianOfSquares.java ddogleg-0.22+ds/src/org/ddogleg/fitting/modelset/lmeds/LeastMedianOfSquares.java --- ddogleg-0.18+ds/src/org/ddogleg/fitting/modelset/lmeds/LeastMedianOfSquares.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/fitting/modelset/lmeds/LeastMedianOfSquares.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2022, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -18,68 +18,77 @@ package org.ddogleg.fitting.modelset.lmeds; - -import org.ddogleg.fitting.modelset.DistanceFromModel; -import org.ddogleg.fitting.modelset.ModelGenerator; -import org.ddogleg.fitting.modelset.ModelManager; -import org.ddogleg.fitting.modelset.ModelMatcher; +import lombok.Getter; +import lombok.Setter; +import org.ddogleg.fitting.modelset.*; import org.ddogleg.fitting.modelset.ransac.Ransac; import org.ddogleg.sorting.QuickSelect; +import org.ddogleg.struct.DogArray_F64; +import org.ddogleg.struct.DogArray_I32; +import org.ddogleg.struct.Factory; +import org.ddogleg.struct.FastArray; +import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.Random; +import static org.ddogleg.fitting.modelset.ransac.Ransac.addSelect; +import static org.ddogleg.fitting.modelset.ransac.Ransac.randomDraw; /** *

- * Another technique similar to RANSAC known as Least Median of Squares (LMedS). For each iteration a small number N points are selected. A model - * is fit to these points and then the error is computed for the whole set. The model which minimizes the - * median is selected as the final model. No pruning or formal selection of inlier set is done. + * Another technique similar to RANSAC known as Least Median of Squares (LMedS). For each iteration a small + * number N points are selected. A model is fit to these points and then the error is computed for the whole + * set. The model which minimizes the median is selected as the final model. No pruning or formal + * selection of inlier set is done. *

+ * * @author Peter Abeles */ // TODO Better algorithm for selecting the inlier set. // Maybe revert this back to the way it was before and just have it be a separate alg entirely. -public class LeastMedianOfSquares implements ModelMatcher { - // random number generator for selecting points - private Random rand; - private long randSeed; +@SuppressWarnings("NullAway.Init") +public class LeastMedianOfSquares implements ModelMatcherPost, InlierFraction { + /** random number generator for selecting points */ + @Getter private final long randSeed; - // number of times it performs its fit cycle - private int totalCycles; + // Each trial has its own seed to enable concurrent implementations that will produce identical results + protected final FastArray trialRNG = new FastArray<>(Random.class); + + /** number of times it performs its fit cycle */ + @Getter protected final int totalCycles; // how many points it samples to generate a model from - private int sampleSize; + protected int sampleSize; // if the best model has more than this error then it is considered a bad match - private double maxMedianError; - // fits a model to the provided data - private ModelGenerator generator; - // computes the error for a point to the model - private DistanceFromModel errorMetric; - - // where the initial small set of points is stored - private List smallSet = new ArrayList(); - - // parameter being considered - private Model candidate; - // the parameter with the best error - private Model bestParam; - private double bestMedian; + protected final double maxMedianError; + protected final ModelManager ModelManager; + + /** Used to create model generators for each thread */ + @Getter @Nullable Factory> factoryGenerator; + + /** Used to create distance functions for each thread */ + @Getter @Nullable Factory> factoryDistance; + + // list of indexes converting it from match set to input list + protected int[] matchToInput = new int[1]; + + protected volatile double bestMedian; // The specifies the error fraction its optimizing against. Almost always this should be 0.5 - private double errorFraction = 0.5; // 0.5 = median + protected double errorFraction = 0.5; // 0.5 = median - // copy of the input data set so that it can be modified - protected List dataSet = new ArrayList<>(); + protected List inlierSet; + protected final double inlierFrac; - // stores all the errors for quicker sorting - private double []errors = new double[1]; + protected @Nullable TrialHelper helper; - // list of indexes converting it from match set to input list - private int []matchToInput = new int[1]; + /** Optional function for initializing generator and distance functions */ + protected @Setter @Nullable Ransac.InitializeModels initializeModels; - private List inlierSet; - private double inlierFrac; + Class modelType; + Class pointType; /** * Configures the algorithm. @@ -87,33 +96,30 @@ * @param randSeed Random seed used internally. * @param totalCycles Number of random draws it will make when estimating model parameters. * @param maxMedianError If the best median error is larger than this it is considered a failure. - * @param inlierFraction Data which is this fraction or lower is considered an inlier and used to recompute model parameters at the end. Set to 0 to turn off. Domain: 0 to 1. - * @param generator Creates a list of model hypotheses from a small set of points. - * @param errorMetric Computes the error between a point and a model + * @param inlierFraction Data which is this fraction or lower is considered an inlier and used to + * recompute model parameters at the end. Set to 0 to turn off. Domain: 0 to 1. */ - public LeastMedianOfSquares( long randSeed , - int totalCycles , - double maxMedianError , - double inlierFraction , + public LeastMedianOfSquares( long randSeed, + int totalCycles, + double maxMedianError, + double inlierFraction, ModelManager modelManager, - ModelGenerator generator, - DistanceFromModel errorMetric ) - { + Class pointType ) { + if (totalCycles <= 0) + throw new IllegalArgumentException("Number of cycles must be positive"); + this.randSeed = randSeed; - this.rand = new Random(randSeed); this.totalCycles = totalCycles; this.maxMedianError = maxMedianError; this.inlierFrac = inlierFraction; - this.generator = generator; - this.errorMetric = errorMetric; + this.pointType = pointType; + this.ModelManager = modelManager; - bestParam = modelManager.createModelInstance(); - candidate = modelManager.createModelInstance(); - this.sampleSize = generator.getMinimumPoints(); - - if( inlierFrac > 0.0 ) { - inlierSet = new ArrayList(); - } else if( inlierFrac > 1.0 ) { + this.modelType = (Class)modelManager.createModelInstance().getClass(); + + if (inlierFrac > 0.0) { + inlierSet = new ArrayList<>(); + } else if (inlierFrac > 1.0) { throw new IllegalArgumentException("Inlier fraction must be <= 1"); } } @@ -123,16 +129,21 @@ * * @param randSeed Random seed used internally. * @param totalCycles Number of random draws it will make when estimating model parameters. - * @param generator Creates a list of model hypotheses from a small set of points. - * @param errorMetric Computes the error between a point and a model */ - public LeastMedianOfSquares( long randSeed , - int totalCycles , + public LeastMedianOfSquares( long randSeed, + int totalCycles, ModelManager modelManager, - ModelGenerator generator, - DistanceFromModel errorMetric ) - { - this(randSeed,totalCycles,Double.MAX_VALUE,0,modelManager,generator,errorMetric); + Class pointType ) { + this(randSeed, totalCycles, Double.MAX_VALUE, 0, modelManager, pointType); + } + + @Override + public void setModel( Factory> factoryGenerator, + Factory> factoryDistance ) { + this.factoryGenerator = factoryGenerator; + this.factoryDistance = factoryDistance; + this.helper = new TrialHelper(); + sampleSize = helper.modelGenerator.getMinimumPoints(); } /** @@ -140,65 +151,67 @@ * * @param sampleSize Number of points sampled when computing the model. */ - public void setSampleSize(int sampleSize) { + public void setSampleSize( int sampleSize ) { this.sampleSize = sampleSize; } @Override - public boolean process(List _dataSet) { - if( _dataSet.size() < sampleSize ) + public boolean process( List dataSet ) { + if (dataSet.size() < sampleSize) return false; - dataSet.clear(); - dataSet.addAll(_dataSet); - + checkTrialGenerators(); + int N = dataSet.size(); // make sure the array is large enough. If not declare a new one that is - if( errors.length < N ) { - errors = new double[ N ]; + if (matchToInput.length < N) { matchToInput = new int[N]; } + TrialHelper helper = Objects.requireNonNull(this.helper, "Need to call setModel()"); + helper.initialize(N); bestMedian = Double.MAX_VALUE; - for( int i = 0; i < totalCycles; i++ ) { - Ransac.randomDraw(dataSet, sampleSize, smallSet, rand); - - if( generator.generate(smallSet, candidate) ) { - errorMetric.setModel(candidate); - errorMetric.computeDistance(_dataSet,errors); - - double median = QuickSelect.select(errors, (int)(N*errorFraction+0.5), N); - - if( median < bestMedian ) { - bestMedian = median; - Model t = bestParam; - bestParam = candidate; - candidate = t; - } + for (int trial = 0; trial < totalCycles; trial++) { + // See RANSAC for a detailed description for why this is done. It's related to concurrency + randomDraw(helper.selectedIdx, N, sampleSize, trialRNG.get(trial)); + addSelect(helper.selectedIdx, sampleSize, dataSet, helper.initialSample); + + if (!helper.modelGenerator.generate(helper.initialSample, helper.candidate)) + continue; + + helper.modelDistance.setModel(helper.candidate); + helper.modelDistance.distances(dataSet, helper.errors.data); + + double median = QuickSelect.select(helper.errors.data, (int)(N*errorFraction + 0.5), N); + + if (median < bestMedian) { + helper.swapModels(); + bestMedian = median; } } // if configured to do so compute the inlier set - computeInlierSet(_dataSet, N); + computeInlierSet(dataSet, N, helper); return bestMedian <= maxMedianError; } - private void computeInlierSet(List dataSet, int n) { - int numPts = (int)(n *inlierFrac); + protected void computeInlierSet( List dataSet, int n, + TrialHelper helper ) { + int numPts = (int)(n*inlierFrac); - if( inlierFrac > 0 && numPts > sampleSize ) { + if (inlierFrac > 0 && numPts > sampleSize) { inlierSet.clear(); - errorMetric.setModel(bestParam); - errorMetric.computeDistance(dataSet,errors); + helper.modelDistance.setModel(helper.bestParam); + helper.modelDistance.distances(dataSet, helper.errors.data); - int []indexes = new int[n]; - QuickSelect.selectIndex(errors,numPts, n,indexes); - for( int i = 0; i < numPts; i++ ) { + int[] indexes = new int[n]; + QuickSelect.selectIndex(helper.errors.data, numPts, n, indexes); + for (int i = 0; i < numPts; i++) { int origIndex = indexes[i]; - inlierSet.add( dataSet.get(origIndex) ); + inlierSet.add(dataSet.get(origIndex)); matchToInput[i] = origIndex; } } else { @@ -206,17 +219,72 @@ } } + /** + * If the maximum number of iterations has changed then re-generate the RNG for each trial + */ + protected void checkTrialGenerators() { + if (trialRNG.size == totalCycles) { + return; + } + Random rand = new Random(randSeed); + trialRNG.resize(totalCycles); + for (int i = 0; i < totalCycles; i++) { + trialRNG.set(i, new Random(rand.nextLong())); + } + } + + protected class TrialHelper { + // generates an initial model given a set of points + ModelGenerator modelGenerator = Objects.requireNonNull(factoryGenerator).newInstance(); + + // computes the distance a point is from the model + DistanceFromModel modelDistance = Objects.requireNonNull(factoryDistance).newInstance(); + + // Initial sample to generate the model from + final List initialSample = new ArrayList<>(); + + // the best model found so far + Model bestParam = ModelManager.createModelInstance(); + // the current model being considered + Model candidate = ModelManager.createModelInstance(); + + // Which indexes were selected + protected final DogArray_I32 selectedIdx = new DogArray_I32(); + + // stores all the errors for quicker sorting + protected final DogArray_F64 errors = new DogArray_F64(); + + public void initialize( int datasetSize ) { + selectedIdx.reset(); + errors.resize(datasetSize); + if (matchToInput.length != datasetSize) { + matchToInput = new int[datasetSize]; + } + + if (initializeModels != null) + initializeModels.initialize(modelGenerator, modelDistance); + } + + public void swapModels() { + Model t = bestParam; + bestParam = candidate; + candidate = t; + } + } + + @Override public double getErrorFraction() { return errorFraction; } - public void setErrorFraction(double errorFraction) { + @Override + public void setErrorFraction( double errorFraction ) { this.errorFraction = errorFraction; } @Override public Model getModelParameters() { - return bestParam; + return Objects.requireNonNull(helper).bestParam; } /** @@ -231,13 +299,12 @@ } @Override - public int getInputIndex(int matchIndex) { + public int getInputIndex( int matchIndex ) { return matchToInput[matchIndex]; } /** * Value of the best median error. - * @return */ @Override public double getFitQuality() { @@ -251,16 +318,16 @@ @Override public void reset() { - this.rand = new Random(randSeed); + trialRNG.resize(0); } @Override public Class getPointType() { - return errorMetric.getPointType(); + return pointType; } @Override public Class getModelType() { - return errorMetric.getModelType(); + return modelType; } } diff -Nru ddogleg-0.18+ds/src/org/ddogleg/fitting/modelset/lmeds/LeastMedianOfSquares_MT.java ddogleg-0.22+ds/src/org/ddogleg/fitting/modelset/lmeds/LeastMedianOfSquares_MT.java --- ddogleg-0.18+ds/src/org/ddogleg/fitting/modelset/lmeds/LeastMedianOfSquares_MT.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/fitting/modelset/lmeds/LeastMedianOfSquares_MT.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2012-2022, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.fitting.modelset.lmeds; + +import org.ddogleg.DDoglegConcurrency; +import org.ddogleg.fitting.modelset.DistanceFromModel; +import org.ddogleg.fitting.modelset.ModelGenerator; +import org.ddogleg.fitting.modelset.ModelManager; +import org.ddogleg.sorting.QuickSelect; +import org.ddogleg.struct.Factory; +import org.jetbrains.annotations.Nullable; +import pabeles.concurrency.GrowArray; + +import java.util.List; +import java.util.Objects; + +import static org.ddogleg.fitting.modelset.ransac.Ransac.addSelect; +import static org.ddogleg.fitting.modelset.ransac.Ransac.randomDraw; + +/** + * Concurrent version of {@link LeastMedianOfSquares} + * + * @author Peter Abeles + */ +public class LeastMedianOfSquares_MT extends LeastMedianOfSquares { + // Storage for each thread's state + final GrowArray helpers; + + //------------------- BEGIN LOCK OWNED + final Object lock = new Object(); + // The trial which has the best. See code comments below + volatile int bestMedianTrial; + // The helper which contains the best solution + volatile @Nullable TrialHelper bestHelper; + //------------------- END LOCK OWNED + + /** + * @see LeastMedianOfSquares + */ + public LeastMedianOfSquares_MT( long randSeed, int totalCycles, double maxMedianError, + double inlierFraction, + ModelManager modelManager, + Class pointType ) { + super(randSeed, totalCycles, maxMedianError, inlierFraction, modelManager, pointType); + + // Initialize size is zero. so factories not being defined won't cause problems + helpers = new GrowArray<>(TrialHelper::new, ( a ) -> a.initialize(matchToInput.length), (Class)TrialHelper.class); + } + + public LeastMedianOfSquares_MT( long randSeed, + int totalCycles, + ModelManager modelManager, + Class pointType ) { + this(randSeed, totalCycles, Double.MAX_VALUE, 0, modelManager, pointType); + } + + @Override + public boolean process( List dataSet ) { + if (dataSet.size() < sampleSize) + return false; + + checkTrialGenerators(); + + final int N = dataSet.size(); + + // make sure the array is large enough. If not declare a new one that is + if (matchToInput.length < N) { + matchToInput = new int[N]; + } + + bestMedian = Double.MAX_VALUE; + + helpers.reset(); + DDoglegConcurrency.loopFor(0, totalCycles, 1, helpers, ( helper, trial ) -> { + // See RANSAC for a detailed description for why this is done. It's related to concurrency + randomDraw(helper.selectedIdx, N, sampleSize, trialRNG.get(trial)); + addSelect(helper.selectedIdx, sampleSize, dataSet, helper.initialSample); + + if (!helper.modelGenerator.generate(helper.initialSample, helper.candidate)) + return; + + helper.modelDistance.setModel(helper.candidate); + helper.modelDistance.distances(dataSet, helper.errors.data); + + double median = QuickSelect.select(helper.errors.data, (int)(N*errorFraction + 0.5), N); + + // see if it could be better and avoid the synchronize + if (median > bestMedian) { + return; + } + + synchronized (lock) { + // Need to do it again since it wasn't locked + if (median > bestMedian) + return; + + // There is a tie. Current results are only better if they come from an earlier trial + // This is done to ensure identical results with single thread version. + if (median == bestMedian && bestMedianTrial < trial) + return; + + helper.swapModels(); + bestMedian = median; + bestMedianTrial = trial; + bestHelper = helper; + } + }); + + super.helper = Objects.requireNonNull(bestHelper); + + // if configured to do so compute the inlier set + computeInlierSet(dataSet, N, Objects.requireNonNull(super.helper)); + + return bestMedian <= maxMedianError; + } + + @Override + public void setModel( Factory> factoryGenerator, + Factory> factoryDistance ) { + this.factoryGenerator = factoryGenerator; + this.factoryDistance = factoryDistance; + + // discard previous helpers since they are no longer valid + helpers.releaseInternalArray(); + + sampleSize = factoryGenerator.newInstance().getMinimumPoints(); + } +} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/fitting/modelset/ModelCodec.java ddogleg-0.22+ds/src/org/ddogleg/fitting/modelset/ModelCodec.java --- ddogleg-0.18+ds/src/org/ddogleg/fitting/modelset/ModelCodec.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/fitting/modelset/ModelCodec.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -32,7 +32,7 @@ * @param input input model parameters. * @param outputModel Output. The decoded model.. */ - void decode( double input[] , T outputModel ); + void decode( double[] input, T outputModel ); /** * Converts the provided model into the array format. @@ -40,7 +40,7 @@ * @param inputModel Input model. * @param output Output parameterized model */ - void encode( T inputModel , double output[] ); + void encode( T inputModel , double[] output ); /** * Number of elements in array encoded parameters. diff -Nru ddogleg-0.18+ds/src/org/ddogleg/fitting/modelset/ModelGenerator.java ddogleg-0.22+ds/src/org/ddogleg/fitting/modelset/ModelGenerator.java --- ddogleg-0.18+ds/src/org/ddogleg/fitting/modelset/ModelGenerator.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/fitting/modelset/ModelGenerator.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2013, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -27,8 +27,7 @@ * * @author Peter Abeles */ -public interface ModelGenerator { - +public interface ModelGenerator { /** * Creates a list of hypotheses from the set of sample points. * @@ -36,12 +35,12 @@ * @param output Storage for generated model. * @return true if a model was generated, otherwise false is none were */ - public boolean generate( List dataSet , Model output ); + boolean generate( List dataSet, Model output ); /** * The minimum number of points required to fit a data set * * @return Number of points. */ - public int getMinimumPoints(); + int getMinimumPoints(); } diff -Nru ddogleg-0.18+ds/src/org/ddogleg/fitting/modelset/ModelMatcher.java ddogleg-0.22+ds/src/org/ddogleg/fitting/modelset/ModelMatcher.java --- ddogleg-0.18+ds/src/org/ddogleg/fitting/modelset/ModelMatcher.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/fitting/modelset/ModelMatcher.java 2022-09-01 02:18:09.000000000 +0000 @@ -34,7 +34,6 @@ * @author Peter Abeles */ public interface ModelMatcher { - /** * Finds a set of points from the provided list that are a good fit for the internal model and * computes the fit parameters for the model. diff -Nru ddogleg-0.18+ds/src/org/ddogleg/fitting/modelset/ModelMatcherPost.java ddogleg-0.22+ds/src/org/ddogleg/fitting/modelset/ModelMatcherPost.java --- ddogleg-0.18+ds/src/org/ddogleg/fitting/modelset/ModelMatcherPost.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/fitting/modelset/ModelMatcherPost.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.fitting.modelset; + +import org.ddogleg.struct.Factory; + +/** + * Extension of {@link ModelMatcher} where the models are specified after construction. + * + * @author Peter Abeles + */ +public interface ModelMatcherPost extends ModelMatcher { + /** + * Specifies the internal model. Factories are provided since each thread might need its own unique instance of + * the generator and distance function if they are not thread safe. + * + * @param factoryGenerator {@link ModelGenerator} + * @param factoryDistance {@link DistanceFromModel} + */ + void setModel( Factory> factoryGenerator, + Factory> factoryDistance ); +} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/fitting/modelset/ransac/Ransac.java ddogleg-0.22+ds/src/org/ddogleg/fitting/modelset/ransac/Ransac.java --- ddogleg-0.18+ds/src/org/ddogleg/fitting/modelset/ransac/Ransac.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/fitting/modelset/ransac/Ransac.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2022, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -18,16 +18,18 @@ package org.ddogleg.fitting.modelset.ransac; -import org.ddogleg.fitting.modelset.DistanceFromModel; -import org.ddogleg.fitting.modelset.ModelGenerator; -import org.ddogleg.fitting.modelset.ModelManager; -import org.ddogleg.fitting.modelset.ModelMatcher; +import lombok.Setter; +import org.ddogleg.fitting.modelset.*; +import org.ddogleg.struct.DogArray_I32; +import org.ddogleg.struct.Factory; +import org.ddogleg.struct.FastArray; +import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.Random; - /** *

* RANSAC is an abbreviation for "RANdom SAmple Consensus" and is an iterative algorithm. The model with the @@ -44,122 +46,121 @@ * * @author Peter Abeles */ -public class Ransac implements ModelMatcher { +public class Ransac implements ModelMatcherPost, InlierThreshold { // how many points are drawn to generate the model protected int sampleSize; // how close a point needs to be considered part of the model protected double thresholdFit; - // generates an initial model given a set of points - protected ModelGenerator modelGenerator; - // computes the distance a point is from the model - protected DistanceFromModel modelDistance; + @Nullable Factory> factoryGenerator; + @Nullable Factory> factoryDistance; + + // Used to create new models + protected ModelManager masterModelManager; // used to randomly select points/samples - protected Random rand; protected long randSeed; - // list of points which are a candidate for the best fit set - protected List candidatePoints = new ArrayList(); - - // list of samples from the best fit model - protected List bestFitPoints = new ArrayList(); - - // the best model found so far - protected Model bestFitParam; - // the current model being considered - protected Model candidateParam; + // Each trial has its own seed to enable concurrent implementations that will produce identical results + protected final FastArray trialRNG = new FastArray<>(Random.class); // the maximum number of iterations it will perform protected int maxIterations; - // copy of the input data set so that it can be modified - protected List dataSet = new ArrayList<>(); + // RANSAC's internal state while trying to find the best solution + protected @Nullable TrialHelper helper; - // the set of points which were initially sampled - protected List initialSample = new ArrayList(); + /** Optional function for initializing generator and distance functions */ + protected @Setter @Nullable InitializeModels initializeModels; - // list of indexes converting it from match set to input list - protected int []matchToInput = new int[1]; - protected int []bestMatchToInput = new int[1]; + Class modelType; + Class pointType; /** * Creates a new instance of the ransac algorithm. The number of points sampled will default to the * minimum number. To override this default invoke {@link #setSampleSize(int)}. * * @param randSeed The random seed used by the random number generator. - * @param modelGenerator Creates new model(s) given a small number of points. - * @param modelDistance Computes the difference between a point an a model. * @param maxIterations The maximum number of iterations the RANSAC algorithm will perform. * @param thresholdFit How close of a fit a points needs to be to the model to be considered a fit. */ - public Ransac(long randSeed, - ModelManager modelManager, - ModelGenerator modelGenerator, - DistanceFromModel modelDistance, - int maxIterations, - double thresholdFit) { - this.modelGenerator = modelGenerator; - this.modelDistance = modelDistance; - + public Ransac( long randSeed, + int maxIterations, + double thresholdFit, + ModelManager modelManager, + Class pointType ) { + if (maxIterations <= 0) + throw new IllegalArgumentException("Number of iterations must be positive"); + this.masterModelManager = modelManager; this.randSeed = randSeed; - this.rand = new Random(randSeed); this.maxIterations = maxIterations; - - this.bestFitParam = modelManager.createModelInstance(); - this.candidateParam = modelManager.createModelInstance(); - - this.sampleSize = modelGenerator.getMinimumPoints(); + this.pointType = pointType; this.thresholdFit = thresholdFit; + modelType = (Class)modelManager.createModelInstance().getClass(); } @Override - public boolean process(List _dataSet ) { - + public boolean process( List dataSet ) { // see if it has the minimum number of points - if (_dataSet.size() < modelGenerator.getMinimumPoints() ) + if (dataSet.size() < sampleSize) return false; - // the data set will be modified so a copy is needed. Otherwise indexes of match set will not - // be correct - dataSet.clear(); - dataSet.addAll(_dataSet); - - // configure internal data structures - initialize(dataSet); + // make sure there is a RNG for each trial + checkTrialGenerators(); // iterate until it has exhausted all iterations or stop if the entire data set // is in the inlier set - for (int i = 0; i < maxIterations && bestFitPoints.size() != dataSet.size(); i++) { - // sample the a small set of points - randomDraw(dataSet, sampleSize, initialSample, rand); - - // get the candidate(s) for this sample set - if( modelGenerator.generate(initialSample, candidateParam ) ) { + TrialHelper helper = Objects.requireNonNull(this.helper, "Need to call setModel()"); + helper.reset(); + for (int trial = 0; trial < maxIterations && helper.bestFitPoints.size() != dataSet.size(); trial++) { + // sample the a small set of points, then make sure the index ordering is back to the original + // This more convoluted way of sampling the array is needed to ensure single and threaded code + // produces the exact same results. To always produce the same results the order of the sampled + // array has to be the same at the start of each trial. + // + // The original code, where it modified a copy of dataSet would be slightly faster in the single + // thread case + randomDraw(helper.selectedIdx, dataSet.size(), sampleSize, trialRNG.get(trial)); + addSelect(helper.selectedIdx, sampleSize, dataSet, helper.initialSample); - // see if it can find a model better than the current best one - selectMatchSet(_dataSet, thresholdFit, candidateParam); + // get the candidate(s) for this sample set + if (!helper.modelGenerator.generate(helper.initialSample, helper.candidateParam)) + continue; - // save this results - if (bestFitPoints.size() < candidatePoints.size()) { - swapCandidateWithBest(); - } + // see if it can find a model better than the current best one + if (!helper.selectMatchSet(dataSet, helper.bestFitPoints.size(), thresholdFit, helper.candidateParam)) + continue; + + // save this results + if (helper.bestFitPoints.size() < helper.candidatePoints.size()) { + helper.swapCandidateWithBest(); } } - return bestFitPoints.size() > 0; + return helper.bestFitPoints.size() > 0; + } + + @Override + public void setModel( Factory> factoryGenerator, + Factory> factoryDistance ) { + this.factoryGenerator = factoryGenerator; + this.factoryDistance = factoryDistance; + this.helper = new TrialHelper(); + sampleSize = helper.modelGenerator.getMinimumPoints(); } /** - * Initialize internal data structures + * If the maximum number of iterations has changed then re-generate the RNG for each trial */ - public void initialize( List dataSet ) { - bestFitPoints.clear(); - - if( dataSet.size() > matchToInput.length ) { - matchToInput = new int[ dataSet.size() ]; - bestMatchToInput = new int[ dataSet.size() ]; + protected void checkTrialGenerators() { + if (trialRNG.size == maxIterations) { + return; + } + Random rand = new Random(randSeed); + trialRNG.resize(maxIterations); + for (int i = 0; i < maxIterations; i++) { + trialRNG.set(i, new Random(rand.nextLong())); } } @@ -169,89 +170,180 @@ * * @param dataSet List that points are to be selected from. Modified. */ - public static void randomDraw(List dataSet, int numSample, - List initialSample, Random rand) { + public static void randomDraw( List dataSet, int numSample, + List initialSample, Random rand ) { initialSample.clear(); for (int i = 0; i < numSample; i++) { // index of last element that has not been selected - int indexLast = dataSet.size()-i-1; + int indexLast = dataSet.size() - i - 1; // randomly select an item from the list which has not been selected - int indexSelected = rand.nextInt(indexLast+1); + int indexSelected = rand.nextInt(indexLast + 1); T a = dataSet.get(indexSelected); initialSample.add(a); // Swap the selected item with the last unselected item in the list. This way the selected // item can't be selected again and the last item can now be selected - dataSet.set(indexSelected,dataSet.set(indexLast,a)); + dataSet.set(indexSelected, dataSet.set(indexLast, a)); } } /** - * Looks for points in the data set which closely match the current best - * fit model in the optimizer. - * - * @param dataSet The points being considered + * Randomly selects a set of points in indexes. The selects points are put at the end of the array */ - @SuppressWarnings({"ForLoopReplaceableByForEach"}) - protected void selectMatchSet(List dataSet, double threshold, Model param) { - candidatePoints.clear(); - modelDistance.setModel(param); - - for (int i = 0; i < dataSet.size(); i++) { - Point point = dataSet.get(i); - - double distance = modelDistance.computeDistance(point); - if (distance < threshold) { - matchToInput[candidatePoints.size()] = i; - candidatePoints.add(point); + public static void randomDraw( DogArray_I32 indexes, int size, int numSample, Random rand ) { + // If this is the first time, fill it numbers counting up to size + if (indexes.size != size) { + indexes.resize(size); + for (int i = 0; i < size; i++) { + indexes.data[i] = i; } } + + for (int i = 0; i < numSample; i++) { + // index of last element that has not been selected + int last = size - i - 1; + // randomly select an item from the list which has not been selected + int selected = rand.nextInt(last + 1); + + // put the selected value at the end + int tmp = indexes.get(last); + indexes.set(last, indexes.get(selected)); + indexes.set(selected, tmp); + } } /** - * Turns the current candidates into the best ones. + * Adds the selected elements to the initialSample list and undoes the shuffling. There is probably a slightly + * more elegant way to do this... */ - protected void swapCandidateWithBest() { - List tempPts = candidatePoints; - candidatePoints = bestFitPoints; - bestFitPoints = tempPts; - - int tempIndex[] = matchToInput; - matchToInput = bestMatchToInput; - bestMatchToInput = tempIndex; - - Model m = candidateParam; - candidateParam = bestFitParam; - bestFitParam = m; + public static void addSelect( DogArray_I32 indexes, int numSample, List dataSet, List initialSample ) { + initialSample.clear(); + int start = indexes.size - numSample; + for (int i = start; i < indexes.size; i++) { + int selectedIdx = indexes.get(i); + initialSample.add(dataSet.get(selectedIdx)); + if (selectedIdx < start) + indexes.set(selectedIdx, selectedIdx); + } + for (int i = start; i < indexes.size; i++) { + indexes.set(i, i); + } + } + + protected class TrialHelper { + // generates an initial model given a set of points + ModelGenerator modelGenerator = Objects.requireNonNull(factoryGenerator).newInstance(); + + // computes the distance a point is from the model + DistanceFromModel modelDistance = Objects.requireNonNull(factoryDistance).newInstance(); + + List initialSample = new ArrayList<>(); + + // list of points which are a candidate for the best fit set + List candidatePoints = new ArrayList<>(); + + // list of samples from the best fit model + List bestFitPoints = new ArrayList<>(); + + // the best model found so far + Model bestFitParam = masterModelManager.createModelInstance(); + // the current model being considered + Model candidateParam = masterModelManager.createModelInstance(); + + // list of indexes converting it from match set to input list + protected int[] matchToInput = new int[1]; + protected int[] bestMatchToInput = new int[1]; + + // Which indexes were selected + DogArray_I32 selectedIdx = new DogArray_I32(); + + /** + * Looks for points in the data set which closely match the current best + * fit model in the optimizer. + * + * @param dataSet The points being considered + */ + protected boolean selectMatchSet( List dataSet, int bestModelSize, double threshold, Model param ) { + if (dataSet.size() > matchToInput.length) { + matchToInput = new int[dataSet.size()]; + bestMatchToInput = new int[dataSet.size()]; + } + + candidatePoints.clear(); + modelDistance.setModel(param); + + // If it fails more than this it can't possibly beat the best model and should stop + int maxFailures = dataSet.size() - bestModelSize; + + for (int i = 0; i < dataSet.size() && maxFailures >= 0; i++) { + Point point = dataSet.get(i); + + double distance = modelDistance.distance(point); + if (distance < threshold) { + matchToInput[candidatePoints.size()] = i; + candidatePoints.add(point); + } else { + maxFailures--; + } + } + + return maxFailures >= 0; + } + + /** + * Turns the current candidates into the best ones. + */ + protected void swapCandidateWithBest() { + List tempPts = candidatePoints; + candidatePoints = bestFitPoints; + bestFitPoints = tempPts; + + int[] tempIndex = matchToInput; + matchToInput = bestMatchToInput; + bestMatchToInput = tempIndex; + + Model m = candidateParam; + candidateParam = bestFitParam; + bestFitParam = m; + } + + public void reset() { + candidatePoints.clear(); + bestFitPoints.clear(); + selectedIdx.reset(); + + if (initializeModels != null) + initializeModels.initialize(modelGenerator, modelDistance); + } } @Override public List getMatchSet() { - return bestFitPoints; + return Objects.requireNonNull(helper).bestFitPoints; } @Override - public int getInputIndex(int matchIndex) { - return bestMatchToInput[matchIndex]; + public int getInputIndex( int matchIndex ) { + return Objects.requireNonNull(helper).bestMatchToInput[matchIndex]; } @Override public Model getModelParameters() { - return bestFitParam; + return Objects.requireNonNull(helper).bestFitParam; } @Override public double getFitQuality() { - return bestFitPoints.size(); + return Objects.requireNonNull(helper).bestFitPoints.size(); } public int getMaxIterations() { return maxIterations; } - public void setMaxIterations(int maxIterations) { + public void setMaxIterations( int maxIterations ) { this.maxIterations = maxIterations; } @@ -262,7 +354,7 @@ @Override public void reset() { - this.rand = new Random(randSeed); + trialRNG.resize(0); } /** @@ -271,25 +363,32 @@ * * @param sampleSize Number of sample points. */ - public void setSampleSize(int sampleSize) { + public void setSampleSize( int sampleSize ) { this.sampleSize = sampleSize; } + @Override public double getThresholdFit() { return thresholdFit; } - public void setThresholdFit(double thresholdFit) { + @Override + public void setThresholdFit( double thresholdFit ) { this.thresholdFit = thresholdFit; } @Override public Class getPointType() { - return modelDistance.getPointType(); + return pointType; } @Override public Class getModelType() { - return modelDistance.getModelType(); + return modelType; + } + + @FunctionalInterface + public interface InitializeModels { + void initialize( ModelGenerator generator, DistanceFromModel distance ); } } \ No newline at end of file diff -Nru ddogleg-0.18+ds/src/org/ddogleg/fitting/modelset/ransac/Ransac_MT.java ddogleg-0.22+ds/src/org/ddogleg/fitting/modelset/ransac/Ransac_MT.java --- ddogleg-0.18+ds/src/org/ddogleg/fitting/modelset/ransac/Ransac_MT.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/fitting/modelset/ransac/Ransac_MT.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2012-2022, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.fitting.modelset.ransac; + +import org.ddogleg.DDoglegConcurrency; +import org.ddogleg.fitting.modelset.DistanceFromModel; +import org.ddogleg.fitting.modelset.ModelGenerator; +import org.ddogleg.fitting.modelset.ModelManager; +import org.ddogleg.struct.Factory; +import org.jetbrains.annotations.Nullable; +import pabeles.concurrency.GrowArray; + +import java.util.List; +import java.util.Objects; + +/** + * Concurrent implementation of {@link Ransac}. It will produce identical results when given the same seed. + * + * @author Peter Abeles + */ +public class Ransac_MT extends Ransac { + + // Storage for each thread's state + final GrowArray helpers; + + //------------------- BEGIN LOCK OWNED + final Object lock = new Object(); + // Size of the best model across all threads + // NOTE: Reading from an int is safe across threads on 32-bit and 64-bit machines. + volatile int bestInlierSize; + // The trial which has the best. See code comments below + volatile int bestInlierTrial; + // The helper which contains the best solution + volatile @Nullable TrialHelper bestHelper; + //------------------- END LOCK OWNED + + public Ransac_MT( long randSeed, int maxIterations, double thresholdFit, + ModelManager modelManager, + Class pointType ) { + super(randSeed, maxIterations, thresholdFit, modelManager, pointType); + + // This should be safe even though the factories aren't defined size the initial size will be zero + helpers = new GrowArray<>(TrialHelper::new, TrialHelper::reset, TrialHelper.class); + } + + @Override + public boolean process( List dataSet ) { + // see if it has the minimum number of points + if (dataSet.size() < sampleSize) + return false; + + Objects.requireNonNull(factoryDistance, "Must specify the model"); + + // make sure there is a RNG for each trial + checkTrialGenerators(); + + bestInlierSize = -1; + bestInlierTrial = -1; + bestHelper = null; + + // iterate until it has exhausted all iterations or stop if the entire data set + // is in the inlier set + DDoglegConcurrency.loopFor(0, maxIterations, 1, helpers, ( helper, trial ) -> { + // See comment in single threaded code. This convoluted way of sampling is needed to ensure + // single and multi threaded code produce the same results. + randomDraw(helper.selectedIdx, dataSet.size(), sampleSize, trialRNG.get(trial)); + addSelect(helper.selectedIdx, sampleSize, dataSet, helper.initialSample); + + // get the candidate(s) for this sample set + if (!helper.modelGenerator.generate(helper.initialSample, helper.candidateParam)) + return; + + // NOTE: A design requirement is that produce identical results to the single thread version. + // That means that if there are multiple models with the same number of inliers then the + // model which was generated in a lower trial number is selected + + // the global best inlier size is set to -1 so that if a model just as good is found now + // we can compare trial numbers to see who the winner is + int threshold = Math.max(bestInlierSize - 1, helper.bestFitPoints.size()); + + // see if it can find a model better than the current best one + if (!helper.selectMatchSet(dataSet, threshold, thresholdFit, helper.candidateParam)) + return; + + // See if the global best is the winner + if (bestInlierSize > helper.candidatePoints.size()) + return; + + synchronized (lock) { + // Need to do it again since previously it wasn't locked and might have changed + if (bestInlierSize > helper.candidatePoints.size()) + return; + + // There is a tie. Current results are only better if they come from an earlier trial + if (bestInlierSize == helper.candidatePoints.size() && bestInlierTrial < trial) + return; + + // Record the new champion + bestInlierSize = helper.candidatePoints.size(); + bestInlierTrial = trial; + bestHelper = helper; + } + + // Save the best results + helper.swapCandidateWithBest(); + }); + + // Set the winner to the helper with the best results + TrialHelper result = super.helper = Objects.requireNonNull(bestHelper); + + return result.bestFitPoints.size() > 0; + } + + @Override + public void setModel( Factory> factoryGenerator, + Factory> factoryDistance ) { + this.factoryGenerator = factoryGenerator; + this.factoryDistance = factoryDistance; + + // discard previous helpers since they are no longer valid + helpers.releaseInternalArray(); + + sampleSize = factoryGenerator.newInstance().getMinimumPoints(); + } +} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/fitting/modelset/ransac/RansacMulti.java ddogleg-0.22+ds/src/org/ddogleg/fitting/modelset/ransac/RansacMulti.java --- ddogleg-0.18+ds/src/org/ddogleg/fitting/modelset/ransac/RansacMulti.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/fitting/modelset/ransac/RansacMulti.java 2022-09-01 02:18:09.000000000 +0000 @@ -50,6 +50,7 @@ * * @author Peter Abeles */ +@SuppressWarnings("NullAway.Init") public class RansacMulti implements ModelMatcherMulti { // how many points are drawn to generate the model protected int sampleSize; @@ -226,7 +227,7 @@ for (int i = 0; i < dataSet.size(); i++) { Point point = dataSet.get(i); - double distance = modelDistance.computeDistance(point); + double distance = modelDistance.distance(point); if (distance < threshold) { matchToInput[candidatePoints.size()] = i; candidatePoints.add(point); @@ -249,6 +250,7 @@ bestFitParam = param; } + @Override public List getMatchSet() { return bestFitPoints; } @@ -330,6 +332,7 @@ /** * Describes a model and RANSAC fit parameters for specific type of object. */ + @SuppressWarnings("NullAway.Init") public static class ObjectType { /** how close a point needs to be considered part of the model */ diff -Nru ddogleg-0.18+ds/src/org/ddogleg/graph/Edge.java ddogleg-0.22+ds/src/org/ddogleg/graph/Edge.java --- ddogleg-0.18+ds/src/org/ddogleg/graph/Edge.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/graph/Edge.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2013, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -18,11 +18,13 @@ package org.ddogleg.graph; +import org.jetbrains.annotations.Nullable; + /** * @author Peter Abeles */ public class Edge { - E data; - Node dest; + @Nullable E data; + @Nullable Node dest; } diff -Nru ddogleg-0.18+ds/src/org/ddogleg/graph/GraphDataManager.java ddogleg-0.22+ds/src/org/ddogleg/graph/GraphDataManager.java --- ddogleg-0.18+ds/src/org/ddogleg/graph/GraphDataManager.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/graph/GraphDataManager.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2013, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -18,9 +18,9 @@ package org.ddogleg.graph; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.List; -import java.util.Stack; /** * Handles creating and recycling data in a graph. @@ -30,14 +30,14 @@ public class GraphDataManager { // edges which are being actively used - protected List> usedEdges = new ArrayList>(); + protected final List> usedEdges = new ArrayList<>(); // edge data which is not being used - protected Stack> unusedEdges = new Stack>(); + protected final ArrayDeque> unusedEdges = new ArrayDeque<>(); // nodes which are being actively used - protected List> usedNodes = new ArrayList>(); + protected final List> usedNodes = new ArrayList<>(); // node data which is not being used - protected Stack> unusedNodes = new Stack>(); + protected final ArrayDeque> unusedNodes = new ArrayDeque<>(); /** * Takes all the used nodes and makes them unused. @@ -77,7 +77,7 @@ public Edge createEdge() { Edge e; if( unusedEdges.isEmpty() ) { - e = new Edge(); + e = new Edge<>(); } else { e = unusedEdges.pop(); } @@ -95,7 +95,7 @@ public Node createNode() { Node n; if( unusedNodes.isEmpty() ) { - n = new Node(); + n = new Node<>(); } else { n = unusedNodes.pop(); } diff -Nru ddogleg-0.18+ds/src/org/ddogleg/graph/Node.java ddogleg-0.22+ds/src/org/ddogleg/graph/Node.java --- ddogleg-0.18+ds/src/org/ddogleg/graph/Node.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/graph/Node.java 2022-09-01 02:18:09.000000000 +0000 @@ -18,13 +18,14 @@ package org.ddogleg.graph; -import org.ddogleg.struct.FastQueue; +import org.ddogleg.struct.DogArray; +import org.jetbrains.annotations.Nullable; /** * @author Peter Abeles */ public class Node { - N data; - FastQueue> edges = new FastQueue<>(Edge::new); + @Nullable N data; + final DogArray> edges = new DogArray<>(Edge::new); } diff -Nru ddogleg-0.18+ds/src/org/ddogleg/nn/alg/AxisSplitRuleRandomK.java ddogleg-0.22+ds/src/org/ddogleg/nn/alg/AxisSplitRuleRandomK.java --- ddogleg-0.18+ds/src/org/ddogleg/nn/alg/AxisSplitRuleRandomK.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/nn/alg/AxisSplitRuleRandomK.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2014, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -19,7 +19,9 @@ package org.ddogleg.nn.alg; import org.ddogleg.sorting.QuickSelect; +import org.jetbrains.annotations.Nullable; +import java.util.Objects; import java.util.Random; /** @@ -41,13 +43,8 @@ int actualConsiderSplit; // stores the original indexes of the 'numConsider' largest elements - int indexes[]; + @Nullable int[] indexes; - /** - * - * @param rand - * @param numConsiderSplit - */ public AxisSplitRuleRandomK(Random rand, int numConsiderSplit) { this.rand = rand; this.numConsiderSplit = numConsiderSplit; @@ -62,6 +59,7 @@ @Override public int select(double[] variance) { + Objects.requireNonNull(indexes,"Call setDimensions()"); // invert so that the largest variances will be at the bottom for( int i = 0; i < N; i++ ) diff -Nru ddogleg-0.18+ds/src/org/ddogleg/nn/alg/AxisSplitter.java ddogleg-0.22+ds/src/org/ddogleg/nn/alg/AxisSplitter.java --- ddogleg-0.18+ds/src/org/ddogleg/nn/alg/AxisSplitter.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/nn/alg/AxisSplitter.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -18,9 +18,9 @@ package org.ddogleg.nn.alg; -import org.ddogleg.struct.GrowQueue_I32; +import org.ddogleg.struct.DogArray_I32; +import org.jetbrains.annotations.Nullable; -import javax.annotation.Nullable; import java.util.List; /** @@ -44,9 +44,9 @@ * @param right Output: Storage for points more than the split point. * @param righrIndexes Output: (Optional) Storage for indexes associated with right. Can be null. */ - void splitData( List

points , @Nullable GrowQueue_I32 indexes , - List

left , @Nullable GrowQueue_I32 leftIndexes , - List

right , @Nullable GrowQueue_I32 righrIndexes ); + void splitData( List

points , @Nullable DogArray_I32 indexes , + List

left , @Nullable DogArray_I32 leftIndexes , + List

right , @Nullable DogArray_I32 righrIndexes ); /** * Returns the point used to split the data diff -Nru ddogleg-0.18+ds/src/org/ddogleg/nn/alg/AxisSplitterMedian.java ddogleg-0.22+ds/src/org/ddogleg/nn/alg/AxisSplitterMedian.java --- ddogleg-0.18+ds/src/org/ddogleg/nn/alg/AxisSplitterMedian.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/nn/alg/AxisSplitterMedian.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -19,9 +19,11 @@ package org.ddogleg.nn.alg; import org.ddogleg.sorting.QuickSelect; -import org.ddogleg.struct.GrowQueue_I32; +import org.ddogleg.struct.DogArray_I32; +import org.jetbrains.annotations.Nullable; import java.util.List; +import java.util.Objects; /** * Splits the points in K-D Tree node by selecting the axis with the largest variance. The point with the median @@ -30,18 +32,19 @@ * * @author Peter Abeles */ +@SuppressWarnings("NullAway.Init") public class AxisSplitterMedian

implements AxisSplitter

{ // Number of elements/axes in each data point - private int N; + private final int N; // storage for variance calculation - private double mean[]; - private double var[]; + private final double[] mean; + private final double[] var; // storage for median calculation - private double tmp[] = new double[1]; - private int indexes[] = new int[1]; + private double[] tmp = new double[1]; + private int[] indexes = new int[1]; // using each axis's variance, selects which axis to split along // This abstraction was done so that random trees could use the same code @@ -74,9 +77,9 @@ } @Override - public void splitData(List

points, GrowQueue_I32 indexes, - List

left, GrowQueue_I32 leftIndexes, - List

right, GrowQueue_I32 rightIndexes) { + public void splitData(List

points, @Nullable DogArray_I32 indexes, + List

left, @Nullable DogArray_I32 leftIndexes, + List

right, @Nullable DogArray_I32 rightIndexes) { computeAxisVariance(points); for (int i = 0; i < N; i++) { if( Double.isNaN(var[i])) { @@ -102,6 +105,8 @@ right.add(points.get(this.indexes[i])); } } else { + Objects.requireNonNull(leftIndexes); + Objects.requireNonNull(rightIndexes); leftIndexes.reset(); rightIndexes.reset(); diff -Nru ddogleg-0.18+ds/src/org/ddogleg/nn/alg/distance/KdTreeEuclideanSq_U8.java ddogleg-0.22+ds/src/org/ddogleg/nn/alg/distance/KdTreeEuclideanSq_U8.java --- ddogleg-0.18+ds/src/org/ddogleg/nn/alg/distance/KdTreeEuclideanSq_U8.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/nn/alg/distance/KdTreeEuclideanSq_U8.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -39,7 +39,7 @@ final int N = a.length; for (int i = 0; i < N; i++) { - double d = (a[i]&0xFF)-(b[i]&0xFF); + int d = (a[i]&0xFF)-(b[i]&0xFF); sum += d*d; } diff -Nru ddogleg-0.18+ds/src/org/ddogleg/nn/alg/ExhaustiveNeighbor.java ddogleg-0.22+ds/src/org/ddogleg/nn/alg/ExhaustiveNeighbor.java --- ddogleg-0.18+ds/src/org/ddogleg/nn/alg/ExhaustiveNeighbor.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/nn/alg/ExhaustiveNeighbor.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -19,8 +19,8 @@ package org.ddogleg.nn.alg; import org.ddogleg.sorting.QuickSelect; -import org.ddogleg.struct.GrowQueue_F64; -import org.ddogleg.struct.GrowQueue_I32; +import org.ddogleg.struct.DogArray_F64; +import org.ddogleg.struct.DogArray_I32; import java.util.List; @@ -29,6 +29,7 @@ * * @author Peter Abeles */ +@SuppressWarnings("NullAway.Init") public class ExhaustiveNeighbor

{ // List of points @@ -37,9 +38,9 @@ // the distance to the closest node found so far double bestDistance; - GrowQueue_F64 distances = new GrowQueue_F64(); - GrowQueue_I32 indexes = new GrowQueue_I32(); - GrowQueue_I32 indexesSort = new GrowQueue_I32(); + final DogArray_F64 distances = new DogArray_F64(); + final DogArray_I32 indexes = new DogArray_I32(); + final DogArray_I32 indexesSort = new DogArray_I32(); KdTreeDistance

distance; @@ -47,9 +48,6 @@ this.distance = distance; } - public ExhaustiveNeighbor() { - } - /** * The input list which the nearest-neighbor is to be found inside of * @@ -96,8 +94,8 @@ * @param outputDistance Storage for the distance of the closest elements */ public void findClosestN( P p , double maxDistance , int numNeighbors , - GrowQueue_I32 outputIndex , - GrowQueue_F64 outputDistance ) { + DogArray_I32 outputIndex , + DogArray_F64 outputDistance ) { // Compute the distance of each point and save the ones within range distances.reset(); diff -Nru ddogleg-0.18+ds/src/org/ddogleg/nn/alg/KdTreeConstructor.java ddogleg-0.22+ds/src/org/ddogleg/nn/alg/KdTreeConstructor.java --- ddogleg-0.18+ds/src/org/ddogleg/nn/alg/KdTreeConstructor.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/nn/alg/KdTreeConstructor.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -18,7 +18,8 @@ package org.ddogleg.nn.alg; -import org.ddogleg.struct.GrowQueue_I32; +import org.ddogleg.struct.DogArray_I32; +import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; @@ -70,9 +71,9 @@ */ public KdTree construct(List

points , boolean trackIndexes ) { - GrowQueue_I32 indexes = null; + DogArray_I32 indexes = null; if( trackIndexes ) { - indexes = new GrowQueue_I32(); + indexes = new DogArray_I32(); indexes.resize(points.size()); for (int i = 0; i < indexes.size; i++) { indexes.data[i] = i; @@ -96,18 +97,18 @@ * * @return The node associated with this region */ - protected KdTree.Node computeBranch(List

points, GrowQueue_I32 indexes) + protected KdTree.Node computeBranch(List

points, @Nullable DogArray_I32 indexes) { // declare storage for the split data List

left = new ArrayList<>(points.size()/2); List

right = new ArrayList<>(points.size()/2); - GrowQueue_I32 leftIndexes,rightIndexes; + DogArray_I32 leftIndexes,rightIndexes; if( indexes == null ) { leftIndexes = null; rightIndexes = null; } else { - leftIndexes = new GrowQueue_I32(points.size()/2); - rightIndexes = new GrowQueue_I32(points.size()/2); + leftIndexes = new DogArray_I32(points.size()/2); + rightIndexes = new DogArray_I32(points.size()/2); } // perform the splitting @@ -132,7 +133,7 @@ /** * Creates a child by checking to see if it is a leaf or branch. */ - protected KdTree.Node computeChild(List

points , GrowQueue_I32 indexes ) + protected @Nullable KdTree.Node computeChild(List

points , @Nullable DogArray_I32 indexes ) { if( points.size() == 0 ) return null; @@ -146,7 +147,7 @@ /** * Convenient function for creating a leaf node */ - private KdTree.Node createLeaf(List

points , GrowQueue_I32 indexes ) { + private KdTree.Node createLeaf(List

points , @Nullable DogArray_I32 indexes ) { int index = indexes == null ? -1 : indexes.get(0); return memory.requestNode(points.get(0),index); } diff -Nru ddogleg-0.18+ds/src/org/ddogleg/nn/alg/KdTree.java ddogleg-0.22+ds/src/org/ddogleg/nn/alg/KdTree.java --- ddogleg-0.18+ds/src/org/ddogleg/nn/alg/KdTree.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/nn/alg/KdTree.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -18,6 +18,8 @@ package org.ddogleg.nn.alg; +import org.jetbrains.annotations.Nullable; + /** *

* K-D Tree is short for k-dimensional tree and is a binary tree data structure used for quickly finding the @@ -38,7 +40,7 @@ // Number of elements/dimension in each point public int N; // tree data structure - public Node root; + public @Nullable Node root; /** * Specifies the type of points it can process. @@ -49,16 +51,16 @@ this.N = N; } - public KdTree() { - } + public KdTree() {} /** * Data type for each node in the binary tree. A branch will have two non-null left and right children * and the value for split will be {@code >= 0}. If any of those conditions are not meet then it is a leaf. */ + @SuppressWarnings("NullAway.Init") public static class Node { - - /** The node's point. For branches this is used to split the data. NOTE: This is a reference to the + /** + * The node's point. For branches this is used to split the data. NOTE: This is a reference to the * original input data. **/ public Object point; @@ -67,9 +69,9 @@ /** axis used to split the data. -1 for leafs */ public int split = -1; /** Branch ≤ point[split] */ - public Node left; + public @Nullable Node left; /** Branch ≥ point[split] */ - public Node right; + public @Nullable Node right; public Node( double[] point , int index ) { this.point = point; @@ -81,8 +83,7 @@ this.index = -1; } - public Node() { - } + public Node() {} public T getPoint() { return (T)point; diff -Nru ddogleg-0.18+ds/src/org/ddogleg/nn/alg/KdTreeMemory.java ddogleg-0.22+ds/src/org/ddogleg/nn/alg/KdTreeMemory.java --- ddogleg-0.18+ds/src/org/ddogleg/nn/alg/KdTreeMemory.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/nn/alg/KdTreeMemory.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -37,7 +37,6 @@ /** * Returns a new node. All object references can be assumed to be null. - * @return */ public KdTree.Node requestNode() { if( unusedNodes.isEmpty() ) @@ -65,6 +64,7 @@ return tree; } + @SuppressWarnings("NullAway") public void recycle( KdTree.Node node ) { // null to avoid potential memory leaks node.point = null; diff -Nru ddogleg-0.18+ds/src/org/ddogleg/nn/alg/KdTreeResult.java ddogleg-0.22+ds/src/org/ddogleg/nn/alg/KdTreeResult.java --- ddogleg-0.18+ds/src/org/ddogleg/nn/alg/KdTreeResult.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/nn/alg/KdTreeResult.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2013, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -23,6 +23,7 @@ * * @author Peter Abeles */ +@SuppressWarnings("NullAway.Init") public class KdTreeResult { public KdTree.Node node; public double distance; diff -Nru ddogleg-0.18+ds/src/org/ddogleg/nn/alg/KdTreeSearch1.java ddogleg-0.22+ds/src/org/ddogleg/nn/alg/KdTreeSearch1.java --- ddogleg-0.18+ds/src/org/ddogleg/nn/alg/KdTreeSearch1.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/nn/alg/KdTreeSearch1.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -18,6 +18,8 @@ package org.ddogleg.nn.alg; +import org.jetbrains.annotations.Nullable; + /** * Interface for searching a single tree for the nearest-neighbor * @@ -46,7 +48,7 @@ * @param target Point whose nearest neighbor is being searched for * @return The closest point or null if there is none. */ - KdTree.Node findNeighbor(P target); + @Nullable KdTree.Node findNeighbor(P target); /** * Returns the distance of the closest node. diff -Nru ddogleg-0.18+ds/src/org/ddogleg/nn/alg/KdTreeSearchN.java ddogleg-0.22+ds/src/org/ddogleg/nn/alg/KdTreeSearchN.java --- ddogleg-0.18+ds/src/org/ddogleg/nn/alg/KdTreeSearchN.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/nn/alg/KdTreeSearchN.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -18,7 +18,7 @@ package org.ddogleg.nn.alg; -import org.ddogleg.struct.FastQueue; +import org.ddogleg.struct.DogArray; /** * Interface for searching a single tree for the N nearest-neighbors. @@ -48,7 +48,7 @@ * @param searchN Number of closest points it will find. Must be {@code >=} 1 * @param results Storage for the found neighbors. */ - void findNeighbor(P target, int searchN, FastQueue results); + void findNeighbor(P target, int searchN, DogArray results); /** * Creates a copy of this search with the same configuration. workspace isn't copied diff -Nru ddogleg-0.18+ds/src/org/ddogleg/nn/alg/searches/KdTreeSearch1Bbf.java ddogleg-0.22+ds/src/org/ddogleg/nn/alg/searches/KdTreeSearch1Bbf.java --- ddogleg-0.18+ds/src/org/ddogleg/nn/alg/searches/KdTreeSearch1Bbf.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/nn/alg/searches/KdTreeSearch1Bbf.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -21,6 +21,7 @@ import org.ddogleg.nn.alg.KdTree; import org.ddogleg.nn.alg.KdTreeDistance; import org.ddogleg.nn.alg.KdTreeSearch1; +import org.jetbrains.annotations.Nullable; /** *

@@ -32,7 +33,7 @@ public class KdTreeSearch1Bbf

extends KdTreeSearchBestBinFirst

implements KdTreeSearch1

{ // the best node so far - private KdTree.Node bestNode; + private @Nullable KdTree.Node bestNode; /** * Configures the search @@ -53,7 +54,7 @@ } @Override - public KdTree.Node findNeighbor(P target) { + public @Nullable KdTree.Node findNeighbor(P target) { bestNode = null; diff -Nru ddogleg-0.18+ds/src/org/ddogleg/nn/alg/searches/KdTreeSearch1Standard.java ddogleg-0.22+ds/src/org/ddogleg/nn/alg/searches/KdTreeSearch1Standard.java --- ddogleg-0.18+ds/src/org/ddogleg/nn/alg/searches/KdTreeSearch1Standard.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/nn/alg/searches/KdTreeSearch1Standard.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -21,6 +21,7 @@ import org.ddogleg.nn.alg.KdTree; import org.ddogleg.nn.alg.KdTreeDistance; import org.ddogleg.nn.alg.KdTreeSearch1; +import org.jetbrains.annotations.Nullable; /** * Standard algorithm for searching a {@link KdTree} for the nearest-neighbor of a search. This is the algorithm @@ -31,6 +32,7 @@ * * @author Peter Abeles */ +@SuppressWarnings("NullAway.Init") public class KdTreeSearch1Standard

implements KdTreeSearch1

{ // the targeted tree @@ -45,7 +47,7 @@ private double bestDistanceSq; // the node which has been found to be the closest so far - private KdTree.Node closest; + private @Nullable KdTree.Node closest; KdTreeDistance

distance; @@ -75,7 +77,7 @@ * @return Closest node or null if none is within the minimum distance. */ @Override - public KdTree.Node findNeighbor(P target) { + public @Nullable KdTree.Node findNeighbor(P target) { if( tree.root == null ) return null; @@ -101,7 +103,7 @@ /** * Recursive step for finding the closest point */ - private void stepClosest(KdTree.Node node) { + private void stepClosest(@Nullable KdTree.Node node) { if( node == null ) return; diff -Nru ddogleg-0.18+ds/src/org/ddogleg/nn/alg/searches/KdTreeSearchBestBinFirst.java ddogleg-0.22+ds/src/org/ddogleg/nn/alg/searches/KdTreeSearchBestBinFirst.java --- ddogleg-0.18+ds/src/org/ddogleg/nn/alg/searches/KdTreeSearchBestBinFirst.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/nn/alg/searches/KdTreeSearchBestBinFirst.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -47,6 +47,7 @@ * * @author Peter Abeles */ +@SuppressWarnings("NullAway.Init") public abstract class KdTreeSearchBestBinFirst

{ // the maximum number of nodes it will search @@ -59,16 +60,16 @@ private double maxDistance = Double.MAX_VALUE; // List of graph nodes that still need to be explored - private PriorityQueue queue = new PriorityQueue(); + private final PriorityQueue queue = new PriorityQueue<>(); // Forest of trees to search - private KdTree trees[]; + private KdTree[] trees; // distance of the best node squared protected double bestDistanceSq; // used for recycling data structures - private List unused = new ArrayList(); + private final List unused = new ArrayList<>(); // number of nodes which have been searched protected int numNodesSearched = 0; @@ -80,7 +81,7 @@ * * @param maxNodesSearched Maximum number of nodes it will search. Used to limit CPU time. */ - public KdTreeSearchBestBinFirst(KdTreeDistance

distance, int maxNodesSearched) { + protected KdTreeSearchBestBinFirst(KdTreeDistance

distance, int maxNodesSearched) { this.distance = distance; this.maxNodesSearched = maxNodesSearched; } @@ -94,7 +95,7 @@ this.N = tree.N; } - public void setTrees(KdTree[]trees ) { + public void setTrees(KdTree[] trees ) { if( this.trees == null || this.trees.length != trees.length ) { this.trees = trees.clone(); } else { @@ -226,12 +227,7 @@ @Override public int compareTo(Helper o) { - if( closestPossibleSq < o.closestPossibleSq) - return -1; - else if( closestPossibleSq > o.closestPossibleSq) - return 1; - else - return 0; + return Double.compare(closestPossibleSq, o.closestPossibleSq); } } } diff -Nru ddogleg-0.18+ds/src/org/ddogleg/nn/alg/searches/KdTreeSearchNBbf.java ddogleg-0.22+ds/src/org/ddogleg/nn/alg/searches/KdTreeSearchNBbf.java --- ddogleg-0.18+ds/src/org/ddogleg/nn/alg/searches/KdTreeSearchNBbf.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/nn/alg/searches/KdTreeSearchNBbf.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -22,7 +22,7 @@ import org.ddogleg.nn.alg.KdTreeDistance; import org.ddogleg.nn.alg.KdTreeResult; import org.ddogleg.nn.alg.KdTreeSearchN; -import org.ddogleg.struct.FastQueue; +import org.ddogleg.struct.DogArray; /** *

@@ -31,6 +31,7 @@ * * @author Peter Abeles */ +@SuppressWarnings("NullAway.Init") public class KdTreeSearchNBbf

extends KdTreeSearchBestBinFirst

implements KdTreeSearchN

{ @@ -38,15 +39,14 @@ private int searchN; // storage for found results nodes - private FastQueue neighbors; + private DogArray neighbors; /** * Configures the search * * @param maxNodesSearched Maximum number of nodes it will search. Used to limit CPU time. */ - public KdTreeSearchNBbf(KdTreeDistance

distance, - int maxNodesSearched) { + public KdTreeSearchNBbf(KdTreeDistance

distance, int maxNodesSearched) { super(distance,maxNodesSearched); } @@ -60,7 +60,7 @@ } @Override - public void findNeighbor(P target, int searchN, FastQueue results) { + public void findNeighbor(P target, int searchN, DogArray results) { this.searchN = searchN; this.neighbors = results; @@ -76,6 +76,7 @@ /** * Checks to see if the current node's point is the closet point found so far */ + @Override protected void checkBestDistance(KdTree.Node node, P target) { double distanceSq = distance.distance((P)node.point,target); diff -Nru ddogleg-0.18+ds/src/org/ddogleg/nn/alg/searches/KdTreeSearchNStandard.java ddogleg-0.22+ds/src/org/ddogleg/nn/alg/searches/KdTreeSearchNStandard.java --- ddogleg-0.18+ds/src/org/ddogleg/nn/alg/searches/KdTreeSearchNStandard.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/nn/alg/searches/KdTreeSearchNStandard.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -22,7 +22,8 @@ import org.ddogleg.nn.alg.KdTreeDistance; import org.ddogleg.nn.alg.KdTreeResult; import org.ddogleg.nn.alg.KdTreeSearchN; -import org.ddogleg.struct.FastQueue; +import org.ddogleg.struct.DogArray; +import org.jetbrains.annotations.Nullable; /** * Standard algorithm for searching a {@link KdTree} for the nearest-neighbor of a search. @@ -30,6 +31,7 @@ * * @author Peter Abeles */ +@SuppressWarnings("NullAway.Init") public class KdTreeSearchNStandard

implements KdTreeSearchN

{ // the targeted tree @@ -78,7 +80,7 @@ * @param results Storage for the found neighbors */ @Override - public void findNeighbor(P target, int searchN, FastQueue results) { + public void findNeighbor(P target, int searchN, DogArray results) { if( searchN <= 0 ) throw new IllegalArgumentException("I'm sorry, but I refuse to search for less than or equal to 0 neighbors."); @@ -100,7 +102,7 @@ /** * Recursive step for finding the closest point */ - private void stepClosest(KdTree.Node node , FastQueue neighbors ) { + private void stepClosest(@Nullable KdTree.Node node , DogArray neighbors ) { if( node == null ) return; @@ -140,7 +142,7 @@ /** * See if the node being considered is a new nearest-neighbor */ - private void checkBestDistance(KdTree.Node node, FastQueue neighbors) { + private void checkBestDistance(KdTree.Node node, DogArray neighbors) { double distSq = distance.distance((P)node.point,target); // <= because multiple nodes could be at the bestDistanceSq if( distSq <= mostDistantNeighborSq) { diff -Nru ddogleg-0.18+ds/src/org/ddogleg/nn/alg/VpTree.java ddogleg-0.22+ds/src/org/ddogleg/nn/alg/VpTree.java --- ddogleg-0.18+ds/src/org/ddogleg/nn/alg/VpTree.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/nn/alg/VpTree.java 2022-09-01 02:18:09.000000000 +0000 @@ -20,9 +20,10 @@ import org.ddogleg.nn.NearestNeighbor; import org.ddogleg.nn.NnData; +import org.ddogleg.struct.DogArray; +import org.ddogleg.struct.DogArray_I32; import org.ddogleg.struct.FastArray; -import org.ddogleg.struct.FastQueue; -import org.ddogleg.struct.GrowQueue_I32; +import org.jetbrains.annotations.Nullable; import java.util.List; import java.util.PriorityQueue; @@ -50,11 +51,12 @@ * * @author Karel Petránek */ +@SuppressWarnings("NullAway.Init") public class VpTree implements NearestNeighbor { - GrowQueue_I32 indexes; + DogArray_I32 indexes; private double[][] items; - private Node root; - private Random random; + private @Nullable Node root; + private final Random random; /** * Constructor @@ -70,7 +72,7 @@ public void setPoints(List points, boolean trackIndicies) { // Make a copy because we mutate the lists this.items = points.toArray(new double[0][]); - this.indexes = new GrowQueue_I32(); + this.indexes = new DogArray_I32(); indexes.resize(points.size()); for (int i = 0; i < points.size(); i++) { indexes.data[i] = i; @@ -86,7 +88,7 @@ * @param upper end of range (exclusive) * @return root of the tree or null if lower == upper */ - private Node buildFromPoints(int lower, int upper) { + private @Nullable Node buildFromPoints(int lower, int upper) { if (upper == lower) { return null; } @@ -172,13 +174,12 @@ list[b] = tmp; } - private void listSwap(GrowQueue_I32 list, int a, int b) { + private void listSwap( DogArray_I32 list, int a, int b) { int tmp = list.get(a); list.data[a] = list.data[b]; list.data[b] = tmp; } - @Override public Search createSearch() { return new InternalSearch(); @@ -195,7 +196,7 @@ @Override public void findNearest(double[] target, double maxDistance, - int numNeighbors, FastQueue> results) + int numNeighbors, DogArray> results) { results.reset(); PriorityQueue heap = search(target, maxDistance < 0 ? Double.POSITIVE_INFINITY : Math.sqrt(maxDistance), numNeighbors); @@ -319,15 +320,14 @@ } } - /** * Separates the points to "closer than the threshold" (left) and "farther than the threshold" (right). */ private static class Node { int index; double threshold; - Node left; - Node right; + @Nullable Node left; + @Nullable Node right; } /** diff -Nru ddogleg-0.18+ds/src/org/ddogleg/nn/ConfigNearestNeighborSearch.java ddogleg-0.22+ds/src/org/ddogleg/nn/ConfigNearestNeighborSearch.java --- ddogleg-0.18+ds/src/org/ddogleg/nn/ConfigNearestNeighborSearch.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/nn/ConfigNearestNeighborSearch.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.nn; + +/** + * Generic configuration for all {@link NearestNeighbor} algorithms. + * + * @author Peter Abeles + */ +public class ConfigNearestNeighborSearch { + /** Which search algorithm */ + public Type type = Type.EXHAUSTIVE; + + /** Configuration for {@link Type#RANDOM_FOREST} */ + public final RandomForest randomForest = new RandomForest(); + + /** Configuration for {@link Type#KD_TREE} */ + public final KDTree kdtree = new KDTree(); + + /** Seed used by random number generator */ + public long randomSeed = 0x42; + + public void checkValidity() { + randomForest.checkValidity(); + kdtree.checkValidity(); + } + + public void setTo( ConfigNearestNeighborSearch src ) { + this.type = src.type; + this.randomForest.setTo(src.randomForest); + this.kdtree.setTo(src.kdtree); + this.randomSeed = src.randomSeed; + } + + public static class RandomForest { + /** Maximum number of nodes it will search. Controls speed and accuracy. */ + public int maxNodesSearched = 2000; + /** Number of trees that are considered. Tune this. */ + public int numTrees = 10; + /** Number of nodes that are considered when generating a tree. Must be less than the point's dimension. */ + public int numConsiderSplit = 5; + + public void checkValidity() { + if (maxNodesSearched < 0) + throw new IllegalArgumentException("maxNodesSearched can't be negative"); + + if (numTrees <= 0) + throw new IllegalArgumentException("numTrees must be positive"); + + if (numConsiderSplit <= 0) + throw new IllegalArgumentException("numConsiderSplit must be positive"); + } + + public void setTo( RandomForest src ) { + this.maxNodesSearched = src.maxNodesSearched; + this.numTrees = src.numTrees; + this.numConsiderSplit = src.numConsiderSplit; + } + } + + public static class KDTree { + /** Maximum number of nodes it will search. Controls speed and accuracy. */ + public int maxNodesSearched = Integer.MAX_VALUE; + + public void checkValidity() { + if (maxNodesSearched < 0) + throw new IllegalArgumentException("maxNodesSearched can't be negative"); + } + + public void setTo( KDTree src ) { + this.maxNodesSearched = src.maxNodesSearched; + } + } + + public enum Type { + EXHAUSTIVE, + RANDOM_FOREST, + KD_TREE, + VP_TREE + } +} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/nn/FactoryNearestNeighbor.java ddogleg-0.22+ds/src/org/ddogleg/nn/FactoryNearestNeighbor.java --- ddogleg-0.18+ds/src/org/ddogleg/nn/FactoryNearestNeighbor.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/nn/FactoryNearestNeighbor.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -36,16 +36,41 @@ * @author Peter Abeles */ public class FactoryNearestNeighbor { + /** + * Factory for generic {@link NearestNeighbor}. + */ + public static

NearestNeighbor

generic( ConfigNearestNeighborSearch config, KdTreeDistance

distance ) { + switch (config.type) { + case EXHAUSTIVE: { + return exhaustive(distance); + } + + case KD_TREE: { + return kdtree(distance, config.kdtree.maxNodesSearched); + } + + case RANDOM_FOREST: { + return kdRandomForest(distance, config.randomForest.maxNodesSearched, config.randomForest.numTrees, + config.randomForest.numConsiderSplit, config.randomSeed); + } + + case VP_TREE: { + throw new RuntimeException("VP-Tree needs to be updated to support the generic distance"); +// return vptree(config.randomSeed); + } + default: + throw new RuntimeException("Unknown Type: " + config.type); + } + } /** * Performs an optimal {@link NearestNeighbor} search using K-D tree. Distance measure is Euclidean squared. * - * @see KdTreeNearestNeighbor - * @see AxisSplitterMedian - * * @param

Point type. * @param distance Specifies how distance is computed between two points. * @return {@link NearestNeighbor} implementation + * @see KdTreeNearestNeighbor + * @see AxisSplitterMedian */ public static

NearestNeighbor

kdtree( KdTreeDistance

distance ) { return new KdTreeNearestNeighbor<>(distance); @@ -55,56 +80,52 @@ * Performs an approximate {@link NearestNeighbor} search using K-D tree. Node are searched in Best-Bin-First * order. Distance measure is Euclidean squared. * - * @see KdTreeNearestNeighbor - * @see KdTreeSearch1Bbf - * @see AxisSplitterMedian - * * @param maxNodesSearched Maximum number of nodes it will search. Controls speed and accuracy. * @param

Point type. * @param distance Specifies how distance is computed between two points. * @return {@link NearestNeighbor} implementation + * @see KdTreeNearestNeighbor + * @see KdTreeSearch1Bbf + * @see AxisSplitterMedian */ - public static

NearestNeighbor

kdtree( KdTreeDistance

distance , int maxNodesSearched ) { - return new KdTreeNearestNeighbor

(new KdTreeSearch1Bbf<>(distance,maxNodesSearched), - new KdTreeSearchNBbf<>(distance,maxNodesSearched),new AxisSplitterMedian<>(distance)); + public static

NearestNeighbor

kdtree( KdTreeDistance

distance, int maxNodesSearched ) { + return new KdTreeNearestNeighbor<>(new KdTreeSearch1Bbf<>(distance, maxNodesSearched), + new KdTreeSearchNBbf<>(distance, maxNodesSearched), new AxisSplitterMedian<>(distance)); } /** * Approximate {@link NearestNeighbor} search which uses a set of randomly generated K-D trees and a Best-Bin-First * search. Designed to work in high dimensional space. Distance measure is Euclidean squared. * - * @see KdForestBbfNearestNeighbor - * @see AxisSplitterMedian - * * @param distance Specifies how distance is computed between two points. - * @param maxNodesSearched Maximum number of nodes it will search. Controls speed and accuracy. + * @param maxNodesSearched Maximum number of nodes it will search. Controls speed and accuracy. * @param numTrees Number of trees that are considered. Try 10 and tune. * @param numConsiderSplit Number of nodes that are considered when generating a tree. Must be less than the - * point's dimension. Try 5 + * point's dimension. Try 5 * @param randomSeed Seed used by random number generator * @param

Point type. * @return {@link NearestNeighbor} implementation + * @see KdForestBbfNearestNeighbor + * @see AxisSplitterMedian */ - public static

NearestNeighbor

kdRandomForest( KdTreeDistance

distance , - int maxNodesSearched , int numTrees , int numConsiderSplit , - long randomSeed ) { - + public static

NearestNeighbor

kdRandomForest( KdTreeDistance

distance, + int maxNodesSearched, int numTrees, int numConsiderSplit, + long randomSeed ) { Random rand = new Random(randomSeed); - return new KdForestBbfNearestNeighbor<>(numTrees,maxNodesSearched,distance, - new AxisSplitterMedian<>(distance,new AxisSplitRuleRandomK(rand,numConsiderSplit))); + return new KdForestBbfNearestNeighbor<>(numTrees, maxNodesSearched, distance, + new AxisSplitterMedian<>(distance, new AxisSplitRuleRandomK(rand, numConsiderSplit))); } /** * Performs an optimal {@link NearestNeighbor} by exhaustively consider all possible solutions. * Distance measure is Euclidean squared. * - * @see org.ddogleg.nn.alg.ExhaustiveNeighbor - * * @param distance Specifies how distance is computed between two points. * @return {@link NearestNeighbor} implementation + * @see org.ddogleg.nn.alg.ExhaustiveNeighbor */ - public static

NearestNeighbor

exhaustive(KdTreeDistance

distance) { + public static

NearestNeighbor

exhaustive( KdTreeDistance

distance ) { return new WrapExhaustiveNeighbor<>(distance); } @@ -112,10 +133,9 @@ * {@link VpTree Vantage point} tree implementation for nearest neighbor search. Slower than KD-Tree on * random data, but faster than it for some pathological cases. * - * @see VpTree - * * @param randSeed Random seed * @return {@link NearestNeighbor} implementation + * @see VpTree */ public static NearestNeighbor vptree( long randSeed ) { return new VpTree(randSeed); diff -Nru ddogleg-0.18+ds/src/org/ddogleg/nn/NearestNeighbor.java ddogleg-0.22+ds/src/org/ddogleg/nn/NearestNeighbor.java --- ddogleg-0.18+ds/src/org/ddogleg/nn/NearestNeighbor.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/nn/NearestNeighbor.java 2022-09-01 02:18:09.000000000 +0000 @@ -18,7 +18,7 @@ package org.ddogleg.nn; -import org.ddogleg.struct.FastQueue; +import org.ddogleg.struct.DogArray; import java.util.List; @@ -90,6 +90,6 @@ * @param numNeighbors (Input) The number of neighbors it will search for. * @param results (Output) Storage for the result. Reset() is called. */ - void findNearest( P point , double maxDistance , int numNeighbors , FastQueue> results ); + void findNearest( P point , double maxDistance , int numNeighbors , DogArray> results ); } } diff -Nru ddogleg-0.18+ds/src/org/ddogleg/nn/NnData.java ddogleg-0.22+ds/src/org/ddogleg/nn/NnData.java --- ddogleg-0.18+ds/src/org/ddogleg/nn/NnData.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/nn/NnData.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -23,6 +23,7 @@ * * @author Peter Abeles */ +@SuppressWarnings("NullAway.Init") public class NnData

{ // tuple data public P point; diff -Nru ddogleg-0.18+ds/src/org/ddogleg/nn/wrap/KdTreeInternalSearch.java ddogleg-0.22+ds/src/org/ddogleg/nn/wrap/KdTreeInternalSearch.java --- ddogleg-0.18+ds/src/org/ddogleg/nn/wrap/KdTreeInternalSearch.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/nn/wrap/KdTreeInternalSearch.java 2022-09-01 02:18:09.000000000 +0000 @@ -24,7 +24,7 @@ import org.ddogleg.nn.alg.KdTreeResult; import org.ddogleg.nn.alg.KdTreeSearch1; import org.ddogleg.nn.alg.KdTreeSearchN; -import org.ddogleg.struct.FastQueue; +import org.ddogleg.struct.DogArray; /** * @author Peter Abeles @@ -34,7 +34,7 @@ KdTreeSearchN

searchN; // storage for multiple results - FastQueue found = new FastQueue<>(KdTreeResult::new); + DogArray found = new DogArray<>(KdTreeResult::new); KdTreeInternalSearch( KdTreeSearch1

search1, KdTreeSearchN

searchN ) { @@ -66,7 +66,7 @@ } @Override - public void findNearest(P point, double maxDistance, int numNeighbors, FastQueue> results) { + public void findNearest(P point, double maxDistance, int numNeighbors, DogArray> results) { setTree(); results.reset(); if( maxDistance <= 0 ) diff -Nru ddogleg-0.18+ds/src/org/ddogleg/nn/wrap/KdTreeNearestNeighbor.java ddogleg-0.22+ds/src/org/ddogleg/nn/wrap/KdTreeNearestNeighbor.java --- ddogleg-0.18+ds/src/org/ddogleg/nn/wrap/KdTreeNearestNeighbor.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/nn/wrap/KdTreeNearestNeighbor.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -30,6 +30,7 @@ * * @author Peter Abeles */ +@SuppressWarnings("NullAway.Init") public class KdTreeNearestNeighbor

implements NearestNeighbor

{ // tree being searched diff -Nru ddogleg-0.18+ds/src/org/ddogleg/nn/wrap/WrapExhaustiveNeighbor.java ddogleg-0.22+ds/src/org/ddogleg/nn/wrap/WrapExhaustiveNeighbor.java --- ddogleg-0.18+ds/src/org/ddogleg/nn/wrap/WrapExhaustiveNeighbor.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/nn/wrap/WrapExhaustiveNeighbor.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -22,9 +22,9 @@ import org.ddogleg.nn.NnData; import org.ddogleg.nn.alg.ExhaustiveNeighbor; import org.ddogleg.nn.alg.KdTreeDistance; -import org.ddogleg.struct.FastQueue; -import org.ddogleg.struct.GrowQueue_F64; -import org.ddogleg.struct.GrowQueue_I32; +import org.ddogleg.struct.DogArray; +import org.ddogleg.struct.DogArray_F64; +import org.ddogleg.struct.DogArray_I32; import java.util.List; @@ -33,6 +33,7 @@ * * @author Peter Abeles */ +@SuppressWarnings("NullAway.Init") public class WrapExhaustiveNeighbor

implements NearestNeighbor

{ KdTreeDistance

distance; @@ -54,8 +55,8 @@ private class InternalSearch implements Search

{ ExhaustiveNeighbor

alg; - GrowQueue_I32 outputIndex = new GrowQueue_I32(); - GrowQueue_F64 outputDistance = new GrowQueue_F64(); + DogArray_I32 outputIndex = new DogArray_I32(); + DogArray_F64 outputDistance = new DogArray_F64(); InternalSearch(KdTreeDistance

distance) { alg = new ExhaustiveNeighbor<>(distance); @@ -81,7 +82,7 @@ } @Override - public void findNearest(P point, double maxDistance, int numNeighbors, FastQueue> results) { + public void findNearest(P point, double maxDistance, int numNeighbors, DogArray> results) { results.reset(); if (maxDistance < 0) diff -Nru ddogleg-0.18+ds/src/org/ddogleg/optimization/derivative/NumericalDerivativeFB.java ddogleg-0.22+ds/src/org/ddogleg/optimization/derivative/NumericalDerivativeFB.java --- ddogleg-0.18+ds/src/org/ddogleg/optimization/derivative/NumericalDerivativeFB.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/optimization/derivative/NumericalDerivativeFB.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -54,8 +54,6 @@ public double process(double x) { double temp; - - double valueOrig = function.process(x); double h = x != 0 ? differenceScale*Math.abs(x) : differenceScale; // backwards diff -Nru ddogleg-0.18+ds/src/org/ddogleg/optimization/derivative/NumericalJacobianForward_DDRM.java ddogleg-0.22+ds/src/org/ddogleg/optimization/derivative/NumericalJacobianForward_DDRM.java --- ddogleg-0.18+ds/src/org/ddogleg/optimization/derivative/NumericalJacobianForward_DDRM.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/optimization/derivative/NumericalJacobianForward_DDRM.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -43,13 +43,13 @@ private final int M; // function being differentiated - private FunctionNtoM function; + private final FunctionNtoM function; // scaling of the difference parameter - private double differenceScale; + private final double differenceScale; - private double output0[]; - private double output1[]; + private final double[] output0; + private final double[] output1; public NumericalJacobianForward_DDRM(FunctionNtoM function, double differenceScale) { this.function = function; diff -Nru ddogleg-0.18+ds/src/org/ddogleg/optimization/derivative/NumericalJacobianForward_DSCC.java ddogleg-0.22+ds/src/org/ddogleg/optimization/derivative/NumericalJacobianForward_DSCC.java --- ddogleg-0.18+ds/src/org/ddogleg/optimization/derivative/NumericalJacobianForward_DSCC.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/optimization/derivative/NumericalJacobianForward_DSCC.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -23,7 +23,7 @@ import org.ejml.UtilEjml; import org.ejml.data.DMatrixSparseCSC; import org.ejml.data.DMatrixSparseTriplet; -import org.ejml.ops.ConvertDMatrixStruct; +import org.ejml.ops.DConvertMatrixStruct; /** * Finite difference numerical gradient calculation using forward equation. Forward @@ -45,13 +45,13 @@ private final int M; // function being differentiated - private FunctionNtoM function; + private final FunctionNtoM function; // scaling of the difference parameter - private double differenceScale; + private final double differenceScale; - private double output0[]; - private double output1[]; + private final double[] output0; + private final double[] output1; // used to decide if a variable is a zero private double zeroTolerance = UtilEjml.EPS; @@ -106,7 +106,7 @@ input[i] = x; } - ConvertDMatrixStruct.convert(tmp,jacobian); + DConvertMatrixStruct.convert(tmp,jacobian); } @Override diff -Nru ddogleg-0.18+ds/src/org/ddogleg/optimization/FactoryOptimization.java ddogleg-0.22+ds/src/org/ddogleg/optimization/FactoryOptimization.java --- ddogleg-0.18+ds/src/org/ddogleg/optimization/FactoryOptimization.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/optimization/FactoryOptimization.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -31,8 +31,7 @@ import org.ejml.data.DMatrixRMaj; import org.ejml.dense.row.factory.LinearSolverFactory_DDRM; import org.ejml.interfaces.linsol.LinearSolverDense; - -import javax.annotation.Nullable; +import org.jetbrains.annotations.Nullable; /** * Creates optimization algorithms using easy to use interfaces. These implementations/interfaces diff -Nru ddogleg-0.18+ds/src/org/ddogleg/optimization/FactoryOptimizationSparse.java ddogleg-0.22+ds/src/org/ddogleg/optimization/FactoryOptimizationSparse.java --- ddogleg-0.18+ds/src/org/ddogleg/optimization/FactoryOptimizationSparse.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/optimization/FactoryOptimizationSparse.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -30,8 +30,7 @@ import org.ejml.interfaces.linsol.LinearSolverSparse; import org.ejml.sparse.FillReducing; import org.ejml.sparse.csc.factory.LinearSolverFactory_DSCC; - -import javax.annotation.Nullable; +import org.jetbrains.annotations.Nullable; /** * Factory for sparse optimization algorithms. These implementations/interfaces diff -Nru ddogleg-0.18+ds/src/org/ddogleg/optimization/functions/SchurJacobian.java ddogleg-0.22+ds/src/org/ddogleg/optimization/functions/SchurJacobian.java --- ddogleg-0.18+ds/src/org/ddogleg/optimization/functions/SchurJacobian.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/optimization/functions/SchurJacobian.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -42,5 +42,5 @@ * @param left (Output) left side of jacobian. Will be resized to fit. * @param right (Output) right side of jacobian. Will be resized to fit. */ - void process( double input[] , S left , S right ); + void process(double[] input, S left , S right ); } diff -Nru ddogleg-0.18+ds/src/org/ddogleg/optimization/GaussNewtonBase_F64.java ddogleg-0.22+ds/src/org/ddogleg/optimization/GaussNewtonBase_F64.java --- ddogleg-0.18+ds/src/org/ddogleg/optimization/GaussNewtonBase_F64.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/optimization/GaussNewtonBase_F64.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -21,8 +21,8 @@ import org.ddogleg.optimization.math.HessianMath; import org.ejml.data.DMatrixRMaj; import org.ejml.dense.row.CommonOps_DDRM; +import org.jetbrains.annotations.Nullable; -import javax.annotation.Nullable; import java.io.PrintStream; import java.util.Arrays; @@ -34,6 +34,7 @@ * * @author Peter Abeles */ +@SuppressWarnings("NullAway.Init") public abstract class GaussNewtonBase_F64 { // Manipulating and extracting information from the Hessian @@ -67,7 +68,7 @@ protected int totalFullSteps, totalSelectSteps; // If not null then print additional information to this stream - protected PrintStream verbose; + protected @Nullable PrintStream verbose; protected int verboseLevel=0; // Optimization configuration @@ -76,14 +77,11 @@ // value of convergence tests after the last test public double ftest_val,gtest_val; - public GaussNewtonBase_F64(HM hessian) { + protected GaussNewtonBase_F64(HM hessian) { this.hessian = hessian; } - protected GaussNewtonBase_F64() { - } - - public void initialize(double initial[] , int numberOfParameters ) { + public void initialize(double[] initial, int numberOfParameters ) { x.reshape(numberOfParameters,1); x_next.reshape(numberOfParameters,1); p.reshape(numberOfParameters,1); diff -Nru ddogleg-0.18+ds/src/org/ddogleg/optimization/IterativeOptimization.java ddogleg-0.22+ds/src/org/ddogleg/optimization/IterativeOptimization.java --- ddogleg-0.18+ds/src/org/ddogleg/optimization/IterativeOptimization.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/optimization/IterativeOptimization.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -18,7 +18,8 @@ package org.ddogleg.optimization; -import javax.annotation.Nullable; +import org.jetbrains.annotations.Nullable; + import java.io.PrintStream; import java.io.Serializable; diff -Nru ddogleg-0.18+ds/src/org/ddogleg/optimization/lm/LevenbergMarquardt_F64.java ddogleg-0.22+ds/src/org/ddogleg/optimization/lm/LevenbergMarquardt_F64.java --- ddogleg-0.18+ds/src/org/ddogleg/optimization/lm/LevenbergMarquardt_F64.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/optimization/lm/LevenbergMarquardt_F64.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -78,12 +78,12 @@ public static final double MAX_LAMBDA = 1e100; // Math for some matrix operations - protected MatrixMath math; + public MatrixMath math; - DMatrixRMaj residuals = new DMatrixRMaj(1,1); + public DMatrixRMaj residuals = new DMatrixRMaj(1,1); - DMatrixRMaj diagOrig = new DMatrixRMaj(1,1); - DMatrixRMaj diagStep = new DMatrixRMaj(1,1); + public DMatrixRMaj diagOrig = new DMatrixRMaj(1,1); + public DMatrixRMaj diagStep = new DMatrixRMaj(1,1); /** * Dampening parameter. Scalar that's adjusted at every step. smaller values for a Gauss-Newton step and larger @@ -95,7 +95,7 @@ protected double nu; private final static double NU_INITIAL = 2; - public LevenbergMarquardt_F64(MatrixMath math , HM hessian ) + protected LevenbergMarquardt_F64(MatrixMath math , HM hessian ) { super(hessian); configure(new ConfigLevenbergMarquardt()); @@ -136,6 +136,7 @@ * Computes derived * @return true if it has converged or false if it has not */ + @Override protected boolean updateDerivates() { functionGradientHessian(x,true, gradient, hessian); diff -Nru ddogleg-0.18+ds/src/org/ddogleg/optimization/lm/UnconLeastSqLevenbergMarquardt_F64.java ddogleg-0.22+ds/src/org/ddogleg/optimization/lm/UnconLeastSqLevenbergMarquardt_F64.java --- ddogleg-0.18+ds/src/org/ddogleg/optimization/lm/UnconLeastSqLevenbergMarquardt_F64.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/optimization/lm/UnconLeastSqLevenbergMarquardt_F64.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -27,12 +27,14 @@ import org.ejml.data.DMatrix; import org.ejml.data.DMatrixRMaj; import org.ejml.data.ReshapeMatrix; +import org.jetbrains.annotations.Nullable; /** * Implementation of {@link LevenbergMarquardt_F64} for {@link UnconstrainedLeastSquares}. * * @author Peter Abeles */ +@SuppressWarnings("NullAway.Init") public class UnconLeastSqLevenbergMarquardt_F64 extends LevenbergMarquardt_F64> implements UnconstrainedLeastSquares @@ -51,7 +53,7 @@ } @Override - public void setFunction(FunctionNtoM function, FunctionNtoMxN jacobian) { + public void setFunction(FunctionNtoM function, @Nullable FunctionNtoMxN jacobian) { this.functionResiduals = function; if( jacobian == null ) this.functionJacobian = FactoryNumericalDerivative.jacobianForwards(function,(Class)this.jacobian.getClass()); diff -Nru ddogleg-0.18+ds/src/org/ddogleg/optimization/lm/UnconLeastSqLevenbergMarquardtSchur_F64.java ddogleg-0.22+ds/src/org/ddogleg/optimization/lm/UnconLeastSqLevenbergMarquardtSchur_F64.java --- ddogleg-0.18+ds/src/org/ddogleg/optimization/lm/UnconLeastSqLevenbergMarquardtSchur_F64.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/optimization/lm/UnconLeastSqLevenbergMarquardtSchur_F64.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -26,23 +26,22 @@ import org.ejml.data.DMatrix; import org.ejml.data.DMatrixRMaj; -import javax.annotation.Nonnull; - /** * Implementation of {@link LevenbergMarquardt_F64} for {@link UnconstrainedLeastSquaresSchur}. * * @author Peter Abeles */ +@SuppressWarnings("NullAway.Init") public class UnconLeastSqLevenbergMarquardtSchur_F64 extends LevenbergMarquardt_F64> implements UnconstrainedLeastSquaresSchur { // Left and right side of the jacobian matrix - protected S jacLeft; - protected S jacRight; + public S jacLeft; + public S jacRight; - protected FunctionNtoM functionResiduals; - protected SchurJacobian functionJacobian; + public FunctionNtoM functionResiduals; + public SchurJacobian functionJacobian; public UnconLeastSqLevenbergMarquardtSchur_F64(MatrixMath math, HessianSchurComplement hessian ) @@ -54,7 +53,7 @@ } @Override - public void setFunction(FunctionNtoM function, @Nonnull SchurJacobian jacobian) { + public void setFunction(FunctionNtoM function, SchurJacobian jacobian) { this.functionResiduals = function; this.functionJacobian = jacobian; } @@ -64,9 +63,7 @@ config.ftol = ftol; config.gtol = gtol; - super.initialize(initial, - functionResiduals.getNumOfInputsN(), - functionResiduals.getNumOfOutputsM()); + super.initialize(initial, functionResiduals.getNumOfInputsN(), functionResiduals.getNumOfOutputsM()); } @Override diff -Nru ddogleg-0.18+ds/src/org/ddogleg/optimization/math/HessianLeastSquares_DSCC.java ddogleg-0.22+ds/src/org/ddogleg/optimization/math/HessianLeastSquares_DSCC.java --- ddogleg-0.18+ds/src/org/ddogleg/optimization/math/HessianLeastSquares_DSCC.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/optimization/math/HessianLeastSquares_DSCC.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -33,7 +33,7 @@ { IGrowArray gw = new IGrowArray(); DGrowArray gx = new DGrowArray(); - + DMatrixSparseCSC transpose = new DMatrixSparseCSC(1,1); public HessianLeastSquares_DSCC() { } @@ -43,6 +43,7 @@ @Override public void updateHessian(DMatrixSparseCSC jacobian) { - CommonOps_DSCC.multTransA(jacobian,jacobian, hessian,gw,gx); + CommonOps_DSCC.transpose(jacobian,transpose,gw); + CommonOps_DSCC.mult(transpose,jacobian, hessian,gw,gx); } } diff -Nru ddogleg-0.18+ds/src/org/ddogleg/optimization/math/HessianMath_DDRM.java ddogleg-0.22+ds/src/org/ddogleg/optimization/math/HessianMath_DDRM.java --- ddogleg-0.18+ds/src/org/ddogleg/optimization/math/HessianMath_DDRM.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/optimization/math/HessianMath_DDRM.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -30,6 +30,7 @@ * * @author Peter Abeles */ +@SuppressWarnings("NullAway.Init") public class HessianMath_DDRM implements HessianMath { protected LinearSolver solver; diff -Nru ddogleg-0.18+ds/src/org/ddogleg/optimization/math/HessianMath_DSCC.java ddogleg-0.22+ds/src/org/ddogleg/optimization/math/HessianMath_DSCC.java --- ddogleg-0.18+ds/src/org/ddogleg/optimization/math/HessianMath_DSCC.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/optimization/math/HessianMath_DSCC.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -31,6 +31,7 @@ * * @author Peter Abeles */ +@SuppressWarnings("NullAway.Init") public class HessianMath_DSCC implements HessianMath { LinearSolver solver; diff -Nru ddogleg-0.18+ds/src/org/ddogleg/optimization/math/HessianSchurComplement_Base.java ddogleg-0.22+ds/src/org/ddogleg/optimization/math/HessianSchurComplement_Base.java --- ddogleg-0.18+ds/src/org/ddogleg/optimization/math/HessianSchurComplement_Base.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/optimization/math/HessianSchurComplement_Base.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -60,8 +60,8 @@ // Two solvers are created so that the structure can be saved and not recomputed each iteration protected LinearSolverSparse solverA, solverD; - public HessianSchurComplement_Base(LinearSolverSparse solverA, - LinearSolverSparse solverD) { + protected HessianSchurComplement_Base(LinearSolverSparse solverA, + LinearSolverSparse solverD) { this.solverA = solverA; this.solverD = solverD; diff -Nru ddogleg-0.18+ds/src/org/ddogleg/optimization/math/HessianSchurComplement_DSCC.java ddogleg-0.22+ds/src/org/ddogleg/optimization/math/HessianSchurComplement_DSCC.java --- ddogleg-0.18+ds/src/org/ddogleg/optimization/math/HessianSchurComplement_DSCC.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/optimization/math/HessianSchurComplement_DSCC.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -18,6 +18,7 @@ package org.ddogleg.optimization.math; +import org.ddogleg.DDoglegConcurrency; import org.ejml.data.DGrowArray; import org.ejml.data.DMatrixRMaj; import org.ejml.data.DMatrixSparseCSC; @@ -25,23 +26,28 @@ import org.ejml.interfaces.linsol.LinearSolverSparse; import org.ejml.sparse.FillReducing; import org.ejml.sparse.csc.CommonOps_DSCC; +import org.ejml.sparse.csc.CommonOps_MT_DSCC; import org.ejml.sparse.csc.factory.LinearSolverFactory_DSCC; import org.ejml.sparse.csc.mult.MatrixVectorMult_DSCC; +import org.ejml.sparse.csc.mult.Workspace_MT_DSCC; +import pabeles.concurrency.GrowArray; /** * Implementation of {@link HessianSchurComplement_Base} for {@link DMatrixSparseCSC} * * @author Peter Abeles */ +@SuppressWarnings("NullAway.Init") public class HessianSchurComplement_DSCC extends HessianSchurComplement_Base { + DMatrixSparseCSC transposed = new DMatrixSparseCSC(1,1); + // Workspace variables IGrowArray gw = new IGrowArray(); DGrowArray gx = new DGrowArray(); - - // Two solvers are created so that the structure can be saved and not recomputed each iteration - protected LinearSolverSparse solverA, solverD; + GrowArray concurrentWork = new GrowArray<>(Workspace_MT_DSCC::new); + GrowArray concurrentArrays = new GrowArray<>(DGrowArray::new); public HessianSchurComplement_DSCC() { this( LinearSolverFactory_DSCC.cholesky(FillReducing.NONE), @@ -63,13 +69,29 @@ B.reshape(jacLeft.numCols,jacRight.numCols,1); D.reshape(jacRight.numCols,jacRight.numCols,1); - // take advantage of the inner product's symmetry when possible to reduce - // the number of calculations - CommonOps_DSCC.innerProductLower(jacLeft,tmp0,gw,gx); - CommonOps_DSCC.symmLowerToFull(tmp0,A,gw); - CommonOps_DSCC.multTransA(jacLeft,jacRight,B,gw,gx); - CommonOps_DSCC.innerProductLower(jacRight,tmp0,gw,gx); - CommonOps_DSCC.symmLowerToFull(tmp0,D,gw); + if (DDoglegConcurrency.isUseConcurrent()) { + // A = L'*L + CommonOps_DSCC.transpose(jacLeft, transposed, gw); + CommonOps_MT_DSCC.mult(transposed, jacLeft, A, concurrentWork); + + // B = L'*R + CommonOps_MT_DSCC.mult(transposed, jacRight, B, concurrentWork); + + // D = R'*R + CommonOps_DSCC.transpose(jacRight, transposed, gw); + CommonOps_MT_DSCC.mult(transposed, jacRight, D, concurrentWork); + } else { + // A = L'*L + CommonOps_DSCC.transpose(jacLeft, transposed, gw); + CommonOps_DSCC.mult(transposed, jacLeft, A, gw, gx); + + // B = L'*R + CommonOps_DSCC.mult(transposed, jacRight, B, gw, gx); + + // D = R'*R + CommonOps_DSCC.transpose(jacRight, transposed, gw); + CommonOps_DSCC.mult(transposed, jacRight, D, gw, gx); + } } @Override @@ -94,21 +116,38 @@ @Override protected void multTransA(DMatrixSparseCSC A, DMatrixSparseCSC B, DMatrixSparseCSC C){ - CommonOps_DSCC.multTransA(A,B,C,gw,gx); + CommonOps_DSCC.transpose(A, transposed,gw); + if (DDoglegConcurrency.isUseConcurrent()) { + CommonOps_MT_DSCC.mult(transposed, B, C, concurrentWork); + } else { + CommonOps_DSCC.mult(transposed, B, C, gw, gx); + } } @Override protected void multTransA(DMatrixSparseCSC A, DMatrixRMaj B, DMatrixRMaj C) { - CommonOps_DSCC.multTransA(A,B,C); + if (DDoglegConcurrency.isUseConcurrent()) { + CommonOps_MT_DSCC.multTransA(A, B, C, concurrentArrays); + } else { + CommonOps_DSCC.multTransA(A, B, C, gx); + } } @Override protected void add(double alpha, DMatrixSparseCSC A, double beta, DMatrixSparseCSC B, DMatrixSparseCSC C) { - CommonOps_DSCC.add(alpha,A,beta,B,C,gw,gx); + if (DDoglegConcurrency.isUseConcurrent()) { + CommonOps_MT_DSCC.add(alpha,A,beta,B,C,concurrentWork); + } else { + CommonOps_DSCC.add(alpha,A,beta,B,C,gw,gx); + } } @Override protected void mult(DMatrixSparseCSC A, DMatrixRMaj B, DMatrixRMaj C) { - CommonOps_DSCC.mult(A,B,C); + if (DDoglegConcurrency.isUseConcurrent()) { + CommonOps_MT_DSCC.mult(A, B, C, concurrentArrays); + } else { + CommonOps_DSCC.mult(A, B, C); + } } } diff -Nru ddogleg-0.18+ds/src/org/ddogleg/optimization/math/MatrixMath_DSCC.java ddogleg-0.22+ds/src/org/ddogleg/optimization/math/MatrixMath_DSCC.java --- ddogleg-0.18+ds/src/org/ddogleg/optimization/math/MatrixMath_DSCC.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/optimization/math/MatrixMath_DSCC.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -18,6 +18,7 @@ package org.ddogleg.optimization.math; +import org.ejml.data.DGrowArray; import org.ejml.data.DMatrixRMaj; import org.ejml.data.DMatrixSparseCSC; import org.ejml.sparse.csc.CommonOps_DSCC; @@ -26,6 +27,8 @@ * @author Peter Abeles */ public class MatrixMath_DSCC implements MatrixMath { + DGrowArray workArray = new DGrowArray(); + @Override public void divideColumns(DMatrixRMaj divisor, DMatrixSparseCSC A) { CommonOps_DSCC.divideColumns(A,divisor.data,0); @@ -33,7 +36,7 @@ @Override public void multTransA(DMatrixSparseCSC A, DMatrixRMaj B, DMatrixRMaj output) { - CommonOps_DSCC.multTransA(A,B,output); + CommonOps_DSCC.multTransA(A,B,output,workArray); } @Override diff -Nru ddogleg-0.18+ds/src/org/ddogleg/optimization/quasinewton/EquationsBFGS.java ddogleg-0.22+ds/src/org/ddogleg/optimization/quasinewton/EquationsBFGS.java --- ddogleg-0.18+ds/src/org/ddogleg/optimization/quasinewton/EquationsBFGS.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/optimization/quasinewton/EquationsBFGS.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -143,17 +143,12 @@ * [1] D. Byatt and I. D. Coope and C. J. Price, "Effect of limited precision on the BFGS quasi-Newton algorithm" * Proc. of 11th Computational Techniques and Applications Conference CTAC-2003 *

- * - * @param C - * @param d - * @param y - * @param tempV0 */ public static void conjugateUpdateC( DMatrixRMaj C , DMatrixRMaj d , DMatrixRMaj y , double step, DMatrixRMaj tempV0 , DMatrixRMaj tempV1) { DMatrixRMaj z = tempV0; - DMatrixRMaj d_bar = tempV1; +// DMatrixRMaj d_bar = tempV1; CommonOps_DDRM.multTransA(C,y,z); diff -Nru ddogleg-0.18+ds/src/org/ddogleg/optimization/quasinewton/LineSearchFletcher86.java ddogleg-0.22+ds/src/org/ddogleg/optimization/quasinewton/LineSearchFletcher86.java --- ddogleg-0.18+ds/src/org/ddogleg/optimization/quasinewton/LineSearchFletcher86.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/optimization/quasinewton/LineSearchFletcher86.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -21,8 +21,8 @@ import org.ddogleg.optimization.LineSearch; import org.ddogleg.optimization.functions.CoupledDerivative; import org.ejml.UtilEjml; +import org.jetbrains.annotations.Nullable; -import javax.annotation.Nullable; import java.io.PrintStream; /** @@ -50,6 +50,7 @@ * * @author Peter Abeles */ +@SuppressWarnings("NullAway.Init") public class LineSearchFletcher86 implements LineSearch { // step tolerance change @@ -100,7 +101,7 @@ // if true the estimated parameters have been updated boolean updated; - PrintStream verbose; + @Nullable PrintStream verbose; boolean converged; /** diff -Nru ddogleg-0.18+ds/src/org/ddogleg/optimization/quasinewton/LineSearchMore94.java ddogleg-0.22+ds/src/org/ddogleg/optimization/quasinewton/LineSearchMore94.java --- ddogleg-0.18+ds/src/org/ddogleg/optimization/quasinewton/LineSearchMore94.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/optimization/quasinewton/LineSearchMore94.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -21,8 +21,8 @@ import org.ddogleg.optimization.LineSearch; import org.ddogleg.optimization.OptimizationException; import org.ddogleg.optimization.functions.CoupledDerivative; +import org.jetbrains.annotations.Nullable; -import javax.annotation.Nullable; import java.io.PrintStream; /** @@ -35,8 +35,8 @@ * *

* Wolfe condition
- * {@code φ(α) ≤ φ(0) + ftol*αφ'(0)}
- * {@code |φ'(α)| ≤ gtol*|φ'(0)|}
+ * φ(α) ≤ φ(0) + ftol*αφ'(0)
+ * |φ'(α)| ≤ gtol*|φ'(0)|
* where ftol and gtol determine the precision needed to terminate the search.. *

* @@ -57,6 +57,7 @@ *

* @author Peter Abeles */ +@SuppressWarnings("NullAway.Init") public class LineSearchMore94 implements LineSearch { // Various magic numbers. See [1] @@ -102,7 +103,7 @@ private double stpmin,stpmax; // Verbose output if not null - private PrintStream verbose; + private @Nullable PrintStream verbose; // Indicates if this iit is the first iteration and indicates if what values need to be computed at stp private boolean firstIteration; @@ -238,7 +239,7 @@ } // Check for convergence using the Wolfe conditions - if( fp <= ftest && Math.abs(gp) <= gtol*(-ginit)) { + if( fp <= ftest && Math.abs(gp) <= -gtol*ginit) { converged = true; return true; } @@ -298,7 +299,7 @@ // see if further progress can be made. If not set stp to be equal to // the best point obtained so far - if( bracket && (stp <= stmin || stp >= stmax ) || (bracket && stmax-stmin <= xtol*stmax)) + if( (bracket && (stp <= stmin || stp >= stmax )) || (bracket && stmax-stmin <= xtol*stmax)) stp=stx; updated = true; diff -Nru ddogleg-0.18+ds/src/org/ddogleg/optimization/quasinewton/QuasiNewtonBFGS.java ddogleg-0.22+ds/src/org/ddogleg/optimization/quasinewton/QuasiNewtonBFGS.java --- ddogleg-0.18+ds/src/org/ddogleg/optimization/quasinewton/QuasiNewtonBFGS.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/optimization/quasinewton/QuasiNewtonBFGS.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -23,6 +23,7 @@ import org.ejml.data.DMatrixRMaj; import org.ejml.dense.row.CommonOps_DDRM; import org.ejml.dense.row.NormOps_DDRM; +import org.jetbrains.annotations.Nullable; import java.io.PrintStream; @@ -46,6 +47,7 @@ *

* @author Peter Abeles */ +@SuppressWarnings("NullAway.Init") public class QuasiNewtonBFGS { // number of inputs @@ -90,7 +92,7 @@ private int mode; // error message - private PrintStream verbose; + private @Nullable PrintStream verbose; private int verboseLevel; // if it converged to a solution or not private boolean hasConverged; @@ -158,7 +160,7 @@ * @param Hinverse Initial hessian approximation */ public void setInitialHInv( DMatrixRMaj Hinverse) { - B.set(Hinverse); + B.setTo(Hinverse); } public void initialize(double[] initial) { @@ -402,7 +404,7 @@ return updated; } - public void setVerbose( PrintStream out , int level ) { + public void setVerbose(@Nullable PrintStream out , int level ) { this.verbose = out; if( level != 0 ) this.lineSearch.setVerbose(out,level); diff -Nru ddogleg-0.18+ds/src/org/ddogleg/optimization/quasinewton/SearchInterpolate.java ddogleg-0.22+ds/src/org/ddogleg/optimization/quasinewton/SearchInterpolate.java --- ddogleg-0.18+ds/src/org/ddogleg/optimization/quasinewton/SearchInterpolate.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/optimization/quasinewton/SearchInterpolate.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -78,9 +78,9 @@ * needs to be computed. Care has been taken reduce overflow/underflow by normalizing. *

* - * {@code + *

* φ(α) = a*α3 + b*α2 + α3 + α*φ'(0) + φ(0) - * } + *

* * @param f0 Function value at f(0) * @param g0 Derivative value at g(0) diff -Nru ddogleg-0.18+ds/src/org/ddogleg/optimization/trustregion/TrustRegionBase_F64.java ddogleg-0.22+ds/src/org/ddogleg/optimization/trustregion/TrustRegionBase_F64.java --- ddogleg-0.18+ds/src/org/ddogleg/optimization/trustregion/TrustRegionBase_F64.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/optimization/trustregion/TrustRegionBase_F64.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -26,8 +26,8 @@ import org.ejml.data.DMatrixRMaj; import org.ejml.dense.row.CommonOps_DDRM; import org.ejml.dense.row.NormOps_DDRM; +import org.jetbrains.annotations.Nullable; -import javax.annotation.Nullable; import java.io.PrintStream; import static java.lang.Math.max; @@ -57,6 +57,7 @@ * * @author Peter Abeles */ +@SuppressWarnings("NullAway.Init") public abstract class TrustRegionBase_F64 extends GaussNewtonBase_F64 { @@ -74,7 +75,7 @@ */ public double gradientNorm; - public TrustRegionBase_F64(ParameterUpdate parameterUpdate, HM hessian ) { + protected TrustRegionBase_F64(ParameterUpdate parameterUpdate, HM hessian ) { super(hessian); configure(new ConfigTrustRegion()); this.parameterUpdate = parameterUpdate; @@ -88,7 +89,7 @@ * @param numberOfParameters Number many parameters are being optimized. * @param minimumFunctionValue The minimum possible value that the function can output */ - public void initialize(double initial[] , int numberOfParameters , double minimumFunctionValue ) { + public void initialize(double[] initial, int numberOfParameters , double minimumFunctionValue ) { super.initialize(initial,numberOfParameters); tmp_p.reshape(numberOfParameters,1); @@ -353,7 +354,7 @@ */ double getStepLength(); - void setVerbose( PrintStream out , int level ); + void setVerbose( @Nullable PrintStream out , int level ); } /** diff -Nru ddogleg-0.18+ds/src/org/ddogleg/optimization/trustregion/TrustRegionUpdateCauchy_F64.java ddogleg-0.22+ds/src/org/ddogleg/optimization/trustregion/TrustRegionUpdateCauchy_F64.java --- ddogleg-0.18+ds/src/org/ddogleg/optimization/trustregion/TrustRegionUpdateCauchy_F64.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/optimization/trustregion/TrustRegionUpdateCauchy_F64.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -23,6 +23,7 @@ import org.ejml.data.DMatrix; import org.ejml.data.DMatrixRMaj; import org.ejml.dense.row.CommonOps_DDRM; +import org.jetbrains.annotations.Nullable; import java.io.PrintStream; @@ -38,6 +39,7 @@ * * @author Peter Abeles */ +@SuppressWarnings("NullAway.Init") public class TrustRegionUpdateCauchy_F64 implements TrustRegionBase_F64.ParameterUpdate { @@ -55,7 +57,7 @@ // This is the length of the step f-norm of p double stepLength; - PrintStream verbose=null; + @Nullable PrintStream verbose=null; @Override public void initialize( TrustRegionBase_F64 owner , @@ -110,7 +112,7 @@ } @Override - public void setVerbose(PrintStream out, int level) { + public void setVerbose(@Nullable PrintStream out, int level) { this.verbose = out; } } diff -Nru ddogleg-0.18+ds/src/org/ddogleg/optimization/trustregion/TrustRegionUpdateDogleg_F64.java ddogleg-0.22+ds/src/org/ddogleg/optimization/trustregion/TrustRegionUpdateDogleg_F64.java --- ddogleg-0.18+ds/src/org/ddogleg/optimization/trustregion/TrustRegionUpdateDogleg_F64.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/optimization/trustregion/TrustRegionUpdateDogleg_F64.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -25,6 +25,7 @@ import org.ejml.dense.row.CommonOps_DDRM; import org.ejml.dense.row.NormOps_DDRM; import org.ejml.dense.row.SpecializedOps_DDRM; +import org.jetbrains.annotations.Nullable; import java.io.PrintStream; @@ -45,11 +46,11 @@ * * @author Peter Abeles */ +@SuppressWarnings("NullAway.Init") public class TrustRegionUpdateDogleg_F64 implements TrustRegionBase_F64.ParameterUpdate { // TODO consider more accurate intersection method in paper - // the trust region instance which is using the update function protected TrustRegionBase_F64 owner; @@ -79,7 +80,7 @@ // This is the length of the step f-norm of p double stepLength; - PrintStream verbose = null; + @Nullable PrintStream verbose = null; @Override public void initialize( TrustRegionBase_F64 owner , int numberOfParameters , double minimumFunctionValue) { @@ -156,7 +157,7 @@ } protected void gaussNewtonStep(DMatrixRMaj step) { - step.set(stepGN); + step.setTo(stepGN); predictedReduction = owner.computePredictedReduction(stepGN); stepLength = distanceGN; } @@ -172,7 +173,7 @@ } @Override - public void setVerbose(PrintStream verbose, int level ) { + public void setVerbose(@Nullable PrintStream verbose, int level ) { this.verbose = verbose; } diff -Nru ddogleg-0.18+ds/src/org/ddogleg/optimization/trustregion/UnconLeastSqTrustRegion_F64.java ddogleg-0.22+ds/src/org/ddogleg/optimization/trustregion/UnconLeastSqTrustRegion_F64.java --- ddogleg-0.18+ds/src/org/ddogleg/optimization/trustregion/UnconLeastSqTrustRegion_F64.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/optimization/trustregion/UnconLeastSqTrustRegion_F64.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -28,12 +28,14 @@ import org.ejml.data.DMatrixRMaj; import org.ejml.data.ReshapeMatrix; import org.ejml.dense.row.SpecializedOps_DDRM; +import org.jetbrains.annotations.Nullable; /** * Implementations of {@link TrustRegionBase_F64 Trust Region} for {@link UnconstrainedLeastSquares}. * * @author Peter Abeles */ +@SuppressWarnings("NullAway.Init") public class UnconLeastSqTrustRegion_F64 extends TrustRegionBase_F64> implements UnconstrainedLeastSquares @@ -57,7 +59,7 @@ } @Override - public void setFunction(FunctionNtoM function, FunctionNtoMxN jacobian) { + public void setFunction(FunctionNtoM function, @Nullable FunctionNtoMxN jacobian) { this.functionResiduals = function; if( jacobian == null ) diff -Nru ddogleg-0.18+ds/src/org/ddogleg/optimization/trustregion/UnconLeastSqTrustRegionSchur_F64.java ddogleg-0.22+ds/src/org/ddogleg/optimization/trustregion/UnconLeastSqTrustRegionSchur_F64.java --- ddogleg-0.18+ds/src/org/ddogleg/optimization/trustregion/UnconLeastSqTrustRegionSchur_F64.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/optimization/trustregion/UnconLeastSqTrustRegionSchur_F64.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -27,8 +27,6 @@ import org.ejml.data.DMatrixRMaj; import org.ejml.dense.row.SpecializedOps_DDRM; -import javax.annotation.Nonnull; - /** * Implementations of {@link UnconstrainedLeastSquaresSchur}. Uses {@link HessianSchurComplement_DSCC} * to compute the Schur complement and perform all math related to the Hessian. The Hessian @@ -37,6 +35,7 @@ * * @author Peter Abeles */ +@SuppressWarnings("NullAway.Init") public class UnconLeastSqTrustRegionSchur_F64 extends TrustRegionBase_F64> implements UnconstrainedLeastSquaresSchur @@ -60,7 +59,7 @@ } @Override - public void setFunction(FunctionNtoM function, @Nonnull SchurJacobian jacobian) { + public void setFunction(FunctionNtoM function, SchurJacobian jacobian) { this.functionResiduals = function; this.functionJacobian = jacobian; residuals.reshape(jacobian.getNumOfOutputsM(),1); diff -Nru ddogleg-0.18+ds/src/org/ddogleg/optimization/trustregion/UnconMinTrustRegionBFGS_F64.java ddogleg-0.22+ds/src/org/ddogleg/optimization/trustregion/UnconMinTrustRegionBFGS_F64.java --- ddogleg-0.18+ds/src/org/ddogleg/optimization/trustregion/UnconMinTrustRegionBFGS_F64.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/optimization/trustregion/UnconMinTrustRegionBFGS_F64.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -34,16 +34,17 @@ * * @author Peter Abeles */ +@SuppressWarnings("NullAway.Init") public class UnconMinTrustRegionBFGS_F64 extends TrustRegionBase_F64 implements UnconstrainedMinimization { // temp variable of length N - private DMatrixRMaj y = new DMatrixRMaj(1,1); + private final DMatrixRMaj y = new DMatrixRMaj(1,1); - private DMatrixRMaj gradientPrevious = new DMatrixRMaj(1,1); - private DMatrixRMaj xPrevious = new DMatrixRMaj(1,1); - private DMatrixRMaj s = new DMatrixRMaj(1,1); + private final DMatrixRMaj gradientPrevious = new DMatrixRMaj(1,1); + private final DMatrixRMaj xPrevious = new DMatrixRMaj(1,1); + private final DMatrixRMaj s = new DMatrixRMaj(1,1); protected double f_prev; @@ -57,7 +58,7 @@ double c1=1e-4,c2=0.9; - public UnconMinTrustRegionBFGS_F64(ParameterUpdate parameterUpdate, HessianBFGS hessian ) { + public UnconMinTrustRegionBFGS_F64(ParameterUpdate parameterUpdate, HessianBFGS hessian ) { super(parameterUpdate, hessian); } @@ -116,14 +117,14 @@ // Note: there is some duplication in math between Wolfe, update(), and inverseUpdate() hessian.update(s,y); - gradientPrevious.set(gradient); - xPrevious.set(x); + gradientPrevious.setTo(gradient); + xPrevious.setTo(x); f_prev = fx; } } else { firstIteration = false; - gradientPrevious.set(gradient); - xPrevious.set(x); + gradientPrevious.setTo(gradient); + xPrevious.setTo(x); f_prev = fx; } } diff -Nru ddogleg-0.18+ds/src/org/ddogleg/optimization/UnconstrainedLeastSquares.java ddogleg-0.22+ds/src/org/ddogleg/optimization/UnconstrainedLeastSquares.java --- ddogleg-0.18+ds/src/org/ddogleg/optimization/UnconstrainedLeastSquares.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/optimization/UnconstrainedLeastSquares.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -21,6 +21,7 @@ import org.ddogleg.optimization.functions.FunctionNtoM; import org.ddogleg.optimization.functions.FunctionNtoMxN; import org.ejml.data.DMatrix; +import org.jetbrains.annotations.Nullable; /** *

@@ -61,7 +62,6 @@ public interface UnconstrainedLeastSquares extends IterativeOptimization { - /** * Specifies a set of functions and their Jacobian. See class description for documentation * on output data format. @@ -69,7 +69,7 @@ * @param function Computes the output of M functions fi(x) which take in N fit parameters as input. * @param jacobian Computes the Jacobian of the M functions. If null a numerical Jacobian will be used. */ - void setFunction( FunctionNtoM function , FunctionNtoMxN jacobian ); + void setFunction( FunctionNtoM function , @Nullable FunctionNtoMxN jacobian ); /** * Specify the initial set of parameters from which to start from. Call after @@ -80,7 +80,7 @@ * @param gtol Absolute threshold for convergence based on the gradient's norm. 0 disables test. 0 ≤ gtol. * Try 1e-12 */ - void initialize( double initial[] , double ftol , double gtol ); + void initialize(double[] initial, double ftol , double gtol ); // TODO consider adding scaling vector /** diff -Nru ddogleg-0.18+ds/src/org/ddogleg/optimization/UnconstrainedLeastSquaresSchur.java ddogleg-0.22+ds/src/org/ddogleg/optimization/UnconstrainedLeastSquaresSchur.java --- ddogleg-0.18+ds/src/org/ddogleg/optimization/UnconstrainedLeastSquaresSchur.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/optimization/UnconstrainedLeastSquaresSchur.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -23,8 +23,6 @@ import org.ddogleg.optimization.math.HessianSchurComplement_DSCC; import org.ejml.data.DMatrix; -import javax.annotation.Nonnull; - /** *

* A variant on {@link UnconstrainedLeastSquares} for solving large scale systems which can be simplified using the @@ -42,7 +40,6 @@ public interface UnconstrainedLeastSquaresSchur extends IterativeOptimization { - /** * Specifies a set of functions and their Jacobian. See class description for documentation * on output data format. @@ -50,7 +47,7 @@ * @param function Computes the output of M functions fi(x) which take in N fit parameters as input. * @param jacobian Computes the Jacobian of the M functions and breaks it up into left and right components. */ - void setFunction(FunctionNtoM function, @Nonnull SchurJacobian jacobian); + void setFunction(FunctionNtoM function, SchurJacobian jacobian); /** * Specify the initial set of parameters from which to start from. Call after @@ -61,7 +58,7 @@ * @param gtol Absolute threshold for convergence based on the gradient's norm. 0 disables test. 0 ≤ gtol. * Try 1e-12 */ - void initialize(double initial[], double ftol, double gtol); + void initialize(double[] initial, double ftol, double gtol); // TODO consider adding scaling vector /** diff -Nru ddogleg-0.18+ds/src/org/ddogleg/optimization/wrap/CachedGradientLineFunction.java ddogleg-0.22+ds/src/org/ddogleg/optimization/wrap/CachedGradientLineFunction.java --- ddogleg-0.18+ds/src/org/ddogleg/optimization/wrap/CachedGradientLineFunction.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/optimization/wrap/CachedGradientLineFunction.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2013, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -29,6 +29,7 @@ * * @author Peter Abeles */ +@SuppressWarnings("NullAway.Init") public class CachedGradientLineFunction implements GradientLineFunction { // number of parameters diff -Nru ddogleg-0.18+ds/src/org/ddogleg/optimization/wrap/CachedNumericalGradientLineFunction.java ddogleg-0.22+ds/src/org/ddogleg/optimization/wrap/CachedNumericalGradientLineFunction.java --- ddogleg-0.18+ds/src/org/ddogleg/optimization/wrap/CachedNumericalGradientLineFunction.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/optimization/wrap/CachedNumericalGradientLineFunction.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -32,6 +32,7 @@ * @author Peter Abeles */ // todo use already computed function value for gradient and derivative computation +@SuppressWarnings("NullAway.Init") public class CachedNumericalGradientLineFunction implements GradientLineFunction { // number of parameters diff -Nru ddogleg-0.18+ds/src/org/ddogleg/optimization/wrap/Individual_to_CoupledJacobian.java ddogleg-0.22+ds/src/org/ddogleg/optimization/wrap/Individual_to_CoupledJacobian.java --- ddogleg-0.18+ds/src/org/ddogleg/optimization/wrap/Individual_to_CoupledJacobian.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/optimization/wrap/Individual_to_CoupledJacobian.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -28,6 +28,7 @@ * * @author Peter Abeles */ +@SuppressWarnings("NullAway.Init") public class Individual_to_CoupledJacobian implements CoupledJacobian { FunctionNtoM func; diff -Nru ddogleg-0.18+ds/src/org/ddogleg/optimization/wrap/QuasiNewtonBFGS_to_UnconstrainedMinimization.java ddogleg-0.22+ds/src/org/ddogleg/optimization/wrap/QuasiNewtonBFGS_to_UnconstrainedMinimization.java --- ddogleg-0.18+ds/src/org/ddogleg/optimization/wrap/QuasiNewtonBFGS_to_UnconstrainedMinimization.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/optimization/wrap/QuasiNewtonBFGS_to_UnconstrainedMinimization.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -25,6 +25,7 @@ import org.ddogleg.optimization.functions.GradientLineFunction; import org.ddogleg.optimization.quasinewton.LineSearchMore94; import org.ddogleg.optimization.quasinewton.QuasiNewtonBFGS; +import org.jetbrains.annotations.Nullable; import java.io.PrintStream; @@ -77,7 +78,7 @@ } @Override - public void setVerbose(PrintStream verbose, int level) { + public void setVerbose(@Nullable PrintStream verbose, int level) { alg.setVerbose(verbose,level); } diff -Nru ddogleg-0.18+ds/src/org/ddogleg/optimization/wrap/SchurJacobian_to_NtoMxN.java ddogleg-0.22+ds/src/org/ddogleg/optimization/wrap/SchurJacobian_to_NtoMxN.java --- ddogleg-0.18+ds/src/org/ddogleg/optimization/wrap/SchurJacobian_to_NtoMxN.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/optimization/wrap/SchurJacobian_to_NtoMxN.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -31,13 +31,14 @@ * * @author Peter Abeles */ +@SuppressWarnings("NullAway.Init") public abstract class SchurJacobian_to_NtoMxN implements FunctionNtoMxN { SchurJacobian function; T left,right; - public SchurJacobian_to_NtoMxN(SchurJacobian function) { + protected SchurJacobian_to_NtoMxN(SchurJacobian function) { this.function = function; } diff -Nru ddogleg-0.18+ds/src/org/ddogleg/rand/MultivariateGaussianDraw.java ddogleg-0.22+ds/src/org/ddogleg/rand/MultivariateGaussianDraw.java --- ddogleg-0.18+ds/src/org/ddogleg/rand/MultivariateGaussianDraw.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/rand/MultivariateGaussianDraw.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2017, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -92,7 +92,7 @@ for( int i = 0; i < r.numRows; i++ ) { r.set(i,0,rand.nextGaussian()); } - x.set(mean); + x.setTo(mean); multAdd(A,r,x); diff -Nru ddogleg-0.18+ds/src/org/ddogleg/rand/UniformDraw.java ddogleg-0.22+ds/src/org/ddogleg/rand/UniformDraw.java --- ddogleg-0.18+ds/src/org/ddogleg/rand/UniformDraw.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/rand/UniformDraw.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2012-2015, Peter Abeles. All Rights Reserved. - * - * This file is part of DDogleg (http://ddogleg.org). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ddogleg.rand; - -import java.util.Random; - -/** - * Used to draw numbers from an uniform distibution. - */ -public class UniformDraw { - double min; - double max; - Random rand; - - public UniformDraw( Random rand , double min , double max ) - { - if( max < min ) { - throw new IllegalArgumentException("max must be greater than or equal min"); - } else if( Double.isInfinite(min) || Double.isInfinite(max)) { - throw new IllegalArgumentException("Must be finite"); - } else if( Double.isNaN(min) || Double.isNaN(max)) { - throw new IllegalArgumentException("Must not be NaN"); - } - - this.min = min; - this.max = max; - this.rand = rand; - } - - public UniformDraw( double min , double max ) - { - if( max < min ) { - throw new IllegalArgumentException("max must be greater than or equal min"); - } else if( Double.isInfinite(min) || Double.isInfinite(max)) { - throw new IllegalArgumentException("Must be finite"); - } else if( Double.isNaN(min) || Double.isNaN(max)) { - throw new IllegalArgumentException("Must not be NaN"); - } - - this.min = min; - this.max = max; - } - - public double getMin() { - return min; - } - - public void setRand(Random rand) { - this.rand = rand; - } - - public double getMax() { - return max; - } - - public double next() { - return rand.nextDouble()*(max-min)+min; - } - - public static double draw( Random rand , double min , double max ) - { - return rand.nextDouble()*(max-min)+min; - } - - -} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/solver/PolynomialOps.java ddogleg-0.22+ds/src/org/ddogleg/solver/PolynomialOps.java --- ddogleg-0.18+ds/src/org/ddogleg/solver/PolynomialOps.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/solver/PolynomialOps.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2015, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -22,6 +22,7 @@ import org.ddogleg.solver.impl.RootFinderCompanion; import org.ddogleg.solver.impl.SturmSequence; import org.ddogleg.solver.impl.WrapRealRootsSturm; +import org.jetbrains.annotations.Nullable; /** * @author Peter Abeles @@ -41,7 +42,7 @@ return -b/(2.0*a); } - public static Polynomial derivative( Polynomial poly , Polynomial deriv ) { + public static Polynomial derivative( Polynomial poly , @Nullable Polynomial deriv ) { if( deriv == null ) { deriv = new Polynomial(poly.size-1); } else { @@ -147,7 +148,7 @@ * If null a new instance is declared. * @return Results of the multiplication */ - public static Polynomial multiply( Polynomial a , Polynomial b , Polynomial result ) { + public static Polynomial multiply( Polynomial a , Polynomial b , @Nullable Polynomial result ) { int N = Math.max(0,a.size() + b.size() - 1); @@ -181,7 +182,7 @@ * its length must be the same as the largest polynomial 'a' or 'b'. * @return Polynomial 'a' and 'b' added together. */ - public static Polynomial add( Polynomial a , Polynomial b , Polynomial results ) { + public static Polynomial add( Polynomial a , Polynomial b , @Nullable Polynomial results ) { int N = Math.max(a.size,b.size); if( results == null ) { diff -Nru ddogleg-0.18+ds/src/org/ddogleg/sorting/ApproximateSort_F32.java ddogleg-0.22+ds/src/org/ddogleg/sorting/ApproximateSort_F32.java --- ddogleg-0.18+ds/src/org/ddogleg/sorting/ApproximateSort_F32.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/sorting/ApproximateSort_F32.java 2022-09-01 02:18:09.000000000 +0000 @@ -18,9 +18,9 @@ package org.ddogleg.sorting; +import org.ddogleg.struct.DogArray; +import org.ddogleg.struct.DogArray_I32; import org.ddogleg.struct.FastArray; -import org.ddogleg.struct.FastQueue; -import org.ddogleg.struct.GrowQueue_I32; /** * Counting sort for floating point numbers. Sorting accuracy will be to within range/numBins. @@ -29,7 +29,7 @@ */ public class ApproximateSort_F32 { - FastQueue histIndexes = new FastQueue<>(GrowQueue_I32::new); + DogArray histIndexes = new DogArray<>(DogArray_I32::new); FastArray[] histObjs = new FastArray[0]; double minValue,maxValue,divisor; @@ -139,7 +139,7 @@ // over wrist the input data with sorted elements int index = 0; for( int i = 0; i < histIndexes.size; i++ ) { - GrowQueue_I32 matches = histIndexes.get(i); + DogArray_I32 matches = histIndexes.get(i); for( int j = 0; j < matches.size; j++ ) { indexes[index++] = matches.data[j]; } diff -Nru ddogleg-0.18+ds/src/org/ddogleg/sorting/ApproximateSort_F64.java ddogleg-0.22+ds/src/org/ddogleg/sorting/ApproximateSort_F64.java --- ddogleg-0.18+ds/src/org/ddogleg/sorting/ApproximateSort_F64.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/sorting/ApproximateSort_F64.java 2022-09-01 02:18:09.000000000 +0000 @@ -18,9 +18,9 @@ package org.ddogleg.sorting; +import org.ddogleg.struct.DogArray; +import org.ddogleg.struct.DogArray_I32; import org.ddogleg.struct.FastArray; -import org.ddogleg.struct.FastQueue; -import org.ddogleg.struct.GrowQueue_I32; /** * Counting sort for floating point numbers. Sorting accuracy will be to within range/numBins. @@ -29,7 +29,7 @@ */ public class ApproximateSort_F64 { - FastQueue histIndexes = new FastQueue<>(GrowQueue_I32::new); + DogArray histIndexes = new DogArray<>(DogArray_I32::new); FastArray[] histObjs = new FastArray[0]; double minValue,maxValue,divisor; @@ -140,7 +140,7 @@ // over wrist the input data with sorted elements int index = 0; for( int i = 0; i < histIndexes.size; i++ ) { - GrowQueue_I32 matches = histIndexes.get(i); + DogArray_I32 matches = histIndexes.get(i); for( int j = 0; j < matches.size; j++ ) { indexes[index++] = matches.data[j]; } diff -Nru ddogleg-0.18+ds/src/org/ddogleg/sorting/CountingSort.java ddogleg-0.22+ds/src/org/ddogleg/sorting/CountingSort.java --- ddogleg-0.18+ds/src/org/ddogleg/sorting/CountingSort.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/sorting/CountingSort.java 2022-09-01 02:18:09.000000000 +0000 @@ -18,8 +18,8 @@ package org.ddogleg.sorting; -import org.ddogleg.struct.FastQueue; -import org.ddogleg.struct.GrowQueue_I32; +import org.ddogleg.struct.DogArray; +import org.ddogleg.struct.DogArray_I32; /** * A O(N) sorting routine for integer valued elements with a known upper and lower bound. This performance can @@ -29,9 +29,9 @@ */ public class CountingSort { - GrowQueue_I32 histogram = new GrowQueue_I32(); + DogArray_I32 histogram = new DogArray_I32(); - FastQueue histIndexes = new FastQueue<>(GrowQueue_I32::new); + DogArray histIndexes = new DogArray<>(DogArray_I32::new); int minValue,maxValue; @@ -129,7 +129,7 @@ // over wrist the input data with sorted elements int index = 0; for( int i = 0; i < histIndexes.size; i++ ) { - GrowQueue_I32 matches = histIndexes.get(i); + DogArray_I32 matches = histIndexes.get(i); for( int j = 0; j < matches.size; j++ ) { indexes[index++] = matches.data[j]; } diff -Nru ddogleg-0.18+ds/src/org/ddogleg/sorting/QuickSelect.java ddogleg-0.22+ds/src/org/ddogleg/sorting/QuickSelect.java --- ddogleg-0.18+ds/src/org/ddogleg/sorting/QuickSelect.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/sorting/QuickSelect.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -18,7 +18,6 @@ package org.ddogleg.sorting; - /** *

* QuickSelect searches for the k-th largest item in the list. While doing this search @@ -45,69 +44,81 @@ * * @param data The unsorted list * @param k The element of the sorted list that is to be found - * @param maxIndex Only element up to this value are considered + * @param idx1 Only element up to this value are considered * @return the 'k'th largest element */ - public static Comparable select( Comparable[]data , int k , int maxIndex ) { - - int i,j,mid; - int n = maxIndex; - Comparable a; - int l = 0; - int ir = n-1; + public static > T select( T[] data, int k, int idx1 ) { + return select(data, k, 0, idx1); + } - Comparable temp; + /** + * + * @param k All elements i data[idx0+i] <= data[idx0+k] + * @param idx0 Lower extent of the array considered. Inclusive. + * @param idx1 Upper extent of the array considered. Exclusive. + * @return value at 'k' + */ + public static > T select( T[] data, int k, int idx0, int idx1 ) { + k += idx0; - for(;;) { - if( ir <= l+1 ) { - if( ir == l+1 && data[ir].compareTo(data[l]) < 0 ) { + int i, j, mid; + int n = idx1; + T a; + int l = idx0; + int ir = n - 1; + + T temp; + + for (; ; ) { + if (ir <= l + 1) { + if (ir == l + 1 && data[ir].compareTo(data[l]) < 0) { temp = data[l]; data[l] = data[ir]; data[ir] = temp; } return data[k]; } else { - mid = (l+ir) >> 1; + mid = (l + ir) >> 1; - int lp1 = l+1; + int lp1 = l + 1; temp = data[mid]; data[mid] = data[lp1]; data[lp1] = temp; - if( data[l].compareTo(data[ir]) > 0 ) { + if (data[l].compareTo(data[ir]) > 0) { temp = data[l]; data[l] = data[ir]; data[ir] = temp; } - if( data[lp1].compareTo( data[ir] ) > 0 ) { + if (data[lp1].compareTo(data[ir]) > 0) { temp = data[lp1]; data[lp1] = data[ir]; data[ir] = temp; } - if( data[l].compareTo(data[lp1]) > 0 ) { + if (data[l].compareTo(data[lp1]) > 0) { temp = data[lp1]; data[lp1] = data[l]; data[l] = temp; } - i=lp1; - j=ir; - a=data[lp1]; + i = lp1; + j = ir; + a = data[lp1]; - for(;;) { - do i++; while(data[i].compareTo(a) < 0); + for (; ; ) { + do i++; while (data[i].compareTo(a) < 0); do j--; while (data[j].compareTo(a) > 0); - if( j < i) break; + if (j < i) break; temp = data[i]; data[i] = data[j]; data[j] = temp; } data[lp1] = data[j]; data[j] = a; - if( j >= k ) ir=j-1; - if( j <= k ) l=i; + if (j >= k) ir = j - 1; + if (j <= k) l = i; } } } @@ -123,23 +134,22 @@ * @param indexes (output) Sorted list of indexes. * @return the 'k'th largest element */ - public static Comparable select( Comparable[]data , int k , int maxIndex , int []indexes) { - - for( int i = 0; i < maxIndex; i++ ) { + public static > T select( T[] data, int k, int maxIndex, int[] indexes ) { + for (int i = 0; i < maxIndex; i++) { indexes[i] = i; } - int i,j,mid; + int i, j, mid; int n = maxIndex; int a; int l = 0; - int ir = n-1; + int ir = n - 1; int temp; - for(;;) { - if( ir <= l+1 ) { - if( ir == l+1 && data[indexes[ir]].compareTo(data[indexes[l]]) < 0 ) { + for (; ; ) { + if (ir <= l + 1) { + if (ir == l + 1 && data[indexes[ir]].compareTo(data[indexes[l]]) < 0) { temp = indexes[l]; indexes[l] = indexes[ir]; @@ -147,47 +157,47 @@ } return data[k]; } else { - mid = (l+ir) >> 1; + mid = (l + ir) >> 1; - int lp1 = l+1; + int lp1 = l + 1; temp = indexes[mid]; indexes[mid] = indexes[lp1]; indexes[lp1] = temp; - if( data[indexes[l]].compareTo(data[indexes[ir]]) > 0 ) { + if (data[indexes[l]].compareTo(data[indexes[ir]]) > 0) { temp = indexes[l]; indexes[l] = indexes[ir]; indexes[ir] = temp; } - if( data[indexes[lp1]].compareTo( data[indexes[ir]] ) > 0 ) { + if (data[indexes[lp1]].compareTo(data[indexes[ir]]) > 0) { temp = indexes[lp1]; indexes[lp1] = indexes[ir]; indexes[ir] = temp; } - if( data[indexes[l]].compareTo(data[indexes[lp1]]) > 0 ) { + if (data[indexes[l]].compareTo(data[indexes[lp1]]) > 0) { temp = indexes[lp1]; indexes[lp1] = indexes[l]; indexes[l] = temp; } - i=lp1; - j=ir; - a=indexes[lp1]; + i = lp1; + j = ir; + a = indexes[lp1]; - for(;;) { - do i++; while(data[indexes[i]].compareTo(data[a]) < 0); + for (; ; ) { + do i++; while (data[indexes[i]].compareTo(data[a]) < 0); do j--; while (data[indexes[j]].compareTo(data[a]) > 0); - if( j < i) break; + if (j < i) break; temp = indexes[i]; indexes[i] = indexes[j]; indexes[j] = temp; } indexes[lp1] = indexes[j]; indexes[j] = a; - if( j >= k ) ir=j-1; - if( j <= k ) l=i; + if (j >= k) ir = j - 1; + if (j <= k) l = i; } } } @@ -199,69 +209,80 @@ * * @param data The unsorted list. Is modified. * @param k The element of the sorted list that is to be found - * @param maxIndex Only element up to this value are considered + * @param size Only element up to this value are considered * @return the 'k'th largest element */ - public static float select( float []data , int k , int maxIndex ) { + public static float select( float[] data, int k, int size ) { + return select(data, k, 0, size); + } - int i,j,mid; - int n = maxIndex; + /** + * + * @param k All elements i data[idx0+i] <= data[idx0+k] + * @param idx0 Lower extent of the array considered. Inclusive. + * @param idx1 Upper extent of the array considered. Exclusive. + * @return value at 'k' + */ + public static float select( float[] data, int k, int idx0, int idx1 ) { + k += idx0; + int i, j, mid; + int n = idx1; float a; - int l = 0; - int ir = n-1; + int l = idx0; + int ir = n - 1; float temp; - for(;;) { - if( ir <= l+1 ) { - if( ir == l+1 && data[ir] < data[l] ) { + for (; ; ) { + if (ir <= l + 1) { + if (ir == l + 1 && data[ir] < data[l]) { temp = data[l]; data[l] = data[ir]; data[ir] = temp; } return data[k]; } else { - mid = (l+ir) >> 1; + mid = (l + ir) >> 1; - int lp1 = l+1; + int lp1 = l + 1; temp = data[mid]; data[mid] = data[lp1]; data[lp1] = temp; - if( data[l] > data[ir] ) { + if (data[l] > data[ir]) { temp = data[l]; data[l] = data[ir]; data[ir] = temp; } - if( data[lp1] > data[ir] ) { + if (data[lp1] > data[ir]) { temp = data[lp1]; data[lp1] = data[ir]; data[ir] = temp; } - if( data[l] > data[lp1] ) { + if (data[l] > data[lp1]) { temp = data[lp1]; data[lp1] = data[l]; data[l] = temp; } - i=lp1; - j=ir; - a=data[lp1]; - - for(;;) { - do i++; while(data[i]a); - if( j < i) break; + i = lp1; + j = ir; + a = data[lp1]; + + for (; ; ) { + do i++; while (data[i] < a); + do j--; while (data[j] > a); + if (j < i) break; temp = data[i]; data[i] = data[j]; data[j] = temp; } data[lp1] = data[j]; data[j] = a; - if( j >= k ) ir=j-1; - if( j <= k ) l=i; + if (j >= k) ir = j - 1; + if (j <= k) l = i; } } } @@ -270,77 +291,76 @@ *

* Returns the original index of the 'k' largest element in the list. *

- * + * *

* Note: There is additional overhead since the values of indexes needs to be set *

- * + * * @param indexes Temporary storage and is overwritten */ - public static int selectIndex( float []data , int k , int maxIndex , int []indexes) { - - for( int i = 0; i < maxIndex; i++ ) { + public static int selectIndex( float[] data, int k, int maxIndex, int[] indexes ) { + for (int i = 0; i < maxIndex; i++) { indexes[i] = i; } - int i,j,mid; + int i, j, mid; int n = maxIndex; int a; int l = 0; - int ir = n-1; + int ir = n - 1; int temp; - for(;;) { - if( ir <= l+1 ) { - if( ir == l+1 && data[indexes[ir]] < data[indexes[l]] ) { + for (; ; ) { + if (ir <= l + 1) { + if (ir == l + 1 && data[indexes[ir]] < data[indexes[l]]) { temp = indexes[l]; indexes[l] = indexes[ir]; indexes[ir] = temp; } return indexes[k]; } else { - mid = (l+ir) >> 1; + mid = (l + ir) >> 1; - int lp1 = l+1; + int lp1 = l + 1; temp = indexes[mid]; indexes[mid] = indexes[lp1]; indexes[lp1] = temp; - if( data[indexes[l]] > data[indexes[ir]] ) { + if (data[indexes[l]] > data[indexes[ir]]) { temp = indexes[l]; indexes[l] = indexes[ir]; indexes[ir] = temp; } - if( data[indexes[lp1]] > data[indexes[ir]] ) { + if (data[indexes[lp1]] > data[indexes[ir]]) { temp = indexes[lp1]; indexes[lp1] = indexes[ir]; indexes[ir] = temp; } - if( data[indexes[l]] > data[indexes[lp1]] ) { + if (data[indexes[l]] > data[indexes[lp1]]) { temp = indexes[lp1]; indexes[lp1] = indexes[l]; indexes[l] = temp; } - i=lp1; - j=ir; - a=indexes[lp1]; - - for(;;) { - do i++; while(data[indexes[i]]data[a]); - if( j < i) break; + i = lp1; + j = ir; + a = indexes[lp1]; + + for (; ; ) { + do i++; while (data[indexes[i]] < data[a]); + do j--; while (data[indexes[j]] > data[a]); + if (j < i) break; temp = indexes[i]; indexes[i] = indexes[j]; indexes[j] = temp; } indexes[lp1] = indexes[j]; indexes[j] = a; - if( j >= k ) ir=j-1; - if( j <= k ) l=i; + if (j >= k) ir = j - 1; + if (j <= k) l = i; } } } @@ -352,69 +372,81 @@ * * @param data The unsorted list. Is modified. * @param k The element of the sorted list that is to be found - * @param maxIndex Only element up to this value are considered + * @param size Only element up to this value are considered * @return the 'k'th largest element */ - public static double select( double []data , int k , int maxIndex ) { + public static double select( double[] data, int k, int size ) { + return select(data, k, 0, size); + } - int i,j,mid; - int n = maxIndex; + /** + * + * @param k All elements i data[idx0+i] <= data[idx0+k] + * @param idx0 Lower extent of the array considered. Inclusive. + * @param idx1 Upper extent of the array considered. Exclusive. + * @return value at 'k' + */ + public static double select( double[] data, int k, int idx0, int idx1 ) { + k += idx0; + + int i, j, mid; + int n = idx1; double a; - int l = 0; - int ir = n-1; + int l = idx0; + int ir = n - 1; double temp; - for(;;) { - if( ir <= l+1 ) { - if( ir == l+1 && data[ir] < data[l] ) { + for (; ; ) { + if (ir <= l + 1) { + if (ir == l + 1 && data[ir] < data[l]) { temp = data[l]; data[l] = data[ir]; data[ir] = temp; } return data[k]; } else { - mid = (l+ir) >> 1; + mid = (l + ir) >> 1; - int lp1 = l+1; + int lp1 = l + 1; temp = data[mid]; data[mid] = data[lp1]; data[lp1] = temp; - if( data[l] > data[ir] ) { + if (data[l] > data[ir]) { temp = data[l]; data[l] = data[ir]; data[ir] = temp; } - if( data[lp1] > data[ir] ) { + if (data[lp1] > data[ir]) { temp = data[lp1]; data[lp1] = data[ir]; data[ir] = temp; } - if( data[l] > data[lp1] ) { + if (data[l] > data[lp1]) { temp = data[lp1]; data[lp1] = data[l]; data[l] = temp; } - i=lp1; - j=ir; - a=data[lp1]; - - for(;;) { - do i++; while(data[i]a); - if( j < i) break; + i = lp1; + j = ir; + a = data[lp1]; + + for (; ; ) { + do i++; while (data[i] < a); + do j--; while (data[j] > a); + if (j < i) break; temp = data[i]; data[i] = data[j]; data[j] = temp; } data[lp1] = data[j]; data[j] = a; - if( j >= k ) ir=j-1; - if( j <= k ) l=i; + if (j >= k) ir = j - 1; + if (j <= k) l = i; } } } @@ -423,77 +455,77 @@ *

* Returns the original index of the 'k' largest element in the list. *

- * + * *

* Note: There is additional overhead since the values of indexes needs to be set *

- * + * * @param indexes Temporary storage and is overwritten */ - public static int selectIndex( double []data , int k , int maxIndex , int []indexes) { + public static int selectIndex( double[] data, int k, int maxIndex, int[] indexes ) { - for( int i = 0; i < maxIndex; i++ ) { + for (int i = 0; i < maxIndex; i++) { indexes[i] = i; } - int i,j,mid; + int i, j, mid; int n = maxIndex; int a; int l = 0; - int ir = n-1; + int ir = n - 1; int temp; - for(;;) { - if( ir <= l+1 ) { - if( ir == l+1 && data[indexes[ir]] < data[indexes[l]] ) { + for (; ; ) { + if (ir <= l + 1) { + if (ir == l + 1 && data[indexes[ir]] < data[indexes[l]]) { temp = indexes[l]; indexes[l] = indexes[ir]; indexes[ir] = temp; } return indexes[k]; } else { - mid = (l+ir) >> 1; + mid = (l + ir) >> 1; - int lp1 = l+1; + int lp1 = l + 1; temp = indexes[mid]; indexes[mid] = indexes[lp1]; indexes[lp1] = temp; - if( data[indexes[l]] > data[indexes[ir]] ) { + if (data[indexes[l]] > data[indexes[ir]]) { temp = indexes[l]; indexes[l] = indexes[ir]; indexes[ir] = temp; } - if( data[indexes[lp1]] > data[indexes[ir]] ) { + if (data[indexes[lp1]] > data[indexes[ir]]) { temp = indexes[lp1]; indexes[lp1] = indexes[ir]; indexes[ir] = temp; } - if( data[indexes[l]] > data[indexes[lp1]] ) { + if (data[indexes[l]] > data[indexes[lp1]]) { temp = indexes[lp1]; indexes[lp1] = indexes[l]; indexes[l] = temp; } - i=lp1; - j=ir; - a=indexes[lp1]; - - for(;;) { - do i++; while(data[indexes[i]]data[a]); - if( j < i) break; + i = lp1; + j = ir; + a = indexes[lp1]; + + for (; ; ) { + do i++; while (data[indexes[i]] < data[a]); + do j--; while (data[indexes[j]] > data[a]); + if (j < i) break; temp = indexes[i]; indexes[i] = indexes[j]; indexes[j] = temp; } indexes[lp1] = indexes[j]; indexes[j] = a; - if( j >= k ) ir=j-1; - if( j <= k ) l=i; + if (j >= k) ir = j - 1; + if (j <= k) l = i; } } } @@ -508,66 +540,66 @@ * @param maxIndex Only element up to this value are considered * @return the 'k'th largest element */ - public static long select( long []data , int k , int maxIndex ) { + public static long select( long[] data, int k, int maxIndex ) { - int i,j,mid; + int i, j, mid; int n = maxIndex; long a; int l = 0; - int ir = n-1; + int ir = n - 1; long temp; - for(;;) { - if( ir <= l+1 ) { - if( ir == l+1 && data[ir] < data[l] ) { + for (; ; ) { + if (ir <= l + 1) { + if (ir == l + 1 && data[ir] < data[l]) { temp = data[l]; data[l] = data[ir]; data[ir] = temp; } return data[k]; } else { - mid = (l+ir) >> 1; + mid = (l + ir) >> 1; - int lp1 = l+1; + int lp1 = l + 1; temp = data[mid]; data[mid] = data[lp1]; data[lp1] = temp; - if( data[l] > data[ir] ) { + if (data[l] > data[ir]) { temp = data[l]; data[l] = data[ir]; data[ir] = temp; } - if( data[lp1] > data[ir] ) { + if (data[lp1] > data[ir]) { temp = data[lp1]; data[lp1] = data[ir]; data[ir] = temp; } - if( data[l] > data[lp1] ) { + if (data[l] > data[lp1]) { temp = data[lp1]; data[lp1] = data[l]; data[l] = temp; } - i=lp1; - j=ir; - a=data[lp1]; - - for(;;) { - do i++; while(data[i]a); - if( j < i) break; + i = lp1; + j = ir; + a = data[lp1]; + + for (; ; ) { + do i++; while (data[i] < a); + do j--; while (data[j] > a); + if (j < i) break; temp = data[i]; data[i] = data[j]; data[j] = temp; } data[lp1] = data[j]; data[j] = a; - if( j >= k ) ir=j-1; - if( j <= k ) l=i; + if (j >= k) ir = j - 1; + if (j <= k) l = i; } } } @@ -576,77 +608,77 @@ *

* Returns the original index of the 'k' largest element in the list. *

- * + * *

* Note: There is additional overhead since the values of indexes needs to be set *

- * + * * @param indexes Temporary storage and is overwritten */ - public static int selectIndex( long []data , int k , int maxIndex , int []indexes) { + public static int selectIndex( long[] data, int k, int maxIndex, int[] indexes ) { - for( int i = 0; i < maxIndex; i++ ) { + for (int i = 0; i < maxIndex; i++) { indexes[i] = i; } - int i,j,mid; + int i, j, mid; int n = maxIndex; int a; int l = 0; - int ir = n-1; + int ir = n - 1; int temp; - for(;;) { - if( ir <= l+1 ) { - if( ir == l+1 && data[indexes[ir]] < data[indexes[l]] ) { + for (; ; ) { + if (ir <= l + 1) { + if (ir == l + 1 && data[indexes[ir]] < data[indexes[l]]) { temp = indexes[l]; indexes[l] = indexes[ir]; indexes[ir] = temp; } return indexes[k]; } else { - mid = (l+ir) >> 1; + mid = (l + ir) >> 1; - int lp1 = l+1; + int lp1 = l + 1; temp = indexes[mid]; indexes[mid] = indexes[lp1]; indexes[lp1] = temp; - if( data[indexes[l]] > data[indexes[ir]] ) { + if (data[indexes[l]] > data[indexes[ir]]) { temp = indexes[l]; indexes[l] = indexes[ir]; indexes[ir] = temp; } - if( data[indexes[lp1]] > data[indexes[ir]] ) { + if (data[indexes[lp1]] > data[indexes[ir]]) { temp = indexes[lp1]; indexes[lp1] = indexes[ir]; indexes[ir] = temp; } - if( data[indexes[l]] > data[indexes[lp1]] ) { + if (data[indexes[l]] > data[indexes[lp1]]) { temp = indexes[lp1]; indexes[lp1] = indexes[l]; indexes[l] = temp; } - i=lp1; - j=ir; - a=indexes[lp1]; - - for(;;) { - do i++; while(data[indexes[i]]data[a]); - if( j < i) break; + i = lp1; + j = ir; + a = indexes[lp1]; + + for (; ; ) { + do i++; while (data[indexes[i]] < data[a]); + do j--; while (data[indexes[j]] > data[a]); + if (j < i) break; temp = indexes[i]; indexes[i] = indexes[j]; indexes[j] = temp; } indexes[lp1] = indexes[j]; indexes[j] = a; - if( j >= k ) ir=j-1; - if( j <= k ) l=i; + if (j >= k) ir = j - 1; + if (j <= k) l = i; } } } @@ -661,66 +693,66 @@ * @param maxIndex Only element up to this value are considered * @return the 'k'th largest element */ - public static int select( int []data , int k , int maxIndex ) { + public static int select( int[] data, int k, int maxIndex ) { - int i,j,mid; + int i, j, mid; int n = maxIndex; int a; int l = 0; - int ir = n-1; + int ir = n - 1; int temp; - for(;;) { - if( ir <= l+1 ) { - if( ir == l+1 && data[ir] < data[l] ) { + for (; ; ) { + if (ir <= l + 1) { + if (ir == l + 1 && data[ir] < data[l]) { temp = data[l]; data[l] = data[ir]; data[ir] = temp; } return data[k]; } else { - mid = (l+ir) >> 1; + mid = (l + ir) >> 1; - int lp1 = l+1; + int lp1 = l + 1; temp = data[mid]; data[mid] = data[lp1]; data[lp1] = temp; - if( data[l] > data[ir] ) { + if (data[l] > data[ir]) { temp = data[l]; data[l] = data[ir]; data[ir] = temp; } - if( data[lp1] > data[ir] ) { + if (data[lp1] > data[ir]) { temp = data[lp1]; data[lp1] = data[ir]; data[ir] = temp; } - if( data[l] > data[lp1] ) { + if (data[l] > data[lp1]) { temp = data[lp1]; data[lp1] = data[l]; data[l] = temp; } - i=lp1; - j=ir; - a=data[lp1]; - - for(;;) { - do i++; while(data[i]a); - if( j < i) break; + i = lp1; + j = ir; + a = data[lp1]; + + for (; ; ) { + do i++; while (data[i] < a); + do j--; while (data[j] > a); + if (j < i) break; temp = data[i]; data[i] = data[j]; data[j] = temp; } data[lp1] = data[j]; data[j] = a; - if( j >= k ) ir=j-1; - if( j <= k ) l=i; + if (j >= k) ir = j - 1; + if (j <= k) l = i; } } } @@ -729,77 +761,77 @@ *

* Returns the original index of the 'k' largest element in the list. *

- * + * *

* Note: There is additional overhead since the values of indexes needs to be set *

- * + * * @param indexes Temporary storage and is overwritten */ - public static int selectIndex( int []data , int k , int maxIndex , int []indexes) { + public static int selectIndex( int[] data, int k, int maxIndex, int[] indexes ) { - for( int i = 0; i < maxIndex; i++ ) { + for (int i = 0; i < maxIndex; i++) { indexes[i] = i; } - int i,j,mid; + int i, j, mid; int n = maxIndex; int a; int l = 0; - int ir = n-1; + int ir = n - 1; int temp; - for(;;) { - if( ir <= l+1 ) { - if( ir == l+1 && data[indexes[ir]] < data[indexes[l]] ) { + for (; ; ) { + if (ir <= l + 1) { + if (ir == l + 1 && data[indexes[ir]] < data[indexes[l]]) { temp = indexes[l]; indexes[l] = indexes[ir]; indexes[ir] = temp; } return indexes[k]; } else { - mid = (l+ir) >> 1; + mid = (l + ir) >> 1; - int lp1 = l+1; + int lp1 = l + 1; temp = indexes[mid]; indexes[mid] = indexes[lp1]; indexes[lp1] = temp; - if( data[indexes[l]] > data[indexes[ir]] ) { + if (data[indexes[l]] > data[indexes[ir]]) { temp = indexes[l]; indexes[l] = indexes[ir]; indexes[ir] = temp; } - if( data[indexes[lp1]] > data[indexes[ir]] ) { + if (data[indexes[lp1]] > data[indexes[ir]]) { temp = indexes[lp1]; indexes[lp1] = indexes[ir]; indexes[ir] = temp; } - if( data[indexes[l]] > data[indexes[lp1]] ) { + if (data[indexes[l]] > data[indexes[lp1]]) { temp = indexes[lp1]; indexes[lp1] = indexes[l]; indexes[l] = temp; } - i=lp1; - j=ir; - a=indexes[lp1]; - - for(;;) { - do i++; while(data[indexes[i]]data[a]); - if( j < i) break; + i = lp1; + j = ir; + a = indexes[lp1]; + + for (; ; ) { + do i++; while (data[indexes[i]] < data[a]); + do j--; while (data[indexes[j]] > data[a]); + if (j < i) break; temp = indexes[i]; indexes[i] = indexes[j]; indexes[j] = temp; } indexes[lp1] = indexes[j]; indexes[j] = a; - if( j >= k ) ir=j-1; - if( j <= k ) l=i; + if (j >= k) ir = j - 1; + if (j <= k) l = i; } } } @@ -814,66 +846,66 @@ * @param maxIndex Only element up to this value are considered * @return the 'k'th largest element */ - public static short select( short []data , int k , int maxIndex ) { + public static short select( short[] data, int k, int maxIndex ) { - int i,j,mid; + int i, j, mid; int n = maxIndex; short a; int l = 0; - int ir = n-1; + int ir = n - 1; short temp; - for(;;) { - if( ir <= l+1 ) { - if( ir == l+1 && data[ir] < data[l] ) { + for (; ; ) { + if (ir <= l + 1) { + if (ir == l + 1 && data[ir] < data[l]) { temp = data[l]; data[l] = data[ir]; data[ir] = temp; } return data[k]; } else { - mid = (l+ir) >> 1; + mid = (l + ir) >> 1; - int lp1 = l+1; + int lp1 = l + 1; temp = data[mid]; data[mid] = data[lp1]; data[lp1] = temp; - if( data[l] > data[ir] ) { + if (data[l] > data[ir]) { temp = data[l]; data[l] = data[ir]; data[ir] = temp; } - if( data[lp1] > data[ir] ) { + if (data[lp1] > data[ir]) { temp = data[lp1]; data[lp1] = data[ir]; data[ir] = temp; } - if( data[l] > data[lp1] ) { + if (data[l] > data[lp1]) { temp = data[lp1]; data[lp1] = data[l]; data[l] = temp; } - i=lp1; - j=ir; - a=data[lp1]; - - for(;;) { - do i++; while(data[i]a); - if( j < i) break; + i = lp1; + j = ir; + a = data[lp1]; + + for (; ; ) { + do i++; while (data[i] < a); + do j--; while (data[j] > a); + if (j < i) break; temp = data[i]; data[i] = data[j]; data[j] = temp; } data[lp1] = data[j]; data[j] = a; - if( j >= k ) ir=j-1; - if( j <= k ) l=i; + if (j >= k) ir = j - 1; + if (j <= k) l = i; } } } @@ -882,77 +914,77 @@ *

* Returns the original index of the 'k' largest element in the list. *

- * + * *

* Note: There is additional overhead since the values of indexes needs to be set *

- * + * * @param indexes Temporary storage and is overwritten */ - public static int selectIndex( short []data , int k , int maxIndex , int []indexes) { + public static int selectIndex( short[] data, int k, int maxIndex, int[] indexes ) { - for( int i = 0; i < maxIndex; i++ ) { + for (int i = 0; i < maxIndex; i++) { indexes[i] = i; } - int i,j,mid; + int i, j, mid; int n = maxIndex; int a; int l = 0; - int ir = n-1; + int ir = n - 1; int temp; - for(;;) { - if( ir <= l+1 ) { - if( ir == l+1 && data[indexes[ir]] < data[indexes[l]] ) { + for (; ; ) { + if (ir <= l + 1) { + if (ir == l + 1 && data[indexes[ir]] < data[indexes[l]]) { temp = indexes[l]; indexes[l] = indexes[ir]; indexes[ir] = temp; } return indexes[k]; } else { - mid = (l+ir) >> 1; + mid = (l + ir) >> 1; - int lp1 = l+1; + int lp1 = l + 1; temp = indexes[mid]; indexes[mid] = indexes[lp1]; indexes[lp1] = temp; - if( data[indexes[l]] > data[indexes[ir]] ) { + if (data[indexes[l]] > data[indexes[ir]]) { temp = indexes[l]; indexes[l] = indexes[ir]; indexes[ir] = temp; } - if( data[indexes[lp1]] > data[indexes[ir]] ) { + if (data[indexes[lp1]] > data[indexes[ir]]) { temp = indexes[lp1]; indexes[lp1] = indexes[ir]; indexes[ir] = temp; } - if( data[indexes[l]] > data[indexes[lp1]] ) { + if (data[indexes[l]] > data[indexes[lp1]]) { temp = indexes[lp1]; indexes[lp1] = indexes[l]; indexes[l] = temp; } - i=lp1; - j=ir; - a=indexes[lp1]; - - for(;;) { - do i++; while(data[indexes[i]]data[a]); - if( j < i) break; + i = lp1; + j = ir; + a = indexes[lp1]; + + for (; ; ) { + do i++; while (data[indexes[i]] < data[a]); + do j--; while (data[indexes[j]] > data[a]); + if (j < i) break; temp = indexes[i]; indexes[i] = indexes[j]; indexes[j] = temp; } indexes[lp1] = indexes[j]; indexes[j] = a; - if( j >= k ) ir=j-1; - if( j <= k ) l=i; + if (j >= k) ir = j - 1; + if (j <= k) l = i; } } } @@ -967,66 +999,66 @@ * @param maxIndex Only element up to this value are considered * @return the 'k'th largest element */ - public static byte select( byte []data , int k , int maxIndex ) { + public static byte select( byte[] data, int k, int maxIndex ) { - int i,j,mid; + int i, j, mid; int n = maxIndex; byte a; int l = 0; - int ir = n-1; + int ir = n - 1; byte temp; - for(;;) { - if( ir <= l+1 ) { - if( ir == l+1 && data[ir] < data[l] ) { + for (; ; ) { + if (ir <= l + 1) { + if (ir == l + 1 && data[ir] < data[l]) { temp = data[l]; data[l] = data[ir]; data[ir] = temp; } return data[k]; } else { - mid = (l+ir) >> 1; + mid = (l + ir) >> 1; - int lp1 = l+1; + int lp1 = l + 1; temp = data[mid]; data[mid] = data[lp1]; data[lp1] = temp; - if( data[l] > data[ir] ) { + if (data[l] > data[ir]) { temp = data[l]; data[l] = data[ir]; data[ir] = temp; } - if( data[lp1] > data[ir] ) { + if (data[lp1] > data[ir]) { temp = data[lp1]; data[lp1] = data[ir]; data[ir] = temp; } - if( data[l] > data[lp1] ) { + if (data[l] > data[lp1]) { temp = data[lp1]; data[lp1] = data[l]; data[l] = temp; } - i=lp1; - j=ir; - a=data[lp1]; - - for(;;) { - do i++; while(data[i]a); - if( j < i) break; + i = lp1; + j = ir; + a = data[lp1]; + + for (; ; ) { + do i++; while (data[i] < a); + do j--; while (data[j] > a); + if (j < i) break; temp = data[i]; data[i] = data[j]; data[j] = temp; } data[lp1] = data[j]; data[j] = a; - if( j >= k ) ir=j-1; - if( j <= k ) l=i; + if (j >= k) ir = j - 1; + if (j <= k) l = i; } } } @@ -1035,80 +1067,78 @@ *

* Returns the original index of the 'k' largest element in the list. *

- * + * *

* Note: There is additional overhead since the values of indexes needs to be set *

- * + * * @param indexes Temporary storage and is overwritten */ - public static int selectIndex( byte []data , int k , int maxIndex , int []indexes) { + public static int selectIndex( byte[] data, int k, int maxIndex, int[] indexes ) { - for( int i = 0; i < maxIndex; i++ ) { + for (int i = 0; i < maxIndex; i++) { indexes[i] = i; } - int i,j,mid; + int i, j, mid; int n = maxIndex; int a; int l = 0; - int ir = n-1; + int ir = n - 1; int temp; - for(;;) { - if( ir <= l+1 ) { - if( ir == l+1 && data[indexes[ir]] < data[indexes[l]] ) { + for (; ; ) { + if (ir <= l + 1) { + if (ir == l + 1 && data[indexes[ir]] < data[indexes[l]]) { temp = indexes[l]; indexes[l] = indexes[ir]; indexes[ir] = temp; } return indexes[k]; } else { - mid = (l+ir) >> 1; + mid = (l + ir) >> 1; - int lp1 = l+1; + int lp1 = l + 1; temp = indexes[mid]; indexes[mid] = indexes[lp1]; indexes[lp1] = temp; - if( data[indexes[l]] > data[indexes[ir]] ) { + if (data[indexes[l]] > data[indexes[ir]]) { temp = indexes[l]; indexes[l] = indexes[ir]; indexes[ir] = temp; } - if( data[indexes[lp1]] > data[indexes[ir]] ) { + if (data[indexes[lp1]] > data[indexes[ir]]) { temp = indexes[lp1]; indexes[lp1] = indexes[ir]; indexes[ir] = temp; } - if( data[indexes[l]] > data[indexes[lp1]] ) { + if (data[indexes[l]] > data[indexes[lp1]]) { temp = indexes[lp1]; indexes[lp1] = indexes[l]; indexes[l] = temp; } - i=lp1; - j=ir; - a=indexes[lp1]; - - for(;;) { - do i++; while(data[indexes[i]]data[a]); - if( j < i) break; + i = lp1; + j = ir; + a = indexes[lp1]; + + for (; ; ) { + do i++; while (data[indexes[i]] < data[a]); + do j--; while (data[indexes[j]] > data[a]); + if (j < i) break; temp = indexes[i]; indexes[i] = indexes[j]; indexes[j] = temp; } indexes[lp1] = indexes[j]; indexes[j] = a; - if( j >= k ) ir=j-1; - if( j <= k ) l=i; + if (j >= k) ir = j - 1; + if (j <= k) l = i; } } } - - } diff -Nru ddogleg-0.18+ds/src/org/ddogleg/sorting/QuickSortComparable.java ddogleg-0.22+ds/src/org/ddogleg/sorting/QuickSortComparable.java --- ddogleg-0.18+ds/src/org/ddogleg/sorting/QuickSortComparable.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/sorting/QuickSortComparable.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,291 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.sorting; + +import java.util.List; + +/** + * An implementation of the quick sort algorithm from Numerical Recipes Third Edition + * that is specified for arrays of floats. + * + * A small amount of memory is declared for this sorting algorithm. + * + * This implementation of QuickSort allows you to use a Comparator. Useful when you want to ensure that no + * extra memory is declared each time you sort. This is possible when using built in methods. + */ +public class QuickSortComparable> { + // an architecture dependent tuning parameter + private int M = 7; + final private int NSTACK; + + final private int[] istack; + + public QuickSortComparable() { + this(65, 7); + } + + public QuickSortComparable( int NSTACK, int M ) { + this.M = M; + this.NSTACK = NSTACK; + + istack = new int[NSTACK]; + } + + public void sort( T[] arr, int length ) { + int i, ir, j, k; + int jstack = -1; + int l = 0; + // if I ever publish a book I will never use variable l in an algorithm with lots of 1 + T a; + + ir = length - 1; + + T temp; + + for (; ; ) { + if (ir - l < M) { + for (j = l + 1; j <= ir; j++) { + a = arr[j]; + for (i = j - 1; i >= l; i--) { + if (arr[i].compareTo(a) <= 0) break; + arr[i + 1] = arr[i]; + } + arr[i + 1] = a; + } + if (jstack < 0) break; + + ir = istack[jstack--]; + l = istack[jstack--]; + } else { + k = (l + ir) >>> 1; + temp = arr[k]; + arr[k] = arr[l + 1]; + arr[l + 1] = temp; + + if (arr[l].compareTo(arr[ir]) > 0) { + temp = arr[l]; + arr[l] = arr[ir]; + arr[ir] = temp; + } + if (arr[l + 1].compareTo(arr[ir]) > 0) { + temp = arr[l + 1]; + arr[l + 1] = arr[ir]; + arr[ir] = temp; + } + if (arr[l].compareTo(arr[l + 1]) > 0) { + temp = arr[l]; + arr[l] = arr[l + 1]; + arr[l + 1] = temp; + } + i = l + 1; + j = ir; + a = arr[l + 1]; + for (; ; ) { + do { + i++; + } while (arr[i].compareTo(a) < 0); + do { + j--; + } while (arr[j].compareTo(a) > 0); + if (j < i) break; + temp = arr[i]; + arr[i] = arr[j]; + arr[j] = temp; + } + arr[l + 1] = arr[j]; + arr[j] = a; + jstack += 2; + + if (jstack >= NSTACK) + throw new RuntimeException("NSTACK too small"); + if (ir - i + 1 >= j - l) { + istack[jstack] = ir; + istack[jstack - 1] = i; + ir = j - 1; + } else { + istack[jstack] = j - 1; + istack[jstack - 1] = l; + l = i; + } + } + } + } + + public void sort( List arr, int length ) { + int i, ir, j, k; + int jstack = -1; + int l = 0; // if I ever publish a book I will never use variable l in an algorithm with lots of 1 + T a; + + ir = length - 1; + + T temp; + + for (; ; ) { + if (ir - l < M) { + for (j = l + 1; j <= ir; j++) { + a = arr.get(j); + for (i = j - 1; i >= l; i--) { + if (arr.get(i).compareTo(a) <= 0) break; + arr.set(i + 1, arr.get(i)); + } + arr.set(i + 1, a); + } + if (jstack < 0) break; + + ir = istack[jstack--]; + l = istack[jstack--]; + } else { + k = (l + ir) >>> 1; + swap(arr, k, l + 1); + + if (arr.get(l).compareTo(arr.get(ir)) > 0) { + swap(arr, l, ir); + } + if (arr.get(l + 1).compareTo(arr.get(ir)) > 0) { + swap(arr, l + 1, ir); + } + if (arr.get(l).compareTo(arr.get(l + 1)) > 0) { + swap(arr, l, l + 1); + } + i = l + 1; + j = ir; + a = arr.get(l + 1); + for (; ; ) { + do { + i++; + } while (arr.get(i).compareTo(a) < 0); + do { + j--; + } while (arr.get(j).compareTo(a) > 0); + if (j < i) break; + swap(arr, i, j); + } + arr.set(l + 1, arr.get(j)); + arr.set(j, a); + jstack += 2; + + if (jstack >= NSTACK) + throw new RuntimeException("NSTACK too small"); + if (ir - i + 1 >= j - l) { + istack[jstack] = ir; + istack[jstack - 1] = i; + ir = j - 1; + } else { + istack[jstack] = j - 1; + istack[jstack - 1] = l; + l = i; + } + } + } + } + + private static void swap( List list, int indexA, int indexB ) { + T tmp = list.get(indexA); + list.set(indexA, list.get(indexB)); + list.set(indexB, tmp); + } + + public void sort( T[] arr, int length, int[] indexes ) { + for (int i = 0; i < length; i++) { + indexes[i] = i; + } + + int i, ir, j, k; + int jstack = -1; + int l = 0; + // if I ever publish a book I will never use variable l in an algorithm with lots of 1 + + T a; + + ir = length - 1; + + int temp; + + for (; ; ) { + if (ir - l < M) { + for (j = l + 1; j <= ir; j++) { + a = arr[indexes[j]]; + temp = indexes[j]; + for (i = j - 1; i >= l; i--) { + if (arr[indexes[i]].compareTo(a) <= 0) break; + indexes[i + 1] = indexes[i]; + } + indexes[i + 1] = temp; + } + if (jstack < 0) break; + + ir = istack[jstack--]; + l = istack[jstack--]; + } else { + k = (l + ir) >>> 1; + temp = indexes[k]; + indexes[k] = indexes[l + 1]; + indexes[l + 1] = temp; + + if (arr[indexes[l]].compareTo(arr[indexes[ir]]) > 0) { + temp = indexes[l]; + indexes[l] = indexes[ir]; + indexes[ir] = temp; + } + if (arr[indexes[l + 1]].compareTo(arr[indexes[ir]]) > 0) { + temp = indexes[l + 1]; + indexes[l + 1] = indexes[ir]; + indexes[ir] = temp; + } + if (arr[indexes[l]].compareTo(arr[indexes[l + 1]]) > 0) { + temp = indexes[l]; + indexes[l] = indexes[l + 1]; + indexes[l + 1] = temp; + } + i = l + 1; + j = ir; + a = arr[indexes[l + 1]]; + for (; ; ) { + do { + i++; + } while (arr[indexes[i]].compareTo(a) < 0); + do { + j--; + } while (arr[indexes[j]].compareTo(a) > 0); + if (j < i) break; + temp = indexes[i]; + indexes[i] = indexes[j]; + indexes[j] = temp; + } + temp = indexes[l + 1]; + indexes[l + 1] = indexes[j]; + indexes[j] = temp; + jstack += 2; + + if (jstack >= NSTACK) + throw new RuntimeException("NSTACK too small"); + if (ir - i + 1 >= j - l) { + istack[jstack] = ir; + istack[jstack - 1] = i; + ir = j - 1; + } else { + istack[jstack] = j - 1; + istack[jstack - 1] = l; + l = i; + } + } + } + } +} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/sorting/QuickSortComparator.java ddogleg-0.22+ds/src/org/ddogleg/sorting/QuickSortComparator.java --- ddogleg-0.18+ds/src/org/ddogleg/sorting/QuickSortComparator.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/sorting/QuickSortComparator.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2015, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -18,7 +18,10 @@ package org.ddogleg.sorting; +import lombok.Getter; + import java.util.Comparator; +import java.util.List; /** * An implementation of the quick sort algorithm from Numerical Recipes Third Edition @@ -32,11 +35,11 @@ public class QuickSortComparator { // an architecture dependent tuning parameter private int M = 7; - private int NSTACK; + final private int NSTACK; - private int istack[]; + final private int[] istack; - Comparator comparator; + final @Getter Comparator comparator; public QuickSortComparator(Comparator comparator) { this(65,7,comparator); @@ -128,7 +131,80 @@ } } - public void sort( T[] arr , int length , int indexes[] ) + public void sort( List arr, int length ) + { + int i,ir,j,k; + int jstack = -1; + int l = 0; // if I ever publish a book I will never use variable l in an algorithm with lots of 1 + T a; + + ir=length-1; + + T temp; + + for(;;) { + if( ir-l < M) { + for( j=l+1;j<=ir;j++) { + a = arr.get(j); + for( i=j-1; i>=l;i-- ) { + if( comparator.compare(arr.get(i),a) <= 0 ) break; + arr.set(i+1,arr.get(i)); + } + arr.set(i+1,a); + } + if(jstack < 0) break; + + ir = istack[jstack--]; + l=istack[jstack--]; + + } else { + k=(l+ir)>>>1; + swap(arr,k,l+1); + + if( comparator.compare(arr.get(l), arr.get(ir)) > 0) { + swap(arr,l,ir); + } + if( comparator.compare(arr.get(l+1), arr.get(ir)) > 0) { + swap(arr,l+1,ir); + } + if( comparator.compare(arr.get(l), arr.get(l+1)) > 0) { + swap(arr,l,l+1); + } + i=l+1; + j=ir; + a=arr.get(l+1); + for(;;) { + do { i++; } while( comparator.compare(arr.get(i), a) < 0); + do { j--; } while( comparator.compare(arr.get(j), a) > 0); + if(j < i ) break; + swap(arr,i,j); + } + arr.set(l+1,arr.get(j)); + arr.set(j,a); + jstack += 2; + + if( jstack >= NSTACK ) + throw new RuntimeException("NSTACK too small"); + if( ir-i+1 >= j-l ) { + istack[jstack] = ir; + istack[jstack-1] = i; + ir=j-1; + } else { + istack[jstack] = j-1; + istack[jstack-1] = l; + l=i; + } + } + } + } + + private static void swap( List list, int indexA , int indexB ) { + T tmp = list.get(indexA); + list.set(indexA, list.get(indexB)); + list.set(indexB, tmp); + } + + public void sort( T[] arr , int length , int[] indexes ) { for( int i = 0; i < length; i++ ) { indexes[i] = i; diff -Nru ddogleg-0.18+ds/src/org/ddogleg/sorting/StraightInsertionSort.java ddogleg-0.22+ds/src/org/ddogleg/sorting/StraightInsertionSort.java --- ddogleg-0.18+ds/src/org/ddogleg/sorting/StraightInsertionSort.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/sorting/StraightInsertionSort.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2013, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -32,10 +32,10 @@ /** * Sorts data into ascending order */ + @SuppressWarnings("EmptyCatch") public static void sort( double[] data ) { int i=0,j; - final int n = data.length; double a; // by doing the ugly exception catching it was 13% faster @@ -47,9 +47,9 @@ for( i=j; data[i-1] > a;i-- ) { data[i]=data[i-1]; } - }catch( ArrayIndexOutOfBoundsException e ) {} + }catch( ArrayIndexOutOfBoundsException ignore ) {} data[i]=a; } - }catch( ArrayIndexOutOfBoundsException e ) {} + }catch( ArrayIndexOutOfBoundsException ignore ) {} } } diff -Nru ddogleg-0.18+ds/src/org/ddogleg/stats/StatisticsDogArray.java ddogleg-0.22+ds/src/org/ddogleg/stats/StatisticsDogArray.java --- ddogleg-0.18+ds/src/org/ddogleg/stats/StatisticsDogArray.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/stats/StatisticsDogArray.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.stats; + +import org.ddogleg.sorting.QuickSelect; +import org.ddogleg.struct.DogArray_F64; + +/** + * @author Peter Abeles + */ +public class StatisticsDogArray { + + public static double mean( DogArray_F64 list ) { + double total = 0; + for (int i = 0; i < list.size(); i++) { + total += list.data[i]; + } + + return total / list.size(); + } + + public static double variance( DogArray_F64 list , double mean ) { + double total = 0; + for (int i = 0; i < list.size(); i++) { + double d = list.data[i] - mean; + total += d*d; + } + + return total / (list.size()-1); + } + + public static double stdev( DogArray_F64 list , double mean ) { + return Math.sqrt(variance(list,mean)); + } + + public static double fraction( DogArray_F64 list , double fraction ) { + int k = (int)((list.size-1)*fraction+0.5); + return QuickSelect.select(list.data,k,list.size); + } +} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/stats/UtilStatisticsQueue.java ddogleg-0.22+ds/src/org/ddogleg/stats/UtilStatisticsQueue.java --- ddogleg-0.18+ds/src/org/ddogleg/stats/UtilStatisticsQueue.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/stats/UtilStatisticsQueue.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2012-2015, Peter Abeles. All Rights Reserved. - * - * This file is part of DDogleg (http://ddogleg.org). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ddogleg.stats; - -import org.ddogleg.sorting.QuickSelect; -import org.ddogleg.struct.GrowQueue_F64; - -/** - * @author Peter Abeles - */ -public class UtilStatisticsQueue { - - public static double mean( GrowQueue_F64 list ) { - double total = 0; - for (int i = 0; i < list.size(); i++) { - total += list.data[i]; - } - - return total / list.size(); - } - - public static double variance( GrowQueue_F64 list , double mean ) { - double total = 0; - for (int i = 0; i < list.size(); i++) { - double d = list.data[i] - mean; - total += d*d; - } - - return total / (list.size()-1); - } - - public static double stdev( GrowQueue_F64 list , double mean ) { - return Math.sqrt(variance(list,mean)); - } - - public static double fraction( GrowQueue_F64 list , double fraction ) { - int k = (int)((list.size-1)*fraction+0.5); - return QuickSelect.select(list.data,k,list.size); - } -} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/struct/BigDogArrayBase.java ddogleg-0.22+ds/src/org/ddogleg/struct/BigDogArrayBase.java --- ddogleg-0.18+ds/src/org/ddogleg/struct/BigDogArrayBase.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/struct/BigDogArrayBase.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,389 @@ +/* + * Copyright (c) 2012-2022, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.struct; + +import lombok.Getter; + +/** + * A growable array that is composed of internal blocks. This is intended to reduce overhead when growing a very large + * array. Contrast this with {@link DogArray} for which its entire internal array needs to be copied when growing. + * While more complex and in some classes slightly slower, the approach employed here is much more memory and + * speed efficient while growing. See {@link BigDogGrowth} for a description of different growth strategies. + * + * When operations are used which add/append to the end of the array then extra room is typically added, if a grow + * strategy is employed. This is done to avoid excessive amount of memory copy operations. + * + * @author Peter Abeles + */ +public abstract class BigDogArrayBase { + /** + * Default block size. It's assumed that this is used in fairly large arrays. + */ + public static final int DEFAULT_BLOCK_SIZE = 32768; + /** + * Storage for blocks. Note that the size is always the number of elements with non-null values. + */ + protected @Getter final FastArray blocks; + /** + * Number of elements in a full block + */ + protected @Getter final int blockSize; + /** + * Number of elements in the array being used. Know what you're doing before modifying. + */ + public @Getter int size; + + /** + * If a grow strategy is employed, this is the initial size of a block. Do not set this to be + * larger than {@link #blockSize}. + */ + private @Getter int initialBlockSize = 8; + + /** + * Approach used for growth. See enum for a description + */ + protected @Getter final BigDogGrowth growth; + + protected BigDogArrayBase( int initialAllocation, int blockSize, BigDogGrowth growth ) { + // initializing with a size of zero causes all sorts of edge cases. So just make it illegal. + if (initialAllocation <= 0) + throw new IllegalArgumentException("initialAllocation size must be a positive value"); + if (blockSize <= 0) + throw new IllegalArgumentException("Block size must be a positive value"); + this.blockSize = blockSize; + this.growth = growth; + + Class type = (Class)newArrayInstance(0).getClass(); + blocks = new FastArray<>(type, getDesiredBlocks(initialAllocation)); + blocks.size = blocks.data.length; // hack to enable the code below to work + + // Declare all full blocks + for (int i = 0; i < blocks.size - 1; i++) { + blocks.set(i, newArrayInstance(blockSize)); + } + + // The last block might be a partial block + if (blocks.size > 0) { + if ((blocks.size == 1 && growth == BigDogGrowth.GROW_FIRST) || growth == BigDogGrowth.GROW) { + blocks.set(blocks.size - 1, newArrayInstance(initialAllocation%blockSize)); + } else { + blocks.set(blocks.size - 1, newArrayInstance(blockSize)); + } + } + } + + /** + * Returns true if the specified array index is outside the allowed value range + */ + public boolean isIndexOutOfBounds( int index ) { + return index < 0 || index >= size; + } + + /** + * Sets the array size to zero. No memory is freed. + */ + public void reset() { + this.size = 0; + } + + /** + * Ensures that the internal data can store up to this number of elements before needing to allocate more memory. + * No extra data is added and this function is only recommended when the array has a known max size. + */ + public void reserve( int desiredSize ) { + allocate(desiredSize, false, false); + } + + /** + * Allocate more memory so that an array of the specified desiredSize can be stored. Optionally copy old + * values into new arrays when growing + * + * @param desiredSize New size of internal array, not just a single block. + * @param saveValues If old values should be copied. + * @param addExtra If using a grow strategy, is this a case where it should add extra elements or do the + * exact request? + */ + protected void allocate( int desiredSize, boolean saveValues, boolean addExtra ) { + if (desiredSize < 0) + throw new IllegalArgumentException("desiredSize must be positive. Overflowed? desiredSize=" + desiredSize); + + int desiredNumBlocks = getDesiredBlocks(desiredSize); + + // See if the current allocation in blocks is larger than what's requested + if (blocks.size() > desiredNumBlocks) { + return; + } + + // Add more blocks + int priorNumBlocks = blocks.size; + blocks.resize(desiredNumBlocks); + + // If the number of blocks is increasing, make sure the prior last block is the correct size + if (priorNumBlocks > 0 && priorNumBlocks < desiredNumBlocks) { + // all growth methods require the non last block to be blockSize + Array old = blocks.data[priorNumBlocks - 1]; + if (arrayLength(old) != blockSize) { + Array replacement = newArrayInstance(blockSize); + if (saveValues) + System.arraycopy(old, 0, replacement, 0, arrayLength(old)); + blocks.data[priorNumBlocks - 1] = replacement; + } + } + + // All new blocks, but the last one, must be blockSize in length + for (int i = priorNumBlocks; i < desiredNumBlocks - 1; i++) { + blocks.data[i] = newArrayInstance(blockSize); + } + + // The last block might not be a full block + int desiredLastBlockSize = computeLastBlockSize(desiredSize, desiredNumBlocks); + + if (priorNumBlocks == desiredNumBlocks && priorNumBlocks > 0) { + // If requested/allowed, increase the size the last array. This will effectively double its size. + // Adding a bit of extra when growing significantly reduces number of array copies + Array old = blocks.data[priorNumBlocks - 1]; + int oldLength = arrayLength(old); + + // See if it needs to allocate more memory + if (oldLength < desiredLastBlockSize) { + // Add extra elements to reduce how often an expensive growth operation is done + if (addExtra) + desiredLastBlockSize = Math.min(blockSize, initialBlockSize + oldLength*2 + desiredLastBlockSize); + + Array replacement = newArrayInstance(desiredLastBlockSize); + if (saveValues) + System.arraycopy(old, 0, replacement, 0, arrayLength(old)); + blocks.data[desiredNumBlocks - 1] = replacement; + } + } else if (priorNumBlocks < desiredNumBlocks) { + if (addExtra) + desiredLastBlockSize = Math.min(blockSize, initialBlockSize + 2*desiredLastBlockSize); + + blocks.data[desiredNumBlocks - 1] = newArrayInstance(desiredLastBlockSize); + } + } + + /** + * Declare an array for the last block + * + * @param desiredSize (Input) The new desired size of this array + * @return A new array + */ + private int computeLastBlockSize( int desiredSize, int numBlocks ) { + if (growth == BigDogGrowth.FIXED || (numBlocks > 1 && growth == BigDogGrowth.GROW_FIRST)) { + return blockSize; + } else { + return desiredSize%blockSize == 0 ? blockSize : desiredSize%blockSize; + } + } + + /** + * Either increased or decreases the array size. If it's increased then the new elements will be filled with + * undefined values, depending on their previous state. + * + * @param desiredSize (Input) New array size + */ + public void resize( int desiredSize ) { + allocate(desiredSize, true, false); + this.size = desiredSize; + } + + /** + * Adds the input array to the end of this array. + * + * @param array (Input) Array which is to be copied + * @param offset (Input) First element in the array which is to be copied + * @param length (Input) Number of elements which are to be copied + */ + public void append( Array array, int offset, int length ) { + // make sure enough memory has been allocated + allocate(this.size + length, true, true); + this.size = this.size + length; + setArray(this.size - length, array, offset, length); + } + + /** + * Copies the passed in array into this array at the specified location + * + * @param location (Input) First element that the array is to be inserted at + * @param array (Input) Array which is to be copied in + * @param offset (Input) Offset inside of array that it should be copied from + * @param length (Input) Number of elements in array to copy + */ + public void setArray( long location, Array array, int offset, int length ) { + // must use long to avoid overflow issues + long idx0 = location; + long idx1 = location + length; + + // First block is a special case. Fill up the block and make sure idx0 is aligned to the blocks + if (idx0%blockSize != 0) { + int blockIdx0 = (int)(idx0%blockSize); + int remainingInBlock = blockSize - blockIdx0; + int lengthInBlock = Math.min(remainingInBlock, (int)(idx1 - idx0)); + System.arraycopy(array, offset, blocks.data[(int)(idx0/blockSize)], blockIdx0, lengthInBlock); + offset += lengthInBlock; + idx0 += lengthInBlock; + } + + // idx0 should be at the start of a block now. Just mindlessly copy until it hits the end of a block + while (idx0 + blockSize <= idx1) { + System.arraycopy(array, offset, blocks.data[(int)(idx0/blockSize)], 0, blockSize); + offset += blockSize; + idx0 += blockSize; + } + + // fill in the last block + if (idx0 != idx1) { + System.arraycopy(array, offset, blocks.data[(int)(idx0/blockSize)], 0, (int)(idx1 - idx0)); + } + } + + /** + * Passes in array elements to the operator one block at a time. What's given to the operator + * is the first index in the block it should process, the last (exclusive) index in the block, + * and the number of elements offset from the original range requested. + * + * @param idx0 (Input) First index, inclusive. + * @param idx1 (Input) Last index, exclusive. + * @param op The operator which processes the values + */ + public void processByBlock( int idx0, int idx1, FunctionEachRange op ) { + int origIdx0 = idx0; + + // If not initially aligned at the block boundaries, process it until it hits the first boundary + if (idx0%blockSize != 0) { + int blockIdx0 = idx0%blockSize; + int remainingInBlock = blockSize - blockIdx0; + int lengthInBlock = Math.min(remainingInBlock, idx1 - idx0); + int blockIdx1 = blockIdx0 + lengthInBlock; + + op.process(blocks.data[idx0/blockSize], blockIdx0, blockIdx1, 0); + + idx0 += lengthInBlock; + } + + // We can now process it one block at a time + while (idx0 + blockSize < idx1) { + op.process(blocks.data[idx0/blockSize], 0, blockSize, idx0 - origIdx0); + idx0 += blockSize; + } + + // The end is another special case. It might end before a block boundary + if (idx0 != idx1) { + op.process(blocks.data[idx0/blockSize], 0, idx1 - idx0, idx0 - origIdx0); + } + } + + /** + * Returns the number of blocks needed to store an array of the specified size + */ + protected final int getDesiredBlocks( int desiredSize ) { + return desiredSize/blockSize + (desiredSize%blockSize > 0 ? 1 : 0); + } + + /** + * Performs an internal check to make sure all data structures and values are internally consistent. Used for + * debugging and paranoia. + */ + public boolean isValidStructure() { + // there has to be one block. the minimum allocation size is 1 + if (blocks.size == 0) + return false; + + // make sure the size isn't impossibly large + int maxStorage = arrayLength(blocks.getTail()) + (blocks.size - 1)*blockSize; + if (size > maxStorage) + return false; + + // Make sure internal blocks are correct size + switch (growth) { + case FIXED: { + for (int i = 0; i < blocks.size; i++) { + if (arrayLength(blocks.get(i)) != blockSize) + return false; + } + } + break; + + case GROW_FIRST: { + for (int i = 1; i < blocks.size; i++) { + if (arrayLength(blocks.get(i)) != blockSize) + return false; + } + if (blocks.size == 1) { + if (arrayLength(blocks.get(0)) > blockSize) + return false; + } else { + if (arrayLength(blocks.get(0)) != blockSize) + return false; + } + } + break; + + case GROW: { + for (int i = 0; i < blocks.size - 1; i++) { + if (arrayLength(blocks.get(i)) != blockSize) + return false; + } + if (arrayLength(blocks.getTail()) > blockSize) + return false; + } + break; + } + + return true; + } + + /** + * Returns the number of elements which have been allocated. This array size has to be less than + * or equal to this number\ + */ + public int getTotalAllocation() { + return (blocks.size - 1)*blockSize + arrayLength(blocks.data[blocks.size - 1]); + } + + protected int blockArrayLength( int block ) { + return arrayLength(blocks.get(block)); + } + + /** + * Assigns a new initial block size. The value is adjusted to ensure that it is valid. Can't be + * larger than a block or less than 1. + */ + public void setInitialBlockSize( int initialBlockSize ) { + this.initialBlockSize = Math.min(blockSize, Math.max(1, initialBlockSize)); + } + + protected abstract Array newArrayInstance( int size ); + + protected abstract int arrayLength( Array array ); + + @FunctionalInterface + public interface FunctionEachRange { + /** + * Passes in internal blocks to be processed in chunks + * + * @param block Array block to process + * @param idx0 First valid index in the array, inclusive + * @param idx1 Last index in the array, exclusive + * @param offset The number of elements offset from the first element it requested + */ + void process( Array block, int idx0, int idx1, int offset ); + } +} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/struct/BigDogArray_B.java ddogleg-0.22+ds/src/org/ddogleg/struct/BigDogArray_B.java --- ddogleg-0.18+ds/src/org/ddogleg/struct/BigDogArray_B.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/struct/BigDogArray_B.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.struct; + +import java.util.Arrays; + +/** + * Implementation of {@link BigDogArrayBase} for boolean[]. + * + * @author Peter Abeles + */ +public class BigDogArray_B extends BigDogArrayBase { + + // WARNING: Autogenerated from TupleDesc_F64. Do not modify. + + public BigDogArray_B() { + this(8, DEFAULT_BLOCK_SIZE, BigDogGrowth.GROW_FIRST); + } + + public BigDogArray_B( int initialAllocation ) { + this(initialAllocation, DEFAULT_BLOCK_SIZE, BigDogGrowth.GROW_FIRST); + } + + public BigDogArray_B( int initialAllocation, int blockSize, BigDogGrowth growth ) { + super(initialAllocation, blockSize, growth); + } + + @Override protected boolean[] newArrayInstance( int size ) { + return new boolean[size]; + } + + @Override protected int arrayLength( boolean[] array ) { + return array.length; + } + + /** + * Appends a single value to the end of the array + * + * @param value (Input) The new value which is to be added + */ + public void append( boolean value ) { + allocate(this.size + 1, true, true); + blocks.data[size/blockSize][size%blockSize] = value; + this.size++; + } + + /** + * Appends a single value to the end of the array. identical to {@link #append(boolean)}. + * + * @param value (Input) The new value which is to be added + */ + public void add( boolean value ) { + append(value); + } + + /** + * Resizes the array and fills all new elements with the specified value + * + * @param desiredSize New array size + * @param initialValue The value of new elements + */ + public void resize( int desiredSize, boolean initialValue ) { + allocate(desiredSize, true, false); + int originalSize = size; + this.size = desiredSize; + fill(originalSize, desiredSize, initialValue); + } + + /** + * Fills the elements in the specified range with the specified value. + * + * @param idx0 (Input) First index, inclusive. + * @param idx1 (Input) last index, exclusive. + * @param value (Input) Fill value + */ + public void fill( int idx0, int idx1, boolean value ) { + processByBlock(idx0, idx1, ( block, block0, block1, offset ) -> { + Arrays.fill(block, block0, block1, value); + }); + } + + /** + * Assigns an element a new value + * + * @param index (Input) Which element to modify + * @param value (Input) The element's new value + */ + public void set( int index, boolean value ) { + blocks.data[index/blockSize][index%blockSize] = value; + } + + /** + * Assigns an element a new value, counting from the end of the array. + * + * @param index (Input) Index relative to the end counting in reverse order. setTail(0, 5) = set(size-1, 5) + * @param value (Input) The element's new value + */ + public void setTail( int index, boolean value ) { + set( size - index - 1, value); + } + + /** + * Copies a sub-array into the passed in array + * + * @param index (Input) Start index in this array + * @param array (Output) destination array + * @param offset Offset from start of destination array + * @param length Number of elements to copy + */ + public void getArray( int index, boolean[] array, int offset, int length ) { + processByBlock(index, index + length, ( block, block0, block1, arrayLoc ) -> { + System.arraycopy(block, block0, array, offset + arrayLoc, block1 - block0); + }); + } + + /** + * Returns the value in the array at the specified index + * + * @param index (Input) Index in the array + * @return value at index + */ + public boolean get( int index ) { + return blocks.data[index/blockSize][index%blockSize]; + } + + /** + * Returns the value in the array at the specified index, counting from the end of the array. + * + * @param index (Input) Index relative to the end counting in reverse order. 0 = get(size-1) + */ + public boolean getTail( int index ) { + return get( size - index - 1); + } + + /** + * Simulates a for-each loop. Passes in element values to 'op' from the specified range. + * + * @param idx0 (Input) First index, inclusive. + * @param idx1 (Input) Last index, exclusive. + * @param op The operator which processes the values + */ + public void forEach( int idx0, int idx1, DogArray_B.FunctionEach op ) { + processByBlock(idx0, idx1, ( block, block0, block1, offset ) -> { + for (int i = block0; i < block1; i++) { + op.process(block[i]); + } + }); + } + + /** + * Simulates a for-each loop. Passes in array indexes and element values to 'op' from the specified range. + * + * @param idx0 (Input) First index, inclusive. + * @param idx1 (Input) Last index, exclusive. + * @param op The operator which processes the values + */ + public void forIdx( int idx0, int idx1, DogArray_B.FunctionEachIdx op ) { + processByBlock(idx0, idx1, ( block, block0, block1, offset ) -> { + int index = idx0 + offset; + for (int i = block0; i < block1; i++) { + op.process(index++, block[i]); + } + }); + } + + /** + * Simulates a for-each loop. Passes in array indexes and element values to 'op' from the specified range. + * After calling op, the array is modified by the return value + * + * @param idx0 (Input) First index, inclusive. + * @param idx1 (Input) Last index, exclusive. + * @param op The operator which processes the values + */ + public void applyIdx( int idx0, int idx1, DogArray_B.FunctionApplyIdx op ) { + processByBlock(idx0, idx1, ( block, block0, block1, offset ) -> { + int index = idx0 + offset; + for (int i = block0; i < block1; i++) { + block[i] = op.process(index++, block[i]); + } + }); + } +} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/struct/BigDogArray_F32.java ddogleg-0.22+ds/src/org/ddogleg/struct/BigDogArray_F32.java --- ddogleg-0.18+ds/src/org/ddogleg/struct/BigDogArray_F32.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/struct/BigDogArray_F32.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.struct; + +import java.util.Arrays; + +/** + * Implementation of {@link BigDogArrayBase} for float[]. + * + * @author Peter Abeles + */ +public class BigDogArray_F32 extends BigDogArrayBase { + + // WARNING: Autogenerated from TupleDesc_F64. Do not modify. + + public BigDogArray_F32() { + this(8, DEFAULT_BLOCK_SIZE, BigDogGrowth.GROW_FIRST); + } + + public BigDogArray_F32( int initialAllocation ) { + this(initialAllocation, DEFAULT_BLOCK_SIZE, BigDogGrowth.GROW_FIRST); + } + + public BigDogArray_F32( int initialAllocation, int blockSize, BigDogGrowth growth ) { + super(initialAllocation, blockSize, growth); + } + + @Override protected float[] newArrayInstance( int size ) { + return new float[size]; + } + + @Override protected int arrayLength( float[] array ) { + return array.length; + } + + /** + * Appends a single value to the end of the array + * + * @param value (Input) The new value which is to be added + */ + public void append( float value ) { + allocate(this.size + 1, true, true); + blocks.data[size/blockSize][size%blockSize] = value; + this.size++; + } + + /** + * Appends a single value to the end of the array. identical to {@link #append(float)}. + * + * @param value (Input) The new value which is to be added + */ + public void add( float value ) { + append(value); + } + + /** + * Resizes the array and fills all new elements with the specified value + * + * @param desiredSize New array size + * @param initialValue The value of new elements + */ + public void resize( int desiredSize, float initialValue ) { + allocate(desiredSize, true, false); + int originalSize = size; + this.size = desiredSize; + fill(originalSize, desiredSize, initialValue); + } + + /** + * Fills the elements in the specified range with the specified value. + * + * @param idx0 (Input) First index, inclusive. + * @param idx1 (Input) last index, exclusive. + * @param value (Input) Fill value + */ + public void fill( int idx0, int idx1, float value ) { + processByBlock(idx0, idx1, ( block, block0, block1, offset ) -> { + Arrays.fill(block, block0, block1, value); + }); + } + + /** + * Assigns an element a new value + * + * @param index (Input) Which element to modify + * @param value (Input) The element's new value + */ + public void set( int index, float value ) { + blocks.data[index/blockSize][index%blockSize] = value; + } + + /** + * Assigns an element a new value, counting from the end of the array. + * + * @param index (Input) Index relative to the end counting in reverse order. setTail(0, 5) = set(size-1, 5) + * @param value (Input) The element's new value + */ + public void setTail( int index, float value ) { + set( size - index - 1, value); + } + + /** + * Copies a sub-array into the passed in array + * + * @param index (Input) Start index in this array + * @param array (Output) destination array + * @param offset Offset from start of destination array + * @param length Number of elements to copy + */ + public void getArray( int index, float[] array, int offset, int length ) { + processByBlock(index, index + length, ( block, block0, block1, arrayLoc ) -> { + System.arraycopy(block, block0, array, offset + arrayLoc, block1 - block0); + }); + } + + /** + * Returns the value in the array at the specified index + * + * @param index (Input) Index in the array + * @return value at index + */ + public float get( int index ) { + return blocks.data[index/blockSize][index%blockSize]; + } + + /** + * Returns the value in the array at the specified index, counting from the end of the array. + * + * @param index (Input) Index relative to the end counting in reverse order. 0 = get(size-1) + */ + public float getTail( int index ) { + return get( size - index - 1); + } + + /** + * Simulates a for-each loop. Passes in element values to 'op' from the specified range. + * + * @param idx0 (Input) First index, inclusive. + * @param idx1 (Input) Last index, exclusive. + * @param op The operator which processes the values + */ + public void forEach( int idx0, int idx1, DogArray_F32.FunctionEach op ) { + processByBlock(idx0, idx1, ( block, block0, block1, offset ) -> { + for (int i = block0; i < block1; i++) { + op.process(block[i]); + } + }); + } + + /** + * Simulates a for-each loop. Passes in array indexes and element values to 'op' from the specified range. + * + * @param idx0 (Input) First index, inclusive. + * @param idx1 (Input) Last index, exclusive. + * @param op The operator which processes the values + */ + public void forIdx( int idx0, int idx1, DogArray_F32.FunctionEachIdx op ) { + processByBlock(idx0, idx1, ( block, block0, block1, offset ) -> { + int index = idx0 + offset; + for (int i = block0; i < block1; i++) { + op.process(index++, block[i]); + } + }); + } + + /** + * Simulates a for-each loop. Passes in array indexes and element values to 'op' from the specified range. + * After calling op, the array is modified by the return value + * + * @param idx0 (Input) First index, inclusive. + * @param idx1 (Input) Last index, exclusive. + * @param op The operator which processes the values + */ + public void applyIdx( int idx0, int idx1, DogArray_F32.FunctionApplyIdx op ) { + processByBlock(idx0, idx1, ( block, block0, block1, offset ) -> { + int index = idx0 + offset; + for (int i = block0; i < block1; i++) { + block[i] = op.process(index++, block[i]); + } + }); + } +} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/struct/BigDogArray_F64.java ddogleg-0.22+ds/src/org/ddogleg/struct/BigDogArray_F64.java --- ddogleg-0.18+ds/src/org/ddogleg/struct/BigDogArray_F64.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/struct/BigDogArray_F64.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.struct; + +import java.util.Arrays; + +/** + * Implementation of {@link BigDogArrayBase} for double[]. + * + * @author Peter Abeles + */ +public class BigDogArray_F64 extends BigDogArrayBase { + + public BigDogArray_F64() { + this(8, DEFAULT_BLOCK_SIZE, BigDogGrowth.GROW_FIRST); + } + + public BigDogArray_F64( int initialAllocation ) { + this(initialAllocation, DEFAULT_BLOCK_SIZE, BigDogGrowth.GROW_FIRST); + } + + public BigDogArray_F64( int initialAllocation, int blockSize, BigDogGrowth growth ) { + super(initialAllocation, blockSize, growth); + } + + @Override protected double[] newArrayInstance( int size ) { + return new double[size]; + } + + @Override protected int arrayLength( double[] array ) { + return array.length; + } + + /** + * Appends a single value to the end of the array + * + * @param value (Input) The new value which is to be added + */ + public void append( double value ) { + allocate(this.size + 1, true, true); + blocks.data[size/blockSize][size%blockSize] = value; + this.size++; + } + + /** + * Appends a single value to the end of the array. identical to {@link #append(double)}. + * + * @param value (Input) The new value which is to be added + */ + public void add( double value ) { + append(value); + } + + /** + * Resizes the array and fills all new elements with the specified value + * + * @param desiredSize New array size + * @param initialValue The value of new elements + */ + public void resize( int desiredSize, double initialValue ) { + allocate(desiredSize, true, false); + int originalSize = size; + this.size = desiredSize; + fill(originalSize, desiredSize, initialValue); + } + + /** + * Fills the elements in the specified range with the specified value. + * + * @param idx0 (Input) First index, inclusive. + * @param idx1 (Input) last index, exclusive. + * @param value (Input) Fill value + */ + public void fill( int idx0, int idx1, double value ) { + processByBlock(idx0, idx1, ( block, block0, block1, offset ) -> { + Arrays.fill(block, block0, block1, value); + }); + } + + /** + * Assigns an element a new value + * + * @param index (Input) Which element to modify + * @param value (Input) The element's new value + */ + public void set( int index, double value ) { + blocks.data[index/blockSize][index%blockSize] = value; + } + + /** + * Assigns an element a new value, counting from the end of the array. + * + * @param index (Input) Index relative to the end counting in reverse order. setTail(0, 5) = set(size-1, 5) + * @param value (Input) The element's new value + */ + public void setTail( int index, double value ) { + set( size - index - 1, value); + } + + /** + * Copies a sub-array into the passed in array + * + * @param index (Input) Start index in this array + * @param array (Output) destination array + * @param offset Offset from start of destination array + * @param length Number of elements to copy + */ + public void getArray( int index, double[] array, int offset, int length ) { + processByBlock(index, index + length, ( block, block0, block1, arrayLoc ) -> { + System.arraycopy(block, block0, array, offset + arrayLoc, block1 - block0); + }); + } + + /** + * Returns the value in the array at the specified index + * + * @param index (Input) Index in the array + * @return value at index + */ + public double get( int index ) { + return blocks.data[index/blockSize][index%blockSize]; + } + + /** + * Returns the value in the array at the specified index, counting from the end of the array. + * + * @param index (Input) Index relative to the end counting in reverse order. 0 = get(size-1) + */ + public double getTail( int index ) { + return get( size - index - 1); + } + + /** + * Simulates a for-each loop. Passes in element values to 'op' from the specified range. + * + * @param idx0 (Input) First index, inclusive. + * @param idx1 (Input) Last index, exclusive. + * @param op The operator which processes the values + */ + public void forEach( int idx0, int idx1, DogArray_F64.FunctionEach op ) { + processByBlock(idx0, idx1, ( block, block0, block1, offset ) -> { + for (int i = block0; i < block1; i++) { + op.process(block[i]); + } + }); + } + + /** + * Simulates a for-each loop. Passes in array indexes and element values to 'op' from the specified range. + * + * @param idx0 (Input) First index, inclusive. + * @param idx1 (Input) Last index, exclusive. + * @param op The operator which processes the values + */ + public void forIdx( int idx0, int idx1, DogArray_F64.FunctionEachIdx op ) { + processByBlock(idx0, idx1, ( block, block0, block1, offset ) -> { + int index = idx0 + offset; + for (int i = block0; i < block1; i++) { + op.process(index++, block[i]); + } + }); + } + + /** + * Simulates a for-each loop. Passes in array indexes and element values to 'op' from the specified range. + * After calling op, the array is modified by the return value + * + * @param idx0 (Input) First index, inclusive. + * @param idx1 (Input) Last index, exclusive. + * @param op The operator which processes the values + */ + public void applyIdx( int idx0, int idx1, DogArray_F64.FunctionApplyIdx op ) { + processByBlock(idx0, idx1, ( block, block0, block1, offset ) -> { + int index = idx0 + offset; + for (int i = block0; i < block1; i++) { + block[i] = op.process(index++, block[i]); + } + }); + } +} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/struct/BigDogArray_I32.java ddogleg-0.22+ds/src/org/ddogleg/struct/BigDogArray_I32.java --- ddogleg-0.18+ds/src/org/ddogleg/struct/BigDogArray_I32.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/struct/BigDogArray_I32.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.struct; + +import java.util.Arrays; + +/** + * Implementation of {@link BigDogArrayBase} for int[]. + * + * @author Peter Abeles + */ +public class BigDogArray_I32 extends BigDogArrayBase { + + // WARNING: Autogenerated from TupleDesc_F64. Do not modify. + + public BigDogArray_I32() { + this(8, DEFAULT_BLOCK_SIZE, BigDogGrowth.GROW_FIRST); + } + + public BigDogArray_I32( int initialAllocation ) { + this(initialAllocation, DEFAULT_BLOCK_SIZE, BigDogGrowth.GROW_FIRST); + } + + public BigDogArray_I32( int initialAllocation, int blockSize, BigDogGrowth growth ) { + super(initialAllocation, blockSize, growth); + } + + @Override protected int[] newArrayInstance( int size ) { + return new int[size]; + } + + @Override protected int arrayLength( int[] array ) { + return array.length; + } + + /** + * Appends a single value to the end of the array + * + * @param value (Input) The new value which is to be added + */ + public void append( int value ) { + allocate(this.size + 1, true, true); + blocks.data[size/blockSize][size%blockSize] = value; + this.size++; + } + + /** + * Appends a single value to the end of the array. identical to {@link #append(int)}. + * + * @param value (Input) The new value which is to be added + */ + public void add( int value ) { + append(value); + } + + /** + * Resizes the array and fills all new elements with the specified value + * + * @param desiredSize New array size + * @param initialValue The value of new elements + */ + public void resize( int desiredSize, int initialValue ) { + allocate(desiredSize, true, false); + int originalSize = size; + this.size = desiredSize; + fill(originalSize, desiredSize, initialValue); + } + + /** + * Fills the elements in the specified range with the specified value. + * + * @param idx0 (Input) First index, inclusive. + * @param idx1 (Input) last index, exclusive. + * @param value (Input) Fill value + */ + public void fill( int idx0, int idx1, int value ) { + processByBlock(idx0, idx1, ( block, block0, block1, offset ) -> { + Arrays.fill(block, block0, block1, value); + }); + } + + /** + * Assigns an element a new value + * + * @param index (Input) Which element to modify + * @param value (Input) The element's new value + */ + public void set( int index, int value ) { + blocks.data[index/blockSize][index%blockSize] = value; + } + + /** + * Assigns an element a new value, counting from the end of the array. + * + * @param index (Input) Index relative to the end counting in reverse order. setTail(0, 5) = set(size-1, 5) + * @param value (Input) The element's new value + */ + public void setTail( int index, int value ) { + set( size - index - 1, value); + } + + /** + * Copies a sub-array into the passed in array + * + * @param index (Input) Start index in this array + * @param array (Output) destination array + * @param offset Offset from start of destination array + * @param length Number of elements to copy + */ + public void getArray( int index, int[] array, int offset, int length ) { + processByBlock(index, index + length, ( block, block0, block1, arrayLoc ) -> { + System.arraycopy(block, block0, array, offset + arrayLoc, block1 - block0); + }); + } + + /** + * Returns the value in the array at the specified index + * + * @param index (Input) Index in the array + * @return value at index + */ + public int get( int index ) { + return blocks.data[index/blockSize][index%blockSize]; + } + + /** + * Returns the value in the array at the specified index, counting from the end of the array. + * + * @param index (Input) Index relative to the end counting in reverse order. 0 = get(size-1) + */ + public int getTail( int index ) { + return get( size - index - 1); + } + + /** + * Simulates a for-each loop. Passes in element values to 'op' from the specified range. + * + * @param idx0 (Input) First index, inclusive. + * @param idx1 (Input) Last index, exclusive. + * @param op The operator which processes the values + */ + public void forEach( int idx0, int idx1, DogArray_I32.FunctionEach op ) { + processByBlock(idx0, idx1, ( block, block0, block1, offset ) -> { + for (int i = block0; i < block1; i++) { + op.process(block[i]); + } + }); + } + + /** + * Simulates a for-each loop. Passes in array indexes and element values to 'op' from the specified range. + * + * @param idx0 (Input) First index, inclusive. + * @param idx1 (Input) Last index, exclusive. + * @param op The operator which processes the values + */ + public void forIdx( int idx0, int idx1, DogArray_I32.FunctionEachIdx op ) { + processByBlock(idx0, idx1, ( block, block0, block1, offset ) -> { + int index = idx0 + offset; + for (int i = block0; i < block1; i++) { + op.process(index++, block[i]); + } + }); + } + + /** + * Simulates a for-each loop. Passes in array indexes and element values to 'op' from the specified range. + * After calling op, the array is modified by the return value + * + * @param idx0 (Input) First index, inclusive. + * @param idx1 (Input) Last index, exclusive. + * @param op The operator which processes the values + */ + public void applyIdx( int idx0, int idx1, DogArray_I32.FunctionApplyIdx op ) { + processByBlock(idx0, idx1, ( block, block0, block1, offset ) -> { + int index = idx0 + offset; + for (int i = block0; i < block1; i++) { + block[i] = op.process(index++, block[i]); + } + }); + } +} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/struct/BigDogArray_I64.java ddogleg-0.22+ds/src/org/ddogleg/struct/BigDogArray_I64.java --- ddogleg-0.18+ds/src/org/ddogleg/struct/BigDogArray_I64.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/struct/BigDogArray_I64.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.struct; + +import java.util.Arrays; + +/** + * Implementation of {@link BigDogArrayBase} for long[]. + * + * @author Peter Abeles + */ +public class BigDogArray_I64 extends BigDogArrayBase { + + // WARNING: Autogenerated from TupleDesc_F64. Do not modify. + + public BigDogArray_I64() { + this(8, DEFAULT_BLOCK_SIZE, BigDogGrowth.GROW_FIRST); + } + + public BigDogArray_I64( int initialAllocation ) { + this(initialAllocation, DEFAULT_BLOCK_SIZE, BigDogGrowth.GROW_FIRST); + } + + public BigDogArray_I64( int initialAllocation, int blockSize, BigDogGrowth growth ) { + super(initialAllocation, blockSize, growth); + } + + @Override protected long[] newArrayInstance( int size ) { + return new long[size]; + } + + @Override protected int arrayLength( long[] array ) { + return array.length; + } + + /** + * Appends a single value to the end of the array + * + * @param value (Input) The new value which is to be added + */ + public void append( long value ) { + allocate(this.size + 1, true, true); + blocks.data[size/blockSize][size%blockSize] = value; + this.size++; + } + + /** + * Appends a single value to the end of the array. identical to {@link #append(long)}. + * + * @param value (Input) The new value which is to be added + */ + public void add( long value ) { + append(value); + } + + /** + * Resizes the array and fills all new elements with the specified value + * + * @param desiredSize New array size + * @param initialValue The value of new elements + */ + public void resize( int desiredSize, long initialValue ) { + allocate(desiredSize, true, false); + int originalSize = size; + this.size = desiredSize; + fill(originalSize, desiredSize, initialValue); + } + + /** + * Fills the elements in the specified range with the specified value. + * + * @param idx0 (Input) First index, inclusive. + * @param idx1 (Input) last index, exclusive. + * @param value (Input) Fill value + */ + public void fill( int idx0, int idx1, long value ) { + processByBlock(idx0, idx1, ( block, block0, block1, offset ) -> { + Arrays.fill(block, block0, block1, value); + }); + } + + /** + * Assigns an element a new value + * + * @param index (Input) Which element to modify + * @param value (Input) The element's new value + */ + public void set( int index, long value ) { + blocks.data[index/blockSize][index%blockSize] = value; + } + + /** + * Assigns an element a new value, counting from the end of the array. + * + * @param index (Input) Index relative to the end counting in reverse order. setTail(0, 5) = set(size-1, 5) + * @param value (Input) The element's new value + */ + public void setTail( int index, long value ) { + set( size - index - 1, value); + } + + /** + * Copies a sub-array into the passed in array + * + * @param index (Input) Start index in this array + * @param array (Output) destination array + * @param offset Offset from start of destination array + * @param length Number of elements to copy + */ + public void getArray( int index, long[] array, int offset, int length ) { + processByBlock(index, index + length, ( block, block0, block1, arrayLoc ) -> { + System.arraycopy(block, block0, array, offset + arrayLoc, block1 - block0); + }); + } + + /** + * Returns the value in the array at the specified index + * + * @param index (Input) Index in the array + * @return value at index + */ + public long get( int index ) { + return blocks.data[index/blockSize][index%blockSize]; + } + + /** + * Returns the value in the array at the specified index, counting from the end of the array. + * + * @param index (Input) Index relative to the end counting in reverse order. 0 = get(size-1) + */ + public long getTail( int index ) { + return get( size - index - 1); + } + + /** + * Simulates a for-each loop. Passes in element values to 'op' from the specified range. + * + * @param idx0 (Input) First index, inclusive. + * @param idx1 (Input) Last index, exclusive. + * @param op The operator which processes the values + */ + public void forEach( int idx0, int idx1, DogArray_I64.FunctionEach op ) { + processByBlock(idx0, idx1, ( block, block0, block1, offset ) -> { + for (int i = block0; i < block1; i++) { + op.process(block[i]); + } + }); + } + + /** + * Simulates a for-each loop. Passes in array indexes and element values to 'op' from the specified range. + * + * @param idx0 (Input) First index, inclusive. + * @param idx1 (Input) Last index, exclusive. + * @param op The operator which processes the values + */ + public void forIdx( int idx0, int idx1, DogArray_I64.FunctionEachIdx op ) { + processByBlock(idx0, idx1, ( block, block0, block1, offset ) -> { + int index = idx0 + offset; + for (int i = block0; i < block1; i++) { + op.process(index++, block[i]); + } + }); + } + + /** + * Simulates a for-each loop. Passes in array indexes and element values to 'op' from the specified range. + * After calling op, the array is modified by the return value + * + * @param idx0 (Input) First index, inclusive. + * @param idx1 (Input) Last index, exclusive. + * @param op The operator which processes the values + */ + public void applyIdx( int idx0, int idx1, DogArray_I64.FunctionApplyIdx op ) { + processByBlock(idx0, idx1, ( block, block0, block1, offset ) -> { + int index = idx0 + offset; + for (int i = block0; i < block1; i++) { + block[i] = op.process(index++, block[i]); + } + }); + } +} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/struct/BigDogArray_I8.java ddogleg-0.22+ds/src/org/ddogleg/struct/BigDogArray_I8.java --- ddogleg-0.18+ds/src/org/ddogleg/struct/BigDogArray_I8.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/struct/BigDogArray_I8.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.struct; + +import java.util.Arrays; + +/** + * Implementation of {@link BigDogArrayBase} for byte[]. + * + * @author Peter Abeles + */ +public class BigDogArray_I8 extends BigDogArrayBase { + + // WARNING: Autogenerated from TupleDesc_F64. Do not modify. + + public BigDogArray_I8() { + this(8, DEFAULT_BLOCK_SIZE, BigDogGrowth.GROW_FIRST); + } + + public BigDogArray_I8( int initialAllocation ) { + this(initialAllocation, DEFAULT_BLOCK_SIZE, BigDogGrowth.GROW_FIRST); + } + + public BigDogArray_I8( int initialAllocation, int blockSize, BigDogGrowth growth ) { + super(initialAllocation, blockSize, growth); + } + + @Override protected byte[] newArrayInstance( int size ) { + return new byte[size]; + } + + @Override protected int arrayLength( byte[] array ) { + return array.length; + } + + /** + * Appends a single value to the end of the array + * + * @param value (Input) The new value which is to be added + */ + public void append( byte value ) { + allocate(this.size + 1, true, true); + blocks.data[size/blockSize][size%blockSize] = value; + this.size++; + } + + /** + * Appends a single value to the end of the array. identical to {@link #append(byte)}. + * + * @param value (Input) The new value which is to be added + */ + public void add( byte value ) { + append(value); + } + + /** + * Resizes the array and fills all new elements with the specified value + * + * @param desiredSize New array size + * @param initialValue The value of new elements + */ + public void resize( int desiredSize, byte initialValue ) { + allocate(desiredSize, true, false); + int originalSize = size; + this.size = desiredSize; + fill(originalSize, desiredSize, initialValue); + } + + /** + * Fills the elements in the specified range with the specified value. + * + * @param idx0 (Input) First index, inclusive. + * @param idx1 (Input) last index, exclusive. + * @param value (Input) Fill value + */ + public void fill( int idx0, int idx1, byte value ) { + processByBlock(idx0, idx1, ( block, block0, block1, offset ) -> { + Arrays.fill(block, block0, block1, value); + }); + } + + /** + * Assigns an element a new value + * + * @param index (Input) Which element to modify + * @param value (Input) The element's new value + */ + public void set( int index, byte value ) { + blocks.data[index/blockSize][index%blockSize] = value; + } + + /** + * Assigns an element a new value, counting from the end of the array. + * + * @param index (Input) Index relative to the end counting in reverse order. setTail(0, 5) = set(size-1, 5) + * @param value (Input) The element's new value + */ + public void setTail( int index, byte value ) { + set( size - index - 1, value); + } + + /** + * Copies a sub-array into the passed in array + * + * @param index (Input) Start index in this array + * @param array (Output) destination array + * @param offset Offset from start of destination array + * @param length Number of elements to copy + */ + public void getArray( int index, byte[] array, int offset, int length ) { + processByBlock(index, index + length, ( block, block0, block1, arrayLoc ) -> { + System.arraycopy(block, block0, array, offset + arrayLoc, block1 - block0); + }); + } + + /** + * Returns the value in the array at the specified index + * + * @param index (Input) Index in the array + * @return value at index + */ + public byte get( int index ) { + return blocks.data[index/blockSize][index%blockSize]; + } + + /** + * Returns the value in the array at the specified index, counting from the end of the array. + * + * @param index (Input) Index relative to the end counting in reverse order. 0 = get(size-1) + */ + public byte getTail( int index ) { + return get( size - index - 1); + } + + /** + * Simulates a for-each loop. Passes in element values to 'op' from the specified range. + * + * @param idx0 (Input) First index, inclusive. + * @param idx1 (Input) Last index, exclusive. + * @param op The operator which processes the values + */ + public void forEach( int idx0, int idx1, DogArray_I8.FunctionEach op ) { + processByBlock(idx0, idx1, ( block, block0, block1, offset ) -> { + for (int i = block0; i < block1; i++) { + op.process(block[i]); + } + }); + } + + /** + * Simulates a for-each loop. Passes in array indexes and element values to 'op' from the specified range. + * + * @param idx0 (Input) First index, inclusive. + * @param idx1 (Input) Last index, exclusive. + * @param op The operator which processes the values + */ + public void forIdx( int idx0, int idx1, DogArray_I8.FunctionEachIdx op ) { + processByBlock(idx0, idx1, ( block, block0, block1, offset ) -> { + int index = idx0 + offset; + for (int i = block0; i < block1; i++) { + op.process(index++, block[i]); + } + }); + } + + /** + * Simulates a for-each loop. Passes in array indexes and element values to 'op' from the specified range. + * After calling op, the array is modified by the return value + * + * @param idx0 (Input) First index, inclusive. + * @param idx1 (Input) Last index, exclusive. + * @param op The operator which processes the values + */ + public void applyIdx( int idx0, int idx1, DogArray_I8.FunctionApplyIdx op ) { + processByBlock(idx0, idx1, ( block, block0, block1, offset ) -> { + int index = idx0 + offset; + for (int i = block0; i < block1; i++) { + block[i] = op.process(index++, block[i]); + } + }); + } +} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/struct/BigDogGrowth.java ddogleg-0.22+ds/src/org/ddogleg/struct/BigDogGrowth.java --- ddogleg-0.18+ds/src/org/ddogleg/struct/BigDogGrowth.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/struct/BigDogGrowth.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.struct; + +/** + * Specifies which strategy is used to select internal array size when adding new blocks. If a fixed sized + * block is always used this can be memory inefficient, but is faster as array creation and copy can be avoided. + */ +public enum BigDogGrowth { + /** + * Blocks always have a fixed size. + */ + FIXED, + /** + * The first block will grow as needed but all blocks later on are fixed. The idea being that initially + * a block can be much bigger than needed initially, but after the first block the amount of memory wasted + * is relatively small. + */ + GROW_FIRST, + /** + * Every new block will start out as small as possible. Most memory efficient approach but will be + * wasteful as smaller arrays will be continuously created and copied as the array grows. + */ + GROW +} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/struct/CircularArray_F32.java ddogleg-0.22+ds/src/org/ddogleg/struct/CircularArray_F32.java --- ddogleg-0.18+ds/src/org/ddogleg/struct/CircularArray_F32.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/struct/CircularArray_F32.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.struct; + +/** + * A circular queue which can grow as needed. + * + * @author Peter Abeles + */ +public class CircularArray_F32 { + // + public float[] data; + + // index which is the start of the queue + public int start; + // number of elements in the queue + public int size; + + public CircularArray_F32() { + this(10); + } + + public CircularArray_F32( int dataSize ) { + data = new float[dataSize]; + } + + public void reset() { + start = size = 0; + } + + /** + * Returns and removes the first element from the queue. + * @return first element in the queue + */ + public float popHead() { + float r = data[start]; + removeHead(); + return r; + } + + /** + * Returns and removes the last element from the queue. + * @return last element in the queue + */ + public float popTail() { + float r = tail(); + removeTail(); + return r; + } + + /** + * Value of the first element in the queue + */ + public float head() { + return data[start]; + } + + /** + * Value of the last element in the queue + */ + public float tail() { + return data[(start+size-1)%data.length]; + } + + /** + * Removes the first element + */ + public void removeHead() { + start = (start+1)%data.length; + size--; + } + + /** + * Removes the last element + */ + public void removeTail() { + size--; + } + + /** + * Returns the element in the queue at index. No bounds check is performed and a garbage value might be returned. + * @param index Which element in the queue you wish to access + * @return the element's value + */ + public float get( int index ) { + return data[(start+index)%data.length]; + } + + /** + * Adds a new element to the queue, but if the queue is full write over the oldest element. + * + * @param value Value which is to be added + */ + public void add( float value ) { + // see if it needs to grow the queue + if( size >= data.length) { + data[start] = value; + start = (start+1)%data.length; + } else { + data[(start+size)%data.length] = value; + size++; + } + } + + public void set( CircularArray_F32 original ) { + if( this.data.length != original.data.length) { + this.data = new float[original.data.length]; + } + System.arraycopy(original.data,0,this.data,0,this.data.length); + this.size = original.size; + this.start = original.start; + } + + public CircularArray_F32 copy() { + CircularArray_F32 r = new CircularArray_F32(); + r.set(this); + return r; + } + + public void resizeQueue( int maxSize ) { + if( this.data.length != maxSize) { + this.data = new float[maxSize]; + } + } + + public int queueSize() { + return data.length; + } + + public int size() { + return size; + } + + public boolean isEmpty() { + return size == 0; + } + + public boolean isFull(){ return size == data.length;} +} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/struct/CircularArray_F64.java ddogleg-0.22+ds/src/org/ddogleg/struct/CircularArray_F64.java --- ddogleg-0.18+ds/src/org/ddogleg/struct/CircularArray_F64.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/struct/CircularArray_F64.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.struct; + +/** + * A circular queue which can grow as needed. + * + * @author Peter Abeles + */ +public class CircularArray_F64 { + // + public double[] data; + + // index which is the start of the queue + public int start; + // number of elements in the queue + public int size; + + public CircularArray_F64() { + this(10); + } + + public CircularArray_F64( int dataSize ) { + data = new double[dataSize]; + } + + public void reset() { + start = size = 0; + } + + /** + * Returns and removes the first element from the queue. + * @return first element in the queue + */ + public double popHead() { + double r = data[start]; + removeHead(); + return r; + } + + /** + * Returns and removes the last element from the queue. + * @return last element in the queue + */ + public double popTail() { + double r = tail(); + removeTail(); + return r; + } + + /** + * Value of the first element in the queue + */ + public double head() { + return data[start]; + } + + /** + * Value of the last element in the queue + */ + public double tail() { + return data[(start+size-1)%data.length]; + } + + /** + * Removes the first element + */ + public void removeHead() { + start = (start+1)%data.length; + size--; + } + + /** + * Removes the last element + */ + public void removeTail() { + size--; + } + + /** + * Returns the element in the queue at index. No bounds check is performed and a garbage value might be returned. + * @param index Which element in the queue you wish to access + * @return the element's value + */ + public double get( int index ) { + return data[(start+index)%data.length]; + } + + /** + * Adds a new element to the queue, but if the queue is full write over the oldest element. + * + * @param value Value which is to be added + */ + public void add( double value ) { + // see if it needs to grow the queue + if( size >= data.length) { + data[start] = value; + start = (start+1)%data.length; + } else { + data[(start+size)%data.length] = value; + size++; + } + } + + public void set( CircularArray_F64 original ) { + if( this.data.length != original.data.length) { + this.data = new double[original.data.length]; + } + System.arraycopy(original.data,0,this.data,0,this.data.length); + this.size = original.size; + this.start = original.start; + } + + public CircularArray_F64 copy() { + CircularArray_F64 r = new CircularArray_F64(); + r.set(this); + return r; + } + + public void resizeQueue( int maxSize ) { + if( this.data.length != maxSize) { + this.data = new double[maxSize]; + } + } + + public int queueSize() { + return data.length; + } + + public int size() { + return size; + } + + public boolean isEmpty() { + return size == 0; + } + + public boolean isFull(){ return size == data.length;} +} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/struct/CircularArray_I32.java ddogleg-0.22+ds/src/org/ddogleg/struct/CircularArray_I32.java --- ddogleg-0.18+ds/src/org/ddogleg/struct/CircularArray_I32.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/struct/CircularArray_I32.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.struct; + +/** + * A circular queue which can grow as needed. + * + * @author Peter Abeles + */ +public class CircularArray_I32 { + // + public int data[]; + + // index which is the start of the queue + public int start; + // number of elements in the queue + public int size; + + public CircularArray_I32() { + this(10); + } + + public CircularArray_I32( int dataSize ) { + data = new int[dataSize]; + } + + public void reset() { + start = size = 0; + } + + /** + * Returns and removes the first element from the queue. + * @return first element in the queue + */ + public int popHead() { + int r = data[start]; + removeHead(); + return r; + } + + /** + * Returns and removes the last element from the queue. + * @return last element in the queue + */ + public int popTail() { + int r = tail(); + removeTail(); + return r; + } + + /** + * Value of the first element in the queue + */ + public int head() { + return data[start]; + } + + /** + * Value of the last element in the queue + */ + public int tail() { + return data[(start+size-1)%data.length]; + } + + /** + * Removes the first element + */ + public void removeHead() { + start = (start+1)%data.length; + size--; + } + + /** + * Removes the last element + */ + public void removeTail() { + size--; + } + + /** + * Returns the element in the queue at index. No bounds check is performed and a garbage value might be returned. + * @param index Which element in the queue you wish to access + * @return the element's value + */ + public int get( int index ) { + return data[(start+index)%data.length]; + } + + /** + * Adds a new element to the queue. If the queue isn't large enough to store this value then its internal data + * array will grow + * @param value Value which is to be added + */ + public void add( int value ) { + // see if it needs to grow the queue + if( size >= data.length) { + int a[] = new int[ nextDataSize() ]; + + System.arraycopy(data,start,a,0,data.length-start); + System.arraycopy(data,0,a,data.length-start,start); + start = 0; + data = a; + } + data[(start+size)%data.length] = value; + size++; + } + + /** + * Adds a new element to the queue, but if the queue is full write over the oldest element. + * + * @param value Value which is to be added + */ + public void addW( int value ) { + // see if it needs to grow the queue + if( size >= data.length) { + data[start] = value; + start = (start+1)%data.length; + } else { + data[(start+size)%data.length] = value; + size++; + } + } + + private int nextDataSize() { + if( data.length < 1000 ) + return data.length*2; + else if( data.length < 10000 ) + return data.length*3/2; + else + return data.length*6/5; + } + + public int size() { + return size; + } + + public boolean isEmpty() { + return size == 0; + } + + public boolean isFull(){ return size == data.length;} +} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/struct/CircularArray.java ddogleg-0.22+ds/src/org/ddogleg/struct/CircularArray.java --- ddogleg-0.18+ds/src/org/ddogleg/struct/CircularArray.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/struct/CircularArray.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.struct; + +import java.lang.reflect.Array; + +/** + * A circular queue which can grow as needed. + * + * @author Peter Abeles + */ +public class CircularArray { + // inner data array + public T[] data; + + // index which is the start of the queue + public int start; + // number of elements in the queue + public int size; + + Class type; + + public CircularArray( Class type ) { + this(type,10); + } + + public CircularArray( Class type , int maxSize) { + this.type = type; + data = (T[])Array.newInstance(type,maxSize); + } + + public void reset() { + start = size = 0; + } + + /** + * Returns and removes the first element from the queue. + * @return first element in the queue + */ + public T popHead() { + T r = data[start]; + removeHead(); + return r; + } + + /** + * Returns and removes the last element from the queue. + * @return last element in the queue + */ + public T popTail() { + T r = tail(); + removeTail(); + return r; + } + + /** + * Value of the first element in the queue + */ + public T head() { + return data[start]; + } + + /** + * Value of the last element in the queue + */ + public T tail() { + return data[(start+size-1)%data.length]; + } + + /** + * Removes the first element + */ + public void removeHead() { + start = (start+1)%data.length; + size--; + } + + /** + * Removes the last element + */ + public void removeTail() { + size--; + } + + /** + * Returns the element in the queue at index. No bounds check is performed and a garbage value might be returned. + * @param index Which element in the queue you wish to access + * @return the element's value + */ + public T get( int index ) { + return data[(start+index)%data.length]; + } + + /** + * Adds a new element to the end of the list and returns it. If the inner array isn't large enough + * then it will grow. + * @return instance at the tail + */ + public T grow() { + if( size >= data.length) { + T a = createInstance(); + add(a); + return a; + } else { + T a = data[(start+size)%data.length]; + if( a == null ) { + data[(start+size)%data.length] = a = createInstance(); + } + size++; + return a; + } + } + + /** + * Adds a new element to the end of the list and returns it. If the inner array isn't large enough + * then the oldest element will be written over. + * @return instance at the tail + */ + public T growW() { + T a; + if( size >= data.length) { + a = data[start]; + if( a == null ) + data[start] = a = createInstance(); + start = (start+1)%data.length; + } else { + a = data[(start+size)%data.length]; + if( a == null ) + data[(start+size)%data.length] = a = createInstance(); + size++; + } + return a; + } + + /** + * Adds a new element to the queue. If the queue isn't large enough to store this value then its internal data + * array will grow + * @param value Value which is to be added + */ + public void add( T value ) { + // see if it needs to grow the queue + if( size >= data.length) { + growInnerArray(); + } + data[(start+size)%data.length] = value; + size++; + } + + private void growInnerArray() { + T[] a = (T[]) Array.newInstance(type, nextDataSize()); + + System.arraycopy(data,start,a,0,data.length-start); + System.arraycopy(data,0,a,data.length-start,start); + start = 0; + data = a; + } + + /** + * Adds a new element to the queue, but if the queue is full write over the oldest element. + * + * @param value Value which is to be added + */ + public void addW( T value ) { + // see if it needs to grow the queue + if( size >= data.length) { + data[start] = value; + start = (start+1)%data.length; + } else { + data[(start+size)%data.length] = value; + size++; + } + } + + private int nextDataSize() { + if( data.length < 1000 ) + return data.length*2; + else if( data.length < 10000 ) + return data.length*3/2; + else + return data.length*6/5; + } + + public int size() { + return size; + } + + public boolean isEmpty() { + return size == 0; + } + + public boolean isFull(){ return size == data.length;} + + protected T createInstance() { + try { + return type.newInstance(); + } catch (InstantiationException e) { + throw new RuntimeException(e); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + } +} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/struct/CircularQueue_F32.java ddogleg-0.22+ds/src/org/ddogleg/struct/CircularQueue_F32.java --- ddogleg-0.18+ds/src/org/ddogleg/struct/CircularQueue_F32.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/struct/CircularQueue_F32.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,164 +0,0 @@ -/* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. - * - * This file is part of DDogleg (http://ddogleg.org). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ddogleg.struct; - -/** - * A circular queue which can grow as needed. - * - * @author Peter Abeles - */ -public class CircularQueue_F32 { - // - public float data[]; - - // index which is the start of the queue - public int start; - // number of elements in the queue - public int size; - - public CircularQueue_F32() { - this(10); - } - - public CircularQueue_F32(int dataSize ) { - data = new float[dataSize]; - } - - public void reset() { - start = size = 0; - } - - /** - * Returns and removes the first element from the queue. - * @return first element in the queue - */ - public float popHead() { - float r = data[start]; - removeHead(); - return r; - } - - /** - * Returns and removes the last element from the queue. - * @return last element in the queue - */ - public float popTail() { - float r = tail(); - removeTail(); - return r; - } - - /** - * Value of the first element in the queue - */ - public float head() { - return data[start]; - } - - /** - * Value of the last element in the queue - */ - public float tail() { - return data[(start+size-1)%data.length]; - } - - /** - * Removes the first element - */ - public void removeHead() { - start = (start+1)%data.length; - size--; - } - - /** - * Removes the last element - */ - public void removeTail() { - size--; - } - - /** - * Returns the element in the queue at index. No bounds check is performed and a garbage value might be returned. - * @param index Which element in the queue you wish to access - * @return the element's value - */ - public float get( int index ) { - return data[(start+index)%data.length]; - } - - /** - * Adds a new element to the queue, but if the queue is full write over the oldest element. - * - * @param value Value which is to be added - */ - public void add( float value ) { - // see if it needs to grow the queue - if( size >= data.length) { - data[start] = value; - start = (start+1)%data.length; - } else { - data[(start+size)%data.length] = value; - size++; - } - } - - private int nextDataSize() { - if( data.length < 1000 ) - return data.length*2; - else if( data.length < 10000 ) - return data.length*3/2; - else - return data.length*6/5; - } - - public void set( CircularQueue_F32 original ) { - if( this.data.length != original.data.length) { - this.data = new float[original.data.length]; - } - System.arraycopy(original.data,0,this.data,0,this.data.length); - this.size = original.size; - this.start = original.start; - } - - public CircularQueue_F32 copy() { - CircularQueue_F32 r = new CircularQueue_F32(); - r.set(this); - return r; - } - - public void resizeQueue( int maxSize ) { - if( this.data.length != maxSize) { - this.data = new float[maxSize]; - } - } - - public int queueSize() { - return data.length; - } - - public int size() { - return size; - } - - public boolean isEmpty() { - return size == 0; - } - - public boolean isFull(){ return size == data.length;} -} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/struct/CircularQueue_F64.java ddogleg-0.22+ds/src/org/ddogleg/struct/CircularQueue_F64.java --- ddogleg-0.18+ds/src/org/ddogleg/struct/CircularQueue_F64.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/struct/CircularQueue_F64.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,164 +0,0 @@ -/* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. - * - * This file is part of DDogleg (http://ddogleg.org). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ddogleg.struct; - -/** - * A circular queue which can grow as needed. - * - * @author Peter Abeles - */ -public class CircularQueue_F64 { - // - public double data[]; - - // index which is the start of the queue - public int start; - // number of elements in the queue - public int size; - - public CircularQueue_F64() { - this(10); - } - - public CircularQueue_F64(int dataSize ) { - data = new double[dataSize]; - } - - public void reset() { - start = size = 0; - } - - /** - * Returns and removes the first element from the queue. - * @return first element in the queue - */ - public double popHead() { - double r = data[start]; - removeHead(); - return r; - } - - /** - * Returns and removes the last element from the queue. - * @return last element in the queue - */ - public double popTail() { - double r = tail(); - removeTail(); - return r; - } - - /** - * Value of the first element in the queue - */ - public double head() { - return data[start]; - } - - /** - * Value of the last element in the queue - */ - public double tail() { - return data[(start+size-1)%data.length]; - } - - /** - * Removes the first element - */ - public void removeHead() { - start = (start+1)%data.length; - size--; - } - - /** - * Removes the last element - */ - public void removeTail() { - size--; - } - - /** - * Returns the element in the queue at index. No bounds check is performed and a garbage value might be returned. - * @param index Which element in the queue you wish to access - * @return the element's value - */ - public double get( int index ) { - return data[(start+index)%data.length]; - } - - /** - * Adds a new element to the queue, but if the queue is full write over the oldest element. - * - * @param value Value which is to be added - */ - public void add( double value ) { - // see if it needs to grow the queue - if( size >= data.length) { - data[start] = value; - start = (start+1)%data.length; - } else { - data[(start+size)%data.length] = value; - size++; - } - } - - private int nextDataSize() { - if( data.length < 1000 ) - return data.length*2; - else if( data.length < 10000 ) - return data.length*3/2; - else - return data.length*6/5; - } - - public void set( CircularQueue_F64 original ) { - if( this.data.length != original.data.length) { - this.data = new double[original.data.length]; - } - System.arraycopy(original.data,0,this.data,0,this.data.length); - this.size = original.size; - this.start = original.start; - } - - public CircularQueue_F64 copy() { - CircularQueue_F64 r = new CircularQueue_F64(); - r.set(this); - return r; - } - - public void resizeQueue( int maxSize ) { - if( this.data.length != maxSize) { - this.data = new double[maxSize]; - } - } - - public int queueSize() { - return data.length; - } - - public int size() { - return size; - } - - public boolean isEmpty() { - return size == 0; - } - - public boolean isFull(){ return size == data.length;} -} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/struct/CircularQueue_I32.java ddogleg-0.22+ds/src/org/ddogleg/struct/CircularQueue_I32.java --- ddogleg-0.18+ds/src/org/ddogleg/struct/CircularQueue_I32.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/struct/CircularQueue_I32.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,158 +0,0 @@ -/* - * Copyright (c) 2012-2014, Peter Abeles. All Rights Reserved. - * - * This file is part of DDogleg (http://ddogleg.org). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ddogleg.struct; - -/** - * A circular queue which can grow as needed. - * - * @author Peter Abeles - */ -public class CircularQueue_I32 { - // - public int data[]; - - // index which is the start of the queue - public int start; - // number of elements in the queue - public int size; - - public CircularQueue_I32() { - this(10); - } - - public CircularQueue_I32( int dataSize ) { - data = new int[dataSize]; - } - - public void reset() { - start = size = 0; - } - - /** - * Returns and removes the first element from the queue. - * @return first element in the queue - */ - public int popHead() { - int r = data[start]; - removeHead(); - return r; - } - - /** - * Returns and removes the last element from the queue. - * @return last element in the queue - */ - public int popTail() { - int r = tail(); - removeTail(); - return r; - } - - /** - * Value of the first element in the queue - */ - public int head() { - return data[start]; - } - - /** - * Value of the last element in the queue - */ - public int tail() { - return data[(start+size-1)%data.length]; - } - - /** - * Removes the first element - */ - public void removeHead() { - start = (start+1)%data.length; - size--; - } - - /** - * Removes the last element - */ - public void removeTail() { - size--; - } - - /** - * Returns the element in the queue at index. No bounds check is performed and a garbage value might be returned. - * @param index Which element in the queue you wish to access - * @return the element's value - */ - public int get( int index ) { - return data[(start+index)%data.length]; - } - - /** - * Adds a new element to the queue. If the queue isn't large enough to store this value then its internal data - * array will grow - * @param value Value which is to be added - */ - public void add( int value ) { - // see if it needs to grow the queue - if( size >= data.length) { - int a[] = new int[ nextDataSize() ]; - - System.arraycopy(data,start,a,0,data.length-start); - System.arraycopy(data,0,a,data.length-start,start); - start = 0; - data = a; - } - data[(start+size)%data.length] = value; - size++; - } - - /** - * Adds a new element to the queue, but if the queue is full write over the oldest element. - * - * @param value Value which is to be added - */ - public void addW( int value ) { - // see if it needs to grow the queue - if( size >= data.length) { - data[start] = value; - start = (start+1)%data.length; - } else { - data[(start+size)%data.length] = value; - size++; - } - } - - private int nextDataSize() { - if( data.length < 1000 ) - return data.length*2; - else if( data.length < 10000 ) - return data.length*3/2; - else - return data.length*6/5; - } - - public int size() { - return size; - } - - public boolean isEmpty() { - return size == 0; - } - - public boolean isFull(){ return size == data.length;} -} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/struct/CircularQueue.java ddogleg-0.22+ds/src/org/ddogleg/struct/CircularQueue.java --- ddogleg-0.18+ds/src/org/ddogleg/struct/CircularQueue.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/struct/CircularQueue.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,218 +0,0 @@ -/* - * Copyright (c) 2012-2014, Peter Abeles. All Rights Reserved. - * - * This file is part of DDogleg (http://ddogleg.org). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ddogleg.struct; - -import java.lang.reflect.Array; - -/** - * A circular queue which can grow as needed. - * - * @author Peter Abeles - */ -public class CircularQueue { - // inner data array - public T data[]; - - // index which is the start of the queue - public int start; - // number of elements in the queue - public int size; - - Class type; - - public CircularQueue( Class type ) { - this(type,10); - } - - public CircularQueue( Class type , int maxSize) { - this.type = type; - data = (T[])Array.newInstance(type,maxSize); - } - - public void reset() { - start = size = 0; - } - - /** - * Returns and removes the first element from the queue. - * @return first element in the queue - */ - public T popHead() { - T r = data[start]; - removeHead(); - return r; - } - - /** - * Returns and removes the last element from the queue. - * @return last element in the queue - */ - public T popTail() { - T r = tail(); - removeTail(); - return r; - } - - /** - * Value of the first element in the queue - */ - public T head() { - return data[start]; - } - - /** - * Value of the last element in the queue - */ - public T tail() { - return data[(start+size-1)%data.length]; - } - - /** - * Removes the first element - */ - public void removeHead() { - start = (start+1)%data.length; - size--; - } - - /** - * Removes the last element - */ - public void removeTail() { - size--; - } - - /** - * Returns the element in the queue at index. No bounds check is performed and a garbage value might be returned. - * @param index Which element in the queue you wish to access - * @return the element's value - */ - public T get( int index ) { - return data[(start+index)%data.length]; - } - - /** - * Adds a new element to the end of the list and returns it. If the inner array isn't large enough - * then it will grow. - * @return instance at the tail - */ - public T grow() { - if( size >= data.length) { - T a = createInstance(); - add(a); - return a; - } else { - T a = data[(start+size)%data.length]; - if( a == null ) { - data[(start+size)%data.length] = a = createInstance(); - } - size++; - return a; - } - } - - /** - * Adds a new element to the end of the list and returns it. If the inner array isn't large enough - * then the oldest element will be written over. - * @return instance at the tail - */ - public T growW() { - T a; - if( size >= data.length) { - a = data[start]; - if( a == null ) - data[start] = a = createInstance(); - start = (start+1)%data.length; - } else { - a = data[(start+size)%data.length]; - if( a == null ) - data[(start+size)%data.length] = a = createInstance(); - size++; - } - return a; - } - - /** - * Adds a new element to the queue. If the queue isn't large enough to store this value then its internal data - * array will grow - * @param value Value which is to be added - */ - public void add( T value ) { - // see if it needs to grow the queue - if( size >= data.length) { - growInnerArray(); - } - data[(start+size)%data.length] = value; - size++; - } - - private void growInnerArray() { - T a[] = (T[]) Array.newInstance(type, nextDataSize()); - - System.arraycopy(data,start,a,0,data.length-start); - System.arraycopy(data,0,a,data.length-start,start); - start = 0; - data = a; - } - - /** - * Adds a new element to the queue, but if the queue is full write over the oldest element. - * - * @param value Value which is to be added - */ - public void addW( T value ) { - // see if it needs to grow the queue - if( size >= data.length) { - data[start] = value; - start = (start+1)%data.length; - } else { - data[(start+size)%data.length] = value; - size++; - } - } - - private int nextDataSize() { - if( data.length < 1000 ) - return data.length*2; - else if( data.length < 10000 ) - return data.length*3/2; - else - return data.length*6/5; - } - - public int size() { - return size; - } - - public boolean isEmpty() { - return size == 0; - } - - public boolean isFull(){ return size == data.length;} - - protected T createInstance() { - try { - return type.newInstance(); - } catch (InstantiationException e) { - throw new RuntimeException(e); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } - } -} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/struct/DogArray_B.java ddogleg-0.22+ds/src/org/ddogleg/struct/DogArray_B.java --- ddogleg-0.18+ds/src/org/ddogleg/struct/DogArray_B.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/struct/DogArray_B.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,491 @@ +/* + * Copyright (c) 2012-2022, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.struct; + +import java.util.Arrays; +import java.util.Random; + +/** + * Growable array composed of booleans. + * + * @author Peter Abeles + */ +public class DogArray_B implements DogArrayPrimitive { + + public boolean[] data; + public int size; + + public DogArray_B( int reserve ) { + data = new boolean[reserve]; + this.size = 0; + } + + public DogArray_B() { + this(10); + } + + /** + * Creates a queue with the specified length as its size filled with false + */ + public static DogArray_B zeros( int length ) { + DogArray_B out = new DogArray_B(length); + out.size = length; + return out; + } + + public static DogArray_B array( boolean... values ) { + DogArray_B out = zeros(values.length); + for (int i = 0; i < values.length; i++) { + out.data[i] = values[i]; + } + return out; + } + + /** + * Non-zero values are set to true + */ + public static DogArray_B array( int... values ) { + DogArray_B out = zeros(values.length); + for (int i = 0; i < values.length; i++) { + out.data[i] = values[i] != 0; + } + return out; + } + + /** + * Counts the number of times the specified value occurs in the list + */ + public int count( boolean value ) { + int total = 0; + for (int i = 0; i < size; i++) { + if (data[i] == value) + total++; + } + return total; + } + + /** + * Sees is the primitive array is equal to the values in this array + * + * @param values primitive array + * @return true if equal or false if not + */ + public boolean isEquals( int... values ) { + if (size != values.length) + return false; + for (int i = 0; i < size; i++) { + boolean v = values[i] != 0; + if (data[i] != v) + return false; + } + return true; + } + + public boolean isEquals( boolean... values ) { + if (size != values.length) + return false; + for (int i = 0; i < size; i++) { + if (data[i] != values[i]) + return false; + } + return true; + } + + public boolean isEquals( DogArray_B values ) { + if (size != values.size) + return false; + for (int i = 0; i < size; i++) { + if (data[i] != values.data[i]) + return false; + } + return true; + } + + @Override + public DogArray_B reset() { + size = 0; + return this; + } + + public void addAll( DogArray_B queue ) { + if (size + queue.size > data.length) { + boolean[] temp = new boolean[(size + queue.size)*2]; + System.arraycopy(data, 0, temp, 0, size); + data = temp; + } + System.arraycopy(queue.data, 0, data, size, queue.size); + size += queue.size; + } + + public void addAll( boolean[] array, int startIndex, int endIndex ) { + if (endIndex > array.length) + throw new IllegalAccessError("endIndex is larger than input array. " + endIndex + " > " + array.length); + + int arraySize = endIndex - startIndex; + + if (size + arraySize > data.length) { + boolean[] temp = new boolean[(size + arraySize)*2]; + System.arraycopy(data, 0, temp, 0, size); + data = temp; + } + System.arraycopy(array, startIndex, data, size, arraySize); + size += arraySize; + } + + public void add( boolean val ) { + push(val); + } + + public void push( boolean val ) { + if (size == data.length) { + boolean[] temp; + try { + temp = new boolean[size*2 + 5]; + } catch (OutOfMemoryError e) { + System.gc(); + temp = new boolean[3*size/2]; + } + System.arraycopy(data, 0, temp, 0, size); + data = temp; + } + data[size++] = val; + } + + /** + * Sets this array to be equal to the array segment + * + * @param array (Input) source array + * @param offset first index + * @param length number of elements to copy + */ + public DogArray_B setTo( boolean[] array, int offset, int length ) { + resize(length); + System.arraycopy(array, offset, data, 0, length); + return this; + } + + /** + * Set's the value of this array to the passed in raw array. + * + * @param src (Input) The input array + * @return A reference to "this" to allow chaining of commands + */ + public DogArray_B setTo( boolean... src ) { + setTo(src, 0, src.length); + return this; + } + + /** + * Creates a new primitive array which is a copy. + */ + public boolean[] toArray() { + boolean[] out = new boolean[size]; + System.arraycopy(data, 0, out, 0, size); + return out; + } + + public void remove( int index ) { + for (int i = index + 1; i < size; i++) { + data[i - 1] = data[i]; + } + size--; + } + + /** + * Removes elements from the list starting at 'first' and ending at 'last' + * + * @param first First index you wish to remove. Inclusive. + * @param last Last index you wish to remove. Inclusive. + */ + public void remove( int first, int last ) { + if (last < first) + throw new IllegalArgumentException("first <= last. first=" + first + " last=" + last); + if (last >= size) + throw new IllegalArgumentException("last must be less than the max size. last=" + last + " size=" + size); + + int delta = last - first + 1; + for (int i = last + 1; i < size; i++) { + data[i - delta] = data[i]; + } + size -= delta; + } + + /** + * Inserts the value at the specified index and shifts all the other values down. + */ + public void insert( int index, boolean value ) { + if (size == data.length) { + boolean[] temp = new boolean[size*2 + 5]; + System.arraycopy(data, 0, temp, 0, index); + temp[index] = value; + System.arraycopy(data, index, temp, index + 1, size - index); + this.data = temp; + size++; + } else { + size++; + for (int i = size - 1; i > index; i--) { + data[i] = data[i - 1]; + } + data[index] = value; + } + } + + /** + * Removes the specified index from the array by swapping it with last element. Does not preserve order + * but has a runtime of O(1). + * + * @param index The index to be removed. + * @return The removed object + */ + public boolean removeSwap( int index ) { + if (index < 0 || index >= size) + throw new IllegalArgumentException("Out of bounds. index=" + index + " max size " + size); + boolean ret = data[index]; + size -= 1; + data[index] = data[size]; + return ret; + } + + public boolean removeTail() { + if (size > 0) { + size--; + return data[size]; + } else { + throw new RuntimeException("Size zero, no tail"); + } + } + + public boolean get( int index ) { + if (index < 0 || index >= size) + throw new IndexOutOfBoundsException("index = " + index + " size = " + size); + return data[index]; + } + + public boolean getTail() { + if (size == 0) + throw new IndexOutOfBoundsException("Array is empty"); + return data[size - 1]; + } + + /** + * Returns an element starting from the end of the list. 0 = size -1 + */ + public boolean getTail( int index ) { + if (index < 0 || index >= size) + throw new IndexOutOfBoundsException("index = " + index + " size = " + size); + return data[size - index - 1]; + } + + public void setTail( int index, boolean value ) { + if (index < 0 || index >= size) + throw new IndexOutOfBoundsException("index = " + index + " size = " + size); + data[size - index - 1] = value; + } + + public boolean unsafe_get( int index ) { + return data[index]; + } + + public void set( int index, boolean value ) { + data[index] = value; + } + + @Override public DogArray_B setTo( DogArray_B original ) { + resize(original.size); + System.arraycopy(original.data, 0, data, 0, size()); + return this; + } + + @Override public DogArray_B resize( int size ) { + reserve(size); + this.size = size; + return this; + } + + /** + * Resizes the array and assigns the default value to every new element. + * + * @param size New size + * @param value Default value + */ + public DogArray_B resize( int size, boolean value ) { + int priorSize = this.size; + resize(size); + if (priorSize >= size) + return this; + fill(priorSize, size, value); + return this; + } + + /** + * Convenience function that will first call {@link #reset} then {@link #resize(int, boolean)}, ensuring + * that every element in the array will have the specified value + * + * @param size New size + * @param value New value of every element + */ + @Deprecated + public void resetResize( int size, boolean value ) { + reset(); + resize(size, value); + } + + /** + * Resizes and assigns the new elements (if any) to the value specified by the lambda + * + * @param size New sie + * @param op Assigns default values + */ + public DogArray_B resize( int size, DogLambdas.AssignIdx_B op ) { + int priorSize = this.size; + resize(size); + for (int i = priorSize; i < size; i++) { + data[i] = op.assign(i); + } + return this; + } + + public void fill( boolean value ) { + Arrays.fill(data, 0, size, value); + } + + public void fill( int idx0, int idx1, boolean value ) { + Arrays.fill(data, idx0, idx1, value); + } + + public boolean contains( boolean value ) { + for (int i = 0; i < size; i++) { + if (data[i] == value) + return true; + } + return false; + } + + @Override public void extend( int size ) { + reserve(size); + this.size = size; + } + + @SuppressWarnings("NullAway") + @Override public void reserve( int amount ) { + if (data.length >= amount) + return; + if (size == 0) { + // In this special case we can dereference the old array and this might allow the GC to free up memory + // before declaring the new array. Could be useful if the arrays are very large. + this.data = null; + this.data = new boolean[amount]; + } else { + boolean[] tmp = new boolean[amount]; + System.arraycopy(data, 0, tmp, 0, this.size); + data = tmp; + } + } + + @Override public int size() { + return size; + } + + @Override public void zero() { + Arrays.fill(data, 0, size, false); + } + + @Override public DogArray_B copy() { + var ret = new DogArray_B(size); + ret.setTo(this); + return ret; + } + + @Override public void flip() { + if (size <= 1) + return; + + int D = size/2; + for (int i = 0, j = size - 1; i < D; i++, j--) { + boolean tmp = data[i]; + data[i] = data[j]; + data[j] = tmp; + } + } + + public boolean pop() { + return data[--size]; + } + + /** + * Returns the index of the first element with the specified 'value'. return -1 if it wasn't found + * + * @param value Value to search for + * @return index or -1 if it's not in the list + */ + public int indexOf( boolean value ) { + for (int i = 0; i < size; i++) { + if (data[i] == value) + return i; + } + return -1; + } + + @Override public void sort() { + throw new RuntimeException("Undefined for boolean"); + } + + /** Shuffle elements by randomly swapping them */ + public void shuffle( Random rand ) { + for (int i = 0; i < size; i++) { + int src = rand.nextInt(size - i) + i; + boolean tmp = data[i]; + data[i] = data[src]; + data[src] = tmp; + } + } + + public void forIdx( FunctionEachIdx func ) { + for (int i = 0; i < size; i++) { + func.process(i, data[i]); + } + } + + public void forEach( FunctionEach func ) { + for (int i = 0; i < size; i++) { + func.process(data[i]); + } + } + + public void applyIdx( FunctionApplyIdx func ) { + for (int i = 0; i < size; i++) { + data[i] = func.process(i, data[i]); + } + } + + @FunctionalInterface + public interface FunctionEachIdx { + void process( int index, boolean value ); + } + + @FunctionalInterface + public interface FunctionEach { + void process( boolean value ); + } + + @FunctionalInterface + public interface FunctionApplyIdx { + boolean process( int index, boolean value ); + } +} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/struct/DogArray_F32.java ddogleg-0.22+ds/src/org/ddogleg/struct/DogArray_F32.java --- ddogleg-0.18+ds/src/org/ddogleg/struct/DogArray_F32.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/struct/DogArray_F32.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,551 @@ +/* + * Copyright (c) 2012-2022, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.struct; + +import org.ddogleg.sorting.QuickSort_F32; + +import java.util.Arrays; +import java.util.Random; + +/** + * Growable array composed of floats. + * + * @author Peter Abeles + */ +public class DogArray_F32 implements DogArrayPrimitive { + + public float[] data; + public int size; + + public DogArray_F32( int reserve ) { + data = new float[reserve]; + this.size = 0; + } + + public DogArray_F32() { + this(10); + } + + /** + * Creates a queue with the specified length as its size filled with all zeros + */ + public static DogArray_F32 zeros( int length ) { + var out = new DogArray_F32(length); + out.size = length; + return out; + } + + public static DogArray_F32 array( float... values ) { + DogArray_F32 out = zeros(values.length); + for (int i = 0; i < values.length; i++) { + out.data[i] = values[i]; + } + return out; + } + + /** + * Returns a new array with values containing range of integer numbers from idx0 to idx1-1. + * + * @param idx0 Lower extent, inclusive. + * @param idx1 Upper extent, exclusive. + * @return new array. + */ + public static DogArray_F32 range( int idx0, int idx1 ) { + DogArray_F32 out = zeros(idx1 - idx0); + for (int i = idx0; i < idx1; i++) { + out.data[i - idx0] = (float)i; + } + return out; + } + + /** + * Counts the number of times the specified value occurs in the list + */ + public int count( float value ) { + int total = 0; + for (int i = 0; i < size; i++) { + if (data[i] == value) + total++; + } + return total; + } + + /** + * Sees is the primitive array is equal to the values in this array + * + * @param values primitive array + * @return true if equal or false if not + */ + public boolean isEquals( float... values ) { + if (size != values.length) + return false; + for (int i = 0; i < size; i++) { + if (data[i] != values[i]) + return false; + } + return true; + } + + public boolean isEquals( DogArray_F32 values ) { + if (size != values.size) + return false; + for (int i = 0; i < size; i++) { + if (data[i] != values.data[i]) + return false; + } + return true; + } + + @Override + public DogArray_F32 reset() { + size = 0; + return this; + } + + public void addAll( DogArray_F32 queue ) { + if (size + queue.size > data.length) { + float[] temp = new float[(size + queue.size)*2]; + System.arraycopy(data, 0, temp, 0, size); + data = temp; + } + System.arraycopy(queue.data, 0, data, size, queue.size); + size += queue.size; + } + + public void addAll( float[] array, int startIndex, int endIndex ) { + if (endIndex > array.length) + throw new IllegalAccessError("endIndex is larger than input array. " + endIndex + " > " + array.length); + + int arraySize = endIndex - startIndex; + + if (size + arraySize > data.length) { + float[] temp = new float[(size + arraySize)*2]; + System.arraycopy(data, 0, temp, 0, size); + data = temp; + } + System.arraycopy(array, startIndex, data, size, arraySize); + size += arraySize; + } + + public void add( float val ) { + push(val); + } + + public void push( float val ) { + if (size == data.length) { + float[] temp; + try { + temp = new float[size*2 + 5]; + } catch (OutOfMemoryError e) { + System.gc(); + temp = new float[3*size/2]; + } + System.arraycopy(data, 0, temp, 0, size); + data = temp; + } + data[size++] = (float)val; + } + + /** + * Sets this array to be equal to the array segment + * + * @param array (Input) source array + * @param offset first index + * @param length number of elements to copy + */ + public DogArray_F32 setTo( float[] array, int offset, int length ) { + resize(length); + System.arraycopy(array, offset, data, 0, length); + return this; + } + + /** + * Set's the value of this array to the passed in raw array. + * + * @param src (Input) The input array + * @return A reference to "this" to allow chaining of commands + */ + public DogArray_F32 setTo( float... src ) { + setTo(src, 0, src.length); + return this; + } + + /** + * Creates a new primitive array which is a copy. + */ + public float[] toArray() { + float[] out = new float[size]; + System.arraycopy(data, 0, out, 0, size); + return out; + } + + public void remove( int index ) { + for (int i = index + 1; i < size; i++) { + data[i - 1] = data[i]; + } + size--; + } + + /** + * Removes elements from the list starting at 'first' and ending at 'last' + * + * @param first First index you wish to remove. Inclusive. + * @param last Last index you wish to remove. Inclusive. + */ + public void remove( int first, int last ) { + if (last < first) + throw new IllegalArgumentException("first <= last. first=" + first + " last=" + last); + if (last >= size) + throw new IllegalArgumentException("last must be less than the max size. last=" + last + " size=" + size); + + int delta = last - first + 1; + for (int i = last + 1; i < size; i++) { + data[i - delta] = data[i]; + } + size -= delta; + } + + /** + * Inserts the value at the specified index and shifts all the other values down. + */ + public void insert( int index, float value ) { + if (size == data.length) { + float[] temp = new float[size*2 + 5]; + System.arraycopy(data, 0, temp, 0, index); + temp[index] = value; + System.arraycopy(data, index, temp, index + 1, size - index); + this.data = temp; + size++; + } else { + size++; + for (int i = size - 1; i > index; i--) { + data[i] = data[i - 1]; + } + data[index] = value; + } + } + + /** + * Removes the specified index from the array by swapping it with last element. Does not preserve order + * but has a runtime of O(1). + * + * @param index The index to be removed. + * @return The removed object + */ + public float removeSwap( int index ) { + if (index < 0 || index >= size) + throw new IllegalArgumentException("Out of bounds. index=" + index + " max size " + size); + float ret = data[index]; + size -= 1; + data[index] = data[size]; + return ret; + } + + public float removeTail() { + if (size > 0) { + size--; + return data[size]; + } else { + throw new RuntimeException("Size zero, no tail"); + } + } + + public float get( int index ) { + if (index < 0 || index >= size) + throw new IndexOutOfBoundsException("index = " + index + " size = " + size); + return data[index]; + } + + public float getTail() { + if (size == 0) + throw new IndexOutOfBoundsException("Array is empty"); + return data[size - 1]; + } + + /** + * Returns an element starting from the end of the list. 0 = size -1 + */ + public float getTail( int index ) { + if (index < 0 || index >= size) + throw new IndexOutOfBoundsException("index = " + index + " size = " + size); + return data[size - index - 1]; + } + + public void setTail( int index, float value ) { + if (index < 0 || index >= size) + throw new IndexOutOfBoundsException("index = " + index + " size = " + size); + data[size - index - 1] = value; + } + + /** + * Gets the value at the index which corresponds to the specified fraction + * + * @param fraction 0 to 1 inclusive + * @return value at fraction + */ + public float getFraction( double fraction ) { + return get((int)((size - 1)*fraction)); + } + + public float unsafe_get( int index ) { + return data[index]; + } + + public void set( int index, float value ) { + data[index] = value; + } + + @Override public DogArray_F32 setTo( DogArray_F32 original ) { + resize(original.size); + System.arraycopy(original.data, 0, data, 0, size()); + return this; + } + + @Override public DogArray_F32 resize( int size ) { + reserve(size); + this.size = size; + return this; + } + + /** + * Resizes the array and assigns the default value to every new element. + * + * @param size New size + * @param value Default value + */ + public DogArray_F32 resize( int size, float value ) { + int priorSize = this.size; + resize(size); + if (priorSize >= size ) + return this; + fill(priorSize, size, value); + return this; + } + + /** + * Convenience function that will first call {@link #reset} then {@link #resize(int, float)}, ensuring + * that every element in the array will have the specified value + * + * @param size New size + * @param value New value of every element + */ + @Deprecated + public void resetResize( int size, float value ) { + reset(); + resize(size, value); + } + + /** + * Resizes and assigns the new elements (if any) to the value specified by the lambda + * + * @param size New sie + * @param op Assigns default values + */ + public DogArray_F32 resize( int size, DogLambdas.AssignIdx_F32 op ) { + int priorSize = this.size; + resize(size); + for (int i = priorSize; i < size; i++) { + data[i] = op.assign(i); + } + return this; + } + + public void fill( float value ) { + Arrays.fill(data, 0, size, value); + } + + public void fill( int idx0, int idx1, float value ) { + Arrays.fill(data, idx0, idx1, value); + } + + public boolean contains( float value ) { + for (int i = 0; i < size; i++) { + if (data[i] == value) + return true; + } + return false; + } + + @Override public void extend( int size ) { + reserve(size); + this.size = size; + } + + @SuppressWarnings("NullAway") + @Override public void reserve( int amount ) { + if (data.length >= amount) + return; + if (size == 0) { + // In this special case we can dereference the old array and this might allow the GC to free up memory + // before declaring the new array. Could be useful if the arrays are very large. + this.data = null; + this.data = new float[amount]; + } else { + var tmp = new float[amount]; + System.arraycopy(data, 0, tmp, 0, this.size); + data = tmp; + } + } + + @Override public int size() { + return size; + } + + @Override public void zero() { + Arrays.fill(data, 0, size, (float)0); + } + + @Override public DogArray_F32 copy() { + var ret = new DogArray_F32(size); + ret.setTo(this); + return ret; + } + + @Override public void flip() { + if (size <= 1) + return; + + int D = size/2; + for (int i = 0, j = size - 1; i < D; i++, j--) { + float tmp = data[i]; + data[i] = data[j]; + data[j] = tmp; + } + } + + public float pop() { + return data[--size]; + } + + /** + * Returns the index of the first element with the specified 'value'. return -1 if it wasn't found + * + * @param value Value to search for + * @return index or -1 if it's not in the list + */ + public int indexOf( float value ) { + for (int i = 0; i < size; i++) { + if (data[i] == value) + return i; + } + return -1; + } + + public int indexOfGreatest() { + if (size <= -0) + return -1; + + int selected = 0; + float best = data[0]; + + for (int i = 1; i < size; i++) { + if (data[i] > best) { + best = data[i]; + selected = i; + } + } + + return selected; + } + + public int indexOfLeast() { + if (size <= -0) + return -1; + + int selected = 0; + float best = data[0]; + + for (int i = 1; i < size; i++) { + if (data[i] < best) { + best = data[i]; + selected = i; + } + } + + return selected; + } + + @Override public void sort() { + Arrays.sort(data, 0, size); + } + + /** + * Sort but with a re-usable sorter to avoid declaring new memory + */ + public void sort( QuickSort_F32 sorter ) { + sorter.sort(data, size); + } + + /** Shuffle elements by randomly swapping them */ + public void shuffle( Random rand ) { + for (int i = 0; i < size; i++) { + int src = rand.nextInt(size - i) + i; + float tmp = data[i]; + data[i] = data[src]; + data[src] = tmp; + } + } + + public void forIdx( FunctionEachIdx func ) { + for (int i = 0; i < size; i++) { + func.process(i, data[i]); + } + } + + public void forEach( FunctionEach func ) { + for (int i = 0; i < size; i++) { + func.process(data[i]); + } + } + + public void applyIdx( FunctionApplyIdx func ) { + for (int i = 0; i < size; i++) { + data[i] = func.process(i, data[i]); + } + } + + public int count( Filter filter ) { + int total = 0; + for (int i = 0; i < size; i++) { + if (filter.include(data[i])) + total++; + } + return total; + } + + @FunctionalInterface + public interface FunctionEachIdx { + void process( int index, float value ); + } + + @FunctionalInterface + public interface FunctionEach { + void process( float value ); + } + + @FunctionalInterface + public interface FunctionApplyIdx { + float process( int index, float value ); + } + + @FunctionalInterface + public interface Filter { + boolean include( double value ); + } +} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/struct/DogArray_F64.java ddogleg-0.22+ds/src/org/ddogleg/struct/DogArray_F64.java --- ddogleg-0.18+ds/src/org/ddogleg/struct/DogArray_F64.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/struct/DogArray_F64.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,551 @@ +/* + * Copyright (c) 2012-2022, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.struct; + +import org.ddogleg.sorting.QuickSort_F64; + +import java.util.Arrays; +import java.util.Random; + +/** + * Growable array composed of doubles. + * + * @author Peter Abeles + */ +public class DogArray_F64 implements DogArrayPrimitive { + + public double[] data; + public int size; + + public DogArray_F64( int reserve ) { + data = new double[reserve]; + this.size = 0; + } + + public DogArray_F64() { + this(10); + } + + /** + * Creates a queue with the specified length as its size filled with all zeros + */ + public static DogArray_F64 zeros( int length ) { + var out = new DogArray_F64(length); + out.size = length; + return out; + } + + public static DogArray_F64 array( double... values ) { + DogArray_F64 out = zeros(values.length); + for (int i = 0; i < values.length; i++) { + out.data[i] = values[i]; + } + return out; + } + + /** + * Returns a new array with values containing range of integer numbers from idx0 to idx1-1. + * + * @param idx0 Lower extent, inclusive. + * @param idx1 Upper extent, exclusive. + * @return new array. + */ + public static DogArray_F64 range( int idx0, int idx1 ) { + DogArray_F64 out = zeros(idx1 - idx0); + for (int i = idx0; i < idx1; i++) { + out.data[i - idx0] = (double)i; + } + return out; + } + + /** + * Counts the number of times the specified value occurs in the list + */ + public int count( double value ) { + int total = 0; + for (int i = 0; i < size; i++) { + if (data[i] == value) + total++; + } + return total; + } + + /** + * Sees is the primitive array is equal to the values in this array + * + * @param values primitive array + * @return true if equal or false if not + */ + public boolean isEquals( double... values ) { + if (size != values.length) + return false; + for (int i = 0; i < size; i++) { + if (data[i] != values[i]) + return false; + } + return true; + } + + public boolean isEquals( DogArray_F64 values ) { + if (size != values.size) + return false; + for (int i = 0; i < size; i++) { + if (data[i] != values.data[i]) + return false; + } + return true; + } + + @Override + public DogArray_F64 reset() { + size = 0; + return this; + } + + public void addAll( DogArray_F64 queue ) { + if (size + queue.size > data.length) { + double[] temp = new double[(size + queue.size)*2]; + System.arraycopy(data, 0, temp, 0, size); + data = temp; + } + System.arraycopy(queue.data, 0, data, size, queue.size); + size += queue.size; + } + + public void addAll( double[] array, int startIndex, int endIndex ) { + if (endIndex > array.length) + throw new IllegalAccessError("endIndex is larger than input array. " + endIndex + " > " + array.length); + + int arraySize = endIndex - startIndex; + + if (size + arraySize > data.length) { + double[] temp = new double[(size + arraySize)*2]; + System.arraycopy(data, 0, temp, 0, size); + data = temp; + } + System.arraycopy(array, startIndex, data, size, arraySize); + size += arraySize; + } + + public void add( double val ) { + push(val); + } + + public void push( double val ) { + if (size == data.length) { + double[] temp; + try { + temp = new double[size*2 + 5]; + } catch (OutOfMemoryError e) { + System.gc(); + temp = new double[3*size/2]; + } + System.arraycopy(data, 0, temp, 0, size); + data = temp; + } + data[size++] = (double)val; + } + + /** + * Sets this array to be equal to the array segment + * + * @param array (Input) source array + * @param offset first index + * @param length number of elements to copy + */ + public DogArray_F64 setTo( double[] array, int offset, int length ) { + resize(length); + System.arraycopy(array, offset, data, 0, length); + return this; + } + + /** + * Set's the value of this array to the passed in raw array. + * + * @param src (Input) The input array + * @return A reference to "this" to allow chaining of commands + */ + public DogArray_F64 setTo( double... src ) { + setTo(src, 0, src.length); + return this; + } + + /** + * Creates a new primitive array which is a copy. + */ + public double[] toArray() { + double[] out = new double[size]; + System.arraycopy(data, 0, out, 0, size); + return out; + } + + public void remove( int index ) { + for (int i = index + 1; i < size; i++) { + data[i - 1] = data[i]; + } + size--; + } + + /** + * Removes elements from the list starting at 'first' and ending at 'last' + * + * @param first First index you wish to remove. Inclusive. + * @param last Last index you wish to remove. Inclusive. + */ + public void remove( int first, int last ) { + if (last < first) + throw new IllegalArgumentException("first <= last. first=" + first + " last=" + last); + if (last >= size) + throw new IllegalArgumentException("last must be less than the max size. last=" + last + " size=" + size); + + int delta = last - first + 1; + for (int i = last + 1; i < size; i++) { + data[i - delta] = data[i]; + } + size -= delta; + } + + /** + * Inserts the value at the specified index and shifts all the other values down. + */ + public void insert( int index, double value ) { + if (size == data.length) { + double[] temp = new double[size*2 + 5]; + System.arraycopy(data, 0, temp, 0, index); + temp[index] = value; + System.arraycopy(data, index, temp, index + 1, size - index); + this.data = temp; + size++; + } else { + size++; + for (int i = size - 1; i > index; i--) { + data[i] = data[i - 1]; + } + data[index] = value; + } + } + + /** + * Removes the specified index from the array by swapping it with last element. Does not preserve order + * but has a runtime of O(1). + * + * @param index The index to be removed. + * @return The removed object + */ + public double removeSwap( int index ) { + if (index < 0 || index >= size) + throw new IllegalArgumentException("Out of bounds. index=" + index + " max size " + size); + double ret = data[index]; + size -= 1; + data[index] = data[size]; + return ret; + } + + public double removeTail() { + if (size > 0) { + size--; + return data[size]; + } else { + throw new RuntimeException("Size zero, no tail"); + } + } + + public double get( int index ) { + if (index < 0 || index >= size) + throw new IndexOutOfBoundsException("index = " + index + " size = " + size); + return data[index]; + } + + public double getTail() { + if (size == 0) + throw new IndexOutOfBoundsException("Array is empty"); + return data[size - 1]; + } + + /** + * Returns an element starting from the end of the list. 0 = size -1 + */ + public double getTail( int index ) { + if (index < 0 || index >= size) + throw new IndexOutOfBoundsException("index = " + index + " size = " + size); + return data[size - index - 1]; + } + + public void setTail( int index, double value ) { + if (index < 0 || index >= size) + throw new IndexOutOfBoundsException("index = " + index + " size = " + size); + data[size - index - 1] = value; + } + + /** + * Gets the value at the index which corresponds to the specified fraction + * + * @param fraction 0 to 1 inclusive + * @return value at fraction + */ + public double getFraction( double fraction ) { + return get((int)((size - 1)*fraction)); + } + + public double unsafe_get( int index ) { + return data[index]; + } + + public void set( int index, double value ) { + data[index] = value; + } + + @Override public DogArray_F64 setTo( DogArray_F64 original ) { + resize(original.size); + System.arraycopy(original.data, 0, data, 0, size()); + return this; + } + + @Override public DogArray_F64 resize( int size ) { + reserve(size); + this.size = size; + return this; + } + + /** + * Resizes the array and assigns the default value to every new element. + * + * @param size New size + * @param value Default value + */ + public DogArray_F64 resize( int size, double value ) { + int priorSize = this.size; + resize(size); + if (priorSize >= size) + return this; + fill(priorSize, size, value); + return this; + } + + /** + * Convenience function that will first call {@link #reset} then {@link #resize(int, double)}, ensuring + * that every element in the array will have the specified value + * + * @param size New size + * @param value New value of every element + */ + @Deprecated + public void resetResize( int size, double value ) { + reset(); + resize(size, value); + } + + /** + * Resizes and assigns the new elements (if any) to the value specified by the lambda + * + * @param size New sie + * @param op Assigns default values + */ + public DogArray_F64 resize( int size, DogLambdas.AssignIdx_F64 op ) { + int priorSize = this.size; + resize(size); + for (int i = priorSize; i < size; i++) { + data[i] = op.assign(i); + } + return this; + } + + public void fill( double value ) { + Arrays.fill(data, 0, size, value); + } + + public void fill( int idx0, int idx1, double value ) { + Arrays.fill(data, idx0, idx1, value); + } + + public boolean contains( double value ) { + for (int i = 0; i < size; i++) { + if (data[i] == value) + return true; + } + return false; + } + + @Override public void extend( int size ) { + reserve(size); + this.size = size; + } + + @SuppressWarnings("NullAway") + @Override public void reserve( int amount ) { + if (data.length >= amount) + return; + if (size == 0) { + // In this special case we can dereference the old array and this might allow the GC to free up memory + // before declaring the new array. Could be useful if the arrays are very large. + this.data = null; + this.data = new double[amount]; + } else { + var tmp = new double[amount]; + System.arraycopy(data, 0, tmp, 0, this.size); + data = tmp; + } + } + + @Override public int size() { + return size; + } + + @Override public void zero() { + Arrays.fill(data, 0, size, (double)0); + } + + @Override public DogArray_F64 copy() { + var ret = new DogArray_F64(size); + ret.setTo(this); + return ret; + } + + @Override public void flip() { + if (size <= 1) + return; + + int D = size/2; + for (int i = 0, j = size - 1; i < D; i++, j--) { + double tmp = data[i]; + data[i] = data[j]; + data[j] = tmp; + } + } + + public double pop() { + return data[--size]; + } + + /** + * Returns the index of the first element with the specified 'value'. return -1 if it wasn't found + * + * @param value Value to search for + * @return index or -1 if it's not in the list + */ + public int indexOf( double value ) { + for (int i = 0; i < size; i++) { + if (data[i] == value) + return i; + } + return -1; + } + + public int indexOfGreatest() { + if (size <= -0) + return -1; + + int selected = 0; + double best = data[0]; + + for (int i = 1; i < size; i++) { + if (data[i] > best) { + best = data[i]; + selected = i; + } + } + + return selected; + } + + public int indexOfLeast() { + if (size <= -0) + return -1; + + int selected = 0; + double best = data[0]; + + for (int i = 1; i < size; i++) { + if (data[i] < best) { + best = data[i]; + selected = i; + } + } + + return selected; + } + + @Override public void sort() { + Arrays.sort(data, 0, size); + } + + /** + * Sort but with a re-usable sorter to avoid declaring new memory + */ + public void sort( QuickSort_F64 sorter ) { + sorter.sort(data, size); + } + + /** Shuffle elements by randomly swapping them */ + public void shuffle( Random rand ) { + for (int i = 0; i < size; i++) { + int src = rand.nextInt(size - i) + i; + double tmp = data[i]; + data[i] = data[src]; + data[src] = tmp; + } + } + + public void forIdx( FunctionEachIdx func ) { + for (int i = 0; i < size; i++) { + func.process(i, data[i]); + } + } + + public void forEach( FunctionEach func ) { + for (int i = 0; i < size; i++) { + func.process(data[i]); + } + } + + public void applyIdx( FunctionApplyIdx func ) { + for (int i = 0; i < size; i++) { + data[i] = func.process(i, data[i]); + } + } + + public int count( Filter filter ) { + int total = 0; + for (int i = 0; i < size; i++) { + if (filter.include(data[i])) + total++; + } + return total; + } + + @FunctionalInterface + public interface FunctionEachIdx { + void process( int index, double value ); + } + + @FunctionalInterface + public interface FunctionEach { + void process( double value ); + } + + @FunctionalInterface + public interface FunctionApplyIdx { + double process( int index, double value ); + } + + @FunctionalInterface + public interface Filter { + boolean include( double value ); + } +} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/struct/DogArray_I16.java ddogleg-0.22+ds/src/org/ddogleg/struct/DogArray_I16.java --- ddogleg-0.18+ds/src/org/ddogleg/struct/DogArray_I16.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/struct/DogArray_I16.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,586 @@ +/* + * Copyright (c) 2012-2022, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.struct; + +import java.util.Arrays; +import java.util.Random; + +/** + * Growable array composed of short. + * + * @author Peter Abeles + */ +public class DogArray_I16 implements DogArrayPrimitive { + + public short[] data; + public int size; + + public DogArray_I16( int reserve ) { + data = new short[reserve]; + this.size = 0; + } + + public DogArray_I16() { + this(10); + } + + /** + * Creates a queue with the specified length as its size filled with all zeros + */ + public static DogArray_I16 zeros( int length ) { + var out = new DogArray_I16(length); + out.size = length; + return out; + } + + public static DogArray_I16 array( short... values ) { + DogArray_I16 out = zeros(values.length); + for (int i = 0; i < values.length; i++) { + out.data[i] = values[i]; + } + return out; + } + + public static DogArray_I16 array( int... values ) { + DogArray_I16 out = zeros(values.length); + for (int i = 0; i < values.length; i++) { + out.data[i] = (short)values[i]; + } + return out; + } + + /** + * Returns a new array with values containing range of integer numbers from idx0 to idx1-1. + * + * @param idx0 Lower extent, inclusive. + * @param idx1 Upper extent, exclusive. + * @return new array. + */ + public static DogArray_I16 range( int idx0, int idx1 ) { + DogArray_I16 out = zeros(idx1 - idx0); + for (int i = idx0; i < idx1; i++) { + out.data[i - idx0] = (short)i; + } + return out; + } + + /** + * Counts the number of times the specified value occurs in the list + */ + public int count( int value ) { + int total = 0; + for (int i = 0; i < size; i++) { + if (data[i] == value) + total++; + } + return total; + } + + /** + * Sees is the primitive array is equal to the values in this array + * + * @param values primitive array + * @return true if equal or false if not + */ + public boolean isEquals( short... values ) { + if (size != values.length) + return false; + for (int i = 0; i < size; i++) { + if (data[i] != values[i]) + return false; + } + return true; + } + + public boolean isEquals( int... values ) { + if (size != values.length) + return false; + for (int i = 0; i < size; i++) { + if (data[i] != values[i]) + return false; + } + return true; + } + + public boolean isEquals( DogArray_I16 values ) { + if (size != values.size) + return false; + for (int i = 0; i < size; i++) { + if (data[i] != values.data[i]) + return false; + } + return true; + } + + @Override + public DogArray_I16 reset() { + size = 0; + return this; + } + + public void addAll( DogArray_I16 queue ) { + if (size + queue.size > data.length) { + short[] temp = new short[(size + queue.size)*2]; + System.arraycopy(data, 0, temp, 0, size); + data = temp; + } + System.arraycopy(queue.data, 0, data, size, queue.size); + size += queue.size; + } + + public void addAll( short[] array, int startIndex, int endIndex ) { + if (endIndex > array.length) + throw new IllegalAccessError("endIndex is larger than input array. " + endIndex + " > " + array.length); + + int arraySize = endIndex - startIndex; + + if (size + arraySize > data.length) { + short[] temp = new short[(size + arraySize)*2]; + System.arraycopy(data, 0, temp, 0, size); + data = temp; + } + System.arraycopy(array, startIndex, data, size, arraySize); + size += arraySize; + } + + public void add( int val ) { + push(val); + } + + public void push( int val ) { + if (size == data.length) { + short[] temp; + try { + temp = new short[size*2 + 5]; + } catch (OutOfMemoryError e) { + System.gc(); + temp = new short[3*size/2]; + } + System.arraycopy(data, 0, temp, 0, size); + data = temp; + } + data[size++] = (short)val; + } + + /** + * Sets this array to be equal to the array segment + * + * @param array (Input) source array + * @param offset first index + * @param length number of elements to copy + */ + public DogArray_I16 setTo( short[] array, int offset, int length ) { + resize(length); + System.arraycopy(array, offset, data, 0, length); + return this; + } + + /** + * Set's the value of this array to the passed in raw array. + * + * @param src (Input) The input array + * @return A reference to "this" to allow chaining of commands + */ + public DogArray_I16 setTo( short... src ) { + setTo(src, 0, src.length); + return this; + } + + /** + * Creates a new primitive array which is a copy. + */ + public short[] toArray() { + short[] out = new short[size]; + System.arraycopy(data, 0, out, 0, size); + return out; + } + + public void remove( int index ) { + for (int i = index + 1; i < size; i++) { + data[i - 1] = data[i]; + } + size--; + } + + /** + * Removes elements from the list starting at 'first' and ending at 'last' + * + * @param first First index you wish to remove. Inclusive. + * @param last Last index you wish to remove. Inclusive. + */ + public void remove( int first, int last ) { + if (last < first) + throw new IllegalArgumentException("first <= last. first=" + first + " last=" + last); + if (last >= size) + throw new IllegalArgumentException("last must be less than the max size. last=" + last + " size=" + size); + + int delta = last - first + 1; + for (int i = last + 1; i < size; i++) { + data[i - delta] = data[i]; + } + size -= delta; + } + + /** + * Inserts the value at the specified index and shifts all the other values down. + */ + public void insert( int index, int value ) { + if (size == data.length) { + short[] temp = new short[size*2 + 5]; + System.arraycopy(data, 0, temp, 0, index); + temp[index] = (short)value; + System.arraycopy(data, index, temp, index + 1, size - index); + this.data = temp; + size++; + } else { + size++; + for (int i = size - 1; i > index; i--) { + data[i] = data[i - 1]; + } + data[index] = (short)value; + } + } + + /** + * Removes the specified index from the array by swapping it with last element. Does not preserve order + * but has a runtime of O(1). + * + * @param index The index to be removed. + * @return The removed object + */ + public short removeSwap( int index ) { + if (index < 0 || index >= size) + throw new IllegalArgumentException("Out of bounds. index=" + index + " max size " + size); + short ret = data[index]; + size -= 1; + data[index] = data[size]; + return ret; + } + + public short removeTail() { + if (size > 0) { + size--; + return data[size]; + } else { + throw new RuntimeException("Size zero, no tail"); + } + } + + public short get( int index ) { + if (index < 0 || index >= size) + throw new IndexOutOfBoundsException("index = " + index + " size = " + size); + return data[index]; + } + + public short getTail() { + if (size == 0) + throw new IndexOutOfBoundsException("Array is empty"); + return data[size - 1]; + } + + /** + * Returns an element starting from the end of the list. 0 = size -1 + */ + public short getTail( int index ) { + if (index < 0 || index >= size) + throw new IndexOutOfBoundsException("index = " + index + " size = " + size); + return data[size - index - 1]; + } + + public void setTail( int index, int value ) { + if (index < 0 || index >= size) + throw new IndexOutOfBoundsException("index = " + index + " size = " + size); + data[size - index - 1] = (short)value; + } + + /** + * Gets the value at the index which corresponds to the specified fraction + * + * @param fraction 0 to 1 inclusive + * @return value at fraction + */ + public short getFraction( double fraction ) { + return get((int)((size - 1)*fraction)); + } + + public short unsafe_get( int index ) { + return data[index]; + } + + public void set( int index, int value ) { + data[index] = (short)value; + } + + @Override public DogArray_I16 setTo( DogArray_I16 original ) { + resize(original.size); + System.arraycopy(original.data, 0, data, 0, size()); + return this; + } + + @Override public DogArray_I16 resize( int size ) { + reserve(size); + this.size = size; + return this; + } + + /** + * Resizes the array and assigns the default value to every new element. + * + * @param size New size + * @param value Default value + */ + public DogArray_I16 resize( int size, short value ) { + int priorSize = this.size; + resize(size); + if (priorSize >= size) + return this; + fill(priorSize, size, value); + return this; + } + + /** + * Convenience function that will first call {@link #reset} then {@link #resize(int, short)}, ensuring + * that every element in the array will have the specified value + * + * @param size New size + * @param value New value of every element + */ + @Deprecated + public void resetResize( int size, short value ) { + reset(); + resize(size, value); + } + + /** + * Resizes and assigns the new elements (if any) to the value specified by the lambda + * + * @param size New sie + * @param op Assigns default values + */ + public void resize( int size, DogLambdas.AssignIdx_I16 op ) { + int priorSize = this.size; + resize(size); + for (int i = priorSize; i < size; i++) { + data[i] = op.assign(i); + } + } + + public void fill( short value ) { + Arrays.fill(data, 0, size, value); + } + + public void fill( int idx0, int idx1, short value ) { + Arrays.fill(data, idx0, idx1, value); + } + + public boolean contains( short value ) { + for (int i = 0; i < size; i++) { + if (data[i] == value) + return true; + } + return false; + } + + @Override public void extend( int size ) { + reserve(size); + this.size = size; + } + + @SuppressWarnings("NullAway") + @Override public void reserve( int amount ) { + if (data.length >= amount) + return; + if (size == 0) { + // In this special case we can dereference the old array and this might allow the GC to free up memory + // before declaring the new array. Could be useful if the arrays are very large. + this.data = null; + this.data = new short[amount]; + } else { + var tmp = new short[amount]; + System.arraycopy(data, 0, tmp, 0, this.size); + data = tmp; + } + } + + @Override public int size() { + return size; + } + + @Override public void zero() { + Arrays.fill(data, 0, size, (short)0); + } + + @Override public DogArray_I16 copy() { + var ret = new DogArray_I16(size); + ret.setTo(this); + return ret; + } + + @Override public void flip() { + if (size <= 1) + return; + + int D = size/2; + for (int i = 0, j = size - 1; i < D; i++, j--) { + short tmp = data[i]; + data[i] = data[j]; + data[j] = tmp; + } + } + + /** + * Prints the queue to stdout as a hex array + */ + public void printHex() { + System.out.print("[ "); + for (int i = 0; i < size; i++) { + System.out.printf("0x%04X ", data[i]); + } + System.out.print("]"); + } + + public static DogArray_I16 parseHex( String message ) { + message = message.replaceAll("\\[", ""); + message = message.replaceAll("\\]", ""); + message = message.replaceAll(" ", ""); + + String[] words = message.split(","); + + var out = new DogArray_I16(words.length); + out.size = words.length; + + for (int i = 0; i < words.length; i++) { + out.data[i] = Integer.decode(words[i]).shortValue(); + } + return out; + } + + public short pop() { + return data[--size]; + } + + /** + * Returns the index of the first element with the specified 'value'. return -1 if it wasn't found + * + * @param value Value to search for + * @return index or -1 if it's not in the list + */ + public int indexOf( int value ) { + for (int i = 0; i < size; i++) { + if (data[i] == value) + return i; + } + return -1; + } + + public int indexOfGreatest() { + if (size <= -0) + return -1; + + int selected = 0; + short best = data[0]; + + for (int i = 1; i < size; i++) { + if (data[i] > best) { + best = data[i]; + selected = i; + } + } + + return selected; + } + + public int indexOfLeast() { + if (size <= -0) + return -1; + + int selected = 0; + short best = data[0]; + + for (int i = 1; i < size; i++) { + if (data[i] < best) { + best = data[i]; + selected = i; + } + } + + return selected; + } + + @Override public void sort() { + Arrays.sort(data, 0, size); + } + + /** Shuffle elements by randomly swapping them */ + public void shuffle( Random rand ) { + for (int i = 0; i < size; i++) { + int src = rand.nextInt(size - i) + i; + short tmp = data[i]; + data[i] = data[src]; + data[src] = tmp; + } + } + + public void forIdx( FunctionEachIdx func ) { + for (int i = 0; i < size; i++) { + func.process(i, data[i]); + } + } + + public void forEach( FunctionEach func ) { + for (int i = 0; i < size; i++) { + func.process(data[i]); + } + } + + public void applyIdx( FunctionApplyIdx func ) { + for (int i = 0; i < size; i++) { + data[i] = func.process(i, data[i]); + } + } + + public int count( Filter filter ) { + int total = 0; + for (int i = 0; i < size; i++) { + if (filter.include(data[i])) + total++; + } + return total; + } + + @FunctionalInterface + public interface FunctionEachIdx { + void process( int index, short value ); + } + + @FunctionalInterface + public interface FunctionEach { + void process( short value ); + } + + @FunctionalInterface + public interface FunctionApplyIdx { + short process( int index, short value ); + } + + @FunctionalInterface + public interface Filter { + boolean include( int value ); + } +} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/struct/DogArray_I32.java ddogleg-0.22+ds/src/org/ddogleg/struct/DogArray_I32.java --- ddogleg-0.18+ds/src/org/ddogleg/struct/DogArray_I32.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/struct/DogArray_I32.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,565 @@ +/* + * Copyright (c) 2012-2022, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.struct; + +import org.ddogleg.sorting.QuickSort_S32; + +import java.util.Arrays; +import java.util.Random; + +/** + * Growable array composed of ints. + * + * @author Peter Abeles + */ +public class DogArray_I32 implements DogArrayPrimitive { + + public int[] data; + public int size; + + public DogArray_I32( int reserve ) { + data = new int[reserve]; + this.size = 0; + } + + public DogArray_I32() { + this(10); + } + + /** + * Creates a queue with the specified length as its size filled with all zeros + */ + public static DogArray_I32 zeros( int length ) { + var out = new DogArray_I32(length); + out.size = length; + return out; + } + + public static DogArray_I32 array( int... values ) { + DogArray_I32 out = zeros(values.length); + for (int i = 0; i < values.length; i++) { + out.data[i] = values[i]; + } + return out; + } + + /** + * Returns a new array with values containing range of integer numbers from idx0 to idx1-1. + * + * @param idx0 Lower extent, inclusive. + * @param idx1 Upper extent, exclusive. + * @return new array. + */ + public static DogArray_I32 range( int idx0, int idx1 ) { + DogArray_I32 out = zeros(idx1 - idx0); + for (int i = idx0; i < idx1; i++) { + out.data[i - idx0] = (int)i; + } + return out; + } + + /** + * Counts the number of times the specified value occurs in the list + */ + public int count( int value ) { + int total = 0; + for (int i = 0; i < size; i++) { + if (data[i] == value) + total++; + } + return total; + } + + /** + * Sees is the primitive array is equal to the values in this array + * + * @param values primitive array + * @return true if equal or false if not + */ + public boolean isEquals( int... values ) { + if (size != values.length) + return false; + for (int i = 0; i < size; i++) { + if (data[i] != values[i]) + return false; + } + return true; + } + + public boolean isEquals( DogArray_I32 values ) { + if (size != values.size) + return false; + for (int i = 0; i < size; i++) { + if (data[i] != values.data[i]) + return false; + } + return true; + } + + @Override + public DogArray_I32 reset() { + size = 0; + return this; + } + + public void addAll( DogArray_I32 queue ) { + if (size + queue.size > data.length) { + int[] temp = new int[(size + queue.size)*2]; + System.arraycopy(data, 0, temp, 0, size); + data = temp; + } + System.arraycopy(queue.data, 0, data, size, queue.size); + size += queue.size; + } + + public void addAll( int[] array, int startIndex, int endIndex ) { + if (endIndex > array.length) + throw new IllegalAccessError("endIndex is larger than input array. " + endIndex + " > " + array.length); + + int arraySize = endIndex - startIndex; + + if (size + arraySize > data.length) { + int[] temp = new int[(size + arraySize)*2]; + System.arraycopy(data, 0, temp, 0, size); + data = temp; + } + System.arraycopy(array, startIndex, data, size, arraySize); + size += arraySize; + } + + public void add( int val ) { + push(val); + } + + public void push( int val ) { + if (size == data.length) { + int[] temp; + try { + temp = new int[size*2 + 5]; + } catch (OutOfMemoryError e) { + System.gc(); + temp = new int[3*size/2]; + } + System.arraycopy(data, 0, temp, 0, size); + data = temp; + } + data[size++] = (int)val; + } + + /** + * Sets this array to be equal to the array segment + * + * @param array (Input) source array + * @param offset first index + * @param length number of elements to copy + */ + public void setTo( int[] array, int offset, int length ) { + resize(length); + System.arraycopy(array, offset, data, 0, length); + } + + /** + * Set's the value of this array to the passed in raw array. + * + * @param src (Input) The input array + * @return A reference to "this" to allow chaining of commands + */ + public DogArray_I32 setTo( int... src ) { + setTo(src, 0, src.length); + return this; + } + + /** + * Creates a new primitive array which is a copy. + */ + public int[] toArray() { + int[] out = new int[size]; + System.arraycopy(data, 0, out, 0, size); + return out; + } + + public void remove( int index ) { + for (int i = index + 1; i < size; i++) { + data[i - 1] = data[i]; + } + size--; + } + + /** + * Removes elements from the list starting at 'first' and ending at 'last' + * + * @param first First index you wish to remove. Inclusive. + * @param last Last index you wish to remove. Inclusive. + */ + public void remove( int first, int last ) { + if (last < first) + throw new IllegalArgumentException("first <= last. first=" + first + " last=" + last); + if (last >= size) + throw new IllegalArgumentException("last must be less than the max size. last=" + last + " size=" + size); + + int delta = last - first + 1; + for (int i = last + 1; i < size; i++) { + data[i - delta] = data[i]; + } + size -= delta; + } + + /** + * Inserts the value at the specified index and shifts all the other values down. + */ + public void insert( int index, int value ) { + if (size == data.length) { + int[] temp = new int[size*2 + 5]; + System.arraycopy(data, 0, temp, 0, index); + temp[index] = value; + System.arraycopy(data, index, temp, index + 1, size - index); + this.data = temp; + size++; + } else { + size++; + for (int i = size - 1; i > index; i--) { + data[i] = data[i - 1]; + } + data[index] = value; + } + } + + /** + * Removes the specified index from the array by swapping it with last element. Does not preserve order + * but has a runtime of O(1). + * + * @param index The index to be removed. + * @return The removed object + */ + public int removeSwap( int index ) { + if (index < 0 || index >= size) + throw new IllegalArgumentException("Out of bounds. index=" + index + " max size " + size); + int ret = data[index]; + size -= 1; + data[index] = data[size]; + return ret; + } + + public int removeTail() { + if (size > 0) { + size--; + return data[size]; + } else { + throw new RuntimeException("Size zero, no tail"); + } + } + + public int get( int index ) { + if (index < 0 || index >= size) + throw new IndexOutOfBoundsException("index = " + index + " size = " + size); + return data[index]; + } + + public int getTail() { + if (size == 0) + throw new IndexOutOfBoundsException("Array is empty"); + return data[size - 1]; + } + + /** + * Returns an element starting from the end of the list. 0 = size -1 + */ + public int getTail( int index ) { + if (index < 0 || index >= size) + throw new IndexOutOfBoundsException("index = " + index + " size = " + size); + return data[size - index - 1]; + } + + public void setTail( int index, int value ) { + if (index < 0 || index >= size) + throw new IndexOutOfBoundsException("index = " + index + " size = " + size); + data[size - index - 1] = value; + } + + /** + * Gets the value at the index which corresponds to the specified fraction + * + * @param fraction 0 to 1 inclusive + * @return value at fraction + */ + public int getFraction( double fraction ) { + return get((int)((size - 1)*fraction)); + } + + public int unsafe_get( int index ) { + return data[index]; + } + + public void set( int index, int value ) { + data[index] = value; + } + + @Override public DogArray_I32 setTo( DogArray_I32 original ) { + resize(original.size); + System.arraycopy(original.data, 0, data, 0, size()); + return this; + } + + @Override public DogArray_I32 resize( int size ) { + reserve(size); + this.size = size; + return this; + } + + /** + * Resizes the array and assigns the default value to every new element. + * + * @param size New size + * @param value Default value + */ + public DogArray_I32 resize( int size, int value ) { + int priorSize = this.size; + resize(size); + if (priorSize >= size) + return this; + fill(priorSize, size, value); + return this; + } + + /** + * Convenience function that will first call {@link #reset} then {@link #resize(int, int)}, ensuring + * that every element in the array will have the specified value + * + * @param size New size + * @param value New value of every element + */ + @Deprecated + public void resetResize( int size, int value ) { + reset(); + resize(size, value); + } + + /** + * Resizes and assigns the new elements (if any) to the value specified by the lambda + * + * @param size New sie + * @param op Assigns default values + */ + public void resize( int size, DogLambdas.AssignIdx_I32 op ) { + int priorSize = this.size; + resize(size); + for (int i = priorSize; i < size; i++) { + data[i] = op.assign(i); + } + } + + public void fill( int value ) { + Arrays.fill(data, 0, size, value); + } + + public void fill( int idx0, int idx1, int value ) { + Arrays.fill(data, idx0, idx1, value); + } + + public boolean contains( int value ) { + for (int i = 0; i < size; i++) { + if (data[i] == value) + return true; + } + return false; + } + + @Override public void extend( int size ) { + reserve(size); + this.size = size; + } + + @SuppressWarnings("NullAway") + @Override public void reserve( int amount ) { + if (data.length >= amount) + return; + if (size == 0) { + // In this special case we can dereference the old array and this might allow the GC to free up memory + // before declaring the new array. Could be useful if the arrays are very large. + this.data = null; + this.data = new int[amount]; + } else { + var tmp = new int[amount]; + System.arraycopy(data, 0, tmp, 0, this.size); + data = tmp; + } + } + + @Override public int size() { + return size; + } + + @Override public void zero() { + Arrays.fill(data, 0, size, (int)0); + } + + @Override public DogArray_I32 copy() { + var ret = new DogArray_I32(size); + ret.setTo(this); + return ret; + } + + @Override public void flip() { + if (size <= 1) + return; + + int D = size/2; + for (int i = 0, j = size - 1; i < D; i++, j--) { + int tmp = data[i]; + data[i] = data[j]; + data[j] = tmp; + } + } + + public static DogArray_I32 parseHex( String message ) { + message = message.replaceAll("\\[", ""); + message = message.replaceAll("\\]", ""); + message = message.replaceAll(" ", ""); + + String[] words = message.split(","); + + var out = new DogArray_I32(words.length); + out.size = words.length; + + for (int i = 0; i < words.length; i++) { + out.data[i] = Integer.decode(words[i]).byteValue(); + } + return out; + } + + public int pop() { + return data[--size]; + } + + /** + * Returns the index of the first element with the specified 'value'. return -1 if it wasn't found + * + * @param value Value to search for + * @return index or -1 if it's not in the list + */ + public int indexOf( int value ) { + for (int i = 0; i < size; i++) { + if (data[i] == value) + return i; + } + return -1; + } + + public int indexOfGreatest() { + if (size <= -0) + return -1; + + int selected = 0; + int best = data[0]; + + for (int i = 1; i < size; i++) { + if (data[i] > best) { + best = data[i]; + selected = i; + } + } + + return selected; + } + + public int indexOfLeast() { + if (size <= -0) + return -1; + + int selected = 0; + int best = data[0]; + + for (int i = 1; i < size; i++) { + if (data[i] < best) { + best = data[i]; + selected = i; + } + } + + return selected; + } + + @Override public void sort() { + Arrays.sort(data, 0, size); + } + + /** + * Sort but with a re-usable sorter to avoid declaring new memory + */ + public void sort( QuickSort_S32 sorter ) { + sorter.sort(data, size); + } + + /** Shuffle elements by randomly swapping them */ + public void shuffle( Random rand ) { + for (int i = 0; i < size; i++) { + int src = rand.nextInt(size - i); + int tmp = data[i]; + data[i] = data[src]; + data[src] = tmp; + } + } + + public void forIdx( FunctionEachIdx func ) { + for (int i = 0; i < size; i++) { + func.process(i, data[i]); + } + } + + public void forEach( FunctionEach func ) { + for (int i = 0; i < size; i++) { + func.process(data[i]); + } + } + + public void applyIdx( FunctionApplyIdx func ) { + for (int i = 0; i < size; i++) { + data[i] = func.process(i, data[i]); + } + } + + public int count( Filter filter ) { + int total = 0; + for (int i = 0; i < size; i++) { + if (filter.include(data[i])) + total++; + } + return total; + } + + @FunctionalInterface + public interface FunctionEachIdx { + void process( int index, int value ); + } + + @FunctionalInterface + public interface FunctionEach { + void process( int value ); + } + + @FunctionalInterface + public interface FunctionApplyIdx { + int process( int index, int value ); + } + + @FunctionalInterface + public interface Filter { + boolean include( int value ); + } +} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/struct/DogArray_I64.java ddogleg-0.22+ds/src/org/ddogleg/struct/DogArray_I64.java --- ddogleg-0.18+ds/src/org/ddogleg/struct/DogArray_I64.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/struct/DogArray_I64.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,541 @@ +/* + * Copyright (c) 2012-2022, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.struct; + +import java.util.Arrays; +import java.util.Random; + +/** + * Growable array composed of longs. + * + * @author Peter Abeles + */ +public class DogArray_I64 implements DogArrayPrimitive { + + public long[] data; + public int size; + + public DogArray_I64( int reserve ) { + data = new long[reserve]; + this.size = 0; + } + + public DogArray_I64() { + this(10); + } + + /** + * Creates a queue with the specified length as its size filled with all zeros + */ + public static DogArray_I64 zeros( int length ) { + var out = new DogArray_I64(length); + out.size = length; + return out; + } + + public static DogArray_I64 array( long... values ) { + DogArray_I64 out = zeros(values.length); + for (int i = 0; i < values.length; i++) { + out.data[i] = values[i]; + } + return out; + } + + /** + * Returns a new array with values containing range of integer numbers from idx0 to idx1-1. + * + * @param idx0 Lower extent, inclusive. + * @param idx1 Upper extent, exclusive. + * @return new array. + */ + public static DogArray_I64 range( int idx0, int idx1 ) { + DogArray_I64 out = zeros(idx1 - idx0); + for (int i = idx0; i < idx1; i++) { + out.data[i - idx0] = (long)i; + } + return out; + } + + /** + * Counts the number of times the specified value occurs in the list + */ + public int count( long value ) { + int total = 0; + for (int i = 0; i < size; i++) { + if (data[i] == value) + total++; + } + return total; + } + + /** + * Sees is the primitive array is equal to the values in this array + * + * @param values primitive array + * @return true if equal or false if not + */ + public boolean isEquals( long... values ) { + if (size != values.length) + return false; + for (int i = 0; i < size; i++) { + if (data[i] != values[i]) + return false; + } + return true; + } + + public boolean isEquals( DogArray_I64 values ) { + if (size != values.size) + return false; + for (int i = 0; i < size; i++) { + if (data[i] != values.data[i]) + return false; + } + return true; + } + + @Override + public DogArray_I64 reset() { + size = 0; + return this; + } + + public void addAll( DogArray_I64 queue ) { + if (size + queue.size > data.length) { + long[] temp = new long[(size + queue.size)*2]; + System.arraycopy(data, 0, temp, 0, size); + data = temp; + } + System.arraycopy(queue.data, 0, data, size, queue.size); + size += queue.size; + } + + public void addAll( long[] array, int startIndex, int endIndex ) { + if (endIndex > array.length) + throw new IllegalAccessError("endIndex is larger than input array. " + endIndex + " > " + array.length); + + int arraySize = endIndex - startIndex; + + if (size + arraySize > data.length) { + long[] temp = new long[(size + arraySize)*2]; + System.arraycopy(data, 0, temp, 0, size); + data = temp; + } + System.arraycopy(array, startIndex, data, size, arraySize); + size += arraySize; + } + + public void add( long val ) { + push(val); + } + + public void push( long val ) { + if (size == data.length) { + long[] temp; + try { + temp = new long[size*2 + 5]; + } catch (OutOfMemoryError e) { + System.gc(); + temp = new long[3*size/2]; + } + System.arraycopy(data, 0, temp, 0, size); + data = temp; + } + data[size++] = (long)val; + } + + /** + * Sets this array to be equal to the array segment + * + * @param array (Input) source array + * @param offset first index + * @param length number of elements to copy + */ + public void setTo( long[] array, int offset, int length ) { + resize(length); + System.arraycopy(array, offset, data, 0, length); + } + + /** + * Set's the value of this array to the passed in raw array. + * + * @param src (Input) The input array + * @return A reference to "this" to allow chaining of commands + */ + public DogArray_I64 setTo( long... src ) { + setTo(src, 0, src.length); + return this; + } + + /** + * Creates a new primitive array which is a copy. + */ + public long[] toArray() { + long[] out = new long[size]; + System.arraycopy(data, 0, out, 0, size); + return out; + } + + public void remove( int index ) { + for (int i = index + 1; i < size; i++) { + data[i - 1] = data[i]; + } + size--; + } + + /** + * Removes elements from the list starting at 'first' and ending at 'last' + * + * @param first First index you wish to remove. Inclusive. + * @param last Last index you wish to remove. Inclusive. + */ + public void remove( int first, int last ) { + if (last < first) + throw new IllegalArgumentException("first <= last. first=" + first + " last=" + last); + if (last >= size) + throw new IllegalArgumentException("last must be less than the max size. last=" + last + " size=" + size); + + int delta = last - first + 1; + for (int i = last + 1; i < size; i++) { + data[i - delta] = data[i]; + } + size -= delta; + } + + /** + * Inserts the value at the specified index and shifts all the other values down. + */ + public void insert( int index, long value ) { + if (size == data.length) { + long[] temp = new long[size*2 + 5]; + System.arraycopy(data, 0, temp, 0, index); + temp[index] = value; + System.arraycopy(data, index, temp, index + 1, size - index); + this.data = temp; + size++; + } else { + size++; + for (int i = size - 1; i > index; i--) { + data[i] = data[i - 1]; + } + data[index] = value; + } + } + + /** + * Removes the specified index from the array by swapping it with last element. Does not preserve order + * but has a runtime of O(1). + * + * @param index The index to be removed. + * @return The removed object + */ + public long removeSwap( int index ) { + if (index < 0 || index >= size) + throw new IllegalArgumentException("Out of bounds. index=" + index + " max size " + size); + long ret = data[index]; + size -= 1; + data[index] = data[size]; + return ret; + } + + public long removeTail() { + if (size > 0) { + size--; + return data[size]; + } else { + throw new RuntimeException("Size zero, no tail"); + } + } + + public long get( int index ) { + if (index < 0 || index >= size) + throw new IndexOutOfBoundsException("index = " + index + " size = " + size); + return data[index]; + } + + public long getTail() { + if (size == 0) + throw new IndexOutOfBoundsException("Array is empty"); + return data[size - 1]; + } + + /** + * Returns an element starting from the end of the list. 0 = size -1 + */ + public long getTail( int index ) { + if (index < 0 || index >= size) + throw new IndexOutOfBoundsException("index = " + index + " size = " + size); + return data[size - index - 1]; + } + + public void setTail( int index, long value ) { + if (index < 0 || index >= size) + throw new IndexOutOfBoundsException("index = " + index + " size = " + size); + data[size - index - 1] = value; + } + + /** + * Gets the value at the index which corresponds to the specified fraction + * + * @param fraction 0 to 1 inclusive + * @return value at fraction + */ + public long getFraction( double fraction ) { + return get((int)((size - 1)*fraction)); + } + + public long unsafe_get( int index ) { + return data[index]; + } + + public void set( int index, long value ) { + data[index] = value; + } + + @Override public DogArray_I64 setTo( DogArray_I64 original ) { + resize(original.size); + System.arraycopy(original.data, 0, data, 0, size()); + return this; + } + + @Override public DogArray_I64 resize( int size ) { + reserve(size); + this.size = size; + return this; + } + + /** + * Resizes the array and assigns the default value to every element. + * + * @param size New size + * @param value Default value + */ + public DogArray_I64 resize( int size, long value ) { + int priorSize = this.size; + resize(size); + if (priorSize >= size) + return this; + fill(priorSize, size, value); + return this; + } + + /** + * Convenience function that will first call {@link #reset} then {@link #resize(int, long)}, ensuring + * that every element in the array will have the specified value + * + * @param size New size + * @param value New value of every element + */ + @Deprecated + public void resetResize( int size, long value ) { + reset(); + resize(size, value); + } + + /** + * Resizes and assigns the new elements (if any) to the value specified by the lambda + * + * @param size New sie + * @param op Assigns default values + */ + public DogArray_I64 resize( int size, DogLambdas.AssignIdx_I64 op ) { + int priorSize = this.size; + resize(size); + for (int i = priorSize; i < size; i++) { + data[i] = op.assign(i); + } + return this; + } + + public void fill( long value ) { + Arrays.fill(data, 0, size, value); + } + + public void fill( int idx0, int idx1, long value ) { + Arrays.fill(data, idx0, idx1, value); + } + + public boolean contains( long value ) { + for (int i = 0; i < size; i++) { + if (data[i] == value) + return true; + } + return false; + } + + @Override public void extend( int size ) { + reserve(size); + this.size = size; + } + + @SuppressWarnings("NullAway") + @Override public void reserve( int amount ) { + if (data.length >= amount) + return; + if (size == 0) { + // In this special case we can dereference the old array and this might allow the GC to free up memory + // before declaring the new array. Could be useful if the arrays are very large. + this.data = null; + this.data = new long[amount]; + } else { + var tmp = new long[amount]; + System.arraycopy(data, 0, tmp, 0, this.size); + data = tmp; + } + } + + @Override public int size() { + return size; + } + + @Override public void zero() { + Arrays.fill(data, 0, size, (long)0); + } + + @Override public DogArray_I64 copy() { + var ret = new DogArray_I64(size); + ret.setTo(this); + return ret; + } + + @Override public void flip() { + if (size <= 1) + return; + + int D = size/2; + for (int i = 0, j = size - 1; i < D; i++, j--) { + long tmp = data[i]; + data[i] = data[j]; + data[j] = tmp; + } + } + + public long pop() { + return data[--size]; + } + + /** + * Returns the index of the first element with the specified 'value'. return -1 if it wasn't found + * + * @param value Value to search for + * @return index or -1 if it's not in the list + */ + public int indexOf( long value ) { + for (int i = 0; i < size; i++) { + if (data[i] == value) + return i; + } + return -1; + } + + public int indexOfGreatest() { + if (size <= -0) + return -1; + + int selected = 0; + long best = data[0]; + + for (int i = 1; i < size; i++) { + if (data[i] > best) { + best = data[i]; + selected = i; + } + } + + return selected; + } + + public int indexOfLeast() { + if (size <= -0) + return -1; + + int selected = 0; + long best = data[0]; + + for (int i = 1; i < size; i++) { + if (data[i] < best) { + best = data[i]; + selected = i; + } + } + + return selected; + } + + @Override public void sort() { + Arrays.sort(data, 0, size); + } + + /** Shuffle elements by randomly swapping them */ + public void shuffle( Random rand ) { + for (int i = 0; i < size; i++) { + int src = rand.nextInt(size - i) + i; + long tmp = data[i]; + data[i] = data[src]; + data[src] = tmp; + } + } + + public void forIdx( FunctionEachIdx func ) { + for (int i = 0; i < size; i++) { + func.process(i, data[i]); + } + } + + public void forEach( FunctionEach func ) { + for (int i = 0; i < size; i++) { + func.process(data[i]); + } + } + + public void applyIdx( FunctionApplyIdx func ) { + for (int i = 0; i < size; i++) { + data[i] = func.process(i, data[i]); + } + } + + public int count( Filter filter ) { + int total = 0; + for (int i = 0; i < size; i++) { + if (filter.include(data[i])) + total++; + } + return total; + } + + @FunctionalInterface + public interface FunctionEachIdx { + void process( int index, long value ); + } + + @FunctionalInterface + public interface FunctionEach { + void process( long value ); + } + + @FunctionalInterface + public interface FunctionApplyIdx { + long process( int index, long value ); + } + + @FunctionalInterface + public interface Filter { + boolean include( long value ); + } +} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/struct/DogArray_I8.java ddogleg-0.22+ds/src/org/ddogleg/struct/DogArray_I8.java --- ddogleg-0.18+ds/src/org/ddogleg/struct/DogArray_I8.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/struct/DogArray_I8.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,587 @@ +/* + * Copyright (c) 2012-2022, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.struct; + +import java.util.Arrays; +import java.util.Random; + +/** + * Growable array composed of bytes. + * + * @author Peter Abeles + */ +public class DogArray_I8 implements DogArrayPrimitive { + + public byte[] data; + public int size; + + public DogArray_I8( int reserve ) { + data = new byte[reserve]; + this.size = 0; + } + + public DogArray_I8() { + this(10); + } + + /** + * Creates a queue with the specified length as its size filled with all zeros + */ + public static DogArray_I8 zeros( int length ) { + var out = new DogArray_I8(length); + out.size = length; + return out; + } + + public static DogArray_I8 array( byte... values ) { + DogArray_I8 out = zeros(values.length); + for (int i = 0; i < values.length; i++) { + out.data[i] = values[i]; + } + return out; + } + + public static DogArray_I8 array( int... values ) { + DogArray_I8 out = zeros(values.length); + for (int i = 0; i < values.length; i++) { + out.data[i] = (byte)values[i]; + } + return out; + } + + /** + * Returns a new array with values containing range of integer numbers from idx0 to idx1-1. + * + * @param idx0 Lower extent, inclusive. + * @param idx1 Upper extent, exclusive. + * @return new array. + */ + public static DogArray_I8 range( int idx0, int idx1 ) { + DogArray_I8 out = zeros(idx1 - idx0); + for (int i = idx0; i < idx1; i++) { + out.data[i - idx0] = (byte)i; + } + return out; + } + + /** + * Counts the number of times the specified value occurs in the list + */ + public int count( int value ) { + int total = 0; + for (int i = 0; i < size; i++) { + if (data[i] == value) + total++; + } + return total; + } + + /** + * Sees is the primitive array is equal to the values in this array + * + * @param values primitive array + * @return true if equal or false if not + */ + public boolean isEquals( byte... values ) { + if (size != values.length) + return false; + for (int i = 0; i < size; i++) { + if (data[i] != values[i]) + return false; + } + return true; + } + + public boolean isEquals( int... values ) { + if (size != values.length) + return false; + for (int i = 0; i < size; i++) { + if (data[i] != values[i]) + return false; + } + return true; + } + + public boolean isEquals( DogArray_I8 values ) { + if (size != values.size) + return false; + for (int i = 0; i < size; i++) { + if (data[i] != values.data[i]) + return false; + } + return true; + } + + @Override + public DogArray_I8 reset() { + size = 0; + return this; + } + + public void addAll( DogArray_I8 queue ) { + if (size + queue.size > data.length) { + byte[] temp = new byte[(size + queue.size)*2]; + System.arraycopy(data, 0, temp, 0, size); + data = temp; + } + System.arraycopy(queue.data, 0, data, size, queue.size); + size += queue.size; + } + + public void addAll( byte[] array, int startIndex, int endIndex ) { + if (endIndex > array.length) + throw new IllegalAccessError("endIndex is larger than input array. " + endIndex + " > " + array.length); + + int arraySize = endIndex - startIndex; + + if (size + arraySize > data.length) { + byte[] temp = new byte[(size + arraySize)*2]; + System.arraycopy(data, 0, temp, 0, size); + data = temp; + } + System.arraycopy(array, startIndex, data, size, arraySize); + size += arraySize; + } + + public void add( int val ) { + push(val); + } + + public void push( int val ) { + if (size == data.length) { + byte[] temp; + try { + temp = new byte[size*2 + 5]; + } catch (OutOfMemoryError e) { + System.gc(); + temp = new byte[3*size/2]; + } + System.arraycopy(data, 0, temp, 0, size); + data = temp; + } + data[size++] = (byte)val; + } + + /** + * Sets this array to be equal to the array segment + * + * @param array (Input) source array + * @param offset first index + * @param length number of elements to copy + */ + public DogArray_I8 setTo( byte[] array, int offset, int length ) { + resize(length); + System.arraycopy(array, offset, data, 0, length); + return this; + } + + /** + * Set's the value of this array to the passed in raw array. + * + * @param src (Input) The input array + * @return A reference to "this" to allow chaining of commands + */ + public DogArray_I8 setTo( byte... src ) { + setTo(src, 0, src.length); + return this; + } + + /** + * Creates a new primitive array which is a copy. + */ + public byte[] toArray() { + byte[] out = new byte[size]; + System.arraycopy(data, 0, out, 0, size); + return out; + } + + public void remove( int index ) { + for (int i = index + 1; i < size; i++) { + data[i - 1] = data[i]; + } + size--; + } + + /** + * Removes elements from the list starting at 'first' and ending at 'last' + * + * @param first First index you wish to remove. Inclusive. + * @param last Last index you wish to remove. Inclusive. + */ + public void remove( int first, int last ) { + if (last < first) + throw new IllegalArgumentException("first <= last. first=" + first + " last=" + last); + if (last >= size) + throw new IllegalArgumentException("last must be less than the max size. last=" + last + " size=" + size); + + int delta = last - first + 1; + for (int i = last + 1; i < size; i++) { + data[i - delta] = data[i]; + } + size -= delta; + } + + /** + * Inserts the value at the specified index and shifts all the other values down. + */ + public void insert( int index, int value ) { + if (size == data.length) { + byte[] temp = new byte[size*2 + 5]; + System.arraycopy(data, 0, temp, 0, index); + temp[index] = (byte)value; + System.arraycopy(data, index, temp, index + 1, size - index); + this.data = temp; + size++; + } else { + size++; + for (int i = size - 1; i > index; i--) { + data[i] = data[i - 1]; + } + data[index] = (byte)value; + } + } + + /** + * Removes the specified index from the array by swapping it with last element. Does not preserve order + * but has a runtime of O(1). + * + * @param index The index to be removed. + * @return The removed object + */ + public byte removeSwap( int index ) { + if (index < 0 || index >= size) + throw new IllegalArgumentException("Out of bounds. index=" + index + " max size " + size); + byte ret = data[index]; + size -= 1; + data[index] = data[size]; + return ret; + } + + public byte removeTail() { + if (size > 0) { + size--; + return data[size]; + } else { + throw new RuntimeException("Size zero, no tail"); + } + } + + public byte get( int index ) { + if (index < 0 || index >= size) + throw new IndexOutOfBoundsException("index = " + index + " size = " + size); + return data[index]; + } + + public byte getTail() { + if (size == 0) + throw new IndexOutOfBoundsException("Array is empty"); + return data[size - 1]; + } + + /** + * Returns an element starting from the end of the list. 0 = size -1 + */ + public byte getTail( int index ) { + if (index < 0 || index >= size) + throw new IndexOutOfBoundsException("index = " + index + " size = " + size); + return data[size - index - 1]; + } + + public void setTail( int index, int value ) { + if (index < 0 || index >= size) + throw new IndexOutOfBoundsException("index = " + index + " size = " + size); + data[size - index - 1] = (byte)value; + } + + /** + * Gets the value at the index which corresponds to the specified fraction + * + * @param fraction 0 to 1 inclusive + * @return value at fraction + */ + public byte getFraction( double fraction ) { + return get((int)((size - 1)*fraction)); + } + + public byte unsafe_get( int index ) { + return data[index]; + } + + public void set( int index, int value ) { + data[index] = (byte)value; + } + + @Override public DogArray_I8 setTo( DogArray_I8 original ) { + resize(original.size); + System.arraycopy(original.data, 0, data, 0, size()); + return this; + } + + @Override public DogArray_I8 resize( int size ) { + reserve(size); + this.size = size; + return this; + } + + /** + * Resizes the array and assigns the default value to every new element. + * + * @param size New size + * @param value Default value + */ + public DogArray_I8 resize( int size, byte value ) { + int priorSize = this.size; + resize(size); + if (priorSize >= size ) + return this; + fill(priorSize, size, value); + return this; + } + + /** + * Convenience function that will first call {@link #reset} then {@link #resize(int, byte)}, ensuring + * that every element in the array will have the specified value + * + * @param size New size + * @param value New value of every element + */ + @Deprecated + public void resetResize( int size, byte value ) { + reset(); + resize(size, value); + } + + /** + * Resizes and assigns the new elements (if any) to the value specified by the lambda + * + * @param size New sie + * @param op Assigns default values + */ + public DogArray_I8 resize( int size, DogLambdas.AssignIdx_I8 op ) { + int priorSize = this.size; + resize(size); + for (int i = priorSize; i < size; i++) { + data[i] = op.assign(i); + } + return this; + } + + public void fill( byte value ) { + Arrays.fill(data, 0, size, value); + } + + public void fill( int idx0, int idx1, byte value ) { + Arrays.fill(data, idx0, idx1, value); + } + + public boolean contains( byte value ) { + for (int i = 0; i < size; i++) { + if (data[i] == value) + return true; + } + return false; + } + + @Override public void extend( int size ) { + reserve(size); + this.size = size; + } + + @SuppressWarnings("NullAway") + @Override public void reserve( int amount ) { + if (data.length >= amount) + return; + if (size == 0) { + // In this special case we can dereference the old array and this might allow the GC to free up memory + // before declaring the new array. Could be useful if the arrays are very large. + this.data = null; + this.data = new byte[amount]; + } else { + var tmp = new byte[amount]; + System.arraycopy(data, 0, tmp, 0, this.size); + data = tmp; + } + } + + @Override public int size() { + return size; + } + + @Override public void zero() { + Arrays.fill(data, 0, size, (byte)0); + } + + @Override public DogArray_I8 copy() { + var ret = new DogArray_I8(size); + ret.setTo(this); + return ret; + } + + @Override public void flip() { + if (size <= 1) + return; + + int D = size/2; + for (int i = 0, j = size - 1; i < D; i++, j--) { + byte tmp = data[i]; + data[i] = data[j]; + data[j] = tmp; + } + } + + /** + * Prints the queue to stdout as a hex array + */ + public void printHex() { + System.out.print("[ "); + for (int i = 0; i < size; i++) { + System.out.printf("0x%02X ",data[i]); + } + System.out.print("]"); + } + + public static DogArray_I8 parseHex( String message ) { + message = message.replaceAll("\\[",""); + message = message.replaceAll("\\]",""); + message = message.replaceAll(" ",""); + + String[] words = message.split(","); + + DogArray_I8 out = new DogArray_I8(words.length); + out.size = words.length; + + for (int i = 0; i < words.length; i++) { + out.data[i] = Integer.decode(words[i]).byteValue(); + } + return out; + } + + public byte pop() { + return data[--size]; + } + + /** + * Returns the index of the first element with the specified 'value'. return -1 if it wasn't found + * + * @param value Value to search for + * @return index or -1 if it's not in the list + */ + public int indexOf( int value ) { + for (int i = 0; i < size; i++) { + if (data[i] == value) + return i; + } + return -1; + } + + public int indexOfGreatest() { + if (size <= -0) + return -1; + + int selected = 0; + byte best = data[0]; + + for (int i = 1; i < size; i++) { + if (data[i] > best) { + best = data[i]; + selected = i; + } + } + + return selected; + } + + public int indexOfLeast() { + if (size <= -0) + return -1; + + int selected = 0; + byte best = data[0]; + + for (int i = 1; i < size; i++) { + if (data[i] < best) { + best = data[i]; + selected = i; + } + } + + return selected; + } + + @Override public void sort() { + Arrays.sort(data, 0, size); + } + + /** Shuffle elements by randomly swapping them */ + public void shuffle( Random rand ) { + for (int i = 0; i < size; i++) { + int src = rand.nextInt(size - i) + i; + byte tmp = data[i]; + data[i] = data[src]; + data[src] = tmp; + } + } + + public void forIdx( FunctionEachIdx func ) { + for (int i = 0; i < size; i++) { + func.process(i, data[i]); + } + } + + public void forEach( FunctionEach func ) { + for (int i = 0; i < size; i++) { + func.process(data[i]); + } + } + + public void applyIdx( FunctionApplyIdx func ) { + for (int i = 0; i < size; i++) { + data[i] = func.process(i, data[i]); + } + } + + public int count( Filter filter ) { + int total = 0; + for (int i = 0; i < size; i++) { + if (filter.include(data[i])) + total++; + } + return total; + } + + @FunctionalInterface + public interface FunctionEachIdx { + void process( int index, byte value ); + } + + @FunctionalInterface + public interface FunctionEach { + void process( byte value ); + } + + @FunctionalInterface + public interface FunctionApplyIdx { + byte process( int index, byte value ); + } + + @FunctionalInterface + public interface Filter { + boolean include( int value ); + } +} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/struct/DogArray.java ddogleg-0.22+ds/src/org/ddogleg/struct/DogArray.java --- ddogleg-0.18+ds/src/org/ddogleg/struct/DogArray.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/struct/DogArray.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,445 @@ +/* + * Copyright (c) 2012-2022, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.struct; + +import lombok.Getter; +import lombok.Setter; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Random; + +/** + * Growable array which automatically creates, recycles, and resets its elements. Access to internal variables + * is provided for high performant code. If a reset function is provided then when an object is created or recycled + * the reset function is called with the objective of giving the object a repeatable initial state. + * + * @author Peter Abeles + */ +@SuppressWarnings({"unchecked", "NullAway.Init", "ForLoopReplaceableByForEach", "ManualArrayToCollectionCopy"}) +public class DogArray extends FastAccess { + // new instances are created using this. If null then no new instances are created automatically. + private @Getter Factory factory; + // function that's called to reset a returned instance + private @Getter @Setter DProcess reset; + // function that's called to initialize a new instance + private @Getter @Setter DProcess initialize = new DProcess.DoNothing<>(); + + // Wrapper around this class for lists + private final DogArrayList list = new DogArrayList<>(this); + + /** + * Constructor which allows new instances to be created using a lambda + */ + public DogArray( Class type, Factory factory ) { + super(type); + init(10, factory); + } + + /** + * Constructor which allows new instances to be created using a lambda and determines the class by + * creating a new instance. + */ + public DogArray( Factory factory ) { + super((Class)factory.newInstance().getClass()); + init(10, factory); + } + + /** + * User provided factory function and reset function. + * + * @param factory Creates new instances + * @param reset Called whenever an element is recycled and needs to be reset + */ + public DogArray( Factory factory, DProcess reset ) { + super((Class)factory.newInstance().getClass()); + this.reset = reset; + init(10, factory); + } + + /** + * User provided factory function and reset function. + * + * @param factory Creates new instances + * @param reset Called whenever an element is recycled and needs to be reset + * @param initialize Called after a new instance is created + */ + public DogArray( Factory factory, DProcess reset, DProcess initialize ) { + super((Class)factory.newInstance().getClass()); + this.reset = reset; + this.initialize = initialize; + init(10, factory); + } + + /** + * Constructor which allows new instances to be created using a lambda + */ + public DogArray( int initialMaxSize, Factory factory ) { + super((Class)factory.newInstance().getClass()); + init(initialMaxSize, factory); + } + + /** + * Data structure initialization is done here so that child classes can declay initialization until they are ready + */ + protected void init( int initialMaxSize, Factory factory ) { + this.size = 0; + this.factory = factory; + if (this.reset == null) + this.reset = new DProcess.DoNothing<>(); + + data = (T[])Array.newInstance(type, initialMaxSize); + + if (factory != null) { + for (int i = 0; i < initialMaxSize; i++) { + try { + data[i] = createInstance(); + } catch (RuntimeException e) { + throw new RuntimeException("declareInstances is true, but createInstance() can't create a new instance. Maybe override createInstance()?"); + } + } + } + } + + /** + * Returns a wrapper around FastQueue that allows it to act as a read only list. + * There is little overhead in using this interface. + * + * NOTE: The same instead of a list is returned each time. Be careful when writing + * concurrent code and create a copy. + * + * @return List wrapper. + */ + @Override + public List toList() { + return list; + } + + /** + * Removes the indexes from the array. This is done by swapping removed elements with the last element. O(N) copies. + * + * @param indexes Index of elements which are to be removed. This will be modified + * @param fromIndex the index of the first element, inclusive, to be sorted + * @param toIndex the index of the last element, exclusive, to be sorted + * @param workSpace Optional internal workspace. Can be set to null. + */ + public void remove( int[] indexes, int fromIndex, int toIndex, @Nullable List workSpace ) { + if (toIndex <= fromIndex) + return; + if (workSpace == null) { + workSpace = new ArrayList<>(); + } else { + workSpace.clear(); + } + // sort indexes from lowest to highest + Arrays.sort(indexes, fromIndex, toIndex); + + // the next index wihch should be skipped + int target = indexes[fromIndex]; + // how many indexes have been removed so far + int count = 0; + for (int i = indexes[fromIndex]; i < size; i++) { + if (i == target) { + workSpace.add(data[i]); + count++; + if (count < toIndex - fromIndex) { + target = indexes[fromIndex + count]; + } else { + target = -1; + } + } else { + data[i - count] = data[i]; + } + } + + // push removed objects to the end + for (int i = 0; i < workSpace.size(); i++) { + data[size - i - 1] = workSpace.get(i); + } + size -= workSpace.size(); + } + + /** + * Shrinks the size of the array by one and returns the element stored at the former last element. + * + * @return The last element in the list that was removed. + */ + public T removeTail() { + if (size > 0) { + size--; + return data[size]; + } else + throw new IllegalArgumentException("Size is already zero"); + } + + public DogArray reset() { + size = 0; + return this; + } + + /** + * Returns a new element of data. If there are new data elements available then array will + * automatically grow. + * + * @return A new instance. + */ + public T grow() { + if (size < data.length) { + T ret = data[size++]; + reset.process(ret); + return ret; + } else { + reserve((data.length + 1)*2); + return data[size++]; + } + } + + /** + * Grows the array and adds all the items in list. Values are copied using the provided function + */ + public void copyAll( List list, Set setter ) { + reserve(size() + list.size()); + for (int i = 0; i < list.size(); i++) { + T dst = grow(); + setter.set(list.get(i), dst); + } + } + + /** + * Removes an element from the array and preserves the order of all elements. This is done by shifting elements + * in the array down one and placing the removed element at the old end of the list. O(N) runtime. + * + * @param index Index of the element being removed + * @return The object removed. + */ + @Override + public T remove( int index ) { + T removed = data[index]; + for (int i = index + 1; i < size; i++) { + data[i - 1] = data[i]; + } + data[size - 1] = removed; + size--; + return removed; + } + + /** + * Searches for and removes the 'target' from the list. Returns true if the target was found. If false + * then the target was never found and no change has been made. This is an O(N) operation. + * + * @param target Object to remove from the list + * @return true if the target was found and removed + */ + public boolean remove( T target ) { + int index = indexOf(target); + if (index < 0) + return false; + remove(index); + return true; + } + + /** + * Removes the specified index from the array by swapping it with last element. Does not preserve order + * but has a runtime of O(1). + * + * @param index The index to be removed. + * @return The removed object + */ + @Override + public T removeSwap( int index ) { + T removed = data[index]; + data[index] = data[size - 1]; + data[size - 1] = removed; + size--; + return removed; + } + + /** + * Ensures that the internal array has at least `length` elements. If it does not then a new internal array + * is created with the specified length and elements from the old are copied into the new. The `size` does + * not change. + * + * @param length Requested minimum internal array length + */ + public void reserve( int length ) { + // now need to grow since it is already larger + if (this.data.length >= length) + return; + + T[] data = (T[])Array.newInstance(type, length); + System.arraycopy(this.data, 0, data, 0, this.data.length); + + if (factory != null) { + for (int i = this.data.length; i < length; i++) { + data[i] = createInstance(); + } + } + this.data = data; + } + + /** + * Resize with a configuration operator. Equivalent to calling {@link #reserve} and this.size = N, then + * applying the 'configure' operator to each new element. + * + * NOTE: The 'reset' operator is applied before the 'configure' operator. + * + * @param length The new size of the array + * @param configure Operator that the "new" element is passed in to. + */ + public DogArray resize( int length, DProcess configure ) { + reserve(length); + for (int i = size; i < length; i++) { + reset.process(data[i]); + configure.process(data[i]); + } + this.size = length; + return this; + } + + /** + * Resize with a configuration operator. Equivalent to calling {@link #reserve} and this.size = N, then + * applying the 'configure' operator to each new element. + * + * NOTE: The 'reset' operator is applied before the 'configure' operator. + * + * @param length The new size of the array + * @param configure Operator that the "new" element is passed in to along with the index of the element. + */ + public DogArray resize( int length, DProcessIdx configure ) { + reserve(length); + for (int i = size; i < length; i++) { + reset.process(data[i]); + configure.process(i, data[i]); + } + this.size = length; + return this; + } + + /** + * Changes the size to the specified length. Equivalent to calling {@link #reserve} and this.size = N. + * + * All new elements will be passed in to {@link #reset}. + * + * @param newSize New array size + */ + public DogArray resize( int newSize ) { + reserve(newSize); + for (int i = size; i < newSize; i++) { + reset.process(data[i]); + } + this.size = newSize; + return this; + } + + /** + * Convenience functions that calls {@link #reset} first before {@link #resize}. + * + * @param newSize New array size + */ + @Deprecated + public void resetResize( int newSize ) { + reset(); + resize(newSize); + } + + /** + * Convenience functions that calls {@link #reset} first before {@link #resize}, then applies the + * configure function for each element.. + * + * @param newSize New array size + * @param configure Operator that the "new" element is passed in to along with the index of the element. + */ + @Deprecated + public void resetResize( int newSize, DProcessIdx configure ) { + reset(); + resize(newSize, configure); + } + + public void shuffle( Random rand ) { + for (int i = 0; i < size; i++) { + int selected = rand.nextInt(size - i); + T tmp = data[selected]; + data[selected] = data[size - i - 1]; + data[size - i - 1] = tmp; + } + } + + /** + * Creates a new instance of elements stored in this array + */ + protected T createInstance() { + T instance = factory.newInstance(); + initialize.process(instance); + reset.process(instance); + return instance; + } + + public List copyIntoList( List ret ) { + if (ret == null) + ret = new ArrayList<>(size); + for (int i = 0; i < size; i++) { + ret.add(data[i]); + } + return ret; + } + + /** + * Checks to see if the object is in the unused list. + */ + public boolean isUnused( T object ) { + final T[] data = this.data; + for (int i = size; i < data.length; i++) { + if (data[i] == object) + return true; + } + return false; + } + + // -------- These are only around so that it can be a java bean + public T[] getData() { + return data; + } + + public void setData( T[] data ) { + this.data = data; + } + + public int getSize() { + return size; + } + + public void setSize( int size ) { + this.size = size; + } + + public final boolean isDeclare() { + return factory != null; + } + + public Class getType() { + return type; + } + + public interface Set { + void set( S src, D dst ); + } +} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/struct/DogArrayList.java ddogleg-0.22+ds/src/org/ddogleg/struct/DogArrayList.java --- ddogleg-0.18+ds/src/org/ddogleg/struct/DogArrayList.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/struct/DogArrayList.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.struct; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; + +/** + * Wrapper around queue which allows it to act as a {@link List}. + * + * @author Peter Abeles + */ +public class DogArrayList implements List , Serializable { + DogArray array; + + public DogArrayList( DogArray array ) { + this.array = array; + } + + @Override + public int size() { + return array.size; + } + + @Override + public boolean isEmpty() { + return array.size() == 0; + } + + @Override + public boolean contains(Object o) { + return array.contains(o); + } + + @Override + public Iterator iterator() { + return new MyIterator(); + } + + @Override + public Object[] toArray() { + Object[] ret = new Object[array.size]; + + System.arraycopy(array.data,0,ret,0, array.size); + + return ret; + } + + @Override + public A[] toArray(A[] a) { + System.arraycopy(array.data,0,a,0, array.size); + return a; + } + + @Override + public boolean add(T t) { + throw new RuntimeException("Add is not supported by FastQueue. You need FastArray instead"); + } + + @Override + public boolean remove(Object o) { + throw new RuntimeException("Not all list operations are supposed."); + } + + @Override + public boolean containsAll(Collection c) { + for( Object o : c ) { + if( !contains(o) ) + return false; + } + return true; + } + + @Override + public boolean addAll(Collection c) { + throw new RuntimeException("Add is not supported by FastQueue. You need FastArray instead"); + } + + @Override + public boolean addAll(int index, Collection c) { + throw new RuntimeException("Not all list operations are supposed."); + } + + @Override + public boolean removeAll(Collection c) { + throw new RuntimeException("Not all list operations are supposed."); + } + + @Override + public boolean retainAll(Collection c) { + throw new RuntimeException("Not all list operations are supposed."); + } + + @Override + public void clear() { + array.reset(); + } + + @Override + public T get(int index) { + return array.data[index]; + } + + @Override + public T set(int index, T element) { + throw new RuntimeException("Set is not supported by FastQueue. You need FastArray instead"); + } + + @Override + public void add(int index, T element) { + throw new RuntimeException("Not all list operations are supposed."); + } + + @Override + public T remove(int index) { + throw new RuntimeException("Not all list operations are supposed."); + } + + @Override + public int indexOf(Object o) { + return array.indexOf((T)o); + } + + @Override + public int lastIndexOf(Object o) { + for(int i = array.size-1; i >= 0; i-- ) { + if( array.data[i].equals(o) ) + return i; + } + return -1; + } + + @Override + public ListIterator listIterator() { + return new MyIterator(); + } + + @Override + public ListIterator listIterator(int index) { + throw new RuntimeException("Not supported"); + } + + @Override + public List subList(int fromIndex, int toIndex) { + throw new RuntimeException("Not supported"); + } + + public class MyIterator implements ListIterator + { + int index = 0; + + @Override + public boolean hasNext() { + return index < array.size; + } + + @Override + public T next() { + return array.data[index++]; + } + + @Override + public boolean hasPrevious() { + return index > 0; + } + + @Override + public T previous() { + return array.data[--index]; + } + + @Override + public int nextIndex() { + return index; + } + + @Override + public int previousIndex() { + return index-1; + } + + @Override + public void remove() { + throw new RuntimeException("Not all list operations are supposed."); + } + + @Override + public void set(T t) { + array.data[index-1] = t; + } + + @Override + public void add(T t) { + throw new RuntimeException("Add is not supported by FastQueue. Use FastArray instead"); + } + } + + public DogArray getArray() { + return array; + } + + public void setArray( DogArray array ) { + this.array = array; + } +} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/struct/DogArrayPrimitive.java ddogleg-0.22+ds/src/org/ddogleg/struct/DogArrayPrimitive.java --- ddogleg-0.18+ds/src/org/ddogleg/struct/DogArrayPrimitive.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/struct/DogArrayPrimitive.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2012-2022, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.struct; + +/** + * Interface for growable queues of primitive types + * + * @author Peter Abeles + */ +public interface DogArrayPrimitive> { + + /** + * Sets the size to zero. + * + * @return Returns 'this' to allow chaining of operations. + */ + T reset(); + + /** + * Returns true if the specified array index is outside the allowed value range + */ + default boolean isIndexOutOfBounds( int index ) { + return index < 0 || index >= size(); + } + + /** + * Turns 'this' into a copy of 'original' + * + * @param original queue that is to be copied + * @return Returns 'this' to allow chaining of operations. + */ + T setTo( T original ); + + /** + *

Ensures that the internal array is at least this size. Value of elements previously in the array will + * not be changed. If the size is increased then the value of new elements in undefined.

+ * + * If you wish to resize the array and avoid copying over past values for performance reasons, then you must + * either resize(0) or call {@link #reset} first. + * + * @param size desired new size + */ + T resize( int size ); + + /** + * Changes the array to the specified size. If there is not enough storage, a new internal array is created + * and the elements copied over. This is the same as: a.reserve(size);a.size = size; + * + * @param size desired new size + */ + void extend( int size ); + + /** + * Ensures that the internal array's length is at least this size. Size is left unchanged. If the array + * is not empty and it needs to grow then the existing array is copied into the new array. + * + * @param amount minimum size of internal array + */ + void reserve( int amount ); + + /** + * Flips the elements such that a[i] = a[N-i-1] where N is the number of elements. + */ + void flip(); + + /** + * Number of elements in the queue + * + * @return size of queue + */ + int size(); + + /** + * Sets all elements to 0 or False for binary queues + */ + void zero(); + + T copy(); + + /** + * Sorts the data from smallest to largest + */ + void sort(); + + /** True if the container has no elements */ + default boolean isEmpty() { + return size() == 0; + } +} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/struct/DogLambdas.java ddogleg-0.22+ds/src/org/ddogleg/struct/DogLambdas.java --- ddogleg-0.18+ds/src/org/ddogleg/struct/DogLambdas.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/struct/DogLambdas.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.struct; + +/** + * Common lambdas used in DDogleg + * + * @author Peter Abeles + */ +public interface DogLambdas { + @FunctionalInterface interface NewInstance

{ + P newInstance(); + } + + @FunctionalInterface interface Copy

{ + void copy(P src, P dst); + } + + @FunctionalInterface interface AssignIdx_B { + boolean assign(int idx); + } + + @FunctionalInterface interface AssignIdx_I8 { + byte assign(int idx); + } + + @FunctionalInterface interface AssignIdx_I16 { + short assign(int idx); + } + + @FunctionalInterface interface AssignIdx_I32 { + int assign(int idx); + } + + @FunctionalInterface interface AssignIdx_I64 { + long assign(int idx); + } + + @FunctionalInterface interface AssignIdx_F32 { + float assign(int idx); + } + + @FunctionalInterface interface AssignIdx_F64 { + double assign(int idx); + } +} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/struct/DogLinkedList.java ddogleg-0.22+ds/src/org/ddogleg/struct/DogLinkedList.java --- ddogleg-0.18+ds/src/org/ddogleg/struct/DogLinkedList.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/struct/DogLinkedList.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,468 @@ +/* + * Copyright (c) 2012-2022, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.struct; + +import lombok.Getter; +import lombok.Setter; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayDeque; +import java.util.List; +import java.util.Objects; + +/** + * A double linked list. Internal data structures are recycled to minimize creation of new memory. + * + * @author Peter Abeles + */ +public class DogLinkedList { + // first element in the list + protected @Nullable Element first; + // last element in the list + protected @Nullable Element last; + // total number of elements in the list + protected int size; + + // recycled elements. It is assumed that all elements inside of here have all parameters set to null already + protected final ArrayDeque> available = new ArrayDeque<>(); + + /** + * Puts the linked list back into its initial state. Elements are saved for later use. + */ + public void reset() { + Element e = first; + while( e != null ) { + Element n = e.next; + e.clear(); + available.add( e ); + e = n; + // This is possible if the list is cyclical + if (e == first) + break; + } + first = last = null; + size = 0; + } + + /** + * Checks to see if there are no elements in the list + * @return true if empty or false if not + */ + public boolean isEmpty() { + return size == 0; + } + + /** + * Returns the N'th element when counting from the from or from the back + * + * @param index Number of elements away from the first or last element. Must be positive. + * @return if true then the number of elements will be from first otherwise last + */ + @SuppressWarnings("NullAway") + public Element getElement( int index , boolean fromFront ) { + if( index > size || index < 0 ) { + throw new IllegalArgumentException("index is out of bounds"); + } + if( fromFront ) { + Element e = first; + for( int i = 0; i < index; i++ ) { + if( e == null ) + throw new IllegalArgumentException("Element "+i+" is null"); + e = e.next; + } + return e; + } else { + Element e = last; + for( int i = 0; i < index; i++ ) { + if( e == null ) + throw new IllegalArgumentException("Element "+i+" is null"); + e = e.prev; + } + return e; + } + } + + /** + * Adds the element to the front of the list. + * + * @param object Object being added. + * @return The element it was placed inside of + */ + public Element pushHead( T object ) { + Element e = requestNew(); + e.object = object; + + if( first == null ) { + first = last = e; + } else { + e.next = first; + first.prev = e; + first = e; + } + size++; + + return e; + } + + /** + * Adds the element to the back of the list. + * + * @param object Object being added. + * @return The element it was placed inside of + */ + public Element pushTail( T object ) { + Element e = requestNew(); + e.object = object; + + if( last == null ) { + first = last = e; + } else { + e.prev = last; + last.next = e; + last = e; + } + size++; + + return e; + } + + /** + * Inserts the object into a new element after the provided element. + * + * @param previous Element which will be before the new one + * @param object The object which goes into the new element + * @return The new element + */ + public Element insertAfter( Element previous , T object ) { + Element e = requestNew(); + e.object = object; + e.prev = previous; + e.next = previous.next; + if( e.next != null ) { + e.next.prev = e; + } else { + last = e; + } + previous.next = e; + size++; + return e; + } + + /** + * Inserts the object into a new element before the provided element. + * + * @param next Element which will be after the new one + * @param object The object which goes into the new element + * @return The new element + */ + public Element insertBefore( Element next , T object ) { + Element e = requestNew(); + e.object = object; + e.prev = next.prev; + e.next = next; + + if( e.prev != null ) { + e.prev.next = e; + } else { + first = e; + } + next.prev = e; + size++; + return e; + } + + /** + * Swaps the location of the two elements + * + * @param a Element + * @param b Element + */ + public void swap( Element a , Element b ) { + if (a.next == b) { + if( a.prev != null ) { + a.prev.next = b; + } + if( b.next != null ) { + b.next.prev = a; + } + Element tmp = a.prev; + a.prev = b; + a.next = b.next; + b.prev = tmp; + b.next = a; + if( first == a ) + first = b; + if( last == b ) + last = a; + } else if (a.prev == b) { + if( a.next != null ) { + a.next.prev = b; + } + if( b.prev != null ) { + b.prev.next = a; + } + Element tmp = a.next; + a.next = b; + a.prev = b.prev; + b.prev = a; + b.next = tmp; + + if( first == b ) + first = a; + if( last == a ) + last = b; + } else { + if (a.next != null) { + a.next.prev = b; + } + if (a.prev != null) { + a.prev.next = b; + } + if (b.next != null) { + b.next.prev = a; + } + if (b.prev != null) { + b.prev.next = a; + } + Element tempNext = b.next; + Element tempPrev = b.prev; + b.next = a.next; + b.prev = a.prev; + a.next = tempNext; + a.prev = tempPrev; + + if (a.next == null) + last = a; + else if (b.next == null) + last = b; + if (a.prev == null) + first = a; + else if (b.prev == null) + first = b; + } + } + + /** + * Removes the element from the list and saves the element data structure for later reuse. + * @param element The item which is to be removed from the list + */ + public void remove( Element element ) { + if( element.next == null ) { + last = element.prev; + } else { + element.next.prev = element.prev; + } + if( element.prev == null ) { + first = element.next; + } else { + element.prev.next = element.next; + } + size--; + element.clear(); + available.add(element); + } + + /** + * Removes the first element from the list + * @return The object which was contained in the first element + */ + public T removeHead() { + if( first == null ) + throw new IllegalArgumentException("Empty list"); + + T ret = first.getObject(); + Element e = first; + available.add(first); + + if( first.next != null ) { + first.next.prev = null; + first = first.next; + } else { + // there's only one element in the list + first = last = null; + } + e.clear(); + size--; + return ret; + } + + /** + * Removes the last element from the list + * @return The object which was contained in the last element + */ + public T removeTail() { + if( last == null ) + throw new IllegalArgumentException("Empty list"); + + T ret = last.getObject(); + Element e = last; + available.add(last); + + if( last.prev != null ) { + last.prev.next = null; + last = last.prev; + } else { + // there's only one element in the list + first = last = null; + } + e.clear(); + size--; + return ret; + } + + /** + * Returns the first element which contains 'object' starting from the head. + * @param object Object which is being searched for + * @return First element which contains object or null if none can be found + */ + public @Nullable Element find( T object ) { + Element e = first; + while( e != null ) { + if( e.object == object ) { + return e; + } + e = e.next; + } + return null; + } + + /** + * Returns the first element in the list + * @return first element + */ + public @Nullable Element getHead() { + return first; + } + + /** + * Returns the last element in the list + * @return last element + */ + public @Nullable Element getTail() { + return last; + } + + /** Returns the value in the head */ + public T getFirst() { + return Objects.requireNonNull(first).object; + } + + /** Returns the value in the trail */ + public T getLast() { + return Objects.requireNonNull(last).object; + } + + /** + * Add all elements in list into this linked list + * @param list List + */ + public void addAll( List list ) { + if( list.isEmpty() ) + return; + + Element a = requestNew(); + a.object = list.get(0); + + if( first == null ) { + first = a; + } else if( last != null ) { + last.next = a; + a.prev = last; + } + + for (int i = 1; i < list.size(); i++) { + Element b = requestNew(); + b.object = list.get(i); + + a.next = b; + b.prev = a; + a = b; + } + + last = a; + size += list.size(); + } + + /** + * Adds the specified elements from array into this list + * @param array The array + * @param first First element to be added + * @param length The number of elements to be added + */ + public void addAll( T[] array , int first , int length ) { + if( length <= 0 ) + return; + + Element a = requestNew(); + a.object = array[first]; + + if( this.first == null ) { + this.first = a; + } else if( last != null ) { + last.next = a; + a.prev = last; + } + + for (int i = 1; i < length; i++) { + Element b = requestNew(); + b.object = array[first+i]; + + a.next = b; + b.prev = a; + a = b; + } + + last = a; + size += length; + } + + /** + * Returns the number of elements in the list + */ + public int size() { + return size; + } + + /** + * Returns a new element. If there are old elements available those are returned, otherwise a new one is returned. + * + * @return Unused element. + */ + protected Element requestNew () { + if( available.isEmpty() ) { + return new Element<>(); + } else { + return available.pop(); + } + } + + @SuppressWarnings("NullAway") + public static class Element + { + public @Nullable @Getter @Setter Element next; + public @Nullable @Getter @Setter Element prev; + public @Getter @Setter T object; + + public void clear() { + next = null; + prev = null; + object = null; + } + } +} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/struct/DProcessIdx.java ddogleg-0.22+ds/src/org/ddogleg/struct/DProcessIdx.java --- ddogleg-0.18+ds/src/org/ddogleg/struct/DProcessIdx.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/struct/DProcessIdx.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.struct; + +import java.io.Serializable; + +/** + * Function which processes an object and passes in an integer that represents the index + * + * @author Peter Abeles + */ +public interface DProcessIdx { + void process(int index, T o); + + /** + * Default implementation which does nothing + */ + class DoNothing implements DProcessIdx, Serializable { + @Override + public void process(int index, T o) {} + } +} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/struct/DProcess.java ddogleg-0.22+ds/src/org/ddogleg/struct/DProcess.java --- ddogleg-0.18+ds/src/org/ddogleg/struct/DProcess.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/struct/DProcess.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.struct; + +import java.io.Serializable; + +/** + * Function which processes an object + * + * @author Peter Abeles + */ +public interface DProcess { + void process(T o); + + /** + * Default implementation which does nothing + */ + class DoNothing implements DProcess, Serializable { + @Override + public void process(T o) {} + } +} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/struct/FastAccess.java ddogleg-0.22+ds/src/org/ddogleg/struct/FastAccess.java --- ddogleg-0.18+ds/src/org/ddogleg/struct/FastAccess.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/struct/FastAccess.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2022, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -18,21 +18,25 @@ package org.ddogleg.struct; +import org.jetbrains.annotations.Nullable; + import java.io.Serializable; +import java.util.ArrayList; import java.util.List; /** - * Base class for {@link FastArray} and {@link FastQueue}. Provides access to the data but does not provide + * Base class for {@link FastArray} and {@link DogArray}. Provides access to the data but does not provide * methods which add or grow the internal data structure. * * @author Peter Abeles */ +@SuppressWarnings("NullAway.Init") public abstract class FastAccess implements Serializable { public T[] data; public int size; public final Class type; - public FastAccess(Class type ) { + protected FastAccess(Class type ) { this.type = type; } @@ -43,6 +47,13 @@ } /** + * Returns true if the specified array index is outside the allowed value range + */ + public boolean isIndexOutOfBounds( int index ) { + return index < 0 || index >= size; + } + + /** * The maximum number of elements before the 'data' array needs to grow * @return length of 'data' */ @@ -75,6 +86,11 @@ return size; } + /** True if the container has no elements */ + public boolean isEmpty() { + return size == 0; + } + /** * Returns a wrapper around FastQueue that allows it to act as a read only list. * There is little overhead in using this interface. @@ -100,6 +116,33 @@ } /** + * Returns true if an object 'a' in the array returns true for 'a.equals(o)' + */ + public boolean contains(Object o) { + for( int i = 0; i < size; i++ ) { + if( data[i].equals(o) ) + return true; + } + + return false; + } + + /** + * Returns the first index which equals() obj. -1 is there is no match + * + * @param obj The object being searched for + * @return index or -1 if not found + */ + public int indexOf( T obj ) { + for (int i = 0; i < size; i++) { + if( data[i].equals(obj) ) { + return i; + } + } + return -1; + } + + /** * Reverse the item order in this queue. */ public void reverse() { @@ -109,4 +152,139 @@ data[size - i - 1] = tmp; } } + + /** + * Swaps the two elements in the array + * @param i index + * @param j index + */ + public void swap( int i, int j) { + T tmp = data[i]; + data[i] = data[j]; + data[j] = tmp; + } + + /** Returns the first instance's index which matches the function. -1 if no match is found*/ + public int findIdx(FunctionMatches function) { + for (int i = 0; i < size; i++) { + if (function.process(data[i])) { + return i; + } + } + return -1; + } + + /** Returns the first instance which matches the function. Null if no matches are found */ + public @Nullable T find(FunctionMatches function) { + int match = findIdx(function); + if (match<0) + return null; + return data[match]; + } + + /** + * Finds the indexes of all elements which match. Returns true if at least one match was found + */ + public boolean findAllIdx( DogArray_I32 matches, FunctionMatches function) { + matches.reset(); + for (int i = 0; i < size; i++) { + if (function.process(data[i])) { + matches.add(i); + } + } + return !matches.isEmpty(); + } + + /** + * Finds the indexes of all elements which match. Returns true if at least one match was found + */ + public boolean findAll(List matches, FunctionMatches function) { + matches.clear(); + for (int i = 0; i < size; i++) { + if (function.process(data[i])) { + matches.add(data[i]); + } + } + return !matches.isEmpty(); + } + + /** + * Same as {@link #findAll} but returns a new list. + */ + public List filter( FunctionMatches function) { + List list = new ArrayList<>(); + findAll(list, function); + return list; + } + + /** + * The passed in function is called once for each element in the list + */ + public void forIdx(FunctionEachIdx function ) { + for (int i = 0; i < size; i++) { + function.process(i,data[i]); + } + } + + /** + * For each with a range of values specified + * @param idx0 lower extent, inclusive + * @param idx1 upper extent, exclusive + */ + public void forIdx(int idx0 , int idx1, FunctionEachIdx function ) { + if( idx1 > size ) + throw new IllegalArgumentException("idx1 is out of range"); + + for (int i = idx0; i < idx1; i++) { + function.process(i,data[i]); + } + } + + /** + * The passed in function is called once for each element in the list + */ + public void forEach(FunctionEach function ) { + for (int i = 0; i < size; i++) { + function.process(data[i]); + } + } + + /** Counts the number of times an element returns true when passed into 'test' */ + public int count( FunctionMatches test ) { + int total = 0; + for (int i = 0; i < size; i++) { + if (test.process(data[i])) + total++; + } + return total; + } + + /** + * For each with a range of values specified + * @param idx0 lower extent, inclusive + * @param idx1 upper extent, exclusive + */ + public void forEach(int idx0 , int idx1, FunctionEach function ) { + if( idx1 > size ) + throw new IllegalArgumentException("idx1 is out of range"); + + for (int i = idx0; i < idx1; i++) { + function.process(data[i]); + } + } + + @FunctionalInterface + public interface FunctionEachIdx { + void process( int index, T o ); + } + + @FunctionalInterface + public interface FunctionEach { + void process( T value ); + } + + @FunctionalInterface + public interface FunctionMatches { + boolean process( T value ); + } } diff -Nru ddogleg-0.18+ds/src/org/ddogleg/struct/FastArray.java ddogleg-0.22+ds/src/org/ddogleg/struct/FastArray.java --- ddogleg-0.18+ds/src/org/ddogleg/struct/FastArray.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/struct/FastArray.java 2022-09-01 02:18:09.000000000 +0000 @@ -51,7 +51,7 @@ public void add( T value ) { if( size >= data.length ) { - growArray((data.length+1)*2); + reserve((data.length+1)*2); } data[size++] = value; } @@ -78,6 +78,19 @@ return ret; } + /** + * Searches for the object and removes it if it's contained in the list. O(N) operation. + * @param target Object to be removed + * @return true if it was removed or false if it was not found + */ + public boolean remove( T target ) { + int index = indexOf(target); + if( index < 0 ) + return false; + remove(index); + return true; + } + public T removeTail() { if( size <= 0 ) throw new IllegalArgumentException("The array is empty"); @@ -94,6 +107,12 @@ size = 0; } + /** Convenience function which resets the array and reserves memory */ + public void resetReserve(int length) { + reset(); + reserve(length); + } + /** * Sets the size of the list to zero and removes all internal references inside the current array. */ @@ -103,58 +122,46 @@ } /** - * Increases the size of the internal array without changing the shape's size. If the array - * is already larger than the specified length then nothing is done. Elements previously - * stored in the array are copied over is a new internal array is declared. + * Ensures that the internal array has at least `length` elements. If it does not then a new internal array + * is created with the specified length and elements from the old are copied into the new. The `size` does + * not change. * - * @param length Requested size of internal array. + * @param length Requested minimum internal array length */ - public void growArray( int length) { + public void reserve(int length ) { + reserve(length,true); + } + + public void reserve(int length, boolean copy ) { // now need to grow since it is already larger - if( this.data.length >= length) + if (this.data.length >= length) return; T []data = (T[])Array.newInstance(type, length); - System.arraycopy(this.data,0,data,0,size); + if (copy) + System.arraycopy(this.data,0,data,0,size); this.data = data; } /** - * Returns the first index which equals() obj. -1 is there is no match - * - * @param obj The object being searched for - * @return index or -1 if not found - */ - public int indexOf( T obj ) { - for (int i = 0; i < size; i++) { - if( data[i].equals(obj) ) { - return i; - } - } - return -1; - } - - /** - * Changes the size to the specified length. Equivalent to calling {@link #growArray} and this.size = N. + * Changes the size to the specified length. Equivalent to calling {@link #reserve} and this.size = N. * @param length The new size of the queue */ public void resize(int length) { - growArray(length); + reserve(length); this.size = length; } /** - * Returns true if the object is contained inside the array + * Changes the size and fills each element with this value */ - public boolean contains(T o) { - for( int i = 0; i < size; i++ ) { - if( data[i].equals(o) ) - return true; - } - - return false; + public void resize(int length, T value) { + reserve(length,false); + Arrays.fill(data,0,length,value); + this.size = length; } + public void addAll( FastAccess list ) { for( int i = 0; i < list.size; i++ ) { add( list.data[i]); @@ -167,6 +174,12 @@ } } + public void setTail( int index, T value ) { + if( index < 0 || index >= size) + throw new IndexOutOfBoundsException("index = "+index+" size = "+size); + data[size-index-1] = value; + } + public void addAll( final List list ) { final int originalSize = this.size; resize(this.size+list.size()); diff -Nru ddogleg-0.18+ds/src/org/ddogleg/struct/FastArrayList.java ddogleg-0.22+ds/src/org/ddogleg/struct/FastArrayList.java --- ddogleg-0.18+ds/src/org/ddogleg/struct/FastArrayList.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/struct/FastArrayList.java 2022-09-01 02:18:09.000000000 +0000 @@ -66,7 +66,7 @@ } @Override - public T[] toArray(T[] a) { + public A[] toArray(A[] a) { System.arraycopy(queue.data,0,a,0,queue.size); return a; } diff -Nru ddogleg-0.18+ds/src/org/ddogleg/struct/FastQueue.java ddogleg-0.22+ds/src/org/ddogleg/struct/FastQueue.java --- ddogleg-0.18+ds/src/org/ddogleg/struct/FastQueue.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/struct/FastQueue.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,401 +0,0 @@ -/* - * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. - * - * This file is part of DDogleg (http://ddogleg.org). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ddogleg.struct; - -import javax.annotation.Nullable; -import java.lang.reflect.Array; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Random; - - -/** - * Growable array designed for fast access which creates, recycles and in general owns all of its elements. - * - * @author Peter Abeles - */ -@SuppressWarnings("unchecked") -public class FastQueue extends FastAccess { - // new instances are created using this. If null then no new instances are created automatically. - private Factory factory; - // function that's called to reset a returned instance - private Process reset; - - // Wrapper around this class for lists - private FastQueueList list = new FastQueueList(this); - - /** - * Constructor which allows new instances to be created using a lambda - */ - public FastQueue(Class type, Factory factory ) { - super(type); - init(10, factory); - } - - /** - * Constructor which allows new instances to be created using a lambda and determines the class by - * creating a new instance. - */ - public FastQueue( Factory factory ) { - super((Class)factory.newInstance().getClass()); - init(10, factory); - } - - /** - * User provided factory function and reset function. - * @param factory Creates new instances - * @param reset Called whenever an element is recycled and needs to be reset - */ - public FastQueue( Factory factory , Process reset ) { - super((Class)factory.newInstance().getClass()); - this.reset = reset; - init(10, factory); - } - - /** - * Constructor which allows new instances to be created using a lambda - */ - public FastQueue( int initialMaxSize, Factory factory ) { - super((Class)factory.newInstance().getClass()); - init(initialMaxSize, factory); - } - - /** - * Data structure initialization is done here so that child classes can declay initialization until they are ready - */ - protected void init(int initialMaxSize, Factory factory) { - this.size = 0; - this.factory = factory; - if( this.reset == null ) - this.reset = new Process.DoNothing<>(); - - data = (T[]) Array.newInstance(type, initialMaxSize); - - if( factory != null ) { - for( int i = 0; i < initialMaxSize; i++ ) { - try { - data[i] = createInstance(); - } catch( RuntimeException e ) { - throw new RuntimeException("declareInstances is true, but createInstance() can't create a new instance. Maybe override createInstance()?"); - } - } - } - } - - /** - * Returns a wrapper around FastQueue that allows it to act as a read only list. - * There is little overhead in using this interface. - * - * NOTE: The same instead of a list is returned each time. Be careful when writing - * concurrent code and create a copy. - * - * @return List wrapper. - */ - @Override - public List toList() { - return list; - } - - /** - * Removes the indexes from the queue. This is done by swapping removed elements with the last element. O(N) copies. - * - * @param indexes Index of elements which are to be removed. This will be modified - * @param fromIndex the index of the first element, inclusive, to be sorted - * @param toIndex the index of the last element, exclusive, to be sorted - * @param workSpace Optional internal workspace. Can be set to null. - */ - public void remove( int[] indexes , int fromIndex, int toIndex, @Nullable List workSpace ) { - if( toIndex <= fromIndex ) - return; - if( workSpace == null ) { - workSpace = new ArrayList<>(); - } else { - workSpace.clear(); - } - // sort indexes from lowest to highest - Arrays.sort(indexes,fromIndex,toIndex); - - // the next index wihch should be skipped - int target = indexes[fromIndex]; - // how many indexes have been removed so far - int count = 0; - for ( int i = indexes[fromIndex]; i < size; i++ ) { - if( i == target ) { - workSpace.add( data[i] ); - count++; - if( count < toIndex-fromIndex ) { - target = indexes[fromIndex+count]; - } else { - target = -1; - } - } else { - data[i-count] = data[i]; - } - } - - // push removed objects to the end - for (int i = 0; i < workSpace.size(); i++) { - data[size-i-1] = workSpace.get(i); - } - size -= workSpace.size(); - } - - /** - * Shrinks the size of the array by one and returns the element stored at the former last element. - * - * @return The last element in the list that was removed. - */ - public T removeTail() { - if( size > 0 ) { - size--; - return data[size]; - } else - throw new IllegalArgumentException("Size is already zero"); - } - - public void reset() { - size = 0; - } - - /** - * Returns a new element of data. If there are new data elements available then array will - * automatically grow. - * - * @return A new instance. - */ - public T grow() { - if( size < data.length ) { - T ret = data[size++]; - reset.process(ret); - return ret; - } else { - growArray((data.length+1)*2); - return data[size++]; - } - } - - /** - * Grows the array and adds all the items in list. Values are copied using the provided function - */ - public void copyAll(List list , Set setter ) { - growArray(size()+list.size()); - for (int i = 0; i < list.size(); i++) { - T dst = grow(); - setter.set(list.get(i),dst); - } - } - - /** - * Removes an element from the queue and preserves the order of all elements. This is done by shifting elements - * in the array down one and placing the removed element at the old end of the list. O(N) runtime. - * - * @param index Index of the element being removed - * @return The object removed. - */ - @Override - public T remove( int index ) { - T removed = data[index]; - for( int i = index+1; i < size; i++ ) { - data[i-1] = data[i]; - } - data[size-1] = removed; - size--; - return removed; - } - - /** - * Removes the specified index from the array by swapping it with last element. Does not preserve order - * but has a runtime of O(1). - * - * @param index The index to be removed. - * @return The removed object - */ - @Override - public T removeSwap( int index ) { - T removed = data[index]; - data[index] = data[size-1]; - data[size-1] = removed; - size--; - return removed; - } - - /** - * Increases the size of the internal array without changing the shape's size. If the array - * is already larger than the specified length then nothing is done. Elements previously - * stored in the array are copied over is a new internal array is declared. - * - * @param length Requested size of internal array. - */ - public void growArray( int length) { - // now need to grow since it is already larger - if( this.data.length >= length) - return; - - T []data = (T[])Array.newInstance(type, length); - System.arraycopy(this.data,0,data,0,this.data.length); - - if( factory != null ) { - for( int i = this.data.length; i < length; i++ ) { - data[i] = createInstance(); - } - } - this.data = data; - } - - /** - * Changes the size to the specified length. Equivalent to calling {@link #growArray} and this.size = N. - * @param length The new size of the queue - */ - public void resize(int length) { - growArray(length); - for (int i = size; i < length; i++) { - reset.process(data[i]); - } - this.size = length; - } - - public boolean contains(Object o) { - for( int i = 0; i < size; i++ ) { - if( data[i].equals(o) ) - return true; - } - - return false; - } - - public void shuffle( Random rand ) { - for (int i = 0; i < size; i++) { - int selected = rand.nextInt(size-i); - T tmp = data[selected]; - data[selected] = data[size-i-1]; - data[size-i-1] = tmp; - } - } - - /** - * This function will be removed eventually and the factory used directly. DO NOT USE IN NEW CODE - */ - @Deprecated - protected T createInstance() { - T instance = factory.newInstance(); - reset.process(instance); - return instance; - } - - public List copyIntoList(List ret) { - if( ret == null ) - ret = new ArrayList(size); - for( int i = 0; i < size; i++ ) { - ret.add(data[i]); - } - return ret; - } - - /** - * Returns the first index which equals() obj. -1 is there is no match - * - * @param obj The object being searched for - * @return index or -1 if not found - */ - public int indexOf( T obj ) { - for (int i = 0; i < size; i++) { - if( data[i].equals(obj) ) { - return i; - } - } - return -1; - } - - public void flip() { - if( size <= 1 ) - return; - - int D = size/2; - for (int i = 0,j=size-1; i < D; i++,j--) { - T tmp = data[i]; - data[i] = data[j]; - data[j] = tmp; - } - } - - public void swap( int idx0 , int idx1 ) { - T tmp = data[idx0]; - data[idx0] = data[idx1]; - data[idx1] = tmp; - } - - /** - * Checks to see if the object is in the unused list. - */ - public boolean isUnused( T object ) { - final T[] data = this.data; - for (int i = size; i < data.length; i++) { - if( data[i] == object ) - return true; - } - return false; - } - - // -------- These are only around so that it can be a java bean - public T[] getData() { - return data; - } - - public void setData(T[] data) { - this.data = data; - } - - public int getSize() { - return size; - } - - public void setSize(int size) { - this.size = size; - } - - public final boolean isDeclare() { - return factory != null; - } - - public Class getType() { - return type; - } - - public interface Set { - void set( T src , T dst ); - } - - public class FactoryClass implements Factory { - Class type; - - public FactoryClass(Class type) { - this.type = type; - } - - @Override - public T newInstance() { - try { - return type.newInstance(); - } catch (InstantiationException | IllegalAccessException e) { - throw new RuntimeException(e); - } - } - } -} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/struct/FastQueueList.java ddogleg-0.22+ds/src/org/ddogleg/struct/FastQueueList.java --- ddogleg-0.18+ds/src/org/ddogleg/struct/FastQueueList.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/struct/FastQueueList.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,224 +0,0 @@ -/* - * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. - * - * This file is part of DDogleg (http://ddogleg.org). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ddogleg.struct; - -import java.io.Serializable; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.ListIterator; - -/** - * Wrapper around queue which allows it to act as a {@link List}. - * - * @author Peter Abeles - */ -public class FastQueueList implements List , Serializable { - FastQueue queue; - - public FastQueueList(FastQueue queue) { - this.queue = queue; - } - - @Override - public int size() { - return queue.size; - } - - @Override - public boolean isEmpty() { - return queue.size() == 0; - } - - @Override - public boolean contains(Object o) { - return queue.contains(o); - } - - @Override - public Iterator iterator() { - return new MyIterator(); - } - - @Override - public Object[] toArray() { - Object[] ret = new Object[queue.size]; - - System.arraycopy(queue.data,0,ret,0,queue.size); - - return ret; - } - - @Override - public T[] toArray(T[] a) { - System.arraycopy(queue.data,0,a,0,queue.size); - return a; - } - - @Override - public boolean add(T t) { - throw new RuntimeException("Add is not supported by FastQueue. You need FastArray instead"); - } - - @Override - public boolean remove(Object o) { - throw new RuntimeException("Not all list operations are supposed."); - } - - @Override - public boolean containsAll(Collection c) { - for( Object o : c ) { - if( !contains(o) ) - return false; - } - return true; - } - - @Override - public boolean addAll(Collection c) { - throw new RuntimeException("Add is not supported by FastQueue. You need FastArray instead"); - } - - @Override - public boolean addAll(int index, Collection c) { - throw new RuntimeException("Not all list operations are supposed."); - } - - @Override - public boolean removeAll(Collection c) { - throw new RuntimeException("Not all list operations are supposed."); - } - - @Override - public boolean retainAll(Collection c) { - throw new RuntimeException("Not all list operations are supposed."); - } - - @Override - public void clear() { - queue.reset(); - } - - @Override - public T get(int index) { - return queue.data[index]; - } - - @Override - public T set(int index, T element) { - throw new RuntimeException("Set is not supported by FastQueue. You need FastArray instead"); - } - - @Override - public void add(int index, T element) { - throw new RuntimeException("Not all list operations are supposed."); - } - - @Override - public T remove(int index) { - throw new RuntimeException("Not all list operations are supposed."); - } - - @Override - public int indexOf(Object o) { - return queue.indexOf((T)o); - } - - @Override - public int lastIndexOf(Object o) { - for( int i = queue.size-1; i >= 0; i-- ) { - if( queue.data[i].equals(o) ) - return i; - } - return -1; - } - - @Override - public ListIterator listIterator() { - return new MyIterator(); - } - - @Override - public ListIterator listIterator(int index) { - throw new RuntimeException("Not supported"); - } - - @Override - public List subList(int fromIndex, int toIndex) { - throw new RuntimeException("Not supported"); - } - - public class MyIterator implements ListIterator - { - int index = 0; - - @Override - public boolean hasNext() { - return index < queue.size; - } - - @Override - public T next() { - return queue.data[index++]; - } - - @Override - public boolean hasPrevious() { - return index > 0; - } - - @Override - public T previous() { - return queue.data[--index]; - } - - @Override - public int nextIndex() { - return index; - } - - @Override - public int previousIndex() { - return index-1; - } - - @Override - public void remove() { - throw new RuntimeException("Not all list operations are supposed."); - } - - @Override - public void set(T t) { - queue.data[index-1] = t; - } - - @Override - public void add(T t) { - throw new RuntimeException("Add is not supported by FastQueue. Use FastArray instead"); - } - } - - public FastQueue getQueue() { - return queue; - } - - public void setQueue(FastQueue queue) { - this.queue = queue; - } -} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/struct/GrowQueue_B.java ddogleg-0.22+ds/src/org/ddogleg/struct/GrowQueue_B.java --- ddogleg-0.18+ds/src/org/ddogleg/struct/GrowQueue_B.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/struct/GrowQueue_B.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,245 +0,0 @@ -/* - * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. - * - * This file is part of DDogleg (http://ddogleg.org). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ddogleg.struct; - - -import java.util.Arrays; - -/** - * This is a queue that is composed of booleans. Elements are added and removed from the tail - * - * @author Peter Abeles - */ -public class GrowQueue_B implements GrowQueue { - - public boolean data[]; - public int size; - - public GrowQueue_B(int maxSize) { - data = new boolean[ maxSize ]; - this.size = 0; - } - - public GrowQueue_B() { - this(10); - } - - /** - * Creates a queue with the specified length as its size filled with false - */ - public static GrowQueue_B zeros( int length ) { - GrowQueue_B out = new GrowQueue_B(length); - out.size = length; - return out; - } - - public static GrowQueue_B array( boolean ...values ) { - GrowQueue_B out = zeros(values.length); - for (int i = 0; i < values.length; i++) { - out.data[i] = values[i]; - } - return out; - } - - /** - * Non-zero values are set to true - */ - public static GrowQueue_B array( int ...values ) { - GrowQueue_B out = zeros(values.length); - for (int i = 0; i < values.length; i++) { - out.data[i] = values[i] != 0; - } - return out; - } - - @Override - public void reset() { - size = 0; - } - - public void add(boolean value) { - push(value); - } - - public void push( boolean val ) { - if( size == data.length ) { - boolean temp[] = new boolean[ size * 2+5]; - System.arraycopy(data,0,temp,0,size); - data = temp; - } - data[size++] = val; - } - - public boolean get( int index ) { - if( index < 0 || index >= size) - throw new IndexOutOfBoundsException("index = "+index+" size = "+size); - return data[index]; - } - - /** - * Returns an element starting from the end of the list. 0 = size -1 - */ - public boolean getTail( int index ) { - if( index < 0 || index >= size) - throw new IndexOutOfBoundsException("index = "+index+" size = "+size); - return data[size-index-1]; - } - - public boolean unsafe_get( int index ) { - return data[index]; - } - - public void set( int index, boolean value ) { - data[index] = value; - } - - public void setTo( GrowQueue_B original ) { - resize(original.size); - System.arraycopy(original.data, 0, data, 0, size()); - } - - public void fill( boolean value ) { - Arrays.fill(data, 0, size, value); - } - - /** - * Inserts the value at the specified index and shifts all the other values down. - */ - public void insert( int index , boolean value ) { - if( size == data.length ) { - boolean temp[] = new boolean[ size * 2+5]; - System.arraycopy(data,0,temp,0,index); - temp[index] = value; - System.arraycopy(data,index,temp,index+1,size-index); - this.data = temp; - size++; - } else { - size++; - for( int i = size-1; i > index; i-- ) { - data[i] = data[i-1]; - } - data[index] = value; - } - } - - public boolean removeTail() { - if( size > 0 ) { - size--; - return data[size]; - } else { - throw new RuntimeException("Size zero, no tail"); - } - } - - - /** - * Removes elements from the list starting at 'first' and ending at 'last' - * @param first First index you wish to remove. Inclusive. - * @param last Last index you wish to remove. Inclusive. - */ - public void remove( int first , int last ) { - if( last < first ) - throw new IllegalArgumentException("first <= last"); - if( last >= size ) - throw new IllegalArgumentException("last must be less than the max size"); - - int delta = last-first+1; - for( int i = last+1; i < size; i++ ) { - data[i-delta] = data[i]; - } - size -= delta; - } - - @Override - public void resize( int size ) { - if( data.length < size ) { - data = new boolean[size]; - } - this.size = size; - } - - @Override - public void extend( int size ) { - if( data.length < size ) { - boolean []tmp = new boolean[size]; - System.arraycopy(data,0,tmp,0,this.size); - data = tmp; - } - this.size = size; - } - - @Override - public void setMaxSize( int size ) { - if( data.length < size ) { - data = new boolean[size]; - } - } - - @Override - public int size() { - return size; - } - - @Override - public void zero() { - Arrays.fill(data,0,size,false); - } - - @Override - public GrowQueue_B copy() { - GrowQueue_B ret = new GrowQueue_B(size); - ret.setTo(this); - return ret; - } - - @Override - public void flip() { - if( size <= 1 ) - return; - - int D = size/2; - for (int i = 0,j=size-1; i < D; i++,j--) { - boolean tmp = data[i]; - data[i] = data[j]; - data[j] = tmp; - } - } - - public boolean pop() { - return data[--size]; - } - - /** - * Returns the index of the first element with the specified 'value'. return -1 if it wasn't found - * @param value Value to search for - * @return index or -1 if it's not in the list - */ - public int indexOf( boolean value ) { - for (int i = 0; i < size; i++) { - if( data[i] == value ) - return i; - } - return -1; - } - - @Override - public void sort() { - throw new RuntimeException("Undefined for boolean"); - } -} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/struct/GrowQueue_F32.java ddogleg-0.22+ds/src/org/ddogleg/struct/GrowQueue_F32.java --- ddogleg-0.18+ds/src/org/ddogleg/struct/GrowQueue_F32.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/struct/GrowQueue_F32.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,328 +0,0 @@ -/* - * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. - * - * This file is part of DDogleg (http://ddogleg.org). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ddogleg.struct; - - -import java.util.Arrays; - -/** - * This is a queue that is composed of integers. Elements are added and removed from the tail - * - * @author Peter Abeles - */ -public class GrowQueue_F32 implements GrowQueue { - - public float data[]; - public int size; - - public GrowQueue_F32( int maxSize ) { - data = new float[ maxSize ]; - this.size = 0; - } - - public GrowQueue_F32() { - this(10); - } - - /** - * Creates a queue with the specified length as its size filled with all zeros - */ - public static GrowQueue_F32 zeros( int length ) { - GrowQueue_F32 out = new GrowQueue_F32(length); - out.size = length; - return out; - } - - public static GrowQueue_F32 array( float ...values ) { - GrowQueue_F32 out = zeros(values.length); - for (int i = 0; i < values.length; i++) { - out.data[i] = values[i]; - } - return out; - } - - @Override - public void reset() { - size = 0; - } - - public void addAll( GrowQueue_F32 queue ) { - if( size+queue.size > data.length ) { - float temp[] = new float[ (size+queue.size) * 2]; - System.arraycopy(data,0,temp,0,size); - data = temp; - } - System.arraycopy(queue.data,0,data,size,queue.size); - size += queue.size; - } - - public void addAll( float[] array , int startIndex , int endIndex ) { - if( endIndex > array.length ) - throw new IllegalAccessError("endIndex is larger than input array"); - - int arraySize = endIndex-startIndex; - - if( size+arraySize > data.length ) { - float temp[] = new float[ (size+arraySize) * 2]; - System.arraycopy(data,0,temp,0,size); - data = temp; - } - System.arraycopy(array,startIndex,data,size,arraySize); - size += arraySize; - } - - public void add( float val ) { - push(val); - } - - public void push( float val ) { - if( size == data.length ) { - float temp[] = new float[ size * 2+5]; - System.arraycopy(data,0,temp,0,size); - data = temp; - } - data[size++] = val; - } - - public void remove( int index ) { - for( int i = index+1; i < size; i++ ) { - data[i-1] = data[i]; - } - size--; - } - - /** - * Sets this array to be equal to the array segment - * @param array (Input) source array - * @param offset first index - * @param length number of elements to copy - */ - public void setTo( float[] array , int offset , int length ) { - resize(length); - System.arraycopy(array,offset,data,0,length); - } - - /** - * Removes elements from the list starting at 'first' and ending at 'last' - * @param first First index you wish to remove. Inclusive. - * @param last Last index you wish to remove. Inclusive. - */ - public void remove( int first , int last ) { - if( last < first ) - throw new IllegalArgumentException("first <= last"); - if( last >= size ) - throw new IllegalArgumentException("last must be less than the max size"); - - int delta = last-first+1; - for( int i = last+1; i < size; i++ ) { - data[i-delta] = data[i]; - } - size -= delta; - } - - /** - * Inserts the value at the specified index and shifts all the other values down. - */ - public void insert( int index , float value ) { - if( size == data.length ) { - float temp[] = new float[ size * 2+5]; - System.arraycopy(data,0,temp,0,index); - temp[index] = value; - System.arraycopy(data,index,temp,index+1,size-index); - this.data = temp; - size++; - } else { - size++; - for( int i = size-1; i > index; i-- ) { - data[i] = data[i-1]; - } - data[index] = value; - } - } - - public float removeTail() { - if( size > 0 ) { - size--; - return data[size]; - } else { - throw new RuntimeException("Size zero, no tail"); - } - } - - public float get( int index ) { - if( index < 0 || index >= size) - throw new IndexOutOfBoundsException("index = "+index+" size = "+size); - return data[index]; - } - - /** - * Returns an element starting from the end of the list. 0 = size -1 - */ - public float getTail( int index ) { - if( index < 0 || index >= size) - throw new IndexOutOfBoundsException("index = "+index+" size = "+size); - return data[size-index-1]; - } - - /** - * Gets the value at the index which corresponds to the specified fraction - * @param fraction 0 to 1 inclusive - * @return value at fraction - */ - public float getFraction( double fraction ) { - return get( (int)((size-1)*fraction) ); - } - - public float unsafe_get( int index ) { - return data[index]; - } - - public void set( int index, float value ) { - data[index] = value; - } - - public void setTo( GrowQueue_F32 original ) { - resize(original.size); - System.arraycopy(original.data, 0, data, 0, size()); - } - - public void fill( float value ) { - Arrays.fill(data, 0, size, value); - } - - public boolean contains( float value ) { - for (int i = 0; i < size; i++) { - if( data[i] == value ) - return true; - } - return false; - } - - @Override - public void resize( int size ) { - if( data.length < size ) { - data = new float[size]; - } - this.size = size; - } - - @Override - public void extend( int size ) { - if( data.length < size ) { - float []tmp = new float[size]; - System.arraycopy(data,0,tmp,0,this.size); - data = tmp; - } - this.size = size; - } - - @Override - public void setMaxSize( int size ) { - if( data.length < size ) { - data = new float[size]; - } - } - - @Override - public int size() { - return size; - } - - @Override - public void zero() { - Arrays.fill(data,0,size,0); - } - - @Override - public GrowQueue_F32 copy() { - GrowQueue_F32 ret = new GrowQueue_F32(size); - ret.setTo(this); - return ret; - } - - @Override - public void flip() { - if( size <= 1 ) - return; - - int D = size/2; - for (int i = 0,j=size-1; i < D; i++,j--) { - float tmp = data[i]; - data[i] = data[j]; - data[j] = tmp; - } - } - - public float pop() { - return data[--size]; - } - - /** - * Returns the index of the first element with the specified 'value'. return -1 if it wasn't found - * @param value Value to search for - * @return index or -1 if it's not in the list - */ - public int indexOf( float value ) { - for (int i = 0; i < size; i++) { - if( data[i] == value ) - return i; - } - return -1; - } - - public int indexOfGreatest() { - if( size <=- 0 ) - return -1; - - int selected = 0; - float best = data[0]; - - for (int i = 1; i < size; i++) { - if( data[i] > best ) { - best = data[i]; - selected = i; - } - } - - return selected ; - } - - public int indexOfLeast() { - if( size <=- 0 ) - return -1; - - int selected = 0; - float best = data[0]; - - for (int i = 1; i < size; i++) { - if( data[i] < best ) { - best = data[i]; - selected = i; - } - } - - return selected; - } - - @Override - public void sort() { - Arrays.sort(data,0,size); - } - -} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/struct/GrowQueue_F64.java ddogleg-0.22+ds/src/org/ddogleg/struct/GrowQueue_F64.java --- ddogleg-0.18+ds/src/org/ddogleg/struct/GrowQueue_F64.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/struct/GrowQueue_F64.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,337 +0,0 @@ -/* - * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. - * - * This file is part of DDogleg (http://ddogleg.org). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ddogleg.struct; - - -import java.util.Arrays; - -/** - * This is a queue that is composed of integers. Elements are added and removed from the tail - * - * @author Peter Abeles - */ -public class GrowQueue_F64 implements GrowQueue{ - - public double data[]; - public int size; - - public GrowQueue_F64( int maxSize ) { - data = new double[ maxSize ]; - this.size = 0; - } - - public GrowQueue_F64() { - this(10); - } - - /** - * Creates a queue with the specified length as its size filled with all zeros - */ - public static GrowQueue_F64 zeros( int length ) { - GrowQueue_F64 out = new GrowQueue_F64(length); - out.size = length; - return out; - } - - public static GrowQueue_F64 array( double ...values ) { - GrowQueue_F64 out = zeros(values.length); - for (int i = 0; i < values.length; i++) { - out.data[i] = values[i]; - } - return out; - } - - @Override - public void reset() { - size = 0; - } - - public void addAll( GrowQueue_F64 queue ) { - if( size+queue.size > data.length ) { - double temp[] = new double[ (size+queue.size) * 2]; - System.arraycopy(data,0,temp,0,size); - data = temp; - } - System.arraycopy(queue.data,0,data,size,queue.size); - size += queue.size; - } - - public void addAll( double[] array , int startIndex , int endIndex ) { - if( endIndex > array.length ) - throw new IllegalAccessError("endIndex is larger than input array"); - - int arraySize = endIndex-startIndex; - - if( size+arraySize > data.length ) { - double temp[] = new double[ (size+arraySize) * 2]; - System.arraycopy(data,0,temp,0,size); - data = temp; - } - System.arraycopy(array,startIndex,data,size,arraySize); - size += arraySize; - } - - public void add( double val ) { - push(val); - } - - public void push( double val ) { - if( size == data.length ) { - double temp[]; - try { - temp = new double[ size * 2+5]; - } catch( OutOfMemoryError e ) { - System.gc(); -// System.out.println("Memory on size "+size+" or "+(size*8/1024/1024)+" MB"); -// System.out.println("Trying smaller increment"); - temp = new double[ 3*size/2]; - } - System.arraycopy(data,0,temp,0,size); - data = temp; - } - data[size++] = val; - } - - /** - * Sets this array to be equal to the array segment - * @param array (Input) source array - * @param offset first index - * @param length number of elements to copy - */ - public void setTo( double[] array , int offset , int length ) { - resize(length); - System.arraycopy(array,offset,data,0,length); - } - - public void remove( int index ) { - for( int i = index+1; i < size; i++ ) { - data[i-1] = data[i]; - } - size--; - } - - - /** - * Removes elements from the list starting at 'first' and ending at 'last' - * @param first First index you wish to remove. Inclusive. - * @param last Last index you wish to remove. Inclusive. - */ - public void remove( int first , int last ) { - if( last < first ) - throw new IllegalArgumentException("first <= last"); - if( last >= size ) - throw new IllegalArgumentException("last must be less than the max size"); - - int delta = last-first+1; - for( int i = last+1; i < size; i++ ) { - data[i-delta] = data[i]; - } - size -= delta; - } - - /** - * Inserts the value at the specified index and shifts all the other values down. - */ - public void insert( int index , double value ) { - if( size == data.length ) { - double temp[] = new double[ size * 2+5]; - System.arraycopy(data,0,temp,0,index); - temp[index] = value; - System.arraycopy(data,index,temp,index+1,size-index); - this.data = temp; - size++; - } else { - size++; - for( int i = size-1; i > index; i-- ) { - data[i] = data[i-1]; - } - data[index] = value; - } - } - - public double removeTail() { - if( size > 0 ) { - size--; - return data[size]; - } else { - throw new RuntimeException("Size zero, no tail"); - } - } - - public double get( int index ) { - if( index < 0 || index >= size) - throw new IndexOutOfBoundsException("index = "+index+" size = "+size); - return data[index]; - } - - /** - * Returns an element starting from the end of the list. 0 = size -1 - */ - public double getTail( int index ) { - if( index < 0 || index >= size) - throw new IndexOutOfBoundsException("index = "+index+" size = "+size); - return data[size-index-1]; - } - - /** - * Gets the value at the index which corresponds to the specified fraction - * @param fraction 0 to 1 inclusive - * @return value at fraction - */ - public double getFraction( double fraction ) { - return get( (int)((size-1)*fraction) ); - } - - public double unsafe_get( int index ) { - return data[index]; - } - - public void set( int index , double value ) { - data[index] = value; - } - - @Override - public void setTo( GrowQueue_F64 original ) { - resize(original.size); - System.arraycopy(original.data, 0, data, 0, size()); - } - - @Override - public void resize( int size ) { - if( data.length < size ) { - data = new double[size]; - } - this.size = size; - } - - @Override - public void extend( int size ) { - if( data.length < size ) { - double []tmp = new double[size]; - System.arraycopy(data,0,tmp,0,this.size); - data = tmp; - } - this.size = size; - } - - public void fill( double value ) { - Arrays.fill(data, 0, size, value); - } - - public boolean contains( double value ) { - for (int i = 0; i < size; i++) { - if( data[i] == value ) - return true; - } - return false; - } - - @Override - public void setMaxSize( int size ) { - if( data.length < size ) { - data = new double[size]; - } - } - - @Override - public int size() { - return size; - } - - @Override - public void zero() { - Arrays.fill(data,0,size,0); - } - - @Override - public GrowQueue_F64 copy() { - GrowQueue_F64 ret = new GrowQueue_F64(size); - ret.setTo(this); - return ret; - } - - @Override - public void flip() { - if( size <= 1 ) - return; - - int D = size/2; - for (int i = 0,j=size-1; i < D; i++,j--) { - double tmp = data[i]; - data[i] = data[j]; - data[j] = tmp; - } - } - - public double pop() { - return data[--size]; - } - - /** - * Returns the index of the first element with the specified 'value'. return -1 if it wasn't found - * @param value Value to search for - * @return index or -1 if it's not in the list - */ - public int indexOf( double value ) { - for (int i = 0; i < size; i++) { - if( data[i] == value ) - return i; - } - return -1; - } - - public int indexOfGreatest() { - if( size <=- 0 ) - return -1; - - int selected = 0; - double best = data[0]; - - for (int i = 1; i < size; i++) { - if( data[i] > best ) { - best = data[i]; - selected = i; - } - } - - return selected ; - } - - public int indexOfLeast() { - if( size <=- 0 ) - return -1; - - int selected = 0; - double best = data[0]; - - for (int i = 1; i < size; i++) { - if( data[i] < best ) { - best = data[i]; - selected = i; - } - } - - return selected; - } - - @Override - public void sort() { - Arrays.sort(data,0,size); - } -} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/struct/GrowQueue_I32.java ddogleg-0.22+ds/src/org/ddogleg/struct/GrowQueue_I32.java --- ddogleg-0.18+ds/src/org/ddogleg/struct/GrowQueue_I32.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/struct/GrowQueue_I32.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,306 +0,0 @@ -/* - * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. - * - * This file is part of DDogleg (http://ddogleg.org). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ddogleg.struct; - - -import java.util.Arrays; - -/** - * This is a queue that is composed of integers. Elements are added and removed from the tail - * - * @author Peter Abeles - */ -public class GrowQueue_I32 implements GrowQueue { - - public int data[]; - public int size; - - public GrowQueue_I32( int maxSize ) { - data = new int[ maxSize ]; - this.size = 0; - } - - public GrowQueue_I32() { - this(10); - } - - /** - * Creates a queue with the specified length as its size filled with all zeros - */ - public static GrowQueue_I32 zeros( int length ) { - GrowQueue_I32 out = new GrowQueue_I32(length); - out.size = length; - return out; - } - - public static GrowQueue_I32 array( int ...values ) { - GrowQueue_I32 out = zeros(values.length); - for (int i = 0; i < values.length; i++) { - out.data[i] = values[i]; - } - return out; - } - - @Override - public void reset() { - size = 0; - } - - public void addAll( GrowQueue_I32 queue ) { - if( size+queue.size > data.length ) { - int temp[] = new int[ (size+queue.size) * 2]; - System.arraycopy(data,0,temp,0,size); - data = temp; - } - System.arraycopy(queue.data,0,data,size,queue.size); - size += queue.size; - } - - public void addAll( int[] array , int startIndex , int endIndex ) { - if( endIndex > array.length ) - throw new IllegalAccessError("endIndex is larger than input array"); - - int arraySize = endIndex-startIndex; - - if( size+arraySize > data.length ) { - int temp[] = new int[ (size+arraySize) * 2]; - System.arraycopy(data,0,temp,0,size); - data = temp; - } - System.arraycopy(array,startIndex,data,size,arraySize); - size += arraySize; - } - - public void add(int value) { - push(value); - } - - public void push( int val ) { - if( size == data.length ) { - int temp[] = new int[ size * 2+5]; - System.arraycopy(data,0,temp,0,size); - data = temp; - } - data[size++] = val; - } - - public int get( int index ) { - if( index < 0 || index >= size) - throw new IndexOutOfBoundsException("index = "+index+" size = "+size); - return data[index]; - } - - /** - * Returns an element starting from the end of the list. 0 = size -1 - */ - public int getTail( int index ) { - if( index < 0 || index >= size) - throw new IndexOutOfBoundsException("index = "+index+" size = "+size); - return data[size-index-1]; - } - - /** - * Gets the value at the index which corresponds to the specified fraction - * @param fraction 0 to 1 inclusive - * @return value at fraction - */ - public int getFraction( double fraction ) { - return get( (int)((size-1)*fraction) ); - } - - public int unsafe_get( int index ) { - return data[index]; - } - - public void set( int index , int value ) { - data[index] = value; - } - - @Override - public void setTo( GrowQueue_I32 original ) { - resize(original.size); - System.arraycopy(original.data, 0, data, 0, size()); - } - - /** - * Sets this array to be equal to the array segment - * @param array (Input) source array - * @param offset first index - * @param length number of elements to copy - */ - public void setTo( int[] array , int offset , int length ) { - resize(length); - System.arraycopy(array,offset,data,0,length); - } - - public void remove( int index ) { - for( int i = index+1; i < size; i++ ) { - data[i-1] = data[i]; - } - size--; - } - - /** - * Removes elements from the list starting at 'first' and ending at 'last' - * @param first First index you wish to remove. Inclusive. - * @param last Last index you wish to remove. Inclusive. - */ - public void remove( int first , int last ) { - if( last < first ) - throw new IllegalArgumentException("first <= last"); - if( last >= size ) - throw new IllegalArgumentException("last must be less than the max size"); - - int delta = last-first+1; - for( int i = last+1; i < size; i++ ) { - data[i-delta] = data[i]; - } - size -= delta; - } - - /** - * Inserts the value at the specified index and shifts all the other values down. - */ - public void insert( int index , int value ) { - if( size == data.length ) { - int temp[] = new int[ size * 2+5]; - System.arraycopy(data,0,temp,0,index); - temp[index] = value; - System.arraycopy(data,index,temp,index+1,size-index); - this.data = temp; - size++; - } else { - size++; - for( int i = size-1; i > index; i-- ) { - data[i] = data[i-1]; - } - data[index] = value; - } - } - - /** - * Removes the first 'total' elements from the queue. Element 'total' will be the new start of the queue - * - * @param total Number of elements to remove from the head of the queue - */ - public void removeHead( int total ) { - for( int j = total; j < size; j++ ) { - data[j-total] = data[j]; - } - size -= total; - } - - public int removeTail() { - if( size > 0 ) { - size--; - return data[size]; - } else { - throw new RuntimeException("Size zero, no tail"); - } - } - - @Override - public void resize( int size ) { - if( data.length < size ) { - data = new int[size]; - } - this.size = size; - } - - @Override - public void extend( int size ) { - if( data.length < size ) { - int []tmp = new int[size]; - System.arraycopy(data,0,tmp,0,this.size); - data = tmp; - } - this.size = size; - } - - @Override - public void setMaxSize( int size ) { - if( data.length < size ) { - data = new int[size]; - } - } - - public void fill( int value ) { - Arrays.fill(data,0,size,value); - } - - public boolean contains( int value ) { - for (int i = 0; i < size; i++) { - if( data[i] == value ) - return true; - } - return false; - } - - @Override - public int size() { - return size; - } - - public int pop() { - return data[--size]; - } - - @Override - public void zero() { - Arrays.fill(data,0,size,0); - } - - @Override - public GrowQueue_I32 copy() { - GrowQueue_I32 ret = new GrowQueue_I32(size); - ret.setTo(this); - return ret; - } - - @Override - public void flip() { - if( size <= 1 ) - return; - - int D = size/2; - for (int i = 0,j=size-1; i < D; i++,j--) { - int tmp = data[i]; - data[i] = data[j]; - data[j] = tmp; - } - } - - /** - * Returns the index of the first element with the specified 'value'. return -1 if it wasn't found - * @param value Value to search for - * @return index or -1 if it's not in the list - */ - public int indexOf( int value ) { - for (int i = 0; i < size; i++) { - if( data[i] == value ) - return i; - } - return -1; - } - - @Override - public void sort() { - Arrays.sort(data,0,size); - } -} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/struct/GrowQueue_I64.java ddogleg-0.22+ds/src/org/ddogleg/struct/GrowQueue_I64.java --- ddogleg-0.18+ds/src/org/ddogleg/struct/GrowQueue_I64.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/struct/GrowQueue_I64.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,295 +0,0 @@ -/* - * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. - * - * This file is part of DDogleg (http://ddogleg.org). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ddogleg.struct; - - -import java.util.Arrays; - -/** - * This is a queue that is composed of integers. Elements are added and removed from the tail - * - * @author Peter Abeles - */ -public class GrowQueue_I64 implements GrowQueue { - - public long data[]; - public int size; - - public GrowQueue_I64(int maxSize) { - data = new long[ maxSize ]; - this.size = 0; - } - - public GrowQueue_I64() { - this(10); - } - - /** - * Creates a queue with the specified length as its size filled with all zeros - */ - public static GrowQueue_I64 zeros( int length ) { - GrowQueue_I64 out = new GrowQueue_I64(length); - out.size = length; - return out; - } - - public static GrowQueue_I64 array( long ...values ) { - GrowQueue_I64 out = zeros(values.length); - for (int i = 0; i < values.length; i++) { - out.data[i] = values[i]; - } - return out; - } - - @Override - public void reset() { - size = 0; - } - - public void addAll( GrowQueue_I64 queue ) { - if( size+queue.size > data.length ) { - long temp[] = new long[ (size+queue.size) * 2]; - System.arraycopy(data,0,temp,0,size); - data = temp; - } - System.arraycopy(queue.data,0,data,size,queue.size); - size += queue.size; - } - - public void addAll( long[] array , int startIndex , int endIndex ) { - if( endIndex > array.length ) - throw new IllegalAccessError("endIndex is larger than input array"); - - int arraySize = endIndex-startIndex; - - if( size+arraySize > data.length ) { - long temp[] = new long[ (size+arraySize) * 2]; - System.arraycopy(data,0,temp,0,size); - data = temp; - } - System.arraycopy(array,startIndex,data,size,arraySize); - size += arraySize; - } - - public void add(long value) { - push(value); - } - - public void push( long val ) { - if( size == data.length ) { - long temp[] = new long[ size * 2+5]; - System.arraycopy(data,0,temp,0,size); - data = temp; - } - data[size++] = val; - } - - public long get( int index ) { - if( index < 0 || index >= size) - throw new IndexOutOfBoundsException("index = "+index+" size = "+size); - return data[index]; - } - - /** - * Returns an element starting from the end of the list. 0 = size -1 - */ - public long getTail( int index ) { - if( index < 0 || index >= size) - throw new IndexOutOfBoundsException("index = "+index+" size = "+size); - return data[size-index-1]; - } - - /** - * Gets the value at the index which corresponds to the specified fraction - * @param fraction 0 to 1 inclusive - * @return value at fraction - */ - public long getFraction( double fraction ) { - return get( (int)((size-1)*fraction) ); - } - - public long unsafe_get( int index ) { - return data[index]; - } - - public void set( int index , long value ) { - data[index] = value; - } - - @Override - public void setTo( GrowQueue_I64 original ) { - resize(original.size); - System.arraycopy(original.data, 0, data, 0, size()); - } - - /** - * Sets this array to be equal to the array segment - * @param array (Input) source array - * @param offset first index - * @param length number of elements to copy - */ - public void setTo( long[] array , int offset , int length ) { - resize(length); - System.arraycopy(array,offset,data,0,length); - } - - public void remove( int index ) { - for( int i = index+1; i < size; i++ ) { - data[i-1] = data[i]; - } - size--; - } - - - /** - * Removes elements from the list starting at 'first' and ending at 'last' - * @param first First index you wish to remove. Inclusive. - * @param last Last index you wish to remove. Inclusive. - */ - public void remove( int first , int last ) { - if( last < first ) - throw new IllegalArgumentException("first <= last"); - if( last >= size ) - throw new IllegalArgumentException("last must be less than the max size"); - - int delta = last-first+1; - for( int i = last+1; i < size; i++ ) { - data[i-delta] = data[i]; - } - size -= delta; - } - - /** - * Inserts the value at the specified index and shifts all the other values down. - */ - public void insert( int index , long value ) { - if( size == data.length ) { - long temp[] = new long[ size * 2+5]; - System.arraycopy(data,0,temp,0,index); - temp[index] = value; - System.arraycopy(data,index,temp,index+1,size-index); - this.data = temp; - size++; - } else { - size++; - for( int i = size-1; i > index; i-- ) { - data[i] = data[i-1]; - } - data[index] = value; - } - } - - public long removeTail() { - if( size > 0 ) { - size--; - return data[size]; - } else { - throw new RuntimeException("Size zero, no tail"); - } - } - - @Override - public void resize( int size ) { - if( data.length < size ) { - data = new long[size]; - } - this.size = size; - } - - @Override - public void extend( int size ) { - if( data.length < size ) { - long []tmp = new long[size]; - System.arraycopy(data,0,tmp,0,this.size); - data = tmp; - } - this.size = size; - } - - public void fill( long value ) { - Arrays.fill(data, 0, size, value); - } - - public boolean contains( long value ) { - for (int i = 0; i < size; i++) { - if( data[i] == value ) - return true; - } - return false; - } - - @Override - public void setMaxSize( int size ) { - if( data.length < size ) { - data = new long[size]; - } - } - - @Override - public int size() { - return size; - } - - public long pop() { - return data[--size]; - } - - @Override - public void zero() { - Arrays.fill(data,0,size,0); - } - - @Override - public GrowQueue_I64 copy() { - GrowQueue_I64 ret = new GrowQueue_I64(size); - ret.setTo(this); - return ret; - } - - @Override - public void flip() { - if( size <= 1 ) - return; - - int D = size/2; - for (int i = 0,j=size-1; i < D; i++,j--) { - long tmp = data[i]; - data[i] = data[j]; - data[j] = tmp; - } - } - - /** - * Returns the index of the first element with the specified 'value'. return -1 if it wasn't found - * @param value Value to search for - * @return index or -1 if it's not in the list - */ - public int indexOf( long value ) { - for (int i = 0; i < size; i++) { - if( data[i] == value ) - return i; - } - return -1; - } - - @Override - public void sort() { - Arrays.sort(data,0,size); - } -} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/struct/GrowQueue_I8.java ddogleg-0.22+ds/src/org/ddogleg/struct/GrowQueue_I8.java --- ddogleg-0.18+ds/src/org/ddogleg/struct/GrowQueue_I8.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/struct/GrowQueue_I8.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,313 +0,0 @@ -/* - * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. - * - * This file is part of DDogleg (http://ddogleg.org). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ddogleg.struct; - - -import java.util.Arrays; - -/** - * This is a queue that is composed of bytes. Elements are added and removed from the tail - * - * @author Peter Abeles - */ -public class GrowQueue_I8 implements GrowQueue { - - public byte data[]; - public int size; - - public GrowQueue_I8(int maxSize) { - data = new byte[ maxSize ]; - this.size = 0; - } - - public GrowQueue_I8() { - this(10); - } - - /** - * Creates a queue with the specified length as its size filled with all zeros - */ - public static GrowQueue_I8 zeros( int length ) { - GrowQueue_I8 out = new GrowQueue_I8(length); - out.size = length; - return out; - } - - public static GrowQueue_I8 array( int ...values ) { - GrowQueue_I8 out = zeros(values.length); - for (int i = 0; i < values.length; i++) { - out.data[i] = (byte)values[i]; - } - return out; - } - - @Override - public void reset() { - size = 0; - } - - public void addAll( GrowQueue_I8 queue ) { - if( size+queue.size > data.length ) { - byte temp[] = new byte[ (size+queue.size) * 2]; - System.arraycopy(data,0,temp,0,size); - data = temp; - } - System.arraycopy(queue.data,0,data,size,queue.size); - size += queue.size; - } - - public void addAll( byte[] array , int startIndex , int endIndex ) { - if( endIndex > array.length ) - throw new IllegalAccessError("endIndex is larger than input array"); - - int arraySize = endIndex-startIndex; - - if( size+arraySize > data.length ) { - byte temp[] = new byte[ (size+arraySize) * 2]; - System.arraycopy(data,0,temp,0,size); - data = temp; - } - System.arraycopy(array,startIndex,data,size,arraySize); - size += arraySize; - } - - public void add(int value) { - push(value); - } - - public void push( int val ) { - if( size == data.length ) { - byte temp[] = new byte[ size * 2+5]; - System.arraycopy(data,0,temp,0,size); - data = temp; - } - data[size++] = (byte)val; - } - - public int get( int index ) { - if( index < 0 || index >= size) - throw new IndexOutOfBoundsException("index = "+index+" size = "+size); - return data[index]; - } - - /** - * Returns an element starting from the end of the list. 0 = size -1 - */ - public int getTail( int index ) { - if( index < 0 || index >= size) - throw new IndexOutOfBoundsException("index = "+index+" size = "+size); - return data[size-index-1]; - } - - /** - * Gets the value at the index which corresponds to the specified fraction - * @param fraction 0 to 1 inclusive - * @return value at fraction - */ - public int getFraction( double fraction ) { - return get( (int)((size-1)*fraction) ); - } - - public int unsafe_get( int index ) { - return data[index]; - } - - public void set( int index , int value ) { - data[index] = (byte)value; - } - - public void setTo( GrowQueue_I8 original ) { - resize(original.size); - System.arraycopy(original.data, 0, data, 0, size()); - } - - /** - * Inserts the value at the specified index and shifts all the other values down. - */ - public void insert( int index , int value ) { - if( size == data.length ) { - byte temp[] = new byte[ size * 2+5]; - System.arraycopy(data,0,temp,0,index); - temp[index] = (byte)value; - System.arraycopy(data,index,temp,index+1,size-index); - this.data = temp; - size++; - } else { - size++; - for( int i = size-1; i > index; i-- ) { - data[i] = data[i-1]; - } - data[index] = (byte)value; - } - } - - public byte removeTail() { - if( size > 0 ) { - size--; - return data[size]; - } else { - throw new RuntimeException("Size zero, no tail"); - } - } - - /** - * Sets this array to be equal to the array segment - * @param array (Input) source array - * @param offset first index - * @param length number of elements to copy - */ - public void setTo( byte[] array , int offset , int length ) { - resize(length); - System.arraycopy(array,offset,data,0,length); - } - - /** - * Removes elements from the list starting at 'first' and ending at 'last' - * @param first First index you wish to remove. Inclusive. - * @param last Last index you wish to remove. Inclusive. - */ - public void remove( int first , int last ) { - if( last < first ) - throw new IllegalArgumentException("first <= last"); - if( last >= size ) - throw new IllegalArgumentException("last must be less than the max size"); - - int delta = last-first+1; - for( int i = last+1; i < size; i++ ) { - data[i-delta] = data[i]; - } - size -= delta; - } - - public void fill( byte value ) { - Arrays.fill(data, 0, size, value); - } - - public boolean contains( byte value ) { - for (int i = 0; i < size; i++) { - if( data[i] == value ) - return true; - } - return false; - } - - @Override - public void resize( int size ) { - if( data.length < size ) { - data = new byte[size]; - } - this.size = size; - } - - @Override - public void extend( int size ) { - if( data.length < size ) { - byte []tmp = new byte[size]; - System.arraycopy(data,0,tmp,0,this.size); - data = tmp; - } - this.size = size; - } - - public void setMaxSize( int size ) { - if( data.length < size ) { - data = new byte[size]; - } - } - - @Override - public int size() { - return size; - } - - public int pop() { - return data[--size]; - } - - @Override - public void zero() { - Arrays.fill(data,0,size,(byte)0); - } - - @Override - public GrowQueue_I8 copy() { - GrowQueue_I8 ret = new GrowQueue_I8(size); - ret.setTo(this); - return ret; - } - - @Override - public void flip() { - if( size <= 1 ) - return; - - int D = size/2; - for (int i = 0,j=size-1; i < D; i++,j--) { - byte tmp = data[i]; - data[i] = data[j]; - data[j] = tmp; - } - } - - /** - * Prints the queue to stdout as a hex array - */ - public void printHex() { - System.out.print("[ "); - for (int i = 0; i < size; i++) { - System.out.printf("0x%02X ",data[i]); - } - System.out.print("]"); - } - - public static GrowQueue_I8 parseHex( String message ) { - message = message.replaceAll("\\[",""); - message = message.replaceAll("\\]",""); - message = message.replaceAll(" ",""); - - String words[] = message.split(","); - - GrowQueue_I8 out = new GrowQueue_I8(words.length); - out.size = words.length; - - for (int i = 0; i < words.length; i++) { - out.data[i] = Integer.decode(words[i]).byteValue(); - } - return out; - } - - /** - * Returns the index of the first element with the specified 'value'. return -1 if it wasn't found - * @param value Value to search for - * @return index or -1 if it's not in the list - */ - public int indexOf( byte value ) { - for (int i = 0; i < size; i++) { - if( data[i] == value ) - return i; - } - return -1; - } - - @Override - public void sort() { - Arrays.sort(data,0,size); - } - -} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/struct/GrowQueue.java ddogleg-0.22+ds/src/org/ddogleg/struct/GrowQueue.java --- ddogleg-0.18+ds/src/org/ddogleg/struct/GrowQueue.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/struct/GrowQueue.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,81 +0,0 @@ -/* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. - * - * This file is part of DDogleg (http://ddogleg.org). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ddogleg.struct; - -/** - * Interface for growable queues of primitive types - * - * @author Peter Abeles - */ -public interface GrowQueue> { - - /** - * Sets the size to zero. - */ - void reset(); - - /** - * Turns 'this' into a copy of 'original' - * @param original queue that is to be copied - */ - void setTo( T original ); - - /** - * Ensures that the internal array is at least this size and changes the size to be this. Array - * data is not saved - * @param size desired new size - */ - void resize( int size ); - - /** - * Ensures that the internal array this size. If a new array needs to be declared the old data - * is saved - * @param size desired new size - */ - void extend( int size ); - - /** - * Ensures that the internal array's length is at least this size. Size is left unchanged - * @param size minimum size of internal array - */ - void setMaxSize( int size ); - - /** - * Flips the elements such that a[i] = a[N-i-1] where N is the number of elements. - */ - void flip(); - - /** - * Number of elements in the queue - * @return size of queue - */ - int size(); - - /** - * Sets all elements to 0 or False for binary queues - */ - void zero(); - - T copy(); - - /** - * Sorts the data from smallest to largest - */ - void sort(); -} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/struct/LArrayAccessor.java ddogleg-0.22+ds/src/org/ddogleg/struct/LArrayAccessor.java --- ddogleg-0.18+ds/src/org/ddogleg/struct/LArrayAccessor.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/struct/LArrayAccessor.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.struct; + +/** + * Provides access to the elements inside very large or small arrays. Designed to provide efficient access if the + * data is compressed or not. As a convenience, a function is provided for creating a copy of the elements. + * + * @author Peter Abeles + */ +public interface LArrayAccessor

{ + /** + * Returns an instance of P which has the value of the element at 'index'. Note that the accessor will + * own the data type which is returned and can modify it on the next call. + * + *

This design is intended to be efficient when a massive array that's compressed and a very small array + * which is not compressed is used.

+ */ + P getTemp( int index ); + + /** Copies the element at 'index' into 'dst'. Only use if a copy is required. */ + void getCopy( int index , P dst ); + + /** Copies src into dst */ + void copy(P src, P dst); + + /** Number of elements in the set */ + int size(); + + /** + * Data type of element + */ + Class

getElementType(); +} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/struct/LinkedList.java ddogleg-0.22+ds/src/org/ddogleg/struct/LinkedList.java --- ddogleg-0.18+ds/src/org/ddogleg/struct/LinkedList.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/struct/LinkedList.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,470 +0,0 @@ -/* - * Copyright (c) 2012-2017, Peter Abeles. All Rights Reserved. - * - * This file is part of DDogleg (http://ddogleg.org). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ddogleg.struct; - -import java.util.List; -import java.util.Stack; - -/** - * A double linked list. Internal data structures are recycled to minimize creation of new memory. - * - * @author Peter Abeles - */ -public class LinkedList { - - // first element in the list - Element first; - // last element in the list - Element last; - // total number of elements in the list - int size; - - // recycled elements. It is assumed that all elements inside of here have all parameters set to null already - Stack> available = new Stack<>(); - - /** - * Puts the linked list back into its initial state. Elements are saved for later use. - */ - public void reset() { - Element e = first; - while( e != null ) { - Element n = e.next; - e.clear(); - available.add( e ); - e = n; - } - first = last = null; - size = 0; - } - - /** - * Checks to see if there are no elements in the list - * @return true if empty or false if not - */ - public boolean isEmpty() { - return size == 0; - } - - /** - * Returns the N'th element when counting from the from or from the back - * - * @param index Number of elements away from the first or last element. Must be positive. - * @return if true then the number of elements will be from first otherwise last - */ - - public Element getElement( int index , boolean fromFront ) { - if( index > size || index < 0 ) { - throw new IllegalArgumentException("index is out of bounds"); - } - if( fromFront ) { - Element e = first; - for( int i = 0; i < index; i++ ) { - e = e.next; - } - return e; - } else { - Element e = last; - for( int i = 0; i < index; i++ ) { - e = e.previous; - } - return e; - } - } - - /** - * Adds the element to the front of the list. - * - * @param object Object being added. - * @return The element it was placed inside of - */ - public Element pushHead( T object ) { - Element e = requestNew(); - e.object = object; - - if( first == null ) { - first = last = e; - } else { - e.next = first; - first.previous = e; - first = e; - } - size++; - - return e; - } - - /** - * Adds the element to the back of the list. - * - * @param object Object being added. - * @return The element it was placed inside of - */ - public Element pushTail( T object ) { - Element e = requestNew(); - e.object = object; - - if( last == null ) { - first = last = e; - } else { - e.previous = last; - last.next = e; - last = e; - } - size++; - - return e; - } - - /** - * Inserts the object into a new element after the provided element. - * - * @param previous Element which will be before the new one - * @param object The object which goes into the new element - * @return The new element - */ - public Element insertAfter( Element previous , T object ) { - Element e = requestNew(); - e.object = object; - e.previous = previous; - e.next = previous.next; - if( e.next != null ) { - e.next.previous = e; - } else { - last = e; - } - previous.next = e; - size++; - return e; - } - - /** - * Inserts the object into a new element before the provided element. - * - * @param next Element which will be after the new one - * @param object The object which goes into the new element - * @return The new element - */ - public Element insertBefore( Element next , T object ) { - Element e = requestNew(); - e.object = object; - e.previous = next.previous; - e.next = next; - - if( e.previous != null ) { - e.previous.next = e; - } else { - first = e; - } - next.previous = e; - size++; - return e; - } - - /** - * Swaps the location of the two elements - * - * @param a Element - * @param b Element - */ - public void swap( Element a , Element b ) { - if (a.next == b) { - if( a.previous != null ) { - a.previous.next = b; - } - if( b.next != null ) { - b.next.previous = a; - } - Element tmp = a.previous; - a.previous = b; - a.next = b.next; - b.previous = tmp; - b.next = a; - if( first == a ) - first = b; - if( last == b ) - last = a; - } else if (a.previous == b) { - if( a.next != null ) { - a.next.previous = b; - } - if( b.previous != null ) { - b.previous.next = a; - } - Element tmp = a.next; - a.next = b; - a.previous = b.previous; - b.previous = a; - b.next = tmp; - - if( first == b ) - first = a; - if( last == a ) - last = b; - } else { - if (a.next != null) { - a.next.previous = b; - } - if (a.previous != null) { - a.previous.next = b; - } - if (b.next != null) { - b.next.previous = a; - } - if (b.previous != null) { - b.previous.next = a; - } - Element tempNext = b.next; - Element tempPrev = b.previous; - b.next = a.next; - b.previous = a.previous; - a.next = tempNext; - a.previous = tempPrev; - - if (a.next == null) - last = a; - else if (b.next == null) - last = b; - if (a.previous == null) - first = a; - else if (b.previous == null) - first = b; - } - } - - /** - * Removes the element from the list and saves the element data structure for later reuse. - * @param element The item which is to be removed from the list - */ - public void remove( Element element ) { - if( element.next == null ) { - last = element.previous; - } else { - element.next.previous = element.previous; - } - if( element.previous == null ) { - first = element.next; - } else { - element.previous.next = element.next; - } - size--; - element.clear(); - available.push(element); - } - - /** - * Removes the first element from the list - * @return The object which was contained in the first element - */ - public T removeHead() { - if( first == null ) - throw new IllegalArgumentException("Empty list"); - - T ret = first.getObject(); - Element e = first; - available.push(first); - - if( first.next != null ) { - first.next.previous = null; - first = first.next; - } else { - // there's only one element in the list - first = last = null; - } - e.clear(); - size--; - return ret; - } - - /** - * Removes the last element from the list - * @return The object which was contained in the lsat element - */ - public Object removeTail() { - if( last == null ) - throw new IllegalArgumentException("Empty list"); - - Object ret = last.getObject(); - Element e = last; - available.add(last); - - if( last.previous != null ) { - last.previous.next = null; - last = last.previous; - } else { - // there's only one element in the list - first = last = null; - } - e.clear(); - size--; - return ret; - } - - /** - * Returns the first element which contains 'object' starting from the head. - * @param object Object which is being searched for - * @return First element which contains object or null if none can be found - */ - public Element find( T object ) { - Element e = first; - while( e != null ) { - if( e.object == object ) { - return e; - } - e = e.next; - } - return null; - } - - /** - * Returns the first element in the list - * @return first element - */ - public Element getHead() { - return first; - } - - /** - * Returns the last element in the list - * @return last element - */ - public Element getTail() { - return last; - } - - /** - * Add all elements in list into this linked list - * @param list List - */ - public void addAll( List list ) { - if( list.isEmpty() ) - return; - - Element a = requestNew(); - a.object = list.get(0); - - if( first == null ) { - first = a; - } else if( last != null ) { - last.next = a; - a.previous = last; - } - - for (int i = 1; i < list.size(); i++) { - Element b = requestNew(); - b.object = list.get(i); - - a.next = b; - b.previous = a; - a = b; - } - - last = a; - size += list.size(); - } - - /** - * Adds the specified elements from array into this list - * @param array The array - * @param first First element to be added - * @param length The number of elements to be added - */ - public void addAll( T[] array , int first , int length ) { - if( length <= 0 ) - return; - - Element a = requestNew(); - a.object = array[first]; - - if( this.first == null ) { - this.first = a; - } else if( last != null ) { - last.next = a; - a.previous = last; - } - - for (int i = 1; i < length; i++) { - Element b = requestNew(); - b.object = array[first+i]; - - a.next = b; - b.previous = a; - a = b; - } - - last = a; - size += length; - } - - /** - * Returns the number of elements in the list - */ - public int size() { - return size; - } - - /** - * Returns a new element. If there are old elements available those are returned, otherwise a new one is returned. - * - * @return Unused element. - */ - protected Element requestNew () { - if( available.isEmpty() ) { - return new Element<>(); - } else { - return available.pop(); - } - } - - public static class Element - { - public Element next; - public Element previous; - public T object; - - public void clear() { - next = null; - previous = null; - object = null; - } - - public Element getNext() { - return next; - } - - public void setNext(Element next) { - this.next = next; - } - - public Element getPrevious() { - return previous; - } - - public void setPrevious(Element previous) { - this.previous = previous; - } - - public T getObject() { - return object; - } - - public void setObject(T object) { - this.object = object; - } - } -} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/struct/Process.java ddogleg-0.22+ds/src/org/ddogleg/struct/Process.java --- ddogleg-0.18+ds/src/org/ddogleg/struct/Process.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/struct/Process.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. - * - * This file is part of DDogleg (http://ddogleg.org). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ddogleg.struct; - -import java.io.Serializable; - -/** - * Function which processes an object - * - * @author Peter Abeles - */ -public interface Process { - void process(T o); - - /** - * Default implementation which does nothing - */ - class DoNothing implements Process, Serializable { - @Override - public void process(T o) {} - } -} diff -Nru ddogleg-0.18+ds/src/org/ddogleg/struct/RecycleManager.java ddogleg-0.22+ds/src/org/ddogleg/struct/RecycleManager.java --- ddogleg-0.18+ds/src/org/ddogleg/struct/RecycleManager.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/struct/RecycleManager.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2015, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -18,7 +18,7 @@ package org.ddogleg.struct; -import java.util.Stack; +import java.util.ArrayDeque; /** * Simple class which helps minimize declaring new objects by helping you recycle them. @@ -28,22 +28,19 @@ public class RecycleManager { protected Class targetClass; - protected Stack unused = new Stack(); + protected ArrayDeque unused = new ArrayDeque<>(); public RecycleManager(Class targetClass) { this.targetClass = targetClass; } - protected RecycleManager() { - } - /** * Either returns a recycled instance or a new one. */ public T requestInstance() { T a; if( unused.size() > 0 ) { - a = unused.pop(); + a = unused.removeLast(); } else { a = createInstance(); } @@ -63,9 +60,7 @@ protected T createInstance() { try { return targetClass.newInstance(); - } catch (InstantiationException e) { - throw new RuntimeException(e); - } catch (IllegalAccessException e) { + } catch (InstantiationException | IllegalAccessException e) { throw new RuntimeException(e); } } @@ -73,7 +68,7 @@ /** * Returns the stack containing all the unused instances. */ - public Stack getUnused() { + public ArrayDeque getUnused() { return unused; } } diff -Nru ddogleg-0.18+ds/src/org/ddogleg/struct/Tuple2.java ddogleg-0.22+ds/src/org/ddogleg/struct/Tuple2.java --- ddogleg-0.18+ds/src/org/ddogleg/struct/Tuple2.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/struct/Tuple2.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2015, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -18,37 +18,36 @@ package org.ddogleg.struct; +import lombok.Getter; +import lombok.Setter; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; + /** * Simple data structure for passing a pair of data. * * @author Peter Abeles */ public class Tuple2 { - public A data0; - public B data1; - - public Tuple2(A data0, B data1) { - this.data0 = data0; - this.data1 = data1; - } + public @Nullable @Getter @Setter A d0; + public @Nullable @Getter @Setter B d1; - public Tuple2() { + public Tuple2(@Nullable A d0, @Nullable B d1) { + this.d0 = d0; + this.d1 = d1; } - public A getData0() { - return data0; - } - - public void setData0(A data0) { - this.data0 = data0; - } + public Tuple2() {} - public B getData1() { - return data1; + /** Returns d0 but performs a null check first */ + public A getCheckD0() { + return Objects.requireNonNull(d0); } - public void setData1(B data1) { - this.data1 = data1; + /** Returns d1 but performs a null check first */ + public B getCheckD1() { + return Objects.requireNonNull(d1); } } diff -Nru ddogleg-0.18+ds/src/org/ddogleg/struct/Tuple3.java ddogleg-0.22+ds/src/org/ddogleg/struct/Tuple3.java --- ddogleg-0.18+ds/src/org/ddogleg/struct/Tuple3.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/struct/Tuple3.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -18,47 +18,43 @@ package org.ddogleg.struct; +import lombok.Getter; +import lombok.Setter; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; + /** * Simple data structure for passing a triple of data. * * @author Peter Abeles */ public class Tuple3 { - public A d0; - public B d1; - public C d2; + public @Nullable @Getter @Setter A d0; + public @Nullable @Getter @Setter B d1; + public @Nullable @Getter @Setter C d2; - public Tuple3(A d0, B d1, C d2) { + public Tuple3(@Nullable A d0, @Nullable B d1, @Nullable C d2) { this.d0 = d0; this.d1 = d1; this.d2 = d2; } - public Tuple3() { - } - - public A getD0() { - return d0; - } - - public void setD0(A d0) { - this.d0 = d0; - } - - public B getD1() { - return d1; - } + public Tuple3() {} - public void setD1(B d1) { - this.d1 = d1; + /** Returns d0 but performs a null check first */ + public A getCheckD0() { + return Objects.requireNonNull(d0); } - public void setD2(C d2) { - this.d2 = d2; + /** Returns d1 but performs a null check first */ + public B getCheckD1() { + return Objects.requireNonNull(d1); } - public C getD2() { - return d2; + /** Returns d2 but performs a null check first */ + public C getCheckD2() { + return Objects.requireNonNull(d2); } } diff -Nru ddogleg-0.18+ds/src/org/ddogleg/struct/Tuple4.java ddogleg-0.22+ds/src/org/ddogleg/struct/Tuple4.java --- ddogleg-0.18+ds/src/org/ddogleg/struct/Tuple4.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/struct/Tuple4.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -18,57 +18,50 @@ package org.ddogleg.struct; +import lombok.Getter; +import lombok.Setter; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; + /** * Simple data structure for passing a quad of data. * * @author Peter Abeles */ public class Tuple4 { - public A d0; - public B d1; - public C d2; - public D d3; + public @Nullable @Getter @Setter A d0; + public @Nullable @Getter @Setter B d1; + public @Nullable @Getter @Setter C d2; + public @Nullable @Getter @Setter D d3; - public Tuple4(A d0, B d1, C d2, D d3) { + public Tuple4(@Nullable A d0, @Nullable B d1, @Nullable C d2, @Nullable D d3) { this.d0 = d0; this.d1 = d1; this.d2 = d2; this.d3 = d3; } - public Tuple4() { - } - - public A getD0() { - return d0; - } + public Tuple4() {} - public void setD0(A d0) { - this.d0 = d0; + /** Returns d0 but performs a null check first */ + public A getCheckD0() { + return Objects.requireNonNull(d0); } - public B getD1() { - return d1; + /** Returns d1 but performs a null check first */ + public B getCheckD1() { + return Objects.requireNonNull(d1); } - public void setD1(B d1) { - this.d1 = d1; + /** Returns d2 but performs a null check first */ + public C getCheckD2() { + return Objects.requireNonNull(d2); } - public void setD2(C d2) { - this.d2 = d2; - } - - public C getD2() { - return d2; - } - - public D getD3() { - return d3; - } - - public void setD3(D d3) { - this.d3 = d3; + /** Returns d3 but performs a null check first */ + public D getCheckD3() { + return Objects.requireNonNull(d3); } } diff -Nru ddogleg-0.18+ds/src/org/ddogleg/struct/VerbosePrint.java ddogleg-0.22+ds/src/org/ddogleg/struct/VerbosePrint.java --- ddogleg-0.18+ds/src/org/ddogleg/struct/VerbosePrint.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/struct/VerbosePrint.java 2022-09-01 02:18:09.000000000 +0000 @@ -18,7 +18,8 @@ package org.ddogleg.struct; -import javax.annotation.Nullable; +import org.jetbrains.annotations.Nullable; + import java.io.PrintStream; import java.util.Set; diff -Nru ddogleg-0.18+ds/src/org/ddogleg/util/PrimitiveArrays.java ddogleg-0.22+ds/src/org/ddogleg/util/PrimitiveArrays.java --- ddogleg-0.18+ds/src/org/ddogleg/util/PrimitiveArrays.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/src/org/ddogleg/util/PrimitiveArrays.java 2022-09-01 02:18:09.000000000 +0000 @@ -18,6 +18,10 @@ package org.ddogleg.util; +import org.ddogleg.struct.DogArray_I32; +import org.ddogleg.struct.DogArray_I8; +import org.jetbrains.annotations.Nullable; + import java.util.Random; /** @@ -28,100 +32,234 @@ public class PrimitiveArrays { /** + * Finds the itersection of two sets. Uses a algorithm that requires linear time and memory. Manually + * determines the min and max values contained in both sets. + * + * @param setA Set A of integers. Unsorted. + * @param sizeA Number of elements in set A + * @param setB Set B of integers. Unsorted. + * @param sizeB Number of elements in set B + * @param work Work space + * @param results Output set that is the intersection. Sorted from least to greatest + */ + public static void intersection( int[] setA, int sizeA, + int[] setB, int sizeB, + DogArray_I32 results, + @Nullable DogArray_I8 work ) { + // Handling the pathological case where enables safely accessing the first element in setA + if (sizeA == 0 || sizeB == 0) { + results.reset(); + return; + } + + // Set the min/max to an actual element. This enables if else to be used below. + int min = setA[0]; + int max = min; + + // Exhaustively search to find the minimum and maximum values + for (int i = 1; i < sizeA; i++) { + int v = setA[i]; + if (v < min) + min = v; + else if (v > max) + max = v; + } + + for (int i = 0; i < sizeB; i++) { + int v = setB[i]; + if (v < min) + min = v; + else if (v > max) + max = v; + } + intersection(setA, sizeA, setB, sizeB, min, max, results, work); + } + + /** + * Finds the intersection of two sets. Uses a algorithm that requires linear time and memory. + * + * @param setA Set A of integers. Unsorted. + * @param sizeA Number of elements in set A + * @param setB Set B of integers. Unsorted. + * @param sizeB Number of elements in set B + * @param valueMin Minimum value in either set + * @param valueMax Maximum value in either set + * @param work Work space + * @param results Output set that is the intersection. Sorted from least to greatest + */ + public static void intersection( int[] setA, int sizeA, + int[] setB, int sizeB, + int valueMin, int valueMax, + DogArray_I32 results, + @Nullable DogArray_I8 work ) { + work = countOccurrences(setA, sizeA, setB, sizeB, valueMin, valueMax, results, work); + + for (int i = 0; i < work.size; i++) { + if (work.data[i] != 2) + continue; + results.add(i + valueMin); + } + } + + /** + * Finds the intersection of two sets. Uses a algorithm that requires linear time and memory. + * + * @param setA Set A of integers. Unsorted. + * @param sizeA Number of elements in set A + * @param setB Set B of integers. Unsorted. + * @param sizeB Number of elements in set B + * @param valueMin Minimum value in either set + * @param valueMax Maximum value in either set + * @param work Work space + * @param results Output set that is the intersection. Sorted from least to greatest + */ + public static void union( int[] setA, int sizeA, + int[] setB, int sizeB, + int valueMin, int valueMax, + DogArray_I32 results, + @Nullable DogArray_I8 work ) { + work = countOccurrences(setA, sizeA, setB, sizeB, valueMin, valueMax, results, work); + + for (int i = 0; i < work.size; i++) { + if (work.data[i] == 0) + continue; + results.add(i + valueMin); + } + } + + private static DogArray_I8 countOccurrences( int[] setA, int sizeA, + int[] setB, int sizeB, + int valueMin, int valueMax, + DogArray_I32 results, + @Nullable DogArray_I8 work ) { + results.reset(); + results.reserve(Math.min(sizeA, sizeB)); + + if (work == null) + work = new DogArray_I8(valueMax - valueMin + 1); + work.reset(); + work.resize(valueMax - valueMin + 1, (byte)0); + + for (int i = 0; i < sizeA; i++) { + work.data[setA[i] - valueMin]++; + } + for (int i = 0; i < sizeB; i++) { + work.data[setB[i] - valueMin]++; + } + return work; + } + + /** * Sets each element within range to a number counting up */ - public static void fillCounting(int[] array , int offset, int length ) { + public static void fillCounting( int[] array, int offset, int length ) { + sanityCheckRange(array.length, offset, length); + for (int i = 0; i < length; i++) { - array[i+offset] = i; + array[i + offset] = i; } } - public static int[] fillCounting(int length ) { + public static int[] fillCounting( int length ) { int[] array = new int[length]; - fillCounting(array,0,length); + fillCounting(array, 0, length); return array; } /** * Randomly shuffle the array */ - public static void shuffle( byte []array , int offset , int length , Random rand ) { + public static void shuffle( byte[] array, int offset, int length, Random rand ) { + sanityCheckShuffle(array.length, offset, length); + for (int i = 0; i < length; i++) { - int src = rand.nextInt(length-i); - byte tmp = array[offset+src+i]; - array[offset+src+i]=array[offset+i]; - array[offset+i] = tmp; + int src = rand.nextInt(length - i); + byte tmp = array[offset + src + i]; + array[offset + src + i] = array[offset + i]; + array[offset + i] = tmp; } } /** * Randomly shuffle the array */ - public static void shuffle( short []array , int offset , int length , Random rand ) { + public static void shuffle( short[] array, int offset, int length, Random rand ) { + sanityCheckShuffle(array.length, offset, length); + for (int i = 0; i < length; i++) { - int src = rand.nextInt(length-i); - short tmp = array[offset+src+i]; - array[offset+src+i]=array[offset+i]; - array[offset+i] = tmp; + int src = rand.nextInt(length - i); + short tmp = array[offset + src + i]; + array[offset + src + i] = array[offset + i]; + array[offset + i] = tmp; } } /** * Randomly shuffle the array */ - public static void shuffle( int []array , int offset , int length , Random rand ) { + public static void shuffle( int[] array, int offset, int length, Random rand ) { + sanityCheckShuffle(array.length, offset, length); + for (int i = 0; i < length; i++) { - int src = rand.nextInt(length-i); - int tmp = array[offset+src+i]; - array[offset+src+i]=array[offset+i]; - array[offset+i] = tmp; + int src = rand.nextInt(length - i); + int tmp = array[offset + src + i]; + array[offset + src + i] = array[offset + i]; + array[offset + i] = tmp; } } /** * Randomly shuffle the array */ - public static void shuffle( long []array , int offset , int length , Random rand ) { + public static void shuffle( long[] array, int offset, int length, Random rand ) { + sanityCheckShuffle(array.length, offset, length); + for (int i = 0; i < length; i++) { - int src = rand.nextInt(length-i); - long tmp = array[offset+src+i]; - array[offset+src+i]=array[offset+i]; - array[offset+i] = tmp; + int src = rand.nextInt(length - i); + long tmp = array[offset + src + i]; + array[offset + src + i] = array[offset + i]; + array[offset + i] = tmp; } } /** * Randomly shuffle the array */ - public static void shuffle( float []array , int offset , int length , Random rand ) { + public static void shuffle( float[] array, int offset, int length, Random rand ) { + sanityCheckShuffle(array.length, offset, length); + for (int i = 0; i < length; i++) { - int src = rand.nextInt(length-i); - float tmp = array[offset+src+i]; - array[offset+src+i]=array[offset+i]; - array[offset+i] = tmp; + int src = rand.nextInt(length - i); + float tmp = array[offset + src + i]; + array[offset + src + i] = array[offset + i]; + array[offset + i] = tmp; } } /** * Randomly shuffle the array */ - public static void shuffle( double []array , int offset , int length , Random rand ) { + public static void shuffle( double[] array, int offset, int length, Random rand ) { + sanityCheckShuffle(array.length, offset, length); + for (int i = 0; i < length; i++) { - int src = rand.nextInt(length-i); - double tmp = array[offset+src+i]; - array[offset+src+i]=array[offset+i]; - array[offset+i] = tmp; + int src = rand.nextInt(length - i); + double tmp = array[offset + src + i]; + array[offset + src + i] = array[offset + i]; + array[offset + i] = tmp; } } /** * Returns the value of the element with the minimum value */ - public static int min( byte []array , int offset , int length ) { - int min = Integer.MAX_VALUE; - for (int i = 0; i < length; i++) { - int tmp = array[offset+i]; - if( tmp < min ) { + public static int min( byte[] array, int offset, int length ) { + sanityCheckRange(array.length, offset, length); + + byte min = array[offset]; + for (int i = 1; i < length; i++) { + byte tmp = array[offset + i]; + if (tmp < min) { min = tmp; } } @@ -131,11 +269,13 @@ /** * Returns the value of the element with the minimum value */ - public static int min( short []array , int offset , int length ) { - int min = Integer.MAX_VALUE; - for (int i = 0; i < length; i++) { - int tmp = array[offset+i]; - if( tmp < min ) { + public static int min( short[] array, int offset, int length ) { + sanityCheckRange(array.length, offset, length); + + short min = array[offset]; + for (int i = 1; i < length; i++) { + short tmp = array[offset + i]; + if (tmp < min) { min = tmp; } } @@ -145,11 +285,13 @@ /** * Returns the value of the element with the minimum value */ - public static int min( int []array , int offset , int length ) { - int min = Integer.MAX_VALUE; - for (int i = 0; i < length; i++) { - int tmp = array[offset+i]; - if( tmp < min ) { + public static int min( int[] array, int offset, int length ) { + sanityCheckRange(array.length, offset, length); + + int min = array[offset]; + for (int i = 1; i < length; i++) { + int tmp = array[offset + i]; + if (tmp < min) { min = tmp; } } @@ -159,11 +301,13 @@ /** * Returns the value of the element with the minimum value */ - public static long min( long []array , int offset , int length ) { - long min = Long.MAX_VALUE; - for (int i = 0; i < length; i++) { - long tmp = array[offset+i]; - if( tmp < min ) { + public static long min( long[] array, int offset, int length ) { + sanityCheckRange(array.length, offset, length); + + long min = array[offset]; + for (int i = 1; i < length; i++) { + long tmp = array[offset + i]; + if (tmp < min) { min = tmp; } } @@ -173,11 +317,13 @@ /** * Returns the value of the element with the minimum value */ - public static float min( float []array , int offset , int length ) { - float min = Float.MAX_VALUE; - for (int i = 0; i < length; i++) { - float tmp = array[offset+i]; - if( tmp < min ) { + public static float min( float[] array, int offset, int length ) { + sanityCheckRange(array.length, offset, length); + + float min = array[offset]; + for (int i = 1; i < length; i++) { + float tmp = array[offset + i]; + if (tmp < min) { min = tmp; } } @@ -187,11 +333,13 @@ /** * Returns the value of the element with the minimum value */ - public static double min( double []array , int offset , int length ) { - double min = Double.MAX_VALUE; - for (int i = 0; i < length; i++) { - double tmp = array[offset+i]; - if( tmp < min ) { + public static double min( double[] array, int offset, int length ) { + sanityCheckRange(array.length, offset, length); + + double min = array[offset]; + for (int i = 1; i < length; i++) { + double tmp = array[offset + i]; + if (tmp < min) { min = tmp; } } @@ -199,13 +347,87 @@ } /** + * Returns the index of the element with the minimum value + */ + public static int minIdx( byte[] array, int offset, int length ) { + sanityCheckRange(array.length, offset, length); + + byte min = array[offset]; + int index = 0; + for (int i = 1; i < length; i++) { + byte tmp = array[offset + i]; + if (tmp < min) { + min = tmp; + index = i; + } + } + return offset + index; + } + + /** + * Returns the index of the element with the minimum value + */ + public static int minIdx( int[] array, int offset, int length ) { + sanityCheckRange(array.length, offset, length); + + int min = array[offset]; + int index = 0; + for (int i = 1; i < length; i++) { + int tmp = array[offset + i]; + if (tmp < min) { + min = tmp; + index = i; + } + } + return offset + index; + } + + /** + * Returns the index of the element with the minimum value + */ + public static int minIdx( float[] array, int offset, int length ) { + sanityCheckRange(array.length, offset, length); + + float min = array[offset]; + int index = 0; + for (int i = 1; i < length; i++) { + float tmp = array[offset + i]; + if (tmp < min) { + min = tmp; + index = i; + } + } + return offset + index; + } + + /** + * Returns the index of the element with the minimum value + */ + public static int minIdx( double[] array, int offset, int length ) { + sanityCheckRange(array.length, offset, length); + + double min = array[offset]; + int index = 0; + for (int i = 1; i < length; i++) { + double tmp = array[offset + i]; + if (tmp < min) { + min = tmp; + index = i; + } + } + return offset + index; + } + + /** * Returns the value of the element with the maximum value */ - public static int max( byte []array , int offset , int length ) { - int max = -Integer.MAX_VALUE; - for (int i = 0; i < length; i++) { - int tmp = array[offset+i]; - if( tmp > max ) { + public static int max( byte[] array, int offset, int length ) { + sanityCheckRange(array.length, offset, length); + + byte max = array[offset]; + for (int i = 1; i < length; i++) { + byte tmp = array[offset + i]; + if (tmp > max) { max = tmp; } } @@ -215,11 +437,13 @@ /** * Returns the value of the element with the maximum value */ - public static int max( short []array , int offset , int length ) { - int max = -Integer.MAX_VALUE; - for (int i = 0; i < length; i++) { - int tmp = array[offset+i]; - if( tmp > max ) { + public static int max( short[] array, int offset, int length ) { + sanityCheckRange(array.length, offset, length); + + short max = array[offset]; + for (int i = 1; i < length; i++) { + short tmp = array[offset + i]; + if (tmp > max) { max = tmp; } } @@ -229,11 +453,13 @@ /** * Returns the value of the element with the maximum value */ - public static int max( int []array , int offset , int length ) { - int max = -Integer.MAX_VALUE; - for (int i = 0; i < length; i++) { - int tmp = array[offset+i]; - if( tmp > max ) { + public static int max( int[] array, int offset, int length ) { + sanityCheckRange(array.length, offset, length); + + int max = array[offset]; + for (int i = 1; i < length; i++) { + int tmp = array[offset + i]; + if (tmp > max) { max = tmp; } } @@ -243,11 +469,13 @@ /** * Returns the value of the element with the maximum value */ - public static long max( long []array , int offset , int length ) { - long max = -Long.MAX_VALUE; - for (int i = 0; i < length; i++) { - long tmp = array[offset+i]; - if( tmp > max ) { + public static long max( long[] array, int offset, int length ) { + sanityCheckRange(array.length, offset, length); + + long max = array[offset]; + for (int i = 1; i < length; i++) { + long tmp = array[offset + i]; + if (tmp > max) { max = tmp; } } @@ -257,11 +485,13 @@ /** * Returns the value of the element with the maximum value */ - public static float max( float []array , int offset , int length ) { - float max = -Float.MAX_VALUE; - for (int i = 0; i < length; i++) { - float tmp = array[offset+i]; - if( tmp > max ) { + public static float max( float[] array, int offset, int length ) { + sanityCheckRange(array.length, offset, length); + + float max = array[offset]; + for (int i = 1; i < length; i++) { + float tmp = array[offset + i]; + if (tmp > max) { max = tmp; } } @@ -271,11 +501,13 @@ /** * Returns the value of the element with the maximum value */ - public static double max( double []array , int offset , int length ) { - double max = -Double.MAX_VALUE; - for (int i = 0; i < length; i++) { - double tmp = array[offset+i]; - if( tmp > max ) { + public static double max( double[] array, int offset, int length ) { + sanityCheckRange(array.length, offset, length); + + double max = array[offset]; + for (int i = 1; i < length; i++) { + double tmp = array[offset + i]; + if (tmp > max) { max = tmp; } } @@ -283,21 +515,96 @@ } /** + * Returns the value of the element with the maximum value + */ + public static int maxIdx( byte[] array, int offset, int length ) { + sanityCheckRange(array.length, offset, length); + + byte max = array[offset]; + int index = 0; + for (int i = 1; i < length; i++) { + byte tmp = array[offset + i]; + if (tmp > max) { + max = tmp; + index = i; + } + } + return offset + index; + } + + /** + * Returns the value of the element with the maximum value + */ + public static int maxIdx( int[] array, int offset, int length ) { + sanityCheckRange(array.length, offset, length); + + int max = array[offset]; + int index = 0; + for (int i = 1; i < length; i++) { + int tmp = array[offset + i]; + if (tmp > max) { + max = tmp; + index = i; + } + } + return offset + index; + } + + /** + * Returns the value of the element with the maximum value + */ + public static int maxIdx( float[] array, int offset, int length ) { + sanityCheckRange(array.length, offset, length); + + float max = array[offset]; + int index = 0; + for (int i = 1; i < length; i++) { + float tmp = array[offset + i]; + if (tmp > max) { + max = tmp; + index = i; + } + } + return offset + index; + } + + /** + * Returns the value of the element with the maximum value + */ + public static int maxIdx( double[] array, int offset, int length ) { + sanityCheckRange(array.length, offset, length); + + double max = array[offset]; + int index = 0; + for (int i = 1; i < length; i++) { + double tmp = array[offset + i]; + if (tmp > max) { + max = tmp; + index = i; + } + } + return offset + index; + } + + /** * Finds the first index in 'array' for which val is not ≤ val + * * @param offset First index in the array * @param length Number of elements in the array * @param val The value for which the lower bound is being searched for. * @return lower bound index */ - public static int lowerBound( byte[] array, int offset , int length , int val ) { + public static int lowerBound( byte[] array, int offset, int length, int val ) { + sanityCheckRange(array.length, offset, length); + int count = length; int first = offset; - while( count > 0 ) { + while (count > 0) { int step = count/2; - int idx = first+step; - if( array[idx] < val ) { - first = idx+1; - count -= step+1; + int idx = first + step; + if (array[idx] < val) { + first = idx + 1; + count -= step + 1; } else { count = step; } @@ -307,21 +614,24 @@ /** * Finds the first index in 'array' for which val is not ≤ val + * * @param array unsigned byte array * @param offset First index in the array * @param length Number of elements in the array * @param val The value for which the lower bound is being searched for. * @return lower bound index */ - public static int lowerBoundU( byte[] array, int offset , int length , int val ) { + public static int lowerBoundU( byte[] array, int offset, int length, int val ) { + sanityCheckRange(array.length, offset, length); + int count = length; int first = offset; - while( count > 0 ) { + while (count > 0) { int step = count/2; - int idx = first+step; - if( (array[idx]&0xFF) < val ) { - first = idx+1; - count -= step+1; + int idx = first + step; + if ((array[idx] & 0xFF) < val) { + first = idx + 1; + count -= step + 1; } else { count = step; } @@ -331,20 +641,23 @@ /** * Finds the first index in 'array' for which val is not ≤ val + * * @param offset First index in the array * @param length Number of elements in the array * @param val The value for which the lower bound is being searched for. * @return lower bound index */ - public static int lowerBound( short[] array, int offset , int length , int val ) { + public static int lowerBound( short[] array, int offset, int length, int val ) { + sanityCheckRange(array.length, offset, length); + int count = length; int first = offset; - while( count > 0 ) { + while (count > 0) { int step = count/2; - int idx = first+step; - if( array[idx] < val ) { - first = idx+1; - count -= step+1; + int idx = first + step; + if (array[idx] < val) { + first = idx + 1; + count -= step + 1; } else { count = step; } @@ -354,20 +667,23 @@ /** * Finds the first index in 'array' for which val is not ≤ val + * * @param offset First index in the array * @param length Number of elements in the array * @param val The value for which the lower bound is being searched for. * @return lower bound index */ - public static int lowerBound( int[] array, int offset , int length , int val ) { + public static int lowerBound( int[] array, int offset, int length, int val ) { + sanityCheckRange(array.length, offset, length); + int count = length; int first = offset; - while( count > 0 ) { + while (count > 0) { int step = count/2; - int idx = first+step; - if( array[idx] < val ) { - first = idx+1; - count -= step+1; + int idx = first + step; + if (array[idx] < val) { + first = idx + 1; + count -= step + 1; } else { count = step; } @@ -377,20 +693,23 @@ /** * Finds the first index in 'array' for which val is not ≤ val + * * @param offset First index in the array * @param length Number of elements in the array * @param val The value for which the lower bound is being searched for. * @return lower bound index */ - public static int lowerBound( float[] array, int offset , int length , float val ) { + public static int lowerBound( float[] array, int offset, int length, float val ) { + sanityCheckRange(array.length, offset, length); + int count = length; int first = offset; - while( count > 0 ) { + while (count > 0) { int step = count/2; - int idx = first+step; - if( array[idx] < val ) { - first = idx+1; - count -= step+1; + int idx = first + step; + if (array[idx] < val) { + first = idx + 1; + count -= step + 1; } else { count = step; } @@ -400,24 +719,209 @@ /** * Finds the first index in 'array' for which val is not ≤ val + * * @param offset First index in the array * @param length Number of elements in the array * @param val The value for which the lower bound is being searched for. * @return lower bound index */ - public static int lowerBound( double[] array, int offset , int length , double val ) { + public static int lowerBound( double[] array, int offset, int length, double val ) { + sanityCheckRange(array.length, offset, length); + int count = length; int first = offset; - while( count > 0 ) { + while (count > 0) { int step = count/2; - int idx = first+step; - if( array[idx] < val ) { - first = idx+1; - count -= step+1; + int idx = first + step; + if (array[idx] < val) { + first = idx + 1; + count -= step + 1; } else { count = step; } } return first; } + + /** + * Computes the sum of the array and stores the result in a double + */ + public static double sumD( byte[] array, int offset, int length ) { + sanityCheckRange(array.length, offset, length); + + double sum = 0.0; + for (int i = 0; i < length; i++) { + sum += array[offset + i]; + } + return sum; + } + + /** + * Computes the sum of the array and stores the result in a double + */ + public static double sumD( short[] array, int offset, int length ) { + sanityCheckRange(array.length, offset, length); + + double sum = 0.0; + for (int i = 0; i < length; i++) { + sum += array[offset + i]; + } + return sum; + } + + /** + * Computes the sum of the array and stores the result in a double + */ + public static double sumD( int[] array, int offset, int length ) { + sanityCheckRange(array.length, offset, length); + + double sum = 0.0; + for (int i = 0; i < length; i++) { + sum += array[offset + i]; + } + return sum; + } + + /** + * Computes the sum of the array and stores the result in a double + */ + public static double sumD( long[] array, int offset, int length ) { + sanityCheckRange(array.length, offset, length); + + double sum = 0.0; + for (int i = 0; i < length; i++) { + sum += array[offset + i]; + } + return sum; + } + + /** + * Computes the sum of the array and stores the result in a double + */ + public static double sumD( float[] array, int offset, int length ) { + sanityCheckRange(array.length, offset, length); + + double sum = 0.0; + for (int i = 0; i < length; i++) { + sum += array[offset + i]; + } + return sum; + } + + /** + * Computes the sum of the array and stores the result in a double + */ + public static double sumD( double[] array, int offset, int length ) { + sanityCheckRange(array.length, offset, length); + + double sum = 0.0; + for (int i = 0; i < length; i++) { + sum += array[offset + i]; + } + return sum; + } + + /** + * Recursively computes a result from an array. Previous results are feedback into the current value being + * considered. + */ + public static double feedbackIdxDOp( byte[] array, int offset, int length, FeedbackIdxD op ) { + sanityCheckRange(array.length, offset, length); + + double result = 0.0; + for (int i = 0; i < length; i++) { + result = op.process(i, array[i + offset], result); + } + return result; + } + + /** + * Recursively computes a result from an array. Previous results are feedback into the current value being + * considered. + */ + public static double feedbackIdxDOp( short[] array, int offset, int length, FeedbackIdxD op ) { + sanityCheckRange(array.length, offset, length); + + double result = 0.0; + for (int i = 0; i < length; i++) { + result = op.process(i, array[i + offset], result); + } + return result; + } + + /** + * Recursively computes a result from an array. Previous results are feedback into the current value being + * considered. + */ + public static double feedbackIdxDOp( int[] array, int offset, int length, FeedbackIdxD op ) { + sanityCheckRange(array.length, offset, length); + + double result = 0.0; + for (int i = 0; i < length; i++) { + result = op.process(i, array[i + offset], result); + } + return result; + } + + /** + * Recursively computes a result from an array. Previous results are feedback into the current value being + * considered. + */ + public static double feedbackIdxDOp( long[] array, int offset, int length, FeedbackIdxD op ) { + sanityCheckRange(array.length, offset, length); + + double result = 0.0; + for (int i = 0; i < length; i++) { + result = op.process(i, array[i + offset], result); + } + return result; + } + + /** + * Recursively computes a result from an array. Previous results are feedback into the current value being + * considered. + */ + public static double feedbackIdxDOp( float[] array, int offset, int length, FeedbackIdxD op ) { + sanityCheckRange(array.length, offset, length); + + double result = 0.0; + for (int i = 0; i < length; i++) { + result = op.process(i, array[i + offset], result); + } + return result; + } + + /** + * Recursively computes a result from an array. Previous results are feedback into the current value being + * considered. + */ + public static double feedbackIdxDOp( double[] array, int offset, int length, FeedbackIdxD op ) { + sanityCheckRange(array.length, offset, length); + + double result = 0.0; + for (int i = 0; i < length; i++) { + result = op.process(i, array[i + offset], result); + } + return result; + } + + private static void sanityCheckRange( int arrayLength, int offset, int length ) { + if (length <= 0) + throw new IllegalArgumentException("length must be positive. length=" + length); + if (offset < 0 || offset >= arrayLength) + throw new IllegalArgumentException("offset is invalid. offset=" + offset); + } + + + private static void sanityCheckShuffle( int arrayLength, int offset, int length ) { + if (length < 0) + throw new IllegalArgumentException("length must not be negative. length=" + length); + if (offset < 0 || offset > arrayLength) + throw new IllegalArgumentException("offset is invalid. offset=" + offset); + } + + @FunctionalInterface + public interface FeedbackIdxD { + double process( int idx, double value, double previous ); + } } diff -Nru ddogleg-0.18+ds/test/org/ddogleg/clustering/GenericClusterChecks_F64.java ddogleg-0.22+ds/test/org/ddogleg/clustering/GenericClusterChecks_F64.java --- ddogleg-0.18+ds/test/org/ddogleg/clustering/GenericClusterChecks_F64.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/clustering/GenericClusterChecks_F64.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -18,11 +18,13 @@ package org.ddogleg.clustering; -import org.ddogleg.clustering.kmeans.TestStandardKMeans_F64; +import org.ddogleg.clustering.kmeans.TestStandardKMeans; +import org.ddogleg.clustering.misc.ListAccessor; import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.List; +import java.util.Random; import static org.junit.jupiter.api.Assertions.*; @@ -30,17 +32,26 @@ * @author Peter Abeles */ public abstract class GenericClusterChecks_F64 { + + protected Random rand = new Random(234); + /** * If hint is true then the first 3 elements are good initial seeds for clustering */ - public abstract ComputeClusters createClustersAlg(boolean seedHint); + public abstract ComputeClusters createClustersAlg(boolean seedHint, int dof); + + /** + * Selects the best cluster using internal model. Inspired by a bug + */ + protected abstract int selectBestCluster( ComputeClusters alg, double[] p ); /** * Very simple and obvious clustering problem */ - @Test - public void simpleCluster() { - List points = new ArrayList(); + @Test void simpleCluster() { + List points = new ArrayList<>(); + ListAccessor accessor = new ListAccessor<>(points, + (src,dst)->System.arraycopy(src,0,dst,0,1), double[].class); for (int i = 0; i < 20; i++) { points.add( new double[]{ i}); @@ -48,18 +59,18 @@ points.add( new double[]{200+i}); } - ComputeClusters alg = createClustersAlg(true); + ComputeClusters alg = createClustersAlg(true,1); - alg.init(1,243234); + alg.initialize(243234); - alg.process(points,3); + alg.process(accessor,3); - AssignCluster ass = alg.getAssignment(); + AssignCluster assignments = alg.getAssignment(); // test assignment - int cluster0 = ass.assign(points.get(0)); - int cluster1 = ass.assign(points.get(1)); - int cluster2 = ass.assign(points.get(2)); + int cluster0 = assignments.assign(points.get(0)); + int cluster1 = assignments.assign(points.get(1)); + int cluster2 = assignments.assign(points.get(2)); // make sure the clusters are unique assertTrue(cluster0!=cluster1); @@ -69,25 +80,26 @@ // see if it correctly assigns the inputs int index = 0; for (int i = 0; i < 20; i++) { - assertEquals(cluster0,ass.assign(points.get(index++)),1e-8); - assertEquals(cluster1,ass.assign(points.get(index++)),1e-8); - assertEquals(cluster2,ass.assign(points.get(index++)),1e-8); + assertEquals(cluster0,assignments.assign(points.get(index++)),1e-8); + assertEquals(cluster1,assignments.assign(points.get(index++)),1e-8); + assertEquals(cluster2,assignments.assign(points.get(index++)),1e-8); } } - @Test - public void computeDistance() { + @Test void computeDistance() { int DOF = 5; - List points = TestStandardKMeans_F64.createPoints(DOF,200,true); + List points = TestStandardKMeans.createPoints(DOF,200,true); + ListAccessor accessor = new ListAccessor<>(points, + (src,dst)->System.arraycopy(src,0,dst,0,src.length), double[].class); - ComputeClusters alg = createClustersAlg(false); + ComputeClusters alg = createClustersAlg(false,5); - alg.init(DOF,243234); + alg.initialize(243234); - alg.process(points,3); + alg.process(accessor,3); double first = alg.getDistanceMeasure(); - alg.process(points,10); + alg.process(accessor,10); double second = alg.getDistanceMeasure(); // it's actually difficult to come up with meaningful tests for distance which don't make @@ -98,4 +110,33 @@ assertFalse(Double.isInfinite(first)); assertFalse(Double.isInfinite(second)); } + + /** + * Make sure the assigner matches the best assignment. + */ + @Test void consistentAssignments() { + ComputeClusters alg = createClustersAlg(false,1); + + for (int trial = 0; trial < 5; trial++) { + List points = new ArrayList<>(); + ListAccessor accessor = new ListAccessor<>(points, + (src,dst)->System.arraycopy(src,0,dst,0,1), double[].class); + + for (int i = 0; i < 100; i++) { + points.add( new double[]{rand.nextDouble()}); + } + + alg.initialize(243234); + + alg.process(accessor, 7); + + AssignCluster assigner = alg.getAssignment(); + + for (int pointIdx = 0; pointIdx < points.size(); pointIdx++) { + double[] p = points.get(pointIdx); + assertEquals(selectBestCluster(alg,p), assigner.assign(p)); + } + } + + } } diff -Nru ddogleg-0.18+ds/test/org/ddogleg/clustering/gmm/TestAssignGmm_F64.java ddogleg-0.22+ds/test/org/ddogleg/clustering/gmm/TestAssignGmm_F64.java --- ddogleg-0.18+ds/test/org/ddogleg/clustering/gmm/TestAssignGmm_F64.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/clustering/gmm/TestAssignGmm_F64.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -18,7 +18,6 @@ package org.ddogleg.clustering.gmm; -import org.ejml.dense.row.MatrixFeatures_DDRM; import org.junit.jupiter.api.Test; import java.util.ArrayList; @@ -27,13 +26,9 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -/** - * @author Peter Abeles - */ public class TestAssignGmm_F64 { - @Test - public void assign() { - List clusters = new ArrayList(); + @Test void assign() { + List clusters = new ArrayList<>(); clusters.add( createGaussian(2,1)); clusters.add( createGaussian(10,2) ); @@ -44,9 +39,8 @@ assertEquals(1, alg.assign(new double[]{9})); } - @Test - public void assign_soft() { - List clusters = new ArrayList(); + @Test void assign_soft() { + List clusters = new ArrayList<>(); clusters.add(createGaussian(2, 1)); clusters.add(createGaussian(4, 1)); @@ -62,29 +56,6 @@ assertTrue(fit[0] < fit[1]); } - @Test - public void copy() { - List clusters = new ArrayList(); - - clusters.add(createGaussian(2, 1)); - clusters.add(createGaussian(10, 2)); - - AssignGmm_F64 original = new AssignGmm_F64(clusters); - AssignGmm_F64 copy = (AssignGmm_F64)original.copy(); - - assertEquals(original.getNumberOfClusters(),copy.getNumberOfClusters()); - - for (int i = 0; i < original.getNumberOfClusters(); i++) { - GaussianGmm_F64 o = original.mixture.get(i); - GaussianGmm_F64 c = copy.mixture.get(i); - - assertTrue(o!=c); - assertTrue(MatrixFeatures_DDRM.isIdentical(o.mean,c.mean,1e-8)); - assertTrue(MatrixFeatures_DDRM.isIdentical(o.covariance,c.covariance,1e-8)); - assertTrue(o.weight == c.weight); - } - } - public static GaussianGmm_F64 createGaussian( double mean , double var ) { GaussianGmm_F64 ret = new GaussianGmm_F64(1); diff -Nru ddogleg-0.18+ds/test/org/ddogleg/clustering/gmm/TestExpectationMaximizationGmm_F64.java ddogleg-0.22+ds/test/org/ddogleg/clustering/gmm/TestExpectationMaximizationGmm_F64.java --- ddogleg-0.18+ds/test/org/ddogleg/clustering/gmm/TestExpectationMaximizationGmm_F64.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/clustering/gmm/TestExpectationMaximizationGmm_F64.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -19,11 +19,12 @@ package org.ddogleg.clustering.gmm; import org.ddogleg.clustering.ComputeClusters; +import org.ddogleg.clustering.ConfigKMeans; +import org.ddogleg.clustering.FactoryClustering; import org.ddogleg.clustering.GenericClusterChecks_F64; import org.ddogleg.clustering.gmm.ExpectationMaximizationGmm_F64.PointInfo; -import org.ddogleg.clustering.kmeans.InitializeStandard_F64; -import org.ddogleg.clustering.kmeans.StandardKMeans_F64; -import org.ddogleg.clustering.kmeans.TestStandardKMeans_F64; +import org.ddogleg.clustering.kmeans.StandardKMeans; +import org.ddogleg.clustering.kmeans.TestStandardKMeans; import org.ejml.data.DMatrixRMaj; import org.ejml.dense.row.CommonOps_DDRM; import org.ejml.dense.row.MatrixFeatures_DDRM; @@ -43,22 +44,16 @@ public class TestExpectationMaximizationGmm_F64 extends GenericClusterChecks_F64 { Random rand = new Random(234); - - StandardKMeans_F64 kmeans = new StandardKMeans_F64(1000,1000,1e-8,new TestStandardKMeans_F64.FixedSeeds());; - SeedFromKMeans_F64 seeds = new SeedFromKMeans_F64(kmeans); - + /** * Computes the expectation for several points and a variable number of Gaussians. Sees if points which * are at the mean of the Gaussians have a peak at the expected location */ - @Test - public void expectation() { - + @Test void expectation() { int DOF = 3; - ExpectationMaximizationGmm_F64 alg = new ExpectationMaximizationGmm_F64(100,1e-8,seeds); - - alg.init(DOF,34535); + var alg = new ExpectationMaximizationGmm_F64(100,1e-8,DOF,createSeeds(DOF)); + alg.initialize(34535); // randomly create a few points for (int i = 0; i < 20; i++) { @@ -106,12 +101,11 @@ } } - @Test - public void maximization() { + @Test void maximization() { int DOF = 2; - ExpectationMaximizationGmm_F64 alg = new ExpectationMaximizationGmm_F64(100,1e-8,seeds); - alg.init(DOF,34535); + ExpectationMaximizationGmm_F64 alg = new ExpectationMaximizationGmm_F64(100,1e-8,DOF,createSeeds(DOF)); + alg.initialize(34535); GaussianGmm_F64 a = alg.mixture.grow(); a.setMean(new double[]{1,0.5}); @@ -172,7 +166,6 @@ } private GaussianGmm_F64 computeGaussian( int which , List points ) { - int N = points.get(0).point.length; GaussianGmm_F64 out = new GaussianGmm_F64(N); @@ -205,15 +198,36 @@ } @Override - public ComputeClusters createClustersAlg( boolean hint ) { - + public ComputeClusters createClustersAlg( boolean hint, int dof ) { if( hint ) { - return new ExpectationMaximizationGmm_F64(1000, 1e-8, seeds); + return new ExpectationMaximizationGmm_F64(1000, 1e-8, dof, createSeeds(dof)); } else { - InitializeStandard_F64 kseeds = new InitializeStandard_F64(); - StandardKMeans_F64 kmeans = new StandardKMeans_F64(1000,1000,1e-8,kseeds); - SeedFromKMeans_F64 seeds = new SeedFromKMeans_F64(kmeans); - return new ExpectationMaximizationGmm_F64(1000, 1e-8, seeds); + return FactoryClustering.gaussianMixtureModelEM_F64(1000,1000,1e-8,dof); } } + + @Override protected int selectBestCluster( ComputeClusters alg, double[] p ) { + ExpectationMaximizationGmm_F64 kmeans = (ExpectationMaximizationGmm_F64)alg; + double bestDistance = 0; + int best = -1; + for (int i = 0; i < kmeans.mixture.size; i++) { + double d = kmeans.likelihoodManager.getLikelihood(i).likelihood(p); + if (d > bestDistance) { + bestDistance = d; + best = i; + } + } + return best; + } + + private SeedFromKMeans_F64 createSeeds(int DOF) { + ConfigKMeans config = new ConfigKMeans(); + config.convergeTol = 1e-8; + config.maxIterations = 1000; + config.reseedAfterIterations = 1000; + + StandardKMeans kmeans = FactoryClustering.kMeans(config,DOF, double[].class); + kmeans.seedSelector = new TestStandardKMeans.FixedSeeds(); + return new SeedFromKMeans_F64(kmeans); + } } \ No newline at end of file diff -Nru ddogleg-0.18+ds/test/org/ddogleg/clustering/gmm/TestGaussianGmm_F64.java ddogleg-0.22+ds/test/org/ddogleg/clustering/gmm/TestGaussianGmm_F64.java --- ddogleg-0.18+ds/test/org/ddogleg/clustering/gmm/TestGaussianGmm_F64.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/clustering/gmm/TestGaussianGmm_F64.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -37,9 +37,8 @@ Random rand = new Random(234); - @Test - public void zero() { - GaussianGmm_F64 g = new GaussianGmm_F64(3); + @Test void zero() { + var g = new GaussianGmm_F64(3); CommonOps_DDRM.fill(g.mean,1); CommonOps_DDRM.fill(g.covariance,2); @@ -47,14 +46,13 @@ g.zero(); - assertTrue(CommonOps_DDRM.elementSumAbs(g.mean)==0); - assertTrue(CommonOps_DDRM.elementSumAbs(g.covariance)==0); - assertTrue(g.weight == 0); + assertEquals(CommonOps_DDRM.elementSumAbs(g.mean), 0); + assertEquals(CommonOps_DDRM.elementSumAbs(g.covariance), 0); + assertEquals(g.weight, 0); } - @Test - public void addMean() { - GaussianGmm_F64 g = new GaussianGmm_F64(3); + @Test void addMean() { + var g = new GaussianGmm_F64(3); g.addMean(new double[]{2,3,-1},0.7); g.addMean(new double[]{4,1,0.5},1.2); @@ -65,13 +63,11 @@ assertEquals(0.7 + 1.2, g.weight, 1e-8); } - @Test - public void addCovariance() { - - GaussianGmm_F64 g = new GaussianGmm_F64(3); + @Test void addCovariance() { + var g = new GaussianGmm_F64(3); g.setMean(new double[]{4,3,6}); - Equation eq = new Equation(); + var eq = new Equation(); eq.process("Q = zeros(3,3)"); for (int i = 0; i < 5; i++) { DMatrixRMaj x = RandomMatrices_DDRM.rectangle(3,1,rand); @@ -85,9 +81,8 @@ assertTrue(MatrixFeatures_DDRM.isIdentical(Q, g.covariance, 1e-8)); } - @Test - public void setMean() { - GaussianGmm_F64 g = new GaussianGmm_F64(3); + @Test void setMean() { + var g = new GaussianGmm_F64(3); g.setMean(new double[]{1,2,3}); diff -Nru ddogleg-0.18+ds/test/org/ddogleg/clustering/gmm/TestGaussianLikelihoodManager.java ddogleg-0.22+ds/test/org/ddogleg/clustering/gmm/TestGaussianLikelihoodManager.java --- ddogleg-0.18+ds/test/org/ddogleg/clustering/gmm/TestGaussianLikelihoodManager.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/clustering/gmm/TestGaussianLikelihoodManager.java 2022-09-01 02:18:09.000000000 +0000 @@ -29,10 +29,7 @@ * @author Peter Abeles */ public class TestGaussianLikelihoodManager { - - @Test - public void likelihood() { - + @Test void likelihood() { int DOF = 3; GaussianGmm_F64 a = new GaussianGmm_F64(DOF); @@ -49,7 +46,6 @@ b.covariance.set(1,1,30); b.covariance.set(2,2,25); - FastArray mixtures = new FastArray<>(GaussianGmm_F64.class); mixtures.add(a); mixtures.add(b); @@ -76,7 +72,6 @@ assertEquals(computeChiSq(a,p),chiSqA,1e-8); assertEquals(computeChiSq(b,p),chiSqB,1e-8); - // look at the ratio of the two // the found will be off by a scale factor from the actual likelihood double found = foundA/foundB; diff -Nru ddogleg-0.18+ds/test/org/ddogleg/clustering/gmm/TestSeedFromKMeans_F64.java ddogleg-0.22+ds/test/org/ddogleg/clustering/gmm/TestSeedFromKMeans_F64.java --- ddogleg-0.18+ds/test/org/ddogleg/clustering/gmm/TestSeedFromKMeans_F64.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/clustering/gmm/TestSeedFromKMeans_F64.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -18,8 +18,10 @@ package org.ddogleg.clustering.gmm; -import org.ddogleg.clustering.kmeans.InitializeKMeans_F64; -import org.ddogleg.clustering.kmeans.StandardKMeans_F64; +import org.ddogleg.clustering.FactoryClustering; +import org.ddogleg.clustering.kmeans.StandardKMeans; +import org.ddogleg.clustering.kmeans.TestStandardKMeans; +import org.ddogleg.clustering.misc.ListAccessor; import org.ejml.data.DMatrixRMaj; import org.ejml.dense.row.CommonOps_DDRM; import org.ejml.dense.row.MatrixFeatures_DDRM; @@ -42,7 +44,7 @@ @Test public void selectSeeds() { - + int dof = 2; // first 2 points will act as the initial seed for K-Means // rest will be in a known Gaussian distribution double sigmaX = 1; @@ -65,14 +67,16 @@ } // recompute original distributions - SeedFromKMeans_F64 alg = new SeedFromKMeans_F64(createKMeans()); + SeedFromKMeans_F64 alg = new SeedFromKMeans_F64(createKMeans(dof)); alg.init(2,234234); List seeds = new ArrayList(); seeds.add( new GaussianGmm_F64(2)); seeds.add( new GaussianGmm_F64(2)); + ListAccessor accessor = new ListAccessor<>(points, + (src, dst) -> System.arraycopy(src, 0, dst, 0, dof), double[].class); - alg.selectSeeds(points,seeds); + alg.selectSeeds(accessor,seeds); GaussianGmm_F64 a = seeds.get(0); GaussianGmm_F64 b = seeds.get(1); @@ -115,21 +119,13 @@ return out; } - private StandardKMeans_F64 createKMeans() { - return new StandardKMeans_F64(200,200,1e-6,new FixedSeeds()); + private StandardKMeans createKMeans(int dof) { + var alg = FactoryClustering.kMeans(null,dof, double[].class); + alg.maxIterations = 200; + alg.reseedAfterIterations = 200; + alg.convergeTol = 1e-6; + alg.seedSelector = new TestStandardKMeans.FixedSeeds(); + return alg; } - public static class FixedSeeds implements InitializeKMeans_F64 { - - @Override - public void init(int pointDimension, long randomSeed) {} - - @Override - public void selectSeeds(List points, List seeds) { - int N = seeds.get(0).length; - for (int i = 0; i < 2; i++) { - System.arraycopy(points.get(i), 0, seeds.get(i), 0, N); - } - } - } } \ No newline at end of file diff -Nru ddogleg-0.18+ds/test/org/ddogleg/clustering/kmeans/StandardInitializeKMeansChecks.java ddogleg-0.22+ds/test/org/ddogleg/clustering/kmeans/StandardInitializeKMeansChecks.java --- ddogleg-0.18+ds/test/org/ddogleg/clustering/kmeans/StandardInitializeKMeansChecks.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/clustering/kmeans/StandardInitializeKMeansChecks.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -18,170 +18,199 @@ package org.ddogleg.clustering.kmeans; +import org.ddogleg.clustering.misc.EuclideanSqArrayF64; +import org.ddogleg.clustering.misc.ListAccessor; +import org.ddogleg.struct.DogArray; import org.junit.jupiter.api.Test; -import java.util.Arrays; import java.util.List; +import java.util.Random; import static org.junit.jupiter.api.Assertions.*; -/** - * @author Peter Abeles - */ public abstract class StandardInitializeKMeansChecks { + protected Random rand = new Random(234); + + public abstract InitializeKMeans createAlg( int dof ); + + /** + * Zero seeds have been requested. It should reset the output and return nothing + */ + @Test void zeroSeeds() { + int DOF = 20; + var seeds = new DogArray<>(() -> new double[DOF]); + List points = TestStandardKMeans.createPoints(DOF, 30, true); + + // make the input not zero to ensure it has been reset + seeds.grow(); + performClustering(DOF, 0, points, seeds); + assertEquals(0, seeds.size); + } + + /** + * No points have been passed in. It shouldn't select any seeds in this situation. + */ + @Test void zeroPoints() { + int DOF = 20; + var seeds = new DogArray<>(() -> new double[DOF]); + List points = TestStandardKMeans.createPoints(DOF, 0, true); + + // make the input not zero to ensure it has been reset + seeds.grow(); + performClustering(DOF, 5, points, seeds); + assertEquals(0, seeds.size); + } + + /** + * The number of seeds is greater than the number of points. It should at most select one seed for each point + */ + @Test void fewerPointsThanSeeds() { + int DOF = 20; + var seeds = new DogArray<>(() -> new double[DOF]); + List points = TestStandardKMeans.createPoints(DOF, 5, true); - public abstract InitializeKMeans_F64 createAlg(); + performClustering(DOF, 10, points, seeds); + assertEquals(5, seeds.size); + + // The points are all unique so each seed should match a point + for (double[] p : points) { + assertNotEquals(-1,seeds.findIdx(( a ) -> { + for (int i = 0; i < DOF; i++) { + if (a[i] != p[i]) + return false; + } + return true; + })); + } + } /** - * In this situation there are not enough unique points which can act as unique seeds + * In this situation there are not enough unique points which can act as unique seeds. Generic test + * to see if it meets the abstract classes contract. A specific implementation can be more strict. */ - @Test - public void notEnoughUniquePoints() { + @Test void notEnoughUniquePoints_generic() { int DOF = 20; + int NUM_SEEDS = 20; - List points = TestStandardKMeans_F64.createPoints(DOF,30,true); + List points = TestStandardKMeans.createPoints(DOF, 30, true); for (int i = 1; i < points.size(); i += 2) { - System.arraycopy(points.get(i-1),0,points.get(i),0,DOF); + System.arraycopy(points.get(i - 1), 0, points.get(i), 0, DOF); } - List seeds = TestStandardKMeans_F64.createPoints(DOF,20,false); - InitializeKMeans_F64 alg = createAlg(); - alg.init(DOF,0xBEEF); + var seeds = new DogArray<>(() -> new double[DOF]); - alg.selectSeeds(points, seeds); + performClustering(DOF, NUM_SEEDS, points, seeds); // just make sure it found a match in the input set - for( double[] a : seeds ) { - findMatch( a , points ); + for (double[] a : seeds.toList()) { + findMatch(a, points); } } - public static int findMatch( double[] a , List list ) { + public static int findMatch( double[] a, List list ) { for (int i = 0; i < list.size(); i++) { double[] b = list.get(i); boolean match = true; for (int j = 0; j < a.length; j++) { - if( a[j] != b[j]) { + if (a[j] != b[j]) { match = false; break; } } - if( match ) + if (match) return i; } throw new RuntimeException("Egads. bug?"); } - @Test - public void selectSeeds() { + @Test void selectSeeds() { int DOF = 20; + int NUM_SEEDS = 20; - InitializeKMeans_F64 alg = createAlg(); - - alg.init(DOF,0xBEEF); - - List points = TestStandardKMeans_F64.createPoints(DOF,100,true); - List seeds = TestStandardKMeans_F64.createPoints(DOF,20,false); - + List points = TestStandardKMeans.createPoints(DOF, 100, true); + var seeds = new DogArray<>(() -> new double[DOF]); - alg.selectSeeds(points,seeds); + performClustering(DOF, NUM_SEEDS, points, seeds); // make sure nothing was added to the list - assertEquals(20,seeds.size()); + assertEquals(20, seeds.size()); for (int i = 0; i < seeds.size(); i++) { double[] s = seeds.get(i); // make sure the seed was written to for (int j = 0; j < DOF; j++) { - assertTrue(s[j]!=0); + assertTrue(s[j] != 0); } // make sure it wasn't swapped with one of the points for (int j = 0; j < points.size(); j++) { - assertTrue(points.get(j) != s); + assertNotSame(points.get(j), s); } } } /** - * Request more seeds than there are points. This is impossible to do and ensure the seeds are - * unique. - */ - @Test - public void impossible() { - try { - int DOF = 20; - - InitializeKMeans_F64 alg = createAlg(); - - alg.init(DOF,0xBEEF); - - // 4 points and 4 seeds. Each point must be a seed - List points = TestStandardKMeans_F64.createPoints(DOF,3,true); - List seeds = TestStandardKMeans_F64.createPoints(DOF,4,false); - - alg.selectSeeds(points,seeds); - - fail("Should have thrown an exception!"); - } catch( Exception e ) { - - } - } - - /** * Makes sure the seeds that it selects are unique */ - @Test - public void uniqueSeeds() { + @Test void uniqueSeeds() { // extremely unlikely that it will select each one uniquely if random - uniqueSeeds(4,4); + uniqueSeeds(4, 4); // The standard algorithm switches technique when the ratio of points to seeds become // more extreme. This might not have a collision - uniqueSeeds(10,4); + uniqueSeeds(10, 4); } - public void uniqueSeeds( int numPoints , int numSeeds) { + public void uniqueSeeds( int numPoints, int numSeeds ) { int DOF = 20; - InitializeKMeans_F64 alg = createAlg(); + InitializeKMeans alg = createAlg(DOF); + var distance = new EuclideanSqArrayF64(DOF); - alg.init(DOF,0xBEEF); + alg.initialize(distance, 0xBEEF); // 4 points and 4 seeds. Each point must be a seed - List points = TestStandardKMeans_F64.createPoints(DOF,numPoints,true); - List seeds = TestStandardKMeans_F64.createPoints(DOF,numSeeds,false); - - boolean matched[] = new boolean[4]; + List points = TestStandardKMeans.createPoints(DOF, numPoints, true); + var seeds = new DogArray<>(() -> new double[DOF]); + var accessor = new ListAccessor<>(points, + ( src, dst ) -> System.arraycopy(src, 0, dst, 0, DOF), double[].class); for (int i = 0; i < 30; i++) { - alg.selectSeeds(points,seeds); + alg.selectSeeds(accessor, numSeeds, seeds); // quick test to see if it modified the inputs by adding/removing - assertEquals(numPoints,points.size()); - assertEquals(numSeeds,seeds.size()); - - Arrays.fill(matched,false); + assertEquals(numPoints, points.size()); + assertEquals(numSeeds, seeds.size()); + // Make sure each seed is only selected once for (int j = 0; j < seeds.size(); j++) { double[] a = seeds.get(j); - for (int k = j+1; k < seeds.size(); k++) { + for (int k = j + 1; k < seeds.size(); k++) { double[] b = seeds.get(k); // see if they are identical boolean identical = true; for (int l = 0; l < a.length; l++) { - if( a[l] != b[l]) { + if (a[l] != b[l]) { identical = false; break; } } - if( identical ) { + if (identical) { fail("Seed is not unique"); } } } } } + + protected void performClustering( int DOF, int NUM_SEEDS, List points, DogArray seeds ) { + var accessor = new ListAccessor<>(points, + ( src, dst ) -> System.arraycopy(src, 0, dst, 0, DOF), double[].class); + InitializeKMeans alg = createAlg(DOF); + EuclideanSqArrayF64 distance = new EuclideanSqArrayF64(DOF); + alg.initialize(distance, 0xBEEF); + alg.selectSeeds(accessor, NUM_SEEDS, seeds); + } } diff -Nru ddogleg-0.18+ds/test/org/ddogleg/clustering/kmeans/TestAssignKMeans_F64.java ddogleg-0.22+ds/test/org/ddogleg/clustering/kmeans/TestAssignKMeans_F64.java --- ddogleg-0.18+ds/test/org/ddogleg/clustering/kmeans/TestAssignKMeans_F64.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/clustering/kmeans/TestAssignKMeans_F64.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,146 +0,0 @@ -/* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. - * - * This file is part of DDogleg (http://ddogleg.org). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ddogleg.clustering.kmeans; - -import org.junit.jupiter.api.Test; - -import java.io.*; -import java.util.ArrayList; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -/** - * @author Peter Abeles - */ -public class TestAssignKMeans_F64 { - - @Test - public void serialize() { - - List clusters = new ArrayList(); - - clusters.add( new double[]{10,0,0}); - clusters.add( new double[]{0,0,10}); - - AssignKMeans_F64 alg = new AssignKMeans_F64(clusters); - - byte[] encoded = save(alg); - -// AssignKMeans_F64 found = load( encoded ); - } - - public static byte[] save( Object o ) { - try { - FileOutputStream fileOut = new FileOutputStream("junk.txt"); - ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); - ObjectOutputStream out = new ObjectOutputStream(fileOut); - out.writeObject(o); - out.close(); - return byteStream.toByteArray(); - } catch(IOException e) { - throw new RuntimeException(e); - } - } - - public static T load( byte[] data ) { - try { - ByteArrayInputStream fileIn = new ByteArrayInputStream(data); - ObjectInputStream in = new ObjectInputStream(fileIn); - T obj = (T)in.readObject(); - in.close(); - return obj; - } catch(IOException e) { - throw new RuntimeException(e); - } catch (ClassNotFoundException e) { - throw new RuntimeException(e); - } - } - - @Test - public void assign() { - - List clusters = new ArrayList(); - - clusters.add( new double[]{10,0,0}); - clusters.add( new double[]{0,0,10}); - - AssignKMeans_F64 alg = new AssignKMeans_F64(clusters); - - assertEquals(1,alg.assign(new double[]{0,0,9})); - assertEquals(0, alg.assign(new double[]{12, 0, 0})); - } - - @Test - public void assign_soft() { - - List clusters = new ArrayList(); - - clusters.add( new double[]{10,0,0}); - clusters.add( new double[]{5,0,0}); - - AssignKMeans_F64 alg = new AssignKMeans_F64(clusters); - - double histogram[] = new double[2]; - - alg.assign(new double[]{10,0,0},histogram); - assertEquals(1.0, histogram[0], 1e-8); - assertEquals(0.0, histogram[1],1e-8); - - // see if much more weight is given to the second one - alg.assign(new double[]{6, 0, 0}, histogram); - assertTrue(histogram[0]*10 < histogram[1]); - - // this is actually a difficult case for using this type of distance metric - // one cluster is much farther away and as a result the weight is equality split between the two closer points - // which might not be desirable - clusters.add( new double[]{5000,0,0}); - histogram = new double[3]; - alg.assign(new double[]{6,0,0},histogram); - assertTrue(histogram[0]/30.0 > histogram[2]); - assertEquals(histogram[0], histogram[1], 0.01); - - } - - @Test - public void copy() { - List clusters = new ArrayList(); - - clusters.add( new double[]{10,0,0}); - clusters.add( new double[]{0,0,10}); - - AssignKMeans_F64 original = new AssignKMeans_F64(clusters); - AssignKMeans_F64 copy = (AssignKMeans_F64)original.copy(); - - assertEquals(original.clusters.size(),copy.clusters.size()); - - for (int i = 0; i < original.clusters.size(); i++) { - double[] o = original.clusters.get(i); - double[] c = copy.clusters.get(i); - - assertTrue(o!=c); - - for (int j = 0; j < o.length; j++) { - assertEquals(o[j],c[j],1e-8); - } - } - - } -} diff -Nru ddogleg-0.18+ds/test/org/ddogleg/clustering/kmeans/TestAssignKMeans.java ddogleg-0.22+ds/test/org/ddogleg/clustering/kmeans/TestAssignKMeans.java --- ddogleg-0.18+ds/test/org/ddogleg/clustering/kmeans/TestAssignKMeans.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/clustering/kmeans/TestAssignKMeans.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2012-2022, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.clustering.kmeans; + +import org.ddogleg.clustering.misc.EuclideanSqArrayF64; +import org.ddogleg.struct.FastArray; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@SuppressWarnings({"NullAway"}) +public class TestAssignKMeans { + @Test void assign() { + var clusters = new FastArray<>(double[].class); + + clusters.add( new double[]{10,0,0}); + clusters.add( new double[]{0,0,10}); + + var alg = new AssignKMeans<>(clusters.toList(), new EuclideanSqArrayF64(3)); + + assertEquals(1,alg.assign(new double[]{0,0,9})); + assertEquals(0, alg.assign(new double[]{12, 0, 0})); + } + + @Test void assign_soft() { + var clusters = new FastArray<>(double[].class); + + clusters.add( new double[]{10,0,0}); + clusters.add( new double[]{5,0,0}); + + var alg = new AssignKMeans<>(clusters.toList(), new EuclideanSqArrayF64(3)); + + var histogram = new double[2]; + + alg.assign(new double[]{10,0,0},histogram); + assertEquals(1.0, histogram[0], 1e-8); + assertEquals(0.0, histogram[1],1e-8); + + // see if much more weight is given to the second one + alg.assign(new double[]{6, 0, 0}, histogram); + assertTrue(histogram[0]*10 < histogram[1]); + + // this is actually a difficult case for using this type of distance metric + // one cluster is much farther away and as a result the weight is equality split between the two closer points + // which might not be desirable + alg.clusters.add( new double[]{5000,0,0}); + histogram = new double[3]; + alg.assign(new double[]{6,0,0},histogram); + assertTrue(histogram[0]/30.0 > histogram[2]); + assertEquals(histogram[0], histogram[1], 0.01); + } +} diff -Nru ddogleg-0.18+ds/test/org/ddogleg/clustering/kmeans/TestInitializePlusPlus.java ddogleg-0.22+ds/test/org/ddogleg/clustering/kmeans/TestInitializePlusPlus.java --- ddogleg-0.18+ds/test/org/ddogleg/clustering/kmeans/TestInitializePlusPlus.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/clustering/kmeans/TestInitializePlusPlus.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -18,110 +18,95 @@ package org.ddogleg.clustering.kmeans; +import org.ddogleg.clustering.misc.EuclideanSqArrayF64; +import org.ddogleg.clustering.misc.ListAccessor; +import org.ddogleg.struct.DogArray; +import org.ddogleg.struct.DogArray_F64; import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.List; -import java.util.Random; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; /** * @author Peter Abeles */ -public class TestInitializePlusPlus extends StandardInitializeKMeansChecks{ - - Random rand = new Random(234); - +public class TestInitializePlusPlus extends StandardInitializeKMeansChecks { /** - * In this situation there are not enough unique points which can act as unique seeds. - * - * This is a stricter version of generic test + * Every point has a duplicate and even though there are more points than seeds it should only + * select 1/2 the points as seeds */ - @Test - public void notEnoughUniquePoints_strict() { - int DOF = 20; + @Test void notEnoughUniquePoints_strict() { + int DOF = 18; + int NUM_SEEDS = 20; + + List points = TestStandardKMeans.createPoints(DOF, 30, true); - List points = TestStandardKMeans_F64.createPoints(DOF,30,true); for (int i = 1; i < points.size(); i += 2) { - System.arraycopy(points.get(i-1),0,points.get(i),0,DOF); + System.arraycopy(points.get(i - 1), 0, points.get(i), 0, DOF); } - List seeds = TestStandardKMeans_F64.createPoints(DOF,20,false); + var seeds = new DogArray<>(() -> new double[DOF]); - InitializeKMeans_F64 alg = createAlg(); - alg.init(DOF,0xBEEF); + performClustering(DOF, NUM_SEEDS, points, seeds); + assertEquals(15, seeds.size); - alg.selectSeeds(points, seeds); - - int hits[] = new int[15]; - for( double[] a : seeds ) { - int match = findMatch( a , points )/2; + var hits = new int[15]; + for (double[] a : seeds.toList()) { + int match = findMatch(a, points)/2; hits[match]++; } // make sure each one was selected at least once for (int i = 0; i < hits.length; i++) { - assertTrue(hits[i] > 0); + assertEquals(hits[i], 1); } } /** * Test seed selection by seeing if it has the expected distribution. */ - @Test - public void selectNextSeed() { - InitializePlusPlus alg = new InitializePlusPlus(); - alg.init(1,123); - - alg.distance.resize(3); - alg.distance.data = new double[]{3,6,1}; - alg.totalDistance = 10.0; + @Test void selectPointForNextSeed() { + final int DOF = 1; + var alg = new InitializePlusPlus(); + alg.initialize(new EuclideanSqArrayF64(DOF), 123); - List points = new ArrayList(); - for (int i = 0; i < 3; i++) { - points.add(new double[1]); - } + alg.distances = DogArray_F64.array(3, 6, 1); + alg.sumDistances = 10.0; - double histogram[] = new double[3]; + var histogram = new double[3]; for (int i = 0; i < 1000; i++) { - double[] seed = alg.selectNextSeed(points,rand.nextDouble()); - int which = -1; - for (int j = 0; j < points.size(); j++) { - if( points.get(j) == seed ) { - which = j; - break; - } - } - histogram[which]++; + histogram[alg.selectPointForNextSeed(rand.nextDouble())]++; } - assertEquals(0.3,histogram[0]/1000.0,0.02); - assertEquals(0.6,histogram[1]/1000.0,0.02); - assertEquals(0.1,histogram[2]/1000.0,0.02); + assertEquals(0.3, histogram[0]/1000.0, 0.02); + assertEquals(0.6, histogram[1]/1000.0, 0.02); + assertEquals(0.1, histogram[2]/1000.0, 0.02); } - @Test - public void updateDistances() { - InitializePlusPlus alg = new InitializePlusPlus(); - alg.init(1,123); - - alg.distance.resize(3); - alg.distance.data = new double[]{3,6,1}; - List points = new ArrayList(); + @Test void updateDistanceWithNewSeed() { + final int DOF = 1; + var alg = new InitializePlusPlus(); + alg.initialize(new EuclideanSqArrayF64(DOF), 123); + + alg.distances = DogArray_F64.array(3, 6, 1); + alg.sumDistances = Double.NaN; // if not reset this will mess it up + var points = new ArrayList(); for (int i = 0; i < 3; i++) { points.add(new double[]{i*i}); } - alg.updateDistances(points, new double[]{-1}); - assertEquals(1,alg.distance.get(0),1e-8); - assertEquals(4,alg.distance.get(1),1e-8); - assertEquals(1,alg.distance.get(2),1e-8); - assertEquals(6,alg.totalDistance,1e-8); + var accessor = new ListAccessor<>(points, + ( src, dst ) -> System.arraycopy(src, 0, dst, 0, DOF), double[].class); + alg.updateDistanceWithNewSeed(accessor, new double[]{-1}); + assertEquals(1, alg.distances.get(0), 1e-8); + assertEquals(4, alg.distances.get(1), 1e-8); + assertEquals(1, alg.distances.get(2), 1e-8); + assertEquals(6, alg.sumDistances, 1e-8); } @Override - public InitializeKMeans_F64 createAlg() { - return new InitializePlusPlus(); + public InitializeKMeans createAlg( int DOF ) { + return new InitializePlusPlus<>(); } } \ No newline at end of file diff -Nru ddogleg-0.18+ds/test/org/ddogleg/clustering/kmeans/TestInitializePlusPlus_MT.java ddogleg-0.22+ds/test/org/ddogleg/clustering/kmeans/TestInitializePlusPlus_MT.java --- ddogleg-0.18+ds/test/org/ddogleg/clustering/kmeans/TestInitializePlusPlus_MT.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/clustering/kmeans/TestInitializePlusPlus_MT.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.clustering.kmeans; + +import org.ddogleg.clustering.misc.EuclideanSqArrayF64; +import org.ddogleg.clustering.misc.ListAccessor; +import org.ddogleg.struct.DogArray; +import org.ejml.UtilEjml; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author Peter Abeles + */ +class TestInitializePlusPlus_MT { + /** + * Create a random scenario and compare toe single threaded results + */ + @Test void compare() { + final int DOF = 9; + var single = new InitializePlusPlus(); + var threaded = new InitializePlusPlus_MT<>(() -> new double[DOF]); + + List points = TestStandardKMeans.createPoints(DOF, 500, true); + + var seedsSingle = new DogArray<>(() -> new double[DOF]); + var seedsThreaded = new DogArray<>(() -> new double[DOF]); + + for (int trial = 0; trial < 2; trial++) { + performClustering(single, DOF, 15, points, seedsSingle); + performClustering(threaded, DOF, 15, points, seedsThreaded); + + assertEquals(seedsSingle.size, seedsThreaded.size); + for (int seedIdx = 0; seedIdx < seedsThreaded.size; seedIdx++) { + double[] e = seedsSingle.get(seedIdx); + double[] f = seedsThreaded.get(seedIdx); + + assertArrayEquals(e, f, UtilEjml.TEST_F64); + } + } + } + + protected void performClustering( InitializePlusPlus alg, + int DOF, int NUM_SEEDS, List points, DogArray seeds ) { + var accessor = new ListAccessor<>(points, + ( src, dst ) -> System.arraycopy(src, 0, dst, 0, DOF), double[].class); + EuclideanSqArrayF64 distance = new EuclideanSqArrayF64(DOF); + alg.initialize(distance, 0xBEEF); + alg.selectSeeds(accessor, NUM_SEEDS, seeds); + } +} \ No newline at end of file diff -Nru ddogleg-0.18+ds/test/org/ddogleg/clustering/kmeans/TestInitializeStandard_F64.java ddogleg-0.22+ds/test/org/ddogleg/clustering/kmeans/TestInitializeStandard_F64.java --- ddogleg-0.18+ds/test/org/ddogleg/clustering/kmeans/TestInitializeStandard_F64.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/clustering/kmeans/TestInitializeStandard_F64.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2015, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -23,7 +23,7 @@ */ public class TestInitializeStandard_F64 extends StandardInitializeKMeansChecks { @Override - public InitializeKMeans_F64 createAlg() { - return new InitializeStandard_F64(); + public InitializeKMeans createAlg(int dof) { + return new InitializeStandard<>(); } } diff -Nru ddogleg-0.18+ds/test/org/ddogleg/clustering/kmeans/TestStandardKMeans_F64.java ddogleg-0.22+ds/test/org/ddogleg/clustering/kmeans/TestStandardKMeans_F64.java --- ddogleg-0.18+ds/test/org/ddogleg/clustering/kmeans/TestStandardKMeans_F64.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/clustering/kmeans/TestStandardKMeans_F64.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,161 +0,0 @@ -/* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. - * - * This file is part of DDogleg (http://ddogleg.org). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ddogleg.clustering.kmeans; - -import org.ddogleg.clustering.ComputeClusters; -import org.ddogleg.clustering.GenericClusterChecks_F64; -import org.junit.jupiter.api.Test; - -import java.util.ArrayList; -import java.util.List; -import java.util.Random; - -import static org.junit.jupiter.api.Assertions.assertEquals; - - -/** - * - * Can;t use generic checks because he seeds might suck and the sets will be poorly chosen. - * - * @author Peter Abeles - */ -public class TestStandardKMeans_F64 extends GenericClusterChecks_F64{ - - @Test - public void matchPointsToClusters() { - StandardKMeans_F64 alg = new StandardKMeans_F64(100,100,1,new InitializeStandard_F64()); - - alg.init(4, 123); - - alg.clusters.resize(3); - alg.workClusters.resize(3); - alg.memberCount.resize(3); - - alg.clusters.data[0] = new double[]{20,0,0,0}; - alg.clusters.data[1] = new double[]{0,20,0,0}; - alg.clusters.data[2] = new double[]{0,0,20,0}; - - List points = new ArrayList(); - points.add( new double[]{20,5,0,0}); - points.add( new double[]{25,-4,0,0}); - points.add( new double[]{0,0,22,0}); - - alg.matchPointsToClusters(points); - - assertEquals(2,alg.memberCount.get(0)); - assertEquals(0,alg.memberCount.get(1)); - assertEquals(1,alg.memberCount.get(2)); - - assertEquals(45,alg.workClusters.data[0][0],1e-8); - assertEquals(1 ,alg.workClusters.data[0][1],1e-8); - assertEquals(0 ,alg.workClusters.data[0][2],1e-8); - assertEquals(0 ,alg.workClusters.data[0][3],1e-8); - - assertEquals(0 ,alg.workClusters.data[1][1],1e-8); - - assertEquals(22,alg.workClusters.data[2][2],1e-8); - } - - @Test - public void updateClusterCenters() { - - StandardKMeans_F64 alg = new StandardKMeans_F64(100,100,1,new InitializeStandard_F64()); - - alg.init(4,123); - - alg.clusters.resize(3); - alg.workClusters.resize(3); - alg.memberCount.resize(3); - - double orig[][] = new double[3][4]; - orig[0] = new double[]{10,20,30,20}; - orig[1] = new double[]{20,10,30,40}; - orig[2] = new double[]{3,9,1,12}; - - alg.workClusters.data[0] = orig[0].clone(); - alg.workClusters.data[1] = orig[1].clone(); - alg.workClusters.data[2] = orig[2].clone(); - - alg.memberCount.data[0] = 10; - alg.memberCount.data[1] = 1; - alg.memberCount.data[2] = 3; - - // previous clusters will be near zero - alg.updateClusterCenters(); - - for (int i = 0; i < 4; i++) { - assertEquals(alg.clusters.data[0][i],orig[0][i]/10); - assertEquals(alg.clusters.data[1][i],orig[1][i]/1); - assertEquals(alg.clusters.data[2][i],orig[2][i]/3); - } - } - - @Test - public void distanceSq() { - double a[] = new double[]{1,2,3,4,5}; - double b[] = new double[]{4,6,3,1,-1}; - - double found = StandardKMeans_F64.distanceSq(a,b); - assertEquals(70.0,found,1e-8); - } - - public static List createPoints( int DOF , int total , boolean fillRandom ) { - List ret = new ArrayList(); - - Random random = new Random(23432+DOF+total); - - for (int i = 0; i < total; i++) { - double[] a = new double[DOF]; - if( fillRandom ) { - for (int j = 0; j < a.length; j++) { - a[j] = random.nextGaussian(); - } - } - - ret.add(a); - } - - return ret; - } - - @Override - public ComputeClusters createClustersAlg( boolean hint ) { - if( hint ) { - // assume the first 3 are in different groups for the seeds - return new StandardKMeans_F64(1000,1000, 1e-8, new FixedSeeds()); - } else { - InitializeStandard_F64 seeds = new InitializeStandard_F64(); - return new StandardKMeans_F64(1000,1000, 1e-8, seeds ); - } - } - - public static class FixedSeeds implements InitializeKMeans_F64 { - - @Override - public void init(int pointDimension, long randomSeed) {} - - @Override - public void selectSeeds(List points, List seeds) { - int N = seeds.get(0).length; - for (int i = 0; i < 3; i++) { - System.arraycopy(points.get(i), 0, seeds.get(i), 0, N); - } - } - } -} diff -Nru ddogleg-0.18+ds/test/org/ddogleg/clustering/kmeans/TestStandardKMeans.java ddogleg-0.22+ds/test/org/ddogleg/clustering/kmeans/TestStandardKMeans.java --- ddogleg-0.18+ds/test/org/ddogleg/clustering/kmeans/TestStandardKMeans.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/clustering/kmeans/TestStandardKMeans.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.clustering.kmeans; + +import org.ddogleg.clustering.ComputeClusters; +import org.ddogleg.clustering.ComputeMeanClusters; +import org.ddogleg.clustering.GenericClusterChecks_F64; +import org.ddogleg.clustering.PointDistance; +import org.ddogleg.clustering.misc.EuclideanSqArrayF64; +import org.ddogleg.clustering.misc.ListAccessor; +import org.ddogleg.clustering.misc.MeanArrayF64; +import org.ddogleg.struct.DogArray; +import org.ddogleg.struct.LArrayAccessor; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * Can;t use generic checks because he seeds might suck and the sets will be poorly chosen. + * + * @author Peter Abeles + */ +public class TestStandardKMeans extends GenericClusterChecks_F64 { + int DOF = 4; + + @Test void matchPointsToClusters() { + StandardKMeans alg = createAlg(DOF); + + // Don't use internal clusters, use this new one to make sure it's referencing the correct one + DogArray clusters = new DogArray<>(() -> new double[DOF]); + clusters.resize(3); + alg.memberCount.resize(3); + + clusters.data[0] = new double[]{20, 0, 0, 0}; + clusters.data[1] = new double[]{0, 20, 0, 0}; + clusters.data[2] = new double[]{0, 0, 20, 0}; + + List points = new ArrayList<>(); + points.add(new double[]{20, 5, 0, 0}); + points.add(new double[]{25, -4, 0, 0}); + points.add(new double[]{0, 0, 22, 0}); + + ListAccessor accessor = new ListAccessor<>(points, + ( src, dst ) -> System.arraycopy(src, 0, dst, 0, DOF), double[].class); + + alg.matchPointsToClusters(accessor, clusters); + + assertEquals(2, alg.memberCount.get(0)); + assertEquals(0, alg.memberCount.get(1)); + assertEquals(1, alg.memberCount.get(2)); + + assertEquals(0, alg.assignments.get(0)); + assertEquals(0, alg.assignments.get(1)); + assertEquals(2, alg.assignments.get(2)); + } + + private StandardKMeans createAlg( int DOF ) { + ComputeMeanClusters updateMeans = new MeanArrayF64(DOF); + InitializeKMeans seedSelector = new InitializePlusPlus<>(); + PointDistance distancer = new EuclideanSqArrayF64(DOF); + StandardKMeans alg = new StandardKMeans<>(updateMeans, seedSelector, distancer, + () -> new double[DOF]); + alg.reseedAfterIterations = 100; + alg.maxIterations = 100; + alg.maxReSeed = 0; + alg.convergeTol = 1e-8; + + alg.initialize(123); + return alg; + } + + public static List createPoints( int DOF, int total, boolean fillRandom ) { + List ret = new ArrayList<>(); + + var random = new Random(23432 + DOF + total); + + for (int i = 0; i < total; i++) { + double[] a = new double[DOF]; + if (fillRandom) { + for (int j = 0; j < a.length; j++) { + a[j] = random.nextGaussian(); + } + } + + ret.add(a); + } + + return ret; + } + + @Override + public ComputeClusters createClustersAlg( boolean seedHint, int dof ) { + return createAlg(dof); + } + + @Override protected int selectBestCluster( ComputeClusters alg, double[] p ) { + StandardKMeans kmeans = (StandardKMeans)alg; + double bestDistance = Double.MAX_VALUE; + int best = -1; + for (int i = 0; i < kmeans.bestClusters.size; i++) { + double d = kmeans.distancer.distance(p, kmeans.bestClusters.get(i)); + if (d < bestDistance) { + bestDistance = d; + best = i; + } + } + return best; + } + + public static class FixedSeeds implements InitializeKMeans { + @Override + public void initialize( PointDistance distance, long randomSeed ) {} + + @Override + public void selectSeeds( LArrayAccessor points, int requestedSeeds, DogArray selectedSeeds ) { + selectedSeeds.reset(); + for (int i = 0; i < requestedSeeds; i++) { + points.getCopy(i, selectedSeeds.grow()); + } + } + + @Override public InitializeKMeans newInstanceThread() { + return this; + } + } +} diff -Nru ddogleg-0.18+ds/test/org/ddogleg/clustering/kmeans/TestStandardKMeans_MT.java ddogleg-0.22+ds/test/org/ddogleg/clustering/kmeans/TestStandardKMeans_MT.java --- ddogleg-0.18+ds/test/org/ddogleg/clustering/kmeans/TestStandardKMeans_MT.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/clustering/kmeans/TestStandardKMeans_MT.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.clustering.kmeans; + +import org.ddogleg.clustering.ComputeMeanClusters; +import org.ddogleg.clustering.PointDistance; +import org.ddogleg.clustering.misc.EuclideanSqArrayF64; +import org.ddogleg.clustering.misc.ListAccessor; +import org.ddogleg.clustering.misc.MeanArrayF64; +import org.ddogleg.struct.DogArray; +import org.ejml.UtilEjml; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author Peter Abeles + */ +class TestStandardKMeans_MT { + /** + * Create a simple random scenario and compare single to threaded results + */ + @Test void compare() { + final int DOF = 13; + List points = TestStandardKMeans.createPoints(DOF, 500, true); + ListAccessor accessor = new ListAccessor<>(points, + ( src, dst ) -> System.arraycopy(src, 0, dst, 0, DOF), double[].class); + + StandardKMeans single = createAlg(DOF, false); + StandardKMeans multi = createAlg(DOF, true); + + single.initialize(0xBEEF); + multi.initialize(0xBEEF); + + for (int trial = 0; trial < 2; trial++) { + single.process(accessor, 14); + multi.process(accessor, 14); + + assertEquals(single.getBestClusterScore(), multi.getBestClusterScore(), UtilEjml.TEST_F64); + + DogArray singleCluster = single.getBestClusters(); + DogArray multiCluster = multi.getBestClusters(); + + assertEquals(14, singleCluster.size); + assertEquals(14, multiCluster.size); + + for (int seedIdx = 0; seedIdx < singleCluster.size; seedIdx++) { + double[] e = singleCluster.get(seedIdx); + double[] f = multiCluster.get(seedIdx); + + assertArrayEquals(e, f, UtilEjml.TEST_F64); + } + } + } + + private StandardKMeans createAlg( int DOF, boolean threaded ) { + ComputeMeanClusters updateMeans = new MeanArrayF64(DOF); + InitializeKMeans seedSelector = new InitializePlusPlus<>(); + PointDistance distancer = new EuclideanSqArrayF64(DOF); + StandardKMeans alg = + threaded ? + new StandardKMeans_MT<>(updateMeans, seedSelector, distancer, () -> new double[DOF]) : + new StandardKMeans<>(updateMeans, seedSelector, distancer, () -> new double[DOF]); + alg.reseedAfterIterations = 100; + alg.maxIterations = 100; + alg.maxReSeed = 0; + alg.convergeTol = 1e-8; + + alg.initialize(123); + return alg; + } +} \ No newline at end of file diff -Nru ddogleg-0.18+ds/test/org/ddogleg/clustering/misc/TestEuclideanSqArrayF64.java ddogleg-0.22+ds/test/org/ddogleg/clustering/misc/TestEuclideanSqArrayF64.java --- ddogleg-0.18+ds/test/org/ddogleg/clustering/misc/TestEuclideanSqArrayF64.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/clustering/misc/TestEuclideanSqArrayF64.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.clustering.misc; + +import org.ejml.UtilEjml; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author Peter Abeles + */ +class TestEuclideanSqArrayF64 { + @Test void distance() { + var alg = new EuclideanSqArrayF64(5); + + var a = new double[]{1, 2, 3, 4, 5}; + var b = new double[]{1, 2, 3, 4, 5}; + + assertEquals(0.0, alg.distance(a, b), UtilEjml.TEST_F64); + b[4] = 10; + assertEquals(25.0, alg.distance(a, b), UtilEjml.TEST_F64); + } +} \ No newline at end of file diff -Nru ddogleg-0.18+ds/test/org/ddogleg/clustering/misc/TestListAccessor.java ddogleg-0.22+ds/test/org/ddogleg/clustering/misc/TestListAccessor.java --- ddogleg-0.18+ds/test/org/ddogleg/clustering/misc/TestListAccessor.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/clustering/misc/TestListAccessor.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.clustering.misc; + +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; + +/** + * @author Peter Abeles + */ +class TestListAccessor { + /** + * A few simple tests combined into one function + */ + @Test void various() { + var list = new ArrayList(); + list.add(new Foo(1)); + list.add(new Foo(-1)); + var alg = new ListAccessor<>(list, (a,b)->a.a=b.a, Foo.class); + + assertEquals(2, alg.size()); + + var tmp = new Foo(10); + for (int i = 0; i < 2; i++) { + assertSame(list.get(i), alg.getTemp(i)); + + alg.getCopy(i, tmp); + assertEquals(list.get(i).a, tmp.a); + } + + tmp.a = 999; + alg.copy(list.get(0), tmp); + assertEquals(list.get(0).a, tmp.a); + } + + public static class Foo { + public int a; + public Foo(int a) { + this.a = a; + } + } +} \ No newline at end of file diff -Nru ddogleg-0.18+ds/test/org/ddogleg/clustering/misc/TestMeanArrayF64.java ddogleg-0.22+ds/test/org/ddogleg/clustering/misc/TestMeanArrayF64.java --- ddogleg-0.18+ds/test/org/ddogleg/clustering/misc/TestMeanArrayF64.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/clustering/misc/TestMeanArrayF64.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.clustering.misc; + +import org.ddogleg.struct.DogArray; +import org.ddogleg.struct.DogArray_I32; +import org.ejml.UtilEjml; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author Peter Abeles + */ +class TestMeanArrayF64 { + /** + * Create a situation that's easy to hand compute the solution and see if it's right + */ + @Test void easy() { + int dof = 2; + List list = new ArrayList<>(); + var assignments = new DogArray_I32(); + for (int i = 0; i < 9; i++) { + list.add(new double[]{i+1,i}); + + // only the first element will be assigned to 0, all the others are 1 + assignments.add(i==0?0:1); + } + var accessor = new ListAccessor<>(list, + (src, dst) -> System.arraycopy(src, 0, dst, 0, dof), double[].class); + + var means = new DogArray<>(()->new double[dof]); + means.resize(2); + + var alg = new MeanArrayF64(dof); + alg.process(accessor, assignments, means); + + // make sure it didn't mess with the size + assertEquals(2, means.size); + + assertArrayEquals(list.get(0), means.get(0), UtilEjml.TEST_F64); + + assertEquals(5.5, means.get(1)[0], UtilEjml.TEST_F64); + assertEquals(4.5, means.get(1)[1], UtilEjml.TEST_F64); + } +} \ No newline at end of file diff -Nru ddogleg-0.18+ds/test/org/ddogleg/combinatorics/TestCombinations.java ddogleg-0.22+ds/test/org/ddogleg/combinatorics/TestCombinations.java --- ddogleg-0.18+ds/test/org/ddogleg/combinatorics/TestCombinations.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/combinatorics/TestCombinations.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -32,10 +32,10 @@ @Test public void next_1_1() { - List list = new ArrayList(); + List list = new ArrayList<>(); list.add(1); - Combinations alg =new Combinations(list,1); + Combinations alg = new Combinations<>(list, 1); assertEquals(1, (int)alg.get(0)); assertFalse(alg.next()); @@ -43,11 +43,11 @@ @Test public void next_1_2() { - List list = new ArrayList(); + List list = new ArrayList<>(); list.add(1); list.add(2); - Combinations alg =new Combinations(list,1); + Combinations alg = new Combinations<>(list, 1); assertEquals(1, (int)alg.get(0)); @@ -58,11 +58,11 @@ @Test public void next_2_2() { - List list = new ArrayList(); + List list = new ArrayList<>(); list.add(1); list.add(2); - Combinations alg = new Combinations(list,2); + Combinations alg = new Combinations<>(list, 2); assertEquals(1, (int)alg.get(0)); assertEquals(2, (int)alg.get(1)); @@ -72,12 +72,12 @@ @Test public void next_2_3() { - List list = new ArrayList(); + List list = new ArrayList<>(); list.add(1); list.add(2); list.add(3); - Combinations alg = new Combinations(list,2); + Combinations alg = new Combinations<>(list, 2); assertEquals(1, (int)alg.get(0)); assertEquals(2, (int)alg.get(1)); @@ -93,12 +93,12 @@ @Test public void previous() { - List list = new ArrayList(); + List list = new ArrayList<>(); list.add(1); list.add(2); list.add(3); - Combinations alg = new Combinations(list,2); + Combinations alg = new Combinations<>(list, 2); assertTrue(alg.next()); // sanity check @@ -126,17 +126,17 @@ @Test public void getList_getOutside() { - List list = new ArrayList(); + List list = new ArrayList<>(); list.add(1); list.add(2); list.add(3); - Combinations alg = new Combinations(list,2); + Combinations alg = new Combinations<>(list, 2); assertTrue(alg.next()); // test getBucket() with and without storage - List a = new ArrayList(); + List a = new ArrayList<>(); alg.getBucket(a); List b = alg.getBucket(null); @@ -148,7 +148,7 @@ assertEquals(3,(int)b.get(1)); // test getOutside() with and without storage - a = new ArrayList(); + a = new ArrayList<>(); alg.getOutside(a); b = alg.getOutside(null); @@ -168,11 +168,11 @@ } private long computeNumShuffles( int numBins , int numItems ) { - List l = new ArrayList(); + List l = new ArrayList<>(); for( int i = 0; i < numItems; i++ ) { l.add(i); } - Combinations c = new Combinations(l,numBins); + Combinations c = new Combinations<>(l, numBins); return c.computeTotalCombinations(); } diff -Nru ddogleg-0.18+ds/test/org/ddogleg/combinatorics/TestPermute.java ddogleg-0.22+ds/test/org/ddogleg/combinatorics/TestPermute.java --- ddogleg-0.18+ds/test/org/ddogleg/combinatorics/TestPermute.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/combinatorics/TestPermute.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -32,10 +32,10 @@ @Test public void testSetSize1() { - List l = new ArrayList(); + List l = new ArrayList<>(); l.add(1); - Permute alg = new Permute(l); + Permute alg = new Permute<>(l); assertEquals(1, alg.getTotalPermutations()); @@ -44,11 +44,11 @@ @Test public void testSetSize2() { - List l = new ArrayList(); + List l = new ArrayList<>(); l.add(1); l.add(2); - Permute alg = new Permute(l); + Permute alg = new Permute<>(l); assertEquals(2, alg.getTotalPermutations()); @@ -57,12 +57,12 @@ @Test public void testSetSize3() { - List l = new ArrayList(); + List l = new ArrayList<>(); l.add(1); l.add(2); l.add(3); - Permute alg = new Permute(l); + Permute alg = new Permute<>(l); assertEquals(6, alg.getTotalPermutations()); @@ -72,13 +72,13 @@ @Test public void testSetSize4() { - List l = new ArrayList(); + List l = new ArrayList<>(); l.add(1); l.add(2); l.add(3); l.add(4); - Permute alg = new Permute(l); + Permute alg = new Permute<>(l); assertEquals(24, alg.getTotalPermutations()); @@ -87,7 +87,7 @@ @Test public void testSetSize5_to_7() { - List l = new ArrayList(); + List l = new ArrayList<>(); l.add(1); l.add(2); l.add(3); @@ -97,7 +97,7 @@ int total = 24*5; for (int i = 0; i <= 2; i++) { - Permute alg = new Permute(l); + Permute alg = new Permute<>(l); assertEquals(total, alg.getTotalPermutations()); @@ -110,13 +110,13 @@ @Test public void previous() { - List l = new ArrayList(); + List l = new ArrayList<>(); for (int size = 0; size < 9; size++) { l.add(size); - Permute alg = new Permute(l); + Permute alg = new Permute<>(l); - List> forward = new ArrayList>(); + List> forward = new ArrayList<>(); do { forward.add( alg.getPermutation(null)); } while( alg.next() ); @@ -127,14 +127,14 @@ List expected = forward.get(i--); for( int j = 0; j < size; j++ ) { - assertTrue(found.get(j) == expected.get(j)); + assertSame(found.get(j), expected.get(j)); } } while( alg.previous() ); } } private void checkList( Permute p , int expected ) { - List all = new ArrayList(); + List all = new ArrayList<>(); do { List l = p.getPermutation(null); diff -Nru ddogleg-0.18+ds/test/org/ddogleg/fitting/modelset/distance/DistanceFromMeanModel.java ddogleg-0.22+ds/test/org/ddogleg/fitting/modelset/distance/DistanceFromMeanModel.java --- ddogleg-0.18+ds/test/org/ddogleg/fitting/modelset/distance/DistanceFromMeanModel.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/fitting/modelset/distance/DistanceFromMeanModel.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2016, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -37,16 +37,14 @@ } @Override - public double computeDistance(Double pt) { + public double distance(Double pt) { return Math.abs(pt - mean); } @Override - public void computeDistance(List points, double[] distance) { + public void distances(List points, double[] distance) { for (int i = 0; i < points.size(); i++) { - double d = points.get(i); - - distance[i] = Math.abs(d - mean); + distance[i] = Math.abs(points.get(i) - mean); } } diff -Nru ddogleg-0.18+ds/test/org/ddogleg/fitting/modelset/distance/TestFitByMeanStatistics.java ddogleg-0.22+ds/test/org/ddogleg/fitting/modelset/distance/TestFitByMeanStatistics.java --- ddogleg-0.18+ds/test/org/ddogleg/fitting/modelset/distance/TestFitByMeanStatistics.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/fitting/modelset/distance/TestFitByMeanStatistics.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -20,9 +20,7 @@ import org.junit.jupiter.api.Test; -import java.util.Collections; -import java.util.LinkedList; -import java.util.Random; +import java.util.*; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -36,16 +34,17 @@ @Test public void metric_and_prune() { - LinkedList> inliers = new LinkedList>(); + List> list = new ArrayList<>(200); for (int i = 0; i < 200; i++) { - inliers.add(new PointIndex((double) i,i)); + list.add(new PointIndex<>((double) i,i)); } // randomize the inputs - Collections.shuffle(inliers,rand); + Collections.shuffle(list,rand); + ArrayDeque> inliers = new ArrayDeque<>(list); - FitByMeanStatistics fit = new FitByMeanStatistics(1); + FitByMeanStatistics fit = new FitByMeanStatistics<>(1); fit.init(new DistanceFromMeanModel(), inliers); diff -Nru ddogleg-0.18+ds/test/org/ddogleg/fitting/modelset/distance/TestFitByMedianStatistics.java ddogleg-0.22+ds/test/org/ddogleg/fitting/modelset/distance/TestFitByMedianStatistics.java --- ddogleg-0.18+ds/test/org/ddogleg/fitting/modelset/distance/TestFitByMedianStatistics.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/fitting/modelset/distance/TestFitByMedianStatistics.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -20,9 +20,7 @@ import org.junit.jupiter.api.Test; -import java.util.Collections; -import java.util.LinkedList; -import java.util.Random; +import java.util.*; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -36,16 +34,17 @@ @Test public void metric_and_prune() { - LinkedList> inliers = new LinkedList>(); + List> list = new ArrayList<>(200); for (int i = 0; i < 200; i++) { - inliers.add(new PointIndex((double) i,i)); + list.add(new PointIndex<>((double) i,i)); } // randomize the inputs - Collections.shuffle(inliers, rand); + Collections.shuffle(list,rand); + ArrayDeque> inliers = new ArrayDeque<>(list); - FitByMedianStatistics fit = new FitByMedianStatistics(0.90); + FitByMedianStatistics fit = new FitByMedianStatistics<>(0.90); fit.init(new DistanceFromMeanModel(), inliers); diff -Nru ddogleg-0.18+ds/test/org/ddogleg/fitting/modelset/distance/TestStatisticalDistanceModelMatcher.java ddogleg-0.22+ds/test/org/ddogleg/fitting/modelset/distance/TestStatisticalDistanceModelMatcher.java --- ddogleg-0.18+ds/test/org/ddogleg/fitting/modelset/distance/TestStatisticalDistanceModelMatcher.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/fitting/modelset/distance/TestStatisticalDistanceModelMatcher.java 2022-09-01 02:18:09.000000000 +0000 @@ -36,9 +36,9 @@ ModelGenerator generator, ModelFitter fitter, int minPoints, double fitThreshold) { - return new StatisticalDistanceModelMatcher(5, 0, 0, 10000, minPoints, + return new StatisticalDistanceModelMatcher<>(5, 0, 0, 10000, minPoints, StatisticalDistance.PERCENTILE, - 0.95, manager,fitter, distance, new ArrayCodec()); + 0.95, manager, generator, distance, new ArrayCodec()); } private static class ArrayCodec implements ModelCodec diff -Nru ddogleg-0.18+ds/test/org/ddogleg/fitting/modelset/DoNothingModelFitter.java ddogleg-0.22+ds/test/org/ddogleg/fitting/modelset/DoNothingModelFitter.java --- ddogleg-0.18+ds/test/org/ddogleg/fitting/modelset/DoNothingModelFitter.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/fitting/modelset/DoNothingModelFitter.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -18,33 +18,33 @@ package org.ddogleg.fitting.modelset; +import org.jetbrains.annotations.Nullable; + import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; - /** * Claims to fit a model but really doesn't * * @author Peter Abeles */ -public class DoNothingModelFitter implements ModelFitter , - ModelGenerator{ +public class DoNothingModelFitter implements ModelFitter, ModelGenerator { int minPoints; - public DoNothingModelFitter(int minPoints) { + public DoNothingModelFitter( int minPoints ) { this.minPoints = minPoints; } @Override - public boolean generate(List dataSet, double[] param ) { - return fitModel(dataSet,null,param); + public boolean generate( List dataSet, double[] param ) { + return fitModel(dataSet, null, param); } @Override - public boolean fitModel(List dataSet, double[] initParam, double[] foundParam) { - assertEquals(minPoints,dataSet.size()); + public boolean fitModel( List dataSet, @Nullable double[] initParam, double[] foundParam ) { + assertEquals(minPoints, dataSet.size()); return true; } diff -Nru ddogleg-0.18+ds/test/org/ddogleg/fitting/modelset/GenericModelMatcherMultiTests.java ddogleg-0.22+ds/test/org/ddogleg/fitting/modelset/GenericModelMatcherMultiTests.java --- ddogleg-0.18+ds/test/org/ddogleg/fitting/modelset/GenericModelMatcherMultiTests.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/fitting/modelset/GenericModelMatcherMultiTests.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -33,8 +33,8 @@ * * @author Peter Abeles */ +@SuppressWarnings({"NullAway.Init"}) public abstract class GenericModelMatcherMultiTests extends GenericModelMatcherTests{ - /** * Wrapper to provide support for single ModelMatcher tests */ diff -Nru ddogleg-0.18+ds/test/org/ddogleg/fitting/modelset/GenericModelMatcherPostTests.java ddogleg-0.22+ds/test/org/ddogleg/fitting/modelset/GenericModelMatcherPostTests.java --- ddogleg-0.18+ds/test/org/ddogleg/fitting/modelset/GenericModelMatcherPostTests.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/fitting/modelset/GenericModelMatcherPostTests.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.fitting.modelset; + +import org.ddogleg.fitting.modelset.distance.DistanceFromMeanModel; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Random; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * A series of tests are provided that test to see the provided model set algorithm has the expected + * behavior. This includes removing outliers and estimating the model parameters. The test + * cases tend to be fairly easy so that they will work on all model set algorithms. + * + * @author Peter Abeles + */ +public abstract class GenericModelMatcherPostTests { + + protected Random rand = new Random(0x353456); + + // how many of the points is it expected to match + protected double minMatchFrac = 1.0; + // how close does the parameter set need to be + protected double parameterTol = 1e-8; + // should it check the inlier set for accuracy + protected boolean checkInlierSet = true; + + // mean of the true inlier set + // this value is the best estimate that can possibly be done + private double inlierMean; + + // If the model matcher is deterministic and doesn't use any random numbers + protected boolean deterministic = false; + + protected void configure( double minMatchFrac, double parameterTol, boolean checkInlierSet ) { + this.minMatchFrac = minMatchFrac; + this.parameterTol = parameterTol; + this.checkInlierSet = checkInlierSet; + } + + /** + * Sees if it can correctly select a model set and determine the best fit parameters for + * a simple test case. + */ + @Test + public void performSimpleModelFit() { + double mean = 2.5; + double tol = 0.2; + + // generate the points with a smaller tolerance to account for fitting error + // later on. + ModelMatcher alg = createModel(4, tol*0.95); + + List samples = createSampleSet(100, mean, tol, 0.1); + + assertTrue(alg.process(samples)); + + List matchSet = alg.getMatchSet(); + + if (checkInlierSet) + assertTrue(matchSet.size()/90.0 >= minMatchFrac); + assertEquals(inlierMean, alg.getModelParameters()[0], parameterTol); + } + + /** + * Multiple data sets are processed in an attempt to see if it is properly reinitializing + * itself and returns the correct solutions. + */ + @Test + public void runMultipleTimes() { + double mean = 2.5; + double tol = 0.2; + + // generate the points with a smaller tolerance to account for fitting error + // later on. + ModelMatcher alg = createModel(4, tol); + + for (int i = 0; i < 10; i++) { + // try different sample sizes in each trial. a bug was found once where + // a small value of N than previous caused a problem + int N = 200 - i*10; + List samples = createSampleSet(N, mean, tol*0.90, 0.1); + + assertTrue(alg.process(samples)); + + List matchSet = alg.getMatchSet(); + + double foundMean = alg.getModelParameters()[0]; + + if (checkInlierSet) + assertTrue(matchSet.size()/(N*0.9) >= minMatchFrac); + assertEquals(inlierMean, foundMean, parameterTol); + } + } + + /** + * Creates a set of sample points that are part of the model and some outliers + * + * @param numPoints Number of sample points it will generate + * @param mean Mean of the distribution + * @param modelDist How close to the model do the points need to be. + * @param fracOutlier Fraction of the points which will be outliers. + * @return Set of sample points + */ + protected List createSampleSet( int numPoints, double mean, double modelDist, double fracOutlier ) { + List ret = new ArrayList<>(); + + double numOutlier = (int)(numPoints*fracOutlier); + + inlierMean = 0; + + for (int i = 0; i < numPoints - numOutlier; i++) { + double d = mean + (rand.nextDouble() - 0.5)*2.0*modelDist; + + inlierMean += d; + + ret.add(d); + } + + inlierMean /= ret.size(); + + while (ret.size() < numPoints) { + double d = (rand.nextDouble() - 0.5)*200*modelDist; + + // add a point if its sufficiently far away from the model + if (Math.abs(d) > modelDist*10) { + ret.add(d); + } + } + + // randomize the order + Collections.shuffle(ret, rand); + + return ret; + } + + /** + * Make sure the function getInputIndex() returns the original index + */ + @Test + public void checkMatchSetToInputIndex() { + double mean = 2.5; + double tol = 0.2; + + // generate the points with a smaller tolerance to account for fitting error + // later on. + ModelMatcher alg = createModel(4, tol*0.95); + + List samples = createSampleSet(100, mean, tol, 0.1); + + assertTrue(alg.process(samples)); + + List matchSet = alg.getMatchSet(); + + // sanity check to make sure there is a large enough match set + assertTrue(matchSet.size() > 20); + + int orderNotTheSame = 0; + for (int i = 0; i < matchSet.size(); i++) { + int expected = samples.indexOf(matchSet.get(i)); + int found = alg.getInputIndex(i); + + if (found != i) + orderNotTheSame++; + + assertEquals(expected, found); + } + + // sanity check to make sure the order has been changed + assertTrue(orderNotTheSame != matchSet.size()); + } + + /** + * Make sure that if reset is called it produces identical results + */ + @Test + public void reset() { + double mean = 2.5; + double tol = 0.2; + + // generate the points with a smaller tolerance to account for fitting error + // later on. + ModelMatcher alg = createModel(1, 0.6); + + // Create a uniform sample with multiple just as good solutions so it's unlikely that two random + // draws will get the exact same solution + List samples = new ArrayList<>(); + for (int i = 0; i < 500; i++) { + samples.add(mean - (0.5 - i/500.0)*2.0); + } + + assertTrue(alg.process(samples)); + List matchesA = new ArrayList<>(alg.getMatchSet()); + + assertTrue(alg.process(samples)); + List matchesB = new ArrayList<>(alg.getMatchSet()); + + // See if this produces different results + boolean matched = matchesA.size() == matchesB.size(); + if (matched) { + for (int i = 0; i < matchesA.size(); i++) { + if (!matchesA.get(i).equals(matchesB.get(i))) { + matched = false; + break; + } + } + } + assertEquals(deterministic, matched); + + if (deterministic) + return; + + // It should now produce identical results to the first run + alg.reset(); + assertTrue(alg.process(samples)); + List matchesC = new ArrayList<>(alg.getMatchSet()); + assertEquals(matchesA.size(), matchesC.size()); + for (int i = 0; i < matchesA.size(); i++) { + assertEquals(matchesA.get(i), matchesC.get(i)); + } + } + + protected ModelMatcherPost createModel( int minPoints, double fitThreshold ) { + DoubleArrayManager manager = new DoubleArrayManager(1); + + ModelMatcherPost alg = createModelMatcher(manager, minPoints, fitThreshold); + alg.setModel(MeanModelFitter::new, DistanceFromMeanModel::new); + return alg; + } + + public abstract ModelMatcherPost createModelMatcher( + ModelManager manager, + int minPoints, double fitThreshold ); +} diff -Nru ddogleg-0.18+ds/test/org/ddogleg/fitting/modelset/GenericModelMatcherTests.java ddogleg-0.22+ds/test/org/ddogleg/fitting/modelset/GenericModelMatcherTests.java --- ddogleg-0.18+ds/test/org/ddogleg/fitting/modelset/GenericModelMatcherTests.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/fitting/modelset/GenericModelMatcherTests.java 2022-09-01 02:18:09.000000000 +0000 @@ -29,7 +29,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; - /** * A series of tests are provided that test to see the provided model set algorithm has the expected * behavior. This includes removing outliers and estimating the model parameters. The test @@ -55,7 +54,7 @@ // If the model matcher is deterministic and doesn't use any random numbers protected boolean deterministic = false; - protected void configure(double minMatchFrac, double parameterTol, boolean checkInlierSet) { + protected void configure( double minMatchFrac, double parameterTol, boolean checkInlierSet ) { this.minMatchFrac = minMatchFrac; this.parameterTol = parameterTol; this.checkInlierSet = checkInlierSet; @@ -72,7 +71,7 @@ // generate the points with a smaller tolerance to account for fitting error // later on. - ModelMatcher alg = createModel(4, tol * 0.95); + ModelMatcher alg = createModel(4, tol*0.95); List samples = createSampleSet(100, mean, tol, 0.1); @@ -81,7 +80,7 @@ List matchSet = alg.getMatchSet(); if (checkInlierSet) - assertTrue(matchSet.size() / 90.0 >= minMatchFrac); + assertTrue(matchSet.size()/90.0 >= minMatchFrac); assertEquals(inlierMean, alg.getModelParameters()[0], parameterTol); } @@ -96,13 +95,13 @@ // generate the points with a smaller tolerance to account for fitting error // later on. - ModelMatcher alg = createModel(4, tol); + ModelMatcher alg = createModel(4, tol); for (int i = 0; i < 10; i++) { // try different sample sizes in each trial. a bug was found once where // a small value of N than previous caused a problem - int N = 200 - i * 10; - List samples = createSampleSet(N, mean, tol * 0.90, 0.1); + int N = 200 - i*10; + List samples = createSampleSet(N, mean, tol*0.90, 0.1); assertTrue(alg.process(samples)); @@ -111,7 +110,7 @@ double foundMean = alg.getModelParameters()[0]; if (checkInlierSet) - assertTrue(matchSet.size() / (N * 0.9) >= minMatchFrac); + assertTrue(matchSet.size()/(N*0.9) >= minMatchFrac); assertEquals(inlierMean, foundMean, parameterTol); } } @@ -120,20 +119,20 @@ * Creates a set of sample points that are part of the model and some outliers * * @param numPoints Number of sample points it will generate - * @param mean Mean of the distribution - * @param modelDist How close to the model do the points need to be. + * @param mean Mean of the distribution + * @param modelDist How close to the model do the points need to be. * @param fracOutlier Fraction of the points which will be outliers. * @return Set of sample points */ - private List createSampleSet(int numPoints, double mean, double modelDist, double fracOutlier) { + protected List createSampleSet( int numPoints, double mean, double modelDist, double fracOutlier ) { List ret = new ArrayList<>(); - double numOutlier = (int) (numPoints * fracOutlier); + double numOutlier = (int)(numPoints*fracOutlier); inlierMean = 0; for (int i = 0; i < numPoints - numOutlier; i++) { - double d = mean + (rand.nextDouble() - 0.5) * 2.0 * modelDist; + double d = mean + (rand.nextDouble() - 0.5)*2.0*modelDist; inlierMean += d; @@ -143,10 +142,10 @@ inlierMean /= ret.size(); while (ret.size() < numPoints) { - double d = (rand.nextDouble() - 0.5) * 200 * modelDist; + double d = (rand.nextDouble() - 0.5)*200*modelDist; // add a point if its sufficiently far away from the model - if (Math.abs(d) > modelDist * 10) { + if (Math.abs(d) > modelDist*10) { ret.add(d); } } @@ -167,7 +166,7 @@ // generate the points with a smaller tolerance to account for fitting error // later on. - ModelMatcher alg = createModel(4, tol * 0.95); + ModelMatcher alg = createModel(4, tol*0.95); List samples = createSampleSet(100, mean, tol, 0.1); @@ -179,11 +178,11 @@ assertTrue(matchSet.size() > 20); int orderNotTheSame = 0; - for( int i = 0; i < matchSet.size(); i++ ) { + for (int i = 0; i < matchSet.size(); i++) { int expected = samples.indexOf(matchSet.get(i)); int found = alg.getInputIndex(i); - if( found != i ) + if (found != i) orderNotTheSame++; assertEquals(expected, found); @@ -199,17 +198,16 @@ @Test public void reset() { double mean = 2.5; - double tol = 0.2; // generate the points with a smaller tolerance to account for fitting error // later on. - ModelMatcher alg = createModel(1, tol * 0.95); - - List samples = createSampleSet(500, mean, tol, 0.0); + ModelMatcher alg = createModel(1, 0.6); - // add noise so it's ambiguous - for (int i = 0; i < samples.size(); i++) { - samples.set(i, samples.get(i)+rand.nextDouble()*0.3); + // Create a uniform sample with multiple just as good solutions so it's unlikely that two random + // draws will get the exact same solution + List samples = new ArrayList<>(); + for (int i = 0; i < 500; i++) { + samples.add(mean - (0.5 - i/500.0)*2.0); } assertTrue(alg.process(samples)); @@ -220,44 +218,41 @@ // See if this produces different results boolean matched = matchesA.size() == matchesB.size(); - if( matched ) { + if (matched) { for (int i = 0; i < matchesA.size(); i++) { - if(!matchesA.get(i).equals(matchesB.get(i))) { + if (!matchesA.get(i).equals(matchesB.get(i))) { matched = false; break; } } } - assertEquals(deterministic,matched); + assertEquals(deterministic, matched); - if( deterministic ) + if (deterministic) return; // It should now produce identical results to the first run alg.reset(); assertTrue(alg.process(samples)); List matchesC = new ArrayList<>(alg.getMatchSet()); - assertEquals(matchesA.size(),matchesC.size()); + assertEquals(matchesA.size(), matchesC.size()); for (int i = 0; i < matchesA.size(); i++) { assertEquals(matchesA.get(i), matchesC.get(i)); } - } - private ModelMatcher createModel(int minPoints, double fitThreshold) { + protected ModelMatcher createModel( int minPoints, double fitThreshold ) { DoubleArrayManager manager = new DoubleArrayManager(1); DistanceFromMeanModel dist = new DistanceFromMeanModel(); MeanModelFitter fitter = new MeanModelFitter(); - return createModelMatcher(manager,dist, fitter,fitter, minPoints, fitThreshold); + return createModelMatcher(manager, dist, fitter, fitter, minPoints, fitThreshold); } - public abstract ModelMatcher createModelMatcher( + public abstract ModelMatcher createModelMatcher( ModelManager manager, - DistanceFromModel distance, - ModelGenerator generator, - ModelFitter fitter, - int minPoints, double fitThreshold); - - + DistanceFromModel distance, + ModelGenerator generator, + ModelFitter fitter, + int minPoints, double fitThreshold ); } diff -Nru ddogleg-0.18+ds/test/org/ddogleg/fitting/modelset/lmeds/TestLeastMedianOfSquares.java ddogleg-0.22+ds/test/org/ddogleg/fitting/modelset/lmeds/TestLeastMedianOfSquares.java --- ddogleg-0.18+ds/test/org/ddogleg/fitting/modelset/lmeds/TestLeastMedianOfSquares.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/fitting/modelset/lmeds/TestLeastMedianOfSquares.java 2022-09-01 02:18:09.000000000 +0000 @@ -18,24 +18,24 @@ package org.ddogleg.fitting.modelset.lmeds; -import org.ddogleg.fitting.modelset.*; +import org.ddogleg.fitting.modelset.GenericModelMatcherPostTests; +import org.ddogleg.fitting.modelset.ModelManager; +import org.ddogleg.fitting.modelset.ModelMatcherPost; /** * @author Peter Abeles */ -public class TestLeastMedianOfSquares extends GenericModelMatcherTests { +public class TestLeastMedianOfSquares extends GenericModelMatcherPostTests { public TestLeastMedianOfSquares() { configure(0.9, 0.1, false); } @Override - public ModelMatcher createModelMatcher(ModelManager manager, - DistanceFromModel distance, - ModelGenerator generator, - ModelFitter fitter, - int minPoints, double fitThreshold) { - return new LeastMedianOfSquares<>(4234,50,fitThreshold,0.9,manager,generator,distance); + public ModelMatcherPost createModelMatcher( ModelManager manager, + int minPoints, + double fitThreshold ) { + return new LeastMedianOfSquares<>(4234,50,fitThreshold,0.9,manager, Double.class); } } diff -Nru ddogleg-0.18+ds/test/org/ddogleg/fitting/modelset/lmeds/TestLeastMedianOfSquares_MT.java ddogleg-0.22+ds/test/org/ddogleg/fitting/modelset/lmeds/TestLeastMedianOfSquares_MT.java --- ddogleg-0.18+ds/test/org/ddogleg/fitting/modelset/lmeds/TestLeastMedianOfSquares_MT.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/fitting/modelset/lmeds/TestLeastMedianOfSquares_MT.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.fitting.modelset.lmeds; + +import org.ddogleg.fitting.modelset.*; +import org.ddogleg.fitting.modelset.distance.DistanceFromMeanModel; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * @author Peter Abeles + */ +class TestLeastMedianOfSquares_MT extends GenericModelMatcherPostTests { + int numTrials = 50; + + public TestLeastMedianOfSquares_MT() { + configure(0.9, 0.1, false); + } + + @Override + public ModelMatcherPost createModelMatcher( ModelManager manager, + int minPoints, + double fitThreshold ) { + return new LeastMedianOfSquares_MT<>(4234,numTrials,fitThreshold,0.9,manager, Double.class); + } + + protected ModelMatcherPost createModelSingle( int minPoints, double fitThreshold ) { + DoubleArrayManager manager = new DoubleArrayManager(1); + + var alg = new LeastMedianOfSquares_MT<>(4234,numTrials, fitThreshold, 0.9, manager, Double.class); + alg.setModel(MeanModelFitter::new, DistanceFromMeanModel::new); + return alg; + } + + @Test void compareToSingleThread() { + double mean = 2.5; + double tol = 0.2; + + numTrials = 200; + + ModelMatcherPost multi = createModel(4, tol); + ModelMatcherPost single = createModelSingle(4, tol); + + for (int trial = 0; trial < 10; trial++) { + // try different sample sizes in each trial. a bug was found once where + // a small value of N than previous caused a problem + int N = 500; + List samples = createSampleSet(N, mean, tol*0.90, 0.1); + + assertTrue(multi.process(samples)); + assertTrue(single.process(samples)); + + assertEquals(single.getFitQuality(), multi.getFitQuality()); + + List expected = single.getMatchSet(); + List found = multi.getMatchSet(); + assertEquals(expected.size(), found.size()); + for (int i = 0; i < expected.size(); i++) { + assertEquals(expected.get(i), found.get(i)); + assertEquals(single.getInputIndex(i), multi.getInputIndex(i)); + } + + assertArrayEquals(single.getModelParameters(), multi.getModelParameters(), 1e-16); + } + } +} \ No newline at end of file diff -Nru ddogleg-0.18+ds/test/org/ddogleg/fitting/modelset/MeanModelFitter.java ddogleg-0.22+ds/test/org/ddogleg/fitting/modelset/MeanModelFitter.java --- ddogleg-0.18+ds/test/org/ddogleg/fitting/modelset/MeanModelFitter.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/fitting/modelset/MeanModelFitter.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -18,24 +18,23 @@ package org.ddogleg.fitting.modelset; -import java.util.List; +import org.jetbrains.annotations.Nullable; +import java.util.List; /** * Computes the mean of a set of points. * * @author Peter Abeles */ -public class MeanModelFitter implements ModelFitter , - ModelGenerator{ - +public class MeanModelFitter implements ModelFitter, ModelGenerator { @Override - public boolean generate(List dataSet, double[] param ) { - return fitModel(dataSet,null,param); + public boolean generate( List dataSet, double[] param ) { + return fitModel(dataSet, null, param); } @Override - public boolean fitModel(List dataSet, double[] initParam, double[] foundParam) { + public boolean fitModel( List dataSet, @Nullable double[] initParam, double[] foundParam ) { double mean = 0; for (double d : dataSet) { diff -Nru ddogleg-0.18+ds/test/org/ddogleg/fitting/modelset/MultipleToSingle.java ddogleg-0.22+ds/test/org/ddogleg/fitting/modelset/MultipleToSingle.java --- ddogleg-0.18+ds/test/org/ddogleg/fitting/modelset/MultipleToSingle.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/fitting/modelset/MultipleToSingle.java 2022-09-01 02:18:09.000000000 +0000 @@ -25,6 +25,7 @@ * * @author Peter Abeles */ +@SuppressWarnings({"NullAway.Init"}) public class MultipleToSingle implements ModelMatcher { ModelMatcherMulti alg; diff -Nru ddogleg-0.18+ds/test/org/ddogleg/fitting/modelset/RandomDistanceModel.java ddogleg-0.22+ds/test/org/ddogleg/fitting/modelset/RandomDistanceModel.java --- ddogleg-0.18+ds/test/org/ddogleg/fitting/modelset/RandomDistanceModel.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/fitting/modelset/RandomDistanceModel.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2016, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -21,36 +21,36 @@ import java.util.List; import java.util.Random; - /** * Returns a random distance * * @author Peter Abeles */ -public class RandomDistanceModel implements DistanceFromModel { +@SuppressWarnings({"NullAway"}) +public class RandomDistanceModel implements DistanceFromModel { Random rand = new Random(234); double errorMagnitude; double errorMinimum; - public RandomDistanceModel(double errorMagnitude, double errorMinimum) { + public RandomDistanceModel( double errorMagnitude, double errorMinimum ) { this.errorMagnitude = errorMagnitude; this.errorMinimum = errorMinimum; } @Override - public void setModel(double[] param) { + public void setModel( double[] param ) { } @Override - public double computeDistance(Double pt) { - return rand.nextDouble()*errorMagnitude+errorMinimum; + public double distance( Double pt ) { + return rand.nextDouble()*errorMagnitude + errorMinimum; } @Override - public void computeDistance(List points, double[] distance) { + public void distances( List points, double[] distance ) { for (int i = 0; i < points.size(); i++) { - distance[i] = computeDistance(null); + distance[i] = distance(null); } } diff -Nru ddogleg-0.18+ds/test/org/ddogleg/fitting/modelset/ransac/TestRansac.java ddogleg-0.22+ds/test/org/ddogleg/fitting/modelset/ransac/TestRansac.java --- ddogleg-0.18+ds/test/org/ddogleg/fitting/modelset/ransac/TestRansac.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/fitting/modelset/ransac/TestRansac.java 2022-09-01 02:18:09.000000000 +0000 @@ -19,33 +19,30 @@ package org.ddogleg.fitting.modelset.ransac; import org.ddogleg.fitting.modelset.*; +import org.ddogleg.struct.DogArray_I32; import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import static org.junit.jupiter.api.Assertions.*; - /** * @author Peter Abeles */ -public class TestRansac extends GenericModelMatcherTests { +public class TestRansac extends GenericModelMatcherPostTests { public TestRansac() { configure(0.9, 0.05, true); } @Override - public ModelMatcher createModelMatcher(ModelManager manager, - DistanceFromModel distance, - ModelGenerator generator, - ModelFitter fitter, - int minPoints, - double fitThreshold) { - Ransac ret = new Ransac(344, manager,generator, distance, 50, fitThreshold); + public ModelMatcherPost createModelMatcher( ModelManager manager, + int minPoints, + double fitThreshold ) { + Ransac ret = new Ransac<>(344, 50, fitThreshold, manager, Double.class); ret.setSampleSize(minPoints); - return ret; } @@ -56,18 +53,18 @@ @SuppressWarnings({"NumberEquality"}) @Test public void randomDraw() { - randomDraw_sanity(200,15); - randomDraw_sanity(200,150); + randomDraw_sanity(200, 15); + randomDraw_sanity(200, 150); } - private void randomDraw_sanity( int total , int numSample ) { - List dataSet = new ArrayList(); + private void randomDraw_sanity( int total, int numSample ) { + List dataSet = new ArrayList<>(); for (int i = 0; i < total; i++) { dataSet.add(i); } - List initSet = new ArrayList(); + List initSet = new ArrayList<>(); Ransac.randomDraw(dataSet, numSample, initSet, rand); assertEquals(numSample, initSet.size()); @@ -80,7 +77,7 @@ assertTrue(dataSet.contains(o)); // make sure the order has been changed - assertTrue(dataSet.get(i) != o); + assertNotSame(dataSet.get(i), o); // make sure the order has been changed if (o == i) @@ -88,14 +85,14 @@ // make sure only one copy is in the init set for (int j = i + 1; j < initSet.size(); j++) { - if (o == initSet.get(j)) { + if (o.equals(initSet.get(j))) { fail("Multiple copies in initSet"); } } } // if the order has been randomized then very few should be in the original order - assertTrue(numTheSame < initSet.size() * 0.9); + assertTrue(numTheSame < initSet.size()*0.9); // call get init set once more and see if it was cleared Ransac.randomDraw(dataSet, numSample, initSet, rand); @@ -107,30 +104,65 @@ */ @Test public void randomDraw_Histogram() { - List dataSet = new ArrayList(); + List dataSet = new ArrayList<>(); for (int i = 0; i < 30; i++) { dataSet.add(i); } - int histogram[] = new int[ dataSet.size() ]; + int[] histogram = new int[dataSet.size()]; - List selected = new ArrayList(); + List selected = new ArrayList<>(); int numTrials = 10000; for (int i = 0; i < numTrials; i++) { Ransac.randomDraw(dataSet, 3, selected, rand); for (int j = 0; j < selected.size(); j++) { - histogram[ selected.get(j)]++; + histogram[selected.get(j)]++; } } double expected = (3.0/30.0)*numTrials; for (int i = 0; i < histogram.length; i++) { - assertTrue( Math.abs(histogram[i]-expected)/expected < 0.1 ); + assertTrue(Math.abs(histogram[i] - expected)/expected < 0.1); + } + } + + /** + * Checks the histogram of selected items to see if it is a uniformly random distribution + */ + @Test + public void randomDraw_integer_Histogram() { + DogArray_I32 selectedIdx = new DogArray_I32(); + + List dataSet = new ArrayList<>(); + for (int i = 0; i < 30; i++) { + dataSet.add(i); + } + int[] histogram = new int[dataSet.size()]; + + List selected = new ArrayList<>(); + + int numTrials = 10000; + for (int i = 0; i < numTrials; i++) { + Ransac.randomDraw(selectedIdx, dataSet.size(), 3, rand); + Ransac.addSelect(selectedIdx, 3, dataSet, selected); + + for (int idx = 0; idx < selectedIdx.size; idx++) { + assertEquals(idx, selectedIdx.get(idx)); + } + + for (int j = 0; j < selected.size(); j++) { + histogram[selected.get(j)]++; + } } + double expected = (3.0/30.0)*numTrials; + + for (int i = 0; i < histogram.length; i++) { + assertTrue(Math.abs(histogram[i] - expected)/expected < 0.1); + } } /** @@ -140,49 +172,71 @@ public void selectMatchSet() { double modelVal = 50; - List dataSet = new ArrayList(); + List dataSet = new ArrayList<>(); for (int i = 0; i < 200; i++) { dataSet.add(i); } - - DebugModelStuff stuff = new DebugModelStuff((int) modelVal); - Ransac ransac = new Ransac(234,stuff,stuff,stuff,20,1); + Ransac ransac = new Ransac<>(234, 20, 1, new DebugModelStuff((int)modelVal), Integer.class); + ransac.setModel(()-> new DebugModelStuff((int)modelVal), ()-> new DebugModelStuff((int)modelVal)); ransac.setSampleSize(5); // declare the array so it doesn't blow up when accessed - ransac.matchToInput = new int[ dataSet.size()]; - double param[] = new double[]{modelVal}; + Objects.requireNonNull(ransac.helper).matchToInput = new int[dataSet.size()]; + double[] param = new double[]{modelVal}; - ransac.selectMatchSet(dataSet, 4, param); + assertTrue(ransac.helper.selectMatchSet(dataSet, 0, 4, param)); - assertTrue(ransac.candidatePoints.size() == 7); + assertEquals(ransac.helper.candidatePoints.size(), 7); } - public static class DebugModelStuff implements - ModelManager,DistanceFromModel, ModelGenerator { + /** + * Checks to see if it will abort if it can't possibly beat the best model + */ + @Test + public void selectMatchSet_abort() { + double modelVal = 50; - int threshold; + List dataSet = new ArrayList<>(); - double error; + for (int i = 0; i < 200; i++) { + dataSet.add(i); + } + + Ransac ransac = new Ransac<>(234, 20, 1, new DebugModelStuff((int)modelVal), Integer.class); + ransac.setModel(()-> new DebugModelStuff((int)modelVal), ()-> new DebugModelStuff((int)modelVal)); + ransac.setSampleSize(5); + // declare the array so it doesn't blow up when accessed + Objects.requireNonNull(ransac.helper).matchToInput = new int[dataSet.size()]; + double[] param = new double[]{modelVal}; + + assertFalse(ransac.helper.selectMatchSet(dataSet, 150, 4, param)); + } + @SuppressWarnings({"NullAway"}) + public static class DebugModelStuff + implements ModelManager, + DistanceFromModel, + ModelGenerator { + int threshold; + double error; double[] param; - public DebugModelStuff(int threshold) { + public DebugModelStuff( int threshold ) { this.threshold = threshold; } @Override - public void setModel(double[] param) { + public void setModel( double[] param ) { this.param = param; } @Override - public double computeDistance(Integer pt) { + public double distance( Integer pt ) { return Math.abs(pt - param[0]); } @Override - public void computeDistance(List points, double[] distance) { + public void distances( List points, double[] distance ) { throw new RuntimeException("Why was this called?"); } @@ -202,16 +256,16 @@ } @Override - public void copyModel(double[] src, double[] dst) { - System.arraycopy(src,0,dst,0,1); + public void copyModel( double[] src, double[] dst ) { + System.arraycopy(src, 0, dst, 0, 1); } @Override - public boolean generate(List dataSet, double[] p) { + public boolean generate( List dataSet, double[] p ) { error = 0; - int offset = (int) p[0]; + int offset = (int)p[0]; for (Integer a : dataSet) { if (a + offset >= threshold) { diff -Nru ddogleg-0.18+ds/test/org/ddogleg/fitting/modelset/ransac/TestRansac_MT.java ddogleg-0.22+ds/test/org/ddogleg/fitting/modelset/ransac/TestRansac_MT.java --- ddogleg-0.18+ds/test/org/ddogleg/fitting/modelset/ransac/TestRansac_MT.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/fitting/modelset/ransac/TestRansac_MT.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.fitting.modelset.ransac; + +import org.ddogleg.fitting.modelset.*; +import org.ddogleg.fitting.modelset.distance.DistanceFromMeanModel; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * @author Peter Abeles + */ +public class TestRansac_MT extends GenericModelMatcherPostTests { + + int numTrials = 50; + + public TestRansac_MT() { + configure(0.9, 0.05, true); + } + + @Override + public ModelMatcherPost createModelMatcher( ModelManager manager, + int minPoints, + double fitThreshold ) { + var ret = new Ransac_MT<>(344, 50, fitThreshold, manager, Double.class); + ret.setSampleSize(minPoints); + return ret; + } + + protected ModelMatcherPost createModelSingle( int minPoints, double fitThreshold ) { + var manager = new DoubleArrayManager(1); + var ret = new Ransac<>(344, 50, fitThreshold, manager, Double.class); + ret.setSampleSize(minPoints); + ret.setModel(MeanModelFitter::new, DistanceFromMeanModel::new); + return ret; + } + + @Test void compareToSingleThread() { + double mean = 2.5; + double tol = 0.2; + + numTrials = 200; + + ModelMatcherPost multi = createModel(4, tol); + ModelMatcherPost single = createModelSingle(4, tol); + + for (int trial = 0; trial < 10; trial++) { + // try different sample sizes in each trial. a bug was found once where + // a small value of N than previous caused a problem + int N = 500; + List samples = createSampleSet(N, mean, tol*0.90, 0.1); + + assertTrue(multi.process(samples)); + assertTrue(single.process(samples)); + + assertEquals(single.getFitQuality(), multi.getFitQuality()); + + List expected = single.getMatchSet(); + List found = multi.getMatchSet(); + assertEquals(expected.size(), found.size()); + for (int i = 0; i < expected.size(); i++) { + assertEquals(expected.get(i), found.get(i)); + assertEquals(single.getInputIndex(i), multi.getInputIndex(i)); + } + + assertArrayEquals(single.getModelParameters(), multi.getModelParameters(), 1e-16); + } + } +} diff -Nru ddogleg-0.18+ds/test/org/ddogleg/graph/TestGraphDataManager.java ddogleg-0.22+ds/test/org/ddogleg/graph/TestGraphDataManager.java --- ddogleg-0.18+ds/test/org/ddogleg/graph/TestGraphDataManager.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/graph/TestGraphDataManager.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -20,127 +20,124 @@ import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; /** * @author Peter Abeles */ public class TestGraphDataManager { - - @Test - public void reset() - { - GraphDataManager alg = new GraphDataManager(); - - Node n = alg.createNode(); - n.data = 1; - n.edges.grow(); - - Edge e = alg.createEdge(); - e.data = 1; - e.dest = n; - - alg.reset(); - - assertEquals(0,alg.usedEdges.size()); - assertEquals(1,alg.unusedEdges.size()); - assertEquals(0,alg.usedNodes.size()); - assertEquals(1,alg.unusedNodes.size()); - - assertTrue(((Edge)alg.unusedEdges.get(0)).data != null); - assertTrue(((Edge)alg.unusedEdges.get(0)).dest != null); - assertTrue(((Node)alg.unusedNodes.get(0)).data != null); - assertTrue(((Node)alg.unusedNodes.get(0)).edges.size != 0 ); - } - - @Test - public void resetHard() - { - GraphDataManager alg = new GraphDataManager(); - - Node n = alg.createNode(); - n.data = 1; - n.edges.grow(); - - Edge e = alg.createEdge(); - e.data = 1; - e.dest = n; - - alg.resetHard(); - - assertEquals(0,alg.usedEdges.size()); - assertEquals(1,alg.unusedEdges.size()); - assertEquals(0,alg.usedNodes.size()); - assertEquals(1,alg.unusedNodes.size()); - - assertTrue(((Edge)alg.unusedEdges.get(0)).data == null); - assertTrue(((Edge)alg.unusedEdges.get(0)).dest == null); - assertTrue(((Node)alg.unusedNodes.get(0)).data == null); - assertTrue(((Node)alg.unusedNodes.get(0)).edges.size == 0 ); - } - - @Test - public void createEdge() - { - GraphDataManager alg = new GraphDataManager(); - - Edge e = alg.createEdge(); - assertEquals(1,alg.usedEdges.size()); - assertEquals(0,alg.unusedEdges.size()); - - alg.reset(); - - assertTrue( e == alg.createEdge() ); - assertEquals(1,alg.usedEdges.size()); - assertEquals(0,alg.unusedEdges.size()); - } - - @Test - public void recycleEdge() - { - GraphDataManager alg = new GraphDataManager(); - - Edge e = alg.createEdge(); - assertEquals(1,alg.usedEdges.size()); - assertEquals(0,alg.unusedEdges.size()); - - alg.recycleEdge(e); - - assertEquals(0,alg.usedEdges.size()); - assertEquals(1,alg.unusedEdges.size()); - } - - @Test - public void createNode() - { - GraphDataManager alg = new GraphDataManager(); - - Node e = alg.createNode(); - assertEquals(1,alg.usedNodes.size()); - assertEquals(0,alg.unusedNodes.size()); - - alg.recycleNode(e); - - assertEquals(0,alg.usedNodes.size()); - assertEquals(1, alg.unusedNodes.size()); - } - - @Test - public void recycleNode() - { - GraphDataManager alg = new GraphDataManager(); - - Node n = alg.createNode(); - assertEquals(1,alg.usedNodes.size()); - assertEquals(0,alg.unusedNodes.size()); - - alg.reset(); - - assertTrue( n == alg.createNode() ); - assertEquals(1,alg.usedNodes.size()); - assertEquals(0, alg.unusedNodes.size()); - } - + @Test + void reset() + { + GraphDataManager alg = new GraphDataManager(); + + Node n = alg.createNode(); + n.data = 1; + n.edges.grow(); + + Edge e = alg.createEdge(); + e.data = 1; + e.dest = n; + + alg.reset(); + + assertEquals(0,alg.usedEdges.size()); + assertEquals(1,alg.unusedEdges.size()); + assertEquals(0,alg.usedNodes.size()); + assertEquals(1,alg.unusedNodes.size()); + + assertNotNull(((Edge) alg.unusedEdges.getFirst()).data); + assertNotNull(((Edge) alg.unusedEdges.getFirst()).dest); + assertNotNull(((Node) alg.unusedNodes.getFirst()).data); + assertTrue(((Node)alg.unusedNodes.getFirst()).edges.size != 0 ); + } + + @Test + void resetHard() + { + GraphDataManager alg = new GraphDataManager(); + + Node n = alg.createNode(); + n.data = 1; + n.edges.grow(); + + Edge e = alg.createEdge(); + e.data = 1; + e.dest = n; + + alg.resetHard(); + + assertEquals(0,alg.usedEdges.size()); + assertEquals(1,alg.unusedEdges.size()); + assertEquals(0,alg.usedNodes.size()); + assertEquals(1,alg.unusedNodes.size()); + + assertNull(((Edge) alg.unusedEdges.getFirst()).data); + assertNull(((Edge) alg.unusedEdges.getFirst()).dest); + assertNull(((Node) alg.unusedNodes.getFirst()).data); + assertEquals(((Node) alg.unusedNodes.getFirst()).edges.size, 0); + } + + @Test + void createEdge() + { + GraphDataManager alg = new GraphDataManager<>(); + + Edge e = alg.createEdge(); + assertEquals(1,alg.usedEdges.size()); + assertEquals(0,alg.unusedEdges.size()); + + alg.reset(); + + assertSame(e, alg.createEdge()); + assertEquals(1,alg.usedEdges.size()); + assertEquals(0,alg.unusedEdges.size()); + } + + @Test + void recycleEdge() + { + GraphDataManager alg = new GraphDataManager(); + + Edge e = alg.createEdge(); + assertEquals(1,alg.usedEdges.size()); + assertEquals(0,alg.unusedEdges.size()); + + alg.recycleEdge(e); + + assertEquals(0,alg.usedEdges.size()); + assertEquals(1,alg.unusedEdges.size()); + } + + @Test + void createNode() + { + GraphDataManager alg = new GraphDataManager(); + + Node e = alg.createNode(); + assertEquals(1,alg.usedNodes.size()); + assertEquals(0,alg.unusedNodes.size()); + + alg.recycleNode(e); + + assertEquals(0,alg.usedNodes.size()); + assertEquals(1, alg.unusedNodes.size()); + } + + @Test + void recycleNode() + { + GraphDataManager alg = new GraphDataManager(); + + Node n = alg.createNode(); + assertEquals(1,alg.usedNodes.size()); + assertEquals(0,alg.unusedNodes.size()); + + alg.reset(); + + assertSame(n, alg.createNode()); + assertEquals(1,alg.usedNodes.size()); + assertEquals(0, alg.unusedNodes.size()); + } } diff -Nru ddogleg-0.18+ds/test/org/ddogleg/nn/alg/searches/StandardKdTreeSearch1Tests.java ddogleg-0.22+ds/test/org/ddogleg/nn/alg/searches/StandardKdTreeSearch1Tests.java --- ddogleg-0.18+ds/test/org/ddogleg/nn/alg/searches/StandardKdTreeSearch1Tests.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/nn/alg/searches/StandardKdTreeSearch1Tests.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -29,6 +29,7 @@ * * @author Peter Abeles */ +@SuppressWarnings({"NullAway"}) public abstract class StandardKdTreeSearch1Tests { // DOF of a point diff -Nru ddogleg-0.18+ds/test/org/ddogleg/nn/alg/searches/StandardKdTreeSearchNTests.java ddogleg-0.22+ds/test/org/ddogleg/nn/alg/searches/StandardKdTreeSearchNTests.java --- ddogleg-0.18+ds/test/org/ddogleg/nn/alg/searches/StandardKdTreeSearchNTests.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/nn/alg/searches/StandardKdTreeSearchNTests.java 2022-09-01 02:18:09.000000000 +0000 @@ -22,9 +22,9 @@ import org.ddogleg.nn.alg.KdTreeResult; import org.ddogleg.nn.alg.KdTreeSearchN; import org.ddogleg.sorting.QuickSelect; -import org.ddogleg.struct.FastQueue; -import org.ddogleg.struct.GrowQueue_F64; -import org.ddogleg.struct.GrowQueue_I32; +import org.ddogleg.struct.DogArray; +import org.ddogleg.struct.DogArray_F64; +import org.ddogleg.struct.DogArray_I32; import org.junit.jupiter.api.Test; import java.util.ArrayList; @@ -38,10 +38,11 @@ * * @author Peter Abeles */ +@SuppressWarnings({"NullAway"}) public abstract class StandardKdTreeSearchNTests { public int N = 2; - FastQueue found = new FastQueue<>(KdTreeResult::new); + DogArray found = new DogArray<>(KdTreeResult::new); Random rand = new Random(234); @@ -67,22 +68,22 @@ found.reset(); alg.findNeighbor(new double[]{11, 8}, 1, found); assertEquals(1,found.size); - assertTrue(found.data[0].node == tree.root.right.right); + assertSame(found.data[0].node, tree.root.right.right); // the root will be the best found.reset(); alg.findNeighbor(new double[]{1.001, 1.99999}, 1, found); - assertTrue(found.data[0].node == tree.root); + assertSame(found.data[0].node, tree.root); // a point on the left branch will be a perfect fit found.reset(); alg.findNeighbor(new double[]{2, 0.8}, 1, found); - assertTrue(found.data[0].node == tree.root.left.right); + assertSame(found.data[0].node, tree.root.left.right); // a point way outside the tree's bounds found.reset(); alg.findNeighbor(new double[]{-10000, 0.5}, 1, found); - assertTrue(found.data[0].node == tree.root.left.left); + assertSame(found.data[0].node, tree.root.left.left); } /** @@ -99,7 +100,7 @@ // the first decision will be incorrect and it will need to back track found.reset(); alg.findNeighbor(new double[]{2, 3}, 1, found); - assertTrue(found.get(0).node == tree.root); + assertSame(found.get(0).node, tree.root); } @@ -113,7 +114,7 @@ KdTree tree = StandardKdTreeSearch1Tests.createTreeA(); setTree(alg,tree); - List data = new ArrayList(); + List data = new ArrayList<>(); flattenTree(tree.root,data); for( int i = 0; i < 100; i++ ) { @@ -163,11 +164,11 @@ found.reset(); alg.findNeighbor(new double[]{11, 8}, 2, found); assertEquals(1,found.size); - assertTrue(found.data[0].node == tree.root); + assertSame(found.data[0].node, tree.root); found.reset(); alg.findNeighbor(new double[]{2, 5}, 2, found); assertEquals(1,found.size); - assertTrue(found.data[0].node == tree.root); + assertSame(found.data[0].node, tree.root); } /** @@ -188,7 +189,7 @@ found.reset(); alg.findNeighbor(new double[]{1, 1.5}, 1, found); assertEquals(1, found.size); - assertTrue(found.data[0].node == tree.root); + assertSame(found.data[0].node, tree.root); } /** @@ -233,20 +234,11 @@ for( int i = 0; i < 3; i++ ) { double[] a = (double[])found.get(i).node.point; for( int j = i+1; j < 3; j++ ) { - assertTrue(found.get(j).node.point != a); + assertNotSame(found.get(j).node.point, a); } } } - private void checkContains( KdTree.Node node ) { - for( int i = 0; i < found.size; i++ ) { - if( found.data[i].node == node ) - return; - } - - fail("can't find"); - } - private void checkContains( double[] d ) { for( int i = 0; i < found.size; i++ ) { boolean identical = true; @@ -273,11 +265,11 @@ } private static List findNeighbors( List data , double[]target , double maxDistance , int maxN ) { - List ret = new ArrayList(); + List ret = new ArrayList<>(); - List found = new ArrayList(); - GrowQueue_F64 distances = new GrowQueue_F64(); - GrowQueue_I32 indexes = new GrowQueue_I32(); + List found = new ArrayList<>(); + DogArray_F64 distances = new DogArray_F64(); + DogArray_I32 indexes = new DogArray_I32(); for( int i = 0; i < data.size(); i++ ) { diff -Nru ddogleg-0.18+ds/test/org/ddogleg/nn/alg/searches/TestKdTreeSearchBestBinFirst.java ddogleg-0.22+ds/test/org/ddogleg/nn/alg/searches/TestKdTreeSearchBestBinFirst.java --- ddogleg-0.18+ds/test/org/ddogleg/nn/alg/searches/TestKdTreeSearchBestBinFirst.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/nn/alg/searches/TestKdTreeSearchBestBinFirst.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -22,11 +22,13 @@ import org.ddogleg.nn.alg.distance.KdTreeEuclideanSq_F64; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; /** * @author Peter Abeles */ +@SuppressWarnings({"NullAway"}) public class TestKdTreeSearchBestBinFirst { /** @@ -43,7 +45,7 @@ KdTree.Node found = alg.findClosest(new double[]{12,2}); // The first search from the root node is not counted. In that search it will traverse down to a leaf - assertTrue(found==tree.root.left.right); + assertSame(found, tree.root.left.right); } /** @@ -64,7 +66,7 @@ // make sure it searched some nodes besides the root ones assertTrue(alg.numNodesSearched>0); // the best node should be the root node in the second forest - assertTrue(found==forest[1].root); + assertSame(found, forest[1].root); } private static class BBF extends KdTreeSearchBestBinFirst { diff -Nru ddogleg-0.18+ds/test/org/ddogleg/nn/alg/searches/TestKdTreeSearchNBbf.java ddogleg-0.22+ds/test/org/ddogleg/nn/alg/searches/TestKdTreeSearchNBbf.java --- ddogleg-0.18+ds/test/org/ddogleg/nn/alg/searches/TestKdTreeSearchNBbf.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/nn/alg/searches/TestKdTreeSearchNBbf.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -29,6 +29,7 @@ /** * @author Peter Abeles */ +@SuppressWarnings({"NullAway"}) public class TestKdTreeSearchNBbf extends StandardKdTreeSearchNTests { @Override public KdTreeSearchN createAlg() { @@ -55,7 +56,7 @@ KdTree.Node found = alg.findNeighbor(new double[]{12, 2}); // The first search from the root node is not counted. In that search it will traverse down to a leaf - assertTrue(found==tree.root.left.right); + assertSame(found, tree.root.left.right); } /** @@ -63,7 +64,7 @@ */ @Test public void multiTreeSearch() { - KdTree forest[] = new KdTree[2]; + KdTree[] forest = new KdTree[2]; forest[0] = StandardKdTreeSearch1Tests.createTreeA(); forest[1] = new KdTree(2); forest[1].root = new KdTree.Node(new double[]{12,2}); diff -Nru ddogleg-0.18+ds/test/org/ddogleg/nn/alg/TestAxisSplitterMedian.java ddogleg-0.22+ds/test/org/ddogleg/nn/alg/TestAxisSplitterMedian.java --- ddogleg-0.18+ds/test/org/ddogleg/nn/alg/TestAxisSplitterMedian.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/nn/alg/TestAxisSplitterMedian.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -19,7 +19,7 @@ package org.ddogleg.nn.alg; import org.ddogleg.nn.alg.distance.KdTreeEuclideanSq_F64; -import org.ddogleg.struct.GrowQueue_I32; +import org.ddogleg.struct.DogArray_I32; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -37,8 +37,8 @@ List left = new ArrayList<>(); List right = new ArrayList<>(); - GrowQueue_I32 leftData = new GrowQueue_I32(); - GrowQueue_I32 rightData = new GrowQueue_I32(); + DogArray_I32 leftData = new DogArray_I32(); + DogArray_I32 rightData = new DogArray_I32(); KdTreeDistance distance = new KdTreeEuclideanSq_F64(2); @@ -109,7 +109,7 @@ @Test public void splitData_withData() { List points = createPoints(2, 1,2 , 3,5 , -3,4); - GrowQueue_I32 data = new GrowQueue_I32(); + DogArray_I32 data = new DogArray_I32(); for( int i = 0; i < points.size(); i++ ) data.add(i); @@ -123,9 +123,9 @@ assertEquals(1,alg.getSplitAxis(),1e-8); assertEquals(-3,alg.getSplitPoint()[0],1e-8); - assertTrue(data.get(2) == alg.getSplitIndex()); - assertTrue(data.get(0) == leftData.get(0)); - assertTrue(data.get(1) == rightData.get(0)); + assertEquals(alg.getSplitIndex(), data.get(2)); + assertEquals(leftData.get(0), data.get(0)); + assertEquals(rightData.get(0), data.get(1)); } /** @@ -151,8 +151,7 @@ @Test public void checkRuleSetCalled() { DummyRule rule = new DummyRule(2); - AxisSplitterMedian alg = new AxisSplitterMedian<>(distance,rule); - + new AxisSplitterMedian<>(distance,rule); assertTrue(rule.calledSetDimension); } diff -Nru ddogleg-0.18+ds/test/org/ddogleg/nn/alg/TestExhaustiveNeighbor.java ddogleg-0.22+ds/test/org/ddogleg/nn/alg/TestExhaustiveNeighbor.java --- ddogleg-0.18+ds/test/org/ddogleg/nn/alg/TestExhaustiveNeighbor.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/nn/alg/TestExhaustiveNeighbor.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -19,8 +19,8 @@ package org.ddogleg.nn.alg; import org.ddogleg.nn.alg.distance.KdTreeEuclideanSq_F64; -import org.ddogleg.struct.GrowQueue_F64; -import org.ddogleg.struct.GrowQueue_I32; +import org.ddogleg.struct.DogArray_F64; +import org.ddogleg.struct.DogArray_I32; import org.junit.jupiter.api.Test; import java.util.ArrayList; @@ -83,8 +83,8 @@ ExhaustiveNeighbor alg = new ExhaustiveNeighbor<>(distance); alg.setPoints(list); - GrowQueue_I32 outputIndex = new GrowQueue_I32(); - GrowQueue_F64 outputDistance = new GrowQueue_F64(); + DogArray_I32 outputIndex = new DogArray_I32(); + DogArray_F64 outputDistance = new DogArray_F64(); alg.findClosestN(new double[]{1, 2}, 10, 5, outputIndex, outputDistance); @@ -102,8 +102,8 @@ ExhaustiveNeighbor alg = new ExhaustiveNeighbor<>(distance); alg.setPoints(list); - GrowQueue_I32 outputIndex = new GrowQueue_I32(); - GrowQueue_F64 outputDistance = new GrowQueue_F64(); + DogArray_I32 outputIndex = new DogArray_I32(); + DogArray_F64 outputDistance = new DogArray_F64(); alg.findClosestN(new double[]{1, 2}, 10, 5, outputIndex, outputDistance); @@ -124,8 +124,8 @@ ExhaustiveNeighbor alg = new ExhaustiveNeighbor<>(distance); alg.setPoints(list); - GrowQueue_I32 outputIndex = new GrowQueue_I32(); - GrowQueue_F64 outputDistance = new GrowQueue_F64(); + DogArray_I32 outputIndex = new DogArray_I32(); + DogArray_F64 outputDistance = new DogArray_F64(); alg.findClosestN(new double[]{1, 2}, 0.1, 5, outputIndex, outputDistance); @@ -143,8 +143,8 @@ ExhaustiveNeighbor alg = new ExhaustiveNeighbor<>(distance); alg.setPoints(list); - GrowQueue_I32 outputIndex = new GrowQueue_I32(); - GrowQueue_F64 outputDistance = new GrowQueue_F64(); + DogArray_I32 outputIndex = new DogArray_I32(); + DogArray_F64 outputDistance = new DogArray_F64(); alg.findClosestN(new double[]{4.1, 4.9}, 10, 3, outputIndex, outputDistance); @@ -166,8 +166,8 @@ ExhaustiveNeighbor alg = new ExhaustiveNeighbor<>(distance); alg.setPoints(list); - GrowQueue_I32 outputIndex = new GrowQueue_I32(); - GrowQueue_F64 outputDistance = new GrowQueue_F64(); + DogArray_I32 outputIndex = new DogArray_I32(); + DogArray_F64 outputDistance = new DogArray_F64(); alg.findClosestN(new double[]{4.1, 4.9}, 10, 3, outputIndex, outputDistance); @@ -184,7 +184,7 @@ checkContains(3,outputIndex); } - private void checkContains( int value , GrowQueue_I32 list ) { + private void checkContains( int value , DogArray_I32 list ) { for( int i = 0; i < list.size; i++ ) { if( list.data[i] == value ) { return; diff -Nru ddogleg-0.18+ds/test/org/ddogleg/nn/alg/TestKdTreeConstructor.java ddogleg-0.22+ds/test/org/ddogleg/nn/alg/TestKdTreeConstructor.java --- ddogleg-0.18+ds/test/org/ddogleg/nn/alg/TestKdTreeConstructor.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/nn/alg/TestKdTreeConstructor.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -19,7 +19,8 @@ package org.ddogleg.nn.alg; import org.ddogleg.nn.alg.distance.KdTreeEuclideanSq_F64; -import org.ddogleg.struct.GrowQueue_I32; +import org.ddogleg.struct.DogArray_I32; +import org.jetbrains.annotations.Nullable; import org.junit.jupiter.api.Test; import java.util.ArrayList; @@ -30,6 +31,7 @@ /** * @author Peter Abeles */ +@SuppressWarnings({"NullAway"}) public class TestKdTreeConstructor { KdTreeDistance distance = new KdTreeEuclideanSq_F64(2); @@ -70,7 +72,7 @@ KdTreeConstructor alg = new KdTreeConstructor<>(new KdTreeMemory(),splitter); - KdTree.Node n = alg.computeBranch(new ArrayList<>(),new GrowQueue_I32()); + KdTree.Node n = alg.computeBranch(new ArrayList<>(),new DogArray_I32()); assertSame(n.point, splitter.splitPoint); assertEquals(n.split, splitter.splitAxis); @@ -88,7 +90,7 @@ KdTreeConstructor alg = new KdTreeConstructor<>(distance); List points = new ArrayList<>(); - GrowQueue_I32 data = new GrowQueue_I32(); + DogArray_I32 data = new DogArray_I32(); // empty lists should be null KdTree.Node n = new KdTree.Node(); @@ -136,8 +138,6 @@ assertTrue(tree.root.right.isLeaf()); } - - public static List createPoints( int dimen , double ...v ) { List ret = new ArrayList(); @@ -156,8 +156,8 @@ private DummySplitter createSplitter( int numLeft , int numRight , boolean withData) { List left = new ArrayList<>(); List right = new ArrayList<>(); - GrowQueue_I32 leftData = null; - GrowQueue_I32 rightData = null; + DogArray_I32 leftData = null; + DogArray_I32 rightData = null; for( int i = 0; i < numLeft; i++ ) { left.add( new double[2] ); @@ -167,8 +167,8 @@ } if( withData ) { - leftData = new GrowQueue_I32(); - rightData = new GrowQueue_I32(); + leftData = new DogArray_I32(); + rightData = new DogArray_I32(); for( int i = 0; i < numLeft; i++ ) { leftData.add( i ); @@ -178,7 +178,6 @@ } } - return new DummySplitter(-2,null,1,left,leftData,right,rightData); } @@ -188,13 +187,14 @@ double[] splitPoint; int splitAxis; List left; - GrowQueue_I32 leftIndex; + DogArray_I32 leftIndex; List right; - GrowQueue_I32 rightIndex; + DogArray_I32 rightIndex; - public DummySplitter(int splitIndex, double[] splitPoint, int splitAxis, - List left, GrowQueue_I32 leftIndex, - List right, GrowQueue_I32 rightIndex) + public DummySplitter( int splitIndex, double[] splitPoint, + int splitAxis, List left, + DogArray_I32 leftIndex, List right, + DogArray_I32 rightIndex) { this.splitIndex = splitIndex; this.splitPoint = splitPoint; @@ -206,9 +206,9 @@ } @Override - public void splitData(List points, GrowQueue_I32 data, - List left, GrowQueue_I32 leftData, - List right, GrowQueue_I32 rightData) + public void splitData(List points, @Nullable DogArray_I32 data, + List left, @Nullable DogArray_I32 leftData, + List right, @Nullable DogArray_I32 rightData) { left.addAll(this.left); right.addAll(this.right); diff -Nru ddogleg-0.18+ds/test/org/ddogleg/nn/alg/TestKdTreeMemory.java ddogleg-0.22+ds/test/org/ddogleg/nn/alg/TestKdTreeMemory.java --- ddogleg-0.18+ds/test/org/ddogleg/nn/alg/TestKdTreeMemory.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/nn/alg/TestKdTreeMemory.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -29,7 +29,7 @@ public class TestKdTreeMemory { @Test - public void requestNode() { + void requestNode() { KdTreeMemory alg = new KdTreeMemory(); // Empty unused list @@ -46,7 +46,7 @@ } @Test - public void requestNode_leaf() { + void requestNode_leaf() { // create a node with values that need to be changed KdTree.Node n = new KdTree.Node(); n.point = new double[2]; @@ -65,7 +65,7 @@ } @Test - public void requestTree() { + void requestTree() { KdTreeMemory alg = new KdTreeMemory(); // Empty unused list @@ -80,7 +80,7 @@ } @Test - public void recycle() { + void recycle() { KdTreeMemory alg = new KdTreeMemory(); KdTree.Node n = new KdTree.Node(); @@ -93,8 +93,10 @@ assertTrue(n.right == null); assertEquals(1,alg.unusedNodes.size()); } + @Test - public void recycleGraph() { + @SuppressWarnings("NullAway") + void recycleGraph() { KdTreeMemory alg = new KdTreeMemory(); KdTree tree = new KdTree(); @@ -113,7 +115,7 @@ } @Test - public void recycleGraph_nullRoot() { + void recycleGraph_nullRoot() { KdTreeMemory alg = new KdTreeMemory(); KdTree tree = new KdTree(); diff -Nru ddogleg-0.18+ds/test/org/ddogleg/nn/StandardNearestNeighborTests.java ddogleg-0.22+ds/test/org/ddogleg/nn/StandardNearestNeighborTests.java --- ddogleg-0.18+ds/test/org/ddogleg/nn/StandardNearestNeighborTests.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/nn/StandardNearestNeighborTests.java 2022-09-01 02:18:09.000000000 +0000 @@ -20,9 +20,9 @@ import org.ddogleg.nn.alg.ExhaustiveNeighbor; import org.ddogleg.nn.alg.distance.KdTreeEuclideanSq_F64; -import org.ddogleg.struct.FastQueue; -import org.ddogleg.struct.GrowQueue_F64; -import org.ddogleg.struct.GrowQueue_I32; +import org.ddogleg.struct.DogArray; +import org.ddogleg.struct.DogArray_F64; +import org.ddogleg.struct.DogArray_I32; import org.junit.jupiter.api.Test; import java.util.ArrayList; @@ -35,16 +35,16 @@ /** * @author Peter Abeles */ +@SuppressWarnings({"NullAway"}) public abstract class StandardNearestNeighborTests { - - private Random rand = new Random(234); + private final Random rand = new Random(234); public int N = 2; - private KdTreeEuclideanSq_F64 distance = new KdTreeEuclideanSq_F64(N); + private final KdTreeEuclideanSq_F64 distance = new KdTreeEuclideanSq_F64(N); private NearestNeighbor alg; - private NnData found = new NnData<>(); - private FastQueue> foundN = new FastQueue<>(NnData::new); + private final NnData found = new NnData<>(); + private final DogArray> foundN = new DogArray<>(NnData::new); public void setAlg(NearestNeighbor alg) { this.alg = alg; @@ -292,8 +292,8 @@ @Test void findNearestN_compareToNaive() { - GrowQueue_I32 outputIndex = new GrowQueue_I32(); - GrowQueue_F64 outputDistance = new GrowQueue_F64(); + DogArray_I32 outputIndex = new DogArray_I32(); + DogArray_F64 outputDistance = new DogArray_F64(); for( int i = 0; i < 200; i++ ) { int numPoints = 8 + rand.nextInt(100); @@ -404,15 +404,15 @@ .parallel().forEach(i -> { NearestNeighbor.Search s = searches.get(i); NnData r = new NnData<>(); - assertTrue(s.findNearest(targets.get(i),Double.MAX_VALUE,r)); + assertTrue(s.findNearest(points.get(i),Double.MAX_VALUE,r)); results[i] = r.point; }); // compare results to single thread version NearestNeighbor.Search search = searches.get(0); NnData r = new NnData<>(); - for (int i = 0; i < searches.size(); i++) { - search.findNearest(targets.get(i),Double.MAX_VALUE,r); + for (int i = 0; i < points.size(); i++) { + search.findNearest(points.get(i),Double.MAX_VALUE,r); assertSame(results[i], r.point); } } diff -Nru ddogleg-0.18+ds/test/org/ddogleg/nn/wrap/TestVpTreeNearestNeighbor.java ddogleg-0.22+ds/test/org/ddogleg/nn/wrap/TestVpTreeNearestNeighbor.java --- ddogleg-0.18+ds/test/org/ddogleg/nn/wrap/TestVpTreeNearestNeighbor.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/nn/wrap/TestVpTreeNearestNeighbor.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,31 +1,31 @@ -/* - * Copyright (c) 2012-2014, Peter Abeles. All Rights Reserved. - * - * This file is part of DDogleg (http://ddogleg.org). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ddogleg.nn.wrap; - -import org.ddogleg.nn.FactoryNearestNeighbor; -import org.ddogleg.nn.StandardNearestNeighborTests; - -/** - * @author Karel Petránek - */ -public class TestVpTreeNearestNeighbor extends StandardNearestNeighborTests { - public TestVpTreeNearestNeighbor() { - setAlg(FactoryNearestNeighbor.vptree(345345)); - } -} +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.nn.wrap; + +import org.ddogleg.nn.FactoryNearestNeighbor; +import org.ddogleg.nn.StandardNearestNeighborTests; + +/** + * @author Karel Petránek + */ +public class TestVpTreeNearestNeighbor extends StandardNearestNeighborTests { + public TestVpTreeNearestNeighbor() { + setAlg(FactoryNearestNeighbor.vptree(345345)); + } +} diff -Nru ddogleg-0.18+ds/test/org/ddogleg/optimization/lm/TestUnconLeastSqLevenbergMarquardt_F64.java ddogleg-0.22+ds/test/org/ddogleg/optimization/lm/TestUnconLeastSqLevenbergMarquardt_F64.java --- ddogleg-0.18+ds/test/org/ddogleg/optimization/lm/TestUnconLeastSqLevenbergMarquardt_F64.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/optimization/lm/TestUnconLeastSqLevenbergMarquardt_F64.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -44,6 +44,7 @@ /** * @author Peter Abeles */ +@SuppressWarnings({"NullAway"}) public class TestUnconLeastSqLevenbergMarquardt_F64 extends GenericUnconstrainedLeastSquaresTests_F64 { @Test public void computeGradientHessian() { @@ -148,20 +149,9 @@ output.reshape(2,3); } - @Override - public DMatrixRMaj declareMatrixMxN() { - return null; - } - - @Override - public int getNumOfInputsN() { - return 0; - } - - @Override - public int getNumOfOutputsM() { - return 0; - } + @Override public DMatrixRMaj declareMatrixMxN() {return null;} + @Override public int getNumOfInputsN() {return 0;} + @Override public int getNumOfOutputsM() {return 0;} } private class MockHessian extends HessianLeastSquares_DDRM { diff -Nru ddogleg-0.18+ds/test/org/ddogleg/optimization/math/StandardHessianLeastSquaresChecks.java ddogleg-0.22+ds/test/org/ddogleg/optimization/math/StandardHessianLeastSquaresChecks.java --- ddogleg-0.18+ds/test/org/ddogleg/optimization/math/StandardHessianLeastSquaresChecks.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/optimization/math/StandardHessianLeastSquaresChecks.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -32,7 +32,7 @@ * @author Peter Abeles */ public abstract class StandardHessianLeastSquaresChecks extends StandardHessianMathChecks { - public StandardHessianLeastSquaresChecks(HessianMath alg) { + protected StandardHessianLeastSquaresChecks(HessianMath alg) { super(alg); } diff -Nru ddogleg-0.18+ds/test/org/ddogleg/optimization/math/StandardHessianMathChecks.java ddogleg-0.22+ds/test/org/ddogleg/optimization/math/StandardHessianMathChecks.java --- ddogleg-0.18+ds/test/org/ddogleg/optimization/math/StandardHessianMathChecks.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/optimization/math/StandardHessianMathChecks.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -39,7 +39,7 @@ Random rand = new Random(234); HessianMath alg; - public StandardHessianMathChecks(HessianMath alg ) { + protected StandardHessianMathChecks(HessianMath alg ) { this.alg = alg; } diff -Nru ddogleg-0.18+ds/test/org/ddogleg/optimization/math/StandardMatrixMathChecks.java ddogleg-0.22+ds/test/org/ddogleg/optimization/math/StandardMatrixMathChecks.java --- ddogleg-0.18+ds/test/org/ddogleg/optimization/math/StandardMatrixMathChecks.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/optimization/math/StandardMatrixMathChecks.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -44,7 +44,7 @@ Random rand = new Random(234); MatrixMath alg; - public StandardMatrixMathChecks(MatrixMath alg ) { + protected StandardMatrixMathChecks(MatrixMath alg ) { this.alg = alg; } diff -Nru ddogleg-0.18+ds/test/org/ddogleg/optimization/math/TestHessianBFGS_DDRM.java ddogleg-0.22+ds/test/org/ddogleg/optimization/math/TestHessianBFGS_DDRM.java --- ddogleg-0.18+ds/test/org/ddogleg/optimization/math/TestHessianBFGS_DDRM.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/optimization/math/TestHessianBFGS_DDRM.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -41,7 +41,7 @@ protected void setHessian(HessianMath alg, DMatrixRMaj H) { HessianBFGS_DDRM a = (HessianBFGS_DDRM)alg; - a.hessian.set(H); + a.hessian.setTo(H); CommonOps_DDRM.invert(H,a.hessianInverse); } @@ -58,6 +58,7 @@ * Not supported yet */ @Test + @Override public void divideRowsCols() { assertThrows(RuntimeException.class, super::divideRowsCols); } diff -Nru ddogleg-0.18+ds/test/org/ddogleg/optimization/math/TestHessianLeastSquares_DDRM.java ddogleg-0.22+ds/test/org/ddogleg/optimization/math/TestHessianLeastSquares_DDRM.java --- ddogleg-0.18+ds/test/org/ddogleg/optimization/math/TestHessianLeastSquares_DDRM.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/optimization/math/TestHessianLeastSquares_DDRM.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -36,7 +36,7 @@ protected void setHessian(HessianMath alg, DMatrixRMaj H) { HessianLeastSquares_DDRM a = (HessianLeastSquares_DDRM)alg; - a.hessian.set(H); + a.hessian.setTo(H); } @Override diff -Nru ddogleg-0.18+ds/test/org/ddogleg/optimization/math/TestHessianLeastSquares_DSCC.java ddogleg-0.22+ds/test/org/ddogleg/optimization/math/TestHessianLeastSquares_DSCC.java --- ddogleg-0.18+ds/test/org/ddogleg/optimization/math/TestHessianLeastSquares_DSCC.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/optimization/math/TestHessianLeastSquares_DSCC.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -21,7 +21,7 @@ import org.ejml.data.DMatrix; import org.ejml.data.DMatrixRMaj; import org.ejml.data.DMatrixSparseCSC; -import org.ejml.ops.ConvertDMatrixStruct; +import org.ejml.ops.DConvertMatrixStruct; import org.ejml.sparse.FillReducing; import org.ejml.sparse.csc.factory.LinearSolverFactory_DSCC; @@ -38,12 +38,12 @@ @Override protected DMatrix convert(DMatrixRMaj M) { DMatrixSparseCSC out = new DMatrixSparseCSC(1,1); - ConvertDMatrixStruct.convert(M,out); + DConvertMatrixStruct.convert(M,out); return out; } @Override protected void setHessian(HessianMath alg, DMatrixRMaj H) { - ConvertDMatrixStruct.convert(H,((HessianLeastSquares_DSCC)alg).hessian); + DConvertMatrixStruct.convert(H,((HessianLeastSquares_DSCC)alg).hessian); } } \ No newline at end of file diff -Nru ddogleg-0.18+ds/test/org/ddogleg/optimization/math/TestHessianMath_DDRM.java ddogleg-0.22+ds/test/org/ddogleg/optimization/math/TestHessianMath_DDRM.java --- ddogleg-0.18+ds/test/org/ddogleg/optimization/math/TestHessianMath_DDRM.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/optimization/math/TestHessianMath_DDRM.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -31,6 +31,6 @@ @Override protected void setHessian(HessianMath alg, DMatrixRMaj H) { - ((HessianMath_DDRM)alg).hessian.set(H); + ((HessianMath_DDRM)alg).hessian.setTo(H); } } \ No newline at end of file diff -Nru ddogleg-0.18+ds/test/org/ddogleg/optimization/math/TestHessianMath_DSCC.java ddogleg-0.22+ds/test/org/ddogleg/optimization/math/TestHessianMath_DSCC.java --- ddogleg-0.18+ds/test/org/ddogleg/optimization/math/TestHessianMath_DSCC.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/optimization/math/TestHessianMath_DSCC.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -19,7 +19,7 @@ package org.ddogleg.optimization.math; import org.ejml.data.DMatrixRMaj; -import org.ejml.ops.ConvertDMatrixStruct; +import org.ejml.ops.DConvertMatrixStruct; import org.ejml.sparse.FillReducing; import org.ejml.sparse.csc.factory.LinearSolverFactory_DSCC; @@ -33,6 +33,6 @@ @Override protected void setHessian(HessianMath alg, DMatrixRMaj H) { - ConvertDMatrixStruct.convert(H, ((HessianMath_DSCC)alg).hessian); + DConvertMatrixStruct.convert(H, ((HessianMath_DSCC)alg).hessian); } } \ No newline at end of file diff -Nru ddogleg-0.18+ds/test/org/ddogleg/optimization/math/TestHessianSchurComplement_DDRM.java ddogleg-0.22+ds/test/org/ddogleg/optimization/math/TestHessianSchurComplement_DDRM.java --- ddogleg-0.18+ds/test/org/ddogleg/optimization/math/TestHessianSchurComplement_DDRM.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/optimization/math/TestHessianSchurComplement_DDRM.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -24,7 +24,7 @@ import org.ejml.dense.row.CommonOps_DDRM; import org.ejml.dense.row.MatrixFeatures_DDRM; import org.ejml.dense.row.RandomMatrices_DDRM; -import org.ejml.ops.ConvertDMatrixStruct; +import org.ejml.ops.DConvertMatrixStruct; import org.ejml.sparse.csc.CommonOps_DSCC; import org.junit.jupiter.api.Test; @@ -69,7 +69,7 @@ HessianSchurComplement_DSCC hm = (HessianSchurComplement_DSCC)alg; DMatrixSparseCSC SH = new DMatrixSparseCSC(1,1); - ConvertDMatrixStruct.convert(H,SH); + DConvertMatrixStruct.convert(H,SH); hm.A.reshape(M,M); hm.B.reshape(M,N); diff -Nru ddogleg-0.18+ds/test/org/ddogleg/optimization/math/TestHessianSchurComplement_DSCC.java ddogleg-0.22+ds/test/org/ddogleg/optimization/math/TestHessianSchurComplement_DSCC.java --- ddogleg-0.18+ds/test/org/ddogleg/optimization/math/TestHessianSchurComplement_DSCC.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/optimization/math/TestHessianSchurComplement_DSCC.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -25,7 +25,7 @@ import org.ejml.data.IGrowArray; import org.ejml.dense.row.MatrixFeatures_DDRM; import org.ejml.dense.row.RandomMatrices_DDRM; -import org.ejml.ops.ConvertDMatrixStruct; +import org.ejml.ops.DConvertMatrixStruct; import org.ejml.sparse.csc.CommonOps_DSCC; import org.ejml.sparse.csc.RandomMatrices_DSCC; import org.junit.jupiter.api.Test; @@ -60,7 +60,8 @@ public TestHessianSchurComplement_DSCC() { super(new HessianSchurComplement_DSCC()); CommonOps_DSCC.concatColumns(jacLeft,jacRight,J); - CommonOps_DSCC.multTransA(J,J,H,gw,gx); + DMatrixSparseCSC J_t = CommonOps_DSCC.transpose(J,null,gw); + CommonOps_DSCC.mult(J_t,J,H,gw,gx); } @@ -73,7 +74,7 @@ HessianSchurComplement_DSCC hm = (HessianSchurComplement_DSCC)alg; DMatrixSparseCSC SH = new DMatrixSparseCSC(1,1); - ConvertDMatrixStruct.convert(H,SH); + DConvertMatrixStruct.convert(H,SH); hm.A.reshape(M,M); hm.B.reshape(M,N); @@ -91,7 +92,7 @@ math.computeGradient(jacLeft,jacRight,residuals,found); - CommonOps_DSCC.multTransA(J,residuals,expected); + CommonOps_DSCC.multTransA(J,residuals,expected,gx); assertTrue(MatrixFeatures_DDRM.isIdentical(expected,found,UtilEjml.TEST_F64)); } diff -Nru ddogleg-0.18+ds/test/org/ddogleg/optimization/math/TestMatrixMath_DSCC.java ddogleg-0.22+ds/test/org/ddogleg/optimization/math/TestMatrixMath_DSCC.java --- ddogleg-0.18+ds/test/org/ddogleg/optimization/math/TestMatrixMath_DSCC.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/optimization/math/TestMatrixMath_DSCC.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -20,7 +20,7 @@ import org.ejml.data.DMatrixRMaj; import org.ejml.data.DMatrixSparseCSC; -import org.ejml.ops.ConvertDMatrixStruct; +import org.ejml.ops.DConvertMatrixStruct; /** * @author Peter Abeles @@ -33,14 +33,14 @@ @Override public DMatrixSparseCSC convertA(DMatrixRMaj A) { DMatrixSparseCSC out = new DMatrixSparseCSC(A.numRows,A.numCols,1); - ConvertDMatrixStruct.convert(A,out); + DConvertMatrixStruct.convert(A,out); return out; } @Override public DMatrixRMaj convertB(DMatrixSparseCSC A) { DMatrixRMaj out = new DMatrixRMaj(A.numRows,A.numCols); - ConvertDMatrixStruct.convert(A,out); + DConvertMatrixStruct.convert(A,out); return out; } diff -Nru ddogleg-0.18+ds/test/org/ddogleg/optimization/quasinewton/TestEquationsBFGS.java ddogleg-0.22+ds/test/org/ddogleg/optimization/quasinewton/TestEquationsBFGS.java --- ddogleg-0.18+ds/test/org/ddogleg/optimization/quasinewton/TestEquationsBFGS.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/optimization/quasinewton/TestEquationsBFGS.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -85,7 +85,7 @@ SimpleMatrix SS = _s.mult(_s.transpose()).scale(p); SimpleMatrix M = A1.mult(B).mult(A2).plus(SS); - H.set(M.getMatrix()); + H.setTo(M.getMatrix()); } @Test diff -Nru ddogleg-0.18+ds/test/org/ddogleg/optimization/quasinewton/TestSearchInterpolate.java ddogleg-0.22+ds/test/org/ddogleg/optimization/quasinewton/TestSearchInterpolate.java --- ddogleg-0.18+ds/test/org/ddogleg/optimization/quasinewton/TestSearchInterpolate.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/optimization/quasinewton/TestSearchInterpolate.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -49,7 +49,7 @@ public void quadratic2() { double a = 2; double b = -3; - double c = 1; +// double c = 1; double alpha0 = 1; double g0 = quadDeriv(a,b,alpha0); diff -Nru ddogleg-0.18+ds/test/org/ddogleg/optimization/trustregion/TestTrustRegionBase_F64.java ddogleg-0.22+ds/test/org/ddogleg/optimization/trustregion/TestTrustRegionBase_F64.java --- ddogleg-0.18+ds/test/org/ddogleg/optimization/trustregion/TestTrustRegionBase_F64.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/optimization/trustregion/TestTrustRegionBase_F64.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -24,6 +24,7 @@ import org.ddogleg.optimization.math.HessianMath_DDRM; import org.ejml.UtilEjml; import org.ejml.data.DMatrixRMaj; +import org.jetbrains.annotations.Nullable; import org.junit.jupiter.api.Test; import java.io.PrintStream; @@ -174,14 +175,13 @@ } @Override - public void setVerbose(PrintStream out, int level) { - - } + public void setVerbose(@Nullable PrintStream out, int level) {} } + @SuppressWarnings({"NullAway"}) private static class MockTrustRegionBase_F64 extends TrustRegionBase_F64 { - public MockTrustRegionBase_F64(ParameterUpdate parameterUpdate) { + public MockTrustRegionBase_F64(@Nullable ParameterUpdate parameterUpdate) { super(parameterUpdate, new HessianMath_DDRM()); } diff -Nru ddogleg-0.18+ds/test/org/ddogleg/optimization/trustregion/TestTrustRegionUpdateCauchy_F64.java ddogleg-0.22+ds/test/org/ddogleg/optimization/trustregion/TestTrustRegionUpdateCauchy_F64.java --- ddogleg-0.18+ds/test/org/ddogleg/optimization/trustregion/TestTrustRegionUpdateCauchy_F64.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/optimization/trustregion/TestTrustRegionUpdateCauchy_F64.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -26,6 +26,7 @@ import org.ejml.dense.row.CommonOps_DDRM; import org.ejml.dense.row.NormOps_DDRM; import org.ejml.dense.row.RandomMatrices_DDRM; +import org.jetbrains.annotations.Nullable; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -132,10 +133,10 @@ assertEquals(2, NormOps_DDRM.normF(p), UtilEjml.TEST_F64); } - + @SuppressWarnings({"NullAway"}) private static class MockOwner extends TrustRegionBase_F64 { - public MockOwner(ParameterUpdate parameterUpdate) { + public MockOwner( @Nullable ParameterUpdate parameterUpdate) { super(parameterUpdate, new HessianMath_DDRM()); } diff -Nru ddogleg-0.18+ds/test/org/ddogleg/optimization/trustregion/TestUnconLeastSqTrustRegionSchur_F64.java ddogleg-0.22+ds/test/org/ddogleg/optimization/trustregion/TestUnconLeastSqTrustRegionSchur_F64.java --- ddogleg-0.18+ds/test/org/ddogleg/optimization/trustregion/TestUnconLeastSqTrustRegionSchur_F64.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/optimization/trustregion/TestUnconLeastSqTrustRegionSchur_F64.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -66,7 +66,8 @@ public TestUnconLeastSqTrustRegionSchur_F64() { CommonOps_DSCC.concatColumns(jacLeft,jacRight,J); - CommonOps_DSCC.multTransA(J,J,H,gw,gx); + DMatrixSparseCSC J_t = CommonOps_DSCC.transpose(J,null,gw); + CommonOps_DSCC.mult(J,J_t,H,gw,gx); H.sortIndices(null); } @@ -100,7 +101,7 @@ // Only the gradient is computed and returned. The hessian is saved internally DMatrixRMaj exp_g = new DMatrixRMaj(N,1); - CommonOps_DSCC.multTransA(J,residuals,exp_g); + CommonOps_DSCC.multTransA(J,residuals,exp_g,gx); assertTrue(MatrixFeatures_DDRM.isIdentical(exp_g,g,UtilEjml.TEST_F64)); } @@ -127,8 +128,8 @@ @Override public void process(double[] input, DMatrixSparseCSC left, DMatrixSparseCSC right) { - left.set(jacLeft); - right.set(jacRight); + left.setTo(jacLeft); + right.setTo(jacRight); } @Override @@ -144,7 +145,6 @@ @Nested class LeastSquaresDDRM extends CommonChecksUnconstrainedLeastSquaresSchur_DDRM { - LeastSquaresDDRM() { // we can do fast convergence check here because dense supports robust solvers like SVD and it // won't get stuck diff -Nru ddogleg-0.18+ds/test/org/ddogleg/optimization/wrap/GenericUnconstrainedLeastSquaresTests_F64.java ddogleg-0.22+ds/test/org/ddogleg/optimization/wrap/GenericUnconstrainedLeastSquaresTests_F64.java --- ddogleg-0.18+ds/test/org/ddogleg/optimization/wrap/GenericUnconstrainedLeastSquaresTests_F64.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/optimization/wrap/GenericUnconstrainedLeastSquaresTests_F64.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -57,7 +57,7 @@ double[] prev = new double[]{1,0.5}; int i; for( i = 0; i < 200 && !alg.iterate(); i++ ) { - double found[] = alg.getParameters(); + double[] found = alg.getParameters(); // check updated flag if( alg.isUpdated() ) { @@ -69,7 +69,7 @@ assertTrue(changed); } else { for( int j = 0; j < found.length; j++ ) { - assertTrue(found[j]==prev[j]); + assertEquals(prev[j], found[j]); } } @@ -80,7 +80,7 @@ assertTrue(i != 200); assertTrue(alg.isConverged()); - double found[] = alg.getParameters(); + double[] found = alg.getParameters(); assertEquals(a, found[0], UtilEjml.TEST_F64_SQ); assertEquals(b, found[1], UtilEjml.TEST_F64_SQ); @@ -104,17 +104,17 @@ for( int i = 0; i < 200 && !alg.iterate(); i++ ) {} - double expected[] = alg.getParameters().clone(); + double[] expected = alg.getParameters().clone(); alg.setFunction(residual,null); alg.initialize(new double[]{1,0.5},1e-10,1e-10); for( int i = 0; i < 200 && !alg.iterate(); i++ ) {} - double found[] = alg.getParameters().clone(); + double[] found = alg.getParameters().clone(); for( int i = 0; i < found.length; i++ ) { - assertTrue(found[i]==expected[i]); + assertEquals(expected[i], found[i]); } } @@ -132,18 +132,18 @@ for( int i = 0; i < 200 && !alg.iterate(); i++ ) {} - double found[] = alg.getParameters().clone(); - double expected[] = new double[]{1,2,3}; + double[] found = alg.getParameters().clone(); + double[] expected = new double[]{1,2,3}; for( int i = 0; i < found.length; i++ ) { - assertTrue(found[i]==expected[i]); + assertEquals(expected[i], found[i]); } // This will modify it on the first process(). Should make test more robust by having it modify it // later on too } - private class ModifyInputFunctions implements FunctionNtoM { + private static class ModifyInputFunctions implements FunctionNtoM { @Override public int getNumOfInputsN() { diff -Nru ddogleg-0.18+ds/test/org/ddogleg/optimization/wrap/GenericUnconstrainedMinimizationTests_F64.java ddogleg-0.22+ds/test/org/ddogleg/optimization/wrap/GenericUnconstrainedMinimizationTests_F64.java --- ddogleg-0.18+ds/test/org/ddogleg/optimization/wrap/GenericUnconstrainedMinimizationTests_F64.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/optimization/wrap/GenericUnconstrainedMinimizationTests_F64.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -33,6 +33,7 @@ * * @author Peter Abeles */ +@SuppressWarnings({"NullAway"}) public abstract class GenericUnconstrainedMinimizationTests_F64 { public abstract UnconstrainedMinimization createAlgorithm(); diff -Nru ddogleg-0.18+ds/test/org/ddogleg/optimization/wrap/TestIndividual_to_CoupledJacobian.java ddogleg-0.22+ds/test/org/ddogleg/optimization/wrap/TestIndividual_to_CoupledJacobian.java --- ddogleg-0.18+ds/test/org/ddogleg/optimization/wrap/TestIndividual_to_CoupledJacobian.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/optimization/wrap/TestIndividual_to_CoupledJacobian.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -28,6 +28,7 @@ /** * @author Peter Abeles */ +@SuppressWarnings({"NullAway"}) public class TestIndividual_to_CoupledJacobian { @Test diff -Nru ddogleg-0.18+ds/test/org/ddogleg/optimization/wrap/TestLsToNonLinearDeriv.java ddogleg-0.22+ds/test/org/ddogleg/optimization/wrap/TestLsToNonLinearDeriv.java --- ddogleg-0.18+ds/test/org/ddogleg/optimization/wrap/TestLsToNonLinearDeriv.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/optimization/wrap/TestLsToNonLinearDeriv.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -91,7 +91,7 @@ @Override public void process(double[] input, DMatrixRMaj output) { - double x1 = input[0]; +// double x1 = input[0]; double x2 = input[1]; output.data[0] = 1; diff -Nru ddogleg-0.18+ds/test/org/ddogleg/solver/impl/TestFindRealRootsSturm.java ddogleg-0.22+ds/test/org/ddogleg/solver/impl/TestFindRealRootsSturm.java --- ddogleg-0.18+ds/test/org/ddogleg/solver/impl/TestFindRealRootsSturm.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/solver/impl/TestFindRealRootsSturm.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -30,7 +30,6 @@ */ public class TestFindRealRootsSturm extends GeneralPolynomialRootReal { - @Override public List computeRealRoots(Polynomial poly) { FindRealRootsSturm alg = new FindRealRootsSturm(poly.size,-1,1e-16,500,500); @@ -48,6 +47,7 @@ } @Test + @Override public void rootsLargeReal() { // THIS TEST IS INTENTIONALLY BEING BYPASSED // There appears to be a very basic problem with the formulation of Sturm sequences that cases diff -Nru ddogleg-0.18+ds/test/org/ddogleg/solver/TestPolynomialOps.java ddogleg-0.22+ds/test/org/ddogleg/solver/TestPolynomialOps.java --- ddogleg-0.18+ds/test/org/ddogleg/solver/TestPolynomialOps.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/solver/TestPolynomialOps.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -99,8 +99,8 @@ Polynomial f = PolynomialOps.multiply(Polynomial.wrap(2,3),Polynomial.wrap(4,8),null); assertTrue(Polynomial.wrap(6).isIdentical(a, 1e-8)); - assertTrue(b.size == 0); - assertTrue(c.size == 0); + assertEquals(b.size, 0); + assertEquals(c.size, 0); assertTrue(Polynomial.wrap(8, 12).isIdentical(d, 1e-8)); assertTrue(Polynomial.wrap(8, 12).isIdentical(e, 1e-8)); assertTrue(Polynomial.wrap(8, 28, 24).isIdentical(f, 1e-8)); diff -Nru ddogleg-0.18+ds/test/org/ddogleg/sorting/TestQuickSelect.java ddogleg-0.22+ds/test/org/ddogleg/sorting/TestQuickSelect.java --- ddogleg-0.18+ds/test/org/ddogleg/sorting/TestQuickSelect.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/sorting/TestQuickSelect.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -23,9 +23,7 @@ import java.util.Arrays; import java.util.Random; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - +import static org.junit.jupiter.api.Assertions.*; public class TestQuickSelect { Random rand = new Random(0xFF); @@ -36,46 +34,70 @@ */ @Test public void testWithQuickSort() { - Comparable orig[] = new Comparable[100]; - Comparable copy[] = new Comparable[orig.length]; - Comparable sorted[] = new Comparable[orig.length]; + Double[] orig = new Double[100]; + Double[] copy = new Double[orig.length]; + Double[] sorted = new Double[orig.length]; - for( int i = 0; i < orig.length; i++ ) { + for (int i = 0; i < orig.length; i++) { orig[i] = rand.nextDouble(); sorted[i] = orig[i]; } Arrays.sort(sorted); - for( int i = 0; i < orig.length; i++ ) { - System.arraycopy(orig,0,copy,0,orig.length); - QuickSelect.select(copy,i,copy.length); + for (int i = 0; i < orig.length; i++) { + System.arraycopy(orig, 0, copy, 0, orig.length); + Double found = QuickSelect.select(copy, i, copy.length); + + assertSame(found, copy[i]); + assertEquals(sorted[i], copy[i]); + } + } + + @Test + public void testWithQuickSort_offset() { + int offset = 20; + Double[] orig = new Double[100]; + Double[] copy = new Double[orig.length]; + Double[] sorted = new Double[orig.length - offset]; - assertEquals(sorted[i],copy[i]); + for (int i = 0; i < orig.length; i++) { + orig[i] = rand.nextDouble(); + if (i >= offset) + sorted[i - offset] = orig[i]; } + Arrays.sort(sorted); + + for (int i = offset; i < orig.length; i++) { + System.arraycopy(orig, 0, copy, 0, orig.length); + Double found = QuickSelect.select(copy, i - offset, 20, copy.length); + + assertSame(found, copy[i]); + assertEquals(sorted[i - offset], copy[i]); + } } @Test public void testWithQuickSortIndex() { - Comparable orig[] = new Comparable[100]; - Comparable copy[] = new Comparable[orig.length]; - Comparable sorted[] = new Comparable[orig.length]; - int indexes[] = new int[ orig.length ]; + Double[] orig = new Double[100]; + Double[] copy = new Double[orig.length]; + Double[] sorted = new Double[orig.length]; + int[] indexes = new int[orig.length]; - for( int i = 0; i < orig.length; i++ ) { + for (int i = 0; i < orig.length; i++) { orig[i] = rand.nextDouble(); sorted[i] = orig[i]; } Arrays.sort(sorted); - for( int i = 0; i < orig.length; i++ ) { - System.arraycopy(orig,0,copy,0,orig.length); - QuickSelect.select(copy,i,copy.length,indexes); + for (int i = 0; i < orig.length; i++) { + System.arraycopy(orig, 0, copy, 0, orig.length); + QuickSelect.select(copy, i, copy.length, indexes); - assertEquals(orig[i],copy[i]); - assertEquals(sorted[i],copy[indexes[i]]); + assertEquals(orig[i], copy[i]); + assertEquals(sorted[i], copy[indexes[i]]); } } @@ -85,90 +107,128 @@ */ @Test public void testWithQuickSort_F64() { - double orig[] = new double[100]; - double copy[] = new double[orig.length]; - double sorted[] = new double[orig.length]; + double[] orig = new double[100]; + double[] copy = new double[orig.length]; + double[] sorted = new double[orig.length]; - for( int i = 0; i < orig.length; i++ ) { + for (int i = 0; i < orig.length; i++) { orig[i] = rand.nextDouble(); sorted[i] = orig[i]; } Arrays.sort(sorted); - for( int i = 0; i < orig.length; i++ ) { - System.arraycopy(orig,0,copy,0,orig.length); - double val = QuickSelect.select(copy,i,copy.length); + for (int i = 0; i < orig.length; i++) { + System.arraycopy(orig, 0, copy, 0, orig.length); + double val = QuickSelect.select(copy, i, copy.length); + + assertEquals(sorted[i], copy[i], 1e-6); + + // make sure everything earlier in the list is less than the selected one + for (int j = 0; j < i; j++) { + assertTrue(copy[j] <= val); + } + // everything after it should be greater + for (int j = i + 1; j < copy.length; j++) { + assertTrue(copy[j] > val); + } + } + } + + @Test + public void testWithQuickSort_offset_F64() { + int offset = 20; + + double[] orig = new double[100]; + double[] copy = new double[orig.length]; + double[] sorted = new double[orig.length - offset]; + + for (int i = 0; i < orig.length; i++) { + orig[i] = rand.nextDouble(); + if (i >= offset) + sorted[i - offset] = orig[i]; + } + + Arrays.sort(sorted); + + for (int i = offset; i < orig.length; i++) { + System.arraycopy(orig, 0, copy, 0, orig.length); + double val = QuickSelect.select(copy, i - offset, offset, copy.length); - assertEquals(sorted[i],copy[i],1e-6); + assertEquals(sorted[i-offset], copy[i], 1e-6); + + // earlier elements should not be modified + for (int j = 0; j < offset; j++) { + assertEquals(orig[j], copy[j]); + } // make sure everything earlier in the list is less than the selected one - for( int j = 0; j < i; j++ ) { - assertTrue(copy[j]<=val); + for (int j = offset; j < i; j++) { + assertTrue(copy[j] <= val); } // everything after it should be greater - for( int j = i+1; j < copy.length; j++ ) { - assertTrue(copy[j]>val); + for (int j = i + 1; j < copy.length; j++) { + assertTrue(copy[j] > val); } } } @Test public void testWithQuickSort_I32() { - int orig[] = new int[100]; - int copy[] = new int[orig.length]; - int sorted[] = new int[orig.length]; + int[] orig = new int[100]; + int[] copy = new int[orig.length]; + int[] sorted = new int[orig.length]; - for( int i = 0; i < orig.length; i++ ) { + for (int i = 0; i < orig.length; i++) { orig[i] = rand.nextInt(5000); sorted[i] = orig[i]; } Arrays.sort(sorted); - for( int i = 0; i < orig.length; i++ ) { - System.arraycopy(orig,0,copy,0,orig.length); - double val = QuickSelect.select(copy,i,copy.length); + for (int i = 0; i < orig.length; i++) { + System.arraycopy(orig, 0, copy, 0, orig.length); + double val = QuickSelect.select(copy, i, copy.length); - assertEquals(sorted[i],copy[i],1e-6); + assertEquals(sorted[i], copy[i], 1e-6); // make sure everything earlier in the list is less than the selected one - for( int j = 0; j < i; j++ ) { - assertTrue(copy[j]<=val); + for (int j = 0; j < i; j++) { + assertTrue(copy[j] <= val); } // everything after it should be greater - for( int j = i+1; j < copy.length; j++ ) { - assertTrue(copy[j]>val); + for (int j = i + 1; j < copy.length; j++) { + assertTrue(copy[j] > val); } } } @Test public void testWithQuickSort_I64() { - long orig[] = new long[100]; - long copy[] = new long[orig.length]; - long sorted[] = new long[orig.length]; + long[] orig = new long[100]; + long[] copy = new long[orig.length]; + long[] sorted = new long[orig.length]; - for( int i = 0; i < orig.length; i++ ) { + for (int i = 0; i < orig.length; i++) { orig[i] = rand.nextInt(5000); sorted[i] = orig[i]; } Arrays.sort(sorted); - for( int i = 0; i < orig.length; i++ ) { - System.arraycopy(orig,0,copy,0,orig.length); - double val = QuickSelect.select(copy,i,copy.length); + for (int i = 0; i < orig.length; i++) { + System.arraycopy(orig, 0, copy, 0, orig.length); + double val = QuickSelect.select(copy, i, copy.length); - assertEquals(sorted[i],copy[i],1e-6); + assertEquals(sorted[i], copy[i], 1e-6); // make sure everything earlier in the list is less than the selected one - for( int j = 0; j < i; j++ ) { - assertTrue(copy[j]<=val); + for (int j = 0; j < i; j++) { + assertTrue(copy[j] <= val); } // everything after it should be greater - for( int j = i+1; j < copy.length; j++ ) { - assertTrue(copy[j]>val); + for (int j = i + 1; j < copy.length; j++) { + assertTrue(copy[j] > val); } } } @@ -179,101 +239,101 @@ */ @Test public void testWithQuickSortIndex_F64() { - double orig[] = new double[100]; - double copy[] = new double[orig.length]; - double sorted[] = new double[orig.length]; - int indexes[] = new int[orig.length]; + double[] orig = new double[100]; + double[] copy = new double[orig.length]; + double[] sorted = new double[orig.length]; + int[] indexes = new int[orig.length]; - for( int i = 0; i < orig.length; i++ ) { + for (int i = 0; i < orig.length; i++) { orig[i] = rand.nextDouble(); sorted[i] = orig[i]; } Arrays.sort(sorted); - for( int i = 0; i < orig.length; i++ ) { - System.arraycopy(orig,0,copy,0,orig.length); - int index = QuickSelect.selectIndex(copy,i,copy.length,indexes); + for (int i = 0; i < orig.length; i++) { + System.arraycopy(orig, 0, copy, 0, orig.length); + int index = QuickSelect.selectIndex(copy, i, copy.length, indexes); - assertEquals(sorted[i],orig[index],1e-6); + assertEquals(sorted[i], orig[index], 1e-6); double val = orig[index]; // make sure the input hasn't been modified for (int j = 0; j < copy.length; j++) { - assertEquals(orig[j],copy[j],1e-8); + assertEquals(orig[j], copy[j], 1e-8); } // make sure everything earlier in the list is less than the selected one - for( int j = 0; j < i; j++ ) { - assertTrue(orig[indexes[j]]<=val); + for (int j = 0; j < i; j++) { + assertTrue(orig[indexes[j]] <= val); } // everything after it should be greater - for( int j = i+1; j < copy.length; j++ ) { - assertTrue(orig[indexes[j]]>val); + for (int j = i + 1; j < copy.length; j++) { + assertTrue(orig[indexes[j]] > val); } } } @Test public void testWithQuickSortIndex_I32() { - int orig[] = new int[100]; - int copy[] = new int[orig.length]; - int sorted[] = new int[orig.length]; - int indexes[] = new int[orig.length]; + int[] orig = new int[100]; + int[] copy = new int[orig.length]; + int[] sorted = new int[orig.length]; + int[] indexes = new int[orig.length]; - for( int i = 0; i < orig.length; i++ ) { + for (int i = 0; i < orig.length; i++) { orig[i] = rand.nextInt(6000); sorted[i] = orig[i]; } Arrays.sort(sorted); - for( int i = 0; i < orig.length; i++ ) { - System.arraycopy(orig,0,copy,0,orig.length); - int index = QuickSelect.selectIndex(copy,i,copy.length,indexes); + for (int i = 0; i < orig.length; i++) { + System.arraycopy(orig, 0, copy, 0, orig.length); + int index = QuickSelect.selectIndex(copy, i, copy.length, indexes); - assertEquals(sorted[i],orig[index],1e-6); + assertEquals(sorted[i], orig[index], 1e-6); double val = orig[index]; // make sure the input hasn't been modified for (int j = 0; j < copy.length; j++) { - assertEquals(orig[j],copy[j]); + assertEquals(orig[j], copy[j]); } // make sure everything earlier in the list is less than the selected one - for( int j = 0; j < i; j++ ) { - assertTrue(orig[indexes[j]]<=val); + for (int j = 0; j < i; j++) { + assertTrue(orig[indexes[j]] <= val); } // everything after it should be greater - for( int j = i+1; j < copy.length; j++ ) { - assertTrue(orig[indexes[j]]>val); + for (int j = i + 1; j < copy.length; j++) { + assertTrue(orig[indexes[j]] > val); } } } @Test public void testWithQuickSortIndex_I64() { - long orig[] = new long[100]; - long copy[] = new long[orig.length]; - long sorted[] = new long[orig.length]; - int indexes[] = new int[orig.length]; + long[] orig = new long[100]; + long[] copy = new long[orig.length]; + long[] sorted = new long[orig.length]; + int[] indexes = new int[orig.length]; - for( int i = 0; i < orig.length; i++ ) { + for (int i = 0; i < orig.length; i++) { orig[i] = rand.nextInt(6000); sorted[i] = orig[i]; } Arrays.sort(sorted); - for( int i = 0; i < orig.length; i++ ) { - System.arraycopy(orig,0,copy,0,orig.length); - int index = QuickSelect.selectIndex(copy,i,copy.length,indexes); + for (int i = 0; i < orig.length; i++) { + System.arraycopy(orig, 0, copy, 0, orig.length); + int index = QuickSelect.selectIndex(copy, i, copy.length, indexes); - assertEquals(sorted[i],orig[index],1e-6); + assertEquals(sorted[i], orig[index], 1e-6); double val = orig[index]; @@ -283,13 +343,13 @@ } // make sure everything earlier in the list is less than the selected one - for( int j = 0; j < i; j++ ) { - assertTrue(orig[indexes[j]]<=val); + for (int j = 0; j < i; j++) { + assertTrue(orig[indexes[j]] <= val); } // everything after it should be greater - for( int j = i+1; j < copy.length; j++ ) { - assertTrue(orig[indexes[j]]>val); + for (int j = i + 1; j < copy.length; j++) { + assertTrue(orig[indexes[j]] > val); } } } diff -Nru ddogleg-0.18+ds/test/org/ddogleg/sorting/TestQuickSortComparable.java ddogleg-0.22+ds/test/org/ddogleg/sorting/TestQuickSortComparable.java --- ddogleg-0.18+ds/test/org/ddogleg/sorting/TestQuickSortComparable.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/sorting/TestQuickSortComparable.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.sorting; + +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +public class TestQuickSortComparable { + Random rand = new Random(0xfeed4); + + @Test void sort_array() { + Double[] ret = createRandom(rand, 200); + + double preTotal = sum(ret); + + var sorter = new QuickSortComparable(); + + sorter.sort(ret, ret.length); + + double postTotal = sum(ret); + + // make sure it didn't modify the list, in an unexpected way + assertEquals(preTotal, postTotal, 1e-8); + + double prev = ret[0]; + for (int i = 1; i < ret.length; i++) { + if (ret[i] < prev) + fail("Not ascending"); + prev = ret[i]; + } + } + + @Test void sort_list() { + List ret = new ArrayList<>(); + for (int i = 0; i < 200; i++) { + ret.add((rand.nextDouble() - 0.5)*2000.0); + } + + double preTotal = sum(ret); + + var sorter = new QuickSortComparable(); + + sorter.sort(ret, ret.size()); + + double postTotal = sum(ret); + + // make sure it didn't modify the list, in an unexpected way + assertEquals(preTotal, postTotal, 1e-8); + + double prev = ret.get(0); + for (int i = 1; i < ret.size(); i++) { + if (ret.get(i) < prev) + fail("Not ascending"); + prev = ret.get(i); + } + } + + @Test + public void testSortingRandom_indexes() { + for (int a = 0; a < 20; a++) { + Double[] normal = createRandom(rand, 20); + Double[] original = normal.clone(); + Double[] withIndexes = normal.clone(); + int[] indexes = new int[normal.length]; + + var sorter = new QuickSortComparable(); + + sorter.sort(normal, normal.length); + sorter.sort(withIndexes, normal.length, indexes); + + for (int i = 0; i < normal.length; i++) { + // make sure the original hasn't been modified + assertEquals(original[i], withIndexes[i], 1e-8); + // see if it produced the same results as the normal one + assertEquals(normal[i], withIndexes[indexes[i]], 1e-8); + } + } + } + + public static double sum( Double[] list ) { + double total = 0; + for (int i = 0; i < list.length; i++) { + total += list[i]; + } + return total; + } + + public static double sum( List list ) { + double total = 0; + for (int i = 0; i < list.size(); i++) { + total += list.get(i); + } + return total; + } + + public static Double[] createRandom( Random rand, final int num ) { + Double[] ret = new Double[num]; + + for (int i = 0; i < num; i++) { + ret[i] = (rand.nextDouble() - 0.5)*2000.0; + } + + return ret; + } +} \ No newline at end of file diff -Nru ddogleg-0.18+ds/test/org/ddogleg/sorting/TestQuickSortComparator_F64.java ddogleg-0.22+ds/test/org/ddogleg/sorting/TestQuickSortComparator_F64.java --- ddogleg-0.18+ds/test/org/ddogleg/sorting/TestQuickSortComparator_F64.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/sorting/TestQuickSortComparator_F64.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,106 +0,0 @@ -/* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. - * - * This file is part of DDogleg (http://ddogleg.org). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ddogleg.sorting; - -import org.junit.jupiter.api.Test; - -import java.util.Comparator; -import java.util.Random; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.fail; - -public class TestQuickSortComparator_F64 { - Random rand = new Random(0xfeed4); - - Comparator comparator = new Comparator() { - @Override - public int compare(Double o1, Double o2) { - if( o1 > o2 ) - return 1; - else if( o1 < o2 ) - return -1; - else - return 0; - } - }; - - @Test - public void testSortingRandom() { - Double[] ret = createRandom(rand, 200); - - double preTotal = sum(ret); - - QuickSortComparator sorter = new QuickSortComparator(comparator); - - sorter.sort(ret, ret.length); - - double postTotal = sum(ret); - - // make sure it didn't modify the list, in an unexpected way - assertEquals(preTotal,postTotal,1e-8); - - double prev = ret[0]; - for( int i = 1; i < ret.length; i++ ) { - if( ret[i] < prev ) - fail("Not ascending"); - prev = ret[i]; - } - } - - @Test - public void testSortingRandom_indexes() { - for( int a = 0; a < 20; a++ ) { - Double[] normal = createRandom(rand,20); - Double[] original = normal.clone(); - Double[] withIndexes = normal.clone(); - int[] indexes = new int[ normal.length ]; - - QuickSortComparator sorter = new QuickSortComparator(comparator); - - sorter.sort(normal,normal.length); - sorter.sort(withIndexes,normal.length,indexes); - - for( int i = 0; i < normal.length; i++ ) { - // make sure the original hasn't been modified - assertEquals(original[i],withIndexes[i],1e-8); - // see if it produced the same results as the normal one - assertEquals(normal[i],withIndexes[indexes[i]],1e-8); - } - } - } - - public static double sum( Double[] list ) { - double total = 0; - for (int i = 0; i < list.length; i++) { - total += list[i]; - } - return total; - } - - public static Double[] createRandom( Random rand , final int num ) { - Double[] ret = new Double[ num ]; - - for( int i = 0; i < num; i++ ) { - ret[i] = (rand.nextDouble()-0.5)*2000.0; - } - - return ret; - } -} \ No newline at end of file diff -Nru ddogleg-0.18+ds/test/org/ddogleg/sorting/TestQuickSortComparator.java ddogleg-0.22+ds/test/org/ddogleg/sorting/TestQuickSortComparator.java --- ddogleg-0.18+ds/test/org/ddogleg/sorting/TestQuickSortComparator.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/sorting/TestQuickSortComparator.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.sorting; + +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Random; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +public class TestQuickSortComparator { + Random rand = new Random(0xfeed4); + + Comparator comparator = Double::compareTo; + + @Test void sort_array() { + Double[] ret = createRandom(rand, 200); + + double preTotal = sum(ret); + + QuickSortComparator sorter = new QuickSortComparator<>(comparator); + + sorter.sort(ret, ret.length); + + double postTotal = sum(ret); + + // make sure it didn't modify the list, in an unexpected way + assertEquals(preTotal,postTotal,1e-8); + + double prev = ret[0]; + for( int i = 1; i < ret.length; i++ ) { + if( ret[i] < prev ) + fail("Not ascending"); + prev = ret[i]; + } + } + + @Test void sort_list() { + List ret = new ArrayList<>(); + for (int i = 0; i < 200; i++) { + ret.add((rand.nextDouble()-0.5)*2000.0); + } + + double preTotal = sum(ret); + + QuickSortComparator sorter = new QuickSortComparator<>(comparator); + + sorter.sort(ret, ret.size()); + + double postTotal = sum(ret); + + // make sure it didn't modify the list, in an unexpected way + assertEquals(preTotal,postTotal,1e-8); + + double prev = ret.get(0); + for( int i = 1; i < ret.size(); i++ ) { + if( ret.get(i) < prev ) + fail("Not ascending"); + prev = ret.get(i); + } + } + + @Test + public void testSortingRandom_indexes() { + for( int a = 0; a < 20; a++ ) { + Double[] normal = createRandom(rand,20); + Double[] original = normal.clone(); + Double[] withIndexes = normal.clone(); + int[] indexes = new int[ normal.length ]; + + QuickSortComparator sorter = new QuickSortComparator<>(comparator); + + sorter.sort(normal,normal.length); + sorter.sort(withIndexes,normal.length,indexes); + + for( int i = 0; i < normal.length; i++ ) { + // make sure the original hasn't been modified + assertEquals(original[i],withIndexes[i],1e-8); + // see if it produced the same results as the normal one + assertEquals(normal[i],withIndexes[indexes[i]],1e-8); + } + } + } + + public static double sum( Double[] list ) { + double total = 0; + for (int i = 0; i < list.length; i++) { + total += list[i]; + } + return total; + } + + public static double sum( List list ) { + double total = 0; + for (int i = 0; i < list.size(); i++) { + total += list.get(i); + } + return total; + } + + public static Double[] createRandom( Random rand , final int num ) { + Double[] ret = new Double[ num ]; + + for( int i = 0; i < num; i++ ) { + ret[i] = (rand.nextDouble()-0.5)*2000.0; + } + + return ret; + } +} \ No newline at end of file diff -Nru ddogleg-0.18+ds/test/org/ddogleg/stats/TestStatisticsDogArray.java ddogleg-0.22+ds/test/org/ddogleg/stats/TestStatisticsDogArray.java --- ddogleg-0.18+ds/test/org/ddogleg/stats/TestStatisticsDogArray.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/stats/TestStatisticsDogArray.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.stats; + +import org.ddogleg.struct.DogArray_F64; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author Peter Abeles + */ +public class TestStatisticsDogArray { + @Test + public void mean_F64() { + DogArray_F64 l = new DogArray_F64(); + l.add(0); + l.add(1); + l.add(2); + l.add(3); + l.add(4); + + double found = StatisticsDogArray.mean(l); + assertEquals(2,found,1e-8); + } + + @Test + public void variance_F64() { + DogArray_F64 l = new DogArray_F64(); + l.add(0); + l.add(1); + l.add(2); + l.add(3); + l.add(4); + + double found = StatisticsDogArray.variance(l,2); + assertEquals(2.5,found,1e-8); + } + + @Test + public void fraction_F64() { + + DogArray_F64 l = new DogArray_F64(); + for (int i = 0; i < 100; i++) { + l.add(i); + } + + assertEquals(50, StatisticsDogArray.fraction(l,0.5),1e-8); + assertEquals(0, StatisticsDogArray.fraction(l,0),1e-8); + assertEquals(99, StatisticsDogArray.fraction(l,1.0),1e-8); + } +} diff -Nru ddogleg-0.18+ds/test/org/ddogleg/stats/TestUtilStatisticsQueue.java ddogleg-0.22+ds/test/org/ddogleg/stats/TestUtilStatisticsQueue.java --- ddogleg-0.18+ds/test/org/ddogleg/stats/TestUtilStatisticsQueue.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/stats/TestUtilStatisticsQueue.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. - * - * This file is part of DDogleg (http://ddogleg.org). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ddogleg.stats; - -import org.ddogleg.struct.GrowQueue_F64; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -/** - * @author Peter Abeles - */ -public class TestUtilStatisticsQueue { - @Test - public void mean_F64() { - GrowQueue_F64 l = new GrowQueue_F64(); - l.add(0); - l.add(1); - l.add(2); - l.add(3); - l.add(4); - - double found = UtilStatisticsQueue.mean(l); - assertEquals(2,found,1e-8); - } - - @Test - public void variance_F64() { - GrowQueue_F64 l = new GrowQueue_F64(); - l.add(0); - l.add(1); - l.add(2); - l.add(3); - l.add(4); - - double found = UtilStatisticsQueue.variance(l,2); - assertEquals(2.5,found,1e-8); - } - - @Test - public void fraction_F64() { - - GrowQueue_F64 l = new GrowQueue_F64(); - for (int i = 0; i < 100; i++) { - l.add(i); - } - - assertEquals(50,UtilStatisticsQueue.fraction(l,0.5),1e-8); - assertEquals(0,UtilStatisticsQueue.fraction(l,0),1e-8); - assertEquals(99,UtilStatisticsQueue.fraction(l,1.0),1e-8); - } -} diff -Nru ddogleg-0.18+ds/test/org/ddogleg/struct/ChecksBigDogArray.java ddogleg-0.22+ds/test/org/ddogleg/struct/ChecksBigDogArray.java --- ddogleg-0.18+ds/test/org/ddogleg/struct/ChecksBigDogArray.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/struct/ChecksBigDogArray.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.struct; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * @author Peter Abeles + */ +public abstract class ChecksBigDogArray { + + public abstract BigDogArrayBase createBigDog( int initialAllocation, int blockSize, BigDogGrowth growth); + + /** + * Ensures that the initial array allocation is done correctly by the constructor + */ + @Test void constructor() { + checkConstructor(5, 10, BigDogGrowth.GROW_FIRST, 5); + checkConstructor(85, 10, BigDogGrowth.GROW_FIRST, 10); + checkConstructor(5, 10, BigDogGrowth.GROW, 5); + checkConstructor(85, 10, BigDogGrowth.GROW, 5); + checkConstructor(5, 10, BigDogGrowth.FIXED, 10); + checkConstructor(85, 10, BigDogGrowth.FIXED, 10); + } + + private void checkConstructor( int initialAllocation, int blockSize, BigDogGrowth growth, int expectLastBlock ) { + int expectedBlocks = initialAllocation/blockSize + (initialAllocation%blockSize != 0 ? 1 : 0); + + BigDogArrayBase alg = createBigDog(initialAllocation, blockSize, growth); + assertEquals(0, alg.size); + assertEquals(growth, alg.growth); + assertEquals(10, alg.blockSize); + assertEquals(expectedBlocks, alg.blocks.size); + if (expectedBlocks > 0) + assertEquals(expectLastBlock, alg.blockArrayLength(expectedBlocks - 1)); + assertTrue(alg.isValidStructure()); + } + + @Test void reserve() { + checkReserve(5, BigDogGrowth.GROW_FIRST, 5); + checkReserve(10, BigDogGrowth.GROW_FIRST, 10); + checkReserve(11, BigDogGrowth.GROW_FIRST, 10); + checkReserve(45, BigDogGrowth.GROW_FIRST, 10); + checkReserve(50, BigDogGrowth.GROW_FIRST, 10); + checkReserve(5, BigDogGrowth.GROW, 5); + checkReserve(10, BigDogGrowth.GROW, 10); + checkReserve(45, BigDogGrowth.GROW, 5); + checkReserve(50, BigDogGrowth.GROW, 10); + checkReserve(5, BigDogGrowth.FIXED, 10); + checkReserve(10, BigDogGrowth.FIXED, 10); + checkReserve(45, BigDogGrowth.FIXED, 10); + checkReserve(50, BigDogGrowth.FIXED, 10); + } + + private void checkReserve( int allocation, BigDogGrowth growth, int expectLastBlock ) { + int blockSize = 10; + int expectedBlocks = allocation/blockSize + (allocation%blockSize != 0 ? 1 : 0); + + // Allocate the memory + BigDogArrayBase alg = createBigDog(1, blockSize, growth); + alg.reserve(allocation); + assertEquals(0, alg.size); // make sure the size didn't change + assertTrue(alg.isValidStructure()); + // verify the blocks have the expected structure + assertEquals(expectedBlocks, alg.blocks.size); + for (int i = 0; i < alg.blocks.size - 1; i++) { + assertEquals(blockSize, alg.blockArrayLength(i)); + } + + // See how well it handles reserve after being reset + alg.reset(); + alg.reserve(allocation); + assertEquals(0, alg.size); + assertTrue(alg.isValidStructure()); + assertEquals(expectedBlocks, alg.blocks.size); + for (int i = 0; i < alg.blocks.size - 1; i++) { + assertEquals(blockSize, alg.blockArrayLength(i)); + } + } + + @Test void getDesiredBlocks() { + BigDogArrayBase alg = createBigDog(1, 10, BigDogGrowth.GROW_FIRST); + + assertEquals(1, alg.getDesiredBlocks(1)); + assertEquals(1, alg.getDesiredBlocks(9)); + assertEquals(1, alg.getDesiredBlocks(10)); + assertEquals(2, alg.getDesiredBlocks(11)); + assertEquals(2, alg.getDesiredBlocks(19)); + assertEquals(2, alg.getDesiredBlocks(20)); + } + + @Test void allocate_GROW_FIRST() { + BigDogArrayBase alg = createBigDog(1, 10, BigDogGrowth.GROW_FIRST); + + assertEquals(1, alg.getTotalAllocation()); + alg.allocate(2,false,false); + assertEquals(2, alg.getTotalAllocation()); + alg.allocate(3,false,true); + assertTrue(3 <= alg.getTotalAllocation() && 10 >= alg.getTotalAllocation()); + + alg.allocate(10,false,false); + assertEquals(10, alg.getTotalAllocation()); + + alg.allocate(12,false,false); + assertEquals(20, alg.getTotalAllocation()); + + alg.allocate(13,false,true); + assertEquals(20, alg.getTotalAllocation()); + } + + @Test void allocate_GROW() { + BigDogArrayBase alg = createBigDog(1, 10, BigDogGrowth.GROW); + + assertEquals(1, alg.getTotalAllocation()); + alg.allocate(2,false,false); + assertEquals(2, alg.getTotalAllocation()); + alg.allocate(3,false,true); + assertTrue(3 <= alg.getTotalAllocation() && 10 >= alg.getTotalAllocation()); + + alg.allocate(10,false,false); + assertEquals(10, alg.getTotalAllocation()); + + alg.allocate(12,false,false); + assertEquals(12, alg.getTotalAllocation()); + + alg.allocate(13,false,true); + assertTrue(13 <= alg.getTotalAllocation() && 20 >= alg.getTotalAllocation()); + } + + @Test void allocate_FIXED() { + BigDogArrayBase alg = createBigDog(1, 10, BigDogGrowth.FIXED); + + assertEquals(10, alg.getTotalAllocation()); + alg.allocate(2,false,false); + assertEquals(10, alg.getTotalAllocation()); + alg.allocate(3,false,true); + assertEquals(10, alg.getTotalAllocation()); + + alg.allocate(10,false,false); + assertEquals(10, alg.getTotalAllocation()); + + alg.allocate(12,false,false); + assertEquals(20, alg.getTotalAllocation()); + + alg.allocate(13,false,true); + assertEquals(20, alg.getTotalAllocation()); + } +} diff -Nru ddogleg-0.18+ds/test/org/ddogleg/struct/ChecksDogArrayPrimitive.java ddogleg-0.22+ds/test/org/ddogleg/struct/ChecksDogArrayPrimitive.java --- ddogleg-0.18+ds/test/org/ddogleg/struct/ChecksDogArrayPrimitive.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/struct/ChecksDogArrayPrimitive.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2012-2022, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.struct; + +import org.junit.jupiter.api.Test; + +import java.util.Random; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author Peter Abeles + */ +public abstract class ChecksDogArrayPrimitive> { + + protected Random rand = new Random(0xfeed); + + public abstract T declare( int maxsize ); + + public abstract void push( T queue , double value ); + public abstract void insert( T queue , int index, double value ); + public abstract void check( T queue , int index , double value ); + + @Test void insert() { + T alg = declare(10); + + // insert with no array resize + push(alg, 1); + push(alg, 3); + push(alg, 4); + push(alg, 5); + + insert(alg, 2, 6); + + assertEquals(5,alg.size()); + check(alg,0,1); + check(alg,1,3); + check(alg,2,6); + check(alg,3,4); + check(alg,4,5); + + // insert with array resize + + alg = declare(4); + push(alg, 1); + push(alg, 3); + push(alg, 4); + push(alg, 5); + + insert(alg, 2, 6); + + assertEquals(5,alg.size()); + check(alg,0,1); + check(alg,1,3); + check(alg,2,6); + check(alg,3,4); + check(alg,4,5); + } + + @Test void extend() { + T alg = declare(2); + push(alg, 1); + push(alg, 3); + + alg.extend(4); + check(alg,0,1); + check(alg,1,3); + assertEquals(4,alg.size()); + + alg.extend(2); + assertEquals(2,alg.size()); + } + + @Test void flip() { + T alg = declare(10); + + alg.flip(); + assertEquals(0,alg.size()); + + push(alg, 1); + alg.flip(); + assertEquals(1,alg.size()); + check(alg,0,1); + + push(alg, 2); + alg.flip(); + assertEquals(2,alg.size()); + check(alg,0,2); + check(alg,1,1); + + push(alg, 3); + alg.flip(); + assertEquals(3,alg.size()); + check(alg,0,3); + check(alg,1,1); + check(alg,2,2); + } + + @Test void reserve() { + T alg = declare(2); + assertEquals(0, alg.size()); + push(alg, 3); + push(alg, 4); + assertEquals(2, alg.size()); + + // Change the reserve, which should leave the current values alone + alg.reserve(4); + check(alg,0,3); + check(alg,1,4); + assertEquals(2, alg.size()); + push(alg, 5); + assertEquals(3, alg.size()); + + alg.reserve(1); + check(alg,0,3); + assertEquals(3, alg.size()); + } +} diff -Nru ddogleg-0.18+ds/test/org/ddogleg/struct/ChecksGrowQueue.java ddogleg-0.22+ds/test/org/ddogleg/struct/ChecksGrowQueue.java --- ddogleg-0.18+ds/test/org/ddogleg/struct/ChecksGrowQueue.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/struct/ChecksGrowQueue.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,111 +0,0 @@ -/* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. - * - * This file is part of DDogleg (http://ddogleg.org). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ddogleg.struct; - -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -/** - * @author Peter Abeles - */ -public abstract class ChecksGrowQueue> { - - public abstract T declare( int maxsize ); - - public abstract void push( T queue , double value ); - public abstract void insert( T queue , int index, double value ); - public abstract void check( T queue , int index , double value ); - - - @Test - public void insert() { - T alg = declare(10); - - // insert with no array resize - push(alg, 1); - push(alg, 3); - push(alg, 4); - push(alg, 5); - - insert(alg, 2, 6); - - assertEquals(5,alg.size()); - check(alg,0,1); - check(alg,1,3); - check(alg,2,6); - check(alg,3,4); - check(alg,4,5); - - // insert with array resize - - alg = declare(4); - push(alg, 1); - push(alg, 3); - push(alg, 4); - push(alg, 5); - - insert(alg, 2, 6); - - assertEquals(5,alg.size()); - check(alg,0,1); - check(alg,1,3); - check(alg,2,6); - check(alg,3,4); - check(alg,4,5); - } - - @Test - public void extend() { - T alg = declare(2); - push(alg, 1); - push(alg, 3); - - alg.extend(4); - check(alg,0,1); - check(alg,1,3); - assertEquals(4,alg.size()); - } - - @Test - public void flip() { - T alg = declare(10); - - alg.flip(); - assertEquals(0,alg.size()); - - push(alg, 1); - alg.flip(); - assertEquals(1,alg.size()); - check(alg,0,1); - - push(alg, 2); - alg.flip(); - assertEquals(2,alg.size()); - check(alg,0,2); - check(alg,1,1); - - push(alg, 3); - alg.flip(); - assertEquals(3,alg.size()); - check(alg,0,3); - check(alg,1,1); - check(alg,2,2); - } -} diff -Nru ddogleg-0.18+ds/test/org/ddogleg/struct/TestBigDogArray_B.java ddogleg-0.22+ds/test/org/ddogleg/struct/TestBigDogArray_B.java --- ddogleg-0.18+ds/test/org/ddogleg/struct/TestBigDogArray_B.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/struct/TestBigDogArray_B.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,327 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.struct; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * @author Peter Abeles + */ +public class TestBigDogArray_B extends ChecksBigDogArray { + + /** resize with a set value */ + @Test void resizeValue() { + checkResizeValue(BigDogGrowth.GROW_FIRST); + checkResizeValue(BigDogGrowth.GROW); + checkResizeValue(BigDogGrowth.FIXED); + } + + /** Check resize by seeing if the size changed then checking the values */ + private void checkResizeValue( BigDogGrowth growth ) { + int blockSize = 10; + + var alg = new BigDogArray_B(1, blockSize, growth); + + // give it some values that we know and this is within the first block + alg.resize(blockSize - 4, true); + alg.resize(blockSize - 1, false); + assertEquals(blockSize - 1, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals(i < blockSize - 4, alg.get(i)); + } + + // Resize past the first block + alg.resize(blockSize + 3, false); + assertEquals(blockSize + 3, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals(i < blockSize - 4, alg.get(i)); + } + + // Fill this all in with a known value + alg.fill(0, alg.size, true); + // Resize past the second block + alg.resize(blockSize*2 + 1, false); + assertEquals(blockSize*2 + 1, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals(i < blockSize + 3, alg.get(i)); + } + } + + /** resize only */ + @Test void resize() { + checkResize(BigDogGrowth.GROW_FIRST); + checkResize(BigDogGrowth.GROW); + checkResize(BigDogGrowth.FIXED); + } + + /** Check resize by seeing if the size changed then checking the values */ + private void checkResize( BigDogGrowth growth ) { + int blockSize = 10; + + var alg = new BigDogArray_B(1, blockSize, growth); + + // give it some values that we know and this is within the first block + alg.resize(blockSize - 4, true); + alg.resize(blockSize - 1); + assertEquals(blockSize - 1, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals(i < blockSize - 4, alg.get(i)); + } + + // Resize past the first block + alg.resize(blockSize + 3); + assertEquals(blockSize + 3, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals(i < blockSize - 4, alg.get(i)); + } + + // Fill this all in with a known value + alg.fill(0, alg.size, true); + // Resize past the second block + alg.resize(blockSize*2 + 1); + assertEquals(blockSize*2 + 1, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals(i < blockSize + 3, alg.get(i)); + } + } + + @Test void appendArray() { + for (BigDogGrowth growth : BigDogGrowth.values()) { + checkAppendArray(1, growth, 5); + checkAppendArray(1, growth, 12); + checkAppendArray(4, growth, 8); + } + } + + private void checkAppendArray( int initialSize, BigDogGrowth growth, int arraySize ) { + boolean[] array = new boolean[arraySize]; + for (int i = 0; i < arraySize; i++) { + array[i] = true; + } + + var alg = new BigDogArray_B(initialSize, 10, growth); + alg.resize(initialSize); + alg.append(array, 1, arraySize - 1); + assertTrue(alg.isValidStructure()); + assertEquals(initialSize + arraySize - 1, alg.size); + for (int i = 0; i < initialSize; i++) { + assertFalse(alg.get(i)); + } + for (int i = initialSize; i < alg.size; i++) { + assertEquals(array[i - initialSize + 1], alg.get(i)); + } + } + + @Test void appendValue() { + for (BigDogGrowth growth : BigDogGrowth.values()) { + checkAppendValue(1, growth); + checkAppendValue(4, growth); + } + } + + private void checkAppendValue( int initialSize, BigDogGrowth growth ) { + var alg = new BigDogArray_B(initialSize, 10, growth); + alg.resize(initialSize); + + for (int i = 0; i < 21; i++) { + alg.append(true); + assertEquals(initialSize + i + 1, alg.size); + assertTrue(alg.isValidStructure()); + } + assertEquals(3, alg.blocks.size); + + for (int i = 0; i < initialSize; i++) { + assertFalse(alg.get(i)); + } + for (int i = initialSize; i < alg.size; i++) { + assertTrue(alg.get(i)); + } + + // Do it over again with a predeclared array + alg.reserve(45); + alg.resize(initialSize); + + for (int i = 0; i < 10; i++) { + alg.append(true); + assertEquals(initialSize + i + 1, alg.size); + assertTrue(alg.isValidStructure()); + } + + for (int i = 0; i < initialSize; i++) { + assertFalse(alg.get(i)); + } + for (int i = initialSize; i < alg.size; i++) { + assertTrue(alg.get(i)); + } + } + + @Test void fill() { + for (BigDogGrowth growth : BigDogGrowth.values()) { + checkFill(growth); + } + } + + private void checkFill( BigDogGrowth growth ) { + var alg = new BigDogArray_B(1, 10, growth); + alg.resize(4, true); + alg.resize(11, false); + alg.fill(0, 3, false); + alg.fill(3, 9, true); + + for (int i = 0; i < 3; i++) { + assertFalse(alg.get(i)); + } + for (int i = 3; i < 9; i++) { + assertTrue(alg.get(i)); + } + assertFalse(alg.get(9)); + assertFalse(alg.get(10)); + + // fill across block boundary + alg.fill(0, alg.size, true); + for (int i = 3; i < alg.size; i++) { + assertTrue(alg.get(i)); + } + } + + @Test void setArray() { + var alg = new BigDogArray_B(2, 10, BigDogGrowth.GROW); + alg.resize(25); + + boolean[] array = new boolean[21]; + for (int i = 0; i < array.length; i++) { + array[i] = i%2==0; + } + + alg.setArray(1, array, 1, array.length - 1); + for (int i = 1; i < array.length; i++) { + assertEquals(array[i], alg.get(i)); + } + } + + @Test void set_get() { + var alg = new BigDogArray_B(2, 10, BigDogGrowth.GROW); + alg.resize(25); + + for (int i = 0; i < 25; i++) { + alg.set(i, i%2==0); + assertEquals(i%2==0, alg.get(i)); + } + } + + @Test void setTail_getTail() { + var alg = new BigDogArray_B(2, 10, BigDogGrowth.GROW); + alg.resize(25); + + for (int i = 0; i < 25; i++) { + alg.setTail(i, i%2==0); + assertEquals(i%2==0, alg.getTail(i)); + } + + assertEquals(alg.get(alg.size-1), alg.getTail(0)); + assertEquals(alg.get(alg.size-2), alg.getTail(1)); + } + + @Test void getArray() { + var alg = new BigDogArray_B(2, 10, BigDogGrowth.GROW); + alg.resize(22); + for (int i = 0; i < alg.size; i++) { + alg.set(i, i%2==0); + } + boolean[] array = new boolean[15]; + alg.getArray(2, array, 1, 12); + for (int i = 0; i < array.length; i++) { + if (i >= 1 && i < 13) + assertEquals(i%2==1, array[i]); + else + assertFalse(array[i]); + } + } + + @Test void forEach() { + var alg = new BigDogArray_B(2, 10, BigDogGrowth.GROW); + alg.resize(22); + for (int i = 0; i < alg.size; i++) { + alg.set(i, i%2==0); + } + + DogArray_B results = new DogArray_B(); + alg.forEach(1, 12, results::add); + assertEquals(11, results.size); + for (int i = 0; i < 11; i++) { + assertEquals(i%2==1, results.get(i)); + } + } + + @Test void forIdx() { + var alg = new BigDogArray_B(2, 10, BigDogGrowth.GROW); + alg.resize(22); + for (int i = 0; i < alg.size; i++) { + alg.set(i, i%2==0); + } + + DogArray_B results = new DogArray_B(12); + results.resize(12); + alg.forIdx(1, 12, results::set); + assertFalse(results.get(0)); + for (int i = 1; i < 12; i++) { + assertEquals(i%2==0, results.get(i)); + } + } + + @Test void applyIdx() { + var alg = new BigDogArray_B(2, 10, BigDogGrowth.GROW); + alg.resize(22); + for (int i = 0; i < alg.size; i++) { + alg.set(i, i%2==0); + } + + alg.applyIdx(1, 12, ( idx, val ) -> idx%2==1); + for (int i = 0; i < alg.size; i++) { + if (i >= 1 && i < 12) { + assertEquals(i%2==1, alg.get(i)); + } else { + assertEquals(i%2==0, alg.get(i)); + } + } + } + + @Test void processByBlock() { + var alg = new BigDogArray_B(2, 10, BigDogGrowth.GROW); + alg.resize(26); + alg.processByBlock(1, alg.size - 1, ( block, idx0, idx1, offset ) -> { + for (int i = idx0; i < idx1; i++) { + block[i] = (i - idx0 + offset)%2==0; + } + }); + for (int i = 0; i < alg.size; i++) { + if (i >= 1 && i < alg.size - 1) { + assertEquals((i - 1)%2==0, alg.get(i)); + } else { + assertFalse(alg.get(i)); + } + } + } + + @Override public BigDogArrayBase createBigDog( int initialAllocation, int blockSize, BigDogGrowth growth ) { + return new BigDogArray_B(initialAllocation, blockSize, growth); + } +} diff -Nru ddogleg-0.18+ds/test/org/ddogleg/struct/TestBigDogArray_F32.java ddogleg-0.22+ds/test/org/ddogleg/struct/TestBigDogArray_F32.java --- ddogleg-0.18+ds/test/org/ddogleg/struct/TestBigDogArray_F32.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/struct/TestBigDogArray_F32.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,328 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.struct; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * @author Peter Abeles + */ +public class TestBigDogArray_F32 extends ChecksBigDogArray { + + /** resize with a set value */ + @Test void resizeValue() { + checkResizeValue(BigDogGrowth.GROW_FIRST); + checkResizeValue(BigDogGrowth.GROW); + checkResizeValue(BigDogGrowth.FIXED); + } + + /** Check resize by seeing if the size changed then checking the values */ + private void checkResizeValue( BigDogGrowth growth ) { + int blockSize = 10; + + var alg = new BigDogArray_F32(1, blockSize, growth); + + // give it some values that we know and this is within the first block + alg.resize(blockSize - 4, (float)4); + alg.resize(blockSize - 1, (float)3); + assertEquals(blockSize - 1, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals(i < blockSize - 4 ? 4 : 3, alg.get(i)); + } + + // Resize past the first block + alg.resize(blockSize + 3, (float)3); + assertEquals(blockSize + 3, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals(i < blockSize - 4 ? 4 : 3, alg.get(i)); + } + + // Fill this all in with a known value + alg.fill(0, alg.size, (float)5); + // Resize past the second block + alg.resize(blockSize*2 + 1, (float)6); + assertEquals(blockSize*2 + 1, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals(i < blockSize + 3 ? 5 : 6, alg.get(i)); + } + } + + /** resize only */ + @Test void resize() { + checkResize(BigDogGrowth.GROW_FIRST); + checkResize(BigDogGrowth.GROW); + checkResize(BigDogGrowth.FIXED); + } + + /** Check resize by seeing if the size changed then checking the values */ + private void checkResize( BigDogGrowth growth ) { + int blockSize = 10; + + var alg = new BigDogArray_F32(1, blockSize, growth); + + // give it some values that we know and this is within the first block + alg.resize(blockSize - 4, (float)4); + alg.resize(blockSize - 1); + assertEquals(blockSize - 1, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals(i < blockSize - 4 ? 4 : 0, alg.get(i)); + } + + // Resize past the first block + alg.resize(blockSize + 3); + assertEquals(blockSize + 3, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals(i < blockSize - 4 ? 4 : 0, alg.get(i)); + } + + // Fill this all in with a known value + alg.fill(0, alg.size, (float)5); + // Resize past the second block + alg.resize(blockSize*2 + 1); + assertEquals(blockSize*2 + 1, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals(i < blockSize + 3 ? 5 : 0, alg.get(i)); + } + } + + @Test void appendArray() { + for (BigDogGrowth growth : BigDogGrowth.values()) { + checkAppendArray(1, growth, 5); + checkAppendArray(1, growth, 12); + checkAppendArray(4, growth, 8); + } + } + + private void checkAppendArray( int initialSize, BigDogGrowth growth, int arraySize ) { + float[] array = new float[arraySize]; + for (int i = 0; i < arraySize; i++) { + array[i] = (float)(i + 1); + } + + var alg = new BigDogArray_F32(initialSize, 10, growth); + alg.resize(initialSize); + alg.append(array, 1, arraySize - 1); + assertTrue(alg.isValidStructure()); + assertEquals(initialSize + arraySize - 1, alg.size); + for (int i = 0; i < initialSize; i++) { + assertEquals(0, alg.get(i)); + } + for (int i = initialSize; i < alg.size; i++) { + assertEquals(array[i - initialSize + 1], alg.get(i)); + } + } + + @Test void appendValue() { + for (BigDogGrowth growth : BigDogGrowth.values()) { + checkAppendValue(1, growth); + checkAppendValue(4, growth); + } + } + + private void checkAppendValue( int initialSize, BigDogGrowth growth ) { + var alg = new BigDogArray_F32(initialSize, 10, growth); + alg.resize(initialSize); + + for (int i = 0; i < 21; i++) { + alg.append((float)(i + 1)); + assertEquals(initialSize + i + 1, alg.size); + assertTrue(alg.isValidStructure()); + } + assertEquals(3, alg.blocks.size); + + for (int i = 0; i < initialSize; i++) { + assertEquals(0, alg.get(i)); + } + for (int i = initialSize; i < alg.size; i++) { + assertEquals(i + 1 - initialSize, alg.get(i)); + } + + // Do it over again with a predeclared array + alg.reserve(45); + alg.resize(initialSize); + + for (int i = 0; i < 10; i++) { + alg.append((float)(i + 1)); + assertEquals(initialSize + i + 1, alg.size); + assertTrue(alg.isValidStructure()); + } + + for (int i = 0; i < initialSize; i++) { + assertEquals(0, alg.get(i)); + } + for (int i = initialSize; i < alg.size; i++) { + assertEquals(i + 1 - initialSize, alg.get(i)); + } + } + + @Test void fill() { + for (BigDogGrowth growth : BigDogGrowth.values()) { + checkFill(growth); + } + } + + private void checkFill( BigDogGrowth growth ) { + var alg = new BigDogArray_F32(1, 10, growth); + alg.resize(4, (float)2); + alg.resize(11, (float)10); + alg.fill(0, 3, (float)1); + alg.fill(3, 9, (float)2); + + for (int i = 0; i < 3; i++) { + assertEquals(1, alg.get(i)); + } + for (int i = 3; i < 9; i++) { + assertEquals(2, alg.get(i)); + } + assertEquals(10, alg.get(9)); + assertEquals(10, alg.get(10)); + + // fill across block boundary + alg.fill(0, alg.size, (float)4); + for (int i = 3; i < alg.size; i++) { + assertEquals(4, alg.get(i)); + } + } + + @Test void setArray() { + var alg = new BigDogArray_F32(2, 10, BigDogGrowth.GROW); + alg.resize(25); + + float[] array = new float[21]; + for (int i = 0; i < array.length; i++) { + array[i] = (float)(i + 1); + } + + alg.setArray(1, array, 1, array.length - 1); + for (int i = 1; i < array.length; i++) { + assertEquals(array[i], alg.get(i)); + } + } + + @Test void set_get() { + var alg = new BigDogArray_F32(2, 10, BigDogGrowth.GROW); + alg.resize(25); + + for (int i = 0; i < 25; i++) { + alg.set(i, (float)(i + 1)); + assertEquals(i + 1, alg.get(i)); + } + } + + @Test void setTail_getTail() { + var alg = new BigDogArray_F32(2, 10, BigDogGrowth.GROW); + alg.resize(25); + + for (int i = 0; i < 25; i++) { + alg.setTail(i, (float)(i + 1)); + assertEquals(i + 1, alg.getTail(i)); + } + + assertEquals(alg.get(alg.size-1), alg.getTail(0)); + assertEquals(alg.get(alg.size-2), alg.getTail(1)); + } + + @Test void getArray() { + var alg = new BigDogArray_F32(2, 10, BigDogGrowth.GROW); + alg.resize(22); + for (int i = 0; i < alg.size; i++) { + alg.set(i, (float)(i + 1)); + } + float[] array = new float[15]; + alg.getArray(2, array, 1, 12); + for (int i = 0; i < array.length; i++) { + if (i >= 1 && i < 13) + assertEquals(2 + i, array[i]); + else + assertEquals(0, array[i]); + } + } + + @Test void forEach() { + var alg = new BigDogArray_F32(2, 10, BigDogGrowth.GROW); + alg.resize(22); + for (int i = 0; i < alg.size; i++) { + alg.set(i, (float)(i + 1)); + } + + DogArray_F32 results = new DogArray_F32(); + alg.forEach(1, 12, results::add); + assertEquals(11, results.size); + for (int i = 0; i < 11; i++) { + assertEquals(i + 2, results.get(i)); + } + } + + @Test void forIdx() { + var alg = new BigDogArray_F32(2, 10, BigDogGrowth.GROW); + alg.resize(22); + for (int i = 0; i < alg.size; i++) { + alg.set(i, (float)(i + 1)); + } + + DogArray_F32 results = new DogArray_F32(12); + results.resize(12); + alg.forIdx(1, 12, results::set); + assertEquals(0, results.get(0)); + for (int i = 1; i < 12; i++) { + assertEquals(i + 1, results.get(i)); + } + } + + @Test void applyIdx() { + var alg = new BigDogArray_F32(2, 10, BigDogGrowth.GROW); + alg.resize(22); + for (int i = 0; i < alg.size; i++) { + alg.set(i, (float)(i + 1)); + } + + alg.applyIdx(1, 12, ( idx, val ) -> (float)(idx*2)); + for (int i = 0; i < alg.size; i++) { + if (i >= 1 && i < 12) { + assertEquals(i*2, alg.get(i)); + } else { + assertEquals(i + 1, alg.get(i)); + } + } + } + + @Test void processByBlock() { + var alg = new BigDogArray_F32(2, 10, BigDogGrowth.GROW); + alg.resize(26); + alg.processByBlock(1, alg.size - 1, ( block, idx0, idx1, offset ) -> { + for (int i = idx0; i < idx1; i++) { + block[i] = (float)(i - idx0 + offset); + } + }); + for (int i = 0; i < alg.size; i++) { + if (i >= 1 && i < alg.size - 1) { + assertEquals((float)(i - 1), alg.get(i)); + } else { + assertEquals(0, alg.get(i)); + } + } + } + + @Override public BigDogArrayBase createBigDog( int initialAllocation, int blockSize, BigDogGrowth growth ) { + return new BigDogArray_F32(initialAllocation, blockSize, growth); + } +} diff -Nru ddogleg-0.18+ds/test/org/ddogleg/struct/TestBigDogArray_F64.java ddogleg-0.22+ds/test/org/ddogleg/struct/TestBigDogArray_F64.java --- ddogleg-0.18+ds/test/org/ddogleg/struct/TestBigDogArray_F64.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/struct/TestBigDogArray_F64.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,328 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.struct; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * @author Peter Abeles + */ +public class TestBigDogArray_F64 extends ChecksBigDogArray { + + /** resize with a set value */ + @Test void resizeValue() { + checkResizeValue(BigDogGrowth.GROW_FIRST); + checkResizeValue(BigDogGrowth.GROW); + checkResizeValue(BigDogGrowth.FIXED); + } + + /** Check resize by seeing if the size changed then checking the values */ + private void checkResizeValue( BigDogGrowth growth ) { + int blockSize = 10; + + var alg = new BigDogArray_F64(1, blockSize, growth); + + // give it some values that we know and this is within the first block + alg.resize(blockSize - 4, (double)4); + alg.resize(blockSize - 1, (double)3); + assertEquals(blockSize - 1, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals(i < blockSize - 4 ? 4 : 3, alg.get(i)); + } + + // Resize past the first block + alg.resize(blockSize + 3, (double)3); + assertEquals(blockSize + 3, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals(i < blockSize - 4 ? 4 : 3, alg.get(i)); + } + + // Fill this all in with a known value + alg.fill(0, alg.size, (double)5); + // Resize past the second block + alg.resize(blockSize*2 + 1, (double)6); + assertEquals(blockSize*2 + 1, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals(i < blockSize + 3 ? 5 : 6, alg.get(i)); + } + } + + /** resize only */ + @Test void resize() { + checkResize(BigDogGrowth.GROW_FIRST); + checkResize(BigDogGrowth.GROW); + checkResize(BigDogGrowth.FIXED); + } + + /** Check resize by seeing if the size changed then checking the values */ + private void checkResize( BigDogGrowth growth ) { + int blockSize = 10; + + var alg = new BigDogArray_F64(1, blockSize, growth); + + // give it some values that we know and this is within the first block + alg.resize(blockSize - 4, (double)4); + alg.resize(blockSize - 1); + assertEquals(blockSize - 1, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals(i < blockSize - 4 ? 4 : 0, alg.get(i)); + } + + // Resize past the first block + alg.resize(blockSize + 3); + assertEquals(blockSize + 3, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals(i < blockSize - 4 ? 4 : 0, alg.get(i)); + } + + // Fill this all in with a known value + alg.fill(0, alg.size, (double)5); + // Resize past the second block + alg.resize(blockSize*2 + 1); + assertEquals(blockSize*2 + 1, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals(i < blockSize + 3 ? 5 : 0, alg.get(i)); + } + } + + @Test void appendArray() { + for (BigDogGrowth growth : BigDogGrowth.values()) { + checkAppendArray(1, growth, 5); + checkAppendArray(1, growth, 12); + checkAppendArray(4, growth, 8); + } + } + + private void checkAppendArray( int initialSize, BigDogGrowth growth, int arraySize ) { + double[] array = new double[arraySize]; + for (int i = 0; i < arraySize; i++) { + array[i] = (double)(i + 1); + } + + var alg = new BigDogArray_F64(initialSize, 10, growth); + alg.resize(initialSize); + alg.append(array, 1, arraySize - 1); + assertTrue(alg.isValidStructure()); + assertEquals(initialSize + arraySize - 1, alg.size); + for (int i = 0; i < initialSize; i++) { + assertEquals(0, alg.get(i)); + } + for (int i = initialSize; i < alg.size; i++) { + assertEquals(array[i - initialSize + 1], alg.get(i)); + } + } + + @Test void appendValue() { + for (BigDogGrowth growth : BigDogGrowth.values()) { + checkAppendValue(1, growth); + checkAppendValue(4, growth); + } + } + + private void checkAppendValue( int initialSize, BigDogGrowth growth ) { + var alg = new BigDogArray_F64(initialSize, 10, growth); + alg.resize(initialSize); + + for (int i = 0; i < 21; i++) { + alg.append((double)(i + 1)); + assertEquals(initialSize + i + 1, alg.size); + assertTrue(alg.isValidStructure()); + } + assertEquals(3, alg.blocks.size); + + for (int i = 0; i < initialSize; i++) { + assertEquals(0, alg.get(i)); + } + for (int i = initialSize; i < alg.size; i++) { + assertEquals(i + 1 - initialSize, alg.get(i)); + } + + // Do it over again with a predeclared array + alg.reserve(45); + alg.resize(initialSize); + + for (int i = 0; i < 10; i++) { + alg.append((double)(i + 1)); + assertEquals(initialSize + i + 1, alg.size); + assertTrue(alg.isValidStructure()); + } + + for (int i = 0; i < initialSize; i++) { + assertEquals(0, alg.get(i)); + } + for (int i = initialSize; i < alg.size; i++) { + assertEquals(i + 1 - initialSize, alg.get(i)); + } + } + + @Test void fill() { + for (BigDogGrowth growth : BigDogGrowth.values()) { + checkFill(growth); + } + } + + private void checkFill( BigDogGrowth growth ) { + var alg = new BigDogArray_F64(1, 10, growth); + alg.resize(4, (double)2); + alg.resize(11, (double)10); + alg.fill(0, 3, (double)1); + alg.fill(3, 9, (double)2); + + for (int i = 0; i < 3; i++) { + assertEquals(1, alg.get(i)); + } + for (int i = 3; i < 9; i++) { + assertEquals(2, alg.get(i)); + } + assertEquals(10, alg.get(9)); + assertEquals(10, alg.get(10)); + + // fill across block boundary + alg.fill(0, alg.size, (double)4); + for (int i = 3; i < alg.size; i++) { + assertEquals(4, alg.get(i)); + } + } + + @Test void setArray() { + var alg = new BigDogArray_F64(2, 10, BigDogGrowth.GROW); + alg.resize(25); + + double[] array = new double[21]; + for (int i = 0; i < array.length; i++) { + array[i] = (double)(i + 1); + } + + alg.setArray(1, array, 1, array.length - 1); + for (int i = 1; i < array.length; i++) { + assertEquals(array[i], alg.get(i)); + } + } + + @Test void set_get() { + var alg = new BigDogArray_F64(2, 10, BigDogGrowth.GROW); + alg.resize(25); + + for (int i = 0; i < 25; i++) { + alg.set(i, (double)(i + 1)); + assertEquals(i + 1, alg.get(i)); + } + } + + @Test void setTail_getTail() { + var alg = new BigDogArray_F64(2, 10, BigDogGrowth.GROW); + alg.resize(25); + + for (int i = 0; i < 25; i++) { + alg.setTail(i, (double)(i + 1)); + assertEquals(i + 1, alg.getTail(i)); + } + + assertEquals(alg.get(alg.size-1), alg.getTail(0)); + assertEquals(alg.get(alg.size-2), alg.getTail(1)); + } + + @Test void getArray() { + var alg = new BigDogArray_F64(2, 10, BigDogGrowth.GROW); + alg.resize(22); + for (int i = 0; i < alg.size; i++) { + alg.set(i, (double)(i + 1)); + } + double[] array = new double[15]; + alg.getArray(2, array, 1, 12); + for (int i = 0; i < array.length; i++) { + if (i >= 1 && i < 13) + assertEquals(2 + i, array[i]); + else + assertEquals(0, array[i]); + } + } + + @Test void forEach() { + var alg = new BigDogArray_F64(2, 10, BigDogGrowth.GROW); + alg.resize(22); + for (int i = 0; i < alg.size; i++) { + alg.set(i, (double)(i + 1)); + } + + DogArray_F64 results = new DogArray_F64(); + alg.forEach(1, 12, results::add); + assertEquals(11, results.size); + for (int i = 0; i < 11; i++) { + assertEquals(i + 2, results.get(i)); + } + } + + @Test void forIdx() { + var alg = new BigDogArray_F64(2, 10, BigDogGrowth.GROW); + alg.resize(22); + for (int i = 0; i < alg.size; i++) { + alg.set(i, (double)(i + 1)); + } + + DogArray_F64 results = new DogArray_F64(12); + results.resize(12); + alg.forIdx(1, 12, results::set); + assertEquals(0, results.get(0)); + for (int i = 1; i < 12; i++) { + assertEquals(i + 1, results.get(i)); + } + } + + @Test void applyIdx() { + var alg = new BigDogArray_F64(2, 10, BigDogGrowth.GROW); + alg.resize(22); + for (int i = 0; i < alg.size; i++) { + alg.set(i, (double)(i + 1)); + } + + alg.applyIdx(1, 12, ( idx, val ) -> (double)(idx*2)); + for (int i = 0; i < alg.size; i++) { + if (i >= 1 && i < 12) { + assertEquals(i*2, alg.get(i)); + } else { + assertEquals(i + 1, alg.get(i)); + } + } + } + + @Test void processByBlock() { + var alg = new BigDogArray_F64(2, 10, BigDogGrowth.GROW); + alg.resize(26); + alg.processByBlock(1, alg.size - 1, ( block, idx0, idx1, offset ) -> { + for (int i = idx0; i < idx1; i++) { + block[i] = (double)(i - idx0 + offset); + } + }); + for (int i = 0; i < alg.size; i++) { + if (i >= 1 && i < alg.size - 1) { + assertEquals((double)(i - 1), alg.get(i)); + } else { + assertEquals(0, alg.get(i)); + } + } + } + + @Override public BigDogArrayBase createBigDog( int initialAllocation, int blockSize, BigDogGrowth growth ) { + return new BigDogArray_F64(initialAllocation, blockSize, growth); + } +} diff -Nru ddogleg-0.18+ds/test/org/ddogleg/struct/TestBigDogArray_I32.java ddogleg-0.22+ds/test/org/ddogleg/struct/TestBigDogArray_I32.java --- ddogleg-0.18+ds/test/org/ddogleg/struct/TestBigDogArray_I32.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/struct/TestBigDogArray_I32.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,328 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.struct; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * @author Peter Abeles + */ +public class TestBigDogArray_I32 extends ChecksBigDogArray { + + /** resize with a set value */ + @Test void resizeValue() { + checkResizeValue(BigDogGrowth.GROW_FIRST); + checkResizeValue(BigDogGrowth.GROW); + checkResizeValue(BigDogGrowth.FIXED); + } + + /** Check resize by seeing if the size changed then checking the values */ + private void checkResizeValue( BigDogGrowth growth ) { + int blockSize = 10; + + var alg = new BigDogArray_I32(1, blockSize, growth); + + // give it some values that we know and this is within the first block + alg.resize(blockSize - 4, (int)4); + alg.resize(blockSize - 1, (int)3); + assertEquals(blockSize - 1, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals(i < blockSize - 4 ? 4 : 3, alg.get(i)); + } + + // Resize past the first block + alg.resize(blockSize + 3, (int)3); + assertEquals(blockSize + 3, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals(i < blockSize - 4 ? 4 : 3, alg.get(i)); + } + + // Fill this all in with a known value + alg.fill(0, alg.size, (int)5); + // Resize past the second block + alg.resize(blockSize*2 + 1, (int)6); + assertEquals(blockSize*2 + 1, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals(i < blockSize + 3 ? 5 : 6, alg.get(i)); + } + } + + /** resize only */ + @Test void resize() { + checkResize(BigDogGrowth.GROW_FIRST); + checkResize(BigDogGrowth.GROW); + checkResize(BigDogGrowth.FIXED); + } + + /** Check resize by seeing if the size changed then checking the values */ + private void checkResize( BigDogGrowth growth ) { + int blockSize = 10; + + var alg = new BigDogArray_I32(1, blockSize, growth); + + // give it some values that we know and this is within the first block + alg.resize(blockSize - 4, (int)4); + alg.resize(blockSize - 1); + assertEquals(blockSize - 1, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals(i < blockSize - 4 ? 4 : 0, alg.get(i)); + } + + // Resize past the first block + alg.resize(blockSize + 3); + assertEquals(blockSize + 3, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals(i < blockSize - 4 ? 4 : 0, alg.get(i)); + } + + // Fill this all in with a known value + alg.fill(0, alg.size, (int)5); + // Resize past the second block + alg.resize(blockSize*2 + 1); + assertEquals(blockSize*2 + 1, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals(i < blockSize + 3 ? 5 : 0, alg.get(i)); + } + } + + @Test void appendArray() { + for (BigDogGrowth growth : BigDogGrowth.values()) { + checkAppendArray(1, growth, 5); + checkAppendArray(1, growth, 12); + checkAppendArray(4, growth, 8); + } + } + + private void checkAppendArray( int initialSize, BigDogGrowth growth, int arraySize ) { + int[] array = new int[arraySize]; + for (int i = 0; i < arraySize; i++) { + array[i] = (int)(i + 1); + } + + var alg = new BigDogArray_I32(initialSize, 10, growth); + alg.resize(initialSize); + alg.append(array, 1, arraySize - 1); + assertTrue(alg.isValidStructure()); + assertEquals(initialSize + arraySize - 1, alg.size); + for (int i = 0; i < initialSize; i++) { + assertEquals(0, alg.get(i)); + } + for (int i = initialSize; i < alg.size; i++) { + assertEquals(array[i - initialSize + 1], alg.get(i)); + } + } + + @Test void appendValue() { + for (BigDogGrowth growth : BigDogGrowth.values()) { + checkAppendValue(1, growth); + checkAppendValue(4, growth); + } + } + + private void checkAppendValue( int initialSize, BigDogGrowth growth ) { + var alg = new BigDogArray_I32(initialSize, 10, growth); + alg.resize(initialSize); + + for (int i = 0; i < 21; i++) { + alg.append((int)(i + 1)); + assertEquals(initialSize + i + 1, alg.size); + assertTrue(alg.isValidStructure()); + } + assertEquals(3, alg.blocks.size); + + for (int i = 0; i < initialSize; i++) { + assertEquals(0, alg.get(i)); + } + for (int i = initialSize; i < alg.size; i++) { + assertEquals(i + 1 - initialSize, alg.get(i)); + } + + // Do it over again with a predeclared array + alg.reserve(45); + alg.resize(initialSize); + + for (int i = 0; i < 10; i++) { + alg.append((int)(i + 1)); + assertEquals(initialSize + i + 1, alg.size); + assertTrue(alg.isValidStructure()); + } + + for (int i = 0; i < initialSize; i++) { + assertEquals(0, alg.get(i)); + } + for (int i = initialSize; i < alg.size; i++) { + assertEquals(i + 1 - initialSize, alg.get(i)); + } + } + + @Test void fill() { + for (BigDogGrowth growth : BigDogGrowth.values()) { + checkFill(growth); + } + } + + private void checkFill( BigDogGrowth growth ) { + var alg = new BigDogArray_I32(1, 10, growth); + alg.resize(4, (int)2); + alg.resize(11, (int)10); + alg.fill(0, 3, (int)1); + alg.fill(3, 9, (int)2); + + for (int i = 0; i < 3; i++) { + assertEquals(1, alg.get(i)); + } + for (int i = 3; i < 9; i++) { + assertEquals(2, alg.get(i)); + } + assertEquals(10, alg.get(9)); + assertEquals(10, alg.get(10)); + + // fill across block boundary + alg.fill(0, alg.size, (int)4); + for (int i = 3; i < alg.size; i++) { + assertEquals(4, alg.get(i)); + } + } + + @Test void setArray() { + var alg = new BigDogArray_I32(2, 10, BigDogGrowth.GROW); + alg.resize(25); + + int[] array = new int[21]; + for (int i = 0; i < array.length; i++) { + array[i] = (int)(i + 1); + } + + alg.setArray(1, array, 1, array.length - 1); + for (int i = 1; i < array.length; i++) { + assertEquals(array[i], alg.get(i)); + } + } + + @Test void set_get() { + var alg = new BigDogArray_I32(2, 10, BigDogGrowth.GROW); + alg.resize(25); + + for (int i = 0; i < 25; i++) { + alg.set(i, (int)(i + 1)); + assertEquals(i + 1, alg.get(i)); + } + } + + @Test void setTail_getTail() { + var alg = new BigDogArray_I32(2, 10, BigDogGrowth.GROW); + alg.resize(25); + + for (int i = 0; i < 25; i++) { + alg.setTail(i, (int)(i + 1)); + assertEquals(i + 1, alg.getTail(i)); + } + + assertEquals(alg.get(alg.size-1), alg.getTail(0)); + assertEquals(alg.get(alg.size-2), alg.getTail(1)); + } + + @Test void getArray() { + var alg = new BigDogArray_I32(2, 10, BigDogGrowth.GROW); + alg.resize(22); + for (int i = 0; i < alg.size; i++) { + alg.set(i, (int)(i + 1)); + } + int[] array = new int[15]; + alg.getArray(2, array, 1, 12); + for (int i = 0; i < array.length; i++) { + if (i >= 1 && i < 13) + assertEquals(2 + i, array[i]); + else + assertEquals(0, array[i]); + } + } + + @Test void forEach() { + var alg = new BigDogArray_I32(2, 10, BigDogGrowth.GROW); + alg.resize(22); + for (int i = 0; i < alg.size; i++) { + alg.set(i, (int)(i + 1)); + } + + DogArray_I32 results = new DogArray_I32(); + alg.forEach(1, 12, results::add); + assertEquals(11, results.size); + for (int i = 0; i < 11; i++) { + assertEquals(i + 2, results.get(i)); + } + } + + @Test void forIdx() { + var alg = new BigDogArray_I32(2, 10, BigDogGrowth.GROW); + alg.resize(22); + for (int i = 0; i < alg.size; i++) { + alg.set(i, (int)(i + 1)); + } + + DogArray_I32 results = new DogArray_I32(12); + results.resize(12); + alg.forIdx(1, 12, results::set); + assertEquals(0, results.get(0)); + for (int i = 1; i < 12; i++) { + assertEquals(i + 1, results.get(i)); + } + } + + @Test void applyIdx() { + var alg = new BigDogArray_I32(2, 10, BigDogGrowth.GROW); + alg.resize(22); + for (int i = 0; i < alg.size; i++) { + alg.set(i, (int)(i + 1)); + } + + alg.applyIdx(1, 12, ( idx, val ) -> (int)(idx*2)); + for (int i = 0; i < alg.size; i++) { + if (i >= 1 && i < 12) { + assertEquals(i*2, alg.get(i)); + } else { + assertEquals(i + 1, alg.get(i)); + } + } + } + + @Test void processByBlock() { + var alg = new BigDogArray_I32(2, 10, BigDogGrowth.GROW); + alg.resize(26); + alg.processByBlock(1, alg.size - 1, ( block, idx0, idx1, offset ) -> { + for (int i = idx0; i < idx1; i++) { + block[i] = (int)(i - idx0 + offset); + } + }); + for (int i = 0; i < alg.size; i++) { + if (i >= 1 && i < alg.size - 1) { + assertEquals((int)(i - 1), alg.get(i)); + } else { + assertEquals(0, alg.get(i)); + } + } + } + + @Override public BigDogArrayBase createBigDog( int initialAllocation, int blockSize, BigDogGrowth growth ) { + return new BigDogArray_I32(initialAllocation, blockSize, growth); + } +} diff -Nru ddogleg-0.18+ds/test/org/ddogleg/struct/TestBigDogArray_I64.java ddogleg-0.22+ds/test/org/ddogleg/struct/TestBigDogArray_I64.java --- ddogleg-0.18+ds/test/org/ddogleg/struct/TestBigDogArray_I64.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/struct/TestBigDogArray_I64.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,328 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.struct; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * @author Peter Abeles + */ +public class TestBigDogArray_I64 extends ChecksBigDogArray { + + /** resize with a set value */ + @Test void resizeValue() { + checkResizeValue(BigDogGrowth.GROW_FIRST); + checkResizeValue(BigDogGrowth.GROW); + checkResizeValue(BigDogGrowth.FIXED); + } + + /** Check resize by seeing if the size changed then checking the values */ + private void checkResizeValue( BigDogGrowth growth ) { + int blockSize = 10; + + var alg = new BigDogArray_I64(1, blockSize, growth); + + // give it some values that we know and this is within the first block + alg.resize(blockSize - 4, (long)4); + alg.resize(blockSize - 1, (long)3); + assertEquals(blockSize - 1, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals(i < blockSize - 4 ? 4 : 3, alg.get(i)); + } + + // Resize past the first block + alg.resize(blockSize + 3, (long)3); + assertEquals(blockSize + 3, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals(i < blockSize - 4 ? 4 : 3, alg.get(i)); + } + + // Fill this all in with a known value + alg.fill(0, alg.size, (long)5); + // Resize past the second block + alg.resize(blockSize*2 + 1, (long)6); + assertEquals(blockSize*2 + 1, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals(i < blockSize + 3 ? 5 : 6, alg.get(i)); + } + } + + /** resize only */ + @Test void resize() { + checkResize(BigDogGrowth.GROW_FIRST); + checkResize(BigDogGrowth.GROW); + checkResize(BigDogGrowth.FIXED); + } + + /** Check resize by seeing if the size changed then checking the values */ + private void checkResize( BigDogGrowth growth ) { + int blockSize = 10; + + var alg = new BigDogArray_I64(1, blockSize, growth); + + // give it some values that we know and this is within the first block + alg.resize(blockSize - 4, (long)4); + alg.resize(blockSize - 1); + assertEquals(blockSize - 1, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals(i < blockSize - 4 ? 4 : 0, alg.get(i)); + } + + // Resize past the first block + alg.resize(blockSize + 3); + assertEquals(blockSize + 3, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals(i < blockSize - 4 ? 4 : 0, alg.get(i)); + } + + // Fill this all in with a known value + alg.fill(0, alg.size, (long)5); + // Resize past the second block + alg.resize(blockSize*2 + 1); + assertEquals(blockSize*2 + 1, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals(i < blockSize + 3 ? 5 : 0, alg.get(i)); + } + } + + @Test void appendArray() { + for (BigDogGrowth growth : BigDogGrowth.values()) { + checkAppendArray(1, growth, 5); + checkAppendArray(1, growth, 12); + checkAppendArray(4, growth, 8); + } + } + + private void checkAppendArray( int initialSize, BigDogGrowth growth, int arraySize ) { + long[] array = new long[arraySize]; + for (int i = 0; i < arraySize; i++) { + array[i] = (long)(i + 1); + } + + var alg = new BigDogArray_I64(initialSize, 10, growth); + alg.resize(initialSize); + alg.append(array, 1, arraySize - 1); + assertTrue(alg.isValidStructure()); + assertEquals(initialSize + arraySize - 1, alg.size); + for (int i = 0; i < initialSize; i++) { + assertEquals(0, alg.get(i)); + } + for (int i = initialSize; i < alg.size; i++) { + assertEquals(array[i - initialSize + 1], alg.get(i)); + } + } + + @Test void appendValue() { + for (BigDogGrowth growth : BigDogGrowth.values()) { + checkAppendValue(1, growth); + checkAppendValue(4, growth); + } + } + + private void checkAppendValue( int initialSize, BigDogGrowth growth ) { + var alg = new BigDogArray_I64(initialSize, 10, growth); + alg.resize(initialSize); + + for (int i = 0; i < 21; i++) { + alg.append((long)(i + 1)); + assertEquals(initialSize + i + 1, alg.size); + assertTrue(alg.isValidStructure()); + } + assertEquals(3, alg.blocks.size); + + for (int i = 0; i < initialSize; i++) { + assertEquals(0, alg.get(i)); + } + for (int i = initialSize; i < alg.size; i++) { + assertEquals(i + 1 - initialSize, alg.get(i)); + } + + // Do it over again with a predeclared array + alg.reserve(45); + alg.resize(initialSize); + + for (int i = 0; i < 10; i++) { + alg.append((long)(i + 1)); + assertEquals(initialSize + i + 1, alg.size); + assertTrue(alg.isValidStructure()); + } + + for (int i = 0; i < initialSize; i++) { + assertEquals(0, alg.get(i)); + } + for (int i = initialSize; i < alg.size; i++) { + assertEquals(i + 1 - initialSize, alg.get(i)); + } + } + + @Test void fill() { + for (BigDogGrowth growth : BigDogGrowth.values()) { + checkFill(growth); + } + } + + private void checkFill( BigDogGrowth growth ) { + var alg = new BigDogArray_I64(1, 10, growth); + alg.resize(4, (long)2); + alg.resize(11, (long)10); + alg.fill(0, 3, (long)1); + alg.fill(3, 9, (long)2); + + for (int i = 0; i < 3; i++) { + assertEquals(1, alg.get(i)); + } + for (int i = 3; i < 9; i++) { + assertEquals(2, alg.get(i)); + } + assertEquals(10, alg.get(9)); + assertEquals(10, alg.get(10)); + + // fill across block boundary + alg.fill(0, alg.size, (long)4); + for (int i = 3; i < alg.size; i++) { + assertEquals(4, alg.get(i)); + } + } + + @Test void setArray() { + var alg = new BigDogArray_I64(2, 10, BigDogGrowth.GROW); + alg.resize(25); + + long[] array = new long[21]; + for (int i = 0; i < array.length; i++) { + array[i] = (long)(i + 1); + } + + alg.setArray(1, array, 1, array.length - 1); + for (int i = 1; i < array.length; i++) { + assertEquals(array[i], alg.get(i)); + } + } + + @Test void set_get() { + var alg = new BigDogArray_I64(2, 10, BigDogGrowth.GROW); + alg.resize(25); + + for (int i = 0; i < 25; i++) { + alg.set(i, (long)(i + 1)); + assertEquals(i + 1, alg.get(i)); + } + } + + @Test void setTail_getTail() { + var alg = new BigDogArray_I64(2, 10, BigDogGrowth.GROW); + alg.resize(25); + + for (int i = 0; i < 25; i++) { + alg.setTail(i, (long)(i + 1)); + assertEquals(i + 1, alg.getTail(i)); + } + + assertEquals(alg.get(alg.size-1), alg.getTail(0)); + assertEquals(alg.get(alg.size-2), alg.getTail(1)); + } + + @Test void getArray() { + var alg = new BigDogArray_I64(2, 10, BigDogGrowth.GROW); + alg.resize(22); + for (int i = 0; i < alg.size; i++) { + alg.set(i, (long)(i + 1)); + } + long[] array = new long[15]; + alg.getArray(2, array, 1, 12); + for (int i = 0; i < array.length; i++) { + if (i >= 1 && i < 13) + assertEquals(2 + i, array[i]); + else + assertEquals(0, array[i]); + } + } + + @Test void forEach() { + var alg = new BigDogArray_I64(2, 10, BigDogGrowth.GROW); + alg.resize(22); + for (int i = 0; i < alg.size; i++) { + alg.set(i, (long)(i + 1)); + } + + DogArray_I64 results = new DogArray_I64(); + alg.forEach(1, 12, results::add); + assertEquals(11, results.size); + for (int i = 0; i < 11; i++) { + assertEquals(i + 2, results.get(i)); + } + } + + @Test void forIdx() { + var alg = new BigDogArray_I64(2, 10, BigDogGrowth.GROW); + alg.resize(22); + for (int i = 0; i < alg.size; i++) { + alg.set(i, (long)(i + 1)); + } + + DogArray_I64 results = new DogArray_I64(12); + results.resize(12); + alg.forIdx(1, 12, results::set); + assertEquals(0, results.get(0)); + for (int i = 1; i < 12; i++) { + assertEquals(i + 1, results.get(i)); + } + } + + @Test void applyIdx() { + var alg = new BigDogArray_I64(2, 10, BigDogGrowth.GROW); + alg.resize(22); + for (int i = 0; i < alg.size; i++) { + alg.set(i, (long)(i + 1)); + } + + alg.applyIdx(1, 12, ( idx, val ) -> (long)(idx*2)); + for (int i = 0; i < alg.size; i++) { + if (i >= 1 && i < 12) { + assertEquals(i*2, alg.get(i)); + } else { + assertEquals(i + 1, alg.get(i)); + } + } + } + + @Test void processByBlock() { + var alg = new BigDogArray_I64(2, 10, BigDogGrowth.GROW); + alg.resize(26); + alg.processByBlock(1, alg.size - 1, ( block, idx0, idx1, offset ) -> { + for (int i = idx0; i < idx1; i++) { + block[i] = (long)(i - idx0 + offset); + } + }); + for (int i = 0; i < alg.size; i++) { + if (i >= 1 && i < alg.size - 1) { + assertEquals((long)(i - 1), alg.get(i)); + } else { + assertEquals(0, alg.get(i)); + } + } + } + + @Override public BigDogArrayBase createBigDog( int initialAllocation, int blockSize, BigDogGrowth growth ) { + return new BigDogArray_I64(initialAllocation, blockSize, growth); + } +} diff -Nru ddogleg-0.18+ds/test/org/ddogleg/struct/TestBigDogArray_I8.java ddogleg-0.22+ds/test/org/ddogleg/struct/TestBigDogArray_I8.java --- ddogleg-0.18+ds/test/org/ddogleg/struct/TestBigDogArray_I8.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/struct/TestBigDogArray_I8.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,328 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.struct; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * @author Peter Abeles + */ +public class TestBigDogArray_I8 extends ChecksBigDogArray { + + /** resize with a set value */ + @Test void resizeValue() { + checkResizeValue(BigDogGrowth.GROW_FIRST); + checkResizeValue(BigDogGrowth.GROW); + checkResizeValue(BigDogGrowth.FIXED); + } + + /** Check resize by seeing if the size changed then checking the values */ + private void checkResizeValue( BigDogGrowth growth ) { + int blockSize = 10; + + var alg = new BigDogArray_I8(1, blockSize, growth); + + // give it some values that we know and this is within the first block + alg.resize(blockSize - 4, (byte)4); + alg.resize(blockSize - 1, (byte)3); + assertEquals(blockSize - 1, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals(i < blockSize - 4 ? 4 : 3, alg.get(i)); + } + + // Resize past the first block + alg.resize(blockSize + 3, (byte)3); + assertEquals(blockSize + 3, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals(i < blockSize - 4 ? 4 : 3, alg.get(i)); + } + + // Fill this all in with a known value + alg.fill(0, alg.size, (byte)5); + // Resize past the second block + alg.resize(blockSize*2 + 1, (byte)6); + assertEquals(blockSize*2 + 1, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals(i < blockSize + 3 ? 5 : 6, alg.get(i)); + } + } + + /** resize only */ + @Test void resize() { + checkResize(BigDogGrowth.GROW_FIRST); + checkResize(BigDogGrowth.GROW); + checkResize(BigDogGrowth.FIXED); + } + + /** Check resize by seeing if the size changed then checking the values */ + private void checkResize( BigDogGrowth growth ) { + int blockSize = 10; + + var alg = new BigDogArray_I8(1, blockSize, growth); + + // give it some values that we know and this is within the first block + alg.resize(blockSize - 4, (byte)4); + alg.resize(blockSize - 1); + assertEquals(blockSize - 1, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals(i < blockSize - 4 ? 4 : 0, alg.get(i)); + } + + // Resize past the first block + alg.resize(blockSize + 3); + assertEquals(blockSize + 3, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals(i < blockSize - 4 ? 4 : 0, alg.get(i)); + } + + // Fill this all in with a known value + alg.fill(0, alg.size, (byte)5); + // Resize past the second block + alg.resize(blockSize*2 + 1); + assertEquals(blockSize*2 + 1, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals(i < blockSize + 3 ? 5 : 0, alg.get(i)); + } + } + + @Test void appendArray() { + for (BigDogGrowth growth : BigDogGrowth.values()) { + checkAppendArray(1, growth, 5); + checkAppendArray(1, growth, 12); + checkAppendArray(4, growth, 8); + } + } + + private void checkAppendArray( int initialSize, BigDogGrowth growth, int arraySize ) { + byte[] array = new byte[arraySize]; + for (int i = 0; i < arraySize; i++) { + array[i] = (byte)(i + 1); + } + + var alg = new BigDogArray_I8(initialSize, 10, growth); + alg.resize(initialSize); + alg.append(array, 1, arraySize - 1); + assertTrue(alg.isValidStructure()); + assertEquals(initialSize + arraySize - 1, alg.size); + for (int i = 0; i < initialSize; i++) { + assertEquals(0, alg.get(i)); + } + for (int i = initialSize; i < alg.size; i++) { + assertEquals(array[i - initialSize + 1], alg.get(i)); + } + } + + @Test void appendValue() { + for (BigDogGrowth growth : BigDogGrowth.values()) { + checkAppendValue(1, growth); + checkAppendValue(4, growth); + } + } + + private void checkAppendValue( int initialSize, BigDogGrowth growth ) { + var alg = new BigDogArray_I8(initialSize, 10, growth); + alg.resize(initialSize); + + for (int i = 0; i < 21; i++) { + alg.append((byte)(i + 1)); + assertEquals(initialSize + i + 1, alg.size); + assertTrue(alg.isValidStructure()); + } + assertEquals(3, alg.blocks.size); + + for (int i = 0; i < initialSize; i++) { + assertEquals(0, alg.get(i)); + } + for (int i = initialSize; i < alg.size; i++) { + assertEquals(i + 1 - initialSize, alg.get(i)); + } + + // Do it over again with a predeclared array + alg.reserve(45); + alg.resize(initialSize); + + for (int i = 0; i < 10; i++) { + alg.append((byte)(i + 1)); + assertEquals(initialSize + i + 1, alg.size); + assertTrue(alg.isValidStructure()); + } + + for (int i = 0; i < initialSize; i++) { + assertEquals(0, alg.get(i)); + } + for (int i = initialSize; i < alg.size; i++) { + assertEquals(i + 1 - initialSize, alg.get(i)); + } + } + + @Test void fill() { + for (BigDogGrowth growth : BigDogGrowth.values()) { + checkFill(growth); + } + } + + private void checkFill( BigDogGrowth growth ) { + var alg = new BigDogArray_I8(1, 10, growth); + alg.resize(4, (byte)2); + alg.resize(11, (byte)10); + alg.fill(0, 3, (byte)1); + alg.fill(3, 9, (byte)2); + + for (int i = 0; i < 3; i++) { + assertEquals(1, alg.get(i)); + } + for (int i = 3; i < 9; i++) { + assertEquals(2, alg.get(i)); + } + assertEquals(10, alg.get(9)); + assertEquals(10, alg.get(10)); + + // fill across block boundary + alg.fill(0, alg.size, (byte)4); + for (int i = 3; i < alg.size; i++) { + assertEquals(4, alg.get(i)); + } + } + + @Test void setArray() { + var alg = new BigDogArray_I8(2, 10, BigDogGrowth.GROW); + alg.resize(25); + + byte[] array = new byte[21]; + for (int i = 0; i < array.length; i++) { + array[i] = (byte)(i + 1); + } + + alg.setArray(1, array, 1, array.length - 1); + for (int i = 1; i < array.length; i++) { + assertEquals(array[i], alg.get(i)); + } + } + + @Test void set_get() { + var alg = new BigDogArray_I8(2, 10, BigDogGrowth.GROW); + alg.resize(25); + + for (int i = 0; i < 25; i++) { + alg.set(i, (byte)(i + 1)); + assertEquals(i + 1, alg.get(i)); + } + } + + @Test void setTail_getTail() { + var alg = new BigDogArray_I8(2, 10, BigDogGrowth.GROW); + alg.resize(25); + + for (int i = 0; i < 25; i++) { + alg.setTail(i, (byte)(i + 1)); + assertEquals(i + 1, alg.getTail(i)); + } + + assertEquals(alg.get(alg.size-1), alg.getTail(0)); + assertEquals(alg.get(alg.size-2), alg.getTail(1)); + } + + @Test void getArray() { + var alg = new BigDogArray_I8(2, 10, BigDogGrowth.GROW); + alg.resize(22); + for (int i = 0; i < alg.size; i++) { + alg.set(i, (byte)(i + 1)); + } + byte[] array = new byte[15]; + alg.getArray(2, array, 1, 12); + for (int i = 0; i < array.length; i++) { + if (i >= 1 && i < 13) + assertEquals(2 + i, array[i]); + else + assertEquals(0, array[i]); + } + } + + @Test void forEach() { + var alg = new BigDogArray_I8(2, 10, BigDogGrowth.GROW); + alg.resize(22); + for (int i = 0; i < alg.size; i++) { + alg.set(i, (byte)(i + 1)); + } + + DogArray_I8 results = new DogArray_I8(); + alg.forEach(1, 12, results::add); + assertEquals(11, results.size); + for (int i = 0; i < 11; i++) { + assertEquals(i + 2, results.get(i)); + } + } + + @Test void forIdx() { + var alg = new BigDogArray_I8(2, 10, BigDogGrowth.GROW); + alg.resize(22); + for (int i = 0; i < alg.size; i++) { + alg.set(i, (byte)(i + 1)); + } + + DogArray_I8 results = new DogArray_I8(12); + results.resize(12); + alg.forIdx(1, 12, results::set); + assertEquals(0, results.get(0)); + for (int i = 1; i < 12; i++) { + assertEquals(i + 1, results.get(i)); + } + } + + @Test void applyIdx() { + var alg = new BigDogArray_I8(2, 10, BigDogGrowth.GROW); + alg.resize(22); + for (int i = 0; i < alg.size; i++) { + alg.set(i, (byte)(i + 1)); + } + + alg.applyIdx(1, 12, ( idx, val ) -> (byte)(idx*2)); + for (int i = 0; i < alg.size; i++) { + if (i >= 1 && i < 12) { + assertEquals(i*2, alg.get(i)); + } else { + assertEquals(i + 1, alg.get(i)); + } + } + } + + @Test void processByBlock() { + var alg = new BigDogArray_I8(2, 10, BigDogGrowth.GROW); + alg.resize(26); + alg.processByBlock(1, alg.size - 1, ( block, idx0, idx1, offset ) -> { + for (int i = idx0; i < idx1; i++) { + block[i] = (byte)(i - idx0 + offset); + } + }); + for (int i = 0; i < alg.size; i++) { + if (i >= 1 && i < alg.size - 1) { + assertEquals((byte)(i - 1), alg.get(i)); + } else { + assertEquals(0, alg.get(i)); + } + } + } + + @Override public BigDogArrayBase createBigDog( int initialAllocation, int blockSize, BigDogGrowth growth ) { + return new BigDogArray_I8(initialAllocation, blockSize, growth); + } +} diff -Nru ddogleg-0.18+ds/test/org/ddogleg/struct/TestCircularArray_F32.java ddogleg-0.22+ds/test/org/ddogleg/struct/TestCircularArray_F32.java --- ddogleg-0.18+ds/test/org/ddogleg/struct/TestCircularArray_F32.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/struct/TestCircularArray_F32.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.struct; + +import org.ejml.UtilEjml; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * @author Peter Abeles + */ +public class TestCircularArray_F32 { + + @Test + public void popHead() { + CircularArray_F32 alg = new CircularArray_F32(); + + alg.add(1); + alg.add(2); + assertEquals(1,alg.popHead(), UtilEjml.TEST_F64); + assertEquals(1,alg.size()); + + assertEquals(2,alg.popHead(), UtilEjml.TEST_F64); + assertEquals(0,alg.size()); + } + + @Test + public void popTail() { + CircularArray_F32 alg = new CircularArray_F32(); + + alg.add(1); + alg.add(2); + assertEquals(2,alg.popTail(), UtilEjml.TEST_F64); + assertEquals(1,alg.size()); + + assertEquals(1,alg.popTail(), UtilEjml.TEST_F64); + assertEquals(0, alg.size()); + } + + @Test + public void head() { + CircularArray_F32 alg = new CircularArray_F32(); + + alg.add(1); + assertEquals(1, alg.head(), UtilEjml.TEST_F64); + alg.add(3); + assertEquals(1,alg.head(), UtilEjml.TEST_F64); + } + + @Test + public void head_offset() { + CircularArray_F32 alg = new CircularArray_F32(3); + + alg.start = 2; + alg.size = 0; + + alg.add(1); + assertEquals(1, alg.head(), UtilEjml.TEST_F64); + alg.add(3); + assertEquals(1,alg.head(), UtilEjml.TEST_F64); + } + + @Test + public void tail() { + CircularArray_F32 alg = new CircularArray_F32(); + + alg.add(1); + assertEquals(1,alg.tail(), UtilEjml.TEST_F64); + alg.add(3); + assertEquals(3, alg.tail(), UtilEjml.TEST_F64); + } + + @Test + public void tail_offset() { + CircularArray_F32 alg = new CircularArray_F32(3); + + alg.start = 2; + alg.size = 0; + + alg.add(1); + assertEquals(1,alg.tail(), UtilEjml.TEST_F64); + alg.add(3); + assertEquals(3, alg.tail(), UtilEjml.TEST_F64); + } + + @Test + public void removeHead() { + CircularArray_F32 alg = new CircularArray_F32(); + + alg.add(1); + alg.add(2); + alg.removeHead(); + assertEquals(2, alg.head(), UtilEjml.TEST_F64); + assertEquals(1, alg.size()); + + alg.removeHead(); + assertEquals(0, alg.size()); + } + + @Test + public void removeTail() { + CircularArray_F32 alg = new CircularArray_F32(); + + alg.add(1); + alg.add(2); + alg.removeTail(); + assertEquals(1,alg.head(), UtilEjml.TEST_F64); + assertEquals(1,alg.size()); + + alg.removeTail(); + assertEquals(0, alg.size()); + } + + @Test + public void get() { + CircularArray_F32 alg = new CircularArray_F32(2); + assertEquals(2,alg.data.length); + + // easy case + alg.add(1); + alg.add(2); + + assertEquals(1,alg.get(0), UtilEjml.TEST_F64); + assertEquals(2,alg.get(1), UtilEjml.TEST_F64); + + // make there be an offset + alg.removeHead(); + alg.add(3); + assertEquals(2,alg.data.length); // sanity check + assertEquals(2,alg.get(0), UtilEjml.TEST_F64); + assertEquals(3,alg.get(1), UtilEjml.TEST_F64); + } + + @Test + public void add() { + CircularArray_F32 alg = new CircularArray_F32(3); + assertEquals(3,alg.data.length); + + alg.add(1); + assertEquals(1,alg.data[0], UtilEjml.TEST_F64); + assertEquals(1,alg.size); + + alg.add(2); + assertEquals(1,alg.data[0], UtilEjml.TEST_F64); + assertEquals(2,alg.data[1], UtilEjml.TEST_F64); + assertEquals(2,alg.size); + + // see if it over writes + alg.add(3); + alg.add(4); + assertEquals(4,alg.data[0], UtilEjml.TEST_F64); + assertEquals(2,alg.data[1], UtilEjml.TEST_F64); + assertEquals(3,alg.data[2], UtilEjml.TEST_F64); + assertEquals(3,alg.size); + assertEquals(1,alg.start); + + // wrap around case + alg.start = 1; + alg.size = 2; + alg.data = new float[3]; + alg.add(10); + assertEquals(10,alg.data[0], UtilEjml.TEST_F64); + assertEquals(3,alg.size); + } + + @Test + public void isEmpty() { + CircularArray_F32 alg = new CircularArray_F32(3); + + assertTrue(alg.isEmpty()); + alg.add(5); + assertFalse(alg.isEmpty()); + alg.removeTail(); + assertTrue(alg.isEmpty()); + + } + + @Test + public void reset() { + CircularArray_F32 alg = new CircularArray_F32(3); + + alg.start = 2; + alg.size = 5; + + alg.reset(); + + assertEquals(0,alg.size); + assertEquals(0,alg.start); + } + + @Test + public void set_queue() { + CircularArray_F32 a = new CircularArray_F32(3); + + for (int i = 0; i < 4; i++) { + a.add(i); + } + + CircularArray_F32 b = new CircularArray_F32(10); + b.set(a); + + assertEquals(3,b.queueSize()); + for (int i = 0; i < a.data.length; i++) { + assertEquals(a.data[i],b.data[i], UtilEjml.TEST_F64); + } + assertEquals(a.size,b.size); + assertEquals(a.start,b.start); + + } + + +} diff -Nru ddogleg-0.18+ds/test/org/ddogleg/struct/TestCircularArray_F64.java ddogleg-0.22+ds/test/org/ddogleg/struct/TestCircularArray_F64.java --- ddogleg-0.18+ds/test/org/ddogleg/struct/TestCircularArray_F64.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/struct/TestCircularArray_F64.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.struct; + +import org.ejml.UtilEjml; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * @author Peter Abeles + */ +public class TestCircularArray_F64 { + + @Test + public void popHead() { + CircularArray_F64 alg = new CircularArray_F64(); + + alg.add(1); + alg.add(2); + assertEquals(1,alg.popHead(), UtilEjml.TEST_F64); + assertEquals(1,alg.size()); + + assertEquals(2,alg.popHead(), UtilEjml.TEST_F64); + assertEquals(0,alg.size()); + } + + @Test + public void popTail() { + CircularArray_F64 alg = new CircularArray_F64(); + + alg.add(1); + alg.add(2); + assertEquals(2,alg.popTail(), UtilEjml.TEST_F64); + assertEquals(1,alg.size()); + + assertEquals(1,alg.popTail(), UtilEjml.TEST_F64); + assertEquals(0, alg.size()); + } + + @Test + public void head() { + CircularArray_F64 alg = new CircularArray_F64(); + + alg.add(1); + assertEquals(1, alg.head(), UtilEjml.TEST_F64); + alg.add(3); + assertEquals(1,alg.head(), UtilEjml.TEST_F64); + } + + @Test + public void head_offset() { + CircularArray_F64 alg = new CircularArray_F64(3); + + alg.start = 2; + alg.size = 0; + + alg.add(1); + assertEquals(1, alg.head(), UtilEjml.TEST_F64); + alg.add(3); + assertEquals(1,alg.head(), UtilEjml.TEST_F64); + } + + @Test + public void tail() { + CircularArray_F64 alg = new CircularArray_F64(); + + alg.add(1); + assertEquals(1,alg.tail(), UtilEjml.TEST_F64); + alg.add(3); + assertEquals(3, alg.tail(), UtilEjml.TEST_F64); + } + + @Test + public void tail_offset() { + CircularArray_F64 alg = new CircularArray_F64(3); + + alg.start = 2; + alg.size = 0; + + alg.add(1); + assertEquals(1,alg.tail(), UtilEjml.TEST_F64); + alg.add(3); + assertEquals(3, alg.tail(), UtilEjml.TEST_F64); + } + + @Test + public void removeHead() { + CircularArray_F64 alg = new CircularArray_F64(); + + alg.add(1); + alg.add(2); + alg.removeHead(); + assertEquals(2, alg.head(), UtilEjml.TEST_F64); + assertEquals(1, alg.size()); + + alg.removeHead(); + assertEquals(0, alg.size()); + } + + @Test + public void removeTail() { + CircularArray_F64 alg = new CircularArray_F64(); + + alg.add(1); + alg.add(2); + alg.removeTail(); + assertEquals(1,alg.head(), UtilEjml.TEST_F64); + assertEquals(1,alg.size()); + + alg.removeTail(); + assertEquals(0, alg.size()); + } + + @Test + public void get() { + CircularArray_F64 alg = new CircularArray_F64(2); + assertEquals(2,alg.data.length); + + // easy case + alg.add(1); + alg.add(2); + + assertEquals(1,alg.get(0), UtilEjml.TEST_F64); + assertEquals(2,alg.get(1), UtilEjml.TEST_F64); + + // make there be an offset + alg.removeHead(); + alg.add(3); + assertEquals(2,alg.data.length); // sanity check + assertEquals(2,alg.get(0), UtilEjml.TEST_F64); + assertEquals(3,alg.get(1), UtilEjml.TEST_F64); + } + + @Test + public void add() { + CircularArray_F64 alg = new CircularArray_F64(3); + assertEquals(3,alg.data.length); + + alg.add(1); + assertEquals(1,alg.data[0], UtilEjml.TEST_F64); + assertEquals(1,alg.size); + + alg.add(2); + assertEquals(1,alg.data[0], UtilEjml.TEST_F64); + assertEquals(2,alg.data[1], UtilEjml.TEST_F64); + assertEquals(2,alg.size); + + // see if it over writes + alg.add(3); + alg.add(4); + assertEquals(4,alg.data[0], UtilEjml.TEST_F64); + assertEquals(2,alg.data[1], UtilEjml.TEST_F64); + assertEquals(3,alg.data[2], UtilEjml.TEST_F64); + assertEquals(3,alg.size); + assertEquals(1,alg.start); + + // wrap around case + alg.start = 1; + alg.size = 2; + alg.data = new double[3]; + alg.add(10); + assertEquals(10,alg.data[0], UtilEjml.TEST_F64); + assertEquals(3,alg.size); + } + + @Test + public void isEmpty() { + CircularArray_F64 alg = new CircularArray_F64(3); + + assertTrue(alg.isEmpty()); + alg.add(5); + assertFalse(alg.isEmpty()); + alg.removeTail(); + assertTrue(alg.isEmpty()); + + } + + @Test + public void reset() { + CircularArray_F64 alg = new CircularArray_F64(3); + + alg.start = 2; + alg.size = 5; + + alg.reset(); + + assertEquals(0,alg.size); + assertEquals(0,alg.start); + } + + @Test + public void set_queue() { + CircularArray_F64 a = new CircularArray_F64(3); + + for (int i = 0; i < 4; i++) { + a.add(i); + } + + CircularArray_F64 b = new CircularArray_F64(10); + b.set(a); + + assertEquals(3,b.queueSize()); + for (int i = 0; i < a.data.length; i++) { + assertEquals(a.data[i],b.data[i], UtilEjml.TEST_F64); + } + assertEquals(a.size,b.size); + assertEquals(a.start,b.start); + + } +} diff -Nru ddogleg-0.18+ds/test/org/ddogleg/struct/TestCircularArray_I32.java ddogleg-0.22+ds/test/org/ddogleg/struct/TestCircularArray_I32.java --- ddogleg-0.18+ds/test/org/ddogleg/struct/TestCircularArray_I32.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/struct/TestCircularArray_I32.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.struct; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * @author Peter Abeles + */ +public class TestCircularArray_I32 { + + @Test + public void popHead() { + CircularArray_I32 alg = new CircularArray_I32(); + + alg.add(1); + alg.add(2); + assertEquals(1,alg.popHead()); + assertEquals(1,alg.size()); + + assertEquals(2,alg.popHead()); + assertEquals(0,alg.size()); + } + + @Test + public void popTail() { + CircularArray_I32 alg = new CircularArray_I32(); + + alg.add(1); + alg.add(2); + assertEquals(2,alg.popTail()); + assertEquals(1,alg.size()); + + assertEquals(1,alg.popTail()); + assertEquals(0, alg.size()); + } + + @Test + public void head() { + CircularArray_I32 alg = new CircularArray_I32(); + + alg.add(1); + assertEquals(1, alg.head()); + alg.add(3); + assertEquals(1,alg.head()); + } + + @Test + public void head_offset() { + CircularArray_I32 alg = new CircularArray_I32(3); + + alg.start = 2; + alg.size = 0; + + alg.add(1); + assertEquals(1, alg.head()); + alg.add(3); + assertEquals(1,alg.head()); + } + + @Test + public void tail() { + CircularArray_I32 alg = new CircularArray_I32(); + + alg.add(1); + assertEquals(1,alg.tail()); + alg.add(3); + assertEquals(3, alg.tail()); + } + + @Test + public void tail_offset() { + CircularArray_I32 alg = new CircularArray_I32(3); + + alg.start = 2; + alg.size = 0; + + alg.add(1); + assertEquals(1,alg.tail()); + alg.add(3); + assertEquals(3, alg.tail()); + } + + @Test + public void removeHead() { + CircularArray_I32 alg = new CircularArray_I32(); + + alg.add(1); + alg.add(2); + alg.removeHead(); + assertEquals(2, alg.head()); + assertEquals(1, alg.size()); + + alg.removeHead(); + assertEquals(0, alg.size()); + } + + @Test + public void removeTail() { + CircularArray_I32 alg = new CircularArray_I32(); + + alg.add(1); + alg.add(2); + alg.removeTail(); + assertEquals(1,alg.head()); + assertEquals(1,alg.size()); + + alg.removeTail(); + assertEquals(0, alg.size()); + } + + @Test + public void get() { + CircularArray_I32 alg = new CircularArray_I32(2); + assertEquals(2,alg.data.length); + + // easy case + alg.add(1); + alg.add(2); + + assertEquals(1,alg.get(0)); + assertEquals(2,alg.get(1)); + + // make there be an offset + alg.removeHead(); + alg.add(3); + assertEquals(2,alg.data.length); // sanity check + assertEquals(2,alg.get(0)); + assertEquals(3,alg.get(1)); + } + + @Test + public void add() { + CircularArray_I32 alg = new CircularArray_I32(3); + assertEquals(3,alg.data.length); + + alg.add(1); + assertEquals(1,alg.data[0]); + assertEquals(1,alg.size); + + alg.add(2); + assertEquals(1,alg.data[0]); + assertEquals(2,alg.data[1]); + assertEquals(2,alg.size); + + // see if it grows + alg.add(3); + alg.add(4); + assertEquals(1,alg.data[0]); + assertEquals(2,alg.data[1]); + assertEquals(3,alg.data[2]); + assertEquals(4,alg.data[3]); + assertEquals(4,alg.size); + + // grows with offset + alg.start = 1; + alg.data = new int[]{1,2,3}; + alg.size = 3; + alg.add(4); + assertEquals(2,alg.data[0]); + assertEquals(3,alg.data[1]); + assertEquals(1,alg.data[2]); + assertEquals(4,alg.data[3]); + assertEquals(4,alg.size); + + // wrap around case + alg.start = 1; + alg.size = 2; + alg.data = new int[3]; + alg.add(10); + assertEquals(10,alg.data[0]); + assertEquals(10,alg.data[0]); + assertEquals(3,alg.size); + + } + + @Test + public void addW() { + CircularArray_I32 alg = new CircularArray_I32(3); + assertEquals(3,alg.data.length); + + alg.addW(1); + assertEquals(1,alg.data[0]); + assertEquals(1,alg.size); + + alg.addW(2); + assertEquals(1,alg.data[0]); + assertEquals(2,alg.data[1]); + assertEquals(2,alg.size); + + // see if it over writes + alg.addW(3); + alg.addW(4); + assertEquals(4,alg.data[0]); + assertEquals(2,alg.data[1]); + assertEquals(3,alg.data[2]); + assertEquals(3,alg.size); + assertEquals(1,alg.start); + + // wrap around case + alg.start = 1; + alg.size = 2; + alg.data = new int[3]; + alg.addW(10); + assertEquals(10,alg.data[0]); + assertEquals(3,alg.size); + } + + @Test + public void isEmpty() { + CircularArray_I32 alg = new CircularArray_I32(3); + + assertTrue(alg.isEmpty()); + alg.add(5); + assertFalse(alg.isEmpty()); + alg.removeTail(); + assertTrue(alg.isEmpty()); + + } + + @Test + public void reset() { + CircularArray_I32 alg = new CircularArray_I32(3); + + alg.start = 2; + alg.size = 5; + + alg.reset(); + + assertEquals(0,alg.size); + assertEquals(0,alg.start); + } + + +} diff -Nru ddogleg-0.18+ds/test/org/ddogleg/struct/TestCircularArray.java ddogleg-0.22+ds/test/org/ddogleg/struct/TestCircularArray.java --- ddogleg-0.18+ds/test/org/ddogleg/struct/TestCircularArray.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/struct/TestCircularArray.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,303 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.struct; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * @author Peter Abeles + */ +public class TestCircularArray { + + @Test + public void popHead() { + CircularArray alg = new CircularArray(A.class); + + alg.grow().value = 1; + alg.grow().value = 2; + assertEquals(1,alg.popHead().value); + assertEquals(1,alg.size()); + + assertEquals(2,alg.popHead().value); + assertEquals(0,alg.size()); + } + + @Test + public void popTail() { + CircularArray alg = new CircularArray(A.class); + + alg.grow().value = 1; + alg.grow().value = 2; + assertEquals(2,alg.popTail().value); + assertEquals(1,alg.size()); + + assertEquals(1,alg.popTail().value); + assertEquals(0, alg.size()); + } + + @Test + public void head() { + CircularArray alg = new CircularArray(A.class); + + alg.grow().value = 1; + assertEquals(1, alg.head().value); + alg.grow().value = 3; + assertEquals(1,alg.head().value); + } + + @Test + public void head_offset() { + CircularArray alg = new CircularArray(A.class,3); + + alg.start = 2; + alg.size = 0; + + alg.grow().value = 1; + assertEquals(1, alg.head().value); + alg.grow().value = 3; + assertEquals(1,alg.head().value); + } + + @Test + public void tail() { + CircularArray alg = new CircularArray(A.class); + + alg.grow().value = 1; + assertEquals(1,alg.tail().value); + alg.grow().value = 3; + assertEquals(3, alg.tail().value); + } + + @Test + public void tail_offset() { + CircularArray alg = new CircularArray(A.class); + + alg.start = 2; + alg.size = 0; + + alg.grow().value = 1; + assertEquals(1,alg.tail().value); + alg.grow().value = 3; + assertEquals(3, alg.tail().value); + } + + @Test + public void removeHead() { + CircularArray alg = new CircularArray(A.class); + + alg.grow().value = 1; + alg.grow().value = 2; + alg.removeHead(); + assertEquals(2, alg.head().value); + assertEquals(1, alg.size()); + + alg.removeHead(); + assertEquals(0, alg.size()); + } + + @Test + public void removeTail() { + CircularArray alg = new CircularArray(A.class); + + alg.grow().value = 1; + alg.grow().value = 2; + alg.removeTail(); + assertEquals(1,alg.head().value); + assertEquals(1,alg.size()); + + alg.removeTail(); + assertEquals(0, alg.size()); + } + + @Test + public void get() { + CircularArray alg = new CircularArray(A.class,2); + assertEquals(2,alg.data.length); + + // easy case + alg.grow().value = 1; + alg.grow().value = 2; + + assertEquals(1,alg.get(0).value); + assertEquals(2,alg.get(1).value); + + // make there be an offset + alg.removeHead(); + alg.grow().value = 3; + assertEquals(2,alg.data.length); // sanity check + assertEquals(2,alg.get(0).value); + assertEquals(3,alg.get(1).value); + } + + @Test + public void add() { + CircularArray alg = new CircularArray(A.class,3); + assertEquals(3,alg.data.length); + + alg.add( new A(1)); + assertEquals(1,alg.data[0].value); + assertEquals(1,alg.size); + + alg.add( new A(2)); + assertEquals(1,alg.data[0].value); + assertEquals(2,alg.data[1].value); + assertEquals(2,alg.size); + + // see if it grows + alg.add( new A(3)); + alg.add( new A(4)); + assertEquals(1,alg.data[0].value); + assertEquals(2,alg.data[1].value); + assertEquals(3,alg.data[2].value); + assertEquals(4,alg.data[3].value); + assertEquals(4,alg.size); + + // grows with offset + alg.start = 1; + alg.data = new A[]{new A(1),new A(2), new A(3)}; + alg.size = 3; + alg.add( new A(4)); + assertEquals(2,alg.data[0].value); + assertEquals(3,alg.data[1].value); + assertEquals(1,alg.data[2].value); + assertEquals(4,alg.data[3].value); + assertEquals(4,alg.size); + + // wrap around case + alg.start = 1; + alg.size = 2; + alg.data = new A[3]; + alg.add( new A(10)); + assertEquals(10,alg.data[0].value); + assertEquals(3,alg.size); + + } + + @Test + public void addW() { + CircularArray alg = new CircularArray(A.class,3); + assertEquals(3,alg.data.length); + + alg.addW(new A(1)); + assertEquals(1,alg.data[0].value); + assertEquals(1,alg.size); + + alg.addW(new A(2)); + assertEquals(1,alg.data[0].value); + assertEquals(2,alg.data[1].value); + assertEquals(2,alg.size); + + // see if it over writes + alg.addW(new A(3)); + alg.addW(new A(4)); + assertEquals(4,alg.data[0].value); + assertEquals(2,alg.data[1].value); + assertEquals(3,alg.data[2].value); + assertEquals(3,alg.size); + assertEquals(1,alg.start); + + // wrap around case + alg.start = 1; + alg.size = 2; + alg.data = new A[3]; + alg.addW(new A(10)); + assertEquals(10,alg.data[0].value); + assertEquals(3,alg.size); + } + + @Test + public void isEmpty() { + CircularArray alg = new CircularArray(A.class,3); + + assertTrue(alg.isEmpty()); + alg.add(new A(5)); + assertFalse(alg.isEmpty()); + alg.removeTail(); + assertTrue(alg.isEmpty()); + + } + + @Test + public void reset() { + CircularArray alg = new CircularArray(A.class,3); + + alg.start = 2; + alg.size = 5; + + alg.reset(); + + assertEquals(0,alg.size); + assertEquals(0,alg.start); + } + + @Test + public void grow() { + CircularArray alg = new CircularArray(A.class,3); + + alg.grow().value = 1; + assertEquals(1,alg.size); + alg.grow().value = 2; + alg.grow().value = 3; + alg.grow().value = 4; + assertEquals(1,alg.data[0].value); + assertEquals(4,alg.data[3].value); + assertEquals(4,alg.size); + assertTrue(alg.data.length >= 4); + + // wrap around case + alg = new CircularArray(A.class,3); + alg.size = 2; + alg.start = 1; + alg.grow().value = 1; + assertEquals(1,alg.data[0].value); + assertTrue(null == alg.data[1]); + assertTrue(null == alg.data[2]); + + } + + @Test + public void growW() { + CircularArray alg = new CircularArray(A.class,3); + + alg.growW().value = 1; + assertEquals(1,alg.size); + alg.growW().value = 2; + alg.growW().value = 3; + alg.growW().value = 4; + assertEquals(4,alg.data[0].value); + assertEquals(3,alg.data[2].value); + assertEquals(3,alg.size); + assertTrue(alg.data.length == 3); + } + + + public static class A + { + public int value; + + public A() { + } + + public A(int value) { + this.value = value; + } + } +} diff -Nru ddogleg-0.18+ds/test/org/ddogleg/struct/TestCircularQueue_F32.java ddogleg-0.22+ds/test/org/ddogleg/struct/TestCircularQueue_F32.java --- ddogleg-0.18+ds/test/org/ddogleg/struct/TestCircularQueue_F32.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/struct/TestCircularQueue_F32.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,229 +0,0 @@ -/* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. - * - * This file is part of DDogleg (http://ddogleg.org). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ddogleg.struct; - -import org.ejml.UtilEjml; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.*; - -/** - * @author Peter Abeles - */ -public class TestCircularQueue_F32 { - - @Test - public void popHead() { - CircularQueue_F32 alg = new CircularQueue_F32(); - - alg.add(1); - alg.add(2); - assertEquals(1,alg.popHead(), UtilEjml.TEST_F64); - assertEquals(1,alg.size()); - - assertEquals(2,alg.popHead(), UtilEjml.TEST_F64); - assertEquals(0,alg.size()); - } - - @Test - public void popTail() { - CircularQueue_F32 alg = new CircularQueue_F32(); - - alg.add(1); - alg.add(2); - assertEquals(2,alg.popTail(), UtilEjml.TEST_F64); - assertEquals(1,alg.size()); - - assertEquals(1,alg.popTail(), UtilEjml.TEST_F64); - assertEquals(0, alg.size()); - } - - @Test - public void head() { - CircularQueue_F32 alg = new CircularQueue_F32(); - - alg.add(1); - assertEquals(1, alg.head(), UtilEjml.TEST_F64); - alg.add(3); - assertEquals(1,alg.head(), UtilEjml.TEST_F64); - } - - @Test - public void head_offset() { - CircularQueue_F32 alg = new CircularQueue_F32(3); - - alg.start = 2; - alg.size = 0; - - alg.add(1); - assertEquals(1, alg.head(), UtilEjml.TEST_F64); - alg.add(3); - assertEquals(1,alg.head(), UtilEjml.TEST_F64); - } - - @Test - public void tail() { - CircularQueue_F32 alg = new CircularQueue_F32(); - - alg.add(1); - assertEquals(1,alg.tail(), UtilEjml.TEST_F64); - alg.add(3); - assertEquals(3, alg.tail(), UtilEjml.TEST_F64); - } - - @Test - public void tail_offset() { - CircularQueue_F32 alg = new CircularQueue_F32(3); - - alg.start = 2; - alg.size = 0; - - alg.add(1); - assertEquals(1,alg.tail(), UtilEjml.TEST_F64); - alg.add(3); - assertEquals(3, alg.tail(), UtilEjml.TEST_F64); - } - - @Test - public void removeHead() { - CircularQueue_F32 alg = new CircularQueue_F32(); - - alg.add(1); - alg.add(2); - alg.removeHead(); - assertEquals(2, alg.head(), UtilEjml.TEST_F64); - assertEquals(1, alg.size()); - - alg.removeHead(); - assertEquals(0, alg.size()); - } - - @Test - public void removeTail() { - CircularQueue_F32 alg = new CircularQueue_F32(); - - alg.add(1); - alg.add(2); - alg.removeTail(); - assertEquals(1,alg.head(), UtilEjml.TEST_F64); - assertEquals(1,alg.size()); - - alg.removeTail(); - assertEquals(0, alg.size()); - } - - @Test - public void get() { - CircularQueue_F32 alg = new CircularQueue_F32(2); - assertEquals(2,alg.data.length); - - // easy case - alg.add(1); - alg.add(2); - - assertEquals(1,alg.get(0), UtilEjml.TEST_F64); - assertEquals(2,alg.get(1), UtilEjml.TEST_F64); - - // make there be an offset - alg.removeHead(); - alg.add(3); - assertEquals(2,alg.data.length); // sanity check - assertEquals(2,alg.get(0), UtilEjml.TEST_F64); - assertEquals(3,alg.get(1), UtilEjml.TEST_F64); - } - - @Test - public void add() { - CircularQueue_F32 alg = new CircularQueue_F32(3); - assertEquals(3,alg.data.length); - - alg.add(1); - assertEquals(1,alg.data[0], UtilEjml.TEST_F64); - assertEquals(1,alg.size); - - alg.add(2); - assertEquals(1,alg.data[0], UtilEjml.TEST_F64); - assertEquals(2,alg.data[1], UtilEjml.TEST_F64); - assertEquals(2,alg.size); - - // see if it over writes - alg.add(3); - alg.add(4); - assertEquals(4,alg.data[0], UtilEjml.TEST_F64); - assertEquals(2,alg.data[1], UtilEjml.TEST_F64); - assertEquals(3,alg.data[2], UtilEjml.TEST_F64); - assertEquals(3,alg.size); - assertEquals(1,alg.start); - - // wrap around case - alg.start = 1; - alg.size = 2; - alg.data = new float[3]; - alg.add(10); - assertEquals(10,alg.data[0], UtilEjml.TEST_F64); - assertEquals(3,alg.size); - } - - @Test - public void isEmpty() { - CircularQueue_F32 alg = new CircularQueue_F32(3); - - assertTrue(alg.isEmpty()); - alg.add(5); - assertFalse(alg.isEmpty()); - alg.removeTail(); - assertTrue(alg.isEmpty()); - - } - - @Test - public void reset() { - CircularQueue_F32 alg = new CircularQueue_F32(3); - - alg.start = 2; - alg.size = 5; - - alg.reset(); - - assertEquals(0,alg.size); - assertEquals(0,alg.start); - } - - @Test - public void set_queue() { - CircularQueue_F32 a = new CircularQueue_F32(3); - - for (int i = 0; i < 4; i++) { - a.add(i); - } - - CircularQueue_F32 b = new CircularQueue_F32(10); - b.set(a); - - assertEquals(3,b.queueSize()); - for (int i = 0; i < a.data.length; i++) { - assertEquals(a.data[i],b.data[i], UtilEjml.TEST_F64); - } - assertEquals(a.size,b.size); - assertEquals(a.start,b.start); - - } - - -} diff -Nru ddogleg-0.18+ds/test/org/ddogleg/struct/TestCircularQueue_F64.java ddogleg-0.22+ds/test/org/ddogleg/struct/TestCircularQueue_F64.java --- ddogleg-0.18+ds/test/org/ddogleg/struct/TestCircularQueue_F64.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/struct/TestCircularQueue_F64.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,227 +0,0 @@ -/* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. - * - * This file is part of DDogleg (http://ddogleg.org). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ddogleg.struct; - -import org.ejml.UtilEjml; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.*; - -/** - * @author Peter Abeles - */ -public class TestCircularQueue_F64 { - - @Test - public void popHead() { - CircularQueue_F64 alg = new CircularQueue_F64(); - - alg.add(1); - alg.add(2); - assertEquals(1,alg.popHead(), UtilEjml.TEST_F64); - assertEquals(1,alg.size()); - - assertEquals(2,alg.popHead(), UtilEjml.TEST_F64); - assertEquals(0,alg.size()); - } - - @Test - public void popTail() { - CircularQueue_F64 alg = new CircularQueue_F64(); - - alg.add(1); - alg.add(2); - assertEquals(2,alg.popTail(), UtilEjml.TEST_F64); - assertEquals(1,alg.size()); - - assertEquals(1,alg.popTail(), UtilEjml.TEST_F64); - assertEquals(0, alg.size()); - } - - @Test - public void head() { - CircularQueue_F64 alg = new CircularQueue_F64(); - - alg.add(1); - assertEquals(1, alg.head(), UtilEjml.TEST_F64); - alg.add(3); - assertEquals(1,alg.head(), UtilEjml.TEST_F64); - } - - @Test - public void head_offset() { - CircularQueue_F64 alg = new CircularQueue_F64(3); - - alg.start = 2; - alg.size = 0; - - alg.add(1); - assertEquals(1, alg.head(), UtilEjml.TEST_F64); - alg.add(3); - assertEquals(1,alg.head(), UtilEjml.TEST_F64); - } - - @Test - public void tail() { - CircularQueue_F64 alg = new CircularQueue_F64(); - - alg.add(1); - assertEquals(1,alg.tail(), UtilEjml.TEST_F64); - alg.add(3); - assertEquals(3, alg.tail(), UtilEjml.TEST_F64); - } - - @Test - public void tail_offset() { - CircularQueue_F64 alg = new CircularQueue_F64(3); - - alg.start = 2; - alg.size = 0; - - alg.add(1); - assertEquals(1,alg.tail(), UtilEjml.TEST_F64); - alg.add(3); - assertEquals(3, alg.tail(), UtilEjml.TEST_F64); - } - - @Test - public void removeHead() { - CircularQueue_F64 alg = new CircularQueue_F64(); - - alg.add(1); - alg.add(2); - alg.removeHead(); - assertEquals(2, alg.head(), UtilEjml.TEST_F64); - assertEquals(1, alg.size()); - - alg.removeHead(); - assertEquals(0, alg.size()); - } - - @Test - public void removeTail() { - CircularQueue_F64 alg = new CircularQueue_F64(); - - alg.add(1); - alg.add(2); - alg.removeTail(); - assertEquals(1,alg.head(), UtilEjml.TEST_F64); - assertEquals(1,alg.size()); - - alg.removeTail(); - assertEquals(0, alg.size()); - } - - @Test - public void get() { - CircularQueue_F64 alg = new CircularQueue_F64(2); - assertEquals(2,alg.data.length); - - // easy case - alg.add(1); - alg.add(2); - - assertEquals(1,alg.get(0), UtilEjml.TEST_F64); - assertEquals(2,alg.get(1), UtilEjml.TEST_F64); - - // make there be an offset - alg.removeHead(); - alg.add(3); - assertEquals(2,alg.data.length); // sanity check - assertEquals(2,alg.get(0), UtilEjml.TEST_F64); - assertEquals(3,alg.get(1), UtilEjml.TEST_F64); - } - - @Test - public void add() { - CircularQueue_F64 alg = new CircularQueue_F64(3); - assertEquals(3,alg.data.length); - - alg.add(1); - assertEquals(1,alg.data[0], UtilEjml.TEST_F64); - assertEquals(1,alg.size); - - alg.add(2); - assertEquals(1,alg.data[0], UtilEjml.TEST_F64); - assertEquals(2,alg.data[1], UtilEjml.TEST_F64); - assertEquals(2,alg.size); - - // see if it over writes - alg.add(3); - alg.add(4); - assertEquals(4,alg.data[0], UtilEjml.TEST_F64); - assertEquals(2,alg.data[1], UtilEjml.TEST_F64); - assertEquals(3,alg.data[2], UtilEjml.TEST_F64); - assertEquals(3,alg.size); - assertEquals(1,alg.start); - - // wrap around case - alg.start = 1; - alg.size = 2; - alg.data = new double[3]; - alg.add(10); - assertEquals(10,alg.data[0], UtilEjml.TEST_F64); - assertEquals(3,alg.size); - } - - @Test - public void isEmpty() { - CircularQueue_F64 alg = new CircularQueue_F64(3); - - assertTrue(alg.isEmpty()); - alg.add(5); - assertFalse(alg.isEmpty()); - alg.removeTail(); - assertTrue(alg.isEmpty()); - - } - - @Test - public void reset() { - CircularQueue_F64 alg = new CircularQueue_F64(3); - - alg.start = 2; - alg.size = 5; - - alg.reset(); - - assertEquals(0,alg.size); - assertEquals(0,alg.start); - } - - @Test - public void set_queue() { - CircularQueue_F64 a = new CircularQueue_F64(3); - - for (int i = 0; i < 4; i++) { - a.add(i); - } - - CircularQueue_F64 b = new CircularQueue_F64(10); - b.set(a); - - assertEquals(3,b.queueSize()); - for (int i = 0; i < a.data.length; i++) { - assertEquals(a.data[i],b.data[i], UtilEjml.TEST_F64); - } - assertEquals(a.size,b.size); - assertEquals(a.start,b.start); - - } -} diff -Nru ddogleg-0.18+ds/test/org/ddogleg/struct/TestCircularQueue_I32.java ddogleg-0.22+ds/test/org/ddogleg/struct/TestCircularQueue_I32.java --- ddogleg-0.18+ds/test/org/ddogleg/struct/TestCircularQueue_I32.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/struct/TestCircularQueue_I32.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,253 +0,0 @@ -/* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. - * - * This file is part of DDogleg (http://ddogleg.org). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ddogleg.struct; - -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.*; - -/** - * @author Peter Abeles - */ -public class TestCircularQueue_I32 { - - @Test - public void popHead() { - CircularQueue_I32 alg = new CircularQueue_I32(); - - alg.add(1); - alg.add(2); - assertEquals(1,alg.popHead()); - assertEquals(1,alg.size()); - - assertEquals(2,alg.popHead()); - assertEquals(0,alg.size()); - } - - @Test - public void popTail() { - CircularQueue_I32 alg = new CircularQueue_I32(); - - alg.add(1); - alg.add(2); - assertEquals(2,alg.popTail()); - assertEquals(1,alg.size()); - - assertEquals(1,alg.popTail()); - assertEquals(0, alg.size()); - } - - @Test - public void head() { - CircularQueue_I32 alg = new CircularQueue_I32(); - - alg.add(1); - assertEquals(1, alg.head()); - alg.add(3); - assertEquals(1,alg.head()); - } - - @Test - public void head_offset() { - CircularQueue_I32 alg = new CircularQueue_I32(3); - - alg.start = 2; - alg.size = 0; - - alg.add(1); - assertEquals(1, alg.head()); - alg.add(3); - assertEquals(1,alg.head()); - } - - @Test - public void tail() { - CircularQueue_I32 alg = new CircularQueue_I32(); - - alg.add(1); - assertEquals(1,alg.tail()); - alg.add(3); - assertEquals(3, alg.tail()); - } - - @Test - public void tail_offset() { - CircularQueue_I32 alg = new CircularQueue_I32(3); - - alg.start = 2; - alg.size = 0; - - alg.add(1); - assertEquals(1,alg.tail()); - alg.add(3); - assertEquals(3, alg.tail()); - } - - @Test - public void removeHead() { - CircularQueue_I32 alg = new CircularQueue_I32(); - - alg.add(1); - alg.add(2); - alg.removeHead(); - assertEquals(2, alg.head()); - assertEquals(1, alg.size()); - - alg.removeHead(); - assertEquals(0, alg.size()); - } - - @Test - public void removeTail() { - CircularQueue_I32 alg = new CircularQueue_I32(); - - alg.add(1); - alg.add(2); - alg.removeTail(); - assertEquals(1,alg.head()); - assertEquals(1,alg.size()); - - alg.removeTail(); - assertEquals(0, alg.size()); - } - - @Test - public void get() { - CircularQueue_I32 alg = new CircularQueue_I32(2); - assertEquals(2,alg.data.length); - - // easy case - alg.add(1); - alg.add(2); - - assertEquals(1,alg.get(0)); - assertEquals(2,alg.get(1)); - - // make there be an offset - alg.removeHead(); - alg.add(3); - assertEquals(2,alg.data.length); // sanity check - assertEquals(2,alg.get(0)); - assertEquals(3,alg.get(1)); - } - - @Test - public void add() { - CircularQueue_I32 alg = new CircularQueue_I32(3); - assertEquals(3,alg.data.length); - - alg.add(1); - assertEquals(1,alg.data[0]); - assertEquals(1,alg.size); - - alg.add(2); - assertEquals(1,alg.data[0]); - assertEquals(2,alg.data[1]); - assertEquals(2,alg.size); - - // see if it grows - alg.add(3); - alg.add(4); - assertEquals(1,alg.data[0]); - assertEquals(2,alg.data[1]); - assertEquals(3,alg.data[2]); - assertEquals(4,alg.data[3]); - assertEquals(4,alg.size); - - // grows with offset - alg.start = 1; - alg.data = new int[]{1,2,3}; - alg.size = 3; - alg.add(4); - assertEquals(2,alg.data[0]); - assertEquals(3,alg.data[1]); - assertEquals(1,alg.data[2]); - assertEquals(4,alg.data[3]); - assertEquals(4,alg.size); - - // wrap around case - alg.start = 1; - alg.size = 2; - alg.data = new int[3]; - alg.add(10); - assertEquals(10,alg.data[0]); - assertEquals(10,alg.data[0]); - assertEquals(3,alg.size); - - } - - @Test - public void addW() { - CircularQueue_I32 alg = new CircularQueue_I32(3); - assertEquals(3,alg.data.length); - - alg.addW(1); - assertEquals(1,alg.data[0]); - assertEquals(1,alg.size); - - alg.addW(2); - assertEquals(1,alg.data[0]); - assertEquals(2,alg.data[1]); - assertEquals(2,alg.size); - - // see if it over writes - alg.addW(3); - alg.addW(4); - assertEquals(4,alg.data[0]); - assertEquals(2,alg.data[1]); - assertEquals(3,alg.data[2]); - assertEquals(3,alg.size); - assertEquals(1,alg.start); - - // wrap around case - alg.start = 1; - alg.size = 2; - alg.data = new int[3]; - alg.addW(10); - assertEquals(10,alg.data[0]); - assertEquals(3,alg.size); - } - - @Test - public void isEmpty() { - CircularQueue_I32 alg = new CircularQueue_I32(3); - - assertTrue(alg.isEmpty()); - alg.add(5); - assertFalse(alg.isEmpty()); - alg.removeTail(); - assertTrue(alg.isEmpty()); - - } - - @Test - public void reset() { - CircularQueue_I32 alg = new CircularQueue_I32(3); - - alg.start = 2; - alg.size = 5; - - alg.reset(); - - assertEquals(0,alg.size); - assertEquals(0,alg.start); - } - - -} diff -Nru ddogleg-0.18+ds/test/org/ddogleg/struct/TestCircularQueue.java ddogleg-0.22+ds/test/org/ddogleg/struct/TestCircularQueue.java --- ddogleg-0.18+ds/test/org/ddogleg/struct/TestCircularQueue.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/struct/TestCircularQueue.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,303 +0,0 @@ -/* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. - * - * This file is part of DDogleg (http://ddogleg.org). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ddogleg.struct; - -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.*; - -/** - * @author Peter Abeles - */ -public class TestCircularQueue { - - @Test - public void popHead() { - CircularQueue alg = new CircularQueue(A.class); - - alg.grow().value = 1; - alg.grow().value = 2; - assertEquals(1,alg.popHead().value); - assertEquals(1,alg.size()); - - assertEquals(2,alg.popHead().value); - assertEquals(0,alg.size()); - } - - @Test - public void popTail() { - CircularQueue alg = new CircularQueue(A.class); - - alg.grow().value = 1; - alg.grow().value = 2; - assertEquals(2,alg.popTail().value); - assertEquals(1,alg.size()); - - assertEquals(1,alg.popTail().value); - assertEquals(0, alg.size()); - } - - @Test - public void head() { - CircularQueue alg = new CircularQueue(A.class); - - alg.grow().value = 1; - assertEquals(1, alg.head().value); - alg.grow().value = 3; - assertEquals(1,alg.head().value); - } - - @Test - public void head_offset() { - CircularQueue alg = new CircularQueue(A.class,3); - - alg.start = 2; - alg.size = 0; - - alg.grow().value = 1; - assertEquals(1, alg.head().value); - alg.grow().value = 3; - assertEquals(1,alg.head().value); - } - - @Test - public void tail() { - CircularQueue alg = new CircularQueue(A.class); - - alg.grow().value = 1; - assertEquals(1,alg.tail().value); - alg.grow().value = 3; - assertEquals(3, alg.tail().value); - } - - @Test - public void tail_offset() { - CircularQueue alg = new CircularQueue(A.class); - - alg.start = 2; - alg.size = 0; - - alg.grow().value = 1; - assertEquals(1,alg.tail().value); - alg.grow().value = 3; - assertEquals(3, alg.tail().value); - } - - @Test - public void removeHead() { - CircularQueue alg = new CircularQueue(A.class); - - alg.grow().value = 1; - alg.grow().value = 2; - alg.removeHead(); - assertEquals(2, alg.head().value); - assertEquals(1, alg.size()); - - alg.removeHead(); - assertEquals(0, alg.size()); - } - - @Test - public void removeTail() { - CircularQueue alg = new CircularQueue(A.class); - - alg.grow().value = 1; - alg.grow().value = 2; - alg.removeTail(); - assertEquals(1,alg.head().value); - assertEquals(1,alg.size()); - - alg.removeTail(); - assertEquals(0, alg.size()); - } - - @Test - public void get() { - CircularQueue alg = new CircularQueue(A.class,2); - assertEquals(2,alg.data.length); - - // easy case - alg.grow().value = 1; - alg.grow().value = 2; - - assertEquals(1,alg.get(0).value); - assertEquals(2,alg.get(1).value); - - // make there be an offset - alg.removeHead(); - alg.grow().value = 3; - assertEquals(2,alg.data.length); // sanity check - assertEquals(2,alg.get(0).value); - assertEquals(3,alg.get(1).value); - } - - @Test - public void add() { - CircularQueue alg = new CircularQueue(A.class,3); - assertEquals(3,alg.data.length); - - alg.add( new A(1)); - assertEquals(1,alg.data[0].value); - assertEquals(1,alg.size); - - alg.add( new A(2)); - assertEquals(1,alg.data[0].value); - assertEquals(2,alg.data[1].value); - assertEquals(2,alg.size); - - // see if it grows - alg.add( new A(3)); - alg.add( new A(4)); - assertEquals(1,alg.data[0].value); - assertEquals(2,alg.data[1].value); - assertEquals(3,alg.data[2].value); - assertEquals(4,alg.data[3].value); - assertEquals(4,alg.size); - - // grows with offset - alg.start = 1; - alg.data = new A[]{new A(1),new A(2), new A(3)}; - alg.size = 3; - alg.add( new A(4)); - assertEquals(2,alg.data[0].value); - assertEquals(3,alg.data[1].value); - assertEquals(1,alg.data[2].value); - assertEquals(4,alg.data[3].value); - assertEquals(4,alg.size); - - // wrap around case - alg.start = 1; - alg.size = 2; - alg.data = new A[3]; - alg.add( new A(10)); - assertEquals(10,alg.data[0].value); - assertEquals(3,alg.size); - - } - - @Test - public void addW() { - CircularQueue alg = new CircularQueue(A.class,3); - assertEquals(3,alg.data.length); - - alg.addW(new A(1)); - assertEquals(1,alg.data[0].value); - assertEquals(1,alg.size); - - alg.addW(new A(2)); - assertEquals(1,alg.data[0].value); - assertEquals(2,alg.data[1].value); - assertEquals(2,alg.size); - - // see if it over writes - alg.addW(new A(3)); - alg.addW(new A(4)); - assertEquals(4,alg.data[0].value); - assertEquals(2,alg.data[1].value); - assertEquals(3,alg.data[2].value); - assertEquals(3,alg.size); - assertEquals(1,alg.start); - - // wrap around case - alg.start = 1; - alg.size = 2; - alg.data = new A[3]; - alg.addW(new A(10)); - assertEquals(10,alg.data[0].value); - assertEquals(3,alg.size); - } - - @Test - public void isEmpty() { - CircularQueue alg = new CircularQueue(A.class,3); - - assertTrue(alg.isEmpty()); - alg.add(new A(5)); - assertFalse(alg.isEmpty()); - alg.removeTail(); - assertTrue(alg.isEmpty()); - - } - - @Test - public void reset() { - CircularQueue alg = new CircularQueue(A.class,3); - - alg.start = 2; - alg.size = 5; - - alg.reset(); - - assertEquals(0,alg.size); - assertEquals(0,alg.start); - } - - @Test - public void grow() { - CircularQueue alg = new CircularQueue(A.class,3); - - alg.grow().value = 1; - assertEquals(1,alg.size); - alg.grow().value = 2; - alg.grow().value = 3; - alg.grow().value = 4; - assertEquals(1,alg.data[0].value); - assertEquals(4,alg.data[3].value); - assertEquals(4,alg.size); - assertTrue(alg.data.length >= 4); - - // wrap around case - alg = new CircularQueue(A.class,3); - alg.size = 2; - alg.start = 1; - alg.grow().value = 1; - assertEquals(1,alg.data[0].value); - assertTrue(null == alg.data[1]); - assertTrue(null == alg.data[2]); - - } - - @Test - public void growW() { - CircularQueue alg = new CircularQueue(A.class,3); - - alg.growW().value = 1; - assertEquals(1,alg.size); - alg.growW().value = 2; - alg.growW().value = 3; - alg.growW().value = 4; - assertEquals(4,alg.data[0].value); - assertEquals(3,alg.data[2].value); - assertEquals(3,alg.size); - assertTrue(alg.data.length == 3); - } - - - public static class A - { - public int value; - - public A() { - } - - public A(int value) { - this.value = value; - } - } -} diff -Nru ddogleg-0.18+ds/test/org/ddogleg/struct/TestDogArray_B.java ddogleg-0.22+ds/test/org/ddogleg/struct/TestDogArray_B.java --- ddogleg-0.18+ds/test/org/ddogleg/struct/TestDogArray_B.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/struct/TestDogArray_B.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,289 @@ +/* + * Copyright (c) 2012-2022, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.struct; + +import org.junit.jupiter.api.Test; + +import java.util.Random; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * @author Peter Abeles + */ +public class TestDogArray_B { + Random rand = new Random(29394); + + @Test void isEquals() { + DogArray_B alg = DogArray_B.array(0, 0, 1, 1, 0); + assertTrue(alg.isEquals(0, 0, 1, 1, 0)); + assertFalse(alg.isEquals(0, 0, 1, 1)); + assertFalse(alg.isEquals(0, 0, 1, 0, 0)); + } + + @Test void auto_grow() { + DogArray_B alg = new DogArray_B(3); + + assertEquals(3, alg.data.length); + + for (int i = 0; i < 10; i++) + alg.push((i%2) == 0); + + assertEquals(10, alg.size); + + for (int i = 0; i < 10; i++) + assertEquals((i%2) == 0, alg.get(i)); + } + + @Test void count() { + DogArray_B alg = DogArray_B.array(0, 0, 1, 1, 1); + + assertEquals(2, alg.count(false)); + assertEquals(3, alg.count(true)); + } + + @Test void reset() { + DogArray_B alg = new DogArray_B(10); + + alg.push(true); + alg.push(false); + alg.push(false); + + assertTrue(alg.get(0)); + assertEquals(3, alg.size); + + alg.reset(); + + assertEquals(0, alg.size); + } + + @Test void resize() { + DogArray_B alg = new DogArray_B(2); + assertEquals(0, alg.size); + alg.resize(12); + assertTrue(alg.data.length >= 12); + assertEquals(12, alg.size); + // Make sure it doesn't declare a new array since it doesn't have to + alg.data[2] = true; + alg.resize(10); + assertTrue(alg.data.length >= 10); + assertEquals(10, alg.size); + assertTrue(alg.get(2)); + } + + @Test void resize_default() { + var alg = new DogArray_B(2); + assertEquals(0, alg.size); + alg.resize(12, true); + assertTrue(alg.data.length >= 12); + assertEquals(12, alg.size); + for (int i = 0; i < alg.size; i++) { + assertTrue(alg.get(i)); + } + + // If the size has been reduced then there are no new elements to initialize + alg.resize(10, false); + assertTrue(alg.data.length >= 10); + assertEquals(10, alg.size); + for (int i = 0; i < alg.size; i++) { + assertTrue(alg.get(i)); + } + + // It's now larger and the new elements should be initialized + alg.resize(20, false); + for (int i = 0; i < alg.size; i++) { + assertEquals(i < 10, alg.get(i)); + } + } + + @Test void resize_operator() { + var alg = new DogArray_B(2); + assertEquals(0, alg.size); + alg.resize(12, ( idx ) -> (idx%2 == 0)); + assertTrue(alg.data.length >= 12); + assertEquals(12, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals(i%2 == 0, alg.get(i)); + } + + // If the size has been reduced then there are no new elements to initialize + alg.resize(10, ( idx ) -> false); + assertTrue(alg.data.length >= 10); + assertEquals(10, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals(i%2 == 0, alg.get(i)); + } + + // It's now larger and the new elements should be initialized + alg.resize(20, ( idx ) -> true); + for (int i = 0; i < alg.size; i++) { + assertEquals(i >= 10 || i%2 == 0, alg.get(i)); + } + } + + @Test void push_pop() { + DogArray_B alg = new DogArray_B(10); + + alg.push(false); + alg.push(true); + + assertEquals(2, alg.size); + assertTrue(alg.pop()); + assertFalse(alg.pop()); + assertEquals(0, alg.size); + } + + @Test void setTo_array_off() { + DogArray_B alg = new DogArray_B(10); + + boolean[] foo = new boolean[]{true, true, false, true, false}; + alg.setTo(foo, 1, 3); + assertEquals(3, alg.size); + for (int i = 0; i < 3; i++) { + assertEquals(alg.get(i), foo[i + 1]); + } + } + + @Test void setTo_array() { + DogArray_B alg = new DogArray_B(10); + + boolean[] array = new boolean[]{true, true, false, true, false}; + + assertSame(alg, alg.setTo(array)); + assertEquals(array.length, alg.size); + + for (int i = 0; i < array.length; i++) { + assertEquals(alg.get(i), array[i]); + } + } + + @Test void remove_two() { + DogArray_B alg = new DogArray_B(10); + + alg.push(true); + alg.push(true); + alg.push(false); + alg.push(true); + alg.push(false); + + alg.remove(1, 1); + assertEquals(4, alg.size); + assertTrue(alg.get(0)); + assertFalse(alg.get(1)); + assertTrue(alg.get(2)); + assertFalse(alg.get(3)); + alg.remove(0, 1); + assertEquals(2, alg.size); + assertTrue(alg.get(0)); + assertFalse(alg.get(1)); + } + + @Test void remove_swap() { + DogArray_B alg = DogArray_B.array(0, 0, 0, 0, 1); + alg.removeSwap(1); + assertEquals(4, alg.size); + alg.forIdx(( i, v ) -> assertEquals((i == 1), v)); + } + + @Test void indexOf() { + DogArray_B alg = new DogArray_B(10); + + alg.push(true); + alg.push(false); + alg.push(false); + alg.push(true); + + assertEquals(0, alg.indexOf(true)); + assertEquals(1, alg.indexOf(false)); + } + + @Test void getTail() { + var alg = new DogArray_B(20); + + for (int i = 0; i < 20; i++) { + alg.add(i%2 == 0); + assertEquals(alg.getTail(), alg.data[i]); + } + } + + @Test void getTail_idx() { + var alg = new DogArray_B(20); + + for (int i = 0; i < 20; i++) { + alg.add(i%2 == 0); + } + + for (int i = 0; i < 20; i++) { + assertEquals((20 - i - 1)%2 == 0, alg.getTail(i)); + } + } + + @Test void setTail() { + var alg = new DogArray_B(20); + + for (int i = 0; i < 20; i++) { + alg.add(i%2 == 0); + } + + for (int i = 0; i < 20; i++) { + alg.setTail(i, i%2 == 1); + } + for (int i = 0; i < 20; i++) { + assertEquals(i%2 == 1, alg.getTail(i)); + } + } + + @Test void shuffle() { + int N = 20; + var alg = new DogArray_B(N); + for (int i = 0; i < N; i++) { + alg.add(i>= N/2); + } + alg.shuffle(rand); + int changed = 0; + for (int i = 0; i < N; i++) { + if (alg.get(i) != i >= N/2) + changed++; + } + assertTrue(changed >= 5); + } + + @Test void forIdx() { + DogArray_B alg = DogArray_B.array(true, false, true, false, true); + alg.forIdx(( idx, value ) -> assertEquals(idx%2 == 0, value)); + } + + @Test void forEach() { + DogArray_B alg = DogArray_B.array(true, false, true, false, true); + DogArray_B cpy = new DogArray_B(alg.size); + alg.forEach(cpy::add); + assertEquals(alg.size, cpy.size); + for (int i = 0; i < alg.size; i++) { + assertEquals(alg.get(i), cpy.get(i)); + } + } + + @Test void applyIdx() { + DogArray_B alg = DogArray_B.array(true, false, true, false, true); + alg.applyIdx(( idx, value ) -> false); + for (int i = 0; i < alg.size; i++) { + assertFalse(alg.get(i)); + } + } +} diff -Nru ddogleg-0.18+ds/test/org/ddogleg/struct/TestDogArray_F32.java ddogleg-0.22+ds/test/org/ddogleg/struct/TestDogArray_F32.java --- ddogleg-0.18+ds/test/org/ddogleg/struct/TestDogArray_F32.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/struct/TestDogArray_F32.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,434 @@ +/* + * Copyright (c) 2012-2022, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.struct; + +import org.ejml.UtilEjml; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * @author Peter Abeles + */ +public class TestDogArray_F32 extends ChecksDogArrayPrimitive { + @Test void range() { + DogArray_F32 alg = DogArray_F32.range(-1, 20); + + assertEquals(21, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals(i - 1, alg.get(i)); + } + } + + @Test void count() { + DogArray_F32 alg = DogArray_F32.array(0, 0, 1, 1, 1); + + assertEquals(2, alg.count(0)); + assertEquals(3, alg.count(1)); + } + + @Test void isEquals() { + DogArray_F32 alg = DogArray_F32.array(0, 0, 1, 1, 4); + assertTrue(alg.isEquals(0, 0, 1, 1, 4)); + assertFalse(alg.isEquals(0, 0, 1, 1)); + assertFalse(alg.isEquals(0, 0, 1, 2, 4)); + } + + @Test void addAll_queue() { + DogArray_F32 queue0 = new DogArray_F32(2); + DogArray_F32 queue1 = new DogArray_F32(3); + + queue0.add(1); + queue0.add(2); + + queue1.add(3); + queue1.add(4); + queue1.add(5); + + assertEquals(2, queue0.size); + queue0.addAll(queue1); + assertEquals(5, queue0.size); + for (int i = 0; i < queue0.size; i++) { + assertEquals(queue0.get(i), i + 1, 1e-8); + } + + queue0.reset(); + queue0.addAll(queue1); + assertEquals(3, queue0.size); + for (int i = 0; i < queue0.size; i++) { + assertEquals(queue0.get(i), i + 3, 1e-8); + } + } + + @Test void addAll_array() { + DogArray_F32 queue0 = new DogArray_F32(2); + float[] array = new float[]{3, 4, 5}; + + queue0.add(1); + queue0.add(2); + + assertEquals(2, queue0.size); + queue0.addAll(array, 0, 3); + assertEquals(5, queue0.size); + for (int i = 0; i < queue0.size; i++) { + assertEquals(queue0.get(i), i + 1, 1e-8); + } + + queue0.reset(); + queue0.addAll(array, 1, 3); + assertEquals(2, queue0.size); + for (int i = 0; i < queue0.size; i++) { + assertEquals(queue0.get(i), i + 4, 1e-8); + } + } + + @Test void auto_grow() { + DogArray_F32 alg = new DogArray_F32(3); + + assertEquals(3, alg.data.length); + + for (int i = 0; i < 10; i++) + alg.push(i); + + assertEquals(10, alg.size); + + for (int i = 0; i < 10; i++) + assertEquals(i, alg.get(i), 1e-8); + } + + @Test void reset() { + DogArray_F32 alg = new DogArray_F32(10); + + alg.push(1); + alg.push(3); + alg.push(-2); + + assertEquals(1.0, alg.get(0)); + assertEquals(3, alg.size); + + alg.reset(); + + assertEquals(0, alg.size); + } + + @Test void resize() { + DogArray_F32 alg = new DogArray_F32(2); + assertEquals(0, alg.size); + alg.resize(12); + assertTrue(alg.data.length >= 12); + assertEquals(12, alg.size); + // Make sure it doesn't declare a new array since it doesn't have to + alg.data[2] = 5; + alg.resize(10); + assertTrue(alg.data.length >= 10); + assertEquals(10, alg.size); + assertEquals(5, alg.get(2)); + } + + @Test void resize_default() { + var alg = new DogArray_F32(2); + assertEquals(0, alg.size); + alg.resize(12, (float)1); + assertTrue(alg.data.length >= 12); + assertEquals(12, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals((float)1, alg.get(i)); + } + + // If the size has been reduced then there are no new elements to initialize + alg.resize(10, (float)2); + assertTrue(alg.data.length >= 10); + assertEquals(10, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals((float)1, alg.get(i)); + } + + // It's now larger and the new elements should be initialized + alg.resize(20, (float)3); + for (int i = 0; i < alg.size; i++) { + assertEquals((float)(i < 10 ? 1 : 3), alg.get(i)); + } + } + + @Test void resize_operator() { + var alg = new DogArray_F32(2); + assertEquals(0, alg.size); + alg.resize(12, ( idx ) -> (float)idx); + assertTrue(alg.data.length >= 12); + assertEquals(12, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals((double)i, alg.get(i)); + } + + // If the size has been reduced then there are no new elements to initialize + alg.resize(10, ( idx ) -> (float)2); + assertTrue(alg.data.length >= 10); + assertEquals(10, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals((float)i, alg.get(i)); + } + + // It's now larger and the new elements should be initialized + alg.resize(20, ( idx ) -> (float)(100 + idx)); + for (int i = 0; i < alg.size; i++) { + assertEquals((float)(i < 10 ? i : 100 + i), alg.get(i)); + } + } + + @Test void push_pop() { + DogArray_F32 alg = new DogArray_F32(10); + + alg.push(1); + alg.push(3); + + assertEquals(2, alg.size); + assertEquals(3, alg.pop()); + assertEquals(1, alg.pop()); + assertEquals(0, alg.size); + } + + @Test void setTo_array_off() { + DogArray_F32 alg = new DogArray_F32(10); + + float[] foo = new float[]{1, 3, 4, 5, 7}; + alg.setTo(foo, 1, 3); + assertEquals(3, alg.size); + for (int i = 0; i < 3; i++) { + assertEquals(alg.get(i), foo[i + 1], UtilEjml.TEST_F32); + } + } + + @Test void setTo_array() { + DogArray_F32 alg = new DogArray_F32(10); + + float[] array = new float[]{1, 3, 4, 5, 7}; + + assertSame(alg, alg.setTo(array)); + assertEquals(array.length, alg.size); + + for (int i = 0; i < array.length; i++) { + assertEquals(alg.get(i), array[i]); + } + } + + @Test void remove_swap() { + var alg = DogArray_F32.array(0, 0, 0, 0, 1); + alg.removeSwap(1); + assertEquals(4, alg.size); + alg.forIdx(( i, v ) -> assertEquals(i != 1 ? 0.0 : 1.0, v, UtilEjml.TEST_F32)); + } + + @Test void remove() { + var alg = new DogArray_F32(10); + + alg.push(1); + alg.push(3); + alg.push(4); + alg.push(5); + + alg.remove(1); + assertEquals(3, alg.size); + assertEquals(1, alg.get(0), 1e-8); + assertEquals(4, alg.get(1), 1e-8); + assertEquals(5, alg.get(2), 1e-8); + } + + @Test void remove_two() { + DogArray_F32 alg = new DogArray_F32(10); + + alg.push(1); + alg.push(3); + alg.push(4); + alg.push(5); + alg.push(6); + + alg.remove(1, 1); + assertEquals(4, alg.size); + assertEquals(1, alg.get(0), UtilEjml.TEST_F32); + assertEquals(4, alg.get(1), UtilEjml.TEST_F32); + assertEquals(5, alg.get(2), UtilEjml.TEST_F32); + assertEquals(6, alg.get(3), UtilEjml.TEST_F32); + alg.remove(0, 1); + assertEquals(2, alg.size); + assertEquals(5, alg.get(0), UtilEjml.TEST_F32); + assertEquals(6, alg.get(1), UtilEjml.TEST_F32); + } + + @Override public DogArray_F32 declare( int maxsize ) { + return new DogArray_F32(maxsize); + } + + @Override public void push( DogArray_F32 queue, double value ) { + queue.push((float)value); + } + + @Override public void insert( DogArray_F32 queue, int index, double value ) { + queue.insert(index, (float)value); + } + + @Override public void check( DogArray_F32 queue, int index, double value ) { + assertEquals(value, queue.get(index), 1e-8); + } + + @Test void indexOf() { + var alg = new DogArray_F32(10); + + alg.push(1); + alg.push(3); + alg.push(4); + alg.push(5); + + assertEquals(1, alg.indexOf(3)); + assertEquals(-1, alg.indexOf(8)); + } + + @Test void sort() { + var alg = new DogArray_F32(6); + + alg.push(8); + alg.push(2); + alg.push(4); + alg.push(3); + + alg.sort(); + + assertEquals(4, alg.size); + assertEquals(2, alg.get(0), 1e-8); + assertEquals(3, alg.get(1), 1e-8); + assertEquals(4, alg.get(2), 1e-8); + assertEquals(8, alg.get(3), 1e-8); + } + + @Test void shuffle() { + int N = 20; + var alg = new DogArray_F32(N); + for (int i = 0; i < N; i++) { + alg.add(i); + } + alg.shuffle(rand); + int changed = 0; + for (int i = 0; i < N; i++) { + if (alg.get(i) != i) + changed++; + } + assertTrue(changed >= 18); + } + + @Test void getFraction() { + var alg = new DogArray_F32(20); + + for (int i = 0; i < 20; i++) { + alg.add(i); + } + + assertEquals(0, alg.getFraction(0.0), UtilEjml.TEST_F32); + assertEquals(0, alg.getFraction(0.02), UtilEjml.TEST_F32); + assertEquals(0, alg.getFraction(0.03), UtilEjml.TEST_F32); + assertEquals(1, alg.getFraction(1.0/19.0), UtilEjml.TEST_F32); + assertEquals(1, alg.getFraction(1.7/19.0), UtilEjml.TEST_F32); + assertEquals(19/2, alg.getFraction(0.5), UtilEjml.TEST_F32); + assertEquals(19, alg.getFraction(1.0), UtilEjml.TEST_F32); + } + + @Test void indexOfGreatest() { + var alg = new DogArray_F32(20); + + assertEquals(-1, alg.indexOfGreatest()); + + alg.add(-3); + alg.add(-2); + alg.add(-1); + + assertEquals(2, alg.indexOfGreatest()); + } + + @Test void indexOfLeast() { + var alg = new DogArray_F32(20); + + assertEquals(-1, alg.indexOfLeast()); + + alg.add(-3); + alg.add(-2); + alg.add(-4); + + assertEquals(2, alg.indexOfLeast()); + } + + @Test void getTail() { + var alg = new DogArray_F32(20); + + for (int i = 0; i < 20; i++) { + alg.add(i); + assertEquals(alg.getTail(), alg.data[i]); + } + } + + @Test void getTail_idx() { + var alg = new DogArray_F32(20); + + for (int i = 0; i < 20; i++) { + alg.add(i); + } + + for (int i = 0; i < 20; i++) { + assertEquals(20 - i - 1, alg.getTail(i)); + } + } + + @Test void setTail() { + var alg = new DogArray_F32(20); + + for (int i = 0; i < 20; i++) { + alg.add(i); + } + + for (int i = 0; i < 20; i++) { + alg.setTail(i, -i); + } + for (int i = 0; i < 20; i++) { + assertEquals(-i, alg.getTail(i)); + } + } + + @Test void forIdx() { + DogArray_F32 alg = DogArray_F32.array(1, 2, 3, 4, 5); + alg.forIdx(( idx, value ) -> assertEquals(idx + 1, value, 1e-8)); + } + + @Test void forEach() { + DogArray_F32 alg = DogArray_F32.array(1, 2, 3, 4, 5); + var cpy = new DogArray_F32(alg.size); + alg.forEach(cpy::add); + assertEquals(alg.size, cpy.size); + for (int i = 0; i < alg.size; i++) { + assertEquals(alg.get(i), cpy.get(i)); + } + } + + @Test void applyIdx() { + DogArray_F32 alg = DogArray_F32.array(1, 2, 3, 4, 5); + alg.applyIdx(( idx, value ) -> (value < 3) ? 0 : value); + for (int i = 0; i < 2; i++) { + assertEquals(0, alg.get(i)); + } + for (int i = 2; i < alg.size; i++) { + assertEquals(i + 1, alg.get(i)); + } + } +} diff -Nru ddogleg-0.18+ds/test/org/ddogleg/struct/TestDogArray_F64.java ddogleg-0.22+ds/test/org/ddogleg/struct/TestDogArray_F64.java --- ddogleg-0.18+ds/test/org/ddogleg/struct/TestDogArray_F64.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/struct/TestDogArray_F64.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,434 @@ +/* + * Copyright (c) 2012-2022, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.struct; + +import org.ejml.UtilEjml; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * @author Peter Abeles + */ +public class TestDogArray_F64 extends ChecksDogArrayPrimitive { + @Test void range() { + DogArray_F64 alg = DogArray_F64.range(-1, 20); + + assertEquals(21, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals(i - 1, alg.get(i)); + } + } + + @Test void count() { + DogArray_F64 alg = DogArray_F64.array(0, 0, 1, 1, 1); + + assertEquals(2, alg.count(0)); + assertEquals(3, alg.count(1)); + } + + @Test void isEquals() { + DogArray_F64 alg = DogArray_F64.array(0, 0, 1, 1, 4); + assertTrue(alg.isEquals(0, 0, 1, 1, 4)); + assertFalse(alg.isEquals(0, 0, 1, 1)); + assertFalse(alg.isEquals(0, 0, 1, 2, 4)); + } + + @Test void addAll_queue() { + DogArray_F64 queue0 = new DogArray_F64(2); + DogArray_F64 queue1 = new DogArray_F64(3); + + queue0.add(1); + queue0.add(2); + + queue1.add(3); + queue1.add(4); + queue1.add(5); + + assertEquals(2, queue0.size); + queue0.addAll(queue1); + assertEquals(5, queue0.size); + for (int i = 0; i < queue0.size; i++) { + assertEquals(queue0.get(i), i + 1, 1e-8); + } + + queue0.reset(); + queue0.addAll(queue1); + assertEquals(3, queue0.size); + for (int i = 0; i < queue0.size; i++) { + assertEquals(queue0.get(i), i + 3, 1e-8); + } + } + + @Test void addAll_array() { + DogArray_F64 queue0 = new DogArray_F64(2); + double[] array = new double[]{3, 4, 5}; + + queue0.add(1); + queue0.add(2); + + assertEquals(2, queue0.size); + queue0.addAll(array, 0, 3); + assertEquals(5, queue0.size); + for (int i = 0; i < queue0.size; i++) { + assertEquals(queue0.get(i), i + 1, 1e-8); + } + + queue0.reset(); + queue0.addAll(array, 1, 3); + assertEquals(2, queue0.size); + for (int i = 0; i < queue0.size; i++) { + assertEquals(queue0.get(i), i + 4, 1e-8); + } + } + + @Test void auto_grow() { + DogArray_F64 alg = new DogArray_F64(3); + + assertEquals(3, alg.data.length); + + for (int i = 0; i < 10; i++) + alg.push(i); + + assertEquals(10, alg.size); + + for (int i = 0; i < 10; i++) + assertEquals(i, alg.get(i), 1e-8); + } + + @Test void reset() { + DogArray_F64 alg = new DogArray_F64(10); + + alg.push(1); + alg.push(3); + alg.push(-2); + + assertEquals(1.0, alg.get(0)); + assertEquals(3, alg.size); + + alg.reset(); + + assertEquals(0, alg.size); + } + + @Test void resize() { + DogArray_F64 alg = new DogArray_F64(2); + assertEquals(0, alg.size); + alg.resize(12); + assertTrue(alg.data.length >= 12); + assertEquals(12, alg.size); + // Make sure it doesn't declare a new array since it doesn't have to + alg.data[2] = 5; + alg.resize(10); + assertTrue(alg.data.length >= 10); + assertEquals(10, alg.size); + assertEquals(5, alg.get(2)); + } + + @Test void resize_default() { + var alg = new DogArray_F64(2); + assertEquals(0, alg.size); + alg.resize(12, (double)1); + assertTrue(alg.data.length >= 12); + assertEquals(12, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals((double)1, alg.get(i)); + } + + // If the size has been reduced then there are no new elements to initialize + alg.resize(10, (double)2); + assertTrue(alg.data.length >= 10); + assertEquals(10, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals((double)1, alg.get(i)); + } + + // It's now larger and the new elements should be initialized + alg.resize(20, (double)3); + for (int i = 0; i < alg.size; i++) { + assertEquals((double)(i < 10 ? 1 : 3), alg.get(i)); + } + } + + @Test void resize_operator() { + var alg = new DogArray_F64(2); + assertEquals(0, alg.size); + alg.resize(12, ( idx ) -> (double)idx); + assertTrue(alg.data.length >= 12); + assertEquals(12, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals((double)i, alg.get(i)); + } + + // If the size has been reduced then there are no new elements to initialize + alg.resize(10, ( idx ) -> (double)2); + assertTrue(alg.data.length >= 10); + assertEquals(10, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals((double)i, alg.get(i)); + } + + // It's now larger and the new elements should be initialized + alg.resize(20, ( idx ) -> (double)(100 + idx)); + for (int i = 0; i < alg.size; i++) { + assertEquals((double)(i < 10 ? i : 100 + i), alg.get(i)); + } + } + + @Test void push_pop() { + DogArray_F64 alg = new DogArray_F64(10); + + alg.push(1); + alg.push(3); + + assertEquals(2, alg.size); + assertEquals(3, alg.pop()); + assertEquals(1, alg.pop()); + assertEquals(0, alg.size); + } + + @Test void setTo_array_off() { + DogArray_F64 alg = new DogArray_F64(10); + + double[] foo = new double[]{1, 3, 4, 5, 7}; + alg.setTo(foo, 1, 3); + assertEquals(3, alg.size); + for (int i = 0; i < 3; i++) { + assertEquals(alg.get(i), foo[i + 1], UtilEjml.TEST_F64); + } + } + + @Test void setTo_array() { + DogArray_F64 alg = new DogArray_F64(10); + + double[] array = new double[]{1, 3, 4, 5, 7}; + + assertSame(alg, alg.setTo(array)); + assertEquals(array.length, alg.size); + + for (int i = 0; i < array.length; i++) { + assertEquals(alg.get(i), array[i]); + } + } + + @Test void remove_swap() { + var alg = DogArray_F64.array(0, 0, 0, 0, 1); + alg.removeSwap(1); + assertEquals(4, alg.size); + alg.forIdx(( i, v ) -> assertEquals(i != 1 ? 0.0 : 1.0, v, UtilEjml.TEST_F64)); + } + + @Test void remove() { + var alg = new DogArray_F64(10); + + alg.push(1); + alg.push(3); + alg.push(4); + alg.push(5); + + alg.remove(1); + assertEquals(3, alg.size); + assertEquals(1, alg.get(0), 1e-8); + assertEquals(4, alg.get(1), 1e-8); + assertEquals(5, alg.get(2), 1e-8); + } + + @Test void remove_two() { + DogArray_F64 alg = new DogArray_F64(10); + + alg.push(1); + alg.push(3); + alg.push(4); + alg.push(5); + alg.push(6); + + alg.remove(1, 1); + assertEquals(4, alg.size); + assertEquals(1, alg.get(0), UtilEjml.TEST_F64); + assertEquals(4, alg.get(1), UtilEjml.TEST_F64); + assertEquals(5, alg.get(2), UtilEjml.TEST_F64); + assertEquals(6, alg.get(3), UtilEjml.TEST_F64); + alg.remove(0, 1); + assertEquals(2, alg.size); + assertEquals(5, alg.get(0), UtilEjml.TEST_F64); + assertEquals(6, alg.get(1), UtilEjml.TEST_F64); + } + + @Override public DogArray_F64 declare( int maxsize ) { + return new DogArray_F64(maxsize); + } + + @Override public void push( DogArray_F64 queue, double value ) { + queue.push(value); + } + + @Override public void insert( DogArray_F64 queue, int index, double value ) { + queue.insert(index, value); + } + + @Override public void check( DogArray_F64 queue, int index, double value ) { + assertEquals(value, queue.get(index), 1e-8); + } + + @Test void indexOf() { + var alg = new DogArray_F64(10); + + alg.push(1); + alg.push(3); + alg.push(4); + alg.push(5); + + assertEquals(1, alg.indexOf(3)); + assertEquals(-1, alg.indexOf(8)); + } + + @Test void sort() { + var alg = new DogArray_F64(6); + + alg.push(8); + alg.push(2); + alg.push(4); + alg.push(3); + + alg.sort(); + + assertEquals(4, alg.size); + assertEquals(2, alg.get(0), 1e-8); + assertEquals(3, alg.get(1), 1e-8); + assertEquals(4, alg.get(2), 1e-8); + assertEquals(8, alg.get(3), 1e-8); + } + + @Test void shuffle() { + int N = 20; + var alg = new DogArray_F64(N); + for (int i = 0; i < N; i++) { + alg.add(i); + } + alg.shuffle(rand); + int changed = 0; + for (int i = 0; i < N; i++) { + if (alg.get(i) != i) + changed++; + } + assertTrue(changed >= 18); + } + + @Test void getFraction() { + var alg = new DogArray_F64(20); + + for (int i = 0; i < 20; i++) { + alg.add(i); + } + + assertEquals(0, alg.getFraction(0.0), UtilEjml.TEST_F64); + assertEquals(0, alg.getFraction(0.02), UtilEjml.TEST_F64); + assertEquals(0, alg.getFraction(0.03), UtilEjml.TEST_F64); + assertEquals(1, alg.getFraction(1.0/19.0), UtilEjml.TEST_F64); + assertEquals(1, alg.getFraction(1.7/19.0), UtilEjml.TEST_F64); + assertEquals(19/2, alg.getFraction(0.5), UtilEjml.TEST_F64); + assertEquals(19, alg.getFraction(1.0), UtilEjml.TEST_F64); + } + + @Test void indexOfGreatest() { + var alg = new DogArray_F64(20); + + assertEquals(-1, alg.indexOfGreatest()); + + alg.add(-3); + alg.add(-2); + alg.add(-1); + + assertEquals(2, alg.indexOfGreatest()); + } + + @Test void indexOfLeast() { + var alg = new DogArray_F64(20); + + assertEquals(-1, alg.indexOfLeast()); + + alg.add(-3); + alg.add(-2); + alg.add(-4); + + assertEquals(2, alg.indexOfLeast()); + } + + @Test void getTail() { + var alg = new DogArray_F64(20); + + for (int i = 0; i < 20; i++) { + alg.add(i); + assertEquals(alg.getTail(), alg.data[i]); + } + } + + @Test void getTail_idx() { + var alg = new DogArray_F64(20); + + for (int i = 0; i < 20; i++) { + alg.add(i); + } + + for (int i = 0; i < 20; i++) { + assertEquals(20 - i - 1, alg.getTail(i)); + } + } + + @Test void setTail() { + var alg = new DogArray_F64(20); + + for (int i = 0; i < 20; i++) { + alg.add(i); + } + + for (int i = 0; i < 20; i++) { + alg.setTail(i, -i); + } + for (int i = 0; i < 20; i++) { + assertEquals(-i, alg.getTail(i)); + } + } + + @Test void forIdx() { + DogArray_F64 alg = DogArray_F64.array(1, 2, 3, 4, 5); + alg.forIdx(( idx, value ) -> assertEquals(idx + 1, value, 1e-8)); + } + + @Test void forEach() { + DogArray_F64 alg = DogArray_F64.array(1, 2, 3, 4, 5); + var cpy = new DogArray_F64(alg.size); + alg.forEach(cpy::add); + assertEquals(alg.size, cpy.size); + for (int i = 0; i < alg.size; i++) { + assertEquals(alg.get(i), cpy.get(i)); + } + } + + @Test void applyIdx() { + DogArray_F64 alg = DogArray_F64.array(1, 2, 3, 4, 5); + alg.applyIdx(( idx, value ) -> (value < 3) ? 0 : value); + for (int i = 0; i < 2; i++) { + assertEquals(0, alg.get(i)); + } + for (int i = 2; i < alg.size; i++) { + assertEquals(i + 1, alg.get(i)); + } + } +} diff -Nru ddogleg-0.18+ds/test/org/ddogleg/struct/TestDogArray_I16.java ddogleg-0.22+ds/test/org/ddogleg/struct/TestDogArray_I16.java --- ddogleg-0.18+ds/test/org/ddogleg/struct/TestDogArray_I16.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/struct/TestDogArray_I16.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,430 @@ +/* + * Copyright (c) 2012-2022, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.struct; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class TestDogArray_I16 extends ChecksDogArrayPrimitive { + @Test void range() { + var alg = DogArray_I16.range(-1, 20); + + assertEquals(21, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals(i - 1, alg.get(i)); + } + } + + @Test void count() { + var alg = DogArray_I16.array(0, 0, 31000, 31000, 31000); + + assertEquals(2, alg.count(0)); + assertEquals(3, alg.count(31000)); + } + + @Test void isEquals() { + var alg = DogArray_I16.array(0, 0, 1, 1, 4); + assertTrue(alg.isEquals(0, 0, 1, 1, 4)); + assertFalse(alg.isEquals(0, 0, 1, 1)); + assertFalse(alg.isEquals(0, 0, 1, 2, 4)); + } + + @Test void addAll_queue() { + var queue0 = new DogArray_I16(2); + var queue1 = new DogArray_I16(3); + + queue0.add(1); + queue0.add(2); + + queue1.add(3); + queue1.add(4); + queue1.add(5); + + assertEquals(2, queue0.size); + queue0.addAll(queue1); + assertEquals(5, queue0.size); + for (int i = 0; i < queue0.size; i++) { + assertEquals(queue0.get(i), i + 1, 1e-8); + } + + queue0.reset(); + queue0.addAll(queue1); + assertEquals(3, queue0.size); + for (int i = 0; i < queue0.size; i++) { + assertEquals(queue0.get(i), i + 3, 1e-8); + } + } + + @Test void addAll_array() { + var queue0 = new DogArray_I16(2); + short[] array = new short[]{3, 4, 5}; + + queue0.add(1); + queue0.add(2); + + assertEquals(2, queue0.size); + queue0.addAll(array, 0, 3); + assertEquals(5, queue0.size); + for (int i = 0; i < queue0.size; i++) { + assertEquals(queue0.get(i), i + 1, 1e-8); + } + + queue0.reset(); + queue0.addAll(array, 1, 3); + assertEquals(2, queue0.size); + for (int i = 0; i < queue0.size; i++) { + assertEquals(queue0.get(i), i + 4, 1e-8); + } + } + + @Test void auto_grow() { + var alg = new DogArray_I16(3); + + assertEquals(3, alg.data.length); + + for (int i = 0; i < 10; i++) + alg.push(i); + + assertEquals(10, alg.size); + + for (int i = 0; i < 10; i++) + assertEquals(i, alg.get(i), 1e-8); + } + + @Test void reset() { + var alg = new DogArray_I16(10); + + alg.push(1); + alg.push(3); + alg.push(-2); + + assertEquals(1.0, alg.get(0)); + assertEquals(3, alg.size); + + alg.reset(); + + assertEquals(0, alg.size); + } + + @Test void resize() { + var alg = new DogArray_I16(2); + assertEquals(0, alg.size); + alg.resize(12); + assertTrue(alg.data.length >= 12); + assertEquals(12, alg.size); + // Make sure it doesn't declare a new array since it doesn't have to + alg.data[2] = 5; + alg.resize(10); + assertTrue(alg.data.length >= 10); + assertEquals(10, alg.size); + assertEquals(5, alg.get(2)); + } + + @Test void resize_default() { + var alg = new DogArray_I16(2); + assertEquals(0, alg.size); + alg.resize(12, (short)1); + assertTrue(alg.data.length >= 12); + assertEquals(12, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals((double)1, alg.get(i)); + } + + // If the size has been reduced then there are no new elements to initialize + alg.resize(10, (short)2); + assertTrue(alg.data.length >= 10); + assertEquals(10, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals((short)1, alg.get(i)); + } + + // It's now larger and the new elements should be initialized + alg.resize(20, (short)3); + for (int i = 0; i < alg.size; i++) { + assertEquals((short)(i < 10 ? 1 : 3), alg.get(i)); + } + } + + @Test void resize_operator() { + var alg = new DogArray_I16(2); + assertEquals(0, alg.size); + alg.resize(12, ( idx ) -> (short)idx); + assertTrue(alg.data.length >= 12); + assertEquals(12, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals((short)i, alg.get(i)); + } + + // If the size has been reduced then there are no new elements to initialize + alg.resize(10, ( idx ) -> (short)2); + assertTrue(alg.data.length >= 10); + assertEquals(10, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals((short)i, alg.get(i)); + } + + // It's now larger and the new elements should be initialized + alg.resize(20, ( idx ) -> (short)(100 + idx)); + for (int i = 0; i < alg.size; i++) { + assertEquals((short)(i < 10 ? i : 100 + i), alg.get(i)); + } + } + + @Test void push_pop() { + var alg = new DogArray_I16(10); + + alg.push(1); + alg.push(3); + + assertEquals(2, alg.size); + assertEquals(3, alg.pop()); + assertEquals(1, alg.pop()); + assertEquals(0, alg.size); + } + + @Test void setTo_array_off() { + var alg = new DogArray_I16(10); + + short[] foo = new short[]{1, 3, 4, 5, 7}; + alg.setTo(foo, 1, 3); + assertEquals(3, alg.size); + for (int i = 0; i < 3; i++) { + assertEquals(alg.get(i), foo[i + 1]); + } + } + + @Test void setTo_array() { + var alg = new DogArray_I16(10); + + short[] array = new short[]{1, 3, 4, 5, 7}; + + assertSame(alg, alg.setTo(array)); + assertEquals(array.length, alg.size); + + for (int i = 0; i < array.length; i++) { + assertEquals(alg.get(i), array[i]); + } + } + + @Test void remove_swap() { + var alg = DogArray_I16.array(0, 0, 0, 0, 1); + alg.removeSwap(1); + assertEquals(4, alg.size); + alg.forIdx(( i, v ) -> assertEquals(i != 1 ? 0.0 : 1.0, v)); + } + + @Test void remove() { + var alg = new DogArray_I16(10); + + alg.push(1); + alg.push(3); + alg.push(4); + alg.push(5); + + alg.remove(1); + assertEquals(3, alg.size); + assertEquals(1, alg.get(0), 1e-8); + assertEquals(4, alg.get(1), 1e-8); + assertEquals(5, alg.get(2), 1e-8); + } + + @Test void remove_two() { + var alg = new DogArray_I16(10); + + alg.push(1); + alg.push(3); + alg.push(4); + alg.push(5); + alg.push(6); + + alg.remove(1, 1); + assertEquals(4, alg.size); + assertEquals(1, alg.get(0)); + assertEquals(4, alg.get(1)); + assertEquals(5, alg.get(2)); + assertEquals(6, alg.get(3)); + alg.remove(0, 1); + assertEquals(2, alg.size); + assertEquals(5, alg.get(0)); + assertEquals(6, alg.get(1)); + } + + @Override public DogArray_I16 declare( int maxsize ) { + return new DogArray_I16(maxsize); + } + + @Override public void push( DogArray_I16 queue, double value ) { + queue.push((short)value); + } + + @Override public void insert( DogArray_I16 queue, int index, double value ) { + queue.insert(index, (short)value); + } + + @Override public void check( DogArray_I16 queue, int index, double value ) { + assertEquals(value, queue.get(index), 1e-8); + } + + @Test void indexOf() { + var alg = new DogArray_I16(10); + + alg.push(1); + alg.push(3); + alg.push(4); + alg.push(5); + + assertEquals(1, alg.indexOf(3)); + assertEquals(-1, alg.indexOf(8)); + } + + @Test void sort() { + var alg = new DogArray_I16(6); + + alg.push(8); + alg.push(2); + alg.push(4); + alg.push(3); + + alg.sort(); + + assertEquals(4, alg.size); + assertEquals(2, alg.get(0), 1e-8); + assertEquals(3, alg.get(1), 1e-8); + assertEquals(4, alg.get(2), 1e-8); + assertEquals(8, alg.get(3), 1e-8); + } + + @Test void shuffle() { + int N = 20; + var alg = new DogArray_I16(N); + for (int i = 0; i < N; i++) { + alg.add(i); + } + alg.shuffle(rand); + int changed = 0; + for (int i = 0; i < N; i++) { + if (alg.get(i) != i) + changed++; + } + assertTrue(changed >= 18); + } + + @Test void getFraction() { + var alg = new DogArray_I16(20); + + for (int i = 0; i < 20; i++) { + alg.add(i); + } + + assertEquals(0, alg.getFraction(0.0)); + assertEquals(0, alg.getFraction(0.02)); + assertEquals(0, alg.getFraction(0.03)); + assertEquals(1, alg.getFraction(1.0/19.0)); + assertEquals(1, alg.getFraction(1.7/19.0)); + assertEquals(19/2, alg.getFraction(0.5)); + assertEquals(19, alg.getFraction(1.0)); + } + + @Test void indexOfGreatest() { + var alg = new DogArray_I16(20); + + assertEquals(-1, alg.indexOfGreatest()); + + alg.add(-3); + alg.add(-2); + alg.add(-1); + + assertEquals(2, alg.indexOfGreatest()); + } + + @Test void indexOfLeast() { + var alg = new DogArray_I16(20); + + assertEquals(-1, alg.indexOfLeast()); + + alg.add(-3); + alg.add(-2); + alg.add(-4); + + assertEquals(2, alg.indexOfLeast()); + } + + @Test void getTail() { + var alg = new DogArray_I16(20); + + for (int i = 0; i < 20; i++) { + alg.add(i); + assertEquals(alg.getTail(), alg.data[i]); + } + } + + @Test void getTail_idx() { + var alg = new DogArray_I16(20); + + for (int i = 0; i < 20; i++) { + alg.add(i); + } + + for (int i = 0; i < 20; i++) { + assertEquals(20 - i - 1, alg.getTail(i)); + } + } + + @Test void setTail() { + var alg = new DogArray_I16(20); + + for (int i = 0; i < 20; i++) { + alg.add(i); + } + + for (int i = 0; i < 20; i++) { + alg.setTail(i, -i); + } + for (int i = 0; i < 20; i++) { + assertEquals(-i, alg.getTail(i)); + } + } + + @Test void forIdx() { + var alg = DogArray_I16.array(1001, 1002, 1003, 1004, 1005); + alg.forIdx(( idx, value ) -> assertEquals(idx + 1001, value, 1e-8)); + } + + @Test void forEach() { + var alg = DogArray_I16.array(1001, 1002, 1003, 1004, 1005); + var cpy = new DogArray_I16(alg.size); + alg.forEach(cpy::add); + assertEquals(alg.size, cpy.size); + for (int i = 0; i < alg.size; i++) { + assertEquals(alg.get(i), cpy.get(i)); + } + } + + @Test void applyIdx() { + var alg = DogArray_I16.array(1, 2, 3, 4, 5); + alg.applyIdx(( idx, value ) -> (value < 3) ? 0 : value); + for (int i = 0; i < 2; i++) { + assertEquals(0, alg.get(i)); + } + for (int i = 2; i < alg.size; i++) { + assertEquals(i + 1, alg.get(i)); + } + } +} diff -Nru ddogleg-0.18+ds/test/org/ddogleg/struct/TestDogArray_I32.java ddogleg-0.22+ds/test/org/ddogleg/struct/TestDogArray_I32.java --- ddogleg-0.18+ds/test/org/ddogleg/struct/TestDogArray_I32.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/struct/TestDogArray_I32.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,433 @@ +/* + * Copyright (c) 2012-2022, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.struct; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * @author Peter Abeles + */ +public class TestDogArray_I32 extends ChecksDogArrayPrimitive { + @Test void range() { + DogArray_I32 alg = DogArray_I32.range(-1, 20); + + assertEquals(21, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals(i - 1, alg.get(i)); + } + } + + @Test void count() { + DogArray_I32 alg = DogArray_I32.array(0, 0, 1, 1, 1); + + assertEquals(2, alg.count(0)); + assertEquals(3, alg.count(1)); + } + + @Test void isEquals() { + DogArray_I32 alg = DogArray_I32.array(0, 0, 1, 1, 4); + assertTrue(alg.isEquals(0, 0, 1, 1, 4)); + assertFalse(alg.isEquals(0, 0, 1, 1)); + assertFalse(alg.isEquals(0, 0, 1, 2, 4)); + } + + @Test void addAll_queue() { + DogArray_I32 queue0 = new DogArray_I32(2); + DogArray_I32 queue1 = new DogArray_I32(3); + + queue0.add(1); + queue0.add(2); + + queue1.add(3); + queue1.add(4); + queue1.add(5); + + assertEquals(2, queue0.size); + queue0.addAll(queue1); + assertEquals(5, queue0.size); + for (int i = 0; i < queue0.size; i++) { + assertEquals(queue0.get(i), i + 1, 1e-8); + } + + queue0.reset(); + queue0.addAll(queue1); + assertEquals(3, queue0.size); + for (int i = 0; i < queue0.size; i++) { + assertEquals(queue0.get(i), i + 3, 1e-8); + } + } + + @Test void addAll_array() { + DogArray_I32 queue0 = new DogArray_I32(2); + int[] array = new int[]{3, 4, 5}; + + queue0.add(1); + queue0.add(2); + + assertEquals(2, queue0.size); + queue0.addAll(array, 0, 3); + assertEquals(5, queue0.size); + for (int i = 0; i < queue0.size; i++) { + assertEquals(queue0.get(i), i + 1, 1e-8); + } + + queue0.reset(); + queue0.addAll(array, 1, 3); + assertEquals(2, queue0.size); + for (int i = 0; i < queue0.size; i++) { + assertEquals(queue0.get(i), i + 4, 1e-8); + } + } + + @Test void auto_grow() { + DogArray_I32 alg = new DogArray_I32(3); + + assertEquals(3, alg.data.length); + + for (int i = 0; i < 10; i++) + alg.push(i); + + assertEquals(10, alg.size); + + for (int i = 0; i < 10; i++) + assertEquals(i, alg.get(i), 1e-8); + } + + @Test void reset() { + DogArray_I32 alg = new DogArray_I32(10); + + alg.push(1); + alg.push(3); + alg.push(-2); + + assertEquals(1.0, alg.get(0)); + assertEquals(3, alg.size); + + alg.reset(); + + assertEquals(0, alg.size); + } + + @Test void resize() { + DogArray_I32 alg = new DogArray_I32(2); + assertEquals(0, alg.size); + alg.resize(12); + assertTrue(alg.data.length >= 12); + assertEquals(12, alg.size); + // Make sure it doesn't declare a new array since it doesn't have to + alg.data[2] = 5; + alg.resize(10); + assertTrue(alg.data.length >= 10); + assertEquals(10, alg.size); + assertEquals(5, alg.get(2)); + } + + @Test void resize_default() { + var alg = new DogArray_I32(2); + assertEquals(0, alg.size); + alg.resize(12, (int)1); + assertTrue(alg.data.length >= 12); + assertEquals(12, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals((int)1, alg.get(i)); + } + + // If the size has been reduced then there are no new elements to initialize + alg.resize(10, (int)2); + assertTrue(alg.data.length >= 10); + assertEquals(10, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals((int)1, alg.get(i)); + } + + // It's now larger and the new elements should be initialized + alg.resize(20, (int)3); + for (int i = 0; i < alg.size; i++) { + assertEquals((int)(i < 10 ? 1 : 3), alg.get(i)); + } + } + + @Test void resize_operator() { + var alg = new DogArray_I32(2); + assertEquals(0, alg.size); + alg.resize(12, ( idx ) -> (int)idx); + assertTrue(alg.data.length >= 12); + assertEquals(12, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals((int)i, alg.get(i)); + } + + // If the size has been reduced then there are no new elements to initialize + alg.resize(10, ( idx ) -> (int)2); + assertTrue(alg.data.length >= 10); + assertEquals(10, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals((int)i, alg.get(i)); + } + + // It's now larger and the new elements should be initialized + alg.resize(20, ( idx ) -> (int)(100 + idx)); + for (int i = 0; i < alg.size; i++) { + assertEquals((int)(i < 10 ? i : 100 + i), alg.get(i)); + } + } + + @Test void push_pop() { + DogArray_I32 alg = new DogArray_I32(10); + + alg.push(1); + alg.push(3); + + assertEquals(2, alg.size); + assertEquals(3, alg.pop()); + assertEquals(1, alg.pop()); + assertEquals(0, alg.size); + } + + @Test void setTo_array_off() { + DogArray_I32 alg = new DogArray_I32(10); + + int[] foo = new int[]{1, 3, 4, 5, 7}; + alg.setTo(foo, 1, 3); + assertEquals(3, alg.size); + for (int i = 0; i < 3; i++) { + assertEquals(alg.get(i), foo[i + 1]); + } + } + + @Test void setTo_array() { + DogArray_I32 alg = new DogArray_I32(10); + + int[] array = new int[]{1, 3, 4, 5, 7}; + + assertSame(alg, alg.setTo(array)); + assertEquals(array.length, alg.size); + + for (int i = 0; i < array.length; i++) { + assertEquals(alg.get(i), array[i]); + } + } + + @Test void remove_swap() { + var alg = DogArray_I32.array(0, 0, 0, 0, 1); + alg.removeSwap(1); + assertEquals(4, alg.size); + alg.forIdx(( i, v ) -> assertEquals(i != 1 ? 0.0 : 1.0, v)); + } + + @Test void remove() { + var alg = new DogArray_I32(10); + + alg.push(1); + alg.push(3); + alg.push(4); + alg.push(5); + + alg.remove(1); + assertEquals(3, alg.size); + assertEquals(1, alg.get(0), 1e-8); + assertEquals(4, alg.get(1), 1e-8); + assertEquals(5, alg.get(2), 1e-8); + } + + @Test void remove_two() { + DogArray_I32 alg = new DogArray_I32(10); + + alg.push(1); + alg.push(3); + alg.push(4); + alg.push(5); + alg.push(6); + + alg.remove(1, 1); + assertEquals(4, alg.size); + assertEquals(1, alg.get(0)); + assertEquals(4, alg.get(1)); + assertEquals(5, alg.get(2)); + assertEquals(6, alg.get(3)); + alg.remove(0, 1); + assertEquals(2, alg.size); + assertEquals(5, alg.get(0)); + assertEquals(6, alg.get(1)); + } + + @Override public DogArray_I32 declare( int maxsize ) { + return new DogArray_I32(maxsize); + } + + @Override public void push( DogArray_I32 queue, double value ) { + queue.push((int)value); + } + + @Override public void insert( DogArray_I32 queue, int index, double value ) { + queue.insert(index, (int)value); + } + + @Override public void check( DogArray_I32 queue, int index, double value ) { + assertEquals(value, queue.get(index), 1e-8); + } + + @Test void indexOf() { + var alg = new DogArray_I32(10); + + alg.push(1); + alg.push(3); + alg.push(4); + alg.push(5); + + assertEquals(1, alg.indexOf(3)); + assertEquals(-1, alg.indexOf(8)); + } + + @Test void sort() { + var alg = new DogArray_I32(6); + + alg.push(8); + alg.push(2); + alg.push(4); + alg.push(3); + + alg.sort(); + + assertEquals(4, alg.size); + assertEquals(2, alg.get(0), 1e-8); + assertEquals(3, alg.get(1), 1e-8); + assertEquals(4, alg.get(2), 1e-8); + assertEquals(8, alg.get(3), 1e-8); + } + + @Test void shuffle() { + int N = 20; + var alg = new DogArray_I32(N); + for (int i = 0; i < N; i++) { + alg.add(i); + } + alg.shuffle(rand); + int changed = 0; + for (int i = 0; i < N; i++) { + if (alg.get(i) != i) + changed++; + } + assertTrue(changed >= 18); + } + + @Test void getFraction() { + var alg = new DogArray_I32(20); + + for (int i = 0; i < 20; i++) { + alg.add(i); + } + + assertEquals(0, alg.getFraction(0.0)); + assertEquals(0, alg.getFraction(0.02)); + assertEquals(0, alg.getFraction(0.03)); + assertEquals(1, alg.getFraction(1.0/19.0)); + assertEquals(1, alg.getFraction(1.7/19.0)); + assertEquals(19/2, alg.getFraction(0.5)); + assertEquals(19, alg.getFraction(1.0)); + } + + @Test void indexOfGreatest() { + var alg = new DogArray_I32(20); + + assertEquals(-1, alg.indexOfGreatest()); + + alg.add(-3); + alg.add(-2); + alg.add(-1); + + assertEquals(2, alg.indexOfGreatest()); + } + + @Test void indexOfLeast() { + var alg = new DogArray_I32(20); + + assertEquals(-1, alg.indexOfLeast()); + + alg.add(-3); + alg.add(-2); + alg.add(-4); + + assertEquals(2, alg.indexOfLeast()); + } + + @Test void getTail() { + var alg = new DogArray_I32(20); + + for (int i = 0; i < 20; i++) { + alg.add(i); + assertEquals(alg.getTail(), alg.data[i]); + } + } + + @Test void getTail_idx() { + var alg = new DogArray_I32(20); + + for (int i = 0; i < 20; i++) { + alg.add(i); + } + + for (int i = 0; i < 20; i++) { + assertEquals(20 - i - 1, alg.getTail(i)); + } + } + + @Test void setTail() { + var alg = new DogArray_I32(20); + + for (int i = 0; i < 20; i++) { + alg.add(i); + } + + for (int i = 0; i < 20; i++) { + alg.setTail(i, -i); + } + for (int i = 0; i < 20; i++) { + assertEquals(-i, alg.getTail(i)); + } + } + + @Test void forIdx() { + DogArray_I32 alg = DogArray_I32.array(1, 2, 3, 4, 5); + alg.forIdx(( idx, value ) -> assertEquals(idx + 1, value, 1e-8)); + } + + @Test void forEach() { + DogArray_I32 alg = DogArray_I32.array(1, 2, 3, 4, 5); + var cpy = new DogArray_I32(alg.size); + alg.forEach(cpy::add); + assertEquals(alg.size, cpy.size); + for (int i = 0; i < alg.size; i++) { + assertEquals(alg.get(i), cpy.get(i)); + } + } + + @Test void applyIdx() { + DogArray_I32 alg = DogArray_I32.array(1, 2, 3, 4, 5); + alg.applyIdx(( idx, value ) -> (value < 3) ? 0 : value); + for (int i = 0; i < 2; i++) { + assertEquals(0, alg.get(i)); + } + for (int i = 2; i < alg.size; i++) { + assertEquals(i + 1, alg.get(i)); + } + } +} diff -Nru ddogleg-0.18+ds/test/org/ddogleg/struct/TestDogArray_I64.java ddogleg-0.22+ds/test/org/ddogleg/struct/TestDogArray_I64.java --- ddogleg-0.18+ds/test/org/ddogleg/struct/TestDogArray_I64.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/struct/TestDogArray_I64.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,433 @@ +/* + * Copyright (c) 2012-2022, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.struct; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * @author Peter Abeles + */ +public class TestDogArray_I64 extends ChecksDogArrayPrimitive { + @Test void range() { + DogArray_I64 alg = DogArray_I64.range(-1, 20); + + assertEquals(21, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals(i - 1, alg.get(i)); + } + } + + @Test void count() { + DogArray_I64 alg = DogArray_I64.array(0, 0, 1, 1, 1); + + assertEquals(2, alg.count(0)); + assertEquals(3, alg.count(1)); + } + + @Test void isEquals() { + DogArray_I64 alg = DogArray_I64.array(0, 0, 1, 1, 4); + assertTrue(alg.isEquals(0, 0, 1, 1, 4)); + assertFalse(alg.isEquals(0, 0, 1, 1)); + assertFalse(alg.isEquals(0, 0, 1, 2, 4)); + } + + @Test void addAll_queue() { + DogArray_I64 queue0 = new DogArray_I64(2); + DogArray_I64 queue1 = new DogArray_I64(3); + + queue0.add(1); + queue0.add(2); + + queue1.add(3); + queue1.add(4); + queue1.add(5); + + assertEquals(2, queue0.size); + queue0.addAll(queue1); + assertEquals(5, queue0.size); + for (int i = 0; i < queue0.size; i++) { + assertEquals(queue0.get(i), i + 1, 1e-8); + } + + queue0.reset(); + queue0.addAll(queue1); + assertEquals(3, queue0.size); + for (int i = 0; i < queue0.size; i++) { + assertEquals(queue0.get(i), i + 3, 1e-8); + } + } + + @Test void addAll_array() { + DogArray_I64 queue0 = new DogArray_I64(2); + long[] array = new long[]{3, 4, 5}; + + queue0.add(1); + queue0.add(2); + + assertEquals(2, queue0.size); + queue0.addAll(array, 0, 3); + assertEquals(5, queue0.size); + for (int i = 0; i < queue0.size; i++) { + assertEquals(queue0.get(i), i + 1, 1e-8); + } + + queue0.reset(); + queue0.addAll(array, 1, 3); + assertEquals(2, queue0.size); + for (int i = 0; i < queue0.size; i++) { + assertEquals(queue0.get(i), i + 4, 1e-8); + } + } + + @Test void auto_grow() { + DogArray_I64 alg = new DogArray_I64(3); + + assertEquals(3, alg.data.length); + + for (int i = 0; i < 10; i++) + alg.push(i); + + assertEquals(10, alg.size); + + for (int i = 0; i < 10; i++) + assertEquals(i, alg.get(i), 1e-8); + } + + @Test void reset() { + DogArray_I64 alg = new DogArray_I64(10); + + alg.push(1); + alg.push(3); + alg.push(-2); + + assertEquals(1.0, alg.get(0)); + assertEquals(3, alg.size); + + alg.reset(); + + assertEquals(0, alg.size); + } + + @Test void resize() { + DogArray_I64 alg = new DogArray_I64(2); + assertEquals(0, alg.size); + alg.resize(12); + assertTrue(alg.data.length >= 12); + assertEquals(12, alg.size); + // Make sure it doesn't declare a new array since it doesn't have to + alg.data[2] = 5; + alg.resize(10); + assertTrue(alg.data.length >= 10); + assertEquals(10, alg.size); + assertEquals(5, alg.get(2)); + } + + @Test void resize_default() { + var alg = new DogArray_I64(2); + assertEquals(0, alg.size); + alg.resize(12, (long)1); + assertTrue(alg.data.length >= 12); + assertEquals(12, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals((long)1, alg.get(i)); + } + + // If the size has been reduced then there are no new elements to initialize + alg.resize(10, (long)2); + assertTrue(alg.data.length >= 10); + assertEquals(10, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals((double)1, alg.get(i)); + } + + // It's now larger and the new elements should be initialized + alg.resize(20, (long)3); + for (int i = 0; i < alg.size; i++) { + assertEquals((long)(i < 10 ? 1 : 3), alg.get(i)); + } + } + + @Test void resize_operator() { + var alg = new DogArray_I64(2); + assertEquals(0, alg.size); + alg.resize(12, ( idx ) -> (long)idx); + assertTrue(alg.data.length >= 12); + assertEquals(12, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals((long)i, alg.get(i)); + } + + // If the size has been reduced then there are no new elements to initialize + alg.resize(10, ( idx ) -> (long)2); + assertTrue(alg.data.length >= 10); + assertEquals(10, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals((long)i, alg.get(i)); + } + + // It's now larger and the new elements should be initialized + alg.resize(20, ( idx ) -> (long)(100 + idx)); + for (int i = 0; i < alg.size; i++) { + assertEquals((long)(i < 10 ? i : 100 + i), alg.get(i)); + } + } + + @Test void push_pop() { + DogArray_I64 alg = new DogArray_I64(10); + + alg.push(1); + alg.push(3); + + assertEquals(2, alg.size); + assertEquals(3, alg.pop()); + assertEquals(1, alg.pop()); + assertEquals(0, alg.size); + } + + @Test void setTo_array_off() { + DogArray_I64 alg = new DogArray_I64(10); + + long[] foo = new long[]{1, 3, 4, 5, 7}; + alg.setTo(foo, 1, 3); + assertEquals(3, alg.size); + for (int i = 0; i < 3; i++) { + assertEquals(alg.get(i), foo[i + 1]); + } + } + + @Test void setTo_array() { + DogArray_I64 alg = new DogArray_I64(10); + + long[] array = new long[]{1, 3, 4, 5, 7}; + + assertSame(alg, alg.setTo(array)); + assertEquals(array.length, alg.size); + + for (int i = 0; i < array.length; i++) { + assertEquals(alg.get(i), array[i]); + } + } + + @Test void remove_swap() { + var alg = DogArray_I64.array(0, 0, 0, 0, 1); + alg.removeSwap(1); + assertEquals(4, alg.size); + alg.forIdx(( i, v ) -> assertEquals(i != 1 ? 0.0 : 1.0, v)); + } + + @Test void remove() { + var alg = new DogArray_I64(10); + + alg.push(1); + alg.push(3); + alg.push(4); + alg.push(5); + + alg.remove(1); + assertEquals(3, alg.size); + assertEquals(1, alg.get(0), 1e-8); + assertEquals(4, alg.get(1), 1e-8); + assertEquals(5, alg.get(2), 1e-8); + } + + @Test void remove_two() { + DogArray_I64 alg = new DogArray_I64(10); + + alg.push(1); + alg.push(3); + alg.push(4); + alg.push(5); + alg.push(6); + + alg.remove(1, 1); + assertEquals(4, alg.size); + assertEquals(1, alg.get(0)); + assertEquals(4, alg.get(1)); + assertEquals(5, alg.get(2)); + assertEquals(6, alg.get(3)); + alg.remove(0, 1); + assertEquals(2, alg.size); + assertEquals(5, alg.get(0)); + assertEquals(6, alg.get(1)); + } + + @Override public DogArray_I64 declare( int maxsize ) { + return new DogArray_I64(maxsize); + } + + @Override public void push( DogArray_I64 queue, double value ) { + queue.push((long)value); + } + + @Override public void insert( DogArray_I64 queue, int index, double value ) { + queue.insert(index, (long)value); + } + + @Override public void check( DogArray_I64 queue, int index, double value ) { + assertEquals(value, queue.get(index), 1e-8); + } + + @Test void indexOf() { + var alg = new DogArray_I64(10); + + alg.push(1); + alg.push(3); + alg.push(4); + alg.push(5); + + assertEquals(1, alg.indexOf(3)); + assertEquals(-1, alg.indexOf(8)); + } + + @Test void sort() { + var alg = new DogArray_I64(6); + + alg.push(8); + alg.push(2); + alg.push(4); + alg.push(3); + + alg.sort(); + + assertEquals(4, alg.size); + assertEquals(2, alg.get(0), 1e-8); + assertEquals(3, alg.get(1), 1e-8); + assertEquals(4, alg.get(2), 1e-8); + assertEquals(8, alg.get(3), 1e-8); + } + + @Test void shuffle() { + int N = 20; + var alg = new DogArray_I64(N); + for (int i = 0; i < N; i++) { + alg.add(i); + } + alg.shuffle(rand); + int changed = 0; + for (int i = 0; i < N; i++) { + if (alg.get(i) != i) + changed++; + } + assertTrue(changed >= 18); + } + + @Test void getFraction() { + var alg = new DogArray_I64(20); + + for (int i = 0; i < 20; i++) { + alg.add(i); + } + + assertEquals(0, alg.getFraction(0.0)); + assertEquals(0, alg.getFraction(0.02)); + assertEquals(0, alg.getFraction(0.03)); + assertEquals(1, alg.getFraction(1.0/19.0)); + assertEquals(1, alg.getFraction(1.7/19.0)); + assertEquals(19/2, alg.getFraction(0.5)); + assertEquals(19, alg.getFraction(1.0)); + } + + @Test void indexOfGreatest() { + var alg = new DogArray_I64(20); + + assertEquals(-1, alg.indexOfGreatest()); + + alg.add(-3); + alg.add(-2); + alg.add(-1); + + assertEquals(2, alg.indexOfGreatest()); + } + + @Test void indexOfLeast() { + var alg = new DogArray_I64(20); + + assertEquals(-1, alg.indexOfLeast()); + + alg.add(-3); + alg.add(-2); + alg.add(-4); + + assertEquals(2, alg.indexOfLeast()); + } + + @Test void getTail() { + var alg = new DogArray_I64(20); + + for (int i = 0; i < 20; i++) { + alg.add(i); + assertEquals(alg.getTail(), alg.data[i]); + } + } + + @Test void getTail_idx() { + var alg = new DogArray_I64(20); + + for (int i = 0; i < 20; i++) { + alg.add(i); + } + + for (int i = 0; i < 20; i++) { + assertEquals(20 - i - 1, alg.getTail(i)); + } + } + + @Test void setTail() { + var alg = new DogArray_I64(20); + + for (int i = 0; i < 20; i++) { + alg.add(i); + } + + for (int i = 0; i < 20; i++) { + alg.setTail(i, -i); + } + for (int i = 0; i < 20; i++) { + assertEquals(-i, alg.getTail(i)); + } + } + + @Test void forIdx() { + DogArray_I64 alg = DogArray_I64.array(1, 2, 3, 4, 5); + alg.forIdx(( idx, value ) -> assertEquals(idx + 1, value, 1e-8)); + } + + @Test void forEach() { + DogArray_I64 alg = DogArray_I64.array(1, 2, 3, 4, 5); + var cpy = new DogArray_I64(alg.size); + alg.forEach(cpy::add); + assertEquals(alg.size, cpy.size); + for (int i = 0; i < alg.size; i++) { + assertEquals(alg.get(i), cpy.get(i)); + } + } + + @Test void applyIdx() { + DogArray_I64 alg = DogArray_I64.array(1, 2, 3, 4, 5); + alg.applyIdx(( idx, value ) -> (value < 3) ? 0 : value); + for (int i = 0; i < 2; i++) { + assertEquals(0, alg.get(i)); + } + for (int i = 2; i < alg.size; i++) { + assertEquals(i + 1, alg.get(i)); + } + } +} diff -Nru ddogleg-0.18+ds/test/org/ddogleg/struct/TestDogArray_I8.java ddogleg-0.22+ds/test/org/ddogleg/struct/TestDogArray_I8.java --- ddogleg-0.18+ds/test/org/ddogleg/struct/TestDogArray_I8.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/struct/TestDogArray_I8.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,433 @@ +/* + * Copyright (c) 2012-2022, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.struct; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * @author Peter Abeles + */ +public class TestDogArray_I8 extends ChecksDogArrayPrimitive { + @Test void range() { + DogArray_I8 alg = DogArray_I8.range(-1, 20); + + assertEquals(21, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals(i - 1, alg.get(i)); + } + } + + @Test void count() { + DogArray_I8 alg = DogArray_I8.array(0, 0, 1, 1, 1); + + assertEquals(2, alg.count(0)); + assertEquals(3, alg.count(1)); + } + + @Test void isEquals() { + DogArray_I8 alg = DogArray_I8.array(0, 0, 1, 1, 4); + assertTrue(alg.isEquals(0, 0, 1, 1, 4)); + assertFalse(alg.isEquals(0, 0, 1, 1)); + assertFalse(alg.isEquals(0, 0, 1, 2, 4)); + } + + @Test void addAll_queue() { + DogArray_I8 queue0 = new DogArray_I8(2); + DogArray_I8 queue1 = new DogArray_I8(3); + + queue0.add(1); + queue0.add(2); + + queue1.add(3); + queue1.add(4); + queue1.add(5); + + assertEquals(2, queue0.size); + queue0.addAll(queue1); + assertEquals(5, queue0.size); + for (int i = 0; i < queue0.size; i++) { + assertEquals(queue0.get(i), i + 1, 1e-8); + } + + queue0.reset(); + queue0.addAll(queue1); + assertEquals(3, queue0.size); + for (int i = 0; i < queue0.size; i++) { + assertEquals(queue0.get(i), i + 3, 1e-8); + } + } + + @Test void addAll_array() { + DogArray_I8 queue0 = new DogArray_I8(2); + byte[] array = new byte[]{3, 4, 5}; + + queue0.add(1); + queue0.add(2); + + assertEquals(2, queue0.size); + queue0.addAll(array, 0, 3); + assertEquals(5, queue0.size); + for (int i = 0; i < queue0.size; i++) { + assertEquals(queue0.get(i), i + 1, 1e-8); + } + + queue0.reset(); + queue0.addAll(array, 1, 3); + assertEquals(2, queue0.size); + for (int i = 0; i < queue0.size; i++) { + assertEquals(queue0.get(i), i + 4, 1e-8); + } + } + + @Test void auto_grow() { + DogArray_I8 alg = new DogArray_I8(3); + + assertEquals(3, alg.data.length); + + for (int i = 0; i < 10; i++) + alg.push(i); + + assertEquals(10, alg.size); + + for (int i = 0; i < 10; i++) + assertEquals(i, alg.get(i), 1e-8); + } + + @Test void reset() { + DogArray_I8 alg = new DogArray_I8(10); + + alg.push(1); + alg.push(3); + alg.push(-2); + + assertEquals(1.0, alg.get(0)); + assertEquals(3, alg.size); + + alg.reset(); + + assertEquals(0, alg.size); + } + + @Test void resize() { + DogArray_I8 alg = new DogArray_I8(2); + assertEquals(0, alg.size); + alg.resize(12); + assertTrue(alg.data.length >= 12); + assertEquals(12, alg.size); + // Make sure it doesn't declare a new array since it doesn't have to + alg.data[2] = 5; + alg.resize(10); + assertTrue(alg.data.length >= 10); + assertEquals(10, alg.size); + assertEquals(5, alg.get(2)); + } + + @Test void resize_default() { + var alg = new DogArray_I8(2); + assertEquals(0, alg.size); + alg.resize(12, (byte)1); + assertTrue(alg.data.length >= 12); + assertEquals(12, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals((double)1, alg.get(i)); + } + + // If the size has been reduced then there are no new elements to initialize + alg.resize(10, (byte)2); + assertTrue(alg.data.length >= 10); + assertEquals(10, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals((byte)1, alg.get(i)); + } + + // It's now larger and the new elements should be initialized + alg.resize(20, (byte)3); + for (int i = 0; i < alg.size; i++) { + assertEquals((byte)(i < 10 ? 1 : 3), alg.get(i)); + } + } + + @Test void resize_operator() { + var alg = new DogArray_I8(2); + assertEquals(0, alg.size); + alg.resize(12, ( idx ) -> (byte)idx); + assertTrue(alg.data.length >= 12); + assertEquals(12, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals((byte)i, alg.get(i)); + } + + // If the size has been reduced then there are no new elements to initialize + alg.resize(10, ( idx ) -> (byte)2); + assertTrue(alg.data.length >= 10); + assertEquals(10, alg.size); + for (int i = 0; i < alg.size; i++) { + assertEquals((byte)i, alg.get(i)); + } + + // It's now larger and the new elements should be initialized + alg.resize(20, ( idx ) -> (byte)(100 + idx)); + for (int i = 0; i < alg.size; i++) { + assertEquals((byte)(i < 10 ? i : 100 + i), alg.get(i)); + } + } + + @Test void push_pop() { + DogArray_I8 alg = new DogArray_I8(10); + + alg.push(1); + alg.push(3); + + assertEquals(2, alg.size); + assertEquals(3, alg.pop()); + assertEquals(1, alg.pop()); + assertEquals(0, alg.size); + } + + @Test void setTo_array_off() { + DogArray_I8 alg = new DogArray_I8(10); + + byte[] foo = new byte[]{1, 3, 4, 5, 7}; + alg.setTo(foo, 1, 3); + assertEquals(3, alg.size); + for (int i = 0; i < 3; i++) { + assertEquals(alg.get(i), foo[i + 1]); + } + } + + @Test void setTo_array() { + DogArray_I8 alg = new DogArray_I8(10); + + byte[] array = new byte[]{1, 3, 4, 5, 7}; + + assertSame(alg, alg.setTo(array)); + assertEquals(array.length, alg.size); + + for (int i = 0; i < array.length; i++) { + assertEquals(alg.get(i), array[i]); + } + } + + @Test void remove_swap() { + var alg = DogArray_I8.array(0, 0, 0, 0, 1); + alg.removeSwap(1); + assertEquals(4, alg.size); + alg.forIdx(( i, v ) -> assertEquals(i != 1 ? 0.0 : 1.0, v)); + } + + @Test void remove() { + var alg = new DogArray_I8(10); + + alg.push(1); + alg.push(3); + alg.push(4); + alg.push(5); + + alg.remove(1); + assertEquals(3, alg.size); + assertEquals(1, alg.get(0), 1e-8); + assertEquals(4, alg.get(1), 1e-8); + assertEquals(5, alg.get(2), 1e-8); + } + + @Test void remove_two() { + DogArray_I8 alg = new DogArray_I8(10); + + alg.push(1); + alg.push(3); + alg.push(4); + alg.push(5); + alg.push(6); + + alg.remove(1, 1); + assertEquals(4, alg.size); + assertEquals(1, alg.get(0)); + assertEquals(4, alg.get(1)); + assertEquals(5, alg.get(2)); + assertEquals(6, alg.get(3)); + alg.remove(0, 1); + assertEquals(2, alg.size); + assertEquals(5, alg.get(0)); + assertEquals(6, alg.get(1)); + } + + @Override public DogArray_I8 declare( int maxsize ) { + return new DogArray_I8(maxsize); + } + + @Override public void push( DogArray_I8 queue, double value ) { + queue.push((byte)value); + } + + @Override public void insert( DogArray_I8 queue, int index, double value ) { + queue.insert(index, (byte)value); + } + + @Override public void check( DogArray_I8 queue, int index, double value ) { + assertEquals(value, queue.get(index), 1e-8); + } + + @Test void indexOf() { + var alg = new DogArray_I8(10); + + alg.push(1); + alg.push(3); + alg.push(4); + alg.push(5); + + assertEquals(1, alg.indexOf(3)); + assertEquals(-1, alg.indexOf(8)); + } + + @Test void sort() { + var alg = new DogArray_I8(6); + + alg.push(8); + alg.push(2); + alg.push(4); + alg.push(3); + + alg.sort(); + + assertEquals(4, alg.size); + assertEquals(2, alg.get(0), 1e-8); + assertEquals(3, alg.get(1), 1e-8); + assertEquals(4, alg.get(2), 1e-8); + assertEquals(8, alg.get(3), 1e-8); + } + + @Test void shuffle() { + int N = 20; + var alg = new DogArray_I8(N); + for (int i = 0; i < N; i++) { + alg.add(i); + } + alg.shuffle(rand); + int changed = 0; + for (int i = 0; i < N; i++) { + if (alg.get(i) != i) + changed++; + } + assertTrue(changed >= 18); + } + + @Test void getFraction() { + var alg = new DogArray_I8(20); + + for (int i = 0; i < 20; i++) { + alg.add(i); + } + + assertEquals(0, alg.getFraction(0.0)); + assertEquals(0, alg.getFraction(0.02)); + assertEquals(0, alg.getFraction(0.03)); + assertEquals(1, alg.getFraction(1.0/19.0)); + assertEquals(1, alg.getFraction(1.7/19.0)); + assertEquals(19/2, alg.getFraction(0.5)); + assertEquals(19, alg.getFraction(1.0)); + } + + @Test void indexOfGreatest() { + var alg = new DogArray_I8(20); + + assertEquals(-1, alg.indexOfGreatest()); + + alg.add(-3); + alg.add(-2); + alg.add(-1); + + assertEquals(2, alg.indexOfGreatest()); + } + + @Test void indexOfLeast() { + var alg = new DogArray_I8(20); + + assertEquals(-1, alg.indexOfLeast()); + + alg.add(-3); + alg.add(-2); + alg.add(-4); + + assertEquals(2, alg.indexOfLeast()); + } + + @Test void getTail() { + var alg = new DogArray_I8(20); + + for (int i = 0; i < 20; i++) { + alg.add(i); + assertEquals(alg.getTail(), alg.data[i]); + } + } + + @Test void getTail_idx() { + var alg = new DogArray_I8(20); + + for (int i = 0; i < 20; i++) { + alg.add(i); + } + + for (int i = 0; i < 20; i++) { + assertEquals(20 - i - 1, alg.getTail(i)); + } + } + + @Test void setTail() { + var alg = new DogArray_I8(20); + + for (int i = 0; i < 20; i++) { + alg.add(i); + } + + for (int i = 0; i < 20; i++) { + alg.setTail(i, -i); + } + for (int i = 0; i < 20; i++) { + assertEquals(-i, alg.getTail(i)); + } + } + + @Test void forIdx() { + DogArray_I8 alg = DogArray_I8.array(1, 2, 3, 4, 5); + alg.forIdx(( idx, value ) -> assertEquals(idx + 1, value, 1e-8)); + } + + @Test void forEach() { + DogArray_I8 alg = DogArray_I8.array(1, 2, 3, 4, 5); + var cpy = new DogArray_I8(alg.size); + alg.forEach(cpy::add); + assertEquals(alg.size, cpy.size); + for (int i = 0; i < alg.size; i++) { + assertEquals(alg.get(i), cpy.get(i)); + } + } + + @Test void applyIdx() { + DogArray_I8 alg = DogArray_I8.array(1, 2, 3, 4, 5); + alg.applyIdx(( idx, value ) -> (value < 3) ? 0 : value); + for (int i = 0; i < 2; i++) { + assertEquals(0, alg.get(i)); + } + for (int i = 2; i < alg.size; i++) { + assertEquals(i + 1, alg.get(i)); + } + } +} diff -Nru ddogleg-0.18+ds/test/org/ddogleg/struct/TestDogArray.java ddogleg-0.22+ds/test/org/ddogleg/struct/TestDogArray.java --- ddogleg-0.18+ds/test/org/ddogleg/struct/TestDogArray.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/struct/TestDogArray.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,374 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.struct; + +import org.ejml.UtilEjml; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * @author Peter Abeles + */ +class TestDogArray { + + /** + * makes sure reset function is called when the grow command is used + */ + @Test + void reset_grow() { + DogArray alg = new DogArray<>(DummyData::new, ( d ) -> d.value = 2); + + for (int i = 0; i < 5; i++) { + DummyData d = alg.grow(); + assertEquals(2, d.value); + } + assertEquals(5, alg.size); + } + + /** + * makes sure reset function is called when the grow command is used + */ + @Test + void reset_resize() { + DogArray alg = new DogArray<>(DummyData::new, ( d ) -> d.value = 2); + + alg.resize(3); + for (int i = 0; i < alg.size; i++) { + alg.get(i).value = 100; + } + + // resize again and make sure it doesn't reset elements already in the list + alg.resize(10); + for (int i = 0; i < 3; i++) { + assertEquals(100, alg.get(i).value); + } + for (int i = 3; i < 10; i++) { + assertEquals(2, alg.get(i).value); + } + } + + @Test + void resize_Initialize() { + DogArray alg = new DogArray<>(DummyData::new, ( d ) -> d.value = 2); + + alg.resize(3, ( o ) -> o.value = 100); + alg.resize(10, ( o ) -> o.value = 200); + + for (int i = 0; i < 3; i++) { + assertEquals(100, alg.get(i).value); + } + for (int i = 3; i < 10; i++) { + assertEquals(200, alg.get(i).value); + } + } + + @Test + void resize_InitializeIdx() { + DogArray alg = new DogArray<>(DummyData::new, ( d ) -> d.value = 2); + + alg.resize(3, ( idx, o ) -> o.value = 100 + idx); + alg.resize(10, ( idx, o ) -> o.value = 200 + idx); + + for (int i = 0; i < 3; i++) { + assertEquals(100 + i, alg.get(i).value); + } + for (int i = 3; i < 10; i++) { + assertEquals(200 + i, alg.get(i).value); + } + } + + @Test + void checkDeclareInstance() { + DogArray alg = new DogArray<>(DummyData::new); + + assertTrue(alg.getMaxSize() > 0); + assertNotNull(alg.data[0]); + } + + @Test + void toList() { + DogArray alg = new DogArray<>(DummyData::new); + + List l = alg.toList(); + assertEquals(0, l.size()); + + alg.grow().value = 1; + alg.grow().value = 1; + alg.grow().value = 2; + alg.removeTail(); + + l = alg.toList(); + assertEquals(2, l.size()); + assertEquals(1, l.get(0).value); + assertEquals(1, l.get(1).value); + } + + @Test + void remove_indexes() { + DogArray alg = new DogArray<>(10, DummyData::new); + for (int i = 0; i < 10; i++) { + alg.grow().value = i; + } + + int[] indexes = new int[]{0, 1, 4, 2, 6, 8, 9}; + alg.remove(indexes, 2, 6, null); + assertEquals(6, alg.size()); + assertEquals(0, alg.get(0).value); + assertEquals(1, alg.get(1).value); + assertEquals(3, alg.get(2).value); + assertEquals(5, alg.get(3).value); + assertEquals(7, alg.get(4).value); + assertEquals(9, alg.get(5).value); + + // make sure original objects were recycled properly + for (int i = 0; i < 10; i++) { + for (int j = i + 1; j < 10; j++) { + assertNotEquals(alg.data[i].value, alg.data[j].value); + } + } + } + + @Test + void remove_indexes_RemoveNothing() { + DogArray alg = new DogArray<>(10, DummyData::new); + for (int i = 0; i < 10; i++) { + alg.grow().value = i; + } + int[] indexes = new int[]{}; + alg.remove(indexes, 2, 2, null); + assertEquals(10, alg.size()); + } + + @Test + void removeTail() { + DogArray alg = new DogArray<>(10, DummyData::new); + + alg.grow(); + assertEquals(1, alg.size); + alg.removeTail(); + assertEquals(0, alg.size); + } + + @Test + void copyAll() { + List data = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + DummyData d = new DummyData(); + d.value = i; + data.add(d); + } + DogArray alg = new DogArray<>(DummyData::new); + alg.copyAll(data, ( src, dst ) -> dst.value = src.value); + + for (int i = 0; i < 10; i++) { + assertNotEquals(data.get(i), alg.get(i)); + assertEquals(i, alg.get(i).value); + } + } + + @Test + void remove_index() { + DogArray alg = new DogArray<>(10, DummyData::new); + + List l = alg.toList(); + assertEquals(0, l.size()); + + alg.grow().value = 1; + alg.grow().value = 2; + alg.grow().value = 3; + + alg.remove(1); + + assertEquals(2, alg.size()); + assertEquals(1, alg.get(0).value); + assertEquals(3, alg.get(1).value); + // make sure the data was shifted to the end + assertEquals(2, alg.data[2].value); + + alg.remove(1); + assertEquals(1, alg.size()); + assertEquals(1, alg.get(0).value); + assertEquals(3, alg.data[1].value); + assertEquals(2, alg.data[2].value); + + alg.remove(0); + assertEquals(0, alg.size()); + assertEquals(1, alg.data[0].value); + assertEquals(3, alg.data[1].value); + assertEquals(2, alg.data[2].value); + } + + @Test + void remove_object() { + DogArray alg = new DogArray<>(DummyData::new); + + alg.grow().value = 10; + alg.grow().value = 11; + alg.grow().value = 12; + + assertFalse(alg.remove(new DummyData())); + + assertTrue(alg.remove(alg.get(1))); + assertEquals(2, alg.size); + assertEquals(10, alg.get(0).value); + assertEquals(12, alg.get(1).value); + } + + @Test + void removeSwap() { + DogArray alg = new DogArray<>(10, DummyData::new); + + List l = alg.toList(); + assertEquals(0, l.size()); + + alg.grow().value = 1; + DummyData d = alg.get(0); + assertSame(d, alg.removeSwap(0)); + assertEquals(0, alg.size()); + + alg.grow().value = 1; + alg.grow().value = 2; + alg.grow().value = 3; + alg.grow().value = 4; + + alg.removeSwap(1); + + assertEquals(3, alg.size()); + assertEquals(1, alg.get(0).value); + assertEquals(4, alg.get(1).value); + assertEquals(3, alg.get(2).value); + // Make sure the removed element is at the tail + assertEquals(2, alg.data[3].value); + } + + @Test + void getTail() { + DogArray alg = new DogArray<>(10, DummyData::new); + + alg.grow(); + alg.grow(); + + assertSame(alg.data[1], alg.getTail()); + } + + @Test + void getTail_index() { + DogArray alg = new DogArray<>(10, DummyData::new); + + alg.grow(); + alg.grow(); + + for (int i = 0; i < alg.size(); i++) { + assertSame(alg.data[i], alg.getTail(alg.size - i - 1)); + } + } + + @Test + void get_pop() { + DogArray alg = new DogArray<>(DummyData::new); + + // test a failure case + try { + alg.get(0); + fail("Didn't fail"); + } catch (IllegalArgumentException ignore) { + } + + alg.grow(); + alg.get(0); + } + + @Test + void size() { + DogArray alg = new DogArray<>(DummyData::new); + assertEquals(0, alg.size); + alg.grow(); + assertEquals(1, alg.size); + } + + /** + * Checks to see if pop automatically grows correctly + */ + @Test + void pop_grow() { + DogArray alg = new DogArray<>(1, DummyData::new); + + int before = alg.getMaxSize(); + for (int i = 0; i < 20; i++) { + alg.grow(); + } + alg.get(19); + int after = alg.getMaxSize(); + assertTrue(after > before); + } + + @Test + void reserve() { + DogArray alg = new DogArray<>(1, DummyData::new); + + alg.grow().value = 10; + int before = alg.getMaxSize(); + alg.reserve(before + 5); + assertEquals(10, alg.get(0).value); + } + + @Test + void contains() { + DogArray queue = new DogArray<>(DummyData::new); + queue.grow(); + + assertFalse(queue.contains(new DummyData())); + + assertTrue(queue.contains(queue.get(0))); + } + + @Test + void indexOf() { + DogArray queue = new DogArray<>(100, DummyData::new); + queue.grow().value = 1; + queue.grow().value = 3; + queue.grow().value = 2; + + assertEquals(-1, queue.indexOf(new DummyData()), UtilEjml.TEST_F64); + assertEquals(0, queue.indexOf(queue.get(0)), UtilEjml.TEST_F64); + assertEquals(1, queue.indexOf(queue.get(1)), UtilEjml.TEST_F64); + assertEquals(2, queue.indexOf(queue.get(2)), UtilEjml.TEST_F64); + } + + @Test + void swap() { + DogArray queue = new DogArray<>(100, DummyData::new); + queue.grow().value = 1; + queue.grow().value = 2; + queue.grow().value = 3; + queue.grow().value = 4; + + queue.swap(0, 3); + queue.swap(0, 1); + + assertEquals(2, queue.get(0).value); + assertEquals(4, queue.get(1).value); + assertEquals(3, queue.get(2).value); + assertEquals(1, queue.get(3).value); + } +} diff -Nru ddogleg-0.18+ds/test/org/ddogleg/struct/TestDogArrayList.java ddogleg-0.22+ds/test/org/ddogleg/struct/TestDogArrayList.java --- ddogleg-0.18+ds/test/org/ddogleg/struct/TestDogArrayList.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/struct/TestDogArrayList.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.struct; + +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * @author Peter Abeles + */ +public class TestDogArrayList { + + @Test + public void size() { + DogArray queue = new DogArray<>(100,()->new Dummy(0)); + + List list = queue.toList(); + + assertEquals(0, list.size()); + + queue.grow().value = 1.0; + + assertEquals(1, list.size()); + } + + @Test + public void isEmpty() { + DogArray queue = new DogArray<>(100,()->new Dummy(0)); + + List list = queue.toList(); + + assertTrue( list.isEmpty() ); + + queue.grow().value = 1.0; + + assertFalse(list.isEmpty()); + } + + @Test + public void contains() { + DogArray queue = new DogArray<>(100,()->new Dummy(0)); + List list = queue.toList(); + + assertFalse(list.contains(new Dummy(1.0))); + + Dummy d = queue.grow(); + + assertTrue(list.contains(d)); + } + + @Test + public void iterator() { + DogArray queue = new DogArray<>(100,()->new Dummy(0)); + queue.grow().value = 1.0; + queue.grow().value = 2.0; + queue.grow().value = 3.0; + + Iterator iterator = queue.toList().iterator(); + assertTrue(iterator.hasNext()); + assertEquals(1.0, iterator.next().value); + assertTrue(iterator.hasNext()); + assertEquals(2.0, iterator.next().value); + assertTrue(iterator.hasNext()); + assertEquals(3.0, iterator.next().value); + assertFalse(iterator.hasNext()); + + } + + @Test + public void toArray() { + DogArray queue = new DogArray<>(100,()->new Dummy(0)); + queue.grow().value = 1.0; + queue.grow().value = 2.0; + queue.grow().value = 3.0; + + Object[] array = queue.toList().toArray(); + assertEquals(3, array.length); + assertEquals(1.0, ((Dummy) array[0]).value); + assertEquals(2.0, ((Dummy) array[1]).value); + assertEquals(3.0, ((Dummy) array[2]).value); + + // remove an element from the queue to make sure it isn't using array length + queue.removeTail(); + array = queue.toList().toArray(); + assertEquals(2, array.length); + } + + @Test + public void containsAll() { + DogArray queue = new DogArray<>(100,()->new Dummy(0)); + queue.grow().value = 1.0; + queue.grow().value = 2.0; + queue.grow().value = 3.0; + + List list = new ArrayList<>(); + list.add(queue.get(0)); + list.add(queue.get(1)); + + assertTrue(queue.toList().containsAll(list)); + + list.add(new Dummy(5.0)); + assertFalse(queue.toList().containsAll(list)); + } + + @Test + public void get() { + DogArray queue = new DogArray<>(100,()->new Dummy(0)); + queue.grow().value = 1.0; + queue.grow().value = 2.0; + + assertEquals(2.0, queue.toList().get(1).value); + } + + public static class Dummy { + double value; + + public Dummy(double value) { + this.value = value; + } + } +} diff -Nru ddogleg-0.18+ds/test/org/ddogleg/struct/TestDogLinkedList.java ddogleg-0.22+ds/test/org/ddogleg/struct/TestDogLinkedList.java --- ddogleg-0.18+ds/test/org/ddogleg/struct/TestDogLinkedList.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/struct/TestDogLinkedList.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,431 @@ +/* + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.struct; + +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * @author Peter Abeles + */ +@SuppressWarnings("NullAway") +public class TestDogLinkedList { + @Test + public void reset() { + DogLinkedList alg = new DogLinkedList<>(); + + alg.pushHead(1); + alg.pushHead(2); + alg.reset(); + assertEquals(2,alg.available.size()); + assertEquals(0,alg.size); + assertNull(alg.first); + assertNull(alg.last); + + checkList(alg); + } + + @Test + public void isEmpty() { + DogLinkedList alg = new DogLinkedList<>(); + + assertTrue(alg.isEmpty()); + alg.pushHead(1); + assertFalse(alg.isEmpty()); + alg.reset(); + assertTrue(alg.isEmpty()); + } + + @Test + public void getElement() { + DogLinkedList alg = new DogLinkedList<>(); + + DogLinkedList.Element e0 = alg.pushTail(1); + DogLinkedList.Element e1 = alg.pushTail(2); + DogLinkedList.Element e2 = alg.pushTail(2); + + assertSame(e0, alg.getElement(0, true)); + assertSame(e1, alg.getElement(1, true)); + assertSame(e2, alg.getElement(2, true)); + + assertSame(e2, alg.getElement(0, false)); + assertSame(e1, alg.getElement(1, false)); + assertSame(e0, alg.getElement(2, false)); + } + + @Test + public void pushHead() { + + DogLinkedList alg = new DogLinkedList<>(); + + DogLinkedList.Element e0 = alg.pushHead(1); + assertEquals(1,alg.size); + checkList(alg); + DogLinkedList.Element e1 = alg.pushHead(2); + assertEquals(2,alg.size); + assertSame(e1, alg.first); + assertSame(e0, alg.last); + checkList(alg); + + DogLinkedList.Element e2 = alg.pushHead(3); + assertEquals(3,alg.size); + assertSame(e2, alg.first); + assertSame(e0, alg.last); + checkList(alg); + } + + @Test + public void pushTail() { + DogLinkedList alg = new DogLinkedList<>(); + + DogLinkedList.Element e0 = alg.pushTail(1); + assertEquals(1,alg.size); + checkList(alg); + + DogLinkedList.Element e1 = alg.pushTail(2); + assertEquals(2,alg.size); + assertSame(e0, alg.first); + assertSame(e1, alg.last); + checkList(alg); + + DogLinkedList.Element e2 = alg.pushTail(3); + assertEquals(3,alg.size); + assertSame(e0, alg.first); + assertSame(e2, alg.last); + checkList(alg); + } + + @Test + public void insertAfter() { + DogLinkedList alg = new DogLinkedList<>(); + + DogLinkedList.Element e0 = alg.pushHead(1); + DogLinkedList.Element e1 = alg.insertAfter(e0, 2); + assertSame(e0.next, e1); + assertSame(e1.prev, e0); + assertEquals(2,alg.size); + checkList(alg); + + DogLinkedList.Element e2 = alg.insertAfter(e1, 2); + assertSame(e1.next, e2); + assertSame(e2.prev, e1); + assertSame(e2, alg.last); + assertEquals(3,alg.size); + checkList(alg); + } + + @Test + public void insertBefore() { + DogLinkedList alg = new DogLinkedList<>(); + + DogLinkedList.Element e0 = alg.pushHead(1); + DogLinkedList.Element e1 = alg.insertBefore(e0, 2); + assertSame(e0.prev, e1); + assertSame(e1.next, e0); + assertEquals(2,alg.size); + checkList(alg); + + DogLinkedList.Element e2 = alg.insertBefore(e1, 2); + assertSame(e1.prev, e2); + assertSame(e2.next, e1); + assertSame(e2, alg.first); + assertEquals(3,alg.size); + checkList(alg); + } + + @Test + public void swap() { + DogLinkedList alg = new DogLinkedList<>(); + + DogLinkedList.Element e0 = alg.pushTail(1); + DogLinkedList.Element e1 = alg.pushTail(2); + + alg.swap(e0,e1); + assertSame(alg.first, e1); + assertSame(alg.last, e0); + checkList(alg); + alg.swap(e0,e1); + assertSame(alg.first, e0); + assertSame(alg.last, e1); + checkList(alg); + + DogLinkedList.Element e2 = alg.pushTail(3); + alg.swap(e0,e1); + assertSame(alg.first, e1); + assertSame(alg.last, e2); + checkList(alg); + alg.swap(e0,e1); + assertSame(alg.first, e0); + checkList(alg); + alg.swap(e1,e2); + assertSame(alg.last, e1); + checkList(alg); + alg.swap(e1,e2); + assertSame(alg.last, e2); + checkList(alg); + + DogLinkedList.Element e3 = alg.pushTail(4); + alg.swap(e0,e2); + assertSame(alg.first, e2); + assertSame(alg.last, e3); + checkList(alg); + alg.swap(e0,e2); + checkList(alg); + assertSame(alg.first, e0); + assertSame(alg.last, e3); + + } + + @Test + public void remove() { + DogLinkedList alg = new DogLinkedList<>(); + + DogLinkedList.Element e0,e1; + + e0 = alg.pushTail(1); + alg.remove(e0); + assertEquals(0,alg.size); + assertEquals(1,alg.available.size()); + checkList(alg); + + e0 = alg.pushTail(1); + e1 = alg.pushTail(2); + alg.remove(e1); + assertSame(e0, alg.first); + assertEquals(1,alg.size); + assertEquals(1,alg.available.size()); + checkList(alg); + + e1 = alg.pushTail(2); + alg.remove(e0); + assertSame(e1, alg.first); + assertEquals(1,alg.size); + assertEquals(1,alg.available.size()); + checkList(alg); + + e0 = alg.pushHead(1); + alg.pushTail(3); + alg.remove(e1); + assertSame(e0, alg.first); + assertEquals(2,alg.size); + assertEquals(1,alg.available.size()); + checkList(alg); + } + + @Test + public void removeHead() { + DogLinkedList alg = new DogLinkedList<>(); + + alg.pushHead(1); + assertEquals((int) alg.removeHead(), 1); + assertEquals(0,alg.size); + assertEquals(1,alg.available.size()); + checkList(alg); + + alg.pushTail(1); + alg.pushTail(2); + assertEquals((int) alg.removeHead(), 1); + assertEquals(1,alg.size); + assertEquals(1,alg.available.size()); + checkList(alg); + } + + @Test + public void removeTail() { + DogLinkedList alg = new DogLinkedList<>(); + + alg.pushHead(1); + assertEquals(alg.removeTail(), 1); + assertEquals(0,alg.size); + assertEquals(1,alg.available.size()); + checkList(alg); + + alg.pushTail(1); + alg.pushTail(2); + assertEquals(alg.removeTail(), 2); + assertEquals(1,alg.size); + assertEquals(1,alg.available.size()); + checkList(alg); + } + + @Test + public void find() { + DogLinkedList alg = new DogLinkedList<>(); + + assertNull(alg.find(1)); + + DogLinkedList.Element e1 = alg.pushHead(1); + assertNull(alg.find(4)); + assertSame(e1, alg.find(e1.object)); + + DogLinkedList.Element e2 = alg.pushHead(2); + assertNull(alg.find(4)); + assertSame(e2, alg.find(e2.object)); + assertSame(e1, alg.find(e1.object)); + } + + @Test + public void getHead() { + DogLinkedList alg = new DogLinkedList<>(); + + assertNull(alg.getHead()); + DogLinkedList.Element e = alg.pushHead(1); + assertSame(e, alg.getHead()); + + } + + @Test + public void getTail() { + DogLinkedList alg = new DogLinkedList<>(); + + assertNull(alg.getHead()); + DogLinkedList.Element e = alg.pushHead(1); + assertSame(e, alg.getTail()); + e = alg.pushTail(2); + assertSame(e, alg.getTail()); + } + + @Test + public void addAll_List() { + DogLinkedList alg = new DogLinkedList<>(); + + List list0 = new ArrayList<>(); + List list1 = new ArrayList<>(); + + alg.addAll(list0); + assertEquals(0,alg.size); + checkList(alg); + + list0.add(1); + alg.addAll(list0); + assertEquals(1,alg.size); + checkList(alg); + + alg.addAll(list1); + assertEquals(1,alg.size); + checkList(alg); + + list1.add(2); + list1.add(3); + alg.addAll(list1); + assertEquals(3,alg.size); + checkList(alg); + assertSame(1, alg.getHead().object); + assertSame(3, alg.getTail().object); + } + + @Test + public void addAll_array() { + DogLinkedList alg = new DogLinkedList<>(); + + Integer[] array0 = new Integer[0]; + + alg.addAll(array0,0,0); + assertEquals(0,alg.size); + checkList(alg); + + array0 = new Integer[3]; + for (int i = 0; i < array0.length; i++) { + array0[i] = i; + } + + alg.addAll(array0,1,1); + assertEquals(1,alg.size); + assertSame(1, alg.getHead().object); + checkList(alg); + + alg.addAll(array0,0,3); + assertEquals(4,alg.size); + assertSame(1, alg.getHead().object); + assertSame(0, alg.getElement(1, true).object); + assertSame(1, alg.getElement(2, true).object); + assertSame(2, alg.getElement(3, true).object); + checkList(alg); + } + + @Test + public void requestNew() { + DogLinkedList alg = new DogLinkedList<>(); + + DogLinkedList.Element e0 = alg.requestNew(); + assertNotNull(e0); + + DogLinkedList.Element e1 = alg.requestNew(); + assertNotNull(e1); + assertNotSame(e1, e0); + + alg.available.add(e0); + DogLinkedList.Element e2 = alg.requestNew(); + assertSame(e2, e0); + + DogLinkedList.Element e3 = alg.requestNew(); + assertNotNull(e3); + assertNotSame(e3, e0); + assertNotSame(e3, e1); + assertNotSame(e3, e2); + } + + /** + * Performs checks on the lists preconditions + */ + protected void checkList( DogLinkedList queue ) { + for( var e : queue.available ) { + assertNull(e.prev); + assertNull(e.next); + assertNull(e.object); + } + + if( queue.size == 0 ) { + assertNull(queue.first); + assertNull(queue.last); + } else { + List> forwards = new ArrayList<>(); + List> backwards = new ArrayList<>(); + + DogLinkedList.Element e = queue.first; + while( e != null ) { + forwards.add(e); + e = e.next; + if( forwards.size() > queue.size() ) + fail("too many elements in forward direction"); + } + + e = queue.last; + while( e != null ) { + backwards.add(e); + e = e.prev; + if( backwards.size() > queue.size() ) + fail("too many elements in forward direction"); + } + + assertEquals(forwards.size(),backwards.size()); + assertEquals(forwards.size(),queue.size()); + + for (int i = 0; i < forwards.size(); i++) { + int j = forwards.size()-1-i; + assertSame(forwards.get(i), backwards.get(j)); + } + } + + } +} \ No newline at end of file diff -Nru ddogleg-0.18+ds/test/org/ddogleg/struct/TestFastAccess.java ddogleg-0.22+ds/test/org/ddogleg/struct/TestFastAccess.java --- ddogleg-0.18+ds/test/org/ddogleg/struct/TestFastAccess.java 1970-01-01 00:00:00.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/struct/TestFastAccess.java 2022-09-01 02:18:09.000000000 +0000 @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2012-2022, Peter Abeles. All Rights Reserved. + * + * This file is part of DDogleg (http://ddogleg.org). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ddogleg.struct; + +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * @author Peter Abeles + */ +public class TestFastAccess { + @Test void find() { + var alg = new DogArray<>(DummyData::new); + assertNull(alg.find((a)->a.value==5)); + + alg.grow(); + assertNull(alg.find((a)->a.value==5)); + + alg.grow().value = 5; + assertSame(alg.get(1),alg.find((a)->a.value==5)); + } + + @Test void findIdx() { + var alg = new DogArray<>(DummyData::new); + assertEquals(-1,alg.findIdx((a)->a.value==5)); + + alg.grow(); + assertEquals(-1,alg.findIdx((a)->a.value==5)); + + alg.grow().value = 5; + assertEquals(1,alg.findIdx((a)->a.value==5)); + } + + @Test void findAll() { + List found = new ArrayList<>(); + + var alg = new DogArray<>(DummyData::new); + assertFalse(alg.findAll(found,(a)->a.value==5)); + assertEquals(0, found.size()); + + alg.grow(); + assertFalse(alg.findAll(found,(a)->a.value==5)); + assertEquals(0, found.size()); + + alg.grow().value = 5; + assertTrue(alg.findAll(found,(a)->a.value==5)); + assertEquals(1, found.size()); + + alg.grow().value = 5; + assertTrue(alg.findAll(found,(a)->a.value==5)); + assertEquals(2, found.size()); + } + + @Test void findAllIdx() { + DogArray_I32 found = new DogArray_I32(); + + var alg = new DogArray<>(DummyData::new); + assertFalse(alg.findAllIdx(found,(a)->a.value==5)); + assertEquals(0, found.size()); + + alg.grow(); + assertFalse(alg.findAllIdx(found,(a)->a.value==5)); + assertEquals(0, found.size()); + + alg.grow().value = 5; + assertTrue(alg.findAllIdx(found,(a)->a.value==5)); + assertEquals(1, found.size()); + + alg.grow().value = 5; + assertTrue(alg.findAllIdx(found,(a)->a.value==5)); + assertEquals(2, found.size()); + assertEquals(1, found.get(0)); + assertEquals(2, found.get(1)); + } + + @Test void forIdx() { + DogArray alg = new DogArray<>(DummyData::new); + alg.grow(); + alg.grow(); + alg.grow(); + + alg.forIdx((i, o)->o.value=i); + + assertEquals(0,alg.get(0).value); + assertEquals(1,alg.get(1).value); + assertEquals(2,alg.get(2).value); + } + + @Test void forIdx_range() { + DogArray alg = new DogArray<>(DummyData::new); + for (int i = 0; i < 10; i++) { + alg.grow(); + } + + alg.forIdx(2,5,(i, o)->o.value=i); + + for (int i = 0; i < 10; i++) { + if( i >= 2 && i < 5 ) { + assertEquals(i,alg.get(i).value); + } else { + assertEquals(0,alg.get(i).value); + } + } + } + + @Test void forEach() { + DogArray alg = new DogArray<>(DummyData::new); + alg.grow(); + alg.grow(); + alg.grow(); + + alg.forEach((o)->o.value=2); + + assertEquals(2,alg.get(0).value); + assertEquals(2,alg.get(1).value); + assertEquals(2,alg.get(2).value); + } + + @Test void forEach_range() { + DogArray alg = new DogArray<>(DummyData::new); + for (int i = 0; i < 10; i++) { + alg.grow(); + } + + alg.forEach(2,5,(o)->o.value=3); + + for (int i = 0; i < 10; i++) { + if( i >= 2 && i < 5 ) { + assertEquals(3,alg.get(i).value); + } else { + assertEquals(0,alg.get(i).value); + } + } + } + + @Test void count() { + DogArray alg = new DogArray<>(DummyData::new); + alg.grow(); + alg.grow(); + alg.grow(); + + alg.forEach((o)->o.value=2); + + assertEquals(0, alg.count((o)->o.value==0)); + assertEquals(3, alg.count((o)->o.value==2)); + } +} diff -Nru ddogleg-0.18+ds/test/org/ddogleg/struct/TestFastArray.java ddogleg-0.22+ds/test/org/ddogleg/struct/TestFastArray.java --- ddogleg-0.18+ds/test/org/ddogleg/struct/TestFastArray.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/struct/TestFastArray.java 2022-09-01 02:18:09.000000000 +0000 @@ -115,6 +115,63 @@ } @Test + void reverse() { + DogArray alg = new DogArray<>(2,DummyData::new); + + // 0 items + alg.reverse(); + assertEquals(0, alg.size()); + + // 1 item + alg.grow().value = 1; + alg.reverse(); + + assertEquals(1, alg.get(0).value); + + // 2 items + alg.grow().value = 2; + alg.reverse(); + + assertEquals(2, alg.get(0).value); + assertEquals(1, alg.get(1).value); + + // 3 items (odd) + alg.reset(); + + alg.grow().value = 1; + alg.grow().value = 2; + alg.grow().value = 3; + + alg.reverse(); + + assertEquals(3, alg.get(0).value); + assertEquals(2,alg.get(1).value); + assertEquals(1,alg.get(2).value); + + // 4 items (even) + alg.reset(); + + alg.grow().value = 1; + alg.grow().value = 2; + alg.grow().value = 3; + alg.grow().value = 4; + + alg.reverse(); + + assertEquals(4,alg.get(0).value); + assertEquals(3,alg.get(1).value); + assertEquals(2,alg.get(2).value); + assertEquals(1,alg.get(3).value); + + // double reverse = original + alg.reverse(); + assertEquals(1,alg.get(0).value); + assertEquals(2,alg.get(1).value); + assertEquals(3,alg.get(2).value); + assertEquals(4,alg.get(3).value); + } + + @Test void add() { FastArray alg = new FastArray<>(DummyData.class); @@ -125,7 +182,7 @@ @Test void addAll() { - FastQueue alg = new FastQueue<>(DummyData::new); + DogArray alg = new DogArray<>(DummyData::new); alg.grow(); alg.grow(); @@ -136,4 +193,23 @@ assertSame(alg.get(0), alg2.get(0)); assertSame(alg.get(1), alg2.get(1)); } + + @Test + void resize_fill() { + DummyData data0 = new DummyData(); + + FastArray alg = new FastArray<>(DummyData.class); + for (int i = 0; i < 3; i++) { + alg.add(data0); + } + + DummyData data1 = new DummyData(); + alg.resize(6,data1); + assertEquals(6,alg.size); + assertTrue(alg.data.length>=6); + + for (int i = 0; i < alg.size; i++) { + assertSame(data1,alg.get(i)); + } + } } \ No newline at end of file diff -Nru ddogleg-0.18+ds/test/org/ddogleg/struct/TestFastQueue.java ddogleg-0.22+ds/test/org/ddogleg/struct/TestFastQueue.java --- ddogleg-0.18+ds/test/org/ddogleg/struct/TestFastQueue.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/struct/TestFastQueue.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,404 +0,0 @@ -/* - * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. - * - * This file is part of DDogleg (http://ddogleg.org). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ddogleg.struct; - -import org.ejml.UtilEjml; -import org.junit.jupiter.api.Test; - -import java.util.ArrayList; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.*; - - -/** - * @author Peter Abeles - */ -class TestFastQueue { - - /** - * makes sure reset function is called when the grow command is used - */ - @Test - void reset_grow() { - FastQueue alg = new FastQueue<>(DummyData::new,(d)->d.value=2); - - for (int i = 0; i < 5; i++) { - DummyData d = alg.grow(); - assertEquals(2,d.value); - } - assertEquals(5,alg.size); - } - - /** - * makes sure reset function is called when the grow command is used - */ - @Test - void reset_resize() { - FastQueue alg = new FastQueue<>(DummyData::new,(d)->d.value=2); - - alg.resize(3); - for (int i = 0; i < alg.size; i++) { - alg.get(i).value = 100; - } - - // resize again and make sure it doesn't reset elements already in the list - alg.resize(10); - for (int i = 0; i < 3; i++) { - assertEquals(100, alg.get(i).value); - } - for (int i = 3; i < 10; i++) { - assertEquals(2, alg.get(i).value); - } - } - - @Test - void checkDeclareInstance() { - FastQueue alg = new FastQueue<>(DummyData::new); - - assertTrue(alg.getMaxSize()>0); - assertNotNull(alg.data[0]); - } - - @Test - void toList() { - FastQueue alg = new FastQueue<>(DummyData::new); - - List l = alg.toList(); - assertEquals(0,l.size()); - - alg.grow().value = 1; - alg.grow().value = 1; - alg.grow().value = 2; - alg.removeTail(); - - l = alg.toList(); - assertEquals(2,l.size()); - assertEquals(1,l.get(0).value); - assertEquals(1,l.get(1).value); - } - - @Test - void remove_indexes() { - FastQueue alg = new FastQueue<>(10,DummyData::new); - for (int i = 0; i < 10; i++) { - alg.grow().value = i; - } - - int[] indexes = new int[]{0,1,4,2,6,8,9}; - alg.remove(indexes,2,6,null); - assertEquals(6,alg.size()); - assertEquals(0,alg.get(0).value); - assertEquals(1,alg.get(1).value); - assertEquals(3,alg.get(2).value); - assertEquals(5,alg.get(3).value); - assertEquals(7,alg.get(4).value); - assertEquals(9,alg.get(5).value); - - // make sure original objects were recycled properly - for (int i = 0; i < 10; i++) { - for (int j = i+1; j < 10; j++) { - assertNotEquals(alg.data[i].value,alg.data[j].value); - } - } - } - - @Test - void remove_indexes_RemoveNothing() { - FastQueue alg = new FastQueue<>(10,DummyData::new); - for (int i = 0; i < 10; i++) { - alg.grow().value = i; - } - int[] indexes = new int[]{}; - alg.remove(indexes,2,2,null); - assertEquals(10,alg.size()); - } - - @Test - void removeTail() { - FastQueue alg = new FastQueue<>(10,DummyData::new); - - alg.grow(); - assertEquals(1,alg.size); - alg.removeTail(); - assertEquals(0,alg.size); - } - - @Test - void copyAll() { - List data = new ArrayList<>(); - for (int i = 0; i < 10; i++) { - DummyData d = new DummyData(); - d.value = i; - data.add(d); - } - FastQueue alg = new FastQueue<>(DummyData::new); - alg.copyAll(data,(src, dst)-> dst.value=src.value); - - for (int i = 0; i < 10; i++) { - assertNotEquals(data.get(i), alg.get(i)); - assertEquals(i, alg.get(i).value); - } - } - - @Test - void remove() { - FastQueue alg = new FastQueue<>(10,DummyData::new); - - List l = alg.toList(); - assertEquals(0,l.size()); - - alg.grow().value = 1; - alg.grow().value = 2; - alg.grow().value = 3; - - alg.remove(1); - - assertEquals(2,alg.size()); - assertEquals(1,alg.get(0).value); - assertEquals(3,alg.get(1).value); - // make sure the data was shifted to the end - assertEquals(2,alg.data[2].value); - - alg.remove(1); - assertEquals(1,alg.size()); - assertEquals(1,alg.get(0).value); - assertEquals(3,alg.data[1].value); - assertEquals(2,alg.data[2].value); - - alg.remove(0); - assertEquals(0,alg.size()); - assertEquals(1,alg.data[0].value); - assertEquals(3,alg.data[1].value); - assertEquals(2,alg.data[2].value); - } - - @Test - void removeSwap() { - FastQueue alg = new FastQueue<>(10,DummyData::new); - - List l = alg.toList(); - assertEquals(0,l.size()); - - alg.grow().value = 1; - DummyData d = alg.get(0); - assertSame(d,alg.removeSwap(0)); - assertEquals(0,alg.size()); - - alg.grow().value = 1; - alg.grow().value = 2; - alg.grow().value = 3; - alg.grow().value = 4; - - alg.removeSwap(1); - - assertEquals(3,alg.size()); - assertEquals(1,alg.get(0).value); - assertEquals(4,alg.get(1).value); - assertEquals(3,alg.get(2).value); - // Make sure the removed element is at the tail - assertEquals(2,alg.data[3].value); - } - - @Test - void getTail() { - FastQueue alg = new FastQueue<>(10,DummyData::new); - - alg.grow();alg.grow(); - - assertSame(alg.data[1], alg.getTail()); - } - - @Test - void getTail_index() { - FastQueue alg = new FastQueue<>(10,DummyData::new); - - alg.grow();alg.grow(); - - for (int i = 0; i < alg.size(); i++) { - assertSame(alg.data[i], alg.getTail(alg.size - i - 1)); - } - } - - @Test - void get_pop() { - FastQueue alg = new FastQueue<>(DummyData::new); - - // test a failure case - try { - alg.get(0); - fail("Didn't fail"); - } catch( IllegalArgumentException ignore ) {} - - alg.grow(); - alg.get(0); - } - - @Test - void size() { - FastQueue alg = new FastQueue<>(DummyData::new); - assertEquals(0,alg.size); - alg.grow(); - assertEquals(1,alg.size); - } - - @Test - void reverse() { - FastQueue alg = new FastQueue<>(2,DummyData::new); - - // 0 items - alg.reverse(); - assertEquals(0, alg.size()); - - // 1 item - alg.grow().value = 1; - alg.reverse(); - - assertEquals(1, alg.get(0).value); - - // 2 items - alg.grow().value = 2; - alg.reverse(); - - assertEquals(2, alg.get(0).value); - assertEquals(1, alg.get(1).value); - - // 3 items (odd) - alg.reset(); - - alg.grow().value = 1; - alg.grow().value = 2; - alg.grow().value = 3; - - alg.reverse(); - - assertEquals(3, alg.get(0).value); - assertEquals(2,alg.get(1).value); - assertEquals(1,alg.get(2).value); - - // 4 items (even) - alg.reset(); - - alg.grow().value = 1; - alg.grow().value = 2; - alg.grow().value = 3; - alg.grow().value = 4; - - alg.reverse(); - - assertEquals(4,alg.get(0).value); - assertEquals(3,alg.get(1).value); - assertEquals(2,alg.get(2).value); - assertEquals(1,alg.get(3).value); - - // double reverse = original - alg.reverse(); - assertEquals(1,alg.get(0).value); - assertEquals(2,alg.get(1).value); - assertEquals(3,alg.get(2).value); - assertEquals(4,alg.get(3).value); - } - - /** - * Checks to see if pop automatically grows correctly - */ - @Test - void pop_grow() { - FastQueue alg = new FastQueue<>(1,DummyData::new); - - int before = alg.getMaxSize(); - for( int i = 0; i < 20; i++ ) { - alg.grow(); - } - alg.get(19); - int after = alg.getMaxSize(); - assertTrue(after>before); - } - - @Test - void growArray() { - FastQueue alg = new FastQueue<>(1,DummyData::new); - - alg.grow().value = 10; - int before = alg.getMaxSize(); - alg.growArray(before+5); - assertEquals(10,alg.get(0).value); - } - - @Test - void contains() { - FastQueue queue = new FastQueue<>(DummyData::new); - queue.grow(); - - assertFalse(queue.contains(new DummyData())); - - assertTrue(queue.contains(queue.get(0))); - } - - @Test - void indexOf() { - FastQueue queue = new FastQueue<>(100,DummyData::new); - queue.grow().value = 1; - queue.grow().value = 3; - queue.grow().value = 2; - - assertEquals(-1,queue.indexOf(new DummyData()), UtilEjml.TEST_F64); - assertEquals(0,queue.indexOf(queue.get(0)), UtilEjml.TEST_F64); - assertEquals(1,queue.indexOf(queue.get(1)), UtilEjml.TEST_F64); - assertEquals(2,queue.indexOf(queue.get(2)), UtilEjml.TEST_F64); - } - - @Test - void flip() { - FastQueue queue = new FastQueue<>(DummyData::new); - queue.flip(); - - queue.grow().value = 1; - queue.flip(); - assertEquals(1,queue.get(0).value); - - queue.grow().value = 2; - queue.flip(); - assertEquals(2,queue.get(0).value); - assertEquals(1,queue.get(1).value); - - queue.grow().value = 3; - queue.flip(); - assertEquals(3,queue.get(0).value); - assertEquals(1,queue.get(1).value); - assertEquals(2,queue.get(2).value); - } - - @Test - void swap() { - FastQueue queue = new FastQueue<>(100,DummyData::new); - queue.grow().value = 1; - queue.grow().value = 2; - queue.grow().value = 3; - queue.grow().value = 4; - - queue.swap(0,3); - queue.swap(0,1); - - assertEquals(2,queue.get(0).value); - assertEquals(4,queue.get(1).value); - assertEquals(3,queue.get(2).value); - assertEquals(1,queue.get(3).value); - } -} diff -Nru ddogleg-0.18+ds/test/org/ddogleg/struct/TestFastQueueList.java ddogleg-0.22+ds/test/org/ddogleg/struct/TestFastQueueList.java --- ddogleg-0.18+ds/test/org/ddogleg/struct/TestFastQueueList.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/struct/TestFastQueueList.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,142 +0,0 @@ -/* - * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. - * - * This file is part of DDogleg (http://ddogleg.org). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ddogleg.struct; - -import org.junit.jupiter.api.Test; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.*; - -/** - * @author Peter Abeles - */ -public class TestFastQueueList { - - @Test - public void size() { - FastQueue queue = new FastQueue<>(100,()->new Dummy(0)); - - List list = queue.toList(); - - assertEquals(0, list.size()); - - queue.grow().value = 1.0; - - assertEquals(1, list.size()); - } - - @Test - public void isEmpty() { - FastQueue queue = new FastQueue<>(100,()->new Dummy(0)); - - List list = queue.toList(); - - assertTrue( list.isEmpty() ); - - queue.grow().value = 1.0; - - assertFalse(list.isEmpty()); - } - - @Test - public void contains() { - FastQueue queue = new FastQueue<>(100,()->new Dummy(0)); - List list = queue.toList(); - - assertFalse(list.contains(new Dummy(1.0))); - - Dummy d = queue.grow(); - - assertTrue(list.contains(d)); - } - - @Test - public void iterator() { - FastQueue queue = new FastQueue<>(100,()->new Dummy(0)); - queue.grow().value = 1.0; - queue.grow().value = 2.0; - queue.grow().value = 3.0; - - Iterator iterator = queue.toList().iterator(); - assertTrue(iterator.hasNext()); - assertEquals(1.0, iterator.next().value); - assertTrue(iterator.hasNext()); - assertEquals(2.0, iterator.next().value); - assertTrue(iterator.hasNext()); - assertEquals(3.0, iterator.next().value); - assertFalse(iterator.hasNext()); - - } - - @Test - public void toArray() { - FastQueue queue = new FastQueue<>(100,()->new Dummy(0)); - queue.grow().value = 1.0; - queue.grow().value = 2.0; - queue.grow().value = 3.0; - - Object[] array = queue.toList().toArray(); - assertEquals(3, array.length); - assertEquals(1.0, ((Dummy) array[0]).value); - assertEquals(2.0, ((Dummy) array[1]).value); - assertEquals(3.0, ((Dummy) array[2]).value); - - // remove an element from the queue to make sure it isn't using array length - queue.removeTail(); - array = queue.toList().toArray(); - assertEquals(2, array.length); - } - - @Test - public void containsAll() { - FastQueue queue = new FastQueue<>(100,()->new Dummy(0)); - queue.grow().value = 1.0; - queue.grow().value = 2.0; - queue.grow().value = 3.0; - - List list = new ArrayList<>(); - list.add(queue.get(0)); - list.add(queue.get(1)); - - assertTrue(queue.toList().containsAll(list)); - - list.add(new Dummy(5.0)); - assertFalse(queue.toList().containsAll(list)); - } - - @Test - public void get() { - FastQueue queue = new FastQueue<>(100,()->new Dummy(0)); - queue.grow().value = 1.0; - queue.grow().value = 2.0; - - assertEquals(2.0, queue.toList().get(1).value); - } - - public static class Dummy { - double value; - - public Dummy(double value) { - this.value = value; - } - } -} diff -Nru ddogleg-0.18+ds/test/org/ddogleg/struct/TestGrowQueue_B.java ddogleg-0.22+ds/test/org/ddogleg/struct/TestGrowQueue_B.java --- ddogleg-0.18+ds/test/org/ddogleg/struct/TestGrowQueue_B.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/struct/TestGrowQueue_B.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,121 +0,0 @@ -/* - * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. - * - * This file is part of DDogleg (http://ddogleg.org). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ddogleg.struct; - -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.*; - -/** - * @author Peter Abeles - */ -public class TestGrowQueue_B { - - @Test - void auto_grow() { - GrowQueue_B alg = new GrowQueue_B(3); - - assertEquals(3,alg.data.length); - - for( int i = 0; i < 10; i++ ) - alg.push((i%2)==0); - - assertEquals(10,alg.size); - - for( int i = 0; i < 10; i++ ) - assertEquals((i%2)==0,alg.get(i)); - } - - @Test - void reset() { - GrowQueue_B alg = new GrowQueue_B(10); - - alg.push(true); - alg.push(false); - alg.push(false); - - assertTrue(alg.get(0)); - assertEquals(3,alg.size); - - alg.reset(); - - assertEquals(0, alg.size); - } - - @Test - void push_pop() { - GrowQueue_B alg = new GrowQueue_B(10); - - alg.push(false); - alg.push(true); - - assertEquals(2,alg.size); - assertTrue(alg.pop()); - assertFalse(alg.pop()); - assertEquals(0, alg.size); - } - - @Test - void remove_two() { - GrowQueue_B alg = new GrowQueue_B(10); - - alg.push(true); - alg.push(true); - alg.push(false); - alg.push(true); - alg.push(false); - - alg.remove(1,1); - assertEquals(4,alg.size); - assertTrue(alg.get(0)); - assertFalse(alg.get(1)); - assertTrue(alg.get(2)); - assertFalse(alg.get(3)); - alg.remove(0,1); - assertEquals(2,alg.size); - assertTrue(alg.get(0)); - assertFalse(alg.get(1)); - } - - @Test - void indexOf() { - GrowQueue_B alg = new GrowQueue_B(10); - - alg.push(true); - alg.push(false); - alg.push(false); - alg.push(true); - - assertEquals(0,alg.indexOf(true)); - assertEquals(1,alg.indexOf(false)); - } - - @Test - void getTail() { - GrowQueue_B alg = new GrowQueue_B(20); - - for (int i = 0; i < 20; i++) { - alg.add(i%2==0); - } - - for (int i = 0; i < 20; i++) { - assertEquals((20-i-1)%2==0,alg.getTail(i)); - } - } -} diff -Nru ddogleg-0.18+ds/test/org/ddogleg/struct/TestGrowQueue_F32.java ddogleg-0.22+ds/test/org/ddogleg/struct/TestGrowQueue_F32.java --- ddogleg-0.18+ds/test/org/ddogleg/struct/TestGrowQueue_F32.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/struct/TestGrowQueue_F32.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,286 +0,0 @@ -/* - * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. - * - * This file is part of DDogleg (http://ddogleg.org). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ddogleg.struct; - -import org.ejml.UtilEjml; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - - -/** - * @author Peter Abeles - */ -public class TestGrowQueue_F32 extends ChecksGrowQueue { - - @Test - void addAll_queue() { - GrowQueue_F32 queue0 = new GrowQueue_F32(2); - GrowQueue_F32 queue1 = new GrowQueue_F32(3); - - queue0.add(1); - queue0.add(2); - - queue1.add(3); - queue1.add(4); - queue1.add(5); - - assertEquals(2,queue0.size); - queue0.addAll(queue1); - assertEquals(5,queue0.size); - for( int i = 0; i < queue0.size; i++ ) { - assertEquals(queue0.get(i),i+1,1e-5); - } - - queue0.reset(); - queue0.addAll(queue1); - assertEquals(3,queue0.size); - for( int i = 0; i < queue0.size; i++ ) { - assertEquals(queue0.get(i),i+3,1e-5); - } - } - - @Test - void addAll_array() { - GrowQueue_F32 queue0 = new GrowQueue_F32(2); - float[] array = new float[]{3,4,5}; - - queue0.add(1); - queue0.add(2); - - assertEquals(2,queue0.size); - queue0.addAll(array,0,3); - assertEquals(5,queue0.size); - for( int i = 0; i < queue0.size; i++ ) { - assertEquals(queue0.get(i),i+1,1e-4f); - } - - queue0.reset(); - queue0.addAll(array,1,3); - assertEquals(2,queue0.size); - for( int i = 0; i < queue0.size; i++ ) { - assertEquals(queue0.get(i),i+4,1e-4f); - } - } - - @Test - void auto_grow() { - GrowQueue_F32 alg = new GrowQueue_F32(3); - - assertEquals(3,alg.data.length); - - for( int i = 0; i < 10; i++ ) - alg.push(i); - - assertEquals(10,alg.size); - - for( int i = 0; i < 10; i++ ) - assertEquals(i,alg.get(i),1e-4f); - } - - @Test - void reset() { - GrowQueue_F32 alg = new GrowQueue_F32(10); - - alg.push(1); - alg.push(3); - alg.push(-2); - - assertTrue(1.0f == alg.get(0)); - assertEquals(3,alg.size); - - alg.reset(); - - assertEquals(0, alg.size); - } - - @Test - void push_pop() { - GrowQueue_F32 alg = new GrowQueue_F32(10); - - alg.push(1); - alg.push(3); - - assertEquals(2,alg.size); - assertTrue(3==alg.pop()); - assertTrue(1==alg.pop()); - assertEquals(0,alg.size); - } - - - @Test - void setTo_array() { - GrowQueue_F32 alg = new GrowQueue_F32(10); - - float[] foo = new float[]{1,3,4,5,7}; - alg.setTo(foo,1,3); - assertEquals(3,alg.size); - for (int i = 0; i < 3; i++) { - assertEquals(alg.get(i),foo[i+1], UtilEjml.TEST_F64); - } - } - - @Test - void remove_two() { - GrowQueue_F32 alg = new GrowQueue_F32(10); - - alg.push(1); - alg.push(3); - alg.push(4); - alg.push(5); - alg.push(6); - - alg.remove(1,1); - assertEquals(4,alg.size); - assertEquals(1,alg.get(0), UtilEjml.TEST_F32); - assertEquals(4,alg.get(1), UtilEjml.TEST_F32); - assertEquals(5,alg.get(2), UtilEjml.TEST_F32); - assertEquals(6,alg.get(3), UtilEjml.TEST_F32); - alg.remove(0,1); - assertEquals(2,alg.size); - assertEquals(5,alg.get(0), UtilEjml.TEST_F32); - assertEquals(6,alg.get(1), UtilEjml.TEST_F32); - } - - @Test - void remove() { - - GrowQueue_F32 alg = new GrowQueue_F32(10); - - alg.push(1); - alg.push(3); - alg.push(4); - alg.push(5); - - alg.remove(1); - assertEquals(3,alg.size); - assertEquals(1,alg.get(0),1e-4f); - assertEquals(4,alg.get(1),1e-4f); - assertEquals(5,alg.get(2),1e-4f); - } - - @Override - public GrowQueue_F32 declare(int maxsize) { - return new GrowQueue_F32(maxsize); - } - - @Override - public void push(GrowQueue_F32 queue, double value) { - queue.push((float)value); - } - - @Override - public void insert(GrowQueue_F32 queue, int index, double value) { - queue.insert(index,(float)value); - } - - @Override - public void check(GrowQueue_F32 queue, int index, double value) { - assertEquals((float)value,queue.get(index),1e-4f); - } - - @Test - void indexOf() { - GrowQueue_F32 alg = new GrowQueue_F32(10); - - alg.push(1); - alg.push(3); - alg.push(4); - alg.push(5); - - assertEquals(1,alg.indexOf(3)); - assertEquals(-1,alg.indexOf(8)); - } - - @Test - void sort() { - GrowQueue_F32 alg = new GrowQueue_F32(6); - - alg.push(8); - alg.push(2); - alg.push(4); - alg.push(3); - - alg.sort(); - - assertEquals(4,alg.size); - assertEquals(2,alg.get(0),1e-4f); - assertEquals(3,alg.get(1),1e-4f); - assertEquals(4,alg.get(2),1e-4f); - assertEquals(8,alg.get(3),1e-4f); - } - - @Test - void getFraction() { - GrowQueue_F32 alg = new GrowQueue_F32(20); - - for (int i = 0; i < 20; i++) { - alg.add(i); - } - - assertEquals(0,alg.getFraction(0.0), UtilEjml.TEST_F32); - assertEquals(0,alg.getFraction(0.02), UtilEjml.TEST_F32); - assertEquals(0,alg.getFraction(0.03), UtilEjml.TEST_F32); - assertEquals(1,alg.getFraction(1.0/19.0), UtilEjml.TEST_F32); - assertEquals(1,alg.getFraction(1.7/19.0), UtilEjml.TEST_F32); - assertEquals(19/2,alg.getFraction(0.5), UtilEjml.TEST_F32); - assertEquals(19,alg.getFraction(1.0), UtilEjml.TEST_F32); - } - - @Test - void indexOfGreatest() { - GrowQueue_F32 alg = new GrowQueue_F32(20); - - assertEquals(-1,alg.indexOfGreatest()); - - alg.add(-3); - alg.add(-2); - alg.add(-1); - - assertEquals(2, alg.indexOfGreatest()); - } - - @Test - void indexOfLeast() { - GrowQueue_F32 alg = new GrowQueue_F32(20); - - assertEquals(-1,alg.indexOfLeast()); - - alg.add(-3); - alg.add(-2); - alg.add(-4); - - assertEquals(2, alg.indexOfLeast()); - } - - @Test - void getTail() { - GrowQueue_F32 alg = new GrowQueue_F32(20); - - for (int i = 0; i < 20; i++) { - alg.add(i); - } - - for (int i = 0; i < 20; i++) { - assertEquals(20-i-1,alg.getTail(i)); - } - } -} - diff -Nru ddogleg-0.18+ds/test/org/ddogleg/struct/TestGrowQueue_F64.java ddogleg-0.22+ds/test/org/ddogleg/struct/TestGrowQueue_F64.java --- ddogleg-0.18+ds/test/org/ddogleg/struct/TestGrowQueue_F64.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/struct/TestGrowQueue_F64.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,284 +0,0 @@ -/* - * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. - * - * This file is part of DDogleg (http://ddogleg.org). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ddogleg.struct; - -import org.ejml.UtilEjml; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - - -/** - * @author Peter Abeles - */ -public class TestGrowQueue_F64 extends ChecksGrowQueue { - - @Test - void addAll_queue() { - GrowQueue_F64 queue0 = new GrowQueue_F64(2); - GrowQueue_F64 queue1 = new GrowQueue_F64(3); - - queue0.add(1); - queue0.add(2); - - queue1.add(3); - queue1.add(4); - queue1.add(5); - - assertEquals(2,queue0.size); - queue0.addAll(queue1); - assertEquals(5,queue0.size); - for( int i = 0; i < queue0.size; i++ ) { - assertEquals(queue0.get(i),i+1,1e-8); - } - - queue0.reset(); - queue0.addAll(queue1); - assertEquals(3,queue0.size); - for( int i = 0; i < queue0.size; i++ ) { - assertEquals(queue0.get(i),i+3,1e-8); - } - } - - @Test - void addAll_array() { - GrowQueue_F64 queue0 = new GrowQueue_F64(2); - double[] array = new double[]{3,4,5}; - - queue0.add(1); - queue0.add(2); - - assertEquals(2,queue0.size); - queue0.addAll(array,0,3); - assertEquals(5,queue0.size); - for( int i = 0; i < queue0.size; i++ ) { - assertEquals(queue0.get(i),i+1,1e-8); - } - - queue0.reset(); - queue0.addAll(array,1,3); - assertEquals(2,queue0.size); - for( int i = 0; i < queue0.size; i++ ) { - assertEquals(queue0.get(i),i+4,1e-8); - } - } - - @Test - void auto_grow() { - GrowQueue_F64 alg = new GrowQueue_F64(3); - - assertEquals(3,alg.data.length); - - for( int i = 0; i < 10; i++ ) - alg.push(i); - - assertEquals(10,alg.size); - - for( int i = 0; i < 10; i++ ) - assertEquals(i,alg.get(i),1e-8); - } - - @Test - void reset() { - GrowQueue_F64 alg = new GrowQueue_F64(10); - - alg.push(1); - alg.push(3); - alg.push(-2); - - assertTrue(1.0 == alg.get(0)); - assertEquals(3,alg.size); - - alg.reset(); - - assertEquals(0,alg.size); - } - - @Test - void push_pop() { - GrowQueue_F64 alg = new GrowQueue_F64(10); - - alg.push(1); - alg.push(3); - - assertEquals(2,alg.size); - assertTrue(3==alg.pop()); - assertTrue(1==alg.pop()); - assertEquals(0, alg.size); - } - - @Test - void setTo_array() { - GrowQueue_F64 alg = new GrowQueue_F64(10); - - double[] foo = new double[]{1,3,4,5,7}; - alg.setTo(foo,1,3); - assertEquals(3,alg.size); - for (int i = 0; i < 3; i++) { - assertEquals(alg.get(i),foo[i+1], UtilEjml.TEST_F64); - } - } - - @Test - void remove() { - - GrowQueue_F64 alg = new GrowQueue_F64(10); - - alg.push(1); - alg.push(3); - alg.push(4); - alg.push(5); - - alg.remove(1); - assertEquals(3,alg.size); - assertEquals(1,alg.get(0),1e-8); - assertEquals(4,alg.get(1),1e-8); - assertEquals(5,alg.get(2),1e-8); - } - - @Test - void remove_two() { - GrowQueue_F64 alg = new GrowQueue_F64(10); - - alg.push(1); - alg.push(3); - alg.push(4); - alg.push(5); - alg.push(6); - - alg.remove(1,1); - assertEquals(4,alg.size); - assertEquals(1,alg.get(0), UtilEjml.TEST_F64); - assertEquals(4,alg.get(1), UtilEjml.TEST_F64); - assertEquals(5,alg.get(2), UtilEjml.TEST_F64); - assertEquals(6,alg.get(3), UtilEjml.TEST_F64); - alg.remove(0,1); - assertEquals(2,alg.size); - assertEquals(5,alg.get(0), UtilEjml.TEST_F64); - assertEquals(6,alg.get(1), UtilEjml.TEST_F64); - } - - @Override - public GrowQueue_F64 declare(int maxsize) { - return new GrowQueue_F64(maxsize); - } - - @Override - public void push(GrowQueue_F64 queue, double value) { - queue.push(value); - } - - @Override - public void insert(GrowQueue_F64 queue, int index, double value) { - queue.insert(index,value); - } - - @Override - public void check(GrowQueue_F64 queue, int index, double value) { - assertEquals(value,queue.get(index),1e-8); - } - - @Test - void indexOf() { - GrowQueue_F64 alg = new GrowQueue_F64(10); - - alg.push(1); - alg.push(3); - alg.push(4); - alg.push(5); - - assertEquals(1,alg.indexOf(3)); - assertEquals(-1,alg.indexOf(8)); - } - - @Test - void sort() { - GrowQueue_F64 alg = new GrowQueue_F64(6); - - alg.push(8); - alg.push(2); - alg.push(4); - alg.push(3); - - alg.sort(); - - assertEquals(4,alg.size); - assertEquals(2,alg.get(0),1e-8); - assertEquals(3,alg.get(1),1e-8); - assertEquals(4,alg.get(2),1e-8); - assertEquals(8,alg.get(3),1e-8); - } - - @Test - void getFraction() { - GrowQueue_F64 alg = new GrowQueue_F64(20); - - for (int i = 0; i < 20; i++) { - alg.add(i); - } - - assertEquals(0,alg.getFraction(0.0), UtilEjml.TEST_F64); - assertEquals(0,alg.getFraction(0.02), UtilEjml.TEST_F64); - assertEquals(0,alg.getFraction(0.03), UtilEjml.TEST_F64); - assertEquals(1,alg.getFraction(1.0/19.0), UtilEjml.TEST_F64); - assertEquals(1,alg.getFraction(1.7/19.0), UtilEjml.TEST_F64); - assertEquals(19/2,alg.getFraction(0.5), UtilEjml.TEST_F64); - assertEquals(19,alg.getFraction(1.0), UtilEjml.TEST_F64); - } - - @Test - void indexOfGreatest() { - GrowQueue_F64 alg = new GrowQueue_F64(20); - - assertEquals(-1,alg.indexOfGreatest()); - - alg.add(-3); - alg.add(-2); - alg.add(-1); - - assertEquals(2, alg.indexOfGreatest()); - } - - @Test - void indexOfLeast() { - GrowQueue_F64 alg = new GrowQueue_F64(20); - - assertEquals(-1,alg.indexOfLeast()); - - alg.add(-3); - alg.add(-2); - alg.add(-4); - - assertEquals(2, alg.indexOfLeast()); - } - - @Test - void getTail() { - GrowQueue_F64 alg = new GrowQueue_F64(20); - - for (int i = 0; i < 20; i++) { - alg.add(i); - } - - for (int i = 0; i < 20; i++) { - assertEquals(20-i-1,alg.getTail(i)); - } - } -} diff -Nru ddogleg-0.18+ds/test/org/ddogleg/struct/TestGrowQueue_I32.java ddogleg-0.22+ds/test/org/ddogleg/struct/TestGrowQueue_I32.java --- ddogleg-0.18+ds/test/org/ddogleg/struct/TestGrowQueue_I32.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/struct/TestGrowQueue_I32.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,275 +0,0 @@ -/* - * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. - * - * This file is part of DDogleg (http://ddogleg.org). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ddogleg.struct; - -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; - - -/** - * @author Peter Abeles - */ -public class TestGrowQueue_I32 extends ChecksGrowQueue { - - @Test - void addAll_queue() { - GrowQueue_I32 queue0 = new GrowQueue_I32(2); - GrowQueue_I32 queue1 = new GrowQueue_I32(3); - - queue0.add(1); - queue0.add(2); - - queue1.add(3); - queue1.add(4); - queue1.add(5); - - assertEquals(2,queue0.size); - queue0.addAll(queue1); - assertEquals(5,queue0.size); - for( int i = 0; i < queue0.size; i++ ) { - assertEquals(queue0.get(i),i+1); - } - - queue0.reset(); - queue0.addAll(queue1); - assertEquals(3,queue0.size); - for( int i = 0; i < queue0.size; i++ ) { - assertEquals(queue0.get(i),i+3); - } - } - - @Test - void addAll_array() { - GrowQueue_I32 queue0 = new GrowQueue_I32(2); - int[] array = new int[]{3,4,5}; - - queue0.add(1); - queue0.add(2); - - assertEquals(2,queue0.size); - queue0.addAll(array,0,3); - assertEquals(5,queue0.size); - for( int i = 0; i < queue0.size; i++ ) { - assertEquals(queue0.get(i),i+1,1e-8); - } - - queue0.reset(); - queue0.addAll(array,1,3); - assertEquals(2,queue0.size); - for( int i = 0; i < queue0.size; i++ ) { - assertEquals(queue0.get(i),i+4,1e-8); - } - } - - @Test - void auto_grow() { - GrowQueue_I32 alg = new GrowQueue_I32(3); - - assertEquals(3,alg.data.length); - - for( int i = 0; i < 10; i++ ) - alg.push(i); - - assertEquals(10,alg.size); - - for( int i = 0; i < 10; i++ ) - assertEquals(i,alg.get(i),1e-8); - } - - @Test - void reset() { - GrowQueue_I32 alg = new GrowQueue_I32(10); - - alg.push(1); - alg.push(3); - alg.push(-2); - - assertEquals(1.0, alg.get(0)); - assertEquals(3,alg.size); - - alg.reset(); - - assertEquals(0,alg.size); - } - - @Test - void push_pop() { - GrowQueue_I32 alg = new GrowQueue_I32(10); - - alg.push(1); - alg.push(3); - - assertEquals(2,alg.size); - assertEquals(3, alg.pop()); - assertEquals(1, alg.pop()); - assertEquals(0, alg.size); - } - - @Test - void setTo_array() { - GrowQueue_I32 alg = new GrowQueue_I32(10); - - int[] foo = new int[]{1,3,4,5,7}; - alg.setTo(foo,1,3); - assertEquals(3,alg.size); - for (int i = 0; i < 3; i++) { - assertEquals(alg.get(i),foo[i+1]); - } - } - - @Test - void remove_one() { - - GrowQueue_I32 alg = new GrowQueue_I32(10); - - alg.push(1); - alg.push(3); - alg.push(4); - alg.push(5); - - alg.remove(1); - assertEquals(3,alg.size); - assertEquals(1,alg.get(0)); - assertEquals(4,alg.get(1)); - assertEquals(5,alg.get(2)); - } - - @Test - void remove_two() { - GrowQueue_I32 alg = new GrowQueue_I32(10); - - alg.push(1); - alg.push(3); - alg.push(4); - alg.push(5); - alg.push(6); - - alg.remove(1,1); - assertEquals(4,alg.size); - assertEquals(1,alg.get(0)); - assertEquals(4,alg.get(1)); - assertEquals(5,alg.get(2)); - assertEquals(6,alg.get(3)); - alg.remove(0,1); - assertEquals(2,alg.size); - assertEquals(5,alg.get(0)); - assertEquals(6,alg.get(1)); - } - - @Override - public GrowQueue_I32 declare(int maxsize) { - return new GrowQueue_I32(maxsize); - } - - @Override - public void push(GrowQueue_I32 queue, double value) { - queue.push((int)value); - } - - @Override - public void insert(GrowQueue_I32 queue, int index, double value) { - queue.insert(index,(int)value); - } - - @Override - public void check(GrowQueue_I32 queue, int index, double value) { - assertEquals((int)value,queue.get(index)); - } - - @Test - void removeHead() { - - GrowQueue_I32 alg = new GrowQueue_I32(10); - - alg.push(1); - alg.push(3); - alg.push(4); - alg.push(5); - - alg.removeHead(0); - assertEquals(4,alg.size); - assertEquals(1,alg.get(0)); - - alg.removeHead(2); - assertEquals(2,alg.size); - assertEquals(4,alg.get(0)); - } - - @Test - void indexOf() { - GrowQueue_I32 alg = new GrowQueue_I32(10); - - alg.push(1); - alg.push(3); - alg.push(4); - alg.push(3); - - assertEquals(1,alg.indexOf(3)); - assertEquals(-1,alg.indexOf(8)); - } - - @Test - void sort() { - GrowQueue_I32 alg = new GrowQueue_I32(6); - - alg.push(8); - alg.push(2); - alg.push(4); - alg.push(3); - - alg.sort(); - - assertEquals(4,alg.size); - assertEquals(2,alg.get(0)); - assertEquals(3,alg.get(1)); - assertEquals(4,alg.get(2)); - assertEquals(8,alg.get(3)); - } - - @Test - void getFraction() { - GrowQueue_I32 alg = new GrowQueue_I32(20); - - for (int i = 0; i < 20; i++) { - alg.add(i); - } - - assertEquals(0,alg.getFraction(0.0)); - assertEquals(0,alg.getFraction(0.02)); - assertEquals(0,alg.getFraction(0.03)); - assertEquals(1,alg.getFraction(1.0/19.0)); - assertEquals(1,alg.getFraction(1.7/19.0)); - assertEquals(19/2,alg.getFraction(0.5)); - assertEquals(19,alg.getFraction(1.0)); - } - - @Test - void getTail() { - GrowQueue_I32 alg = new GrowQueue_I32(20); - - for (int i = 0; i < 20; i++) { - alg.add(i); - } - - for (int i = 0; i < 20; i++) { - assertEquals(20-i-1,alg.getTail(i)); - } - } -} diff -Nru ddogleg-0.18+ds/test/org/ddogleg/struct/TestGrowQueue_I64.java ddogleg-0.22+ds/test/org/ddogleg/struct/TestGrowQueue_I64.java --- ddogleg-0.18+ds/test/org/ddogleg/struct/TestGrowQueue_I64.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/struct/TestGrowQueue_I64.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,257 +0,0 @@ -/* - * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. - * - * This file is part of DDogleg (http://ddogleg.org). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ddogleg.struct; - -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - - -/** - * @author Peter Abeles - */ -public class TestGrowQueue_I64 extends ChecksGrowQueue { - - @Test - void addAll_queue() { - GrowQueue_I64 queue0 = new GrowQueue_I64(2); - GrowQueue_I64 queue1 = new GrowQueue_I64(3); - - queue0.add(1); - queue0.add(2); - - queue1.add(3); - queue1.add(4); - queue1.add(5); - - assertEquals(2,queue0.size); - queue0.addAll(queue1); - assertEquals(5,queue0.size); - for( int i = 0; i < queue0.size; i++ ) { - assertEquals(queue0.get(i),i+1); - } - - queue0.reset(); - queue0.addAll(queue1); - assertEquals(3,queue0.size); - for( int i = 0; i < queue0.size; i++ ) { - assertEquals(queue0.get(i),i+3); - } - } - - @Test - void addAll_array() { - GrowQueue_I64 queue0 = new GrowQueue_I64(2); - long[] array = new long[]{3,4,5}; - - queue0.add(1); - queue0.add(2); - - assertEquals(2,queue0.size); - queue0.addAll(array,0,3); - assertEquals(5,queue0.size); - for( int i = 0; i < queue0.size; i++ ) { - assertEquals(queue0.get(i),i+1,1e-8); - } - - queue0.reset(); - queue0.addAll(array,1,3); - assertEquals(2,queue0.size); - for( int i = 0; i < queue0.size; i++ ) { - assertEquals(queue0.get(i),i+4,1e-8); - } - } - - @Test - void auto_grow() { - GrowQueue_I64 alg = new GrowQueue_I64(3); - - assertEquals(3,alg.data.length); - - for( int i = 0; i < 10; i++ ) - alg.push(i); - - assertEquals(10,alg.size); - - for( int i = 0; i < 10; i++ ) - assertEquals(i,alg.get(i),1e-8); - } - - @Test - void reset() { - GrowQueue_I64 alg = new GrowQueue_I64(10); - - alg.push(1); - alg.push(3); - alg.push(-2); - - assertTrue(1.0 == alg.get(0)); - assertEquals(3,alg.size); - - alg.reset(); - - assertEquals(0,alg.size); - } - - @Test - void push_pop() { - GrowQueue_I64 alg = new GrowQueue_I64(10); - - alg.push(1); - alg.push(3); - - assertEquals(2,alg.size); - assertTrue(3==alg.pop()); - assertTrue(1==alg.pop()); - assertEquals(0, alg.size); - } - - @Test - void setTo_array() { - GrowQueue_I64 alg = new GrowQueue_I64(10); - - long[] foo = new long[]{1,3,4,5,7}; - alg.setTo(foo,1,3); - assertEquals(3,alg.size); - for (int i = 0; i < 3; i++) { - assertEquals(alg.get(i),foo[i+1]); - } - } - - @Test - void remove_one() { - - GrowQueue_I64 alg = new GrowQueue_I64(10); - - alg.push(1); - alg.push(3); - alg.push(4); - alg.push(5); - - alg.remove(1); - assertEquals(3,alg.size); - assertEquals(1,alg.get(0)); - assertEquals(4,alg.get(1)); - assertEquals(5,alg.get(2)); - } - - @Test - void remove_two() { - GrowQueue_I64 alg = new GrowQueue_I64(10); - - alg.push(1); - alg.push(3); - alg.push(4); - alg.push(5); - alg.push(6); - - alg.remove(1,1); - assertEquals(4,alg.size); - assertEquals(1,alg.get(0)); - assertEquals(4,alg.get(1)); - assertEquals(5,alg.get(2)); - assertEquals(6,alg.get(3)); - alg.remove(0,1); - assertEquals(2,alg.size); - assertEquals(5,alg.get(0)); - assertEquals(6,alg.get(1)); - } - - @Override - public GrowQueue_I64 declare(int maxsize) { - return new GrowQueue_I64(maxsize); - } - - @Override - public void push(GrowQueue_I64 queue, double value) { - queue.push((long)value); - } - - @Override - public void insert(GrowQueue_I64 queue, int index, double value) { - queue.insert(index,(long)value); - } - - @Override - public void check(GrowQueue_I64 queue, int index, double value) { - assertEquals((long)value,queue.get(index)); - } - - @Test - void indexOf() { - GrowQueue_I64 alg = new GrowQueue_I64(10); - - alg.push(1); - alg.push(3); - alg.push(4); - alg.push(5); - - assertEquals(1,alg.indexOf(3)); - assertEquals(-1,alg.indexOf(8)); - } - - @Test - void sort() { - GrowQueue_I64 alg = new GrowQueue_I64(6); - - alg.push(8); - alg.push(2); - alg.push(4); - alg.push(3); - - alg.sort(); - - assertEquals(4,alg.size); - assertEquals(2,alg.get(0)); - assertEquals(3,alg.get(1)); - assertEquals(4,alg.get(2)); - assertEquals(8,alg.get(3)); - } - - @Test - void getFraction() { - GrowQueue_I64 alg = new GrowQueue_I64(20); - - for (int i = 0; i < 20; i++) { - alg.add(i); - } - - assertEquals(0,alg.getFraction(0.0)); - assertEquals(0,alg.getFraction(0.02)); - assertEquals(0,alg.getFraction(0.03)); - assertEquals(1,alg.getFraction(1.0/19.0)); - assertEquals(1,alg.getFraction(1.7/19.0)); - assertEquals(19/2,alg.getFraction(0.5)); - assertEquals(19,alg.getFraction(1.0)); - } - - @Test - void getTail() { - GrowQueue_I64 alg = new GrowQueue_I64(20); - - for (int i = 0; i < 20; i++) { - alg.add(i); - } - - for (int i = 0; i < 20; i++) { - assertEquals(20-i-1,alg.getTail(i)); - } - } -} diff -Nru ddogleg-0.18+ds/test/org/ddogleg/struct/TestGrowQueue_I8.java ddogleg-0.22+ds/test/org/ddogleg/struct/TestGrowQueue_I8.java --- ddogleg-0.18+ds/test/org/ddogleg/struct/TestGrowQueue_I8.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/struct/TestGrowQueue_I8.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,242 +0,0 @@ -/* - * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. - * - * This file is part of DDogleg (http://ddogleg.org). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ddogleg.struct; - -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - - -/** - * @author Peter Abeles - */ -public class TestGrowQueue_I8 extends ChecksGrowQueue { - - @Test - void addAll_queue() { - GrowQueue_I8 queue0 = new GrowQueue_I8(2); - GrowQueue_I8 queue1 = new GrowQueue_I8(3); - - queue0.add(1); - queue0.add(2); - - queue1.add(3); - queue1.add(4); - queue1.add(5); - - assertEquals(2,queue0.size); - queue0.addAll(queue1); - assertEquals(5,queue0.size); - for( int i = 0; i < queue0.size; i++ ) { - assertEquals(queue0.get(i),i+1); - } - - queue0.reset(); - queue0.addAll(queue1); - assertEquals(3,queue0.size); - for( int i = 0; i < queue0.size; i++ ) { - assertEquals(queue0.get(i),i+3); - } - } - - @Test - void addAll_array() { - GrowQueue_I8 queue0 = new GrowQueue_I8(2); - byte[] array = new byte[]{3,4,5}; - - queue0.add(1); - queue0.add(2); - - assertEquals(2,queue0.size); - queue0.addAll(array,0,3); - assertEquals(5,queue0.size); - for( int i = 0; i < queue0.size; i++ ) { - assertEquals(queue0.get(i),i+1,1e-8); - } - - queue0.reset(); - queue0.addAll(array,1,3); - assertEquals(2,queue0.size); - for( int i = 0; i < queue0.size; i++ ) { - assertEquals(queue0.get(i),i+4,1e-8); - } - } - - @Test - void auto_grow() { - GrowQueue_I8 alg = new GrowQueue_I8(3); - - assertEquals(3,alg.data.length); - - for( int i = 0; i < 10; i++ ) - alg.push(i); - - assertEquals(10,alg.size); - - for( int i = 0; i < 10; i++ ) - assertEquals(i,alg.get(i),1e-8); - } - - @Test - void reset() { - GrowQueue_I8 alg = new GrowQueue_I8(10); - - alg.push(1); - alg.push(3); - alg.push(-2); - - assertTrue(1.0 == alg.get(0)); - assertEquals(3,alg.size); - - alg.reset(); - - assertEquals(0,alg.size); - } - - @Test - void push_pop() { - GrowQueue_I8 alg = new GrowQueue_I8(10); - - alg.push(1); - alg.push(3); - - assertEquals(2,alg.size); - assertTrue(3==alg.pop()); - assertTrue(1==alg.pop()); - assertEquals(0, alg.size); - } - - @Test - void setTo_array() { - GrowQueue_I8 alg = new GrowQueue_I8(10); - - byte[] foo = new byte[]{1,3,4,5,7}; - alg.setTo(foo,1,3); - assertEquals(3,alg.size); - for (int i = 0; i < 3; i++) { - assertEquals(alg.get(i),foo[i+1]); - } - } - - @Test - void remove_two() { - GrowQueue_I8 alg = new GrowQueue_I8(10); - - alg.push(1); - alg.push(3); - alg.push(4); - alg.push(5); - alg.push(6); - - alg.remove(1,1); - assertEquals(4,alg.size); - assertEquals(1,alg.get(0)); - assertEquals(4,alg.get(1)); - assertEquals(5,alg.get(2)); - assertEquals(6,alg.get(3)); - alg.remove(0,1); - assertEquals(2,alg.size); - assertEquals(5,alg.get(0)); - assertEquals(6,alg.get(1)); - } - - @Override - public GrowQueue_I8 declare(int maxsize) { - return new GrowQueue_I8(maxsize); - } - - @Override - public void push(GrowQueue_I8 queue, double value) { - queue.push((int)value); - } - - @Override - public void insert(GrowQueue_I8 queue, int index, double value) { - queue.insert(index,(int)value); - } - - @Override - public void check(GrowQueue_I8 queue, int index, double value) { - assertEquals((int)value,queue.get(index)); - } - - @Test - void indexOf() { - GrowQueue_I8 alg = new GrowQueue_I8(10); - - alg.push(1); - alg.push(3); - alg.push(4); - alg.push(5); - - assertEquals(1,alg.indexOf((byte)3)); - assertEquals(-1,alg.indexOf((byte)8)); - } - - @Test - void sort() { - GrowQueue_I8 alg = new GrowQueue_I8(6); - - alg.push(8); - alg.push(2); - alg.push(4); - alg.push(3); - alg.push(-1); - - alg.sort(); - - assertEquals(5,alg.size); - assertEquals(-1,alg.get(0)); - assertEquals(2,alg.get(1)); - assertEquals(3,alg.get(2)); - assertEquals(4,alg.get(3)); - assertEquals(8,alg.get(4)); - } - - @Test - void getFraction() { - GrowQueue_I8 alg = new GrowQueue_I8(20); - - for (int i = 0; i < 20; i++) { - alg.add(i); - } - - assertEquals(0,alg.getFraction(0.0)); - assertEquals(0,alg.getFraction(0.02)); - assertEquals(0,alg.getFraction(0.03)); - assertEquals(1,alg.getFraction(1.0/19.0)); - assertEquals(1,alg.getFraction(1.7/19.0)); - assertEquals(19/2,alg.getFraction(0.5)); - assertEquals(19,alg.getFraction(1.0)); - } - - @Test - void getTail() { - GrowQueue_I8 alg = new GrowQueue_I8(20); - - for (int i = 0; i < 20; i++) { - alg.add(i); - } - - for (int i = 0; i < 20; i++) { - assertEquals(20-i-1,alg.getTail(i)); - } - } -} diff -Nru ddogleg-0.18+ds/test/org/ddogleg/struct/TestLinkedList.java ddogleg-0.22+ds/test/org/ddogleg/struct/TestLinkedList.java --- ddogleg-0.18+ds/test/org/ddogleg/struct/TestLinkedList.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/struct/TestLinkedList.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,433 +0,0 @@ -/* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. - * - * This file is part of DDogleg (http://ddogleg.org). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ddogleg.struct; - -import org.junit.jupiter.api.Test; - -import java.util.ArrayList; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.*; - -/** - * @author Peter Abeles - */ -public class TestLinkedList { - @Test - public void reset() { - LinkedList alg = new LinkedList(); - - alg.pushHead(1); - alg.pushHead(2); - alg.reset(); - assertEquals(2,alg.available.size()); - assertEquals(0,alg.size); - assertTrue(null==alg.first); - assertTrue(null==alg.last); - - checkList(alg); - } - - @Test - public void isEmpty() { - LinkedList alg = new LinkedList(); - - assertTrue(alg.isEmpty()); - alg.pushHead(1); - assertFalse(alg.isEmpty()); - alg.reset(); - assertTrue(alg.isEmpty()); - } - - @Test - public void getElement() { - LinkedList alg = new LinkedList(); - - LinkedList.Element e0 = alg.pushTail(1); - LinkedList.Element e1 = alg.pushTail(2); - LinkedList.Element e2 = alg.pushTail(2); - - assertTrue(e0 == alg.getElement(0,true)); - assertTrue(e1 == alg.getElement(1,true)); - assertTrue(e2 == alg.getElement(2,true)); - - assertTrue(e2 == alg.getElement(0,false)); - assertTrue(e1 == alg.getElement(1,false)); - assertTrue(e0 == alg.getElement(2,false)); - } - - @Test - public void pushHead() { - - LinkedList alg = new LinkedList(); - - LinkedList.Element e0 = alg.pushHead(1); - assertEquals(1,alg.size); - checkList(alg); - LinkedList.Element e1 = alg.pushHead(2); - assertEquals(2,alg.size); - assertTrue(e1 == alg.first); - assertTrue(e0 == alg.last); - checkList(alg); - - LinkedList.Element e2 = alg.pushHead(3); - assertEquals(3,alg.size); - assertTrue(e2 == alg.first); - assertTrue(e0 == alg.last); - checkList(alg); - } - - @Test - public void pushTail() { - LinkedList alg = new LinkedList(); - - LinkedList.Element e0 = alg.pushTail(1); - assertEquals(1,alg.size); - checkList(alg); - - LinkedList.Element e1 = alg.pushTail(2); - assertEquals(2,alg.size); - assertTrue(e0 == alg.first); - assertTrue(e1 == alg.last); - checkList(alg); - - LinkedList.Element e2 = alg.pushTail(3); - assertEquals(3,alg.size); - assertTrue(e0 == alg.first); - assertTrue(e2 == alg.last); - checkList(alg); - } - - @Test - public void insertAfter() { - LinkedList alg = new LinkedList(); - - LinkedList.Element e0 = alg.pushHead(1); - LinkedList.Element e1 = alg.insertAfter(e0, 2); - assertTrue(e0.next==e1); - assertTrue(e1.previous==e0); - assertEquals(2,alg.size); - checkList(alg); - - LinkedList.Element e2 = alg.insertAfter(e1, 2); - assertTrue(e1.next==e2); - assertTrue(e2.previous==e1); - assertTrue(e2 == alg.last); - assertEquals(3,alg.size); - checkList(alg); - } - - @Test - public void insertBefore() { - LinkedList alg = new LinkedList(); - - LinkedList.Element e0 = alg.pushHead(1); - LinkedList.Element e1 = alg.insertBefore(e0, 2); - assertTrue(e0.previous==e1); - assertTrue(e1.next==e0); - assertEquals(2,alg.size); - checkList(alg); - - LinkedList.Element e2 = alg.insertBefore(e1, 2); - assertTrue(e1.previous==e2); - assertTrue(e2.next==e1); - assertTrue(e2 == alg.first); - assertEquals(3,alg.size); - checkList(alg); - } - - @Test - public void swap() { - LinkedList alg = new LinkedList(); - - LinkedList.Element e0 = alg.pushTail(1); - LinkedList.Element e1 = alg.pushTail(2); - - alg.swap(e0,e1); - assertTrue(alg.first == e1); - assertTrue(alg.last == e0); - checkList(alg); - alg.swap(e0,e1); - assertTrue(alg.first == e0); - assertTrue(alg.last == e1); - checkList(alg); - - LinkedList.Element e2 = alg.pushTail(3); - alg.swap(e0,e1); - assertTrue(alg.first == e1); - assertTrue(alg.last == e2); - checkList(alg); - alg.swap(e0,e1); - assertTrue(alg.first == e0); - checkList(alg); - alg.swap(e1,e2); - assertTrue(alg.last == e1); - checkList(alg); - alg.swap(e1,e2); - assertTrue(alg.last == e2); - checkList(alg); - - LinkedList.Element e3 = alg.pushTail(4); - alg.swap(e0,e2); - assertTrue(alg.first == e2); - assertTrue(alg.last == e3); - checkList(alg); - alg.swap(e0,e2); - checkList(alg); - assertTrue(alg.first == e0); - assertTrue(alg.last == e3); - - } - - @Test - public void remove() { - LinkedList alg = new LinkedList(); - - LinkedList.Element e0,e1,e2; - - e0 = alg.pushTail(1); - alg.remove(e0); - assertEquals(0,alg.size); - assertEquals(1,alg.available.size()); - checkList(alg); - - e0 = alg.pushTail(1); - e1 = alg.pushTail(2); - alg.remove(e1); - assertTrue(e0==alg.first); - assertEquals(1,alg.size); - assertEquals(1,alg.available.size()); - checkList(alg); - - e1 = alg.pushTail(2); - alg.remove(e0); - assertTrue(e1==alg.first); - assertEquals(1,alg.size); - assertEquals(1,alg.available.size()); - checkList(alg); - - e0 = alg.pushHead(1); - e2 = alg.pushTail(3); - alg.remove(e1); - assertTrue(e0==alg.first); - assertEquals(2,alg.size); - assertEquals(1,alg.available.size()); - checkList(alg); - } - - @Test - public void removeHead() { - LinkedList alg = new LinkedList(); - - alg.pushHead(1); - assertTrue(alg.removeHead().equals(1)); - assertEquals(0,alg.size); - assertEquals(1,alg.available.size()); - checkList(alg); - - alg.pushTail(1); - alg.pushTail(2); - assertTrue(alg.removeHead().equals(1)); - assertEquals(1,alg.size); - assertEquals(1,alg.available.size()); - checkList(alg); - } - - @Test - public void removeTail() { - LinkedList alg = new LinkedList(); - - alg.pushHead(1); - assertTrue(alg.removeTail().equals(1)); - assertEquals(0,alg.size); - assertEquals(1,alg.available.size()); - checkList(alg); - - alg.pushTail(1); - alg.pushTail(2); - assertTrue(alg.removeTail().equals(2)); - assertEquals(1,alg.size); - assertEquals(1,alg.available.size()); - checkList(alg); - } - - @Test - public void find() { - LinkedList alg = new LinkedList(); - - assertTrue(null==alg.find(1)); - - LinkedList.Element e1 = alg.pushHead(1); - assertTrue(null==alg.find(4)); - assertTrue(e1==alg.find(e1.object)); - - LinkedList.Element e2 = alg.pushHead(2); - assertTrue(null==alg.find(4)); - assertTrue(e2==alg.find(e2.object)); - assertTrue(e1==alg.find(e1.object)); - } - - @Test - public void getHead() { - LinkedList alg = new LinkedList(); - - assertTrue(null==alg.getHead()); - LinkedList.Element e = alg.pushHead(1); - assertTrue(e==alg.getHead()); - - } - - @Test - public void getTail() { - LinkedList alg = new LinkedList(); - - assertTrue(null==alg.getHead()); - LinkedList.Element e = alg.pushHead(1); - assertTrue(e == alg.getTail()); - e = alg.pushTail(2); - assertTrue(e == alg.getTail()); - } - - @Test - public void addAll_List() { - LinkedList alg = new LinkedList(); - - List list0 = new ArrayList(); - List list1 = new ArrayList(); - - - alg.addAll(list0); - assertEquals(0,alg.size); - checkList(alg); - - list0.add(1); - alg.addAll(list0); - assertEquals(1,alg.size); - checkList(alg); - - alg.addAll(list1); - assertEquals(1,alg.size); - checkList(alg); - - list1.add(2); - list1.add(3); - alg.addAll(list1); - assertEquals(3,alg.size); - checkList(alg); - assertTrue(Integer.valueOf(1)==alg.getHead().object); - assertTrue(Integer.valueOf(3)==alg.getTail().object); - - } - - @Test - public void addAll_array() { - LinkedList alg = new LinkedList(); - - Integer[] array0 = new Integer[0]; - - alg.addAll(array0,0,0); - assertEquals(0,alg.size); - checkList(alg); - - array0 = new Integer[3]; - for (int i = 0; i < array0.length; i++) { - array0[i] = i; - } - - alg.addAll(array0,1,1); - assertEquals(1,alg.size); - assertTrue(Integer.valueOf(1)==alg.getHead().object); - checkList(alg); - - alg.addAll(array0,0,3); - assertEquals(4,alg.size); - assertTrue(Integer.valueOf(1)==alg.getHead().object); - assertTrue(Integer.valueOf(0)==alg.getElement(1, true).object); - assertTrue(Integer.valueOf(1)==alg.getElement(2, true).object); - assertTrue(Integer.valueOf(2)==alg.getElement(3, true).object); - checkList(alg); - } - - @Test - public void requestNew() { - LinkedList alg = new LinkedList(); - - LinkedList.Element e0 = alg.requestNew(); - assertTrue(e0 != null); - - LinkedList.Element e1 = alg.requestNew(); - assertTrue(e1 != null); - assertTrue(e1 != e0); - - alg.available.add(e0); - LinkedList.Element e2 = alg.requestNew(); - assertTrue(e2 == e0); - - LinkedList.Element e3 = alg.requestNew(); - assertTrue(e3 != null); - assertTrue(e3 != e0); - assertTrue(e3 != e1); - assertTrue(e3 != e2); - } - - /** - * Performs checks on the lists preconditions - */ - protected void checkList( LinkedList list ) { - for (int i = 0; i < list.available.size(); i++) { - LinkedList.Element e = (LinkedList.Element)list.available.get(i); - assertTrue(null==e.previous); - assertTrue(null==e.next); - assertTrue(null==e.object); - } - - if( list.size == 0 ) { - assertTrue(null==list.first); - assertTrue(null==list.last); - } else { - List forwards = new ArrayList(); - List backwards = new ArrayList(); - - LinkedList.Element e = list.first; - while( e != null ) { - forwards.add(e); - e = e.next; - if( forwards.size() > list.size() ) - fail("too many elements in forward direction"); - } - - e = list.last; - while( e != null ) { - backwards.add(e); - e = e.previous; - if( backwards.size() > list.size() ) - fail("too many elements in forward direction"); - } - - assertEquals(forwards.size(),backwards.size()); - assertEquals(forwards.size(),list.size()); - - for (int i = 0; i < forwards.size(); i++) { - int j = forwards.size()-1-i; - assertTrue(forwards.get(i)==backwards.get(j)); - } - } - - } -} \ No newline at end of file diff -Nru ddogleg-0.18+ds/test/org/ddogleg/struct/TestRecycleManagerL.java ddogleg-0.22+ds/test/org/ddogleg/struct/TestRecycleManagerL.java --- ddogleg-0.18+ds/test/org/ddogleg/struct/TestRecycleManagerL.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/struct/TestRecycleManagerL.java 2022-09-01 02:18:09.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, Peter Abeles. All Rights Reserved. + * Copyright (c) 2012-2020, Peter Abeles. All Rights Reserved. * * This file is part of DDogleg (http://ddogleg.org). * @@ -40,11 +40,9 @@ assertEquals(3, manager.getUnused().size()); assertEquals(0, manager.getUsed().size()); - assertTrue(first != third); assertTrue(first != second); - assertTrue(third == manager.requestInstance()); // just needs to be an element previously returned, really assertEquals(2, manager.getUnused().size()); assertEquals(1, manager.getUsed().size()); diff -Nru ddogleg-0.18+ds/test/org/ddogleg/util/TestPrimitiveArrays.java ddogleg-0.22+ds/test/org/ddogleg/util/TestPrimitiveArrays.java --- ddogleg-0.18+ds/test/org/ddogleg/util/TestPrimitiveArrays.java 2020-05-16 02:12:10.000000000 +0000 +++ ddogleg-0.22+ds/test/org/ddogleg/util/TestPrimitiveArrays.java 2022-09-01 02:18:09.000000000 +0000 @@ -18,6 +18,7 @@ package org.ddogleg.util; +import org.ddogleg.struct.DogArray_I32; import org.junit.jupiter.api.Test; import java.util.Random; @@ -29,376 +30,778 @@ * @author Peter Abeles */ class TestPrimitiveArrays { + @Test void intersection_FindMinMax() { + int[] setA = new int[]{8, 7, 6, 3, 1, 0, -2, -5}; + int[] setB = new int[]{3, 4, 5, -3, 1, 7, -2, -5}; + int[] expected = new int[]{-5, -2, 1, 3, 7}; - @Test - void fillCount_input() { - int[] orig = new int[100]; - PrimitiveArrays.fillCounting(orig,10,40); - for (int i = 0; i < orig.length; i++) { - if( i < 10 || i >= 50 ) { - assertEquals(0,orig[i]); + var results = new DogArray_I32(); + results.add(-99); // make sure it's cleared + + // intentionally give it a min/max that's larger than the actual + PrimitiveArrays.intersection(setA, setA.length, setB, setB.length, results, null); + + assertEquals(expected.length, results.size); + for (int i = 0; i < results.size; i++) { + assertEquals(expected[i], results.get(i)); + } + } + + @Test void intersection_GivenMinMax() { + int[] setA = new int[]{8, 7, 6, 3, 1, 0, -2, -5}; + int[] setB = new int[]{3, 4, 5, -3, 1, 7, -2, -5}; + int[] expected = new int[]{-5, -2, 1, 3, 7}; + + var results = new DogArray_I32(); + results.add(-99); // make sure it's cleared + + // intentionally give it a min/max that's larger than the actual + PrimitiveArrays.intersection(setA, setA.length, setB, setB.length, -6, 20, results, null); + + assertEquals(expected.length, results.size); + for (int i = 0; i < results.size; i++) { + assertEquals(expected[i], results.get(i)); + } + } + + @Test void union_GivenMinMax() { + int[] setA = new int[]{8, 7, 6, 3, 1, 0, -2, -5}; + int[] setB = new int[]{3, 4, 5, -3, 1, 7, -2, -5}; + int[] expected = new int[]{-5, -3, -2, 0, 1, 3, 4, 5, 6, 7, 8}; + + var results = new DogArray_I32(); + results.add(-99); // make sure it's cleared + + // intentionally give it a min/max that's larger than the actual + PrimitiveArrays.union(setA, setA.length, setB, setB.length, -6, 20, results, null); + + assertEquals(expected.length, results.size); + for (int i = 0; i < results.size; i++) { + assertEquals(expected[i], results.get(i)); + } + } + + @Test void fillCount_input() { + int[] values = new int[100]; + PrimitiveArrays.fillCounting(values, 10, 40); + for (int i = 0; i < values.length; i++) { + if (i < 10 || i >= 50) { + assertEquals(0, values[i]); } else { - assertEquals(i-10,orig[i]); + assertEquals(i - 10, values[i]); } } } - @Test - void shuffle_byte() { + @Test void shuffle_byte() { Random rand = new Random(234); - byte[] orig = new byte[100]; + byte[] values = new byte[100]; for (int i = 0; i < 90; i++) { - orig[i+10] = (byte)i; + values[i + 10] = (byte)i; } - PrimitiveArrays.shuffle(orig,10,90,rand); + PrimitiveArrays.shuffle(values, 10, 90, rand); // make sure each element still only appears once for (int i = 0; i < 90; i++) { int count = 0; for (int j = 0; j < 90; j++) { - if( Math.abs(orig[j+10]-i) == 0 ) { + if (Math.abs(values[j + 10] - i) == 0) { count++; } } - assertEquals(1,count); + assertEquals(1, count); } // see if elements were shuffled around. If too many match // then something is wrong int matches = 0; for (int i = 0; i < 90; i++) { - if( Math.abs(orig[i+10]-i) == 0 ) { + if (Math.abs(values[i + 10] - i) == 0) { matches++; } } - assertTrue(matches<10); + assertTrue(matches < 10); + } + + @Test void shuffle_byte_zeroLength() { + var values = new byte[5]; + for (int i = 0; i < values.length; i++) { + values[i] = (byte)(5-i); + } + + // nothing should change if it shuffles with a length of 1 + PrimitiveArrays.shuffle(values, 1, 0, new Random(234)); + for (int i = 0; i < 5; i++) { + assertEquals(5-i, values[i]); + } + + PrimitiveArrays.shuffle(new byte[0], 0, 0, new Random(234)); } - @Test - void shuffle_short() { + @Test void shuffle_short() { Random rand = new Random(234); - short[] orig = new short[100]; + short[] values = new short[100]; for (int i = 0; i < 90; i++) { - orig[i+10] = (short)i; + values[i + 10] = (short)i; } - PrimitiveArrays.shuffle(orig,10,90,rand); + PrimitiveArrays.shuffle(values, 10, 90, rand); // make sure each element still only appears once for (int i = 0; i < 90; i++) { int count = 0; for (int j = 0; j < 90; j++) { - if( Math.abs(orig[j+10]-i) == 0 ) { + if (Math.abs(values[j + 10] - i) == 0) { count++; } } - assertEquals(1,count); + assertEquals(1, count); } // see if elements were shuffled around. If too many match // then something is wrong int matches = 0; for (int i = 0; i < 90; i++) { - if( Math.abs(orig[i+10]-i) == 0 ) { + if (Math.abs(values[i + 10] - i) == 0) { matches++; } } - assertTrue(matches<10); + assertTrue(matches < 10); } - @Test - void shuffle_int() { + @Test void shuffle_short_zeroLength() { + var values = new short[5]; + for (int i = 0; i < values.length; i++) { + values[i] = (short)(5-i); + } + + // nothing should change if it shuffles with a length of 1 + PrimitiveArrays.shuffle(values, 1, 0, new Random(234)); + for (int i = 0; i < 5; i++) { + assertEquals(5-i, values[i]); + } + + PrimitiveArrays.shuffle(new short[0], 0, 0, new Random(234)); + } + + @Test void shuffle_int() { Random rand = new Random(234); - int[] orig = new int[100]; + int[] values = new int[100]; for (int i = 0; i < 90; i++) { - orig[i+10] = i; + values[i + 10] = i; } - PrimitiveArrays.shuffle(orig,10,90,rand); + PrimitiveArrays.shuffle(values, 10, 90, rand); // make sure each element still only appears once for (int i = 0; i < 90; i++) { int count = 0; for (int j = 0; j < 90; j++) { - if( Math.abs(orig[j+10]-i) == 0 ) { + if (Math.abs(values[j + 10] - i) == 0) { count++; } } - assertEquals(1,count); + assertEquals(1, count); } // see if elements were shuffled around. If too many match // then something is wrong int matches = 0; for (int i = 0; i < 90; i++) { - if( Math.abs(orig[i+10]-i) == 0 ) { + if (Math.abs(values[i + 10] - i) == 0) { matches++; } } - assertTrue(matches<10); + assertTrue(matches < 10); + } + + @Test void shuffle_int_zeroLength() { + var values = new int[5]; + for (int i = 0; i < values.length; i++) { + values[i] = (int)(5-i); + } + + // nothing should change if it shuffles with a length of 1 + PrimitiveArrays.shuffle(values, 1, 0, new Random(234)); + for (int i = 0; i < 5; i++) { + assertEquals(5-i, values[i]); + } + + PrimitiveArrays.shuffle(new int[0], 0, 0, new Random(234)); } - @Test - void shuffle_float() { + @Test void shuffle_long() { Random rand = new Random(234); - float[] orig = new float[100]; + long[] values = new long[100]; for (int i = 0; i < 90; i++) { - orig[i+10] = i; + values[i + 10] = i; } - PrimitiveArrays.shuffle(orig,10,90,rand); + PrimitiveArrays.shuffle(values, 10, 90, rand); // make sure each element still only appears once for (int i = 0; i < 90; i++) { int count = 0; for (int j = 0; j < 90; j++) { - if( Math.abs(orig[j+10]-i) == 0 ) { + if (Math.abs(values[j + 10] - i) == 0) { count++; } } - assertEquals(1,count); + assertEquals(1, count); } // see if elements were shuffled around. If too many match // then something is wrong int matches = 0; for (int i = 0; i < 90; i++) { - if( Math.abs(orig[i+10]-i) == 0 ) { + if (Math.abs(values[i + 10] - i) == 0) { matches++; } } - assertTrue(matches<10); + assertTrue(matches < 10); } - @Test - void shuffle_double() { + @Test void shuffle_long_zeroLength() { + var values = new long[5]; + for (int i = 0; i < values.length; i++) { + values[i] = (long)(5-i); + } + + // nothing should change if it shuffles with a length of 1 + PrimitiveArrays.shuffle(values, 1, 0, new Random(234)); + for (int i = 0; i < values.length; i++) { + assertEquals(5-i, values[i]); + } + + PrimitiveArrays.shuffle(new long[0], 0, 0, new Random(234)); + } + + @Test void shuffle_float() { Random rand = new Random(234); - double[] orig = new double[100]; + float[] values = new float[100]; for (int i = 0; i < 90; i++) { - orig[i+10] = i; + values[i + 10] = i; } - PrimitiveArrays.shuffle(orig,10,90,rand); + PrimitiveArrays.shuffle(values, 10, 90, rand); // make sure each element still only appears once for (int i = 0; i < 90; i++) { int count = 0; for (int j = 0; j < 90; j++) { - if( Math.abs(orig[j+10]-i) == 0 ) { + if (Math.abs(values[j + 10] - i) == 0) { count++; } } - assertEquals(1,count); + assertEquals(1, count); } // see if elements were shuffled around. If too many match // then something is wrong int matches = 0; for (int i = 0; i < 90; i++) { - if( Math.abs(orig[i+10]-i) == 0 ) { + if (Math.abs(values[i + 10] - i) == 0) { matches++; } } - assertTrue(matches<10); + assertTrue(matches < 10); + } + + @Test void shuffle_float_zeroLength() { + var values = new float[5]; + for (int i = 0; i < values.length; i++) { + values[i] = (float)(5-i); + } + + // nothing should change if it shuffles with a length of 1 + PrimitiveArrays.shuffle(values, 1, 0, new Random(234)); + for (int i = 0; i < 5; i++) { + assertEquals(5-i, values[i]); + } + + PrimitiveArrays.shuffle(new float[0], 0, 0, new Random(234)); } - @Test - void min_byte() { - byte[] orig = new byte[100]; - orig[1] = 120; + @Test void shuffle_double() { + Random rand = new Random(234); + double[] values = new double[100]; + for (int i = 0; i < 90; i++) { + values[i + 10] = i; + } + PrimitiveArrays.shuffle(values, 10, 90, rand); + + // make sure each element still only appears once for (int i = 0; i < 90; i++) { - orig[i+10] = (byte)(i+2); + int count = 0; + for (int j = 0; j < 90; j++) { + if (Math.abs(values[j + 10] - i) == 0) { + count++; + } + } + assertEquals(1, count); + } + + // see if elements were shuffled around. If too many match + // then something is wrong + int matches = 0; + for (int i = 0; i < 90; i++) { + if (Math.abs(values[i + 10] - i) == 0) { + matches++; + } + } + assertTrue(matches < 10); + + PrimitiveArrays.shuffle(new double[0], 0, 0, new Random(234)); + } + + @Test void shuffle_double_zeroLength() { + var values = new double[5]; + for (int i = 0; i < values.length; i++) { + values[i] = (double)(5-i); + } + + // nothing should change if it shuffles with a length of 1 + PrimitiveArrays.shuffle(values, 1, 0, new Random(234)); + for (int i = 0; i < 5; i++) { + assertEquals(5-i, values[i]); } - assertEquals(2, PrimitiveArrays.min(orig,10,90)); } - @Test - void min_short() { - short[] orig = new short[100]; - orig[1] = 120; + @Test void min_byte() { + byte[] values = new byte[100]; + values[1] = 120; for (int i = 0; i < 90; i++) { - orig[i+10] = (short)(i+2); + values[i + 10] = (byte)(i + 2); } - assertEquals(2, PrimitiveArrays.min(orig,10,90)); + assertEquals(2, PrimitiveArrays.min(values, 10, 90)); + } + + @Test void min_byte_extremes() { + assertEquals(Byte.MIN_VALUE, PrimitiveArrays.min(new byte[]{Byte.MIN_VALUE}, 0, 1)); + assertEquals(Byte.MAX_VALUE, PrimitiveArrays.min(new byte[]{Byte.MAX_VALUE}, 0, 1)); } - @Test - void min_int() { - int[] orig = new int[100]; - orig[1] = 120; + @Test void min_short() { + short[] values = new short[100]; + values[1] = 120; for (int i = 0; i < 90; i++) { - orig[i+10] = i+2; + values[i + 10] = (short)(i + 2); } - assertEquals(2, PrimitiveArrays.min(orig,10,90)); + assertEquals(2, PrimitiveArrays.min(values, 10, 90)); + } + + @Test void min_short_extremes() { + assertEquals(Short.MIN_VALUE, PrimitiveArrays.min(new short[]{Short.MIN_VALUE}, 0, 1)); + assertEquals(Short.MAX_VALUE, PrimitiveArrays.min(new short[]{Short.MAX_VALUE}, 0, 1)); } - @Test - void min_long() { - long[] orig = new long[100]; - orig[1] = 120; + @Test void min_int() { + int[] values = new int[100]; + values[1] = 120; for (int i = 0; i < 90; i++) { - orig[i+10] = i+2; + values[i + 10] = i + 2; } - assertEquals(2, PrimitiveArrays.min(orig,10,90)); + assertEquals(2, PrimitiveArrays.min(values, 10, 90)); } - @Test - void min_float() { - float[] orig = new float[100]; - orig[1] = 120; + @Test void min_int_extremes() { + assertEquals(Integer.MIN_VALUE, PrimitiveArrays.min(new int[]{Integer.MIN_VALUE}, 0, 1)); + assertEquals(Integer.MAX_VALUE, PrimitiveArrays.min(new int[]{Integer.MAX_VALUE}, 0, 1)); + } + + @Test void min_long() { + long[] values = new long[100]; + values[1] = 120; for (int i = 0; i < 90; i++) { - orig[i+10] = i+2; + values[i + 10] = i + 2; } - assertEquals(2, PrimitiveArrays.min(orig,10,90)); + assertEquals(2, PrimitiveArrays.min(values, 10, 90)); + } + + @Test void min_long_extremes() { + assertEquals(Long.MIN_VALUE, PrimitiveArrays.min(new long[]{Long.MIN_VALUE}, 0, 1)); + assertEquals(Long.MAX_VALUE, PrimitiveArrays.min(new long[]{Long.MAX_VALUE}, 0, 1)); } - @Test - void min_double() { - double[] orig = new double[100]; - orig[1] = 120; + @Test void min_float() { + float[] values = new float[100]; + values[1] = 120; for (int i = 0; i < 90; i++) { - orig[i+10] = i+2; + values[i + 10] = i + 2; } - assertEquals(2, PrimitiveArrays.min(orig,10,90)); + assertEquals(2, PrimitiveArrays.min(values, 10, 90)); + } + + @Test void min_float_extremes() { + assertEquals(-Float.MAX_VALUE, PrimitiveArrays.min(new float[]{-Float.MAX_VALUE}, 0, 1)); + assertEquals(Float.MAX_VALUE, PrimitiveArrays.min(new float[]{Float.MAX_VALUE}, 0, 1)); } - @Test - void max_byte() { - byte[] orig = new byte[100]; - orig[1] = 120; + @Test void min_double() { + double[] values = new double[100]; + values[1] = 120; for (int i = 0; i < 90; i++) { - orig[i+10] = (byte)i; + values[i + 10] = i + 2; } - assertEquals(89, PrimitiveArrays.max(orig,10,90)); + assertEquals(2, PrimitiveArrays.min(values, 10, 90)); } - @Test - void max_short() { - short[] orig = new short[100]; - orig[1] = 120; + @Test void min_double_extremes() { + assertEquals(-Double.MAX_VALUE, PrimitiveArrays.min(new double[]{-Double.MAX_VALUE}, 0, 1)); + assertEquals(Double.MAX_VALUE, PrimitiveArrays.min(new double[]{Double.MAX_VALUE}, 0, 1)); + } + + @Test void minIdx_byte() { + byte[] values = new byte[100]; + values[1] = 120; for (int i = 0; i < 90; i++) { - orig[i+10] = (short)i; + values[i + 10] = (byte)(i + 2); } - assertEquals(89, PrimitiveArrays.max(orig,10,90)); + assertEquals(10, PrimitiveArrays.minIdx(values, 10, 90)); + } + + @Test void minIdx_byte_extremes() { + assertEquals(0, PrimitiveArrays.minIdx(new byte[]{Byte.MIN_VALUE}, 0, 1)); + assertEquals(0, PrimitiveArrays.minIdx(new byte[]{Byte.MAX_VALUE}, 0, 1)); } - @Test - void max_int() { - int[] orig = new int[100]; - orig[1] = 120; + @Test void minIdx_int() { + int[] values = new int[100]; + values[1] = 120; for (int i = 0; i < 90; i++) { - orig[i+10] = i; + values[i + 10] = i + 2; } - assertEquals(89, PrimitiveArrays.max(orig,10,90)); + assertEquals(10, PrimitiveArrays.minIdx(values, 10, 90)); + } + + @Test void minIdx_int_extremes() { + assertEquals(0, PrimitiveArrays.minIdx(new int[]{Integer.MIN_VALUE}, 0, 1)); + assertEquals(0, PrimitiveArrays.minIdx(new int[]{Integer.MAX_VALUE}, 0, 1)); } - @Test - void max_long() { - long[] orig = new long[100]; - orig[1] = 120; + @Test void minIdx_float() { + float[] values = new float[100]; + values[1] = 120; for (int i = 0; i < 90; i++) { - orig[i+10] = (long)i; + values[i + 10] = i + 2; } - assertEquals(89, PrimitiveArrays.max(orig,10,90)); + assertEquals(10, PrimitiveArrays.minIdx(values, 10, 90)); } - @Test - void max_float() { - float[] orig = new float[100]; - orig[1] = 120; + @Test void minIdx_float_extremes() { + assertEquals(0, PrimitiveArrays.minIdx(new float[]{-Float.MAX_VALUE}, 0, 1)); + assertEquals(0, PrimitiveArrays.minIdx(new float[]{Float.MAX_VALUE}, 0, 1)); + } + + @Test void minIdx_double() { + double[] values = new double[100]; + values[1] = 120; for (int i = 0; i < 90; i++) { - orig[i+10] = i; + values[i + 10] = i + 2; } - assertEquals(89, PrimitiveArrays.max(orig,10,90)); + assertEquals(10, PrimitiveArrays.minIdx(values, 10, 90)); } - @Test - void max_double() { - double[] orig = new double[100]; - orig[1] = 120; + @Test void minIdx_double_extremes() { + assertEquals(0, PrimitiveArrays.minIdx(new double[]{-Double.MAX_VALUE}, 0, 1)); + assertEquals(0, PrimitiveArrays.minIdx(new double[]{Double.MAX_VALUE}, 0, 1)); + } + + @Test void max_byte() { + byte[] values = new byte[100]; + values[1] = 120; for (int i = 0; i < 90; i++) { - orig[i+10] = i; + values[i + 10] = (byte)i; } - assertEquals(89, PrimitiveArrays.max(orig,10,90)); + assertEquals(89, PrimitiveArrays.max(values, 10, 90)); } - @Test - void lowerBoundU_byte() { - byte[] orig = new byte[100]; - orig[0] = 124; + @Test void max_byte_extremes() { + assertEquals(Byte.MIN_VALUE, PrimitiveArrays.max(new byte[]{Byte.MIN_VALUE}, 0, 1)); + assertEquals(Byte.MAX_VALUE, PrimitiveArrays.max(new byte[]{Byte.MAX_VALUE}, 0, 1)); + } + + @Test void max_short() { + short[] values = new short[100]; + values[1] = 120; + for (int i = 0; i < 90; i++) { + values[i + 10] = (short)i; + } + assertEquals(89, PrimitiveArrays.max(values, 10, 90)); + } + + @Test void max_short_extremes() { + assertEquals(Short.MIN_VALUE, PrimitiveArrays.max(new short[]{Short.MIN_VALUE}, 0, 1)); + assertEquals(Short.MAX_VALUE, PrimitiveArrays.max(new short[]{Short.MAX_VALUE}, 0, 1)); + } + + @Test void max_int() { + int[] values = new int[100]; + values[1] = 120; + for (int i = 0; i < 90; i++) { + values[i + 10] = i; + } + assertEquals(89, PrimitiveArrays.max(values, 10, 90)); + } + + @Test void max_int_extremes() { + assertEquals(Integer.MIN_VALUE, PrimitiveArrays.max(new int[]{Integer.MIN_VALUE}, 0, 1)); + assertEquals(Integer.MAX_VALUE, PrimitiveArrays.max(new int[]{Integer.MAX_VALUE}, 0, 1)); + } + + @Test void max_long() { + long[] values = new long[100]; + values[1] = 120; + for (int i = 0; i < 90; i++) { + values[i + 10] = (long)i; + } + assertEquals(89, PrimitiveArrays.max(values, 10, 90)); + } + + @Test void max_long_extremes() { + assertEquals(Long.MIN_VALUE, PrimitiveArrays.max(new long[]{Long.MIN_VALUE}, 0, 1)); + assertEquals(Long.MAX_VALUE, PrimitiveArrays.max(new long[]{Long.MAX_VALUE}, 0, 1)); + } + + @Test void max_float() { + float[] values = new float[100]; + values[1] = 120; + for (int i = 0; i < 90; i++) { + values[i + 10] = i; + } + assertEquals(89, PrimitiveArrays.max(values, 10, 90)); + } + + @Test void max_float_extremes() { + float[] values = {-Float.MAX_VALUE, Float.MAX_VALUE}; + assertEquals(-Float.MAX_VALUE, PrimitiveArrays.min(values, 0, values.length)); + } + + @Test void max_double() { + double[] values = new double[100]; + values[1] = 120; + for (int i = 0; i < 90; i++) { + values[i + 10] = i; + } + assertEquals(89, PrimitiveArrays.max(values, 10, 90)); + } + + @Test void max_double_extremes() { + double[] values = {-Double.MAX_VALUE, Double.MAX_VALUE}; + assertEquals(-Double.MAX_VALUE, PrimitiveArrays.min(values, 0, values.length)); + } + + @Test void maxIdx_byte() { + byte[] values = new byte[100]; + values[1] = 120; + for (int i = 0; i < 90; i++) { + values[i + 10] = (byte)i; + } + assertEquals(99, PrimitiveArrays.maxIdx(values, 10, 90)); + } + + @Test void maxIdx_byte_extremes() { + assertEquals(0, PrimitiveArrays.maxIdx(new byte[]{Byte.MIN_VALUE}, 0, 1)); + assertEquals(0, PrimitiveArrays.maxIdx(new byte[]{Byte.MAX_VALUE}, 0, 1)); + } + + @Test void maxIdx_int() { + int[] values = new int[100]; + values[1] = 120; + for (int i = 0; i < 90; i++) { + values[i + 10] = i; + } + assertEquals(99, PrimitiveArrays.maxIdx(values, 10, 90)); + } + + @Test void maxIdx_int_extremes() { + assertEquals(0, PrimitiveArrays.maxIdx(new int[]{Integer.MIN_VALUE}, 0, 1)); + assertEquals(0, PrimitiveArrays.maxIdx(new int[]{Integer.MAX_VALUE}, 0, 1)); + } + + @Test void maxIdx_float() { + float[] values = new float[100]; + values[1] = 120; + for (int i = 0; i < 90; i++) { + values[i + 10] = i; + } + assertEquals(99, PrimitiveArrays.maxIdx(values, 10, 90)); + } + + @Test void maxIdx_float_extremes() { + assertEquals(0, PrimitiveArrays.maxIdx(new float[]{-Float.MAX_VALUE}, 0, 1)); + assertEquals(0, PrimitiveArrays.maxIdx(new float[]{Float.MAX_VALUE}, 0, 1)); + } + + @Test void maxIdx_double() { + double[] values = new double[100]; + values[1] = 120; + for (int i = 0; i < 90; i++) { + values[i + 10] = i; + } + assertEquals(99, PrimitiveArrays.maxIdx(values, 10, 90)); + } + + @Test void maxIdx_double_extremes() { + assertEquals(0, PrimitiveArrays.maxIdx(new double[]{-Double.MAX_VALUE}, 0, 1)); + assertEquals(0, PrimitiveArrays.maxIdx(new double[]{Double.MAX_VALUE}, 0, 1)); + } + + @Test void lowerBoundU_byte() { + byte[] values = new byte[100]; + values[0] = 124; for (int i = 1; i < 100; i++) { - orig[i] = (byte)(i+100); + values[i] = (byte)(i + 100); } - assertEquals(51, PrimitiveArrays.lowerBoundU(orig,1,99,151)); - assertEquals(50, PrimitiveArrays.lowerBoundU(orig,1,99,150)); - assertEquals(49, PrimitiveArrays.lowerBoundU(orig,1,99,149)); - assertEquals(1, PrimitiveArrays.lowerBoundU(orig,1,99,0)); - assertEquals(100, PrimitiveArrays.lowerBoundU(orig,1,99,300)); + assertEquals(51, PrimitiveArrays.lowerBoundU(values, 1, 99, 151)); + assertEquals(50, PrimitiveArrays.lowerBoundU(values, 1, 99, 150)); + assertEquals(49, PrimitiveArrays.lowerBoundU(values, 1, 99, 149)); + assertEquals(1, PrimitiveArrays.lowerBoundU(values, 1, 99, 0)); + assertEquals(100, PrimitiveArrays.lowerBoundU(values, 1, 99, 300)); } - @Test - void lowerBound_byte() { - byte[] orig = new byte[100]; - orig[0] = 124; + @Test void lowerBound_byte() { + byte[] values = new byte[100]; + values[0] = 124; for (int i = 1; i < 100; i++) { - orig[i] = (byte)(i-50); + values[i] = (byte)(i - 50); } - assertEquals(51, PrimitiveArrays.lowerBound(orig,1,99,1)); - assertEquals(50, PrimitiveArrays.lowerBound(orig,1,99,0)); - assertEquals(49, PrimitiveArrays.lowerBound(orig,1,99,-1)); - assertEquals(1, PrimitiveArrays.lowerBound(orig,1,99,-100)); - assertEquals(100, PrimitiveArrays.lowerBound(orig,1,99,200)); + assertEquals(51, PrimitiveArrays.lowerBound(values, 1, 99, 1)); + assertEquals(50, PrimitiveArrays.lowerBound(values, 1, 99, 0)); + assertEquals(49, PrimitiveArrays.lowerBound(values, 1, 99, -1)); + assertEquals(1, PrimitiveArrays.lowerBound(values, 1, 99, -100)); + assertEquals(100, PrimitiveArrays.lowerBound(values, 1, 99, 200)); } - @Test - void lowerBound_short() { - short[] orig = new short[100]; - orig[0] = 9999; + @Test void lowerBound_short() { + short[] values = new short[100]; + values[0] = 9999; for (int i = 1; i < 100; i++) { - orig[i] = (short)(i-50); + values[i] = (short)(i - 50); } - assertEquals(51, PrimitiveArrays.lowerBound(orig,1,99,1)); - assertEquals(50, PrimitiveArrays.lowerBound(orig,1,99,0)); - assertEquals(49, PrimitiveArrays.lowerBound(orig,1,99,-1)); - assertEquals(1, PrimitiveArrays.lowerBound(orig,1,99,-100)); - assertEquals(100, PrimitiveArrays.lowerBound(orig,1,99,200)); + assertEquals(51, PrimitiveArrays.lowerBound(values, 1, 99, 1)); + assertEquals(50, PrimitiveArrays.lowerBound(values, 1, 99, 0)); + assertEquals(49, PrimitiveArrays.lowerBound(values, 1, 99, -1)); + assertEquals(1, PrimitiveArrays.lowerBound(values, 1, 99, -100)); + assertEquals(100, PrimitiveArrays.lowerBound(values, 1, 99, 200)); } - @Test - void lowerBound_int() { - int[] orig = new int[100]; - orig[0] = 99999; + @Test void lowerBound_int() { + int[] values = new int[100]; + values[0] = 99999; for (int i = 1; i < 100; i++) { - orig[i] = i; + values[i] = i; } - assertEquals(51, PrimitiveArrays.lowerBound(orig,1,99,51)); - assertEquals(50, PrimitiveArrays.lowerBound(orig,1,99,50)); - assertEquals(49, PrimitiveArrays.lowerBound(orig,1,99,49)); - assertEquals(1, PrimitiveArrays.lowerBound(orig,1,99,-1)); - assertEquals(100, PrimitiveArrays.lowerBound(orig,1,99,200)); + assertEquals(51, PrimitiveArrays.lowerBound(values, 1, 99, 51)); + assertEquals(50, PrimitiveArrays.lowerBound(values, 1, 99, 50)); + assertEquals(49, PrimitiveArrays.lowerBound(values, 1, 99, 49)); + assertEquals(1, PrimitiveArrays.lowerBound(values, 1, 99, -1)); + assertEquals(100, PrimitiveArrays.lowerBound(values, 1, 99, 200)); } - @Test - void lowerBound_float() { - float[] orig = new float[100]; - orig[0] = 99999; + @Test void lowerBound_float() { + float[] values = new float[100]; + values[0] = 99999; for (int i = 1; i < 100; i++) { - orig[i] = i; + values[i] = i; } - assertEquals(51, PrimitiveArrays.lowerBound(orig,1,99,50.1f)); - assertEquals(50, PrimitiveArrays.lowerBound(orig,1,99,50f)); - assertEquals(50, PrimitiveArrays.lowerBound(orig,1,99,49.9f)); - assertEquals(1, PrimitiveArrays.lowerBound(orig,1,99,-1f)); - assertEquals(100, PrimitiveArrays.lowerBound(orig,1,99,200f)); + assertEquals(51, PrimitiveArrays.lowerBound(values, 1, 99, 50.1f)); + assertEquals(50, PrimitiveArrays.lowerBound(values, 1, 99, 50f)); + assertEquals(50, PrimitiveArrays.lowerBound(values, 1, 99, 49.9f)); + assertEquals(1, PrimitiveArrays.lowerBound(values, 1, 99, -1f)); + assertEquals(100, PrimitiveArrays.lowerBound(values, 1, 99, 200f)); } - @Test - void lowerBound_double() { - double[] orig = new double[100]; - orig[0] = 99999; + @Test void lowerBound_double() { + double[] values = new double[100]; + values[0] = 99999; for (int i = 1; i < 100; i++) { - orig[i] = i; + values[i] = i; } - assertEquals(51, PrimitiveArrays.lowerBound(orig,1,99,50.1)); - assertEquals(50, PrimitiveArrays.lowerBound(orig,1,99,50)); - assertEquals(50, PrimitiveArrays.lowerBound(orig,1,99,49.9)); - assertEquals(1, PrimitiveArrays.lowerBound(orig,1,99,-1)); - assertEquals(100, PrimitiveArrays.lowerBound(orig,1,99,200)); + assertEquals(51, PrimitiveArrays.lowerBound(values, 1, 99, 50.1)); + assertEquals(50, PrimitiveArrays.lowerBound(values, 1, 99, 50)); + assertEquals(50, PrimitiveArrays.lowerBound(values, 1, 99, 49.9)); + assertEquals(1, PrimitiveArrays.lowerBound(values, 1, 99, -1)); + assertEquals(100, PrimitiveArrays.lowerBound(values, 1, 99, 200)); + } + + @Test void sumD_byte() { + var array = new byte[]{5, -2, 10, 120}; + + assertEquals(133.0, PrimitiveArrays.sumD(array, 0, 4)); + assertEquals(128.0, PrimitiveArrays.sumD(array, 1, 3)); + } + + @Test void sumD_short() { + var array = new short[]{5, -2, 10, 120}; + + assertEquals(133.0, PrimitiveArrays.sumD(array, 0, 4)); + assertEquals(128.0, PrimitiveArrays.sumD(array, 1, 3)); + } + + @Test void sumD_int() { + var array = new int[]{5, -2, 10, 120}; + + assertEquals(133.0, PrimitiveArrays.sumD(array, 0, 4)); + assertEquals(128.0, PrimitiveArrays.sumD(array, 1, 3)); + } + + @Test void sumD_long() { + var array = new long[]{5, -2, 10, 120}; + + assertEquals(133.0, PrimitiveArrays.sumD(array, 0, 4)); + assertEquals(128.0, PrimitiveArrays.sumD(array, 1, 3)); + } + + @Test void sumD_float() { + var array = new float[]{5, -2, 10, 120}; + + assertEquals(133.0, PrimitiveArrays.sumD(array, 0, 4)); + assertEquals(128.0, PrimitiveArrays.sumD(array, 1, 3)); + } + + @Test void sumD_double() { + var array = new double[]{5, -2, 10, 120}; + + assertEquals(133.0, PrimitiveArrays.sumD(array, 0, 4)); + assertEquals(128.0, PrimitiveArrays.sumD(array, 1, 3)); + } + + @Test void feedbackIdxDOp_byte() { + var array = new byte[]{5, -2, 10, 120}; + + assertEquals(133.0, PrimitiveArrays.feedbackIdxDOp(array, 0, 4, ( idx, value, prior ) -> prior + value)); + assertEquals(128.0, PrimitiveArrays.feedbackIdxDOp(array, 1, 3, ( idx, value, prior ) -> prior + value)); + } + + @Test void feedbackIdxDOp_short() { + var array = new short[]{5, -2, 10, 120}; + + assertEquals(133.0, PrimitiveArrays.feedbackIdxDOp(array, 0, 4, ( idx, value, prior ) -> prior + value)); + assertEquals(128.0, PrimitiveArrays.feedbackIdxDOp(array, 1, 3, ( idx, value, prior ) -> prior + value)); + } + + @Test void feedbackIdxDOp_int() { + var array = new int[]{5, -2, 10, 120}; + + assertEquals(133.0, PrimitiveArrays.feedbackIdxDOp(array, 0, 4, ( idx, value, prior ) -> prior + value)); + assertEquals(128.0, PrimitiveArrays.feedbackIdxDOp(array, 1, 3, ( idx, value, prior ) -> prior + value)); + } + + @Test void feedbackIdxDOp_long() { + var array = new long[]{5, -2, 10, 120}; + + assertEquals(133.0, PrimitiveArrays.feedbackIdxDOp(array, 0, 4, ( idx, value, prior ) -> prior + value)); + assertEquals(128.0, PrimitiveArrays.feedbackIdxDOp(array, 1, 3, ( idx, value, prior ) -> prior + value)); + } + + @Test void feedbackIdxDOp_float() { + var array = new float[]{5, -2, 10, 120}; + + assertEquals(133.0, PrimitiveArrays.feedbackIdxDOp(array, 0, 4, ( idx, value, prior ) -> prior + value)); + assertEquals(128.0, PrimitiveArrays.feedbackIdxDOp(array, 1, 3, ( idx, value, prior ) -> prior + value)); + } + + @Test void feedbackIdxDOp_double() { + var array = new double[]{5, -2, 10, 120}; + + assertEquals(133.0, PrimitiveArrays.feedbackIdxDOp(array, 0, 4, ( idx, value, prior ) -> prior + value)); + assertEquals(128.0, PrimitiveArrays.feedbackIdxDOp(array, 1, 3, ( idx, value, prior ) -> prior + value)); } } \ No newline at end of file