pax_global_header 0000666 0000000 0000000 00000000064 14742747421 0014526 g ustar 00root root 0000000 0000000 52 comment=3e2c1a351f670376e2aaa26813642c19da76dbcf
euclid-euclid-2.10/ 0000775 0000000 0000000 00000000000 14742747421 0014160 5 ustar 00root root 0000000 0000000 euclid-euclid-2.10/.github/ 0000775 0000000 0000000 00000000000 14742747421 0015520 5 ustar 00root root 0000000 0000000 euclid-euclid-2.10/.github/workflows/ 0000775 0000000 0000000 00000000000 14742747421 0017555 5 ustar 00root root 0000000 0000000 euclid-euclid-2.10/.github/workflows/maven.yml 0000664 0000000 0000000 00000001707 14742747421 0021413 0 ustar 00root root 0000000 0000000 name: Java CI with Maven
on:
push:
pull_request:
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
java: [ 8, 11, 17, 19 ]
name: Java ${{ matrix.java }}
steps:
- uses: actions/checkout@v4
- name: Set up Java
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: ${{ matrix.java }}
- name: Build with Maven
run: mvn clean install -Dgpg.skip -Dmaven.javadoc.skip=true
coverage:
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v4
- name: Set up Java
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 8
- name: Build with Maven
run: mvn clean install -Dgpg.skip -Dmaven.javadoc.skip=true -Dmaven.test.failure.ignore=true
- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
euclid-euclid-2.10/.gitignore 0000664 0000000 0000000 00000000172 14742747421 0016150 0 ustar 00root root 0000000 0000000 foo
.project
.settings/
.classpath
^.hgignore~$
^.gitignore~$
^target/.*
target/
# IntelliJ Idear files
/.idea/*
**/*.iml
euclid-euclid-2.10/CITATION.cff 0000664 0000000 0000000 00000001044 14742747421 0016051 0 ustar 00root root 0000000 0000000 cff-version: 1.2.0
message: "If you use this software, please cite it as below."
title: Euclid
version: 2.10
date-released: 2025-01-18
url: "https://github.com/BlueObelisk/euclid"
preferred-citation:
type: article
authors:
- family-names: Murray-Rust
given-names: Peter
- family-names: Rzepa
given-names: Henry S.
title: "CML: Evolution and design"
year: 2011
month: 10
day: 14
journal: Journal of Cheminformatics
volume: 3
issue: 44
doi: 10.1186/1758-2946-3-44
url: https://doi.org/10.1186/1758-2946-3-44
euclid-euclid-2.10/LICENSE.txt 0000664 0000000 0000000 00000026135 14742747421 0016012 0 ustar 00root root 0000000 0000000 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.
euclid-euclid-2.10/README.md 0000664 0000000 0000000 00000002327 14742747421 0015443 0 ustar 00root root 0000000 0000000 # CML Euclid
[](https://maven-badges.herokuapp.com/maven-central/org.blueobelisk/euclid)
[](https://github.com/BlueObelisk/euclid/actions/workflows/maven.yml)
[](https://doi.org/10.5281/zenodo.5815148)
[](https://codecov.io/gh/BlueObelisk/euclid)
A library of numeric, geometric and XML routines
Euclid was written ca. 1994 as Java had no useful libraries then. Much of the
functionality is now present in Apache and other libraries and in an ideal world
Euclid maths and geometry could be replaced. However, there are additions that are valuable.
It's used a lot in CML tools (JUMBO, JUMBO-converters) and also AMI (for extracting semantics from PDFs).
## Releases
Instructions to increase the version:
```shell
mvn versions:set -DnewVersion=2.11-SNAPSHOT
```
Deploy to Sonatype with the following commands, for snapshots and releases respectively:
```shell
mvn clean deploy
```
euclid-euclid-2.10/pom.xml 0000664 0000000 0000000 00000027317 14742747421 0015507 0 ustar 00root root 0000000 0000000 4.0.0org.blueobeliskeuclid2.10jarUTF-8UTF-8CML EuclidA Java library for 2D and 3D geometric calculationshttps://github.com/BlueObelisk/euclidApache License, Version 2.0http://www.apache.org/licenses/LICENSE-2.0.txtrepohttps://github.com/BlueObelisk/euclidscm:git:git://github.com/blueobelisk/euclid.gitscm:git:ssh://git@github.com/blueobelisk/euclid.gitHEADpm286Peter Murray-Rust1994cml-discusshttps://lists.sourceforge.net/lists/listinfo/cml-discuss
https://lists.sourceforge.net/lists/listinfo/cml-discuss
http://sourceforge.net/mailarchive/forum.php?forum_name=cml-discuss
ossrhhttps://s01.oss.sonatype.org/content/repositories/snapshotsossrhhttps://s01.oss.sonatype.org/service/local/staging/deploy/maven2/Peter Murray-Rusthttp://wwmm.ch.cam.ac.uk/blogs/murrayrust/ org.apache.maven.pluginsmaven-enforcer-plugin3.4.1enforce-mavenenforce3.6.31.8org.apache.maven.pluginsmaven-compiler-plugin3.13.01.81.8org.apache.maven.pluginsmaven-javadoc-plugin3.6.3attach-javadocsjaraggregateaggregatesitemaven-source-plugin3.3.1attach-sourcesjar-no-forkorg.codehaus.mojocobertura-maven-plugin2.7falseorg.xmlcml.*8080org/xmlcml/**/*.classcleanpre-sitecleaninstrumentsiteinstrumentcoberturacheckcom.mycila.maven-license-pluginmaven-license-plugin1.10.b1src/main/resources/header.txt.travis.yml.gitignoreREADME.mdLICENSE.***/READMEsrc/test/resources/org/xmlcml/files/**src/*/java/blogspot/software_and_algorithms/stern_library/****/*.java-old**/junk.idea/**org.sonatype.pluginsnexus-staging-maven-plugin1.6.13trueossrhhttps://s01.oss.sonatype.org/trueorg.apache.maven.pluginsmaven-gpg-plugin3.2.4sign-artifactsverifysignorg.jacocojacoco-maven-plugin0.8.12start-agentprepare-agentgenerate-reportreportjunitjunit4.13.2commons-iocommons-io2.18.0org.apache.logging.log4jlog4j2.24.3pomorg.apache.logging.log4jlog4j-1.2-api2.24.3org.apache.logging.log4jlog4j-core2.24.3org.apache.commonscommons-lang33.17.0org.apache.commonscommons-math2.2joda-timejoda-time2.13.0xomxom1.3.9xercesxercesImplreleasemaven-assembly-plugin3.7.1srcorg.apache.maven.pluginsmaven-project-info-reports-plugin3.5.0indexsummarydependenciesproject-teamlicensecimscmorg.apache.maven.pluginsmaven-javadoc-pluginorg.apache.maven.pluginsmaven-surefire-report-plugin3.2.5org.apache.maven.pluginsmaven-jxr-plugin3.3.2org.apache.maven.pluginsmaven-pmd-plugin3.22.01.7trueorg.apache.maven.pluginsmaven-checkstyle-plugin3.3.1src/test/resources/checkstyle.xml
org.codehaus.mojocobertura-maven-pluginorg.codehaus.mojoapt-maven-plugin1.0-alpha-5
euclid-euclid-2.10/src/ 0000775 0000000 0000000 00000000000 14742747421 0014747 5 ustar 00root root 0000000 0000000 euclid-euclid-2.10/src/main/ 0000775 0000000 0000000 00000000000 14742747421 0015673 5 ustar 00root root 0000000 0000000 euclid-euclid-2.10/src/main/java/ 0000775 0000000 0000000 00000000000 14742747421 0016614 5 ustar 00root root 0000000 0000000 euclid-euclid-2.10/src/main/java/blogspot/ 0000775 0000000 0000000 00000000000 14742747421 0020445 5 ustar 00root root 0000000 0000000 euclid-euclid-2.10/src/main/java/blogspot/software_and_algorithms/ 0000775 0000000 0000000 00000000000 14742747421 0025352 5 ustar 00root root 0000000 0000000 euclid-euclid-2.10/src/main/java/blogspot/software_and_algorithms/stern_library/ 0000775 0000000 0000000 00000000000 14742747421 0030231 5 ustar 00root root 0000000 0000000 euclid-euclid-2.10/src/main/java/blogspot/software_and_algorithms/stern_library/data_structure/ 0000775 0000000 0000000 00000000000 14742747421 0033262 5 ustar 00root root 0000000 0000000 DynamicIntervalTree.java 0000664 0000000 0000000 00000024116 14742747421 0037763 0 ustar 00root root 0000000 0000000 euclid-euclid-2.10/src/main/java/blogspot/software_and_algorithms/stern_library/data_structure package blogspot.software_and_algorithms.stern_library.data_structure;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import blogspot.software_and_algorithms.stern_library.data_structure.RedBlackTree.Node.NodeColor;
/* Copyright (c) 2012 Kevin L. Stern
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* A dynamic interval tree is a balanced binary search tree which
* stores intervals so that both point queries (queries that return intervals
* from the set which contain a query point) and overlapping interval queries
* (queries that return intervals from the set which overlap a query interval)
* can be completed in time O(k*log(n)), where n is the number of intervals
* stored in the tree and k is the size of the result set from the query.
*
* Insertion and deletion of intervals to and from this tree completes in time
* O(log(n)) where n is the number of intervals stored in the tree.
*
* This tree consumes linear space in the number of intervals stored in the
* tree.
*
* Note that this implementation supports all three closed, open and half-open
* intervals.
*
* @author Kevin L. Stern
*/
public class DynamicIntervalTree, T extends Interval> {
public RedBlackTree binarySearchTree = new RedBlackTree(
new Comparator() {
@Override
public int compare(T o1, T o2) {
int result = o1.getLow().compareTo(o2.getLow());
if (result == 0) {
if (o1.isClosedOnLow() != o2.isClosedOnLow()) {
result = o1.isClosedOnLow() ? -1 : 1;
} else {
result = o1.compareTo(o2);
}
}
return result;
}
}) {
@Override
protected RedBlackTree.Node createNewNode(T value) {
return new DynamicIntervalTree.Node(value);
}
@Override
public RedBlackTree.Node delete(T value) {
RedBlackTree.Node node = super.delete(value);
if (node != null && node.getColor() != NodeColor.BLACK) {
DynamicIntervalTree.Node temp = (DynamicIntervalTree.Node) node
.getParent();
while (temp != null) {
temp.computeMaximumHighEndpoint();
temp = temp.getParent();
}
}
return node;
}
@Override
protected void fixAfterDeletion(RedBlackTree.Node node) {
DynamicIntervalTree.Node temp = (DynamicIntervalTree.Node) node
.getParent();
while (temp != null) {
temp.computeMaximumHighEndpoint();
temp = temp.getParent();
}
super.fixAfterDeletion(node);
}
@Override
protected void fixAfterInsertion(RedBlackTree.Node node) {
DynamicIntervalTree.Node temp = (DynamicIntervalTree.Node) node
.getParent();
while (temp != null) {
temp.computeMaximumHighEndpoint();
temp = temp.getParent();
}
super.fixAfterInsertion(node);
}
@Override
protected void leftRotate(RedBlackTree.Node node) {
super.leftRotate(node);
DynamicIntervalTree.Node temp = (DynamicIntervalTree.Node) node;
temp.computeMaximumHighEndpoint();
temp.getParent().computeMaximumHighEndpoint();
}
@Override
protected void rightRotate(RedBlackTree.Node node) {
super.rightRotate(node);
DynamicIntervalTree.Node temp = (DynamicIntervalTree.Node) node;
temp.computeMaximumHighEndpoint();
temp.getParent().computeMaximumHighEndpoint();
}
};
/**
* Clear the contents of the tree.
*/
public void clear() {
binarySearchTree.clear();
}
/**
* Delete the specified interval from this tree.
*
* @param interval
* the interval to delete.
* @return true if an element was deleted as a result of this call, false
* otherwise.
*/
public boolean delete(T interval) {
return binarySearchTree.delete(interval) != null;
}
/**
* Fetch an interval containing the specified point.
*
* @param queryPoint
* the query point.
* @return an interval containing the specified point, null if none.
*/
protected T fetchContainingInterval(U queryPoint) {
Node node = (Node) binarySearchTree.getRoot();
while (node != null) {
if (node.getValue().contains(queryPoint)) {
return node.getValue();
}
Node leftChild = node.getLeft();
node = node.getRight();
if (leftChild != null) {
int cmp = leftChild.getMaximumHighEndpoint().compareTo(
queryPoint);
if (cmp > 0 || cmp == 0 && leftChild.isClosedOnEndpoint()) {
node = leftChild;
}
}
}
return null;
}
/**
* Fetch intervals containing the specified point.
*
* @param queryPoint
* the query point.
* @return a Collection of all intervals containing the specified point.
*/
public Collection fetchContainingIntervals(U queryPoint) {
if (queryPoint == null) {
throw new NullPointerException("queryPoint is null");
}
List result = new ArrayList();
T interval;
while ((interval = fetchContainingInterval(queryPoint)) != null) {
result.add(interval);
delete(interval);
}
for (T next : result) {
insert(next);
}
return result;
}
/**
* Fetch an interval overlapping the specified interval.
*
* @param queryInterval
* the query interval.
* @return an interval overlapping the specified interval, null if none.
*/
protected T fetchOverlappingInterval(T queryInterval) {
Node node = (Node) binarySearchTree.getRoot();
while (node != null) {
if (node.getValue().overlaps(queryInterval)) {
return node.getValue();
}
Node leftChild = node.getLeft();
node = node.getRight();
if (leftChild != null) {
int cmp = leftChild.getMaximumHighEndpoint().compareTo(
queryInterval.getLow());
if (cmp > 0 || cmp == 0 && leftChild.isClosedOnEndpoint()
&& queryInterval.isClosedOnLow()) {
node = leftChild;
}
}
}
return null;
}
/**
* Fetch intervals overlapping the specified interval.
*
* @param queryInterval
* the query interval.
* @return a Collection of all intervals overlapping the specified point.
*/
public Collection fetchOverlappingIntervals(T queryInterval) {
if (queryInterval == null) {
throw new NullPointerException("queryInterval is null");
}
List result = new ArrayList();
T interval;
while ((interval = fetchOverlappingInterval(queryInterval)) != null) {
result.add(interval);
delete(interval);
}
for (T next : result) {
insert(next);
}
return result;
}
/**
* Get the number of intervals being stored in the tree.
*
* @return the number of intervals being stored in the tree.
*/
public int getSize() {
return binarySearchTree.getSize();
}
/**
* Insert the specified interval into this tree.
*
* @param interval
* the interval to insert.
* @return true if an element was inserted as a result of this call, false
* otherwise.
*/
public boolean insert(T interval) {
return binarySearchTree.insert(interval) != null;
}
/**
* A node for a dynamic interval tree is a red-black tree node
* augmented to store the maximum high endpoint among intervals stored
* within the subtree rooted at the node.
*/
protected static class Node, T extends Interval>
extends RedBlackTree.Node {
private U maximumHighEndpoint;
private boolean isClosedOnEndpoint;
/**
* Construct a new node associated with the specified interval.
*
* @param interval
* the interval with which this node is associated.
*/
public Node(T interval) {
super(interval);
maximumHighEndpoint = interval.getHigh();
isClosedOnEndpoint = interval.isClosedOnHigh();
}
/**
* Compute the maximum high endpoint among intervals stored within the
* subtree rooted at this node and correct values up the tree.
*/
protected void computeMaximumHighEndpoint() {
U maximumHighEndpoint = getValue().getHigh();
boolean isClosedOnEndpoint = getValue().isClosedOnHigh();
Node child;
child = getLeft();
if (child != null) {
int cmp = child.maximumHighEndpoint
.compareTo(maximumHighEndpoint);
if (cmp > 0 || cmp == 0 && child.isClosedOnEndpoint) {
maximumHighEndpoint = child.maximumHighEndpoint;
isClosedOnEndpoint = child.isClosedOnEndpoint;
}
}
child = getRight();
if (child != null) {
int cmp = child.maximumHighEndpoint
.compareTo(maximumHighEndpoint);
if (cmp > 0 || cmp == 0 && child.isClosedOnEndpoint) {
maximumHighEndpoint = child.maximumHighEndpoint;
isClosedOnEndpoint = child.isClosedOnEndpoint;
}
}
this.maximumHighEndpoint = maximumHighEndpoint;
this.isClosedOnEndpoint = isClosedOnEndpoint;
}
/**
* {@inheritDoc}
*/
@Override
public Node getLeft() {
return (Node) super.getLeft();
}
public U getMaximumHighEndpoint() {
return maximumHighEndpoint;
}
/**
* {@inheritDoc}
*/
@Override
public Node getParent() {
return (Node) super.getParent();
}
/**
* {@inheritDoc}
*/
@Override
public Node getRight() {
return (Node) super.getRight();
}
public boolean isClosedOnEndpoint() {
return isClosedOnEndpoint;
}
}
}
Interval.java 0000664 0000000 0000000 00000015532 14742747421 0035640 0 ustar 00root root 0000000 0000000 euclid-euclid-2.10/src/main/java/blogspot/software_and_algorithms/stern_library/data_structure package blogspot.software_and_algorithms.stern_library.data_structure;
/* Copyright (c) 2012 Kevin L. Stern
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* An interval is the subset of elements which fall between (with
* respect to a total order) two endpoint elements of a set. An interval that
* contains its endpoints is closed, an interval that contains one of
* its endpoints but not the other is half open and an interval that
* does not contain either of its endpoints is open. This class
* encapsulates the concept of an interval and uses a class's natural order.
*
* @author Kevin L. Stern
*/
public class Interval> implements
Comparable> {
private T low, high;
private boolean isClosedOnLow, isClosedOnHigh;
private int hashCode = 0;
/**
* Construct a new instance with the specified low and high endpoints.
*
* @param low
* the low endpoint.
* @param isClosedOnLow
* true if this interval contains its low endpoint, false
* otherwise.
* @param high
* the high endpoint.
* @param isClosedOnHigh
* true if this interval contains its high endpoint, false
* otherwise.
*/
public Interval(T low, boolean isClosedOnLow, T high, boolean isClosedOnHigh) {
if (low == null) {
throw new NullPointerException("low endpoint is null");
} else if (high == null) {
throw new NullPointerException("high endpoint is null");
}
this.low = low;
this.isClosedOnLow = isClosedOnLow;
this.high = high;
this.isClosedOnHigh = isClosedOnHigh;
}
/**
* {@inheritDoc}
*/
@Override
public int compareTo(Interval o) {
int result = low.compareTo(o.low);
if (result == 0) {
if (isClosedOnLow != o.isClosedOnLow) {
result = isClosedOnLow ? -1 : 1;
} else {
result = high.compareTo(o.high);
if (result == 0) {
if (isClosedOnHigh != o.isClosedOnHigh) {
result = isClosedOnHigh ? -1 : 1;
}
}
}
}
return result;
}
/**
* Test whether or not this interval contains the specified interval. An
* interval is contained by another precisely when all of its values are
* contained by the other.
*
* @param interval
* the query interval, non-null.
* @return true if this interval contains the specified interval, false
* otherwise.
*/
public boolean contains(Interval interval) {
boolean lowIsLowerBound = low.equals(interval.low)
&& (isClosedOnLow || !interval.isClosedOnLow)
|| low.compareTo(interval.low) < 0;
boolean highIsUpperBound = high.equals(interval.high)
&& (isClosedOnHigh || !interval.isClosedOnHigh)
|| high.compareTo(interval.high) > 0;
return lowIsLowerBound && highIsUpperBound;
}
/**
* Test whether or not this interval contains the specified value.
*
* @param value
* the query value, non-null.
* @return true if this interval contains the specified value, false
* otherwise.
*/
public boolean contains(T value) {
return value.equals(low) && isClosedOnLow || value.equals(high)
&& isClosedOnHigh || low.compareTo(value) < 0
&& value.compareTo(high) < 0;
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Interval other = (Interval) obj;
if (high == null) {
if (other.high != null)
return false;
} else if (!high.equals(other.high))
return false;
if (isClosedOnHigh != other.isClosedOnHigh)
return false;
if (low == null) {
if (other.low != null)
return false;
} else if (!low.equals(other.low))
return false;
if (isClosedOnLow != other.isClosedOnLow)
return false;
return true;
}
/**
* Get the high endpoint.
*
* @return the high endpoint.
*/
public T getHigh() {
return high;
}
/**
* Get the low endpoint.
*
* @return the low endpoint.
*/
public T getLow() {
return low;
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
if (hashCode == 0) {
final int prime = 31;
int result = 1;
result = prime * result + ((high == null) ? 0 : high.hashCode());
result = prime * result + (isClosedOnHigh ? 1231 : 1237);
result = prime * result + ((low == null) ? 0 : low.hashCode());
result = prime * result + (isClosedOnLow ? 1231 : 1237);
hashCode = result;
}
return hashCode;
}
/**
*
* @return true if this interval is closed at its high endpoint, false
* otherwise.
*/
public boolean isClosedOnHigh() {
return isClosedOnHigh;
}
/**
*
* @return true if the interval is closed at its low endpoint, false
* otherwise.
*/
public boolean isClosedOnLow() {
return isClosedOnLow;
}
/**
* Test whether or not this interval and the specified interval overlap. Two
* intervals overlap precisely when their intersection is non-empty.
*
* @param interval
* the query interval.
* @return true if this interval and the specified interval overlap, false
* otherwise.
*/
public boolean overlaps(Interval interval) {
if (interval.isClosedOnLow && contains(interval.low) || isClosedOnLow
&& interval.contains(low)) {
return true;
}
if (!interval.isClosedOnLow && low.compareTo(interval.low) <= 0
&& interval.low.compareTo(high) < 0 || !isClosedOnLow
&& interval.low.compareTo(low) <= 0
&& low.compareTo(interval.high) < 0) {
return true;
}
return false;
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
String format;
if (isClosedOnLow) {
if (isClosedOnHigh) {
format = "[%s, %s]";
} else {
format = "[%s, %s)";
}
} else {
if (isClosedOnHigh) {
format = "(%s, %s]";
} else {
format = "(%s, %s)";
}
}
return String.format(format, low.toString(), high.toString());
}
} OrderLinkedRedBlackTree.java 0000664 0000000 0000000 00000012506 14742747421 0040464 0 ustar 00root root 0000000 0000000 euclid-euclid-2.10/src/main/java/blogspot/software_and_algorithms/stern_library/data_structure package blogspot.software_and_algorithms.stern_library.data_structure;
import java.util.Comparator;
/* Copyright (c) 2012 Kevin L. Stern
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* A red black tree that has been augmented to support linear time partial
* iteration by storing pointers to a node's predecessor and successor within
* the node itself.
*
* @author Kevin L. Stern
*/
public class OrderLinkedRedBlackTree extends RedBlackTree {
private RedBlackTree.Node head;
/**
* Default constructor.
*/
public OrderLinkedRedBlackTree() {
this(null);
}
/**
* Construct a new instance which uses the specified comparator.
*
* @param comparator
* the comparator to use when ordering elements.
*/
public OrderLinkedRedBlackTree(Comparator comparator) {
super(comparator);
}
/**
* {@inheritDoc}
*/
@Override
public void clear() {
super.clear();
head = null;
}
/**
* {@inheritDoc}
*/
@Override
protected RedBlackTree.Node createNewNode(T value) {
return new Node(value);
}
/**
* {@inheritDoc}
*/
@Override
public RedBlackTree.Node delete(T value) {
if (head != null && head.getValue().equals(value)) {
head = getSuccessor(head);
}
RedBlackTree.Node result = super.delete(value);
if (result != null) {
Node linkedNode = (Node) result;
if (linkedNode.getPredecessor() != null)
linkedNode.getPredecessor().setSuccessor(
linkedNode.getSuccessor());
if (linkedNode.getSuccessor() != null)
linkedNode.getSuccessor().setPredecessor(
linkedNode.getPredecessor());
}
return result;
}
/**
* {@inheritDoc}
*/
@Override
protected void exchangeValues(RedBlackTree.Node node,
RedBlackTree.Node successor) {
super.exchangeValues(node, successor);
Node linkedNode = (Node) node;
Node linkedSuccessor = (Node) successor;
linkedNode.setSuccessor(linkedSuccessor.getSuccessor());
if (linkedNode.getSuccessor() != null)
linkedNode.getSuccessor().setPredecessor(linkedNode);
linkedSuccessor.setPredecessor(null);
linkedSuccessor.setSuccessor(null);
}
/**
* {@inheritDoc}
*/
@Override
public RedBlackTree.Node getFirstNode() {
return head;
}
/**
* {@inheritDoc}
*/
@Override
public RedBlackTree.Node getPredecessor(RedBlackTree.Node node) {
return ((Node) node).getPredecessor();
}
/**
* {@inheritDoc}
*/
@Override
public RedBlackTree.Node getSuccessor(RedBlackTree.Node node) {
return ((Node) node).getSuccessor();
}
/**
* {@inheritDoc}
*/
@Override
public RedBlackTree.Node insert(T value) {
RedBlackTree.Node result = super.insert(value);
if (result != null) {
Node linkedNode = (Node) result;
Node pred = (Node) super.getPredecessor(result);
linkedNode.setPredecessor(pred);
if (pred != null) {
pred.setSuccessor(linkedNode);
}
Node succ = (Node) super.getSuccessor(result);
linkedNode.setSuccessor(succ);
if (succ != null) {
succ.setPredecessor(linkedNode);
}
if (head == null) {
head = getRoot();
} else {
RedBlackTree.Node node = getPredecessor(head);
if (node != null) {
head = node;
}
}
}
return result;
}
/**
* A red-black tree node augmented to store pointers to its predecessor and
* successor.
*
* @author Kevin L. Stern
*/
public static class Node extends RedBlackTree.Node {
private Node predecessor, successor;
/**
* Construct a node with the specified value.
*
* @param value
* the value to associate with this node.
*/
public Node(T value) {
super(value);
}
/**
* Get the predecessor node.
*
* @return the predecessor of this node in the tree.
*/
public Node getPredecessor() {
return predecessor;
}
/**
* Get the successor node.
*
* @return the successor of this node in the tree.
*/
public Node getSuccessor() {
return successor;
}
/**
* Set the predecessor node.
*
* @param node
* the predecessor of this node in the tree.
*/
protected void setPredecessor(Node node) {
predecessor = node;
}
/**
* Set the successor node.
*
* @param node
* the successor of this node in the tree.
*/
protected void setSuccessor(Node node) {
successor = node;
}
}
} RedBlackTree.java 0000664 0000000 0000000 00000046070 14742747421 0036344 0 ustar 00root root 0000000 0000000 euclid-euclid-2.10/src/main/java/blogspot/software_and_algorithms/stern_library/data_structure package blogspot.software_and_algorithms.stern_library.data_structure;
import java.util.Comparator;
import java.util.Iterator;
import java.util.NoSuchElementException;
import blogspot.software_and_algorithms.stern_library.data_structure.RedBlackTree.Node.NodeColor;
/* Copyright (c) 2012 Kevin L. Stern
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* A red-black tree is a binary search tree guaranteeing that no path
* from root to leaf is more than twice as long as any other such path. This
* property provides an assurance that the height of a red-black tree is
* logarithmic in the number of nodes in the tree.
*
* This implementation is based upon Cormen, Leiserson, Rivest, Stein's
* Introduction to Algorithms book.
*
* @see "Introduction to Algorithms Cormen, Leiserson, Rivest, and Stein.
* Introduction to Algorithms. 2nd ed. Cambridge, MA: MIT Press, 2001.
* ISBN: 0262032937."
*/
public class RedBlackTree implements Iterable {
private Comparator super T> comparator;
private Node root;
private int size;
/**
* Default constructor. Tree uses the natural ordering of elements defined
* by {@link Comparable#compareTo(Object)}; for custom ordering see
* {@link RedBlackTree#RedBlackTree(Comparator)}.
*/
public RedBlackTree() {
this(null);
}
/**
* Construct a new tree which uses the specified custom comparator.
*
* @param comparator
* the comparator to use when ordering elements.
*/
public RedBlackTree(Comparator super T> comparator) {
this.comparator = comparator;
this.root = null;
this.size = 0;
}
/**
* Clear all entries from this tree.
*/
public void clear() {
root = null;
size = 0;
}
/**
* Convenience method to compare two values either by use of the Comparator,
* if not null, or by casting to Comparable otherwise. This method may
* result in a ClassCastException if the tree holds non-Comparable values
* and no Comparator was specified upon construction.
*
* @param val1
* the lhs of the compare operation.
* @param val2
* the rhs of the compare operation.
* @return a negative integer, zero, or a positive integer depending upon
* whether val1 is less than, equal to, or greater than val2,
* respectively.
*/
private int compare(T val1, T val2) {
return comparator == null ? ((Comparable) val1).compareTo(val2)
: comparator.compare(val1, val2);
}
/**
* Test whether or not the specified value is an element of this tree.
*
* @param value
* the query value.
* @return true if the specified value is an element of this tree, false
* otherwise.
*/
public boolean contains(T value) {
return getNode(value) != null;
}
/**
* Create a new node with the specified value. This method allows a subclass
* to assert control over the type of the nodes held by this tree.
*
* @param value
* the value to apply to the new node.
* @return a new node holding the specified value.
*/
protected Node createNewNode(T value) {
return new Node(value);
}
/**
* Delete the specified value from this tree.
*
* @param value
* the value to delete.
* @return the node which contained the value before it was removed from
* this tree, null if the value is not an element of this tree.
*/
public Node delete(T value) {
if (value == null) {
return null;
}
Node node = getNode(value);
if (node == null)
return null;
Node swap;
if (!(node.getLeft() == null || node.getRight() == null)) {
Node successor = getSuccessor(node);
exchangeValues(node, successor);
node = successor;
}
if (node.getLeft() != null) {
swap = node.getLeft();
} else {
swap = node.getRight();
}
if (swap != null)
swap.setParent(node.getParent());
if (node.getParent() == null)
root = swap;
else if (node == node.getParent().getLeft())
node.getParent().setLeft(swap);
else
node.getParent().setRight(swap);
if (node.getColor() == NodeColor.BLACK) {
if (root != null)
fixAfterDeletion(swap == null ? node : swap);
}
size--;
return node;
}
/**
* Called by {@link #delete} when the node to be removed is a leaf. In
* this case, the node's value is exchanged with its successor as per the
* typical binary tree node removal operation. This method allows a subclass
* to influence value exchange behavior (e.g. if additional node information
* needs to be exchanged).
*
* @param node
* the node whose value is to be removed.
* @param successor
* the node to actually be removed.
*/
protected void exchangeValues(Node node, Node successor) {
T tempValue = successor.getValue();
successor.setValue(node.getValue());
node.setValue(tempValue);
}
/**
* Re-balance the tree after a delete operation.
*
* @param node
* the deleted node or the swap node.
*
* @see "CLRS Introduction to Algorithms"
*/
protected void fixAfterDeletion(Node node) {
while (node != root && getColor(node) == NodeColor.BLACK) {
if (node == node.getParent().getLeft()
|| (node.getParent().getRight() != null && node != node
.getParent().getRight())) {
Node temp = node.getParent().getRight();
if (getColor(temp) == NodeColor.RED) {
setColor(temp, NodeColor.BLACK);
setColor(node.getParent(), NodeColor.RED);
leftRotate(node.getParent());
temp = node.getParent().getRight();
}
if (getColor(temp.getLeft()) == NodeColor.BLACK
&& getColor(temp.getRight()) == NodeColor.BLACK) {
setColor(temp, NodeColor.RED);
node = node.getParent();
} else {
if (getColor(temp.getRight()) == NodeColor.BLACK) {
setColor(temp.getLeft(), NodeColor.BLACK);
setColor(temp, NodeColor.RED);
rightRotate(temp);
temp = node.getParent().getRight();
}
setColor(temp, getColor(node.getParent()));
setColor(node.getParent(), NodeColor.BLACK);
setColor(temp.getRight(), NodeColor.BLACK);
leftRotate(node.getParent());
node = root;
}
} else {
Node temp = node.getParent().getLeft();
if (getColor(temp) == NodeColor.RED) {
setColor(temp, NodeColor.BLACK);
setColor(node.getParent(), NodeColor.RED);
rightRotate(node.getParent());
temp = node.getParent().getLeft();
}
if (getColor(temp.getRight()) == NodeColor.BLACK
&& getColor(temp.getLeft()) == NodeColor.BLACK) {
setColor(temp, NodeColor.RED);
node = node.getParent();
} else {
if (getColor(temp.getLeft()) == NodeColor.BLACK) {
setColor(temp.getRight(), NodeColor.BLACK);
setColor(temp, NodeColor.RED);
leftRotate(temp);
temp = node.getParent().getLeft();
}
setColor(temp, getColor(node.getParent()));
setColor(node.getParent(), NodeColor.BLACK);
setColor(temp.getLeft(), NodeColor.BLACK);
rightRotate(node.getParent());
node = root;
}
}
}
setColor(node, NodeColor.BLACK);
}
/**
* Re-balance the tree after an insert operation.
*
* @param node
* the inserted node.
*
* @see "CLRS Introduction to Algorithms"
*/
protected void fixAfterInsertion(Node node) {
while (getColor(node.getParent()) == NodeColor.RED) {
if (node.getParent() == node.getParent().getParent().getLeft()) {
Node temp = node.getParent().getParent().getRight();
if (getColor(temp) == NodeColor.RED) {
setColor(node.getParent(), (NodeColor.BLACK));
setColor(temp, NodeColor.BLACK);
setColor(node.getParent().getParent(), NodeColor.RED);
node = node.getParent().getParent();
} else {
if (node == node.getParent().getRight()) {
node = node.getParent();
leftRotate(node);
}
setColor(node.getParent(), NodeColor.BLACK);
setColor(node.getParent().getParent(), NodeColor.RED);
rightRotate(node.getParent().getParent());
}
} else {
Node temp = node.getParent().getParent().getLeft();
if (getColor(temp) == NodeColor.RED) {
setColor(node.getParent(), NodeColor.BLACK);
setColor(temp, NodeColor.BLACK);
setColor(node.getParent().getParent(), NodeColor.RED);
node = node.getParent().getParent();
} else {
if (node == node.getParent().getLeft()) {
node = node.getParent();
rightRotate(node);
}
setColor(node.getParent(), NodeColor.BLACK);
setColor(node.getParent().getParent(), NodeColor.RED);
leftRotate(node.getParent().getParent());
}
}
}
setColor(root, NodeColor.BLACK);
}
/**
* Convenience method implementing the concept of a null-node leaf being
* black.
*
* @param node
* the node whose color is to be determined, null is interpreted
* as a null leaf and is assigned the color black.
* @return the color of the specified node.
*/
private NodeColor getColor(Node node) {
return (node == null) ? NodeColor.BLACK : node.getColor();
}
/**
* Get the node containing the smallest value held by this tree.
*
* @return the node containing the smallest value held by this tree.
*/
public Node getFirstNode() {
Node result = root;
if (result != null) {
while (result.getLeft() != null) {
result = result.getLeft();
}
}
return result;
}
/**
* Get the node that holds the specified value.
*
* @param value
* the query value.
* @return the node that holds the specified value, null if none.
*/
public Node getNode(T value) {
if (value == null) {
return null;
}
Node node = root;
while (node != null) {
int delta = compare(node.getValue(), value);
if (delta < 0) {
node = node.getRight();
} else if (delta > 0) {
node = node.getLeft();
} else {
break;
}
}
return node;
}
/**
* Get the predecessor of the specified node. The predecessor of a node n is
* the node with the largest value in the tree smaller than the value held
* by n.
*
* @param node the node compared to which predecessor node will be found
* @return the predecessor of the specified node
* @see "CLRS"
*/
public Node getPredecessor(Node node) {
if (node.getLeft() != null) {
node = node.getLeft();
while (node.getRight() != null)
node = node.getRight();
return node;
}
Node temp = node.getParent();
while (temp != null && node == temp.getLeft()) {
node = temp;
temp = temp.getParent();
}
return temp;
}
/**
* Get the root of this tree.
*
* @return the root of this tree.
*/
public Node getRoot() {
return root;
}
/**
* Get the number of elements contained within this tree.
*
* @return the number of elements contained within this tree.
*/
public int getSize() {
return size;
}
/**
* Get the successor of the specified node. The successor of a node n is the
* node with the smallest value in the tree larger than the value held by n.
*
* @param node the node compared to which successor node will be found
* @return the successor of the specified node
* @see "CLRS"
*/
public Node getSuccessor(Node node) {
if (node.getRight() != null) {
node = node.getRight();
while (node.getLeft() != null)
node = node.getLeft();
return node;
}
Node temp = node.getParent();
while (temp != null && node == temp.getRight()) {
node = temp;
temp = temp.getParent();
}
return temp;
}
/**
* Insert the specified value into this tree.
*
* @param value
* the value to insert.
* @return the new node containing the specified value if the value was not
* already present in the tree, null otherwise.
*/
public Node insert(T value) {
Node node = null;
Node parent = root;
while (parent != null) {
int delta = compare(parent.getValue(), value);
if (delta < 0) {
if (parent.getRight() == null) {
node = createNewNode(value);
parent.setRight(node);
node.setParent(parent);
parent = null;
} else {
parent = parent.getRight();
}
} else if (delta > 0) {
if (parent.getLeft() == null) {
node = createNewNode(value);
parent.setLeft(node);
node.setParent(parent);
parent = null;
} else {
parent = parent.getLeft();
}
} else {
return null;
}
}
if (node == null) {
node = createNewNode(value);
root = node;
}
setColor(node, NodeColor.RED);
fixAfterInsertion(node);
size++;
return node;
}
/**
* @return true if there are no items in this tree, false otherwise.
*/
public boolean isEmpty() {
return size == 0;
}
/**
* Returns an Iterator over the elements of this tree.
*
* @return an Iterator over the elements of this tree.
*/
@Override
public Iterator iterator() {
return new Iterator() {
private Node cursor = getFirstNode();
private T lastReturn;
@Override
public boolean hasNext() {
return cursor != null;
}
@Override
public T next() {
if (cursor == null) {
throw new NoSuchElementException();
}
lastReturn = cursor.getValue();
cursor = getSuccessor(cursor);
return lastReturn;
}
@Override
public void remove() {
if (lastReturn == null) {
throw new NoSuchElementException();
}
T currentValue = cursor == null ? null : cursor.getValue();
delete(lastReturn);
cursor = currentValue == null ? null : getNode(currentValue);
lastReturn = null;
}
};
}
/**
* Perform a left rotate operation on the specified node.
*
* @param node
* the node on which the left rotate operation will be performed.
*
* @see "CLRS Introduction to Algorithms"
*/
protected void leftRotate(Node node) {
Node temp = node.getRight();
node.setRight(temp.getLeft());
if (temp.getLeft() != null)
temp.getLeft().setParent(node);
temp.setParent(node.getParent());
if (node.getParent() == null)
root = temp;
else if (node == node.getParent().getLeft())
node.getParent().setLeft(temp);
else
node.getParent().setRight(temp);
temp.setLeft(node);
node.setParent(temp);
}
/**
* Perform a right rotate operation on the specified node.
*
* @param node
* the node on which a right rotate operation is to be performed.
*
* @see "CLRS Introduction to Algorithms"
*/
protected void rightRotate(Node node) {
Node temp = node.getLeft();
node.setLeft(temp.getRight());
if (temp.getRight() != null)
temp.getRight().setParent(node);
temp.setParent(node.getParent());
if (node.getParent() == null)
root = temp;
else if (node == node.getParent().getRight())
node.getParent().setRight(temp);
else
node.getParent().setLeft(temp);
temp.setRight(node);
node.setParent(temp);
}
/**
* Convenience method to set the color of the specified node to the
* specified color if the node is non-null.
*
* @param node
* the target node, possibly null.
* @param color
* the target color, non-null.
*/
private void setColor(Node node, NodeColor color) {
if (node != null)
node.setColor(color);
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
StringBuilder builder = new StringBuilder("{");
if (!isEmpty()) {
for (Iterator i = iterator();;) {
builder.append(i.next());
if (i.hasNext()) {
builder.append(", ");
} else {
break;
}
}
}
builder.append("}");
return builder.toString();
}
/**
* A red-black tree node is a binary tree node augmented to hold an
* additional piece of information called the node's color. The
* domain of values from which a red-black tree node's color is assigned
* comprises red and black. A red-black tree requires that
* its nodes provide the ability to set the parent and children and to set
* the value stored by the node. This class encapsulates the concept of a
* red-black tree node.
*/
public static class Node {
private NodeColor color;
private Node left, right, parent;
private T value;
/**
* Construct a new node with the specified value.
*
* @param value
* the value to store in this node.
*/
public Node(T value) {
if (value == null) {
throw new NullPointerException("value is null");
}
this.value = value;
}
/**
* Get the color.
*
* @return the color, never null.
*/
public NodeColor getColor() {
return color;
}
/**
* Get the left child.
*
* @return the left child, possibly null.
*/
public Node getLeft() {
return left;
}
/**
* Get the parent.
*
* @return the parent, possibly null.
*/
public Node getParent() {
return parent;
}
/**
* Get the right child.
*
* @return the right child, possibly null.
*/
public Node getRight() {
return right;
}
/**
* Get the value.
*
* @return the value.
*/
public T getValue() {
return value;
}
/**
* Test whether or not this node is a leaf node.
*
* @return true if this node is a leaf node, false otherwise.
*/
public boolean isLeaf() {
return left == null && right == null;
}
/**
* Set the color.
*
* @param color
* the color to set to this node.
*/
protected void setColor(NodeColor color) {
this.color = color;
}
/**
* Set the left child.
*
* @param node
* the node to set as the left child of this node.
*/
protected void setLeft(Node node) {
this.left = node;
}
/**
* Set the parent.
*
* @param node
* the node to set as the parent of this node.
*/
protected void setParent(Node node) {
this.parent = node;
}
/**
* Set the right child.
*
* @param node
* the node to set as the right child of this node.
*/
protected void setRight(Node node) {
this.right = node;
}
/**
* Set the value.
*
* @param value
* the value to store in this node, must be non-null.
*/
protected void setValue(T value) {
if (value == null) {
throw new IllegalArgumentException("value is null");
}
this.value = value;
}
/**
* The domain of values from which a node's color is assigned.
*/
public static enum NodeColor {
BLACK, RED
}
}
} StaticIntervalTree.java 0000664 0000000 0000000 00000046033 14742747421 0037630 0 ustar 00root root 0000000 0000000 euclid-euclid-2.10/src/main/java/blogspot/software_and_algorithms/stern_library/data_structure package blogspot.software_and_algorithms.stern_library.data_structure;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
/* Copyright (c) 2012 Kevin L. Stern
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* A static interval tree is a balanced binary search tree which is
* built to store a pre-specified set of intervals so that both point queries
* (queries that return intervals from the set which contain a query point) and
* overlapping interval queries (queries that return intervals from the set
* which overlap a query interval) can be completed in time O(log(n)+k), where n
* is the size of the pre-specified set of intervals and k is the size of the
* result set from the query.
*
* While this implementation of a static interval tree is built to support a
* pre-specified set of intervals, intervals from this set may be added to and
* removed from the tree at will, giving a semi-static nature to the tree. The
* construction process completes in time O(n*log(n)) where n is the size of the
* set of intervals with which the tree is built.
*
* Insertion and deletion of intervals to and from a constructed tree completes
* in time O(log(n)) where n is the size of the set of intervals with which the
* tree was built.
*
* A constructed tree consumes linear space in the size of the set of intervals
* with which the tree was built.
*
* Note that this implementation supports all three closed, open and half-open
* intervals.
*
* @author Kevin L. Stern
*/
public class StaticIntervalTree, T extends Interval> {
private Node root;
private int size;
/**
* Default constructor.
*/
public StaticIntervalTree() {
size = 0;
}
/**
* Internal helper method to construct a subtree structure capable of
* holding the elements of the specified portion of the specified list of
* intervals.
*
* @param intervalList
* the list of intervals with which to build the subtree; must be
* ordered by low endpoint.
* @param low
* the low index of the portion of intervalList to consider,
* inclusive.
* @param high
* the high index of the portion of intervalList to consider,
* exclusive.
*/
private Node buildSubtree(List intervalList, int low, int high) {
U point = intervalList.get((low + high) >>> 1).getLow();
Node result = new Node(point);
int lowPointer = low;
int highPointer = high;
for (int j = low; j < highPointer; j++) {
T next = intervalList.get(j);
if (next.getHigh().compareTo(point) < 0) {
Collections.swap(intervalList, lowPointer++, j);
} else if (next.getLow().compareTo(point) > 0) {
highPointer = j;
}
}
if (low < lowPointer) {
result.setLeft(buildSubtree(intervalList, low, lowPointer));
}
if (highPointer < high) {
result.setRight(buildSubtree(intervalList, highPointer, high));
}
return result;
}
/**
* Build the interval tree to support the elements of the specified set of
* intervals. Note that this method does not insert intervals into the tree
* (this must be done via {@link #insert(Interval)} after the tree is
* built).
*
* @param intervals
* the set of intervals for which the tree is to be built.
*/
public void buildTree(Set intervals) {
List intervalList = new ArrayList(intervals);
Collections.sort(intervalList, new Comparator() {
@Override
public int compare(T o1, T o2) {
return o1.getLow().compareTo(o2.getLow());
}
});
root = buildSubtree(intervalList, 0, intervals.size());
size = 0;
}
/**
* Clear the contents of the tree, leaving the tree structure intact.
*/
public void clear() {
if (root != null) {
List> stack = new ArrayList>();
stack.add(root);
while (!stack.isEmpty()) {
Node next = stack.remove(stack.size() - 1);
next.clear();
Node temp;
if ((temp = next.getLeft()) != null) {
stack.add(temp);
}
if ((temp = next.getRight()) != null) {
stack.add(temp);
}
}
}
size = 0;
}
/**
* Delete the specified interval from this tree.
*
* @param interval
* the interval to delete.
* @return true if an element was deleted as a result of this call, false
* otherwise.
*/
public boolean delete(T interval) {
if (interval == null) {
return false;
}
Node node = root;
while (node != null) {
U temp = node.getPoint();
if (interval.getLow().compareTo(temp) <= 0
&& temp.compareTo(interval.getHigh()) <= 0) {
if (node.delete(interval)) {
size -= 1;
return true;
}
} else if (interval.getHigh().compareTo(temp) < 0) {
node = node.getLeft();
} else {
node = node.getRight();
}
}
return false;
}
/**
* Fetch intervals containing the specified point.
*
* @param target
* the target Collection into which to place the desired
* intervals.
* @param queryPoint
* the query point.
* @return the target Collection.
*/
public > V fetchContainingIntervals(V target,
U queryPoint) {
if (target == null) {
throw new NullPointerException("target is null");
}
if (queryPoint == null) {
throw new NullPointerException("queryPoint is null");
}
Node node = root;
while (node != null) {
U temp = node.getPoint();
if (queryPoint.equals(temp)) {
node.fetchIntervalsContainingNodePoint(target);
node = null;
} else if (queryPoint.compareTo(temp) < 0) {
node.fetchIntervalsContainingPointLow(target, queryPoint, true);
node = node.getLeft();
} else {
node.fetchIntervalsContainingPointHigh(target, queryPoint, true);
node = node.getRight();
}
}
return target;
}
/**
* Fetch intervals overlapping the specified interval.
*
* @param target
* the target Collection into which to place the desired
* intervals.
* @param queryInterval
* the query interval.
* @return the target Collection.
*/
public > V fetchOverlappingIntervals(V target,
T queryInterval) {
if (target == null) {
throw new NullPointerException("target is null");
}
if (queryInterval == null) {
throw new NullPointerException("queryInterval is null");
}
List> stack = new ArrayList>();
if (root != null) {
stack.add(root);
}
while (!stack.isEmpty()) {
Node node = stack.remove(stack.size() - 1);
U temp = node.getPoint();
if (queryInterval.getLow().compareTo(temp) <= 0
&& temp.compareTo(queryInterval.getHigh()) <= 0) {
node.fetchOverlappingIntervals(target, queryInterval);
if (node.getLeft() != null) {
stack.add(node.getLeft());
}
if (node.getRight() != null) {
stack.add(node.getRight());
}
} else if (queryInterval.getHigh().compareTo(temp) < 0) {
node.fetchIntervalsContainingPointLow(target,
queryInterval.getHigh(), queryInterval.isClosedOnHigh());
if (node.getLeft() != null) {
stack.add(node.getLeft());
}
} else {
node.fetchIntervalsContainingPointHigh(target,
queryInterval.getLow(), queryInterval.isClosedOnLow());
if (node.getRight() != null) {
stack.add(node.getRight());
}
}
}
return target;
}
/**
* Get the number of intervals being stored in the tree.
*
* @return the number of intervals being stored in the tree.
*/
public int getSize() {
return size;
}
/**
* Insert the specified interval into this tree. Behavior is undefined when
* the interval was not included in the set of intervals presented at the
* most recent call to {@link #buildTree(Set)}.
*
* @param interval
* the interval to insert.
* @return true if an element was inserted as a result of this call, false
* otherwise.
*/
public boolean insert(T interval) {
Node node = root;
while (node != null) {
U temp = node.getPoint();
if (interval.getLow().compareTo(temp) <= 0
&& temp.compareTo(interval.getHigh()) <= 0) {
if (node.insert(interval)) {
size++;
return true;
}
} else if (interval.getHigh().compareTo(temp) < 0) {
node = node.getLeft();
} else {
node = node.getRight();
}
}
return false;
}
/**
* A node for a static interval tree is a binary tree node
* augmented with an associated point value and the ability to store
* intervals.
*
* Intervals that are stored within this node either contain the node's
* point value or are open at an endpoint that equals the node's point
* value. Intervals are stored so that these two cases are easily
* distinguished from one another: Each such class of interval is stored in
* two structures, one is a tree sorted by low endpoint and the other is a
* tree sorted by high endpoint. This enables efficient point queries as
* well as insertions and deletions from the node.
*/
protected static class Node, T extends Interval> {
private RedBlackTree highOrderedContainingIntervals = new OrderLinkedRedBlackTree(
new Comparator() {
@Override
public int compare(T o1, T o2) {
int result = o1.getHigh().compareTo(o2.getHigh());
if (result == 0) {
if (o1.isClosedOnHigh() != o2.isClosedOnHigh()) {
result = o1.isClosedOnHigh() ? 1 : -1;
} else {
result = o1.compareTo(o2);
}
}
return result > 0 ? -1 : result < 0 ? 1 : 0;
}
});
private RedBlackTree lowOrderedContainingIntervals = new OrderLinkedRedBlackTree(
new Comparator() {
@Override
public int compare(T o1, T o2) {
int result = o1.getLow().compareTo(o2.getLow());
if (result == 0) {
result = o1.compareTo(o2);
}
return result > 0 ? 1 : result < 0 ? -1 : 0;
}
});
private RedBlackTree highOrderedExcludingIntervals = new OrderLinkedRedBlackTree(
new Comparator() {
@Override
public int compare(T o1, T o2) {
int result = o1.getHigh().compareTo(o2.getHigh());
if (result == 0) {
if (o1.isClosedOnHigh() != o2.isClosedOnHigh()) {
result = o1.isClosedOnHigh() ? 1 : -1;
} else {
result = o1.compareTo(o2);
}
}
return result > 0 ? -1 : result < 0 ? 1 : 0;
}
});
private RedBlackTree lowOrderedExcludingIntervals = new OrderLinkedRedBlackTree(
new Comparator() {
@Override
public int compare(T o1, T o2) {
int result = o1.getLow().compareTo(o2.getLow());
if (result == 0) {
result = o1.compareTo(o2);
}
return result > 0 ? 1 : result < 0 ? -1 : 0;
}
});
private Node left, right;
private U point;
/**
* Construct a new node associated with the specified point.
*
* @param point
* the point with which this node is associated.
*/
public Node(U point) {
if (point == null) {
throw new NullPointerException("point is null");
}
this.point = point;
}
/**
* Clear the elements of this node.
*/
public void clear() {
lowOrderedContainingIntervals.clear();
highOrderedContainingIntervals.clear();
lowOrderedExcludingIntervals.clear();
highOrderedExcludingIntervals.clear();
}
/**
* Delete the specified interval from this node.
*
* @param interval
* the interval to delete.
* @return true if an element was deleted as a result of this call,
* false otherwise.
*/
public boolean delete(T interval) {
if (interval.contains(point)) {
if (lowOrderedContainingIntervals.delete(interval) == null) {
return false;
}
highOrderedContainingIntervals.delete(interval);
} else {
if (lowOrderedExcludingIntervals.delete(interval) == null) {
return false;
}
highOrderedExcludingIntervals.delete(interval);
}
return true;
}
/**
* Fetch all intervals from this node that contain the node's point.
*
* @param target
* the target Collection into which to place the desired
* intervals.
*/
public void fetchIntervalsContainingNodePoint(Collection target) {
if (highOrderedContainingIntervals.getSize() > 0) {
RedBlackTree.Node temp = highOrderedContainingIntervals
.getFirstNode();
while (temp != null) {
target.add(temp.getValue());
temp = highOrderedContainingIntervals.getSuccessor(temp);
}
}
}
/**
* Fetch intervals containing the specified value. This method is called
* when the specified value is greater than this node's point.
*
* @param target
* the target Collection into which to place the desired
* intervals.
* @param queryPoint
* the query value.
* @param isClosedOnValue
* true if the search is inclusive of the specified value,
* false otherwise.
*/
public void fetchIntervalsContainingPointHigh(Collection target,
U queryPoint, boolean isClosedOnValue) {
for (Iterator i = highOrderedContainingIntervals.iterator(); i
.hasNext();) {
T next = i.next();
int cmp = next.getHigh().compareTo(queryPoint);
if (cmp < 0 || cmp == 0
&& (!isClosedOnValue || !next.isClosedOnHigh())) {
break;
}
target.add(next);
}
for (Iterator i = highOrderedExcludingIntervals.iterator(); i
.hasNext();) {
T next = i.next();
int cmp = next.getHigh().compareTo(queryPoint);
if (cmp < 0 || cmp == 0
&& (!isClosedOnValue || !next.isClosedOnHigh())) {
break;
}
target.add(next);
}
}
/**
* Fetch intervals containing the specified value. This method is called
* when the specified value is less than this node's point.
*
* @param target
* the target Collection into which to place the desired
* intervals.
* @param queryPoint
* the query value.
* @param isClosedOnValue
* true if the search is inclusive of the specified value,
* false otherwise.
*/
public void fetchIntervalsContainingPointLow(Collection target,
U queryPoint, boolean isClosedOnValue) {
for (Iterator i = lowOrderedContainingIntervals.iterator(); i
.hasNext();) {
T next = i.next();
int cmp = next.getLow().compareTo(queryPoint);
if (cmp > 0 || cmp == 0
&& (!isClosedOnValue || !next.isClosedOnLow()))
break;
target.add(next);
}
for (Iterator i = lowOrderedExcludingIntervals.iterator(); i
.hasNext();) {
T next = i.next();
int cmp = next.getLow().compareTo(queryPoint);
if (cmp > 0 || cmp == 0
&& (!isClosedOnValue || !next.isClosedOnLow()))
break;
target.add(next);
}
}
/**
* Fetch all intervals from this node which overlap the specified
* interval. By contract, the interval must be such that
* {@link Interval#getLow()} {@literal <}= {@link Node#getPoint()}
* {@literal <}= {@link Interval#getHigh()}.
*
* @param target
* the target Collection into which to place the desired
* intervals.
* @param queryInterval
* the query interval.
*/
public void fetchOverlappingIntervals(Collection target,
Interval queryInterval) {
if (queryInterval.getLow().compareTo(point) == 0) {
fetchIntervalsContainingPointHigh(target,
queryInterval.getLow(), queryInterval.isClosedOnLow());
} else if (queryInterval.getHigh().compareTo(point) == 0) {
fetchIntervalsContainingPointLow(target,
queryInterval.getHigh(), queryInterval.isClosedOnHigh());
} else {
fetchIntervalsContainingNodePoint(target);
if (highOrderedExcludingIntervals.getSize() > 0) {
RedBlackTree.Node temp = highOrderedExcludingIntervals
.getFirstNode();
while (temp != null) {
target.add(temp.getValue());
temp = highOrderedExcludingIntervals.getSuccessor(temp);
}
}
}
}
/**
* Get the left child.
*
* @return the left child, null if none exists.
*/
public Node getLeft() {
return left;
}
/**
* Get the point associated with this node.
*
* @return the point associated with this node.
*/
public U getPoint() {
return point;
}
/**
* Get the right child.
*
* @return the right child, null if none exists.
*/
public Node getRight() {
return right;
}
/**
* Insert the specified interval into this node. By contract, the
* interval must be such that {@link Interval#getLow()} <=
* {@link Node#getPoint()} <= {@link Interval#getHigh()}.
*
* @param interval
* the interval to insert.
* @return true if an element was inserted as a result of this call,
* false otherwise.
*/
private boolean insert(T interval) {
if (interval.contains(point)) {
if (lowOrderedContainingIntervals.insert(interval) == null) {
return false;
}
highOrderedContainingIntervals.insert(interval);
} else {
if (lowOrderedExcludingIntervals.insert(interval) == null) {
return false;
}
highOrderedExcludingIntervals.insert(interval);
}
return true;
}
/**
* Set the left child to the specified node.
*
* @param node
* the left child.
*/
private void setLeft(Node node) {
left = node;
}
/**
* Set the right child to the specified node.
*
* @param node
* the right child.
*/
private void setRight(Node node) {
right = node;
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
StringBuilder builder = new StringBuilder("{");
for (Iterator i = lowOrderedContainingIntervals.iterator();;) {
builder.append(i.next().toString());
if (i.hasNext()) {
builder.append(", ");
} else {
break;
}
}
for (Iterator i = lowOrderedExcludingIntervals.iterator(); i
.hasNext();) {
builder.append(", ").append(i.next().toString());
}
builder.append("}");
return builder.toString();
}
}
} ThriftyList.java 0000664 0000000 0000000 00000144521 14742747421 0036342 0 ustar 00root root 0000000 0000000 euclid-euclid-2.10/src/main/java/blogspot/software_and_algorithms/stern_library/data_structure package blogspot.software_and_algorithms.stern_library.data_structure;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.AbstractList;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Deque;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.NoSuchElementException;
/* Copyright (C) 2012 Kevin L. Stern.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* A dynamically sized compact data structure implementing both the
* Deque and List interfaces. Internally, this data structure
* maintains multiple sublists each having size O(sqrt(this.size())), giving a
* reasonable upper bound on the size of any one contiguous memory block
* consumed by an instance. Furthermore, the resizing approach taken by this
* data structure guarantees a memory overhead of O(sqrt(this.size())) instead
* of the O(this.size()) overhead that is typical of resizable array
* implementations (including ArrayList) while maintaining O(1) amortized time
* add and remove operations; the data structure is compact in the
* sense that this is theoretically optimal [ResizableArraysTR]. In addition,
* this data structure achieves O(sqrt(this.size())) time middle
* insertion/deletion by maintaining circular lists throughout.
*
*
* @techreport{ResizableArraysTR, author = {Andrej Brodnik and Svante Carlsson
* and Erik D. Demaine and J. Ian Munro and
* Robert Sedgewick}, title = {Resizable Arrays
* in Optimal Time and Space}, institution =
* {Department of Computer Science, University of
* Waterloo}, institutionurl =
* {http://www.cs.uwaterloo.ca/}, number =
* {CS-99-09}, numberurl =
* {http://www.cs.uwaterloo
* .ca/research/tr/1999/09/CS-99-09.pdf}, year =
* {1999}}
* @misc{Goodrich_tieredvectors:, author = {Michael T. Goodrich and John G.
* Kloss and II}, title = {Tiered Vectors:
* Efficient Dynamic Arrays for Rank-Based
* Sequences}, year = {} }
*
*
* @author Kevin L. Stern
*
* @see ArrayList
* @see ArrayDeque
*/
public class ThriftyList extends AbstractList implements List,
Deque, Serializable, Cloneable {
/**
* Helper function to copy all data from the source to the destination and
* return the destination.
*
* @param source
* the source list.
* @param destination
* the destination list.
* @return the destination list.
*/
public static > E copyTo(E source,
E destination) {
assert destination.capacity() >= source.size();
destination.addAll(source);
return destination;
}
/**
* Helper function to merge two {@link ListInternal} instances into the
* target {@link ListInternal} instance. The target capacity should be equal
* to the sum of the capacities of its constituent lists.
*
* @param l1
* the first constituent list.
* @param l2
* the second constituent list.
* @return the target {@link ListInternal} instance containing the data of
* l1 followed by the data of l2.
*/
public static > E merge(E l1, E l2, E target) {
assert target.capacity() == l1.capacity() + l2.capacity();
target.addAll(l1);
target.addAll(l2);
return target;
}
/**
* Helper function to split the data of the source list between two
* destination lists. The sum of the individual capacities of the
* destination lists should be equal to the capacity of the source list.
*
* @param alignRight
* true for right alignment of the data in the src to the two
* destinations, false for left alignment.
*/
public static void split(ListInternal src, ListInternal dst1,
ListInternal dst2, boolean alignRight) {
assert dst1.capacity() + dst2.capacity() == src.capacity();
if (alignRight) {
int splitIndex = src.size() - dst2.capacity();
dst2.addSome(src, Math.max(0, splitIndex),
Math.min(src.size(), dst2.capacity()));
dst1.addSome(src, 0, splitIndex);
} else {
dst1.addSome(src, 0, Math.min(src.size(), dst1.capacity()));
dst2.addSome(src, dst1.capacity(), src.size() - dst1.capacity());
}
}
private FixedListInternal> sublists;
private int size;
private int capacity;
private int smallSublistCount;
private int smallSublistSizeExp, largeSublistSizeExp;
private int headSublistIndex, tailSublistIndex;
private int freeCapacityHead;
private int halveCapacityLimit, doubleCapacityLimit;
/**
* Construct an empty instance of {@link ThriftyList} with the default
* capacity.
*/
public ThriftyList() {
sublists = new FixedListInternal>(4);
sublists.addTail(new CircularListInternal(2));
sublists.addTail(new CircularListInternal(4));
sublists.addTail(new CircularListInternal(4));
capacity = 10;
smallSublistSizeExp = 1;
largeSublistSizeExp = 2;
halveCapacityLimit = 4;
doubleCapacityLimit = 16;
smallSublistCount = 1;
size = 0;
headSublistIndex = tailSublistIndex = 1;
freeCapacityHead = 6;
}
/**
* {@inheritDoc}
*/
@Override
public void add(int index, T item) {
if (0 > index || index > size) {
throw new IndexOutOfBoundsException();
}
int sublistIndex, sublistOffset;
int projectedIndex = index + freeCapacityHead;
int smallListCapacity = smallSublistCount << smallSublistSizeExp;
if (projectedIndex < smallListCapacity) {
sublistIndex = projectedIndex >>> smallSublistSizeExp;
sublistOffset = sublistIndex == headSublistIndex ? index
: projectedIndex & ((1 << smallSublistSizeExp) - 1);
} else {
int largeListOffset = projectedIndex - smallListCapacity;
sublistIndex = smallSublistCount
+ (largeListOffset >>> largeSublistSizeExp);
sublistOffset = sublistIndex == headSublistIndex ? index
: largeListOffset & ((1 << largeSublistSizeExp) - 1);
}
addImpl(index, sublistIndex, sublistOffset, item);
}
/**
* {@inheritDoc}
*/
@Override
public boolean add(T item) {
CircularListInternal sublist = sublists.get(tailSublistIndex);
if (sublist.isFull()) {
if (tailSublistIndex != sublists.size() - 1 || growTail()) {
tailSublistIndex += 1;
}
sublist = sublists.get(tailSublistIndex);
}
sublist.addTail(item);
if (tailSublistIndex == headSublistIndex) {
freeCapacityHead -= 1;
}
size++;
assert checkListState(false, false);
return true;
}
/**
* {@inheritDoc}
*/
@Override
public void addFirst(T item) {
CircularListInternal sublist = sublists.get(headSublistIndex);
if (sublist.isFull()) {
if (headSublistIndex == 0) {
growHead();
} else {
headSublistIndex -= 1;
}
sublist = sublists.get(headSublistIndex);
}
sublist.addHead(item);
freeCapacityHead -= 1;
size++;
assert checkListState(false, false);
}
/**
* Internal add method requiring the sublist index and offset be
* pre-calculated.
*
*/
protected void addImpl(int index, int sublistIndex, int sublistOffset,
T item) {
if (sublistIndex < headSublistIndex + (calculateSublistsUsed() >>> 1)) {
if (index == 0) {
addFirst(item);
} else {
CircularListInternal prev = sublists.get(headSublistIndex);
if (sublistOffset == 0) {
sublistIndex -= 1;
sublistOffset = sublists.get(sublistIndex).size() - 1;
} else {
sublistOffset -= 1;
}
T carryItem = prev.removeHead();
CircularListInternal next = prev;
for (int j = headSublistIndex + 1; j <= sublistIndex; j++) {
next = sublists.get(j);
prev.addTail(next.removeHead());
prev = next;
}
next.add(sublistOffset, item);
addFirst(carryItem);
}
} else {
if (index == size) {
add(item);
} else {
CircularListInternal prev = sublists.get(tailSublistIndex);
T carryItem = prev.removeTail();
CircularListInternal next = prev;
for (int j = tailSublistIndex - 1; j >= sublistIndex; j--) {
next = sublists.get(j);
prev.addHead(next.removeTail());
prev = next;
}
next.add(sublistOffset, item);
add(carryItem);
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void addLast(T item) {
add(item);
}
/**
* Helper method to calculate the free capacity at the head end of the
* sublists.
*/
protected int calculateFreeCapacityHead() {
return headSublistIndex == 0 ? sublists.get(0).calculateFreeCapacity()
: sublists.get(0).calculateFreeCapacity()
+ sublists.get(1).calculateFreeCapacity();
}
/**
* Helper method to fetch the count of used sublists.
*/
protected int calculateSublistsUsed() {
return tailSublistIndex - headSublistIndex + 1;
}
/**
* Manipulate the state of this instance appropriately if its capacity has
* fallen outside of either capacity limit.
*/
protected void checkCapacity() {
if (capacity >= doubleCapacityLimit) {
assert checkListState(false, true);
largeSublistSizeExp += 1;
smallSublistSizeExp += 1;
halveCapacityLimit = doubleCapacityLimit;
doubleCapacityLimit <<= 2;
smallSublistCount = sublists.size();
} else if (capacity < halveCapacityLimit) {
assert checkListState(true, false);
largeSublistSizeExp -= 1;
smallSublistSizeExp -= 1;
doubleCapacityLimit = halveCapacityLimit;
halveCapacityLimit >>>= 2;
smallSublistCount = 0;
}
}
private boolean checkListState(boolean checkAllSmall, boolean checkAllLarge) {
if (checkAllLarge) {
for (int j = 0; j < sublists.size(); j++) {
assert sublists.get(j).capacity() == (1 << largeSublistSizeExp);
}
} else if (checkAllSmall) {
for (int j = 0; j < sublists.size(); j++) {
assert sublists.get(j).capacity() == (1 << smallSublistSizeExp);
}
} else {
assert headSublistIndex + 1 <= 2;
assert sublists.size() - tailSublistIndex <= 2;
assert sublists.size() >= (sublists.capacity() >>> 2);
assert freeCapacityHead == calculateFreeCapacityHead();
int localCapacity = 0;
for (int j = 0; j < sublists.size(); j++) {
localCapacity += sublists.get(j).capacity();
if (j < headSublistIndex || j > tailSublistIndex) {
assert sublists.get(j).isEmpty();
} else if (size > 0) {
if (j == headSublistIndex || j == tailSublistIndex) {
assert !sublists.get(j).isEmpty();
} else {
assert sublists.get(j).calculateFreeCapacity() == 0;
}
}
if (j < smallSublistCount) {
assert sublists.get(j).capacity() == (1 << smallSublistSizeExp);
} else {
assert sublists.get(j).capacity() == (1 << largeSublistSizeExp);
}
}
assert localCapacity == capacity;
}
return true;
}
/**
* {@inheritDoc}
*/
@Override
public void clear() {
sublists = new FixedListInternal>(4);
sublists.addTail(new CircularListInternal(2));
sublists.addTail(new CircularListInternal(4));
sublists.addTail(new CircularListInternal(4));
capacity = 10;
smallSublistSizeExp = 1;
largeSublistSizeExp = 2;
halveCapacityLimit = 4;
doubleCapacityLimit = 16;
smallSublistCount = 1;
size = 0;
headSublistIndex = tailSublistIndex = 1;
freeCapacityHead = 6;
}
/**
* Clone this list.
*/
@Override
public Object clone() {
try {
ThriftyList result = (ThriftyList) super.clone();
result.sublists = (FixedListInternal>) sublists
.clone();
for (int j = 0; j < result.sublists.size(); j++) {
result.sublists.set(j,
(CircularListInternal) result.sublists.get(j)
.clone());
}
return result;
} catch (CloneNotSupportedException e) {
throw new InternalError();
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean contains(Object o) {
return indexOf(o) != -1;
}
/**
* {@inheritDoc}
*/
@Override
public Iterator descendingIterator() {
return new ReverseIter(size - 1);
}
/**
* {@inheritDoc}
*/
@Override
public T element() {
return getFirst();
}
/**
* {@inheritDoc}
*/
@Override
public T get(int index) {
if (0 > index || index >= size) {
throw new IndexOutOfBoundsException();
}
int sublistIndex, sublistOffset;
int projectedIndex = index + freeCapacityHead;
int smallListCapacity = smallSublistCount << smallSublistSizeExp;
if (projectedIndex < smallListCapacity) {
sublistIndex = projectedIndex >>> smallSublistSizeExp;
sublistOffset = sublistIndex == headSublistIndex ? index
: projectedIndex & ((1 << smallSublistSizeExp) - 1);
} else {
int largeListOffset = projectedIndex - smallListCapacity;
sublistIndex = smallSublistCount
+ (largeListOffset >>> largeSublistSizeExp);
sublistOffset = sublistIndex == headSublistIndex ? index
: largeListOffset & ((1 << largeSublistSizeExp) - 1);
}
return sublists.get(sublistIndex).get(sublistOffset);
}
/**
* {@inheritDoc}
*/
@Override
public T getFirst() {
if (size == 0) {
throw new NoSuchElementException();
}
return sublists.get(headSublistIndex).getHead();
}
/**
* {@inheritDoc}
*/
@Override
public T getLast() {
if (size == 0) {
throw new NoSuchElementException();
}
return sublists.get(tailSublistIndex).getTail();
}
/**
* Grow the head of this list by adding capacity of
* {@link #smallSublistSizeExp} to the beginning. Note that each time
* capacity is added to this {@link ThriftyList}, small lists will be merged
* to large lists.
*
* @return true if a new sublist was added, false otherwise.
*/
protected boolean growHead() {
assert sublists.getHead().isFull();
mergeNextSmallSublists();
checkCapacity();
if (!sublists.getHead().isFull()) {
return false;
}
if (sublists.isFull()) {
sublists = copyTo(
sublists,
new FixedListInternal>(sublists
.capacity() << 1));
}
int smallListSize = 1 << smallSublistSizeExp;
sublists.addHead(new CircularListInternal(smallListSize));
smallSublistCount += 1;
capacity += smallListSize;
freeCapacityHead += smallListSize;
tailSublistIndex += 1;
return true;
}
/**
* Grow the tail of this list. Note that each time capacity is added to this
* {@link ThriftyList}, small lists will be merged to large lists.
*
* @return true if a new sublist was added, false otherwise.
*/
protected boolean growTail() {
assert sublists.getTail().isFull();
mergeNextSmallSublists();
checkCapacity();
if (!sublists.getTail().isFull()) {
return false;
}
if (sublists.isFull()) {
sublists = copyTo(
sublists,
new FixedListInternal>(sublists
.capacity() << 1));
}
int largeListSize = 1 << largeSublistSizeExp;
sublists.addTail(new CircularListInternal(largeListSize));
capacity += largeListSize;
return true;
}
/**
* {@inheritDoc}
*/
@Override
public int indexOf(Object o) {
int total = 0;
for (int j = headSublistIndex; j <= tailSublistIndex; j++) {
CircularListInternal next = sublists.get(j);
int index = next.indexOf(o);
if (index != -1) {
return total + index;
}
total += next.size();
}
return -1;
}
/**
* {@inheritDoc}
*/
@Override
public boolean isEmpty() {
return size == 0;
}
/**
* {@inheritDoc}
*/
@Override
public Iterator iterator() {
return new Iter(0);
}
/**
* {@inheritDoc}
*/
@Override
public int lastIndexOf(Object o) {
int total = 0;
for (int j = tailSublistIndex; j >= headSublistIndex; j--) {
CircularListInternal next = sublists.get(j);
total += next.size();
int index = next.lastIndexOf(o);
if (index != -1) {
return size - total + index;
}
}
return -1;
}
/**
* {@inheritDoc}
*/
@Override
public ListIterator listIterator() {
return listIterator(0);
}
/**
* {@inheritDoc}
*/
@Override
public ListIterator listIterator(int index) {
if (0 > index || index > size) {
throw new IndexOutOfBoundsException();
}
return new Iter(index);
}
/**
* Merge two small sublists to a single large sublist if two small sublists
* exist, or double the capacity of the single small sublist to that of a
* large sublist if only a single small sublist exists. Do nothing if no
* small sublists exist.
*/
protected void mergeNextSmallSublists() {
if (smallSublistCount >= 2) {
CircularListInternal small2 = sublists
.remove(--smallSublistCount);
CircularListInternal small1 = sublists.get(--smallSublistCount);
sublists.set(
smallSublistCount,
merge(small1, small2,
new CircularListInternal(small1.capacity()
+ small2.capacity())));
if (sublists.size() <= (sublists.capacity() >>> 2)) {
sublists = copyTo(
sublists,
new FixedListInternal>(sublists
.capacity() >>> 1));
}
if (size > 0) {
headSublistIndex = sublists.getHead().isEmpty() ? 1 : 0;
tailSublistIndex = sublists.getTail().isEmpty() ? sublists
.size() - 2 : sublists.size() - 1;
} else {
headSublistIndex = tailSublistIndex = 0;
}
if (headSublistIndex == tailSublistIndex) {
freeCapacityHead = calculateFreeCapacityHead();
}
} else if (smallSublistCount == 1) {
CircularListInternal small = sublists.get(--smallSublistCount);
sublists.set(
smallSublistCount,
copyTo(small, new CircularListInternal(
small.capacity() << 1)));
int smallSublistSize = (1 << smallSublistSizeExp);
capacity += smallSublistSize;
freeCapacityHead += smallSublistSize;
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean offer(T e) {
return offerLast(e);
}
/**
* {@inheritDoc}
*/
@Override
public boolean offerFirst(T e) {
addFirst(e);
return true;
}
/**
* {@inheritDoc}
*/
@Override
public boolean offerLast(T e) {
add(e);
return true;
}
/**
* {@inheritDoc}
*/
@Override
public T peek() {
return peekFirst();
}
/**
* {@inheritDoc}
*/
@Override
public T peekFirst() {
if (size == 0) {
return null;
}
return sublists.get(headSublistIndex).getHead();
}
/**
* {@inheritDoc}
*/
@Override
public T peekLast() {
if (size == 0) {
return null;
}
return sublists.get(tailSublistIndex).getTail();
}
/**
* {@inheritDoc}
*/
@Override
public T poll() {
return pollFirst();
}
/**
* {@inheritDoc}
*/
@Override
public T pollFirst() {
if (size == 0) {
return null;
}
return removeFirst();
}
/**
* {@inheritDoc}
*/
@Override
public T pollLast() {
if (size == 0) {
return null;
}
return removeLast();
}
/**
* {@inheritDoc}
*/
@Override
public T pop() {
return removeFirst();
}
/**
* {@inheritDoc}
*/
@Override
public void push(T e) {
addFirst(e);
}
/**
* Initializes this instance from the specified stream.
*/
private void readObject(ObjectInputStream s) throws java.io.IOException,
ClassNotFoundException {
clear();
int localSize = s.readInt();
for (int j = 0; j < localSize; j++) {
add((T) s.readObject());
}
}
/**
* {@inheritDoc}
*/
@Override
public T remove() {
return removeFirst();
}
/**
* {@inheritDoc}
*/
@Override
public T remove(int index) {
if (0 > index || index >= size) {
throw new IndexOutOfBoundsException();
}
int sublistIndex, sublistOffset;
int projectedIndex = index + freeCapacityHead;
int smallListCapacity = smallSublistCount << smallSublistSizeExp;
if (projectedIndex < smallListCapacity) {
sublistIndex = projectedIndex >>> smallSublistSizeExp;
sublistOffset = sublistIndex == headSublistIndex ? index
: projectedIndex & ((1 << smallSublistSizeExp) - 1);
} else {
int largeListOffset = projectedIndex - smallListCapacity;
sublistIndex = smallSublistCount
+ (largeListOffset >>> largeSublistSizeExp);
sublistOffset = sublistIndex == headSublistIndex ? index
: largeListOffset & ((1 << largeSublistSizeExp) - 1);
}
return removeImpl(sublistIndex, sublistOffset);
}
/**
* {@inheritDoc}
*/
@Override
public T removeFirst() {
if (size == 0) {
throw new NoSuchElementException();
}
return removeImpl(headSublistIndex, 0);
}
/**
* {@inheritDoc}
*/
@Override
public boolean removeFirstOccurrence(Object o) {
int index = indexOf(o);
if (index == -1) {
return false;
}
remove(index);
return true;
}
/**
* Internal remove method requiring the sublist index and offset be
* pre-calculated.
*
* @return the item removed from this list.
*/
protected T removeImpl(int sublistIndex, int sublistOffset) {
T result;
if (sublistIndex < headSublistIndex + (calculateSublistsUsed() >>> 1)) {
CircularListInternal prev = sublists.get(sublistIndex);
result = prev.remove(sublistOffset);
size -= 1;
for (int j = sublistIndex - 1; j >= headSublistIndex; j--) {
CircularListInternal next = sublists.get(j);
prev.addHead(next.removeTail());
prev = next;
}
freeCapacityHead += 1;
if (sublists.get(headSublistIndex).isEmpty()) {
if (headSublistIndex < tailSublistIndex) {
headSublistIndex += 1;
if (headSublistIndex == tailSublistIndex) {
freeCapacityHead += sublists.get(tailSublistIndex)
.calculateFreeCapacity();
}
}
shrinkHead();
}
} else {
CircularListInternal prev = sublists.get(sublistIndex);
result = prev.remove(sublistOffset);
size -= 1;
if (sublistIndex <= headSublistIndex) {
freeCapacityHead += 1;
}
for (int j = sublistIndex + 1; j <= tailSublistIndex; j++) {
CircularListInternal next = sublists.get(j);
prev.addTail(next.removeHead());
prev = next;
}
if (sublists.get(tailSublistIndex).isEmpty()) {
tailSublistIndex = Math.max(headSublistIndex,
tailSublistIndex - 1);
shrinkTail();
}
}
assert checkListState(false, false);
return result;
}
/**
* {@inheritDoc}
*/
@Override
public T removeLast() {
if (size == 0) {
throw new NoSuchElementException();
}
CircularListInternal sublist = sublists.get(tailSublistIndex);
return removeImpl(tailSublistIndex, sublist.size() - 1);
}
/**
* {@inheritDoc}
*/
@Override
public boolean removeLastOccurrence(Object o) {
int index = lastIndexOf(o);
if (index == -1) {
return false;
}
remove(index);
return true;
}
/**
* {@inheritDoc}
*/
@Override
public T set(int index, T item) {
if (0 > index || index >= size) {
throw new IndexOutOfBoundsException();
}
int sublistIndex, sublistOffset;
int projectedIndex = index + freeCapacityHead;
int smallListCapacity = smallSublistCount << smallSublistSizeExp;
if (projectedIndex < smallListCapacity) {
sublistIndex = projectedIndex >>> smallSublistSizeExp;
sublistOffset = sublistIndex == headSublistIndex ? index
: projectedIndex & ((1 << smallSublistSizeExp) - 1);
} else {
int largeListOffset = projectedIndex - smallListCapacity;
sublistIndex = smallSublistCount
+ (largeListOffset >>> largeSublistSizeExp);
sublistOffset = sublistIndex == headSublistIndex ? index
: largeListOffset & ((1 << largeSublistSizeExp) - 1);
}
return setImpl(sublistIndex, sublistOffset, item);
}
/**
* Internal set method requiring the sublist index and offset be
* pre-calculated.
*
* @return the element of this list overwritten by the specified item.
*/
protected T setImpl(int sublistIndex, int sublistOffset, T item) {
CircularListInternal sublist = sublists.get(sublistIndex);
return sublist.set(sublistOffset, item);
}
/**
* Shrink the head of this list. Note that each time capacity is removed
* from this {@link ThriftyList}, large lists will be split to small lists.
*/
protected void shrinkHead() {
while (headSublistIndex >= 2) {
splitNextLargeSublist();
CircularListInternal head = sublists.removeHead();
assert head.isEmpty();
if (sublists.size() <= (sublists.capacity() >>> 2)) {
sublists = copyTo(
sublists,
new FixedListInternal>(sublists
.capacity() >>> 1));
}
capacity -= head.capacity();
headSublistIndex = Math.max(headSublistIndex - 1, 0);
tailSublistIndex -= 1;
freeCapacityHead = calculateFreeCapacityHead();
if (head.capacity() == (1 << smallSublistSizeExp)) {
smallSublistCount -= 1;
}
checkCapacity();
shrinkTail();
}
}
/**
* Shrink the tail of this list. Note that each time capacity is removed
* from this {@link ThriftyList}, large lists will be split to small lists.
*/
protected void shrinkTail() {
while (sublists.size() - tailSublistIndex > 2) {
splitNextLargeSublist();
CircularListInternal tail = sublists.removeTail();
assert tail.isEmpty();
if (sublists.size() <= (sublists.capacity() >>> 2)) {
sublists = copyTo(
sublists,
new FixedListInternal>(sublists
.capacity() >>> 1));
}
capacity -= tail.capacity();
freeCapacityHead = calculateFreeCapacityHead();
if (tail.capacity() == (1 << smallSublistSizeExp)) {
smallSublistCount -= 1;
}
checkCapacity();
shrinkHead();
}
}
/**
* {@inheritDoc}
*/
@Override
public int size() {
return size;
}
/**
* Splits a large sublist into two small sublists. If one of the small
* sublists is empty, then it is discarded. No operation is performed if no
* large sublists exist.
*/
protected void splitNextLargeSublist() {
if (smallSublistCount < sublists.size()) {
if (sublists.isFull()) {
sublists = copyTo(
sublists,
new FixedListInternal>(sublists
.capacity() << 1));
}
int firstLargeSublistIndex = smallSublistCount;
boolean isHeadList = firstLargeSublistIndex <= headSublistIndex;
boolean isTailList = firstLargeSublistIndex >= tailSublistIndex;
CircularListInternal large = sublists
.get(firstLargeSublistIndex);
assert large.capacity() == (1 << largeSublistSizeExp);
CircularListInternal small1 = new CircularListInternal(
large.capacity() >>> 1);
CircularListInternal small2 = new CircularListInternal