pax_global_header00006660000000000000000000000064136750565500014526gustar00rootroot0000000000000052 comment=a24b95f7154218af41249698a5bf6a647b730772 jheaps-jheaps-0.14/000077500000000000000000000000001367505655000141745ustar00rootroot00000000000000jheaps-jheaps-0.14/.gitignore000066400000000000000000000006101367505655000161610ustar00rootroot00000000000000*.class # Mobile Tools for Java (J2ME) .mtj.tmp/ # Package Files # *.jar *.war *.ear # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* # development stuff build/ doc/ release/ .settings .classpath .project target/ *~ # Jekyll stuff .sass-cache/ _site/ # release stuff release.properties pom.xml.releaseBackup # ide stuff .idea/ *.iml jheaps-jheaps-0.14/LICENSE000066400000000000000000000261351367505655000152100ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright {yyyy} {name of copyright owner} 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. jheaps-jheaps-0.14/README.md000066400000000000000000000047411367505655000154610ustar00rootroot00000000000000# JHeaps Library Copyright (C) 2014-2020 Dimitrios Michail 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. *** ## What is this library? This library contains various heap implementations written in Java. * It is easy to use * The data structures have a well defined interface * It is fast and well documented * The heaps are written in a similar way as in the JDK * It does not depend on other libraries, so classpathing 'jheaps.jar' is sufficient to use in your project. ## What is a heap? A heap is a priority queue data type which contains elements with keys (duplicate keys are permitted) from a totally-ordered universe. A min-oriented heap supports the following core operations: * MAKE-HEAP(): create an empty heap * INSERT(H,x): insert an element x into the heap * FIND-MIN(H): return an element with the smallest key * EXTRACT-MIN(H): remove the element with the smallest key * IS-EMPTY(H): is the heap empty? * SIZE(H): return the number of elements of the heap * CLEAR(H): remove all elements of the heap A heap does not support a search operation. A special type of heap called explicit or addressable resolves this issue by returning a handle when inserting a new element. This handle can later be used to additionally perform the following operations: * DECREASE-KEY(H,x,k): decrease the key of element x to k * DELETE(H,x): delete the element x from the heap Implicit heaps are represented using arrays. They are not addressable as the location of the elements in memory can change. However, they can be made to be addressable by using an additional layer of indirection. In this case we store handles inside the array. Each handle contains an additional integer property, designating the location in the array where the handle is stored. Some heaps are meldable, that is they efficiently support the union operation: * MELD(H1,H2): add all elements of H2 into H1 and destroy H2 As a general rule, heaps using an array representation are not meldable. ## Compatibility The library requires JDK v1.8 and above. jheaps-jheaps-0.14/etc/000077500000000000000000000000001367505655000147475ustar00rootroot00000000000000jheaps-jheaps-0.14/etc/jheaps_checks.xml000066400000000000000000000016631367505655000202710ustar00rootroot00000000000000 jheaps-jheaps-0.14/etc/jheaps_eclipse_formatter.xml000066400000000000000000001005611367505655000225350ustar00rootroot00000000000000 jheaps-jheaps-0.14/pom.xml000066400000000000000000000244041367505655000155150ustar00rootroot00000000000000 4.0.0 org.jheaps jheaps 0.14 jar JHeaps A free, production-ready, efficient Java library containing a collection of heap data-structures. http://www.jheaps.org Apache License, Version 2.0 http://www.apache.org/licenses/LICENSE-2.0.txt repo 1.8 4.13 8.33 UTF-8 3.8.1 3.2.0 3.2.0 3.0.0-M1 3.2.1 3.0.0-M1 3.1.1 3.0.0-M5 4.2.1 0.8.5 scm:git:git@github.com:d-michail/jheaps.git https://github.com/d-michail/jheaps.git scm:git:git@github.com:d-michail/jheaps.git jheaps-0.14 https://github.com/d-michail/jheaps/issues Dimitrios Michail dimitrios.michail@gmail.com Dimitrios Michail https://github.com/d-michail sonatype-nexus-snapshots Sonatype Nexus Snapshots https://oss.sonatype.org/content/repositories/snapshots false true ossrh https://oss.sonatype.org/content/repositories/snapshots/ ossrh https://oss.sonatype.org/service/local/staging/deploy/maven2/ junit junit ${junit.version} test org.apache.maven.plugins maven-compiler-plugin ${maven-compiler-plugin.version} ${java.version} ${java.version} org.apache.maven.plugins maven-surefire-plugin ${maven-surefire-plugin.version} org.apache.maven.plugins maven-release-plugin ${maven-release-plugin.version} forked-path release org.apache.maven.plugins maven-javadoc-plugin ${maven-javadoc-plugin.version} public JHeaps none 8 --no-module-directories org.apache.maven.plugins maven-source-plugin ${maven-source-plugin.version} org.apache.maven.plugins maven-deploy-plugin ${maven-deploy-plugin.version} default-deploy deploy deploy org.apache.felix maven-bundle-plugin ${maven-bundle-plugin.version} process-classes manifest org.jheaps Dimitrios Michail !org.jheaps.*,* org.apache.maven.plugins maven-jar-plugin ${maven-jar-plugin.version} logback.xml ${project.build.outputDirectory}/META-INF/MANIFEST.MF false org.jacoco jacoco-maven-plugin ${jacoco.version} default-prepare-agent prepare-agent default-report prepare-package report default-check check BUNDLE INSTRUCTION COVEREDRATIO 0.75 CLASS COVEREDRATIO 0.75 METHOD COVEREDRATIO 0.75 BRANCH COVEREDRATIO 0.75 COMPLEXITY COVEREDRATIO 0.75 LINE COVEREDRATIO 0.75 org.jacoco jacoco-maven-plugin report release performRelease true org.apache.maven.plugins maven-gpg-plugin 1.6 sign-artifacts verify sign org.apache.maven.plugins maven-javadoc-plugin ${maven-javadoc-plugin.version} attach-javadocs jar org.apache.maven.plugins maven-source-plugin attach-sources jar-no-fork checkstyle org.apache.maven.plugins maven-checkstyle-plugin ${maven-checkstyle-plugin.version} com.puppycrawl.tools checkstyle ${checkstyle.version} checkstyle validate check etc/jheaps_checks.xml false jheaps-jheaps-0.14/src/000077500000000000000000000000001367505655000147635ustar00rootroot00000000000000jheaps-jheaps-0.14/src/main/000077500000000000000000000000001367505655000157075ustar00rootroot00000000000000jheaps-jheaps-0.14/src/main/java/000077500000000000000000000000001367505655000166305ustar00rootroot00000000000000jheaps-jheaps-0.14/src/main/java/org/000077500000000000000000000000001367505655000174175ustar00rootroot00000000000000jheaps-jheaps-0.14/src/main/java/org/jheaps/000077500000000000000000000000001367505655000206715ustar00rootroot00000000000000jheaps-jheaps-0.14/src/main/java/org/jheaps/AddressableHeap.java000066400000000000000000000116121367505655000245440ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps; import java.util.Comparator; /** * A heap whose elements can be addressed using handles. * * An insert operation returns a {@link AddressableHeap.Handle} which can later * be used in order to manipulate the element, such as decreasing its key, or * deleting it. Storing the handle externally is the responsibility of the user. * * @param * the type of keys maintained by this heap * @param * the type of values maintained by this heap * * @author Dimitrios Michail */ public interface AddressableHeap { /** * A heap element handle. Allows someone to address an element already in a * heap and perform additional operations. * * @param * the type of keys maintained by this heap * @param * the type of values maintained by this heap */ interface Handle { /** * Return the key of the element. * * @return the key of the element */ K getKey(); /** * Return the value of the element. * * @return the value of the element */ V getValue(); /** * Set the value of the element. * * @param value * the new value */ void setValue(V value); /** * Decrease the key of the element. * * @param newKey * the new key * @throws IllegalArgumentException * if the new key is larger than the old key according to * the comparator used when constructing the heap or the * natural ordering of the elements if no comparator was * used */ void decreaseKey(K newKey); /** * Delete the element from the heap that it belongs. * * @throws IllegalArgumentException * in case this function is called twice on the same element * or the element has already been deleted using * {@link AddressableHeap#deleteMin()}. */ void delete(); } /** * Returns the comparator used to order the keys in this AddressableHeap, or * {@code null} if this heap uses the {@linkplain Comparable natural * ordering} of its keys. * * @return the comparator used to order the keys in this heap, or * {@code null} if this addressable heap uses the natural ordering * of its keys */ Comparator comparator(); /** * Insert a new element into the heap. * * @param key * the element's key * @param value * the element's value * * @return a handle for the newly added element */ AddressableHeap.Handle insert(K key, V value); /** * Insert a new element into the heap with a null value. * * @param key * the element's key * @return a handle for the newly added element */ AddressableHeap.Handle insert(K key); /** * Find an element with the minimum key. * * @return a handle to an element with minimum key */ AddressableHeap.Handle findMin(); /** * Delete and return an element with the minimum key. If multiple such * elements exists, only one of them will be deleted. After the element is * deleted the handle is invalidated and only method {@link Handle#getKey()} * and {@link Handle#getValue()} can be used. * * @return a handle to the deleted element with minimum key */ AddressableHeap.Handle deleteMin(); /** * Returns {@code true} if this heap is empty. * * @return {@code true} if this heap is empty, {@code false} otherwise */ boolean isEmpty(); /** * Returns the number of elements in the heap. * * @return the number of elements in the heap */ long size(); /** * Clear all the elements of the heap. After calling this method all handles * should be considered invalidated and the behavior of methods * {@link Handle#decreaseKey(Object)} and {@link Handle#delete()} is * undefined. */ void clear(); } jheaps-jheaps-0.14/src/main/java/org/jheaps/AddressableHeapFactory.java000066400000000000000000000024471367505655000261020ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps; import java.util.Comparator; /** * An addressable heap factory. * * @author Dimitrios Michail * * @param * the type of keys maintained by the heap * @param * the type of values maintained by the heap */ public interface AddressableHeapFactory { /** * Get a new heap. * * @param comparator * the comparator that will be used to order this heap. If * {@code null}, the {@linkplain Comparable natural ordering} of * the keys will be used. * * @return a new heap */ AddressableHeap get(Comparator comparator); } jheaps-jheaps-0.14/src/main/java/org/jheaps/Constants.java000066400000000000000000000030621367505655000235110ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps; /** * Global library configuration flags which affect generated code at compile * time. * * @author Dimitrios Michail */ public class Constants { /** * Library name */ public static final String NAME = "JHeaps"; /** * Global debug flag which affects compiled code */ public static final boolean DEBUG = false; /** * Global level one debug flag which affects compiled code. */ public static final boolean DEBUG_LEVEL1 = false; /** * Global level two debug flag which affects compiled code */ public static final boolean DEBUG_LEVEL2 = false; /** * Global benchmarking flag. This flag enables sanity checks when the code * is not for benchmarking performance. When benchmarking we assume that the * user provided the correct input. */ public static final boolean NOT_BENCHMARK = true; private Constants() { } } jheaps-jheaps-0.14/src/main/java/org/jheaps/DoubleEndedAddressableHeap.java000066400000000000000000000073011367505655000266370ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps; import java.util.NoSuchElementException; /** * A double-ended heap whose elements can be addressed using handles. * * An insert operation returns a {@link AddressableHeap.Handle} which can later * be used in order to manipulate the element, such as decreasing its key, * increasing its key, or deleting it. Storing the handle externally is the * responsibility of the user. * * @param * the type of keys maintained by this heap * @param * the type of values maintained by this heap * * @author Dimitrios Michail */ public interface DoubleEndedAddressableHeap extends AddressableHeap { /** * Insert a new element into the heap. * * @param key * the element's key * @param value * the element's value * * @return a handle for the newly added element */ Handle insert(K key, V value); /** * Insert a new element into the heap with a null value. * * @param key * the element's key * @return a handle for the newly added element */ Handle insert(K key); /** * Find an element with the minimum key. * * @return a handle to an element with minimum key */ Handle findMin(); /** * Delete and return an element with the minimum key. If multiple such * elements exists, only one of them will be deleted. After the element is * deleted the handle is invalidated and only method {@link Handle#getKey()} * and {@link Handle#getValue()} can be used. * * @return a handle to the deleted element with minimum key */ Handle deleteMin(); /** * Find an element with the maximum key. * * @return an element with the maximum key * @throws NoSuchElementException * if the heap is empty */ Handle findMax(); /** * Delete and return an element with the maximum key. If multiple such * elements exists, only one of them will be deleted. * * @return the deleted element with the maximum key * @throws NoSuchElementException * if the heap is empty */ Handle deleteMax(); /** * A double-ended heap element handle. Allows someone to address an element * already in a heap and perform additional operations. * * @param * the type of keys maintained by this heap * @param * the type of values maintained by this heap */ interface Handle extends AddressableHeap.Handle { /** * Increase the key of the element. * * @param newKey * the new key * @throws IllegalArgumentException * if the new key is smaller than the old key according to * the comparator used when constructing the heap or the * natural ordering of the elements if no comparator was * used */ void increaseKey(K newKey); } } jheaps-jheaps-0.14/src/main/java/org/jheaps/DoubleEndedHeap.java000066400000000000000000000026051367505655000245070ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps; import java.util.NoSuchElementException; /** * A double-ended heap. * * @param * the type of keys maintained by this heap * * @author Dimitrios Michail */ public interface DoubleEndedHeap extends Heap { /** * Find an element with the maximum key. * * @return an element with the maximum key * @throws NoSuchElementException * if the heap is empty */ K findMax(); /** * Delete and return an element with the maximum key. If multiple such * elements exists, only one of them will be deleted. * * @return the deleted element with the maximum key * @throws NoSuchElementException * if the heap is empty */ K deleteMax(); } jheaps-jheaps-0.14/src/main/java/org/jheaps/DoubleEndedValueHeap.java000066400000000000000000000032271367505655000255050ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps; import java.util.NoSuchElementException; /** * A double-ended heap with values. * * @param * the type of keys maintained by this heap * @param * the type of values maintained by this heap * * @author Dimitrios Michail */ public interface DoubleEndedValueHeap extends DoubleEndedHeap { /** * Insert an element into the heap. * * @param key * the key to insert * @param value * the value to insert */ void insert(K key, V value); /** * Find the value of an element with the minimum key. * * @return the value of an element with the minimum key * @throws NoSuchElementException * if the heap is empty */ V findMinValue(); /** * Find the value of an element with the maximum key. * * @return the value of an element with the maximum key * @throws NoSuchElementException * if the heap is empty */ V findMaxValue(); } jheaps-jheaps-0.14/src/main/java/org/jheaps/Heap.java000066400000000000000000000044211367505655000224120ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps; import java.util.Comparator; import java.util.NoSuchElementException; /** * A heap. * * @param * the type of keys maintained by this heap * * @author Dimitrios Michail */ public interface Heap { /** * Returns the comparator used to order the keys in this heap, or * {@code null} if this heap uses the {@linkplain Comparable natural * ordering} of its keys. * * @return the comparator used to order the keys in this heap, or * {@code null} if this heap uses the natural ordering of its keys */ Comparator comparator(); /** * Insert a key into the heap. * * @param key * the key to insert */ void insert(K key); /** * Find an element with the minimum key. * * @return an element with the minimum key * @throws NoSuchElementException * if the heap is empty */ K findMin(); /** * Delete and return an element with the minimum key. If multiple such * elements exists, only one of them will be deleted. * * @return the deleted element with the minimum key * @throws NoSuchElementException * if the heap is empty */ K deleteMin(); /** * Returns {@code true} if this heap is empty. * * @return {@code true} if this heap is empty, {@code false} otherwise */ boolean isEmpty(); /** * Returns the number of elements in this heap. * * @return the number of elements in this heap */ long size(); /** * Clear all the elements of this heap. */ void clear(); } jheaps-jheaps-0.14/src/main/java/org/jheaps/MergeableAddressableHeap.java000066400000000000000000000045351367505655000263560ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps; /** * An addressable heap that allows melding with another addressable heap. * *

* The second heap becomes empty and unusable after the meld operation, meaning * than further insertions are not possible and will throw an * {@link IllegalStateException}. * *

* A {@link ClassCastException} will be thrown if the two heaps are not of the * same type. Moreover, the two heaps need to use the same comparators. If only * one of them uses a custom comparator or both use custom comparators but are * not the same by equals, an {@code IllegalArgumentException} is * thrown. * *

* Note that all running time bounds on mergeable heaps are valid assuming that * the user does not perform cascading melds on heaps such as: * *

 * d.meld(e);
 * c.meld(d);
 * b.meld(c);
 * a.meld(b);
 * 
* * The above scenario, although efficiently supported by using union-find with * path compression, invalidates the claimed bounds. * * @param * the type of keys maintained by this heap * @param * the type of values maintained by this heap * * @author Dimitrios Michail */ public interface MergeableAddressableHeap extends AddressableHeap { /** * Meld a heap into the current heap. * * After the operation the {@code other} heap will be empty and will not * permit further insertions. * * @param other * a merge-able heap * @throws ClassCastException * if {@code other} is not compatible with this heap * @throws IllegalArgumentException * if {@code other} does not have a compatible comparator */ void meld(MergeableAddressableHeap other); } jheaps-jheaps-0.14/src/main/java/org/jheaps/MergeableDoubleEndedAddressableHeap.java000066400000000000000000000046321367505655000304470ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps; /** * A double-ended addressable heap that allows melding with another double-ended * addressable heap. * *

* The second heap becomes empty and unusable after the meld operation, meaning * than further insertions are not possible and will throw an * {@link IllegalStateException}. * *

* A {@link ClassCastException} will be thrown if the two heaps are not of the * same type. Moreover, the two heaps need to use the same comparators. If only * one of them uses a custom comparator or both use custom comparators but are * not the same by equals, an {@code IllegalArgumentException} is * thrown. * *

* Note that all running time bounds on mergeable heaps are valid assuming that * the user does not perform cascading melds on heaps such as: * *

 * d.meld(e);
 * c.meld(d);
 * b.meld(c);
 * a.meld(b);
 * 
* * The above scenario, although efficiently supported by using union-find with * path compression, invalidates the claimed bounds. * * @param * the type of keys maintained by this heap * @param * the type of values maintained by this heap * * @author Dimitrios Michail */ public interface MergeableDoubleEndedAddressableHeap extends DoubleEndedAddressableHeap { /** * Meld a heap into the current heap. * * After the operation the {@code other} heap will be empty and will not * permit further insertions. * * @param other * a merge-able heap * @throws ClassCastException * if {@code other} is not compatible with this heap * @throws IllegalArgumentException * if {@code other} does not have a compatible comparator */ void meld(MergeableDoubleEndedAddressableHeap other); } jheaps-jheaps-0.14/src/main/java/org/jheaps/MergeableHeap.java000066400000000000000000000043231367505655000242170ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps; /** * A heap that allows melding with another heap. * *

* The second heap becomes empty and unusable after the meld operation, meaning * than further insertions are not possible and will throw an * {@link IllegalStateException}. * *

* A {@link ClassCastException} will be thrown if the two heaps are not of the * same type. Moreover, the two heaps need to use the same comparators. If only * one of them uses a custom comparator or both use custom comparators but are * not the same by equals, an {@code IllegalArgumentException} is * thrown. * *

* Note that all running time bounds on mergeable heaps are valid assuming that * the user does not perform cascading melds on heaps such as: * *

 * d.meld(e);
 * c.meld(d);
 * b.meld(c);
 * a.meld(b);
 * 
* * The above scenario, although efficiently supported by using union-find with * path compression, invalidates the claimed bounds. * * @param * the type of keys maintained by this heap * * @author Dimitrios Michail */ public interface MergeableHeap extends Heap { /** * Meld a heap into the current heap. * * After the operation the {@code other} heap will be empty and will not * permit further insertions. * * @param other * a merge-able heap * @throws ClassCastException * if {@code other} is not compatible with this heap * @throws IllegalArgumentException * if {@code other} does not have a compatible comparator */ void meld(MergeableHeap other); } jheaps-jheaps-0.14/src/main/java/org/jheaps/ValueHeap.java000066400000000000000000000026021367505655000234060ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps; import java.util.NoSuchElementException; /** * A heap with values. * * @param * the type of keys maintained by this heap * @param * the type of values maintained by this heap * * @author Dimitrios Michail */ public interface ValueHeap extends Heap { /** * Insert an element into the heap. * * @param key * the key to insert * @param value * the value to insert */ void insert(K key, V value); /** * Find the value of an element with the minimum key. * * @return the value of an element with the minimum key * @throws NoSuchElementException * if the heap is empty */ V findMinValue(); } jheaps-jheaps-0.14/src/main/java/org/jheaps/annotations/000077500000000000000000000000001367505655000232265ustar00rootroot00000000000000jheaps-jheaps-0.14/src/main/java/org/jheaps/annotations/Beta.java000066400000000000000000000022151367505655000247440ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.annotations; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Marker interface to indicate that a public API may change or be removed in * the future. * * @author Dimitrios Michail */ @Retention(RetentionPolicy.CLASS) @Target(value = { ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.TYPE, ElementType.ANNOTATION_TYPE }) public @interface Beta { } jheaps-jheaps-0.14/src/main/java/org/jheaps/annotations/ConstantTime.java000066400000000000000000000025611367505655000265050ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.annotations; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Marker interface to indicate that an operation takes constant time. The * primary purpose of this interface is to allow generic algorithms to alter * their behavior to provide good performance. * * @author Dimitrios Michail */ @Retention(RetentionPolicy.RUNTIME) @Target(value = { ElementType.METHOD, ElementType.CONSTRUCTOR }) public @interface ConstantTime { /** * Whether the running time is amortized or actual. * * @return {@code true} if amortized, {@code false} if actual */ boolean amortized() default false; } jheaps-jheaps-0.14/src/main/java/org/jheaps/annotations/LinearTime.java000066400000000000000000000025471367505655000261320ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.annotations; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Marker interface to indicate that an operation takes O(n) time where n is the * size of the input. The primary purpose of this interface is to allow generic * algorithms to alter their behavior to provide good performance. * * @author Dimitrios Michail */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface LinearTime { /** * Whether the running time is amortized or actual. * * @return {@code true} if amortized, {@code false} if actual */ boolean amortized() default false; } jheaps-jheaps-0.14/src/main/java/org/jheaps/annotations/LogLogTime.java000066400000000000000000000025571367505655000261040ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.annotations; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Marker interface to indicate that an operation takes O(loglog(n)) time where * n is the size of the input. The primary purpose of this interface is to allow * generic algorithms to alter their behavior to provide good performance. * * @author Dimitrios Michail */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface LogLogTime { /** * Whether the running time is amortized or actual. * * @return {@code true} if amortized, {@code false} if actual */ boolean amortized() default false; } jheaps-jheaps-0.14/src/main/java/org/jheaps/annotations/LogarithmicTime.java000066400000000000000000000025611367505655000271560ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.annotations; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Marker interface to indicate that an operation takes O(log(n)) time where n * is the size of the input. The primary purpose of this interface is to allow * generic algorithms to alter their behavior to provide good performance. * * @author Dimitrios Michail */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface LogarithmicTime { /** * Whether the running time is amortized or actual. * * @return {@code true} if amortized, {@code false} if actual */ boolean amortized() default false; } jheaps-jheaps-0.14/src/main/java/org/jheaps/annotations/VisibleForTesting.java000066400000000000000000000022511367505655000274730ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.annotations; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Marker interface to indicate that something is visible as package-private * only for testing purposes. * * @author Dimitrios Michail */ @Retention(RetentionPolicy.SOURCE) @Target(value = { ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.TYPE, ElementType.ANNOTATION_TYPE }) public @interface VisibleForTesting { } jheaps-jheaps-0.14/src/main/java/org/jheaps/annotations/package-info.java000066400000000000000000000012711367505655000264160ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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. */ /** * Annotation types */ package org.jheaps.annotations; jheaps-jheaps-0.14/src/main/java/org/jheaps/array/000077500000000000000000000000001367505655000220075ustar00rootroot00000000000000jheaps-jheaps-0.14/src/main/java/org/jheaps/array/AbstractArrayAddressableHeap.java000066400000000000000000000171541367505655000303540ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.array; import java.io.Serializable; import java.lang.reflect.Array; import java.util.Comparator; import java.util.NoSuchElementException; import org.jheaps.AddressableHeap; import org.jheaps.Constants; import org.jheaps.annotations.ConstantTime; import org.jheaps.annotations.LogarithmicTime; /** * Abstract implementation of a heap using an array representation. * * @author Dimitrios Michail * * @param * the type of keys maintained by this heap */ abstract class AbstractArrayAddressableHeap implements AddressableHeap, Serializable { private static final long serialVersionUID = 1L; /** * Denotes that a handle is not in the array */ protected static final int NO_INDEX = -1; /** * The maximum heap capacity. */ protected static final int MAX_HEAP_CAPACITY = Integer.MAX_VALUE - 8 - 1; /** * The minimum heap capacity. */ protected static final int MIN_HEAP_CAPACITY = 0; /** * Limit for the heap capacity when down-sizing. */ protected static final int DOWNSIZING_MIN_HEAP_CAPACITY = 16; /** * The comparator used to maintain order in this heap, or null if it uses * the natural ordering of its keys. * * @serial */ protected Comparator comparator; /** * The array use for representing the tree. */ protected ArrayHandle[] array; /** * Number of elements in the heap. */ protected int size; /** * Minimum capacity due to initially requested capacity. */ protected final int minCapacity; /** * {@inheritDoc} */ @SuppressWarnings("unchecked") public AbstractArrayAddressableHeap(Comparator comparator, int capacity) { checkCapacity(capacity); this.size = 0; this.comparator = comparator; this.minCapacity = Math.max(capacity, DOWNSIZING_MIN_HEAP_CAPACITY); this.array = (ArrayHandle[]) Array.newInstance(ArrayHandle.class, minCapacity + 1); } /** * {@inheritDoc} */ @Override @ConstantTime public Handle findMin() { if (Constants.NOT_BENCHMARK && size == 0) { throw new NoSuchElementException(); } return array[1]; } /** * {@inheritDoc} */ @Override @ConstantTime public boolean isEmpty() { return size == 0; } /** * {@inheritDoc} */ @Override @ConstantTime public long size() { return size; } /** * {@inheritDoc} */ @Override public Comparator comparator() { return comparator; } /** * {@inheritDoc} */ @Override @ConstantTime public void clear() { size = 0; } /** * {@inheritDoc} */ @Override @LogarithmicTime(amortized = true) public Handle insert(K key) { return insert(key, null); } /** * {@inheritDoc} */ @Override @LogarithmicTime(amortized = true) public Handle insert(K key, V value) { if (Constants.NOT_BENCHMARK) { if (key == null) { throw new NullPointerException("Null keys not permitted"); } // make sure there is space if (size == array.length - 1) { if (array.length == 1) { ensureCapacity(1); } else { ensureCapacity(2 * (array.length - 1)); } } } ArrayHandle p = new ArrayHandle(key, value); size++; array[size] = p; p.index = size; if (comparator == null) { fixup(size); } else { fixupWithComparator(size); } return p; } /** * {@inheritDoc} */ @Override @LogarithmicTime(amortized = true) public Handle deleteMin() { if (Constants.NOT_BENCHMARK && size == 0) { throw new NoSuchElementException(); } ArrayHandle result = array[1]; result.index = NO_INDEX; if (size == 1) { array[1] = null; size = 0; } else { array[1] = array[size--]; if (comparator == null) { fixdown(1); } else { fixdownWithComparator(1); } } if (Constants.NOT_BENCHMARK) { if (2 * minCapacity < array.length - 1 && 4 * size < array.length - 1) { ensureCapacity((array.length - 1) / 2); } } return result; } protected final void checkCapacity(int capacity) { if (capacity < MIN_HEAP_CAPACITY) { throw new IllegalArgumentException("Heap capacity must be >= " + MIN_HEAP_CAPACITY); } if (capacity > MAX_HEAP_CAPACITY) { throw new IllegalArgumentException("Heap capacity too large"); } } protected abstract void ensureCapacity(int capacity); protected abstract void forceFixup(int k); protected abstract void fixup(int k); protected abstract void fixupWithComparator(int k); protected abstract void fixdown(int k); protected abstract void fixdownWithComparator(int k); // handle protected class ArrayHandle implements AddressableHeap.Handle, Serializable { private final static long serialVersionUID = 1; K key; V value; int index; ArrayHandle(K key, V value) { this.key = key; this.value = value; this.index = NO_INDEX; } @Override public K getKey() { return key; } @Override public V getValue() { return value; } @Override public void setValue(V value) { this.value = value; } @Override @SuppressWarnings("unchecked") @LogarithmicTime public void decreaseKey(K newKey) { if (index == NO_INDEX) { throw new IllegalArgumentException("Invalid handle!"); } int c; if (comparator == null) { c = ((Comparable) newKey).compareTo(key); } else { c = comparator.compare(newKey, key); } if (c > 0) { throw new IllegalArgumentException("Keys can only be decreased!"); } key = newKey; if (c == 0 || index == 1) { return; } if (comparator == null) { fixup(index); } else { fixupWithComparator(index); } } @Override public void delete() { if (index == NO_INDEX) { throw new IllegalArgumentException("Invalid handle!"); } if (index == 1) { deleteMin(); return; } forceFixup(index); deleteMin(); } } } jheaps-jheaps-0.14/src/main/java/org/jheaps/array/AbstractArrayHeap.java000066400000000000000000000067511367505655000262230ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.array; import java.util.Comparator; import java.util.NoSuchElementException; import org.jheaps.Constants; import org.jheaps.annotations.ConstantTime; import org.jheaps.annotations.LogarithmicTime; /** * Abstract implementation of a heap using an array representation. * * @author Dimitrios Michail * * @param * the type of keys maintained by this heap */ abstract class AbstractArrayHeap extends AbstractArrayWeakHeap { private static final long serialVersionUID = 1L; /** * Construct a new heap * * @param comparator * the comparator to use * @param capacity * the initial capacity */ public AbstractArrayHeap(Comparator comparator, int capacity) { super(comparator, capacity); } /** * Initialize the array representation */ @Override @SuppressWarnings("unchecked") protected void initCapacity(int capacity) { this.array = (K[]) new Object[capacity + 1]; } /** * {@inheritDoc} */ @Override @ConstantTime public K findMin() { if (Constants.NOT_BENCHMARK && size == 0) { throw new NoSuchElementException(); } return array[1]; } /** * {@inheritDoc} */ @Override @LogarithmicTime(amortized = true) public void insert(K key) { if (Constants.NOT_BENCHMARK) { if (key == null) { throw new NullPointerException("Null keys not permitted"); } // make sure there is space if (size == array.length - 1) { if (array.length == 1) { ensureCapacity(1); } else { ensureCapacity(2 * (array.length - 1)); } } } array[++size] = key; if (comparator == null) { fixup(size); } else { fixupWithComparator(size); } } /** * {@inheritDoc} */ @Override @LogarithmicTime(amortized = true) public K deleteMin() { if (Constants.NOT_BENCHMARK && size == 0) { throw new NoSuchElementException(); } K result = array[1]; if (size == 1) { array[1] = null; size = 0; } else { array[1] = array[size]; array[size] = null; size--; if (comparator == null) { fixdown(1); } else { fixdownWithComparator(1); } } if (Constants.NOT_BENCHMARK) { // free unused space int currentCapacity = array.length - 1; if (2 * minCapacity <= currentCapacity && 4 * size < currentCapacity) { ensureCapacity(currentCapacity / 2); } } return result; } } jheaps-jheaps-0.14/src/main/java/org/jheaps/array/AbstractArrayWeakHeap.java000066400000000000000000000101431367505655000270210ustar00rootroot00000000000000package org.jheaps.array; import java.io.Serializable; import java.util.Comparator; import org.jheaps.Heap; import org.jheaps.annotations.ConstantTime; /** * An abstract weak heap with an array representation. * * @author Dimitrios Michail * * @param * the type of keys maintained by this heap */ abstract class AbstractArrayWeakHeap implements Heap, Serializable { private static final long serialVersionUID = 1L; /** * The maximum heap capacity. */ protected static final int MAX_HEAP_CAPACITY = Integer.MAX_VALUE - 8 - 1; /** * The minimum heap capacity. */ protected static final int MIN_HEAP_CAPACITY = 0; /** * Limit for the heap capacity when down-sizing. */ protected static final int DOWNSIZING_MIN_HEAP_CAPACITY = 16; /** * The comparator used to maintain order in this heap, or null if it uses * the natural ordering of its keys. * * @serial */ protected final Comparator comparator; /** * The array used for representing the heap. */ protected K[] array; /** * Number of elements in the heap. */ protected int size; /** * Minimum capacity due to initially requested capacity. */ protected final int minCapacity; /** * Constructor * * @param comparator * the comparator to use * @param capacity * the requested capacity */ public AbstractArrayWeakHeap(Comparator comparator, int capacity) { checkCapacity(capacity); this.size = 0; this.comparator = comparator; this.minCapacity = Math.max(capacity, DOWNSIZING_MIN_HEAP_CAPACITY); initCapacity(minCapacity); } /** * {@inheritDoc} */ @Override @ConstantTime public boolean isEmpty() { return size == 0; } /** * {@inheritDoc} */ @Override @ConstantTime public long size() { return size; } /** * {@inheritDoc} */ @Override public Comparator comparator() { return comparator; } /** * {@inheritDoc} */ @Override @ConstantTime public void clear() { size = 0; } /** * Check that a capacity is valid. * * @param capacity * the capacity * * @throws IllegalArgumentException * if the capacity is negative or more than the maximum array * size */ protected final void checkCapacity(int capacity) { if (capacity < MIN_HEAP_CAPACITY) { throw new IllegalArgumentException("Heap capacity must be >= " + MIN_HEAP_CAPACITY); } if (capacity > MAX_HEAP_CAPACITY) { throw new IllegalArgumentException("Heap capacity too large"); } } /** * Initialize array representation. * * @param capacity * the capacity */ protected abstract void initCapacity(int capacity); /** * Make sure the array representation can hold a certain number of elements. * * @param capacity * the capacity */ protected abstract void ensureCapacity(int capacity); /** * Upwards fix starting from a particular element * * @param k * the index of the starting element */ protected abstract void fixup(int k); /** * Upwards fix starting from a particular element. Performs comparisons * using the comparator. * * @param k * the index of the starting element */ protected abstract void fixupWithComparator(int k); /** * Downwards fix starting from a particular element * * @param k * the index of the starting element */ protected abstract void fixdown(int k); /** * Downwards fix starting from a particular element. Performs comparisons * using the comparator. * * @param k * the index of the starting element */ protected abstract void fixdownWithComparator(int k); } jheaps-jheaps-0.14/src/main/java/org/jheaps/array/BinaryArrayAddressableHeap.java000066400000000000000000000354041367505655000300330ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.array; import java.io.Serializable; import java.lang.reflect.Array; import java.util.Comparator; import java.util.Iterator; import java.util.NoSuchElementException; import org.jheaps.AddressableHeap; import org.jheaps.annotations.LinearTime; /** * An array based binary addressable heap. The heap is sorted according to the * {@linkplain Comparable natural ordering} of its keys, or by a * {@link Comparator} provided at heap creation time, depending on which * constructor is used. * *

* The implementation uses an array in order to store the elements and * automatically maintains the size of the array much like a * {@link java.util.Vector} does, providing amortized O(log(n)) time cost for * the {@code insert} and {@code deleteMin} operations. Operation * {@code findMin}, is a worst-case O(1) operation. Operations {@code delete} * and {@code decreaseKey} take worst-case O(log(n)) time. The bounds are * worst-case if the user initializes the heap with a capacity larger or equal * to the total number of elements that are going to be inserted into the heap. * *

* Constructing such a heap from an array of elements can be performed using the * method {@link #heapify(Object[], Object[])} or * {@link #heapify(Object[], Object[], Comparator)} in linear time. * *

* Note that the ordering maintained by a binary heap, like any heap, and * whether or not an explicit comparator is provided, must be consistent * with {@code equals} if this heap is to correctly implement the * {@code Heap} interface. (See {@code Comparable} or {@code Comparator} for a * precise definition of consistent with equals.) This is so because * the {@code Heap} interface is defined in terms of the {@code equals} * operation, but a binary heap performs all key comparisons using its * {@code compareTo} (or {@code compare}) method, so two keys that are deemed * equal by this method are, from the standpoint of the binary heap, equal. The * behavior of a heap is well-defined even if its ordering is * inconsistent with {@code equals}; it just fails to obey the general contract * of the {@code Heap} interface. * *

* Note that this implementation is not synchronized. If * multiple threads access a heap concurrently, and at least one of the threads * modifies the heap structurally, it must be synchronized externally. * (A structural modification is any operation that adds or deletes one or more * elements or changing the key of some element.) This is typically accomplished * by synchronizing on some object that naturally encapsulates the heap. * * @param * the type of keys maintained by this heap * @param * the type of values maintained by this heap * * @author Dimitrios Michail */ public class BinaryArrayAddressableHeap extends AbstractArrayAddressableHeap implements Serializable { private final static long serialVersionUID = 1; /** * Default initial capacity of the binary heap. */ public static final int DEFAULT_HEAP_CAPACITY = 16; /** * Constructs a new, empty heap, using the natural ordering of its keys. * *

* All keys inserted into the heap must implement the {@link Comparable} * interface. Furthermore, all such keys must be mutually * comparable: {@code k1.compareTo(k2)} must not throw a * {@code ClassCastException} for any keys {@code k1} and {@code k2} in the * heap. If the user attempts to put a key into the heap that violates this * constraint (for example, the user attempts to put a string key into a * heap whose keys are integers), the {@code insert(Object key)} call will * throw a {@code ClassCastException}. * *

* The initial capacity of the heap is {@link #DEFAULT_HEAP_CAPACITY} and * adjusts automatically based on the sequence of insertions and deletions. */ public BinaryArrayAddressableHeap() { this(null, DEFAULT_HEAP_CAPACITY); } /** * Constructs a new, empty heap, with a provided initial capacity using the * natural ordering of its keys. * *

* All keys inserted into the heap must implement the {@link Comparable} * interface. Furthermore, all such keys must be mutually * comparable: {@code k1.compareTo(k2)} must not throw a * {@code ClassCastException} for any keys {@code k1} and {@code k2} in the * heap. If the user attempts to put a key into the heap that violates this * constraint (for example, the user attempts to put a string key into a * heap whose keys are integers), the {@code insert(Object key)} call will * throw a {@code ClassCastException}. * *

* The initial capacity of the heap is provided by the user and is adjusted * automatically based on the sequence of insertions and deletions. The * capacity will never become smaller than the initial requested capacity. * * @param capacity * the initial heap capacity */ public BinaryArrayAddressableHeap(int capacity) { this(null, capacity); } /** * Constructs a new, empty heap, ordered according to the given comparator. * *

* All keys inserted into the heap must be mutually comparable by * the given comparator: {@code comparator.compare(k1, * k2)} must not throw a {@code ClassCastException} for any keys {@code k1} * and {@code k2} in the heap. If the user attempts to put a key into the * heap that violates this constraint, the {@code insert(Object key)} call * will throw a {@code ClassCastException}. * *

* The initial capacity of the heap is {@link #DEFAULT_HEAP_CAPACITY} and * adjusts automatically based on the sequence of insertions and deletions. * * @param comparator * the comparator that will be used to order this heap. If * {@code null}, the {@linkplain Comparable natural ordering} of * the keys will be used. */ public BinaryArrayAddressableHeap(Comparator comparator) { this(comparator, DEFAULT_HEAP_CAPACITY); } /** * Constructs a new, empty heap, with a provided initial capacity ordered * according to the given comparator. * *

* All keys inserted into the heap must be mutually comparable by * the given comparator: {@code comparator.compare(k1, * k2)} must not throw a {@code ClassCastException} for any keys {@code k1} * and {@code k2} in the heap. If the user attempts to put a key into the * heap that violates this constraint, the {@code insert(Object key)} call * will throw a {@code ClassCastException}. * *

* The initial capacity of the heap is provided by the user and is adjusted * automatically based on the sequence of insertions and deletions. The * capacity will never become smaller than the initial requested capacity. * * @param comparator * the comparator that will be used to order this heap. If * {@code null}, the {@linkplain Comparable natural ordering} of * the keys will be used. * @param capacity * the initial heap capacity */ public BinaryArrayAddressableHeap(Comparator comparator, int capacity) { super(comparator, capacity); } /** * Create a heap from an array of elements. The elements of the array are * not destroyed. The method has linear time complexity. * * @param * the type of keys maintained by the heap * @param * the type of values maintained by the heap * @param keys * an array of keys * @param values * an array of values * @return a binary heap * @throws IllegalArgumentException * in case the array is null */ @LinearTime public static BinaryArrayAddressableHeap heapify(K[] keys, V[] values) { if (keys == null) { throw new IllegalArgumentException("Key array cannot be null"); } if (values != null && keys.length != values.length) { throw new IllegalArgumentException("Values array must have the same length as the keys array"); } if (keys.length == 0) { return new BinaryArrayAddressableHeap(); } BinaryArrayAddressableHeap h = new BinaryArrayAddressableHeap(keys.length); for (int i = 0; i < keys.length; i++) { K key = keys[i]; V value = (values == null) ? null : values[i]; AbstractArrayAddressableHeap.ArrayHandle ah = h.new ArrayHandle(key, value); ah.index = i + 1; h.array[i + 1] = ah; } h.size = keys.length; for (int i = keys.length / 2; i > 0; i--) { h.fixdown(i); } return h; } /** * Create a heap from an array of elements. The elements of the array are * not destroyed. The method has linear time complexity. * * @param * the type of keys maintained by the heap * @param * the type of values maintained by the heap * @param keys * an array of keys * @param values * an array of values * @param comparator * the comparator to use * @return a binary heap * @throws IllegalArgumentException * in case the array is null */ @LinearTime public static BinaryArrayAddressableHeap heapify(K[] keys, V[] values, Comparator comparator) { if (keys == null) { throw new IllegalArgumentException("Keys array cannot be null"); } if (values != null && keys.length != values.length) { throw new IllegalArgumentException("Values array must have the same length as the keys array"); } if (keys.length == 0) { return new BinaryArrayAddressableHeap(comparator); } BinaryArrayAddressableHeap h = new BinaryArrayAddressableHeap(comparator, keys.length); for (int i = 0; i < keys.length; i++) { K key = keys[i]; V value = (values == null) ? null : values[i]; AbstractArrayAddressableHeap.ArrayHandle ah = h.new ArrayHandle(key, value); ah.index = i + 1; h.array[i + 1] = ah; } h.size = keys.length; for (int i = keys.length / 2; i > 0; i--) { h.fixdownWithComparator(i); } return h; } /** * Get an iterator for all handles currently in the heap. * * This method is especially useful when building a heap using the heapify * method. Unspecified behavior will occur if the heap is modified while * using this iterator. * * @return an iterator which will return all handles of the heap */ public Iterator> handlesIterator() { return new Iterator>() { private int pos = 1; @Override public boolean hasNext() { return pos <= size; } @Override public AddressableHeap.Handle next() { if (pos > size) { throw new NoSuchElementException(); } return array[pos++]; } @Override public void remove() { throw new UnsupportedOperationException(); } }; } /** * Ensure that the array representation has the necessary capacity. * * @param capacity * the requested capacity */ @Override @SuppressWarnings("unchecked") protected void ensureCapacity(int capacity) { checkCapacity(capacity); ArrayHandle[] newArray = (ArrayHandle[]) Array.newInstance(ArrayHandle.class, capacity + 1); System.arraycopy(array, 1, newArray, 1, size); array = newArray; } @Override protected void forceFixup(int k) { ArrayHandle h = array[k]; while (k > 1) { array[k] = array[k / 2]; array[k].index = k; k /= 2; } array[k] = h; h.index = k; } @Override @SuppressWarnings("unchecked") protected void fixup(int k) { ArrayHandle h = array[k]; while (k > 1 && ((Comparable) array[k / 2].getKey()).compareTo(h.getKey()) > 0) { array[k] = array[k / 2]; array[k].index = k; k /= 2; } array[k] = h; h.index = k; } @Override protected void fixupWithComparator(int k) { ArrayHandle h = array[k]; while (k > 1 && comparator.compare(array[k / 2].getKey(), h.getKey()) > 0) { array[k] = array[k / 2]; array[k].index = k; k /= 2; } array[k] = h; h.index = k; } @Override @SuppressWarnings("unchecked") protected void fixdown(int k) { ArrayHandle h = array[k]; while (2 * k <= size) { int j = 2 * k; if (j < size && ((Comparable) array[j].getKey()).compareTo(array[j + 1].getKey()) > 0) { j++; } if (((Comparable) h.getKey()).compareTo(array[j].getKey()) <= 0) { break; } array[k] = array[j]; array[k].index = k; k = j; } array[k] = h; h.index = k; } @Override protected void fixdownWithComparator(int k) { ArrayHandle h = array[k]; while (2 * k <= size) { int j = 2 * k; if (j < size && comparator.compare(array[j].getKey(), array[j + 1].getKey()) > 0) { j++; } if (comparator.compare(h.getKey(), array[j].getKey()) <= 0) { break; } array[k] = array[j]; array[k].index = k; k = j; } array[k] = h; h.index = k; } } jheaps-jheaps-0.14/src/main/java/org/jheaps/array/BinaryArrayBulkInsertWeakHeap.java000066400000000000000000000442311367505655000305120ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.array; import java.io.Serializable; import java.util.Comparator; import java.util.NoSuchElementException; import org.jheaps.annotations.ConstantTime; import org.jheaps.annotations.LinearTime; import org.jheaps.annotations.LogarithmicTime; /** * An array based binary weak heap using bulk insertion. The heap is sorted * according to the {@linkplain Comparable natural ordering} of its keys, or by * a {@link Comparator} provided at heap creation time, depending on which * constructor is used. * *

* The implementation uses an array in order to store the elements and * automatically maintains the size of the array much like a * {@link java.util.Vector} does, providing amortized O(1) time cost for the * {@code insert} and amortized O(log(n)) for the {@code deleteMin} operation. * Operation {@code findMin}, is a worst-case O(1) operation. * *

* Constructing such a heap from an array of elements can be performed using the * method {@link #heapify(Object[])} or {@link #heapify(Object[], Comparator)} * in linear time. * *

* Note that the ordering maintained by a binary heap, like any heap, and * whether or not an explicit comparator is provided, must be consistent * with {@code equals} if this heap is to correctly implement the * {@code Heap} interface. (See {@code Comparable} or {@code Comparator} for a * precise definition of consistent with equals.) This is so because * the {@code Heap} interface is defined in terms of the {@code equals} * operation, but a binary heap performs all key comparisons using its * {@code compareTo} (or {@code compare}) method, so two keys that are deemed * equal by this method are, from the standpoint of the binary heap, equal. The * behavior of a heap is well-defined even if its ordering is * inconsistent with {@code equals}; it just fails to obey the general contract * of the {@code Heap} interface. * *

* Note that this implementation is not synchronized. If * multiple threads access a heap concurrently, and at least one of the threads * modifies the heap structurally, it must be synchronized externally. * (A structural modification is any operation that adds or deletes one or more * elements or changing the key of some element.) This is typically accomplished * by synchronizing on some object that naturally encapsulates the heap. * * @param * the type of keys maintained by this heap * * @author Dimitrios Michail */ public class BinaryArrayBulkInsertWeakHeap extends BinaryArrayWeakHeap implements Serializable { private static final long serialVersionUID = 1; /** * Insertion buffer capacity for integer size since we are using Java arrays * to store elements. */ protected static final int INSERTION_BUFFER_CAPACITY = 32 + 2; /** * The insertion buffer */ protected K[] insertionBuffer; /** * Number of elements in the insertion buffer */ protected int insertionBufferSize; /** * Position of minimum in the insertion buffer */ protected int insertionBufferMinPos; /** * Constructs a new, empty heap, using the natural ordering of its keys. * *

* All keys inserted into the heap must implement the {@link Comparable} * interface. Furthermore, all such keys must be mutually * comparable: {@code k1.compareTo(k2)} must not throw a * {@code ClassCastException} for any keys {@code k1} and {@code k2} in the * heap. If the user attempts to put a key into the heap that violates this * constraint (for example, the user attempts to put a string key into a * heap whose keys are integers), the {@code insert(Object key)} call will * throw a {@code ClassCastException}. * *

* The initial capacity of the heap is * {@link BinaryArrayBulkInsertWeakHeap#DEFAULT_HEAP_CAPACITY} and adjusts * automatically based on the sequence of insertions and deletions. */ public BinaryArrayBulkInsertWeakHeap() { this(null, DEFAULT_HEAP_CAPACITY); } /** * Constructs a new, empty heap, with a provided initial capacity using the * natural ordering of its keys. * *

* All keys inserted into the heap must implement the {@link Comparable} * interface. Furthermore, all such keys must be mutually * comparable: {@code k1.compareTo(k2)} must not throw a * {@code ClassCastException} for any keys {@code k1} and {@code k2} in the * heap. If the user attempts to put a key into the heap that violates this * constraint (for example, the user attempts to put a string key into a * heap whose keys are integers), the {@code insert(Object key)} call will * throw a {@code ClassCastException}. * *

* The initial capacity of the heap is provided by the user and is adjusted * automatically based on the sequence of insertions and deletions. * * @param capacity * the initial heap capacity */ public BinaryArrayBulkInsertWeakHeap(int capacity) { this(null, capacity); } /** * Constructs a new, empty heap, ordered according to the given comparator. * *

* All keys inserted into the heap must be mutually comparable by * the given comparator: {@code comparator.compare(k1, * k2)} must not throw a {@code ClassCastException} for any keys {@code k1} * and {@code k2} in the heap. If the user attempts to put a key into the * heap that violates this constraint, the {@code insert(Object key)} call * will throw a {@code ClassCastException}. * *

* The initial capacity of the heap is * {@link BinaryArrayBulkInsertWeakHeap#DEFAULT_HEAP_CAPACITY} and adjusts * automatically based on the sequence of insertions and deletions. * * @param comparator * the comparator that will be used to order this heap. If * {@code null}, the {@linkplain Comparable natural ordering} of * the keys will be used. */ public BinaryArrayBulkInsertWeakHeap(Comparator comparator) { this(comparator, DEFAULT_HEAP_CAPACITY); } /** * Constructs a new, empty heap, with a provided initial capacity ordered * according to the given comparator. * *

* All keys inserted into the heap must be mutually comparable by * the given comparator: {@code comparator.compare(k1, * k2)} must not throw a {@code ClassCastException} for any keys {@code k1} * and {@code k2} in the heap. If the user attempts to put a key into the * heap that violates this constraint, the {@code insert(Object key)} call * will throw a {@code ClassCastException}. * *

* The initial capacity of the heap is provided by the user and is adjusted * automatically based on the sequence of insertions and deletions. * * @param comparator * the comparator that will be used to order this heap. If * {@code null}, the {@linkplain Comparable natural ordering} of * the keys will be used. * @param capacity * the initial heap capacity */ @SuppressWarnings("unchecked") public BinaryArrayBulkInsertWeakHeap(Comparator comparator, int capacity) { super(comparator, capacity); this.insertionBuffer = (K[]) new Object[INSERTION_BUFFER_CAPACITY]; this.insertionBufferSize = 0; this.insertionBufferMinPos = 0; } /** * {@inheritDoc} */ @Override @ConstantTime public boolean isEmpty() { return size + insertionBufferSize == 0; } /** * {@inheritDoc} */ @Override @ConstantTime public long size() { return (long)size + insertionBufferSize; } /** * {@inheritDoc} */ @Override @ConstantTime public void clear() { size = 0; insertionBufferSize = 0; insertionBufferMinPos = 0; } /** * {@inheritDoc} */ @Override @ConstantTime @SuppressWarnings("unchecked") public K findMin() { if (size + insertionBufferSize == 0) { throw new NoSuchElementException(); } if (insertionBufferSize == 0) { return array[0]; } else if (size == 0) { return insertionBuffer[insertionBufferMinPos]; } else { K insertionBufferMin = insertionBuffer[insertionBufferMinPos]; if (comparator == null) { if (((Comparable) array[0]).compareTo(insertionBufferMin) <= 0) { return array[0]; } else { return insertionBufferMin; } } else { if (comparator.compare(array[0], insertionBufferMin) <= 0) { return array[0]; } else { return insertionBufferMin; } } } } /** * {@inheritDoc} */ @Override @SuppressWarnings("unchecked") @ConstantTime(amortized = true) public void insert(K key) { if (key == null) { throw new NullPointerException("Null keys not permitted"); } // add in buffer insertionBuffer[insertionBufferSize++] = key; if (isBulkInsertionBufferFull()) { if (size + insertionBufferSize > array.length) { // first try to double size if (array.length == 0) { ensureCapacity(1); } else { ensureCapacity(2 * array.length); } // if not enough, set to requested size ensureCapacity(size + insertionBufferSize); } if (comparator == null) { bulkInsert(); } else { bulkInsertWithComparator(); } } else if (insertionBufferSize > 1) { // update minimum K insertionBufferMin = insertionBuffer[insertionBufferMinPos]; if (comparator == null) { if (((Comparable) key).compareTo(insertionBufferMin) < 0) { insertionBufferMinPos = insertionBufferSize - 1; } } else { if (comparator.compare(key, insertionBufferMin) < 0) { insertionBufferMinPos = insertionBufferSize - 1; } } } } /** * {@inheritDoc} */ @Override @SuppressWarnings("unchecked") @LogarithmicTime(amortized = true) public K deleteMin() { if (size + insertionBufferSize == 0) { throw new NoSuchElementException(); } // where is the minimum boolean deleteFromInsertionBuffer = false; if (size == 0) { deleteFromInsertionBuffer = true; } else if (insertionBufferSize > 0) { K arrayMin = array[0]; K insertionBufferMin = insertionBuffer[insertionBufferMinPos]; if (comparator == null) { if (((Comparable) insertionBufferMin).compareTo(arrayMin) < 0) { deleteFromInsertionBuffer = true; } } else { if (comparator.compare(insertionBufferMin, arrayMin) < 0) { deleteFromInsertionBuffer = true; } } } K result; if (deleteFromInsertionBuffer) { result = insertionBuffer[insertionBufferMinPos]; insertionBuffer[insertionBufferMinPos] = insertionBuffer[insertionBufferSize - 1]; insertionBuffer[insertionBufferSize - 1] = null; insertionBufferSize--; insertionBufferMinPos = 0; if (comparator == null) { for (int i = 1; i < insertionBufferSize; i++) { if (((Comparable) insertionBuffer[i]) .compareTo(insertionBuffer[insertionBufferMinPos]) < 0) { insertionBufferMinPos = i; } } } else { for (int i = 1; i < insertionBufferSize; i++) { if (comparator.compare(insertionBuffer[i], insertionBuffer[insertionBufferMinPos]) < 0) { insertionBufferMinPos = i; } } } } else { result = array[0]; size--; array[0] = array[size]; array[size] = null; if (size > 1) { if (comparator == null) { fixdown(0); } else { fixdownWithComparator(0); } } if (minCapacity <= array.length && 4 * size < array.length) { ensureCapacity(array.length / 2); } } return result; } /** * Create a heap from an array of elements. The elements of the array are * not destroyed. The method has linear time complexity. * * @param * the type of keys maintained by the heap * @param array * an array of elements * @return a binary heap * @throws IllegalArgumentException * in case the array is null */ @LinearTime public static BinaryArrayBulkInsertWeakHeap heapify(K[] array) { if (array == null) { throw new IllegalArgumentException("Array cannot be null"); } if (array.length == 0) { return new BinaryArrayBulkInsertWeakHeap(); } BinaryArrayBulkInsertWeakHeap h = new BinaryArrayBulkInsertWeakHeap(array.length); System.arraycopy(array, 0, h.array, 0, array.length); h.size = array.length; for (int j = h.size - 1; j > 0; j--) { h.join(h.dancestor(j), j); } return h; } /** * Create a heap from an array of elements. The elements of the array are * not destroyed. The method has linear time complexity. * * @param * the type of keys maintained by the heap * @param array * an array of elements * @param comparator * the comparator to use * @return a binary heap * @throws IllegalArgumentException * in case the array is null */ @LinearTime public static BinaryArrayBulkInsertWeakHeap heapify(K[] array, Comparator comparator) { if (array == null) { throw new IllegalArgumentException("Array cannot be null"); } if (array.length == 0) { return new BinaryArrayBulkInsertWeakHeap(comparator); } BinaryArrayBulkInsertWeakHeap h = new BinaryArrayBulkInsertWeakHeap(comparator, array.length); System.arraycopy(array, 0, h.array, 0, array.length); h.size = array.length; for (int j = h.size - 1; j > 0; j--) { h.joinWithComparator(h.dancestor(j), j); } return h; } /** * Check if the bulk insertion buffer is full. * * @return true if the bulk insertion buffer is full, false otherwise */ protected boolean isBulkInsertionBufferFull() { if (insertionBufferSize >= insertionBuffer.length) { return true; } double sizeAsDouble = (double)size + insertionBufferSize; return Math.getExponent(sizeAsDouble) + 3 >= insertionBuffer.length; } /** * Bulk insert from insertion buffer into the weak heap. */ protected void bulkInsert() { if (insertionBufferSize == 0) { return; } int right = size + insertionBufferSize - 2; int left = Math.max(size, right / 2); while (insertionBufferSize > 0) { --insertionBufferSize; array[size] = insertionBuffer[insertionBufferSize]; insertionBuffer[insertionBufferSize] = null; reverse.clear(size); ++size; } while (right > left + 1) { left = left / 2; right = right / 2; for (int j = left; j <= right; j++) { fixdown(j); } } if (left != 0) { int i = dancestor(left); fixdown(i); fixup(i); } if (right != 0) { int i = dancestor(right); fixdown(i); fixup(i); } insertionBufferMinPos = 0; } /** * Bulk insert from insertion buffer into the weak heap. */ protected void bulkInsertWithComparator() { if (insertionBufferSize == 0) { return; } int right = size + insertionBufferSize - 2; int left = Math.max(size, right / 2); while (insertionBufferSize > 0) { --insertionBufferSize; array[size] = insertionBuffer[insertionBufferSize]; insertionBuffer[insertionBufferSize] = null; reverse.clear(size); ++size; } while (right > left + 1) { left = left / 2; right = right / 2; for (int j = left; j <= right; j++) { fixdownWithComparator(j); } } if (left != 0) { int i = dancestor(left); fixdownWithComparator(i); fixupWithComparator(i); } if (right != 0) { int i = dancestor(right); fixdownWithComparator(i); fixupWithComparator(i); } insertionBufferMinPos = 0; } } jheaps-jheaps-0.14/src/main/java/org/jheaps/array/BinaryArrayHeap.java000066400000000000000000000270041367505655000256760ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.array; import java.util.Comparator; import org.jheaps.annotations.LinearTime; /** * An array based binary heap. The heap is sorted according to the * {@linkplain Comparable natural ordering} of its keys, or by a * {@link Comparator} provided at heap creation time, depending on which * constructor is used. * *

* The implementation uses an array in order to store the elements and * automatically maintains the size of the array much like a * {@link java.util.Vector} does, providing amortized O(log(n)) time cost for * the {@code insert} and {@code deleteMin} operations. Operation * {@code findMin}, is a worst-case O(1) operation. The bounds are worst-case if * the user initializes the heap with a capacity larger or equal to the total * number of elements that are going to be inserted into the heap. * *

* Constructing such a heap from an array of elements can be performed using the * method {@link #heapify(Object[])} or {@link #heapify(Object[], Comparator)} * in linear time. * *

* Note that the ordering maintained by a binary heap, like any heap, and * whether or not an explicit comparator is provided, must be consistent * with {@code equals} if this heap is to correctly implement the * {@code Heap} interface. (See {@code Comparable} or {@code Comparator} for a * precise definition of consistent with equals.) This is so because * the {@code Heap} interface is defined in terms of the {@code equals} * operation, but a binary heap performs all key comparisons using its * {@code compareTo} (or {@code compare}) method, so two keys that are deemed * equal by this method are, from the standpoint of the binary heap, equal. The * behavior of a heap is well-defined even if its ordering is * inconsistent with {@code equals}; it just fails to obey the general contract * of the {@code Heap} interface. * *

* Note that this implementation is not synchronized. If * multiple threads access a heap concurrently, and at least one of the threads * modifies the heap structurally, it must be synchronized externally. * (A structural modification is any operation that adds or deletes one or more * elements or changing the key of some element.) This is typically accomplished * by synchronizing on some object that naturally encapsulates the heap. * * @param * the type of keys maintained by this heap * * @author Dimitrios Michail */ public class BinaryArrayHeap extends AbstractArrayHeap { private static final long serialVersionUID = 1L; /** * Default initial capacity of the binary heap. */ public static final int DEFAULT_HEAP_CAPACITY = 16; /** * Constructs a new, empty heap, using the natural ordering of its keys. * *

* All keys inserted into the heap must implement the {@link Comparable} * interface. Furthermore, all such keys must be mutually * comparable: {@code k1.compareTo(k2)} must not throw a * {@code ClassCastException} for any keys {@code k1} and {@code k2} in the * heap. If the user attempts to put a key into the heap that violates this * constraint (for example, the user attempts to put a string key into a * heap whose keys are integers), the {@code insert(Object key)} call will * throw a {@code ClassCastException}. * *

* The initial capacity of the heap is {@link #DEFAULT_HEAP_CAPACITY} and * adjusts automatically based on the sequence of insertions and deletions. */ public BinaryArrayHeap() { super(null, DEFAULT_HEAP_CAPACITY); } /** * Constructs a new, empty heap, with a provided initial capacity using the * natural ordering of its keys. * *

* All keys inserted into the heap must implement the {@link Comparable} * interface. Furthermore, all such keys must be mutually * comparable: {@code k1.compareTo(k2)} must not throw a * {@code ClassCastException} for any keys {@code k1} and {@code k2} in the * heap. If the user attempts to put a key into the heap that violates this * constraint (for example, the user attempts to put a string key into a * heap whose keys are integers), the {@code insert(Object key)} call will * throw a {@code ClassCastException}. * *

* The initial capacity of the heap is provided by the user and is adjusted * automatically based on the sequence of insertions and deletions. The * capacity will never become smaller than the initial requested capacity. * * @param capacity * the initial heap capacity */ public BinaryArrayHeap(int capacity) { super(null, capacity); } /** * Constructs a new, empty heap, ordered according to the given comparator. * *

* All keys inserted into the heap must be mutually comparable by * the given comparator: {@code comparator.compare(k1, * k2)} must not throw a {@code ClassCastException} for any keys {@code k1} * and {@code k2} in the heap. If the user attempts to put a key into the * heap that violates this constraint, the {@code insert(Object key)} call * will throw a {@code ClassCastException}. * *

* The initial capacity of the heap is * {@link BinaryArrayHeap#DEFAULT_HEAP_CAPACITY} and adjusts automatically * based on the sequence of insertions and deletions. * * @param comparator * the comparator that will be used to order this heap. If * {@code null}, the {@linkplain Comparable natural ordering} of * the keys will be used. */ public BinaryArrayHeap(Comparator comparator) { super(comparator, DEFAULT_HEAP_CAPACITY); } /** * Constructs a new, empty heap, with a provided initial capacity ordered * according to the given comparator. * *

* All keys inserted into the heap must be mutually comparable by * the given comparator: {@code comparator.compare(k1, * k2)} must not throw a {@code ClassCastException} for any keys {@code k1} * and {@code k2} in the heap. If the user attempts to put a key into the * heap that violates this constraint, the {@code insert(Object key)} call * will throw a {@code ClassCastException}. * *

* The initial capacity of the heap is provided by the user and is adjusted * automatically based on the sequence of insertions and deletions. The * capacity will never become smaller than the initial requested capacity. * * @param comparator * the comparator that will be used to order this heap. If * {@code null}, the {@linkplain Comparable natural ordering} of * the keys will be used. * @param capacity * the initial heap capacity */ public BinaryArrayHeap(Comparator comparator, int capacity) { super(comparator, capacity); } /** * Create a heap from an array of elements. The elements of the array are * not destroyed. The method has linear time complexity. * * @param * the type of keys maintained by the heap * @param array * an array of elements * @return a binary heap * @throws IllegalArgumentException * in case the array is null */ @LinearTime public static BinaryArrayHeap heapify(K[] array) { if (array == null) { throw new IllegalArgumentException("Array cannot be null"); } if (array.length == 0) { return new BinaryArrayHeap(); } BinaryArrayHeap h = new BinaryArrayHeap(array.length); System.arraycopy(array, 0, h.array, 1, array.length); h.size = array.length; for (int i = array.length / 2; i > 0; i--) { h.fixdown(i); } return h; } /** * Create a heap from an array of elements. The elements of the array are * not destroyed. The method has linear time complexity. * * @param * the type of keys maintained by the heap * @param array * an array of elements * @param comparator * the comparator to use * @return a binary heap * @throws IllegalArgumentException * in case the array is null */ @LinearTime public static BinaryArrayHeap heapify(K[] array, Comparator comparator) { if (array == null) { throw new IllegalArgumentException("Array cannot be null"); } if (array.length == 0) { return new BinaryArrayHeap(comparator); } BinaryArrayHeap h = new BinaryArrayHeap(comparator, array.length); System.arraycopy(array, 0, h.array, 1, array.length); h.size = array.length; for (int i = array.length / 2; i > 0; i--) { h.fixdownWithComparator(i); } return h; } /** * Ensure that the array representation has the necessary capacity. * * @param capacity * the requested capacity */ @Override @SuppressWarnings("unchecked") protected void ensureCapacity(int capacity) { checkCapacity(capacity); K[] newArray = (K[]) new Object[capacity + 1]; System.arraycopy(array, 1, newArray, 1, size); array = newArray; } @Override @SuppressWarnings("unchecked") protected void fixup(int k) { K key = array[k]; while (k > 1 && ((Comparable) array[k >> 1]).compareTo(key) > 0) { array[k] = array[k >> 1]; k >>= 1; } array[k] = key; } @Override protected void fixupWithComparator(int k) { K key = array[k]; while (k > 1 && comparator.compare(array[k >> 1], key) > 0) { array[k] = array[k >> 1]; k >>= 1; } array[k] = key; } @Override @SuppressWarnings("unchecked") protected void fixdown(int k) { K key = array[k]; while (2 * k <= size) { int j = 2 * k; if (j < size && ((Comparable) array[j]).compareTo(array[j + 1]) > 0) { j++; } if (((Comparable) key).compareTo(array[j]) <= 0) { break; } array[k] = array[j]; k = j; } array[k] = key; } @Override protected void fixdownWithComparator(int k) { K key = array[k]; while (2 * k <= size) { int j = 2 * k; if (j < size && comparator.compare(array[j], array[j + 1]) > 0) { j++; } if (comparator.compare(key, array[j]) <= 0) { break; } array[k] = array[j]; k = j; } array[k] = key; } } jheaps-jheaps-0.14/src/main/java/org/jheaps/array/BinaryArrayIntegerValueHeap.java000066400000000000000000000225221367505655000302110ustar00rootroot00000000000000package org.jheaps.array; import java.io.Serializable; import java.lang.reflect.Array; import java.util.Comparator; import java.util.NoSuchElementException; import org.jheaps.Constants; import org.jheaps.ValueHeap; import org.jheaps.annotations.ConstantTime; import org.jheaps.annotations.LogarithmicTime; /** * An optimized array-based binary heap with integer keys. * *

* This is a highly optimized implementation which uses (a) the Wegener * bottom-up heuristic and (b) sentinel values. The implementation uses an array * in order to store the elements, providing amortized O(log(n)) time for the * {@code insert} and {@code deleteMin} operations. Operation {@code findMin}, * is a worst-case O(1) operation. All bounds are worst-case if the user * initializes the heap with a capacity larger or equal to the total number of * elements that are going to be inserted into the heap. * *

* See the following papers for details about the optimizations: *

    *
  • Ingo Wegener. BOTTOM-UP-HEAPSORT, a new variant of HEAPSORT beating, on * an average, QUICKSORT (if n is not very small). Theoretical Computer Science, * 118(1), 81--98, 1993.
  • *
  • Peter Sanders. Fast Priority Queues for Cached Memory. Algorithms * Engineering and Experiments (ALENEX), 312--327, 1999.
  • *
* *

* Note that this implementation is not synchronized. If * multiple threads access a heap concurrently, and at least one of the threads * modifies the heap structurally, it must be synchronized externally. * (A structural modification is any operation that adds or deletes one or more * elements or changing the key of some element.) This is typically accomplished * by synchronizing on some object that naturally encapsulates the heap. * * @param * the type of values maintained by this heap * * @author Dimitrios Michail */ public class BinaryArrayIntegerValueHeap implements ValueHeap, Serializable { private static final long serialVersionUID = 1L; /** * Default initial capacity of the heap. */ public static final int DEFAULT_HEAP_CAPACITY = 16; /** * Supremum */ private static final int SUP_KEY = Integer.MAX_VALUE; /** * Infimum */ private static final int INF_KEY = Integer.MIN_VALUE; /** * The maximum heap capacity. */ private static final int MAX_HEAP_CAPACITY = Integer.MAX_VALUE - 8 - 1; /** * The minimum heap capacity. */ private static final int MIN_HEAP_CAPACITY = 0; /** * The array used for representing the heap. */ private Elem[] array; /** * Number of elements in the heap. */ private int size; /** * Minimum capacity due to initially requested capacity. */ private int minCapacity; /** * Constructs a new, empty heap, using the natural ordering of its keys. * *

* The initial capacity of the heap is {@link #DEFAULT_HEAP_CAPACITY} and * adjusts automatically based on the sequence of insertions and deletions. */ public BinaryArrayIntegerValueHeap() { this(DEFAULT_HEAP_CAPACITY); } /** * Constructs a new, empty heap, with a provided initial capacity using the * natural ordering of its keys. * *

* The initial capacity of the heap is provided by the user and is adjusted * automatically based on the sequence of insertions and deletions. The * capacity will never become smaller than the initial requested capacity. * * @param capacity * the initial heap capacity */ @SuppressWarnings("unchecked") public BinaryArrayIntegerValueHeap(int capacity) { checkCapacity(capacity); this.minCapacity = Math.max(capacity, DEFAULT_HEAP_CAPACITY); this.array = (Elem[]) Array.newInstance(Elem.class, minCapacity + 2); this.array[0] = new Elem(INF_KEY, null); for (int i = 1; i < minCapacity + 2; i++) { this.array[i] = new Elem(SUP_KEY, null); } this.size = 0; } /** * {@inheritDoc} */ @Override @ConstantTime public boolean isEmpty() { return size == 0; } /** * {@inheritDoc} */ @Override @ConstantTime public long size() { return size; } /** * {@inheritDoc} */ @Override @ConstantTime public void clear() { size = 0; } /** * {@inheritDoc} */ @Override public Comparator comparator() { return null; } /** * {@inheritDoc} */ @Override @ConstantTime public Integer findMin() { if (Constants.NOT_BENCHMARK && size == 0) { throw new NoSuchElementException(); } return array[1].key; } /** * {@inheritDoc} */ @Override @ConstantTime public V findMinValue() { if (Constants.NOT_BENCHMARK && size == 0) { throw new NoSuchElementException(); } return array[1].value; } /** * {@inheritDoc} */ @Override @LogarithmicTime public void insert(Integer key, V value) { if (Constants.NOT_BENCHMARK) { if (key == null) { throw new NullPointerException("Null keys not permitted"); } // make space if needed if (size == array.length - 2) { if (array.length == 2) { ensureCapacity(1); } else { ensureCapacity(2 * (array.length - 2)); } } } ++size; int hole = size; int pred = hole >> 1; Elem predElem = array[pred]; while (predElem.key > key) { array[hole].key = predElem.key; array[hole].value = predElem.value; hole = pred; pred >>= 1; predElem = array[pred]; } array[hole].key = key; array[hole].value = value; } /** * {@inheritDoc} */ @Override @LogarithmicTime public void insert(Integer key) { insert(key, null); } /** * {@inheritDoc} */ @Override @LogarithmicTime public Integer deleteMin() { if (Constants.NOT_BENCHMARK && size == 0) { throw new NoSuchElementException(); } Integer result = array[1].key; // first move up elements on a min-path int hole = 1; int succ = 2; int sz = size; while (succ < sz) { int key1 = array[succ].key; int key2 = array[succ + 1].key; if (key1 > key2) { succ++; array[hole].key = key2; array[hole].value = array[succ].value; } else { array[hole].key = key1; array[hole].value = array[succ].value; } hole = succ; succ <<= 1; } // bubble up rightmost element int bubble = array[sz].key; int pred = hole >> 1; while (array[pred].key > bubble) { array[hole].key = array[pred].key; array[hole].value = array[pred].value; hole = pred; pred >>= 1; } // finally move data to hole array[hole].key = bubble; array[hole].value = array[sz].value; array[size].key = SUP_KEY; array[size].value = null; size = sz - 1; if (Constants.NOT_BENCHMARK) { // free unused space int currentCapacity = array.length - 2; if (2 * minCapacity <= currentCapacity && 4 * size < currentCapacity) { ensureCapacity(currentCapacity / 2); } } return result; } /** * Ensure that the array representation has the necessary capacity. * * @param capacity * the requested capacity */ @SuppressWarnings("unchecked") private void ensureCapacity(int capacity) { checkCapacity(capacity); Elem[] newArray = (Elem[]) Array.newInstance(Elem.class, capacity + 2); if (newArray.length >= array.length) { System.arraycopy(array, 0, newArray, 0, array.length); for (int i = array.length; i < newArray.length; i++) { newArray[i] = new Elem(SUP_KEY, null); } } else { System.arraycopy(array, 0, newArray, 0, newArray.length); } array = newArray; } /** * Check that a capacity is valid. * * @param capacity * the capacity * * @throws IllegalArgumentException * if the capacity is negative or more than the maximum array * size */ private void checkCapacity(int capacity) { if (capacity < MIN_HEAP_CAPACITY) { throw new IllegalArgumentException("Heap capacity must be >= " + MIN_HEAP_CAPACITY); } if (capacity > MAX_HEAP_CAPACITY) { throw new IllegalArgumentException("Heap capacity too large"); } } private static class Elem implements Serializable { private static final long serialVersionUID = 1L; int key; V value; public Elem(Integer key, V value) { this.key = key; this.value = value; } } } jheaps-jheaps-0.14/src/main/java/org/jheaps/array/BinaryArrayWeakHeap.java000066400000000000000000000360161367505655000265110ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.array; import java.io.Serializable; import java.util.BitSet; import java.util.Comparator; import java.util.NoSuchElementException; import org.jheaps.Constants; import org.jheaps.annotations.ConstantTime; import org.jheaps.annotations.LinearTime; import org.jheaps.annotations.LogarithmicTime; /** * An array based binary weak heap. The heap is sorted according to the * {@linkplain Comparable natural ordering} of its keys, or by a * {@link Comparator} provided at heap creation time, depending on which * constructor is used. * *

* The implementation uses an array in order to store the elements and * automatically maintains the size of the array much like a * {@link java.util.Vector} does, providing amortized O(log(n)) time cost for * the {@code insert} and {@code deleteMin} operations. Operation * {@code findMin}, is a worst-case O(1) operation. The bounds are worst-case if * the user initializes the heap with a capacity larger or equal to the total * number of elements that are going to be inserted into the heap. * *

* Constructing such a heap from an array of elements can be performed using the * method {@link #heapify(Object[])} or {@link #heapify(Object[], Comparator)} * in linear time. * *

* Note that the ordering maintained by a binary heap, like any heap, and * whether or not an explicit comparator is provided, must be consistent * with {@code equals} if this heap is to correctly implement the * {@code Heap} interface. (See {@code Comparable} or {@code Comparator} for a * precise definition of consistent with equals.) This is so because * the {@code Heap} interface is defined in terms of the {@code equals} * operation, but a binary heap performs all key comparisons using its * {@code compareTo} (or {@code compare}) method, so two keys that are deemed * equal by this method are, from the standpoint of the binary heap, equal. The * behavior of a heap is well-defined even if its ordering is * inconsistent with {@code equals}; it just fails to obey the general contract * of the {@code Heap} interface. * *

* Note that this implementation is not synchronized. If * multiple threads access a heap concurrently, and at least one of the threads * modifies the heap structurally, it must be synchronized externally. * (A structural modification is any operation that adds or deletes one or more * elements or changing the key of some element.) This is typically accomplished * by synchronizing on some object that naturally encapsulates the heap. * * @param * the type of keys maintained by this heap * * @author Dimitrios Michail */ public class BinaryArrayWeakHeap extends AbstractArrayWeakHeap implements Serializable { private static final long serialVersionUID = 7721391024028836146L; /** * Default initial capacity of the binary heap. */ public static final int DEFAULT_HEAP_CAPACITY = 16; /** * Reverse bits */ protected BitSet reverse; /** * Constructs a new, empty heap, using the natural ordering of its keys. * *

* All keys inserted into the heap must implement the {@link Comparable} * interface. Furthermore, all such keys must be mutually * comparable: {@code k1.compareTo(k2)} must not throw a * {@code ClassCastException} for any keys {@code k1} and {@code k2} in the * heap. If the user attempts to put a key into the heap that violates this * constraint (for example, the user attempts to put a string key into a * heap whose keys are integers), the {@code insert(Object key)} call will * throw a {@code ClassCastException}. * *

* The initial capacity of the heap is * {@link BinaryArrayWeakHeap#DEFAULT_HEAP_CAPACITY} and adjusts * automatically based on the sequence of insertions and deletions. */ public BinaryArrayWeakHeap() { super(null, DEFAULT_HEAP_CAPACITY); } /** * Constructs a new, empty heap, with a provided initial capacity using the * natural ordering of its keys. * *

* All keys inserted into the heap must implement the {@link Comparable} * interface. Furthermore, all such keys must be mutually * comparable: {@code k1.compareTo(k2)} must not throw a * {@code ClassCastException} for any keys {@code k1} and {@code k2} in the * heap. If the user attempts to put a key into the heap that violates this * constraint (for example, the user attempts to put a string key into a * heap whose keys are integers), the {@code insert(Object key)} call will * throw a {@code ClassCastException}. * *

* The initial capacity of the heap is provided by the user and is adjusted * automatically based on the sequence of insertions and deletions. The * capacity will never become smaller than the initial requested capacity. * * @param capacity * the initial heap capacity */ public BinaryArrayWeakHeap(int capacity) { super(null, capacity); } /** * Constructs a new, empty heap, ordered according to the given comparator. * *

* All keys inserted into the heap must be mutually comparable by * the given comparator: {@code comparator.compare(k1, * k2)} must not throw a {@code ClassCastException} for any keys {@code k1} * and {@code k2} in the heap. If the user attempts to put a key into the * heap that violates this constraint, the {@code insert(Object key)} call * will throw a {@code ClassCastException}. * *

* The initial capacity of the heap is * {@link BinaryArrayWeakHeap#DEFAULT_HEAP_CAPACITY} and adjusts * automatically based on the sequence of insertions and deletions. * * @param comparator * the comparator that will be used to order this heap. If * {@code null}, the {@linkplain Comparable natural ordering} of * the keys will be used. */ public BinaryArrayWeakHeap(Comparator comparator) { super(comparator, DEFAULT_HEAP_CAPACITY); } /** * Constructs a new, empty heap, with a provided initial capacity ordered * according to the given comparator. * *

* All keys inserted into the heap must be mutually comparable by * the given comparator: {@code comparator.compare(k1, * k2)} must not throw a {@code ClassCastException} for any keys {@code k1} * and {@code k2} in the heap. If the user attempts to put a key into the * heap that violates this constraint, the {@code insert(Object key)} call * will throw a {@code ClassCastException}. * *

* The initial capacity of the heap is provided by the user and is adjusted * automatically based on the sequence of insertions and deletions. The * capacity will never become smaller than the initial requested capacity. * * @param comparator * the comparator that will be used to order this heap. If * {@code null}, the {@linkplain Comparable natural ordering} of * the keys will be used. * @param capacity * the initial heap capacity */ public BinaryArrayWeakHeap(Comparator comparator, int capacity) { super(comparator, capacity); } /** * Create a heap from an array of elements. The elements of the array are * not destroyed. The method has linear time complexity. * * @param * the type of keys maintained by the heap * @param array * an array of elements * @return a heap * @throws IllegalArgumentException * in case the array is null */ @LinearTime public static BinaryArrayWeakHeap heapify(K[] array) { if (array == null) { throw new IllegalArgumentException("Array cannot be null"); } if (array.length == 0) { return new BinaryArrayWeakHeap(); } BinaryArrayWeakHeap h = new BinaryArrayWeakHeap(array.length); System.arraycopy(array, 0, h.array, 0, array.length); h.size = array.length; for (int j = h.size - 1; j > 0; j--) { h.join(h.dancestor(j), j); } return h; } /** * Create a heap from an array of elements. The elements of the array are * not destroyed. The method has linear time complexity. * * @param * the type of keys maintained by the heap * @param array * an array of elements * @param comparator * the comparator to use * @return a heap * @throws IllegalArgumentException * in case the array is null */ @LinearTime public static BinaryArrayWeakHeap heapify(K[] array, Comparator comparator) { if (array == null) { throw new IllegalArgumentException("Array cannot be null"); } if (array.length == 0) { return new BinaryArrayWeakHeap(comparator); } BinaryArrayWeakHeap h = new BinaryArrayWeakHeap(comparator, array.length); System.arraycopy(array, 0, h.array, 0, array.length); h.size = array.length; for (int j = h.size - 1; j > 0; j--) { h.joinWithComparator(h.dancestor(j), j); } return h; } /** * {@inheritDoc} */ @Override @ConstantTime public K findMin() { if (Constants.NOT_BENCHMARK && size == 0) { throw new NoSuchElementException(); } return array[0]; } /** * {@inheritDoc} */ @Override @LogarithmicTime(amortized = true) public void insert(K key) { if (Constants.NOT_BENCHMARK) { if (key == null) { throw new NullPointerException("Null keys not permitted"); } // make sure there is space if (size == array.length) { if (size == 0) { ensureCapacity(1); } else { ensureCapacity(2 * array.length); } } } array[size] = key; reverse.clear(size); if (size % 2 == 0) { reverse.clear(size / 2); } if (comparator == null) { fixup(size); } else { fixupWithComparator(size); } ++size; } /** * {@inheritDoc} */ @Override @LogarithmicTime(amortized = true) public K deleteMin() { if (Constants.NOT_BENCHMARK && size == 0) { throw new NoSuchElementException(); } K result = array[0]; size--; array[0] = array[size]; array[size] = null; if (size > 1) { if (comparator == null) { fixdown(0); } else { fixdownWithComparator(0); } } if (Constants.NOT_BENCHMARK) { if (2 * minCapacity <= array.length && 4 * size < array.length) { ensureCapacity(array.length / 2); } } return result; } @Override @SuppressWarnings("unchecked") protected void initCapacity(int capacity) { this.array = (K[]) new Object[capacity]; this.reverse = new BitSet(capacity); } /** * Ensure that the array representation has the necessary capacity. * * @param capacity * the requested capacity */ @Override @SuppressWarnings("unchecked") protected void ensureCapacity(int capacity) { checkCapacity(capacity); K[] newArray = (K[]) new Object[capacity]; System.arraycopy(array, 0, newArray, 0, size); array = newArray; BitSet newBitSet = new BitSet(capacity); newBitSet.or(reverse); reverse = newBitSet; } /** * Return the distinguished ancestor of an element. * * @param j * the element * @return the distinguished ancestor of the element */ protected int dancestor(int j) { while ((j % 2 == 1) == reverse.get(j / 2)) { j = j / 2; } return j / 2; } /** * Join two weak heaps into one. * * @param i * root of the first weak heap * @param j * root of the second weak heap * @return true if already a weak heap, false if a flip was needed */ @SuppressWarnings("unchecked") protected boolean join(int i, int j) { if (((Comparable) array[j]).compareTo(array[i]) < 0) { K tmp = array[i]; array[i] = array[j]; array[j] = tmp; reverse.flip(j); return false; } return true; } /** * Join two weak heaps into one. * * @param i * root of the first weak heap * @param j * root of the second weak heap * @return true if already a weak heap, false if a flip was needed */ protected boolean joinWithComparator(int i, int j) { if (comparator.compare(array[j], array[i]) < 0) { K tmp = array[i]; array[i] = array[j]; array[j] = tmp; reverse.flip(j); return false; } return true; } @Override protected void fixup(int j) { int i; while (j > 0) { i = dancestor(j); if (join(i, j)) { break; } j = i; } } @Override protected void fixupWithComparator(int j) { int i; while (j > 0) { i = dancestor(j); if (joinWithComparator(i, j)) { break; } j = i; } } @Override protected void fixdown(int j) { int k = 2 * j + (reverse.get(j) ? 0 : 1); int c; while ((c = 2 * k + (reverse.get(k) ? 1 : 0)) < size) { k = c; } while (k != j) { join(j, k); k = k / 2; } } @Override protected void fixdownWithComparator(int j) { int k = 2 * j + (reverse.get(j) ? 0 : 1); int c; while ((c = 2 * k + (reverse.get(k) ? 1 : 0)) < size) { k = c; } while (k != j) { joinWithComparator(j, k); k = k / 2; } } } jheaps-jheaps-0.14/src/main/java/org/jheaps/array/DaryArrayAddressableHeap.java000066400000000000000000000425421367505655000275070ustar00rootroot00000000000000/* * (C) Copyright 2014-2019, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.array; import java.io.Serializable; import java.lang.reflect.Array; import java.util.Comparator; import java.util.Iterator; import java.util.NoSuchElementException; import org.jheaps.AddressableHeap; import org.jheaps.annotations.LinearTime; /** * An array based d-ary addressable heap. The heap is sorted according to the * {@linkplain Comparable natural ordering} of its keys, or by a * {@link Comparator} provided at heap creation time, depending on which * constructor is used. * *

* The implementation uses an array in order to store the elements. and * automatically maintains the size of the array much like a * {@link java.util.Vector} does, providing amortized O(log_d(n)) time cost for * the {@code insert} and amortized O(d log_d(n)) for the {@code deleteMin} * operation. Operation {@code findMin}, is a worst-case O(1) operation. * Operations {@code delete} and {@code decreaseKey} take worst-case O(log(n)) * time. The bounds are worst-case if the user initializes the heap with a * capacity larger or equal to the total number of elements that are going to be * inserted into the heap. * *

* Constructing such a heap from an array of elements can be performed using the * method {@link #heapify(int, Object[], Object[])} or * {@link #heapify(int, Object[], Object[], Comparator)} in linear time. * *

* Note that the ordering maintained by a d-ary heap, like any heap, and whether * or not an explicit comparator is provided, must be consistent with * {@code equals} if this heap is to correctly implement the {@code Heap} * interface. (See {@code Comparable} or {@code Comparator} for a precise * definition of consistent with equals.) This is so because the * {@code Heap} interface is defined in terms of the {@code equals} operation, * but a binary heap performs all key comparisons using its {@code compareTo} * (or {@code compare}) method, so two keys that are deemed equal by this method * are, from the standpoint of the d-ary heap, equal. The behavior of a heap * is well-defined even if its ordering is inconsistent with * {@code equals}; it just fails to obey the general contract of the * {@code Heap} interface. * *

* Note that this implementation is not synchronized. If * multiple threads access a heap concurrently, and at least one of the threads * modifies the heap structurally, it must be synchronized externally. * (A structural modification is any operation that adds or deletes one or more * elements or changing the key of some element.) This is typically accomplished * by synchronizing on some object that naturally encapsulates the heap. * * @param * the type of keys maintained by this heap * @param * the type of values maintained by this heap * * @author Dimitrios Michail */ public class DaryArrayAddressableHeap extends AbstractArrayAddressableHeap implements Serializable { private static final long serialVersionUID = 1; private static final String D_ARY_HEAPS_MUST_HAVE_AT_LEAST_2_CHILDREN_PER_NODE = "D-ary heaps must have at least 2 children per node"; /** * Default initial capacity of the binary heap. */ public static final int DEFAULT_HEAP_CAPACITY = 16; /** * Degree */ protected int d; /** * Constructs a new, empty heap, using the natural ordering of its keys. * *

* All keys inserted into the heap must implement the {@link Comparable} * interface. Furthermore, all such keys must be mutually * comparable: {@code k1.compareTo(k2)} must not throw a * {@code ClassCastException} for any keys {@code k1} and {@code k2} in the * heap. If the user attempts to put a key into the heap that violates this * constraint (for example, the user attempts to put a string key into a * heap whose keys are integers), the {@code insert(Object key)} call will * throw a {@code ClassCastException}. * *

* The initial capacity of the heap is {@link #DEFAULT_HEAP_CAPACITY} and * adjusts automatically based on the sequence of insertions and deletions. * * @param d * the number of children of each node in the d-ary heap * @throws IllegalArgumentException * in case the number of children per node are less than 2 */ public DaryArrayAddressableHeap(int d) { this(d, null, DEFAULT_HEAP_CAPACITY); } /** * Constructs a new, empty heap, with a provided initial capacity using the * natural ordering of its keys. * *

* All keys inserted into the heap must implement the {@link Comparable} * interface. Furthermore, all such keys must be mutually * comparable: {@code k1.compareTo(k2)} must not throw a * {@code ClassCastException} for any keys {@code k1} and {@code k2} in the * heap. If the user attempts to put a key into the heap that violates this * constraint (for example, the user attempts to put a string key into a * heap whose keys are integers), the {@code insert(Object key)} call will * throw a {@code ClassCastException}. * *

* The initial capacity of the heap is provided by the user and is adjusted * automatically based on the sequence of insertions and deletions. The * capacity will never become smaller than the initial requested capacity. * * @param d * the number of children of each node in the d-ary heap * @param capacity * the initial heap capacity * @throws IllegalArgumentException * in case the number of children per node are less than 2 */ public DaryArrayAddressableHeap(int d, int capacity) { this(d, null, capacity); } /** * Constructs a new, empty heap, ordered according to the given comparator. * *

* All keys inserted into the heap must be mutually comparable by * the given comparator: {@code comparator.compare(k1, * k2)} must not throw a {@code ClassCastException} for any keys {@code k1} * and {@code k2} in the heap. If the user attempts to put a key into the * heap that violates this constraint, the {@code insert(Object key)} call * will throw a {@code ClassCastException}. * *

* The initial capacity of the heap is {@link #DEFAULT_HEAP_CAPACITY} and * adjusts automatically based on the sequence of insertions and deletions. * * @param d * the number of children of each node in the d-ary heap * @param comparator * the comparator that will be used to order this heap. If * {@code null}, the {@linkplain Comparable natural ordering} of * the keys will be used. * @throws IllegalArgumentException * in case the number of children per node are less than 2 * */ public DaryArrayAddressableHeap(int d, Comparator comparator) { this(d, comparator, DEFAULT_HEAP_CAPACITY); } /** * Constructs a new, empty heap, with a provided initial capacity ordered * according to the given comparator. * *

* All keys inserted into the heap must be mutually comparable by * the given comparator: {@code comparator.compare(k1, * k2)} must not throw a {@code ClassCastException} for any keys {@code k1} * and {@code k2} in the heap. If the user attempts to put a key into the * heap that violates this constraint, the {@code insert(Object key)} call * will throw a {@code ClassCastException}. * *

* The initial capacity of the heap is provided by the user and is adjusted * automatically based on the sequence of insertions and deletions. The * capacity will never become smaller than the initial requested capacity. * * @param d * the number of children of each node in the d-ary heap * @param comparator * the comparator that will be used to order this heap. If * {@code null}, the {@linkplain Comparable natural ordering} of * the keys will be used. * @param capacity * the initial heap capacity * @throws IllegalArgumentException * in case the number of children per node are less than 2 */ public DaryArrayAddressableHeap(int d, Comparator comparator, int capacity) { super(comparator, capacity); if (d < 2) { throw new IllegalArgumentException(D_ARY_HEAPS_MUST_HAVE_AT_LEAST_2_CHILDREN_PER_NODE); } this.d = d; } /** * Create a heap from an array of elements. The elements of the array are * not destroyed. The method has linear time complexity. * * @param * the type of keys maintained by the heap * @param * the type of values maintained by the heap * @param d * the number of children of the d-ary heap * @param keys * an array of keys * @param values * an array of values, can be null * @return a d-ary heap * @throws IllegalArgumentException * in case the number of children per node are less than 2 * @throws IllegalArgumentException * in case the keys array is null * @throws IllegalArgumentException * in case the values array has different length than the keys * array */ @LinearTime public static DaryArrayAddressableHeap heapify(int d, K[] keys, V[] values) { if (d < 2) { throw new IllegalArgumentException(D_ARY_HEAPS_MUST_HAVE_AT_LEAST_2_CHILDREN_PER_NODE); } if (keys == null) { throw new IllegalArgumentException("Key array cannot be null"); } if (values != null && keys.length != values.length) { throw new IllegalArgumentException("Values array must have the same length as the keys array"); } if (keys.length == 0) { return new DaryArrayAddressableHeap(d); } DaryArrayAddressableHeap h = new DaryArrayAddressableHeap(d, keys.length); for (int i = 0; i < keys.length; i++) { K key = keys[i]; V value = (values == null) ? null : values[i]; AbstractArrayAddressableHeap.ArrayHandle ah = h.new ArrayHandle(key, value); ah.index = i + 1; h.array[i + 1] = ah; } h.size = keys.length; for (int i = keys.length / d; i > 0; i--) { h.fixdown(i); } return h; } /** * Create a heap from an array of elements. The elements of the array are * not destroyed. The method has linear time complexity. * * @param * the type of keys maintained by the heap * @param * the type of values maintained by the heap * @param d * the number of children of the d-ary heap * @param keys * an array of keys * @param values * an array of values, can be null * @param comparator * the comparator to use * @return a d-ary heap * @throws IllegalArgumentException * in case the number of children per node are less than 2 * @throws IllegalArgumentException * in case the keys array is null * @throws IllegalArgumentException * in case the values array has different length than the keys * array */ @LinearTime public static DaryArrayAddressableHeap heapify(int d, K[] keys, V[] values, Comparator comparator) { if (d < 2) { throw new IllegalArgumentException(D_ARY_HEAPS_MUST_HAVE_AT_LEAST_2_CHILDREN_PER_NODE); } if (keys == null) { throw new IllegalArgumentException("Keys array cannot be null"); } if (values != null && keys.length != values.length) { throw new IllegalArgumentException("Values array must have the same length as the keys array"); } if (keys.length == 0) { return new DaryArrayAddressableHeap(d, comparator); } DaryArrayAddressableHeap h = new DaryArrayAddressableHeap(d, comparator, keys.length); for (int i = 0; i < keys.length; i++) { K key = keys[i]; V value = (values == null) ? null : values[i]; AbstractArrayAddressableHeap.ArrayHandle ah = h.new ArrayHandle(key, value); ah.index = i + 1; h.array[i + 1] = ah; } h.size = keys.length; for (int i = keys.length / d; i > 0; i--) { h.fixdownWithComparator(i); } return h; } /** * Get an iterator for all handles currently in the heap. * * This method is especially useful when building a heap using the heapify * method. Unspecified behavior will occur if the heap is modified while * using this iterator. * * @return an iterator which will return all handles of the heap */ public Iterator> handlesIterator() { return new Iterator>() { private int pos = 1; @Override public boolean hasNext() { return pos <= size; } @Override public AddressableHeap.Handle next() { if (pos > size) { throw new NoSuchElementException(); } return array[pos++]; } @Override public void remove() { throw new UnsupportedOperationException(); } }; } /** * Ensure that the array representation has the necessary capacity. * * @param capacity * the requested capacity */ @Override @SuppressWarnings("unchecked") protected void ensureCapacity(int capacity) { checkCapacity(capacity); ArrayHandle[] newArray = (ArrayHandle[]) Array.newInstance(ArrayHandle.class, capacity + 1); System.arraycopy(array, 1, newArray, 1, size); array = newArray; } @Override protected void forceFixup(int k) { ArrayHandle h = array[k]; while (k > 1) { int p = (k - 2) / d + 1; array[k] = array[p]; array[k].index = k; k = p; } array[k] = h; h.index = k; } @Override @SuppressWarnings("unchecked") protected void fixup(int k) { ArrayHandle h = array[k]; while (k > 1) { int p = (k - 2) / d + 1; if (((Comparable) array[p].getKey()).compareTo(h.getKey()) <= 0) { break; } array[k] = array[p]; array[k].index = k; k = p; } array[k] = h; h.index = k; } @Override protected void fixupWithComparator(int k) { ArrayHandle h = array[k]; while (k > 1) { int p = (k - 2) / d + 1; if (comparator.compare(array[p].getKey(), h.getKey()) <= 0) { break; } array[k] = array[p]; array[k].index = k; k = p; } array[k] = h; h.index = k; } @Override @SuppressWarnings("unchecked") protected void fixdown(int k) { int c; ArrayHandle h = array[k]; while ((c = d * (k - 1) + 2) <= size) { int maxc = c; for (int i = 1; i < d && c + i <= size; i++) { if (((Comparable) array[maxc].getKey()).compareTo(array[c + i].getKey()) > 0) { maxc = c + i; } } if (((Comparable) h.getKey()).compareTo(array[maxc].getKey()) <= 0) { break; } array[k] = array[maxc]; array[k].index = k; k = maxc; } array[k] = h; h.index = k; } @Override protected void fixdownWithComparator(int k) { int c; ArrayHandle h = array[k]; while ((c = d * (k - 1) + 2) <= size) { int maxc = c; for (int i = 1; i < d && c + i <= size; i++) { if (comparator.compare(array[maxc].getKey(), array[c + i].getKey()) > 0) { maxc = c + i; } } if (comparator.compare(h.getKey(), array[maxc].getKey()) <= 0) { break; } array[k] = array[maxc]; array[k].index = k; k = maxc; } array[k] = h; h.index = k; } } jheaps-jheaps-0.14/src/main/java/org/jheaps/array/DaryArrayHeap.java000066400000000000000000000333021367505655000253470ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.array; import java.util.Comparator; import org.jheaps.annotations.LinearTime; /** * An array based d-ary heap. The heap is sorted according to the * {@linkplain Comparable natural ordering} of its keys, or by a * {@link Comparator} provided at heap creation time, depending on which * constructor is used. * *

* The implementation uses an array in order to store the elements and * automatically maintains the size of the array much like a * {@link java.util.Vector} does, providing amortized O(log_d(n)) time cost for * the {@code insert} and amortized O(d log_d(n)) for the {@code deleteMin} * operation. Operation {@code findMin}, is a worst-case O(1) operation. The * bounds are worst-case if the user initializes the heap with a capacity larger * or equal to the total number of elements that are going to be inserted into * the heap. * *

* Constructing such a heap from an array of elements can be performed using the * method {@link #heapify(int, Object[])} or * {@link #heapify(int, Object[], Comparator)} in linear time. * *

* Note that the ordering maintained by a d-ary heap, like any heap, and whether * or not an explicit comparator is provided, must be consistent with * {@code equals} if this heap is to correctly implement the {@code Heap} * interface. (See {@code Comparable} or {@code Comparator} for a precise * definition of consistent with equals.) This is so because the * {@code Heap} interface is defined in terms of the {@code equals} operation, * but a d-ary heap performs all key comparisons using its {@code compareTo} (or * {@code compare}) method, so two keys that are deemed equal by this method * are, from the standpoint of the d-ary heap, equal. The behavior of a heap * is well-defined even if its ordering is inconsistent with * {@code equals}; it just fails to obey the general contract of the * {@code Heap} interface. * *

* Note that this implementation is not synchronized. If * multiple threads access a heap concurrently, and at least one of the threads * modifies the heap structurally, it must be synchronized externally. * (A structural modification is any operation that adds or deletes one or more * elements or changing the key of some element.) This is typically accomplished * by synchronizing on some object that naturally encapsulates the heap. * * @param * the type of keys maintained by this heap * * @author Dimitrios Michail */ public class DaryArrayHeap extends AbstractArrayHeap { private static final long serialVersionUID = 1L; /** * Default initial capacity of the heap. */ public static final int DEFAULT_HEAP_CAPACITY = 16; /** * Degree */ protected int d; /** * Constructs a new, empty heap, using the natural ordering of its keys. * *

* All keys inserted into the heap must implement the {@link Comparable} * interface. Furthermore, all such keys must be mutually * comparable: {@code k1.compareTo(k2)} must not throw a * {@code ClassCastException} for any keys {@code k1} and {@code k2} in the * heap. If the user attempts to put a key into the heap that violates this * constraint (for example, the user attempts to put a string key into a * heap whose keys are integers), the {@code insert(Object key)} call will * throw a {@code ClassCastException}. * *

* The initial capacity of the heap is * {@link DaryArrayHeap#DEFAULT_HEAP_CAPACITY} and adjusts automatically * based on the sequence of insertions and deletions. * * @param d * the number of children of each node in the d-ary heap * @throws IllegalArgumentException * in case the number of children per node are less than 2 */ public DaryArrayHeap(int d) { this(d, null, DEFAULT_HEAP_CAPACITY); } /** * Constructs a new, empty heap, with a provided initial capacity using the * natural ordering of its keys. * *

* All keys inserted into the heap must implement the {@link Comparable} * interface. Furthermore, all such keys must be mutually * comparable: {@code k1.compareTo(k2)} must not throw a * {@code ClassCastException} for any keys {@code k1} and {@code k2} in the * heap. If the user attempts to put a key into the heap that violates this * constraint (for example, the user attempts to put a string key into a * heap whose keys are integers), the {@code insert(Object key)} call will * throw a {@code ClassCastException}. * *

* The initial capacity of the heap is provided by the user and is adjusted * automatically based on the sequence of insertions and deletions. The * capacity will never become smaller than the initial requested capacity. * * @param d * the number of children of each node in the d-ary heap * @param capacity * the initial heap capacity * @throws IllegalArgumentException * in case the number of children per node are less than 2 */ public DaryArrayHeap(int d, int capacity) { this(d, null, capacity); } /** * Constructs a new, empty heap, ordered according to the given comparator. * *

* All keys inserted into the heap must be mutually comparable by * the given comparator: {@code comparator.compare(k1, * k2)} must not throw a {@code ClassCastException} for any keys {@code k1} * and {@code k2} in the heap. If the user attempts to put a key into the * heap that violates this constraint, the {@code insert(Object key)} call * will throw a {@code ClassCastException}. * *

* The initial capacity of the heap is * {@link DaryArrayHeap#DEFAULT_HEAP_CAPACITY} and adjusts automatically * based on the sequence of insertions and deletions. * * @param d * the number of children of each node in the d-ary heap * @param comparator * the comparator that will be used to order this heap. If * {@code null}, the {@linkplain Comparable natural ordering} of * the keys will be used. * @throws IllegalArgumentException * in case the number of children per node are less than 2 */ public DaryArrayHeap(int d, Comparator comparator) { this(d, comparator, DEFAULT_HEAP_CAPACITY); } /** * Constructs a new, empty heap, with a provided initial capacity ordered * according to the given comparator. * *

* All keys inserted into the heap must be mutually comparable by * the given comparator: {@code comparator.compare(k1, * k2)} must not throw a {@code ClassCastException} for any keys {@code k1} * and {@code k2} in the heap. If the user attempts to put a key into the * heap that violates this constraint, the {@code insert(Object key)} call * will throw a {@code ClassCastException}. * *

* The initial capacity of the heap is provided by the user and is adjusted * automatically based on the sequence of insertions and deletions. The * capacity will never become smaller than the initial requested capacity. * * @param d * the number of children of each node in the d-ary heap * @param comparator * the comparator that will be used to order this heap. If * {@code null}, the {@linkplain Comparable natural ordering} of * the keys will be used. * @param capacity * the initial heap capacity * @throws IllegalArgumentException * in case the number of children per node are less than 2 */ public DaryArrayHeap(int d, Comparator comparator, int capacity) { super(comparator, capacity); if (d < 2) { throw new IllegalArgumentException("D-ary heaps must have at least 2 children per node"); } this.d = d; } /** * Create a heap from an array of elements. The elements of the array are * not destroyed. The method has linear time complexity. * * @param * the type of keys maintained by the heap * @param d * the number of children of the d-ary heap * @param array * an array of elements * @return a d-ary heap * @throws IllegalArgumentException * in case the number of children per node are less than 2 * @throws IllegalArgumentException * in case the array is null */ @LinearTime public static DaryArrayHeap heapify(int d, K[] array) { if (d < 2) { throw new IllegalArgumentException("D-ary heaps must have at least 2 children per node"); } if (array == null) { throw new IllegalArgumentException("Array cannot be null"); } if (array.length == 0) { return new DaryArrayHeap(d); } DaryArrayHeap h = new DaryArrayHeap(d, array.length); System.arraycopy(array, 0, h.array, 1, array.length); h.size = array.length; for (int i = array.length / d; i > 0; i--) { h.fixdown(i); } return h; } /** * Create a heap from an array of elements. The elements of the array are * not destroyed. The method has linear time complexity. * * @param * the type of keys maintained by the heap * @param d * the number of children of the d-ary heap * @param array * an array of elements * @param comparator * the comparator to use * @return a d-ary heap * @throws IllegalArgumentException * in case the number of children per node are less than 2 * @throws IllegalArgumentException * in case the array is null */ @LinearTime public static DaryArrayHeap heapify(int d, K[] array, Comparator comparator) { if (d < 2) { throw new IllegalArgumentException("D-ary heaps must have at least 2 children per node"); } if (array == null) { throw new IllegalArgumentException("Array cannot be null"); } if (array.length == 0) { return new DaryArrayHeap(d, comparator); } DaryArrayHeap h = new DaryArrayHeap(d, comparator, array.length); System.arraycopy(array, 0, h.array, 1, array.length); h.size = array.length; for (int i = array.length / d; i > 0; i--) { h.fixdownWithComparator(i); } return h; } /** * Ensure that the array representation has the necessary capacity. * * @param capacity * the requested capacity */ @Override @SuppressWarnings("unchecked") protected void ensureCapacity(int capacity) { checkCapacity(capacity); K[] newArray = (K[]) new Object[capacity + 1]; System.arraycopy(array, 1, newArray, 1, size); array = newArray; } @Override @SuppressWarnings("unchecked") protected void fixup(int k) { // assert k >= 1 && k <= size; K key = array[k]; while (k > 1) { int p = (k - 2) / d + 1; if (((Comparable) array[p]).compareTo(key) <= 0) { break; } array[k] = array[p]; k = p; } array[k] = key; } @Override protected void fixupWithComparator(int k) { // assert k >= 1 && k <= size; K key = array[k]; while (k > 1) { int p = (k - 2) / d + 1; if (comparator.compare(array[p], key) <= 0) { break; } array[k] = array[p]; k = p; } array[k] = key; } @Override @SuppressWarnings("unchecked") protected void fixdown(int k) { int c; K key = array[k]; while ((c = d * (k - 1) + 2) <= size) { int maxc = c; for (int i = 1; i < d; i++) { if (c + i <= size && ((Comparable) array[maxc]).compareTo(array[c + i]) > 0) { maxc = c + i; } } if (((Comparable) key).compareTo(array[maxc]) <= 0) { break; } array[k] = array[maxc]; k = maxc; } array[k] = key; } @Override protected void fixdownWithComparator(int k) { int c; K key = array[k]; while ((c = d * (k - 1) + 2) <= size) { int maxc = c; for (int i = 1; i < d; i++) { if (c + i <= size && comparator.compare(array[maxc], array[c + i]) > 0) { maxc = c + i; } } if (comparator.compare(key, array[maxc]) <= 0) { break; } array[k] = array[maxc]; k = maxc; } array[k] = key; } } jheaps-jheaps-0.14/src/main/java/org/jheaps/array/MinMaxBinaryArrayDoubleEndedHeap.java000066400000000000000000001003031367505655000310750ustar00rootroot00000000000000package org.jheaps.array; import java.io.Serializable; import java.util.Comparator; import java.util.NoSuchElementException; import org.jheaps.Constants; import org.jheaps.DoubleEndedHeap; import org.jheaps.annotations.LinearTime; import org.jheaps.annotations.VisibleForTesting; /** * An array based binary MinMax heap. The heap is sorted according to the * {@linkplain Comparable natural ordering} of its keys, or by a * {@link Comparator} provided at heap creation time, depending on which * constructor is used. * *

* For details about the implementation see the following * paper: *

    *
  • M. D. Atkinson, J.-R. Sack, N. Santoro, and T. Strothotte. Min-max Heaps * and Generalized Priority Queues. Commun. ACM, 29(10), 996--1000, 1986.
  • *
* *

* The implementation uses an array in order to store the elements and * automatically maintains the size of the array much like a * {@link java.util.Vector} does, providing amortized O(log(n)) time cost for * the {@code insert}, {@code deleteMin}, and {@code deleteMax} operations. * Operations {@code findMin} and {@code findMax} are worst-case O(1). The * bounds are worst-case if the user initializes the heap with a capacity larger * or equal to the total number of elements that are going to be inserted into * the heap. * *

* Constructing such a heap from an array of elements can be performed using the * method {@link #heapify(Object[])} or {@link #heapify(Object[], Comparator)} * in linear time. * *

* Note that the ordering maintained by this heap, like any heap, and whether or * not an explicit comparator is provided, must be consistent with * {@code equals} if this heap is to correctly implement the {@code Heap} * interface. (See {@code Comparable} or {@code Comparator} for a precise * definition of consistent with equals.) This is so because the * {@code Heap} interface is defined in terms of the {@code equals} operation, * but this heap performs all key comparisons using its {@code compareTo} (or * {@code compare}) method, so two keys that are deemed equal by this method * are, from the standpoint of this heap, equal. The behavior of a heap * is well-defined even if its ordering is inconsistent with * {@code equals}; it just fails to obey the general contract of the * {@code Heap} interface. * *

* Note that this implementation is not synchronized. If * multiple threads access a heap concurrently, and at least one of the threads * modifies the heap structurally, it must be synchronized externally. * (A structural modification is any operation that adds or deletes one or more * elements or changing the key of some element.) This is typically accomplished * by synchronizing on some object that naturally encapsulates the heap. * * @param * the type of keys maintained by this heap * * @author Dimitrios Michail */ public class MinMaxBinaryArrayDoubleEndedHeap extends AbstractArrayHeap implements DoubleEndedHeap, Serializable { private static final long serialVersionUID = -8985374211686556917L; /** * Default initial capacity of the heap. */ public static final int DEFAULT_HEAP_CAPACITY = 16; /** * Constructs a new, empty heap, using the natural ordering of its keys. * *

* All keys inserted into the heap must implement the {@link Comparable} * interface. Furthermore, all such keys must be mutually * comparable: {@code k1.compareTo(k2)} must not throw a * {@code ClassCastException} for any keys {@code k1} and {@code k2} in the * heap. If the user attempts to put a key into the heap that violates this * constraint (for example, the user attempts to put a string key into a * heap whose keys are integers), the {@code insert(Object key)} call will * throw a {@code ClassCastException}. * *

* The initial capacity of the heap is {@link #DEFAULT_HEAP_CAPACITY} and * adjusts automatically based on the sequence of insertions and deletions. */ public MinMaxBinaryArrayDoubleEndedHeap() { super(null, DEFAULT_HEAP_CAPACITY); } /** * Constructs a new, empty heap, with a provided initial capacity using the * natural ordering of its keys. * *

* All keys inserted into the heap must implement the {@link Comparable} * interface. Furthermore, all such keys must be mutually * comparable: {@code k1.compareTo(k2)} must not throw a * {@code ClassCastException} for any keys {@code k1} and {@code k2} in the * heap. If the user attempts to put a key into the heap that violates this * constraint (for example, the user attempts to put a string key into a * heap whose keys are integers), the {@code insert(Object key)} call will * throw a {@code ClassCastException}. * *

* The initial capacity of the heap is provided by the user and is adjusted * automatically based on the sequence of insertions and deletions. The * capacity will never become smaller than the initial requested capacity. * * @param capacity * the initial heap capacity */ public MinMaxBinaryArrayDoubleEndedHeap(int capacity) { super(null, capacity); } /** * Constructs a new, empty heap, ordered according to the given comparator. * *

* All keys inserted into the heap must be mutually comparable by * the given comparator: {@code comparator.compare(k1, * k2)} must not throw a {@code ClassCastException} for any keys {@code k1} * and {@code k2} in the heap. If the user attempts to put a key into the * heap that violates this constraint, the {@code insert(Object key)} call * will throw a {@code ClassCastException}. * *

* The initial capacity of the heap is {@link #DEFAULT_HEAP_CAPACITY} and * adjusts automatically based on the sequence of insertions and deletions. * * @param comparator * the comparator that will be used to order this heap. If * {@code null}, the {@linkplain Comparable natural ordering} of * the keys will be used. */ public MinMaxBinaryArrayDoubleEndedHeap(Comparator comparator) { super(comparator, DEFAULT_HEAP_CAPACITY); } /** * Constructs a new, empty heap, with a provided initial capacity ordered * according to the given comparator. * *

* All keys inserted into the heap must be mutually comparable by * the given comparator: {@code comparator.compare(k1, * k2)} must not throw a {@code ClassCastException} for any keys {@code k1} * and {@code k2} in the heap. If the user attempts to put a key into the * heap that violates this constraint, the {@code insert(Object key)} call * will throw a {@code ClassCastException}. * *

* The initial capacity of the heap is provided by the user and is adjusted * automatically based on the sequence of insertions and deletions.The * capacity will never become smaller than the initial requested capacity. * * @param comparator * the comparator that will be used to order this heap. If * {@code null}, the {@linkplain Comparable natural ordering} of * the keys will be used. * @param capacity * the initial heap capacity */ public MinMaxBinaryArrayDoubleEndedHeap(Comparator comparator, int capacity) { super(comparator, capacity); } /** * Create a heap from an array of elements. The elements of the array are * not destroyed. The method has linear time complexity. * * @param * the type of keys maintained by the heap * @param array * an array of elements * @return a heap * @throws IllegalArgumentException * in case the array is null */ @LinearTime public static MinMaxBinaryArrayDoubleEndedHeap heapify(K[] array) { if (array == null) { throw new IllegalArgumentException("Array cannot be null"); } if (array.length == 0) { return new MinMaxBinaryArrayDoubleEndedHeap(); } MinMaxBinaryArrayDoubleEndedHeap h = new MinMaxBinaryArrayDoubleEndedHeap(array.length); System.arraycopy(array, 0, h.array, 1, array.length); h.size = array.length; for (int i = array.length / 2; i > 0; i--) { h.fixdown(i); } return h; } /** * Create a heap from an array of elements. The elements of the array are * not destroyed. The method has linear time complexity. * * @param * the type of keys maintained by the heap * @param array * an array of elements * @param comparator * the comparator to use * @return a heap * @throws IllegalArgumentException * in case the array is null */ @LinearTime public static MinMaxBinaryArrayDoubleEndedHeap heapify(K[] array, Comparator comparator) { if (array == null) { throw new IllegalArgumentException("Array cannot be null"); } if (array.length == 0) { return new MinMaxBinaryArrayDoubleEndedHeap(comparator); } MinMaxBinaryArrayDoubleEndedHeap h = new MinMaxBinaryArrayDoubleEndedHeap(comparator, array.length); System.arraycopy(array, 0, h.array, 1, array.length); h.size = array.length; for (int i = array.length / 2; i > 0; i--) { h.fixdownWithComparator(i); } return h; } /** * Ensure that the array representation has the necessary capacity. * * @param capacity * the requested capacity */ @Override @SuppressWarnings("unchecked") protected void ensureCapacity(int capacity) { checkCapacity(capacity); K[] newArray = (K[]) new Object[capacity + 1]; System.arraycopy(array, 1, newArray, 1, size); array = newArray; } /** * {@inheritDoc} */ @Override @SuppressWarnings("unchecked") public K findMax() { switch (size) { case 0: throw new NoSuchElementException(); case 1: return array[1]; case 2: return array[2]; default: if (comparator == null) { if (((Comparable) array[3]).compareTo(array[2]) > 0) { return array[3]; } else { return array[2]; } } else { if (comparator.compare(array[3], array[2]) > 0) { return array[3]; } else { return array[2]; } } } } /** * {@inheritDoc} */ @Override @SuppressWarnings("unchecked") public K deleteMax() { K result; switch (size) { case 0: throw new NoSuchElementException(); case 1: result = array[1]; array[1] = null; size--; break; case 2: result = array[2]; array[2] = null; size--; break; default: if (comparator == null) { if (((Comparable) array[3]).compareTo(array[2]) > 0) { result = array[3]; array[3] = array[size]; array[size] = null; size--; if (size >= 3) { fixdownMax(3); } } else { result = array[2]; array[2] = array[size]; array[size] = null; size--; fixdownMax(2); } } else { if (comparator.compare(array[3], array[2]) > 0) { result = array[3]; array[3] = array[size]; array[size] = null; size--; if (size >= 3) { fixdownMaxWithComparator(3); } } else { result = array[2]; array[2] = array[size]; array[size] = null; size--; fixdownMaxWithComparator(2); } } break; } if (Constants.NOT_BENCHMARK) { if (2 * minCapacity < array.length - 1 && 4 * size < array.length - 1) { ensureCapacity((array.length - 1) / 2); } } return result; } /** * Upwards fix starting from a particular element * * @param k * the index of the starting element */ @Override @SuppressWarnings("unchecked") protected void fixup(int k) { if (onMinLevel(k)) { int p = k / 2; K kValue = array[k]; if (p > 0 && ((Comparable) array[p]).compareTo(kValue) < 0) { array[k] = array[p]; array[p] = kValue; fixupMax(p); } else { fixupMin(k); } } else { int p = k / 2; K kValue = array[k]; if (p > 0 && ((Comparable) kValue).compareTo(array[p]) < 0) { array[k] = array[p]; array[p] = kValue; fixupMin(p); } else { fixupMax(k); } } } /** * Upwards fix starting from a particular element * * @param k * the index of the starting element */ protected void fixupWithComparator(int k) { if (onMinLevel(k)) { int p = k / 2; K kValue = array[k]; if (p > 0 && comparator.compare(array[p], kValue) < 0) { array[k] = array[p]; array[p] = kValue; fixupMaxWithComparator(p); } else { fixupMinWithComparator(k); } } else { int p = k / 2; K kValue = array[k]; if (p > 0 && comparator.compare(kValue, array[p]) < 0) { array[k] = array[p]; array[p] = kValue; fixupMinWithComparator(p); } else { fixupMaxWithComparator(k); } } } /** * Upwards fix starting from a particular element at a minimum level * * @param k * the index of the starting element */ @SuppressWarnings("unchecked") private void fixupMin(int k) { K key = array[k]; int gp = k / 4; while (gp > 0 && ((Comparable) array[gp]).compareTo(key) > 0) { array[k] = array[gp]; k = gp; gp = k / 4; } array[k] = key; } /** * Upwards fix starting from a particular element at a minimum level. * Performs comparisons using the comparator. * * @param k * the index of the starting element */ private void fixupMinWithComparator(int k) { K key = array[k]; int gp = k / 4; while (gp > 0 && comparator.compare(array[gp], key) > 0) { array[k] = array[gp]; k = gp; gp = k / 4; } array[k] = key; } /** * Upwards fix starting from a particular element at a maximum level * * @param k * the index of the starting element */ @SuppressWarnings("unchecked") private void fixupMax(int k) { K key = array[k]; int gp = k / 4; while (gp > 0 && ((Comparable) array[gp]).compareTo(key) < 0) { array[k] = array[gp]; k = gp; gp = k / 4; } array[k] = key; } /** * Upwards fix starting from a particular element at a maximum level. * Performs comparisons using the comparator. * * @param k * the index of the starting element */ private void fixupMaxWithComparator(int k) { K key = array[k]; int gp = k / 4; while (gp > 0 && comparator.compare(array[gp], key) < 0) { array[k] = array[gp]; k = gp; gp = k / 4; } array[k] = key; } /** * Downwards fix starting from a particular element. * * @param k * the index of the starting element */ @Override protected void fixdown(int k) { if (onMinLevel(k)) { fixdownMin(k); } else { fixdownMax(k); } } /** * Downwards fix starting from a particular element. Performs comparisons * using the comparator. * * @param k * the index of the starting element */ @Override protected void fixdownWithComparator(int k) { if (onMinLevel(k)) { fixdownMinWithComparator(k); } else { fixdownMaxWithComparator(k); } } /** * Downwards fix starting from a particular element at a minimum level. * * @param k * the index of the starting element */ @SuppressWarnings("unchecked") private void fixdownMin(int k) { int c = 2 * k; while (c <= size) { int m = minChildOrGrandchild(k); if (m > c + 1) { // grandchild if (((Comparable) array[m]).compareTo(array[k]) >= 0) { break; } K tmp = array[k]; array[k] = array[m]; array[m] = tmp; if (((Comparable) array[m]).compareTo(array[m / 2]) > 0) { tmp = array[m]; array[m] = array[m / 2]; array[m / 2] = tmp; } // go down k = m; c = 2 * k; } else { // child if (((Comparable) array[m]).compareTo(array[k]) < 0) { K tmp = array[k]; array[k] = array[m]; array[m] = tmp; } break; } } } /** * Downwards fix starting from a particular element at a minimum level. * Performs comparisons using the comparator. * * @param k * the index of the starting element */ private void fixdownMinWithComparator(int k) { int c = 2 * k; while (c <= size) { int m = minChildOrGrandchildWithComparator(k); if (m > c + 1) { // grandchild if (comparator.compare(array[m], array[k]) >= 0) { break; } K tmp = array[k]; array[k] = array[m]; array[m] = tmp; if (comparator.compare(array[m], array[m / 2]) > 0) { tmp = array[m]; array[m] = array[m / 2]; array[m / 2] = tmp; } // go down k = m; c = 2 * k; } else { // child if (comparator.compare(array[m], array[k]) < 0) { K tmp = array[k]; array[k] = array[m]; array[m] = tmp; } break; } } } /** * Downwards fix starting from a particular element at a maximum level. * * @param k * the index of the starting element */ @SuppressWarnings("unchecked") private void fixdownMax(int k) { int c = 2 * k; while (c <= size) { int m = maxChildOrGrandchild(k); if (m > c + 1) { // grandchild if (((Comparable) array[m]).compareTo(array[k]) <= 0) { break; } K tmp = array[k]; array[k] = array[m]; array[m] = tmp; if (((Comparable) array[m]).compareTo(array[m / 2]) < 0) { tmp = array[m]; array[m] = array[m / 2]; array[m / 2] = tmp; } // go down k = m; c = 2 * k; } else { // child if (((Comparable) array[m]).compareTo(array[k]) > 0) { K tmp = array[k]; array[k] = array[m]; array[m] = tmp; } break; } } } /** * Downwards fix starting from a particular element at a maximum level. * Performs comparisons using the comparator. * * @param k * the index of the starting element */ private void fixdownMaxWithComparator(int k) { int c = 2 * k; while (c <= size) { int m = maxChildOrGrandchildWithComparator(k); if (m > c + 1) { // grandchild if (comparator.compare(array[m], array[k]) <= 0) { break; } K tmp = array[k]; array[k] = array[m]; array[m] = tmp; if (comparator.compare(array[m], array[m / 2]) < 0) { tmp = array[m]; array[m] = array[m / 2]; array[m / 2] = tmp; } // go down k = m; c = 2 * k; } else { // child if (comparator.compare(array[m], array[k]) > 0) { K tmp = array[k]; array[k] = array[m]; array[m] = tmp; } break; } } } /** * Return true if on a minimum level, false otherwise. * * @param k * the element * @return true if on a minimum level, false otherwise */ @VisibleForTesting boolean onMinLevel(int k) { float kAsFloat = k; int exponent = Math.getExponent(kAsFloat); return exponent % 2 == 0; } /** * Given a node at a maximum level, find its child or grandchild with the * maximum key. This method should not be called for a node which has no * children. * * @param k * a node at a maximum level * @return the child or grandchild with a maximum key, or undefined if there * are no children */ @SuppressWarnings("unchecked") private int maxChildOrGrandchild(int k) { int gc = 4 * k; int maxgc; K gcValue; // 4 grandchilden if (gc + 3 <= size) { gcValue = array[gc]; maxgc = gc; if (((Comparable) array[++gc]).compareTo(gcValue) > 0) { gcValue = array[gc]; maxgc = gc; } if (((Comparable) array[++gc]).compareTo(gcValue) > 0) { gcValue = array[gc]; maxgc = gc; } if (((Comparable) array[++gc]).compareTo(gcValue) > 0) { maxgc = gc; } return maxgc; } // less or equal to 3 switch (size - gc) { case 2: // 3 grandchildren, two children gcValue = array[gc]; maxgc = gc; if (((Comparable) array[++gc]).compareTo(gcValue) > 0) { gcValue = array[gc]; maxgc = gc; } if (((Comparable) array[++gc]).compareTo(gcValue) > 0) { maxgc = gc; } return maxgc; case 1: // 2 grandchildren, maybe two children gcValue = array[gc]; maxgc = gc; if (((Comparable) array[++gc]).compareTo(gcValue) > 0) { gcValue = array[gc]; maxgc = gc; } if (2 * k + 1 <= size && ((Comparable) array[2 * k + 1]).compareTo(gcValue) > 0) { maxgc = 2 * k + 1; } return maxgc; case 0: // 1 grandchild, maybe two children gcValue = array[gc]; maxgc = gc; if (2 * k + 1 <= size && ((Comparable) array[2 * k + 1]).compareTo(gcValue) > 0) { maxgc = 2 * k + 1; } return maxgc; } // 0 grandchildren maxgc = 2 * k; gcValue = array[maxgc]; if (2 * k + 1 <= size && ((Comparable) array[2 * k + 1]).compareTo(gcValue) > 0) { maxgc = 2 * k + 1; } return maxgc; } /** * Given a node at a maximum level, find its child or grandchild with the * maximum key. This method should not be called for a node which has no * children. * * @param k * a node at a maximum level * @return the child or grandchild with a maximum key, or undefined if there * are no children */ private int maxChildOrGrandchildWithComparator(int k) { int gc = 4 * k; int maxgc; K gcValue; // 4 grandchilden if (gc + 3 <= size) { gcValue = array[gc]; maxgc = gc; if (comparator.compare(array[++gc], gcValue) > 0) { gcValue = array[gc]; maxgc = gc; } if (comparator.compare(array[++gc], gcValue) > 0) { gcValue = array[gc]; maxgc = gc; } if (comparator.compare(array[++gc], gcValue) > 0) { maxgc = gc; } return maxgc; } // less or equal to 3 switch (size - gc) { case 2: // 3 grandchildren, two children gcValue = array[gc]; maxgc = gc; if (comparator.compare(array[++gc], gcValue) > 0) { gcValue = array[gc]; maxgc = gc; } if (comparator.compare(array[++gc], gcValue) > 0) { maxgc = gc; } return maxgc; case 1: // 2 grandchildren, maybe two children gcValue = array[gc]; maxgc = gc; if (comparator.compare(array[++gc], gcValue) > 0) { gcValue = array[gc]; maxgc = gc; } if (2 * k + 1 <= size && comparator.compare(array[2 * k + 1], gcValue) > 0) { maxgc = 2 * k + 1; } return maxgc; case 0: // 1 grandchild, maybe two children gcValue = array[gc]; maxgc = gc; if (2 * k + 1 <= size && comparator.compare(array[2 * k + 1], gcValue) > 0) { maxgc = 2 * k + 1; } return maxgc; } // 0 grandchildren maxgc = 2 * k; gcValue = array[maxgc]; if (2 * k + 1 <= size && comparator.compare(array[2 * k + 1], gcValue) > 0) { maxgc = 2 * k + 1; } return maxgc; } /** * Given a node at a minimum level, find its child or grandchild with the * minimum key. This method should not be called for a node which has no * children. * * @param k * a node at a minimum level * @return the child or grandchild with a minimum key, or undefined if there * are no children */ @SuppressWarnings("unchecked") private int minChildOrGrandchild(int k) { int gc = 4 * k; int mingc; K gcValue; // 4 grandchilden if (gc + 3 <= size) { gcValue = array[gc]; mingc = gc; if (((Comparable) array[++gc]).compareTo(gcValue) < 0) { gcValue = array[gc]; mingc = gc; } if (((Comparable) array[++gc]).compareTo(gcValue) < 0) { gcValue = array[gc]; mingc = gc; } if (((Comparable) array[++gc]).compareTo(gcValue) < 0) { mingc = gc; } return mingc; } // less or equal to 3 switch (size - gc) { case 2: // 3 grandchildren, two children gcValue = array[gc]; mingc = gc; if (((Comparable) array[++gc]).compareTo(gcValue) < 0) { gcValue = array[gc]; mingc = gc; } if (((Comparable) array[++gc]).compareTo(gcValue) < 0) { mingc = gc; } return mingc; case 1: // 2 grandchildren, maybe two children gcValue = array[gc]; mingc = gc; if (((Comparable) array[++gc]).compareTo(gcValue) < 0) { gcValue = array[gc]; mingc = gc; } if (2 * k + 1 <= size && ((Comparable) array[2 * k + 1]).compareTo(gcValue) < 0) { mingc = 2 * k + 1; } return mingc; case 0: // 1 grandchild, maybe two children gcValue = array[gc]; mingc = gc; if (2 * k + 1 <= size && ((Comparable) array[2 * k + 1]).compareTo(gcValue) < 0) { mingc = 2 * k + 1; } return mingc; } // 0 grandchildren mingc = 2 * k; gcValue = array[mingc]; if (2 * k + 1 <= size && ((Comparable) array[2 * k + 1]).compareTo(gcValue) < 0) { mingc = 2 * k + 1; } return mingc; } /** * Given a node at a minimum level, find its child or grandchild with the * minimum key. This method should not be called for a node which has no * children. * * @param k * a node at a minimum level * @return the child or grandchild with a minimum key, or undefined if there * are no children */ private int minChildOrGrandchildWithComparator(int k) { int gc = 4 * k; int mingc; K gcValue; // 4 grandchilden if (gc + 3 <= size) { gcValue = array[gc]; mingc = gc; if (comparator.compare(array[++gc], gcValue) < 0) { gcValue = array[gc]; mingc = gc; } if (comparator.compare(array[++gc], gcValue) < 0) { gcValue = array[gc]; mingc = gc; } if (comparator.compare(array[++gc], gcValue) < 0) { mingc = gc; } return mingc; } // less or equal to 3 switch (size - gc) { case 2: // 3 grandchildren, two children gcValue = array[gc]; mingc = gc; if (comparator.compare(array[++gc], gcValue) < 0) { gcValue = array[gc]; mingc = gc; } if (comparator.compare(array[++gc], gcValue) < 0) { mingc = gc; } return mingc; case 1: // 2 grandchildren, maybe two children gcValue = array[gc]; mingc = gc; if (comparator.compare(array[++gc], gcValue) < 0) { gcValue = array[gc]; mingc = gc; } if (2 * k + 1 <= size && comparator.compare(array[2 * k + 1], gcValue) < 0) { mingc = 2 * k + 1; } return mingc; case 0: // 1 grandchild, maybe two children gcValue = array[gc]; mingc = gc; if (2 * k + 1 <= size && comparator.compare(array[2 * k + 1], gcValue) < 0) { mingc = 2 * k + 1; } return mingc; } // 0 grandchildren mingc = 2 * k; gcValue = array[mingc]; if (2 * k + 1 <= size && comparator.compare(array[2 * k + 1], gcValue) < 0) { mingc = 2 * k + 1; } return mingc; } } jheaps-jheaps-0.14/src/main/java/org/jheaps/array/package-info.java000066400000000000000000000013051367505655000251750ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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. */ /** * Heaps using an array representation */ package org.jheaps.array; jheaps-jheaps-0.14/src/main/java/org/jheaps/dag/000077500000000000000000000000001367505655000214245ustar00rootroot00000000000000jheaps-jheaps-0.14/src/main/java/org/jheaps/dag/HollowHeap.java000066400000000000000000000401031367505655000243270ustar00rootroot00000000000000package org.jheaps.dag; import java.io.Serializable; import java.lang.reflect.Array; import java.util.Comparator; import java.util.NoSuchElementException; import org.jheaps.AddressableHeap; import org.jheaps.MergeableAddressableHeap; import org.jheaps.annotations.ConstantTime; import org.jheaps.annotations.LogarithmicTime; /** * Hollow heaps. The heap is sorted according to the {@linkplain Comparable * natural ordering} of its keys, or by a {@link Comparator} provided at heap * creation time, depending on which constructor is used. * *

* This is the hollow heap described in detail in the following * paper: *

    *
  • TD Hansen, H Kaplan, RE Tarjan and U Zwick. Hollow heaps. ACM * Transactions on Algorithms (TALG), 13(3), 42, 2017.
  • *
* *

* This implementation provides amortized O(1) time for operations that do not * involve deleting an element such as {@code insert}, and {@code decreaseKey}. * Operations {@code deleteMin} and {@code delete} are amortized O(log(n)). The * operation {@code meld} is also amortized O(1). * *

* All the above bounds, however, assume that the user does not perform * cascading melds on heaps such as: * *

 * d.meld(e);
 * c.meld(d);
 * b.meld(c);
 * a.meld(b);
 * 
* * The above scenario, although efficiently supported by using union-find with * path compression, invalidates the claimed bounds. * *

* Note that the ordering maintained by this heap, like any heap, and whether or * not an explicit comparator is provided, must be consistent with * {@code equals} if this heap is to correctly implement the * {@code AddressableHeap} interface. (See {@code Comparable} or * {@code Comparator} for a precise definition of consistent with * equals.) This is so because the {@code AddressableHeap} interface is * defined in terms of the {@code equals} operation, but this heap performs all * key comparisons using its {@code compareTo} (or {@code compare}) method, so * two keys that are deemed equal by this method are, from the standpoint of * this heap, equal. The behavior of a heap is well-defined even if its * ordering is inconsistent with {@code equals}; it just fails to obey the * general contract of the {@code AddressableHeap} interface. * *

* Note that this implementation is not synchronized. If * multiple threads access a heap concurrently, and at least one of the threads * modifies the heap structurally, it must be synchronized externally. * (A structural modification is any operation that adds or deletes one or more * elements or changing the key of some element.) This is typically accomplished * by synchronizing on some object that naturally encapsulates the heap. * * @param * the type of keys maintained by this heap * @param * the type of values maintained by this heap * * @author Dimitrios Michail * */ public class HollowHeap implements MergeableAddressableHeap, Serializable { private final static long serialVersionUID = 1; /** * Size of bucket array. Based on maximum rank. */ private final static int AUX_BUCKET_ARRAY_SIZE = 128; /** * The comparator used to maintain order in this heap, or null if it uses * the natural ordering of its keys. * * @serial */ private final Comparator comparator; /** * The last node in the root list */ private HollowNode root; /** * Size of the heap */ private long size; /** * Number of nodes (hollow or not). Useful for rebuilding. */ private long nodes; /** * Auxiliary array for performing links. */ private HollowNode[] aux; /** * Used to reference the current heap or some other pairing heap in case of * melding, so that handles remain valid even after a meld, without having * to iterate over them. * * In order to avoid maintaining a full-fledged union-find data structure, * we disallow a heap to be used in melding more than once. We use however, * path-compression in case of cascading melds, that it, a handle moves from * one heap to another and then another. */ private HollowHeap other; /** * Constructs a new, empty heap, using the natural ordering of its keys. All * keys inserted into the heap must implement the {@link Comparable} * interface. Furthermore, all such keys must be mutually * comparable: {@code k1.compareTo(k2)} must not throw a * {@code ClassCastException} for any keys {@code k1} and {@code k2} in the * heap. If the user attempts to put a key into the heap that violates this * constraint (for example, the user attempts to put a string key into a * heap whose keys are integers), the {@code insert(Object key)} call will * throw a {@code ClassCastException}. */ @ConstantTime public HollowHeap() { this(null); } /** * Constructs a new, empty heap, ordered according to the given comparator. * All keys inserted into the heap must be mutually comparable by * the given comparator: {@code comparator.compare(k1, * k2)} must not throw a {@code ClassCastException} for any keys {@code k1} * and {@code k2} in the heap. If the user attempts to put a key into the * heap that violates this constraint, the {@code insert(Object key)} call * will throw a {@code ClassCastException}. * * @param comparator * the comparator that will be used to order this heap. If * {@code null}, the {@linkplain Comparable natural ordering} of * the keys will be used. */ @ConstantTime @SuppressWarnings("unchecked") public HollowHeap(Comparator comparator) { this.root = null; this.comparator = comparator; this.size = 0; this.nodes = 0; this.aux = (HollowNode[]) Array.newInstance(HollowNode.class, AUX_BUCKET_ARRAY_SIZE); this.other = this; } /** * {@inheritDoc} * * @throws IllegalStateException * if the heap has already been used in the right hand side of a * meld */ @Override @ConstantTime(amortized = true) public AddressableHeap.Handle insert(K key, V value) { if (other != this) { throw new IllegalStateException("A heap cannot be used after a meld"); } if (key == null) { throw new NullPointerException("Null keys not permitted"); } Item e = new Item(key, value); HollowNode u = new HollowNode(this, key); u.item = e; e.node = u; nodes++; if (root == null) { root = u; } else { root = link(root, u); } size++; return e; } /** * {@inheritDoc} * * @throws IllegalStateException * if the heap has already been used in the right hand side of a * meld */ @Override @ConstantTime(amortized = true) public AddressableHeap.Handle insert(K key) { return insert(key, null); } /** * {@inheritDoc} */ @Override @ConstantTime(amortized = false) public AddressableHeap.Handle findMin() { if (size == 0) { throw new NoSuchElementException(); } return root.item; } /** * {@inheritDoc} */ @Override @LogarithmicTime(amortized = true) public AddressableHeap.Handle deleteMin() { if (size == 0) { throw new NoSuchElementException(); } Item item = root.item; item.delete(); return item; } /** * {@inheritDoc} */ @Override @ConstantTime public boolean isEmpty() { return size == 0; } /** * {@inheritDoc} */ @Override @ConstantTime public long size() { return size; } /** * {@inheritDoc} */ @Override public Comparator comparator() { return comparator; } /** * {@inheritDoc} */ @Override @ConstantTime(amortized = false) public void clear() { root = null; size = 0; nodes = 0; } /** * {@inheritDoc} */ @Override @ConstantTime public void meld(MergeableAddressableHeap other) { HollowHeap h = (HollowHeap) other; // check same comparator if (comparator != null) { if (h.comparator == null || !h.comparator.equals(comparator)) { throw new IllegalArgumentException("Cannot meld heaps using different comparators!"); } } else if (h.comparator != null) { throw new IllegalArgumentException("Cannot meld heaps using different comparators!"); } if (h.other != h) { throw new IllegalStateException("A heap cannot be used after a meld."); } // meld if (root == null) { root = h.root; } else if (h.root != null) { root = link(root, h.root); } size += h.size; nodes += h.nodes; // clear other h.size = 0; h.nodes = 0; h.root = null; // take ownership h.other = this; } // -------------------------------------------------------------------- static class Item implements AddressableHeap.Handle, Serializable { private final static long serialVersionUID = 1; private HollowNode node; private K key; private V value; public Item(K key, V value) { this.key = key; this.value = value; this.node = null; } @Override public K getKey() { return key; } @Override public V getValue() { return value; } @Override public void setValue(V value) { this.value = value; } @Override public void decreaseKey(K newKey) { checkInvalid(); getOwner().decreaseKey(this, newKey); } @Override public void delete() { checkInvalid(); getOwner().delete(this); } HollowHeap getOwner() { return node.getOwner(); } private void checkInvalid() { if (node == null) { throw new IllegalArgumentException("Invalid handle!"); } } } static class HollowNode implements Serializable { private final static long serialVersionUID = 1; /* * We maintain explicitly the belonging heap, instead of using an inner * class due to possible cascading melding. */ HollowHeap heap; K key; HollowNode child; // child HollowNode next; // next sibling in list of children of first // parent HollowNode sp; // second parent int rank; /* * The item inside the node. If null the node is hollow. */ Item item; HollowNode(HollowHeap heap, K key) { this.heap = heap; this.key = key; this.item = null; this.child = null; this.next = null; this.sp = null; this.rank = 0; } /* * Get the owner heap of the handle. This is union-find with * path-compression between heaps. */ HollowHeap getOwner() { if (heap.other != heap) { // find root HollowHeap root = heap; while (root != root.other) { root = root.other; } // path-compression HollowHeap cur = heap; while (cur.other != root) { HollowHeap next = cur.other; cur.other = root; cur = next; } heap = root; } return heap; } } /** * Decrease the key of an item. * * @param n * the item * @param newKey * the new key */ @SuppressWarnings("unchecked") private void decreaseKey(Item e, K newKey) { assert e.node != null; int c; if (comparator == null) { c = ((Comparable) newKey).compareTo(e.getKey()); } else { c = comparator.compare(newKey, e.getKey()); } if (c > 0) { throw new IllegalArgumentException("Keys can only be decreased!"); } HollowNode u = e.node; if (c == 0 || u == root) { e.key = newKey; u.key = newKey; return; } // move item to new node HollowNode v = new HollowNode(this, newKey); nodes++; u.item = null; v.item = e; e.node = v; e.key = newKey; if (u.rank > 2) { v.rank = u.rank - 2; } v.child = u; u.sp = v; // link new node with root root = link(root, v); } /** * Delete an item * * @param e * the item */ private void delete(Item e) { assert e.node != null; assert e.node.item == e; // delete item e.node.item = null; e.node = null; size--; if (root.item != null) { // non-minimum deletion return; } // minimum deletion int maxRank = -1; while (root != null) { HollowNode v = root; root = root.next; HollowNode w = v.child; while (w != null) { HollowNode u = w; w = w.next; u.next = null; if (u.item == null) { // hollow if (u.sp == null) { // v is the only parent u.next = root; root = u; } else { // two parents if (u.sp == v) { // v is the second parent u.sp = null; } else { // v is the first parent u.sp = null; u.next = null; } } } else { maxRank = Math.max(maxRank, doRankedLinks(u)); } } nodes--; // garbage collect v v.next = null; v.child = null; v.sp = null; v.item = null; } doUnrankedLinks(maxRank); } private int doRankedLinks(HollowNode u) { while (aux[u.rank] != null) { u = link(u, aux[u.rank]); aux[u.rank] = null; u.rank += 1; } aux[u.rank] = u; return u.rank; } private void doUnrankedLinks(int maxRank) { assert root == null; for (int i = 0; i <= maxRank; i++) { HollowNode u = aux[i]; if (u != null) { if (root == null) { root = u; } else { root = link(root, u); } aux[i] = null; } } } @SuppressWarnings("unchecked") private HollowNode link(HollowNode v, HollowNode w) { int c; if (comparator == null) { c = ((Comparable) v.key).compareTo(w.key); } else { c = comparator.compare(v.key, w.key); } if (c > 0) { // v a child of w v.next = w.child; w.child = v; return w; } else { // w a child of v w.next = v.child; v.child = w; return v; } } } jheaps-jheaps-0.14/src/main/java/org/jheaps/dag/package-info.java000066400000000000000000000013321367505655000246120ustar00rootroot00000000000000/* * (C) Copyright 2014-2019, by Dimitrios Michail * * JHeaps Library * * 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. */ /** * Heaps using an explicit "pointer-based" dag representation */ package org.jheaps.dag; jheaps-jheaps-0.14/src/main/java/org/jheaps/monotone/000077500000000000000000000000001367505655000225275ustar00rootroot00000000000000jheaps-jheaps-0.14/src/main/java/org/jheaps/monotone/AbstractRadixAddressableHeap.java000066400000000000000000000322601367505655000310600ustar00rootroot00000000000000/* * (C) Copyright 2014-2018, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.monotone; import java.io.Serializable; import java.util.Comparator; import java.util.NoSuchElementException; import org.jheaps.AddressableHeap; import org.jheaps.annotations.ConstantTime; import org.jheaps.annotations.LogarithmicTime; /** * Base abstract implementation of an addressable radix heap. * * @author Dimitrios Michail * * @param * the type of keys maintained by this heap * @param * the type of values maintained by this heap * */ abstract class AbstractRadixAddressableHeap implements AddressableHeap, Serializable { private static final long serialVersionUID = 1L; /** * Denotes that a key does not belong to a bucket */ protected static final int EMPTY = -1; /** * The buckets as lists. */ protected Node[] buckets; /** * Number of elements */ protected long size; /** * Last deleted key. This value is used to distribute elements in the * buckets. Should be initialized with the {@link #minKey} value. */ protected K lastDeletedKey; /** * The current minimum value (cached) */ protected Node currentMin; /** * Minimum key allowed */ protected K minKey; /** * Maximum key allowed */ protected K maxKey; /** * Constructor */ AbstractRadixAddressableHeap() { } /** * {@inheritDoc} */ @Override @ConstantTime public Handle findMin() { if (size == 0) { throw new NoSuchElementException(); } return currentMin; } /** * {@inheritDoc} * * @throws IllegalArgumentException * if the key is null * @throws IllegalArgumentException * if the key is less than the minimum allowed key * @throws IllegalArgumentException * if the key is more than the maximum allowed key * @throws IllegalArgumentException * if the key is less than the last deleted key (or the minimum * key allowed if no key has been deleted) */ @Override @ConstantTime public Handle insert(K key) { return insert(key, null); } /** * {@inheritDoc} * * @throws IllegalArgumentException * if the key is null * @throws IllegalArgumentException * if the key is less than the minimum allowed key * @throws IllegalArgumentException * if the key is more than the maximum allowed key * @throws IllegalArgumentException * if the key is less than the last deleted key (or the minimum * key allowed if no key has been deleted) */ @Override @ConstantTime public Handle insert(K key, V value) { if (key == null) { throw new IllegalArgumentException("Null keys not permitted"); } if (compare(key, maxKey) > 0) { throw new IllegalArgumentException("Key is more than the maximum allowed key"); } if (compare(key, lastDeletedKey) < 0) { throw new IllegalArgumentException("Invalid key. Monotone heap."); } // add to bucket Node p = new Node(key, value); int b = computeBucket(key, lastDeletedKey); p.bucket = b; if (buckets[b] == null) { buckets[b] = p; } else { buckets[b].prev = p; p.next = buckets[b]; buckets[b] = p; } // update current minimum cache if (currentMin == null || compare(key, currentMin.key) < 0) { currentMin = p; } size++; return p; } /** * {@inheritDoc} * * The cost of this operation is amortized O(logC) assuming the heap * contains keys in the range [0, C] or equivalently [a, a+C]. */ @Override @LogarithmicTime(amortized = true) public Handle deleteMin() { if (size == 0) { throw new NoSuchElementException(); } // updated last deleted key Node result = currentMin; lastDeletedKey = currentMin.key; if (currentMin.bucket == 0) { Node head = buckets[currentMin.bucket]; if (currentMin.next != null) { currentMin.next.prev = currentMin.prev; } if (currentMin.prev != null) { currentMin.prev.next = currentMin.next; } if (head == currentMin) { currentMin.prev = null; buckets[currentMin.bucket] = currentMin.next; } currentMin.next = null; currentMin.prev = null; currentMin.bucket = EMPTY; // update minimum cache currentMin = buckets[0]; if (--size > 0) { findAndCacheMinimum(0); } } else { // redistribute all elements based on new lastDeletedKey Node newMin = null; int currentMinBucket = currentMin.bucket; Node val = buckets[currentMinBucket]; while (val != null) { // remove first from list buckets[currentMinBucket] = val.next; if (buckets[currentMinBucket] != null) { buckets[currentMinBucket].prev = null; } val.next = null; val.prev = null; val.bucket = EMPTY; // redistribute if (val != currentMin) { int b = computeBucket(val.key, lastDeletedKey); assert b < currentMinBucket; val.next = buckets[b]; if (buckets[b] != null) { buckets[b].prev = val; } buckets[b] = val; val.bucket = b; if (newMin == null || compare(val.key, newMin.key) < 0) { newMin = val; } } val = buckets[currentMinBucket]; } // update minimum cache currentMin = newMin; if (--size > 0) { findAndCacheMinimum(currentMinBucket + 1); } } return result; } /** * {@inheritDoc} */ @Override @ConstantTime public boolean isEmpty() { return size == 0; } /** * {@inheritDoc} */ @Override @ConstantTime public long size() { return size; } /** * {@inheritDoc} */ @Override public void clear() { for (int i = 0; i < buckets.length; i++) { buckets[i] = null; } size = 0; lastDeletedKey = minKey; currentMin = null; } /** * Always returns {@code null} since this heap uses the * {@linkplain Comparable natural ordering} of its keys. * * @return {@code null} since this heap uses the natural ordering of its * keys */ @Override public Comparator comparator() { return null; } /** * Compares its two arguments for order. Returns a negative integer, zero, * or a positive integer as the first argument is less than, equal to, or * greater than the second. * * @param o1 * the first object to be compared. * @param o2 * the second object to be compared. * @return a negative integer, zero, or a positive integer as the first * argument is less than, equal to, or greater than the second. */ protected abstract int compare(K o1, K o2); /** * Compute the bucket of a key based on a minimum key. * * @param key * the key * @param minKey * the minimum key * @return the bucket where the key should go */ protected int computeBucket(K key, K minKey) { return 1 + Math.min(msd(key, minKey), buckets.length - 2); } /** * Compute the most significant digit which is different in the binary * representation of two values, or -1 if numbers are equal. * * @param a * the first value * @param b * the second value * @return the most significant digit which is different or -1 if numbers * are equal */ protected abstract int msd(K a, K b); /** * List Node */ protected class Node implements Handle, Serializable { private static final long serialVersionUID = 1L; K key; V value; Node next; Node prev; int bucket; public Node(K key, V value) { this.key = key; this.value = value; this.next = null; this.prev = null; this.bucket = EMPTY; } @Override public K getKey() { return key; } @Override public V getValue() { return value; } @Override public void setValue(V value) { this.value = value; } @Override public void decreaseKey(K newKey) { if (size == 0) { throw new IllegalArgumentException("Invalid handle!"); } if (bucket == EMPTY) { throw new IllegalArgumentException("Invalid handle!"); } if (compare(newKey, lastDeletedKey) < 0) { throw new IllegalArgumentException("Invalid key. Monotone heap."); } int c = compare(newKey, key); if (c > 0) { throw new IllegalArgumentException("Keys can only be decreased!"); } key = newKey; if (c == 0) { return; } // update minimum cache if (this == currentMin || compare(key, currentMin.key) < 0) { currentMin = this; } // find new bucket int newBucket = computeBucket(key, lastDeletedKey); if (newBucket == bucket) { return; } // remove from list Node head = buckets[bucket]; if (next != null) { next.prev = prev; } if (prev != null) { prev.next = next; } if (head == this) { prev = null; buckets[bucket] = next; } // add to new list if (buckets[newBucket] == null) { buckets[newBucket] = this; this.next = null; } else { buckets[newBucket].prev = this; this.next = buckets[newBucket]; buckets[newBucket] = this; } this.prev = null; this.bucket = newBucket; } @Override public void delete() { if (size == 0 || bucket == EMPTY) { throw new IllegalArgumentException("Invalid handle!"); } if (this == currentMin) { deleteMin(); return; } // remove from list Node head = buckets[bucket]; if (next != null) { next.prev = prev; } if (prev != null) { prev.next = next; } if (head == this) { buckets[bucket] = next; } prev = null; next = null; bucket = EMPTY; size--; } } /** * Helper method for finding and caching the minimum. Assumes that the heap * contains at least one element. * * @param firstBucket * start looking for elements from this bucket */ private void findAndCacheMinimum(int firstBucket) { if (currentMin == null) { // find first non-empty bucket int currentMinBucket = EMPTY; for (int i = firstBucket; i < this.buckets.length; i++) { if (buckets[i] != null) { currentMinBucket = i; break; } } // find new minimum and cache it if (currentMinBucket >= 0) { Node val = buckets[currentMinBucket]; while (val != null) { if (currentMin == null || compare(val.key, currentMin.key) < 0) { currentMin = val; } val = val.next; } } } } } jheaps-jheaps-0.14/src/main/java/org/jheaps/monotone/AbstractRadixHeap.java000066400000000000000000000213251367505655000267260ustar00rootroot00000000000000/* * (C) Copyright 2014-2018, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.monotone; import java.io.Serializable; import java.util.Comparator; import java.util.List; import java.util.NoSuchElementException; import org.jheaps.Heap; import org.jheaps.annotations.ConstantTime; import org.jheaps.annotations.LogarithmicTime; /** * Base abstract implementation of a radix heap. * * @author Dimitrios Michail * * @param * the key type */ abstract class AbstractRadixHeap implements Heap, Serializable { private static final long serialVersionUID = 1L; /** * Denotes that a key does not belong to a bucket */ protected static final int EMPTY = -1; /** * The buckets as lists. We use array-lists instead of linked-lists, to be * cache friendly. */ protected List[] buckets; /** * Number of elements */ protected long size; /** * Last deleted key. This value is used to distribute elements in the * buckets. Should be initialized with the {@link #minKey} value. */ protected K lastDeletedKey; /** * The current minimum value (cached) */ protected K currentMin; /** * The current minimum value bucket (cached) */ protected int currentMinBucket; /** * The current minimum value position in bucket (cached) */ protected int currentMinPos; /** * Minimum key allowed */ protected K minKey; /** * Maximum key allowed */ protected K maxKey; /** * Constructor */ AbstractRadixHeap() { } /** * {@inheritDoc} */ @Override @ConstantTime public K findMin() { if (size == 0) { throw new NoSuchElementException(); } return currentMin; } /** * {@inheritDoc} * * @throws IllegalArgumentException * if the key is null * @throws IllegalArgumentException * if the key is less than the minimum allowed key * @throws IllegalArgumentException * if the key is more than the maximum allowed key * @throws IllegalArgumentException * if the key is less than the last deleted key (or the minimum * key allowed if no key has been deleted) */ @Override @ConstantTime(amortized = true) public void insert(K key) { if (key == null) { throw new IllegalArgumentException("Null keys not permitted"); } if (compare(key, maxKey) > 0) { throw new IllegalArgumentException("Key is more than the maximum allowed key"); } if (compare(key, lastDeletedKey) < 0) { throw new IllegalArgumentException("Invalid key. Monotone heap."); } int b = computeBucket(key, lastDeletedKey); buckets[b].add(key); // update current minimum cache if (currentMin == null || compare(key, currentMin) < 0) { currentMin = key; currentMinBucket = b; currentMinPos = buckets[b].size() - 1; } size++; } /** * {@inheritDoc} * * The cost of this operation is amortized O(logC) assuming the heap * contains keys in the range [0, C] or equivalently [a, a+C]. */ @Override @LogarithmicTime(amortized = true) public K deleteMin() { if (size == 0) { throw new NoSuchElementException(); } // updated last deleted key lastDeletedKey = currentMin; if (currentMinBucket == 0) { buckets[currentMinBucket].remove(currentMinPos); // update minimum cache currentMin = null; currentMinBucket = EMPTY; currentMinPos = EMPTY; if (--size > 0) { findAndCacheMinimum(0); } } else { K newMin = null; int newMinBucket = EMPTY; int newMinPos = EMPTY; // redistribute all elements based on new lastDeletedKey int pos = 0; for (K val : buckets[currentMinBucket]) { if (pos != currentMinPos) { int b = computeBucket(val, lastDeletedKey); assert b < currentMinBucket; buckets[b].add(val); if (newMin == null || compare(val, newMin) < 0) { newMin = val; newMinBucket = b; newMinPos = buckets[b].size() - 1; } } ++pos; } buckets[currentMinBucket].clear(); // update minimum cache currentMin = newMin; currentMinBucket = newMinBucket; currentMinPos = newMinPos; if (--size > 0) { findAndCacheMinimum(currentMinBucket + 1); } } return lastDeletedKey; } /** * {@inheritDoc} */ @Override @ConstantTime public boolean isEmpty() { return size == 0; } /** * {@inheritDoc} */ @Override @ConstantTime public long size() { return size; } /** * {@inheritDoc} */ @Override public void clear() { for (List bucket : buckets) { bucket.clear(); } size = 0; lastDeletedKey = minKey; currentMin = null; currentMinBucket = EMPTY; currentMinPos = EMPTY; } /** * Always returns {@code null} since this heap uses the * {@linkplain Comparable natural ordering} of its keys. * * @return {@code null} since this heap uses the natural ordering of its * keys */ @Override public Comparator comparator() { return null; } /** * Compares its two arguments for order. Returns a negative integer, zero, * or a positive integer as the first argument is less than, equal to, or * greater than the second. * * @param o1 * the first object to be compared. * @param o2 * the second object to be compared. * @return a negative integer, zero, or a positive integer as the first * argument is less than, equal to, or greater than the second. */ protected abstract int compare(K o1, K o2); /** * Compute the bucket of a key based on a minimum key. * * @param key * the key * @param minKey * the minimum key * @return the bucket where the key should go */ protected int computeBucket(K key, K minKey) { return 1 + Math.min(msd(key, minKey), buckets.length - 2); } /** * Compute the most significant digit which is different in the binary * representation of two values, or -1 if numbers are equal. * * @param a * the first value * @param b * the second value * @return the most significant digit which is different or -1 if numbers * are equal */ protected abstract int msd(K a, K b); /** * Helper method for finding and caching the minimum. Assumes that the heap * contains at least one element. * * @param firstBucket * start looking for elements from this bucket */ private void findAndCacheMinimum(int firstBucket) { if (currentMin == null) { // find first non-empty bucket currentMinBucket = EMPTY; for (int i = firstBucket; i < this.buckets.length; i++) { if (!buckets[i].isEmpty()) { currentMinBucket = i; break; } } // find new minimum and its position (beware of cached values) currentMinPos = EMPTY; if (currentMinBucket >= 0) { int pos = 0; for (K val : buckets[currentMinBucket]) { if (currentMin == null || compare(val, currentMin) < 0) { currentMin = val; currentMinPos = pos; } ++pos; } } } } } jheaps-jheaps-0.14/src/main/java/org/jheaps/monotone/BigIntegerRadixAddressableHeap.java000066400000000000000000000105721367505655000313360ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.monotone; import java.lang.reflect.Array; import java.math.BigInteger; import org.jheaps.AddressableHeap; /** * An addressable radix heap for {@link BigInteger} keys. The heap stores * {@link BigInteger} keys sorted according to the {@linkplain Comparable * natural ordering} of its keys. A radix heap is a monotone heap, especially * designed for algorithms (such as Dijkstra) which scan elements in order of * nondecreasing keys. * *

* The implementation use arrays in order to store the elements. Operations * {@code insert} and {@code findMin} are worst-case constant time. The cost of * operation {@code deleteMin} is amortized O(logC) assuming the radix-heap * contains keys in the range {@literal [0, C]} or equivalently * {@literal [a,a+C]}. * *

* Note that this implementation is not synchronized. If * multiple threads access a heap concurrently, and at least one of the threads * modifies the heap structurally, it must be synchronized externally. * (A structural modification is any operation that adds or deletes one or more * elements or changing the key of some element.) This is typically accomplished * by synchronizing on some object that naturally encapsulates the heap. * * @author Dimitrios Michail * * @param * the type of values maintained by this heap * * @see AddressableHeap */ public class BigIntegerRadixAddressableHeap extends AbstractRadixAddressableHeap { private final static long serialVersionUID = 1; /** * Constructs a new heap which can store values between a minimum and a * maximum key value (inclusive). * * It is important to use the smallest key range as the heap uses O(logC) * where C=maxKey-minKey+1 buckets to store elements. Moreover, the * operation {@code deleteMin} requires amortized O(logC) time. * * @param minKey * the non-negative minimum key that this heap supports * (inclusive) * @param maxKey * the maximum key that this heap supports (inclusive) * @throws IllegalArgumentException * if the minimum key is negative * @throws IllegalArgumentException * if the maximum key is less than the minimum key */ @SuppressWarnings("unchecked") public BigIntegerRadixAddressableHeap(BigInteger minKey, BigInteger maxKey) { super(); if (minKey == null) { throw new IllegalArgumentException("Minimum key cannot be null"); } if (minKey.compareTo(BigInteger.ZERO) < 0) { throw new IllegalArgumentException("Minimum key must be non-negative"); } this.minKey = minKey; this.lastDeletedKey = minKey; if (maxKey == null) { throw new IllegalArgumentException("Maximum key cannot be null"); } if (maxKey.compareTo(minKey) < 0) { throw new IllegalArgumentException("Maximum key cannot be less than the minimum"); } this.maxKey = maxKey; // compute number of buckets BigInteger diff = maxKey.subtract(minKey); int numBuckets = 2 + 1 + diff.bitLength(); // construct representation this.buckets = (Node[]) Array.newInstance(Node.class, numBuckets); this.size = 0; this.currentMin = null; } /** * {@inheritDoc} */ @Override protected int compare(BigInteger o1, BigInteger o2) { return o1.compareTo(o2); } /** * {@inheritDoc} */ @Override protected int msd(BigInteger a, BigInteger b) { if (a.equals(b)) { return -1; } /* * return floor(log_2(a xor b)). */ return a.xor(b).bitLength() - 1; } } jheaps-jheaps-0.14/src/main/java/org/jheaps/monotone/BigIntegerRadixHeap.java000066400000000000000000000105711367505655000272030ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.monotone; import java.lang.reflect.Array; import java.math.BigInteger; import java.util.ArrayList; import java.util.List; /** * A radix heap for {@link BigInteger} keys. The heap stores {@link BigInteger} * keys sorted according to the {@linkplain Comparable natural ordering} of its * keys. A radix heap is a monotone heap, especially designed for algorithms * (such as Dijkstra) which scan elements in order of nondecreasing keys. * *

* The implementation uses arrays in order to store the elements. Operations * {@code insert} and {@code findMin} are worst-case constant time. The cost of * operation {@code deleteMin} is amortized O(logC) assuming the radix-heap * contains keys in the range {@literal [0, C]} or equivalently * {@literal [a,a+C]}. * *

* Note that this implementation is not synchronized. If * multiple threads access a heap concurrently, and at least one of the threads * modifies the heap structurally, it must be synchronized externally. * (A structural modification is any operation that adds or deletes one or more * elements or changing the key of some element.) This is typically accomplished * by synchronizing on some object that naturally encapsulates the heap. * * @author Dimitrios Michail */ public class BigIntegerRadixHeap extends AbstractRadixHeap { private final static long serialVersionUID = 1; /** * Constructs a new heap which can store values between a minimum and a * maximum key value (inclusive). * * It is important to use the smallest key range as the heap uses O(logC) * where C=maxKey-minKey+1 buckets to store elements. Moreover, the * operation {@code deleteMin} requires amortized O(logC) time. * * @param minKey * the non-negative minimum key that this heap supports * (inclusive) * @param maxKey * the maximum key that this heap supports (inclusive) * @throws IllegalArgumentException * if the minimum key is negative * @throws IllegalArgumentException * if the maximum key is less than the minimum key */ @SuppressWarnings("unchecked") public BigIntegerRadixHeap(BigInteger minKey, BigInteger maxKey) { super(); if (minKey == null) { throw new IllegalArgumentException("Minimum key cannot be null"); } if (minKey.compareTo(BigInteger.ZERO) < 0) { throw new IllegalArgumentException("Minimum key must be non-negative"); } this.minKey = minKey; this.lastDeletedKey = minKey; if (maxKey == null) { throw new IllegalArgumentException("Maximum key cannot be null"); } if (maxKey.compareTo(minKey) < 0) { throw new IllegalArgumentException("Maximum key cannot be less than the minimum"); } this.maxKey = maxKey; // compute number of buckets BigInteger diff = maxKey.subtract(minKey); int numBuckets = 2 + 1 + diff.bitLength(); // construct representation this.buckets = (List[]) Array.newInstance(List.class, numBuckets); for (int i = 0; i < this.buckets.length; i++) { buckets[i] = new ArrayList(); } this.size = 0; this.currentMin = null; } /** * {@inheritDoc} */ @Override protected int compare(BigInteger o1, BigInteger o2) { return o1.compareTo(o2); } /** * {@inheritDoc} */ @Override protected int msd(BigInteger a, BigInteger b) { if (a.equals(b)) { return -1; } /* * return floor(log_2(a xor b)). */ return a.xor(b).bitLength() - 1; } } jheaps-jheaps-0.14/src/main/java/org/jheaps/monotone/DoubleRadixAddressableHeap.java000066400000000000000000000126361367505655000305340ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.monotone; import java.io.Serializable; import java.lang.reflect.Array; import java.math.BigInteger; /** * An addressable radix heap for double keys. The heap stores double keys sorted * according to the {@linkplain Comparable natural ordering} of its keys. A * radix heap is a monotone heap, especially designed for algorithms (such as * Dijkstra) which scan elements in order of nondecreasing keys. * *

* Note that this implementation uses the fact that the IEEE floating-point * standard has the property that for any valid floating-point numbers a and b, * {@literal a<=b} if and only if {@literal bits(a)<= bits(b)}, where * {@literal bits(x)} denotes the re-interpretation of x as an unsigned integer * (long in our case). * *

* The implementation use arrays in order to store the elements. Operations * {@code insert} and {@code findMin} are worst-case constant time. The cost of * operation {@code deleteMin} is amortized O(logC) assuming the radix-heap * contains keys in the range {@literal [0, C]} or equivalently * {@literal [a,a+C]}. Note, however, that C here depends on the distance of the * minimum and maximum value when they are translated into unsigned longs. * *

* Note that this implementation is not synchronized. If * multiple threads access a heap concurrently, and at least one of the threads * modifies the heap structurally, it must be synchronized externally. * (A structural modification is any operation that adds or deletes one or more * elements or changing the key of some element.) This is typically accomplished * by synchronizing on some object that naturally encapsulates the heap. * * @author Dimitrios Michail * * @param * the type of values maintained by this heap * * @see Serializable */ public class DoubleRadixAddressableHeap extends AbstractRadixAddressableHeap { private final static long serialVersionUID = 1; /** * Constructs a new heap which can store values between a minimum and a * maximum key value (inclusive). * * It is important to use the smallest key range as the heap uses O(logC) * where C=maxKey-minKey+1 buckets to store elements. Moreover, the * operation {@code deleteMin} requires amortized O(logC) time. * * @param minKey * the non-negative minimum key that this heap supports * (inclusive) * @param maxKey * the maximum key that this heap supports (inclusive) * @throws IllegalArgumentException * if the minimum key is negative * @throws IllegalArgumentException * if the maximum key is less than the minimum key */ @SuppressWarnings("unchecked") public DoubleRadixAddressableHeap(double minKey, double maxKey) { super(); if (!Double.isFinite(minKey) || minKey < 0.0) { throw new IllegalArgumentException("Minimum key must be finite and non-negative"); } this.minKey = minKey; this.lastDeletedKey = minKey; if (!Double.isFinite(maxKey) || maxKey < minKey) { throw new IllegalArgumentException("Maximum key must be finite and not less than the minimum"); } this.maxKey = maxKey; // compute number of buckets BigInteger minKeyAsBigInt = UnsignedUtils.unsignedLongToBigInt(Double.doubleToLongBits(minKey)); BigInteger maxKeyAsBigInt = UnsignedUtils.unsignedLongToBigInt(Double.doubleToLongBits(maxKey)); BigInteger diff = maxKeyAsBigInt.subtract(minKeyAsBigInt); int numBuckets = 2 + 1 + diff.bitLength(); // construct representation this.buckets = (Node[]) Array.newInstance(Node.class, numBuckets); this.size = 0; this.currentMin = null; } /** * {@inheritDoc} */ @Override protected int compare(Double o1, Double o2) { /* * Convert to IEEE and compare as unsigned */ long x = Double.doubleToLongBits(o1) ^ Long.MIN_VALUE; long y = Double.doubleToLongBits(o2) ^ Long.MIN_VALUE; // assert if (o1.doubleValue() < o2.doubleValue()) { assert x < y; } else if (o1.doubleValue() == o2.doubleValue()) { assert x == y; } else { assert x > y; } return (x < y) ? -1 : ((x == y) ? 0 : 1); } /** * {@inheritDoc} */ @Override protected int msd(Double a, Double b) { /* * For this to work, arithmetic must be unsigned */ long ux = Double.doubleToLongBits(a); long uy = Double.doubleToLongBits(b); if (ux == uy) { return -1; } double d = UnsignedUtils.unsignedLongToDouble(ux ^ uy); return Math.getExponent(d); } } jheaps-jheaps-0.14/src/main/java/org/jheaps/monotone/DoubleRadixHeap.java000066400000000000000000000122541367505655000263760ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.monotone; import java.lang.reflect.Array; import java.math.BigInteger; import java.util.ArrayList; import java.util.List; /** * A radix heap for double keys. The heap stores double keys sorted according to * the {@linkplain Comparable natural ordering} of its keys. A radix heap is a * monotone heap, especially designed for algorithms (such as Dijkstra) which * scan elements in order of nondecreasing keys. * *

* Note that this implementation uses the fact that the IEEE floating-point * standard has the property that for any valid floating-point numbers a and b, * {@literal a<=b} if and only if {@literal bits(a)<= bits(b)}, where * {@literal bits(x)} denotes the re-interpretation of x as an unsigned integer * (long in our case). * *

* This implementation uses arrays in order to store the elements. Operations * {@code insert} and {@code findMin} are worst-case constant time. The cost of * operation {@code deleteMin} is amortized O(logC) assuming the radix-heap * contains keys in the range {@literal [0, C]} or equivalently * {@literal [a,a+C]}. Note, however, that C here depends on the distance of the * minimum and maximum value when they are translated into unsigned longs. * *

* Note that this implementation is not synchronized. If * multiple threads access a heap concurrently, and at least one of the threads * modifies the heap structurally, it must be synchronized externally. * (A structural modification is any operation that adds or deletes one or more * elements or changing the key of some element.) This is typically accomplished * by synchronizing on some object that naturally encapsulates the heap. * * @author Dimitrios Michail */ public class DoubleRadixHeap extends AbstractRadixHeap { private final static long serialVersionUID = 1; /** * Constructs a new heap which can store values between a minimum and a * maximum key value (inclusive). * * It is important to use the smallest key range as the heap uses O(logC) * where C=maxKey-minKey+1 buckets to store elements. Moreover, the * operation {@code deleteMin} requires amortized O(logC) time. * * @param minKey * the non-negative minimum key that this heap supports * (inclusive) * @param maxKey * the maximum key that this heap supports (inclusive) * @throws IllegalArgumentException * if the minimum key is negative * @throws IllegalArgumentException * if the maximum key is less than the minimum key */ @SuppressWarnings("unchecked") public DoubleRadixHeap(double minKey, double maxKey) { super(); if (!Double.isFinite(minKey) || minKey < 0.0) { throw new IllegalArgumentException("Minimum key must be finite and non-negative"); } this.minKey = minKey; this.lastDeletedKey = minKey; if (!Double.isFinite(maxKey) || maxKey < minKey) { throw new IllegalArgumentException("Maximum key must be finite and not less than the minimum"); } this.maxKey = maxKey; // compute number of buckets BigInteger minKeyAsBigInt = UnsignedUtils.unsignedLongToBigInt(Double.doubleToLongBits(minKey)); BigInteger maxKeyAsBigInt = UnsignedUtils.unsignedLongToBigInt(Double.doubleToLongBits(maxKey)); BigInteger diff = maxKeyAsBigInt.subtract(minKeyAsBigInt); int numBuckets = 2 + 1 + diff.bitLength(); // construct representation this.buckets = (List[]) Array.newInstance(List.class, numBuckets); for (int i = 0; i < this.buckets.length; i++) { buckets[i] = new ArrayList(); } this.size = 0; this.currentMin = null; } /** * {@inheritDoc} */ @Override protected int compare(Double o1, Double o2) { /* * Convert to IEEE and compare as unsigned */ long x = Double.doubleToLongBits(o1) ^ Long.MIN_VALUE; long y = Double.doubleToLongBits(o2) ^ Long.MIN_VALUE; return (x < y) ? -1 : ((x == y) ? 0 : 1); } /** * {@inheritDoc} */ @Override protected int msd(Double a, Double b) { /* * For this to work, arithmetic must be unsigned */ long ux = Double.doubleToLongBits(a); long uy = Double.doubleToLongBits(b); if (ux == uy) { return -1; } double d = UnsignedUtils.unsignedLongToDouble(ux ^ uy); return Math.getExponent(d); } } jheaps-jheaps-0.14/src/main/java/org/jheaps/monotone/IntegerRadixAddressableHeap.java000066400000000000000000000106461367505655000307160ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.monotone; import java.io.Serializable; import java.lang.reflect.Array; /** * An addressable radix heap for (signed) integer keys. The heap stores integer * keys sorted according to the {@linkplain Comparable natural ordering} of its * keys. A radix heap is a monotone heap, especially designed for algorithms * (such as Dijkstra) which scan elements in order of nondecreasing keys. * *

* The implementation uses arrays in order to store the elements. Operations * {@code insert} and {@code findMin} are worst-case constant time. The cost of * operation {@code deleteMin} is amortized O(logC) assuming the radix-heap * contains keys in the range {@literal [0, C]} or equivalently * {@literal [a,a+C]}. This implementation views integer values as signed * numbers. * *

* Note that this implementation is not synchronized. If * multiple threads access a heap concurrently, and at least one of the threads * modifies the heap structurally, it must be synchronized externally. * (A structural modification is any operation that adds or deletes one or more * elements or changing the key of some element.) This is typically accomplished * by synchronizing on some object that naturally encapsulates the heap. * * @author Dimitrios Michail * * @param * the type of values maintained by this heap * * @see Serializable */ public class IntegerRadixAddressableHeap extends AbstractRadixAddressableHeap { private static final long serialVersionUID = 1; /** * Constructs a new heap which can store values between a minimum and a * maximum key value (inclusive). * * It is important to use the smallest key range as the heap uses O(logC) * where C=maxKey-minKey+1 buckets to store elements. Moreover, the * operation {@code deleteMin} requires amortized O(logC) time. * * @param minKey * the non-negative minimum key that this heap supports * (inclusive) * @param maxKey * the maximum key that this heap supports (inclusive) * @throws IllegalArgumentException * if the minimum key is negative * @throws IllegalArgumentException * if the maximum key is less than the minimum key */ @SuppressWarnings("unchecked") public IntegerRadixAddressableHeap(int minKey, int maxKey) { super(); if (minKey < 0) { throw new IllegalArgumentException("Minimum key must be non-negative"); } this.minKey = minKey; this.lastDeletedKey = minKey; if (maxKey < minKey) { throw new IllegalArgumentException("Maximum key cannot be less than the minimum"); } this.maxKey = maxKey; // compute number of buckets int numBuckets; if (maxKey == minKey) { numBuckets = 2; } else { numBuckets = 2 + 1 + (int) Math.floor(Math.log((double)maxKey - minKey) / Math.log(2)); } // construct representation this.buckets = (Node[]) Array.newInstance(Node.class, numBuckets); this.size = 0; this.currentMin = null; } /** * {@inheritDoc} */ @Override protected int compare(Integer o1, Integer o2) { if (o1 < o2) { return -1; } else if (o1 > o2) { return 1; } else { return 0; } } /** * {@inheritDoc} */ @Override protected int msd(Integer a, Integer b) { /* * Value equal */ if (a.intValue() == b.intValue()) { return -1; } /* * This is a fast way to compute floor(log_2(a xor b)). */ float axorb = a ^ b; return Math.getExponent(axorb); } } jheaps-jheaps-0.14/src/main/java/org/jheaps/monotone/IntegerRadixHeap.java000066400000000000000000000106211367505655000265550ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.monotone; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.List; /** * A radix heap for (signed) integer keys. The heap stores integer keys sorted * according to the {@linkplain Comparable natural ordering} of its keys. A * radix heap is a monotone heap, especially designed for algorithms (such as * Dijkstra) which scan elements in order of nondecreasing keys. * *

* This implementation uses arrays in order to store the elements. Operations * {@code insert} and {@code findMin} are worst-case constant time. The cost of * operation {@code deleteMin} is amortized O(logC) assuming the radix-heap * contains keys in the range {@literal [0, C]} or equivalently * {@literal [a,a+C]}. Integer values are viewed as signed numbers. * *

* Note that this implementation is not synchronized. If * multiple threads access a heap concurrently, and at least one of the threads * modifies the heap structurally, it must be synchronized externally. * (A structural modification is any operation that adds or deletes one or more * elements or changing the key of some element.) This is typically accomplished * by synchronizing on some object that naturally encapsulates the heap. * * @author Dimitrios Michail */ public class IntegerRadixHeap extends AbstractRadixHeap { private static final long serialVersionUID = 1; /** * Constructs a new heap which can store values between a minimum and a * maximum key value (inclusive). * * It is important to use the smallest key range as the heap uses O(logC) * where C=maxKey-minKey+1 buckets to store elements. Moreover, the * operation {@code deleteMin} requires amortized O(logC) time. * * @param minKey * the non-negative minimum key that this heap supports * (inclusive) * @param maxKey * the maximum key that this heap supports (inclusive) * @throws IllegalArgumentException * if the minimum key is negative * @throws IllegalArgumentException * if the maximum key is less than the minimum key */ @SuppressWarnings("unchecked") public IntegerRadixHeap(int minKey, int maxKey) { super(); if (minKey < 0) { throw new IllegalArgumentException("Minimum key must be non-negative"); } this.minKey = minKey; this.lastDeletedKey = minKey; if (maxKey < minKey) { throw new IllegalArgumentException("Maximum key cannot be less than the minimum"); } this.maxKey = maxKey; // compute number of buckets int numBuckets; if (maxKey == minKey) { numBuckets = 2; } else { numBuckets = 2 + 1 + (int) Math.floor(Math.log((double)maxKey - minKey) / Math.log(2)); } // construct representation this.buckets = (List[]) Array.newInstance(List.class, numBuckets); for (int i = 0; i < this.buckets.length; i++) { buckets[i] = new ArrayList(); } this.size = 0; this.currentMin = null; } /** * {@inheritDoc} */ @Override protected int compare(Integer o1, Integer o2) { if (o1 < o2) { return -1; } else if (o1 > o2) { return 1; } else { return 0; } } /** * {@inheritDoc} */ @Override protected int msd(Integer a, Integer b) { /* * Value equal */ if (a.intValue() == b.intValue()) { return -1; } /* * This is a fast way to compute floor(log_2(a xor b)). */ float axorb = a ^ b; return Math.getExponent(axorb); } } jheaps-jheaps-0.14/src/main/java/org/jheaps/monotone/LongRadixAddressableHeap.java000066400000000000000000000105161367505655000302140ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.monotone; import java.lang.reflect.Array; /** * An addressable radix heap for (signed) long keys. The heap stores long keys * sorted according to the {@linkplain Comparable natural ordering} of its keys. * A radix heap is a monotone heap, especially designed for algorithms (such as * Dijkstra) which scan elements in order of nondecreasing keys. * *

* The implementation uses arrays in order to store the elements. Operations * {@code insert} and {@code findMin} are worst-case constant time. The cost of * operation {@code deleteMin} is amortized O(logC) assuming the radix-heap * contains keys in the range {@literal [0, C]} or equivalently * {@literal [a,a+C]}. This implementation views long values as signed numbers. * *

* Note that this implementation is not synchronized. If * multiple threads access a heap concurrently, and at least one of the threads * modifies the heap structurally, it must be synchronized externally. * (A structural modification is any operation that adds or deletes one or more * elements or changing the key of some element.) This is typically accomplished * by synchronizing on some object that naturally encapsulates the heap. * * @author Dimitrios Michail * * @param * the type of values maintained by this heap * */ public class LongRadixAddressableHeap extends AbstractRadixAddressableHeap { private static final long serialVersionUID = 1; /** * Constructs a new heap which can store values between a minimum and a * maximum key value (inclusive). * * It is important to use the smallest key range as the heap uses O(logC) * where C=maxKey-minKey+1 buckets to store elements. Moreover, the * operation {@code deleteMin} requires amortized O(logC) time. * * @param minKey * the non-negative minimum key that this heap supports * (inclusive) * @param maxKey * the maximum key that this heap supports (inclusive) * @throws IllegalArgumentException * if the minimum key is negative * @throws IllegalArgumentException * if the maximum key is less than the minimum key */ @SuppressWarnings("unchecked") public LongRadixAddressableHeap(long minKey, long maxKey) { super(); if (minKey < 0) { throw new IllegalArgumentException("Minimum key must be non-negative"); } this.minKey = minKey; this.lastDeletedKey = minKey; if (maxKey < minKey) { throw new IllegalArgumentException("Maximum key cannot be less than the minimum"); } this.maxKey = maxKey; // compute number of buckets int numBuckets; if (maxKey == minKey) { numBuckets = 2; } else { numBuckets = 2 + 1 + (int) Math.floor(Math.log((double)maxKey - minKey) / Math.log(2)); } // construct representation this.buckets = (Node[]) Array.newInstance(Node.class, numBuckets); this.size = 0; this.currentMin = null; } /** * {@inheritDoc} */ @Override protected int compare(Long o1, Long o2) { if (o1 < o2) { return -1; } else if (o1 > o2) { return 1; } else { return 0; } } /** * {@inheritDoc} */ @Override protected int msd(Long a, Long b) { /* * Value equal */ if (a.longValue() == b.longValue()) { return -1; } /* * This is a fast way to compute floor(log_2(a xor b)). */ double axorb = a ^ b; return Math.getExponent(axorb); } } jheaps-jheaps-0.14/src/main/java/org/jheaps/monotone/LongRadixHeap.java000066400000000000000000000105621367505655000260630ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.monotone; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.List; /** * A radix heap for (signed) long keys. The heap stores long keys sorted * according to the {@linkplain Comparable natural ordering} of its keys. A * radix heap is a monotone heap, especially designed for algorithms (such as * Dijkstra) which scan elements in order of nondecreasing keys. * *

* This implementation uses arrays in order to store the elements. Operations * {@code insert} and {@code findMin} are worst-case constant time. The cost of * operation {@code deleteMin} is amortized O(logC) assuming the radix-heap * contains keys in the range {@literal [0, C]} or equivalently * {@literal [a,a+C]}. Long values are viewed as signed numbers. * *

* Note that this implementation is not synchronized. If * multiple threads access a heap concurrently, and at least one of the threads * modifies the heap structurally, it must be synchronized externally. * (A structural modification is any operation that adds or deletes one or more * elements or changing the key of some element.) This is typically accomplished * by synchronizing on some object that naturally encapsulates the heap. * * @author Dimitrios Michail */ public class LongRadixHeap extends AbstractRadixHeap { private static final long serialVersionUID = 1; /** * Constructs a new heap which can store values between a minimum and a * maximum key value (inclusive). * * It is important to use the smallest key range as the heap uses O(logC) * where C=maxKey-minKey+1 buckets to store elements. Moreover, the * operation {@code deleteMin} requires amortized O(logC) time. * * @param minKey * the non-negative minimum key that this heap supports * (inclusive) * @param maxKey * the maximum key that this heap supports (inclusive) * @throws IllegalArgumentException * if the minimum key is negative * @throws IllegalArgumentException * if the maximum key is less than the minimum key */ @SuppressWarnings("unchecked") public LongRadixHeap(long minKey, long maxKey) { super(); if (minKey < 0) { throw new IllegalArgumentException("Minimum key must be non-negative"); } this.minKey = minKey; this.lastDeletedKey = minKey; if (maxKey < minKey) { throw new IllegalArgumentException("Maximum key cannot be less than the minimum"); } this.maxKey = maxKey; // compute number of buckets int numBuckets; if (maxKey == minKey) { numBuckets = 2; } else { numBuckets = 2 + 1 + (int) Math.floor(Math.log((double)maxKey - minKey) / Math.log(2)); } // construct representation this.buckets = (List[]) Array.newInstance(List.class, numBuckets); for (int i = 0; i < this.buckets.length; i++) { buckets[i] = new ArrayList(); } this.size = 0; this.currentMin = null; } /** * {@inheritDoc} */ @Override protected int compare(Long o1, Long o2) { if (o1 < o2) { return -1; } else if (o1 > o2) { return 1; } else { return 0; } } /** * {@inheritDoc} */ @Override protected int msd(Long a, Long b) { /* * Value equal */ if (a.longValue() == b.longValue()) { return -1; } /* * This is a fast way to compute floor(log_2(a xor b)). */ double axorb = a ^ b; return Math.getExponent(axorb); } } jheaps-jheaps-0.14/src/main/java/org/jheaps/monotone/UnsignedUtils.java000066400000000000000000000024461367505655000261750ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.monotone; import java.math.BigInteger; /** * Utilities for unsigned computation * * @author Dimitrios Michail */ class UnsignedUtils { UnsignedUtils() { } private static final long UNSIGNED_MASK = 0x7fffffffffffffffL; static double unsignedLongToDouble(long x) { double d = (double) (x & UNSIGNED_MASK); if (x < 0) { d += 0x1.0p63; } return d; } static BigInteger unsignedLongToBigInt(long x) { BigInteger asBigInt = BigInteger.valueOf(x & UNSIGNED_MASK); if (x < 0) { asBigInt = asBigInt.setBit(Long.SIZE - 1); } return asBigInt; } } jheaps-jheaps-0.14/src/main/java/org/jheaps/monotone/package-info.java000066400000000000000000000012641367505655000257210ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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. */ /** * Monotone heaps */ package org.jheaps.monotone; jheaps-jheaps-0.14/src/main/java/org/jheaps/package-info.java000066400000000000000000000012471367505655000240640ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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. */ /** * Interfaces */ package org.jheaps; jheaps-jheaps-0.14/src/main/java/org/jheaps/tree/000077500000000000000000000000001367505655000216305ustar00rootroot00000000000000jheaps-jheaps-0.14/src/main/java/org/jheaps/tree/BinaryTreeAddressableHeap.java000066400000000000000000000406131367505655000274730ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.tree; import java.io.Serializable; import java.util.BitSet; import java.util.Comparator; import java.util.NoSuchElementException; import org.jheaps.AddressableHeap; import org.jheaps.annotations.ConstantTime; import org.jheaps.annotations.LogarithmicTime; /** * An explicit binary tree addressable heap. The heap is sorted according to the * {@linkplain Comparable natural ordering} of its keys, or by a * {@link Comparator} provided at heap creation time, depending on which * constructor is used. * *

* The worst-case cost of {@code insert}, {@code deleteMin}, {@code delete} and * {@code decreaceKey} operations is O(log(n)) and the cost of {@code findMin} * is O(1). * *

* Note that the ordering maintained by a binary heap, like any heap, and * whether or not an explicit comparator is provided, must be consistent * with {@code equals} if this heap is to correctly implement the * {@code Heap} interface. (See {@code Comparable} or {@code Comparator} for a * precise definition of consistent with equals.) This is so because * the {@code Heap} interface is defined in terms of the {@code equals} * operation, but a binary heap performs all key comparisons using its * {@code compareTo} (or {@code compare}) method, so two keys that are deemed * equal by this method are, from the standpoint of the binary heap, equal. The * behavior of a heap is well-defined even if its ordering is * inconsistent with {@code equals}; it just fails to obey the general contract * of the {@code Heap} interface. * *

* Note that this implementation is not synchronized. If * multiple threads access a heap concurrently, and at least one of the threads * modifies the heap structurally, it must be synchronized externally. * (A structural modification is any operation that adds or deletes one or more * elements or changing the key of some element.) This is typically accomplished * by synchronizing on some object that naturally encapsulates the heap. * * @param * the type of keys maintained by this heap * @param * the type of values maintained by this heap * * @author Dimitrios Michail * * @see AddressableHeap * @see Comparable * @see Comparator */ public class BinaryTreeAddressableHeap implements AddressableHeap, Serializable { private final static long serialVersionUID = 1; /** * The comparator used to maintain order in this heap, or null if it uses * the natural ordering of its keys. * * @serial */ private final Comparator comparator; /** * Size of the heap */ private long size; /** * Root node of the heap */ private Node root; /** * Constructs a new, empty heap, using the natural ordering of its keys. * *

* All keys inserted into the heap must implement the {@link Comparable} * interface. Furthermore, all such keys must be mutually * comparable: {@code k1.compareTo(k2)} must not throw a * {@code ClassCastException} for any keys {@code k1} and {@code k2} in the * heap. If the user attempts to put a key into the heap that violates this * constraint (for example, the user attempts to put a string key into a * heap whose keys are integers), the {@code insert(Object key)} call will * throw a {@code ClassCastException}. */ public BinaryTreeAddressableHeap() { this(null); } /** * Constructs a new, empty heap, ordered according to the given comparator. * *

* All keys inserted into the heap must be mutually comparable by * the given comparator: {@code comparator.compare(k1, * k2)} must not throw a {@code ClassCastException} for any keys {@code k1} * and {@code k2} in the heap. If the user attempts to put a key into the * heap that violates this constraint, the {@code insert(Object key)} call * will throw a {@code ClassCastException}. * * @param comparator * the comparator that will be used to order this heap. If * {@code null}, the {@linkplain Comparable natural ordering} of * the keys will be used. */ public BinaryTreeAddressableHeap(Comparator comparator) { this.comparator = comparator; this.size = 0; this.root = null; } /** * {@inheritDoc} */ @Override @LogarithmicTime public AddressableHeap.Handle insert(K key) { return insert(key, null); } /** * {@inheritDoc} */ @Override @LogarithmicTime @SuppressWarnings("unchecked") public AddressableHeap.Handle insert(K key, V value) { if (key == null) { throw new NullPointerException("Null keys not permitted"); } Node n = new Node(key, value); // easy special cases if (size == 0) { root = n; size = 1; return n; } else if (size == 1) { int c; if (comparator == null) { c = ((Comparable) key).compareTo(root.key); } else { c = comparator.compare(key, root.key); } if (c < 0) { n.o_c = root; root.y_s = n; root = n; } else { root.o_c = n; n.y_s = root; } size = 2; return n; } // find parent of last node and hang Node p = findParentNode(size + 1); if (p.o_c == null) { p.o_c = n; } else { p.o_c.y_s = n; } n.y_s = p; // increase size size++; // fix priorities fixup(n); return n; } /** * {@inheritDoc} */ @Override @ConstantTime public AddressableHeap.Handle findMin() { if (size == 0) { throw new NoSuchElementException(); } return root; } /** * {@inheritDoc} */ @Override @LogarithmicTime public AddressableHeap.Handle deleteMin() { if (size == 0) { throw new NoSuchElementException(); } Node oldRoot = root; // easy special cases if (size == 1) { root = null; size = 0; return oldRoot; } else if (size == 2) { root = root.o_c; root.o_c = null; root.y_s = null; size = 1; oldRoot.o_c = null; return oldRoot; } // remove last node Node lastNodeParent = findParentNode(size); Node lastNode = lastNodeParent.o_c; if (lastNode.y_s != lastNodeParent) { Node tmp = lastNode; lastNode = tmp.y_s; tmp.y_s = lastNodeParent; } else { lastNodeParent.o_c = null; } lastNode.y_s = null; // decrease size size--; // place it as root // (assumes root.o_c exists) if (root.o_c.y_s == root) { root.o_c.y_s = lastNode; } else { root.o_c.y_s.y_s = lastNode; } lastNode.o_c = root.o_c; root = lastNode; // fix priorities fixdown(root); oldRoot.o_c = null; return oldRoot; } /** * {@inheritDoc} */ @Override @ConstantTime public boolean isEmpty() { return size == 0; } /** * {@inheritDoc} */ @Override @ConstantTime public long size() { return size; } /** * {@inheritDoc} */ @Override public Comparator comparator() { return comparator; } /** * {@inheritDoc} */ @Override @ConstantTime public void clear() { root = null; size = 0; } // handle private class Node implements AddressableHeap.Handle, Serializable { private final static long serialVersionUID = 1; K key; V value; Node o_c; // older child Node y_s; // younger sibling or parent Node(K key, V value) { this.key = key; this.value = value; this.o_c = null; this.y_s = null; } @Override public K getKey() { return key; } @Override public V getValue() { return value; } @Override public void setValue(V value) { this.value = value; } @Override @LogarithmicTime @SuppressWarnings("unchecked") public void decreaseKey(K newKey) { if (this != root && y_s == null) { throw new IllegalArgumentException("Invalid handle!"); } int c; if (comparator == null) { c = ((Comparable) newKey).compareTo(key); } else { c = comparator.compare(newKey, key); } if (c > 0) { throw new IllegalArgumentException("Keys can only be decreased!"); } key = newKey; if (c == 0 || root == this) { return; } fixup(this); } @Override @LogarithmicTime public void delete() { if (this != root && y_s == null) { throw new IllegalArgumentException("Invalid handle!"); } Node p = getParent(this); while (p != null) { Node pp = getParent(p); swap(this, p, pp); p = pp; } // remove root deleteMin(); o_c = null; y_s = null; } } @SuppressWarnings("unchecked") private void fixup(Node n) { if (comparator == null) { Node p = getParent(n); while (p != null) { if (((Comparable) n.key).compareTo(p.key) >= 0) { break; } Node pp = getParent(p); swap(n, p, pp); p = pp; } } else { Node p = getParent(n); while (p != null) { if (comparator.compare(n.key, p.key) >= 0) { break; } Node pp = getParent(p); swap(n, p, pp); p = pp; } } } @SuppressWarnings("unchecked") private void fixdown(Node n) { if (comparator == null) { Node p = getParent(n); while (n.o_c != null) { Node child = n.o_c; if (child.y_s != n && ((Comparable) child.y_s.key).compareTo(child.key) < 0) { child = child.y_s; } if (((Comparable) n.key).compareTo(child.key) <= 0) { break; } swap(child, n, p); p = child; } } else { Node p = getParent(n); while (n.o_c != null) { Node child = n.o_c; if (child.y_s != n && comparator.compare(child.y_s.key, child.key) < 0) { child = child.y_s; } if (comparator.compare(n.key, child.key) <= 0) { break; } swap(child, n, p); p = child; } } } /* * Get the parent node of a given node. */ private Node getParent(Node n) { if (n.y_s == null) { return null; } Node c = n.y_s; if (c.o_c == n) { return c; } Node p1 = c.y_s; if (p1 != null && p1.o_c == n) { return p1; } return c; } /* * Start at the root and traverse the tree in order to find the parent node * of a particular node. Uses the bit representation to keep the cost * log(n). * * @param node the node number assuming that the root node is number one */ private Node findParentNode(long node) { // assert node > 0; // find bit representation of node long[] s = { node }; BitSet bits = BitSet.valueOf(s); // traverse path to last node Node cur = root; for (int i = bits.length() - 2; i > 0; i--) { if (bits.get(i)) { cur = cur.o_c.y_s; } else { cur = cur.o_c; } } return cur; } /* * Swap a node with its parent which must be the root. */ private void swap(Node n, Node root) { // assert this.root == root; Node nLeftChild = n.o_c; if (root.o_c == n) { if (n.y_s == root) { // n is left child and no right sibling n.o_c = root; root.y_s = n; } else { // n is left child and has right sibling root.y_s = n.y_s; root.y_s.y_s = n; n.o_c = root; } } else { // n is right child root.o_c.y_s = root; n.o_c = root.o_c; root.y_s = n; } n.y_s = null; // hang children root.o_c = nLeftChild; if (nLeftChild != null) { if (nLeftChild.y_s == n) { nLeftChild.y_s = root; } else { nLeftChild.y_s.y_s = root; } } this.root = n; } /* * Swap a node with its parent * * @param n the node * * @param p the parent node * * @param pp the parent of the parent node, maybe null */ private void swap(Node n, Node p, Node pp) { if (pp == null) { swap(n, p); return; } Node nLeftChild = n.o_c; if (pp.o_c == p) { // p left child of pp if (p.o_c == n) { if (n.y_s == p) { // n left child of p and no sibling pp.o_c = n; n.y_s = p.y_s; n.o_c = p; p.y_s = n; } else { // n left child or p and sibling n.y_s.y_s = n; Node tmp = n.y_s; n.y_s = p.y_s; p.y_s = tmp; pp.o_c = n; n.o_c = p; } } else { // n right child of p Node tmp = p.o_c; n.y_s = p.y_s; pp.o_c = n; n.o_c = tmp; tmp.y_s = p; p.y_s = n; } } else { // p right child of pp if (p.o_c == n) { if (n.y_s == p) { // n left child of p and no sibling n.y_s = pp; pp.o_c.y_s = n; n.o_c = p; p.y_s = n; } else { // n left child of p and sibling pp.o_c.y_s = n; p.y_s = n.y_s; n.y_s = pp; n.o_c = p; p.y_s.y_s = n; } } else { // n right child of p pp.o_c.y_s = n; n.y_s = pp; n.o_c = p.o_c; n.o_c.y_s = p; p.y_s = n; } } // hang children p.o_c = nLeftChild; if (nLeftChild != null) { if (nLeftChild.y_s == n) { nLeftChild.y_s = p; } else { nLeftChild.y_s.y_s = p; } } } } jheaps-jheaps-0.14/src/main/java/org/jheaps/tree/BinaryTreeSoftAddressableHeap.java000066400000000000000000000756761367505655000303500ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.tree; import java.io.Serializable; import java.util.ArrayDeque; import java.util.Comparator; import java.util.Deque; import java.util.NoSuchElementException; import org.jheaps.AddressableHeap; import org.jheaps.MergeableAddressableHeap; import org.jheaps.annotations.ConstantTime; import org.jheaps.annotations.VisibleForTesting; /** * A binary tree soft addressable heap. The heap is sorted according to the * {@linkplain Comparable natural ordering} of its keys, or by a * {@link Comparator} provided at heap creation time, depending on which * constructor is used. * *

* If n elements are inserted into a soft heap, then up to εn of the * elements still contained in the heap, for a given error parameter ε, may * be corrupted, i.e., have their keys artificially increased. In exchange for * allowing these corruptions, each soft heap operation is performed in O(log * 1/ε) amortized time. Note that n here is the number of elements inserted * into the heaps, not the current number of elements in the heap which may be * considerably smaller. Moreover the user has no control on which elements may * be corrupted. * *

* This variant of the soft heap is due to Kaplan and Zwick, described in detail * in the following * paper: *

    *
  • Haim Kaplan and Uri Zwick, A simpler implementation and analysis of * Chazelle's Soft Heaps, In Proceedings of the 20th Annual ACM-SIAM Symposium * on Discrete Algorithms (SODA 2009), 477--485, 2009.
  • *
* *

* Note that the operation {@code decreaseKey()} always throws an * {@link UnsupportedOperationException} as a soft heap does not support such an * operation. * *

* All the above bounds, however, assume that the user does not perform * cascading melds on heaps such as: * *

 * d.meld(e);
 * c.meld(d);
 * b.meld(c);
 * a.meld(b);
 * 
* * The above scenario, although efficiently supported by using union-find with * path compression, invalidates the claimed bounds. * *

* Note that the ordering maintained by a soft heap, like any heap, and whether * or not an explicit comparator is provided, must be consistent with * {@code equals} if this heap is to correctly implement the {@code Heap} * interface. (See {@code Comparable} or {@code Comparator} for a precise * definition of consistent with equals.) This is so because the * {@code Heap} interface is defined in terms of the {@code equals} operation, * but a pairing heap performs all key comparisons using its {@code compareTo} * (or {@code compare}) method, so two keys that are deemed equal by this method * are, from the standpoint of the heap, equal. The behavior of a heap * is well-defined even if its ordering is inconsistent with * {@code equals}; it just fails to obey the general contract of the * {@code Heap} interface. * *

* Note that this implementation is not synchronized. If * multiple threads access a heap concurrently, and at least one of the threads * modifies the heap structurally, it must be synchronized externally. * (A structural modification is any operation that adds or deletes one or more * elements or changing the key of some element.) This is typically accomplished * by synchronizing on some object that naturally encapsulates the heap. * * @param * the type of keys maintained by this heap * @param * the type of values maintained by this heap * * @author Dimitrios Michail */ public class BinaryTreeSoftAddressableHeap implements MergeableAddressableHeap, Serializable { private static final long serialVersionUID = 1; /** * The comparator used to maintain order in this heap, or null if it uses * the natural ordering of its keys. * * @serial */ private final Comparator comparator; /** * Already computed values for target sizes. */ private static final long[] TARGET_SIZE = { 1, 2, 3, 5, 8, 12, 18, 27, 41, 62, 93, 140, 210, 315, 473, 710, 1065, 1598, 2397, 3596, 5394, 8091, 12137, 18206, 27309, 40964, 61446, 92169, 138254, 207381, 311072, 466608, 699912, 1049868, 1574802, 2362203, 3543305, 5314958, 7972437, 11958656, 17937984, 26906976, 40360464, 60540696, 90811044, 136216566, 204324849, 306487274, 459730911, 689596367, 1034394551, 1551591827, 2327387741L, 3491081612L, 5236622418L, 7854933627L, 11782400441L, 17673600662L, 26510400993L, 39765601490L, 59648402235L, 89472603353L, 134208905030L }; /** * Tree nodes with less or equal than this rank will have no corrupted keys. */ private final int rankLimit; /** * The root list, in non-decreasing rank order. */ @VisibleForTesting final RootList rootList; /** * Size of the heap. */ private long size; /** * Used to reference the current heap or some other heap in case of melding, * so that handles remain valid even after a meld, without having to iterate * over them. * * In order to avoid maintaining a full-fledged union-find data structure, * we disallow a heap to be used in melding more than once. We use however, * path-compression in case of cascading melds, that it, a handle moves from * one heap to another and then another. */ private BinaryTreeSoftAddressableHeap other; /** * Constructs a new, empty heap, using the natural ordering of its keys. All * keys inserted into the heap must implement the {@link Comparable} * interface. Furthermore, all such keys must be mutually * comparable: {@code k1.compareTo(k2)} must not throw a * {@code ClassCastException} for any keys {@code k1} and {@code k2} in the * heap. If the user attempts to put a key into the heap that violates this * constraint (for example, the user attempts to put a string key into a * heap whose keys are integers), the {@code insert(Object key)} call will * throw a {@code ClassCastException}. * * @param errorRate * the error rate * @throws IllegalArgumentException * if the error rate is less or equal to zero * @throws IllegalArgumentException * if the error rate is greater or equal to one */ public BinaryTreeSoftAddressableHeap(double errorRate) { this(errorRate, null); } /** * Constructs a new, empty heap, ordered according to the given comparator. * All keys inserted into the heap must be mutually comparable by * the given comparator: {@code comparator.compare(k1, * k2)} must not throw a {@code ClassCastException} for any keys {@code k1} * and {@code k2} in the heap. If the user attempts to put a key into the * heap that violates this constraint, the {@code insert(Object key)} call * will throw a {@code ClassCastException}. * * @param errorRate * the error rate * @param comparator * the comparator that will be used to order this heap. If * {@code null}, the {@linkplain Comparable natural ordering} of * the keys will be used. * @throws IllegalArgumentException * if the error rate is less or equal to zero * @throws IllegalArgumentException * if the error rate is greater or equal to one */ public BinaryTreeSoftAddressableHeap(double errorRate, Comparator comparator) { if (Double.compare(errorRate, 0d) <= 0) { throw new IllegalArgumentException("Error rate must be positive"); } if (Double.compare(errorRate, 1d) >= 0) { throw new IllegalArgumentException("Error rate must be less than one"); } this.rankLimit = (int) Math.ceil(Math.log(1d / errorRate) / Math.log(2)) + 5; this.rootList = new RootList(); this.comparator = comparator; this.size = 0; this.other = this; } /** * {@inheritDoc} */ @Override @ConstantTime public boolean isEmpty() { return size == 0; } /** * {@inheritDoc} */ @Override @ConstantTime public long size() { return size; } /** * {@inheritDoc} */ @Override public Comparator comparator() { return comparator; } /** * {@inheritDoc} */ @Override @ConstantTime(amortized = false) public void clear() { rootList.head = null; rootList.tail = null; size = 0; } /** * {@inheritDoc} * * @throws IllegalArgumentException * if {@code other} has a different error rate */ @Override public void meld(MergeableAddressableHeap other) { BinaryTreeSoftAddressableHeap h = (BinaryTreeSoftAddressableHeap) other; // check same comparator if (comparator != null) { if (h.comparator == null || !h.comparator.equals(comparator)) { throw new IllegalArgumentException("Cannot meld heaps using different comparators!"); } } else if (h.comparator != null) { throw new IllegalArgumentException("Cannot meld heaps using different comparators!"); } if (rankLimit != h.rankLimit) { throw new IllegalArgumentException("Cannot meld heaps with different error rates!"); } if (h.other != h) { throw new IllegalStateException("A heap cannot be used after a meld."); } // perform the meld mergeInto(h.rootList.head, h.rootList.tail); size += h.size; // clear other h.size = 0; h.rootList.head = null; h.rootList.tail = null; // take ownership h.other = this; } /** * {@inheritDoc} */ @Override public Handle insert(K key, V value) { if (other != this) { throw new IllegalStateException("A heap cannot be used after a meld"); } if (key == null) { throw new NullPointerException("Null keys not permitted"); } /* * Create a single element heap */ SoftHandle n = new SoftHandle(this, key, value); TreeNode treeNode = new TreeNode(n); RootListNode rootListNode = new RootListNode(treeNode); /* * Merge new list into old list */ mergeInto(rootListNode, rootListNode); size++; return n; } /** * {@inheritDoc} */ @Override public Handle insert(K key) { return insert(key, null); } /** * {@inheritDoc} */ @Override public SoftHandle findMin() { if (size == 0) { throw new NoSuchElementException(); } return rootList.head.suffixMin.root.cHead; } /** * {@inheritDoc} */ @Override public Handle deleteMin() { if (size == 0) { throw new NoSuchElementException(); } // find tree with minimum RootListNode minRootListNode = rootList.head.suffixMin; TreeNode root = minRootListNode.root; // remove from list SoftHandle result = root.cHead; if (result.next != null) { result.next.prev = null; result.next.tree = root; } root.cHead = result.next; root.cSize--; // replenish keys if needed if (root.cHead == null || root.cSize <= targetSize(root.rank) / 2) { if (root.left != null || root.right != null) { // get keys from children sift(root); updateSuffixMin(minRootListNode); } else if (root.cHead == null) { // no children and empty list, just remove the tree RootListNode minRootPrevListNode = minRootListNode.prev; delete(minRootListNode); updateSuffixMin(minRootPrevListNode); } } result.next = null; result.prev = null; result.tree = null; size--; return result; } // -------------------------------------------------------------------- @VisibleForTesting static class RootList implements Serializable { private static final long serialVersionUID = 1; RootListNode head; RootListNode tail; RootList() { this.head = null; this.tail = null; } } // -------------------------------------------------------------------- @VisibleForTesting static class RootListNode implements Serializable { private static final long serialVersionUID = 1; RootListNode next; RootListNode prev; RootListNode suffixMin; TreeNode root; RootListNode(TreeNode tree) { this.root = tree; tree.parent = this; this.suffixMin = this; this.next = null; this.prev = null; } } // -------------------------------------------------------------------- @VisibleForTesting static class TreeNode implements Serializable { private static final long serialVersionUID = 1; // rank int rank; // parent Object parent; // left child TreeNode left; // right child TreeNode right; // corrupted list head SoftHandle cHead; // corrupted list tail SoftHandle cTail; /* * Corrupted list size. This may be larger than the actual size as it * contains also a count of ghost elements (deleted by using directly * the handle). Checking whether the corrupted list is empty should be * performed using cHead. */ long cSize; // corrupted key K cKey; TreeNode() { this(null); } TreeNode(SoftHandle n) { this.rank = 0; this.parent = null; this.left = null; this.right = null; this.cHead = n; this.cTail = n; if (n != null) { this.cSize = 1; this.cKey = n.key; n.tree = this; } else { this.cSize = 0; this.cKey = null; } } } // -------------------------------------------------------------------- @VisibleForTesting static class SoftHandle implements AddressableHeap.Handle, Serializable { private static final long serialVersionUID = 1; /* * We maintain explicitly the belonging heap, instead of using an inner * class due to possible cascading melding. */ BinaryTreeSoftAddressableHeap heap; K key; V value; SoftHandle next; SoftHandle prev; /* * We maintain the invariant that the first node of a list must contain * the tree that it belongs. Due to appending lists, other nodes may * point to the wrong tree. */ TreeNode tree; SoftHandle(BinaryTreeSoftAddressableHeap heap, K key, V value) { this.heap = heap; this.key = key; this.value = value; this.next = null; this.prev = null; this.tree = null; } /** * {@inheritDoc} */ @Override public K getKey() { return key; } /** * {@inheritDoc} */ @Override public V getValue() { return value; } /** * {@inheritDoc} */ @Override public void setValue(V value) { this.value = value; } /** * {@inheritDoc} * * @throws UnsupportedOperationException * always, as this operation is not supported in soft heaps */ @Override public void decreaseKey(K newKey) { throw new UnsupportedOperationException("Not supported in a soft heap"); } /** * {@inheritDoc} */ @Override public void delete() { getOwner().delete(this); } /* * Get the owner heap of the handle. This is union-find with * path-compression between heaps. */ BinaryTreeSoftAddressableHeap getOwner() { if (heap.other != heap) { // find root BinaryTreeSoftAddressableHeap root = heap; while (root != root.other) { root = root.other; } // path-compression BinaryTreeSoftAddressableHeap cur = heap; while (cur.other != root) { BinaryTreeSoftAddressableHeap nextOne = cur.other; cur.other = root; cur = nextOne; } heap = root; } return heap; } } /** * Compute the target size for a particular rank. * * @param rank * the rank * @return the target size */ private long targetSize(int rank) { return rank <= rankLimit ? 1 : TARGET_SIZE[rank - rankLimit]; } /** * Sift elements from children nodes until the current node has enough * elements in its list. * * @param x * the node */ @SuppressWarnings("unchecked") private void sift(TreeNode x) { Deque> stack = new ArrayDeque>(); stack.push(x); while (!stack.isEmpty()) { x = stack.peek(); TreeNode xLeft = x.left; TreeNode xRight = x.right; // if leaf or list has enough elements, skip if (xLeft == null && xRight == null || x.cHead != null && x.cSize >= targetSize(x.rank)) { stack.pop(); continue; } // swap if needed if (xLeft == null || xRight != null && ((comparator == null && ((Comparable) xLeft.cKey).compareTo(xRight.cKey) > 0) || (comparator != null && comparator.compare(xLeft.cKey, xRight.cKey) > 0))) { x.left = xRight; x.right = xLeft; xLeft = x.left; } // grab non-empty list from left child xLeft.cTail.next = x.cHead; if (x.cHead != null) { x.cHead.prev = xLeft.cTail; } x.cHead = xLeft.cHead; if (x.cTail == null) { x.cTail = xLeft.cTail; } x.cHead.tree = x; x.cSize += xLeft.cSize; // set new corrupted key x.cKey = xLeft.cKey; // clear left child list xLeft.cKey = null; xLeft.cHead = null; xLeft.cTail = null; xLeft.cSize = 0; // recursively to left child if not a leaf if (xLeft.left != null || xLeft.right != null) { stack.push(xLeft); } else { x.left = null; } } } /** * Combine two trees into a new tree. * * @param x * the first tree * @param y * the second tree * @return the combined tree */ private TreeNode combine(TreeNode x, TreeNode y) { TreeNode z = new TreeNode(); z.left = x; x.parent = z; z.right = y; y.parent = z; z.rank = x.rank + 1; sift(z); return z; } /** * Update all suffix minimum pointers for a node and all its predecessors in * the root list. * * @param t * the node */ @SuppressWarnings("unchecked") private void updateSuffixMin(RootListNode t) { if (comparator == null) { while (t != null) { if (t.next == null) { t.suffixMin = t; } else { RootListNode nextSuffixMin = t.next.suffixMin; if (((Comparable) t.root.cKey).compareTo(nextSuffixMin.root.cKey) <= 0) { t.suffixMin = t; } else { t.suffixMin = nextSuffixMin; } } t = t.prev; } } else { while (t != null) { if (t.next == null) { t.suffixMin = t; } else { RootListNode nextSuffixMin = t.next.suffixMin; if (comparator.compare(t.root.cKey, nextSuffixMin.root.cKey) <= 0) { t.suffixMin = t; } else { t.suffixMin = nextSuffixMin; } } t = t.prev; } } } /** * Merge a list into the root list. Assumes that the two lists are sorted in * non-decreasing order of rank. * * @param head * the list head * @param tail * the list tail */ @SuppressWarnings("squid:S2259") private void mergeInto(RootListNode head, RootListNode tail) { // if root list empty, just copy if (rootList.head == null) { rootList.head = head; rootList.tail = tail; return; } // initialize RootListNode resultHead; RootListNode resultTail; RootListNode resultTailPrev = null; RootListNode cur1 = rootList.head; RootListNode cur2 = head; // add first node if (cur1.root.rank <= cur2.root.rank) { resultHead = cur1; resultTail = cur1; RootListNode cur1next = cur1.next; cur1.next = null; cur1 = cur1next; if (cur1next != null) { cur1next.prev = null; } } else { resultHead = cur2; resultTail = cur2; RootListNode cur2next = cur2.next; cur2.next = null; cur2 = cur2next; if (cur2next != null) { cur2next.prev = null; } } // merge int rank1; int rank2; while (true) { int resultRank = resultTail.root.rank; // read rank1 if (cur1 != null) { rank1 = cur1.root.rank; } else { if (cur2 != null && cur2.root.rank <= resultRank) { rank1 = Integer.MAX_VALUE; } else { break; } } // read rank2 if (cur2 != null) { rank2 = cur2.root.rank; } else { if (cur1 != null && cur1.root.rank <= resultRank) { rank2 = Integer.MAX_VALUE; } else { break; } } if (rank1 <= rank2) { switch (Integer.compare(rank1, resultRank)) { case 0: // combine into result resultTail.root = combine(cur1.root, resultTail.root); resultTail.root.parent = resultTail; // remove cur1 RootListNode cur1next = cur1.next; cur1.next = null; if (cur1next != null) { cur1next.prev = null; } cur1 = cur1next; break; case -1: // can happen if three same ranks cur1next = cur1.next; // add before tail into result cur1.next = resultTail; resultTail.prev = cur1; cur1.prev = resultTailPrev; if (resultTailPrev != null) { resultTailPrev.next = cur1; } else { resultHead = cur1; } resultTailPrev = cur1; // advance cur1 if (cur1next != null) { cur1next.prev = null; } cur1 = cur1next; break; case 1: // append into result resultTail.next = cur1; cur1.prev = resultTail; resultTailPrev = resultTail; resultTail = cur1; // remove cur1 cur1 = cur1.next; resultTail.next = null; if (cur1 != null) { cur1.prev = null; } break; default: break; } } else { // symmetric case rank2 < rank1 switch (Integer.compare(rank2, resultRank)) { case 0: // combine into result resultTail.root = combine(cur2.root, resultTail.root); resultTail.root.parent = resultTail; // remove cur2 RootListNode cur2next = cur2.next; cur2.next = null; if (cur2next != null) { cur2next.prev = null; } cur2 = cur2next; break; case -1: // can happen if three same ranks cur2next = cur2.next; // add before tail into result cur2.next = resultTail; resultTail.prev = cur2; cur2.prev = resultTailPrev; if (resultTailPrev != null) { resultTailPrev.next = cur2; } else { resultHead = cur2; } resultTailPrev = cur2; // advance cur2 if (cur2next != null) { cur2next.prev = null; } cur2 = cur2next; break; case 1: // append into result resultTail.next = cur2; cur2.prev = resultTail; resultTailPrev = resultTail; resultTail = cur2; // remove cur2 cur2 = cur2.next; resultTail.next = null; if (cur2 != null) { cur2.prev = null; } break; default: break; } } } // record up to which point a suffix minimum update is needed RootListNode updateSuffixFix = resultTail; // here rank of cur1 is more than result rank if (cur1 != null) { cur1.prev = resultTail; resultTail.next = cur1; resultTail = rootList.tail; } // here rank of cur2 is more than result rank if (cur2 != null) { cur2.prev = resultTail; resultTail.next = cur2; resultTail = tail; } // update suffix minimum updateSuffixMin(updateSuffixFix); // store final list rootList.head = resultHead; rootList.tail = resultTail; } /** * Delete a node from the root list. * * @param n * the node */ private void delete(RootListNode n) { RootListNode nPrev = n.prev; if (nPrev != null) { nPrev.next = n.next; } else { rootList.head = n.next; } if (n.next != null) { n.next.prev = nPrev; } else { rootList.tail = nPrev; } n.prev = null; n.next = null; } /** * Delete an element. * * @param n * the element to delete */ @SuppressWarnings("unchecked") private void delete(SoftHandle n) { if (n.tree == null) { throw new IllegalArgumentException("Invalid handle!"); } /* * Delete from belonging list. Care must be taken as the tree reference * is valid only if the node is the first in the list. */ TreeNode tree = n.tree; if (tree.cHead != n) { /* * Not first in list. Each case, remove and leave as ghost element. */ if (n.next != null) { n.next.prev = n.prev; } n.prev.next = n.next; } else { /* * First in list */ SoftHandle nNext = n.next; tree.cHead = nNext; if (nNext != null) { /* * More elements exists, remove and leave as ghost element. * Update new first element to point to correct tree. */ nNext.prev = null; nNext.tree = tree; } else { /* * No more elements, sift. */ sift(tree); /* * If still no elements, remove tree. */ if (tree.cHead == null) { if (tree.parent instanceof TreeNode) { TreeNode p = (TreeNode) tree.parent; if (p.left == tree) { p.left = null; } else { p.right = null; } } else { delete((RootListNode) tree.parent); } } } } n.tree = null; n.prev = null; n.next = null; size--; } } jheaps-jheaps-0.14/src/main/java/org/jheaps/tree/BinaryTreeSoftHeap.java000066400000000000000000000572441367505655000262050ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.tree; import java.io.Serializable; import java.util.ArrayDeque; import java.util.Comparator; import java.util.Deque; import java.util.NoSuchElementException; import org.jheaps.Heap; import org.jheaps.MergeableHeap; import org.jheaps.annotations.ConstantTime; import org.jheaps.annotations.VisibleForTesting; /** * A binary tree soft heap. The heap is sorted according to the * {@linkplain Comparable natural ordering} of its keys, or by a * {@link Comparator} provided at heap creation time, depending on which * constructor is used. * *

* If n elements are inserted into a soft heap, then up to εn of the * elements still contained in the heap, for a given error parameter ε, may * be corrupted, i.e., have their keys artificially increased. In exchange for * allowing these corruptions, each soft heap operation is performed in O(log * 1/ε) amortized time. Note that n here is the number of elements inserted * into the heaps, not the current number of elements in the heap which may be * considerably smaller. Moreover the user has no control on which elements may * be corrupted. * *

* This variant of the soft heap is due to Kaplan and Zwick, described in detail * in the following * paper: *

    *
  • Haim Kaplan and Uri Zwick, A simpler implementation and analysis of * Chazelle's Soft Heaps, In Proceedings of the 20th Annual ACM-SIAM Symposium * on Discrete Algorithms (SODA 2009), 477--485, 2009.
  • *
* *

* Note that the ordering maintained by a soft heap, like any heap, and whether * or not an explicit comparator is provided, must be consistent with * {@code equals} if this heap is to correctly implement the {@code Heap} * interface. (See {@code Comparable} or {@code Comparator} for a precise * definition of consistent with equals.) This is so because the * {@code Heap} interface is defined in terms of the {@code equals} operation, * but a pairing heap performs all key comparisons using its {@code compareTo} * (or {@code compare}) method, so two keys that are deemed equal by this method * are, from the standpoint of the heap, equal. The behavior of a heap * is well-defined even if its ordering is inconsistent with * {@code equals}; it just fails to obey the general contract of the * {@code Heap} interface. * *

* Note that this implementation is not synchronized. If * multiple threads access a heap concurrently, and at least one of the threads * modifies the heap structurally, it must be synchronized externally. * (A structural modification is any operation that adds or deletes one or more * elements or changing the key of some element.) This is typically accomplished * by synchronizing on some object that naturally encapsulates the heap. * * @param * the type of keys maintained by this heap * * @author Dimitrios Michail */ public class BinaryTreeSoftHeap implements Heap, MergeableHeap, Serializable { private static final long serialVersionUID = 1; /** * The comparator used to maintain order in this heap, or null if it uses * the natural ordering of its keys. * * @serial */ private final Comparator comparator; /** * Already computed values for target sizes. */ private static final long[] TARGET_SIZE = { 1, 2, 3, 5, 8, 12, 18, 27, 41, 62, 93, 140, 210, 315, 473, 710, 1065, 1598, 2397, 3596, 5394, 8091, 12137, 18206, 27309, 40964, 61446, 92169, 138254, 207381, 311072, 466608, 699912, 1049868, 1574802, 2362203, 3543305, 5314958, 7972437, 11958656, 17937984, 26906976, 40360464, 60540696, 90811044, 136216566, 204324849, 306487274, 459730911, 689596367, 1034394551, 1551591827, 2327387741L, 3491081612L, 5236622418L, 7854933627L, 11782400441L, 17673600662L, 26510400993L, 39765601490L, 59648402235L, 89472603353L, 134208905030L }; /** * Tree nodes with less or equal than this rank will have no corrupted keys. */ private final int rankLimit; /** * The root list, in non-decreasing rank order. */ @VisibleForTesting final RootList rootList; /** * Size of the heap. */ private long size; /** * Constructs a new, empty heap, using the natural ordering of its keys. All * keys inserted into the heap must implement the {@link Comparable} * interface. Furthermore, all such keys must be mutually * comparable: {@code k1.compareTo(k2)} must not throw a * {@code ClassCastException} for any keys {@code k1} and {@code k2} in the * heap. If the user attempts to put a key into the heap that violates this * constraint (for example, the user attempts to put a string key into a * heap whose keys are integers), the {@code insert(Object key)} call will * throw a {@code ClassCastException}. * * @param errorRate * the error rate * @throws IllegalArgumentException * if the error rate is less or equal to zero * @throws IllegalArgumentException * if the error rate is greater or equal to one */ public BinaryTreeSoftHeap(double errorRate) { this(errorRate, null); } /** * Constructs a new, empty heap, ordered according to the given comparator. * All keys inserted into the heap must be mutually comparable by * the given comparator: {@code comparator.compare(k1, * k2)} must not throw a {@code ClassCastException} for any keys {@code k1} * and {@code k2} in the heap. If the user attempts to put a key into the * heap that violates this constraint, the {@code insert(Object key)} call * will throw a {@code ClassCastException}. * * @param errorRate * the error rate * @param comparator * the comparator that will be used to order this heap. If * {@code null}, the {@linkplain Comparable natural ordering} of * the keys will be used. * @throws IllegalArgumentException * if the error rate is less or equal to zero * @throws IllegalArgumentException * if the error rate is greater or equal to one */ public BinaryTreeSoftHeap(double errorRate, Comparator comparator) { if (Double.compare(errorRate, 0d) <= 0) { throw new IllegalArgumentException("Error rate must be positive"); } if (Double.compare(errorRate, 1d) >= 0) { throw new IllegalArgumentException("Error rate must be less than one"); } this.rankLimit = (int) Math.ceil(Math.log(1d / errorRate) / Math.log(2d)) + 5; this.rootList = new RootList(); this.comparator = comparator; this.size = 0; } /** * {@inheritDoc} */ @Override @ConstantTime public boolean isEmpty() { return size == 0; } /** * {@inheritDoc} */ @Override @ConstantTime public long size() { return size; } /** * {@inheritDoc} */ @Override public Comparator comparator() { return comparator; } /** * {@inheritDoc} */ @Override @ConstantTime public void clear() { rootList.head = null; rootList.tail = null; size = 0; } /** * {@inheritDoc} * * @throws IllegalArgumentException * if {@code other} has a different error rate */ @Override public void meld(MergeableHeap other) { BinaryTreeSoftHeap h = (BinaryTreeSoftHeap) other; // check same comparator if (comparator != null) { if (h.comparator == null || !h.comparator.equals(comparator)) { throw new IllegalArgumentException("Cannot meld heaps using different comparators!"); } } else if (h.comparator != null) { throw new IllegalArgumentException("Cannot meld heaps using different comparators!"); } if (rankLimit != h.rankLimit) { throw new IllegalArgumentException("Cannot meld heaps with different error rates!"); } // perform the meld mergeInto(h.rootList.head, h.rootList.tail); size += h.size; // clear other h.size = 0; h.rootList.head = null; h.rootList.tail = null; } /** * {@inheritDoc} */ @Override public void insert(K key) { /* * Create a single element heap */ SoftHandle n = new SoftHandle(key); TreeNode treeNode = new TreeNode(n); RootListNode rootListNode = new RootListNode(treeNode); /* * Merge new list into old list */ mergeInto(rootListNode, rootListNode); size++; } /** * {@inheritDoc} */ @Override public K findMin() { if (size == 0) { throw new NoSuchElementException(); } return rootList.head.suffixMin.root.cHead.key; } /** * {@inheritDoc} */ @Override public K deleteMin() { if (size == 0) { throw new NoSuchElementException(); } // find tree with minimum RootListNode minRootListNode = rootList.head.suffixMin; TreeNode root = minRootListNode.root; // remove from list SoftHandle result = root.cHead; root.cHead = result.next; root.cSize--; // replenish keys if needed if (root.cSize <= targetSize(root.rank) / 2) { if (root.left != null || root.right != null) { // get keys from children sift(root); updateSuffixMin(minRootListNode); } else if (root.cSize == 0) { // no children and empty list, just remove the tree RootListNode minRootPrevListNode = minRootListNode.prev; if (minRootPrevListNode != null) { minRootPrevListNode.next = minRootListNode.next; } else { rootList.head = minRootListNode.next; } if (minRootListNode.next != null) { minRootListNode.next.prev = minRootPrevListNode; } else { rootList.tail = minRootPrevListNode; } minRootListNode.prev = null; minRootListNode.next = null; updateSuffixMin(minRootPrevListNode); } } result.next = null; size--; return result.key; } // -------------------------------------------------------------------- @VisibleForTesting static class RootList implements Serializable { private static final long serialVersionUID = 1; RootListNode head; RootListNode tail; RootList() { this.head = null; this.tail = null; } } // -------------------------------------------------------------------- @VisibleForTesting static class RootListNode implements Serializable { private static final long serialVersionUID = 1; RootListNode next; RootListNode prev; RootListNode suffixMin; TreeNode root; RootListNode(TreeNode tree) { this.root = tree; this.suffixMin = this; this.next = null; this.prev = null; } } // -------------------------------------------------------------------- @VisibleForTesting static class TreeNode implements Serializable { private static final long serialVersionUID = 1; int rank; // left child TreeNode left; // right child TreeNode right; // corrupted list head SoftHandle cHead; // corrupted list tail SoftHandle cTail; // corrupted list size long cSize; // corrupted key K cKey; TreeNode() { this(null); } TreeNode(SoftHandle n) { this.rank = 0; this.left = null; this.right = null; this.cHead = n; this.cTail = n; if (n != null) { this.cSize = 1; this.cKey = n.key; } else { this.cSize = 0; this.cKey = null; } } } // -------------------------------------------------------------------- @VisibleForTesting static class SoftHandle implements Serializable { private static final long serialVersionUID = 1; K key; SoftHandle next; SoftHandle(K key) { this.key = key; this.next = null; } } /** * Compute the target size for a particular rank. * * @param rank * the rank * @return the target size */ private long targetSize(int rank) { return rank <= rankLimit ? 1 : TARGET_SIZE[rank - rankLimit]; } /** * Sift elements from children nodes until the current node has enough * elements in its list. * * @param x * the node */ @SuppressWarnings("unchecked") private void sift(TreeNode x) { Deque> stack = new ArrayDeque>(); stack.push(x); while (!stack.isEmpty()) { x = stack.peek(); TreeNode xLeft = x.left; TreeNode xRight = x.right; // if leaf or list has enough elements, skip if (xLeft == null && xRight == null || x.cSize >= targetSize(x.rank)) { stack.pop(); continue; } // swap if needed if (xLeft == null || xRight != null && ((comparator == null && ((Comparable) xLeft.cKey).compareTo(xRight.cKey) > 0) || (comparator != null && comparator.compare(xLeft.cKey, xRight.cKey) > 0))) { x.left = xRight; x.right = xLeft; xLeft = x.left; } // grab non-empty list from left child xLeft.cTail.next = x.cHead; x.cHead = xLeft.cHead; if (x.cTail == null) { x.cTail = xLeft.cTail; } x.cSize += xLeft.cSize; // set new corrupted key x.cKey = xLeft.cKey; // clear left child list xLeft.cKey = null; xLeft.cHead = null; xLeft.cTail = null; xLeft.cSize = 0; // recursively to left child if not a leaf if (xLeft.left != null || xLeft.right != null) { stack.push(xLeft); } else { x.left = null; } } } /** * Combine two trees into a new tree. * * @param x * the first tree * @param y * the second tree * @return the combined tree */ private TreeNode combine(TreeNode x, TreeNode y) { TreeNode z = new TreeNode(); z.left = x; z.right = y; z.rank = x.rank + 1; sift(z); return z; } /** * Update all suffix minimum pointers for a node and all its predecessors in * the root list. * * @param t * the node */ @SuppressWarnings("unchecked") private void updateSuffixMin(RootListNode t) { if (comparator == null) { while (t != null) { if (t.next == null) { t.suffixMin = t; } else { RootListNode nextSuffixMin = t.next.suffixMin; if (((Comparable) t.root.cKey).compareTo(nextSuffixMin.root.cKey) <= 0) { t.suffixMin = t; } else { t.suffixMin = nextSuffixMin; } } t = t.prev; } } else { while (t != null) { if (t.next == null) { t.suffixMin = t; } else { RootListNode nextSuffixMin = t.next.suffixMin; if (comparator.compare(t.root.cKey, nextSuffixMin.root.cKey) <= 0) { t.suffixMin = t; } else { t.suffixMin = nextSuffixMin; } } t = t.prev; } } } /** * Merge a list into the root list. Assumes that the two lists are sorted in * non-decreasing order of rank. * * @param head * the list head * @param tail * the list tail */ @SuppressWarnings("squid:S2259") private void mergeInto(RootListNode head, RootListNode tail) { // if root list empty, just copy if (rootList.head == null) { rootList.head = head; rootList.tail = tail; return; } // initialize RootListNode resultHead; RootListNode resultTail; RootListNode resultTailPrev = null; RootListNode cur1 = rootList.head; RootListNode cur2 = head; // add first node if (cur1.root.rank <= cur2.root.rank) { resultHead = cur1; resultTail = cur1; RootListNode cur1next = cur1.next; cur1.next = null; cur1 = cur1next; if (cur1next != null) { cur1next.prev = null; } } else { resultHead = cur2; resultTail = cur2; RootListNode cur2next = cur2.next; cur2.next = null; cur2 = cur2next; if (cur2next != null) { cur2next.prev = null; } } // merge int rank1; int rank2; while (true) { int resultRank = resultTail.root.rank; // read rank1 if (cur1 != null) { rank1 = cur1.root.rank; } else { if (cur2 != null && cur2.root.rank <= resultRank) { rank1 = Integer.MAX_VALUE; } else { break; } } // read rank2 if (cur2 != null) { rank2 = cur2.root.rank; } else { if (cur1 != null && cur1.root.rank <= resultRank) { rank2 = Integer.MAX_VALUE; } else { break; } } if (rank1 <= rank2) { switch (Integer.compare(rank1, resultRank)) { case 0: // combine into result resultTail.root = combine(cur1.root, resultTail.root); // remove cur1 RootListNode cur1next = cur1.next; cur1.next = null; if (cur1next != null) { cur1next.prev = null; } cur1 = cur1next; break; case -1: // can happen if three same ranks cur1next = cur1.next; // add before tail into result cur1.next = resultTail; resultTail.prev = cur1; cur1.prev = resultTailPrev; if (resultTailPrev != null) { resultTailPrev.next = cur1; } else { resultHead = cur1; } resultTailPrev = cur1; // advance cur1 if (cur1next != null) { cur1next.prev = null; } cur1 = cur1next; break; case 1: // append into result resultTail.next = cur1; cur1.prev = resultTail; resultTailPrev = resultTail; resultTail = cur1; // remove cur1 cur1 = cur1.next; resultTail.next = null; if (cur1 != null) { cur1.prev = null; } break; default: break; } } else { // symmetric case rank2 < rank1 switch (Integer.compare(rank2, resultRank)) { case 0: // combine into result resultTail.root = combine(cur2.root, resultTail.root); // remove cur2 RootListNode cur2next = cur2.next; cur2.next = null; if (cur2next != null) { cur2next.prev = null; } cur2 = cur2next; break; case -1: // can happen if three same ranks cur2next = cur2.next; // add before tail into result cur2.next = resultTail; resultTail.prev = cur2; cur2.prev = resultTailPrev; if (resultTailPrev != null) { resultTailPrev.next = cur2; } else { resultHead = cur2; } resultTailPrev = cur2; // advance cur2 if (cur2next != null) { cur2next.prev = null; } cur2 = cur2next; break; case 1: // append into result resultTail.next = cur2; cur2.prev = resultTail; resultTailPrev = resultTail; resultTail = cur2; // remove cur2 cur2 = cur2.next; resultTail.next = null; if (cur2 != null) { cur2.prev = null; } break; default: break; } } } // record up to which point a suffix minimum update is needed RootListNode updateSuffixFix = resultTail; // here rank of cur1 is more than result rank if (cur1 != null) { cur1.prev = resultTail; resultTail.next = cur1; resultTail = rootList.tail; } // here rank of cur2 is more than result rank if (cur2 != null) { cur2.prev = resultTail; resultTail.next = cur2; resultTail = tail; } // update suffix minimum updateSuffixMin(updateSuffixFix); // store final list rootList.head = resultHead; rootList.tail = resultTail; } } jheaps-jheaps-0.14/src/main/java/org/jheaps/tree/CostlessMeldPairingHeap.java000066400000000000000000000714261367505655000272160ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.tree; import java.io.Serializable; import java.lang.reflect.Array; import java.util.Arrays; import java.util.Comparator; import java.util.NoSuchElementException; import org.jheaps.AddressableHeap; import org.jheaps.MergeableAddressableHeap; import org.jheaps.annotations.ConstantTime; import org.jheaps.annotations.LogLogTime; import org.jheaps.annotations.LogarithmicTime; /** * The costless meld variant of the pairing heaps. The heap is sorted according * to the {@linkplain Comparable natural ordering} of its keys, or by a * {@link Comparator} provided at heap creation time, depending on which * constructor is used. * *

* This implementation provides amortized O(1) time for {@code findMin} and * {@code insert}, amortized O(log(n)) for {@code deleteMin} and {@code delete} * and amortized O(loglog(n)) for the {@code decreaseKey} operation. The * operation {@code meld} takes amortized zero time. * *

* This variant of the pairing heap is due to Amr Elmasry, described in detail * in the following * paper: *

    *
  • Amr Elmasry, Pairing Heaps with Costless Meld, In Proceedings of the 18th * Annual European Symposium on Algorithms (ESA 2010), 183--193, 2010.
  • *
* *

* All the above bounds, however, assume that the user does not perform * cascading melds on heaps such as: * *

 * d.meld(e);
 * c.meld(d);
 * b.meld(c);
 * a.meld(b);
 * 
* * The above scenario, although efficiently supported by using union-find with * path compression, invalidates the claimed bounds. * *

* Note that the ordering maintained by a pairing heap, like any heap, and * whether or not an explicit comparator is provided, must be consistent * with {@code equals} if this heap is to correctly implement the * {@code AddressableHeap} interface. (See {@code Comparable} or * {@code Comparator} for a precise definition of consistent with * equals.) This is so because the {@code AddressableHeap} interface is * defined in terms of the {@code equals} operation, but a pairing heap performs * all key comparisons using its {@code compareTo} (or {@code compare}) method, * so two keys that are deemed equal by this method are, from the standpoint of * the pairing heap, equal. The behavior of a heap is well-defined even * if its ordering is inconsistent with {@code equals}; it just fails to obey * the general contract of the {@code AddressableHeap} interface. * *

* Note that this implementation is not synchronized. If * multiple threads access a heap concurrently, and at least one of the threads * modifies the heap structurally, it must be synchronized externally. * (A structural modification is any operation that adds or deletes one or more * elements or changing the key of some element.) This is typically accomplished * by synchronizing on some object that naturally encapsulates the heap. * * @param * the type of keys maintained by this heap * @param * the type of values maintained by this heap * * @author Dimitrios Michail * * @see PairingHeap * @see FibonacciHeap */ public class CostlessMeldPairingHeap implements MergeableAddressableHeap, Serializable { private final static long serialVersionUID = 1; /** * Maximum length of decrease pool for long type. */ private final static int DEFAULT_DECREASE_POOL_SIZE = 64 + 1; /** * The comparator used to maintain order in this heap, or null if it uses * the natural ordering of its keys. * * @serial */ private final Comparator comparator; /** * The root of the pairing heap */ private Node root; /** * Size of the pairing heap */ private long size; /** * The decrease pool */ private Node[] decreasePool; /** * How many elements are valid in the decrease pool */ private byte decreasePoolSize; /** * Index of node with minimum key in the decrease pool. Not existent if * {@literal decreasePoolMin >= decreasePoolSize}. */ private byte decreasePoolMinPos; /** * Comparator for nodes in the decrease pool. Initialized lazily and used * when sorting entries in the decrease pool. */ private transient Comparator> decreasePoolComparator; /** * Used to reference the current heap or some other pairing heap in case of * melding, so that handles remain valid even after a meld, without having * to iterate over them. * * In order to avoid maintaining a full-fledged union-find data structure, * we disallow a heap to be used in melding more than once. We use however, * path-compression in case of cascading melds, that it, a handle moves from * one heap to another and then another. */ private CostlessMeldPairingHeap other; /** * Constructs a new, empty heap, using the natural ordering of its keys. All * keys inserted into the heap must implement the {@link Comparable} * interface. Furthermore, all such keys must be mutually * comparable: {@code k1.compareTo(k2)} must not throw a * {@code ClassCastException} for any keys {@code k1} and {@code k2} in the * heap. If the user attempts to put a key into the heap that violates this * constraint (for example, the user attempts to put a string key into a * heap whose keys are integers), the {@code insert(Object key)} call will * throw a {@code ClassCastException}. */ @ConstantTime public CostlessMeldPairingHeap() { this(null); } /** * Constructs a new, empty heap, ordered according to the given comparator. * All keys inserted into the heap must be mutually comparable by * the given comparator: {@code comparator.compare(k1, * k2)} must not throw a {@code ClassCastException} for any keys {@code k1} * and {@code k2} in the heap. If the user attempts to put a key into the * heap that violates this constraint, the {@code insert(Object key)} call * will throw a {@code ClassCastException}. * * @param comparator * the comparator that will be used to order this heap. If * {@code null}, the {@linkplain Comparable natural ordering} of * the keys will be used. */ @ConstantTime @SuppressWarnings("unchecked") public CostlessMeldPairingHeap(Comparator comparator) { this.decreasePool = (Node[]) Array.newInstance(Node.class, DEFAULT_DECREASE_POOL_SIZE); this.decreasePoolSize = 0; this.decreasePoolMinPos = 0; this.comparator = comparator; this.decreasePoolComparator = null; this.other = this; } /** * {@inheritDoc} * * @throws IllegalStateException * if the heap has already been used in the right hand side of a * meld */ @Override @LogarithmicTime(amortized = true) public AddressableHeap.Handle insert(K key, V value) { if (other != this) { throw new IllegalStateException("A heap cannot be used after a meld"); } if (key == null) { throw new NullPointerException("Null keys not permitted"); } Node n = new Node(this, key, value); if (comparator == null) { root = link(root, n); } else { root = linkWithComparator(root, n); } size++; return n; } /** * {@inheritDoc} * * @throws IllegalStateException * if the heap has already been used in the right hand side of a * meld */ @Override @LogarithmicTime(amortized = true) public AddressableHeap.Handle insert(K key) { return insert(key, null); } /** * {@inheritDoc} */ @Override @SuppressWarnings("unchecked") @ConstantTime public AddressableHeap.Handle findMin() { if (size == 0) { throw new NoSuchElementException(); } else if (decreasePoolMinPos >= decreasePoolSize) { return root; } else { Node poolMin = decreasePool[decreasePoolMinPos]; int c; if (comparator == null) { c = ((Comparable) root.key).compareTo(poolMin.key); } else { c = comparator.compare(root.key, poolMin.key); } if (c <= 0) { return root; } else { return poolMin; } } } /** * {@inheritDoc} */ @Override @ConstantTime public boolean isEmpty() { return size == 0; } /** * {@inheritDoc} */ @Override @ConstantTime public long size() { return size; } /** * {@inheritDoc} */ @Override public Comparator comparator() { return comparator; } /** * {@inheritDoc} */ @Override @SuppressWarnings("unchecked") @ConstantTime public void clear() { root = null; size = 0; decreasePool = (Node[]) Array.newInstance(Node.class, DEFAULT_DECREASE_POOL_SIZE); decreasePoolSize = 0; decreasePoolMinPos = 0; } /** * {@inheritDoc} */ @Override @SuppressWarnings("unchecked") @LogarithmicTime(amortized = true) public AddressableHeap.Handle deleteMin() { if (size == 0) { throw new NoSuchElementException(); } Node min; if (decreasePoolMinPos >= decreasePoolSize) { // decrease pool empty min = root; // cut all children, and combine them root = combine(cutChildren(root)); } else { Node poolMin = decreasePool[decreasePoolMinPos]; int c; if (comparator == null) { c = ((Comparable) root.key).compareTo(poolMin.key); } else { c = comparator.compare(root.key, poolMin.key); } if (c <= 0) { // root is smaller min = root; // cut children, combine Node childrenTree = combine(cutChildren(root)); root = null; /* * Append to decrease pool without updating minimum as we are * going to consolidate anyway */ if (childrenTree != null) { addPool(childrenTree, false); } consolidate(); } else { // minimum in pool is smaller min = poolMin; // cut children, combine Node childrenTree = combine(cutChildren(poolMin)); if (childrenTree != null) { // add to location of previous minimum and consolidate decreasePool[decreasePoolMinPos] = childrenTree; childrenTree.poolIndex = decreasePoolMinPos; } else { decreasePool[decreasePoolMinPos] = decreasePool[decreasePoolSize - 1]; decreasePool[decreasePoolMinPos].poolIndex = decreasePoolMinPos; decreasePool[decreasePoolSize - 1] = null; decreasePoolSize--; } poolMin.poolIndex = Node.NO_INDEX; consolidate(); } } size--; return min; } /** * {@inheritDoc} * * This operation takes amortized zero cost. */ @Override @ConstantTime(amortized = true) public void meld(MergeableAddressableHeap other) { CostlessMeldPairingHeap h = (CostlessMeldPairingHeap) other; // check same comparator if (comparator != null) { if (h.comparator == null || !h.comparator.equals(comparator)) { throw new IllegalArgumentException("Cannot meld heaps using different comparators!"); } } else if (h.comparator != null) { throw new IllegalArgumentException("Cannot meld heaps using different comparators!"); } if (h.other != h) { throw new IllegalStateException("A heap cannot be used after a meld."); } // meld if (size < h.size) { consolidate(); if (comparator == null) { root = link(h.root, root); } else { root = linkWithComparator(h.root, root); } decreasePoolSize = h.decreasePoolSize; h.decreasePoolSize = 0; decreasePoolMinPos = h.decreasePoolMinPos; h.decreasePoolMinPos = 0; Node[] tmp = decreasePool; decreasePool = h.decreasePool; h.decreasePool = tmp; } else { h.consolidate(); if (comparator == null) { root = link(h.root, root); } else { root = linkWithComparator(h.root, root); } } size += h.size; h.root = null; h.size = 0; // take ownership h.other = this; } // node static class Node implements AddressableHeap.Handle, Serializable { private final static long serialVersionUID = 1; static final byte NO_INDEX = -1; /* * We maintain explicitly the belonging heap, instead of using an inner * class due to possible cascading melding. */ CostlessMeldPairingHeap heap; K key; V value; Node o_c; // older child Node y_s; // younger sibling Node o_s; // older sibling or parent byte poolIndex; // position in decrease pool Node(CostlessMeldPairingHeap heap, K key, V value) { this.heap = heap; this.key = key; this.value = value; this.o_c = null; this.y_s = null; this.o_s = null; this.poolIndex = NO_INDEX; } /** * {@inheritDoc} */ @Override public K getKey() { return key; } /** * {@inheritDoc} */ @Override public V getValue() { return value; } /** * {@inheritDoc} */ @Override public void setValue(V value) { this.value = value; } /** * {@inheritDoc} */ @Override @LogLogTime(amortized = true) public void delete() { getOwner().delete(this); } /** * {@inheritDoc} */ @Override @LogLogTime(amortized = true) public void decreaseKey(K newKey) { getOwner().decreaseKey(this, newKey); } /* * Get the owner heap of the handle. This is union-find with * path-compression between heaps. */ CostlessMeldPairingHeap getOwner() { if (heap.other != heap) { // find root CostlessMeldPairingHeap root = heap; while (root != root.other) { root = root.other; } // path-compression CostlessMeldPairingHeap cur = heap; while (cur.other != root) { CostlessMeldPairingHeap next = cur.other; cur.other = root; cur = next; } heap = root; } return heap; } } /* * Decrease the key of a node. */ @SuppressWarnings("unchecked") private void decreaseKey(Node n, K newKey) { int c; if (comparator == null) { c = ((Comparable) newKey).compareTo(n.key); } else { c = comparator.compare(newKey, n.key); } if (c > 0) { throw new IllegalArgumentException("Keys can only be decreased!"); } n.key = newKey; if (c == 0 || root == n) { // root or no change in key return; } else if (n.o_s == null && n.poolIndex == Node.NO_INDEX) { // no root, no parent and no pool index throw new IllegalArgumentException("Invalid handle!"); } else if (n.o_s == null) { // no parent and not root, so inside pool Node poolMin = decreasePool[decreasePoolMinPos]; if (comparator == null) { c = ((Comparable) newKey).compareTo(poolMin.key); } else { c = comparator.compare(newKey, poolMin.key); } if (c < 0) { decreasePoolMinPos = n.poolIndex; } return; } else { // node has a parent Node oldestChild = cutOldestChild(n); if (oldestChild != null) { linkInPlace(oldestChild, n); } else { cutFromParent(n); } // append node (minus oldest child) to decrease pool addPool(n, true); // if decrease pool has >= ceil(logn) trees, consolidate double sizeAsDouble = size; if (decreasePoolSize >= Math.getExponent(sizeAsDouble) + 1) { consolidate(); } } } /** * Delete a node. * * @param n * the node */ private void delete(Node n) { if (n != root && n.o_s == null && n.poolIndex == Node.NO_INDEX) { // no root, no parent and no pool index throw new IllegalArgumentException("Invalid handle!"); } // node has a parent if (n.o_s != null) { // cut oldest child Node oldestChild = cutOldestChild(n); if (oldestChild != null) { linkInPlace(oldestChild, n); } else { cutFromParent(n); } } // node has no parent // cut children Node childrenTree = combine(cutChildren(n)); boolean checkConsolidate = false; if (childrenTree != null) { checkConsolidate = true; addPool(childrenTree, true); } size--; if (n == root) { root = null; consolidate(); checkConsolidate = false; } else if (n.poolIndex != Node.NO_INDEX) { byte curIndex = n.poolIndex; decreasePool[curIndex] = decreasePool[decreasePoolSize - 1]; decreasePool[curIndex].poolIndex = curIndex; decreasePool[decreasePoolSize - 1] = null; decreasePoolSize--; n.poolIndex = Node.NO_INDEX; if (curIndex == decreasePoolMinPos) { // in decrease pool, and also the minimum consolidate(); checkConsolidate = false; } else { // in decrease pool, and not the minimum if (decreasePoolMinPos == decreasePoolSize) { decreasePoolMinPos = curIndex; } checkConsolidate = true; } } // if decrease pool has >= ceil(logn) trees, consolidate if (checkConsolidate) { double sizeAsDouble = size; if (decreasePoolSize >= Math.getExponent(sizeAsDouble) + 1) { consolidate(); } } } /* * Consolidate. Combine the trees of the decrease pool in one tree by * sorting the values of the roots of these trees, and linking the trees in * this order such that their roots form a path of nodes in the combined * tree (make every root the leftmost child of the root with the next * smaller value). Join this combined tree with the main tree. */ private void consolidate() { if (decreasePoolSize == 0) { return; } // lazily initialize comparator if (decreasePoolComparator == null) { if (comparator == null) { decreasePoolComparator = new Comparator>() { @Override @SuppressWarnings("unchecked") public int compare(Node o1, Node o2) { return ((Comparable) o1.key).compareTo(o2.key); } }; } else { decreasePoolComparator = new Comparator>() { @Override public int compare(Node o1, Node o2) { return CostlessMeldPairingHeap.this.comparator.compare(o1.key, o2.key); } }; } } // sort Arrays.sort(decreasePool, 0, decreasePoolSize, decreasePoolComparator); int i = decreasePoolSize - 1; Node s = decreasePool[i]; s.poolIndex = Node.NO_INDEX; while (i > 0) { Node f = decreasePool[i - 1]; f.poolIndex = Node.NO_INDEX; decreasePool[i] = null; // link (no comparison, due to sort) s.y_s = f.o_c; s.o_s = f; if (f.o_c != null) { f.o_c.o_s = s; } f.o_c = s; // advance s = f; i--; } // empty decrease pool decreasePool[0] = null; decreasePoolSize = 0; decreasePoolMinPos = 0; // merge tree with root if (comparator == null) { root = link(root, s); } else { root = linkWithComparator(root, s); } } /** * Append to decrease pool. */ @SuppressWarnings("unchecked") private void addPool(Node n, boolean updateMinimum) { decreasePool[decreasePoolSize] = n; n.poolIndex = decreasePoolSize; decreasePoolSize++; if (updateMinimum && decreasePoolSize > 1) { Node poolMin = decreasePool[decreasePoolMinPos]; int c; if (comparator == null) { c = ((Comparable) n.key).compareTo(poolMin.key); } else { c = comparator.compare(n.key, poolMin.key); } if (c < 0) { decreasePoolMinPos = n.poolIndex; } } } /** * Two pass pair and compute root. */ private Node combine(Node l) { if (l == null) { return null; } assert l.o_s == null; // left-right pass Node pairs = null; Node it = l, p_it; if (comparator == null) { // no comparator while (it != null) { p_it = it; it = it.y_s; if (it == null) { // append last node to pair list p_it.y_s = pairs; p_it.o_s = null; pairs = p_it; } else { Node n_it = it.y_s; // disconnect both p_it.y_s = null; p_it.o_s = null; it.y_s = null; it.o_s = null; // link trees p_it = link(p_it, it); // append to pair list p_it.y_s = pairs; pairs = p_it; // advance it = n_it; } } } else { while (it != null) { p_it = it; it = it.y_s; if (it == null) { // append last node to pair list p_it.y_s = pairs; p_it.o_s = null; pairs = p_it; } else { Node n_it = it.y_s; // disconnect both p_it.y_s = null; p_it.o_s = null; it.y_s = null; it.o_s = null; // link trees p_it = linkWithComparator(p_it, it); // append to pair list p_it.y_s = pairs; pairs = p_it; // advance it = n_it; } } } // second pass (reverse order - due to add first) it = pairs; Node f = null; if (comparator == null) { while (it != null) { Node nextIt = it.y_s; it.y_s = null; f = link(f, it); it = nextIt; } } else { while (it != null) { Node nextIt = it.y_s; it.y_s = null; f = linkWithComparator(f, it); it = nextIt; } } return f; } /** * Cut the children of a node and return the list. * * @param n * the node * @return the first node in the children list */ private Node cutChildren(Node n) { Node child = n.o_c; n.o_c = null; if (child != null) { child.o_s = null; } return child; } /** * Cut the oldest child of a node. * * @param n * the node * @return the oldest child of a node or null */ private Node cutOldestChild(Node n) { Node oldestChild = n.o_c; if (oldestChild != null) { if (oldestChild.y_s != null) { oldestChild.y_s.o_s = n; } n.o_c = oldestChild.y_s; oldestChild.y_s = null; oldestChild.o_s = null; } return oldestChild; } /** * Cut a node from its parent. * * @param n * the node */ private void cutFromParent(Node n) { if (n.o_s != null) { if (n.y_s != null) { n.y_s.o_s = n.o_s; } if (n.o_s.o_c == n) { // I am the oldest :( n.o_s.o_c = n.y_s; } else { // I have an older sibling! n.o_s.y_s = n.y_s; } n.y_s = null; n.o_s = null; } } /** * Put an orphan node into the position of another node. The other node * becomes an orphan. * * @param orphan * the orphan node * @param n * the node which will become an orphan */ private void linkInPlace(Node orphan, Node n) { // link orphan at node's position orphan.y_s = n.y_s; if (n.y_s != null) { n.y_s.o_s = orphan; } orphan.o_s = n.o_s; if (n.o_s != null) { if (n.o_s.o_c == n) { // node is the oldest :( n.o_s.o_c = orphan; } else { // node has an older sibling! n.o_s.y_s = orphan; } } n.o_s = null; n.y_s = null; } @SuppressWarnings("unchecked") private Node link(Node f, Node s) { if (s == null) { return f; } else if (f == null) { return s; } else if (((Comparable) f.key).compareTo(s.key) <= 0) { s.y_s = f.o_c; s.o_s = f; if (f.o_c != null) { f.o_c.o_s = s; } f.o_c = s; return f; } else { return link(s, f); } } private Node linkWithComparator(Node f, Node s) { if (s == null) { return f; } else if (f == null) { return s; } else if (comparator.compare(f.key, s.key) <= 0) { s.y_s = f.o_c; s.o_s = f; if (f.o_c != null) { f.o_c.o_s = s; } f.o_c = s; return f; } else { return linkWithComparator(s, f); } } } jheaps-jheaps-0.14/src/main/java/org/jheaps/tree/DaryTreeAddressableHeap.java000066400000000000000000000371031367505655000271460ustar00rootroot00000000000000package org.jheaps.tree; import java.io.Serializable; import java.lang.reflect.Array; import java.util.Comparator; import java.util.NoSuchElementException; import org.jheaps.AddressableHeap; import org.jheaps.annotations.ConstantTime; import org.jheaps.annotations.LogarithmicTime; /** * An explicit d-ary tree addressable heap. The heap is sorted according to the * {@linkplain Comparable natural ordering} of its keys, or by a * {@link Comparator} provided at heap creation time, depending on which * constructor is used. * *

* The worst-case cost of {@code insert}, {@code deleteMin}, {@code delete} and * {@code decreaceKey} operations is O(d log_d(n)) and the cost of {@code findMin} * is O(1). * *

* Note that the ordering maintained by a binary heap, like any heap, and * whether or not an explicit comparator is provided, must be consistent * with {@code equals} if this heap is to correctly implement the * {@code Heap} interface. (See {@code Comparable} or {@code Comparator} for a * precise definition of consistent with equals.) This is so because * the {@code Heap} interface is defined in terms of the {@code equals} * operation, but a binary heap performs all key comparisons using its * {@code compareTo} (or {@code compare}) method, so two keys that are deemed * equal by this method are, from the standpoint of the binary heap, equal. The * behavior of a heap is well-defined even if its ordering is * inconsistent with {@code equals}; it just fails to obey the general contract * of the {@code Heap} interface. * *

* Note that this implementation is not synchronized. If * multiple threads access a heap concurrently, and at least one of the threads * modifies the heap structurally, it must be synchronized externally. * (A structural modification is any operation that adds or deletes one or more * elements or changing the key of some element.) This is typically accomplished * by synchronizing on some object that naturally encapsulates the heap. * * @param * the type of keys maintained by this heap * @param * the type of values maintained by this heap * * @author Dimitrios Michail * * @see AddressableHeap * @see Comparable * @see Comparator */ public class DaryTreeAddressableHeap implements AddressableHeap, Serializable { private static final long serialVersionUID = 1; /** * The comparator used to maintain order in this heap, or null if it uses * the natural ordering of its keys. * * @serial */ private final Comparator comparator; /** * Size of the heap */ private long size; /** * Root node of the heap */ private Node root; /** * Branching factor. Always a power of two. */ private final int d; /** * Base 2 logarithm of branching factor. */ private final int log2d; /** * Auxiliary for swapping children. */ private Node[] aux; /** * Constructs a new, empty heap, using the natural ordering of its keys. * *

* All keys inserted into the heap must implement the {@link Comparable} * interface. Furthermore, all such keys must be mutually * comparable: {@code k1.compareTo(k2)} must not throw a * {@code ClassCastException} for any keys {@code k1} and {@code k2} in the * heap. If the user attempts to put a key into the heap that violates this * constraint (for example, the user attempts to put a string key into a * heap whose keys are integers), the {@code insert(Object key)} call will * throw a {@code ClassCastException}. * * @param d * the branching factor. Should be a power of 2. */ public DaryTreeAddressableHeap(int d) { this(d, null); } /** * Constructs a new, empty heap, ordered according to the given comparator. * *

* All keys inserted into the heap must be mutually comparable by * the given comparator: {@code comparator.compare(k1, * k2)} must not throw a {@code ClassCastException} for any keys {@code k1} * and {@code k2} in the heap. If the user attempts to put a key into the * heap that violates this constraint, the {@code insert(Object key)} call * will throw a {@code ClassCastException}. * * @param d * the branching factor. Should be a power of 2. * @param comparator * the comparator that will be used to order this heap. If * {@code null}, the {@linkplain Comparable natural ordering} of * the keys will be used. */ @SuppressWarnings("unchecked") public DaryTreeAddressableHeap(int d, Comparator comparator) { this.comparator = comparator; this.size = 0; this.root = null; if (d < 2 || ((d & (d - 1)) != 0)) { throw new IllegalArgumentException("Branching factor d should be a power of 2."); } this.d = d; this.log2d = log2(d); this.aux = (DaryTreeAddressableHeap.Node[]) Array.newInstance(Node.class, d); } @Override @LogarithmicTime public Handle insert(K key, V value) { if (key == null) { throw new NullPointerException("Null keys not permitted"); } Node n = new Node(key, value, d); if (size == 0) { root = n; size = 1; return n; } Node p = findNode(size); for (int i = 0; i < d; i++) { if (p.children[i] == null) { p.children[i] = n; break; } } n.parent = p; size++; fixup(n); return n; } @Override @LogarithmicTime public Handle insert(K key) { return insert(key, null); } @Override @ConstantTime public Handle findMin() { if (size == 0) { throw new NoSuchElementException(); } return root; } @Override @LogarithmicTime public Handle deleteMin() { if (size == 0) { throw new NoSuchElementException(); } Node oldRoot = root; if (size == 1) { root = null; size = 0; } else { root.delete(); } return oldRoot; } @Override @ConstantTime public boolean isEmpty() { return size == 0; } @Override @ConstantTime public long size() { return size; } @Override @ConstantTime public void clear() { size = 0; root = null; } @Override public Comparator comparator() { return comparator; } // handle private class Node implements AddressableHeap.Handle, Serializable { private final static long serialVersionUID = 1; K key; V value; Node parent; Node[] children; @SuppressWarnings("unchecked") Node(K key, V value, int d) { this.key = key; this.value = value; this.parent = null; this.children = (DaryTreeAddressableHeap.Node[]) Array.newInstance(Node.class, d); } @Override public K getKey() { return key; } @Override public V getValue() { return value; } @Override public void setValue(V value) { this.value = value; } @Override @LogarithmicTime @SuppressWarnings("unchecked") public void decreaseKey(K newKey) { if (parent == null && root != this) { throw new IllegalArgumentException("Invalid handle!"); } int c; if (comparator == null) { c = ((Comparable) newKey).compareTo(key); } else { c = comparator.compare(newKey, key); } if (c > 0) { throw new IllegalArgumentException("Keys can only be decreased!"); } key = newKey; if (c == 0 || root == this) { return; } fixup(this); } @Override @LogarithmicTime public void delete() { if (parent == null && root != this) { throw new IllegalArgumentException("Invalid handle!"); } if (size == 0) { throw new NoSuchElementException(); } // swap with last node Node last = findNode(size - 1); swap(this, last); // remove from parent if (this.parent != null) { for (int i = 0; i < d; i++) { if (this.parent.children[i] == this) { this.parent.children[i] = null; } } this.parent = null; } size--; if (size == 0) { root = null; } else if (this != last) { fixdown(last); } } } /** * Start at the root and traverse the tree in order to find a particular * node based on its numbering on a level-order traversal of the tree. Uses * the bit representation to keep the cost log_d(n). * * @param node * the node number assuming that the root node is number zero */ private Node findNode(long node) { if (node == 0) return root; long mask = (long)d - 1; long location = (node - 1); int log = log2(node - 1) / log2d; Node cur = root; for (int i = log; i >= 0; i--) { int s = i * log2d; int path = (int) ((location & (mask << s)) >>> s); Node next = cur.children[path]; if (next == null) { break; } cur = next; } return cur; } /** * Calculate the floor of the binary logarithm of n. * * @param n * the input number * @return the binary logarithm */ private int log2(long n) { // returns 0 for n=0 long log = 0; if ((n & 0xffffffff00000000L) != 0) { n >>>= 32; log = 32; } if ((n & 0xffff0000) != 0) { n >>>= 16; log += 16; } if (n >= 256) { n >>>= 8; log += 8; } if (n >= 16) { n >>>= 4; log += 4; } if (n >= 4) { n >>>= 2; log += 2; } return (int) (log + (n >>> 1)); } @SuppressWarnings("unchecked") private void fixup(Node n) { if (comparator == null) { Node p = n.parent; while (p != null) { if (((Comparable) n.key).compareTo(p.key) >= 0) { break; } Node pp = p.parent; swap(n, p); p = pp; } } else { Node p = n.parent; while (p != null) { if (comparator.compare(n.key, p.key) >= 0) { break; } Node pp = p.parent; swap(n, p); p = pp; } } } @SuppressWarnings("unchecked") private void fixdown(Node n) { if (comparator == null) { while (n.children[0] != null) { int min = 0; Node child = n.children[min]; for (int i = 1; i < d; i++) { Node candidate = n.children[i]; if (candidate != null && ((Comparable) candidate.key).compareTo(child.key) < 0) { min = i; child = candidate; } } if (((Comparable) n.key).compareTo(child.key) <= 0) { break; } swap(child, n); } } else { while (n.children[0] != null) { int min = 0; Node child = n.children[min]; for (int i = 1; i < d; i++) { Node candidate = n.children[i]; if (candidate != null && comparator.compare(candidate.key, child.key) < 0) { min = i; child = candidate; } } if (comparator.compare(n.key, child.key) <= 0) { break; } swap(child, n); } } } /** * Swap two nodes * * @param a * first node * @param b * second node */ private void swap(Node a, Node b) { if (a == null || b == null || a == b) { return; } if (a.parent == b) { Node tmp = a; a = b; b = tmp; } Node pa = a.parent; if (b.parent == a) { // a is the parent int whichChild = -1; for (int i = 0; i < d; i++) { aux[i] = b.children[i]; if (b == a.children[i]) { b.children[i] = a; a.parent = b; } else { b.children[i] = a.children[i]; if (b.children[i] != null) { b.children[i].parent = b; } } if (pa != null && pa.children[i] == a) { whichChild = i; } } b.parent = pa; if (pa != null) { pa.children[whichChild] = b; } for (int i = 0; i < d; i++) { a.children[i] = aux[i]; if (a.children[i] != null) { a.children[i].parent = a; } aux[i] = null; } } else { // no parent child relationship Node pb = b.parent; for (int i = 0; i < d; i++) { aux[i] = b.children[i]; b.children[i] = a.children[i]; if (b.children[i] != null) { b.children[i].parent = b; } } for (int i = 0; i < d; i++) { a.children[i] = aux[i]; if (a.children[i] != null) { a.children[i].parent = a; } aux[i] = null; } int aIsChild = -1; if (pa != null) { for (int i = 0; i < d; i++) { if (pa.children[i] == a) { aIsChild = i; } } } else { b.parent = null; } int bIsChild = -1; if (pb != null) { for (int i = 0; i < d; i++) { if (pb.children[i] == b) { bIsChild = i; } } } else { a.parent = null; } if (aIsChild>=0) { pa.children[aIsChild] = b; b.parent = pa; } if (bIsChild>=0) { pb.children[bIsChild] = a; a.parent = pb; } } // switch root if (root == a) { root = b; } else if (root == b) { root = a; } } } jheaps-jheaps-0.14/src/main/java/org/jheaps/tree/FibonacciHeap.java000066400000000000000000000502561367505655000251560ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.tree; import java.io.Serializable; import java.lang.reflect.Array; import java.util.Comparator; import java.util.NoSuchElementException; import org.jheaps.AddressableHeap; import org.jheaps.MergeableAddressableHeap; import org.jheaps.annotations.ConstantTime; import org.jheaps.annotations.LogarithmicTime; /** * Fibonacci heaps. The heap is sorted according to the {@linkplain Comparable * natural ordering} of its keys, or by a {@link Comparator} provided at heap * creation time, depending on which constructor is used. * *

* This implementation provides amortized O(1) time for operations that do not * involve deleting an element such as {@code insert}, and {@code decreaseKey}. * Operation {@code findMin} is worst-case O(1). Operations {@code deleteMin} * and {@code delete} are amortized O(log(n)). The operation {@code meld} is * also amortized O(1). * *

* All the above bounds, however, assume that the user does not perform * cascading melds on heaps such as: * *

 * d.meld(e);
 * c.meld(d);
 * b.meld(c);
 * a.meld(b);
 * 
* * The above scenario, although efficiently supported by using union-find with * path compression, invalidates the claimed bounds. * *

* Note that the ordering maintained by a Fibonacci heap, like any heap, and * whether or not an explicit comparator is provided, must be consistent * with {@code equals} if this heap is to correctly implement the * {@code AddressableHeap} interface. (See {@code Comparable} or * {@code Comparator} for a precise definition of consistent with * equals.) This is so because the {@code AddressableHeap} interface is * defined in terms of the {@code equals} operation, but a Fibonacci heap * performs all key comparisons using its {@code compareTo} (or {@code compare}) * method, so two keys that are deemed equal by this method are, from the * standpoint of the Fibonacci heap, equal. The behavior of a heap is * well-defined even if its ordering is inconsistent with {@code equals}; it * just fails to obey the general contract of the {@code AddressableHeap} * interface. * *

* Note that this implementation is not synchronized. If * multiple threads access a heap concurrently, and at least one of the threads * modifies the heap structurally, it must be synchronized externally. * (A structural modification is any operation that adds or deletes one or more * elements or changing the key of some element.) This is typically accomplished * by synchronizing on some object that naturally encapsulates the heap. * * @param * the type of keys maintained by this heap * @param * the type of values maintained by this heap * * @author Dimitrios Michail * * @see PairingHeap * @see CostlessMeldPairingHeap */ public class FibonacciHeap implements MergeableAddressableHeap, Serializable { private final static long serialVersionUID = 1; /** * Size of consolidation auxiliary array. Computed for number of elements * equal to {@link Long#MAX_VALUE}. */ private final static int AUX_CONSOLIDATE_ARRAY_SIZE = 91; /** * The comparator used to maintain order in this heap, or null if it uses * the natural ordering of its keys. * * @serial */ private final Comparator comparator; /** * The root with the minimum key */ private Node minRoot; /** * Number of roots in the root list */ private int roots; /** * Size of the heap */ private long size; /** * Auxiliary array for consolidation */ private Node[] aux; /** * Used to reference the current heap or some other heap in case of melding, * so that handles remain valid even after a meld, without having to iterate * over them. * * In order to avoid maintaining a full-fledged union-find data structure, * we disallow a heap to be used in melding more than once. We use however, * path-compression in case of cascading melds, that it, a handle moves from * one heap to another and then another. */ protected FibonacciHeap other; /** * Constructs a new, empty heap, using the natural ordering of its keys. All * keys inserted into the heap must implement the {@link Comparable} * interface. Furthermore, all such keys must be mutually * comparable: {@code k1.compareTo(k2)} must not throw a * {@code ClassCastException} for any keys {@code k1} and {@code k2} in the * heap. If the user attempts to put a key into the heap that violates this * constraint (for example, the user attempts to put a string key into a * heap whose keys are integers), the {@code insert(Object key)} call will * throw a {@code ClassCastException}. */ @ConstantTime public FibonacciHeap() { this(null); } /** * Constructs a new, empty heap, ordered according to the given comparator. * All keys inserted into the heap must be mutually comparable by * the given comparator: {@code comparator.compare(k1, * k2)} must not throw a {@code ClassCastException} for any keys {@code k1} * and {@code k2} in the heap. If the user attempts to put a key into the * heap that violates this constraint, the {@code insert(Object key)} call * will throw a {@code ClassCastException}. * * @param comparator * the comparator that will be used to order this heap. If * {@code null}, the {@linkplain Comparable natural ordering} of * the keys will be used. */ @ConstantTime @SuppressWarnings("unchecked") public FibonacciHeap(Comparator comparator) { this.minRoot = null; this.roots = 0; this.comparator = comparator; this.size = 0; this.aux = (Node[]) Array.newInstance(Node.class, AUX_CONSOLIDATE_ARRAY_SIZE); this.other = this; } /** * {@inheritDoc} * * @throws IllegalStateException * if the heap has already been used in the right hand side of a * meld */ @Override @ConstantTime(amortized = true) public AddressableHeap.Handle insert(K key, V value) { if (other != this) { throw new IllegalStateException("A heap cannot be used after a meld"); } if (key == null) { throw new NullPointerException("Null keys not permitted"); } Node n = new Node(this, key, value); addToRootList(n); size++; return n; } /** * {@inheritDoc} * * @throws IllegalStateException * if the heap has already been used in the right hand side of a * meld */ @Override @ConstantTime(amortized = true) public AddressableHeap.Handle insert(K key) { return insert(key, null); } /** * {@inheritDoc} */ @Override @ConstantTime(amortized = true) public AddressableHeap.Handle findMin() { if (size == 0) { throw new NoSuchElementException(); } return minRoot; } /** * {@inheritDoc} */ @Override @LogarithmicTime(amortized = true) public AddressableHeap.Handle deleteMin() { if (size == 0) { throw new NoSuchElementException(); } Node z = minRoot; // move z children into root list Node x = z.child; while (x != null) { Node nextX = (x.next == x) ? null : x.next; // clear parent x.parent = null; // remove from child list x.prev.next = x.next; x.next.prev = x.prev; // add to root list x.next = minRoot.next; x.prev = minRoot; minRoot.next = x; x.next.prev = x; roots++; // advance x = nextX; } z.degree = 0; z.child = null; // remove z from root list z.prev.next = z.next; z.next.prev = z.prev; roots--; // decrease size size--; // update minimum root if (z == z.next) { minRoot = null; } else { minRoot = z.next; consolidate(); } // clear other fields z.next = null; z.prev = null; return z; } /** * {@inheritDoc} */ @Override @ConstantTime public boolean isEmpty() { return size == 0; } /** * {@inheritDoc} */ @Override @ConstantTime public long size() { return size; } /** * {@inheritDoc} */ @Override public Comparator comparator() { return comparator; } /** * {@inheritDoc} */ @Override @ConstantTime public void clear() { minRoot = null; roots = 0; size = 0; } /** * {@inheritDoc} */ @Override @ConstantTime(amortized = true) @SuppressWarnings("unchecked") public void meld(MergeableAddressableHeap other) { FibonacciHeap h = (FibonacciHeap) other; // check same comparator if (comparator != null) { if (h.comparator == null || !h.comparator.equals(comparator)) { throw new IllegalArgumentException("Cannot meld heaps using different comparators!"); } } else if (h.comparator != null) { throw new IllegalArgumentException("Cannot meld heaps using different comparators!"); } if (h.other != h) { throw new IllegalStateException("A heap cannot be used after a meld."); } if (size == 0) { // copy the other minRoot = h.minRoot; } else if (h.size != 0) { // concatenate root lists Node h11 = minRoot; Node h12 = h11.next; Node h21 = h.minRoot; Node h22 = h21.next; h11.next = h22; h22.prev = h11; h21.next = h12; h12.prev = h21; // find new minimum if ((comparator == null && ((Comparable) h.minRoot.key).compareTo(minRoot.key) < 0) || (comparator != null && comparator.compare(h.minRoot.key, minRoot.key) < 0)) { minRoot = h.minRoot; } } roots += h.roots; size += h.size; // clear other h.size = 0; h.minRoot = null; h.roots = 0; // take ownership h.other = this; } // -------------------------------------------------------------------- static class Node implements AddressableHeap.Handle, Serializable { private final static long serialVersionUID = 1; /* * We maintain explicitly the belonging heap, instead of using an inner * class due to possible cascading melding. */ FibonacciHeap heap; K key; V value; Node parent; // parent Node child; // any child Node next; // younger sibling Node prev; // older sibling int degree; // number of children boolean mark; // marked or not Node(FibonacciHeap heap, K key, V value) { this.heap = heap; this.key = key; this.value = value; this.parent = null; this.child = null; this.next = null; this.prev = null; this.degree = 0; this.mark = false; } /** * {@inheritDoc} */ @Override public K getKey() { return key; } /** * {@inheritDoc} */ @Override public V getValue() { return value; } /** * {@inheritDoc} */ @Override public void setValue(V value) { this.value = value; } /** * {@inheritDoc} */ @Override @ConstantTime(amortized = true) public void decreaseKey(K newKey) { FibonacciHeap h = getOwner(); if (h.comparator == null) { h.decreaseKey(this, newKey); } else { h.decreaseKeyWithComparator(this, newKey); } } /** * {@inheritDoc} */ @Override @LogarithmicTime(amortized = true) public void delete() { if (this.next == null) { throw new IllegalArgumentException("Invalid handle!"); } FibonacciHeap h = getOwner(); h.forceDecreaseKeyToMinimum(this); h.deleteMin(); } /* * Get the owner heap of the handle. This is union-find with * path-compression between heaps. */ FibonacciHeap getOwner() { if (heap.other != heap) { // find root FibonacciHeap root = heap; while (root != root.other) { root = root.other; } // path-compression FibonacciHeap cur = heap; while (cur.other != root) { FibonacciHeap next = cur.other; cur.other = root; cur = next; } heap = root; } return heap; } } /* * Decrease the key of a node. */ @SuppressWarnings("unchecked") private void decreaseKey(Node n, K newKey) { int c = ((Comparable) newKey).compareTo(n.key); if (c > 0) { throw new IllegalArgumentException("Keys can only be decreased!"); } n.key = newKey; if (c == 0) { return; } if (n.next == null) { throw new IllegalArgumentException("Invalid handle!"); } // if not root and heap order violation Node y = n.parent; if (y != null && ((Comparable) n.key).compareTo(y.key) < 0) { cut(n, y); cascadingCut(y); } // update minimum root if (((Comparable) n.key).compareTo(minRoot.key) < 0) { minRoot = n; } } /* * Decrease the key of a node. */ private void decreaseKeyWithComparator(Node n, K newKey) { int c = comparator.compare(newKey, n.key); if (c > 0) { throw new IllegalArgumentException("Keys can only be decreased!"); } n.key = newKey; if (c == 0) { return; } if (n.next == null) { throw new IllegalArgumentException("Invalid handle!"); } // if not root and heap order violation Node y = n.parent; if (y != null && comparator.compare(n.key, y.key) < 0) { cut(n, y); cascadingCut(y); } // update minimum root if (comparator.compare(n.key, minRoot.key) < 0) { minRoot = n; } } /* * Decrease the key of a node to the minimum. Helper function for performing * a delete operation. Does not change the node's actual key, but behaves as * the key is the minimum key in the heap. */ private void forceDecreaseKeyToMinimum(Node n) { // if not root Node y = n.parent; if (y != null) { cut(n, y); cascadingCut(y); } minRoot = n; } /* * Consolidate: Make sure each root tree has a distinct degree. */ @SuppressWarnings("unchecked") private void consolidate() { int maxDegree = -1; // for each node in root list int numRoots = roots; Node x = minRoot; while (numRoots > 0) { Node nextX = x.next; int d = x.degree; while (true) { Node y = aux[d]; if (y == null) { break; } // make sure x's key is smaller int c; if (comparator == null) { c = ((Comparable) y.key).compareTo(x.key); } else { c = comparator.compare(y.key, x.key); } if (c < 0) { Node tmp = x; x = y; y = tmp; } // make y a child of x link(y, x); aux[d] = null; d++; } // store result aux[d] = x; // keep track of max degree if (d > maxDegree) { maxDegree = d; } // advance x = nextX; numRoots--; } // recreate root list and find minimum root minRoot = null; roots = 0; for (int i = 0; i <= maxDegree; i++) { if (aux[i] != null) { addToRootList(aux[i]); aux[i] = null; } } } /* * Remove node y from the root list and make it a child of x. Degree of x * increases by 1 and y is unmarked if marked. */ private void link(Node y, Node x) { // remove from root list y.prev.next = y.next; y.next.prev = y.prev; // one less root roots--; // clear if marked y.mark = false; // hang as x's child x.degree++; y.parent = x; Node child = x.child; if (child == null) { x.child = y; y.next = y; y.prev = y; } else { y.prev = child; y.next = child.next; child.next = y; y.next.prev = y; } } /* * Cut the link between x and its parent y making x a root. */ private void cut(Node x, Node y) { // remove x from child list of y x.prev.next = x.next; x.next.prev = x.prev; y.degree--; if (y.degree == 0) { y.child = null; } else if (y.child == x) { y.child = x.next; } // add x to the root list x.parent = null; addToRootList(x); // clear if marked x.mark = false; } /* * Cascading cut until a root or an unmarked node is found. */ private void cascadingCut(Node y) { Node z; while ((z = y.parent) != null) { if (!y.mark) { y.mark = true; break; } cut(y, z); y = z; } } /* * Add a node to the root list and update the minimum. */ @SuppressWarnings("unchecked") private void addToRootList(Node n) { if (minRoot == null) { n.next = n; n.prev = n; minRoot = n; roots = 1; } else { n.next = minRoot.next; n.prev = minRoot; minRoot.next.prev = n; minRoot.next = n; int c; if (comparator == null) { c = ((Comparable) n.key).compareTo(minRoot.key); } else { c = comparator.compare(n.key, minRoot.key); } if (c < 0) { minRoot = n; } roots++; } } } jheaps-jheaps-0.14/src/main/java/org/jheaps/tree/LeftistHeap.java000066400000000000000000000275561367505655000247220ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.tree; import java.util.Comparator; import java.util.Deque; import java.util.LinkedList; /** * Leftist heaps. The heap is sorted according to the {@linkplain Comparable * natural ordering} of its keys, or by a {@link Comparator} provided at heap * creation time, depending on which constructor is used. * *

* Operations {@code insert}, {@code deleteMin}, {@code decreaseKey}, and * {@code delete} take worst-case O(log(n)). Operation {@code findMin} is * worst-case O(1). * *

* Note that the ordering maintained by this heap, like any heap, and whether or * not an explicit comparator is provided, must be consistent with * {@code equals} if this heap is to correctly implement the {@code Heap} * interface. (See {@code Comparable} or {@code Comparator} for a precise * definition of consistent with equals.) This is so because the * {@code Heap} interface is defined in terms of the {@code equals} operation, * but this heap performs all key comparisons using its {@code compareTo} (or * {@code compare}) method, so two keys that are deemed equal by this method * are, from the standpoint of this heap, equal. The behavior of a heap * is well-defined even if its ordering is inconsistent with * {@code equals}; it just fails to obey the general contract of the * {@code Heap} interface. * *

* Note that this implementation is not synchronized. If * multiple threads access a heap concurrently, and at least one of the threads * modifies the heap structurally, it must be synchronized externally. * (A structural modification is any operation that adds or deletes one or more * elements or changing the key of some element.) This is typically accomplished * by synchronizing on some object that naturally encapsulates the heap. * * @param * the type of keys maintained by this heap * @param * the type of values maintained by this heap * * @author Dimitrios Michail */ public class LeftistHeap extends SkewHeap { private static final long serialVersionUID = -5948402731186806608L; /** * Constructs a new, empty heap, using the natural ordering of its keys. * *

* All keys inserted into the heap must implement the {@link Comparable} * interface. Furthermore, all such keys must be mutually * comparable: {@code k1.compareTo(k2)} must not throw a * {@code ClassCastException} for any keys {@code k1} and {@code k2} in the * heap. If the user attempts to put a key into the heap that violates this * constraint (for example, the user attempts to put a string key into a * heap whose keys are integers), the {@code insert(Object key)} call will * throw a {@code ClassCastException}. */ public LeftistHeap() { this(null); } /** * Constructs a new, empty heap, ordered according to the given comparator. * *

* All keys inserted into the heap must be mutually comparable by * the given comparator: {@code comparator.compare(k1, * k2)} must not throw a {@code ClassCastException} for any keys {@code k1} * and {@code k2} in the heap. If the user attempts to put a key into the * heap that violates this constraint, the {@code insert(Object key)} call * will throw a {@code ClassCastException}. * * @param comparator * the comparator that will be used to order this heap. If * {@code null}, the {@linkplain Comparable natural ordering} of * the keys will be used. */ public LeftistHeap(Comparator comparator) { super(comparator); } // ~----------------------------------------------------------------------- static class LeftistNode extends Node { private static final long serialVersionUID = 1L; int npl; // null path length LeftistNode(LeftistHeap heap, K key, V value) { super(heap, key, value); this.npl = 0; } } /** * Factory method for new node creation * * @param key * the key * @param value * the value * @return the newly created node */ protected Node createNode(K key, V value) { return new LeftistNode(this, key, value); } /** * Swap the children of a node. * * @param n * the node */ protected void swapChildren(Node n) { Node left = n.o_c; if (left != null) { Node right = left.y_s; if (right != n) { n.o_c = right; right.y_s = left; left.y_s = n; } } } /** * Top-down union two skew heaps * * @param root1 * the root of the first heap * @param root2 * the root of the right heap * @return the new root of the merged heap */ @Override @SuppressWarnings("unchecked") protected Node union(Node root1, Node root2) { if (root1 == null) { return root2; } else if (root2 == null) { return root1; } Node newRoot; Deque> path = new LinkedList>(); // find initial int c = ((Comparable) root1.key).compareTo(root2.key); if (c <= 0) { newRoot = root1; root1 = unlinkRightChild(root1); } else { newRoot = root2; root2 = unlinkRightChild(root2); } Node cur = newRoot; path.push((LeftistNode) cur); // merge while (root1 != null && root2 != null) { c = ((Comparable) root1.key).compareTo(root2.key); if (c <= 0) { // link as right child of cur if (cur.o_c == null) { cur.o_c = root1; } else { cur.o_c.y_s = root1; } root1.y_s = cur; cur = root1; path.push((LeftistNode) cur); root1 = unlinkRightChild(root1); } else { // link as right child of cur if (cur.o_c == null) { cur.o_c = root2; } else { cur.o_c.y_s = root2; } root2.y_s = cur; cur = root2; path.push((LeftistNode) cur); root2 = unlinkRightChild(root2); } } if (root1 != null) { // link as right child of cur if (cur.o_c == null) { cur.o_c = root1; } else { cur.o_c.y_s = root1; } root1.y_s = cur; } if (root2 != null) { // link as right child of cur if (cur.o_c == null) { cur.o_c = root2; } else { cur.o_c.y_s = root2; } root2.y_s = cur; } /* * Traverse path upwards, update null path length and swap if needed. */ while (!path.isEmpty()) { LeftistNode n = path.pop(); if (n.o_c != null) { // at least on child LeftistNode nLeft = (LeftistNode) n.o_c; int nplLeft = nLeft.npl; int nplRight = -1; if (nLeft.y_s != n) { // two children LeftistNode nRight = (LeftistNode) nLeft.y_s; nplRight = nRight.npl; } n.npl = 1 + Math.min(nplLeft, nplRight); if (nplLeft < nplRight) { // swap swapChildren(n); } } else { // no children n.npl = 0; } } return newRoot; } /** * Top-down union of two leftist heaps with comparator. * * @param root1 * the root of the first heap * @param root2 * the root of the right heap * @return the new root of the merged heap */ @Override protected Node unionWithComparator(Node root1, Node root2) { if (root1 == null) { return root2; } else if (root2 == null) { return root1; } Node newRoot; Deque> path = new LinkedList>(); // find initial int c = comparator.compare(root1.key, root2.key); if (c <= 0) { newRoot = root1; root1 = unlinkRightChild(root1); } else { newRoot = root2; root2 = unlinkRightChild(root2); } Node cur = newRoot; path.push((LeftistNode) cur); // merge while (root1 != null && root2 != null) { c = comparator.compare(root1.key, root2.key); if (c <= 0) { // link as right child of cur if (cur.o_c == null) { cur.o_c = root1; } else { cur.o_c.y_s = root1; } root1.y_s = cur; cur = root1; path.push((LeftistNode) cur); root1 = unlinkRightChild(root1); } else { // link as right child of cur if (cur.o_c == null) { cur.o_c = root2; } else { cur.o_c.y_s = root2; } root2.y_s = cur; cur = root2; path.push((LeftistNode) cur); root2 = unlinkRightChild(root2); } } if (root1 != null) { // link as right child of cur if (cur.o_c == null) { cur.o_c = root1; } else { cur.o_c.y_s = root1; } root1.y_s = cur; } if (root2 != null) { // link as right child of cur if (cur.o_c == null) { cur.o_c = root2; } else { cur.o_c.y_s = root2; } root2.y_s = cur; } /* * Traverse path upwards, update null path length and swap if needed. */ while (!path.isEmpty()) { LeftistNode n = path.pop(); if (n.o_c != null) { // at least on child LeftistNode nLeft = (LeftistNode) n.o_c; int nplLeft = nLeft.npl; int nplRight = -1; if (nLeft.y_s != n) { // two children LeftistNode nRight = (LeftistNode) nLeft.y_s; nplRight = nRight.npl; } n.npl = 1 + Math.min(nplLeft, nplRight); if (nplLeft < nplRight) { // swap swapChildren(n); } } else { // no children n.npl = 0; } } return newRoot; } } jheaps-jheaps-0.14/src/main/java/org/jheaps/tree/PairingHeap.java000066400000000000000000000437431367505655000246750ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.tree; import java.io.Serializable; import java.util.Comparator; import java.util.NoSuchElementException; import org.jheaps.AddressableHeap; import org.jheaps.MergeableAddressableHeap; import org.jheaps.annotations.ConstantTime; import org.jheaps.annotations.LogarithmicTime; /** * Pairing heaps. The heap is sorted according to the {@linkplain Comparable * natural ordering} of its keys, or by a {@link Comparator} provided at heap * creation time, depending on which constructor is used. * *

* This implementation provides amortized O(log(n)) time cost for the * {@code insert}, {@code deleteMin}, and {@code decreaseKey} operations. * Operation {@code findMin}, is a worst-case O(1) operation. The algorithms are * based on the pairing heap * paper. Pairing heaps are very efficient in practice, especially in * applications requiring the {@code decreaseKey} operation. The operation * {@code meld} is amortized O(log(n)). * *

* All the above bounds, however, assume that the user does not perform * cascading melds on heaps such as: * *

 * d.meld(e);
 * c.meld(d);
 * b.meld(c);
 * a.meld(b);
 * 
* * The above scenario, although efficiently supported by using union-find with * path compression, invalidates the claimed bounds. * *

* Note that the ordering maintained by a pairing heap, like any heap, and * whether or not an explicit comparator is provided, must be consistent * with {@code equals} if this heap is to correctly implement the * {@code AddressableHeap} interface. (See {@code Comparable} or * {@code Comparator} for a precise definition of consistent with * equals.) This is so because the {@code AddressableHeap} interface is * defined in terms of the {@code equals} operation, but a pairing heap performs * all key comparisons using its {@code compareTo} (or {@code compare}) method, * so two keys that are deemed equal by this method are, from the standpoint of * this heap, equal. The behavior of a heap is well-defined even if its * ordering is inconsistent with {@code equals}; it just fails to obey the * general contract of the {@code AddressableHeap} interface. * *

* Note that this implementation is not synchronized. If * multiple threads access a heap concurrently, and at least one of the threads * modifies the heap structurally, it must be synchronized externally. * (A structural modification is any operation that adds or deletes one or more * elements or changing the key of some element.) This is typically accomplished * by synchronizing on some object that naturally encapsulates the heap. * * @param * the type of keys maintained by this heap * @param * the type of values maintained by this heap * * @author Dimitrios Michail * * @see RankPairingHeap * @see CostlessMeldPairingHeap * @see FibonacciHeap */ public class PairingHeap implements MergeableAddressableHeap, Serializable { private final static long serialVersionUID = 1; /** * The comparator used to maintain order in this heap, or null if it uses * the natural ordering of its keys. * * @serial */ private final Comparator comparator; /** * The root of the pairing heap */ private Node root; /** * Size of the pairing heap */ private long size; /** * Used to reference the current heap or some other pairing heap in case of * melding, so that handles remain valid even after a meld, without having * to iterate over them. * * In order to avoid maintaining a full-fledged union-find data structure, * we disallow a heap to be used in melding more than once. We use however, * path-compression in case of cascading melds, that it, a handle moves from * one heap to another and then another. */ private PairingHeap other; /** * Constructs a new, empty heap, using the natural ordering of its keys. All * keys inserted into the heap must implement the {@link Comparable} * interface. Furthermore, all such keys must be mutually * comparable: {@code k1.compareTo(k2)} must not throw a * {@code ClassCastException} for any keys {@code k1} and {@code k2} in the * heap. If the user attempts to put a key into the heap that violates this * constraint (for example, the user attempts to put a string key into a * heap whose keys are integers), the {@code insert(Object key)} call will * throw a {@code ClassCastException}. */ @ConstantTime public PairingHeap() { this(null); } /** * Constructs a new, empty heap, ordered according to the given comparator. * All keys inserted into the heap must be mutually comparable by * the given comparator: {@code comparator.compare(k1, * k2)} must not throw a {@code ClassCastException} for any keys {@code k1} * and {@code k2} in the heap. If the user attempts to put a key into the * heap that violates this constraint, the {@code insert(Object key)} call * will throw a {@code ClassCastException}. * * @param comparator * the comparator that will be used to order this heap. If * {@code null}, the {@linkplain Comparable natural ordering} of * the keys will be used. */ @ConstantTime public PairingHeap(Comparator comparator) { this.root = null; this.comparator = comparator; this.size = 0; this.other = this; } /** * {@inheritDoc} * * @throws IllegalStateException * if the heap has already been used in the right hand side of a * meld */ @Override @LogarithmicTime(amortized = true) public AddressableHeap.Handle insert(K key, V value) { if (other != this) { throw new IllegalStateException("A heap cannot be used after a meld"); } if (key == null) { throw new NullPointerException("Null keys not permitted"); } Node n = new Node(this, key, value); if (comparator == null) { root = link(root, n); } else { root = linkWithComparator(root, n); } size++; return n; } /** * {@inheritDoc} * * @throws IllegalStateException * if the heap has already been used in the right hand side of a * meld */ @Override @LogarithmicTime(amortized = true) public AddressableHeap.Handle insert(K key) { return insert(key, null); } /** * {@inheritDoc} */ @Override @ConstantTime(amortized = false) public AddressableHeap.Handle findMin() { if (size == 0) { throw new NoSuchElementException(); } return root; } /** * {@inheritDoc} */ @Override @LogarithmicTime(amortized = true) public AddressableHeap.Handle deleteMin() { if (size == 0) { throw new NoSuchElementException(); } // assert root.o_s == null && root.y_s == null; Handle oldRoot = root; // cut all children, combine them and overwrite old root root = combine(cutChildren(root)); // decrease size size--; return oldRoot; } /** * {@inheritDoc} */ @Override @ConstantTime public boolean isEmpty() { return size == 0; } /** * {@inheritDoc} */ @Override @ConstantTime public long size() { return size; } /** * {@inheritDoc} */ @Override public Comparator comparator() { return comparator; } /** * {@inheritDoc} */ @Override @ConstantTime(amortized = false) public void clear() { root = null; size = 0; } /** * {@inheritDoc} */ @Override @LogarithmicTime(amortized = true) public void meld(MergeableAddressableHeap other) { PairingHeap h = (PairingHeap) other; // check same comparator if (comparator != null) { if (h.comparator == null || !h.comparator.equals(comparator)) { throw new IllegalArgumentException("Cannot meld heaps using different comparators!"); } } else if (h.comparator != null) { throw new IllegalArgumentException("Cannot meld heaps using different comparators!"); } if (h.other != h) { throw new IllegalStateException("A heap cannot be used after a meld."); } // perform the meld size += h.size; if (comparator == null) { root = link(root, h.root); } else { root = linkWithComparator(root, h.root); } // clear other h.size = 0; h.root = null; // take ownership h.other = this; } // -------------------------------------------------------------------- static class Node implements AddressableHeap.Handle, Serializable { private final static long serialVersionUID = 1; /* * We maintain explicitly the belonging heap, instead of using an inner * class due to possible cascading melding. */ PairingHeap heap; K key; V value; Node o_c; // older child Node y_s; // younger sibling Node o_s; // older sibling or parent Node(PairingHeap heap, K key, V value) { this.heap = heap; this.key = key; this.value = value; this.o_c = null; this.y_s = null; this.o_s = null; } /** * {@inheritDoc} */ @Override public K getKey() { return key; } /** * {@inheritDoc} */ @Override public V getValue() { return value; } /** * {@inheritDoc} */ @Override public void setValue(V value) { this.value = value; } /** * {@inheritDoc} */ @Override @LogarithmicTime(amortized = true) public void decreaseKey(K newKey) { getOwner().decreaseKey(this, newKey); } /** * {@inheritDoc} */ @Override @LogarithmicTime(amortized = true) public void delete() { getOwner().delete(this); } /* * Get the owner heap of the handle. This is union-find with * path-compression between heaps. */ PairingHeap getOwner() { if (heap.other != heap) { // find root PairingHeap root = heap; while (root != root.other) { root = root.other; } // path-compression PairingHeap cur = heap; while (cur.other != root) { PairingHeap next = cur.other; cur.other = root; cur = next; } heap = root; } return heap; } } /** * Decrease the key of a node. * * @param n * the node * @param newKey * the new key */ @SuppressWarnings("unchecked") private void decreaseKey(Node n, K newKey) { int c; if (comparator == null) { c = ((Comparable) newKey).compareTo(n.key); } else { c = comparator.compare(newKey, n.key); } if (c > 0) { throw new IllegalArgumentException("Keys can only be decreased!"); } n.key = newKey; if (c == 0 || root == n) { return; } if (n.o_s == null) { throw new IllegalArgumentException("Invalid handle!"); } // unlink from parent if (n.y_s != null) { n.y_s.o_s = n.o_s; } if (n.o_s.o_c == n) { // I am the oldest :( n.o_s.o_c = n.y_s; } else { // I have an older sibling! n.o_s.y_s = n.y_s; } n.y_s = null; n.o_s = null; // merge with root if (comparator == null) { root = link(root, n); } else { root = linkWithComparator(root, n); } } /* * Delete a node */ private void delete(Node n) { if (root == n) { deleteMin(); n.o_c = null; n.y_s = null; n.o_s = null; return; } if (n.o_s == null) { throw new IllegalArgumentException("Invalid handle!"); } // unlink from parent if (n.y_s != null) { n.y_s.o_s = n.o_s; } if (n.o_s.o_c == n) { // I am the oldest :( n.o_s.o_c = n.y_s; } else { // I have an older sibling! n.o_s.y_s = n.y_s; } n.y_s = null; n.o_s = null; // perform delete-min at tree rooted at this Node t = combine(cutChildren(n)); // and merge with other cut tree if (comparator == null) { root = link(root, t); } else { root = linkWithComparator(root, t); } size--; } /* * Two pass pair and compute root. */ private Node combine(Node l) { if (l == null) { return null; } assert l.o_s == null; // left-right pass Node pairs = null; Node it = l, p_it; if (comparator == null) { // no comparator while (it != null) { p_it = it; it = it.y_s; if (it == null) { // append last node to pair list p_it.y_s = pairs; p_it.o_s = null; pairs = p_it; } else { Node n_it = it.y_s; // disconnect both p_it.y_s = null; p_it.o_s = null; it.y_s = null; it.o_s = null; // link trees p_it = link(p_it, it); // append to pair list p_it.y_s = pairs; pairs = p_it; // advance it = n_it; } } } else { while (it != null) { p_it = it; it = it.y_s; if (it == null) { // append last node to pair list p_it.y_s = pairs; p_it.o_s = null; pairs = p_it; } else { Node n_it = it.y_s; // disconnect both p_it.y_s = null; p_it.o_s = null; it.y_s = null; it.o_s = null; // link trees p_it = linkWithComparator(p_it, it); // append to pair list p_it.y_s = pairs; pairs = p_it; // advance it = n_it; } } } // second pass (reverse order - due to add first) it = pairs; Node f = null; if (comparator == null) { while (it != null) { Node nextIt = it.y_s; it.y_s = null; f = link(f, it); it = nextIt; } } else { while (it != null) { Node nextIt = it.y_s; it.y_s = null; f = linkWithComparator(f, it); it = nextIt; } } return f; } /** * Cut the children of a node and return the list. * * @param n * the node * @return the first node in the children list */ private Node cutChildren(Node n) { Node child = n.o_c; n.o_c = null; if (child != null) { child.o_s = null; } return child; } @SuppressWarnings("unchecked") private Node link(Node f, Node s) { if (s == null) { return f; } else if (f == null) { return s; } else if (((Comparable) f.key).compareTo(s.key) <= 0) { s.y_s = f.o_c; s.o_s = f; if (f.o_c != null) { f.o_c.o_s = s; } f.o_c = s; return f; } else { return link(s, f); } } private Node linkWithComparator(Node f, Node s) { if (s == null) { return f; } else if (f == null) { return s; } else if (comparator.compare(f.key, s.key) <= 0) { s.y_s = f.o_c; s.o_s = f; if (f.o_c != null) { f.o_c.o_s = s; } f.o_c = s; return f; } else { return linkWithComparator(s, f); } } } jheaps-jheaps-0.14/src/main/java/org/jheaps/tree/RankPairingHeap.java000066400000000000000000000507541367505655000255110ustar00rootroot00000000000000package org.jheaps.tree; import java.io.Serializable; import java.lang.reflect.Array; import java.util.Comparator; import java.util.NoSuchElementException; import org.jheaps.AddressableHeap; import org.jheaps.MergeableAddressableHeap; import org.jheaps.annotations.ConstantTime; import org.jheaps.annotations.LogarithmicTime; /** * Rank-Pairing heaps. The heap is sorted according to the * {@linkplain Comparable natural ordering} of its keys, or by a * {@link Comparator} provided at heap creation time, depending on which * constructor is used. * *

* This is the type-1 rank-pairing heap described in detail in the following * paper: *

    *
  • B Haeupler, S Sen, RE Tarjan. Rank-Pairing Heaps. SIAM Journal of * Computing, 40(6): 1463--1485, 2011.
  • *
* *

* This implementation provides amortized O(1) time for operations that do not * involve deleting an element such as {@code insert}, and {@code decreaseKey}. * Operations {@code deleteMin} and {@code delete} are amortized O(log(n)). The * operation {@code meld} is also amortized O(1). * *

* All the above bounds, however, assume that the user does not perform * cascading melds on heaps such as: * *

 * d.meld(e);
 * c.meld(d);
 * b.meld(c);
 * a.meld(b);
 * 
* * The above scenario, although efficiently supported by using union-find with * path compression, invalidates the claimed bounds. * *

* Note that the ordering maintained by this heap, like any heap, and whether or * not an explicit comparator is provided, must be consistent with * {@code equals} if this heap is to correctly implement the * {@code AddressableHeap} interface. (See {@code Comparable} or * {@code Comparator} for a precise definition of consistent with * equals.) This is so because the {@code AddressableHeap} interface is * defined in terms of the {@code equals} operation, but this heap performs all * key comparisons using its {@code compareTo} (or {@code compare}) method, so * two keys that are deemed equal by this method are, from the standpoint of * this heap, equal. The behavior of a heap is well-defined even if its * ordering is inconsistent with {@code equals}; it just fails to obey the * general contract of the {@code AddressableHeap} interface. * *

* Note that this implementation is not synchronized. If * multiple threads access a heap concurrently, and at least one of the threads * modifies the heap structurally, it must be synchronized externally. * (A structural modification is any operation that adds or deletes one or more * elements or changing the key of some element.) This is typically accomplished * by synchronizing on some object that naturally encapsulates the heap. * * @param * the type of keys maintained by this heap * @param * the type of values maintained by this heap * * @author Dimitrios Michail * * @see RankPairingHeap * @see CostlessMeldPairingHeap * @see FibonacciHeap */ public class RankPairingHeap implements MergeableAddressableHeap, Serializable { private final static long serialVersionUID = 1; /** * Size of bucket array. Based on maximum rank. */ private final static int AUX_BUCKET_ARRAY_SIZE = 65; /** * The comparator used to maintain order in this heap, or null if it uses * the natural ordering of its keys. * * @serial */ private final Comparator comparator; /** * The last node in the root list */ private Node minRoot; /** * Size of the pairing heap */ private long size; /** * Auxiliary array for consolidation. */ private Node[] aux; /** * Used to reference the current heap or some other pairing heap in case of * melding, so that handles remain valid even after a meld, without having * to iterate over them. * * In order to avoid maintaining a full-fledged union-find data structure, * we disallow a heap to be used in melding more than once. We use however, * path-compression in case of cascading melds, that it, a handle moves from * one heap to another and then another. */ private RankPairingHeap other; /** * Constructs a new, empty heap, using the natural ordering of its keys. All * keys inserted into the heap must implement the {@link Comparable} * interface. Furthermore, all such keys must be mutually * comparable: {@code k1.compareTo(k2)} must not throw a * {@code ClassCastException} for any keys {@code k1} and {@code k2} in the * heap. If the user attempts to put a key into the heap that violates this * constraint (for example, the user attempts to put a string key into a * heap whose keys are integers), the {@code insert(Object key)} call will * throw a {@code ClassCastException}. */ @ConstantTime public RankPairingHeap() { this(null); } /** * Constructs a new, empty heap, ordered according to the given comparator. * All keys inserted into the heap must be mutually comparable by * the given comparator: {@code comparator.compare(k1, * k2)} must not throw a {@code ClassCastException} for any keys {@code k1} * and {@code k2} in the heap. If the user attempts to put a key into the * heap that violates this constraint, the {@code insert(Object key)} call * will throw a {@code ClassCastException}. * * @param comparator * the comparator that will be used to order this heap. If * {@code null}, the {@linkplain Comparable natural ordering} of * the keys will be used. */ @ConstantTime @SuppressWarnings("unchecked") public RankPairingHeap(Comparator comparator) { this.minRoot = null; this.comparator = comparator; this.size = 0; this.aux = (Node[]) Array.newInstance(Node.class, AUX_BUCKET_ARRAY_SIZE); this.other = this; } /** * {@inheritDoc} * * @throws IllegalStateException * if the heap has already been used in the right hand side of a * meld */ @Override @ConstantTime(amortized = true) public AddressableHeap.Handle insert(K key, V value) { if (other != this) { throw new IllegalStateException("A heap cannot be used after a meld"); } if (key == null) { throw new NullPointerException("Null keys not permitted"); } Node n = new Node(this, key, value); if (minRoot == null) { n.r = n; minRoot = n; } else { n.r = minRoot.r; minRoot.r = n; if (less(n, minRoot)) { minRoot = n; } } size++; return n; } /** * {@inheritDoc} * * @throws IllegalStateException * if the heap has already been used in the right hand side of a * meld */ @Override @ConstantTime(amortized = true) public AddressableHeap.Handle insert(K key) { return insert(key, null); } /** * {@inheritDoc} */ @Override @ConstantTime(amortized = false) public AddressableHeap.Handle findMin() { if (size == 0) { throw new NoSuchElementException(); } return minRoot; } /** * {@inheritDoc} */ @Override @LogarithmicTime(amortized = true) public AddressableHeap.Handle deleteMin() { if (size == 0) { throw new NoSuchElementException(); } /* * Sever spine of left child of minimum */ Node oldMinRoot = minRoot; Node spine = null; if (minRoot.l != null) { spine = severSpine(minRoot.l); minRoot.l = null; } /* * One pass spine */ int maxRank = -1; Node output = null; while (spine != null) { // remove second from list Node cur; if (spine.r == spine) { cur = spine; spine = null; } else { cur = spine.r; spine.r = cur.r; } cur.r = null; int rank = cur.rank; Node auxEntry = aux[rank]; if (auxEntry == null) { aux[rank] = cur; if (rank > maxRank) { maxRank = rank; } } else { aux[rank] = null; cur = link(cur, auxEntry); // add to output list if (output == null) { cur.r = cur; output = cur; } else { cur.r = output.r; output.r = cur; if (less(cur, output)) { // keep track of new minimum output = cur; } } } } /* * One pass old half-trees, careful to skip old minimum which is still * in the root list. */ while (minRoot != null) { // remove second from list Node cur; if (minRoot.r == minRoot) { cur = minRoot; minRoot = null; } else { cur = minRoot.r; minRoot.r = cur.r; } cur.r = null; if (cur == oldMinRoot) { // skip the old minimum continue; } int rank = cur.rank; Node auxEntry = aux[rank]; if (auxEntry == null) { aux[rank] = cur; if (rank > maxRank) { maxRank = rank; } } else { aux[rank] = null; cur = link(cur, auxEntry); // add to output list if (output == null) { cur.r = cur; output = cur; } else { cur.r = output.r; output.r = cur; if (less(cur, output)) { // keep track of new minimum output = cur; } } } } /* * Process remaining in buckets */ for (int i = 0; i <= maxRank; i++) { Node cur = aux[i]; if (cur != null) { aux[i] = null; // add to output list if (output == null) { cur.r = cur; output = cur; } else { cur.r = output.r; output.r = cur; if (less(cur, output)) { // keep track of new minimum output = cur; } } } } minRoot = output; size--; return oldMinRoot; } /** * {@inheritDoc} */ @Override @ConstantTime public boolean isEmpty() { return size == 0; } /** * {@inheritDoc} */ @Override @ConstantTime public long size() { return size; } /** * {@inheritDoc} */ @Override public Comparator comparator() { return comparator; } /** * {@inheritDoc} */ @Override @ConstantTime(amortized = false) public void clear() { minRoot = null; size = 0; } /** * {@inheritDoc} */ @Override @ConstantTime public void meld(MergeableAddressableHeap other) { RankPairingHeap h = (RankPairingHeap) other; // check same comparator if (comparator != null) { if (h.comparator == null || !h.comparator.equals(comparator)) { throw new IllegalArgumentException("Cannot meld heaps using different comparators!"); } } else if (h.comparator != null) { throw new IllegalArgumentException("Cannot meld heaps using different comparators!"); } if (h.other != h) { throw new IllegalStateException("A heap cannot be used after a meld."); } // meld if (minRoot == null) { minRoot = h.minRoot; } else if (h.minRoot != null) { Node afterMinRoot = minRoot.r; Node hAfterMinRoot = h.minRoot.r; minRoot.r = hAfterMinRoot; h.minRoot.r = afterMinRoot; if (less(h.minRoot, minRoot)) { minRoot = h.minRoot; } } size += h.size; // clear other h.size = 0; h.minRoot = null; // take ownership h.other = this; } // -------------------------------------------------------------------- static class Node implements AddressableHeap.Handle, Serializable { private final static long serialVersionUID = 1; /* * We maintain explicitly the belonging heap, instead of using an inner * class due to possible cascading melding. */ RankPairingHeap heap; K key; V value; Node p; // parent Node l; // left child Node r; // right child or next root int rank; Node(RankPairingHeap heap, K key, V value) { this.heap = heap; this.key = key; this.value = value; this.p = null; this.l = null; this.r = null; this.rank = 0; } /** * {@inheritDoc} */ @Override public K getKey() { return key; } /** * {@inheritDoc} */ @Override public V getValue() { return value; } /** * {@inheritDoc} */ @Override public void setValue(V value) { this.value = value; } /** * {@inheritDoc} */ @Override @ConstantTime(amortized = true) public void decreaseKey(K newKey) { getOwner().decreaseKey(this, newKey); } /** * {@inheritDoc} */ @Override @LogarithmicTime(amortized = true) public void delete() { if (p == null && r == null) { throw new IllegalArgumentException("Invalid handle!"); } RankPairingHeap h = getOwner(); h.forceDecreaseKeyToMinimum(this); h.deleteMin(); } /* * Get the owner heap of the handle. This is union-find with * path-compression between heaps. */ RankPairingHeap getOwner() { if (heap.other != heap) { // find root RankPairingHeap root = heap; while (root != root.other) { root = root.other; } // path-compression RankPairingHeap cur = heap; while (cur.other != root) { RankPairingHeap next = cur.other; cur.other = root; cur = next; } heap = root; } return heap; } } /* * Decrease the key of a node to the minimum. Helper function for performing * a delete operation. Does not change the node's actual key, but behaves as * the key is the minimum key in the heap. */ private void forceDecreaseKeyToMinimum(Node n) { // already at root list if (n.p == null) { minRoot = n; return; } // perform the cut Node u = n.p; cut(n); if (minRoot == null) { n.r = n; } else { n.r = minRoot.r; minRoot.r = n; } minRoot = n; // restore type-1 ranks n.rank = (n.l == null) ? 0 : n.l.rank + 1; restoreType1Ranks(u); } /** * Decrease the key of a node. * * @param n * the node * @param newKey * the new key */ @SuppressWarnings("unchecked") private void decreaseKey(Node n, K newKey) { int c; if (comparator == null) { c = ((Comparable) newKey).compareTo(n.key); } else { c = comparator.compare(newKey, n.key); } if (c > 0) { throw new IllegalArgumentException("Keys can only be decreased!"); } n.key = newKey; if (c == 0) { return; } if (n.p == null && n.r == null) { throw new IllegalArgumentException("Invalid handle!"); } // already at root list, just update minimum if needed if (n.p == null) { if (less(n, minRoot)) { minRoot = n; } return; } // perform the cut Node u = n.p; cut(n); if (minRoot == null) { n.r = n; minRoot = n; } else { n.r = minRoot.r; minRoot.r = n; if (less(n, minRoot)) { minRoot = n; } } n.rank = (n.l == null) ? 0 : n.l.rank + 1; restoreType1Ranks(u); } /* * Return the minimum node */ @SuppressWarnings("unchecked") private boolean less(Node x, Node y) { if (comparator == null) { return (((Comparable) x.key).compareTo(y.key) < 0); } else { return comparator.compare(x.key, y.key) < 0; } } /* * Iterate over all right children of x and create a list of half-tree * roots. We only need to nullify the parent pointers. At the same time give * each new root a rank that is one greater than that of its left child. */ private Node severSpine(Node x) { Node cur = x; while (cur.r != null) { cur.p = null; if (cur.l == null) { cur.rank = 0; } else { cur.rank = cur.l.rank + 1; } cur = cur.r; } cur.p = null; if (cur.l == null) { cur.rank = 0; } else { cur.rank = cur.l.rank + 1; } cur.r = x; return x; } /* * Link two half-trees. Assumes that x and y are the roots. */ @SuppressWarnings("unchecked") private Node link(Node x, Node y) { assert x.rank == y.rank; int c; if (comparator == null) { c = ((Comparable) x.key).compareTo(y.key); } else { c = comparator.compare(x.key, y.key); } if (c <= 0) { y.r = x.l; if (x.l != null) { x.l.p = y; } x.l = y; y.p = x; x.rank += 1; return x; } else { x.r = y.l; if (y.l != null) { y.l.p = x; } y.l = x; x.p = y; y.rank += 1; return y; } } /* * Cut x and its left child from its parent. Leave the right child of x in * place of x. */ private void cut(Node x) { Node u = x.p; assert u != null; Node y = x.r; if (u.l == x) { u.l = y; } else { u.r = y; } if (y != null) { y.p = u; } x.p = null; x.r = x; } /* * Restore the type-1 ranks after a cut. * * @param The old parent of the node which was cut. */ private void restoreType1Ranks(Node u) { while (u != null) { int leftRank = (u.l == null) ? -1 : u.l.rank; if (u.p == null) { u.rank = leftRank + 1; break; } int rightRank = (u.r == null) ? -1 : u.r.rank; int k = (leftRank == rightRank) ? leftRank + 1 : Math.max(leftRank, rightRank); if (k >= u.rank) { break; } u.rank = k; u = u.p; } } } jheaps-jheaps-0.14/src/main/java/org/jheaps/tree/ReflectedFibonacciHeap.java000066400000000000000000000130251367505655000267650ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.tree; import java.util.Comparator; import org.jheaps.AddressableHeap; import org.jheaps.AddressableHeapFactory; /** * Reflected double ended heaps based on Fibonacci heaps. The heap is sorted * according to the {@linkplain Comparable natural ordering} of its keys, or by * a {@link Comparator} provided at heap creation time, depending on which * constructor is used. * *

* This class implements a general technique which uses two * {@link FibonacciHeap}s to implement a double ended heap, described in detail * in the following * paper: *

    *
  • C. Makris, A. Tsakalidis, and K. Tsichlas. Reflected min-max heaps. * Information Processing Letters, 86(4), 209--214, 2003.
  • *
* *

* This implementation provides amortized O(1) time for {@code insert}, * {@code findMin}, and {@code findMax}. Operations {@code decreaseKey}, * {@code increaseKey}, {@code deleteMin}, {@code deleteMax}, and {@code delete} * are amortized O(log(n)). The operation {@code meld} is amortized O(1). * *

* All the above bounds, however, assume that the user does not perform * cascading melds on heaps such as: * *

 * d.meld(e);
 * c.meld(d);
 * b.meld(c);
 * a.meld(b);
 * 
* * The above scenario, although efficiently supported by using union-find with * path compression, invalidates the claimed bounds. * *

* Note that the ordering maintained by a this heap, like any heap, and whether * or not an explicit comparator is provided, must be consistent with * {@code equals} if this heap is to correctly implement the * {@code AddressableHeap} interface. (See {@code Comparable} or * {@code Comparator} for a precise definition of consistent with * equals.) This is so because the {@code AddressableHeap} interface is * defined in terms of the {@code equals} operation, but this heap performs all * key comparisons using its {@code compareTo} (or {@code compare}) method, so * two keys that are deemed equal by this method are, from the standpoint of * this heap, equal. The behavior of a heap is well-defined even if its * ordering is inconsistent with {@code equals}; it just fails to obey the * general contract of the {@code AddressableHeap} interface. * *

* Note that this implementation is not synchronized. If * multiple threads access a heap concurrently, and at least one of the threads * modifies the heap structurally, it must be synchronized externally. * (A structural modification is any operation that adds or deletes one or more * elements or changing the key of some element.) This is typically accomplished * by synchronizing on some object that naturally encapsulates the heap. * * @param * the type of keys maintained by this heap * @param * the type of values maintained by this heap * * @author Dimitrios Michail */ public class ReflectedFibonacciHeap extends ReflectedHeap { private static final long serialVersionUID = 651281438828109106L; /** * Constructs a new, empty heap, using the natural ordering of its keys. All * keys inserted into the heap must implement the {@link Comparable} * interface. Furthermore, all such keys must be mutually * comparable: {@code k1.compareTo(k2)} must not throw a * {@code ClassCastException} for any keys {@code k1} and {@code k2} in the * heap. If the user attempts to put a key into the heap that violates this * constraint (for example, the user attempts to put a string key into a * heap whose keys are integers), the {@code insert(Object key)} call will * throw a {@code ClassCastException}. */ public ReflectedFibonacciHeap() { this(null); } /** * Constructs a new, empty heap, ordered according to the given comparator. * All keys inserted into the heap must be mutually comparable by * the given comparator: {@code comparator.compare(k1, * k2)} must not throw a {@code ClassCastException} for any keys {@code k1} * and {@code k2} in the heap. If the user attempts to put a key into the * heap that violates this constraint, the {@code insert(Object key)} call * will throw a {@code ClassCastException}. * * @param comparator * the comparator that will be used to order this heap. If * {@code null}, the {@linkplain Comparable natural ordering} of * the keys will be used. */ public ReflectedFibonacciHeap(Comparator comparator) { super(new Factory(), comparator); } // underlying heap factory private static class Factory implements AddressableHeapFactory { @Override public AddressableHeap get(Comparator comparator) { return new FibonacciHeap(comparator); } } } jheaps-jheaps-0.14/src/main/java/org/jheaps/tree/ReflectedHeap.java000066400000000000000000000630171367505655000251750ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.tree; import java.io.Serializable; import java.util.Collections; import java.util.Comparator; import java.util.NoSuchElementException; import org.jheaps.AddressableHeap; import org.jheaps.AddressableHeapFactory; import org.jheaps.MergeableAddressableHeap; import org.jheaps.MergeableDoubleEndedAddressableHeap; /** * Reflected double ended heaps. The heap is sorted according to the * {@linkplain Comparable natural ordering} of its keys, or by a * {@link Comparator} provided at heap creation time, depending on which * constructor is used. * *

* This class implements a general technique which uses two * {@link MergeableAddressableHeap}s to implement a double ended heap, described * in detail in the following * paper: *

    *
  • C. Makris, A. Tsakalidis, and K. Tsichlas. Reflected min-max heaps. * Information Processing Letters, 86(4), 209--214, 2003.
  • *
* *

* The running time bounds depend on the implementation of the underlying used * heap. All the above bounds, however, assume that the user does not perform * cascading melds on heaps such as: * *

 * d.meld(e);
 * c.meld(d);
 * b.meld(c);
 * a.meld(b);
 * 
* * The above scenario, although efficiently supported by using union-find with * path compression, invalidates the claimed bounds. * *

* Note that the ordering maintained by a this heap, like any heap, and whether * or not an explicit comparator is provided, must be consistent with * {@code equals} if this heap is to correctly implement the * {@code AddressableHeap} interface. (See {@code Comparable} or * {@code Comparator} for a precise definition of consistent with * equals.) This is so because the {@code AddressableHeap} interface is * defined in terms of the {@code equals} operation, but this heap performs all * key comparisons using its {@code compareTo} (or {@code compare}) method, so * two keys that are deemed equal by this method are, from the standpoint of * this heap, equal. The behavior of a heap is well-defined even if its * ordering is inconsistent with {@code equals}; it just fails to obey the * general contract of the {@code AddressableHeap} interface. * *

* Note that this implementation is not synchronized. If * multiple threads access a heap concurrently, and at least one of the threads * modifies the heap structurally, it must be synchronized externally. * (A structural modification is any operation that adds or deletes one or more * elements or changing the key of some element.) This is typically accomplished * by synchronizing on some object that naturally encapsulates the heap. * * @param * the type of keys maintained by this heap * @param * the type of values maintained by this heap * * @author Dimitrios Michail */ public class ReflectedHeap implements MergeableDoubleEndedAddressableHeap, Serializable { private static final long serialVersionUID = -5428954082047233961L; /** * The comparator used to maintain order in this heap, or null if it uses * the natural ordering of its keys. * * @serial */ private final Comparator comparator; /** * A minimum heap */ private final AddressableHeap> minHeap; /** * A maximum heap */ private final AddressableHeap> maxHeap; /** * A free element in case the size is odd */ private ReflectedHandle free; /** * Size of the heap */ private long size; /** * Used to reference the current heap or some other heap in case of melding, * so that handles remain valid even after a meld, without having to iterate * over them. * * In order to avoid maintaining a full-fledged union-find data structure, * we disallow a heap to be used in melding more than once. We use however, * path-compression in case of cascading melds, that it, a handle moves from * one heap to another and then another. */ private ReflectedHeap other; /** * Constructs a new, empty heap, using the natural ordering of its keys. All * keys inserted into the heap must implement the {@link Comparable} * interface. Furthermore, all such keys must be mutually * comparable: {@code k1.compareTo(k2)} must not throw a * {@code ClassCastException} for any keys {@code k1} and {@code k2} in the * heap. If the user attempts to put a key into the heap that violates this * constraint (for example, the user attempts to put a string key into a * heap whose keys are integers), the {@code insert(Object key)} call will * throw a {@code ClassCastException}. * * @param heapFactory * a factory for the underlying heap implementation * @throws NullPointerException * if the heap factory is null */ public ReflectedHeap(AddressableHeapFactory heapFactory) { this(heapFactory, null); } /** * Constructs a new, empty heap, ordered according to the given comparator. * All keys inserted into the heap must be mutually comparable by * the given comparator: {@code comparator.compare(k1, * k2)} must not throw a {@code ClassCastException} for any keys {@code k1} * and {@code k2} in the heap. If the user attempts to put a key into the * heap that violates this constraint, the {@code insert(Object key)} call * will throw a {@code ClassCastException}. * * @param heapFactory * a factory for the underlying heap implementation * @param comparator * the comparator that will be used to order this heap. If * {@code null}, the {@linkplain Comparable natural ordering} of * the keys will be used. * * @throws NullPointerException * if the heap factory is null */ @SuppressWarnings("unchecked") public ReflectedHeap(AddressableHeapFactory heapFactory, Comparator comparator) { if (heapFactory == null) { throw new NullPointerException("Underlying heap factory cannot be null"); } this.comparator = comparator; this.minHeap = (AddressableHeap>) heapFactory.get(comparator); this.maxHeap = (AddressableHeap>) heapFactory.get(Collections.reverseOrder(comparator)); this.free = null; this.size = 0; this.other = this; } /** * {@inheritDoc} */ @Override public Comparator comparator() { return comparator; } /** * {@inheritDoc} */ @Override public boolean isEmpty() { return size == 0; } /** * {@inheritDoc} */ @Override public long size() { return size; } /** * {@inheritDoc} */ @Override public void clear() { size = 0; free = null; minHeap.clear(); maxHeap.clear(); } /** * {@inheritDoc} */ @Override public Handle insert(K key, V value) { if (key == null) { throw new NullPointerException("Null keys not permitted"); } else if (other != this) { throw new IllegalStateException("A heap cannot be used after a meld"); } else if (size % 2 == 0) { free = new ReflectedHandle(this, key, value); size++; return free; } else { ReflectedHandle newHandle = new ReflectedHandle(this, key, value); insertPair(newHandle, free); free = null; size++; return newHandle; } } /** * {@inheritDoc} */ @Override public Handle insert(K key) { return insert(key, null); } /** * {@inheritDoc} */ @Override @SuppressWarnings("unchecked") public Handle findMin() { if (size == 0) { throw new NoSuchElementException(); } else if (size == 1) { return free; } else if (size % 2 == 0) { return minHeap.findMin().getValue().outer; } else { AddressableHeap.Handle> minInnerHandle = minHeap.findMin(); int c; if (comparator == null) { c = ((Comparable) minInnerHandle.getKey()).compareTo(free.key); } else { c = comparator.compare(minInnerHandle.getKey(), free.key); } if (c < 0) { return minInnerHandle.getValue().outer; } else { return free; } } } /** * {@inheritDoc} */ @Override @SuppressWarnings("unchecked") public Handle findMax() { if (size == 0) { throw new NoSuchElementException(); } else if (size == 1) { return free; } else if (size % 2 == 0) { return maxHeap.findMin().getValue().outer; } else { AddressableHeap.Handle> maxInnerHandle = maxHeap.findMin(); int c; if (comparator == null) { c = ((Comparable) maxInnerHandle.getKey()).compareTo(free.key); } else { c = comparator.compare(maxInnerHandle.getKey(), free.key); } if (c > 0) { return maxInnerHandle.getValue().outer; } else { return free; } } } /** * {@inheritDoc} */ @Override @SuppressWarnings("unchecked") public Handle deleteMin() { if (size == 0) { throw new NoSuchElementException(); } else if (size == 1) { Handle min = free; free = null; size--; return min; } else if (size % 2 == 0) { // find min AddressableHeap.Handle> minInner = minHeap.deleteMin(); ReflectedHandle minOuter = minInner.getValue().outer; minOuter.inner = null; minOuter.minNotMax = false; // delete max and keep as free AddressableHeap.Handle> maxInner = minInner.getValue().otherInner; ReflectedHandle maxOuter = maxInner.getValue().outer; maxInner.delete(); maxOuter.inner = null; maxOuter.minNotMax = false; free = maxOuter; size--; return minOuter; } else { // find min AddressableHeap.Handle> minInner = minHeap.findMin(); int c; if (comparator == null) { c = ((Comparable) minInner.getKey()).compareTo(free.key); } else { c = comparator.compare(minInner.getKey(), free.key); } if (c >= 0) { Handle min = free; free = null; size--; return min; } // minInner is smaller minInner.delete(); ReflectedHandle minOuter = minInner.getValue().outer; minOuter.inner = null; minOuter.minNotMax = false; // delete max AddressableHeap.Handle> maxInner = minInner.getValue().otherInner; ReflectedHandle maxOuter = maxInner.getValue().outer; maxInner.delete(); maxOuter.inner = null; maxOuter.minNotMax = false; // reinsert max with free insertPair(maxOuter, free); free = null; size--; return minOuter; } } /** * {@inheritDoc} */ @Override @SuppressWarnings("unchecked") public Handle deleteMax() { if (size == 0) { throw new NoSuchElementException(); } else if (size == 1) { Handle max = free; free = null; size--; return max; } else if (size % 2 == 0) { // find max AddressableHeap.Handle> maxInner = maxHeap.deleteMin(); ReflectedHandle maxOuter = maxInner.getValue().outer; maxOuter.inner = null; maxOuter.minNotMax = false; // delete min and keep as free AddressableHeap.Handle> minInner = maxInner.getValue().otherInner; ReflectedHandle minOuter = minInner.getValue().outer; minInner.delete(); minOuter.inner = null; minOuter.minNotMax = false; free = minOuter; size--; return maxOuter; } else { // find max AddressableHeap.Handle> maxInner = maxHeap.findMin(); int c; if (comparator == null) { c = ((Comparable) maxInner.getKey()).compareTo(free.key); } else { c = comparator.compare(maxInner.getKey(), free.key); } if (c < 0) { Handle max = free; free = null; size--; return max; } // maxInner is larger maxInner.delete(); ReflectedHandle maxOuter = maxInner.getValue().outer; maxOuter.inner = null; maxOuter.minNotMax = false; // delete min AddressableHeap.Handle> minInner = maxInner.getValue().otherInner; ReflectedHandle minOuter = minInner.getValue().outer; minInner.delete(); minOuter.inner = null; minOuter.minNotMax = false; // reinsert min with free insertPair(minOuter, free); free = null; size--; return maxOuter; } } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") @Override public void meld(MergeableDoubleEndedAddressableHeap other) { ReflectedHeap h = (ReflectedHeap) other; // check same comparator if (comparator != null) { if (h.comparator == null || !h.comparator.equals(comparator)) { throw new IllegalArgumentException("Cannot meld heaps using different comparators!"); } } else if (h.comparator != null) { throw new IllegalArgumentException("Cannot meld heaps using different comparators!"); } if (h.other != h) { throw new IllegalStateException("A heap cannot be used after a meld."); } if (!(minHeap instanceof MergeableAddressableHeap)) { throw new IllegalArgumentException("Underlying heaps are not meldable."); } // meld min heaps MergeableAddressableHeap minAsMergeableHeap = (MergeableAddressableHeap) minHeap; MergeableAddressableHeap hMinAsMergeableHeap = (MergeableAddressableHeap) h.minHeap; minAsMergeableHeap.meld(hMinAsMergeableHeap); // meld max heaps MergeableAddressableHeap maxAsMergeableHeap = (MergeableAddressableHeap) maxHeap; MergeableAddressableHeap hMaxAsMergeableHeap = (MergeableAddressableHeap) h.maxHeap; maxAsMergeableHeap.meld(hMaxAsMergeableHeap); // meld free if (free == null) { if (h.free != null) { free = h.free; h.free = null; } } else { if (h.free != null) { insertPair(free, h.free); h.free = null; free = null; } } // set new sizes size += h.size; h.size = 0; // take ownership h.other = this; } /** * Insert a pair of elements, one in the min heap and one in the max heap. * * @param handle1 * a handle to the first element * @param handle2 * a handle to the second element */ @SuppressWarnings("unchecked") private void insertPair(ReflectedHandle handle1, ReflectedHandle handle2) { int c; if (comparator == null) { c = ((Comparable) handle1.key).compareTo(handle2.key); } else { c = comparator.compare(handle1.key, handle2.key); } AddressableHeap.Handle> innerHandle1; AddressableHeap.Handle> innerHandle2; if (c <= 0) { innerHandle1 = minHeap.insert(handle1.key); handle1.minNotMax = true; innerHandle2 = maxHeap.insert(handle2.key); handle2.minNotMax = false; } else { innerHandle1 = maxHeap.insert(handle1.key); handle1.minNotMax = false; innerHandle2 = minHeap.insert(handle2.key); handle2.minNotMax = true; } handle1.inner = innerHandle1; handle2.inner = innerHandle2; innerHandle1.setValue(new HandleMap(handle1, innerHandle2)); innerHandle2.setValue(new HandleMap(handle2, innerHandle1)); } /** * Delete an element * * @param n * a handle to the element */ private void delete(ReflectedHandle n) { if (n.inner == null && free != n) { throw new IllegalArgumentException("Invalid handle!"); } if (free == n) { free = null; } else { // delete from inner queue AddressableHeap.Handle> nInner = n.inner; ReflectedHandle nOuter = nInner.getValue().outer; nInner.delete(); nOuter.inner = null; nOuter.minNotMax = false; // delete pair from inner queue AddressableHeap.Handle> otherInner = nInner.getValue().otherInner; ReflectedHandle otherOuter = otherInner.getValue().outer; otherInner.delete(); otherOuter.inner = null; otherOuter.minNotMax = false; // reinsert either as free or as pair with free if (free == null) { free = otherOuter; } else { insertPair(otherOuter, free); free = null; } } size--; } /** * Decrease the key of an element. * * @param n * the element * @param newKey * the new key */ @SuppressWarnings("unchecked") private void decreaseKey(ReflectedHandle n, K newKey) { if (n.inner == null && free != n) { throw new IllegalArgumentException("Invalid handle!"); } int c; if (comparator == null) { c = ((Comparable) newKey).compareTo(n.key); } else { c = comparator.compare(newKey, n.key); } if (c > 0) { throw new IllegalArgumentException("Keys can only be decreased!"); } n.key = newKey; if (c == 0 || free == n) { return; } // actual decrease AddressableHeap.Handle> nInner = n.inner; if (n.minNotMax) { // we are in the min heap, easy case n.inner.decreaseKey(newKey); } else { // we are in the max heap, remove nInner.delete(); ReflectedHandle nOuter = nInner.getValue().outer; nOuter.inner = null; nOuter.minNotMax = false; // remove min AddressableHeap.Handle> minInner = nInner.getValue().otherInner; ReflectedHandle minOuter = minInner.getValue().outer; minInner.delete(); minOuter.inner = null; minOuter.minNotMax = false; // update key nOuter.key = newKey; // reinsert both insertPair(nOuter, minOuter); } } /** * Increase the key of an element. * * @param n * the element * @param newKey * the new key */ @SuppressWarnings("unchecked") private void increaseKey(ReflectedHandle n, K newKey) { if (n.inner == null && free != n) { throw new IllegalArgumentException("Invalid handle!"); } int c; if (comparator == null) { c = ((Comparable) newKey).compareTo(n.key); } else { c = comparator.compare(newKey, n.key); } if (c < 0) { throw new IllegalArgumentException("Keys can only be increased!"); } n.key = newKey; if (c == 0 || free == n) { return; } // actual increase AddressableHeap.Handle> nInner = n.inner; if (!n.minNotMax) { // we are in the max heap, easy case n.inner.decreaseKey(newKey); } else { // we are in the min heap, remove nInner.delete(); ReflectedHandle nOuter = nInner.getValue().outer; nOuter.inner = null; nOuter.minNotMax = false; // remove max AddressableHeap.Handle> maxInner = nInner.getValue().otherInner; ReflectedHandle maxOuter = maxInner.getValue().outer; maxInner.delete(); maxOuter.inner = null; maxOuter.minNotMax = false; // update key nOuter.key = newKey; // reinsert both insertPair(nOuter, maxOuter); } } // ~------------------------------------------------------------------- /* * This is the outer handle which we provide to the users. */ private static class ReflectedHandle implements Handle, Serializable { private static final long serialVersionUID = 3179286196684064903L; /* * We maintain explicitly the belonging heap, instead of using an inner * class due to possible cascading melding. */ ReflectedHeap heap; K key; V value; /* * Whether the key is inside the minimum or the maximum heap (if not the * free element). */ boolean minNotMax; /* * Handle inside one of the inner heaps, or null if free element. */ AddressableHeap.Handle> inner; public ReflectedHandle(ReflectedHeap heap, K key, V value) { this.heap = heap; this.key = key; this.value = value; } /** * {@inheritDoc} */ @Override public K getKey() { return key; } /** * {@inheritDoc} */ @Override public V getValue() { return value; } /** * {@inheritDoc} */ @Override public void setValue(V value) { this.value = value; } /** * {@inheritDoc} */ @Override public void decreaseKey(K newKey) { getOwner().decreaseKey(this, newKey); } /** * {@inheritDoc} */ @Override public void delete() { getOwner().delete(this); } /** * {@inheritDoc} */ @Override public void increaseKey(K newKey) { getOwner().increaseKey(this, newKey); } /* * Get the owner heap of the handle. This is union-find with * path-compression between heaps. */ ReflectedHeap getOwner() { if (heap.other != heap) { // find root ReflectedHeap root = heap; while (root != root.other) { root = root.other; } // path-compression ReflectedHeap cur = heap; while (cur.other != root) { ReflectedHeap next = cur.other; cur.other = root; cur = next; } heap = root; } return heap; } } /* * Value kept in the inner heaps, in order to map (a) to the outer heap and * (b) to the pair inside the other inner heap. */ private static class HandleMap implements Serializable { private static final long serialVersionUID = 1L; ReflectedHandle outer; AddressableHeap.Handle> otherInner; public HandleMap(ReflectedHandle outer, AddressableHeap.Handle> otherInner) { this.outer = outer; this.otherInner = otherInner; } } } jheaps-jheaps-0.14/src/main/java/org/jheaps/tree/ReflectedPairingHeap.java000066400000000000000000000130421367505655000265000ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.tree; import java.util.Comparator; import org.jheaps.AddressableHeap; import org.jheaps.AddressableHeapFactory; /** * Reflected double ended heaps based on pairing heaps. The heap is sorted * according to the {@linkplain Comparable natural ordering} of its keys, or by * a {@link Comparator} provided at heap creation time, depending on which * constructor is used. * *

* This class implements a general technique which uses two {@link PairingHeap}s * to implement a double ended heap, described in detail in the following * paper: *

    *
  • C. Makris, A. Tsakalidis, and K. Tsichlas. Reflected min-max heaps. * Information Processing Letters, 86(4), 209--214, 2003.
  • *
* *

* This implementation provides amortized O(log(n)) time cost for the * {@code insert}, {@code deleteMin}, {@code deleteMax}, {@code decreaseKey}, * {@code increaseKey}, and {@code delete} operations. {@code findMin} and * {@code findMax} are worst-case O(1) operations. The operation {@code meld} is * amortized O(log(n)). * *

* All the above bounds, however, assume that the user does not perform * cascading melds on heaps such as: * *

 * d.meld(e);
 * c.meld(d);
 * b.meld(c);
 * a.meld(b);
 * 
* * The above scenario, although efficiently supported by using union-find with * path compression, invalidates the claimed bounds. * *

* Note that the ordering maintained by a this heap, like any heap, and whether * or not an explicit comparator is provided, must be consistent with * {@code equals} if this heap is to correctly implement the * {@code AddressableHeap} interface. (See {@code Comparable} or * {@code Comparator} for a precise definition of consistent with * equals.) This is so because the {@code AddressableHeap} interface is * defined in terms of the {@code equals} operation, but this heap performs all * key comparisons using its {@code compareTo} (or {@code compare}) method, so * two keys that are deemed equal by this method are, from the standpoint of * this heap, equal. The behavior of a heap is well-defined even if its * ordering is inconsistent with {@code equals}; it just fails to obey the * general contract of the {@code AddressableHeap} interface. * *

* Note that this implementation is not synchronized. If * multiple threads access a heap concurrently, and at least one of the threads * modifies the heap structurally, it must be synchronized externally. * (A structural modification is any operation that adds or deletes one or more * elements or changing the key of some element.) This is typically accomplished * by synchronizing on some object that naturally encapsulates the heap. * * @param * the type of keys maintained by this heap * @param * the type of values maintained by this heap * * @author Dimitrios Michail */ public class ReflectedPairingHeap extends ReflectedHeap { private static final long serialVersionUID = 651281438828109106L; /** * Constructs a new, empty heap, using the natural ordering of its keys. All * keys inserted into the heap must implement the {@link Comparable} * interface. Furthermore, all such keys must be mutually * comparable: {@code k1.compareTo(k2)} must not throw a * {@code ClassCastException} for any keys {@code k1} and {@code k2} in the * heap. If the user attempts to put a key into the heap that violates this * constraint (for example, the user attempts to put a string key into a * heap whose keys are integers), the {@code insert(Object key)} call will * throw a {@code ClassCastException}. */ public ReflectedPairingHeap() { this(null); } /** * Constructs a new, empty heap, ordered according to the given comparator. * All keys inserted into the heap must be mutually comparable by * the given comparator: {@code comparator.compare(k1, * k2)} must not throw a {@code ClassCastException} for any keys {@code k1} * and {@code k2} in the heap. If the user attempts to put a key into the * heap that violates this constraint, the {@code insert(Object key)} call * will throw a {@code ClassCastException}. * * @param comparator * the comparator that will be used to order this heap. If * {@code null}, the {@linkplain Comparable natural ordering} of * the keys will be used. */ public ReflectedPairingHeap(Comparator comparator) { super(new Factory(), comparator); } // underlying heap factory private static class Factory implements AddressableHeapFactory { @Override public AddressableHeap get(Comparator comparator) { return new PairingHeap(comparator); } } } jheaps-jheaps-0.14/src/main/java/org/jheaps/tree/SimpleFibonacciHeap.java000066400000000000000000000533511367505655000263270ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.tree; import java.io.Serializable; import java.lang.reflect.Array; import java.util.Comparator; import java.util.NoSuchElementException; import org.jheaps.AddressableHeap; import org.jheaps.MergeableAddressableHeap; import org.jheaps.annotations.ConstantTime; import org.jheaps.annotations.LogarithmicTime; /** * Simple Fibonacci heaps. The heap is sorted according to the * {@linkplain Comparable natural ordering} of its keys, or by a * {@link Comparator} provided at heap creation time, depending on which * constructor is used. * *

* This variant of the Fibonacci heaps is described in detail in the following * paper: *

    *
  • Haim Kaplan, Robert E. Tarjan, Uri Zwick, Fibonacci Heaps Revisited, * arXiv 1407.5750, 2014.
  • *
* *

* This implementation provides amortized O(1) time for operations that do not * involve deleting an element such as {@code insert}, and {@code decreaseKey}. * Operation {@code findMin} is worst-case O(1). Operations {@code deleteMin} * and {@code delete} are amortized O(log(n)). The operation {@code meld} is * also amortized O(1). * *

* All the above bounds, however, assume that the user does not perform * cascading melds on heaps such as: * *

 * d.meld(e);
 * c.meld(d);
 * b.meld(c);
 * a.meld(b);
 * 
* * The above scenario, although efficiently supported by using union-find with * path compression, invalidates the claimed bounds. * *

* Note that the ordering maintained by this heap, like any heap, and whether or * not an explicit comparator is provided, must be consistent with * {@code equals} if this heap is to correctly implement the * {@code AddressableHeap} interface. (See {@code Comparable} or * {@code Comparator} for a precise definition of consistent with * equals.) This is so because the {@code AddressableHeap} interface is * defined in terms of the {@code equals} operation, but this heap performs all * key comparisons using its {@code compareTo} (or {@code compare}) method, so * two keys that are deemed equal by this method are, from the standpoint of the * Fibonacci heap, equal. The behavior of a heap is well-defined even * if its ordering is inconsistent with {@code equals}; it just fails to obey * the general contract of the {@code AddressableHeap} interface. * *

* Note that this implementation is not synchronized. If * multiple threads access a heap concurrently, and at least one of the threads * modifies the heap structurally, it must be synchronized externally. * (A structural modification is any operation that adds or deletes one or more * elements or changing the key of some element.) This is typically accomplished * by synchronizing on some object that naturally encapsulates the heap. * * @param * the type of keys maintained by this heap * @param * the type of values maintained by this heap * * @author Dimitrios Michail * */ public class SimpleFibonacciHeap implements MergeableAddressableHeap, Serializable { private final static long serialVersionUID = 1; /** * Size of consolidation auxiliary array. Computed for number of elements * equal to {@link Long#MAX_VALUE}. */ private final static int AUX_CONSOLIDATE_ARRAY_SIZE = 91; /** * The comparator used to maintain order in this heap, or null if it uses * the natural ordering of its keys. * * @serial */ private final Comparator comparator; /** * The root */ private Node root; /** * Size of the heap */ private long size; /** * Auxiliary array for consolidation */ private Node[] aux; /** * Used to reference the current heap or some other heap in case of melding, * so that handles remain valid even after a meld, without having to iterate * over them. * * In order to avoid maintaining a full-fledged union-find data structure, * we disallow a heap to be used in melding more than once. We use however, * path-compression in case of cascading melds, that it, a handle moves from * one heap to another and then another. */ protected SimpleFibonacciHeap other; /** * Constructs a new, empty heap, using the natural ordering of its keys. All * keys inserted into the heap must implement the {@link Comparable} * interface. Furthermore, all such keys must be mutually * comparable: {@code k1.compareTo(k2)} must not throw a * {@code ClassCastException} for any keys {@code k1} and {@code k2} in the * heap. If the user attempts to put a key into the heap that violates this * constraint (for example, the user attempts to put a string key into a * heap whose keys are integers), the {@code insert(Object key)} call will * throw a {@code ClassCastException}. */ @ConstantTime public SimpleFibonacciHeap() { this(null); } /** * Constructs a new, empty heap, ordered according to the given comparator. * All keys inserted into the heap must be mutually comparable by * the given comparator: {@code comparator.compare(k1, * k2)} must not throw a {@code ClassCastException} for any keys {@code k1} * and {@code k2} in the heap. If the user attempts to put a key into the * heap that violates this constraint, the {@code insert(Object key)} call * will throw a {@code ClassCastException}. * * @param comparator * the comparator that will be used to order this heap. If * {@code null}, the {@linkplain Comparable natural ordering} of * the keys will be used. */ @ConstantTime @SuppressWarnings("unchecked") public SimpleFibonacciHeap(Comparator comparator) { this.root = null; this.comparator = comparator; this.size = 0; this.aux = (Node[]) Array.newInstance(Node.class, AUX_CONSOLIDATE_ARRAY_SIZE); this.other = this; } /** * {@inheritDoc} * * @throws IllegalStateException * if the heap has already been used in the right hand side of a * meld */ @Override @SuppressWarnings("unchecked") @ConstantTime(amortized = true) public AddressableHeap.Handle insert(K key, V value) { if (other != this) { throw new IllegalStateException("A heap cannot be used after a meld"); } if (key == null) { throw new NullPointerException("Null keys not permitted"); } Node n = new Node(this, key, value); if (root == null) { root = n; } else { if (comparator == null) { if (((Comparable) n.key).compareTo(root.key) < 0) { root = link(root, n); } else { link(n, root); } } else { if (comparator.compare(n.key, root.key) < 0) { root = link(root, n); } else { link(n, root); } } } size++; return n; } /** * {@inheritDoc} * * @throws IllegalStateException * if the heap has already been used in the right hand side of a * meld */ @Override @ConstantTime(amortized = true) public AddressableHeap.Handle insert(K key) { return insert(key, null); } /** * {@inheritDoc} */ @Override @ConstantTime(amortized = true) public AddressableHeap.Handle findMin() { if (size == 0) { throw new NoSuchElementException(); } return root; } /** * {@inheritDoc} */ @Override @LogarithmicTime(amortized = true) public AddressableHeap.Handle deleteMin() { if (comparator == null) { return comparableDeleteMin(); } else { return comparatorDeleteMin(); } } /** * {@inheritDoc} */ @Override @ConstantTime public boolean isEmpty() { return size == 0; } /** * {@inheritDoc} */ @Override @ConstantTime public long size() { return size; } /** * {@inheritDoc} */ @Override public Comparator comparator() { return comparator; } /** * {@inheritDoc} */ @Override @ConstantTime public void clear() { root = null; size = 0; } /** * {@inheritDoc} */ @Override @SuppressWarnings("unchecked") @ConstantTime(amortized = true) public void meld(MergeableAddressableHeap other) { SimpleFibonacciHeap h = (SimpleFibonacciHeap) other; // check same comparator if (comparator != null) { if (h.comparator == null || !h.comparator.equals(comparator)) { throw new IllegalArgumentException("Cannot meld heaps using different comparators!"); } } else if (h.comparator != null) { throw new IllegalArgumentException("Cannot meld heaps using different comparators!"); } if (h.other != h) { throw new IllegalStateException("A heap cannot be used after a meld."); } // meld if (root == null) { root = h.root; } else if (h.root != null) { if (comparator == null) { if (((Comparable) h.root.key).compareTo(root.key) < 0) { root = link(root, h.root); } else { link(h.root, root); } } else { if (comparator.compare(h.root.key, root.key) < 0) { root = link(root, h.root); } else { link(h.root, root); } } } size += h.size; // clear other h.size = 0; h.root = null; // take ownership h.other = this; } // -------------------------------------------------------------------- static class Node implements AddressableHeap.Handle, Serializable { private final static long serialVersionUID = 1; /* * We maintain explicitly the belonging heap, instead of using an inner * class due to possible cascading melding. */ SimpleFibonacciHeap heap; K key; V value; Node parent; // parent Node child; // any child Node next; // younger sibling Node prev; // older sibling int rank; // node rank boolean mark; // marked or not Node(SimpleFibonacciHeap heap, K key, V value) { this.heap = heap; this.key = key; this.value = value; this.parent = null; this.child = null; this.next = this; this.prev = this; this.rank = 0; this.mark = false; } /** * {@inheritDoc} */ @Override public K getKey() { return key; } /** * {@inheritDoc} */ @Override public V getValue() { return value; } /** * {@inheritDoc} */ @Override public void setValue(V value) { this.value = value; } /** * {@inheritDoc} */ @Override @ConstantTime(amortized = true) public void decreaseKey(K newKey) { SimpleFibonacciHeap h = getOwner(); if (h.comparator == null) { h.comparableDecreaseKey(this, newKey); } else { h.comparatorDecreaseKey(this, newKey); } } /** * {@inheritDoc} */ @Override @LogarithmicTime(amortized = true) public void delete() { if (this.next == null) { throw new IllegalArgumentException("Invalid handle!"); } SimpleFibonacciHeap h = getOwner(); h.forceDecreaseKeyToMinimum(this); h.deleteMin(); } /* * Get the owner heap of the handle. This is union-find with * path-compression between heaps. */ SimpleFibonacciHeap getOwner() { if (heap.other != heap) { // find root SimpleFibonacciHeap root = heap; while (root != root.other) { root = root.other; } // path-compression SimpleFibonacciHeap cur = heap; while (cur.other != root) { SimpleFibonacciHeap next = cur.other; cur.other = root; cur = next; } heap = root; } return heap; } } /* * Decrease the key of a node. */ @SuppressWarnings("unchecked") private void comparableDecreaseKey(Node n, K newKey) { int c = ((Comparable) newKey).compareTo(n.key); if (c > 0) { throw new IllegalArgumentException("Keys can only be decreased!"); } n.key = newKey; if (c == 0) { return; } if (n.next == null) { throw new IllegalArgumentException("Invalid handle!"); } // if not root and heap order violation Node y = n.parent; if (y != null && ((Comparable) n.key).compareTo(y.key) < 0) { cut(n, y); root.mark = false; cascadingRankChange(y); if (((Comparable) n.key).compareTo(root.key) < 0) { root = link(root, n); } else { link(n, root); } } } /* * Decrease the key of a node. */ private void comparatorDecreaseKey(Node n, K newKey) { int c = comparator.compare(newKey, n.key); if (c > 0) { throw new IllegalArgumentException("Keys can only be decreased!"); } n.key = newKey; if (c == 0) { return; } if (n.next == null) { throw new IllegalArgumentException("Invalid handle!"); } // if not root and heap order violation Node y = n.parent; if (y != null && comparator.compare(n.key, y.key) < 0) { cut(n, y); root.mark = false; cascadingRankChange(y); if (comparator.compare(n.key, root.key) < 0) { root = link(root, n); } else { link(n, root); } } } /* * Decrease the key of a node to the minimum. Helper function for performing * a delete operation. Does not change the node's actual key, but behaves as * the key is the minimum key in the heap. */ private void forceDecreaseKeyToMinimum(Node n) { Node y = n.parent; if (y != null) { cut(n, y); root.mark = false; cascadingRankChange(y); root = link(root, n); } } @SuppressWarnings("unchecked") private AddressableHeap.Handle comparableDeleteMin() { if (size == 0) { throw new NoSuchElementException(); } Node z = root; Node x = root.child; // clear fields of previous root z.child = null; z.next = null; z.prev = null; // simple case, no children if (x == null) { root = null; size = 0; return z; } // iterate over all children of root int maxDegree = -1; while (x != null) { Node nextX = (x.next == x) ? null : x.next; // clear parent x.parent = null; // remove from child list x.prev.next = x.next; x.next.prev = x.prev; x.next = x; x.prev = x; int d = x.rank; while (true) { Node y = aux[d]; if (y == null) { break; } // make sure x's key is smaller if (((Comparable) y.key).compareTo(x.key) < 0) { Node tmp = x; x = y; y = tmp; } // make y a child of x link(y, x); // make link fair by increasing rank x.rank++; aux[d] = null; d++; } // store result aux[d] = x; // keep track of max degree if (d > maxDegree) { maxDegree = d; } // advance x = nextX; } // recreate tree int i = 0; while (i <= maxDegree && aux[i] == null) { i++; } root = aux[i]; aux[i] = null; i++; while (i <= maxDegree) { Node n = aux[i]; if (n != null) { if (((Comparable) n.key).compareTo(root.key) < 0) { root = link(root, n); } else { link(n, root); } aux[i] = null; } i++; } // decrease size size--; return z; } private AddressableHeap.Handle comparatorDeleteMin() { if (size == 0) { throw new NoSuchElementException(); } Node z = root; Node x = root.child; // clear fields of previous root z.child = null; z.next = null; z.prev = null; // simple case, no children if (x == null) { root = null; size = 0; return z; } // iterate over all children of root int maxDegree = -1; while (x != null) { Node nextX = (x.next == x) ? null : x.next; // clear parent x.parent = null; // remove from child list x.prev.next = x.next; x.next.prev = x.prev; x.next = x; x.prev = x; int d = x.rank; while (true) { Node y = aux[d]; if (y == null) { break; } // make sure x's key is smaller if (comparator.compare(y.key, x.key) < 0) { Node tmp = x; x = y; y = tmp; } // make y a child of x link(y, x); // make link fair by increasing rank x.rank++; aux[d] = null; d++; } // store result aux[d] = x; // keep track of max degree if (d > maxDegree) { maxDegree = d; } // advance x = nextX; } // recreate tree int i = 0; while (i <= maxDegree && aux[i] == null) { i++; } root = aux[i]; aux[i] = null; i++; while (i <= maxDegree) { Node n = aux[i]; if (n != null) { if (comparator.compare(n.key, root.key) < 0) { root = link(root, n); } else { link(n, root); } aux[i] = null; } i++; } // decrease size size--; return z; } /* * (unfair) Link y as a child of x. */ private Node link(Node y, Node x) { y.parent = x; Node child = x.child; if (child == null) { x.child = y; y.next = y; y.prev = y; } else { y.prev = child; y.next = child.next; child.next = y; y.next.prev = y; } return x; } /* * Cut the link between x and its parent y. */ private void cut(Node x, Node y) { // advance y child y.child = x.next; if (y.child == x) { y.child = null; } // remove x from child list of y x.prev.next = x.next; x.next.prev = x.prev; x.next = x; x.prev = x; x.parent = null; // clear mark x.mark = false; } /* * Cascading rank change. Assumes that the root is unmarked. */ private void cascadingRankChange(Node y) { while (y.mark == true) { y.mark = false; if (y.rank > 0) { --y.rank; } y = y.parent; } y.mark = true; if (y.rank > 0) { --y.rank; } } } jheaps-jheaps-0.14/src/main/java/org/jheaps/tree/SkewHeap.java000066400000000000000000000473441367505655000242160ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.tree; import java.io.Serializable; import java.util.Comparator; import java.util.NoSuchElementException; import org.jheaps.AddressableHeap; import org.jheaps.MergeableAddressableHeap; import org.jheaps.annotations.ConstantTime; import org.jheaps.annotations.LogarithmicTime; /** * Skew heaps. The heap is sorted according to the {@linkplain Comparable * natural ordering} of its keys, or by a {@link Comparator} provided at heap * creation time, depending on which constructor is used. * *

* Operations {@code insert}, {@code deleteMin}, and {@code delete} take * amortized O(log(n)). Operation {@code findMin} is worst-case O(1). Note that * a skew-heap does not efficiently support the operation {@code decreaseKey} * which is amortized Ω(log(n)). * *

* Note that the ordering maintained by this heap, like any heap, and whether or * not an explicit comparator is provided, must be consistent with * {@code equals} if this heap is to correctly implement the {@code Heap} * interface. (See {@code Comparable} or {@code Comparator} for a precise * definition of consistent with equals.) This is so because the * {@code Heap} interface is defined in terms of the {@code equals} operation, * but this heap performs all key comparisons using its {@code compareTo} (or * {@code compare}) method, so two keys that are deemed equal by this method * are, from the standpoint of this heap, equal. The behavior of a heap * is well-defined even if its ordering is inconsistent with * {@code equals}; it just fails to obey the general contract of the * {@code Heap} interface. * *

* Note that this implementation is not synchronized. If * multiple threads access a heap concurrently, and at least one of the threads * modifies the heap structurally, it must be synchronized externally. * (A structural modification is any operation that adds or deletes one or more * elements or changing the key of some element.) This is typically accomplished * by synchronizing on some object that naturally encapsulates the heap. * * @param * the type of keys maintained by this heap * @param * the type of values maintained by this heap * * @author Dimitrios Michail */ public class SkewHeap implements MergeableAddressableHeap, Serializable { private final static long serialVersionUID = 1; /** * The comparator used to maintain order in this heap, or null if it uses * the natural ordering of its keys. * * @serial */ protected final Comparator comparator; /** * Size of the heap */ protected long size; /** * Root node of the heap */ protected Node root; /** * Used to reference the current heap or some other heap in case of melding, * so that handles remain valid even after a meld, without having to iterate * over them. * * In order to avoid maintaining a full-fledged union-find data structure, * we disallow a heap to be used in melding more than once. We use however, * path-compression in case of cascading melds, that it, a handle moves from * one heap to another and then another. */ protected SkewHeap other; /** * Constructs a new, empty heap, using the natural ordering of its keys. * *

* All keys inserted into the heap must implement the {@link Comparable} * interface. Furthermore, all such keys must be mutually * comparable: {@code k1.compareTo(k2)} must not throw a * {@code ClassCastException} for any keys {@code k1} and {@code k2} in the * heap. If the user attempts to put a key into the heap that violates this * constraint (for example, the user attempts to put a string key into a * heap whose keys are integers), the {@code insert(Object key)} call will * throw a {@code ClassCastException}. */ public SkewHeap() { this(null); } /** * Constructs a new, empty heap, ordered according to the given comparator. * *

* All keys inserted into the heap must be mutually comparable by * the given comparator: {@code comparator.compare(k1, * k2)} must not throw a {@code ClassCastException} for any keys {@code k1} * and {@code k2} in the heap. If the user attempts to put a key into the * heap that violates this constraint, the {@code insert(Object key)} call * will throw a {@code ClassCastException}. * * @param comparator * the comparator that will be used to order this heap. If * {@code null}, the {@linkplain Comparable natural ordering} of * the keys will be used. */ public SkewHeap(Comparator comparator) { this.comparator = comparator; this.size = 0; this.root = null; this.other = this; } /** * {@inheritDoc} */ @Override @LogarithmicTime(amortized = true) public AddressableHeap.Handle insert(K key) { return insert(key, null); } /** * {@inheritDoc} */ @Override @LogarithmicTime(amortized = true) @SuppressWarnings("unchecked") public AddressableHeap.Handle insert(K key, V value) { if (other != this) { throw new IllegalStateException("A heap cannot be used after a meld"); } if (key == null) { throw new NullPointerException("Null keys not permitted"); } Node n = createNode(key, value); // easy special cases if (size == 0) { root = n; size = 1; return n; } else if (size == 1) { int c; if (comparator == null) { c = ((Comparable) key).compareTo(root.key); } else { c = comparator.compare(key, root.key); } if (c <= 0) { n.o_c = root; root.y_s = n; root = n; } else { root.o_c = n; n.y_s = root; } size = 2; return n; } if (comparator == null) { root = union(root, n); } else { root = unionWithComparator(root, n); } size++; return n; } /** * {@inheritDoc} */ @Override @ConstantTime public AddressableHeap.Handle findMin() { if (size == 0) { throw new NoSuchElementException(); } return root; } /** * {@inheritDoc} */ @Override @LogarithmicTime(amortized = true) public Handle deleteMin() { if (size == 0) { throw new NoSuchElementException(); } Node oldRoot = root; // easy special cases if (size == 1) { root = null; size = 0; return oldRoot; } else if (size == 2) { root = root.o_c; root.o_c = null; root.y_s = null; size = 1; oldRoot.o_c = null; return oldRoot; } root = unlinkAndUnionChildren(root); size--; return oldRoot; } /** * {@inheritDoc} */ @Override @ConstantTime public boolean isEmpty() { return size == 0; } /** * {@inheritDoc} */ @Override @ConstantTime public long size() { return size; } /** * {@inheritDoc} */ @Override public Comparator comparator() { return comparator; } /** * {@inheritDoc} */ @Override @ConstantTime public void clear() { root = null; size = 0; } @Override public void meld(MergeableAddressableHeap other) { SkewHeap h = (SkewHeap) other; // check same comparator if (comparator != null) { if (h.comparator == null || !h.comparator.equals(comparator)) { throw new IllegalArgumentException("Cannot meld heaps using different comparators!"); } } else if (h.comparator != null) { throw new IllegalArgumentException("Cannot meld heaps using different comparators!"); } if (h.other != h) { throw new IllegalStateException("A heap cannot be used after a meld."); } // perform the meld size += h.size; if (comparator == null) { root = union(root, h.root); } else { root = unionWithComparator(root, h.root); } // clear other h.size = 0; h.root = null; // take ownership h.other = this; } // ~----------------------------------------------------------------------------- static class Node implements AddressableHeap.Handle, Serializable { private final static long serialVersionUID = 1; /* * We maintain explicitly the belonging heap, instead of using an inner * class due to possible cascading melding. */ SkewHeap heap; K key; V value; Node o_c; // older child Node y_s; // younger sibling or parent Node(SkewHeap heap, K key, V value) { this.heap = heap; this.key = key; this.value = value; this.o_c = null; this.y_s = null; } @Override public K getKey() { return key; } @Override public V getValue() { return value; } @Override public void setValue(V value) { this.value = value; } @Override public void decreaseKey(K newKey) { getOwner().decreaseKey(this, newKey); } @Override public void delete() { getOwner().delete(this); } /* * Get the owner heap of the handle. This is union-find with * path-compression between heaps. */ SkewHeap getOwner() { if (heap.other != heap) { // find root SkewHeap root = heap; while (root != root.other) { root = root.other; } // path-compression SkewHeap cur = heap; while (cur.other != root) { SkewHeap next = cur.other; cur.other = root; cur = next; } heap = root; } return heap; } } @SuppressWarnings("unchecked") private void decreaseKey(Node n, K newKey) { int c; if (comparator == null) { c = ((Comparable) newKey).compareTo(n.key); } else { c = comparator.compare(newKey, n.key); } if (c > 0) { throw new IllegalArgumentException("Keys can only be decreased!"); } if (c == 0 || root == n) { n.key = newKey; return; } /* * Delete and reinsert */ delete(n); n.key = newKey; if (comparator == null) { root = union(root, n); } else { root = unionWithComparator(root, n); } size++; } /** * Create a new node. * * @param key * the key * @param value * the value * @return the newly created node */ protected Node createNode(K key, V value) { return new Node(this, key, value); } /** * Delete a node from the heap. * * @param n * the node */ protected void delete(Node n) { if (n == root) { deleteMin(); return; } if (n.y_s == null) { throw new IllegalArgumentException("Invalid handle!"); } // disconnect and union children of node Node childTree = unlinkAndUnionChildren(n); // find parent Node p = getParent(n); // link children tree in place of node if (childTree == null) { // no children, just unlink from parent if (p.o_c == n) { if (n.y_s == p) { p.o_c = null; } else { p.o_c = n.y_s; } } else { p.o_c.y_s = p; } } else { // link children tree to parent if (p.o_c == n) { childTree.y_s = n.y_s; p.o_c = childTree; } else { p.o_c.y_s = childTree; childTree.y_s = p; } } size--; n.o_c = null; n.y_s = null; } /** * Unlink the two children of a node and union them forming a new tree. * * @param n * the node * @return the tree which is formed by the two children subtrees of the node */ protected Node unlinkAndUnionChildren(Node n) { // disconnect children Node child1 = n.o_c; if (child1 == null) { return null; } n.o_c = null; Node child2 = child1.y_s; if (child2 == n) { child2 = null; } else { child2.y_s = null; } child1.y_s = null; if (comparator == null) { return union(child1, child2); } else { return unionWithComparator(child1, child2); } } /** * Get the parent node of a given node. * * @param n * the node * @return the parent of a node */ protected Node getParent(Node n) { if (n.y_s == null) { return null; } Node c = n.y_s; if (c.o_c == n) { return c; } Node p1 = c.y_s; if (p1 != null && p1.o_c == n) { return p1; } return c; } /** * Unlink the right child of a node. * * @param n * the node * @return the right child after unlinking */ protected Node unlinkRightChild(Node n) { Node left = n.o_c; if (left == null || left.y_s == n) { return null; } Node right = left.y_s; left.y_s = n; right.y_s = null; return right; } /** * Top-down union of two skew heaps. * * @param root1 * the root of the first heap * @param root2 * the root of the right heap * @return the new root of the merged heap */ @SuppressWarnings("unchecked") protected Node union(Node root1, Node root2) { if (root1 == null) { return root2; } else if (root2 == null) { return root1; } Node newRoot; Node cur; // find initial int c = ((Comparable) root1.key).compareTo(root2.key); if (c <= 0) { newRoot = root1; root1 = unlinkRightChild(root1); } else { newRoot = root2; root2 = unlinkRightChild(root2); } cur = newRoot; // merge while (root1 != null && root2 != null) { c = ((Comparable) root1.key).compareTo(root2.key); if (c <= 0) { // link as left child of cur if (cur.o_c == null) { root1.y_s = cur; } else { root1.y_s = cur.o_c; } cur.o_c = root1; cur = root1; root1 = unlinkRightChild(root1); } else { // link as left child of cur if (cur.o_c == null) { root2.y_s = cur; } else { root2.y_s = cur.o_c; } cur.o_c = root2; cur = root2; root2 = unlinkRightChild(root2); } } while (root1 != null) { // link as left child of cur if (cur.o_c == null) { root1.y_s = cur; } else { root1.y_s = cur.o_c; } cur.o_c = root1; cur = root1; root1 = unlinkRightChild(root1); } while (root2 != null) { // link as left child of cur if (cur.o_c == null) { root2.y_s = cur; } else { root2.y_s = cur.o_c; } cur.o_c = root2; cur = root2; root2 = unlinkRightChild(root2); } return newRoot; } /** * Top-down union of two skew heaps with comparator. * * @param root1 * the root of the first heap * @param root2 * the root of the right heap * @return the new root of the merged heap */ protected Node unionWithComparator(Node root1, Node root2) { if (root1 == null) { return root2; } else if (root2 == null) { return root1; } Node newRoot; Node cur; // find initial int c = comparator.compare(root1.key, root2.key); if (c <= 0) { newRoot = root1; root1 = unlinkRightChild(root1); } else { newRoot = root2; root2 = unlinkRightChild(root2); } cur = newRoot; // merge while (root1 != null && root2 != null) { c = comparator.compare(root1.key, root2.key); if (c <= 0) { // link as left child of cur if (cur.o_c == null) { root1.y_s = cur; } else { root1.y_s = cur.o_c; } cur.o_c = root1; cur = root1; root1 = unlinkRightChild(root1); } else { // link as left child of cur if (cur.o_c == null) { root2.y_s = cur; } else { root2.y_s = cur.o_c; } cur.o_c = root2; cur = root2; root2 = unlinkRightChild(root2); } } while (root1 != null) { // link as left child of cur if (cur.o_c == null) { root1.y_s = cur; } else { root1.y_s = cur.o_c; } cur.o_c = root1; cur = root1; root1 = unlinkRightChild(root1); } while (root2 != null) { // link as left child of cur if (cur.o_c == null) { root2.y_s = cur; } else { root2.y_s = cur.o_c; } cur.o_c = root2; cur = root2; root2 = unlinkRightChild(root2); } return newRoot; } } jheaps-jheaps-0.14/src/main/java/org/jheaps/tree/package-info.java000066400000000000000000000013341367505655000250200ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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. */ /** * Heaps using an explicit "pointer-based" tree representation */ package org.jheaps.tree; jheaps-jheaps-0.14/src/test/000077500000000000000000000000001367505655000157425ustar00rootroot00000000000000jheaps-jheaps-0.14/src/test/java/000077500000000000000000000000001367505655000166635ustar00rootroot00000000000000jheaps-jheaps-0.14/src/test/java/org/000077500000000000000000000000001367505655000174525ustar00rootroot00000000000000jheaps-jheaps-0.14/src/test/java/org/jheaps/000077500000000000000000000000001367505655000207245ustar00rootroot00000000000000jheaps-jheaps-0.14/src/test/java/org/jheaps/array/000077500000000000000000000000001367505655000220425ustar00rootroot00000000000000jheaps-jheaps-0.14/src/test/java/org/jheaps/array/BinaryArrayAddressableHeapTest.java000066400000000000000000000065721367505655000307320ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.array; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.util.Comparator; import java.util.HashSet; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.Set; import org.jheaps.AddressableHeap; import org.jheaps.AddressableHeap.Handle; import org.jheaps.tree.AbstractAddressableHeapTest; import org.junit.Test; public class BinaryArrayAddressableHeapTest extends AbstractAddressableHeapTest { @Override protected AddressableHeap createHeap() { return new BinaryArrayAddressableHeap(); } @Override protected AddressableHeap createHeap(Comparator comparator) { return new BinaryArrayAddressableHeap(comparator); } @Override protected AddressableHeap createHeapWithStringValues() { return new BinaryArrayAddressableHeap(); } @Test(expected = IllegalArgumentException.class) public void testIllegal1() { new BinaryArrayAddressableHeap(-5); } @Test(expected = IllegalArgumentException.class) public void testIllegal2() { new BinaryArrayAddressableHeap(Integer.MAX_VALUE); } @Test public void testHandlesIterator() { Integer[] a = new Integer[SIZE]; for (int i = 0; i < SIZE; i++) { a[i] = SIZE - i; } BinaryArrayAddressableHeap heap = BinaryArrayAddressableHeap.heapify(a, null); Iterator> it = heap.handlesIterator(); Set found = new HashSet(); while (it.hasNext()) { Handle handle = it.next(); assertTrue(found.add(handle.getKey())); } assertEquals(SIZE, found.size()); } @Test(expected = NoSuchElementException.class) public void testHandlesIteratorTooMuch() { Integer[] a = new Integer[SIZE]; for (int i = 0; i < SIZE; i++) { a[i] = SIZE-i; } BinaryArrayAddressableHeap heap = BinaryArrayAddressableHeap.heapify(a, null); Iterator> it = heap.handlesIterator(); while(it.hasNext()) { it.next(); } it.next(); } @Test(expected = UnsupportedOperationException.class) public void testNoRemove() { Integer[] a = new Integer[SIZE]; for (int i = 0; i < SIZE; i++) { a[i] = SIZE-i; } BinaryArrayAddressableHeap heap = BinaryArrayAddressableHeap.heapify(a, null); Iterator> it = heap.handlesIterator(); it.remove(); } } jheaps-jheaps-0.14/src/test/java/org/jheaps/array/BinaryArrayBulkInsertWeakHeapTest.java000066400000000000000000000031771367505655000314110ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.array; import java.util.Comparator; import org.jheaps.Heap; import org.jheaps.tree.AbstractComparatorLongHeapTest; import org.junit.Test; public class BinaryArrayBulkInsertWeakHeapTest extends AbstractComparatorLongHeapTest { protected Heap createHeap() { return new BinaryArrayBulkInsertWeakHeap(); } protected Heap createHeap(int capacity) { return new BinaryArrayBulkInsertWeakHeap(capacity); } @Override protected Heap createHeap(Comparator comparator) { return new BinaryArrayBulkInsertWeakHeap(comparator); } @Test(expected = IllegalArgumentException.class) public void testIllegalSize() { Heap h = createHeap(-4); h.insert(1L); } @Test(expected = IllegalArgumentException.class) public void testIllegalSize1() { Heap h = createHeap(-1); h.insert(1L); } @Test(expected = IllegalArgumentException.class) public void testIllegalSize2() { Heap h = createHeap(Integer.MAX_VALUE - 8); h.insert(1L); } } jheaps-jheaps-0.14/src/test/java/org/jheaps/array/BinaryArrayHeapTest.java000066400000000000000000000031071367505655000265670ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.array; import java.util.Comparator; import org.jheaps.Heap; import org.jheaps.tree.AbstractComparatorLongHeapTest; import org.junit.Test; public class BinaryArrayHeapTest extends AbstractComparatorLongHeapTest { protected Heap createHeap() { return new BinaryArrayHeap(); } protected Heap createHeap(int capacity) { return new BinaryArrayHeap(capacity); } @Override protected Heap createHeap(Comparator comparator) { return new BinaryArrayHeap(comparator); } @Test(expected = IllegalArgumentException.class) public void testIllegalSize() { Heap h = createHeap(-4); h.insert(1L); } @Test(expected = IllegalArgumentException.class) public void testIllegalSize1() { Heap h = createHeap(-1); h.insert(1L); } @Test(expected = IllegalArgumentException.class) public void testIllegalSize2() { Heap h = createHeap(Integer.MAX_VALUE - 8); h.insert(1L); } } jheaps-jheaps-0.14/src/test/java/org/jheaps/array/BinaryArrayIntegerValueHeapTest.java000077500000000000000000000024321367505655000311050ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.array; import java.util.NoSuchElementException; import org.jheaps.ValueHeap; import org.jheaps.tree.AbstractIntegerHeapTest; import org.junit.Test; public class BinaryArrayIntegerValueHeapTest extends AbstractIntegerHeapTest { @Override protected ValueHeap createHeap() { return new BinaryArrayIntegerValueHeap(1); } @Override protected ValueHeap createHeap(int capacity) { return new BinaryArrayIntegerValueHeap(capacity); } @Test(expected = NoSuchElementException.class) public void testBadFindMinValue() { ValueHeap h = createHeap(1); h.findMinValue(); } } jheaps-jheaps-0.14/src/test/java/org/jheaps/array/BinaryArrayWeakHeapTest.java000066400000000000000000000031271367505655000274010ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.array; import java.util.Comparator; import org.jheaps.Heap; import org.jheaps.tree.AbstractComparatorLongHeapTest; import org.junit.Test; public class BinaryArrayWeakHeapTest extends AbstractComparatorLongHeapTest { protected Heap createHeap() { return new BinaryArrayWeakHeap(); } protected Heap createHeap(int capacity) { return new BinaryArrayWeakHeap(capacity); } @Override protected Heap createHeap(Comparator comparator) { return new BinaryArrayWeakHeap(comparator); } @Test(expected = IllegalArgumentException.class) public void testIllegalSize() { Heap h = createHeap(-4); h.insert(1L); } @Test(expected = IllegalArgumentException.class) public void testIllegalSize1() { Heap h = createHeap(-1); h.insert(1L); } @Test(expected = IllegalArgumentException.class) public void testIllegalSize2() { Heap h = createHeap(Integer.MAX_VALUE - 8); h.insert(1L); } } jheaps-jheaps-0.14/src/test/java/org/jheaps/array/D3DaryArrayAddressableHeapTest.java000066400000000000000000000066301367505655000305670ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.array; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.util.Comparator; import java.util.HashSet; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.Set; import org.jheaps.AddressableHeap; import org.jheaps.AddressableHeap.Handle; import org.jheaps.array.DaryArrayAddressableHeap; import org.jheaps.tree.AbstractAddressableHeapTest; import org.junit.Test; public class D3DaryArrayAddressableHeapTest extends AbstractAddressableHeapTest { @Override protected AddressableHeap createHeap() { return new DaryArrayAddressableHeap(3); } @Override protected AddressableHeap createHeap(Comparator comparator) { return new DaryArrayAddressableHeap(3, comparator); } @Override protected AddressableHeap createHeapWithStringValues() { return new DaryArrayAddressableHeap(3); } @Test(expected = IllegalArgumentException.class) public void testWrongD() { new DaryArrayAddressableHeap(1); } @Test(expected = IllegalArgumentException.class) public void testIllegal1() { new DaryArrayAddressableHeap(3, -5); } @Test(expected = IllegalArgumentException.class) public void testIllegal2() { new DaryArrayAddressableHeap(3, Integer.MAX_VALUE); } @Test public void testHandlesIterator() { Integer[] a = new Integer[SIZE]; for (int i = 0; i < SIZE; i++) { a[i] = SIZE-i; } DaryArrayAddressableHeap heap = DaryArrayAddressableHeap.heapify(3, a, null); Iterator> it = heap.handlesIterator(); Set found = new HashSet(); while(it.hasNext()) { Handle handle = it.next(); assertTrue(found.add(handle.getKey())); } assertEquals(SIZE, found.size()); } @Test(expected = NoSuchElementException.class) public void testHandlesIteratorTooMuch() { Integer[] a = new Integer[SIZE]; for (int i = 0; i < SIZE; i++) { a[i] = SIZE-i; } DaryArrayAddressableHeap heap = DaryArrayAddressableHeap.heapify(3, a, null); Iterator> it = heap.handlesIterator(); while(it.hasNext()) { it.next(); } it.next(); } @Test(expected = UnsupportedOperationException.class) public void testNoRemove() { Integer[] a = new Integer[SIZE]; for (int i = 0; i < SIZE; i++) { a[i] = SIZE-i; } DaryArrayAddressableHeap heap = DaryArrayAddressableHeap.heapify(3, a, null); Iterator> it = heap.handlesIterator(); it.remove(); } } jheaps-jheaps-0.14/src/test/java/org/jheaps/array/D3DaryArrayHeapTest.java000066400000000000000000000032701367505655000264320ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.array; import java.util.Comparator; import org.jheaps.Heap; import org.jheaps.tree.AbstractComparatorLongHeapTest; import org.junit.Test; public class D3DaryArrayHeapTest extends AbstractComparatorLongHeapTest { protected Heap createHeap() { return new DaryArrayHeap(3); } protected Heap createHeap(int capacity) { return new DaryArrayHeap(3, capacity); } @Override protected Heap createHeap(Comparator comparator) { return new DaryArrayHeap(3, comparator); } @Test(expected = IllegalArgumentException.class) public void testWrongD() { new DaryArrayHeap(1); } @Test(expected = IllegalArgumentException.class) public void testIllegalSize() { Heap h = createHeap(-4); h.insert(1L); } @Test(expected = IllegalArgumentException.class) public void testIllegalSize1() { Heap h = createHeap(-1); h.insert(1L); } @Test(expected = IllegalArgumentException.class) public void testIllegalSize2() { Heap h = createHeap(Integer.MAX_VALUE - 8); h.insert(1L); } } jheaps-jheaps-0.14/src/test/java/org/jheaps/array/D4DaryArrayAddressableHeapTest.java000066400000000000000000000045571367505655000305760ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.array; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.util.Comparator; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import org.jheaps.AddressableHeap; import org.jheaps.AddressableHeap.Handle; import org.jheaps.array.DaryArrayAddressableHeap; import org.jheaps.tree.AbstractAddressableHeapTest; import org.junit.Test; public class D4DaryArrayAddressableHeapTest extends AbstractAddressableHeapTest { @Override protected AddressableHeap createHeap() { return new DaryArrayAddressableHeap(4); } @Override protected AddressableHeap createHeap(Comparator comparator) { return new DaryArrayAddressableHeap(4, comparator); } @Override protected AddressableHeap createHeapWithStringValues() { return new DaryArrayAddressableHeap(4); } @Test(expected = IllegalArgumentException.class) public void testIllegal1() { new DaryArrayAddressableHeap(4, -5); } @Test(expected = IllegalArgumentException.class) public void testIllegal2() { new DaryArrayAddressableHeap(4, Integer.MAX_VALUE); } @Test public void testHandlesIterator() { Integer[] a = new Integer[SIZE]; for (int i = 0; i < SIZE; i++) { a[i] = SIZE-i; } DaryArrayAddressableHeap heap = DaryArrayAddressableHeap.heapify(4, a, null); Iterator> it = heap.handlesIterator(); Set found = new HashSet(); while(it.hasNext()) { Handle handle = it.next(); assertTrue(found.add(handle.getKey())); } assertEquals(SIZE, found.size()); } } jheaps-jheaps-0.14/src/test/java/org/jheaps/array/D4DaryArrayHeapTest.java000066400000000000000000000031211367505655000264260ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.array; import java.util.Comparator; import org.jheaps.Heap; import org.jheaps.array.DaryArrayHeap; import org.jheaps.tree.AbstractLongHeapTest; import org.junit.Test; public class D4DaryArrayHeapTest extends AbstractLongHeapTest { protected Heap createHeap() { return new DaryArrayHeap(4); } protected Heap createHeap(Comparator comparator) { return new DaryArrayHeap(4, comparator); } protected Heap createHeap(int capacity) { return new DaryArrayHeap(4, capacity); } @Test(expected = IllegalArgumentException.class) public void testIllegalSize() { Heap h = createHeap(-4); h.insert(1L); } @Test(expected = IllegalArgumentException.class) public void testIllegalSize1() { Heap h = createHeap(-1); h.insert(1L); } @Test(expected = IllegalArgumentException.class) public void testIllegalSize2() { Heap h = createHeap(Integer.MAX_VALUE - 8); h.insert(1L); } } jheaps-jheaps-0.14/src/test/java/org/jheaps/array/D5DaryArrayHeapTest.java000066400000000000000000000031201367505655000264260ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.array; import java.util.Comparator; import org.jheaps.Heap; import org.jheaps.array.DaryArrayHeap; import org.jheaps.tree.AbstractLongHeapTest; import org.junit.Test; public class D5DaryArrayHeapTest extends AbstractLongHeapTest { protected Heap createHeap() { return new DaryArrayHeap(5); } protected Heap createHeap(Comparator comparator) { return new DaryArrayHeap(5, comparator); } protected Heap createHeap(int capacity) { return new DaryArrayHeap(5, capacity); } @Test(expected = IllegalArgumentException.class) public void testIllegalSize() { Heap h = createHeap(-4); h.insert(1L); } @Test(expected = IllegalArgumentException.class) public void testIllegalSize1() { Heap h = createHeap(-1); h.insert(1L); } @Test(expected = IllegalArgumentException.class) public void testIllegalSize2() { Heap h = createHeap(Integer.MAX_VALUE - 8); h.insert(1L); } } jheaps-jheaps-0.14/src/test/java/org/jheaps/array/HeapifyTest.java000066400000000000000000000442151367505655000251400ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.array; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.lang.reflect.Array; import java.util.Comparator; import java.util.Random; import org.jheaps.AddressableHeap; import org.jheaps.Heap; import org.junit.BeforeClass; import org.junit.Test; public class HeapifyTest { private static final int SIZE = 100000; private static Comparator comparator; @BeforeClass public static void setUpClass() { comparator = new Comparator() { @Override public int compare(Integer o1, Integer o2) { if (o1 < o2) { return 1; } else if (o1 > o2) { return -1; } else { return 0; } } }; } @Test public void testHeapifySort() { Random generator = new Random(1); final int classes = 8; Integer[] a = new Integer[SIZE]; for (int i = 0; i < SIZE; i++) { a[i] = generator.nextInt(); } @SuppressWarnings("unchecked") Heap[] h = (Heap[]) Array.newInstance(Heap.class, classes); h[0] = BinaryArrayHeap.heapify(a); h[1] = DaryArrayHeap.heapify(2, a); h[2] = DaryArrayHeap.heapify(3, a); h[3] = DaryArrayHeap.heapify(4, a); h[4] = DaryArrayHeap.heapify(5, a); h[5] = BinaryArrayWeakHeap.heapify(a); h[6] = BinaryArrayBulkInsertWeakHeap.heapify(a); h[7] = MinMaxBinaryArrayDoubleEndedHeap.heapify(a); int elements = SIZE; Integer prev = null, cur; while (elements > 0) { cur = h[0].findMin(); for (int i = 1; i < classes; i++) { assertEquals(cur.intValue(), h[i].findMin().intValue()); } for (int i = 0; i < classes; i++) { h[i].deleteMin(); } if (prev != null) { assertTrue(prev.compareTo(cur) <= 0); } prev = cur; elements--; } } @Test public void testHeapifySortWithComparator() { Random generator = new Random(1); final int classes = 8; Integer[] a = new Integer[SIZE]; for (int i = 0; i < SIZE; i++) { a[i] = generator.nextInt(); } @SuppressWarnings("unchecked") Heap[] h = (Heap[]) Array.newInstance(Heap.class, classes); h[0] = BinaryArrayHeap.heapify(a, comparator); h[1] = DaryArrayHeap.heapify(2, a, comparator); h[2] = DaryArrayHeap.heapify(3, a, comparator); h[3] = DaryArrayHeap.heapify(4, a, comparator); h[4] = DaryArrayHeap.heapify(5, a, comparator); h[5] = BinaryArrayWeakHeap.heapify(a, comparator); h[6] = BinaryArrayBulkInsertWeakHeap.heapify(a, comparator); h[7] = MinMaxBinaryArrayDoubleEndedHeap.heapify(a, comparator); int elements = SIZE; Integer prev = null, cur; while (elements > 0) { cur = h[0].findMin(); for (int i = 1; i < classes; i++) { assertEquals(cur.intValue(), h[i].findMin().intValue()); } for (int i = 0; i < classes; i++) { h[i].deleteMin(); } if (prev != null) { assertTrue(comparator.compare(prev, cur) <= 0); } prev = cur; elements--; } } @Test @SuppressWarnings("unchecked") public void testHeapifyZeroLengthArray() { Integer[] a = new Integer[0]; final int nonfixed = 8; Heap[] h = (Heap[]) Array.newInstance(Heap.class, nonfixed); h[0] = BinaryArrayHeap.heapify(a); h[1] = DaryArrayHeap.heapify(2, a); h[2] = DaryArrayHeap.heapify(3, a); h[3] = DaryArrayHeap.heapify(4, a); h[4] = DaryArrayHeap.heapify(5, a); h[5] = BinaryArrayWeakHeap.heapify(a); h[6] = BinaryArrayBulkInsertWeakHeap.heapify(a); h[7] = MinMaxBinaryArrayDoubleEndedHeap.heapify(a); for (int i = 0; i < nonfixed; i++) { assertTrue(h[i].isEmpty()); try { h[i].insert(1); } catch (IllegalStateException e) { fail("No!"); } } } @Test @SuppressWarnings("unchecked") public void testHeapifyZeroLengthArrayComparator() { Integer[] a = new Integer[0]; final int nonfixed = 8; Heap[] h = (Heap[]) Array.newInstance(Heap.class, nonfixed); h[0] = BinaryArrayHeap.heapify(a, comparator); h[1] = DaryArrayHeap.heapify(2, a, comparator); h[2] = DaryArrayHeap.heapify(3, a, comparator); h[3] = DaryArrayHeap.heapify(4, a, comparator); h[4] = DaryArrayHeap.heapify(5, a, comparator); h[5] = BinaryArrayWeakHeap.heapify(a, comparator); h[6] = BinaryArrayBulkInsertWeakHeap.heapify(a, comparator); h[7] = MinMaxBinaryArrayDoubleEndedHeap.heapify(a, comparator); for (int i = 0; i < nonfixed; i++) { assertTrue(h[i].isEmpty()); try { h[i].insert(1); } catch (IllegalStateException e) { fail("No!"); } } } @Test public void testHeapifyZeroLengthArray1() { Integer[] a = new Integer[0]; final int classes = 4; @SuppressWarnings("unchecked") AddressableHeap[] h = (AddressableHeap[]) Array .newInstance(AddressableHeap.class, classes); h[0] = BinaryArrayAddressableHeap.heapify(a, null); h[1] = DaryArrayAddressableHeap.heapify(3, a, null); h[2] = DaryArrayAddressableHeap.heapify(4, a, null); h[3] = DaryArrayAddressableHeap.heapify(5, a, null); for (int i = 0; i < classes; i++) { assertTrue(h[i].isEmpty()); try { assertEquals(1, h[i].insert(1).getKey().intValue()); } catch (IllegalStateException e) { fail("No!"); } } } @Test public void testHeapifyZeroLengthArray2() { Integer[] a = new Integer[0]; final int classes = 4; @SuppressWarnings("unchecked") AddressableHeap[] h = (AddressableHeap[]) Array .newInstance(AddressableHeap.class, classes); h[0] = BinaryArrayAddressableHeap.heapify(a, null); h[1] = DaryArrayAddressableHeap.heapify(3, a, null); h[2] = DaryArrayAddressableHeap.heapify(4, a, null); h[3] = DaryArrayAddressableHeap.heapify(5, a, null); for (int i = 0; i < classes; i++) { h[i].insert(1); h[i].insert(2); h[i].insert(3); h[i].insert(4); assertEquals(4, h[i].size()); } } @Test public void testHeapifyZeroLengthArrayComparator1() { Integer[] a = new Integer[0]; final int classes = 4; @SuppressWarnings("unchecked") AddressableHeap[] h = (AddressableHeap[]) Array .newInstance(AddressableHeap.class, classes); h[0] = BinaryArrayAddressableHeap.heapify(a, null, comparator); h[1] = DaryArrayAddressableHeap.heapify(3, a, null, comparator); h[2] = DaryArrayAddressableHeap.heapify(4, a, null, comparator); h[3] = DaryArrayAddressableHeap.heapify(5, a, null, comparator); for (int i = 0; i < classes; i++) { assertTrue(h[i].isEmpty()); try { assertEquals(1, h[i].insert(1).getKey().intValue()); } catch (IllegalStateException e) { fail("No!"); } } } @Test public void testHeapifyBadParameters() { Integer[] a = new Integer[0]; try { BinaryArrayHeap.heapify(null); fail("No!"); } catch (IllegalArgumentException ignored) { } try { BinaryArrayHeap.heapify(null, comparator); fail("No!"); } catch (IllegalArgumentException ignored) { } try { DaryArrayHeap.heapify(1, a); fail("No!"); } catch (IllegalArgumentException ignored) { } try { DaryArrayHeap.heapify(1, a, comparator); fail("No!"); } catch (IllegalArgumentException ignored) { } try { DaryArrayHeap.heapify(2, null); fail("No!"); } catch (IllegalArgumentException ignored) { } try { DaryArrayHeap.heapify(2, null, comparator); fail("No!"); } catch (IllegalArgumentException ignored) { } try { BinaryArrayAddressableHeap.heapify(null, null); fail("No!"); } catch (IllegalArgumentException ignored) { } try { BinaryArrayAddressableHeap.heapify(null, null, comparator); fail("No!"); } catch (IllegalArgumentException ignored) { } try { BinaryArrayAddressableHeap.heapify(new Integer[2], new Integer[3]); fail("No!"); } catch (IllegalArgumentException ignored) { } try { BinaryArrayAddressableHeap.heapify(new Integer[2], new Integer[3], comparator); fail("No!"); } catch (IllegalArgumentException ignored) { } try { DaryArrayAddressableHeap.heapify(1, new Integer[2], new Integer[2]); fail("No!"); } catch (IllegalArgumentException ignored) { } try { DaryArrayAddressableHeap.heapify(3, null, new Integer[2]); fail("No!"); } catch (IllegalArgumentException ignored) { } try { DaryArrayAddressableHeap.heapify(3, new Integer[3], new Integer[2]); fail("No!"); } catch (IllegalArgumentException ignored) { } try { DaryArrayAddressableHeap.heapify(1, new Integer[2], new Integer[2], comparator); fail("No!"); } catch (IllegalArgumentException ignored) { } try { DaryArrayAddressableHeap.heapify(3, null, new Integer[2], comparator); fail("No!"); } catch (IllegalArgumentException ignored) { } try { DaryArrayAddressableHeap.heapify(3, new Integer[3], new Integer[2], comparator); fail("No!"); } catch (IllegalArgumentException ignored) { } try { BinaryArrayWeakHeap.heapify(null); fail("No!"); } catch (IllegalArgumentException ignored) { } try { BinaryArrayWeakHeap.heapify(null, comparator); fail("No!"); } catch (IllegalArgumentException ignored) { } try { BinaryArrayBulkInsertWeakHeap.heapify(null); fail("No!"); } catch (IllegalArgumentException ignored) { } try { BinaryArrayBulkInsertWeakHeap.heapify(null, comparator); fail("No!"); } catch (IllegalArgumentException ignored) { } try { MinMaxBinaryArrayDoubleEndedHeap.heapify(null); fail("No!"); } catch (IllegalArgumentException ignored) { } try { MinMaxBinaryArrayDoubleEndedHeap.heapify(null, comparator); fail("No!"); } catch (IllegalArgumentException ignored) { } } @Test public void testArrayAddressableHeapifySort() { Random generator = new Random(1); Integer[] a = new Integer[SIZE]; for (int i = 0; i < SIZE; i++) { a[i] = generator.nextInt(); } int classes = 4; @SuppressWarnings("unchecked") AddressableHeap[] h = (AddressableHeap[]) Array .newInstance(AddressableHeap.class, classes); h[0] = BinaryArrayAddressableHeap.heapify(a, null); h[1] = DaryArrayAddressableHeap.heapify(3, a, null); h[2] = DaryArrayAddressableHeap.heapify(4, a, null); h[3] = DaryArrayAddressableHeap.heapify(5, a, null); int elements = SIZE; Integer[] prev = new Integer[classes]; Integer[] cur = new Integer[classes]; while (elements > 0) { for (int i = 0; i < classes; i++) { cur[i] = h[i].findMin().getKey(); if (i > 0) { assertEquals(cur[0], cur[i]); } } for (int i = 0; i < classes; i++) { h[i].deleteMin(); } for (int i = 0; i < classes; i++) { if (prev[i] != null) { assertTrue(prev[i].compareTo(cur[i]) <= 0); } prev[i] = cur[i]; } elements--; } } @Test public void testArrayAddressableHeapifySortWithValues() { Random generator = new Random(1); Integer[] a = new Integer[SIZE]; String[] b = new String[SIZE]; for (int i = 0; i < SIZE; i++) { a[i] = generator.nextInt(); b[i] = a[i].toString(); } int classes = 4; @SuppressWarnings("unchecked") AddressableHeap[] h = (AddressableHeap[]) Array .newInstance(AddressableHeap.class, classes); h[0] = BinaryArrayAddressableHeap.heapify(a, b); h[1] = DaryArrayAddressableHeap.heapify(3, a, b); h[2] = DaryArrayAddressableHeap.heapify(4, a, b); h[3] = DaryArrayAddressableHeap.heapify(5, a, b); int elements = SIZE; Integer[] prev = new Integer[classes]; Integer[] cur = new Integer[classes]; while (elements > 0) { for (int i = 0; i < classes; i++) { cur[i] = h[i].findMin().getKey(); assertEquals(h[i].findMin().getValue(), h[i].findMin().getKey().toString()); if (i > 0) { assertEquals(cur[0], cur[i]); } } for (int i = 0; i < classes; i++) { h[i].deleteMin(); } for (int i = 0; i < classes; i++) { if (prev[i] != null) { assertTrue(prev[i].compareTo(cur[i]) <= 0); } prev[i] = cur[i]; } elements--; } } @Test public void testArrayAddressableHeapifySortComparator() { Random generator = new Random(1); Integer[] a = new Integer[SIZE]; for (int i = 0; i < SIZE; i++) { a[i] = generator.nextInt(); } int classes = 4; @SuppressWarnings("unchecked") AddressableHeap[] h = (AddressableHeap[]) Array .newInstance(AddressableHeap.class, classes); h[0] = BinaryArrayAddressableHeap.heapify(a, null, comparator); h[1] = DaryArrayAddressableHeap.heapify(3, a, null, comparator); h[2] = DaryArrayAddressableHeap.heapify(4, a, null, comparator); h[3] = DaryArrayAddressableHeap.heapify(5, a, null, comparator); int elements = SIZE; Integer[] prev = new Integer[classes]; Integer[] cur = new Integer[classes]; while (elements > 0) { for (int i = 0; i < classes; i++) { cur[i] = h[i].findMin().getKey(); if (i > 0) { assertEquals(cur[0], cur[i]); } } for (int i = 0; i < classes; i++) { h[i].deleteMin(); } for (int i = 0; i < classes; i++) { if (prev[i] != null) { assertTrue(comparator.compare(prev[i], cur[i]) <= 0); } prev[i] = cur[i]; } elements--; } } @Test public void testArrayAddressableHeapifySortComparatorWithValues() { Random generator = new Random(1); Integer[] a = new Integer[SIZE]; String[] b = new String[SIZE]; for (int i = 0; i < SIZE; i++) { a[i] = generator.nextInt(); b[i] = a[i].toString(); } int classes = 4; @SuppressWarnings("unchecked") AddressableHeap[] h = (AddressableHeap[]) Array .newInstance(AddressableHeap.class, classes); h[0] = BinaryArrayAddressableHeap.heapify(a, b, comparator); h[1] = DaryArrayAddressableHeap.heapify(3, a, b, comparator); h[2] = DaryArrayAddressableHeap.heapify(4, a, b, comparator); h[3] = DaryArrayAddressableHeap.heapify(5, a, b, comparator); int elements = SIZE; Integer[] prev = new Integer[classes]; Integer[] cur = new Integer[classes]; while (elements > 0) { for (int i = 0; i < classes; i++) { cur[i] = h[i].findMin().getKey(); assertEquals(h[i].findMin().getValue(), h[i].findMin().getKey().toString()); if (i > 0) { assertEquals(cur[0], cur[i]); } } for (int i = 0; i < classes; i++) { h[i].deleteMin(); } for (int i = 0; i < classes; i++) { if (prev[i] != null) { assertTrue(comparator.compare(prev[i], cur[i]) <= 0); } prev[i] = cur[i]; } elements--; } } } jheaps-jheaps-0.14/src/test/java/org/jheaps/array/MinMaxBinaryArrayDoubleEndedHeapTest.java000066400000000000000000000024241367505655000317750ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.array; import java.util.Comparator; import org.jheaps.DoubleEndedHeap; import org.jheaps.tree.AbstractDoubleEndedHeapTest; public class MinMaxBinaryArrayDoubleEndedHeapTest extends AbstractDoubleEndedHeapTest { protected DoubleEndedHeap createHeap() { return new MinMaxBinaryArrayDoubleEndedHeap(); } protected DoubleEndedHeap createHeap(int capacity) { return new MinMaxBinaryArrayDoubleEndedHeap(capacity); } @Override protected DoubleEndedHeap createHeap(Comparator comparator) { return new MinMaxBinaryArrayDoubleEndedHeap(comparator); } } jheaps-jheaps-0.14/src/test/java/org/jheaps/array/MinMaxBinaryArrayHeapTest.java000077500000000000000000000073701367505655000277120ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.array; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.util.Comparator; import org.jheaps.Heap; import org.jheaps.tree.AbstractComparatorLongHeapTest; import org.junit.Test; public class MinMaxBinaryArrayHeapTest extends AbstractComparatorLongHeapTest { protected Heap createHeap() { return new MinMaxBinaryArrayDoubleEndedHeap(); } protected Heap createHeap(int capacity) { return new MinMaxBinaryArrayDoubleEndedHeap(capacity); } @Override protected Heap createHeap(Comparator comparator) { return new MinMaxBinaryArrayDoubleEndedHeap(comparator); } @Test(expected = IllegalArgumentException.class) public void testIllegalSize() { Heap h = createHeap(-4); h.insert(1L); } @Test(expected = IllegalArgumentException.class) public void testIllegalSize1() { Heap h = createHeap(-1); h.insert(1L); } @Test(expected = IllegalArgumentException.class) public void testIllegalSize2() { Heap h = createHeap(Integer.MAX_VALUE - 8); h.insert(1L); } @Test public void testSimple() { MinMaxBinaryArrayDoubleEndedHeap h = new MinMaxBinaryArrayDoubleEndedHeap(); h.insert(1); assertEquals(1, h.findMin().intValue()); assertEquals(1, h.findMax().intValue()); h.insert(2); assertEquals(1, h.findMin().intValue()); assertEquals(2, h.findMax().intValue()); h.insert(3); assertEquals(1, h.findMin().intValue()); assertEquals(3, h.findMax().intValue()); h.insert(10); assertEquals(1, h.findMin().intValue()); assertEquals(10, h.findMax().intValue()); h.insert(11); assertEquals(1, h.findMin().intValue()); assertEquals(11, h.findMax().intValue()); h.insert(12); assertEquals(1, h.findMin().intValue()); assertEquals(12, h.findMax().intValue()); h.insert(5); assertEquals(1, h.findMin().intValue()); assertEquals(12, h.findMax().intValue()); h.insert(7); assertEquals(1, h.findMin().intValue()); assertEquals(12, h.findMax().intValue()); h.insert(15); assertEquals(1, h.findMin().intValue()); assertEquals(15, h.findMax().intValue()); h.insert(100); assertEquals(1, h.findMin().intValue()); assertEquals(100, h.findMax().intValue()); h.insert(200); assertEquals(1, h.findMin().intValue()); assertEquals(200, h.findMax().intValue()); assertEquals(1, h.deleteMin().intValue()); assertEquals(2, h.findMin().intValue()); assertEquals(200, h.findMax().intValue()); } @Test public void testonMinLevel() { MinMaxBinaryArrayDoubleEndedHeap h = new MinMaxBinaryArrayDoubleEndedHeap(); assertTrue(h.onMinLevel(1)); assertFalse(h.onMinLevel(2)); assertFalse(h.onMinLevel(3)); assertTrue(h.onMinLevel(4)); assertTrue(h.onMinLevel(5)); assertTrue(h.onMinLevel(6)); assertTrue(h.onMinLevel(7)); for (int i = 8; i < 16; i++) { assertFalse(h.onMinLevel(i)); } for (int i = 16; i < 32; i++) { assertTrue(h.onMinLevel(i)); } for (int i = 32; i < 64; i++) { assertFalse(h.onMinLevel(i)); } for (int i = 64; i < 128; i++) { assertTrue(h.onMinLevel(i)); } } } jheaps-jheaps-0.14/src/test/java/org/jheaps/dag/000077500000000000000000000000001367505655000214575ustar00rootroot00000000000000jheaps-jheaps-0.14/src/test/java/org/jheaps/dag/HollowHeapAddressableHeapTest.java000066400000000000000000000024351367505655000301600ustar00rootroot00000000000000/* * (C) Copyright 2014-2019, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.dag; import java.util.Comparator; import org.jheaps.AddressableHeap; import org.jheaps.tree.AbstractAddressableHeapTest; public class HollowHeapAddressableHeapTest extends AbstractAddressableHeapTest { @Override protected AddressableHeap createHeap() { return new HollowHeap(); } @Override protected AddressableHeap createHeap(Comparator comparator) { return new HollowHeap(comparator); } @Override protected AddressableHeap createHeapWithStringValues() { return new HollowHeap(); } } jheaps-jheaps-0.14/src/test/java/org/jheaps/dag/HollowHeapMergeableAddressableHeapTest.java000066400000000000000000000022711367505655000317620ustar00rootroot00000000000000/* * (C) Copyright 2014-2019, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.dag; import java.util.Comparator; import org.jheaps.MergeableAddressableHeap; import org.jheaps.tree.AbstractMergeableAddressableHeapTest; public class HollowHeapMergeableAddressableHeapTest extends AbstractMergeableAddressableHeapTest { protected MergeableAddressableHeap createHeap() { return new HollowHeap(); } @Override protected MergeableAddressableHeap createHeap(Comparator comparator) { return new HollowHeap(comparator); } } jheaps-jheaps-0.14/src/test/java/org/jheaps/monotone/000077500000000000000000000000001367505655000225625ustar00rootroot00000000000000jheaps-jheaps-0.14/src/test/java/org/jheaps/monotone/BigIntegerRadixAddressableHeapTest.java000066400000000000000000000357701367505655000322400ustar00rootroot00000000000000/* * (C) Copyright 2014-2018, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.monotone; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.math.BigInteger; import java.util.Arrays; import java.util.Random; import org.jheaps.AddressableHeap; import org.junit.Test; public class BigIntegerRadixAddressableHeapTest { private static final BigInteger SIZE = BigInteger.valueOf(100000); @Test public void testBug2() { AddressableHeap h = new BigIntegerRadixAddressableHeap(BigInteger.ZERO, BigInteger.valueOf(100)); h.insert(BigInteger.ZERO); assertEquals(BigInteger.ZERO, h.findMin().getKey()); assertEquals(BigInteger.ZERO, h.deleteMin().getKey()); h.insert(BigInteger.valueOf(15)); assertEquals(BigInteger.valueOf(15), h.findMin().getKey()); } @Test public void testVerySmall() { AddressableHeap h = new BigIntegerRadixAddressableHeap(BigInteger.valueOf(29), BigInteger.valueOf(36)); h.insert(BigInteger.valueOf(29)); h.insert(BigInteger.valueOf(30)); h.insert(BigInteger.valueOf(31)); h.insert(BigInteger.valueOf(30)); h.insert(BigInteger.valueOf(33)); h.insert(BigInteger.valueOf(36)); h.insert(BigInteger.valueOf(35)); assertEquals(h.size(), 7); assertEquals(h.findMin().getKey(), BigInteger.valueOf(29)); assertEquals(h.size(), 7); assertEquals(h.deleteMin().getKey(), BigInteger.valueOf(29)); assertEquals(h.size(), 6); assertEquals(h.findMin().getKey(), BigInteger.valueOf(30)); assertEquals(h.deleteMin().getKey(), BigInteger.valueOf(30)); assertEquals(h.findMin().getKey(), BigInteger.valueOf(30)); assertEquals(h.deleteMin().getKey(), BigInteger.valueOf(30)); assertEquals(h.findMin().getKey(), BigInteger.valueOf(31)); assertEquals(h.deleteMin().getKey(), BigInteger.valueOf(31)); assertEquals(h.findMin().getKey(), BigInteger.valueOf(33)); assertEquals(h.deleteMin().getKey(), BigInteger.valueOf(33)); assertEquals(h.findMin().getKey(), BigInteger.valueOf(35)); assertEquals(h.deleteMin().getKey(), BigInteger.valueOf(35)); assertEquals(h.findMin().getKey(), BigInteger.valueOf(36)); assertEquals(h.deleteMin().getKey(), BigInteger.valueOf(36)); assertEquals(h.size(), 0); assertTrue(h.isEmpty()); } @Test public void test() { AddressableHeap h = new BigIntegerRadixAddressableHeap(BigInteger.valueOf(0), SIZE); for (int i = 0; i < SIZE.intValue(); i++) { h.insert(BigInteger.valueOf(i)); assertEquals(BigInteger.ZERO, h.findMin().getKey()); assertFalse(h.isEmpty()); assertEquals(h.size(), i + 1); } for (int i = SIZE.intValue() - 1; i >= 0; i--) { assertEquals(SIZE.subtract(BigInteger.valueOf(i + 1)), h.findMin().getKey()); h.deleteMin(); } } @Test public void testSortRandomSeed1() { AddressableHeap h = new BigIntegerRadixAddressableHeap(BigInteger.valueOf(0), SIZE.add(BigInteger.ONE)); Random generator = new Random(1); long[] a = new long[SIZE.intValue()]; for (int i = 0; i < SIZE.intValue(); i++) { a[i] = (long) (SIZE.longValue() * generator.nextDouble()); } Arrays.sort(a); for (int i = 0; i < SIZE.intValue(); i++) { h.insert(BigInteger.valueOf(a[i])); } BigInteger prev = null, cur; while (!h.isEmpty()) { cur = h.findMin().getKey(); h.deleteMin(); if (prev != null) { assertTrue(prev.compareTo(cur) <= 0); } prev = cur; } } @Test public void testMonotoneOkOnLastDeleted() { AddressableHeap h = new BigIntegerRadixAddressableHeap(BigInteger.ZERO, BigInteger.valueOf(100)); h.insert(BigInteger.valueOf(100L)); assertEquals(BigInteger.valueOf(100L), h.findMin().getKey()); h.insert(BigInteger.valueOf(99L)); assertEquals(BigInteger.valueOf(99L), h.findMin().getKey()); } @Test(expected = IllegalArgumentException.class) public void testMonotoneNotOkOnLastDeleted() { AddressableHeap h = new BigIntegerRadixAddressableHeap(BigInteger.ZERO, BigInteger.valueOf(100)); h.insert(BigInteger.valueOf(100L)); h.deleteMin(); h.insert(BigInteger.valueOf(99L)); } @Test(expected = IllegalArgumentException.class) public void testIllegalConstruction() { new BigIntegerRadixAddressableHeap(BigInteger.ZERO.subtract(BigInteger.ONE), BigInteger.valueOf(100)); } @Test(expected = IllegalArgumentException.class) public void testIllegalConstruction1() { new BigIntegerRadixAddressableHeap(BigInteger.valueOf(100), BigInteger.valueOf(99)); } @Test(expected = IllegalArgumentException.class) public void testIllegalConstruction2() { new BigIntegerRadixAddressableHeap(null, BigInteger.valueOf(99)); } @Test(expected = IllegalArgumentException.class) public void testIllegalConstruction3() { new BigIntegerRadixAddressableHeap(BigInteger.valueOf(99), null); } @Test public void testSameMinMax() { AddressableHeap h = new BigIntegerRadixAddressableHeap(BigInteger.ONE, BigInteger.ONE); for (int i = 0; i < 15; i++) { h.insert(BigInteger.valueOf(1)); } assertEquals(15, h.size()); for (int i = 0; i < 15; i++) { assertEquals(BigInteger.ONE, h.deleteMin().getKey()); } assertEquals(0, h.size()); } @Test public void testBigDifference() { BigInteger longMax = BigInteger.valueOf(Long.MAX_VALUE); BigInteger bigValue = longMax.multiply(longMax); AddressableHeap h = new BigIntegerRadixAddressableHeap(BigInteger.ZERO, bigValue); h.insert(BigInteger.ZERO); h.insert(longMax); h.insert(bigValue); assertEquals(3, h.size()); assertEquals(BigInteger.ZERO, h.deleteMin().getKey()); assertEquals(longMax, h.deleteMin().getKey()); assertEquals(bigValue, h.deleteMin().getKey()); assertEquals(0, h.size()); } @Test @SuppressWarnings("unchecked") public void testDelete() { AddressableHeap h = new BigIntegerRadixAddressableHeap(BigInteger.valueOf(0), BigInteger.valueOf(15)); AddressableHeap.Handle array[]; array = new AddressableHeap.Handle[15]; for (int i = 0; i < 15; i++) { array[i] = h.insert(BigInteger.valueOf(i)); } array[5].delete(); assertEquals(BigInteger.valueOf(0), h.findMin().getKey()); array[7].delete(); assertEquals(BigInteger.valueOf(0), h.findMin().getKey()); array[0].delete(); assertEquals(BigInteger.valueOf(1), h.findMin().getKey()); array[2].delete(); assertEquals(BigInteger.valueOf(1), h.findMin().getKey()); array[1].delete(); assertEquals(BigInteger.valueOf(3), h.findMin().getKey()); array[3].delete(); assertEquals(BigInteger.valueOf(4), h.findMin().getKey()); array[9].delete(); assertEquals(BigInteger.valueOf(4), h.findMin().getKey()); array[4].delete(); assertEquals(BigInteger.valueOf(6), h.findMin().getKey()); array[8].delete(); assertEquals(BigInteger.valueOf(6), h.findMin().getKey()); array[11].delete(); assertEquals(BigInteger.valueOf(6), h.findMin().getKey()); array[6].delete(); assertEquals(BigInteger.valueOf(10), h.findMin().getKey()); array[12].delete(); assertEquals(BigInteger.valueOf(10), h.findMin().getKey()); array[10].delete(); assertEquals(BigInteger.valueOf(13), h.findMin().getKey()); array[13].delete(); assertEquals(BigInteger.valueOf(14), h.findMin().getKey()); array[14].delete(); assertTrue(h.isEmpty()); } @Test @SuppressWarnings("unchecked") public void testDelete1() { AddressableHeap h = new BigIntegerRadixAddressableHeap(BigInteger.valueOf(0), BigInteger.valueOf(10)); AddressableHeap.Handle array[]; array = new AddressableHeap.Handle[8]; for (int i = 0; i < 8; i++) { array[i] = h.insert(BigInteger.valueOf(i)); } array[5].delete(); assertEquals(BigInteger.valueOf(0), h.findMin().getKey()); array[7].delete(); assertEquals(BigInteger.valueOf(0), h.findMin().getKey()); array[0].delete(); assertEquals(BigInteger.valueOf(1), h.findMin().getKey()); array[2].delete(); assertEquals(BigInteger.valueOf(1), h.findMin().getKey()); array[1].delete(); assertEquals(BigInteger.valueOf(3), h.findMin().getKey()); } @Test @SuppressWarnings("unchecked") public void testAddDelete() { AddressableHeap h = new BigIntegerRadixAddressableHeap(BigInteger.ZERO, SIZE); AddressableHeap.Handle array[]; array = new AddressableHeap.Handle[SIZE.intValue()]; for (int i = 0; i < SIZE.intValue(); i++) { array[i] = h.insert(BigInteger.valueOf(i)); } for (int i = SIZE.intValue() - 1; i >= 0; i--) { array[i].delete(); if (i > 0) { assertEquals(BigInteger.valueOf(0), h.findMin().getKey()); } } assertTrue(h.isEmpty()); } @Test public void testClear() { AddressableHeap h = new BigIntegerRadixAddressableHeap(BigInteger.ZERO, BigInteger.valueOf(15)); for (int i = 0; i < 15; i++) { h.insert(BigInteger.valueOf(i)); } h.clear(); assertEquals(0L, h.size()); assertTrue(h.isEmpty()); } @SuppressWarnings("unchecked") @Test(expected = IllegalArgumentException.class) public void testDeleteTwice() { AddressableHeap h = new BigIntegerRadixAddressableHeap(BigInteger.ZERO, BigInteger.valueOf(15)); AddressableHeap.Handle array[]; array = new AddressableHeap.Handle[15]; for (int i = 0; i < 15; i++) { array[i] = h.insert(BigInteger.valueOf(i)); } array[5].delete(); assertEquals(BigInteger.valueOf(0), h.findMin().getKey()); array[7].delete(); assertEquals(BigInteger.valueOf(0), h.findMin().getKey()); array[0].delete(); assertEquals(BigInteger.valueOf(1), h.findMin().getKey()); array[2].delete(); assertEquals(BigInteger.valueOf(1), h.findMin().getKey()); array[1].delete(); assertEquals(BigInteger.valueOf(3), h.findMin().getKey()); array[3].delete(); assertEquals(BigInteger.valueOf(4), h.findMin().getKey()); array[9].delete(); assertEquals(BigInteger.valueOf(4), h.findMin().getKey()); array[4].delete(); assertEquals(BigInteger.valueOf(6), h.findMin().getKey()); // again array[2].delete(); } @Test(expected = IllegalArgumentException.class) public void testDeleteMinDeleteTwice() { AddressableHeap h = new BigIntegerRadixAddressableHeap(BigInteger.ZERO, BigInteger.valueOf(100)); AddressableHeap.Handle e1 = h.insert(BigInteger.valueOf(50)); h.insert(BigInteger.valueOf(100)); h.deleteMin(); e1.delete(); } @Test(expected = IllegalArgumentException.class) public void testDeleteMinDeleteTwice1() { AddressableHeap h = new BigIntegerRadixAddressableHeap(BigInteger.valueOf(99), BigInteger.valueOf(200)); for (int i = 100; i < 200; i++) { h.insert(BigInteger.valueOf(i)); } h.deleteMin().delete(); } @Test(expected = IllegalArgumentException.class) public void testDeleteMinDecreaseKey() { AddressableHeap h = new BigIntegerRadixAddressableHeap(BigInteger.valueOf(100), BigInteger.valueOf(200)); for (int i = 100; i < 200; i++) { h.insert(BigInteger.valueOf(i)); } h.deleteMin().decreaseKey(BigInteger.ZERO); } @Test @SuppressWarnings("unchecked") public void testDecreaseKey() { AddressableHeap h = new BigIntegerRadixAddressableHeap(BigInteger.ZERO, BigInteger.valueOf(200)); AddressableHeap.Handle array[]; array = new AddressableHeap.Handle[15]; h.insert(BigInteger.ZERO); // monotone for (int i = 0; i < 15; i++) { array[i] = h.insert(BigInteger.valueOf(i).add(BigInteger.valueOf(100))); } array[5].decreaseKey(BigInteger.valueOf(5)); array[1].decreaseKey(BigInteger.valueOf(50)); array[10].decreaseKey(BigInteger.valueOf(3)); array[0].decreaseKey(BigInteger.valueOf(1)); array[5].delete(); array[2].delete(); array[11].delete(); array[9].delete(); assertEquals(BigInteger.valueOf(0), h.deleteMin().getKey()); assertEquals(BigInteger.valueOf(1), h.deleteMin().getKey()); assertEquals(BigInteger.valueOf(3), h.deleteMin().getKey()); assertEquals(BigInteger.valueOf(50), h.deleteMin().getKey()); assertEquals(BigInteger.valueOf(103), h.deleteMin().getKey()); assertEquals(BigInteger.valueOf(104), h.deleteMin().getKey()); array[14].decreaseKey(BigInteger.valueOf(111)); array[13].decreaseKey(BigInteger.valueOf(109)); assertEquals(BigInteger.valueOf(106), h.deleteMin().getKey()); assertEquals(BigInteger.valueOf(107), h.deleteMin().getKey()); assertEquals(BigInteger.valueOf(108), h.deleteMin().getKey()); assertEquals(BigInteger.valueOf(109), h.deleteMin().getKey()); assertEquals(BigInteger.valueOf(111), h.deleteMin().getKey()); assertEquals(BigInteger.valueOf(112), h.deleteMin().getKey()); } @SuppressWarnings("unchecked") @Test(expected = IllegalArgumentException.class) public void testIncreaseKey() { AddressableHeap h = new BigIntegerRadixAddressableHeap(BigInteger.ZERO, BigInteger.valueOf(200)); AddressableHeap.Handle array[]; array = new AddressableHeap.Handle[15]; for (int i = 0; i < 15; i++) { array[i] = h.insert(BigInteger.valueOf(i).add(BigInteger.valueOf(100))); } assertEquals(BigInteger.valueOf(100), h.findMin().getKey()); array[5].decreaseKey(BigInteger.valueOf(5)); assertEquals(BigInteger.valueOf(5), h.findMin().getKey()); array[1].decreaseKey(BigInteger.valueOf(102)); } @Test @SuppressWarnings("unchecked") public void testSerializable() throws IOException, ClassNotFoundException { AddressableHeap h = new BigIntegerRadixAddressableHeap(BigInteger.ZERO, BigInteger.valueOf(15)); for (long i = 0; i < 15; i++) { h.insert(BigInteger.valueOf(i)); } // write ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(h); oos.close(); byte[] data = baos.toByteArray(); // read ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data)); Object o = ois.readObject(); ois.close(); h = (AddressableHeap) o; for (long i = 0; i < 15; i++) { assertEquals(15 - i, h.size()); assertEquals(BigInteger.valueOf(i), h.findMin().getKey()); h.deleteMin(); } assertTrue(h.isEmpty()); } } jheaps-jheaps-0.14/src/test/java/org/jheaps/monotone/BigIntegerRadixHeapTest.java000066400000000000000000000156461367505655000301060ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.monotone; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.math.BigInteger; import java.util.Arrays; import java.util.Random; import org.jheaps.Heap; import org.jheaps.monotone.BigIntegerRadixHeap; import org.junit.Test; public class BigIntegerRadixHeapTest { private static final BigInteger SIZE = BigInteger.valueOf(100000); @Test public void testBug2() { Heap h = new BigIntegerRadixHeap(BigInteger.ZERO, BigInteger.valueOf(100)); h.insert(BigInteger.ZERO); assertEquals(BigInteger.ZERO, h.findMin()); assertEquals(BigInteger.ZERO, h.deleteMin()); h.insert(BigInteger.valueOf(15)); assertEquals(BigInteger.valueOf(15), h.findMin()); } @Test public void test() { Heap h = new BigIntegerRadixHeap(BigInteger.ZERO, SIZE); for (long i = 0; i < SIZE.longValue(); i++) { h.insert(BigInteger.valueOf(i)); assertEquals(BigInteger.valueOf(0), h.findMin()); assertFalse(h.isEmpty()); assertEquals(h.size(), i + 1); } for (long i = SIZE.longValue() - 1; i >= 0; i--) { assertEquals(h.findMin(), BigInteger.valueOf(SIZE.longValue() - i - 1)); h.deleteMin(); } } @Test public void testVerySmall() { Heap h = new BigIntegerRadixHeap(BigInteger.valueOf(29), BigInteger.valueOf(36)); h.insert(BigInteger.valueOf(29L)); h.insert(BigInteger.valueOf(30L)); h.insert(BigInteger.valueOf(31L)); h.insert(BigInteger.valueOf(30L)); h.insert(BigInteger.valueOf(33L)); h.insert(BigInteger.valueOf(36L)); h.insert(BigInteger.valueOf(35L)); assertEquals(h.size(), 7); assertEquals(h.findMin().longValue(), 29L); assertEquals(h.size(), 7); assertEquals(h.deleteMin().longValue(), 29L); assertEquals(h.size(), 6); assertEquals(h.findMin().longValue(), 30L); assertEquals(h.deleteMin().longValue(), 30L); assertEquals(h.findMin().longValue(), 30L); assertEquals(h.deleteMin().longValue(), 30L); assertEquals(h.findMin().longValue(), 31L); assertEquals(h.deleteMin().longValue(), 31L); assertEquals(h.findMin().longValue(), 33L); assertEquals(h.deleteMin().longValue(), 33L); assertEquals(h.findMin().longValue(), 35L); assertEquals(h.deleteMin().longValue(), 35L); assertEquals(h.findMin().longValue(), 36L); assertEquals(h.deleteMin().longValue(), 36L); assertEquals(h.size(), 0); assertTrue(h.isEmpty()); } @Test public void testSortRandomSeed1() { Heap h = new BigIntegerRadixHeap(BigInteger.valueOf(0), SIZE.add(BigInteger.ONE)); Random generator = new Random(1); long[] a = new long[SIZE.intValue()]; for (int i = 0; i < SIZE.intValue(); i++) { a[i] = (long) (SIZE.longValue() * generator.nextDouble()); } Arrays.sort(a); for (int i = 0; i < SIZE.intValue(); i++) { h.insert(BigInteger.valueOf(a[i])); } BigInteger prev = null, cur; while (!h.isEmpty()) { cur = h.findMin(); h.deleteMin(); if (prev != null) { assertTrue(prev.compareTo(cur) <= 0); } prev = cur; } } @Test public void testClear() { Heap h = new BigIntegerRadixHeap(BigInteger.ZERO, BigInteger.valueOf(15)); for (long i = 0; i < 15; i++) { h.insert(BigInteger.valueOf(i)); } h.clear(); assertEquals(0L, h.size()); assertTrue(h.isEmpty()); } @Test public void testMultipleDeleteMin() { final BigInteger step = BigInteger.valueOf(7); final BigInteger min = BigInteger.ZERO; final BigInteger max = BigInteger.valueOf(100000); Heap h = new BigIntegerRadixHeap(min, max); h.insert(min); BigInteger cur = min; while(cur.compareTo(max) < 0) { assertEquals(cur, h.findMin()); if (cur.add(step).compareTo(max) >= 0) { break; } BigInteger newCur = cur.add(step); h.insert(newCur); assertEquals(cur, h.findMin()); assertEquals(cur, h.deleteMin()); cur = newCur; } } @Test public void testMonotoneOkOnLastDeleted() { Heap h = new BigIntegerRadixHeap(BigInteger.ZERO, BigInteger.valueOf(100)); h.insert(BigInteger.valueOf(100L)); assertEquals(BigInteger.valueOf(100L), h.findMin()); h.insert(BigInteger.valueOf(99L)); assertEquals(BigInteger.valueOf(99L), h.findMin()); } @Test(expected = IllegalArgumentException.class) public void testMonotoneNotOkOnLastDeleted() { Heap h = new BigIntegerRadixHeap(BigInteger.ZERO, BigInteger.valueOf(100)); h.insert(BigInteger.valueOf(100L)); h.deleteMin(); h.insert(BigInteger.valueOf(99L)); } @Test(expected = IllegalArgumentException.class) public void testIllegalConstruction() { new BigIntegerRadixHeap(BigInteger.ZERO.subtract(BigInteger.ONE), BigInteger.valueOf(100)); } @Test(expected = IllegalArgumentException.class) public void testIllegalConstruction1() { new BigIntegerRadixHeap(BigInteger.valueOf(100), BigInteger.valueOf(99)); } @Test(expected = IllegalArgumentException.class) public void testIllegalConstruction2() { new BigIntegerRadixHeap(null, BigInteger.valueOf(99)); } @Test(expected = IllegalArgumentException.class) public void testIllegalConstruction3() { new BigIntegerRadixHeap(BigInteger.valueOf(99), null); } @Test @SuppressWarnings("unchecked") public void testSerializable() throws IOException, ClassNotFoundException { Heap h = new BigIntegerRadixHeap(BigInteger.ZERO, BigInteger.valueOf(15)); for (long i = 0; i < 15; i++) { h.insert(BigInteger.valueOf(i)); } // write ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(h); oos.close(); byte[] data = baos.toByteArray(); // read ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data)); Object o = ois.readObject(); ois.close(); h = (Heap) o; for (long i = 0; i < 15; i++) { assertEquals(15 - i, h.size()); assertEquals(BigInteger.valueOf(i), h.findMin()); h.deleteMin(); } assertTrue(h.isEmpty()); } } jheaps-jheaps-0.14/src/test/java/org/jheaps/monotone/DoubleRadixAddressableHeapTest.java000066400000000000000000000313631367505655000314250ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.monotone; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Random; import org.jheaps.AddressableHeap; import org.junit.Test; public class DoubleRadixAddressableHeapTest { private static final int SIZE = 100000; @Test public void testBug1() { AddressableHeap h = new DoubleRadixAddressableHeap(0.0, 3.667944409236726); h.insert(0.0); assertEquals(0.0, h.findMin().getKey(), 1e-9); h.insert(0.9169861023091815); h.deleteMin(); assertEquals(0.9169861023091815, h.findMin().getKey(), 1e-9); h.insert(1.7814708581727154); h.deleteMin(); assertEquals(1.7814708581727154, h.findMin().getKey(), 1e-9); } @Test public void testBug2() { AddressableHeap h = new DoubleRadixAddressableHeap(0.0, 100.0); h.insert(0.0); assertEquals(0.0, h.findMin().getKey(), 1e-9); assertEquals(0.0, h.deleteMin().getKey(), 1e-9); h.insert(15.0); assertEquals(15.0, h.findMin().getKey(), 1e-9); } @Test public void testVerySmall() { AddressableHeap h = new DoubleRadixAddressableHeap(15.0, 50.5); h.insert(15.3); h.insert(50.4); h.insert(20.999999); h.insert(50.5); h.insert(30.3); h.insert(25.2); h.insert(17.7777); assertEquals(7, h.size()); assertEquals(15.3, h.findMin().getKey(), 1e-9); assertEquals(7, h.size()); assertEquals(15.3, h.deleteMin().getKey(), 1e-9); assertEquals(6, h.size()); assertEquals(17.7777, h.findMin().getKey(), 1e-9); assertEquals(17.7777, h.deleteMin().getKey(), 1e-9); assertEquals(20.999999, h.findMin().getKey(), 1e-9); assertEquals(20.999999, h.deleteMin().getKey(), 1e-9); assertEquals(25.2, h.findMin().getKey(), 1e-9); assertEquals(25.2, h.deleteMin().getKey(), 1e-9); assertEquals(30.3, h.findMin().getKey(), 1e-9); assertEquals(30.3, h.deleteMin().getKey(), 1e-9); assertEquals(50.4, h.findMin().getKey(), 1e-9); assertEquals(50.4, h.deleteMin().getKey(), 1e-9); assertEquals(50.5, h.findMin().getKey(), 1e-9); assertEquals(50.5, h.deleteMin().getKey(), 1e-9); assertEquals(h.size(), 0); assertTrue(h.isEmpty()); } @Test public void test() { AddressableHeap h = new DoubleRadixAddressableHeap(0.0, SIZE); for (long i = 0; i < SIZE; i++) { h.insert((double) i); assertEquals(0d, h.findMin().getKey(), 1e-9); assertFalse(h.isEmpty()); assertEquals(h.size(), i + 1); } for (int i = SIZE - 1; i >= 0; i--) { assertEquals((double) (SIZE - i - 1), h.findMin().getKey(), 1e-9); h.deleteMin(); } } @Test public void testSortRandomSeed1() { AddressableHeap h = new DoubleRadixAddressableHeap(0.0, 1.0); Random generator = new Random(1); h.insert(0.0d); for (int i = 1; i < SIZE; i++) { double d = generator.nextDouble(); h.insert(d); } Double prev = null, cur; while (!h.isEmpty()) { cur = h.deleteMin().getKey(); if (prev != null) { assertTrue(prev.compareTo(cur) <= 0); } prev = cur; } } @Test public void testSortRandomSeed2() { AddressableHeap h = new DoubleRadixAddressableHeap(0.0, 1.0); Random generator = new Random(2); h.insert(0.0d); for (int i = 1; i < SIZE; i++) { double d = generator.nextDouble(); h.insert(d); } Double prev = null, cur; while (!h.isEmpty()) { cur = h.deleteMin().getKey(); if (prev != null) { assertTrue(prev.compareTo(cur) <= 0); } prev = cur; } } @Test public void testSameMinMax() { AddressableHeap h = new DoubleRadixAddressableHeap(1.0, 1.0); for (int i = 0; i < 15; i++) { h.insert(1.0); } assertEquals(15, h.size()); for (int i = 0; i < 15; i++) { assertEquals(1.0, h.deleteMin().getKey(), 1e-9); } assertEquals(0, h.size()); } @Test public void testMaxDifference() { AddressableHeap h = new DoubleRadixAddressableHeap(0.0, Double.MAX_VALUE); h.insert(0.0); h.insert(Double.MAX_VALUE); assertEquals(2, h.size()); assertEquals(0.0, h.deleteMin().getKey(), 1e-9); assertEquals(Double.MAX_VALUE, h.deleteMin().getKey(), 1e-9); assertEquals(0, h.size()); } @Test @SuppressWarnings("unchecked") public void testDelete() { AddressableHeap h = new DoubleRadixAddressableHeap(0.0, 15.0); AddressableHeap.Handle array[]; array = new AddressableHeap.Handle[15]; for (int i = 0; i < 15; i++) { array[i] = h.insert((double) i); } array[5].delete(); assertEquals(0d, h.findMin().getKey(), 1e-9); array[7].delete(); assertEquals(0d, h.findMin().getKey(), 1e-9); array[0].delete(); assertEquals(1d, h.findMin().getKey(), 1e-9); array[2].delete(); assertEquals(1d, h.findMin().getKey(), 1e-9); array[1].delete(); assertEquals(3d, h.findMin().getKey(), 1e-9); array[3].delete(); assertEquals(4d, h.findMin().getKey(), 1e-9); array[9].delete(); assertEquals(4d, h.findMin().getKey(), 1e-9); array[4].delete(); assertEquals(6d, h.findMin().getKey(), 1e-9); array[8].delete(); assertEquals(6d, h.findMin().getKey(), 1e-9); array[11].delete(); assertEquals(6d, h.findMin().getKey(), 1e-9); array[6].delete(); assertEquals(10d, h.findMin().getKey(), 1e-9); array[12].delete(); assertEquals(10d, h.findMin().getKey(), 1e-9); array[10].delete(); assertEquals(13d, h.findMin().getKey(), 1e-9); array[13].delete(); assertEquals(14d, h.findMin().getKey(), 1e-9); array[14].delete(); assertTrue(h.isEmpty()); } @Test @SuppressWarnings("unchecked") public void testDelete1() { AddressableHeap h = new DoubleRadixAddressableHeap(0.0, 10.0); AddressableHeap.Handle array[]; array = new AddressableHeap.Handle[8]; for (int i = 0; i < 8; i++) { array[i] = h.insert((double) i); } array[5].delete(); assertEquals(0d, h.findMin().getKey(), 1e-9); array[7].delete(); assertEquals(0d, h.findMin().getKey(), 1e-9); array[0].delete(); assertEquals(1d, h.findMin().getKey(), 1e-9); array[2].delete(); assertEquals(1d, h.findMin().getKey(), 1e-9); array[1].delete(); assertEquals(3d, h.findMin().getKey(), 1e-9); } @Test @SuppressWarnings("unchecked") public void testAddDelete() { AddressableHeap h = new DoubleRadixAddressableHeap(0.0, (double) SIZE); AddressableHeap.Handle array[]; array = new AddressableHeap.Handle[SIZE]; for (int i = 0; i < SIZE; i++) { array[i] = h.insert((double) i); } for (int i = SIZE - 1; i >= 0; i--) { array[i].delete(); if (i > 0) { assertEquals(0d, h.findMin().getKey(), 1e-9); } } assertTrue(h.isEmpty()); } @Test public void testClear() { AddressableHeap h = new DoubleRadixAddressableHeap(0.0, 15.0); for (int i = 0; i < 15; i++) { h.insert((double) i); } h.clear(); assertEquals(0L, h.size()); assertTrue(h.isEmpty()); } @SuppressWarnings("unchecked") @Test(expected = IllegalArgumentException.class) public void testDeleteTwice() { AddressableHeap h = new DoubleRadixAddressableHeap(0.0, 15.0); AddressableHeap.Handle array[]; array = new AddressableHeap.Handle[15]; for (int i = 0; i < 15; i++) { array[i] = h.insert((double) i); } array[5].delete(); assertEquals(0d, h.findMin().getKey(), 1e-9); array[7].delete(); assertEquals(0d, h.findMin().getKey(), 1e-9); array[0].delete(); assertEquals(1d, h.findMin().getKey(), 1e-9); array[2].delete(); assertEquals(1d, h.findMin().getKey(), 1e-9); array[1].delete(); assertEquals(3d, h.findMin().getKey(), 1e-9); array[3].delete(); assertEquals(4d, h.findMin().getKey(), 1e-9); array[9].delete(); assertEquals(4d, h.findMin().getKey(), 1e-9); array[4].delete(); assertEquals(6d, h.findMin().getKey(), 1e-9); // again array[2].delete(); } @Test(expected = IllegalArgumentException.class) public void testDeleteMinDeleteTwice() { AddressableHeap h = new DoubleRadixAddressableHeap(0.0, 100.0); AddressableHeap.Handle e1 = h.insert(50.0); h.insert(100.0); h.deleteMin(); e1.delete(); } @Test(expected = IllegalArgumentException.class) public void testDeleteMinDeleteTwice1() { AddressableHeap h = new DoubleRadixAddressableHeap(99.0, 200.0); for (int i = 100; i < 200; i++) { h.insert((double) i); } h.deleteMin().delete(); } @Test(expected = IllegalArgumentException.class) public void testDeleteMinDecreaseKey() { AddressableHeap h = new DoubleRadixAddressableHeap(100.0, 200.0); for (int i = 100; i < 200; i++) { h.insert((double) i); } h.deleteMin().decreaseKey(0.0); } @Test @SuppressWarnings("unchecked") public void testDecreaseKey() { AddressableHeap h = new DoubleRadixAddressableHeap(0.0, 200.0); AddressableHeap.Handle array[]; array = new AddressableHeap.Handle[15]; h.insert(0.0); // monotone for (int i = 0; i < 15; i++) { array[i] = h.insert((double) i + 100.0); } array[5].decreaseKey(5.0); array[1].decreaseKey(50.0); array[10].decreaseKey(3.0); array[0].decreaseKey(1.0); array[5].delete(); array[2].delete(); assertEquals(0.0, h.deleteMin().getKey(), 1e-9); assertEquals(1.0, h.deleteMin().getKey(), 1e-9); assertEquals(3.0, h.deleteMin().getKey(), 1e-9); assertEquals(50.0, h.deleteMin().getKey(), 1e-9); assertEquals(103.0, h.deleteMin().getKey(), 1e-9); assertEquals(104.0, h.deleteMin().getKey(), 1e-9); array[14].decreaseKey(107.5); array[13].decreaseKey(108.5); assertEquals(106.0, h.deleteMin().getKey(), 1e-9); assertEquals(107.0, h.deleteMin().getKey(), 1e-9); assertEquals(107.5, h.deleteMin().getKey(), 1e-9); assertEquals(108.0, h.deleteMin().getKey(), 1e-9); assertEquals(108.5, h.deleteMin().getKey(), 1e-9); assertEquals(109.0, h.deleteMin().getKey(), 1e-9); assertEquals(111.0, h.deleteMin().getKey(), 1e-9); assertEquals(112.0, h.deleteMin().getKey(), 1e-9); } @SuppressWarnings("unchecked") @Test(expected = IllegalArgumentException.class) public void testIncreaseKey() { AddressableHeap h = new DoubleRadixAddressableHeap(0.0, 200.0); AddressableHeap.Handle array[]; array = new AddressableHeap.Handle[15]; for (int i = 0; i < 15; i++) { array[i] = h.insert((double) i + 100.0); } assertEquals(Double.valueOf(100), h.findMin().getKey()); array[5].decreaseKey(5.0); assertEquals(Double.valueOf(5), h.findMin().getKey()); array[1].decreaseKey(102.0); } @SuppressWarnings("unchecked") @Test public void testSerializable() throws IOException, ClassNotFoundException { AddressableHeap h = new DoubleRadixAddressableHeap(0.0, 15.0); for (int i = 0; i < 15; i++) { h.insert((double) i); } // write ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(h); oos.close(); byte[] data = baos.toByteArray(); // read ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data)); Object o = ois.readObject(); ois.close(); h = (AddressableHeap) o; for (int i = 0; i < 15; i++) { assertEquals(15 - i, h.size()); assertEquals((double) i, h.findMin().getKey(), 1e-9); h.deleteMin(); } assertTrue(h.isEmpty()); } @Test(expected = IllegalArgumentException.class) public void testIllegalConstruction() { new DoubleRadixAddressableHeap(-1, 10); } @Test(expected = IllegalArgumentException.class) public void testIllegalConstruction1() { new DoubleRadixAddressableHeap(10, 9); } @Test(expected = IllegalArgumentException.class) public void testIllegalConstruction2() { new DoubleRadixAddressableHeap(Double.NEGATIVE_INFINITY, 9); } @Test(expected = IllegalArgumentException.class) public void testIllegalConstruction3() { new DoubleRadixAddressableHeap(0d, Double.POSITIVE_INFINITY); } } jheaps-jheaps-0.14/src/test/java/org/jheaps/monotone/DoubleRadixHeapTest.java000066400000000000000000000131151367505655000272660ustar00rootroot00000000000000/* * (C) Copyright 2014-2018, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.monotone; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.util.Random; import org.jheaps.Heap; import org.junit.Test; public class DoubleRadixHeapTest { private static final int SIZE = 100000; /* * Affects version 0.7 of the library. */ @Test public void testBug1() { Heap h = new DoubleRadixHeap(0.0, 3.667944409236726); h.insert(0.0); assertEquals(0.0, h.findMin(), 1e-9); h.insert(0.9169861023091815); h.deleteMin(); assertEquals(0.9169861023091815, h.findMin(), 1e-9); h.insert(1.7814708581727154); h.deleteMin(); assertEquals(1.7814708581727154, h.findMin(), 1e-9); } @Test public void testBug2() { Heap h = new DoubleRadixHeap(0.0, 100.0); h.insert(0.0); assertEquals(0.0, h.findMin(), 1e-9); assertEquals(0.0, h.deleteMin(), 1e-9); h.insert(15.0); assertEquals(15.0, h.findMin(), 1e-9); } @Test public void testVerySmall() { Heap h = new DoubleRadixHeap(15.0, 50.5); h.insert(15.3); h.insert(50.4); h.insert(20.999999); h.insert(50.5); h.insert(30.3); h.insert(25.2); h.insert(17.7777); assertEquals(7, h.size()); assertEquals(15.3, h.findMin(), 1e-9); assertEquals(7, h.size()); assertEquals(15.3, h.deleteMin(), 1e-9); assertEquals(6, h.size()); assertEquals(17.7777, h.findMin(), 1e-9); assertEquals(17.7777, h.deleteMin(), 1e-9); assertEquals(20.999999, h.findMin(), 1e-9); assertEquals(20.999999, h.deleteMin(), 1e-9); assertEquals(25.2, h.findMin(), 1e-9); assertEquals(25.2, h.deleteMin(), 1e-9); assertEquals(30.3, h.findMin(), 1e-9); assertEquals(30.3, h.deleteMin(), 1e-9); assertEquals(50.4, h.findMin(), 1e-9); assertEquals(50.4, h.deleteMin(), 1e-9); assertEquals(50.5, h.findMin(), 1e-9); assertEquals(50.5, h.deleteMin(), 1e-9); assertEquals(h.size(), 0); assertTrue(h.isEmpty()); } @Test public void test() { Heap h = new DoubleRadixHeap(0.0, SIZE); for (long i = 0; i < SIZE; i++) { h.insert((double) i); assertEquals(0d, h.findMin(), 1e-9); assertFalse(h.isEmpty()); assertEquals(h.size(), i + 1); } for (int i = SIZE - 1; i >= 0; i--) { assertEquals((double) (SIZE - i - 1), h.findMin(), 1e-9); h.deleteMin(); } } @Test public void testSortRandomSeed1() { Heap h = new DoubleRadixHeap(0.0, 1.0); Random generator = new Random(1); h.insert(0.0d); for (int i = 1; i < SIZE; i++) { double d = generator.nextDouble(); h.insert(d); } Double prev = null, cur; while (!h.isEmpty()) { cur = h.deleteMin(); if (prev != null) { assertTrue(prev.compareTo(cur) <= 0); } prev = cur; } } @Test public void testSortRandomSeed2() { Heap h = new DoubleRadixHeap(0.0, 1.0); Random generator = new Random(2); h.insert(0.0d); for (int i = 1; i < SIZE; i++) { double d = generator.nextDouble(); h.insert(d); } Double prev = null, cur; while (!h.isEmpty()) { cur = h.deleteMin(); if (prev != null) { assertTrue(prev.compareTo(cur) <= 0); } prev = cur; } } @Test public void testSameMinMax() { Heap h = new DoubleRadixHeap(1.0, 1.0); for (int i = 0; i < 15; i++) { h.insert(1.0); } assertEquals(15, h.size()); for (int i = 0; i < 15; i++) { assertEquals(1.0, h.deleteMin(), 1e-9); } assertEquals(0, h.size()); } @Test public void testMultipleDeleteMin() { final double step = 0.3333; final double max = 1000.0; Heap h = new DoubleRadixHeap(0.0, max); h.insert(0.0); double cur = 0.0; while(cur < max) { assertEquals(cur, h.findMin(), 1e-9); if (cur + step >= max) { break; } double newCur = cur + step; h.insert(newCur); assertEquals(cur, h.findMin(), 1e-9); assertEquals(cur, h.deleteMin(), 1e-9); cur = newCur; } } @Test public void testMaxDifference() { Heap h = new DoubleRadixHeap(0.0, Double.MAX_VALUE); h.insert(0.0); h.insert(Double.MAX_VALUE); assertEquals(2, h.size()); assertEquals(0.0, h.deleteMin(), 1e-9); assertEquals(Double.MAX_VALUE, h.deleteMin(), 1e-9); assertEquals(0, h.size()); } @Test(expected = IllegalArgumentException.class) public void testIllegalConstruction1() { new DoubleRadixHeap(-1.0, Double.MAX_VALUE); } @Test(expected = IllegalArgumentException.class) public void testIllegalConstruction2() { new DoubleRadixHeap(Double.NEGATIVE_INFINITY, Double.MAX_VALUE); } @Test(expected = IllegalArgumentException.class) public void testIllegalConstruction3() { new DoubleRadixHeap(0d, Double.POSITIVE_INFINITY); } @Test(expected = IllegalArgumentException.class) public void testIllegalConstruction4() { new DoubleRadixHeap(15d, 14d); } } jheaps-jheaps-0.14/src/test/java/org/jheaps/monotone/IntegerRadixAddressableHeapTest.java000066400000000000000000000323171367505655000316100ustar00rootroot00000000000000/* * (C) Copyright 2014-2018, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.monotone; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Random; import org.jheaps.AddressableHeap; import org.jheaps.AddressableHeap.Handle; import org.junit.Test; public class IntegerRadixAddressableHeapTest { private static final int SIZE = 100000; @Test public void testBug2() { AddressableHeap h = new IntegerRadixAddressableHeap(0, 100); h.insert(0); assertEquals(0, h.findMin().getKey().intValue()); assertEquals(0, h.deleteMin().getKey().intValue()); h.insert(15); assertEquals(15, h.findMin().getKey().intValue()); } @Test public void testVerySmall() { AddressableHeap h = new IntegerRadixAddressableHeap(15, 100); h.insert(15); h.insert(50); h.insert(21); h.insert(51); h.insert(30); h.insert(25); h.insert(18); assertEquals(7, h.size()); assertEquals(15L, h.findMin().getKey().longValue()); assertEquals(7, h.size()); assertEquals(15L, h.deleteMin().getKey().longValue()); assertEquals(6, h.size()); assertEquals(18L, h.findMin().getKey().longValue()); assertEquals(18L, h.deleteMin().getKey().longValue()); assertEquals(21L, h.findMin().getKey().longValue()); assertEquals(21L, h.deleteMin().getKey().longValue()); assertEquals(25L, h.findMin().getKey().longValue()); assertEquals(25L, h.deleteMin().getKey().longValue()); assertEquals(30L, h.findMin().getKey().longValue()); assertEquals(30L, h.deleteMin().getKey().longValue()); assertEquals(50L, h.findMin().getKey().longValue()); assertEquals(50L, h.deleteMin().getKey().longValue()); assertEquals(51L, h.findMin().getKey().longValue()); assertEquals(51L, h.deleteMin().getKey().longValue()); assertEquals(h.size(), 0); assertTrue(h.isEmpty()); } @Test public void test() { AddressableHeap h = new IntegerRadixAddressableHeap(0, SIZE); for (int i = 0; i < SIZE; i++) { h.insert(i); assertEquals(0, h.findMin().getKey(), 1e-9); assertFalse(h.isEmpty()); assertEquals(h.size(), i + 1); } for (int i = SIZE - 1; i >= 0; i--) { assertEquals(SIZE - i - 1, h.findMin().getKey(), 1e-9); h.deleteMin(); } } @Test public void testSortRandomSeed1() { AddressableHeap h = new IntegerRadixAddressableHeap(0, Integer.MAX_VALUE); Random generator = new Random(1); h.insert(0); for (int i = 1; i < SIZE; i++) { int d = Math.abs(generator.nextInt()) + 1; h.insert(d); } Integer prev = null, cur; while (!h.isEmpty()) { cur = h.deleteMin().getKey(); if (prev != null) { assertTrue(prev.compareTo(cur) <= 0); } prev = cur; } } @Test public void testSortRandomSeed2() { AddressableHeap h = new IntegerRadixAddressableHeap(0, Integer.MAX_VALUE); Random generator = new Random(2); h.insert(0); for (int i = 1; i < SIZE; i++) { int d = Math.abs(generator.nextInt()) + 1; h.insert(d); } Integer prev = null, cur; while (!h.isEmpty()) { cur = h.deleteMin().getKey(); if (prev != null) { assertTrue(prev.compareTo(cur) <= 0); } prev = cur; } } @Test public void testSameMinMax() { AddressableHeap h = new IntegerRadixAddressableHeap(1, 1); for (int i = 0; i < 15; i++) { h.insert(1); } assertEquals(15, h.size()); for (int i = 0; i < 15; i++) { assertEquals(1L, h.deleteMin().getKey().longValue()); } assertEquals(0, h.size()); } @Test public void testMaxDifference() { AddressableHeap h = new IntegerRadixAddressableHeap(0, Integer.MAX_VALUE); h.insert(0); h.insert(Integer.MAX_VALUE); assertEquals(2, h.size()); assertEquals(0L, h.deleteMin().getKey().longValue()); assertEquals(Integer.MAX_VALUE, h.deleteMin().getKey().longValue()); assertEquals(0, h.size()); } @Test @SuppressWarnings("unchecked") public void testDelete() { AddressableHeap h = new IntegerRadixAddressableHeap(0, 15); AddressableHeap.Handle array[]; array = new AddressableHeap.Handle[15]; for (int i = 0; i < 15; i++) { array[i] = h.insert(i); } array[5].delete(); assertEquals(Integer.valueOf(0), h.findMin().getKey()); array[7].delete(); assertEquals(Integer.valueOf(0), h.findMin().getKey()); array[0].delete(); assertEquals(Integer.valueOf(1), h.findMin().getKey()); array[2].delete(); assertEquals(Integer.valueOf(1), h.findMin().getKey()); array[1].delete(); assertEquals(Integer.valueOf(3), h.findMin().getKey()); array[3].delete(); assertEquals(Integer.valueOf(4), h.findMin().getKey()); array[9].delete(); assertEquals(Integer.valueOf(4), h.findMin().getKey()); array[4].delete(); assertEquals(Integer.valueOf(6), h.findMin().getKey()); array[8].delete(); assertEquals(Integer.valueOf(6), h.findMin().getKey()); array[11].delete(); assertEquals(Integer.valueOf(6), h.findMin().getKey()); array[6].delete(); assertEquals(Integer.valueOf(10), h.findMin().getKey()); array[12].delete(); assertEquals(Integer.valueOf(10), h.findMin().getKey()); array[10].delete(); assertEquals(Integer.valueOf(13), h.findMin().getKey()); array[13].delete(); assertEquals(Integer.valueOf(14), h.findMin().getKey()); array[14].delete(); assertTrue(h.isEmpty()); } @Test @SuppressWarnings("unchecked") public void testDelete1() { AddressableHeap h = new IntegerRadixAddressableHeap(0, 10); AddressableHeap.Handle array[]; array = new AddressableHeap.Handle[8]; for (int i = 0; i < 8; i++) { array[i] = h.insert(i); } array[5].delete(); assertEquals(0, h.findMin().getKey(), 1e-9); array[7].delete(); assertEquals(0, h.findMin().getKey(), 1e-9); array[0].delete(); assertEquals(1, h.findMin().getKey(), 1e-9); array[2].delete(); assertEquals(1, h.findMin().getKey(), 1e-9); array[1].delete(); assertEquals(3, h.findMin().getKey(), 1e-9); } @Test @SuppressWarnings("unchecked") public void testAddDelete() { AddressableHeap h = new IntegerRadixAddressableHeap(0, SIZE); AddressableHeap.Handle array[]; array = new AddressableHeap.Handle[SIZE]; for (int i = 0; i < SIZE; i++) { array[i] = h.insert(i); } for (int i = SIZE - 1; i >= 0; i--) { array[i].delete(); if (i > 0) { assertEquals(Integer.valueOf(0), h.findMin().getKey()); } } assertTrue(h.isEmpty()); } @Test public void testClear() { AddressableHeap h = new IntegerRadixAddressableHeap(0, 15); for (int i = 0; i < 15; i++) { h.insert(i); } h.clear(); assertEquals(0L, h.size()); assertTrue(h.isEmpty()); } @SuppressWarnings("unchecked") @Test(expected = IllegalArgumentException.class) public void testDeleteTwice() { AddressableHeap h = new IntegerRadixAddressableHeap(0, 15); AddressableHeap.Handle array[]; array = new AddressableHeap.Handle[15]; for (int i = 0; i < 15; i++) { array[i] = h.insert(i); } array[5].delete(); assertEquals(Integer.valueOf(0), h.findMin().getKey()); array[7].delete(); assertEquals(Integer.valueOf(0), h.findMin().getKey()); array[0].delete(); assertEquals(Integer.valueOf(1), h.findMin().getKey()); array[2].delete(); assertEquals(Integer.valueOf(1), h.findMin().getKey()); array[1].delete(); assertEquals(Integer.valueOf(3), h.findMin().getKey()); array[3].delete(); assertEquals(Integer.valueOf(4), h.findMin().getKey()); array[9].delete(); assertEquals(Integer.valueOf(4), h.findMin().getKey()); array[4].delete(); assertEquals(Integer.valueOf(6), h.findMin().getKey()); // again array[2].delete(); } @Test(expected = IllegalArgumentException.class) public void testDeleteMinDeleteTwice() { AddressableHeap h = new IntegerRadixAddressableHeap(0, 100); AddressableHeap.Handle e1 = h.insert(50); h.insert(100); h.deleteMin(); e1.delete(); } @Test(expected = IllegalArgumentException.class) public void testDeleteMinDeleteTwice1() { AddressableHeap h = new IntegerRadixAddressableHeap(99, 200); for (int i = 100; i < 200; i++) { h.insert(i); } h.deleteMin().delete(); } @Test(expected = IllegalArgumentException.class) public void testDeleteMinDecreaseKey() { AddressableHeap h = new IntegerRadixAddressableHeap(100, 200); for (int i = 100; i < 200; i++) { h.insert(i); } h.deleteMin().decreaseKey(0); } @Test(expected = IllegalArgumentException.class) public void testDeleteEmpty() { AddressableHeap h = new IntegerRadixAddressableHeap(0, 200); Handle handle = h.insert(1); h.deleteMin(); handle.delete(); } @Test(expected = IllegalArgumentException.class) public void testDecreaseKeyEmpty() { AddressableHeap h = new IntegerRadixAddressableHeap(100, 200); Handle handle = h.insert(150); h.deleteMin(); handle.decreaseKey(120); } @Test(expected = IllegalArgumentException.class) public void testDecreaseKeyMore() { AddressableHeap h = new IntegerRadixAddressableHeap(100, 200); Handle handle = h.insert(150); handle.decreaseKey(160); } @Test public void testDecreaseKeySame() { AddressableHeap h = new IntegerRadixAddressableHeap(100, 200); Handle handle = h.insert(150); handle.decreaseKey(150); assertEquals(150, h.findMin().getKey().intValue()); } @Test @SuppressWarnings("unchecked") public void testDecreaseKey() { AddressableHeap h = new IntegerRadixAddressableHeap(0, 200); AddressableHeap.Handle array[]; array = new AddressableHeap.Handle[15]; h.insert(0); // monotone for (int i = 0; i < 15; i++) { array[i] = h.insert(i + 100); } array[5].decreaseKey(5); array[1].decreaseKey(50); array[10].decreaseKey(3); array[0].decreaseKey(1); array[5].delete(); array[2].delete(); array[11].delete(); array[9].delete(); assertEquals(Integer.valueOf(0), h.deleteMin().getKey()); assertEquals(Integer.valueOf(1), h.deleteMin().getKey()); assertEquals(Integer.valueOf(3), h.deleteMin().getKey()); assertEquals(Integer.valueOf(50), h.deleteMin().getKey()); assertEquals(Integer.valueOf(103), h.deleteMin().getKey()); assertEquals(Integer.valueOf(104), h.deleteMin().getKey()); array[14].decreaseKey(111); array[13].decreaseKey(109); assertEquals(Integer.valueOf(106), h.deleteMin().getKey()); assertEquals(Integer.valueOf(107), h.deleteMin().getKey()); assertEquals(Integer.valueOf(108), h.deleteMin().getKey()); assertEquals(Integer.valueOf(109), h.deleteMin().getKey()); assertEquals(Integer.valueOf(111), h.deleteMin().getKey()); assertEquals(Integer.valueOf(112), h.deleteMin().getKey()); } @SuppressWarnings("unchecked") @Test(expected = IllegalArgumentException.class) public void testIncreaseKey() { AddressableHeap h = new IntegerRadixAddressableHeap(0, 200); AddressableHeap.Handle array[]; array = new AddressableHeap.Handle[15]; for (int i = 0; i < 15; i++) { array[i] = h.insert(i + 100); } assertEquals(Integer.valueOf(100), h.findMin().getKey()); array[5].decreaseKey(5); assertEquals(Integer.valueOf(5), h.findMin().getKey()); array[1].decreaseKey(102); } @SuppressWarnings("unchecked") @Test public void testSerializable() throws IOException, ClassNotFoundException { AddressableHeap h = new IntegerRadixAddressableHeap(0, 15); for (int i = 0; i < 15; i++) { h.insert(i); } // write ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(h); oos.close(); byte[] data = baos.toByteArray(); // read ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data)); Object o = ois.readObject(); ois.close(); h = (AddressableHeap) o; for (int i = 0; i < 15; i++) { assertEquals(15 - i, h.size()); assertEquals(Integer.valueOf(i), h.findMin().getKey()); h.deleteMin(); } assertTrue(h.isEmpty()); } @Test(expected = IllegalArgumentException.class) public void testIllegalConstruction() { new IntegerRadixAddressableHeap(-1, 10); } @Test(expected = IllegalArgumentException.class) public void testIllegalConstruction1() { new IntegerRadixAddressableHeap(10, 9); } } jheaps-jheaps-0.14/src/test/java/org/jheaps/monotone/IntegerRadixHeapTest.java000066400000000000000000000206071367505655000274550ustar00rootroot00000000000000/* * (C) Copyright 2014-2018, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.monotone; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Arrays; import java.util.NoSuchElementException; import java.util.Random; import org.jheaps.Heap; import org.jheaps.monotone.IntegerRadixHeap; import org.junit.Test; public class IntegerRadixHeapTest { private static final int SIZE = 100000; @Test public void testBug2() { Heap h = new IntegerRadixHeap(0, 100); h.insert(0); assertEquals(0, h.findMin().intValue()); assertEquals(0, h.deleteMin().intValue()); h.insert(15); assertEquals(15, h.findMin().intValue()); } @Test public void test() { Heap h = new IntegerRadixHeap(0, SIZE); for (int i = 0; i < SIZE; i++) { h.insert(i); assertEquals(Integer.valueOf(0), h.findMin()); assertFalse(h.isEmpty()); assertEquals(h.size(), i + 1); } for (int i = SIZE - 1; i >= 0; i--) { assertEquals(h.findMin().intValue(), Integer.valueOf(SIZE - i - 1).intValue()); h.deleteMin(); } } @Test public void testVerySmall() { Heap h = new IntegerRadixHeap(29, 36); h.insert(29); h.insert(30); h.insert(31); h.insert(30); h.insert(33); h.insert(36); h.insert(35); assertEquals(h.size(), 7); assertEquals(h.findMin().intValue(), 29L); assertEquals(h.size(), 7); assertEquals(h.deleteMin().intValue(), 29L); assertEquals(h.size(), 6); assertEquals(h.findMin().intValue(), 30L); assertEquals(h.deleteMin().intValue(), 30L); assertEquals(h.findMin().intValue(), 30L); assertEquals(h.deleteMin().intValue(), 30L); assertEquals(h.findMin().intValue(), 31L); assertEquals(h.deleteMin().intValue(), 31L); assertEquals(h.findMin().intValue(), 33L); assertEquals(h.deleteMin().intValue(), 33L); assertEquals(h.findMin().intValue(), 35L); assertEquals(h.deleteMin().intValue(), 35L); assertEquals(h.findMin().intValue(), 36L); assertEquals(h.deleteMin().intValue(), 36L); assertEquals(h.size(), 0); assertTrue(h.isEmpty()); } @Test public void testSortRandomSeed1() { Heap h = new IntegerRadixHeap(0, SIZE + 1); Random generator = new Random(1); int[] a = new int[SIZE]; for (int i = 0; i < SIZE; i++) { a[i] = (int) (SIZE * generator.nextDouble()); } Arrays.sort(a); for (int i = 0; i < SIZE; i++) { h.insert(a[i]); } Integer prev = null, cur; while (!h.isEmpty()) { cur = h.findMin(); h.deleteMin(); if (prev != null) { assertTrue(prev.compareTo(cur) <= 0); } prev = cur; } } @Test public void testSort2RandomSeed1() { Heap h = new IntegerRadixHeap(0, SIZE + 1); Random generator = new Random(1); int[] a = new int[SIZE]; for (int i = 0; i < SIZE; i++) { a[i] = (int) (SIZE * generator.nextDouble()); } Arrays.sort(a); for (int i = 0; i < SIZE; i++) { h.insert(a[i]); } Integer prev = null, cur; while (!h.isEmpty()) { cur = h.deleteMin(); if (prev != null) { assertTrue(prev.compareTo(cur) <= 0); } prev = cur; } } @Test public void testSortRandomSeed2() { Heap h = new IntegerRadixHeap(0, SIZE + 1); Random generator = new Random(2); int[] a = new int[SIZE]; for (int i = 0; i < SIZE; i++) { a[i] = (int) (SIZE * generator.nextDouble()); } Arrays.sort(a); for (int i = 0; i < SIZE; i++) { h.insert(a[i]); } Integer prev = null, cur; while (!h.isEmpty()) { cur = h.findMin(); h.deleteMin(); if (prev != null) { assertTrue(prev.compareTo(cur) <= 0); } prev = cur; } } @Test public void testSort2RandomSeed2() { Heap h = new IntegerRadixHeap(0, SIZE + 1); Random generator = new Random(2); int[] a = new int[SIZE]; for (int i = 0; i < SIZE; i++) { a[i] = (int) (SIZE * generator.nextDouble()); } Arrays.sort(a); for (int i = 0; i < SIZE; i++) { h.insert(a[i]); } Integer prev = null, cur; while (!h.isEmpty()) { cur = h.deleteMin(); if (prev != null) { assertTrue(prev.compareTo(cur) <= 0); } prev = cur; } } @Test public void testSameMinMax() { Heap h = new IntegerRadixHeap(100, 100); for (int i = 0; i < 15; i++) { h.insert(100); } assertEquals(15, h.size()); for (int i = 0; i < 15; i++) { assertEquals(100L, h.deleteMin().intValue()); } assertEquals(0, h.size()); } @Test public void testMultipleDeleteMin() { final int step = 7; final int min = 0; final int max = 100000; Heap h = new IntegerRadixHeap(min, max); h.insert(min); int cur = min; while(cur < max) { assertEquals(cur, h.findMin(), 1e-9); if (cur + step >= max) { break; } int newCur = cur + step; h.insert(newCur); assertEquals(cur, h.findMin(), 1e-9); assertEquals(cur, h.deleteMin(), 1e-9); cur = newCur; } } @Test public void testMaxDifference() { Heap h = new IntegerRadixHeap(0, Integer.MAX_VALUE); h.insert(0); h.insert(Integer.MAX_VALUE); assertEquals(2, h.size()); assertEquals(0, h.deleteMin().intValue()); assertEquals(Integer.MAX_VALUE, h.deleteMin().intValue()); assertEquals(0, h.size()); } @Test public void testClear() { Heap h = new IntegerRadixHeap(0, 15); for (int i = 0; i < 15; i++) { h.insert(i); } h.clear(); assertEquals(0L, h.size()); assertTrue(h.isEmpty()); } @Test public void testMonotoneOkOnLastDeleted() { Heap h = new IntegerRadixHeap(0, 1000); h.insert(100); assertEquals(100, h.findMin().intValue()); h.insert(99); assertEquals(99, h.findMin().intValue()); } @Test(expected = IllegalArgumentException.class) public void testMonotoneNotOkOnLastDeleted() { Heap h = new IntegerRadixHeap(0, 1000); h.insert(100); assertEquals(100, h.findMin().intValue()); h.deleteMin(); h.insert(99); } @Test(expected = IllegalArgumentException.class) public void testIllegalConstruction() { new IntegerRadixHeap(-1, 100); } @Test(expected = IllegalArgumentException.class) public void testIllegalConstruction1() { new IntegerRadixHeap(100, 99); } @Test @SuppressWarnings("unchecked") public void testSerializable() throws IOException, ClassNotFoundException { Heap h = new IntegerRadixHeap(0, 15); for (int i = 0; i < 15; i++) { h.insert(i); } // write ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(h); oos.close(); byte[] data = baos.toByteArray(); // read ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data)); Object o = ois.readObject(); ois.close(); h = (Heap) o; for (int i = 0; i < 15; i++) { assertEquals(15 - i, h.size()); assertEquals(Integer.valueOf(i), h.findMin()); h.deleteMin(); } assertTrue(h.isEmpty()); } @Test(expected = IllegalArgumentException.class) public void testBadInsert() { new IntegerRadixHeap(0, 15).insert(null); } @Test(expected = IllegalArgumentException.class) public void testBadInsert1() { new IntegerRadixHeap(10, 15).insert(9); } @Test(expected = IllegalArgumentException.class) public void testBadInsert2() { new IntegerRadixHeap(10, 15).insert(16); } @Test(expected = NoSuchElementException.class) public void testBadDeleteMin() { new IntegerRadixHeap(10, 15).deleteMin(); } @Test(expected = NoSuchElementException.class) public void testBadFindMin() { new IntegerRadixHeap(10, 15).findMin(); } } jheaps-jheaps-0.14/src/test/java/org/jheaps/monotone/LongRadixAddressableHeapTest.java000066400000000000000000000327721367505655000311170ustar00rootroot00000000000000/* * (C) Copyright 2014-2018, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.monotone; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.NoSuchElementException; import java.util.Random; import org.jheaps.AddressableHeap; import org.junit.Test; public class LongRadixAddressableHeapTest { private static final int SIZE = 100000; @Test public void testBug2() { AddressableHeap h = new LongRadixAddressableHeap(0L, 100L); h.insert(0L); assertEquals(0L, h.findMin().getKey().longValue()); assertEquals(0L, h.deleteMin().getKey().longValue()); h.insert(15L); assertEquals(15L, h.findMin().getKey().longValue()); } @Test public void testVerySmall() { AddressableHeap h = new LongRadixAddressableHeap(15, 100); h.insert(15L); h.insert(50L); h.insert(21L); h.insert(51L); h.insert(30L); h.insert(25L); h.insert(18L); assertEquals(7, h.size()); assertEquals(15L, h.findMin().getKey().longValue()); assertEquals(7, h.size()); assertEquals(15L, h.deleteMin().getKey().longValue()); assertEquals(6, h.size()); assertEquals(18L, h.findMin().getKey().longValue()); assertEquals(18L, h.deleteMin().getKey().longValue()); assertEquals(21L, h.findMin().getKey().longValue()); assertEquals(21L, h.deleteMin().getKey().longValue()); assertEquals(25L, h.findMin().getKey().longValue()); assertEquals(25L, h.deleteMin().getKey().longValue()); assertEquals(30L, h.findMin().getKey().longValue()); assertEquals(30L, h.deleteMin().getKey().longValue()); assertEquals(50L, h.findMin().getKey().longValue()); assertEquals(50L, h.deleteMin().getKey().longValue()); assertEquals(51L, h.findMin().getKey().longValue()); assertEquals(51L, h.deleteMin().getKey().longValue()); assertEquals(h.size(), 0); assertTrue(h.isEmpty()); } @Test public void test() { AddressableHeap h = new LongRadixAddressableHeap(0, SIZE); for (long i = 0; i < SIZE; i++) { h.insert(i); assertEquals(0L, h.findMin().getKey(), 1e-9); assertFalse(h.isEmpty()); assertEquals(h.size(), i + 1); } for (int i = SIZE - 1; i >= 0; i--) { assertEquals((long) (SIZE - i - 1), h.findMin().getKey(), 1e-9); h.deleteMin(); } } @Test public void testSortRandomSeed1() { AddressableHeap h = new LongRadixAddressableHeap(0, Long.MAX_VALUE); Random generator = new Random(1); h.insert(0L); for (int i = 1; i < SIZE; i++) { long d = Math.abs(generator.nextLong()) + 1; h.insert(d); } Long prev = null, cur; while (!h.isEmpty()) { cur = h.deleteMin().getKey(); if (prev != null) { assertTrue(prev.compareTo(cur) <= 0); } prev = cur; } } @Test public void testSortRandomSeed2() { AddressableHeap h = new LongRadixAddressableHeap(0, Long.MAX_VALUE); Random generator = new Random(2); h.insert(0L); for (int i = 1; i < SIZE; i++) { long d = Math.abs(generator.nextLong()) + 1; h.insert(d); } Long prev = null, cur; while (!h.isEmpty()) { cur = h.deleteMin().getKey(); if (prev != null) { assertTrue(prev.compareTo(cur) <= 0); } prev = cur; } } @Test public void testSameMinMax() { AddressableHeap h = new LongRadixAddressableHeap(1L, 1L); for (int i = 0; i < 15; i++) { h.insert(1L); } assertEquals(15, h.size()); for (int i = 0; i < 15; i++) { assertEquals(1L, h.deleteMin().getKey().longValue()); } assertEquals(0, h.size()); } @Test public void testMaxDifference() { AddressableHeap h = new LongRadixAddressableHeap(0L, Long.MAX_VALUE); h.insert(0L); h.insert(Long.MAX_VALUE); assertEquals(2, h.size()); assertEquals(0L, h.deleteMin().getKey().longValue()); assertEquals(Long.MAX_VALUE, h.deleteMin().getKey().longValue()); assertEquals(0, h.size()); } @Test @SuppressWarnings("unchecked") public void testDelete() { AddressableHeap h = new LongRadixAddressableHeap(0L, 15L); AddressableHeap.Handle array[]; array = new AddressableHeap.Handle[15]; for (int i = 0; i < 15; i++) { array[i] = h.insert((long) i); } array[5].delete(); assertEquals(Long.valueOf(0), h.findMin().getKey()); array[7].delete(); assertEquals(Long.valueOf(0), h.findMin().getKey()); array[0].delete(); assertEquals(Long.valueOf(1), h.findMin().getKey()); array[2].delete(); assertEquals(Long.valueOf(1), h.findMin().getKey()); array[1].delete(); assertEquals(Long.valueOf(3), h.findMin().getKey()); array[3].delete(); assertEquals(Long.valueOf(4), h.findMin().getKey()); array[9].delete(); assertEquals(Long.valueOf(4), h.findMin().getKey()); array[4].delete(); assertEquals(Long.valueOf(6), h.findMin().getKey()); array[8].delete(); assertEquals(Long.valueOf(6), h.findMin().getKey()); array[11].delete(); assertEquals(Long.valueOf(6), h.findMin().getKey()); array[6].delete(); assertEquals(Long.valueOf(10), h.findMin().getKey()); array[12].delete(); assertEquals(Long.valueOf(10), h.findMin().getKey()); array[10].delete(); assertEquals(Long.valueOf(13), h.findMin().getKey()); array[13].delete(); assertEquals(Long.valueOf(14), h.findMin().getKey()); array[14].delete(); assertTrue(h.isEmpty()); } @Test @SuppressWarnings("unchecked") public void testDelete1() { AddressableHeap h = new LongRadixAddressableHeap(0L, 10L); AddressableHeap.Handle array[]; array = new AddressableHeap.Handle[8]; for (int i = 0; i < 8; i++) { array[i] = h.insert((long) i); } array[5].delete(); assertEquals(0L, h.findMin().getKey(), 1e-9); array[7].delete(); assertEquals(0L, h.findMin().getKey(), 1e-9); array[0].delete(); assertEquals(1L, h.findMin().getKey(), 1e-9); array[2].delete(); assertEquals(1L, h.findMin().getKey(), 1e-9); array[1].delete(); assertEquals(3L, h.findMin().getKey(), 1e-9); } @Test @SuppressWarnings("unchecked") public void testAddDelete() { AddressableHeap h = new LongRadixAddressableHeap(0, (long) SIZE); AddressableHeap.Handle array[]; array = new AddressableHeap.Handle[SIZE]; for (int i = 0; i < SIZE; i++) { array[i] = h.insert((long) i); } for (int i = SIZE - 1; i >= 0; i--) { array[i].delete(); if (i > 0) { assertEquals(Long.valueOf(0), h.findMin().getKey()); } } assertTrue(h.isEmpty()); } @Test public void testClear() { AddressableHeap h = new LongRadixAddressableHeap(0, 15); for (int i = 0; i < 15; i++) { h.insert((long) i); } h.clear(); assertEquals(0L, h.size()); assertTrue(h.isEmpty()); } @SuppressWarnings("unchecked") @Test(expected = IllegalArgumentException.class) public void testDeleteTwice() { AddressableHeap h = new LongRadixAddressableHeap(0, 15); AddressableHeap.Handle array[]; array = new AddressableHeap.Handle[15]; for (int i = 0; i < 15; i++) { array[i] = h.insert((long) i); } array[5].delete(); assertEquals(Long.valueOf(0), h.findMin().getKey()); array[7].delete(); assertEquals(Long.valueOf(0), h.findMin().getKey()); array[0].delete(); assertEquals(Long.valueOf(1), h.findMin().getKey()); array[2].delete(); assertEquals(Long.valueOf(1), h.findMin().getKey()); array[1].delete(); assertEquals(Long.valueOf(3), h.findMin().getKey()); array[3].delete(); assertEquals(Long.valueOf(4), h.findMin().getKey()); array[9].delete(); assertEquals(Long.valueOf(4), h.findMin().getKey()); array[4].delete(); assertEquals(Long.valueOf(6), h.findMin().getKey()); // again array[2].delete(); } @Test public void testDeleteMinUpdate() { AddressableHeap h = new LongRadixAddressableHeap(0, Long.MAX_VALUE); h.insert(0L); h.insert(0L); h.insert(Long.MAX_VALUE); h.insert(Long.MAX_VALUE); h.insert(Long.MAX_VALUE); h.insert(Long.MAX_VALUE); h.deleteMin(); h.deleteMin(); assertEquals(Long.MAX_VALUE, h.findMin().getKey().longValue()); } @Test(expected = IllegalArgumentException.class) public void testDeleteMinDeleteTwice() { AddressableHeap h = new LongRadixAddressableHeap(0, 100); AddressableHeap.Handle e1 = h.insert(50L); h.insert(100L); h.deleteMin(); e1.delete(); } @Test(expected = IllegalArgumentException.class) public void testDeleteMinDeleteTwice1() { AddressableHeap h = new LongRadixAddressableHeap(99, 200); for (int i = 100; i < 200; i++) { h.insert((long) i); } h.deleteMin().delete(); } @Test(expected = IllegalArgumentException.class) public void testDeleteMinDecreaseKey() { AddressableHeap h = new LongRadixAddressableHeap(100, 200); for (int i = 100; i < 200; i++) { h.insert((long) i); } h.deleteMin().decreaseKey(0L); } @Test(expected = IllegalArgumentException.class) public void testBadInsert() { AddressableHeap h = new LongRadixAddressableHeap(0, 100); h.insert(null); } @Test(expected = IllegalArgumentException.class) public void testBadInsert1() { AddressableHeap h = new LongRadixAddressableHeap(0, 100); h.insert(200L); } @Test(expected = NoSuchElementException.class) public void testBadDeleteMin() { AddressableHeap h = new LongRadixAddressableHeap(0, 100); h.deleteMin(); } @Test(expected = NoSuchElementException.class) public void testBadFindMin() { AddressableHeap h = new LongRadixAddressableHeap(0, 100); h.findMin(); } @Test public void testComparator() { AddressableHeap h = new LongRadixAddressableHeap(0, 100); assertNull(h.comparator()); } @Test @SuppressWarnings("unchecked") public void testDecreaseKey() { AddressableHeap h = new LongRadixAddressableHeap(0, 200); AddressableHeap.Handle array[]; array = new AddressableHeap.Handle[15]; h.insert(0L); // monotone for (int i = 0; i < 15; i++) { array[i] = h.insert((long) i + 100); } array[5].decreaseKey(5L); array[1].decreaseKey(50L); array[10].decreaseKey(3L); array[0].decreaseKey(1L); array[5].delete(); array[2].delete(); array[11].delete(); array[9].delete(); assertEquals(Long.valueOf(0), h.deleteMin().getKey()); assertEquals(Long.valueOf(1), h.deleteMin().getKey()); assertEquals(Long.valueOf(3), h.deleteMin().getKey()); assertEquals(Long.valueOf(50), h.deleteMin().getKey()); assertEquals(Long.valueOf(103), h.deleteMin().getKey()); assertEquals(Long.valueOf(104), h.deleteMin().getKey()); array[14].decreaseKey(111L); array[13].decreaseKey(109L); assertEquals(Long.valueOf(106), h.deleteMin().getKey()); assertEquals(Long.valueOf(107), h.deleteMin().getKey()); assertEquals(Long.valueOf(108), h.deleteMin().getKey()); assertEquals(Long.valueOf(109), h.deleteMin().getKey()); assertEquals(Long.valueOf(111), h.deleteMin().getKey()); assertEquals(Long.valueOf(112), h.deleteMin().getKey()); } @SuppressWarnings("unchecked") @Test(expected = IllegalArgumentException.class) public void testIncreaseKey() { AddressableHeap h = new LongRadixAddressableHeap(0, 200); AddressableHeap.Handle array[]; array = new AddressableHeap.Handle[15]; for (int i = 0; i < 15; i++) { array[i] = h.insert((long) i + 100); } assertEquals(Long.valueOf(100), h.findMin().getKey()); array[5].decreaseKey(5L); assertEquals(Long.valueOf(5), h.findMin().getKey()); array[1].decreaseKey(102L); } @SuppressWarnings("unchecked") @Test public void testSerializable() throws IOException, ClassNotFoundException { AddressableHeap h = new LongRadixAddressableHeap(0, 15); for (int i = 0; i < 15; i++) { h.insert((long) i); } // write ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(h); oos.close(); byte[] data = baos.toByteArray(); // read ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data)); Object o = ois.readObject(); ois.close(); h = (AddressableHeap) o; for (int i = 0; i < 15; i++) { assertEquals(15 - i, h.size()); assertEquals(Long.valueOf(i), h.findMin().getKey()); h.deleteMin(); } assertTrue(h.isEmpty()); } @Test public void testGetValue() { AddressableHeap h = new LongRadixAddressableHeap(0, 0); assertEquals("hello", h.insert(0L, "hello").getValue()); } @Test(expected = IllegalArgumentException.class) public void testIllegalConstruction() { new LongRadixAddressableHeap(-1, 10); } @Test(expected = IllegalArgumentException.class) public void testIllegalConstruction1() { new LongRadixAddressableHeap(10, 9); } } jheaps-jheaps-0.14/src/test/java/org/jheaps/monotone/LongRadixHeapTest.java000066400000000000000000000206521367505655000267570ustar00rootroot00000000000000/* * (C) Copyright 2014-2018, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.monotone; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Arrays; import java.util.NoSuchElementException; import java.util.Random; import org.jheaps.Heap; import org.jheaps.monotone.LongRadixHeap; import org.junit.Test; public class LongRadixHeapTest { private static final int SIZE = 100000; @Test public void testBug2() { Heap h = new LongRadixHeap(0L, 100L); h.insert(0L); assertEquals(0L, h.findMin().longValue()); assertEquals(0L, h.deleteMin().longValue()); h.insert(15L); assertEquals(15L, h.findMin().longValue()); } @Test public void test() { Heap h = new LongRadixHeap(0, SIZE); for (long i = 0; i < SIZE; i++) { h.insert(i); assertEquals(Long.valueOf(0), h.findMin()); assertFalse(h.isEmpty()); assertEquals(h.size(), i + 1); } for (int i = SIZE - 1; i >= 0; i--) { assertEquals(h.findMin().longValue(), Long.valueOf(SIZE - i - 1).longValue()); h.deleteMin(); } } @Test public void testVerySmall() { Heap h = new LongRadixHeap(29, 36); h.insert(29L); h.insert(30L); h.insert(31L); h.insert(30L); h.insert(33L); h.insert(36L); h.insert(35L); assertEquals(h.size(), 7); assertEquals(h.findMin().longValue(), 29L); assertEquals(h.size(), 7); assertEquals(h.deleteMin().longValue(), 29L); assertEquals(h.size(), 6); assertEquals(h.findMin().longValue(), 30L); assertEquals(h.deleteMin().longValue(), 30L); assertEquals(h.findMin().longValue(), 30L); assertEquals(h.deleteMin().longValue(), 30L); assertEquals(h.findMin().longValue(), 31L); assertEquals(h.deleteMin().longValue(), 31L); assertEquals(h.findMin().longValue(), 33L); assertEquals(h.deleteMin().longValue(), 33L); assertEquals(h.findMin().longValue(), 35L); assertEquals(h.deleteMin().longValue(), 35L); assertEquals(h.findMin().longValue(), 36L); assertEquals(h.deleteMin().longValue(), 36L); assertEquals(h.size(), 0); assertTrue(h.isEmpty()); } @Test public void testSortRandomSeed1() { Heap h = new LongRadixHeap(0, SIZE + 1); Random generator = new Random(1); long[] a = new long[SIZE]; for (int i = 0; i < SIZE; i++) { a[i] = (long) (SIZE * generator.nextDouble()); } Arrays.sort(a); for (int i = 0; i < SIZE; i++) { h.insert(a[i]); } Long prev = null, cur; while (!h.isEmpty()) { cur = h.findMin(); h.deleteMin(); if (prev != null) { assertTrue(prev.compareTo(cur) <= 0); } prev = cur; } } @Test public void testSort2RandomSeed1() { Heap h = new LongRadixHeap(0, SIZE + 1); Random generator = new Random(1); long[] a = new long[SIZE]; for (int i = 0; i < SIZE; i++) { a[i] = (long) (SIZE * generator.nextDouble()); } Arrays.sort(a); for (int i = 0; i < SIZE; i++) { h.insert(a[i]); } Long prev = null, cur; while (!h.isEmpty()) { cur = h.deleteMin(); if (prev != null) { assertTrue(prev.compareTo(cur) <= 0); } prev = cur; } } @Test public void testSortRandomSeed2() { Heap h = new LongRadixHeap(0, SIZE + 1); Random generator = new Random(2); long[] a = new long[SIZE]; for (int i = 0; i < SIZE; i++) { a[i] = (long) (SIZE * generator.nextDouble()); } Arrays.sort(a); for (int i = 0; i < SIZE; i++) { h.insert(a[i]); } Long prev = null, cur; while (!h.isEmpty()) { cur = h.findMin(); h.deleteMin(); if (prev != null) { assertTrue(prev.compareTo(cur) <= 0); } prev = cur; } } @Test public void testSort2RandomSeed2() { Heap h = new LongRadixHeap(0, SIZE + 1); Random generator = new Random(2); long[] a = new long[SIZE]; for (int i = 0; i < SIZE; i++) { a[i] = (long) (SIZE * generator.nextDouble()); } Arrays.sort(a); for (int i = 0; i < SIZE; i++) { h.insert(a[i]); } Long prev = null, cur; while (!h.isEmpty()) { cur = h.deleteMin(); if (prev != null) { assertTrue(prev.compareTo(cur) <= 0); } prev = cur; } } @Test public void testSameMinMax() { Heap h = new LongRadixHeap(100, 100); for (int i = 0; i < 15; i++) { h.insert(100L); } assertEquals(15, h.size()); for (int i = 0; i < 15; i++) { assertEquals(100L, h.deleteMin().longValue()); } assertEquals(0, h.size()); } @Test public void testMultipleDeleteMin() { final long step = 7; final long min = 0L; final long max = 100000L; Heap h = new LongRadixHeap(min, max); h.insert(min); long cur = min; while(cur < max) { assertEquals(cur, h.findMin(), 1e-9); if (cur + step >= max) { break; } long newCur = cur + step; h.insert(newCur); assertEquals(cur, h.findMin(), 1e-9); assertEquals(cur, h.deleteMin(), 1e-9); cur = newCur; } } @Test public void testMaxDifference() { Heap h = new LongRadixHeap(0, Long.MAX_VALUE); h.insert(0L); h.insert(Long.MAX_VALUE); assertEquals(2, h.size()); assertEquals(0L, h.deleteMin().longValue()); assertEquals(Long.MAX_VALUE, h.deleteMin().longValue()); assertEquals(0, h.size()); } @Test public void testClear() { Heap h = new LongRadixHeap(0, 15); for (long i = 0; i < 15; i++) { h.insert(i); } h.clear(); assertEquals(0L, h.size()); assertTrue(h.isEmpty()); } @Test public void testComparator() { Heap h = new LongRadixHeap(0, 15); assertNull(h.comparator()); } @Test public void testMonotoneOkOnLastDeleted() { Heap h = new LongRadixHeap(0, 1000); h.insert(100L); assertEquals(100L, h.findMin().longValue()); h.insert(99L); assertEquals(99L, h.findMin().longValue()); } @Test(expected = IllegalArgumentException.class) public void testMonotoneNotOkOnLastDeleted() { Heap h = new LongRadixHeap(0, 1000); h.insert(100L); h.deleteMin(); h.insert(99L); } @Test(expected = IllegalArgumentException.class) public void testIllegalConstruction() { new LongRadixHeap(-1, 100); } @Test(expected = IllegalArgumentException.class) public void testIllegalConstruction1() { new LongRadixHeap(100, 99); } @Test(expected = IllegalArgumentException.class) public void testBadInsert() { new LongRadixHeap(0, 15).insert(null); } @Test(expected = IllegalArgumentException.class) public void testBadInsert1() { new LongRadixHeap(10, 15).insert(9L); } @Test(expected = IllegalArgumentException.class) public void testBadInsert2() { new LongRadixHeap(10, 15).insert(16L); } @Test(expected = NoSuchElementException.class) public void testBadDeleteMin() { new LongRadixHeap(10, 15).deleteMin(); } @Test(expected = NoSuchElementException.class) public void testBadFindMin() { new LongRadixHeap(10, 15).findMin(); } @Test @SuppressWarnings("unchecked") public void testSerializable() throws IOException, ClassNotFoundException { Heap h = new LongRadixHeap(0, 15); for (long i = 0; i < 15; i++) { h.insert(i); } // write ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(h); oos.close(); byte[] data = baos.toByteArray(); // read ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data)); Object o = ois.readObject(); ois.close(); h = (Heap) o; for (int i = 0; i < 15; i++) { assertEquals(15 - i, h.size()); assertEquals(Long.valueOf(i), h.findMin()); h.deleteMin(); } assertTrue(h.isEmpty()); } } jheaps-jheaps-0.14/src/test/java/org/jheaps/monotone/UnsignedUtilsTest.java000066400000000000000000000023561367505655000270700ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.monotone; import static org.junit.Assert.assertEquals; import java.math.BigInteger; import org.junit.Test; public class UnsignedUtilsTest { @Test public void test() { new UnsignedUtils(); assertEquals(BigInteger.ONE, UnsignedUtils.unsignedLongToBigInt(1L)); assertEquals(BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.ONE).multiply(BigInteger.valueOf(2)) .subtract(BigInteger.ONE), UnsignedUtils.unsignedLongToBigInt(-1L)); assertEquals(1.0, UnsignedUtils.unsignedLongToDouble(1L), 1e-9); assertEquals(1.8446744073709552E19, UnsignedUtils.unsignedLongToDouble(-1L), 1e-9); } } jheaps-jheaps-0.14/src/test/java/org/jheaps/tree/000077500000000000000000000000001367505655000216635ustar00rootroot00000000000000jheaps-jheaps-0.14/src/test/java/org/jheaps/tree/AbstractAddressableHeapTest.java000066400000000000000000000553531367505655000300740ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.tree; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Comparator; import java.util.NoSuchElementException; import java.util.Random; import org.jheaps.AddressableHeap; import org.jheaps.AddressableHeap.Handle; import org.junit.BeforeClass; import org.junit.Test; public abstract class AbstractAddressableHeapTest { protected static final int SIZE = 100000; protected static Comparator comparator; private static class TestComparator implements Comparator, Serializable { private static final long serialVersionUID = 1L; @Override public int compare(Integer o1, Integer o2) { if (o1 < o2) { return 1; } else if (o1 > o2) { return -1; } else { return 0; } } } @BeforeClass public static void setUpClass() { comparator = new TestComparator(); } protected abstract AddressableHeap createHeap(); protected abstract AddressableHeap createHeap(Comparator comparator); protected abstract AddressableHeap createHeapWithStringValues(); @Test public void test() { AddressableHeap h = createHeap(); for (int i = 0; i < SIZE; i++) { h.insert(i); assertEquals(Integer.valueOf(0), h.findMin().getKey()); assertFalse(h.isEmpty()); assertEquals(h.size(), i + 1); } for (int i = SIZE - 1; i >= 0; i--) { assertEquals(h.findMin().getKey(), Integer.valueOf(SIZE - i - 1)); h.deleteMin(); } } @Test public void testOnlyInsert() { AddressableHeap h = createHeap(); for (int i = 0; i < SIZE; i++) { h.insert(SIZE - i); assertEquals(Integer.valueOf(SIZE - i), h.findMin().getKey()); assertFalse(h.isEmpty()); assertEquals(h.size(), i + 1); } } @Test public void testSetValue() { AddressableHeap h = createHeapWithStringValues(); h.insert(1, "value1").setValue("value2"); assertEquals("value2", h.findMin().getValue()); } @Test public void testOnly4() { AddressableHeap h = createHeap(); assertTrue(h.isEmpty()); h.insert(780); assertEquals(h.size(), 1); assertEquals(Integer.valueOf(780), h.findMin().getKey()); h.insert(-389); assertEquals(h.size(), 2); assertEquals(Integer.valueOf(-389), h.findMin().getKey()); h.insert(306); assertEquals(h.size(), 3); assertEquals(Integer.valueOf(-389), h.findMin().getKey()); h.insert(579); assertEquals(h.size(), 4); assertEquals(Integer.valueOf(-389), h.findMin().getKey()); h.deleteMin(); assertEquals(h.size(), 3); assertEquals(Integer.valueOf(306), h.findMin().getKey()); h.deleteMin(); assertEquals(h.size(), 2); assertEquals(Integer.valueOf(579), h.findMin().getKey()); h.deleteMin(); assertEquals(h.size(), 1); assertEquals(Integer.valueOf(780), h.findMin().getKey()); h.deleteMin(); assertEquals(h.size(), 0); assertTrue(h.isEmpty()); } @Test public void testOnly4Reverse() { AddressableHeap h = createHeap(comparator); assertTrue(h.isEmpty()); h.insert(780); assertEquals(h.size(), 1); assertEquals(Integer.valueOf(780), h.findMin().getKey()); h.insert(-389); assertEquals(h.size(), 2); assertEquals(Integer.valueOf(780), h.findMin().getKey()); h.insert(306); assertEquals(h.size(), 3); assertEquals(Integer.valueOf(780), h.findMin().getKey()); h.insert(579); assertEquals(h.size(), 4); assertEquals(Integer.valueOf(780), h.findMin().getKey()); h.deleteMin(); assertEquals(h.size(), 3); assertEquals(Integer.valueOf(579), h.findMin().getKey()); h.deleteMin(); assertEquals(h.size(), 2); assertEquals(Integer.valueOf(306), h.findMin().getKey()); h.deleteMin(); assertEquals(h.size(), 1); assertEquals(Integer.valueOf(-389), h.findMin().getKey()); h.deleteMin(); assertEquals(h.size(), 0); assertTrue(h.isEmpty()); } @Test public void testSortRandomSeed1() { AddressableHeap h = createHeap(); Random generator = new Random(1); for (int i = 0; i < SIZE; i++) { h.insert(generator.nextInt()); } Integer prev = null, cur; while (!h.isEmpty()) { cur = h.findMin().getKey(); h.deleteMin(); if (prev != null) { assertTrue(prev.compareTo(cur) <= 0); } prev = cur; } } @Test public void testSort1RandomSeed1() { AddressableHeap h = createHeap(); Random generator = new Random(1); for (int i = 0; i < SIZE; i++) { h.insert(generator.nextInt()); } Integer prev = null, cur; while (!h.isEmpty()) { cur = h.deleteMin().getKey(); if (prev != null) { assertTrue(prev.compareTo(cur) <= 0); } prev = cur; } } @Test public void testSortRandomSeed2() { AddressableHeap h = createHeap(); Random generator = new Random(2); for (int i = 0; i < SIZE; i++) { h.insert(generator.nextInt()); } Integer prev = null, cur; while (!h.isEmpty()) { cur = h.findMin().getKey(); h.deleteMin(); if (prev != null) { assertTrue(prev.compareTo(cur) <= 0); } prev = cur; } } @Test public void testSort2RandomSeed2() { AddressableHeap h = createHeap(); Random generator = new Random(2); for (int i = 0; i < SIZE; i++) { h.insert(generator.nextInt()); } Integer prev = null, cur; while (!h.isEmpty()) { cur = h.deleteMin().getKey(); if (prev != null) { assertTrue(prev.compareTo(cur) <= 0); } prev = cur; } } @Test public void testFindMinDeleteMinSameObject() { AddressableHeap h = createHeap(); Random generator = new Random(1); for (int i = 0; i < SIZE; i++) { h.insert(generator.nextInt()); } while (!h.isEmpty()) { assertEquals(h.findMin(), h.deleteMin()); } } @Test @SuppressWarnings("unchecked") public void testDelete() { AddressableHeap h = createHeap(); AddressableHeap.Handle array[]; array = new AddressableHeap.Handle[15]; for (int i = 0; i < 15; i++) { array[i] = h.insert(i); } array[5].delete(); assertEquals(Integer.valueOf(0), h.findMin().getKey()); array[7].delete(); assertEquals(Integer.valueOf(0), h.findMin().getKey()); array[0].delete(); assertEquals(Integer.valueOf(1), h.findMin().getKey()); array[2].delete(); assertEquals(Integer.valueOf(1), h.findMin().getKey()); array[1].delete(); assertEquals(Integer.valueOf(3), h.findMin().getKey()); array[3].delete(); assertEquals(Integer.valueOf(4), h.findMin().getKey()); array[9].delete(); assertEquals(Integer.valueOf(4), h.findMin().getKey()); array[4].delete(); assertEquals(Integer.valueOf(6), h.findMin().getKey()); array[8].delete(); assertEquals(Integer.valueOf(6), h.findMin().getKey()); array[11].delete(); assertEquals(Integer.valueOf(6), h.findMin().getKey()); array[6].delete(); assertEquals(Integer.valueOf(10), h.findMin().getKey()); array[12].delete(); assertEquals(Integer.valueOf(10), h.findMin().getKey()); array[10].delete(); assertEquals(Integer.valueOf(13), h.findMin().getKey()); array[13].delete(); assertEquals(Integer.valueOf(14), h.findMin().getKey()); array[14].delete(); assertTrue(h.isEmpty()); } @Test @SuppressWarnings("unchecked") public void testDelete1() { AddressableHeap h = createHeap(); AddressableHeap.Handle array[]; array = new AddressableHeap.Handle[8]; for (int i = 0; i < 8; i++) { array[i] = h.insert(i); } array[5].delete(); assertEquals(Integer.valueOf(0), h.findMin().getKey()); array[7].delete(); assertEquals(Integer.valueOf(0), h.findMin().getKey()); array[0].delete(); assertEquals(Integer.valueOf(1), h.findMin().getKey()); array[2].delete(); assertEquals(Integer.valueOf(1), h.findMin().getKey()); array[1].delete(); assertEquals(Integer.valueOf(3), h.findMin().getKey()); } @Test @SuppressWarnings("unchecked") public void testAddDelete() { AddressableHeap h = createHeap(); AddressableHeap.Handle array[]; array = new AddressableHeap.Handle[SIZE]; for (int i = 0; i < SIZE; i++) { array[i] = h.insert(i); } for (int i = SIZE - 1; i >= 0; i--) { array[i].delete(); if (i > 0) { assertEquals(Integer.valueOf(0), h.findMin().getKey()); } } assertTrue(h.isEmpty()); } @Test @SuppressWarnings("unchecked") public void testAddDeleteComparator() { AddressableHeap h = createHeap(comparator); AddressableHeap.Handle array[]; array = new AddressableHeap.Handle[SIZE]; for (int i = 0; i < SIZE; i++) { array[i] = h.insert(i); } for (int i = 0; i < SIZE; i++) { array[i].delete(); if (i < SIZE - 1) { assertEquals(Integer.valueOf(SIZE - 1), h.findMin().getKey()); } } assertTrue(h.isEmpty()); } @Test @SuppressWarnings("unchecked") public void testAddDecreaseKeyDeleteMin() { AddressableHeap h = createHeap(); AddressableHeap.Handle array[]; array = new AddressableHeap.Handle[SIZE]; for (int i = 0; i < SIZE; i++) { array[i] = h.insert(i); } for (int i = SIZE / 2; i < SIZE / 2 + 10; i++) { array[i].decreaseKey(i / 2); } array[0].delete(); for (int i = SIZE / 2 + 10; i < SIZE / 2 + 20; i++) { array[i].decreaseKey(0); } assertEquals(0, h.deleteMin().getKey().intValue()); } @Test @SuppressWarnings("unchecked") public void testAddDecreaseKeyDeleteMinComparator() { AddressableHeap h = createHeap(comparator); AddressableHeap.Handle array[]; array = new AddressableHeap.Handle[SIZE]; for (int i = 0; i < SIZE; i++) { array[i] = h.insert(i); } for (int i = SIZE / 2; i < SIZE / 2 + 10; i++) { array[i].decreaseKey(SIZE - 1); } array[SIZE - 1].delete(); for (int i = SIZE / 2 + 10; i < SIZE / 2 + 20; i++) { array[i].decreaseKey(SIZE - 1); } assertEquals(SIZE - 1, h.deleteMin().getKey().intValue()); } @Test public void testClear() { AddressableHeap h = createHeap(); for (int i = 0; i < 15; i++) { h.insert(i); } h.clear(); assertEquals(0L, h.size()); assertTrue(h.isEmpty()); } @SuppressWarnings("unchecked") @Test(expected = IllegalArgumentException.class) public void testDeleteTwice() { AddressableHeap h = createHeap(); AddressableHeap.Handle array[]; array = new AddressableHeap.Handle[15]; for (int i = 0; i < 15; i++) { array[i] = h.insert(i); } array[5].delete(); assertEquals(Integer.valueOf(0), h.findMin().getKey()); array[7].delete(); assertEquals(Integer.valueOf(0), h.findMin().getKey()); array[0].delete(); assertEquals(Integer.valueOf(1), h.findMin().getKey()); array[2].delete(); assertEquals(Integer.valueOf(1), h.findMin().getKey()); array[1].delete(); assertEquals(Integer.valueOf(3), h.findMin().getKey()); array[3].delete(); assertEquals(Integer.valueOf(4), h.findMin().getKey()); array[9].delete(); assertEquals(Integer.valueOf(4), h.findMin().getKey()); array[4].delete(); assertEquals(Integer.valueOf(6), h.findMin().getKey()); // again array[2].delete(); } @Test(expected = IllegalArgumentException.class) public void testDeleteMinDeleteTwice() { AddressableHeap h = createHeap(); AddressableHeap.Handle e1 = h.insert(50); h.insert(100); h.deleteMin(); e1.delete(); } @Test(expected = IllegalArgumentException.class) public void testDeleteMinDeleteTwice1() { AddressableHeap h = createHeap(); for (int i = 100; i < 200; i++) { h.insert(i); } h.deleteMin().delete(); } @Test(expected = IllegalArgumentException.class) public void testDeleteMinDecreaseKey() { AddressableHeap h = createHeap(); for (int i = 100; i < 200; i++) { h.insert(i); } h.deleteMin().decreaseKey(0); } @Test(expected = NoSuchElementException.class) public void testNoElementFindMin() { AddressableHeap h = createHeap(); h.findMin(); } @Test(expected = NoSuchElementException.class) public void testNoElementDeleteMin() { AddressableHeap h = createHeap(); h.deleteMin(); } @Test(expected = NullPointerException.class) public void testInsertNull() { AddressableHeap h = createHeap(); h.insert(null, null); } @Test @SuppressWarnings("unchecked") public void testDecreaseKey() { AddressableHeap h = createHeap(); AddressableHeap.Handle array[]; array = new AddressableHeap.Handle[15]; for (int i = 0; i < 15; i++) { array[i] = h.insert(i + 100); } assertEquals(Integer.valueOf(100), h.findMin().getKey()); array[5].decreaseKey(5); assertEquals(Integer.valueOf(5), h.findMin().getKey()); array[1].decreaseKey(50); assertEquals(Integer.valueOf(5), h.findMin().getKey()); array[1].decreaseKey(20); assertEquals(Integer.valueOf(5), h.findMin().getKey()); array[5].delete(); assertEquals(Integer.valueOf(20), h.findMin().getKey()); array[10].decreaseKey(3); assertEquals(Integer.valueOf(3), h.findMin().getKey()); array[0].decreaseKey(0); assertEquals(Integer.valueOf(0), h.findMin().getKey()); } @Test @SuppressWarnings("unchecked") public void testDecreaseKeyWithComparator1() { AddressableHeap h = createHeap(comparator); AddressableHeap.Handle array[]; array = new AddressableHeap.Handle[15]; for (int i = 0; i < 15; i++) { array[i] = h.insert(i); } assertEquals(Integer.valueOf(14), h.findMin().getKey()); array[5].decreaseKey(205); assertEquals(Integer.valueOf(205), h.findMin().getKey()); array[1].decreaseKey(250); assertEquals(Integer.valueOf(250), h.findMin().getKey()); array[1].decreaseKey(300); assertEquals(Integer.valueOf(300), h.findMin().getKey()); array[5].delete(); assertEquals(Integer.valueOf(300), h.findMin().getKey()); array[10].decreaseKey(403); assertEquals(Integer.valueOf(403), h.findMin().getKey()); array[0].decreaseKey(1000); assertEquals(Integer.valueOf(1000), h.findMin().getKey()); } @Test @SuppressWarnings("unchecked") public void testDecreaseKey1() { AddressableHeap h = createHeap(); AddressableHeap.Handle array[]; array = new AddressableHeap.Handle[1000]; for (int i = 0; i < 1000; i++) { array[i] = h.insert(2000 + i); } for (int i = 999; i >= 0; i--) { array[i].decreaseKey(array[i].getKey() - 2000); } for (int i = 0; i < 1000; i++) { assertEquals(i, h.deleteMin().getKey().intValue()); } } @Test @SuppressWarnings("unchecked") public void testDecreaseKeyWithComparator() { AddressableHeap h = createHeap(comparator); AddressableHeap.Handle array[]; array = new AddressableHeap.Handle[1000]; for (int i = 0; i < 1000; i++) { array[i] = h.insert(i); } for (int i = 0; i < 1000; i++) { array[i].decreaseKey(array[i].getKey() + 2000); } for (int i = 999; i >= 0; i--) { assertEquals(i + 2000, h.deleteMin().getKey().intValue()); } } @SuppressWarnings("unchecked") @Test(expected = IllegalArgumentException.class) public void testIncreaseKey() { AddressableHeap h = createHeap(); AddressableHeap.Handle array[]; array = new AddressableHeap.Handle[15]; for (int i = 0; i < 15; i++) { array[i] = h.insert(i + 100); } assertEquals(Integer.valueOf(100), h.findMin().getKey()); array[5].decreaseKey(5); assertEquals(Integer.valueOf(5), h.findMin().getKey()); array[1].decreaseKey(102); } @Test(expected = IllegalArgumentException.class) public void testIncreaseKeyWithComparator() { AddressableHeap h = createHeap(comparator); h.insert(10).decreaseKey(9); } @SuppressWarnings("unchecked") @Test public void testSerializable() throws IOException, ClassNotFoundException { AddressableHeap h = createHeap(); for (int i = 0; i < 15; i++) { h.insert(i); } // write ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(h); oos.close(); byte[] data = baos.toByteArray(); // read ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data)); Object o = ois.readObject(); ois.close(); h = (AddressableHeap) o; for (int i = 0; i < 15; i++) { assertEquals(15 - i, h.size()); assertEquals(Integer.valueOf(i), h.findMin().getKey()); h.deleteMin(); } assertTrue(h.isEmpty()); } @Test public void testSameKey() { AddressableHeap h = createHeap(); assertTrue(h.isEmpty()); Handle handle = h.insert(780); handle.decreaseKey(780); assertEquals(780, h.deleteMin().getKey().intValue()); assertTrue(h.isEmpty()); } @Test public void testGetValue() { AddressableHeap h = createHeapWithStringValues(); assertTrue(h.isEmpty()); Handle handle = h.insert(1, "1"); assertEquals("1", handle.getValue()); } @Test public void testComparator() { AddressableHeap h = createHeap(comparator); int i; for (i = 0; i < SIZE; i++) { h.insert(i); assertEquals(Integer.valueOf(i), h.findMin().getKey()); assertFalse(h.isEmpty()); assertEquals(h.size(), i + 1); } for (i = SIZE - 1; i >= 0; i--) { assertEquals(h.findMin().getKey(), Integer.valueOf(i)); h.deleteMin(); } } @Test public void testDecreaseSame() { AddressableHeap h = createHeap(comparator); h.insert(10).decreaseKey(10); assertEquals(10, h.findMin().getKey().intValue()); } @Test(expected = IllegalArgumentException.class) public void testInvalidHandleDecreaseKey() { AddressableHeap h = createHeap(comparator); Handle handle = h.insert(10); h.deleteMin(); handle.decreaseKey(11); } @SuppressWarnings("unchecked") @Test public void testSerializableWithComparator() throws IOException, ClassNotFoundException { AddressableHeap h = createHeap(comparator); for (int i = 0; i < 15; i++) { h.insert(i); } // write ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(h); oos.close(); byte[] data = baos.toByteArray(); // read ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data)); Object o = ois.readObject(); ois.close(); h = (AddressableHeap) o; for (int i = 0; i < 15; i++) { assertEquals(15 - i, h.size()); assertEquals(Integer.valueOf(15 - i - 1), h.findMin().getKey()); h.deleteMin(); } assertTrue(h.isEmpty()); } @Test public void testGetComparator() { AddressableHeap h = createHeap(comparator); assertEquals(comparator, h.comparator()); } } jheaps-jheaps-0.14/src/test/java/org/jheaps/tree/AbstractComparatorLongHeapTest.java000066400000000000000000000071571367505655000306110ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.tree; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Comparator; import org.jheaps.Heap; import org.junit.BeforeClass; import org.junit.Test; public abstract class AbstractComparatorLongHeapTest extends AbstractLongHeapTest { protected abstract Heap createHeap(Comparator comparator); protected static Comparator comparator; private static class TestComparator implements Comparator, Serializable { private static final long serialVersionUID = 1L; @Override public int compare(Long o1, Long o2) { if (o1 < o2) { return 1; } else if (o1 > o2) { return -1; } else { return 0; } } } @BeforeClass public static void setUpClass() { comparator = new TestComparator(); } @Test public void testWithComparator() { Heap h = createHeap(comparator); long i; for (i = 0; i < SIZE; i++) { h.insert(i); assertEquals(Long.valueOf(i), h.findMin()); assertFalse(h.isEmpty()); assertEquals(h.size(), i + 1); } for (i = SIZE - 1; i >= 0; i--) { assertEquals(h.findMin(), Long.valueOf(i)); h.deleteMin(); } } @Test public void testOnly4Reverse() { Heap h = createHeap(comparator); assertTrue(h.isEmpty()); h.insert(780L); assertEquals(h.size(), 1); assertEquals(Long.valueOf(780), h.findMin()); h.insert(-389L); assertEquals(h.size(), 2); assertEquals(Long.valueOf(780), h.findMin()); h.insert(306L); assertEquals(h.size(), 3); assertEquals(Long.valueOf(780), h.findMin()); h.insert(579L); assertEquals(h.size(), 4); assertEquals(Long.valueOf(780), h.findMin()); h.deleteMin(); assertEquals(h.size(), 3); assertEquals(Long.valueOf(579), h.findMin()); h.deleteMin(); assertEquals(h.size(), 2); assertEquals(Long.valueOf(306), h.findMin()); h.deleteMin(); assertEquals(h.size(), 1); assertEquals(Long.valueOf(-389), h.findMin()); h.deleteMin(); assertEquals(h.size(), 0); assertTrue(h.isEmpty()); } @Test @SuppressWarnings("unchecked") public void testSerializableWithComparator() throws IOException, ClassNotFoundException { Heap h = createHeap(comparator); for (long i = 0; i < 15; i++) { h.insert(i); } // write ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(h); oos.close(); byte[] data = baos.toByteArray(); // read ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data)); Object o = ois.readObject(); ois.close(); h = (Heap) o; for (int i = 0; i < 15; i++) { assertEquals(15 - i, h.size()); assertEquals(Long.valueOf(15 - i - 1), h.findMin()); h.deleteMin(); } assertTrue(h.isEmpty()); } } jheaps-jheaps-0.14/src/test/java/org/jheaps/tree/AbstractDoubleEndedAddressableHeapTest.java000066400000000000000000000444741367505655000321710ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.tree; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.util.Comparator; import java.util.NoSuchElementException; import java.util.Random; import org.jheaps.DoubleEndedAddressableHeap; import org.junit.Test; public abstract class AbstractDoubleEndedAddressableHeapTest extends AbstractAddressableHeapTest { protected abstract DoubleEndedAddressableHeap createHeap(); protected abstract DoubleEndedAddressableHeap createHeap(Comparator comparator); protected abstract DoubleEndedAddressableHeap createHeapWithStringValues(); @Test @Override public void test() { DoubleEndedAddressableHeap h = createHeap(); for (int i = 0; i < SIZE; i++) { h.insert(i); assertEquals(Integer.valueOf(0), h.findMin().getKey()); assertEquals(Integer.valueOf(i), h.findMax().getKey()); assertFalse(h.isEmpty()); assertEquals(h.size(), i + 1); } for (int i = SIZE - 1; i >= 0; i--) { assertEquals(h.findMin().getKey(), Integer.valueOf(SIZE - i - 1)); assertEquals(h.findMax().getKey(), Integer.valueOf(SIZE - 1)); h.deleteMin(); } } @Test @Override public void testOnlyInsert() { DoubleEndedAddressableHeap h = createHeap(); for (int i = 0; i < SIZE; i++) { h.insert(SIZE - i); assertEquals(Integer.valueOf(SIZE - i), h.findMin().getKey()); assertEquals(Integer.valueOf(SIZE), h.findMax().getKey()); assertFalse(h.isEmpty()); assertEquals(h.size(), i + 1); } } @Test @Override public void testComparator() { DoubleEndedAddressableHeap h = createHeap(comparator); int i; for (i = 0; i < SIZE; i++) { h.insert(i); assertEquals(Integer.valueOf(i), h.findMin().getKey()); assertEquals(Integer.valueOf(0), h.findMax().getKey()); assertFalse(h.isEmpty()); assertEquals(h.size(), i + 1); } for (i = SIZE - 1; i >= 0; i--) { assertEquals(h.findMin().getKey(), Integer.valueOf(i)); assertEquals(h.findMax().getKey(), Integer.valueOf(0)); h.deleteMin(); } } @Test @Override public void testOnly4() { DoubleEndedAddressableHeap h = createHeap(); assertTrue(h.isEmpty()); h.insert(780); assertEquals(h.size(), 1); assertEquals(Integer.valueOf(780), h.findMin().getKey()); assertEquals(Integer.valueOf(780), h.findMax().getKey()); h.insert(-389); assertEquals(h.size(), 2); assertEquals(Integer.valueOf(-389), h.findMin().getKey()); assertEquals(Integer.valueOf(780), h.findMax().getKey()); h.insert(306); assertEquals(h.size(), 3); assertEquals(Integer.valueOf(-389), h.findMin().getKey()); assertEquals(Integer.valueOf(780), h.findMax().getKey()); h.insert(579); assertEquals(h.size(), 4); assertEquals(Integer.valueOf(-389), h.findMin().getKey()); assertEquals(Integer.valueOf(780), h.findMax().getKey()); h.deleteMin(); assertEquals(h.size(), 3); assertEquals(Integer.valueOf(306), h.findMin().getKey()); assertEquals(Integer.valueOf(780), h.findMax().getKey()); h.deleteMax(); assertEquals(h.size(), 2); assertEquals(Integer.valueOf(306), h.findMin().getKey()); assertEquals(Integer.valueOf(579), h.findMax().getKey()); h.deleteMin(); assertEquals(h.size(), 1); assertEquals(Integer.valueOf(579), h.findMin().getKey()); assertEquals(Integer.valueOf(579), h.findMax().getKey()); h.deleteMax(); assertEquals(h.size(), 0); assertTrue(h.isEmpty()); } @Test @Override public void testOnly4Reverse() { DoubleEndedAddressableHeap h = createHeap(comparator); assertTrue(h.isEmpty()); h.insert(780); assertEquals(h.size(), 1); assertEquals(Integer.valueOf(780), h.findMin().getKey()); assertEquals(Integer.valueOf(780), h.findMax().getKey()); h.insert(-389); assertEquals(h.size(), 2); assertEquals(Integer.valueOf(780), h.findMin().getKey()); assertEquals(Integer.valueOf(-389), h.findMax().getKey()); h.insert(306); assertEquals(h.size(), 3); assertEquals(Integer.valueOf(780), h.findMin().getKey()); assertEquals(Integer.valueOf(-389), h.findMax().getKey()); h.insert(579); assertEquals(h.size(), 4); assertEquals(Integer.valueOf(780), h.findMin().getKey()); assertEquals(Integer.valueOf(-389), h.findMax().getKey()); h.deleteMin(); assertEquals(h.size(), 3); assertEquals(Integer.valueOf(579), h.findMin().getKey()); assertEquals(Integer.valueOf(-389), h.findMax().getKey()); h.deleteMax(); assertEquals(h.size(), 2); assertEquals(Integer.valueOf(579), h.findMin().getKey()); assertEquals(Integer.valueOf(306), h.findMax().getKey()); h.deleteMin(); assertEquals(h.size(), 1); assertEquals(Integer.valueOf(306), h.findMin().getKey()); assertEquals(Integer.valueOf(306), h.findMax().getKey()); h.deleteMin(); assertEquals(h.size(), 0); assertTrue(h.isEmpty()); } @Test public void testSortRandomSeed1Max() { DoubleEndedAddressableHeap h = createHeap(); Random generator = new Random(1); for (int i = 0; i < SIZE; i++) { h.insert(generator.nextInt()); } Integer prev = null, cur; while (!h.isEmpty()) { cur = h.findMax().getKey(); h.deleteMax(); if (prev != null) { assertTrue(prev.compareTo(cur) >= 0); } prev = cur; } } @Test public void testSort1RandomSeed1Max() { DoubleEndedAddressableHeap h = createHeap(); Random generator = new Random(1); for (int i = 0; i < SIZE; i++) { h.insert(generator.nextInt()); } Integer prev = null, cur; while (!h.isEmpty()) { cur = h.deleteMax().getKey(); if (prev != null) { assertTrue(prev.compareTo(cur) >= 0); } prev = cur; } } @Test public void testSortRandomSeed2Max() { DoubleEndedAddressableHeap h = createHeap(); Random generator = new Random(2); for (int i = 0; i < SIZE; i++) { h.insert(generator.nextInt()); } Integer prev = null, cur; while (!h.isEmpty()) { cur = h.findMax().getKey(); h.deleteMax(); if (prev != null) { assertTrue(prev.compareTo(cur) >= 0); } prev = cur; } } @Test public void testSort2RandomSeed2Max() { DoubleEndedAddressableHeap h = createHeap(); Random generator = new Random(2); for (int i = 0; i < SIZE; i++) { h.insert(generator.nextInt()); } Integer prev = null, cur; while (!h.isEmpty()) { cur = h.deleteMax().getKey(); if (prev != null) { assertTrue(prev.compareTo(cur) >= 0); } prev = cur; } } @Test public void testFindMaxDeleteMaxSameObject() { DoubleEndedAddressableHeap h = createHeap(); Random generator = new Random(1); for (int i = 0; i < SIZE; i++) { h.insert(generator.nextInt()); } while (!h.isEmpty()) { assertEquals(h.findMax(), h.deleteMax()); } } @Test @Override @SuppressWarnings("unchecked") public void testDelete() { DoubleEndedAddressableHeap h = createHeap(); DoubleEndedAddressableHeap.Handle array[]; array = new DoubleEndedAddressableHeap.Handle[15]; for (int i = 0; i < 15; i++) { array[i] = h.insert(i); } array[5].delete(); assertEquals(Integer.valueOf(0), h.findMin().getKey()); assertEquals(Integer.valueOf(14), h.findMax().getKey()); array[7].delete(); assertEquals(Integer.valueOf(0), h.findMin().getKey()); assertEquals(Integer.valueOf(14), h.findMax().getKey()); array[0].delete(); assertEquals(Integer.valueOf(1), h.findMin().getKey()); assertEquals(Integer.valueOf(14), h.findMax().getKey()); array[2].delete(); assertEquals(Integer.valueOf(1), h.findMin().getKey()); assertEquals(Integer.valueOf(14), h.findMax().getKey()); array[1].delete(); assertEquals(Integer.valueOf(3), h.findMin().getKey()); assertEquals(Integer.valueOf(14), h.findMax().getKey()); array[3].delete(); assertEquals(Integer.valueOf(4), h.findMin().getKey()); assertEquals(Integer.valueOf(14), h.findMax().getKey()); array[9].delete(); assertEquals(Integer.valueOf(4), h.findMin().getKey()); assertEquals(Integer.valueOf(14), h.findMax().getKey()); array[4].delete(); assertEquals(Integer.valueOf(6), h.findMin().getKey()); assertEquals(Integer.valueOf(14), h.findMax().getKey()); array[8].delete(); assertEquals(Integer.valueOf(6), h.findMin().getKey()); assertEquals(Integer.valueOf(14), h.findMax().getKey()); array[11].delete(); assertEquals(Integer.valueOf(6), h.findMin().getKey()); assertEquals(Integer.valueOf(14), h.findMax().getKey()); array[6].delete(); assertEquals(Integer.valueOf(10), h.findMin().getKey()); assertEquals(Integer.valueOf(14), h.findMax().getKey()); array[12].delete(); assertEquals(Integer.valueOf(10), h.findMin().getKey()); assertEquals(Integer.valueOf(14), h.findMax().getKey()); array[10].delete(); assertEquals(Integer.valueOf(13), h.findMin().getKey()); assertEquals(Integer.valueOf(14), h.findMax().getKey()); array[13].delete(); assertEquals(Integer.valueOf(14), h.findMin().getKey()); assertEquals(Integer.valueOf(14), h.findMax().getKey()); array[14].delete(); assertTrue(h.isEmpty()); } @Test @Override @SuppressWarnings("unchecked") public void testDelete1() { DoubleEndedAddressableHeap h = createHeap(); DoubleEndedAddressableHeap.Handle array[]; array = new DoubleEndedAddressableHeap.Handle[8]; for (int i = 0; i < 8; i++) { array[i] = h.insert(i); } array[5].delete(); assertEquals(Integer.valueOf(0), h.findMin().getKey()); assertEquals(Integer.valueOf(7), h.findMax().getKey()); array[7].delete(); assertEquals(Integer.valueOf(0), h.findMin().getKey()); assertEquals(Integer.valueOf(6), h.findMax().getKey()); array[0].delete(); assertEquals(Integer.valueOf(1), h.findMin().getKey()); assertEquals(Integer.valueOf(6), h.findMax().getKey()); array[2].delete(); assertEquals(Integer.valueOf(1), h.findMin().getKey()); assertEquals(Integer.valueOf(6), h.findMax().getKey()); array[1].delete(); assertEquals(Integer.valueOf(3), h.findMin().getKey()); assertEquals(Integer.valueOf(6), h.findMax().getKey()); } @Test @Override @SuppressWarnings("unchecked") public void testAddDelete() { DoubleEndedAddressableHeap h = createHeap(); DoubleEndedAddressableHeap.Handle array[]; array = new DoubleEndedAddressableHeap.Handle[SIZE]; for (int i = 0; i < SIZE; i++) { array[i] = h.insert(i); } for (int i = SIZE - 1; i >= 0; i--) { array[i].delete(); if (i > 0) { assertEquals(Integer.valueOf(0), h.findMin().getKey()); assertEquals(Integer.valueOf(i - 1), h.findMax().getKey()); } } assertTrue(h.isEmpty()); } @Test @Override @SuppressWarnings("unchecked") public void testAddDeleteComparator() { DoubleEndedAddressableHeap h = createHeap(comparator); DoubleEndedAddressableHeap.Handle array[]; array = new DoubleEndedAddressableHeap.Handle[SIZE]; for (int i = 0; i < SIZE; i++) { array[i] = h.insert(i); } for (int i = 0; i < SIZE; i++) { array[i].delete(); if (i < SIZE - 1) { assertEquals(Integer.valueOf(SIZE - 1), h.findMin().getKey()); assertEquals(Integer.valueOf(i + 1), h.findMax().getKey()); } } assertTrue(h.isEmpty()); } @Test @Override @SuppressWarnings("unchecked") public void testAddDecreaseKeyDeleteMin() { DoubleEndedAddressableHeap h = createHeap(); DoubleEndedAddressableHeap.Handle array[]; array = new DoubleEndedAddressableHeap.Handle[SIZE]; for (int i = 0; i < SIZE; i++) { array[i] = h.insert(i); } assertEquals(0, h.findMin().getKey().intValue()); assertEquals(SIZE - 1, h.findMax().getKey().intValue()); for (int i = SIZE / 2; i < SIZE / 2 + 10; i++) { array[i].decreaseKey(i / 2); } array[0].delete(); assertEquals(1, h.findMin().getKey().intValue()); for (int i = SIZE / 2 + 10; i < SIZE / 2 + 20; i++) { array[i].decreaseKey(0); } assertEquals(0, h.deleteMin().getKey().intValue()); array[SIZE - 1].delete(); assertEquals(SIZE - 2, h.findMax().getKey().intValue()); for (int i = SIZE / 2 + 20; i < SIZE / 2 + 30; i++) { array[i].increaseKey(SIZE - 1); } assertEquals(SIZE - 1, h.deleteMax().getKey().intValue()); } @Test @SuppressWarnings("unchecked") public void testAddDecreaseKeyDeleteMinComparator() { DoubleEndedAddressableHeap h = createHeap(comparator); DoubleEndedAddressableHeap.Handle array[]; array = new DoubleEndedAddressableHeap.Handle[SIZE]; for (int i = 0; i < SIZE; i++) { array[i] = h.insert(i); } assertEquals(SIZE - 1, h.findMin().getKey().intValue()); assertEquals(0, h.findMax().getKey().intValue()); for (int i = SIZE / 2; i < SIZE / 2 + 10; i++) { array[i].decreaseKey(SIZE - 1); } array[SIZE - 1].delete(); for (int i = SIZE / 2 + 10; i < SIZE / 2 + 20; i++) { array[i].decreaseKey(SIZE - 1); } assertEquals(SIZE - 1, h.deleteMin().getKey().intValue()); assertEquals(0, h.findMax().getKey().intValue()); array[0].delete(); assertEquals(1, h.findMax().getKey().intValue()); for (int i = SIZE / 2 + 20; i < SIZE / 2 + 30; i++) { array[i].increaseKey(0); } assertEquals(0, h.findMax().getKey().intValue()); } @Test(expected = IllegalArgumentException.class) public void testDeleteMaxDeleteTwice() { DoubleEndedAddressableHeap h = createHeap(); h.insert(50); DoubleEndedAddressableHeap.Handle e1 = h.insert(100); h.deleteMax(); e1.delete(); } @Test(expected = IllegalArgumentException.class) public void testDeleteMaxDeleteTwice1() { DoubleEndedAddressableHeap h = createHeap(); for (int i = 100; i < 200; i++) { h.insert(i); } h.deleteMax().delete(); } @Test(expected = IllegalArgumentException.class) public void testDeleteMaxDecreaseKey() { DoubleEndedAddressableHeap h = createHeap(); for (int i = 100; i < 200; i++) { h.insert(i); } h.deleteMax().decreaseKey(0); } @Test(expected = IllegalArgumentException.class) public void testDeleteMaxIncreaseKey() { DoubleEndedAddressableHeap h = createHeap(); for (int i = 100; i < 200; i++) { h.insert(i); } h.deleteMax().increaseKey(200); } @Test(expected = NoSuchElementException.class) public void testNoElementFindMax() { DoubleEndedAddressableHeap h = createHeap(); h.findMax(); } @Test(expected = NoSuchElementException.class) public void testNoElementDeleteMax() { DoubleEndedAddressableHeap h = createHeap(); h.deleteMax(); } @Test(expected = IllegalArgumentException.class) public void testWrongIncreaseKey() { DoubleEndedAddressableHeap h = createHeap(); h.insert(15).increaseKey(14); } @Test public void testSameKeyAfterIncreaseDecrease() { DoubleEndedAddressableHeap h = createHeap(); assertTrue(h.isEmpty()); DoubleEndedAddressableHeap.Handle handle = h.insert(780); handle.increaseKey(780); assertEquals(780, h.deleteMax().getKey().intValue()); h.insert(500); h.insert(600); DoubleEndedAddressableHeap.Handle handle1 = h.insert(700); handle1.increaseKey(700); assertEquals(700, handle1.getKey().intValue()); handle1.decreaseKey(700); assertEquals(700, handle1.getKey().intValue()); } } jheaps-jheaps-0.14/src/test/java/org/jheaps/tree/AbstractDoubleEndedHeapTest.java000066400000000000000000000150071367505655000300250ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.tree; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.util.Comparator; import java.util.NoSuchElementException; import java.util.Random; import org.jheaps.DoubleEndedHeap; import org.junit.Test; public abstract class AbstractDoubleEndedHeapTest extends AbstractComparatorLongHeapTest { @Override protected abstract DoubleEndedHeap createHeap(); @Override protected abstract DoubleEndedHeap createHeap(int capacity); @Override protected abstract DoubleEndedHeap createHeap(Comparator comparator); @Test public void testInsertDeleteMax() { DoubleEndedHeap h = createHeap(); for (long i = 0; i < SIZE; i++) { h.insert(i); assertEquals(Long.valueOf(0), h.findMin()); assertEquals(Long.valueOf(i), h.findMax()); assertFalse(h.isEmpty()); assertEquals(h.size(), i + 1); } for (int i = SIZE - 1; i >= 0; i--) { assertEquals(h.findMin(), Long.valueOf(0)); assertEquals(h.findMax(), Long.valueOf(i)); h.deleteMax(); } } @Test(expected = NoSuchElementException.class) public void testBadDeleteMax() { DoubleEndedHeap h = createHeap(); h.deleteMax(); } @Test(expected = NoSuchElementException.class) public void testBadFindMax() { DoubleEndedHeap h = createHeap(); h.findMax(); } @Test public void testSortMaxRandom() { testSort(SIZE, new Random(1)); testSortReverse(SIZE, new Random(1)); testSort(SIZE, new Random(2)); testSortReverse(SIZE, new Random(2)); testSort(SIZE, new Random(3)); testSortReverse(SIZE, new Random(3)); testSort(SIZE, new Random(4)); testSortReverse(SIZE, new Random(4)); testSort(SIZE, new Random(5)); testSortReverse(SIZE, new Random(5)); testSort(SIZE, new Random(6)); testSortReverse(SIZE, new Random(6)); } @Test public void testSmallInstanceSortMaxRandom() { for (long seed = 13; seed < 1000; seed++) { testSort(SIZE / 100, new Random(seed)); testSortReverse(SIZE / 100, new Random(seed)); } } @Test public void testFindMaxDeleteMaxSameObject() { DoubleEndedHeap h = createHeap(); Random generator = new Random(1); for (int i = 0; i < SIZE; i++) { h.insert(generator.nextLong()); } while (!h.isEmpty()) { assertEquals(h.findMax(), h.deleteMax()); } } @Test public void testWithComparatorMax() { DoubleEndedHeap h = createHeap(comparator); long i; for (i = 0; i < SIZE; i++) { h.insert(i); assertEquals(Long.valueOf(i), h.findMin()); assertEquals(Long.valueOf(0), h.findMax()); assertFalse(h.isEmpty()); assertEquals(h.size(), i + 1); } for (i = SIZE - 1; i >= 0; i--) { assertEquals(h.findMin(), Long.valueOf(i)); assertEquals(h.findMax(), Long.valueOf(0)); h.deleteMin(); } } @Test public void test3Elements() { DoubleEndedHeap h = createHeap(); assertTrue(h.isEmpty()); h.insert(780L); h.insert(800L); h.insert(900L); assertEquals(h.size(), 3); assertEquals(Long.valueOf(780), h.findMin()); assertEquals(Long.valueOf(900), h.findMax()); } @Test public void test3Elements1() { DoubleEndedHeap h = createHeap(); assertTrue(h.isEmpty()); h.insert(900L); h.insert(800L); h.insert(780L); assertEquals(h.size(), 3); assertEquals(Long.valueOf(780), h.findMin()); assertEquals(Long.valueOf(900), h.findMax()); assertEquals(Long.valueOf(900), h.deleteMax()); } @Test public void test3ElementsComparator() { DoubleEndedHeap h = createHeap(comparator); assertTrue(h.isEmpty()); h.insert(900L); h.insert(800L); h.insert(780L); assertEquals(h.size(), 3); assertEquals(Long.valueOf(780), h.findMax()); assertEquals(Long.valueOf(900), h.findMin()); } @Test public void test3ElementsComparator1() { DoubleEndedHeap h = createHeap(comparator); assertTrue(h.isEmpty()); h.insert(900L); h.insert(800L); h.insert(780L); h.insert(850L); assertEquals(h.size(), 4); assertEquals(Long.valueOf(780), h.findMax()); assertEquals(Long.valueOf(900), h.findMin()); assertEquals(Long.valueOf(780), h.deleteMax()); } @Test public void testOnly4ReverseMax() { DoubleEndedHeap h = createHeap(comparator); assertTrue(h.isEmpty()); h.insert(780L); assertEquals(h.size(), 1); assertEquals(Long.valueOf(780), h.findMin()); assertEquals(Long.valueOf(780), h.findMax()); h.insert(-389L); assertEquals(h.size(), 2); assertEquals(Long.valueOf(780), h.findMin()); assertEquals(Long.valueOf(-389), h.findMax()); h.insert(306L); assertEquals(h.size(), 3); assertEquals(Long.valueOf(780), h.findMin()); assertEquals(Long.valueOf(-389), h.findMax()); h.insert(579L); assertEquals(h.size(), 4); assertEquals(Long.valueOf(780), h.findMin()); assertEquals(Long.valueOf(-389), h.findMax()); h.deleteMin(); assertEquals(h.size(), 3); assertEquals(Long.valueOf(579), h.findMin()); assertEquals(Long.valueOf(-389), h.findMax()); h.deleteMax(); assertEquals(h.size(), 2); assertEquals(Long.valueOf(579), h.findMin()); assertEquals(Long.valueOf(306), h.findMax()); h.deleteMin(); assertEquals(h.size(), 1); assertEquals(Long.valueOf(306), h.findMin()); h.deleteMax(); assertEquals(h.size(), 0); assertTrue(h.isEmpty()); } private void testSort(int size, Random rng) { DoubleEndedHeap h = createHeap(); for (int i = 0; i < size; i++) { h.insert(rng.nextLong()); } Long prev = null, cur; while (!h.isEmpty()) { cur = h.deleteMax(); if (prev != null) { assertTrue(prev.compareTo(cur) >= 0); } prev = cur; } } private void testSortReverse(int size, Random rng) { DoubleEndedHeap h = createHeap(comparator); for (int i = 0; i < size; i++) { h.insert(rng.nextLong()); } Long prev = null, cur; while (!h.isEmpty()) { cur = h.deleteMax(); if (prev != null) { assertTrue(comparator.compare(prev, cur) >= 0); } prev = cur; } } } jheaps-jheaps-0.14/src/test/java/org/jheaps/tree/AbstractIntegerHeapTest.java000066400000000000000000000144631367505655000272550ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.tree; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.NoSuchElementException; import java.util.Random; import org.jheaps.Heap; import org.junit.Test; public abstract class AbstractIntegerHeapTest { protected static final int SIZE = 100000; protected abstract Heap createHeap(); protected abstract Heap createHeap(int capacity); @Test public void test() { Heap h = createHeap(); for (int i = 0; i < SIZE; i++) { h.insert(i); assertEquals(Integer.valueOf(0), h.findMin()); assertFalse(h.isEmpty()); assertEquals(h.size(), i + 1); } for (int i = SIZE - 1; i >= 0; i--) { assertEquals(Integer.valueOf(SIZE - i - 1), h.findMin()); h.deleteMin(); } } @Test public void testOnlyInsert() { Heap h = createHeap(); for (int i = 0; i < SIZE; i++) { h.insert(SIZE - i); assertEquals(Integer.valueOf(SIZE - i), h.findMin()); assertFalse(h.isEmpty()); assertEquals(h.size(), i + 1); } } @Test public void testInsertFromZero() { Heap h = createHeap(0); for (int i = 0; i < SIZE; i++) { h.insert(SIZE - i); assertEquals(Integer.valueOf(SIZE - i), h.findMin()); assertFalse(h.isEmpty()); assertEquals(h.size(), i + 1); } } @Test(expected = NullPointerException.class) public void testBadInsert() { Heap h = createHeap(); h.insert(null); } @Test(expected = NoSuchElementException.class) public void testBadDeleteMin() { Heap h = createHeap(); h.deleteMin(); } @Test(expected = NoSuchElementException.class) public void testBadFindMin() { Heap h = createHeap(); h.findMin(); } @Test public void testOnly4() { Heap h = createHeap(); assertTrue(h.isEmpty()); h.insert(780); assertEquals(h.size(), 1); assertEquals(Integer.valueOf(780), h.findMin()); h.insert(-389); assertEquals(h.size(), 2); assertEquals(Integer.valueOf(-389), h.findMin()); h.insert(306); assertEquals(h.size(), 3); assertEquals(Integer.valueOf(-389), h.findMin()); h.insert(579); assertEquals(h.size(), 4); assertEquals(Integer.valueOf(-389), h.findMin()); h.deleteMin(); assertEquals(h.size(), 3); assertEquals(Integer.valueOf(306), h.findMin()); h.deleteMin(); assertEquals(h.size(), 2); assertEquals(Integer.valueOf(579), h.findMin()); h.deleteMin(); assertEquals(h.size(), 1); assertEquals(Integer.valueOf(780), h.findMin()); h.deleteMin(); assertEquals(h.size(), 0); assertTrue(h.isEmpty()); } @Test public void testSortRandomSeed1() { Heap h = createHeap(); Random generator = new Random(1); for (int i = 0; i < SIZE; i++) { h.insert(generator.nextInt()); } Integer prev = null, cur; while (!h.isEmpty()) { cur = h.findMin(); h.deleteMin(); if (prev != null) { assertTrue(prev.compareTo(cur) <= 0); } prev = cur; } } @Test public void testSort1RandomSeed1() { Heap h = createHeap(); Random generator = new Random(1); for (int i = 0; i < SIZE; i++) { h.insert(generator.nextInt()); } Integer prev = null, cur; while (!h.isEmpty()) { cur = h.deleteMin(); if (prev != null) { assertTrue(prev.compareTo(cur) <= 0); } prev = cur; } } @Test public void testSortRandomSeed2() { Heap h = createHeap(); Random generator = new Random(2); for (int i = 0; i < SIZE; i++) { h.insert(generator.nextInt()); } Integer prev = null, cur; while (!h.isEmpty()) { cur = h.findMin(); h.deleteMin(); if (prev != null) { assertTrue(prev.compareTo(cur) <= 0); } prev = cur; } } @Test public void testSort1RandomSeed2() { Heap h = createHeap(); Random generator = new Random(1); for (int i = 0; i < SIZE; i++) { h.insert(generator.nextInt()); } Integer prev = null, cur; while (!h.isEmpty()) { cur = h.deleteMin(); if (prev != null) { assertTrue(prev.compareTo(cur) <= 0); } prev = cur; } } @Test public void testFindMinDeleteMinSameObject() { Heap h = createHeap(); Random generator = new Random(1); for (int i = 0; i < SIZE; i++) { h.insert(generator.nextInt()); } while (!h.isEmpty()) { assertEquals(h.findMin(), h.deleteMin()); } } @Test public void testSizeOneInitial() { Heap h = createHeap(1); for (int i = 0; i < 15; i++) { h.insert(i); } assertEquals(15, h.size()); } @Test public void testClear() { Heap h = createHeap(); for (int i = 0; i < 15; i++) { h.insert(i); } h.clear(); assertEquals(0L, h.size()); assertTrue(h.isEmpty()); } @Test public void testComparator() { Heap h = createHeap(); assertNull(h.comparator()); } @SuppressWarnings("unchecked") @Test public void testSerializable() throws IOException, ClassNotFoundException { Heap h = createHeap(); for (int i = 0; i < 15; i++) { h.insert(i); } // write ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(h); oos.close(); byte[] data = baos.toByteArray(); // read ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data)); Object o = ois.readObject(); ois.close(); h = (Heap) o; for (int i = 0; i < 15; i++) { assertEquals(15 - i, h.size()); assertEquals(Integer.valueOf(i), h.findMin()); h.deleteMin(); } assertTrue(h.isEmpty()); } }jheaps-jheaps-0.14/src/test/java/org/jheaps/tree/AbstractLongHeapTest.java000066400000000000000000000143571367505655000265610ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.tree; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.NoSuchElementException; import java.util.Random; import org.jheaps.Heap; import org.junit.Test; public abstract class AbstractLongHeapTest { protected static final int SIZE = 100000; protected abstract Heap createHeap(); protected abstract Heap createHeap(int capacity); @Test public void test() { Heap h = createHeap(); for (long i = 0; i < SIZE; i++) { h.insert(i); assertEquals(Long.valueOf(0), h.findMin()); assertFalse(h.isEmpty()); assertEquals(h.size(), i + 1); } for (int i = SIZE - 1; i >= 0; i--) { assertEquals(h.findMin(), Long.valueOf(SIZE - i - 1)); h.deleteMin(); } } @Test public void testOnlyInsert() { Heap h = createHeap(); for (long i = 0; i < SIZE; i++) { h.insert(SIZE - i); assertEquals(Long.valueOf(SIZE - i), h.findMin()); assertFalse(h.isEmpty()); assertEquals(h.size(), i + 1); } } @Test public void testInsertFromZero() { Heap h = createHeap(0); for (long i = 0; i < SIZE; i++) { h.insert(SIZE - i); assertEquals(Long.valueOf(SIZE - i), h.findMin()); assertFalse(h.isEmpty()); assertEquals(h.size(), i + 1); } } @Test(expected = NullPointerException.class) public void testBadInsert() { Heap h = createHeap(); h.insert(null); } @Test(expected = NoSuchElementException.class) public void testBadDeleteMin() { Heap h = createHeap(); h.deleteMin(); } @Test(expected = NoSuchElementException.class) public void testBadFindMin() { Heap h = createHeap(); h.findMin(); } @Test public void testOnly4() { Heap h = createHeap(); assertTrue(h.isEmpty()); h.insert(780L); assertEquals(h.size(), 1); assertEquals(Long.valueOf(780).longValue(), h.findMin().longValue()); h.insert(-389L); assertEquals(h.size(), 2); assertEquals(Long.valueOf(-389), h.findMin()); h.insert(306L); assertEquals(h.size(), 3); assertEquals(Long.valueOf(-389), h.findMin()); h.insert(579L); assertEquals(h.size(), 4); assertEquals(Long.valueOf(-389), h.findMin()); h.deleteMin(); assertEquals(h.size(), 3); assertEquals(Long.valueOf(306), h.findMin()); h.deleteMin(); assertEquals(h.size(), 2); assertEquals(Long.valueOf(579), h.findMin()); h.deleteMin(); assertEquals(h.size(), 1); assertEquals(Long.valueOf(780), h.findMin()); h.deleteMin(); assertEquals(h.size(), 0); assertTrue(h.isEmpty()); } @Test public void testSortRandomSeed1() { Heap h = createHeap(); Random generator = new Random(1); for (int i = 0; i < SIZE; i++) { h.insert(generator.nextLong()); } Long prev = null, cur; while (!h.isEmpty()) { cur = h.findMin(); h.deleteMin(); if (prev != null) { assertTrue(prev.compareTo(cur) <= 0); } prev = cur; } } @Test public void testSort1RandomSeed1() { Heap h = createHeap(); Random generator = new Random(1); for (int i = 0; i < SIZE; i++) { h.insert(generator.nextLong()); } Long prev = null, cur; while (!h.isEmpty()) { cur = h.deleteMin(); if (prev != null) { assertTrue(prev.compareTo(cur) <= 0); } prev = cur; } } @Test public void testSortRandomSeed2() { Heap h = createHeap(); Random generator = new Random(2); for (int i = 0; i < SIZE; i++) { h.insert(generator.nextLong()); } Long prev = null, cur; while (!h.isEmpty()) { cur = h.findMin(); h.deleteMin(); if (prev != null) { assertTrue(prev.compareTo(cur) <= 0); } prev = cur; } } @Test public void testSort1RandomSeed2() { Heap h = createHeap(); Random generator = new Random(1); for (int i = 0; i < SIZE; i++) { h.insert(generator.nextLong()); } Long prev = null, cur; while (!h.isEmpty()) { cur = h.deleteMin(); if (prev != null) { assertTrue(prev.compareTo(cur) <= 0); } prev = cur; } } @Test public void testFindMinDeleteMinSameObject() { Heap h = createHeap(); Random generator = new Random(1); for (int i = 0; i < SIZE; i++) { h.insert(generator.nextLong()); } while (!h.isEmpty()) { assertEquals(h.findMin(), h.deleteMin()); } } @Test public void testSizeOneInitial() { Heap h = createHeap(1); for (long i = 0; i < 15; i++) { h.insert(i); } assertEquals(15, h.size()); } @Test public void testClear() { Heap h = createHeap(); for (long i = 0; i < 15; i++) { h.insert(i); } h.clear(); assertEquals(0L, h.size()); assertTrue(h.isEmpty()); } @Test public void testComparator() { Heap h = createHeap(); assertNull(h.comparator()); } @SuppressWarnings("unchecked") @Test public void testSerializable() throws IOException, ClassNotFoundException { Heap h = createHeap(); for (long i = 0; i < 15; i++) { h.insert(i); } // write ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(h); oos.close(); byte[] data = baos.toByteArray(); // read ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data)); Object o = ois.readObject(); ois.close(); h = (Heap) o; for (int i = 0; i < 15; i++) { assertEquals(15 - i, h.size()); assertEquals(Long.valueOf(i), h.findMin()); h.deleteMin(); } assertTrue(h.isEmpty()); } }jheaps-jheaps-0.14/src/test/java/org/jheaps/tree/AbstractMergeableAddressableHeapTest.java000066400000000000000000000307451367505655000316760ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.tree; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.IOException; import java.io.Serializable; import java.util.Comparator; import org.jheaps.AddressableHeap.Handle; import org.jheaps.MergeableAddressableHeap; import org.junit.BeforeClass; import org.junit.Test; public abstract class AbstractMergeableAddressableHeapTest { protected static final int SIZE = 100000; private static Comparator comparator; private static class TestComparator implements Comparator, Serializable { private static final long serialVersionUID = 1L; @Override public int compare(Integer o1, Integer o2) { if (o1 < o2) { return 1; } else if (o1 > o2) { return -1; } else { return 0; } } } @BeforeClass public static void setUpClass() { comparator = new TestComparator(); } protected abstract MergeableAddressableHeap createHeap(Comparator comparator); protected abstract MergeableAddressableHeap createHeap(); @Test public void testMeld1() { MergeableAddressableHeap a = createHeap(); a.insert(10); a.insert(11); a.insert(12); a.insert(13); MergeableAddressableHeap b = createHeap(); b.insert(14); b.insert(15); b.insert(16); Handle b4 = b.insert(17); a.meld(b); assertEquals(8, a.size()); assertTrue(b.isEmpty()); assertEquals(0, b.size()); b4.decreaseKey(9); assertEquals(Integer.valueOf(9), a.findMin().getKey()); } @Test public void testMeld2() { MergeableAddressableHeap a = createHeap(); a.insert(10); a.insert(11); a.insert(12); a.insert(13); MergeableAddressableHeap b = createHeap(); b.insert(14); b.insert(15); b.insert(16); Handle b4 = b.insert(17); MergeableAddressableHeap c = createHeap(); c.insert(18); c.insert(19); c.insert(20); Handle c4 = c.insert(21); a.meld(b); a.meld(c); assertEquals(12, a.size()); assertTrue(b.isEmpty()); assertEquals(0, b.size()); assertTrue(c.isEmpty()); assertEquals(0, c.size()); assertEquals(Integer.valueOf(10), a.findMin().getKey()); b4.decreaseKey(9); assertEquals(Integer.valueOf(9), a.findMin().getKey()); c4.decreaseKey(8); assertEquals(Integer.valueOf(8), a.findMin().getKey()); } @Test(expected = IllegalStateException.class) public void testMultipleMelds() { MergeableAddressableHeap a = createHeap(); a.insert(10); a.insert(11); a.insert(12); a.insert(13); MergeableAddressableHeap b = createHeap(); b.insert(14); b.insert(15); b.insert(16); b.insert(17); MergeableAddressableHeap c = createHeap(); c.insert(18); c.insert(19); c.insert(20); c.insert(21); a.meld(b); a.meld(b); } @Test(expected = IllegalStateException.class) public void testInsertAfterAMeld() { MergeableAddressableHeap a = createHeap(); a.insert(10); a.insert(11); a.insert(12); a.insert(13); MergeableAddressableHeap b = createHeap(); b.insert(14); b.insert(15); b.insert(16); b.insert(17); a.meld(b); b.insert(30); } @Test public void testCascadingMelds() { MergeableAddressableHeap a = createHeap(); a.insert(10); a.insert(11); a.insert(12); a.insert(13); MergeableAddressableHeap b = createHeap(); b.insert(14); b.insert(15); b.insert(16); b.insert(17); MergeableAddressableHeap c = createHeap(); c.insert(18); c.insert(19); c.insert(20); c.insert(21); MergeableAddressableHeap d = createHeap(); d.insert(22); d.insert(23); d.insert(24); d.insert(25); MergeableAddressableHeap e = createHeap(); e.insert(26); e.insert(27); Handle e3 = e.insert(28); Handle e4 = e.insert(29); d.meld(e); c.meld(d); b.meld(c); a.meld(b); assertEquals(20, a.size()); assertEquals(0, b.size()); assertEquals(0, c.size()); assertEquals(0, d.size()); assertEquals(0, e.size()); assertEquals(Integer.valueOf(10), a.findMin().getKey()); e4.decreaseKey(9); assertEquals(Integer.valueOf(9), a.findMin().getKey()); e3.decreaseKey(8); assertEquals(Integer.valueOf(8), a.findMin().getKey()); e3.delete(); assertEquals(Integer.valueOf(9), a.findMin().getKey()); } @Test public void testMeldGeneric() { MergeableAddressableHeap h1 = createHeap(); for (int i = 0; i < SIZE; i++) { h1.insert(2 * i); } MergeableAddressableHeap h2 = createHeap(); for (int i = 0; i < SIZE; i++) { h2.insert(2 * i + 1); } h1.meld(h2); assertEquals(h1.size(), SIZE * 2); assertEquals(h2.size(), 0); Integer prev = null, cur; while (!h1.isEmpty()) { cur = h1.findMin().getKey(); h1.deleteMin(); if (prev != null) { assertTrue(prev.compareTo(cur) <= 0); } prev = cur; } } @Test public void testMeldGeneric1() { MergeableAddressableHeap h1 = createHeap(); MergeableAddressableHeap h2 = createHeap(); for (int i = 0; i < SIZE; i++) { h2.insert(i); } h1.meld(h2); assertEquals(h1.size(), SIZE); assertEquals(h2.size(), 0); Integer prev = null, cur; while (!h1.isEmpty()) { cur = h1.findMin().getKey(); h1.deleteMin(); if (prev != null) { assertTrue(prev.compareTo(cur) <= 0); } prev = cur; } } @Test public void testMeldGeneric2() { MergeableAddressableHeap h1 = createHeap(); MergeableAddressableHeap h2 = createHeap(); for (int i = 0; i < SIZE; i++) { h1.insert(i); } h1.meld(h2); assertEquals(h1.size(), SIZE); assertEquals(h2.size(), 0); Integer prev = null, cur; while (!h1.isEmpty()) { cur = h1.findMin().getKey(); h1.deleteMin(); if (prev != null) { assertTrue(prev.compareTo(cur) <= 0); } prev = cur; } } @Test public void testMeld() { MergeableAddressableHeap h1 = createHeap(); MergeableAddressableHeap h2 = createHeap(); for (int i = 0; i < SIZE; i++) { if (i % 2 == 0) { h1.insert(i); } else { h2.insert(i); } } h1.meld(h2); assertTrue(h2.isEmpty()); assertEquals(0, h2.size()); for (int i = 0; i < SIZE; i++) { assertEquals(Integer.valueOf(i), h1.findMin().getKey()); h1.deleteMin(); } assertTrue(h1.isEmpty()); } @Test public void testMeldWithComparator() { MergeableAddressableHeap h1 = createHeap(comparator); MergeableAddressableHeap h2 = createHeap(comparator); for (int i = 0; i < SIZE; i++) { if (i % 2 == 0) { h1.insert(i); } else { h2.insert(i); } } h1.meld(h2); assertTrue(h2.isEmpty()); assertEquals(0, h2.size()); for (int i = 0; i < SIZE; i++) { assertEquals(Integer.valueOf(SIZE - i - 1), h1.findMin().getKey()); h1.deleteMin(); } assertTrue(h1.isEmpty()); } @Test public void testMeldBadComparator() { MergeableAddressableHeap h1 = createHeap(comparator); MergeableAddressableHeap h2 = createHeap(new Comparator() { @Override public int compare(Integer o1, Integer o2) { return o1 - o2; } }); for (int i = 0; i < SIZE; i++) { if (i % 2 == 0) { h1.insert(i); } else { h2.insert(i); } } try { h1.meld(h2); fail("No!"); } catch (IllegalArgumentException ignored) { } } @Test public void testMeldBadComparator1() { MergeableAddressableHeap h1 = createHeap(comparator); MergeableAddressableHeap h2 = createHeap(null); try { h1.meld(h2); fail("No!"); } catch (IllegalArgumentException ignored) { } } @Test(expected = IllegalArgumentException.class) public void testMeldWrong1() throws IOException, ClassNotFoundException { MergeableAddressableHeap h1 = createHeap(comparator); MergeableAddressableHeap h2 = createHeap(); h1.meld(h2); } @Test public void testMeldWithComparatorSmallerFirst() { MergeableAddressableHeap h1 = createHeap(comparator); MergeableAddressableHeap h2 = createHeap(comparator); h1.insert(0); h1.insert(1); h1.insert(2); h1.insert(3); for (int i = 4; i < SIZE; i++) { h2.insert(i); } h1.meld(h2); assertTrue(h2.isEmpty()); assertEquals(0, h2.size()); for (int i = 0; i < SIZE; i++) { assertEquals(Integer.valueOf(SIZE - i - 1), h1.findMin().getKey()); h1.deleteMin(); } assertTrue(h1.isEmpty()); } @Test public void testMeldWithComparator1() { MergeableAddressableHeap h1 = createHeap(comparator); MergeableAddressableHeap h2 = createHeap(comparator); for (int i = 0; i < SIZE; i++) { if (i % 2 == 0) { h1.insert(i); } else { h2.insert(i); } } h1.meld(h2); assertTrue(h2.isEmpty()); assertEquals(0, h2.size()); for (int i = 0; i < SIZE; i++) { assertEquals(Integer.valueOf(SIZE - i - 1), h1.findMin().getKey()); h1.deleteMin(); } assertTrue(h1.isEmpty()); } @Test(expected = IllegalArgumentException.class) public void testMeldWrong() { MergeableAddressableHeap h1 = createHeap(); MergeableAddressableHeap h2 = createHeap(comparator); for (int i = 0; i < SIZE; i++) { if (i % 2 == 0) { h1.insert(i); } else { h2.insert(i); } } h1.meld(h2); assertTrue(h2.isEmpty()); assertEquals(0, h2.size()); for (int i = 0; i < SIZE; i++) { assertEquals(Integer.valueOf(i), h1.findMin().getKey()); h1.deleteMin(); } assertTrue(h1.isEmpty()); } } AbstractMergeableDoubleEndedAddressableHeapTest.java000066400000000000000000000304611367505655000337050ustar00rootroot00000000000000jheaps-jheaps-0.14/src/test/java/org/jheaps/tree/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.tree; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.Serializable; import java.util.Comparator; import org.jheaps.AddressableHeap.Handle; import org.jheaps.MergeableDoubleEndedAddressableHeap; import org.junit.BeforeClass; import org.junit.Test; public abstract class AbstractMergeableDoubleEndedAddressableHeapTest { protected static final int SIZE = 100000; private static Comparator comparator; private static class TestComparator implements Comparator, Serializable { private static final long serialVersionUID = 1L; @Override public int compare(Integer o1, Integer o2) { if (o1 < o2) { return 1; } else if (o1 > o2) { return -1; } else { return 0; } } } @BeforeClass public static void setUpClass() { comparator = new TestComparator(); } protected abstract MergeableDoubleEndedAddressableHeap createHeap(); protected abstract MergeableDoubleEndedAddressableHeap createHeap(Comparator comparator); @Test public void testMeld1() { MergeableDoubleEndedAddressableHeap a = createHeap(); a.insert(10); a.insert(11); a.insert(12); a.insert(13); MergeableDoubleEndedAddressableHeap b = createHeap(); b.insert(14); b.insert(15); b.insert(16); Handle b4 = b.insert(17); a.meld(b); assertEquals(8, a.size()); assertTrue(b.isEmpty()); assertEquals(0, b.size()); b4.decreaseKey(9); assertEquals(Integer.valueOf(9), a.findMin().getKey()); } @Test public void testMeld2() { MergeableDoubleEndedAddressableHeap a = createHeap(); a.insert(10); a.insert(11); a.insert(12); a.insert(13); MergeableDoubleEndedAddressableHeap b = createHeap(); b.insert(14); b.insert(15); b.insert(16); Handle b4 = b.insert(17); MergeableDoubleEndedAddressableHeap c = createHeap(); c.insert(18); c.insert(19); c.insert(20); Handle c4 = c.insert(21); a.meld(b); a.meld(c); assertEquals(12, a.size()); assertTrue(b.isEmpty()); assertEquals(0, b.size()); assertTrue(c.isEmpty()); assertEquals(0, c.size()); assertEquals(Integer.valueOf(10), a.findMin().getKey()); b4.decreaseKey(9); assertEquals(Integer.valueOf(9), a.findMin().getKey()); c4.decreaseKey(8); assertEquals(Integer.valueOf(8), a.findMin().getKey()); } @Test(expected = IllegalStateException.class) public void testMultipleMelds() { MergeableDoubleEndedAddressableHeap a = createHeap(); a.insert(10); a.insert(11); a.insert(12); a.insert(13); MergeableDoubleEndedAddressableHeap b = createHeap(); b.insert(14); b.insert(15); b.insert(16); b.insert(17); MergeableDoubleEndedAddressableHeap c = createHeap(); c.insert(18); c.insert(19); c.insert(20); c.insert(21); a.meld(b); a.meld(b); } @Test(expected = IllegalStateException.class) public void testInsertAfterAMeld() { MergeableDoubleEndedAddressableHeap a = createHeap(); a.insert(10); a.insert(11); a.insert(12); a.insert(13); MergeableDoubleEndedAddressableHeap b = createHeap(); b.insert(14); b.insert(15); b.insert(16); b.insert(17); a.meld(b); b.insert(30); } @Test public void testCascadingMelds() { MergeableDoubleEndedAddressableHeap a = createHeap(); a.insert(10); a.insert(11); a.insert(12); a.insert(13); MergeableDoubleEndedAddressableHeap b = createHeap(); b.insert(14); b.insert(15); b.insert(16); b.insert(17); MergeableDoubleEndedAddressableHeap c = createHeap(); c.insert(18); c.insert(19); c.insert(20); c.insert(21); MergeableDoubleEndedAddressableHeap d = createHeap(); d.insert(22); d.insert(23); d.insert(24); d.insert(25); MergeableDoubleEndedAddressableHeap e = createHeap(); e.insert(26); e.insert(27); Handle e3 = e.insert(28); Handle e4 = e.insert(29); d.meld(e); c.meld(d); b.meld(c); a.meld(b); assertEquals(20, a.size()); assertEquals(0, b.size()); assertEquals(0, c.size()); assertEquals(0, d.size()); assertEquals(0, e.size()); assertEquals(Integer.valueOf(10), a.findMin().getKey()); e4.decreaseKey(9); assertEquals(Integer.valueOf(9), a.findMin().getKey()); e3.decreaseKey(8); assertEquals(Integer.valueOf(8), a.findMin().getKey()); e3.delete(); assertEquals(Integer.valueOf(9), a.findMin().getKey()); } @Test public void testMeldGeneric() { MergeableDoubleEndedAddressableHeap h1 = createHeap(); for (int i = 0; i < SIZE; i++) { h1.insert(2 * i); } MergeableDoubleEndedAddressableHeap h2 = createHeap(); for (int i = 0; i < SIZE; i++) { h2.insert(2 * i + 1); } h1.meld(h2); assertEquals(h1.size(), SIZE * 2); assertEquals(h2.size(), 0); Integer prev = null, cur; while (!h1.isEmpty()) { cur = h1.findMin().getKey(); h1.deleteMin(); if (prev != null) { assertTrue(prev.compareTo(cur) <= 0); } prev = cur; } } @Test public void testMeldGeneric1() { MergeableDoubleEndedAddressableHeap h1 = createHeap(); MergeableDoubleEndedAddressableHeap h2 = createHeap(); for (int i = 0; i < SIZE; i++) { h2.insert(i); } h1.meld(h2); assertEquals(h1.size(), SIZE); assertEquals(h2.size(), 0); Integer prev = null, cur; while (!h1.isEmpty()) { cur = h1.findMin().getKey(); h1.deleteMin(); if (prev != null) { assertTrue(prev.compareTo(cur) <= 0); } prev = cur; } } @Test public void testMeldGeneric2() { MergeableDoubleEndedAddressableHeap h1 = createHeap(); MergeableDoubleEndedAddressableHeap h2 = createHeap(); for (int i = 0; i < SIZE; i++) { h1.insert(i); } h1.meld(h2); assertEquals(h1.size(), SIZE); assertEquals(h2.size(), 0); Integer prev = null, cur; while (!h1.isEmpty()) { cur = h1.findMin().getKey(); h1.deleteMin(); if (prev != null) { assertTrue(prev.compareTo(cur) <= 0); } prev = cur; } } @Test public void testMeld() { MergeableDoubleEndedAddressableHeap h1 = createHeap(); MergeableDoubleEndedAddressableHeap h2 = createHeap(); for (int i = 0; i < SIZE; i++) { if (i % 2 == 0) { h1.insert(i); } else { h2.insert(i); } } h1.meld(h2); assertTrue(h2.isEmpty()); assertEquals(0, h2.size()); for (int i = 0; i < SIZE; i++) { assertEquals(Integer.valueOf(i), h1.findMin().getKey()); h1.deleteMin(); } assertTrue(h1.isEmpty()); } @Test public void testMeldWithComparator() { MergeableDoubleEndedAddressableHeap h1 = createHeap(comparator); MergeableDoubleEndedAddressableHeap h2 = createHeap(comparator); for (int i = 0; i < SIZE; i++) { if (i % 2 == 0) { h1.insert(i); } else { h2.insert(i); } } h1.meld(h2); assertTrue(h2.isEmpty()); assertEquals(0, h2.size()); for (int i = 0; i < SIZE; i++) { assertEquals(Integer.valueOf(SIZE - i - 1), h1.findMin().getKey()); h1.deleteMin(); } assertTrue(h1.isEmpty()); } @Test public void testMeldBadComparator() { MergeableDoubleEndedAddressableHeap h1 = createHeap(comparator); MergeableDoubleEndedAddressableHeap h2 = createHeap(new Comparator() { @Override public int compare(Integer o1, Integer o2) { return o1 - o2; } }); for (int i = 0; i < SIZE; i++) { if (i % 2 == 0) { h1.insert(i); } else { h2.insert(i); } } try { h1.meld(h2); fail("No!"); } catch (IllegalArgumentException ignored) { } } @Test public void testMeldBadComparator1() { MergeableDoubleEndedAddressableHeap h1 = createHeap(comparator); MergeableDoubleEndedAddressableHeap h2 = createHeap(null); try { h1.meld(h2); fail("No!"); } catch (IllegalArgumentException ignored) { } } @Test public void testMeldEvenOddSizes() { MergeableDoubleEndedAddressableHeap a = createHeap(); a.insert(10); a.insert(11); a.insert(12); a.insert(13); MergeableDoubleEndedAddressableHeap b = createHeap(); b.insert(14); b.insert(15); b.insert(16); a.meld(b); assertEquals(7, a.size()); assertTrue(b.isEmpty()); assertEquals(0, b.size()); assertEquals(Integer.valueOf(10), a.findMin().getKey()); assertEquals(Integer.valueOf(16), a.findMax().getKey()); } @Test public void testMeldOddOddSizes() { MergeableDoubleEndedAddressableHeap a = createHeap(); a.insert(10); a.insert(11); a.insert(12); MergeableDoubleEndedAddressableHeap b = createHeap(); b.insert(14); b.insert(15); b.insert(16); a.meld(b); assertEquals(6, a.size()); assertTrue(b.isEmpty()); assertEquals(0, b.size()); assertEquals(Integer.valueOf(10), a.findMin().getKey()); assertEquals(Integer.valueOf(16), a.findMax().getKey()); } @Test public void testMeldOddEvenSizes() { MergeableDoubleEndedAddressableHeap a = createHeap(); a.insert(10); a.insert(11); a.insert(12); MergeableDoubleEndedAddressableHeap b = createHeap(); b.insert(13); b.insert(14); b.insert(15); b.insert(16); a.meld(b); assertEquals(7, a.size()); assertTrue(b.isEmpty()); assertEquals(0, b.size()); assertEquals(Integer.valueOf(10), a.findMin().getKey()); assertEquals(Integer.valueOf(16), a.findMax().getKey()); } } jheaps-jheaps-0.14/src/test/java/org/jheaps/tree/AddressableHeapsRandomTest.java000066400000000000000000000145341367505655000277300ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.tree; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Random; import org.jheaps.AddressableHeap; import org.jheaps.AddressableHeap.Handle; import org.jheaps.array.BinaryArrayAddressableHeap; import org.junit.Test; public class AddressableHeapsRandomTest { private static final int SIZE = 250000; @Test public void test() { test(new Random()); } @Test public void testSeed13() { test(new Random(13)); } @Test public void testSeed37() { test(new Random(37)); } @Test public void testRandomDeletesSeed37() { testRandomDeletes(37); } @Test public void testRandomDelete() { testRandomDeletes(new Random().nextLong()); } private void test(Random rng) { final int classes = 8; @SuppressWarnings("unchecked") AddressableHeap[] h = (AddressableHeap[]) Array.newInstance(AddressableHeap.class, classes); h[0] = new PairingHeap(); h[1] = new BinaryTreeAddressableHeap(); h[2] = new FibonacciHeap(); h[3] = new BinaryArrayAddressableHeap(); h[4] = new CostlessMeldPairingHeap(); h[5] = new SkewHeap(); h[6] = new ReflectedPairingHeap(); h[7] = new LeftistHeap(); @SuppressWarnings("unchecked") List>[] s = (List>[]) Array.newInstance(List.class, classes); for (int j = 0; j < classes; j++) { s[j] = new ArrayList>(); } for (int i = 0; i < SIZE; i++) { Integer k = rng.nextInt(); for (int j = 0; j < classes; j++) { s[j].add(h[j].insert(k, null)); } for (int j = 1; j < classes; j++) { assertEquals(h[0].findMin().getKey().intValue(), h[j].findMin().getKey().intValue()); } } for (int i = 0; i < 5; i++) { @SuppressWarnings("unchecked") Iterator>[] it = (Iterator>[]) Array.newInstance(Iterator.class, classes); for (int j = 0; j < classes; j++) { it[j] = s[j].iterator(); } while (true) { boolean shouldStop = false; for (int j = 0; j < classes; j++) { if (!it[j].hasNext()) { shouldStop = true; break; } } if (shouldStop) { break; } @SuppressWarnings("unchecked") Handle[] handle = (Handle[]) Array.newInstance(Handle.class, classes); for (int j = 0; j < classes; j++) { handle[j] = it[j].next(); } int newKey = handle[0].getKey() / 2; if (newKey < handle[0].getKey()) { for (int j = 0; j < classes; j++) { handle[j].decreaseKey(newKey); } } for (int j = 1; j < classes; j++) { assertEquals(h[0].findMin().getKey().intValue(), h[j].findMin().getKey().intValue()); } } } while (!h[0].isEmpty()) { for (int j = 1; j < classes; j++) { assertEquals(h[0].findMin().getKey().intValue(), h[j].findMin().getKey().intValue()); } for (int j = 0; j < classes; j++) { h[j].deleteMin(); } } } private void testRandomDeletes(long seed) { final int classes = 8; @SuppressWarnings("unchecked") AddressableHeap[] h = (AddressableHeap[]) Array.newInstance(AddressableHeap.class, classes); h[0] = new PairingHeap(); h[1] = new BinaryTreeAddressableHeap(); h[2] = new FibonacciHeap(); h[3] = new BinaryArrayAddressableHeap(); h[4] = new CostlessMeldPairingHeap(); h[5] = new SkewHeap(); h[6] = new ReflectedPairingHeap(); h[7] = new LeftistHeap(); @SuppressWarnings("unchecked") List>[] s = (List>[]) Array.newInstance(List.class, classes); for (int i = 0; i < classes; i++) { s[i] = new ArrayList>(); } for (int i = 0; i < SIZE; i++) { for (int j = 0; j < classes; j++) { s[j].add(h[j].insert(i, null)); } for (int j = 1; j < classes; j++) { assertEquals(h[0].findMin().getKey().intValue(), h[j].findMin().getKey().intValue()); } } for (int j = 0; j < classes; j++) { Collections.shuffle(s[j], new Random(seed)); } for (int i = 0; i < SIZE; i++) { for (int j = 1; j < classes; j++) { assertEquals(h[0].findMin().getKey().intValue(), h[j].findMin().getKey().intValue()); } for (int j = 0; j < classes; j++) { s[j].get(i).delete(); } } for (int j = 0; j < classes; j++) { assertTrue(h[j].isEmpty()); } } } jheaps-jheaps-0.14/src/test/java/org/jheaps/tree/BinaryTreeAddressableHeapTest.java000066400000000000000000000024341367505655000303650ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.tree; import java.util.Comparator; import org.jheaps.AddressableHeap; import org.jheaps.tree.BinaryTreeAddressableHeap; public class BinaryTreeAddressableHeapTest extends AbstractAddressableHeapTest { @Override protected AddressableHeap createHeap() { return new BinaryTreeAddressableHeap(); } @Override protected AddressableHeap createHeap(Comparator comparator) { return new BinaryTreeAddressableHeap(comparator); } @Override protected AddressableHeap createHeapWithStringValues() { return new BinaryTreeAddressableHeap(); } } jheaps-jheaps-0.14/src/test/java/org/jheaps/tree/BinaryTreeSoftAddressableHeapTest.java000077500000000000000000000677061367505655000312410ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.tree; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Comparator; import java.util.NoSuchElementException; import java.util.Random; import org.jheaps.AddressableHeap; import org.jheaps.MergeableHeap; import org.jheaps.AddressableHeap.Handle; import org.jheaps.tree.BinaryTreeSoftAddressableHeap; import org.jheaps.tree.BinaryTreeSoftAddressableHeap.RootListNode; import org.jheaps.tree.BinaryTreeSoftAddressableHeap.SoftHandle; import org.jheaps.tree.BinaryTreeSoftAddressableHeap.TreeNode; import org.junit.BeforeClass; import org.junit.Test; public class BinaryTreeSoftAddressableHeapTest { private static final int SIZE = 100000; private static Comparator comparator; @BeforeClass public static void setUpClass() { comparator = new Comparator() { @Override public int compare(Integer o1, Integer o2) { if (o1 < o2) { return 1; } else if (o1 > o2) { return -1; } else { return 0; } } }; } @Test public void testNoCorruptionAsHeap() { final int n = SIZE; double epsilon = 1.0 / (n + 1); BinaryTreeSoftAddressableHeap a = new BinaryTreeSoftAddressableHeap(epsilon); for (int i = 0; i < n; i++) { a.insert(i); } for (int i = 0; i < n; i++) { assertEquals(i, a.deleteMin().getKey().intValue()); } assertTrue(a.isEmpty()); } @Test public void testNoCorruptionWithComparatorAsHeap() { final int n = SIZE; double epsilon = 1.0 / (n + 1); BinaryTreeSoftAddressableHeap a = new BinaryTreeSoftAddressableHeap(epsilon, comparator); for (int i = 0; i < n; i++) { a.insert(i); } for (int i = SIZE - 1; i >= 0; i--) { assertEquals(i, a.deleteMin().getKey().intValue()); } assertTrue(a.isEmpty()); } @Test public void testSort1RandomSeed1() { AddressableHeap h = new BinaryTreeSoftAddressableHeap(1.0 / (SIZE + 1)); Random generator = new Random(1); for (int i = 0; i < SIZE; i++) { h.insert(generator.nextInt()); } Integer prev = null, cur; while (!h.isEmpty()) { cur = h.deleteMin().getKey(); if (prev != null) { assertTrue(prev.compareTo(cur) <= 0); } prev = cur; } } @Test public void testSort1RandomSeed1WithComparator() { AddressableHeap h = new BinaryTreeSoftAddressableHeap(1.0 / (SIZE + 1), comparator); Random generator = new Random(1); for (int i = 0; i < SIZE; i++) { h.insert(generator.nextInt()); } Integer prev = null, cur; while (!h.isEmpty()) { cur = h.deleteMin().getKey(); if (prev != null) { assertTrue(prev.compareTo(cur) >= 0); } prev = cur; } } @Test public void testComparator() { AddressableHeap h = new BinaryTreeSoftAddressableHeap(0.5); assertNull(h.comparator()); } @Test public void testFindMinDeleteMinSameObject() { AddressableHeap h = new BinaryTreeSoftAddressableHeap(0.5); Random generator = new Random(1); for (int i = 0; i < SIZE; i++) { h.insert(generator.nextLong()); } while (!h.isEmpty()) { assertEquals(h.findMin(), h.deleteMin()); } } @Test public void testSoftHeap0() { testSoftHeapInsert(0.01, null); testSoftHeapInsert(0.01, comparator); testSoftHeapInsertDeleteMin(0.01, null); testSoftHeapInsertDeleteMin(0.01, comparator); testSoftHeapInsertDelete(0.01, null); testSoftHeapInsertDelete(0.01, comparator); testSoftHeapInsertDeleteDeleteMin(0.01, null); testSoftHeapInsertDeleteDeleteMin(0.01, comparator); } @Test public void testSoftHeap1() { testSoftHeapInsert(0.25, null); testSoftHeapInsert(0.25, comparator); testSoftHeapInsertDeleteMin(0.25, null); testSoftHeapInsertDeleteMin(0.25, comparator); testSoftHeapInsertDelete(0.25, null); testSoftHeapInsertDelete(0.25, comparator); testSoftHeapInsertDeleteDeleteMin(0.25, null); testSoftHeapInsertDeleteDeleteMin(0.25, comparator); } @Test public void testSoftHeap2() { testSoftHeapInsert(0.5, null); testSoftHeapInsert(0.5, comparator); testSoftHeapInsertDeleteMin(0.5, null); testSoftHeapInsertDeleteMin(0.5, comparator); testSoftHeapInsertDelete(0.5, null); testSoftHeapInsertDelete(0.5, comparator); testSoftHeapInsertDeleteDeleteMin(0.5, null); testSoftHeapInsertDeleteDeleteMin(0.5, comparator); } @Test public void testSoftHeap3() { testSoftHeapInsert(0.75, null); testSoftHeapInsert(0.75, comparator); testSoftHeapInsertDeleteMin(0.75, null); testSoftHeapInsertDeleteMin(0.75, comparator); testSoftHeapInsertDelete(0.75, null); testSoftHeapInsertDelete(0.75, comparator); testSoftHeapInsertDeleteDeleteMin(0.75, null); testSoftHeapInsertDeleteDeleteMin(0.75, comparator); } @Test public void testSoftHeap4() { testSoftHeapInsert(0.99, null); testSoftHeapInsert(0.99, comparator); testSoftHeapInsertDeleteMin(0.99, null); testSoftHeapInsertDeleteMin(0.99, comparator); testSoftHeapInsertDelete(0.99, null); testSoftHeapInsertDelete(0.99, comparator); testSoftHeapInsertDeleteDeleteMin(0.99, null); testSoftHeapInsertDeleteDeleteMin(0.99, comparator); } @Test(expected = IllegalArgumentException.class) public void testIllegalConstruction1() { new BinaryTreeSoftAddressableHeap(0.0); } @Test(expected = IllegalArgumentException.class) public void testIllegalConstruction2() { new BinaryTreeSoftAddressableHeap(1.0); } @Test(expected = IllegalArgumentException.class) public void testIllegalConstruction3() { new BinaryTreeSoftAddressableHeap(-1.0); } @Test(expected = IllegalArgumentException.class) public void testIllegalConstruction4() { new BinaryTreeSoftAddressableHeap(2.0); } @Test(expected = NoSuchElementException.class) public void testIllegalDeleteMin() { new BinaryTreeSoftAddressableHeap(0.5).deleteMin(); } @Test(expected = NoSuchElementException.class) public void testIllegalFindMin() { new BinaryTreeSoftAddressableHeap(0.5).findMin(); } @Test(expected = IllegalArgumentException.class) public void testIllegalDeleteTwice() { BinaryTreeSoftAddressableHeap h = new BinaryTreeSoftAddressableHeap(0.5); Handle e = h.insert(1); e.delete(); e.delete(); } @Test public void testGetValue() { AddressableHeap h = new BinaryTreeSoftAddressableHeap(0.5); Handle e = h.insert(1, "999"); assertEquals("999", e.getValue()); } @Test(expected = UnsupportedOperationException.class) public void testNoDecreaseKey() { AddressableHeap h = new BinaryTreeSoftAddressableHeap(0.5); Handle e = h.insert(1, "999"); e.decreaseKey(0); } @Test public void testIsEmpty() { AddressableHeap h = new BinaryTreeSoftAddressableHeap(0.5); assertTrue(h.isEmpty()); h.insert(1); assertFalse(h.isEmpty()); } @Test public void testClear() { AddressableHeap h = new BinaryTreeSoftAddressableHeap(0.5); assertTrue(h.isEmpty()); h.insert(1); h.insert(2); h.insert(3); h.insert(4); assertFalse(h.isEmpty()); assertEquals(4, h.size()); h.clear(); assertTrue(h.isEmpty()); assertEquals(0, h.size()); } @SuppressWarnings("unchecked") @Test public void testSerializable() throws IOException, ClassNotFoundException { AddressableHeap h = new BinaryTreeSoftAddressableHeap(1.0 / 16); for (long i = 0; i < 15; i++) { h.insert(i); } // write ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(h); oos.close(); byte[] data = baos.toByteArray(); // read ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data)); Object o = ois.readObject(); ois.close(); h = (AddressableHeap) o; for (int i = 0; i < 15; i++) { assertEquals(15 - i, h.size()); assertEquals(Long.valueOf(i), h.findMin().getKey()); h.deleteMin(); } assertTrue(h.isEmpty()); } @Test @SuppressWarnings("unchecked") public void testMeldGeneric() { AddressableHeap h1 = new BinaryTreeSoftAddressableHeap(1.0 / (SIZE + 1)); if (h1 instanceof MergeableHeap) { for (int i = 0; i < SIZE; i++) { h1.insert(2 * i); } AddressableHeap h2 = new BinaryTreeSoftAddressableHeap(1.0 / (SIZE + 1)); for (int i = 0; i < SIZE; i++) { h2.insert(2 * i + 1); } ((MergeableHeap) h1).meld((MergeableHeap) h2); assertEquals(h1.size(), SIZE * 2); assertEquals(h2.size(), 0); Integer prev = null, cur; while (!h1.isEmpty()) { cur = h1.findMin().getKey(); h1.deleteMin(); if (prev != null) { assertTrue(prev.compareTo(cur) <= 0); } prev = cur; } } } @Test public void testMeld1() { testSoftHeapMeld(SIZE, 0.10, null); testSoftHeapMeld(SIZE, 0.10, comparator); testSoftHeapMeld(SIZE, 0.25, null); testSoftHeapMeld(SIZE, 0.25, comparator); testSoftHeapMeld(SIZE, 0.5, null); testSoftHeapMeld(SIZE, 0.5, comparator); testSoftHeapMeld(SIZE, 0.75, null); testSoftHeapMeld(SIZE, 0.75, comparator); testSoftHeapMeld(SIZE, 0.95, null); testSoftHeapMeld(SIZE, 0.95, comparator); } @Test public void testMeld2() { testSoftHeapMeldSmallLarge(SIZE, 0.10, null); testSoftHeapMeldSmallLarge(SIZE, 0.10, comparator); testSoftHeapMeldSmallLarge(SIZE, 0.25, null); testSoftHeapMeldSmallLarge(SIZE, 0.25, comparator); testSoftHeapMeldSmallLarge(SIZE, 0.5, null); testSoftHeapMeldSmallLarge(SIZE, 0.5, comparator); testSoftHeapMeldSmallLarge(SIZE, 0.75, null); testSoftHeapMeldSmallLarge(SIZE, 0.75, comparator); testSoftHeapMeldSmallLarge(SIZE, 0.95, null); testSoftHeapMeldSmallLarge(SIZE, 0.95, comparator); } @Test(expected = IllegalArgumentException.class) public void testMeldWrong1() { BinaryTreeSoftAddressableHeap h1 = new BinaryTreeSoftAddressableHeap(0.5, comparator); BinaryTreeSoftAddressableHeap h2 = new BinaryTreeSoftAddressableHeap(0.5); h1.meld(h2); } @Test(expected = IllegalArgumentException.class) public void testMeldWrong2() { BinaryTreeSoftAddressableHeap h1 = new BinaryTreeSoftAddressableHeap(0.4); BinaryTreeSoftAddressableHeap h2 = new BinaryTreeSoftAddressableHeap(0.7); h1.meld(h2); } @Test(expected = IllegalArgumentException.class) public void testMeldWrong3() { BinaryTreeSoftAddressableHeap h1 = new BinaryTreeSoftAddressableHeap(0.4); BinaryTreeSoftAddressableHeap h2 = new BinaryTreeSoftAddressableHeap(0.4, comparator); h1.meld(h2); } @Test(expected = IllegalArgumentException.class) public void testMeldWrong4() { BinaryTreeSoftAddressableHeap h1 = new BinaryTreeSoftAddressableHeap(0.4, comparator); BinaryTreeSoftAddressableHeap h2 = new BinaryTreeSoftAddressableHeap(0.4, new Comparator() { @Override public int compare(Integer o1, Integer o2) { return comparator.compare(o1, o2); } }); h1.meld(h2); } @Test(expected = IllegalStateException.class) public void testMultipleMelds() { BinaryTreeSoftAddressableHeap a = new BinaryTreeSoftAddressableHeap(0.5); a.insert(10); a.insert(11); a.insert(12); a.insert(13); BinaryTreeSoftAddressableHeap b = new BinaryTreeSoftAddressableHeap(0.5); b.insert(14); b.insert(15); b.insert(16); b.insert(17); BinaryTreeSoftAddressableHeap c = new BinaryTreeSoftAddressableHeap(0.5); c.insert(18); c.insert(19); c.insert(20); c.insert(21); a.meld(b); a.meld(b); validateSoftHeap(a, 0.5, 12L); } @Test public void testDeleteAfterMultipleMelds() { BinaryTreeSoftAddressableHeap a = new BinaryTreeSoftAddressableHeap(0.5); a.insert(10); a.insert(11); a.insert(12); a.insert(13); BinaryTreeSoftAddressableHeap b = new BinaryTreeSoftAddressableHeap(0.5); b.insert(14); b.insert(15); Handle b3 = b.insert(16); b.insert(17); BinaryTreeSoftAddressableHeap c = new BinaryTreeSoftAddressableHeap(0.5); c.insert(18); c.insert(19); Handle c3 = c.insert(20); c.insert(21); a.meld(b); a.meld(c); validateSoftHeap(a, 0.5, 12L); b3.delete(); validateSoftHeap(a, 0.5, 12L); c3.delete(); validateSoftHeap(a, 0.5, 12L); } @Test(expected = IllegalStateException.class) public void testInsertAfterAMeld() { BinaryTreeSoftAddressableHeap a = new BinaryTreeSoftAddressableHeap(0.5); a.insert(10); a.insert(11); a.insert(12); a.insert(13); BinaryTreeSoftAddressableHeap b = new BinaryTreeSoftAddressableHeap(0.5); b.insert(14); b.insert(15); b.insert(16); b.insert(17); a.meld(b); b.insert(30); } @Test public void testCascadingMelds() { BinaryTreeSoftAddressableHeap a = new BinaryTreeSoftAddressableHeap(0.5); a.insert(10); a.insert(11); a.insert(12); a.insert(13); BinaryTreeSoftAddressableHeap b = new BinaryTreeSoftAddressableHeap(0.5); b.insert(14); b.insert(15); b.insert(16); b.insert(17); BinaryTreeSoftAddressableHeap c = new BinaryTreeSoftAddressableHeap(0.5); c.insert(18); c.insert(19); c.insert(20); c.insert(21); BinaryTreeSoftAddressableHeap d = new BinaryTreeSoftAddressableHeap(0.5); d.insert(22); d.insert(23); d.insert(24); d.insert(25); BinaryTreeSoftAddressableHeap e = new BinaryTreeSoftAddressableHeap(0.5); e.insert(26); e.insert(27); e.insert(28); e.insert(29); d.meld(e); c.meld(d); b.meld(c); a.meld(b); assertEquals(20, a.size()); assertEquals(0, b.size()); assertEquals(0, c.size()); assertEquals(0, d.size()); assertEquals(0, e.size()); validateSoftHeap(a, 0.5, 20L); } @Test public void testDeleteAfterCascadingMelds() { BinaryTreeSoftAddressableHeap a = new BinaryTreeSoftAddressableHeap(0.5); a.insert(10); a.insert(11); Handle a3 = a.insert(12); a.insert(13); BinaryTreeSoftAddressableHeap b = new BinaryTreeSoftAddressableHeap(0.5); b.insert(14); b.insert(15); Handle b3 = b.insert(16); b.insert(17); BinaryTreeSoftAddressableHeap c = new BinaryTreeSoftAddressableHeap(0.5); c.insert(18); c.insert(19); Handle c3 = c.insert(20); c.insert(21); BinaryTreeSoftAddressableHeap d = new BinaryTreeSoftAddressableHeap(0.5); d.insert(22); d.insert(23); Handle d3 = d.insert(24); d.insert(25); BinaryTreeSoftAddressableHeap e = new BinaryTreeSoftAddressableHeap(0.5); e.insert(26); e.insert(27); Handle e3 = e.insert(28); e.insert(29); d.meld(e); c.meld(d); b.meld(c); a.meld(b); assertEquals(20, a.size()); assertEquals(0, b.size()); assertEquals(0, c.size()); assertEquals(0, d.size()); assertEquals(0, e.size()); e3.delete(); validateSoftHeap(a, 0.5, 20L); d3.delete(); validateSoftHeap(a, 0.5, 20L); c3.delete(); validateSoftHeap(a, 0.5, 20L); b3.delete(); validateSoftHeap(a, 0.5, 20L); a3.delete(); validateSoftHeap(a, 0.5, 20L); } @Test(expected = NullPointerException.class) public void testInsertNullKey() { new BinaryTreeSoftAddressableHeap(0.5).insert(null); } private void testSoftHeapMeld(int n, double epsilon, Comparator comparator) { BinaryTreeSoftAddressableHeap h1 = new BinaryTreeSoftAddressableHeap(epsilon, comparator); BinaryTreeSoftAddressableHeap h2 = new BinaryTreeSoftAddressableHeap(epsilon, comparator); for (int i = 0; i < n; i++) { if (i % 2 == 0) { h1.insert(i); } else { h2.insert(i); } } validateSoftHeap(h1, epsilon, n / 2); assertEquals(n / 2, h1.size()); validateSoftHeap(h2, epsilon, n / 2); assertEquals(n / 2, h2.size()); h1.meld(h2); validateSoftHeap(h1, epsilon, n); validateSoftHeap(h2, epsilon, n); } private void testSoftHeapMeldSmallLarge(int n, double epsilon, Comparator comparator) { BinaryTreeSoftAddressableHeap h1 = new BinaryTreeSoftAddressableHeap(epsilon, comparator); BinaryTreeSoftAddressableHeap h2 = new BinaryTreeSoftAddressableHeap(epsilon, comparator); for (int i = 0; i < n / 3; i++) { h1.insert(i); } for (int i = n / 3; i < n; i++) { h2.insert(i); } validateSoftHeap(h1, epsilon, n / 3); assertEquals(n / 3, h1.size()); validateSoftHeap(h2, epsilon, 2 * n / 3); assertEquals((int) Math.ceil(2.0 * n / 3.0), h2.size()); h1.meld(h2); validateSoftHeap(h1, epsilon, n); validateSoftHeap(h2, epsilon, n); } private void testSoftHeapInsert(double epsilon, Comparator comparator) { final int n = SIZE; BinaryTreeSoftAddressableHeap a = new BinaryTreeSoftAddressableHeap(epsilon, comparator); for (int i = 0; i < n; i++) { a.insert(i); } validateSoftHeap(a, epsilon, n); } private void testSoftHeapInsertDeleteMin(double epsilon, Comparator comparator) { final int n = SIZE; BinaryTreeSoftAddressableHeap a = new BinaryTreeSoftAddressableHeap(epsilon, comparator); for (int i = 0; i < n; i++) { a.insert(i); } for (int i = 0; i < n / 4; i++) { a.deleteMin(); } validateSoftHeap(a, epsilon, n); } @SuppressWarnings("unchecked") private void testSoftHeapInsertDelete(double epsilon, Comparator comparator) { final int n = SIZE; BinaryTreeSoftAddressableHeap h = new BinaryTreeSoftAddressableHeap(epsilon, comparator); AddressableHeap.Handle array[]; array = new AddressableHeap.Handle[n]; for (int i = 0; i < n; i++) { array[i] = h.insert(i); } for (int i = 0; i < n / 4; i++) { array[i].delete(); } validateSoftHeap(h, epsilon, n); } @SuppressWarnings("unchecked") private void testSoftHeapInsertDeleteDeleteMin(double epsilon, Comparator comparator) { final int n = SIZE; BinaryTreeSoftAddressableHeap h = new BinaryTreeSoftAddressableHeap(epsilon, comparator); AddressableHeap.Handle array[]; array = new AddressableHeap.Handle[n]; for (int i = 0; i < n; i++) { array[i] = h.insert(i); } for (int i = 0; i < n / 4; i++) { h.deleteMin(); } for (int i = n - 1; i >= 3 * n / 4; i--) { try { array[i].delete(); } catch (IllegalArgumentException e) { // ignore, already deleted due to corruption } } validateSoftHeap(h, epsilon, n); } /** * Validate the invariants of a soft heap. * * @param h * the soft heap * @param epsilon * the error rate of the soft heap * @param totalInsertedElements * the total number of elements added in the heap */ @SuppressWarnings("unchecked") private static void validateSoftHeap(BinaryTreeSoftAddressableHeap h, double epsilon, long totalInsertedElements) { long total = 0; long corrupted = 0; Comparator comparator = h.comparator(); RootListNode cur = h.rootList.head; while (cur != null) { // validate each heap KeyCount kc = validateRoot(cur.root, comparator); // keep total count of total and corrupted elements total += kc.total; corrupted += kc.corrupted; cur = cur.next; } assertEquals(total, h.size()); assertTrue("Too many corrupted elemenets", corrupted <= totalInsertedElements * epsilon); // validate suffix min pointers K minSoFar = null; cur = h.rootList.tail; while (cur != null) { // find min by hand if (minSoFar == null) { minSoFar = cur.root.cKey; } else { if (comparator == null) { if (((Comparable) cur.root.cKey).compareTo(minSoFar) <= 0) { minSoFar = cur.root.cKey; } } else { if (comparator.compare(cur.root.cKey, minSoFar) <= 0) { minSoFar = cur.root.cKey; } } } // compare with suffix min assertEquals(minSoFar, cur.suffixMin.root.cKey); // keep total count of total and corrupted elements cur = cur.prev; } } @SuppressWarnings("unchecked") private static KeyCount validateRoot(TreeNode root, Comparator comparator) { assertTrue(root.parent != null); if (root.left != null) { assertEquals(root.left.parent, root); assertTrue(root.rank > root.left.rank); if (comparator == null) { assertTrue(((Comparable) root.cKey).compareTo(root.left.cKey) <= 0); } else { assertTrue(comparator.compare(root.cKey, root.left.cKey) <= 0); } } if (root.right != null) { assertEquals(root.right.parent, root); assertTrue(root.rank > root.right.rank); if (comparator == null) { assertTrue(((Comparable) root.cKey).compareTo(root.right.cKey) <= 0); } else { assertTrue(comparator.compare(root.cKey, root.right.cKey) <= 0); } } if (root.left != null && root.right != null) { assertEquals(root.left.rank, root.right.rank); } assertTrue(root.cKey != null); assertTrue(root.cHead != null); assertTrue(root.cHead.tree == root); assertTrue(root.cSize > 0); long total = 0; long corrupted = 0; SoftHandle e = root.cHead; while (e != null) { total++; if (comparator == null) { if (((Comparable) e.key).compareTo(root.cKey) < 0) { corrupted++; } } else { if (comparator.compare(e.key, root.cKey) < 0) { corrupted++; } } e = e.next; } /* * Due to ghost elements (if delete operation is used) */ assertTrue(total > 0 && total <= root.cSize); if (root.left != null) { KeyCount leftKeyCount = validateRoot(root.left, comparator); total += leftKeyCount.total; corrupted += leftKeyCount.corrupted; } if (root.right != null) { KeyCount rightKeyCount = validateRoot(root.right, comparator); total += rightKeyCount.total; corrupted += rightKeyCount.corrupted; } return new KeyCount(total, corrupted); } private static class KeyCount { long total; long corrupted; public KeyCount(long total, long corrupted) { this.total = total; this.corrupted = corrupted; } } } jheaps-jheaps-0.14/src/test/java/org/jheaps/tree/BinaryTreeSoftHeapTest.java000066400000000000000000000426701367505655000270750ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.tree; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Comparator; import java.util.NoSuchElementException; import java.util.Random; import org.jheaps.Heap; import org.jheaps.MergeableHeap; import org.jheaps.tree.BinaryTreeSoftHeap; import org.jheaps.tree.BinaryTreeSoftHeap.RootListNode; import org.jheaps.tree.BinaryTreeSoftHeap.SoftHandle; import org.jheaps.tree.BinaryTreeSoftHeap.TreeNode; import org.junit.BeforeClass; import org.junit.Test; public class BinaryTreeSoftHeapTest { private static final int SIZE = 100000; private static Comparator comparator; @BeforeClass public static void setUpClass() { comparator = new Comparator() { @Override public int compare(Integer o1, Integer o2) { if (o1 < o2) { return 1; } else if (o1 > o2) { return -1; } else { return 0; } } }; } @Test public void testNoCorruptionAsHeap() { final int n = SIZE; double epsilon = 1.0 / (n + 1); BinaryTreeSoftHeap a = new BinaryTreeSoftHeap(epsilon); for (int i = 0; i < n; i++) { a.insert(i); } for (int i = 0; i < n; i++) { assertEquals(i, a.deleteMin().intValue()); } assertTrue(a.isEmpty()); } @Test public void testNoCorruptionWithComparatorAsHeap() { final int n = SIZE; double epsilon = 1.0 / (n + 1); BinaryTreeSoftHeap a = new BinaryTreeSoftHeap(epsilon, comparator); for (int i = 0; i < n; i++) { a.insert(i); } for (int i = SIZE - 1; i >= 0; i--) { assertEquals(i, a.deleteMin().intValue()); } assertTrue(a.isEmpty()); } @Test public void testSort1RandomSeed1() { Heap h = new BinaryTreeSoftHeap(1.0 / (SIZE + 1)); Random generator = new Random(1); for (int i = 0; i < SIZE; i++) { h.insert(generator.nextInt()); } Integer prev = null, cur; while (!h.isEmpty()) { cur = h.deleteMin(); if (prev != null) { assertTrue(prev.compareTo(cur) <= 0); } prev = cur; } } @Test public void testSort1RandomSeed1WithComparator() { Heap h = new BinaryTreeSoftHeap(1.0 / (SIZE + 1), comparator); Random generator = new Random(1); for (int i = 0; i < SIZE; i++) { h.insert(generator.nextInt()); } Integer prev = null, cur; while (!h.isEmpty()) { cur = h.deleteMin(); if (prev != null) { assertTrue(prev.compareTo(cur) >= 0); } prev = cur; } } @Test public void testComparator() { Heap h = new BinaryTreeSoftHeap(0.5); assertNull(h.comparator()); } @Test public void testFindMinDeleteMinSameObject() { Heap h = new BinaryTreeSoftHeap(0.5); Random generator = new Random(1); for (int i = 0; i < SIZE; i++) { h.insert(generator.nextLong()); } while (!h.isEmpty()) { assertEquals(h.findMin(), h.deleteMin()); } } @Test public void testSoftHeap0() { testSoftHeapInsert(0.01, null); testSoftHeapInsert(0.01, comparator); testSoftHeapInsertDeleteMin(0.01, null); testSoftHeapInsertDeleteMin(0.01, comparator); } @Test public void testSoftHeap1() { testSoftHeapInsert(0.25, null); testSoftHeapInsert(0.25, comparator); testSoftHeapInsertDeleteMin(0.25, null); testSoftHeapInsertDeleteMin(0.25, comparator); } @Test public void testSoftHeap2() { testSoftHeapInsert(0.5, null); testSoftHeapInsert(0.5, comparator); testSoftHeapInsertDeleteMin(0.5, null); testSoftHeapInsertDeleteMin(0.5, comparator); } @Test public void testSoftHeap3() { testSoftHeapInsert(0.75, null); testSoftHeapInsert(0.75, comparator); testSoftHeapInsertDeleteMin(0.75, null); testSoftHeapInsertDeleteMin(0.75, comparator); } @Test public void testSoftHeap4() { testSoftHeapInsert(0.99, null); testSoftHeapInsert(0.99, comparator); testSoftHeapInsertDeleteMin(0.99, null); testSoftHeapInsertDeleteMin(0.99, comparator); } @Test(expected = IllegalArgumentException.class) public void testIllegalConstruction1() { new BinaryTreeSoftHeap(0.0); } @Test(expected = IllegalArgumentException.class) public void testIllegalConstruction2() { new BinaryTreeSoftHeap(1.0); } @Test(expected = IllegalArgumentException.class) public void testIllegalConstruction3() { new BinaryTreeSoftHeap(-1.0); } @Test(expected = IllegalArgumentException.class) public void testIllegalConstruction4() { new BinaryTreeSoftHeap(2.0); } @Test(expected = NoSuchElementException.class) public void testIllegalDeleteMin() { new BinaryTreeSoftHeap(0.5).deleteMin(); } @Test(expected = NoSuchElementException.class) public void testIllegalFindMin() { new BinaryTreeSoftHeap(0.5).findMin(); } @Test public void testIsEmpty() { BinaryTreeSoftHeap h = new BinaryTreeSoftHeap(0.5); assertTrue(h.isEmpty()); h.insert(1); assertFalse(h.isEmpty()); } @Test public void testClear() { BinaryTreeSoftHeap h = new BinaryTreeSoftHeap(0.5); assertTrue(h.isEmpty()); h.insert(1); h.insert(2); h.insert(3); h.insert(4); assertFalse(h.isEmpty()); assertEquals(4, h.size()); h.clear(); assertTrue(h.isEmpty()); assertEquals(0, h.size()); } @SuppressWarnings("unchecked") @Test public void testSerializable() throws IOException, ClassNotFoundException { Heap h = new BinaryTreeSoftHeap(1.0 / 16); for (long i = 0; i < 15; i++) { h.insert(i); } // write ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(h); oos.close(); byte[] data = baos.toByteArray(); // read ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data)); Object o = ois.readObject(); ois.close(); h = (Heap) o; for (int i = 0; i < 15; i++) { assertEquals(15 - i, h.size()); assertEquals(Long.valueOf(i), h.findMin()); h.deleteMin(); } assertTrue(h.isEmpty()); } @Test public void testMeldGeneric() { Heap h1 = new BinaryTreeSoftHeap(1.0 / (SIZE + 1)); if (h1 instanceof MergeableHeap) { for (int i = 0; i < SIZE; i++) { h1.insert(2 * i); } Heap h2 = new BinaryTreeSoftHeap(1.0 / (SIZE + 1)); for (int i = 0; i < SIZE; i++) { h2.insert(2 * i + 1); } ((MergeableHeap) h1).meld((MergeableHeap) h2); assertEquals(h1.size(), SIZE * 2); assertEquals(h2.size(), 0); Integer prev = null, cur; while (!h1.isEmpty()) { cur = h1.findMin(); h1.deleteMin(); if (prev != null) { assertTrue(prev.compareTo(cur) <= 0); } prev = cur; } } } @Test public void testMeld1() { testSoftHeapMeld(SIZE, 0.10, null); testSoftHeapMeld(SIZE, 0.10, comparator); testSoftHeapMeld(SIZE, 0.25, null); testSoftHeapMeld(SIZE, 0.25, comparator); testSoftHeapMeld(SIZE, 0.5, null); testSoftHeapMeld(SIZE, 0.5, comparator); testSoftHeapMeld(SIZE, 0.75, null); testSoftHeapMeld(SIZE, 0.75, comparator); testSoftHeapMeld(SIZE, 0.95, null); testSoftHeapMeld(SIZE, 0.95, comparator); } @Test public void testMeld2() { testSoftHeapMeldSmallLarge(SIZE, 0.10, null); testSoftHeapMeldSmallLarge(SIZE, 0.10, comparator); testSoftHeapMeldSmallLarge(SIZE, 0.25, null); testSoftHeapMeldSmallLarge(SIZE, 0.25, comparator); testSoftHeapMeldSmallLarge(SIZE, 0.5, null); testSoftHeapMeldSmallLarge(SIZE, 0.5, comparator); testSoftHeapMeldSmallLarge(SIZE, 0.75, null); testSoftHeapMeldSmallLarge(SIZE, 0.75, comparator); testSoftHeapMeldSmallLarge(SIZE, 0.95, null); testSoftHeapMeldSmallLarge(SIZE, 0.95, comparator); } @Test(expected = IllegalArgumentException.class) public void testMeldWrong1() { MergeableHeap h1 = new BinaryTreeSoftHeap(0.5, comparator); MergeableHeap h2 = new BinaryTreeSoftHeap(0.5); h1.meld(h2); } @Test(expected = IllegalArgumentException.class) public void testMeldWrong2() { MergeableHeap h1 = new BinaryTreeSoftHeap(0.4); MergeableHeap h2 = new BinaryTreeSoftHeap(0.7); h1.meld(h2); } @Test(expected = IllegalArgumentException.class) public void testMeldWrong3() { MergeableHeap h1 = new BinaryTreeSoftHeap(0.4); MergeableHeap h2 = new BinaryTreeSoftHeap(0.4, comparator); h1.meld(h2); } @Test(expected = IllegalArgumentException.class) public void testMeldWrong4() { MergeableHeap h1 = new BinaryTreeSoftHeap(0.4, comparator); MergeableHeap h2 = new BinaryTreeSoftHeap(0.4, new Comparator() { @Override public int compare(Integer o1, Integer o2) { return comparator.compare(o1, o2); } }); h1.meld(h2); } private void testSoftHeapMeld(int n, double epsilon, Comparator comparator) { BinaryTreeSoftHeap h1 = new BinaryTreeSoftHeap(epsilon, comparator); BinaryTreeSoftHeap h2 = new BinaryTreeSoftHeap(epsilon, comparator); for (int i = 0; i < n; i++) { if (i % 2 == 0) { h1.insert(i); } else { h2.insert(i); } } validateSoftHeap(h1, epsilon, n / 2); assertEquals(n / 2, h1.size()); validateSoftHeap(h2, epsilon, n / 2); assertEquals(n / 2, h2.size()); h1.meld(h2); validateSoftHeap(h1, epsilon, n); validateSoftHeap(h2, epsilon, n); } private void testSoftHeapMeldSmallLarge(int n, double epsilon, Comparator comparator) { BinaryTreeSoftHeap h1 = new BinaryTreeSoftHeap(epsilon, comparator); BinaryTreeSoftHeap h2 = new BinaryTreeSoftHeap(epsilon, comparator); for (int i = 0; i < n / 3; i++) { h1.insert(i); } for (int i = n / 3; i < n; i++) { h2.insert(i); } validateSoftHeap(h1, epsilon, n / 3); assertEquals(n / 3, h1.size()); validateSoftHeap(h2, epsilon, 2 * n / 3); assertEquals((int) Math.ceil(2.0 * n / 3.0), h2.size()); h1.meld(h2); validateSoftHeap(h1, epsilon, n); validateSoftHeap(h2, epsilon, n); } private void testSoftHeapInsert(double epsilon, Comparator comparator) { final int n = SIZE; BinaryTreeSoftHeap a = new BinaryTreeSoftHeap(epsilon, comparator); for (int i = 0; i < n; i++) { a.insert(i); } validateSoftHeap(a, epsilon, n); } private void testSoftHeapInsertDeleteMin(double epsilon, Comparator comparator) { final int n = SIZE; BinaryTreeSoftHeap a = new BinaryTreeSoftHeap(epsilon, comparator); for (int i = 0; i < n; i++) { a.insert(i); } for (int i = 0; i < n / 4; i++) { a.deleteMin(); } validateSoftHeap(a, epsilon, n); } /** * Validate the invariants of a soft heap. * * @param h * the soft heap * @param epsilon * the error rate of the soft heap * @param totalInsertedElements * the total number of elements added in the heap */ @SuppressWarnings("unchecked") private static void validateSoftHeap(BinaryTreeSoftHeap h, double epsilon, long totalInsertedElements) { long total = 0; long corrupted = 0; Comparator comparator = h.comparator(); RootListNode cur = h.rootList.head; while (cur != null) { // validate each heap KeyCount kc = validateRoot(cur.root, comparator); // keep total count of total and corrupted elements total += kc.total; corrupted += kc.corrupted; cur = cur.next; } assertEquals(total, h.size()); assertTrue("Too many corrupted elemenets", corrupted <= totalInsertedElements * epsilon); // validate suffix min pointers K minSoFar = null; cur = h.rootList.tail; while (cur != null) { // find min by hand if (minSoFar == null) { minSoFar = cur.root.cKey; } else { if (comparator == null) { if (((Comparable) cur.root.cKey).compareTo(minSoFar) <= 0) { minSoFar = cur.root.cKey; } } else { if (comparator.compare(cur.root.cKey, minSoFar) <= 0) { minSoFar = cur.root.cKey; } } } // compare with suffix min assertEquals(minSoFar, cur.suffixMin.root.cKey); // keep total count of total and corrupted elements cur = cur.prev; } } @SuppressWarnings("unchecked") private static KeyCount validateRoot(TreeNode root, Comparator comparator) { if (root.left != null) { assertTrue(root.rank > root.left.rank); if (comparator == null) { assertTrue(((Comparable) root.cKey).compareTo(root.left.cKey) <= 0); } else { assertTrue(comparator.compare(root.cKey, root.left.cKey) <= 0); } } if (root.right != null) { assertTrue(root.rank > root.right.rank); if (comparator == null) { assertTrue(((Comparable) root.cKey).compareTo(root.right.cKey) <= 0); } else { assertTrue(comparator.compare(root.cKey, root.right.cKey) <= 0); } } if (root.left != null && root.right != null) { assertEquals(root.left.rank, root.right.rank); } assertTrue(root.cKey != null); assertTrue(root.cHead != null); assertTrue(root.cSize > 0); long total = 0; long corrupted = 0; SoftHandle e = root.cHead; while (e != null) { total++; if (comparator == null) { if (((Comparable) e.key).compareTo(root.cKey) < 0) { corrupted++; } } else { if (comparator.compare(e.key, root.cKey) < 0) { corrupted++; } } e = e.next; } assertEquals(total, root.cSize); if (root.left != null) { KeyCount leftKeyCount = validateRoot(root.left, comparator); total += leftKeyCount.total; corrupted += leftKeyCount.corrupted; } if (root.right != null) { KeyCount rightKeyCount = validateRoot(root.right, comparator); total += rightKeyCount.total; corrupted += rightKeyCount.corrupted; } return new KeyCount(total, corrupted); } private static class KeyCount { long total; long corrupted; public KeyCount(long total, long corrupted) { this.total = total; this.corrupted = corrupted; } } } jheaps-jheaps-0.14/src/test/java/org/jheaps/tree/CostlessMeldPairingHeapAddressableHeapTest.java000066400000000000000000000024411367505655000330300ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.tree; import java.util.Comparator; import org.jheaps.AddressableHeap; import org.jheaps.tree.CostlessMeldPairingHeap; public class CostlessMeldPairingHeapAddressableHeapTest extends AbstractAddressableHeapTest { @Override protected AddressableHeap createHeap() { return new CostlessMeldPairingHeap(); } @Override protected AddressableHeap createHeap(Comparator comparator) { return new CostlessMeldPairingHeap(comparator); } @Override protected AddressableHeap createHeapWithStringValues() { return new CostlessMeldPairingHeap(); } } CostlessMeldPairingHeapMergeableAddressableHeapTest.java000066400000000000000000000023241367505655000345550ustar00rootroot00000000000000jheaps-jheaps-0.14/src/test/java/org/jheaps/tree/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.tree; import java.util.Comparator; import org.jheaps.MergeableAddressableHeap; import org.jheaps.tree.CostlessMeldPairingHeap; public class CostlessMeldPairingHeapMergeableAddressableHeapTest extends AbstractMergeableAddressableHeapTest { protected MergeableAddressableHeap createHeap() { return new CostlessMeldPairingHeap(); } @Override protected MergeableAddressableHeap createHeap(Comparator comparator) { return new CostlessMeldPairingHeap(comparator); } } jheaps-jheaps-0.14/src/test/java/org/jheaps/tree/D16DaryTreeAddressableHeapTest.java000066400000000000000000000023551367505655000303150ustar00rootroot00000000000000/* * (C) Copyright 2014-2019, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.tree; import java.util.Comparator; import org.jheaps.AddressableHeap; public class D16DaryTreeAddressableHeapTest extends AbstractAddressableHeapTest { @Override protected AddressableHeap createHeap() { return new DaryTreeAddressableHeap(16); } @Override protected AddressableHeap createHeap(Comparator comparator) { return new DaryTreeAddressableHeap(16, comparator); } @Override protected AddressableHeap createHeapWithStringValues() { return new DaryTreeAddressableHeap(16); } } jheaps-jheaps-0.14/src/test/java/org/jheaps/tree/D2DaryTreeAddressableHeapTest.java000066400000000000000000000023511367505655000302240ustar00rootroot00000000000000/* * (C) Copyright 2014-2019, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.tree; import java.util.Comparator; import org.jheaps.AddressableHeap; public class D2DaryTreeAddressableHeapTest extends AbstractAddressableHeapTest { @Override protected AddressableHeap createHeap() { return new DaryTreeAddressableHeap(2); } @Override protected AddressableHeap createHeap(Comparator comparator) { return new DaryTreeAddressableHeap(2, comparator); } @Override protected AddressableHeap createHeapWithStringValues() { return new DaryTreeAddressableHeap(2); } } jheaps-jheaps-0.14/src/test/java/org/jheaps/tree/D4DaryTreeAddressableHeapTest.java000066400000000000000000000023511367505655000302260ustar00rootroot00000000000000/* * (C) Copyright 2014-2019, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.tree; import java.util.Comparator; import org.jheaps.AddressableHeap; public class D4DaryTreeAddressableHeapTest extends AbstractAddressableHeapTest { @Override protected AddressableHeap createHeap() { return new DaryTreeAddressableHeap(4); } @Override protected AddressableHeap createHeap(Comparator comparator) { return new DaryTreeAddressableHeap(4, comparator); } @Override protected AddressableHeap createHeapWithStringValues() { return new DaryTreeAddressableHeap(4); } } jheaps-jheaps-0.14/src/test/java/org/jheaps/tree/D8DaryTreeAddressableHeapTest.java000066400000000000000000000023511367505655000302320ustar00rootroot00000000000000/* * (C) Copyright 2014-2019, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.tree; import java.util.Comparator; import org.jheaps.AddressableHeap; public class D8DaryTreeAddressableHeapTest extends AbstractAddressableHeapTest { @Override protected AddressableHeap createHeap() { return new DaryTreeAddressableHeap(8); } @Override protected AddressableHeap createHeap(Comparator comparator) { return new DaryTreeAddressableHeap(8, comparator); } @Override protected AddressableHeap createHeapWithStringValues() { return new DaryTreeAddressableHeap(8); } } jheaps-jheaps-0.14/src/test/java/org/jheaps/tree/FibonacciHeapAddressableHeapTest.java000066400000000000000000000024341367505655000307740ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.tree; import java.util.Comparator; import org.jheaps.AddressableHeap; import org.jheaps.tree.FibonacciHeap; public class FibonacciHeapAddressableHeapTest extends AbstractAddressableHeapTest { @Override protected AddressableHeap createHeap() { return new FibonacciHeap(); } @Override protected AddressableHeap createHeap(Comparator comparator) { return new FibonacciHeap(comparator); } @Override protected AddressableHeap createHeapWithStringValues() { return new FibonacciHeap(); } } jheaps-jheaps-0.14/src/test/java/org/jheaps/tree/FibonacciHeapMergeableAddressableHeapTest.java000066400000000000000000000022541367505655000326000ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.tree; import java.util.Comparator; import org.jheaps.MergeableAddressableHeap; import org.jheaps.tree.FibonacciHeap; public class FibonacciHeapMergeableAddressableHeapTest extends AbstractMergeableAddressableHeapTest { protected MergeableAddressableHeap createHeap() { return new FibonacciHeap(); } @Override protected MergeableAddressableHeap createHeap(Comparator comparator) { return new FibonacciHeap(comparator); } } jheaps-jheaps-0.14/src/test/java/org/jheaps/tree/LeftistHeapAddressableHeapTest.java000066400000000000000000000023561367505655000305340ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.tree; import java.util.Comparator; import org.jheaps.AddressableHeap; public class LeftistHeapAddressableHeapTest extends AbstractAddressableHeapTest { @Override protected AddressableHeap createHeap() { return new LeftistHeap(); } @Override protected AddressableHeap createHeap(Comparator comparator) { return new LeftistHeap(comparator); } @Override protected AddressableHeap createHeapWithStringValues() { return new LeftistHeap(); } } jheaps-jheaps-0.14/src/test/java/org/jheaps/tree/LeftistHeapMergeableAddressableHeapTest.java000066400000000000000000000022161367505655000323330ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.tree; import java.util.Comparator; import org.jheaps.MergeableAddressableHeap; public class LeftistHeapMergeableAddressableHeapTest extends AbstractMergeableAddressableHeapTest { @Override protected MergeableAddressableHeap createHeap() { return new LeftistHeap(); } @Override protected MergeableAddressableHeap createHeap(Comparator comparator) { return new LeftistHeap(comparator); } } jheaps-jheaps-0.14/src/test/java/org/jheaps/tree/PairingHeapAddressableHeapTest.java000066400000000000000000000023451367505655000305110ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.tree; import java.util.Comparator; import org.jheaps.AddressableHeap; import org.jheaps.tree.PairingHeap; public class PairingHeapAddressableHeapTest extends AbstractAddressableHeapTest { @Override protected AddressableHeap createHeap() { return new PairingHeap(); } @Override protected AddressableHeap createHeap(Comparator comparator) { return new PairingHeap(comparator); } @Override protected AddressableHeap createHeapWithStringValues() { return new PairingHeap(); } } jheaps-jheaps-0.14/src/test/java/org/jheaps/tree/PairingHeapMergeableAddressableHeapTest.java000066400000000000000000000022441367505655000323130ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.tree; import java.util.Comparator; import org.jheaps.MergeableAddressableHeap; import org.jheaps.tree.PairingHeap; public class PairingHeapMergeableAddressableHeapTest extends AbstractMergeableAddressableHeapTest { protected MergeableAddressableHeap createHeap() { return new PairingHeap(); } @Override protected MergeableAddressableHeap createHeap(Comparator comparator) { return new PairingHeap(comparator); } } jheaps-jheaps-0.14/src/test/java/org/jheaps/tree/RankPairingHeapAddressableHeapTest.java000066400000000000000000000023211367505655000313170ustar00rootroot00000000000000/* * (C) Copyright 2014-2019, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.tree; import java.util.Comparator; import org.jheaps.AddressableHeap; public class RankPairingHeapAddressableHeapTest extends AbstractAddressableHeapTest { @Override protected AddressableHeap createHeap() { return new RankPairingHeap(); } @Override protected AddressableHeap createHeap(Comparator comparator) { return new RankPairingHeap(comparator); } @Override protected AddressableHeap createHeapWithStringValues() { return new RankPairingHeap(); } } jheaps-jheaps-0.14/src/test/java/org/jheaps/tree/RankPairingHeapMergeableAddressableHeapTest.java000066400000000000000000000022141367505655000331240ustar00rootroot00000000000000/* * (C) Copyright 2014-2019, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.tree; import java.util.Comparator; import org.jheaps.MergeableAddressableHeap; public class RankPairingHeapMergeableAddressableHeapTest extends AbstractMergeableAddressableHeapTest { protected MergeableAddressableHeap createHeap() { return new RankPairingHeap(); } @Override protected MergeableAddressableHeap createHeap(Comparator comparator) { return new RankPairingHeap(comparator); } } jheaps-jheaps-0.14/src/test/java/org/jheaps/tree/ReflectedFibonacciHeapAddressableHeapTest.java000066400000000000000000000024361367505655000326140ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.tree; import java.util.Comparator; import org.jheaps.AddressableHeap; public class ReflectedFibonacciHeapAddressableHeapTest extends AbstractAddressableHeapTest { @Override protected AddressableHeap createHeap() { return new ReflectedFibonacciHeap(); } @Override protected AddressableHeap createHeap(Comparator comparator) { return new ReflectedFibonacciHeap(comparator); } @Override protected AddressableHeap createHeapWithStringValues() { return new ReflectedFibonacciHeap(null); } } ReflectedFibonacciHeapDoubleEndedAddressableHeapTest.java000066400000000000000000000025341367505655000346270ustar00rootroot00000000000000jheaps-jheaps-0.14/src/test/java/org/jheaps/tree/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.tree; import java.util.Comparator; import org.jheaps.DoubleEndedAddressableHeap; public class ReflectedFibonacciHeapDoubleEndedAddressableHeapTest extends AbstractDoubleEndedAddressableHeapTest { @Override protected DoubleEndedAddressableHeap createHeap() { return new ReflectedFibonacciHeap(); } @Override protected DoubleEndedAddressableHeap createHeap(Comparator comparator) { return new ReflectedFibonacciHeap(comparator); } @Override protected DoubleEndedAddressableHeap createHeapWithStringValues() { return new ReflectedFibonacciHeap(); } } jheaps-jheaps-0.14/src/test/java/org/jheaps/tree/ReflectedPairingHeapAddressableHeapTest.java000077500000000000000000000025071367505655000323320ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.tree; import java.util.Comparator; import org.jheaps.AddressableHeap; import org.jheaps.tree.ReflectedPairingHeap; public class ReflectedPairingHeapAddressableHeapTest extends AbstractAddressableHeapTest { @Override protected AddressableHeap createHeap() { return new ReflectedPairingHeap(null); } @Override protected AddressableHeap createHeap(Comparator comparator) { return new ReflectedPairingHeap(comparator); } @Override protected AddressableHeap createHeapWithStringValues() { return new ReflectedPairingHeap(null); } } ReflectedPairingHeapDoubleEndedAddressableHeapTest.java000066400000000000000000000037071367505655000343460ustar00rootroot00000000000000jheaps-jheaps-0.14/src/test/java/org/jheaps/tree/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.tree; import java.util.Comparator; import org.jheaps.AddressableHeap; import org.jheaps.AddressableHeapFactory; import org.jheaps.DoubleEndedAddressableHeap; import org.jheaps.tree.PairingHeap; import org.jheaps.tree.ReflectedHeap; import org.junit.Test; public class ReflectedPairingHeapDoubleEndedAddressableHeapTest extends AbstractDoubleEndedAddressableHeapTest { private static AddressableHeapFactory FACTORY = new AddressableHeapFactory() { @Override public AddressableHeap get(Comparator comparator) { return new PairingHeap(comparator); } }; @Override protected DoubleEndedAddressableHeap createHeap() { return new ReflectedHeap(FACTORY, null); } @Override protected DoubleEndedAddressableHeap createHeap(Comparator comparator) { return new ReflectedHeap(FACTORY, comparator); } @Override protected DoubleEndedAddressableHeap createHeapWithStringValues() { return new ReflectedHeap(FACTORY); } @Test(expected = NullPointerException.class) public void testNoFactory() { new ReflectedHeap(null); } } ReflectedPairingHeapMergeableDoubleEndedAddressableHeapTest.java000066400000000000000000000046521367505655000361520ustar00rootroot00000000000000jheaps-jheaps-0.14/src/test/java/org/jheaps/tree/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.tree; import java.util.Comparator; import org.jheaps.AddressableHeap; import org.jheaps.AddressableHeapFactory; import org.jheaps.MergeableDoubleEndedAddressableHeap; import org.jheaps.tree.BinaryTreeAddressableHeap; import org.jheaps.tree.PairingHeap; import org.jheaps.tree.ReflectedHeap; import org.junit.Test; public class ReflectedPairingHeapMergeableDoubleEndedAddressableHeapTest extends AbstractMergeableDoubleEndedAddressableHeapTest { private static AddressableHeapFactory FACTORY = new AddressableHeapFactory() { @Override public AddressableHeap get(Comparator comparator) { return new PairingHeap(comparator); } }; private static AddressableHeapFactory NON_MELDABLE_FACTORY = new AddressableHeapFactory() { @Override public AddressableHeap get(Comparator comparator) { return new BinaryTreeAddressableHeap(comparator); } }; @Override protected MergeableDoubleEndedAddressableHeap createHeap() { return new ReflectedHeap(FACTORY, null); } @Override protected MergeableDoubleEndedAddressableHeap createHeap(Comparator comparator) { return new ReflectedHeap(FACTORY, comparator); } @Test(expected = IllegalArgumentException.class) public void testNonMeldableInnerHeaps() { ReflectedHeap h1 = new ReflectedHeap(NON_MELDABLE_FACTORY); ReflectedHeap h2 = new ReflectedHeap(NON_MELDABLE_FACTORY); h1.meld(h2); } } jheaps-jheaps-0.14/src/test/java/org/jheaps/tree/SimpleFibonacciHeapAddressableHeapTest.java000066400000000000000000000024161367505655000321460ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.tree; import java.util.Comparator; import org.jheaps.AddressableHeap; public class SimpleFibonacciHeapAddressableHeapTest extends AbstractAddressableHeapTest { @Override protected AddressableHeap createHeap() { return new SimpleFibonacciHeap(); } @Override protected AddressableHeap createHeap(Comparator comparator) { return new SimpleFibonacciHeap(comparator); } @Override protected AddressableHeap createHeapWithStringValues() { return new SimpleFibonacciHeap(); } } SimpleFibonacciHeapMergeableAddressableHeapTest.java000066400000000000000000000022301367505655000336650ustar00rootroot00000000000000jheaps-jheaps-0.14/src/test/java/org/jheaps/tree/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.tree; import java.util.Comparator; import org.jheaps.MergeableAddressableHeap; public class SimpleFibonacciHeapMergeableAddressableHeapTest extends AbstractMergeableAddressableHeapTest { protected MergeableAddressableHeap createHeap() { return new SimpleFibonacciHeap(); } @Override protected MergeableAddressableHeap createHeap(Comparator comparator) { return new SimpleFibonacciHeap(comparator); } } jheaps-jheaps-0.14/src/test/java/org/jheaps/tree/SkewHeapAddressableHeapTest.java000066400000000000000000000023421367505655000300260ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.tree; import java.util.Comparator; import org.jheaps.AddressableHeap; public class SkewHeapAddressableHeapTest extends AbstractAddressableHeapTest { @Override protected AddressableHeap createHeap() { return new SkewHeap(); } @Override protected AddressableHeap createHeap(Comparator comparator) { return new SkewHeap(comparator); } @Override protected AddressableHeap createHeapWithStringValues() { return new SkewHeap(); } } jheaps-jheaps-0.14/src/test/java/org/jheaps/tree/SkewHeapMergeableAddressableHeapTest.java000066400000000000000000000022051367505655000316300ustar00rootroot00000000000000/* * (C) Copyright 2014-2016, by Dimitrios Michail * * JHeaps Library * * 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.jheaps.tree; import java.util.Comparator; import org.jheaps.MergeableAddressableHeap; public class SkewHeapMergeableAddressableHeapTest extends AbstractMergeableAddressableHeapTest { @Override protected MergeableAddressableHeap createHeap() { return new SkewHeap(); } @Override protected MergeableAddressableHeap createHeap(Comparator comparator) { return new SkewHeap(comparator); } }