pax_global_header00006660000000000000000000000064146172141070014515gustar00rootroot0000000000000052 comment=b86a49bc00e99941e9af5f2bddfe893e33f11ccd euclid-euclid-2.9/000077500000000000000000000000001461721410700140775ustar00rootroot00000000000000euclid-euclid-2.9/.github/000077500000000000000000000000001461721410700154375ustar00rootroot00000000000000euclid-euclid-2.9/.github/workflows/000077500000000000000000000000001461721410700174745ustar00rootroot00000000000000euclid-euclid-2.9/.github/workflows/maven.yml000066400000000000000000000017071461721410700213320ustar00rootroot00000000000000name: 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.9/.gitignore000066400000000000000000000001721461721410700160670ustar00rootroot00000000000000foo .project .settings/ .classpath ^.hgignore~$ ^.gitignore~$ ^target/.* target/ # IntelliJ Idear files /.idea/* **/*.iml euclid-euclid-2.9/CITATION.cff000066400000000000000000000010431461721410700157670ustar00rootroot00000000000000cff-version: 1.2.0 message: "If you use this software, please cite it as below." title: Euclid version: 2.9 date-released: 2024-05-09 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.9/LICENSE.txt000066400000000000000000000261351461721410700157310ustar00rootroot00000000000000 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.9/README.md000066400000000000000000000023271461721410700153620ustar00rootroot00000000000000# CML Euclid [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.blueobelisk/euclid/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.blueobelisk/euclid) [![Build Status](https://github.com/BlueObelisk/euclid/actions/workflows/maven.yml/badge.svg)](https://github.com/BlueObelisk/euclid/actions/workflows/maven.yml) [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.5815148.svg)](https://doi.org/10.5281/zenodo.5815148) [![codecov](https://codecov.io/gh/BlueObelisk/euclid/branch/main/graph/badge.svg?token=E1NGWVWL04)](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.10-SNAPSHOT ``` Deploy to Sonatype with the following commands, for snapshots and releases respectively: ```shell mvn clean deploy ``` euclid-euclid-2.9/pom.xml000066400000000000000000000273161461721410700154250ustar00rootroot00000000000000 4.0.0 org.blueobelisk euclid 2.9 jar UTF-8 UTF-8 CML Euclid A Java library for 2D and 3D geometric calculations https://github.com/BlueObelisk/euclid Apache License, Version 2.0 http://www.apache.org/licenses/LICENSE-2.0.txt repo https://github.com/BlueObelisk/euclid scm:git:git://github.com/blueobelisk/euclid.git scm:git:ssh://git@github.com/blueobelisk/euclid.git HEAD pm286 Peter Murray-Rust 1994 cml-discuss https://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 ossrh https://s01.oss.sonatype.org/content/repositories/snapshots ossrh https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ Peter Murray-Rust http://wwmm.ch.cam.ac.uk/blogs/murrayrust/ org.apache.maven.plugins maven-enforcer-plugin 3.4.1 enforce-maven enforce 3.6.3 1.8 org.apache.maven.plugins maven-compiler-plugin 3.13.0 1.8 1.8 org.apache.maven.plugins maven-javadoc-plugin 3.6.3 attach-javadocs jar aggregate aggregate site maven-source-plugin 3.3.1 attach-sources jar-no-fork org.codehaus.mojo cobertura-maven-plugin 2.7 false org.xmlcml.* 80 80 org/xmlcml/**/*.class clean pre-site clean instrument site instrument cobertura check com.mycila.maven-license-plugin maven-license-plugin 1.10.b1
src/main/resources/header.txt
.travis.yml .gitignore README.md LICENSE.* **/README src/test/resources/org/xmlcml/files/** src/*/java/blogspot/software_and_algorithms/stern_library/** **/*.java-old **/junk .idea/**
org.sonatype.plugins nexus-staging-maven-plugin 1.6.13 true ossrh https://s01.oss.sonatype.org/ true org.apache.maven.plugins maven-gpg-plugin 3.2.4 sign-artifacts verify sign org.jacoco jacoco-maven-plugin 0.8.12 start-agent prepare-agent generate-report report
junit junit 4.13.2 commons-io commons-io 2.16.1 org.apache.logging.log4j log4j 2.23.1 pom org.apache.logging.log4j log4j-1.2-api 2.23.1 org.apache.logging.log4j log4j-core 2.23.1 org.apache.commons commons-lang3 3.14.0 org.apache.commons commons-math 2.2 joda-time joda-time 2.12.7 xom xom 1.3.9 xerces xercesImpl release maven-assembly-plugin 3.7.1 src org.apache.maven.plugins maven-project-info-reports-plugin 3.5.0 index summary dependencies project-team license cim scm org.apache.maven.plugins maven-javadoc-plugin org.apache.maven.plugins maven-surefire-report-plugin 3.2.5 org.apache.maven.plugins maven-jxr-plugin 3.3.2 org.apache.maven.plugins maven-pmd-plugin 3.22.0 1.7 true org.apache.maven.plugins maven-checkstyle-plugin 3.3.1 src/test/resources/checkstyle.xml org.codehaus.mojo cobertura-maven-plugin org.codehaus.mojo apt-maven-plugin 1.0-alpha-5
euclid-euclid-2.9/src/000077500000000000000000000000001461721410700146665ustar00rootroot00000000000000euclid-euclid-2.9/src/main/000077500000000000000000000000001461721410700156125ustar00rootroot00000000000000euclid-euclid-2.9/src/main/java/000077500000000000000000000000001461721410700165335ustar00rootroot00000000000000euclid-euclid-2.9/src/main/java/blogspot/000077500000000000000000000000001461721410700203645ustar00rootroot00000000000000euclid-euclid-2.9/src/main/java/blogspot/software_and_algorithms/000077500000000000000000000000001461721410700252715ustar00rootroot00000000000000euclid-euclid-2.9/src/main/java/blogspot/software_and_algorithms/stern_library/000077500000000000000000000000001461721410700301505ustar00rootroot00000000000000euclid-euclid-2.9/src/main/java/blogspot/software_and_algorithms/stern_library/data_structure/000077500000000000000000000000001461721410700332015ustar00rootroot00000000000000DynamicIntervalTree.java000066400000000000000000000241161461721410700377020ustar00rootroot00000000000000euclid-euclid-2.9/src/main/java/blogspot/software_and_algorithms/stern_library/data_structurepackage 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.java000066400000000000000000000155321461721410700355570ustar00rootroot00000000000000euclid-euclid-2.9/src/main/java/blogspot/software_and_algorithms/stern_library/data_structurepackage 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.java000066400000000000000000000125061461721410700404030ustar00rootroot00000000000000euclid-euclid-2.9/src/main/java/blogspot/software_and_algorithms/stern_library/data_structurepackage 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.java000066400000000000000000000460701461721410700362630ustar00rootroot00000000000000euclid-euclid-2.9/src/main/java/blogspot/software_and_algorithms/stern_library/data_structurepackage 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 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 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.java000066400000000000000000000460331461721410700375470ustar00rootroot00000000000000euclid-euclid-2.9/src/main/java/blogspot/software_and_algorithms/stern_library/data_structurepackage 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.java000066400000000000000000001445211461721410700362610ustar00rootroot00000000000000euclid-euclid-2.9/src/main/java/blogspot/software_and_algorithms/stern_library/data_structurepackage 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( large.capacity() >>> 1); split(large, small1, small2, isHeadList); if (isHeadList) { sublists.set(firstLargeSublistIndex, small2); sublists.add(firstLargeSublistIndex, small1); smallSublistCount += 2; if (small1.isEmpty()) { headSublistIndex += 1; } tailSublistIndex += 1; } else if (isTailList) { sublists.set(firstLargeSublistIndex, small1); sublists.add(firstLargeSublistIndex + 1, small2); smallSublistCount += 2; if (!small2.isEmpty()) { tailSublistIndex += 1; } } else { sublists.set(firstLargeSublistIndex, small2); sublists.add(firstLargeSublistIndex, small1); smallSublistCount += 2; tailSublistIndex += 1; } } } /** * {@inheritDoc} */ @Override public T[] toArray() { return (T[]) toArray((Object[]) null); } /** * {@inheritDoc} */ @Override public U[] toArray(U[] target) { if (target == null || target.length < size) { target = (U[]) new Object[size]; } int index = 0; for (int j = headSublistIndex; j <= tailSublistIndex; j++) { CircularListInternal sublist = sublists.get(j); sublist.fill((T[]) target, index, 0, sublist.size()); index += sublist.size(); } if (target.length > size) { target[size] = null; } return target; } /** * {@inheritDoc} */ @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("["); for (int j = headSublistIndex; j <= tailSublistIndex; j++) { builder.append(sublists.get(j).toString()).append(","); } builder.setCharAt(builder.length() - 1, ']'); return builder.toString(); } /** * Writes this instance to the specified stream. */ private void writeObject(ObjectOutputStream s) throws java.io.IOException { s.writeInt(size); for (T next : this) { s.writeObject(next); } } /** * A fixed capacity circular list. This is strictly an internal helper class * to {@link ThriftyList} as it performs no bounds checking, delegating all * such responsibilities to {@link ThriftyList} itself. */ protected static class CircularListInternal implements ListInternal, Cloneable { protected Object[] array; protected int head; protected int size; /** * Construct an empty instance with the specified capacity. * * @param capacity * the capacity of the list, must be a power of two. */ public CircularListInternal(int capacity) { assert (capacity & (capacity - 1)) == 0; array = new Object[capacity]; head = size = 0; } /** * {@inheritDoc} */ @Override public void add(int index, T item) { assert 0 <= index && index <= size; assert size < capacity(); int mask = array.length - 1; if (index <= (size >>> 1)) { if (index == 0) { addHead(item); } else { int i = (head - 1 + index) & mask; if (head <= i) { if (head == 0) { array[mask] = array[0]; System.arraycopy(array, 1, array, 0, index - 1); head = mask; } else { System.arraycopy(array, head, array, head - 1, index); head -= 1; } } else { int amount = array.length - head; System.arraycopy(array, head, array, head - 1, amount); array[mask] = array[0]; System.arraycopy(array, 1, array, 0, i); head -= 1; } array[i] = item; size += 1; } } else { if (index == size) { addTail(item); } else { int i = (head + index) & mask; int tail = (head + size) & mask; if (i <= tail) { System.arraycopy(array, i, array, i + 1, tail - i); tail = (tail + 1) & mask; } else { System.arraycopy(array, 0, array, 1, tail); array[0] = array[mask]; System.arraycopy(array, i, array, i + 1, mask - i); tail += 1; } array[i] = item; size += 1; } } } /** * {@inheritDoc} */ @Override public void addAll(ListInternal source) { addSome(source, 0, source.size()); } /** * {@inheritDoc} */ @Override public void addHead(T item) { assert size < capacity(); head = (head - 1) & (array.length - 1); array[head] = item; size += 1; } /** * {@inheritDoc} */ @Override public void addSome(ListInternal source, int index, int count) { assert size + count <= capacity(); int mask = array.length - 1; int tail = (head + size) & mask; while (count > 0) { int batch = Math.min(count, array.length - tail); source.fill((T[]) array, tail, index, batch); tail = (tail + batch) & mask; index += batch; count -= batch; size += batch; } } /** * {@inheritDoc} */ @Override public void addTail(T item) { assert size < capacity(); array[(head + size) & (array.length - 1)] = item; size += 1; } /** * {@inheritDoc} */ @Override public int calculateFreeCapacity() { return array.length - size; } /** * {@inheritDoc} */ @Override public int capacity() { return array.length; } /** * {@inheritDoc} */ @Override public void clear() { Arrays.fill(array, null); head = size = 0; } /** * Clone this list. */ @Override public Object clone() { try { CircularListInternal result = (CircularListInternal) super .clone(); result.array = Arrays.copyOf(array, array.length); return result; } catch (CloneNotSupportedException e) { throw new InternalError(); } } /** * {@inheritDoc} */ @Override public void fill(T[] target, int targetIndex, int index, int count) { assert (target.length - targetIndex) >= count; assert count <= size; if (count == 0) { return; } index = (head + index) & (array.length - 1); int tail = (index + count) & (array.length - 1); if (index < tail) { System.arraycopy(array, index, target, targetIndex, count); } else { int c = array.length - index; System.arraycopy(array, index, target, targetIndex, c); count -= c; targetIndex += c; System.arraycopy(array, 0, target, targetIndex, count); } } /** * {@inheritDoc} */ @Override public T get(int index) { assert 0 <= index && index < size; return (T) array[(head + index) & (array.length - 1)]; } /** * {@inheritDoc} */ @Override public T getHead() { assert size > 0; return (T) array[head]; } /** * {@inheritDoc} */ @Override public T getTail() { assert size > 0; return (T) array[(head + size - 1) & (array.length - 1)]; } /** * {@inheritDoc} */ @Override public int indexOf(Object o) { if (o == null) { for (int j = 0; j < size; j++) { if (get(j) == null) { return j; } } } else { for (int j = 0; j < size; j++) { if (o.equals(get(j))) { return j; } } } return -1; } /** * {@inheritDoc} */ @Override public boolean isEmpty() { return size == 0; } /** * {@inheritDoc} */ @Override public boolean isFull() { return size == array.length; } /** * {@inheritDoc} */ @Override public int lastIndexOf(Object o) { if (o == null) { for (int j = size - 1; j >= 0; j--) { if (get(j) == null) { return j; } } } else { for (int j = size - 1; j >= 0; j--) { if (o.equals(get(j))) { return j; } } } return -1; } /** * {@inheritDoc} */ @Override public T remove(int index) { assert 0 <= index && index < size; int mask = array.length - 1; int i = (head + index) & mask; T result = (T) array[i]; if (index < (size >>> 1)) { if (head <= i) { System.arraycopy(array, head, array, head + 1, index); } else { System.arraycopy(array, 0, array, 1, i); array[0] = array[mask]; System.arraycopy(array, head, array, head + 1, mask - head); } array[head] = null; head = (head + 1) & mask; } else { int tail = (head + size) & mask; if (i < tail) { System.arraycopy(array, i + 1, array, i, tail - i - 1); tail -= 1; } else { System.arraycopy(array, i + 1, array, i, mask - i); array[mask] = array[0]; if (tail > 0) { System.arraycopy(array, 1, array, 0, tail - 1); } tail = (tail - 1) & mask; } array[tail] = null; } size -= 1; return result; } /** * {@inheritDoc} */ @Override public T removeHead() { assert size > 0; T result = (T) array[head]; array[head] = null; head = (head + 1) & (array.length - 1); size -= 1; return result; } /** * {@inheritDoc} */ @Override public T removeTail() { assert size > 0; int tail = (head + size - 1) & (array.length - 1); T result = (T) array[tail]; array[tail] = null; size -= 1; return result; } /** * {@inheritDoc} */ @Override public T set(int index, T item) { assert 0 <= index && index < size; int i = (head + index) & (array.length - 1); T result = (T) array[i]; array[i] = item; return result; } /** * {@inheritDoc} */ @Override public int size() { return size; } /** * {@inheritDoc} */ @Override public String toString() { StringBuilder builder = new StringBuilder(); for (int j = 0; j < size; j++) { builder.append(get(j)).append(","); } return builder.length() == 0 ? "" : builder.substring(0, builder.length() - 1); } } /** * A fixed capacity list. This is strictly an internal helper class to * {@link ThriftyList} as it performs no bounds checking, delegating all * such responsibilities to {@link ThriftyList} itself. */ protected static class FixedListInternal implements ListInternal, Cloneable { protected Object[] array; protected int size; /** * Construct an empty instance with the specified capacity. * * @param capacity * the capacity of the list, must be a power of two. */ public FixedListInternal(int capacity) { assert (capacity & (capacity - 1)) == 0; array = new Object[capacity]; size = 0; } /** * {@inheritDoc} */ @Override public void add(int index, T item) { assert 0 <= index && index <= size; assert size < capacity(); System.arraycopy(array, index, array, index + 1, size - index); array[index] = item; size += 1; } /** * {@inheritDoc} */ @Override public void addAll(ListInternal source) { addSome(source, 0, source.size()); } /** * {@inheritDoc} */ @Override public void addHead(T item) { assert size < capacity(); add(0, item); } /** * {@inheritDoc} */ @Override public void addSome(ListInternal source, int index, int count) { assert size + count <= capacity(); if (count < 0) { return; } source.fill((T[]) array, size, index, count); size += count; } /** * {@inheritDoc} */ @Override public void addTail(T item) { assert size < capacity(); add(size, item); } /** * {@inheritDoc} */ @Override public int calculateFreeCapacity() { return array.length - size; } /** * {@inheritDoc} */ @Override public int capacity() { return array.length; } /** * {@inheritDoc} */ @Override public void clear() { Arrays.fill(array, null); size = 0; } /** * Clone this list. */ @Override public Object clone() { try { FixedListInternal result = (FixedListInternal) super .clone(); result.array = Arrays.copyOf(array, array.length); return result; } catch (CloneNotSupportedException e) { throw new InternalError(); } } /** * {@inheritDoc} */ @Override public void fill(T[] target, int targetIndex, int index, int count) { assert (target.length - targetIndex) >= count; assert count <= size; if (count == 0) { return; } System.arraycopy(array, index, target, targetIndex, count); } /** * {@inheritDoc} */ @Override public T get(int index) { assert 0 <= index && index < size; return (T) array[index]; } /** * {@inheritDoc} */ @Override public T getHead() { assert size > 0; return (T) array[0]; } /** * {@inheritDoc} */ @Override public T getTail() { assert size > 0; return (T) array[size - 1]; } /** * {@inheritDoc} */ @Override public int indexOf(Object o) { if (o == null) { for (int j = 0; j < size; j++) { if (get(j) == null) { return j; } } } else { for (int j = 0; j < size; j++) { if (o.equals(get(j))) { return j; } } } return -1; } /** * {@inheritDoc} */ @Override public boolean isEmpty() { return size == 0; } /** * {@inheritDoc} */ @Override public boolean isFull() { return size == array.length; } /** * {@inheritDoc} */ @Override public int lastIndexOf(Object o) { if (o == null) { for (int j = size - 1; j >= 0; j--) { if (get(j) == null) { return j; } } } else { for (int j = size - 1; j >= 0; j--) { if (o.equals(get(j))) { return j; } } } return -1; } /** * {@inheritDoc} */ @Override public T remove(int index) { assert 0 <= index && index < size; T result = (T) array[index]; System.arraycopy(array, index + 1, array, index, size - index - 1); size -= 1; array[size] = null; return result; } /** * {@inheritDoc} */ @Override public T removeHead() { assert size > 0; return remove(0); } /** * {@inheritDoc} */ @Override public T removeTail() { assert size > 0; return remove(size - 1); } /** * {@inheritDoc} */ @Override public T set(int index, T item) { assert 0 <= index && index < size; T result = (T) array[index]; array[index] = item; return result; } /** * {@inheritDoc} */ @Override public int size() { return size; } /** * {@inheritDoc} */ @Override public String toString() { StringBuilder builder = new StringBuilder(); for (int j = 0; j < size; j++) { builder.append(get(j)).append(","); } return builder.length() == 0 ? "" : builder.substring(0, builder.length() - 1); } } protected class Iter extends IterBase { public Iter(int index) { super(index); } /** * {@inheritDoc} */ @Override public boolean hasNext() { return index < size; } /** * {@inheritDoc} */ @Override public boolean hasPrevious() { return index > 0; } /** * {@inheritDoc} */ @Override public int nextIndex() { return index; } /** * {@inheritDoc} */ @Override public int previousIndex() { return index - 1; } /** * {@inheritDoc} */ @Override protected void stepForward() { currentIndex = index; currentSublistIndex = sublistIndex; currentSublistOffset = sublistOffset; index += 1; sublistOffset += 1; if (sublistOffset == sublists.get(sublistIndex).size() && sublistIndex < sublists.size() - 1) { sublistIndex += 1; sublistOffset = 0; } } /** * {@inheritDoc} */ @Override protected void stepReverse() { index -= 1; if (sublistOffset == 0 && sublistIndex > 0) { sublistIndex -= 1; sublistOffset = sublists.get(sublistIndex).size() - 1; } else { sublistOffset -= 1; } currentIndex = index; currentSublistIndex = sublistIndex; currentSublistOffset = sublistOffset; } } protected abstract class IterBase implements ListIterator { protected int index, sublistIndex, sublistOffset; protected int currentIndex, currentSublistIndex, currentSublistOffset; public IterBase(int index) { this.index = index; this.currentIndex = -1; cursor(); } /** * {@inheritDoc} */ @Override public void add(T item) { addImpl(index, sublistIndex, sublistOffset, item); cursor(); stepForward(); currentIndex = -1; } /** * Calculates the sublist index/offset for the index. */ protected void cursor() { 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); } } /** * {@inheritDoc} */ @Override public T next() { if (!hasNext()) { throw new NoSuchElementException(); } T result = sublists.get(sublistIndex).get(sublistOffset); stepForward(); return result; } /** * {@inheritDoc} */ @Override public T previous() { if (!hasPrevious()) { throw new NoSuchElementException(); } stepReverse(); return sublists.get(sublistIndex).get(sublistOffset); } /** * {@inheritDoc} */ @Override public void remove() { if (currentIndex == -1) { throw new IllegalStateException(); } if (currentIndex < index) { stepReverse(); } removeImpl(currentSublistIndex, currentSublistOffset); cursor(); currentIndex = -1; } /** * {@inheritDoc} */ @Override public void set(T e) { if (currentIndex == -1) { throw new IllegalStateException(); } setImpl(currentSublistIndex, currentSublistOffset, e); } protected abstract void stepForward(); protected abstract void stepReverse(); } /** *

* Interface for internal helper lists to {@link ThriftyList} as instances * are not required to perform bounds checking (all such responsibilities * are delegated to {@link ThriftyList} itself). * *

* This interface exists primarily to support the generic static helper * functions * {@link ThriftyList#merge(ListInternal, ListInternal, ListInternal)}, * {@link ThriftyList#split(ListInternal, ListInternal, ListInternal, boolean)} * and {@link ThriftyList#copyTo(ListInternal, ListInternal)}. */ protected static interface ListInternal { /** * Add the specified item to this list at the specified index, shifting * elements of this list if necessary. * * @param index * the index at which to add the item. * @param item * the item to add. */ public void add(int index, T item); /** * Add all elements of the specified source list to this list. * * @param source * the source list. */ public void addAll(ListInternal source); /** * Add the specified item to the beginning of this list. * * @param item * the item to add to this list. */ public void addHead(T item); /** * Add a number of elements from the specified source list, beginning * with the element at the specified index and ending once the specified * count of added elements has been reached, to this list. * * @param source * the source list. * @param index * the index at which to begin adding elements to this list. * @param count * the number of elements to add to this list. */ public void addSome(ListInternal source, int index, int count); /** * Add the specified item to the end of this list. * * @param item * the item to add to this list. */ public void addTail(T item); /** * * @return the amount of free capacity. */ public int calculateFreeCapacity(); /** * * @return the capacity of the list. */ public int capacity(); /** * Clear all elements from this list. */ public void clear(); /** * Fill the specified array, beginning with the specified index, with * the data of this list. * * @param target * the target array. * @param targetIndex * the array index at which to begin filling the target array * with this list's data. * @param index * the index of this list at which to begin filling the * target array. * @param count * the number of elements to copy to the target array. */ public void fill(T[] target, int targetIndex, int index, int count); /** * Get the element of this list at the specified index. * * @param index * the index of the desired element. * @return the element at the specified index. */ public T get(int index); /** * * @return the first element of this list. */ public T getHead(); /** * * @return the last element of this list. */ public T getTail(); /** * Get the index of the first list element equal to the specified * Object. * * @param o * the Object for which to search. * @return the index of the first element equal to the specified Object, * -1 if none. */ public int indexOf(Object o); /** * * @return true if this list is empty, false otherwise. */ public boolean isEmpty(); /** * * @return true if this list has no free capacity, false otherwise. */ public boolean isFull(); /** * Get the index of the last element equal to the specified Object. * * @param o * the Object for which to search. * @return the index of the last element equal to the specified Object, * -1 if none. */ public int lastIndexOf(Object o); /** * Remove and return the element of this list at the specified index. * * @param index * the index of the element to remove. * @return the item removed from this list. */ public T remove(int index); /** * Remove the head element of this list. * * @return the item removed from this list. */ public T removeHead(); /** * Remove the tail element of this list. * * @return the item removed from this list. */ public T removeTail(); /** * Set the element of this list at the specified index to the specified * item. * * @param index * the index at which to set the item. * @param item * the item to set to this list. * @return the element currently at the specified index. */ public T set(int index, T item); /** * * @return the number of elements in the list. */ public int size(); } protected class ReverseIter extends IterBase { public ReverseIter(int index) { super(index); } /** * {@inheritDoc} */ @Override public boolean hasNext() { return index >= 0; } /** * {@inheritDoc} */ @Override public boolean hasPrevious() { return index < size - 1; } /** * {@inheritDoc} */ @Override public int nextIndex() { return index; } /** * {@inheritDoc} */ @Override public int previousIndex() { return index + 1; } /** * {@inheritDoc} */ @Override protected void stepForward() { currentIndex = index; currentSublistIndex = sublistIndex; currentSublistOffset = sublistOffset; index -= 1; if (sublistOffset == 0 && sublistIndex > 0) { sublistIndex -= 1; sublistOffset = sublists.get(sublistIndex).size() - 1; } else { sublistOffset -= 1; } } /** * {@inheritDoc} */ @Override protected void stepReverse() { index += 1; sublistOffset += 1; if (sublistOffset == sublists.get(sublistIndex).size() && sublistIndex < sublists.size() - 1) { sublistIndex += 1; sublistOffset = 0; } currentIndex = index; currentSublistIndex = sublistIndex; currentSublistOffset = sublistOffset; } } }euclid-euclid-2.9/src/main/java/blogspot/software_and_algorithms/stern_library/geometry/000077500000000000000000000000001461721410700320035ustar00rootroot00000000000000ClosestPointPairAlgorithm.java000066400000000000000000000202571461721410700377060ustar00rootroot00000000000000euclid-euclid-2.9/src/main/java/blogspot/software_and_algorithms/stern_library/geometrypackage blogspot.software_and_algorithms.stern_library.geometry; import java.awt.geom.Point2D; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; 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. */ /** * An implementation of the divide-and-conquer algorithm for computing the * closest pair among elements of a set of points. The algorithm consists of * constructing a list of points, then recursively dividing the list into a left * and right sublist and inspecting each sublist individually for closest point * pairs. The two sub-results are merged by searching for closer point pairs * that cross the boundary of separation. Happily, only a linear amount of work * is required to merge the two results into a final closest pair of points, * giving a total runtime of O(n*log(n)) for the algorithm. * * @author Kevin L. Stern */ public class ClosestPointPairAlgorithm { private List pointsOrderedByXCoordinate, pointsOrderedByYCoordinate; /** * Construct an instance of the algorithm for the specified point * Collection. * * @param points * the Collection of points through which to search for the * closest pair. */ public ClosestPointPairAlgorithm(Collection points) { if (points == null) { throw new NullPointerException("points is null"); } if (points.size() < 2) { throw new IllegalArgumentException("points is too small"); } pointsOrderedByXCoordinate = new ArrayList(points); Collections.sort(pointsOrderedByXCoordinate, new Comparator() { @Override public int compare(Point2D o1, Point2D o2) { double delta = o1.getX() - o2.getX(); if (delta == 0.0) { delta = o1.getY() - o2.getY(); } return delta < 0 ? -1 : delta > 0 ? 1 : 0; } }); pointsOrderedByYCoordinate = new ArrayList(points); Collections.sort(pointsOrderedByYCoordinate, new Comparator() { @Override public int compare(Point2D o1, Point2D o2) { double delta = o1.getY() - o2.getY(); if (delta == 0.0) { delta = o1.getX() - o2.getX(); } return delta < 0 ? -1 : delta > 0 ? 1 : 0; } }); } /** * Internal helper method which implements the closest point pair algorithm. * * @param low * the index, inclusive, delimiting the low boundary of the * portion of the list in which to search for the closest point * pair. * @param high * the index, exclusive, delimiting the high boundary of the * portion of the list in which to search for the closest point * pair. * @param localPointsSortedByYCoordinate * the points from the portion of the list delimited by the low * and high parameters, sorted by y coordinate. * @return a PairStructure containing the closest point pair among elements * of the specified portion of the list. */ protected PairStructure closestPair(int low, int high, List localPointsSortedByYCoordinate) { int size = high - low; if (size == 3) { Point2D p1 = pointsOrderedByXCoordinate.get(low); Point2D p2 = pointsOrderedByXCoordinate.get(low + 1); Point2D p3 = pointsOrderedByXCoordinate.get(low + 2); double d1 = p1.distanceSq(p2); double d2 = p2.distanceSq(p3); double d3 = p1.distanceSq(p3); if (d1 < d2) { if (d1 < d3) { return new PairStructure(p1, p2, d1); } else { return new PairStructure(p1, p3, d3); } } else { if (d2 < d3) { return new PairStructure(p2, p3, d2); } else { return new PairStructure(p1, p3, d3); } } } else if (size == 2) { Point2D p1 = pointsOrderedByXCoordinate.get(low); Point2D p2 = pointsOrderedByXCoordinate.get(low + 1); return new PairStructure(p1, p2, p1.distanceSq(p2)); } assert size > 3; int mid = (low + high) >>> 1; Set leftSubtreeMemberSet = new HashSet(mid - low); for (int j = low; j < mid; j++) { leftSubtreeMemberSet.add(pointsOrderedByXCoordinate.get(j)); } /* * Construct the lists of points ordered by y coordinate for the left * and right subtrees in linear time by drawing upon the master list of * points ordered by y coordinate. */ List leftPointsOrderedByYCoordinate = new ArrayList( mid - low); List rightPointsOrderedByYCoordinate = new ArrayList( high - mid); for (Point2D next : localPointsSortedByYCoordinate) { if (leftSubtreeMemberSet.contains(next)) { leftPointsOrderedByYCoordinate.add(next); } else { rightPointsOrderedByYCoordinate.add(next); } } PairStructure leftSubtreeResult = closestPair(low, mid, leftPointsOrderedByYCoordinate); PairStructure rightSubtreeResult = closestPair(mid, high, rightPointsOrderedByYCoordinate); PairStructure result = leftSubtreeResult.distanceSq < rightSubtreeResult.distanceSq ? leftSubtreeResult : rightSubtreeResult; List boundaryPointsOrderedByYCoordinate = new ArrayList(); double midXCoordinate = pointsOrderedByXCoordinate.get(mid).getX(); for (Point2D next : localPointsSortedByYCoordinate) { double v = next.getX() - midXCoordinate; if (v * v < result.distanceSq) { boundaryPointsOrderedByYCoordinate.add(next); } } for (int i = 0; i < boundaryPointsOrderedByYCoordinate.size(); i++) { Point2D next = boundaryPointsOrderedByYCoordinate.get(i); int index; for (int j = 1; (index = i + j) < boundaryPointsOrderedByYCoordinate .size(); j += 1) { Point2D candidatePartner = boundaryPointsOrderedByYCoordinate .get(index); /* * Only a constant number of points will be so that their y * coordinate is within the minimum of the result distances for * the left/right subtrees. */ double v = candidatePartner.getY() - next.getY(); if (v * v >= result.distanceSq) { break; } double candidateDistance = next.distanceSq(candidatePartner); if (candidateDistance < result.distanceSq) { result = new PairStructure(next, candidatePartner, candidateDistance); } } } return result; } /** * Execute the algorithm. * * @return a Point2D[] containing exactly two elements which are the closest * pair of points among those in the collection used to construct * this instance. */ public Point2D[] execute() { PairStructure result = closestPair(0, pointsOrderedByXCoordinate.size(), pointsOrderedByYCoordinate); return new Point2D[] { result.p1, result.p2 }; } /** * Convenience data structure to hold a pair of points along with distance * information. */ protected static class PairStructure { private Point2D p1, p2; private double distanceSq; /** * Constructor. * * @param point1 * the first point. * @param point2 * the second point. * @param distanceSq * the distance between the two points, squared. */ public PairStructure(Point2D point1, Point2D point2, double distanceSq) { this.p1 = point1; this.p2 = point2; this.distanceSq = distanceSq; } } }euclid-euclid-2.9/src/main/java/blogspot/software_and_algorithms/stern_library/optimization/000077500000000000000000000000001461721410700326765ustar00rootroot00000000000000HungarianAlgorithm.java000066400000000000000000000257511461721410700372570ustar00rootroot00000000000000euclid-euclid-2.9/src/main/java/blogspot/software_and_algorithms/stern_library/optimizationpackage blogspot.software_and_algorithms.stern_library.optimization; import java.util.Arrays; /* 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 implementation of the Hungarian algorithm for solving the assignment * problem. An instance of the assignment problem consists of a number of * workers along with a number of jobs and a cost matrix which gives the cost of * assigning the i'th worker to the j'th job at position (i, j). The goal is to * find an assignment of workers to jobs so that no job is assigned more than * one worker and so that no worker is assigned to more than one job in such a * manner so as to minimize the total cost of completing the jobs. *

* * An assignment for a cost matrix that has more workers than jobs will * necessarily include unassigned workers, indicated by an assignment value of * -1; in no other circumstance will there be unassigned workers. Similarly, an * assignment for a cost matrix that has more jobs than workers will necessarily * include unassigned jobs; in no other circumstance will there be unassigned * jobs. For completeness, an assignment for a square cost matrix will give * exactly one unique worker to each job. *

* * This version of the Hungarian algorithm runs in time O(n^3), where n is the * maximum among the number of workers and the number of jobs. * * @author Kevin L. Stern */ public class HungarianAlgorithm { private final double[][] costMatrix; private final int rows, cols, dim; private final double[] labelByWorker, labelByJob; private final int[] minSlackWorkerByJob; private final double[] minSlackValueByJob; private final int[] matchJobByWorker, matchWorkerByJob; private final int[] parentWorkerByCommittedJob; private final boolean[] committedWorkers; /** * Construct an instance of the algorithm. * * @param costMatrix * the cost matrix, where matrix[i][j] holds the cost of * assigning worker i to job j, for all i, j. The cost matrix * must not be irregular in the sense that all rows must be the * same length. */ public HungarianAlgorithm(double[][] costMatrix) { this.dim = Math.max(costMatrix.length, costMatrix[0].length); this.rows = costMatrix.length; this.cols = costMatrix[0].length; this.costMatrix = new double[this.dim][this.dim]; for (int w = 0; w < this.dim; w++) { if (w < costMatrix.length) { if (costMatrix[w].length != this.cols) { throw new IllegalArgumentException("Irregular cost matrix"); } this.costMatrix[w] = Arrays.copyOf(costMatrix[w], this.dim); } else { this.costMatrix[w] = new double[this.dim]; } } labelByWorker = new double[this.dim]; labelByJob = new double[this.dim]; minSlackWorkerByJob = new int[this.dim]; minSlackValueByJob = new double[this.dim]; committedWorkers = new boolean[this.dim]; parentWorkerByCommittedJob = new int[this.dim]; matchJobByWorker = new int[this.dim]; Arrays.fill(matchJobByWorker, -1); matchWorkerByJob = new int[this.dim]; Arrays.fill(matchWorkerByJob, -1); } /** * Compute an initial feasible solution by assigning zero labels to the * workers and by assigning to each job a label equal to the minimum cost * among its incident edges. */ protected void computeInitialFeasibleSolution() { for (int j = 0; j < dim; j++) { labelByJob[j] = Double.POSITIVE_INFINITY; } for (int w = 0; w < dim; w++) { for (int j = 0; j < dim; j++) { if (costMatrix[w][j] < labelByJob[j]) { labelByJob[j] = costMatrix[w][j]; } } } } /** * Execute the algorithm. * * @return the minimum cost matching of workers to jobs based upon the * provided cost matrix. A matching value of -1 indicates that the * corresponding worker is unassigned. */ public int[] execute() { /* * Heuristics to improve performance: Reduce rows and columns by their * smallest element, compute an initial non-zero dual feasible solution * and create a greedy matching from workers to jobs of the cost matrix. */ reduce(); computeInitialFeasibleSolution(); greedyMatch(); int w = fetchUnmatchedWorker(); while (w < dim) { initializePhase(w); executePhase(); w = fetchUnmatchedWorker(); } int[] result = Arrays.copyOf(matchJobByWorker, rows); for (w = 0; w < result.length; w++) { if (result[w] >= cols) { result[w] = -1; } } return result; } /** * Execute a single phase of the algorithm. A phase of the Hungarian * algorithm consists of building a set of committed workers and a set of * committed jobs from a root unmatched worker by following alternating * unmatched/matched zero-slack edges. If an unmatched job is encountered, * then an augmenting path has been found and the matching is grown. If the * connected zero-slack edges have been exhausted, the labels of committed * workers are increased by the minimum slack among committed workers and * non-committed jobs to create more zero-slack edges (the labels of * committed jobs are simultaneously decreased by the same amount in order * to maintain a feasible labeling). *

* * The runtime of a single phase of the algorithm is O(n^2), where n is the * dimension of the internal square cost matrix, since each edge is visited * at most once and since increasing the labeling is accomplished in time * O(n) by maintaining the minimum slack values among non-committed jobs. * When a phase completes, the matching will have increased in size. */ protected void executePhase() { while (true) { int minSlackWorker = -1, minSlackJob = -1; double minSlackValue = Double.POSITIVE_INFINITY; for (int j = 0; j < dim; j++) { if (parentWorkerByCommittedJob[j] == -1) { if (minSlackValueByJob[j] < minSlackValue) { minSlackValue = minSlackValueByJob[j]; minSlackWorker = minSlackWorkerByJob[j]; minSlackJob = j; } } } if (minSlackValue > 0) { updateLabeling(minSlackValue); } parentWorkerByCommittedJob[minSlackJob] = minSlackWorker; if (matchWorkerByJob[minSlackJob] == -1) { /* * An augmenting path has been found. */ int committedJob = minSlackJob; int parentWorker = parentWorkerByCommittedJob[committedJob]; while (true) { int temp = matchJobByWorker[parentWorker]; match(parentWorker, committedJob); committedJob = temp; if (committedJob == -1) { break; } parentWorker = parentWorkerByCommittedJob[committedJob]; } return; } else { /* * Update slack values since we increased the size of the * committed workers set. */ int worker = matchWorkerByJob[minSlackJob]; committedWorkers[worker] = true; for (int j = 0; j < dim; j++) { if (parentWorkerByCommittedJob[j] == -1) { double slack = costMatrix[worker][j] - labelByWorker[worker] - labelByJob[j]; if (minSlackValueByJob[j] > slack) { minSlackValueByJob[j] = slack; minSlackWorkerByJob[j] = worker; } } } } } } /** * * @return the first unmatched worker or {@link #dim} if none. */ protected int fetchUnmatchedWorker() { int w; for (w = 0; w < dim; w++) { if (matchJobByWorker[w] == -1) { break; } } return w; } /** * Find a valid matching by greedily selecting among zero-cost matchings. * This is a heuristic to jump-start the augmentation algorithm. */ protected void greedyMatch() { for (int w = 0; w < dim; w++) { for (int j = 0; j < dim; j++) { if (matchJobByWorker[w] == -1 && matchWorkerByJob[j] == -1 && costMatrix[w][j] - labelByWorker[w] - labelByJob[j] == 0) { match(w, j); } } } } /** * Initialize the next phase of the algorithm by clearing the committed * workers and jobs sets and by initializing the slack arrays to the values * corresponding to the specified root worker. * * @param w * the worker at which to root the next phase. */ protected void initializePhase(int w) { Arrays.fill(committedWorkers, false); Arrays.fill(parentWorkerByCommittedJob, -1); committedWorkers[w] = true; for (int j = 0; j < dim; j++) { minSlackValueByJob[j] = costMatrix[w][j] - labelByWorker[w] - labelByJob[j]; minSlackWorkerByJob[j] = w; } } /** * Helper method to record a matching between worker w and job j. * @param w the worker * @param j the job */ protected void match(int w, int j) { matchJobByWorker[w] = j; matchWorkerByJob[j] = w; } /** * Reduce the cost matrix by subtracting the smallest element of each row * from all elements of the row as well as the smallest element of each * column from all elements of the column. Note that an optimal assignment * for a reduced cost matrix is optimal for the original cost matrix. */ protected void reduce() { for (int w = 0; w < dim; w++) { double min = Double.POSITIVE_INFINITY; for (int j = 0; j < dim; j++) { if (costMatrix[w][j] < min) { min = costMatrix[w][j]; } } for (int j = 0; j < dim; j++) { costMatrix[w][j] -= min; } } double[] min = new double[dim]; for (int j = 0; j < dim; j++) { min[j] = Double.POSITIVE_INFINITY; } for (int w = 0; w < dim; w++) { for (int j = 0; j < dim; j++) { if (costMatrix[w][j] < min[j]) { min[j] = costMatrix[w][j]; } } } for (int w = 0; w < dim; w++) { for (int j = 0; j < dim; j++) { costMatrix[w][j] -= min[j]; } } } /** * Update labels with the specified slack by adding the slack value for * committed workers and by subtracting the slack value for committed jobs. * In addition, update the minimum slack values appropriately. * * @param slack the specified slack for which labels will be updated */ protected void updateLabeling(double slack) { for (int w = 0; w < dim; w++) { if (committedWorkers[w]) { labelByWorker[w] += slack; } } for (int j = 0; j < dim; j++) { if (parentWorkerByCommittedJob[j] != -1) { labelByJob[j] -= slack; } else { minSlackValueByJob[j] -= slack; } } } } euclid-euclid-2.9/src/main/java/blogspot/software_and_algorithms/stern_library/string/000077500000000000000000000000001461721410700314565ustar00rootroot00000000000000DamerauLevenshteinAlgorithm.java000066400000000000000000000142541461721410700377020ustar00rootroot00000000000000euclid-euclid-2.9/src/main/java/blogspot/software_and_algorithms/stern_library/stringpackage blogspot.software_and_algorithms.stern_library.string; import java.util.HashMap; import java.util.Map; /* 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. */ /** * The Damerau-Levenshtein Algorithm is an extension to the Levenshtein * Algorithm which solves the edit distance problem between a source string and * a target string with the following operations: * *

    *
  • Character Insertion
  • *
  • Character Deletion
  • *
  • Character Replacement
  • *
  • Adjacent Character Swap
  • *
* * Note that the adjacent character swap operation is an edit that may be * applied when two adjacent characters in the source string match two adjacent * characters in the target string, but in reverse order, rather than a general * allowance for adjacent character swaps. *

* * This implementation allows the client to specify the costs of the various * edit operations with the restriction that the cost of two swap operations * must not be less than the cost of a delete operation followed by an insert * operation. This restriction is required to preclude two swaps involving the * same character being required for optimality which, in turn, enables a fast * dynamic programming solution. *

* * The running time of the Damerau-Levenshtein algorithm is O(n*m) where n is * the length of the source string and m is the length of the target string. * This implementation consumes O(n*m) space. * * @author Kevin L. Stern */ public class DamerauLevenshteinAlgorithm { private final int deleteCost, insertCost, replaceCost, swapCost; /** * Constructor. * * @param deleteCost * the cost of deleting a character. * @param insertCost * the cost of inserting a character. * @param replaceCost * the cost of replacing a character. * @param swapCost * the cost of swapping two adjacent characters. */ public DamerauLevenshteinAlgorithm(int deleteCost, int insertCost, int replaceCost, int swapCost) { /* * Required to facilitate the premise to the algorithm that two swaps of * the same character are never required for optimality. */ if (2 * swapCost < insertCost + deleteCost) { throw new IllegalArgumentException("Unsupported cost assignment"); } this.deleteCost = deleteCost; this.insertCost = insertCost; this.replaceCost = replaceCost; this.swapCost = swapCost; } /** * Compute the Damerau-Levenshtein distance between the specified source * string and the specified target string. * @param source the source string * @param target the target string * @return Damerau-Levenshtein distance between the specified source */ public int execute(String source, String target) { if (source.length() == 0) { return target.length() * insertCost; } if (target.length() == 0) { return source.length() * deleteCost; } int[][] table = new int[source.length()][target.length()]; Map sourceIndexByCharacter = new HashMap(); if (source.charAt(0) != target.charAt(0)) { table[0][0] = Math.min(replaceCost, deleteCost + insertCost); } sourceIndexByCharacter.put(source.charAt(0), 0); for (int i = 1; i < source.length(); i++) { int deleteDistance = table[i - 1][0] + deleteCost; int insertDistance = (i + 1) * deleteCost + insertCost; int matchDistance = i * deleteCost + (source.charAt(i) == target.charAt(0) ? 0 : replaceCost); table[i][0] = Math.min(Math.min(deleteDistance, insertDistance), matchDistance); } for (int j = 1; j < target.length(); j++) { int deleteDistance = table[0][j - 1] + insertCost; int insertDistance = (j + 1) * insertCost + deleteCost; int matchDistance = j * insertCost + (source.charAt(0) == target.charAt(j) ? 0 : replaceCost); table[0][j] = Math.min(Math.min(deleteDistance, insertDistance), matchDistance); } for (int i = 1; i < source.length(); i++) { int maxSourceLetterMatchIndex = source.charAt(i) == target .charAt(0) ? 0 : -1; for (int j = 1; j < target.length(); j++) { Integer candidateSwapIndex = sourceIndexByCharacter.get(target .charAt(j)); int jSwap = maxSourceLetterMatchIndex; int deleteDistance = table[i - 1][j] + deleteCost; int insertDistance = table[i][j - 1] + insertCost; int matchDistance = table[i - 1][j - 1]; if (source.charAt(i) != target.charAt(j)) { matchDistance += replaceCost; } else { maxSourceLetterMatchIndex = j; } int swapDistance; if (candidateSwapIndex != null && jSwap != -1) { int iSwap = candidateSwapIndex; int preSwapCost; if (iSwap == 0 && jSwap == 0) { preSwapCost = 0; } else { preSwapCost = table[Math.max(0, iSwap - 1)][Math.max(0, jSwap - 1)]; } swapDistance = preSwapCost + (i - iSwap - 1) * deleteCost + (j - jSwap - 1) * insertCost + swapCost; } else { swapDistance = Integer.MAX_VALUE; } table[i][j] = Math.min( Math.min(Math.min(deleteDistance, insertDistance), matchDistance), swapDistance); } sourceIndexByCharacter.put(source.charAt(i), i); } return table[source.length() - 1][target.length() - 1]; } } KnuthMorrisPrattAlgorithm.java000066400000000000000000000071121461721410700374120ustar00rootroot00000000000000euclid-euclid-2.9/src/main/java/blogspot/software_and_algorithms/stern_library/stringpackage blogspot.software_and_algorithms.stern_library.string; /* 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 implementation of the Knuth Morris Pratt substring search algorithm. An * instance of the algorithm is constructed around a needle string of length m, * a process which consumes O(m) time as well as O(m) space. Once an instance is * constructed, it is capable of searching for the needle string in any number * of haystack strings. The search process consumes O(n) time in a haystack * string of length n. * * @author Kevin L. Stern */ public class KnuthMorrisPrattAlgorithm { private final String needle; private final int[] stateTransitionTable; /** * Constructor. * * @param needle * the search string for which the instance will be constructed. */ public KnuthMorrisPrattAlgorithm(String needle) { this.needle = needle; this.stateTransitionTable = new int[needle.length()]; stateTransitionTable[0] = -1; int state = 0; for (int i = 1; i < needle.length(); i++) { int transition = state; if (needle.charAt(transition) == needle.charAt(i)) { transition = stateTransitionTable[transition]; } stateTransitionTable[i] = transition; if (needle.charAt(i) == needle.charAt(state)) { state += 1; } else { state = 0; } } } /** * Execute the search algorithm. * * @param haystack * the string in which to search for the needle specified at * construction time. * @return the index of the first occurrence of the needle string within the * specified haystack string, -1 if none. */ public int execute(String haystack) { return execute(haystack, 0); } /** * Execute the search algorithm. * * @param haystack * the string in which to search for the needle specified at * construction time. * @param index * the index at which to begin the search within the haystack * string. * @return the index of the first occurrence of the needle string within the * specified portion of the haystack string, -1 if none. */ public int execute(String haystack, int index) { int state = 0; for (int i = index; i < haystack.length(); i++) { if (haystack.charAt(i) == needle.charAt(state)) { state += 1; if (state == needle.length()) { return i - needle.length() + 1; } } else { do { state = stateTransitionTable[state]; } while (state >= 0 && haystack.charAt(i) != needle.charAt(state)); state += 1; } } return -1; } } euclid-euclid-2.9/src/main/java/org/000077500000000000000000000000001461721410700173225ustar00rootroot00000000000000euclid-euclid-2.9/src/main/java/org/xmlcml/000077500000000000000000000000001461721410700206165ustar00rootroot00000000000000euclid-euclid-2.9/src/main/java/org/xmlcml/args/000077500000000000000000000000001461721410700215525ustar00rootroot00000000000000euclid-euclid-2.9/src/main/java/org/xmlcml/args/ArgIterator.java-old000066400000000000000000000131031461721410700254120ustar00rootroot00000000000000package org.xmlcml.args; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.ListIterator; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.xmlcml.euclid.IntArray; import org.xmlcml.euclid.IntRange; import org.xmlcml.euclid.RealArray; import org.xmlcml.euclid.RealRange; /** wraps ListIterator as this causes reflection problems. * * Tokens of form -[digit]... are treated as numbers * Tokens of form -[letter]... are treated as flags * * @author pm286 * */ @Deprecated public class ArgIterator { private static final Logger LOG = Logger.getLogger(ArgIterator.class); static { LOG.setLevel(Level.DEBUG); } private final static char MINUS = '-'; private ListIterator listIterator; private RealArray doubleArray; public ArgIterator(ListIterator listIterator) { this.listIterator = listIterator; } public ArgIterator(String[] args) { listIterator = args == null ? null : Arrays.asList(args).listIterator(); } public boolean hasNext() { return listIterator == null ? false : listIterator.hasNext(); } public String previous() { return listIterator == null ? null : listIterator.previous(); } public String next() { return listIterator == null ? null : listIterator.next(); } /** read tokens until next - sign. * * minus must be folloed by a letter, not a number * leave iterator ready to read next minus * * @param argIterator * @return */ public List createTokenListUpToNextNonDigitMinus(ArgumentOption argumentOption) { List list = this.createTokenListUpToNextNonDigitMinus(); checkListSemantics(argumentOption, list); return list; } private void checkListSemantics(ArgumentOption argumentOption, List list) { String msg = null; msg = argumentOption.checkArgumentCount(list); if (msg != null) throw new IllegalArgumentException(argumentOption.getVerbose()+"; "+msg); msg = argumentOption.checkArgumentValues(list); if (msg != null) throw new IllegalArgumentException(argumentOption.getVerbose()+"; "+msg); } /** read tokens until next - sign. * * leave iterator ready to read next minus * * @param argIterator * @return */ public List createTokenListUpToNextNonDigitMinus() { List list = new ArrayList(); while (this.hasNext()) { String next = this.next(); Character next1 = next.length() <= 1 ? null : next.charAt(1); // flag is -letter... or -- if (next.charAt(0) == MINUS && (Character.isLetter(next1) || next1 == MINUS)) { this.previous(); break; } list.add(next); } return list; } // STRING public List getStrings(ArgumentOption option) { return createTokenListUpToNextNonDigitMinus(option); } public String getString(ArgumentOption option) { List tokens = createTokenListUpToNextNonDigitMinus(option); if (tokens.size() != 1) { throw new RuntimeException("Expected only 1 argument; found: "+tokens.size()); } return tokens.get(0); } // BOOLEAN public Boolean getBoolean(ArgumentOption option) { Boolean bool = null; String s = getString(option); try { bool = new Boolean(s); } catch (Exception e) { throw new RuntimeException("Cannot create a Boolean from: "+s); } return bool; } // DOUBLE public Double getDouble(ArgumentOption option) { Double dubble = null; String s = getString(option); try { dubble = new Double(s); } catch (Exception e) { throw new RuntimeException("Cannot create a Double from: "+s); } return dubble; } // REAL RANGE public RealRange getRealRange(ArgumentOption option) { List tokens = this.createTokenListUpToNextNonDigitMinus(option); List intRangeList = RealRange.createRealRangeList(tokens); if (intRangeList.size() != 1) { throw new RuntimeException("requires exactly one RealRange token: "+tokens); } return intRangeList.get(0); } public List getRealRangeList(ArgumentOption option) { List tokens = this.createTokenListUpToNextNonDigitMinus(option); return RealRange.createRealRangeList(tokens); } // REAL ARRAY public RealArray getDoubleArray(ArgumentOption option) { List tokens = this.createTokenListUpToNextNonDigitMinus(option); RealArray realArray = null; try { realArray = new RealArray(tokens.toArray(new String[0])); } catch (Exception e) { throw new RuntimeException("bad real array"+tokens, e); } return realArray; } // INTEGER public Integer getInteger(ArgumentOption option) { Integer intg = null; String s = getString(option); try { intg = new Integer(s); } catch (Exception e) { throw new RuntimeException("Cannot create an Integer from: "+s, e); } return intg; } // INT RANGE public IntRange getIntRange(ArgumentOption option) { List tokens = this.createTokenListUpToNextNonDigitMinus(option); List intRangeList = IntRange.createIntRangeList(tokens); if (intRangeList.size() != 1) { throw new RuntimeException("requires exactly one IntRange token: "+tokens); } return intRangeList.get(0); } public List getIntRangeList(ArgumentOption option) { List tokens = this.createTokenListUpToNextNonDigitMinus(option); return IntRange.createIntRangeList(tokens); } // INT ARRAY public IntArray getIntArray(ArgumentOption option) { List tokens = this.createTokenListUpToNextNonDigitMinus(option); IntArray intArray = null; try { intArray = new IntArray(tokens.toArray(new String[0])); } catch (Exception e) { throw new RuntimeException("bad integer array"+tokens, e); } return intArray; } }euclid-euclid-2.9/src/main/java/org/xmlcml/args/ArgumentOption.java-old000066400000000000000000000616661461721410700261630ustar00rootroot00000000000000package org.xmlcml.args; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import nu.xom.Attribute; import nu.xom.Element; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.xmlcml.euclid.IntRange; import org.xmlcml.euclid.RealRange; import org.xmlcml.xml.XMLUtil; /** simple option for controlling arguments. * * @author pm286 * */ @Deprecated public class ArgumentOption { private static final String BRIEF = "brief"; private static final String LONG = "long"; public static final String NAME = "name"; private static final String HELP = "help"; public static final String VALUE = "value"; private static final String ARGS = "args"; private static final String CLASS_TYPE = "class"; private static final String DEFAULT = "default"; private static final String COUNT_RANGE = "countRange"; private static final String VALUE_RANGE = "valueRange"; private static final String FINAL_METHOD = "finalMethod"; private static final String INIT_METHOD = "initMethod"; private static final String OUTPUT_METHOD = "outputMethod"; private static final String PARSE_METHOD = "parseMethod"; private static final String RUN_METHOD = "runMethod"; // these may be obsolete private static final String FORBIDDEN = "forbidden"; private static final String REQUIRED = "required"; private static final String PATTERN = "pattern"; private static final Pattern INT_RANGE = Pattern.compile("\\{(\\*|\\-?\\d+),(\\-?\\d*|\\*)\\}"); private static final Pattern DOUBLE_RANGE = Pattern.compile("\\{(\\-?\\+?\\d+\\.?\\d*|\\*),(\\-?\\+?\\d+\\.?\\d*|\\*)\\}"); public static String CLASSNAME = "className"; private static final Logger LOG = Logger.getLogger(ArgumentOption.class); static { LOG.setLevel(Level.DEBUG); } private static Set MANDATORY_ATTRIBUTES; static { MANDATORY_ATTRIBUTES = new HashSet(); MANDATORY_ATTRIBUTES.add(NAME); MANDATORY_ATTRIBUTES.add(LONG); MANDATORY_ATTRIBUTES.add(ARGS); // MANDATORY_ATTRIBUTES.add(PARSE_METHOD); // not required for some args } private static Set MANDATORY_CHILDREN; static { MANDATORY_CHILDREN = new HashSet(); MANDATORY_CHILDREN.add(HELP); } private static Map OPTIONAL_ATTRIBUTES; static { OPTIONAL_ATTRIBUTES = new HashMap(); OPTIONAL_ATTRIBUTES.put(CLASS_TYPE, "java.lang.String"); // class defaults to String OPTIONAL_ATTRIBUTES.put(DEFAULT, ""); // default defaults to "" } private String name; private String brief; private String verbose; private String help; private Class classType; private Object defalt; private IntRange countRange; private String countRangeString; private IntRange intValueRange = null; private RealRange realValueRange = null; private String valueRangeString; private String patternString = null; private Pattern pattern = null; private String forbiddenString = null; private List forbiddenArguments = null; private String requiredString = null; private List requiredArguments = null; private List defaultStrings; private List defaultIntegers; private List defaultDoubles; private List stringValues; private List doubleValues; private List integerValues; private Double defaultDouble; private String defaultString; private Integer defaultInteger; private Boolean defaultBoolean; private String stringValue; private Integer integerValue; private Double doubleValue; private Boolean booleanValue; private String args; private List stringPairValues; private String parseMethodName; private String runMethodName; private String outputMethodName; private String finalMethodName; private String initMethodName; private Class argProcessorClass; private List valueNodes; private List helpNodes; private Element element; public ArgumentOption(Class argProcessorClass) { setDefaults(); this.argProcessorClass = argProcessorClass; } private void setDefaults() { brief = ""; intValueRange = new IntRange(-Integer.MAX_VALUE, Integer.MAX_VALUE); realValueRange = new RealRange(-Double.MAX_VALUE, Double.MAX_VALUE); } /** factory method option for ArgumentOptions * * @param argProcessor * @param element * @return */ public static ArgumentOption createOption(Class argProcessor, Element element) { ArgumentOption argumentOption = new ArgumentOption(argProcessor); argumentOption.setElement(element); Set mandatorySet = new HashSet(MANDATORY_ATTRIBUTES); Map optionalAttributes = new HashMap(OPTIONAL_ATTRIBUTES); // get class first because so much else depends String classType = element.getAttributeValue(CLASS_TYPE); argumentOption.setClassType(classType); lookForKnownAttributes(element, argumentOption, mandatorySet, optionalAttributes); if (mandatorySet.size() > 0) { throw new RuntimeException("The following attributes for "+argumentOption.name+" are mandatory: "+mandatorySet); } argumentOption.getDefaults(optionalAttributes); argumentOption.getOrCreateHelpNodes(); argumentOption.getOrCreateValues(); return argumentOption; } private void setElement(Element element) { this.element = element; } private void getDefaults(Map optionalAttributes) { for (String name : optionalAttributes.keySet()) { String value = optionalAttributes.get(name); if (value != null) { this.setValue(name, value); } } } public List getOrCreateHelpNodes() { if (helpNodes == null) { helpNodes = XMLUtil.getQueryElements(element, "./*[local-name()='"+HELP+"']"); if (helpNodes.size() != 1) { throw new RuntimeException("No help given for "+this.name); } else { this.setHelp(helpNodes.get(0).getValue()); } } return helpNodes; } public List getOrCreateValues() { if (valueNodes == null) { valueNodes = XMLUtil.getQueryElements(element, "./*[local-name()='"+VALUE+"']"); } LOG.trace("VALUES: "+valueNodes); return valueNodes; } private static void lookForKnownAttributes(Element element, ArgumentOption argumentOption, Set mandatorySet, Map optionalAttributes) { for (int i = 0; i < element.getAttributeCount(); i++) { Attribute attribute = element.getAttribute(i); String name = attribute.getLocalName(); String value = attribute.getValue(); argumentOption.setValue(name, value); mandatorySet.remove(name); optionalAttributes.put(name, null); } } private void setValue(String namex, String value) { if (BRIEF.equals(namex)) { this.setBrief(value); } else if (LONG.equals(namex)) { this.setLong(value); } else if (NAME.equals(namex)) { this.setName(value); } else if (HELP.equals(namex)) { this.setHelp(value); } else if (ARGS.equals(namex)) { this.setArgs(value); } else if (CLASS_TYPE.equals(namex)) { this.setClassType(value); } else if (DEFAULT.equals(namex)) { this.setDefault(value); } else if (COUNT_RANGE.equals(namex)) { this.setCountRange(value); } else if (FORBIDDEN.equals(namex)) { this.setForbiddenString(value); } else if (REQUIRED.equals(namex)) { this.setRequiredString(value); } else if (FINAL_METHOD.equals(namex)) { this.setFinalMethod(value); } else if (INIT_METHOD.equals(namex)) { this.setInitMethod(value); } else if (OUTPUT_METHOD.equals(namex)) { this.setOutputMethod(value); } else if (PARSE_METHOD.equals(namex)) { this.setParseMethod(value); } else if (PATTERN.equals(namex)) { this.setPatternString(value); } else if (RUN_METHOD.equals(namex)) { this.setRunMethod(value); } else if (VALUE_RANGE.equals(namex)) { this.setValueRange(value); } else { throw new RuntimeException("Unknown attribute on : "+namex+"='"+value+"'"); } } private void setCountRange(String value) { countRangeString = value; setCountRange(createIntRange(countRangeString)); } private void setCountRange(IntRange intRange) { this.countRange = intRange; } private void setValueRange(String value) { valueRangeString = value; if (Integer.class.equals(classType)) { setValueRange(createIntRange(valueRangeString)); } else if (Double.class.equals(classType)) { setValueRange(createDoubleRange(valueRangeString)); } else { throw new RuntimeException("Must give class if using valueRanges: "+classType); } } private void setValueRange(RealRange realRange) { this.realValueRange = realRange; } private void setValueRange(IntRange intRange) { this.intValueRange = intRange; } private RealRange createDoubleRange(String valueRangeString) { RealRange realRange = null; Matcher matcher = DOUBLE_RANGE.matcher(valueRangeString); if (matcher.matches()) { String min = matcher.group(1); double minReal = (min.equals("*")) ? -1 * Double.MAX_VALUE : new Double(min); String max = matcher.group(2); double maxReal = (max.equals("*")) ? Double.MAX_VALUE : new Double(max); if (minReal > maxReal) { throw new RuntimeException("Minimum ("+minReal+")must be less-than/equals: "+maxReal+" in :"+valueRangeString); } realRange = new RealRange(minReal, maxReal); } else { throw new RuntimeException("count range must be of form {min,max}; was "+valueRangeString); } return realRange; } private IntRange createIntRange(String ss) { IntRange intRange = null; Matcher matcher = INT_RANGE.matcher(ss); if (matcher.matches()) { String min = matcher.group(1); int minInt = (min.equals("*")) ? -1*Integer.MAX_VALUE : new Integer(min); String max = matcher.group(2); int maxInt = (max.equals("*")) ? Integer.MAX_VALUE : new Integer(max); if (minInt > maxInt) { throw new RuntimeException("Minimum ("+min+") must be less-than/equals max ("+max+") in "+ss); } intRange = new IntRange(minInt, maxInt); } else { throw new RuntimeException("count range must be of form {min,max}; was "+ss); } return intRange; } private void setClassType(String className) { if (className != null) { try { classType = Class.forName(className); } catch (ClassNotFoundException e) { throw new RuntimeException("Cannot create class for: "+className); } } else { classType = java.lang.String.class; } } public String getBrief() { return brief; } public void setBrief(String brief) { this.brief = brief; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getVerbose() { return verbose; } public void setLong(String verbose) { this.verbose = verbose; } public String getArgs() { return args; } public void setArgs(String args) { this.args = args; } public String getHelp() { StringBuilder sb = new StringBuilder(); sb.append("\n"+brief); sb.append(" or "+verbose+" "); if (args.trim().length() > 0) { sb.append(" "+args); } sb.append("\n"); sb.append(help); return sb.toString(); } public void setHelp(String help) { this.help = help; } public Class getClassType() { return classType; } public void setClassType(Class classType) { this.classType = classType; } public Object getDefault() { return defalt; } public void setDefault(Object defalt) { this.defalt = defalt; } public String getParseMethodName() { return parseMethodName; } public void setParseMethod(String parseMethodName) { if (parseMethodName != null) { try { Method method = argProcessorClass.getMethod(parseMethodName, ArgumentOption.class, ArgIterator.class); this.parseMethodName = parseMethodName; } catch (NoSuchMethodException e) { throw new RuntimeException("Non-existent method "+argProcessorClass+"; "+parseMethodName+" (edit ArgProcessor)", e); } } } public String getRunMethodName() { return runMethodName; } public void setRunMethod(String runMethodName) { if (runMethodName != null) { try { Method method = argProcessorClass.getMethod(runMethodName, ArgumentOption.class); LOG.trace("RUN METHOD "+method); this.runMethodName = runMethodName; } catch (NoSuchMethodException e) { throw new RuntimeException("Non-existent method "+argProcessorClass+"; "+runMethodName+" (edit ArgProcessor)", e); } } } public String getOutputMethodName() { return outputMethodName; } public void setOutputMethod(String outputMethodName) { if (outputMethodName != null) { try { Method method = argProcessorClass.getMethod(outputMethodName, ArgumentOption.class); LOG.trace("OUTPUT METHOD "+method); this.outputMethodName = outputMethodName; } catch (NoSuchMethodException e) { throw new RuntimeException("Non-existent outputMethod "+argProcessorClass+"; "+outputMethodName+" (edit ArgProcessor)", e); } } } public void setInitMethod(String initMethodName) { if (initMethodName != null) { try { LOG.trace("INIT METHODNAME "+initMethodName); Method method = argProcessorClass.getMethod(initMethodName, ArgumentOption.class); LOG.trace("INIT METHOD "+method); this.initMethodName = initMethodName; } catch (NoSuchMethodException e) { throw new RuntimeException("Non-existent initMethod "+argProcessorClass+"; "+initMethodName+" (edit ArgProcessor)", e); } } } public String getInitMethodName() { return initMethodName; } public void setFinalMethod(String finalMethodName) { if (finalMethodName != null) { try { Method method = argProcessorClass.getMethod(finalMethodName, ArgumentOption.class); LOG.trace("FINAL METHOD "+method); this.finalMethodName = finalMethodName; } catch (NoSuchMethodException e) { throw new RuntimeException("Non-existent finalMethod "+argProcessorClass+"; "+finalMethodName+" (edit ArgProcessor)", e); } } } public String getFinalMethodName() { return finalMethodName; } public String getPatternString() { return patternString; } public void setPatternString(String patternString) { if (patternString == null) { LOG.error("null pattern"); } else { Pattern pattern = Pattern.compile(patternString); } } private String getForbiddenString() { return forbiddenString; } public String getRequiredString() { return requiredString; } public void setRequiredString(String required) { this.requiredString = required; } public void setForbiddenString(String forbidden) { this.forbiddenString = forbidden; } public ArgumentOption processArgs(List inputs) { ensureDefaults(); stringValue = defaultString; stringValues = defaultStrings; doubleValue = defaultDouble; doubleValues = defaultDoubles; doubleValue = defaultDouble; doubleValues = defaultDoubles; if (!countRange.includes(inputs.size())) { throw new RuntimeException("Bad number of arguments: "+inputs.size()+" incompatible with "+countRangeString); } if (classType == null) { classType = String.class; } if (classType.equals(String.class)) { stringValues = inputs; stringValue = (inputs.size() == 0) ? defaultString : inputs.get(0); } else if (classType.equals(Double.class)) { doubleValues = new ArrayList(); for (String input : inputs) { doubleValues.add(new Double(input)); } doubleValue = (doubleValues.size() == 0) ? defaultDouble : doubleValues.get(0); for (Number number : doubleValues) { if (!realValueRange.includes((double)number)) { throw new RuntimeException("bad numeric value: "+number+" should conform to "+valueRangeString); } } } else if (classType.equals(Boolean.class)) { booleanValue = inputs.size() == 1 ? new Boolean(inputs.get(0)) : defaultBoolean; } else if (classType.equals(Integer.class)) { integerValues = new ArrayList(); for (String input : inputs) { integerValues.add(new Integer(input)); } integerValue = (integerValues.size() == 0) ? defaultInteger : integerValues.get(0); for (Number number : integerValues) { if (!realValueRange.includes((double)number)) { throw new RuntimeException("bad numeric value: "+number+" should conform to "+valueRangeString); } } } else if (classType.equals(StringPair.class)) { checkStringPairs(inputs); } else { LOG.error("currently cannot support type: "+classType); } return this; } private void checkStringPairs(List inputs) { stringPairValues = new ArrayList(); for (String input : inputs) { String[] fields = input.trim().split(","); if (fields.length != 2) { throw new RuntimeException("Cannot parse "+input+" as comma-separated pair (foo,bar)"); } stringPairValues.add(new StringPair(fields[0], fields[1])); } // NYI // stringPairValueValue = (stringPairValues.size() == 0) ? defaultStringPairValue : stringPairValues.get(0); } public ArgumentOption ensureDefaults() { if (classType == null) { // no defaults } else if (defalt == null) { // no defaults } else if (classType.equals(String.class)) { defaultStrings = new ArrayList(); defaultStrings.add((String)defalt); if (countRange.isEqualTo(new IntRange(1,1))) { defaultString = (String)defalt; } } else if (classType.equals(Integer.class)) { Integer defaultInteger = null; try { defaultInteger = new Integer(String.valueOf(defalt)); } catch (Exception e) { throw new RuntimeException("default should be of type Integer"); } // FIXME no defaults } else if (classType.equals(Double.class) && defalt instanceof Double) { Double defaultDouble = null; try { defaultDouble = new Double(String.valueOf(defalt)); } catch (Exception e) { throw new RuntimeException("default should be of type Double"); } // FIXME no defaults } else if (classType.equals(Boolean.class) && defalt instanceof String) { defaultBoolean = false; try { defaultBoolean = new Boolean(String.valueOf(defalt)); } catch (Exception e) { throw new RuntimeException("default should be of type Boolean"); } } else { LOG.error("Incompatible type and default: "+classType+"; "+defalt.getClass()); } return this; } public String getDefaultString() { return defaultString; } public Integer getDefaultInteger() { return defaultInteger; } public Double getDefaultDouble() { return defaultDouble; } public Boolean getDefaultBoolean() { return defaultBoolean; } public List getDefaultStrings() { return defaultStrings; } public List getDefaultIntegers() { return defaultIntegers; } public List getDefaultDoubles() { return defaultDoubles; } public boolean matches(String arg) { return (brief.equals(arg) || verbose.equals(arg)); } public List getDoubleValues() { return doubleValues; } public List getIntegerValues() { return integerValues; } public List getStringValues() { return stringValues; } public String getStringValue() { return stringValue; } public Integer getIntegerValue() { return integerValue; } public Double getDoubleValue() { return doubleValue; } public Boolean getBooleanValue() { return booleanValue; } public List getStringPairValues() { return stringPairValues; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(brief+" or "+verbose+"; "+countRange+"; "+parseMethodName+"; "); if (classType == null) { sb.append("NULL CLASS: "+defaultString+" / "+defaultStrings+"; "+stringValue+"; "+stringValues); } else if (classType.equals(String.class)) { sb.append("STRING: "+defaultString+" / "+defaultStrings+"; "+stringValue+"; "+stringValues); } else if (classType.equals(Integer.class)) { sb.append("INTEGER: "+defaultInteger+" / "+defaultIntegers+"; "+integerValue+"; "+integerValues); } else if (classType.equals(Double.class)) { sb.append("DOUBLE: "+defaultDouble+" / "+defaultDoubles+"; "+doubleValue+"; "+doubleValues); } else if (classType.equals(Boolean.class)) { sb.append("BOOLEAN: "+defaultBoolean+"; "+booleanValue); } else if (classType.equals(StringPair.class)) { sb.append("STRINGPAIRS: ; "+stringPairValues); } else if (classType.equals(Object.class)) { sb.append("OBJECT ; "+stringValue); } return sb.toString(); } /** checks argument count; * * @param list * @return null indicates correct; non-null is explanatory message. */ public String checkArgumentCount(List list) { String message = null; if (countRange != null) { if (!countRange.includes(list.size())) { message = "argument count ("+list.size()+") is not compatible with "+countRangeString; } } return message; } /** checks argument values; * * @param list * @return null indicates correct; non-null is explanatory message. */ public String checkArgumentValues(List list) { String message = null; for (String s : list) { if (s == null) { message = "Cannot have null values in "+verbose; break; } message = checkBooleanValue(s); if (message != null) break; message = checkNumericValue(s); if (message != null) break; message = checkPatternValue(s); if (message != null) break; } return message; } private String checkBooleanValue(String s) { String message = null; if (classType != null && classType.isAssignableFrom(Boolean.class)) { try { new Boolean(s); } catch (Exception e) { message = "Argument for "+verbose +" ("+s+") should be true or false"; } } return message; } private String checkPatternValue(String s) { String message = null; if (classType != null && classType.isAssignableFrom(String.class)) { if (pattern != null) { Matcher matcher = pattern.matcher(s); message = "Argument for "+verbose +" ("+s+") does not match "+pattern; } } return message; } private String checkNumericValue(String s) { String message = null; if (classType == null) { throw new RuntimeException("null classType"); } else if (Double.class.isAssignableFrom(classType)) { message = checkDouble(s, message); } else if (Integer.class.isAssignableFrom(classType)) { message = checkInteger(s, message); } return message; } private String checkDouble(String s, String message) { Double d = null; try { d = new Double(s); } catch (NumberFormatException nfe) { message = "Not a number: "+nfe+"; in "+verbose; } if (realValueRange == null) { LOG.error("No value range"); } if (d == null || !realValueRange.includes(d)) { message = "value: "+s+" incompatible with: "+realValueRange; } return message; } private String checkInteger(String s, String message) { Integer ii = null; try { ii = new Integer(s); } catch (NumberFormatException nfe) { message = "Not a number: "+nfe+"; in "+verbose; } if (intValueRange == null) { LOG.error("No value range"); } if (ii == null || !intValueRange.includes(ii)) { message = "value: "+s+" incompatible with: "+intValueRange; } return message; } public void processDependencies(List argumentOptionList) { processForbidden(argumentOptionList); processRequired(argumentOptionList); } private void processRequired(List argumentOptionList) { this.getRequiredArguments(); if (requiredArguments != null) { for (String requiredArgument : requiredArguments) { LOG.trace(this.getVerbose()+" REQUIRED "+requiredArgument); if (!argumentOccursInOptions(requiredArgument, argumentOptionList)) { throw new RuntimeException("Cannot find required option: "+requiredArgument); } } } } private static boolean argumentOccursInOptions(String requiredArgument, List argumentOptionList) { for (ArgumentOption argumentOption : argumentOptionList) { String optionVerbose = argumentOption.getVerbose(); if (requiredArgument.equals(optionVerbose)) { return true; } } return false; } private void processForbidden(List argumentOptionList) { for (ArgumentOption argumentOption : argumentOptionList) { if (isForbidden(argumentOption)) { throw new RuntimeException("Must not have both "+this.verbose+" and "+argumentOption.getVerbose()); } } } private boolean isForbidden(ArgumentOption argumentOption) { String argument = argumentOption.getVerbose(); if (argument != null) { List forbiddenArguments = this.getForbiddenArguments(); for (String forbiddenArgument : forbiddenArguments) { if (argument.equals(forbiddenArgument)) { return true; } } } return false; } private List getForbiddenArguments() { forbiddenArguments = getWhitespaceSeparatedArguments(this.getForbiddenString()); return forbiddenArguments; } private List getRequiredArguments() { requiredArguments = getWhitespaceSeparatedArguments(this.getRequiredString()); return requiredArguments; } private static List getWhitespaceSeparatedArguments(String strings) { return (strings == null) ? new ArrayList() : new ArrayList(Arrays.asList(strings.split("\\s+"))); } } euclid-euclid-2.9/src/main/java/org/xmlcml/args/DefaultArgProcessor.java-old000066400000000000000000000466251461721410700271240ustar00rootroot00000000000000package org.xmlcml.args; import java.io.File; import java.io.FileFilter; import java.io.InputStream; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import nu.xom.Builder; import nu.xom.Element; import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.xmlcml.files.QuickscrapeNorma; import org.xmlcml.files.QuickscrapeNormaList; import org.xmlcml.xml.XMLUtil; @Deprecated public class DefaultArgProcessor { private static final Logger LOG = Logger.getLogger(DefaultArgProcessor.class); static { LOG.setLevel(Level.DEBUG); } public static final String MINUS = "-"; public static final String[] DEFAULT_EXTENSIONS = {"html", "xml", "pdf"}; public final static String H = "-h"; public final static String HELP = "--help"; private static Pattern INTEGER_RANGE = Pattern.compile("(.*)\\{(\\d+),(\\d+)\\}(.*)"); private static String RESOURCE_NAME_TOP = "/org/xmlcml/args"; private static String ARGS_RESOURCE = RESOURCE_NAME_TOP+"/"+"args.xml"; private static final Pattern INTEGER_RANGE_PATTERN = Pattern.compile("(\\d+):(\\d+)"); protected static final String ARGS_XML = "args.xml"; public static Pattern GENERAL_PATTERN = Pattern.compile("\\{([^\\}]*)\\}"); /** creates a list of tokens that are found in an allowed list. * * @param allowed * @param tokens * @return list of allowed tokens */ protected static List getChosenList(List allowed, List tokens) { List chosenTokens = new ArrayList(); for (String method : tokens) { if (allowed.contains(method)) { chosenTokens.add(method); } else { LOG.error("Unknown token: "+method); } } return chosenTokens; } protected String output; protected List extensionList = null; private boolean recursive = false; protected List inputList; public List argumentOptionList; public List chosenArgumentOptionList; protected QuickscrapeNormaList quickscrapeNormaList; protected QuickscrapeNorma currentQuickscrapeNorma; protected String summaryFileName; protected Map variableByNameMap; private VariableProcessor variableProcessor; protected List getArgumentOptionList() { return argumentOptionList; } public DefaultArgProcessor() { readArgumentOptions(ARGS_RESOURCE); } public DefaultArgProcessor(String resourceName) { this(); readArgumentOptions(resourceName); } public void readArgumentOptions(String resourceName) { ensureArgumentOptionList(); try { InputStream is = this.getClass().getResourceAsStream(resourceName); if (is == null) { throw new RuntimeException("Cannot read/find input resource stream: "+resourceName); } Element argElement = new Builder().build(is).getRootElement(); List elementList = XMLUtil.getQueryElements(argElement, "/*/*[local-name()='arg']"); for (Element element : elementList) { ArgumentOption argOption = ArgumentOption.createOption(this.getClass(), element); LOG.trace("created ArgumentOption: "+argOption); argumentOptionList.add(argOption); } } catch (Exception e) { throw new RuntimeException("Cannot read/process args file "+resourceName, e); } } private void ensureArgumentOptionList() { if (this.argumentOptionList == null) { this.argumentOptionList = new ArrayList(); } } public void expandWildcardsExhaustively() { while (expandWildcardsOnce()); } public boolean expandWildcardsOnce() { boolean change = false; ensureInputList(); List newInputList = new ArrayList(); for (String input : inputList) { List expanded = expandWildcardsOnce(input); newInputList.addAll(expanded); change |= (expanded.size() > 1 || !expanded.get(0).equals(input)); } inputList = newInputList; return change; } /** expand expressions/wildcards in input. * * @param input * @return */ private List expandWildcardsOnce(String input) { Matcher matcher = GENERAL_PATTERN.matcher(input); List inputs = new ArrayList(); if (matcher.find()) { String content = matcher.group(1); String pre = input.substring(0, matcher.start()); String post = input.substring(matcher.end()); inputs = expandIntegerMatch(content, pre, post); if (inputs.size() == 0) { inputs = expandStrings(content, pre, post); } if (inputs.size() == 0) { LOG.error("Cannot expand "+content); } } else { inputs.add(input); } return inputs; } private List expandIntegerMatch(String content, String pre, String post) { List stringList = new ArrayList(); Matcher matcher = INTEGER_RANGE_PATTERN.matcher(content); if (matcher.find()) { int start = Integer.parseInt(matcher.group(1)); int end = Integer.parseInt(matcher.group(2)); for (int i = start; i <= end; i++) { String s = pre + i + post; stringList.add(s); } } return stringList; } private List expandStrings(String content, String pre, String post) { List newStringList = new ArrayList(); List vars = Arrays.asList(content.split("\\|")); for (String var : vars) { newStringList.add(pre + var + post); } return newStringList; } // ============ METHODS =============== public void parseExtensions(ArgumentOption option, ArgIterator argIterator) { List extensions = argIterator.createTokenListUpToNextNonDigitMinus(option); setExtensions(extensions); } public void parseQuickscrapeNorma(ArgumentOption option, ArgIterator argIterator) { List qDirectoryNames = argIterator.createTokenListUpToNextNonDigitMinus(option); createQuickscrapeNormaList(qDirectoryNames); } public void printHelp(ArgumentOption option, ArgIterator argIterator) { printHelp(); } public void parseInput(ArgumentOption option, ArgIterator argIterator) { List inputs = argIterator.createTokenListUpToNextNonDigitMinus(option); inputList = expandAllWildcards(inputs); } public void parseOutput(ArgumentOption option, ArgIterator argIterator) { output = argIterator.getString(option); } public void parseRecursive(ArgumentOption option, ArgIterator argIterator) { recursive = argIterator.getBoolean(option); } public void parseSummaryFile(ArgumentOption option, ArgIterator argIterator) { summaryFileName = argIterator.getString(option); } public void outputMethod(ArgumentOption option) { LOG.error("outputMethod NYI"); } // ===================================== private void createQuickscrapeNormaList(List qDirectoryNames) { FileFilter directoryFilter = new FileFilter() { public boolean accept(File file) { return file.isDirectory(); } }; quickscrapeNormaList = new QuickscrapeNormaList(); for (String qDirectoryName : qDirectoryNames) { File qDirectory = new File(qDirectoryName); if (!qDirectory.exists()) { LOG.error("File does not exist: "+qDirectory.getAbsolutePath()); continue; } if (!qDirectory.isDirectory()) { LOG.error("Not a directory: "+qDirectory.getAbsolutePath()); continue; } QuickscrapeNorma quickscrapeNorma = new QuickscrapeNorma(qDirectoryName); if (quickscrapeNorma.containsNoReservedFilenames()) { List childFiles = new ArrayList(Arrays.asList(qDirectory.listFiles(directoryFilter))); List childFilenames = new ArrayList(); for (File childFile : childFiles) { if (childFile.isDirectory()) { childFilenames.add(childFile.toString()); } } LOG.trace(childFilenames); // recurse (no mixed directory structures) createQuickscrapeNormaList(childFilenames); } else { quickscrapeNormaList.add(quickscrapeNorma); } } } private List expandAllWildcards(List inputs) { inputList = new ArrayList(); for (String input : inputs) { inputList.addAll(expandWildcards(input)); } return inputList; } /** expand expressions/wildcards in input. * * @param input * @return */ private List expandWildcards(String input) { Matcher matcher = INTEGER_RANGE.matcher(input); List inputs = new ArrayList(); if (matcher.matches()) { int start = Integer.parseInt(matcher.group(2)); int end = Integer.parseInt(matcher.group(3)); if (start <= end) { for (int i = start; i <= end; i++) { String input0 = matcher.group(1)+i+matcher.group(4); inputs.add(input0); } } } else { inputs.add(input); } LOG.trace("inputs: "+inputs); return inputs; } // ===================================== public void setExtensions(List extensions) { this.extensionList = extensions; } public List getInputList() { ensureInputList(); return inputList; } public String getString() { ensureInputList(); return (inputList.size() != 1) ? null : inputList.get(0); } private void ensureInputList() { if (inputList == null) { inputList = new ArrayList(); } } public String getOutput() { return output; } public boolean isRecursive() { return recursive; } public String getSummaryFileName() { return summaryFileName; } public QuickscrapeNormaList getQuickscrapeNormaList() { ensureQuickscrapeNormaList(); return quickscrapeNormaList; } protected void ensureQuickscrapeNormaList() { if (quickscrapeNormaList == null) { quickscrapeNormaList = new QuickscrapeNormaList(); } } // -------------------------------- public void parseArgs(String[] commandLineArgs) { if (commandLineArgs == null || commandLineArgs.length == 0) { printHelp(); } else { String[] totalArgs = addDefaultsAndParsedArgs(commandLineArgs); ArgIterator argIterator = new ArgIterator(totalArgs); LOG.trace("args with defaults is: "+new ArrayList(Arrays.asList(totalArgs))); while (argIterator.hasNext()) { String arg = argIterator.next(); LOG.trace("arg> "+arg); try { addArgumentOptionsAndRunParseMethods(argIterator, arg); } catch (Exception e) { throw new RuntimeException("cannot process argument: "+arg+" ("+ExceptionUtils.getRootCauseMessage(e)+")", e); } } finalizeArgs(); } } public void parseArgs(String args) { parseArgs(args.split("\\s+")); } private void finalizeArgs() { processArgumentDependencies(); finalizeInputList(); } private void processArgumentDependencies() { for (ArgumentOption argumentOption : chosenArgumentOptionList) { argumentOption.processDependencies(chosenArgumentOptionList); } } private void finalizeInputList() { List inputList0 = new ArrayList(); ensureInputList(); for (String input : inputList) { File file = new File(input); if (file.isDirectory()) { LOG.debug("DIR: "+file.getAbsolutePath()+"; "+file.isDirectory()); addDirectoryFiles(inputList0, file); } else { inputList0.add(input); } } inputList = inputList0; } private void addDirectoryFiles(List inputList0, File file) { String[] extensions = getExtensions().toArray(new String[0]); List files = new ArrayList( FileUtils.listFiles(file, extensions, recursive)); for (File file0 : files) { inputList0.add(file0.toString()); } } private String[] addDefaultsAndParsedArgs(String[] commandLineArgs) { String[] defaultArgs = createDefaultArgumentStrings(); List totalArgList = new ArrayList(Arrays.asList(createDefaultArgumentStrings())); List commandArgList = Arrays.asList(commandLineArgs); totalArgList.addAll(commandArgList); String[] totalArgs = totalArgList.toArray(new String[0]); return totalArgs; } private String[] createDefaultArgumentStrings() { StringBuilder sb = new StringBuilder(); for (ArgumentOption option : argumentOptionList) { String defalt = String.valueOf(option.getDefault()); if (defalt != null && defalt.toString().trim().length() > 0) { String command = getBriefOrVerboseCommand(option); sb.append(command+" "+option.getDefault()+" "); } } String s = sb.toString().trim(); return s.length() == 0 ? new String[0] : s.split("\\s+"); } private String getBriefOrVerboseCommand(ArgumentOption option) { String command = option.getBrief(); if (command == null || command.trim().length() == 0) { command = option.getVerbose(); } return command; } public List getExtensions() { ensureExtensionList(); return extensionList; } private void ensureExtensionList() { if (extensionList == null) { extensionList = new ArrayList(); } } public void runRunMethodsOnChosenArgOptions() { for (ArgumentOption option : chosenArgumentOptionList) { String runMethodName = option.getRunMethodName(); LOG.trace("Method: "+runMethodName); if (runMethodName != null) { LOG.trace("Method " + runMethodName); try { runRunMethod(option); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("cannot process argument: "+option.getVerbose()+" ("+ExceptionUtils.getRootCauseMessage(e)+")", e); } } } } public void runOutputMethodsOnChosenArgOptions() { for (ArgumentOption option : chosenArgumentOptionList) { String outputMethodName = option.getOutputMethodName(); LOG.trace("OUTPUT "+outputMethodName); if (outputMethodName != null) { try { runOutputMethod(option); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("cannot process argument: "+option.getVerbose()+" ("+ExceptionUtils.getRootCauseMessage(e)+")"); } } } } public void runFinalMethodsOnChosenArgOptions() { ensureChosenArgumentList(); for (ArgumentOption option : chosenArgumentOptionList) { String finalMethodName = option.getFinalMethodName(); if (finalMethodName != null) { try { runFinalMethod(option); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("cannot process argument: "+option.getVerbose()+" ("+ExceptionUtils.getRootCauseMessage(e)+")"); } } } } protected void addArgumentOptionsAndRunParseMethods(ArgIterator argIterator, String arg) throws Exception { ensureChosenArgumentList(); boolean processed = false; if (!arg.startsWith(MINUS)) { LOG.error("Parsing failed at: ("+arg+"), expected \"-\" trying to recover"); } else { for (ArgumentOption option : argumentOptionList) { if (option.matches(arg)) { LOG.trace("OPTION>> "+option); String initMethodName = option.getInitMethodName(); if (initMethodName != null) { runInitMethod(option, initMethodName); } String parseMethodName = option.getParseMethodName(); if (parseMethodName != null) { runParseMethod(argIterator, option, parseMethodName); } processed = true; chosenArgumentOptionList.add(option); break; } } if (!processed) { LOG.error("Unknown arg: ("+arg+"), trying to recover"); } } } private void runInitMethod(ArgumentOption option, String initMethodName) { runMethod(null, option, initMethodName); } private void runParseMethod(ArgIterator argIterator, ArgumentOption option, String parseMethodName) { runMethod(argIterator, option, parseMethodName); } private void runMethod(ArgIterator argIterator, ArgumentOption option, String methodName) { Method method; try { if (argIterator == null) { method = this.getClass().getMethod(methodName, option.getClass()); } else { method = this.getClass().getMethod(methodName, option.getClass(), argIterator.getClass()); } } catch (NoSuchMethodException e) { debugMethods(); throw new RuntimeException("Cannot find: "+methodName+" in "+this.getClass()+"; from argument "+option.getClass()+";", e); } method.setAccessible(true); try { if (argIterator == null) { method.invoke(this, option); } else { method.invoke(this, option, argIterator); } } catch (Exception e) { LOG.trace("failed to run "+methodName+" in "+this.getClass()+"; from argument "+option.getClass()+";"+e.getCause()); // e.printStackTrace(); throw new RuntimeException("Cannot run: "+methodName+" in "+this.getClass()+"; from argument "+option.getClass()+";", e); } } private void debugMethods() { LOG.debug("methods for "+this.getClass()); for (Method meth : this.getClass().getDeclaredMethods()) { LOG.debug(meth); } } protected void runRunMethod(ArgumentOption option) throws Exception { String runMethodName = option.getRunMethodName(); if (runMethodName != null) { LOG.trace("running "+runMethodName); Method runMethod = null; try { runMethod = this.getClass().getMethod(runMethodName, option.getClass()); } catch (NoSuchMethodException nsme) { throw new RuntimeException(runMethodName+"; "+this.getClass()+"; "+option.getClass()+"; \nContact Norma developers: ", nsme); } runMethod.setAccessible(true); runMethod.invoke(this, option); } } protected void runOutputMethod(ArgumentOption option) throws Exception { String outputMethodName = option.getOutputMethodName(); if (outputMethodName != null) { Method outputMethod = null; try { outputMethod = this.getClass().getMethod(outputMethodName, option.getClass()); } catch (NoSuchMethodException nsme) { throw new RuntimeException(outputMethodName+"; "+this.getClass()+"; "+option.getClass()+"; \nContact Norma developers: ", nsme); } outputMethod.setAccessible(true); outputMethod.invoke(this, option); } } protected void runFinalMethod(ArgumentOption option) throws Exception { String finalMethodName = option.getFinalMethodName(); if (finalMethodName != null) { Method finalMethod = null; try { finalMethod = this.getClass().getMethod(finalMethodName, option.getClass()); } catch (NoSuchMethodException nsme) { throw new RuntimeException(finalMethodName+"; "+this.getClass()+"; "+option.getClass()+"; \nContact Norma developers: ", nsme); } finalMethod.setAccessible(true); finalMethod.invoke(this, option); } } private void ensureChosenArgumentList() { if (chosenArgumentOptionList == null) { chosenArgumentOptionList = new ArrayList(); } } protected void printHelp() { for (ArgumentOption option : argumentOptionList) { System.err.println(option.getHelp()); } } public List getChosenArgumentList() { ensureChosenArgumentList(); return chosenArgumentOptionList; } public String createDebugString() { StringBuilder sb = new StringBuilder(); getChosenArgumentList(); for (ArgumentOption argumentOption : chosenArgumentOptionList) { sb.append(argumentOption.toString()+"\n"); } return sb.toString(); } public void runAndOutput() { ensureQuickscrapeNormaList(); if (quickscrapeNormaList.size() == 0) { LOG.warn("Could not find list of CMdirs; possible error"); } for (int i = 0; i < quickscrapeNormaList.size(); i++) { currentQuickscrapeNorma = quickscrapeNormaList.get(i); runRunMethodsOnChosenArgOptions(); runOutputMethodsOnChosenArgOptions(); } runFinalMethodsOnChosenArgOptions(); } protected void addVariableAndExpandReferences(String name, String value) { ensureVariableProcessor(); try { variableProcessor.addVariableAndExpandReferences(name, value); } catch (Exception e) { LOG.error("add variable {"+name+", "+value+"} failed"); } } public VariableProcessor ensureVariableProcessor() { if (variableProcessor == null) { variableProcessor = new VariableProcessor(); } return variableProcessor; } } euclid-euclid-2.9/src/main/java/org/xmlcml/args/StringPair.java-old000066400000000000000000000004011461721410700252460ustar00rootroot00000000000000package org.xmlcml.args; /** a pair of strings. * * @author pm286 * */ @Deprecated public class StringPair { public String left; public String right; public StringPair(String left, String right) { this.left = left; this.right = right; } } euclid-euclid-2.9/src/main/java/org/xmlcml/args/VariableProcessor.java-old000066400000000000000000000033271461721410700266230ustar00rootroot00000000000000package org.xmlcml.args; import java.util.HashMap; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.log4j.Level; import org.apache.log4j.Logger; /** holds symbolic variabls for processing input. * * @author pm286 * */ @Deprecated public class VariableProcessor { private static final Logger LOG = Logger.getLogger(VariableProcessor.class); static { LOG.setLevel(Level.DEBUG); } // ${ ... } private static final Pattern VARIABLE_PATTERN = Pattern.compile("\\$\\{[^\\}]*\\}"); private HashMap variableByNameMap; public VariableProcessor() { this.variableByNameMap = new HashMap(); } public String getVariable(String name) { return variableByNameMap.get(name); } public boolean addVariableAndExpandReferences(String name, String value) { boolean added = true; if (variableByNameMap.containsKey(name)) { LOG.error("Duplicate variable name: "+name); added = false; } else { try { value = substituteVariables(value); } catch (Exception e) { added = false; } } return added; } public String substituteVariables(String value) { StringBuilder sb = new StringBuilder(); Matcher matcher = VARIABLE_PATTERN.matcher(value); int start = 0; int end = 0; while (matcher.find()) { start = matcher.start(); sb.append(value.substring(end, start)); end = matcher.end(); String variableRef = value.substring(start + 2, end - 1); String variableValue = variableByNameMap.get(variableRef); if (variableValue == null) { throw new RuntimeException("Cannot resolve variable ${"+variableRef+"} in "+value); } sb.append(variableValue); } sb.append(value.substring(end)); return sb.toString(); } } euclid-euclid-2.9/src/main/java/org/xmlcml/args/sandpit/000077500000000000000000000000001461721410700232145ustar00rootroot00000000000000euclid-euclid-2.9/src/main/java/org/xmlcml/args/sandpit/SandpitArgProcessor.java-old000077500000000000000000000035441461721410700306000ustar00rootroot00000000000000package org.xmlcml.args.sandpit; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.xmlcml.args.ArgIterator; import org.xmlcml.args.ArgumentOption; import org.xmlcml.args.DefaultArgProcessor; import org.xmlcml.euclid.IntArray; import org.xmlcml.euclid.RealArray; /** * Processes commandline arguments. * for Norma * * @author pm286 */ public class SandpitArgProcessor extends DefaultArgProcessor{ public static final Logger LOG = Logger.getLogger(SandpitArgProcessor.class); static { LOG.setLevel(Level.DEBUG); } private static String RESOURCE_NAME_TOP = "/org/xmlcml/args/sandpit"; private static String ARGS_RESOURCE = RESOURCE_NAME_TOP+"/"+"args.xml"; private Double dubble; private RealArray doubleArray; private Integer intg; private IntArray intArray; public SandpitArgProcessor() { super(ARGS_RESOURCE); } public SandpitArgProcessor(String[] args) { this(); parseArgs(args); } // ============= METHODS ============= public void parseDouble(ArgumentOption option, ArgIterator argIterator) { dubble = argIterator.getDouble(option); } public void parseDoubleArray(ArgumentOption option, ArgIterator argIterator) { doubleArray = argIterator.getDoubleArray(option); } public void parseInteger(ArgumentOption option, ArgIterator argIterator) { intg = argIterator.getInteger(option); } public void parseIntegerArray(ArgumentOption option, ArgIterator argIterator) { intArray = argIterator.getIntArray(option); } // ===========run=============== public Double getDouble() { return dubble; } public RealArray getDoubleArray() { return doubleArray; } public Integer getInteger() { return intg; } public IntArray getIntArray() { return intArray; } public void runTest(ArgumentOption option) { LOG.debug("RUN_TEST "+"is a dummy"); } // ========================== } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/000077500000000000000000000000001461721410700220635ustar00rootroot00000000000000euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/Angle.java000066400000000000000000000260721461721410700237630ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid; import org.apache.log4j.Logger; /** * Angle object * * Angle represents an angle The reason for the class is to help remember about * radian/degree problems, to keep the angle in the right range (0, 2*PI) or * (-PI, PI) as required, and to format output. *

* To construct an angle the user must consciously use RADIANS *

* The angle returned is always in RADIANS (except if getDegrees() is used) *

*
* If SIGNED is used, the angle is in the range -180 to 180 (-pi to pi)
* If UNSIGNED is used, the angle is in the range 0 to 360 (0 to 2*pi)
* Default is SIGNED *

* Default value of Angle is 0.0; Constructions of invalid angles should throw * exceptions rather than try to make invalid angles. * * @author (C) P. Murray-Rust, 1996 */ public class Angle { private final static Logger LOG = Logger.getLogger(Angle.class); /** units */ public enum Units { /** */ DEGREES, /** */ RADIANS; } /** range */ public enum Range { /** * any value. */ UNLIMITED, /** * 0 to 2*PI. */ UNSIGNED, /** * -PI to PI. */ SIGNED; } /** * default is UNLIMITED */ Range range = Range.UNLIMITED; /** * default is RADIANS */ Units type = Units.RADIANS; /** */ public final static double DEGREES_IN_RADIAN = 180.0 / Math.PI; /** * ALWAYS held as radians internally */ double angle = 0.0; /** * create default Angle default is (0.0) */ public Angle() { } /** * create an angle IN RADIANS * * @param a radian value */ public Angle(double a) { angle = a; } /** * construct using degrees or radians * * @param a value of angle * @param units unit of angle. It can either be in DEGREES or RADIANS */ public Angle(double a, Units units) { angle = (units == Units.RADIANS) ? a : a / DEGREES_IN_RADIAN; } /** * from X and Y components (uses atan2) * * @param x The x coordinate of the point * @param y The y coordinate of the point */ public Angle(double y, double x) { angle = Math.atan2(y, x); } /** * copy constructor * * @param a Angle object that needs to be copied */ public Angle(Angle a) { angle = a.angle; range = a.range; type = a.type; } /** * shallowCopy * * @param a Angle object that needs to be copied */ public void shallowCopy(Angle a) { range = a.range; type = a.type; angle = a.angle; } /** * add two angles * * @param a2 the specified angle to be added to this angle * @return new angle */ public Angle plus(Angle a2) { Angle temp = new Angle(angle + a2.angle); return temp; } /** * subtract two angles * * @param a2 the specified angle to be subtracted from this angle * @return new angle */ public Angle subtract(Angle a2) { Angle temp = new Angle(angle - a2.angle); return temp; } /** * multiply an angle by a scalar * * @param f a floating point scalar value * @return new angle */ public Angle multiplyBy(double f) { Angle temp = new Angle(angle * f); return temp; } /** * trigonometric functions * * @return cosine of angle */ public double cos() { return Math.cos(angle); } /** * sin of angle * * @return sine */ public double sin() { return Math.sin(angle); } /** * tan. * * @return the tan */ public double tan() { return Math.tan(angle); } /** * normalise angle. to range 0 -{@literal >} 2*PI * * @param angle the angle to be normalised * @return normalised angle */ public static double normalise(double angle) { while (angle > 2 * Math.PI) { angle -= 2 * Math.PI; } while (angle < 0.0) { angle += 2 * Math.PI; } LOG.trace(angle); return angle; } /** normalizes angle to be in range - Math.PI -{@literal >} Math.PI. * */ public void normalizeToPlusMinusPI() { angle = Angle.normalise(angle); if (angle > Math.PI) { angle -= 2 * Math.PI; } } /** * Normalises angle to be in range 0 -> Math.PI * 2. */ public void normalizeTo2Pi() { angle = Angle.normalise(angle); } /** * Tests whether this is a right angle. * * @param eps tolerance * @return 1 for PI/2, -1 for -PI/2 else 0 */ public Integer getRightAngle(Angle eps) { if (eps == null) return null; double absEps = Math.abs(eps.getRadian()); normalizeToPlusMinusPI(); Integer rt = 0; if (Math.abs(Math.PI / 2. - this.getRadian()) < absEps) { rt = 1; } else if (Math.abs(-Math.PI / 2. - this.getRadian()) < absEps) { rt = -1; } return rt; } /** * relational operators normalise the angles internally before comparison */ /** * are two normalised angles equal. * * @param a the specified angle to which this angle is compared * @return boolean * @deprecated // use epsilon method * */ public boolean isEqualTo(double a) { return Real.isEqual(Angle.normalise(angle), Angle.normalise(a)); } /** compare angles allowing for epsilon * * @param a the specified angle to which this angle is compared to be equal to * @param epsilon tolerance limit for equality * @return boolean */ public boolean isEqualTo(double a, double epsilon) { return Real.isEqual(Angle.normalise(angle), Angle.normalise(a), epsilon); } /** * is one angle greater than another (after normalisation) * * @param a the specified angle to which this angle is compared to be greater * @return greater than */ public boolean greaterThan(double a) { return Angle.normalise(angle) > Angle.normalise(a); } /** * is one angle greater than or equal to another (after normalisation) * * @param a the specified angle to which this angle is compared to be greater than or equal to * @return greater than or equals */ public boolean greaterThanOrEquals(double a) { return Angle.normalise(angle) >= Angle.normalise(a); } /** * is one angle less than another (after normalisation) * * @param a the specified angle to which this angle is compared to be lesser * @return {@literal <} */ public boolean lessThan(double a) { return Angle.normalise(angle) < Angle.normalise(a); } /** * is one angle less than or equal to another (after normalisation) * * @param a the specified angle to which this angle is compared to be lesser than or equal to * @return {@literal <}= */ public boolean lessThanOrEquals(double a) { return Angle.normalise(angle) <= Angle.normalise(a); } /** * are two angles equal * * @param a the specified angle to which this angle is compared to be equal to * @return == */ public boolean isEqualTo(Angle a) { return isEqualTo(a.angle); } /** * are two angles equal * * @param a the specified angle to which this angle is compared to be equal to * @param eps epsilon tolerance limit for equality * @return == */ public boolean isEqualTo(Angle a, double eps) { return a != null && Real.isEqual(a.getRadian(), this.getRadian(), eps); } /** * is one angle greater than another (after normalisation) * * @param a the specified angle to which this angle is compared to be greater (after normalisation) * @return {@literal >} */ public boolean greaterThan(Angle a) { return greaterThan(a.angle); } /** * is one angle greater than or equal to another (after normalisation) * * @param a the specified angle to which this angle is compared to be greater than or equal to (after normalisation) * @return {@literal >}= */ public boolean greaterThanOrEquals(Angle a) { return greaterThanOrEquals(a.angle); } /** * is one angle less than another (after normalisation) * * @param a the specified angle to which this angle is compared to be lesser (after normalisation) * @return {@literal <} */ public boolean lessThan(Angle a) { return lessThan(a.angle); } /** * is one angle less than or equal to another (after normalisation) * * @param a the specified angle to which this angle is compared to be lesser than or equal to (after normalisation) * @return {@literal <}= */ public boolean lessThanOrEquals(Angle a) { return lessThanOrEquals(a.angle); } /** * get angle in radians * * @return angle */ public double getAngle() { return adjust(angle); } /** * get angle in radians * * @return angle */ public double getRadian() { return adjust(angle); } /** * get angle in degrees * * @return angle */ public double getDegrees() { return adjust(angle) * DEGREES_IN_RADIAN; } /** * @param a angle in degrees */ public void putDegrees(double a) { angle = a / DEGREES_IN_RADIAN; } /** * set type of range * * @param range type of range */ public void setRange(Range range) { this.range = range; } /** * set angle to correct range */ private double adjust(double a) { if (range == Range.UNLIMITED) return a; double temp = normalise(a); if (range == Range.UNSIGNED) { return temp; } if (temp > Math.PI) { temp -= 2 * Math.PI; } else if (temp < -Math.PI) { temp += 2 * Math.PI; } return temp; } /** * to string. * * @return string */ public String toString() { StringBuffer s = new StringBuffer(); double temp = adjust(angle); if (type == Units.DEGREES) { s.append(temp).append(" degrees"); } else { s.append(temp); } return s.toString(); } } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/ArrayBase.java000066400000000000000000000023411461721410700245770ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid; import org.apache.log4j.Logger; /** * super class of array methods * * @author (C) P. Murray-Rust, 1996 */ public abstract class ArrayBase implements EuclidConstants { final static Logger LOG = Logger.getLogger(ArrayBase.class); /** */ public enum Trim { /** */ ABOVE(1), /** */ BELOW(2); /** */ public int trim; private Trim(int t) { this.trim = t; } } /** splits string versions of arrays. * */ public final static String ARRAY_REGEX = "\\s+|\\s*\\|\\s*|\\s*\\,\\s*"; } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/Axis.java000066400000000000000000000042761461721410700236430ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid; /** * enums to represent 2- or 3-D axes * * @author (C) P. Murray-Rust, 2005 */ public class Axis { /** enum for x y z axes */ public enum Axis2 { /** * x axis. value is 0 for indexing arrays. */ X("x", 0), /** * y axis. value is 1 for indexing arrays. */ Y("y", 1); /** string value */ public final String axis; /** integer value */ public final int value; /** * constructor. * * @param axis * label for the axis * @param value * serial number (starts at 0) */ private Axis2(String axis, int value) { this.axis = axis; this.value = value; } } /** 3d axes */ public enum Axis3 { /** * x axis. value is 0 for indexing arrays. */ X("x", 0), /** * y axis. value is 1 for indexing arrays. */ Y("y", 1), /** * z axis. value is 2 for indexing arrays. */ Z("z", 2); /** string value */ public final String axis; /** int value */ public final int value; /** * constructor. * * @param axis * label for the axis * @param value * serial number (starts at 0) */ private Axis3(String axis, int value) { this.axis = axis; this.value = value; } } } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/Bivariate.java000077500000000000000000000051661461721410700246470ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid; import org.apache.log4j.Logger; public class Bivariate { private final static Logger LOG = Logger.getLogger(Bivariate.class); private Real2Array real2Array; private Double slope; private Double intercept; private RealArray xarr; private RealArray yarr; private Double corrCoeff; private RealArray residuals; public Bivariate(Real2Array real2Array) { this.real2Array = real2Array; this.xarr = real2Array.getXArray(); this.yarr = real2Array.getYArray(); } public Double getSlope() { ensureSlope(); return slope; } public Double getIntercept() { ensureSlope(); return intercept; } private void ensureSlope() { if (slope == null && xarr.size() > 1) { double count = (double) xarr.size(); double sigmax = xarr.sumAllElements(); double sigmay = yarr.sumAllElements(); double sigmaxy = xarr.sumProductOfAllElements(yarr); double numerator = sigmaxy - sigmax * sigmay / count; double sigmax2 = xarr.sumProductOfAllElements(xarr); double sigmay2 = yarr.sumProductOfAllElements(yarr); double denominator = sigmax2 - sigmax * sigmax / count; slope = numerator / denominator; intercept = sigmay / count - slope * sigmax / count; corrCoeff = (count * sigmaxy - sigmax * sigmay) / Math.sqrt((count * sigmax2 - sigmax * sigmax)*(count * sigmay2 - sigmay * sigmay)); } } public Double getCorrelationCoefficient() { ensureSlope(); return corrCoeff; } public RealArray getResiduals() { ensureSlope(); residuals = new RealArray(xarr.size()); for (int i = 0; i < xarr.size(); i++) { double deltay = yarr.elementAt(i) - (slope * xarr.elementAt(i) + intercept); residuals.setElementAt(i, deltay); } return residuals; } public RealArray getNormalizedResiduals() { getResiduals(); LOG.trace("R> "+residuals.format(2)); Univariate univariate = new Univariate(residuals); RealArray normalisedResiduals = univariate.getNormalizedValues(); LOG.trace("N> "+normalisedResiduals.format(2)); return normalisedResiduals; } } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/Complex.java000066400000000000000000000101771461721410700243430ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid; /** * A complex number derived from Real2 * * Complex represents a complex number A reasonable number of arithmetic * operations are included DeMoivre's theorem is used for some of them so there * may be quicker implementations elsewhere. * * @author (C) P. Murray-Rust, 1996 */ public class Complex extends Real2 { /** * constructor. */ public Complex() { super(); } /** * real component only * * @param a */ public Complex(double a) { super(a, 0.0); } /** * from components * * @param a * @param b */ public Complex(double a, double b) { super(a, b); } /** * from base class * * @param a */ public Complex(Real2 a) { this.x = a.x; this.y = a.y; } /** * in polar coords * * @param r * @param th */ public Complex(double r, Angle th) { Polar p = new Polar(r, th); x = p.getX(); y = p.getY(); } /** * construct from polar * * @param p */ public Complex(Polar p) { x = p.getX(); y = p.getY(); } /** * copy constructor * * @param a */ public Complex(Complex a) { this.x = a.x; this.y = a.y; } /** * gets real part. * * @return real part */ public double getReal() { return x; } /** * gets imaginary part. * * @return imaginary * */ public double getImaginary() { return y; } /** * unary minus MODIFIES object */ public void negative() { this.x = -this.x; this.y = -this.y; } /** * multiply a complex by a complex. * * @param f * @return complex */ public Complex multiply(Complex f) { Complex temp = new Complex(this); temp.x = x * f.x - y * f.y; temp.y = x * f.y + y * f.x; return temp; } /** * divide a complex by a complex. * * @param f * @return complex * @throws EuclidRuntimeException */ public Complex divideBy(Complex f) throws EuclidRuntimeException { double denom = f.x * f.x + f.y * f.y; if (Real.isZero(denom, Real.getEpsilon())) { throw new EuclidRuntimeException("cannot divide by zero"); } Complex temp = new Complex(f.x, -f.y); temp = new Complex((temp.multiply(this)).multiplyBy(1 / denom)); return temp; } /** * get as polar coords. * * @return radius */ public double getR() { double t = Math.sqrt(x * x + y * y); // double t = x * x + y * y; return t; } /** * angle. * * @return the angle */ public Angle getTheta() { return new Angle(y, x); } /** * polar object. * * @return polar object */ public Polar getPolar() { return new Polar(getR(), getTheta()); } /** * complex square root. * * @param a * @return complex sqrt */ public static Complex sqrt(Complex a) { Polar temp = new Polar(a); temp.r = Math.sqrt(temp.r); temp.theta *= 0.5; return new Complex(temp); } /** * to string. * * @return string */ public String toString() { StringBuffer sb = new StringBuffer(); sb.append(x + EC.S_COMMA + y); return sb.toString(); } } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/DoubleTool.java000066400000000000000000000034131461721410700247770ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid; /** * *

* Tool providing methods for working with doubles. *

* * @author Sam Adams * */ public class DoubleTool { /** * tests equality of doubles. * * @param a * @param b * @param eps * margin of identity * @return true if a == b within eps */ public static boolean equals(double a, double b, double eps) { return (Math.abs(a - b) < Math.abs(eps)); } /** * tests equality of double arrays. arrays must be of same length * * @param a * first array * @param b * second array * @param eps * margin of identity * @return array elements equal within eps */ public static boolean equals(double[] a, double[] b, double eps) { boolean result = false; if (a.length == b.length) { result = true; for (int i = 0; i < a.length; i++) { if (Math.abs(a[i] - b[i]) > Math.abs(eps)) { result = false; break; } } } return result; } } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/EC.java000066400000000000000000000013711461721410700232170ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid; /** alias for brevity. * * @author pm286 * */ public interface EC extends EuclidConstants { } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/Euclid.java000066400000000000000000000025551461721410700241420ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * */ package org.xmlcml.euclid; /** * These routines have evolved over {@literal >}10 years and have now settled down to * primitive dataTypes to support CML. In general a CML data element (scalar, * array, matrix) * (double, int, String) will have a euclid primitive unless it * is already provided by Java or not useful. Almost all double types are * supported but some int and String types (e.g. IntSquareMatrix, StringArray, * etc.) are missing. The emphasis is on algebrra and geometry. * * In some CML routines (e.g. atom.geXYZ() a Point3 is returned, but in others * the wrapped type (e.g. STMLArray) is used. Please let us know if this design * works satisfactorily. * * @author pmr 2005 * */ public @interface Euclid { } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/EuclidConstants.java000066400000000000000000000144701461721410700260360ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid; import java.io.File; /** * *

* Constants *

* * @author Peter Murray-Rust * @version 5.0 * */ public interface EuclidConstants { char C_DEL = (char) 127; char C_BACKSPACE = (char) 8; /** constant */ char C_COLON = ':'; /** constant */ char C_SPACE = ' '; /** constant */ char C_NL = '\n'; /** constant */ char C_NBSP = (char) 160; /** constant */ char C_QUOT = '"'; /** constant */ char C_SLASH = '/'; /** constant */ char C_TAB = '\t'; /** constant */ char C_RETURN = '\r'; /** constant */ char C_NEWLINE = '\n'; /** constant */ char C_FORMFEED = '\f'; /** constant */ char C_LBRAK = '('; /** constant */ char C_RBRAK = ')'; /** constant */ char C_SHRIEK = '!'; /** constant */ char C_POUND = '\u00A3'; /** constant */ char C_DOLLAR = '$'; /** constant */ char C_PERCENT = '%'; /** constant */ char C_CARET = '^'; /** constant */ char C_AMP = '&'; /** constant */ char C_STAR = '*'; /** constant */ char C_UNDER = '_'; /** constant */ char C_MINUS = '-'; /** constant */ char C_PLUS = '+'; /** constant */ char C_EQUALS = '='; /** constant */ char C_LCURLY = '{'; /** constant */ char C_RCURLY = '}'; /** constant */ char C_LSQUARE = '['; /** constant */ char C_RSQUARE = ']'; /** constant */ char C_TILDE = '~'; /** constant */ char C_HASH = '#'; /** constant */ char C_SEMICOLON = ';'; /** constant */ char C_ATSIGN = '@'; /** constant */ char C_APOS = '\''; /** constant */ char C_COMMA = ','; /** constant */ // char C_CUBED = (char); /** constant */ char C_PERIOD = '.'; /** constant */ char C_QUERY = '?'; /** constant */ char C_LANGLE = '<'; /** constant */ char C_RANGLE = '>'; /** constant */ char C_PIPE = '|'; /** constant */ char C_BACKSLASH = '\\'; /** constant */ String S_BACKSLASH = "\\"; /** constant */ String S_COLON = ":"; /** constant */ String S_EMPTY = ""; /** constant */ String S_SPACE = " "; /** constant */ String S_NL = "\n"; /** constant */ String S_QUOT = "\""; /** constant */ String S_SLASH = "/"; /** constant */ String S_WHITEREGEX = S_BACKSLASH+"s+"; // java regex for any whitespace /** constant */ String S_TAB = "\t"; /** constant */ String S_RETURN = "\r"; /** constant */ String S_NEWLINE = "\n"; /** constant */ String S_FORMFEED = "\f"; /** constant */ String WHITESPACE = S_SPACE + S_TAB + S_RETURN + S_NEWLINE + S_FORMFEED; /** constant */ String S_LBRAK = "("; /** constant */ String S_RBRAK = ")"; /** constant */ String S_SHRIEK = "!"; /** constant */ String S_POUND = String.valueOf('\u00A3'); /** constant */ String S_DOLLAR = "$"; /** constant */ String S_PERCENT = "%"; /** constant */ String S_CARET = "^"; /** constant */ String S_AMP = "&"; /** constant */ String S_STAR = "*"; /** constant */ String S_UNDER = "_"; /** constant */ String S_MINUS = "-"; /** constant */ String S_PLUS = "+"; /** constant */ String S_EQUALS = "="; /** constant */ String S_LCURLY = "{"; /** constant */ String S_RCURLY = "}"; /** constant */ String S_LSQUARE = "["; /** constant */ String S_RSQUARE = "]"; /** constant */ String S_TILDE = "~"; /** constant */ String S_HASH = "#"; /** constant */ String S_SEMICOLON = ";"; /** constant */ String S_ATSIGN = "@"; /** constant */ String S_APOS = "'"; /** constant */ String S_COMMA = ","; /** constant */ String S_PERIOD = "."; /** constant */ String S_QUERY = "?"; /** constant */ String S_LANGLE = "<"; /** constant */ String S_RANGLE = ">"; /** constant */ String S_PIPE = "|"; /** punctuation without _.- and whitespace */ String NONWHITEPUNC0 = S_LBRAK + S_RBRAK + S_SHRIEK + S_QUOT + S_POUND + S_DOLLAR + S_PERCENT + S_CARET + S_AMP + S_STAR + S_PLUS + S_EQUALS + S_LCURLY + S_RCURLY + S_LSQUARE + S_RSQUARE + S_TILDE + S_HASH + S_COLON + S_SEMICOLON + S_ATSIGN + S_APOS + S_COMMA + S_SLASH + S_QUERY + S_LANGLE + S_RANGLE + S_PIPE + S_BACKSLASH; /** punctuation without _.- and whitespace */ String NONWHITEPUNC0REGEX = S_BACKSLASH+S_LBRAK + S_BACKSLASH+S_RBRAK + S_BACKSLASH+S_SHRIEK + S_BACKSLASH+S_QUOT + S_BACKSLASH+S_POUND + S_BACKSLASH+S_DOLLAR + S_BACKSLASH+S_PERCENT + S_BACKSLASH+S_CARET + S_BACKSLASH+S_AMP + S_BACKSLASH+S_STAR + S_BACKSLASH+S_PLUS + S_BACKSLASH+S_EQUALS + S_BACKSLASH+S_LCURLY + S_BACKSLASH+S_RCURLY + S_BACKSLASH+S_LSQUARE + S_BACKSLASH+S_RSQUARE + S_BACKSLASH+S_TILDE + S_BACKSLASH+S_HASH + S_BACKSLASH+S_COLON + S_BACKSLASH+S_SEMICOLON + S_BACKSLASH+S_ATSIGN + S_BACKSLASH+S_APOS + S_BACKSLASH+S_COMMA + S_BACKSLASH+S_SLASH + S_BACKSLASH+S_QUERY + S_BACKSLASH+S_LANGLE + S_BACKSLASH+S_RANGLE + S_BACKSLASH+S_PIPE + S_BACKSLASH+S_BACKSLASH; /** all punctuation without whitespace */ String NONWHITEPUNC = NONWHITEPUNC0 + S_UNDER + S_MINUS+ S_PERIOD ; /** all punctuation */ String PUNC = WHITESPACE + NONWHITEPUNC; /** convenience */ String F_S = File.separator; /** URL separator */ String U_S = S_SLASH; /** */ double EPS = 1.0E-14; /** */ double ONE_THIRD = 1.0/3.0; /** */ double TWO_THIRDS = 2.0/3.0; }euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/EuclidException.java000066400000000000000000000023301461721410700260100ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid; /** * replaces all Exceptions in jumbo.euclid with a single Exception. The old * Exceptions were too numerous and confused signatures * * @author (C) P. Murray-Rust, 1996 */ public class EuclidException extends Exception { /** * Comment for serialVersionUID */ private static final long serialVersionUID = 3617576011412288051L; /** * constructor. */ public EuclidException() { super(); } /** * constructor. * * @param s */ public EuclidException(String s) { super(s); } } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/EuclidRuntimeException.java000066400000000000000000000030241461721410700273550ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * */ package org.xmlcml.euclid; /** * *

* runtime exception for Euclid *

* * @author Joe Townsend * @version 5.0 * */ public class EuclidRuntimeException extends RuntimeException implements EuclidConstants { /** constructor * * @param message * @param cause */ public EuclidRuntimeException(String message, Throwable cause) { super(message, cause); } /** * */ private static final long serialVersionUID = 3618697517584169017L; protected EuclidRuntimeException() { super(); } /** * creates EuclidRuntime with message. * * @param msg */ public EuclidRuntimeException(String msg) { super(msg); } /** * creates EuclidRuntime from EuclidException. * * @param exception */ public EuclidRuntimeException(EuclidException exception) { this(S_EMPTY + exception); } } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/EuclidTestUtils.java000066400000000000000000000120111461721410700260070ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid; /** * contains tests for equality, etc. but not not use Assert. * Can therefore be used in tests without worrying about inclusion of * Junit, etc. * @author pm286 * */ public class EuclidTestUtils { /** * returns a message if arrays differ. * * @param a * array to compare * @param b * array to compare * @param eps * tolerance * @return null if arrays are equal else indicative message */ public static String testEquals(double[] a, double[] b, double eps) { String s = null; if (a == null) { s = "a is null"; } else if (b == null) { s = "b is null"; } else if (a.length != b.length) { s = "unequal arrays: " + a.length + EC.S_SLASH + b.length; } else { for (int i = 0; i < a.length; i++) { if (!Real.isEqual(a[i], b[i], eps)) { s = "unequal element at (" + i + "), " + a[i] + " != " + b[i]; break; } } } return s; } /** * returns a message if arrays differ. * @param msg to prepend * @param a array to compare * @param b array to compare * @param eps tolerance * @return null if arrays are equal else indicative message */ public static String testEquals(String msg, double[] a, double[] b, double eps) { String s = testEquals(a, b, eps); if (s != null) { s = msg+": "+s; } return s; } /** * returns a message if arrays of arrays differ. * * @param a * array to compare * @param b * array to compare * @param eps * tolerance * @return null if array are equal else indicative message */ static String testEquals(double[][] a, double[][] b, double eps) { String s = null; if (a == null) { s = "a is null"; } else if (b == null) { s = "b is null"; } else if (a.length != b.length) { s = "unequal arrays: " + a.length + EC.S_SLASH + b.length; } else { for (int i = 0; i < a.length; i++) { if (a[i].length != b[i].length) { s = "row (" + i + ") has unequal lengths: " + a[i].length + EC.S_SLASH + b[i].length; break; } for (int j = 0; j < a[i].length; j++) { if (!Real.isEqual(a[i][j], b[i][j], eps)) { s = "unequal element at (" + i + ", " + j + "), (" + a[i][j] + " != " + b[i][j] + EC.S_RBRAK; break; } } } } return s; } /** * returns a message if arrays differ. * @param msg to prepend * @param a array to compare * @param b array to compare * @param eps tolerance * @return null if arrays are equal else indicative message */ public static String testEquals(String msg, double[][] a, double[][] b, double eps) { String s = testEquals(a, b, eps); if (s != null) { s = msg+": "+s; } return s; } // Real2 /** * returns a message if arrays differ. * * @param a * array to compare * @param b * array to compare * @param eps * tolerance * @return null if arrays are equal else indicative message */ public static String testEquals(Real2 a, Real2 b, double eps) { String s = null; if (a == null) { s = "a is null"; } else if (b == null) { s = "b is null"; } else { if (!Real.isEqual(a.x, b.x, eps) || !Real.isEqual(a.y, b.y, eps)) { s = ""+a+" != "+b; } } return s; } // Plane3 /** * equality test. true if both args not null and equal within epsilon * * @param msg * message * @param test * @param expected * @param epsilon */ public static String testEquals(String msg, Plane3 expected, Plane3 test, double epsilon) { String s = null; if (test == null) { s = msg+": null test"; } else if (expected == null) { s = msg+": null expected"; } else { s = testEquals(msg, expected.getArray(), test, epsilon); } return s; } /** * equality test. true if both args not null and equal within epsilon * * @param msg * message * @param test * array must be of length 4 * @param expected * @param epsilon */ public static String testEquals(String msg, double[] expected, Plane3 test, double epsilon) { String s = null; if (expected == null) { s = msg+": expected should not be null"; } else if (expected.length != 4) { s = msg+": expected must be of length 4; was "+expected.length; } else if (test == null) { s = msg+": test should not be null"; } else { s = testEquals(msg, expected, test.getArray(), epsilon); } return s; } } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/Int.java000066400000000000000000000066161461721410700234710ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid; import org.apache.log4j.Logger; /** * Int supports various utilities for integers Use Integer where you want a * first-class Java object * * @author (C) P. Murray-Rust, 1996 */ public abstract class Int implements EuclidConstants { final static Logger LOG = Logger.getLogger(Int.class); /** * set an array to zero * * @param nelem * @param arr */ public static void zeroArray(int nelem, int[] arr) { for (int i = 0; i < nelem; i++) { arr[i] = 0; } } /** * set an array to given value * * @param nelem * @param arr * @param f */ public static void initArray(int nelem, int[] arr, int f) { for (int i = 0; i < nelem; i++) { arr[i] = f; } } /** * print a int[] * * @param a * */ public static void printArray(int[] a) { for (int i = 0; i < a.length; i++) { LOG.info(a[i] + EC.S_SPACE); } LOG.info(""); } /** * tests equality of int arrays. arrays must be of same length * * @param a * first array * @param b * second array * @return array elements equal */ public static boolean equals(int[] a, int[] b) { boolean result = false; if (a.length == b.length) { result = true; for (int i = 0; i < a.length; i++) { if (a[i] != b[i]) { result = false; break; } } } return result; } /** * compare integer arrays. * * @param a * @param b * @return message or null */ public static String testEquals(int[] a, int[] b) { String s = null; if (a == null) { s = "a is null"; } else if (b == null) { s = "b is null"; } else if (a.length != b.length) { s = "unequal arrays: " + a.length + S_SLASH + b.length; } else { for (int i = 0; i < a.length; i++) { if (a[i] != b[i]) { s = "unequal element (" + i + "), " + a[i] + " != " + b[i]; break; } } } return s; } /** * compare arrays. * * @param a * @param b * @return message or null if equal */ public static String testEquals(int[][] a, int[][] b) { String s = null; if (a == null) { s = "a is null"; } else if (b == null) { s = "b is null"; } else if (a.length != b.length) { s = "unequal arrays: " + a.length + S_SLASH + b.length; } else { for (int i = 0; i < a.length; i++) { if (a[i].length != b[i].length) { s = "row (" + i + ") has unequal lengths: " + a[i].length + S_SLASH + b[i].length; break; } for (int j = 0; j < a[i].length; j++) { if (a[i][j] != b[i][j]) { s = "unequal element at (" + i + ", " + j + "), (" + a[i][j] + " != " + b[i][j] + S_RBRAK; break; } } } } return s; } } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/Int2.java000066400000000000000000000207731461721410700235530ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid; import java.util.Arrays; /** * A pair of integers with no other assumptions Contains two ints. * * The default value is 0, 0. * * @author (C) P. Murray-Rust, 1996 */ public class Int2 implements EuclidConstants { /** the first integer value */ int x; /** the second integer value */ int y; /** * constructor. */ public Int2() { x = 0; y = 0; } /** * constructor. * * @param x * @param y */ public Int2(int x, int y) { this.x = x; this.y = y; } /** * copy constructor * * @param r */ public Int2(Int2 r) { this.x = r.x; this.y = r.y; } /** cast a Real2 into an Int2. * * @param xy2 * @return null if argument is null */ public static Int2 getInt2(Real2 xy2) { return (xy2 == null) ? null : new Int2((int)xy2.x, (int)xy2.y); } public boolean equals(Object obj) { if (obj == null || !(obj instanceof Int2)) { return false; } Int2 i2 = (Int2) obj; return (x == i2.x && y == i2.y); } public int hashCode() { return 13*x + 37*y; } /** * swaps the x and y values */ public void swap() { int t = x; x = y; y = t; } /** * sorts x and y so that x {@literal <}= y */ public void sortAscending() { if (x > y) this.swap(); } /** * sorts x and y so that x {@literal >}= y */ public void sortDescending() { if (x < y) this.swap(); } /** * set to 0, 0 */ public void clear() { x = y = 0; } /** * set x. * * @param xx */ public void setX(int xx) { x = xx; } /** * set y. * * @param yy */ public void setY(int yy) { y = yy; } /** * is equal to. * * @param r * @return true if equal */ public boolean isEqualTo(Int2 r) { return (x == r.x && y == r.y); } /** * add two points to give vector sum * * @param r2 * @return new point */ public Int2 plus(Int2 r2) { return new Int2(x + r2.x, y + r2.y); } /** * subtract two points to give vector difference * * @param r2 * @return point */ public Int2 subtract(Int2 r2) { return new Int2(x - r2.x, y - r2.y); } /** * multiply both components by minus one MODIFIES 'this' */ public void negative() { this.x = -this.x; this.y = -this.y; } /** * multiply a point by a scalar * * @param f * @return point */ public Int2 multiplyBy(int f) { return new Int2(x * f, y * f); } /** * get X value * * @return value */ public int getX() { return x; } /** * get Y value * * @return value */ public int getY() { return y; } /** * get either value; counts from ZERO * * @param elem * @return element * @throws EuclidRuntimeException */ public int elementAt(int elem) throws EuclidRuntimeException { if (elem == 0) { return x; } else if (elem == 1) { return y; } throw new EuclidRuntimeException("bad index " + elem); } /** * point midway between 'this' and 'p' * * @param p * @return midpoint */ public Int2 getMidPoint(Int2 p) { return new Int2((this.x + p.x) / 2, (this.y + p.y) / 2); } /** * get dot product * * @param r * @return dot */ public int dotProduct(Int2 r) { return (this.x * r.x + this.y * r.y); } /** * to string. * * @return string */ public String toString() { return EC.S_LBRAK + x + EC.S_COMMA + y + EC.S_RBRAK; } public double getEuclideanDistance(Int2 int2) { return new Real2(this).getDistance(new Real2(int2)); } public double getManhattanDistance(Int2 int2) { return Math.abs(this.x - int2.x) + Math.abs(this.y - int2.y); } /** adds 1 to x value. * * changes this */ public Int2 incrementX() { this.x++; return this; } /** subtracts 1 from x value. * * changes this */ public Int2 decrementX() { this.x--; return this; } /** adds 1 to y value. * * changes this */ public Int2 incrementY() { this.y++; return this; } /** subtracts 1 from y value. * * changes this */ public Int2 decrementY() { this.y--; return this; } /** compares two Int2s. Any null values return false. * * @param xy0 * @param xy1 * @return */ public static boolean isEqual(Int2 xy0, Int2 xy1) { return (xy0 != null && xy1 != null && xy0.isEqualTo(xy1)); } } /** * Int2Array is NOT a Vector of Int2s, but a container for a 2-D array with a * variety of ways of managing the data including IntArrays for the x and y * arrays, and also an array of Int2s The latter is only stored if required, and * then is cached. */ class Int2Array { // store in both ways... (wasteful but convenient) IntArray xarr; IntArray yarr; Int2[] xy; int nelem = 0; private boolean expanded = false; /** * default constructor gives an array of 0 points */ public Int2Array() { } /** * get max and min value of Int2_Array * * @return range */ public Int2Range getRange2() { Int2Range temp = new Int2Range(); expand(); for (int i = 0; i < nelem; i++) { temp.add(elementAt(i)); } return temp; } /** * make an Int2Array from 2 IntArrays. copies each. * * @param x * @param y * @exception EuclidRuntimeException unequal sized arrays */ public Int2Array(IntArray x, IntArray y) throws EuclidRuntimeException { if (x.size() != y.size()) { throw new EuclidRuntimeException("arrays of different sizes"); } nelem = x.size(); xarr = (IntArray) x.clone(); yarr = (IntArray) y.clone(); expanded = false; } private void expand() { if (expanded) return; expanded = true; xy = new Int2[nelem]; try { for (int i = 0; i < nelem; i++) { xy[i] = new Int2(xarr.elementAt(i), yarr.elementAt(i)); } } catch (Exception e) { Util.BUG(e); } } /** * size of array * * @return size */ public int size() { return nelem; } /** * get element. * * @param elem * @return element */ public Int2 elementAt(int elem) { expand(); return xy[elem]; } /** hash code. * @return hash code */ @Override public int hashCode() { final int PRIME = 31; int result = 1; result = PRIME * result + ((xarr == null) ? 0 : xarr.hashCode()); result = PRIME * result + Arrays.hashCode(xy); result = PRIME * result + ((yarr == null) ? 0 : yarr.hashCode()); return result; } /** equals. * @param obj * @return equality */ @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; final Int2Array other = (Int2Array) obj; if (xarr == null) { if (other.xarr != null) return false; } else if (!xarr.equals(other.xarr)) return false; if (!Arrays.equals(xy, other.xy)) return false; if (yarr == null) { if (other.yarr != null) return false; } else if (!yarr.equals(other.yarr)) return false; return true; } } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/Int2Range.java000066400000000000000000000151761461721410700245310ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid; /** * * 2-D int limits * * Contains two IntRanges. Can therefore be used to describe 2-dimensional * limits (for example axes of graphs, rectangles in graphics, limits of a * molecule, etc.) *

* Default is two default/invalid IntRange components. Adding points will create * valid ranges. * * @author (C) P. Murray-Rust, 1996 */ public class Int2Range implements EuclidConstants { /** * X-range */ IntRange xrange; /** * Y-range */ IntRange yrange; /** * creates zero range. * * */ public Int2Range() { xrange = new IntRange(); yrange = new IntRange(); } /** * initialise with min and max values; * * @param xr X-range * @param yr Y-range */ public Int2Range(IntRange xr, IntRange yr) { if (xr.isValid() && yr.isValid()) { xrange = xr; yrange = yr; } } /** * copy constructor * * @param r the IntRange object to be copied */ public Int2Range(Int2Range r) { if (r.isValid()) { xrange = new IntRange(r.xrange); yrange = new IntRange(r.yrange); } } /** * copy constructor * * @param r the IntRange object to be copied */ public Int2Range(Real2Range r) { xrange = new IntRange(r.xrange); yrange = new IntRange(r.yrange); } /** * a Int2Range is valid if both its constituent ranges are * * @return valid */ public boolean isValid() { return (xrange != null && yrange != null && xrange.isValid() && yrange .isValid()); } /** * is equal to. * * @param r2 the intRange to be compared with this intRange * @return true if equal */ public boolean isEqualTo(Int2Range r2) { if (isValid() && r2 != null && r2.isValid()) { return (xrange.isEqualTo(r2.xrange) && yrange.isEqualTo(r2.yrange)); } else { return false; } } @Override public boolean equals(Object o) { boolean equals = false; if (o != null && o instanceof Int2Range) { Int2Range i2r =(Int2Range) o; equals = this.getXRange().equals(i2r.getXRange()) && this.getYRange().equals(i2r.getYRange()); } return equals; } @Override public int hashCode() { return 17*xrange.hashCode() + 31*yrange.hashCode(); } /** * merge two ranges and take the maximum extents * * @param r2 the intRange to be added with this intRange * @return range */ public Int2Range plus(Int2Range r2) { if (!isValid()) { if (r2 == null || !r2.isValid()) { return new Int2Range(); } else { return new Int2Range(r2); } } if (r2 == null || !r2.isValid()) { return new Int2Range(this); } return new Int2Range(xrange.plus(r2.xrange), yrange.plus(r2.yrange)); } /** * intersect two ranges and take the range common to both; return invalid * range if no overlap or either is null/invalid * * @param r2 * @return range * */ public Int2Range intersectionWith(Int2Range r2) { if (!isValid() || r2 == null || !r2.isValid()) { return new Int2Range(); } IntRange xr = this.getXRange().intersectionWith(r2.getXRange()); IntRange yr = this.getYRange().intersectionWith(r2.getYRange()); return new Int2Range(xr, yr); } /** * get xrange * * @return range */ public IntRange getXRange() { return xrange; } /** * get yrange * * @return range */ public IntRange getYRange() { return yrange; } /** extends XRange. * * does not alter this. Uses range.extendBy(). Positive numbers will expand the range * * @param leftSide * @param rightSide */ public Int2Range getInt2RangeExtendedInX(int leftSide, int rightSide) { Int2Range i2r = new Int2Range(this); if (i2r.xrange != null) { i2r.xrange = i2r.xrange.getRangeExtendedBy(leftSide, rightSide); } return i2r; } /** extends XRange. * * does not alter this. Uses range.extendBy(). Positive numbers will expand the range * * @param topExtend * @param bottomExtend */ public Int2Range getInt2RangeExtendedInY(int topExtend, int bottomExtend) { Int2Range i2r = new Int2Range(this); if (i2r.yrange != null) { i2r.yrange = i2r.yrange.getRangeExtendedBy(topExtend, bottomExtend); } return i2r; } /** * is an Int2 within a Int2Range * * @param p * @return includes */ public boolean includes(Int2 p) { if (!isValid()) { return false; } return (xrange.includes(p.getX()) && yrange.includes(p.getY())); } /** * is one Int2Range completely within another * * @param r * @return includes */ public boolean includes(Int2Range r) { if (!isValid() || r == null || !r.isValid()) { return false; } IntRange xr = r.getXRange(); IntRange yr = r.getYRange(); return (xrange.includes(xr) && yrange.includes(yr)); } /** * add a Int2 to a range * * @param p */ public void add(Int2 p) { xrange.add(p.getX()); yrange.add(p.getY()); } /** * to string. * * @return string */ public String toString() { return EC.S_LBRAK + xrange.toString() + EC.S_COMMA + yrange.toString() + EC.S_RBRAK; } /** do two boxes touch? * * if box a extends to x and box b extends from x+1 they are touching. * uses IntRange.touches() * * Note that if box a and b share an integer coordinate then they *intersect*, not touch * * @param bbox * @return */ public boolean touches(Int2Range bbox) { return this.xrange.touches(bbox.xrange) || this.yrange.touches(bbox.yrange); } }euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/IntArray.java000066400000000000000000001174111461721410700244640ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid; import java.util.Arrays; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import org.apache.log4j.Logger; /** * array of ints * * IntArray represents a 1-dimensional vector or array of ints and is basically * a wrapper for int[] in Java There are a lot of useful member functions * (sorting, ranges, parallel operations The default is an array with zero * points All arrays are valid objects. Attempting to create an array with {@literal <} 0 * points creates a default array (zero points). Since int[] knows its length * (unlike C), there are many cases where int[] can be safely used. However it * is not a first-class object and IntArray supplies this feature int[] is * referenceable through getArray() note that the length of the internal array * may not be a useful guide to the number of elements * * @author (C) P. Murray-Rust, 1996 */ public class IntArray extends ArrayBase implements Iterable { final static Logger LOG = Logger.getLogger(IntArray.class); /** * maximum number of elements (for bound checking) * * resettable */ private int maxelem = 10000; /** * actual number of elements */ int nelem; /** * the array of ints */ int[] array; int bufsize = 5; /** * create default Array. default is an array of zero points */ public IntArray() { nelem = 0; bufsize = 5; array = new int[bufsize]; } /** * checks potential size of array. if n < 0, set to 0, otherwise adjust * bufsize to be consistent * * @param n * size of array * @return false if negative */ private boolean checkSize(int n) { if (n < 0) { n = 0; return false; } else { nelem = n; if (nelem > maxelem) maxelem = nelem; if (bufsize < nelem) bufsize = nelem; return true; } } // expands buffer if necessary and copies array into it private void makeSpace(int newCount) { if (newCount > bufsize) { while (newCount > bufsize) { bufsize *= 2; } int[] array1 = new int[bufsize]; System.arraycopy(array, 0, array1, 0, nelem); array = array1; } } /** * creates n-element array. initialised to 0 * * @param n * size of array */ public IntArray(int n) { this(n, 0); } /** * create n-element array initialised linearly. values are elem1+(i-1)*delta * * @param n * size of array * @param elem1 * starting value * @param delta * setpsize */ public IntArray(int n, int elem1, int delta) { if (!checkSize(n)) return; array = new int[n]; bufsize = n; int ff = elem1; for (int i = 0; i < n; i++) { array[i] = ff; ff += delta; } } /** * create Array initialized to constant value. all elements of the array are * set to a given value * * @param n * size of array * @param elem1 * value to set */ public IntArray(int n, int elem1) { if (!checkSize(n)) return; array = new int[n]; bufsize = n; for (int i = 0; i < n; i++) { array[i] = elem1; } } /** * create Array from part of java array. use first n elements of array. * * @param n * number of elements to use * @param arr * array to read from * @throws EuclidRuntimeException * n larger than arraysize */ public IntArray(int n, int[] arr) throws EuclidRuntimeException { if (!checkSize(n)) throw new EuclidRuntimeException("Cannot have negative array length"); if (n > arr.length) { throw new EuclidRuntimeException("Array would overflow"); } array = new int[n]; bufsize = n; System.arraycopy(arr, 0, array, 0, n); } /** * create Array from java array. * * @param arr * array to read from */ public IntArray(int[] arr) { addArray(arr); } private void addArray(int[] arr) { nelem = arr.length; array = new int[nelem]; bufsize = nelem; System.arraycopy(arr, 0, array, 0, nelem); } public IntArray(List intList) { int[] arr = new int[intList.size()]; for (int i = 0; i < arr.length; i++) { arr[i] = intList.get(i); } addArray(arr); } /** * create from subarray of another Array. * * @param m * array to slice * @param low * inclusive start index of array * @param high * inclusive end index of array * @throws EuclidRuntimeException * low {@literal >} high or negative indices or outside size of m */ public IntArray(IntArray m, int low, int high) throws EuclidRuntimeException { if (low < 0 || low > high || high >= m.size()) { throw new EuclidRuntimeException("index out of range: " + low + EC.S_SLASH + high); } nelem = high - low + 1; checkSize(nelem); array = new int[nelem]; bufsize = nelem; System.arraycopy(m.array, low, array, 0, nelem); } /** * create mixed sliced array. use another IntArray to subscript this one * where I(this) = I(ref) subscripted by I(sub); Result has dimension of * I(sub). caller is responsible for making sure elements of sub are unique * * @param ref * matrix to slice * @param sub * subscripts. * @throws EuclidRuntimeException * if any of I(sub) lies outside 0...refmax-1 */ public IntArray(IntArray ref, IntArray sub) throws EuclidRuntimeException { this(sub.size()); for (int i = 0; i < sub.size(); i++) { int j = sub.elementAt(i); if (j < 0 || j >= ref.size()) { throw new EuclidRuntimeException(); } this.setElementAt(i, ref.elementAt(j)); } } /** * clone. * * @return the clone */ public Object clone() { IntArray temp = new IntArray(nelem); temp.nelem = nelem; temp.maxelem = maxelem; System.arraycopy(array, 0, temp.array, 0, nelem); temp.bufsize = nelem; return (Object) temp; } /** * copy constructor. * * @param m * array to copy */ public IntArray(IntArray m) { this.shallowCopy(m); System.arraycopy(m.array, 0, array, 0, nelem); } /** * Create customized array. create a given 'shape' of array for data * filtering An intended use is with IntArray.arrayFilter(). The shapes * (before scaling by maxval) are: *

    *
  • "TRIANGLE"; 1/nn, 2/nn, ... 1 ... 2/nn, 1/nn; nelem is set to 2*nn - * 1 *
  • "ZIGZAG"; 1/nn, 2/nn, ... 1 ... 1/nn, 0, -1/nn, -2/nn, -1, ... * -1/nn,; nelem is set to 4*nn - 1 *
* step is maxval / nn * * @param nn * number of elements * @param shape * TRIANGLE or ZIGZAG * @param maxval * used to compute step */ public IntArray(int nn, String shape, int maxval) { if (shape.toUpperCase().equals("TRIANGLE")) { nelem = nn * 2 - 1; if (!checkSize(nelem)) return; array = new int[nelem]; int delta = maxval / ((int) nn); for (int i = 0; i < nn; i++) { array[i] = (i + 1) * delta; array[nelem - i - 1] = array[i]; } } else if (shape.toUpperCase().equals("ZIGZAG")) { nelem = nn * 4 - 1; if (!checkSize(nelem)) return; array = new int[nelem]; int delta = maxval / ((int) nn); for (int i = 0; i < nn; i++) { array[i] = (i + 1) * delta; array[2 * nn - i - 2] = array[i]; array[2 * nn + i] = -array[i]; array[nelem - i - 1] = -array[i]; } array[2 * nn - 1] = 0; } } /** * construct from an array of Strings. must represent integers * * @param strings values as Strings * @exception NumberFormatException * a string could not be interpreted as integer */ public IntArray(String[] strings) throws NumberFormatException { this(strings.length); for (int i = 0; i < strings.length; i++) { array[i] = (Integer.valueOf(strings[i])).intValue(); } } /** * create from a space-separated string of integers. * * @param string * of form "1 3 56 2..." * @exception NumberFormatException * a substring could not be interpreted as integer */ public IntArray(String string) throws NumberFormatException { this(string.split(S_WHITEREGEX)); } /** * contracts internal array to be of same length as number of elements. * * should be used if the array will be used elsewhere with a fixed length. * */ public void contractArray() { int[] array1 = new int[nelem]; System.arraycopy(array, 0, array1, 0, nelem); array = array1; } /** * shallowCopy * * @param m */ public void shallowCopy(IntArray m) { nelem = m.nelem; bufsize = m.bufsize; maxelem = m.maxelem; array = m.array; } /** * get element by index. * * @param elem * the index * @exception ArrayIndexOutOfBoundsException * elem {@literal >}= size of this * @return element value */ public int elementAt(int elem) throws ArrayIndexOutOfBoundsException { return array[elem]; } /** * get actual number of elements. * * @return number of elements */ public int size() { return nelem; } /** * get java array. * * @return the array */ public int[] getArray() { if (nelem != array.length) { int[] temp = new int[nelem]; System.arraycopy(array, 0, temp, 0, nelem); array = temp; } return array; } /** * are two arrays equal. * * @param f * array to compare * @return true if arrays are of same size and elements are equal) */ public boolean equals(IntArray f) { boolean equal = false; try { checkConformable(f); equal = true; for (int i = 0; i < nelem; i++) { if (array[i] != f.array[i]) { equal = false; break; } } } catch (Exception e) { equal = false; } return equal; } /** * clear all elements of array. sets value to 0 */ public void clearArray() { for (int i = 0; i < size(); i++) { array[i] = 0; } } /** * get java array in reverse order. * * @return array */ public int[] getReverseArray() { int count = size(); int[] temp = new int[count]; for (int i = 0; i < size(); i++) { temp[i] = this.array[--count]; } return temp; } /** * reset the maximum index (for when poking elements) (no other effect) */ /*-- public void setMaxIndex(int max) { maxelem = max; } --*/ private void checkConformable(IntArray m) throws EuclidRuntimeException { if (nelem != m.nelem) { throw new EuclidRuntimeException(); } } /** * are two arrays equal. * * @param f * array to compare * @return true if arrays are of same size and this(i) = f(i) */ public boolean isEqualTo(IntArray f) { boolean equal = false; try { checkConformable(f); equal = true; for (int i = 0; i < nelem; i++) { if (array[i] != f.array[i]) { equal = false; break; } } } catch (Exception e) { equal = false; } return equal; } /** * adds arrays. does not modify this * * @param f * array to add * @exception EuclidRuntimeException * f is different size from this * @return new array as this + f */ public IntArray plus(IntArray f) throws EuclidRuntimeException { checkConformable(f); IntArray m = (IntArray) this.clone(); for (int i = 0; i < nelem; i++) { m.array[i] = f.array[i] + array[i]; } return m; } /** * subtracts arrays. does not modify this * * @param f * array to substract * @exception EuclidRuntimeException * f is different size from this * @return new array as this - f */ public IntArray subtract(IntArray f) throws EuclidRuntimeException { checkConformable(f); IntArray m = (IntArray) this.clone(); for (int i = 0; i < nelem; i++) { m.array[i] = array[i] - f.array[i]; } return m; } /** * array subtraction. modifies this -= f * * @param f * array to subtract * @exception EuclidRuntimeException * f is different size from this */ public void subtractEquals(IntArray f) throws EuclidRuntimeException { checkConformable(f); for (int i = 0; i < nelem; i++) { array[i] -= f.array[i]; } } /** * change the sign of all elements. MODIFIES this */ public void negative() { for (int i = 0; i < size(); i++) { array[i] = -array[i]; } } /** * add a scalar to all elements. creates new array; does NOT modify 'this'; * for subtraction use negative scalar * * @param f * to add * @return new array */ /*-- public RealArray addScalar(int f) { RealArray m = (RealArray) this.clone(); for(int i = 0; i < nelem; i++) { m.array[i] += f; } return m; } --*/ /** * array multiplication by a scalar. creates new array; does NOT modify * 'this' * * @param f * multiplier * @return the new array */ public IntArray multiplyBy(int f) { IntArray m = (IntArray) this.clone(); for (int i = 0; i < nelem; i++) { m.array[i] *= f; } return m; } /** * set element value. * * @param elem * index * @param f * value * @exception ArrayIndexOutOfBoundsException * elem {@literal >}= size of this */ public void setElementAt(int elem, int f) throws ArrayIndexOutOfBoundsException { array[elem] = f; } /** * get array slice. creates new array; does not modify this * * @param start * index inclusive * @param end * index inclusive * @return new array */ public IntArray getSubArray(int start, int end) { int nel = end - start + 1; IntArray f = new IntArray(nel, 0); System.arraycopy(array, start, f.array, 0, nel); return f; } /** * set array slice. copy whole array into the array. * * @param start * index in this * @param a * array to copy * @throws ArrayIndexOutOfBoundsException * start {@literal <} 0 or start+a.length {@literal >} this.size() */ public void setElements(int start, int[] a) { if (start < 0 || start + a.length > nelem) { throw new ArrayIndexOutOfBoundsException(); } System.arraycopy(a, 0, this.array, start, a.length); } /** * is the array filled with zeros. * * @return true if this(i) = 0 */ public boolean isClear() { for (int i = 0; i < nelem; i++) { if (array[i] != 0) return false; } return true; } /** * initialise array to given value. this(i) = f * * @param f * value to set */ public void setAllElements(int f) { Int.initArray(nelem, array, f); } /** * sum all elements. * * @return sigma(this(i)) */ public int sumAllElements() { int sum = 0; for (int i = 0; i < nelem; i++) { sum += array[i]; } return sum; } /** * sum of all absolute element values. * * @return sigma(abs(this(i))) */ public int absSumAllElements() { int sum = 0; for (int i = 0; i < nelem; i++) { sum += Math.abs(array[i]); } return sum; } /** * inner product. dotProduct(this) * * @return sigma(this(i)**2) */ public int innerProduct() { int result = Integer.MIN_VALUE; try { result = this.dotProduct(this); } catch (EuclidRuntimeException x) { throw new EuclidRuntimeException("bug " + x); } return result; } /** * dot product of two arrays. sigma(this(i)*(f(i)); * * @param f * array to multiply * @exception EuclidRuntimeException * f is different size from this * @return dot */ public int dotProduct(IntArray f) throws EuclidRuntimeException { checkConformable(f); int sum = 0; for (int i = 0; i < nelem; i++) { sum += array[i] * f.array[i]; } return sum; } /** * cumulative sum of array. create new array as elem[i] = sum(k = 0 to i) * f[k] does not modify 'this' * * @return each element is cumulative sum to that point */ public IntArray cumulativeSum() { IntArray temp = new IntArray(nelem); int sum = 0; for (int i = 0; i < nelem; i++) { sum += array[i]; temp.array[i] = sum; } return temp; } /** * apply filter. convolute array with another array. This is 1-D image * processing. If filter has {@literal <}= 1 element, return this * unchanged. filter should have an odd number of elements. The * filter can be created with a IntArray constructor filter is moved along * stepwise * * @param filter * to apply normally smaller than this * @return filtered array */ public IntArray applyFilter(IntArray filter) { if (nelem == 0 || filter == null || filter.nelem <= 1) { return this; } int nfilter = filter.size(); int midfilter = (nfilter - 1) / 2; IntArray temp = new IntArray(nelem); int wt = 0; int sum = 0; for (int j = 0; j < midfilter; j++) { // get weight wt = 0; sum = 0; int l = 0; for (int k = midfilter - j; k < nfilter; k++) { wt += Math.abs(filter.array[k]); sum += filter.array[k] * this.array[l++]; } temp.array[j] = sum / wt; } wt = filter.absSumAllElements(); for (int j = midfilter; j < nelem - midfilter; j++) { sum = 0; int l = j - midfilter; for (int k = 0; k < nfilter; k++) { sum += filter.array[k] * this.array[l++]; } temp.array[j] = sum / wt; } for (int j = nelem - midfilter; j < nelem; j++) { // get weight wt = 0; sum = 0; int l = j - midfilter; for (int k = 0; k < midfilter + nelem - j; k++) { wt += Math.abs(filter.array[k]); sum += filter.array[k] * this.array[l++]; } temp.array[j] = sum / wt; } return temp; } /** * trims array to lie within limit. * * if flag == BELOW values below limit are set to limit. if flag == ABOVE * values above limit are set to limit. by repeated use of trim() values can * be constrained to lie within or outside a window does not modify this. * * @param flag * BELOW or ABOVE * @param limit * value to constrain * @return new array */ public IntArray trim(Trim flag, int limit) { IntArray temp = new IntArray(nelem); for (int i = 0; i < nelem; i++) { int v = array[i]; if ((flag == Trim.BELOW && v < limit) || (flag == Trim.ABOVE && v > limit)) v = limit; temp.array[i] = v; } return temp; } /** * index of largest element. * * @throws ArrayIndexOutOfBoundsException * array is zero length * @return index */ public int indexOfLargestElement() throws ArrayIndexOutOfBoundsException { if (nelem == 0) { throw new ArrayIndexOutOfBoundsException(); } int index = -1; int value = Integer.MIN_VALUE; for (int i = 0; i < nelem; i++) { if (array[i] > value) { value = array[i]; index = i; } } return index; } /** * index of smallest element. * * @throws ArrayIndexOutOfBoundsException * array is zero length * @return index */ public int indexOfSmallestElement() throws ArrayIndexOutOfBoundsException { if (nelem == 0) { throw new ArrayIndexOutOfBoundsException(); } int index = -1; int value = Integer.MAX_VALUE; for (int i = 0; i < nelem; i++) { if (array[i] < value) { value = array[i]; index = i; } } return index; } /** * value of largest element. * * @throws ArrayIndexOutOfBoundsException * array is zero length * @return value */ public int largestElement() throws ArrayIndexOutOfBoundsException { return array[indexOfLargestElement()]; } /** * value of largest element. synonym for largestElement(); * * @throws ArrayIndexOutOfBoundsException * array is zero length * @return value */ public int getMax() throws ArrayIndexOutOfBoundsException { return array[indexOfLargestElement()]; } /** * value of smallest element. * * @throws ArrayIndexOutOfBoundsException * array is zero length * @return index */ public int smallestElement() throws ArrayIndexOutOfBoundsException { return array[indexOfSmallestElement()]; } /** * value of smallest element. synonym for smallestElement(); * * @throws ArrayIndexOutOfBoundsException * array is zero length * @return value */ public int getMin() throws ArrayIndexOutOfBoundsException { return array[indexOfSmallestElement()]; } /** * range of array. * * @throws ArrayIndexOutOfBoundsException * array is zero length * @return (minValue, maxValue) */ public IntRange getRange() throws ArrayIndexOutOfBoundsException { IntRange r = null; if (nelem == 0) { throw new ArrayIndexOutOfBoundsException(); } r = new IntRange(); for (int i = 0; i < nelem; i++) { r.add(array[i]); } return r; } /** * as above (deprecated) */ /*-- public RealRange range() {return this.getRange();} --*/ /** * delete element and close up. modifies this. * * @param elem * to delete * @throws ArrayIndexOutOfBoundsException * elem out of range */ public void deleteElement(int elem) throws ArrayIndexOutOfBoundsException { if (elem < 0 || elem >= nelem) { throw new ArrayIndexOutOfBoundsException(); } nelem--; if (bufsize > nelem * 2) { bufsize /= 2; } int[] temp = new int[bufsize]; System.arraycopy(array, 0, temp, 0, elem); System.arraycopy(array, elem + 1, temp, elem, nelem - elem); array = temp; } /** * delete elements and close up. modifies this. * * @param low * lowest index inclusive * @param high * highest index inclusive * @throws ArrayIndexOutOfBoundsException * low or high out of range or low > high */ /** * delete elements and close up. modifies this. * * @param low * lowest index inclusive * @param high * highest index inclusive * @throws ArrayIndexOutOfBoundsException * low or high out of range or low {@literal >} high */ public void deleteElements(int low, int high) throws ArrayIndexOutOfBoundsException { if (low < 0 || low > high || high >= nelem) { throw new ArrayIndexOutOfBoundsException(); } int ndeleted = high - low + 1; int[] temp = new int[nelem - ndeleted]; System.arraycopy(array, 0, temp, 0, low); System.arraycopy(array, high + 1, temp, low, nelem - low - ndeleted); array = temp; nelem -= ndeleted; bufsize = nelem; int[] array = new int[nelem]; System.arraycopy(temp, 0, array, 0, nelem); } /** * insert element and expand. modifies this. * * @param elem * index of element to insert * @param f * value of element * @throws ArrayIndexOutOfBoundsException * elem out of range */ public void insertElementAt(int elem, int f) throws ArrayIndexOutOfBoundsException { if (elem < 0 || elem > nelem) { throw new ArrayIndexOutOfBoundsException(); } int[] array1 = new int[nelem + 1]; System.arraycopy(array, 0, array1, 0, elem); array1[elem] = f; System.arraycopy(array, elem, array1, elem + 1, nelem - elem); nelem++; array = array1; } /** * insert an array and expand. modifies this. * * @param elem * index of element to insert * @param f * value of element * @throws ArrayIndexOutOfBoundsException * elem out of range */ public void insertArray(int elem, IntArray f) throws ArrayIndexOutOfBoundsException { int n = f.size(); if (elem < 0 || elem >= nelem || n < 1) { throw new ArrayIndexOutOfBoundsException(); } nelem += n; int[] array1 = new int[nelem]; System.arraycopy(array, 0, array1, 0, elem); System.arraycopy(f.getArray(), 0, array1, elem, n); System.arraycopy(array, elem, array1, n + elem, nelem - elem - n); array = array1; } /** * append element. modifies this. * * @param f * element to append */ public void addElement(int f) { makeSpace(nelem + 1); array[nelem++] = f; } /** * append elements. modifies this. * * @param f * elements to append */ public void addArray(IntArray f) { makeSpace(nelem + f.nelem); System.arraycopy(f.array, 0, array, nelem, f.nelem); nelem += f.nelem; } /** * get reordered Array. reorder by index in IntSet new(i) = this(idx(i)) * does NOT modify array * * @param idx * array of indexes * @exception EuclidRuntimeException * an element of idx is outside range of this * @return array */ public IntArray getReorderedArray(IntSet idx) throws EuclidRuntimeException { IntArray temp = new IntArray(nelem); for (int i = 0; i < nelem; i++) { int index = idx.elementAt(i); if (index > nelem) { throw new EuclidRuntimeException(); } temp.array[i] = array[index]; } return temp; } /** * get elements within a range. * * @param r * within which element values must lie * @return indexes of conforming elements */ public IntSet inRange(IntRange r) { int n = size(); IntSet temp = new IntSet(); for (int i = 0; i < n; i++) { if (r.isValid() && r.includes(array[i])) { temp.addElement(i); } } return temp; } /** * get elements outside a range. * * @param r * outside which element values must lie * @return indexes of conforming elements */ public IntSet outOfRange(IntRange r) { int n = size(); IntSet temp = new IntSet(); for (int i = 0; i < n; i++) { if (r.isValid() && !r.includes(array[i])) { temp.addElement(i); } } return temp; } /** * returns values as strings. * * @return string values of elements */ public String[] getStringValues() { String[] temp = new String[nelem]; for (int i = 0; i < nelem; i++) { temp[i] = Integer.toString(array[i]); } return temp; } /** * gets values as string. * * @return element values seperated with spaces */ public String toString() { // don't change this routine!!! StringBuffer s = new StringBuffer(); s.append(S_LBRAK); for (int i = 0; i < nelem; i++) { if (i > 0) { s.append(S_COMMA); } s.append(array[i]); } s.append(S_RBRAK); return s.toString(); } /** * delete elements. utility routine. delete elements from java routine and * close up * * @param low * index inclusive * @param hi * index inclusive if hi >= float.length hi is reset to * float.length-1. */ static int[] deleteElements(int[] f, int low, int hi) { if (hi >= f.length) hi = f.length - 1; if (low < 0) low = 0; int ndel = hi - low + 1; if (ndel <= 0) return f; int[] temp = new int[f.length - ndel]; System.arraycopy(f, 0, temp, 0, low); System.arraycopy(f, hi + 1, temp, low, f.length - hi - 1); return temp; } /** * copy java array utility routine */ static int[] copy(int[] f) { int temp[] = new int[f.length]; System.arraycopy(f, 0, temp, 0, f.length); return temp; } /** * quick sort - modified from p96 - 97 (Hansen - C++ answer book) * * Scalar sort refers to sorting IntArray and IntArray (and similar classes) * where the objects themeselves are sorted. * * Index sort refers to sorting indexes (held as IntSet's) to the object and * getting the sorted object(s) with reorderBy(IntSet idx); * */ void xfswap(int[] x, int a, int b) { int tmp = x[a]; x[a] = x[b]; x[b] = tmp; } // scalar sort routines (internal) static final int CUTOFF = 16; private void inssort(int left, int right) { int k; for (int i = left + 1; i <= right; i++) { int v = array[i]; int j; for (j = i, k = j - 1; j > 0 && array[k] > v; j--, k--) { array[j] = array[k]; } array[j] = v; } } private int partition(int left, int right) { int mid = (left + right) / 2; if (array[left] > array[mid]) xfswap(array, left, mid); if (array[left] > array[right]) xfswap(array, left, right); if (array[mid] > array[right]) xfswap(array, mid, right); int j = right - 1; xfswap(array, mid, j); int i = left; int v = array[j]; do { do { i++; } while (array[i] < v); do { j--; } while (array[j] > v); xfswap(array, i, j); } while (i < j); xfswap(array, j, i); xfswap(array, i, right - 1); return i; } private void iqsort(int left, int right) { while (right - left > CUTOFF) { int i = partition(left, right); if (i - left > right - i) { iqsort(i + 1, right); right = i - 1; } else { iqsort(left, i - 1); left = i + 1; } } } /** * sorts array into ascending order. MODIFIES this */ public void sortAscending() { if (nelem <= 0) return; iqsort(0, nelem - 1); inssort(0, nelem - 1); } /** * sorts array into descending order. MODIFIES this */ public void sortDescending() { sortAscending(); reverse(); } /** * puts array into reverse order. MODIFIES this */ public void reverse() { int i = 0, j = nelem - 1; while (i < j) { xfswap(array, i, j); i++; j--; } } private static final int XXCUTOFF = 16; /** * get indexes of ascending sorted array. this array NOT MODIFIED * * @return indexes idx so that element(idx(0)) is lowest */ public IntSet indexSortAscending() { if (nelem <= 0) { return new IntSet(); } IntSet idx = new IntSet(nelem); IntArray iarray = new IntArray(idx.getElements()); xxiqsort(iarray, array, 0, nelem - 1); xxinssort(iarray, array, 0, nelem - 1); try { idx = new IntSet(iarray.getArray()); } catch (Exception e) { throw new EuclidRuntimeException(e.toString()); } return idx; } /** * get indexes of descending sorted array. this array NOT MODIFIED * * @return indexes idx so that element(idx(0)) is highest */ public IntSet indexSortDescending() { IntSet idx; idx = indexSortAscending(); int[] temp = new IntArray(idx.getElements()).getReverseArray(); try { idx = new IntSet(temp); } catch (Exception e) { throw new EuclidRuntimeException(e.toString()); } return idx; } private void xxinssort(IntArray iarr, int[] pfl, int left, int right) { int j, k; for (int i = left + 1; i <= right; i++) { int v = iarr.elementAt(i); for (j = i, k = j - 1; j > 0 && pfl[iarr.elementAt(k)] > pfl[v]; j--, k--) { iarr.setElementAt(j, iarr.elementAt(k)); } iarr.setElementAt(j, v); } } private int xxpartition(IntArray iarr, int[] pfl, int left, int right) { int mid = (left + right) / 2; if (pfl[iarr.elementAt(left)] > pfl[iarr.elementAt(mid)]) xxfswap(iarr, left, mid); if (pfl[iarr.elementAt(left)] > pfl[iarr.elementAt(right)]) xxfswap(iarr, left, right); if (pfl[iarr.elementAt(mid)] > pfl[iarr.elementAt(right)]) xxfswap(iarr, mid, right); int j = right - 1; xxfswap(iarr, mid, j); int i = left; int v = pfl[iarr.elementAt(j)]; do { do { i++; } while (pfl[iarr.elementAt(i)] < v); do { j--; } while (pfl[iarr.elementAt(j)] > v); xxfswap(iarr, i, j); } while (i < j); xxfswap(iarr, j, i); xxfswap(iarr, i, right - 1); return i; } private void xxiqsort(IntArray iarr, int[] pfl, int left, int right) { while (right - left > XXCUTOFF) { int i = xxpartition(iarr, pfl, left, right); if (i - left > right - i) { xxiqsort(iarr, pfl, i + 1, right); right = i - 1; } else { xxiqsort(iarr, pfl, left, i - 1); left = i + 1; } } } private void xxfswap(IntArray iarr, int a, int b) { int t = iarr.elementAt(a); iarr.setElementAt(a, iarr.elementAt(b)); iarr.setElementAt(b, t); } /** * parse string as integerArray. * * @param s * @param delimiterRegex * @return true if can be parsed. */ public static boolean isIntArray(String s, String delimiterRegex) { boolean couldBeIntArray = true; String[] ss = s.split(delimiterRegex); try { new IntArray(ss); } catch (NumberFormatException e) { couldBeIntArray = false; } return couldBeIntArray; } /** returns true if array is of form: i, i+delta, i+2*delta ... * * @param delta * @return */ public boolean isArithmeticProgression(int delta) { for (int i = 1; i < nelem; i++) { if (array[i] -array[i-1] != delta) { return false; } } return true; } /** if all values are equal returns value else null * * @return null if no values or unequal else value */ public Integer getConstant() { if (nelem == 0) return null; for (int i = 1; i < nelem; i++) { if (array[i] != array[0]) { return null; } } return array[0]; } public void decrementElementAt(int i) { if (i >= 0 && i < nelem) { array[i]--; } } public void incrementElementAt(int i) { if (i >= 0 && i < nelem) { array[i]++; } } public Iterator iterator() { return (array == null || array.length < nelem) ? null : new IntegerIterator(array, nelem); } public Set createIntegerSet() { Set integerSet = new HashSet(); for (int i = 0; i < array.length; i++) { integerSet.add(array[i]); } return integerSet; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + Arrays.hashCode(array); result = prime * result + nelem; return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; IntArray other = (IntArray) obj; if (!Arrays.equals(array, other.array)) return false; if (nelem != other.nelem) return false; return true; } } class IntegerIterator implements Iterator { private int counter; private int[] array; private int nelem; public IntegerIterator(int[] array, int nelem) { this.array = array; this.nelem = nelem; counter = -1; } public boolean hasNext() { return counter < nelem - 1; } public Integer next() { if (!hasNext()) return null; return array[++counter]; } public void remove() { throw new UnsupportedOperationException(); } } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/IntMatrix.java000066400000000000000000001372161461721410700246570ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid; import java.io.IOException; import java.io.Writer; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.List; import org.apache.log4j.Logger; /** * rectangular real number matrix class IntMatrix represents a rectangular m-x-n * matrix. The basic matrix algebra for non-square matrices is represented here * and this class is also a base for square matrices. *

* Read the signature of each member function carefully as some MODIFY the * object and some CREATE A NEW ONE. Among the reasons for this is that * subclassing (e.g to IntSquareMatrix) is easier with one of these forms in * certain cases. Note that if you modify an object, then all references to it * will refer to the changed object * * @author (C) P. Murray-Rust, 1996 */ public class IntMatrix implements EuclidConstants { final static Logger LOG = Logger.getLogger(IntMatrix.class); /** * number of rows */ protected int rows = 0; /** * number of columns */ protected int cols = 0; /** * the matrix */ protected int[][] flmat = new int[0][0]; DecimalFormat format = null; /** * construct default matrix. cols = rows = 0 */ public IntMatrix() { } /** * Create matrix with given rows and columns. A rows*cols matrix values set * to 0 (rows or cols {@literal <} 0 defaults to 0) * * @param r * number of rows * @param c * number of columns */ public IntMatrix(int r, int c) { if (r < 0) r = 0; if (c < 0) c = 0; rows = r; cols = c; flmat = new int[r][c]; } /** * Create from 1-D array. Formed by feeding in an existing 1-D array to a * rowsXcols matrix THE COLUMN IS THE FASTEST MOVING INDEX, that is the * matrix is filled as flmat(0,0), flmat(0,1) ... C-LIKE. COPIES the array * * @param rows * @param cols * @param array * @exception EuclidRuntimeException * size of array is not rows*cols */ public IntMatrix(int rows, int cols, int[] array) throws EuclidRuntimeException { this(rows, cols); check(rows, cols, array); this.rows = rows; this.cols = cols; int count = 0; for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { flmat[i][j] = array[count++]; } } } /** * creates matrix with initialised values. * * @param r * rows * @param c * columns * @param f * value to initialize with */ public IntMatrix(int r, int c, int f) { this(r, c); for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { flmat[i][j] = f; } } } /** * create from submatrix of another matrix. fails if lowrow {@literal >} hirow, lowrow {@literal <} * 0, etc * * COPIES the parts of m * * @param m * the matrix to slice * @param lowcol * lowest column index * @param hicol * highest column index * @param lowrow * lowest row index * @param hirow * highest row index * @exception EuclidRuntimeException * impossible value of hirow, hicol, lowrow, lowcol */ public IntMatrix(IntMatrix m, int lowrow, int hirow, int lowcol, int hicol) throws EuclidRuntimeException { this(hirow - lowrow + 1, hicol - lowcol + 1); if (hirow >= m.getRows() || lowrow < 0) { throw new EuclidRuntimeException("bad row index: " + lowrow + S_SLASH + hirow + " outside 0/" + m.getRows()); } if (hicol >= m.getCols() || lowcol < 0) { throw new EuclidRuntimeException("bad col index: " + lowcol + S_SLASH + hicol + " outside 0/" + m.getCols()); } for (int i = 0, mrow = lowrow; i < rows; i++, mrow++) { for (int j = 0, mcol = lowcol; j < cols; j++, mcol++) { flmat[i][j] = m.flmat[mrow][mcol]; } } } /** * copy constructor. copies matrix including values * * @param m * matrix to copy */ public IntMatrix(IntMatrix m) { this(m.rows, m.cols); for (int i = 0; i < rows; i++) { System.arraycopy(m.flmat[i], 0, flmat[i], 0, cols); } } /** create from list of rowvalues * * @param intListList * @return */ public static IntMatrix createByRows(List> intListList) { IntMatrix intMatrix = null; if (intListList != null) { int rows = intListList.size(); if (rows > 0) { List row0 =intListList.get(0); int cols = row0.size(); if (cols > 0) { intMatrix = new IntMatrix(rows, cols); for (int i = 0; i < rows; i++) { List rowi =intListList.get(i); if (rowi.size() == 0) { // skip } else if (rowi.size() == cols) { for (int j = 0; j < cols; j++) { intMatrix.flmat[i][j] = rowi.get(j); } } } } } } return intMatrix; } /** * shallow copy constructor. copies references (uses same internal array) * * @param m * matrix to copy */ public void shallowCopy(IntMatrix m) { this.rows = m.rows; this.cols = m.cols; this.flmat = m.flmat; } /** * constructs an IntMatrix. intm(i,j) = (int) this(i,j), i.e.gets nearest * integers as matrix. * * @return the nearest IntMatrix */ public IntMatrix getIntMatrix() { IntMatrix im = new IntMatrix(rows, cols); int[][] matrix = im.getMatrix(); for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { matrix[i][j] = (int) flmat[i][j]; } } return im; } /** * create from a java matrix. must be rectangular copies matrix values (i.e. * m can be discarded) * * @param m * natrix to copy from * @exception EuclidRuntimeException * m has rows of different lengths */ public IntMatrix(int[][] m) throws EuclidRuntimeException { this(m.length, m[0].length); for (int i = 0; i < rows; i++) { if (m[i].length != cols) { throw new EuclidRuntimeException("non-rectangular matrix cols: " + cols + " row: " + i + " length: " + m[i].length); } for (int j = 0; j < cols; j++) { flmat[i][j] = m[i][j]; } } } /** casts doubles to int. * * @param realMatrix */ public IntMatrix(RealMatrix realMatrix) { this(realMatrix.getRows(), realMatrix.getCols()); for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { flmat[i][j] = (int) realMatrix.flmat[i][j]; } } } /** * set output format. * * @param f * the format */ public void setFormat(DecimalFormat f) { format = f; } /** * get output format. * * @return the format */ public DecimalFormat getFormat() { return format; } /** * get number of rows. * * @return number of rows */ public int getRows() { return rows; } /** * get number of columns. * * @return number of columns */ public int getCols() { return cols; } /** * get matrix as java matrix. shallow copy - any alterations to java matrix * will alter this and vice versa. * * @return matrix as java matrix */ public int[][] getMatrix() { return flmat; } /** * get matrix as array. * * @return matrix as 1-D array in C order: (m(0,0), m(0,1) ...) */ public int[] getMatrixAsArray() { int[] temp = new int[rows * cols]; int count = 0; for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { temp[count++] = flmat[i][j]; } } return temp; } /** * tests matrices for equality. * * uses Int.isEqual(int) for tests * * @param m * @return true if all corresponding elements are equal */ public boolean isEqualTo(IntMatrix m) { boolean ok = true; try { checkConformable(m); for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { if (flmat[i][j] != m.flmat[i][j]) { ok = false; break; } } } } catch (EuclidRuntimeException e) { ok = false; } return ok; } // check that plus, subtract is possible private void checkConformable(IntMatrix m) throws EuclidRuntimeException { if (rows != m.rows || cols != m.cols) { throw new EuclidRuntimeException("unequal matrices"); } } // check that multiply is possible private void checkConformable2(IntMatrix m) throws EuclidRuntimeException { if (m.rows != this.cols) { throw new EuclidRuntimeException("unequal matrices (" + this.cols + ", " + m.rows + S_RBRAK); } } private void check(int rows, int cols, int[] array) throws EuclidRuntimeException { if (array == null) { throw new EuclidRuntimeException("IntMatrix(null)"); } if (array.length != rows * cols) { throw new EuclidRuntimeException("rows * cols (" + rows + S_STAR + cols + ") != array (" + array.length + S_RBRAK); } } /** * matrix addition. adds conformable matrices giving NEW matrix. this is * unaltered * * @param m2 matrix * @exception EuclidRuntimeException * m and this are different sizes * @return new matrix */ public IntMatrix plus(IntMatrix m2) throws EuclidRuntimeException { IntMatrix m = new IntMatrix(m2.rows, m2.cols); checkConformable(m2); for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { m.flmat[i][j] = flmat[i][j] + m2.flmat[i][j]; } } return m; } /** * matrix subtraction. subtracts conformable matrices giving NEW matrix this * is unaltered * * @param m2 * @exception EuclidRuntimeException * m and this are different sizes * @return new matrix */ public IntMatrix subtract(IntMatrix m2) throws EuclidRuntimeException { IntMatrix m = new IntMatrix(m2.rows, m2.cols); checkConformable(m2); for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { m.flmat[i][j] = flmat[i][j] - m2.flmat[i][j]; } } return m; } /** * unary minus. negate all elements of matrix; MODIFIES matrix */ public void negative() { for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { flmat[i][j] = -flmat[i][j]; } } } /** * matrix multiplication. * * multiplies conformable matrices to give NEW matrix. this is unaltered * result = 'this' * m; (order matters) * * @param m * @exception EuclidRuntimeException * m and this are different sizes * @return new matrix */ public IntMatrix multiply(IntMatrix m) throws EuclidRuntimeException { checkConformable2(m); IntMatrix m1 = new IntMatrix(rows, m.cols); for (int i = 0; i < rows; i++) { for (int j = 0; j < m.cols; j++) { m1.flmat[i][j] = 0; for (int k = 0; k < cols; k++) { m1.flmat[i][j] += flmat[i][k] * m.flmat[k][j]; } } } return m1; } /** * matrix multiplication by a scalar. creates this(i,j) = f*this(i,j) * MODIFIES matrix * * @param f * scalar */ public void multiplyBy(int f) { for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { flmat[i][j] *= f; } } } /** * matrix multiplication. multiplies conformable matrices and stores result * in this matrix. this = 'this' * m; * * @param m * matrix to multiply by * @exception EuclidRuntimeException * m and this are different sizes */ public void multiplyEquals(IntMatrix m) throws EuclidRuntimeException { IntMatrix mm = this.multiply(m); this.rows = mm.rows; this.cols = mm.cols; this.flmat = new int[this.rows][]; for (int i = 0; i < rows; i++) { this.flmat[i] = new int[this.cols]; System.arraycopy(mm.flmat[i], 0, this.flmat[i], 0, this.cols); } } /** * subtract value from each row. this[i,j] = this[i,j] - d[j] modifies this * * @param d * array of ints to subtract */ /*-- public void translateByRow(int[] d) { checkColumns(d); for (int i = rows - 1; i >= 0; -- i) { for (int j = cols - 1; j >= 0; -- j) { flmat [i] [j] -= d [j]; } } } --*/ /** * check. * * @param d * @throws EuclidRuntimeException */ /* private */void checkColumns(int[] d) throws EuclidRuntimeException { if (d.length != cols) { throw new EuclidRuntimeException("array size " + d.length + "!= cols length " + cols); } } private void checkRows(int[] d) throws EuclidRuntimeException { if (d.length != rows) { throw new EuclidRuntimeException("array size " + d.length + "!= rows length " + rows); } } /** * subtract value from each colum. this[i,j] = this[i,j] - d[i] modifies * this * * @param d * array of ints to subtract * @throws EuclidRuntimeException */ public void translateByColumn(int[] d) throws EuclidRuntimeException { checkRows(d); for (int i = cols - 1; i >= 0; --i) { for (int j = rows - 1; j >= 0; --j) { flmat[j][i] -= d[j]; } } } /** * matrix multiplication of a COLUMN vector. creates new vector * * @param f * vector to multiply * @exception EuclidRuntimeException * f.size() differs from cols * @return transformed array */ public IntArray multiply(IntArray f) throws EuclidRuntimeException { if (f.size() != this.cols) { throw new EuclidRuntimeException("unequal matrices"); } int[] temp = new int[rows]; int[] farray = f.getArray(); for (int i = 0; i < rows; i++) { temp[i] = 0; for (int j = 0; j < cols; j++) { temp[i] += this.flmat[i][j] * farray[j]; } } IntArray ff = new IntArray(temp); return ff; } /** * divide each column of a matrix by a vector of scalars (that is mat[i][j] = * mat[i][j] / vect[i] - MODIFIES matrix * * @param f * array to divide by * @exception EuclidRuntimeException * f.size() and rows differ */ public void columnwiseDivide(IntArray f) throws EuclidRuntimeException { if (this.cols != f.size()) { throw new EuclidRuntimeException("unequal matrices " + this.cols + S_SLASH + f.size()); } for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { this.flmat[i][j] /= f.elementAt(j); } } } /** * extracts a given element. * * @param row * @param col * @throws EuclidRuntimeException * bad value of row or column * @return the element at row,col */ public int elementAt(int row, int col) throws EuclidRuntimeException { checkRow(row); checkColumn(col); return flmat[row][col]; } /** * checks a row is in range. * * @throws EuclidRuntimeException * if it isn't */ private void checkRow(int row) throws EuclidRuntimeException { if (row < 0 || row >= rows) throw new EuclidRuntimeException("Bad value of row: " + row + S_SLASH + rows); } /** * checks a col is in range. * * @throws EuclidRuntimeException * if it isn't */ private void checkColumn(int col) throws EuclidRuntimeException { if (col < 0 || col >= cols) throw new EuclidRuntimeException("Bad value of col: " + col + S_SLASH + cols); } /** * extracts a given element. * * @param rowcol * represents row,col * @return the element at row,col * @throws EuclidRuntimeException */ public int elementAt(Int2 rowcol) throws EuclidRuntimeException { return elementAt(rowcol.elementAt(0), rowcol.elementAt(1)); } /** * sets a given element MODIFIES matrix * * @param row * @param col * @param f * @throws EuclidRuntimeException */ public void setElementAt(int row, int col, int f) throws EuclidRuntimeException { checkRow(row); checkColumn(col); flmat[row][col] = f; } /** * get value of largest element. * * @return value of largest element */ public int largestElement() { Int2 temp = indexOfLargestElement(); if (temp == null) { throw new EuclidRuntimeException("bug; null index for largest element"); } int d = this.elementAt(temp); return d; } /** * get index of largest element. * * @return (row, col) */ public Int2 indexOfLargestElement() { Int2 int2 = null; if (cols != 0 && rows != 0) { int f = Integer.MIN_VALUE; int im = 0; int jm = 0; for (int irow = 0; irow < rows; irow++) { for (int jcol = 0; jcol < cols; jcol++) { if (f < flmat[irow][jcol]) { f = flmat[irow][jcol]; im = irow; jm = jcol; } } } int2 = new Int2(im, jm); } return int2; } /** * get value of largest element in a column * * @param jcol * @throws EuclidRuntimeException * @return the value */ public int largestElementInColumn(int jcol) throws EuclidRuntimeException { return this.elementAt(indexOfLargestElementInColumn(jcol), jcol); } /** * get index of largest element in column. * * @param jcol * index * @return index (-1 if empty matrix) * @throws EuclidRuntimeException * bad value of jcol */ public int indexOfLargestElementInColumn(int jcol) throws EuclidRuntimeException { checkColumn(jcol); int imax = -1; int max = Integer.MIN_VALUE; for (int irow = 0; irow < rows; irow++) { if (max < flmat[irow][jcol]) { max = flmat[irow][jcol]; imax = irow; } } return imax; } /** * get index of largest element in row. * * @param irow * index * @return index (-1 if empty matrix) * @throws EuclidRuntimeException * bad value of irow */ public int indexOfLargestElementInRow(int irow) throws EuclidRuntimeException { checkRow(irow); int imax = -1; int max = Integer.MIN_VALUE; for (int jcol = 0; jcol < cols; jcol++) { if (max < flmat[irow][jcol]) { max = flmat[irow][jcol]; imax = jcol; } } return imax; } /** * get index of smallest element in column. * * @param jcol * index * @return index (-1 if empty matrix) * @throws EuclidRuntimeException * bad value of jcol */ public int indexOfSmallestElementInColumn(int jcol) throws EuclidRuntimeException { checkColumn(jcol); int imin = -1; int min = Integer.MAX_VALUE; for (int irow = 0; irow < rows; irow++) { if (min > flmat[irow][jcol]) { min = flmat[irow][jcol]; imin = irow; } } return imin; } protected boolean checkNonEmptyMatrix() { return (cols > 0 && rows > 0); } /** * get value of largest element in a row. * * @param irow * @return value (0 if no columns) * @throws EuclidRuntimeException */ public int largestElementInRow(int irow) throws EuclidRuntimeException { int idx = indexOfLargestElementInRow(irow); if (idx < 0) { throw new EuclidRuntimeException("empty matrix"); } return this.elementAt(irow, idx); } /** * get index of smallest element in row. * * @param irow * index * @return index (-1 if empty matrix) * @throws EuclidRuntimeException * bad value of irow */ public int indexOfSmallestElementInRow(int irow) throws EuclidRuntimeException { checkRow(irow); int imin = -1; int min = Integer.MAX_VALUE; for (int jcol = 0; jcol < cols; jcol++) { if (min > flmat[irow][jcol]) { min = flmat[irow][jcol]; imin = jcol; } } return imin; } /** * get value of smallest element. * * @return value * @throws EuclidRuntimeException */ public int smallestElement() throws EuclidRuntimeException { Int2 temp = indexOfSmallestElement(); return this.elementAt(temp); } /** * get index of smallest element. * * @return (row,col) or null for empty matrix */ public Int2 indexOfSmallestElement() { int f = Integer.MAX_VALUE; int im = -1; int jm = -1; for (int irow = 0; irow < rows; irow++) { for (int jcol = 0; jcol < cols; jcol++) { if (f > flmat[irow][jcol]) { f = flmat[irow][jcol]; im = irow; jm = jcol; } } } return (im >= 0) ? new Int2(im, jm) : null; } /** * get smallest element in a column. * * @param jcol * @return smallest value * @exception EuclidRuntimeException * bad value of jcol */ public int smallestElementInColumn(int jcol) throws EuclidRuntimeException { int idx = indexOfSmallestElementInColumn(jcol); if (idx < 0) { throw new EuclidRuntimeException("empty matrix"); } return this.elementAt(idx, jcol); } /** * get smallest element in a row. * * @param irow * @return smallest value * @exception EuclidRuntimeException * bad value of irow */ public int smallestElementInRow(int irow) throws EuclidRuntimeException { int idx = indexOfSmallestElementInRow(irow); if (idx < 0) { throw new EuclidRuntimeException("empty matrix"); } return this.elementAt(irow, idx); } /** * is matrix Orthogonal row-wise. * * that is row(i) * row(j) = 0 if i not equals j. * * @return true if orthogonal */ public boolean isOrthogonal() { for (int i = 1; i < rows; i++) { IntArray rowi = extractRowData(i); int dot = 0; for (int j = i + 1; j < rows; j++) { IntArray rowj = extractRowData(j); dot = rowi.dotProduct(rowj); if (dot != 0) return false; } } return true; } /** * get column data from matrix. * * @param col * the column * @return the column data (or length rows) * @throws EuclidRuntimeException */ public IntArray extractColumnData(int col) throws EuclidRuntimeException { checkColumn(col); IntArray fa = new IntArray(rows); for (int i = 0; i < rows; i++) { fa.setElementAt(i, this.flmat[i][col]); } return fa; } /** * get row data from matrix. * * @param row * the column * @return the column data (of length cols) */ public IntArray extractRowData(int row) { return new IntArray(flmat[row]); } /** * clear matrix. */ public void clearMatrix() { for (int irow = 0; irow < rows; irow++) { for (int jcol = 0; jcol < cols; jcol++) { flmat[irow][jcol] = 0; } } } /** * initialise matrix to given int. * * @param f */ public void setAllElements(int f) { for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { flmat[i][j] = f; } } } /** * transpose matrix - creates new Matrix * * @return transpose */ public IntMatrix getTranspose() { int[][] m = new int[cols][rows]; for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { m[j][i] = this.flmat[i][j]; } } return new IntMatrix(m); } /** * is the matrix square * * @return is square */ public boolean isSquare() { return (cols == rows && cols > 0); } /** * delete column from matrix and close up. no-op if impermissible value of * col * * @param col * the column */ public void deleteColumn(int col) { if (col >= 0 && col < cols) { int[][] temp = new int[rows][cols - 1]; for (int i = 0; i < rows; i++) { for (int j = 0; j < col; j++) { temp[i][j] = flmat[i][j]; } for (int j = col + 1; j < cols; j++) { temp[i][j - 1] = flmat[i][j]; } } cols--; flmat = temp; } } /** * delete 2 or more adjacent columns (inclusive) from matrix and close up. * no action if impermissible value of low and high * * @param low * start column * @param high * end column */ public void deleteColumns(int low, int high) { high = (high > cols - 1) ? cols - 1 : high; low = (low < 0) ? 0 : low; for (int i = 0; i < rows; i++) { this.flmat[i] = IntArray.deleteElements(this.flmat[i], low, high); } this.cols -= (high - low + 1); } /** * delete row from matrix and close up. * * @param row */ public void deleteRow(int row) { deleteRows(row, row); } /** * delete 2 or more adjacent rows (inclusive) from matrix and close up. if * (high {@literal >} rows-1 high -{@literal >} rows-1; or low {@literal <} 0, low -{@literal >} 0 * * @param low * start row * @param high * end row */ public void deleteRows(int low, int high) { high = (high >= rows) ? rows - 1 : high; low = (low < 0) ? 0 : low; if (low > high) return; int newrows = rows + high - low - 1; int temp[][] = new int[newrows][cols]; int oldrow = 0; int newrow = 0; while (oldrow < rows) { if (oldrow < low || oldrow > high) { temp[newrow++] = flmat[oldrow]; } oldrow++; } this.rows = newrows; flmat = temp; } /** * replace data in a single column. * * @param column * @param f * data must be of length rows * @throws EuclidRuntimeException */ public void replaceColumnData(int column, IntArray f) throws EuclidRuntimeException { checkRows(f); checkColumn(column); int[] temp = f.getArray(); for (int i = 0; i < rows; i++) { flmat[i][column] = temp[i]; } } private void checkRows(IntArray f) throws EuclidRuntimeException { if (f == null || f.size() != rows) { throw new EuclidRuntimeException("incompatible value of array size: " + f.size() + S_SLASH + rows); } } private void checkColumns(IntArray f) throws EuclidRuntimeException { if (f == null || f.size() != cols) { throw new EuclidRuntimeException("incompatible value of array size: " + f.size() + S_SLASH + cols); } } private void checkColumns(IntSet is) throws EuclidRuntimeException { if (is == null || is.size() != cols) { throw new EuclidRuntimeException("incompatible value of IntSet size: " + is.size() + S_SLASH + cols); } } private void checkColumns(IntMatrix m) throws EuclidRuntimeException { if (m == null || m.getCols() != cols) { throw new EuclidRuntimeException("incompatible value of matrix size: " + m.getCols() + S_SLASH + cols); } } private void checkRows(IntMatrix m) throws EuclidRuntimeException { if (m == null || m.getRows() != rows) { throw new EuclidRuntimeException("incompatible value of matrix size: " + m.getRows() + S_SLASH + rows); } } /** * replace data in a single column. * * @param starting_col * @param f * data must be of length rows * @throws EuclidRuntimeException */ public void replaceColumnData(int starting_col, int[] f) throws EuclidRuntimeException { replaceColumnData(starting_col, new IntArray(rows, f)); } /** * replace data in a block of columns. * * @param start_column * (gets overwritten) * @param m * must have same row count and fit into gap * @throws EuclidRuntimeException */ public void replaceColumnData(int start_column, IntMatrix m) throws EuclidRuntimeException { // must trap copying a matrix into itself! if (this == m) { return; } cols = this.getCols(); int mcols = m.getCols(); checkRows(m); if (start_column < 0) { throw new EuclidRuntimeException("cannot start at negative column: " + start_column); } int end_column = start_column + mcols; if (end_column > cols) { throw new EuclidRuntimeException("too many columns to copy: " + start_column + "|" + mcols + S_SLASH + cols); } copyColumns(m.flmat, start_column, mcols); } private void copyColumns(int[][] mat, int start_column, int nToCopy) { for (int j = 0; j < nToCopy; j++) { for (int i = 0; i < rows; i++) { this.flmat[i][start_column + j] = mat[i][j]; } } } /** * insert a hole into the matrix and expand. result is blank space in matrix * * @param after_col * @param delta_cols */ public void makeSpaceForNewColumns(int after_col, int delta_cols) { if (after_col >= 0 && after_col <= cols && delta_cols > 0) { int newcols = delta_cols + cols; IntMatrix temp = new IntMatrix(rows, newcols); for (int irow = 0; irow < rows; irow++) { for (int jcol = 0; jcol < after_col; jcol++) { temp.flmat[irow][jcol] = this.flmat[irow][jcol]; } for (int jcol = after_col; jcol < cols; jcol++) { temp.flmat[irow][jcol + delta_cols] = this.flmat[irow][jcol]; } } shallowCopy(temp); } } /** * add data as column or column block into matrix and expand. column is * inserted after given column * * @param after_col * -1 to cols-1 * @param f * @throws EuclidRuntimeException */ public void insertColumnData(int after_col, IntArray f) throws EuclidRuntimeException { checkRows(f); if (cols == 0) { rows = f.size(); flmat = new int[rows][1]; int[] arr = f.getArray(); cols = 1; for (int i = 0; i < rows; i++) { flmat[i][0] = arr[i]; } } else { if (f.size() == rows) { makeSpaceForNewColumns(after_col + 1, 1); replaceColumnData(after_col + 1, f); } } } /** * add data as column or column block into matrix and expand. * * @param afterCol * -1 to cols-1 * @param m * @throws EuclidRuntimeException */ public void insertColumnData(int afterCol, IntMatrix m) throws EuclidRuntimeException { // must trap copying a matrix into itself! if (this == m) { return; } checkRows(m); int mcols = m.getCols(); cols = this.getCols(); if (afterCol < -1 || afterCol >= cols) { throw new EuclidRuntimeException("afterCol must be >= -1 or < cols: " + afterCol); } makeSpaceForNewColumns(afterCol + 1, mcols); replaceColumnData(afterCol + 1, m); } /** * make space for new rows in matrix and expand. * * @param after_row * -1 to rows-1 * @param delta_rows * size of space */ public void insertRows(int after_row, int delta_rows) { if (after_row >= 0 && after_row <= cols && delta_rows > 0) { int newrows = delta_rows + rows; IntMatrix temp = new IntMatrix(newrows, cols); for (int jcol = 0; jcol < cols; jcol++) { for (int irow = 0; irow < after_row; irow++) { temp.flmat[irow][jcol] = this.flmat[irow][jcol]; } for (int irow = after_row; irow < rows; irow++) { temp.flmat[irow + delta_rows][jcol] = this.flmat[irow][jcol]; } } shallowCopy(temp); } } /** * overwrite existing row of data. * * @param row * to replace * @param f * row to use * @exception EuclidRuntimeException * f.size() and cols differ */ public void replaceRowData(int row, IntArray f) throws EuclidRuntimeException { checkColumns(f); int mcols = f.size(); System.arraycopy(f.getArray(), 0, flmat[row], 0, mcols); } /** * overwrite existing row of data. * * @param row * to replace * @param f * row to use * @exception EuclidRuntimeException * f.length and cols differ */ public void replaceRowData(int row, int[] f) throws EuclidRuntimeException { IntArray temp = new IntArray(cols, f); replaceRowData(row, temp); } /** * overwrite existing block of rows; if too big, copying is truncated * * @param afterRow * from -1 to rows-1 * @param m * data to replace with * @exception EuclidRuntimeException * m.rows and this.rows differ */ public void replaceRowData(int afterRow, IntMatrix m) throws EuclidRuntimeException { // must trap copying a matrix into itself! if (this == m) return; checkColumns(m); if (afterRow < -1) { throw new EuclidRuntimeException("afterRow must be >= -1 :" + afterRow); } if (!(afterRow <= (rows - m.rows))) { throw new EuclidRuntimeException("afterRow (" + afterRow + ")must be <= rows (" + rows + ") - m.rows (" + m.rows + S_RBRAK); } copyRowData(m.flmat, afterRow + 1, m.rows); } /** * insert 2 or more adjacent rows of data into matrix and expand * * @param afterRow * from -1 to rows-1 * @param m * data to insert * @exception EuclidRuntimeException * m.cols and this.colsdiffer */ public void insertRowData(int afterRow, IntMatrix m) throws EuclidRuntimeException { // must trap copying a matrix into itself! if (this == m) { return; } rows = this.getRows(); int mrows = m.getRows(); checkColumns(m); if (afterRow < -1) { throw new EuclidRuntimeException("must insert after -1 or higher"); } if (afterRow >= rows) { throw new EuclidRuntimeException("must insert after nrows-1 or lower"); } insertRows(afterRow + 1, mrows); copyRowData(m.flmat, afterRow + 1, mrows); } private void copyRowData(int[][] mat, int afterRow, int nrows) { for (int i = 0; i < nrows; i++) { for (int j = 0; j < cols; j++) { this.flmat[afterRow + i][j] = mat[i][j]; } } } /** * insert row of data into matrix and expand. * * @param after_row * from -1 to rows-1 * @param f * data to insert * @exception EuclidRuntimeException * f.size() and this.cols differ */ public void insertRowData(int after_row, IntArray f) throws EuclidRuntimeException { checkColumns(f); int mcols = f.size(); if (after_row >= -1 && after_row <= rows && mcols == cols) { insertRows(after_row + 1, 1); replaceRowData(after_row + 1, f); } else { throw new EuclidRuntimeException("Cannot add array after row" + after_row + S_SLASH + rows + "==" + mcols + S_SLASH + cols); } } /** * append data to matrix columnwise. * * @param f * data to append * @exception EuclidRuntimeException * f.size() and this.rows differ */ public void appendColumnData(IntArray f) throws EuclidRuntimeException { if (cols == 0) { rows = f.size(); } insertColumnData(cols - 1, f); } /** * append data to matrix columnwise. * * @param m data to append * @exception EuclidRuntimeException m.rows and this.rows differ */ public void appendColumnData(IntMatrix m) throws EuclidRuntimeException { if (cols == 0) { rows = m.getRows(); } insertColumnData(cols - 1, m); } /** * append data to matrix rowwise. * * @param f * data to append * @exception EuclidRuntimeException * m.cols and this.cols differ */ public void appendRowData(IntArray f) throws EuclidRuntimeException { if (rows == 0) { cols = f.size(); } insertRowData(rows - 1, f); } /** * append data to matrix rowwise. * * @param m * data to append * @exception EuclidRuntimeException * m.cols and this.cols differ */ public void appendRowData(IntMatrix m) throws EuclidRuntimeException { if (rows == 0) { cols = m.getCols(); } insertRowData(rows - 1, m); } /** * replaces the data in a submatrix. starts at (low_row, low_col) and * extends by the dimensions for the matrix m * * @param low_row * starting row * @param low_col * starting col * @param m * data to append */ public void replaceSubMatrixData(int low_row, int low_col, IntMatrix m) { if (this == m) return; if (low_row > 0 && low_col > 0) { int mrows = m.getRows(); int mcols = m.getCols(); if (low_row + mrows - 1 < rows && low_col + mcols - 1 < cols) { for (int i = 0; i < mrows; i++) { for (int j = 0; j < mcols; j++) { flmat[i + low_row - 1][j] = m.flmat[i][j]; } } } } } /** * reorder the columns of a matrix. * * @param is * indexes to reorder by * @exception EuclidRuntimeException * is.size() and this.cols differ * @return new matrix */ public IntMatrix reorderColumnsBy(IntSet is) throws EuclidRuntimeException { checkColumns(is); IntMatrix temp = new IntMatrix(rows, is.size()); for (int i = 0; i < is.size(); i++) { int icol = is.elementAt(i); if (icol >= cols || icol < 0) { throw new ArrayIndexOutOfBoundsException(); } IntArray coldat = this.extractColumnData(icol); temp.replaceColumnData(i, coldat); } return temp; } /** * reorder the rows of a matrix Deleting rows is allowed * * @param is * indexes to reprder by * @exception EuclidRuntimeException * is.size() and this.rows differ * @return matrix */ public IntMatrix reorderRowsBy(IntSet is) throws EuclidRuntimeException { if (is.size() != rows) { throw new EuclidRuntimeException("unequal matrices"); } IntMatrix temp = new IntMatrix(is.size(), cols); for (int i = 0; i < is.size(); i++) { int irow = is.elementAt(i); if (irow >= rows || irow < 0) { throw new EuclidRuntimeException("irow: " + irow); } IntArray rowdat = this.extractRowData(irow); temp.replaceRowData(i, rowdat); } return temp; } /** * extract a IntMatrix submatrix from a IntMatrix * * @param low_row * starting row * @param high_row * end row * @param low_col * starting col * @param high_col * end col * @exception EuclidRuntimeException * low/high_row/col are outside range of this * @return matrix */ public IntMatrix extractSubMatrixData(int low_row, int high_row, int low_col, int high_col) throws EuclidRuntimeException { return new IntMatrix(this, low_row, high_row, low_col, high_col); } /** * make an Int2_Array from columns. * * @param col1 * @param col2 * @throws EuclidRuntimeException * bad values of columns * @return 2*rows data */ public Int2Array extractColumns(int col1, int col2) throws EuclidRuntimeException { IntArray x = this.extractColumnData(col1); IntArray y = this.extractColumnData(col2); return new Int2Array(x, y); } /** * make an Int2_Array from rows. * * @param row1 * @param row2 * @throws EuclidRuntimeException * bad values of rows * @return 2*cols data */ public Int2Array extractRows(int row1, int row2) throws EuclidRuntimeException { IntArray x = this.extractRowData(row1); IntArray y = this.extractRowData(row2); return new Int2Array(x, y); } /** * produce a mask of those elements which fall in a range. result is matrix * with (1) else (0) * * @param r * the range * @throws EuclidRuntimeException * bad values of rows * @return matrix with 1s where data is in range else 0 */ public IntMatrix elementsInRange(IntRange r) throws EuclidRuntimeException { IntMatrix m = new IntMatrix(rows, cols); for (int irow = 0; irow < rows; irow++) { for (int jcol = 0; jcol < cols; jcol++) { int elem = 0; if (r.includes(elementAt(irow, jcol))) { elem = 1; } m.setElementAt(irow, jcol, elem); } } return m; } /** * output matrix - very crude * * @return the string */ public String toString() { StringBuffer sb = new StringBuffer(); // rows and cols if (rows > 0 && cols > 0) { sb.append(S_LCURLY); sb.append(rows); sb.append(S_COMMA); sb.append(cols); sb.append(S_RCURLY); } else { sb.append(S_LBRAK); } for (int i = 0; i < rows; i++) { sb.append(S_NEWLINE); sb.append(S_LBRAK); for (int j = 0; j < cols; j++) { if (j > 0) { sb.append(S_COMMA); } if (format == null) { sb.append(flmat[i][j]); } else { sb.append(format.format(flmat[i][j])); } } sb.append(S_RBRAK); } if (rows == 0 || cols == 0) { sb.append(S_RBRAK); } return sb.toString(); } /** * output xml as a CML matrix. * * @param w * the writer * @exception IOException */ public void writeXML(Writer w) throws IOException { StringBuffer sb = new StringBuffer(); sb.append(""); for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { if (i != 0 || j != 0) sb.append(S_SPACE); if (format == null) { sb.append(flmat[i][j]); } else { sb.append(format.format(flmat[i][j])); } } } sb.append(""); w.write(sb.toString()); } /** * should really be in IntMatrix */ public static List findLargestUniqueElementsInRowColumn(IntMatrix intMatrix) { List intList = new ArrayList(); for (int jcol = 0, max = intMatrix.getCols(); jcol < max; jcol++) { int irow = intMatrix.indexOfLargestElementInColumn(jcol); int maxval = intMatrix.elementAt(irow, jcol); if (maxval == -1) { irow = -1; } else { for (int ii = irow + 1, maxrow = intMatrix.getRows(); ii < maxrow; ii++) { int val = intMatrix.elementAt(ii, jcol); if (val >= maxval) { irow = -1; break; } } } intList.add(irow); } return intList; } } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/IntRange.java000066400000000000000000000274041461721410700244440ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.log4j.Level; import org.apache.log4j.Logger; /** * maximum and minimum values * * Contains two ints representing the minimum and maximum of an allowed or * observed range. *

* Default is range with low {@literal >} high; this can be regarded as the uninitialised * state. If points are added to a default IntRange it becomes initialised. * * @author (C) P. Murray-Rust, 1996 */ public class IntRange implements EuclidConstants, Comparable { private static final Logger LOG = Logger.getLogger(IntRange.class); static { LOG.setLevel(Level.DEBUG); } private final static Pattern CURLY_PATTERN1 = Pattern.compile("\\{([^,]+)\\}"); private final static Pattern CURLY_PATTERN2 = Pattern.compile("\\{([^,]+),([^,]+)\\}"); private final static String ANY = "*"; public final static IntRangeComparator ASCENDING_MIN_COMPARATOR = new IntRangeComparator(); /** * maximum of range */ protected int maxval; /** * minimum of range */ protected int minval; /** * creates invalid range from MAX_VALUE to MIN_VALUE */ public IntRange() { minval = Integer.MAX_VALUE; maxval = Integer.MIN_VALUE; } /** * initialise with min and max values; if minv {@literal >} maxv create inValid * IntRange * * @param minv * @param maxv */ public IntRange(int minv, int maxv) { maxval = maxv; minval = minv; if (minval > maxval) { minval = Integer.MAX_VALUE; maxval = Integer.MIN_VALUE; } } /** * copy constructor * * @param r */ public IntRange(IntRange r) { minval = r.minval; maxval = r.maxval; } /** * * @param r */ public IntRange(RealRange r) { minval = (int) Math.round(r.minval); maxval = (int) Math.round(r.maxval); } /** * a Range is only valid if its maxval is not less than its minval; this * tests for uninitialised ranges * * @return valid */ public boolean isValid() { return (minval <= maxval); } /** * invalid ranges return false * * @param r * @return equals * */ public boolean isEqualTo(IntRange r) { return (r != null && minval == r.minval && maxval == r.maxval && minval <= maxval); } @Override public boolean equals(Object o) { boolean equals = false; if (o != null && o instanceof IntRange) { IntRange ir =(IntRange) o; equals = this.minval == ir.minval && this.maxval == ir.maxval; } return equals; } @Override public int hashCode() { return 17*minval + 31*maxval; } /** * combine two ranges if both valid; takes greatest limits of both, else * returns InValid * * @param r2 * @return range */ public IntRange plus(IntRange r2) { if (!this.isValid()) { if (r2 == null || !r2.isValid()) { return new IntRange(); } return new IntRange(r2); } IntRange temp = new IntRange(); temp = new IntRange(Math.min(minval, r2.minval), Math.max(maxval, r2.maxval)); return temp; } public boolean intersectsWith(IntRange r2) { IntRange r = this.intersectionWith(r2); return r != null && r.isValid(); } /** * intersect two ranges and take the range common to both; return invalid * range if no overlap * * @param r2 * @return range */ public IntRange intersectionWith(IntRange r2) { if (!isValid() || r2 == null || !r2.isValid()) { return new IntRange(); } int minv = Math.max(minval, r2.minval); int maxv = Math.min(maxval, r2.maxval); return new IntRange(minv, maxv); } /** * get minimum value (MAX_VALUE if inValid) * * @return min */ public int getMin() { return minval; } /** * get maximum value (MIN_VALUE if inValid) * * @return max * */ public int getMax() { return maxval; } /** * get range (MIN_VALUE if invalid) * * @return range */ public int getRange() { if (!isValid()) return Integer.MIN_VALUE; return maxval - minval; } /** * does one range include another * * @param r2 * @return includes */ public boolean includes(IntRange r2) { return (r2 != null && r2.isValid() && this.includes(r2.getMin()) && this .includes(r2.getMax())); } /** * is a int within a IntRange * * @param f * @return includes If inValid, return false */ public boolean includes(int f) { return f >= minval && f <= maxval; } /** * synonym for includes() * * @param f * @return includes */ public boolean contains(int f) { return includes(f); } /** * add a value to a range * * @param x */ public void add(int x) { maxval = Math.max(maxval, x); minval = Math.min(minval, x); } /** * to string * * @return string */ public String toString() { return (minval > maxval) ? "NULL" : S_LBRAK + minval + S_COMMA + maxval + S_RBRAK; } /** comparees on min values * * @param intRange * @return */ public int compareTo(IntRange intRange) { if (intRange == null) { return -1; } else if (this.minval < intRange.minval) { return -1; } else if (this.minval > intRange.minval) { return 1; } else { if (this.maxval < intRange.maxval) { return -1; } else if (this.maxval > intRange.maxval) { return 1; } } return 0; } /** makes new IntRange extended by deltaMin and deltaMax. * * the effect is for positive numbers to increase the range. * if extensions are negative they are applied, but may result * in invalid range (this is not checked at this stage). *

* Does not alter this. *

* * @param minExtend subtracted from min * @param maxExtend added to max */ public IntRange getRangeExtendedBy(int minExtend, int maxExtend) { return new IntRange(minval - minExtend, maxval + maxExtend); } /** do ranges touch but not overlap? * * range from [a,b] inclusive touches [c,a-1] or [b+1,c] * * ([a,b] overlaps with [b,c]) * @param range * @return */ public boolean touches(IntRange range) { return range != null && (this.maxval + 1 == range.minval || range.maxval + 1 == this.minval); } /** mid point of range. * * @return */ public int getMidPoint() { return (minval + maxval)/2; } /** interprets a String as an IntRange. * * {m,n} is interpreted as IntRange(m,n) * {*,n} is interpreted as IntRange(any,n) * {m,*} is interpreted as IntRange(m,any) * {*,*} is interpreted as IntRange(any,any) * {m} is interpreted as IntRange(m,m) * {*} is interpreted as IntRange(any,any) * * @param token * @return null if cannot create a valid IntRange */ public static IntRange parseCurlyBracketString(String token) { IntRange intRange = null; if (token != null) { Integer min = null; Integer max = null; token = token.replaceAll("\\s+", ""); // strip spaces Matcher matcher = CURLY_PATTERN2.matcher(token); try { if (matcher.matches()) { String minS = matcher.group(1); String maxS = matcher.group(2); min = (ANY.equals(minS)) ? -Integer.MAX_VALUE : Integer.parseInt(minS); max = (ANY.equals(maxS)) ? Integer.MAX_VALUE : Integer.parseInt(maxS); } else { matcher = CURLY_PATTERN1.matcher(token); if (matcher.matches()) { String minS = matcher.group(1); min = (ANY.equals(minS)) ? -Integer.MAX_VALUE : Integer.parseInt(minS); max = min; } } intRange = new IntRange(min, max); } catch (Exception e) { LOG.error("Cannot parse range: "+token); } } return intRange; } /** creates a list of IntRanges from {...} syntax. * * uses parseCurlyBracketString() * * @param tokens * @return */ public static List createIntRangeList(List tokens) { List intRangeList = new ArrayList(); for (String token : tokens) { IntRange intRange = IntRange.parseCurlyBracketString(token); if (intRange == null) { throw new RuntimeException("Cannot parse ("+token+") as IntRange in : "+tokens); } intRangeList.add(intRange); } return intRangeList; } /** joins intRanges A and B when A.getMax() == B.getMin(). * Ranges must either "join" within a tolerance or be disjoint. * Overlapping ranges will throw an RuntimeException. * * (1,5), (5,10), (10,15) * creates * (1,15) * * (1,5), (5,10), (15,20), (20, 25) * creates * (1,10) (15, 25) * * (1,5), (15,20) * creates * (1,5) (15, 20) * * (1,5) (2,10) (5, 12) throws an Exception * * @param rangeList can be in any order, not altered * @param tolerance allows for overlap, i.e. (1,5) and (4,10) would "touch" , so would (1,5) and (6,10) * @return List of non-touching joined ranges, sorted by new R.getMin() */ public static List joinRanges(List rangeList, int tolerance) { if (rangeList.size() <= 1) { return rangeList; } List sortedList = new ArrayList(); for (IntRange range : rangeList) { sortedList.add(new IntRange(range)); // must copy to preserve original list unaltered } Collections.sort(sortedList, ASCENDING_MIN_COMPARATOR); List newList = new ArrayList(); IntRange currentRange = sortedList.get(0); newList.add(currentRange); for (int i = 1; i < sortedList.size(); i++) { IntRange range = sortedList.get(i); if (currentRange.canJoin(range, tolerance)) { currentRange.setMax(range.getMax()); } else { if (range.getMin() < currentRange.getMax()) { throw new RuntimeException("ranges overlap: "+currentRange+" / "+range); } currentRange = range; newList.add(currentRange); } } return newList; } /** can two ranges be joined at a common point? * (1,5) and (5,10) can be joined. * Because ranges are sometimes created from real numbers, a tolerance is allowed. * This should should normally not be set beyond 1 * (1,5) and (6,10) would join with tolerance=1, as would (1,5) and (4,10) * (1,5) and (3,10) would not be joinable with tolerance=1 * * @param range * @param tolerance * @return */ public boolean canJoin(IntRange range, int tolerance) { return Math.abs(this.maxval - range.minval) <= tolerance; } public void setMax(int max) { if (max >= this.minval) { this.maxval = max; } } public void setMin(int min) { if (min <= this.maxval) { this.minval = min; } } } class IntRangeComparator implements Comparator{ @Override public int compare(IntRange o1, IntRange o2) { if (o1 == null || o2 == null) return -1; return o1.getMin() - o2.getMin(); } } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/IntRangeArray.java000077500000000000000000000115311461721410700254400ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid; import java.io.PrintStream; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; /** holds an array of IntRanges * may or may not overlap or be sorted * @author pm286 * */ public class IntRangeArray implements Iterable { private static final PrintStream SYSOUT = System.out; private List rangeList; public IntRangeArray() { init(); } public IntRangeArray(List ranges) { init(); rangeList.addAll(ranges); } /** deep copy * * @param array */ public IntRangeArray(IntRangeArray array) { this(); for (IntRange range : array.rangeList) { this.add(new IntRange(range)); } } private void init() { rangeList = new ArrayList(); } public void add(IntRange range) { rangeList.add(range); } public void sort() { Collections.sort(rangeList); } public void sortAndRemoveOverlapping() { sort(); List newList = new ArrayList(); Iterator iterator = rangeList.iterator(); IntRange lastRange = null; while (iterator.hasNext()) { IntRange range = iterator.next(); if (lastRange == null) { newList.add(range); lastRange = range; } else { boolean intersects = lastRange.intersectsWith(range); if (intersects) { IntRange merged = lastRange.plus(range); newList.set(newList.size() - 1, merged); lastRange = merged; } else { newList.add(range); lastRange = range; } } } rangeList = newList; } @Override public boolean equals(Object obj) { if (obj == null || !(obj instanceof IntRangeArray)) { return false; } IntRangeArray array2 = (IntRangeArray) obj; if (this.size() != array2.size()) return false; for (int i = 0; i < this.size(); i++) { if (!this.get(i).equals(array2.get(i))) return false; } return true; } @Override public int hashCode() { int h = 17; for (int i = 0; i < rangeList.size(); i++) { h += rangeList.get(i).hashCode() * 31; } return h; } public int size() { return rangeList.size(); } public IntRange get(int serial) { return rangeList.get(serial); } public void debug() { for (IntRange range : rangeList) { SYSOUT.println(range); } } // public IntRangeArray intersectionWith(IntRangeArray array) { // IntRangeArray newArray = null; // if (array != null) { // IntRangeArray this2 = new IntRangeArray(this); // this2.sortAndRemoveOverlapping(); // Iterator thisIterator = this2.iterator(); // IntRangeArray array2 = new IntRangeArray(array); // array2.sortAndRemoveOverlapping(); // Iterator arrayIterator = array.iterator(); // Iterator currentIterator = thisIterator; // Iterator otherIterator = arrayIterator; // IntRange currentRange = currentIterator.hasNext() ? currentIterator.next() : null; // IntRange otherRange = otherIterator.hasNext() ? otherIterator.next() : null; // while (true) { // if (currentRange.i) // } // } // return newArray; // } public IntRangeArray plus(IntRangeArray array) { IntRangeArray newArray = null; if (array != null) { newArray = new IntRangeArray(); for (IntRange intRange : this.rangeList) { newArray.add(new IntRange(intRange)); } for (IntRange intRange : array.rangeList) { newArray.add(new IntRange(intRange)); } newArray.sortAndRemoveOverlapping(); } return newArray; } /** create array representing the gaps in this * gaps at ends are NOT filled * does not alter this * @return */ public IntRangeArray inverse() { IntRangeArray newArray = null; IntRangeArray copy = new IntRangeArray(this); copy.sortAndRemoveOverlapping(); if (copy.size() > 0) { newArray = new IntRangeArray(); IntRange last = null; for (IntRange current : copy) { if (last != null) { IntRange gap = new IntRange(last.maxval, current.minval); newArray.add(gap); } last = current; } } return newArray; } public Iterator iterator() { return rangeList.iterator(); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("["); for (IntRange range : rangeList) { sb.append(range.toString()); } sb.append("]"); return sb.toString(); } } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/IntSet.java000066400000000000000000000317731461721410700241470ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.log4j.Logger; /** * public class IntSet * * Stores a unique set of ints (that is cannot contain duplicate ints. The * limits can be set with setMin and setMax. There are operations for combining * sets (for example NOT, OR) and sets can be built up incrementally. *

* Inverse mapping. IntSets can be used to map one set of indexed data to * another, for example *

* RealArray x = someFunction();
* InstSet idx = x.indexSortAscending();
* for (int i = 0; i {@literal <}x.size(); i++) {
* y[i] = x[idx[i]];
}
*
To map the other way, x[i] = y[inv[i]]; the inverse IntSet * can be used * * @author (C) P. Murray-Rust, 1996 */ public class IntSet implements EuclidConstants { final static Logger LOG = Logger.getLogger(IntSet.class); private int array[]; private int nelem = 0; private int min = Integer.MIN_VALUE; private int max = Integer.MAX_VALUE; Set set; public static Map>> integerMap = new HashMap>>(); /** * constructor. */ public IntSet() { initialise(0, 0, true); } /** * creates an IntSet with the integers 0...nelem-1 * * @param nelem */ public IntSet(int nelem) { nelem = (nelem < 0) ? 0 : nelem; this.nelem = nelem; initialise(0, nelem, true); } /** * creates an IntSet with the integers start...end (if start {@literal <}= end) * * @param start * @param end * */ public IntSet(int start, int end) { nelem = end - start + 1; if (nelem <= 0) { nelem = 0; } initialise(start, nelem, true); } /** * copy constructor * * @param is */ public IntSet(IntSet is) { if (is != null) { array = is.array; System.arraycopy(is.array, 0, array, 0, nelem); nelem = is.nelem; min = is.min; max = is.max; this.set = new HashSet(); for (Integer ii : is.set) { this.set.add(ii); } } } /** * make from an int[] - all values must be distinct; * * @param is * @exception EuclidRuntimeException * values were not distinct */ public IntSet(int[] is) throws EuclidRuntimeException { nelem = is.length; initialise(0, nelem, false); int i = 0; for (int ii : is) { if (this.contains(ii)) { throw new EuclidRuntimeException("Duplicate value: " + i); } array[i++] = ii; set.add(ii); } } /** * use another IntSet to subscript this one that is I(new) = I(this) * subscripted by I(sub); Result has dimension of I(sub). If any of I(sub) * lies outside 0...this.size()-1, throw an error * * @param sub * @return the matrix * * @throws EuclidRuntimeException */ public IntSet getSubscriptedIntSet(IntSet sub) throws EuclidRuntimeException { IntSet is = new IntSet(sub.size()); for (int i = 0; i < sub.size(); i++) { int j = sub.elementAt(i); if (j < 0 || j >= this.nelem) { throw new EuclidRuntimeException("sub index (" + j + ") too large for " + this.toString()); } is.setElementAt(i, this.array[j]); } return is; } private void initialise(int start, int nelem, boolean addSet) { array = new int[nelem]; set = new HashSet(); int nToAdd = nelem /* - start */; for (int i = 0; i < nToAdd; i++) { array[i] = start + i; if (addSet) { set.add(array[i]); } } } /** * element-by-element comparison of sets * * @param is * @return equal */ public boolean isEqualTo(IntSet is) { for (int i = 0; i < nelem; i++) { if (array[i] != is.array[i]) return false; } return true; } /** * get elements. * * @return elements as array */ public int[] getElements() { // since the array may have spare space, contract if (nelem != array.length) { int[] temp = new int[nelem]; System.arraycopy(array, 0, temp, 0, nelem); array = temp; } return array; } /** * set maximum allowed value. if current set has elements greater than max * throws exception. * * @param max * @exception EuclidRuntimeException */ public void setMax(int max) throws EuclidRuntimeException { for (int i = 0; i < nelem; i++) { if (array[i] > max) { throw new EuclidRuntimeException("element in set (" + array[i] + ") greater than new max (" + max + S_RBRAK); } } this.max = max; } /** * set minimum allowed value. if current set has elements less than min * throws exception. * * @param min * @exception EuclidRuntimeException */ public void setMin(int min) { for (int i = 0; i < nelem; i++) { if (array[i] < min) { throw new EuclidRuntimeException("element in set (" + array[i] + ") less than new max (" + max + S_RBRAK); } } this.min = min; } /** * size of array. * * @return size */ public int size() { return nelem; } /** * add integer Fails if it is outside limits or already exists in set * * @param value * @return if successful * * @throws EuclidRuntimeException */ public boolean addElement(int value) throws EuclidRuntimeException { if (value < min || value > max) { throw new EuclidRuntimeException("value (" + value + ")outside range (" + min + "..." + max + S_RBRAK); } if (set.contains(value)) { throw new EuclidRuntimeException("value already in set: " + value); } if (nelem >= array.length) { int nbuff = (array.length == 0) ? 1 : array.length; while (nelem >= nbuff) { nbuff *= 2; } int temp[] = new int[nbuff]; for (int i = 0; i < nelem; i++) { temp[i] = array[i]; } array = temp; } array[nelem++] = value; set.add(value); return true; } /** * does set contain value. * * @param value * @return tur if contains */ public boolean contains(int value) { return set.contains(Integer.valueOf(value)); } /** * get element. * * @param i * @return element * @throws ArrayIndexOutOfBoundsException */ public int elementAt(int i) throws ArrayIndexOutOfBoundsException { if (i < 0 || i >= nelem) { throw new ArrayIndexOutOfBoundsException(); } return array[i]; } /** * gets the ints as an IntArray. * * @see #getElements() * @return the array */ public IntArray getIntArray() { int[] temp = new int[nelem]; System.arraycopy(array, 0, temp, 0, nelem); return new IntArray(temp); } /** * sort the IntSet; MODIFIES 'this' */ public void sortAscending() { IntArray temp = this.getIntArray(); temp.sortAscending(); for (int i = 0; i < nelem; i++) { array[i] = temp.array[i]; } } /** * concatenate sets. * * @param is * set to append * @throws EuclidRuntimeException * if there are elements in common or exceed max/min */ public void addSet(IntSet is) throws EuclidRuntimeException { for (int i = 0; i < is.nelem; i++) { int ii = is.elementAt(i); if (this.contains(ii)) { throw new EuclidRuntimeException("duplicate element " + ii); } this.addElement(ii); } } /** * intersect two sets (that is elements common to both) * * @param is * @return set * */ public IntSet intersectionWith(IntSet is) { IntSet ix = new IntSet(); for (int i = 0; i < is.nelem; i++) { int ii = is.elementAt(i); if (this.contains(ii)) { ix.addElement(ii); } } return ix; } /** * elements only in first set * * @param is * @return set */ public IntSet notIn(IntSet is) { IntSet ix = new IntSet(); for (int i = 0; i < this.nelem; i++) { int ii = this.elementAt(i); if (!is.contains(ii)) { ix.addElement(ii); } } return ix; } /** * add all values from an IntRange if range is 2,5 adds 2,3,4,5 * * @param ir */ public void addRange(IntRange ir) { if (ir == null) return; for (int i = ir.getMin(); i <= ir.getMax(); i++) { this.addElement(i); } } /** * Inverse mapping - see introduction if y[i] = x[this.elementAt(i)]; * then the result supports x[i] = y[inv.elementAt(i)]; * * @exception ArrayIndexOutOfBoundsException * the set must contain the integers 0...nelem-1 * @return inverse map */ public IntSet inverseMap() throws ArrayIndexOutOfBoundsException { IntSet temp = new IntSet(this.size()); for (int i = 0; i < size(); i++) { temp.setElementAt(this.elementAt(i), i); } return temp; } /** * private routine to set elements; check is made on the index, but not on * duplicate values */ void setElementAt(int i, int value) throws ArrayIndexOutOfBoundsException { if (i >= size()) { throw new ArrayIndexOutOfBoundsException(); } array[i] = value; } /** * debug. */ public void debug() { for (int i = 0; i < nelem; i++) { Util.print(S_SPACE + array[i]); } Util.println(); } /** * to string. * * @return string */ public String toString() { StringBuffer s = new StringBuffer(); s.append(S_LBRAK); for (int i = 0; i < nelem; i++) { if (i > 0) { s.append(S_COMMA); } s.append(array[i]); } s.append(S_RBRAK); return s.toString(); } private static List copy(List list) { List newList = new ArrayList(); for (Integer ii : list) { newList.add(ii); } return newList; } /** create all permutations of an integer. * runs from 0,1,... number-1 * @param number * @return */ public static List getPermutations(Integer number) { List intArrayList = new ArrayList(); List> intListList = IntSet.getPermutations0(number); for (List intList : intListList) { int size = intList.size(); int[] ints = new int[size]; for (int i = 0; i < size; i++) { ints[i] = intList.get(i).intValue(); } intArrayList.add(ints); } return intArrayList; } /** create all permutations of an integer. * runs from 0,1,... number-1 * @param number * @return */ private static List> getPermutations0(Integer number) { List> listList = IntSet.integerMap.get(number); if (listList == null) { listList = new ArrayList>(); if (number.equals(0)) { listList.add(new ArrayList()); } else { List> listListMinus = IntSet.getPermutations0(Integer.valueOf(number - 1)); for (List listMinus : listListMinus) { for (Integer ii = 0; ii < number; ii++) { List copyList = IntSet.copy(listMinus); copyList.add(ii.intValue(), number); listList.add(copyList); } } } IntSet.integerMap.put(number, listList); } return listList; } } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/IntSquareMatrix.java000066400000000000000000000265621461721410700260410ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid; import org.apache.log4j.Logger; /** * square matrix class * * IntSquareMatrix represents a square m-x-m matrix. The basic matrix algebra * for square matrices is represented here Check out the exciting member * functions, which are supported by Exceptions where appropriate. (NB. No * attempt has been made to provide numerical robustness and inversion, * diagonalisation, etc are as you find them.) *

* * @author (C) P. Murray-Rust, 1996 */ public class IntSquareMatrix extends IntMatrix { /** * helper class to provide types of matrix. */ /** type */ public enum Type { /** */ UPPER_TRIANGLE(1), /** */ LOWER_TRIANGLE(2), /** */ SYMMETRIC(3), /** */ DIAGONAL(4), /** */ OUTER_PRODUCT(5), /** */ UNKNOWN(6); /** integer value */ public int i; private Type(int i) { this.i = i; } } final static Logger LOG = Logger.getLogger(IntSquareMatrix.class); /** * Constructor. This gives a default matrix with cols = rows = 0. */ public IntSquareMatrix() { super(); } /** * Constructor. * * @param rows * number of rows and columns values are set to zero */ public IntSquareMatrix(int rows) { super(rows, rows); } /** * Creates square matrix from real matrix. * * @param f * real array (length rows) multiplied to give outer product * @return square matrix of size rows * rows */ public static IntSquareMatrix outerProduct(IntArray f) { int rows = f.size(); IntSquareMatrix temp = new IntSquareMatrix(rows); for (int i = 0; i < rows; i++) { for (int j = 0; j < rows; j++) { temp.flmat[i][j] = f.elementAt(i) * f.elementAt(j); } } return temp; } /** * create diagonal matrix from real matrix. * * @param f * real array (length rows) * @return square matrix with elem (i, i) = f(i), else 0 */ public static IntSquareMatrix diagonal(IntArray f) { int rows = f.size(); IntSquareMatrix temp = new IntSquareMatrix(rows); for (int i = 0; i < rows; i++) { temp.flmat[i][i] = f.elementAt(i); } return temp; } /** * Creates real square matrix from array THE COLUMN IS THE FASTEST MOVING * INDEX, that is the matrix is filled as mat(0,0), mat(0,1) ... C-LIKE * * @param rows * the final rows and cols of real square matrix * @param array * of size (rows * rows) * @exception EuclidRuntimeException * array size must be multiple of rows */ public IntSquareMatrix(int rows, int[] array) { super(rows, rows, array); } /** * Creates real square matrix with all elements initialized to int value. * * @param rows * size of square matrix * @param f * value of all elements */ public IntSquareMatrix(int rows, int f) { super(rows, rows, f); } /** * Constructor for submatrix of another matrix. * * @param m * matrix to slice (need not be square) * @param lowrow * the start row inclusive (count from 0) * @param lowcol * the start column inclusive (count from 0) * @param rows * size of final matrix * @throws EuclidRuntimeException */ public IntSquareMatrix(IntMatrix m, int lowrow, int lowcol, int rows) throws EuclidRuntimeException { super(m, lowrow, lowrow + rows - 1, lowcol, lowcol + rows - 1); } /** * copy constructor. * * @param m * matrix to copy */ public IntSquareMatrix(IntSquareMatrix m) { super(m); } /** * shallow copy from IntMatrix * * the array values are not copied (only the reference) * * @param m * matrix to copy reference from * * @exception EuclidRuntimeException * m must be square (that is cols = rows) */ public IntSquareMatrix(IntMatrix m) throws EuclidRuntimeException { super(m.rows, m.cols); if (m.cols != m.rows) { throw new EuclidRuntimeException("non square matrix"); } this.flmat = m.flmat; } /** * constructor from array. * * form from a Java 2-D array (it holds row and column count) * * @param matrix * to copy * @exception EuclidRuntimeException * matrix is not square (might even not be * rectangular!) */ public IntSquareMatrix(int[][] matrix) throws EuclidRuntimeException { super(matrix); if (cols != rows) { throw new EuclidRuntimeException("non square matrix"); } } /** * shallowCopy an existing square matrix. * * @param m * matrix to shallow copy * @exception EuclidRuntimeException * m must have the same number of rows and cols as * this */ public void shallowCopy(IntSquareMatrix m) throws EuclidRuntimeException { super.shallowCopy((IntMatrix) m); } /** * are two matrices identical * * @param r * matrix to compare * @return true if equal (see IntMatrix.equals for details) */ public boolean isEqualTo(IntSquareMatrix r) { return super.isEqualTo((IntMatrix) r); } /** * matrix addition. adds conformable matrices. Does NOT alter this. * * @param m * matrix to add * @exception EuclidRuntimeException * m must have the same number of rows and cols as * this * @return resultant matrix */ public IntSquareMatrix plus(IntSquareMatrix m) throws EuclidRuntimeException { IntMatrix temp = super.plus((IntMatrix) m); IntSquareMatrix sqm = new IntSquareMatrix(temp); return sqm; } /** * matrix subtraction. subtracts conformable matrices. Does NOT alter this. * * @param m * matrix to subtract from this * @exception EuclidRuntimeException * m must have the same number of rows and cols as * this * @return resultant matrix */ public IntSquareMatrix subtract(IntSquareMatrix m) throws EuclidRuntimeException { IntMatrix temp = super.subtract((IntMatrix) m); IntSquareMatrix sqm = new IntSquareMatrix(temp); return sqm; } /** * matrix multiplication. * * multiplies conformable matrices; result is this*m * * @param m * matrix to multiply by * @exception EuclidRuntimeException * m must have the same number of rows as this * has cols * @return new matrix */ public IntSquareMatrix multiply(IntSquareMatrix m) throws EuclidRuntimeException { IntMatrix temp = super.multiply((IntMatrix) m); IntSquareMatrix sqm = new IntSquareMatrix(temp); return sqm; } /** * trace. * * @return the trace */ public int trace() { int trace = 0; for (int i = 0; i < rows; i++) { trace += flmat[i][i]; } return trace; } /** * is it a unit matrix. * * @return are all diagonals 1 and off-diagonal zero (within Int.isEqual()) */ public boolean isUnit() { for (int i = 0; i < rows; i++) { for (int j = 0; j < rows; j++) { int f = flmat[i][j]; if ((f != 0 && (i != j)) || (f != 1 && (i == j))) { return false; } } } return true; } /** * is matrix symmetric. * * @return is Int.isEqual(elem(i,j), elem(j,i)) */ public boolean isSymmetric() { for (int i = 0; i < rows - 1; i++) { for (int j = i + 1; j < rows; j++) { if (flmat[i][j] != flmat[j][i]) { return false; } } } return true; } /** * is matrix UpperTriangular. * * @return true if all bottom triangle excluding diagona Int.isZero() */ public boolean isUpperTriangular() { for (int i = 1; i < rows; i++) { for (int j = 0; j < i; j++) { if (flmat[i][j] != 0) return false; } } return true; } /** * is matrix LowerTriangular. diagonal must also be zero * * @return true if all bottom triangle Int.isZero() */ public boolean isLowerTriangular() { for (int i = 0; i < rows - 1; i++) { for (int j = i + 1; j < rows; j++) { if (flmat[i][j] != 0) return false; } } return true; } /** * copy upper triangle into lower triangle. alters this to make it symmetric * * @return this as new square matrix */ public IntSquareMatrix copyUpperToLower() { for (int i = 0; i < cols - 1; i++) { for (int j = i + 1; j < cols; j++) { flmat[j][i] = flmat[i][j]; } } return this; } /** * copy lower triangle into upper triangle. alters this to make it symmetric * * @return this as new square matrix */ public IntSquareMatrix copyLowerToUpper() { for (int i = 0; i < cols - 1; i++) { for (int j = i + 1; j < cols; j++) { flmat[i][j] = flmat[j][i]; } } return this; } /** * copy lower triangle into linear array; order: 0,0; 1,0; 1,1; 2,0 * * @return linear array of size rows * (rows+1) / 2 */ public IntArray lowerTriangle() { int n = rows; IntArray triangle = new IntArray((n * (n + 1)) / 2); int count = 0; for (int i = 0; i < rows; i++) { for (int j = 0; j <= i; j++) { triangle.setElementAt(count++, flmat[i][j]); } } return triangle; } /** * transpose. MODIFIES this */ public void transpose() { for (int i = 0; i < rows; i++) { for (int j = 0; j < i; j++) { int t = flmat[i][j]; flmat[i][j] = flmat[j][i]; flmat[j][i] = t; } } } } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/JodaDate.java000066400000000000000000000163041461721410700244050ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid; import java.util.Date; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import org.joda.time.MutableDateTime; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; import org.joda.time.format.ISODateTimeFormat; /** * really just to remember at this stage * @author pm286 * The pattern syntax is mostly compatible with java.text.SimpleDateFormat - time zone names cannot be parsed and a few more symbols are supported. All ASCII letters are reserved as pattern letters, which are defined as follows: Symbol Meaning Presentation Examples ------ ------- ------------ ------- G era text AD C century of era ({@literal >}=0) number 20 Y year of era ({@literal >}=0) year 1996 x weekyear year 1996 w week of weekyear number 27 e day of week number 2 E day of week text Tuesday; Tue y year year 1996 D day of year number 189 M month of year month July; Jul; 07 d day of month number 10 a halfday of day text PM K hour of halfday (0~11) number 0 h clockhour of halfday (1~12) number 12 H hour of day (0~23) number 0 k clockhour of day (1~24) number 24 m minute of hour number 30 s second of minute number 55 S fraction of second number 978 z time zone text Pacific Standard Time; PST Z time zone offset/id zone -0800; -08:00; America/Los_Angeles ' escape for text delimiter '' single quote literal ' The count of pattern letters determine the format. Text: If the number of pattern letters is 4 or more, the full form is used; otherwise a short or abbreviated form is used if available. Number: The minimum number of digits. Shorter numbers are zero-padded to this amount. Year: Numeric presentation for year and weekyear fields are handled specially. For example, if the count of 'y' is 2, the year will be displayed as the zero-based year of the century, which is two digits. Month: 3 or over, use text, otherwise use number. Zone: 'Z' outputs offset without a colon, 'ZZ' outputs the offset with a colon, 'ZZZ' or more outputs the zone id. Zone names: Time zone names ('z') cannot be parsed. Any characters in the pattern that are not in the ranges of ['a'..'z'] and ['A'..'Z'] will be treated as quoted text. For instance, characters like ':', '.', ' ', '#' and '?' will appear in the resulting time text even they are not embraced within single quotes. DateTimeFormat is thread-safe and immutable, and the formatters it returns are as well. * */ public class JodaDate { // from W3C 2000-01-12T12:13:14Z // Wed Jul 8 10:53:24 PDT 1998 private static final String ALPHA_ZONE_PATTERN = "EEE MMM d HH:mm:ss ZZZ yyyy"; private static final DateTimeFormatter ALPHA_ZONE_FORMATTER = DateTimeFormat.forPattern(ALPHA_ZONE_PATTERN); // Wed Jul 8 10:53:24 1998 private static final String ALPHA_PATTERN1 = "EEE MMM d HH:mm:ss yyyy"; private static final String ALPHA_PATTERN2 = "EEE MMM dd HH:mm:ss yyyy"; private static final DateTimeFormatter ALPHA_FORMATTER1 = DateTimeFormat.forPattern(ALPHA_PATTERN1); private static final DateTimeFormatter ALPHA_FORMATTER2 = DateTimeFormat.forPattern(ALPHA_PATTERN2); private static final String DATETIME_PATTERN = "yyyy-MM-dd'T'HH:mm:ssZZ"; private static final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormat.forPattern(DATETIME_PATTERN); private static final String ZULU_DATETIME_PATTERN = "yyyy-MM-dd'T'HH:mm:ss'Z"; private static final DateTimeFormatter ZULU_FORMATTER = DateTimeFormat.forPattern(ZULU_DATETIME_PATTERN); public static String formatDate(DateTime datetime) { if (DateTimeZone.UTC.equals(datetime.getZone())) { return ZULU_FORMATTER.print(datetime); } else { return DATETIME_FORMATTER.print(datetime); } } public static String formatIsoDate(DateTime datetime) { return ISODateTimeFormat.dateTime().print(datetime); } public static DateTime parseDate(String s) { if (s.endsWith("Z")) { MutableDateTime dateTime = ZULU_FORMATTER.parseMutableDateTime(s); dateTime.setZone(DateTimeZone.UTC); return dateTime.toDateTime(); } else if (Character.isLetter(s.charAt(0))){ DateTime dateTime = JodaDate.parseQuietly(ALPHA_FORMATTER2, s); if (dateTime == null) { dateTime = JodaDate.parseQuietly(ALPHA_FORMATTER1, s); } return dateTime; } else { return DATETIME_FORMATTER.parseDateTime(s); } } /** return null if fails * * @param formatter * @param s * @return */ public static DateTime parseQuietly(DateTimeFormatter formatter, String s) { DateTime dateTime = null; if (formatter != null) { try { dateTime = formatter.parseDateTime(s); } catch (Exception e) { // } } return dateTime; } public static DateTime parseDate(String date, String format) { DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(format); if (format.endsWith("Z")) { } else { dateTimeFormatter = dateTimeFormatter.withZone(DateTimeZone.forID("UTC")); } DateTime dateTime = dateTimeFormatter.parseDateTime(date); return dateTime.withZone(DateTimeZone.forID("UTC")); } public static DateTime parseJavaDate(Date javaDate) { long seconds = javaDate.getTime(); return new DateTime(seconds); } @SuppressWarnings("deprecation") public static Date parseJodaDate(DateTime jodaDate) { int year = jodaDate.getYear(); int month = jodaDate.getMonthOfYear(); int day = jodaDate.getDayOfMonth(); int hour = jodaDate.getHourOfDay(); int min = jodaDate.getMinuteOfDay(); int sec = jodaDate.getSecondOfMinute(); // arghh Date date = new Date(year-1900, month, day, hour, min, sec); return date; } } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/Line2.java000066400000000000000000000307271461721410700237100ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid; import org.apache.log4j.Logger; /** line * determined by one point (R) and a vector (V) * this gives L = R + nV * can assume that R and R+V are the two "ends of the line" * the semantics of this are application-dependent * @author pm286 * */ public class Line2 implements EuclidConstants { private static Logger LOG = Logger.getLogger(Line2.class); public final static Line2 XAXIS = new Line2(new Real2(0.0, 0.0), new Real2(1.0, 0.0)); public final static Line2 YAXIS = new Line2(new Real2(0.0, 0.0), new Real2(0.0, 1.0)); private Real2 from; private Real2 to; private Vector2 vector; private Vector2 unitVector = null; // lazy evaluation private double slope = Double.NaN; private double c = Double.NaN; private double xint = Double.NaN; /** * generates vector * @param from * @param to */ public Line2(Real2 from, Real2 to) { this.from = new Real2(from); this.to = new Real2(to); createVector(); init(); } private void createVector() { vector = new Vector2(to.subtract(from)); if (vector.getLength() < Real.EPS) { LOG.trace("line has coincident points: "+from+" ... "+to); } } private void init() { slope = Double.NaN; c = Double.NaN; xint = Double.NaN; } /** * generates to * @param from * @param v */ public Line2(Real2 from, Vector2 v) { if (v.getLength() < Real.EPS) { throw new EuclidRuntimeException("Cannot form line from coincident points"); } this.from = new Real2(from); this.vector = new Vector2(v); to = from.plus(v); } /** get slope. * "m" in y=m*x+c * if x component is zero returns Double.*_INFINITY; * @return slope, Double.POSITIVE_INFINITY or Double.NEGATIVE_INFINITY; */ public double getSlope() { if (Double.isNaN(slope)) { try { slope = vector.getY() / vector.getX(); } catch (ArithmeticException ae) { slope = (vector.getY() > 0) ? Double.POSITIVE_INFINITY : Double.NEGATIVE_INFINITY; } } return slope; } /** * * "c" in y=m*x+c * @return intercept or Double.NaN if slope is infinite */ public double getYIntercept() { if (Double.isNaN(c)) { getSlope(); if (!Double.isNaN(slope) && slope < Double.POSITIVE_INFINITY && slope > Double.NEGATIVE_INFINITY ) { c = from.getY() - from.getX() * slope; } } return c; } /** * "c" in y=m*x+c * @return intercept or Double.NaN if slope is infinite */ public double getXIntercept() { if (Double.isNaN(xint)) { getYIntercept(); if (Double.isNaN(slope) || Double.compare(slope, Double.NEGATIVE_INFINITY) == 0 || Double.compare(slope, Double.POSITIVE_INFINITY) == 0 ) { xint = from.getX(); } else if(Math.abs(slope) > Real.EPS) { xint = - c / slope; } } return xint; } /** get intersection of two lines * see softSurfer algorithm * @param line1 * @return null if parallel or antiparallel */ public Real2 getIntersection(Line2 line1) { Real2 inter = null; if (this.from.getDistance(line1.from) < Real.EPS) { inter = this.from; } else { double perpv = this.vector.getPerpProduct(line1.vector); Vector2 w = new Vector2(this.from.subtract(line1.from)); double perpw = line1.vector.getPerpProduct(w); // this = point + lambda * vector; double lambda = perpw / perpv; Real2 vv = vector.multiplyBy(lambda); inter = this.from.plus(vv); } return inter; } /** does a line contain a point. * line is of zero thickness * * @param point * @param eps distance within which point muct approach line * @param allowExtension if true allow point to be "outside" line * segment * @return true if within eps of line */ public boolean contains(Real2 point, double eps, boolean allowExtension) { boolean contains = false; if (point != null) { double dist = Math.abs(this.getDistanceFromPoint(point)); if (dist < eps) { double length = this.getLength() + eps; contains = allowExtension || (point.getDistance(from) < length && point.getDistance(to) < length); } } return contains; } /** swaps to and from coordinates. * */ public void flipCoordinates() { Real2 temp = from; from = to; to = temp; } /** * get unit vector convenience * @return vector */ public Vector2 getUnitVector() { if (unitVector == null) { unitVector = new Vector2(vector.getUnitVector()); } return unitVector; } /** signed perpendicular distance from point to infinite line. * @param point * @return distance * @deprecated use new name (unsignedDistanceFromPoint) */ public double getDistanceFromPoint(Real2 point) { //FIXME for lines parallel to axis getUnitVector(); LOG.trace(unitVector); Vector2 w = new Vector2(point.subtract(from)); LOG.trace(w); return unitVector.getPerpProduct(w); } /** signed perpendicular distance from point to infinite line. * * will depend on direction of line. * * @param point * @return distance */ public double getSignedDistanceFromPoint(Real2 point) { getUnitVector(); LOG.trace(unitVector); Vector2 w = new Vector2(point.subtract(from)); LOG.trace(w); return unitVector.getPerpProduct(w); } /** perpendicular distance from point to infinite line. * @param point * @return distance */ public double getUnsignedDistanceFromPoint(Real2 point) { Real2 pb = getNearestPointOnLine(point); return pb.getDistance(point); } /** may be redundant... * * @param point * @return */ public Real2 getNearestPointNew(Real2 point) { /** dist_Point_to_Line( Point P, Line L) { Vector v = L.P1 - L.P0; Vector w = P - L.P0; double c1 = dot(w,v); double c2 = dot(v,v); double b = c1 / c2; Point Pb = L.P0 + b * v; return d(P, Pb); } */ Vector2 v = new Vector2(this.getXY(1).subtract(this.getXY(0))); Vector2 w = new Vector2(point.subtract(this.getXY(0))); double c1 = w.dotProduct(v); double c2 = v.dotProduct(v); double b = c1 / c2; Real2 pb = this.getXY(0).plus(v.multiplyBy(b)); return pb; } /** get nearest point on infinite line. * @param point * @return distance */ public Real2 getNearestPointOnLine(Real2 point) { getUnitVector(); Vector2 lp = new Vector2(point.subtract(this.from)); double lambda = unitVector.dotProduct(lp); Real2 vv = unitVector.multiplyBy(lambda); return from.plus(vv); } /** are two lines parallel within tolerance. * * @param line * @param eps maximum allowed angle between lines * @return null if any arguments null */ public Boolean isParallelTo(Line2 line, Angle eps) { Boolean parallel = null; if (line != null && eps != null) { Angle angle = getAngleMadeWith(line); angle.normalizeToPlusMinusPI(); parallel = Math.abs(angle.getRadian()) < Math.abs(eps.getRadian()); } return parallel; } /** * @param line * @param eps maximum allowed angle between unsigned lines (i.e. {@literal <}{@literal <} Math.PI/2) * @return null if any arguments null */ public boolean isAntiParallelTo(Line2 line, Angle eps) { Boolean antiParallel = null; if (line != null && eps != null) { Angle angle = getAngleMadeWith(line); angle.normalizeTo2Pi(); antiParallel = Math.abs(Math.abs(angle.getRadian()) - Math.PI) < Math.abs(eps.getRadian()); } return antiParallel; } /** are unsigned lines parallel. * * @param line * @param eps * @return isParallel() or isAntiParallel; null if line or eps is null */ public Boolean isParallelOrAntiParallelTo(Line2 line, Angle eps) { Boolean para = null; if (line != null && eps != null) { para = this.isParallelTo(line, eps) || this.isAntiParallelTo(line, eps); } return para; } /** calculated unsigned distance between parallel lines. * *

uses distance from this.getXY(0) to nearest point on line.

* *

if lines are not exactly parallel the result has no absolute meaning but is heuristically useful.

* * @param line * @param eps * @return null if args are null or lines are not parallel */ public Double calculateUnsignedDistanceBetweenLines(Line2 line, Angle eps) { Double d = null; if (this.isParallelOrAntiParallelTo(line, eps)) { Real2 p = line.getNearestPointOnLine(this.getXY(0)); d = this.getXY(0).getDistance(p); } return d; } /** convenience method. * gets angle formed between lines using * Vector2.getAngleMadeWith(Vector2) * @param line * @return angle or null */ public Angle getAngleMadeWith(Line2 line) { Angle angle = null; if (line != null) { angle = this.getVector().getAngleMadeWith(line.getVector()); } return angle; } public Boolean isPerpendicularTo(Line2 line, Angle angleEps) { if (line == null || angleEps == null) return null; Angle angle = this.getAngleMadeWith(line); return Math.abs(Math.abs(angle.getRadian()) - Math.PI * 0.5) < angleEps.getRadian(); } /** gets multiplier of point from "from" * finds nearest point (pp) on line (so avoids rounding errors) * then finds pp = from + vector * lambda * if pp is within segment , lambda is 0, 1 * @param p * @return lambda */ public double getLambda(Real2 p) { Real2 near = this.getNearestPointOnLine(p); Real2 delta = near.subtract(from); double lambda = (Math.abs(vector.getX()) > Math.abs(vector.getY())) ? delta.getX() / vector.getX() : delta.getY() / vector.getY(); return lambda; } /** get mid point * @return mid point */ public Real2 getMidPoint() { Real2 mm = this.from.plus(this.to); return mm.multiplyBy(0.5); } /** get length * @return length */ public double getLength() { return vector.getLength(); } /** * @return the from */ public Real2 getFrom() { return from; } /** * @return the to */ public Real2 getTo() { return to; } /** get point at either end. * * @param i (0/from or 1/to) * @return */ public Real2 getXY(int i) { Real2 xy = null; if (i == 0) { xy = from; } else if (i == 1) { xy = to; } else { throw new EuclidRuntimeException("Bad point in Line2 "+i); } return xy; } /** set point at either end. * * @param i (0/from or 1/to) */ public void setXY(Real2 xy, int i) { if (i == 0) { from = new Real2(xy); } else if (i == 1) { to = new Real2(xy); } else { throw new EuclidRuntimeException("Bad point in Line2 "+i); } createVector(); } /** * @return the vector */ public Vector2 getVector() { return vector; } /** creates point at (signed) distance dist from "from" point * * newPoint = from + (dist / line.length) * vector * @param dist * @return new Point * */ public Real2 createPointOnLine(Double dist) { double length = this.getLength(); double multiplier = dist / length; Real2 newVector = vector.multiplyBy(multiplier); Real2 newPoint = new Real2(from); newPoint.plusEquals(newVector); return newPoint; } /** creates point at (signed) distance dist from index point * * vector = xy(1-index) {@literal <}- xy(index) * newPoint = xy(index) + (dist / line.length) * vector * @param dist * @param index * @return new Point */ public Real2 createPointOnLine(Double dist, int index) { double length = this.getLength(); double multiplier = dist / length; Real2 newVector = vector.multiplyBy(multiplier); if (index == 1){ newVector.negative(); } Real2 newPoint = getXY(index).plus(newVector); return newPoint; } /** gets serial number of point in line specification * if point is within EPS of "from" returns 0 * if point is within EPS of "to" returns 1 * else returns -1 */ public int getSerial(Real2 point, double eps) { if (from.getDistance(point) < eps) { return 0; } if (to != null && to.getDistance(point) < eps) { return 1; } return -1; } /** * @return string */ public String toString() { return "line: from("+from+") to("+to+") v("+vector+")"; } public boolean isHorizontal(Angle eps) { return this.isParallelOrAntiParallelTo(XAXIS, eps); } public boolean isVertical(Angle eps) { return this.isParallelOrAntiParallelTo(YAXIS, eps); } } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/Line2AndReal2Calculator.java000066400000000000000000000033301461721410700272210ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid; public class Line2AndReal2Calculator { public double distanceOfProjectionFromEnd1; public double distanceOfProjectionFromEnd2; public double minimumDistance; public boolean offEnd1; public boolean offEnd2; public Line2AndReal2Calculator(Line2 line, Real2 point) { Real2 point1 = line.getXY(0); Real2 point2 = line.getXY(1); Real2 proj = line.getNearestPointOnLine(point); /*double dist = arrowLine.getUnsignedDistanceFromPoint(corners[j]); if (dist < minDistanceToLine) { minDistanceToLine = dist; }*/ distanceOfProjectionFromEnd1 = proj.getDistance(point1); distanceOfProjectionFromEnd2 = proj.getDistance(point2); if (distanceOfProjectionFromEnd1 > line.getLength() && distanceOfProjectionFromEnd1 > distanceOfProjectionFromEnd2) { minimumDistance = point.getDistance(point2); offEnd2 = true; } else if (distanceOfProjectionFromEnd2 > line.getLength() && distanceOfProjectionFromEnd2 > distanceOfProjectionFromEnd1) { minimumDistance = point.getDistance(point1); offEnd1 = true; } else { minimumDistance = point.getDistance(proj); } } }euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/Line3.java000066400000000000000000000163721461721410700237110ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid; /** * 3-dimensional line class * * Line3 represents a 3-dimensional line It is one of a set of primitives which * can be combined to create and manipulate complex 3-dimensional objects. Lines * can be transformed with rotation matrices or rotation-translation matrices * (Transform3), can be calculated from other primitives or can be used to * generate other primitives. * * * A line is a vector which is located in space. It is described by a unit * vector (vector) and a point (p) on the line. Any point on the line can be used for * p, and p could change during the existence of a calculation without affecting * the integrity of the line, for example p = {1,1,1}, vector = {1,0,0} is the same * line as p = {2,1,1}, vector = {1,0,0}. However the absolute direction of vector IS * important, giving the line a direction. * * Default is a default Point3 (0.0, 0.0, 0.0) and default Vector3 (0.0, 0.0, * 0.0). Operations on this line may lead to Exceptions such as * ZeroLengthVector. * * @author (C) P. Murray-Rust, 1996 */ public class Line3 implements EuclidConstants { /** * the (normalised) vector for the line */ Vector3 vect = new Vector3(); /** * any point on the line */ Point3 point = new Point3(); /** * contents as array */ // double[] array = new double[6]; /** * default consstructor. no vector and point set * */ public Line3() { } /** * construct from point and vector. the line will not necessarily retain the * exact point and the vector need not be normalized p and vector are copied * * @param p * a point on the line * @param v * non-zero vector through the point */ public Line3(Point3 p, Vector3 v) { vect = new Vector3(v); point = new Point3(p); if (!vect.isZero()) { // normalise vector vect.normalize(); } } /** * construct from array. dangerous as it is easy to muddle point and vector * * @param array * of length 6. first 3 are VECTOR, next are POINT */ /*-- public Line3(double[] array) throws EuclidRuntimeException { Util.check(array, 6); vect = new Vector3(); System.arraycopy(array, 0, vect.flarray, 0, 3); vect.normalize(); point = new Point3(); System.arraycopy(array, 3, point.flarray, 0, 3); System.arraycopy(array, 0, this.array, 0, 6); } --*/ /** * construct a line from two Point3s. the line will not necessarily retain * the exact points * * @param p1 * a point on the line * @param p2 * another point on the line */ public Line3(Point3 p1, Point3 p2) { this(p1, p2.subtract(p1)); } /** * copy constructor. * * @param l * Line3 to copy */ public Line3(Line3 l) { vect = new Vector3(l.vect); point = new Point3(l.point); } /** * are two lines identical. must be coincident and parallel uses * vect.equals() and containsPoint * * @param l2 * Line3 to compare * @return equals */ public boolean isEqualTo(Line3 l2) { if (!vect.isEqualTo(l2.vect)) { return false; } else { return containsPoint(l2.point); } } /** * form coincident antiparallel line. * * @return antiparallel line */ public Line3 negative() { Line3 l = new Line3(point, vect.negative()); return l; } /** * get return contents as an array. * * @return the array (v0, v1, v2, p0, p1, p2) */ /*-- public double[] getArray() { System.arraycopy(vect.flarray, 0, array, 0, 3); System.arraycopy(point.flarray, 0, array, 3, 3); return array; } --*/ /** * get vector from line. * * @return the vector (need not be normalized) */ public Vector3 getVector() { return vect; } /** * get point from line. * * @return any point on line */ public Point3 getPoint() { return point; } /** * get transformed line. does not alter this * * @param t * transform * @return transformed line */ public Line3 transform(Transform3 t) { Line3 lout = new Line3(); lout.point = point.transform(t); lout.vect = vect.transform(t); return lout; } /** * are two lines parallel. (not antiparallel) does not test coincidence * * @param l2 * line to compare * @return true if parallel */ public boolean isParallelTo(Line3 l2) { return vect.isIdenticalTo(l2.vect); } /** * are two lines antiparallel. (not parallel) does not test coincidence * * @param l2 * line to compare * @return true if antiparallel */ public boolean isAntiparallelTo(Line3 l2) { Vector3 v = new Vector3(l2.vect); return vect.isIdenticalTo(v.negative()); } /** * is a point on a line. tests for Real.isZero() distance from line * * @param p * point * @return true if within Real.isZero() */ public boolean containsPoint(Point3 p) { return Real.isZero(getDistanceFromPoint(p), Real.getEpsilon()); } /** * point on line closest to another point. * * @param p2 * reference point * @return point on line closest to p2 */ public Point3 getClosestPointTo(Point3 p2) { Point3 p1 = new Point3(); Vector3 v2 = new Vector3(p2); Vector3 v1 = new Vector3(point); p1 = point.plus(vect.multiplyBy((v2.subtract(v1)).dot(vect))); return p1; } /** * distance of a point from a line * * @param p * reference point * @return distance from line */ public double getDistanceFromPoint(Point3 p) { Point3 p0 = getClosestPointTo(p); Vector3 v = new Vector3(p.subtract(p0)); return v.getLength(); } /** * point of intersection of line and plane calls * Plane3.getIntersectionWith(Point3) * * @param pl * plane intersecting line * @return point (null if line parallel to plane) */ public Point3 getIntersectionWith(Plane3 pl) { return pl.getIntersectionWith(this); } /** * get string representation. * * @return string */ public String toString() { return S_LBRAK + vect + S_COMMA + point + S_RBRAK; } } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/ParsedSymop.java000077500000000000000000000165321461721410700252060ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.log4j.Logger; /** * Parses common crystallographic representations of a single component of a symmetry operator * * Lowercases everything * * e.g. x * y+2/3 * 0.5-Z * @author pm286 * */ public class ParsedSymop { private final static Logger LOG = Logger.getLogger(ParsedSymop.class); /** use regex * * ([\+\-]?)(x|y|z) x,+y,-z * ([\+\-]?)(\d*\.\d+)\s*([\+\-])(x|y|z) 0.25+x, .5-y, -0.5-z * ([\+\-]?)(x|y|z)\s*([\+\-])(\d*\.\d+) x-.25, -y+0.5, z-0.5 * ([\+\-]?)(1|2|3|4|5|6|7|8|9|10|11)\/(2|3|4|6|12)\s*[\+\-]\s*(x|y|z)) 1/2+x, 2/3-y, -7/12+z * ([\+\-]?)((x|y|z)\s*[\+\-]\s*(1|2|3|4|5|6|7|8|9|10|11)\/(2|3|4|6|12)) x-1/2, -y+11/12, z+5/6 */ private final static Pattern FRACT = Pattern.compile("(\\-?\\d+)/(\\d+)"); private final static String SIGNED_NUMBER = "([\\+\\-]?\\d*\\.\\d+)"; private final static String SIGNED_FRACT = "([\\+\\-]?(1|2|3|4|5|6|7|8|9|10|11)\\/(2|3|4|6|12))"; private final static String SIGNED_XYZ = "(([\\+\\-]?)(x|y|z))"; public final static Pattern XYZ = Pattern.compile(SIGNED_XYZ+"("+SIGNED_XYZ+"?)"); // signedxyz signedxyz? public final static Pattern NUMB_XYZ = Pattern.compile(SIGNED_NUMBER+SIGNED_XYZ+"("+SIGNED_XYZ+"?)"); // signednumber, signedxyz signedxyz? public final static Pattern XYZ_NUMB = Pattern.compile(SIGNED_XYZ+"("+SIGNED_XYZ+"?)"+SIGNED_NUMBER); // signedxyz signedxyz? signednumber public final static Pattern FRACT_XYZ = Pattern.compile(SIGNED_FRACT+SIGNED_XYZ+"("+SIGNED_XYZ+"?)"); // signedfract, signedxyz signedxyz? public final static Pattern XYZ_FRACT = Pattern.compile(SIGNED_XYZ+"("+SIGNED_XYZ+"?)"+SIGNED_FRACT); // xyzsign, xyz, numsign, num, denom private String xyz; private String xyz1; private String numberS; private String fractS; private Double number; public ParsedSymop() { // TODO Auto-generated constructor stub } public final static ParsedSymop createSymop(String s) { ParsedSymop symop = new ParsedSymop(); try { symop.parse(s); } catch (RuntimeException e) { throw (e); } return symop; } public final static Transform3 createTransform( String[] ss) { return createTransform(new Transform3(), ss); } /** populate transform with rows for each symop. * * recoomended to use createTransform( String[] ss) * * @param t3 empty transform * @param ss * @return */ public final static Transform3 createTransform(Transform3 t3, String[] ss) { for (int i = 0; i < 3; i++) { ParsedSymop symop = ParsedSymop.createSymop(ss[i]); double[] row = symop.getRow(); for (int j = 0; j < 4; j++) { t3.setElementAt(i, j, row[j]); } } return t3; } private void parse(String s) { s = s.toLowerCase(); s = s.replaceAll(" ", ""); xyz = null; xyz1 = null; numberS = null; fractS = null; // these return at the first match. The xyz, then fracts are commonest boolean matched = matchXYZ(s) || matchFRACT_XYZ(s) || matchXYZ_FRACT(s) || matchNUMB_XYZ(s) || matchXYZ_NUMB(s); if (!matched) { throw new RuntimeException("Cannot parse as symmetry operator: "+s); } number = (numberS != null) ? Double.valueOf(numberS) : ((fractS != null) ? calculateFract() : null); } public String getXyz() { return xyz; } public String getXyz1() { return xyz1; } public Double getNumber() { return number; } private Double calculateFract() { Matcher matcher = FRACT.matcher(fractS); if (!matcher.matches()) { throw new RuntimeException("Cannot parse as fraction: "+fractS); } return Double.valueOf(matcher.group(1))/Double.valueOf(matcher.group(2)); } // Pattern.compile(SIGNED_XYZ+"("+SIGNED_XYZ+"?)"); // signedxyz signedxyz? private boolean matchXYZ(String s) { Matcher matcher = XYZ.matcher(s); LOG.trace("XYZ "+XYZ+" "+s); if (matcher.matches()) { debug(matcher); LOG.trace("groups"+matcher.groupCount()+matcher); xyz = deplus(matcher.group(1)); xyz1 = deplus(matcher.group(4)); return true; } return false; } // Pattern.compile(SIGNED_XYZ+"("+SIGNED_XYZ+")"+SIGNED_FRACT); // xyzsign, xyz, numsign, num, denom private boolean matchXYZ_FRACT(String s) { Matcher matcher = XYZ_FRACT.matcher(s); LOG.trace("XYZ_FRACT "+XYZ_FRACT+" "+s); if (matcher.matches()) { debug(matcher); xyz = deplus(matcher.group(1)); xyz1 = deplus(matcher.group(4)); fractS = deplus(matcher.group(8)); return true; } return false; } // Pattern.compile(SIGNED_FRACT+SIGNED_XYZ+"("+SIGNED_XYZ+")"); // signedfract, signedxyz signedxyz? private boolean matchFRACT_XYZ(String s) { Matcher matcher = FRACT_XYZ.matcher(s); LOG.trace("FRACT_XYZ "+FRACT_XYZ+" "+s); if (matcher.matches()) { debug(matcher); xyz = deplus(matcher.group(4)); xyz1 = deplus(matcher.group(7)); fractS = deplus(matcher.group(1)); return true; } return false; } // Pattern.compile(SIGNED_NUMBER+SIGNED_XYZ+"("+SIGNED_XYZ+")"); // signednumber, signedxyz signedxyz? private boolean matchNUMB_XYZ(String s) { Matcher matcher = NUMB_XYZ.matcher(s); LOG.trace("NUMB_XYZ "+NUMB_XYZ+" "+s); if (matcher.matches()) { debug(matcher); xyz = deplus(matcher.group(2)); xyz1 = deplus(matcher.group(5)); numberS = deplus(matcher.group(1)); return true; } return false; } // Pattern.compile(SIGNED_XYZ+"("+SIGNED_XYZ+")"+SIGNED_NUMBER); // signedxyz signedxyz? signednumber private boolean matchXYZ_NUMB(String s) { Matcher matcher = XYZ_NUMB.matcher(s); if (matcher.matches()) { debug(matcher); xyz = deplus(matcher.group(1)); xyz1 = deplus(matcher.group(4)); numberS = deplus(matcher.group(8)); return true; } return false; } private void debug(Matcher matcher) { for (int i = 1; i < matcher.groupCount()+1; i++) { LOG.trace(matcher.group(i)); } } private String deplus(String xyz) { return xyz.startsWith("+") ? xyz.substring(1) : xyz; } public double[] getRow() { double[] row = new double[4]; setRow(xyz, row); setRow(xyz1, row); row[3] = number; return row; } private void addXyz(String xyz, double[][] flmat, int i) { int sign = 1; String xyz0 = xyz; if (xyz.startsWith("-")) { sign = -1; xyz0 = xyz.substring(1); } if (xyz0.length() != 1) { throw new RuntimeException("Cannot process x/y/z: "+xyz); } int j = xyz0.charAt(0) - (int) 'x'; // rely on numeric order xyx flmat[i][j] = sign; } private void setRow(String xyz, double[] row) { int sign = 1; String xyz0 = xyz; if (xyz.startsWith("-")) { sign = -1; xyz0 = xyz.substring(1); } if (xyz0.length() != 1) { throw new RuntimeException("Cannot process x/y/z: "+xyz); } int j = xyz0.charAt(0) - (int) 'x'; // rely on numeric order xyx row[j] = sign; } } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/Plane3.java000066400000000000000000000240731461721410700240560ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid; import org.apache.log4j.Logger; /** * 3-dimensional plane class * * Plane3 represents a 3-dimensional plane. It is one of a set of primitives * which can be combined to create and manipulate complex 3-dimensional objects. * Planes can be transformed with rotation matrices or rotation-translation * matrices (Transform3), can be calculated from other primitives or can be used * to generate other primitives. *

* A plane is described by a unit vector (vector) and the perpendicular distance * (dist) of the plane from the origin. The absolute direction of the plane * (vector) IS important, giving the plane a direction (back and front faces). *

* The default plane is a Vector of (0.0, 0.0, 0.0) and a distance of 0.0. * Operations on this default may lead to Exceptions such as ZeroLengthvector. * * @author (C) P. Murray-Rust, 1996 */ public class Plane3 implements EuclidConstants { final static Logger LOG = Logger.getLogger(Plane3.class); /** * vector of plane (normalised) */ protected Vector3 vect; /** * distance of plane from origin */ protected double dist; /** * status (OK or not) */ /** * 4-component array representing contents of vector and distance */ double[] array = new double[4]; /** * default constructor. uses default vector3 */ public Plane3() { vect = new Vector3(); dist = 0.0; } /** * formed from components. vector is normalised * * @param l * component * @param m * component * @param n * component * @param d * distance * @throws EuclidRuntimeException */ public Plane3(double l, double m, double n, double d) throws EuclidRuntimeException { vect = new Vector3(l, m, n); if (vect.isZero()) { throw new EuclidRuntimeException("zero length normal"); } dist = d; // normalise vector vect.normalize(); } /** * formed from components. vector is normalised * * @param lmn * component * @param d * distance * @throws EuclidRuntimeException */ public Plane3(double[] lmn, double d) throws EuclidRuntimeException { Util.check(lmn, 3); vect = new Vector3(lmn); dist = d; // normalise vector vect.normalize(); } /** * construct from array. * * @param array * 4-components * @throws EuclidRuntimeException */ public Plane3(double[] array) throws EuclidRuntimeException { Util.check(array, 4); vect = new Vector3(); System.arraycopy(array, 0, vect.flarray, 0, 3); dist = array[3]; System.arraycopy(array, 0, this.array, 0, 4); } /** * formed from plane and distance. vector is copied and normalised * * @param v * vector * @param d * distance * @throws EuclidRuntimeException */ public Plane3(Vector3 v, double d) throws EuclidRuntimeException { if (v.isZero()) { throw new EuclidRuntimeException("zero length normal"); } vect = new Vector3(v); dist = d; vect.normalize(); } /** * copy constructor: * * @param pl * place */ public Plane3(Plane3 pl) { vect = new Vector3(pl.vect); dist = pl.dist; } /** * make a plane from three points. * * @param p1 * point * @param p2 * point * @param p3 * point * @throws EuclidRuntimeException */ public Plane3(Point3 p1, Point3 p2, Point3 p3) throws EuclidRuntimeException { vect = new Vector3(); dist = 0.0; vect = (p2.subtract(p1)).cross(p3.subtract(p2)); if (vect.isZero()) { throw new EuclidRuntimeException("zero length normal"); } vect.normalize(); Vector3 vp1 = new Vector3(p1); dist = vp1.dot(vect); } /** * make a plane from a line and a point not on the line. * * @param l * point * @param p * point * @throws EuclidRuntimeException */ public Plane3(Line3 l, Point3 p) throws EuclidRuntimeException { // oKness dealt with by previous constructor this(l.getPoint(), (Point3) (l.getPoint().plus(l.getVector())), p); } /** * get return contents as an array. * * @return the array (l,m,n,d) */ public double[] getArray() { System.arraycopy(vect.flarray, 0, array, 0, 3); array[3] = dist; return array; } /** * get vector. * * @return the vector */ public Vector3 getVector() { return vect; } /** * get distance from origin. * * @return the distance */ public double getDistance() { return dist; } /** * reverse direction of plane. */ public void negative() { vect.negativeEquals(); } /** * are two planes coincident and parallel. * * @param pl2 * plane to compare * @return true if equal within Real.isEqual() */ public boolean isEqualTo(Plane3 pl2) { if (!vect.isEqualTo(pl2.vect)) return false; return Real.isEqual(pl2.dist, dist); } /** * form coincident antiparallel plane. * * @return antiparallel plane */ public Plane3 subtract() { Plane3 pl = this; pl.vect = pl.vect.negative(); return pl; } /** * distance of point from plane. will be a signed quantity * * @param p * the point * @return the distance */ public double getDistanceFromPoint(Point3 p) { Vector3 v = new Vector3(p); return (v.dot(vect) - dist); } /** * are two planes parallel. not antiparallel * * @param pl2 * the plane * @return true if parallel within Real.isEqual() */ public boolean isParallelTo(Plane3 pl2) { return vect.isIdenticalTo(pl2.vect); } /** * are two planes antiparallel. not parallel * * @param pl2 * the plane * @return true if antiparallel within Real.isEqual() */ public boolean isAntiparallelTo(Plane3 pl2) { Vector3 v = new Vector3(pl2.vect); return vect.isIdenticalTo(v.negative()); } /** * is a point on the plane. * * @param p * the point * @return true if within Real.isEqual() */ public boolean containsPoint(Point3 p) { return Real.isZero(this.getDistanceFromPoint(p), Real.getEpsilon()); } /** * point on plane closest to another point. if p2 is on plane then result * will coincide * * @param p2 * other point * @return the closest point */ public Point3 getClosestPointTo(Point3 p2) { Point3 p1 = new Point3(); double d = getDistanceFromPoint(p2); Vector3 v = new Vector3(vect.multiplyBy(d)); Vector3 vv = new Vector3(p2); p1 = new Point3(vv.subtract(v)); return p1; } /** * point of intersection of plane and line. * * @param l * line * @return intersection point (null if line parallel to plane) */ public Point3 getIntersectionWith(Line3 l) { Point3 p = null; double lambda; Vector3 v = new Vector3(l.getPoint()); Vector3 lvect = new Vector3(l.getVector()); double numer = dist - vect.dot(v); double denom = vect.dot(lvect); // check for line and plane parallel if (!Real.isZero(denom, Real.getEpsilon())) { lambda = numer / denom; p = l.getPoint().plus(lvect.multiplyBy(lambda)); } return p; } /** * get line as intersection of two planes. * * @param pl2 * plane * @return intersection line (null if parallel) */ public Line3 getIntersectionWith(Plane3 pl2) { Vector3 v3 = vect.cross(pl2.vect); v3.normalize(); // point on p1 nearest origin Point3 p = new Point3(vect.multiplyBy(dist)); Vector3 v1a = vect.cross(v3); Line3 l1 = new Line3(p, v1a); Point3 p2 = pl2.getIntersectionWith(l1); // this should take care of null vector (that is parallel planes) return (p2 == null) ? null : new Line3(p2, v3); } /** * point where three planes intersect * * @param pl2 * plane * @param pl3 * plane * @return intersection point (null if any planes parallel) */ public Point3 getIntersectionWith(Plane3 pl2, Plane3 pl3) { Point3 p = new Point3(); Line3 l = pl2.getIntersectionWith(pl3); p = getIntersectionWith(l); return p; } /** * the angle between 2 planes. * * @param pl2 * plane * @return the angle (unsigned) */ public Angle getAngleMadeWith(Plane3 pl2) { return this.getVector().getAngleMadeWith(pl2.getVector()); } /** * string representation. * * @return the string */ public String toString() { return EuclidConstants.S_LBRAK + vect + EuclidConstants.S_COMMA + dist + EuclidConstants.S_RBRAK; } } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/Point2.java000066400000000000000000000035471461721410700241120ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid; /** * 2-dimensional point class * PROBABLY OBSOLETE - WE USE Real2 instead * * Point2 represents a 2-dimensional point. It is one of a set of primitives * which can be combined to create and manipulate complex 2-dimensional objects. * Points can be transformed with rotation matrices or rotation-translation * matrices (Transform2), can be calculated from other primitives or can be used * to generate other primitives. * * Default point is 0.0, 0.0 * * @author Peter Murray-Rust * * @author (C) P. Murray-Rust, 1996 */ public class Point2 extends Real2 { /** * the coordinates of the point */ // protected double[] p2_array; /** * constructor. */ public Point2() { super(); } /** * formed from point components * * @param x * @param y */ public Point2(double x, double y) { super(x, y); } /** * copy constructor * * @param p */ public Point2(Point2 p) { super(p); } /** * constructor from a double[] (or a RealArray) * * @param f */ public Point2(double[] f) { super(f); } } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/Point3.java000066400000000000000000000431461461721410700241120ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid; import org.apache.log4j.Logger; /** * 3-dimensional point class * * * Point3 represents a 3-dimensional point. It is one of a set of primitives * which can be combined to create and manipulate complex 3-dimensional objects. * Points can be transformed with rotation matrices or rotation-translation * matrices (Transform3), can be calculated from other primitives or can be used * to generate other primitives. * * Default point is 0.0, 0.0, 0.0 * * @author Peter Murray-Rust * @see Vector3 * @see Line3 * @see Point3Vector * @see Plane3 * * @author (C) P. Murray-Rust, 1996 */ public class Point3 implements EuclidConstants { final static Logger LOG = Logger.getLogger(Point3.class); /** * tolerance between crystal fractional coordinates. allows for 1/3 being * represented as 0.3333 which will not fit normal equality */ public final static double CRYSTALFRACTEPSILON = 0.001; /** * the coordinates of the point */ protected double[] flarray = new double[3]; /** * constructor. */ public Point3() { } /** * formed from point components * * @param x * @param y * @param z */ public Point3(double x, double y, double z) { flarray[0] = x; flarray[1] = y; flarray[2] = z; } /** * copy constructor * * @param p */ public Point3(Point3 p) { System.arraycopy(p.flarray, 0, flarray, 0, 3); } /** * constructor from a double[] (or a RealArray). * * @param f * @throws EuclidRuntimeException */ public Point3(double[] f) throws EuclidRuntimeException { Util.check(f, 3); System.arraycopy(f, 0, flarray, 0, 3); } /** * make a point from a vector creates the point at head of vector rooted at * the origin * * @param v */ public Point3(Vector3 v) { System.arraycopy(v.flarray, 0, flarray, 0, 3); } /** * get components as double[] * * @return the array */ public double[] getArray() { return flarray; } /** * sets the point to the origin */ public void clear() { flarray[0] = flarray[1] = flarray[2] = 0.0; } /** * are two points identical. compares content of points with Real.isEqual() * * @param p * point to compare * @return equal if coordinates are equal within Real.epsilon */ public boolean isEqualTo(Point3 p) { return Real.isEqual(flarray, p.flarray, Real.getEpsilon()); } /** * are two points identical. compares x, y, z coordinates of points * * @param p * point to compare * @param eps * the tolerance * @return equal if coordinates are equal within Real.epsilon */ public boolean isEqualTo(Point3 p, double eps) { return Real.isEqual(flarray, p.flarray, eps); } /** * are two crystallographic points identical. shifts x, y, z by +-1.0 if * necessary compares content of crystallographically normalised points with * Real.isEqual() * * @param p * point to compare * @return equal if coordinates are equal within CRYSTALFRACTEPSILON */ public boolean equalsCrystallographically(Point3 p) { if (p.flarray == null) return false; Point3 crystal = new Point3(this); crystal.normaliseCrystallographically(); Point3 crystalP = new Point3(p); crystalP.normaliseCrystallographically(); return Real.isEqual(crystalP.flarray, crystal.flarray, Point3.CRYSTALFRACTEPSILON); } /** * normalise crystallographically. shifts x, y, z so that values lie between * 0.0 (inclusive) and 1.0 (exclusive) modifies this * * @return Vector3 corresponding to the translation vector to crystallographically normalise Point3 */ public Vector3 normaliseCrystallographically() { double[] arr = new double[3]; for (int i = 0; i < 3; i++) { double start = flarray[i]; flarray[i] = normaliseCrystallographically(flarray[i]); arr[i] = Math.round(flarray[i] - start); } return new Vector3(arr); } private static double normaliseCrystallographically(double d) { while (d >= 1.0) { d -= 1.0; } while (d < 0.0) { d += 1.0; } return d; } /** * is point invariant wrt symmetry operation. * * tolerance is decided by Real.isEqual() * * @param t3 * the transformation * @param translate * allow crystallographic translations (+-1) * @return true if t3 transforms this onto itself */ public boolean isInvariant(Transform3 t3, boolean translate) { Point3 pNew = this.transform(t3); return (translate) ? pNew.equalsCrystallographically(this) : pNew .isEqualTo(this); } /** * vector between two points. result is vector FROM p2 TO this this -= p2 * alters this * * @param p2 * point to subtract * @return vector */ public Vector3 subtract(Point3 p2) { Vector3 v1 = new Vector3(this); for (int i = 0; i < 3; i++) { v1.flarray[i] -= p2.flarray[i]; } return v1; } /** * New point by adding points as vectors. used for finding centroids, etc. * does NOT alter this * * @param p * to add * @return NEW point */ public Point3 plus(Point3 p) { Point3 p1 = new Point3(); for (int i = 0; i < 3; i++) { p1.flarray[i] = flarray[i] + p.flarray[i]; } return p1; } /** * Move this Point3. alters this * * @param pt * point to shift by */ public void plusEquals(final Point3 pt) { for (int i = 0; i < 3; i++) { flarray[i] += pt.flarray[i]; } } /** * New point from point+vector does NOT alter this * * @param v * to add * @return NEW point */ public Point3 plus(Vector3 v) { Point3 p1 = new Point3(); for (int i = 0; i < 3; i++) { p1.flarray[i] = flarray[i] + v.flarray[i]; } return p1; } /** * point from point and vector. alter this * * @param v * to add */ public void plusEquals(Vector3 v) { for (int i = 0; i < 3; i++) { flarray[i] += v.flarray[i]; } } /** * New point from point minus vector. does NOT alter this * * @param v * to subtract * @return NEW point */ public Point3 subtract(Vector3 v) { Point3 p1 = new Point3(); for (int i = 0; i < 3; i++) { p1.flarray[i] = flarray[i] - v.flarray[i]; } return p1; } /** * Shift point from point. does alter this * * @param pt * the Point3 to subtract from this */ public void subtractEquals(final Point3 pt) { for (int i = 0; i < 3; i++) { flarray[i] -= pt.flarray[i]; } } /** * Shift point from vector3. does alter this * * @param vec3 * the Vector3 to subtract from this */ public void subtractEquals(final Vector3 vec3) { for (int i = 0; i < 3; i++) { flarray[i] -= vec3.flarray[i]; } } /** * scale point. does NOT alter this * * @param f * factor to multiply by * @return NEW point */ public Point3 multiplyBy(double f) { Point3 p1 = new Point3(); for (int i = 0; i < 3; i++) { p1.flarray[i] = flarray[i] * f; } return p1; } /** * scale point. alters this * * @param f * factor to multiply by */ public void multiplyEquals(final double f) { for (int i = 2; i >= 0; --i) { flarray[i] *= f; } } /** * create inverse point. alters this = -this */ public void reflect() { flarray[0] = -flarray[0]; flarray[1] = -flarray[1]; flarray[2] = -flarray[2]; } /** * scale point does NOT alter this * * @param f * factor to divide by * @return NEW point */ public Point3 divideBy(double f) { Point3 p1 = new Point3(); for (int i = 0; i < 3; i++) { p1.flarray[i] = flarray[i] / f; } return p1; } /** * scale point. alters this * * @param f * factor to divide by */ public void divideEquals(final double f) { final double f1 = 1.0 / f; for (int i = 2; i >= 0; --i) { flarray[i] *= f1; } } /** * subscript operator. * * @param n * the index * @throws EuclidRuntimeException * @return the element */ public double elementAt(int n) throws EuclidRuntimeException { Util.check(n, 0, 2); return flarray[n]; } /** * sets element. * * @param n * the index * @param d * the value * @throws EuclidRuntimeException */ public void setElementAt(int n, double d) throws EuclidRuntimeException { Util.check(n, 0, 2); flarray[n] = d; } /** * get transformed point. does NOT modify 'this' * * @param t * the transform * @return new point */ public Point3 transform(Transform3 t) { RealArray col3 = new RealArray(4, 1.0); // set the translation in col 3 col3.setElements(0, this.flarray); RealArray result = t.multiply(col3); Point3 pout = new Point3(result.getSubArray(0, 2).getArray()); return pout; } /** * get transformed point. does modify 'this' * * @param t * the transform */ public void transformEquals(Transform3 t) { RealArray col3 = new RealArray(4, 1.0); // set the translation in col 3 col3.setElements(0, this.flarray); RealArray result = t.multiply(col3); System.arraycopy(result.getSubArray(0, 2).getArray(), 0, flarray, 0, 3); } /** * distance of point from origin. * * @return distance */ public double getDistanceFromOrigin() { Vector3 v = new Vector3(this); return v.getLength(); } /** * Gets the squared Distance between this point and another * * @param p2 * the other point to get the distance from * @return the squared distance */ public double getSquaredDistanceFromPoint(final Point3 p2) { double d = flarray[0] - p2.flarray[0]; double sqdDist = d * d; d = flarray[1] - p2.flarray[1]; sqdDist += (d * d); d = flarray[2] - p2.flarray[2]; sqdDist += (d * d); return sqdDist; } /** * distance of point from another point * * @param p2 * the other point to get the distance from * @return the distance */ public double getDistanceFromPoint(Point3 p2) { Vector3 v = new Vector3(p2.subtract(this)); return v.getLength(); } /** * distance from plane * * @param pl * @return distance */ public double distanceFromPlane(Plane3 pl) { return pl.getDistanceFromPoint(this); } /** * get closest point on line. * * @param l * the line * @return the point where distance is shortest */ public Point3 getClosestPointOnLine(Line3 l) { return l.getClosestPointTo(this); } /** * is point on line. * * @param l * the line * @return true if within Real.isEqual() of line */ public boolean isOnLine(Line3 l) { // TODO add epsilon return l.containsPoint(this); } /** * is point on plane. * * @param pl * the plane * @return true if within Real.isEqual() of plane */ public boolean isOnPlane(Plane3 pl) { return pl.containsPoint(this); } /** * distance from line. * * @param l * the line * @return the distance */ public double distanceFromLine(Line3 l) { return l.getDistanceFromPoint(this); } /** * mid-point of two points. * * @param p2 * the other point * @return the midPoint */ public Point3 getMidPoint(Point3 p2) { Point3 p = new Point3(); { for (int i = 0; i < 3; i++) { p.flarray[i] = (this.flarray[i] + p2.flarray[i]) / 2.0; } } return p; } /** * get angle. p1-p2-p3 * * @param p1 * the start point * @param p2 * the vertex point * @param p3 * the remote point * @return angle null if coincient points */ public static Angle getAngle(Point3 p1, Point3 p2, Point3 p3) { Vector3 v1 = p1.subtract(p2); return (v1.isZero()) ? null : v1.getAngleMadeWith(p3.subtract(p2)); } /** * torsion angle. p1-p2-p3-p4 * * @param p1 * @param p2 * @param p3 * @param p4 * @return angle unsigned radians or null (null args, or colinearity) */ public static Angle getTorsion(Point3 p1, Point3 p2, Point3 p3, Point3 p4) { Angle angle = null; Vector3 v23 = p3.subtract(p2); Vector3 v13a = p2.subtract(p1); Vector3 v13 = v13a.cross(v23); Vector3 v24 = v23.cross(p4.subtract(p3)); v13.normalize(); v24.normalize(); double ang = v13.getAngleMadeWith(v24).getAngle(); if (v13.getScalarTripleProduct(v24, v23) < 0.0) { ang = -ang; } angle = new Angle(ang); return angle; } /** * add point using internal coordinates. used for z-matrix like building * p1-p2-p3-newPoint * * @param p1 * existing point * @param p2 * existing point * @param p3 * existing point * @param length * p3-p4 * @param angle * p2-p3-p4 * @param torsion * this-p2-p3-p4 * @exception EuclidRuntimeException * two points are coincident or 3 colinear * @return new point; null if two points are coincident or three points are * colinear */ public static Point3 calculateFromInternalCoordinates(Point3 p1, Point3 p2, Point3 p3, double length, Angle angle, Angle torsion) throws EuclidRuntimeException { Vector3 v32 = p2.subtract(p3); Vector3 v12 = p2.subtract(p1); // perp to p1-2-3 Vector3 v13a = v12.cross(v32); Vector3 v13n = v13a.normalize(); // in plane Vector3 v32n = v32.normalize(); Vector3 v34 = v32n.multiplyBy(length); Transform3 t = new Transform3(v13n, angle); v34 = v34.transform(t); v32n = v32n.negative(); Transform3 t1 = new Transform3(v32n, torsion); v34 = v34.transform(t1); Point3 p4 = p3.plus(v34); return p4; } /** * is a point at Origin * * @return is this within Real.isEqual() of origin */ public boolean isOrigin() { for (int i = 0; i < 3; i++) { if (!Real.isZero(flarray[i], Real.getEpsilon())) return false; } return true; } /** * string representation. * * @return the string */ public String toString() { return EC.S_LBRAK + flarray[0] + EC.S_COMMA + EC.S_SPACE + flarray[1] + EC.S_COMMA + EC.S_SPACE + flarray[2] + EC.S_RBRAK; } /** equals. * @param that * @return is equal */ public boolean equals(Object that) { if (this == that) return true; if (!(that instanceof Point3)) return false; Point3 p3 = (Point3) that; return Real.isEqual(this.getArray(), p3.getArray(), Point3.CRYSTALFRACTEPSILON); } /** hash code. * @return coe */ public int hashCode() { int result = 17; int c = 1; long x = Double.doubleToLongBits(flarray[0]); c = c * (int)(x^(x>>>32)); long y = Double.doubleToLongBits(flarray[1]); c = c * (int)(y^(x>>>32)); long z = Double.doubleToLongBits(flarray[2]); c = c * (int)(z^(x>>>32)); return 37*result+c; } /** main. * * @param args */ public static void main(String[] args) { Point3 one = new Point3(0.1, 0.2, 0.3); Point3 two = new Point3(0.1, 0.2, 0.5); if (one.equals(two)) { Util.println("are equal in state"); } if (one == two) { Util.println("same object"); } } } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/Point3Vector.java000066400000000000000000001230521461721410700252700ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid; import java.util.ArrayList; import java.util.List; import org.apache.log4j.Logger; import org.xmlcml.euclid.Axis.Axis3; /** * a (Java) Vector of Point3s (Note that 'Vector' is used by Java to describe an * array of objects - there is no relationship to geometrical vectors in this * package.) *

* There are a large number of routines for manipulating 3-D coordinates. * Examples are distance, torsion, angle, planes, fitting, etc. The routines * have NOT been optimised. In the previous incarnation (C++) all coordinates * were handled by Point3s, etc because pointers are fragile. In Java, however, * a lot more intermediate information could be passed through double[] arrays * and this will be gradually converted. *

* All the routines compile and give answers. The answers were right in the C++ * version. Most seem to be right here, but the FITTING ROUTINES ARE CURRENTLY * NOT WORKING PROPERLY. This will be fixed... *

* Default is an empty (Java) Vector; * * @author (C) P. Murray-Rust, 1996 */ public class Point3Vector implements EuclidConstants { final static Logger LOG = Logger.getLogger(Point3Vector.class); protected List vector; /** * Comment for serialVersionUID */ private static final long serialVersionUID = 3258126964282635312L; /** * constructor. */ public Point3Vector() { vector = new ArrayList(); } /** * Formed by feeding in an existing array to a 3xn matrix flarray is in form * (x, y, z, x, y, z ...) * * @param flarray * @exception EuclidException * size of flarray must be multiple of 3 */ /** * constructor. * * @param flarray * @exception EuclidRuntimeException */ public Point3Vector(double[] flarray) throws EuclidRuntimeException { this(); if (flarray == null) { throw new EuclidRuntimeException("null array"); } int count = 0; int n = flarray.length / 3; if (flarray.length != 3 * n) { throw new EuclidRuntimeException("array length must be multiple of 3"); } for (int i = 0; i < n; i++) { Point3 p = new Point3(flarray[count++], flarray[count++], flarray[count++]); vector.add(p); } } /** * from three parallel arrays of x, y and z - by REFERENCE * * @param n * @param x * @param y * @param z * @throws EuclidRuntimeException * */ public Point3Vector(int n, double[] x, double[] y, double[] z) throws EuclidRuntimeException { this(); Util.check(x, n); Util.check(y, n); Util.check(z, n); for (int i = 0; i < n; i++) { vector.add(new Point3(x[i], y[i], z[i])); } } /** * constructor from RealArray - by REFERENCE * * @param m * @exception EuclidRuntimeException * size of flarray must be multiple of 3 */ public Point3Vector(RealArray m) throws EuclidRuntimeException { this(); double[] marray = m.getArray(); int count = marray.length / 3; if (marray == null || marray.length != count * 3) { throw new EuclidRuntimeException("null array or count not divisible by 3"); } int j = 0; for (int i = 0; i < count; i++) { vector.add(new Point3(marray[j++], marray[j++], marray[j++])); } } /** * copy constructor from Point3Vector COPIES pv * * @param pv */ public Point3Vector(Point3Vector pv) { this(); for (int i = 0; i < pv.size(); i++) { Point3 point = (Point3) pv.elementAt(i); if (point != null) { point = new Point3(point); } this.addElement(point); } } /** * size of vector. * * @return the size */ public int size() { return vector.size(); } /** * get points as list. * * @return list */ public List getPoint3List() { return vector; } /** * is equal. * * @param p * @return true if equals */ public boolean isEqualTo(Point3Vector p) { boolean ok = true; if (p == null || this.size() != p.size()) { ok = false; } else { int i = 0; for (Point3 pp : p.vector) { Point3 thisP = this.vector.get(i++); if (!thisP.isEqualTo(pp)) { ok = false; break; } } } return ok; } /** * get array. * * @return array */ public double[] getArray() { double[] array = new double[3 * vector.size()]; int i = 0; for (Point3 p : vector) { array[i++] = p.flarray[0]; array[i++] = p.flarray[1]; array[i++] = p.flarray[2]; } return array; } /** * add point. * * @param p * the point */ public void add(Point3 p) { vector.add(p); } /** * insert point. * * @param p * point to insert * @param i * position to insert at */ public void setElementAt(Point3 p, int i) { vector.set(i, p); } /** * get Point3 element. * * @param i * serial of the element to get * @return the element */ public Point3 elementAt(int i) { return vector.get(i); } /** * get Point3 element. * * @param i * serial of the element to get * @return the element */ public Point3 get(int i) { return (Point3) vector.get(i); } private void checkConformable(Point3Vector v) throws EuclidRuntimeException { if (size() != v.size()) { throw new EuclidRuntimeException("incompatible Point3Vector sizes: " + size() + EC.S_SLASH + v.size()); } } /** * add point. * * @param p */ public void addElement(Point3 p) { vector.add(p); } /** * sets a given coordinate (i) to vector * * @param v * @param i * @exception EuclidRuntimeException * i is {@literal >}= number of current points (cann use this to * increase size of Point3Vector) */ public void setElementAt(Vector3 v, int i) throws EuclidRuntimeException { vector.set(i, new Point3(v)); } /** * get range of one coordinate * * @param ax * @return range */ public RealRange getRange(Axis3 ax) { RealArray temp = new RealArray(vector.size()); double[] dd = temp.getArray(); int i = 0; for (Point3 p : vector) { dd[i++] = p.getArray()[ax.value]; } RealRange range = new RealRange(); if (size() > 0) { range.add(temp.smallestElement()); range.add(temp.largestElement()); } return range; } /** * get range of all 3 coordinates * * @return range */ public Real3Range getRange3() { Axis3 axes[] = Axis3.values(); Real3Range range = new Real3Range(); for (Axis3 ax : axes) { range.add(ax, getRange(ax)); } return range; } /** * gets sums of squared distances between points. * * result = sigma[i = 1, npoints] (this.point(i)getDistanceFrom(vector.point(i)) ** * 2 * * @param v * the other vector * @throws EuclidRuntimeException * vectors not of same length * @return the sums of squared distances */ public double getSigmaDeltaSquared(Point3Vector v) throws EuclidRuntimeException { int np = size(); if (v.size() != np) { throw new EuclidRuntimeException("Vectors of different lengths"); } double d = 0.0; for (int i = 0; i < np; i++) { Point3 thisP = vector.get(i); Point3 vP = v.vector.get(i); double dist = thisP.getDistanceFromPoint(vP); d += dist * dist; } return d; } /** * create a NEW subset of the points; points are COPIED * * @param is * @return vector * @exception EuclidRuntimeException * an element of is is outside range of this */ public Point3Vector subArray(IntSet is) throws EuclidRuntimeException { Point3Vector sub = new Point3Vector(); for (int i = 0; i < is.size(); i++) { int ix = is.elementAt(i); if (ix < 0 || ix >= this.size()) { throw new EuclidRuntimeException("element out of range: "+ix); } sub.addElement(new Point3(this.getPoint3(ix))); } return sub; } /** * multiply all coords. does not alter this. * * @param scale * @return the scaled vector */ public Point3Vector multiplyBy(double scale) { Point3Vector p3v = new Point3Vector(this); p3v.multiplyByEquals(scale); return p3v; } /** * multiply all coords - alters this. * * @param scale */ public void multiplyByEquals(double scale) { for (int i = 0; i < this.size(); i++) { this.getPoint3(i).multiplyEquals(scale); } } /** * form the point+point sum for two vectors of points * * @param pv2 * @exception EuclidRuntimeException * pv2 is different size from this * @return vector */ public Point3Vector plus(Point3Vector pv2) throws EuclidRuntimeException { checkConformable(pv2); Point3Vector pv1 = new Point3Vector(); for (int i = 0; i < this.size(); i++) { Point3 temp = (getPoint3(i)).plus(pv2.getPoint3(i)); pv1.addElement(temp); } return pv1; } /** * form the point+point difference for two vectors of points * * @param pv2 * @return vector * @exception EuclidRuntimeException * pv2 is different size from this */ public Point3Vector subtract(Point3Vector pv2) throws EuclidRuntimeException { checkConformable(pv2); Point3Vector pv1 = new Point3Vector(); for (int i = 0; i < size(); i++) { Vector3 v = (getPoint3(i)).subtract(pv2.getPoint3(i)); Point3 temp = new Point3(v.getArray()); pv1.addElement(temp); } return pv1; } /** * get the line between two points * * @param i1 * @param i2 * @return line */ public Line3 getLine(int i1, int i2) { Line3 temp = new Line3(); if (i1 >= 0 && i1 < size() && i2 >= 0 && i2 < size()) { temp = new Line3(getPoint3(i1), getPoint3(i2)); } return temp; } /** * get centroid of all points * * @return point */ synchronized public Point3 getCentroid() { final int size = size(); if (size < 1) { return null; } Point3 p = new Point3(); for (int j = size - 1; j >= 0; --j) { p = p.plus(getPoint3(j)); } double scale = 1.0 / ((double) size); p = p.multiplyBy(scale); return p; } /** * translate by a vector - do NOT modify this * * @param v * @return vector */ public Point3Vector plus(Vector3 v) { Point3Vector temp = new Point3Vector(); for (int i = 0; i < size(); i++) { Point3 p = getPoint3(i).plus(v); temp.addElement(p); } return temp; } /** * translate by a vector - modify this * * @param v */ public void plusEquals(Vector3 v) { for (int i = 0; i < size(); i++) { getPoint3(i).plusEquals(v); } } /** * translate negatively. does NOT modify this * * @param v vector to subtract * @return the NEW translated p3v */ public Point3Vector subtract(Vector3 v) { Vector3 v1 = v.negative(); Point3Vector temp = this.plus(v1); return temp; } /** * centre molecule on origin. translate to centroid MODIFIES PV */ public void moveToCentroid() { Point3 temp = this.getCentroid(); temp = temp.multiplyBy(-1.); this.plusEquals(new Vector3(temp)); } /** removes null values if both vectors have them at same position. * else throws exception * @param a * @param b */ public static void removeNullValues(Point3Vector a, Point3Vector b) { int n = a.size(); if (b.size() != n) { throw new EuclidRuntimeException("vectors of different sizes"); } for (int i = n-1; i >= 0; i--) { Point3 pa = a.elementAt(i); Point3 pb = b.elementAt(i); if (pa != null && pb != null) { } else if (pa == null && pb == null) { a.vector.remove(i); b.vector.remove(i); } else { throw new EuclidRuntimeException("unmatched null values at: "+i); } } } /** * get inertial tensor. (second moments) * * @return the inertial tensor */ public RealSquareMatrix calculateNonMassWeightedInertialTensorOld() { RealSquareMatrix tensor = new RealSquareMatrix(3); Point3 centre = this.getCentroid(); // subtract centroid from each coord and sum outerproduct of result for (int i = 0; i < size(); i++) { Point3 temp = new Point3(this.getPoint3(i).subtract(centre)); RealArray delta = new RealArray(3, temp.getArray()); RealSquareMatrix op = RealSquareMatrix.outerProduct(delta); tensor = tensor.plus(op); } return tensor; } public RealSquareMatrix calculateRotationToInertialAxes() { RealSquareMatrix inertialTensor = calculateNonMassWeightedInertialTensor(); RealSquareMatrix eigenvectors = inertialTensor.calculateEigenvectors(); if (eigenvectors != null) { // make sure axes are right-handed double determinant = eigenvectors.determinant(); if (determinant < 0.1) { RealSquareMatrix flip = new RealSquareMatrix( new double[][] { new double[] {1.0, 0.0, 0.0}, new double[] {0.0, -1.0, 0.0}, new double[] {0.0, 0.0, 1.0}, } ); eigenvectors = eigenvectors.multiply(flip); } eigenvectors = new RealSquareMatrix(eigenvectors.getTranspose()); } return eigenvectors; } /** * transform all coordinates. MODIFIES p3Vector * * @param t */ public void transform(Transform3 t) { for (int i = 0; i < size(); i++) { Point3 p = new Point3(getPoint3(i)); vector.set(i, p.transform(t)); } } /** * transform subset of coordinates - MODIFIES Vector * * @param t * @param is */ public void transform(Transform3 t, IntSet is) { int nis = is.size(); for (int j = 0; j < nis; j++) { int i = is.elementAt(j); if (i >= 0 && i < size()) { Point3 p = new Point3(getPoint3(i)); vector.set(i, p.transform(t)); } } } /** * get distance between 2 points. * * @param i1 * index of first point * @param i2 * index of second point * @return distance */ public double distance(int i1, int i2) { Vector3 v1 = getPoint3(i1).subtract(getPoint3(i2)); return v1.getLength(); } /** * get distance between 2 points. * * @param is * of exactly 2 integers * @exception EuclidRuntimeException * is must have exactly 2 points * @return distance */ public double distance(IntSet is) throws EuclidRuntimeException { if (is.size() != 2) { throw new EuclidRuntimeException("int set must have exactly 2 points"); } return distance(is.elementAt(0), is.elementAt(1)); } /** * get angle between 3 points. * * @param i1 * serial of first point * @param i2 * serial of second point * @param i3 * serial of third point * @exception EuclidRuntimeException * two points are coincident or identical * @return the angle */ public Angle angle(int i1, int i2, int i3) throws EuclidRuntimeException { Angle a; a = Point3.getAngle(getPoint3(i1), getPoint3(i2), getPoint3(i3)); return a; } /** * get angle between 3 points * * @param is * of exactly 3 integers * @exception EuclidRuntimeException * is must have exactly 3 points * @exception EuclidRuntimeException * two points are coincident or identical * @return the angle */ public Angle angle(IntSet is) throws EuclidRuntimeException { if (is.size() != 3) { throw new EuclidRuntimeException("size must be 3"); } return angle(is.elementAt(0), is.elementAt(1), is.elementAt(2)); } /** * get torsion angle between 4 points. * * @param i1 * serial of first point * @param i2 * serial of second point * @param i3 * serial of third point * @param i4 * serial of fourth point * @exception EuclidRuntimeException * either 2 points are identical or coincident or 3 * successive points are colinear * @return the angle */ public Angle torsion(int i1, int i2, int i3, int i4) throws EuclidRuntimeException { return Point3.getTorsion(getPoint3(i1), getPoint3(i2), getPoint3(i3), getPoint3(i4)); } /** * get torsion angle between 4 points. * * @param is * of exactly 3 integers * @exception EuclidRuntimeException * is must have exactly 4 points * @exception EuclidRuntimeException * either 2 points are identical or coincident or 3 * successive points are colinear * @return the angle */ public Angle torsion(IntSet is) throws EuclidRuntimeException { if (is.size() != 4) { throw new EuclidRuntimeException("size must be 4"); } return torsion(is.elementAt(0), is.elementAt(1), is.elementAt(2), is .elementAt(3)); } /** * distance matrix * * @return matrix */ public RealSquareMatrix getDistanceMatrix() { int size = this.size(); RealSquareMatrix distances = new RealSquareMatrix(size); double zero = 0.0; double[][] distanceMatrix = distances.getMatrix(); for (int i = 0; i < size; i++) { distanceMatrix[i][i] = zero; for (int j = i + 1; j < size; j++) { double distance = this.getPoint3(i).getDistanceFromPoint( this.getPoint3(j)); distanceMatrix[i][j] = distance; distanceMatrix[j][i] = distance; } } return distances; } public RealSquareMatrix calculateNonMassWeightedInertialTensor() { RealSquareMatrix rsm = new RealSquareMatrix(3); for (int i = 0; i < this.size(); i++) { Point3 p = this.get(i); double x = p.getArray()[0]; double y = p.getArray()[1]; double z = p.getArray()[2]; rsm.flmat[0][0] += y*y + z*z; rsm.flmat[1][1] += x*x + z*z; rsm.flmat[2][2] += y*y + x*x; rsm.flmat[0][1] += -x*y; rsm.flmat[0][2] += -x*z; rsm.flmat[1][2] += -y*z; rsm.flmat[1][0] = rsm.flmat[0][1]; rsm.flmat[2][0] = rsm.flmat[0][2]; rsm.flmat[2][1] = rsm.flmat[1][2]; } return rsm; } /** * get Inertial axes; do not throw exception for pathological cases, but * return it. Axes (lengths and unit vectors) are returned through the * arguments eigval and eigvect. OBSOLETE * NYI * @param eigval * @param eigvect * @param illCond * @exception EuclidRuntimeException * must have at least 3 points * @deprecated (doesn't work) */ public void inertialAxes(RealArray eigval, RealSquareMatrix eigvect, EuclidRuntimeException illCond) throws EuclidRuntimeException { RealArray val = new RealArray(3); RealSquareMatrix vect = new RealSquareMatrix(3); illCond = null; eigval.shallowCopy(val); eigvect.shallowCopy(vect); } /** * get best plane * * @return plane * * @exception EuclidRuntimeException * must have at least 3 points * @deprecated doesn't work */ public Plane3 bestPlane() throws EuclidRuntimeException { RealSquareMatrix eigvect = new RealSquareMatrix(3); RealArray eigval = new RealArray(3); EuclidRuntimeException illCond = null; inertialAxes(eigval, eigvect, illCond); // smallest eigenvalue RealArray temp = eigvect.extractRowData(2); Vector3 v = new Vector3(temp); // construct plane double dist = v.dot(getCentroid().getArray()); Plane3 p = new Plane3(v, dist); return p; } /** * get deviations of coordinates from plane * * @param p * @return deviations * */ public RealArray deviationsFromPlane(Plane3 p) { double[] farray = new double[size()]; for (int i = 0; i < size(); i++) { double dist = p.getVector().dot(getPoint3(i).getArray()); dist = dist - p.getDistance(); farray[i] = dist; } return new RealArray(farray); } /** * fit two coordinates of same length and alignment - quaternions */ // Transform3 newfitTo(Point3Vector c) { // // Uses Quaternions (method developed by Alan L. MacKay); // // // number of points must be the same // /* // if(size() != c.size()) { // Transform3 t; // return t; // } // Point3Vector c1 = c; // reference // Point3Vector pv2 = this; // moving molecule // c1.moveToCentroid(); // pv2.moveToCentroid(); // Point3Vector csum(count); // csum = c1 + pv2; // Point3Vector cdiff(count); // cdiff = c1 - pv2; // */ // // /** now set up the matrix elements*/ // /* // double mat[] = {0.,0.,0.,0., 0.,0.,0.,0., 0.,0.,0.,0., 0.,0.,0.,0.,}; // double[] psum = csum.flarray; // double[] pdiff = cdiff.flarray; // for (int i = 0; i < count; i++) { // double xp = *(psum); // double yp = *(psum + 1); // double zp = *(psum + 2); // // double xm = *(pdiff); // double ym = *(pdiff + 1); // double zm = *(pdiff + 2); // // mat[0] += xm*xm + ym*ym + zm*zm; // mat[1] += yp*zm - ym*zp; // mat[2] += xm*zp - xp*zm; // mat[3] += xp*ym - xm*yp; // // mat[5] += yp*yp + zp*zp + xm*xm; // mat[6] += xm*ym - xp*yp; // mat[7] += xm*zm - xp*zp; // // mat[10] += xp*xp + zp*zp + ym*ym; // mat[11] += ym*zm - yp*zp; // // mat[15] += xp*xp + yp*yp + zm*zm; // // psum++; pdiff++; // } // RealSquareMatrix s(4, mat); // // symmetrize matrix: upper to lower // // s.copyUpperToLower(); // // diagonalise // RealSquareMatrix evect(4); // RealArray eval(4); // s.diagonalise(eval, evect); // */ // // /*jacobi(mat,4,eval,evec,&nrot);*/ // // /*eigsrt(eval,evec,4);*/ // /* // return evect; // */ // return new Transform3(); // } /** * get a single point by REFERENCE * * @param i * @return point */ public Point3 getPoint3(int i) { return vector.get(i); } /** * get the coordinate coordinate array as doubles x,y,z,x,y,z, * * @return array */ public RealArray getXYZ() { double[] f = new double[3 * size()]; int count = 0; for (int i = 0; i < size(); i++) { double[] p = getPoint3(i).flarray; f[count++] = p[0]; f[count++] = p[1]; f[count++] = p[2]; } return new RealArray(f); } /** * get a single coordinate value * * @param i * @param j * @return coordinate */ public double getCoordinate(int i, Axis3 j) { return getPoint3(i).flarray[j.value]; } /** * get a single coordinate value; as above but not public */ double getCoordinate(int i, int j) { return getPoint3(i).flarray[j]; } /** * get a single coordinate array for example all x-coordinates * * @param axis * @return array */ public RealArray getXYZ(Axis3 axis) { double[] f = new double[size()]; for (int i = 0; i < size(); i++) { f[i] = getPoint3(i).flarray[axis.value]; } return new RealArray(f); } /** * rms between two molecules - per atom * * @param c * @return rms */ public double rms(Point3Vector c) { RealArray tt = getXYZ(); RealArray cc = c.getXYZ(); tt = tt.subtract(cc); return Math.sqrt(tt.innerProduct()) / (Double.valueOf(size())).doubleValue(); } /** * get point furthest from another. useful for alignments * * @param p * point to avoid (need not be in this) * @return the serial of the furthest point */ public int getFurthestPointFrom(Point3 p) { double d = -0.1; int serial = -1; for (int i = 0; i < this.size(); i++) { Point3 pp = vector.get(i); double dd = p.getDistanceFromPoint(pp); if (dd > d) { d = dd; serial = i; } } return serial; } /** * get point making smallest angle with two others. useful for alignments * * @param p1 * point to avoid (need not be in this) * @param p2 * point to avoid (need not be in this) * @return the serial of the point making smallest angle */ public int getPointMakingSmallestAngle(Point3 p1, Point3 p2) { double a = 999.; int serial = -1; for (int i = 0; i < this.size(); i++) { Point3 pp = vector.get(i); if (pp.isEqualTo(p1) || pp.isEqualTo(p2)) { continue; } try { Angle ang = Point3.getAngle(p1, pp, p2); double aa = ang.getAngle(); if (aa < a) { a = aa; serial = i; } } catch (Exception e) { ; } } return serial; } /** * fit two coordinates of same length and alignment. very approximate uses * get3SeparatedPoints() and then three find point1 furthest from centroid * find point2 furthest from point1 find point3 making smallest angle with * point1 and point2 m1 is moving molecule, m2 is fixed translate centroids * to match then rotate so that m1-point1 coincides with m2-point1 then * rotate so that m1-point2 coincides with m2-point2 This is rough, but will * be a good starting point for most molecules. *

* * @param ref * @return transformation * @exception EuclidRuntimeException * some unusual geometry (for example one coordinate set is * linear, has coincident points, etc.) */ public Transform3 alignUsing3Points(Point3Vector ref) throws EuclidRuntimeException { if (this.size() != ref.size()) { throw new EuclidRuntimeException("this and ref must be same size"); } if (this.size() < 3) { throw new EuclidRuntimeException("Need at least 3 points"); } int[] serial = get3SeparatedPoints(); Point3Vector thisVector = new Point3Vector(); Point3Vector refVector = new Point3Vector(); for (int i = 0; i < serial.length; i++) { thisVector.add(this.getPoint3(serial[i])); refVector.add(ref.getPoint3(serial[i])); } return thisVector.align3PointVectors(refVector); /*-- RealSquareMatrix unit = new RealSquareMatrix(3, new double[]{1., 0., 0., 0., 1., 0., 0., 0., 1.} ); Point3Vector moving = new Point3Vector(this); Point3 c1 = moving.getCentroid(); Point3 rc1 = ref.getCentroid(); Vector3 v12 = rc1.subtract(c1); Transform3 tr = new Transform3(unit, v12); moving.transform(tr); int i1 = moving.getFurthestPointFrom(c1); Point3 p1 = (Point3) moving.elementAt(i1); int i2 = moving.getFurthestPointFrom(p1); Point3 p2 = (Point3) moving.elementAt(i2); Point3 r1 = (Point3) ref.elementAt(i1); Point3 r2 = (Point3) ref.elementAt(i2); Vector3 p12 = p1.subtract(c1); Vector3 r12 = r1.subtract(rc1); Transform3 rot = new Transform3(p12, r12); moving.transform(rot); p1 = (Point3) moving.elementAt(i1); p2 = (Point3) moving.elementAt(i2); tr = rot.concatenate(tr); p12 = p1.subtract(p2); int i3 = moving.getPointMakingSmallestAngle(p1, p2); Point3 p3 = (Point3) moving.elementAt(i3); Point3 r3 = (Point3) ref.elementAt(i3); Vector3 p13 = p3.subtract(c1); Vector3 r13 = r3.subtract(rc1); Vector3 px = p12.cross(p13); px = px.normalize(); Vector3 rx = r12.cross(r13); rx = rx.normalize(); Transform3 rotx = new Transform3(px, rx); tr = rotx.concatenate(tr); return tr; --*/ } /** * get three widely separated points. useful for initial alignments p1 is * furthest from centroid p2 is furthest from p1 p3 is the point making * smallest value of p1-p3-p2 * * @return array with serial numbers of points * @exception EuclidRuntimeException * this or ref do not have 3 points or one has colinear or * coincident points */ public int[] get3SeparatedPoints() throws EuclidRuntimeException { int[] serial = new int[3]; Point3 c1 = this.getCentroid(); serial[0] = this.getFurthestPointFrom(c1); Point3 p1 = this.getPoint3(serial[0]); serial[1] = this.getFurthestPointFrom(p1); Point3 p2 = this.getPoint3(serial[1]); if (p1.isEqualTo(p2)) { throw new EuclidRuntimeException("Cannot find 3 separated points"); } serial[2] = this.getPointMakingSmallestAngle(p1, p2); Point3 p3 = this.getPoint3(serial[2]); if (p1.isEqualTo(p3) || p2.isEqualTo(p3)) { throw new EuclidRuntimeException("Cannot find 3 separated points"); } if ((p3.subtract(p1)).cross(p3.subtract(p2)).isZero()) { throw new EuclidRuntimeException("Cannot find 3 non-colinear points"); } return serial; } /** * fit two sets of three points. simple method. this and ref must each have * 3 points rotates p1-p2 vector in each to be parallel then rotates * p1-p2-p3 plane to be parallel This is rough, but will be a good starting * point for many systems, especially if get3SeparatedPoints is used * * no translation this is not changed * * @param ref * reference points * @return transform such that ttansform * this = ref * @exception EuclidRuntimeException * this or ref do not have 3 points or one has colinear or * coincident points */ public Transform3 align3PointVectors(Point3Vector ref) throws EuclidRuntimeException { if (this.size() != 3) { throw new EuclidRuntimeException("this requires 3 points"); } if (ref.size() != 3) { throw new EuclidRuntimeException("ref requires 3 points"); } RealSquareMatrix unit = new RealSquareMatrix(3, new double[] { 1., 0., 0., 0., 1., 0., 0., 0., 1. }); Transform3 overallTransform = new Transform3(unit); // copy this Point3Vector moving = new Point3Vector(this); Point3 thisCentroid = moving.getCentroid(); Point3 refCentroid = ref.getCentroid(); Point3 p1 = moving.getPoint3(0); Point3 p2 = moving.getPoint3(1); Point3 r1 = ref.getPoint3(0); Vector3 p12 = p1.subtract(thisCentroid); Vector3 r12 = r1.subtract(refCentroid); // align p1-p2 vectors Transform3 rot = new Transform3(p12, r12); moving.transform(rot); p1 = moving.getPoint3(0); p2 = moving.getPoint3(1); overallTransform = rot.concatenate(overallTransform); p12 = p1.subtract(p2); Point3 p3 = moving.getPoint3(2); Point3 r3 = ref.getPoint3(2); Vector3 p13 = p3.subtract(thisCentroid); Vector3 r13 = r3.subtract(refCentroid); // get normals Vector3 px = p12.cross(p13); px = px.normalize(); Vector3 rx = r12.cross(r13); rx = rx.normalize(); // and align Transform3 rotx = new Transform3(px, rx); overallTransform = rotx.concatenate(overallTransform); return overallTransform; } /** * fit two coordinates of same length and alignment. * * rough method . * fit 'this' to ref ('this' is moving molecule, ref is fixed) * take copies and refer each to their centroid as origin. * pick three points in ref which are well separated * Find the plane of these three and use the normal, * together with the vector to one of the points, to define two coordinate axes. * Calculate the third axis as right-handed orthogonal (check). * This gives a pure rotation matrix (Tref) * using the corresponding points in 'this' repeat to give Tthis * Tranformation matrix is then * Tthis(T). Tref. * Defining the intercentroid vector as Tthis2ref = centRef - centThis * we have * Tfinal = Tthis2Ref . Tthis(T) . Tref . (-Tthis2Ref) * This is rough, but will be a good starting point for many systems. * * @param ref * @return transformation * @exception EuclidRuntimeException * some unusual geometry (for example one coordinate set is * linear, has coincident points, etc.) */ public Transform3 roughAlign(Point3Vector ref) throws EuclidRuntimeException { // int[] points; int nn = ref.size(); if (nn != size()) { throw new EuclidRuntimeException("arrays of different lengths: "+this.size()+"/"+nn); } if (nn < 3) { throw new EuclidRuntimeException("must have 3 points to align: "+this.size()+"/"+nn); } Point3 centThis = this.getCentroid(); Point3 centRef = ref.getCentroid(); Transform3 r = fit3Points(ref); return translateRotateRetranslate(centThis, centRef, r); } private Transform3 fit3Points(Point3Vector ref) { int[] points; Point3Vector pvThis = new Point3Vector(this); pvThis.moveToCentroid(); Point3Vector pvRef = new Point3Vector(ref); pvRef.moveToCentroid(); points = this.get3SeparatedPoints(); Transform3 tThis = getTransformOfPlane(points, pvThis); Transform3 tRef = getTransformOfPlane(points, pvRef); tRef.transpose(); Transform3 r = tRef.concatenate(tThis); return r; } private Transform3 translateRotateRetranslate(Point3 centThis, Point3 centRef, Transform3 rotate) { Vector3 this2Origv = new Vector3(centThis.multiplyBy(-1.0)); Transform3 trans2Orig = new Transform3(this2Origv); Transform3 trans1 = rotate.concatenate(trans2Orig); Vector3 orig2Refv = new Vector3(centRef); Transform3 orig2Ref = new Transform3(orig2Refv); Transform3 finalT = orig2Ref.concatenate(trans1); return finalT; } private Transform3 getTransformOfPlane(int[] ii, Point3Vector pv) { Plane3 p = new Plane3(pv.getPoint3(ii[0]), pv.getPoint3(ii[1]), pv .getPoint3(ii[2])); // get reference point in each plane Vector3 v = new Vector3(pv.getPoint3(ii[0])).normalize(); // and form axes: Vector3 w = p.getVector().cross(v).normalize(); // form the two sets of axes Vector3 vv = v.cross(w); Transform3 t = new Transform3(vv, v, w); return t; } // private void eigenvectorFit(Point3Vector pv2) { // RealSquareMatrix eigvec = new RealSquareMatrix(4); // RealArray eigval = new RealArray(4); // EuclidRuntimeException illCond = null; // pv2.inertialAxes(eigval, eigvec, illCond); // LOG.debug("EIG "+eigval+"/"+eigvec); // if (illCond != null) { // throw illCond; // } // Transform3 axes = new Transform3(eigvec); // LOG.debug("AX "+axes); // axes.transpose(); // Point3Vector pv22 = new Point3Vector(pv2); // LOG.debug("PVV "+pv22); // pv22.transform(axes); // LOG.debug("PVVZZ "+pv22); // } /** * fit two coordinates of same length and alignment * * CURRENTLY NOT VALIDATED * * @param ref * @return transformation * @exception EuclidRuntimeException * some unusual geometry (for example one coordinate set is * linear, has coincident points, etc.) */ public Transform3 fitTo(Point3Vector ref) throws EuclidRuntimeException { // these should be set as parameters? double damp = 1.0; double converge = 0.0002; Point3 thisCent = this.getCentroid(); Point3 refCent = ref.getCentroid(); /** * make copies of each molecule and translate to centroid */ Point3Vector thistmp = new Point3Vector(this); Point3Vector reftmp = new Point3Vector(ref); thistmp.moveToCentroid(); reftmp.moveToCentroid(); /** * roughly rotate moving molecule onto reference one */ Transform3 t = thistmp.roughAlign(reftmp); thistmp.transform(t); RealArray shift = new RealArray(3); int NCYC = 20; for (int icyc = 0; icyc < NCYC; icyc++) { double maxshift = 0.0; /** * loop through x,y,z */ for (int jax0 = 0; jax0 < 3; jax0++) { int jax1 = (jax0 + 1) % 3; int jax2 = (jax1 + 1) % 3; double rh = 0.0; double lh = 0.0; for (int ipt = 0; ipt < size(); ipt++) { double refj1 = reftmp.getCoordinate(ipt, jax1); double refj2 = reftmp.getCoordinate(ipt, jax2); double movj1 = thistmp.getCoordinate(ipt, jax1); double movj2 = thistmp.getCoordinate(ipt, jax2); lh += refj1 * refj1 + refj2 * refj2; rh += refj1 * (refj2 - movj2) - refj2 * (refj1 - movj1); } /** * get shifts */ double sft = -(damp * (rh / lh)); maxshift = Math.max(maxshift, Math.abs(sft)); shift.setElementAt(jax0, sft); } /** * break out if converged */ if (maxshift < converge) { // LOG.debug("CONVERGED"); break; } else if (maxshift < 0.1) { // not yet used damp = 1.0; } else if (maxshift > 0.1) { // not yet used damp = 1.0; } // LOG.debug("CYCLE"); /** * make transformation matrix by rotations about 3 axes */ Transform3 t1 = new Transform3( new Angle(shift.elementAt(0)), new Angle(shift.elementAt(1)), new Angle(shift.elementAt(2))); thistmp.transform(t1); /** * concatenate transformations */ t = new Transform3(t1.concatenate(t)); } Transform3 tt = translateRotateRetranslate(thisCent, refCent, t); return tt; } /** * to string. * * @return string */ public String toString() { StringBuffer sb = new StringBuffer(); sb.append(S_LBRAK); for (int i = 0; i < size(); i++) { sb.append(get(i).toString()); sb.append(S_NEWLINE); } sb.append(S_RBRAK); return sb.toString(); } public RealArray extractRealArray() { return new RealArray(getArray()); } public Double innerProduct() { return extractRealArray().innerProduct(); } } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/Polar.java000066400000000000000000000105611461721410700240060ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid; /** * Polar coordinates (r, theta) * * @author (C) P. Murray-Rust, 1996 */ public class Polar implements EuclidConstants { /** * the radius from the origin */ double r = 0.0; /** * the angle with the X-axis (anticlockwise) */ double theta = 0.0; /** * constructor. */ public Polar() { super(); } /** * constructor. * * @param x * @param y */ public Polar(double x, double y) { super(); r = Math.sqrt(x * x + y * y); theta = Math.atan2(y, x); } /** * constructor. * * @param a * @param b */ public Polar(double a, Angle b) { r = a; theta = b.getAngle(); } /** * constructor. * * @param c */ public Polar(Complex c) { r = c.getR(); theta = c.getTheta().getAngle(); } /** * constructor. * * @param a */ public Polar(Polar a) { r = a.r; theta = a.theta; } /** * gets radial part * * @return radius */ public double getR() { return r; } /** * gets angular part * * @return angle */ public Angle getTheta() { return new Angle(theta); } /** * add two polars * * @param a2 * @return new polar */ public Polar plus(Polar a2) { Complex tmp = new Complex(getX() + a2.getX(), getY() + a2.getY()); Polar temp = new Polar(tmp); return temp; } /** * subtract two polars * * @param a2 * @return new polar */ public Polar subtract(Polar a2) { Complex tmp = new Complex(getX() - a2.getX(), getY() - a2.getY()); Polar temp = new Polar(tmp); return temp; } /** * unary minus */ public void subtract() { theta = theta + Math.PI; } /** * multiply a polar by a polar * * @param f * @return new polar */ public Polar multiplyBy(Polar f) { Polar temp = new Polar(this); temp.r = r * f.r; temp.theta = theta + f.theta; return temp; } /** * multiply a polar by a scalar * * @param f * @return new polar */ public Polar multiplyBy(double f) { Polar temp = new Polar(this); temp.r *= f; return temp; } /** * divide a polar by a polar * * @param f * @return polar * @throws EuclidRuntimeException */ public Polar divideBy(Polar f) throws EuclidRuntimeException { Polar temp = new Polar(this); if (Real.isZero(f.r, Real.getEpsilon())) { throw new EuclidRuntimeException(); } temp.r = r / f.r; temp.theta = theta - f.theta; return temp; } /** * are two polar equal * * @param a * @return equals */ public boolean isEqualTo(Polar a) { return Real.isEqual(r, a.r) && Real.isEqual(theta, a.theta); } /** * get X, Y and XY coords * * @return coord */ public double getX() { double temp = r * Math.cos(theta); return temp; } /** * get y. * * @return coord */ public double getY() { double temp = r * Math.sin(theta); return temp; } /** * get coordinates. * * @return coordinates */ public Real2 getXY() { return new Real2(r * Math.cos(theta), r * Math.sin(theta)); } /** * to string. * * @return string */ public String toString() { StringBuffer sb = new StringBuffer(); sb.append("Polar: " + r + EC.S_COMMA + theta); return sb.toString(); } } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/RandomNumberGenerator.java000066400000000000000000000235601461721410700271740ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid; /** * @(#)RandomNumberGenerator.java * * Copyright (c) 2000 by Sundar Dorai-Raj * * @author Sundar Dorai-Raj * * Email: sdoraira@vt.edu * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License * * as published by the Free Software Foundation; either version 2 * * of the License, or (at your option) any later version, * * provided that any use properly credits the author. * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details at http://www.gnu.org * * */ import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; /* * Reference. * M. Matsumoto and T. Nishimura, * "Mersenne Twister: A 623-Dimensionally Equidistributed Uniform * Pseudo-Random Number Generator", * ACM Transactions on Modeling and Computer Simulation, * Vol. 8, No. 1, January 1998, pp 3--30. */ public class RandomNumberGenerator implements java.io.Serializable { static final long serialVersionUID = 3905348978240129619L; // Period parameters private static final int N = 624; private static final int M = 397; private static final int MATRIX_A = 0x9908b0df; private static final int UPPER_MASK = 0x80000000; private static final int LOWER_MASK = 0x7fffffff; // Tempering parameters private static final int TEMPERING_MASK_B = 0x9d2c5680; private static final int TEMPERING_MASK_C = 0xefc60000; private int mt[]; // the array for the state vector private int mti; // mti==N+1 means mt[N] is not initialized private int mag01[]; // a good initial seed (of int size, though stored in a long) @SuppressWarnings("unused") private static final long GOOD_SEED = 4357; private synchronized void writeObject(ObjectOutputStream out) throws IOException { // just so we're synchronized. out.defaultWriteObject(); } private synchronized void readObject (ObjectInputStream in) throws IOException, ClassNotFoundException { // just so we're synchronized. in.defaultReadObject(); } protected synchronized void setSeed(long seed) { haveNextGaussian = false; mt = new int[N]; // setting initial seeds to mt[N] using // the generator Line 25 of Table 1 in // [KNUTH 1981, The Art of Computer Programming // Vol. 2 (2nd Ed.), pp102] // the 0xffffffff is commented out because in Java // ints are always 32 bits; hence i & 0xffffffff == i mt[0]= ((int)seed); // & 0xffffffff; for(mti = 1; mti < N; mti++) mt[mti] = (69069 * mt[mti-1]); // mag01[x] = x * MATRIX_A for x=0,1 mag01 = new int[2]; mag01[0] = 0x0; mag01[1] = MATRIX_A; } protected synchronized int next(int bits) { int y; if(mti >= N) { int kk; for(kk = 0; kk < N - M; kk++) { y = (mt[kk] & UPPER_MASK) | (mt[kk+1] & LOWER_MASK); mt[kk] = mt[kk+M] ^ (y >>> 1) ^ mag01[y & 0x1]; } for(; kk < N-1; kk++) { y = (mt[kk] & UPPER_MASK) | (mt[kk+1] & LOWER_MASK); mt[kk] = mt[kk+(M-N)] ^ (y >>> 1) ^ mag01[y & 0x1]; } y = (mt[N-1] & UPPER_MASK) | (mt[0] & LOWER_MASK); mt[N-1] = mt[M-1] ^ (y >>> 1) ^ mag01[y & 0x1]; mti = 0; } y = mt[mti++]; y ^= y >>> 11; y ^= (y << 7) & TEMPERING_MASK_B; y ^= (y << 15) & TEMPERING_MASK_C; y ^= (y >>> 18); return y >>> (32 - bits); } public RandomNumberGenerator() { this(System.currentTimeMillis()); } public RandomNumberGenerator(long seed) { setSeed(seed); } // generate integers between 0 and 2^32 public synchronized int nextInt() { return next(32); } // generate integers between 0 and n-1 (inclusive) public synchronized int nextInt(int n) { if (n <= 0) throw new IllegalArgumentException( "n must be positive" ); if ((n & -n) == n) { // i.e., n is a power of 2 return (int)((n * (long)next(31)) >> 31); } int bits, val; do { bits = next(31); val = bits % n; } while(bits - val + (n-1) < 0); return val; } // generate Poisson(lambda) // E(X)=lambda ; Var(X)=lambda public synchronized int nextPoisson(double lambda) { int v=-1; double l=Math.exp(-lambda),p; p=1.0; while (p>=l) { p*=nextUniform(); v++; } return v; } // generate Poisson(1) // E(X)=1 ; Var(X)=1 public synchronized int nextPoisson() { return nextPoisson(1); } // generate random boolean variables public synchronized boolean nextBoolean() { return (next(32) & 1 << 15) != 0; } // generates true with probability p public synchronized boolean nextBoolean(double p) { double u=nextUniform(); if(u < p) return true; return false; } // generate U(0,1) // E(X)=1/2 ; Var(X)=1/12 public synchronized double nextUniform() { long l = ((long)(next(26)) << 27) + next(27); return l / (double)(1L << 53); } // generate U(a,b) // E(X)=(b-a)/2 ; Var(X)=(b-a)^2/12 public synchronized double nextUniform(double a,double b) { return a + (b-a)*nextUniform(); } private double nextGaussian; private boolean haveNextGaussian = false; // generate N(0,1) // E(X)=1 ; Var(X)=1 public synchronized double nextGaussian() { if (!haveNextGaussian) { double v1=nextUniform(),v2=nextUniform(); double x1,x2; x1=Math.sqrt(-2*Math.log(v1))*Math.cos(2*Math.PI*v2); x2=Math.sqrt(-2*Math.log(v1))*Math.sin(2*Math.PI*v2); nextGaussian=x2; haveNextGaussian=true; return x1; } else { haveNextGaussian=false; return nextGaussian; } } // generate N(m,s2) // E(X)=m ; Var(X)=s2 public synchronized double nextGaussian(double m,double s2) { return nextGaussian()*Math.sqrt(s2)+m; } // generate Gamma(1,1) // E(X)=1 ; Var(X)=1 public synchronized double nextGamma() { return nextGamma(1,1,0); } // generate Gamma(alpha,beta) // E(X)=alpha*beta ; Var(X)=alpha*beta^2 public synchronized double nextGamma(double alpha,double beta) { return nextGamma(alpha,beta,0); } // generate shifted-Gamma(alpha,beta) // E(X)=alpha*beta+lambda ; Var(X)=alpha*beta^2 public synchronized double nextGamma(double alpha,double beta,double lambda) { double gamma=0; if(alpha <= 0 || beta <= 0) throw new IllegalArgumentException("alpha and beta must be strictly positive."); if (alpha < 1) { double b,p; boolean flag=false; b=1+alpha*Math.exp(-1); while(!flag) { p=b*nextUniform(); if (p>1) { gamma=-Math.log((b-p)/alpha); if (nextUniform()<=Math.pow(gamma,alpha-1)) flag=true; } else { gamma=Math.pow(p,1/alpha); if (nextUniform()<=Math.exp(-gamma)) flag=true; } } } else if (alpha == 1) gamma=-Math.log(nextUniform()); else { double y=-Math.log(nextUniform()); while (nextUniform()>Math.pow(y*Math.exp(1-y),alpha-1)) y=-Math.log(nextUniform()); gamma=alpha*y; } return beta*gamma+lambda; } // generate Exp(1) // E(X)=1 ; Var(X)=1 public synchronized double nextExp() { return nextGamma(1,1,0); } // generate Exp(beta) // E(X)=beta ; Var(X)=beta^2 public synchronized double nextExp(double beta) { return nextGamma(1,beta,0); } // generate shifted-Exp(beta) // E(X)=beta+lambda ; Var(X)=beta^2 public synchronized double nextExp(double beta,double lambda) { return nextGamma(1,beta,lambda); } // generate ChiSq(1) // E(X)=1 ; Var(X)=2 public synchronized double nextChiSq() { return nextGamma(0.5,2,0); } // generate ChiSq(df) // E(X)=df ; Var(X)=2*df public synchronized double nextChiSq(int df) { return nextGamma(0.5*(double)df,2,0); } // generate shifted-ChiSq(df) // E(X)=df+lambda ; Var(X)=2*df public synchronized double nextChiSq(int df,double lambda) { return nextGamma(0.5*(double)df,2,lambda); } // generate Beta(alpha,beta) // E(X)=a/(a+b) ; Var(X)=ab/[(a+b+1)(a+b)^2] public synchronized double nextBeta(double alpha,double beta) { if(alpha <= 0 || beta <=0) throw new IllegalArgumentException("alpha and beta must be strictly positive."); if (alpha == 1 && beta == 1) return nextUniform(); else if (alpha >= 1 && beta >=1) { double A=alpha-1, B=beta-1, C=A+B, L=C*Math.log(C), mu=A/C, sigma=0.5/Math.sqrt(C); double y=nextGaussian(),x=sigma*y+mu; while (x < 0 || x > 1) { y=nextGaussian(); x=sigma*y+mu; } double u=nextUniform(); while (Math.log(u) >= A*Math.log(x/A)+B*Math.log((1-x)/B)+L+0.5*y*y) { y=nextGaussian(); x=sigma*y+mu; while (x < 0 || x > 1) { y=nextGaussian(); x=sigma*y+mu; } u=nextUniform(); } return x; } else { double v1=Math.pow(nextUniform(),1/alpha), v2=Math.pow(nextUniform(),1/beta); while (v1+v2>1) { v1=Math.pow(nextUniform(),1/alpha); v2=Math.pow(nextUniform(),1/beta); } return v1/(v1+v2); } } } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/Real.java000066400000000000000000000206161461721410700236160ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid; import java.util.regex.Pattern; import org.apache.log4j.Logger; /** * Real supports various utilities for real numbers Use Double where you want a * first-class Java object * * @author (C) P. Murray-Rust, 1996 */ public abstract class Real implements EuclidConstants { final static Logger LOG = Logger.getLogger(Real.class); /** standard for equality of numbers */ static double epsx = 0.0000000001; /** * get current version of epsilon. * * @return maximum difference between numbers */ public static double getEpsilon() { return epsx; } /** * set current version of epsilon. * * @param epsilon * will be used in any implicit comparison until reset * */ public static void setEpsilon(double epsilon) { epsx = epsilon; } /** truncate to given number of decimals. * forms nint(d * 10^ndec)/10^ndec * @param d to truncate * @param ndec * @return */ public static double normalize(double d, int ndec) { int dd = 1; for (int i = 0; i < ndec; i++) { dd *= 10; } return ((double) Math.round(d * (double)dd)) / (double) dd; } /** * are two numbers equal within epsx. * * @param a * number * @param b * number * @return true if a equals b within epsilon * */ public static boolean isEqual(Double a, Double b) { return a != null && b != null && Math.abs(a - b) < epsx; } /** * is a number zero within epsx * * @param a * number * @return true if a is zero within epsilon * * @deprecated use epsilon method */ public static boolean isZero(Double a) { return a != null && Real.isZero(a, epsx); } /** * are all members of an array equal within epsilon. * * @param n * length of array * @param a * first array * @param b * first array * @param epsilon * difference * @return true is all arrays are of equals lengths and members are equal * within epsilon * * @deprecated omit n */ public static boolean isEqual(int n, double[] a, double[] b, double epsilon) { if (a == null || b == null ) return false; if (a.length != b.length) { return false; } for (int i = 0; i < n; i++) { if (!Real.isEqual(a[i], b[i], epsilon)) return false; } return true; } /** * are all members of an array equal within epsilon. * * @param a first array * @param b first array * @param epsilon difference * @return true is all arrays are of equals lengths and members are equal * within epsilon * */ public static boolean isEqual(double[] a, double[] b, double epsilon) { if (a == null || b == null || a.length != b.length) { return false; } for (int i = 0; i < a.length; i++) { if (!Real.isEqual(a[i], b[i], epsilon)) return false; } return true; } /** * are all members of an array equal within epsx * * @param n length of array * @param a first array * @param b first array * @return true is all arrays are of equals lengths and members are equal * within epsilon * @deprecated use epsilon method */ public static boolean isEqual(int n, double[] a, double b[]) { return isEqual(n, a, b, epsx); } /** * are two numbers equal within epsilon * * @param a * number * @param b * number * @param epsilon * difference * @return true if a equals b within epsilon */ public static boolean isEqual(Double a, Double b, double epsilon) { return a != null && b != null && Math.abs(a - b) < epsilon; } /** * is a number zero within epsilon * * @param a * number * * @param epsilon * difference * * @return true if a is zero within epsilon * */ /** * A regular expression match a number pattern. */ public static final String SCIENTIFIC_PARSE = "(?:[+-]?(?:(?:\\d*(?:\\.?\\d+)?)|(?:\\d+(?:\\.?\\d*)?))(?:[EeDdGgHh][+-]?\\d+[dDfF]?)?)"; // sign? | number | exponent? | precision? /** * A compiled Pattern object which matches a number pattern. */ public static final Pattern SCIENTIFIC_PATTERN = Pattern.compile(SCIENTIFIC_PARSE); /** * Parse a string to double value, similar to Double.parseDouble function, but * also try to parse against FORTRAN number, e.g. 12.3D-05. * *

If the return value is Double.NaN, a RuntimeException is thrown. * This should not happen anyway. * * @author (C) Weerapong Phadungsukanan, 2009 * @param db String of number value * @return double value of the given String * @throws NullPointerException if db is null. * @throws NumberFormatException if db is not a number. */ public static double parseDouble(String db) { double d = Double.NaN; db = db.trim(); // Try to parse string using java routine first. If it does not match // the string could be number in FORTRAN format, [DdGgHh] as exponential // notation. So we replace the first exponential notation by E and then // try to parse again with java routine. The two steps are necessary and // cannot be removed otherwise the number such as 12.0d will not be parsed. try { d = Double.parseDouble(db); } catch (NumberFormatException nfe) { d = Double.parseDouble(db.replaceFirst("[DdGgHh]", "E")); } if (d == Double.NaN) throw new RuntimeException("Cannot parse {" + db + "} as double and cannot throw NumberFormatException. This is a program bug."); return d; } public static boolean isZero(double a, double epsilon) { return Math.abs(a) < epsilon; } /** * is a less than epsx less than b * * @param a * number * * @param b * number * * @return true if a {@literal <} b within epsx * */ public static boolean isLessThan(double a, double b) { return ((b - a) > epsx); } /** * is a more than epsx greater than b * * @param a * number * * @param b * number * * @return true if a {@literal >} b within epsx * */ public static boolean isGreaterThan(double a, double b) { return ((a - b) > epsx); } /** * set an array to zero * * @param nelem * length of array * * @param arr * array * */ public static void zeroArray(int nelem, double[] arr) { for (int i = 0; i < nelem; i++) { arr[i] = 0.0; } } /** * set an array to given value * * @param nelem * length of array * * @param arr * array * * @param f * the value * */ public static void initArray(int nelem, double[] arr, double f) { for (int i = 0; i < nelem; i++) { arr[i] = f; } } /** * print a double[] * * @param a * array * */ public static void printArray(double[] a) { for (int i = 0; i < a.length; i++) { LOG.info(a[i] + S_SPACE); } LOG.info(""); } } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/Real2.java000066400000000000000000000426671461721410700237120ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid; import java.util.List; import org.apache.log4j.Logger; /** * A pair of FPt numbers with no other assumptions * * Contains two doubles Can be used as it is, but there are some specialised * derived classes (for example Complex (a complex number), Point2 (x,y coords), * etc), The default value is 0.0, 0.0. * * @author (C) P. Murray-Rust, 1996 */ public class Real2 implements EuclidConstants { private static Logger LOG = Logger.getLogger(Real2.class); /** the first floating point value */ public double x; /** the second floating point value */ public double y; /** * constructor. */ public Real2() { x = 0.0; y = 0.0; } /** * constructor. * * @param x the first floating point value * @param y the second floating point value */ public Real2(double x, double y) { this.x = x; this.y = y; } /** * constructor. * * @param x the first integer value * @param y the second integer value */ public Real2(int x, int y) { this((double) x, (double) y); } /** * constructor. * * @param int2 An object of type Int2 containing two integer values */ public Real2(Int2 int2) { this(int2.getX(), int2.getY()); } /** * constructor. * * @param dd * @throws RuntimeException null or not length=2 */ /** * two reals separated by whitespace * OR * (real, real) * * @param s the string containing two reals separated by whitespace */ public Real2(String s) { this(Util.splitToDoubleArray(s, S_SPACE)); } /** * two integers separated by delimiter * @param s the string containing two integers separated by delimiter * @param delimiter the delimiter */ public Real2(String s, String delimiter) { this(Util.splitToDoubleArray(s, delimiter)); } /** * constructor. * * @param x the two components */ public Real2(double[] x) { if (x == null) { throw new EuclidRuntimeException("requires non-null array"); } if (x.length != 2) { throw new EuclidRuntimeException("requires array of length 2; found: "+x.length); } this.x = x[0]; this.y = x[1]; } /** reads output format from toString() * "(x,y)" * @param s string representation of real, "(x,y)" * @return */ public static Real2 createFromString(String s) { Real2 real2 = null; if (s != null) { s = s.trim(); if (s.startsWith(S_LBRAK) && s.endsWith(S_RBRAK)) { real2 = new Real2(s.substring(1, s.length()-1), S_COMMA); } } return real2; } /** * copy constructor * * @param r the real object to be copied */ public Real2(Real2 r) { this.x = r.x; this.y = r.y; } /** * swaps the x and y values */ public void swap() { double t = x; x = y; y = t; } /** * sorts x and y so that x {@literal <}= y */ public void sortAscending() { if (x > y) this.swap(); } /** * sorts x and y so that x {@literal >}= y */ public void sortDescending() { if (x < y) this.swap(); } /** * set to 0 0 */ public void clear() { x = y = 0.0; } /** * set x. * * @param xx floating point x value to be set */ public void setX(double xx) { x = xx; } /** * set y. * * @param yy floating point y value to be set */ public void setY(double yy) { y = yy; } /** * equality. * * @param r to test * @return equals * @deprecated */ public boolean isEqualTo(Real2 r) { return (Real.isEqual(x, r.x) && Real.isEqual(y, r.y)); } /** * equality. * * @param r to test * @param eps tolerance * @return equals */ public boolean isEqualTo(Real2 r, double eps) { return (Real.isEqual(x, r.x, eps) && Real.isEqual(y, r.y, eps)); } /** * is the point the origin. * * @return true if origin * @deprecated USE epsilon method */ public boolean isOrigin() { return Real.isZero(x, Real.getEpsilon()) && Real.isZero(y, Real.getEpsilon()); } /** * is the point the origin. * @param epsilon Epsilon * @return true if origin */ public boolean isOrigin(double epsilon) { return Real.isZero(x, epsilon) && Real.isZero(y, epsilon); } /** * add two points to give vector sum * * @param r2 the specified real to be added with this real * @return point * */ public Real2 plus(Real2 r2) { return new Real2(x + r2.x, y + r2.y); } /** * add two points to give vector sum * * @param r2 the specified real to be added and assigned with this real * */ public void plusEquals(Real2 r2) { this.x = x + r2.x; this.y = y + r2.y; } /** * subtract two points to give vector difference * * @param r2 the specified real to be subtracted from this real * @return point * */ public Real2 subtract(Real2 r2) { return new Real2(x - r2.x, y - r2.y); } /** * multiply both components by minus one MODIFIES 'this' */ public void negative() { this.x = -this.x; this.y = -this.y; } /** * retuen multiplication of a point by a scalar * does not alter this * @param f a floating point scalar value * @return point */ public Real2 multiplyBy(double f) { return new Real2(x * f, y * f); } /** * multiply a point by a scalar * alters this * @param f a floating point scalar value */ public void multiplyEquals(double f) { this.x = x * f; this.y = y * f; } /** * get X value * * @return x value */ public double getX() { return x; } /** * get Y value * * @return y value */ public double getY() { return y; } /** * get either value counts from ZERO * * @param elem index of element * @return value * @throws EuclidRuntimeException */ public double elementAt(int elem) throws EuclidRuntimeException { if (elem == 0) { return x; } else if (elem == 1) { return y; } throw new EuclidRuntimeException("bad index " + elem); } /** * get length of Real2 if centered on origin * * @return length */ public double getLength() { return Math.sqrt(x * x + y * y); } /** * get distance to another Real2 * * @param r the specified real to be measured against this real * @return distance */ public double getDistance(Real2 r) { return (r == null) ? Double.NaN : Math.sqrt((x - r.x) * (x - r.x) + (y - r.y) * (y - r.y)); } /** * get squared distance to another Real2 * * @param r the specified real to be measured against this real * @return dist2 */ public double getSquaredDistance(Real2 r) { return (r == null) ? Double.NaN : (x - r.x) * (x - r.x) + (y - r.y) * (y - r.y); } /** * point midway between 'this' and 'p' * * @param p the specified real to be measured against this real * @return midpoint */ public Real2 getMidPoint(Real2 p) { return (p == null) ? null : new Real2((this.x + p.x) / 2.0, (this.y + p.y) / 2.0); } /** * get unit vector * * @return unit vector * @exception EuclidRuntimeException * this was of zero length */ public Real2 getUnitVector() { double length = this.getLength(); if (Real.isZero(length, Real.getEpsilon())) { throw new EuclidRuntimeException("zero length vector"); } Real2 temp = new Real2(this.x, this.y); temp = temp.multiplyBy(1.0 / length); return temp; } /** * get dot product * * @param r the specified real to be measured against this real * @return dot */ public double dotProduct(Real2 r) { return (this.x * r.x + this.y * r.y); } /** * get angle between 3 Real2s (the second is in the centre) * * @param p1 point 1 * @param p2 point 2 * @param p3 point 3 * @return angle is CLOCKWISE from p1 to p3 */ public static Angle getAngle(Real2 p1, Real2 p2, Real2 p3) { if (p1 == null || p2 == null || p3 == null) { throw new RuntimeException("null coordinates"); } double x1 = p1.x - p2.x; double y1 = p1.y - p2.y; double x3 = p3.x - p2.x; double y3 = p3.y - p2.y; double angle1 = Math.atan2(x1, y1); double angle3 = Math.atan2(x3, y3); double angle13 = angle1 - angle3; Angle angle123 = new Angle(angle13, Angle.Units.RADIANS); double d12 = p1.getDistance(p2); double d13 = p1.getDistance(p3); double d23 = p2.getDistance(p3); double cost = (-d13*d13 + d12*d12 + d23*d23)/(2*d12*d23); double anglex = Math.acos(cost); LOG.trace("AAA "+anglex+"/"+angle13+"//"+p1+"/"+p2+"/"+p3); return angle123; } /** * transforms the point by a rot-trans matrix MODIFIES 'this' Note the next * routine is often better * * @param t rot-trans matrix * */ public void transformBy(Transform2 t) { double xx = t.flmat[0][0] * this.x + t.flmat[0][1] * this.y + t.flmat[0][2]; double yy = t.flmat[1][0] * this.x + t.flmat[1][1] * this.y + t.flmat[1][2]; this.x = xx; this.y = yy; } /** * gets a point transformed by a rot-trans matrix does NOT MODIFY 'this' * * @param t rot-trans matrix * @return point */ public Real2 getTransformed(Transform2 t) { Real2 p = new Real2(this); p.transformBy(t); return p; } /** * creates a polygon * * returns a point array from two points Serial numbers of points are 0 and * end that is point1 is points[0] and point2 is points[end] if end == 0, * end ={@literal >}1 * * @param point1 starting point * @param nPoints number of points in the resulting polygon * @param point2 ending point * @param end index of point2 in points array * @param repelPoint repel point * @return points * */ public static Real2[] addPolygonOnLine(Real2 point1, Real2 point2, int nPoints, int end, Real2 repelPoint) { if (end < 1) end = 1; Real2[] newPoints = new Real2[nPoints]; Real2 mid = point1.getMidPoint(point2); double dTheta = Math.PI * 2.0 / (double) nPoints; double lineTheta = (point1.subtract(point2)).getAngle(); double line2 = (point1.subtract(mid)).getLength(); // this accounts for number of points skipped double halfTheta = 0.5 * (dTheta * (double) end); // find centre double dist = line2 / Math.tan(halfTheta); // as far as possible from repelPoint double angle = lineTheta - 0.5 * Math.PI; Real2 center0 = mid.makePoint(dist, angle); Real2 center = center0; // this mess takes care of direction double theta0 = -halfTheta + angle + Math.PI; double theta = theta0; if (repelPoint != null) { double tempDist = (center.subtract(repelPoint)).getLength(); Real2 center1 = mid.makePoint(dist, angle + Math.PI); // swap it, change direction of traverse if ((center1.subtract(repelPoint)).getLength() > tempDist) { center = center1; dTheta = -dTheta; theta = +halfTheta + angle; } } // radius of polygon double rad = line2 / Math.sin(halfTheta); // now add points newPoints = makePoints(center, nPoints, rad, theta, dTheta); // for even-number polygons exactly bisected the repel cannot work on // centers so // also required for centroids on re-entrant nuclei // we examine the newly created points if (repelPoint != null /* && 2*end == nPoints */) { double dista = newPoints[1].subtract(repelPoint).getLength(); double distb = newPoints[nPoints - 2].subtract(repelPoint) .getLength(); // LOG.debug(""+dista+S_SLASH+distb+"/"+repelPoint+"/"+newPoints[1]+"/"+newPoints[nPoints-2]); if (dista > distb) { // logger.info("SWAP"); center = center0; dTheta = -dTheta; theta = theta0; newPoints = makePoints(center, nPoints, rad, theta, dTheta); } else { // LOG.debug("NOSWAP"); } } return newPoints; } private static Real2[] makePoints(Real2 center, int nPoints, double rad, double theta, double dTheta) { Real2[] points = new Real2[nPoints]; for (int i = 0; i < nPoints; i++) { points[i] = center.makePoint(rad, theta); theta += dTheta; } return points; } /** * get angle between origin and this point (i.e polar coords) - uses atan2 * (that is anticlockwise from X axis); if x == y == 0.0 presumably returns * NaN * * @return angle * */ public double getAngle() { return Math.atan2(y, x); } /** * make a new point at (dist, theta) from this * * @param rad * the distance to new point * @param theta * the angle to new point (anticlockwise from X axis), that is * theta=0 gives (rad, 0), theta=PI/2 gives (0, rad) * @return Real2 new point */ public Real2 makePoint(double rad, double theta) { Real2 point = new Real2(); point.x = this.x + rad * Math.cos(theta); point.y = this.y + rad * Math.sin(theta); return point; } /** * get centroid of all points. does not modify this * * @param p2Vector list of all real points * @return the centroid */ public static Real2 getCentroid(List p2Vector) { if (p2Vector.size() < 1) return null; Real2 p = new Real2(); for (int j = 0; j < p2Vector.size(); j++) { p = p.plus(p2Vector.get(j)); } double scale = 1.0 / (Double.valueOf(p2Vector.size()).doubleValue()); p = p.multiplyBy(scale); return p; } /** * get serialNumber of nearest point * * @param p2v list of all real points * @param point point wrt which serialNumber is calculated * @return serial */ public static int getSerialOfNearestPoint(List p2v, Real2 point) { double dist = Double.MAX_VALUE; int serial = -1; for (int j = 0; j < p2v.size(); j++) { double d = p2v.get(j).subtract(point).getLength(); if (d < dist) { serial = j; dist = d; } } return serial; } /** * get rectangular distance matrix. * * matrix need not be square or symmetric unless coords == coords2 d(ij) is * distance between coord(i) and coords2(j). * * @param coords1 first coordinate of the rectangle * @param coords2 second coordinate of the rectangle * @return distance matrix */ public static RealMatrix getDistanceMatrix(List coords1, List coords2) { int size = coords1.size(); int size2 = coords2.size(); RealMatrix distMatrix = new RealMatrix(size, size2); double[][] distMatrixMat = distMatrix.getMatrix(); for (int i = 0; i < size; i++) { Real2 ri = coords1.get(i); for (int j = 0; j < size2; j++) { Real2 rj = coords2.get(j); double dij = ri.getDistance(rj); distMatrixMat[i][j] = dij; } } return distMatrix; } /** * to string. * * @return string */ public String toString() { return S_LBRAK + x + S_COMMA + y + S_RBRAK; } /** * get x y as array. * * @return array of length 2 */ public double[] getXY() { double[] dd = new double[2]; dd[0] = x; dd[1] = y; return dd; } /** round to decimal places. * * @param places max number of decimals (3 = ****.ddd) * @return this */ public Real2 format(int places) { x = Util.format(x, places); y = Util.format(y, places); return this; } } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/Real2Array.java000066400000000000000000000342711461721410700247010ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.log4j.Logger; /** * Real2Array is NOT a Vector of Real2s but a container for a 2 * n matrix * * with a variety of ways of managing the data including RealArrays for the x * and y arrays, and also an array of Real2s The latter is only stored if * required, and then is cached. * * Note that we also have Real2Vector which acts like a List{@literal (Real2)}. We may * therefore obsolete the Real2Array at some time - it is not used in Jumbo * elsewhere although it might be useful for spectra. * * NOTE: I think it's used quite a lot in fact... * * @author P.Murray-Rust, Copyright 1997 */ public class Real2Array implements EuclidConstants , Iterable { @SuppressWarnings("unused") private static Logger LOG = Logger.getLogger(Real2Array.class); RealArray xarr; RealArray yarr; int nelem = 0; /** * default constructor gives an array of 0 points */ public Real2Array() { } /** * get max and min value of Real2_Array * * @return range */ public Real2Range getRange2() { Real2Range range = new Real2Range(); for (int i = 0; i < nelem; i++) { Real2 r2 = new Real2(xarr.elementAt(i), yarr.elementAt(i)); range.add(r2); } return range; } /** copy * * @param r2a */ public Real2Array(Real2Array r2a) { if (r2a != null && !r2a.equals(this)) { xarr = new RealArray(r2a.getXArray()); yarr = new RealArray(r2a.getYArray()); this.nelem = r2a.nelem; } } /** * make an Real2_Array from 2 RealVec's; takes a copy * * @param x * @param y * @exception EuclidRuntimeException * x and x must have number of elements */ public Real2Array(RealArray x, RealArray y) throws EuclidRuntimeException { if (x.size() != y.size()) { throw new EuclidRuntimeException("incompatible array sizes "+x.size()+"/"+y.size()); } nelem = x.size(); xarr = (RealArray) x.clone(); yarr = (RealArray) y.clone(); } /** create with RealArrays of pre-allocated size. * * @param size */ public Real2Array(int size) { this(new RealArray(size), new RealArray(size)); } /** create from list of points. * * @param points */ public Real2Array(List points) { for (Real2 point : points) { this.add(point); } } /** * compares the arrays * @param r2b array to compare to * @param epsilon tolerance in coordinates * @return true if all points agree within epsilon */ public boolean isEqualTo(Real2Array r2b, double epsilon) { if (r2b == null || this.size() != r2b.size()) { return false; } for (int i = 0; i < this.size(); i++) { Real2 ra = this.get(i); Real2 rb = r2b.get(i); if (!ra.isEqualTo(rb, epsilon)) { return false; } } return true; } /** * @param r2 */ public void add(Real2 r2) { if (nelem == 0 || xarr == null || yarr == null) { xarr = new RealArray(); yarr = new RealArray(); } xarr.addElement(r2.getX()); yarr.addElement(r2.getY()); nelem++; } /** * @param real2Array */ public void add(Real2Array real2Array) { if (real2Array != null) { if (nelem == 0 || xarr == null || yarr == null) { xarr = new RealArray(); yarr = new RealArray(); } xarr.addArray(real2Array.xarr); yarr.addArray(real2Array.yarr); nelem += real2Array.size(); } } /** * make an Real2_Array from pairs of numbers separated by delimiter * * @param sss * @param delimiter (might be a regex e.g. ",| " (comma or space)) * @exception EuclidRuntimeException * x and x must have number of elements */ public static Real2Array createFromPairs(String sss, String delimiter) { Real2Array real2Array = null; if (sss != null) { String[] ss = sss.trim().split(delimiter); if (ss.length % 2 == 0) { RealArray realArray = new RealArray(ss); real2Array = Real2Array.createFromPairs(realArray); } } return real2Array; } /** * make an Real2_Array from pairs of numbers x1,y1 .. x2,y2 .. etc * * @param ra * @exception EuclidRuntimeException * x and x must have number of elements */ public static Real2Array createFromPairs(RealArray ra) { if (ra == null) { throw new RuntimeException("Null RealArray"); } if (ra.size() % 2 != 0) { throw new RuntimeException("Must have even number of points"); } Real2Array real2Array = new Real2Array(); real2Array.xarr = new RealArray(); real2Array.yarr = new RealArray(); for (int i = 0; i < ra.size(); ) { real2Array.xarr.addElement(ra.elementAt(i++)); real2Array.yarr.addElement(ra.elementAt(i++)); real2Array.nelem++; } return real2Array; } /** * extract X array. * * @return array */ public RealArray getXArray() { return xarr; } /** * extract Y array. * * @return array */ public RealArray getYArray() { return yarr; } /** * size of array. * * @return size */ public int size() { return nelem; } /** * get element. * * @param elem * @return element */ public Real2 elementAt(int elem) { return new Real2(xarr.elementAt(elem), yarr.elementAt(elem)); } /** * get element. * * @param elem * @return element */ public Real2 get(int elem) { return new Real2(xarr.elementAt(elem), yarr.elementAt(elem)); } /** * get element. * * @param elem * @param r2 */ public void setElement(int elem, Real2 r2) { xarr.setElementAt(elem, r2.getX()); yarr.setElementAt(elem, r2.getY()); } /** delete element. * * @param i */ public void deleteElement(int i) { if (i >= 0 && i < nelem) { xarr.deleteElement(i); yarr.deleteElement(i); nelem--; } else { throw new EuclidRuntimeException("Cannt delete element at: "+i); } } public void transformBy(Transform2 t2) { for (int i = 0; i < nelem; i++) { Real2 xy = this.get(i); xy.transformBy(t2); this.setElement(i, xy); } } /** round to decimal places. * * @param places * @return this */ public Real2Array format(int places) { double[] xarray = xarr.getArray(); double[] yarray = yarr.getArray(); for (int i = 0; i < nelem; i++) { xarray[i] = Util.format(xarray[i], places); yarray[i] = Util.format(yarray[i], places); } return this; } /** * to space-separated string. * * @return string */ public String getStringArray() { // don't change this routine!!! StringBuffer s = new StringBuffer(); double[] xarray = (xarr == null) ? null : xarr.getArray(); double[] yarray = (yarr == null) ? null : yarr.getArray(); for (int i = 0; i < nelem; i++) { if (i > 0) { s.append(S_SPACE); } s.append(xarray[i]); s.append(S_SPACE); s.append(yarray[i]); } return s.toString(); } /** * to string. * * @return string */ public String toString() { // don't change this routine!!! StringBuffer s = new StringBuffer(); double[] xarray = (xarr == null) ? null : xarr.getArray(); double[] yarray = (yarr == null) ? null : yarr.getArray(); s.append(S_LBRAK); for (int i = 0; i < nelem; i++) { s.append(S_LBRAK); s.append(xarray[i]); s.append(S_COMMA); s.append(yarray[i]); s.append(S_RBRAK); } s.append(S_RBRAK); return s.toString(); } public Real2 getLastElement() { Real2 xy = null; if (nelem > 0) { xy = get(nelem-1); } return xy; } /** reverse direction of array MODIFIES THIS * */ public void reverse() { xarr.reverse(); yarr.reverse(); } /** * sorts so that x0y0 has lowest x (0) or y (1) * @param xy = 0 sort on x; xy = 1 sort on y */ public void sortAscending(int xy) { IntSet is = null; if (xy == 0) { is = xarr.indexSortAscending(); } else if (xy == 1) { is = yarr.indexSortAscending(); } xarr = xarr.createReorderedArray(is); yarr = yarr.createReorderedArray(is); } /** * sorts so that x0y0 has highest x (0) or y (1) * @param xy = 0 sort on x; xy = 1 sort on y */ public void sortDescending(int xy) { IntSet is = null; if (xy == 0) { is = xarr.indexSortDescending(); } else if (xy == 1) { is = yarr.indexSortDescending(); } xarr = xarr.createReorderedArray(is); yarr = yarr.createReorderedArray(is); } /** create subArray starting at start inclusive * * @param start * @return */ public Real2Array createSubArray(int start) { return createSubArray(start, xarr.size()-1); } /** create subArray * * @param start inclusive * @param end inclusive * @return */ public Real2Array createSubArray(int start, int end) { RealArray xarr = this.xarr.getSubArray(start, end); RealArray yarr = this.yarr.getSubArray(start, end); return new Real2Array(xarr, yarr); } public static Real2Array createFromCoords(String coords) { Real2Array real2Array = null; if (coords != null) { coords = coords.trim(); if (coords.startsWith(EuclidConstants.S_LBRAK) && coords.endsWith(EuclidConstants.S_RBRAK)) { real2Array = new Real2Array(); coords = coords.substring(1, coords.length()-1); Pattern COORD_PATTERN = Pattern.compile("\\(([0-9\\.\\-\\+]+)\\,([0-9\\.\\-\\+]+)\\)"); Matcher matcher = COORD_PATTERN.matcher(coords); while (matcher.find()) { try { double dd[] = new double[2]; dd[0] = Util.parseFlexibleDouble(matcher.group(1)); dd[1] = Util.parseFlexibleDouble(matcher.group(2)); Real2 coord = new Real2(dd); real2Array.add(coord); } catch (Exception e) { LOG.trace("bad coord "+e); real2Array = null; break; } } } } return real2Array; } public Real2 getPointWithMinimumX() { int idx = getIndexOfPointWithMinimumX(); return idx == -1 ? null : get(idx); } private int getIndexOfPointWithMinimumX() { RealArray xArray = getXArray(); return (xArray == null || xArray.size() == 0) ? -1 : xArray.indexOfSmallestElement(); } public Real2 getPointWithMaximumX() { int idx = getIndexOfPointWithMaximumX(); return idx == -1 ? null : get(idx); } private int getIndexOfPointWithMaximumX() { RealArray xArray = getXArray(); return (xArray == null || xArray.size() == 0) ? -1 : xArray.indexOfSmallestElement(); } public Real2 getPointWithMinimumY() { int idx = getIndexOfPointWithMinimumY(); return idx == -1 ? null : get(idx); } private int getIndexOfPointWithMinimumY() { RealArray yArray = getYArray(); return (yArray == null || yArray.size() == 0) ? -1 : yArray.indexOfSmallestElement(); } public Real2 getPointWithMaximumY() { int idx = getIndexOfPointWithMaximumY(); return idx == -1 ? null : get(idx); } private int getIndexOfPointWithMaximumY() { RealArray yArray = getYArray(); return (yArray == null || yArray.size() == 0) ? -1 : yArray.indexOfSmallestElement(); } /** gets average of two Real2Arrays. * * @param real2Array * @return null if arrays are null or of different sizes */ public Real2Array getMidPointArray(Real2Array real2Array) { Real2Array new2Array = null; if (real2Array != null && this.nelem == real2Array.nelem) { RealArray newXArray = (this.xarr.plus(real2Array.xarr)).multiplyBy(0.5); RealArray newYArray = (this.yarr.plus(real2Array.yarr)).multiplyBy(0.5); new2Array = new Real2Array(newXArray, newYArray); } return new2Array; } /** gets unweighted mean of points. * * @return null if no points */ public Real2 getMean() { Real2 mean = null; if (xarr != null && nelem > 0) { double xMean = xarr.getMean(); double yMean = yarr.getMean(); mean = new Real2(xMean, yMean); } return mean; } public Iterator iterator() { Real2Iterator iterator = (xarr == null || yarr == null || xarr.size() != yarr.size()) ? null : new Real2Iterator(this); return iterator; } public Double sumProductOfAllElements() { return (xarr == null || yarr == null) ? null : xarr.sumProductOfAllElements(yarr); } public Real2 getLastPoint() { return nelem == 0 ? null : new Real2(xarr.elementAt(nelem-1), yarr.elementAt(nelem-1)); } public List getList() { List points = new ArrayList(); for (int i = 0; i < nelem; i++) { points.add(new Real2(xarr.elementAt(i), yarr.elementAt(i))); } return points; } } class Real2Iterator implements Iterator { private Iterator xIterator; private Iterator yIterator; public Real2Iterator(Real2Array real2Array) { this.xIterator = real2Array.xarr.iterator(); this.yIterator = real2Array.yarr.iterator(); } public boolean hasNext() { return xIterator.hasNext() && yIterator.hasNext(); } public Real2 next() { Double x = xIterator.next(); Double y = yIterator.next(); return (x == null || y == null) ? null : new Real2(x, y); } public void remove() { throw new UnsupportedOperationException(); } } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/Real2Interval.java000066400000000000000000000103661461721410700254060ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid; /** a two dimensional area. * may have negative ranges * @author pm286 * */ public class Real2Interval implements EuclidConstants { RealInterval xInterval; RealInterval yInterval; /** constructor. * defaults to 0,1 0,1 * */ public Real2Interval() { this(new RealInterval(), new RealInterval()); } /** constructor from 1-D intervals. * * @param xInterval * @param yInterval */ public Real2Interval(RealInterval xInterval, RealInterval yInterval) { this.xInterval = new RealInterval(xInterval); this.yInterval = new RealInterval(yInterval); } /** copy constructor. * * @param xyInterval */ public Real2Interval(Real2Interval xyInterval) { this(xyInterval.xInterval, xyInterval.yInterval); } /** constructor from range. * interval direction is therefore always positive * @param range2 */ public Real2Interval(Real2Range range2) { this(new RealInterval(range2.getXRange()), new RealInterval(range2.getYRange())); } /** get x and y scales to other interval2. * exact scaling * @param interval2 * @return scales never null but component(s) may be NaN */ public double[] scalesTo(Real2Interval interval2) { double[] scales = new double[2]; scales[0] = xInterval.scaleTo(interval2.xInterval); scales[1] = yInterval.scaleTo(interval2.yInterval); return scales; } /** get isotropic scale to other interval2. * takes minimum value of x and y scales * @param interval2 * @return scale may be NaN */ public double scaleTo(Real2Interval interval2) { double scale = Double.NaN; double[] scales = scalesTo(interval2); if (Double.isNaN(scales[0])) { scale = scales[1]; } else if (Double.isNaN(scales[1])) { scale = scales[0]; } else { scale = Math.min(scales[0], scales[0]); } return scale; } /** offsets to overlap origins in each * scales these by scales and then calculates offsets to overlap midpoints * @param interval2 * @param scales (some might be NaN) * @return offsets (components might be NaN. */ public double[] offsetsTo(Real2Interval interval2, double[] scales) { double[] offsets = new double[2]; offsets[0] = xInterval.offsetTo(interval2.xInterval, scales[0]); offsets[1] = yInterval.offsetTo(interval2.yInterval, scales[1]); return offsets; } /** offsets to overlap origins in each * isotropic scales * scales these by scale * @param interval2 * @param scale * @return offsets (components might be NaN. */ public double[] offsetsTo(Real2Interval interval2, double scale) { return offsetsTo(interval2, new double[]{scale, scale}); } /** offset to map one interval onto another. * exact mapping in both directions * @param interval2 * @return offset applied after scaling */ public double[] offsetsTo(Real2Interval interval2) { double[] offsets = new double[2]; offsets[0] = xInterval.offsetTo(interval2.xInterval); offsets[1] = yInterval.offsetTo(interval2.yInterval); return offsets; } /** gets midpoint. * * @return midpoint of both axes */ public Real2 midPoint() { return new Real2(xInterval.midPoint(), yInterval.midPoint()); } // /** offsets to overlap given points in each // * scales these by scales and then calculates offsets to overlap midpoints // * @param interval2 // * @param scales (some might be NaN) // * @return offsets (components might be NaN. // */ // public double[] offsetsTo(Real2Interval interval2, double scale, Real2 xyThis, Real2 xy2) { // double[] offsets = new double[2]; // offsets[0] = xInterval.offsetTo(interval2.xInterval, scales[0]); // offsets[1] = yInterval.offsetTo(interval2.yInterval, scales[1]); // return offsets; // } } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/Real2Range.java000066400000000000000000000325211461721410700246530ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid; import java.awt.Dimension; import java.util.List; import org.xmlcml.euclid.Axis.Axis2; import org.xmlcml.euclid.RealRange.Direction; /** * 2-D double limits Contains two RealRanges. Can therefore be used to describe * 2-dimensional limits (for example axes of graphs, rectangles in graphics, * limits of a molecule, etc.) *

* Default is two default/invalid RealRange components. Adding points will * create valid ranges. * * @author (C) P. Murray-Rust, 1996 */ public class Real2Range implements EuclidConstants { public enum BoxDirection { LEFT, RIGHT, TOP, BOTTOM } /** * X-range */ RealRange xrange; /** * Y-range */ RealRange yrange; /** * constructor. * Creates INVALID range */ public Real2Range() { } /** * initialise with min and max values; * * @param xr * @param yr */ public Real2Range(RealRange xr, RealRange yr) { if (xr.isValid() && yr.isValid()) { xrange = xr; yrange = yr; } } /** create from min corner of box and max corner * * @param r2a * @param r2b */ public Real2Range(Real2 r2a, Real2 r2b) { double x0 = r2a.getX(); double x1 = r2b.getX(); xrange = new RealRange(Math.min(x0, x1), Math.max(x0, x1)); double y0 = r2a.getY(); double y1 = r2b.getY(); yrange = new RealRange(Math.min(y0, y1), Math.max(y0, y1)); } /** * copy constructor * * @param r */ public Real2Range(Real2Range r) { if (r.isValid()) { xrange = new RealRange(r.xrange); yrange = new RealRange(r.yrange); } } /** * reads in format of toString() * ((a,b)(c,d)) * @param s */ public static Real2Range createFrom(String s) { if (s == null) { return null; } if (s.startsWith(S_LBRAK+S_LBRAK) && s.endsWith(S_RBRAK+S_RBRAK)) { String ss = s.substring(2, s.length()-2); int i = ss.indexOf(S_RBRAK); int j = ss.indexOf(S_LBRAK); if (i == -1 || j == -1 || i+2 != j) { throw new RuntimeException("Bad Real2Range syntax: "+s); } RealRange xr = getRealRange(ss.substring(0, i)); RealRange yr = getRealRange(ss.substring(j+1)); if (xr == null || yr == null) { throw new RuntimeException("Bad Real2Range syntax: "+s); } return new Real2Range(xr, yr); } else { throw new RuntimeException("Bad Real2Range syntax: "+s); } } private static RealRange getRealRange(String s) { RealArray xa = new RealArray(s.replaceAll(S_COMMA, S_SPACE)); return (xa.size() == 2) ? new RealRange(xa.get(0), xa.get(1)) : null; } /** * a Real2Range is valid if both its constituent ranges are * * @return valid */ public boolean isValid() { return (xrange != null && yrange != null && xrange.isValid() && yrange.isValid()); } /** * is equals to. * * @param r2 * @return tru if equal */ @Deprecated public boolean isEqualTo(Real2Range r2) { if (isValid() && r2 != null && r2.isValid()) { return (xrange.isEqualTo(r2.xrange) && yrange.isEqualTo(r2.yrange)); } else { return false; } } /** * is equals to. * * @param r2 * @return tru if equal */ public boolean isEqualTo(Real2Range r2, double eps) { if (isValid() && r2 != null && r2.isValid()) { return (xrange.isEqualTo(r2.xrange, eps) && yrange.isEqualTo(r2.yrange, eps)); } else { return false; } } /** * merge two ranges and take the maximum extents * not sure the logic is right * if this is INVALID then replace with r2 * if this is VALID and r2 is INVALID return this * else return union of the two * @param r2 * @return range */ public Real2Range plus(Real2Range r2) { if (!isValid()) { if (r2 == null || !r2.isValid()) { return new Real2Range(); } else { return new Real2Range(r2); } } if (r2 == null || !r2.isValid()) { return new Real2Range(this); } return new Real2Range(xrange.plus(r2.xrange), yrange.plus(r2.yrange)); } public Real2Range plusEquals(Real2Range r2) { if (r2 != null) { xrange = (xrange == null) ? r2.xrange : xrange.plus(r2.xrange); yrange = (yrange == null) ? r2.yrange : yrange.plus(r2.yrange); } return this; } /** * intersect two ranges and take the range common to both; return invalid * range if no overlap or either is null/invalid * * @param r2 * @return range * */ public Real2Range intersectionWith(Real2Range r2) { if (!isValid() || r2 == null || !r2.isValid()) { return new Real2Range(); } RealRange xr = this.getXRange().intersectionWith(r2.getXRange()); RealRange yr = this.getYRange().intersectionWith(r2.getYRange()); return (xr == null || yr == null) ? null : new Real2Range(xr, yr); } /** * get xrange * * @return range */ public RealRange getXRange() { return xrange; } /** * get yrange * * @return range */ public RealRange getYRange() { return yrange; } public void setXRange(RealRange xrange) { this.xrange = xrange; } public void setYRange(RealRange yrange) { this.yrange = yrange; } /** * get yrange * * @return range */ public Real2 getCentroid() { return new Real2(xrange.getMidPoint(), yrange.getMidPoint()); } /** gets lower left and upper right. * @return minx,miny ... maxx, maxy */ public Real2[] getCorners() { Real2[] rr = null; if (xrange != null && yrange != null) { rr = new Real2[2]; rr[0] = new Real2(xrange.getMin(), yrange.getMin()); rr[1] = new Real2(xrange.getMax(), yrange.getMax()); } return rr; } /** * is an Real2 within a Real2Range * * @param p * @return includes */ public boolean includes(Real2 p) { if (!isValid()) { return false; } return (xrange.includes(p.getX()) && yrange.includes(p.getY())); } /** * is one Real2Range completely within another * * @param r * @return includes */ public boolean includes(Real2Range r) { if (!isValid() || r == null || !r.isValid()) { return false; } RealRange xr = r.getXRange(); RealRange yr = r.getYRange(); return (xrange.includes(xr) && yrange.includes(yr)); } /** * add a Real2 to a range * * @param p */ public void add(Real2 p) { if (p == null) return; if (xrange == null) xrange = new RealRange(); if (yrange == null) yrange = new RealRange(); xrange.add(p.getX()); yrange.add(p.getY()); } /** * merge range for given axis. * * @param ax * axis * @param range */ public void add(Axis2 ax, RealRange range) { if (range == null) return; if (ax.equals(Axis2.X)) { if (xrange == null) { xrange = new RealRange(); } xrange = xrange.plus(range); } if (ax.equals(Axis2.Y)) { if (yrange == null) { yrange = new RealRange(); } yrange = yrange.plus(range); } } /** gets minimum X and Y translations required to move point into range * uses RealRange.distanceOutside() - see this * @param p * @return null if p == null or has bad coordinates; Real2(0,0) if in or on range, else translations to touch range */ public Real2 distanceOutside(Real2 p) { Real2 r2 = null; if (p != null) { double dx = xrange.distanceOutside(p.getX()); double dy = yrange.distanceOutside(p.getY()); if (!Double.isNaN(dx) && !Double.isNaN(dy)) { r2 = new Real2(dx, dy); } } return r2; } /** transform range (as copy) * * @param t2 * @return new Range */ public Real2Range getTranformedRange(Transform2 t2) { RealRange xRange = this.getXRange(); RealRange yRange = this.getYRange(); Real2 xyMin = new Real2(xRange.getMin(), yRange.getMin()); xyMin.transformBy(t2); Real2 xyMax = new Real2(xRange.getMax(), yRange.getMax()); xyMax.transformBy(t2); return new Real2Range(xyMin, xyMax); } /** * to string. * * @return string */ public String toString() { String xx = (xrange == null) ? "NULL" : xrange.toString(); String yy = (yrange == null) ? "NULL" : yrange.toString(); return EC.S_LBRAK + xx + EC.S_COMMA + yy + EC.S_RBRAK; } public Real2Range format(Integer decimalPlaces) { if (xrange != null && yrange != null) { xrange = xrange.format(decimalPlaces); yrange = yrange.format(decimalPlaces); } return this; } public static boolean isNull(Real2Range r2r) { return r2r == null || (r2r.getXRange() == null && r2r.getYRange() == null); } public boolean isHorizontal() { return xrange != null && yrange != null && xrange.getRange() > yrange.getRange(); } public boolean isVertical() { return xrange != null && yrange != null && xrange.getRange() < yrange.getRange(); } /** aspect ratio * * @return xrange / yrange */ public Double getHorizontalVerticalRatio() { Double ratio = null; if (xrange != null && yrange != null) { ratio = xrange.getRange() / yrange.getRange(); } return ratio; } public RealRange getRealRange(Direction direction) { RealRange range = null; if (Direction.HORIZONTAL.equals(direction)) { range = getXRange(); } else if (Direction.VERTICAL.equals(direction)) { range = getYRange(); } return range; } /** iterates through all boxes and return true r2r is contained in any range * * @param r2rList * @return */ public boolean isContainedInAnyRange(List r2rList) { if (r2rList != null) { for (Real2Range r2r : r2rList) { if (r2r.includes(this)) { return true; } } } return false; } public Dimension getDimension() { return new Dimension((int) getXRange().getRange(), (int) getYRange().getRange()); } public Double getXMin() { return xrange == null ? null : xrange.getMin(); } public Double getXMax() { return xrange == null ? null : xrange.getMax(); } public Double getYMin() { return yrange == null ? null : yrange.getMin(); } public Double getYMax() { return yrange == null ? null : yrange.getMax(); } /** extends XRange. * * does not alter this. Uses range.extendBy(). Positive numbers will expand the range * * @param leftSide * @param rightSide */ public Real2Range getReal2RangeExtendedInX(double leftSide, double rightSide) { Real2Range r2r = new Real2Range(this); if (r2r.xrange != null) { r2r.xrange = r2r.xrange.getRangeExtendedBy(leftSide, rightSide); } return r2r; } /** extends XRange. * * does not alter this. Uses range.extendBy(). Positive numbers will expand the range * * @param topExtend * @param bottomExtend */ public Real2Range getReal2RangeExtendedInY(double topExtend, double bottomExtend) { Real2Range r2r = new Real2Range(this); if (r2r.yrange != null) { r2r.yrange = r2r.yrange.getRangeExtendedBy(topExtend, bottomExtend); } return r2r; } public static void format(List boxList, int nplaces) { for (Real2Range box : boxList) { box.format(nplaces); } } public Double calculateArea() { return (xrange == null || yrange == null) ? null : xrange.getRange() * yrange.getRange(); } /** returns midPoint of box edge. * * @param direction * @return mid point */ public Real2 getMidPoint(BoxDirection direction) { Real2 midPoint = null; if (BoxDirection.TOP.equals(direction)) { midPoint = new Real2(getXRange().getMidPoint() , getYMax()); } else if (BoxDirection.BOTTOM.equals(direction)) { midPoint = new Real2(getXRange().getMidPoint() , getYMin()); } else if (BoxDirection.LEFT.equals(direction)) { midPoint = new Real2(getXMin(), getYRange().getMidPoint()); } else if (BoxDirection.RIGHT.equals(direction)) { midPoint = new Real2(getXMax(), getYRange().getMidPoint()); } return midPoint; } /** compares boxes independent of origin. * * @param bbox * @return */ public boolean isLessThan(Real2Range bbox) { return (bbox != null) && xrange.isLessThan(bbox.getXRange()) && yrange.isLessThan(bbox.getYRange()); } } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/Real2RangeComparator.java000077500000000000000000000053151461721410700267070ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid; import java.util.Comparator; import org.apache.log4j.Logger; /** comparator for use with {@code TreeSet} and other tools which normally require equals(). * * @author pm286 * */ public class Real2RangeComparator implements Comparator { private final static Logger LOG = Logger.getLogger(Real2RangeComparator.class); private RealRangeComparator comparatorx; private RealRangeComparator comparatory; public Real2RangeComparator(RealRangeComparator comparator) { this.setComparators(comparator, comparator); } public Real2RangeComparator(RealRangeComparator comparatorx, RealRangeComparator comparatory) { this.setComparators(comparatorx, comparatory); } public Real2RangeComparator(double d) { this(new RealRangeComparator(d)); } /** * if Math.abs(d0-d1) {@literal <}= epsilon * return -1 if either arg is null or any ranges in r0 or r1 are null or comparisons clash */ public int compare(Real2Range r0, Real2Range r1) { Real2Range limit = new Real2Range(new RealRange(0.,240.), new RealRange(0.,60.)); String s1 = "(56.689,231.236),(31.643,49.536)"; // (56.689,231.236),(31.643,49.536) if (r0.toString().contains(s1) || r1.toString().contains(s1)) { LOG.trace(r0+" / "+r1); } if (r0 == null || r1 == null) { return -1; } RealRange r0x = r0.getXRange(); RealRange r0y = r0.getYRange(); RealRange r1x = r1.getXRange(); RealRange r1y = r1.getYRange(); if (r0x == null || r1x == null || r0y == null || r1y == null) { return -1; } int comparex = comparatorx.compare(r0x, r1x); int comparey = comparatory.compare(r0y, r1y); if (limit.includes(r0) && limit.includes(r1)) { // if (comparex * comparey == 0) { LOG.trace(r0+" / "+r1+" / "+comparex+"/"+comparey); } return (comparex == comparey) ? comparex : -1; } /** set the tolerance * negative values are converted to positive * @param comparatorx * @param comparatory */ public void setComparators(RealRangeComparator comparatorx, RealRangeComparator comparatory) { this.comparatorx = comparatorx; this.comparatory = comparatory; } } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/Real2Vector.java000066400000000000000000000545721461721410700250730ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid; import java.util.ArrayList; import java.util.List; import org.xmlcml.euclid.Axis.Axis2; /** * a (Java) Vector of Real2s (Note that 'Vector' is used by Java to describe an * array of objects - there is no relationship to geometrical vectors in this * package.) *

* Support is also given for the two component arrays as RealArrays *

* Default is an empty (Java) Vector; * * @author (C) P. Murray-Rust, 1996 */ public class Real2Vector implements EuclidConstants { List vector; /** * Comment for serialVersionUID */ private static final long serialVersionUID = 3834026952770990647L; /** * constructor. */ public Real2Vector() { vector = new ArrayList(); } /** * Formed by feeding in an existing array to a 2xn matrix THE COLUMN IS THE * FASTEST MOVING INDEX, that is the matrix is filled as flarray(0,0, * flarray(0,1). Primarily for compatibility with other apps * * @param flarray * @exception EuclidRuntimeException * array must have even number of elements * */ public Real2Vector(double[] flarray) throws EuclidRuntimeException { this(); int count = 0; int n = flarray.length / 2; if (flarray.length != 2 * n) { throw new EuclidRuntimeException("size must be multiple of 2"); } for (int i = 0; i < n; i++) { Real2 p = new Real2(flarray[count++], flarray[count++]); vector.add(p); } } /** * from two parallel arrays of x, y - by REFERENCE. * * @param n * @param x * @param y * @throws EuclidRuntimeException * */ public Real2Vector(int n, double[] x, double[] y) throws EuclidRuntimeException { this(); Util.check(x, n); Util.check(y, n); for (int i = 0; i < n; i++) { vector.add(new Real2(x[i], y[i])); } } /** conversion routine. * * @param r2a */ public Real2Vector(Real2Array r2a) { this(r2a.size(), r2a.getXArray().getArray(), r2a.getYArray().getArray()); } /** * constructor from RealArray - by REFERENCE * * @param m * @exception EuclidRuntimeException * array must have even number of elements */ public Real2Vector(RealArray m) throws EuclidRuntimeException { this(); int count = m.size() / 2; if (m.size() != count * 2) { throw new EuclidRuntimeException("size must be multiple of 2"); } double[] marray = m.getArray(); int j = 0; for (int i = 0; i < count; i++) { vector.add(new Real2(marray[j++], marray[j++])); } } /** * copy constructor from Real2Vector COPIES pv * * @param pv */ public Real2Vector(Real2Vector pv) { this(); for (int i = 0; i < pv.size(); i++) { vector.add(new Real2(pv.get(i))); } } /** * construct from list of Real2. * * @param rList * list */ public Real2Vector(List rList) { this(); for (Real2 r : rList) { add(r); } } /** * appens a Real2. * * @param p * to append */ public void add(Real2 p) { vector.add(p); } /** * gets Real2 element. * * @param i * the position * @return the Real2 or null if outside range */ public Real2 get(int i) { return (Real2) vector.get(i); } /** * @param i * @param v * @exception EuclidRuntimeException * vector does not have an i'th element */ public void set(int i, Real2 v) throws EuclidRuntimeException { vector.set(i, v); } /** * size of vector. * * @return the size */ public int size() { return vector.size(); } /** access the vector. * this is the only storage so should be safe * @return vector */ public List getVector() { return vector; } /** * get range of one coordinate * * @param ax * @return range */ public RealRange getRange(Axis2 ax) { RealArray temp = new RealArray(vector.size()); double[] dd = temp.getArray(); int i = 0; for (Real2 p : vector) { dd[i++] = (ax.equals(Axis2.X)) ? p.getX() : p.getY(); } RealRange range = new RealRange(); if (size() > 0) { range.add(temp.smallestElement()); range.add(temp.largestElement()); } return range; } /** * get range of both coordinates * * @return range */ public Real2Range getRange2() { Axis2 axes[] = Axis2.values(); Real2Range range2 = new Real2Range(); for (Axis2 ax : axes) { RealRange range = getRange(ax); range2.add(ax, range); } return range2; } /** * create a NEW subset of the points; points are COPIED * * @param is * @return sub array * @exception EuclidRuntimeException * an element of is is out of range of this */ public Real2Vector subArray(IntSet is) throws EuclidRuntimeException { Real2Vector sub = new Real2Vector(); for (int i = 0; i < is.size(); i++) { int ix = is.elementAt(i); if (ix < 0 || ix >= vector.size()) { throw new EuclidRuntimeException("index out of range " + ix); } sub.add(new Real2(this.getReal2(ix))); } return sub; } /** * create a subset of the points within a box * * @param r * @return sub set */ public IntSet subSet(Real2Range r) { IntSet is = new IntSet(); for (int i = 0; i < size(); i++) { Real2 point = (Real2) vector.get(i); if (r.includes(point)) { is.addElement(i); } } return is; } /** * get the closest point (both ranges are assumed to have the same scales * * @param p * @return index of point */ public int getClosestPoint(Real2 p) { double dist2 = Double.POSITIVE_INFINITY; int ipoint = -1; for (int i = 0; i < size(); i++) { Real2 point = this.get(i); double dx = p.x - point.x; double dy = p.y - point.y; double d2 = dx * dx + dy * dy; if (d2 < dist2) { dist2 = d2; ipoint = i; } } return ipoint; } /** * get the index of the first point within a box centered on p (that is p+- * width/2, height/2) or -1 if none * * @param p * @param width * @param height * @return index of point * */ public int getPoint(Real2 p, double width, double height) { double hwidth = width / 2.0; double hheight = height / 2.0; for (int i = 0; i < size(); i++) { Real2 point = this.get(i); if (Math.abs(p.x - point.x) <= hwidth && Math.abs(p.y - point.y) <= hheight) return i; } return -1; } /** * translate by a vector, synonym for plus. MODIFIES 'this' * * @param v */ public void translateBy(Real2 v) { for (int i = 0; i < size(); i++) { Real2 p = getReal2(i).plus(v); vector.set(i, p); } } /** * add a Real2 to all elements of this. MODIFIES 'this' * * @param p */ public void plus(Real2 p) { this.translateBy(p); } /** * translate negatively; MODIFIES 'this' * * @param v */ public void subtract(Real2 v) { Real2 v1 = new Real2(v); v1.negative(); this.plus(v1); } /** * multiply all coordinates be a given scalar (that is expands scale) * * @param f */ public void multiplyBy(double f) { for (int i = 0; i < size(); i++) { Real2 p = getReal2(i).multiplyBy(f); vector.set(i, p); } } /** * get distance between 2 points * * @param i1 * @param i2 * @return distance */ public double distance(int i1, int i2) { Real2 v1 = getReal2(i1).subtract(getReal2(i2)); return v1.getLength(); } /** * get distance between 2 points * * @param is * @return distance * @exception EuclidRuntimeException * a value in IntSet is not in the range 0 ... nelem-1 */ public double distance(IntSet is) throws EuclidRuntimeException { if (is.size() != 2) { throw new EuclidRuntimeException("index must be multiple of 2"); } return distance(is.elementAt(0), is.elementAt(1)); } /** * get angle between 3 points * * @param i1 * @param i2 * @param i3 * @return angle * @exception EuclidRuntimeException * two points are coincident */ public Angle angle(int i1, int i2, int i3) throws EuclidRuntimeException { return Real2.getAngle(getReal2(i1), getReal2(i2), getReal2(i3)); } /** * get angle between 3 points * * @param is * @return angle * @exception EuclidRuntimeException * a value in IntSet is not in the range 0 ... nelem-1 */ public Angle angle(IntSet is) throws EuclidRuntimeException { if (is.size() != 3) { throw new EuclidRuntimeException("index must be multiple of 3"); } return angle(is.elementAt(0), is.elementAt(1), is.elementAt(2)); } /** * get the i'th Real2 * * @param i * @return element */ public Real2 getReal2(int i) { return (Real2) get(i); } /** get List of Real2s. * * @return the list */ public List getReal2List() { return vector; } /** * get the coordinate coordinate array as doubles x,y,x,y, * * @return array */ public RealArray getXY() { double[] f = new double[2 * size()]; int count = 0; for (int i = 0; i < size(); i++) { Real2 p = getReal2(i); f[count++] = p.getX(); f[count++] = p.getY(); } return new RealArray(f); } /** * get the X coordinate array * * @return array */ public RealArray getXArray() { double[] f = new double[size()]; int count = 0; for (int i = 0; i < size(); i++) { Real2 p = getReal2(i); f[count++] = p.getX(); } return new RealArray(f); } /** * get the Y coordinate array * * @return array */ public RealArray getYArray() { double[] f = new double[size()]; int count = 0; for (int i = 0; i < size(); i++) { Real2 p = getReal2(i); f[count++] = p.getY(); } return new RealArray(f); } /** convenience * * @return real2Array */ public Real2Array getReal2Array() { return new Real2Array(getXArray(), getYArray()); } /** * get a single coordinate value * * @param i * @param j * @return coord */ public double getCoordinate(int i, Axis2 j) { return (j.value == 0) ? getReal2(i).getX() : getReal2(i).getY(); } /** * get a single coordinate array - for example all x-coordinates * * @param axis * @return array */ public RealArray getXorY(Axis2 axis) { double[] f = new double[size()]; for (int i = 0; i < size(); i++) { f[i] = (axis.value == 0) ? getReal2(i).getX() : getReal2(i).getY(); } return new RealArray(f); } /** * swap all X and Y coordinates; MODIFIES array */ public void swapXY() { for (int i = 0; i < size(); i++) { Real2 temp = (Real2) vector.get(i); temp.swap(); vector.set(i, temp); } } /** * sort ARRAY on X or Y coordinate; returns new array * * @param ax * @return vector */ public Real2Vector sortAscending(Axis2 ax) { IntSet is = getXorY(ax).indexSortAscending(); Real2Vector temp = new Real2Vector(); for (int i = 0; i < size(); i++) { temp.add(this.get(is.elementAt(i))); } return temp; } /** * sort ARRAY on X or Y coordinate; returns new array * * @param ax * @return vector * */ public Real2Vector sortDescending(Axis2 ax) { IntSet is = getXorY(ax).indexSortDescending(); Real2Vector temp = new Real2Vector(); for (int i = 0; i < size(); i++) { temp.add(this.get(is.elementAt(i))); } return temp; } /** * transforms 'this' by rotation-translation matrix; MODIFIES 'this' * * @param t */ public void transformBy(Transform2 t) { for (int i = 0; i < size(); i++) { Real2 point = (Real2) vector.get(i); point.transformBy(t); vector.set(i, point); } } /** * squared difference between corresponding points in 2 real2Vectors. must * be of same length else returns Double.NaN * * @param r2v * vector to compare * @return sum ((x1-x2)**2 + (y1-y2)**2) */ public double getSquaredDifference(Real2Vector r2v) { double sum = Double.NaN; if (r2v.size() == 0 || vector.size() == 0 || r2v.size() != vector.size()) { } else { sum = 0.0; for (int i = 0; i < size(); i++) { Real2 xy1 = this.get(i); Real2 xy2 = r2v.get(i); double d2 = xy1.getSquaredDistance(xy2); sum += d2; } } return sum; } /** * gets array of squared distances between corresponding points. * * array of d(this(i)-r2v(i))^2; returns null if Real2Vectors are different * lengths * * @param r2v * other vector * @return array of squares distances else null */ public double[] getSquaredDistances(Real2Vector r2v) { double dist2[] = null; if (r2v.size() == vector.size()) { dist2 = new double[vector.size()]; for (int i = 0; i < size(); i++) { Real2 xy1 = this.get(i); Real2 xy2 = r2v.get(i); dist2[i] = xy1.getSquaredDistance(xy2); } } return dist2; } /** * rotate about centroid by given angle; MODIFIES 'this' * * @param a */ public void rotateAboutCentroid(Angle a) { Real2 temp = this.getCentroid(); Transform2 t2 = new Transform2(a); t2 = new Transform2(t2, temp); this.transformBy(t2); } /** creates mirror image * changes sign of x-coords */ public void flipX() { for (Real2 r2 : vector) { r2.setX(-r2.getX()); } } /** creates mirror image * changes sign of y-coords */ public void flipY() { for (Real2 r2 : vector) { r2.setY(-r2.getY()); } } /** * create regular polygon. * centre is at 0.0, first point at * (rad*cos(angle+dangle), rad*sin(angle+dangle)) * points go anticlockwise * * @param nsides * @param rad the radius * @param angle offset to vertical (radians) * (point 0 is at rad*cos(angle), rad*sin(angle)) * @return array of points */ public static Real2Vector regularPolygon(int nsides, double rad, double angle) { Real2Vector temp = new Real2Vector(); double dangle = 2 * Math.PI / ((double)nsides); for (int i = 0; i < nsides; i++) { Real2 p = new Real2(rad * Math.sin(angle), rad * Math.cos(angle)); temp.add(p); angle += dangle; } return temp; } /** * create part of regular polygon. * centre is at 0.0, last point at (rad, 0) * points go anticlockwise * produces points pointn, pointn+1, ... nsides-1, 0 * if npoint is 1 we get full polygon * if npoint {@literal >}= nsides returns null * @param nsides * @param pointn * @param dist0n between point0 and npoint * @return array of points */ public static Real2Vector partOfRegularPolygon(int nsides, int pointn, double dist0n) { Real2Vector temp = new Real2Vector(); double dangle = 2 * Math.PI / ((double)nsides); double sinAngle2 = Math.sin((double) pointn * dangle / 2.); double rad = 0.5 * dist0n / sinAngle2; double angle = dangle; for (int i = pointn; i <= nsides; i++) { Real2 p = new Real2(rad * Math.sin(angle), rad * Math.cos(angle)); temp.add(p); angle += dangle; } return temp; } /** * create regular polygon. * centre is at 0.0, first point at (0, rad) * points go anticlockwise (rad*cos, -rad*sin) * * @param nsides * @param rad the radius * @return array of points */ public static Real2Vector regularPolygon(int nsides, double rad) { return regularPolygon(nsides, rad, 0.0); } /** create regular polygon. * goes through points p1, p2 * if m = (p1 +p2)/2 * and c is centre of circle * and pp = p2 - p1 * and d = c-m * then signed angle(pp, d) is 90 * * points go clockwise * to make the polygon go the other way reverse p1 and p2 * @param nsides * @param p1 first point * @param p2 second point * @param flip use mirror image * @return array of points */ public static Real2Vector regularPolygon(int nsides, Real2 p1, Real2 p2, boolean flip) { double halfAngle = Math.PI / (double) nsides; Vector2 p1top2 = new Vector2(p2.subtract(p1)); double halfSidelength = p1top2.getLength() / 2.0; double rad = halfSidelength / Math.sin(halfAngle); Real2Vector temp = regularPolygon(nsides, rad); if (flip) { temp.flipY(); } // deviation between p1p2 and the y-axis Angle rot = null; rot = p1top2.getAngleMadeWith(new Vector2(0.0, 1.0)); rot = new Vector2(1.0, 0.0).getAngleMadeWith(p1top2); temp.transformBy(new Transform2(rot)); temp.transformBy(new Transform2(new Angle(-halfAngle))); Real2 shift = p1.subtract(new Vector2(temp.get(0))); temp.transformBy(new Transform2(new Vector2(shift))); return temp; } /** * get centroid of all points. does not modify this * * @return the centroid */ public Real2 getCentroid() { if (vector.size() < 1) return null; Real2 p = new Real2(); for (int j = 0; j < vector.size(); j++) { Real2 pp = (Real2) vector.get(j); p = p.plus(pp); } double scale = 1.0 / ((double)vector.size()); p = p.multiplyBy(scale); return p; } /** * get serialNumber of nearest point. * * @param point * @return serial of point */ public int getSerialOfNearestPoint(Real2 point) { double dist = Double.MAX_VALUE; int serial = -1; for (int j = 0; j < vector.size(); j++) { double d = this.get(j).subtract(point).getLength(); if (d < dist) { serial = j; dist = d; } } return serial; } /** * get rectangular distance matrix. * * matrix need not be square or symmetric unless coords == coords2 d(ij) is * distance between coord(i) and coords2(j). * * @param coords2 * @return distance matrix */ public RealMatrix getDistanceMatrix(List coords2) { int size = vector.size(); int size2 = coords2.size(); RealMatrix distMatrix = new RealMatrix(size, size2); double[][] mat = distMatrix.getMatrix(); for (int i = 0; i < size; i++) { Real2 ri = this.get(i); for (int j = 0; j < size2; j++) { Real2 rj = coords2.get(j); double dij = ri.getDistance(rj); mat[i][j] = dij; } } return distMatrix; } /** * if real2Vector is treated as a polygon, determines whether point * is inside it // The function will return true if the point x,y is inside the polygon, or // false if it is not. If the point is exactly on the edge of the polygon, // then the function may return YES or NO. */ public boolean encloses(Real2 point) { int nvect = vector.size(); int j = nvect - 1; boolean oddNodes = false; double x = point.getX(); double y = point.getY(); for (int i=0; i < nvect; i++) { double xi = vector.get(i).getX(); double yi = vector.get(i).getY(); double xj = vector.get(j).getX(); double yj = vector.get(j).getY(); if (yi < y && yj >= y || yj < y && yi >= y) { if (xi + (y - yi) / (yj - yi) * (xj - xi) < x) { oddNodes = !oddNodes; } } j = i; } return oddNodes; } /** * to string. * * @return string */ public String toString() { StringBuffer sb = new StringBuffer(); sb.append(S_LBRAK); for (int i = 0; i < size(); i++) { sb.append(getReal2(i)); if (i < (size() - 1)) { sb.append(S_NEWLINE); } } sb.append(S_RBRAK); return sb.toString(); } } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/Real3Range.java000066400000000000000000000134101461721410700246500ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid; import org.xmlcml.euclid.Axis.Axis3; /** * 3-D double limits * * Contains 3 RealRanges. Can therefore be used to describe 3-dimensional limits * (for example axes of 3-D graphs, boxes in graphics, limits of a molecule, * etc.) *

* Default is three invalid RealRange components. * * @author (C) P. Murray-Rust, 1996 */ public class Real3Range implements EuclidConstants { private RealRange[] xyzrange = new RealRange[3]; /** * default is three default RealRanges */ public Real3Range() { for (int i = 0; i < 3; i++) { xyzrange[i] = new RealRange(); } } /** * initialise with min and max values; takes COPIES * * @param xr * @param yr * @param zr */ public Real3Range(RealRange xr, RealRange yr, RealRange zr) { setRanges(xr, yr, zr); } /** reset ranges. * clears any existing content * @param xr * @param yr * @param zr */ public void setRanges(RealRange xr, RealRange yr, RealRange zr) { xyzrange[0] = new RealRange(xr); xyzrange[1] = new RealRange(yr); xyzrange[2] = new RealRange(zr); } /** * copy constructor * * @param r */ public Real3Range(Real3Range r) { for (int i = 0; i < 3; i++) { xyzrange[i] = new RealRange(r.xyzrange[i]); } } /** * is equal to. * * @param r3 * @return tru if equals */ public boolean isEqualTo(Real3Range r3, double eps) { return (xyzrange[0].isEqualTo(r3.xyzrange[0], eps) && xyzrange[1].isEqualTo(r3.xyzrange[1], eps) && xyzrange[2] .isEqualTo(r3.xyzrange[2], eps)); } /** * is equal to. * * @param r3 * @return tru if equals */ @Deprecated public boolean isEqualTo(Real3Range r3) { return (xyzrange[0].isEqualTo(r3.xyzrange[0]) && xyzrange[1].isEqualTo(r3.xyzrange[1]) && xyzrange[2] .isEqualTo(r3.xyzrange[2])); } /** * add two ranges. applies plus to each of x, y, z. creates minima and * maxima of this, r3 * * @param r3 * @return new range */ public Real3Range plus(Real3Range r3) { return new Real3Range(xyzrange[0].plus(r3.xyzrange[0]), xyzrange[1] .plus(r3.xyzrange[1]), xyzrange[2].plus(r3.xyzrange[2])); } /** * get xrange * * @return range */ public RealRange getXRange() { return xyzrange[0]; } /** * get yrange * * @return range */ public RealRange getYRange() { return xyzrange[1]; } /** * get zrange * * @return range */ public RealRange getZRange() { return xyzrange[2]; } /** * add a single value * * @param ax * @param value */ public void add(Axis3 ax, double value) { xyzrange[ax.value].add(value); } /** * add a single value * * @param ax * @param range */ public void add(Axis3 ax, RealRange range) { xyzrange[ax.value] = range; } /** * add a single value - not for general use * * @param axis * @param value */ protected void add(int axis, double value) { xyzrange[axis].add(value); } /** * is a Point3 within a Real3Range * * @param p * @return includes */ public boolean includes(Point3 p) { double[] coords = p.getArray(); return (xyzrange[0].includes(coords[0]) && xyzrange[1].includes(coords[1]) && xyzrange[2] .includes(coords[2])); } /** * add a Point3 to a range * * @param p */ public void add(Point3 p) { double[] coords = p.getArray(); xyzrange[0].add(coords[0]); xyzrange[1].add(coords[1]); xyzrange[2].add(coords[2]); } /** get point with min x, y, z. * * @return the point. */ public Point3 getMinPoint3() { return new Point3( xyzrange[0].getMin(), xyzrange[1].getMin(), xyzrange[2].getMin()); } /** get point with max x, y, z. * * @return the point. */ public Point3 getMaxPoint3() { return new Point3( xyzrange[0].getMax(), xyzrange[1].getMax(), xyzrange[2].getMax()); } /** transforms range. * modifies this * @param transform */ public void transformEquals(Transform3 transform) { Point3 minxyz = getMinPoint3(); Point3 maxxyz = getMaxPoint3(); minxyz.transformEquals(transform); maxxyz.transformEquals(transform); Real3Range newRange3 = new Real3Range(); newRange3.add(minxyz); newRange3.add(maxxyz); int i = 0; for (RealRange xyzr : newRange3.xyzrange) { this.xyzrange[i++] = new RealRange(xyzr); } } /** * to string. * * @return string */ public String toString() { return EC.S_LBRAK + xyzrange[0] + EC.S_COMMA + xyzrange[1] + EC.S_COMMA + xyzrange[2] + EC.S_RBRAK; } } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/RealArray.java000066400000000000000000001772321461721410700246240ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid; import java.text.DecimalFormat; import java.util.Iterator; import java.util.List; import org.apache.log4j.Logger; /** * array of doubles * * RealArray represents a 1-dimensional array of doubles and is basically a * wrapper for double[] in Java There are a lot of useful member functions * (sorting, ranges, parallel operations * * The default is an array with zero points All arrays are valid objects. * * Attempting to create an array with {@literal <} 0 points creates a default array (zero * points). * * Since double[] knows its length (unlike C), there are many cases where * double[] can be safely used. However it is not a first-class object and * RealArray supplies this feature. double[] is referenceable through getArray() * * note that the length of the internal array may not be a useful guide to the * number of elements. Use contractArray() to adjust arraySize to number of * elements (useful after adding elements). * * * @author (C) P. Murray-Rust, 1996 */ public class RealArray extends ArrayBase implements Iterable { final static Logger LOG = Logger.getLogger(RealArray.class); /** filter */ public enum Filter { /** */ GAUSSIAN("Gaussian"), /** */ GAUSSIAN_FIRST_DERIVATIVE("Gaussian First Derivative"), /** */ GAUSSIAN_SECOND_DERIVATIVE("Gaussian Second Derivative"); /** string value */ public String string; private Filter(String s) { this.string = s; } } public enum Monotonicity { INCREASING, DECREASING } /** * maximum number of elements (for bound checking) - resettable */ private int maxelem = 10000; /** * actual number of elements */ private int nelem; /** * the array of doubles */ private double[] array; private int bufsize = 5; private DecimalFormat format = null; /** * create default Array. default is an array of zero points */ public RealArray() { nelem = 0; bufsize = 5; array = new double[bufsize]; } /** * checks potential size of array. if n < 0, set to 0, otherwise adjust * bufsize to be consistent * * @param n * size of array * @return false if negative */ private boolean checkSize(int n) { if (n < 0) { n = 0; return false; } else { nelem = n; if (nelem > maxelem) maxelem = nelem; if (bufsize < nelem) bufsize = nelem; return true; } } // expands buffer if necessary and copies array into it private void makeSpace(int newCount) { if (bufsize < 5) { bufsize = 5; } if (newCount >= bufsize || array.length < newCount) { while (newCount >= bufsize) { bufsize *= 2; } double[] array1 = new double[bufsize]; System.arraycopy(array, 0, array1, 0, nelem); array = array1; } } /** * creates n-element array. initialised to 0 * * @param n * size of array */ public RealArray(int n) { this(n, 0.0); } /** * create n-element array initialised linearly. values are elem1+(i-1)*delta * * @param n * size of array * @param elem1 * starting value * @param delta * setpsize */ public RealArray(int n, double elem1, double delta) { if (!checkSize(n)) return; array = new double[n]; bufsize = n; double ff = elem1; for (int i = 0; i < n; i++) { array[i] = ff; ff += delta; } } /** * create Array initialized to constant value. all elements of the array are * set to a given value * * @param n * size of array * @param elem1 * value to set */ public RealArray(int n, double elem1) { if (!checkSize(n)) return; array = new double[n]; bufsize = n; for (int i = 0; i < n; i++) { array[i] = elem1; } } /** * create Array from part of java array. use first n elements of array. * * @param n * number of elements to use * @param arr * array to read from * @throws EuclidRuntimeException * n larger than arraysize */ public RealArray(int n, double[] arr) throws EuclidRuntimeException { if (!checkSize(n)) throw new EuclidRuntimeException("Cannot have negative array length"); if (n > arr.length) { throw new EuclidRuntimeException("Array size too small"); } array = new double[n]; bufsize = n; System.arraycopy(arr, 0, array, 0, n); } /** * create Array from java array. * * @param arr * array to read from */ public RealArray(double[] arr) { setElements(arr); } /** * create from IntArray. * * @param ia * IntArray to copy from */ public RealArray(IntArray ia) { if (!checkSize(ia.size())) return; array = new double[nelem]; bufsize = nelem; for (int i = 0; i < nelem; i++) { array[i] = ia.elementAt(i); } } /** * create from subarray of another Array. * * @param m * array to slice * @param low * inclusive start index of array * @param high * inclusive end index of array * @throws EuclidRuntimeException * low {@literal >} high or negative indices or outside size of m */ public RealArray(RealArray m, int low, int high) throws EuclidRuntimeException { if (low < 0 || low > high || high >= m.size()) { throw new EuclidRuntimeException("index out of range " + low + EC.S_SLASH + high); } nelem = high - low + 1; checkSize(nelem); array = new double[nelem]; bufsize = nelem; System.arraycopy(m.array, low, array, 0, nelem); } /** * create mixed sliced array. use another IntArray to subscript this one * where I(this) = I(ref) subscripted by I(sub); Result has dimension of * I(sub). caller is responsible for making sure elements of sub are unique * * @param ref * matrix to slice * @param sub * subscripts. * @throws EuclidRuntimeException * if any of I(sub) lies outside 0...refmax-1 */ public RealArray(RealArray ref, IntArray sub) throws EuclidRuntimeException { this(sub.size()); for (int i = 0; i < sub.size(); i++) { int j = sub.elementAt(i); if (j < 0 || j >= ref.size()) { throw new EuclidRuntimeException("index out of range " + j); } this.setElementAt(i, ref.elementAt(j)); } } /** gets range distributed about a midpoint. * * array is from n steps from mid-halfrange to mid to mid+halfrange * @param mid midpoint of range * @param nsteps odd number of steps * @param halfrange * @return array */ public static RealArray getSymmetricalArray( double mid, int nsteps, double halfrange) { if (nsteps < 3 || nsteps % 2 != 1) { throw new EuclidRuntimeException( "Number of steps must be positive odd number; was: "+nsteps); } int nhalfsteps = (nsteps - 1) / 2; double step = halfrange / nhalfsteps; RealArray realArray = new RealArray(nsteps, mid - halfrange, step); return realArray; } /** * clone. * * @return the clone */ public Object clone() { RealArray temp = new RealArray(nelem); temp.nelem = nelem; temp.maxelem = maxelem; System.arraycopy(array, 0, temp.array, 0, nelem); temp.bufsize = nelem; return (Object) temp; } /** * copy constructor. * * @param m * array to copy */ public RealArray(RealArray m) { this.shallowCopy(m); System.arraycopy(m.array, 0, array, 0, nelem); } /** * Create customized array. create a given 'shape' of array for data * filtering An intended use is with RealArray.arrayFilter(). The shapes * (before scaling by maxval) are: *

    *
  • "TRIANGLE"; 1/nn, 2/nn, ... 1 ... 2/nn, 1/nn; nelem is set to 2*nn - * 1 *
  • "ZIGZAG"; 1/nn, 2/nn, ... 1 ... 1/nn, 0, -1/nn, -2/nn, -1, ... * -1/nn,; nelem is set to 4*nn - 1 *
* step is maxval / nn * * @param nn * number of elements * @param shape * TRIANGLE or ZIGZAG * @param maxval * used to compute step */ public RealArray(int nn, String shape, double maxval) { if (shape.toUpperCase().equals("TRIANGLE")) { nelem = nn * 2 - 1; if (!checkSize(nelem)) return; array = new double[nelem]; double delta = maxval / ((double) nn); for (int i = 0; i < nn; i++) { array[i] = (i + 1) * delta; array[nelem - i - 1] = array[i]; } } else if (shape.toUpperCase().equals("ZIGZAG")) { nelem = nn * 4 - 1; if (!checkSize(nelem)) return; array = new double[nelem]; double delta = maxval / ((double) nn); for (int i = 0; i < nn; i++) { array[i] = (i + 1) * delta; array[2 * nn - i - 2] = array[i]; array[2 * nn + i] = -array[i]; array[nelem - i - 1] = -array[i]; } array[2 * nn - 1] = 0.0; } } /** * construct from an array of Strings. must represent doubles * * @param strings * values as Strings * @exception EuclidRuntimeException * a string could not be interpreted as doubles */ public RealArray(String[] strings) throws EuclidRuntimeException { this(strings.length); for (int i = 0; i < strings.length; i++) { try { array[i] = Real.parseDouble(strings[i]); } catch (Exception e) { throw new EuclidRuntimeException("Bad array element at ("+i+") :"+strings[i]+":"); } } } /** interprets list of Strings as numbers. * * @param stringList * @return null if stringList is null, zero length or has non-numbers */ public static RealArray createRealArray(List stringList) { RealArray realArray = null; if (stringList != null && stringList.size() > 0) { try { realArray = new RealArray(stringList.toArray(new String[0])); } catch (EuclidRuntimeException ere) { // bad strings } } return realArray; } /** * create from a space-separated string of doubles. * * @param string * of form "1.1 -3.2 0.56E-04 2..." * @exception NumberFormatException * a substring could not be interpreted as double */ public RealArray(String string) throws NumberFormatException { this(string.split(S_WHITEREGEX)); } /** * set output format. doesn't yet do anything! * * @param f */ public void setFormat(DecimalFormat f) { format = f; } /** * get output format. * * @return format */ public DecimalFormat getFormat() { return format; } /** * replace all values of NaN with given value. use with care as there is * probably something wrong * * @param d * default value */ public void replaceNaN(double d) { for (int i = 0; i < nelem; i++) { if (Double.isNaN(array[i])) array[i] = d; } } /** * contracts internal array to be of same length as number of elements. * should be used if the array will be used elsewhere with a fixed length. * called by getArray() */ private void contractArray() { double[] array1 = new double[nelem]; System.arraycopy(array, 0, array1, 0, nelem); array = array1; } /** * shallowCopy. * * @param m * array to copy */ void shallowCopy(RealArray m) { nelem = m.nelem; bufsize = m.bufsize; maxelem = m.maxelem; array = m.array; } /** * creates a filter based on Gaussian and derivatives. Scaled so that * approximately 2.5 sigma is included (that is value at edge is ca 0.01 of * centre * * @param halfWidth * @param function * @return array */ public static RealArray getFilter(int halfWidth, Filter function) { if (!function.equals(Filter.GAUSSIAN) && !function.equals(Filter.GAUSSIAN_FIRST_DERIVATIVE) && !function.equals(Filter.GAUSSIAN_SECOND_DERIVATIVE)) return null; if (halfWidth < 1) halfWidth = 1; double xar[] = new double[2 * halfWidth + 1]; double limit = 7.0; // ymin ca 0.01 double sum = 0; double x = 0.0; double y = 1.0; // double dHalf = Math.sqrt(0.693); double dHalf = limit * 0.693 * 0.693 / (double) halfWidth; for (int i = 0; i <= halfWidth; i++) { if (function.equals(Filter.GAUSSIAN)) { y = Math.exp(-x * x); } if (function.equals(Filter.GAUSSIAN_FIRST_DERIVATIVE)) { y = -2 * x * Math.exp(-x * x); } if (function.equals(Filter.GAUSSIAN_SECOND_DERIVATIVE)) { y = (4 * (x * x) - 2.0) * Math.exp(-x * x); } xar[halfWidth + i] = (function .equals(Filter.GAUSSIAN_FIRST_DERIVATIVE)) ? -y : y; xar[halfWidth - i] = y; sum += (i == 0) ? y : 2 * y; x += dHalf; } // normalise for Gaussian (only = the others are meaningless) if (function.equals(Filter.GAUSSIAN)) { for (int i = 0; i < 2 * halfWidth + 1; i++) { xar[i] /= sum; } } RealArray r = new RealArray(xar); return r; } /** creates a normal distribution about a mean. * returns N*(exp((x-mean)^2/(2*sigma^2)) * where N = 1/sigma*Math.sqrt(2*PI) * * The distribution is scaled to unit area over -INF to +INF * so a smaller selection will not be precisely normalised * however -5 sigma to + 5 sigma should be fine * * the x range is nsteps from maen-range to mean+range. This should be * prepared using * RealArray xvalues = RealArray.getSymmetricalArray(mean, nsteps, halfrange); * RealArray normalDist = xvalues.getNormalDistribution(sigma); * nsteps should be an odd positive integer * halfrange is the range either side of the mean * * @param sigma standard deviation * @return array */ public RealArray getNormalDistribution(double sigma) { int nsteps = this.size(); double norm = 1.0/(sigma*Math.sqrt(2*Math.PI)); double scale = 1.0/(2*sigma*sigma); RealArray normal = new RealArray(this.size()); double[] array = this.getArray(); double mean = (array[0] + array[nsteps-1])/2.0; for (int i = 0; i < nsteps; i++) { double delta = array[i] - mean; normal.array[i] = norm * Math.exp(-delta * delta * scale); } return normal; } /** gets a variate from a distribution. * 'this' is normally a regularly spaced RealArray x (e.g. * with values x[i] = x0, x0+dx, x0+2*dx, etc. * * distribution is a set of frequencies of occurrence of the values * x[0], x[1]..., i.e. f[0], f[1]... * * The method uses a cumulative dictribution with a uniformaly * distributed variable (e.g. math.random() to read off the interpolated * value of x. * * the cumulativeSum is normally used as a cache, e.g. * RealArray x = new RealArray(11, 20., 1.) // gives 20, 21 ...30 * RealArray freq = new RealArray(new double[] {23., 3., 45....) * RealArray cumulativeSum = new RealArray(); * * for (int i = 0, i {@literal <} npoints; i++) { * double random = x.getRandomVariate(freq, sumulativeSum); * } * * the size of this and freq must be identical * @param distribution * @param cumulativeDistribution initially clea, then used as cache * @return a random variate */ public double getRandomVariate( RealArray distribution, RealArray cumulativeDistribution) { if (cumulativeDistribution.size() == 0) { RealArray cumul = distribution.cumulativeSum(); cumulativeDistribution.setElements(cumul.getArray()); } double[] cArray = cumulativeDistribution.getArray(); double range = cArray[cArray.length-1] - cArray[0]; double probe = cArray[0] + Math.random()* range; return lineSearch(probe, cumulativeDistribution); } /** binary search on monotonic increasing distribution. * 'this' must be regular array of values (x0, x0+dx, x0+2dx...) * @param probe * @param distribution * @return interpolated point in this */ public double lineSearch(double probe, RealArray distribution) { if (this.size() <= 1) { throw new EuclidRuntimeException("unfilled arrays in line search"); } if (this.size() != distribution.size()) { throw new EuclidRuntimeException("unequal arrays in line search"); } double[] distArray = distribution.getArray(); // binary chop int top = distArray.length-1; int bottom = 0; boolean change = true; while (change) { if (top - bottom <= 1) { break; } change = false; int mid = (top + bottom) / 2; if (distArray[mid] < probe) { bottom = mid; change = true; } else if (distArray[mid] > probe) { top = mid; change = true; } } double ratio = (probe - distArray[bottom]) / (distArray[top] - distArray[bottom]); double step = array[1] - array[0]; return this.array[bottom] + step * ratio; } /** get element by index. * * @param elem the index * @exception ArrayIndexOutOfBoundsException * elem {@literal >}= size of this * @return element value */ public double elementAt(int elem) throws ArrayIndexOutOfBoundsException { return array[elem]; } /** get element by index. * * @param elem the index * @exception ArrayIndexOutOfBoundsException * elem {@literal >}= size of this * @return element value */ public double get(int elem) throws ArrayIndexOutOfBoundsException { return array[elem]; } /** * get actual number of elements. * * @return number of elements */ public int size() { return nelem; } /** * get java array. always adjusted to be same length as element count * * @return the array */ public double[] getArray() { if (nelem != array.length) { contractArray(); } return array; } /** * clear all elements of array. sets value to 0.0 */ public void clearArray() { for (int i = 0; i < size(); i++) { array[i] = 0.0; } } /** * get java array in reverse order. * * @return array */ public double[] getReverseArray() { int count = size(); double[] temp = new double[count]; for (int i = 0; i < size(); i++) { temp[i] = this.array[--count]; } return temp; } private void checkConformable(RealArray m) throws EuclidRuntimeException { if (nelem != m.nelem) { throw new EuclidRuntimeException(); } } /** * are two arrays equal. * * @param f * array to compare * @return true if arrays are of same size and this(i) = f(i) */ public boolean isEqualTo(RealArray f) { return equals(f, Real.getEpsilon()); } /** * are two arrays equal. * * @param f * array to compare * @param epsilon * tolerance * @return true if arrays are of same size and Real.isEqual(array[i], * f.array[i], epsilon) */ public boolean equals(RealArray f, double epsilon) { boolean equal = false; try { checkConformable(f); equal = true; for (int i = 0; i < nelem; i++) { if (!Real.isEqual(array[i], f.array[i], epsilon)) { equal = false; break; } } } catch (Exception e) { equal = false; } return equal; } /** * adds arrays. does not modify this * * @param f * array to add * @exception EuclidRuntimeException * f is different size from this * @return new array as this + f */ public RealArray plus(RealArray f) throws EuclidRuntimeException { checkConformable(f); RealArray m = (RealArray) this.clone(); for (int i = 0; i < nelem; i++) { m.array[i] = f.array[i] + array[i]; } return m; } /** * adds arrays. modifies this += f * * @param f * array to add * @exception EuclidRuntimeException * f is different size from this */ public void plusEquals(RealArray f) throws EuclidRuntimeException { checkConformable(f); for (int i = nelem - 1; i >= 0; --i) { array[i] += f.array[i]; } } /** * subtracts arrays. does not modify this * * @param f * array to substract * @exception EuclidRuntimeException * f is different size from this * @return new array as this - f */ public RealArray subtract(RealArray f) throws EuclidRuntimeException { checkConformable(f); RealArray m = (RealArray) this.clone(); for (int i = 0; i < nelem; i++) { m.array[i] = array[i] - f.array[i]; } return m; } /** * array subtraction. modifies this -= f * * @param f * array to subtract * @exception EuclidRuntimeException * f is different size from this */ public void subtractEquals(RealArray f) throws EuclidRuntimeException { checkConformable(f); for (int i = nelem - 1; i >= 0; --i) { array[i] -= f.array[i]; } } /** * change the sign of all elements. MODIFIES this */ public void negative() { for (int i = 0; i < size(); i++) { array[i] = -array[i]; } } /** * add a scalar to all elements. creates new array; does NOT modify 'this'; * for subtraction use negative scalar * * @param f * to add * @return new array */ public RealArray addScalar(double f) { RealArray m = (RealArray) this.clone(); for (int i = 0; i < nelem; i++) { m.array[i] += f; } return m; } /** * array multiplication by a scalar. creates new array; does NOT modify * 'this' * * @param f * multiplier * @return the new array */ public RealArray multiplyBy(double f) { RealArray m = (RealArray) this.clone(); for (int i = 0; i < nelem; i++) { m.array[i] *= f; } return m; } /** * add a scalar to each element. creates new array; does NOT modify * 'this' * * @param f multiplier * @return the new array */ public RealArray plus(double f) { RealArray m = (RealArray) this.clone(); for (int i = 0; i < nelem; i++) { m.array[i] += f; } return m; } /** * set element value. * * @param elem * index * @param f * value * @exception ArrayIndexOutOfBoundsException * elem {@literal >}= size of this */ public void setElementAt(int elem, double f) throws ArrayIndexOutOfBoundsException { array[elem] = f; } /** * get array slice. creates new array; does not modify this * * @param start * index inclusive * @param end * index inclusive * @return new array */ public RealArray getSubArray(int start, int end) { int nel = end - start + 1; RealArray f = new RealArray(nel, 0); System.arraycopy(array, start, f.array, 0, nel); return f; } /** * set array slice. copy whole array into the array. * * @param start * index in this * @param a * array to copy * @throws ArrayIndexOutOfBoundsException * start {@literal <} 0 or start+a.length {@literal >} this.size() */ public void setElements(int start, double[] a) { if (start < 0 || start + a.length > nelem) { throw new ArrayIndexOutOfBoundsException("was "+start+" in 0-"+a.length); } System.arraycopy(a, 0, this.array, start, a.length); } /** set array. * clears any existing arrays * @param a * array to copy */ public void setElements(double[] a) { nelem = a.length; array = new double[nelem]; bufsize = nelem; System.arraycopy(a, 0, array, 0, nelem); } /** * is the array filled with zeros. * * @return true if this(i) = 0 */ public boolean isClear() { for (int i = 0; i < nelem; i++) { if (!Real.isZero(array[i], Real.getEpsilon())) return false; } return true; } /** * initialise array to given value. this(i) = f * * @param f * value to set */ public void setAllElements(double f) { Real.initArray(nelem, array, f); } /** * sum all elements. * * @return sigma(this(i)) */ public double sumAllElements() { double sum = 0.0; for (int i = 0; i < nelem; i++) { sum += array[i]; } return sum; } /** * sum all product of elements. * * @return sigma(this(i)*y(i) */ public double sumProductOfAllElements(RealArray yarr) { if (yarr == null || yarr.size() != nelem) { throw new RuntimeException("arrays must be of equal length"); } double sum = 0.0; for (int i = 0; i < nelem; i++) { sum += array[i]*yarr.elementAt(i); } return sum; } /** * sum of all absolute element values. * * @return sigma(abs(this(i))) */ public double absSumAllElements() { double sum = 0.0; for (int i = 0; i < nelem; i++) { sum += Math.abs(array[i]); } return sum; } /** * inner product. dotProduct(this) * * @return sigma(this(i)**2) */ public double innerProduct() { return this.dotProduct(this); } /** * dot product of two arrays. sigma(this(i)*(f(i)); * * @param f * array to multiply * @exception EuclidRuntimeException * f is different size from this * @return dot product */ public double dotProduct(RealArray f) throws EuclidRuntimeException { checkConformable(f); double sum = 0.0; for (int i = 0; i < nelem; i++) { sum += array[i] * f.array[i]; } return sum; } /** * Euclidean length of vector * * @return length */ public double euclideanLength() { return Math.sqrt(innerProduct()); } /** * root mean square sqrt(sigma(x(i)**2)/n) * * @exception EuclidRuntimeException * must have at least 1 point * @return rms */ public double rms() throws EuclidRuntimeException { if (nelem == 0) { throw new EuclidRuntimeException("must have at least one point"); } return euclideanLength() / Math.sqrt((double) nelem); } /** * get unit vector * * @exception EuclidRuntimeException * elements of this are all zero * @return the unit vector */ public RealArray unitVector() throws EuclidRuntimeException { double l = euclideanLength(); if (Real.isZero(l, Real.getEpsilon())) { throw new EuclidRuntimeException("zero length vector"); } double scale = 1.0 / l; RealArray f = new RealArray(nelem); f = this.multiplyBy(scale); return f; } /** * cumulative sum of array. create new array as elem[i] = sum(k = 0 to i) * f[k] does not modify 'this' * * @return each element is cumulative sum to that point */ public RealArray cumulativeSum() { RealArray temp = new RealArray(nelem); double sum = 0.0; for (int i = 0; i < nelem; i++) { sum += array[i]; temp.array[i] = sum; } return temp; } /** * apply filter. convolute array with another array. This is 1-D image * processing. If filter has {@literal <}= 1 element, return this * unchanged. filter should have an odd number of elements. The * filter can be created with a IntArray constructor filter is moved along * stepwise * * @param filter * to apply normally smaller than this * @return filtered array */ public RealArray applyFilter(RealArray filter) { if (nelem == 0 || filter == null || filter.nelem <= 1) { return this; } int nfilter = filter.size(); int midfilter = (nfilter - 1) / 2; RealArray temp = new RealArray(nelem); double wt = 0; double sum = 0; for (int j = 0; j < midfilter; j++) { // get weight wt = 0.0; sum = 0.0; int l = 0; for (int k = midfilter - j; k < nfilter; k++) { wt += Math.abs(filter.array[k]); sum += filter.array[k] * this.array[l++]; } temp.array[j] = sum / wt; } wt = filter.absSumAllElements(); for (int j = midfilter; j < nelem - midfilter; j++) { sum = 0.0; int l = j - midfilter; for (int k = 0; k < nfilter; k++) { sum += filter.array[k] * this.array[l++]; } temp.array[j] = sum / wt; } for (int j = nelem - midfilter; j < nelem; j++) { // get weight wt = 0.0; sum = 0.0; int l = j - midfilter; for (int k = 0; k < midfilter + nelem - j; k++) { wt += Math.abs(filter.array[k]); sum += filter.array[k] * this.array[l++]; } temp.array[j] = sum / wt; } return temp; } /** * trims array to lie within limit. * * if flag == BELOW values below limit are set to limit. if flag == ABOVE * values above limit are set to limit. by repeated use of trim() values can * be constrained to lie within or outside a window does not modify this. * * @param flag * BELOW or ABOVE * @param limit * value to constrain * @return new array */ public RealArray trim(Trim flag, double limit) { RealArray temp = new RealArray(nelem); for (int i = 0; i < nelem; i++) { double v = array[i]; if ((flag == Trim.BELOW && v < limit) || (flag == Trim.ABOVE && v > limit)) v = limit; temp.array[i] = v; } return temp; } /** * index of largest element. * * @throws ArrayIndexOutOfBoundsException * array is zero length * @return index */ public int indexOfLargestElement() { if (nelem == 0) { throw new ArrayIndexOutOfBoundsException(); } int index = -1; double value = Double.NEGATIVE_INFINITY; for (int i = 0; i < nelem; i++) { if (array[i] > value) { value = array[i]; index = i; } } return index; } /** * index of smallest element. * * @throws ArrayIndexOutOfBoundsException * array is zero length * @return index */ public int indexOfSmallestElement() { if (nelem == 0) { throw new ArrayIndexOutOfBoundsException(); } int index = -1; double value = Double.POSITIVE_INFINITY; for (int i = 0; i < nelem; i++) { if (array[i] < value) { value = array[i]; index = i; } } return index; } /** * value of largest element. * * @throws ArrayIndexOutOfBoundsException * array is zero length * @return value */ public double largestElement() throws ArrayIndexOutOfBoundsException { return array[indexOfLargestElement()]; } /** * value of largest element. synonym for largestElement(); * * @throws ArrayIndexOutOfBoundsException * array is zero length * @return value */ public double getMax() throws ArrayIndexOutOfBoundsException { return array[indexOfLargestElement()]; } /** * value of smallest element. * * @throws ArrayIndexOutOfBoundsException * array is zero length * @return index */ public double smallestElement() throws ArrayIndexOutOfBoundsException { return array[indexOfSmallestElement()]; } /** * value of smallest element. synonym for smallestElement(); * * @throws ArrayIndexOutOfBoundsException * array is zero length * @return value */ public double getMin() throws ArrayIndexOutOfBoundsException { return array[indexOfSmallestElement()]; } /** * calculates mean as sum of all elements divided by number of elements * @return null if zero-length array. */ public Double getMean() { Double mean = null; if (nelem > 0) { mean = 0.0; for (int i = 0; i < nelem; i++) { mean += array[i]; } mean /= (double) nelem; } return mean; } /** * range of array. * * @throws ArrayIndexOutOfBoundsException * array is zero length * @return (minValue, maxValue) */ public RealRange getRange() throws ArrayIndexOutOfBoundsException { if (nelem == 0) { throw new ArrayIndexOutOfBoundsException(); } RealRange r = new RealRange(); for (int i = 0; i < nelem; i++) { r.add(array[i]); } return r; } /** * delete element and close up. modifies this. * * @param elem * to delete * @throws ArrayIndexOutOfBoundsException * elem out of range */ public void deleteElement(int elem) throws ArrayIndexOutOfBoundsException { if (elem < 0 || elem >= nelem) { throw new ArrayIndexOutOfBoundsException(); } nelem--; if (bufsize > nelem * 2) { bufsize /= 2; } double[] temp = new double[bufsize]; System.arraycopy(array, 0, temp, 0, elem); System.arraycopy(array, elem + 1, temp, elem, nelem - elem); array = temp; } /** * delete elements and close up. modifies this. * * @param low * lowest index inclusive * @param high * highest index inclusive * @throws ArrayIndexOutOfBoundsException * low or high out of range or low {@literal >} high */ public void deleteElements(int low, int high) throws ArrayIndexOutOfBoundsException { if (low < 0 || low > high || high >= nelem) { throw new ArrayIndexOutOfBoundsException(); } int ndeleted = high - low + 1; double[] temp = new double[nelem - ndeleted]; System.arraycopy(array, 0, temp, 0, low); System.arraycopy(array, high + 1, temp, low, nelem - low - ndeleted); array = temp; nelem -= ndeleted; bufsize = nelem; double[] array = new double[nelem]; System.arraycopy(temp, 0, array, 0, nelem); } /** * insert element and expand. modifies this. * * @param elem * index of element to insert * @param f * value of element * @throws ArrayIndexOutOfBoundsException * elem out of range */ public void insertElementAt(int elem, double f) throws ArrayIndexOutOfBoundsException { if (elem < 0 || elem > nelem) { throw new ArrayIndexOutOfBoundsException(); } double[] array1 = new double[nelem + 1]; System.arraycopy(array, 0, array1, 0, elem); array1[elem] = f; System.arraycopy(array, elem, array1, elem + 1, nelem - elem); nelem++; array = array1; } /** * insert an array and expand. modifies this. * * @param elem * index of element to insert * @param f * value of element * @throws ArrayIndexOutOfBoundsException * elem out of range */ public void insertArray(int elem, RealArray f) throws ArrayIndexOutOfBoundsException { int n = f.size(); if (elem < 0 || elem >= nelem || n < 1) { throw new ArrayIndexOutOfBoundsException(); } nelem += n; double[] array1 = new double[nelem]; System.arraycopy(array, 0, array1, 0, elem); System.arraycopy(f.getArray(), 0, array1, elem, n); System.arraycopy(array, elem, array1, n + elem, nelem - elem - n); array = array1; } /** * append element. modifies this. * * @param f * element to append */ public void addElement(double f) { makeSpace(nelem + 1); array[nelem++] = f; } /** * append elements. modifies this. * * @param f * elements to append */ public void addArray(RealArray f) { LOG.trace("COPY0 "+array.length+"//"+nelem+"/"+f.nelem+"/"+array.length); makeSpace(nelem + f.nelem); LOG.trace("COPY1 "+array.length+"//"+nelem+"/"+f.nelem+"/"+array.length); System.arraycopy(f.array, 0, array, nelem, f.nelem); nelem += f.nelem; } /** * get reordered Array. reorder by index in IntSet new(i) = this(idx(i)) * does NOT modify array * * @param idx * array of indexes * @exception EuclidRuntimeException * an element of idx is outside range of this * @return array * */ public RealArray getReorderedArray(IntSet idx) throws EuclidRuntimeException { RealArray temp = new RealArray(nelem); for (int i = 0; i < nelem; i++) { int index = idx.elementAt(i); if (index > nelem) { throw new EuclidRuntimeException("index out of range " + index); } temp.array[i] = array[index]; } return temp; } /** normalize to given number of places * replaces each element by (nint(elem*10^ndec))/10^ndec * @param dd array * @param ndec number of places */ public static void round(double[] dd, int ndec) { for (int i = 0; i < dd.length; i++) { dd[i] = Real.normalize(dd[i], ndec); } } /** * get elements within a range. * * @param r * within which element values must lie * @return indexes of conforming elements */ public IntSet inRange(RealRange r) { int n = size(); IntSet temp = new IntSet(); for (int i = 0; i < n; i++) { if (r.isValid() && r.includes(array[i])) { temp.addElement(i); } } return temp; } /** * get elements outside a range. * * @param r * outside which element values must lie * @return indexes of conforming elements */ public IntSet outOfRange(RealRange r) { int n = size(); IntSet temp = new IntSet(); for (int i = 0; i < n; i++) { if (r.isValid() && !r.includes(array[i])) { temp.addElement(i); } } return temp; } /** * returns values as strings. * * @return string values of elements */ public String[] getStringValues() { String[] temp = new String[nelem]; for (int i = 0; i < nelem; i++) { temp[i] = Double.toString(array[i]); } return temp; } public String getStringArray() { StringBuffer s = new StringBuffer(); for (int i = 0; i < nelem; i++) { if (i > 0) { s.append(S_COMMA); } s.append(array[i]); } return s.toString(); } /** * gets values as string. * within brackets and including commas * @return element values separated with spaces */ public String toString() { // don't change this routine!!! StringBuffer s = new StringBuffer(); s.append(S_LBRAK); for (int i = 0; i < nelem; i++) { if (i > 0) { s.append(S_COMMA); } s.append(array[i]); } s.append(S_RBRAK); return s.toString(); } /** * some operations on float[] (static) */ /** * delete elements (lo - {@literal >} hi inclusive) in a float[] and close up; if hi {@literal >}= * float.length hi is reset to float.length-1. * * @param f * @param hi * @param low * @return new array */ public static double[] deleteElements(double[] f, int low, int hi) { if (hi >= f.length) hi = f.length - 1; if (low < 0) low = 0; int ndel = hi - low + 1; if (ndel <= 0) return f; double[] temp = new double[f.length - ndel]; System.arraycopy(f, 0, temp, 0, low); System.arraycopy(f, hi + 1, temp, low, f.length - hi - 1); return temp; } /** if RA is an autocorrelation array find first maximum after origin. * rather hairy so not generally recommended * runs through array till value below cutoff * then runs till value above cutoff * then aggregates values until drops below cutoff * @param cutoff * @return the maximum element (may be non-integral) */ public double findFirstLocalMaximumafter(int start, double cutoff) { double index = Double.NaN; boolean hitmin = false; boolean hitmax = false; double sigyx = 0.0; double sigy = 0.0; for (int i = start; i < nelem; i++) { double d = array[i]; if (!hitmin && !hitmax) { if (d < cutoff) { hitmin = true; } } else if (hitmin && !hitmax) { if (d > cutoff) { hitmax = true; hitmin = false; } } else if (hitmax) { if (d < cutoff) { hitmin = true; break; } sigyx += d*i; sigy += d; } } if (hitmin && hitmax) { index = sigyx / sigy; } return index; } /** find baseline. * experimental approach to finding baseline and adjusting to it. * Finds peak of distribution * read source code if you need to use this * * @return base offset */ public double getBaseLine() { double baseOffset; Univariate univariate = new Univariate(this); int binCount = 100; univariate.setBinCount(binCount); double step = this.getRange().getRange()/(double)binCount; double min = this.getMin(); int[] bins = univariate.getHistogramCounts(); int ibMax = -1; int binMax = -1; for (int i = 0; i < bins.length; i++) { if (bins[i] > binMax) { binMax = bins[i]; ibMax = i; } } int iMin = -1; for (int i = ibMax; i >= 0; i--) { if (bins[i] < binMax/2) { iMin = i; break; } } iMin = (iMin > 0) ? iMin : 0; int iMax = -1; for (int i = ibMax; i < binCount; i++) { if (bins[i] < binMax/2) { iMax = i; break; } } iMax = (iMax > 0) ? iMax : binCount-1; if (iMin == ibMax || ibMax == binCount-1) { baseOffset = 0.0; } else { double weight = 0.0; double sum = 0.0; for (int i = iMin; i <= iMax; i++) { double w = (double) bins[i]; weight += w; sum += w*i; } double deltaB = sum/weight; baseOffset = step*(deltaB)+min; } return baseOffset; } /** round to decimal places. * * @param places * @return this */ public RealArray format(int places) { for (int i = 0; i < nelem; i++) { array[i] = Util.format(array[i], places); } return this; } /** * copy a double[] into a new one */ /*-- private static double[] copy(double[] f) { double temp[] = new double[f.length]; System.arraycopy(f, 0, temp, 0, f.length); return temp; } --*/ /** * quick sort - modified from p96 - 97 (Hansen - C++ answer book) * * Scalar sort refers to sorting IntArray and RealArray (and similar * classes) where the objects themeselves are sorted. * * Index sort refers to sorting indexes (held as IntSet's) to the object and * getting the sorted object(s) with reorderBy(IntSet idx); * */ private void xfswap(double[] x, int a, int b) { double tmp = x[a]; x[a] = x[b]; x[b] = tmp; } // scalar sort routines (internal) private static final int CUTOFF = 16; private void inssort(int left, int right) { int k; for (int i = left + 1; i <= right; i++) { double v = array[i]; int j; for (j = i, k = j - 1; j > 0 && array[k] > v; j--, k--) { array[j] = array[k]; } array[j] = v; } } private int partition(int left, int right) { int mid = (left + right) / 2; if (array[left] > array[mid]) xfswap(array, left, mid); if (array[left] > array[right]) xfswap(array, left, right); if (array[mid] > array[right]) xfswap(array, mid, right); int j = right - 1; xfswap(array, mid, j); int i = left; double v = array[j]; do { do { i++; } while (array[i] < v); do { j--; } while (array[j] > v); xfswap(array, i, j); } while (i < j); xfswap(array, j, i); xfswap(array, i, right - 1); return i; } private void iqsort(int left, int right) { while (right - left > CUTOFF) { int i = partition(left, right); if (i - left > right - i) { iqsort(i + 1, right); right = i - 1; } else { iqsort(left, i - 1); left = i + 1; } } } /** * sorts array into ascending order. MODIFIES this */ public void sortAscending() { if (nelem <= 0) return; iqsort(0, nelem - 1); inssort(0, nelem - 1); } /** * sorts array into descending order. MODIFIES this */ public void sortDescending() { sortAscending(); reverse(); } /** * puts array into reverse order. MODIFIES this */ public void reverse() { int i = 0, j = nelem - 1; while (i < j) { xfswap(array, i, j); i++; j--; } } /** create new reordered array. * * @param intSet * @return */ public RealArray createReorderedArray(IntSet intSet) { RealArray ra = null; if (intSet.size() == this.size()) { ra = new RealArray(intSet.size()); for (int i = 0; i < intSet.size(); i++) { ra.setElementAt(i, this.get(intSet.elementAt(i))); } } return ra; } private static final int XXCUTOFF = 16; /** * get indexes of ascending sorted array. this array NOT MODIFIED * * @return indexes idx so that element(idx(0)) is lowest */ public IntSet indexSortAscending() { if (nelem <= 0) { return new IntSet(); } IntSet idx = new IntSet(nelem); IntArray iarray = new IntArray(idx.getElements()); xxiqsort(iarray, array, 0, nelem - 1); xxinssort(iarray, array, 0, nelem - 1); try { idx = new IntSet(iarray.getArray()); } catch (Exception e) { throw new EuclidRuntimeException(e.toString()); } return idx; } /** * get indexes of descending sorted array. this array NOT MODIFIED * * @return indexes idx so that element(idx(0)) is highest */ public IntSet indexSortDescending() { IntSet idx; idx = indexSortAscending(); int[] temp = new IntArray(idx.getElements()).getReverseArray(); try { idx = new IntSet(temp); } catch (Exception e) { throw new EuclidRuntimeException(e.toString()); } return idx; } private void xxinssort(IntArray iarr, double[] pfl, int left, int right) { int j, k; for (int i = left + 1; i <= right; i++) { int v = iarr.elementAt(i); for (j = i, k = j - 1; j > 0 && pfl[iarr.elementAt(k)] > pfl[v]; j--, k--) { iarr.setElementAt(j, iarr.elementAt(k)); } iarr.setElementAt(j, v); } } private int xxpartition(IntArray iarr, double[] pfl, int left, int right) { int mid = (left + right) / 2; if (pfl[iarr.elementAt(left)] > pfl[iarr.elementAt(mid)]) xxfswap(iarr, left, mid); if (pfl[iarr.elementAt(left)] > pfl[iarr.elementAt(right)]) xxfswap(iarr, left, right); if (pfl[iarr.elementAt(mid)] > pfl[iarr.elementAt(right)]) xxfswap(iarr, mid, right); int j = right - 1; xxfswap(iarr, mid, j); int i = left; double v = pfl[iarr.elementAt(j)]; do { do { i++; } while (pfl[iarr.elementAt(i)] < v); do { j--; } while (pfl[iarr.elementAt(j)] > v); xxfswap(iarr, i, j); } while (i < j); xxfswap(iarr, j, i); xxfswap(iarr, i, right - 1); return i; } private void xxiqsort(IntArray iarr, double[] pfl, int left, int right) { while (right - left > XXCUTOFF) { int i = xxpartition(iarr, pfl, left, right); if (i - left > right - i) { xxiqsort(iarr, pfl, i + 1, right); right = i - 1; } else { xxiqsort(iarr, pfl, left, i - 1); left = i + 1; } } } private void xxfswap(IntArray iarr, int a, int b) { int t = iarr.elementAt(a); iarr.setElementAt(a, iarr.elementAt(b)); iarr.setElementAt(b, t); } /** * @return monotonicity */ public Monotonicity getMonotonicity() { Monotonicity monotonicity = null; if (size() > 1) { double last = get(0); for (int i = 1; i < size(); i++) { double current = get(i); // equality with last Monotonicity m = null; if (current < last) { m = Monotonicity.DECREASING; } else if (current > last) { m = Monotonicity.INCREASING; } // compare with last monotonocity if (m != null) { if (monotonicity == null) { monotonicity = m; } else if (monotonicity != m) { monotonicity = null; break; } } last = current; } } return monotonicity; } /** * checks RealArray is not null and is of given size. * * @param array * to check * @param size * required size * @throws EuclidRuntimeException * if null or wrong size */ public static void check(RealArray array, int size) throws EuclidRuntimeException { if (array == null) { throw new EuclidRuntimeException("null array"); } else if (array.size() != size) { throw new EuclidRuntimeException("array size required (" + size + ") found " + array.size()); } } /** * parse string as realArray. * * @param s * @param delimiterRegex * @return true if can be parsed. */ public static boolean isFloatArray(String s, String delimiterRegex) { boolean couldBeFloatArray = true; String[] ss = s.split(delimiterRegex); try { new RealArray(ss); } catch (Exception e) { couldBeFloatArray = false; } return couldBeFloatArray; } /** creates scaled array so it runs spans new Range. * e.g. * array = {1,2,3} * thisx0, thisx1 = {0.5, 2.5} * targetx0, targetx1 = {100, 200} * would create {125., 175., 225.} * * allows for x0 {@literal >} x1 * @param thisX0 low map point of this * @param thisX1 high map point of this * @param targetX0 low map point of target * @param targetX1 high map point of target * @return */ public RealArray createScaledArrayToRange(double thisX0, double thisX1, double targetX0, double targetX1) { RealArray newArray = null; if (this.nelem > 1) { Double scale = null; try { scale = (targetX0 - targetX1) / (thisX0 - thisX1); } catch (Exception e) { // } if (scale != null && !Double.isNaN(scale) && !Double.isInfinite(scale) && nelem > 0) { newArray = new RealArray(this); newArray = newArray.addScalar(-thisX0); newArray = newArray.multiplyBy(scale); newArray = newArray.addScalar(targetX0); } } return newArray; } /** creates scaled array so it runs spans new Range. * e.g. * array = {1,2,3} * newRange = {5, 9} * would create (5, 7, 9} * * allows for x0 {@literal >} x1 * @return */ public RealArray createScaledArrayToRange(double x0, double x1) { RealArray newArray = null; if (this.nelem > 1) { double thisX0 = this.get(0); double thisX1 = this.get(nelem - 1); Double scale = null; try { scale = (x0 - x1) / (thisX0 - thisX1); } catch (Exception e) { // } if (scale != null && !Double.isNaN(scale) && !Double.isInfinite(scale) && nelem > 0) { newArray = new RealArray(this); newArray = newArray.addScalar(-thisX0); newArray = newArray.multiplyBy(scale); newArray = newArray.addScalar(x0); } } return newArray; } /** casts an integer array to RealArray * * @param integers * @return */ public static RealArray createRealArray(int[] integers) { RealArray realArray = null; if (integers != null) { realArray = new RealArray(integers.length); for (int i = 0; i < integers.length; i++) { realArray.array[i] = (double) integers[i]; } } return realArray; } /** casts an integer array to RealArray * * @param intArray integer array * @return */ public static RealArray createRealArray(IntArray intArray) { int[] ints = (intArray == null) ? null : intArray.array; return RealArray.createRealArray(ints); } /** calculate differences between elements i and i+1 * * @return array of n-1 differences elem[i+1] - elem[i]. */ public RealArray calculateDifferences() { RealArray differenceArray = new RealArray(nelem - 1); for (int i = 0; i < nelem - 1; i++) { double diff = array[i + 1] - array[i]; differenceArray.setElementAt(i, diff); } return differenceArray; } /** converts RealArray into IntArray. * * casts to (int). * * @return new IntArray */ public IntArray createIntArray() { IntArray intArray = new IntArray(nelem); for (int i = 0; i < nelem; i++) { intArray.array[i] = (int) array[i]; } return intArray; } public Iterator iterator() { return (array == null || array.length < nelem) ? null : new DoubleIterator(array, nelem); } /** assumes all elements are equally spaced with "x" separation of 1.0. * * a linear interpolation approach. calculates new values of cells. x may be gt or lt 0 and may * have integer arts (e.g. 1.23) * * // FIXME not yet working for large negative shifts * * @param delta distance to shift by * @return shifted array */ public RealArray shiftOriginToRight(double delta) { RealArray newArray = new RealArray(nelem); int offset = 0; //shift d to 0,1 interval and compute integer offset while (delta < 0.0) { offset++; delta++; } while (delta >= 1.0) { offset--; delta--; } LOG.trace(offset+" "+delta); for (int i = 0; i < nelem; i++) { int index0 = i - offset +1; int index1 = index0 - 1; double previousValue = getValueCorrectedForEnds(index1); double thisValue = getValueCorrectedForEnds(index0); LOG.trace(i+" "+previousValue+" "+thisValue); double newValue = previousValue * (1.0-delta) + thisValue * delta; newArray.setElementAt(i, newValue); } LOG.trace("newArray: "+newArray.format(2)+"\n"); return newArray; } /** interpolate array into new array of different length. * * * @param newNelem new array length * @return inerpolated array * * */ @Deprecated // not yet tested and better to use imgscalr for images public RealArray scaleAndInterpolate(int newNelem) { if (newNelem == nelem) { return new RealArray(this); } RealArray newArray = new RealArray(newNelem); double old2NewScale = (double) nelem / (double) newNelem; double pixelScale = 1.0; // double old2NewScale = 1.0; for (int iold = 0; iold < nelem; iold++) { LOG.trace("===="+iold+"===="); double value = this.elementAt(iold) * pixelScale; // partial chunk at LH end if (old2NewScale < 1) { scaleFewToMany(newArray, old2NewScale, iold, value); } else { scaleManyToFew(newNelem, newArray, old2NewScale, iold, value); } } LOG.trace("newArray: "+newArray.format(2)+"\n"); return newArray; } private void scaleManyToFew(int newNelem, RealArray newArray, double old2NewScale, int iold, double value) { double lowerNew = (double) iold / old2NewScale; double upperNew = (double) (iold + 1) / old2NewScale; int intLowerNew = (int) lowerNew; int intUpperNew = (int) upperNew; if (intUpperNew == intLowerNew) intUpperNew++; lowerNew = intLowerNew * old2NewScale; upperNew = intUpperNew * old2NewScale; LOG.trace(lowerNew+" "+upperNew); if (lowerNew <= iold && upperNew >= iold+1) { newArray.array[intLowerNew] += value; LOG.trace("complete "+intLowerNew+" "+value); } else { double delta = 0; int index = -1; if (lowerNew > iold) { delta = lowerNew - iold; index = intLowerNew; } else { delta = upperNew - iold; index = intUpperNew; } newArray.array[index] += value * delta; if (index < newNelem -1) newArray.array[index + 1] += value * (1.0 - delta); LOG.trace("split "+index+" "+delta+" "+value); } } private void scaleFewToMany(RealArray newArray, double old2NewScale, int iold, double value) { double lowerNew = ((double) iold) / old2NewScale; double upperNew = ((double) (iold + 1)) / old2NewScale; int intLowerNew = (int) lowerNew; int intUpperNew = (int) upperNew; if (intLowerNew + 1 < upperNew) { double delta = value * (intLowerNew + 1 - lowerNew); LOG.trace("deltalow: "+Util.format(delta, 2)); newArray.array[intLowerNew] += delta; } // these are complete chunks of value for (int middle = intLowerNew + 1; middle < intUpperNew; middle++) { LOG.trace("deltamid: "+Util.format(value, 2)); newArray.array[middle] += value; } if (intUpperNew < upperNew) { double delta = value * (upperNew - intUpperNew); LOG.trace("deltahi: "+Util.format(delta, 2)); newArray.array[intUpperNew] += delta; } } private double getValueCorrectedForEnds(int index) { double value = 0.0; if (index < 0) { value = this.elementAt(0); } else if (index >= nelem) { value = this.elementAt(nelem - 1); } else { value = this.elementAt(index); } return value; } /** assumes all elements are equally spaced with "x" separation of 1.0. * * a linear interpolation approach. calculates new values of cells. x may be gt or lt 0 and may * have integer arts (e.g. 1.23) * * Not fully tested for all cases. * * @param delta distance to shift by * @return shifted array */ public RealArray shiftOriginToRightOld(double delta) { RealArray array0 = new RealArray(this); RealArray array1 = new RealArray(this); int offset = 0; //shift d to 0,1 interval and compute integer offset while (delta < 0) { offset++; delta++; } while (delta >= 1) { offset--; delta--; } double scale = Math.abs(delta); LOG.debug("this: "+this+" "+offset+" "+Util.format(delta, 2)); array0.shiftArrayRight(offset); array1.shiftArrayRight(offset); array0 = array0.multiplyBy(scale); array1 = array1.multiplyBy(1.0 - scale); if (delta >= 0) { array0.shiftArrayRight(-1); } else if (delta < 0) { array1.shiftArrayRight(1); } LOG.debug("array0: "+array0.format(2)); LOG.debug("array1: "+array1.format(2)); RealArray newArray = array0.plus(array1); // if (delta <= 0) { // newArray.shiftArrayRight(offset); // } else { // newArray.shiftArrayRight(offset); // } LOG.debug("newArray: "+newArray.format(2)+"\n"); return newArray; } /** shift array and fill with end elements. * * @param nplaces if positive sift right; if negative shift left */ public void shiftArrayRight(int nplaces) { if (nplaces >= 0) { for (int i = 0; i < nplaces; i++) { this.insertElementAt(0, this.get(0)); this.deleteElement(nelem - 1); } } else { nplaces = 0 - nplaces; for (int i = 0; i < nplaces; i++) { this.insertElementAt(nelem, this.get(nelem - 1)); this.deleteElement(0); } } } /** gets last element in array * @return element ; Double.NaN for zero length array */ public double getLast() { return nelem == 0 ? Double.NaN : this.get(nelem - 1); } } class DoubleIterator implements Iterator { private int counter; private double[] array; private double nelem; public DoubleIterator(double[] array, double nelem) { this.array = array; this.nelem = nelem; counter = -1; } public boolean hasNext() { return counter < nelem - 1; } public Double next() { if (!hasNext()) return null; return (Double) array[++counter]; } public void remove() { throw new UnsupportedOperationException(); } } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/RealComparator.java000077500000000000000000000027631461721410700256540ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid; import java.util.Comparator; /** comparator for use with {@code TreeSet} and other tools which normally require equals(). * epsilon is initially set to zero, so only exact equality matches * * @author pm286 * */ public class RealComparator implements Comparator { private double epsilon = 0.0d; public RealComparator(double eps) { this.setEpsilon(eps); } /** * if Math.abs(d0-d1) {@literal <}= epsilon * return -1 if either arg is null */ public int compare(Double d0, Double d1) { if (d0 == null || d1 == null) { return -1; } double delta = Math.abs(d0 - d1); if (delta <= epsilon) { return 0; } return (d0 < d1) ? -1 : 1; } /** set the tolerance * negative values are converted to positive * @param epsilon */ public void setEpsilon(double epsilon) { this.epsilon = Math.abs(epsilon); } } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/RealInterval.java000066400000000000000000000054661461721410700253310ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid; /** an interval on the real line. * may be negative * it is allowed for the interval to be of zero length * @author pm286 * */ public class RealInterval implements EuclidConstants { double x1; double x2; /** constructor. * defaults to 0,1 * */ public RealInterval() { this(0., 1.0); } /** construct from Real2. * * @param x1 * @param x2 */ public RealInterval(double x1, double x2) { this.x1 = x1; this.x2 = x2; } /** copy constructor. * * @param interval */ public RealInterval(RealInterval interval) { this.x1 = interval.x1; this.x2 = interval.x2; } /** get length. * could be negative * @return length */ public double getLength() { return x2 - x1; } /** constructor from range. * interval direction is therefore always positive * @param range */ public RealInterval(RealRange range) { this(range.getMin(), range.getMax()); } /** get scale to other interval. * scale is interval.length / this.length * @param interval * @return scale may be NaN */ public double scaleTo(RealInterval interval) { double scale = Double.NaN; try { scale = interval.getLength() / this.getLength(); } catch (Throwable t) { // } return scale; } /** offset to translate xthis to xthat after scaling. * X = (x - xthis)*scale + Xthat * X = offset + scale*xthis * so offset = xthat - scale*xthis * @param scale * @param xthis * @param xthat * @return offset */ static double offsetTo(double scale, double xthis, double xthat) { return (xthat - xthis * scale); } /** offset to map one interval onto another. * precise mapping * @param interval * @return offset applied after scaling */ public double offsetTo(RealInterval interval) { return this.offsetTo(interval, this.scaleTo(interval)); } /** offset to map one interval onto another. * maps x1 of each onto eachg other * @param interval to map to * @param scale * @return offset applied after scaling */ public double offsetTo(RealInterval interval, double scale) { return interval.x1 - scale * this.x1; } /** gets midpoint. * * @return midpoint */ public double midPoint() { return (x1 + x2) / 2.0; } } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/RealMatrix.java000066400000000000000000001465521461721410700250130ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid; import java.io.IOException; import java.io.Writer; import java.text.DecimalFormat; import org.apache.log4j.Logger; /** * rectangular real number matrix class RealMatrix represents a rectangular * m-x-n matrix. The basic matrix algebra for non-square matrices is represented * here and this class is also a base for square matrices. *

* Read the signature of each member function carefully as some MODIFY the * object and some CREATE A NEW ONE. Among the reasons for this is that * subclassing (e.g to RealSquareMatrix) is easier with one of these forms in * certain cases. Note that if you modify an object, then all references to it * will refer to the changed object * * @author (C) P. Murray-Rust, 1996 */ public class RealMatrix implements EuclidConstants { final static Logger LOG = Logger.getLogger(RealMatrix.class); /** * number of rows */ protected int rows = 0; /** * number of columns */ protected int cols = 0; /** * the matrix */ protected double[][] flmat = new double[0][0]; DecimalFormat format = null; /** * construct default matrix. cols = rows = 0 */ public RealMatrix() { } /** * Create matrix with given rows and columns. A rows*cols matrix values set * to 0 (rows or cols {@literal <} 0 defaults to 0) * * @param r * number of rows * @param c * number of columns */ public RealMatrix(int r, int c) { if (r < 0) r = 0; if (c < 0) c = 0; rows = r; cols = c; flmat = new double[r][c]; } /** * Create from 1-D array. Formed by feeding in an existing 1-D array to a * rowsXcols matrix THE COLUMN IS THE FASTEST MOVING INDEX, that is the * matrix is filled as flmat(0,0), flmat(0,1) ... C-LIKE. COPIES the array * * @param rows * @param cols * @param array * @exception EuclidRuntimeException * size of array is not rows*cols */ public RealMatrix(int rows, int cols, double[] array) throws EuclidRuntimeException { this(rows, cols); check(rows, cols, array); this.rows = rows; this.cols = cols; int count = 0; for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { flmat[i][j] = array[count++]; } } } /** * creates matrix with initialised values. * * @param r * rows * @param c * columns * @param f * value to initialize with */ public RealMatrix(int r, int c, double f) { this(r, c); for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { flmat[i][j] = f; } } } /** * create from submatrix of another matrix. fails if lowrow {@literal >} hirow, lowrow {@literal <} * 0, etc * * COPIES the parts of m * * @param m * the matrix to slice * @param lowcol * lowest column index * @param hicol * highest column index * @param lowrow * lowest row index * @param hirow * highest row index * @exception EuclidRuntimeException * impossible value of hirow, hicol, lowrow, lowcol */ public RealMatrix(RealMatrix m, int lowrow, int hirow, int lowcol, int hicol) throws EuclidRuntimeException { this(hirow - lowrow + 1, hicol - lowcol + 1); if (hirow >= m.getRows() || lowrow < 0) { throw new EuclidRuntimeException("bad row index: " + lowrow + S_SLASH + hirow + " outside 0/" + m.getRows()); } if (hicol >= m.getCols() || lowcol < 0) { throw new EuclidRuntimeException("bad col index: " + lowcol + S_SLASH + hicol + " outside 0/" + m.getCols()); } for (int i = 0, mrow = lowrow; i < rows; i++, mrow++) { for (int j = 0, mcol = lowcol; j < cols; j++, mcol++) { flmat[i][j] = m.flmat[mrow][mcol]; } } } /** * copy constructor. copies matrix including values * * @param m * matrix to copy */ public RealMatrix(RealMatrix m) { this(m.rows, m.cols); for (int i = 0; i < rows; i++) { System.arraycopy(m.flmat[i], 0, flmat[i], 0, cols); } } /** * shallow copy constructor. copies references (uses same internal array) * * @param m * matrix to copy */ public void shallowCopy(RealMatrix m) { this.rows = m.rows; this.cols = m.cols; this.flmat = m.flmat; } /** * construct from a IntMatrix. * * @param m * matrix to copy (this(i,j) = m(i,j)) */ public RealMatrix(IntMatrix m) { this(m.rows, m.cols); for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { flmat[i][j] = m.flmat[i][j]; } } } /** * constructs an IntMatrix. intm(i,j) = (int) this(i,j), i.e.gets nearest * integers as matrix. * * @return the nearest IntMatrix */ public IntMatrix getIntMatrix() { IntMatrix im = new IntMatrix(rows, cols); int[][] matrix = im.getMatrix(); for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { matrix[i][j] = (int) flmat[i][j]; } } return im; } /** * create from a java matrix. must be rectangular copies matrix values (i.e. * m can be discarded) * * @param m * natrix to copy from * @exception EuclidRuntimeException * m has rows of different lengths */ public RealMatrix(double[][] m) throws EuclidRuntimeException { this(m.length, m[0].length); for (int i = 0; i < rows; i++) { if (m[i].length != cols) { throw new EuclidRuntimeException("non-rectangular matrix cols: " + cols + " row: " + i + " length: " + m[i].length); } for (int j = 0; j < cols; j++) { flmat[i][j] = m[i][j]; } } } /** * set output format. * * @param f * the format */ public void setFormat(DecimalFormat f) { format = f; } /** * get output format. * * @return the format */ public DecimalFormat getFormat() { return format; } /** * get number of rows. * * @return number of rows */ public int getRows() { return rows; } /** * get number of columns. * * @return number of columns */ public int getCols() { return cols; } /** * get matrix as java matrix. shallow copy - any alterations to java matrix * will alter this and vice versa. * * @return matrix as java matrix */ public double[][] getMatrix() { return flmat; } /** * get matrix as array. * * @return matrix as 1-D array in C order: (m(0,0), m(0,1) ...) */ public double[] getMatrixAsArray() { double[] temp = new double[rows * cols]; int count = 0; for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { temp[count++] = flmat[i][j]; } } return temp; } /** * tests matrices for equality. * * uses Real.isEqual(double) for tests * * @param m * @return true if all corresponding elements are equal */ public boolean isEqualTo(RealMatrix m) { boolean ok = true; try { checkConformable(m); for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { if (!Real.isEqual(flmat[i][j], m.flmat[i][j])) { ok = false; break; } } } } catch (EuclidRuntimeException e) { ok = false; } return ok; } // check that plus, subtract is possible private void checkConformable(RealMatrix m) throws EuclidRuntimeException { if (rows != m.rows || cols != m.cols) { throw new EuclidRuntimeException("unequal matrices"); } } // check that multiply is possible private void checkConformable2(RealMatrix m) throws EuclidRuntimeException { if (m.rows != this.cols) { throw new EuclidRuntimeException("unequal matrices (" + this.cols + ", " + m.rows + S_RBRAK); } } private void check(int rows, int cols, double[] array) throws EuclidRuntimeException { if (array == null) { throw new EuclidRuntimeException("RealMatrix(null)"); } if (array.length != rows * cols) { throw new EuclidRuntimeException("rows * cols (" + rows + S_STAR + cols + ") != array (" + array.length + S_RBRAK); } } /** * matrix addition. adds conformable matrices giving NEW matrix. this is * unaltered * * @param m2 * @exception EuclidRuntimeException * m and this are different sizes * @return new matrix */ public RealMatrix plus(RealMatrix m2) throws EuclidRuntimeException { RealMatrix m = new RealMatrix(m2.rows, m2.cols); checkConformable(m2); for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { m.flmat[i][j] = flmat[i][j] + m2.flmat[i][j]; } } return m; } /** * matrix subtraction. subtracts conformable matrices giving NEW matrix this * is unaltered * * @param m2 * @exception EuclidRuntimeException * m and this are different sizes * @return new matrix */ public RealMatrix subtract(RealMatrix m2) throws EuclidRuntimeException { RealMatrix m = new RealMatrix(m2.rows, m2.cols); checkConformable(m2); for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { m.flmat[i][j] = flmat[i][j] - m2.flmat[i][j]; } } return m; } /** * unary minus. negate all elements of matrix; MODIFIES matrix */ public void negative() { for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { flmat[i][j] = -flmat[i][j]; } } } /** * matrix multiplication. * * multiplies conformable matrices to give NEW matrix. this is unaltered * result = 'this' * m; (order matters) * * @param m * @exception EuclidRuntimeException * m and this are different sizes * @return new matrix */ public RealMatrix multiply(RealMatrix m) throws EuclidRuntimeException { checkConformable2(m); RealMatrix m1 = new RealMatrix(rows, m.cols); for (int i = 0; i < rows; i++) { for (int j = 0; j < m.cols; j++) { m1.flmat[i][j] = 0.0; for (int k = 0; k < cols; k++) { m1.flmat[i][j] += flmat[i][k] * m.flmat[k][j]; } } } return m1; } /** * matrix multiplication by a scalar. creates this(i,j) = f*this(i,j) * MODIFIES matrix * * @param f * scalar */ public void multiplyBy(double f) { for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { flmat[i][j] *= f; } } } /** * matrix multiplication. multiplies conformable matrices and stores result * in this matrix. this = 'this' * m; * * @param m * matrix to multiply by * @exception EuclidRuntimeException * m and this are different sizes */ public void multiplyEquals(RealMatrix m) throws EuclidRuntimeException { RealMatrix mm = this.multiply(m); this.rows = mm.rows; this.cols = mm.cols; this.flmat = new double[this.rows][]; for (int i = 0; i < rows; i++) { this.flmat[i] = new double[this.cols]; System.arraycopy(mm.flmat[i], 0, this.flmat[i], 0, this.cols); } } /** * subtract value from each row. this[i,j] = this[i,j] - d[j] modifies this * @param d * array of doubles to subtract * @throws EuclidRuntimeException */ /*-- public void translateByRow(double[] d) { checkColumns(d); for (int i = rows - 1; i >= 0; -- i) { for (int j = cols - 1; j >= 0; -- j) { flmat [i] [j] -= d [j]; } } } --*/ void checkColumns(double[] d) throws EuclidRuntimeException { if (d.length != cols) { throw new EuclidRuntimeException("array size " + d.length + "!= cols length " + cols); } } private void checkRows(double[] d) throws EuclidRuntimeException { if (d.length != rows) { throw new EuclidRuntimeException("array size " + d.length + "!= rows length " + rows); } } /** * subtract value from each colum. this[i,j] = this[i,j] - d[i] modifies * this * * @param d * array of doubles to subtract * @throws EuclidRuntimeException */ public void translateByColumn(double[] d) throws EuclidRuntimeException { checkRows(d); for (int i = cols - 1; i >= 0; --i) { for (int j = rows - 1; j >= 0; --j) { flmat[j][i] -= d[j]; } } } /** * matrix multiplication of a COLUMN vector. creates new vector * * @param f * vector to multiply * @exception EuclidRuntimeException * f.size() differs from cols * @return transformed array */ public RealArray multiply(RealArray f) throws EuclidRuntimeException { if (f.size() != this.cols) { throw new EuclidRuntimeException("unequal matrices"); } double[] temp = new double[rows]; double[] farray = f.getArray(); for (int i = 0; i < rows; i++) { temp[i] = 0.0; for (int j = 0; j < cols; j++) { temp[i] += this.flmat[i][j] * farray[j]; } } RealArray ff = new RealArray(temp); return ff; } /** * divide each column of a matrix by a vector of scalars (that is mat[i][j] = * mat[i][j] / vect[i] - MODIFIES matrix * * @param f * array to divide by * @exception EuclidRuntimeException * f.size() and rows differ */ public void columnwiseDivide(RealArray f) throws EuclidRuntimeException { if (this.cols != f.size()) { throw new EuclidRuntimeException("unequal matrices " + this.cols + S_SLASH + f.size()); } for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { this.flmat[i][j] /= f.elementAt(j); } } } /** * extracts a given element. * * @param row * @param col * @throws EuclidRuntimeException * bad value of row or column * @return the element at row,col */ public double elementAt(int row, int col) throws EuclidRuntimeException { checkRow(row); checkColumn(col); return flmat[row][col]; } /** * checks a row is in range. * * @throws EuclidRuntimeException * if it isn't */ private void checkRow(int row) throws EuclidRuntimeException { if (row < 0 || row >= rows) throw new EuclidRuntimeException("Bad value of row: " + row + S_SLASH + rows); } /** * checks a col is in range. * * @throws EuclidRuntimeException * if it isn't */ private void checkColumn(int col) throws EuclidRuntimeException { if (col < 0 || col >= cols) throw new EuclidRuntimeException("Bad value of col: " + col + S_SLASH + cols); } /** * extracts a given element. * * @param rowcol * represents row,col * @return the element at row,col * @throws EuclidRuntimeException */ public double elementAt(Int2 rowcol) throws EuclidRuntimeException { return elementAt(rowcol.elementAt(0), rowcol.elementAt(1)); } /** * sets a given element MODIFIES matrix * * @param row * @param col * @param f * @throws EuclidRuntimeException */ public void setElementAt(int row, int col, double f) throws EuclidRuntimeException { checkRow(row); checkColumn(col); flmat[row][col] = f; } /** * get value of largest element. * * @return value of largest element */ public double largestElement() { Int2 temp = indexOfLargestElement(); if (temp == null) { throw new EuclidRuntimeException("bug; null index for largest element"); } return this.elementAt(temp); } /** * get index of largest element. * * @return (row, col) */ public Int2 indexOfLargestElement() { Int2 int2 = null; if (cols != 0 && rows != 0) { double f = Double.NEGATIVE_INFINITY; int im = 0; int jm = 0; for (int irow = 0; irow < rows; irow++) { for (int jcol = 0; jcol < cols; jcol++) { if (f < flmat[irow][jcol]) { f = flmat[irow][jcol]; im = irow; jm = jcol; } } } int2 = new Int2(im, jm); } return int2; } /** * get value of largest element in a column * * @param jcol * @return the value * @throws EuclidRuntimeException */ public double largestElementInColumn(int jcol) throws EuclidRuntimeException { return this.elementAt(indexOfLargestElementInColumn(jcol), jcol); } /** * get index of largest element in column. * * @param jcol * index * @return index (-1 if empty matrix) * @throws EuclidRuntimeException * bad value of jcol */ public int indexOfLargestElementInColumn(int jcol) throws EuclidRuntimeException { checkColumn(jcol); int imax = -1; double max = Double.NEGATIVE_INFINITY; for (int irow = 0; irow < rows; irow++) { if (max < flmat[irow][jcol]) { max = flmat[irow][jcol]; imax = irow; } } return imax; } /** * get index of largest element in row. * * @param irow * index * @return index (-1 if empty matrix) * @throws EuclidRuntimeException * bad value of irow */ public int indexOfLargestElementInRow(int irow) throws EuclidRuntimeException { checkRow(irow); int imax = -1; double max = Double.NEGATIVE_INFINITY; for (int jcol = 0; jcol < cols; jcol++) { if (max < flmat[irow][jcol]) { max = flmat[irow][jcol]; imax = jcol; } } return imax; } /** * get index of smallest element in column. * * @param jcol * index * @return index (-1 if empty matrix) * @throws EuclidRuntimeException * bad value of jcol */ public int indexOfSmallestElementInColumn(int jcol) throws EuclidRuntimeException { checkColumn(jcol); int imin = -1; double min = Double.POSITIVE_INFINITY; for (int irow = 0; irow < rows; irow++) { if (min > flmat[irow][jcol]) { min = flmat[irow][jcol]; imin = irow; } } return imin; } protected boolean checkNonEmptyMatrix() { return (cols > 0 && rows > 0); } /** * get value of largest element in a row. * * @param irow * @return value (0 if no columns) * @throws EuclidRuntimeException */ public double largestElementInRow(int irow) throws EuclidRuntimeException { int idx = indexOfLargestElementInRow(irow); if (idx < 0) { throw new EuclidRuntimeException("empty matrix"); } return this.elementAt(irow, idx); } /** * get index of smallest element in row. * * @param irow * index * @return index (-1 if empty matrix) * @throws EuclidRuntimeException * bad value of irow */ public int indexOfSmallestElementInRow(int irow) throws EuclidRuntimeException { checkRow(irow); int imin = -1; double min = Double.POSITIVE_INFINITY; for (int jcol = 0; jcol < cols; jcol++) { if (min > flmat[irow][jcol]) { min = flmat[irow][jcol]; imin = jcol; } } return imin; } /** * get value of smallest element. * * @return value * @throws EuclidRuntimeException */ public double smallestElement() throws EuclidRuntimeException { Int2 temp = indexOfSmallestElement(); return this.elementAt(temp); } /** * get index of smallest element. * * @return (row,col) or null for empty matrix */ public Int2 indexOfSmallestElement() { double f = Double.POSITIVE_INFINITY; int im = -1; int jm = -1; for (int irow = 0; irow < rows; irow++) { for (int jcol = 0; jcol < cols; jcol++) { if (f > flmat[irow][jcol]) { f = flmat[irow][jcol]; im = irow; jm = jcol; } } } return (im >= 0) ? new Int2(im, jm) : null; } /** * get smallest element in a column. * * @param jcol * @return smallest value * @exception EuclidRuntimeException * bad value of jcol */ public double smallestElementInColumn(int jcol) throws EuclidRuntimeException { int idx = indexOfSmallestElementInColumn(jcol); if (idx < 0) { throw new EuclidRuntimeException("empty matrix"); } return this.elementAt(idx, jcol); } /** * get smallest element in a row. * * @param irow * @return smallest value * @exception EuclidRuntimeException * bad value of irow */ public double smallestElementInRow(int irow) throws EuclidRuntimeException { int idx = indexOfSmallestElementInRow(irow); if (idx < 0) { throw new EuclidRuntimeException("empty matrix"); } return this.elementAt(irow, idx); } /** * get index of smallest element in row. * * @param irow * @return index of smallest value */ /*-- public int indexOfSmallestElementInRow(int irow) { double f = Double.POSITIVE_INFINITY; int imax = 0; for(int jcol = 0; jcol < cols; jcol++) { if(f > flmat[irow][jcol]) { f = flmat[irow][jcol]; imax = jcol; } } return imax; } -*/ /** * is matrix Orthogonal row-wise. * * that is row(i) * row(j) = 0 if i not equals j. * * @return true if orthogonal */ public boolean isOrthogonal() { for (int i = 1; i < rows; i++) { RealArray rowi = extractRowData(i); double dot = 0.0; for (int j = i + 1; j < rows; j++) { RealArray rowj = extractRowData(j); dot = rowi.dotProduct(rowj); if (!Real.isZero(dot, Real.getEpsilon())) return false; } } return true; } /** * get Euclidean length of row. * * @param i * the row * @return length of row * @throws EuclidRuntimeException * bad row number */ public double euclideanRowLength(int i) throws EuclidRuntimeException { checkRow(i); RealArray f = extractRowData(i); return f.euclideanLength(); } /** * get array of Euclidean row lengths. * * @return the array (of dimension cols) */ public RealArray euclideanRowLengths() { RealArray fa = new RealArray(rows); for (int i = 0; i < rows; i++) { fa.setElementAt(i, this.euclideanRowLength(i)); } return fa; } /** * get Euclidean length of column. * * @param jcol * the column * @return the length * @throws EuclidRuntimeException */ public double euclideanColumnLength(int jcol) throws EuclidRuntimeException { RealArray f = extractColumnData(jcol); return f.euclideanLength(); } /** * get array of Euclidean column lengths * * @return the array (of dimension rows) */ public RealArray euclideanColumnLengths() { RealArray fa = new RealArray(cols); for (int i = 0; i < cols; i++) { try { fa.setElementAt(i, this.euclideanColumnLength(i)); } catch (Exception e) { e.printStackTrace(); throw new EuclidRuntimeException("BUG " + e); } } return fa; } /** * get column data from matrix. * * @param col * the column * @return the column data (or length rows) * @throws EuclidRuntimeException * bad index */ public RealArray extractColumnData(int col) throws EuclidRuntimeException { checkColumn(col); RealArray fa = new RealArray(rows); for (int i = 0; i < rows; i++) { fa.setElementAt(i, this.flmat[i][col]); } return fa; } /** * get row data from matrix. * * @param row * the column * @return the row data (of length cols) * @throws EuclidRuntimeException * bad index */ public RealArray extractRowData(int row) throws EuclidRuntimeException { checkRow(row); return new RealArray(flmat[row]); } /** * clear matrix. */ public void clearMatrix() { for (int irow = 0; irow < rows; irow++) { for (int jcol = 0; jcol < cols; jcol++) { flmat[irow][jcol] = 0.0; } } } /** * initialise matrix to given double. * * @param f */ public void setAllElements(double f) { for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { flmat[i][j] = f; } } } /** * normalise matrix rows to length of unity */ public void normaliseByRows() { RealArray rowlen = new RealArray(cols); rowlen = euclideanRowLengths(); for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { this.flmat[i][j] /= rowlen.elementAt(i); } } } /** * normalise matrix columns to length of unity */ public void normaliseByColumns() { RealArray ra = euclideanColumnLengths(); this.columnwiseDivide(ra); } /** * transpose matrix - creates new Matrix * * @return transpoe */ public RealMatrix getTranspose() { double[][] m = new double[cols][rows]; for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { m[j][i] = this.flmat[i][j]; } } RealMatrix mm = new RealMatrix(m); return mm; } /** * is the matrix square * * @return is square */ public boolean isSquare() { return (cols == rows && cols > 0); } /** are all elements of matrix zero? * * @param eps tolerance * @return true if all elements zero */ public boolean isZero(double eps) { boolean zero = true; for (int irow = 0; irow < rows; irow++) { for (int icol = 0; icol < cols; icol++) { if (!Real.isZero(flmat[irow][icol], eps)) { zero = false; break; } } } return zero; } /** * delete column from matrix and close up. no-op if impermissible value of * col * * @param col * the column */ public void deleteColumn(int col) { if (col >= 0 && col < cols) { double[][] temp = new double[rows][cols - 1]; for (int i = 0; i < rows; i++) { for (int j = 0; j < col; j++) { temp[i][j] = flmat[i][j]; } for (int j = col + 1; j < cols; j++) { temp[i][j - 1] = flmat[i][j]; } } cols--; flmat = temp; } } /** * delete 2 or more adjacent columns (inclusive) from matrix and close up. * no action if impermissible value of low and high * * @param low * start column * @param high * end column */ public void deleteColumns(int low, int high) { high = (high > cols - 1) ? cols - 1 : high; low = (low < 0) ? 0 : low; for (int i = 0; i < rows; i++) { this.flmat[i] = RealArray.deleteElements(this.flmat[i], low, high); } this.cols -= (high - low + 1); } /** * delete row from matrix and close up. * * @param row */ public void deleteRow(int row) { deleteRows(row, row); } /** * delete 2 or more adjacent rows (inclusive) from matrix and close up. if * (high {@literal >} rows-1 high -{@literal >} rows-1; or low {@literal <} 0, low -{@literal >} 0 * * @param low * start row * @param high * end row */ public void deleteRows(int low, int high) { high = (high >= rows) ? rows - 1 : high; low = (low < 0) ? 0 : low; if (low > high) return; int newrows = rows + high - low - 1; double temp[][] = new double[newrows][cols]; int oldrow = 0; int newrow = 0; while (oldrow < rows) { if (oldrow < low || oldrow > high) { temp[newrow++] = flmat[oldrow]; } oldrow++; } this.rows = newrows; flmat = temp; } /** * replace data in a single column. * * @param column * @param f * data must be of length rows * @throws EuclidRuntimeException */ public void replaceColumnData(int column, RealArray f) throws EuclidRuntimeException { checkRows(f); checkColumn(column); double[] temp = f.getArray(); for (int i = 0; i < rows; i++) { flmat[i][column] = temp[i]; } } private void checkRows(RealArray f) throws EuclidRuntimeException { if (f == null || f.size() != rows) { throw new EuclidRuntimeException("incompatible value of array size: " + f.size() + S_SLASH + rows); } } private void checkColumns(RealArray f) throws EuclidRuntimeException { if (f == null || f.size() != cols) { throw new EuclidRuntimeException("incompatible value of array size: " + f.size() + S_SLASH + cols); } } private void checkColumns(IntSet is) throws EuclidRuntimeException { if (is == null || is.size() != cols) { throw new EuclidRuntimeException("incompatible value of IntSet size: " + is.size() + S_SLASH + cols); } } private void checkColumns(RealMatrix m) throws EuclidRuntimeException { if (m == null || m.getCols() != cols) { throw new EuclidRuntimeException("incompatible value of matrix size: " + m.getCols() + S_SLASH + cols); } } private void checkRows(RealMatrix m) throws EuclidRuntimeException { if (m == null || m.getRows() != rows) { throw new EuclidRuntimeException("incompatible value of matrix size: " + m.getRows() + S_SLASH + rows); } } /** * replace data in a single column. * * @param starting_col * @param f * data must be of length rows * @throws EuclidRuntimeException */ public void replaceColumnData(int starting_col, double[] f) throws EuclidRuntimeException { replaceColumnData(starting_col, new RealArray(rows, f)); } /** * replace data in a block of columns. * * @param start_column * (gets overwritten) * @param m * must have same row count and fit into gap * @throws EuclidRuntimeException */ public void replaceColumnData(int start_column, RealMatrix m) throws EuclidRuntimeException { // must trap copying a matrix into itself! if (this == m) { return; } cols = this.getCols(); int mcols = m.getCols(); checkRows(m); if (start_column < 0) { throw new EuclidRuntimeException("cannot start at negative column: " + start_column); } int end_column = start_column + mcols; if (end_column > cols) { throw new EuclidRuntimeException("too many columns to copy: " + start_column + "|" + mcols + S_SLASH + cols); } copyColumns(m.flmat, start_column, mcols); } private void copyColumns(double[][] mat, int start_column, int nToCopy) { for (int j = 0; j < nToCopy; j++) { for (int i = 0; i < rows; i++) { this.flmat[i][start_column + j] = mat[i][j]; } } } /** * insert a hole into the matrix and expand. result is blank space in matrix * * @param after_col * @param delta_cols */ public void makeSpaceForNewColumns(int after_col, int delta_cols) { if (after_col >= 0 && after_col <= cols && delta_cols > 0) { int newcols = delta_cols + cols; RealMatrix temp = new RealMatrix(rows, newcols); for (int irow = 0; irow < rows; irow++) { for (int jcol = 0; jcol < after_col; jcol++) { temp.flmat[irow][jcol] = this.flmat[irow][jcol]; } for (int jcol = after_col; jcol < cols; jcol++) { temp.flmat[irow][jcol + delta_cols] = this.flmat[irow][jcol]; } } shallowCopy(temp); } } /** * add data as column or column block into matrix and expand. column is * inserted after given column * * @param after_col * -1 to cols-1 * @param f * @throws EuclidRuntimeException */ public void insertColumnData(int after_col, RealArray f) throws EuclidRuntimeException { checkRows(f); if (cols == 0) { rows = f.size(); flmat = new double[rows][1]; double[] arr = f.getArray(); cols = 1; for (int i = 0; i < rows; i++) { flmat[i][0] = arr[i]; } } else { if (f.size() == rows) { makeSpaceForNewColumns(after_col + 1, 1); replaceColumnData(after_col + 1, f); } } } /** * add data as column or column block into matrix and expand. * * @param afterCol * -1 to cols-1 * @param m * @throws EuclidRuntimeException */ public void insertColumnData(int afterCol, RealMatrix m) throws EuclidRuntimeException { // must trap copying a matrix into itself! if (this == m) { return; } checkRows(m); int mcols = m.getCols(); cols = this.getCols(); if (afterCol < -1 || afterCol >= cols) { throw new EuclidRuntimeException("afterCol must be >= -1 or < cols: " + afterCol); } makeSpaceForNewColumns(afterCol + 1, mcols); replaceColumnData(afterCol + 1, m); } /** * make space for new rows in matrix and expand. * * @param after_row * -1 to rows-1 * @param delta_rows * size of space */ public void insertRows(int after_row, int delta_rows) { if (after_row >= 0 && after_row <= cols && delta_rows > 0) { int newrows = delta_rows + rows; RealMatrix temp = new RealMatrix(newrows, cols); for (int jcol = 0; jcol < cols; jcol++) { for (int irow = 0; irow < after_row; irow++) { temp.flmat[irow][jcol] = this.flmat[irow][jcol]; } for (int irow = after_row; irow < rows; irow++) { temp.flmat[irow + delta_rows][jcol] = this.flmat[irow][jcol]; } } shallowCopy(temp); } } /** * overwrite existing row of data. * * @param row * to replace * @param f * row to use * @exception EuclidRuntimeException * f.size() and cols differ */ public void replaceRowData(int row, RealArray f) throws EuclidRuntimeException{ checkColumns(f); int mcols = f.size(); System.arraycopy(f.getArray(), 0, flmat[row], 0, mcols); } /** * overwrite existing row of data. * * @param row * to replace * @param f * row to use * @exception EuclidRuntimeException * f.length and cols differ */ public void replaceRowData(int row, double[] f) throws EuclidRuntimeException { RealArray temp = new RealArray(cols, f); replaceRowData(row, temp); } /** * overwrite existing block of rows; if too big, copying is truncated * * @param afterRow * from -1 to rows-1 * @param m * data to replace with * @exception EuclidRuntimeException * m.rows and this.rows differ */ public void replaceRowData(int afterRow, RealMatrix m) throws EuclidRuntimeException { // must trap copying a matrix into itself! if (this == m) return; checkColumns(m); if (afterRow < -1) { throw new EuclidRuntimeException("afterRow must be >= -1 :" + afterRow); } if (!(afterRow <= (rows - m.rows))) { throw new EuclidRuntimeException("afterRow (" + afterRow + ")must be <= rows (" + rows + ") - m.rows (" + m.rows + S_RBRAK); } copyRowData(m.flmat, afterRow + 1, m.rows); } /** * insert 2 or more adjacent rows of data into matrix and expand * * @param afterRow * from -1 to rows-1 * @param m * data to insert * @exception EuclidRuntimeException * m.cols and this.colsdiffer */ public void insertRowData(int afterRow, RealMatrix m) throws EuclidRuntimeException { // must trap copying a matrix into itself! if (this == m) { return; } rows = this.getRows(); int mrows = m.getRows(); checkColumns(m); if (afterRow < -1) { throw new EuclidRuntimeException("must insert after -1 or higher"); } if (afterRow >= rows) { throw new EuclidRuntimeException("must insert after nrows-1 or lower"); } insertRows(afterRow + 1, mrows); copyRowData(m.flmat, afterRow + 1, mrows); } private void copyRowData(double[][] mat, int afterRow, int nrows) { for (int i = 0; i < nrows; i++) { for (int j = 0; j < cols; j++) { this.flmat[afterRow + i][j] = mat[i][j]; } } } /** * insert row of data into matrix and expand. * * @param after_row * from -1 to rows-1 * @param f * data to insert * @exception EuclidRuntimeException * f.size() and this.cols differ */ public void insertRowData(int after_row, RealArray f) throws EuclidRuntimeException { checkColumns(f); int mcols = f.size(); if (after_row >= -1 && after_row <= rows && mcols == cols) { insertRows(after_row + 1, 1); replaceRowData(after_row + 1, f); } else { throw new EuclidRuntimeException("Cannot add array after row" + after_row + S_SLASH + rows + "==" + mcols + S_SLASH + cols); } } /** * append data to matrix columnwise. * * @param f * data to append * @exception EuclidRuntimeException * f.size() and this.rows differ */ public void appendColumnData(RealArray f) throws EuclidRuntimeException { if (cols == 0) { rows = f.size(); } insertColumnData(cols - 1, f); } /** * append data to matrix columnwise. * * @param m * data to append * @exception EuclidRuntimeException * m.rows and this.rows differ */ public void appendColumnData(RealMatrix m) throws EuclidRuntimeException { if (cols == 0) { rows = m.getRows(); } insertColumnData(cols - 1, m); } /** * append data to matrix rowwise. * * @param f * data to append * @exception EuclidRuntimeException * m.cols and this.cols differ */ public void appendRowData(RealArray f) throws EuclidRuntimeException { if (rows == 0) { cols = f.size(); } insertRowData(rows - 1, f); } /** * append data to matrix rowwise. * * @param m * data to append * @exception EuclidRuntimeException * m.cols and this.cols differ */ public void appendRowData(RealMatrix m) throws EuclidRuntimeException { if (rows == 0) { cols = m.getCols(); } insertRowData(rows - 1, m); } /** * replaces the data in a submatrix. starts at (low_row, low_col) and * extends by the dimensions for the matrix m * * @param low_row * starting row * @param low_col * starting col * @param m * data to append */ public void replaceSubMatrixData(int low_row, int low_col, RealMatrix m) { if (this == m) return; if (low_row > 0 && low_col > 0) { int mrows = m.getRows(); int mcols = m.getCols(); if (low_row + mrows - 1 < rows && low_col + mcols - 1 < cols) { for (int i = 0; i < mrows; i++) { for (int j = 0; j < mcols; j++) { flmat[i + low_row - 1][j] = m.flmat[i][j]; } } } } } /** * reorder the columns of a matrix. * * @param is * indexes to reorder by * @exception EuclidRuntimeException * is.size() and this.cols differ * @return matrix */ public RealMatrix reorderColumnsBy(IntSet is) throws EuclidRuntimeException { checkColumns(is); RealMatrix temp = new RealMatrix(rows, is.size()); for (int i = 0; i < is.size(); i++) { int icol = is.elementAt(i); if (icol >= cols || icol < 0) { throw new EuclidRuntimeException("bad value of column "+icol); } RealArray coldat = this.extractColumnData(icol); temp.replaceColumnData(i, coldat); } return temp; } /** * reorder the rows of a matrix Deleting rows is allowed * * @param is * indexes to reprder by * @exception EuclidRuntimeException * is.size() and this.rows differ * @return matrix */ public RealMatrix reorderRowsBy(IntSet is) throws EuclidRuntimeException { if (is.size() != rows) { throw new EuclidRuntimeException("unequal matrices"); } RealMatrix temp = new RealMatrix(is.size(), cols); for (int i = 0; i < is.size(); i++) { int irow = is.elementAt(i); if (irow >= rows || irow < 0) { throw new ArrayIndexOutOfBoundsException("irow: " + irow); } RealArray rowdat = this.extractRowData(irow); temp.replaceRowData(i, rowdat); } return temp; } /** * extract a RealMatrix submatrix from a RealMatrix * * @param low_row * starting row * @param high_row * end row * @param low_col * starting col * @param high_col * end col * @exception EuclidRuntimeException * low/high_row/col are outside range of this * @return matrix */ public RealMatrix extractSubMatrixData(int low_row, int high_row, int low_col, int high_col) throws EuclidRuntimeException { return new RealMatrix(this, low_row, high_row, low_col, high_col); } /** * make an Real2_Array from columns. * * @param col1 * @param col2 * @throws EuclidRuntimeException * bad values of columns * @return 2*rows data */ public Real2Array extractColumns(int col1, int col2) throws EuclidRuntimeException { RealArray x = this.extractColumnData(col1); RealArray y = this.extractColumnData(col2); return new Real2Array(x, y); } /** * make an Real2_Array from rows. * * @param row1 * @param row2 * @throws EuclidRuntimeException * bad values of rows * @return 2*cols data */ public Real2Array extractRows(int row1, int row2) throws EuclidRuntimeException { RealArray x = this.extractRowData(row1); RealArray y = this.extractRowData(row2); return new Real2Array(x, y); } /** * produce a mask of those elements which fall in a range. result is matrix * with (1) else (0) * * @param r * the range * @throws EuclidRuntimeException * bad values of rows * @return matrix with 1s where data is in range else 0 */ public IntMatrix elementsInRange(RealRange r) throws EuclidRuntimeException { IntMatrix m = new IntMatrix(rows, cols); for (int irow = 0; irow < rows; irow++) { for (int jcol = 0; jcol < cols; jcol++) { int elem = 0; if (r.includes(elementAt(irow, jcol))) { elem = 1; } m.setElementAt(irow, jcol, elem); } } return m; } /** round to decimal places. * * @param places * @return this */ public RealMatrix format(int places) { for (int irow = 0; irow < rows; irow++) { for (int jcol = 0; jcol < cols; jcol++) { flmat[irow][jcol] = Util.format(flmat[irow][jcol], places); } } return this; } /** * output matrix - very crude * * @return the string */ public String toString() { StringBuffer sb = new StringBuffer(); // rows and cols if (rows > 0 && cols > 0) { sb.append(S_LCURLY); sb.append(rows); sb.append(S_COMMA); sb.append(cols); sb.append(S_RCURLY); } else { sb.append(S_LBRAK); } for (int i = 0; i < rows; i++) { sb.append(S_NEWLINE); sb.append(S_LBRAK); for (int j = 0; j < cols; j++) { if (j > 0) { sb.append(S_COMMA); } if (format == null) { sb.append(flmat[i][j]); } else { sb.append(format.format(flmat[i][j])); } } sb.append(S_RBRAK); } if (rows == 0 || cols == 0) { sb.append(S_RBRAK); } return sb.toString(); } /** * output xml as a CML matrix. * * @param w * the writer * @throws IOException */ public void writeXML(Writer w) throws IOException { StringBuffer sb = new StringBuffer(); sb.append(""); for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { if (i != 0 || j != 0) sb.append(S_SPACE); if (format == null) { sb.append(flmat[i][j]); } else { sb.append(format.format(flmat[i][j])); } } } sb.append(""); w.write(sb.toString()); } public RealMatrix createMatrixWithOriginShifted(double deltax, double deltay) { RealMatrix newMatrix = new RealMatrix(this.rows, this.cols, 0.0); LOG.trace(this.rows+"/"+this.cols); for (int irow = 0; irow < rows; irow++) { RealArray rowData = extractRowData(irow); LOG.trace(rowData); rowData = rowData.shiftOriginToRight(deltax); newMatrix.replaceRowData(irow, rowData); } LOG.trace(">> "+newMatrix); for (int jcol = 0; jcol < cols; jcol++) { RealArray columnData = newMatrix.extractColumnData(jcol); columnData = columnData.shiftOriginToRight(deltay); newMatrix.replaceColumnData(jcol, columnData); } LOG.trace(newMatrix.format(2)); return newMatrix; } public RealMatrix scaleAndInterpolate(int newRows, int newCols) { RealMatrix midMatrix = new RealMatrix(this.rows, newCols, 0.0); LOG.trace(this.rows+"/"+this.cols); for (int irow = 0; irow < rows; irow++) { RealArray rowData = extractRowData(irow); LOG.trace(rowData); rowData = rowData.scaleAndInterpolate(newCols); midMatrix.replaceRowData(irow, rowData); } LOG.trace(">> "+midMatrix); RealMatrix newMatrix = new RealMatrix(newRows, newCols, 0.0); for (int jcol = 0; jcol < newCols; jcol++) { RealArray columnData = midMatrix.extractColumnData(jcol); columnData = columnData.scaleAndInterpolate(newRows); newMatrix.replaceColumnData(jcol, columnData); } LOG.trace(newMatrix.format(2)); return newMatrix; } } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/RealRange.java000066400000000000000000000337011461721410700245720ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.log4j.Level; import org.apache.log4j.Logger; /** * maximum and minimum values * * Contains two doubles representing the minimum and maximum of an allowed or * observed range. *

* Default is range with low {@literal >} high; this can be regarded as the uninitialised * state. If points are added to a default RealRange it becomes initialised. * * @author (C) P. Murray-Rust, 1996 */ public class RealRange implements EuclidConstants, Comparable { private static final Logger LOG = Logger.getLogger(RealRange.class); static { LOG.setLevel(Level.DEBUG); } public enum Direction { HORIZONTAL, VERTICAL }; private final static Pattern CURLY_PATTERN1 = Pattern.compile("\\{([^,]+)\\}"); private final static Pattern CURLY_PATTERN2 = Pattern.compile("\\{([^,]+),([^,]+)\\}"); private final static String ANY = "*"; /** * maximum of range */ protected double maxval; /** * minimum of range */ protected double minval; /** * creates invalid range from POSITIVE_INFINITY to NEGATIVE_INFINITY */ public RealRange() { minval = Double.POSITIVE_INFINITY; maxval = Double.NEGATIVE_INFINITY; } /** * initialise with min and max values; if minv {@literal >} maxv create inValid * RealRange * * @param minv * @param maxv */ public RealRange(double minv, double maxv) { setRange(minv, maxv); } /** * initialise with min and max values; if minv {@literal >} maxv create inValid * RealRange * * @param minv * @param maxv * @param normalize swap params if min {@literal >} max */ public RealRange(double minv, double maxv, boolean normalize) { if (minv > maxv) { double temp = minv; minv = maxv; maxv = temp; } setRange(minv, maxv); } /** sets range. * overwrites any previous info * @param minv * @param maxv */ public void setRange(double minv, double maxv) { maxval = maxv; minval = minv; if (minval > maxval) { minval = Double.POSITIVE_INFINITY; maxval = Double.NEGATIVE_INFINITY; } } /** * copy constructor * * @param r */ public RealRange(RealRange r) { minval = r.minval; maxval = r.maxval; } /** * from an IntRange * * @param ir */ public RealRange(IntRange ir) { minval = (double) ir.minval; maxval = (double) ir.maxval; } public static RealRange getRange(String s) { RealRange rr = null; RealArray ra = new RealArray(s); if (ra.size() == 2) { rr = new RealRange(ra.get(0), ra.get(1)); } return rr; } /** use with care as uses == * */ @Override public boolean equals(Object o) { boolean equals = false; if (o != null && o instanceof RealRange) { RealRange ir =(RealRange) o; equals = this.minval == ir.minval && this.maxval == ir.maxval; } return equals; } @Override public int hashCode() { return 17*(int)Math.round(minval) + 31*(int)Math.round(maxval); } /** * a Range is only valid if its maxval is not less than its minval; this * tests for uninitialised ranges * * @return valid */ public boolean isValid() { return (minval <= maxval); } /** * invalid ranges return false * * @param r * @return equal */ @Deprecated public boolean isEqualTo(RealRange r) { return (r != null && Real.isEqual(minval, r.minval) && Real.isEqual(maxval, r.maxval) && minval <= maxval); } /** * invalid ranges return false * * @param r * @return equal */ public boolean isEqualTo(RealRange r, double eps) { return (r != null && Real.isEqual(minval, r.minval, eps) && Real.isEqual(maxval, r.maxval, eps) && minval <= maxval); } /** * combine two ranges if both valid; takes greatest limits of both, else * returns InValid * * @param r2 * @return range */ public RealRange plus(RealRange r2) { if (r2 == null) { return this; } if (!this.isValid()) { if (r2 == null || !r2.isValid()) { return new RealRange(); } return new RealRange(r2); } RealRange temp = new RealRange( Math.min(minval, r2.minval), Math.max(maxval, r2.maxval)); return temp; } public RealRange plusEquals(RealRange r2) { if (r2 != null) { minval = Math.min(minval, r2.minval); maxval = Math.max(maxval, r2.maxval); } return this; } public boolean intersectsWith(RealRange r2) { RealRange r = this.intersectionWith(r2); return r != null && r.isValid(); } /** * intersect two ranges and take the range common to both; * return null if no overlap * * @param r2 * @return range */ public RealRange intersectionWith(RealRange r2) { RealRange inter = null; if (isValid() && r2 != null && r2.isValid()) { double minv = Math.max(minval, r2.minval); double maxv = Math.min(maxval, r2.maxval); if (minv <= maxv) { inter = new RealRange(minv, maxv); } } return inter; } /** * get minimum value (POSITIVE_INFINITY if inValid) * * @return min */ public double getMin() { return minval; } /** * get maximum value (NEGATIVE_INFINITY if inValid) * * @return max */ public double getMax() { return maxval; } /** * get centroid value (NEGATIVE_INFINITY if inValid) * * @return centroid */ public double getMidPoint() { return (minval + maxval) * 0.5; } /** * get range (NaN if invalid) * * @return range */ public double getRange() { if (!isValid()) return Double.NaN; return maxval - minval; } /** * does one range include another * * @param r2 * @return includes */ public boolean includes(RealRange r2) { return (r2 != null && r2.isValid() && this.includes(r2.getMin()) && this .includes(r2.getMax())); } /** * is a double within a RealRange * * If inValid, return false * * @param f * @return includes */ public boolean includes(double f) { return f >= minval && f <= maxval; } /** * synonym for includes() * * @param f * @return includes */ public boolean contains(double f) { return includes(f); } /** * add a value to a range * * @param x */ public void add(double x) { maxval = Math.max(maxval, x); minval = Math.min(minval, x); } /** return a number uniformaly distributed within the range. * * @return number. */ public double getRandomVariate() { double range = maxval - minval; return minval + Math.random() * range; } /** get scale to convert this range to same extent as other. * * @param range to scale to * @return scale or Double.NaN */ public Double getScaleTo(RealRange range) { Double scale = null; if (range != null) { try { scale = range.getRange() / this.getRange(); } catch (Exception e) { // returns null; } } return scale; } /** * if min {@literal >} max swap them */ public void normalize() { if (minval > maxval) { double temp = minval; minval = maxval; maxval = temp; } } /** gets minimum signed translation required to move point into range * If range=(-1, 10) * -3 returns 2 * -1 returns 0 * 3 returns 0 * 10 returns 0 * 12 returns -2 * @param d * @return 0 if in or on range and if null returns Double.NaN */ public double distanceOutside(double d) { double dd = Double.NaN; if (!Double.isNaN(d)) { dd = 0.0; if (d < minval) { dd = d - minval; } else if (d > maxval) { dd = maxval - d; } } return dd; } /** * to string. format: "NULL" or EC.S_LBRAK+minval+S_COMMA+maxval+S_RBRAK; * * @return string */ public String toString() { return (minval > maxval) ? "NULL" : EC.S_LBRAK + minval + EC.S_COMMA + maxval + EC.S_RBRAK; } public RealRange format(Integer decimalPlaces) { maxval = Util.format(maxval, decimalPlaces); minval = Util.format(minval, decimalPlaces); return this; } /** comparees on min values * * @param realRange * @return */ public int compareTo(RealRange realRange) { if (realRange == null) { return -1; } else if (this.minval < realRange.minval) { return -1; } else if (this.minval > realRange.minval) { return 1; } else { if (this.maxval < realRange.maxval) { return -1; } else if (this.maxval > realRange.maxval) { return 1; } } return 0; } /** * subtracts tolerance from min and adds to max * if tolerance is negative adds and subtracts * if this would result in maxval {@literal <} minval sets them to mean * @param tolerance */ public void extendBothEndsBy(double tolerance) { this.minval -= tolerance; this.maxval += tolerance; if (tolerance < 0.0) { if (minval > maxval) { double middle = (minval + maxval) /2.0; minval = middle; maxval = middle; } } } /** extend minval * * @param tolerance if negative cannot get larger than maxval */ public void extendLowerEndBy(double tolerance) { this.minval -= tolerance; if (tolerance < 0.0) { if (minval > maxval) { minval = maxval; } } } /** extend maxval * * @param tolerance if negative cannot get lower than minval */ public void extendUpperEndBy(double tolerance) { this.maxval += tolerance; if (tolerance < 0.0) { if (minval > maxval) { maxval = minval; } } } /** makes new RealRange extended by deltaMin and deltaMax. * * the effect is for positive numbers to increase the range. * if extensions are negative they are applied, but may result * in invalid range (this is not checked at this stage). *

* Does not alter this. *

* * @param minExtend subtracted from min * @param maxExtend added to max */ public RealRange getRangeExtendedBy(double minExtend, double maxExtend) { return new RealRange(minval - minExtend, maxval + maxExtend); } // public static RealRange createRange(String rangeString) { // RealRange range = null; // Matcher matcher = RANGE_PATTERN.matcher(rangeString); // if (matcher.matches()) { // Double min = new Double(matcher.group(1)); // Double max = new Double(matcher.group(2)); // if (min <= max) { // range = new RealRange(min, max); // } // } // return range; // } // /** interprets a String as an RealRange. * * {m,n} is interpreted as RealRange(m,n) * {*,n} is interpreted as RealRange(any,n) * {m,*} is interpreted as RealRange(m,any) * {*,*} is interpreted as RealRange(any,any) * {m} is interpreted as RealRange(m,m) * {*} is interpreted as RealRange(any,any) * * @param token * @return null if cannot create a valid RealRange */ public static RealRange parseCurlyBracketString(String token) { RealRange intRange = null; if (token != null) { Double min = null; Double max = null; token = token.replaceAll("\\s+", ""); // strip spaces Matcher matcher = CURLY_PATTERN2.matcher(token); try { if (matcher.matches()) { String minS = matcher.group(1); String maxS = matcher.group(2); min = (ANY.equals(minS)) ? -Double.MAX_VALUE : Double.valueOf(minS); max = (ANY.equals(maxS)) ? Double.MAX_VALUE : Double.valueOf(maxS); } else { matcher = CURLY_PATTERN1.matcher(token); if (matcher.matches()) { String minS = matcher.group(1); min = (ANY.equals(minS)) ? -Double.MAX_VALUE : Double.valueOf(minS); max = min; } } intRange = new RealRange(min, max); } catch (Exception e) { LOG.error("Cannot parse range: "+token); } } return intRange; } /** creates a list of RealRanges from {...} syntax. * * uses parseCurlyBracketString() * * @param tokens * @return */ public static List createRealRangeList(List tokens) { List realRangeList = new ArrayList(); for (String token : tokens) { RealRange realRange = RealRange.parseCurlyBracketString(token); if (realRange == null) { throw new RuntimeException("Cannot parse ("+token+") as RealRange in : "+tokens); } realRangeList.add(realRange); } return realRangeList; } public static boolean isEqual(RealRange range0, RealRange range1, double eps) { return (range0 == null && range1 == null) ? false : Real.isEqual(range0.getMin(), range1.getMin(), eps) && Real.isEqual(range0.getMax(), range1.getMax(), eps); } /** compares ranges independent of origin. * * @param range * @return */ public boolean isLessThan(RealRange range) { return range != null && getRange() < range.getRange(); } } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/RealRangeArray.java000077500000000000000000000171271461721410700256000ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid; import java.io.PrintStream; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import org.xmlcml.euclid.RealRange.Direction; /** holds an array of RealRanges * may or may not overlap or be sorted * perhaps replace by Google rangeSet of IntervalTree later * @author pm286 * */ public class RealRangeArray implements Iterable { private static final PrintStream SYSOUT = System.out; private List rangeList; private Direction direction; public RealRangeArray() { init(); } /** deep copy * * @param array */ public RealRangeArray(RealRangeArray array) { this(); if (array != null && array.rangeList != null) { for (RealRange range : array.rangeList) { this.add(new RealRange(range)); } } } public RealRangeArray(List r2rList, Direction dir) { init(); for (Real2Range r2r : r2rList) { RealRange range = (RealRange.Direction.HORIZONTAL.equals(dir)) ? r2r.getXRange() : r2r.getYRange(); this.add(range); } this.direction = dir; } public RealRangeArray(List rangeList) { this.rangeList = rangeList; } public RealRangeArray(Real2Range textBox, Direction direction) { RealRange range = textBox.getRealRange(direction); this.add(range); } private void init() { rangeList = new ArrayList(); } public void add(RealRange range) { if (range != null && rangeList != null) { rangeList.add(range); } } public void sort() { Collections.sort(rangeList); } /** sort ranges into order and merge overlapping ones * */ public void sortAndRemoveOverlapping() { sort(); List newList = new ArrayList(); Iterator iterator = rangeList.iterator(); RealRange lastRange = null; while (iterator.hasNext()) { RealRange range = iterator.next(); if (lastRange == null) { newList.add(range); lastRange = range; } else { boolean intersects = lastRange.intersectsWith(range); if (intersects) { RealRange merged = lastRange.plus(range); newList.set(newList.size() - 1, merged); lastRange = merged; } else { newList.add(range); lastRange = range; } } } rangeList = newList; } @Override public boolean equals(Object obj) { if (obj == null || !(obj instanceof RealRangeArray)) { return false; } RealRangeArray array2 = (RealRangeArray) obj; if (this.size() != array2.size()) return false; for (int i = 0; i < this.size(); i++) { if (!this.get(i).equals(array2.get(i))) return false; } return true; } @Override public int hashCode() { int h = 17; for (int i = 0; i < rangeList.size(); i++) { h += rangeList.get(i).hashCode() * 31; } return h; } public int size() { return rangeList.size(); } public RealRange get(int serial) { return rangeList.get(serial); } public void debug() { for (RealRange range : rangeList) { SYSOUT.println(range); } } public RealRangeArray plus(RealRangeArray array) { RealRangeArray newArray = null; if (array != null) { newArray = new RealRangeArray(); for (RealRange realRange : this.rangeList) { newArray.add(new RealRange(realRange)); } for (RealRange realRange : array.rangeList) { newArray.add(new RealRange(realRange)); } newArray.sortAndRemoveOverlapping(); } return newArray; } /** create array representing the gaps in this * gaps at ends are NOT filled * does not alter this * @return */ public RealRangeArray inverse() { RealRangeArray newArray = null; RealRangeArray copy = new RealRangeArray(this); copy.sortAndRemoveOverlapping(); if (copy.size() > 0) { newArray = new RealRangeArray(); RealRange last = null; for (RealRange current : copy) { if (last != null) { RealRange gap = new RealRange(last.maxval, current.minval); newArray.add(gap); } last = current; } } return newArray; } public Iterator iterator() { return rangeList.iterator(); } public void addCaps(Real2Range r2r) { if (direction == null) { throw new RuntimeException("Must give direction"); } addCaps(r2r, direction); } public void addCaps(Real2Range r2r, Direction dir) { if (direction == null) { this.direction = dir; } else { if (direction != dir) { throw new RuntimeException("Cannot change direction"); } } RealRange range = RealRange.Direction.HORIZONTAL.equals(dir) ? r2r.getXRange() : r2r.getYRange(); Double xmin = range.getMin(); Double xmax = range.getMax(); addTerminatingCaps(xmin, xmax); } /** add virtual ends to the array * * @param xmin * @param xmax */ public void addTerminatingCaps(Double xmin, Double xmax) { this.add(new RealRange(xmin, xmin)); this.add(new RealRange(xmax, xmax)); this.sortAndRemoveOverlapping(); } /** remove small ranges (e.g. between characters) * * @param rangeMin */ public void removeLessThan(double rangeMin) { List copyList = new ArrayList(rangeList); for (int i = 0; i < copyList.size(); i++) { RealRange range = copyList.get(i); if (range.getRange() < rangeMin) { rangeList.remove(range); } } } /** is the range completely contained within any subrange? * * @param rr * @return */ public boolean includes(RealRange rr) { for (RealRange range : rangeList) { if (range.includes(rr)) return true; } return false; } public void setDirection(Direction direction) { this.direction = direction; } //=============================================== public String toString() { StringBuilder sb = new StringBuilder(); sb.append("Direction: "+direction+"; size: "+rangeList.size()+"\n("); for (int i = 0; i < rangeList.size(); i++) { sb.append(""+rangeList.get(i)); if ((i+1) %5 == 0) sb.append("\n"); } sb.append(")"); return sb.toString(); } public void format(int decimalPlaces) { for (RealRange range : rangeList) { range.format(decimalPlaces); } } public RealArray getGaps() { RealArray gaps = null; if (rangeList.size() > 1) { gaps = new RealArray(); for (int i = 1; i < rangeList.size(); i++) { double dist = rangeList.get(i).getMin() - rangeList.get(i-1).getMax(); gaps.addElement(dist); } } return gaps; } /** adds tolerance to ends of ranges * see realRange.extendsRangesBy() for positive and negative tolerance * if result means ranges overlap, takes the mean * @param tolerance */ public void extendRangesBy(double tolerance) { if (rangeList.size() > 0) { rangeList.get(0).extendLowerEndBy(tolerance); for (int i = 1; i < rangeList.size(); i++) { RealRange range0 = rangeList.get(i-1); RealRange range1 = rangeList.get(i); double gap = range1.getMin() - range0.getMax(); if (gap < tolerance * 2.) { range0.extendUpperEndBy(gap /2.); range1.extendLowerEndBy(gap /2.); } else { range0.extendUpperEndBy(tolerance); range1.extendLowerEndBy(tolerance); } } rangeList.get(rangeList.size()-1).extendUpperEndBy(tolerance); } } } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/RealRangeComparator.java000077500000000000000000000033611461721410700266240ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid; import java.util.Comparator; /** comparator for use with {@code TreeSet} and other tools which normally require equals(). * * @author pm286 * */ public class RealRangeComparator implements Comparator { private RealComparator comparator; public RealRangeComparator(RealComparator comparator) { this.setComparator(comparator); } public RealRangeComparator(double d) { this(new RealComparator(d)); } /** * if Math.abs(d0-d1) {@literal <}= epsilon * return -1 if either arg is null or any ranges in r0 or r1 are null or comparisons clash */ public int compare(RealRange r0, RealRange r1) { if (r0 == null || r1 == null) { return -1; } Double r0min = r0.getMin(); Double r0max = r0.getMax(); Double r1min = r1.getMin(); Double r1max = r1.getMax(); int compareMin = comparator.compare(r0min, r1min); int compareMax = comparator.compare(r0max, r1max); return (compareMin == compareMax) ? compareMin : -1; } /** set the comparator * @param comparator */ public void setComparator(RealComparator comparator) { this.comparator = comparator; } } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/RealRangeList.java000066400000000000000000000141451461721410700254270ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.apache.log4j.Logger; import org.xmlcml.euclid.RealRange; /** * a sorted list of ranges. * * keeps list sorted at all times. * * when a RealRange is added it looks for the next lowest and highest ranges. If * it does not overlap it is inserted in the free space. If it overlaps with * either or both they merge. * * Currently buublesort-like and assumes "not too many" and may suffer from * quadratic performance. * * // TODO add binary chop or other sort * * @author pm286 * */ public class RealRangeList { private static final long serialVersionUID = 1L; private final static Logger LOG = Logger.getLogger(RealRangeList.class); private List rangeList; private int pointer; private boolean merged; private RealRange newRange; private RealRange oldRange; public RealRangeList() { rangeList = new ArrayList(); } /** * adds range and returns position of result. * * if range overlaps with any existing ranges merges them * * if no overlap inserts before next highest non-overlapping range * * @param range * to add * @return */ public int addRange(RealRange range) { int result = -1; if (rangeList.size() == 0) { rangeList.add(range); result = 0; } else { this.newRange = range; result = insertRange1(); } return result; } // private int insertRange() { // int result = -1; // pointer = 0; // while (pointer < rangeList.size()) { // RealRange lastRange = (pointer == 0) ? null : rangeList // .get(pointer - 1); // RealRange nextRange = (pointer >= rangeList.size()) ? null // : rangeList.get(pointer); // // look for next? // if (newRange.getMin() > nextRange.getMax()) { // pointer++; // continue; // } // // insert? // boolean overlapsLast = overlapsAtBottom(newRange, lastRange) // && lastRange != null; // boolean nextOverlaps = overlapsAtBottom(nextRange, newRange); // if (!overlapsLast && !nextOverlaps) { // result = pointer; // rangeList.add(pointer, newRange); // break; // } // result = pointer; // merged = false; // if (overlapsLast) { // lastRange.plusEquals(newRange); // newRange = lastRange; // result--; // merged = true; // } // // overlap with next and merge //// LOG.debug(this); // result = pointer; // List mergedRanges = iterateTillNextNonOverlapping(newRange, // nextRange, nextOverlaps); // LOG.trace(newRange + "/" +this + "/" + pointer); // if (merged) { // int largest = Math.max(0, rangeList.size() - 1); // newRange.plusEquals(rangeList.get(largest)); // take largest extent // removeOverlappedRangesAndReplaceByOverallRange(result, // mergedRanges); // } // break; // } // // add at end? // if (result == -1) { // result = rangeList.size(); // rangeList.add(newRange); // } // return result; // } private int insertRange1() { pointer = findFirstLargerOrOverlappingExistingRange(); int firstHigher = pointer; List overlappingRanges = findAllOverlappingRanges(); subsumeAndDeleteAllOverlappingRanges(overlappingRanges); rangeList.add(firstHigher, newRange); return firstHigher; } private int findFirstLargerOrOverlappingExistingRange() { pointer = 0; for (; pointer < rangeList.size(); pointer++) { oldRange = rangeList.get(pointer); if (oldRange.getMax() >= newRange.getMin()) { break; } } return pointer; } private List findAllOverlappingRanges() { List overlappingRanges = new ArrayList(); for (; pointer < rangeList.size(); pointer++) { oldRange = rangeList.get(pointer); if (oldRange.getMin() > newRange.getMax()) { break; } newRange.plusEquals(oldRange); overlappingRanges.add(pointer); } return overlappingRanges; } private void subsumeAndDeleteAllOverlappingRanges(List overlappingRanges) { Collections.reverse(overlappingRanges); int noverlap = overlappingRanges.size(); for (int i = 0; i < noverlap; i++) { int toRemove = overlappingRanges.get(i); rangeList.remove(toRemove); } } // private void removeOverlappedRangesAndReplaceByOverallRange( // int result, List mergedRanges) { // Collections.reverse(mergedRanges); // //LOG.debug(mergedRanges); // for (int ii = 0; ii < mergedRanges.size() - 1; ii++) { // int toRemove = (int) mergedRanges.get(ii); // rangeList.remove(toRemove); // } // rangeList.set(result, newRange); // } // private List iterateTillNextNonOverlapping(RealRange range, // RealRange nextRange, boolean nextOverlaps) { // List mergedRanges = new ArrayList(); // while (nextOverlaps) { // range.plusEquals(nextRange); // nextRange = this.get(pointer); // nextOverlaps = overlapsAtBottom(nextRange, range); // if (!nextOverlaps) { // break; // } // mergedRanges.add(pointer); // merged = true; // pointer++; // } // return mergedRanges; // } // // is all of range1 less than all of range0? // private boolean overlapsAtBottom(RealRange higher, RealRange lower) { // return higher != null && lower != null // && lower.getMax() >= higher.getMin(); // } public int size() { return rangeList.size(); } public RealRange get(int i) { return (i < 0 || i >= rangeList.size()) ? null : rangeList.get(i); } public RealRange remove(int i) { return (i < 0 || i >= rangeList.size()) ? null : rangeList.remove(i); } public String toString() { return rangeList.toString(); } } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/RealSquareMatrix.java000066400000000000000000001173341461721410700261700ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid; import org.apache.log4j.Logger; import org.apache.commons.math.linear.EigenDecompositionImpl; import org.apache.commons.math.linear.Array2DRowRealMatrix; import org.apache.commons.math.linear.InvalidMatrixException; /** * square matrix class * * RealSquareMatrix represents a square m-x-m matrix. The basic matrix algebra * for square matrices is represented here Check out the exciting member * functions, which are supported by Exceptions where appropriate. (NB. No * attempt has been made to provide numerical robustness and inversion, * diagonalisation, etc are as you find them.) *

* * @author (C) P. Murray-Rust, 1996 */ public class RealSquareMatrix extends RealMatrix { final static Logger LOG = Logger.getLogger(RealSquareMatrix.class); /** * helper class to provide types of matrix. */ /** type */ public enum Type { /** */ UPPER_TRIANGLE(1), /** */ LOWER_TRIANGLE(2), /** */ SYMMETRIC(3), /** */ DIAGONAL(4), /** */ OUTER_PRODUCT(5), /** */ UNKNOWN(6); /** integer value */ public int i; private Type(int i) { this.i = i; } } private EigenDecompositionImpl eigenDecompositionImpl; private RealSquareMatrix inverse; private RealArray eigenvalues; private RealSquareMatrix eigenvectors; /** * Constructor. This gives a default matrix with cols = rows = 0. */ public RealSquareMatrix() { super(); } /** * Constructor. * * @param rows * number of rows and columns values are set to zero */ public RealSquareMatrix(int rows) { super(rows, rows); } /** * Creates square matrix from real matrix. * * @param f * real array (length rows) multiplied to give outer product * @return square matrix of size rows * rows */ public static RealSquareMatrix outerProduct(RealArray f) { int rows = f.size(); RealSquareMatrix temp = new RealSquareMatrix(rows); for (int i = 0; i < rows; i++) { for (int j = 0; j < rows; j++) { temp.flmat[i][j] = f.elementAt(i) * f.elementAt(j); } } return temp; } /** * create diagonal matrix from real matrix. * * @param f * real array (length rows) * @return square matrix with elem (i, i) = f(i), else 0.0 */ public static RealSquareMatrix diagonal(RealArray f) { int rows = f.size(); RealSquareMatrix temp = new RealSquareMatrix(rows); for (int i = 0; i < rows; i++) { temp.flmat[i][i] = f.elementAt(i); } return temp; } /** * Creates real square matrix from array * THE COLUMN IS THE FASTEST MOVING * INDEX, that is the matrix is filled as mat(0,0), mat(0,1) ... C-LIKE * * @param rows * the final rows and cols of real square matrix * @param array * of size (rows * rows) * @exception EuclidRuntimeException * array size must be multiple of rows */ public RealSquareMatrix(int rows, double[] array) throws EuclidRuntimeException { super(rows, rows, array); } /** * Creates real square matrix with all elements initialized to double value. * * @param rows * size of square matrix * @param f * value of all elements */ public RealSquareMatrix(int rows, double f) { super(rows, rows, f); } /** * Constructor for submatrix of another matrix. * * @param m * matrix to slice (need not be square) * @param lowrow * the start row inclusive (count from 0) * @param lowcol * the start column inclusive (count from 0) * @param rows * size of final matrix * @exception EuclidRuntimeException * lowrow, lowcol or rows are not consistent with size of * m */ public RealSquareMatrix(RealMatrix m, int lowrow, int lowcol, int rows) throws EuclidRuntimeException { super(m, lowrow, lowrow + rows - 1, lowcol, lowcol + rows - 1); } /** * copy constructor. * * @param m * matrix to copy */ public RealSquareMatrix(RealSquareMatrix m) { super(m); } /** * shallow copy from RealMatrix * * the array values are not copied (only the reference) * * @param m * matrix to copy reference from * * @exception EuclidRuntimeException * m must be square (that is cols = rows) */ public RealSquareMatrix(RealMatrix m) throws EuclidRuntimeException { super(m.rows, m.cols); if (m.cols != m.rows) { throw new EuclidRuntimeException("non square matrix"); } this.flmat = m.flmat; } /** * constructor from array. * * form from a Java 2-D array (it holds row and column count) * * @param matrix * to copy * @exception EuclidRuntimeException * matrix is not square (might even not be * rectangular!) */ public RealSquareMatrix(double[][] matrix) throws EuclidRuntimeException { super(matrix); if (cols != rows) { throw new EuclidRuntimeException("non square matrix"); } } /** * shallowCopy an existing square matrix. * * @param m * matrix to shallow copy * @exception EuclidRuntimeException * m must have the same number of rows and cols as * this */ public void shallowCopy(RealSquareMatrix m) throws EuclidRuntimeException { super.shallowCopy((RealMatrix) m); } /** create square matrix from lower triangle values * upper triangle is filled with zeros * @param f real array (length rows * (rows+1) / 2) * @return square matrix with elem (i, j) = f(k++), else 0.0 */ public static RealSquareMatrix fromLowerTriangle(RealArray f) { int n = f.size(); int rows = (int) Math.round((Math.sqrt(8*n+1) - 1 + 0.001) / 2.); if ((rows * (rows + 1))/2 != n) { throw new RuntimeException("band number of values ("+n+") for lower Triangle"); } RealSquareMatrix temp = new RealSquareMatrix(rows); int count = 0; for (int i = 0; i < rows; i++) { for (int j = 0; j <= i; j++) { temp.flmat[i][j] = f.elementAt(count); if (i != j) { temp.flmat[j][i] = 0.0; } count++; } } return temp; } /** create square matrix from lower triangle * lower triangle is filled with zeros * @param f real array (length rows * (rows+1) / 2) * @return square matrix with elem (i, j) = f(k++), else 0.0 */ public static RealSquareMatrix fromUpperTriangle(RealArray f) { int n = f.size(); int rows = (int) Math.round((Math.sqrt(8*n+1) - 1 + 0.001) / 2.); if ((rows * (rows + 1))/2 != n) { throw new RuntimeException("band number of values ("+n+") for lower Triangle"); } RealSquareMatrix temp = new RealSquareMatrix(rows); int count = 0; for (int i = 0; i < rows; i++) { for (int j = i; j < rows; j++) { temp.flmat[i][j] = f.elementAt(count); if (i != j) { temp.flmat[j][i] = 0.0; } count++; } } return temp; } /** * are two matrices identical * * @param r * matrix to compare * @return true if equal (see RealMatrix.equals for details) */ public boolean isEqualTo(RealSquareMatrix r) { return super.isEqualTo((RealMatrix) r); } /** * matrix addition. adds conformable matrices. Does NOT alter this. * * @param m * matrix to add * @exception EuclidRuntimeException * m must have the same number of rows and cols as * this * @return resultant matrix */ public RealSquareMatrix plus(RealSquareMatrix m) throws EuclidRuntimeException { RealMatrix temp = super.plus((RealMatrix) m); RealSquareMatrix sqm = new RealSquareMatrix(temp); return sqm; } /** * matrix subtraction. subtracts conformable matrices. Does NOT alter this. * * @param m * matrix to subtract from this * @exception EuclidRuntimeException * m must have the same number of rows and cols as * this * @return resultant matrix */ public RealSquareMatrix subtract(RealSquareMatrix m) throws EuclidRuntimeException { RealMatrix temp = super.subtract((RealMatrix) m); RealSquareMatrix sqm = new RealSquareMatrix(temp); return sqm; } /** * matrix multiplication. * * multiplies conformable matrices; result is this*m * * @param m * matrix to multiply by * @exception EuclidRuntimeException * m must have the same number of rows as this * has cols * @return new matrix */ public RealSquareMatrix multiply(RealSquareMatrix m) throws EuclidRuntimeException { RealMatrix temp = super.multiply((RealMatrix) m); RealSquareMatrix sqm = new RealSquareMatrix(temp); return sqm; } /** * determinant. hardcoded up to order 3 at present; rest is VERY slow :-( * calls determinant recursively for order {@literal >} 3 * * @return the determinant */ public double determinant() { double det = 0.0; if (rows == 1) { det = flmat[0][0]; } else if (rows == 2) { det = flmat[0][0] * flmat[1][1] - flmat[1][0] * flmat[0][1]; } else if (rows == 3) { det = flmat[0][0] * (flmat[1][1] * flmat[2][2] - flmat[1][2] * flmat[2][1]) + flmat[0][1] * (flmat[1][2] * flmat[2][0] - flmat[1][0] * flmat[2][2]) + flmat[0][2] * (flmat[1][0] * flmat[2][1] - flmat[1][1] * flmat[2][0]); } else { int sign = 1; for (int j = 0; j < cols; j++) { det += sign * flmat[0][j] * minorDet(j); sign = -sign; } } return det; } private double minorDet(int ii) { int r = rows - 1; double array[] = new double[r * r]; int countN = 0; for (int i = 1; i < rows; i++) { for (int j = 0; j < cols; j++) { if (j != ii) { array[countN++] = flmat[i][j]; } } } RealSquareMatrix mm = null; try { mm = new RealSquareMatrix(r, array); } catch (Exception e) { throw new EuclidRuntimeException(e.toString()); } double d = mm.determinant(); return d; } /** * trace. * * @return the trace */ public double trace() { double trace = 0.0; for (int i = 0; i < rows; i++) { trace += flmat[i][i]; } return trace; } /** * is it a unit matrix. * * @return are all diagonals 1 and off-diagonal zero (within Real.isEqual()) */ public boolean isUnit() { for (int i = 0; i < rows; i++) { for (int j = 0; j < rows; j++) { double f = flmat[i][j]; if ((!Real.isZero(f, Real.getEpsilon()) && (i != j)) || (!Real.isEqual(f, 1.0, Real.getEpsilon()) && (i == j))) { return false; } } } return true; } /** * is matrix symmetric. * * @return is Real.isEqual(elem(i,j), elem(j,i)) */ public boolean isSymmetric() { for (int i = 0; i < rows - 1; i++) { for (int j = i + 1; j < rows; j++) { if (!Real.isEqual(flmat[i][j], flmat[j][i])) { return false; } } } return true; } /** * orthonormalise matrix. crude (only works for 1, 2x2, 3x3 at present) - * use jama? * * @exception EuclidRuntimeException * I have only written this for this.rows up to 3. * If anyone can find a routine, this will disappear ... -( * @return the orthonormalized matrix */ public RealSquareMatrix orthonormalize() { if (cols == 1) { flmat[0][0] = 1.; } else if (cols == 2) { Vector3 v1 = new Vector3(flmat[0][0], flmat[0][1], 0.); v1 = v1.normalize(); Vector3 v2 = new Vector3(flmat[1][0], flmat[1][1], 0.); v2 = v2.normalize(); Vector3 v3 = v1.cross(v2); v2 = v3.cross(v1); v2 = v2.normalize(); flmat[0][0] = v1.flarray[0]; flmat[0][1] = v1.flarray[1]; flmat[1][0] = v2.flarray[0]; flmat[1][1] = v2.flarray[1]; } else if (cols == 3) { Vector3 v0 = new Vector3(extractRowData(0)); Vector3 v1 = new Vector3(extractRowData(1)); Vector3 v2 = new Vector3(extractRowData(2)); // check handedness double det = v0.getScalarTripleProduct(v1, v2); v0.normalize(); v2 = v0.cross(v1); v2.normalize(); v1 = v2.cross(v0); if (det < 0.0) { v2.negative(); } replaceRowData(0, v0.getArray()); replaceRowData(1, v1.getArray()); replaceRowData(2, v2.getArray()); } else { throw new EuclidRuntimeException( "Sorry: orthonormalise only up to 3x3 matrices: had " + cols + " columns"); } return this; } /** * is matrix unitary. i.e is it orthonormal? * * (synonym for isUnitary()) * * @return true if is Unitary */ public boolean isOrthonormal() { return isUnitary(); } /** * is matrix UpperTriangular. * * @return true if all bottom triangle excluding diagona Real.isZero() */ public boolean isUpperTriangular() { for (int i = 1; i < rows; i++) { for (int j = 0; j < i; j++) { if (!Real.isZero(flmat[i][j], Real.getEpsilon())) return false; } } return true; } /** * is matrix LowerTriangular. diagonal must also be zero * * @return true if all bottom triangle Real.isZero() */ public boolean isLowerTriangular() { for (int i = 0; i < rows - 1; i++) { for (int j = i + 1; j < rows; j++) { if (!Real.isZero(flmat[i][j], Real.getEpsilon())) return false; } } return true; } double rowDotproduct(int row1, int row2) { double sum = 0.0; for (int i = 0; i < cols; i++) { sum += flmat[row1][i] * flmat[row2][i]; } return sum; } /** * is matrix orthogonal. * * checks if Real.isZero(dotProduct) (rowwise calculation) * * @return true if orthogonal */ public boolean isOrthogonal() { for (int i = 0; i < rows - 1; i++) { for (int j = i + 1; j < rows; j++) { double dp = rowDotproduct(i, j); if (!Real.isZero(dp, Real.getEpsilon())) { return false; } } } return true; } /** * is matrix an improper rotation. * * @return true if determinant is -1.0 and isOrthogonal * */ public boolean isImproperRotation() { double f = determinant(); return (Real.isEqual(f, -1.0) && isOrthogonal()); } /** * is matrix unitary. i.e is it orthonormal? * * (synonym for isUnitary()) * * @return true if is Unitary */ public boolean isUnitary() { double f = determinant(); double fa = Math.abs(f); return (Real.isEqual(fa, 1.0) && isOrthogonal()); } /** * copy upper triangle into lower triangle. alters this to make it symmetric * * @return this as new square matrix */ public RealSquareMatrix copyUpperToLower() { for (int i = 0; i < cols - 1; i++) { for (int j = i + 1; j < cols; j++) { flmat[j][i] = flmat[i][j]; } } return this; } /** * copy lower triangle into upper triangle. alters this to make it symmetric * * @return this as new square matrix */ public RealSquareMatrix copyLowerToUpper() { for (int i = 0; i < cols - 1; i++) { for (int j = i + 1; j < cols; j++) { flmat[i][j] = flmat[j][i]; } } return this; } /** * copy lower triangle into linear array; order: 0,0; 1,0; 1,1; 2,0 * * @return linear array of size rows * (rows+1) / 2 */ public RealArray lowerTriangle() { int n = rows; RealArray triangle = new RealArray((n * (n + 1)) / 2); int count = 0; for (int i = 0; i < rows; i++) { for (int j = 0; j <= i; j++) { triangle.setElementAt(count++, flmat[i][j]); } } return triangle; } /** * transpose. MODIFIES this */ public void transpose() { for (int i = 0; i < rows; i++) { for (int j = 0; j < i; j++) { double t = flmat[i][j]; flmat[i][j] = flmat[j][i]; flmat[j][i] = t; } } } /** * diagonalisation returns eigenvalues and vectors as MODIFIED arguments; * 'this' is NOT affected USE JAMA INSTEAD Note that IllCondMatrixException * is RETURNED and not thrown * * @param eigenvalues * @param eigenvectors * @param illCond * @exception EuclidRuntimeException * must have at least order 2 * @return flag */ public int diagonaliseAndReturnRank(RealArray eigenvalues, RealSquareMatrix eigenvectors, EuclidRuntimeException illCond) throws EuclidRuntimeException { // because this was translated from FORTRAN there are some offsets // store current matrix as 1-D array lower Triangle RealArray lowert = this.lowerTriangle(); // f77 offset double[] lower77 = new double[lowert.size() + 1]; System.arraycopy(lowert.getArray(), 0, lower77, 1, lowert.size()); int order = rows; // size must be at least 2! if (rows < 2) { throw new EuclidRuntimeException("need at least 2 rows"); } double[] eigenval77 = new double[rows + 1]; double[] eigenvect77 = new double[rows * rows + 1]; illCond = null; int rank = Diagonalise.vneigl(order, lower77, eigenval77, eigenvect77, illCond); // unoffset the f77 double[] eigenval = new double[rows]; System.arraycopy(eigenval77, 1, eigenval, 0, rows); double[] eigenvect = new double[rows * rows]; System.arraycopy(eigenvect77, 1, eigenvect, 0, rows * rows); eigenvalues.shallowCopy(new RealArray(eigenval)); eigenvectors.shallowCopy(new RealSquareMatrix(rows, eigenvect)); return rank; } /** * orthogonalise matrix. (only works for 3x3 at present); MODIFIES this * * @exception EuclidRuntimeException * I have only written this for this.rows up to 3. * If anyone can find a routine, this will disappear ... -( */ public void orthogonalise() throws EuclidRuntimeException { if (cols == 3) { Vector3 v0 = new Vector3(extractRowData(0)); Vector3 v1 = new Vector3(extractRowData(1)); Vector3 v2 = new Vector3(extractRowData(2)); double l0 = v0.getLength(); double l1 = v1.getLength(); double l2 = v2.getLength(); /** * check handedness */ double det = v0.getScalarTripleProduct(v1, v2); v0.normalize(); v2 = v0.cross(v1); v2.normalize(); v1 = v2.cross(v0); if (det < 0.0) { v2 = v2.negative(); } v0 = v0.multiplyBy(l0); v1 = v1.multiplyBy(l1); v2 = v2.multiplyBy(l2); replaceRowData(0, v0.getArray()); replaceRowData(1, v1.getArray()); replaceRowData(2, v2.getArray()); } else { throw new EuclidRuntimeException( "Sorry: orthogonalise only up to 3x3 matrices"); } } public RealArray calculateEigenvalues() { solveDecomposition(); return eigenvalues; } public RealSquareMatrix calculateInverse() { solveDecomposition(); return inverse; } private void solveDecomposition() { if (eigenDecompositionImpl == null) { Array2DRowRealMatrix realMatrix = new Array2DRowRealMatrix(this.getMatrix()); try { eigenDecompositionImpl = new EigenDecompositionImpl(realMatrix, 0.0); inverse = new RealSquareMatrix(eigenDecompositionImpl.getSolver().getInverse().getData()); eigenvalues = new RealArray(eigenDecompositionImpl.getRealEigenvalues()); eigenvectors = new RealSquareMatrix(eigenDecompositionImpl.getV().getData()); } catch (InvalidMatrixException ime) { inverse = null; eigenvalues = null; eigenvectors = null; } } } /** V is an orthogonal matrix, i.e. its transpose is also its inverse. The columns of V are the eigenvectors of the original matrix. uses apachae.commons.math * @return */ public RealSquareMatrix calculateEigenvectors() { solveDecomposition(); return eigenvectors; } public void resetEigenDecomposition() { eigenDecompositionImpl = null; inverse = null; } /** * create orthogonlisation matrix from cell lengths and angles. Rollett * "Computing Methods in Crystallography" Pergamon 1965 p.23 * * @param celleng * 3 lengths * @param angle * 3 angles in degrees * @return matrix */ public static RealSquareMatrix getCrystallographicOrthogonalisation( double[] celleng, double[] angle) { RealSquareMatrix orthMat = new RealSquareMatrix(3); double dtor = Math.PI / 180.0; double sina = Math.sin(dtor * angle[0]); double cosa = Math.cos(dtor * angle[0]); double sinb = Math.sin(dtor * angle[1]); double cosb = Math.cos(dtor * angle[1]); double cosg = Math.cos(dtor * angle[2]); double cosgstar = (cosa * cosb - cosg) / (sina * sinb); double singstar = Math.sqrt(1.0 - cosgstar * cosgstar); double[][] omat = orthMat.getMatrix(); omat[0][0] = celleng[0] * sinb * singstar; omat[0][1] = 0.0; omat[0][2] = 0.0; omat[1][0] = -celleng[0] * sinb * cosgstar; omat[1][1] = celleng[1] * sina; omat[1][2] = 0.0; omat[2][0] = celleng[0] * cosb; omat[2][1] = celleng[1] * cosa; omat[2][2] = celleng[2]; return orthMat; } /** * inversion of matrix. creates NEW matrix * Hard-coded up to 3x3, if matrix is larger uses JAMA * * @exception EuclidRuntimeException * singular matrix (or worse!) * @return inverse matrix */ public RealSquareMatrix getInverse() throws EuclidRuntimeException { double[][] inv = new double[rows][rows]; double[][] temp = getMatrix(); double det = this.determinant(); if (det == 0) { throw new EuclidRuntimeException("Cannot invert matrix: determinant=0"); } double detr = 1 / det; if (this.rows == 1) { // 1x1 Matrix inv[0][0] = detr; } else if (this.rows == 2) { // 2x2 Matrix inv[0][0] = detr * temp[1][1]; inv[1][0] = 0 - (detr * temp[0][1]); inv[0][1] = 0 - (detr * temp[1][0]); inv[1][1] = detr * temp[0][0]; } else if (this.rows == 3) { // 3x3 Matrix inv[0][0] = detr * (temp[1][1] * temp[2][2] - temp[1][2] * temp[2][1]); inv[0][1] = detr * (temp[0][2] * temp[2][1] - temp[0][1] * temp[2][2]); inv[0][2] = detr * (temp[0][1] * temp[1][2] - temp[0][2] * temp[1][1]); inv[1][0] = detr * (temp[1][2] * temp[2][0] - temp[1][0] * temp[2][2]); inv[1][1] = detr * (temp[0][0] * temp[2][2] - temp[0][2] * temp[2][0]); inv[1][2] = detr * (temp[0][2] * temp[1][0] - temp[0][0] * temp[1][2]); inv[2][0] = detr * (temp[1][0] * temp[2][1] - temp[1][1] * temp[2][0]); inv[2][1] = detr * (temp[0][1] * temp[2][0] - temp[0][0] * temp[2][1]); inv[2][2] = detr * (temp[0][0] * temp[1][1] - temp[0][1] * temp[1][0]); } else { throw new EuclidRuntimeException("Inverse of larger than 3x3 matricies: NYI"); } RealSquareMatrix imat = new RealSquareMatrix(inv); return imat; } /** * invert a square matrix from Hansen (The C++ answer Book - pp 114-5) */ private boolean dopivot(double[][] A, double[][] I, int diag, int nelem) { if (A[diag][diag] != 0.0) return true; int i; for (i = diag + 1; i < nelem; i++) { if (A[i][diag] != 0.0) { double[] t; t = A[diag]; A[diag] = A[i]; A[i] = t; t = I[diag]; I[diag] = I[i]; I[i] = t; break; } } return i < nelem; } @SuppressWarnings("unused") private void matinv(double[][] A, double[][] I, int nelem) throws EuclidRuntimeException { // identity for (int i = 0; i < nelem; i++) { for (int j = 0; j < nelem; j++) { I[i][j] = 0.0; } I[i][i] = 1.0; } for (int diag = 0; diag < nelem; diag++) { if (!dopivot(A, I, diag, nelem)) { throw new EuclidRuntimeException("singular matrix"); } double div = A[diag][diag]; if (div != 1.0) { A[diag][diag] = 1.0; for (int j = diag + 1; j < nelem; j++) A[diag][j] /= div; for (int j = 0; j < nelem; j++) I[diag][j] /= div; } for (int i = 0; i < nelem; i++) { if (i == diag) continue; double sub = A[i][diag]; if (sub != 0.0) { A[i][diag] = 0.0; for (int j = diag + 1; j < nelem; j++) A[i][j] -= sub * A[diag][j]; for (int j = 0; j < nelem; j++) I[i][j] -= sub * I[diag][j]; } } } } } /* in separate class because jvc bombs on compiling this */ class Diagonalise implements EuclidConstants { final static double ZERO = 0.0; final static double ONE = 1.0; final static double TWO = 2.0; final static double SQRT2 = Math.sqrt(TWO); /** * diagonalisation routine * * @param order * @param a * matrix as 1d array * @param eigval * eigenvalues * @param eigvec * egenvectors as 1d array * @param illCond * @throws EuclidRuntimeException * contains reason for failure (probably illconditioned) * @exception EuclidRuntimeException * order < 2 * @return flag */ public static int vneigl(int order, double[] a, double[] eigval, double[] eigvec, EuclidRuntimeException illCond) throws EuclidRuntimeException { /*********************************************************************** * // translated from FORTRAN * * * BECAUSE THIS HAS BEEN TRANSLATED, THERE IS AN OFFSET OF 1 IN THE * ARRAY ADDRESSES * * YOU SHOULD NEVER CALL THIS DIRECTLY, BUT USE THE WRAPPER * (diagonalise). Category: Eigenvectors and Inversion Function: * Destructive diagonalisation of symmetric matrix Mnemonic: Vector * N-dimensional : EIGenvectors/values of Lower triangle * * * Inputs: long int order : order of matrix * * Input And Outputs: double a[] : lower triangular matrix (DESTROYED) * * Outputs: double eigval[] : returned eigenvalues double eigvec[] : * returned eigenvectors * * Syntax: call VNEIGL(order,a,eigval,eigvec) * * Other Information: I think this takes a lower triangle A, * destructively diagonalises it * * into A with eigenvectors in R. Eigenvalues are left down diagonal of * A. The method is (I think) Householder and others. N is order of * matrix The eigenvectors, although declared in routine as * 1-dimensional, can be taken as eigvec(N,N) * * ******************************************************************* * * Local variables: */ double cosx, sinx, xxyy, cosx2, sinx2 = 0.0; int i, j, k, l, m = 0; double x, y, range, anorm, sincs, anrmx = 0.0; int ia, ij, /* il, *//* im, */ll, lm, iq, mm, jq, lq, mq, ind, ilq, imq, ilr, imr = 0; double thr = 0.0; /* -------------------------------------------------------------------- */ /* Conversion from F77 */ // int /*ivar1, ivar2,*/ ivar3 = 0; // double rvar1 = 0.0; cosx = 0.0; sinx = 0.0; xxyy = 0.0; cosx2 = 0.0; sinx2 = 0.0; i = 0; j = 0; k = 0; l = 0; m = 0; x = 0.0; y = 0.0; range = 0.0; anorm = 0.0; sincs = 0.0; anrmx = 0.0; ia = 0; ij = 0; // il = 0; // im = 0; ll = 0; lm = 0; iq = 0; mm = 0; jq = 0; lq = 0; mq = 0; ind = 0; ilq = 0; imq = 0; ilr = 0; imr = 0; thr = 0.0; // ivar3 = 0; // rvar1 = 0.0; /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ RealSquareMatrix.LOG.info("O..." + order); /* Function Body */ if (order < 2) { throw new EuclidRuntimeException("order too small"); } /* generate identity matrix */ range = 1e-6f; iq = -(order); /* ivar1 = order; */ for (j = 1; j <= /* ivar1 */order; ++j) { iq += order; /* ivar2 = order; */ for (i = 1; i <= /* ivar2 */order; ++i) { ij = iq + i; eigvec[ij] = ZERO; if (i - j == 0) { eigvec[ij] = ONE; } } } RealSquareMatrix.LOG.info("O " + order); /*----------------------------------------------------------------------- */ /* compute initial and final norms (anorm and anormx) */ anorm = ZERO; /* ivar2 = order; */ for (i = 1; i <= /* ivar2 */order; ++i) { /* ivar1 = order; */ for (j = i; j <= /* ivar1 */order; ++j) { if (i - j != 0) { ia = i + (j * j - j) / 2; anorm += a[ia] * a[ia]; } } } if (anorm > ZERO) { anorm = Math.sqrt(anorm) * SQRT2; anrmx = anorm * range / ((double)order); /* initialize indicators and compute threshold, thr */ ind = 0; thr = anorm; // L80: while (true) { thr /= ((double)order); // L90: while (true) { l = 1; while (true) { // L100: m = l + 1; /* compute sin and cos */ // L110: while (true) { mq = (m * m - m) / 2; lq = (l * l - l) / 2; lm = l + mq; if (Math.abs(a[lm]) - thr >= ZERO) { // if (Math.abs(a[lm]) - thr > ZERO) { ind = 1; ll = l + lq; mm = m + mq; x = (a[ll] - a[mm]) * .5f; y = -a[lm] / Math.sqrt(a[lm] * a[lm] + x * x); if (x < ZERO) { y = -y; } /* check to avoid rounding errors */ // L140: xxyy = ONE - y * y; if (xxyy < ZERO) { xxyy = ZERO; } sinx = y / Math.sqrt((Math.sqrt(xxyy) + ONE) * TWO); sinx2 = sinx * sinx; cosx = Math.sqrt(ONE - sinx2); cosx2 = cosx * cosx; sincs = sinx * cosx; /* rotate l and m columns */ ilq = order * (l - 1); imq = order * (m - 1); Diagonalise.subroutine(order, a, ilq, imq, l, m, lq, mq, ll, lm, mm, sinx, cosx, eigval, eigvec, sincs, sinx2, cosx2); } // L250 if (m == order) { break; } // goto L110; // L260 ++m; } /* test for l = second from last column */ // L270 if (l == (order - 1)) { RealSquareMatrix.LOG.info(S_LSQUARE + l + EC.S_SLASH + order + EC.S_RSQUARE); break; } // goto L100; // L280 ++l; } // L290 RealSquareMatrix.LOG.info(S_PLUS + l + EC.S_SLASH + ind); if (ind != 1) { break; } // L300 ind = 0; // goto L90; } RealSquareMatrix.LOG.info("====================broke"); /* compare threshold with final norm */ // L310: if (thr - anrmx <= ZERO) { break; // goto L320; } // goto L80; } } /* sort eigenvalues and eigenvectors */ // L320: iq = -(order); /* ivar1 = order; */ for (i = 1; i <= /* ivar1 */order; ++i) { iq += order; ll = i + (i * i - i) / 2; jq = order * (i - 2); /* ivar2 = order; */ for (j = i; j <= /* ivar2 */order; ++j) { jq += order; mm = j + (j * j - j) / 2; if (a[ll] - a[mm] >= ZERO) { continue; } x = a[ll]; a[ll] = a[mm]; a[mm] = x; /* ivar3 = order; */ for (k = 1; k <= /* ivar3 */order; ++k) { ilr = iq + k; imr = jq + k; x = eigvec[ilr]; eigvec[ilr] = eigvec[imr]; eigvec[imr] = x; } } } return Diagonalise.eigtest(order, a, eigval, range); } private static int eigtest(int order, double[] a, double[] eigval, double range) { /* get eigenvalues and test for singularity */ int rank = 0; for (int i = order; i >= 1; --i) { eigval[i] = a[(i * i + i) / 2]; if (eigval[i] < ZERO) { // illCond = new NPDMatrixException(); break; } else if (eigval[i] < range) { RealSquareMatrix.LOG.info("SING"); // illCond = new SingMatrixException(); break; } else { ++rank; } } return rank; } static void subroutine(int order, double[] a, int ilq, int imq, int l, int m, int lq, int mq, int ll, int lm, int mm, double sinx, double cosx, double[] eigval, double[] eigvec, double sincs, double sinx2, double cosx2) { double x, y; /* ivar1 = order; */ for (int i = 1; i <= /* ivar1 */order; ++i) { int iq = (i * i - i) / 2; if (i != l) { // L150: int im, il; int ivar2 = i - m; if (ivar2 != 0) { if (ivar2 < 0) { // L160: im = i + mq; } else { // L170: im = m + iq; } // L180: if (i >= l) { // L200: il = l + iq; } else { // L190: il = i + lq; } // L210: x = a[il] * cosx - a[im] * sinx; a[im] = a[il] * sinx + a[im] * cosx; a[il] = x; } // L220: int ilr = ilq + i; int imr = imq + i; x = eigvec[ilr] * cosx - eigvec[imr] * sinx; eigvec[imr] = eigvec[ilr] * sinx + eigvec[imr] * cosx; eigvec[ilr] = x; } x = a[lm] * 2.f * sincs; y = a[ll] * cosx2 + a[mm] * sinx2 - x; x = a[ll] * sinx2 + a[mm] * cosx2 + x; a[lm] = (a[ll] - a[mm]) * sincs + a[lm] * (cosx2 - sinx2); a[ll] = y; a[mm] = x; /* * tests for completion test for m = last column */ } } } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/StringComparator.java000077500000000000000000000015121461721410700262260ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid; import java.util.Comparator; public class StringComparator implements Comparator { public int compare(String obj1, String obj2) { return obj1.compareTo(obj2); } } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/Transform2.java000066400000000000000000000554271461721410700250000ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid; import java.awt.geom.AffineTransform; import org.apache.log4j.Logger; /** * 2-D transformation matrix class * * Transform2 represents a transformation matrix for 2-D objects. Its actual * form may be implementation-dependent, but we have started with 3x3 matrices. * The following transformations will be supported as the class is developed: * * * * TRFORM2_NULL no transformation allowed
* ROT_ORIG rotation about the origin
* ROT_TRANS rotation and translation
* ROT_TRANS_SCALE rotation, translation and single scale factor
* ROT_TRANS_AXIAL_SCALE rotation, translation + 3 axial scale factors
* ROT_TRANS_SCALE_PERSP rotation, translation, scale, perspective
* TRFORM3_ANY any matrix at all - user beware! *

* * The basic stuff is all there - the user will do best to look at examples. * * * NOTE; this has been hacked in a hurry from Transform3. Many of the internal * routines are grossly overcomplicated. I'll clean it up some time. * * @author (C) P. Murray-Rust, 1996 * @author PMR 20 August 2003 */ public class Transform2 extends RealSquareMatrix { final static Logger LOG = Logger.getLogger(Transform2.class.getName()); /** type */ public enum Type { /** */ NULL(1, "none"), /** */ ROT_ORIG(2, "rotation about origin"), /** */ ROT_TRANS(3, "rotation translation"), /** */ ROT_TRANS_SCALE(4, "rotation translation scale"), /** */ ROT_TRANS_AXIAL_SCALE(5, "rotation translation axial scale"), /** */ ROT_TRANS_SCALE_PERSP(6, "perspective"), /** */ ANY(7, "any"); /** integer value */ public int i; /** String value */ public String s; private Type(int i, String s) { this.i = i; this.s = s; } }; /** * Transform2 inherits all public or protected members of RealSquareMatrix * and its ancestors */ Type trnsfrm; /** * default is a unit matrix */ public Transform2() { super(3); for (int i = 0; i < 3; i++) { flmat[i][i] = 1.0; } trnsfrm = Type.ANY; } /** * This gives a default unit matrix of type t (default ROT_ORIG) * * @param t * Description of the Parameter */ public Transform2(Type t) { this(); trnsfrm = t; } /** identity matrix with translation component * * @param v amount to translate by */ public Transform2(Vector2 v) { this(); trnsfrm = Type.ROT_TRANS; flmat[0][2] = v.x; flmat[1][2] = v.y; } public Transform2(AffineTransform at) { this(); double[] dd = new double[6]; at.getMatrix(dd); flmat[0][0] = dd[0]; flmat[0][1] = dd[2]; flmat[0][2] = dd[4]; flmat[1][0] = dd[1]; flmat[1][1] = dd[3]; flmat[1][2] = dd[5]; } /** clockwise rotation about z- axis * * @param zrot radians clockwise rotation */ public Transform2(Angle zrot) { this(); double cosx = zrot.cos(); double sinx = zrot.sin(); this.flmat[0][0] = cosx; this.flmat[0][1] = sinx; this.flmat[1][0] = -sinx; this.flmat[1][1] = cosx; this.flmat[2][2] = 1.0; this.trnsfrm = Type.ROT_ORIG; } /** * from rotation about a point, given a matrix NOT CHECKED * * @param t * Description of the Parameter * @param p * Description of the Parameter */ public Transform2(Transform2 t, Real2 p) { super(3); // translation = -M*r + r flmat[0][2] = -t.flmat[0][0] * p.x - t.flmat[0][1] * p.y + p.x; flmat[1][2] = -t.flmat[1][0] * p.x - t.flmat[1][1] * p.y + p.y; this.flmat[0][0] = t.flmat[0][0]; this.flmat[0][1] = t.flmat[0][1]; this.flmat[1][0] = t.flmat[1][0]; this.flmat[1][1] = t.flmat[1][1]; trnsfrm = Type.ROT_TRANS; } /** * rotation of one vector onto another * * @param v1 * Description of the Parameter * @param v2 * Description of the Parameter * @exception EuclidRuntimeException * v1 or v2 is zero length */ public Transform2(Vector2 v1, Vector2 v2) throws EuclidRuntimeException { super(3); Angle a = v1.getAngleMadeWith(v2); Transform2 temp = new Transform2(a); this.flmat = temp.flmat; this.trnsfrm = temp.trnsfrm; } /** * from 2 vector components - NOT checked for validity * * @param v1 * Description of the Parameter * @param v2 * Description of the Parameter */ public Transform2(Real2 v1, Real2 v2) { super(3); for (int i = 0; i < 2; i++) { flmat[0][0] = v1.x; flmat[1][0] = v2.x; flmat[0][1] = v1.y; flmat[1][1] = v2.y; flmat[2][i] = 0.0; flmat[i][2] = 0.0; } flmat[2][2] = 1.0; trnsfrm = Type.ROT_ORIG; } /** * construct from an array. Formed by feeding in an existing array. to a * cols*cols matrix array is of type m00, m01, m02, m10 ... * * @param array * to create the Transform from * @exception EuclidRuntimeException * array must have 9 elements */ public Transform2(double[] array) throws EuclidRuntimeException { super(3, array); trnsfrm = calculateMatrixType(); } /** * copy constructors - assumed to be an OK matrix. * * @param m * the transform to copy */ public Transform2(Transform2 m) { super(m); trnsfrm = m.trnsfrm; } /** * from a 2x2 or 3x3 matrix * * @param m * Description of the Parameter * @exception EuclidRuntimeException * m must be 2*2 or 3*3 */ public Transform2(RealSquareMatrix m) throws EuclidRuntimeException { this(); // 2x2 matrix. convert to 3x3 if (m.getCols() == 2) { // convert to 3x3 for (int i = 0; i < 2; i++) { for (int j = 0; j < 2; j++) { flmat[i][j] = m.flmat[i][j]; } } } else if (m.getCols() != 3) { throw new EuclidRuntimeException("bad size for transform "+m.getCols()); } else { this.flmat = m.flmat; } this.trnsfrm = calculateMatrixType(); } /** * from a 2x2 rotation matrix and a translation vector * * @param m * Description of the Parameter * @param v * Description of the Parameter * @exception EuclidRuntimeException * m must be 2*2 */ public Transform2(RealSquareMatrix m, Vector2 v) throws EuclidRuntimeException { this(m); // 2x2 matrix. convert to 3x3 if (m.getCols() == 2) { // convert to 3x3 flmat[0][2] = v.x; flmat[1][2] = v.y; } else { throw new EuclidRuntimeException("must have 2*2 rotation matrix"); } } /** * clone * * @param m * Description of the Parameter * @return Description of the Return Value */ public Transform2 clone(Transform2 m) { // delete existing matrix in this Transform2 temp = new Transform2((RealSquareMatrix) m); temp.trnsfrm = m.trnsfrm; return temp; } public static Transform2 getTranslationTransform(Real2 translation) { Transform2 transform = null; if (translation != null) { transform = new Transform2(new Vector2(translation)); } return transform; } /** * Carries out graphics transform * * transforms between rectangular coordinates. * Example: *

     * Real2 inputDim = new Real2(2.7, 20000);
     * Real2 outputDim = new Real2(-300, 300);
     * 
* *@param in Description of the Parameter *@param out Description of the Parameter *@param keepAspectRatio Description of the Parameter *@throws ArithmeticException */ public Transform2(Window2 in, Window2 out, boolean keepAspectRatio) throws ArithmeticException { this(in.origin, in.dim, out.origin, out.dim, keepAspectRatio); } /** * graphics transform (transforms between rectangular coordinates * ("windows") originIn maps onto originOut and dimensionIn (width, height) * onto dimensionOut. If keepAspectRatio id true, scales will be isotropic. * Note that ranges can be inverted by using negative coordinates in * dimensions. Example:
     *Real2 inputDim = new Real2(2.7, 20000);
     *Real2 outputDim = new Real2(-300, 300);
     *
* *@param originIn Description of the Parameter *@param dimensionIn Description of the Parameter *@param originOut Description of the Parameter *@param dimensionOut Description of the Parameter *@param keepAspectRatio Description of the Parameter *@throws ArithmeticException */ public Transform2(Real2 originIn, Real2 dimensionIn, Real2 originOut, Real2 dimensionOut, boolean keepAspectRatio) throws ArithmeticException { this(); double scaleX; double scaleY; scaleX = dimensionOut.getX() / dimensionIn.getX(); scaleY = dimensionOut.getY() / dimensionIn.getY(); if (keepAspectRatio) { if (Math.abs(scaleX) < Math.abs(scaleY)) { scaleY = scaleX * (scaleY / Math.abs(scaleY)); } if (Math.abs(scaleY) < Math.abs(scaleX)) { scaleX = scaleY * (scaleX / Math.abs(scaleX)); } } flmat[0][0] = scaleX; flmat[1][1] = scaleY; flmat[0][2] = originOut.getX() - scaleX * originIn.getX(); flmat[1][2] = originOut.getY() - scaleY * originIn.getY(); } /** * seem to require this one * * @param m * Description of the Parameter * @return Description of the Return Value */ Transform2 clone(RealSquareMatrix m) { Transform2 temp = new Transform2(m); temp.trnsfrm = calculateMatrixType(); return temp; } /** * Description of the Method * * @param m * Description of the Parameter * @return Description of the Return Value */ public boolean isEqualTo(Transform2 m) { return super.isEqualTo((RealSquareMatrix) m) && trnsfrm == m.trnsfrm; } /** rotate about a point. * * @param angle * @param point * @return tramsformation */ public static Transform2 getRotationAboutPoint(Angle angle, Real2 point) { Transform2 t3 = new Transform2(new Vector2(point)); Transform2 t2 = new Transform2(angle); Transform2 t1 = new Transform2(new Vector2(point.multiplyBy(-1.0))); return t3.concatenate(t2).concatenate(t1); } /** * apply scales to each axis * @param scaleX * @param scaleY * @return transform * @deprecated use createScaleTransform */ public static Transform2 applyScales(double scaleX, double scaleY) { return new Transform2( new double[] { scaleX, 0.0, 0.0, 0.0, scaleY, 0.0, 0.0, 0.0, 1.0 }); } /** * create diagonal scale matrix. * * @param scaleX * @param scaleY * @return transform */ public static Transform2 createScaleTransform(double scaleX, double scaleY) { return new Transform2( new double[] { scaleX, 0.0, 0.0, 0.0, scaleY, 0.0, 0.0, 0.0, 1.0 }); } /** * apply scale to each axis * @param scale * @return transform */ public static Transform2 applyScale(double scale) { return Transform2.applyScales(scale, scale); } public void applyScalesToThis(double scaleX, double scaleY) { Transform2 t2 = Transform2.applyScales(scaleX, scaleY); t2 = this.concatenate(t2); this.flmat = t2.flmat; } /** * concatenate * * @param m2 postmultiplies this * @return Description of the Return Value */ public Transform2 concatenate(Transform2 m2) { RealSquareMatrix temp = new RealSquareMatrix(((RealSquareMatrix) this) .multiply((RealSquareMatrix) m2)); // maximum value is matrix of greatest generality (horrible) Transform2 temp1 = new Transform2(temp); temp1.trnsfrm = (trnsfrm.i > m2.trnsfrm.i) ? trnsfrm : m2.trnsfrm; return temp1; } /** * set transformation type may attempt to orthonomalise if type includes a * rotation * * @param option * The new transformationType value * @return Description of the Return Value */ public int setTransformationType(Type option) { RealSquareMatrix s3 = new RealSquareMatrix(); if (option == Type.ROT_ORIG) { s3 = new RealSquareMatrix(this.extractSubMatrixData(0, 1, 0, 1)); s3.orthonormalize(); this.replaceSubMatrixData(0, 0, s3); } else if (option == Type.ROT_TRANS) { s3 = new RealSquareMatrix(this.extractSubMatrixData(0, 1, 0, 1)); s3.orthonormalize(); this.replaceSubMatrixData(0, 0, s3); } else if (option == Type.ROT_TRANS_SCALE) { s3 = new RealSquareMatrix(this.extractSubMatrixData(0, 1, 0, 1)); double[] scale = s3.euclideanColumnLengths().getArray(); double scale3 = Math.exp(Math.log(scale[0] * scale[1]) / 2.0); s3.orthonormalize(); RealArray sc1 = new RealArray(3, scale3); RealSquareMatrix s = RealSquareMatrix.diagonal(sc1); s3 = s.multiply(s3); replaceSubMatrixData(0, 0, s3); } else if (option == Type.ROT_TRANS_SCALE_PERSP) { } else if (option == Type.ROT_TRANS_AXIAL_SCALE) { s3 = new RealSquareMatrix(this.extractSubMatrixData(0, 1, 0, 1)); RealArray scale = s3.euclideanColumnLengths(); s3.orthonormalize(); RealSquareMatrix s = RealSquareMatrix.diagonal(scale); s3 = s.multiply(s3); replaceSubMatrixData(0, 0, s3); } else if (option == Type.ANY) { } else if (option == Type.NULL) { } else { return 1; } trnsfrm = option; return 0; } /** * get transformation type * * @return The transformationType value */ public Type getTransformationType() { return trnsfrm; } /** * get new matrix type * * @return Description of the Return Value */ public Type calculateMatrixType() { RealSquareMatrix s3 = new RealSquareMatrix(extractSubMatrixData(0, 1, 0, 1)); RealArray c3 = extractColumnData(2); if (c3 != null) { if (Real.isZero(c3.elementAt(0), Real.getEpsilon()) && Real.isZero(c3.elementAt(1), Real.getEpsilon())) { return Type.NULL; } } { if (s3.isUnit()) { return Type.NULL; } // unit matrix if (s3.isUnitary()) { return Type.ROT_ORIG; } // unitary matrix } if (s3.isUnitary()) { return Type.ROT_TRANS; } // rot + trans; no scale if (s3.isOrthogonal()) { double[] scale = s3.euclideanColumnLengths().getArray(); if (Real.isEqual(scale[0], scale[1])) { return Type.ROT_TRANS_SCALE; } return Type.ROT_TRANS_AXIAL_SCALE; } return Type.ANY; } /** * interpret current matrix as rotation about axis NOT YET CHECKED; * assume combined rotation and Xskew * and isotropic scale * (cos -sin) * (1 d) // d is tan(skew angle) * (sin cos) (0 1) * * gives * (cos d.cos-sin) * (sin d.sin+cos) * * * @return The angleOfRotation value */ public Angle getAngleOfRotation() { Angle ang = null; // antisymmetric, including unit if (Real.isEqual(flmat[0][1] + flmat[1][0], 0.0, EPS)) { // this is the ony generic rotation that can be extracted ang = new Angle(Math.atan2(flmat[0][1], flmat[0][0])); } else if (Real.isEqual(flmat[0][0], flmat[1][1], EPS)){ if (Real.isZero(flmat[1][0], EPS)) { ang = new Angle(0); } else if (Real.isZero(flmat[0][0], EPS)) { ang = new Angle(0); } } else { LOG.trace("TRANSFORM "+this.toString()); ang = null; } return ang; } /** * interpret current matrix as rotation about axis NOT YET CHECKED; * assume combined rotation and Xskew * and isotropic scale * (cos -sin) * (1 tand) // d is tan(skew angle) * (sin cos) (0 1) * * gives * (cos tand.cos-sin) * (sin tand.sin+cos) * * * @return The angleOfRotation value */ public Angle getAngleOfRotationNew() { Angle ang = new Angle(Math.atan2(flmat[0][1], flmat[0][0])); return ang; } /** * interpret current matrix as rotation about axis NOT YET CHECKED; * assume combined rotation and Xskew * and isotropic scale * (cos -sin) * (1 tand) // d is tan(skew angle) * (sin cos) (0 1) * * gives * (cos tand.cos-sin) * (sin tand.sin+cos) * * * @return The angleOfRotation value */ public Angle getAngleOfSkew(double eps) { double tandcos = flmat[0][1] + flmat[1][0]; double tandsin = flmat[1][1] - flmat[0][0]; Angle d = new Angle(0); if (!Real.isZero(tandcos, eps) && !Real.isZero(tandsin, eps)) { d = new Angle(Math.atan2(tandsin, tandcos)); } return d; } /** * get Transformation to mirror ('flip') across an axis NOT YET CHECKED * * @param r * Description of the Parameter * @return Description of the Return Value * @exception EuclidRuntimeException */ public static Transform2 flipAboutVector(Real2 r) throws EuclidRuntimeException { r = r.getUnitVector(); double cost = r.x; double sint = r.y; Transform2 temp = new Transform2(); temp.flmat[0][0] = cost * cost - sint * sint; temp.flmat[1][1] = -temp.flmat[0][0]; temp.flmat[0][1] = temp.flmat[1][0] = 2 * cost * sint; return temp; } /** * get translation component only * * @return The translation value */ public Real2 getTranslation() { return new Real2(flmat[0][2], flmat[1][2]); } /** * get translation component only * * @param xy */ public void setTranslation(Real2 xy) { flmat[0][2] = xy.x; flmat[1][2] = xy.y; } /** * get scales (as a 2-element RealArray) * * @return The scales value */ public RealArray getScales() { RealArray scales; RealSquareMatrix s3 = new RealSquareMatrix(extractSubMatrixData(0, 1, 0, 1)); scales = s3.euclideanColumnLengths(); return scales; } /** * get Unitary matrix (that is eliminate scales and translation) * * @return The rotationMatrix value */ public RealSquareMatrix getRotationMatrix() { RealSquareMatrix s; RealSquareMatrix s3 = new RealSquareMatrix(extractSubMatrixData(0, 1, 0, 1)); s3.normaliseByColumns(); s = s3; return s; } /** remove translations * does to alter this * @return */ public Transform2 removeTranslations() { double[] arr = this.getMatrixAsArray(); arr[2] = 0.0; arr[5] = 0.0; return new Transform2(arr); } /** * transform describing the rotation and stretching of a line. * used in bondTool.getTranformToRotateAndStretchBond(movingAtom, targetPoint); * Transform2 t = this.getTranformToRotateAndStretchLine(movingAtom, targetPoint) { * @param pivotPoint * @param movingPoint * @param targetPoint point to translate movingAtom to * @return */ public static Transform2 getTransformToRotateAndStretchLine( Real2 pivotPoint, Real2 movingPoint, Real2 targetPoint) { Vector2 pivotVector = new Vector2(movingPoint.subtract(pivotPoint)); Vector2 targetVector = new Vector2(targetPoint.subtract(pivotPoint)); Angle angle = pivotVector.getAngleMadeWith(targetVector); Transform2 rotate = new Transform2(angle); Transform2 rotateAboutOtherPoint = new Transform2(rotate, pivotPoint); Vector2 deltaVector = new Vector2(targetVector.subtract(pivotVector)); Vector2 stretchVector = deltaVector.projectOnto(pivotVector); Transform2 stretch = new Transform2(stretchVector); Transform2 finalTransform = rotateAboutOtherPoint.concatenate(stretch); return finalTransform; } /** * Description of the Method * * @return Description of the Return Value */ public String toString() { String s = EC.S_LBRAK; for (int i = 0; i < this.flmat.length; i++) { for (int j = 0; j < this.flmat[i].length; j++) { s += flmat[i][j]; s += EC.S_COMMA; } if (i < flmat.length - 1) { s += EC.S_NEWLINE; } } s += EC.S_RBRAK; return s; } /** at.getMatrix(dd); flmat[0][0] = dd[0]; flmat[0][1] = dd[2]; flmat[0][2] = dd[4]; flmat[1][0] = dd[1]; flmat[1][1] = dd[3]; flmat[1][2] = dd[5]; */ public AffineTransform getAffineTransform() { AffineTransform affineTransform = null; double[] dd = new double[6]; if (flmat != null) { dd[0] = flmat[0][0]; dd[2] = flmat[0][1]; dd[4] = flmat[0][2]; dd[1] = flmat[1][0]; dd[3] = flmat[1][1]; dd[5] = flmat[1][2]; affineTransform = new AffineTransform(dd[0],dd[1],dd[2],dd[3],dd[4],dd[5]); } return affineTransform; } } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/Transform3.java000066400000000000000000000647151461721410700250010ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid; import java.io.PrintStream; import java.util.StringTokenizer; import org.apache.log4j.Logger; import org.xmlcml.euclid.Axis.Axis3; /** * 3-D transformation matrix class Transform3 represents a transformation matrix * for 3-D objects. Its actual form may be implementation-dependent, but we have * started with 4x4 matrices. The following transformations will be supported as * the class is developed: *

*
* TRFORM3_NULL no transformation allowed
* ROT_ORIG rotation about the origin
* ROT_TRANS rotation and translation
* ROT_TRANS_SCALE rotation, translation and single scale factor
* ROT_TRANS_AXIAL_SCALE rotation, translation + 3 axial scale factors
* ROT_TRANS_SCALE_PERSP rotation, translation, scale, perspective
* TRFORM3_ANY any matrix at all - user beware! *

* The basic stuff is all there - the user will do best to look at examples. * * @author (C) P. Murray-Rust, 1996 */ public class Transform3 extends RealSquareMatrix { private static final PrintStream SYSOUT = System.out; final static Logger LOG = Logger.getLogger(Transform3.class); /** type */ public enum Type { /** */ NULL(1, "none"), /** */ ROT_ORIG(2, "rotation about origin"), /** */ ROT_TRANS(3, "rotation translation"), /** */ ROT_TRANS_SCALE(4, "rotation translation scale"), /** */ ROT_TRANS_AXIAL_SCALE(5, "rotation translation axial scale"), /** */ ROT_TRANS_SCALE_PERSP(6, "perspective"), /** */ ANY(7, "any"); /** integer value */ public int i; /** String value */ public String s; private Type(int i, String s) { this.i = i; this.s = s; } }; /** * Transform3 inherits all public/protected members of RealSquareMatrix and * its ancestors */ /** * type of transformation ( see above) */ Type trnsfrm = Type.ANY; /** * construct unit matrix T = I */ public Transform3() { super(4); createIdentityTransform(); } private void createIdentityTransform() { for (int i = 0; i < 4; i++) { flmat[i][i] = 1.0; } } /** * This gives a default unit matrix of type t. * * @param t type */ public Transform3(Type t) { this(); trnsfrm = t; } /** * identity matrix with translation component. T = I|vector * * @param v translation vector */ public Transform3(Vector3 v) { this(); trnsfrm = Type.ROT_TRANS; for (int i = 0; i < 3; i++) { flmat[i][3] = v.flarray[i]; } } /** * from rotation about an axis. (use (Choice3.X), etc * * @param axis Choice.X, etc * @param rot angle to rotate by */ public Transform3(Axis3 axis, Angle rot) { this(); trnsfrm = Type.ROT_ORIG; // unit matrix RealSquareMatrix t1 = new RealSquareMatrix(4); double cosx = rot.cos(); double sinx = rot.sin(); // X-axis if (axis == Axis3.X) { t1.flmat[0][0] = 1.0; t1.flmat[1][1] = cosx; t1.flmat[1][2] = sinx; t1.flmat[2][1] = -sinx; t1.flmat[2][2] = cosx; t1.flmat[3][3] = 1.0; } // Y-axis else if (axis == Axis3.Y) { t1.flmat[0][0] = cosx; t1.flmat[0][2] = -sinx; t1.flmat[1][1] = 1.0; t1.flmat[2][0] = sinx; t1.flmat[2][2] = cosx; t1.flmat[3][3] = 1.0; } // Z-axis else if (axis == Axis3.Z) { t1.flmat[0][0] = cosx; t1.flmat[0][1] = sinx; t1.flmat[1][0] = -sinx; t1.flmat[1][1] = cosx; t1.flmat[2][2] = 1.0; t1.flmat[3][3] = 1.0; } this.flmat = t1.flmat; this.trnsfrm = Type.ROT_ORIG; } /** * from rotation about the three orthogonal axes. rotX then rotY then rotZ * * @param xrot * @param yrot * @param zrot */ public Transform3(Angle xrot, Angle yrot, Angle zrot) { super(4); double cosx = 0.0; double sinx = 0.0; trnsfrm = Type.ROT_ORIG; // X-axis int mat = 0; RealSquareMatrix t1 = new RealSquareMatrix(4); //must have non=zero matrix even for zero angle cosx = xrot.cos(); sinx = xrot.sin(); t1.flmat[0][0] = 1.0; t1.flmat[1][1] = cosx; t1.flmat[1][2] = sinx; t1.flmat[2][1] = -sinx; t1.flmat[2][2] = cosx; t1.flmat[3][3] = 1.0; mat = 1; // Y-axis if (!yrot.isEqualTo(0.0)) { cosx = yrot.cos(); sinx = yrot.sin(); // unit matrix RealSquareMatrix t2 = new RealSquareMatrix(4); t2.flmat[0][0] = cosx; t2.flmat[1][1] = 1.0; t2.flmat[0][2] = -sinx; t2.flmat[2][0] = sinx; t2.flmat[2][2] = cosx; t2.flmat[3][3] = 1.0; if (mat == 1) { t1 = t2.multiply(t1); } else { t1 = t2; } mat = 1; } // Z-axis if (!zrot.isEqualTo(0.0)) { cosx = zrot.cos(); sinx = zrot.sin(); // unit matrix RealSquareMatrix t2 = new RealSquareMatrix(4); t2.flmat[0][0] = cosx; t2.flmat[0][1] = sinx; t2.flmat[1][0] = -sinx; t2.flmat[1][1] = cosx; t2.flmat[2][2] = 1.0; t2.flmat[3][3] = 1.0; if (mat == 1) { t1 = t2.multiply(t1); } else { t1 = t2; } } this.flmat = t1.flmat; this.trnsfrm = Type.ROT_ORIG; } /** * from rotation about a point. * * @param t * rotation matrix * @param p * point to rotate about */ public Transform3(Transform3 t, Point3 p) { super(4); double f1[] = { 0.0, 0.0, 0.0, 1.0 }; trnsfrm = Type.ROT_TRANS; Vector3 tvect = new Vector3(p); // translation only matrices Transform3 trans1 = new Transform3(tvect.negative()); Transform3 trans2 = new Transform3(tvect); // remove existing translation RealArray f0 = new RealArray(f1); Transform3 temp = t; temp.replaceColumnData(3, f0); // concatenate Transform3 temp3 = trans2.concatenate(temp.concatenate(trans1)); this.flmat = temp3.flmat; this.trnsfrm = temp3.trnsfrm; } /** * from rotation about a vector. * * @param v * vector to rotate about * @param a * angle to rotate by */ public Transform3(Vector3 v, Angle a) { super(4); trnsfrm = Type.ROT_ORIG; Vector3 v2 = v; v2.normalize(); double cosa = a.cos(); // make a diagonal with identical elements (cos(a)) RealArray tempf = new RealArray(3, cosa); RealSquareMatrix m3 = RealSquareMatrix.diagonal(tempf); // outer product of axial components RealArray temp1 = new RealArray(v2.getArray()); RealSquareMatrix m2 = RealSquareMatrix.outerProduct(temp1); m2.multiplyBy(1.0 - cosa); m2 = m2.plus(m3); // final matrix is (0, -v3, v2; v3, 0, -v1; -v2, v1, 0) * sin(a) // I expect the coding could be more elegant! double sina = a.sin(); m3.clearMatrix(); double f = sina * v2.flarray[2]; m3.flmat[0][1] = -f; m3.flmat[1][0] = f; f = sina * v2.flarray[1]; m3.flmat[0][2] = f; m3.flmat[2][0] = -f; f = sina * v2.flarray[0]; m3.flmat[1][2] = -f; m3.flmat[2][1] = f; m2 = m2.plus(m3); // transfer to main matrix for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { flmat[i][j] = m2.flmat[i][j]; } } flmat[3][3] = 1.0; } /** * Rotation about a line. chooses any point on line as centre of rotation * * @param l * line to rotate about * @param a * angle to rotate by */ public Transform3(Line3 l, Angle a) { super(4); trnsfrm = Type.ROT_TRANS; Vector3 v = l.getVector(); Point3 p = l.getPoint(); Transform3 temp = new Transform3(v, a); Vector3 orig = new Vector3(p); Transform3 trans1 = new Transform3(orig.negative()); // translate to // origin orig = new Vector3(p); Transform3 trans2 = new Transform3(orig); // translate back Transform3 temp2 = new Transform3(); temp2 = new Transform3(trans2.concatenate(temp.concatenate(trans1))); // concatenate // copy back to present one this.flmat = temp2.flmat; } /** * rotation of one vector onto another. this documentation has not been * checked * * @param v1 vector to rotate * @param v2 vector to rotate v1 onto */ public Transform3(Vector3 v1, Vector3 v2) { super(4); Vector3 v12 = v1.cross(v2); // if parallel return unit matrix /* * 06/03/08 * I've changed the code so there is a difference between parallel and * anti-parallel vectors. This is important for the polymer builder, but * may break other applications. Contact me if this is a problem - dmj30@cam */ if (v12.isZero()) { double unit = 1.0; if(v1.dot(v2) < 0) unit = -1.0; for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { flmat[i][j] = 0.0; } flmat[i][i] = unit; } } else { Angle a = v1.getAngleMadeWith(v2); Transform3 temp = new Transform3(v12, a); this.flmat = temp.flmat; this.trnsfrm = temp.trnsfrm; } } /** * from 3 vector components. NOT checked fills rows of T with v1, v2, v3 * * @param v1 * first row of T * @param v2 * second row of T * @param v3 * third row of T */ public Transform3(Vector3 v1, Vector3 v2, Vector3 v3) { super(4); for (int i = 0; i < 3; i++) { flmat[0][i] = v1.flarray[i]; flmat[1][i] = v2.flarray[i]; flmat[2][i] = v3.flarray[i]; flmat[3][i] = 0.0; flmat[i][3] = 0.0; } flmat[3][3] = 1.0; trnsfrm = Type.ROT_ORIG; } /** * apply scales to each axis * @param scaleX * @param scaleY * @param scaleZ * @return transform */ public static Transform3 applyScales(double scaleX, double scaleY, double scaleZ) { return new Transform3( new double[] { scaleX, 0.0, 0.0, 0.0, 0.0, scaleY, 0.0, 0.0, 0.0, 0.0, scaleZ, 0.0, 0.0, 0.0, 0.0, 1.0 }); } /** * apply scale to each axis * @param scale * @return transform */ public static Transform3 applyScale(double scale) { return Transform3.applyScales(scale, scale, scale); } /** * From array. * * @param array * copied to m00, m01, m02, m03, m10 ... * @exception EuclidRuntimeException * array must have 16 elements */ public Transform3(double[] array) throws EuclidRuntimeException { super(4, array); trnsfrm = checkMatrix(); } /** * copy constructor. * * @param m * transform to copy */ public Transform3(Transform3 m) { super(m); trnsfrm = m.trnsfrm; } /** * from a matrix. * * @param m * 3x3 or 4x4 matrix * @exception EuclidRuntimeException * m must be 3*3 or 4*4 */ public Transform3(RealSquareMatrix m) throws EuclidRuntimeException { this(); // 3x3 matrix. convert to 4x4 if (m.getCols() == 3) { // convert to 4x4 for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { flmat[i][j] = m.flmat[i][j]; } } } else if (m.getCols() != 4) { throw new EuclidRuntimeException("must have 3 or 4 cols"); } else { this.flmat = m.flmat; } this.trnsfrm = checkMatrix(); } /** * from a matrix and vector. * * @param m * 3x3 rotation matrix * @param v * translation vector * @exception EuclidRuntimeException * m must be 3*3 */ public Transform3(RealSquareMatrix m, Vector3 v) throws EuclidRuntimeException { this(m); // 3x3 matrix. convert to 4x4 if (m.getCols() == 3) { // convert to 4x4 for (int i = 0; i < 3; i++) { flmat[i][3] = v.flarray[i]; } } else { throw new EuclidRuntimeException("must have 3 columns"); } } /** * from a crystallographic operator. * * @param opString * for example 1/2-x,1/2+y,-z * extended to 0.5+x, y-0.25, z // etc * @throws EuclidRuntimeException * corrupt/invalid string */ public Transform3(String opString) throws EuclidRuntimeException { super(4); String[] ss = opString.split("\\s*,\\s*"); // StringTokenizer st = new StringTokenizer(opString, EuclidConstants.S_COMMA); if (ss.length != 3) { throw new EuclidRuntimeException("Must have 3 operators"); } try { ParsedSymop.createTransform(ss); } catch (RuntimeException e) { for (int i = 0; i < 3; i++) { String s = ss[i]; s = s.trim(); analyzeOperator(opString, i, s); } } } private void analyzeOperator(String opString, int i, String s) { StringTokenizer sst = new StringTokenizer(s, "+-", true); int ntok = sst.countTokens(); double sign = 1; for (int j = 0; j < ntok; j++) { String ss = sst.nextToken().trim(); int idx = ss.indexOf(S_SLASH); if (idx != -1) { final String numerator = ss.substring(0, idx).trim(); final String denominator = ss.substring(idx + 1).trim(); flmat[i][3] = sign * (double) Integer.parseInt(numerator) / (double) Integer.parseInt(denominator); } else if (ss.equalsIgnoreCase("x")) { flmat[i][0] = sign; } else if (ss.equalsIgnoreCase("y")) { flmat[i][1] = sign; } else if (ss.equalsIgnoreCase("z")) { flmat[i][2] = sign; } else if (ss.equals(S_MINUS)) { sign = -1; } else if (ss.equals(S_PLUS)) { sign = 1; } else if (ss.trim().equals("")) { // unusual, but some people put "y+1" } else { try { flmat[i][3] = sign * (double) Integer.parseInt(ss); } catch (NumberFormatException nfe) { SYSOUT.flush(); throw new EuclidRuntimeException("Bad string in symmetry: " + ss + " in " + opString); } } } } /** * clone. * * @param m * @return transform */ public Transform3 clone(Transform3 m) { // delete existing matrix in this Transform3 temp = new Transform3((RealSquareMatrix) m); temp.trnsfrm = m.trnsfrm; return temp; } /** * seem to require this one */ Transform3 clone(RealSquareMatrix m) { Transform3 temp = new Transform3(m); temp.trnsfrm = checkMatrix(); return temp; } /** * equality of two transforms. based on equality of RealSquareMatrix * * @param m * transform to compare * @return true if equal within Real.isEqual() */ public boolean isEqualTo(Transform3 m) { return super.isEqualTo((RealSquareMatrix) m) && trnsfrm == m.trnsfrm; } /** * concatenate. (I think this is right...) result = this * m2 i.e. if x' = * m2 * x and x'' = this * x'' then x'' = result * x; * * @param m2 * transform to be concatenated * @return result of applying this to m2 */ public Transform3 concatenate(Transform3 m2) { RealSquareMatrix temp = new RealSquareMatrix(((RealSquareMatrix) this) .multiply((RealSquareMatrix) m2)); // maximum value is matrix of greatest generality (horrible) Transform3 temp1 = new Transform3(temp); temp1.trnsfrm = (trnsfrm.i > m2.trnsfrm.i) ? trnsfrm : m2.trnsfrm; return temp1; } /** * set transformation type. * * @param option * the type * @return 0 if ok else 1 */ public int setTransformationType(Type option) { RealSquareMatrix s3 = new RealSquareMatrix(); if (option == Type.ROT_ORIG) /** orthonormalise and set trans vector to zero */ { s3 = new RealSquareMatrix(this.extractSubMatrixData(0, 2, 0, 2)); s3.orthonormalize(); this.replaceSubMatrixData(0, 0, s3); } else if (option == Type.ROT_TRANS) /** orthonormalise */ { s3 = new RealSquareMatrix(this.extractSubMatrixData(0, 2, 0, 2)); s3.orthonormalize(); this.replaceSubMatrixData(0, 0, s3); } else if (option == Type.ROT_TRANS_SCALE) /** * orthogonalise and take geometric mean of the three diagonal scale * elements */ { s3 = new RealSquareMatrix(this.extractSubMatrixData(0, 2, 0, 2)); double[] scale = s3.euclideanColumnLengths().getArray(); double scale3 = Math .exp(Math.log(scale[0] * scale[1] * scale[2]) / 3.0); s3.orthonormalize(); /** * diagonal scale matrix */ RealArray sc1 = new RealArray(3, scale3); RealSquareMatrix s = RealSquareMatrix.diagonal(sc1); s3 = s.multiply(s3); replaceSubMatrixData(0, 0, s3); } else if (option == Type.ROT_TRANS_SCALE_PERSP) { } else if (option == Type.ROT_TRANS_AXIAL_SCALE) { s3 = new RealSquareMatrix(this.extractSubMatrixData(0, 2, 0, 2)); RealArray scale = s3.euclideanColumnLengths(); s3.orthonormalize(); /** * diagonal scale matrix */ RealSquareMatrix s = RealSquareMatrix.diagonal(scale); s3 = s.multiply(s3); replaceSubMatrixData(0, 0, s3); } else if (option == Type.ANY) { /** * anything goes! */ } else if (option == Type.NULL) { } else { return 1; } trnsfrm = option; return 0; } /** * get transformation type. * * @return the type */ public Type getTransformationType() { return trnsfrm; } /** * get new matrix type. not sure what this is! * * @return the type */ public Type checkMatrix() { /** * get Top LHS (3x3 matrix) */ RealSquareMatrix s3 = new RealSquareMatrix(extractSubMatrixData(0, 2, 0, 2)); /** * and Column3 (translation) */ RealArray c3 = extractColumnData(3); if (c3 != null) { if (Real.isZero(c3.elementAt(0), Real.getEpsilon()) && Real.isZero(c3.elementAt(1), Real.getEpsilon()) && Real.isZero(c3.elementAt(2), Real.getEpsilon())) return Type.NULL; } /** no translation term */ { if (s3.isUnit()) return Type.NULL; // unit matrix if (s3.isUnitary()) return Type.ROT_ORIG; // unitary matrix } /** * translation term */ if (s3.isUnitary()) return Type.ROT_TRANS; // rot + trans; no scale /** * pure scale, no perspective */ if (s3.isOrthogonal()) { double[] scale = s3.euclideanColumnLengths().getArray(); if (Real.isEqual(scale[0], scale[1]) && Real.isEqual(scale[1], scale[2])) { return Type.ROT_TRANS_SCALE; } return Type.ROT_TRANS_AXIAL_SCALE; } return Type.ANY; } /** * interpret current matrix as rotation about general axis. user must supply * an empty axis and an empty angle, which will be filled by the routine. * will do better to create AxisAndAngle class * * @param axis * (holds return values) * @param ang * angle (holds return value) * @return flag */ public int getAxisAndAngle(Vector3 axis, Angle ang) { RealSquareMatrix s3 = new RealSquareMatrix(this.extractSubMatrixData(0, 2, 0, 2)); s3.orthonormalize(); int chirality = 1; /** * invert improper rotations */ if ((double) s3.determinant() < 0.0) { s3.negative(); chirality = -1; } double theta = Math.acos(((double) s3.trace() - 1.0) * 0.5); double[][] mat = s3.getMatrix(); double[] lmn = new double[3]; /** * theta might be exactly pi or zero */ if (Real.isEqual(theta, Math.PI) || Real.isEqual(theta, 0.0)) { lmn[0] = Math.sqrt((1.0 + mat[0][0]) * 0.5); if (Real.isZero(lmn[0], Real.getEpsilon())) { lmn[1] = mat[0][1] * 0.5 / lmn[0]; lmn[2] = mat[0][2] * 0.5 / lmn[0]; } else { lmn[1] = Math.sqrt((1.0 + mat[1][1]) * 0.5); lmn[2] = mat[1][2] / (2.0 * lmn[1]); } } else { double c = 1.0 / (2.0 * Math.sin(theta)); lmn[0] = (mat[2][1] - mat[1][2]) * c; lmn[1] = (mat[0][2] - mat[2][0]) * c; lmn[2] = (mat[1][0] - mat[0][1]) * c; } /** * stuff into angle and axis */ ang.shallowCopy(new Angle(theta)); System.arraycopy(lmn, 0, axis.getArray(), 0, 3); return chirality; } /** * get translation component. * * @return the translation */ public Vector3 getTranslation() { return new Vector3(flmat[0][3], flmat[1][3], flmat[2][3]); } /** increment translation component. * add vector to current translation * @param dt translation increment */ public void incrementTranslation(Vector3 dt) { flmat[0][3] += dt.flarray[0]; flmat[1][3] += dt.flarray[1]; flmat[2][3] += dt.flarray[2]; } /** set translation component. * * @param t translation vector */ public void setTranslation(Vector3 t) { flmat[0][3] = t.flarray[0]; flmat[1][3] = t.flarray[1]; flmat[2][3] = t.flarray[2]; } /** * get centre of rotation. if R is rotation and t is translation compute p = * ~(I - R) . t * * @return the centre */ public Point3 getCentreOfRotation() { Point3 p = new Point3(); RealSquareMatrix unit = new RealSquareMatrix(3); RealSquareMatrix temp = new RealSquareMatrix(this.extractSubMatrixData(0, 2, 0, 2)); unit = unit.subtract(temp); // (I - Rmat) unit.transpose(); RealArray t = new RealArray(getTranslation().getArray()); p = new Point3(unit.multiply(t).getArray()); // p = ~(I - R) . t return p; } /** * get scales. * * @return 3-element RealArray) */ public RealArray getScales() { RealArray scales; RealSquareMatrix s3 = new RealSquareMatrix(extractSubMatrixData(0, 2, 0, 2)); scales = s3.euclideanColumnLengths(); return scales; } /** * get Unitary matrix. eliminate scales and translation and normalize * * @return unitary 3X3 matrix */ public RealSquareMatrix getRotationMatrix() { RealSquareMatrix s; RealSquareMatrix s3 = new RealSquareMatrix(extractSubMatrixData(0, 2, 0, 2)); s3.normaliseByColumns(); s = s3; return s; } final static String[] oper = {"x", "y", "z"}; /** return operator in crystallographic form; * * @return string of type "x,-y,1/2+z" */ public String getCrystallographicString() { String s = ""; for (int irow = 0; irow < 3; irow++) { s += trans(flmat[irow][3]); for (int jcol = 0; jcol < 3; jcol++) { double f = flmat[irow][jcol]; if (f > 0.1) { s += EuclidConstants.S_PLUS+oper[jcol]; } else if (f < -0.1) { s += EuclidConstants.S_MINUS+oper[jcol]; } } if (irow < 2) { s += EuclidConstants.S_COMMA; } } return s; } private String trans(double dd) { int n = (int) Math.round(dd * 12); int d = 12; if (n % 2 == 0) { n /= 2; d /= 2; } if (n % 2 == 0) { n /= 2; d /= 2; } if (n % 3 == 0) { n /= 3; d /= 3; } String s = EuclidConstants.S_EMPTY; if (n != 0) { if (d == 1) { s = EuclidConstants.S_EMPTY+n; } else { s += EuclidConstants.S_EMPTY+n+S_SLASH+d; } } return s; } /** * check not null * * @param t transform3 */ public static void checkNotNull(Transform3 t) { if (t == null) { throw new EuclidRuntimeException("null transform"); } } } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/Univariate.java000066400000000000000000000566751461721410700250600ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid; import java.util.ArrayList; import java.util.List; import org.apache.log4j.Logger; /** * Univariate stats. * * @author (C) P. Murray-Rust, 2001, 2004 */ public class Univariate { private static Logger LOG = Logger.getLogger(Univariate.class); RealArray realArray; double[] array; int count; double mean = Double.NaN; double sum = Double.NaN; double median = Double.NaN; double variance = Double.NaN; double xMax = Double.NaN; double xMin = Double.NaN; double lowXRange = Double.NaN; double stdev = Double.NaN; RealArray deviateArray = null; RealArray zValueArray = null; boolean gotMean = false; boolean gotVariance = false; boolean gotStandardDeviation = false; boolean isSorted = false; int binCount; int[] binCounts; // count of each bin int[] binStart; // start index of each bin double deltaX = Double.NaN; // bin step RealArray binArray = null; // lower bin values private List binList; private List valueFrequencyList; /** default constructor. */ public Univariate() { init(); realArray = new RealArray(); } /** * creates from data array. copies realArray * * @param realArray * the data */ public Univariate(RealArray realArray) { init(); setArray(realArray); } void init() { setBinCount(10); } /** * sets data copies realArray * * @param realArray * the data */ public void setArray(RealArray realArray) { this.realArray = new RealArray(realArray); getCount(); } /** * set bin count. * * @param binCount * the number of bins */ public void setBinCount(int binCount) { this.binCount = binCount; } /** * get bin count. * * @return binCount (default 10) */ public int getBinCount() { return binCount; } /** * get number of data points. * * @return count */ public int getCount() { count = realArray.size(); return count; } /** * get minimum value. * * @return minimum value */ public double getMin() { xMin = realArray.smallestElement(); return xMin; } /** * get maximum value. * * @return maximum value */ public double getMax() { xMax = realArray.largestElement(); return xMax; } /** * get mean value. @ not enough points * * @return mean value; Double NaN if no points */ public double getMean() { if (!gotMean || mean == Double.NaN) { count = realArray.size(); if (count == 0) { mean = Double.NaN; } else { sum = realArray.sumAllElements(); mean = sum / (double) count; gotMean = true; } } return mean; } /** * get variance. @ not enough points * * @return variance */ public double getVariance() { if (!gotVariance) { getCount(); if (count < 2) { throw new RuntimeException("Only one point"); } getMean(); double sumx2 = 0.0; getDeviateValues(); double x[] = deviateArray.getArray(); for (int i = 0; i < count; i++) { sumx2 += x[i] * x[i]; } // variance = (sumx2 - count * mean * mean) / (double) (count - 1); variance = (sumx2) / (double) (count - 1); gotVariance = true; } return variance; } /** * get standard deviation. @ not enough points * * @return standard deviation */ public double getStandardDeviation() { getVariance(); stdev = Math.sqrt(variance); return stdev; } /** * get standard error. @ not enough points * * @return standard error */ public double getStandardError() { getVariance(); return (Math.sqrt(variance / (double) count)); } /** * get data points. * * @return the points */ public double[] getArray() { array = realArray.getArray(); return array; } /** * get sorted data points. modifies this * * @return the points */ public double[] getSortedArray() { if (!isSorted) { realArray.sortAscending(); getArray(); isSorted = true; } return array; } /** * get normalized values. array transformed by subtracting mean and dividing * by standard deviation. result (the "z"-values") therefore have mean of * zero and stdev of 1.0. does not modify this. * * @return the normalized values */ public RealArray getNormalizedValues() { double[] array = realArray.getArray(); getMean(); getDeviateValues(); double[] dvArray = deviateArray.getArray(); getStandardDeviation(); double[] normArray = new double[array.length]; for (int i = 0; i < array.length; i++) { normArray[i] = (dvArray[i]) / stdev; } zValueArray = new RealArray(normArray); return zValueArray; } /** * get deviate values. array transformed by subtrating mean. result * therefore has mean of zero. does not modify this. * * @return the deviate values */ public RealArray getDeviateValues() { double[] array = realArray.getArray(); getMean(); double[] dvArray = new double[array.length]; for (int i = 0; i < array.length; i++) { dvArray[i] = (array[i] - mean); } deviateArray = new RealArray(dvArray); return deviateArray; } /** * get quantile. * * @param q * the value of the quantile (0 ={@literal <} q ={@literal <} 1.0) @ not enough points * @return quantile */ public double getQuantile(double q) { double quantile = Double.NaN; if (q > 1.0 || q < 0.0) { throw new RuntimeException("Quantile value out of range: " + q); } getSortedArray(); // count of quantile element double dindex = (count + 1) * q; int index = (int) dindex; int flindex = (int) Math.floor(dindex); int cindex = (int) Math.ceil(dindex); // exact match if ((dindex - index) == 0) { quantile = array[index - 1]; } else { quantile = q * array[flindex - 1] + (1 - q) * array[cindex - 1]; } return quantile; } /** * get median. @ not enough points * * @return median */ public double getMedian() { median = getQuantile(0.50); return median; } void calculateSummaryStats() { try { getMin(); getMax(); getMean(); getVariance(); getStandardDeviation(); } catch (Exception e) { LOG.warn("too few points"); } } /** * gets the xvalues for the bins. * * These are the low value for each bin. bin(i) runs from * xvalue(i)-xvalue(i+1) * * the median value for the bin is xvalue(i)+deltax * * the last bin is from xvalue(binCount-1) ... xvalue(binCount-1)+deltaX * * @return xvalues */ public RealArray getXValues() { getLowXRangeAndDeltaX(); binArray = new RealArray(binCount, xMin, deltaX); return binArray; } /** * gets the median xvalues for the bins. * * These are the median for each bin. bin(i) runs from xvalue(i)-xvalue(i+1) * * the median value for the bin is xvalue(i)+deltax * * @return xvalues */ public RealArray getMedianXValues() { getLowXRangeAndDeltaX(); binArray = new RealArray(binCount, xMin + deltaX / 2.0, deltaX); return binArray; } private void getLowXRangeAndDeltaX() { lowXRange = realArray.getRange().getRange() + Real.getEpsilon(); deltaX = lowXRange / binCount; getMin(); } public double getBinWidth() { return deltaX; } /** * this sorts the bins in order of frequency. * * A simple way of finding the local maxima * * @return */ public RealArray getMedianBinValuesSortedByFrequency() { IntArray intArray = getIndexOfBinsSortedByDescendingFrequency(); RealArray sortedBins = new RealArray(getBinCount()); RealArray medians = getMedianXValues(); for (int i : intArray.getArray()) { sortedBins .setElementAt(i, medians.elementAt(intArray.elementAt(i))); } return sortedBins; } /** * gets pointers to bins sorted by descending frequency. * * array.elementAt(0) points to largest bin * * @return pointers */ public IntArray getIndexOfBinsSortedByDescendingFrequency() { IntArray counts = new IntArray(this.getHistogramCounts()); IntSet indexSet = counts.indexSortDescending(); return new IntArray(indexSet.getElements()); } /** * gets bin values and frequencies sorted by frequency * * each Real2 is x-value, frequency * * zero counts are not reported * * @return */ public List getBinsSortedByFrequency() { if (valueFrequencyList == null) { valueFrequencyList = new ArrayList(); for (int i = 0; i < getBinCount(); i++) { valueFrequencyList.add(new Real2()); } IntArray indexes = getIndexOfBinsSortedByDescendingFrequency(); RealArray medians = getMedianXValues(); int[] counts = this.getHistogramCounts(); int numNonZero = 0; for (int ii = 0; ii < getBinCount(); ii++) { int i = indexes.elementAt(ii); if (counts[i] > 0.1) { valueFrequencyList.set(ii, new Real2(medians.elementAt(i), counts[i])); numNonZero++; } else { valueFrequencyList.set(ii, null); // this was a bug? // numNonZero++; } } List array1 = new ArrayList(); for (int i = 0; i < numNonZero; i++) { array1.add(valueFrequencyList.get(i)); } valueFrequencyList = array1; } return valueFrequencyList; } public RealRange getRange() { getXValues(); return new RealRange(binArray.get(0), binArray.getLast() + deltaX); } public List getUnivariatesForBins() { getBins(); List univariateList = new ArrayList(); for (int i = 0; i < binCount; i++) { RealArray array = binList.get(i).getArray(); Univariate univariate = new Univariate(array); univariateList.add(univariate); } return univariateList; } /** * return bins for Histogram. @ not enough points * * @return the counts in each bin */ public int[] getHistogramCounts() { getBins(); binCounts = new int[binCount]; for (int i = 0; i < binCount; i++) { binCounts[i] = binList.get(i).getCount(); } return binCounts; } private List getBins() { calculateSummaryStats(); getXValues(); getSortedArray(); binList = new ArrayList(); for (int i = 0; i < binCount; i++) { binList.add(new UnivariateBin()); } binStart = new int[binCount]; int lastBin = -1; for (int i = 0; i < count; i++) { double x = realArray.getArray()[i]; double diff = x - xMin; int binNumber = (int) (diff / deltaX); if (binNumber < 0) binNumber = 0; if (binNumber >= binCount) binNumber = binCount - 1; if (binNumber > lastBin) { binStart[binNumber] = i; lastBin = binNumber; } UnivariateBin bin = binList.get(binNumber); bin.add(x); } return binList; } /** * get normal parameters * * I think... variate should be normally distributed about 1.0 with sd = 1.0 * * @param count * number of points * @return the normal distribution */ public static Univariate getNormalParams(int count) { double a = (count <= 10) ? 0.375 : 0.500; double[] z = new double[count]; for (int i = 0; i < count; i++) { z[i] = Univariate.qnorm((i + 1 - a) / (count + 1 - 2 * a)); } return (new Univariate(new RealArray(z))); } /** * percentage points of normal distribution. upper is true. * * @param p * (0 {@literal <}= p {@literal <}= 1) * @return the percentage point (?) */ public static double qnorm(double p) { return qnorm(p, true); } /** * percentage points of normal distribution. J. D. Beasley and S. G. * Springer Algorithm AS 111: * "The Percentage Points of the Normal Distribution" Applied Statistics * * @param p * (0 {@literal <}= p {@literal <}= 1) * @param upper * if true use upper half (??) * @return the percentage point (?) */ public static double qnorm(double p, boolean upper) { if (p < 0 || p > 1) { throw new IllegalArgumentException("Illegal argument " + p + " for qnorm(p)."); } double split = 0.42, a0 = 2.50662823884, a1 = -18.61500062529, a2 = 41.39119773534, a3 = -25.44106049637, b1 = -8.47351093090, b2 = 23.08336743743, b3 = -21.06224101826, b4 = 3.13082909833, c0 = -2.78718931138, c1 = -2.29796479134, c2 = 4.85014127135, c3 = 2.32121276858, d1 = 3.54388924762, d2 = 1.63706781897, q = p - 0.5; double r, ppnd; if (Math.abs(q) <= split) { r = q * q; ppnd = q * (((a3 * r + a2) * r + a1) * r + a0) / ((((b4 * r + b3) * r + b2) * r + b1) * r + 1); } else { r = p; if (q > 0) r = 1 - p; if (r > 0) { r = Math.sqrt(-Math.log(r)); ppnd = (((c3 * r + c2) * r + c1) * r + c0) / ((d2 * r + d1) * r + 1); if (q < 0) ppnd = -ppnd; } else { ppnd = 0; } } if (upper) ppnd = 1 - ppnd; return (ppnd); } /** * percentage points of normal distribution. J. D. Beasley and S. G. * Springer Algorithm AS 111: * "The Percentage Points of the Normal Distribution" Applied Statistics * * @param p * (0 {@literal <}= p {@literal <}= 1) * @param upper * if true use upper half (??) * @param mu * mean * @param sigma2 * the variance(?) * @return the percentage point (?) */ public static double qnorm(double p, boolean upper, double mu, double sigma2) { return (qnorm(p, upper) * Math.sqrt(sigma2) + mu); } /** * normal integral. I. D. Hill Algorithm AS 66: "The Normal Integral" * Applied Statistics * * @param z * @param upper * if true use upper half (??) * @return the integral (?) */ public static double pnorm(double z, boolean upper) { /* * Reference: */ double ltone = 7.0, utzero = 18.66, con = 1.28, a1 = 0.398942280444, a2 = 0.399903438504, a3 = 5.75885480458, a4 = 29.8213557808, a5 = 2.62433121679, a6 = 48.6959930692, a7 = 5.92885724438, b1 = 0.398942280385, b2 = 3.8052e-8, b3 = 1.00000615302, b4 = 3.98064794e-4, b5 = 1.986153813664, b6 = 0.151679116635, b7 = 5.29330324926, b8 = 4.8385912808, b9 = 15.1508972451, b10 = 0.742380924027, b11 = 30.789933034, b12 = 3.99019417011; double y, alnorm; if (z < 0) { upper = !upper; z = -z; } if (z <= ltone || upper && z <= utzero) { y = 0.5 * z * z; if (z > con) { alnorm = b1 * Math.exp(-y) / (z - b2 + b3 / (z + b4 + b5 / (z - b6 + b7 / (z + b8 - b9 / (z + b10 + b11 / (z + b12)))))); } else { alnorm = 0.5 - z * (a1 - a2 * y / (y + a3 - a4 / (y + a5 + a6 / (y + a7)))); } } else { alnorm = 0; } if (!upper) { alnorm = 1 - alnorm; } return (alnorm); } /** * normal integral. I. D. Hill Algorithm AS 66: "The Normal Integral" * Applied Statistics * * @param x * @param upper * if true use upper half (??) * @param mu * mean * @param sigma2 * the variance(?) * @return the integral (?) */ public static double pnorm(double x, boolean upper, double mu, double sigma2) { return (pnorm((x - mu) / Math.sqrt(sigma2), upper)); } /** * Student's t-quantiles. Algorithm 396: Student's t-quantiles by G.W. Hill * CACM 13(10), 619-620, October 1970 * * @param p * (0 {@literal <}= p {@literal <}= 1) * @param ndf * degrees of freedom {@literal >}= 1 * @param lower_tail * @return the integral (?) */ public static double qt(double p, double ndf, boolean lower_tail) { if (p <= 0 || p >= 1 || ndf < 1) { throw new IllegalArgumentException( "Invalid p or df in call to qt(double,double,boolean)."); } double eps = 1e-12; double M_PI_2 = 1.570796326794896619231321691640; // pi/2 boolean neg; double P, q, prob, a, b, c, d, y, x; if ((lower_tail && p > 0.5) || (!lower_tail && p < 0.5)) { neg = false; P = 2 * (lower_tail ? (1 - p) : p); } else { neg = true; P = 2 * (lower_tail ? p : (1 - p)); } if (Math.abs(ndf - 2) < eps) { /* df ~= 2 */ q = Math.sqrt(2 / (P * (2 - P)) - 2); } else if (ndf < 1 + eps) { /* df ~= 1 */ prob = P * M_PI_2; q = Math.cos(prob) / Math.sin(prob); } else { /*-- usual case; including, e.g., df = 1.1 */ a = 1 / (ndf - 0.5); b = 48 / (a * a); c = ((20700 * a / b - 98) * a - 16) * a + 96.36; d = ((94.5 / (b + c) - 3) / b + 1) * Math.sqrt(a * M_PI_2) * ndf; y = Math.pow(d * P, 2 / ndf); if (y > 0.05 + a) { /* Asymptotic inverse expansion about normal */ x = qnorm(0.5 * P, false); y = x * x; if (ndf < 5) { c += 0.3 * (ndf - 4.5) * (x + 0.6); } c = (((0.05 * d * x - 5) * x - 7) * x - 2) * x + b + c; y = (((((0.4 * y + 6.3) * y + 36) * y + 94.5) / c - y - 3) / b + 1) * x; y = a * y * y; if (y > 0.002) {/* * FIXME: This cutoff is machine-precision * dependent */ y = Math.exp(y) - 1; } else { /* Taylor of e^y -1 : */ y = (0.5 * y + 1) * y; } } else { y = ((1 / (((ndf + 6) / (ndf * y) - 0.089 * d - 0.822) * (ndf + 2) * 3) + 0.5 / (ndf + 4)) * y - 1) * (ndf + 1) / (ndf + 2) + 1 / y; } q = Math.sqrt(ndf * y); } if (neg) { q = -q; } return q; } /** * T-test. ALGORITHM AS 3 APPL. STATIST. (1968) VOL.17, P.189 Computes P(T {@literal <} * t) * * @param t * @param df * degrees of freedom * @return probability (?) */ public static double pt(double t, double df) { double a, b, idf, im2, ioe, s, c, ks, fk, k; double g1 = 0.3183098862;// =1/pi; if (df < 1) { throw new IllegalArgumentException( "Illegal argument df for pt(t,df)."); } idf = df; a = t / Math.sqrt(idf); b = idf / (idf + t * t); im2 = df - 2; ioe = idf % 2; s = 1; c = 1; idf = 1; ks = 2 + ioe; fk = ks; if (im2 >= 2) { for (k = ks; k <= im2; k += 2) { c = c * b * (fk - 1) / fk; s += c; if (s != idf) { idf = s; fk += 2; } } } if (ioe != 1) { return 0.5 + 0.5 * a * Math.sqrt(b) * s; } if (df == 1) { s = 0; } return 0.5 + (a * b * s + Math.atan(a)) * g1; } /** * chiSquared. Posten, H. (1989) American Statistician 43 p. 261-265 * * @param q * @param df * degrees of freedom * @return chisq (?) */ public double pchisq(double q, double df) { double df2 = df * .5; double q2 = q * .5; int n = 5, k; double tk, CFL, CFU, prob; if (q <= 0 || df <= 0) { throw new IllegalArgumentException("Illegal argument " + q + " or " + df + " for qnorm(p)."); } if (q < df) { tk = q2 * (1 - n - df2) / (df2 + 2 * n - 1 + n * q2 / (df2 + 2 * n)); for (k = n - 1; k > 1; k--) { tk = q2 * (1 - k - df2) / (df2 + 2 * k - 1 + k * q2 / (df2 + 2 * k + tk)); } CFL = 1 - q2 / (df2 + 1 + q2 / (df2 + 2 + tk)); prob = Math.exp(df2 * Math.log(q2) - q2 - lnfgamma(df2 + 1) - Math.log(CFL)); } else { tk = (n - df2) / (q2 + n); for (k = n - 1; k > 1; k--) { tk = (k - df2) / (q2 + k / (1 + tk)); } CFU = 1 + (1 - df2) / (q2 + 1 / (1 + tk)); prob = 1 - Math.exp((df2 - 1) * Math.log(q2) - q2 - lnfgamma(df2) - Math.log(CFU)); } return prob; } /** * betainv ALGORITHM AS 63 APPL. STATIST. VOL.32, NO.1 Computes P(Beta{@literal >}x) * * @param x * @param p * @param q * @return betainv (?) */ public static double betainv(double x, double p, double q) { double beta = lnfbeta(p, q), acu = 1E-14; double cx, psq, pp, qq, x2, term, ai, betain, ns, rx, temp; boolean indx; if (p <= 0 || q <= 0) { return (-1.0); } if (x <= 0 || x >= 1) { return (-1.0); } psq = p + q; cx = 1 - x; if (p < psq * x) { x2 = cx; cx = x; pp = q; qq = p; indx = true; } else { x2 = x; pp = p; qq = q; indx = false; } term = 1; ai = 1; betain = 1; ns = qq + cx * psq; rx = x2 / cx; temp = qq - ai; if (ns == 0) { rx = x2; } while (temp > acu && temp > acu * betain) { term = term * temp * rx / (pp + ai); betain = betain + term; temp = Math.abs(term); if (temp > acu && temp > acu * betain) { ai++; ns--; if (ns >= 0) { temp = qq - ai; if (ns == 0) rx = x2; } else { temp = psq; psq += 1; } } } betain *= Math.exp(pp * Math.log(x2) + (qq - 1) * Math.log(cx) - beta) / pp; if (indx) betain = 1 - betain; return (betain); } /** * betainv ALGORITHM AS 63 APPL. STATIST. VOL.32, NO.1 Computes P(F{@literal >}x) * * @param x * @param df1 * @param df2 * @return (?) */ public static double pf(double x, double df1, double df2) { return (betainv(df1 * x / (df1 * x + df2), 0.5 * df1, 0.5 * df2)); } public static void test() { Util.println("--------------Testing Univariate--------------\n"); RandomNumberGenerator rng = new RandomNumberGenerator(); int npoints1 = 1000; double[] data1 = new double[npoints1]; for (int i = 0; i < npoints1; i++) { data1[i] = rng.nextGaussian(); } RealArray dataArray1 = new RealArray(data1); Univariate univ1 = new Univariate(dataArray1); Util.println("mean: " + univ1.getMean()); Util.println("sdev: " + univ1.getStandardDeviation()); int npoints2 = 300; double[] data2 = new double[npoints2]; for (int i = 0; i < npoints2; i++) { data2[i] = rng.nextGaussian() * 0.5 + 5.0; } RealArray dataArray2 = new RealArray(data2); Univariate univ2 = new Univariate(dataArray2); Util.println("mean: " + univ2.getMean()); Util.println("sdev: " + univ2.getStandardDeviation()); dataArray1.addArray(dataArray2); Univariate univ3 = new Univariate(dataArray1); Util.println("mean: " + univ3.getMean()); Util.println("sdev: " + univ3.getStandardDeviation()); Util.println("min: " + univ3.getMin()); Util.println("q1: " + univ3.getQuantile(0.25)); Util.println("median: " + univ3.getMedian()); Util.println("q3: " + univ3.getQuantile(0.75)); Util.println("max: " + univ3.getMax()); univ3.setBinCount(30); int[] bins = univ3.getHistogramCounts(); for (int i = 0; i < bins.length; i++) { Util.println("bin: " + bins[i]); } Univariate norm = Univariate.getNormalParams(1000); Util.println("mean: " + norm.getMean()); Util.println("sdev: " + norm.getStandardDeviation()); Util.println("min: " + norm.getMin()); Util.println("q1: " + norm.getQuantile(0.25)); Util.println("median: " + norm.getMedian()); Util.println("q3: " + norm.getQuantile(0.75)); Util.println("max: " + norm.getMax()); } public static double lnfgamma(double c) { int j; double x, y, tmp, ser; double[] cof = { 76.18009172947146, -86.50532032941677, 24.01409824083091, -1.231739572450155, 0.1208650973866179e-2, -0.5395239384953e-5 }; y = x = c; tmp = x + 5.5 - (x + 0.5) * Math.log(x + 5.5); ser = 1.000000000190015; for (j = 0; j <= 5; j++) { ser += (cof[j] / ++y); } return (Math.log(2.5066282746310005 * ser / x) - tmp); } public static double lnfbeta(double a, double b) { return (lnfgamma(a) + lnfgamma(b) - lnfgamma(a + b)); } public static double fbeta(double a, double b) { return Math.exp(lnfbeta(a, b)); } public static double fgamma(double c) { return Math.exp(lnfgamma(c)); } public static double fact(int n) { return Math.exp(lnfgamma(n + 1)); } public static double lnfact(int n) { return lnfgamma(n + 1); } public static double nCr(int n, int r) { return Math.exp(lnfact(n) - lnfact(r) - lnfact(n - r)); } public static double nPr(int n, int r) { return Math.exp(lnfact(n) - lnfact(r)); } public static void main(String[] args) { test(); } } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/UnivariateBin.java000066400000000000000000000021271461721410700254700ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid; /** manages the contents of a Bin in Univariate as a sub-Univariate * * * @author pm286 * */ public class UnivariateBin { private RealArray array; public UnivariateBin() { array = new RealArray(); } public void add(double x) { array.addElement(x); } public Univariate getUnivariate() { return new Univariate(array); } public RealArray getArray() { return array; } public int getCount() { return array.size(); } } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/Util.java000066400000000000000000002353341461721410700236550ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.io.EOFException; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.PrintStream; import java.net.HttpURLConnection; import java.net.URISyntaxException; import java.net.URL; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.text.NumberFormat; import java.text.ParseException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Enumeration; import java.util.HashMap; import java.util.Hashtable; import java.util.List; import java.util.Map; import java.util.StringTokenizer; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.IOUtils; import org.apache.log4j.Logger; /** * A number of miscellaneous tools. Originally devised for jumbo.sgml, now * rewritten for jumbo.xml. Use these at your peril - some will be phased out * * @author (C) P. Murray-Rust, 1998 * @author 20 August 2003 */ public class Util implements EuclidConstants { private static final PrintStream SYSOUT = System.out; final static Logger LOG = Logger.getLogger(Util.class); /** messages */ public enum Message { /** not yet implemented */ NYI("not yet implemented"), ; /** value */ public String value; private Message(String v) { value = v; } } private static final String PM286 = "pm286"; public final static String[] LOWER_ROMAN_NUMERAL = { "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix", "x", "xi", "xii", "xiii", "xiv", "xv", "xvi", "xvii", "xviii", "xix", "xx", "xxi", "xxii", "xxiii", "xxiv", "xxv", "xxvi", "xxvii", "xxviii", "xxix", "xxx", "xxxi", "xxxii", "xxxiii", "xxxiv", "xxxv", "xxxvi", "xxxvii", "xxxviii", "xxxix", "xl", "xli", "xlii", "xliii", "xliv", "xlv", "xlvi", "xlvii", "xlviii", "xlix", "l", }; /** * regex matching roman numbers up to m ("[ivxlcdm]+"). */ public final static String LOWER_ROMAN_REGEX = "[ivxlcdm]+"; /** * regex matching roman numbers up to M ("[IVXLCDM]+"). */ public final static String UPPER_ROMAN_REGEX = "[IVXLCDM]+"; private final static File TEMP_DIRECTORY = new File("target" + File.separator + "test-outputs"); /** * get temporary directory - mainly for testing methods with outputs. calls * mkdirs() if does not exist * * @return temporary directory. */ public static File getTEMP_DIRECTORY() { if (!TEMP_DIRECTORY.exists()) { boolean ok = TEMP_DIRECTORY.mkdirs(); if (!ok) { throw new RuntimeException( "Cannot create temporary directory : " + TEMP_DIRECTORY.getAbsolutePath()); } } return TEMP_DIRECTORY; } /** * get class-specific temporary directory - mainly for testing methods with * ouputs. calls mkdirs() if does not exist * * @param classx * @return temporary directory. */ public static File getTestOutputDirectory(Class classx) { File tempDir = getTEMP_DIRECTORY(); String dirs = classx.getName().replace(S_PERIOD, File.separator); File testDir = new File(tempDir, dirs); if (!testDir.exists()) { boolean ok = testDir.mkdirs(); if (!ok) { throw new RuntimeException( "Cannot create temporary class directory : " + testDir.getAbsolutePath()); } } return testDir; } /** * convenience method to extend array of Strings. * * @param array * to extend * @param s * element to add * @return extended array */ public final static String[] addElementToStringArray(String[] array, String s) { int l = array.length; String[] array1 = new String[l + 1]; for (int i = 0; i < l; i++) { array1[i] = array[i]; } array1[l] = s; return array1; } /** * convenience method to remove element from array of Strings. * * Removes ALL occurrences of string * * @param array * to edit * @param s * element to remove * @return depleted array */ public final static String[] removeElementFromStringArray(String[] array, String s) { List sList = new ArrayList(); for (int i = 0; i < array.length; i++) { if (!array[i].equals(s)) { sList.add(array[i]); } } return (String[]) sList.toArray(new String[0]); } /** * traps a bug. use for programming errors where this could can "never be * reached" concatenates msg with "BUG" and throws {@link RuntimeException} * * @param msg * @param e */ public static void BUG(String msg, Exception e) { msg = (msg == null || msg.trim().length() == 0) ? EC.S_EMPTY : EC.S_LBRAK + msg + EC.S_RBRAK; throw new RuntimeException("BUG: " + msg + "should never throw: " + e, e); } /** * traps a bug. empty message. * * @see #BUG(String, Throwable) * @param e */ public static void BUG(Exception e) { BUG(S_EMPTY, e); } /** * convenience method for "not yet implemented". deliberately deprecated so * that it requires deprecated on all modules containing NYI * * @deprecated * @throws RuntimeException */ public static void throwNYI() { throw new RuntimeException(Message.NYI.value); } /** * traps a bug. * * @see #BUG(String, Throwable) * @param msg */ public static void BUG(String msg) { BUG(msg, new RuntimeException()); } /** * convenience method to get input stream from resource. the resource is * packaged with the classes for distribution. typical filename is * org/xmlcml/molutil/elementdata.xml for file elementdata.xml in class * hierarchy org.xmlcml.molutil * * @param filename * relative to current class hierarchy. * @return input stream * @throws IOException */ public static InputStream getInputStreamFromResource(String filename) throws IOException { return getResource(filename).openStream(); } /** * creates directories and files if they don't exist. creates dir/filename * * @param dir * @param filename * @throws IOException */ public static void createFile(File dir, String filename) throws IOException { File file = new File(dir + File.separator + filename); if (!dir.exists()) { boolean ok = dir.mkdirs(); if (!ok) { throw new IOException("cannot make dictories: " + dir + EC.S_SPACE + filename); } } if (!file.exists()) { file.createNewFile(); } } /** * creates resource from filename. uses ClassLoader.getResource() * * @param filename * name relative to classroot * @return url or null */ public static URL getResource(String filename) { URL url = null; if (filename != null) { ClassLoader l = Util.class.getClassLoader(); url = l.getResource(filename); if (url == null) { throw new RuntimeException("No resource with name " + filename); } } return url; } public static InputStream getResourceUsingContextClassLoader(String name, Class clazz) throws FileNotFoundException { ClassLoader cl = Thread.currentThread().getContextClassLoader(); if (cl == null) { cl = clazz.getClassLoader(); } InputStream is = cl.getResourceAsStream(name); if (is == null) { throw new FileNotFoundException("Resource not found: "+name); } return is; } /** * gets file from build path components. pm286 is not quite sure how it does * this... * * @author ojd20@cam.ac.uk * @param path * @return file or null * @throws URISyntaxException */ public static File getResourceFile(String... path) throws URISyntaxException { File f = new File(Util.class.getClassLoader().getResource( buildPath(path)).toURI()); return f; } /** * gets build path from its components. * * @param parts * @return build path concatenated with File.separatorChar */ public static String buildPath(String... parts) { StringBuilder sb = new StringBuilder(parts.length * 20); for (String part : parts) { sb.append(part).append(File.separatorChar); } sb.deleteCharAt(sb.length() - 1); return sb.toString(); } /** * delete a file If directory==true then file will be recursively deleted * * @param file * Description of the Parameter * @param deleteDirectory * Description of the Parameter * @return Description of the Return Value */ public static boolean deleteFile(File file, boolean deleteDirectory) { if (file.exists()) { if (file.isDirectory() && deleteDirectory) { String[] filenames = file.list(); for (int i = 0; i < filenames.length; i++) { File childFile = new File(file.toString() + File.separator + filenames[i]); deleteFile(childFile, deleteDirectory); } } return file.delete(); } else { return false; } } /** * copy one file to another (I suspect there is a better way * * @param inFile * Description of the Parameter * @param outFile * Description of the Parameter * @exception FileNotFoundException * Description of the Exception * @exception IOException * Description of the Exception */ public static void copyFile(File inFile, File outFile) throws FileNotFoundException, IOException { BufferedInputStream bis = new BufferedInputStream(new FileInputStream( inFile)); BufferedOutputStream bos = new BufferedOutputStream( new FileOutputStream(outFile)); byte[] buffer = new byte[10000]; while (true) { int b = bis.read(buffer); if (b == -1) { break; } bos.write(buffer, 0, b); } bis.close(); bos.close(); } /** * reads a stream from url and outputs it as integer values of the * characters and as strings. Emulates UNIX od(). * * @param url * Description of the Parameter * @return String tabular version of input (in 10-column chunks) * @exception Exception * Description of the Exception */ public static String dump(URL url) throws Exception { BufferedReader br = new BufferedReader(new InputStreamReader(url .openStream())); int count = 0; StringBuffer sb = new StringBuffer(); String s0 = "\n"; String s1 = EC.S_EMPTY; while (true) { int i = br.read(); if (i == -1) { break; } String s = " " + i; while (s.length() > 4) { s = s.substring(1); } s0 += s; if (i >= 32 && i < 128) { s1 += (char) i; } else { s1 += EC.S_SPACE; } if (++count % 10 == 0) { sb.append(s0 + " " + s1); s1 = EC.S_EMPTY; s0 = "\n"; } } if (count != 0) { sb.append(s0 + " " + s1); } return sb.toString(); } /** * make a String of a given number of spaces * * @param nspace * Description of the Parameter * @return Description of the Return Value */ public static String spaces(int nspace) { if (nspace <= 0) { return EC.S_EMPTY; } else { StringBuffer sb = new StringBuffer(); for (int i = 0; i < nspace; i++) { sb.append(S_SPACE); } return sb.toString(); } } /** * * gets suffix from filename * * * * @param filename * Description of the Parameter * * @return The suffix value * */ public static String getSuffix(String filename) { int idx = filename.lastIndexOf(Util.S_PERIOD); if (idx == -1) { return null; } return filename.substring(idx + 1, filename.length()); } /** * return the first n characters of a string and add ellipses if truncated * * @param s * @param maxlength * @return String the (possibly) truncated string */ public static String truncateAndAddEllipsis(String s, int maxlength) { if (s != null) { int l = s.length(); s = (l <= maxlength) ? s : s.substring(0, maxlength) + " ... "; } return s; } /** * return the first n characters of a string and add ellipses if truncated * * @param s * @param maxlength * @return String the (possibly) truncated string */ public static String truncateAndAddNewlinesAndEllipsis(String s, int maxlength) { return (s == null) ? null : truncateAndAddEllipsis(s.replace(S_NEWLINE, "\\n"), maxlength); } /** * remove balanced quotes from ends of (trimmed) string, else no action * * @param s * Description of the Parameter * @return Description of the Return Value */ public static String deQuote(String s) { if (s == null) { return null; } String ss = s.trim(); if (ss.equals(S_EMPTY)) { return ss; } char c = ss.charAt(0); if (c == '"' || c == '\'') { int l = ss.length(); if (ss.charAt(l - 1) == c) { return ss.substring(1, l - 1); } } return s; } /** * remove trailing blanks * * @param s * Description of the Parameter * @return Description of the Return Value */ public static String rightTrim(String s) { if (s == null) { return null; } if (s.trim().equals(S_EMPTY)) { return EC.S_EMPTY; } int l = s.length(); while (l >= 0) { if (!Character.isWhitespace(s.charAt(--l))) { l++; break; } } return s.substring(0, l); } /** * remove leading blanks * * @param s * Description of the Parameter * @return Description of the Return Value */ public static String leftTrim(String s) { if (s == null) { return null; } if (s.trim().equals(S_EMPTY)) { return EC.S_EMPTY; } int l = s.length(); for (int i = 0; i < l; i++) { if (s.charAt(i) != ' ') { return s.substring(i); } } return s; } /** * return index of balanced bracket. String MUST start with a generic LH * bracket (e.g. '{', '{@literal <}', '(' '[') * * @param lbrack * starting character * @param s * string to search * @return index of bracket or -1 if none */ public static int indexOfBalancedBracket(char lbrack, String s) { if (s == null) { return -1; } if (s.charAt(0) != lbrack) { return -1; } char rbrack = ' '; if (lbrack == '(') { rbrack = ')'; } else if (lbrack == '<') { rbrack = '>'; } else if (lbrack == '[') { rbrack = ']'; } else if (lbrack == '{') { rbrack = '}'; } int l = s.length(); int i = 0; int level = 0; while (i < l) { if (s.charAt(i) == lbrack) { level++; } else if (s.charAt(i) == rbrack) { level--; if (level == 0) { return i; } } i++; } return -1; } /** * parse comma-separated Strings Note fields can be EC.S_EMPTY (as in ,,,) and * fields can be quoted "...". If so, embedded quotes are represented as * EC.S_EMPTY, for example A," this is a EC.S_EMPTYBS_EMPTY character",C. An * unbalanced quote returns a mess * * @param s * Description of the Parameter * @return List the vector of Strings - any error returns null * @exception RuntimeException * missing quote */ public static List getCommaSeparatedStrings(String s) throws RuntimeException { if (s == null) { return null; } String s0 = s; s = s.trim(); List v = new ArrayList(); while (!s.equals(S_EMPTY)) { if (s.startsWith(S_QUOT)) { String temp = EC.S_EMPTY; s = s.substring(1); while (true) { int idx = s.indexOf(S_QUOT); if (idx == -1) { throw new RuntimeException("Missing Quote:" + s0 + EC.S_COLON); } int idx2 = s.indexOf(S_QUOT + EC.S_QUOT); // next quote is actually EC.S_EMPTY if (idx2 == idx) { temp += s.substring(0, idx) + EC.S_QUOT; s = s.substring(idx + 2); // single quote } else { temp += s.substring(0, idx); s = s.substring(idx + 1); break; } } v.add(temp); if (s.startsWith(S_COMMA)) { s = s.substring(1); } else if (s.equals(S_EMPTY)) { } else { throw new RuntimeException("Unbalanced Quotes:" + s0 + EC.S_COLON); } } else { int idx = s.indexOf(S_COMMA); // end? if (idx == -1) { v.add(s); break; } else { // another comma String temp = s.substring(0, idx); v.add(temp); s = s.substring(idx + 1); if (s.equals(S_EMPTY)) { v.add(s); break; } } } } return v; } /** * create comma-separated Strings fields include a comma or a " they are * wrapped with quotes ("). Note fields can be EC.S_EMPTY (as in ,,,) and * fields can be quoted "...". If so, embedded quotes are represented as * EC.S_EMPTY, for example A," this is a EC.S_EMPTYBS_EMPTY character",C. * * @param v * vector of strings to be concatenated (null returns null) * @return String the concatenated string - any error returns null */ public static String createCommaSeparatedStrings(List v) { if (v == null) { return null; } StringBuffer sb = new StringBuffer(); for (int i = 0; i < v.size(); i++) { String s = v.get(i).toString(); s = Util.substituteStrings(s, new String[] { EC.S_QUOT }, new String[] { EC.S_QUOT + EC.S_QUOT }); // wrap in quotes to escape comma or other quotes if (s.indexOf(S_COMMA) != -1 || s.indexOf(S_QUOT) != -1) { s = EC.S_QUOT + s + EC.S_QUOT; } if (i > 0) { sb.append(S_COMMA); } sb.append(s); } return sb.toString(); } /** * concatenate strings into quote-separated string * * @param s * Description of the Parameter * @return String concatenated string */ public static String quoteConcatenate(String[] s) { StringBuffer sb = new StringBuffer(); for (int i = 0; i < s.length; i++) { if (i > 0) { sb.append(S_SPACE); } boolean quote = false; if (s[i].indexOf(S_SPACE) != -1) { sb.append(S_QUOT); quote = true; } sb.append(s[i]); if (quote) { sb.append(S_QUOT); } } return sb.toString(); } /** * get the index of a String in an array * * @param string * Description of the Parameter * @param strings * Description of the Parameter * @param ignoreCase * ignore case * @return index of string else -1 if not found */ public static int indexOf(String string, String[] strings, boolean ignoreCase) { if (string == null || strings == null) { return -1; } for (int i = 0; i < strings.length; i++) { if (ignoreCase) { if (string.equalsIgnoreCase(strings[i])) { return i; } } else { if (string.equals(strings[i])) { return i; } } } return -1; } /** * remove balanced (well-formed) markup from a string. Crude (that is not * fully XML-compliant); * * Example: * * "This is <A * HREF="foo">bar</A> and </BR> a break" goes to "This is bar * and a break" * * * @param s * Description of the Parameter * @return Description of the Return Value */ public static String removeHTML(String s) { StringBuilder sb = new StringBuilder(); while (true) { int idx = s.indexOf("<"); if (idx == -1) { sb.append(s); break; } else { sb.append(s.substring(0, idx)); s = s.substring(idx); idx = s.indexOf('>'); if (idx == -1) { throw new RuntimeException("missing >"); } else { s = s.substring(idx + 1); } } } return sb.toString(); } /** * * Warning message - nothing fancy at present * * * * @param s * Description of the Parameter * */ public static void warning(String s) { LOG.info("WARNING: " + s); } /** * * message - nothing fancy at present * * * * @param s * Description of the Parameter * */ public static void message(String s) { LOG.info(s); } // static jumbo.xml.gui.XText errorText; /** * * Error message - nothing fancy at present. Display in Text frame * * * * @param s * Description of the Parameter * */ public static void error(String s) { // if (errorText == null) { // errorText = new jumbo.xml.gui.XText(); // errorText.displayInFrame(); // } LOG.info("ERROR: " + s); // errorText.addText(s); } /** * * record that we have hit a program bug!!! * * * * @param s * Description of the Parameter * */ // public static void bug(String s) { // bug(new Exception(s)); // } /** * traps a bug. use for programming errors where this could can "never be * reached" concatenates msg with "BUG" and throws {@link RuntimeException} * * @param msg * @param t */ public static void BUG(String msg, Throwable t) { msg = (msg == null || msg.trim().length() == 0) ? EC.S_EMPTY : EC.S_LBRAK + msg + EC.S_RBRAK; throw new RuntimeException("BUG: " + msg + "should never throw", t); } /** * traps a bug. empty message. * * @see #BUG(String, Throwable) * @param t */ public static void BUG(Throwable t) { BUG(S_EMPTY, t); } /** file separator. */ final static String FS = System.getProperty("file.separator"); /** * * create new file, including making directory if required This seems to be * * a mess - f.createNewFile() doesn't seem to work A directory should have * * a trailing file.separator * * * * @param fileName * Description of the Parameter * * @return Description of the Return Value * * @exception IOException * Description of the Exception * */ // public static File createNewFile(String fileName) throws IOException { // File f = null; // String path = null; // int idx = fileName.lastIndexOf(FS); // if (idx != -1) { // path = fileName.substring(0, idx); // // fileN = fileName.substring(idx+1); // } // // try { // if (path != null) { // f = new File(path); // f.mkdirs(); // } // if (!fileName.endsWith(FS)) { // f = new File(fileName); // } // // } catch (IOException e) { // // logger.info("Failed to create: "+fileName+S_LBRAK+e+S_RBRAK); // // } // return f; // } /** * get current directory * * @return The pWDName value */ public static String getPWDName() { File f = new File(S_PERIOD); return new File(f.getAbsolutePath()).getParent(); } /** * create new file, including making directory if required This seems to be * a mess - f.createNewFile() doesn't seem to work A directory should have a * trailing file.separator * * @param fileName * @return file * * @exception IOException */ public static File createNewFile(String fileName) throws IOException { File f = null; String path = null; int idx = fileName.lastIndexOf(FS); if (idx != -1) { path = fileName.substring(0, idx); } if (path != null) { f = new File(path); f.mkdirs(); } if (!fileName.endsWith(FS)) { f = new File(fileName); } return f; } /** * * make substitutions in a string. If oldSubtrings = "A" and newSubstrings = * "aa" then count occurrences of "A" in s are replaced with "aa", etc. * * "AAA" count=2 would be replaced by "aaaaA" * * @param s * @param oldSubstring * @param newSubstring * @param count * @return new string * */ public static String substituteString(String s, String oldSubstring, String newSubstring, int count) { if (count <= 0) { count = Integer.MAX_VALUE; } StringBuffer sb = new StringBuffer(); int lo = oldSubstring.length(); for (int i = 0; i < count; i++) { int idx = s.indexOf(oldSubstring); if (idx == -1) { break; } sb.append(s.substring(0, idx)); sb.append(newSubstring); s = s.substring(idx + lo); } sb.append(s); return sb.toString(); } /** * make substitutions in a string. If oldSubtrings = {"A", "BB", "C"} and * newSubstrings = {"aa", "b", "zz"} then every occurrence of "A" in s is * * replaced with "aa", etc. "BBB" would be replaced by "bB" * * @param s * @param oldSubstrings * @param newSubstrings * * @return Description of the Return Value * @throws RuntimeException */ public static String substituteStrings(String s, String[] oldSubstrings, String[] newSubstrings) { int ol = oldSubstrings.length; int nl = newSubstrings.length; if (ol != nl) { throw new RuntimeException( "Util.substituteStrings arguments of different lengths: " + ol + EC.S_SLASH + nl); } for (int i = 0; i < ol; i++) { String oldS = oldSubstrings[i]; String newS = newSubstrings[i]; int lo = oldS.length(); if (s.indexOf(oldS) == -1) { continue; } String ss = EC.S_EMPTY; while (true) { int idx = s.indexOf(oldS); if (idx == -1) { ss += s; break; } ss += s.substring(0, idx) + newS; s = s.substring(idx + lo); } s = ss; } return s; } /** * * substitute characters with =Hex values. Thus "=2E" is translated to * * char(46); A trailing EQUALS (continuation line is not affected, nor is * * any non-hex value * * * */ static String[] dosEquivalents = { EC.S_EMPTY + (char) 12, // ?? S_EMPTY + (char) 127, // ?? S_EMPTY + (char) 128, // Ccedil S_EMPTY + (char) 129, // uuml S_EMPTY + (char) 130, // eacute S_EMPTY + (char) 131, // acirc S_EMPTY + (char) 132, // auml S_EMPTY + (char) 133, // agrave S_EMPTY + (char) 134, // aring S_EMPTY + (char) 135, // ccedil S_EMPTY + (char) 136, // ecirc S_EMPTY + (char) 137, // euml S_EMPTY + (char) 138, // egrave S_EMPTY + (char) 139, // iuml S_EMPTY + (char) 140, // icirc S_EMPTY + (char) 141, // igrave S_EMPTY + (char) 142, // Auml S_EMPTY + (char) 143, // Aring S_EMPTY + (char) 144, // Eacute S_EMPTY + (char) 145, // aelig S_EMPTY + (char) 146, // ff? S_EMPTY + (char) 147, // ocirc S_EMPTY + (char) 148, // ouml S_EMPTY + (char) 149, // ograve S_EMPTY + (char) 150, // ucirc S_EMPTY + (char) 151, // ugrave S_EMPTY + (char) 152, // yuml S_EMPTY + (char) 153, // Ouml S_EMPTY + (char) 154, // Uuml S_EMPTY + (char) 155, // ?? S_EMPTY + (char) 156, // ?? S_EMPTY + (char) 157, // ?? S_EMPTY + (char) 158, // ?? S_EMPTY + (char) 159, // ?? S_EMPTY + (char) 160, // aacute S_EMPTY + (char) 161, // iacute S_EMPTY + (char) 162, // oacute S_EMPTY + (char) 163, // uacute S_EMPTY + (char) 164, // nwave? S_EMPTY + (char) 165, // Nwave? S_EMPTY + (char) 166, // ?? S_EMPTY + (char) 167, // ?? S_EMPTY + (char) 168, // ?? S_EMPTY + (char) 169, // ?? S_EMPTY + (char) 170, // 170 S_EMPTY + (char) 171, // ?? S_EMPTY + (char) 172, // ?? S_EMPTY + (char) 173, // ?? S_EMPTY + (char) 174, // ?? S_EMPTY + (char) 175, // ?? S_EMPTY + (char) 176, // ?? S_EMPTY + (char) 177, // ?? S_EMPTY + (char) 178, // ?? S_EMPTY + (char) 179, // ?? S_EMPTY + (char) 180, // 180 // S_EMPTY + (char) 192, // eacute S_EMPTY + (char) 248, // degrees S_EMPTY + (char) 352, // egrave S_EMPTY + (char) 402, // acirc S_EMPTY + (char) 710, // ecirc S_EMPTY + (char) 8218, // eacute S_EMPTY + (char) 8221, // ouml S_EMPTY + (char) 8222, // auml S_EMPTY + (char) 8225, // ccedil S_EMPTY + (char) 8230, // agrave S_EMPTY + (char) 8240, // euml S_EMPTY + (char) 65533, // uuml }; static String[] asciiEquivalents = { EC.S_EMPTY, // ?? S_EMPTY + (char) 0, // ?? S_EMPTY + (char) 199, // Ccedil S_EMPTY + (char) 252, // uuml S_EMPTY + (char) 233, // eacute S_EMPTY + (char) 226, // acirc S_EMPTY + (char) 228, // auml S_EMPTY + (char) 224, // agrave S_EMPTY + (char) 229, // aring S_EMPTY + (char) 231, // ccedil S_EMPTY + (char) 234, // ecirc S_EMPTY + (char) 235, // euml S_EMPTY + (char) 232, // egrave S_EMPTY + (char) 239, // iuml S_EMPTY + (char) 238, // icirc S_EMPTY + (char) 236, // igrave S_EMPTY + (char) 196, // Auml S_EMPTY + (char) 197, // Aring S_EMPTY + (char) 201, // Eacute S_EMPTY + (char) 230, // aelig S_EMPTY + (char) 0, // ff? S_EMPTY + (char) 244, // ocirc S_EMPTY + (char) 246, // ouml S_EMPTY + (char) 242, // ograve S_EMPTY + (char) 251, // ucirc S_EMPTY + (char) 249, // ugrave S_EMPTY + (char) 255, // yuml S_EMPTY + (char) 214, // Ouml S_EMPTY + (char) 220, // Uuml S_EMPTY + (char) 0, // ff? S_EMPTY + (char) 0, // ff? S_EMPTY + (char) 0, // ff? S_EMPTY + (char) 0, // ff? S_EMPTY + (char) 0, // ff? S_EMPTY + (char) 225, // aacute S_EMPTY + (char) 237, // iacute S_EMPTY + (char) 243, // oacute S_EMPTY + (char) 250, // uacute S_EMPTY + (char) 241, // nwave? S_EMPTY + (char) 209, // Nwave? S_EMPTY + (char) 0, // ?? S_EMPTY + (char) 0, // ?? S_EMPTY + (char) 0, // ?? S_EMPTY + (char) 0, // ?? S_EMPTY + (char) 0, // 170 S_EMPTY + (char) 0, // ?? S_EMPTY + (char) 0, // ?? S_EMPTY + (char) 0, // ?? S_EMPTY + (char) 0, // ?? S_EMPTY + (char) 0, // ?? S_EMPTY + (char) 0, // ?? S_EMPTY + (char) 0, // ?? S_EMPTY + (char) 0, // ?? S_EMPTY + (char) 0, // ?? S_EMPTY + (char) 0, // 180 // S_EMPTY + (char) 233, // eacute "[degrees]", // degrees S_EMPTY + (char) 232, // egrave S_EMPTY + (char) 226, // acirc S_EMPTY + (char) 234, // ecirc S_EMPTY + (char) 233, // eacute S_EMPTY + (char) 246, // ouml S_EMPTY + (char) 228, // auml S_EMPTY + (char) 231, // ccedil S_EMPTY + (char) 224, // agrave S_EMPTY + (char) 235, // euml S_EMPTY + (char) 252, // uuml }; /** * isolatin entities */ public final static String[] ISOLATIN_ENTITIES = new String[256]; /** */ public final static int ISOLATIN_LO = 160; /** */ public final static int ISOLATIN_HI = 255; static { // ones before 160 are null ISOLATIN_ENTITIES[160] = "_nbsp_"; ISOLATIN_ENTITIES[161] = "_iexcl_"; ISOLATIN_ENTITIES[162] = "_cent_"; ISOLATIN_ENTITIES[163] = "_pound_"; ISOLATIN_ENTITIES[164] = "_curren_"; ISOLATIN_ENTITIES[165] = "_yen_"; ISOLATIN_ENTITIES[166] = "_brvbar_"; ISOLATIN_ENTITIES[167] = "_sect_"; ISOLATIN_ENTITIES[168] = "_uml_"; ISOLATIN_ENTITIES[169] = "_copy_"; ISOLATIN_ENTITIES[170] = "_ordf_"; ISOLATIN_ENTITIES[171] = "_laquo_"; ISOLATIN_ENTITIES[172] = "_not_"; ISOLATIN_ENTITIES[173] = "_shy_"; ISOLATIN_ENTITIES[174] = "_reg_"; ISOLATIN_ENTITIES[175] = "_macr_"; ISOLATIN_ENTITIES[176] = "_deg_"; ISOLATIN_ENTITIES[177] = "_plusmn_"; ISOLATIN_ENTITIES[178] = "_sup2_"; ISOLATIN_ENTITIES[179] = "_sup3_"; ISOLATIN_ENTITIES[180] = "_acute_"; ISOLATIN_ENTITIES[181] = "_micro_"; ISOLATIN_ENTITIES[182] = "_para_"; ISOLATIN_ENTITIES[183] = "_middot_"; ISOLATIN_ENTITIES[184] = "_cedil_"; ISOLATIN_ENTITIES[185] = "_sup1_"; ISOLATIN_ENTITIES[186] = "_ordm_"; ISOLATIN_ENTITIES[187] = "_raquo_"; ISOLATIN_ENTITIES[188] = "_frac14_"; ISOLATIN_ENTITIES[189] = "_frac12_"; ISOLATIN_ENTITIES[190] = "_frac34_"; ISOLATIN_ENTITIES[191] = "_iquest_"; ISOLATIN_ENTITIES[192] = "_Agrave_"; ISOLATIN_ENTITIES[193] = "_Aacute_"; ISOLATIN_ENTITIES[194] = "_Acirc_"; ISOLATIN_ENTITIES[195] = "_Atilde_"; ISOLATIN_ENTITIES[196] = "_Auml_"; ISOLATIN_ENTITIES[197] = "_Aring_"; ISOLATIN_ENTITIES[198] = "_AElig_"; ISOLATIN_ENTITIES[199] = "_Ccedil_"; ISOLATIN_ENTITIES[200] = "_Egrave_"; ISOLATIN_ENTITIES[201] = "_Eacute_"; ISOLATIN_ENTITIES[202] = "_Ecirc_"; ISOLATIN_ENTITIES[203] = "_Euml_"; ISOLATIN_ENTITIES[204] = "_Igrave_"; ISOLATIN_ENTITIES[205] = "_Iacute_"; ISOLATIN_ENTITIES[206] = "_Icirc_"; ISOLATIN_ENTITIES[207] = "_Iuml_"; ISOLATIN_ENTITIES[208] = "_ETH_"; ISOLATIN_ENTITIES[209] = "_Ntilde_"; ISOLATIN_ENTITIES[210] = "_Ograve_"; ISOLATIN_ENTITIES[211] = "_Oacute_"; ISOLATIN_ENTITIES[212] = "_Ocirc_"; ISOLATIN_ENTITIES[213] = "_Otilde_"; ISOLATIN_ENTITIES[214] = "_Ouml_"; ISOLATIN_ENTITIES[215] = "_times_"; ISOLATIN_ENTITIES[216] = "_Oslash_"; ISOLATIN_ENTITIES[217] = "_Ugrave_"; ISOLATIN_ENTITIES[218] = "_Uacute_"; ISOLATIN_ENTITIES[219] = "_Ucirc_"; ISOLATIN_ENTITIES[220] = "_Uuml_"; ISOLATIN_ENTITIES[221] = "_Yacute_"; ISOLATIN_ENTITIES[222] = "_THORN_"; ISOLATIN_ENTITIES[223] = "_szlig_"; ISOLATIN_ENTITIES[224] = "_agrave_"; ISOLATIN_ENTITIES[225] = "_aacute_"; ISOLATIN_ENTITIES[226] = "_acirc_"; ISOLATIN_ENTITIES[227] = "_atilde_"; ISOLATIN_ENTITIES[228] = "_auml_"; ISOLATIN_ENTITIES[229] = "_aring_"; ISOLATIN_ENTITIES[230] = "_aelig_"; ISOLATIN_ENTITIES[231] = "_ccedil_"; ISOLATIN_ENTITIES[232] = "_egrave_"; ISOLATIN_ENTITIES[233] = "_eacute_"; ISOLATIN_ENTITIES[234] = "_ecirc_"; ISOLATIN_ENTITIES[235] = "_euml_"; ISOLATIN_ENTITIES[236] = "_igrave_"; ISOLATIN_ENTITIES[237] = "_iacute_"; ISOLATIN_ENTITIES[238] = "_icirc_"; ISOLATIN_ENTITIES[239] = "_iuml_"; ISOLATIN_ENTITIES[240] = "_eth_"; ISOLATIN_ENTITIES[241] = "_ntilde_"; ISOLATIN_ENTITIES[242] = "_ograve_"; ISOLATIN_ENTITIES[243] = "_oacute_"; ISOLATIN_ENTITIES[244] = "_ocirc_"; ISOLATIN_ENTITIES[245] = "_otilde_"; ISOLATIN_ENTITIES[246] = "_ouml_"; ISOLATIN_ENTITIES[247] = "_divide_"; ISOLATIN_ENTITIES[248] = "_oslash_"; ISOLATIN_ENTITIES[249] = "_ugrave_"; ISOLATIN_ENTITIES[250] = "_uacute_"; ISOLATIN_ENTITIES[251] = "_ucirc_"; ISOLATIN_ENTITIES[252] = "_uuml_"; ISOLATIN_ENTITIES[253] = "_yacute_"; ISOLATIN_ENTITIES[254] = "_thorn_"; ISOLATIN_ENTITIES[255] = "_yuml_"; } /** * replaces entities in string by ISOLatin mnemonics e.g. replaces ± by * _plusmn_ sometimes find strings of form ± which actually mean * ± the leading string ent should be of form Â&#, etc. &# will do * simple entities * * @param s * to be edited * @param ent * leading string before numeric * @param lo * lowest index allowed * @param hi * highest index allowed * @param chars * list of characters * @return edited string * @throws RuntimeException */ private static String replaceNumericEntityByMnemonic(String s, String ent, int lo, int hi, String[] chars) throws RuntimeException { if (ent == null || !ent.endsWith(S_AMP + EC.S_HASH)) { throw new RuntimeException("bad entity: " + ent); } int idx = s.indexOf(ent); if (idx != -1) { // String sub = ""; while (true) { idx = s.indexOf(ent); if (idx == -1) { break; } String ss = s.substring(idx + ent.length()); int ii = ss.indexOf(S_SEMICOLON); if (ii == -1) { throw new RuntimeException("Bad entity after (" + ent + "): " + s); } String alpha = "_unk_"; String sss = ss.substring(0, ii); try { int ia = Integer.parseInt(sss); // ascii if (ia >= 32 && ia <= 127) { alpha = EC.S_EMPTY + (char) ia; } else if (ia < lo || ia > hi) { alpha = EC.S_UNDER + "ent" + ia + EC.S_UNDER; } else { alpha = EC.S_UNDER + chars[ia] + EC.S_UNDER; } } catch (NumberFormatException e) { throw new RuntimeException("Bad numeric entity: " + sss); } s = s.replace(ent + sss + EC.S_SEMICOLON, alpha); } } return s; } /** * @param s * string to be edited * @param ent * leading entity * @return edited string */ public static String replaceNumericEntityByISOLatinString(String s, String ent) { return replaceNumericEntityByMnemonic(s, ent, 160, 255, ISOLATIN_ENTITIES); } /** greek entities */ public final static String[] GREEK_ENTITIES = new String[200]; static { GREEK_ENTITIES[145] = "Alpha"; GREEK_ENTITIES[146] = "Beta"; GREEK_ENTITIES[147] = "Gamma"; GREEK_ENTITIES[148] = "Delta"; GREEK_ENTITIES[149] = "Epsilon"; GREEK_ENTITIES[150] = "Zeta"; GREEK_ENTITIES[151] = "Eta"; GREEK_ENTITIES[152] = "Theta"; GREEK_ENTITIES[153] = "Iota"; GREEK_ENTITIES[154] = "Kappa"; GREEK_ENTITIES[155] = "Lambda"; GREEK_ENTITIES[156] = "Mu"; GREEK_ENTITIES[157] = "Nu"; GREEK_ENTITIES[158] = "Omicron"; GREEK_ENTITIES[159] = "Pi"; GREEK_ENTITIES[160] = "Rho"; GREEK_ENTITIES[161] = "Sigma"; GREEK_ENTITIES[162] = "Tau"; GREEK_ENTITIES[163] = "Upsilon"; GREEK_ENTITIES[164] = "Phi"; GREEK_ENTITIES[165] = "Phi"; GREEK_ENTITIES[166] = "Psi"; GREEK_ENTITIES[167] = "Omega"; GREEK_ENTITIES[177] = "alpha"; GREEK_ENTITIES[178] = "beta"; GREEK_ENTITIES[179] = "gamma"; GREEK_ENTITIES[180] = "delta"; GREEK_ENTITIES[181] = "epsilon"; GREEK_ENTITIES[182] = "zeta"; GREEK_ENTITIES[183] = "eta"; GREEK_ENTITIES[184] = "theta"; GREEK_ENTITIES[185] = "iota"; GREEK_ENTITIES[186] = "kappa"; GREEK_ENTITIES[187] = "lambda"; GREEK_ENTITIES[188] = "mu"; GREEK_ENTITIES[189] = "nu"; GREEK_ENTITIES[190] = "omicron"; GREEK_ENTITIES[191] = "pi"; GREEK_ENTITIES[192] = "rho"; GREEK_ENTITIES[193] = "sigma"; GREEK_ENTITIES[194] = "tau"; GREEK_ENTITIES[195] = "upsilon"; GREEK_ENTITIES[196] = "phi"; GREEK_ENTITIES[197] = "chi"; GREEK_ENTITIES[198] = "psi"; GREEK_ENTITIES[199] = "omega"; }; /** UPPER_GREEK entities */ public final static String[] UPPER_GREEK_ENTITIES = new String[968]; public final static Map GREEK2CHARACTER_MAP; static { UPPER_GREEK_ENTITIES[912] = "Alpha"; UPPER_GREEK_ENTITIES[914] = "Beta"; UPPER_GREEK_ENTITIES[915] = "Gamma"; UPPER_GREEK_ENTITIES[916] = "Delta"; UPPER_GREEK_ENTITIES[917] = "Epsilon"; UPPER_GREEK_ENTITIES[918] = "Zeta"; UPPER_GREEK_ENTITIES[919] = "Eta"; UPPER_GREEK_ENTITIES[920] = "Theta"; UPPER_GREEK_ENTITIES[921] = "Iota"; UPPER_GREEK_ENTITIES[922] = "Kappa"; UPPER_GREEK_ENTITIES[923] = "Lambda"; UPPER_GREEK_ENTITIES[924] = "Mu"; UPPER_GREEK_ENTITIES[925] = "Nu"; UPPER_GREEK_ENTITIES[926] = "Omicron"; UPPER_GREEK_ENTITIES[927] = "Pi"; UPPER_GREEK_ENTITIES[928] = "Rho"; UPPER_GREEK_ENTITIES[929] = "Sigma"; UPPER_GREEK_ENTITIES[930] = "Tau"; UPPER_GREEK_ENTITIES[931] = "Upsilon"; UPPER_GREEK_ENTITIES[932] = "Phi"; UPPER_GREEK_ENTITIES[933] = "Phi"; UPPER_GREEK_ENTITIES[934] = "Psi"; UPPER_GREEK_ENTITIES[935] = "Omega"; UPPER_GREEK_ENTITIES[945] = "alpha"; UPPER_GREEK_ENTITIES[946] = "beta"; UPPER_GREEK_ENTITIES[947] = "gamma"; UPPER_GREEK_ENTITIES[948] = "delta"; UPPER_GREEK_ENTITIES[949] = "epsilon"; UPPER_GREEK_ENTITIES[950] = "zeta"; UPPER_GREEK_ENTITIES[951] = "eta"; UPPER_GREEK_ENTITIES[952] = "theta"; UPPER_GREEK_ENTITIES[953] = "iota"; UPPER_GREEK_ENTITIES[954] = "kappa"; UPPER_GREEK_ENTITIES[955] = "lambda"; UPPER_GREEK_ENTITIES[956] = "mu"; UPPER_GREEK_ENTITIES[957] = "nu"; UPPER_GREEK_ENTITIES[958] = "omicron"; UPPER_GREEK_ENTITIES[959] = "pi"; UPPER_GREEK_ENTITIES[960] = "rho"; UPPER_GREEK_ENTITIES[961] = "sigma"; UPPER_GREEK_ENTITIES[962] = "tau"; UPPER_GREEK_ENTITIES[963] = "upsilon"; UPPER_GREEK_ENTITIES[964] = "phi"; UPPER_GREEK_ENTITIES[965] = "chi"; UPPER_GREEK_ENTITIES[966] = "psi"; UPPER_GREEK_ENTITIES[967] = "omega"; GREEK2CHARACTER_MAP = new HashMap(); GREEK2CHARACTER_MAP.put("Alpha", (char)912); GREEK2CHARACTER_MAP.put("Beta", (char)914); GREEK2CHARACTER_MAP.put("Gamma", (char)915); GREEK2CHARACTER_MAP.put("Delta", (char)916); GREEK2CHARACTER_MAP.put("Epsilon", (char)917); GREEK2CHARACTER_MAP.put("Zeta", (char)918); GREEK2CHARACTER_MAP.put("Eta", (char)919); GREEK2CHARACTER_MAP.put("Theta", (char)920); GREEK2CHARACTER_MAP.put("Iota", (char)921); GREEK2CHARACTER_MAP.put("Kappa", (char)922); GREEK2CHARACTER_MAP.put("Lambda", (char)923); GREEK2CHARACTER_MAP.put("Mu", (char)924); GREEK2CHARACTER_MAP.put("Nu", (char)925); GREEK2CHARACTER_MAP.put("Omicron", (char)926); GREEK2CHARACTER_MAP.put("Pi", (char)927); GREEK2CHARACTER_MAP.put("Rho", (char)928); GREEK2CHARACTER_MAP.put("Sigma", (char)929); GREEK2CHARACTER_MAP.put("Tau", (char)930); GREEK2CHARACTER_MAP.put("Upsilon", (char)931); GREEK2CHARACTER_MAP.put("Phi", (char)932); GREEK2CHARACTER_MAP.put("Phi", (char)933); GREEK2CHARACTER_MAP.put("Psi", (char)934); GREEK2CHARACTER_MAP.put("Omega", (char)935); GREEK2CHARACTER_MAP.put("alpha", (char)945); GREEK2CHARACTER_MAP.put("beta", (char)946); GREEK2CHARACTER_MAP.put("gamma", (char)947); GREEK2CHARACTER_MAP.put("delta", (char)948); GREEK2CHARACTER_MAP.put("epsilon", (char)949); GREEK2CHARACTER_MAP.put("zeta", (char)950); GREEK2CHARACTER_MAP.put("eta", (char)951); GREEK2CHARACTER_MAP.put("theta", (char)952); GREEK2CHARACTER_MAP.put("iota", (char)953); GREEK2CHARACTER_MAP.put("kappa", (char)954); GREEK2CHARACTER_MAP.put("lambda", (char)955); GREEK2CHARACTER_MAP.put("mu", (char)956); GREEK2CHARACTER_MAP.put("nu", (char)957); GREEK2CHARACTER_MAP.put("omicron", (char)958); GREEK2CHARACTER_MAP.put("pi", (char)959); GREEK2CHARACTER_MAP.put("rho", (char)960); GREEK2CHARACTER_MAP.put("sigma", (char)961); GREEK2CHARACTER_MAP.put("tau", (char)962); GREEK2CHARACTER_MAP.put("upsilon", (char)963); GREEK2CHARACTER_MAP.put("phi", (char)964); GREEK2CHARACTER_MAP.put("chi", (char)965); GREEK2CHARACTER_MAP.put("psi", (char)966); GREEK2CHARACTER_MAP.put("omega", (char)967); }; /** * remove all control (non-printing) characters * * @param s * Description of the Parameter * @return Description of the Return Value */ public static String replaceISOControlsByMnemonic(String s) { if (s == null) { return null; } StringBuffer sb = new StringBuffer(); int l = s.length(); for (int i = 0; i < l; i++) { char ch = s.charAt(i); if (Character.isISOControl(ch)) { sb.append(translateToMnemonic(ch)); } else { sb.append(ch); } } return sb.toString(); } /** * translate non-printing character to ISO mnemonic. e.g. 1 ={@literal >} SOH * * @param ch * [0 - 31] or 127 * @return translation or empty string if out of range */ public static String translateToMnemonic(char ch) { switch (ch) { case 0: return "NUL"; case 1: return "SOH"; case 2: return "STX"; case 3: return "ETX"; case 4: return "EOT"; case 5: return "ENQ"; case 6: return "ACK"; case 7: return "BEL"; case 8: return "BS"; case 9: return "HT"; case 10: return "LF"; case 11: return "VT"; case 12: return "FF"; case 13: return "CR"; case 14: return "SO"; case 15: return "SI"; case 16: return "DLE"; case 17: return "DC1"; case 18: return "DC2"; case 19: return "DC3"; case 20: return "DC4"; case 21: return "NAK"; case 22: return "SYN"; case 23: return "ETB"; case 24: return "CAN"; case 25: return "EM"; case 26: return "SUB"; case 27: return "ESC"; case 28: return "FS"; case 29: return "GS"; case 30: return "RS"; case 31: return "US"; case 127: return "DEL"; default: return ""; } } /** * convert 2 UTF8 characters to single IsoLatin1 character. quick and dirty * UTF8 C2 80 ={@literal >} 80 (etc) UTF8 C3 80 ={@literal >} C0 (i.e. add x40) user has * responsibility for selecting characters * * @param a * leading character (ignored if not C2 and not C3) * @param b * range 80-bf * @return Isolatin equiv or 0 if a,b ignored or bad range */ public static char convertUTF8ToLatin1(char a, char b) { char c = 0; if (b >= 128 && b < 192) { if (a == (char) 194) { c = b; } else if (a == (char) 195) { c = (char) ((int) b + 64); } } return c; } /** * convert single IsoLatin1 character to 2 UTF8 characters . quick and dirty * user has responsibility for selecting characters a {@literal >}= x80 AND a {@literal <}= xBF =={@literal >} * xC2 a a {@literal >}= xC0 AND a {@literal <}= xFF =={@literal >} xC3 a - x40 * * @param a * char to be converted (a {@literal >}= x80 AND a {@literal <}xff) * @return 2 characters or null */ public static char[] convertLatin1ToUTF8(char a) { char[] c = null; if (a >= 128 && a < 192) { c = new char[2]; c[0] = (char) 194; c[1] = a; } else if (a >= 192 && a < 256) { c = new char[2]; c[0] = (char) 195; c[1] = (char) (a - 64); } return c; } /** * @param s * string to be edited * @param ent * leading entity * @return edited string */ public static String replaceNumericEntityByGreekMnemonics(String s, String ent) { return replaceNumericEntityByMnemonic(s, ent, 145, 199, GREEK_ENTITIES); } /** * substitute certain DOS-compatible diacriticals by the Unicode value. Not * guaranteed to be correct. Example 130 is e-acute (== * * @param s * Description of the Parameter * @return Description of the Return Value */ public static String substituteDOSbyAscii(String s) { // check for untranslated chars for (int i = 0; i < s.length(); i++) { int jj = (int) s.charAt(i); if (jj > 180) { boolean ok = false; for (int j = 0; j < dosEquivalents.length; j++) { if (dosEquivalents[j].equals(S_EMPTY + s.charAt(i))) { ok = true; break; } } if (!ok) { LOG.error("==Unknown DOS character==" + jj + "//" + s); } } } String s1 = substituteStrings(s, dosEquivalents, asciiEquivalents); return s1; } public static String substituteNonASCIIChars(String s, char replacement) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < s.length(); i++) { int c = s.charAt(i); c = (c > 256) ? (int) replacement : c; sb.append((char)c); } return sb.toString(); } /* ‌ ‌ Zero Width Non Joiner ‍ ‍ Zero Width Joiner ‎ ‎ Left-Right Mark ‏ ‏ Right-Left Mark – – en dash — — em dash ‘ ‘ left single quotation mark ’ ’ right single quotation mark “ “ left double quotation mark ” ” right double quotation mark */ public static String substituteSmartCharacters(String s) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < s.length(); i++) { int c = s.charAt(i); if (c == 8204 || c == 160) { c = ' '; } else if (c == 8205) { c = -1; } else if (c == 8211 || c == 8212) { c = '-'; } else if (c == 8216 || c == 8217) { c = '\''; } else if (c == 8220 || c == 8221) { c = '"'; } else if (c > 127) { c = '?'; } if (c > 0) { sb.append((char)c); } } return sb.toString(); } /** * replace tabs with spaces while trying to preserve the formatting * @param s * @param width * @return */ public static String replaceTabs(String s, int width) { StringBuilder sb = new StringBuilder(); int in = 0; int out = 0; for (; in < s.length(); in++) { char c = s.charAt(in); if (c == EuclidConstants.C_TAB) { int mod = width - (out % width); for (int i = 0; i < mod; i++) { sb.append(EuclidConstants.C_SPACE); out++; } } else { sb.append(c); out++; } } return sb.toString(); } /** * substitute hex representation of character, for example =2E by char(46). * If line ends with =, ignore that character. * * @param s * Description of the Parameter * @return String result */ public static String substituteEquals(String s) { if (s == null) { return null; } int len = s.length(); StringBuffer sb = new StringBuffer(S_EMPTY); while (true) { int idx = s.indexOf(S_EQUALS); if (idx == -1) { sb.append(s); return sb.toString(); } // remove EQUALS sb.append(s.substring(0, idx)); s = s.substring(idx + 1); len -= idx + 1; // not enough chars if (len <= 1) { sb.append(S_EQUALS); sb.append(s); return sb.toString(); } int hex = getIntFromHex(s.substring(0, 2)); // wasn't a hexchar if (hex < 0) { sb.append(S_EQUALS); } else { sb.append((char) hex); s = s.substring(2); len -= 2; } } } /** * * Translates a Hex number to its int equivalent. Thus "FE" translates to * * 254. Horrid, but I couldn't find if Java reads hex. All results are {@literal >}= * * 0. Errors return -1 * * * * @param hex * Description of the Parameter * * @return The intFromHex value * */ public static int getIntFromHex(String hex) { hex = hex.toUpperCase(); if (hex.startsWith("0X")) { hex = hex.substring(2); } else if (hex.charAt(0) == 'X') { hex = hex.substring(1); } int result = 0; for (int i = 0; i < hex.length(); i++) { char c = hex.charAt(i); if (Character.isDigit(c)) { c -= '0'; } else if (c < 'A' || c > 'F') { return -1; } else { c -= 'A'; c += (char) 10; } result = 16 * result + c; } return result; } /** * capitalise a String (whatever the starting case) * * @param s * Description of the Parameter * @return Description of the Return Value */ public static String capitalise(String s) { if (s.equals(S_EMPTY)) { return EC.S_EMPTY; } if (s.length() == 1) { return s.toUpperCase(); } else { return s.substring(0, 1).toUpperCase() + s.substring(1).toLowerCase(); } } /** * Description of the Method * * @param s * Description of the Parameter * @return Description of the Return Value */ public static String toCamelCase(String s) { StringTokenizer st = new StringTokenizer(s, " \n\r\t"); String out = EC.S_EMPTY; while (st.hasMoreTokens()) { s = st.nextToken(); if (out != EC.S_EMPTY) { s = capitalise(s); } out += s; } return out; } /** * reads a byte array from file, *including* line feeds * * @param filename * Description of the Parameter * @return Description of the Return Value * @exception FileNotFoundException * Description of the Exception * @exception IOException * Description of the Exception */ public static byte[] readByteArray(String filename) throws FileNotFoundException, IOException { DataInputStream dis = new DataInputStream(new FileInputStream(filename)); return Util.readByteArray(dis); } /** * reads a byte array from DataInputStream, *including* line feeds * * @param d * Description of the Parameter * @return Description of the Return Value * @exception IOException * Description of the Exception */ public static byte[] readByteArray(DataInputStream d) throws IOException { int len = 100; int count = 0; byte[] src = new byte[len]; byte b; while (true) { try { b = d.readByte(); } catch (EOFException e) { break; } src[count] = b; if (++count >= len) { len *= 2; byte[] temp = new byte[len]; System.arraycopy(src, 0, temp, 0, count); src = temp; } } len = count; byte[] temp = new byte[len]; System.arraycopy(src, 0, temp, 0, count); return temp; } /** * remove all control (non-printing) characters * * @param s * Description of the Parameter * @return Description of the Return Value */ public static String stripISOControls(String s) { if (s == null) { return null; } StringBuffer sb = new StringBuffer(); int l = s.length(); for (int i = 0; i < l; i++) { char ch = s.charAt(i); if (!Character.isISOControl(ch)) { sb.append(ch); } } return sb.toString(); } /** * normalise whitespace in a String (all whitespace is transformed to single * spaces and the string is NOT trimmed * * @param s * Description of the Parameter * @return Description of the Return Value */ public static String normaliseWhitespace(String s) { if (s == null || s.equals(S_EMPTY)) { return s; } StringTokenizer st = new StringTokenizer(s, Util.WHITESPACE); int l = s.length(); String ss = EC.S_EMPTY; if (Character.isWhitespace(s.charAt(0))) { ss = EC.S_SPACE; } String end = EC.S_EMPTY; if (Character.isWhitespace(s.charAt(l - 1))) { end = EC.S_SPACE; } boolean start = true; while (st.hasMoreTokens()) { if (start) { ss += st.nextToken(); start = false; } else { ss += EC.S_SPACE + st.nextToken(); } } return ss + end; } /** * strip linefeeds from a byte array * * @param b * Description of the Parameter * @return Description of the Return Value */ public static byte[] stripNewlines(byte[] b) { int l = b.length; byte[] bb = new byte[l]; int j = 0; for (int i = 0; i < l; i++) { if (b[i] != '\n') { bb[j++] = b[i]; } } byte[] bbb = new byte[j]; System.arraycopy(bb, 0, bbb, 0, j); return bbb; } /** * get an OutputStream from a file or URL. Required (I think) because * strings of the sort "file:/C:\foo\bat.txt" crash FileOutputStream, so * this strips off the file:/ stuff for Windows-like stuff * * @param fileName * Description of the Parameter * @return FileOutputStream a new (opened) FileOutputStream * @exception java.io.FileNotFoundException * Description of the Exception */ public static FileOutputStream getFileOutputStream(String fileName) throws java.io.FileNotFoundException { if (fileName == null) { return null; } // W-like syntax if (fileName.startsWith("file:") && fileName.substring(5).indexOf(S_COLON) != -1) { fileName = fileName.substring(5); if (fileName.startsWith(S_SLASH) || fileName.startsWith(S_BACKSLASH)) { fileName = fileName.substring(1); } } return new FileOutputStream(fileName); } // cache the formats static Hashtable formTable = new Hashtable(); /** * format for example f8.3 this is a mess; if cannot fit, then either * right-truncates or when that doesn't work, returns **** * * @param nPlaces * Description of the Parameter * @param nDec * Description of the Parameter * @param value * Description of the Parameter * @return Description of the Return Value * @exception EuclidRuntimeException * Description of the Exception */ public static String outputFloat(int nPlaces, int nDec, double value) throws EuclidRuntimeException { String f = "f" + nPlaces + EC.S_PERIOD + nDec; DecimalFormat form = formTable.get(f); if (form == null) { String pattern = EC.S_EMPTY; for (int i = 0; i < nPlaces - nDec - 2; i++) { pattern += "#"; } pattern += "0."; for (int i = nPlaces - nDec; i < nPlaces; i++) { pattern += "0"; } form = (DecimalFormat) NumberFormat.getInstance(); DecimalFormatSymbols symbols = form.getDecimalFormatSymbols(); symbols.setDecimalSeparator('.'); form.setDecimalFormatSymbols(symbols); form.setMaximumIntegerDigits(nPlaces - nDec - 1); // form.applyLocalizedPattern(pattern); form.applyPattern(pattern); formTable.put(f, form); } String result = form.format(value).trim(); boolean negative = false; if (result.charAt(0) == '-') { result = result.substring(1); negative = true; } if (negative) { result = EC.S_MINUS + result; } StringBuffer sb = new StringBuffer(); int l = result.length(); for (int i = 0; i < nPlaces - l; i++) { sb.append(S_SPACE); } String s = sb.append(result).toString(); if (l > nPlaces) { s = s.substring(0, nPlaces); // decimal point got truncated? if (s.indexOf(S_PERIOD) == -1) { s = EC.S_EMPTY; for (int i = 0; i < nPlaces; i++) { s += EC.S_STAR; } } } return s; } /** * as above, but trims trailing zeros * * @param nPlaces * Description of the Parameter * @param nDec * Description of the Parameter * @param c * Description of the Parameter * @return Description of the Return Value */ public static String outputNumber(int nPlaces, int nDec, double c) { String s = Util.outputFloat(nPlaces, nDec, c).trim(); if (s.indexOf(S_PERIOD) != -1) { while (s.endsWith("0")) { s = s.substring(0, s.length() - 1); } if (s.endsWith(S_PERIOD)) { s = s.substring(0, s.length() - 1); } } return s; } /** * invert a Hashtable by interchanging keys and values. This assumes a 1;1 * mapping - if not the result is probably garbage. * * @param table * Description of the Parameter * @return Description of the Return Value */ public static Hashtable invert( Hashtable table) { if (table == null) { return null; } Hashtable newTable = new Hashtable(); for (Enumeration e = table.keys(); e.hasMoreElements();) { Object key = e.nextElement(); Object value = table.get(key); newTable.put(value, key); } return newTable; } /** * checks array is not null and is of given size. * * @param array * to check * @param size * required size * @throws EuclidRuntimeException * if null or wrong size */ public static void check(double[] array, int size) throws EuclidRuntimeException { if (array == null) { throw new EuclidRuntimeException("null array"); } else if (array.length != size) { throw new EuclidRuntimeException("array size required (" + size + ") found " + array.length); } } /** * checks that an in is in the range low to high. * * @param n * to check * @param low * inclusive lower * @param high * inclusive higher * @throws EuclidRuntimeException * if out of range */ public static void check(int n, int low, int high) throws EuclidRuntimeException { if (n < low || n > high) { throw new EuclidRuntimeException("index (" + n + ")out of range: " + low + EC.S_SLASH + high); } } /** * compare two arrays of doubles. * * @param a * first array * @param b * second array * @param eps * maximum allowed difference * @return true if arrays non-null and if arrays are equal length and * corresonding elements agree within eps. */ public static boolean isEqual(double[] a, double[] b, double eps) { boolean equal = (a != null && b != null && a.length == b.length); if (equal) { for (int i = 0; i < a.length; i++) { if (Math.abs(a[i] - b[i]) >= eps) { equal = false; break; } } } return equal; } /** * compare two arrays of ints. * * @param a * first array * @param b * second array * @param eps * maximum allowed difference * @return true if arrays non-null and if arrays are equal length and * corresonding elements agree within eps. */ public static boolean isEqual(int[] a, int[] b, int eps) { boolean equal = (a != null && b != null && a.length == b.length); if (equal) { for (int i = 0; i < a.length; i++) { if (Math.abs(a[i] - b[i]) >= eps) { equal = false; break; } } } return equal; } /** * concatenates array of booleans. * * @param bb * the values * @param separator * @return the String */ public final static String concatenate(boolean[] bb, String separator) { StringBuffer sb = new StringBuffer(); for (int i = 0; i < bb.length; i++) { if (i > 0) { sb.append(separator); } sb.append(bb[i]); } return sb.toString(); } /** * concatenates array of doubles. * * @param ss * the values * @param separator * @return the String */ public final static String concatenate(double[] ss, String separator) { StringBuffer sb = new StringBuffer(); for (int i = 0; i < ss.length; i++) { if (i > 0) { sb.append(separator); } if (Double.isInfinite(ss[i])) { if (ss[i] < 0) { sb.append("-"); } sb.append("INF"); } else if (Double.isNaN(ss[i])) { sb.append("NaN"); } else { sb.append(ss[i]); } } return sb.toString(); } /** * concatenates array of array of doubles. * * @param ss * the values * @param separator * @return the String */ public final static String concatenate(double[][] ss, String separator) { StringBuffer sb = new StringBuffer(); for (int i = 0; i < ss.length; i++) { if (i > 0) { sb.append(separator); } sb.append(Util.concatenate(ss[i], separator)); } return sb.toString(); } /** * splits string into ints. * * @param s * the string * @param delim * @return array * @throws EuclidRuntimeException * cannot parse as ints */ public final static int[] splitToIntArray(String s, String delim) throws EuclidRuntimeException { String[] ss = s.split(delim); int[] ii = new int[ss.length]; for (int i = 0; i < ss.length; i++) { try { ii[i] = Integer.parseInt(ss[i]); } catch (NumberFormatException nfe) { throw new EuclidRuntimeException(S_EMPTY + nfe); } } return ii; } /** * splits string into doubles. assumes single space delimiters * * @param s * the string * @return array * @throws EuclidRuntimeException * cannot parse as ints */ public final static double[] splitToDoubleArray(String s) { return splitToDoubleArray(s, EC.S_SPACE); } /** * Parses double, taking account of lexical forms of special cases allowed * by the XSD spec: INF, -INF and NaN. * * @param value * @return * @throws ParseException */ public static double parseFlexibleDouble(String value) throws ParseException { //LOG.debug("Parsing "+ value); if (value != null) { // 0, -0, INF, -INF and NaN : Special cases from the XSD spec. if ("INF".equals(value)) { return Double.POSITIVE_INFINITY; } else if ("-INF".equals(value)) { return Double.NEGATIVE_INFINITY; } else if ("NaN".equals(value)) { return Double.NaN; } else { try { return Double.valueOf(value); } catch (NumberFormatException e) { throw new ParseException(e.toString(), 0); } } } else { throw new IllegalArgumentException("Null double string not allowed"); } } /** * splits string into doubles. * * @param s * the string * @param delim * @return array * @throws EuclidRuntimeException * cannot parse as ints */ public final static double[] splitToDoubleArray(String s, String delim) throws EuclidRuntimeException { if (s == null) { throw new RuntimeException("null argument"); } String[] ss = s.trim().split(delim); double[] dd = new double[ss.length]; for (int i = 0; i < ss.length; i++) { try { dd[i] = parseFlexibleDouble(ss[i]); } catch (NumberFormatException nfe) { throw new EuclidRuntimeException(S_EMPTY + nfe.getMessage(), nfe); } catch (ParseException e) { throw new EuclidRuntimeException("Bad double in (" + s + ") : " + ss[i] + "at position " + i, e); } } return dd; } /** * concatenates array of ints. * * @param ss * the values * @param separator * @return the String */ public final static String concatenate(int[] ss, String separator) { StringBuffer sb = new StringBuffer(); for (int i = 0; i < ss.length; i++) { if (i > 0) { sb.append(separator); } sb.append(ss[i]); } return sb.toString(); } /** * concatenates array of Strings. * * @param ss * the values * @param separator * @return the String */ public final static String concatenate(String[] ss, String separator) { StringBuffer sb = new StringBuffer(); for (int i = 0; i < ss.length; i++) { if (i > 0) { sb.append(separator); } sb.append(ss[i]); } String s = sb.toString(); if (separator.trim().equals(S_EMPTY)) { s = s.trim(); } return s; } /** * does an array of Strings contain a String. * * @param strings * @param s * string to search for * @return true if any ss[i] == s */ public final static boolean containsString(String[] strings, String s) { boolean b = false; if (s != null && strings != null) { for (int i = 0; i < strings.length; i++) { if (s.equals(strings[i])) { b = true; break; } } } return b; } /** * format trailing decimals * * @param d * value to be formatted * @param ndec * max number of decimals (3 = ****.ddd) * @return the formatted number */ public static double format(double d, int ndec) { int pow = 1; for (int i = 0; i < ndec; i++) { pow *= 10; } return (double) Math.round(d * (double) pow) / (double) pow; } /** * trim trailing zeroes and trailing decimal point. * * @param d * @return trimmed string */ public static String trim(double d) { String s = EC.S_EMPTY + d; int idx = s.lastIndexOf(S_PERIOD); if (idx > 0) { int l = s.length() - 1; while (l > idx) { if (s.charAt(l) != '0') { break; } l--; } if (l == idx) { l--; } l++; s = s.substring(0, l); } return s; } /** * translate array of Strings to a List. * * @param ss * strings (can include null) * @return the list */ public static List createList(String[] ss) { List list = new ArrayList(); for (String s : ss) { list.add(s); } return list; } private static List primeList; /** * get i'th prime. calculates it on demand if not already present and caches * result. * * @param i * @return the primt (starts at 2) */ public static int getPrime(int i) { if (primeList == null) { primeList = new ArrayList(); primeList.add(2); primeList.add(3); primeList.add(5); primeList.add(7); primeList.add(11); } int np = primeList.size(); int p = primeList.get(np - 1).intValue(); while (np <= i) { p = nextPrime(p); primeList.add(p); np++; } return primeList.get(i).intValue(); } private static int nextPrime(int pp) { int p = pp; for (;;) { p = p + 2; if (isPrime(p)) { break; } } return p; } private static boolean isPrime(int p) { boolean prime = true; int sp = (int) Math.sqrt(p) + 1; for (int i = 1; i < primeList.size(); i++) { int pp = primeList.get(i).intValue(); if (p % pp == 0) { prime = false; break; } if (pp > sp) { break; } } return prime; } /** * parse string as integer. * * @param s * @return true if can be parsed. */ public static boolean isInt(String s) { boolean couldBeInt = true; try { Integer.valueOf(s); } catch (NumberFormatException e) { couldBeInt = false; } return couldBeInt; } /** * parse string as integerArray. * * @param s * @param delimiterRegex * @return true if can be parsed. */ public static boolean isIntArray(String s, String delimiterRegex) { boolean couldBeIntArray = true; String[] ss = s.split(delimiterRegex); try { new IntArray(ss); } catch (NumberFormatException e) { couldBeIntArray = false; } return couldBeIntArray; } /** * parse string as realArray. * * @param s * @param delimiterRegex * @return true if can be parsed. */ public static boolean isFloatArray(String s, String delimiterRegex) { boolean couldBeFloatArray = true; String[] ss = s.split(delimiterRegex); try { new RealArray(ss); } catch (Exception e) { couldBeFloatArray = false; } return couldBeFloatArray; } /** * parse string as float. * * @param s * @return true if can be parsed. */ public static boolean isFloat(String s) { boolean couldBeFloat = true; try { Double.valueOf(s); } catch (NumberFormatException e) { couldBeFloat = false; } return couldBeFloat; } /** * date of form 21-jan-1965. */ public final static String DATE_REGEX1 = "([0-3][0-9])\\-(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)\\-(\\d\\d\\d\\d)"; /** * date of form 1965-01-25. */ public final static String DATE_REGEX2 = "\\d\\d\\d\\d\\-[0-1][0-9]\\-[0-3][0-9]"; /** * month data . */ public final static String[] months = { "jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec" }; /** * parse string as date. tries several formats (case insensitive) can be * used to test whether string is parsable as date * * @param s * @return ISO 8601 format if can be parsed or null */ public static String getCanonicalDate(String s) { String dateS = null; Pattern pattern = Pattern .compile(DATE_REGEX1, Pattern.CASE_INSENSITIVE); Matcher matcher = pattern.matcher(s.toLowerCase()); if (matcher.matches()) { int day = Integer.parseInt(matcher.group(1)); String month = matcher.group(2).toLowerCase(); boolean ignoreCase = true; int imonth = Util.indexOf(month, months, ignoreCase); int year = Integer.parseInt(matcher.group(3)); dateS = "" + year + EC.S_MINUS + imonth + EC.S_MINUS + day; } else { pattern = Pattern.compile(DATE_REGEX2, Pattern.CASE_INSENSITIVE); matcher = pattern.matcher(s.toLowerCase()); if (matcher.matches()) { dateS = s; } } return dateS; } public static double getDouble(String s) { double d = Double.NaN; try { d = Double.valueOf(s).doubleValue(); } catch (NumberFormatException nfe) { throw new RuntimeException("Bad double: " + s); } return d; } /** * rounds to decimal place. * * @param dd * number to be rounded * @param ndec * number of places * @return float */ public static double trimFloat(double dd, int ndec) { int trim = 1; ndec = Math.min(ndec, 10); // to avoid problems for (int i = 0; i < ndec; i++) { trim *= 10; } return ((int) (trim * dd)) / (double) trim; } /** * sorts a list of string on integers within them. normal lexical sort will * often produce file1, file10, file11, file2, etc. this will order them by * integers int their * * @param list */ public static void sortByEmbeddedInteger(List list) { StringIntegerComparator fic = new StringIntegerComparator(); Collections.sort(list, fic); } /** outputs to sysout. * primarily to allow trapping and tracing of sysout calls * which we try to avoid anyway * @param s */ public static void print(String s) { SYSOUT.print(s); } /** outputs to sysout. * primarily to allow trapping and tracing of sysout calls * which we try to avoid anyway * @param s */ public static void println(String s) { SYSOUT.println(s); } /** outputs to sysout. * primarily to allow trapping and tracing of sysout calls * which we try to avoid anyway */ public static void println() { SYSOUT.println(); } public static List getRESTQueryAsLines(String s, String u, String mediaType) throws IOException { byte[] content = Util.getRESTQuery(s, u, mediaType); BufferedReader br = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(content))); List lines = new ArrayList(); String line; while ((line = br.readLine()) != null) { lines.add(line); } return lines; } public static byte[] getRESTQuery(String serviceUrl, String urlString, String mediaType) throws IOException { URL url = new URL(urlString+serviceUrl); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.addRequestProperty("accept", mediaType); conn.connect(); InputStream is = conn.getInputStream(); byte[] bytes = IOUtils.toByteArray(is); conn.disconnect(); return bytes; } /** * borrowed from somewhere. * @param s * @return */ public static String calculateMD5(String s) { String md5String = null; if (s != null && s.trim().length() > 0) { StringBuffer hexString = new StringBuffer(); try{ MessageDigest algorithm = MessageDigest.getInstance("MD5"); algorithm.reset(); algorithm.update(s.getBytes()); byte messageDigest[] = algorithm.digest(); for (int i=0; i < messageDigest.length; i++) { hexString.append(Integer.toHexString(0xFF & messageDigest[i])); } } catch(NoSuchAlgorithmException nsae) { } md5String = hexString.toString(); } return md5String; } /** * avoids the checked exception * @param file * @return */ public static String getCanonicalPath(File file) { String path = null; try { File absoluteFile = file.getAbsoluteFile(); path = absoluteFile.getCanonicalPath(); } catch (IOException e) { throw new RuntimeException("cannot canonicalize "+file+" ... "+e.getMessage(), e); } return path; } /** path to create file2 from file1 *@param file1 *@param file2 *@param newSeparator if not null, use as new separator */ public static String getRelativeFilename(File file1, File file2, String newSeparator) { if (newSeparator == null) { newSeparator = File.separator; } String regex = (File.separator.equals("\\")) ? "\\\\" : File.separator; String path = null; try { String path1 = file1.getCanonicalPath(); String path2 = file2.getCanonicalPath(); String[] pathComponent1 = path1.split(regex); String[] pathComponent2 = path2.split(regex); //int minComponents = Math.min(pathComponent1.length, pathComponent2.length); int i = 0; for (; i < pathComponent1.length; i++) { if (!pathComponent2[i].equals(pathComponent1[i])) { break; } } path = ""; for (int j = i; j < pathComponent1.length; j++) { path += ".."+newSeparator; } for (int j = i; j < pathComponent2.length-1; j++) { path += pathComponent2[j]+newSeparator; } path += pathComponent2[pathComponent2.length-1]; } catch (Exception e) { throw new RuntimeException("bad names/BUG", e); // return null } return path; } /** * Get the relative path from one file to another, specifying the directory separator. * If one of the provided resources does not exist, it is assumed to be a file unless it ends with '/' or * '\'. * * from Stackoverflow: * http://stackoverflow.com/questions/204784/how-to-construct-a-relative-path-in-java-from-two-absolute-paths-or-urls * * @param targetPath targetPath is calculated to this file * @param basePath basePath is calculated from this file * @param pathSeparator directory separator. The platform default is not assumed so that we can test Unix behaviour when running on Windows (for example) * @return */ public static String getRelativePath(String targetPath, String basePath, String pathSeparator) { // Normalize the paths String normalizedTargetPath = FilenameUtils.normalizeNoEndSeparator(targetPath); String normalizedBasePath = FilenameUtils.normalizeNoEndSeparator(basePath); // Undo the changes to the separators made by normalization if (pathSeparator.equals("/")) { normalizedTargetPath = FilenameUtils.separatorsToUnix(normalizedTargetPath); normalizedBasePath = FilenameUtils.separatorsToUnix(normalizedBasePath); } else if (pathSeparator.equals("\\")) { normalizedTargetPath = FilenameUtils.separatorsToWindows(normalizedTargetPath); normalizedBasePath = FilenameUtils.separatorsToWindows(normalizedBasePath); } else { throw new IllegalArgumentException("Unrecognised dir separator '" + pathSeparator + "'"); } String[] base = normalizedBasePath.split(Pattern.quote(pathSeparator)); String[] target = normalizedTargetPath.split(Pattern.quote(pathSeparator)); // First get all the common elements. Store them as a string, // and also count how many of them there are. StringBuffer common = new StringBuffer(); int commonIndex = 0; while (commonIndex < target.length && commonIndex < base.length && target[commonIndex].equals(base[commonIndex])) { common.append(target[commonIndex] + pathSeparator); commonIndex++; } if (commonIndex == 0) { // No single common path element. This most // likely indicates differing drive letters, like C: and D:. // These paths cannot be relativized. throw new RuntimeException("No common path element found for '" + normalizedTargetPath + "' and '" + normalizedBasePath + "'"); } // The number of directories we have to backtrack depends on whether the base is a file or a dir // For example, the relative path from // // /foo/bar/baz/gg/ff to /foo/bar/baz // // ".." if ff is a file // "../.." if ff is a directory // // The following is a heuristic to figure out if the base refers to a file or dir. It's not perfect, because // the resource referred to by this path may not actually exist, but it's the best I can do boolean baseIsFile = true; File baseResource = new File(normalizedBasePath); if (baseResource.exists()) { baseIsFile = baseResource.isFile(); } else if (basePath.endsWith(pathSeparator)) { baseIsFile = false; } StringBuffer relative = new StringBuffer(); if (base.length != commonIndex) { int numDirsUp = baseIsFile ? base.length - commonIndex - 1 : base.length - commonIndex; for (int i = 0; i < numDirsUp; i++) { relative.append(".." + pathSeparator); } } relative.append(normalizedTargetPath.substring(common.length())); return relative.toString(); } /** checks that this is local to PMR. * * Stops certain tests being run outside PMR implementation, * especially ones which use directories outside the maven project * * uses System.user.name * * @return */ public static boolean checkPMR() { boolean check = false; if (PM286.equals(System.getProperty("user.name"))) { check = true; } else { LOG.debug("Skipping PMR-only test"); } return check; } } class StringIntegerComparator implements Comparator { public int compare(Object o1, Object o2) { Integer i1 = strip(o1.toString()); Integer i2 = strip(o2.toString()); return i1.compareTo(i2); } private static Integer strip(String s) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < s.length(); i++) { char ch = s.charAt(i); if (ch >= '0' && ch <= '9') { sb.append(ch); } } String ss = sb.toString(); if (ss.length() == 0) { ss = "0"; } return Integer.valueOf(ss); } /** reverses lines in file. * * line(0) and line(n-1) are swapped, etc. * * @param inFilename * @param outFilename * @throws Exception */ public static void reverseLinesInFile(String inFilename, String outFilename) throws Exception { List lines = new ArrayList(); BufferedReader br = new BufferedReader(new FileReader(inFilename)); while (true) { String line = br.readLine(); if (line == null) break; lines.add(line); } br.close(); File file1 = new File(outFilename); FileWriter fw = new FileWriter(file1); for (int i = lines.size() - 1; i >= 0; i--) { fw.write(lines.get(i)+"\n"); } fw.close(); } } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/Vector2.java000066400000000000000000000061461461721410700242610ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid; /** * a 2-D vector relationship with Complex and Polar not fully worked out. It may * simply be a matter of style which is used. * * @author (C) P. Murray-Rust, 1996 */ public class Vector2 extends Real2 { /** * constructor. * * @param r * coordinates */ public Vector2(Real2 r) { super(r); } /** * constructor. * * @param x * @param y */ public Vector2(double x, double y) { this(new Real2(x, y)); } /** * I *think* I have written this so that the angle is positive as this * rotates anticlockwise to vector. * * @param v * @return angle or null */ public Angle getAngleMadeWith(Vector2 v) { Angle angle = null; if (v != null) { double theta0 = Math.atan2(v.x, v.y); double theta1 = Math.atan2(this.x, this.y); angle = new Angle(theta0 - theta1); } return angle; } /** is vector parallel to another * calculates angle between vectors * @param v * @param eps tolerance in radians (should be non-negative) * @return true if abs(angle) (rad) {@literal <} eps */ public boolean isParallelTo(Vector2 v, double eps) { Angle a = this.getAngleMadeWith(v); return Math.abs(a.getRadian()) < eps; } /** is vector antiparallel to another * calculates angle between vectors * @param v * @param eps tolerance in radians (should be non-negative) * @return true if abs(angle) (rad) {@literal <} eps */ public boolean isAntiParallelTo(Vector2 v, double eps) { Angle a = this.getAngleMadeWith(v); return Math.abs(Math.abs(a.getRadian())-Math.PI) < eps; } /** perp product (Hill). * this.getX() * vector.getY() - this.getY() * vector.getX(); * @param v * @return product */ public double getPerpProduct(Vector2 v) { return this.getX() * v.getY() - this.getY() * v.getX(); } /** * projection of this onto vector. does not alter this. result = vector.norm() * * (this.norm() dot vector.norm()) * * @param v2 vector to project onto * @return projected vector */ public Vector2 projectOnto(Vector2 v2) { Real2 unit2 = v2.getUnitVector(); Real2 unit = this.getUnitVector(); double dot = unit2.dotProduct(unit); Vector2 projection = new Vector2(unit2.multiplyBy(this.getLength() * dot)); return projection; } } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/Vector3.java000066400000000000000000000374671461721410700242740ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid; import org.xmlcml.euclid.Axis.Axis3; /** * 3-dimensional vector * * A vector has thre components giving it a length and a direction (whose sign * is important),but no position. Vectors are often normalised to unit length. *

* Vectors and points are very closely related and some people use them * interchangeably. A Point3 has a position and cannot be normalised. In * very many routines, however, Vectors and Points can either be used * interchangeably, or there are equivalent routines or they can be converted * using cross-constructors. (They cannot be interconverted through casts). *

* The default vector is 0.0, 0.0, 0.0. Some operations on this will result in * ZerolengthVector Exceptions. * * @author (C) P. Murray-Rust, 1996 */ public class Vector3 implements EuclidConstants { /** * length of vector is zero */ final static int ZERO_VECT = 0; /** * length of vector is unknown */ final static int UNK_VECT = 1; /** * length is unit vector */ final static int UNIT_VECT = 2; /** * length of vector not zero or unity */ final static int OK_VECT = 3; /** * zero-length vector */ public final static Vector3 ZEROV = new Vector3(0.0, 0.0, 0.0); /** * X axis */ public final static Vector3 XV = new Vector3(1.0, 0.0, 0.0); /** * Y axis */ public final static Vector3 YV = new Vector3(0.0, 1.0, 0.0); /** * Z axis */ public final static Vector3 ZV = new Vector3(0.0, 0.0, 1.0); /** * Vector3 includes protected members which keep track of the sort of vector * At present these can be: null vector, unit vector and unknown. These * serve to reduce repetition in normalising already normalised vectors */ /** * vector status */ // int vecstatus = UNK_VECT; /** * vector length IF status not UNK_VECT, else -1 */ // double veclength; /** * vector components */ double[] flarray = new double[3]; /** * null constructor */ public Vector3() { } /** * construct from vector components. * * @param x * component * @param y * component * @param z * component */ public Vector3(double x, double y, double z) { this(); flarray[0] = x; flarray[1] = y; flarray[2] = z; } /** * construct from vector components. * * @param array * components * @throws EuclidRuntimeException */ public Vector3(double[] array) throws EuclidRuntimeException { this(); Util.check(array, 3); System.arraycopy(array, 0, flarray, 0, 3); } /** * axial unit vector constructor. unit vectors along X, Y, Z axes * * @param axis * to use */ public Vector3(Axis3 axis) { this(); Real.zeroArray(3, flarray); flarray[axis.value] = 1.0; } /** * copy constructor: * * @param v * vector to copy */ public Vector3(Vector3 v) { this(); System.arraycopy(v.flarray, 0, flarray, 0, 3); } /** * copy constructor from RealArray. * * @param f the array (of length 3) * @throws EuclidRuntimeException */ public Vector3(RealArray f) throws EuclidRuntimeException { this(); RealArray.check(f, 3); System.arraycopy(f.getArray(), 0, flarray, 0, 3); } /** * make a vector from a point vector is from origin to point * * @param p * the point */ public Vector3(Point3 p) { this(); System.arraycopy(p.flarray, 0, flarray, 0, 3); } /** * copy constructor: synonym for copy constructor * * @param v * vector to copy * @return vector */ public Vector3 clone(Vector3 v) { System.arraycopy(v.flarray, 0, flarray, 0, 3); return this; } /** * from Point3 vector is from origin to point * * @param p * the point * @return vector */ public Vector3 clone(Point3 p) { System.arraycopy(p.flarray, 0, flarray, 0, 3); return this; } /** * get the vector components * * @return vector of length 3 */ public double[] getArray() { return flarray; } /** * are two vectors equal lengths. uses Real.isEqual * * @param v * @return equal if length difference is within tolerance */ public boolean isEqualTo(Vector3 v) { return Real.isEqual(getLength(), v.getLength()); } /** * vector length {@literal >} vector length * * @param v * vector to compare * @return true if this {@literal >} vector */ public boolean longerThan(Vector3 v) { return (getLength() > v.getLength()); } /** * scalar multiplication. create new vector vector = this*f does not alter this * * @param f * multiplier for all components * @return scaled vector */ public Vector3 multiplyBy(double f) { Vector3 v1 = new Vector3(this); for (int i = 0; i < 3; i++) { v1.flarray[i] *= f; } return v1; } /** * scalar multiplication. sets equal to this*f alters this * * @param f * multiplier for all components */ public void multiplyEquals(double f) { for (int i = 2; i >= 0; --i) { flarray[i] *= f; } } /** * vector addition. create new vector result = this + v3 does not alter this * * @param v3 * vector to add * @return resultant vector */ public Vector3 plus(Vector3 v3) { Vector3 v1 = new Vector3(); v1 = this; for (int i = 0; i < 3; i++) { v1.flarray[i] += v3.flarray[i]; } return v1; } /** * vector addition. sets equal to this + v3 alters this * * @param v3 vector to subtract */ public void plusEquals(Vector3 v3) { for (int i = 2; i >= 0; --i) { flarray[i] += v3.flarray[i]; } } /** * vector subtraction. create new vector result = this - v3 does not alter * this * * @param v3 * vector to subtract * @return resultant vector */ public Vector3 subtract(Vector3 v3) { Vector3 v1 = new Vector3(); v1 = this; for (int i = 0; i < 3; i++) { v1.flarray[i] -= v3.flarray[i]; } return v1; } /** * vector subtraction. sets equal to this - v3 alters this * * @param v3 vector to subtract */ public void subtractEquals(Vector3 v3) { for (int i = 2; i >= 0; --i) { flarray[i] -= v3.flarray[i]; } } /** * negative of vector. result = -this does not alter this * * @return resultant vector */ public Vector3 negative() { Vector3 v1 = new Vector3(this); for (int i = 0; i < 3; i++) { v1.flarray[i] = -flarray[i]; } return v1; } /** * negative of vector. result = - DOES alter this * * @return resultant vector */ public Vector3 negativeEquals() { for (int i = 0; i < 3; i++) { flarray[i] = -flarray[i]; } return this; } /** * get component. use raw array if you are sure checking is not required * * @param n * the zero-based index * @return the n'th component * @throws EuclidRuntimeException */ public double elementAt(int n) throws EuclidRuntimeException { Util.check(n, 0, 2); return flarray[n]; } /** * set component. * * @param n * the zero-based index * @param f * component value * @throws EuclidRuntimeException */ public void setElementAt(int n, double f) throws EuclidRuntimeException { Util.check(n, 0, 2); flarray[n] = f; } /** * are two vectors equal in all components. uses Real.isEqual * * @param v * @return equal if components equal within tolerance */ public boolean isIdenticalTo(Vector3 v) { return Real.isEqual(flarray, v.flarray, Real.getEpsilon()); } /** * is vector of zero length. uses Real.isEqual * * @return if zero within tolerance */ public boolean isZero() { boolean b = Real.isZero(getLength(), Real.getEpsilon()); return b; } /** * create transformed vector. does not alter this. * * @param t * transform * @return tranformed vector */ public Vector3 transform(Transform3 t) { Transform3.checkNotNull(t); Vector3 vout = new Vector3(); double[] pv = vout.flarray; double[][] pt = t.getMatrix(); // just use the 3x3 submatrix and ignore translation for (int i = 0; i < 3; i++) { double[] p = this.flarray; for (int j = 0; j < 3; j++) { pv[i] += pt[i][j] * p[j]; } } return vout; } /** * create cross product. result = this x v3 does not alter this. * * @param v3 * vector to multiply * @return cross product */ public Vector3 cross(Vector3 v3) { Vector3 v1 = new Vector3(); int i, j, k; for (i = 0, j = 1, k = 2; i < 3; i++) { v1.flarray[i] = flarray[j] * v3.flarray[k] - flarray[k] * v3.flarray[j]; j = (j + 1) % 3; k = (k + 1) % 3; } return v1; } /** sets vector components to nearest integer value. * useful for crystallography and other grids * uses Math.round(); * ALTERS THIS * @return vector with integer components */ public Vector3 round() { for (int i = 0; i < 3; i++) { flarray[i] = Math.round(flarray[i]); } return this; } /** normalize vector. * alters this * this = this.normalize() * if zero vector takes no action (maybe this is bad...) * * @return vector of unit length */ public Vector3 normalize() { double veclength = this.getLength(); if (veclength < EPS) { throw new EuclidRuntimeException("cannot normalize zero-length vector"); } for (int i = 0; i < 3; i++) { flarray[i] /= veclength; } return this; } /** normalize vector. alters this this = this.normalize() if zero vector * takes no action (maybe this is bad...) * @deprecated (use normalize()) * @return vector of unit length */ public Vector3 normalise() { return this.normalize(); } /** * get normalized vector. * does not alter this * result = this.normalize() * if zero vector takes no action (maybe this is bad...) * * @return vector of unit length */ public Vector3 getUnitVector() { Vector3 v = new Vector3(this); v.normalize(); return v; } /** * return vector length. does not alter this result = this.length() * * @return vector length */ public double getLength() { double sum = 0.0; for (int i = 0; i < 3; i++) { sum += flarray[i] * flarray[i]; } return Math.sqrt(sum); } /** * create dot product. result = this . v3 does not alter this. * * @param v3 * vector to multiply * @return dot product */ public double dot(Vector3 v3) { double sum = 0.0; for (int i = 0; i < 3; i++) { sum += this.flarray[i] * v3.flarray[i]; } return sum; } /** * dot product - protected * * @param v3 * @return the dotproduct */ protected double dot(double[] v3) { double sum = 0.0; for (int i = 0; i < 3; i++) { sum += this.flarray[i] * v3[i]; } return sum; } /** * calculate unsigned angle between vectors result = angle between this and * v2 uses acos(this.dot.v2) so angle is unsigned does not alter this. * * @param v2 * vector to multiply * @return angle (null if vectors zero length */ public Angle getAngleMadeWith(Vector3 v2) { Angle a = null; if (!this.isZero() && !v2.isZero()) { Vector3 v1a = getUnitVector(); Vector3 v2a = v2.getUnitVector(); double tmp = v1a.dot(v2a); if (tmp < -1.0) { tmp = -1.0; } else if (tmp > 1.0) { tmp = 1.0; } a = new Angle(Math.acos(tmp)); } return a; } /** * calculate scalar triple product between vectors. result = this.(v2 x v3) * does not alter this. * * @param v2 * vector to multiply * @param v3 * vector to multiply * @return stp */ public double getScalarTripleProduct(Vector3 v2, Vector3 v3) { return this.dot(v2.cross(v3)); } /** * projection of this onto vector. does not alter this. result = vector.norm() * * (this.norm() dot vector.norm()) * * @param v * vector to project onto * @exception EuclidRuntimeException * vector or this is zero length * @return projected vector */ public Vector3 projectOnto(Vector3 v) throws EuclidRuntimeException { if (this.isZero() || v.isZero()) { throw new EuclidRuntimeException("zero length vector"); } Vector3 projection = new Vector3(); Vector3 unit3 = v.getUnitVector(); Vector3 unit2 = getUnitVector(); double dot = unit2.dot(unit3); projection = unit3.multiplyBy(getLength() * dot); return projection; } /** * are two vectors colinear. also returns true if one or more vectors are * zero * * @param v * vector to test with this * @return true if cross product isZero() */ public boolean isColinearVector(Vector3 v) { return this.cross(v).isZero(); } /** * get any vector not colinear with this. returns any axis which is not * colinear with this * * @return either XV or YV (even if this is zero) */ public Vector3 getNonColinearVector() { return isColinearVector(XV) ? YV : XV; } /** * get any vector perpendicular to this. useful for creating cartesian axes * containing this * * @return vector perpendicular to this (zero if this is zero) */ public Vector3 getPerpendicularVector() { return this.isZero() ? ZEROV : this.getNonColinearVector().cross(this); } /** * get string representation. * * @return string representation */ public String toString() { return EuclidConstants.S_LBRAK + flarray[0] + EuclidConstants.S_COMMA + flarray[1] + EuclidConstants.S_COMMA + flarray[2] + EuclidConstants.S_RBRAK; } } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/Window2.java000066400000000000000000000050301461721410700242550ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid; /** a 2D window * (bounding box, viewport, etc) * Used with Transform2 @author (C) P. Murray-Rust, 2001 */ public class Window2 extends Object { public final static int X = 0; public final static int Y = 1; Real2 origin; Real2 farPoint; Real2 dim; public Window2(Real2 origin, Real2 farPoint) { this.origin = new Real2(origin); this.farPoint = new Real2(farPoint); getDim(); } public Real2 getDim() { this.dim = new Real2(farPoint.getX() - origin.getX(), farPoint.getY() - origin.getY()); return this.dim; } public Real2 getOrigin() {return origin;} public Real2 getFarPoint() {return farPoint;} /** get extent of a given axis (X or Y); */ public Real2 getExtent(int axis) { if (axis == X) return new Real2(origin.getX(), farPoint.getX()); if (axis == Y) return new Real2(origin.getY(), farPoint.getY()); return null; } /** convenience because negative lengths not allowed in awt */ public void drawRect(java.awt.Graphics g) { int x1 = (int) origin.getX(); int x2 = (int) farPoint.getX(); int y1 = (int) origin.getY(); int y2 = (int) farPoint.getY(); if (x1 > x2) {int t = x2; x2 = x1; x1 = t;} if (y1 > y2) {int t = y2; y2 = y1; y1 = t;} g.drawRect(x1, y1, x2-x1, y2-y1); } /** convenience because negative lengths not allowed in awt */ public void fillRect(java.awt.Graphics g) { int x1 = (int) origin.getX(); int x2 = (int) farPoint.getX(); int y1 = (int) origin.getY(); int y2 = (int) farPoint.getY(); if (x1 > x2) {int t = x2; x2 = x1; x1 = t;} if (y1 > y2) {int t = y2; y2 = y1; y1 = t;} g.fillRect(x1, y1, x2-x1, y2-y1); } public void transformBy(Transform2 tr) { origin.transformBy(tr); farPoint.transformBy(tr); getDim(); } public String toString() { return "Window2: "+origin+"/"+farPoint+"("+dim+")"; } } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/test/000077500000000000000000000000001461721410700230425ustar00rootroot00000000000000euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/test/DoubleTestBase.java000066400000000000000000000111021461721410700265450ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid.test; import org.junit.Assert; /** * *

* superclass for manage common methods for unit tests *

* * @author Peter Murray-Rust * @version 5.0 * */ public class DoubleTestBase { //constants static String S_SLASH = "/"; static String S_RBRAK = ")"; /** * Asserts equality of double arrays. * * checks for non-null, then equality of length, then individual elements * * @param message * @param expected * expected array * @param actual * actual array * @param eps * tolerance for agreement */ public static void assertEquals(String message, double[] expected, double[] actual, double eps) { String s = testEquals(expected, actual, eps); if (s != null) { Assert.fail(message + "; " + s); } } private static boolean isEqual(double a, double b, double epsilon) { return Math.abs(a - b) < epsilon; } @SuppressWarnings("unused") private static boolean isEqual(double[] a, double[] b, double epsilon) { if (a == null || b == null || a.length != b.length) { return false; } for (int i = 0; i < a.length; i++) { if (!isEqual(a[i], b[i], epsilon)) return false; } return true; } public static void assertObjectivelyEquals(String message, double[] a, double[] b, double eps) { String s = null; if (a == null) { s = "a is null"; } else if (b == null) { s = "b is null"; } else if (a.length != b.length) { s = "unequal arrays: " + a.length + S_SLASH + b.length; } else { for (int i = 0; i < a.length; i++) { if ( !isEqual(a[i], b[i], eps)) { s = "unequal element at (" + i + "), " + a[i] + " != " + b[i]; break; } } } if (s != null) { Assert.fail(message + "; " + s); } } /** * Asserts non equality of double arrays. * * checks for non-null, then equality of length, then individual elements * * @param message * @param a * expected array * @param b * actual array * @param eps * tolerance for agreement */ public static void assertNotEquals(String message, double[] a, double[] b, double eps) { String s = testEquals(a, b, eps); if (s == null) { Assert.fail(message + "; arrays are equal"); } } /** * returns a message if arrays differ. * * @param a * array to compare * @param b * array to compare * @param eps * tolerance * @return null if arrays are equal else indicative message */ static String testEquals(double[] a, double[] b, double eps) { String s = null; if (a == null) { s = "a is null"; } else if (b == null) { s = "b is null"; } else if (a.length != b.length) { s = "unequal arrays: " + a.length + S_SLASH + b.length; } else { for (int i = 0; i < a.length; i++) { if (!isEqual(a[i], b[i], eps)) { s = "unequal element at (" + i + "), " + a[i] + " != " + b[i]; break; } } } return s; } /** * returns a message if arrays of arrays differ. * * @param a * array to compare * @param b * array to compare * @param eps * tolerance * @return null if array are equal else indicative message */ static String testEquals(double[][] a, double[][] b, double eps) { String s = null; if (a == null) { s = "a is null"; } else if (b == null) { s = "b is null"; } else if (a.length != b.length) { s = "unequal arrays: " + a.length + S_SLASH + b.length; } else { for (int i = 0; i < a.length; i++) { if (a[i].length != b[i].length) { s = "row (" + i + ") has unequal lengths: " + a[i].length + S_SLASH + b[i].length; break; } for (int j = 0; j < a[i].length; j++) { if (!isEqual(a[i][j], b[i][j], eps)) { s = "unequal element at (" + i + ", " + j + "), (" + a[i][j] + " != " + b[i][j] + S_RBRAK; break; } } } } return s; } } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/test/EuclidTestBase.java000066400000000000000000000024401461721410700265450ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid.test; /** * *

* superclass for manage common methods for unit tests *

* * @author Peter Murray-Rust * @version 5.0 * */ public final class EuclidTestBase { static String S_SPACE = " "; /** * used by Assert routines. copied from Assert * * @param message * prepends if not null * @param expected * @param actual * @return message */ public static String getAssertFormat(String message, Object expected, Object actual) { String formatted = ""; if (message != null) { formatted = message + S_SPACE; } return formatted + "expected:<" + expected + "> but was:<" + actual + ">"; } } euclid-euclid-2.9/src/main/java/org/xmlcml/euclid/test/StringTestBase.java000066400000000000000000000063021461721410700266070ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid.test; import org.junit.Assert; /** * *

* superclass for manage common methods for unit tests *

* * @author Peter Murray-Rust * @version 5.0 * */ public class StringTestBase { //constants static String S_SLASH = "/"; static String S_SPACE = " "; /** * Asserts equality of String arrays. * * checks for non-null, then equality of length, then individual elements * equality if individual elements are equal or both elements are null * * @param message * @param a * expected array may include nulls * @param b * actual array may include nulls */ public static void assertEquals(String message, String[] a, String[] b) { String s = testEquals(a, b); if (s != null) { Assert.fail(message + "; " + s); } } /** * Asserts equality of String arrays. * * convenience method where test is a whitespace-separated set of tokens * * @param message * @param expected * expected array as space concatenated * @param actual * actual array may not include nulls */ public static void assertEquals(String message, String expected, String[] actual) { if(expected==null){ Assert.fail(message+"; "+"null expected String"); } String[] aa = expected.split(S_SPACE); String s = testEquals(aa, actual); if (s != null) { Assert.fail(message + "; " + s); } } /** * match arrays. error is a == null or b == null or a.length != b.length or * a[i] != b[i] nulls match * * @param a * @param b * @return message if errors else null */ public static String testEquals(String[] a, String[] b) { String s = null; if (a == null) { s = "a is null"; } else if (b == null) { s = "b is null"; } else if (a.length != b.length) { s = "unequal arrays: " + a.length + S_SLASH + b.length; } else { for (int i = 0; i < a.length; i++) { if (a[i] == null && b[i] == null) { // both null, match } else if (a[i] == null || b[i] == null || !a[i].equals(b[i])) { s = "unequal element (" + i + "), expected: " + a[i] + " found: " + b[i]; break; } } } return s; } /** * Asserts non equality of String arrays. * * checks for non-null, then equality of length, then individual elements * * @param message * @param expected * expected array * @param actual * actual array */ public static void assertNotEquals(String message, String[] expected, String[] actual) { String s = testEquals(expected, actual); if (s == null) { Assert.fail(message + "; arrays are equal"); } } } euclid-euclid-2.9/src/main/java/org/xmlcml/files/000077500000000000000000000000001461721410700217205ustar00rootroot00000000000000euclid-euclid-2.9/src/main/java/org/xmlcml/files/EuclidSource.java-old000077500000000000000000000034771461721410700257430ustar00rootroot00000000000000package org.xmlcml.files; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStream; import java.net.URL; import org.apache.commons.io.FilenameUtils; import org.xmlcml.euclid.Euclid; /** general static utilities. * * @author pm286 * */ @Deprecated public class EuclidSource { public static final String DOI = "doi:"; public static final String HTTP = "http://"; public static final String HTTPS = "https://"; public static final String HTM = "htm"; public static final String HTML = "html"; public static final String PDF = "pdf"; public static final String SVG = "svg"; public static final String XML = "xml"; public static final String LINE_NUMBER = "lineNumber"; public static final String LINE_VALUE = "lineValue"; public static final String XPATH = "xpath"; public static boolean endsWithSeparator(String filename) { return filename != null && FilenameUtils.indexOfLastSeparator(filename) == filename.length()-1; } /** crude tool to guess whether is URL from name. * * @param name * @return */ public static boolean isURL(String name) { return name.startsWith(HTTP) || name.startsWith(HTTPS); } /** heuristic ducktype to get input stream; * * First assumes name is resource on classpath. * if fails; tries it as http:// or https:// URL * if fails; tries as filename * * @param name (of resource, URL, or filename) * @return Opened stream, or null if not found */ public static InputStream getInputStream(String name) { InputStream is = Euclid.class.getResourceAsStream(name); if (is == null) { try { is = new URL(name).openStream(); } catch (Exception e) { // not a URL } } if (is == null) { try { is = new FileInputStream(name); } catch (FileNotFoundException e) { // no file } } return is; } } euclid-euclid-2.9/src/main/java/org/xmlcml/files/QuickscrapeNorma.java-old000066400000000000000000000447321461721410700266200ustar00rootroot00000000000000package org.xmlcml.files; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import nu.xom.Document; import nu.xom.Element; import org.apache.commons.io.FileUtils; import org.apache.commons.io.FilenameUtils; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.xmlcml.args.ArgumentOption; import org.xmlcml.xml.XMLUtil; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; /** collection of files within the ContentMine system. * * The structure of scholarly articles often requires many constituent articles. For example an article may have a PDF, an HTML abstract, several GIFs for images, some tables in HTML, some DOCX files, CIFs for crystallography, etc.. These all need keeping together... Note that the Catalog (from CottageLabs) primarily holds metadata. [It's possible to hold some of the HTML content, but it soon starts to degrade performance]. We therefore have metadata in the Catalog and contentFiles on disk. These files and Open and can, in principle, be used independently of the Catalog. I am designing a "QuickscrapeNorma" which passes the bundle down the pipeline. This should be independent of what language [Python , JS, Java...] is used to create or read them. We believe that a normal filing system is satisfactory (at least at present while we develop the technology). A typical pass for one DOI (e.g. journal.pone.0115884 ) through the pipeline (mandatory files are marked *, optional ?) might look like: DOI --> Quickscrape --> create directory contentmine/some/where/journal.pone.0115884/. It may contain results.json * // a listing of scraped files fulltext.xml ? // publishers XML fulltext.pdf ? // publishers PDF fulltext.html ? // raw HTML fulltext.pdf.txt ? // raw text from pdf provisional.pdf ? // provisional PDF (often disappears) foo12345.docx ? // data files numbered by publisher/author bar54321.docx ? ah1234.cif ? // crystallographic data pqr987.cml ? // chemistry file mmm.csv ? // table pic5656.png ? // images pic5657.gif ? // image suppdata.pdf ? // supplemental data and more only results.json is mandatory. However there will normally be at least one fulltext.* file and probably at least one *.html file (as the landing page must be in HTML). Since quickscrape can extract data without fulltext it might also be deployed against a site with data files. There may be some redundancy - *.xml may be trasformable into *.html and *.pdf into *.html. The PDF may also contain the same images as some exposed *.png. ================== This container (directory) is then massed to Norma. Norma will normalize as much information as possible, and we can expect this to continue to develop. This includes: * conversion to Unicode (XML, HTML, and most "text" files) * normalization of characters (e.g. Angstrom -> Aring, smart quotes => "", superscript "o" to degrees, etc.) * creating well-formed HTML (often very hard) * converting PDF to SVG (very empirical and heuristic) * converting SVG to running text. * building primitives (circles, squares, from the raw graphics) * building graphics objects (arrows, textboxes, flowcharts) from the primitives * building text from SVG etc... This often creates a lot of temporary files, which may be usefully cached for a period. We may create a subdirectory ./svg with intermediate pages, or extracted SVGs. These will be recorded in results.json, which will act as metadata for the files and subdirectories. Norma will create ./svg/*.svg from PDF (using PDFBox and PDF2SVG), then fulltext.pdf.xhtml (heuristically created XHTML). Norma will also create wellformed fulltext.html.xhtml from raw fulltext.html or from fulltext.xml.xhtml from fulltext.xml. In the future Norma will also convert MS-formats such as DOCX and PPT using Apach POI. Norma will then structure any flat structures into structured XHTML using grouping rules such as in XSLT2. At this stage we shall have structured XHTML files ("scholarly HTML") with linked images and tables and supplemental data. We'll update results.json ======================== AMI can further index or transform the ScholarlyHTML and associated files. An AMI plugin (e.g. AMI-species) will produce species.results.xml - a file with the named species in textual context. Similar outputs come from sequence, or other tagging (geotagging). The main community development will come from regexes. For example we have regex.crystal.results.xml, regex.farm.results.xml, regex.clinical_trials.results.xml, etc. The results file include the regexes used and other metadata (more needed!). Again we can update results.json. We may also wish to delete temporary files such as the *.svg in PDF2SVG.... * * @author pm286 * */ @Deprecated public class QuickscrapeNorma { private static final Logger LOG = Logger.getLogger(QuickscrapeNorma.class); static { LOG.setLevel(Level.DEBUG); } private static final String DOCX = "docx"; private static final String EPUB = "epub"; private static final String HTML = "html"; private static final String PDF = "pdf"; private static final String XML = "xml"; public static final String ABSTRACT_HTML = "abstract.html"; public static final String FULLTEXT_DOCX = "fulltext.docx"; public static final String FULLTEXT_HTML = "fulltext.html"; public static final String FULLTEXT_PDF = "fulltext.pdf"; public static final String FULLTEXT_PDF_TXT = "fulltext.pdf.txt"; public static final String FULLTEXT_XML = "fulltext.xml"; public static final String RESULTS_JSON = "results.json"; public static final String RESULTS_XML = "results.xml"; public static final String RESULTS_HTML = "results.html"; public static final String SCHOLARLY_HTML = "scholarly.html"; public final static List RESERVED_FILE_NAMES; static { RESERVED_FILE_NAMES = Arrays.asList(new String[] { ABSTRACT_HTML, FULLTEXT_DOCX, FULLTEXT_HTML, FULLTEXT_PDF, FULLTEXT_PDF_TXT, FULLTEXT_XML, RESULTS_JSON, RESULTS_XML, SCHOLARLY_HTML }); } public static final String RESULTS_DIR = "results/"; public static final String PDF_DIR = "pdf/"; public final static List RESERVED_DIR_NAMES; static { RESERVED_DIR_NAMES = Arrays.asList(new String[] { RESULTS_DIR, PDF_DIR, }); } public final static Map RESERVED_FILES_BY_EXTENSION = new HashMap(); private static final String RESULTS_DIRECTORY_NAME = "results"; static { RESERVED_FILES_BY_EXTENSION.put(DOCX, FULLTEXT_DOCX); // RESERVED_FILES_BY_EXTENSION.put(EPUB, FULLTEXT_EPUB); RESERVED_FILES_BY_EXTENSION.put(HTML, FULLTEXT_HTML); RESERVED_FILES_BY_EXTENSION.put(PDF, FULLTEXT_PDF); RESERVED_FILES_BY_EXTENSION.put(XML, FULLTEXT_XML); } public static boolean isReservedFilename(String name) { return RESERVED_FILE_NAMES.contains(name); } public static boolean isReservedDirectory(String name) { return RESERVED_DIR_NAMES.contains(name); } private File directory; private List reservedFileList; private List nonReservedFileList; private List reservedDirList; private List nonReservedDirList; public QuickscrapeNorma() { } /** creates QN object but does not alter filestore. * * @param directory */ public QuickscrapeNorma(File directory) { this.directory = directory; } /** ensures filestore matches a QuickscrapeNorma structure. * * @param directory * @param delete */ public QuickscrapeNorma(File directory, boolean delete) { this(directory); this.createDirectory(directory, delete); } public QuickscrapeNorma(String filename) { this(new File(filename), false); } public void ensureReservedFilenames() { if (reservedFileList == null) { reservedFileList = new ArrayList(); nonReservedFileList = new ArrayList(); reservedDirList = new ArrayList(); nonReservedDirList = new ArrayList(); List files = new ArrayList(FileUtils.listFiles(directory, null, false)); for (File file : files) { if (file.isDirectory()) { if (isReservedDirectory(FilenameUtils.getName(file.getAbsolutePath()))) { reservedDirList.add(file); } else { nonReservedDirList.add(file); } } else { if (isReservedFilename(FilenameUtils.getName(file.getAbsolutePath()))) { reservedFileList.add(file); } else { nonReservedFileList.add(file); } } } } } public List getReservedDirectoryList() { ensureReservedFilenames(); return reservedDirList; } public List getReservedFileList() { ensureReservedFilenames(); return reservedFileList; } public List getNonReservedDirectoryList() { ensureReservedFilenames(); return nonReservedDirList; } public List getNonReservedFileList() { ensureReservedFilenames(); return nonReservedFileList; } public static boolean containsNoReservedFilenames(File dir) { if (dir != null && dir.isDirectory()) { List files = new ArrayList(FileUtils.listFiles(dir, null, false)); // int nonReserved = 0; for (File file : files) { if (!file.isHidden()) { String name = FilenameUtils.getName(file.getAbsolutePath()); if (isReservedFilename(name)) { return false; } // nonReserved++; } } return true; } return false; } public boolean containsNoReservedFilenames() { return QuickscrapeNorma.containsNoReservedFilenames(directory); } public void createDirectory(File dir, boolean delete) { this.directory = dir; if (dir == null) { throw new RuntimeException("Null directory"); } if (delete) { try { FileUtils.forceDelete(dir); } catch (IOException e) { throw new RuntimeException("Cannot delete directory: "+dir, e); } } try { FileUtils.forceMkdir(dir); } catch (IOException e) { throw new RuntimeException("Cannot make directory: "+dir+" already exists"); } // maybe } public void readDirectory(File dir) { this.directory = dir; Multimap map = HashMultimap.create(); requireDirectoryExists(dir); // fileList = new ArrayList(FileUtils.listFiles(dir, null, false)); checkRequiredQuickscrapeFiles(); // indexByFileExtensions(); } private void checkRequiredQuickscrapeFiles() { requireExistingNonEmptyFile(new File(directory, RESULTS_JSON)); } public static boolean isExistingFile(File file) { boolean ok = (file != null); ok &= file.exists(); ok &= !file.isDirectory(); return ok; } public static boolean isExistingDirectory(File file) { boolean ok = (file != null); ok &= file.exists(); ok &= file.isDirectory(); return ok; } private void requireDirectoryExists(File dir) { if (dir == null) { throw new RuntimeException("Null directory"); } if (!dir.exists()) { throw new RuntimeException("Directory: "+dir+" does not exist"); } if (!dir.isDirectory()) { throw new RuntimeException("File: "+dir+" is not a directory"); } } private void requireExistingNonEmptyFile(File file) { if (file == null) { throw new RuntimeException("Null file"); } if (!file.exists()) { throw new RuntimeException("File: "+file+" does not exist"); } if (file.isDirectory()) { throw new RuntimeException("File: "+file+" must not be a directory"); } if (FileUtils.sizeOf(file) == 0) { throw new RuntimeException("File: "+file+" must not be empty"); } } public boolean hasFulltextXML() { return isExistingFile(getExistingFulltextXML()); } public File getExistingFulltextXML() { return getExistingReservedFile(FULLTEXT_XML); } public boolean hasFulltextHTML() { return isExistingFile(new File(directory, FULLTEXT_HTML)); } public File getExisitingFulltextHTML() { return getExistingReservedFile(FULLTEXT_HTML); } public boolean hasResultsJSON() { return isExistingFile(new File(directory, RESULTS_JSON)); } public File getExistingResultsJSON() { return getExistingReservedFile(RESULTS_JSON); } public boolean hasResultsXML() { return isExistingFile(new File(directory, RESULTS_XML)); } public File getExistingResultsXML() { return getExistingReservedFile(RESULTS_XML); } public boolean hasScholarlyHTML() { return isExistingFile(new File(directory, SCHOLARLY_HTML)); } public File getExistingScholarlyHTML() { return getExistingReservedFile(SCHOLARLY_HTML); } public boolean hasFulltextPDF() { return isExistingFile(new File(directory, FULLTEXT_PDF)); } public File getExisitingFulltextPDF() { return getExistingReservedFile(FULLTEXT_PDF); } public File getReservedFile(String reservedName) { File file = !isReservedFilename(reservedName) ? null : new File(directory, reservedName); return file; } public File getExistingReservedFile(String reservedName) { File file = new File(directory, reservedName); return isExistingFile(file) ? file : null; } public boolean hasFulltextDOCX() { return isExistingFile(new File(directory, FULLTEXT_DOCX)); } public File getFulltextDOCX() { return new File(directory, FULLTEXT_DOCX); } public File getExistingResultsDir() { return new File(directory, RESULTS_DIR); } @Override public String toString() { ensureReservedFilenames(); StringBuilder sb = new StringBuilder(); sb.append("dir: "+directory+"\n"); for (File file : getReservedFileList()) { sb.append(file.toString()+"\n"); } return sb.toString(); } public void writeFile(String content, String filename) { if (filename == null) { LOG.error("Null output file"); return; } File file = new File(directory, filename); if (file.exists()) { LOG.error("file already exists (overwritten) "+file); } if (content != null) { try { FileUtils.write(file, content); } catch (IOException e) { throw new RuntimeException("Cannot write file: ", e); } } else { LOG.trace("Null content"); } } public File getDirectory() { return directory; } public List listFiles(boolean recursive) { List files = new ArrayList(FileUtils.listFiles(directory, null, recursive)); return files; } public void writeResults(String resultsFileName, String results) throws Exception { File resultsFile = new File(directory, resultsFileName); FileUtils.writeStringToFile(resultsFile, results); } public void writeResults(File resultsFile, Element resultsXML) { // File resultsFile = new File(directory, resultsFileName); try { XMLUtil.debug(resultsXML, new FileOutputStream(resultsFile), 1); } catch (IOException e) { throw new RuntimeException("cannot write XML ", e); } } public void writeResults(String resultsFileName, Element resultsXML) { writeResults(new File(directory, resultsFileName), resultsXML); } public static String getQNReservedFilenameForExtension(String name) { String filename = null; String extension = FilenameUtils.getExtension(name); if (extension.equals("")) { // no type } else if (PDF.equals(extension)) { filename = FULLTEXT_PDF; } else if (XML.equals(extension)) { filename = FULLTEXT_XML; } else if (HTML.equals(extension)) { filename = FULLTEXT_HTML; } return filename; } public Element getMetadataElement() { Element metadata = new Element("qsNorma"); metadata.appendChild(this.toString()); return metadata; } public static boolean isNonEmptyNonReservedInputList(List inputList) { return inputList != null && (inputList.size() != 1 || !QuickscrapeNorma.isReservedFilename(inputList.get(0))); } public void copyTo(File destDir, boolean overwrite) throws IOException { if (destDir == null) { throw new RuntimeException("Null destination file in copyTo()"); } else { boolean canWrite = true; if (destDir.exists()) { if (overwrite) { try { FileUtils.forceDelete(destDir); } catch (IOException e) { LOG.error("cannot delete: "+destDir); canWrite = false; } } else { LOG.error("Cannot overwrite :"+destDir); canWrite = false; } } if (canWrite) { FileUtils.copyDirectory(this.directory, destDir); if (!destDir.exists() || !destDir.isDirectory()) { throw new RuntimeException("failed to create directory: "+destDir); } } } } /** creates a subdirectory of results/ and writes each result file to its own directory. * * Example: * qsnorma1_2_3/ * results/ * words/ * frequencies/ * results.xml * lengths/ * results.xml * * here the option is defined in an element in args.xml with name="words" * * @param option * @param resultsElementList * @param resultsDirectoryName */ public List createResultsDirectoriesAndOutputResultsElement( ArgumentOption option, List resultsElementList, String resultsDirectoryName) { File optionDirectory = new File(getResultsDirectory(), option.getName()); List outputDirectoryList = new ArrayList(); for (ResultsElement resultsElement : resultsElementList) { File outputDirectory = createResultsDirectoryAndOutputResultsElement(optionDirectory, resultsElement); outputDirectoryList.add(outputDirectory); } return outputDirectoryList; } private File createResultsDirectoryAndOutputResultsElement(File optionDirectory, ResultsElement resultsElement) { File resultsSubDirectory = null; String title = resultsElement.getTitle(); if (title == null) { LOG.error("null title"); } else { resultsSubDirectory = new File(optionDirectory, title); resultsSubDirectory.mkdirs(); File resultsFile = new File(resultsSubDirectory, QuickscrapeNorma.RESULTS_XML); writeResults(resultsFile, resultsElement); LOG.debug("Wrote "+resultsFile.getAbsolutePath()); } return resultsSubDirectory; } private File getResultsDirectory() { File resultsDirectory = new File(getDirectory(), RESULTS_DIRECTORY_NAME); return resultsDirectory; } public ResultsElement getResultsElement(String pluginName, String methodName) { File resultsDir = getExistingResultsDir(); ResultsElement resultsElement = null; if (QuickscrapeNorma.isExistingDirectory(resultsDir)) { File pluginDir = new File(resultsDir, pluginName); if (QuickscrapeNorma.isExistingDirectory(pluginDir)) { File methodDir = new File(pluginDir, methodName); if (QuickscrapeNorma.isExistingDirectory(methodDir)) { File resultsXML = new File(methodDir, QuickscrapeNorma.RESULTS_XML); if (QuickscrapeNorma.isExistingFile(resultsXML)) { Document resultsDoc = XMLUtil.parseQuietlyToDocument(resultsXML); resultsElement = ResultsElement.createResults(resultsDoc.getRootElement()); } } } } return resultsElement; } } euclid-euclid-2.9/src/main/java/org/xmlcml/files/QuickscrapeNormaList.java-old000066400000000000000000000017571461721410700274540ustar00rootroot00000000000000package org.xmlcml.files; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.apache.log4j.Level; import org.apache.log4j.Logger; /** list of QuickscrapeNorma objects. * * @author pm286 * */ @Deprecated public class QuickscrapeNormaList implements Iterable { private static final Logger LOG = Logger .getLogger(QuickscrapeNormaList.class); static { LOG.setLevel(Level.DEBUG); } private List qnList; public QuickscrapeNormaList() { ensureQNList(); } private void ensureQNList() { if (qnList == null) { qnList = new ArrayList(); } } public int size() { ensureQNList(); return qnList.size(); } @Override public Iterator iterator() { ensureQNList(); return qnList.iterator(); } public QuickscrapeNorma get(int i) { ensureQNList(); return qnList.get(i); } public void add(QuickscrapeNorma qn) { ensureQNList(); qnList.add(qn); } } euclid-euclid-2.9/src/main/java/org/xmlcml/files/ResultElement.java-old000066400000000000000000000040421461721410700261270ustar00rootroot00000000000000package org.xmlcml.files; import org.apache.log4j.Level; import org.apache.log4j.Logger; import nu.xom.Attribute; import nu.xom.Element; import nu.xom.Node; import nu.xom.ParentNode; /** a container for a "result" from an action on a QSNorma. * * Normally output to the "results" directory * * @author pm286 * */ @Deprecated public class ResultElement extends Element { private static final Logger LOG = Logger.getLogger(ResultElement.class); static { LOG.setLevel(Level.DEBUG); } public static final String TAG = "result"; public static final String TITLE = "title"; public static final String PRE = "pre"; private static final String EXACT = "exact"; public static final String MATCH = "match"; public static final String POST = "post"; private static final String NAME = "name"; private static final String XPATH = "xpath"; public ResultElement() { super(TAG); } public ResultElement(String title) { this(); this.setTitle(title); } private void setTitle(String title) { if (title == null) { throw new RuntimeException("title cannot be null"); } this.addAttribute(new Attribute(TITLE, title)); } public String getExact() { return this.getAttributeValue(EXACT); } public void setExact(String value) { setValue(EXACT, value); } public String getMatch() { return this.getAttributeValue(MATCH); } public void setMatch(String value) { setValue(MATCH, value); } public String getName() { return this.getAttributeValue(NAME); } public void setName(String value) { setValue(NAME, value); } public String getPre() { return this.getAttributeValue(PRE); } public void setPre(String value) { setValue(PRE, value); } public String getPost() { return this.getAttributeValue(POST); } public void setPost(String value) { setValue(POST, value); } public void setValue(String name, String value) { Attribute attribute = new Attribute(name, value); this.addAttribute(attribute); } public void setXPath(String xpath) { this.addAttribute(new Attribute(XPATH, xpath)); } } euclid-euclid-2.9/src/main/java/org/xmlcml/files/ResultsElement.java-old000066400000000000000000000105141461721410700263130ustar00rootroot00000000000000package org.xmlcml.files; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import nu.xom.Attribute; import nu.xom.Element; import nu.xom.Node; import nu.xom.Text; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.xmlcml.xml.XMLUtil; /** a container for ResultElement's. * * @author pm286 * */ @Deprecated public class ResultsElement extends Element implements Iterable { private static final Logger LOG = Logger.getLogger(ResultsElement.class); static { LOG.setLevel(Level.DEBUG); } public static final String TAG = "results"; public static final String TITLE = "title"; protected List resultElementList; public List nameList; public ResultsElement() { super(TAG); } public ResultsElement(ResultsElement element) { this(); copyAttributesAndAddChildren(element); } public ResultsElement(String title) { this(); this.setTitle(title); } public void setTitle(String title) { this.addAttribute(new Attribute(TITLE, title)); } public String getTitle() { return this.getAttributeValue(TITLE); } /** create ResultsElement from reading Element. * * @param element * @return */ public static ResultsElement createResults(Element element) { return (ResultsElement) createResults0(element); } private static Element createResults0(Element element) { Element newElement = null; String tag = element.getLocalName(); if (ResultsElement.TAG.equals(tag)) { newElement = new ResultsElement(); } else if (ResultElement.TAG.equals(tag)) { newElement = new ResultElement(); } else { LOG.error("Unknown element: "+tag); } XMLUtil.copyAttributes(element, newElement); for (int i = 0; i < element.getChildCount(); i++) { Node child = element.getChild(i); if (child instanceof Text) { child = child.copy(); } else { child = ResultsElement.createResults0((Element)child); } if (newElement != null && child != null) { newElement.appendChild(child); } } LOG.trace("XML :"+newElement.toXML()); return newElement; } /** transfers with detachment ResultElement's in one ResultsElement to another. * * @param subResultsElement source of ResultElement's */ public void transferResultElements(ResultsElement subResultsElement) { List subResults = subResultsElement.getOrCreateResultElementList(); for (ResultElement subResult : subResults) { subResult.detach(); this.appendChild(subResult); } } protected List getOrCreateResultElementList() { resultElementList = new ArrayList(); List resultChildren = XMLUtil.getQueryElements(this, "./*[local-name()='"+ResultElement.TAG+"']"); for (Element resultElement : resultChildren) { resultElementList.add((ResultElement) resultElement); } return resultElementList; } @Override public Iterator iterator() { getOrCreateResultElementList(); return resultElementList.iterator(); } public int size() { getOrCreateResultElementList(); return resultElementList == null ? 0 : resultElementList.size(); } protected void copyAttributesAndAddChildren(ResultsElement resultsElement) { if (resultsElement == null) { throw new RuntimeException("Null ResultsElement"); } XMLUtil.copyAttributesFromTo(resultsElement, this); for (ResultElement resultElement : resultsElement) { this.appendChild(resultElement); } } public void setAllResultElementNames(String name) { for (ResultElement resultElement : this) { resultElement.setName(name); } } public void setXPath(String xpath) { for (ResultElement resultElement : this) { resultElement.setXPath(xpath); } } public void addMatchAttributes(List matchList) { if (this.size() != matchList.size()) { throw new RuntimeException("name list wrong length ("+matchList.size()+") rather than ("+this.size()+")"); } int i = 0; for (ResultElement resultElement : this) { resultElement.setMatch(matchList.get(i)); // cosmetic - keeps attributes in natural order resultElement.setPost(resultElement.getPost()); i++; } } public List getExactList() { if (nameList == null) { nameList = new ArrayList(); for (ResultElement resultElement : this) { String name = resultElement.getExact(); nameList.add(name); } } return nameList; } } euclid-euclid-2.9/src/main/java/org/xmlcml/files/ResultsElementList.java-old000066400000000000000000000016621461721410700271530ustar00rootroot00000000000000package org.xmlcml.files; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.apache.log4j.Level; import org.apache.log4j.Logger; /** wrapper for collection of ResultsElement. * * @author pm286 * */ @Deprecated public class ResultsElementList implements Iterable { private static final Logger LOG = Logger.getLogger(ResultsElementList.class); static { LOG.setLevel(Level.DEBUG); } protected List resultsElementList; public ResultsElementList() { } public void add(ResultsElement resultsElement) { ensureResultsElementList(); resultsElementList.add(resultsElement); } protected void ensureResultsElementList() { if (resultsElementList == null) { resultsElementList = new ArrayList(); } } @Override public Iterator iterator() { ensureResultsElementList(); return resultsElementList.iterator(); } } euclid-euclid-2.9/src/main/java/org/xmlcml/stml/000077500000000000000000000000001461721410700215755ustar00rootroot00000000000000euclid-euclid-2.9/src/main/java/org/xmlcml/stml/AbstractSTMTool.java000077500000000000000000000014731461721410700254350ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.stml; import org.xmlcml.xml.XMLConstants; /** * a hook for the Tool machinery * * @author pmr * */ public abstract class AbstractSTMTool implements XMLConstants { }euclid-euclid-2.9/src/main/java/org/xmlcml/stml/STMLArray.java000077500000000000000000000724021461721410700242260ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.stml; import java.text.ParseException; import java.util.ArrayList; import java.util.List; import nu.xom.Attribute; import nu.xom.Element; import nu.xom.Node; import org.apache.log4j.Logger; import org.joda.time.DateTime; import org.xmlcml.euclid.EuclidRuntimeException; import org.xmlcml.euclid.IntArray; import org.xmlcml.euclid.JodaDate; import org.xmlcml.euclid.RealArray; import org.xmlcml.euclid.Util; import org.xmlcml.stml.attribute.DelimiterAttribute; import org.xmlcml.stml.attribute.DelimiterAttribute.Action; import org.xmlcml.stml.attribute.NamespaceRefAttribute; import org.xmlcml.stml.interfacex.HasArraySize; import org.xmlcml.stml.interfacex.HasDataType; import org.xmlcml.stml.interfacex.HasDelimiter; import org.xmlcml.stml.interfacex.HasDictRef; import org.xmlcml.stml.interfacex.HasUnits; /** * user-modifiable class supporting array. * autogenerated from schema use as a * shell which can be edited * */ public class STMLArray extends STMLElement implements HasUnits, HasArraySize, HasDataType, HasDictRef, HasDelimiter { private static Logger logger = Logger.getLogger(STMLArray.class); public final static String TAG = "array"; /** namespaced element name. */ private DelimiterAttribute delimiterAttribute; /** * constructor. */ public STMLArray() { super(TAG); init(); } private void init() { // ensureDelimiterAttribute(Action.RESET); } /** * constructor. * * @param old */ public STMLArray(STMLArray old) { super(old); init(); } /** * copy node . * * @return Node */ @Override public Element copy() { return new STMLArray(this); } // =========================== additional constructors // ======================== /** * formed from components. size is extracted from array dimensions sets * dataType to xsd:string cannot use default delimiter (S_SPACE) if strings * contain whitespace, so choose another delimiter * * @param array * @throws RuntimeException * strings must not contain whitespace */ public STMLArray(String[] array) throws RuntimeException { this.setArray(array); } /** * formed from components. size is extracted from array dimensions sets * dataType to xsd:string cannot use delimiter if strings contain it, so * choose another delimiter * * @param array * @param delimiter * @throws RuntimeException * strings must not contain delimiter */ public STMLArray(String[] array, String delimiter) throws RuntimeException { setDelimiter(delimiter); this.setArray(array); } /** * formed from components. size is extracted from array dimensions sets * dataType to xsd:double * * @param array */ public STMLArray(RealArray array) { this.setArray(array.getArray()); } /** * formed from components. size is extracted from array dimensions sets * dataType to xsd:double * * @param array */ public STMLArray(double[] array) { this.setArray(array); } /** * formed from components. size is extracted from array dimensions sets * dataType to xsd:double * * @param array */ public STMLArray(boolean[] array) { this.setArray(array); } /** * formed from components. size is extracted from array dimensions sets * dataType to xsd:double cannot use delimiter if strings contain it, so * choose another delimiter * * @param array * @param delimiter * @throws RuntimeException * strings must not contain delimiter */ public STMLArray(double[] array, String delimiter) throws RuntimeException { setDelimiter(delimiter); this.setArray(array); } /** * formed from components. size is extracted from array dimensions sets * dataType to xsd:integer * * @param array */ public STMLArray(DateTime[] array) { this.setArray(array); } /** * formed from components. size is extracted from array dimensions sets * dataType to xsd:integer * * @param array */ public STMLArray(int[] array) { this.setArray(array); } /** * formed from components. size is extracted from array dimensions sets * dataType to xsd:integer cannot use delimiter if strings contain it, so * choose another delimiter * * @param array * @param delimiter */ public STMLArray(int[] array, String delimiter) { setDelimiter(delimiter); this.setArray(array); } /** * creates array of given dataType * * @param dataType */ public STMLArray(String dataType) { this(); this.resetDataType(dataType); } public static STMLArray createArray(List scalarList) { STMLArray array = null; if (scalarList != null && scalarList.size() > 0) { STMLScalar scalar = scalarList.get(0); if (scalar == null) { throw new RuntimeException("Null scalar in list: " + scalarList.size()); } array = createArrayWithAttributes(scalar); } for (STMLScalar scalar : scalarList) { array.append(scalar); } array.removeWhitespaceDelimiterAttribute(); return array; } public static STMLArray createArrayWithAttributes(HasDataType hasDataType) { STMLArray array; String dataType = hasDataType.getDataType(); if (dataType == null) { throw new RuntimeException("Null data type"); } array = new STMLArray(dataType); String dictRef = hasDataType.getDictRef(); if (dictRef != null) { array.setDictRef(dictRef); } String units = ((HasUnits) hasDataType).getUnits(); if (units != null) { array.setUnits(units); } array.removeWhitespaceDelimiterAttribute(); return array; } private void setUnits(String units) { // TODO Auto-generated method stub } /** * creates array of type comptatible with scalar can be used to add * subsequently scalar contents to array does NOT add scalar contents * typical use: *
	 * List<STMLScalar> scalars; STMLArray array =
	 * STMLArray.createEmptyArray(scalars.get(0)); for (STMLScalar scalar :
	 * scalars) { array.append(scalar); }
	 * 
* * @param scalar * @return */ public STMLArray createEmptyArray(STMLScalar scalar) { STMLArray array = null; if (scalar != null && scalar.getDataType() != null) { array = new STMLArray(scalar.getDataType()); } array.removeWhitespaceDelimiterAttribute(); return array; } /** * creates a new array formed from a subset of the current array * @param start inclusive start * @param end inclusive end * @return new array of correct dataType and dictRef; null if fails * @throws IllegalArgumentException if indices are out of bounds */ public STMLArray createSubArray(int start, int end) throws IllegalArgumentException { int size = this.getSize(); if (start < 0 || end >= size || end < start) { throw new IllegalArgumentException("bad array slice indexes "+start+"/"+end+" in "+size); } String dataType = this.getDataType(); STMLArray subArray = null; if (dataType == null || dataType.equals(STMLConstants.XSD_STRING)) { String[] sout = new String[end-start+1]; String[] ss = this.getStrings(); for (int i = start; i <= end; i++) { sout[i-start] = ss[i]; } String delimiter = this.getDelimiter(); subArray = (delimiter == null) ? new STMLArray(sout) : new STMLArray(sout, delimiter); } else if (dataType.equals(STMLConstants.XSD_DOUBLE)) { RealArray realArray = new RealArray(this.getDoubles()); subArray = new STMLArray(realArray.getSubArray(start, end)); } else if (dataType.equals(STMLConstants.XSD_INTEGER)) { IntArray intArray = new IntArray(this.getInts()); subArray = new STMLArray(intArray.getSubArray(start, end).getArray()); } String dictRef = this.getDictRef(); if (dictRef != null) { subArray.setDictRef(dictRef); } return subArray; } // ====================== housekeeping methods ===================== /** * get size of array. * * @return size */ public int getArraySize() { return this.getSize(); } /** * returns array of primitive types based on dataType. * * @return double[], int[], String[] or null */ public Object getPrimitiveArray() { Object primitiveArray = null; if (XSD_DOUBLE.equals(this.getDataType())) { primitiveArray = this.getDoubles(); } else if (XSD_BOOLEAN.equals(this.getDataType())) { primitiveArray = this.getBooleans(); } else if (XSD_DATE.equals(this.getDataType())) { primitiveArray = this.getDates(); } else if (XSD_INTEGER.equals(this.getDataType())) { primitiveArray = this.getInts(); } else if (XSD_STRING.equals(this.getDataType())) { primitiveArray = this.getStrings(); } else { primitiveArray = this.getStrings(); } return primitiveArray; } /** * get strings. * * @return strings */ public String[] getStrings() { String[] ss = null; if (this.getDataType().equals(XSD_STRING)) { ss = getSplitContent(); } return ss; } /** * splits content into tokens. if delimiter is whitespace, trims content and * splits at whitespace (however long) else assume starts and ends with * delimiter * * @return the tokens * @throws RuntimeException * if size attribute is inconsistent */ private String[] getSplitContent() throws RuntimeException { String[] ss = new String[0]; String content = this.getXMLContent(); if (content != null) { content = content.trim(); ensureDelimiterAttribute(Action.PRESERVE); ss = delimiterAttribute.getSplitContent(content); int size = -1; if (this.getSizeAttribute() == null) { //size = ss.length; //setSize(size); } else { size = this.getSize(); if (ss.length != size) { // FIXME this is not yet working // throw new STMLRuntime("Bad array length: "+size+" // incompatible // with elements: "+ss.length); } } } this.removeWhitespaceDelimiterAttribute(); return ss; } /** * get doubles. * * @return doubles * @throws RuntimeException */ public boolean[] getBooleans() throws RuntimeException { boolean[] dd = null; String dataType = this.getDataType(); if (dataType != null && XSD_BOOLEAN.equals(STMLType.getNormalizedValue(dataType))) { String[] ss = getSplitContent(); dd = new boolean[ss.length]; for (int i = 0; i < dd.length; i++) { dd[i] = Boolean.parseBoolean(ss[i]); } } return dd; } /** * get doubles. * * @return doubles * @throws RuntimeException */ public double[] getDoubles() throws RuntimeException { double[] dd = null; String dataType = this.getDataType(); if (dataType != null && XSD_DOUBLE.equals(STMLType.getNormalizedValue(dataType))) { String[] ss = getSplitContent(); dd = new double[ss.length]; for (int i = 0; i < dd.length; i++) { try { dd[i] = Util.parseFlexibleDouble(ss[i]); } catch (NumberFormatException nfe) { throw new RuntimeException("Bad double :" + ss[i] + " at position: " + i, nfe); } catch (ParseException e) { throw new RuntimeException("Bad double : " + ss[i] + "at position " + i, e); } } } return dd; } /** convenience method returns a list of doubles for either xsd:integer or xsd:double * * @return */ public double[] getNumbersAsDoubles() { double[] doubles = getDoubles(); if (doubles == null) { int[] integers = getInts(); if (integers != null) { RealArray realArray = RealArray.createRealArray(integers); doubles = (realArray == null) ? null : realArray.getArray(); } } return doubles; } /** * get dates * * @return dates * @throws RuntimeException */ public DateTime[] getDates() throws RuntimeException { DateTime[] dd = null; String dataType = this.getDataType(); if (dataType != null && XSD_DATE.equals(STMLType.getNormalizedValue(dataType))) { String[] ss = getSplitContent(); dd = new DateTime[ss.length]; for (int i = 0; i < dd.length; i++) { dd[i] = JodaDate.parseDate(ss[i]); } } return dd; } /** * get ints. * * @return ints * @throws RuntimeException */ public int[] getInts() throws RuntimeException { int[] ii = null; String dataType = this.getDataType(); if (XSD_INTEGER.equals(dataType)) { String[] ss = getSplitContent(); ii = new int[ss.length]; for (int i = 0; i < ii.length; i++) { try { ii[i] = Integer.valueOf(ss[i]).intValue(); } catch (NumberFormatException nfe) { throw new RuntimeException("Bad int (" + ss[i] + ") at position: " + i); } } } return ii; } public STMLScalar getElementAt(int i) { STMLScalar scalar = null; if (i >= 0 && i < getSize()) { String dataType = this.getDataType(); if (dataType == null) { dataType = XSD_STRING; } if (dataType.equals(XSD_STRING)) { String s = getStrings()[i]; scalar = new STMLScalar(s); } else if (dataType.equals(XSD_BOOLEAN)) { Boolean b = getBooleans()[i]; scalar = new STMLScalar(b); } else if (dataType.equals(XSD_DATE)) { DateTime d = getDates()[i]; scalar = new STMLScalar(d); } else if (dataType.equals(XSD_DOUBLE)) { Double d = getDoubles()[i]; scalar = new STMLScalar(d); } else if (dataType.equals(XSD_INTEGER)) { Integer ii = getInts()[i]; scalar = new STMLScalar(ii); } STMLArray.copyAttributesFromTo(this, scalar); } return scalar; } public static void copyAttributesFromTo(Element from, Element to) { copyAttributeTo(from, to, STMLAttribute.CONSTANT_TO_SI); copyAttributeTo(from, to, STMLAttribute.CONVENTION); copyAttributeTo(from, to, STMLAttribute.DICTREF); copyAttributeTo(from, to, STMLAttribute.ID); copyAttributeTo(from, to, STMLAttribute.MULTIPLIER_TO_SI); copyAttributeTo(from, to, STMLAttribute.TITLE); copyAttributeTo(from, to, STMLAttribute.UNITS); } private static void copyAttributeTo(Element from, Element to, String attName) { String attVal = from.getAttributeValue(attName); if (attVal != null) { to.addAttribute(new Attribute(attName, attVal)); } } /** * returns the String value of the array. convenience method to avoid * repeated accesses i.e. converts int and double to string * * @return strings */ public List getStringValues() { List values = new ArrayList(); String dataType = this.getDataType(); if (dataType == null || dataType.equals(XSD_STRING)) { String[] strings = this.getStrings(); for (String s : strings) { values.add(s); } } else if (XSD_INTEGER.equals(dataType)) { int[] ints = this.getInts(); for (int i : ints) { values.add(S_EMPTY + i); } } else if (XSD_DOUBLE.equals(dataType)) { double[] doubles = this.getDoubles(); for (double d : doubles) { values.add(S_EMPTY + d); } } return values; } // ====================== subsidiary accessors ===================== /** * sets components. * * @param array * @throws RuntimeException */ public void setArray(String[] array) throws RuntimeException { resetDataType(XSD_STRING); ensureDelimiterAttribute(Action.PRESERVE); for (String s : array) { delimiterAttribute.checkDelimiter(s); } setXMLContent(delimiterAttribute.getDelimitedXMLContent(array)); resetSize(array.length); this.removeWhitespaceDelimiterAttribute(); } private void resetDataType(String type) { Attribute a = (this.getAttribute("dataType")); if (a != null) { this.removeAttribute(a); } super.setDataType(type); } private void resetSize(int size) { Attribute a = (this.getAttribute("size")); if (a != null) { this.removeAttribute(a); } super.setSize(size); } /** * sets components. NOT IMPLEMENTED * * @param array */ public void setArray(DateTime[] array) { resetDataType(XSD_DATE); ensureDelimiterAttribute(Action.PRESERVE); // setXMLContent(delimiterAttribute.getDelimitedXMLContent(array)); resetSize(array.length); throw new RuntimeException("dates in array not fully implemented"); // this.removeWhitespaceDelimiterAttribute(); } /** * sets components. * * @param array */ public void setArray(boolean[] array) { resetDataType(XSD_BOOLEAN); ensureDelimiterAttribute(Action.PRESERVE); setXMLContent(delimiterAttribute.getDelimitedXMLContent(array)); resetSize(array.length); this.removeWhitespaceDelimiterAttribute(); } /** * sets components. * * @param array */ public void setArray(double[] array) { resetDataType(XSD_DOUBLE); ensureDelimiterAttribute(Action.PRESERVE); setXMLContent(delimiterAttribute.getDelimitedXMLContent(array)); resetSize(array.length); this.removeWhitespaceDelimiterAttribute(); } /** * sets components. * * @param array */ public void setArray(int[] array) { resetDataType(XSD_INTEGER); ensureDelimiterAttribute(Action.PRESERVE); setXMLContent(delimiterAttribute.getDelimitedXMLContent(array)); resetSize(array.length); this.removeWhitespaceDelimiterAttribute(); } /** * gets size of array. * * @return int size of array */ public int getSize() { int size = -1; if (this.getSizeAttribute() != null) { size = this.getSize(); } else { String[] array = this.getSplitContent(); size = array.length; } return size; } /** * reset null to whitespace, etc. * * @return String */ public String getDelimiter() { String delimiter = super.getDelimiter(); if (delimiter == null) { ensureDelimiterAttribute(Action.RESET); delimiter = delimiterAttribute.getValue(); } this.removeWhitespaceDelimiterAttribute(); return delimiter; } /** * set delimiter. * * @param value */ public void setDelimiter(String value) { String[] old = this.getSplitContent(); ensureDelimiterAttribute(Action.RESET); this.setDelimiter(value); delimiterAttribute = (DelimiterAttribute) this.getDelimiterAttribute(); if (old.length>0) { for (String s : old) { delimiterAttribute.checkDelimiter(s); } setXMLContent(delimiterAttribute.getDelimitedXMLContent(old)); } removeWhitespaceDelimiterAttribute(); } /** * get dataType. if attribute not set, reset to String. * * @return dataType (default XSD_STRING) */ public String getDataType() { String dataType = this.getDataType(); if (dataType == null) { dataType = XSD_STRING; super.setDataType(dataType); } return STMLType.getNormalizedValue(dataType); } /** * set dataType. * * sets dataType. Cannot reset after array is populated * * @param dType * (default XSD_STRING) * @throws RuntimeException * attempt to reset datatype */ public void setDataType(String dType) { if (this.getDataTypeAttribute() != null) { throw new RuntimeException("Cannot reset dataType"); } super.setDataType(dType); } /** * set size. * * @deprecated not user-accesible - throws STMLRuntime sets delimiter. Cannot * reset after array is populated if delimiter is whitespace, * removes the attribute * @param s * the size * @throws RuntimeException * attempt to reset datatype */ public void setSize(int s) { if (this.getSizeAttribute() != null) { throw new RuntimeException("user cannot reset size"); } super.setSize(s); } // ====================== functionality ===================== /** * can two arrays be used for arithmetic. checks that both arrays are * numeric and of same dataType and of same size * * @param array * the array to test; can have different owner */ public void checkNumericConformability(STMLArray array) { String thisDataType = this.getDataType(); String arrayDataType = array.getDataType(); if (thisDataType.equals(XSD_STRING) || !thisDataType.equals(arrayDataType) || this.getSize() != array.getSize()) { throw new RuntimeException( "Unsuitable dataTypes for numeric operations / " + this.getDataType() + STMLConstants.S_SLASH + this.getSize() + STMLConstants.S_SLASH + array.getDataType() + STMLConstants.S_SLASH + array.getSize()); } } /** * subtract an array from this.. * * result = this - array, owner document = this does not alter this only * works if both arrays are numeric and of same dataType * * @param array * the array to subtract; can have different owner * @return new array */ public STMLArray subtract(STMLArray array) { checkNumericConformability(array); STMLArray resultArray = null; try { if (this.getDataType().equals(XSD_DOUBLE)) { RealArray result = new RealArray(array.getDoubles()) .subtract(new RealArray(this.getDoubles())); resultArray = new STMLArray(result.getArray()); } else if (this.getDataType().equals(XSD_INTEGER)) { IntArray result = new IntArray(array.getInts()) .subtract(new IntArray(this.getInts())); resultArray = new STMLArray(result.getArray()); } } catch (EuclidRuntimeException je) { throw new RuntimeException(S_EMPTY + je); } return resultArray; } /** * add an array to this.. * * result is this + array, owner document = this does not alter this * * only works if both arrays are numeric and of same dataType * * @param array * the array to add; can have different owner * @return the new array */ public STMLArray plus(STMLArray array) { checkNumericConformability(array); STMLArray resultArray = null; try { if (this.getDataType().equals(XSD_DOUBLE)) { RealArray result = new RealArray(this.getDoubles()) .plus(new RealArray(array.getDoubles())); resultArray = new STMLArray(result.getArray()); } else if (this.getDataType().equals(XSD_INTEGER)) { IntArray result = new IntArray(this.getInts()) .plus(new IntArray(array.getInts())); resultArray = new STMLArray(result.getArray()); } } catch (EuclidRuntimeException je) { throw new RuntimeException(S_EMPTY + je); } return resultArray; } /** * add a string. * * datatype must be unset or have been set to XSD_STRING * * @param s * String to add * * @throws RuntimeException * dataType not XSD_STRING */ public void append(String s) throws RuntimeException { String dataType = this.getDataType(); if (!XSD_STRING.equals(dataType)) { throw new RuntimeException("Cannot add string (" + s + ") to array of: " + dataType); } appendXML(s, 1); } /** * add a double. datatype must have been set to XSD_DOUBLE * * @param b * @throws RuntimeException * dataType not XSD_DOUBLE */ public void append(boolean b) throws RuntimeException { String dataType = this.getDataType(); if (!XSD_BOOLEAN.equals(dataType)) { throw new RuntimeException("Cannot add boolean to array of: " + dataType); } appendXML(S_EMPTY + b, 1); } /** * add a double. datatype must have been set to XSD_DOUBLE * * @param d * double to add * @throws RuntimeException * dataType not XSD_DOUBLE */ public void append(double d) throws RuntimeException { String dataType = this.getDataType(); if (!XSD_DOUBLE.equals(dataType)) { throw new RuntimeException("Cannot add double to array of: " + dataType); } appendXML(Double.toString(d), 1); } /** * add an integer. datatype must have been set to XSD_INTEGER * * @param i * integer to add * @throws RuntimeException * dataType not XSD_INTEGER */ public void append(int i) throws RuntimeException { String dataType = this.getDataType(); if (!XSD_INTEGER.equals(dataType)) { throw new RuntimeException("Cannot add int to array of: " + dataType); } appendXML(S_EMPTY + i, 1); } public void append(STMLArray array) { if (!this.getDataType().equals(array.getDataType())) { throw new RuntimeException( "Cannot append array of different type: " + this.getDataType() + " != " + array.getDataType()); } if (!this.getDelimiter().equals(array.getDelimiter())) { throw new RuntimeException( "Cannot append array with different delimiter: " + this.getDelimiter() + " != " + array.getDelimiter()); } if (this.getUnits() != null && !this.getUnits().equals(array.getUnits())) { throw new RuntimeException( "Cannot append array with different units: " + this.getDelimiter() + " != " + array.getUnits()); } String arrayString = array.getXMLContent(); String delimiter = this.getDelimiter().trim(); if (delimiter.length() > 0) { arrayString = arrayString.substring(1, arrayString.length() - 1); } appendXML(arrayString, array.getSize()); } private void appendXML(String s, int toAdd) { int size = (this.getSizeAttribute() == null) ? 0 : this.getSize(); ensureDelimiterAttribute(Action.PRESERVE); if (toAdd <= 1) { delimiterAttribute.checkDelimiter(s); } String xmlContent = this.getXMLContent(); String delimitedContent = delimiterAttribute.appendXMLContent( xmlContent, s); this.setXMLContent(delimitedContent); resetSize(size + toAdd); this.removeWhitespaceDelimiterAttribute(); } public void append(STMLScalar scalar) { if (scalar != null) { String dataType = this.getDataType(); if (!dataType.equals(scalar.getDataType())) { throw new RuntimeException( "Cannot append scalar of different type: " + dataType + " != " + scalar.getDataType()); } if (this.getUnits() != null && !this.getUnits().equals(scalar.getUnits())) { throw new RuntimeException( "Cannot append scalar with different units: " + this.getDelimiter() + " != " + scalar.getUnits()); } append(scalar.getXMLContent(), dataType); this.removeWhitespaceDelimiterAttribute(); } } private void append(String content, String dataType) { if (XSD_STRING.equals(dataType)) { this.append(content); } else if (XSD_BOOLEAN.equals(dataType)) { this.append(Boolean.parseBoolean(content)); } else if (XSD_DATE.equals(dataType)) { this.append(JodaDate.parseDate(content).toString()); } else if (XSD_DOUBLE.equals(dataType)) { this.append(Double.valueOf(content).doubleValue()); } else if (XSD_INTEGER.equals(dataType)) { this.append(Integer.valueOf(content).intValue()); } } public void append(HasDictRef hasDictRef) { if (hasDictRef instanceof STMLScalar) { this.append((STMLScalar) hasDictRef); } else if (hasDictRef instanceof STMLArray) { this.append((STMLArray) hasDictRef); } else { throw new RuntimeException("Cannot add HasDictRef: " + ((STMLElement) hasDictRef).getLocalName()); } this.removeWhitespaceDelimiterAttribute(); // this.debug("APPPEND"); } /** * sets units attribute. requires namespace for unit to be in scope. * * @param prefix * for namespace * @param id * for unit * @param namespaceURI * sets units namespace if not present already */ public void setUnits(String prefix, String id, String namespaceURI) { NamespaceRefAttribute.setUnits((HasUnits) this, prefix, id, namespaceURI); } /** * add an integer. datatype must have been set to XSD_DATE * * @param d * date to add */ public void append(DateTime d) throws RuntimeException { String dataType = this.getDataType(); if (!XSD_DATE.equals(dataType)) { throw new RuntimeException("Cannot add date to array of: " + dataType); } appendXML(d.toString(), 1); } /** * removes attributes of the form delimiter="" or delimiter=" " */ public void removeWhitespaceDelimiterAttribute() { STMLArray.removeWhitespaceDelimiterAttribute(this); } public static void removeWhitespaceDelimiterAttribute( HasDelimiter hasDelimiter) { Attribute delimiter = hasDelimiter.getDelimiterAttribute(); if (delimiter != null && delimiter.getValue().trim().length() == 0) { delimiter.detach(); } } /** makes a list of STMLArrays * * @param elements * @return {@code List} */ public static List extractArrays(List elements) { List arrayList = new ArrayList(); for (Element element : elements) { if (element instanceof STMLArray) { arrayList.add((STMLArray) element); } } return arrayList; } public STMLAttribute getDictRefAttribute() { // TODO Auto-generated method stub return null; } public String getDictRef() { // TODO Auto-generated method stub return null; } public void setDictRef(String dictRef) { // TODO Auto-generated method stub } public String getXMLContent() { // TODO Auto-generated method stub return null; } public void setXMLContent(String content) { // TODO Auto-generated method stub } public String getUnits() { // TODO Auto-generated method stub return null; } public void ensureDelimiterAttribute(Action action) { if (action.equals(Action.RESET)) { delimiterAttribute = null; } delimiterAttribute = (DelimiterAttribute) this.getDelimiterAttribute(); if (delimiterAttribute == null) { delimiterAttribute = new DelimiterAttribute(S_SPACE); super.setDelimiter(S_SPACE); } } /** The data type of the object. * Normally applied to scalar/array * objects but may extend to more complex one. * @return STMLAttribute */ public STMLAttribute getDataTypeAttribute() { return (STMLAttribute) getAttribute("dataType"); } } euclid-euclid-2.9/src/main/java/org/xmlcml/stml/STMLAttribute.java000077500000000000000000000205641461721410700251150ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.stml; import org.apache.log4j.Logger; import org.xmlcml.xml.XMLConstants; import nu.xom.Attribute; import nu.xom.NamespaceConflictException; /** * generic subclassed Attribute for CML elements. often further subclassed into * strongly typed attributes * * in CMLSchema an attributeGroup normally wraps the attribute. Normally * the names are the same, but sometimes the attribute group has a different * name from the attribute. In some cases two different attributes can have the same * name but different attribute groups. The attributeGroup name is only used for * collecting the attributes * @author Peter Murray-Rust * @version 5.0 * */ public class STMLAttribute extends Attribute implements XMLConstants { final static Logger logger = Logger.getLogger(STMLAttribute.class); public static final String CONSTANT_TO_SI = "constantToSI"; public final static String CONVENTION = "convention"; public static final String DICTREF = "dictRef"; public static final String ID = "id"; public static final String MULTIPLIER_TO_SI = "multiplierToSI"; public static final String TITLE = "title"; public static final String UNITS = "units"; protected STMLType cmlType; protected String summary; protected String description; protected String attributeGroupName; // used in code generation /** * creates attribute without value. do not use directly * * @param name */ public STMLAttribute(String name) { super(name, ""); } /** * creates attribute. * * @param name * @param value */ public STMLAttribute(String name, String value) { super(name, value); } /** * creates attribute. * * @param name * must be qualified (colonized) * @param URI * namespace * @param value * @throws NamespaceConflictException * probably no prefix in name */ protected STMLAttribute(String name, String URI, String value) throws nu.xom.NamespaceConflictException { super(name, URI, value); } /** * copy constructor * * @param att */ public STMLAttribute(STMLAttribute att) { super(att); this.cmlType = att.cmlType; // if (att.getLocalName().equals("dictRef")) { // new Exception().printStackTrace(); // } } /** * semi copy constructor * * @param att */ public STMLAttribute(Attribute att) { super(att); } /** * copy constructor from empty attribute. * used to create subclasses * @param att to copy * @param value to add (may throw CMLRuntime) */ protected STMLAttribute(Attribute att, String value) { this(att.getLocalName()); this.setSTMLValue(value); } /** * makes copy of correct class. * shallow copy as most fields are not mutable * @return copy of node */ public Attribute copy() { STMLAttribute newAttribute = new STMLAttribute(this); newAttribute.setValue(this.getValue()); return newAttribute; } /** * sets attributeGroup name. normally only useful when generating code when * the attributeGroup name may be different from the attribute name. it is * required for lookup * * @param agn attributeGroup name */ public void setAttributeGroupName(String agn) { attributeGroupName = agn; } /** * gets attributeGroup name. normally only useful when generating code when * the attributeGroup name may be different from the attribute name. it is * required for lookup * * @return attributeGroup name */ public String getAttributeGroupName() { return attributeGroupName; } /** * compares attributes. As we cannot override Node.equals() which compares * identity we have to compare components. order of sorting is: attribute * class cmlType name name value * * null values of any component return -1 * * @param att * to compare * @return 0 if all content is identical, -1 if this less than att, 1 if * greater value * */ public int compareTo(Attribute att) { if (att == null) { return -1; } // same attribute? if (this == att) { return 0; } int order = 0; if (!(att instanceof STMLAttribute)) { order = -1; } STMLAttribute cmlAtt = (STMLAttribute) att; // schemas must either bosth be null or equal if (order == -1) { } else if (cmlType == null && cmlAtt.cmlType == null) { } else if (cmlType != null && cmlAtt.cmlType != null) { order = this.cmlType.compareTo(cmlAtt.cmlType); } else { order = -1; } if (order == 0) { order = this.getClass().getName().compareTo( att.getClass().getName()); } if (order == 0) { order = this.getLocalName().compareTo(cmlAtt.getLocalName()); } if (order == 0) { order = this.getValue().compareTo(cmlAtt.getValue()); } return (order == 0) ? 0 : order / Math.abs(order); } /** * get JavaType default * * @return "String" */ public String getJavaType() { return "String"; } /** * get Java set method default * * @return "setSTMLValue" */ public String getJavaSetMethod() { return "setSTMLValue"; } /** * get Java get method. * * @return "getSTMLValue" */ public String getJavaGetMethod() { return "getSTMLValue"; } /** * get Java ShortClassName. * * @return "STMLAttribute" */ public String getJavaShortClassName() { return this.getClass().getSimpleName(); } /** * get schema type. * * @return "STMLAttribute" */ public STMLType getSchemaType() { return cmlType; } /** * set schema type. * * @param schemaType could be null */ public void setSchemaType(STMLType schemaType) { this.cmlType = schemaType; } /** * returns value as a typed object. if object is a primitive, return in * wrapper (e.g. Integer) object might be an array of primitives (e.g. * int[]) types are: String, String[], Integer, int[], Double, double[] * * @return the value */ public Object getSTMLValue() { return getValue(); } /** * sets value. often subclassed which will throw exception if of wrong type * * @param s * the value */ public void setSTMLValue(String s) { this.setValue(s); } /** * get documentation summary. * * @return the summary */ public String getSummary() { return summary; } /** * set documentation summary. * * @param s * the summary */ public void setSummary(String s) { if (s != null) { summary = s; if (!summary.endsWith(S_PERIOD)) { summary += STMLConstants.S_PERIOD; } } } /** * get Documentation. * * @return the description */ public String getDescription() { return description; } /** * set Documentation. * * @param d * the description */ public void setDescription(String d) { description = d; } /** * @return the cmlType */ public STMLType getCmlType() { return cmlType; } /** * @param cmlType the cmlType to set */ public void setCmlType(STMLType cmlType) { this.cmlType = cmlType; } } euclid-euclid-2.9/src/main/java/org/xmlcml/stml/STMLConstants.java000077500000000000000000000177371461721410700251360ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.stml; import nu.xom.XPathContext; import org.xmlcml.euclid.EC; import org.xmlcml.xml.XMLConstants; /** * *

* Constants *

* * @author Peter Murray-Rust * @version 5.0 * */ public interface STMLConstants extends XMLConstants { public enum Role { GROUP ; private Role() { ; } } /** list of identifiers. * not exhaustive - string values can be used) * @author pm286 * */ public enum Convention { /** CML */ ATOMARRAY("atomArray"), /** authorities */ /** not sure*/ MET3D("3DMET"), /** Anatomical Therapeutic Chemical Classification System for drugs */ ATC("ATC"), /** */ BEILSTEIN("Beilstein"), /** Chemical Abstracts*/ CAS("CAS"), /** Pubchem compound Id*/ CID("CID"), /** Harvard */ DRUGBANK("DrugBank"), /** EBI chemistry*/ CHEBI("ChEBI"), /** European chemicals*/ EINECS("EINECS"), /** */ GMELIN("Gmelin"), /** */ INCHI("InChI"), /** Int Union of Pure and Applied Chemistry */ IUPAC("IUPAC"), /** Japan database*/ KEGG("KEGG"), /** PubMed terminology*/ MESH("MeSH"), /** PubChem*/ PUBCHEM("PubChem"), /** Chemical labelling*/ RTECS("RTECS"), /** SMILES line notation*/ SMILES("SMILES"), /** other */ EXTERNAL("external"), ; /** */ public final String v; private Convention(String s) { v = s; } /** * equality to value * @param s * @return tru if match */ public boolean equals(String s) { return v == s; } } /** common units in chemistry */ public enum Units { /** mass*/ GRAM ("units:g"), /** density*/ GRAM_PER_CMCUBED("units:g.cm-3"), /** molarMass*/ GRAM_PER_MOLE("units:g.mol-1"), /** volume */ CMCUBED ("units:cm3"), /** volume */ ML ("units:ml"), /** volume */ L ("units:l"), /** amount */ MOL ("units:mol"), /** amount */ MMOL ("units:mmol"), ; /** dewisott */ public final String value; private Units(String s) { value = s; } /** * @return string */ public String toString() { return value; } }; /** element types */ /** constant */ String CMLXSD_ANNOTATION = "annotation"; /** constant */ String CMLXSD_ANY = "any"; /** constant */ String CMLXSD_APPINFO = "appinfo"; /** constant */ String CMLXSD_ATTRIBUTE = "attribute"; /** constant */ String CMLXSD_ATTRIBUTEGROUP = "attributeGroup"; /** constant */ String CMLXSD_BASE = "base"; /** constant */ String CMLXSD_CHOICE = "choice"; /** constant */ String CMLXSD_COMPLEXTYPE = "complexType"; /** constant */ String CMLXSD_DOCUMENTATION = "documentation"; /** constant */ String CMLXSD_ELEMENT = "element"; /** constant */ String CMLXSD_ENUMERATION = "enumeration"; /** constant */ String CMLXSD_EXTENSION = "extension"; /** constant */ String CMLXSD_ID = "id"; /** constant */ String CMLXSD_ITEMTYPE = "itemType"; /** constant */ String CMLXSD_LENGTH = "length"; /** constant */ String CMLXSD_LIST = "list"; /** constant */ String CMLXSD_MAXEXCLUSIVE = "maxExclusive"; /** constant */ String CMLXSD_MAXINCLUSIVE = "maxInclusive"; /** constant */ String CMLXSD_MINEXCLUSIVE = "minExclusive"; /** constant */ String CMLXSD_MININCLUSIVE = "minInclusive"; /** constant */ String CMLXSD_NAME = "name"; /** constant */ String CMLXSD_PATTERN = "pattern"; /** constant */ String CMLXSD_REF = "ref"; /** constant */ String CMLXSD_RESTRICTION = "restriction"; /** constant */ String CMLXSD_ROOT = "root"; /** constant */ String CMLXSD_SEQUENCE = "sequence"; /** constant */ String CMLXSD_SIMPLECONTENT = "simpleType"; /** constant */ String CMLXSD_SIMPLETYPE = "simpleType"; /** constant */ String CMLXSD_TEXT = "text"; /** constant */ String CMLXSD_TYPE = "type"; /** constant */ String CMLXSD_UNBOUNDED = "unbounded"; /** constant */ String CMLXSD_UNION = "union"; /** constant */ String CMLXSD_VALUE = "value"; /** constant */ String CMLXSD_ATTPREFIX = "_att_"; /** constant */ String CMLXSD_XMLCONTENT = "_xmlContent"; /** CMLX prefix (cmlx) for experimentation and development */ String CMLX_PREFIX = "cmlx"; /** root of all CML URIs */ String STML_NS_BASE = "http://www.xml-cml.org"; /** cmlx namespace */ String STMLX_NS = STML_NS_BASE+EC.U_S+"schema"+S_SLASH+CMLX_PREFIX; /** * namespace declaration for CMLx with prefix */ String CMLX_XMLNS_PREFIX = XMLNS + S_COLON + CMLX_PREFIX + S_EQUALS + S_APOS + STMLX_NS + S_APOS; /** constant */ // String CML = CML_NS; /** * cml dictionary namespace reserved */ String DICT_NS = STML_NS_BASE+EC.U_S+"dict"; /** CML prefix (cml) reserved: for several uses */ String STML_PREFIX = "stm"; /** CML prefix + colon (cml:) */ String CML_COLON = STML_PREFIX+S_COLON; /** CML prefix when used as element namespace */ String C_E = CML_COLON; /** CML prefix when used as attribute value namespace */ String C_A = CML_COLON; /** constant */ String WARNING_S = "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"; /** constant */ String STML_NS = STML_NS_BASE+EC.U_S+"schema"; /** * namespace declaration for CML without prefix */ String STML_XMLNS = XMLNS + S_EQUALS + S_APOS + STML_NS + S_APOS; /** * namespace declaration for CML with prefix */ String SML_XMLNS_PREFIX = XMLNS + S_COLON + STML_PREFIX + S_EQUALS + S_APOS + STML_NS + S_APOS; /** XPathContext for CML. */ XPathContext STML_XPATH = new XPathContext(STML_PREFIX, STML_NS); // /** // * units dictionary namespace reserved // */ // String UNIT_NS = _UNIT_NS+EC.U_S+"units"; // /** // * siUnits dictionary namespace reserved // */ // String SIUNIT_NS = _UNIT_NS+EC.U_S+"siUnits"; // /** // * unnitTypes dictionary namespace reserved // */ // String UNITTYPES_NS = _UNIT_NS+EC.U_S+"unitTypes"; // ================== crystal ================ // /** // * dictRef ids for 6 scalar children of crystal. // */ // String CRYSTAL_DICT_REFS[] = { CML_PREFIX + S_COLON + "a", // CML_PREFIX + S_COLON + "b", CML_PREFIX + S_COLON + "c", // CML_PREFIX + S_COLON + "alpha", CML_PREFIX + S_COLON + "beta", // CML_PREFIX + S_COLON + "gamma" }; // // /** // * unit refs for 6 scalar children of crystal. // */ // String[] CRYSTAL_DICT_UNITS = { CML_UNITS + S_COLON + "ang", // CML_UNITS + S_COLON + "ang", CML_UNITS + S_COLON + "ang", // CML_UNITS + S_COLON + "degree", CML_UNITS + S_COLON + "degree", // CML_UNITS + S_COLON + "degree" }; // ======= test ========== // /** // * number of dictionaries. has to be altered every time new dictionaries are // * added. // */ // int NDICT = 4; // // /** // * number of units dictionaries. has to be altered every time new units // * dictionaries are added. // */ // int NUNIT_DICT = 5; // // /** // * number of unitType dictionaries. has to be altered every time new units // * dictionaries are added. // */ // int NUNIT_TYPE_DICT = 1; //// public final String value; }euclid-euclid-2.9/src/main/java/org/xmlcml/stml/STMLElement.java000077500000000000000000000576741461721410700245570ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.stml; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.Writer; import nu.xom.Attribute; import nu.xom.Comment; import nu.xom.Document; import nu.xom.Element; import nu.xom.IllegalAddException; import nu.xom.Node; import nu.xom.ParentNode; import nu.xom.ProcessingInstruction; import nu.xom.Serializer; import nu.xom.Text; import org.apache.log4j.Logger; import org.xmlcml.euclid.Util; import org.xmlcml.stml.attribute.AttributeFactory; import org.xmlcml.stml.attribute.DelimiterAttribute; import org.xmlcml.stml.attribute.DictRefAttribute; import org.xmlcml.stml.attribute.DoubleSTAttribute; import org.xmlcml.stml.attribute.IdAttribute; import org.xmlcml.stml.attribute.IntSTAttribute; import org.xmlcml.stml.attribute.StringSTAttribute; import org.xmlcml.stml.attribute.UnitsAttribute; import org.xmlcml.xml.XMLConstants; import org.xmlcml.xml.XMLUtil; /** * base class for all STML elements * can be sorted on id attribute * @author Peter Murray-Rust * @version 5.0 * */ public class STMLElement extends Element implements XMLConstants { private final static Logger LOG = Logger.getLogger(STMLElement.class); final static String ID = "id"; private AbstractSTMTool tool; // attribute: constantToSI /** cache */ DoubleSTAttribute _att_constanttosi = null; // attribute: dataType /** cache */ StringSTAttribute _att_datatype = null; // attribute: delimiter /** cache */ DelimiterAttribute _att_delimiter = null; // attribute: dictRef /** cache */ DictRefAttribute _att_dictref = null; // attribute: id /** cache */ IdAttribute _att_id = null; // attribute: size /** cache */ IntSTAttribute _att_size = null; // attribute: units /** cache */ UnitsAttribute _att_units = null; protected static AttributeFactory attributeFactory = AttributeFactory.attributeFactory; protected STMLElement() { super("element"); init(); } /** * main constructor. * * @param name * tagname */ public STMLElement(String name) { super(name, STMLConstants.STML_NS); init(); } public static STMLElement readAndCreateSTML(Element element) { STMLElement newElement = null; String tag = element.getLocalName(); if (tag == null || tag.equals("")) { throw new RuntimeException("no tag"); } else if (tag.equals(STMLArray.TAG)) { newElement = new STMLArray(); } else if (tag.equals(STMLScalar.TAG)) { newElement = new STMLScalar(); } else { System.err.println("unsupported cml element: "+tag); } if (newElement != null) { XMLUtil.copyAttributesFromTo(element, newElement); createSubclassedChildren(element, newElement); } return newElement; } /** converts a SVG file to SVGElement * * @param file * @return */ public static STMLElement readAndCreateSTML(File file) { Element element = XMLUtil.parseQuietlyToDocument(file).getRootElement(); return (element == null) ? null : (STMLElement) readAndCreateSTML(element); } /** converts a SVG file to SVGElement * * @param is * @return */ public static STMLElement readAndCreateSTML(InputStream is) { Element element = XMLUtil.parseQuietlyToDocument(is).getRootElement(); return (element == null) ? null : (STMLElement) readAndCreateSTML(element); } protected static void createSubclassedChildren(Element oldElement, STMLElement newElement) { if (oldElement != null) { for (int i = 0; i < oldElement.getChildCount(); i++) { Node node = oldElement.getChild(i); Node newNode = null; if (node instanceof Text) { String value = node.getValue(); newNode = new Text(value); } else if (node instanceof Comment) { newNode = new Comment(node.getValue()); } else if (node instanceof ProcessingInstruction) { newNode = new ProcessingInstruction((ProcessingInstruction) node); } else if (node instanceof Element) { newNode = readAndCreateSTML((Element) node); } else { throw new RuntimeException("Cannot create new node: "+node.getClass()); } newElement.appendChild(newNode); } } } private void init() { } /** * copy constructor. copies attributes, children and properties using the * copyFoo() routines (q.v.) * * @param element */ public STMLElement(STMLElement element) { this(element.getLocalName()); copyAttributesFrom(element); copyChildrenFrom(element); } /** * copy node. * * @return node */ public Element copy() { return new STMLElement(this); } /** * copies attributes. makes subclass if necessary. * * @param element to copy from */ public void copyAttributesFrom(Element element) { for (int i = 0; i < element.getAttributeCount(); i++) { Attribute att = element.getAttribute(i); Attribute newAtt = (Attribute) att.copy(); this.addAttribute(newAtt); } } /** copies children of element make subclasses when required * * @param element to copy from */ public void copyChildrenFrom(Element element) { for (int i = 0; i < element.getChildCount(); i++) { Node childNode = element.getChild(i); Node newNode = childNode.copy(); this.appendChild(newNode); } } /** copies children of element make subclasses when required * * @param element to copy from * @param to */ public static void copyChildrenFromTo(Element element, Element to) { for (int i = 0; i < element.getChildCount(); i++) { Node childNode = element.getChild(i); Node newNode = childNode.copy(); to.appendChild(newNode); } } /** override replaceChild. * @param oldNode * @param newNode */ public void replaceChild(Node oldNode, Node newNode) { int pos = this.indexOf(oldNode); if (pos == -1) { throw new RuntimeException("Cannot replace non-child"); } newNode.detach(); this.removeChild(oldNode); this.insertChild(newNode, pos); } /** override insertChild. * if newNode has parent detach()es first * @param newNode * @param pos */ public void insertChild(Node newNode, int pos) { newNode.detach(); super.insertChild(newNode, pos); } /** re-route detach(). * to parent.removeChild(this); */ public void detach() { ParentNode parent = this.getParent(); if (parent != null) { if (parent instanceof Document) { parent.replaceChild(this, new Element("dummy")); } else { parent.removeChild(this); } } } /** override setLocalName(localName) to make it immutable. * if localname is null sets it, else no-op * @param localName */ public void setLocalName(String localName) { String lName = this.getLocalName(); if (lName == null) { super.setLocalName(localName); } } // ========================== utilities ====================== // /** * throws Exception. * * @param name * of attribute */ protected void unknownAttributeName(String name) { throw new RuntimeException("Unknown STML attribute " + name + " on " + this.getLocalName()); } protected String getSTMLAttributeValue(String name) { STMLAttribute a = (STMLAttribute) this.getAttribute(name); return (a == null) ? null : (String) a.getSTMLValue(); } /** * remove attribute. * * @param attName */ public void removeAttribute(String attName) { Attribute att = this.getAttribute(attName); if (att != null) { this.removeAttribute(att); } } /** * copy attributes from one STMLElement to another. overwrites existing * atts * * @param from * element to copy from * @param to * element to copy to */ public static void copyAttributesFromTo(Element from, Element to) { for (int i = 0; i < from.getAttributeCount(); i++) { Attribute att = from.getAttribute(i); Attribute newAtt = (att instanceof STMLAttribute) ? new STMLAttribute( (STMLAttribute) att) : new Attribute(att); to.addAttribute(newAtt); } } /** it attribute exists detach it. * @param element * @param attName */ public static void deleteAttribute(Element element, String attName) { Attribute att = element.getAttribute(attName); if (att != null) { att.detach(); } } /** debug for element. makes copy if not document root writes to sysout * @param message */ public void debug(String message) { Util.println("<<<<<<"+message+"<<<<<<"); debug(); Util.println(">>>>>>"+message+">>>>>>"); } /** debug for element. makes copy if not document root writes to sysout */ public void debug() { try { debug(System.out, 2); } catch (IOException e) { Util.BUG(e); } } /** debug for element. makes copy if not docuemnt root writes to sysout * @param indent */ public void debug(int indent) { try { debug(System.out, indent); } catch (IOException e) { Util.BUG(e); } } /** debug. * * @param os * @param indent * @throws IOException */ public void debug(OutputStream os, int indent) throws IOException { Document document; Node parent = this.getParent(); if (parent instanceof Document) { document = (Document) parent; } else { STMLElement copyElem = new STMLElement(this); document = new Document(copyElem); } Serializer serializer = new Serializer(os); serializer.setIndent(indent); // if (indent == 0) { // serializer.setLineSeparator("\r\n"); // } serializer.write(document); } /** * gets String content. only valid when there is a single Text child. not * checked against the schema, so use with care * * @return the XML text content or null */ public String getStringContent() { Node child = (this.getChildCount() == 0) ? null : this.getChild(0); String s = (child == null || !(child instanceof Text)) ? null : child .getValue(); return s; } /** * sets String content. very FRAGILE. not checked against the schema, so use with care. It * is almost always better to use the accessors generated from the schema * * @param value * the XML text content */ public void setStringContent(String value) { Text newText = new Text(value); if (this.getChildCount() == 0) { this.appendChild(newText); } else { Node child = this.getChild(0); if (child instanceof Text) { this.replaceChild(child, newText); } } } /** * write as HTML. many elements will override this method. * * @param w * writer * @throws IOException */ public void writeHTML(Writer w) throws IOException { w.write(""); w.write(this.getLocalName()); w.write(""); } /** convenience method to serialize the element. * * @param os * @param indent to indent lines by (non-zero may muck up whitespace) * @throws IOException */ public void serialize(OutputStream os, int indent) throws IOException { Document doc = new Document((STMLElement)this.copy()); Serializer serializer = new Serializer(os); serializer.write(doc); } /** convenience method to add cmlx:foo attributes. * * @param attName WITHOUT prefix * @param attValue if null removes any old attributes */ public void setSTMLXAttribute(String attName, String attValue) { if (attValue == null) { Attribute attribute = this.getAttribute(attName, STMLConstants.STMLX_NS); if (attribute != null) { this.removeAttribute(attribute); } } else { addSTMLXAttribute(this, attName, attValue); } } /** * creates a prefixed STMLX attribute (cmlx:foo="bar") on element in STMLX namespace * @param element * @param attName UNPREFIXED * @param attValue */ public static void addSTMLXAttribute(Element element, String attName, String attValue) { Attribute attribute = makeSTMLXAttribute(attName, attValue); element.addAttribute(attribute); element.addNamespaceDeclaration(STMLConstants.CMLX_PREFIX, STMLConstants.STMLX_NS); } /** convenience method to create new cmlx:foo attribute. * * @param attName WITHOUT prefix and colon * @param value if null undefined * @return */ public static Attribute makeSTMLXAttribute(String attName, String value) { return new Attribute(STMLConstants.CMLX_PREFIX+S_COLON+attName, STMLConstants.STMLX_NS, value); } /** convenience method to get value of cmlx:foo attribute. * * @param attName WITHOUT prefix */ public String getSTMLXAttribute(String attName) { String value = null; Attribute attribute = this.getAttribute(attName, STMLConstants.STMLX_NS); if (attribute != null) { value = attribute.getValue(); } return value; } /** *

* Appends a node to the children of this node. *

* * @param child node to append to this node * * @throws IllegalAddException if this node cannot have children * of this type * @throws NullPointerException if child is null * */ public void appendChild(Node child) { // if (child instanceof STMLArray) { // XMLUtil.debug((Element)child, "CHILLLL000"); // } child.detach(); int childCount = this.getChildCount(); insertChild(child, childCount); // if (child instanceof STMLArray) { // XMLUtil.debug((Element)child, "CHILLLL111"); // } } /** * @return the tool */ public AbstractSTMTool getTool() { return tool; } /** * @param tool the tool to set */ public void setTool(AbstractSTMTool tool) { this.tool = tool; } public void setDataType(String type) { // TODO Auto-generated method stub } /** * get namespace. * * @param prefix * @return namespace */ public String getNamespaceURIForPrefix(String prefix) { String namespace = null; Element current = this; while (true) { namespace = current.getNamespaceURI(prefix); if (namespace != null) { break; } Node parent = current.getParent(); if (parent == null || parent instanceof Document) { break; } current = (Element) parent; } return namespace; } /** The data type of the object. * Normally applied to scalar/array * objects but may extend to more complex one. * @return String */ public String getDataType() { StringSTAttribute att = (StringSTAttribute) this.getDataTypeAttribute(); if (att == null) { return null; } return att.getString(); } /** The data type of the object. * Normally applied to scalar/array * objects but may extend to more complex one. * @return STMLAttribute */ public STMLAttribute getDataTypeAttribute() { return (STMLAttribute) getAttribute("dataType"); } /** null * @return String */ public String getDelimiter() { DelimiterAttribute att = (DelimiterAttribute) this.getDelimiterAttribute(); if (att == null) { return null; } return att.getString(); } /** null * @return STMLAttribute */ public STMLAttribute getDelimiterAttribute() { return (STMLAttribute) getAttribute("delimiter"); } /** null * @return String */ public String getDictRef() { DictRefAttribute att = (DictRefAttribute) this.getDictRefAttribute(); if (att == null) { return null; } return att.getString(); } /** null * @param value title value * @throws RuntimeException attribute wrong value/type */ public void setDictRef(String value) throws RuntimeException { DictRefAttribute att = null; _att_dictref = (DictRefAttribute) attributeFactory.getAttribute("dictRef", "scalar"); att = new DictRefAttribute(_att_dictref); this.addRemove(att, value); } /** null * @return STMLAttribute */ public STMLAttribute getDictRefAttribute() { return (STMLAttribute) getAttribute("dictRef"); } protected void addRemove(STMLAttribute att, String value) { if (value == null || value.equals(S_EMPTY)) { this.removeAttribute(att.getLocalName()); } else if (att == null) { } else { att.setSTMLValue(value); super.addAttribute(att); } } /** Additive constant to generate SI equivalent. * The amount to add to a quantity in non-SI units to convert its representation to SI Units. This is applied *after* multiplierToSI. It is necessarily zero for SI units. * @return double */ public double getConstantToSI() { DoubleSTAttribute att = (DoubleSTAttribute) this.getConstantToSIAttribute(); if (att == null) { return Double.NaN; } return att.getDouble(); } /** Additive constant to generate SI equivalent. * The amount to add to a quantity in non-SI units to convert its representation to SI Units. This is applied *after* multiplierToSI. It is necessarily zero for SI units. * @return STMLAttribute */ public STMLAttribute getConstantToSIAttribute() { return (STMLAttribute) getAttribute("constantToSI"); } /** A reference to a convention. * There is no controlled vocabulary for conventions, but the author must ensure that the semantics are openly available and that there are mechanisms for implementation. The convention is inherited by all the subelements, * so that a convention for molecule would by default extend to its bond and atom children. This can be overwritten * if necessary by an explicit convention. * It may be useful to create conventions with namespaces (e.g. iupac:name). * Use of convention will normally require non-STMML semantics, and should be used with * caution. We would expect that conventions prefixed with "ISO" would be useful, * such as ISO8601 for dateTimes. * There is no default, but the conventions of STMML or the related language (e.g. STML) will be assumed. * @return String */ public String getConvention() { StringSTAttribute att = (StringSTAttribute) this.getConventionAttribute(); if (att == null) { return null; } return att.getString(); } /** A reference to a convention. * There is no controlled vocabulary for conventions, but the author must ensure that the semantics are openly available and that there are mechanisms for implementation. The convention is inherited by all the subelements, * so that a convention for molecule would by default extend to its bond and atom children. This can be overwritten * if necessary by an explicit convention. * It may be useful to create conventions with namespaces (e.g. iupac:name). * Use of convention will normally require non-STMML semantics, and should be used with * caution. We would expect that conventions prefixed with "ISO" would be useful, * such as ISO8601 for dateTimes. * There is no default, but the conventions of STMML or the related language (e.g. STML) will be assumed. * @return STMLAttribute */ public STMLAttribute getConventionAttribute() { return (STMLAttribute) getAttribute("convention"); } /** null * @return String */ public String getId() { IdAttribute att = (IdAttribute) this.getIdAttribute(); if (att == null) { return null; } return att.getString(); } /** null * @return STMLAttribute */ public STMLAttribute getIdAttribute() { return (STMLAttribute) getAttribute("id"); } /** null * @return String */ public String getUnits() { UnitsAttribute att = (UnitsAttribute) this.getUnitsAttribute(); if (att == null) { return null; } return att.getString(); } /** null * @return STMLAttribute */ public STMLAttribute getUnitsAttribute() { return (STMLAttribute) getAttribute("units"); } /** The size of an array or matrix. * No description * @return int */ public int getSize() { IntSTAttribute att = (IntSTAttribute) this.getSizeAttribute(); if (att == null) { throw new RuntimeException("int attribute is unset: size"); } return att.getInt(); } /** The size of an array or matrix. * No description * @return STMLAttribute */ public STMLAttribute getSizeAttribute() { return (STMLAttribute) getAttribute("size"); } /** null * @param value title value * @throws RuntimeException attribute wrong value/type */ public void setDelimiter(String value) throws RuntimeException { DelimiterAttribute att = null; if (_att_delimiter == null) { _att_delimiter = (DelimiterAttribute) attributeFactory.getAttribute("delimiter", "array"); if (_att_delimiter == null) { throw new RuntimeException("BUG: cannot process attributeGroupName : delimiter probably incompatible attributeGroupName and attributeName"); } } att = new DelimiterAttribute(_att_delimiter); this.addRemove(att, value); } /** The size of an array or matrix. * No description * @param value title value * @throws RuntimeException attribute wrong value/type */ public void setSize(int value) throws RuntimeException { if (_att_size == null) { _att_size = (IntSTAttribute) attributeFactory.getAttribute("size", "array"); if (_att_size == null) { throw new RuntimeException("BUG: cannot process attributeGroupName : size probably incompatible attributeGroupName and attributeName "); } } IntSTAttribute att = new IntSTAttribute(_att_size); super.addAttribute(att); att.setSTMLValue(value); } /** The size of an array or matrix. * No description * @param value title value * @throws RuntimeException attribute wrong value/type */ public void setSize(String value) throws RuntimeException { IntSTAttribute att = null; if (_att_size == null) { _att_size = (IntSTAttribute) attributeFactory.getAttribute("size", "array"); if (_att_size == null) { throw new RuntimeException("BUG: cannot process attributeGroupName : size probably incompatible attributeGroupName and attributeName"); } } att = new IntSTAttribute(_att_size); this.addRemove(att, value); } /** * * @param value title value * @throws RuntimeException attribute wrong value/type */ public void setXMLContent(String value) throws RuntimeException { this.removeChildren(); this.appendChild(value); } /** * * @return String */ public String getXMLContent() { String content = this.getValue(); return content; } } euclid-euclid-2.9/src/main/java/org/xmlcml/stml/STMLScalar.java000077500000000000000000000273501461721410700243570ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.stml; import java.text.ParseException; import org.apache.log4j.Logger; import org.joda.time.DateTime; import org.xmlcml.euclid.JodaDate; import org.xmlcml.euclid.Util; import org.xmlcml.stml.attribute.DictRefAttribute; import org.xmlcml.stml.attribute.NamespaceRefAttribute; import org.xmlcml.stml.interfacex.HasDataType; import org.xmlcml.stml.interfacex.HasDictRef; import org.xmlcml.stml.interfacex.HasScalar; import org.xmlcml.stml.interfacex.HasUnits; import nu.xom.Element; /** * user-modifiable class supporting scalar. * autogenerated from schema use as a * shell which can be edited * */ public class STMLScalar extends STMLElement implements HasUnits, HasScalar, HasDictRef, HasDataType { @SuppressWarnings("unused") private static Logger LOG = Logger.getLogger(STMLScalar.class); public final static String TAG = "scalar"; /** * default constructor. NOTE creates a CMLScalar with dataType = XSD_STRING * and content CMLConstants.S_EMPTY. * */ public STMLScalar() { super(TAG); init(); } void init() { // setXMLContent(S_EMPTY); } /** * constructor. * * @param old */ public STMLScalar(STMLScalar old) { super( old); } /** * copy node . * * @return Element */ public Element copy() { return new STMLScalar(this); } // =========================== additional constructors // ======================== /** * formed from components. sets dataType to xsd:boolean * * @param scalar */ public STMLScalar(Boolean scalar) { this.setValue(scalar); } /** * formed from components. sets dataType to xsd:date * * @param scalar */ public STMLScalar(DateTime scalar) { this.setValue(scalar); } /** * formed from components. sets dataType to xsd:string * * @param scalar */ public STMLScalar(String scalar) { this(); this.setValue(scalar); } /** * formed from components. sets dataType to xsd:double * * @param scalar */ public STMLScalar(double scalar) { this.setValue(scalar); } /** * formed from components. sets dataType to xsd:integer * * @param scalar */ public STMLScalar(int scalar) { this.setValue(scalar); } /** * gets boolean. dataType must be XSD_BOOLEAN. * * @return the value (null if not set) */ public Boolean getBoolean() { Boolean result = null; if (getDataType().equals(XSD_BOOLEAN)) { String content = getXMLContent(); if (content != null) { result = Boolean.parseBoolean(content); } } return result; } /** * gets real value. dataType must be XSD_DATE * * @return the value (NaN if not set) */ public DateTime getDate() { DateTime result = null; if (getDataType().equals(XSD_DATE)) { String content = getXMLContent(); if (content != null) { try { result = JodaDate.parseDate(content); } catch (Exception e) { throw new RuntimeException("bad date", e); } } } return result; } /** * gets real value. dataType must be XSD_DOUBLE. * * @return the value (NaN if not set) */ public double getDouble() { double result = Double.NaN; if (getDataType().equals(XSD_DOUBLE)) { String content = getXMLContent(); if (content != null) { try { result = (Util.parseFlexibleDouble(content)); } catch (ParseException e) { throw new RuntimeException("Bad double :" + content, e); } } } return result; } /** * gets String value. dataType must be XSD_STRING. * * @return the value (null if not set) */ public String getString() { String result = null; if (getDataType().equals(XSD_STRING)) { result = getXMLContent(); } return result; } /** * gets int value. dataType must be XSD_INTEGER. * * @return the value * @throws RuntimeException * if different type */ public int getInt() { int result = Integer.MIN_VALUE; if (getDataType().equals(XSD_INTEGER)) { String content = getXMLContent(); if (content != null && !content.trim().equals(S_EMPTY)) { try { result = Integer.parseInt(content); } catch (NumberFormatException e) { throw new RuntimeException("bad integer content: " + content); } } } else { throw new RuntimeException("wrong dataType for int " + getDataType()); } return result; } // ====================== subsidiary accessors ===================== /** * sets value to boolean.. updates dataType. * * @param scalar */ public void setValue(Boolean scalar) { setXMLContent(S_EMPTY + scalar); super.setDataType(XSD_BOOLEAN); } /** * sets value to date. updates dataType. * * @param scalar */ public void setValue(DateTime scalar) { String date = JodaDate.formatDate(scalar); setXMLContent(date); super.setDataType(XSD_DATE); } /** * sets value to String.. updates dataType. * TRIMS value * @param scalar no action if null */ public void setValue(String scalar) { if (scalar != null) { setXMLContent(scalar); super.setDataType(XSD_STRING); } } /** * sets value to String.. updates dataType. * does NOT trim value or normalize whitespace * @param scalar no action if null */ public void setValueNoTrim(String scalar) { if (scalar != null) { this.removeChildren(); this.appendChild(scalar); this.setDataType(XSD_STRING); } } /** * sets value to double. updates dataType. * * @param scalar */ public void setValue(double scalar) { setXMLContent(S_EMPTY + scalar); super.setDataType(XSD_DOUBLE); } /** * sets value to int.. updates dataType. * * @param scalar */ public void setValue(int scalar) { setXMLContent(S_EMPTY + scalar); super.setDataType(XSD_INTEGER); } /** * get dataType. if attribute not set, reset to String. * * @return dataType (default XSD_STRING) */ public String getDataType() { String dataType = super.getDataType(); if (dataType == null) { dataType = STMLConstants.XSD_STRING; super.setDataType(dataType); } return STMLType.getNormalizedValue(dataType); } /** * get class. if attribute not set, reset to String. * * @return class, default String.class */ public Class getDataTypeClass() { Class clazz = null; String dataType = getDataType(); if (XSD_STRING.equals(dataType)) { clazz = String.class; } else if (XSD_DOUBLE.equals(dataType)) { clazz = Double.class; } else if (XSD_INTEGER.equals(dataType)) { clazz = Integer.class; } else if (XSD_BOOLEAN.equals(dataType)) { clazz = Boolean.class; } else if (XSD_DATE.equals(dataType)) { clazz = DateTime.class; } else { } return clazz; } /** * set dataType. this may not be set independently of the value, so simply * throws CMLRuntime only place it is usable is within copy constructor * * @param dType * @throws RuntimeException * attempt to reset datatype */ public void setDataType(String dType) { if (this.getAttributeValue("dataType") == null) { super.setDataType(dType); } else { throw new RuntimeException( "Must not reset dataType; use SetValue(...)"); } } // ====================== functionality ===================== /** * can two scalars be used for arithmetic. checks that both scalars are * numeric and of same dataType and of same size * * @param scalar * the scalar to test; can have different owner * @throws CMLException * if not of same numeric data type */ void checkNumericConformability(STMLScalar scalar) { if (!this.getDataType().equals(scalar.getDataType())) { throw new RuntimeException( "Unsuitable dataTypes for numeric operations / " + this.getDataType() + STMLConstants.S_SLASH + scalar.getDataType()); } } /** * subtract an scalar from this.. * * result = this - scalar, owner document = this does not alter this only * works if both scalars are numeric and of same dataType * * @param scalar * the scalar to subtract; can have different owner * @return new scalar */ public STMLScalar subtract(STMLScalar scalar) { checkNumericConformability(scalar); STMLScalar resultScalar = null; if (this.getDataType().equals(XSD_DOUBLE)) { resultScalar = new STMLScalar(this.getDouble() - scalar.getDouble()); } else if (this.getDataType().equals(XSD_INTEGER)) { resultScalar = new STMLScalar(this.getInt() - scalar.getInt()); } return resultScalar; } /** * subtract an scalar from this.. * * this -= scalar, owner document = this alters this only works if both * scalars are numeric and of same dataType * * @param scalar * the scalar to subtract; can have different owner */ public void subtractEquals(STMLScalar scalar) { checkNumericConformability(scalar); if (this.getDataType().equals(XSD_DOUBLE)) { this.setValue(this.getDouble() - scalar.getDouble()); } else if (this.getDataType().equals(XSD_INTEGER)) { this.setValue(this.getInt() - scalar.getInt()); } } /** * add a scalar to this.. * * result = this + scalar does not alter this only works if both scalars are * numeric and of same dataType * * @param scalar * the scalar to add; * @return new scalar */ public STMLScalar plus(STMLScalar scalar) { checkNumericConformability(scalar); STMLScalar resultScalar = null; if (this.getDataType().equals(XSD_DOUBLE)) { resultScalar = new STMLScalar(this.getDouble() + scalar.getDouble()); } else if (this.getDataType().equals(XSD_INTEGER)) { resultScalar = new STMLScalar(this.getInt() + scalar.getInt()); } return resultScalar; } /** * subtract an scalar from this.. * * this += scalar, owner document = this alters this only works if both * scalars are numeric and of same dataType * * @param scalar * the scalar to subtract; */ public void plusEquals(STMLScalar scalar) { checkNumericConformability(scalar); if (this.getDataType().equals(XSD_DOUBLE)) { this.setValue(this.getDouble() + scalar.getDouble()); } else if (this.getDataType().equals(XSD_INTEGER)) { this.setValue(this.getInt() + scalar.getInt()); } } /** * gets dictRef OR from parent. see * DictRefAttribute.getDictRefFromElementOrParent() * * @return the attribute or null */ public DictRefAttribute getDictRefFromElementOrParent() { return DictRefAttribute.getDictRefFromElementOrParent(this); } /** * sets units attribute. requires namespace for unit to be in scope. * * @param prefix * for namespace * @param id * for unit * @param namespaceURI * sets units namespace if not present already */ public void setUnits(String prefix, String id, String namespaceURI) { NamespaceRefAttribute.setUnits((HasUnits) this, prefix, id, namespaceURI); } public Double getNumberAsDouble() { Double d = getDouble(); if (d == null || Double.isNaN(d)) { try { Integer i = getInt(); d = (double) i; } catch (Exception e) { // null } } return d; } public String getUnits() { return getAttributeValue("units"); } /** The data type of the object. * Normally applied to scalar/array * objects but may extend to more complex one. * @return CMLAttribute */ public STMLAttribute getDataTypeAttribute() { return (STMLAttribute) getAttribute("dataType"); } } euclid-euclid-2.9/src/main/java/org/xmlcml/stml/STMLType.java000077500000000000000000001107451461721410700240740ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.stml; import java.text.NumberFormat; import java.text.ParseException; import java.util.List; import nu.xom.Element; import nu.xom.Node; import org.xmlcml.euclid.Util; import org.xmlcml.xml.XMLConstants; import org.xmlcml.xml.XMLUtil; /** * *

* supports XSD and derived types generated by software and only instantiated as * singletons *

* * @author Peter Murray-Rust * @version 5.0 * */ public class STMLType implements XMLConstants { /** dewisott */ public final static String NO_BASE = "Cannot find base: "; protected String summary = ""; protected String description = ""; protected String base = null; protected String name = null; protected String id = null; protected boolean isList = false; protected String pattern = null; protected int listLength = Integer.MIN_VALUE; protected int iMinInclusive = Integer.MIN_VALUE; protected int iMinExclusive = Integer.MIN_VALUE; protected int iMaxInclusive = Integer.MAX_VALUE; protected int iMaxExclusive = Integer.MAX_VALUE; protected double dMinInclusive = Double.NaN; protected double dMinExclusive = Double.NaN; protected double dMaxInclusive = Double.NaN; protected double dMaxExclusive = Double.NaN; protected STMLType[] subTypes = new STMLType[0]; protected String[] sEnumerationValues = new String[0]; protected int[] iEnumerationValues = new int[0]; protected double[] dEnumerationValues = new double[0]; protected String javaType; protected Element restriction; protected Element union; protected Element list; private Element simpleType; /** * default. */ public STMLType() { init(); } private void init() { summary = ""; description = ""; base = null; name = null; id = null; isList = false; pattern = null; listLength = Integer.MIN_VALUE; iMinInclusive = Integer.MIN_VALUE; iMinExclusive = Integer.MIN_VALUE; iMaxInclusive = Integer.MAX_VALUE; iMaxExclusive = Integer.MAX_VALUE; dMinInclusive = Double.NaN; dMinExclusive = Double.NaN; dMaxInclusive = Double.NaN; dMaxExclusive = Double.NaN; subTypes = new STMLType[0]; sEnumerationValues = new String[0]; iEnumerationValues = new int[0]; dEnumerationValues = new double[0]; javaType = null; } /** * copy constructor. * * @param st */ public STMLType(STMLType st) { } /** * create from XSD simpleType. may have to be called repeatedly untill all * superTypes have been created * * @param simpleType */ public STMLType(Element simpleType) { init(); if (!simpleType.getLocalName().equals("simpleType")) { throw new RuntimeException( "element is not a simpleType, found: " + simpleType.getLocalName()); } this.name = simpleType.getAttributeValue("name"); this.id = simpleType.getAttributeValue("id"); this.simpleType = simpleType; createUnion(); // unions are a problem. At present we simply take the first simpleType // child if (union == null || true) { createRestriction(); createList(); createBase(); createDocumentation(); createPattern(); createLength(); createJavaType(); } } /** * create min max. */ public void createMinMaxAndEnumerations() { // if (union == null) { createMinMax(); createEnumerations(); // } } Element createUnion() { List unions = XMLUtil.getQueryNodes(simpleType, ".//" + XSD_UNION, XPATH_XSD); union = null; if (unions.size() == 1) { union = (Element) unions.get(0); } else if (unions.size() > 1) { throw new RuntimeException("More than one union"); } if (union != null) { List nodes = XMLUtil.getQueryNodes(union, "./" + XSD_SIMPLE_TYPE, XPATH_XSD); if (nodes.size() != 2) { throw new RuntimeException( "Union can only have two simpleTypes, found " + nodes.size()); } subTypes = new STMLType[nodes.size()]; int i = 0; for (Node node : nodes) { subTypes[i++] = new STMLType((Element) node); } simpleType = subTypes[0].getSimpleType(); } return union; } Element createRestriction() { List restrictions = XMLUtil.getQueryNodes(simpleType, ".//" + XSD_RESTRICTION, XPATH_XSD); restriction = null; if (restrictions.size() == 1) { restriction = (Element) restrictions.get(0); } else if (restrictions.size() > 1) { System.err.println("More than one restriction"); XMLUtil.debug(simpleType, "CMLTYPE"); } return restriction; } Element createList() { // lists are of two types: // indefinite length // // and // specific length // // // // // // List lists = null; isList = false; if (restriction != null) { lists = XMLUtil.getQueryNodes(restriction, "./" + XSD_SIMPLE_TYPE + XMLConstants.S_SLASH + XSD_LIST, XPATH_XSD); List lengths = XMLUtil.getQueryNodes(restriction, "./" + XSD_LENGTH, XPATH_XSD); if (lengths.size() == 1) { Element length = (Element) lengths.get(0); try { listLength = Integer.parseInt(length .getAttributeValue("value")); } catch (NumberFormatException nfe) { throw new RuntimeException("bad length: " + nfe); } } } else { lists = XMLUtil.getQueryNodes(simpleType, ".//" + XSD_LIST, XPATH_XSD); } if (lists.size() == 1) { list = (Element) lists.get(0); isList = true; String baseS = list.getAttributeValue("itemType"); if (baseS == null) { XMLUtil.debug(simpleType, "SIMPLE1"); throw new RuntimeException("no base for " + name); } base = baseS; } else if (lists.size() > 1) { System.err.println("More than one list"); XMLUtil.debug(simpleType, "SIMPLE2"); } return list; } String createBase() { // base may already have been given if (base == null) { String baseS = simpleType.getAttributeValue("base"); base = (restriction == null) ? null : restriction .getAttributeValue("base"); if (baseS == null && base == null) { throw new RuntimeException("No base or restriction given"); } else if (baseS != null) { if (base != null) { throw new RuntimeException( "Cannot give both base attribute and restriction"); } base = baseS; } } return base; } /** * creates javaType from base. only uses XSD builtins. Id derived from other * types has to be managed from outside this class. * * @return type */ String createJavaType() { if (javaType == null) { if (XSD_INTEGER.equals(base)) { javaType = XSD_INTEGER; } else if (XSD_NONNEGATIVEINTEGER.equals(base)) { javaType = XSD_INTEGER; iMinInclusive = 0; } else if (XSD_DOUBLE.equals(base) || XSD_FLOAT.equals(base)) { javaType = XSD_DOUBLE; } else if (XSD_STRING.equals(base)) { javaType = XSD_STRING; } else if (XSD_BOOLEAN.equals(base)) { javaType = XSD_BOOLEAN; } else { } } return javaType; } void createDocumentation() { List docs = XMLUtil.getQueryNodes(simpleType, "./" + XSD_ANNOTATION + XMLConstants.S_SLASH + XSD_DOCUMENTATION, XPATH_XSD); if (docs.size() == 0) { } else if (docs.size() == 1) { Element documentation = (Element) docs.get(0); List summarys = XMLUtil.getQueryNodes(documentation, ".//*[@class='summary']"); summary = (summarys.size() == 0) ? null : summarys.get(0) .getValue(); List descriptions = XMLUtil.getQueryNodes(documentation, ".//*[@class='description']"); description = (descriptions.size() == 0) ? null : descriptions.get( 0).getValue(); } } void createPattern() { List patterns = XMLUtil.getQueryNodes(simpleType, "./" + XSD_RESTRICTION + XMLConstants.S_SLASH + XSD_PATTERN, XPATH_XSD); if (patterns.size() > 0) { pattern = ((Element) patterns.get(0)).getAttributeValue("value"); } } void createLength() { List lengths = XMLUtil.getQueryNodes(simpleType, "./" + XSD_RESTRICTION + XMLConstants.S_SLASH + XSD_LENGTH, XPATH_XSD); if (lengths.size() > 0) { try { listLength = Integer.parseInt(((Element) lengths.get(0)) .getAttributeValue("value")); } catch (NumberFormatException e) { XMLUtil.debug(simpleType, "SIMPLE3"); throw new RuntimeException("Bad length " + e); } } } void createMinMax() { List minEx = XMLUtil.getQueryNodes(simpleType, "./" + XSD_RESTRICTION + XMLConstants.S_SLASH + XSD_MINEXCLUSIVE, XPATH_XSD); if (minEx.size() > 0) { Element elem = (Element) minEx.get(0); String value = elem.getAttributeValue("value"); if (iMinExclusive == Integer.MIN_VALUE && XSD_INTEGER.equals(javaType)) { try { iMinExclusive = Integer.parseInt(value); } catch (NumberFormatException e) { throw new RuntimeException("Bad length " + e); } } else if (Double.isNaN(dMinExclusive) && XSD_DOUBLE.equals(javaType)) { try { dMinExclusive = (Util.parseFlexibleDouble(value)); } catch (NumberFormatException e) { throw new RuntimeException("Bad length " + e); } catch (ParseException e) { throw new RuntimeException("Bad value for a double: " + value, e); } } } List maxEx = XMLUtil.getQueryNodes(simpleType, "./" + XSD_RESTRICTION + XMLConstants.S_SLASH + XSD_MAXEXCLUSIVE, XPATH_XSD); if (maxEx.size() > 0) { Element elem = (Element) maxEx.get(0); String value = elem.getAttributeValue("value"); if (iMaxExclusive == Integer.MAX_VALUE && XSD_INTEGER.equals(javaType)) { try { iMaxExclusive = Integer.parseInt(value); } catch (NumberFormatException e) { throw new RuntimeException("Bad length " + e); } } else if (Double.isNaN(dMaxExclusive) && XSD_DOUBLE.equals(javaType)) { try { dMaxExclusive = (Util.parseFlexibleDouble(value)); } catch (NumberFormatException e) { throw new RuntimeException("Bad length " + e); } catch (ParseException e) { throw new RuntimeException("Bad value for a double: " + value, e); } } } List minInc = XMLUtil.getQueryNodes(simpleType, "./" + XSD_RESTRICTION + XMLConstants.S_SLASH + XSD_MININCLUSIVE, XPATH_XSD); if (minInc.size() > 0) { Element elem = (Element) minInc.get(0); String value = elem.getAttributeValue("value"); if (iMinInclusive == Integer.MIN_VALUE && XSD_INTEGER.equals(javaType)) { try { iMinInclusive = Integer.parseInt(value); } catch (NumberFormatException e) { throw new RuntimeException("Bad length " + e); } } else if (Double.isNaN(dMinInclusive) && XSD_DOUBLE.equals(javaType)) { try { dMinInclusive = (Util.parseFlexibleDouble(value)); } catch (NumberFormatException e) { throw new RuntimeException("Bad length " + e); } catch (ParseException e) { throw new RuntimeException("Bad value for a double: " + value, e); } } } List maxInc = XMLUtil.getQueryNodes(simpleType, "./" + XSD_RESTRICTION + XMLConstants.S_SLASH + XSD_MAXINCLUSIVE, XPATH_XSD); if (maxInc.size() > 0) { Element elem = (Element) maxInc.get(0); String value = elem.getAttributeValue("value"); if (iMaxInclusive == Integer.MAX_VALUE && XSD_INTEGER.equals(javaType)) { try { iMaxInclusive = Integer.parseInt(value); } catch (NumberFormatException e) { throw new RuntimeException("Bad length " + e); } } else if (Double.isNaN(dMaxInclusive) && XSD_DOUBLE.equals(javaType)) { try { dMaxInclusive = (Util.parseFlexibleDouble(value)); } catch (NumberFormatException e) { throw new RuntimeException("Bad length " + e); } catch (ParseException e) { throw new RuntimeException("Bad value for a double: " + value, e); } } } } void createEnumerations() { List restrictions = XMLUtil.getQueryNodes(simpleType, "./" + XSD_RESTRICTION + XMLConstants.S_SLASH + XSD_ENUMERATION, XPATH_XSD); int size = restrictions.size(); if (size > 0) { int i = 0; if (XSD_INTEGER.equals(javaType)) { iEnumerationValues = new int[size]; for (Node node : restrictions) { Element restriction = (Element) node; try { iEnumerationValues[i++] = Integer.parseInt(restriction .getAttributeValue("value")); } catch (NumberFormatException nfe) { throw new RuntimeException( "Cannot parse enumeration as integer: " + nfe); } } } else if (XSD_DOUBLE.equals(javaType)) { dEnumerationValues = new double[size]; for (Node node : restrictions) { Element restriction = (Element) node; try { dEnumerationValues[i++] = NumberFormat .getNumberInstance().parse( restriction.getAttributeValue("value")) .doubleValue(); } catch (NumberFormatException nfe) { throw new RuntimeException( "Cannot parse enumeration as double: " + nfe); } catch (ParseException e) { throw new RuntimeException( "Bad value for a double: " + restriction .getAttributeValue("value"), e); } } } else if (XSD_STRING.equals(javaType)) { sEnumerationValues = new String[size]; for (Node node : restrictions) { Element restriction = (Element) node; sEnumerationValues[i++] = restriction .getAttributeValue("value"); } } } } /** * checks value of simpleType. throws CMLRuntime if value does not check * against SimpleType or is a list currently only uses pattern. fails if * type is int or double * * @param s * the string * @throws RuntimeException * wrong type or pattern fails */ public void checkValue(String s) throws RuntimeException { if (subTypes.length > 0) { for (int j = 0; j < subTypes.length; j++) { (subTypes[j]).checkValue(s); } } else { if (!base.equals(XSD_STRING)) { throw new RuntimeException("Cannot accept String for type: " + base); } if (isList) { throw new RuntimeException( "cannot accept single String for String[] list"); } checkPattern(s); checkEnumeration(s); } } /** * checks value of simpleType. throws CMLRuntime if value does not check * against SimpleType or is not a list currently only uses pattern. fails if * type is int or double * * @param ss * the strings * @throws RuntimeException * wrong type or pattern fails */ public void checkValue(String ss[]) throws RuntimeException { if (subTypes.length > 0) { for (int j = 0; j < subTypes.length; j++) { (subTypes[j]).checkValue(ss); } } else { if (!base.equals(XSD_STRING)) { throw new RuntimeException("Cannot accept String for type: " + base); } if (!isList) { throw new RuntimeException( "cannot accept a list String[] for single String"); } checkListLength(ss.length); int i = 0; try { while (i < ss.length) { checkPattern(ss[i]); checkEnumeration(ss[i]); i++; } } catch (RuntimeException e) { throw new RuntimeException("String (" + i + ")(" + ss[i] + ") fails: " + e); } } } private void checkListLength(int l) throws RuntimeException { // in many cases there is no set list length... // assume negative list length implies this? // if (listLength < Integer.MAX_VALUE && listLength != l) { if (listLength > 0 && listLength != l) { throw new RuntimeException("listLength required (" + listLength + ") incompatible with: " + l); } } /** * checks value of simpleType. throws CMLRuntime if value does not check * against SimpleType or is a list currently uses min/max/In/Exclusive fails * if type is String or double * * @param i * the int * @throws RuntimeException * wrong type or value fails */ public void checkValue(int i) throws RuntimeException { if (subTypes.length > 0) { for (int j = 0; j < subTypes.length; j++) { (subTypes[j]).checkValue(i); } } else { if (!base.equals(XSD_INTEGER)) { throw new RuntimeException("Cannot accept int for type: " + base); } if (isList) { throw new RuntimeException( "cannot accept single int for int[] list"); } checkMinMax(i); checkEnumeration(i); } } /** * checks value of simpleType. throws CMLRuntime if value does not check * against SimpleType or is not a list currently uses min/max/In/Exclusive * fails if type is String or double * * @param ii * the int * @throws RuntimeException * wrong type or value fails */ public void checkValue(int ii[]) throws RuntimeException { if (subTypes.length > 0) { for (int j = 0; j < subTypes.length; j++) { (subTypes[j]).checkValue(ii); } } else { if (!base.equals(XSD_INTEGER)) { throw new RuntimeException("Cannot accept int for type: " + base); } if (!isList) { throw new RuntimeException( "cannot accept a list int[] for single int"); } checkListLength(ii.length); int i = 0; try { while (i < ii.length) { checkMinMax(ii[i]); checkEnumeration(ii[i]); i++; } } catch (RuntimeException e) { throw new RuntimeException("int[] (" + i + ")(" + ii[i] + ") fails: " + e); } } } /** * checks value of simpleType. throws CMLRuntime if value does not check * against SimpleType or is a list currently uses min/max/In/Exclusive fails * if type is String or int * * @param d * the double * @throws RuntimeException * wrong type or value fails */ public void checkValue(double d) throws RuntimeException { if (subTypes.length > 0) { for (int j = 0; j < subTypes.length; j++) { (subTypes[j]).checkValue(d); } } else { if (!base.equals(XSD_DOUBLE)) { throw new RuntimeException("Cannot accept double for type: " + base); } if (isList) { throw new RuntimeException( "cannot accept single double for double[] list"); } checkMinMax(d); checkEnumeration(d); } } /** * checks value of simpleType. throws CMLRuntime if value does not check * against SimpleType or is not a list currently uses min/max/In/Exclusive * fails if type is String or int * * @param dd * the double * @throws RuntimeException * wrong type or value fails */ public void checkValue(double dd[]) throws RuntimeException { if (subTypes.length > 0) { for (int j = 0; j < subTypes.length; j++) { (subTypes[j]).checkValue(dd); } } else { if (!base.equals(XSD_DOUBLE)) { throw new RuntimeException("Cannot accept String for type: " + base); } if (!isList) { throw new RuntimeException( "cannot accept a list double[] for single double"); } checkListLength(dd.length); int i = 0; try { while (i < dd.length) { checkMinMax(dd[i]); checkEnumeration(dd[i]); i++; } } catch (RuntimeException e) { throw new RuntimeException("double[] (" + i + ")(" + dd[i] + ") fails: " + e); } } } /** * checks value of simpleType. throws CMLRuntime if value does not check * against SimpleType or is a list fails if type is not boolean * * @param b * the boolean * @throws RuntimeException * wrong type or value fails */ public void checkValue(boolean b) throws RuntimeException { if (subTypes.length > 0) { for (int j = 0; j < subTypes.length; j++) { (subTypes[j]).checkValue(b); } } else { if (!base.equals(XSD_BOOLEAN)) { throw new RuntimeException( "Cannot accept boolean for type: " + base); } if (isList) { throw new RuntimeException( "cannot accept single boolean for boolean[] list"); } } } /** * checks value of simpleType. throws CMLRuntime if value does not check * against SimpleType or is not a list fails if type is not boolean * * @param bb * the boolean array * @throws RuntimeException * wrong type or value fails */ public void checkValue(boolean bb[]) throws RuntimeException { if (subTypes.length > 0) { for (int j = 0; j < subTypes.length; j++) { (subTypes[j]).checkValue(bb); } } else { if (!base.equals(XSD_BOOLEAN)) { throw new RuntimeException( "Cannot accept boolean for type: " + base); } if (!isList) { throw new RuntimeException( "cannot accept a list boolean[] for single boolean"); } checkListLength(bb.length); } } /** * get name. * * @return name */ public String getName() { return this.name; } /** * set name. * * @param name */ public void setName(String name) { this.name = name; } /** * get Base. * * @return base */ public String getBase() { return this.base; } /** * set base. * * @param base */ public void setBase(String base) { this.base = base; } /** * set id. * * @param id */ public void setId(String id) { this.id = id; } /** * returns whether ST uses a list. * * @return true if list. */ public boolean getIsList() { return isList; } /** * set is list. * * @param b */ public void setIsList(boolean b) { this.isList = b; } /** * set pattern. * * @param p */ public void setPattern(String p) { this.pattern = p; } /** * get pattern. * * @return pattern */ public String getPattern() { return this.pattern; } /** * get list length. * * @return length */ public int getListLength() { return this.listLength; } /** * set list length. * * @param l */ public void setListLength(int l) { this.listLength = l; } /** * set min inclusive. * * @param i */ public void setMinInclusive(int i) { this.iMinInclusive = i; } /** * set min exclusive. * * @param i */ public void setMinExclusive(int i) { this.iMinExclusive = i; } /** * set max inclusive. * * @param i */ public void setMaxInclusive(int i) { this.iMaxInclusive = i; } /** * set max exclusive. * * @param i */ public void setMaxExclusive(int i) { this.iMaxExclusive = i; } /** * set min exclusive. * * @param d */ public void setMinInclusive(double d) { this.dMinInclusive = d; } /** * set min exclusive. * * @param d */ public void setMinExclusive(double d) { this.dMinExclusive = d; } /** * set max inclusive. * * @param d */ public void setMaxInclusive(double d) { this.dMaxInclusive = d; } /** * set max exclusive. * * @param d */ public void setMaxExclusive(double d) { this.dMaxExclusive = d; } /** * get int min inclusive. * * @return min */ public int getIntMinInclusive() { return this.iMinInclusive; } /** * get int min exclusive. * * @return min */ public int getIntMinExclusive() { return this.iMinExclusive; } /** * get int max inclusive. * * @return max */ public int getIntMaxInclusive() { return this.iMaxInclusive; } /** * get int max exclusive. * * @return int */ public int getIntMaxExclusive() { return this.iMaxExclusive; } /** * get double min inclusive. * * @return min */ public double getDoubleMinInclusive() { return this.dMinInclusive; } /** * get double min exclusive. * * @return min */ public double getDoubleMinExclusive() { return this.dMinExclusive; } /** * get double max inclusive. * * @return max */ public double getDoubleMaxInclusive() { return this.dMaxInclusive; } /** * get double max exclusive. * * @return max */ public double getDoubleMaxExclusive() { return this.dMaxExclusive; } /** * set subtypes. * * @param st */ public void setSubTypes(STMLType[] st) { this.subTypes = new STMLType[st.length]; for (int i = 0; i < st.length; i++) { subTypes[i] = st[i]; } } // protected Elements subTypes = null; /** * set enumeration. * * @param ss */ public void setEnumeration(String[] ss) { this.sEnumerationValues = ss; } /** * set enumeration. * * @param ii */ public void setEnumeration(int[] ii) { this.iEnumerationValues = ii; } /** * set enumeration. * * @param dd */ public void setEnumeration(double[] dd) { this.dEnumerationValues = dd; } /** * get string enumeration. * * @return enumeration */ public String[] getStringEnumeration() { return this.sEnumerationValues; } /** * get int enumeration. * * @return enumeration */ public int[] getIntEnumeration() { return this.iEnumerationValues; } /** * get double enumeration. * * @return enumeration */ public double[] getDoubleEnumeration() { return this.dEnumerationValues; } private void checkPattern(String s) throws RuntimeException { if (s == null) { throw new RuntimeException("Null strings not allowed"); } if (pattern != null && !s.matches(pattern)) { throw new RuntimeException("String (" + s + ") does not match pattern (" + pattern + ") for " + name); } } private void checkMinMax(int i) throws RuntimeException { if (iMinInclusive > Integer.MIN_VALUE && i < iMinInclusive) { throw new RuntimeException("int (" + i + ") less than " + iMinInclusive); } if (iMaxInclusive < Integer.MAX_VALUE && i > iMaxInclusive) { throw new RuntimeException("int (" + i + ") greater than " + iMaxInclusive); } if (iMinExclusive > Integer.MIN_VALUE && i <= iMinExclusive) { throw new RuntimeException("int (" + i + ") less than equals " + iMinExclusive); } if (iMaxExclusive < Integer.MAX_VALUE && i >= iMaxExclusive) { throw new RuntimeException("int (" + i + ") greater than equals " + iMaxExclusive); } } private void checkMinMax(double d) throws RuntimeException { if (!Double.isNaN(dMinInclusive) && d < dMinInclusive) { throw new RuntimeException("double (" + d + ") less than " + dMinInclusive); } if (!Double.isNaN(dMaxInclusive) && d > dMaxInclusive) { throw new RuntimeException("double (" + d + ") greater than " + dMaxInclusive); } if (!Double.isNaN(dMinExclusive) && d <= dMinExclusive) { throw new RuntimeException("double (" + d + ") less than equals " + dMinExclusive); } if (!Double.isNaN(dMaxExclusive) && d >= dMaxExclusive) { throw new RuntimeException("double (" + d + ") greater than equals " + dMaxExclusive); } } private void checkEnumeration(int i) throws RuntimeException { if (iEnumerationValues.length != 0) { boolean ok = false; for (int j = 0; j < iEnumerationValues.length; j++) { if (i == iEnumerationValues[j]) { ok = true; break; } } if (!ok) { throw new RuntimeException("int (" + i + ") not contained in enumeration"); } } } private void checkEnumeration(double d) throws RuntimeException { if (dEnumerationValues.length != 0) { boolean ok = false; for (int j = 0; j < dEnumerationValues.length; j++) { if (d == dEnumerationValues[j]) { ok = true; break; } } if (!ok) { throw new RuntimeException("double (" + d + ") not contained in enumeration"); } } } private void checkEnumeration(String s) throws RuntimeException { if (s == null) { throw new RuntimeException( "Null String cannot be checked against enumeration"); } if (dEnumerationValues.length != 0) { boolean ok = false; for (int j = 0; j < sEnumerationValues.length; j++) { if (s.equals(sEnumerationValues[j])) { ok = true; break; } } if (!ok) { throw new RuntimeException("String (" + s + ") not contained in enumeration"); } } } /** * compares cmlType. uses name only as we expect to have singleton CMLTypes * null values of any component return -1 * * @param type * to compare * @return 0 if all content is identical, -1 if this less than att, 1 if * greater value * */ public int compareTo(STMLType type) { return name.compareTo(type.name); } /** * get summary. * * @return summary */ public String getSummary() { return summary; } /** * set summary. * * @param s */ public void setSummary(String s) { summary = s.trim(); if (summary.length() != 0 && !summary.endsWith(S_PERIOD)) { summary += XMLConstants.S_PERIOD; } } /** * get description. * * @return description */ public String getDescription() { return description; } /** * get full description. * * @return description */ public String getFullDescription() { String desc = ""; if (subTypes.length > 0) { // desc = "**************** UNION ****************\n"; for (STMLType subType : subTypes) { desc += "********SUBTYPE*********\n"; desc += subType.getFullDescription(); } } else { if (summary != null && !summary.trim().equals("")) { desc += "\n.... " + summary; } if (description != null && !description.equals("")) { desc += "\n_______________________________________\n" + description + "\n__________________________________________\n"; } if (pattern != null) { desc += "\n Pattern: " + pattern; } if (listLength != Integer.MIN_VALUE) { desc += "\nLength: " + listLength; } boolean min = false; if (iMinInclusive != Integer.MIN_VALUE) { desc += "\nMinInclusive: " + iMinInclusive; min = true; } if (iMinExclusive != Integer.MIN_VALUE) { desc += "\nMinExclusive: " + iMinExclusive; min = true; } if (!Double.isNaN(dMinInclusive)) { desc += "\nMinInclusive: " + dMinInclusive; min = true; } if (!Double.isNaN(dMinExclusive)) { desc += "\nMinExclusive: " + dMinExclusive; min = true; } if (iMaxInclusive != Integer.MAX_VALUE) { desc += ((min) ? "" : "\n") + " MaxInclusive: " + iMaxInclusive; } if (iMaxExclusive != Integer.MAX_VALUE) { desc += ((min) ? "" : "\n") + " MaxExclusive: " + iMaxExclusive; } if (!Double.isNaN(dMaxInclusive)) { desc += ((min) ? "" : "\n") + " MaxInclusive: " + dMaxInclusive; } if (!Double.isNaN(dMaxExclusive)) { desc += ((min) ? "" : "\n") + " MaxExclusive: " + dMaxExclusive; } if (sEnumerationValues.length > 0) { desc += "\nPermitted String values:"; for (int i = 0; i < sEnumerationValues.length; i++) { desc += "\n " + sEnumerationValues[i]; } } if (iEnumerationValues.length > 0) { desc += "\nPermitted integer values:"; for (int i = 0; i < iEnumerationValues.length; i++) { desc += "\n " + iEnumerationValues[i]; } } if (dEnumerationValues.length > 0) { desc += "\nPermitted double values:"; for (int i = 0; i < dEnumerationValues.length; i++) { desc += "\n " + dEnumerationValues[i]; } } } return desc; } /** * set description. * * @param d */ public void setDescription(String d) { description = d; } /** * to string. * * @return string */ public String toString() { String s = "Name: " + name + "\n"; if (union != null) { s += ".....UNION: " + subTypes.length; } else { s += this.getFullDescription(); s += "\n"; } return s; } /** * @return the dEnumerationValues */ public double[] getDEnumerationValues() { return dEnumerationValues; } /** * @param enumerationValues * the dEnumerationValues to set */ public void setDEnumerationValues(double[] enumerationValues) { dEnumerationValues = enumerationValues; } /** * @return the dMaxExclusive */ public double getDMaxExclusive() { return dMaxExclusive; } /** * @param maxExclusive * the dMaxExclusive to set */ public void setDMaxExclusive(double maxExclusive) { dMaxExclusive = maxExclusive; } /** * @return the dMaxInclusive */ public double getDMaxInclusive() { return dMaxInclusive; } /** * @param maxInclusive * the dMaxInclusive to set */ public void setDMaxInclusive(double maxInclusive) { dMaxInclusive = maxInclusive; } /** * @return the dMinExclusive */ public double getDMinExclusive() { return dMinExclusive; } /** * @param minExclusive * the dMinExclusive to set */ public void setDMinExclusive(double minExclusive) { dMinExclusive = minExclusive; } /** * @return the dMinInclusive */ public double getDMinInclusive() { return dMinInclusive; } /** * @param minInclusive * the dMinInclusive to set */ public void setDMinInclusive(double minInclusive) { dMinInclusive = minInclusive; } /** * @return the iEnumerationValues */ public int[] getIEnumerationValues() { return iEnumerationValues; } /** * @param enumerationValues * the iEnumerationValues to set */ public void setIEnumerationValues(int[] enumerationValues) { iEnumerationValues = enumerationValues; } /** * @return the iMaxExclusive */ public int getIMaxExclusive() { return iMaxExclusive; } /** * @param maxExclusive * the iMaxExclusive to set */ public void setIMaxExclusive(int maxExclusive) { iMaxExclusive = maxExclusive; } /** * @return the iMaxInclusive */ public int getIMaxInclusive() { return iMaxInclusive; } /** * @param maxInclusive * the iMaxInclusive to set */ public void setIMaxInclusive(int maxInclusive) { iMaxInclusive = maxInclusive; } /** * @return the iMinExclusive */ public int getIMinExclusive() { return iMinExclusive; } /** * @param minExclusive * the iMinExclusive to set */ public void setIMinExclusive(int minExclusive) { iMinExclusive = minExclusive; } /** * @return the iMinInclusive */ public int getIMinInclusive() { return iMinInclusive; } /** * @param minInclusive * the iMinInclusive to set */ public void setIMinInclusive(int minInclusive) { iMinInclusive = minInclusive; } /** * @return the javaType */ public String getJavaType() { return javaType; } /** * @param javaType * the javaType to set */ public void setJavaType(String javaType) { this.javaType = javaType; } /** * @return the list */ public Element getList() { return list; } /** * @param list * the list to set */ public void setList(Element list) { this.list = list; } /** * @return the restriction */ public Element getRestriction() { return restriction; } /** * @param restriction * the restriction to set */ public void setRestriction(Element restriction) { this.restriction = restriction; } /** * @return the sEnumerationValues */ public String[] getSEnumerationValues() { return sEnumerationValues; } /** * @param enumerationValues * the sEnumerationValues to set */ public void setSEnumerationValues(String[] enumerationValues) { sEnumerationValues = enumerationValues; } /** * @return the simpleType */ public Element getSimpleType() { return simpleType; } /** * @param simpleType * the simpleType to set */ public void setSimpleType(Element simpleType) { this.simpleType = simpleType; } /** * @return the union */ public Element getUnion() { return union; } /** * @param union * the union to set */ public void setUnion(Element union) { this.union = union; } /** * @return the id */ public String getId() { return id; } /** * @return the subTypes */ public STMLType[] getSubTypes() { return subTypes; } /** * @param isList * the isList to set */ public void setList(boolean isList) { this.isList = isList; } /** * get data type of list. * * @return type */ public String listDataType() { String s = " ... base " + this.base; s += " (java: " + javaType + ") "; if (isList) { s += " [" + ((this.listLength >= 0) ? this.listLength : "*") + "]"; } return s; } /** * maps datatypes onto simpler values. mainly maps float, real, etc. to * XSD_FLOAT * * @param value * @return normalized value */ public static String getNormalizedValue(String value) { String dataType = null; if (value == null || value.trim().equals("") || value.equals(XSD_STRING)) { dataType = XSD_STRING; } else { value = value.trim(); if (value.equals(XSD_INTEGER)) { dataType = XSD_INTEGER; } else if (value.equals(XSD_FLOAT) || value.equals(FPX_REAL) || value.equals(XSD_DOUBLE)) { dataType = XSD_DOUBLE; } else if (value.equals(XSD_DATE)) { dataType = XSD_DATE; } else { throw new RuntimeException("Unknown data type: " + value); } } return dataType; } } euclid-euclid-2.9/src/main/java/org/xmlcml/stml/attribute/000077500000000000000000000000001461721410700236005ustar00rootroot00000000000000euclid-euclid-2.9/src/main/java/org/xmlcml/stml/attribute/AttributeFactory.java000077500000000000000000000030741461721410700277450ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * */ package org.xmlcml.stml.attribute; import org.apache.log4j.Logger; import org.xmlcml.stml.STMLAttribute; import org.xmlcml.stml.STMLConstants; /** * @author pm286 * */ public class AttributeFactory implements STMLConstants { private static final String DELIMITER = "delimiter"; private static final String SIZE = "size"; private static final String SCALAR = "scalar"; private static final String ARRAY = "array"; final static Logger LOG = Logger.getLogger(AttributeFactory.class); // singleton /** singleton attribute factory */ public final static AttributeFactory attributeFactory = new AttributeFactory(); public STMLAttribute getAttribute(String name, String extent) { if (false) { } else if (DELIMITER.equals(name) && ARRAY.equals(extent)) { return new DelimiterAttribute(name); // } else if (SIZE.equals(name) && ARRAY.equals(extent)) { // return new SizeAttribute(name); } return null; } } euclid-euclid-2.9/src/main/java/org/xmlcml/stml/attribute/DelimiterAttribute.java000077500000000000000000000166651461721410700302660ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.stml.attribute; import nu.xom.Attribute; import org.xmlcml.euclid.Util; /** * user-modifiable class supporting "ref", a pointer * to a STML object. default is to clone the object and then process it. * For that reason the referenced species should be pre-declared. */ public class DelimiterAttribute extends StringSTAttribute { /** action when ensuringAttribute * * @author pm286 * */ public enum Action { /** reset to null */ RESET, /** preserve existing */ PRESERVE; private Action() { } } /** dewisott */ public final static String NAME = "delimiter"; private String splitter = null; private String concat = null; /** * constructor. * */ public DelimiterAttribute() { super(NAME); } /** constructor. * @param value */ public DelimiterAttribute(String value) { super(NAME); this.setSTMLValue(value); } /** * constructor. * * @param att * @exception RuntimeException */ public DelimiterAttribute(Attribute att) throws RuntimeException { super(att); this.setSTMLValue(att.getValue()); } /** set value and process. * * @param value * @exception RuntimeException bad value */ public void setSTMLValue(String value) throws RuntimeException { if (value == null) { value = S_SPACE; } value = value.trim(); if (value.equals(S_EMPTY)) { value = S_SPACE; } else if (value.length() > 1) { throw new RuntimeException("Non-whitespace delimiter must only be single character"); } super.setSTMLValue(value); if (value.equals(S_SPACE)) { setSplitter(S_WHITEREGEX); setConcat(S_SPACE); } else { setSplitter(getEscapedDelimiter(value)); setConcat(value); } } /** * @return the concat */ public String getConcat() { return concat; } /** * @param concat the concat to set */ public void setConcat(String concat) { this.concat = concat; } /** * @return the splitter */ public String getSplitter() { return splitter; } /** * @param splitter the splitter to set */ public void setSplitter(String splitter) { this.splitter = splitter; } /** * adds escape for regex metacharacters. e.g. '|' transforms to '\\|' * * @param delim * @return the escaped string */ private static String getEscapedDelimiter(String delim) { String delim1 = delim; if (delim.length() == 1) { // FIXME - need to add other regex characters if (delim.equals(S_PIPE) || delim.equals(S_QUERY) || delim.equals(S_STAR) || delim.equals(S_PERIOD)) { delim1 = "\\" + delim; } } return delim1; } /** * * @param content * @return split strings */ public String[] getSplitContent(String content) { String[] ss = new String[0]; content = content.trim(); if (content.length() > 0) { if (!isWhitespace()) { if (content.startsWith(concat)) { content = content.substring(1); } if (content.endsWith(concat)) { content = content.substring(0, content.length() - concat.length()); } } ss = content.split(splitter); } return ss; } /** * checks that components does not clash with delimiter. * * @param s string to check * @throws RuntimeException if d is part of s */ public void checkDelimiter(String s) throws RuntimeException { if (s.split(splitter).length > 1) { throw new RuntimeException("cannot delimit {" + s + "} with {" + concat + S_RCURLY); } } /** * set text content. if delimiter is not whitespace, prepend and append it * * @param s * @return string */ public String getDelimitedXMLContent(String s) { if (s == null) { s = S_EMPTY; } if (!s.equals(S_EMPTY)) { if (!isWhitespace()) { if (!s.startsWith(concat)) { s = concat + s; } if (!s.endsWith(concat)) { s += concat; } } } return s; } /** * append to text content. if delimiter is not whitespace, prepend and * append it * * @param s previous string * @param snew to append * @return xml content */ public String appendXMLContent(String s, String snew) { s = getDelimitedXMLContent(s); if (!isWhitespace()) { if (s.length() == 0) { s = concat + snew + concat; } else { s += (snew + concat); } } else { s += (concat + snew); } return s; } private boolean isWhitespace() { return S_WHITEREGEX.equals(splitter); } /** set text content. if delimiter is not whitespace, prepend and append it * * @param ss * @return string */ public String getDelimitedXMLContent(String[] ss) { for (String s : ss) { checkDelimiter(s); } String s = Util.concatenate(ss, concat); if (!isWhitespace()) { s = concat + s + concat; } return s; } /** set double content. if delimiter is not whitespace, prepend and append it * * @param bb array of booleans * @return string */ public String getDelimitedXMLContent(boolean[] bb) { for (boolean b: bb) { checkDelimiter(S_EMPTY+b); } String s = Util.concatenate(bb, concat); if (!isWhitespace()) { s = concat + s + concat; } return s; } /** set double content. if delimiter is not whitespace, prepend and append it * * @param dd array of doubles * @return string */ public String getDelimitedXMLContent(double[] dd) { for (double d: dd) { checkDelimiter(S_EMPTY+d); } String s = Util.concatenate(dd, concat); if (!isWhitespace()) { s = concat + s + concat; } return s; } /** set int content. if delimiter is not whitespace, prepend and append it * * @param ii int array * @return content */ public String getDelimitedXMLContent(int[] ii) { for (int i : ii) { checkDelimiter(S_EMPTY+i); } String s = Util.concatenate(ii, concat); if (!isWhitespace()) { s = concat + s + concat; } return s; } /** debug. * * @param s name of debug output */ public void debug(String s) { Util.println("-------- "+s+" -------"); Util.println(this+" .. "+this.getValue()+" .. "+this.splitter+" .. "+concat); } } euclid-euclid-2.9/src/main/java/org/xmlcml/stml/attribute/DictRefAttribute.java000077500000000000000000000055111461721410700276540ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.stml.attribute; import nu.xom.Attribute; import nu.xom.Element; import nu.xom.Node; import org.xmlcml.stml.STMLConstants; import org.xmlcml.stml.STMLElement; /** * user-modifiable class supporting DictRefAttribute. supports dictRef attribute */ public class DictRefAttribute extends NamespaceRefAttribute { /** */ public final static String NAME = "dictRef"; /** * constructor. * */ public DictRefAttribute() { super(NAME); } // /** // * constructor. // * // * @param name // * @param value // */ // public DictRefAttribute(String name, String value) { // super(NAME, value); // } /** * constructor. * * @param att */ public DictRefAttribute(Attribute att) { super(att); } /** * gets dictRef attribute from element or its parent. elements which might * carry dictRef such as scalar may be contained within a parent such as * property. In this case the dictRef may be found on the parent. This * routine returns whichever is not null * * @param el * the element * @return the attribute */ public static DictRefAttribute getDictRefFromElementOrParent(STMLElement el) { DictRefAttribute dictRefAttribute = (DictRefAttribute) el.getAttribute(NAME); if (dictRefAttribute == null) { Node parent = el.getParent(); if (parent instanceof STMLElement) { STMLElement parentElement = (STMLElement) parent; dictRefAttribute = (DictRefAttribute) parentElement.getAttribute(NAME); } } return dictRefAttribute; } /** * gets local value of dictRef value on element * eg dictRef="a:b" returns b * @param element * @return null id no dictRef ; value if no prefix */ public static String getLocalValue(Element element) { Attribute att = element.getAttribute(NAME); String value = (att == null) ? null : att.getValue(); String[] values = (value == null) ? null : value.split(STMLConstants.S_COLON); return (values == null) ? null : values[values.length-1]; } } euclid-euclid-2.9/src/main/java/org/xmlcml/stml/attribute/DoubleArraySTAttribute.java000077500000000000000000000112011461721410700310050ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.stml.attribute; import java.text.ParseException; import nu.xom.Attribute; import nu.xom.Node; import org.xmlcml.euclid.Util; import org.xmlcml.stml.STMLAttribute; /** * attribute representing an array of doubles. */ public class DoubleArraySTAttribute extends STMLAttribute { /** dewisott */ public final static String JAVA_TYPE = "double[]"; /** dewisott */ public final static String JAVA_GET_METHOD = "getDoubleArray"; /** dewisott */ public final static String JAVA_SHORT_CLASS = "DoubleArraySTAttribute"; protected double[] dd = null; protected int length = -1; /** * constructor. * * @param name */ public DoubleArraySTAttribute(String name) { super(name); } /** * construct from existing attribute. * * @param att */ public DoubleArraySTAttribute(Attribute att) { this(att.getLocalName()); this.setSTMLValue(att.getValue()); } /** * from DOM. * * @param att * to copy, except value * @param value */ public DoubleArraySTAttribute(Attribute att, String value) { super(att, value.trim().replace(S_WHITEREGEX, S_SPACE)); } /** * copy constructor * * @param att */ public DoubleArraySTAttribute(DoubleArraySTAttribute att) { super(att); if (att.dd != null) { this.dd = new double[att.dd.length]; for (int i = 0; i < dd.length; i++) { this.dd[i] = att.dd[i]; } } this.length = att.length; } /** * copy. uses copy constructor. * * @return copy */ public Attribute copy() { return new DoubleArraySTAttribute(this); } /** * sets value. throws exception if of wrong type or violates restriction * * @param s * the value */ public void setSTMLValue(String s) { if (s != null && !s.trim().equals(S_EMPTY)) { double[] dd = split(s.trim().replace(S_WHITEREGEX, S_SPACE), S_WHITEREGEX); this.setSTMLValue(dd); } } /** * set and check value. * * @param dd * @throws RuntimeException */ public void setSTMLValue(double[] dd) throws RuntimeException { checkValue(dd); this.dd = new double[dd.length]; for (int i = 0; i < dd.length; i++) { this.dd[i] = dd[i]; } this.setValue(Util.concatenate(dd, S_SPACE)); } /** * checks value of simpleType. if value does not check * against SimpleType uses STMLType.checkvalue() fails if type is String or * int or is not a list * * @param dd * the double array * @throws RuntimeException * wrong type or value fails */ public void checkValue(double[] dd) throws RuntimeException { if (cmlType != null) { cmlType.checkValue(dd); } } /** * splits string into doubles. * * @param s * the string * @param delim * delimiter (if null defaults to S_SPACE); * @throws RuntimeException * If the doubles have bad values. * @return split doubles */ public static double[] split(String s, String delim) { String sss = s; if (delim == null || delim.trim().equals(S_EMPTY) || delim.equals(S_WHITEREGEX)) { delim = S_WHITEREGEX; sss = sss.trim(); } else { } String[] ss = sss.split(delim); double[] dd = new double[ss.length]; for (int i = 0; i < ss.length; i++) { try { dd[i] = (Util.parseFlexibleDouble(ss[i])); } catch (NumberFormatException nfe) { throw new RuntimeException(S_EMPTY + nfe); } catch (ParseException e) { throw new RuntimeException("Bad double value: " + ss[i] + " at " + i +" in "+ sss, e); } } return dd; } /** * get array. * * @return null if not set */ public Object getSTMLValue() { return dd; } /** * get array. * * @return null if not set */ public double[] getDoubleArray() { return dd; } /** * get Java type. * * @return type */ public String getJavaType() { return JAVA_TYPE; } /** * get method. * * @return method */ public String getJavaGetMethod() { return JAVA_GET_METHOD; } /** * get short class name. * * @return classname */ public String getJavaShortClassName() { return JAVA_SHORT_CLASS; } }; euclid-euclid-2.9/src/main/java/org/xmlcml/stml/attribute/DoubleSTAttribute.java000077500000000000000000000072101461721410700300130ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.stml.attribute; import java.text.ParseException; import org.xmlcml.euclid.Util; import org.xmlcml.stml.STMLAttribute; import nu.xom.Attribute; import nu.xom.Node; /** * attribute representing a double value. * */ public class DoubleSTAttribute extends STMLAttribute { /** dewisott */ public final static String JAVA_TYPE = "double"; /** dewisott */ public final static String JAVA_GET_METHOD = "getDouble"; /** dewisott */ public final static String JAVA_SHORT_CLASS = "DoubleSTAttribute"; protected Double d; /** * constructor. * * @param name */ public DoubleSTAttribute(String name) { super(name); } /** * from DOM. * * @param att */ public DoubleSTAttribute(Attribute att) { this(att.getLocalName()); String v = att.getValue(); if (v != null && !v.trim().equals(S_EMPTY)) { this.setSTMLValue(v); } } /** * from DOM. * * @param att * to copy, except value * @param value */ public DoubleSTAttribute(Attribute att, String value) { super(att, value.trim()); } /** * copy constructor * * @param att */ public DoubleSTAttribute(DoubleSTAttribute att) { super(att); if (att.d != null) { this.d = Double.valueOf(att.d.doubleValue()); } } /** * copy. uses copy constructor. * * @return copy */ public Attribute copy() { return new DoubleSTAttribute(this); } /** * get java type. * * @return java type */ public String getJavaType() { return "double"; } /** * sets value. throws exception if of wrong type or violates restriction * * @param s * the value * @throws RuntimeException */ public void setSTMLValue(String s) { if (s != null && !s.trim().equals(S_EMPTY)) { double d; try { String ss = s.trim(); if (ss.startsWith(S_PLUS)) { ss = ss.substring(1); } d = (Util.parseFlexibleDouble(ss)); } catch (NumberFormatException nfe) { throw new RuntimeException("" + nfe, nfe); } catch (ParseException e) { throw new RuntimeException("Bad double: " + s.trim(), e); } this.setSTMLValue(d); } } /** * checks value of simpleType. if value does not check * against SimpleType uses STMLType.checkvalue() fails if type is String or * int or is a list * * @param d * the double * @throws RuntimeException * wrong type or value fails */ public void checkValue(double d) throws RuntimeException { if (cmlType != null) { cmlType.checkValue(d); } } /** * set and check value. * * @param d */ public void setSTMLValue(double d) { checkValue(d); this.d = Double.valueOf(d); this.setValue("" + d); } /** * get double. * * @return value */ public double getDouble() { return d.doubleValue(); } /** * get java method. * * @return java method */ public String getJavaGetMethod() { return JAVA_GET_METHOD; } /** * get java short class name. * * @return java short className */ public String getJavaShortClassName() { return JAVA_SHORT_CLASS; } }; euclid-euclid-2.9/src/main/java/org/xmlcml/stml/attribute/IdAttribute.java000077500000000000000000000037751461721410700267020ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.stml.attribute; import nu.xom.Attribute; import nu.xom.Node; /** * user-modifiable class supporting "id". */ public class IdAttribute extends StringSTAttribute { /** id */ public final static String NAME = "id"; String argName = "null"; int start = 0; int end = 0; /** * constructor. * */ public IdAttribute() { super(NAME); } /** constructor. * @param value */ public IdAttribute(String value) { super(NAME); this.setSTMLValue(value); } /** * constructor from element with IdAttribute * * @param att * @exception RuntimeException */ public IdAttribute(Attribute att) throws RuntimeException { super(att); } /** copy constructor. * @return IdAttribute copy */ public Attribute copy() { return new IdAttribute(this); } /** set value and process. * * @param value * @exception RuntimeException bad value */ public void setSTMLValue(String value) throws RuntimeException { if (value == null) { throw new RuntimeException("null IdAttribute value"); } else if (value.trim().equals(S_EMPTY)) { // seems to get called with empty string initially // this is a bug } else { super.setSTMLValue(value); } } } euclid-euclid-2.9/src/main/java/org/xmlcml/stml/attribute/IntArraySTAttribute.java000077500000000000000000000116131461721410700303340ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.stml.attribute; import nu.xom.Attribute; import nu.xom.Node; import org.xmlcml.euclid.Util; import org.xmlcml.stml.STMLAttribute; import org.xmlcml.stml.STMLConstants; /** * attribute representing an int value.. */ public class IntArraySTAttribute extends STMLAttribute { /** */ public final static String JAVA_TYPE = JAVA_INT+JAVA_ARRAY; /** */ public final static String JAVA_GET_METHOD = "getIntArray"; /** */ public final static String JAVA_SHORT_CLASS = "IntArraySTAttribute"; protected int[] ii = null; protected int length = -1; /** * constructor. * * @param name */ public IntArraySTAttribute(String name) { super(name); } /** * from DOM. * * @param att */ public IntArraySTAttribute(Attribute att) { this(att.getLocalName()); this.setSTMLValue(att.getValue()); } /** * copy constructor * * @param att */ public IntArraySTAttribute(IntArraySTAttribute att) { super(att); if (att.ii != null) { this.ii = new int[att.ii.length]; for (int i = 0; i < ii.length; i++) { this.ii[i] = att.ii[i]; } } this.length = att.length; } /** copy. * uses copy constructor. * @return copy */ public Attribute copy() { return new IntArraySTAttribute(this); } /** * from DOM. * * @param att * to copy, except value * @param value */ public IntArraySTAttribute(Attribute att, String value) { super(att, value.trim().replace(S_WHITEREGEX, STMLConstants.S_SPACE)); } /** * set and check value. * * @param ii */ public void setSTMLValue(int[] ii) { checkValue(ii); this.ii = new int[ii.length]; for (int i = 0; i < ii.length; i++) { this.ii[i] = ii[i]; } this.setValue(Util.concatenate(ii, STMLConstants.S_SPACE)); } /** * checks value of simpleType. if value does not check * against SimpleType uses STMLType.checkvalue() fails if type is String or * double or is not a list * * @param ii * the int array * @throws RuntimeException * wrong type or value fails */ public void checkValue(int[] ii) { if (cmlType != null) { cmlType.checkValue(ii); } } /** * splits string into ints. * * @param s the string * @param delim delimiter * @return array */ public static int[] split(String s, String delim) { String sss = s.trim().replace(S_WHITEREGEX, STMLConstants.S_SPACE); if (delim == null || delim.trim().equals(S_EMPTY) || delim.equals(S_WHITEREGEX)) { delim = STMLConstants.S_WHITEREGEX; sss = sss.trim(); } String[] ss = sss.split(delim); int[] ii = new int[ss.length]; for (int i = 0; i < ss.length; i++) { try { ii[i] = Integer.parseInt(ss[i]); } catch (NumberFormatException nfe) { throw new RuntimeException(S_EMPTY + nfe); } } return ii; } /** * sets value. throws exception if of wrong type or violates restriction * * @param s * the value * @throws RuntimeException */ public void setSTMLValue(String s) { int[] ii = split(s.trim(), STMLConstants.S_WHITEREGEX); this.setSTMLValue(ii); } /** * get array. * * @return null if not set */ public Object getSTMLValue() { return ii; } /** * get array. * * @return null if not set */ public int[] getIntArray() { return ii; } /** * get java type. * * @return java type */ public String getJavaType() { return JAVA_TYPE; } /** * get java method. * * @return java method */ public String getJavaGetMethod() { return JAVA_GET_METHOD; } /** * get java short class name. * * @return java short className */ public String getJavaShortClassName() { return JAVA_SHORT_CLASS; } }; euclid-euclid-2.9/src/main/java/org/xmlcml/stml/attribute/IntSTAttribute.java000077500000000000000000000077371461721410700273510ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.stml.attribute; import org.xmlcml.stml.STMLAttribute; import org.xmlcml.stml.STMLConstants; import nu.xom.Attribute; import nu.xom.Node; /** * attribute representing an int value. */ public class IntSTAttribute extends STMLAttribute { /** */ public final static String JAVA_TYPE = JAVA_INT; /** */ public final static String JAVA_GET_METHOD = "getInt"; /** */ public final static String JAVA_SHORT_CLASS = "IntSTAttribute"; protected Integer i; /** * constructor. * * @param name */ public IntSTAttribute(String name) { super(name); } /** * from DOM. * * @param att */ public IntSTAttribute(Attribute att) { this(att.getLocalName()); String v = att.getValue(); if (v != null && !v.trim().equals(S_EMPTY)) { this.setSTMLValue(v); } } /** * copy constructor * * @param att */ public IntSTAttribute(IntSTAttribute att) { super(att); if (att.i != null) { this.i = att.i.intValue(); } } /** copy. * uses copy constructor. * @return copy */ public Attribute copy() { return new IntSTAttribute(this); } /** * from DOM. * * @param att * to copy, except value * @param value */ public IntSTAttribute(Attribute att, String value) { super(att, value.trim().replace(S_WHITEREGEX, STMLConstants.S_SPACE)); } /** * sets value. throws exception if of wrong type or violates restriction * * @param s * the value * @throws RuntimeException */ public void setSTMLValue(String s) { if (s!= null && !s.trim().equals(S_EMPTY)) { int i; try { i = Integer.parseInt(s.trim()); } catch (NumberFormatException nfe) { throw new RuntimeException(S_EMPTY + nfe); } this.setSTMLValue(i); } } /** * set and check value. * * @param i */ public void setSTMLValue(int i) { checkValue(i); this.i = i; this.setValue(S_EMPTY + i); } /** * checks value of simpleType. if value does not check * against SimpleType uses STMLType.checkvalue() fails if type is String or * double or is a list * * @param i * the value */ public void checkValue(int i) { if (cmlType != null) { cmlType.checkValue(i); } } /** * returns value as Integer. * * @return value */ public Object getSTMLValue() { return i; } /** * returns value as int. * * @return int */ public int getInt() { if (i == null) { throw new RuntimeException("integer attribute unset"); } return i.intValue(); } /** * get java type. * * @return java type */ public String getJavaType() { return JAVA_TYPE; } /** * get java method. * * @return java method */ public String getJavaGetMethod() { return JAVA_GET_METHOD; } /** * get java short class name. * * @return java short className */ public String getJavaShortClassName() { return JAVA_SHORT_CLASS; } }; euclid-euclid-2.9/src/main/java/org/xmlcml/stml/attribute/NamespaceRefAttribute.java000077500000000000000000000140151461721410700306640ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.stml.attribute; import nu.xom.Attribute; import nu.xom.Element; import org.xmlcml.stml.STMLConstants; import org.xmlcml.stml.STMLElement; import org.xmlcml.stml.interfacex.HasUnits; /** * abstract class supporting attributes with namespaceRef values. */ public /*abstract*/ class NamespaceRefAttribute extends StringSTAttribute { /** regex for validating prefixes */ public final static String PREFIX_REGEX = "[A-Za-z][A-Za-z0-9\\.\\-\\_]*"; /** * constructor. * * @param name */ public NamespaceRefAttribute(String name) { super(name); init(name); } public NamespaceRefAttribute(String name, String value) { this(name); this.setSTMLValue(value); } public NamespaceRefAttribute(Attribute att) { this(att.getLocalName()); this.setSTMLValue(att.getValue()); } void init(String name) { // SpecialAttribute.updateSTMLAttributeList(name, name, this); } /** * interlude to check for QName value. * * @param value */ public void setSTMLValue(String value) { super.setSTMLValue(value); if (this.getValue().equals(S_EMPTY)) { // empty string is created in new attribute } else if (this.getPrefix() == null) { throw new RuntimeException("attribute value [" + this.getValue() + "] for " + this.getLocalName() + " must be QName"); } } /** * get parent element. * * @return parent */ public Element getElement() { return (Element) this.getParent(); } /** * gets namespace prefix. * * @return null if attribute has no value or no prefix */ public String getPrefix() { return getPrefix(this.getValue()); } /** gets prefix from a valid namespaceRef string. * * @param value to examine * @return prefix (null if absent) */ public static String getPrefix(String value) { String prefix = null; if (value != null) { int idx = value.indexOf(S_COLON); if (idx != -1) { prefix = value.substring(0, idx); } } return prefix; } /** * get namespaceURI for this attribute; * * @return the namespace */ public String getNamespaceURIString() { Element element = this.getElement(); String prefix = this.getPrefix(); String namespaceURI = (prefix == null) ? null : element .getNamespaceURI(prefix); return namespaceURI; } /** * gets idRef. portion of value following the colon * * @return null if attribute has no value or no prefix */ public String getIdRef() { return getLocalName(this.getValue()); } /** * sets idRef. portion of value following the colon * */ public void setIdRef(String idRef) { String value = NamespaceRefAttribute.createValue(this.getPrefix(), idRef); this.setSTMLValue(value); } static int count = 0; /** create valid prefixed value. * * @param prefix * @param value * @return prefixed value */ public static String createValue(String prefix, String value) { if (prefix == null) { throw new RuntimeException("null prefix"); } if (value == null) { throw new RuntimeException("null value"); } if (prefix.trim().equals("")) { throw new RuntimeException("cannot have empty prefix"); } if (value.trim().equals("")) { throw new RuntimeException("cannot have empty value"); } if (!prefix.matches(PREFIX_REGEX)) { throw new RuntimeException("Prefix [" + prefix + "] incompatible with " + PREFIX_REGEX); } return prefix + STMLConstants.S_COLON + value; } /** return the local name after the colon. * if prefix is missing return whole string * @param name to examine * @return localName */ public static String getLocalName(String name) { String localName = null; if (name != null) { int idx = name.indexOf(S_COLON); localName = name.substring(idx+1); } return localName; } /** set units on STML Element. * * @param hasUnits to set units on * @param prefix unit dictionary prefix * @param id unit id * @param namespaceURI unit dictionary namespace */ public static void setUnits(HasUnits hasUnits, String prefix, String id, String namespaceURI) { STMLElement element = (STMLElement) hasUnits; String currentNamespace = element.getNamespaceURIForPrefix(prefix); if (currentNamespace != null) { if (!currentNamespace.equals(namespaceURI)) { throw new RuntimeException("Cannot reset units namespace for "+prefix+" from " + ""+currentNamespace+" to "+namespaceURI); } } else { element.addNamespaceDeclaration(prefix, namespaceURI); } Attribute units = element.getAttribute("units"); if (units != null) { element.removeAttribute(units); } UnitsAttribute unitAttribute = new UnitsAttribute( NamespaceRefAttribute.createValue(prefix, id)); element.addAttribute(unitAttribute); } } euclid-euclid-2.9/src/main/java/org/xmlcml/stml/attribute/StringArraySTAttribute.java000077500000000000000000000104341461721410700310500ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.stml.attribute; import nu.xom.Attribute; import nu.xom.Node; import org.xmlcml.euclid.Util; import org.xmlcml.stml.STMLAttribute; import org.xmlcml.stml.STMLConstants; /** * attribute representing an array of Strings. */ public class StringArraySTAttribute extends STMLAttribute { /** */ public final static String JAVA_TYPE = JAVA_STRING+JAVA_ARRAY; /** */ public final static String JAVA_GET_METHOD = "getStringArray"; /** */ public final static String JAVA_SHORT_CLASS = "StringArraySTAttribute"; protected String[] ss = null; /** * constructor. * * @param name */ public StringArraySTAttribute(String name) { super(name); } /** * from DOM. * * @param att */ public StringArraySTAttribute(Attribute att) { this(att.getLocalName()); this.setSTMLValue(att.getValue()); } /** * Sets the XOM value, and the STMLValue array. */ @Override public void setValue(String s){ super.setValue(s); this.setSTMLValue(s); } /** * copy constructor * * @param att */ public StringArraySTAttribute(StringArraySTAttribute att) { super(att); if (att.ss != null) { this.ss = new String[att.ss.length]; for (int i = 0; i < ss.length; i++) { this.ss[i] = att.ss[i]; } } } /** copy. * uses copy constructor. * @return copy */ public Attribute copy() { return new StringArraySTAttribute(this); } /** * from DOM. * * @param att * to copy, except value * @param value */ public StringArraySTAttribute(Attribute att, String value) { super(att); this.setSTMLValue(value); } /** * sets value. throws exception if of wrong type or violates restriction * * @param s * the value */ public void setSTMLValue(String s) { this.setSTMLValue(arrayFromString(s)); } protected String[] arrayFromString(String s){ String[] split = s.trim().split(S_WHITEREGEX); return split; } protected String stringFromArray(String[] array){ return Util.concatenate(array, STMLConstants.S_SPACE); } /** * set and check value. * * @param ss */ public void setSTMLValue(String[] ss) { checkValue(ss); this.ss = ss; super.setValue(stringFromArray(ss)); } /** * checks value of simpleType. if value does not check * against SimpleType uses STMLType.checkvalue() fails if type is int or * double or is not a list * * @param ss * the String array * @throws RuntimeException * wrong type or value fails */ public void checkValue(String[] ss) { if (cmlType != null) { cmlType.checkValue(ss); } } /** * get array. * * @return null if not set */ public Object getSTMLValue() { return ss; } /** * get array. * * @return null if not set */ public String[] getStringArray() { return ss; } /** * get java type. * * @return java type */ public String getJavaType() { return JAVA_TYPE; } /** * get java method. * * @return java method */ public String getJavaGetMethod() { return JAVA_GET_METHOD; } /** * get java short class name. * * @return java short className */ public String getJavaShortClassName() { return JAVA_SHORT_CLASS; } }; euclid-euclid-2.9/src/main/java/org/xmlcml/stml/attribute/StringSTAttribute.java000077500000000000000000000070521461721410700300530ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.stml.attribute; import org.xmlcml.stml.STMLAttribute; import org.xmlcml.stml.STMLConstants; import nu.xom.Attribute; import nu.xom.Node; /** * attribute representing a string value. * */ public class StringSTAttribute extends STMLAttribute { /** */ public final static String JAVA_TYPE = JAVA_STRING; /** */ public final static String JAVA_GET_METHOD = "getString"; /** */ public final static String JAVA_SHORT_CLASS = "StringSTAttribute"; /** * constructor. * * @param name */ public StringSTAttribute(String name) { super(name); } /** * from DOM. * * @param att */ public StringSTAttribute(Attribute att) { this(att.getLocalName()); this.setSTMLValue(att.getValue()); } /** * copy constructor * * @param att */ public StringSTAttribute(StringSTAttribute att) { super(att); if (att.getValue() != null) { this.setValue(att.getValue()); } } /** copy. * uses copy constructor. * @return copy */ public Attribute copy() { return new StringSTAttribute(this); } /** * from DOM. * * @param att * to copy, except value * @param value */ public StringSTAttribute(Attribute att, String value) { super(att, value.trim().replace(S_WHITEREGEX, STMLConstants.S_SPACE)); } /** * set and check value. * trims by default * use setSTMLValue(s, trim) * @param s */ public void setSTMLValue(String s) { this.setSTMLValue(s, true); } /** * set and check value. * * @param string */ public void setSTMLValue(String string, boolean trim) { if (string == null) { throw new RuntimeException("Cannot set null attribute value"); } if (trim) { string = string.trim(); } checkValue(string); //this.s = string; this.setValue(string); } /** * checks value of simpleType. uses STMLType.checkvalue() fails if type is * int or double or is a list * * @param s * the value * @throws RuntimeException * wrong type or value fails */ public void checkValue(String s) { if (cmlType != null) { cmlType.checkValue(s); } } /** * get value. * * @return value */ public String getString() { return this.getValue(); } /** * get java type. * * @return java type */ public String getJavaType() { return JAVA_TYPE; } /** * get java method. * * @return java method */ public String getJavaGetMethod() { return JAVA_GET_METHOD; } /** * get java short class name. * * @return java short className */ public String getJavaShortClassName() { return JAVA_SHORT_CLASS; } }; euclid-euclid-2.9/src/main/java/org/xmlcml/stml/attribute/UnitsAttribute.java000077500000000000000000000024361461721410700274410ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.stml.attribute; import nu.xom.Attribute; /** * user-modifiable class supporting UnitsAttribute. supports units attribute */ public class UnitsAttribute extends NamespaceRefAttribute { /** */ public final static String NAME = "units"; /** * constructor. * */ public UnitsAttribute() { super(NAME); } /** * constructor. * * @param value * QName for units */ public UnitsAttribute(String value) { super(NAME, value); } /** * constructor. * * @param att */ public UnitsAttribute(Attribute att) { super(att); } } euclid-euclid-2.9/src/main/java/org/xmlcml/stml/interfacex/000077500000000000000000000000001461721410700237255ustar00rootroot00000000000000euclid-euclid-2.9/src/main/java/org/xmlcml/stml/interfacex/HasArraySize.java000077500000000000000000000023671461721410700271500ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.stml.interfacex; import java.util.List; /** * interface for STMLArray or STMLList */ public interface HasArraySize extends HasDataType { /** get size of array. * @return size */ int getArraySize(); /** get array elements. * recalcuates each time so best cached for frequent use * @return elements as String */ List getStringValues(); /** * gets values of element; * * @return integer values */ int[] getInts(); /** * gets values of element; * * @return double values */ double[] getDoubles(); } euclid-euclid-2.9/src/main/java/org/xmlcml/stml/interfacex/HasDataType.java000077500000000000000000000025241461721410700267450ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.stml.interfacex; /** * attached to elements that can carry dataType. * examples are scalar, array, matrix * * @author pmr * */ public interface HasDataType extends HasDictRef { /** * sets value on dataType attribute. example: setDataType("xsd:double"); * * @param type */ void setDataType(String type); /** * gets value on dataType attribute. example: setDataType("xsd:double"); * * @return type */ String getDataType(); /** * gets value of element; * * @return data */ String getXMLContent(); /** * sets value of element; * * @param content */ void setXMLContent(String content); }euclid-euclid-2.9/src/main/java/org/xmlcml/stml/interfacex/HasDelimiter.java000077500000000000000000000022441461721410700271470ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.stml.interfacex; import nu.xom.Attribute; /** * interface for STMLArray or STMLMatrix */ public interface HasDelimiter { /** * * @return delimiter string */ String getDelimiter(); /** * sets delimiter (should be a single printable character or single space (char)32 * @param delim */ void setDelimiter(String delim); /** removes any attribute of the form * delimiter=" " or delimiter="" */ void removeWhitespaceDelimiterAttribute(); Attribute getDelimiterAttribute(); } euclid-euclid-2.9/src/main/java/org/xmlcml/stml/interfacex/HasDictRef.java000077500000000000000000000027501461721410700265530ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.stml.interfacex; import org.xmlcml.stml.STMLAttribute; /** * attached to elements that can carry dictRef. * examples are scalar, array, matrix, property * * @author pmr * */ public interface HasDictRef { // /** // * sets value on dictRef attribute. // * example: setDictRef("myRef", "floop"); // * // * @param prefix // * @param idRef // * @param namespaceURI // */ // void setDictRef(String prefix, String idRef, String namespaceURI); /** get dictRef attribute. * * @return attribute (must be STMLAttribute to be compatible with autogenerated code) */ STMLAttribute getDictRefAttribute(); /** get dictRef value. * * @return value */ String getDictRef(); /** set dictRef value. * * @param dictRef */ void setDictRef(String dictRef); }euclid-euclid-2.9/src/main/java/org/xmlcml/stml/interfacex/HasScalar.java000077500000000000000000000020531461721410700264340ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.stml.interfacex; /** * interface for STMLScalar */ public interface HasScalar extends HasDataType { /** * gets value of element; * * @return data */ String getString(); /** * gets value of element; * * @return integer value */ int getInt(); /** * gets value of element; * * @return double value */ double getDouble(); } euclid-euclid-2.9/src/main/java/org/xmlcml/stml/interfacex/HasUnits.java000077500000000000000000000030041461721410700263260ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.stml.interfacex; /** * attached to elements that can carry units. * examples are scalar, array, matrix * * @author pmr * */ public interface HasUnits { /** * sets value on units attribute. example: setUnits("myUnits", "floop"); * * @param prefix * @param idRef * @param namespaceURI */ void setUnits(String prefix, String idRef, String namespaceURI); /** * * @return units as String */ String getUnits(); // removed in this version // /** // * converts a real scalar to SI. only affects scalar with units attribute // * and dataType='xsd:double' replaces the value with the converted value and // * the units with the SI Units // * // * @param unitListMap // * map to resolve the units attribute // */ // void convertToSI(NamespaceToUnitListMap unitListMap); }euclid-euclid-2.9/src/main/java/org/xmlcml/xml/000077500000000000000000000000001461721410700214165ustar00rootroot00000000000000euclid-euclid-2.9/src/main/java/org/xmlcml/xml/XMLConstants.java000077500000000000000000000316431461721410700246300ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.xml; import nu.xom.XPathContext; import org.xmlcml.euclid.EuclidConstants; /** * *

* Constants *

* * @author Peter Murray-Rust * @version 5.0 * */ public interface XMLConstants extends EuclidConstants { /** suffix for files */ String XML_SUFF = ".xml"; /** suffix for files */ String XSD_SUFF = ".xsd"; /** xmlns attribute name */ String XMLNS = "xmlns"; /** * XSD namespace. no trailing slash */ String XSD_NS = "http://www.w3.org/2001/XMLSchema"; /** XSD prefix = 'xsd' */ String XSD_PREFIX = "xsd"; /** * namespace declaration for XSD * xmlns:xsd='http://www.w3.org/2001/XMLSchema' */ String XSD_XMLNS = XMLNS + S_COLON + XSD_PREFIX + S_EQUALS + S_APOS + XSD_NS + S_APOS; /** constant */ String XSD_ANYURI = "xsd:anyURI"; /** constant */ String XSD_BOOLEAN = "xsd:boolean"; /** constant */ String XSD_DATE = "xsd:date"; /** constant */ String XSD_DOUBLE = "xsd:double"; /** constant */ String XSD_FLOAT = "xsd:float"; /** constant */ String XSD_INTEGER = "xsd:integer"; /** constant */ String XSD_MAXEXCLUSIVE = "xsd:maxExclusive"; /** constant */ String XSD_MAXINCLUSIVE = "xsd:maxInclusive"; /** constant */ String XSD_MINEXCLUSIVE = "xsd:minExclusive"; /** constant */ String XSD_MININCLUSIVE = "xsd:minInclusive"; /** constant */ String XSD_NONNEGATIVEINTEGER = "xsd:nonNegativeInteger"; /** constant */ String XSD_POSITIVEINTEGER = "xsd:positiveInteger"; /** constant */ String XSD_POSITIVE_NUMBER = "xsd:positiveNumber"; /** constant */ String XSD_STRING = "xsd:string"; /** constant */ String XSD_QNAME = "xsd:QName"; /** element types */ /** */ String XSD_ANNOTATION = "xsd:annotation"; /** */ String XSD_ATTRIBUTE = "xsd:attribute"; /** */ String XSD_ATTRIBUTE_GROUP = "xsd:attributeGroup"; /** */ String XSD_COMPLEX_TYPE = "xsd:complexType"; /** */ String XSD_DOCUMENTATION = "xsd:documentation"; /** */ String XSD_ELEMENT = "xsd:element"; /** */ String XSD_ENUMERATION = "xsd:enumeration"; /** */ String XSD_EXTENSION = "xsd:extension"; /** */ String XSD_LENGTH = "xsd:length"; /** */ String XSD_LIST = "xsd:list"; /** */ String XSD_PATTERN = "xsd:pattern"; /** */ String XSD_RESTRICTION = "xsd:restriction"; /** */ String XSD_SIMPLE_TYPE = "xsd:simpleType"; /** */ String XSD_SIMPLE_CONTENT = "xsd:simpleContent"; /** */ String XSD_UNION = "xsd:union"; /** dewisott */ String FPX_REAL = "fpx:real"; /** */ String PATTERN_ANYURI = "http://.*"; //crude! /** */ String PATTERN_QNAME = "[A-Za-z_][A-Za-z0-9_-\\.]*:[A-Za-z_][A-Za-z0-9_-\\.]*"; /** */ XPathContext XPATH_XSD = new XPathContext("xsd", XSD_NS); // /** constant */ // String CMLXSD_ANNOTATION = "annotation"; // // /** constant */ // String CMLXSD_ANY = "any"; // // /** constant */ // String CMLXSD_APPINFO = "appinfo"; // // /** constant */ // String CMLXSD_ATTRIBUTE = "attribute"; // // /** constant */ // String CMLXSD_ATTRIBUTEGROUP = "attributeGroup"; // // /** constant */ // String CMLXSD_BASE = "base"; // // /** constant */ // String CMLXSD_CHOICE = "choice"; // // /** constant */ // String CMLXSD_COMPLEXTYPE = "complexType"; // // /** constant */ // String CMLXSD_DOCUMENTATION = "documentation"; // // /** constant */ // String CMLXSD_ELEMENT = "element"; // // /** constant */ // String CMLXSD_ENUMERATION = "enumeration"; // // /** constant */ // String CMLXSD_EXTENSION = "extension"; // // /** constant */ // String CMLXSD_ID = "id"; // // /** constant */ // String CMLXSD_ITEMTYPE = "itemType"; // // /** constant */ // String CMLXSD_LENGTH = "length"; // // /** constant */ // String CMLXSD_LIST = "list"; // // /** constant */ // String CMLXSD_MAXEXCLUSIVE = "maxExclusive"; // // /** constant */ // String CMLXSD_MAXINCLUSIVE = "maxInclusive"; // // /** constant */ // String CMLXSD_MINEXCLUSIVE = "minExclusive"; // // /** constant */ // String CMLXSD_MININCLUSIVE = "minInclusive"; // // /** constant */ // String CMLXSD_NAME = "name"; // // /** constant */ // String CMLXSD_PATTERN = "pattern"; // // /** constant */ // String CMLXSD_REF = "ref"; // // /** constant */ // String CMLXSD_RESTRICTION = "restriction"; // // /** constant */ // String CMLXSD_ROOT = "root"; // // /** constant */ // String CMLXSD_SEQUENCE = "sequence"; // // /** constant */ // String CMLXSD_SIMPLECONTENT = "simpleType"; // // /** constant */ // String CMLXSD_SIMPLETYPE = "simpleType"; // // /** constant */ // String CMLXSD_TEXT = "text"; // // /** constant */ // String CMLXSD_TYPE = "type"; // // /** constant */ // String CMLXSD_UNBOUNDED = "unbounded"; // // /** constant */ // String CMLXSD_UNION = "union"; // // /** constant */ // String CMLXSD_VALUE = "value"; // // /** constant */ // String CMLXSD_ATTPREFIX = "_att_"; // // /** constant */ // String CMLXSD_XMLCONTENT = "_xmlContent"; // /** root of all CML URIs */ // String CML_NS_BASE = "http://www.xml-cml.org"; // // /** constant */ // String CML_NS = CML_NS_BASE+EC.U_S+"schema"; // // /** CMLX prefix (cmlx) for experimentation and development // */ // String CMLX_PREFIX = "cmlx"; // // /** cmlx namespace */ // String CMLX_NS = CML_NS_BASE+EC.U_S+"schema"+S_SLASH+CMLX_PREFIX; // // /** // * namespace declaration for CMLx with prefix // */ // String CMLX_XMLNS_PREFIX = XMLNS + S_COLON + CMLX_PREFIX + S_EQUALS + S_APOS // + CMLX_NS + S_APOS; // // /** constant */ //// String CML = CML_NS; // // /** // * cml dictionary namespace reserved // */ // String DICT_NS = CML_NS_BASE+EC.U_S+"dict"; // // /** // * cml URI namespace reserved // */ // String CML_REPOSITORY_BASE = CML_NS_BASE+EC.U_S+"repository"; // /** // * cml dictionary reserved // */ // String CML_DICT_NS = DICT_NS+EC.U_S+"cml"; // // /** constant */ // String CML1 = CML_NS + S_SLASH + "cml1"; // // /** constant */ // String CML2 = CML_NS + S_SLASH + "cml2"; // // /** constant */ // String CML3 = CML_NS + S_SLASH + "cml3"; // // /** CML prefix (cml) reserved: for several uses // */ // String CML_PREFIX = "cml"; // // /** CML prefix + colon (cml:) // */ // String CML_COLON = CML_PREFIX+S_COLON; // // /** CML prefix when used as element namespace // */ // String C_E = CML_COLON; // // /** CML prefix when used as attribute value namespace // */ // String C_A = CML_COLON; // // /** // * namespace declaration for CML without prefix // */ // String CML_XMLNS = XMLNS + S_EQUALS + S_APOS + CML_NS + S_APOS; // // /** // * namespace declaration for CML with prefix // */ // String CML_XMLNS_PREFIX = XMLNS + S_COLON + CML_PREFIX + S_EQUALS + S_APOS // + CML_NS + S_APOS; // // /** // * obsolete CML namespaces // */ // String[] OLD_NAMESPACES = { CML1, CML2, CML3, "http://www.xmlcml.org/", // "http://www.xmlcml.org/schema", "http://www.xml-cml.org/schema/cml2/core"}; /** constant */ String XHTML_NS = "http://www.w3.org/1999/xhtml"; // /** XPathContext for CML. // */ // XPathContext CML_XPATH = new XPathContext("cml", CML_NS); /** namespace for SVG. * */ /** root of all SVG URIs */ String SVG_NS_BASE = "http://www.w3.org/2000/svg"; String SVG_NS = SVG_NS_BASE; /** XPathContext for SVG. */ XPathContext SVG_XPATH = new XPathContext("svg", SVG_NS); /** namespaces for XLINK * */ String XLINK_PREFIX = "xlink"; String XLINK_NS = "http://www.w3.org/1999/xlink"; // /** // * @deprecated // */ // XPathContext X_CML = CML_XPATH; /** XPath 'OR' concatenator*/ String X_OR = S_PIPE; // subdirs of components /** constant */ String TYPES = "types"; /** constant */ String ATTRIBUTES = "attributeGroups"; /** constant */ String ELEMENTS = "elements"; // /** constant */ // String TEXT_LIST = "CMLTextList"; // // /** constant */ // String ATTRIBUTE_LIST = "CMLAttributeList"; // // /** constant */ // String ELEMENT_LIST = "CMLElementList"; // /** constant */ // String TYPE_LIST = "CMLTypeList"; // /** constant */ // String ABSTRACT_NODEFACTORY = "OldNodeFactory"; /** java classnames */ String JAVA_BOOLEAN = "Boolean"; /** java classnames */ String JAVA_DOUBLE = "Double"; /** constant */ String JAVA_INTEGER = "Integer"; /** String class */ String JAVA_STRING = "String"; /** primitive */ String JAVA_BOOL = "boolean"; /** primitive */ String JAVA_INT = "int"; /** primitive */ String JAVA_DOUB = "double"; /** array */ String JAVA_ARRAY = "[]"; // // for generating elements and attributes // /** */ // String ELEMENT_CLASS_BASE = "org.xmlcml.cml.element"; // /** */ // String LITE = "lite"; // /** */ // String MAIN = "main"; // /** */ // String ATTRIBUTE_CLASS_BASE = "org.xmlcml.cml.attribute"; // /** */ // String ATTRIBUTE = "Attribute"; // // sepcial CML dataTypes // /** dataType */ // String CML_DATATYPETYPE = "dataTypeType"; // // /** namespaceRef */ // String CML_NAMESPACEREFTYPE = "namespaceRefType"; // // /** units */ // String CML_UNITSTYPE = "unitsType"; // // /** dictRef value identifying a filename */ // String CML_FILENAME = C_A+"filename"; // ----------------------------------------------- // format /** constant */ String BANNER_S = "**************************************"; /** constant */ String WARNING_S = "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"; /** constant */ String AUTOGENERATED_DONOTEDIT_S = "/*======AUTOGENERATED FROM SCHEMA; DO NOT EDIT BELOW THIS LINE ======*/"; /** catalog.*/ String CATALOG_XML = "catalog.xml"; /** * units prefix reserved: for several uses */ String CML_UNITS = "units"; // /** // * units root namespace reserved // */ // String _UNIT_NS = CML_NS_BASE+EC.U_S+"units"; // /** // * units dictionary namespace reserved // */ // String UNIT_NS = _UNIT_NS+EC.U_S+"units"; // /** // * siUnits dictionary namespace reserved // */ // String SIUNIT_NS = _UNIT_NS+EC.U_S+"siUnits"; // /** // * unnitTypes dictionary namespace reserved // */ // String UNITTYPES_NS = _UNIT_NS+EC.U_S+"unitTypes"; /** * siUnits prefix reserved: for several uses */ String CML_SIUNITS = "siUnits"; // These are IDs, and must match those on the test dictionaries /** angstrom. */ String U_ANGSTROM = CML_UNITS + S_COLON + "ang"; /** degree. */ String U_DEGREE = CML_UNITS + S_COLON + "deg"; /** degree. */ String U_KCAL = CML_UNITS + S_COLON + "kcal"; /** celsius. */ String U_CELSIUS = CML_UNITS + S_COLON + "celsius"; // ================== crystal ================ // /** // * dictRef ids for 6 scalar children of crystal. // */ // String CRYSTAL_DICT_REFS[] = { CML_PREFIX + S_COLON + "a", // CML_PREFIX + S_COLON + "b", CML_PREFIX + S_COLON + "c", // CML_PREFIX + S_COLON + "alpha", CML_PREFIX + S_COLON + "beta", // CML_PREFIX + S_COLON + "gamma" }; // // /** // * unit refs for 6 scalar children of crystal. // */ // String[] CRYSTAL_DICT_UNITS = { CML_UNITS + S_COLON + "ang", // CML_UNITS + S_COLON + "ang", CML_UNITS + S_COLON + "ang", // CML_UNITS + S_COLON + "degree", CML_UNITS + S_COLON + "degree", // CML_UNITS + S_COLON + "degree" }; // ======= test ========== // /** // * number of dictionaries. has to be altered every time new dictionaries are // * added. // */ // int NDICT = 4; // // /** // * number of units dictionaries. has to be altered every time new units // * dictionaries are added. // */ // int NUNIT_DICT = 5; // // /** // * number of unitType dictionaries. has to be altered every time new units // * dictionaries are added. // */ // int NUNIT_TYPE_DICT = 1; //// public final String value; }euclid-euclid-2.9/src/main/java/org/xmlcml/xml/XMLUtil.java000077500000000000000000001131011461721410700235570ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.xml; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.StringReader; import java.util.ArrayList; import java.util.List; import java.util.regex.Pattern; import nu.xom.Attribute; import nu.xom.Builder; import nu.xom.Comment; import nu.xom.Document; import nu.xom.Element; import nu.xom.Elements; import nu.xom.Node; import nu.xom.Nodes; import nu.xom.ParentNode; import nu.xom.ProcessingInstruction; import nu.xom.Serializer; import nu.xom.Text; import nu.xom.XPathContext; import nu.xom.canonical.Canonicalizer; import org.apache.commons.io.IOUtils; import org.apache.log4j.Logger; import org.junit.Assert; import org.xmlcml.euclid.Util; /** * *

* static utilities to help manage common constructs. *

* * @author Peter Murray-Rust * @version 5.0 * */ public abstract class XMLUtil implements XMLConstants { private static Logger LOG = Logger.getLogger(XMLUtil.class); private static final String DOCTYPE = "\\<\\!DOCTYPE[^\\>]*\\>"; public final static String DTD = ".dtd\">"; private static final String DUMMY = "dummy"; private static final String TEXT = "#text"; // ========================== utilities ====================== // /** * checks that name is QName. * * @param name * of XMLName * not colonized */ public final static void checkPrefixedName(String name) { if (name == null || name.indexOf(S_COLON) < 1) { throw new RuntimeException("Unprefixed name (" + name + S_RBRAK); } } /** * get prefix from qualified name. * * @param s * @return prefix (or empty string) */ public static String getPrefix(String s) { int idx = s.indexOf(S_COLON); return (idx == -1) ? S_EMPTY : s.substring(0, idx); } /** * get localName from qualified name. * * @param s * @return localName (or empty string) */ public static String getLocalName(String s) { String ss = null; if (s != null) { int idx = s.indexOf(S_COLON); ss = (idx == -1) ? s : s.substring(idx + 1); } return ss; } /** * convenience method to extract value of exactly one node. * uses element.query(xpath, xPathContext); * @param element * @param xpath * @param xPathContext defines prefix/namespace used in query * @return value if exactly 1 node (0 or many returns null) */ public static String getSingleValue(Element element, String xpath, XPathContext xPathContext) { String s = null; if (element == null) { LOG.warn("Null element"); } else { Nodes nodes = element.query(xpath, xPathContext); s = (nodes.size() == 1) ? nodes.get(0).getValue() : null; } return s; } /** * convenience method to extract value of exactly one node.. * uses element.query(xpath, xPathContext); * @param element * @param xpath * @return value if exactly 1 node (0 or many returns null) */ public static String getSingleValue(Element element, String xpath) { String s = null; if (element == null) { LOG.warn("Null element"); } else { Nodes nodes = element.query(xpath); s = (nodes.size() == 1) ? nodes.get(0).getValue() : null; } return s; } /** * convenience method to extract value of the first of one-or-more nodes. * uses element.query(xpath, xPathContext); * @param element * @param xpath * @param xPathContext defines prefix/namespace used in query * @return value if exactly 1 node (0 or many returns null) */ public static String getFirstValue(Element element, String xpath, XPathContext xPathContext) { String s = null; if (element == null) { LOG.warn("Null element"); } else { Nodes nodes = element.query(xpath, xPathContext); s = (nodes.size() >= 1) ? nodes.get(0).getValue() : null; } return s; } /** * convenience method to get exactly one element. * uses element.query(xpath, xPathContext); * @param element * @param xpath * @param xPathContext defines prefix/namespace used in query * @return value if exactly 1 element (0 or many returns null) */ public static Element getSingleElement(Element element, String xpath, XPathContext xPathContext) { Nodes nodes = element.query(xpath, xPathContext); return (nodes.size() == 1) ? (Element) nodes.get(0) : null; } /** * converts an Elements to a java array. we might convert code to use * Elements through later so this would be unneeded * * @param elements * @param obj * type of array (e.g. "new CMLAtom[0]" * @return the java array 0f objects */ public final static Object[] toArray(Elements elements, Object[] obj) { List list = new ArrayList(); for (int i = 0; i < elements.size(); i++) { list.add(elements.get(i)); } return list.toArray(obj); } /** * debug an element. outputs XML to sysout indent = 2 * * @param el * the element * @deprecated use debug(el, message) instead */ public static void debug(Element el) { try { debug(el, System.out, 2); } catch (IOException e) { throw new RuntimeException("BUG " + e); } } /** * debug an element. outputs XML to syserr * * @param el * the element */ public static void debugToErr(Element el) { try { debug(el, System.err, 2); } catch (IOException e) { throw new RuntimeException("BUG " + e); } } /** * debug an element. * * @param el * the element * @param message */ public static void debug(Element el, String message) { Util.println(">>>>" + message + ">>>>"); XMLUtil.debug(el); Util.println("<<<<" + message + "<<<<"); } /** * debug an element. * * @param el * the element * @param os * output stream * @param indent * indentation * @throws IOException */ public static void debug(Element el, OutputStream os, int indent) throws IOException { Document document; if (el != null) { Node parent = el.getParent(); if (parent instanceof Document) { document = (Document) parent; } else { Element copyElem = new Element(el); document = new Document(copyElem); } Serializer serializer = new Serializer(os, "UTF-8"); if (indent >= 0) { serializer.setIndent(indent); } serializer.write(document); } } public static void debug(Element el, File file, int indent) throws IOException { if (file == null) { throw new RuntimeException("null file"); } if (file.isDirectory()) { file.mkdirs(); } else { file.getParentFile().mkdirs(); } debug(el, new FileOutputStream(file), indent); } /** convenience method to avoid trapping exception * * @param elem * @param file * @param indent */ public static void outputQuietly(Element elem, File file, int indent) { try { XMLUtil.debug(elem, new FileOutputStream(file), indent); } catch (Exception e) { throw new RuntimeException("cannot write file: " + file, e); } } /** * convenience method to get resource from XMLFile. the resource is packaged * with the classes for distribution. typical filename is * org/xmlcml/molutil/elementdata.xml for file elementdata.xml in class * hierarchy org.xmlcml.molutil * * @param filename * relative to current class hierarchy. * @return document for resource * @throws IOException */ public static Document getXMLResource(String filename) throws IOException { Document document = null; InputStream in = null; try { in = Util.getInputStreamFromResource(filename); document = (Document) new Builder().build(in); } catch (IOException e) { throw e; } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("" + e + " in " + filename); } finally { if (in != null) { try { in.close(); } catch (IOException e) { e.printStackTrace(); } } } return document; } /** * convenience routine to get child nodes (iterating through getChild(i) is * fragile if children are removed) * * @param el * may be null * @return list of children (immutable) - empty if none */ public static List getChildNodes(Element el) { List childs = new ArrayList(); if (el != null) { for (int i = 0; i < el.getChildCount(); i++) { childs.add(el.getChild(i)); } } return childs; } /** * parses XML string into element. convenience method to avoid trapping * exceptions when string is known to be valid * * @param xmlString * @return root element * @throws RuntimeException */ public static Element parseXML(String xmlString) throws RuntimeException { Element root = null; try { Document doc = new Builder().build(new StringReader(xmlString)); root = doc.getRootElement(); } catch (Exception e) { throw new RuntimeException(e); } return root; } /** * convenience routine to get query nodes (iterating thorugh get(i) is * fragile if nodes are removed) * * @param node * (can be null) * @param xpath * xpath relative to node * @param context * @return list of nodes (immutable) - empty if none */ public static List getQueryNodes(Node node, String xpath, XPathContext context) { List nodeList = new ArrayList(); if (node != null) { Nodes nodes = node.query(xpath, context); for (int i = 0; i < nodes.size(); i++) { nodeList.add(nodes.get(i)); } } return nodeList; } /** * convenience routine to get query nodes (iterating through get(i) is * fragile if nodes are removed) * * @param node * @param xpath * @return list of nodes (immutable) - empty if none or null node */ public static List getQueryNodes(Node node, String xpath) { List nodeList = new ArrayList(); if (node != null && xpath != null) { try { Nodes nodes = node.query(xpath); for (int i = 0; i < nodes.size(); i++) { nodeList.add(nodes.get(i)); } } catch (Exception e) { throw new RuntimeException("Bad xpath: "+xpath, e); } } return nodeList; } /** * convenience routine to get query Elements * returns empty list if ANY nodes are not elements * @param node * @param xpath * @return list of nodes (immutable) - empty if none or null node */ public static List getQueryElements(Node node, String xpath) { List nodes = getQueryNodes(node, xpath); return castNodesToElements(nodes); } /** * convenience routine to get query Elements * returns empty list if ANY nodes are not elements * @param node * @param xpath * @return list of nodes (immutable) - empty if none or null node */ public static List getQueryElements(Node node, String xpath, XPathContext context) { List nodes = getQueryNodes(node, xpath, context); return castNodesToElements(nodes); } private static List castNodesToElements(List nodes) { List elements = new ArrayList(); for (Node n : nodes) { if (!(n instanceof Element)) { return new ArrayList(); } elements.add((Element) n); } return elements; } /** * get next sibling. * * @author Eliotte Rusty Harold * @param current * may be null * @return following sibling or null */ public static Node getFollowingSibling(Node current) { Node node = null; if (current != null) { ParentNode parent = current.getParent(); if (parent != null) { int index = parent.indexOf(current); if (index + 1 < parent.getChildCount()) { node = parent.getChild(index + 1); } } } return node; } /** * get previous sibling. * * @param current * @return previous sibling */ public static Node getPrecedingSibling(Node current) { Node node = null; if (current != null) { ParentNode parent = current.getParent(); if (parent != null) { int index = parent.indexOf(current); if (index > 0) { node = parent.getChild(index - 1); } } } return node; } /** * gets last text descendant of element. this might be referenced from the * following-sibling and will therefore be the immediately preceding chunk * of text in document order if the node is a text node returns itself * * @param node * @return Text node or null */ public static Text getLastTextDescendant(Node node) { List l = XMLUtil.getQueryNodes(node, ".//text() | self::text()"); return (l.size() == 0) ? null : (Text) l.get(l.size() - 1); } /** * gets first text descendant of element. this might be referenced from the * preceding-sibling and will therefore be the immediately following chunk * of text in document order if the node is a text node returns itself * * @param node * @return Text node or null */ public static Text getFirstTextDescendant(Node node) { List l = XMLUtil.getQueryNodes(node, ".//text() | self::text()"); return (l.size() == 0) ? null : (Text) l.get(0); } /** * transfers children of 'from' to 'to'. * * @param from * (will be left with no children) * @param to * (will gain 'from' children appended after any existing * children */ public static void transferChildren(Element from, Element to) { int nc = from.getChildCount(); int tc = to.getChildCount(); for (int i = nc - 1; i >= 0; i--) { Node child = from.getChild(i); child.detach(); to.insertChild(child, tc); } } /** * copies attributes of 'from' to 'to' * @param from * @param to * @throws IllegalArgumentException null arguments */ public static void copyAttributes(Element from, Element to) throws IllegalArgumentException { if (to == null || from == null) { throw new IllegalArgumentException("cannot copy null elements"); } int natt = from.getAttributeCount(); for (int i = 0; i < natt; i++) { Attribute newAtt = new Attribute(from.getAttribute(i)); to.addAttribute(newAtt); } } /** * get XOM default canonical string. * * @param node * @return the string */ public static String getCanonicalString(Node node) throws IllegalArgumentException{ if (node == null) { throw new IllegalArgumentException("null node"); } ByteArrayOutputStream baos = new ByteArrayOutputStream(); Canonicalizer canon = new Canonicalizer(baos); try { canon.write(node); } catch (IOException e) { throw new RuntimeException("should never throw " + e); } return baos.toString(); } /** * remeoves all whitespace-only text nodes. * * @param element * to strip whitespace from */ public static void removeWhitespaceNodes(Element element) { int nChild = element.getChildCount(); List nodeList = new ArrayList(); for (int i = 0; i < nChild; i++) { Node node = element.getChild(i); if (node instanceof Text) { if (node.getValue().trim().length() == 0) { nodeList.add(node); } } else if (node instanceof Element) { Element childElement = (Element) node; removeWhitespaceNodes(childElement); } else { } } for (Node node : nodeList) { node.detach(); } } /** * sets text content of element. Does not support mixed content. * * @param element * @param s * @throws RuntimeException * if element already has element content */ public static void setXMLContent(Element element, String s) { List elements = XMLUtil.getQueryNodes(element, S_STAR); if (elements.size() > 0) { throw new RuntimeException( "Cannot set text with element children"); } Text text = XMLUtil.getFirstTextDescendant(element); if (text == null) { text = new Text(s); element.appendChild(text); } else { text.setValue(s); } } /** * sets text content of element. Does not support mixed content. * * @param element * @return text value * @throws RuntimeException * if element already has element content */ public static String getXMLContent(Element element) { List elements = XMLUtil.getQueryNodes(element, S_STAR); if (elements.size() > 0) { throw new RuntimeException( "Cannot get text with element children"); } return element.getValue(); } public static String toXMLString(Element element) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); try { XMLUtil.debug(element, baos, 0); } catch (IOException e) { } return new String(baos.toByteArray()); } /** * bug report. * * @param message */ public static void BUG(String message) { Util.BUG(message); } /** * make id from string. convert to lowercase and replace space by underscore * * @param s * @return new id (null if s is null) */ public static String makeId(String s) { String id = null; if (s != null) { id = s.toLowerCase(); id = id.replace(S_SPACE, S_UNDER); } return id; } /** * create local Abstract class name. e.g. AbstractFooBar from fooBar * * @param name * @return name */ public static String makeAbstractName(String name) { return "Abstract" + capitalize(name); } /** * capitalize name e.g. FooBar from fooBar * * @param name * @return name */ public static String capitalize(String name) { return name.substring(0, 1).toUpperCase() + name.substring(1); } /** * tests 2 XML objects for equality using recursive descent. * includes namespace testing * * @param refNodeXML xml serialization of first Element * @param testElement second Element * @param stripWhite if true remove w/s nodes * @return message of where elements differ (null if identical) */ public static String equalsCanonically(String refNodeXML, Element testElement, boolean stripWhite) { Element refElement = null; try { refElement = new Builder().build(new StringReader(refNodeXML)).getRootElement(); } catch (Exception e) { throw new RuntimeException("Parsing failed: "+refNodeXML); } String message = equalsCanonically(refElement, testElement, stripWhite, "/"); LOG.trace("EQCAN "+message); return message; } /** * tests 2 XML objects for equality using recursive descent. * includes namespace testing * * @param refElement first node * @param testElement second node * @param stripWhite if true remove w/s nodes * @return message of where elements differ (null if identical) */ public static String equalsCanonically(Element refElement, Element testElement, boolean stripWhite) { return equalsCanonically(refElement, testElement, stripWhite, "./"); } /** * tests 2 XML objects for equality using recursive descent. * includes namespace testing * * @param refElement first node * @param testElement second node * @param stripWhite if true remove w/s nodes * @return message of where elements differ (null if identical) */ public static String equalsCanonically(Element refElement, Element testElement, boolean stripWhite, String xpath) { String message = null; // check if they are different objects if (refElement != testElement) { if (stripWhite) { refElement = new Element(refElement); removeWhitespaceNodes(refElement); testElement = new Element(testElement); removeWhitespaceNodes(testElement); } xpath = xpath+"*[local-name()='"+refElement.getLocalName()+"']/"; message = equalsCanonically(refElement, testElement, xpath); } return message; } private static String equalsCanonically(Element refElement, Element testElement, String xpath) { String message = null; // remove this as namespaces may be different in different serializations // message = XMLUtil.compareNamespacesCanonically(refElement, testElement, xpath); // if (message != null) { // return message; // } String refName = refElement.getLocalName(); String testName = testElement.getLocalName(); if (message == null && !refName.equals(testName)) { message = "element names differ at "+xpath+": "+refName+" != "+testName; } String refNamespace = refElement.getNamespaceURI(); String testNamespace = testElement.getNamespaceURI(); if (message == null && !refNamespace.equals(testNamespace)) { message = "element namespaces differ at "+xpath+": "+refNamespace+" != "+testNamespace; } if (message == null) { message = XMLUtil.compareAttributesCanonically(refElement, testElement, xpath); } if (message == null) { message = XMLUtil.compareChildNodesCanonically(refElement, testElement, xpath); } return message; } /** compares two XML files for equality using recursive descent. * * @param refFile * @param testFile * @param stripWhite * @return null if identical; else a message about errors in files, XML, etc. */ public static String equalsCanonically(File refFile, File testFile, boolean stripWhite) { String message = isXMLFile(refFile); message = (message != null) ? message : isXMLFile(testFile); if (message == null) { try { Element refElement = XMLUtil.parseQuietlyToDocument(refFile).getRootElement(); Element testElement = XMLUtil.parseQuietlyToDocument(testFile).getRootElement(); message = XMLUtil.equalsCanonically(refElement, testElement, stripWhite); } catch (Exception e) { message = e.getMessage(); } } return message; } /** compares XML element against reference file for equality using recursive descent. * * @param refFile * @param testElement * @param stripWhite * @return null if identical; else a message about errors in files, XML, etc. */ public static String equalsCanonically(File refFile, Element testElement, boolean stripWhite) { String message = isXMLFile(refFile); if (message == null) { try { Element refElement = XMLUtil.parseQuietlyToDocument(refFile).getRootElement(); message = XMLUtil.equalsCanonically(refElement, testElement, stripWhite); } catch (Exception e) { message = e.getMessage(); } } return message; } /** check file is existing file, not null or directory, * * @param file to check * @return null if no problems else message */ public static String isXMLFile(File file) { String message = null; if (file == null) { message = "null ref file"; } else if (!file.exists()) { message = "non-existent ref file "+file; } else if (file.isDirectory()) { message = "ref file is directory "+file; } return message; } public static String getCommonLeadingString(String s1, String s2) { int l = Math.min(s1.length(), s2.length()); int i; for (i = 0; i < l; i++) { if (s1.charAt(i) != s2.charAt(i)) { break; } } return s1.substring(0, i); } /** compare namespaces on two elements * * @param refNode * @param testNode * @param xpath current ancestry of refNode * @return */ public static String compareNamespacesCanonically(Element refNode, Element testNode, String xpath) { String message = null; List refNamespaceURIList = getNamespaceURIList(refNode); List testNamespaceURIList = getNamespaceURIList(testNode); if (refNamespaceURIList.size() != testNamespaceURIList.size()) { message = "unequal namespace count;" + " ref "+refNamespaceURIList.size()+"; " +refNamespaceURIList+";"+ " testCount "+testNamespaceURIList.size()+"; "+testNamespaceURIList; } else { for (String refNamespaceURI : refNamespaceURIList) { if (!testNamespaceURIList.contains(refNamespaceURI)) { message = "Cannot find "+refNamespaceURI+ " in test namespaces "; break; } } } return message; } /** * @param node * @param count */ private static List getNamespaceURIList(Element node) { List namespaceURIList = new ArrayList(); for (int i = 0; i < node.getNamespaceDeclarationCount(); i++) { String prefix = node.getNamespacePrefix(i); String refNamespaceURI = node.getNamespaceURI(prefix); namespaceURIList.add(refNamespaceURI); } return namespaceURIList; } /** compare attributes on two elements. * includes normalizing attribute values * * @param refNode * @param testNode * @param xpath current ancestry of refNode * @return */ public static String compareAttributesCanonically(Element refNode, Element testNode, String xpath) { String message = null; int refCount = refNode.getAttributeCount(); int testCount = testNode.getAttributeCount(); if (refCount != testCount) { message = "unequal attribute count at "+xpath+" ("+refCount+" != "+testCount+")"; } if (message == null) { for (int i = 0; i < refCount; i++) { Attribute attribute = refNode.getAttribute(i); String name = attribute.getLocalName(); String namespace = attribute.getNamespaceURI(); String value = attribute.getValue(); Attribute testAttribute = (namespace == null) ? testNode.getAttribute(name) : testNode.getAttribute(name, namespace); if (testAttribute == null) { message = "no attribute in test ("+xpath+") for "+XMLUtil.printName(name, namespace); break; } String refValue = XMLUtil.normalizeSpace(value); String testValue = XMLUtil.normalizeSpace(testAttribute.getValue()); if (!refValue.equals(testValue)) { message = "normalized attribute values for ("+xpath+"@"+XMLUtil.printName(name, namespace)+") "+refValue+" != "+testValue; break; } } } LOG.trace("ATT MS "+message); return message; } private static String printName(String name, String namespace) { return name+((namespace == null || namespace.equals(S_EMPTY)) ? "" : "["+namespace+"]"); } public static String normalizeSpace(String value) { return value.replaceAll(S_WHITEREGEX, S_SPACE).trim(); } /** compare child nodes recursively * * @param refNode * @param testNode * @param xpath current ancestry of refNode * @return */ public static String compareChildNodesCanonically(Element refNode, Element testNode, String xpath) { String message = null; int refCount = refNode.getChildCount(); int testCount = testNode.getChildCount(); if (refCount != testCount) { message = "unequal child node count at "+xpath+" ("+refCount+" != "+testCount+")"; } if (message == null) { for (int i = 0; i < refCount; i++) { String xpathChild = xpath+"node()[position()="+(i+1)+"]"; Node refChildNode = refNode.getChild(i); Node testChildNode = testNode.getChild(i); Class refClass = refChildNode.getClass(); Class testClass = testChildNode.getClass(); if (!refClass.equals(testClass)) { message = "child node classes differ at "+xpathChild+" "+refClass+"/"+testClass; break; } else if (refChildNode instanceof Element) { message = XMLUtil.equalsCanonically((Element) refChildNode, (Element) testChildNode, xpathChild); } else { message = XMLUtil.compareNonElementNodesCanonically(refChildNode, testChildNode, xpath); if (message != null) { break; } } } } return message; } /** compare non-element nodes. * not yet tuned for normalizing adjacent CDATA and other horrors * @param refNode * @param testNode * @param xpath current ancestry of refNode * @return */ public static String compareNonElementNodesCanonically(Node refNode, Node testNode, String xpath) { String message = null; String refValue = refNode.getValue(); String testValue = testNode.getValue(); if (refNode instanceof Comment) { if (!refValue.equals(testValue)) { message = "comments at ("+xpath+") differ: "+refValue+" != "+testValue; } } else if (refNode instanceof Text) { if (!refValue.equals(testValue)) { message = "text contents at ("+xpath+") differ: ["+refValue+"] != ["+testValue+"]"; } } else if (refNode instanceof ProcessingInstruction) { String refTarget = ((ProcessingInstruction) refNode).getTarget(); String testTarget = ((ProcessingInstruction) testNode).getTarget(); if (!refTarget.equals(testTarget)) { message = "PI targets at ("+xpath+") differ: "+refTarget+" != "+testTarget; } } else { LOG.warn("Unknown XML element in comparison: "+refNode.getClass()); } return message; } /** * some formatted XML introduces spurious WS after text strings * @param element */ public static void stripTrailingWhitespaceinTexts(Element element) { Nodes texts = element.query("//text()"); for (int i = 0; i < texts.size(); i++) { Text text = (Text) texts.get(i); String value = text.getValue(); value = Util.rightTrim(value); text.setValue(value); } } public static Element getSingleElement(Element element, String xpath) { Nodes nodes = element.query(xpath); return (nodes.size() == 1) ? (Element) nodes.get(0) : null; } public static void detach(nu.xom.Element element) { ParentNode parent = (element == null) ? null : element.getParent(); if (parent != null) { if (parent instanceof Document) { parent.replaceChild(element, new Element(DUMMY)); } else { element.detach(); } } } /** gets Document for element. * *

* if none exists, creates one. Note that if the element already has ancestry a Document * is created for the rootElement. That may mean that XOM.Serializer writes the whole document. *

* * @param element * @return */ public static Document ensureDocument(Element element) { Document doc = null; if (element != null) { doc = element.getDocument(); if (doc == null) { Element parent = null; Element root = element; while (true) { parent = (Element) root.getParent(); if (parent == null) break; root = parent; } doc = new Document(root); } } return doc; } public static Document parseQuietlyToDocument(InputStream is) { Document document = null; try { document = new Builder().build(is); } catch (Exception e) { throw new RuntimeException("cannot parse/read stream: ", e); } return document; } public static Document parseResourceQuietlyToDocument(String resource) { Document document = null; try { document = new Builder().build(Util.getInputStreamFromResource(resource)); } catch (Exception e) { throw new RuntimeException("cannot parse/read resource: "+resource, e); } return document; } public static Document parseQuietlyToDocument(File xmlFile) { Document document = null; try { document = new Builder().build(xmlFile); } catch (Exception e) { throw new RuntimeException("cannot parse/read file: "+xmlFile.getAbsolutePath(), e); } return document; } /** * * <!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.0//EN' 'http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd'> * * * @param s * @return * @throws IOException */ @Deprecated // relies on string .dtd" and this could be apostrophe. public static Document stripDTDAndOtherProblematicXMLHeadings(String s) throws IOException { if (s == null || s.length() == 0) { throw new RuntimeException("zero length document"); } // strip DTD int idx = s.indexOf(DTD); String baosS = s; if (idx != -1) { int ld = idx+DTD.length()+1; if (ld < 0) { throw new RuntimeException("BUG in stripping DTD"); } try { baosS = s.substring(ld); } catch (Exception e) { throw new RuntimeException("cannot parse string: ("+s.length()+"/"+ld+"/"+idx+") "+s.substring(0, Math.min(500, s.length())),e); } } // strip HTML namespace baosS = baosS.replace(" xmlns=\"http://www.w3.org/1999/xhtml\"", ""); // strip XML declaration baosS = baosS.replace("", ""); baosS = removeScripts(baosS); Document document; try { ByteArrayInputStream bais = new ByteArrayInputStream(baosS.getBytes()); // avoid reader document = new Builder().build(bais); } catch (Exception e) { throw new RuntimeException("BUG: DTD stripper failed to create valid XML", e); } return document; } /** * Removes DOCTYPE * * DOCTYPE can cause problems by requiring to load DTD from URL which can * take many seconds or, if offline, can cause failure to parse. * * This is dangerous but so is the DOCTYPE: *
 
     * <!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.0//EN'
     *   'http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd'>
     * 
* * @param s * @return */ public static String stripDTD(String s) { if (s != null) { s = s.replaceAll(DOCTYPE, ""); } return s; } /** * Removes DOCTYPE and then parses * * DOCTYPE can cause problems by requiring to load DTD from URL which can * take many seconds or, if offline, can cause failure to parse. * * This is dangerous but so is the DOCTYPE: *
 
     * <!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.0//EN'
     *   'http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd'>
     * 
* * @param s * @return */ public static Element stripDTDAndParse(String s) { Element root = null; if (s != null) { s = stripDTD(s); root = XMLUtil.parseXML(s); } return root; } /** adds missing end tag * * crude - adds /{@literal >} and then deletes any /{@literal >}{@literal (/tag)} * *
	 * String s = "<a><meta></a>";
	 * s = XMLUtil.addMissingEndTags(s, "meta");
	 * Assert.assertEquals("<a><meta/></a>", s);
	 * 
* * @param s * @param tag * @return */ public static String addMissingEndTags(String s, String tag) { // convert balanced tags ( to s = s.replaceAll(">\\s*", "/>"); StringBuilder sb = new StringBuilder(s); int i = 0; boolean inTag = false; String start = "<"+tag; int stl = start.length(); while (i < sb.length()) { if (i < sb.length() - stl && start.equals(sb.substring(i, i + stl))) { inTag = true; i += stl; } else if (inTag && (sb.charAt(i) == '>')) { if (sb.charAt(i-1) != '/') { sb.insert(i, '/'); i++; } i++; inTag = false; } else { i++; } } return sb.toString(); } public static String removeScripts(String s) { return removeTags("script", s); } public static String removeTags(String tag, String ss) { int current = 0; StringBuilder sb = new StringBuilder(); String startTag = "<"+tag; String endTag = ""; while (true) { int i = ss.indexOf(startTag, current); if (i == -1) { sb.append(ss.substring(current)); break; } sb.append(ss.substring(current, i)); i = ss.indexOf(endTag, current); if (i == -1) { throw new RuntimeException("missing endTag: "+endTag); } current = (i + endTag.length()); } return sb.toString(); } public static void debugPreserveWhitespace(Element element) { try { XMLUtil.debug(element, System.out, 0); } catch (Exception e) { throw new RuntimeException("BUG", e); } } public static Element normalizeWhitespaceInTextNodes(Element element) { Nodes texts = element.query(".//text()"); for (int i = 0; i < texts.size(); i++) { Text text = (Text) texts.get(i); text.setValue(normalizeSpace(text.getValue())); } return element; } /** * copies attributes of 'from' to 'to' * @param from * @param to */ public static void copyAttributesFromTo(Element from, Element to) { int natt = from.getAttributeCount(); for (int i = 0; i < natt; i++) { Attribute newAtt = new Attribute(from.getAttribute(i)); to.addAttribute(newAtt); } } /** it attribute exists detach it. * @param element * @param attName */ public static void deleteAttribute(Element element, String attName) { Attribute att = element.getAttribute(attName); if (att != null) { att.detach(); } } public static Document parseQuietlyToDocumentWithoutDTD(File file) { Document doc = null; try { String s = IOUtils.toString(new FileInputStream(file)); doc = XMLUtil.stripDTDAndOtherProblematicXMLHeadings(s); } catch (IOException e) { throw new RuntimeException(e); } return doc; } /** checks that attribute names are in allowed list. * * @param element * @param allowedAttributeNames * * @throws RuntimeException if unknown attribute */ public static void checkAttributeNames(Element element, List allowedAttributeNames) { for (int i = 0; i < element.getAttributeCount(); i++) { String attributeName = element.getAttribute(i).getLocalName(); if (!allowedAttributeNames.contains(attributeName)) { throw new RuntimeException("Unknown attribute: "+attributeName+" in element: "+element.getLocalName()); } } } /** checks that childNode names are in allowed list. * * @param element * @param allowedChildNodeNames * can include element names, #text, - other node types are always allowed * * @throws RuntimeException if unknown childElement */ public static void checkChildElementNames(Element element, List allowedChildNodeNames) { for (int i = 0; i < element.getChildCount(); i++) { Node childNode = element.getChild(i); if (childNode instanceof Text) { // we allow whitespace if (childNode.getValue().trim().length() > 0 && !allowedChildNodeNames.contains(TEXT)) { } } else if (childNode instanceof Element) { String elementName = element.getAttribute(i).getLocalName(); if (!allowedChildNodeNames.contains(elementName)) { throw new RuntimeException("Unknown element: "+elementName+" in element: "+element.getLocalName()); } } else { // OK } } } } euclid-euclid-2.9/src/main/java/org/xmlcml/xml/XPathGenerator.java000066400000000000000000000100271461721410700251540ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.xml; import org.apache.log4j.Level; import org.apache.log4j.Logger; import nu.xom.Attribute; import nu.xom.Element; import nu.xom.Node; import nu.xom.ParentNode; /** generates an xpath for an element in context. * * Heuristic. * * Because of the problems of namespaces, variable use of IDs etc., There are roughly three strategies: * (1) ancestor local names e.g. *[local-name()='body']/*[local-name()='div'][1]/*[local-name='p'][2] * (2) namespaced h:body/h:div[1]/h:p[2] * (3) ids *[@id='id23']/*[@id='id45']/*[@id='id99'] * * If the namespaces are not clear then (2) cannot be used. If there are no ids, then maybe we have to generate * them. but this is fragile against others doing the same with different strategy. (1) is the safest - it fails * ony when there are elements with different namespaces. so (4) we could probably extract the namespaces and use them explicitli * (4) *[local-name()='body' and namespace-uri()='http://www.w3.org/1999/xhtml']/*[local-name()='div'][1]/*[local-name='p'][2] * * I think (4) is safe, but verbose!! * * @author pm286 * */ public class XPathGenerator { private static final Logger LOG = Logger.getLogger(XPathGenerator.class); static { LOG.setLevel(Level.DEBUG); } private static final String NAME = "name"; private static final String ID = "id"; private static final String TAGX = "tagx"; private static final String[] TAGS = {ID, NAME, TAGX}; private Element xmlElement; private boolean shortFlag = false; public XPathGenerator(Element xmlElement) { this.xmlElement = xmlElement; } public void setShort(boolean shortFlag) { this.shortFlag = shortFlag; } public String getXPath() { StringBuilder sb = new StringBuilder(); addAncestors(sb, xmlElement); return sb.toString(); } private void addAncestors(StringBuilder sb, Element element) { if (element == null) return; String name = element.getLocalName(); String el = element.toXML(); // LOG.debug(">anc>"+el.toString().substring(0, Math.min(el.length(), 300))); StringBuilder sb1 = new StringBuilder(); sb1.append(((shortFlag) ? "/" + name : "/*[local-name()='" + name + "']")); // FIXME this is NOT UNIQUE Attribute attribute = getFirstUsefulAttribute(element); attribute = null; // FIXME if (attribute != null) { sb1.append("[@" + attribute.getLocalName() + "='" + attribute.getValue() + "']"); } else { int ordinal = getOrdinalOfChildWithName(element, name); sb1.append("[" + ordinal + "]"); } String ss = sb1.toString(); // LOG.debug(">"+ss); sb.insert(0, ss); ParentNode parent = element.getParent(); if (parent != null && parent instanceof Element) { addAncestors(sb, (Element) parent); } } private int getOrdinalOfChildWithName(Element element, String name) { int ordinal = 1; // we count from 1 in XPath ParentNode parent = element.getParent(); if (parent != null) { int position = parent.indexOf(element); for (int i = 0; i < position; i++) { Node sibling = parent.getChild(i); if (sibling instanceof Element && name.equals(((Element) sibling).getLocalName())) { ordinal++; } } } return ordinal; } private Attribute getFirstUsefulAttribute(Element element) { for (String tag : TAGS) { Attribute attribute = element.getAttribute(tag); if (attribute != null) { if (attribute.getValue().trim().length() > 0) { return attribute; } } } return null; } } euclid-euclid-2.9/src/main/resources/000077500000000000000000000000001461721410700176245ustar00rootroot00000000000000euclid-euclid-2.9/src/main/resources/header.txt000066400000000000000000000011131461721410700216110ustar00rootroot00000000000000 Copyright 2011 Peter Murray-Rust 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.9/src/main/resources/log4j.properties000077500000000000000000000015231461721410700227650ustar00rootroot00000000000000# # Copyright 2011 Peter Murray-Rust # # 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. # log4j.rootLogger=DEBUG, A1 log4j.appender.A1=org.apache.log4j.ConsoleAppender log4j.appender.A1.layout=org.apache.log4j.PatternLayout log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n log4j.logger.uk.ac.cam=DEBUG euclid-euclid-2.9/src/test/000077500000000000000000000000001461721410700156455ustar00rootroot00000000000000euclid-euclid-2.9/src/test/java/000077500000000000000000000000001461721410700165665ustar00rootroot00000000000000euclid-euclid-2.9/src/test/java/blogspot/000077500000000000000000000000001461721410700204175ustar00rootroot00000000000000euclid-euclid-2.9/src/test/java/blogspot/software_and_algorithms/000077500000000000000000000000001461721410700253245ustar00rootroot00000000000000euclid-euclid-2.9/src/test/java/blogspot/software_and_algorithms/stern_library/000077500000000000000000000000001461721410700302035ustar00rootroot00000000000000euclid-euclid-2.9/src/test/java/blogspot/software_and_algorithms/stern_library/data_structure/000077500000000000000000000000001461721410700332345ustar00rootroot00000000000000DynamicIntervalTreeTest.java000066400000000000000000000234671461721410700406050ustar00rootroot00000000000000euclid-euclid-2.9/src/test/java/blogspot/software_and_algorithms/stern_library/data_structurepackage blogspot.software_and_algorithms.stern_library.data_structure; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import org.junit.AfterClass; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; /* 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. */ /** * Test class for DynamicIntervalTree. * * @author Kevin L. Stern */ public class DynamicIntervalTreeTest { private static List> masterClosed; private static List> masterHalfOpenLow; private static List> masterHalfOpenHigh; private static List> masterOpen; private static DynamicIntervalTree> masterClosedTree; private static DynamicIntervalTree> masterHalfOpenLowTree; private static DynamicIntervalTree> masterHalfOpenHighTree; private static DynamicIntervalTree> masterOpenTree; private static void executeContainingIntervalsTest( List queryPoints, List> master, DynamicIntervalTree> tree) { Set> result = new HashSet>(); for (Double queryPoint : queryPoints) { result.addAll(tree.fetchContainingIntervals(queryPoint)); Assert.assertEquals(fetchContainingIntervals(master, queryPoint), result); result.clear(); } } private static void executeOverlappingIntervalsTest( List> queryIntervals, List> master, DynamicIntervalTree> tree) { Set> result = new HashSet>(); for (Interval next : queryIntervals) { result.addAll(tree.fetchOverlappingIntervals(next)); Assert.assertEquals(fetchOverlappingIntervals(master, next), result); result.clear(); } } /** * Helper method to fetch the set of intervals from the master list that * contain the query point using the brute force method. This gives a * correct answer against which to compare the result from the interval * tree. */ private static Set> fetchContainingIntervals( List> master, double queryPoint) { Set> result = new HashSet>(); for (Interval next : master) { if (next.contains(queryPoint)) { result.add(next); } } return result; } /** * Helper method to fetch the set of intervals from the master list that * overlap the query interval using the brute force method. This gives a * correct answer against which to compare the result from the interval * tree. */ private static Set> fetchOverlappingIntervals( List> master, Interval queryInterval) { Set> result = new HashSet>(); for (Interval next : master) { if (next.overlaps(queryInterval)) { result.add(next); } } return result; } @BeforeClass public static void globalSetup() { masterClosed = new ArrayList>(); masterHalfOpenLow = new ArrayList>(); masterHalfOpenHigh = new ArrayList>(); masterOpen = new ArrayList>(); final double intervalWidth = 0.1; final double halfIntervalWidth = intervalWidth / 2; for (int j = 0; j < 10; j++) { Interval next; next = new Interval(intervalWidth * j, true, intervalWidth * (j + 1), true); masterClosed.add(next); masterClosed.add(new Interval(next.getLow() + halfIntervalWidth, true, next.getHigh() + halfIntervalWidth, true)); next = new Interval(intervalWidth * j, false, intervalWidth * (j + 1), true); masterHalfOpenLow.add(next); masterHalfOpenLow.add(new Interval(next.getLow() + halfIntervalWidth, false, next.getHigh() + halfIntervalWidth, true)); next = new Interval(intervalWidth * j, true, intervalWidth * (j + 1), false); masterHalfOpenHigh.add(next); masterHalfOpenHigh.add(new Interval(next.getLow() + halfIntervalWidth, true, next.getHigh() + halfIntervalWidth, false)); next = new Interval(intervalWidth * j, false, intervalWidth * (j + 1), false); masterOpen.add(next); masterOpen.add(new Interval(next.getLow() + halfIntervalWidth, false, next.getHigh() + halfIntervalWidth, false)); } masterClosedTree = makeTree(masterClosed); masterHalfOpenLowTree = makeTree(masterHalfOpenLow); masterHalfOpenHighTree = makeTree(masterHalfOpenHigh); masterOpenTree = makeTree(masterOpen); } @AfterClass public static void globalTeardown() { masterClosed = null; masterHalfOpenLow = null; masterHalfOpenHigh = null; masterOpen = null; masterClosedTree = null; masterHalfOpenLowTree = null; masterHalfOpenHighTree = null; masterOpenTree = null; } /** * Helper method to make and populate an IntervalTree based upon the * specified list of intervals. */ private static DynamicIntervalTree> makeTree( List> list) { DynamicIntervalTree> tree = new DynamicIntervalTree>(); for (Interval next : list) tree.insert(next); return tree; } @Test public void testClear() { DynamicIntervalTree> tree = makeTree(masterClosed); Assert.assertEquals(masterClosed.size(), tree.getSize()); tree.clear(); Assert.assertTrue(tree.fetchContainingIntervals( masterClosed.get(0).getLow()).isEmpty()); Assert.assertEquals(0, tree.getSize()); } @Test public void testConstructionPointerEdgeCases() { List> master = new ArrayList>(); master.add(new Interval(0.0, true, 0.1, true)); master.add(new Interval(0.2, true, 0.3, true)); master.add(new Interval(0.4, true, 0.5, true)); master.add(new Interval(0.6, true, 0.7, true)); DynamicIntervalTree> tree = makeTree(master); Collection> result = tree .fetchContainingIntervals(0.25); Assert.assertEquals(1, result.size()); Assert.assertEquals(master.get(1), result.iterator().next()); } @Test public void testContainingIntervals() { List queryPointList = new ArrayList(); for (Interval next : masterClosed) { queryPointList.add(next.getLow()); } executeContainingIntervalsTest(queryPointList, masterClosed, masterClosedTree); executeContainingIntervalsTest(queryPointList, masterHalfOpenLow, masterHalfOpenLowTree); executeContainingIntervalsTest(queryPointList, masterHalfOpenHigh, masterHalfOpenHighTree); executeContainingIntervalsTest(queryPointList, masterOpen, masterOpenTree); } @Test public void testNull() { DynamicIntervalTree> tree = new DynamicIntervalTree>(); try { tree.fetchContainingIntervals((Double) null); Assert.fail(); } catch (NullPointerException e) { } try { tree.fetchOverlappingIntervals((Interval) null); Assert.fail(); } catch (NullPointerException e) { } Assert.assertFalse(tree.delete(null)); try { tree.insert(null); Assert.fail(); } catch (NullPointerException e) { } } @Test public void testOverlappingIntervals() { executeOverlappingIntervalsTest(masterClosed, masterClosed, masterClosedTree); executeOverlappingIntervalsTest(masterHalfOpenLow, masterClosed, masterClosedTree); executeOverlappingIntervalsTest(masterHalfOpenHigh, masterClosed, masterClosedTree); executeOverlappingIntervalsTest(masterOpen, masterClosed, masterClosedTree); executeOverlappingIntervalsTest(masterClosed, masterHalfOpenLow, masterHalfOpenLowTree); executeOverlappingIntervalsTest(masterHalfOpenLow, masterHalfOpenLow, masterHalfOpenLowTree); executeOverlappingIntervalsTest(masterHalfOpenHigh, masterHalfOpenLow, masterHalfOpenLowTree); executeOverlappingIntervalsTest(masterOpen, masterHalfOpenLow, masterHalfOpenLowTree); executeOverlappingIntervalsTest(masterClosed, masterHalfOpenHigh, masterHalfOpenHighTree); executeOverlappingIntervalsTest(masterHalfOpenLow, masterHalfOpenHigh, masterHalfOpenHighTree); executeOverlappingIntervalsTest(masterHalfOpenHigh, masterHalfOpenHigh, masterHalfOpenHighTree); executeOverlappingIntervalsTest(masterOpen, masterHalfOpenHigh, masterHalfOpenHighTree); executeOverlappingIntervalsTest(masterClosed, masterOpen, masterOpenTree); executeOverlappingIntervalsTest(masterHalfOpenLow, masterOpen, masterOpenTree); executeOverlappingIntervalsTest(masterHalfOpenHigh, masterOpen, masterOpenTree); executeOverlappingIntervalsTest(masterOpen, masterOpen, masterOpenTree); } } IntervalTest.java000066400000000000000000000137731461721410700364570ustar00rootroot00000000000000euclid-euclid-2.9/src/test/java/blogspot/software_and_algorithms/stern_library/data_structurepackage blogspot.software_and_algorithms.stern_library.data_structure; import junit.framework.Assert; import org.junit.Test; /* 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. */ /** * Test class for Interval. * * @author Kevin L. Stern */ public class IntervalTest { @Test public void testCompareTo() { Interval i1 = new Interval(0.0, true, 10.0, true); Interval i2 = new Interval(0.0, true, 10.0, false); Interval i3 = new Interval(0.0, false, 10.0, true); Interval i4 = new Interval(1.0, true, 10.0, true); Interval i5 = new Interval(0.0, true, 11.0, true); Assert.assertTrue(i1.compareTo(i1) == 0); Assert.assertTrue(i1.compareTo(i2) < 0); Assert.assertTrue(i1.compareTo(i3) < 0); Assert.assertTrue(i1.compareTo(i4) < 0); Assert.assertTrue(i1.compareTo(i5) < 0); Assert.assertTrue(i2.compareTo(i1) > 0); Assert.assertTrue(i3.compareTo(i1) > 0); Assert.assertTrue(i4.compareTo(i1) > 0); Assert.assertTrue(i5.compareTo(i1) > 0); } @Test public void testContainsInterval() { Interval i1 = new Interval(0.0, true, 10.0, true); Interval i2 = new Interval(0.0, true, 10.0, false); Interval i3 = new Interval(0.0, false, 10.0, true); Interval i4 = new Interval(0.0, false, 10.0, false); Interval i5 = new Interval(1.0, true, 9.0, true); Assert.assertTrue(i1.contains(i1)); Assert.assertTrue(i1.contains(i2)); Assert.assertTrue(i1.contains(i3)); Assert.assertTrue(i1.contains(i4)); Assert.assertTrue(i1.contains(i5)); Assert.assertFalse(i2.contains(i1)); Assert.assertFalse(i3.contains(i1)); Assert.assertFalse(i4.contains(i1)); Assert.assertFalse(i5.contains(i1)); } @Test public void testContainsPoint() { Interval i; i = new Interval(0.0, true, 10.0, true); for (int j = 0; j < 10; j++) { Assert.assertTrue(i.contains((double) j)); } i = new Interval(0.0, false, 10.0, true); Assert.assertFalse(i.contains((double) 0)); for (int j = 1; j < 10; j++) { Assert.assertTrue(i.contains((double) j)); } i = new Interval(0.0, false, 10.0, false); Assert.assertFalse(i.contains((double) 0)); Assert.assertFalse(i.contains((double) 10)); for (int j = 1; j < 9; j++) { Assert.assertTrue(i.contains((double) j)); } } @Test public void testEquals() { Interval i1 = new Interval(0.0, true, 10.0, true); Interval i2 = new Interval(0.0, true, 10.0, false); Interval i3 = new Interval(0.0, false, 10.0, true); Interval i4 = new Interval(0.0, false, 10.0, false); Interval i5 = new Interval(1.0, true, 9.0, true); Assert.assertTrue(i1.equals(new Interval(i1.getLow(), i1 .isClosedOnLow(), i1.getHigh(), i1.isClosedOnHigh()))); Assert.assertFalse(i1.equals(i2)); Assert.assertFalse(i1.equals(i3)); Assert.assertFalse(i1.equals(i4)); Assert.assertFalse(i1.equals(i5)); Assert.assertFalse(i2.equals(i1)); Assert.assertFalse(i3.equals(i1)); Assert.assertFalse(i4.equals(i1)); Assert.assertFalse(i5.equals(i1)); } @Test public void testHashcode() { Interval i = new Interval(0.0, true, 10.0, true); Assert.assertFalse(i.hashCode() == new Interval(1.0, true, 10.0, true).hashCode()); Assert.assertFalse(i.hashCode() == new Interval(0.0, true, 11.0, true).hashCode()); Assert.assertFalse(i.hashCode() == new Interval(0.0, true, 10.0, false).hashCode()); Assert.assertFalse(i.hashCode() == new Interval(0.0, false, 10.0, true).hashCode()); Assert.assertFalse(i.hashCode() == new Interval(0.0, false, 10.0, false).hashCode()); } @Test public void testOverlaps() { Interval i1 = new Interval(5.0, true, 10.0, true); Interval i2 = new Interval(10.0, true, 15.0, true); Interval i3 = new Interval(10.0, false, 15.0, true); Interval i4 = new Interval(0.0, true, 5.0, true); Interval i5 = new Interval(0.0, true, 5.0, false); Interval i6 = new Interval(6.0, false, 9.0, false); Assert.assertTrue(i1.overlaps(i1)); Assert.assertTrue(i1.overlaps(i2)); Assert.assertFalse(i1.overlaps(i3)); Assert.assertTrue(i1.overlaps(i4)); Assert.assertFalse(i1.overlaps(i5)); Assert.assertTrue(i1.overlaps(i6)); Assert.assertTrue(i2.overlaps(i1)); Assert.assertFalse(i3.overlaps(i1)); Assert.assertTrue(i4.overlaps(i1)); Assert.assertFalse(i5.overlaps(i1)); Assert.assertTrue(i6.overlaps(i1)); } @Test public void testToString() { Assert.assertEquals("(0, 1)", new Interval(0, false, 1, false).toString()); Assert.assertEquals("(0, 1]", new Interval(0, false, 1, true).toString()); Assert.assertEquals("[0, 1)", new Interval(0, true, 1, false).toString()); Assert.assertEquals("[0, 1]", new Interval(0, true, 1, true).toString()); } } OrderLinkedRedBlackTreeTest.java000066400000000000000000000070671461721410700413040ustar00rootroot00000000000000euclid-euclid-2.9/src/test/java/blogspot/software_and_algorithms/stern_library/data_structurepackage blogspot.software_and_algorithms.stern_library.data_structure; import java.util.ArrayList; import java.util.List; import org.junit.Assert; import org.junit.Test; /* 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. */ /** * Test class for OrderLinkedRedBlackTree. * * @author Kevin L. Stern */ public class OrderLinkedRedBlackTreeTest { @Test public void testNull() { OrderLinkedRedBlackTree tree = new OrderLinkedRedBlackTree(); try { tree.insert(null); Assert.fail(); } catch (NullPointerException e) { } try { tree.getPredecessor(null); Assert.fail(); } catch (NullPointerException e) { } try { tree.getSuccessor(null); Assert.fail(); } catch (NullPointerException e) { } Assert.assertNull(tree.delete(null)); Assert.assertNull(tree.getNode(null)); Assert.assertFalse(tree.contains(null)); tree.insert(0); try { tree.insert(null); Assert.fail(); } catch (NullPointerException e) { } try { tree.getPredecessor(null); Assert.fail(); } catch (NullPointerException e) { } try { tree.getSuccessor(null); Assert.fail(); } catch (NullPointerException e) { } Assert.assertNull(tree.delete(null)); Assert.assertNull(tree.getNode(null)); Assert.assertFalse(tree.contains(null)); } @Test public void testPredecessor() { List master = new ArrayList(); OrderLinkedRedBlackTree tree = new OrderLinkedRedBlackTree(); for (int j = 0; j < 100; j++) { tree.insert(j); master.add(j); } while (!master.isEmpty()) { for (int j = 1; j < master.size(); j++) { Assert.assertEquals(master.get(j - 1), tree.getPredecessor(tree.getNode(master.get(j))) .getValue()); } Assert.assertNull(tree.getPredecessor(tree.getNode(master.get(0)))); int index = master.size() >> 1; tree.delete(master.get(index)); master.remove(index); } } @Test public void testSuccessor() { List master = new ArrayList(); OrderLinkedRedBlackTree tree = new OrderLinkedRedBlackTree(); for (int j = 0; j < 100; j++) { tree.insert(j); master.add(j); } while (!master.isEmpty()) { for (int j = 0; j < master.size() - 1; j++) { Assert.assertEquals(master.get(j + 1), tree.getSuccessor(tree.getNode(master.get(j))) .getValue()); } Assert.assertNull(tree.getSuccessor(tree.getNode(master.get(master .size() - 1)))); int index = master.size() >> 1; tree.delete(master.get(index)); master.remove(index); } } } RedBlackTreeTest.java000066400000000000000000000126511461721410700371540ustar00rootroot00000000000000euclid-euclid-2.9/src/test/java/blogspot/software_and_algorithms/stern_library/data_structurepackage blogspot.software_and_algorithms.stern_library.data_structure; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; import java.util.TreeSet; import org.junit.Assert; import org.junit.Test; /* 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. */ /** * Test class for RedBlackTree. * * @author Kevin L. Stern */ public class RedBlackTreeTest { private static void equalsHelper(Collection master, RedBlackTree tree) { Assert.assertEquals(master.size(), tree.getSize()); Iterator iMaster = master.iterator(); Iterator iTree = tree.iterator(); while (iMaster.hasNext()) { Assert.assertTrue(iTree.hasNext()); Assert.assertEquals(iMaster.next(), iTree.next()); } Assert.assertFalse(iTree.hasNext()); } @Test public void testContains() { RedBlackTree tree = new RedBlackTree(); for (int j = 0; j < 100; j++) { Assert.assertFalse(tree.contains(j)); tree.insert(j); Assert.assertTrue(tree.contains(j)); } } @Test public void testDelete() { List master = new ArrayList(); RedBlackTree tree = new RedBlackTree(); for (int j = 0; j < 100; j++) { tree.insert(j); master.add(j); } while (!master.isEmpty()) { Integer val = master.get(master.size() >> 1); master.remove(val); tree.delete(val); equalsHelper(master, tree); } } @Test public void testInsert() { TreeSet master = new TreeSet(); RedBlackTree tree = new RedBlackTree(); for (int j = 99; j >= 0; j--) { tree.insert(j); master.add(j); } equalsHelper(master, tree); } @Test public void testIterator() { RedBlackTree tree = new RedBlackTree(); for (int j = 0; j < 100; j++) { tree.insert(j); } Iterator i = tree.iterator(); try { i.remove(); Assert.fail(); } catch (NoSuchElementException e) { } Assert.assertTrue(i.hasNext()); int test = 0; for (; i.hasNext();) { Integer val = i.next(); Assert.assertEquals(Integer.valueOf(test++), val); if ((val & 1) == 0) { i.remove(); } } try { i.next(); Assert.fail(); } catch (NoSuchElementException e) { } i = tree.iterator(); for (int j = 1; j < 100; j += 2) { Assert.assertEquals(Integer.valueOf(j), i.next()); i.remove(); } try { i.remove(); Assert.fail(); } catch (NoSuchElementException e) { } Assert.assertTrue(tree.isEmpty()); } @Test public void testNull() { RedBlackTree tree = new RedBlackTree(); try { tree.insert(null); Assert.fail(); } catch (NullPointerException e) { } try { tree.getPredecessor(null); Assert.fail(); } catch (NullPointerException e) { } try { tree.getSuccessor(null); Assert.fail(); } catch (NullPointerException e) { } Assert.assertNull(tree.delete(null)); Assert.assertNull(tree.getNode(null)); Assert.assertFalse(tree.contains(null)); tree.insert(0); try { tree.insert(null); Assert.fail(); } catch (NullPointerException e) { } try { tree.getPredecessor(null); Assert.fail(); } catch (NullPointerException e) { } try { tree.getSuccessor(null); Assert.fail(); } catch (NullPointerException e) { } Assert.assertNull(tree.delete(null)); Assert.assertNull(tree.getNode(null)); Assert.assertFalse(tree.contains(null)); } @Test public void testPredecessor() { RedBlackTree tree = new RedBlackTree(); for (int j = 0; j < 100; j++) { tree.insert(j); } for (int j = 1; j < 100; j++) { Assert.assertEquals(Integer.valueOf(j - 1), tree.getPredecessor(tree.getNode(j)).getValue()); } Assert.assertNull(tree.getPredecessor(tree.getNode(0))); } @Test public void testSuccessor() { RedBlackTree tree = new RedBlackTree(); for (int j = 0; j < 100; j++) { tree.insert(j); } for (int j = 0; j < 99; j++) { Assert.assertEquals(Integer.valueOf(j + 1), tree.getSuccessor(tree.getNode(j)).getValue()); } Assert.assertNull(tree.getSuccessor(tree.getNode(99))); } @Test public void testToString() { RedBlackTree tree = new RedBlackTree(); Assert.assertEquals("{}", tree.toString()); tree.insert(2); Assert.assertEquals("{2}", tree.toString()); tree.insert(3); Assert.assertEquals("{2, 3}", tree.toString()); } } StaticIntervalTreeTest.java000066400000000000000000000256051461721410700404440ustar00rootroot00000000000000euclid-euclid-2.9/src/test/java/blogspot/software_and_algorithms/stern_library/data_structurepackage blogspot.software_and_algorithms.stern_library.data_structure; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import org.junit.AfterClass; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; /* 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. */ /** * Test class for StaticIntervalTree. * * @author Kevin L. Stern */ public class StaticIntervalTreeTest { private static List> masterClosed; private static List> masterHalfOpenLow; private static List> masterHalfOpenHigh; private static List> masterOpen; private static StaticIntervalTree> masterClosedTree; private static StaticIntervalTree> masterHalfOpenLowTree; private static StaticIntervalTree> masterHalfOpenHighTree; private static StaticIntervalTree> masterOpenTree; private static void executeContainingIntervalsTest( List queryPoints, List> master, StaticIntervalTree> tree) { Set> result = new HashSet>(); for (Double queryPoint : queryPoints) { Assert.assertEquals(fetchContainingIntervals(master, queryPoint), tree.fetchContainingIntervals(result, queryPoint)); result.clear(); } } private static void executeOverlappingIntervalsTest( List> queryIntervals, List> master, StaticIntervalTree> tree) { Set> result = new HashSet>(); for (Interval next : queryIntervals) { Assert.assertEquals(fetchOverlappingIntervals(master, next), tree.fetchOverlappingIntervals(result, next)); result.clear(); } } /** * Helper method to fetch the set of intervals from the master list that * contain the query point using the brute force method. This gives a * correct answer against which to compare the result from the interval * tree. */ private static Set> fetchContainingIntervals( List> master, double queryPoint) { Set> result = new HashSet>(); for (Interval next : master) { if (next.contains(queryPoint)) { result.add(next); } } return result; } /** * Helper method to fetch the set of intervals from the master list that * overlap the query interval using the brute force method. This gives a * correct answer against which to compare the result from the interval * tree. */ private static Set> fetchOverlappingIntervals( List> master, Interval queryInterval) { Set> result = new HashSet>(); for (Interval next : master) { if (next.overlaps(queryInterval)) { result.add(next); } } return result; } @BeforeClass public static void globalSetup() { masterClosed = new ArrayList>(); masterHalfOpenLow = new ArrayList>(); masterHalfOpenHigh = new ArrayList>(); masterOpen = new ArrayList>(); final double intervalWidth = 0.1; final double halfIntervalWidth = intervalWidth / 2; for (int j = 0; j < 10; j++) { Interval next; next = new Interval(intervalWidth * j, true, intervalWidth * (j + 1), true); masterClosed.add(next); masterClosed.add(new Interval(next.getLow() + halfIntervalWidth, true, next.getHigh() + halfIntervalWidth, true)); next = new Interval(intervalWidth * j, false, intervalWidth * (j + 1), true); masterHalfOpenLow.add(next); masterHalfOpenLow.add(new Interval(next.getLow() + halfIntervalWidth, false, next.getHigh() + halfIntervalWidth, true)); next = new Interval(intervalWidth * j, true, intervalWidth * (j + 1), false); masterHalfOpenHigh.add(next); masterHalfOpenHigh.add(new Interval(next.getLow() + halfIntervalWidth, true, next.getHigh() + halfIntervalWidth, false)); next = new Interval(intervalWidth * j, false, intervalWidth * (j + 1), false); masterOpen.add(next); masterOpen.add(new Interval(next.getLow() + halfIntervalWidth, false, next.getHigh() + halfIntervalWidth, false)); } masterClosedTree = makeTree(masterClosed); masterHalfOpenLowTree = makeTree(masterHalfOpenLow); masterHalfOpenHighTree = makeTree(masterHalfOpenHigh); masterOpenTree = makeTree(masterOpen); } @AfterClass public static void globalTeardown() { masterClosed = null; masterHalfOpenLow = null; masterHalfOpenHigh = null; masterOpen = null; masterClosedTree = null; masterHalfOpenLowTree = null; masterHalfOpenHighTree = null; masterOpenTree = null; } /** * Helper method to make and populate an IntervalTree based upon the * specified list of intervals. */ private static StaticIntervalTree> makeTree( List> list) { StaticIntervalTree> tree = new StaticIntervalTree>(); tree.buildTree(new HashSet>(list)); for (Interval next : list) tree.insert(next); return tree; } @Test public void testClear() { StaticIntervalTree> tree = makeTree(masterClosed); Assert.assertEquals(masterClosed.size(), tree.getSize()); tree.clear(); Assert.assertTrue(tree .fetchContainingIntervals(new ArrayList>(), masterClosed.get(0).getLow()).isEmpty()); Assert.assertEquals(0, tree.getSize()); /* * Test that the tree structure remains intact. */ for (Interval next : masterClosed) { tree.insert(next); } Assert.assertFalse(tree .fetchContainingIntervals(new ArrayList>(), masterClosed.get(0).getLow()).isEmpty()); } @Test public void testConstructionPointerEdgeCases() { List> master = new ArrayList>(); master.add(new Interval(0.0, true, 0.1, true)); master.add(new Interval(0.2, true, 0.3, true)); master.add(new Interval(0.4, true, 0.5, true)); master.add(new Interval(0.6, true, 0.7, true)); StaticIntervalTree> tree = makeTree(master); List> result = tree.fetchContainingIntervals( new ArrayList>(), 0.25); Assert.assertEquals(1, result.size()); Assert.assertEquals(master.get(1), result.get(0)); } @Test public void testContainingIntervals() { List queryPointList = new ArrayList(); for (Interval next : masterClosed) { queryPointList.add(next.getLow()); } executeContainingIntervalsTest(queryPointList, masterClosed, masterClosedTree); executeContainingIntervalsTest(queryPointList, masterHalfOpenLow, masterHalfOpenLowTree); executeContainingIntervalsTest(queryPointList, masterHalfOpenHigh, masterHalfOpenHighTree); executeContainingIntervalsTest(queryPointList, masterOpen, masterOpenTree); } @Test public void testNull() { StaticIntervalTree> tree = new StaticIntervalTree>(); try { tree.buildTree(null); Assert.fail(); } catch (NullPointerException e) { } try { tree.fetchContainingIntervals(new ArrayList>(), (Double) null); Assert.fail(); } catch (NullPointerException e) { } try { tree.fetchContainingIntervals((Collection>) null, 0.0); Assert.fail(); } catch (NullPointerException e) { } try { tree.fetchOverlappingIntervals(new ArrayList>(), (Interval) null); Assert.fail(); } catch (NullPointerException e) { } try { tree.fetchOverlappingIntervals((Collection>) null, new Interval(0.0, true, 1.0, true)); Assert.fail(); } catch (NullPointerException e) { } Assert.assertFalse(tree.delete(null)); Set> list = new HashSet>(masterClosed); list.add(null); try { tree.buildTree(list); Assert.fail(); } catch (NullPointerException e) { } tree.buildTree(new HashSet>(masterClosed)); try { tree.insert(null); Assert.fail(); } catch (NullPointerException e) { } } @Test public void testOverlappingIntervals() { executeOverlappingIntervalsTest(masterClosed, masterClosed, masterClosedTree); executeOverlappingIntervalsTest(masterHalfOpenLow, masterClosed, masterClosedTree); executeOverlappingIntervalsTest(masterHalfOpenHigh, masterClosed, masterClosedTree); executeOverlappingIntervalsTest(masterOpen, masterClosed, masterClosedTree); executeOverlappingIntervalsTest(masterClosed, masterHalfOpenLow, masterHalfOpenLowTree); executeOverlappingIntervalsTest(masterHalfOpenLow, masterHalfOpenLow, masterHalfOpenLowTree); executeOverlappingIntervalsTest(masterHalfOpenHigh, masterHalfOpenLow, masterHalfOpenLowTree); executeOverlappingIntervalsTest(masterOpen, masterHalfOpenLow, masterHalfOpenLowTree); executeOverlappingIntervalsTest(masterClosed, masterHalfOpenHigh, masterHalfOpenHighTree); executeOverlappingIntervalsTest(masterHalfOpenLow, masterHalfOpenHigh, masterHalfOpenHighTree); executeOverlappingIntervalsTest(masterHalfOpenHigh, masterHalfOpenHigh, masterHalfOpenHighTree); executeOverlappingIntervalsTest(masterOpen, masterHalfOpenHigh, masterHalfOpenHighTree); executeOverlappingIntervalsTest(masterClosed, masterOpen, masterOpenTree); executeOverlappingIntervalsTest(masterHalfOpenLow, masterOpen, masterOpenTree); executeOverlappingIntervalsTest(masterHalfOpenHigh, masterOpen, masterOpenTree); executeOverlappingIntervalsTest(masterOpen, masterOpen, masterOpenTree); } } ThriftyListTest.java000066400000000000000000000313751461721410700371560ustar00rootroot00000000000000euclid-euclid-2.9/src/test/java/blogspot/software_and_algorithms/stern_library/data_structurepackage blogspot.software_and_algorithms.stern_library.data_structure; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.PrintWriter; import java.io.StringWriter; import java.util.Arrays; import java.util.Collections; import java.util.Iterator; import java.util.ListIterator; import java.util.NoSuchElementException; import junit.framework.Assert; import org.junit.Test; /* 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. */ /** * Test case for ThriftyList. * * @author Kevin L. Stern */ public class ThriftyListTest { @Test public void testAddAll() { ThriftyList list = new ThriftyList(); list.addAll(0, Collections. emptyList()); list.addAll(Arrays.asList(10, 11, 12, 13, 14)); list.addAll(0, Arrays.asList(0, 1, 2, 3, 4)); list.addAll(5, Arrays.asList(5, 6, 7, 8, 9)); list.addAll(0, Collections. emptyList()); list.addAll(5, Collections. emptyList()); list.addAll(Collections. emptyList()); Assert.assertEquals(15, list.size()); for (int j = 0; j < 15; j++) { Assert.assertEquals(Integer.valueOf(j), list.get(j)); } } @Test public void testAddFirstThenRemoveFirst() { ThriftyList list = new ThriftyList(); int testCount = 100; for (int j = 0; j < testCount; j++) { list.addFirst(j); } for (int j = testCount - 1; j >= 0; j--) { Assert.assertEquals(Integer.valueOf(j), list.removeFirst()); } Assert.assertEquals(0, list.size()); } @Test public void testAddLastThenRemoveLast() { ThriftyList list = new ThriftyList(); int testCount = 100; for (int j = 0; j < testCount; j++) { list.addLast(j); } for (int j = testCount - 1; j >= 0; j--) { Assert.assertEquals(Integer.valueOf(j), list.removeLast()); } Assert.assertEquals(0, list.size()); } @Test public void testClone() { ThriftyList list = new ThriftyList(); int testCount = 10; for (int j = 0; j < testCount; j++) { list.add(j); } ThriftyList cloneList = (ThriftyList) list.clone(); Assert.assertEquals(testCount, cloneList.size()); for (int j = 0; j < testCount; j++) { Assert.assertEquals(Integer.valueOf(j), cloneList.get(j)); } } @Test public void testContains() { ThriftyList list = new ThriftyList(); int testCount = 10; for (int j = 0; j < testCount; j++) { list.add(j); } for (int j = 0; j < testCount; j++) { Assert.assertTrue(list.contains(j)); } Assert.assertFalse(list .contains("This String is certainly not in the list")); Integer[] allValues = new Integer[testCount]; for (int j = 0; j < testCount; j++) { allValues[j] = j; } Assert.assertTrue(list.containsAll(Arrays.asList(allValues))); allValues[0] = -1; Assert.assertFalse(list.containsAll(Arrays.asList(allValues))); } @Test public void testDescendingIterator() { ThriftyList list = new ThriftyList(); Iterator ri = list.descendingIterator(); Assert.assertFalse(ri.hasNext()); try { ri.next(); Assert.fail(); } catch (NoSuchElementException e) { } try { ri.remove(); Assert.fail(); } catch (IllegalStateException e) { } int testCount = 100; for (int j = 0; j < testCount; j++) { list.add(j); } ri = list.descendingIterator(); for (int j = testCount - 1; j >= 0; j--) { Assert.assertTrue(ri.hasNext()); Assert.assertEquals(Integer.valueOf(j), ri.next()); } ri = list.descendingIterator(); try { ri.remove(); Assert.fail(); } catch (IllegalStateException e) { } for (int j = testCount - 1; j >= 0; j--) { Assert.assertTrue(ri.hasNext()); Assert.assertEquals(Integer.valueOf(j), ri.next()); ri.remove(); } Assert.assertFalse(ri.hasNext()); Assert.assertEquals(0, list.size()); try { ri.next(); Assert.fail(); } catch (NoSuchElementException e) { } } @Test public void testGet() { ThriftyList list = new ThriftyList(); int testCount = 10; for (int j = 0; j < testCount; j++) { list.add(j); } for (int j = 0; j < testCount; j++) { Assert.assertEquals(Integer.valueOf(j), list.get(j)); } Assert.assertEquals(Integer.valueOf(0), list.getFirst()); Assert.assertEquals(Integer.valueOf(list.get(list.size() - 1)), list.getLast()); } @Test public void testIndexOfAndLastIndexOf() { ThriftyList list = new ThriftyList(); int testCount = 10; for (int j = 0; j < testCount; j++) { list.add(j); } for (int j = 0; j < testCount; j++) { Assert.assertEquals(j, list.indexOf(list.get(j))); Assert.assertEquals(j, list.lastIndexOf(list.get(j))); } Assert.assertEquals(-1, list.indexOf("This String is certainly not in the list")); Assert.assertEquals(-1, list.lastIndexOf("This String is certainly not in the list")); list.add(0); Assert.assertEquals(0, list.indexOf(0)); Assert.assertEquals(list.size() - 1, list.lastIndexOf(0)); } @Test public void testInsertToBack() { ThriftyList list = new ThriftyList(); int testCount = 100; for (int j = 0; j < testCount; j++) { list.add(j, j); } for (int j = 0; j < testCount; j++) { Assert.assertEquals(Integer.valueOf(j), list.get(j)); } } @Test public void testInsertToFront() { ThriftyList list = new ThriftyList(); int testCount = 100; for (int j = testCount; j >= 0; j--) { list.add(0, j); } for (int j = 0; j < testCount; j++) { Assert.assertEquals(Integer.valueOf(j), list.get(j)); } } @Test public void testInsertToMiddle() { ThriftyList list = new ThriftyList(); int testCount = 100; list.add(0); list.add(testCount - 2); list.add(testCount - 1); for (int j = 1; j < testCount - 2; j++) { list.add(j, j); } for (int j = 0; j < testCount; j++) { Assert.assertEquals(Integer.valueOf(j), list.get(j)); } } @Test public void testIterator() { ThriftyList list = new ThriftyList(); ListIterator i = list.listIterator(); try { i.next(); Assert.fail(); } catch (NoSuchElementException e) { } try { i.remove(); Assert.fail(); } catch (IllegalStateException e) { } int testCount = 100; i = list.listIterator(); for (int j = 0; j < testCount; j++) { i.add(j); } for (int j = testCount - 1; j >= 0; j--) { Assert.assertEquals(j, i.previousIndex()); Assert.assertEquals(Integer.valueOf(j), i.previous()); } for (int j = 0; j < testCount; j++) { Assert.assertTrue(i.hasNext()); Integer o = list.get(j); Assert.assertEquals(j, i.nextIndex()); Assert.assertEquals(o, i.next()); i.remove(); Assert.assertEquals(testCount - 1, list.size()); i.add(o); Assert.assertEquals(testCount, list.size()); Assert.assertEquals(o, i.previous()); i.next(); } int arbitraryIndex = testCount / 2; Integer arbitraryValue = -1; i = list.listIterator(arbitraryIndex); i.next(); i.set(arbitraryValue); for (int j = 0; j < testCount; j++) { if (j == arbitraryIndex) { Assert.assertEquals(arbitraryValue, list.get(j)); continue; } Assert.assertEquals(Integer.valueOf(j), list.get(j)); } i = list.listIterator(list.size()); for (int j = list.size() - 1; i.hasPrevious(); j--) { Integer o = list.get(j); Assert.assertEquals(j, i.previousIndex()); Assert.assertEquals(o, i.previous()); i.remove(); Assert.assertEquals(testCount - 1, list.size()); i.add(o); Assert.assertEquals(testCount, list.size()); Assert.assertEquals(o, i.previous()); } } @Test public void testPeek() { ThriftyList list = new ThriftyList(); Assert.assertNull(list.peek()); Assert.assertNull(list.peekFirst()); Assert.assertNull(list.peekLast()); int testCount = 10; for (int j = 0; j < testCount; j++) { list.add(j); } Assert.assertEquals(list.getFirst(), list.peek()); Assert.assertEquals(list.getFirst(), list.peekFirst()); Assert.assertEquals(list.getLast(), list.peekLast()); Assert.assertEquals(testCount, list.size()); } @Test public void testPoll() { ThriftyList list = new ThriftyList(); Assert.assertNull(list.poll()); Assert.assertNull(list.pollFirst()); Assert.assertNull(list.pollLast()); int testCount = 10; for (int j = 0; j < testCount; j++) { list.add(j); } Assert.assertEquals(list.getFirst(), list.poll()); Assert.assertEquals(list.getFirst(), list.pollFirst()); Assert.assertEquals(list.getLast(), list.pollLast()); Assert.assertEquals(testCount - 3, list.size()); } @Test public void testRemoveBack() { ThriftyList list = new ThriftyList(); int testCount = 100; for (int j = 0; j < testCount; j++) { list.add(j); } for (int j = testCount - 1; j >= 0; j--) { Assert.assertEquals(Integer.valueOf(j), list.remove(j)); } } @Test public void testRemoveFirstAndLastOccurrence() { ThriftyList list = new ThriftyList(); int testCount = 10; for (int j = 0; j < testCount; j++) { list.add(j); } list.add(0); list.removeFirstOccurrence(0); Assert.assertNotSame(Integer.valueOf(0), list.get(0)); Assert.assertEquals(Integer.valueOf(0), list.peekLast()); list.add(0, 0); list.removeLastOccurrence(0); Assert.assertNotSame(Integer.valueOf(0), list.peekLast()); Assert.assertEquals(Integer.valueOf(0), list.peekFirst()); } @Test public void testRemoveFront() { ThriftyList list = new ThriftyList(); int testCount = 100; for (int j = 0; j < testCount; j++) { list.add(j); } for (int j = 0; j < testCount; j++) { Assert.assertEquals(Integer.valueOf(j), list.remove(0)); } } @Test public void testSerialize() { ThriftyList list = new ThriftyList(); int testCount = 10; for (int j = 0; j < testCount; j++) { list.add(j); } try { ByteArrayOutputStream out = new ByteArrayOutputStream(); ObjectOutputStream os = new ObjectOutputStream(out); try { os.writeObject(list); } finally { os.close(); } ObjectInputStream is = new ObjectInputStream( new ByteArrayInputStream(out.toByteArray())); try { list = (ThriftyList) is.readObject(); } finally { is.close(); } } catch (Exception e) { StringWriter writer = new StringWriter(); e.printStackTrace(new PrintWriter(writer)); Assert.fail(writer.toString()); } for (int j = 0; j < testCount; j++) { Assert.assertEquals(Integer.valueOf(j), list.get(j)); } list = new ThriftyList(); try { ByteArrayOutputStream out = new ByteArrayOutputStream(); ObjectOutputStream os = new ObjectOutputStream(out); try { os.writeObject(list); } finally { os.close(); } ObjectInputStream is = new ObjectInputStream( new ByteArrayInputStream(out.toByteArray())); try { list = (ThriftyList) is.readObject(); } finally { is.close(); } } catch (Exception e) { StringWriter writer = new StringWriter(); e.printStackTrace(new PrintWriter(writer)); Assert.fail(writer.toString()); } Assert.assertTrue(list.isEmpty()); } @Test public void testSet() { ThriftyList list = new ThriftyList(); int testCount = 10; Integer dummyValue = -1; for (int j = 0; j < testCount; j++) { list.add(dummyValue); } for (int j = 0; j < testCount; j++) { list.set(j, Integer.valueOf(j)); } for (int j = 0; j < testCount; j++) { Assert.assertEquals(Integer.valueOf(j), list.get(j)); } } @Test public void testSizeAndClear() { ThriftyList list = new ThriftyList(); int testCount = 10; for (int j = 0; j < testCount; j++) { list.add(j); } Assert.assertEquals(testCount, list.size()); list.clear(); Assert.assertTrue(list.isEmpty()); } } euclid-euclid-2.9/src/test/java/blogspot/software_and_algorithms/stern_library/geometry/000077500000000000000000000000001461721410700320365ustar00rootroot00000000000000ClosestPointPairAlgorithmTest.java000066400000000000000000000066151461721410700406030ustar00rootroot00000000000000euclid-euclid-2.9/src/test/java/blogspot/software_and_algorithms/stern_library/geometrypackage blogspot.software_and_algorithms.stern_library.geometry; import java.awt.geom.Point2D; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.List; import org.junit.Assert; import org.junit.Test; /* 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. */ /** * Test class for ClosestPointPairAlgorithm. * * @author Kevin L. Stern */ public class ClosestPointPairAlgorithmTest { private static Point2D[] sort(Point2D[] points) { Arrays.sort(points, new Comparator() { @Override public int compare(Point2D o1, Point2D o2) { double d = o1.getX() - o2.getX(); if (d == 0) { d = o1.getY() - o2.getY(); } return d < 0 ? -1 : d > 0 ? 1 : 0; } }); return points; } @Test public void testBaseCase2Points() { Point2D p1 = new Point2D.Double(.49, .5); Point2D p2 = new Point2D.Double(.5, .2); List list = new ArrayList(); list.add(p1); list.add(p2); Assert.assertTrue(Arrays.equals(new Point2D[] { p1, p2 }, sort(new ClosestPointPairAlgorithm(list).execute()))); } @Test public void testBaseCase3Points() { Point2D p1 = new Point2D.Double(.49, .5); Point2D p2 = new Point2D.Double(.5, .2); Point2D p3 = new Point2D.Double(.51, .5); List list = new ArrayList(); list.add(p1); list.add(p2); list.add(p3); Assert.assertTrue(Arrays.equals(new Point2D[] { p1, p3 }, sort(new ClosestPointPairAlgorithm(list).execute()))); } @Test public void testBasic() { Point2D p1 = new Point2D.Double(.49, .5); Point2D p2 = new Point2D.Double(.51, .5); List list = new ArrayList(); list.add(p1); list.add(p2); list.add(new Point2D.Double(.6, .5)); list.add(new Point2D.Double(.7, .5)); list.add(new Point2D.Double(.8, .5)); Assert.assertTrue(Arrays.equals(new Point2D[] { p1, p2 }, sort(new ClosestPointPairAlgorithm(list).execute()))); } @Test public void testSplitMergeProcedure() { Point2D p1 = new Point2D.Double(.49, .5); Point2D p2 = new Point2D.Double(.51, .5); List list = new ArrayList(); list.add(p1); list.add(p2); list.add(new Point2D.Double(.5, .2)); list.add(new Point2D.Double(.4, .5)); list.add(new Point2D.Double(.6, .5)); Assert.assertTrue(Arrays.equals(new Point2D[] { p1, p2 }, sort(new ClosestPointPairAlgorithm(list).execute()))); } } euclid-euclid-2.9/src/test/java/blogspot/software_and_algorithms/stern_library/optimization/000077500000000000000000000000001461721410700327315ustar00rootroot00000000000000HungarianAlgorithmTest.java000066400000000000000000000103651461721410700401450ustar00rootroot00000000000000euclid-euclid-2.9/src/test/java/blogspot/software_and_algorithms/stern_library/optimizationpackage blogspot.software_and_algorithms.stern_library.optimization; import java.util.Arrays; import java.util.HashSet; import java.util.Set; import org.junit.Assert; import org.junit.Test; /* 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. */ /** * Test class for HungarianAlgorithm. * * @author Kevin L. Stern */ public class HungarianAlgorithmTest { private static double computeCost(double[][] matrix, int[] match) { double result = 0; Set visited = new HashSet(); for (int i = 0; i < matrix.length; i++) { if (match[i] == -1) { continue; } if (!visited.add(match[i])) { Assert.fail(); } result += matrix[i][match[i]]; } return result; } @Test public void test1() { double[][] matrix = new double[][] { new double[] { 4.0, 1.5, 4.0 }, new double[] { 4.0, 4.5, 6.0 }, new double[] { 3.0, 2.25, 3.0 } }; HungarianAlgorithm b = new HungarianAlgorithm(matrix); int[] match = b.execute(); Assert.assertTrue(Arrays.equals(new int[] { 1, 0, 2 }, match)); Assert.assertEquals(8.5, computeCost(matrix, match), 0.0000001); } @Test public void test2() { double[][] matrix = new double[][] { new double[] { 1.0, 1.0, 0.8 }, new double[] { 0.9, 0.8, 0.1 }, new double[] { 0.9, 0.7, 0.4 } }; HungarianAlgorithm b = new HungarianAlgorithm(matrix); int[] match = b.execute(); Assert.assertTrue(Arrays.equals(new int[] { 0, 2, 1 }, match)); Assert.assertEquals(1.8, computeCost(matrix, match), 0.0000001); } @Test public void test3() { double[][] matrix = new double[][] { new double[] { 6.0, 0.0, 7.0, 5.0 }, new double[] { 2.0, 6.0, 2.0, 6.0 }, new double[] { 2.0, 7.0, 2.0, 1.0 }, new double[] { 9.0, 4.0, 7.0, 1.0 } }; HungarianAlgorithm b = new HungarianAlgorithm(matrix); int[] match = b.execute(); Assert.assertTrue(Arrays.equals(new int[] { 1, 0, 2, 3 }, match)); Assert.assertEquals(5, computeCost(matrix, match), 0.0000001); } @Test public void testInvalidInput() { try { new HungarianAlgorithm(new double[][] { new double[] { 1, 2 }, new double[] { 3 } }); Assert.fail(); } catch (IllegalArgumentException e) { } try { new HungarianAlgorithm(null); Assert.fail(); } catch (NullPointerException e) { } } @Test public void testUnassignedJob() { double[][] matrix = new double[][] { new double[] { 6.0, 0.0, 7.0, 5.0, 2.0 }, new double[] { 2.0, 6.0, 2.0, 6.0, 7.0 }, new double[] { 2.0, 7.0, 2.0, 1.0, 1.0 }, new double[] { 9.0, 4.0, 7.0, 1.0, 0.0 } }; HungarianAlgorithm b = new HungarianAlgorithm(matrix); int[] match = b.execute(); Assert.assertTrue(Arrays.equals(new int[] { 1, 0, 3, 4 }, match)); Assert.assertEquals(3, computeCost(matrix, match), 0.0000001); } @Test public void testUnassignedWorker() { double[][] matrix = new double[][] { new double[] { 6.0, 0.0, 7.0, 5.0 }, new double[] { 2.0, 6.0, 2.0, 6.0 }, new double[] { 2.0, 7.0, 2.0, 1.0 }, new double[] { 9.0, 4.0, 7.0, 1.0 }, new double[] { 0.0, 0.0, 0.0, 0.0 } }; HungarianAlgorithm b = new HungarianAlgorithm(matrix); int[] match = b.execute(); Assert.assertTrue(Arrays.equals(new int[] { 1, -1, 2, 3, 0 }, match)); Assert.assertEquals(3, computeCost(matrix, match), 0.0000001); } } euclid-euclid-2.9/src/test/java/blogspot/software_and_algorithms/stern_library/string/000077500000000000000000000000001461721410700315115ustar00rootroot00000000000000DamerauLevenshteinAlgorithmTest.java000066400000000000000000000063531461721410700405760ustar00rootroot00000000000000euclid-euclid-2.9/src/test/java/blogspot/software_and_algorithms/stern_library/stringpackage blogspot.software_and_algorithms.stern_library.string; import org.junit.Assert; import org.junit.Test; /* 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. */ /** * Test class for DamerauLevenshteinAlgorithm. * * @author Kevin L. Stern */ public class DamerauLevenshteinAlgorithmTest { @Test public void test() { Assert.assertEquals(7, new DamerauLevenshteinAlgorithm(1, 1, 1, 1) .execute("NawKtYu", "")); Assert.assertEquals(7, new DamerauLevenshteinAlgorithm(1, 1, 1, 1) .execute("", "NawKtYu")); Assert.assertEquals(0, new DamerauLevenshteinAlgorithm(1, 1, 1, 1) .execute("NawKtYu", "NawKtYu")); Assert.assertEquals(6, new DamerauLevenshteinAlgorithm(1, 1, 1, 1) .execute("NawKtYu", "tKNwYua")); Assert.assertEquals(1, new DamerauLevenshteinAlgorithm(1, 1, 1, 1) .execute("Jdc", "dJc")); Assert.assertEquals(5, new DamerauLevenshteinAlgorithm(1, 1, 1, 1) .execute("sUzSOwx", "zsSxUwO")); Assert.assertEquals(7, new DamerauLevenshteinAlgorithm(1, 1, 1, 1) .execute("eOqoHAta", "tAeaqHoO")); Assert.assertEquals(1, new DamerauLevenshteinAlgorithm(1, 1, 1, 1) .execute("glSbo", "lgSbo")); Assert.assertEquals(4, new DamerauLevenshteinAlgorithm(1, 1, 1, 1) .execute("NJtQKcJE", "cJEtQKJN")); Assert.assertEquals(5, new DamerauLevenshteinAlgorithm(1, 1, 1, 1) .execute("GitIEVs", "EGItVis")); Assert.assertEquals(4, new DamerauLevenshteinAlgorithm(1, 1, 1, 1) .execute("MiWK", "WKiM")); } @Test public void testCosts() { /* * Test replace cost. */ Assert.assertEquals(1, new DamerauLevenshteinAlgorithm(100, 100, 1, 100).execute("a", "b")); /* * Test swap cost. */ Assert.assertEquals(200, new DamerauLevenshteinAlgorithm(100, 100, 100, 200).execute("ab", "ba")); /* * Test delete cost. */ Assert.assertEquals(1, new DamerauLevenshteinAlgorithm(1, 100, 100, 100).execute("aa", "a")); /* * Test insert cost. */ Assert.assertEquals(1, new DamerauLevenshteinAlgorithm(100, 1, 100, 100).execute("a", "aa")); } @Test public void testInvalidCosts() { try { new DamerauLevenshteinAlgorithm(1, 1, 1, 0); Assert.fail(); } catch (IllegalArgumentException e) { } } } KnuthMorrisPrattAlgorithmTest.java000066400000000000000000000051101461721410700403010ustar00rootroot00000000000000euclid-euclid-2.9/src/test/java/blogspot/software_and_algorithms/stern_library/stringpackage blogspot.software_and_algorithms.stern_library.string; import org.junit.Assert; import org.junit.Test; /* 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. */ /** * Test class for KnuthMorrisPrattAlgorithm. * * @author Kevin L. Stern */ public class KnuthMorrisPrattAlgorithmTest { @Test public void test1() { String needle = "needle"; String haystack = "It's like searching for a needle in a haystack."; Assert.assertEquals(haystack.indexOf(needle), new KnuthMorrisPrattAlgorithm(needle).execute(haystack)); } @Test public void test2() { String needle = "01012"; String haystack = "010101012"; Assert.assertEquals(haystack.indexOf(needle), new KnuthMorrisPrattAlgorithm(needle).execute(haystack)); } @Test public void test3() { String needle = "0101"; String haystack = "0102020101"; Assert.assertEquals(haystack.indexOf(needle), new KnuthMorrisPrattAlgorithm(needle).execute(haystack)); } @Test public void test4() { String needle = "aaaaaaa"; String haystack = "aaaaaab"; Assert.assertEquals(haystack.indexOf(needle), new KnuthMorrisPrattAlgorithm(needle).execute(haystack)); } @Test public void test5() { String needle = "aaaaaaa"; String haystack = "aaaaaaa"; Assert.assertEquals(haystack.indexOf(needle, 1), new KnuthMorrisPrattAlgorithm(needle).execute(haystack, 1)); } @Test public void test6() { String needle = "aa"; String haystack = "aaaaaaa"; Assert.assertEquals(haystack.indexOf(needle, 1), new KnuthMorrisPrattAlgorithm(needle).execute(haystack, 1)); } } euclid-euclid-2.9/src/test/java/org/000077500000000000000000000000001461721410700173555ustar00rootroot00000000000000euclid-euclid-2.9/src/test/java/org/xmlcml/000077500000000000000000000000001461721410700206515ustar00rootroot00000000000000euclid-euclid-2.9/src/test/java/org/xmlcml/Fixtures.java000066400000000000000000000023111461721410700233220ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml; import java.io.File; import org.apache.log4j.Level; import org.apache.log4j.Logger; public class Fixtures { private static final Logger LOG = Logger.getLogger(Fixtures.class); static { LOG.setLevel(Level.DEBUG); } public final static File TEST_RESOURCES = new File("src/test/resources"); public final static File TEST_DIR = new File(Fixtures.TEST_RESOURCES, "org/xmlcml"); public final static File FILES_DIR = new File(Fixtures.TEST_DIR, "files"); public final static File TEST_PLOSONE_0115884_DIR = new File(Fixtures.FILES_DIR, "journal.pone.0115884"); } euclid-euclid-2.9/src/test/java/org/xmlcml/args/000077500000000000000000000000001461721410700216055ustar00rootroot00000000000000euclid-euclid-2.9/src/test/java/org/xmlcml/args/DefaultArgProcessorTest.java-old000066400000000000000000000055241461721410700300100ustar00rootroot00000000000000package org.xmlcml.args; import junit.framework.Assert; import org.apache.log4j.Logger; import org.junit.Test; import org.xmlcml.args.DefaultArgProcessor; public class DefaultArgProcessorTest { private static final Logger LOG = Logger.getLogger(DefaultArgProcessorTest.class); static {LOG.setLevel(org.apache.log4j.Level.DEBUG);} @Test public void testArgs() { String[] args = { "-i", "foo", "bar", "-o", "plugh", }; DefaultArgProcessor argProcessor = new DefaultArgProcessor(); argProcessor.parseArgs(args); Assert.assertEquals("input", 2, argProcessor.getInputList().size()); Assert.assertEquals("input", "foo", argProcessor.getInputList().get(0)); Assert.assertEquals("input", "bar", argProcessor.getInputList().get(1)); Assert.assertEquals("output", "plugh", argProcessor.getOutput()); } @Test public void testSingleWildcards() { String[] args = { "-i", "foo{1:3}bof", "bar{a|b|zzz}plugh", }; DefaultArgProcessor argProcessor = new DefaultArgProcessor(); argProcessor.parseArgs(args); Assert.assertEquals("input", 2, argProcessor.getInputList().size()); Assert.assertEquals("input", "foo{1:3}bof", argProcessor.getInputList().get(0)); Assert.assertEquals("input", "bar{a|b|zzz}plugh", argProcessor.getInputList().get(1)); argProcessor.expandWildcardsExhaustively(); Assert.assertEquals("input", 6, argProcessor.getInputList().size()); Assert.assertEquals("input", "foo1bof", argProcessor.getInputList().get(0)); Assert.assertEquals("input", "foo2bof", argProcessor.getInputList().get(1)); Assert.assertEquals("input", "foo3bof", argProcessor.getInputList().get(2)); Assert.assertEquals("input", "baraplugh", argProcessor.getInputList().get(3)); Assert.assertEquals("input", "barbplugh", argProcessor.getInputList().get(4)); Assert.assertEquals("input", "barzzzplugh", argProcessor.getInputList().get(5)); } @Test public void testMultipleWildcards() { String[] args = { "-i", "foo{1:3}bof{3:6}plugh", }; DefaultArgProcessor argProcessor = new DefaultArgProcessor(); argProcessor.parseArgs(args); Assert.assertEquals("input", 1, argProcessor.getInputList().size()); Assert.assertEquals("input", "foo{1:3}bof{3:6}plugh", argProcessor.getInputList().get(0)); argProcessor.expandWildcardsExhaustively(); Assert.assertEquals("input", 12, argProcessor.getInputList().size()); Assert.assertEquals("input", "foo1bof3plugh", argProcessor.getInputList().get(0)); } @Test public void testArgCounts() { String[] args = {"-o", "foo"}; new DefaultArgProcessor().parseArgs(args); try { args = new String[]{"-o", "foo", "bar"}; new DefaultArgProcessor().parseArgs(args); } catch (Exception e) { Assert.assertEquals("too many arguments", "cannot process argument: -o (IllegalArgumentException: --output; argument count (2) is not compatible with {1,1})", e.getMessage()); } } } euclid-euclid-2.9/src/test/java/org/xmlcml/args/sandpit/000077500000000000000000000000001461721410700232475ustar00rootroot00000000000000euclid-euclid-2.9/src/test/java/org/xmlcml/args/sandpit/SandpitArgProcessorDoubleTest.java-old000066400000000000000000000063641461721410700326260ustar00rootroot00000000000000package org.xmlcml.args.sandpit; import junit.framework.Assert; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.junit.Test; import org.xmlcml.euclid.RealArray; public class SandpitArgProcessorDoubleTest { private static final Logger LOG = Logger .getLogger(SandpitArgProcessorDoubleTest.class); static { LOG.setLevel(Level.DEBUG); } @Test public void testDouble() { String args[] = {"--double", "4.5"}; SandpitArgProcessor sandpitArgProcessor = new SandpitArgProcessor(args); Assert.assertEquals("double", 4.5, sandpitArgProcessor.getDouble(), 0.0001); } @Test public void testDoubleOutOfRange() { String args[] = new String[]{"--double", "2.0"}; // will be out of range try { new SandpitArgProcessor(args); Assert.fail("Should throw RuntimeException; Illegal value"); } catch (RuntimeException e) { Assert.assertEquals("illegal value", "cannot process argument: --double (IllegalArgumentException: --double; value: 2.0 incompatible with: (3.1,7.2))", e.getMessage()); } args = new String[]{"--double", "12.0"}; // will be out of range try { new SandpitArgProcessor(args); Assert.fail("Should throw RuntimeException; Illegal value"); } catch (RuntimeException e) { Assert.assertEquals("illegal value", "cannot process argument: --double (IllegalArgumentException: --double; value: 12.0 incompatible with: (3.1,7.2))", e.getMessage()); } } @Test public void testBadDouble() { String args[] = new String[]{"--double", "foo"}; // will be out of range try { new SandpitArgProcessor(args); Assert.fail("Should throw RuntimeException; Illegal value"); } catch (RuntimeException e) { Assert.assertEquals("illegal value", "cannot process argument: --double (IllegalArgumentException: --double; value: foo incompatible with: (3.1,7.2))", e.getMessage()); } } @Test public void testTooManyDoubles() { String[] args = new String[]{"--double", "4.0", "7.0"}; // will be out of range try { new SandpitArgProcessor(args); Assert.fail("Should throw RuntimeException; Too many values"); } catch (RuntimeException e) { Assert.assertEquals("too many doubles", "cannot process argument: --double (IllegalArgumentException: --double; " + "argument count (2) is not compatible with {1,1})", e.getMessage()); } } @Test public void testDoubleArray() { String[] args = new String[]{"--doublearray", "4.0", "7.0", "5.0"}; try { SandpitArgProcessor sandpitArgProcessor = new SandpitArgProcessor(args); RealArray doubleArray = sandpitArgProcessor.getDoubleArray(); Assert.assertTrue("array values", new RealArray(new double[]{4.0, 7.0, 5.0}).equals(doubleArray, 0.001)); } catch (RuntimeException e) { Assert.fail("should not throw "+e); } } @Test public void testBadDoubleArray() { String[] args = new String[]{"--doublearray", "4.0", "7.0", "foo", "-4.0"}; try { SandpitArgProcessor sandpitArgProcessor = new SandpitArgProcessor(args); Assert.fail("should throw bad element exception"); } catch (RuntimeException e) { Assert.assertEquals("bad element", "cannot process argument: --doublearray (IllegalArgumentException: --doublearray; value: foo incompatible with: (3.1,7.2))", e.getMessage()); } } } euclid-euclid-2.9/src/test/java/org/xmlcml/args/sandpit/SandpitArgProcessorIntegerTest.java-old000066400000000000000000000071311461721410700330020ustar00rootroot00000000000000package org.xmlcml.args.sandpit; import junit.framework.Assert; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.junit.Test; import org.xmlcml.euclid.IntArray; public class SandpitArgProcessorIntegerTest { private static final Logger LOG = Logger .getLogger(SandpitArgProcessorIntegerTest.class); static { LOG.setLevel(Level.DEBUG); } @Test public void testInteger() { String args[] = {"--integer", "4"}; SandpitArgProcessor sandpitArgProcessor = new SandpitArgProcessor(args); Assert.assertEquals("int", 4, (int)sandpitArgProcessor.getInteger()); } @Test public void testIntegerOutOfRange() { String args[] = new String[]{"--integer", "2"}; // will be out of range try { new SandpitArgProcessor(args); Assert.fail("Should throw RuntimeException; Illegal value"); } catch (RuntimeException e) { Assert.assertEquals("illegal value", "cannot process argument: --integer (IllegalArgumentException: --integer; value: 2 incompatible with: (3,7))", e.getMessage()); } args = new String[]{"--integer", "12"}; // will be out of range try { new SandpitArgProcessor(args); Assert.fail("Should throw RuntimeException; Illegal value"); } catch (RuntimeException e) { Assert.assertEquals("illegal value", "cannot process argument: --integer (IllegalArgumentException: --integer; value: 12 incompatible with: (3,7))", e.getMessage()); } } @Test public void testBadInteger() { String args[] = new String[]{"--integer", "foo"}; // will be out of range try { new SandpitArgProcessor(args); Assert.fail("Should throw RuntimeException; Illegal value"); } catch (RuntimeException e) { Assert.assertEquals("illegal value", "cannot process argument: --integer (IllegalArgumentException: --integer; value: foo incompatible with: (3,7))", e.getMessage()); } } @Test public void testTooManyIntegers() { String[] args = new String[]{"--integer", "4", "7"}; // will be out of range try { new SandpitArgProcessor(args); Assert.fail("Should throw RuntimeException; Too many values"); } catch (RuntimeException e) { Assert.assertEquals("too many ints", "cannot process argument: --integer (IllegalArgumentException: --integer; " + "argument count (2) is not compatible with {1,1})", e.getMessage()); } } @Test public void testIntegerArray() { String[] args = new String[]{"--integerarray", "4", "7", "5"}; try { SandpitArgProcessor sandpitArgProcessor = new SandpitArgProcessor(args); IntArray intArray = sandpitArgProcessor.getIntArray(); Assert.assertTrue("array values", new IntArray(new int[]{4, 7, 5}).equals(intArray)); } catch (RuntimeException e) { Assert.fail("should not throw "+e); } } @Test public void testBadIntegerArray() { String[] args = new String[]{"--integerarray", "4", "7", "foo", "-4"}; try { new SandpitArgProcessor(args); Assert.fail("should throw bad element exception"); } catch (RuntimeException e) { Assert.assertEquals("bad element", "cannot process argument: --integerarray (IllegalArgumentException: --integerarray; value: foo incompatible with: (3,7))", e.getMessage()); } } @Test public void testIntegerArrayValueOUtOfRange() { String[] args = new String[]{"--integerarray", "4", "7", "-3", "6"}; try { new SandpitArgProcessor(args); Assert.fail("should throw bad element exception"); } catch (RuntimeException e) { Assert.assertEquals("bad element", "cannot process argument: --integerarray (IllegalArgumentException: --integerarray; value: -3 incompatible with: (3,7))", e.getMessage()); } } } euclid-euclid-2.9/src/test/java/org/xmlcml/euclid/000077500000000000000000000000001461721410700221165ustar00rootroot00000000000000euclid-euclid-2.9/src/test/java/org/xmlcml/euclid/test/000077500000000000000000000000001461721410700230755ustar00rootroot00000000000000euclid-euclid-2.9/src/test/java/org/xmlcml/euclid/test/AngleTest.java000066400000000000000000000230411461721410700256260ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid.test; import static org.xmlcml.euclid.EC.EPS; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.xmlcml.euclid.Angle; import org.xmlcml.euclid.Angle.Units; /** * test Angle. * * @author pmr * */ public class AngleTest { Angle zero; Angle pi4; Angle pi2; Angle pi; /** * setup. * * @throws Exception */ @Before public void setUp() throws Exception { zero = new Angle(0.); pi4 = new Angle(Math.PI / 4.); pi2 = new Angle(Math.PI / 2.); pi = new Angle(Math.PI); } /** * Test method for 'org.xmlcml.euclid.Angle.Angle(double)' */ @Test public void testAngleDouble() { Assert.assertEquals("pi/4", 45., pi4.getDegrees(), EPS); } /** * Test method for 'org.xmlcml.euclid.Angle.Angle(double, AngleType)' */ @Test public void testAngleDoubleAngleType() { Angle a1 = new Angle(1., Angle.Units.DEGREES); Assert.assertEquals("degrees ", Math.PI / 180., a1.getRadian(), EPS); a1 = new Angle(1., Angle.Units.RADIANS); Assert.assertEquals("degrees ", 1., a1.getRadian(), EPS); } /** * Test method for 'org.xmlcml.euclid.Angle.Angle(double, double)' */ @Test public void testAngleDoubleDouble() { Angle a1 = new Angle(1., 0.); Assert.assertEquals("degrees ", Math.PI / 2., a1.getRadian(), EPS); a1 = new Angle(1., 1.); Assert.assertEquals("degrees ", Math.PI / 4., a1.getRadian(), EPS); a1 = new Angle(0., 1.); Assert.assertEquals("degrees ", 0., a1.getRadian(), EPS); a1 = new Angle(0., -1.); Assert.assertEquals("degrees ", Math.PI, a1.getRadian(), EPS); a1 = new Angle(-1., -1.); Assert.assertEquals("degrees ", -3 * Math.PI / 4, a1.getRadian(), EPS); } /** * Test method for 'org.xmlcml.euclid.Angle.Angle(Angle)' */ @Test public void testAngleAngle() { Angle a1 = new Angle(pi2); Assert.assertEquals("degrees ", Math.PI / 2., a1.getRadian(), EPS); } /** * Test method for 'org.xmlcml.euclid.Angle.plus(Angle)' */ @Test public void testPlus() { Angle a1 = pi2.plus(pi4); Assert.assertEquals("degrees ", 3 * Math.PI / 4., a1.getRadian(), EPS); } /** * Test method for 'org.xmlcml.euclid.Angle.subtract(Angle)' */ @Test public void testSubtract() { Angle a1 = pi.subtract(pi4); Assert.assertEquals("degrees ", 3 * Math.PI / 4., a1.getRadian(), EPS); } /** * Test method for 'org.xmlcml.euclid.Angle.multiplyBy(double)' */ @Test public void testMultiplyBy() { Angle a1 = pi4.multiplyBy(3.); Assert.assertEquals("degrees ", 3 * Math.PI / 4., a1.getRadian(), EPS); } /** * Test method for 'org.xmlcml.euclid.Angle.cos()' */ @Test public void testCos() { double c = pi4.cos(); Assert.assertEquals("cos ", Math.sqrt(1. / 2.), c, EPS); } /** * Test method for 'org.xmlcml.euclid.Angle.sin()' */ @Test public void testSin() { double s = pi4.sin(); Assert.assertEquals("sin ", Math.sqrt(1. / 2.), s, 1.0E-08); } /** * Test method for 'org.xmlcml.euclid.Angle.tan()' */ @Test public void testTan() { double t = pi4.tan(); Assert.assertEquals("tan ", 1., t, 1.0E-08); } /** * Test method for 'org.xmlcml.euclid.Angle.normalise(double)' */ @Test public void testNormalise() { double a = Angle.normalise(3 * Math.PI); Assert.assertEquals("degrees ", Math.PI, a, EPS); } /** * Test method for 'org.xmlcml.euclid.Angle.isEqualTo(double)' */ @Test public void testIsEqualToDouble() { Assert.assertTrue("is equal", pi.isEqualTo(Math.PI)); Assert.assertFalse("is equal", pi.isEqualTo(Math.PI / 2)); } /** * Test method for 'org.xmlcml.euclid.Angle.greaterThan(double)' */ @Test public void testGreaterThanDouble() { Assert.assertTrue("GreaterThan", pi.greaterThan(Math.PI / 2)); Assert.assertFalse("GreaterThan", pi2.greaterThan(Math.PI)); Assert.assertFalse("GreaterThan", pi.greaterThan(Math.PI)); } /** * Test method for 'org.xmlcml.euclid.Angle.greaterThanOrEquals(double)' */ @Test public void testGreaterThanOrEqualsDouble() { Assert.assertTrue("GreaterThanOrEquals", pi .greaterThanOrEquals(Math.PI / 2)); Assert.assertFalse("GreaterThanOrEquals", pi2 .greaterThanOrEquals(Math.PI)); Assert.assertTrue("GreaterThanOrEquals", pi .greaterThanOrEquals(Math.PI)); } /** * Test method for 'org.xmlcml.euclid.Angle.lessThan(double)' */ @Test public void testLessThanDouble() { Assert.assertFalse("LessThan", pi.lessThan(Math.PI / 2)); Assert.assertTrue("LessThan", pi2.lessThan(Math.PI)); Assert.assertFalse("LessThan", pi.lessThan(Math.PI)); } /** * Test method for 'org.xmlcml.euclid.Angle.lessThanOrEquals(double)' */ @Test public void testLessThanOrEqualsDouble() { Assert .assertFalse("LessThanOrEquals", pi .lessThanOrEquals(Math.PI / 2)); Assert.assertTrue("LessThanOrEquals", pi2.lessThanOrEquals(Math.PI)); Assert.assertTrue("LessThanOrEquals", pi.lessThanOrEquals(Math.PI)); } /** * Test method for 'org.xmlcml.euclid.Angle.isEqualTo(Angle)' */ @Test public void testIsEqualToAngle() { Assert.assertTrue("is equal", pi.isEqualTo(pi)); Assert.assertFalse("is equal", pi.isEqualTo(pi2)); } /** * Test method for 'org.xmlcml.euclid.Angle.greaterThan(Angle)' */ @Test public void testGreaterThanAngle() { Assert.assertTrue("GreaterThan", pi.greaterThan(pi2)); Assert.assertFalse("GreaterThan", pi2.greaterThan(pi)); Assert.assertFalse("GreaterThan", pi.greaterThan(pi)); } /** * Test method for 'org.xmlcml.euclid.Angle.greaterThanOrEquals(Angle)' */ @Test public void testGreaterThanOrEqualsAngle() { Assert.assertTrue("GreaterThanOrEquals", pi.greaterThanOrEquals(pi2)); Assert.assertFalse("GreaterThanOrEquals", pi2.greaterThanOrEquals(pi)); Assert.assertTrue("GreaterThanOrEquals", pi.greaterThanOrEquals(pi)); } /** * Test method for 'org.xmlcml.euclid.Angle.lessThan(Angle)' */ @Test public void testLessThanAngle() { Assert.assertFalse("LessThan", pi.lessThan(pi2)); Assert.assertTrue("LessThan", pi2.lessThan(pi)); Assert.assertFalse("LessThan", pi.lessThan(pi)); } /** * Test method for 'org.xmlcml.euclid.Angle.lessThanOrEquals(Angle)' */ @Test public void testLessThanOrEqualsAngle() { Assert.assertFalse("LessThanOrEquals", pi.lessThanOrEquals(pi2)); Assert.assertTrue("LessThanOrEquals", pi2.lessThanOrEquals(pi)); Assert.assertTrue("LessThanOrEquals", pi.lessThanOrEquals(pi)); } /** * Test method for 'org.xmlcml.euclid.Angle.getAngle()' */ @Test public void testGetAngle() { double a = pi2.getAngle(); Assert.assertEquals("get angle", Math.PI / 2, a, EPS); } /** * Test method for 'org.xmlcml.euclid.Angle.getRadian()' */ @Test public void testGetRadian() { double a = pi2.getRadian(); Assert.assertEquals("get radian", Math.PI / 2, a, EPS); } /** * Test method for 'org.xmlcml.euclid.Angle.getDegrees()' */ @Test public void testGetDegrees() { double a = pi2.getDegrees(); Assert.assertEquals("get degrees", 90., a, EPS); } /** * Test method for 'org.xmlcml.euclid.Angle.putDegrees(double)' */ @Test public void testPutDegrees() { pi2.putDegrees(60.); Assert.assertEquals("put degrees", 60., pi2.getDegrees(), 1.0E-08); Assert.assertEquals("put degrees", Math.PI / 3., pi2.getRadian(), EPS); } /** * Test method for 'org.xmlcml.euclid.Angle.setRange(AngleRange)' */ @Test public void testSetRange() { pi2.putDegrees(-60.); pi2.setRange(Angle.Range.UNSIGNED); Assert.assertEquals("put degrees", 300., pi2.getDegrees(), 1.0E-08); Assert.assertEquals("put degrees", 5. * Math.PI / 3., pi2.getRadian(), EPS); } /** * Test method for 'org.xmlcml.euclid.Angle.Angle(double, Units)' */ @Test public void testAngleDoubleUnits() { Angle a = new Angle(90, Units.DEGREES); Assert.assertEquals("degrees", 90, a.getDegrees(), EPS); Assert.assertEquals("radians", 90 * Math.PI / 180., a.getRadian(), EPS); a = new Angle(1, Units.RADIANS); Assert.assertEquals("degrees", 180. / Math.PI, a.getDegrees(), EPS); Assert.assertEquals("radians", 1., a.getRadian(), EPS); a = new Angle(10, Units.RADIANS); Assert.assertEquals("degrees", 10. * 180. / Math.PI, a.getDegrees(), EPS); Assert.assertEquals("radians", 10., a.getRadian(), EPS); } @Test public void testRightAngle() { Angle EPS = new Angle(0.01); Angle ang = new Angle(Math.PI / 2.); Assert.assertEquals("rt1", 1, (int) new Angle(Math.PI / 2.).getRightAngle(EPS)); Assert.assertEquals("rt1", -1, (int) new Angle(-Math.PI / 2.).getRightAngle(EPS)); Assert.assertEquals("rt0", 0, (int) new Angle(0.).getRightAngle(EPS)); Assert.assertEquals("rtPI", 0, (int) new Angle(Math.PI).getRightAngle(EPS)); Assert.assertEquals("rt1", 1, (int) new Angle(Math.PI / 2.).getRightAngle(EPS)); Assert.assertEquals("rt1", 1, (int) new Angle(Math.PI * 1.001 / 2.).getRightAngle(EPS)); Assert.assertEquals("rt1", 0, (int) new Angle(Math.PI * 1.1 / 2.).getRightAngle(EPS)); Assert.assertEquals("rt1", -1, (int) new Angle(-Math.PI * 1.001 / 2.).getRightAngle(EPS)); Assert.assertEquals("rt1", 0, (int) new Angle(-Math.PI * 1.1 / 2.).getRightAngle(EPS)); } } euclid-euclid-2.9/src/test/java/org/xmlcml/euclid/test/BivariateTest.java000077500000000000000000000030161461721410700265110ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid.test; import junit.framework.Assert; import org.junit.Test; import org.xmlcml.euclid.Bivariate; import org.xmlcml.euclid.Real2Array; import org.xmlcml.euclid.RealArray; public class BivariateTest { @Test public void testBivariate() { Real2Array r2a = new Real2Array( new RealArray(new double[]{1.47, 1.50, 1.52, 1.55, 1.57, 1.60, 1.63, 1.65, 1.68, 1.70, 1.73, 1.75, 1.78, 1.80, 1.83}), new RealArray(new double[]{52.21, 53.12, 54.48, 55.84, 57.20, 58.57, 59.93, 61.29, 63.11, 64.47, 66.28, 68.10, 69.92, 72.19, 74.46}) ); Bivariate bivariate = new Bivariate(r2a); Double slope = bivariate.getSlope(); Assert.assertEquals("slope", 61.272, slope, 0.001); Double intercept = bivariate.getIntercept(); Assert.assertEquals("intercept", -39.062, intercept, 0.001); Double r = bivariate.getCorrelationCoefficient(); Assert.assertEquals("r", 0.9945, r, 0.001); } } euclid-euclid-2.9/src/test/java/org/xmlcml/euclid/test/ComplexTest.java000066400000000000000000000124741461721410700262170ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid.test; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.xmlcml.euclid.Angle; import org.xmlcml.euclid.Complex; import org.xmlcml.euclid.Polar; import org.xmlcml.euclid.Real2; /** * test Complex. * * @author pmr * */ public class ComplexTest { private static final double EPS = 1E-14; Complex c0; Complex c1; Complex c2; /** * setup. * * @throws Exception */ @Before public void setUp() throws Exception { c0 = new Complex(); c1 = new Complex(1, 0); c2 = new Complex(1, 2); } /** * Test method for 'org.xmlcml.euclid.Complex.negative()' */ @Test public void testNegative() { c2.negative(); Assert.assertEquals("negative", -1., c2.getReal(),EPS); Assert.assertEquals("negative", -2., c2.getImaginary(),EPS); } /** * Test method for 'org.xmlcml.euclid.Complex.toString()' */ @Test public void testToString() { String s = c2.toString(); Assert.assertEquals("to string", "1.0,2.0", s); } /** * Test method for 'org.xmlcml.euclid.Complex.Complex()' */ @Test public void testComplex() { String s = c0.toString(); Assert.assertEquals("to string", "0.0,0.0", s); Assert.assertEquals("empty ", 0., c0.getReal(),EPS); Assert.assertEquals("empty", 0., c0.getImaginary(),EPS); } /** * Test method for 'org.xmlcml.euclid.Complex.Complex(double)' */ @Test public void testComplexDouble() { Complex c = new Complex(3.); Assert.assertEquals("to string", "3.0,0.0", c.toString()); } /** * Test method for 'org.xmlcml.euclid.Complex.Complex(double, double)' */ @Test public void testComplexDoubleDouble() { Complex c = new Complex(3., 2.); Assert.assertEquals("to string", "3.0,2.0", c.toString()); } /** * Test method for 'org.xmlcml.euclid.Complex.Complex(Real2)' */ @Test public void testComplexReal2() { Complex c = new Complex(new Real2(3., 2.)); Assert.assertEquals("real 2", "3.0,2.0", c.toString()); } /** * Test method for 'org.xmlcml.euclid.Complex.Complex(double, Angle)' */ @Test public void testComplexDoubleAngle() { Angle a = new Angle(60., Angle.Units.DEGREES); Complex c = new Complex(1., a); Assert.assertEquals("length angle", 1. / 2., c.getReal(), 1.0E-08); Assert.assertEquals("length angle", Math.sqrt(3.) / 2., c .getImaginary(), 1.0E-08); } /** * Test method for 'org.xmlcml.euclid.Complex.Complex(Polar)' */ @Test public void testComplexPolar() { Polar p = new Polar(1., 2.); Complex c = new Complex(p); Assert.assertEquals("polar", 1., c.getReal(), 1.0E-08); Assert.assertEquals("polar", 2., c.getImaginary(), 1.0E-08); } /** * Test method for 'org.xmlcml.euclid.Complex.Complex(Complex)' */ @Test public void testComplexComplex() { Complex c = new Complex(c2); Assert.assertEquals("complex", 1., c.getReal(), 1.0E-08); Assert.assertEquals("complex", 2., c.getImaginary(), 1.0E-08); } /** * Test method for 'org.xmlcml.euclid.Complex.getReal()' */ @Test public void testGetReal() { Assert.assertEquals("real", 1., c2.getReal(), 1.0E-08); } /** * Test method for 'org.xmlcml.euclid.Complex.getImaginary()' */ @Test public void testGetImaginary() { Assert.assertEquals("imaginary", 2., c2.getImaginary(), 1.0E-08); } /** * Test method for 'org.xmlcml.euclid.Complex.multiply(Complex)' */ @Test public void testMultiply() { Complex c = c2.multiply(c2); Assert.assertEquals("multiply", -3., c.getReal(), 1.0E-08); Assert.assertEquals("multiply", 4., c.getImaginary(), 1.0E-08); } /** * Test method for 'org.xmlcml.euclid.Complex.divideBy(Complex)' */ @Test public void testDivideBy() { Complex c = c1.divideBy(c2); Assert.assertEquals("divide", 0.2, c.getReal(), 1.0E-08); Assert.assertEquals("divide", -0.4, c.getImaginary(), 1.0E-08); } /** * Test method for 'org.xmlcml.euclid.Complex.getR()' */ @Test public void testGetR() { double r = c2.getR(); Assert.assertEquals("R", Math.sqrt(5.), r, 1.0E-08); } /** * Test method for 'org.xmlcml.euclid.Complex.getTheta()' */ @Test public void testGetTheta() { Angle a = c2.getTheta(); Assert.assertEquals("theta", Math.atan2(2., 1.), a.getAngle(), 1.0E-08); } /** * Test method for 'org.xmlcml.euclid.Complex.getPolar()' */ @Test public void testGetPolar() { Polar p = c2.getPolar(); Angle a = p.getTheta(); Assert.assertEquals("R", Math.sqrt(5.), p.getR(), 1.0E-08); Assert.assertEquals("theta", Math.atan2(2., 1.), a.getAngle(), 1.0E-08); } /** * Test method for 'org.xmlcml.euclid.Complex.sqrt(Complex)' */ @Test public void testSqrt() { Complex c = Complex.sqrt(c2); Assert.assertEquals("sqrt x", 1.2720196, c.getReal(), 0.000001); Assert.assertEquals("sqrt y", 0.786151, c.getImaginary(), 0.000001); } } euclid-euclid-2.9/src/test/java/org/xmlcml/euclid/test/EuclidTestUtils.java000066400000000000000000000015711461721410700270320ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid.test; import org.apache.log4j.Logger; public class EuclidTestUtils { /** logger */ private final static Logger LOG = Logger.getLogger(EuclidTestUtils.class); public final static String BASE_RESOURCE = "org/xmlcml/euclid"; } euclid-euclid-2.9/src/test/java/org/xmlcml/euclid/test/GeomTest.java000066400000000000000000000052661461721410700255000ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid.test; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.xmlcml.euclid.Line3; import org.xmlcml.euclid.Plane3; import org.xmlcml.euclid.Point3; import org.xmlcml.euclid.Transform3; import org.xmlcml.euclid.Vector3; /** * parent class for geom tests. * * @author pmr * */ public class GeomTest { Line3 l0; Line3 l100000; Line3 l123456; Plane3 pl0; Plane3 pl1000; Plane3 pl0100; Plane3 pl0010; Plane3 pl1234; Plane3 pl1111; Point3 p0; Point3 p000; Point3 p100; Point3 p010; Point3 p001; Point3 p111; Point3 p123; Point3 p321; Transform3 tr0; Transform3 tr1; Transform3 tr2; Vector3 v0; Vector3 v000; Vector3 v100; Vector3 v010; Vector3 v001; Vector3 v123; Vector3 v321; final static double s14 = Math.sqrt(14.); final static double s3 = Math.sqrt(3.); final static double s2 = Math.sqrt(2.); /** * setup. * * @throws Exception */ @Before public void setUp() throws Exception { l0 = new Line3(); l100000 = new Line3(new Point3(0., 0., 0.), new Vector3(1., 0., 0.)); l123456 = new Line3(new Point3(4., 5., 6.), new Vector3(1., 2., 3.)); pl0 = new Plane3(); pl1000 = new Plane3(1., 0., 0., 0); pl0100 = new Plane3(0., 1., 0., 0.); pl0010 = new Plane3(new double[] { 0., 0., 1., 0. }); pl1234 = new Plane3(new double[] { 1., 2., 3. }, 4.); pl1111 = new Plane3(new Vector3(1., 1., 1.), 1.); p0 = new Point3(); p000 = new Point3(0., 0., 0.); p100 = new Point3(1., 0., 0.); p010 = new Point3(0., 1., 0.); p001 = new Point3(0., 0., 1.); p111 = new Point3(1., 1., 1.); p123 = new Point3(1., 2., 3.); p321 = new Point3(3., 2., 1.); tr0 = new Transform3(); tr1 = new Transform3("x, -y, z"); tr2 = new Transform3("-x, -y, z"); v0 = new Vector3(); v000 = new Vector3(0., 0., 0.); v100 = new Vector3(1., 0., 0.); v010 = new Vector3(0., 1., 0.); v001 = new Vector3(0., 0., 1.); v123 = new Vector3(1., 2., 3.); v321 = new Vector3(3., 2., 1.); } /** test */ @Test public void testDummy() { Assert.assertNotNull(p0); } } euclid-euclid-2.9/src/test/java/org/xmlcml/euclid/test/Int2RangeTest.java000066400000000000000000000135101461721410700263710ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid.test; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.xmlcml.euclid.Int2; import org.xmlcml.euclid.Int2Range; import org.xmlcml.euclid.IntRange; /** * test Int2Range * * @author pmr * */ public class Int2RangeTest { Int2Range i2r0; Int2Range i2r1; Int2Range i2r2; /** * main * * @param args */ public static void main(String[] args) { } /** * setup. * * @throws Exception */ @Before public void setUp() throws Exception { i2r0 = new Int2Range(); i2r1 = new Int2Range(new IntRange(1, 2), new IntRange(1, 2)); i2r2 = new Int2Range(new IntRange(1, 2), new IntRange(3, 4)); } /** * Test method for 'org.xmlcml.euclid.Int2Range.Int2Range()' */ @Test public void testInt2Range() { Assert.assertEquals("empty", "(NULL,NULL)", i2r0.toString()); } /** * Test method for 'org.xmlcml.euclid.Int2Range.Int2Range(IntRange, * IntRange)' */ @Test public void testInt2RangeIntRangeIntRange() { Assert.assertEquals("empty", "((1,2),(3,4))", i2r2.toString()); } /** * Test method for 'org.xmlcml.euclid.Int2Range.Int2Range(Int2Range)' */ @Test public void testInt2RangeInt2Range() { Int2Range ii = new Int2Range(i2r2); Assert.assertEquals("empty", "((1,2),(3,4))", ii.toString()); } /** * Test method for 'org.xmlcml.euclid.Int2Range.isValid()' */ @Test public void testIsValid() { Assert.assertTrue("valid", i2r2.isValid()); Assert.assertFalse("invalid", i2r0.isValid()); } /** * Test method for 'org.xmlcml.euclid.Int2Range.isEqualTo(Int2Range)' */ @Test public void testIsEqualTo() { Assert.assertTrue("isEqual", i2r2.isEqualTo(i2r2)); Assert.assertFalse("isEqual", i2r2.isEqualTo(i2r1)); Assert.assertFalse("isEqual", i2r0.isEqualTo(i2r0)); } /** * Test method for 'org.xmlcml.euclid.Int2Range.plus(Int2Range)' */ @Test public void testPlus() { Int2Range ix = new Int2Range(new IntRange(1, 4), new IntRange(11, 14)); Int2Range iy = new Int2Range(new IntRange(2, 5), new IntRange(12, 15)); Int2Range ii = ix.plus(iy); Assert.assertEquals("plus", "((1,5),(11,15))", ii.toString()); iy = new Int2Range(new IntRange(2, 3), new IntRange(12, 13)); ii = ix.plus(iy); Assert.assertEquals("plus", "((1,4),(11,14))", ii.toString()); iy = new Int2Range(new IntRange(0, 8), new IntRange(10, 18)); ii = ix.plus(iy); Assert.assertEquals("plus", "((0,8),(10,18))", ii.toString()); } /** * Test method for 'org.xmlcml.euclid.Int2Range.intersectionWith(Int2Range)' */ @Test public void testIntersectionWith() { Int2Range ix = new Int2Range(new IntRange(1, 4), new IntRange(11, 14)); Int2Range iy = new Int2Range(new IntRange(2, 5), new IntRange(12, 15)); Int2Range ii = ix.intersectionWith(iy); Assert.assertEquals("plus", "((2,4),(12,14))", ii.toString()); iy = new Int2Range(new IntRange(2, 3), new IntRange(12, 13)); ii = ix.intersectionWith(iy); Assert.assertEquals("plus", "((2,3),(12,13))", ii.toString()); iy = new Int2Range(new IntRange(0, 8), new IntRange(10, 18)); ii = ix.intersectionWith(iy); Assert.assertEquals("plus", "((1,4),(11,14))", ii.toString()); } /** * Test method for 'org.xmlcml.euclid.Int2Range.getXRange()' */ @Test public void testGetXRange() { Assert.assertEquals("getXRange", "NULL", i2r0.getXRange().toString()); Assert.assertEquals("getXRange", "(1,2)", i2r2.getXRange().toString()); } /** * Test method for 'org.xmlcml.euclid.Int2Range.getYRange()' */ @Test public void testGetYRange() { Assert.assertEquals("getXRange", "NULL", i2r0.getYRange().toString()); Assert.assertEquals("getXRange", "(3,4)", i2r2.getYRange().toString()); } /** * Test method for 'org.xmlcml.euclid.Int2Range.includes(Int2)' */ @Test public void testIncludesInt2() { Int2Range ix = new Int2Range(new IntRange(1, 4), new IntRange(11, 14)); Assert.assertTrue("include", ix.includes(new Int2(2, 12))); Assert.assertTrue("include", ix.includes(new Int2(1, 11))); Assert.assertTrue("include", ix.includes(new Int2(4, 14))); Assert.assertFalse("include", ix.includes(new Int2(1, 15))); } /** * Test method for 'org.xmlcml.euclid.Int2Range.includes(Int2Range)' */ @Test public void testIncludesInt2Range() { Int2Range ix = new Int2Range(new IntRange(1, 4), new IntRange(11, 14)); Assert.assertTrue("include", ix.includes(new Int2Range(new IntRange(2, 3), new IntRange(12, 13)))); Assert.assertTrue("include", ix.includes(new Int2Range(new IntRange(1, 4), new IntRange(11, 14)))); Assert.assertFalse("include", ix.includes(new Int2Range(new IntRange(0, 4), new IntRange(10, 14)))); Assert.assertFalse("include", ix.includes(new Int2Range(new IntRange(2, 5), new IntRange(12, 15)))); } /** * Test method for 'org.xmlcml.euclid.Int2Range.add(Int2)' */ @Test public void testAdd() { Int2Range ii = new Int2Range(new IntRange(1, 4), new IntRange(11, 14)); Assert.assertEquals("plus", "((1,4),(11,14))", ii.toString()); Int2 i2 = new Int2(2, 12); ii.add(i2); Assert.assertEquals("plus", "((1,4),(11,14))", ii.toString()); i2 = new Int2(0, 15); ii.add(i2); Assert.assertEquals("plus", "((0,4),(11,15))", ii.toString()); i2 = new Int2(8, 7); ii.add(i2); Assert.assertEquals("plus", "((0,8),(7,15))", ii.toString()); } } euclid-euclid-2.9/src/test/java/org/xmlcml/euclid/test/Int2Test.java000066400000000000000000000111711461721410700254150ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid.test; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.xmlcml.euclid.Int2; /** * test Int2 * * @author pmr * */ public class Int2Test { Int2 i0; Int2 i11; Int2 i12; /** * setup. * * @throws Exception */ @Before public void setUp() throws Exception { i0 = new Int2(); i11 = new Int2(1, 1); i12 = new Int2(1, 2); } /** * Test method for 'org.xmlcml.euclid.Int2.Int2()' */ @Test public void testInt2() { Assert.assertEquals("int2", "(0,0)", i0.toString()); } /** * Test method for 'org.xmlcml.euclid.Int2.Int2(int, int)' */ @Test public void testInt2IntInt() { Assert.assertEquals("int2", "(1,2)", i12.toString()); } /** * Test method for 'org.xmlcml.euclid.Int2.Int2(Int2)' */ @Test public void testInt2Int2() { Int2 ii = new Int2(i12); Assert.assertEquals("int2", "(1,2)", ii.toString()); } /** * Test method for 'org.xmlcml.euclid.Int2.swap()' */ @Test public void testSwap() { i12.swap(); Assert.assertEquals("int2", "(2,1)", i12.toString()); } /** * Test method for 'org.xmlcml.euclid.Int2.sortAscending()' */ @Test public void testSortAscending() { i12.sortAscending(); Assert.assertEquals("int2", "(1,2)", i12.toString()); } /** * Test method for 'org.xmlcml.euclid.Int2.sortDescending()' */ @Test public void testSortDescending() { i12.sortDescending(); Assert.assertEquals("int2", "(2,1)", i12.toString()); } /** * Test method for 'org.xmlcml.euclid.Int2.clear()' */ @Test public void testClear() { i12.clear(); Assert.assertEquals("int2", "(0,0)", i12.toString()); } /** * Test method for 'org.xmlcml.euclid.Int2.setX(int)' */ @Test public void testSetX() { i12.setX(3); Assert.assertEquals("int2", "(3,2)", i12.toString()); } /** * Test method for 'org.xmlcml.euclid.Int2.setY(int)' */ @Test public void testSetY() { i12.setY(3); Assert.assertEquals("int2", "(1,3)", i12.toString()); } /** * Test method for 'org.xmlcml.euclid.Int2.isEqualTo(Int2)' */ @Test public void testIsEqualTo() { Assert.assertTrue("equals", i12.isEqualTo(i12)); Assert.assertFalse("equals", i11.isEqualTo(i12)); } /** * Test method for 'org.xmlcml.euclid.Int2.plus(Int2)' */ @Test public void testPlus() { Int2 ii = i12.plus(i11); Assert.assertEquals("plus", "(2,3)", ii.toString()); } /** * Test method for 'org.xmlcml.euclid.Int2.subtract(Int2)' */ @Test public void testSubtract() { Int2 ii = i12.subtract(i11); Assert.assertEquals("subtract", "(0,1)", ii.toString()); } /** * Test method for 'org.xmlcml.euclid.Int2.negative()' */ @Test public void testNegative() { i12.negative(); Assert.assertEquals("negative", "(-1,-2)", i12.toString()); } /** * Test method for 'org.xmlcml.euclid.Int2.multiplyBy(int)' */ @Test public void testMultiplyBy() { Int2 ii = i12.multiplyBy(3); Assert.assertEquals("multiply", "(3,6)", ii.toString()); } /** * Test method for 'org.xmlcml.euclid.Int2.getX()' */ @Test public void testGetX() { Assert.assertEquals("getX", 1, i12.getX()); } /** * Test method for 'org.xmlcml.euclid.Int2.getY()' */ @Test public void testGetY() { Assert.assertEquals("getY", 2, i12.getY()); } /** * Test method for 'org.xmlcml.euclid.Int2.elementAt(int)' */ @Test public void testElementAt() { Assert.assertEquals("elementAt", 1, i12.elementAt(0)); Assert.assertEquals("elementAt", 2, i12.elementAt(1)); } /** * Test method for 'org.xmlcml.euclid.Int2.getMidPoint(Int2)' */ @Test public void testGetMidPoint() { Int2 m = i12.getMidPoint(new Int2(3, 4)); Assert.assertEquals("mid point", "(2,3)", m.toString()); } /** * Test method for 'org.xmlcml.euclid.Int2.dotProduct(Int2)' */ @Test public void testDotProduct() { int i = i12.dotProduct(new Int2(3, 4)); Assert.assertEquals("dor", 11, i); } /** * Test method for 'org.xmlcml.euclid.Int2.toString()' */ @Test public void testToString() { Assert.assertEquals("toString", "(1,2)", i12.toString()); } } euclid-euclid-2.9/src/test/java/org/xmlcml/euclid/test/IntArrayTest.java000066400000000000000000000506641461721410700263440ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid.test; import static org.xmlcml.euclid.EuclidConstants.S_EMPTY; import static org.xmlcml.euclid.EuclidConstants.S_RBRAK; import java.util.Iterator; import org.hamcrest.CoreMatchers; import org.hamcrest.MatcherAssert; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.xmlcml.euclid.ArrayBase.Trim; import org.xmlcml.euclid.EuclidRuntimeException; import org.xmlcml.euclid.Int; import org.xmlcml.euclid.IntArray; import org.xmlcml.euclid.IntRange; import org.xmlcml.euclid.IntSet; /** * test IntArray * * @author pmr * */ public class IntArrayTest { IntArray a0; IntArray a1; /** * setup. * * @throws Exception */ @Before public void setUp() throws Exception { a0 = new IntArray(); a1 = new IntArray(new int[] { 1, 2, 4, 6 }); } /** * equality test. true if both args not null and equal * * @param msg * message * @param test * @param expected */ public static void assertEquals(String msg, IntArray test, IntArray expected) { Assert.assertNotNull("test should not be null (" + msg + S_RBRAK, test); Assert.assertNotNull("expected should not be null (" + msg + S_RBRAK, expected); String s = Int.testEquals(test.getArray(), expected.getArray()); if (s != null) { Assert.fail(msg + "; " + s); } } /** * equality test. true if both args not null and equal * * @param msg * message * @param test * @param expected */ public static void assertEquals(String msg, int[] test, IntArray expected) { Assert.assertNotNull("test should not be null (" + msg + S_RBRAK, test); Assert.assertNotNull("expected should not be null (" + msg + S_RBRAK, expected); Assert.assertEquals("must be of equal length ", test.length, expected .getArray().length); String s = Int.testEquals(test, expected.getArray()); if (s != null) { Assert.fail(msg + "; " + s); } } /** * Test method for 'org.xmlcml.euclid.IntArray.IntArray()' */ @Test public void testIntArray() { Assert.assertEquals("empty", 0, a0.size()); Assert.assertEquals("empty", "()", a0.toString()); } /** * Test method for 'org.xmlcml.euclid.IntArray.IntArray(int)' */ @Test public void testIntArrayInt() { IntArray r = new IntArray(4); Assert.assertEquals("r", 4, r.size()); IntArrayTest.assertEquals("r", new int[] { 0, 0, 0, 0 }, r); } /** * Test method for 'org.xmlcml.euclid.IntArray.IntArray(int, int, int)' */ @Test public void testIntArrayIntDoubleDouble() { IntArray r = new IntArray(4, 1, 2); Assert.assertEquals("r", 4, r.size()); IntArrayTest.assertEquals("r", new int[] { 1, 3, 5, 7 }, r); } /** * Test method for 'org.xmlcml.euclid.IntArray.IntArray(int, int)' */ @Test public void testIntArrayIntDouble() { IntArray r = new IntArray(4, 2); Assert.assertEquals("r", 4, r.size()); IntArrayTest.assertEquals("r", new int[] { 2, 2, 2, 2 }, r); } /** * Test method for 'org.xmlcml.euclid.IntArray.IntArray(int, int[])' */ @Test public void testIntArrayIntDoubleArray() { int[] d = { 1, 2, 3, 4 }; IntArray r = new IntArray(3, d); Assert.assertEquals("r", 3, r.size()); IntArrayTest.assertEquals("r", new int[] { 1, 2, 3 }, r); try { r = new IntArray(5, d); Assert.fail("should always throw " + "Array size too small"); } catch (EuclidRuntimeException e) { Assert .assertEquals("int[]", "Array would overflow", e .getMessage()); } } /** * Test method for 'org.xmlcml.euclid.IntArray.IntArray(int[])' */ @Test public void testIntArrayDoubleArray() { int[] d = { 1, 2, 3, 4 }; IntArray r = new IntArray(d); Assert.assertEquals("r", 4, r.size()); IntArrayTest.assertEquals("r", new int[] { 1, 2, 3, 4 }, r); } /** * Test method for 'org.xmlcml.euclid.IntArray.IntArray(IntArray, int, int)' */ @Test public void testIntArrayIntArrayIntInt() { IntArray r = new IntArray(a1, 1, 2); Assert.assertEquals("r", 2, r.size()); IntArrayTest.assertEquals("r", new int[] { 2, 4 }, r); try { r = new IntArray(a1, 0, 5); } catch (EuclidRuntimeException e) { Assert.assertEquals("int array", "index out of range: 0/5", e .getMessage()); } } /** * Test method for 'org.xmlcml.euclid.IntArray.IntArray(IntArray, IntArray)' */ @Test public void testIntArrayIntArrayIntArray() { IntArray r = new IntArray(a1, new IntArray(new int[] { 3, 1, 2 })); Assert.assertEquals("r", 3, r.size()); IntArrayTest.assertEquals("r", new int[] { 6, 2, 4 }, r); } /** * Test method for 'org.xmlcml.euclid.IntArray.IntArray(IntArray)' */ @Test public void testIntArrayIntArray() { IntArray r = new IntArray(a1); Assert.assertEquals("r", 4, r.size()); IntArrayTest.assertEquals("r", new int[] { 1, 2, 4, 6 }, r); } /** * Test method for 'org.xmlcml.euclid.IntArray.IntArray(String[])' */ @Test public void testIntArrayStringArray() { IntArray r = new IntArray(new String[] { "1", "2", "4", "6" }); IntArrayTest.assertEquals("string array", a1, r); } /** * Test method for 'org.xmlcml.euclid.IntArray.IntArray(String)' */ @Test public void testIntArrayString() { IntArray r = new IntArray("1 2 4 6"); IntArrayTest.assertEquals("string array", a1, r); } /** * Test method for 'org.xmlcml.euclid.IntArray.elementAt(int)' */ @Test public void testElementAt() { Assert.assertEquals("element at", 4, a1.elementAt(2)); try { Assert.assertEquals("element at", 4, a1.elementAt(5)); Assert.fail("should always throw " + "ArrayIndexOutOfBoundsException"); } catch (ArrayIndexOutOfBoundsException e) { MatcherAssert.assertThat("ArrayIndexOutOfBoundsException", e.getMessage(), CoreMatchers.anyOf( CoreMatchers.equalTo("5"), CoreMatchers.equalTo("Index 5 out of bounds for length 4"))); } } /** * Test method for 'org.xmlcml.euclid.IntArray.size()' */ @Test public void testSize() { Assert.assertEquals("size", 0, a0.size()); Assert.assertEquals("size", 4, a1.size()); } /** * Test method for 'org.xmlcml.euclid.IntArray.getArray()' */ @Test public void testGetArray() { String s = Int.testEquals((new int[] {}), a0.getArray()); if (s != null) { Assert.fail("array" + "; " + s); } s = Int.testEquals((new int[] { 1, 2, 4, 6 }), a1.getArray()); if (s != null) { Assert.fail("array" + "; " + s); } } /** * Test method for 'org.xmlcml.euclid.IntArray.clearArray()' */ @Test public void testClearArray() { a1.clearArray(); IntArrayTest.assertEquals("clear", new int[] { 0, 0, 0, 0 }, a1); } /** * Test method for 'org.xmlcml.euclid.IntArray.getReverseArray()' */ @Test public void testGetReverseArray() { int[] d = a1.getReverseArray(); String s = Int.testEquals((new int[] { 6, 4, 2, 1 }), d); if (s != null) { Assert.fail("clear" + "; " + s); } } /** * Test method for 'org.xmlcml.euclid.IntArray.isEqualTo(IntArray)' */ @Test public void testIsEqualTo() { IntArray a = new IntArray("1 2 4 6"); Assert.assertTrue("isEqualTo", a1.isEqualTo(a)); a = new IntArray("1 2 4"); Assert.assertFalse("isEqualTo", a1.isEqualTo(a)); } /** * Test method for 'org.xmlcml.euclid.IntArray.equals(IntArray, int)' */ @Test public void testEqualsIntArrayDouble() { IntArray a = new IntArray("1 2 4 6"); Assert.assertTrue("isEqualTo", a1.equals(a)); a = new IntArray("1 2 4 7"); Assert.assertFalse("isEqualTo", a1.equals(a)); a = new IntArray("1 2 4"); Assert.assertFalse("isEqualTo", a1.equals(a)); } /** * Test method for 'org.xmlcml.euclid.IntArray.plus(IntArray)' */ @Test public void testPlus() { IntArray a2 = a1.plus(new IntArray("10 20 30 40")); IntArrayTest.assertEquals("plus", new int[] { 11, 22, 34, 46 }, a2); } /** * Test method for 'org.xmlcml.euclid.IntArray.subtract(IntArray)' */ @Test public void testSubtract() { IntArray a2 = a1.subtract(new IntArray("10 20 30 40")); IntArrayTest.assertEquals("subtract", new int[] { -9, -18, -26, -34 }, a2); } /** * Test method for 'org.xmlcml.euclid.IntArray.subtractEquals(IntArray)' */ @Test public void testSubtractEquals() { IntArray ia = new IntArray("10 20 30 40"); a1.subtractEquals(ia); IntArrayTest.assertEquals("subtract", new int[] { -9, -18, -26, -34 }, a1); } /** * Test method for 'org.xmlcml.euclid.IntArray.negative()' */ @Test public void testNegative() { a1.negative(); IntArrayTest.assertEquals("negative", new int[] { -1, -2, -4, -6 }, a1); } /** * Test method for 'org.xmlcml.euclid.IntArray.multiplyBy(int)' */ @Test public void testMultiplyBy() { IntArray a = a1.multiplyBy(2); IntArrayTest.assertEquals("multiplyBy", new int[] { 2, 4, 8, 12 }, a); } /** * Test method for 'org.xmlcml.euclid.IntArray.setElementAt(int, int)' */ @Test public void testSetElementAt() { a1.setElementAt(2, 10); IntArrayTest.assertEquals("setElement", new int[] { 1, 2, 10, 6 }, a1); } /** * Test method for 'org.xmlcml.euclid.IntArray.getSubArray(int, int)' */ @Test public void testGetSubArray() { IntArray a = a1.getSubArray(2, 3); IntArrayTest.assertEquals("subArray", new int[] { 4, 6 }, a); a = a1.getSubArray(2, 2); IntArrayTest.assertEquals("subArray", new int[] { 4 }, a); a = a1.getSubArray(0, 3); IntArrayTest.assertEquals("subArray", new int[] { 1, 2, 4, 6 }, a); try { a = a1.getSubArray(0, 5); Assert.fail("should always throw " + "ArrayIndexOutOfBoundsException"); } catch (ArrayIndexOutOfBoundsException e) { MatcherAssert.assertThat("subArray ArrayIndexOutOfBoundsException", S_EMPTY + e, CoreMatchers.startsWith("java.lang.ArrayIndexOutOfBoundsException")); } } /** * Test method for 'org.xmlcml.euclid.IntArray.setElements(int, int[])' */ @Test public void testSetElements() { a1.setElements(1, new int[] { 10, 20 }); IntArrayTest.assertEquals("setElement", new int[] { 1, 10, 20, 6 }, a1); try { a1.setElements(1, new int[] { 10, 20, 30, 40 }); Assert.fail("should always throw " + "ArrayIndexOutOfBoundsException"); } catch (ArrayIndexOutOfBoundsException e) { Assert.assertEquals("subArray ArrayIndexOutOfBoundsException", "java.lang.ArrayIndexOutOfBoundsException", S_EMPTY + e); } } /** * Test method for 'org.xmlcml.euclid.IntArray.isClear()' */ @Test public void testIsClear() { Assert.assertFalse("isClear", a1.isClear()); a1.clearArray(); Assert.assertTrue("isClear", a1.isClear()); } /** * Test method for 'org.xmlcml.euclid.IntArray.setAllElements(int)' */ @Test public void testSetAllElements() { a1.setAllElements(10); IntArrayTest.assertEquals("setElement", new int[] { 10, 10, 10, 10 }, a1); } /** * Test method for 'org.xmlcml.euclid.IntArray.sumAllElements()' */ @Test public void testSumAllElements() { Assert.assertEquals("sum", 13, a1.sumAllElements()); } /** * Test method for 'org.xmlcml.euclid.IntArray.absSumAllElements()' */ @Test public void testAbsSumAllElements() { IntArray a = new IntArray("-1 3 -11 14"); Assert.assertEquals("sum", 5, a.sumAllElements()); Assert.assertEquals("absSum", 29, a.absSumAllElements()); } /** * Test method for 'org.xmlcml.euclid.IntArray.innerProduct()' */ @Test public void testInnerProduct() { Assert.assertEquals("inner", 57, a1.innerProduct()); } /** * Test method for 'org.xmlcml.euclid.IntArray.dotProduct(IntArray)' */ @Test public void testDotProduct() { IntArray a = new IntArray("1 2 3 4"); int d = a1.dotProduct(a); Assert.assertEquals("dot", 41, d); a = new IntArray("1 2 3"); try { a1.dotProduct(a); Assert.fail("should always throw " + "ArrayIndexOutOfBoundsException"); } catch (EuclidRuntimeException e) { Assert.assertEquals("dot", "org.xmlcml.euclid.EuclidRuntimeException", S_EMPTY + e); } } /** * Test method for 'org.xmlcml.euclid.IntArray.cumulativeSum()' */ @Test public void testCumulativeSum() { IntArray a = a1.cumulativeSum(); IntArrayTest.assertEquals("cumulative", new int[] { 1, 3, 7, 13 }, a); } /** * Test method for 'org.xmlcml.euclid.IntArray.trim(int, int)' */ @Test public void testTrim() { IntArray a = new IntArray("1 2 3 4 1 3 5 1 3"); IntArray b = a.trim(Trim.ABOVE, 2); int[] d = { 1, 2, 2, 2, 1, 2, 2, 1, 2 }; IntArrayTest.assertEquals("trim", d, b); b = a.trim(Trim.BELOW, 2); int[] dd = { 2, 2, 3, 4, 2, 3, 5, 2, 3 }; IntArrayTest.assertEquals("trim", dd, b); } /** * Test method for 'org.xmlcml.euclid.IntArray.indexOfLargestElement()' */ @Test public void testIndexOfLargestElement() { Assert.assertEquals("largest", 3, a1.indexOfLargestElement()); } /** * Test method for 'org.xmlcml.euclid.IntArray.indexOfSmallestElement()' */ @Test public void testIndexOfSmallestElement() { Assert.assertEquals("smallest", 0, a1.indexOfSmallestElement()); } /** * Test method for 'org.xmlcml.euclid.IntArray.largestElement()' */ @Test public void testLargestElement() { Assert.assertEquals("largest", 6, a1.largestElement()); } /** * Test method for 'org.xmlcml.euclid.IntArray.getMax()' */ @Test public void testGetMax() { Assert.assertEquals("max", 6, a1.getMax()); } /** * Test method for 'org.xmlcml.euclid.IntArray.smallestElement()' */ @Test public void testSmallestElement() { Assert.assertEquals("smallest", 1, a1.smallestElement()); } /** * Test method for 'org.xmlcml.euclid.IntArray.getMin()' */ @Test public void testGetMin() { Assert.assertEquals("max", 1, a1.getMin()); } /** * Test method for 'org.xmlcml.euclid.IntArray.getRange()' */ @Test public void testGetRange() { IntRange range = a1.getRange(); Assert.assertEquals("range", 1, range.getMin()); Assert.assertEquals("range", 6, range.getMax()); } /** * Test method for 'org.xmlcml.euclid.IntArray.deleteElement(int)' */ @Test public void testDeleteElement() { a1.deleteElement(2); IntArrayTest.assertEquals("delete", new int[] { 1, 2, 6 }, a1); } /** * Test method for 'org.xmlcml.euclid.IntArray.deleteElements(int, int)' */ @Test public void testDeleteElementsIntInt() { IntArray a = new IntArray(a1); a.deleteElements(1, 2); IntArrayTest.assertEquals("delete", new int[] { 1, 6 }, a); a = new IntArray(a1); a.deleteElements(0, 3); IntArrayTest.assertEquals("delete", new int[] {}, a); a = new IntArray(a1); a.deleteElements(2, 2); IntArrayTest.assertEquals("delete", new int[] { 1, 2, 6 }, a); } /** * Test method for 'org.xmlcml.euclid.IntArray.insertElementAt(int, int)' */ @Test public void testInsertElementAt() { IntArray a = new IntArray(a1); a.insertElementAt(1, 30); IntArrayTest.assertEquals("insert", new int[] { 1, 30, 2, 4, 6 }, a); a.insertElementAt(0, 20); IntArrayTest .assertEquals("insert", new int[] { 20, 1, 30, 2, 4, 6 }, a); a.insertElementAt(6, 10); IntArrayTest.assertEquals("insert", new int[] { 20, 1, 30, 2, 4, 6, 10 }, a); } /** * Test method for 'org.xmlcml.euclid.IntArray.insertArray(int, IntArray)' */ @Test public void testInsertArray() { a1.insertArray(1, new IntArray("44 55")); IntArrayTest.assertEquals("insert", new int[] { 1, 44, 55, 2, 4, 6 }, a1); } /** * Test method for 'org.xmlcml.euclid.IntArray.addElement(int)' */ @Test public void testAddElement() { a1.addElement(30); IntArrayTest.assertEquals("insert", new int[] { 1, 2, 4, 6, 30 }, a1); } /** * Test method for 'org.xmlcml.euclid.IntArray.addArray(IntArray)' */ @Test public void testAddArray() { a1.addArray(new IntArray("5 16 7")); IntArrayTest.assertEquals("insert", new int[] { 1, 2, 4, 6, 5, 16, 7 }, a1); } /** * Test method for 'org.xmlcml.euclid.IntArray.getReorderedArray(IntSet)' */ @Test public void testGetReorderedArray() { IntSet intSet = new IntSet(new int[] { 3, 1, 0, 2 }); IntArray a = a1.getReorderedArray(intSet); IntArrayTest.assertEquals("insert", new int[] { 6, 2, 1, 4 }, a); } /** * Test method for 'org.xmlcml.euclid.IntArray.inRange(IntRange)' */ @Test public void testInRange() { IntRange range = new IntRange(1, 5); IntSet intSet = a1.inRange(range); IntArray intArray = intSet.getIntArray(); IntArrayTest.assertEquals("inrange", new int[] { 0, 1, 2 }, intArray); intSet = a1.inRange(new IntRange(-3, 7)); IntArrayTest.assertEquals("inrange", new int[] { 0, 1, 2, 3 }, intSet .getIntArray()); intSet = a1.inRange(new IntRange(5, 5)); IntArrayTest .assertEquals("inrange", new int[] {}, intSet.getIntArray()); } /** * Test method for 'org.xmlcml.euclid.IntArray.outOfRange(IntRange)' */ @Test public void testOutOfRange() { IntRange range = new IntRange(1, 5); IntSet intSet = a1.outOfRange(range); IntArray intArray = intSet.getIntArray(); IntArrayTest.assertEquals("inrange", new int[] { 3 }, intArray); intSet = a1.outOfRange(new IntRange(-3, 7)); IntArrayTest .assertEquals("inrange", new int[] {}, intSet.getIntArray()); intSet = a1.outOfRange(new IntRange(4, 6)); IntArrayTest.assertEquals("inrange", new int[] { 0, 1 }, intSet .getIntArray()); } /** * Test method for 'org.xmlcml.euclid.IntArray.getStringValues()' */ @Test public void testGetStringValues() { String[] ss = a1.getStringValues(); StringTestBase.assertEquals("string values", new String[] { "1", "2", "4", "6" }, ss); } /** * Test method for 'org.xmlcml.euclid.IntArray.sortAscending()' */ @Test public void testSortAscending() { IntArray ra = new IntArray("1 6 3 9 2 0"); ra.sortAscending(); IntArrayTest.assertEquals("sortAscending", new int[] { 0, 1, 2, 3, 6, 9 }, ra); } /** * Test method for 'org.xmlcml.euclid.IntArray.sortDescending()' */ @Test public void testSortDescending() { IntArray ra = new IntArray("1 6 3 9 2 0"); ra.sortDescending(); IntArrayTest.assertEquals("sortDescending", new int[] { 9, 6, 3, 2, 1, 0 }, ra); } /** * Test method for 'org.xmlcml.euclid.IntArray.reverse()' */ @Test public void testReverse() { IntArray ra = new IntArray("1 6 3 9 2 0"); ra.reverse(); IntArrayTest .assertEquals("reverse", new int[] { 0, 2, 9, 3, 6, 1 }, ra); } /** * Test method for 'org.xmlcml.euclid.IntArray.indexSortAscending()' */ @Test public void testIndexSortAscending() { IntArray ra = new IntArray("1 6 3 9 2 0"); IntSet intSet = ra.indexSortAscending(); IntArrayTest.assertEquals("sortAscending", new int[] { 5, 0, 4, 2, 1, 3 }, intSet.getIntArray()); } /** * Test method for 'org.xmlcml.euclid.IntArray.indexSortDescending()' */ @Test public void testIndexSortDescending() { IntArray ra = new IntArray("1 6 3 9 2 0"); IntSet intSet = ra.indexSortDescending(); IntArrayTest.assertEquals("sortDescending", new int[] { 3, 1, 2, 4, 0, 5 }, intSet.getIntArray()); } @Test public void testIterator() { IntArray intArray = new IntArray(new int[]{0,1,2}); Iterator intIterator = intArray.iterator(); Assert.assertTrue("start", intIterator.hasNext()); Assert.assertTrue("start", intIterator.hasNext()); Assert.assertTrue("start", intIterator.hasNext()); Assert.assertTrue("start", intIterator.hasNext()); Assert.assertEquals("start", 0, (int) intIterator.next()); Assert.assertEquals("start", 1, (int) intIterator.next()); Assert.assertTrue("after 1", intIterator.hasNext()); Assert.assertEquals("after 1", 2, (int) intIterator.next()); Assert.assertFalse("end", intIterator.hasNext()); Assert.assertNull("after 2", intIterator.next()); } @Test public void testIterators() { IntArray intArray = new IntArray(new int[]{0,1,2}); Iterator intIterator00 = intArray.iterator(); Iterator intIterator01 = intArray.iterator(); Assert.assertTrue("start", intIterator00.hasNext()); Assert.assertEquals("start", 0, (int) intIterator00.next()); Assert.assertEquals("start", 1, (int) intIterator00.next()); Assert.assertEquals("start", 0, (int) intIterator01.next()); Assert.assertEquals("end0", 2, (int) intIterator00.next()); Assert.assertFalse("end0", intIterator00.hasNext()); Assert.assertTrue("middle1", intIterator01.hasNext()); Assert.assertNull("endo", intIterator00.next()); Assert.assertEquals("start", 1, (int) intIterator01.next()); } } euclid-euclid-2.9/src/test/java/org/xmlcml/euclid/test/IntMatrixTest.java000066400000000000000000000646111461721410700265270ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid.test; import static org.xmlcml.euclid.EC.S_RBRAK; import java.io.IOException; import java.io.StringWriter; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.xmlcml.euclid.EuclidRuntimeException; import org.xmlcml.euclid.Int; import org.xmlcml.euclid.Int2; import org.xmlcml.euclid.IntArray; import org.xmlcml.euclid.IntMatrix; import org.xmlcml.euclid.IntRange; import org.xmlcml.euclid.IntSet; /** * test IntMatrix * * @author pmr * */ public class IntMatrixTest { final static Logger LOG = Logger.getLogger(IntMatrixTest.class); IntMatrix m0; IntMatrix m1; IntMatrix m2; /** * setup. * * @throws Exception */ @Before public void setUp() throws Exception { LOG.setLevel(Level.WARN); m0 = new IntMatrix(); m1 = new IntMatrix(3, 4); m2 = new IntMatrix(3, 4, new int[] { 11, 12, 13, 14, 21, 22, 23, 24, 31, 32, 33, 34, }); } /** * equality test. true if both args not null and equal within epsilon and * rows are present and equals and columns are present and equals * * @param msg * message * @param test * @param expected */ public static void assertEquals(String msg, IntMatrix test, IntMatrix expected) { Assert.assertNotNull("test should not be null (" + msg + S_RBRAK, test); Assert.assertNotNull("expected should not be null (" + msg + S_RBRAK, expected); Assert.assertNotNull("expected should have columns (" + msg + S_RBRAK, expected.getCols()); Assert.assertNotNull("expected should have rows (" + msg + S_RBRAK, expected.getRows()); Assert.assertNotNull("test should have columns (" + msg + S_RBRAK, test .getCols()); Assert.assertNotNull("test should have rows (" + msg + S_RBRAK, test .getRows()); Assert.assertEquals("rows should be equal (" + msg + S_RBRAK, test .getRows(), expected.getRows()); Assert.assertEquals("columns should be equal (" + msg + S_RBRAK, test .getCols(), expected.getCols()); String s = Int.testEquals(test.getMatrixAsArray(), expected .getMatrixAsArray()); if (s != null) { Assert.fail(msg + "; " + s); } } /** * equality test. true if both args not null and equal within epsilon * * @param msg * message * @param rows * @param cols * @param test * @param expected */ public static void assertEquals(String msg, int rows, int cols, int[] test, IntMatrix expected) { Assert.assertNotNull("test should not be null (" + msg + S_RBRAK, test); Assert.assertNotNull("ref should not be null (" + msg + S_RBRAK, expected); Assert.assertEquals("rows should be equal (" + msg + S_RBRAK, rows, expected.getRows()); Assert.assertEquals("columns should be equal (" + msg + S_RBRAK, cols, expected.getCols()); String s = Int.testEquals(test, expected.getMatrixAsArray()); if (s != null) { Assert.fail(msg + "; " + s); } } /** * Test method for 'org.xmlcml.euclid.IntMatrix.IntMatrix()' */ @Test public void testIntMatrix() { Assert.assertEquals("empty", "()", m0.toString()); } /** * Test method for 'org.xmlcml.euclid.IntMatrix.IntMatrix(int, int)' */ @Test public void testIntMatrixIntInt() { Assert.assertEquals("int int", "{3,4}" + "\n(0,0,0,0)" + "\n(0,0,0,0)" + "\n(0,0,0,0)", m1.toString()); Assert.assertEquals("int int rows", 3, m1.getRows()); Assert.assertEquals("int int cols", 4, m1.getCols()); } /** * Test method for 'org.xmlcml.euclid.IntMatrix.IntMatrix(int, int, int[])' */ @Test public void testIntMatrixIntIntIntegerArray() { Assert.assertEquals("int int int[]", "{3,4}" + "\n(11,12,13,14)" + "\n(21,22,23,24)" + "\n(31,32,33,34)", m2.toString()); Assert.assertEquals("int int int[] rows", 3, m2.getRows()); Assert.assertEquals("int int int[] cols", 4, m2.getCols()); } /** * Test method for 'org.xmlcml.euclid.IntMatrix.IntMatrix(int, int, int)' */ @Test public void testIntMatrixIntIntInteger() { IntMatrix m = new IntMatrix(3, 4, 10); Assert.assertEquals("int int int[]", "{3,4}" + "\n(10,10,10,10)" + "\n(10,10,10,10)" + "\n(10,10,10,10)", m.toString()); Assert.assertEquals("int int int[] rows", 3, m.getRows()); Assert.assertEquals("int int int[] cols", 4, m.getCols()); } /** * Test method for 'org.xmlcml.euclid.IntMatrix.IntMatrix(IntMatrix, int, * int, int, int)' */ @Test public void testIntMatrixIntMatrixIntIntIntInt() { IntMatrix m = new IntMatrix(m2, 1, 2, 1, 3); Assert.assertEquals("int int int[]", "{2,3}" + "\n(22,23,24)" + "\n(32,33,34)", m.toString()); Assert.assertEquals("int int int[] rows", 2, m.getRows()); Assert.assertEquals("int int int[] cols", 3, m.getCols()); } /** * Test method for 'org.xmlcml.euclid.IntMatrix.IntMatrix(IntMatrix)' */ @Test public void testIntMatrixIntMatrix() { IntMatrix m = new IntMatrix(m2); Assert.assertEquals("int int int[]", "{3,4}" + "\n(11,12,13,14)" + "\n(21,22,23,24)" + "\n(31,32,33,34)", m.toString()); Assert.assertEquals("int int int[] rows", 3, m.getRows()); Assert.assertEquals("int int int[] cols", 4, m.getCols()); } /** * Test method for 'org.xmlcml.euclid.IntMatrix.getIntMatrix()' */ @Test public void testGetIntMatrix() { IntMatrix mm2 = new IntMatrix(3, 4, new int[] { 11, 12, 13, 14, 21, 22, 23, 24, 31, 32, 33, 34, }); IntMatrix m = mm2.getIntMatrix(); Assert.assertEquals("int int int[]", "{3,4}" + "\n(11,12,13,14)" + "\n(21,22,23,24)" + "\n(31,32,33,34)", m.toString()); Assert.assertEquals("int int int[] rows", 3, m.getRows()); Assert.assertEquals("int int int[] cols", 4, m.getCols()); } /** * Test method for 'org.xmlcml.euclid.IntMatrix.IntMatrix(int[][])' */ @Test public void testIntMatrixIntegerArrayArray() { IntMatrix mm2 = new IntMatrix(new int[][] { new int[] { 11, 12, 13, 14 }, new int[] { 21, 22, 23, 24 }, new int[] { 31, 32, 33, 34 } }); IntMatrix m = mm2.getIntMatrix(); Assert.assertEquals("int int int[]", "{3,4}" + "\n(11,12,13,14)" + "\n(21,22,23,24)" + "\n(31,32,33,34)", m.toString()); Assert.assertEquals("int int int[] rows", 3, m.getRows()); Assert.assertEquals("int int int[] cols", 4, m.getCols()); } /** * Test method for 'org.xmlcml.euclid.IntMatrix.setFormat(DecimalFormat)' */ @Test public void testSetFormat() { } /** * Test method for 'org.xmlcml.euclid.IntMatrix.getFormat()' */ @Test public void testGetFormat() { } /** * Test method for 'org.xmlcml.euclid.IntMatrix.getRows()' */ @Test public void testGetRowsCols() { IntMatrix m = new IntMatrix(new int[][] { new int[] { 11, 12, 13, 14 }, new int[] { 21, 22, 23, 24 }, new int[] { 31, 32, 33, 34 } }); Assert.assertEquals("int int int[] rows", 3, m.getRows()); Assert.assertEquals("int int int[] cols", 4, m.getCols()); } /** * Test method for 'org.xmlcml.euclid.IntMatrix.getMatrix()' */ @Test public void testGetMatrix() { int[][] matrix = m1.getMatrix(); Assert.assertEquals("getMatrix", 3, matrix.length); Assert.assertEquals("getMatrix", 4, matrix[0].length); } /** * Test method for 'org.xmlcml.euclid.IntMatrix.getMatrixAsArray()' */ @Test public void testGetMatrixAsArray() { int[] array = m2.getMatrixAsArray(); String s = Int.testEquals((new int[] { 11, 12, 13, 14, 21, 22, 23, 24, 31, 32, 33, 34 }), array); if (s != null) { Assert.fail("matrix as array" + "; " + s); } } /** * Test method for 'org.xmlcml.euclid.IntMatrix.isEqualTo(IntMatrix)' */ @Test public void testIsEqualTo() { Assert.assertTrue("isEqualTo", m2.isEqualTo(m2)); } /** * Test method for 'org.xmlcml.euclid.IntMatrix.plus(IntMatrix)' */ @Test public void testPlus() { IntMatrix m = m2.plus(m2); IntMatrixTest.assertEquals("matrix as array", 3, 4, new int[] { 22, 24, 26, 28, 42, 44, 46, 48, 62, 64, 66, 68 }, m); } /** * Test method for 'org.xmlcml.euclid.IntMatrix.subtract(IntMatrix)' */ @Test public void testSubtract() { IntMatrix m = new IntMatrix(new int[][] { new int[] { 11, 12, 13, 14 }, new int[] { 21, 22, 23, 24 }, new int[] { 31, 32, 33, 34 } }); IntMatrix mm = m2.subtract(m); IntMatrixTest.assertEquals("matrix as array", 3, 4, new int[] { -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, }, mm); } /** * Test method for 'org.xmlcml.euclid.IntMatrix.negative()' */ @Test public void testNegative() { m2.negative(); IntMatrixTest.assertEquals("matrix as array", 3, 4, new int[] { -11, -12, -13, -14, -21, -22, -23, -24, -31, -32, -33, -34 }, m2); } /** * Test method for 'org.xmlcml.euclid.IntMatrix.multiply(IntMatrix)' */ @Test public void testMultiplyIntMatrix() { IntMatrix m = new IntMatrix(new int[][] { new int[] { 10, 20, 30 }, new int[] { 40, 50, 60 }, }); IntMatrix mm = m.multiply(m2); IntMatrixTest.assertEquals("matrix as array", 2, 4, new int[] { 1460, 1520, 1580, 1640, 3350, 3500, 3650, 3800, }, mm); } /** * Test method for 'org.xmlcml.euclid.IntMatrix.multiplyBy(int)' */ @Test public void testMultiplyBy() { m2.multiplyBy(10); IntMatrixTest.assertEquals("matrix as array", 3, 4, new int[] { 110, 120, 130, 140, 210, 220, 230, 240, 310, 320, 330, 340, }, m2); } /** * Test method for 'org.xmlcml.euclid.IntMatrix.multiplyEquals(IntMatrix)' */ @Test public void testMultiplyEquals() { IntMatrix m = new IntMatrix(new int[][] { new int[] { 10, 20, 30 }, new int[] { 40, 50, 60 }, }); try { m2.multiplyEquals(m); Assert.fail("should always throw " + "non-conformable matrices"); } catch (EuclidRuntimeException e) { Assert.assertEquals("multiplyEquals", "unequal matrices (4, 2)", e .getMessage()); } m.multiplyEquals(m2); IntMatrixTest.assertEquals("matrix as array", 2, 4, new int[] { 1460, 1520, 1580, 1640, 3350, 3500, 3650, 3800, }, m); } /** * Test method for 'org.xmlcml.euclid.IntMatrix.multiply(IntArray)' */ @Test public void testMultiplyIntArray() { IntArray ra = new IntArray(new int[] { 1, 2, 3, 4 }); IntArray raa = m2.multiply(ra); IntArrayTest.assertEquals("array", new int[] { 130, 230, 330 }, raa); } /** * Test method for 'org.xmlcml.euclid.IntMatrix.columnwiseDivide(IntArray)' */ @Test public void testColumnwiseDivide() { IntArray ra = new IntArray(new int[] { 1, 2, 3, 4 }); m2.columnwiseDivide(ra); IntMatrixTest.assertEquals("array", 3, 4, new int[] { 11, 6, 4, 3, 21, 11, 7, 6, 31, 16, 11, 8, }, m2); } /** * Test method for 'org.xmlcml.euclid.IntMatrix.elementAt(int, int)' */ @Test public void testElementAtIntInt() { Assert.assertEquals("elementAt ", 32, m2.elementAt(2, 1)); try { m2.elementAt(5, 5); } catch (EuclidRuntimeException e) { Assert.assertEquals("elementAt", "Bad value of row: 5/3", e .getMessage()); } } /** * Test method for 'org.xmlcml.euclid.IntMatrix.elementAt(Int2)' */ @Test public void testElementAtInt2() { Assert.assertEquals("elementAt ", 32, m2.elementAt(new Int2(2, 1))); try { m2.elementAt(new Int2(5, 5)); } catch (EuclidRuntimeException e) { Assert.assertEquals("elementAt", "Bad value of row: 5/3", e .getMessage()); } } /** * Test method for 'org.xmlcml.euclid.IntMatrix.setElementAt(int, int, int)' */ @Test public void testSetElementAt() { m2.setElementAt(1, 2, 15); IntMatrixTest.assertEquals("matrix as array", 3, 4, new int[] { 11, 12, 13, 14, 21, 22, 15, 24, 31, 32, 33, 34, }, m2); } /** * Test method for 'org.xmlcml.euclid.IntMatrix.largestElement()' */ @Test public void testLargestElement() { int d = m2.largestElement(); Assert.assertEquals("largestElement", 34, d); } /** * Test method for 'org.xmlcml.euclid.IntMatrix.indexOfLargestElement()' */ @Test public void testIndexOfLargestElement() { Int2 ii = m2.indexOfLargestElement(); Assert.assertEquals("indexOfLargestElement", 2, ii.getX()); Assert.assertEquals("indexOfLargestElement", 3, ii.getY()); } /** * Test method for 'org.xmlcml.euclid.IntMatrix.largestElementInColumn(int)' */ @Test public void testLargestElementInColumn() { int d = m2.largestElementInColumn(1); Assert.assertEquals("largestElement", 32, d); } /** * Test method for * 'org.xmlcml.euclid.IntMatrix.indexOfLargestElementInColumn(int)' */ @Test public void testIndexOfLargestElementInColumn() { int i = m2.indexOfLargestElementInColumn(1); Assert.assertEquals("largestElement", 2, i); } /** * Test method for 'org.xmlcml.euclid.IntMatrix.largestElementInRow(int)' */ @Test public void testLargestElementInRow() { int d = m2.largestElementInRow(1); Assert.assertEquals("largestElement", 24, d); } /** * Test method for * 'org.xmlcml.euclid.IntMatrix.indexOfLargestElementInRow(int)' */ @Test public void testIndexOfLargestElementInRow() { int i = m2.indexOfLargestElementInRow(1); Assert.assertEquals("largestElement", 3, i); } /** * Test method for 'org.xmlcml.euclid.IntMatrix.smallestElement()' */ @Test public void testSmallestElement() { int d = m2.smallestElement(); Assert.assertEquals("smallestElement", 11, d); } /** * Test method for 'org.xmlcml.euclid.IntMatrix.indexOfSmallestElement()' */ @Test public void testIndexOfSmallestElement() { Int2 ii = m2.indexOfSmallestElement(); Assert.assertEquals("indexOfSmallestElement", 0, ii.getX()); Assert.assertEquals("indexOfSmallestElement", 0, ii.getY()); } /** * Test method for * 'org.xmlcml.euclid.IntMatrix.smallestElementInColumn(int)' */ @Test public void testSmallestElementInColumn() { int d = m2.smallestElementInColumn(1); Assert.assertEquals("smallestElement", 12, d); } /** * Test method for * 'org.xmlcml.euclid.IntMatrix.indexOfSmallestElementInColumn(int)' */ @Test public void testIndexOfSmallestElementInColumn() { int i = m2.indexOfSmallestElementInColumn(1); Assert.assertEquals("largestElement", 0, i); } /** * Test method for 'org.xmlcml.euclid.IntMatrix.smallestElementInRow(int)' */ @Test public void testSmallestElementInRow() { int d = m2.smallestElementInRow(1); Assert.assertEquals("smallestElement", 21, d); } /** * Test method for * 'org.xmlcml.euclid.IntMatrix.indexOfSmallestElementInRow(int)' */ @Test public void testIndexOfSmallestElementInRow() { int i = m2.indexOfSmallestElementInRow(1); Assert.assertEquals("largestElement", 0, i); } /** * Test method for 'org.xmlcml.euclid.IntMatrix.extractColumnData(int)' */ @Test public void testExtractColumnData() { IntArray ra = m2.extractColumnData(1); IntArrayTest.assertEquals("euclidean column lengths", new int[] { 12, 22, 32 }, ra); } /** * Test method for 'org.xmlcml.euclid.IntMatrix.extractRowData(int)' */ @Test public void testExtractRowData() { IntArray ra = m2.extractRowData(1); IntArrayTest.assertEquals("euclidean column lengths", new int[] { 21, 22, 23, 24 }, ra); } /** * Test method for 'org.xmlcml.euclid.IntMatrix.clearMatrix()' */ @Test public void testClearMatrix() { m2.clearMatrix(); IntMatrixTest.assertEquals("matrix as array", 3, 4, new int[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, m2); } /** * Test method for 'org.xmlcml.euclid.IntMatrix.setAllElements(int)' */ @Test public void testSetAllElements() { m2.setAllElements(23); IntMatrixTest.assertEquals("matrix as array", 3, 4, new int[] { 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, }, m2); } /** * Test method for 'org.xmlcml.euclid.IntMatrix.getTranspose()' */ @Test public void testGetTranspose() { IntMatrix m = m2.getTranspose(); IntMatrixTest.assertEquals("transpose", 4, 3, new int[] { 11, 21, 31, 12, 22, 32, 13, 23, 33, 14, 24, 34, }, m); } /** * Test method for 'org.xmlcml.euclid.IntMatrix.isSquare()' */ @Test public void testIsSquare() { Assert.assertFalse("isSquare", m2.isSquare()); Assert.assertTrue("isSquare", new IntMatrix(2, 2, new int[] { 11, 12, 21, 22 }).isSquare()); } /** * Test method for 'org.xmlcml.euclid.IntMatrix.deleteColumn(int)' */ @Test public void testDeleteColumn() { m2.deleteColumn(1); IntMatrixTest.assertEquals("matrix as array", 3, 3, new int[] { 11, 13, 14, 21, 23, 24, 31, 33, 34, }, m2); } /** * Test method for 'org.xmlcml.euclid.IntMatrix.deleteColumns(int, int)' */ @Test public void testDeleteColumns() { m2.deleteColumns(1, 2); IntMatrixTest.assertEquals("matrix as array", 3, 2, new int[] { 11, 14, 21, 24, 31, 34, }, m2); } /** * Test method for 'org.xmlcml.euclid.IntMatrix.deleteRow(int)' */ @Test public void testDeleteRow() { m2.deleteRow(1); IntMatrixTest.assertEquals("matrix as array", 2, 4, new int[] { 11, 12, 13, 14, 31, 32, 33, 34, }, m2); } /** * Test method for 'org.xmlcml.euclid.IntMatrix.deleteRows(int, int)' */ @Test public void testDeleteRows() { // FIXME does not work for high = nrows m2.deleteRows(1, 1); IntMatrixTest.assertEquals("matrix as array", 2, 4, new int[] { 11, 12, 13, 14, 31, 32, 33, 34, }, m2); } /** * Test method for 'org.xmlcml.euclid.IntMatrix.replaceColumnData(int, * IntArray)' */ @Test public void testReplaceColumnDataIntIntArray() { m2.replaceColumnData(1, new IntArray(new int[] { 19, 29, 39 })); IntMatrixTest.assertEquals("matrix as array", 3, 4, new int[] { 11, 19, 13, 14, 21, 29, 23, 24, 31, 39, 33, 34, }, m2); } /** * Test method for 'org.xmlcml.euclid.IntMatrix.replaceColumnData(int, * int[])' */ @Test public void testReplaceColumnDataIntIntegerArray() { m2.replaceColumnData(1, new int[] { 19, 29, 39 }); IntMatrixTest.assertEquals("matrix as array", 3, 4, new int[] { 11, 19, 13, 14, 21, 29, 23, 24, 31, 39, 33, 34, }, m2); } /** * Test method for 'org.xmlcml.euclid.IntMatrix.replaceColumnData(int, * IntMatrix)' */ @Test public void testReplaceColumnDataIntIntMatrix() { IntMatrix expect = null; IntMatrix m = new IntMatrix(3, 2, new int[] { 72, 73, 82, 83, 92, 93 }); m2.replaceColumnData(1, m); expect = new IntMatrix(3, 4, new int[] { 11, 72, 73, 14, 21, 82, 83, 24, 31, 92, 93, 34, }); IntMatrixTest.assertEquals("matrix as array", m2, expect); } /** * Test method for 'org.xmlcml.euclid.IntMatrix.insertColumns(int, int)' */ @Test public void testInsertColumns() { // inserts 3 empty columns m2.makeSpaceForNewColumns(1, 3); IntMatrix expect = new IntMatrix(3, 7, new int[] { 11, 0, 0, 0, 12, 13, 14, 21, 0, 0, 0, 22, 23, 24, 31, 0, 0, 0, 32, 33, 34, }); IntMatrixTest.assertEquals("matrix as array", m2, expect); } /** * Test method for 'org.xmlcml.euclid.IntMatrix.insertColumnData(int, * IntArray)' */ @Test public void testInsertColumnDataIntIntArray() { // inserts a column m2.insertColumnData(1, new IntArray(new int[] { 91, 92, 93 })); IntMatrix expect = new IntMatrix(3, 5, new int[] { 11, 12, 91, 13, 14, 21, 22, 92, 23, 24, 31, 32, 93, 33, 34, }); IntMatrixTest.assertEquals("matrix as array", m2, expect); } /** * Test method for 'org.xmlcml.euclid.IntMatrix.insertColumnData(int, * IntMatrix)' */ @Test public void testInsertColumnDataIntIntMatrix() { LOG.info("+++insertColumnData>>>"); IntMatrix insert = new IntMatrix(3, 2, new int[] { 72, 73, 82, 83, 92, 93, }); m2.insertColumnData(1, insert); IntMatrix expect = new IntMatrix(3, 6, new int[] { 11, 12, 72, 73, 13, 14, 21, 22, 82, 83, 23, 24, 31, 32, 92, 93, 33, 34, }); IntMatrixTest.assertEquals("matrix as array", m2, expect); } /** * Test method for 'org.xmlcml.euclid.IntMatrix.insertRows(int, int)' */ @Test public void testInsertRows() { m2.insertRows(1, 2); int[] array = m2.getMatrixAsArray(); String s = Int.testEquals((new int[] { 11, 12, 13, 14, 0, 0, 0, 0, 0, 0, 0, 0, 21, 22, 23, 24, 31, 32, 33, 34, }), array); if (s != null) { Assert.fail("matrix as array" + "; " + s); } } /** * Test method for 'org.xmlcml.euclid.IntMatrix.replaceRowData(int, * IntArray)' */ @Test public void testReplaceRowDataIntIntArray() { m2.replaceRowData(1, new IntArray(new int[] { 71, 72, 73, 74 })); int[] array = m2.getMatrixAsArray(); String s = Int.testEquals((new int[] { 11, 12, 13, 14, 71, 72, 73, 74, 31, 32, 33, 34, }), array); if (s != null) { Assert.fail("matrix as array" + "; " + s); } } /** * Test method for 'org.xmlcml.euclid.IntMatrix.replaceRowData(int, int[])' */ @Test public void testReplaceRowDataIntIntegerArray() { m2.replaceRowData(1, new int[] { 71, 72, 73, 74 }); int[] array = m2.getMatrixAsArray(); String s = Int.testEquals((new int[] { 11, 12, 13, 14, 71, 72, 73, 74, 31, 32, 33, 34, }), array); if (s != null) { Assert.fail("matrix as array" + "; " + s); } } /** * Test method for 'org.xmlcml.euclid.IntMatrix.replaceRowData(int, * IntMatrix)' */ @Test public void testReplaceRowDataIntIntMatrix() { LOG.info("+++replaceRowData>>>"); // FIXME IntMatrix insert = new IntMatrix(new IntMatrix(2, 4, new int[] { 71, 72, 73, 74, 81, 82, 83, 84, })); m2.replaceRowData(0, insert); IntMatrix expect = new IntMatrix(3, 4, new int[] { 11, 12, 13, 14, 71, 72, 73, 74, 81, 82, 83, 84, }); // rows 2 and 3 are not filled IntMatrixTest.assertEquals("matrix as array", m2, expect); } /** * Test method for 'org.xmlcml.euclid.IntMatrix.insertRowData(int, * IntMatrix)' */ @Test public void testInsertRowDataIntIntMatrix() { // FIXME m2.insertRowData(1, new IntMatrix(2, 4, new int[] { 71, 72, 73, 74, 81, 82, 83, 84, })); IntMatrix expect = new IntMatrix(5, 4, new int[] { 11, 12, 13, 14, 21, 22, 23, 24, 71, 72, 73, 74, 81, 82, 83, 84, 31, 32, 33, 34, }); String s = Int.testEquals(expect.getMatrixAsArray(), m2 .getMatrixAsArray()); if (s != null) { Assert.fail("matrix as array" + "; " + s); } } /** * Test method for 'org.xmlcml.euclid.IntMatrix.insertRowData(int, * IntArray)' */ @Test public void testInsertRowDataIntIntArray() { IntMatrixTest.assertEquals("matrix as array", 3, 4, new int[] { 11, 12, 13, 14, 21, 22, 23, 24, 31, 32, 33, 34, }, m2); m2.insertRowData(1, new IntArray(new int[] { 71, 72, 73, 74, })); IntMatrixTest.assertEquals("matrix as array", 4, 4, new int[] { 11, 12, 13, 14, 21, 22, 23, 24, 71, 72, 73, 74, 31, 32, 33, 34, }, m2); } /** * Test method for 'org.xmlcml.euclid.IntMatrix.appendColumnData(IntArray)' */ @Test public void testAppendColumnDataIntArray() { m2.appendColumnData(new IntArray(new int[] { 17, 27, 37, })); int[] array = m2.getMatrixAsArray(); String s = Int.testEquals((new int[] { 11, 12, 13, 14, 17, 21, 22, 23, 24, 27, 31, 32, 33, 34, 37 }), array); if (s != null) { Assert.fail("matrix as array" + "; " + s); } } /** * Test method for 'org.xmlcml.euclid.IntMatrix.appendColumnData(IntMatrix)' */ @Test public void testAppendColumnDataIntMatrix() { // logger.info("+++appendColumnData>>>"); IntMatrix rm = new IntMatrix(3, 2, new int[] { 17, 18, 27, 28, 37, 38 }); m2.appendColumnData(rm); IntMatrix expect = new IntMatrix(3, 6, new int[] { 11, 12, 13, 14, 17, 18, 21, 22, 23, 24, 27, 28, 31, 32, 33, 34, 37, 38 }); IntMatrixTest.assertEquals("matrix as array", m2, expect); } /** * Test method for 'org.xmlcml.euclid.IntMatrix.appendRowData(IntArray)' */ @Test public void testAppendRowDataIntArray() { IntArray ra = new IntArray(new int[] { 41, 42, 43, 44 }); m2.appendRowData(ra); // fails to insert data IntMatrixTest.assertEquals("matrix as array", 4, 4, new int[] { 11, 12, 13, 14, 21, 22, 23, 24, 31, 32, 33, 34, 41, 42, 43, 44 }, m2); } /** * Test method for 'org.xmlcml.euclid.IntMatrix.appendRowData(IntMatrix)' */ @Test public void testAppendRowDataIntMatrix() { LOG.info("+++appendRowData>>>"); // FIXME IntMatrix rm = new IntMatrix(2, 4, new int[] { 41, 42, 43, 44, 51, 52, 53, 54 }); m2.appendRowData(rm); IntMatrix expect = new IntMatrix(5, 4, new int[] { 11, 12, 13, 14, 21, 22, 23, 24, 31, 32, 33, 34, 41, 42, 43, 44, 51, 52, 53, 54 }); IntMatrixTest.assertEquals("matrix as array", m2, expect); } /** * Test method for 'org.xmlcml.euclid.IntMatrix.replaceSubMatrixData(int, * int, IntMatrix)' */ @Test public void testReplaceSubMatrixData() { IntMatrix rm = new IntMatrix(2, 2, new int[] { 71, 72, 81, 82 }); m2.replaceSubMatrixData(1, 1, rm); // fails to insert data IntMatrixTest.assertEquals("matrix as array", 3, 4, new int[] { 71, 72, 13, 14, 81, 82, 23, 24, 31, 32, 33, 34, }, m2); } /** * Test method for 'org.xmlcml.euclid.IntMatrix.reorderColumnsBy(IntSet)' */ @Test public void testReorderColumnsBy() { IntMatrix mm = m2 .reorderColumnsBy(new IntSet(new int[] { 3, 1, 2, 0 })); // fails to insert data IntMatrixTest.assertEquals("matrix as array", 3, 4, new int[] { 14, 12, 13, 11, 24, 22, 23, 21, 34, 32, 33, 31 }, mm); } /** * Test method for 'org.xmlcml.euclid.IntMatrix.reorderRowsBy(IntSet)' */ @Test public void testReorderRowsBy() { IntMatrix mm = m2.reorderRowsBy(new IntSet(new int[] { 1, 2, 0 })); // fails to insert data IntMatrixTest.assertEquals("matrix as array", 3, 4, new int[] { 21, 22, 23, 24, 31, 32, 33, 34, 11, 12, 13, 14, }, mm); } /** * Test method for 'org.xmlcml.euclid.IntMatrix.extractSubMatrixData(int, * int, int, int)' */ @Test public void testExtractSubMatrixData() { IntMatrix mm = m2.extractSubMatrixData(1, 2, 2, 3); IntMatrixTest.assertEquals("sub matrix", 2, 2, new int[] { 23, 24, 33, 34 }, mm); } /** * Test method for 'org.xmlcml.euclid.IntMatrix.elementsInRange(IntRange)' */ @Test public void testElementsInRange() { IntMatrix im = m2.elementsInRange(new IntRange(13, 31)); IntMatrixTest.assertEquals("sub matrix", 3, 4, new int[] { 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0 }, im); } /** * Test method for 'org.xmlcml.euclid.IntMatrix.writeXML(Writer)' */ @Test public void testWriteXML() { StringWriter w = new StringWriter(); try { m2.writeXML(w); w.close(); } catch (IOException e) { throw new EuclidRuntimeException("should never throw " + e); } Assert .assertEquals( "writeXML", "11 12 13 14 21 22 23 24 31 32 33 34", w.toString()); } } euclid-euclid-2.9/src/test/java/org/xmlcml/euclid/test/IntRangeArrayTest.java000077500000000000000000000116601461721410700273150ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid.test; import junit.framework.Assert; import org.junit.Test; import org.xmlcml.euclid.IntRange; import org.xmlcml.euclid.IntRangeArray; public class IntRangeArrayTest { private IntRange ir10_25 = new IntRange(10, 25); private IntRange ir10_20 = new IntRange(10, 20); private IntRange ir30_40 = new IntRange(30, 40); private IntRange ir15_25 = new IntRange(15, 25); private IntRange ir15_17 = new IntRange(15, 17); private IntRange ir50_60 = new IntRange(50, 60); @Test public void testAdd() { IntRangeArray array = new IntRangeArray(); Assert.assertEquals(0, array.size()); } @Test public void testAdd1() { IntRangeArray array = new IntRangeArray(); array.add(ir10_20); Assert.assertEquals(1, array.size()); } @Test public void testAdd2() { IntRangeArray array = new IntRangeArray(); array.add(ir10_20); array.add(ir15_25); Assert.assertEquals(2, array.size()); } @Test public void testEquals() { IntRangeArray array = new IntRangeArray(); array.add(ir10_20); array.add(ir15_25); IntRangeArray array2 = new IntRangeArray(); array2.add(ir10_20); array2.add(ir15_25); Assert.assertTrue(array.equals(array2)); } @Test /** * order of addition matters */ public void testNotEquals() { IntRangeArray array = new IntRangeArray(); array.add(ir10_20); array.add(ir15_25); IntRangeArray array2 = new IntRangeArray(); array2.add(ir15_25); array2.add(ir10_20); Assert.assertFalse("order matters", array.equals(array2)); } @Test /** * order of addition matters */ public void testSort() { IntRangeArray array = new IntRangeArray(); array.add(ir10_20); array.add(ir15_17); array.add(ir15_25); array.add(ir30_40); array.add(ir50_60); IntRangeArray array2 = new IntRangeArray(); array2.add(ir50_60); array2.add(ir15_25); array2.add(ir30_40); array2.add(ir15_17); array2.add(ir10_20); Assert.assertFalse("order matters", array.equals(array2)); array2.sort(); Assert.assertTrue("after sorting", array.equals(array2)); } @Test /** * sort and overlap */ public void testSortAndRemoveOverlapping() { IntRangeArray array = new IntRangeArray(); array.add(ir10_25); array.add(ir30_40); array.add(ir50_60); IntRangeArray array2 = new IntRangeArray(); array2.add(ir50_60); array2.add(ir15_25); array2.add(ir30_40); array2.add(ir15_17); array2.add(ir10_20); Assert.assertFalse("order matters", array.equals(array2)); array2.sortAndRemoveOverlapping(); Assert.assertTrue("after sorting", array.equals(array2)); } @Test /** */ public void testPlus() { IntRangeArray array = new IntRangeArray(); array.add(ir10_20); array.add(ir30_40); array.add(ir50_60); IntRangeArray array2 = new IntRangeArray(); array2.add(ir15_25); array2.add(ir15_17); IntRangeArray plus = array2.plus(array); IntRangeArray ref = new IntRangeArray(); ref.add(ir10_25); ref.add(ir30_40); ref.add(ir50_60); Assert.assertTrue("after sorting", ref.equals(plus)); } @Test /** */ public void testInverse() { IntRangeArray array = new IntRangeArray(); array.add(ir10_20); array.add(ir30_40); array.add(ir50_60); IntRangeArray inverse = array.inverse(); IntRangeArray ref = new IntRangeArray(); ref.add(new IntRange(20, 30)); ref.add(new IntRange(40, 50)); Assert.assertTrue("after sorting", ref.equals(inverse)); } @Test /** */ public void testInverse0() { IntRangeArray array = new IntRangeArray(); IntRangeArray inverse = array.inverse(); Assert.assertNull(inverse); } @Test /** */ public void testInverse1() { IntRangeArray array = new IntRangeArray(); array.add(ir10_20); IntRangeArray inverse = array.inverse(); Assert.assertEquals(0, inverse.size()); } @Test /** */ public void testInverse1a() { IntRangeArray array = new IntRangeArray(); array.add(ir10_20); array.add(ir15_25); IntRangeArray inverse = array.inverse(); Assert.assertEquals("inverse without caps", 0, inverse.size()); } @Test /** */ public void testInverseWithCaps() { IntRangeArray array = new IntRangeArray(); array.add(new IntRange(0,0)); array.add(new IntRange(100,100)); IntRangeArray inverse = array.inverse(); Assert.assertEquals(1, inverse.size()); IntRangeArray ref = new IntRangeArray(); ref.add(new IntRange(0,100)); Assert.assertTrue("inverse", ref.equals(inverse)); } } euclid-euclid-2.9/src/test/java/org/xmlcml/euclid/test/IntRangeTest.java000066400000000000000000000162171461721410700263160ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid.test; import java.util.Arrays; import java.util.List; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.xmlcml.euclid.IntRange; /** * test IntRange * * @author pmr * */ public class IntRangeTest { IntRange i0; IntRange i1; IntRange i2; /** * main * * @param args */ public static void main(String[] args) { } /** * setup. * * @throws Exception */ @Before public void setUp() throws Exception { i0 = new IntRange(); i1 = new IntRange(1, 1); i2 = new IntRange(1, 3); } /** * Test method for 'org.xmlcml.euclid.IntRange.IntRange()' */ @Test public void testIntRange() { Assert.assertEquals("empty", "NULL", i0.toString()); } /** * Test method for 'org.xmlcml.euclid.IntRange.IntRange(int, int)' */ @Test public void testIntRangeIntInt() { Assert.assertEquals("i1", "(1,1)", i1.toString()); Assert.assertEquals("i2", "(1,3)", i2.toString()); } /** * Test method for 'org.xmlcml.euclid.IntRange.IntRange(IntRange)' */ @Test public void testIntRangeIntRange() { IntRange ii = new IntRange(i2); Assert.assertEquals("ii", "(1,3)", ii.toString()); } /** * Test method for 'org.xmlcml.euclid.IntRange.isValid()' */ @Test public void testIsValid() { Assert.assertTrue("valid", i2.isValid()); Assert.assertFalse("invalid", i0.isValid()); } /** * Test method for 'org.xmlcml.euclid.IntRange.isEqualTo(IntRange)' */ @Test public void testIsEqualTo() { Assert.assertTrue("equal", i2.isEqualTo(i2)); Assert.assertFalse("equal", i2.isEqualTo(i0)); Assert.assertFalse("equal", i0.isEqualTo(i0)); } /** * Test method for 'org.xmlcml.euclid.IntRange.plus(IntRange)' */ @Test public void testPlus() { IntRange ix = new IntRange(1, 4); IntRange iy = new IntRange(2, 3); IntRange ii = ix.plus(iy); Assert.assertEquals("ii", "(1,4)", ii.toString()); iy = new IntRange(0, 2); ii = ix.plus(iy); Assert.assertEquals("ii", "(0,4)", ii.toString()); iy = new IntRange(2, 6); ii = ix.plus(iy); Assert.assertEquals("ii", "(1,6)", ii.toString()); iy = new IntRange(); ii = ix.plus(iy); Assert.assertEquals("ii", "(1,4)", ii.toString()); } /** * Test method for 'org.xmlcml.euclid.IntRange.intersectionWith(IntRange)' */ @Test public void testIntersectionWith() { IntRange ix = new IntRange(1, 4); IntRange iy = new IntRange(2, 3); IntRange ii = ix.intersectionWith(iy); Assert.assertEquals("ii", "(2,3)", ii.toString()); iy = new IntRange(0, 2); ii = ix.intersectionWith(iy); Assert.assertEquals("ii", "(1,2)", ii.toString()); iy = new IntRange(2, 6); ii = ix.intersectionWith(iy); Assert.assertEquals("ii", "(2,4)", ii.toString()); iy = new IntRange(); ii = ix.intersectionWith(iy); Assert.assertEquals("ii", "NULL", ii.toString()); } /** * Test method for 'org.xmlcml.euclid.IntRange.getMin()' */ @Test public void testGetMin() { Assert.assertEquals("min", 1, i2.getMin()); } /** * Test method for 'org.xmlcml.euclid.IntRange.getMax()' */ @Test public void testGetMax() { Assert.assertEquals("max", 3, i2.getMax()); } /** * Test method for 'org.xmlcml.euclid.IntRange.getRange()' */ @Test public void testGetRange() { Assert.assertEquals("range", 2, i2.getRange()); } /** * Test method for 'org.xmlcml.euclid.IntRange.includes(IntRange)' */ @Test public void testIncludesIntRange() { Assert.assertTrue("includes", i2.includes(new IntRange(2, 3))); Assert.assertFalse("includes", i2.includes(new IntRange(0, 3))); } /** * Test method for 'org.xmlcml.euclid.IntRange.includes(int)' */ @Test public void testIncludesInt() { Assert.assertTrue("includes", i2.includes(1)); Assert.assertFalse("includes", i2.includes(0)); } /** * Test method for 'org.xmlcml.euclid.IntRange.contains(int)' */ @Test public void testContains() { Assert.assertTrue("contains", i2.contains(1)); Assert.assertFalse("contains", i2.contains(0)); } /** * Test method for 'org.xmlcml.euclid.IntRange.add(int)' */ @Test public void testAdd() { i2.add(2); Assert.assertEquals("ii", "(1,3)", i2.toString()); i2.add(0); Assert.assertEquals("ii", "(0,3)", i2.toString()); i2.add(9); Assert.assertEquals("ii", "(0,9)", i2.toString()); } /** midpoint * */ @Test public void testMidPoint() { Assert.assertEquals("mid", 0, i0.getMidPoint()); Assert.assertEquals("mid", 1, i1.getMidPoint()); Assert.assertEquals("mid", 2, i2.getMidPoint()); } @Test public void testCanJoin() { IntRange range = new IntRange(1,5); Assert.assertTrue(range.canJoin(new IntRange(5,8), 0)); Assert.assertTrue(range.canJoin(new IntRange(5,8), 1)); // gap Assert.assertFalse(range.canJoin(new IntRange(6,8), 0)); // allowed tolerance Assert.assertTrue(range.canJoin(new IntRange(6,8), 1)); // overlap too large Assert.assertFalse(range.canJoin(new IntRange(3,8), 1)); // included Assert.assertFalse(range.canJoin(new IntRange(3,5), 1)); Assert.assertFalse(range.canJoin(new IntRange(1,5), 1)); } /** join ranges * * [(269,364) x 6, (478,554) x 6, (420,478) x 6, (364,420) x 6, (151,269) x 6] */ @Test public void testJoinRanges1() { List rangeList = Arrays.asList( new IntRange[] { new IntRange(269,364), new IntRange(478,554), new IntRange(420,478), new IntRange(364,420), new IntRange(151,269), }); String rangeListString = "[(269,364), (478,554), (420,478), (364,420), (151,269)]"; Assert.assertEquals(rangeListString, rangeList.toString()); List totalList = IntRange.joinRanges(rangeList, 0); Assert.assertEquals(1, totalList.size()); Assert.assertEquals("(151,554)", totalList.get(0).toString()); Assert.assertEquals(rangeListString, rangeList.toString()); } /** join ranges * * // this has a gap (241-242) [(253,265) x 6, (276,288) x 6, (242,253) x 6, (265,276) x 6, (222,241) x 6] */ @Test public void testJoinRanges2() { List intRangeList = Arrays.asList( new IntRange[] { new IntRange(253,265), new IntRange(276,288), new IntRange(242,253), new IntRange(265,276), new IntRange(222,241), }); List totalList = IntRange.joinRanges(intRangeList, 0); Assert.assertEquals(2, totalList.size()); Assert.assertEquals("(222,241)", totalList.get(0).toString()); Assert.assertEquals("(242,288)", totalList.get(1).toString()); // allow a tolerance totalList = IntRange.joinRanges(intRangeList, 1); Assert.assertEquals(1, totalList.size()); Assert.assertEquals("(222,288)", totalList.get(0).toString()); } } euclid-euclid-2.9/src/test/java/org/xmlcml/euclid/test/IntSetTest.java000066400000000000000000000262711461721410700260160ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid.test; import static org.xmlcml.euclid.EC.S_EMPTY; import java.util.List; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.xmlcml.euclid.EC; import org.xmlcml.euclid.EuclidRuntimeException; import org.xmlcml.euclid.Int; import org.xmlcml.euclid.IntRange; import org.xmlcml.euclid.IntSet; /** * test IntSet. * * @author pmr * */ public class IntSetTest { IntSet i0; IntSet i1; IntSet i2; IntSet i3; /** * setup. * * @throws Exception */ @Before public void setUp() throws Exception { i0 = new IntSet(); i1 = new IntSet(new int[] { 3, 4, 1, 2 }); i2 = new IntSet(4); i3 = new IntSet(2, 5); } /** * equality test. true if both args not null and equal * * @param msg * message * @param test * @param expected */ public static void assertEquals(String msg, IntSet test, IntSet expected) { Assert.assertNotNull("test should not be null (" + msg + EC.S_RBRAK, test); Assert.assertNotNull("expected should not be null (" + msg + EC.S_RBRAK, expected); String s = Int.testEquals(test.getElements(), expected.getElements()); if (s != null) { Assert.fail(msg + "; " + s); } } /** * equality test. true if both args not null and equal * * @param msg * message * @param test * @param expected */ public static void assertEquals(String msg, int[] test, IntSet expected) { Assert.assertNotNull("test should not be null (" + msg + EC.S_RBRAK, test); Assert.assertNotNull("expected should not be null (" + msg + EC.S_RBRAK, expected); Assert.assertEquals("must be of equal length ", test.length, expected .getElements().length); String s = Int.testEquals(test, expected.getElements()); if (s != null) { Assert.fail(msg + "; " + s); } } /** * Test method for 'org.xmlcml.euclid.IntSet.IntSet()' */ @Test public void testIntSet() { Assert.assertEquals("empty", "()", i0.toString()); Assert.assertFalse("int, int ", i0.contains(0)); Assert.assertFalse("int, int ", i0.contains(1)); } /** * Test method for 'org.xmlcml.euclid.IntSet.IntSet(int)' */ @Test public void testIntSetInt() { Assert.assertEquals("int[]", "(3,4,1,2)", i1.toString()); Assert.assertFalse("int, int ", i1.contains(0)); Assert.assertTrue("int, int ", i1.contains(1)); Assert.assertTrue("int, int ", i1.contains(2)); Assert.assertTrue("int, int ", i1.contains(3)); Assert.assertTrue("int, int ", i1.contains(4)); Assert.assertFalse("int, int ", i1.contains(5)); } /** * Test method for 'org.xmlcml.euclid.IntSet.IntSet(int, int)' */ @Test public void testIntSetIntInt() { Assert.assertEquals("int", "(2,3,4,5)", i3.toString()); Assert.assertFalse("int, int ", i3.contains(0)); Assert.assertFalse("int, int ", i3.contains(1)); Assert.assertTrue("int, int ", i3.contains(2)); Assert.assertTrue("int, int ", i3.contains(3)); Assert.assertTrue("int, int ", i3.contains(4)); Assert.assertTrue("int, int ", i3.contains(5)); Assert.assertFalse("int, int ", i3.contains(6)); } /** * Test method for 'org.xmlcml.euclid.IntSet.IntSet(IntSet)' */ @Test public void testIntSetIntSet() { IntSet ii = new IntSet(i1); Assert.assertEquals("copy", "(3,4,1,2)", ii.toString()); Assert.assertFalse("int, int ", ii.contains(0)); Assert.assertTrue("int, int ", ii.contains(1)); Assert.assertTrue("int, int ", ii.contains(2)); Assert.assertTrue("int, int ", ii.contains(3)); Assert.assertTrue("int, int ", ii.contains(4)); Assert.assertFalse("int, int ", ii.contains(5)); } /** * Test method for 'org.xmlcml.euclid.IntSet.IntSet(int[])' */ @Test public void testIntSetIntArray() { IntSetTest.assertEquals("int", new int[] { 0, 1, 2, 3 }, i2); } /** * Test method for * 'org.xmlcml.euclid.IntSet.IntSet.getSubcriptedIntSet(IntSet)' */ @Test public void testIntSetIntSetIntSet() { IntSet is0 = new IntSet(new int[] { 0, 1, 2, 3 }); IntSet is = i1.getSubscriptedIntSet(is0); IntSetTest.assertEquals("copy", new int[] { 3, 4, 1, 2 }, is); } /** * Test method for 'org.xmlcml.euclid.IntSet.isEqualTo(IntSet)' */ @Test public void testIsEqualTo() { Assert.assertTrue("isEqualsTo", i1.isEqualTo(i1)); Assert.assertFalse("isEqualsTo", i1.isEqualTo(i2)); } /** * Test method for 'org.xmlcml.euclid.IntSet.getElements()' */ @Test public void testGetElements() { String s = Int.testEquals((new int[] {}), i0.getElements()); if (s != null) { Assert.fail("getElements" + "; " + s); } s = Int.testEquals((new int[] { 3, 4, 1, 2 }), i1 .getElements()); if (s != null) { Assert.fail("getElements" + "; " + s); } } /** * Test method for 'org.xmlcml.euclid.IntSet.setMax(int)' */ @Test public void testSetMax() { i1.setMax(7); i1.addElement(6); IntSetTest.assertEquals("getElements", new int[] { 3, 4, 1, 2, 6 }, i1); i1.addElement(7); String s = Int.testEquals((new int[] { 3, 4, 1, 2, 6, 7 }), i1 .getElements()); if (s != null) { Assert.fail("getElements" + "; " + s); } try { i1.addElement(8); } catch (EuclidRuntimeException e) { Assert .assertEquals( "addElement", "org.xmlcml.euclid.EuclidRuntimeException: value (8)outside range (-2147483648...7)", S_EMPTY + e); } } /** * Test method for 'org.xmlcml.euclid.IntSet.setMin(int)' */ @Test public void testSetMin() { i1.setMin(-3); i1.addElement(-2); IntSetTest .assertEquals("getElements", new int[] { 3, 4, 1, 2, -2 }, i1); i1.addElement(-3); IntSetTest.assertEquals("getElements", new int[] { 3, 4, 1, 2, -2, -3 }, i1); try { i1.addElement(-4); } catch (EuclidRuntimeException e) { Assert .assertEquals( "addElement", "org.xmlcml.euclid.EuclidRuntimeException: value (-4)outside range (-3...2147483647)", S_EMPTY + e); } } /** * Test method for 'org.xmlcml.euclid.IntSet.size()' */ @Test public void testSize() { Assert.assertEquals("size", 4, i1.size()); } /** * Test method for 'org.xmlcml.euclid.IntSet.addElement(int)' */ @Test public void testAddElement() { i1.addElement(6); IntSetTest.assertEquals("addElement", new int[] { 3, 4, 1, 2, 6 }, i1); try { i1.addElement(4); } catch (EuclidRuntimeException e) { Assert .assertEquals( "addElement", "org.xmlcml.euclid.EuclidRuntimeException: value already in set: 4", S_EMPTY + e); } IntSetTest.assertEquals("addElement", new int[] { 3, 4, 1, 2, 6 }, i1); } /** * Test method for 'org.xmlcml.euclid.IntSet.contains(int)' */ @Test public void testContains() { Assert.assertTrue("contains", i1.contains(4)); Assert.assertFalse("contains", i1.contains(5)); } /** * Test method for 'org.xmlcml.euclid.IntSet.elementAt(int)' */ @Test public void testElementAt() { Assert.assertEquals("elementAt", 4, i1.elementAt(1)); } /** * Test method for 'org.xmlcml.euclid.IntSet.getIntArray()' */ @Test public void testGetIntArray() { String s = Int.testEquals((new int[] { 3, 4, 1, 2 }), i1 .getIntArray().getArray()); if (s != null) { Assert.fail("getIntArray" + "; " + s); } s = Int.testEquals((new int[] {}), i0.getIntArray() .getArray()); if (s != null) { Assert.fail("getIntArray" + "; " + s); } } /** * Test method for 'org.xmlcml.euclid.IntSet.sortAscending()' */ @Test public void testSortAscending() { i1.sortAscending(); IntSetTest.assertEquals("sort ascending", new int[] { 1, 2, 3, 4 }, i1); i0.sortAscending(); IntSetTest.assertEquals("sort ascending", new int[] {}, i0); } /** * Test method for 'org.xmlcml.euclid.IntSet.addSet(IntSet)' */ @Test public void testAddSet() { i1.addSet(new IntSet(new int[] { 5, 19, 8, 33 })); IntSetTest.assertEquals("addSet", new int[] { 3, 4, 1, 2, 5, 19, 8, 33 }, i1); IntSetTest.assertEquals("addSet", new int[] { 0, 1, 2, 3 }, i2); IntSet newIs = null; newIs = new IntSet(new int[] { 3, 4, 5, 6 }); try { i2.addSet(newIs); } catch (EuclidRuntimeException e) { Assert .assertEquals( "addSet", "org.xmlcml.euclid.EuclidRuntimeException: duplicate element 3", S_EMPTY + e); } IntSetTest.assertEquals("addSet", new int[] { 0, 1, 2, 3 }, i2); } /** * Test method for 'org.xmlcml.euclid.IntSet.intersectionWith(IntSet)' */ @Test public void testIntersectionWith() { IntSet is1 = null; IntSet is2 = null; is1 = new IntSet(new int[] { 1, 2, 3, 4, 5 }); is2 = new IntSet(new int[] { 4, 5, 6, 7, 3 }); IntSet is = is1.intersectionWith((is2)); IntSetTest.assertEquals("intersection", new int[] { 4, 5, 3 }, is); } /** * Test method for 'org.xmlcml.euclid.IntSet.notIn(IntSet)' */ @Test public void testNotIn() { IntSet is1 = null; IntSet is2 = null; is1 = new IntSet(new int[] { 1, 2, 3, 4, 5 }); is2 = new IntSet(new int[] { 4, 5, 6, 7, 3 }); IntSet is = is1.notIn(is2); IntSetTest.assertEquals("notIn", new int[] { 1, 2 }, is); } /** * Test method for 'org.xmlcml.euclid.IntSet.addRange(IntRange)' */ @Test public void testAddRange() { IntSet is1 = new IntSet(new int[] { 1, 2, 3, 4, 5 }); is1.addRange(new IntRange(-2, 0)); IntSetTest.assertEquals("addRange", new int[] { 1, 2, 3, 4, 5, -2, -1, 0 }, is1); } /** * Test method for 'org.xmlcml.euclid.IntSet.inverseMap()' */ @Test public void testInverseMap() { IntSet is1 = new IntSet(new int[] { 4, 0, 1, 3, 2 }); IntSet is = is1.inverseMap(); IntSetTest.assertEquals("inverse", new int[] { 1, 2, 4, 3, 0 }, is); } @Test public final void testGetSubscriptedIntSet() { IntSet is1 = new IntSet(new int[] { 4, 0, 1, 3, 2 }); IntSet is2 = new IntSet(new int[] { 14, 10, 11, 13, 12 }); IntSet is3 = is2.getSubscriptedIntSet(is1); IntSetTest.assertEquals("subscripts", new IntSet(new int[] { 12, 14, 10, 13, 11 }), is3); is1 = new IntSet(new int[] { 4, 0, 5, 3, 2 }); try { is3 = is2.getSubscriptedIntSet(is1); Assert.fail("Should throw exception"); } catch (EuclidRuntimeException e) { ;// expected } is1 = new IntSet(new int[] { 4, 0, 1, 3 }); is3 = is2.getSubscriptedIntSet(is1); IntSetTest.assertEquals("subscripts", new IntSet(new int[] { 12, 14, 10, 13 }), is3); } @Test public final void testGetPermutations() { List perm3 = IntSet.getPermutations(new Integer(3)); String s = Int.testEquals((new int[] { 3, 2, 1 }), perm3 .get(0)); if (s != null) { Assert.fail("permutation" + "; " + s); } s = Int.testEquals((new int[] { 2, 3, 1 }), perm3 .get(1)); if (s != null) { Assert.fail("permutation" + "; " + s); } s = Int.testEquals((new int[] { 1, 2, 3 }), perm3 .get(5)); if (s != null) { Assert.fail("permutation" + "; " + s); } } } euclid-euclid-2.9/src/test/java/org/xmlcml/euclid/test/IntSquareMatrixTest.java000066400000000000000000000256221461721410700277070ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid.test; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.xmlcml.euclid.Int; import org.xmlcml.euclid.IntArray; import org.xmlcml.euclid.IntMatrix; import org.xmlcml.euclid.IntSquareMatrix; /** * test IntSquareMatrix. * * @author pmr * */ public class IntSquareMatrixTest extends MatrixTest { static Logger LOG = Logger.getLogger(IntSquareMatrixTest.class); IntSquareMatrix m0; IntSquareMatrix m1; IntSquareMatrix m2; /** * setup. * * @throws Exception */ @Before public void setUp() throws Exception { super.setUp(); LOG.setLevel(Level.WARN); m0 = new IntSquareMatrix(); m1 = new IntSquareMatrix(3); m2 = new IntSquareMatrix(3, new int[] { 11, 12, 13, 21, 22, 23, 31, 32, 33, }); } /** * Test method for 'org.xmlcml.euclid.IntSquareMatrix.isOrthogonal()' */ @Test public void testIsOrthogonal() { Assert.assertFalse("isOrthogonal", m2.isOrthogonal()); IntSquareMatrix m = new IntSquareMatrix(3, new int[] { 0, 1, 0, -1, 0, 0, 0, 0, 1, }); Assert.assertTrue("isOrthogonal", m.isOrthogonal()); } /** * Test method for 'org.xmlcml.euclid.IntSquareMatrix.IntSquareMatrix()' */ @Test public void testIntSquareMatrix() { Assert.assertEquals("real square matrix", 0, m0.getRows()); Assert.assertEquals("real square matrix", 0, m0.getCols()); } /** * Test method for 'org.xmlcml.euclid.IntSquareMatrix.IntSquareMatrix(int)' */ @Test public void testIntSquareMatrixInt() { Assert.assertEquals("real square matrix", 3, m1.getRows()); Assert.assertEquals("real square matrix", 3, m1.getCols()); } /** * Test method for * 'org.xmlcml.euclid.IntSquareMatrix.outerProduct(IntArray)' */ @Test public void testOuterProduct() { IntArray ra = new IntArray(3, new int[] { 1, 2, 3 }); IntSquareMatrix rsm = IntSquareMatrix.outerProduct(ra); IntMatrix rm = new IntMatrix(3, 3, new int[] { 1, 2, 3, 2, 4, 6, 3, 6, 9, }); MatrixTest.assertEquals("outer product", rm, (IntMatrix) rsm); } /** * Test method for 'org.xmlcml.euclid.IntSquareMatrix.diagonal(IntArray)' */ @Test public void testDiagonal() { IntArray ra = new IntArray(3, new int[] { 1, 2, 3 }); IntMatrix rsm = IntSquareMatrix.diagonal(ra); IntMatrix rm = new IntMatrix(3, 3, new int[] { 1, 0, 0, 0, 2, 0, 0, 0, 3, }); MatrixTest.assertEquals("diagonal", rm, (IntMatrix) rsm); } /** * Test method for 'org.xmlcml.euclid.IntSquareMatrix.IntSquareMatrix(int, * int[])' */ @Test public void testIntSquareMatrixIntIntegerArray() { IntMatrix rm = new IntMatrix(3, 3, new int[] { 1, 2, 3, 2, 4, 6, 3, 6, 9, }); IntSquareMatrix rsm = new IntSquareMatrix(3, new int[] { 1, 2, 3, 2, 4, 6, 3, 6, 9, }); MatrixTest.assertEquals("int int[]", rm, (IntMatrix) rsm); } /** * Test method for 'org.xmlcml.euclid.IntSquareMatrix.IntSquareMatrix(int, * int)' */ @Test public void testIntSquareMatrixIntInteger() { IntMatrix rm = new IntMatrix(3, 3, 10); IntSquareMatrix rsm = new IntSquareMatrix(3, 10); MatrixTest.assertEquals("int int", rm, (IntMatrix) rsm); } /** * Test method for * 'org.xmlcml.euclid.IntSquareMatrix.IntSquareMatrix(IntMatrix, int, int, * int)' */ @Test public void testIntSquareMatrixIntMatrixIntIntInt() { IntMatrix rm = new IntMatrix(3, 4, new int[] { 11, 12, 13, 14, 21, 22, 23, 24, 31, 32, 33, 34 }); IntSquareMatrix rsm = new IntSquareMatrix(rm, 1, 1, 2); IntMatrix rm1 = new IntMatrix(2, 2, new int[] { 22, 23, 32, 33, }); MatrixTest.assertEquals("rsm int int int", rm1, (IntMatrix) rsm); } /** * Test method for * 'org.xmlcml.euclid.IntSquareMatrix.IntSquareMatrix(IntSquareMatrix)' */ @Test public void testIntSquareMatrixIntSquareMatrix() { IntSquareMatrix rsm = new IntSquareMatrix(m2); MatrixTest.assertEquals("copy", m2, rsm); } /** * Test method for * 'org.xmlcml.euclid.IntSquareMatrix.IntSquareMatrix(IntMatrix)' */ @Test public void testIntSquareMatrixIntMatrix() { IntMatrix rm = new IntMatrix(2, 2, new int[] { 22, 23, 32, 33, }); IntSquareMatrix rsm = new IntSquareMatrix(rm); MatrixTest.assertEquals("real matrix", rm, rsm); } /** * Test method for * 'org.xmlcml.euclid.IntSquareMatrix.IntSquareMatrix(int[][])' */ @Test public void testIntSquareMatrixIntegerArrayArray() { int[][] mat = new int[][] { new int[] { 11, 12, 13 }, new int[] { 21, 22, 23 }, new int[] { 31, 32, 33 }, }; IntSquareMatrix rsm = new IntSquareMatrix(mat); IntMatrix rm = new IntMatrix(3, 3, new int[] { 11, 12, 13, 21, 22, 23, 31, 32, 33, }); MatrixTest.assertEquals("real matrix", rm, rsm); } /** * Test method for * 'org.xmlcml.euclid.IntSquareMatrix.isEqualTo(IntSquareMatrix)' */ @Test public void testIsEqualToIntSquareMatrix() { IntSquareMatrix rsm = new IntSquareMatrix(m2); Assert.assertTrue("isEqualTo", m2.isEqualTo(rsm)); } /** * Test method for 'org.xmlcml.euclid.IntSquareMatrix.plus(IntSquareMatrix)' */ @Test public void testPlusIntSquareMatrix() { IntSquareMatrix rsm = m2.plus(m2); IntMatrix rm = new IntMatrix(3, 3, new int[] { 22, 24, 26, 42, 44, 46, 62, 64, 66, }); MatrixTest.assertEquals("real matrix", rm, rsm); } /** * Test method for * 'org.xmlcml.euclid.IntSquareMatrix.subtract(IntSquareMatrix)' */ @Test public void testSubtractIntSquareMatrix() { IntSquareMatrix rsm = m2.plus(m2); IntSquareMatrix rsm1 = m2.subtract(rsm); IntMatrix rm = new IntMatrix(3, 3, new int[] { -11, -12, -13, -21, -22, -23, -31, -32, -33, }); MatrixTest.assertEquals("real matrix", rm, rsm1); } /** * Test method for * 'org.xmlcml.euclid.IntSquareMatrix.multiply(IntSquareMatrix)' */ @Test public void testMultiplyIntSquareMatrix() { IntSquareMatrix rsm = m2.multiply(m2); IntMatrix rm = new IntMatrix(3, 3, new int[] { 776, 812, 848, 1406, 1472, 1538, 2036, 2132, 2228, }); MatrixTest.assertEquals("real matrix", rm, rsm); } /** * Test method for 'org.xmlcml.euclid.IntSquareMatrix.isUnit()' */ @Test public void testIsUnit() { IntSquareMatrix m = new IntSquareMatrix(3, new int[] { 1, 0, 0, 0, 1, 0, 0, 0, 1 }); Assert.assertTrue("unit", m.isUnit()); m = new IntSquareMatrix(3, new int[] { 1, 1, 1, 2, 3, 4, 3, 4, 7 }); Assert.assertFalse("unit", m.isUnit()); } /** * Test method for 'org.xmlcml.euclid.IntSquareMatrix.isSymmetric()' */ @Test public void testIsSymmetric() { IntSquareMatrix m = new IntSquareMatrix(3, new int[] { 1, 0, 3, 0, 1, 0, 3, 0, 1 }); Assert.assertTrue("unit", m.isSymmetric()); m = new IntSquareMatrix(3, new int[] { 1, 1, 1, 2, 3, 4, 3, 4, 7 }); Assert.assertFalse("unit", m.isSymmetric()); } /** * Test method for 'org.xmlcml.euclid.IntSquareMatrix.isUpperTriangular()' */ @Test public void testIsUpperTriangular() { IntSquareMatrix m = new IntSquareMatrix(3, new int[] { 0, 2, 3, 0, 0, 2, 0, 0, 0 }); Assert.assertTrue("upper triangular", m.isUpperTriangular()); m = new IntSquareMatrix(3, new int[] { 1, 2, 3, 0, 1, 2, 0, 0, 1 }); Assert.assertTrue("upper triangular", m.isUpperTriangular()); m = new IntSquareMatrix(3, new int[] { 1, 1, 1, 2, 3, 4, 3, 4, 7 }); Assert.assertFalse("upper triangular false", m.isUpperTriangular()); } /** * Test method for 'org.xmlcml.euclid.IntSquareMatrix.isLowerTriangular()' */ @Test public void testIsLowerTriangular() { IntSquareMatrix m = new IntSquareMatrix(3, new int[] { 0, 0, 0, 2, 0, 0, 3, 2, 0 }); Assert.assertTrue("lower triangular", m.isLowerTriangular()); m = new IntSquareMatrix(3, new int[] { 1, 0, 0, 2, 1, 0, 3, 2, 1 }); Assert.assertTrue("lower triangular", m.isLowerTriangular()); m = new IntSquareMatrix(3, new int[] { 1, 1, 1, 2, 3, 4, 3, 4, 7 }); Assert.assertFalse("lower triangular false", m.isLowerTriangular()); } /** * Test method for 'org.xmlcml.euclid.IntSquareMatrix.copyUpperToLower()' */ @Test public void testCopyUpperToLower() { IntSquareMatrix m = new IntSquareMatrix(3, new int[] { 6, 7, 8, 2, 5, 4, 3, 2, 9 }); m.copyUpperToLower(); IntSquareMatrix mm = new IntSquareMatrix(3, new int[] { 6, 7, 8, 7, 5, 4, 8, 4, 9 }); MatrixTest.assertEquals("copy upper", mm, m); } /** * Test method for 'org.xmlcml.euclid.IntSquareMatrix.copyLowerToUpper()' */ @Test public void testCopyLowerToUpper() { IntSquareMatrix m = new IntSquareMatrix(3, new int[] { 6, 7, 8, 2, 5, 4, 3, 2, 9 }); m.copyLowerToUpper(); IntSquareMatrix mm = new IntSquareMatrix(3, new int[] { 6, 2, 3, 2, 5, 2, 3, 2, 9 }); MatrixTest.assertEquals("copy upper", mm, m); } /** * Test method for 'org.xmlcml.euclid.IntSquareMatrix.lowerTriangle()' */ @Test public void testLowerTriangle() { IntSquareMatrix m = new IntSquareMatrix(3, new int[] { 6, 7, 8, 2, 5, 4, 3, 2, 9 }); IntArray ra = m.lowerTriangle(); String s = Int.testEquals((new int[] { 6, 2, 5, 3, 2, 9 }), ra.getArray()); if (s != null) { Assert.fail("lower triangle" + "; " + s); } } /** * Test method for 'org.xmlcml.euclid.IntSquareMatrix.transpose()' */ @Test public void testTranspose() { IntSquareMatrix m = new IntSquareMatrix(3, new int[] { 6, 7, 8, 2, 5, 4, 3, 1, 9 }); m.transpose(); IntSquareMatrix mm = new IntSquareMatrix(3, new int[] { 6, 2, 3, 7, 5, 1, 8, 4, 9 }); MatrixTest.assertEquals("transpose", mm, m); } } euclid-euclid-2.9/src/test/java/org/xmlcml/euclid/test/IntTest.java000066400000000000000000000025751461721410700253430ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid.test; import org.junit.Assert; import org.junit.Test; import org.xmlcml.euclid.Int; /** * test Int. * * @author pmr * */ public class IntTest { /** * Test method for 'org.xmlcml.euclid.Int.zeroArray(int, int[])' */ @Test public void testZeroArray() { int[] ii = new int[5]; Int.zeroArray(5, ii); String s = Int.testEquals((new int[] { 0, 0, 0, 0, 0 }), ii); if (s != null) { Assert.fail("int[] " + "; " + s); } } /** * Test method for 'org.xmlcml.euclid.Int.initArray(int, int[], int)' */ @Test public void testInitArray() { int[] ii = new int[5]; Int.initArray(5, ii, 3); String s = Int.testEquals((new int[] { 3, 3, 3, 3, 3 }), ii); if (s != null) { Assert.fail("int[] " + "; " + s); } } } euclid-euclid-2.9/src/test/java/org/xmlcml/euclid/test/JodaDateTest.java000066400000000000000000000045441461721410700262620ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid.test; import java.util.Date; import junit.framework.Assert; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import org.junit.Ignore; import org.junit.Test; import org.xmlcml.euclid.JodaDate; public class JodaDateTest { @Test public void testFormatDate() { DateTime datetime = new DateTime(1288135627973L); datetime = datetime.withZone(DateTimeZone.forID("UTC")); String dateTimeString = JodaDate.formatIsoDate(datetime); Assert.assertEquals("date string", "2010-10-26T23:27:07.973Z", dateTimeString); } @Test public void testParseDate() { DateTime dateTime = JodaDate.parseDate("2010-10-27T00:27:07+01:00"); Assert.assertEquals("date millis", 1288135627000L, dateTime.getMillis()); } @Test public void testParseDate1() { DateTime dateTime = JodaDate.parseDate("25/12/1984", "dd/mm/yyyy"); Assert.assertEquals("date format", 443837520000L, dateTime.getMillis()); } @SuppressWarnings("deprecation") @Test @Ignore // This test seems to be time zone dependent - failed in Australia// public void testParseJavaDate() { Date date = new Date(2001 - 1900, 12, 25, 10, 20, 30); Assert.assertNotNull(date); DateTime dateTime = JodaDate.parseJavaDate(date); Assert.assertNotNull(dateTime); Assert.assertEquals("date to datetime", 1011954030000L, dateTime.getMillis()); } @Test public void testParseJodaDate() { DateTime dateTime = new DateTime(1288135627000L).withZone(DateTimeZone .forID("UTC")); Date date = JodaDate.parseJodaDate(dateTime); // I can't hack this at present // Assert.assertEquals("datetime to date", // "Sat Nov 27 22:27:07 GMT 2010", date.toString()); Assert.assertTrue("datetime to date", date.toString().indexOf("Nov 27") != -1); } } euclid-euclid-2.9/src/test/java/org/xmlcml/euclid/test/Line2Test.java000066400000000000000000000326321461721410700255570ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid.test; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.xmlcml.euclid.Angle; import org.xmlcml.euclid.Angle.Units; import org.xmlcml.euclid.Line2; import org.xmlcml.euclid.Real; import org.xmlcml.euclid.Real2; import org.xmlcml.euclid.Vector2; /** * test for Line2 * * @author pm286 * */ public class Line2Test { static double sqrt2 = Math.sqrt(2); static double sqrt5 = Math.sqrt(5); Real2 p00 = new Real2(0, 0); Real2 p02 = new Real2(0, 2); Real2 p20 = new Real2(2, 0); Real2 p12 = new Real2(1, 2); Real2 p21 = new Real2(2, 1); Real2 p11 = new Real2(1, 1); Real2 p0100 = new Real2(0, 100); Real2 p1100 = new Real2(1, 100); Vector2 v12 = new Vector2(1, 2); Line2 l0002; Line2 l0200; Line2 l0020; Line2 l1221; Line2 l1112; Line2 l000100; Line2 l001100; Line2 l110000; /** * @throws Exception * */ @Before public void setUp() throws Exception { l0002 = new Line2(p00, p02); l0200 = new Line2(p02, p00); l0020 = new Line2(p00, p20); l1221 = new Line2(p12, p21); l1112 = new Line2(p11, v12); l000100 = new Line2(p00, p0100); l001100 = new Line2(p00, p1100); l110000 = new Line2(p1100, p00); } @Test public void testParallelAndAntiparallel() { Assert.assertTrue("Line and itself", l0002.isParallelTo(l0002, new Angle(1, Units.DEGREES))); Assert.assertFalse("Line and itself", l0002.isAntiParallelTo(l0002, new Angle(1, Units.DEGREES))); Assert.assertTrue("Line and reversed version", l0002.isAntiParallelTo(l0200, new Angle(1, Units.DEGREES))); Assert.assertFalse("Line and reversed version", l0002.isParallelTo(l0200, new Angle(1, Units.DEGREES))); Assert.assertTrue("2 parallel lines", l0002.isParallelTo(l000100, new Angle(1, Units.DEGREES))); Assert.assertFalse("2 parallel lines", l0002.isAntiParallelTo(l000100, new Angle(1, Units.DEGREES))); Assert.assertTrue("2 almost parallel lines", l000100.isParallelTo(l001100, new Angle(1, Units.DEGREES))); Assert.assertFalse("2 almost parallel lines", l000100.isAntiParallelTo(l001100, new Angle(1, Units.DEGREES))); Assert.assertTrue("2 antiparallel lines", l0200.isAntiParallelTo(l000100, new Angle(1, Units.DEGREES))); Assert.assertFalse("2 antiparallel lines", l0200.isParallelTo(l000100, new Angle(1, Units.DEGREES))); Assert.assertTrue("2 almost antiparallel lines", l000100.isAntiParallelTo(l110000, new Angle(1, Units.DEGREES))); Assert.assertFalse("2 almost antiparallel lines", l000100.isParallelTo(l110000, new Angle(1, Units.DEGREES))); Assert.assertFalse("2 right-angle lines", l0002.isParallelTo(l0020, new Angle(1, Units.DEGREES))); Assert.assertFalse("2 right-angle lines", l0002.isAntiParallelTo(l0020, new Angle(1, Units.DEGREES))); Assert.assertTrue("Line and reversed version", l0200.isAntiParallelTo(l0002, new Angle(1, Units.DEGREES))); Assert.assertFalse("Line and reversed version", l0200.isParallelTo(l0002, new Angle(1, Units.DEGREES))); Assert.assertTrue("2 parallel lines", l000100.isParallelTo(l0002, new Angle(1, Units.DEGREES))); Assert.assertFalse("2 parallel lines", l000100.isAntiParallelTo(l0002, new Angle(1, Units.DEGREES))); Assert.assertTrue("2 almost parallel lines", l001100.isParallelTo(l000100, new Angle(1, Units.DEGREES))); Assert.assertFalse("2 almost parallel lines", l001100.isAntiParallelTo(l000100, new Angle(1, Units.DEGREES))); Assert.assertTrue("2 antiparallel lines", l000100.isAntiParallelTo(l0200, new Angle(1, Units.DEGREES))); Assert.assertFalse("2 antiparallel lines", l000100.isParallelTo(l0200, new Angle(1, Units.DEGREES))); Assert.assertTrue("2 almost antiparallel lines", l110000.isAntiParallelTo(l000100, new Angle(1, Units.DEGREES))); Assert.assertFalse("2 almost antiparallel lines", l110000.isParallelTo(l000100, new Angle(1, Units.DEGREES))); Assert.assertFalse("2 right-angle lines", l0020.isParallelTo(l0002, new Angle(1, Units.DEGREES))); Assert.assertFalse("2 right-angle lines", l0020.isAntiParallelTo(l0002, new Angle(1, Units.DEGREES))); } /** dewisott */ @Test public final void testLine2Real2Real2() { Assert.assertNotNull("l0002", l0002); Vector2 v = l0002.getVector(); Real2Test.assertEquals("vector", new Vector2(0., 2.), v, Real.EPS); Real2 p = l0002.getFrom(); Real2Test.assertEquals("from", new Real2(0., 0.), p, Real.EPS); p = l0002.getTo(); Real2Test.assertEquals("to", new Real2(0., 2.), p, Real.EPS); Assert.assertNotNull("l1221", l1221); v = l1221.getVector(); Real2Test.assertEquals("vector", new Vector2(1., -1.), v, Real.EPS); p = l1221.getFrom(); Real2Test.assertEquals("from", new Real2(1., 2.), p, Real.EPS); p = l1221.getTo(); Real2Test.assertEquals("to", new Real2(2., 1.), p, Real.EPS); } /** dewisott */ @Test public final void testLine2Real2Vector2() { Assert.assertNotNull("l1112", l1112); Vector2 v = l1112.getVector(); Real2Test.assertEquals("vector", new Vector2(1., 2.), v, Real.EPS); Real2 p = l1112.getFrom(); Real2Test.assertEquals("from", new Real2(1., 1.), p, Real.EPS); p = l1112.getTo(); Real2Test.assertEquals("to", new Real2(2., 3.), p, Real.EPS); } /** dewisott */ @Test public final void testGetSlope() { double slope = l0002.getSlope(); Assert.assertEquals("slope", Double.POSITIVE_INFINITY, slope, Real.EPS); slope = l0200.getSlope(); Assert.assertEquals("slope", Double.NEGATIVE_INFINITY, slope, Real.EPS); slope = l0020.getSlope(); Assert.assertEquals("slope", 0.0, slope, Real.EPS); slope = l1221.getSlope(); Assert.assertEquals("slope", -1.0, slope, Real.EPS); slope = l1112.getSlope(); Assert.assertEquals("slope", 2.0, slope, Real.EPS); } /** dewisott */ @Test public final void testGetYIntercept() { double yint = l1221.getYIntercept(); Assert.assertEquals("yint", 3.0, yint, Real.EPS); yint = l1112.getYIntercept(); Assert.assertEquals("yint", -1.0, yint, Real.EPS); Line2 ll = new Line2(new Real2(1., 1.), new Vector2(0., 1.)); yint = ll.getYIntercept(); Assert.assertEquals("yint", Double.NaN, yint, Real.EPS); ll = new Line2(new Real2(1., 1.), new Vector2(1., 0.)); yint = ll.getYIntercept(); Assert.assertEquals("yint", 1.0, yint, Real.EPS); } /** dewisott */ @Test public final void testGetXIntercept() { double xint = l1221.getXIntercept(); Assert.assertEquals("xint", 3.0, xint, Real.EPS); xint = l1112.getXIntercept(); Assert.assertEquals("xint", 0.5, xint, Real.EPS); Line2 ll = new Line2(new Real2(1., 1.), new Vector2(0., 1.)); xint = ll.getXIntercept(); Assert.assertEquals("xint", 1.0, xint, Real.EPS); ll = new Line2(new Real2(1., 1.), new Vector2(1., 0.)); xint = ll.getXIntercept(); Assert.assertEquals("xint", Double.NaN, xint, Real.EPS); } /** dewisott */ @Test public final void testGetIntersection() { Real2 p = l0002.getIntersection(l0020); Real2Test.assertEquals("intersect", new Real2(0., 0.), p, Real.EPS); p = l0002.getIntersection(l1112); Real2Test.assertEquals("intersect", new Real2(0.0, -1.0), p, Real.EPS); p = l1221.getIntersection(l1112); Real2Test.assertEquals("intersect", new Real2(4. / 3., 5. / 3.), p, Real.EPS); } /** dewisott */ @Test public final void testGetUnitVector() { Vector2 v = l1112.getUnitVector(); Real2Test.assertEquals("unitv", new Real2(1. / sqrt5, 2. / sqrt5), v, Real.EPS); } /** dewisott */ @Test public final void testGetDistanceFromPoint() { double d = l1112.getDistanceFromPoint(new Real2(0., 0.)); Assert.assertEquals("distance", 1. / sqrt5, d, Real.EPS); } /** dewisott */ @Test public final void testGetNearestPointOnLine() { Real2 p = l1112.getNearestPointOnLine(new Real2(0., 0.)); Real2Test.assertEquals("point", new Real2(0.4, -0.2), p, 0.0000001); } /** dewisott */ @Test public final void testGetLambda() { Real2 p = new Real2(0.4, -0.2); double lambda = l1112.getLambda(p); Assert.assertEquals("lambda", -0.6, lambda, Real.EPS); p = new Real2(0.0, -1.0); lambda = l1112.getLambda(p); Assert.assertEquals("lambda", -1.0, lambda, Real.EPS); lambda = l1112.getLambda(l1112.getTo()); Assert.assertEquals("lambda", 1.0, lambda, Real.EPS); } /** dewisott */ @Test public final void testGetLength() { Assert.assertEquals("length", sqrt2, l1221.getLength(), Real.EPS); } /** dewisott */ @Test public final void testGetMidPoint() { Real2Test.assertEquals("mid point", new Real2(1.5, 2), l1112 .getMidPoint(), Real.EPS); } @Test public void testCreatePointOnLine() { Line2 line = new Line2(new Real2(0.,0.), new Vector2(1., 0.)); Real2 newPoint = line.createPointOnLine(3.0); Real2Test.assertEquals("extend", new Real2(3.0, 0.), newPoint, 0.01); } @Test public void testCreatePointOnLine1() { Line2 line = new Line2(new Real2(1., 2.), new Real2(4., -2.)); Real2 newPoint = line.createPointOnLine(10.0); Real2Test.assertEquals("extend", new Real2(7., -6.), newPoint, 0.01); } @Test public void testGetSerial() { double eps = 0.01; Line2 line = new Line2(new Real2(1., 1.), new Real2(2., 2.)); Assert.assertEquals("from", 0, line.getSerial(new Real2(1.001, 1.001), eps)); Assert.assertEquals("to", 1, line.getSerial(new Real2(2.001, 2.001), eps)); Assert.assertEquals("none", -1, line.getSerial(new Real2(3.001, 3.001), eps)); } @Test public void testGetSerial1() { double eps = 0.01; Line2 line = new Line2(new Real2(1., 1.), new Vector2(1., 1.)); Real2Test.assertEquals("check to", new Real2(2.001, 2.002), line.getXY(1), eps); Assert.assertEquals("from", 0, line.getSerial(new Real2(1.001, 1.001), eps)); Assert.assertEquals("to", 1, line.getSerial(new Real2(2.001, 2.001), eps)); Assert.assertEquals("to", -1, line.getSerial(new Real2(3.001, 3.001), eps)); } @Test public void testCreatePointOnLineIndex() { Line2 line = new Line2(new Real2(0.0, 0.0), new Real2(3., 4.)); double dist = 10; Real2 point0 = line.createPointOnLine(dist, 0); Real2Test.assertEquals("point0 ", new Real2(6., 8.), point0, 0.01); Real2 point1 = line.createPointOnLine(dist, 1); Real2Test.assertEquals("point1 ", new Real2(-3., -4.), point1, 0.01); } @Test public void testParallelLinesAndDistances() { Angle epsAngle = new Angle(0.015, Angle.Units.RADIANS); // these lines are not quite parallel - quite a large deviation Line2 line0 = new Line2(new Real2(172.14,512.58), new Real2(172.14,504.3)); //v((0.0,-8.28)) Line2 line1 = new Line2(new Real2(172.5,504.06), new Real2 (172.62,512.58)); //v((0.12,8.52)) Double d0 = line0.getUnsignedDistanceFromPoint(new Real2(0., 0.)); Assert.assertEquals(172.14, d0, 0.01); Double d1 = line1.getUnsignedDistanceFromPoint(new Real2(0., 0.)); Assert.assertEquals(165.38, d1, 0.01); Assert.assertTrue(line0.isAntiParallelTo(line1, epsAngle)); Assert.assertFalse(line0.isParallelTo(line1, epsAngle)); Assert.assertTrue(line0.isParallelOrAntiParallelTo(line1, epsAngle)); Double dist01 = line0.calculateUnsignedDistanceBetweenLines(line1, epsAngle); Double dist10 = line1.calculateUnsignedDistanceBetweenLines(line0, epsAngle); // note how distances are not equal - the lines diverge slightly Assert.assertEquals(0.48, dist01, 0.001); Assert.assertEquals(0.36, dist10, 0.001); // smaller tolerance means lines are not parallel Assert.assertFalse(line0.isAntiParallelTo(line1, epsAngle.multiplyBy(0.1))); Assert.assertFalse(line0.isParallelTo(line1, epsAngle.multiplyBy(0.1))); Assert.assertFalse(line0.isParallelOrAntiParallelTo(line1, epsAngle.multiplyBy(0.1))); } @Test public void testIsPerpendicularTo() { Angle angleEps = new Angle(0.01, Units.RADIANS); Line2 line10 = new Line2(new Real2(0., 0.), new Real2(1.0, 0.0)); Line2 line01 = new Line2(new Real2(0., 0.), new Real2(0.0, 0.1)); Assert.assertTrue("perp", line01.isPerpendicularTo(line10, angleEps)); Line2 line10d = new Line2(new Real2(0., 0.), new Real2(1.0, 0.001)); Line2 line01d = new Line2(new Real2(0., 0.), new Real2(0.001, 1.0)); Assert.assertTrue("perp", line01d.isPerpendicularTo(line10d, angleEps)); // lower the tolerance Assert.assertFalse("perp", line01d.isPerpendicularTo(line10d, angleEps.multiplyBy(0.01))); } @Test public void testIsVerticalHorizontal() { Angle angleEps = new Angle(0.01, Units.RADIANS); Line2 line10 = new Line2(new Real2(0., 0.), new Real2(1.0, 0.0)); Line2 line01 = new Line2(new Real2(0., 0.), new Real2(0.0, 0.1)); Assert.assertTrue("hor", line10.isHorizontal(angleEps)); Assert.assertTrue("vert", line01.isVertical(angleEps)); Assert.assertFalse("hor", line01.isHorizontal(angleEps)); Assert.assertFalse("vert", line10.isVertical(angleEps)); Line2 line10d = new Line2(new Real2(0., 0.), new Real2(1.0, 0.001)); Line2 line01d = new Line2(new Real2(0., 0.), new Real2(0.001, 1.0)); Assert.assertTrue("hor", line10d.isHorizontal(angleEps)); Assert.assertTrue("vert", line01d.isVertical(angleEps)); // lower the tolerance Assert.assertFalse("hor", line10d.isHorizontal(angleEps.multiplyBy(0.01))); Assert.assertFalse("vert", line01d.isVertical(angleEps.multiplyBy(0.01))); } } euclid-euclid-2.9/src/test/java/org/xmlcml/euclid/test/Line3Test.java000066400000000000000000000161331461721410700255560ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid.test; import static org.xmlcml.euclid.EC.EPS; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.xmlcml.euclid.Angle; import org.xmlcml.euclid.EC; import org.xmlcml.euclid.Line3; import org.xmlcml.euclid.Point3; import org.xmlcml.euclid.Vector3; /** * test Line3 * * @author pmr * */ public class Line3Test extends GeomTest { /** * set up. * */ /** * setup. * * @throws Exception */ @Before public void setUp() throws Exception { super.setUp(); } /** * equality test. true if both args not null and equal within epsilon * * @param msg * message * @param test * @param expected * @param epsilon */ public static void assertEquals(String msg, Line3 test, Line3 expected, double epsilon) { Assert.assertNotNull("test should not be null (" + msg + EC.S_RBRAK, test); Assert.assertNotNull("ref should not be null (" + msg + EC.S_RBRAK, expected); Point3Test.assertEquals(msg, test.getPoint(), expected.getPoint(), epsilon); Vector3Test.assertEquals(msg, test.getVector(), expected.getVector(), epsilon); } /** * equality test. true if both args not null and equal within epsilon * * @param msg * message * @param testPoint * @param testVector * @param expected * @param epsilon */ public static void assertEquals(String msg, Point3 testPoint, Vector3 testVector, Line3 expected, double epsilon) { Assert.assertNotNull("testPoint should not be null (" + msg + EC.S_RBRAK, testPoint); Assert.assertNotNull("testVector should not be null (" + msg + EC.S_RBRAK, testVector); Assert.assertNotNull("expected should not be null (" + msg + EC.S_RBRAK, expected); Point3Test.assertEquals(msg, testPoint, expected.getPoint(), epsilon); Vector3Test .assertEquals(msg, testVector, expected.getVector(), epsilon); } /** * Test method for 'org.xmlcml.euclid.Line3.Line3()' */ @Test public void testLine3() { Assert.assertNotNull("line", l0); } /** * Test method for 'org.xmlcml.euclid.Line3.Line3(Point3, Vector3)' */ @Test public void testLine3Point3Vector3() { Assert.assertNotNull("line", l123456); Point3 p = l123456.getPoint(); Vector3 v = l123456.getVector(); Point3Test.assertEquals("line", new double[] { 4., 5., 6. }, p, EPS); Vector3Test.assertEquals("line", new double[] { 1. / s14, 2. / s14, 3. / s14 }, v, EPS); } /** * Test method for 'org.xmlcml.euclid.Line3.Line3(Point3, Point3)' */ @Test public void testLine3Point3Point3() { Line3 l = new Line3(p100, p001); Assert.assertNotNull("line", l); Point3 p = l.getPoint(); Vector3 v = l.getVector(); Point3Test.assertEquals("line", new double[] { 1., 0., 0. }, p, EPS); Vector3Test.assertEquals("line", new double[] { -1. / s2, 0., 1. / s2 }, v, EPS); } /** * Test method for 'org.xmlcml.euclid.Line3.Line3(Line3)' */ @Test public void testLine3Line3() { Line3 l = new Line3(l123456); Assert.assertNotNull("line", l); Point3 p = l.getPoint(); Vector3 v = l.getVector(); Point3Test.assertEquals("line", new double[] { 4., 5., 6. }, p, EPS); Vector3Test.assertEquals("line", new double[] { 1. / s14, 2. / s14, 3. / s14 }, v, EPS); } /** * Test method for 'org.xmlcml.euclid.Line3.isEqualTo(Line3)' */ @Test public void testIsEqualTo() { Line3 l = new Line3(l123456); Assert.assertTrue("isEqualTo", l.isEqualTo(l123456)); } /** * Test method for 'org.xmlcml.euclid.Line3.negative()' */ @Test public void testNegative() { Line3 l = new Line3(l123456); l = l.negative(); Vector3Test.assertEquals("negative", new double[] { -1. / s14, -2. / s14, -3. / s14 }, l.getVector(), EPS); Point3Test.assertEquals("negative", new double[] { 4., 5., 6. }, l .getPoint(), EPS); } /** * Test method for 'org.xmlcml.euclid.Line3.transform(Transform3)' */ @Test public void testTransform() { Line3 l = l123456.transform(tr1); Vector3Test.assertEquals("transform", new double[] { 1. / s14, -2. / s14, 3. / s14 }, l.getVector(), EPS); Point3Test.assertEquals("transform", new double[] { 4., -5., 6. }, l .getPoint(), EPS); } /** * Test method for 'org.xmlcml.euclid.Line3.isParallelTo(Line3)' */ @Test public void testIsParallelTo() { Line3 l = new Line3(l123456); Assert.assertTrue("isParallel", l.isParallelTo(l123456)); l = l.negative(); Assert.assertFalse("isParallel", l.isParallelTo(l123456)); } /** * Test method for 'org.xmlcml.euclid.Line3.isAntiparallelTo(Line3)' */ @Test public void testIsAntiparallelTo() { Line3 l = new Line3(l123456); Assert.assertFalse("isAntiParallel", l.isAntiparallelTo(l123456)); l = l.negative(); Assert.assertTrue("isAntiParallel", l.isAntiparallelTo(l123456)); } /** * Test method for 'org.xmlcml.euclid.Line3.containsPoint(Point3)' */ @Test public void testContainsPoint() { Assert.assertTrue("contains", l123456.containsPoint(new Point3(4., 5., 6.))); Assert.assertTrue("contains", l123456.containsPoint(new Point3(3., 3., 3.))); Assert.assertTrue("contains", l123456.containsPoint(new Point3(2., 1., 0.))); Assert.assertFalse("contains", l123456.containsPoint(new Point3(3., 5., 6.))); } /** * Test method for 'org.xmlcml.euclid.Line3.getClosestPointTo(Point3)' */ @Test public void testGetClosestPointTo() { Point3 p = l123456.getClosestPointTo(p100); Assert.assertTrue("contains", l123456.containsPoint(p)); Assert.assertFalse("contains", l123456.containsPoint(p100)); } /** * Test method for 'org.xmlcml.euclid.Line3.getDistanceFromPoint(Point3)' */ @Test public void testGetDistanceFromPoint() { double d = l123456.getDistanceFromPoint(p100); Assert.assertEquals("distance from", 1.1649647450214353, d, EPS); Point3 p = l123456.getClosestPointTo(p100); double dd = p.getDistanceFromPoint(p100); Assert.assertEquals("distance from", 1.1649647450214353, dd, EPS); Vector3 v = p.subtract(p100); Angle a = v.getAngleMadeWith(l123456.getVector()); Assert.assertNotNull("angle ", a); Assert.assertEquals("check angle", Math.PI / 2., a.getRadian(), EPS); } /** * Test method for 'org.xmlcml.euclid.Line3.getIntersectionWith(Plane3)' */ @Test public void testGetIntersectionWith() { Point3 p = l123456.getIntersectionWith(pl1111); Point3Test.assertEquals("intersection", new double[] { 1.788675134594813, 0.5773502691896262, -0.6339745962155607 }, p, EPS); Assert.assertTrue("contains", l123456.containsPoint(p)); Assert.assertTrue("contains", pl1111.containsPoint(p)); } } euclid-euclid-2.9/src/test/java/org/xmlcml/euclid/test/MatrixTest.java000066400000000000000000000101251461721410700260430ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid.test; import static org.xmlcml.euclid.EC.EPS; import static org.xmlcml.euclid.EC.S_EMPTY; import static org.xmlcml.euclid.EC.S_RBRAK; import static org.xmlcml.euclid.test.EuclidTestBase.getAssertFormat; import junit.framework.Assert; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.junit.Before; import org.junit.Test; import org.xmlcml.euclid.Int; import org.xmlcml.euclid.IntMatrix; import org.xmlcml.euclid.Real; import org.xmlcml.euclid.RealMatrix; /** * test Matrix stuff. * * @author pmr * */ public class MatrixTest { private final static Logger LOG = Logger.getLogger(MatrixTest.class); /** * setup. * * @throws Exception */ @Before public void setUp() throws Exception { LOG.setLevel(Level.WARN); } /** * Asserts equality of RealMatrix. * * checks for non-null, then equality of length, then individual elements * * @param message * @param a * expected array * @param b * actual array * @param eps * tolerance for agreement */ public static void assertEquals(String message, RealMatrix a, RealMatrix b, double eps) { if (a == null || b == null) { Assert.fail(getAssertFormat(message, "double[]", "null")); } int aRows = a.getRows(); int bRows = b.getRows(); int aCols = a.getCols(); int bCols = b.getCols(); if (aRows != bRows) { Assert.fail(getAssertFormat(message + "; unequal rows in matrices", S_EMPTY + aRows, S_EMPTY + bRows)); } if (aCols != bCols) { Assert.fail(getAssertFormat(message + "; unequal cols in matrices", S_EMPTY + aCols, S_EMPTY + bCols)); } double[][] aMat = a.getMatrix(); double[][] bMat = b.getMatrix(); for (int i = 0; i < aRows; i++) { for (int j = 0; j < aCols; j++) { if (!Real.isEqual(aMat[i][j], bMat[i][j], eps)) { Assert.fail(getAssertFormat(message + "; unequal element (" + i + ", " + j + S_RBRAK, S_EMPTY + aMat[i][j], S_EMPTY + bMat[i][j])); } } } } /** * Asserts equality of RealMatrix. * * checks for non-null, then equality of length, then individual elements * * @param message * @param a * expected array * @param b * actual array */ public static void assertEquals(String message, IntMatrix a, IntMatrix b) { if (a == null || b == null) { Assert.fail(getAssertFormat(message, "IntMatrix", "null")); } int aRows = a.getRows(); int bRows = b.getRows(); int aCols = a.getCols(); int bCols = b.getCols(); if (aRows != bRows) { Assert.fail(getAssertFormat(message + "; unequal rows in matrices", S_EMPTY + aRows, S_EMPTY + bRows)); } if (aCols != bCols) { Assert.fail(getAssertFormat(message + "; unequal cols in matrices", S_EMPTY + aCols, S_EMPTY + bCols)); } String s = Int.testEquals(a.getMatrix(), b.getMatrix()); if (s != null) { Assert.fail(message + "; " + s); } } /** test */ @Test public void testRealMatrix() { RealMatrix a = new RealMatrix(2, 3, new double[] { 11.0, 12.0, 13.0, 21.0, 22.0, 23.0 }); RealMatrix b = new RealMatrix(2, 3, new double[] { 11.0, 12.0, 13.0, 21.0, 28.0, 23.0 }); MatrixTest.assertEquals("MatrixTest", a, a, EPS); Assert.assertNotNull(b); } /** test */ @Test public void testIntMatrix() { IntMatrix a = new IntMatrix(2, 3, new int[] { 11, 12, 13, 21, 22, 23 }); IntMatrix b = new IntMatrix(2, 3, new int[] { 11, 12, 13, 21, 28, 23 }); Assert.assertNotNull(b); Assert.assertEquals("MatrixTest", a, a); } } euclid-euclid-2.9/src/test/java/org/xmlcml/euclid/test/ParsedSymopTest.java000077500000000000000000000176571461721410700270710ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid.test; import junit.framework.Assert; import org.apache.log4j.Logger; import org.junit.Test; import org.xmlcml.euclid.ParsedSymop; import org.xmlcml.euclid.Transform3; public class ParsedSymopTest { private final static Logger LOG = Logger.getLogger(ParsedSymopTest.class); @Test public void parseXYZ() { ParsedSymop symop = ParsedSymop.createSymop("x"); Assert.assertEquals("xyz", "x", symop.getXyz()); symop = ParsedSymop.createSymop("X"); Assert.assertEquals("xyz", "x", symop.getXyz()); symop = ParsedSymop.createSymop("y"); Assert.assertEquals("xyz", "y", symop.getXyz()); symop = ParsedSymop.createSymop("+z"); Assert.assertEquals("xyz", "z", symop.getXyz()); symop = ParsedSymop.createSymop("-z"); Assert.assertEquals("xyz", "-z", symop.getXyz()); Assert.assertEquals("xyz", "", symop.getXyz1()); Assert.assertNull("xyz", symop.getNumber()); try { symop = ParsedSymop.createSymop("a"); Assert.fail(); } catch (Exception e) { } } @Test public void parseXYZXYZ() { ParsedSymop symop = ParsedSymop.createSymop("+x-y"); Assert.assertEquals("xyz", "x", symop.getXyz()); Assert.assertEquals("xyz", "-y", symop.getXyz1()); symop = ParsedSymop.createSymop("-x-y"); Assert.assertEquals("xyz", "-x", symop.getXyz()); Assert.assertEquals("xyz", "-y", symop.getXyz1()); symop = ParsedSymop.createSymop("-x+y"); Assert.assertEquals("xyz", "-x", symop.getXyz()); Assert.assertEquals("xyz", "y", symop.getXyz1()); try { symop = ParsedSymop.createSymop("xy"); } catch (Exception e) { LOG.debug("'xy' is not picked up as error, sorry"); } } @Test public void parseNUMB_XYZ() { ParsedSymop symop = ParsedSymop.createSymop("0.5+x"); Assert.assertEquals("xyz", "x", symop.getXyz()); Assert.assertEquals("xyz", "", symop.getXyz1()); Assert.assertEquals("xyz", 0.5, symop.getNumber(), 0.001); symop = ParsedSymop.createSymop(".25-y"); Assert.assertEquals("xyz", "-y", symop.getXyz()); Assert.assertEquals("xyz", "", symop.getXyz1()); Assert.assertEquals("xyz", 0.25, symop.getNumber(), 0.001); symop = ParsedSymop.createSymop("-0.75+z"); Assert.assertEquals("xyz", "z", symop.getXyz()); Assert.assertEquals("xyz", "", symop.getXyz1()); Assert.assertEquals("xyz", -0.75, symop.getNumber(), 0.001); } @Test public void parseNUMB_XYZXYZ() { ParsedSymop symop = ParsedSymop.createSymop("0.5+x+y"); Assert.assertEquals("xyz", "x", symop.getXyz()); Assert.assertEquals("xyz", "y", symop.getXyz1()); Assert.assertEquals("xyz", 0.5, symop.getNumber(), 0.001); symop = ParsedSymop.createSymop(".25-y-x"); Assert.assertEquals("xyz", "-y", symop.getXyz()); Assert.assertEquals("xyz", "-x", symop.getXyz1()); Assert.assertEquals("xyz", 0.25, symop.getNumber(), 0.001); symop = ParsedSymop.createSymop("-0.75+z-x"); Assert.assertEquals("xyz", "z", symop.getXyz()); Assert.assertEquals("xyz", "-x", symop.getXyz1()); Assert.assertEquals("xyz", -0.75, symop.getNumber(), 0.001); } @Test public void parseFRACT_XYZ() { ParsedSymop symop = ParsedSymop.createSymop("1/2+x"); Assert.assertEquals("xyz", "x", symop.getXyz()); Assert.assertEquals("xyz", "", symop.getXyz1()); Assert.assertEquals("xyz", 0.5, symop.getNumber(), 0.001); symop = ParsedSymop.createSymop("1/4-y"); Assert.assertEquals("xyz", "-y", symop.getXyz()); Assert.assertEquals("xyz", "", symop.getXyz1()); Assert.assertEquals("xyz", 0.25, symop.getNumber(), 0.001); symop = ParsedSymop.createSymop("-3/4+z"); Assert.assertEquals("xyz", "z", symop.getXyz()); Assert.assertEquals("xyz", "", symop.getXyz1()); Assert.assertEquals("xyz", -0.75, symop.getNumber(), 0.001); } @Test public void parseFRACT_XYZXYZ() { ParsedSymop symop = ParsedSymop.createSymop("1/2+x+y"); Assert.assertEquals("xyz", "x", symop.getXyz()); Assert.assertEquals("xyz", "y", symop.getXyz1()); Assert.assertEquals("xyz", 0.5, symop.getNumber(), 0.001); symop = ParsedSymop.createSymop("1/4-y-x"); Assert.assertEquals("xyz", "-y", symop.getXyz()); Assert.assertEquals("xyz", "-x", symop.getXyz1()); Assert.assertEquals("xyz", 0.25, symop.getNumber(), 0.001); symop = ParsedSymop.createSymop("-3/4+z-x"); Assert.assertEquals("xyz", "z", symop.getXyz()); Assert.assertEquals("xyz", "-x", symop.getXyz1()); Assert.assertEquals("xyz", -0.75, symop.getNumber(), 0.001); } @Test public void parseXYZ_NUMB() { ParsedSymop symop = ParsedSymop.createSymop("x+0.5"); Assert.assertEquals("xyz", "x", symop.getXyz()); Assert.assertEquals("xyz", "", symop.getXyz1()); Assert.assertEquals("xyz", 0.5, symop.getNumber(), 0.001); symop = ParsedSymop.createSymop("-y+.25"); Assert.assertEquals("xyz", "-y", symop.getXyz()); Assert.assertEquals("xyz", "", symop.getXyz1()); Assert.assertEquals("xyz", 0.25, symop.getNumber(), 0.001); symop = ParsedSymop.createSymop("z-0.75"); Assert.assertEquals("xyz", "z", symop.getXyz()); Assert.assertEquals("xyz", "", symop.getXyz1()); Assert.assertEquals("xyz", -0.75, symop.getNumber(), 0.001); } @Test public void parseXYZXYZ_NUMB() { ParsedSymop symop = ParsedSymop.createSymop("x+y+0.5"); Assert.assertEquals("xyz", "x", symop.getXyz()); Assert.assertEquals("xyz", "y", symop.getXyz1()); Assert.assertEquals("xyz", 0.5, symop.getNumber(), 0.001); symop = ParsedSymop.createSymop("-y-x+.25"); Assert.assertEquals("xyz", "-y", symop.getXyz()); Assert.assertEquals("xyz", "-x", symop.getXyz1()); Assert.assertEquals("xyz", 0.25, symop.getNumber(), 0.001); symop = ParsedSymop.createSymop("z-x-0.75"); Assert.assertEquals("xyz", "z", symop.getXyz()); Assert.assertEquals("xyz", "-x", symop.getXyz1()); Assert.assertEquals("xyz", -0.75, symop.getNumber(), 0.001); } @Test public void parseXYZ_FRACT() { ParsedSymop symop = ParsedSymop.createSymop("x+1/2"); Assert.assertEquals("xyz", "x", symop.getXyz()); Assert.assertEquals("xyz", "", symop.getXyz1()); Assert.assertEquals("xyz", 0.5, symop.getNumber(), 0.001); symop = ParsedSymop.createSymop("-y+1/4"); Assert.assertEquals("xyz", "-y", symop.getXyz()); Assert.assertEquals("xyz", "", symop.getXyz1()); Assert.assertEquals("xyz", 0.25, symop.getNumber(), 0.001); symop = ParsedSymop.createSymop("z-3/4"); Assert.assertEquals("xyz", "z", symop.getXyz()); Assert.assertEquals("xyz", "", symop.getXyz1()); Assert.assertEquals("xyz", -0.75, symop.getNumber(), 0.001); } @Test public void parseXYZXYZ_FRACT() { ParsedSymop symop = ParsedSymop.createSymop("x+y+1/2"); Assert.assertEquals("xyz", "x", symop.getXyz()); Assert.assertEquals("xyz", "y", symop.getXyz1()); Assert.assertEquals("xyz", 0.5, symop.getNumber(), 0.001); symop = ParsedSymop.createSymop("-y-x+1/4"); Assert.assertEquals("xyz", "-y", symop.getXyz()); Assert.assertEquals("xyz", "-x", symop.getXyz1()); Assert.assertEquals("xyz", 0.25, symop.getNumber(), 0.001); symop = ParsedSymop.createSymop("z-x-3/4"); Assert.assertEquals("xyz", "z", symop.getXyz()); Assert.assertEquals("xyz", "-x", symop.getXyz1()); Assert.assertEquals("xyz", -0.75, symop.getNumber(), 0.001); } @Test public void createTransformTest() { Transform3 transform = ParsedSymop.createTransform(new String[]{"x+y+1/2", "-y-x+1/4", "z-x-3/4"}); Assert.assertEquals("matrix", "{4,4}\n"+ "(1.0,1.0,0.0,0.5)\n"+ "(-1.0,-1.0,0.0,0.25)\n"+ "(-1.0,0.0,1.0,-0.75)\n"+ "(0.0,0.0,0.0,1.0)", transform.toString()); } } euclid-euclid-2.9/src/test/java/org/xmlcml/euclid/test/Plane3Test.java000066400000000000000000000230311461721410700257210ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid.test; import static org.xmlcml.euclid.EC.EPS; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.xmlcml.euclid.Angle; import org.xmlcml.euclid.EC; import org.xmlcml.euclid.EuclidRuntimeException; import org.xmlcml.euclid.Line3; import org.xmlcml.euclid.Plane3; import org.xmlcml.euclid.Point3; import org.xmlcml.euclid.Vector3; /** * test Plane3 * * @author pmr * */ public class Plane3Test extends GeomTest { /** * setup. * * @throws Exception */ @Before public void setUp() throws Exception { super.setUp(); } /** * equality test. true if both args not null and equal within epsilon * * @param msg * message * @param test * @param expected * @param epsilon */ public static void assertEquals(String msg, Plane3 test, Plane3 expected, double epsilon) { Assert.assertNotNull("test should not be null (" + msg + EC.S_RBRAK, test); Assert.assertNotNull("ref should not be null (" + msg + EC.S_RBRAK, expected); DoubleTestBase.assertEquals(msg, test.getArray(), expected.getArray(), epsilon); } /** * equality test. true if both args not null and equal within epsilon * * @param msg * message * @param test * array must be of length 4 * @param expected * @param epsilon */ public static void assertEquals(String msg, double[] test, Plane3 expected, double epsilon) { Assert.assertNotNull("test should not be null (" + msg + EC.S_RBRAK, test); Assert.assertEquals("must be of length 4", 4, test.length); Assert.assertNotNull("ref should not be null (" + msg + EC.S_RBRAK, expected); DoubleTestBase.assertEquals(msg, test, expected.getArray(), epsilon); } /** * Test method for 'org.xmlcml.euclid.Plane3.Plane3()' */ @Test public void testPlane3() { Assert.assertNotNull("plane", pl0); Plane3Test.assertEquals("plane", new double[] { 0., 0., 0., 0. }, pl0, EPS); } /** * Test method for 'org.xmlcml.euclid.Plane3.Plane3(double, double, double, * double)' */ @Test public void testPlane3DoubleDoubleDoubleDouble() { Plane3Test.assertEquals("plane", new double[] { 1., 0., 0., 0. }, pl1000, EPS); } /** * Test method for 'org.xmlcml.euclid.Plane3.Plane3(double[], double)' */ @Test public void testPlane3DoubleArrayDouble() { Plane3Test.assertEquals("plane", new double[] { 1. / s14, 2. / s14, 3. / s14, 4. }, pl1234, EPS); } /** * Test method for 'org.xmlcml.euclid.Plane3.Plane3(double[])' */ @Test public void testPlane3DoubleArray() { Plane3Test.assertEquals("plane", new double[] { 0., 0., 1., 0. }, pl0010, EPS); } /** * Test method for 'org.xmlcml.euclid.Plane3.Plane3(Vector3, double)' */ @Test public void testPlane3Vector3Double() { Plane3Test.assertEquals("plane", new double[] { 1. / s3, 1. / s3, 1. / s3, 1. }, pl1111, EPS); } /** * Test method for 'org.xmlcml.euclid.Plane3.Plane3(Plane3)' */ @Test public void testPlane3Plane3() { Plane3 pl = new Plane3(pl1234); Plane3Test.assertEquals("plane", new double[] { 1. / s14, 2. / s14, 3. / s14, 4. }, pl, EPS); } /** * Test method for 'org.xmlcml.euclid.Plane3.Plane3(Point3, Point3, Point3)' */ @Test public void testPlane3Point3Point3Point3() { Plane3 pl = new Plane3(p100, p010, p001); Plane3Test.assertEquals("plane", new double[] { 1. / s3, 1. / s3, 1. / s3, 1. / s3 }, pl, EPS); try { pl = new Plane3(p100, p010, p100); Assert.fail("should always throw " + "zero normal"); } catch (EuclidRuntimeException e) { Assert.assertEquals("plane", "zero length normal", e.getMessage()); } } /** * Test method for 'org.xmlcml.euclid.Plane3.Plane3(Line3, Point3)' */ @Test public void testPlane3Line3Point3() { Plane3 pl = new Plane3(l100000, p001); Plane3Test.assertEquals("plane", new double[] { 0., -1., 0., 0. }, pl, EPS); pl = new Plane3(l123456, p321); Assert.assertNotNull("plane", pl); Plane3Test.assertEquals("plane", new double[] { -0.40824829046386013, 0.816496580927727, -0.4082482904638642, 9.325873406851315E-15 }, pl, EPS); } /** * Test method for 'org.xmlcml.euclid.Plane3.getVector()' */ @Test public void testGetVector() { Plane3 pl = new Plane3(p100, p010, p001); Vector3 v = pl.getVector(); Vector3Test.assertEquals("vector", new double[] { 1. / s3, 1. / s3, 1. / s3 }, v, EPS); } /** * Test method for 'org.xmlcml.euclid.Plane3.getDistance()' */ @Test public void testGetDistance() { Plane3 pl = new Plane3(p100, p010, p001); Assert.assertEquals("distance", 1. / s3, pl.getDistance(), EPS); } /** * Test method for 'org.xmlcml.euclid.Plane3.negative()' */ @Test public void testNegative() { Plane3 pl = new Plane3(p100, p010, p001); Plane3Test.assertEquals("negative", new double[] { 1. / s3, 1. / s3, 1. / s3, 1. / s3 }, pl, EPS); pl.negative(); Plane3Test.assertEquals("negative", new double[] { -1. / s3, -1. / s3, -1. / s3, 1. / s3 }, pl, EPS); } /** * Test method for 'org.xmlcml.euclid.Plane3.isEqualTo(Plane3)' */ @Test public void testIsEqualTo() { Plane3 pl = new Plane3(pl1234); Assert.assertTrue("equals", pl.isEqualTo(pl1234)); } /** * Test method for 'org.xmlcml.euclid.Plane3.subtract()' */ @Test public void testSubtract() { Plane3Test.assertEquals("equals", new double[] { 1. / s14, 2. / s14, 3. / s14, 4. }, pl1234, EPS); Plane3 pl = pl1234.subtract(); Plane3Test.assertEquals("equals", new double[] { -1. / s14, -2. / s14, -3. / s14, 4. }, pl, EPS); } /** * Test method for 'org.xmlcml.euclid.Plane3.getDistanceFromPoint(Point3)' */ @Test public void testGetDistanceFromPoint() { double d = pl1111.getDistanceFromPoint(p000); Assert.assertEquals("equals", -1., d, EPS); Plane3 pl = pl1111.subtract(); Assert.assertEquals("equals", -1., pl.getDistanceFromPoint(p000), EPS); } /** * Test method for 'org.xmlcml.euclid.Plane3.isParallelTo(Plane3)' */ @Test public void testIsParallelTo() { Plane3 pl = new Plane3(pl1234); Assert.assertTrue("parallel", pl.isParallelTo(pl1234)); pl.subtract(); Assert.assertFalse("parallel", pl.isParallelTo(pl1234)); } /** * Test method for 'org.xmlcml.euclid.Plane3.isAntiparallelTo(Plane3)' */ @Test public void testIsAntiparallelTo() { Plane3 pl = new Plane3(pl1234); Assert.assertFalse("antiparallel", pl.isAntiparallelTo(pl1234)); pl = pl.subtract(); Assert.assertTrue("isAntiparallelTo", pl.isAntiparallelTo(pl1234)); } /** * Test method for 'org.xmlcml.euclid.Plane3.containsPoint(Point3)' */ @Test public void testContainsPoint() { Assert.assertFalse("contains", pl1111.containsPoint(p111)); Assert.assertTrue("contains", pl1111.containsPoint(new Point3(1. / s3, 1. / s3, 1. / s3))); } /** * Test method for 'org.xmlcml.euclid.Plane3.getClosestPointTo(Point3)' */ @Test public void testGetClosestPointTo() { Point3 p = pl1111.getClosestPointTo(p000); Point3Test.assertEquals("equals", new double[] { 1. / s3, 1. / s3, 1. / s3 }, p, EPS); } /** * Test method for 'org.xmlcml.euclid.Plane3.getIntersectionWith(Line3)' */ @Test public void testGetIntersectionWithLine3() { Point3 p = pl1111.getIntersectionWith(l123456); Point3Test.assertEquals("equals", new double[] { 1.788675134594813, 0.5773502691896262, -0.6339745962155607 }, p, EPS); double d = pl1111.getDistanceFromPoint(p); Assert.assertEquals("intersection", 0.0, d, EPS); d = l123456.getDistanceFromPoint(p); Assert.assertEquals("intersection", 0.0, d, EPS); } /** * Test method for 'org.xmlcml.euclid.Plane3.getIntersectionWith(Plane3)' */ @Test public void testGetIntersectionWithPlane3() { Line3 l = pl1111.getIntersectionWith(pl1234); Vector3Test.assertEquals("plane line", new double[] { 0.4082482904638631, -0.8164965809277261, 0.4082482904638631, }, l.getVector(), EPS); Point3Test.assertEquals("plane line", new double[] { -5.17391369678938, 0.5773502691896258, 6.328614235168632 }, l.getPoint(), EPS); Point3 p = l.getPoint(); double d = pl1111.getDistanceFromPoint(p); Assert.assertEquals("intersection", 0.0, d, EPS); Point3 p1 = p.plus(l.getVector()); d = pl1234.getDistanceFromPoint(p1); Assert.assertEquals("intersection", 0.0, d, EPS); } /** * Test method for 'org.xmlcml.euclid.Plane3.getIntersectionWith(Plane3, * Plane3)' */ @Test public void testGetIntersectionWithPlane3Plane3() { Point3 p = pl1111.getIntersectionWith(pl1234, pl1000); Point3Test.assertEquals("intersection", new double[] { 0.0, -9.770477124389135, 11.502527931958012 }, p, EPS); // check double d = pl1111.getDistanceFromPoint(p); Assert.assertEquals("intersection", 0.0, d, EPS); d = pl1234.getDistanceFromPoint(p); Assert.assertEquals("intersection", 0.0, d, EPS); } /** * Test method for 'org.xmlcml.euclid.Plane3.getAngleMadeWith(Plane3)' */ @Test public void testGetAngleMadeWith() { Angle a = pl1111.getAngleMadeWith(pl1234); Assert.assertEquals("angle", 0.38759668665518016, a.getRadian(), EPS); } } euclid-euclid-2.9/src/test/java/org/xmlcml/euclid/test/Point3Test.java000066400000000000000000000353571461721410700257710ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid.test; import static org.xmlcml.euclid.EC.EPS; import static org.xmlcml.euclid.EC.S_RBRAK; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.xmlcml.euclid.Angle; import org.xmlcml.euclid.EuclidRuntimeException; import org.xmlcml.euclid.Line3; import org.xmlcml.euclid.Plane3; import org.xmlcml.euclid.Point3; import org.xmlcml.euclid.Util; import org.xmlcml.euclid.Vector3; /** * test Point3 * * @author pmr * */ public class Point3Test extends GeomTest { /** * setup. */ /** * setup. * * @throws Exception */ @Before public void setUp() throws Exception { super.setUp(); } /** * equality test. true if both args not null and equal within epsilon * * @param msg * message * @param test * @param expected * @param epsilon */ public static void assertEquals(String msg, Point3 test, Point3 expected, double epsilon) { Assert.assertNotNull("test should not be null (" + msg + S_RBRAK, test); Assert.assertNotNull("ref should not be null (" + msg + S_RBRAK, expected); DoubleTestBase.assertEquals(msg, test.getArray(), expected.getArray(), epsilon); } /** * equality test. true if both args not null and equal within epsilon * * @param msg * message * @param test * array must be of length 3 * @param expected * @param epsilon */ public static void assertEquals(String msg, double[] test, Point3 expected, double epsilon) { Assert.assertNotNull("test should not be null (" + msg + S_RBRAK, test); Assert.assertEquals("must be of length 3", 3, test.length); Assert.assertNotNull("ref should not be null (" + msg + S_RBRAK, expected); DoubleTestBase.assertEquals(msg, test, expected.getArray(), epsilon); } /** * Test method for 'org.xmlcml.euclid.Point3.Point3()' */ @Test public void testPoint3() { Assert.assertNotNull("point", p0); Point3Test .assertEquals("point 0", new double[] { 0., 0., 0. }, p0, EPS); } /** * Test method for 'org.xmlcml.euclid.Point3.Point3(double, double, double)' */ @Test public void testPoint3DoubleDoubleDouble() { Assert.assertNotNull("point", p123); Point3Test.assertEquals("point 123", new double[] { 1., 2., 3. }, p123, EPS); } /** * Test method for 'org.xmlcml.euclid.Point3.Point3(Point3)' */ @Test public void testPoint3Point3() { Point3 pp = new Point3(p123); Assert.assertNotNull("point", pp); Point3Test.assertEquals("point copy", new double[] { 1., 2., 3. }, pp, EPS); } /** * Test method for 'org.xmlcml.euclid.Point3.Point3(double[])' */ @Test public void testPoint3DoubleArray() { Point3 pp = new Point3(new double[] { 1., 3., 5. }); Assert.assertNotNull("point", pp); Point3Test.assertEquals("point copy", new double[] { 1., 3., 5. }, pp, EPS); try { pp = new Point3(new double[] { 1., 3. }); Assert.fail("should always throw " + "must have 3 coordinates"); } catch (EuclidRuntimeException e) { Assert.assertEquals("bad coordinates", "array size required (3) found 2", e.getMessage()); } } /** * Test method for 'org.xmlcml.euclid.Point3.Point3(Vector3)' */ @Test public void testPoint3Vector3() { Point3 pp = new Point3(v123); Assert.assertNotNull("point", pp); Point3Test.assertEquals("point copy", new double[] { 1., 2., 3. }, pp, EPS); } /** * Test method for 'org.xmlcml.euclid.Point3.getArray()' */ @Test public void testGetArray() { Point3Test.assertEquals("point array", new double[] { 1., 2., 3. }, p123, EPS); } /** * Test method for 'org.xmlcml.euclid.Point3.clear()' */ @Test public void testClear() { p123.clear(); Point3Test.assertEquals("point array", new double[] { 0., 0., 0. }, p123, EPS); } /** * Test method for 'org.xmlcml.euclid.Point3.isEqualTo(Point3)' */ @Test public void testIsEqualToPoint3() { Point3 pp = new Point3(p123); Assert.assertTrue("point isEqualTo", p123.isEqualTo(pp)); } /** * Test method for 'org.xmlcml.euclid.Point3.isEqualTo(Point3, double)' */ @Test public void testIsEqualToPoint3Double() { Point3 pp = new Point3(p123); Assert.assertTrue("point isEqualTo", p123.isEqualTo(pp, EPS)); } /** * Test method for * 'org.xmlcml.euclid.Point3.equalsCrystallographically(Point3)' */ @Test public void testEqualsCrystallographically() { Assert.assertTrue("point isEqualToCrystallographically", p123 .equalsCrystallographically(p000)); Assert.assertFalse("point isEqualToCrystallographically", p123 .equalsCrystallographically(new Point3(0.1, 0.2, 0.3))); } /** * Test method for * 'org.xmlcml.euclid.Point3.normaliseCrystallographically()' */ @Test public void testNormaliseCrystallographically() { Point3 p = new Point3(1.1, 2.2, -0.7); p.normaliseCrystallographically(); Point3Test.assertEquals("normalise", new double[] { 0.1, 0.2, 0.3 }, p, EPS); } /** * Test method for 'org.xmlcml.euclid.Point3.isInvariant(Transform3, * boolean)' */ @Test public void testIsInvariant() { boolean allowTranslate = false; Assert.assertTrue("invariant", p123.isInvariant(tr0, allowTranslate)); allowTranslate = true; Assert.assertTrue("invariant", p123.isInvariant(tr0, allowTranslate)); allowTranslate = false; Assert.assertFalse("invariant", p123.isInvariant(tr1, allowTranslate)); allowTranslate = true; Assert.assertTrue("invariant", p123.isInvariant(tr0, allowTranslate)); } /** * Test method for 'org.xmlcml.euclid.Point3.subtract(Point3)' */ @Test public void testSubtractPoint3() { Vector3 v = p123.subtract(p321); Vector3Test.assertEquals("subtract", new double[] { -2, 0, 2 }, v, EPS); } /** * Test method for 'org.xmlcml.euclid.Point3.plus(Point3)' */ @Test public void testPlusPoint3() { Point3 pp = p123.plus(p321); Point3Test.assertEquals("subtract", new double[] { 4., 4., 4. }, pp, EPS); } /** * Test method for 'org.xmlcml.euclid.Point3.plusEquals(Point3)' */ @Test public void testPlusEqualsPoint3() { p123.plusEquals(p321); Point3Test.assertEquals("plusEquals", new double[] { 4., 4., 4. }, p123, EPS); } /** * Test method for 'org.xmlcml.euclid.Point3.plus(Vector3)' */ @Test public void testPlusVector3() { Point3 pp = p123.plus(v321); Point3Test.assertEquals("plus", new double[] { 4., 4., 4. }, pp, EPS); } /** * Test method for 'org.xmlcml.euclid.Point3.plusEquals(Vector3)' */ @Test public void testPlusEqualsVector3() { p123.plusEquals(v321); Point3Test.assertEquals("plusEquals", new double[] { 4., 4., 4. }, p123, EPS); } /** * Test method for 'org.xmlcml.euclid.Point3.subtract(Vector3)' */ @Test public void testSubtractVector3() { Point3 pp = p123.subtract(v321); Point3Test.assertEquals("subtract", new double[] { -2., 0., 2. }, pp, EPS); } /** * Test method for 'org.xmlcml.euclid.Point3.subtractEquals(Point3)' */ @Test public void testSubtractEqualsPoint3() { p123.subtractEquals(p321); Point3Test.assertEquals("subtractEquals", new double[] { -2., 0., 2. }, p123, EPS); } /** * Test method for 'org.xmlcml.euclid.Point3.subtractEquals(Vector3)' */ @Test public void testSubtractEqualsVector3() { p123.subtractEquals(v321); Point3Test.assertEquals("subtractEquals", new double[] { -2., 0., 2. }, p123, EPS); } /** * Test method for 'org.xmlcml.euclid.Point3.multiplyBy(double)' */ @Test public void testMultiplyBy() { Point3 p = p123.multiplyBy(10.); Point3Test.assertEquals("multiply", new double[] { 10., 20., 30. }, p, EPS); } /** * Test method for 'org.xmlcml.euclid.Point3.multiplyEquals(double)' */ @Test public void testMultiplyEquals() { p123.multiplyEquals(10.); Point3Test.assertEquals("multiplyEquals", new double[] { 10., 20., 30. }, p123, EPS); } /** * Test method for 'org.xmlcml.euclid.Point3.reflect()' */ @Test public void testReflect() { p123.reflect(); Point3Test.assertEquals("reflect", new double[] { -1., -2., -3. }, p123, EPS); } /** * Test method for 'org.xmlcml.euclid.Point3.divideBy(double)' */ @Test public void testDivideBy() { Point3 p = p123.divideBy(10.); Point3Test.assertEquals("divideBy", new double[] { 0.1, 0.2, 0.3 }, p, EPS); } /** * Test method for 'org.xmlcml.euclid.Point3.divideEquals(double)' */ @Test public void testDivideEquals() { p123.divideEquals(10.); Point3Test.assertEquals("divideEquals", new double[] { 0.1, 0.2, 0.3 }, p123, EPS); } /** * Test method for 'org.xmlcml.euclid.Point3.elementAt(int)' */ @Test public void testElementAt() { double d = p123.elementAt(1); Assert.assertEquals("element at", 2., d, EPS); try { d = p123.elementAt(3); } catch (EuclidRuntimeException e) { Assert.assertEquals("element at", "index (3)out of range: 0/2", e .getMessage()); } } /** * Test method for 'org.xmlcml.euclid.Point3.setElementAt(int, double)' */ @Test public void testSetElementAt() { p123.setElementAt(1, 10.); Point3Test.assertEquals("set element at", new double[] { 1., 10., 3. }, p123, EPS); try { p123.setElementAt(3, 10.); } catch (EuclidRuntimeException e) { Assert.assertEquals("element at", "index (3)out of range: 0/2", e .getMessage()); } } /** * Test method for 'org.xmlcml.euclid.Point3.transform(Transform3)' */ @Test public void testTransform() { Point3 p = p123.transform(tr1); Point3Test.assertEquals("transform", new double[] { 1., -2., 3. }, p, EPS); } /** * Test method for 'org.xmlcml.euclid.Point3.transformEquals(Transform3)' */ @Test public void testTransformEquals() { p123.transformEquals(tr1); Point3Test.assertEquals("transform", new double[] { 1., -2., 3. }, p123, EPS); } /** * Test method for 'org.xmlcml.euclid.Point3.getDistanceFromOrigin()' */ @Test public void testGetDistanceFromOrigin() { double d = p123.getDistanceFromOrigin(); Assert.assertEquals("distance from origin", Math.sqrt(14.), d, EPS); } /** * Test method for * 'org.xmlcml.euclid.Point3.getSquaredDistanceFromPoint(Point3)' */ @Test public void testGetSquaredDistanceFromPoint() { double d = p123.getSquaredDistanceFromPoint(p321); Assert.assertEquals("distance from origin", 8., d, EPS); } /** * Test method for 'org.xmlcml.euclid.Point3.getDistanceFromPoint(Point3)' */ @Test public void testGetDistanceFromPoint() { double d = p123.getDistanceFromPoint(p321); Assert.assertEquals("distance from origin", Math.sqrt(8.), d, EPS); } /** * Test method for 'org.xmlcml.euclid.Point3.distanceFromPlane(Plane3)' */ @Test public void testDistanceFromPlane() { Point3 p = new Point3(0., 0., 0.); Plane3 pl = null; try { pl = new Plane3(new Vector3(1., 1., 1.), 1.0); } catch (Exception e) { Util.BUG(e); } double d = p.distanceFromPlane(pl); Assert.assertEquals("distance ", -1.0, d, EPS); p = new Point3(1., 1., 1.); d = p.distanceFromPlane(pl); Assert.assertEquals("distance ", Math.sqrt(3.) - 1., d, EPS); } /** * Test method for 'org.xmlcml.euclid.Point3.getClosestPointOnLine(Line3)' */ @Test public void testGetClosestPointOnLine() { Vector3 vv = new Vector3(1., 1., 1.); Point3 pp = new Point3(1., 2., 3.); Line3 ll = new Line3(pp, vv); Assert.assertTrue("on line", pp.isOnLine(ll)); double d = pp.distanceFromLine(ll); Point3 p0 = new Point3(0., 0., 0.); Assert.assertFalse("on line", p0.isOnLine(ll)); Point3 pClose = p0.getClosestPointOnLine(ll); Point3Test.assertEquals("nearest point", new double[] { -1, 0., 1. }, pClose, EPS); d = pClose.distanceFromLine(ll); Assert.assertEquals("distance", 0.0, d, EPS); Vector3 v = pClose.subtract(p0); Angle a = null; a = v.getAngleMadeWith(vv); Assert.assertEquals("angle ", Math.PI / 2., a.getRadian(), EPS); } /** * Test method for 'org.xmlcml.euclid.Point3.isOnLine(Line3)' */ @Test public void testIsOnLine() { Vector3 vv = new Vector3(1., 1., 1.); Point3 p = new Point3(1., 2., 3.); Line3 l3 = new Line3(p, vv); Assert.assertTrue("on line", p.isOnLine(l3)); } /** * Test method for 'org.xmlcml.euclid.Point3.distanceFromLine(Line3)' */ @Test public void testDistanceFromLine() { Point3 pp = new Point3(4., 5., 6.); Vector3 vv = new Vector3(1., 2., 3.); Line3 ll = new Line3(pp, vv); double d = pp.distanceFromLine(ll); Assert.assertEquals("distance from line", 0.0, d,EPS); Point3 p0 = new Point3(0., 0., 0.); d = p0.distanceFromLine(ll); Assert.assertEquals("distance", 1.9639610121239313, d, EPS); } /** * Test method for 'org.xmlcml.euclid.Point3.getMidPoint(Point3)' */ @Test public void testGetMidPoint() { Point3 p = p123.getMidPoint(p321); Point3Test.assertEquals("mid point", new double[] { 2., 2., 2. }, p, EPS); } /** * Test method for 'org.xmlcml.euclid.Point3.getAngle(Point3, Point3, * Point3)' */ @Test public void testGetAngle() { Point3 p444 = new Point3(4., 4., 4.); Angle p = Point3.getAngle(p123, p444, p321); Assert.assertNotNull("angle ", p); Assert.assertEquals("angle", 0.7751933733103613, p.getRadian(), EPS); p = Point3 .getAngle(new Point3(1., 0., 0), p000, new Point3(0., 1., 0.)); Assert.assertNotNull("angle ", p); Assert.assertEquals("angle", Math.PI / 2., p.getRadian(), EPS); } /** * Test method for 'org.xmlcml.euclid.Point3.getTorsion(Point3, Point3, * Point3, Point3)' */ @Test public void testGetTorsion() { Angle p = null; p = Point3.getTorsion(new Point3(1., 0., 0), p000, new Point3(0., 1., 0.), new Point3(0., 1., 1.)); Assert.assertEquals("angle", -Math.PI / 2., p.getRadian(), EPS); } /** * Test method for * 'org.xmlcml.euclid.Point3.calculateFromInternalCoordinates(Point3, * Point3, Point3, double, Angle, Angle)' */ @Test public void testCalculateFromInternalCoordinates() { Point3 p0 = new Point3(1., 0., 0.); Point3 p1 = new Point3(0., 0., 0.); Point3 p2 = new Point3(0., 1., 0.); Point3 p = Point3.calculateFromInternalCoordinates(p0, p1, p2, 2.0, new Angle(Math.PI / 2), new Angle(Math.PI / 2)); Point3Test.assertEquals("internals", new double[] { 0.0, 1.0, -2.0 }, p, EPS); } /** * Test method for 'org.xmlcml.euclid.Point3.isOrigin()' */ @Test public void testIsOrigin() { Assert.assertTrue("origin", p000.isOrigin()); Assert.assertFalse("origin", p123.isOrigin()); } } euclid-euclid-2.9/src/test/java/org/xmlcml/euclid/test/Point3VectorTest.java000066400000000000000000000536671461721410700271600ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid.test; import static org.xmlcml.euclid.EuclidConstants.EPS; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.xmlcml.euclid.Angle; import org.xmlcml.euclid.Axis.Axis3; import org.xmlcml.euclid.EC; import org.xmlcml.euclid.EuclidRuntimeException; import org.xmlcml.euclid.Int; import org.xmlcml.euclid.IntSet; import org.xmlcml.euclid.Line3; import org.xmlcml.euclid.Plane3; import org.xmlcml.euclid.Point3; import org.xmlcml.euclid.Point3Vector; import org.xmlcml.euclid.Real3Range; import org.xmlcml.euclid.RealArray; import org.xmlcml.euclid.RealMatrix; import org.xmlcml.euclid.RealRange; import org.xmlcml.euclid.RealSquareMatrix; import org.xmlcml.euclid.Transform3; import org.xmlcml.euclid.Vector3; /** * test Point3Vector. * * @author pmr * */ public class Point3VectorTest { Point3Vector p0; Point3Vector p1; Point3Vector p2; final static double s2 = Math.sqrt(2.); final static double s3 = Math.sqrt(3.); /** * setup. * * @throws Exception */ @Before public void setUp() throws Exception { p0 = new Point3Vector(); p1 = new Point3Vector(new double[] { 11., 21., 31., 12., 22., 32., 13., 23., 33., 14., 24., 34. }); p2 = new Point3Vector(new double[] { 1., 0., 0., 0., 0., 0., 0., 1., 0., 0., 1., 1. }); } /** * equality test. true if both args not null and equal within epsilon * * @param msg * message * @param test * @param expected * @param epsilon */ public static void assertEquals(String msg, Point3Vector test, Point3Vector expected, double epsilon) { Assert.assertNotNull("test should not be null (" + msg + EC.S_RBRAK, test); Assert.assertNotNull("expected should not be null (" + msg + EC.S_RBRAK, expected); DoubleTestBase.assertEquals(msg, test.getArray(), expected.getArray(), epsilon); } /** * equality test. true if both args not null and equal within epsilon * * @param msg * message * @param test * array must be of length 3 * @param expected * @param epsilon */ public static void assertEquals(String msg, double[] test, Point3Vector expected, double epsilon) { Assert.assertNotNull("test should not be null (" + msg + EC.S_RBRAK, test); Assert.assertNotNull("expected should not be null (" + msg + EC.S_RBRAK, expected); Assert.assertEquals("must be of equal length ", test.length, expected .getArray().length); DoubleTestBase.assertEquals(msg, test, expected.getArray(), epsilon); } /** * Test method for 'org.xmlcml.euclid.Point3Vector.Point3Vector()' */ @Test public void testPoint3Vector() { Assert.assertNotNull("p3v", p0); Assert.assertEquals("p3v", 0, p0.size()); } /** * Test method for 'org.xmlcml.euclid.Point3Vector.Point3Vector(double[])' */ @Test public void testPoint3VectorDoubleArray() { Assert.assertNotNull("p3v", p1); Assert.assertEquals("p3v", 4, p1.size()); try { new Point3Vector(new double[] { 1., 2., 3., 4. }); } catch (EuclidRuntimeException e) { Assert.assertEquals("bad array", "array length must be multiple of 3", e.getMessage()); } } /** * Test method for 'org.xmlcml.euclid.Point3Vector.Point3Vector(int, * double[], double[], double[])' */ @Test public void testPoint3VectorIntDoubleArrayDoubleArrayDoubleArray() { new Point3Vector(3, new double[] { 11., 12., 13., }, new double[] { 21., 22., 23., }, new double[] { 31., 32., 33., }); try { new Point3Vector(3, new double[] { 11., 12., 13., }, new double[] { 21., 22., 23., }, new double[] { 31., 32., }); } catch (EuclidRuntimeException e) { Assert.assertEquals("bad array", "array size required (3) found 2", e.getMessage()); } } /** * Test method for 'org.xmlcml.euclid.Point3Vector.Point3Vector(RealArray)' */ @Test public void testPoint3VectorRealArray() { Point3Vector p3v = new Point3Vector(new RealArray(new double[] { 11., 12., 13., 21., 22., 23., 31., 32., 33., 41., 42., 43., })); Assert.assertEquals("p3v", 4, p3v.size()); } /** * Test method for * 'org.xmlcml.euclid.Point3Vector.Point3Vector(Point3Vector)' */ @Test public void testPoint3VectorPoint3Vector() { Point3Vector p = new Point3Vector(p1); Assert.assertTrue("copy", p.isEqualTo(p1)); } /** * Test method for 'org.xmlcml.euclid.Point3Vector.size()' */ @Test public void testSize() { Assert.assertEquals("size", 4, p1.size()); } /** * Test method for 'org.xmlcml.euclid.Point3Vector.add(Point3)' */ @Test public void testAdd() { Point3 p = new Point3(10., 11., 12.); Point3Test.assertEquals("add", new double[] { 14., 24., 34. }, p1 .get(3), EPS); Assert.assertEquals("add", 4, p1.size()); p1.add(p); Assert.assertEquals("add", 5, p1.size()); Point3Test.assertEquals("add", p, p1.get(4), EPS); } /** * Test method for 'org.xmlcml.euclid.Point3Vector.setElementAt(Point3, * int)' */ @Test public void testSetElementAtPoint3Int() { Point3 p = new Point3(51., 52., 53.); p1.setElementAt(p, 2); Point3VectorTest.assertEquals("set", new double[] { 11., 21., 31., 12., 22., 32., 51., 52., 53., 14., 24., 34., }, p1, EPS); } /** * Test method for 'org.xmlcml.euclid.Point3Vector.elementAt(int)' */ @Test public void testElementAt() { Point3 p = p1.elementAt(2); Point3Test.assertEquals("get", new double[] { 13., 23., 33. }, p, EPS); } /** * Test method for 'org.xmlcml.euclid.Point3Vector.get(int)' */ @Test public void testGet() { Point3 p = p1.get(2); Point3Test.assertEquals("get", new double[] { 13., 23., 33. }, p, EPS); } /** * Test method for 'org.xmlcml.euclid.Point3Vector.addElement(Point3)' */ @Test public void testAddElement() { Point3 p = new Point3(51., 52., 53.); p1.addElement(p); Point3VectorTest.assertEquals("set", new double[] { 11., 21., 31., 12., 22., 32., 13., 23., 33., 14., 24., 34., 51., 52., 53., }, p1, EPS); } /** * Test method for 'org.xmlcml.euclid.Point3Vector.setElementAt(Vector3, * int)' */ @Test public void testSetElementAtVector3Int() { Vector3 p = new Vector3(51., 52., 53.); p1.setElementAt(p, 2); Point3VectorTest.assertEquals("set", new double[] { 11., 21., 31., 12., 22., 32., 51., 52., 53., 14., 24., 34., }, p1, EPS); } /** * Test method for 'org.xmlcml.euclid.Point3Vector.getRange(Axis3)' */ @Test public void testGetRange() { RealRange r = p1.getRange(Axis3.X); Assert.assertEquals("range", 11., r.getMin(), EPS); Assert.assertEquals("range", 14., r.getMax(), EPS); r = p1.getRange(Axis3.Y); Assert.assertEquals("range", 21., r.getMin(), EPS); Assert.assertEquals("range", 24., r.getMax(), EPS); r = p1.getRange(Axis3.Z); Assert.assertEquals("range", 31., r.getMin(), EPS); Assert.assertEquals("range", 34., r.getMax(), EPS); Point3 p = new Point3(51., 52., 53.); p1.addElement(p); r = p1.getRange(Axis3.X); Assert.assertEquals("range", 11., r.getMin(), EPS); Assert.assertEquals("range", 51., r.getMax(), EPS); r = p1.getRange(Axis3.Y); Assert.assertEquals("range", 21., r.getMin(), EPS); Assert.assertEquals("range", 52., r.getMax(), EPS); r = p1.getRange(Axis3.Z); Assert.assertEquals("range", 31., r.getMin(), EPS); Assert.assertEquals("range", 53., r.getMax(), EPS); } /** * Test method for 'org.xmlcml.euclid.Point3Vector.getRange3()' */ @Test public void testGetRange3() { Real3Range rr = p1.getRange3(); RealRange r = rr.getXRange(); Assert.assertEquals("range", 11., r.getMin(), EPS); Assert.assertEquals("range", 14., r.getMax(), EPS); r = rr.getYRange(); Assert.assertEquals("range", 21., r.getMin(), EPS); Assert.assertEquals("range", 24., r.getMax(), EPS); r = rr.getZRange(); Assert.assertEquals("range", 31., r.getMin(), EPS); Assert.assertEquals("range", 34., r.getMax(), EPS); } /** * Test method for * 'org.xmlcml.euclid.Point3Vector.getSigmaDeltaSquared(Point3Vector)' */ @Test public void testGetSigmaDeltaSquared() { Point3Vector p = new Point3Vector(p1); double d = p1.getSigmaDeltaSquared(p); Assert.assertEquals("sigma", 0.0, d, EPS); for (Point3 pp : p.getPoint3List()) { pp.plusEquals(new Vector3(0.1, 0.2, 0.3)); } d = p1.getSigmaDeltaSquared(p); Assert.assertEquals("sigma", 0.56, d, EPS); } /** * Test method for 'org.xmlcml.euclid.Point3Vector.subArray(IntSet)' */ @Test public void testSubArray() { Point3Vector sub = p1.subArray(new IntSet(new int[] { 3, 1, 2 })); Point3VectorTest.assertEquals("set", new double[] { 14.0, 24.0, 34.0, 12.0, 22.0, 32.0, 13.0, 23.0, 33.0 }, sub, EPS); } /** * Test method for 'org.xmlcml.euclid.Point3Vector.plus(Point3Vector)' */ @Test public void testPlusPoint3Vector() { Point3Vector p = new Point3Vector(new double[] { 61., 62., 63., 71., 72., 73. }); try { p1.plus(p); Assert.fail("should always throw " + "incompatible sizes"); } catch (EuclidRuntimeException e) { Assert.assertEquals("plus", "incompatible Point3Vector sizes: 4/2", e.getMessage()); } p = new Point3Vector(new double[] { 61., 62., 63., 71., 72., 73., 81., 82., 83., 91., 92., 93. }); p1 = p1.plus(p); Point3VectorTest.assertEquals("plus", new double[] { 72.0, 83.0, 94.0, 83.0, 94.0, 105.0, 94.0, 105.0, 116.0, 105.0, 116.0, 127.0 }, p1, EPS); } /** * Test method for 'org.xmlcml.euclid.Point3Vector.subtract(Point3Vector)' */ @Test public void testSubtractPoint3Vector() { Point3Vector p = new Point3Vector(new double[] { 61., 62., 63., 71., 72., 73. }); try { p1.subtract(p); Assert.fail("should always throw " + "incompatible sizes"); } catch (EuclidRuntimeException e) { Assert.assertEquals("plus", "incompatible Point3Vector sizes: 4/2", e.getMessage()); } p = new Point3Vector(new double[] { 61., 62., 63., 71., 72., 73., 81., 82., 83., 91., 92., 93. }); p1 = p.subtract(p1); Point3VectorTest .assertEquals("plus", new double[] { 50.0, 41.0, 32.0, 59.0, 50.0, 41.0, 68.0, 59.0, 50.0, 77.0, 68.0, 59.0 }, p1, EPS); } /** * Test method for 'org.xmlcml.euclid.Point3Vector.getLine(int, int)' */ @Test public void testGetLine() { Line3 l = p1.getLine(1, 2); Vector3Test.assertEquals("line", new double[] { 0.5773502691896258, 0.5773502691896258, 0.5773502691896258 }, l.getVector(), EPS); Point3Test.assertEquals("line", new double[] { 12.0, 22.0, 32.0 }, l .getPoint(), EPS); } /** * Test method for 'org.xmlcml.euclid.Point3Vector.getCentroid()' */ @Test public void testGetCentroid() { Point3 p = p1.getCentroid(); Point3Test.assertEquals("centroid", new double[] { 12.5, 22.5, 32.5 }, p, EPS); } /** * Test method for 'org.xmlcml.euclid.Point3Vector.plus(Vector3)' */ @Test public void testPlusVector3() { Point3Vector p = p1.plus(new Vector3(10., 20., 30.)); Point3VectorTest.assertEquals("plus vector", new double[] { 21.0, 41.0, 61.0, 22.0, 42.0, 62.0, 23.0, 43.0, 63.0, 24.0, 44.0, 64.0 }, p, EPS); } /** * Test method for 'org.xmlcml.euclid.Point3Vector.subtract(Vector3)' */ @Test public void testSubtractVector3() { Point3Vector p = p1.subtract(new Vector3(10., 20., 30.)); Point3VectorTest .assertEquals("subtract vector", new double[] { 1.0, 1.0, 1.0, 2.0, 2.0, 2.0, 3.0, 3.0, 3.0, 4.0, 4.0, 4.0 }, p, EPS); } /** * Test method for 'org.xmlcml.euclid.Point3Vector.moveToCentroid()' */ @Test public void testMoveToCentroid() { p1.moveToCentroid(); Point3VectorTest.assertEquals("move to centroid", new double[] { -1.5, -1.5, -1.5, -0.5, -0.5, -0.5, 0.5, 0.5, 0.5, 1.5, 1.5, 1.5 }, p1, EPS); } /** * Test method for 'org.xmlcml.euclid.Point3Vector.inertialTensor()' */ @Test public void testInertialTensor() { Point3Vector p = new Point3Vector(new double[] { 1., 2., 3., 2., 4., 6., 3., 6., 9., 4., 1., 0., 3., 6., 1. }); RealMatrix m = p.calculateNonMassWeightedInertialTensorOld(); RealMatrixTest.assertEquals("move to centroid", 3, 3, new double[] { 5.2, 0.6, -4.4, 0.6, 20.8, 17.8, -4.4, 17.8, 54.8 }, m, EPS); } /** * Test method for 'org.xmlcml.euclid.Point3Vector.transform(Transform3)' */ @Test public void testTransformTransform3() { Transform3 t = new Transform3("y, -x, z"); p1.transform(t); Point3VectorTest.assertEquals("transform", new double[] { 21.0, -11.0, 31.0, 22.0, -12.0, 32.0, 23.0, -13.0, 33.0, 24.0, -14.0, 34.0 }, p1, EPS); } /** * Test method for 'org.xmlcml.euclid.Point3Vector.transform(Transform3, * IntSet)' */ @Test public void testTransformTransform3IntSet() { Transform3 t = new Transform3("y, -x, z"); p1.transform(t, new IntSet(new int[] { 3, 1, 2 })); Point3VectorTest.assertEquals("transform", new double[] { 11.0, 21.0, 31.0, 22.0, -12.0, 32.0, 23.0, -13.0, 33.0, 24.0, -14.0, 34.0 }, p1, EPS); } /** * Test method for 'org.xmlcml.euclid.Point3Vector.distance(int, int)' */ @Test public void testDistanceIntInt() { double d = p1.distance(1, 3); Assert.assertEquals("distance", 3.464101, d, 0.00001); } /** * Test method for 'org.xmlcml.euclid.Point3Vector.distance(IntSet)' */ @Test public void testDistanceIntSet() { double d = 0; try { d = p1.distance(new IntSet(new int[] { 3, 1, 2 })); } catch (EuclidRuntimeException e) { Assert.assertEquals("distance", "int set must have exactly 2 points", e.getMessage()); } d = p1.distance(new IntSet(new int[] { 3, 1 })); Assert.assertEquals("distance", 3.464101, d, 0.00001); } /** * Test method for 'org.xmlcml.euclid.Point3Vector.angle(int, int, int)' */ @Test public void testAngleIntIntInt() { Angle a = p2.angle(1, 2, 3); Assert.assertEquals("angle", Math.PI / 2., a.getRadian(), EPS); a = p2.angle(3, 1, 2); Assert.assertEquals("angle", Math.PI / 4., a.getRadian(), EPS); } /** * Test method for 'org.xmlcml.euclid.Point3Vector.angle(IntSet)' */ @Test public void testAngleIntSet() { Angle a = p2.angle(new IntSet(new int[] { 1, 2, 3 })); Assert.assertEquals("angle", Math.PI / 2., a.getRadian(), EPS); a = p2.angle(new IntSet(new int[] { 2, 0, 1 })); Assert.assertEquals("angle", Math.PI / 4., a.getRadian(), EPS); } /** * Test method for 'org.xmlcml.euclid.Point3Vector.torsion(int, int, int, * int)' */ @Test public void testTorsionIntIntIntInt() { Angle t = p2.torsion(0, 1, 2, 3); Assert.assertEquals("torsion", -Math.PI / 2., t.getRadian(), EPS); } /** * Test method for 'org.xmlcml.euclid.Point3Vector.torsion(IntSet)' */ @Test public void testTorsionIntSet() { Angle t = p2.torsion(new IntSet(new int[] { 0, 1, 2, 3 })); Assert.assertEquals("torsion", -Math.PI / 2., t.getRadian(), EPS); } /** * Test method for 'org.xmlcml.euclid.Point3Vector.getDistanceMatrix()' */ @Test public void testGetDistanceMatrix() { RealSquareMatrix rsm = p2.getDistanceMatrix(); RealSquareMatrixTest.assertEquals("distance matrix", 4, new double[] { 0., 1., s2, s3, 1., 0., 1., s2, s2, 1., 0., 1., s3, s2, 1., 0. }, rsm, EPS); } /** * Test method for * 'org.xmlcml.euclid.Point3Vector.deviationsFromPlane(Plane3)' */ @Test public void testDeviationsFromPlane() { Plane3 pl = new Plane3(1., 0., 0., 0.5); RealArray ra = p2.deviationsFromPlane(pl); RealArrayTest.assertEquals("deviations", new double[] { 0.5, -0.5, -0.5, -0.5 }, ra, EPS); } /** * Test method for 'org.xmlcml.euclid.Point3Vector.getPoint3(int)' */ @Test public void testGetPoint3() { Point3 p = p1.getPoint3(1); Point3Test.assertEquals("get point", new double[] { 12., 22., 32. }, p, EPS); } /** * Test method for 'org.xmlcml.euclid.Point3Vector.getXYZ()' */ @Test public void testGetXYZ() { RealArray ra = p1.getXYZ(); RealArrayTest.assertEquals("get point", new double[] { 11.0, 21.0, 31.0, 12.0, 22.0, 32.0, 13.0, 23.0, 33.0, 14.0, 24.0, 34.0 }, ra, EPS); } /** * Test method for 'org.xmlcml.euclid.Point3Vector.getCoordinate(int, * Axis3)' */ @Test public void testGetCoordinateIntAxis3() { double d = p1.getCoordinate(3, Axis3.Y); Assert.assertEquals("get coord", 24., d, EPS); } /** * Test method for 'org.xmlcml.euclid.Point3Vector.getXYZ(Axis3)' */ @Test public void testGetXYZAxis3() { RealArray ra = p1.getXYZ(Axis3.Y); RealArrayTest.assertEquals("get XYZ", new double[] { 21., 22., 23., 24. }, ra, EPS); } /** * Test method for 'org.xmlcml.euclid.Point3Vector.rms(Point3Vector)' */ @Test public void testRms() { p1.moveToCentroid(); p2.moveToCentroid(); double d = p1.rms(p2); Assert.assertEquals("rms", 0.9185586535436918, d, EPS); } /** * Test method for * 'org.xmlcml.euclid.Point3Vector.getFurthestPointFrom(Point3)' */ @Test public void testGetFurthestPointFrom() { int idx = p1.getFurthestPointFrom(new Point3(0., 0., 0.)); Assert.assertEquals("furthest point", 3, idx); } /** * Test method for * 'org.xmlcml.euclid.Point3Vector.getPointMakingSmallestAngle(Point3, * Point3)' */ @Test public void testGetPointMakingSmallestAngle() { int idx = p1.getPointMakingSmallestAngle(new Point3(0., 0., 0.), new Point3(20., 15., 10.)); Assert.assertEquals("smallest angle point", 3, idx); } /** * Test method for * 'org.xmlcml.euclid.Point3Vector.alignUsing3Points(Point3Vector)' */ @Test public void testAlignUsing3Points() { p1.add(new Point3(20., 10., 0.)); p2.add(new Point3(5., 10., 5.)); Transform3 t = p1.alignUsing3Points(p2); Transform3Test.assertEquals("align", new double[] { 0.11432809806666633, 0.8588003169190181, -0.4993907304428592, 0.0, 0.6186208633307761, -0.45487483749749846, -0.6406224392444506, 0.0, -0.7773270312065208, -0.2356923797483021, -0.5832767685106605, 0.0, 0.0, 0.0, 0.0, 1.0 }, t, EPS); } /** * Test method for 'org.xmlcml.euclid.Point3Vector.get3SeparatedPoints()' */ @Test public void testGet3SeparatedPoints() { p1.add(new Point3(20., 10., 0.)); int[] idx = p1.get3SeparatedPoints(); String s = Int.testEquals((new int[] { 4, 3, 0 }), idx); if (s != null) { Assert.fail("separated points" + "; " + s); } } /** * Test method for * 'org.xmlcml.euclid.Point3Vector.align3PointVectors(Point3Vector)' */ @Test public void testAlign3PointVectors() { Transform3 t = null; try { t = p1.align3PointVectors(p2); } catch (EuclidRuntimeException e) { Assert.assertEquals("align", "this requires 3 points", e .getMessage()); } Point3Vector pa = new Point3Vector(new double[] { 1., 0., 0., 0., 1., 0., 0., 0., 1. }); Point3Vector pb = new Point3Vector(new double[] { 0., 1., 0., 1., 0., 0., 0., 0., 1. }); t = pa.align3PointVectors(pb); Transform3Test.assertEquals("align", new double[] { 0.0, 0.0, -1.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0 }, t, EPS); } /** * Test method for 'org.xmlcml.euclid.Point3Vector.roughAlign(Point3Vector)' */ @Test public void testRoughAlign() { Transform3 t = null; Point3Vector pthis = new Point3Vector(new double[] { 1., -1., 0., 0., 1., -1., -1., 0., 1. }); Point3Vector pref = new Point3Vector(new double[] { 0., 1., -1., 1., -1., 0., -1., 0., 1. }); t = pthis.roughAlign(pref); pthis.transform(t); Point3VectorTest.assertEquals("transformed pthis", pref, pthis, 0.000001); pref = new Point3Vector(new double[] { 12., 0., 1., 11., 2., 0., 10., 1., 2. }); t = pthis.roughAlign(pref); pthis.transform(t); // LOG.debug("PA "+pthis+"\n"+t); RealArray.round(pthis.getArray(), 6); Point3VectorTest.assertEquals("transformed pthis", pref, pthis, 0.000001); } /** * Test method for 'org.xmlcml.euclid.Point3Vector.fitTo(Point3Vector)' */ @Test public void testFitTo() { Transform3 t = null; Point3Vector pa = new Point3Vector(new double[] { 1., 0., 0., 0., 1., 0., 0., 0., 1. }); Point3Vector pb = new Point3Vector(new double[] { 0., 1., 0., 0., 0., 1., 1., 0., 0. }); t = pa.fitTo(pb); pa.transform(t); double rms = pa.rms(pb); Assert.assertEquals("rms", 0.0, rms, 0.000000001); DoubleTestBase.assertEquals("align", new double[] { 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0 }, t .getMatrixAsArray(), EPS); pb = new Point3Vector(new double[] { 10., 1.1, 0., 10., 0., 0.9, 11.1, 0., 0. }); t = pa.fitTo(pb); pa.transform(t); rms = pa.rms(pb); Assert.assertEquals("rms", 0.03470386605101721, rms, 0.0001); } @Test public void calculateInertialTensor() { Point3Vector p3v = new Point3Vector( new double[] { 3., 0., 0., 0., 2., 0., 0., 0., 1., -3., 0., 0., 0., -2., 0., 0., 0., -1.} ); RealSquareMatrix rsm = p3v.calculateNonMassWeightedInertialTensor(); RealSquareMatrixTest.assertEquals("rsm", 3, new double[]{ 10.0, 0.0, 0.0, 0.0, 20.0, 0.0, 0.0, 0.0, 26.0, }, rsm, 0.000001); RealArray realArray = rsm.calculateEigenvalues(); RealArrayTest.assertEquals("eigval ", new double[]{26.0, 20.0, 10.0}, realArray, 0.000001); } @Test public void calculateRotationToInertialAxes() { Point3Vector p3v = new Point3Vector( new double[] { 3., 0., 0., 0., 2., 0., 0., 0., 1., -3., 0., 0., 0., -2., 0., 0., 0., -1.} ); // arbitrary transform Transform3 t = new Transform3(new Angle(0.1), new Angle(0.2), new Angle(0.3)); p3v.transform(t); RealSquareMatrix eigvec = p3v.calculateRotationToInertialAxes(); Transform3 tt = new Transform3(eigvec); p3v.transform(tt); Point3VectorTest.assertEquals("transform to axes ", new double[]{ 0.0, 0.0, 3.0, 0.0, 2.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, -3.0, 0.0, -2.0, 0.0, 1.0, 0.0, 0.0, }, p3v, 0.000001); } } euclid-euclid-2.9/src/test/java/org/xmlcml/euclid/test/PolarTest.java000066400000000000000000000143001461721410700256530ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid.test; import static org.xmlcml.euclid.EC.EPS; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.xmlcml.euclid.Angle; import org.xmlcml.euclid.Complex; import org.xmlcml.euclid.Polar; import org.xmlcml.euclid.Real2; /** * test Polar. * * @author pmr * */ public class PolarTest { Polar p0; Polar p1; Polar p2; /** * setup. * * @throws Exception */ @Before public void setUp() throws Exception { p0 = new Polar(); p1 = new Polar(1, 2); p2 = new Polar(10, new Angle(Math.PI / 3)); } /** * Test method for 'org.xmlcml.euclid.Polar.Polar()' */ @Test public void testPolar() { Assert.assertEquals("polar", 0.0, p0.getTheta().getRadian(), EPS); Assert.assertEquals("polar", 0.0, p0.getR(), EPS); } /** * Test method for 'org.xmlcml.euclid.Polar.Polar(double, double)' */ @Test public void testPolarDoubleDouble() { Assert.assertEquals("polar", Math.atan(2. / 1.), p1.getTheta() .getRadian(), EPS); Assert.assertEquals("polar", Math.sqrt(5.), p1.getR(), EPS); Assert.assertEquals("polar", 1., p1.getX(), EPS); Assert.assertEquals("polar", 2., p1.getY(), EPS); } /** * Test method for 'org.xmlcml.euclid.Polar.Polar(double, Angle)' */ @Test public void testPolarDoubleAngle() { Assert.assertEquals("polar", Math.PI / 3., p2.getTheta().getRadian(), EPS); Assert.assertEquals("polar", 10., p2.getR(), EPS); } /** * Test method for 'org.xmlcml.euclid.Polar.Polar(Complex)' */ @Test public void testPolarComplex() { Polar c = new Polar(new Complex(1., 2.)); Assert.assertEquals("polar", Math.atan(2. / 1.), c.getTheta() .getRadian(), EPS); Assert.assertEquals("polar", Math.sqrt(5.), c.getR(), EPS); } /** * Test method for 'org.xmlcml.euclid.Polar.Polar(Polar)' */ @Test public void testPolarPolar() { Polar p = new Polar(p1); Assert.assertEquals("polar", Math.atan(2. / 1.), p.getTheta() .getRadian(), EPS); Assert.assertEquals("polar", Math.sqrt(5.), p.getR(), EPS); } /** * Test method for 'org.xmlcml.euclid.Polar.getR()' */ @Test public void testGetR() { Assert.assertEquals("polar", Math.sqrt(5.), p1.getR(), EPS); } /** * Test method for 'org.xmlcml.euclid.Polar.getTheta()' */ @Test public void testGetTheta() { Assert.assertEquals("polar", Math.atan(2. / 1.), p1.getTheta() .getRadian(), EPS); } /** * Test method for 'org.xmlcml.euclid.Polar.plus(Polar)' */ @Test public void testPlus() { Polar pa = new Polar(10., 20.); Assert.assertEquals("polar", 10., pa.getX(), EPS); Assert.assertEquals("polar", 20., pa.getY(), EPS); Polar pb = new Polar(30., 40.); Assert.assertEquals("polar", 30., pb.getX(), EPS); Assert.assertEquals("polar", 40., pb.getY(), EPS); Polar pc = pa.plus(pb); Assert.assertEquals("polar", 40., pc.getX(), EPS); Assert.assertEquals("polar", 60., pc.getY(), 2.0 * EPS); } /** * Test method for 'org.xmlcml.euclid.Polar.subtract(Polar)' */ @Test public void testSubtractPolar() { Polar pa = new Polar(10., 20.); Polar pb = new Polar(30., 50.); Polar pc = pa.subtract(pb); Assert.assertEquals("polar", -20., pc.getX(), EPS); Assert.assertEquals("polar", -30., pc.getY(), EPS); } /** * Test method for 'org.xmlcml.euclid.Polar.subtract()' */ @Test public void testSubtract() { Polar pa = new Polar(10., 20.); pa.subtract(); Assert.assertEquals("polar", -10., pa.getX(), EPS); Assert.assertEquals("polar", -20., pa.getY(), EPS); } /** * Test method for 'org.xmlcml.euclid.Polar.multiplyBy(Polar)' */ @Test public void testMultiplyByPolar() { Polar pa = new Polar(10., new Angle(Math.PI / 4.)); Polar pb = new Polar(20., new Angle(Math.PI / 3.)); Polar pc = pa.multiplyBy(pb); Assert.assertEquals("polar", 200., pc.getR(), 1.0E-08); Assert.assertEquals("polar", Math.PI / 4. + Math.PI / 3., pc.getTheta() .getRadian(), 1.0E-08); } /** * Test method for 'org.xmlcml.euclid.Polar.multiplyBy(double)' */ @Test public void testMultiplyByDouble() { Polar pa = new Polar(10., 20.); Polar pb = pa.multiplyBy(3.); Assert.assertEquals("polar", 30., pb.getX(), 1.0E-08); Assert.assertEquals("polar", 60., pb.getY(), 1.0E-08); } /** * Test method for 'org.xmlcml.euclid.Polar.divideBy(Polar)' */ @Test public void testDivideBy() { Polar pa = new Polar(10., new Angle(Math.PI / 4.)); Polar pb = new Polar(20., new Angle(Math.PI / 3.)); Polar pc = pa.divideBy(pb); Assert.assertEquals("polar", 0.5, pc.getR(), 1.0E-08); Assert.assertEquals("polar", Math.PI / 4. - Math.PI / 3., pc.getTheta() .getRadian(), 1.0E-08); } /** * Test method for 'org.xmlcml.euclid.Polar.isEqualTo(Polar)' */ @Test public void testIsEqualTo() { Assert.assertTrue("isEqualTo", p1.isEqualTo(p1)); Assert.assertFalse("isEqualTo", p1.isEqualTo(p2)); } /** * Test method for 'org.xmlcml.euclid.Polar.getX()' */ @Test public void testGetX() { Polar pb = new Polar(20., new Angle(Math.PI / 3.)); Assert.assertEquals("polar", 20 * Math.cos(Math.PI / 3.), pb.getX(), 1.0E-08); } /** * Test method for 'org.xmlcml.euclid.Polar.getY()' */ @Test public void testGetY() { Polar pb = new Polar(20., new Angle(Math.PI / 3.)); Assert.assertEquals("polar", 20 * Math.sin(Math.PI / 3.), pb.getY(), 1.0E-08); } /** * Test method for 'org.xmlcml.euclid.Polar.getXY()' */ @Test public void testGetXY() { Polar pb = new Polar(20., new Angle(Math.PI / 3.)); Real2 r = pb.getXY(); Assert.assertEquals("polar", 20 * Math.cos(Math.PI / 3.), r.getX(), 1.0E-08); Assert.assertEquals("polar", 20 * Math.sin(Math.PI / 3.), r.getY(), 1.0E-08); } } euclid-euclid-2.9/src/test/java/org/xmlcml/euclid/test/Real2ArrayTest.java000066400000000000000000000175001461721410700265470ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid.test; import static org.xmlcml.euclid.EuclidConstants.EPS; import static org.xmlcml.euclid.EuclidConstants.S_PIPE; import static org.xmlcml.euclid.EuclidConstants.S_SPACE; import java.util.Iterator; import org.apache.log4j.Logger; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.xmlcml.euclid.ArrayBase; import org.xmlcml.euclid.EC; import org.xmlcml.euclid.Real2; import org.xmlcml.euclid.Real2Array; import org.xmlcml.euclid.Real2Range; import org.xmlcml.euclid.RealArray; import org.xmlcml.euclid.RealRange; /** * test Real2Array * * @author pmr * */ public class Real2ArrayTest { private final static Logger LOG = Logger.getLogger(Real2ArrayTest.class); Real2Array ra0; Real2Array ra1; /** * setup. * * @throws Exception */ @Before public void setUp() throws Exception { ra0 = new Real2Array(); ra1 = new Real2Array(new RealArray(new double[] { 1, 2, 3, 4, 5, 6 }), new RealArray(new double[] { 11, 12, 13, 14, 15, 16 })); } /** * Test method for 'org.xmlcml.euclid.Real2Array.Real2Array()' */ @Test public void testReal2Array() { Assert.assertEquals("empty", "()", ra0.toString()); } /** * Test method for 'org.xmlcml.euclid.Real2Array.getRange2()' */ @Test public void testGetRange2() { Real2Range real2Range = ra1.getRange2(); Assert.assertTrue("range2", real2Range.isEqualTo(new Real2Range( new RealRange(1, 6), new RealRange(11, 16)), 0.001)); } /** * Test method for 'org.xmlcml.euclid.Real2Array.Real2Array(RealArray, * RealArray)' */ @Test public void testReal2ArrayRealArrayRealArray() { Assert.assertEquals("realArrays", EC.S_LBRAK + "(1.0,11.0)" + "(2.0,12.0)" + "(3.0,13.0)" + "(4.0,14.0)" + "(5.0,15.0)" + "(6.0,16.0)" + EC.S_RBRAK, ra1.toString()); } /** * Test method for 'org.xmlcml.euclid.Real2Array.getXArray()' */ @Test public void testGetXArray() { RealArray xarr = ra1.getXArray(); Assert.assertTrue("getXArray", xarr.isEqualTo(new RealArray( new double[] { 1., 2., 3., 4., 5., 6. }))); } /** * Test method for 'org.xmlcml.euclid.Real2Array.getYArray()' */ @Test public void testGetYArray() { RealArray yarr = ra1.getYArray(); Assert.assertTrue("getYArray", yarr.isEqualTo(new RealArray( new double[] { 11., 12., 13., 14., 15., 16. }))); } /** * Test method for 'org.xmlcml.euclid.Real2Array.size()' */ @Test public void testSize() { Assert.assertEquals("size", 6, ra1.size()); } @Test public void testSumProductOfAllElements() { Assert.assertEquals("sum", 301., ra1.sumProductOfAllElements(), 0.001); } @Test public void testCreateFromPairs() { String s = "1,2 3,4 5,6 7,8"; Real2Array real2Array = Real2Array.createFromPairs(s, ArrayBase.ARRAY_REGEX); Assert.assertEquals("size", 4, real2Array.size()); RealArray xarr = real2Array.getXArray(); Assert.assertTrue("getXArray", xarr.isEqualTo(new RealArray( new double[] { 1., 3., 5., 7. }))); RealArray yarr = real2Array.getYArray(); Assert.assertTrue("getYArray", yarr.isEqualTo(new RealArray( new double[] { 2., 4., 6., 8. }))); } /** * Test method for 'org.xmlcml.euclid.Real2Array.elementAt(int)' */ @Test public void testElementAt() { Real2 real2 = ra1.elementAt(4); Assert.assertEquals("elementAt", 5., real2.getX(), EPS); Assert.assertEquals("elementAt", 15., real2.getY(), EPS); } @Test public void testCreateFromCoords() { String coords = "((112.559,238.695)(121.217,238.695)(129.215,238.695)(139.877,238.695)(146.543,238.695)(149.543,238.695)(153.533,238.695)(160.295,238.695)(167.621,238.695)(176.279,238.695)(182.876,238.695)(186.836,238.695)(197.498,238.695)(204.164,238.695)(208.154,238.695)(211.199,238.695)(219.863,238.695)(223.199,238.695)(227.879,238.695)(230.879,238.695)(236.189,238.695)(241.499,238.695)(244.817,238.695)(250.127,238.695)(256.109,238.695)(259.091,238.695)(262.091,238.695)(266.069,238.695)(272.051,238.695)(276.029,238.695)(279.029,238.695))" ; Real2Array real2Array = Real2Array.createFromCoords(coords); Assert.assertNotNull("coords", real2Array); Assert.assertEquals("coords", 31, real2Array.size()); Assert.assertTrue("coords 0, found: "+real2Array.get(0), new Real2(112.559,238.695).isEqualTo(real2Array.get(0), 0.001)); } @Test public void testGetMidPointArray() { Real2Array r2array0 = new Real2Array( new RealArray(new double[]{1., 2., 3., 4.}), new RealArray(new double[]{21., 22., 23., 24.}) ); Real2Array r2array1 = new Real2Array( new RealArray(new double[]{21., 22., 23., 24.}), new RealArray(new double[]{41., 42., 43., 44.}) ); Real2Array midArray = r2array0.getMidPointArray(r2array1); LOG.trace("mid "+midArray); Assert.assertEquals("mid", "((11.0,31.0)(12.0,32.0)(13.0,33.0)(14.0,34.0))", midArray.toString()); } @Test public void testIterator() { RealArray xArray = new RealArray(new double[]{0.,1.,2.}); RealArray yArray = new RealArray(new double[]{10.,11.,12.}); Real2Array real2Array = new Real2Array(xArray, yArray); Iterator realIterator = real2Array.iterator(); Assert.assertTrue("start", realIterator.hasNext()); Assert.assertTrue("start", realIterator.hasNext()); Assert.assertTrue("start", realIterator.hasNext()); Assert.assertTrue("start", realIterator.hasNext()); Assert.assertTrue("start", new Real2(0., 10.).isEqualTo(realIterator.next(), 0.001)); Assert.assertTrue("start", new Real2(1., 11.).isEqualTo(realIterator.next(), 0.001)); Assert.assertTrue("after 1", realIterator.hasNext()); Assert.assertTrue("after 1", new Real2(2., 12.).isEqualTo(realIterator.next(), 0.001)); Assert.assertFalse("end", realIterator.hasNext()); Assert.assertNull("after 2", realIterator.next()); } @Test public void testIterators() { RealArray realArray = new RealArray(new double[]{0,1,2}); Iterator realIterator00 = realArray.iterator(); Iterator realIterator01 = realArray.iterator(); Assert.assertTrue("start", realIterator00.hasNext()); Assert.assertEquals("start", 0., (double) realIterator00.next(), 0.001); Assert.assertEquals("start", 1., (double) realIterator00.next(), 0.001); Assert.assertEquals("start", 0., (double) realIterator01.next(), 0.001); Assert.assertEquals("end0", 2., (double) realIterator00.next(), 0.001); Assert.assertFalse("end0", realIterator00.hasNext()); Assert.assertTrue("middle1", realIterator01.hasNext()); Assert.assertNull("endo", realIterator00.next()); Assert.assertEquals("start", 1., (double) realIterator01.next(), 0.001); } @Test public void testSort() { Real2Array r2a = new Real2Array(); r2a.add(new Real2(1.0, 9.0)); r2a.add(new Real2(7.0, 3.0)); r2a.add(new Real2(2.0, 8.0)); r2a.add(new Real2(5.0, 1.0)); r2a.add(new Real2(4.0, 7.0)); r2a.add(new Real2(8.0, 6.0)); r2a.sortAscending(0); Assert.assertEquals("xa", "((1.0,9.0)(2.0,8.0)(4.0,7.0)(5.0,1.0)(7.0,3.0)(8.0,6.0))", r2a.toString()); r2a.sortAscending(1); Assert.assertEquals("xa", "((5.0,1.0)(7.0,3.0)(8.0,6.0)(4.0,7.0)(2.0,8.0)(1.0,9.0))", r2a.toString()); r2a.sortDescending(0); Assert.assertEquals("xa", "((8.0,6.0)(7.0,3.0)(5.0,1.0)(4.0,7.0)(2.0,8.0)(1.0,9.0))", r2a.toString()); r2a.sortDescending(1); Assert.assertEquals("xa", "((1.0,9.0)(2.0,8.0)(4.0,7.0)(8.0,6.0)(7.0,3.0)(5.0,1.0))", r2a.toString()); } } euclid-euclid-2.9/src/test/java/org/xmlcml/euclid/test/Real2RangeComparatorTest.java000077500000000000000000000077321461721410700305660ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid.test; import java.util.Set; import java.util.TreeSet; import org.junit.Assert; import org.junit.Test; import org.xmlcml.euclid.Real2Range; import org.xmlcml.euclid.Real2RangeComparator; import org.xmlcml.euclid.RealComparator; import org.xmlcml.euclid.RealRange; import org.xmlcml.euclid.RealRangeComparator; public class Real2RangeComparatorTest { public final static Double ZERO = 0.0; public final static Double EPS = 0.01; public final static Double ONE = 1.0; public final static Double TWO = 2.0; public final static Double THREE = 3.0; public final static RealRange ONE_TWO = new RealRange(ONE, TWO); public final static RealRange ZERO_ONE = new RealRange(ZERO, ONE); public final static Real2Range ONE_TWO_ONE_TWO = new Real2Range(ONE_TWO, ONE_TWO); public final static Real2Range ZERO_ONE_ONE_TWO = new Real2Range(ZERO_ONE, ONE_TWO); public final static Real2Range ONE_TWO_ZERO_ONE = new Real2Range(ONE_TWO, ZERO_ONE); public final static Double SQR3 = Math.sqrt(THREE); public final static RealComparator RZERO = new RealComparator(0.0); public final static RealComparator REPS = new RealComparator(EPS); public final static RealRangeComparator RRZERO = new RealRangeComparator(RZERO); public final static RealRangeComparator RREPS = new RealRangeComparator(REPS); @Test public void testDummy() { } @Test public void comparatorTest() { Real2RangeComparator comparator = new Real2RangeComparator(RRZERO); Assert.assertEquals(0, comparator.compare(ONE_TWO_ONE_TWO, ONE_TWO_ONE_TWO)); Assert.assertEquals(-1, comparator.compare(ZERO_ONE_ONE_TWO, ONE_TWO_ONE_TWO)); Assert.assertEquals(-1, comparator.compare(ONE_TWO_ONE_TWO, ZERO_ONE_ONE_TWO)); Assert.assertEquals(-1, comparator.compare(ONE_TWO_ONE_TWO, ONE_TWO_ZERO_ONE)); } @Test public void comparatorTest1() { Real2RangeComparator comparator = new Real2RangeComparator(RREPS); Assert.assertEquals(0, comparator.compare(ONE_TWO_ONE_TWO, new Real2Range(ONE_TWO, new RealRange(ONE-EPS/2., TWO+EPS/2.)))); // both min and max in first range are larger Assert.assertEquals(-1, comparator.compare(ONE_TWO_ONE_TWO, new Real2Range(new RealRange(ONE-EPS*2., TWO-EPS*2.), ONE_TWO))); // both min and max in first range are smaller Assert.assertEquals(-1, comparator.compare(ONE_TWO_ONE_TWO, new Real2Range(new RealRange(ONE-EPS*2., TWO-EPS*2.), ONE_TWO))); // this gives -1 because there is a disagreement Assert.assertEquals(-1, comparator.compare(ONE_TWO_ONE_TWO, new Real2Range(new RealRange(ONE-EPS*2., TWO+EPS*2.), ONE_TWO))); } /** TreeSet works * */ @Test public void testTreeSet(){ Real2RangeComparator comparator = new Real2RangeComparator(RRZERO); Set set = new TreeSet(comparator); set.add(ONE_TWO_ONE_TWO); set.add(ZERO_ONE_ONE_TWO); set.add(ONE_TWO_ONE_TWO); set.add(new Real2Range(ONE_TWO, new RealRange(ONE, TWO-0.001))); Assert.assertEquals(3, set.size()); } /** TreeSet works * */ @Test public void testTreeSet1(){ Real2RangeComparator comparator = new Real2RangeComparator(RREPS); Set set = new TreeSet(comparator); set.add(ONE_TWO_ONE_TWO); set.add(ZERO_ONE_ONE_TWO); set.add(ONE_TWO_ONE_TWO); set.add(new Real2Range(ONE_TWO, new RealRange(ONE, TWO-0.001))); set.add(new Real2Range(new RealRange(ONE, TWO-0.001), ONE_TWO)); Assert.assertEquals(2, set.size()); } } euclid-euclid-2.9/src/test/java/org/xmlcml/euclid/test/Real2RangeTest.java000066400000000000000000000176671461721410700265430ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid.test; import java.util.ArrayList; import java.util.List; import org.junit.Assert; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.xmlcml.euclid.Real2; import org.xmlcml.euclid.Real2Range; import org.xmlcml.euclid.RealRange; /** * test Real2Range. * * @author pmr * */ public class Real2RangeTest { Real2Range i2r0; Real2Range i2r1; Real2Range i2r2; /** * setup. * * @throws Exception */ @Before public void setUp() throws Exception { i2r0 = new Real2Range(); i2r1 = new Real2Range(new RealRange(1.0, 2.0), new RealRange(1.0, 2.0)); i2r2 = new Real2Range(new RealRange(1.0, 2.0), new RealRange(3.0, 4.0)); } /** * Test method for 'org.xmlcml.euclid.Real2Range.Real2Range()' */ @Test public void testReal2Range() { Assert.assertEquals("empty", "(NULL,NULL)", i2r0.toString()); } /** * Test method for 'org.xmlcml.euclid.Real2Range.Real2Range(RealRange, * RealRange)' */ @Test public void testReal2RangeRealRangeRealRange() { Assert.assertEquals("real range", "((1.0,2.0),(3.0,4.0))", i2r2 .toString()); } /** * Test method for 'org.xmlcml.euclid.Real2Range.Real2Range(Real2Range)' */ @Test public void testReal2RangeReal2Range() { Real2Range ii = new Real2Range(i2r2); Assert.assertEquals("empty", "((1.0,2.0),(3.0,4.0))", ii.toString()); } /** * Test method for 'org.xmlcml.euclid.Real2Range.isValid()' */ @Test public void testIsValid() { Assert.assertTrue("valid", i2r2.isValid()); Assert.assertFalse("invalid", i2r0.isValid()); } /** * Test method for 'org.xmlcml.euclid.Real2Range.isEqualTo(Real2Range)' */ @Test public void testIsEqualTo() { Assert.assertTrue("isEqual", i2r2.isEqualTo(i2r2, 0.001)); Assert.assertFalse("isEqual", i2r2.isEqualTo(i2r1, 0.001)); Assert.assertFalse("isEqual", i2r0.isEqualTo(i2r0, 0.001)); } /** * Test method for 'org.xmlcml.euclid.Real2Range.plus(Real2Range)' */ @Test public void testPlus() { Real2Range ix = new Real2Range(new RealRange(1.0, 4.0), new RealRange( 11.0, 14.0)); Real2Range iy = new Real2Range(new RealRange(2.0, 5.0), new RealRange( 12.0, 15.0)); Real2Range ii = ix.plus(iy); Assert.assertEquals("plus", "((1.0,5.0),(11.0,15.0))", ii.toString()); iy = new Real2Range(new RealRange(2.0, 3.0), new RealRange(12.0, 13.0)); ii = ix.plus(iy); Assert.assertEquals("plus", "((1.0,4.0),(11.0,14.0))", ii.toString()); iy = new Real2Range(new RealRange(0.0, 8.0), new RealRange(10.0, 18.0)); ii = ix.plus(iy); Assert.assertEquals("plus", "((0.0,8.0),(10.0,18.0))", ii.toString()); } /** * Test method for * 'org.xmlcml.euclid.Real2Range.doubleersectionWith(Real2Range)' */ @Test public void testIntersectionWith() { Real2Range ix = new Real2Range(new RealRange(1.0, 4.0), new RealRange( 11.0, 14.0)); Real2Range iy = new Real2Range(new RealRange(2.0, 5.0), new RealRange( 12.0, 15.0)); Real2Range ii = ix.intersectionWith(iy); Assert.assertEquals("plus", "((2.0,4.0),(12.0,14.0))", ii.toString()); iy = new Real2Range(new RealRange(2.0, 3.0), new RealRange(12.0, 13.0)); ii = ix.intersectionWith(iy); Assert.assertEquals("plus", "((2.0,3.0),(12.0,13.0))", ii.toString()); iy = new Real2Range(new RealRange(0.0, 8.0), new RealRange(10.0, 18.0)); ii = ix.intersectionWith(iy); Assert.assertEquals("plus", "((1.0,4.0),(11.0,14.0))", ii.toString()); } /** * Test method for 'org.xmlcml.euclid.Real2Range.getXRange()' */ @Test public void testGetXRange() { Assert.assertNull("getXRange", i2r0.getXRange()); Assert.assertEquals("getXRange", "(1.0,2.0)", i2r2.getXRange() .toString()); } /** * Test method for 'org.xmlcml.euclid.Real2Range.getYRange()' */ @Test public void testGetYRange() { Assert.assertNull("getXRange", i2r0.getYRange()); Assert.assertEquals("getXRange", "(3.0,4.0)", i2r2.getYRange() .toString()); } /** * Test method for 'org.xmlcml.euclid.Real2Range.includes(Real2)' */ @Test public void testIncludesReal2() { Real2Range ix = new Real2Range(new RealRange(1.0, 4.0), new RealRange( 11.0, 14.0)); Assert.assertTrue("include", ix.includes(new Real2(2.0, 12.0))); Assert.assertTrue("include", ix.includes(new Real2(1.0, 11.0))); Assert.assertTrue("include", ix.includes(new Real2(4.0, 14.0))); Assert.assertFalse("include", ix.includes(new Real2(1.0, 15.0))); } /** * Test method for 'org.xmlcml.euclid.Real2Range.includes(Real2Range)' */ @Test public void testIncludesReal2Range() { Real2Range ix = new Real2Range(new RealRange(1.0, 4.0), new RealRange( 11.0, 14.0)); Assert.assertTrue("include", ix.includes(new Real2Range(new RealRange( 2.0, 3.0), new RealRange(12.0, 13.0)))); Assert.assertTrue("include", ix.includes(new Real2Range(new RealRange( 1.0, 4.0), new RealRange(11.0, 14.0)))); Assert.assertFalse("include", ix.includes(new Real2Range(new RealRange( 0.0, 4.0), new RealRange(10.0, 14.0)))); Assert.assertFalse("include", ix.includes(new Real2Range(new RealRange( 2.0, 5.0), new RealRange(12.0, 15.0)))); } /** * Test method for 'org.xmlcml.euclid.Real2Range.add(Real2)' */ @Test public void testAdd() { Real2Range ii = new Real2Range(new RealRange(1.0, 4.0), new RealRange( 11.0, 14.0)); Assert.assertEquals("plus", "((1.0,4.0),(11.0,14.0))", ii.toString()); Real2 i2 = new Real2(2.0, 12.0); ii.add(i2); Assert.assertEquals("plus", "((1.0,4.0),(11.0,14.0))", ii.toString()); i2 = new Real2(0.0, 15.0); ii.add(i2); Assert.assertEquals("plus", "((0.0,4.0),(11.0,15.0))", ii.toString()); i2 = new Real2(8.0, 7.0); ii.add(i2); Assert.assertEquals("plus", "((0.0,8.0),(7.0,15.0))", ii.toString()); } @Test public void testIsHorizontal() { Real2Range r2r = new Real2Range(new RealRange(0., 200.), new RealRange(0., 0.5)); Assert.assertTrue("horizontal", r2r.isHorizontal()); Assert.assertFalse("vertical", r2r.isVertical()); } @Test public void testIsHorizontal1() { Real2Range r2r = new Real2Range(new RealRange(0., 200.), new RealRange(0., 200.)); Assert.assertFalse("horizontal", r2r.isHorizontal()); Assert.assertFalse("vertical", r2r.isVertical()); } @Test public void testHorizontalVerticalRatio() { Real2Range r2r = new Real2Range(new RealRange(0., 100.), new RealRange(0., 1.)); Assert.assertTrue("horizontal", r2r.isHorizontal()); Assert.assertEquals("aspect ratio", 100., r2r.getHorizontalVerticalRatio(), 0.001); } @Test public void testIsContainedIn() { List r2rList = new ArrayList(); Real2Range box = new Real2Range(new RealRange(50, 60),new RealRange(70, 80)); r2rList.add(new Real2Range(new RealRange(0, 30),new RealRange(0, 100))); Assert.assertFalse(box.isContainedInAnyRange(r2rList)); r2rList.add(new Real2Range(new RealRange(0, 100),new RealRange(0, 30))); Assert.assertFalse(box.isContainedInAnyRange(r2rList)); r2rList.add(new Real2Range(new RealRange(40, 80),new RealRange(40, 80))); Assert.assertTrue(box.isContainedInAnyRange(r2rList)); } @Test public void testArea() { Real2Range r2r = new Real2Range(new RealRange(1.0, 4.0), new RealRange( 11.0, 24.0)); Assert.assertEquals("area", 39.0, (double) r2r.calculateArea(), 0.01); } @Test @Ignore //null in constructor fails public void testAreaNull() { Real2Range r2r = new Real2Range(null, new RealRange( 11.0, 24.0)); Assert.assertNull("area", r2r.calculateArea()); } } euclid-euclid-2.9/src/test/java/org/xmlcml/euclid/test/Real2Test.java000066400000000000000000000273421461721410700255550ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid.test; import static org.xmlcml.euclid.EC.EPS; import static org.xmlcml.euclid.EC.S_RBRAK; import java.util.ArrayList; import java.util.List; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.xmlcml.euclid.Angle; import org.xmlcml.euclid.Real2; import org.xmlcml.euclid.RealMatrix; import org.xmlcml.euclid.Transform2; /** * tests real2 * * @author pmr * */ public class Real2Test { Real2 r0; Real2 r11; Real2 r12; List real2List; /** * setup. * * @throws Exception */ @Before public void setUp() throws Exception { r0 = new Real2(); r11 = new Real2(1.0, 1.0); r12 = new Real2(1.0, 2.0); } /** * equality test. true if both args not null and equal within epsilon * * @param msg * message * @param test * @param expected * @param epsilon */ public static void assertEquals(String msg, Real2 test, Real2 expected, double epsilon) { Assert.assertNotNull("test should not be null (" + msg + S_RBRAK, test); Assert.assertNotNull("expected should not be null (" + msg + S_RBRAK, expected); DoubleTestBase.assertEquals(msg, test.getXY(), expected.getXY(), epsilon); } /** * equality test. true if both args not null and equal within epsilon * * @param msg * message * @param test * array must be of length 2 * @param expected * @param epsilon */ public static void assertEquals(String msg, double[] test, Real2 expected, double epsilon) { Assert.assertNotNull("test should not be null (" + msg + S_RBRAK, test); Assert.assertEquals("must be of length 2", 2, test.length); Assert.assertNotNull("ref should not be null (" + msg + S_RBRAK, expected); DoubleTestBase.assertEquals(msg, test, expected.getXY(), epsilon); } /** * Test method for 'org.xmlcml.euclid.Real2.Real2()' */ @Test public void testReal2() { Assert.assertEquals("double2", "(0.0,0.0)", r0.toString()); } /** * Test method for 'org.xmlcml.euclid.Real2.Real2(double, double)' */ @Test public void testReal2RealReal() { Assert.assertEquals("double2", "(1.0,2.0)", r12.toString()); } /** * Test method for 'org.xmlcml.euclid.Real2.Real2(Real2)' */ @Test public void testReal2Real2() { Real2 ii = new Real2(r12); Assert.assertEquals("double2", "(1.0,2.0)", ii.toString()); } /** * Test method for 'org.xmlcml.euclid.Real2.swap()' */ @Test public void testSwap() { r12.swap(); Assert.assertEquals("double2", "(2.0,1.0)", r12.toString()); } /** * Test method for 'org.xmlcml.euclid.Real2.sortAscending()' */ @Test public void testSortAscending() { r12.sortAscending(); Assert.assertEquals("double2", "(1.0,2.0)", r12.toString()); } /** * Test method for 'org.xmlcml.euclid.Real2.sortDescending()' */ @Test public void testSortDescending() { r12.sortDescending(); Assert.assertEquals("double2", "(2.0,1.0)", r12.toString()); } /** * Test method for 'org.xmlcml.euclid.Real2.clear()' */ @Test public void testClear() { r12.clear(); Assert.assertEquals("double2", "(0.0,0.0)", r12.toString()); } /** * Test method for 'org.xmlcml.euclid.Real2.setX(double)' */ @Test public void testSetX() { r12.setX(3); Assert.assertEquals("double2", "(3.0,2.0)", r12.toString()); } /** * Test method for 'org.xmlcml.euclid.Real2.setY(double)' */ @Test public void testSetY() { r12.setY(3); Assert.assertEquals("double2", "(1.0,3.0)", r12.toString()); } // /** // * Test method for 'org.xmlcml.euclid.Real2.isEqualTo(Real2)' // */ // @Test // public void testIsEqualTo() { // Assert.assertTrue("equals", r12.isEqualTo(r12)); // Assert.assertFalse("equals", r11.isEqualTo(r12)); // } /** * Test method for 'org.xmlcml.euclid.Real2.plus(Real2)' */ @Test public void testPlus() { Real2 ii = r12.plus(r11); Assert.assertEquals("plus", "(2.0,3.0)", ii.toString()); } /** * Test method for 'org.xmlcml.euclid.Real2.subtract(Real2)' */ @Test public void testSubtract() { Real2 ii = r12.subtract(r11); Assert.assertEquals("subtract", "(0.0,1.0)", ii.toString()); } /** * Test method for 'org.xmlcml.euclid.Real2.negative()' */ @Test public void testNegative() { r12.negative(); Assert.assertEquals("negative", "(-1.0,-2.0)", r12.toString()); } /** * Test method for 'org.xmlcml.euclid.Real2.multiplyBy(double)' */ @Test public void testMultiplyBy() { Real2 ii = r12.multiplyBy(3); Assert.assertEquals("multiply", "(3.0,6.0)", ii.toString()); } /** * Test method for 'org.xmlcml.euclid.Real2.getX()' */ @Test public void testGetX() { Assert.assertEquals("getX", 1.0, r12.getX(), EPS); } /** * Test method for 'org.xmlcml.euclid.Real2.getY()' */ @Test public void testGetY() { Assert.assertEquals("getY", 2.0, r12.getY(), EPS); } /** * Test method for 'org.xmlcml.euclid.Real2.elementAt(double)' */ @Test public void testElementAt() { Assert.assertEquals("elementAt", 1.0, r12.elementAt(0), EPS); Assert.assertEquals("elementAt", 2.0, r12.elementAt(1), EPS); } /** * Test method for 'org.xmlcml.euclid.Real2.getMidPodouble(Real2)' */ @Test public void testGetMidPoint() { Real2 m = r12.getMidPoint(new Real2(3.0, 4.0)); Assert.assertEquals("mid point", "(2.0,3.0)", m.toString()); } /** * Test method for 'org.xmlcml.euclid.Real2.dotProduct(Real2)' */ @Test public void testDotProduct() { double i = r12.dotProduct(new Real2(3.0, 4.0)); Assert.assertEquals("dot", 11, i, EPS); } /** * Test method for 'org.xmlcml.euclid.Real2.toString()' */ @Test public void testToString() { Assert.assertEquals("toString", "(1.0,2.0)", r12.toString()); } /** * Test method for 'org.xmlcml.euclid.Real2.getLength()' */ @Test public void testGetLength() { Assert.assertEquals("length", Math.sqrt(5.), r12.getLength(), EPS); } /** * Test method for 'org.xmlcml.euclid.Real2.getDistance(Real2)' */ @Test public void testGetDistance() { Assert.assertEquals("distance", Math.sqrt(1.), r12.getDistance(r11), EPS); } /** * Test method for 'org.xmlcml.euclid.Real2.getSquaredDistance(Real2)' */ @Test public void testGetSquaredDistance() { Assert.assertEquals("squared distance", Math.sqrt(1.), r12 .getSquaredDistance(r11), EPS); } /** * Test method for 'org.xmlcml.euclid.Real2.getUnitVector()' */ @Test public void testGetUnitVector() { Real2 unit = r12.getUnitVector(); Assert.assertEquals("vector", Math.sqrt(1. / 5.), unit.getX(), EPS); Assert.assertEquals("vector", Math.sqrt(4. / 5.), unit.getY(), EPS); } /** * Test method for 'org.xmlcml.euclid.Real2.getAngle(Real2, Real2, Real2)' */ @Test public void testGetAngleReal2Real2Real2() { Real2 p1 = new Real2(1. + Math.cos(Math.PI / 3), 2. - Math .sin(Math.PI / 3)); Real2 p2 = new Real2(1., 2.); Real2 p3 = new Real2(1. + Math.cos(Math.PI / 3), 2. + Math .sin(Math.PI / 3)); Angle a = Real2.getAngle(p1, p2, p3); Assert.assertEquals("angle", 2. * Math.PI / 3, a.getAngle(), EPS); p1 = new Real2(0., 1.); p3 = new Real2(1., 0.); p2 = new Real2(0., 0.); a = Real2.getAngle(p1, p2, p3); Assert.assertEquals("angle", - Math.PI / 2., a.getAngle(), EPS); p1 = new Real2(0., 1.); p3 = new Real2(1., 1.); p2 = new Real2(0., 0.); a = Real2.getAngle(p1, p2, p3); Assert.assertEquals("angle", - Math.PI / 4., a.getAngle(), EPS); p1 = new Real2(0., 1.); p3 = new Real2(Math.sqrt(3.)/2., 0.5); p2 = new Real2(0., 0.); a = Real2.getAngle(p1, p2, p3); Assert.assertEquals("angle", - Math.PI / 3., a.getAngle(), EPS); p1 = new Real2(0., 1.); p3 = new Real2(0.5, Math.sqrt(3.)/2.); p2 = new Real2(0., 0.); a = Real2.getAngle(p1, p2, p3); Assert.assertEquals("angle", - Math.PI / 6., a.getAngle(), EPS); } /** * Test method for 'org.xmlcml.euclid.Real2.transformBy(Transform2)' */ @Test public void testTransformBy() { Transform2 t2 = new Transform2(new Angle(Math.PI / 2)); r12.transformBy(t2); Assert.assertEquals("transform", 2.0, r12.getX(), EPS); Assert.assertEquals("transform", -1.0, r12.getY(), EPS); } /** * Test method for 'org.xmlcml.euclid.Real2.getTransformed(Transform2)' */ @Test public void testGetTransformed() { Transform2 t2 = new Transform2(new Angle(Math.PI / 2)); Real2 r2 = r12.getTransformed(t2); Assert.assertEquals("transform", 2.0, r2.getX(), EPS); Assert.assertEquals("transform", -1.0, r2.getY(), EPS); } /** * Test method for 'org.xmlcml.euclid.Real2.addPolygonOnLine(Real2, Real2, * int, int, Real2)' */ // not worth developing // @Test // public void testAddPolygonOnLine() { // //T O D O // // } /** * Test method for 'org.xmlcml.euclid.Real2.getAngle()' */ @Test public void testGetAngle() { double d = r12.getAngle(); Assert.assertEquals("angle", Math.atan2(2., 1.), d, EPS); } /** * Test method for 'org.xmlcml.euclid.Real2.makePoint(double, double)' */ @Test public void testMakePoint() { Real2 real = r12.makePoint(10., Math.PI / 3); Assert.assertEquals("make point", 1. + 10 * Math.cos(Math.PI / 3), real .getX(), EPS); Assert.assertEquals("make point", 2. + 10 * Math.sin(Math.PI / 3), real .getY(), EPS); } /** * Test method for 'org.xmlcml.euclid.Real2.getCentroid(List)' */ @Test public void testGetCentroid() { real2List = new ArrayList(); real2List.add(new Real2(1.0, 2.0)); real2List.add(new Real2(-2.0, 1.0)); real2List.add(new Real2(4.0, 3.0)); Real2 c = Real2.getCentroid(real2List); Assert.assertEquals("centroid", 1., c.getX(), EPS); Assert.assertEquals("centroid", 2., c.getY(), EPS); } /** * Test method for * 'org.xmlcml.euclid.Real2.getSerialOfNearestPoint(List, Real2)' */ @Test public void testGetSerialOfNearestPoint() { real2List = new ArrayList(); real2List.add(new Real2(1.0, 2.0)); real2List.add(new Real2(-2.0, 1.0)); real2List.add(new Real2(4.0, 3.0)); Real2 p = new Real2(-1.6, 0.8); int i = Real2.getSerialOfNearestPoint(real2List, p); Assert.assertEquals("nearest", 1, i); // equidistant from 0 and 2, will choose the first p = new Real2(2.5, 2.5); i = Real2.getSerialOfNearestPoint(real2List, p); Assert.assertEquals("nearest", 0, i); // now choose the nearest p = new Real2(2.5, 2.50001); i = Real2.getSerialOfNearestPoint(real2List, p); Assert.assertEquals("nearest", 2, i); } /** * Test method for 'org.xmlcml.euclid.Real2.getDistanceMatrix(List, * List)' */ @Test public void testGetDistanceMatrix() { real2List = new ArrayList(); real2List.add(new Real2(1.0, 2.0)); real2List.add(new Real2(-2.0, 1.0)); real2List.add(new Real2(4.0, 4.0)); RealMatrix m = Real2.getDistanceMatrix(real2List, real2List); double[] d = new double[9]; d[0] = 0.0; d[1] = Math.sqrt(10.); d[2] = Math.sqrt(13.); d[3] = Math.sqrt(10.); d[4] = 0.0; d[5] = Math.sqrt(45.); d[6] = Math.sqrt(13.); d[7] = Math.sqrt(45.); d[8] = 0.0; RealMatrixTest.assertEquals("distance matrix", 3, 3, d, m, EPS); } @Test public void testFromString() { Real2 real2 = new Real2(1.2, 2.3); String s = real2.toString(); Real2 newReal2 = Real2.createFromString(s); Assert.assertNotNull(newReal2); Real2Test.assertEquals("string", real2, newReal2, 0.001); } } euclid-euclid-2.9/src/test/java/org/xmlcml/euclid/test/Real2VectorTest.java000066400000000000000000000502071461721410700267340ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid.test; import static org.xmlcml.euclid.EuclidConstants.EPS; import java.util.ArrayList; import java.util.List; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.xmlcml.euclid.Angle; import org.xmlcml.euclid.EC; import org.xmlcml.euclid.EuclidRuntimeException; import org.xmlcml.euclid.IntSet; import org.xmlcml.euclid.Real2; import org.xmlcml.euclid.Real2Range; import org.xmlcml.euclid.Real2Vector; import org.xmlcml.euclid.RealArray; import org.xmlcml.euclid.RealMatrix; import org.xmlcml.euclid.RealRange; import org.xmlcml.euclid.Transform2; import org.xmlcml.euclid.Axis.Axis2; /** * tests real2Vector. * * @author pmr * */ public class Real2VectorTest { Real2Vector r0; Real2Vector r1; Real2Vector r2; /** * setup. * * @throws Exception */ @Before public void setUp() throws Exception { r0 = new Real2Vector(); r1 = new Real2Vector(new double[] { 1., 2., 3., 4., 5., 6., }); r2 = new Real2Vector(new double[] { 1., 2., 3., 4., 5., 6., 7., 8., 9., 10. }); } /** * equality test. true if both args not null and equal within epsilon * * @param msg * message * @param test * @param expected * @param epsilon */ public static void assertEquals(String msg, Real2Vector expected, Real2Vector test, double epsilon) { Assert.assertNotNull("test should not be null (" + msg + EC.S_RBRAK, test); Assert.assertNotNull("expected should not be null (" + msg + EC.S_RBRAK, expected); DoubleTestBase.assertEquals(msg, expected.getXY().getArray(), test .getXY().getArray(), epsilon); } /** * equality test. true if both args not null and equal within epsilon * * @param msg * message * @param test * array must be of length 3 * @param expected * @param epsilon */ public static void assertEquals(String msg, double[] test, Real2Vector expected, double epsilon) { Assert.assertNotNull("test should not be null (" + msg + EC.S_RBRAK, test); Assert.assertNotNull("expected should not be null (" + msg + EC.S_RBRAK, expected); Assert.assertEquals("must be of equal length ", test.length, expected .getXY().getArray().length); DoubleTestBase.assertEquals(msg, test, expected.getXY().getArray(), epsilon); } /** * Test method for 'org.xmlcml.euclid.Real2Vector.Real2Vector()' */ @Test public void testReal2Vector() { Assert.assertEquals("r2v", 0, r0.size()); } /** * Test method for 'org.xmlcml.euclid.Real2Vector.Real2Vector(double[])' */ @Test public void testReal2VectorDoubleArray() { Real2Vector r = new Real2Vector(new double[] { 1., 2., 3., 4., 5., 6. }); Assert.assertEquals("r2v", 3, r.size()); try { r = new Real2Vector(new double[] { 1., 2., 3., 4., 5. }); } catch (EuclidRuntimeException e) { Assert.assertEquals("r2v", "size must be multiple of 2", e .getMessage()); } } /** * Test method for 'org.xmlcml.euclid.Real2Vector.Real2Vector(int, double[], * double[])' */ @Test public void testReal2VectorIntDoubleArrayDoubleArray() { Real2Vector r = new Real2Vector(3, new double[] { 1., 2., 3. }, new double[] { 4., 5., 6. }); Assert.assertEquals("r2v", 3, r.size()); try { r = new Real2Vector(3, new double[] { 1., 2., 3. }, new double[] { 4., 5. }); } catch (EuclidRuntimeException e) { Assert.assertEquals("r2v", "array size required (3) found 2", e .getMessage()); } } /** * Test method for 'org.xmlcml.euclid.Real2Vector.Real2Vector(RealArray)' */ @Test public void testReal2VectorRealArray() { Real2Vector r = new Real2Vector(new RealArray(new double[] { 1., 2., 3., 4., 5., 6. })); Assert.assertEquals("r2v", 3, r.size()); try { r = new Real2Vector(new RealArray( new double[] { 1., 2., 3., 4., 5. })); } catch (EuclidRuntimeException e) { Assert.assertEquals("r2v", "size must be multiple of 2", e .getMessage()); } } /** * Test method for 'org.xmlcml.euclid.Real2Vector.Real2Vector(Real2Vector)' */ @Test public void testReal2VectorReal2Vector() { Real2Vector r = new Real2Vector(r1); Assert.assertEquals("r2v", 3, r.size()); } /** * Test method for 'org.xmlcml.euclid.Real2Vector.add(Real2)' */ @Test public void testAdd() { r1.add(new Real2(7., 8.)); Assert.assertEquals("add", 4, r1.size()); Assert.assertEquals("add", 7., r1.get(3).getX(),EPS); Assert.assertEquals("add", 8., r1.get(3).getY(),EPS); } /** * Test method for 'org.xmlcml.euclid.Real2Vector.set(int, Real2)' */ @Test public void testSet() { r1.set(1, new Real2(9., 8.)); Assert.assertEquals("set", 9., r1.get(1).getX(),EPS); Assert.assertEquals("set", 8., r1.get(1).getY(),EPS); Assert.assertEquals("add", 3, r1.size()); } /** * Test method for 'org.xmlcml.euclid.Real2Vector.getRange(Axis2)' */ @Test public void testGetRange() { RealRange r = r1.getRange(Axis2.X); Assert.assertEquals("range", 1., r.getMin(),EPS); Assert.assertEquals("range", 5., r.getMax(),EPS); r1.set(0, new Real2(-9., 8.)); r1.set(1, new Real2(9., 8.)); r = r1.getRange(Axis2.X); Assert.assertEquals("range", -9., r.getMin(),EPS); Assert.assertEquals("range", 9., r.getMax(),EPS); r = r1.getRange(Axis2.Y); Assert.assertEquals("range", 6., r.getMin(),EPS); Assert.assertEquals("range", 8., r.getMax(),EPS); r1.set(0, new Real2(-9., 8.)); r1.set(1, new Real2(9., 8.)); r = r1.getRange(Axis2.Y); Assert.assertEquals("range", 6., r.getMin(),EPS); Assert.assertEquals("range", 8., r.getMax(),EPS); } /** * Test method for 'org.xmlcml.euclid.Real2Vector.getRange2()' */ @Test public void testGetRange2() { Real2Range r2 = r1.getRange2(); RealRange xr = r2.getXRange(); RealRange yr = r2.getYRange(); Assert.assertEquals("range", 1., xr.getMin(),EPS); Assert.assertEquals("range", 5., xr.getMax(),EPS); Assert.assertEquals("range", 2., yr.getMin(),EPS); Assert.assertEquals("range", 6., yr.getMax(),EPS); r1.set(0, new Real2(-9., 8.)); r1.set(1, new Real2(9., 8.)); r2 = r1.getRange2(); xr = r2.getXRange(); yr = r2.getYRange(); Assert.assertEquals("range", -9., xr.getMin(),EPS); Assert.assertEquals("range", 9., xr.getMax(),EPS); Assert.assertEquals("range", 6., yr.getMin(),EPS); Assert.assertEquals("range", 8., yr.getMax(),EPS); } /** * Test method for 'org.xmlcml.euclid.Real2Vector.subArray(IntSet)' */ @Test public void testSubArray() { Real2Vector sub = r2.subArray(new IntSet(new int[] { 3, 1, 2 })); Real2VectorTest.assertEquals("sub", new double[] { 7., 8., 3., 4., 5., 6. }, sub, EPS); } /** * Test method for 'org.xmlcml.euclid.Real2Vector.subSet(Real2Range)' */ @Test public void testSubSet() { Real2Range rr = new Real2Range(new RealRange(2.5, 7.5), new RealRange( 2.5, 7.5)); IntSet is = null; is = r2.subSet(rr); IntSetTest.assertEquals("sub", new int[] { 1, 2 }, is); } /** * Test method for 'org.xmlcml.euclid.Real2Vector.getClosestPoint(Real2)' */ @Test public void testGetClosestPoint() { int ip = r2.getClosestPoint(new Real2(3.5, 6.7)); Assert.assertEquals("closest", 2, ip); } /** * Test method for 'org.xmlcml.euclid.Real2Vector.getPoint(Real2, double, * double)' */ @Test public void testGetPoint() { int ip = r2.getPoint(new Real2(4., 5.), 0.5, 0.5); Assert.assertEquals("closest", -1, ip); ip = r2.getPoint(new Real2(4., 5.), 2.0, 2.0); Assert.assertEquals("closest", 1, ip); } /** * Test method for 'org.xmlcml.euclid.Real2Vector.translateBy(Real2)' */ @Test public void testTranslateBy() { r2.translateBy(new Real2(2., 3.)); Real2VectorTest.assertEquals("translate", new double[] { 3., 5., 5., 7., 7., 9., 9., 11., 11., 13. }, r2, EPS); } /** * Test method for 'org.xmlcml.euclid.Real2Vector.plus(Real2)' */ @Test public void testPlus() { r2.plus(new Real2(2., 3.)); Real2VectorTest.assertEquals("plus", new double[] { 3., 5., 5., 7., 7., 9., 9., 11., 11., 13. }, r2, EPS); } /** * Test method for 'org.xmlcml.euclid.Real2Vector.subtract(Real2)' */ @Test public void testSubtract() { r2.subtract(new Real2(2., 3.)); Real2VectorTest.assertEquals("subtract", new double[] { -1., -1., 1., 1., 3., 3., 5., 5., 7., 7. }, r2, EPS); } /** * Test method for 'org.xmlcml.euclid.Real2Vector.multiplyBy(double)' */ @Test public void testMultiplyBy() { r2.multiplyBy(2.); Real2VectorTest.assertEquals("subtract", new double[] { 2., 4., 6., 8., 10., 12., 14., 16., 18., 20. }, r2, EPS); } /** * Test method for 'org.xmlcml.euclid.Real2Vector.distance(int, int)' */ @Test public void testDistanceIntInt() { double d = r2.distance(2, 3); Assert.assertEquals("distance", Math.sqrt(8.), d, EPS); } /** * Test method for 'org.xmlcml.euclid.Real2Vector.distance(IntSet)' */ @Test public void testDistanceIntSet() { double d = r2.distance(new IntSet(new int[] { 2, 3 })); Assert.assertEquals("distance", Math.sqrt(8.), d, EPS); } /** * Test method for 'org.xmlcml.euclid.Real2Vector.angle(int, int, int)' */ @Test public void testAngleIntIntInt() { Angle a = r2.angle(1, 2, 3); Assert.assertEquals("angle", -Math.PI, a.getRadian(), EPS); } /** * Test method for 'org.xmlcml.euclid.Real2Vector.angle(IntSet)' */ @Test public void testAngleIntSet() { Angle a = r2.angle(new IntSet(new int[] { 1, 2, 3 })); Assert.assertEquals("angle", -Math.PI, a.getRadian(), EPS); } /** * Test method for 'org.xmlcml.euclid.Real2Vector.getReal2(int)' */ @Test public void testGetReal2() { Real2 r = r2.getReal2(1); Assert.assertEquals("angle", 3., r.getX(), EPS); Assert.assertEquals("angle", 4., r.getY(), EPS); } /** * Test method for 'org.xmlcml.euclid.Real2Vector.getXY()' */ @Test public void testGetXY() { RealArray ra = r2.getXY(); RealArrayTest.assertEquals("getXY", new double[] { 1., 2., 3., 4., 5., 6., 7., 8., 9., 10. }, ra, EPS); } /** * Test method for 'org.xmlcml.euclid.Real2Vector.getCoordinate(int, Axis2)' */ @Test public void testGetCoordinate() { double d = r2.getCoordinate(2, Axis2.X); Assert.assertEquals("coord", 5., d, EPS); d = r2.getCoordinate(3, Axis2.Y); Assert.assertEquals("coord", 8., d, EPS); } /** * Test method for 'org.xmlcml.euclid.Real2Vector.getXorY(Axis2)' */ @Test public void testGetXorY() { RealArray ra = r2.getXorY(Axis2.X); RealArrayTest.assertEquals("Xarray", new double[] { 1., 3., 5., 7., 9. }, ra, EPS); ra = r2.getXorY(Axis2.Y); RealArrayTest.assertEquals("Yarray", new double[] { 2., 4., 6., 8., 10. }, ra, EPS); } /** * Test method for 'org.xmlcml.euclid.Real2Vector.swapXY()' */ @Test public void testSwapXY() { r2.swapXY(); Real2VectorTest.assertEquals("getXY", new double[] { 2., 1., 4., 3., 6., 5., 8., 7., 10., 9. }, r2, EPS); } /** * Test method for 'org.xmlcml.euclid.Real2Vector.sortAscending(Axis2)' */ @Test public void testSortAscending() { Real2Vector r2v = new Real2Vector(new double[] { 1., 3., 5., 2., 7., 1., 2., 6. }); r2v = r2v.sortAscending(Axis2.X); Real2VectorTest.assertEquals("sortAsc", new double[] { 1.0, 3.0, 2.0, 6.0, 5.0, 2.0, 7.0, 1.0 }, r2v, EPS); r2v = r2v.sortAscending(Axis2.Y); Real2VectorTest.assertEquals("sortAsc", new double[] { 7.0, 1.0, 5.0, 2.0, 1.0, 3.0, 2.0, 6.0 }, r2v, EPS); r2v = r2v.sortDescending(Axis2.X); Real2VectorTest.assertEquals("sortAsc", new double[] { 7.0, 1.0, 5.0, 2.0, 2.0, 6.0, 1.0, 3.0 }, r2v, EPS); r2v = r2v.sortDescending(Axis2.Y); Real2VectorTest.assertEquals("sortAsc", new double[] { 2.0, 6.0, 1.0, 3.0, 5.0, 2.0, 7.0, 1.0 }, r2v, EPS); } /** * Test method for 'org.xmlcml.euclid.Real2Vector.transformBy(Transform2)' */ @Test public void testTransformBy() { Transform2 t = new Transform2(new Angle(Math.PI / 2.)); r1.transformBy(t); Real2VectorTest.assertEquals("transform", new double[] { 2.0, -1.0, 4.0, -3.0, 6.0, -5.0 }, r1, EPS); } /** * Test method for * 'org.xmlcml.euclid.Real2Vector.getSquaredDifference(Real2Vector)' */ @Test public void testGetSquaredDifference() { Real2Vector r = new Real2Vector(new double[] { 1.1, 2.2, 3.3, 4.4, 5.5, 6.6 }); double d = r1.getSquaredDifference(r); Assert.assertEquals("squared difference", 0.91, d, EPS); } /** * Test method for * 'org.xmlcml.euclid.Real2Vector.getSquaredDistances(Real2Vector)' */ @Test public void testGetSquaredDistances() { Real2Vector r = new Real2Vector(new double[] { 1.1, 2.2, 3.3, 4.4, 5.5, 6.6 }); double[] d = r1.getSquaredDistances(r); DoubleTestBase.assertEquals("squared distances", new double[] { 0.05, 0.25, 0.61 }, d, EPS); } /** * Test method for * 'org.xmlcml.euclid.Real2Vector.rotateAboutCentroid(Angle)' */ @Test public void testRotateAboutCentroid() { Real2VectorTest.assertEquals("transform", new double[] { 1., 2., 3., 4., 5., 6., }, r1, EPS); r1.rotateAboutCentroid(new Angle(Math.PI / 4.)); Real2VectorTest.assertEquals("transform", new double[] { 3. - Math.sqrt(8.), 4.0, 3.0, 4.0, 3. + Math.sqrt(8.), 4.0 }, r1, EPS); } /** * Test method for 'org.xmlcml.euclid.Real2Vector.regularPolygon(int, * double)' */ @Test public void testRegularPolygon() { Real2Vector p = Real2Vector.regularPolygon(3, 1.0); Real2VectorTest.assertEquals("polygon", new double[] { 0.0, 1.0, 0.8660254037844387, -0.5, -0.8660254037844385, -0.5 }, p, EPS); } /** * Test method for 'org.xmlcml.euclid.Real2Vector.getCentroid()' */ @Test public void testGetCentroid() { Real2Vector r = new Real2Vector(new double[] { 1.1, 2.2, 3.3, 4.4, 5.5, 6.6 }); Real2 c = r.getCentroid(); Assert.assertEquals("centroid", 3.3, c.getX(), EPS); Assert.assertEquals("centroid", 4.4, c.getY(), EPS); } /** * Test method for * 'org.xmlcml.euclid.Real2Vector.getSerialOfNearestPoint(Real2)' */ @Test public void testGetSerialOfNearestPoint() { Real2Vector r = new Real2Vector(new double[] { 1.1, 2.2, 3.3, 4.4, 5.5, 6.6 }); int idx = r.getSerialOfNearestPoint(new Real2(3., 4.)); Assert.assertEquals("nearest point", 1, idx); } /** * Test method for * 'org.xmlcml.euclid.Real2Vector.getDistanceMatrix(List)' */ @Test public void testGetDistanceMatrix() { List pointList = new ArrayList(); pointList.add(new Real2(1.1, 2.2)); pointList.add(new Real2(3.3, 4.4)); RealMatrix rsm = r1.getDistanceMatrix(pointList); DoubleTestBase.assertEquals("distance matrix", new double[] { 0.22360679774997916, 3.3241540277189325, 2.6172504656604803, 0.5000000000000001, 5.445181356024793, 2.33452350598575 }, rsm .getMatrixAsArray(), EPS); } /** * Test method for * {@link org.xmlcml.euclid.Real2Vector#Real2Vector(java.util.List)}. */ @Test public final void testReal2VectorListOfReal2() { List lr2 = new ArrayList(); lr2.add(new Real2(1., 2.)); lr2.add(new Real2(3., 4.)); Real2Vector r2v = new Real2Vector(lr2); Real2VectorTest.assertEquals("copy", new Real2Vector(new double[] { 1., 2., 3., 4. }), r2v, EPS); } /** * Test method for {@link org.xmlcml.euclid.Real2Vector#get(int)}. */ @Test public final void testGet() { Real2Test.assertEquals("real list", new Real2(3., 4.), r2.get(1), EPS); } /** * Test method for {@link org.xmlcml.euclid.Real2Vector#size()}. */ @Test public final void testSize() { Assert.assertEquals("real list", 5, r2.size()); } /** * Test method for {@link org.xmlcml.euclid.Real2Vector#getReal2List()}. */ @Test public final void testGetReal2List() { List r2l = r2.getReal2List(); Assert.assertEquals("real list", 5, r2l.size()); } /** * Test method for * {@link org.xmlcml.euclid.Real2Vector#sortDescending(org.xmlcml.euclid.Axis.Axis2)} * . */ @Test public final void testSortDescending() { List lr = new ArrayList(); lr.add(new Real2(3., 5.)); lr.add(new Real2(1., 2.)); lr.add(new Real2(7., 1.)); lr.add(new Real2(5., 4.)); Real2Vector r2v = new Real2Vector(lr); r2v = r2v.sortDescending(Axis2.X); Real2VectorTest.assertEquals("sort", new double[] { 7.0, 1.0, 5.0, 4.0, 3.0, 5.0, 1.0, 2.0, }, r2v, EPS); r2v = r2v.sortDescending(Axis2.Y); Real2VectorTest.assertEquals("sort", new double[] { 3.0, 5.0, 5.0, 4.0, 1.0, 2.0, 7.0, 1.0, }, r2v, EPS); } /** * Test method for * {@link org.xmlcml.euclid.Real2Vector#regularPolygon(int, double)}. */ @Test public final void testRegularPolygonIntDouble() { Real2Vector v = Real2Vector.regularPolygon(4, 1.0); Real2VectorTest.assertEquals("polygon", new Real2Vector( new Real2Vector(new double[] { 0.0, 1.0, 1.0, 0.0, 0.0, -1.0, -1.0, 0.0 })), v, EPS); v = Real2Vector.regularPolygon(6, 1.0); } /** * Test method for * {@link org.xmlcml.euclid.Real2Vector#regularPolygon(int, double, double)} * . */ @Test public final void testRegularPolygonIntDoubleDouble() { Real2Vector v = Real2Vector.regularPolygon(4, 1, 0.1); Real2VectorTest.assertEquals("polygon", new Real2Vector( new Real2Vector(new double[] { 0.09983341664682815, 0.9950041652780258, 0.9950041652780257, -0.09983341664682818, -0.09983341664682811, -0.9950041652780258, -0.9950041652780258, 0.09983341664682761, })), v, EPS); } /** * Test method for * {@link org.xmlcml.euclid.Real2Vector#regularPolygon(int, org.xmlcml.euclid.Real2, org.xmlcml.euclid.Real2, boolean)} * . */ @Test public final void testRegularPolygonIntReal2Real2Boolean() { Real2Vector v = null; v = Real2Vector.regularPolygon(5, new Real2(0.0, 0.0), new Real2(1.0, 0.0), false); Real2VectorTest.assertEquals("polygon", new Real2Vector( new Real2Vector(new double[] { 0.0, 0.0, 1.0, 0.0, 1.3090169943749475, -0.9510565162951536, 0.5, -1.5388417685876266, -0.30901699437494745, -0.9510565162951539 })), v, EPS); v = Real2Vector.regularPolygon(4, new Real2(0.0, 0.0), new Real2(3.0, 5.0), false); Real2VectorTest.assertEquals("polygon", new Real2Vector( new Real2Vector(new double[] { 0.0, 0.0, 3.0, 5.0, 8.0, 2.0, 5.0, -3.0, })), v, EPS); v = Real2Vector.regularPolygon(4, new Real2(1.0, 2.0), new Real2(3.0, 5.0), false); Real2VectorTest.assertEquals("polygon", new Real2Vector( new Real2Vector(new double[] { 1.0, 2.0, 3.0, 5.0, 6.0, 3.0, 4.0, 0.0, })), v, EPS); v = Real2Vector.regularPolygon(4, new Real2(3.0, 5.0), new Real2(1.0, 2.0), false); Real2VectorTest.assertEquals("polygon", new Real2Vector( new Real2Vector(new double[] { 3.0, 5.0, 1.0, 2.0, -2.0, 4.0, 0.0, 7.0 })), v, EPS); v = Real2Vector.regularPolygon(4, new Real2(3.0, 5.0), new Real2(1.0, 2.0), true); Real2VectorTest.assertEquals("polygon", new Real2Vector( new Real2Vector(new double[] { 3.0, 5.0, 6.0, 3.0, 8.0, 6.0, 5.0, 8.0 })), v, EPS); } /** * */ @Test public final void testGetXArrayYArray() { Real2Vector r2v = new Real2Vector(); r2v.add(new Real2(1., 11.)); r2v.add(new Real2(2., 12.)); r2v.add(new Real2(3., 13.)); r2v.add(new Real2(4., 14.)); r2v.add(new Real2(5., 15.)); RealArray xx = r2v.getXArray(); RealArrayTest.assertEquals("x", new double[] { 1., 2., 3., 4., 5. }, xx, 0.00001); RealArray yy = r2v.getYArray(); RealArrayTest.assertEquals("y", new double[] { 11., 12., 13., 14., 15. }, yy, 0.00001); } /** * */ @Test public final void testIsInside() { Real2Vector r2v = new Real2Vector(); r2v.add(new Real2(0., 0.)); r2v.add(new Real2(0., 10.)); r2v.add(new Real2(10., 10.)); r2v.add(new Real2(10., 0.)); Real2 point = new Real2(5., 3.); // simple box Assert.assertTrue("inside", r2v.encloses(point)); // make concave r2v.add(new Real2(5., 5.)); Assert.assertFalse("inside", r2v.encloses(point)); // avoid point r2v.add(new Real2(6., 0.)); Assert.assertTrue("inside", r2v.encloses(point)); } } euclid-euclid-2.9/src/test/java/org/xmlcml/euclid/test/Real3RangeTest.java000066400000000000000000000124331461721410700265260ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid.test; import static org.xmlcml.euclid.EC.EPS; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.xmlcml.euclid.Point3; import org.xmlcml.euclid.Real3Range; import org.xmlcml.euclid.RealRange; import org.xmlcml.euclid.Transform3; import org.xmlcml.euclid.Axis.Axis3; /** * test Real3Range. * * @author pmr * */ public class Real3RangeTest { Real3Range r0 = null; Real3Range r1 = null; /** * setup. * * @throws Exception */ @Before public void setUp() throws Exception { r0 = new Real3Range(); r1 = new Real3Range(new RealRange(1., 2.), new RealRange(3., 4.), new RealRange(5., 6.)); } /** * Test method for 'org.xmlcml.euclid.Real3Range.Real3Range()' */ @Test public void testReal3Range() { Assert.assertEquals("real3", "(NULL,NULL,NULL)", r0.toString()); } /** * Test method for 'org.xmlcml.euclid.Real3Range.Real3Range(RealRange, * RealRange, RealRange)' */ @Test public void testReal3RangeRealRangeRealRangeRealRange() { Assert.assertEquals("real3", "((1.0,2.0),(3.0,4.0),(5.0,6.0))", r1 .toString()); } /** * Test method for 'org.xmlcml.euclid.Real3Range.Real3Range(Real3Range)' */ @Test public void testReal3RangeReal3Range() { Real3Range rr = new Real3Range(r1); Assert.assertEquals("real3", "((1.0,2.0),(3.0,4.0),(5.0,6.0))", rr .toString()); } /** * test ranges for equality. * * @param msg * @param r3ref * @param r3 * @param epsilon */ public static void assertEquals(String msg, Real3Range r3ref, Real3Range r3, double epsilon) { RealRangeTest.assertEquals("xRange", r3.getXRange(), r3ref.getXRange(), epsilon); RealRangeTest.assertEquals("yRange", r3.getYRange(), r3ref.getYRange(), epsilon); RealRangeTest.assertEquals("zRange", r3.getZRange(), r3ref.getZRange(), epsilon); } /** * Test method for 'org.xmlcml.euclid.Real3Range.isEqualTo(Real3Range)' */ @Test public void testIsEqualTo() { Real3Range rr = new Real3Range(r1); Assert.assertTrue("real3", rr.isEqualTo(r1, 0.001)); } /** * Test method for 'org.xmlcml.euclid.Real3Range.plus(Real3Range)' */ @Test public void testPlus() { Real3Range r = new Real3Range(new RealRange(1., 3.), new RealRange(2., 7.), new RealRange(2., 3.)); Real3Range rr = r.plus(r1); Assert.assertEquals("real3", "((1.0,3.0),(2.0,7.0),(2.0,6.0))", rr .toString()); } /** * Test method for 'org.xmlcml.euclid.Real3Range.getXRange()' */ @Test public void testGetXYZRange() { RealRange x = r1.getXRange(); Assert.assertEquals("realx", "(1.0,2.0)", x.toString()); RealRange y = r1.getYRange(); Assert.assertEquals("realy", "(3.0,4.0)", y.toString()); RealRange z = r1.getZRange(); Assert.assertEquals("realz", "(5.0,6.0)", z.toString()); } /** * Test method for 'org.xmlcml.euclid.Real3Range.add(Axis3, double)' */ @Test public void testAddAxis3Double() { r1.add(Axis3.X, 10.); Assert.assertEquals("add", "((1.0,10.0),(3.0,4.0),(5.0,6.0))", r1 .toString()); r1.add(Axis3.X, -1.); Assert.assertEquals("add", "((-1.0,10.0),(3.0,4.0),(5.0,6.0))", r1 .toString()); } /** * Test method for 'org.xmlcml.euclid.Real3Range.includes(Point3)' */ @Test public void testIncludes() { Assert.assertTrue("includes", r1.includes(new Point3(1.1, 3.3, 5.5))); Assert.assertFalse("includes", r1.includes(new Point3(0.9, 3.3, 5.5))); } /** * Test method for 'org.xmlcml.euclid.Real3Range.add(Point3)' */ @Test public void testAddPoint3() { r1.add(new Point3(1.1, 1.2, 10.8)); Assert.assertEquals("add", "((1.0,2.0),(1.2,4.0),(5.0,10.8))", r1 .toString()); } /** * test get point with max x, y, z. */ @Test public void testGetMaxPoint3() { Point3 maxp = r1.getMaxPoint3(); Point3Test.assertEquals("max point", new Point3(2., 4., 6.), maxp, EPS); } /** * test get point with min x, y, z. */ @Test public void testGetMinPoint3() { Point3 minp = r1.getMinPoint3(); Point3Test.assertEquals("min point", new Point3(1., 3., 5.), minp, EPS); } /** * test transforms range. */ @Test public void testTransformEquals() { Transform3 tr = new Transform3("y, -z, -x"); r1.transformEquals(tr); Assert.assertEquals("transformed x range min", 3., r1.getXRange() .getMin(), EPS); Assert.assertEquals("transformed x range max", 4., r1.getXRange() .getMax(), EPS); Assert.assertEquals("transformed y range min", -6., r1.getYRange() .getMin(), EPS); Assert.assertEquals("transformed y range max", -5., r1.getYRange() .getMax(), EPS); Assert.assertEquals("transformed z range min", -2., r1.getZRange() .getMin(), EPS); Assert.assertEquals("transformed z range max", -1., r1.getZRange() .getMax(), EPS); } } euclid-euclid-2.9/src/test/java/org/xmlcml/euclid/test/RealArrayTest.java000066400000000000000000001124471461721410700264730ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid.test; import static org.xmlcml.euclid.EuclidConstants.EPS; import static org.xmlcml.euclid.EuclidConstants.S_EMPTY; import java.util.Iterator; import org.apache.log4j.Logger; import org.hamcrest.CoreMatchers; import org.hamcrest.MatcherAssert; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.xmlcml.euclid.ArrayBase.Trim; import org.xmlcml.euclid.EuclidConstants; import org.xmlcml.euclid.EuclidRuntimeException; import org.xmlcml.euclid.IntArray; import org.xmlcml.euclid.IntSet; import org.xmlcml.euclid.RealArray; import org.xmlcml.euclid.RealArray.Filter; import org.xmlcml.euclid.RealArray.Monotonicity; import org.xmlcml.euclid.RealRange; /** * test RealArray. * * @author pmr * */ public class RealArrayTest { private final static Logger LOG = Logger.getLogger(RealArrayTest.class); RealArray a0; RealArray a1; /** * main * * @param args */ public static void main(String[] args) { } /** * setup. * * @throws Exception */ @Before public void setUp() throws Exception { a0 = new RealArray(); a1 = new RealArray(new double[] { 1.0, 2.0, 4.0, 6.0 }); } /** * equality test. true if both args not null and equal within epsilon * * @param msg * message * @param test * @param expected * @param epsilon */ public static void assertEquals(String msg, RealArray test, RealArray expected, double epsilon) { Assert.assertNotNull("test should not be null (" + msg + EuclidConstants.S_RBRAK, test); Assert.assertNotNull("expected should not be null (" + msg + EuclidConstants.S_RBRAK, expected); DoubleTestBase.assertEquals(msg, test.getArray(), expected.getArray(), epsilon); } @Test public void testShiftArrayRight() { RealArray array = new RealArray(new double[]{2., 15., 100., 40., 3.}); RealArray array0 = new RealArray(array); array0.shiftArrayRight(1); DoubleTestBase.assertEquals("shifted", new double[]{2., 2., 15., 100., 40.}, array0.getArray(), 0.1); array0 = new RealArray(array); array0.shiftArrayRight(2); DoubleTestBase.assertEquals("shifted", new double[]{2., 2., 2., 15., 100.}, array0.getArray(), 0.1); array0 = new RealArray(array); array0.shiftArrayRight(5); DoubleTestBase.assertEquals("shifted", new double[]{2., 2., 2., 2., 2.}, array0.getArray(), 0.1); } @Test public void testShiftArrayLeft() { RealArray array = new RealArray(new double[]{2., 15., 100., 40., 3.}); RealArray array0 = new RealArray(array); array0.shiftArrayRight(-1); DoubleTestBase.assertEquals("shifted", new double[]{15., 100., 40., 3., 3.}, array0.getArray(), 0.1); array0 = new RealArray(array); array0.shiftArrayRight(-2); DoubleTestBase.assertEquals("shifted", new double[]{100., 40., 3., 3., 3.,}, array0.getArray(), 0.1); array0 = new RealArray(array); array0.shiftArrayRight(-5); DoubleTestBase.assertEquals("shifted", new double[]{3., 3., 3., 3., 3.}, array0.getArray(), 0.1); } @Test public void testScaleAndInterpolateLarge() { RealArray arrayRef = new RealArray(new double[]{2., 15., 100., 40., 3.}); RealArray array = new RealArray(arrayRef); LOG.trace(array); RealArray array0 = array.scaleAndInterpolate(8); LOG.trace(array0); DoubleTestBase.assertEquals("5->8", new double[]{2.0,7.2,15.0,83.0,88.0,40.0,17.8,3.0}, array0.getArray(), 0.1); array = new RealArray(arrayRef); array0 = array.scaleAndInterpolate(4); LOG.trace(array0); DoubleTestBase.assertEquals("5->3", new double[]{2.0, 3.75, 61.25, 83.0}, array0.getArray(), 0.1); } @Test public void testScaleAndInterpolateSmall() { RealArray arrayRef = new RealArray(new double[]{2., 15., 100., 40., 3.}); RealArray array = new RealArray(arrayRef); RealArray array0 = array.scaleAndInterpolate(4); DoubleTestBase.assertEquals("5->4", new double[]{2.0,3.75,61.25,83.0}, array0.getArray(), 0.1); } @Test public void testShiftOriginToRight() { RealArray array = new RealArray(new double[]{2., 15., 100., 40., 3.}); RealArray array0 = new RealArray(array); RealArray newArray = array0.shiftOriginToRight(0.1); DoubleTestBase.assertEquals("shifted", new double[]{3.3, 23.5, 94., 36.3, 3.}, newArray.getArray(), 0.1); array0 = new RealArray(array); newArray = array0.shiftOriginToRight(0.2); DoubleTestBase.assertEquals("shifted", new double[]{4.6, 32.0, 88., 32.6, 3.}, newArray.getArray(), 0.1); array0 = new RealArray(array); newArray = array0.shiftOriginToRight(0.5); DoubleTestBase.assertEquals("shifted", new double[]{8.5, 57.5, 70., 21.5, 3.}, newArray.getArray(), 0.1); array0 = new RealArray(array); newArray = array0.shiftOriginToRight(0.9); DoubleTestBase.assertEquals("shifted", new double[]{13.7, 91.5, 46., 6.7, 3.}, newArray.getArray(), 0.1); array0 = new RealArray(array); newArray = array0.shiftOriginToRight(0.9999999); DoubleTestBase.assertEquals("shifted", new double[]{15., 100., 40., 3., 3.}, newArray.getArray(), 0.1); array0 = new RealArray(array); newArray = array0.shiftOriginToRight(1.0); DoubleTestBase.assertEquals("shifted", new double[]{15., 100., 40., 3., 3.}, newArray.getArray(), 0.1); array0 = new RealArray(array); newArray = array0.shiftOriginToRight(1.1); DoubleTestBase.assertEquals("shifted", new double[]{23.5, 94., 36.3, 3., 3.}, newArray.getArray(), 0.1); array0 = new RealArray(array); newArray = array0.shiftOriginToRight(2.1); DoubleTestBase.assertEquals("shifted", new double[]{94., 36.3, 3., 3., 3.}, newArray.getArray(), 0.1); } @Test /** not yet working for large negative * */ // FIXME // @Ignore public void testShiftOriginToLeft() { RealArray array = new RealArray(new double[]{2., 15., 100., 40., 3.}); RealArray array0 = new RealArray(array); RealArray newArray = array0.shiftOriginToRight(-0.1); DoubleTestBase.assertEquals("shifted", new double[]{2.0, 13.7, 91.5, 46.0, 6.7}, newArray.getArray(), 0.1); array0 = new RealArray(array); newArray = array0.shiftOriginToRight(-0.2); DoubleTestBase.assertEquals("shifted", new double[]{2.0,12.4,83.0,52.0,10.4}, newArray.getArray(), 0.1); array0 = new RealArray(array); newArray = array0.shiftOriginToRight(-0.5); DoubleTestBase.assertEquals("shifted", new double[]{2.0,8.5,57.5,70.0,21.5}, newArray.getArray(), 0.1); array0 = new RealArray(array); newArray = array0.shiftOriginToRight(-0.9); DoubleTestBase.assertEquals("shifted", new double[]{2.0,3.3,23.5,94.0,36.3}, newArray.getArray(), 0.1); array0 = new RealArray(array); newArray = array0.shiftOriginToRight(-0.9999999); DoubleTestBase.assertEquals("shifted", new double[]{2.0,2.0,15.0,100.0,40.0}, newArray.getArray(), 0.1); array0 = new RealArray(array); newArray = array0.shiftOriginToRight(-1.0); DoubleTestBase.assertEquals("shifted", new double[]{2.0,2.0,15.0,100.0,40.0}, newArray.getArray(), 0.1); // FIXME these aren't working yet array0 = new RealArray(array); newArray = array0.shiftOriginToRight(-1.1); // DoubleTestBase.assertEquals("shifted", new double[]{2.0, 2.0, 13.7, 91.5, 46.0, }, newArray.getArray(), 0.1); array0 = new RealArray(array); newArray = array0.shiftOriginToRight(-2.1); // DoubleTestBase.assertEquals("shifted", new double[]{2.0, 2.0, 2.0, 13.7, 91.5}, newArray.getArray(), 0.1); } /** * equality test. true if both args not null and equal within epsilon * * @param msg * message * @param test * array must be of length 3 * @param expected * @param epsilon */ public static void assertEquals(String msg, double[] test, RealArray expected, double epsilon) { Assert.assertNotNull("test should not be null (" + msg + EuclidConstants.S_RBRAK, test); Assert.assertNotNull("expected should not be null (" + msg + EuclidConstants.S_RBRAK, expected); Assert.assertEquals("must be of equal length ", test.length, expected .getArray().length); DoubleTestBase.assertEquals(msg, test, expected.getArray(), epsilon); } /** * Test method for 'org.xmlcml.euclid.RealArray.RealArray()' */ @Test public void testRealArray() { Assert.assertEquals("empty", 0, a0.size()); Assert.assertEquals("empty", "()", a0.toString()); } /** * Test method for 'org.xmlcml.euclid.RealArray.RealArray(int)' */ @Test public void testRealArrayInt() { RealArray r = new RealArray(4); Assert.assertEquals("r", 4, r.size()); RealArrayTest.assertEquals("r", new double[] { 0.0, 0.0, 0.0, 0.0 }, r, EPS); } /** * Test method for 'org.xmlcml.euclid.RealArray.RealArray(int, double, * double)' */ @Test public void testRealArrayIntDoubleDouble() { RealArray r = new RealArray(4, 1.0, 2.0); Assert.assertEquals("r", 4, r.size()); RealArrayTest.assertEquals("r", new double[] { 1.0, 3.0, 5.0, 7.0 }, r, EPS); } /** * Test method for 'org.xmlcml.euclid.RealArray.RealArray(int, double)' */ @Test public void testRealArrayIntDouble() { RealArray r = new RealArray(4, 2.0); Assert.assertEquals("r", 4, r.size()); RealArrayTest.assertEquals("r", new double[] { 2.0, 2.0, 2.0, 2.0 }, r, EPS); } /** * Test method for 'org.xmlcml.euclid.RealArray.RealArray(int, double[])' */ @Test public void testRealArrayIntDoubleArray() { double[] d = { 1.0, 2.0, 3.0, 4.0 }; RealArray r = new RealArray(3, d); Assert.assertEquals("r", 3, r.size()); RealArrayTest.assertEquals("r", new double[] { 1.0, 2.0, 3.0 }, r, EPS); try { r = new RealArray(5, d); Assert.fail("should always throw " + "Array size too small"); } catch (EuclidRuntimeException e) { Assert.assertEquals("double[]", "Array size too small", e .getMessage()); } } /** * Test method for 'org.xmlcml.euclid.RealArray.RealArray(double[])' */ @Test public void testRealArrayDoubleArray() { double[] d = { 1.0, 2.0, 3.0, 4.0 }; RealArray r = new RealArray(d); Assert.assertEquals("r", 4, r.size()); RealArrayTest.assertEquals("r", new double[] { 1.0, 2.0, 3.0, 4.0 }, r, EPS); } /** * Test method for 'org.xmlcml.euclid.RealArray.RealArray(IntArray)' */ @Test public void testRealArrayIntArray() { IntArray i = new IntArray(new int[] { 1, 2, 3, 4 }); RealArray r = new RealArray(i); Assert.assertEquals("r", 4, r.size()); RealArrayTest.assertEquals("r", new double[] { 1.0, 2.0, 3.0, 4.0 }, r, EPS); } /** * Test method for 'org.xmlcml.euclid.RealArray.RealArray(RealArray, int, * int)' */ @Test public void testRealArrayRealArrayIntInt() { RealArray r = new RealArray(a1, 1, 2); Assert.assertEquals("r", 2, r.size()); RealArrayTest.assertEquals("r", new double[] { 2.0, 4.0 }, r, EPS); try { r = new RealArray(a1, 0, 5); } catch (EuclidRuntimeException e) { Assert.assertEquals("real array", "index out of range 0/5", e .getMessage()); } } /** * Test method for 'org.xmlcml.euclid.RealArray.RealArray(RealArray, * IntArray)' */ @Test public void testRealArrayRealArrayIntArray() { RealArray r = new RealArray(a1, new IntArray(new int[] { 3, 1, 2 })); Assert.assertEquals("r", 3, r.size()); RealArrayTest.assertEquals("r", new double[] { 6.0, 2.0, 4.0 }, r, EPS); } /** * Test method for 'org.xmlcml.euclid.RealArray.RealArray(RealArray)' */ @Test public void testRealArrayRealArray() { RealArray r = new RealArray(a1); Assert.assertEquals("r", 4, r.size()); RealArrayTest.assertEquals("r", new double[] { 1.0, 2.0, 4.0, 6.0 }, r, EPS); } /** * Test method for 'org.xmlcml.euclid.RealArray.RealArray(int, String, * double)' */ @Test public void testRealArrayIntStringDouble() { RealArray r = new RealArray(3, "TRIANGLE", 2.0); Assert.assertEquals("r", 5, r.size()); RealArrayTest.assertEquals("r", new double[] { 2. / 3., 4. / 3., 2., 4. / 3., 2. / 3. }, r, EPS); } /** * Test method for 'org.xmlcml.euclid.RealArray.RealArray(String[])' */ @Test public void testRealArrayStringArray() { RealArray r = new RealArray(new String[] { "1.0", "2.0", "4.0", "6.0" }); RealArrayTest.assertEquals("string array", a1, r, EPS); } /** * Test method for 'org.xmlcml.euclid.RealArray.RealArray(String)' */ @Test public void testRealArrayString() { RealArray r = new RealArray("1.0 2.0 4.0 6.0"); RealArrayTest.assertEquals("string array", a1, r, EPS); } /** * Test method for 'org.xmlcml.euclid.RealArray.getFilter(int, String)' */ @Test public void testGetFilter() { RealArray filter = RealArray.getFilter(3, Filter.GAUSSIAN); double[] f = new double[] { 0.00000780609891336, 0.00416059856172973, 0.17996534087165197, 0.6317325089354098, 0.17996534087165197, 0.00416059856172973, 0.00000780609891336 }; RealArrayTest.assertEquals("filter", f, filter, 1.0E-16); filter = RealArray.getFilter(2, Filter.GAUSSIAN_FIRST_DERIVATIVE); f = new double[] { -8.307977825463781E-5, -0.19931487665039388, -0.0, 0.19931487665039388, 8.307977825463781E-5 }; RealArrayTest.assertEquals("filter", f, filter, 1.0E-16); filter = RealArray.getFilter(3, Filter.GAUSSIAN_SECOND_DERIVATIVE); f = new double[] { 5.338724215243003E-4, 0.119149066997191, 0.8611247708322933, -2.0, 0.8611247708322933, 0.119149066997191, 5.338724215243003E-4 }; RealArrayTest.assertEquals("filter", f, filter, 1.0E-16); } /** * test getSymmetricalArray() */ @Test public void testGetSymmetricalArray() { RealArray realArray = RealArray.getSymmetricalArray(10., 11, 1.0); Assert.assertEquals("range min", 9.0, realArray.getArray()[0], EPS); Assert.assertEquals("range min+1", 9.2, realArray.getArray()[1], EPS); Assert.assertEquals("range max", 11.0, realArray.getArray()[10], EPS); Assert.assertEquals("range mid", 10.0, realArray.getArray()[5], EPS); } @Test public void testGetMonotonicity() { RealArray realArray = new RealArray(new double[]{ 1., 2., 3., 4., 4., 5. }); Monotonicity m = realArray.getMonotonicity(); Assert.assertEquals("monotonicity", Monotonicity.INCREASING, m); realArray = new RealArray(new double[]{ 1., 2., }); m = realArray.getMonotonicity(); Assert.assertEquals("monotonicity", Monotonicity.INCREASING, m); realArray = new RealArray(new double[]{ 1., 1., }); m = realArray.getMonotonicity(); Assert.assertNull("monotonicity", m); realArray = new RealArray(new double[]{ 1., }); m = realArray.getMonotonicity(); Assert.assertNull("monotonicity", m); realArray = new RealArray(new double[]{ 5., 4., 3., 2., 2., 1. }); m = realArray.getMonotonicity(); Assert.assertEquals("monotonicity", Monotonicity.DECREASING, m); realArray = new RealArray(new double[]{ 5., 4., 1., 2., 2., 1. }); m = realArray.getMonotonicity(); Assert.assertNull("monotonicity", m); } /** * test getNormalDistribution() */ @Test public void testGetNormalDistribution() { double mean = 10.; double halfrange = 5.0; int nsteps = 101; double sigma = 1.0; RealArray realArray = RealArray.getSymmetricalArray(mean, nsteps, halfrange); RealArray normalDist = realArray.getNormalDistribution(sigma); Assert.assertEquals("range min", 1.486e-06, normalDist.getArray()[0], 1E-06); Assert.assertEquals("range mid", 0.398942, normalDist.getArray()[50], 1E-06); Assert.assertEquals("range max", 1.486e-06, normalDist.getArray()[100], 1E-06); RealArray cumulativeSum = normalDist.cumulativeSum(); Assert.assertEquals("range min", 1.5e-06, cumulativeSum.getArray()[0], 1E-06); Assert.assertEquals("range mid", 5.199469, cumulativeSum.getArray()[50], 1E-06); Assert.assertEquals("range max", 9.999996, cumulativeSum.getArray()[100], 1E-06); for (int i = 0; i < 100; i++) { /* double d = mean - halfrange + halfrange */Math.random(); // Util.sysout(d); } } /** * test lineSearch. */ @Test public void testLineSearch() { RealArray x = new RealArray(11, 0., 1.0); RealArray cumulative = new RealArray(new double[] { 0., 4., 5., 20., 23., 26., 33., 40., 41., 44., 50. }); double d = -1.; double probe = 22.; d = x.lineSearch(probe, cumulative); Assert.assertEquals("search ", 11. / 3., d, EPS); probe = 2.; d = x.lineSearch(probe, cumulative); Assert.assertEquals("search ", 0.5, d, EPS); probe = 1.; d = x.lineSearch(probe, cumulative); Assert.assertEquals("search ", 0.25, d, EPS); probe = 4.5; d = x.lineSearch(probe, cumulative); Assert.assertEquals("search ", 1.5, d, EPS); probe = 50; d = x.lineSearch(probe, cumulative); Assert.assertEquals("search ", 10., d, EPS); probe = 0; d = x.lineSearch(probe, cumulative); Assert.assertEquals("search ", 0., d, EPS); } /** * test getRandomVariate. * */ @Test @SuppressWarnings("unused") public void testGetRandomVariate() { RealArray x = new RealArray(11, 20., 1.); // gives 20, 21 ...30 RealArray freq = new RealArray(new double[] { 5., 10., 10., 1., 0., 2., 10., 15., 10., 3., 1. }); RealArray cumulativeSum = new RealArray(); for (int i = 0; i < 50; i++) { double random = x.getRandomVariate(freq, cumulativeSum); // Util.sysout(cumulativeSum); // Util.sysout(random); } } /** * Test method for 'org.xmlcml.euclid.RealArray.elementAt(int)' */ @Test public void testElementAt() { Assert.assertEquals("element at", 4.0, a1.elementAt(2),EPS); try { Assert.assertEquals("element at", 4.0, a1.elementAt(5),EPS); Assert.fail("should always throw " + "ArrayIndexOutOfBoundsException"); } catch (ArrayIndexOutOfBoundsException e) { MatcherAssert.assertThat("ArrayIndexOutOfBoundsException", e.getMessage(), CoreMatchers.anyOf( CoreMatchers.equalTo("5"), CoreMatchers.equalTo("Index 5 out of bounds for length 4"))); } } /** * Test method for 'org.xmlcml.euclid.RealArray.size()' */ @Test public void testSize() { Assert.assertEquals("size", 0, a0.size()); Assert.assertEquals("size", 4, a1.size()); } /** * Test method for 'org.xmlcml.euclid.RealArray' */ @Test public void testGetArray() { RealArrayTest.assertEquals("array", new double[] {}, a0, EPS); RealArrayTest.assertEquals("array", new double[] { 1., 2., 4., 6. }, a1, EPS); } /** * Test method for 'org.xmlcml.euclid.RealArray.clearArray()' */ @Test public void testClearArray() { a1.clearArray(); RealArrayTest.assertEquals("clear", new double[] { 0., 0., 0., 0. }, a1, EPS); } /** * Test method for 'org.xmlcml.euclid.RealArray.getReverseArray()' */ @Test public void testGetReverseArray() { double[] d = a1.getReverseArray(); DoubleTestBase.assertEquals("clear", new double[] { 6., 4., 2., 1. }, d, EPS); } /** * Test method for 'org.xmlcml.euclid.RealArray.isEqualTo(RealArray)' */ @Test public void testIsEqualTo() { RealArray a = new RealArray("1 2 4 6"); Assert.assertTrue("isEqualTo", a1.isEqualTo(a)); a = new RealArray("1 2 4 6.1"); Assert.assertFalse("isEqualTo", a1.isEqualTo(a)); a = new RealArray("1 2 4"); Assert.assertFalse("isEqualTo", a1.isEqualTo(a)); } /** * Test method for 'org.xmlcml.euclid.RealArray.equals(RealArray, double)' */ @Test public void testEqualsRealArrayDouble() { RealArray a = new RealArray("1 2 4 6"); Assert.assertTrue("isEqualTo", a1.equals(a, EPS)); a = new RealArray("1 2 4 6.1"); Assert.assertFalse("isEqualTo", a1.equals(a, EPS)); a = new RealArray("1.00002 1.99999 4.0000007 6.0001"); Assert.assertTrue("isEqualTo", a1.equals(a, .001)); a = new RealArray("1 2 4"); Assert.assertFalse("isEqualTo", a1.equals(a, EPS)); } /** * Test method for 'org.xmlcml.euclid.RealArray.plus(RealArray)' */ @Test public void testPlus() { RealArray a2 = a1.plus(new RealArray("10 20 30 40")); RealArrayTest.assertEquals("plus", new double[] { 11.0, 22.0, 34.0, 46.0 }, a2, EPS); } /** * Test method for 'org.xmlcml.euclid.RealArray.plusEquals(RealArray)' */ @Test public void testPlusEquals() { a1.plusEquals(new RealArray("10 20 30 40")); RealArrayTest.assertEquals("plus", new double[] { 11.0, 22.0, 34.0, 46.0 }, a1, EPS); } /** * calculate differences via filter */ @Test public void testCalculateDiferences() { RealArray a2 = a1.calculateDifferences(); RealArrayTest.assertEquals("subtract", new double[] { 1.0, 2.0, 2.0 }, a2, EPS); } /** * Test method for 'org.xmlcml.euclid.RealArray.subtract(RealArray)' */ @Test public void testSubtract() { RealArray a2 = a1.subtract(new RealArray("10 20 30 40")); RealArrayTest.assertEquals("subtract", new double[] { -9.0, -18.0, -26.0, -34.0 }, a2, EPS); } /** * Test method for 'org.xmlcml.euclid.RealArray.subtractEquals(RealArray)' */ @Test public void testSubtractEquals() { a1.subtractEquals(new RealArray("10 20 30 40")); RealArrayTest.assertEquals("subtract", new double[] { -9.0, -18.0, -26.0, -34.0 }, a1, EPS); } /** * Test method for 'org.xmlcml.euclid.RealArray.negative()' */ @Test public void testNegative() { a1.negative(); RealArrayTest.assertEquals("negative", new double[] { -1, -2, -4, -6 }, a1, EPS); } /** * Test method for 'org.xmlcml.euclid.RealArray.addScalar(double)' */ @Test public void testAddScalar() { RealArray a = a1.addScalar(3.3); RealArrayTest.assertEquals("addScalar", new double[] { 4.3, 5.3, 7.3, 9.3 }, a, EPS); } /** * Test method for 'org.xmlcml.euclid.RealArray.multiplyBy(double)' */ @Test public void testMultiplyBy() { RealArray a = a1.multiplyBy(1.1); RealArrayTest.assertEquals("multiplyBy", new double[] { 1.1, 2.2, 4.4, 6.6 }, a, EPS); } /** * Test method for 'org.xmlcml.euclid.RealArray.setElementAt(int, double)' */ @Test public void testSetElementAt() { a1.setElementAt(2, 10.); RealArrayTest.assertEquals("setElement", new double[] { 1., 2., 10., 6. }, a1, EPS); } /** * Test method for 'org.xmlcml.euclid.RealArray.getSubArray(int, int)' */ @Test public void testGetSubArray() { RealArray a = a1.getSubArray(2, 3); RealArrayTest.assertEquals("subArray", new double[] { 4., 6. }, a, EPS); a = a1.getSubArray(2, 2); RealArrayTest.assertEquals("subArray", new double[] { 4. }, a, EPS); a = a1.getSubArray(0, 3); RealArrayTest.assertEquals("subArray", new double[] { 1., 2., 4., 6. }, a, EPS); try { a = a1.getSubArray(0, 5); Assert.fail("should always throw " + "ArrayIndexOutOfBoundsException"); } catch (ArrayIndexOutOfBoundsException e) { MatcherAssert.assertThat("subArray ArrayIndexOutOfBoundsException", S_EMPTY + e, CoreMatchers.startsWith("java.lang.ArrayIndexOutOfBoundsException")); } } /** * Test method for 'org.xmlcml.euclid.RealArray.setElements(int, double[])' */ @Test public void testSetElements() { a1.setElements(1, new double[] { 10., 20. }); RealArrayTest.assertEquals("setElement", new double[] { 1., 10., 20., 6. }, a1, EPS); try { a1.setElements(1, new double[] { 10., 20., 30., 40. }); Assert.fail("should always throw " + "ArrayIndexOutOfBoundsException"); } catch (ArrayIndexOutOfBoundsException e) { Assert.assertEquals("subArray ArrayIndexOutOfBoundsException", "java.lang.ArrayIndexOutOfBoundsException: was 1 in 0-4", "" + e); } } /** * Test method for 'org.xmlcml.euclid.RealArray.isClear()' */ @Test public void testIsClear() { Assert.assertFalse("isClear", a1.isClear()); a1.clearArray(); Assert.assertTrue("isClear", a1.isClear()); } /** * Test method for 'org.xmlcml.euclid.RealArray.setAllElements(double)' */ @Test public void testSetAllElements() { a1.setAllElements(10.); RealArrayTest.assertEquals("setElement", new double[] { 10., 10., 10., 10. }, a1, EPS); } /** * Test method for 'org.xmlcml.euclid.RealArray.sumAllElements()' */ @Test public void testSumAllElements() { Assert.assertEquals("sum", 13., a1.sumAllElements(), EPS); } /** * Test method for 'org.xmlcml.euclid.RealArray.absSumAllElements()' */ @Test public void testAbsSumAllElements() { RealArray a = new RealArray("-1 3 -11 14"); Assert.assertEquals("sum", 5., a.sumAllElements(), EPS); Assert.assertEquals("absSum", 29., a.absSumAllElements(), EPS); } /** * Test method for 'org.xmlcml.euclid.RealArray.innerProduct()' */ @Test public void testInnerProduct() { Assert.assertEquals("inner", 57., a1.innerProduct(), EPS); } /** * Test method for 'org.xmlcml.euclid.RealArray.dotProduct(RealArray)' */ @Test public void testDotProduct() { RealArray a = new RealArray("1 2 3 4"); double d = a1.dotProduct(a); Assert.assertEquals("dot", 41., d, EPS); a = new RealArray("1 2 3"); try { a1.dotProduct(a); Assert.fail("should always throw " + "ArrayIndexOutOfBoundsException"); } catch (EuclidRuntimeException e) { Assert.assertEquals("dot", "org.xmlcml.euclid.EuclidRuntimeException", "" + e); } } /** * Test method for 'org.xmlcml.euclid.RealArray.euclideanLength()' */ @Test public void testEuclideanLength() { double d = 0.0; d = a1.euclideanLength(); Assert.assertEquals("dot", Math.sqrt(57.), d, EPS); } /** * Test method for 'org.xmlcml.euclid.RealArray.rms()' */ @Test public void testRms() { double d = a1.rms(); Assert.assertEquals("rms", 3.7749172176, d, 1.0E-06); } /** * Test method for 'org.xmlcml.euclid.RealArray.unitVector()' */ @Test public void testUnitVector() { RealArray v = a1.unitVector(); RealArrayTest.assertEquals("unit vector", new double[] { 0.13245323570650439, 0.26490647141300877, 0.5298129428260175, 0.7947194142390264 }, v, 1.0E-10); } /** * Test method for 'org.xmlcml.euclid.RealArray.cumulativeSum()' */ @Test public void testCumulativeSum() { RealArray a = a1.cumulativeSum(); RealArrayTest.assertEquals("cumulative", new double[] { 1., 3., 7., 13. }, a, EPS); } /** * Test method for 'org.xmlcml.euclid.RealArray.applyFilter(RealArray)' */ @Test public void testApplyFilter() { RealArray gaussian = RealArray.getFilter(3, Filter.GAUSSIAN); RealArray raw = new RealArray(new double[] { 1., 2., 1., 2., 1., 5., 7., 9., 3., 2., 3., 1., 1. }); RealArray filtered = raw.applyFilter(gaussian); double[] d = { 1.2205914829606295, 1.6385548625623205, 1.3599647160591364, 1.6525823383375386, 1.9248605506188587, 4.644183080224947, 6.958315953393569, 7.51440140346297, 3.916469098605179, 2.384925497509336, 2.4518253377494292, 1.3656309904274337, 1.010208785051181 }; RealArrayTest.assertEquals("filtered", d, filtered, EPS); } /** * Test method for 'org.xmlcml.euclid.RealArray.trim(int, double)' */ @Test public void testTrim() { RealArray a = new RealArray("1.1 2.1 3 4 1.2 3.2 5 1 3"); RealArray b = a.trim(Trim.ABOVE, 2.0); double[] d = { 1.1, 2.0, 2.0, 2.0, 1.2, 2.0, 2.0, 1.0, 2.0 }; RealArrayTest.assertEquals("trim", d, b, EPS); b = a.trim(Trim.BELOW, 2.0); double[] dd = { 2.0, 2.1, 3.0, 4.0, 2.0, 3.2, 5.0, 2.0, 3.0 }; RealArrayTest.assertEquals("trim", dd, b, EPS); } /** * Test method for 'org.xmlcml.euclid.RealArray.indexOfLargestElement()' */ @Test public void testIndexOfLargestElement() { Assert.assertEquals("largest", 3, a1.indexOfLargestElement()); } /** * Test method for 'org.xmlcml.euclid.RealArray.indexOfSmallestElement()' */ @Test public void testIndexOfSmallestElement() { Assert.assertEquals("smallest", 0, a1.indexOfSmallestElement()); } /** * Test method for 'org.xmlcml.euclid.RealArray.largestElement()' */ @Test public void testLargestElement() { Assert.assertEquals("largest", 6., a1.largestElement(),EPS); } /** * Test method for 'org.xmlcml.euclid.RealArray.getMax()' */ @Test public void testGetMax() { Assert.assertEquals("max", 6., a1.getMax(), EPS); } /** * Test method for 'org.xmlcml.euclid.RealArray.smallestElement()' */ @Test public void testSmallestElement() { Assert.assertEquals("smallest", 1., a1.smallestElement(),EPS); } /** * Test method for 'org.xmlcml.euclid.RealArray.getMin()' */ @Test public void testGetMin() { Assert.assertEquals("max", 1., a1.getMin(), EPS); } /** * Test method for 'org.xmlcml.euclid.RealArray.getRange()' */ @Test public void testGetRange() { RealRange range = a1.getRange(); Assert.assertEquals("range", 1., range.getMin(), EPS); Assert.assertEquals("range", 6., range.getMax(), EPS); } /** * Test method for 'org.xmlcml.euclid.RealArray.deleteElement(int)' */ @Test public void testDeleteElement() { a1.deleteElement(2); RealArrayTest.assertEquals("delete", new double[] { 1., 2., 6. }, a1, EPS); } /** * Test method for 'org.xmlcml.euclid.RealArray.deleteElements(int, int)' */ @Test public void testDeleteElementsIntInt() { RealArray a = new RealArray(a1); a.deleteElements(1, 2); RealArrayTest.assertEquals("delete", new double[] { 1., 6. }, a, EPS); a = new RealArray(a1); a.deleteElements(0, 3); RealArrayTest.assertEquals("delete", new double[] {}, a, EPS); a = new RealArray(a1); a.deleteElements(2, 2); RealArrayTest.assertEquals("delete", new double[] { 1., 2., 6. }, a, EPS); } /** * Test method for 'org.xmlcml.euclid.RealArray.insertElementAt(int, * double)' */ @Test public void testInsertElementAt() { RealArray a = new RealArray(a1); a.insertElementAt(1, 30.); RealArrayTest.assertEquals("insert", new double[] { 1., 30., 2., 4., 6. }, a, EPS); a.insertElementAt(0, 20.); RealArrayTest.assertEquals("insert", new double[] { 20., 1., 30., 2., 4., 6. }, a, EPS); a.insertElementAt(6, 10.); RealArrayTest.assertEquals("insert", new double[] { 20., 1., 30., 2., 4., 6., 10. }, a, EPS); } /** * Test method for 'org.xmlcml.euclid.RealArray.insertArray(int, RealArray)' */ @Test public void testInsertArray() { a1.insertArray(1, new RealArray("44 55")); RealArrayTest.assertEquals("insert", new double[] { 1., 44., 55., 2., 4., 6. }, a1, EPS); } /** * Test method for 'org.xmlcml.euclid.RealArray.addElement(double)' */ @Test public void testAddElement() { a1.addElement(30.); RealArrayTest.assertEquals("insert", new double[] { 1., 2., 4., 6., 30. }, a1, EPS); } /** * Test method for 'org.xmlcml.euclid.RealArray.addArray(RealArray)' */ @Test public void testAddArray() { a1.addArray(new RealArray("5 16 7")); RealArrayTest.assertEquals("insert", new double[] { 1., 2., 4., 6., 5., 16., 7. }, a1, EPS); } /** * Test method for 'org.xmlcml.euclid.RealArray.getReorderedArray(IntSet)' */ @Test public void testGetReorderedArray() { IntSet intSet = new IntSet(new int[] { 3, 1, 0, 2 }); RealArray a = null; a = a1.getReorderedArray(intSet); RealArrayTest.assertEquals("insert", new double[] { 6., 2., 1., 4. }, a, EPS); } /** * Test method for 'org.xmlcml.euclid.RealArray.inRange(RealRange)' */ @Test public void testInRange() { RealRange range = new RealRange(1.3, 5.); IntSet intSet = a1.inRange(range); IntArray intArray = intSet.getIntArray(); IntArrayTest.assertEquals("inrange", new int[] { 1, 2 }, intArray); intSet = a1.inRange(new RealRange(-3., 7.)); IntArrayTest.assertEquals("inrange", new int[] { 0, 1, 2, 3 }, intSet .getIntArray()); intSet = a1.inRange(new RealRange(4.5, 4.6)); IntArrayTest .assertEquals("inrange", new int[] {}, intSet.getIntArray()); } /** * Test method for 'org.xmlcml.euclid.RealArray.outOfRange(RealRange)' */ @Test public void testOutOfRange() { RealRange range = new RealRange(1.3, 5.); IntSet intSet = a1.outOfRange(range); IntArray intArray = intSet.getIntArray(); IntArrayTest.assertEquals("inrange", new int[] { 0, 3 }, intArray); intSet = a1.outOfRange(new RealRange(-3., 7.)); IntArrayTest .assertEquals("inrange", new int[] {}, intSet.getIntArray()); intSet = a1.outOfRange(new RealRange(4.5, 4.6)); IntArrayTest.assertEquals("inrange", new int[] { 0, 1, 2, 3 }, intSet .getIntArray()); } /** * Test method for 'org.xmlcml.euclid.RealArray.getStringValues()' */ @Test public void testGetStringValues() { String[] ss = a1.getStringValues(); StringTestBase.assertEquals("string values", new String[] { "1.0", "2.0", "4.0", "6.0" }, ss); } /** * Test method for 'org.xmlcml.euclid.RealArray.sortAscending()' */ @Test public void testSortAscending() { RealArray ra = new RealArray("1 6 3 9 2 0"); ra.sortAscending(); RealArrayTest.assertEquals("sortAscending", new double[] { 0., 1., 2., 3., 6., 9. }, ra, EPS); } /** * Test method for 'org.xmlcml.euclid.RealArray.sortDescending()' */ @Test public void testSortDescending() { RealArray ra = new RealArray("1 6 3 9 2 0"); ra.sortDescending(); RealArrayTest.assertEquals("sortDescending", new double[] { 9., 6., 3., 2., 1., 0 }, ra, EPS); } /** * Test method for 'org.xmlcml.euclid.RealArray.reverse()' */ @Test public void testReverse() { RealArray ra = new RealArray("1 6 3 9 2 0"); ra.reverse(); RealArrayTest.assertEquals("reverse", new double[] { 0., 2., 9., 3., 6., 1. }, ra, EPS); } @Test public void testCreateReorderedArray() { RealArray ra = new RealArray(new double[]{3.0, 1.0, 5.0, 4.0, 7.0, 0.0}); IntSet intSet = ra.indexSortAscending(); Assert.assertEquals("sort", "(5,1,0,3,2,4)", intSet.toString()); RealArray ra0 = ra.createReorderedArray(intSet); Assert.assertEquals("sort", "(0.0,1.0,3.0,4.0,5.0,7.0)", ra0.toString()); } /** * Test method for 'org.xmlcml.euclid.RealArray.indexSortAscending()' */ @Test public void testIndexSortAscending() { RealArray ra = new RealArray("1 6 3 9 2 0"); IntSet intSet = ra.indexSortAscending(); IntArrayTest.assertEquals("sortAscending", new int[] { 5, 0, 4, 2, 1, 3 }, intSet.getIntArray()); } /** * Test method for 'org.xmlcml.euclid.RealArray.indexSortDescending()' */ @Test public void testIndexSortDescending() { RealArray ra = new RealArray("1 6 3 9 2 0"); IntSet intSet = ra.indexSortDescending(); IntArrayTest.assertEquals("sortDescending", new int[] { 3, 1, 2, 4, 0, 5 }, intSet.getIntArray()); } @Test public void testCreateScaledArray() { RealArray ra = new RealArray(new double[]{1., 2., 3.}); RealArray ra1 = ra.createScaledArrayToRange(3., 7.); RealArrayTest.assertEquals("scale", new double[] {3., 5., 7.}, ra1, 0.001); } @Test public void testCreateScaledArray1() { RealArray ra = new RealArray(new double[]{1., 2., 3.}); RealArray ra1 = ra.createScaledArrayToRange(-3., -7.); RealArrayTest.assertEquals("scale", new double[] {-3., -5., -7.}, ra1, 0.001); } @Test public void testCreateScaledArray0() { RealArray ra = new RealArray(new double[]{1., 2., 3.}); RealArray ra1 = ra.createScaledArrayToRange(3., 3.); RealArrayTest.assertEquals("scale", new double[] {3., 3., 3.}, ra1, 0.001); } @Test public void testCreateScaledArray00() { RealArray ra = new RealArray(new double[]{1., 1., 1.}); RealArray ra1 = ra.createScaledArrayToRange(3., 4.); Assert.assertNull(ra1); } @Test public void testCreateScaledArray2() { RealArray ra = new RealArray(new double[]{1., 2., 3.}); RealArray ra1 = ra.createScaledArrayToRange(0.5, 2.5, 100., 200.); RealArrayTest.assertEquals("scale", new double[] {125., 175., 225.}, ra1, 0.001); } @Test public void testCreateRealArrayIntArray() { IntArray intArray = new IntArray(new int[]{1, 2, 3}); RealArray realArray = RealArray.createRealArray(intArray); RealArrayTest.assertEquals("integers", new double[] {1., 2., 3.}, realArray, 0.001); } @Test public void testCreateRealArrayIntArray1() { int[] ints = new int[]{1, 2, 3}; RealArray realArray = RealArray.createRealArray(ints); RealArrayTest.assertEquals("integers", new double[] {1., 2., 3.}, realArray, 0.001); } @Test public void testIterator() { RealArray realArray = new RealArray(new double[]{0,1,2}); Iterator realIterator = realArray.iterator(); Assert.assertTrue("start", realIterator.hasNext()); Assert.assertTrue("start", realIterator.hasNext()); Assert.assertTrue("start", realIterator.hasNext()); Assert.assertTrue("start", realIterator.hasNext()); Assert.assertEquals("start", 0, (double) realIterator.next(), 0.001); Assert.assertEquals("start", 1, (double) realIterator.next(), 0.001); Assert.assertTrue("after 1", realIterator.hasNext()); Assert.assertEquals("after 1", 2, (double) realIterator.next(), 0.001); Assert.assertFalse("end", realIterator.hasNext()); Assert.assertNull("after 2", realIterator.next()); } @Test public void testIterators() { RealArray realArray = new RealArray(new double[]{0,1,2}); Iterator realIterator00 = realArray.iterator(); Iterator realIterator01 = realArray.iterator(); Assert.assertTrue("start", realIterator00.hasNext()); Assert.assertEquals("start", 0., (double) realIterator00.next(), 0.001); Assert.assertEquals("start", 1., (double) realIterator00.next(), 0.001); Assert.assertEquals("start", 0., (double) realIterator01.next(), 0.001); Assert.assertEquals("end0", 2., (double) realIterator00.next(), 0.001); Assert.assertFalse("end0", realIterator00.hasNext()); Assert.assertTrue("middle1", realIterator01.hasNext()); Assert.assertNull("endo", realIterator00.next()); Assert.assertEquals("start", 1., (double) realIterator01.next(), 0.001); } @Test public void testSumProductOfAllElements() { RealArray ra = new RealArray(new double[]{1., 3., 4.}); RealArray rb = new RealArray(new double[]{2., 5., 3.}); Assert.assertEquals("sum", 29., ra.sumProductOfAllElements(rb), 0.001); } } euclid-euclid-2.9/src/test/java/org/xmlcml/euclid/test/RealComparatorTest.java000077500000000000000000000071641461721410700275260ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid.test; import java.util.HashSet; import java.util.Set; import java.util.TreeSet; import org.junit.Assert; import org.junit.Test; import org.xmlcml.euclid.RealComparator; public class RealComparatorTest { public final static Double ZERO = 0.0; public final static Double ONE = 1.0; public final static Double TWO = 2.0; public final static Double THREE = 3.0; public final static Double SQR3 = Math.sqrt(THREE); @Test public void comparatorTest() { RealComparator comparator = new RealComparator(ZERO); Assert.assertEquals(0, comparator.compare(ONE, ONE)); Assert.assertEquals(1, comparator.compare(ONE, ZERO)); Assert.assertFalse(comparator.compare(THREE, SQR3*SQR3) == 0); Assert.assertEquals(-1, comparator.compare(ONE, TWO)); } @Test public void comparatorTest1() { RealComparator comparator = new RealComparator(0.01); Assert.assertEquals(0, comparator.compare(ONE, 1.001)); Assert.assertEquals(0, comparator.compare(ONE, 0.999)); Assert.assertEquals(0, comparator.compare(THREE, SQR3*SQR3)); Assert.assertEquals(1, comparator.compare(ONE, 0.98)); Assert.assertEquals(-1, comparator.compare(ONE, 1.02)); } /** HashSet only works with exactness * */ @Test public void testHashSet(){ Set set = new HashSet(); set.add(1.0); set.add(1.0); Assert.assertEquals(1, set.size()); } /** HashSet only works with exactness * */ @Test public void testHashSet1(){ Set set = new HashSet(); set.add(1.0); set.add((Math.sqrt(3.0)*Math.sqrt(3.0))/3.0); Assert.assertEquals(2, set.size()); } /** TreeSet works * */ @Test public void testTreeSet(){ RealComparator comparator = new RealComparator(0.0); Set set = new TreeSet(comparator); set.add(ONE); set.add(ONE); set.add(THREE); Assert.assertEquals(2, set.size()); } /** TreeSet works * */ @Test public void testTreeSet1(){ RealComparator comparator = new RealComparator(0.0); Set set = new TreeSet(comparator); set.add(ONE); set.add(ONE-0.001); set.add(THREE); Assert.assertEquals(3, set.size()); } /** TreeSet works * */ @Test public void testTreeSet2(){ RealComparator comparator = new RealComparator(0.01); Set set = new TreeSet(comparator); set.add(ONE); set.add(ONE - 0.001); set.add(THREE); Assert.assertEquals(2, set.size()); } /** TreeSet works * */ @Test public void testTreeSet3(){ RealComparator comparator = new RealComparator(0.01); Set set = new TreeSet(comparator); set.add(ONE - 0.001); set.add(ONE); set.add(THREE); Assert.assertEquals(2, set.size()); } /** TreeSet works * */ @Test public void testTreeSetContains(){ RealComparator comparator = new RealComparator(0.01); Set set = new TreeSet(comparator); set.add(ONE - 0.001); set.add(ONE); set.add(THREE); Assert.assertEquals(2, set.size()); Assert.assertTrue(set.contains(1.0)); Assert.assertTrue(set.contains(0.995)); } } euclid-euclid-2.9/src/test/java/org/xmlcml/euclid/test/RealMatrixTest.java000066400000000000000000001214011461721410700266470ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid.test; import java.io.IOException; import java.io.StringWriter; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.xmlcml.euclid.EC; import org.xmlcml.euclid.EuclidRuntimeException; import org.xmlcml.euclid.Int; import org.xmlcml.euclid.Int2; import org.xmlcml.euclid.IntMatrix; import org.xmlcml.euclid.IntSet; import org.xmlcml.euclid.Real2Array; import org.xmlcml.euclid.RealArray; import org.xmlcml.euclid.RealMatrix; import org.xmlcml.euclid.RealRange; /** * test RealMatrix * * @author pmr * */ public class RealMatrixTest{ private final static Logger LOG = Logger.getLogger(RealMatrixTest.class); static { LOG.setLevel(Level.DEBUG); } RealMatrix m0; RealMatrix m1; RealMatrix m2; /** * setup. * * @throws Exception */ @Before public void setUp() throws Exception { LOG.setLevel(Level.WARN); m0 = new RealMatrix(); m1 = new RealMatrix(3, 4); m2 = new RealMatrix(3, 4, new double[] { 11., 12., 13., 14., 21., 22., 23., 24., 31., 32., 33., 34., }); } /** * equality test. true if both args not null and equal within epsilon and * rows are present and equals and columns are present and equals * * @param msg * message * @param test * @param expected * @param epsilon */ public static void assertEquals(String msg, RealMatrix test, RealMatrix expected, double epsilon) { Assert.assertNotNull("test should not be null (" + msg + EC.S_RBRAK, test); Assert.assertNotNull("expected should not be null (" + msg + EC.S_RBRAK, expected); Assert.assertNotNull("expected should have columns (" + msg + EC.S_RBRAK, expected.getCols()); Assert.assertNotNull("expected should have rows (" + msg + EC.S_RBRAK, expected.getRows()); Assert.assertNotNull("test should have columns (" + msg + EC.S_RBRAK, test .getCols()); Assert.assertNotNull("test should have rows (" + msg + EC.S_RBRAK, test .getRows()); Assert.assertEquals("rows should be equal (" + msg + EC.S_RBRAK, test .getRows(), expected.getRows()); Assert.assertEquals("columns should be equal (" + msg + EC.S_RBRAK, test .getCols(), expected.getCols()); DoubleTestBase.assertEquals(msg, test.getMatrixAsArray(), expected .getMatrixAsArray(), epsilon); } /** * equality test. true if both args not null and equal within epsilon * * @param msg * message * @param rows * @param cols * @param test * @param expected * @param epsilon */ public static void assertEquals(String msg, int rows, int cols, double[] test, RealMatrix expected, double epsilon) { Assert.assertNotNull("test should not be null (" + msg + EC.S_RBRAK, test); Assert.assertNotNull("ref should not be null (" + msg + EC.S_RBRAK, expected); Assert.assertEquals("rows should be equal (" + msg + EC.S_RBRAK, rows, expected.getRows()); Assert.assertEquals("columns should be equal (" + msg + EC.S_RBRAK, cols, expected.getCols()); DoubleTestBase.assertEquals(msg, test, expected.getMatrixAsArray(), epsilon); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.RealMatrix()' */ @Test public void testRealMatrix() { Assert.assertEquals("empty", "()", m0.toString()); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.RealMatrix(int, int)' */ @Test public void testRealMatrixIntInt() { Assert.assertEquals("int int", "{3,4}" + "\n(0.0,0.0,0.0,0.0)" + "\n(0.0,0.0,0.0,0.0)" + "\n(0.0,0.0,0.0,0.0)", m1.toString()); Assert.assertEquals("int int rows", 3, m1.getRows()); Assert.assertEquals("int int cols", 4, m1.getCols()); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.RealMatrix(int, int, * double[])' */ @Test public void testRealMatrixIntIntDoubleArray() { Assert.assertEquals("int int double[]", "{3,4}" + "\n(11.0,12.0,13.0,14.0)" + "\n(21.0,22.0,23.0,24.0)" + "\n(31.0,32.0,33.0,34.0)", m2.toString()); Assert.assertEquals("int int double[] rows", 3, m2.getRows()); Assert.assertEquals("int int double[] cols", 4, m2.getCols()); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.RealMatrix(int, int, * double)' */ @Test public void testRealMatrixIntIntDouble() { RealMatrix m = new RealMatrix(3, 4, 10.); Assert.assertEquals("int int double[]", "{3,4}" + "\n(10.0,10.0,10.0,10.0)" + "\n(10.0,10.0,10.0,10.0)" + "\n(10.0,10.0,10.0,10.0)", m.toString()); Assert.assertEquals("int int double[] rows", 3, m.getRows()); Assert.assertEquals("int int double[] cols", 4, m.getCols()); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.RealMatrix(RealMatrix, int, * int, int, int)' */ @Test public void testRealMatrixRealMatrixIntIntIntInt() { RealMatrix m = new RealMatrix(m2, 1, 2, 1, 3); Assert.assertEquals("int int double[]", "{2,3}" + "\n(22.0,23.0,24.0)" + "\n(32.0,33.0,34.0)", m.toString()); Assert.assertEquals("int int double[] rows", 2, m.getRows()); Assert.assertEquals("int int double[] cols", 3, m.getCols()); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.RealMatrix(RealMatrix)' */ @Test public void testRealMatrixRealMatrix() { RealMatrix m = new RealMatrix(m2); Assert.assertEquals("int int double[]", "{3,4}" + "\n(11.0,12.0,13.0,14.0)" + "\n(21.0,22.0,23.0,24.0)" + "\n(31.0,32.0,33.0,34.0)", m.toString()); Assert.assertEquals("int int double[] rows", 3, m.getRows()); Assert.assertEquals("int int double[] cols", 4, m.getCols()); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.RealMatrix(IntMatrix)' */ @Test public void testRealMatrixIntMatrix() { IntMatrix i2 = new IntMatrix(3, 4, new int[] { 11, 12, 13, 14, 21, 22, 23, 24, 31, 32, 33, 34, }); RealMatrix m = new RealMatrix(i2); Assert.assertEquals("int int double[]", "{3,4}" + "\n(11.0,12.0,13.0,14.0)" + "\n(21.0,22.0,23.0,24.0)" + "\n(31.0,32.0,33.0,34.0)", m.toString()); Assert.assertEquals("int int double[] rows", 3, m.getRows()); Assert.assertEquals("int int double[] cols", 4, m.getCols()); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.getIntMatrix()' */ @Test public void testGetIntMatrix() { RealMatrix mm2 = new RealMatrix(3, 4, new double[] { 11.1, 12.1, 13.1, 14.1, 21.1, 22.1, 23.1, 24.1, 31.1, 32.1, 33.1, 34.1, }); IntMatrix m = mm2.getIntMatrix(); Assert.assertEquals("int int double[]", "{3,4}" + "\n(11,12,13,14)" + "\n(21,22,23,24)" + "\n(31,32,33,34)", m.toString()); Assert.assertEquals("int int double[] rows", 3, m.getRows()); Assert.assertEquals("int int double[] cols", 4, m.getCols()); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.RealMatrix(double[][])' */ @Test public void testRealMatrixDoubleArrayArray() { RealMatrix mm2 = new RealMatrix(new double[][] { new double[] { 11.1, 12.1, 13.1, 14.1 }, new double[] { 21.1, 22.1, 23.1, 24.1 }, new double[] { 31.1, 32.1, 33.1, 34.1 } }); IntMatrix m = mm2.getIntMatrix(); Assert.assertEquals("int int double[]", "{3,4}" + "\n(11,12,13,14)" + "\n(21,22,23,24)" + "\n(31,32,33,34)", m.toString()); Assert.assertEquals("int int double[] rows", 3, m.getRows()); Assert.assertEquals("int int double[] cols", 4, m.getCols()); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.setFormat(DecimalFormat)' */ @Test public void testSetFormat() { } /** * Test method for 'org.xmlcml.euclid.RealMatrix.getFormat()' */ @Test public void testGetFormat() { } /** * Test method for 'org.xmlcml.euclid.RealMatrix.getRows()' */ @Test public void testGetRowsCols() { RealMatrix m = new RealMatrix(new double[][] { new double[] { 11.1, 12.1, 13.1, 14.1 }, new double[] { 21.1, 22.1, 23.1, 24.1 }, new double[] { 31.1, 32.1, 33.1, 34.1 } }); Assert.assertEquals("int int double[] rows", 3, m.getRows()); Assert.assertEquals("int int double[] cols", 4, m.getCols()); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.getMatrix()' */ @Test public void testGetMatrix() { double[][] matrix = m1.getMatrix(); Assert.assertEquals("getMatrix", 3, matrix.length); Assert.assertEquals("getMatrix", 4, matrix[0].length); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.getMatrixAsArray()' */ @Test public void testGetMatrixAsArray() { double[] array = m2.getMatrixAsArray(); DoubleTestBase.assertEquals("matrix as array", new double[] { 11.0, 12.0, 13.0, 14.0, 21.0, 22.0, 23.0, 24.0, 31.0, 32.0, 33.0, 34.0 }, array, EC.EPS); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.isEqualTo(RealMatrix)' */ @Test public void testIsEqualTo() { Assert.assertTrue("isEqualTo", m2.isEqualTo(m2)); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.plus(RealMatrix)' */ @Test public void testPlus() { RealMatrix m = m2.plus(m2); RealMatrixTest.assertEquals("matrix as array", 3, 4, new double[] { 22.0, 24.0, 26.0, 28.0, 42.0, 44.0, 46.0, 48.0, 62.0, 64.0, 66.0, 68.0 }, m, EC.EPS); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.subtract(RealMatrix)' */ @Test public void testSubtract() { RealMatrix m = new RealMatrix(new double[][] { new double[] { 11.1, 12.1, 13.1, 14.1 }, new double[] { 21.1, 22.1, 23.1, 24.1 }, new double[] { 31.1, 32.1, 33.1, 34.1 } }); RealMatrix mm = m2.subtract(m); RealMatrixTest.assertEquals("matrix as array", 3, 4, new double[] { -0.1, -0.1, -0.1, -0.1, -0.1, -0.1, -0.1, -0.1, -0.1, -0.1, -0.1, -0.1, }, mm, EC.EPS); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.negative()' */ @Test public void testNegative() { m2.negative(); RealMatrixTest.assertEquals("matrix as array", 3, 4, new double[] { -11.0, -12.0, -13.0, -14.0, -21.0, -22.0, -23.0, -24.0, -31.0, -32.0, -33.0, -34.0 }, m2, EC.EPS); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.multiply(RealMatrix)' */ @Test public void testMultiplyRealMatrix() { RealMatrix m = new RealMatrix(new double[][] { new double[] { 10, 20, 30 }, new double[] { 40, 50, 60 }, }); RealMatrix mm = m.multiply(m2); RealMatrixTest.assertEquals("matrix as array", 2, 4, new double[] { 1460.0, 1520.0, 1580.0, 1640.0, 3350.0, 3500.0, 3650.0, 3800.0, }, mm, EC.EPS); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.multiplyBy(double)' */ @Test public void testMultiplyBy() { m2.multiplyBy(10.); RealMatrixTest.assertEquals("matrix as array", 3, 4, new double[] { 110.0, 120.0, 130.0, 140.0, 210.0, 220.0, 230.0, 240.0, 310.0, 320.0, 330.0, 340.0, }, m2, EC.EPS); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.multiplyEquals(RealMatrix)' */ @Test public void testMultiplyEquals() { RealMatrix m = new RealMatrix(new double[][] { new double[] { 10, 20, 30 }, new double[] { 40, 50, 60 }, }); try { m2.multiplyEquals(m); Assert.fail("non-conformable matrices"); } catch (EuclidRuntimeException e) { Assert.assertEquals("multiplyEquals", "unequal matrices (4, 2)", e .getMessage()); } m.multiplyEquals(m2); RealMatrixTest.assertEquals("matrix as array", 2, 4, new double[] { 1460.0, 1520.0, 1580.0, 1640.0, 3350.0, 3500.0, 3650.0, 3800.0, }, m, EC.EPS); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.multiply(RealArray)' */ @Test public void testMultiplyRealArray() { RealArray ra = new RealArray(new double[] { 1., 2., 3., 4. }); RealArray raa = m2.multiply(ra); RealArrayTest.assertEquals("array", new double[] { 130.0, 230.0, 330.0 }, raa, EC.EPS); } /** * Test method for * 'org.xmlcml.euclid.RealMatrix.columnwiseDivide(RealArray)' */ @Test public void testColumnwiseDivide() { RealArray ra = new RealArray(new double[] { 1., 2., 3., 4. }); m2.columnwiseDivide(ra); RealMatrixTest.assertEquals("array", 3, 4, new double[] { 11.0, 6.0, 4.333333, 3.5, 21.0, 11.0, 7.66666666, 6.0, 31.0, 16.0, 11.0, 8.5, }, m2, 0.00001); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.elementAt(int, int)' */ @Test public void testElementAtIntInt() { Assert.assertEquals("elementAt ", 32.0, m2.elementAt(2, 1), EC.EPS); try { m2.elementAt(5, 5); } catch (EuclidRuntimeException e) { Assert.assertEquals("elementAt", "Bad value of row: 5/3", e .getMessage()); } } /** * Test method for 'org.xmlcml.euclid.RealMatrix.elementAt(Int2)' */ @Test public void testElementAtInt2() { Assert.assertEquals("elementAt ", 32.0, m2.elementAt(new Int2(2, 1)), EC.EPS); try { m2.elementAt(new Int2(5, 5)); } catch (EuclidRuntimeException e) { Assert.assertEquals("elementAt", "Bad value of row: 5/3", e .getMessage()); } } /** * Test method for 'org.xmlcml.euclid.RealMatrix.setElementAt(int, int, * double)' */ @Test public void testSetElementAt() { m2.setElementAt(1, 2, 15.); RealMatrixTest.assertEquals("matrix as array", 3, 4, new double[] { 11.0, 12.0, 13.0, 14.0, 21.0, 22.0, 15.0, 24.0, 31.0, 32.0, 33.0, 34.0, }, m2, EC.EPS); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.largestElement()' */ @Test public void testLargestElement() { double d = m2.largestElement(); Assert.assertEquals("largestElement", 34., d, EC.EPS); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.indexOfLargestElement()' */ @Test public void testIndexOfLargestElement() { Int2 ii = m2.indexOfLargestElement(); Assert.assertEquals("indexOfLargestElement", 2, ii.getX()); Assert.assertEquals("indexOfLargestElement", 3, ii.getY()); } /** * Test method for * 'org.xmlcml.euclid.RealMatrix.largestElementInColumn(int)' */ @Test public void testLargestElementInColumn() { double d = m2.largestElementInColumn(1); Assert.assertEquals("largestElement", 32., d, EC.EPS); } /** * Test method for * 'org.xmlcml.euclid.RealMatrix.indexOfLargestElementInColumn(int)' */ @Test public void testIndexOfLargestElementInColumn() { int i = m2.indexOfLargestElementInColumn(1); Assert.assertEquals("largestElement", 2, i); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.largestElementInRow(int)' */ @Test public void testLargestElementInRow() { double d = m2.largestElementInRow(1); Assert.assertEquals("largestElement", 24., d, EC.EPS); } /** * Test method for * 'org.xmlcml.euclid.RealMatrix.indexOfLargestElementInRow(int)' */ @Test public void testIndexOfLargestElementInRow() { int i = m2.indexOfLargestElementInRow(1); Assert.assertEquals("largestElement", 3, i); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.smallestElement()' */ @Test public void testSmallestElement() { double d = m2.smallestElement(); Assert.assertEquals("smallestElement", 11., d, EC.EPS); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.indexOfSmallestElement()' */ @Test public void testIndexOfSmallestElement() { Int2 ii = m2.indexOfSmallestElement(); Assert.assertEquals("indexOfSmallestElement", 0, ii.getX()); Assert.assertEquals("indexOfSmallestElement", 0, ii.getY()); } /** * Test method for * 'org.xmlcml.euclid.RealMatrix.smallestElementInColumn(int)' */ @Test public void testSmallestElementInColumn() { double d = m2.smallestElementInColumn(1); Assert.assertEquals("smallestElement", 12., d, EC.EPS); } /** * Test method for * 'org.xmlcml.euclid.RealMatrix.indexOfSmallestElementInColumn(int)' */ @Test public void testIndexOfSmallestElementInColumn() { int i = m2.indexOfSmallestElementInColumn(1); Assert.assertEquals("largestElement", 0, i); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.smallestElementInRow(int)' */ @Test public void testSmallestElementInRow() { double d = m2.smallestElementInRow(1); Assert.assertEquals("smallestElement", 21., d, EC.EPS); } /** * Test method for * 'org.xmlcml.euclid.RealMatrix.indexOfSmallestElementInRow(int)' */ @Test public void testIndexOfSmallestElementInRow() { int i = m2.indexOfSmallestElementInRow(1); Assert.assertEquals("largestElement", 0, i); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.isOrthogonal()' */ @Test public void testIsOrthogonal() { Assert.assertFalse("orthogonal", m2.isOrthogonal()); RealMatrix m = new RealMatrix(2, 2, new double[] { 1., 0., 0., 1. }); Assert.assertTrue("orthogonal", m.isOrthogonal()); m = new RealMatrix(2, 2, new double[] { Math.cos(Math.PI / 3.), Math.sin(Math.PI / 3.), -Math.sin(Math.PI / 3.), Math.cos(Math.PI / 3.) }); Assert.assertTrue("orthogonal", m.isOrthogonal()); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.euclideanRowLength(int)' */ @Test public void testEuclideanRowLength() { double d = m2.euclideanRowLength(1); Assert.assertEquals("euclidean row length", 45.05552130427524, d, EC.EPS); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.euclideanRowLengths()' */ @Test public void testEuclideanRowLengths() { RealArray ra = m2.euclideanRowLengths(); RealArrayTest.assertEquals("euclidean row lengths", new double[] { 25.099800796022265, 45.05552130427524, 65.0384501660364 }, ra, EC.EPS); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.euclideanColumnLength(int)' */ @Test public void testEuclideanColumnLength() { double d = m2.euclideanColumnLength(1); Assert.assertEquals("euclidean row length", 40.64480286580315, d, EC.EPS); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.euclideanColumnLengths()' */ @Test public void testEuclideanColumnLengths() { RealArray ra = m2.euclideanColumnLengths(); RealArrayTest.assertEquals("euclidean column lengths", new double[] { 39.02563260217571, 40.64480286580315, 42.2729227756965, 43.9089968002003 }, ra, EC.EPS); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.extractColumnData(int)' */ @Test public void testExtractColumnData() { RealArray ra = m2.extractColumnData(1); RealArrayTest.assertEquals("euclidean column lengths", new double[] { 12., 22., 32. }, ra, EC.EPS); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.extractRowData(int)' */ @Test public void testExtractRowData() { RealArray ra = m2.extractRowData(1); RealArrayTest.assertEquals("euclidean column lengths", new double[] { 21., 22., 23., 24. }, ra, EC.EPS); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.clearMatrix()' */ @Test public void testClearMatrix() { m2.clearMatrix(); RealMatrixTest.assertEquals("matrix as array", 3, 4, new double[] { 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., }, m2, EC.EPS); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.setAllElements(double)' */ @Test public void testSetAllElements() { m2.setAllElements(23.); RealMatrixTest.assertEquals("matrix as array", 3, 4, new double[] { 23., 23., 23., 23., 23., 23., 23., 23., 23., 23., 23., 23., }, m2, EC.EPS); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.normaliseByRows()' */ @Test public void testNormaliseByRows() { m2.normaliseByRows(); RealMatrixTest.assertEquals("matrix as array", 3, 4, new double[] { 0.4382504900892777, 0.4780914437337575, 0.5179323973782373, 0.5577733510227171, 0.46609159969939906, 0.4882864377803228, 0.5104812758612466, 0.5326761139421703, 0.47664112414825727, 0.49201664428207204, 0.5073921644158867, 0.5227676845497016, }, m2, EC.EPS); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.normaliseByColumns()' */ @Test public void testNormaliseByColumns() { m2.normaliseByColumns(); RealMatrixTest.assertEquals("matrix as array", 3, 4, new double[] { 0.2818660266736263, 0.29524069878307374, 0.30752545947624765, 0.31884126307199384, 0.5381078691041957, 0.5412746144356352, 0.5440835052272074, 0.5465850224091323, 0.7943497115347651, 0.7873085300881967, 0.7806415509781671, 0.7743287817462708, }, m2, EC.EPS); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.getTranspose()' */ @Test public void testGetTranspose() { RealMatrix m = m2.getTranspose(); RealMatrixTest.assertEquals("transpose", 4, 3, new double[] { 11.0, 21.0, 31.0, 12.0, 22.0, 32.0, 13.0, 23.0, 33.0, 14.0, 24.0, 34.0, }, m, EC.EPS); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.isSquare()' */ @Test public void testIsSquare() { Assert.assertFalse("isSquare", m2.isSquare()); Assert.assertTrue("isSquare", new RealMatrix(2, 2, new double[] { 11., 12., 21., 22. }).isSquare()); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.deleteColumn(int)' */ @Test public void testDeleteColumn() { m2.deleteColumn(1); RealMatrixTest.assertEquals("matrix as array", 3, 3, new double[] { 11.0, 13.0, 14.0, 21.0, 23.0, 24.0, 31.0, 33.0, 34.0, }, m2, EC.EPS); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.deleteColumns(int, int)' */ @Test public void testDeleteColumns() { m2.deleteColumns(1, 2); RealMatrixTest.assertEquals("matrix as array", 3, 2, new double[] { 11.0, 14.0, 21.0, 24.0, 31.0, 34.0, }, m2, EC.EPS); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.deleteRow(int)' */ @Test public void testDeleteRow() { m2.deleteRow(1); RealMatrixTest.assertEquals("matrix as array", 2, 4, new double[] { 11.0, 12.0, 13.0, 14.0, 31.0, 32.0, 33.0, 34.0, }, m2, EC.EPS); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.deleteRows(int, int)' */ @Test public void testDeleteRows() { // FIXME does not work for high = nrows m2.deleteRows(1, 1); RealMatrixTest.assertEquals("matrix as array", 2, 4, new double[] { 11.0, 12., 13., 14.0, 31.0, 32., 33., 34.0, }, m2, EC.EPS); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.replaceColumnData(int, * RealArray)' */ @Test public void testReplaceColumnDataIntRealArray() { m2.replaceColumnData(1, new RealArray(new double[] { 19., 29., 39. })); RealMatrixTest.assertEquals("matrix as array", 3, 4, new double[] { 11.0, 19., 13., 14.0, 21.0, 29., 23., 24.0, 31.0, 39., 33., 34.0, }, m2, EC.EPS); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.replaceColumnData(int, * double[])' */ @Test public void testReplaceColumnDataIntDoubleArray() { m2.replaceColumnData(1, new double[] { 19., 29., 39. }); RealMatrixTest.assertEquals("matrix as array", 3, 4, new double[] { 11.0, 19., 13., 14.0, 21.0, 29., 23., 24.0, 31.0, 39., 33., 34.0, }, m2, EC.EPS); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.replaceColumnData(int, * RealMatrix)' */ @Test public void testReplaceColumnDataIntRealMatrix() { RealMatrix m = new RealMatrix(3, 2, new double[] { 72., 73., 82., 83., 92., 93. }); LOG.info("\n--OK replace-- 1 " + m + "\n----"); m2.replaceColumnData(1, m); RealMatrix expect = new RealMatrix(3, 4, new double[] { 11.0, 72.0, 73.0, 14.0, 21.0, 82.0, 83.0, 24.0, 31.0, 92.0, 93.0, 34.0, }); Assert.assertNotNull("test should not be null (" + "matrix as array" + EC.S_RBRAK, m2); Assert.assertNotNull("expected should not be null (" + "matrix as array" + EC.S_RBRAK, expect); Assert.assertNotNull("expected should have columns (" + "matrix as array" + EC.S_RBRAK, expect.getCols()); Assert.assertNotNull("expected should have rows (" + "matrix as array" + EC.S_RBRAK, expect.getRows()); Assert.assertNotNull("test should have columns (" + "matrix as array" + EC.S_RBRAK, m2 .getCols()); Assert.assertNotNull("test should have rows (" + "matrix as array" + EC.S_RBRAK, m2 .getRows()); Assert.assertEquals("rows should be equal (" + "matrix as array" + EC.S_RBRAK, m2 .getRows(), expect.getRows()); Assert.assertEquals("columns should be equal (" + "matrix as array" + EC.S_RBRAK, m2 .getCols(), expect.getCols()); DoubleTestBase.assertEquals("matrix as array", m2.getMatrixAsArray(), expect .getMatrixAsArray(), EC.EPS); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.insertColumns(int, int)' */ @Test public void testInsertColumns() { // inserts 3 empty columns m2.makeSpaceForNewColumns(1, 3); RealMatrix expect = new RealMatrix(3, 7, new double[] { 11.0, 0.0, 0.0, 0.0, 12.0, 13.0, 14.0, 21.0, 0.0, 0.0, 0.0, 22.0, 23.0, 24.0, 31.0, 0.0, 0.0, 0.0, 32.0, 33.0, 34.0, }); Assert.assertNotNull("test should not be null (" + "matrix as array" + EC.S_RBRAK, m2); Assert.assertNotNull("expected should not be null (" + "matrix as array" + EC.S_RBRAK, expect); Assert.assertNotNull("expected should have columns (" + "matrix as array" + EC.S_RBRAK, expect.getCols()); Assert.assertNotNull("expected should have rows (" + "matrix as array" + EC.S_RBRAK, expect.getRows()); Assert.assertNotNull("test should have columns (" + "matrix as array" + EC.S_RBRAK, m2 .getCols()); Assert.assertNotNull("test should have rows (" + "matrix as array" + EC.S_RBRAK, m2 .getRows()); Assert.assertEquals("rows should be equal (" + "matrix as array" + EC.S_RBRAK, m2 .getRows(), expect.getRows()); Assert.assertEquals("columns should be equal (" + "matrix as array" + EC.S_RBRAK, m2 .getCols(), expect.getCols()); DoubleTestBase.assertEquals("matrix as array", m2.getMatrixAsArray(), expect .getMatrixAsArray(), EC.EPS); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.insertColumnData(int, * RealArray)' */ @Test public void testInsertColumnDataIntRealArray() { // inserts a column m2.insertColumnData(1, new RealArray(new double[] { 91., 92., 93. })); RealMatrix expect = new RealMatrix(3, 5, new double[] { 11.0, 12.0, 91.0, 13.0, 14.0, 21.0, 22.0, 92.0, 23.0, 24.0, 31.0, 32.0, 93.0, 33.0, 34.0, }); Assert.assertNotNull("test should not be null (" + "matrix as array" + EC.S_RBRAK, m2); Assert.assertNotNull("expected should not be null (" + "matrix as array" + EC.S_RBRAK, expect); Assert.assertNotNull("expected should have columns (" + "matrix as array" + EC.S_RBRAK, expect.getCols()); Assert.assertNotNull("expected should have rows (" + "matrix as array" + EC.S_RBRAK, expect.getRows()); Assert.assertNotNull("test should have columns (" + "matrix as array" + EC.S_RBRAK, m2 .getCols()); Assert.assertNotNull("test should have rows (" + "matrix as array" + EC.S_RBRAK, m2 .getRows()); Assert.assertEquals("rows should be equal (" + "matrix as array" + EC.S_RBRAK, m2 .getRows(), expect.getRows()); Assert.assertEquals("columns should be equal (" + "matrix as array" + EC.S_RBRAK, m2 .getCols(), expect.getCols()); DoubleTestBase.assertEquals("matrix as array", m2.getMatrixAsArray(), expect .getMatrixAsArray(), EC.EPS); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.insertColumnData(int, * RealMatrix)' */ @Test public void testInsertColumnDataIntRealMatrix() { LOG.info("+++insertColumnData>>>"); RealMatrix insert = new RealMatrix(3, 2, new double[] { 72., 73., 82., 83., 92., 93., }); m2.insertColumnData(1, insert); RealMatrix expect = new RealMatrix(3, 6, new double[] { 11.0, 12.0, 72.0, 73.0, 13.0, 14.0, 21.0, 22.0, 82.0, 83.0, 23.0, 24.0, 31.0, 32.0, 92.0, 93.0, 33.0, 34.0, }); Assert.assertNotNull("test should not be null (" + "matrix as array" + EC.S_RBRAK, m2); Assert.assertNotNull("expected should not be null (" + "matrix as array" + EC.S_RBRAK, expect); Assert.assertNotNull("expected should have columns (" + "matrix as array" + EC.S_RBRAK, expect.getCols()); Assert.assertNotNull("expected should have rows (" + "matrix as array" + EC.S_RBRAK, expect.getRows()); Assert.assertNotNull("test should have columns (" + "matrix as array" + EC.S_RBRAK, m2 .getCols()); Assert.assertNotNull("test should have rows (" + "matrix as array" + EC.S_RBRAK, m2 .getRows()); Assert.assertEquals("rows should be equal (" + "matrix as array" + EC.S_RBRAK, m2 .getRows(), expect.getRows()); Assert.assertEquals("columns should be equal (" + "matrix as array" + EC.S_RBRAK, m2 .getCols(), expect.getCols()); DoubleTestBase.assertEquals("matrix as array", m2.getMatrixAsArray(), expect .getMatrixAsArray(), EC.EPS); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.insertRows(int, int)' */ @Test public void testInsertRows() { m2.insertRows(1, 2); RealMatrixTest.assertEquals("matrix as array", 5, 4, new double[] { 11.0, 12.0, 13.0, 14.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 21.0, 22.0, 23.0, 24.0, 31.0, 32.0, 33.0, 34.0, }, m2, EC.EPS); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.replaceRowData(int, * RealArray)' */ @Test public void testReplaceRowDataIntRealArray() { m2.replaceRowData(1, new RealArray(new double[] { 71.0, 72., 73., 74. })); RealMatrixTest.assertEquals("matrix as array", 3, 4, new double[] { 11.0, 12.0, 13.0, 14.0, 71.0, 72.0, 73.0, 74.0, 31.0, 32.0, 33.0, 34.0, }, m2, EC.EPS); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.replaceRowData(int, * double[])' */ @Test public void testReplaceRowDataIntDoubleArray() { m2.replaceRowData(1, new double[] { 71.0, 72., 73., 74. }); RealMatrixTest.assertEquals("matrix as array", 3, 4, new double[] { 11.0, 12.0, 13.0, 14.0, 71.0, 72.0, 73.0, 74.0, 31.0, 32.0, 33.0, 34.0, }, m2, EC.EPS); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.replaceRowData(int, * RealMatrix)' */ @Test public void testReplaceRowDataIntRealMatrix() { LOG.info("+++replaceRowData>>>"); // FIXME RealMatrix insert = new RealMatrix(new RealMatrix(2, 4, new double[] { 71.0, 72.0, 73.0, 74.0, 81.0, 82.0, 83.0, 84.0, })); m2.replaceRowData(0, insert); RealMatrix expect = new RealMatrix(3, 4, new double[] { 11.0, 12.0, 13.0, 14.0, 71.0, 72.0, 73.0, 74.0, 81.0, 82.0, 83.0, 84.0, }); // rows 2 and 3 are not filled Assert.assertNotNull("test should not be null (" + "matrix as array" + EC.S_RBRAK, m2); Assert.assertNotNull("expected should not be null (" + "matrix as array" + EC.S_RBRAK, expect); Assert.assertNotNull("expected should have columns (" + "matrix as array" + EC.S_RBRAK, expect.getCols()); Assert.assertNotNull("expected should have rows (" + "matrix as array" + EC.S_RBRAK, expect.getRows()); Assert.assertNotNull("test should have columns (" + "matrix as array" + EC.S_RBRAK, m2 .getCols()); Assert.assertNotNull("test should have rows (" + "matrix as array" + EC.S_RBRAK, m2 .getRows()); Assert.assertEquals("rows should be equal (" + "matrix as array" + EC.S_RBRAK, m2 .getRows(), expect.getRows()); Assert.assertEquals("columns should be equal (" + "matrix as array" + EC.S_RBRAK, m2 .getCols(), expect.getCols()); DoubleTestBase.assertEquals("matrix as array", m2.getMatrixAsArray(), expect .getMatrixAsArray(), EC.EPS); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.insertRowData(int, * RealMatrix)' */ @Test public void testInsertRowDataIntRealMatrix() { // FIXME m2.insertRowData(1, new RealMatrix(2, 4, new double[] { 71.0, 72., 73., 74., 81.0, 82., 83., 84., })); RealMatrix expect = new RealMatrix(5, 4, new double[] { 11.0, 12.0, 13.0, 14.0, 21.0, 22.0, 23.0, 24.0, 71.0, 72.0, 73.0, 74.0, 81.0, 82.0, 83.0, 84.0, 31.0, 32.0, 33.0, 34.0, }); Assert.assertNotNull("test should not be null (" + "matrix as array" + EC.S_RBRAK, m2); Assert.assertNotNull("expected should not be null (" + "matrix as array" + EC.S_RBRAK, expect); Assert.assertNotNull("expected should have columns (" + "matrix as array" + EC.S_RBRAK, expect.getCols()); Assert.assertNotNull("expected should have rows (" + "matrix as array" + EC.S_RBRAK, expect.getRows()); Assert.assertNotNull("test should have columns (" + "matrix as array" + EC.S_RBRAK, m2 .getCols()); Assert.assertNotNull("test should have rows (" + "matrix as array" + EC.S_RBRAK, m2 .getRows()); Assert.assertEquals("rows should be equal (" + "matrix as array" + EC.S_RBRAK, m2 .getRows(), expect.getRows()); Assert.assertEquals("columns should be equal (" + "matrix as array" + EC.S_RBRAK, m2 .getCols(), expect.getCols()); DoubleTestBase.assertEquals("matrix as array", m2.getMatrixAsArray(), expect .getMatrixAsArray(), EC.EPS); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.insertRowData(int, * RealArray)' */ @Test public void testInsertRowDataIntRealArray() { m2.insertRowData(1, new RealArray(new double[] { 71.0, 72., 73., 74., })); RealMatrixTest.assertEquals("matrix as array", 4, 4, new double[] { 11.0, 12.0, 13.0, 14.0, 21.0, 22.0, 23.0, 24.0, 71.0, 72.0, 73.0, 74.0, 31.0, 32.0, 33.0, 34.0, }, m2, EC.EPS); } /** * Test method for * 'org.xmlcml.euclid.RealMatrix.appendColumnData(RealArray)' */ @Test public void testAppendColumnDataRealArray() { m2.appendColumnData(new RealArray(new double[] { 17., 27., 37., })); RealMatrixTest.assertEquals("matrix as array", 3, 5, new double[] { 11.0, 12.0, 13.0, 14.0, 17.0, 21.0, 22.0, 23.0, 24.0, 27.0, 31.0, 32.0, 33.0, 34.0, 37.0 }, m2, EC.EPS); } /** * Test method for * 'org.xmlcml.euclid.RealMatrix.appendColumnData(RealMatrix)' */ @Test public void testAppendColumnDataRealMatrix() { // logger.info("+++appendColumnData>>>"); RealMatrix rm = new RealMatrix(3, 2, new double[] { 17., 18., 27., 28., 37., 38. }); m2.appendColumnData(rm); RealMatrix expect = new RealMatrix(3, 6, new double[] { 11.0, 12.0, 13.0, 14.0, 17.0, 18.0, 21.0, 22.0, 23.0, 24.0, 27.0, 28.0, 31.0, 32.0, 33.0, 34.0, 37.0, 38.0 }); Assert.assertNotNull("test should not be null (" + "matrix as array" + EC.S_RBRAK, m2); Assert.assertNotNull("expected should not be null (" + "matrix as array" + EC.S_RBRAK, expect); Assert.assertNotNull("expected should have columns (" + "matrix as array" + EC.S_RBRAK, expect.getCols()); Assert.assertNotNull("expected should have rows (" + "matrix as array" + EC.S_RBRAK, expect.getRows()); Assert.assertNotNull("test should have columns (" + "matrix as array" + EC.S_RBRAK, m2 .getCols()); Assert.assertNotNull("test should have rows (" + "matrix as array" + EC.S_RBRAK, m2 .getRows()); Assert.assertEquals("rows should be equal (" + "matrix as array" + EC.S_RBRAK, m2 .getRows(), expect.getRows()); Assert.assertEquals("columns should be equal (" + "matrix as array" + EC.S_RBRAK, m2 .getCols(), expect.getCols()); DoubleTestBase.assertEquals("matrix as array", m2.getMatrixAsArray(), expect .getMatrixAsArray(), EC.EPS); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.appendRowData(RealArray)' */ @Test public void testAppendRowDataRealArray() { RealArray ra = new RealArray(new double[] { 41., 42., 43., 44. }); m2.appendRowData(ra); // fails to insert data RealMatrixTest.assertEquals("matrix as array", 4, 4, new double[] { 11.0, 12.0, 13.0, 14.0, 21.0, 22.0, 23.0, 24.0, 31.0, 32.0, 33.0, 34.0, 41.0, 42.0, 43.0, 44.0 }, m2, EC.EPS); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.appendRowData(RealMatrix)' */ @Test public void testAppendRowDataRealMatrix() { LOG.info("+++appendRowData>>>"); // FIXME RealMatrix rm = new RealMatrix(2, 4, new double[] { 41., 42., 43., 44., 51., 52., 53., 54. }); m2.appendRowData(rm); RealMatrix expect = new RealMatrix(5, 4, new double[] { 11.0, 12.0, 13.0, 14.0, 21.0, 22.0, 23.0, 24.0, 31.0, 32.0, 33.0, 34.0, 41.0, 42.0, 43.0, 44.0, 51.0, 52.0, 53.0, 54.0 }); Assert.assertNotNull("test should not be null (" + "matrix as array" + EC.S_RBRAK, m2); Assert.assertNotNull("expected should not be null (" + "matrix as array" + EC.S_RBRAK, expect); Assert.assertNotNull("expected should have columns (" + "matrix as array" + EC.S_RBRAK, expect.getCols()); Assert.assertNotNull("expected should have rows (" + "matrix as array" + EC.S_RBRAK, expect.getRows()); Assert.assertNotNull("test should have columns (" + "matrix as array" + EC.S_RBRAK, m2 .getCols()); Assert.assertNotNull("test should have rows (" + "matrix as array" + EC.S_RBRAK, m2 .getRows()); Assert.assertEquals("rows should be equal (" + "matrix as array" + EC.S_RBRAK, m2 .getRows(), expect.getRows()); Assert.assertEquals("columns should be equal (" + "matrix as array" + EC.S_RBRAK, m2 .getCols(), expect.getCols()); DoubleTestBase.assertEquals("matrix as array", m2.getMatrixAsArray(), expect .getMatrixAsArray(), EC.EPS); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.replaceSubMatrixData(int, * int, RealMatrix)' */ @Test public void testReplaceSubMatrixData() { RealMatrix rm = new RealMatrix(2, 2, new double[] { 71., 72., 81., 82. }); m2.replaceSubMatrixData(1, 1, rm); // fails to insert data RealMatrixTest.assertEquals("matrix as array", 3, 4, new double[] { 71.0, 72.0, 13.0, 14.0, 81.0, 82.0, 23.0, 24.0, 31.0, 32.0, 33.0, 34.0, }, m2, EC.EPS); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.reorderColumnsBy(IntSet)' */ @Test public void testReorderColumnsBy() { RealMatrix mm = m2 .reorderColumnsBy(new IntSet(new int[] { 3, 1, 2, 0 })); // fails to insert data RealMatrixTest.assertEquals("matrix as array", 3, 4, new double[] { 14.0, 12.0, 13.0, 11.0, 24.0, 22.0, 23.0, 21.0, 34.0, 32.0, 33.0, 31.0 }, mm, EC.EPS); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.reorderRowsBy(IntSet)' */ @Test public void testReorderRowsBy() { RealMatrix mm = m2.reorderRowsBy(new IntSet(new int[] { 1, 2, 0 })); // fails to insert data RealMatrixTest.assertEquals("matrix as array", 3, 4, new double[] { 21.0, 22.0, 23.0, 24.0, 31.0, 32.0, 33.0, 34.0, 11.0, 12.0, 13.0, 14.0, }, mm, EC.EPS); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.extractSubMatrixData(int, * int, int, int)' */ @Test public void testExtractSubMatrixData() { RealMatrix mm = m2.extractSubMatrixData(1, 2, 2, 3); RealMatrixTest.assertEquals("sub matrix", 2, 2, new double[] { 23.0, 24.0, 33.0, 34.0 }, mm, EC.EPS); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.extractColumns(int, int)' */ @Test public void testExtractColumns() { Real2Array mm = m2.extractColumns(1, 3); RealArrayTest.assertEquals("extract columns", new double[] { 12.0, 22.0, 32.0 }, mm.getXArray(), EC.EPS); RealArrayTest.assertEquals("extract columns", new double[] { 14.0, 24.0, 34.0 }, mm.getYArray(), EC.EPS); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.extractRows(int, int)' */ @Test public void testExtractRows() { Real2Array mm = m2.extractRows(2, 0); RealArrayTest.assertEquals("extract rows", new double[] { 31.0, 32.0, 33.0, 34.0 }, mm.getXArray(), EC.EPS); RealArrayTest.assertEquals("extract rows", new double[] { 11.0, 12.0, 13.0, 14.0 }, mm.getYArray(), EC.EPS); } /** * Test method for 'org.xmlcml.euclid.RealMatrix.elementsInRange(RealRange)' */ @Test public void testElementsInRange() { IntMatrix im = m2.elementsInRange(new RealRange(13.1, 31.1)); String s = Int.testEquals((new int[] { 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0 }), im.getMatrixAsArray()); if (s != null) { Assert.fail("sub matrix" + "; " + s); } } /** * Test method for 'org.xmlcml.euclid.RealMatrix.writeXML(Writer)' */ @Test public void testWriteXML() { StringWriter w = new StringWriter(); try { m2.writeXML(w); w.close(); } catch (IOException e) { throw new EuclidRuntimeException("should never throw " + e); } Assert .assertEquals( "writeXML", "11.0 12.0 13.0 14.0 21.0 22.0 23.0 24.0 31.0 32.0 33.0 34.0", w.toString()); } @Test public void testShiftOrigin() { RealMatrix matrix = new RealMatrix(4, 5, new double[] { 2., 5., 40., 6., 1., 6., 25., 70., 12., 3., 18., 40., 100., 16., 4., 10., 24., 60., 10., 2., } ); RealMatrix matrix0 = new RealMatrix(matrix); LOG.debug(">>>"+matrix0); RealMatrix newMatrix = matrix0.createMatrixWithOriginShifted(0.1, 0.2); DoubleTestBase.assertEquals("shifted", new double[]{ 3.42,12.7,42.12,6.62,1.4, 10.36,32.8,69.68,11.84,3.2, 18.44,42.32,84.28,13.68,3.6, 11.4,27.6,55.0,9.2,2.0 }, newMatrix.getMatrixAsArray(), 0.1); LOG.debug(">>>>>"+newMatrix); } /** NOT CHECKED * */ @Test public void testScaleAndInterpolate() { RealMatrix matrix = new RealMatrix(4, 5, new double[] { 2., 5., 40., 6., 1., 6., 25., 70., 12., 3., 18., 40., 100., 16., 4., 10., 24., 60., 10., 2., } ); RealMatrix matrix0 = new RealMatrix(matrix); LOG.debug(">>>"+matrix0); RealMatrix newMatrix = matrix0.scaleAndInterpolate(5, 7); LOG.debug(">>>"+newMatrix); DoubleTestBase.assertEquals("shifted", new double[]{ 2.0,3.8,12.0,40.0,12.8,4.0,1.0, 5.0,14.0,28.5,62.5,20.9,7.3,2.5, 12.0,24.3,43.0,85.0,28.2,9.8,3.5, 16.0,28.0,46.8,90.0,29.6,10.1,3.5, 10.0,18.4,31.2,60.0,20.0,6.8,2.0, }, newMatrix.getMatrixAsArray(), 0.1); LOG.debug(">>>>>"+newMatrix); } } euclid-euclid-2.9/src/test/java/org/xmlcml/euclid/test/RealRangeArrayTest.java000077500000000000000000000172211461721410700274450ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid.test; import java.util.ArrayList; import java.util.List; import junit.framework.Assert; import org.junit.Test; import org.xmlcml.euclid.RealRange; import org.xmlcml.euclid.RealRangeArray; public class RealRangeArrayTest { private RealRange rr10_25 = new RealRange(10.1, 25.1); private RealRange rr10_20 = new RealRange(10.1, 20.1); private RealRange rr30_40 = new RealRange(30.1, 40.1); private RealRange rr15_25 = new RealRange(15.1, 25.1); private RealRange rr15_17 = new RealRange(15.1, 17.1); private RealRange rr50_60 = new RealRange(50.1, 60.1); @Test public void testAdd() { RealRangeArray array = new RealRangeArray(); Assert.assertEquals(0, array.size()); } @Test public void testAdd1() { RealRangeArray array = new RealRangeArray(); array.add(rr10_20); Assert.assertEquals(1, array.size()); } @Test public void testAdd2() { RealRangeArray array = new RealRangeArray(); array.add(rr10_20); array.add(rr15_25); Assert.assertEquals(2, array.size()); } @Test public void testEquals() { RealRangeArray array = new RealRangeArray(); array.add(rr10_20); array.add(rr15_25); RealRangeArray array2 = new RealRangeArray(); array2.add(rr10_20); array2.add(rr15_25); Assert.assertTrue(array.equals(array2)); } @Test /** * order of addition matters */ public void testNotEquals() { RealRangeArray array = new RealRangeArray(); array.add(rr10_20); array.add(rr15_25); RealRangeArray array2 = new RealRangeArray(); array2.add(rr15_25); array2.add(rr10_20); Assert.assertFalse("order matters", array.equals(array2)); } @Test /** * order of addition matters */ public void testSort() { RealRangeArray array = new RealRangeArray(); array.add(rr10_20); array.add(rr15_17); array.add(rr15_25); array.add(rr30_40); array.add(rr50_60); RealRangeArray array2 = new RealRangeArray(); array2.add(rr50_60); array2.add(rr15_25); array2.add(rr30_40); array2.add(rr15_17); array2.add(rr10_20); Assert.assertFalse("order matters", array.equals(array2)); array2.sort(); Assert.assertTrue("after sorting", array.equals(array2)); } @Test /** * sort and overlap */ public void testSortAndRemoveOverlapping() { RealRangeArray array = new RealRangeArray(); array.add(rr10_25); array.add(rr30_40); array.add(rr50_60); RealRangeArray array2 = new RealRangeArray(); array2.add(rr50_60); array2.add(rr15_25); array2.add(rr30_40); array2.add(rr15_17); array2.add(rr10_20); Assert.assertFalse("order matters", array.equals(array2)); array2.sortAndRemoveOverlapping(); Assert.assertTrue("after sorting", array.equals(array2)); } @Test /** */ public void testPlus() { RealRangeArray array = new RealRangeArray(); array.add(rr10_20); array.add(rr30_40); array.add(rr50_60); RealRangeArray array2 = new RealRangeArray(); array2.add(rr15_25); array2.add(rr15_17); RealRangeArray plus = array2.plus(array); RealRangeArray ref = new RealRangeArray(); ref.add(rr10_25); ref.add(rr30_40); ref.add(rr50_60); Assert.assertTrue("after sorting", ref.equals(plus)); } @Test /** */ public void testInverse() { RealRangeArray array = new RealRangeArray(); array.add(rr10_20); array.add(rr30_40); array.add(rr50_60); RealRangeArray inverse = array.inverse(); RealRangeArray ref = new RealRangeArray(); ref.add(new RealRange(20.1, 30.1)); ref.add(new RealRange(40.1, 50.1)); Assert.assertTrue("after sorting", ref.equals(inverse)); } @Test /** */ public void testInverse0() { RealRangeArray array = new RealRangeArray(); RealRangeArray inverse = array.inverse(); Assert.assertNull(inverse); } @Test /** */ public void testInverse1() { RealRangeArray array = new RealRangeArray(); array.add(rr10_20); RealRangeArray inverse = array.inverse(); Assert.assertEquals(0, inverse.size()); } @Test /** */ public void testInverse1a() { RealRangeArray array = new RealRangeArray(); array.add(rr10_20); array.add(rr15_25); RealRangeArray inverse = array.inverse(); Assert.assertEquals("inverse without caps", 0, inverse.size()); } @Test /** */ public void testInverseWithCaps() { RealRangeArray array = new RealRangeArray(); array.add(new RealRange(0.1,0.1)); array.add(new RealRange(100.1,100.1)); RealRangeArray inverse = array.inverse(); Assert.assertEquals(1, inverse.size()); RealRangeArray ref = new RealRangeArray(); ref.add(new RealRange(0.1,100.1)); Assert.assertTrue("inverse", ref.equals(inverse)); } @Test /** */ public void testExtendRanges100() { List rangeList = createRealRangeArray(); RealRangeArray mask = new RealRangeArray(rangeList); Assert.assertEquals("raw mask", "Direction: null; size: 14\n"+ "((124.838,132.808)(136.802,144.772)(148.765,156.735)(160.672,168.642)(172.636,180.606)\n"+ "(184.599,192.569)(196.562,204.532)(208.525,216.495)(220.489,228.459)(232.452,240.422)\n"+ "(244.415,252.385)(256.323,264.293)(268.286,276.256)(280.249,288.219))", mask.toString()); mask.extendRangesBy(100.); mask.format(3); Assert.assertEquals("raw mask", "Direction: null; size: 14\n"+ "((24.838,134.805)(134.805,146.769)(146.769,158.704)(158.704,170.639)(170.639,182.603)\n"+ "(182.603,194.566)(194.566,206.529)(206.529,218.492)(218.492,230.456)(230.456,242.419)\n"+ "(242.419,254.354)(254.354,266.29)(266.29,278.253)(278.253,388.219))", mask.toString()); } @Test /** */ public void testExtendRanges1() { List rangeList = createRealRangeArray(); RealRangeArray mask = new RealRangeArray(rangeList); Assert.assertEquals("raw mask", "Direction: null; size: 14\n"+ "((124.838,132.808)(136.802,144.772)(148.765,156.735)(160.672,168.642)(172.636,180.606)\n"+ "(184.599,192.569)(196.562,204.532)(208.525,216.495)(220.489,228.459)(232.452,240.422)\n"+ "(244.415,252.385)(256.323,264.293)(268.286,276.256)(280.249,288.219))", mask.toString()); mask.extendRangesBy(1.); mask.format(3); Assert.assertEquals("raw mask", "Direction: null; size: 14\n"+ "((123.838,133.808)(135.802,145.772)(147.765,157.735)(159.672,169.642)(171.636,181.606)\n"+ "(183.599,193.569)(195.562,205.532)(207.525,217.495)(219.489,229.459)(231.452,241.422)\n"+ "(243.415,253.385)(255.323,265.293)(267.286,277.256)(279.249,289.219))", mask.toString()); } private static List createRealRangeArray() { List rangeList = new ArrayList(); rangeList.add(new RealRange(124.838,132.808)); rangeList.add(new RealRange(136.802,144.772)); rangeList.add(new RealRange(148.765,156.735)); rangeList.add(new RealRange(160.672,168.642)); rangeList.add(new RealRange(172.636,180.606)); rangeList.add(new RealRange(184.599,192.569)); rangeList.add(new RealRange(196.562,204.532)); rangeList.add(new RealRange(208.525,216.495)); rangeList.add(new RealRange(220.489,228.459)); rangeList.add(new RealRange(232.452,240.422)); rangeList.add(new RealRange(244.415,252.385)); rangeList.add(new RealRange(256.323,264.293)); rangeList.add(new RealRange(268.286,276.256)); rangeList.add(new RealRange(280.249,288.219)); return rangeList; } } euclid-euclid-2.9/src/test/java/org/xmlcml/euclid/test/RealRangeComparatorTest.java000077500000000000000000000064401461721410700304770ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid.test; import java.util.Set; import java.util.TreeSet; import org.junit.Assert; import org.junit.Test; import org.xmlcml.euclid.RealComparator; import org.xmlcml.euclid.RealRange; import org.xmlcml.euclid.RealRangeComparator; public class RealRangeComparatorTest { public final static Double ZERO = 0.0; public final static Double EPS = 0.01; public final static Double ONE = 1.0; public final static Double TWO = 2.0; public final static Double THREE = 3.0; public final static RealRange ONE_TWO = new RealRange(ONE, TWO); public final static RealRange ZERO_ONE = new RealRange(ZERO, ONE); public final static Double SQR3 = Math.sqrt(THREE); public final static RealComparator RZERO = new RealComparator(0.0); public final static RealComparator REPS = new RealComparator(EPS); @Test public void comparatorTest() { RealRangeComparator comparator = new RealRangeComparator(RZERO); Assert.assertEquals(0, comparator.compare(ONE_TWO, ONE_TWO)); Assert.assertEquals(1, comparator.compare(ONE_TWO, ZERO_ONE)); Assert.assertEquals(-1, comparator.compare(ZERO_ONE, ONE_TWO)); Assert.assertEquals(-1, comparator.compare(ZERO_ONE, null)); } @Test public void comparatorTest1() { RealRangeComparator comparator = new RealRangeComparator(REPS); Assert.assertEquals(0, comparator.compare(ONE_TWO, new RealRange(ONE-EPS/2., TWO+EPS/2.))); // both min and max in first range are larger Assert.assertEquals(1, comparator.compare(ONE_TWO, new RealRange(ONE-EPS*2., TWO-EPS*2))); // both min and max in first range are smaller Assert.assertEquals(-1, comparator.compare(ONE_TWO, new RealRange(ONE+EPS*2., TWO+EPS*2))); // this gives -1 because there is a disagreement Assert.assertEquals(-1, comparator.compare(ONE_TWO, new RealRange(ONE+EPS*2., TWO-EPS*2))); // this gives -1 because there is a disagreement Assert.assertEquals(-1, comparator.compare(ONE_TWO, new RealRange(ONE-EPS*2., TWO+EPS*2))); } /** TreeSet works * */ @Test public void testTreeSet(){ RealRangeComparator comparator = new RealRangeComparator(new RealComparator(0.0)); Set set = new TreeSet(comparator); set.add(ONE_TWO); set.add(ZERO_ONE); set.add(ONE_TWO); set.add(new RealRange(ONE, TWO-0.01)); Assert.assertEquals(3, set.size()); } /** TreeSet works * */ @Test public void testTreeSet1(){ RealRangeComparator comparator = new RealRangeComparator(new RealComparator(0.01)); Set set = new TreeSet(comparator); set.add(ONE_TWO); set.add(ZERO_ONE); set.add(ONE_TWO); set.add(new RealRange(ONE, TWO-0.005)); set.add(new RealRange(ONE+0.005, TWO-0.005)); Assert.assertEquals(2, set.size()); } } euclid-euclid-2.9/src/test/java/org/xmlcml/euclid/test/RealRangeListTest.java000066400000000000000000000152531461721410700273020ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid.test; import org.apache.log4j.Logger; import org.junit.Assert; import org.junit.Test; import org.xmlcml.euclid.RealRange; import org.xmlcml.euclid.RealRangeList; /** tests RealRangeList. * * @author pm286 * */ public class RealRangeListTest { private final static Logger LOG = Logger.getLogger(RealRangeListTest.class); @Test public void testAddInitialRange() { RealRangeList realRangeList = new RealRangeList(); Assert.assertEquals("initial size", 0, realRangeList.size()); RealRange range = new RealRange(3., 5.); int result = realRangeList.addRange(range); Assert.assertEquals("initial", 0, result); Assert.assertEquals("new size", 1, realRangeList.size()); } @Test public void testAddHighest() { RealRangeList realRangeList = new RealRangeList(); RealRange range = new RealRange(3., 5.); int result = realRangeList.addRange(range); Assert.assertTrue("range", range.isEqualTo(realRangeList.get(0), 0.01)); range = new RealRange(7., 10.); result = realRangeList.addRange(range); Assert.assertEquals("highest", 1, result); Assert.assertEquals("new size", 2, realRangeList.size()); Assert.assertTrue("range", range.isEqualTo(realRangeList.get(1), 0.01)); } @Test public void testAddLowest() { RealRangeList realRangeList = new RealRangeList(); RealRange range = new RealRange(13., 15.); int result = realRangeList.addRange(range); Assert.assertTrue("range", range.isEqualTo(realRangeList.get(0), 0.01)); range = new RealRange(7., 10.); result = realRangeList.addRange(range); Assert.assertEquals("lowest", 0, result); Assert.assertEquals("new size", 2, realRangeList.size()); Assert.assertTrue("range0", range.isEqualTo(realRangeList.get(0), 0.01)); Assert.assertTrue("range1", new RealRange(13., 15.).isEqualTo(realRangeList.get(1), 0.01)); } @Test public void testToString() { RealRangeList realRangeList = new RealRangeList(); realRangeList.addRange(new RealRange(13., 15.)); realRangeList.addRange(new RealRange(7., 10.)); Assert.assertEquals("string", "[(7.0,10.0), (13.0,15.0)]", realRangeList.toString()); } @Test public void testAddMiddleNoOverlap() { RealRangeList realRangeList = new RealRangeList(); realRangeList.addRange(new RealRange(3., 5.)); realRangeList.addRange(new RealRange(8., 10.)); realRangeList.addRange(new RealRange(13., 15.)); realRangeList.addRange(new RealRange(18., 20.)); Assert.assertEquals("string", "[(3.0,5.0), (8.0,10.0), (13.0,15.0), (18.0,20.0)]", realRangeList.toString()); int result = realRangeList.addRange(new RealRange(6., 7.)); Assert.assertEquals("insert", 1, result); Assert.assertEquals("string", "[(3.0,5.0), (6.0,7.0), (8.0,10.0), (13.0,15.0), (18.0,20.0)]", realRangeList.toString()); result = realRangeList.addRange(new RealRange(11., 12.)); Assert.assertEquals("insert", 3, result); Assert.assertEquals("string", "[(3.0,5.0), (6.0,7.0), (8.0,10.0), (11.0,12.0), (13.0,15.0), (18.0,20.0)]", realRangeList.toString()); } @Test public void testAddRandomOrderNoOverlap() { RealRangeList realRangeList = new RealRangeList(); realRangeList.addRange(new RealRange(13., 15.)); realRangeList.addRange(new RealRange(3., 5.)); realRangeList.addRange(new RealRange(18., 20.)); realRangeList.addRange(new RealRange(8., 10.)); Assert.assertEquals("string", "[(3.0,5.0), (8.0,10.0), (13.0,15.0), (18.0,20.0)]", realRangeList.toString()); } @Test public void testAddOverlapAbove() { RealRangeList realRangeList = new RealRangeList(); realRangeList.addRange(new RealRange(3., 5.)); RealRange range = new RealRange(4., 6.); realRangeList.addRange(range); Assert.assertEquals("string", "[(3.0,6.0)]", realRangeList.toString()); } @Test public void testAddOverlapBelow() { RealRangeList realRangeList = new RealRangeList(); realRangeList.addRange(new RealRange(4., 6.)); RealRange range = new RealRange(3., 5.); realRangeList.addRange(range); Assert.assertEquals("string", "[(3.0,6.0)]", realRangeList.toString()); } @Test public void testAddOverlapMiddle() { RealRangeList realRangeList = new RealRangeList(); realRangeList.addRange(new RealRange(1., 2.)); realRangeList.addRange(new RealRange(4., 6.)); RealRange range = new RealRange(3., 5.); realRangeList.addRange(range); Assert.assertEquals("string", "[(1.0,2.0), (3.0,6.0)]", realRangeList.toString()); } @Test public void testAddOverlapMiddleDown() { RealRangeList realRangeList = new RealRangeList(); realRangeList.addRange(new RealRange(0., 2.)); realRangeList.addRange(new RealRange(4., 6.)); RealRange range = new RealRange(1., 3.); realRangeList.addRange(range); Assert.assertEquals("string", "[(0.0,3.0), (4.0,6.0)]", realRangeList.toString()); } @Test public void testAddMultipleOverlap() { RealRangeList realRangeList = new RealRangeList(); realRangeList.addRange(new RealRange(0., 2.)); realRangeList.addRange(new RealRange(4., 5.)); realRangeList.addRange(new RealRange(6., 7.)); realRangeList.addRange(new RealRange(8., 9.)); // RealRange range = new RealRange(3.0, 6.5); realRangeList.addRange(range); // [(0.0,2.0), (3.0,7.0), (6.0,7.0), (8.0,9.0)] Assert.assertEquals("string", "[(0.0,2.0), (3.0,7.0), (8.0,9.0)]", realRangeList.toString()); } @Test public void testAddMultipleOverlap1() { RealRangeList realRangeList = new RealRangeList(); realRangeList.addRange(new RealRange(0., 2.)); realRangeList.addRange(new RealRange(4., 5.)); realRangeList.addRange(new RealRange(6., 7.)); realRangeList.addRange(new RealRange(8., 9.)); realRangeList.addRange(new RealRange(10., 11.)); // RealRange range = new RealRange(3.0, 8.5); realRangeList.addRange(range); //[(0.0,2.0), (3.0,9.0), (6.0,7.0), (8.0,9.0), (10.0,11.0)] Assert.assertEquals("string", "[(0.0,2.0), (3.0,9.0), (10.0,11.0)]", realRangeList.toString()); } @Test public void testAddLowerEnd() { RealRangeList realRangeList = new RealRangeList(); realRangeList.addRange(new RealRange(68.0,101.0)); RealRange range = new RealRange(68.0,78.0); realRangeList.addRange(range); Assert.assertEquals("string", "[(68.0,101.0)]", realRangeList.toString()); } } euclid-euclid-2.9/src/test/java/org/xmlcml/euclid/test/RealRangeTest.java000066400000000000000000000151611461721410700264440ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid.test; import static org.xmlcml.euclid.EC.EPS; import org.apache.log4j.Logger; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.xmlcml.euclid.IntRange; import org.xmlcml.euclid.RealRange; /** * tests RealRange. * * @author pmr * */ public class RealRangeTest { @SuppressWarnings("unused") private static Logger LOG = Logger.getLogger(RealRangeTest.class); RealRange r0; RealRange r1; RealRange r2; /** * tests equality of ranges. * * @param msg * message * @param ref * @param r * @param epsilon */ public static void assertEquals(String msg, RealRange ref, RealRange r, double epsilon) { Assert.assertEquals(msg + " min", r.getMin(), ref.getMin(), epsilon); Assert.assertEquals(msg + " max", r.getMax(), ref.getMax(), epsilon); } /** * setup. * * @throws Exception */ @Before public void setUp() throws Exception { r0 = new RealRange(); r1 = new RealRange(1.0, 1.0); r2 = new RealRange(1.0, 3.0); } /** * Test method for 'org.xmlcml.euclid.RealRange.RealRange()' */ @Test public void testRealRange() { Assert.assertEquals("empty", "NULL", r0.toString()); } /** * Test method for 'org.xmlcml.euclid.RealRange.RealRange(double, double)' */ @Test public void testRealRangeRealReal() { Assert.assertEquals("i1", "(1.0,1.0)", r1.toString()); Assert.assertEquals("i2", "(1.0,3.0)", r2.toString()); } /** * Test method for 'org.xmlcml.euclid.RealRange.RealRange(RealRange)' */ @Test public void testRealRangeRealRange() { RealRange ii = new RealRange(r2); Assert.assertEquals("ii", "(1.0,3.0)", ii.toString()); } /** * Test method for 'org.xmlcml.euclid.RealRange.isValid()' */ @Test public void testIsValid() { Assert.assertTrue("valid", r2.isValid()); Assert.assertFalse("invalid", r0.isValid()); } /** * Test method for 'org.xmlcml.euclid.RealRange.isEqualTo(RealRange)' */ @Test public void testIsEqualTo() { Assert.assertTrue("equal", r2.isEqualTo(r2, 0.001)); Assert.assertFalse("equal", r2.isEqualTo(r0, 0.001)); Assert.assertFalse("equal", r0.isEqualTo(r0, 0.001)); } /** * Test method for 'org.xmlcml.euclid.RealRange.plus(RealRange)' */ @Test public void testPlus() { RealRange ix = new RealRange(1.0, 4.0); RealRange iy = new RealRange(2.0, 3.0); RealRange ii = ix.plus(iy); Assert.assertEquals("ii", "(1.0,4.0)", ii.toString()); iy = new RealRange(0, 2); ii = ix.plus(iy); Assert.assertEquals("ii", "(0.0,4.0)", ii.toString()); iy = new RealRange(2.0, 6.0); ii = ix.plus(iy); Assert.assertEquals("ii", "(1.0,6.0)", ii.toString()); iy = new RealRange(); ii = ix.plus(iy); Assert.assertEquals("ii", "(1.0,4.0)", ii.toString()); // RealRange r1 = new RealRange(-1,2); RealRange r2 = new RealRange(-3,-4); // invalid RealRange r = r1.plus(r2); Assert.assertTrue("r1+r2", r.isEqualTo(new RealRange(-1, 2), 0.0001)); r1 = new RealRange(-1,2); r2 = new RealRange(-4,-3); // invalid r = r1.plus(r2); Assert.assertTrue("r1+r2", r.isEqualTo(new RealRange(-4, 2), 0.0001)); r1 = new RealRange(-1,2); r2 = new RealRange(-4,3); // invalid r = r1.plus(r2); Assert.assertTrue("r1+r2", r.isEqualTo(new RealRange(-4, 3), 0.0001)); } /** * Test method for * 'org.xmlcml.euclid.RealRange.doubleersectionWith(RealRange)' */ @Test public void testIntsectionWith() { RealRange ix = new RealRange(1.0, 4.0); RealRange iy = new RealRange(2.0, 3.0); RealRange ii = ix.intersectionWith(iy); Assert.assertEquals("ii", "(2.0,3.0)", ii.toString()); iy = new RealRange(0.0, 2.0); ii = ix.intersectionWith(iy); Assert.assertEquals("ii", "(1.0,2.0)", ii.toString()); iy = new RealRange(2.0, 6.0); ii = ix.intersectionWith(iy); Assert.assertEquals("ii", "(2.0,4.0)", ii.toString()); iy = new RealRange(); ii = ix.intersectionWith(iy); Assert.assertNull("ii", ii); } /** * Test method for 'org.xmlcml.euclid.RealRange.getMin()' */ @Test public void testGetMin() { Assert.assertEquals("min", 1.0, r2.getMin(), EPS); } /** * Test method for 'org.xmlcml.euclid.RealRange.getMax()' */ @Test public void testGetMax() { Assert.assertEquals("max", 3.0, r2.getMax(), EPS); } /** * Test method for 'org.xmlcml.euclid.RealRange.getRange()' */ @Test public void testGetRange() { Assert.assertEquals("range", 2.0, r2.getRange(), EPS); } /** * Test method for 'org.xmlcml.euclid.RealRange.includes(RealRange)' */ @Test public void testIncludesRealRange() { Assert.assertTrue("includes", r2.includes(new RealRange(2.0, 3.0))); Assert.assertFalse("includes", r2.includes(new RealRange(0.0, 3.0))); } /** * Test method for 'org.xmlcml.euclid.RealRange.includes(double)' */ @Test public void testIncludesReal() { Assert.assertTrue("includes", r2.includes(1.0)); Assert.assertFalse("includes", r2.includes(0.0)); } /** * Test method for 'org.xmlcml.euclid.RealRange.contains(double)' */ @Test public void testContains() { Assert.assertTrue("contains", r2.contains(1.0)); Assert.assertFalse("contains", r2.contains(0.0)); } /** * Test method for 'org.xmlcml.euclid.RealRange.add(double)' */ @Test public void testAdd() { r2.add(2); Assert.assertEquals("ii", "(1.0,3.0)", r2.toString()); r2.add(0); Assert.assertEquals("ii", "(0.0,3.0)", r2.toString()); r2.add(9); Assert.assertEquals("ii", "(0.0,9.0)", r2.toString()); } /** * test getting a random variate. tests limits only */ @Test public void testGetRandomVariate() { RealRange range = new RealRange(10, 20); double sum = 0.0; for (int i = 0; i < 100; i++) { double d = range.getRandomVariate(); Assert.assertTrue("limit: ", d >= 10. && d <= 20.); sum += d; } // LOG.debug(sum); // crude check Assert.assertTrue("distribution", sum > 1400 && sum < 1600); } /** * Test method for 'org.xmlcml.euclid.RealRange.RealRange(IntRange)' */ @Test public void testRealRangeIntRange() { RealRange r = new RealRange(new IntRange(1, 2)); Assert.assertEquals("int", "(1.0,2.0)", r.toString()); } } euclid-euclid-2.9/src/test/java/org/xmlcml/euclid/test/RealSquareMatrixTest.java000066400000000000000000000425461461721410700300440ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid.test; import static org.xmlcml.euclid.EC.EPS; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.xmlcml.euclid.EC; import org.xmlcml.euclid.EuclidTestUtils; import org.xmlcml.euclid.RealArray; import org.xmlcml.euclid.RealMatrix; import org.xmlcml.euclid.RealSquareMatrix; /** * tests RealSquareMatrix. * * @author pmr * */ public class RealSquareMatrixTest extends MatrixTest { private final static Logger LOG = Logger.getLogger(RealSquareMatrixTest.class); RealSquareMatrix m0; RealSquareMatrix m1; RealSquareMatrix m2; /** * setup. * * @throws Exception */ @Before public void setUp() throws Exception { super.setUp(); LOG.setLevel(Level.WARN); m0 = new RealSquareMatrix(); m1 = new RealSquareMatrix(3); m2 = new RealSquareMatrix(3, new double[] { 11., 12., 13., 21., 22., 23., 31., 32., 33., }); } /** * equality test. true if both args not null and equal within epsilon and * rows are present and equals and columns are present and equals * * @param msg * message * @param test * @param expected * @param epsilon */ public static void assertEquals(String msg, RealSquareMatrix test, RealSquareMatrix expected, double epsilon) { Assert.assertNotNull("test should not be null (" + msg + EC.S_RBRAK, test); Assert.assertNotNull("expected should not be null (" + msg + EC.S_RBRAK, expected); Assert.assertNotNull("expected should have columns (" + msg + EC.S_RBRAK, expected.getCols()); Assert.assertNotNull("expected should have rows (" + msg + EC.S_RBRAK, expected.getRows()); Assert.assertNotNull("test should have columns (" + msg + EC.S_RBRAK, test .getCols()); Assert.assertNotNull("test should have rows (" + msg + EC.S_RBRAK, test .getRows()); Assert.assertEquals("rows should be equal (" + msg + EC.S_RBRAK, test .getRows(), expected.getRows()); Assert.assertEquals("columns should be equal (" + msg + EC.S_RBRAK, test .getCols(), expected.getCols()); DoubleTestBase.assertEquals(msg, test.getMatrixAsArray(), expected .getMatrixAsArray(), epsilon); } /** * equality test. true if both args not null and equal within epsilon * * @param msg * message * @param rows * @param test * @param expected * @param epsilon */ public static void assertEquals(String msg, int rows, double[] test, RealSquareMatrix expected, double epsilon) { Assert.assertNotNull("test should not be null (" + msg + EC.S_RBRAK, test); Assert.assertNotNull("ref should not be null (" + msg + EC.S_RBRAK, expected); Assert.assertEquals("rows should be equal (" + msg + EC.S_RBRAK, rows, expected.getRows()); DoubleTestBase.assertEquals(msg, test, expected.getMatrixAsArray(), epsilon); } /** * Test method for 'org.xmlcml.euclid.RealSquareMatrix.isOrthogonal()' */ @Test public void testIsOrthogonal() { Assert.assertFalse("isOrthogonal", m2.isOrthogonal()); RealSquareMatrix m = new RealSquareMatrix(3, new double[] { 0, 1, 0, -1, 0, 0, 0, 0, 1, }); Assert.assertTrue("isOrthogonal", m.isOrthogonal()); } /** * Test method for 'org.xmlcml.euclid.RealSquareMatrix.RealSquareMatrix()' */ @Test public void testRealSquareMatrix() { Assert.assertEquals("real square matrix", 0, m0.getRows()); Assert.assertEquals("real square matrix", 0, m0.getCols()); } /** * Test method for * 'org.xmlcml.euclid.RealSquareMatrix.RealSquareMatrix(int)' */ @Test public void testRealSquareMatrixInt() { Assert.assertEquals("real square matrix", 3, m1.getRows()); Assert.assertEquals("real square matrix", 3, m1.getCols()); } /** * Test method for * 'org.xmlcml.euclid.RealSquareMatrix.outerProduct(RealArray)' */ @Test public void testOuterProduct() { RealArray ra = new RealArray(3, new double[] { 1, 2, 3 }); RealSquareMatrix rsm = RealSquareMatrix.outerProduct(ra); RealMatrix rm = null; rm = new RealMatrix(3, 3, new double[] { 1.0, 2.0, 3.0, 2.0, 4.0, 6.0, 3.0, 6.0, 9.0, }); MatrixTest.assertEquals("outer product", rm, (RealMatrix) rsm, EPS); } /** * Test method for 'org.xmlcml.euclid.RealSquareMatrix.diagonal(RealArray)' */ @Test public void testDiagonal() { RealArray ra = new RealArray(3, new double[] { 1, 2, 3 }); RealMatrix rsm = RealSquareMatrix.diagonal(ra); RealMatrix rm = null; rm = new RealMatrix(3, 3, new double[] { 1, 0, 0, 0, 2, 0, 0, 0, 3, }); MatrixTest.assertEquals("diagonal", rm, (RealMatrix) rsm, EPS); } /** * Test method for 'org.xmlcml.euclid.RealSquareMatrix.RealSquareMatrix(int, * double[])' */ @Test public void testRealSquareMatrixIntDoubleArray() { RealMatrix rm = new RealMatrix(3, 3, new double[] { 1.0, 2.0, 3.0, 2.0, 4.0, 6.0, 3.0, 6.0, 9.0, }); RealSquareMatrix rsm = new RealSquareMatrix(3, new double[] { 1.0, 2.0, 3.0, 2.0, 4.0, 6.0, 3.0, 6.0, 9.0, }); MatrixTest.assertEquals("int double[]", rm, (RealMatrix) rsm, EPS); } /** * Test method for 'org.xmlcml.euclid.RealSquareMatrix.RealSquareMatrix(int, * double)' */ @Test public void testRealSquareMatrixIntDouble() { RealMatrix rm = new RealMatrix(3, 3, 10.); RealSquareMatrix rsm = new RealSquareMatrix(3, 10.); MatrixTest.assertEquals("int double", rm, (RealMatrix) rsm, EPS); } /** * Test method for * 'org.xmlcml.euclid.RealSquareMatrix.RealSquareMatrix(RealMatrix, int, * int, int)' */ @Test public void testRealSquareMatrixRealMatrixIntIntInt() { RealMatrix rm = new RealMatrix(3, 4, new double[] { 11., 12., 13., 14., 21., 22., 23., 24., 31., 32., 33., 34. }); RealSquareMatrix rsm = new RealSquareMatrix(rm, 1, 1, 2); RealMatrix rm1 = new RealMatrix(2, 2, new double[] { 22., 23., 32., 33., }); MatrixTest.assertEquals("rsm int int int", rm1, (RealMatrix) rsm, EPS); } /** * Test method for * 'org.xmlcml.euclid.RealSquareMatrix.RealSquareMatrix(RealSquareMatrix)' */ @Test public void testRealSquareMatrixRealSquareMatrix() { RealSquareMatrix rsm = new RealSquareMatrix(m2); MatrixTest.assertEquals("copy", m2, rsm, EPS); } /** * Test method for * 'org.xmlcml.euclid.RealSquareMatrix.RealSquareMatrix(RealMatrix)' */ @Test public void testRealSquareMatrixRealMatrix() { RealMatrix rm = new RealMatrix(2, 2, new double[] { 22., 23., 32., 33., }); RealSquareMatrix rsm = new RealSquareMatrix(rm); MatrixTest.assertEquals("real matrix", rm, rsm, EPS); } /** * Test method for * 'org.xmlcml.euclid.RealSquareMatrix.isEqualTo(RealSquareMatrix)' */ @Test public void testIsEqualToRealSquareMatrix() { RealSquareMatrix rsm = new RealSquareMatrix(m2); Assert.assertTrue("isEqualTo", m2.isEqualTo(rsm)); } /** * Test method for * 'org.xmlcml.euclid.RealSquareMatrix.RealSquareMatrix(double[][])' */ @Test public void testRealSquareMatrixDoubleArrayArray() { double[][] mat = new double[][] { new double[] { 11., 12., 13. }, new double[] { 21., 22., 23. }, new double[] { 31., 32., 33. }, }; RealSquareMatrix rsm = new RealSquareMatrix(mat); RealMatrix rm = new RealMatrix(3, 3, new double[] { 11., 12., 13., 21., 22., 23., 31., 32., 33., }); MatrixTest.assertEquals("real matrix", rm, rsm, EPS); } /** * Test method for * 'org.xmlcml.euclid.RealSquareMatrix.plus(RealSquareMatrix)' */ @Test public void testPlusRealSquareMatrix() { RealSquareMatrix rsm = m2.plus(m2); RealMatrix rm = new RealMatrix(3, 3, new double[] { 22., 24., 26., 42., 44., 46., 62., 64., 66., }); MatrixTest.assertEquals("real matrix", rm, rsm, EPS); } /** * Test method for * 'org.xmlcml.euclid.RealSquareMatrix.subtract(RealSquareMatrix)' */ @Test public void testSubtractRealSquareMatrix() { RealSquareMatrix rsm = m2.plus(m2); RealSquareMatrix rsm1 = m2.subtract(rsm); RealMatrix rm = new RealMatrix(3, 3, new double[] { -11., -12., -13., -21., -22., -23., -31., -32., -33., }); MatrixTest.assertEquals("real matrix", rm, rsm1, EPS); } /** * Test method for * 'org.xmlcml.euclid.RealSquareMatrix.multiply(RealSquareMatrix)' */ @Test public void testMultiplyRealSquareMatrix() { RealSquareMatrix rsm = m2.multiply(m2); RealMatrix rm = new RealMatrix(3, 3, new double[] { 776.0, 812.0, 848.0, 1406.0, 1472.0, 1538.0, 2036.0, 2132.0, 2228.0, }); MatrixTest.assertEquals("real matrix", rm, rsm, EPS); } /** * Test method for 'org.xmlcml.euclid.RealSquareMatrix.determinant()' */ @Test public void testDeterminant() { RealSquareMatrix m = new RealSquareMatrix(3, new double[] { 1., 1., 1., 2., 3., 4., 3., 4., 7. }); double d = m.determinant(); Assert.assertEquals("determinant", 2., d, EPS); } /** * Test method for 'org.xmlcml.euclid.RealSquareMatrix.trace()' */ @Test public void testTrace() { RealSquareMatrix m = new RealSquareMatrix(3, new double[] { 1., 1., 1., 2., 3., 4., 3., 4., 7. }); double d = m.trace(); Assert.assertEquals("trace", 11., d, EPS); } /** * Test method for 'org.xmlcml.euclid.RealSquareMatrix.isUnit()' */ @Test public void testIsUnit() { RealSquareMatrix m = new RealSquareMatrix(3, new double[] { 1., 0., 0., 0., 1., 0., 0., 0., 1. }); Assert.assertTrue("unit", m.isUnit()); m = new RealSquareMatrix(3, new double[] { 1., 1., 1., 2., 3., 4., 3., 4., 7. }); Assert.assertFalse("unit", m.isUnit()); } /** * Test method for 'org.xmlcml.euclid.RealSquareMatrix.isSymmetric()' */ @Test public void testIsSymmetric() { RealSquareMatrix m = new RealSquareMatrix(3, new double[] { 1., 0., 3., 0., 1., 0., 3., 0., 1. }); Assert.assertTrue("unit", m.isSymmetric()); m = new RealSquareMatrix(3, new double[] { 1., 1., 1., 2., 3., 4., 3., 4., 7. }); Assert.assertFalse("unit", m.isSymmetric()); } /** * Test method for 'org.xmlcml.euclid.RealSquareMatrix.orthonormalize()' */ @Test public void testOrthonormalize() { RealSquareMatrix m = new RealSquareMatrix(3, new double[] { 1., 1., 1., 2., 3., 4., 3., 4., 7. }); Assert.assertFalse("orthonormal", m.isOrthonormal()); m.orthonormalize(); Assert.assertTrue("orthonormal", m.isOrthonormal()); RealSquareMatrix mm = new RealSquareMatrix(3, new double[] { 0.5773502691896258, 0.5773502691896258, 0.5773502691896258, -0.7071067811865477, 0.0, 0.7071067811865476, 0.40824829046386296, -0.816496580927726, 0.40824829046386313, }); MatrixTest.assertEquals("orthonormal", mm, m, EPS); } /** * Test method for 'org.xmlcml.euclid.RealSquareMatrix.isUpperTriangular()' */ @Test public void testIsUpperTriangular() { RealSquareMatrix m = new RealSquareMatrix(3, new double[] { 0., 2., 3., 0., 0., 2., 0., 0., 0. }); Assert.assertTrue("upper triangular", m.isUpperTriangular()); m = new RealSquareMatrix(3, new double[] { 1., 2., 3., 0., 1., 2., 0., 0., 1. }); Assert.assertTrue("upper triangular", m.isUpperTriangular()); m = new RealSquareMatrix(3, new double[] { 1., 1., 1., 2., 3., 4., 3., 4., 7. }); Assert.assertFalse("upper triangular false", m.isUpperTriangular()); } /** * Test method for 'org.xmlcml.euclid.RealSquareMatrix.isLowerTriangular()' */ @Test public void testIsLowerTriangular() { RealSquareMatrix m = new RealSquareMatrix(3, new double[] { 0., 0., 0., 2., 0., 0., 3., 2., 0. }); Assert.assertTrue("lower triangular", m.isLowerTriangular()); m = new RealSquareMatrix(3, new double[] { 1., 0., 0., 2., 1., 0., 3., 2., 1. }); Assert.assertTrue("lower triangular", m.isLowerTriangular()); m = new RealSquareMatrix(3, new double[] { 1., 1., 1., 2., 3., 4., 3., 4., 7. }); Assert.assertFalse("lower triangular false", m.isLowerTriangular()); } /** * Test method for 'org.xmlcml.euclid.RealSquareMatrix.isImproperRotation()' */ @Test public void testIsImproperRotation() { RealSquareMatrix m = new RealSquareMatrix(3, new double[] { 1., 0., 0., 0., 1., 0., 0., 0., -1. }); Assert.assertTrue("isImproper", m.isImproperRotation()); m = new RealSquareMatrix(3, new double[] { 1., 0., 0., 0., -1., 0., 0., 0., -1. }); Assert.assertFalse("isImproper", m.isImproperRotation()); } /** * Test method for 'org.xmlcml.euclid.RealSquareMatrix.isUnitary()' */ @Test public void testIsUnitary() { RealSquareMatrix m = new RealSquareMatrix(3, new double[] { 1., 0., 0., 0., 1., 0., 0., 0., -1. }); Assert.assertTrue("isUnitary", m.isUnitary()); m = new RealSquareMatrix(3, new double[] { 1., 0., 0., 0., -1., 0., 0., 0., -1. }); Assert.assertTrue("isUnitary", m.isUnitary()); m = new RealSquareMatrix(3, new double[] { 1., 0., 1., 0., -1., 0., 0., 0., -1. }); Assert.assertFalse("isUnitary", m.isUnitary()); } /** * Test method for 'org.xmlcml.euclid.RealSquareMatrix.copyUpperToLower()' */ @Test public void testCopyUpperToLower() { RealSquareMatrix m = new RealSquareMatrix(3, new double[] { 6., 7., 8., 2., 5., 4., 3., 2., 9. }); m.copyUpperToLower(); RealSquareMatrix mm = new RealSquareMatrix(3, new double[] { 6., 7., 8., 7., 5., 4., 8., 4., 9. }); MatrixTest.assertEquals("copy upper", mm, m, EPS); } /** * Test method for 'org.xmlcml.euclid.RealSquareMatrix.copyLowerToUpper()' */ @Test public void testCopyLowerToUpper() { RealSquareMatrix m = new RealSquareMatrix(3, new double[] { 6., 7., 8., 2., 5., 4., 3., 2., 9. }); m.copyLowerToUpper(); RealSquareMatrix mm = new RealSquareMatrix(3, new double[] { 6., 2., 3., 2., 5., 2., 3., 2., 9. }); MatrixTest.assertEquals("copy upper", mm, m, EPS); } /** * Test method for 'org.xmlcml.euclid.RealSquareMatrix.lowerTriangle()' */ @Test public void testLowerTriangle() { RealSquareMatrix m = new RealSquareMatrix(3, new double[] { 6., 7., 8., 2., 5., 4., 3., 2., 9. }); RealArray ra = m.lowerTriangle(); RealArrayTest.assertEquals("lower triangle", new double[] { 6.0, 2.0, 5.0, 3.0, 2.0, 9.0 }, ra, EPS); } /** * Test method for 'org.xmlcml.euclid.RealSquareMatrix.transpose()' */ @Test public void testTranspose() { RealSquareMatrix m = new RealSquareMatrix(3, new double[] { 6., 7., 8., 2., 5., 4., 3., 1., 9. }); m.transpose(); RealSquareMatrix mm = new RealSquareMatrix(3, new double[] { 6., 2., 3., 7., 5., 1., 8., 4., 9. }); MatrixTest.assertEquals("transpose", mm, m, EPS); } /** * Test method for 'org.xmlcml.euclid.RealSquareMatrix.orthogonalise()' */ @Test public void testOrthogonalise() { RealSquareMatrix m = new RealSquareMatrix(3, new double[] { 6., 7., 8., 7., 5., 4., 8., 4., 9. }); m.orthogonalise(); Assert.assertTrue("orthogonalise", m.isOrthogonal()); RealSquareMatrix mm = new RealSquareMatrix(3, new double[] { 6.0, 7.0, 8.0, 7.7316819236624434, -0.35776420212319654, -5.485717765889034, 3.8939506336049337, -10.383868356279821, 6.1654218365411415, }); MatrixTest.assertEquals("orthogonalise", mm, m, 0.000000000001); } /** * Test method for * 'org.xmlcml.euclid.RealSquareMatrix.getCrystallographicOrthogonalisation(double[ * ] , double[])' */ @Test public void testGetCrystallographicOrthogonalisation() { double[] len = { 10., 11., 12. }; double[] ang = { 80., 90., 100. }; // degrees! RealSquareMatrix m = RealSquareMatrix .getCrystallographicOrthogonalisation(len, ang); RealSquareMatrix mm = new RealSquareMatrix(3, new double[] { 9.843316493307713, 0.0, 0.0, -1.7632698070846495, 10.832885283134289, 0.0, 0.0, 1.9101299543362344, 12.0 }); MatrixTest.assertEquals("orthogonalise", mm, m, 0.000000000001); } /** * Test method for 'org.xmlcml.euclid.RealSquareMatrix.getInverse()' */ @Test public void testGetInverse() { RealSquareMatrix m = new RealSquareMatrix(3, new double[] { 6., 7., 8., 2., 5., 4., 1., 3., 9. }); RealSquareMatrix inv = m.getInverse(); RealSquareMatrix mm = new RealSquareMatrix(3, new double[] { 0.3055555555555556, -0.36111111111111116, -0.11111111111111108, -0.12962962962962962, 0.42592592592592593, -0.07407407407407408, 0.009259259259259259, -0.10185185185185185, 0.14814814814814814, }); MatrixTest.assertEquals("inverse", mm, inv, 0.000000000001); } @Test public void calculateEigenvalues() { RealSquareMatrix m = new RealSquareMatrix(3, new double[] { 1., 2., 3., 2., 1., 8., 3., 8., 7. }); RealArray realArray = m.calculateEigenvalues(); double[] expected = new double[]{13.57729611363183,-0.03241110263496161,-4.54488501099687}; EuclidTestUtils.testEquals("inverse", expected, realArray.getArray(), 0.0000001); } @Test public void calculateInverse() { RealSquareMatrix m = new RealSquareMatrix(3, new double[] { 1., 2., 3., 2., 1., 8., 3., 8., 7. }); RealSquareMatrix expected = new RealSquareMatrix(3, new double[] { -28.5, 5.0, 6.5, 5.0, -1.0, -1.0, 6.5, -1.0, -1.5 }); RealSquareMatrix rsm = m.calculateInverse(); EuclidTestUtils.testEquals("inverse", expected.getMatrix(), rsm.getMatrix(), 0.0000001); RealSquareMatrix one = m.multiply(rsm); Assert.assertNotNull(one); } } euclid-euclid-2.9/src/test/java/org/xmlcml/euclid/test/RealTest.java000066400000000000000000000027721461721410700254730ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid.test; import static org.xmlcml.euclid.EC.EPS; import org.junit.Assert; import org.junit.Test; import org.xmlcml.euclid.Real; /** * test Real. * * @author pmr * */ public class RealTest { /** * Test method for 'org.xmlcml.euclid.Real.zeroArray(double, double[])' */ @Test public void testZeroArray() { double[] rr = new double[5]; Real.zeroArray(5, rr); DoubleTestBase.assertEquals("double[] ", new double[] { 0.0, 0.0, 0.0, 0.0, 0.0 }, rr, EPS); } /** * Test method for 'org.xmlcml.euclid.Real.initArray(double, double[], * double)' */ @Test public void testInitArray() { double[] rr = new double[5]; Real.initArray(5, rr, 3.0); DoubleTestBase.assertEquals("double[] ", new double[] { 3.0, 3.0, 3.0, 3.0, 3.0 }, rr, EPS); } @Test public void testIsRealDDD() { Assert.assertTrue(Real.isEqual(2.0, 1.99, 0.1)); } } euclid-euclid-2.9/src/test/java/org/xmlcml/euclid/test/StringArrayTest.java000066400000000000000000000036041461721410700270500ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid.test; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import org.junit.Assert; import org.junit.Test; /** * * @author pm286 * *There is no StringArray class (use List but here are some *useful functions */ public class StringArrayTest { @Test public void testGetSets() { String[] strings = {"a", "b", "c",}; List stringList = Arrays.asList(strings); Assert.assertEquals("stringList", 3, stringList.size()); Set stringSet = new HashSet(); stringSet.addAll(stringList); Assert.assertEquals("set", 3, stringSet.size()); } @Test public void testGetSets1() { String[] strings = {"a", "a", "c",}; List stringList = Arrays.asList(strings); Assert.assertEquals("stringList", 3, stringList.size()); Set stringSet = new HashSet(); stringSet.addAll(stringList); Assert.assertEquals("set", 2, stringSet.size()); } @Test public void testGetSetsIdentical() { String[] strings = {"a", "a", "a",}; List stringList = Arrays.asList(strings); Assert.assertEquals("stringList", 3, stringList.size()); Set stringSet = new HashSet(); stringSet.addAll(stringList); Assert.assertEquals("set", 1, stringSet.size()); } } euclid-euclid-2.9/src/test/java/org/xmlcml/euclid/test/Transform2Test.java000066400000000000000000000272501461721410700266430ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid.test; import static org.xmlcml.euclid.EC.EPS; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.xmlcml.euclid.Angle; import org.xmlcml.euclid.EC; import org.xmlcml.euclid.EuclidRuntimeException; import org.xmlcml.euclid.Real2; import org.xmlcml.euclid.RealSquareMatrix; import org.xmlcml.euclid.Transform2; import org.xmlcml.euclid.Vector2; import org.xmlcml.euclid.Transform2.Type; /** * test Transform2. * * @author pmr * */ public class Transform2Test { Transform2 t0; Transform2 t1; Transform2 t2; /** * setup. * * @throws Exception */ @Before public void setUp() throws Exception { t0 = new Transform2(); t1 = new Transform2( new double[] { 0., 1., 0., -1., 0., 0., 0., 0., 1. }); t2 = new Transform2(new double[] { 1., 0., 0.7, 0., -1., 0.5, 0., 0., 1. }); } /** * equality test. true if both args not null and equal within epsilon * * @param msg * message * @param test * @param expected * @param epsilon */ public static void assertEquals(String msg, Transform2 test, Transform2 expected, double epsilon) { Assert.assertNotNull("test should not be null (" + msg + EC.S_RBRAK, test); Assert.assertNotNull("expected should not be null (" + msg + EC.S_RBRAK, expected); DoubleTestBase.assertEquals(msg, test.getMatrixAsArray(), expected .getMatrixAsArray(), epsilon); } /** * equality test. true if both args not null and equal within epsilon * * @param msg * message * @param test * 16 values * @param expected * @param epsilon */ public static void assertEquals(String msg, double[] test, Transform2 expected, double epsilon) { Assert.assertNotNull("test should not be null (" + msg + EC.S_RBRAK, test); Assert.assertEquals("test should have 16 elements (" + msg + EC.S_RBRAK, 9, test.length); Assert.assertNotNull("ref should not be null (" + msg + EC.S_RBRAK, expected); DoubleTestBase.assertEquals(msg, test, expected.getMatrixAsArray(), epsilon); } /** * Test method for 'org.xmlcml.euclid.Transform2.Transform2()' */ @Test public void testTransform2() { Assert.assertNotNull("transform2", t0); Transform2Test.assertEquals("transform2", new double[] { 1., 0., 0., 0., 1., 0., 0., 0., 1. }, t0, EPS); Assert.assertTrue("transform2", t0.getTransformationType() == Type.ANY); } /** * Test method for 'org.xmlcml.euclid.Transform2.Transform2(Vector2)' */ @Test public void testTransform2Vector2() { Vector2 v = new Vector2(1., 2.); Transform2 t = new Transform2(v); Real2 vv = t.getTranslation(); Assert.assertEquals("vector", 1., vv.getX(), EPS); Assert.assertEquals("vector", 2., vv.getY(), EPS); Assert.assertEquals("transform2", Type.ROT_TRANS.s, t .getTransformationType().s); } /** * Test method for 'org.xmlcml.euclid.Transform2.Transform2(Angle)' */ @Test public void testTransform2Angle() { Transform2 ta = new Transform2(new Angle(Math.PI / 3.)); Angle aa = ta.getAngleOfRotation(); Assert.assertEquals("transform angle", Math.PI / 3., aa.getRadian(), EPS); ta = new Transform2(new Angle(1.)); aa = ta.getAngleOfRotation(); Assert.assertEquals("transform angle", 1., aa.getRadian(), EPS); Assert.assertEquals("transform2", Type.ROT_ORIG.s, ta .getTransformationType().s); } /** * Test method for 'org.xmlcml.euclid.Transform2.Transform2(Transform2, * Real2)' */ @Test public void testTransform2Transform2Real2() { Transform2 ta = new Transform2(new Angle(Math.PI / 3.)); Transform2 tb = new Transform2(ta, new Real2(3., 4.)); // haven't checked this is right Real2 tr = tb.getTranslation(); Assert.assertEquals("vector", -1.9641016151377553, tr.getX(), EPS); Assert.assertEquals("vector", 4.598076211353316, tr.getY(), EPS); Angle aa = ta.getAngleOfRotation(); Assert.assertEquals("transform angle", Math.PI / 3., aa.getRadian(), EPS); Assert.assertEquals("transform2", Type.ROT_TRANS.s, tb .getTransformationType().s); } /** * Test method for 'org.xmlcml.euclid.Transform2.Transform2(Vector2, * Vector2)' */ @Test public void testTransform2Vector2Vector2() { Transform2 ta = new Transform2(new Vector2(1., 2.), new Vector2(3., 4.)); Transform2Test.assertEquals("transform2", new double[] { 0.9838699100999074, 0.17888543819998318, 0.0, -0.17888543819998318, 0.9838699100999074, 0.0, 0.0, 0.0, 1.0, }, ta, EPS); Assert.assertEquals("transform2", Type.ROT_ORIG.s, ta .getTransformationType().s); } /** * Test method for 'org.xmlcml.euclid.Transform2.Transform2(Real2, Real2)' */ @Test public void testTransform2Real2Real2() { Transform2 ta = null; ta = new Transform2(new Real2(1., 2.), new Real2(3., 4.)); Transform2Test.assertEquals("transform2", new double[] { 1.0, 2.0, 0.0, 3.0, 4.0, 0.0, 0.0, 0.0, 1.0, }, ta, EPS); Assert.assertEquals("transform2", Type.ROT_ORIG.s, ta .getTransformationType().s); } /** * Test method for 'org.xmlcml.euclid.Transform2.Transform2(double[])' */ @Test public void testTransform2DoubleArray() { Transform2 ta = new Transform2(new double[] { 1., 0., 1., 0., -1., 2., 0., 0., 1. }); Transform2Test.assertEquals("transform2", new double[] { 1., 0., 1., 0., -1., 2., 0., 0., 1. }, ta, EPS); Assert.assertEquals("transform2", Type.ROT_ORIG.s, ta .getTransformationType().s); } /** * Test method for 'org.xmlcml.euclid.Transform2.Transform2(Transform2)' */ @Test public void testTransform2Transform2() { Transform2 t = new Transform2(t1); Assert.assertTrue("transform", t.isEqualTo(t1)); } /** * Test method for * 'org.xmlcml.euclid.Transform2.Transform2(RealSquareMatrix)' */ @Test public void testTransform2RealSquareMatrix() { RealSquareMatrix rsm = new RealSquareMatrix(3, new double[] { 1., 0., 2., 0., 1., 3., 0., 0., 1. }); Transform2 t = new Transform2(rsm); DoubleTestBase.assertEquals("rsm", new double[] { 1., 0., 2., 0., 1., 3., 0., 0., 1. }, t.getMatrixAsArray(), EPS); Assert.assertEquals("transform2", Type.NULL.s, t .getTransformationType().s); } /** * Test method for * 'org.xmlcml.euclid.Transform2.Transform2(RealSquareMatrix, Vector2)' */ @Test public void testTransform2RealSquareMatrixVector2() { RealSquareMatrix rsm = new RealSquareMatrix(2, new double[] { 1., 0., 0., 1., }); Transform2 t = new Transform2(rsm, new Vector2(2., 3.)); Transform2Test.assertEquals("rsm", new double[] { 1., 0., 2., 0., 1., 3., 0., 0., 1. }, t, EPS); Assert.assertEquals("transform2", Type.NULL.s, t .getTransformationType().s); rsm = new RealSquareMatrix(3, new double[] { 1., 0., 2., 0., 1., 3., 0., 0., 1. }); try { t = new Transform2(rsm, new Vector2(2., 3.)); Assert.fail("should always throw " + "must have 2*2 rotation matrix"); } catch (EuclidRuntimeException e) { Assert.assertEquals("matrix vector", "must have 2*2 rotation matrix", e.getMessage()); } } /** * Test method for 'org.xmlcml.euclid.Transform2.isEqualTo(Transform2)' */ @Test public void testIsEqualToTransform2() { Transform2 t = new Transform2(t1); Assert.assertTrue("isEqualsTo", t.isEqualTo(t1)); } /** * Test method for 'org.xmlcml.euclid.Transform2.concatenate(Transform2)' */ @Test public void testConcatenate() { Transform2 t = t1.concatenate(t2); Transform2Test.assertEquals("concatenate", new double[] { 0.0, -1.0, 0.5, -1.0, 0.0, -0.7, 0.0, 0.0, 1.0, }, t, EPS); Assert.assertEquals("concatenate", Type.ROT_ORIG.s, t .getTransformationType().s); } /** * Test method for 'org.xmlcml.euclid.Transform2.setTransformationType(int)' */ @Test public void testSetTransformationType() { Transform2 t = t1.concatenate(t2); t.setTransformationType(Type.ROT_TRANS); Assert.assertEquals("setType", Type.ROT_TRANS.s, t .getTransformationType().s); } /** * Test method for 'org.xmlcml.euclid.Transform2.checkMatrix()' */ @Test public void testCheckMatrix() { // not sure this works Transform2 t = new Transform2(new double[] { 0., 1., 0., -1., 0., 0., 0., 0., 1. }); Assert .assertEquals("getType", Type.NULL.s, t.getTransformationType().s); t = new Transform2(new double[] { 1., 0., 0., 0., 1., 0., 0., 0., 1. }); Assert .assertEquals("getType", Type.NULL.s, t.getTransformationType().s); t = new Transform2(new double[] { 1., 0., 2., 0., 1., 3., 0., 0., 1. }); Assert .assertEquals("getType", Type.NULL.s, t.getTransformationType().s); } /** * Test method for 'org.xmlcml.euclid.Transform2.getAngleOfRotation()' */ @Test public void testGetAngleOfRotation() { Transform2 t = new Transform2(new double[] { 0., 1., 0., -1., 0., 0., 0., 0., 1. }); Angle a = t.getAngleOfRotation(); Assert.assertEquals("getAngle", Math.PI / 2., a.getRadian(), EPS); } /** * Test method for 'org.xmlcml.euclid.Transform2.flipAboutVector(Real2)' */ @Test public void testFlipAboutVector() { Transform2 t = Transform2.flipAboutVector(new Vector2(1., 1.)); DoubleTestBase.assertEquals("flip", new double[] { 0.0, 1., 0.0, 1., 0.0, 0.0, 0.0, 0.0, 1.0, }, t.getMatrixAsArray(), 0.0000000001); } /** * Test method for 'org.xmlcml.euclid.Transform2.getTranslation()' */ @Test public void testGetTranslation() { Real2 v = t2.getTranslation(); Assert.assertEquals("translation", 0.7, v.getX(), EPS); Assert.assertEquals("translation", 0.5, v.getY(), EPS); } /** * Test method for 'org.xmlcml.euclid.Transform2.getCentreOfRotation()' */ /** * -- not yet written * * @Test public void testGetCentreOfRotation() { Transform2 t = null; try { * t = new Transform2(new double[]{ 0., 1., 2., -1., 0., 3., 0., 0., * 1. }); } catch (EuclidException e) { neverFail(e); } Real2 r = * t.getCentreOfRotation(); Angle a = t.getAngleOfRotation(); * Assert.assertEquals("getAngle", Math.PI/2., a.getRadian(), EPS); } * -- */ /** * Test method for 'org.xmlcml.euclid.Transform2.getRotationMatrix()' */ @Test public void testGetRotationMatrix() { Transform2 t = new Transform2(new double[] { 0., 1., 2., -1., 0., 3., 0., 0., 1. }); RealSquareMatrix rsm = t.getRotationMatrix(); DoubleTestBase.assertEquals("getRotationMatrix", new double[] { 0.0, 1.0, -1.0, 0.0, }, rsm.getMatrixAsArray(), EPS); } /** * rotation may be confused with skew */ @Test public void testGetAngleOfRotation1() { Transform2 t = new Transform2(new double[] { 0., 1., 0., -1., 0.3, 0., 0., 0., 1. }); Angle a = t.getAngleOfRotation(); Assert.assertEquals("getAngle", Math.PI / 2., a.getRadian(), EPS); } /** * rotation may be confused with skew */ @Test public void testGetAngleOfRotation2() { Transform2 t = new Transform2(new double[] { 1., 0.3, 0., 0., 1, 0., 0., 0., 1. }); Angle a = t.getAngleOfRotation(); Assert.assertEquals("getAngle", 0., a.getRadian(), EPS); } /** * rotation may be confused with skew */ @Test public void testGetAngleOfRotation3() { Transform2 t = new Transform2(new double[] { 10., 3., 0., 0., 10., 0., 0., 0., 1. }); Angle a = t.getAngleOfRotation(); Assert.assertEquals("getAngle", 0., a.getRadian(), EPS); } } euclid-euclid-2.9/src/test/java/org/xmlcml/euclid/test/Transform3Test.java000066400000000000000000000275611461721410700266510ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid.test; import static org.xmlcml.euclid.EC.EPS; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.xmlcml.euclid.Angle; import org.xmlcml.euclid.EC; import org.xmlcml.euclid.Point3; import org.xmlcml.euclid.RealArray; import org.xmlcml.euclid.RealSquareMatrix; import org.xmlcml.euclid.Transform3; import org.xmlcml.euclid.Vector3; import org.xmlcml.euclid.Axis.Axis3; import org.xmlcml.euclid.Transform3.Type; /** * test Transform3. * * @author pmr * */ public class Transform3Test extends GeomTest { /** * setup. * * @throws Exception */ @Before public void setUp() throws Exception { super.setUp(); } /** * equality test. true if both args not null and equal within epsilon * * @param msg * message * @param test * @param expected * @param epsilon */ public static void assertEquals(String msg, Transform3 test, Transform3 expected, double epsilon) { Assert.assertNotNull("test should not be null (" + msg + EC.S_RBRAK, test); Assert.assertNotNull("expected should not be null (" + msg + EC.S_RBRAK, expected); DoubleTestBase.assertEquals(msg, test.getMatrixAsArray(), expected .getMatrixAsArray(), epsilon); } /** * equality test. true if both args not null and equal within epsilon * * @param msg * message * @param test * 16 values * @param expected * @param epsilon */ public static void assertEquals(String msg, double[] test, Transform3 expected, double epsilon) { Assert.assertNotNull("test should not be null (" + msg + EC.S_RBRAK, test); Assert.assertEquals("test should have 16 elements (" + msg + EC.S_RBRAK, 16, test.length); Assert.assertNotNull("ref should not be null (" + msg + EC.S_RBRAK, expected); DoubleTestBase.assertEquals(msg, test, expected.getMatrixAsArray(), epsilon); } /** * Test method for 'org.xmlcml.euclid.Transform3.Transform3()' */ @Test public void testTransform3() { Assert.assertNotNull("transform3", tr0); } /** * Test method for 'org.xmlcml.euclid.Transform3.Transform3(int)' */ @Test public void testTransform3Int() { Transform3 t = new Transform3(Type.ANY); Assert.assertNotNull("transform3", t); Transform3Test.assertEquals("transform3", new double[] { 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, }, t, EPS); } /** * Test method for 'org.xmlcml.euclid.Transform3.Transform3(Vector3)' */ @Test public void testTransform3Vector3() { Transform3 t = new Transform3(new Vector3(1., 2., 3.)); Transform3Test.assertEquals("transform3 vector", new double[] { 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 2.0, 0.0, 0.0, 1.0, 3.0, 0.0, 0.0, 0.0, 1.0, }, t, EPS); } /** * Test method for 'org.xmlcml.euclid.Transform3.Transform3(Axis3, Angle)' */ @Test public void testTransform3Axis3Angle() { Transform3 t = new Transform3(Axis3.X, new Angle(Math.PI / 3.)); // not sure if this is right Transform3Test.assertEquals("transform3 vector", new double[] { 1.0, 0.0, 0.0, 0.0, 0.0, 0.5, 0.8660254037844386, 0.0, 0.0, -0.8660254037844386, 0.5, 0.0, 0.0, 0.0, 0.0, 1.0 }, t, EPS); } /** * Test method for 'org.xmlcml.euclid.Transform3.Transform3(Angle, Angle, * Angle)' */ @Test public void testTransform3AngleAngleAngle() { Transform3 t = new Transform3(new Angle(Math.PI / 2.), new Angle( Math.PI / 2.), new Angle(Math.PI / 2.)); // not sure if this is right Transform3Test.assertEquals("transform3 vector", new double[] { 0., 0., 1., 0., 0., -1., 0., 0., 1., 0., 0., 0., 0., 0., 0., 1. }, t, EPS); } /** * Test method for 'org.xmlcml.euclid.Transform3.Transform3(Transform3, * Point3)' */ @Test public void testTransform3Transform3Point3() { Transform3 t = new Transform3(tr0, new Point3(1., 2., 3.)); // tr0 is rotation 0 degrees, so should give identity matrix Transform3Test.assertEquals("transform3 vector", new double[] { 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 }, t, EPS); } /** * Test method for 'org.xmlcml.euclid.Transform3.Transform3(Vector3, Angle)' */ @Test public void testTransform3Vector3Angle() { Transform3 t = new Transform3(new Vector3(1. / s3, 1. / s3, 1. / s3), new Angle(Math.PI / 3.)); // not sure if this is right Transform3Test.assertEquals("transform3 vector angle", new double[] { 0.6666666666666667, -0.3333333333333333, 0.6666666666666667, 0.0, 0.6666666666666667, 0.6666666666666667, -0.3333333333333333, 0.0, -0.3333333333333333, 0.6666666666666667, 0.6666666666666667, 0.0, 0.0, 0.0, 0.0, 1.0 }, t, EPS); } /** * Test method for 'org.xmlcml.euclid.Transform3.Transform3(Line3, Angle)' */ @Test public void testTransform3Line3Angle() { Transform3 t = new Transform3(l123456, new Angle(Math.PI / 3.)); // not sure if this is right Transform3Test.assertEquals("transform3 line3 angle", new double[] { 0.5357142857142858, -0.6229365034008422, 0.5700529070291328, 1.5515079319722704, 0.765793646257985, 0.642857142857143, -0.01716931065742361, -1.174444435373113, -0.3557671927434186, 0.4457407392288521, 0.8214285714285715, 0.2657936462579853, 0.0, 0.0, 0.0, 1.0 }, t, EPS); } /** * Test method for 'org.xmlcml.euclid.Transform3.Transform3(Vector3, * Vector3)' */ @Test public void testTransform3Vector3Vector3() { Transform3 t = new Transform3(v123, v321); // not sure if this is right Transform3Test.assertEquals("transform3 vector vector", new double[] { 0.761904761904762, 0.1904761904761905, 0.6190476190476192, 0.0, -0.38095238095238104, 0.9047619047619049, 0.1904761904761905, 0.0, -0.5238095238095238, -0.38095238095238104, 0.761904761904762, 0.0, 0.0, 0.0, 0.0, 1.0 }, t, EPS); } /** * Test method for 'org.xmlcml.euclid.Transform3.Transform3(Vector3, * Vector3, Vector3)' */ @Test public void testTransform3Vector3Vector3Vector3() { Transform3 t = null; t = new Transform3(v123, v321, v100); Transform3Test.assertEquals("transform3 vector vector vector", new double[] { 1.0, 2.0, 3.0, 0.0, 3.0, 2.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0 }, t, EPS); } /** * Test method for 'org.xmlcml.euclid.Transform3.Transform3(double[])' */ @Test public void testTransform3DoubleArray() { Transform3 t = new Transform3(new double[] { 0., 0., 1., 4., 0., 1., 0., 8., -1., 0., 0., 9., 0., 0., 0., 1. }); Transform3Test.assertEquals("transform3 double[]", new double[] { 0., 0., 1., 4., 0., 1., 0., 8., -1., 0., 0., 9., 0., 0., 0., 1. }, t, EPS); } /** * Test method for 'org.xmlcml.euclid.Transform3.Transform3(Transform3)' */ @Test public void testTransform3Transform3() { Transform3 t = new Transform3(tr1); Transform3Test.assertEquals("copy", t, tr1, EPS); } /** * Test method for * 'org.xmlcml.euclid.Transform3.Transform3(RealSquareMatrix)' */ @Test public void testTransform3RealSquareMatrix() { Transform3 t = new Transform3(new RealSquareMatrix(4, new double[] { 0., 0., 1., 4., 0., 1., 0., 8., -1., 0., 0., 9., 0., 0., 0., 1. })); Transform3Test.assertEquals("transform3 rsm", new double[] { 0., 0., 1., 4., 0., 1., 0., 8., -1., 0., 0., 9., 0., 0., 0., 1. }, t, EPS); } /** * Test method for * 'org.xmlcml.euclid.Transform3.Transform3(RealSquareMatrix, Vector3)' */ @Test public void testTransform3RealSquareMatrixVector3() { Transform3 t = new Transform3(new RealSquareMatrix(3, new double[] { 0., 0., 1., 0., 1., 0., -1., 0., 0., }), new Vector3(4., 8., 9.)); Transform3Test.assertEquals("transform3 rsm vector", new double[] { 0., 0., 1., 4., 0., 1., 0., 8., -1., 0., 0., 9., 0., 0., 0., 1. }, t, EPS); } /** * Test method for 'org.xmlcml.euclid.Transform3.Transform3(String)' */ @Test public void testTransform3String() { Transform3 t = new Transform3("x, -y, 1/2+z"); Transform3Test.assertEquals("transform3 string", new double[] { 1.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.5, 0.0, 0.0, 0.0, 0.0 }, t, EPS); } /** * Test method for 'org.xmlcml.euclid.Transform3.concatenate(Transform3)' */ @Test public void testConcatenate() { Transform3 t1 = new Transform3("x, -y, z"); Transform3 t2 = new Transform3("-x, -y, -z"); Transform3 t = t1.concatenate(t2); Transform3Test.assertEquals("transform3 concatenate", new double[] { -1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 0.0 }, t, EPS); } /** * Test method for 'org.xmlcml.euclid.Transform3.getAxisAndAngle(Vector3, * Angle)' */ @Test public void testGetAxisAndAngle() { Transform3 t = new Transform3(new Vector3(1. / s3, 1. / s3, 1. / s3), new Angle(Math.PI / 3.)); Vector3 v = new Vector3(); Angle a = new Angle(); t.getAxisAndAngle(v, a); // not sure if this is right Vector3Test.assertEquals("vector angle", new Vector3(1. / s3, 1. / s3, 1. / s3), v, EPS); Assert.assertEquals("vector angle", Math.PI / 3., a.getRadian(), EPS); t = new Transform3("y, -x, z"); t.getAxisAndAngle(v, a); // not sure if this is right Vector3Test.assertEquals("vector angle", new double[] { 0., 0., -1. }, v, EPS); Assert.assertEquals("vector angle", Math.PI / 2., a.getRadian(), EPS); } /** * Test method for 'org.xmlcml.euclid.Transform3.getTranslation()' */ @Test public void testGetTranslation() { Transform3 t = new Transform3("x+1/2, y+1/4, z+1/6"); Vector3Test.assertEquals("transform3 translation", new double[] { 0.5, 0.25, 1. / 6. }, t.getTranslation(), EPS); } /** * Test method for 'org.xmlcml.euclid.Transform3.setTranslation()' */ @Test public void testSetTranslation() { Transform3 t = new Transform3("x+1/2, y+1/4, z+1/6"); t.incrementTranslation(new Vector3(0.6, 0.7, 0.8)); Vector3Test.assertEquals("transform3 increment translation", new double[] { 1.1, 0.95, 0.8 + (1. / 6.) }, t.getTranslation(), EPS); } /** * Test method for 'org.xmlcml.euclid.Transform3.getCentreOfRotation()' */ @Test public void testGetCentreOfRotation() { Transform3 t = new Transform3("-x+1/2, -y+1/2, z"); Transform3Test.assertEquals("transform3 translation", new double[] { -1.0, 0.0, 0.0, 0.5, 0.0, -1.0, 0.0, 0.5, 0.0, 0.0, 1.0, 0., 0.0, 0.0, 0.0, 0.0 }, t, EPS); // not sure if this is right Point3 p = t.getCentreOfRotation(); Point3Test.assertEquals("transform3 centre", new double[] { 0.5, 0.5, 0.0 }, p, EPS); } /** * Test method for 'org.xmlcml.euclid.Transform3.getScales()' */ @Test public void testGetScales() { Transform3 t = new Transform3(new double[] { 10., 0., 0., 0., 0., 20., 0., 0., 0., 0., 30., 0., 0., 0., 0., 1. }); RealArray scales = t.getScales(); RealArrayTest.assertEquals("scales", new double[] { 10., 20., 30. }, scales, EPS); } /** * Test method for 'org.xmlcml.euclid.Transform3.getRotationMatrix()' */ @Test public void testGetRotationMatrix() { Transform3 t = new Transform3(new Angle(Math.PI / 2.), new Angle( Math.PI / 2.), new Angle(Math.PI / 2.)); // not sure if this is right Transform3Test.assertEquals("transform3 vector", new double[] { 0., 0., 1., 0., 0., -1., 0., 0., 1., 0., 0., 0., 0., 0., 0., 1. }, t, EPS); RealMatrixTest.assertEquals("rotation matrix", 3, 3, new double[] { 0., 0., 1., 0., -1., 0., 1., 0., 0. }, t.getRotationMatrix(), EPS); } } euclid-euclid-2.9/src/test/java/org/xmlcml/euclid/test/UtilTest.java000066400000000000000000000543031461721410700255220ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * */ package org.xmlcml.euclid.test; import static org.xmlcml.euclid.EuclidConstants.EPS; import static org.xmlcml.euclid.EuclidConstants.F_S; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.net.URLEncoder; import java.util.ArrayList; import java.util.List; import org.junit.Assert; import org.junit.Ignore; import org.junit.Test; import org.xmlcml.euclid.EuclidConstants; import org.xmlcml.euclid.EuclidRuntimeException; import org.xmlcml.euclid.Int; import org.xmlcml.euclid.Util; /** * @author pm286 * */ public class UtilTest { /** * Test method for 'org.xmlcml.cml.base.CMLUtil.addElement(String[], * String)' */ @Test public final void testAddElementToStringArray() { String[] array = new String[] { "a", "b" }; String[] array1 = Util.addElementToStringArray(array, "c"); Assert.assertEquals("array", 3, array1.length); StringTestBase.assertEquals("array", new String[] { "a", "b", "c" }, array1); } /** * Test method for 'org.xmlcml.cml.base.CMLUtil.removeElement(String[], * String)' */ @Test public final void testRemoveElement() { String[] array = new String[] { "a", "b", "c" }; String[] array1 = Util.removeElementFromStringArray(array, "b"); Assert.assertEquals("array", 2, array1.length); StringTestBase.assertEquals("array", new String[] { "a", "c" }, array1); } /** * Test method for 'org.xmlcml.cml.base.CMLUtil.createFile(File, String)' */ @Test public final void testCreateFile() { File dir = null; try { dir = Util.getResourceFile(EuclidTestUtils.BASE_RESOURCE); } catch (Exception e1) { throw new EuclidRuntimeException("should never throw " + e1); } File junk = new File(dir, "junk"); if (junk.exists()) { junk.delete(); } Assert.assertTrue("create", !junk.exists()); try { Util.createFile(dir, "junk"); } catch (Exception e) { e.printStackTrace(); throw new EuclidRuntimeException("should never throw " + e); } Assert.assertTrue("should exist: " + junk.toString(), junk.exists()); } @Test public void testGetRelativeFilename() { String S = File.separator; File file1 = new File("a"+S+"b"+S+"c"+S+"d"); File file2 = new File("a"+S+"b"+S+"e"); String relative = Util.getRelativeFilename(file1, file2, "/"); // this failed on other machines // Assert.assertEquals("relative", "../../e", relative); try { File file3 = new File(file1, relative); Assert.assertEquals("canonical", file2.getCanonicalPath(), file3.getCanonicalPath()); } catch (IOException e) { e.printStackTrace(); } } /** * Test method for org.xmlcml.euclid.Util.BUG(java.lang.String, * java.lang.Exception)}. */ @Test public final void testBUGStringException() { try { Util.BUG("foo", new Exception("bar")); Assert.fail("should throw exception"); } catch (RuntimeException e) { Assert.assertEquals("bug", "BUG: (foo)should never throw: java.lang.Exception: bar", e .getMessage()); } catch (Exception e) { throw new EuclidRuntimeException("should never throw " + e); } } /** * Test method for {org.xmlcml.euclid.Util.BUG(java.lang.Exception)}. */ @Test public final void testBUGException() { try { Util.BUG(new Exception("bar")); Assert.fail("should throw exception"); } catch (RuntimeException e) { Assert.assertEquals("bug", "BUG: should never throw: java.lang.Exception: bar", e .getMessage()); } catch (Exception e) { throw new EuclidRuntimeException("should never throw " + e); } } /** * @deprecated Test method for {@link org.xmlcml.euclid.Util#throwNYI()}. */ @Test public final void testNYI() { try { Util.throwNYI(); Assert.fail("should throw exception"); } catch (RuntimeException e) { Assert.assertEquals("NYI", "not yet implemented", e.getMessage()); } catch (Exception e) { throw new EuclidRuntimeException("should never throw " + e); } } /** * Test method for {org.xmlcml.euclid.Util.BUG(java.lang.String)}. */ @Test public final void testBUGString() { try { Util.BUG("foo"); Assert.fail("should throw exception"); } catch (RuntimeException e) { Assert.assertEquals("bug", "BUG: (foo)should never throw: java.lang.RuntimeException", e.getMessage()); } catch (Exception e) { throw new EuclidRuntimeException("should never throw " + e); } } /** * Test method for * {@link org.xmlcml.euclid.Util#getInputStreamFromResource(java.lang.String)} * . */ @Test public final void testGetInputStreamFromResource() { String filename = EuclidTestUtils.BASE_RESOURCE +EuclidConstants.U_S + "cml0.xml"; InputStream is = null; try { is = Util.getInputStreamFromResource(filename); } catch (Exception e) { throw new EuclidRuntimeException("should never throw " + e); } try { int read=is.read(); Assert.assertTrue(read!=-1); } catch (Exception e) { throw new EuclidRuntimeException("should never throw " + e); } } /** * Test method for * {@link org.xmlcml.euclid.Util#getResource(java.lang.String)}. */ @Test public final void testGetResource() { String filename = EuclidTestUtils.BASE_RESOURCE +EuclidConstants.U_S + "cml0.xml"; URL url = Util.getResource(filename); Assert.assertNotNull("url", url); Assert.assertTrue("target", url.toString().endsWith( "/org/xmlcml/euclid/cml0.xml")); } /** * Test method for * {@link org.xmlcml.euclid.Util#getResourceFile(java.lang.String[])}. */ @Test public final void testGetResourceFile() { String filename = EuclidTestUtils.BASE_RESOURCE +EuclidConstants.U_S + "cml0.xml"; File file = null; try { file = Util.getResourceFile(filename); } catch (Exception e) { Assert.fail("should never throw " + e); } Assert.assertNotNull("url", file); Assert.assertTrue("target", file.toString().endsWith( "" + F_S + "org" + F_S + "xmlcml" + F_S + "euclid" + F_S+ "cml0.xml")); Assert.assertTrue("file", file.exists()); } /** * Test method for {at link * org.xmlcml.cml.base.CMLUtil#buildPath(java.lang.String...)}. */ @Test public final void testBuildPath() { String s = Util.buildPath("foo", "bar", "plugh"); Assert.assertEquals("build", "foo" + F_S + "bar" + F_S + "plugh", s); } /** * Test method for * {@link org.xmlcml.euclid.Util#deleteFile(java.io.File, boolean)}. */ @Test public final void testDeleteFile() { File dir = Util.getTEMP_DIRECTORY(); try { Util.createFile(dir, "grot"); } catch (IOException e) { Assert.fail("IOException " + e); } File file = new File(dir, "grot"); Assert.assertTrue("exists", file.exists()); boolean deleteDirectory = false; Util.deleteFile(file, deleteDirectory); Assert.assertFalse("exists", file.exists()); } /** * Test method for * {@link org.xmlcml.euclid.Util#copyFile(java.io.File, java.io.File)}. */ @Test public final void testCopyFile() { try { File dir = Util.getTEMP_DIRECTORY(); File file = new File(dir, "grot.txt"); FileWriter fw = new FileWriter(file); fw.write("this is a line\n"); fw.write("and another\n"); fw.close(); File outFile = new File(dir, "grotOut.txt"); Util.copyFile(file, outFile); Assert.assertTrue("exists", outFile.exists()); } catch (IOException e) { Assert.fail("IOException " + e); } } /** * Test method for {@link org.xmlcml.euclid.Util#dump(java.net.URL)}. */ @Test public final void testDump() { try { File dir = Util.getTEMP_DIRECTORY(); File file = new File(dir, "grot.txt"); FileWriter fw = new FileWriter(file); fw.write("this is a line\n"); fw.write("and another\n"); fw.close(); URL url = file.toURI().toURL(); String s = Util.dump(url); String exp = "\n" + " 116 104 105 115 32 105 115 32 97 32 this is a \n" + " 108 105 110 101 10 97 110 100 32 97 line and a\n" + " 110 111 116 104 101 114 10 nother "; Assert.assertEquals("dump", exp, s); } catch (Exception e) { Assert.fail("IOException " + e); } } /** * Test method for {@link org.xmlcml.euclid.Util#spaces(int)}. */ @Test public final void testSpaces() { Assert.assertEquals("spaces", " ", Util.spaces(5)); } /** * Test method for * {@link org.xmlcml.euclid.Util#getSuffix(java.lang.String)}. */ @Test public final void testGetSuffix() { Assert.assertEquals("suffix", "txt", Util.getSuffix("foo.bar.txt")); } /** * Test method for * {@link org.xmlcml.euclid.Util#truncateAndAddEllipsis(java.lang.String, int)} * . */ @Test public final void testTruncateAndAddEllipsis() { Assert.assertEquals("suffix", "qwert ... ", Util .truncateAndAddEllipsis("qwertyuiop", 5)); } /** * Test method for {@link org.xmlcml.euclid.Util#deQuote(java.lang.String)}. */ @Test public final void testDeQuote() { Assert.assertEquals("deQuote", "This is a string", Util .deQuote("'This is a string'")); Assert.assertEquals("deQuote", "This is a string", Util .deQuote("\"This is a string\"")); } /** * Test method for * {@link org.xmlcml.euclid.Util#rightTrim(java.lang.String)}. */ @Test public final void testRightTrim() { Assert.assertEquals("deQuote", " This is a string", Util .rightTrim(" This is a string ")); } /** * Test method for {@link org.xmlcml.euclid.Util#leftTrim(java.lang.String)} * . */ @Test public final void testLeftTrim() { Assert.assertEquals("deQuote", "This is a string ", Util .leftTrim(" This is a string ")); } /** * Test method for * {@link org.xmlcml.euclid.Util#indexOfBalancedBracket(char, java.lang.String)} * . */ @Test public final void testIndexOfBalancedBracket() { String s = "(foo(bar)junk)grot"; Assert .assertEquals("balanced", 13, Util.indexOfBalancedBracket('(', s)); } /** * Test method for * {@link org.xmlcml.euclid.Util#getCommaSeparatedStrings(java.lang.String)} * . */ @Test public final void testGetCommaSeparatedStrings() { List ss = Util .getCommaSeparatedStrings("aa, bb, \"cc dd\", ee "); Assert.assertEquals("list", 4, ss.size()); Assert.assertEquals("s0", "aa", ss.get(0)); Assert.assertEquals("s1", " bb", ss.get(1)); Assert.assertEquals("s2", " \"cc dd\"", ss.get(2)); Assert.assertEquals("s3", " ee", ss.get(3)); } /** * Test method for * {@link org.xmlcml.euclid.Util#createCommaSeparatedStrings(java.util.List)} * . */ @Test public final void testCreateCommaSeparatedStrings() { List ss = new ArrayList(); ss.add("aa"); ss.add("bb"); ss.add("cc \"B\" dd"); ss.add("ee"); String s = Util.createCommaSeparatedStrings(ss); Assert.assertEquals("comma", "aa,bb,\"cc \"\"B\"\" dd\",ee", s); } /** * Test method for * {@link org.xmlcml.euclid.Util#quoteConcatenate(java.lang.String[])}. */ @Test public final void testQuoteConcatenate() { String[] ss = new String[4]; ss[0] = "aa"; ss[1] = "bb"; ss[2] = "cc \"B\" dd"; ss[3] = "ee"; String s = Util.quoteConcatenate(ss); Assert.assertEquals("quote", "aa bb \"cc \"B\" dd\" ee", s); } /** * Test method for * {@link org.xmlcml.euclid.Util#indexOf(java.lang.String, java.lang.String[], boolean)} * . */ @Test public final void testIndexOf() { String[] ss = new String[4]; ss[0] = "aa"; ss[1] = "bb"; ss[2] = "cc \"B\" dd"; ss[3] = "ee"; boolean ignoreCase = false; Assert.assertEquals("index", 1, Util.indexOf("bb", ss, ignoreCase)); Assert.assertEquals("index", -1, Util.indexOf("BB", ss, ignoreCase)); ignoreCase = true; Assert.assertEquals("index", 1, Util.indexOf("BB", ss, ignoreCase)); Assert.assertEquals("index", -1, Util.indexOf("XX", ss, ignoreCase)); } /** * Test method for * {@link org.xmlcml.euclid.Util#removeHTML(java.lang.String)}. */ @Test public final void testRemoveHTML() { String s = "

This is a para

"; String ss = Util.removeHTML(s); Assert.assertEquals("html", "This is a para", ss); } /** * Test method for {@link org.xmlcml.euclid.Util#warning(java.lang.String)}. */ @Test public final void testWarning() { // no useful method } /** * Test method for {@link org.xmlcml.euclid.Util#message(java.lang.String)}. */ @Test public final void testMessage() { // no useful method } /** * Test method for {@link org.xmlcml.euclid.Util#error(java.lang.String)}. */ @Test public final void testError() { // no useful method } /** * Test method for * {@link org.xmlcml.euclid.Util#BUG(java.lang.String, java.lang.Throwable)} * . */ @Test public final void testBUGStringThrowable() { // no useful method } /** * Test method for {@link org.xmlcml.euclid.Util#BUG(java.lang.Throwable)}. */ @Test public final void testBUGThrowable() { // no useful method } /** * Test method for {@link org.xmlcml.euclid.Util#getPWDName()}. */ @Test public final void testGetPWDName() { // no useful method } /** * Test method for * {@link org.xmlcml.euclid.Util#substituteString(java.lang.String, java.lang.String, java.lang.String, int)} * . */ @Test public final void testSubstituteString() { String s = "AAA"; String oldSubstring = "A"; String newSubstring = "aa"; String ss = Util.substituteString(s, oldSubstring, newSubstring, 2); Assert.assertEquals("substitute", "aaaaA", ss); } /** * Test method for * {@link org.xmlcml.euclid.Util#substituteStrings(java.lang.String, java.lang.String[], java.lang.String[])} * . */ @Test public final void testSubstituteStrings() { String s = "AAABBBCCCAAADDDSS"; String[] oldSubstring = new String[] { "AA", "CC", "D" }; String[] newSubstring = new String[] { "aa", "cc", "d" }; String ss = Util.substituteStrings(s, oldSubstring, newSubstring); Assert.assertEquals("substitute", "aaABBBccCaaAdddSS", ss); } /** * Test method for * {@link org.xmlcml.euclid.Util#substituteDOSbyAscii(java.lang.String)}. */ @Test public final void testSubstituteDOSbyAscii() { String ss = Util.substituteDOSbyAscii("" + (char) 161); Assert.assertEquals("char", 237, (int) ss.charAt(0)); } /** * Test method for * {@link org.xmlcml.euclid.Util#substituteEquals(java.lang.String)}. */ @Test public final void testSubstituteEquals() { String ss = Util.substituteEquals("=20"); Assert.assertEquals("equals", EuclidConstants.S_SPACE, ss); } /** * Test method for * {@link org.xmlcml.euclid.Util#getIntFromHex(java.lang.String)}. */ @Test public final void testGetIntFromHex() { Assert.assertEquals("hex", 2707, Util.getIntFromHex("A93")); } /** * Test method for * {@link org.xmlcml.euclid.Util#capitalise(java.lang.String)}. */ @Test public final void testCapitalise() { Assert.assertEquals("capital", "This is fred", Util .capitalise("this is fred")); } /** * Test method for * {@link org.xmlcml.euclid.Util#toCamelCase(java.lang.String)}. */ @Test public final void testToCamelCase() { Assert.assertEquals("capital", "thisIsFred", Util .toCamelCase("this is fred")); } /** * Test method for * {@link org.xmlcml.euclid.Util#readByteArray(java.lang.String)}. */ @Test public final void testReadByteArrayString() { // String filename; // byte[] bb = Util.readByteArray(filename); } /** * Test method for * {@link org.xmlcml.euclid.Util#readByteArray(java.io.DataInputStream)}. */ @Test public final void testReadByteArrayDataInputStream() { // not yet tested } /** * Test method for * {@link org.xmlcml.euclid.Util#stripISOControls(java.lang.String)}. */ @Test public final void testStripISOControls() { // not yet tested } /** * Test method for * {@link org.xmlcml.euclid.Util#normaliseWhitespace(java.lang.String)}. */ @Test public final void testNormaliseWhitespace() { Assert.assertEquals("capital", "this is fred", Util .normaliseWhitespace("this is fred")); } /** * Test method for {@link org.xmlcml.euclid.Util#stripNewlines(byte[])}. */ @Test public final void testStripNewlines() { Assert.assertEquals("capital", "this is fred", Util .normaliseWhitespace("this\nis\nfred")); } /** * Test method for * {@link org.xmlcml.euclid.Util#getFileOutputStream(java.lang.String)}. */ @Test public final void testGetFileOutputStream() { // not yet implemented } /** * Test method for * {@link org.xmlcml.euclid.Util#outputFloat(int, int, double)}. */ @Test public final void testOutputFloat() { // fail("Not yet implemented"); // TODO } /** * Test method for * {@link org.xmlcml.euclid.Util#outputNumber(int, int, double)}. */ @Test public final void testOutputNumber() { // fail("Not yet implemented"); // TODO } /** * Test method for * {@link org.xmlcml.euclid.Util#invert(java.util.Hashtable)}. */ @Test public final void testInvert() { // fail("Not yet implemented"); // TODO } /** * Test method for * {@link org.xmlcml.euclid.Util#concatenate(double[], java.lang.String)}. */ @Test public final void testConcatenateDoubleArrayString() { double[] ss = new double[] { 1.2, 3.4, 5.6 }; String s = Util.concatenate(ss, EuclidConstants.S_SPACE); Assert.assertEquals("Concat", "1.2 3.4 5.6", s); s = Util.concatenate(ss, EuclidConstants.S_COMMA); Assert.assertEquals("Concat", "1.2,3.4,5.6", s); } @Test public void testConcatenateInfinityAndBeyond() { double[] ss = new double[] { Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NaN }; Assert.assertEquals("Concat infinities according to XSD", "INF -INF NaN", Util.concatenate(ss, EuclidConstants.S_SPACE)); } /** * Test method for * {@link org.xmlcml.euclid.Util#concatenate(double[][], java.lang.String)}. */ @Test public final void testConcatenateDoubleArrayArrayString() { double[][] ss = new double[][] { new double[] { 1.2, 3.4, 5.6 }, new double[] { 1.1, 2.2, 3.3, 4.4 } }; String s = Util.concatenate(ss, EuclidConstants.S_SPACE); Assert.assertEquals("Concat", "1.2 3.4 5.6 1.1 2.2 3.3 4.4", s); s = Util.concatenate(ss, EuclidConstants.S_COMMA); Assert.assertEquals("Concat", "1.2,3.4,5.6,1.1,2.2,3.3,4.4", s); } /** * Test method for * {@link org.xmlcml.euclid.Util#splitToIntArray(java.lang.String, java.lang.String)} * . */ @Test public final void testSplitToIntArray() { int[] ii = Util.splitToIntArray("1 2 3 4", EuclidConstants.S_SPACE); String s = Int.testEquals((new int[] { 1, 2, 3, 4 }), ii); if (s != null) { Assert.fail("int split" + "; " + s); } ii = Util.splitToIntArray("1,2,3,4", EuclidConstants.S_COMMA); s = Int.testEquals((new int[] { 1, 2, 3, 4 }), ii); if (s != null) { Assert.fail("int split" + "; " + s); } } /** * Test method for * {@link org.xmlcml.euclid.Util#splitToDoubleArray(java.lang.String, java.lang.String)} * . */ @Test public final void testSplitToDoubleArray() { double[] dd = Util.splitToDoubleArray("1.1 2.2 3.3 4.4", EuclidConstants.S_SPACE); DoubleTestBase.assertEquals("double split", new double[] { 1.1, 2.2, 3.3, 4.4 }, dd, EPS); dd = Util.splitToDoubleArray("1.1,2.2,3.3,4.4", EuclidConstants.S_COMMA); DoubleTestBase.assertEquals("double split", new double[] { 1.1, 2.2, 3.3, 4.4 }, dd, EPS); } /** * Test method for * {@link org.xmlcml.euclid.Util#concatenate(int[], java.lang.String)}. */ @Test public final void testConcatenateIntArrayString() { int[] ii = new int[] { 1, 2, 3, 4 }; String s = Util.concatenate(ii, EuclidConstants.S_SPACE); Assert.assertEquals("int split", "1 2 3 4", s); ii = new int[] { 1, 2, 3, 4 }; s = Util.concatenate(ii, EuclidConstants.S_COMMA); Assert.assertEquals("int split", "1,2,3,4", s); } /** * Test method for * {@link org.xmlcml.euclid.Util#concatenate(java.lang.String[], java.lang.String)} * . */ @Test public final void testConcatenateStringArrayString() { String[] ii = new String[] { "a", "b", "c", "d" }; String s = Util.concatenate(ii, EuclidConstants.S_SPACE); Assert.assertEquals("int split", "a b c d", s); ii = new String[] { "a", "b", "c", "d" }; s = Util.concatenate(ii, EuclidConstants.S_COMMA); Assert.assertEquals("int split", "a,b,c,d", s); } /** * Test method for * {@link org.xmlcml.euclid.Util#containsString(java.lang.String[], java.lang.String)} * . */ @Test public final void testContainsString() { Assert.assertTrue("contains", Util.containsString(new String[] { "aa", "bb", "cc" }, "bb")); Assert.assertFalse("contains", Util.containsString(new String[] { "aa", "bb", "cc" }, "xx")); } /** * Test method for {@link org.xmlcml.euclid.Util#getPrime(int)}. */ @Test public final void testGetPrime() { int i = Util.getPrime(0); Assert.assertEquals("0", 2, i); i = Util.getPrime(1); Assert.assertEquals("1", 3, i); i = Util.getPrime(4); Assert.assertEquals("4", 11, i); i = Util.getPrime(10); Assert.assertEquals("10", 31, i); i = Util.getPrime(100); Assert.assertEquals("100", 547, i); i = Util.getPrime(1000); Assert.assertEquals("1000", 7927, i); i = Util.getPrime(100); Assert.assertEquals("100", 547, i); } @Test public final void testSortByEmbeddedInteger() { String[] ss = { "a123", "b213", "aa1", "ac9", "ax22", }; List ssList = new ArrayList(); for (String s : ss) { ssList.add(s); } Util.sortByEmbeddedInteger(ssList); Assert.assertEquals("0", "aa1", ssList.get(0)); Assert.assertEquals("1", "ac9", ssList.get(1)); Assert.assertEquals("2", "ax22", ssList.get(2)); Assert.assertEquals("3", "a123", ssList.get(3)); Assert.assertEquals("4", "b213", ssList.get(4)); } @Test @Ignore ("switch off if server is down") public void testHTTP() throws IOException { // will fail if server is down String s = "Isopropyl 3-(hydroxymethyl)pyridine-2-carboxylate"; String u = "http://opsin.ch.cam.ac.uk/opsin/"; s = URLEncoder.encode(s, "UTF-8"); String mediaType = "chemical/x-cml"; List lines = Util.getRESTQueryAsLines(s, u, mediaType); Assert.assertEquals("lines", 88, lines.size()); } } euclid-euclid-2.9/src/test/java/org/xmlcml/euclid/test/Vector2Test.java000066400000000000000000000051351461721410700261300ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid.test; import static org.xmlcml.euclid.EC.EPS; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.xmlcml.euclid.Angle; import org.xmlcml.euclid.EC; import org.xmlcml.euclid.Real2; import org.xmlcml.euclid.Vector2; /** * tests for Vector2. * * @author pmr * */ public class Vector2Test { Vector2 v0; Vector2 v1; /** * setup. * * @throws Exception */ @Before public void setUp() throws Exception { v0 = new Vector2(new Real2(3., 4.)); v1 = new Vector2(1., 2.); } /** * equality test. true if both args not null and equal within epsilon * * @param msg * message * @param test * @param expected * @param epsilon */ public static void assertEquals(String msg, Vector2 test, Vector2 expected, double epsilon) { Assert.assertNotNull("test should not be null (" + msg + EC.S_RBRAK, test); Assert.assertNotNull("expected should not be null (" + msg + EC.S_RBRAK, expected); DoubleTestBase.assertEquals(msg, test.getXY(), expected.getXY(), epsilon); } /** * Test method for 'org.xmlcml.euclid.Vector2.Vector2(Real2)' */ @Test public void testVector2Real2() { Assert.assertEquals("vector2 real2", 3., v0.getX(),EPS); Assert.assertEquals("vector2 real2", 4., v0.getY(),EPS); } /** * Test method for 'org.xmlcml.euclid.Vector2.Vector2(double, double)' */ @Test public void testVector2DoubleDouble() { Assert.assertEquals("vector2 real2", 1., v1.getX(),EPS); Assert.assertEquals("vector2 real2", 2., v1.getY(),EPS); } /** * Test method for 'org.xmlcml.euclid.Vector2.getAngleMadeWith(Vector2)' */ @Test public void testGetAngleMadeWith() { Vector2 v1 = new Vector2(Math.sqrt(3.) / 2., 1. / 2.); Vector2 v2 = new Vector2(1. / 2., Math.sqrt(3.) / 2.); Angle a = v1.getAngleMadeWith(v2); Assert.assertEquals("angle", -Math.PI / 6., a.getRadian(), EPS); a = v2.getAngleMadeWith(v1); Assert.assertEquals("angle", Math.PI / 6., a.getRadian(), EPS); } } euclid-euclid-2.9/src/test/java/org/xmlcml/euclid/test/Vector3Test.java000066400000000000000000000272071461721410700261350ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid.test; import static org.xmlcml.euclid.EC.EPS; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.xmlcml.euclid.Angle; import org.xmlcml.euclid.EuclidConstants; import org.xmlcml.euclid.EuclidRuntimeException; import org.xmlcml.euclid.Vector3; import org.xmlcml.euclid.Axis.Axis3; /** * tests for Vector3. * * @author pmr * */ public class Vector3Test extends GeomTest { /** * setup. * * @throws Exception */ @Before public void setUp() throws Exception { super.setUp(); } /** * equality test. true if both args not null and equal within epsilon * * @param msg * message * @param test * @param expected * @param epsilon */ public static void assertEquals(String msg, Vector3 test, Vector3 expected, double epsilon) { Assert.assertNotNull("test should not be null (" + msg + EuclidConstants.S_RBRAK, test); Assert.assertNotNull("expected should not be null (" + msg + EuclidConstants.S_RBRAK, expected); DoubleTestBase.assertEquals(msg, test.getArray(), expected.getArray(), epsilon); } /** * equality test. true if both args not null and equal within epsilon * * @param msg * message * @param test * array must be of length 3 * @param expected * @param epsilon */ public static void assertEquals(String msg, double[] test, Vector3 expected, double epsilon) { Assert.assertNotNull("test should not be null (" + msg + EuclidConstants.S_RBRAK, test); Assert.assertEquals("must be of length 3", 3, test.length); Assert.assertNotNull("expected should not be null (" + msg + EuclidConstants.S_RBRAK, expected); DoubleTestBase.assertEquals(msg, test, expected.getArray(), epsilon); } /** * Test method for 'org.xmlcml.euclid.Vector3.Vector3()' */ @Test public void testVector3() { Vector3Test .assertEquals("vector", new double[] { 0., 0., 0. }, v0, EPS); } /** * Test method for 'org.xmlcml.euclid.Vector3.Vector3(double, double, * double)' */ @Test public void testVector3DoubleDoubleDouble() { Vector3Test.assertEquals("vector", new double[] { 1., 2., 3. }, v123, EPS); } /** * Test method for 'org.xmlcml.euclid.Vector3.Vector3(double[])' */ @Test public void testVector3DoubleArray() { Vector3 v = new Vector3(new double[] { 4., 5., 6. }); Vector3Test.assertEquals("vector", new double[] { 4., 5., 6. }, v, EPS); } /** * Test method for 'org.xmlcml.euclid.Vector3.Vector3(Axis3)' */ @Test public void testVector3Axis3() { Vector3 v = new Vector3(Axis3.X); Vector3Test.assertEquals("vector", new double[] { 1., 0., 0. }, v, EPS); v = new Vector3(Axis3.Y); Vector3Test.assertEquals("vector", new double[] { 0., 1., 0. }, v, EPS); v = new Vector3(Axis3.Z); Vector3Test.assertEquals("vector", new double[] { 0., 0., 1. }, v, EPS); } /** * Test method for 'org.xmlcml.euclid.Vector3.Vector3(Vector3)' */ @Test public void testVector3Vector3() { Vector3 v = new Vector3(v123); Vector3Test.assertEquals("vector", new double[] { 1., 2., 3. }, v, EPS); } /** * Test method for 'org.xmlcml.euclid.Vector3.Vector3(RealArray)' */ @Test public void testVector3RealArray() { Vector3 v = new Vector3(new double[] { 1., 2., 3. }); Vector3Test.assertEquals("vector", new double[] { 1., 2., 3. }, v, EPS); } /** * Test method for 'org.xmlcml.euclid.Vector3.Vector3(Point3)' */ @Test public void testVector3Point3() { Vector3 v = new Vector3(p123); Vector3Test.assertEquals("vector", new double[] { 1., 2., 3. }, v, EPS); } /** * Test method for 'org.xmlcml.euclid.Vector3.getArray()' */ @Test public void testGetArray() { DoubleTestBase.assertEquals("array", new double[] { 1., 2., 3. }, v123 .getArray(), EPS); } /** * Test method for 'org.xmlcml.euclid.Vector3.isEqualTo(Vector3)' */ @Test public void testIsEqualTo() { Vector3 v = new Vector3(v123); Assert.assertTrue("isEqualTo", v.isEqualTo(v)); } /** * Test method for 'org.xmlcml.euclid.Vector3.longerThan(Vector3)' */ @Test public void testLongerThan() { Vector3 v = new Vector3(v123).plus(v100); Assert.assertTrue("isLongerThan", v.longerThan(v123)); } /** * Test method for 'org.xmlcml.euclid.Vector3.multiplyBy(double)' */ @Test public void testMultiplyBy() { Vector3 v = v123.multiplyBy(10.); Vector3Test.assertEquals("multiply", new double[] { 10., 20., 30. }, v, EPS); } /** * Test method for 'org.xmlcml.euclid.Vector3.multiplyEquals(double)' */ @Test public void testMultiplyEquals() { v123.multiplyEquals(10.); Vector3Test.assertEquals("multiply", new double[] { 10., 20., 30. }, v123, EPS); } /** * Test method for 'org.xmlcml.euclid.Vector3.plus(Vector3)' */ @Test public void testPlus() { Vector3 v = v123.plus(v100); Vector3Test.assertEquals("plus", new double[] { 2., 2., 3. }, v, EPS); } /** * Test method for 'org.xmlcml.euclid.Vector3.subtract(Vector3)' */ @Test public void testSubtract() { Vector3 v = v123.subtract(v100); Vector3Test.assertEquals("subtract", new double[] { 0., 2., 3. }, v, EPS); } /** * Test method for 'org.xmlcml.euclid.Vector3.negative()' */ @Test public void testNegative() { Vector3 v = v123.negative(); Vector3Test.assertEquals("negative", new double[] { -1., -2., -3. }, v, EPS); } /** * Test method for 'org.xmlcml.euclid.Vector3.elementAt(int)' */ @Test public void testElementAt() { double d = v123.elementAt(0); Assert.assertEquals("elementAt", 1., d, EPS); d = v123.elementAt(1); Assert.assertEquals("elementAt", 2., d, EPS); d = v123.elementAt(2); Assert.assertEquals("elementAt", 3., d, EPS); try { v123.elementAt(-1); } catch (EuclidRuntimeException e) { Assert.assertEquals("elementAt", "index (-1)out of range: 0/2", e .getMessage()); } try { v123.elementAt(3); } catch (EuclidRuntimeException e) { Assert.assertEquals("elementAt", "index (3)out of range: 0/2", e .getMessage()); } } /** * Test method for 'org.xmlcml.euclid.Vector3.setElementAt(int, double)' */ @Test public void testSetElementAt() { v123.setElementAt(0, 10.); Vector3Test.assertEquals("elementAt", new double[] { 10., 2., 3. }, v123, EPS); v123.setElementAt(1, 20.); Vector3Test.assertEquals("elementAt", new double[] { 10., 20., 3. }, v123, EPS); v123.setElementAt(2, 30.); Vector3Test.assertEquals("elementAt", new double[] { 10., 20., 30. }, v123, EPS); try { v123.elementAt(-1); } catch (EuclidRuntimeException e) { Assert.assertEquals("elementAt", "index (-1)out of range: 0/2", e .getMessage()); } try { v123.elementAt(3); } catch (EuclidRuntimeException e) { Assert.assertEquals("elementAt", "index (3)out of range: 0/2", e .getMessage()); } } /** * Test method for 'org.xmlcml.euclid.Vector3.isIdenticalTo(Vector3)' */ @Test public void testIsIdenticalTo() { Vector3 v = new Vector3(v123); Assert.assertTrue("identical to", v123.isIdenticalTo(v)); Assert.assertFalse("identical to", v123.isIdenticalTo(v100)); } /** * Test method for 'org.xmlcml.euclid.Vector3.isZero()' */ @Test public void testIsZero() { Assert.assertTrue("isZero", v000.isZero()); Assert.assertFalse("isZero", v123.isZero()); } /** * Test method for 'org.xmlcml.euclid.Vector3.transform(Transform3)' */ @Test public void testTransform() { Vector3 v = v123.transform(tr2); Vector3Test.assertEquals("transform", new double[] { 1., 2., 3. }, v123, EPS); Vector3Test.assertEquals("transform", new double[] { -1., -2., 3. }, v, EPS); } /** * Test method for 'org.xmlcml.euclid.Vector3.cross(Vector3)' */ @Test public void testCross() { Vector3 v = v100.cross(v010); Vector3Test.assertEquals("cross", new double[] { 0., 0., 1. }, v, EPS); v = v100.cross(v100); Vector3Test.assertEquals("cross", new double[] { 0., 0., 0. }, v, EPS); } /** * Test method for 'org.xmlcml.euclid.Vector3.normalize()' */ @Test public void testNormalise() { v123.normalize(); double d = Math.sqrt(14.); Vector3Test.assertEquals("normalise", new double[] { 1. / d, 2. / d, 3. / d }, v123, EPS); } /** * Test method for 'org.xmlcml.euclid.Vector3.round()' */ @Test public void testRound() { Vector3 v = new Vector3(0.8, -0.1, -1.2); v.round(); Vector3Test.assertEquals("round", new double[] { 1.0, 0.0, -1.0 }, v, EPS); } /** * Test method for 'org.xmlcml.euclid.Vector3.getUnitVector()' */ @Test public void testGetUnitVector() { Vector3 v = v123.getUnitVector(); double d = Math.sqrt(14.); Vector3Test.assertEquals("unit vector", new double[] { 1. / d, 2. / d, 3. / d }, v, EPS); } /** * Test method for 'org.xmlcml.euclid.Vector3.getLength()' */ @Test public void testGetLength() { Assert.assertEquals("length", Math.sqrt(14.), v123.getLength(), EPS); Assert.assertEquals("length", Math.sqrt(0.), v000.getLength(), EPS); } /** * Test method for 'org.xmlcml.euclid.Vector3.dot(Vector3)' */ @Test public void testDotVector3() { Assert.assertEquals("dot", 3., v001.dot(v123),EPS); Assert.assertEquals("dot", 0., v100.dot(v001),EPS); } /** * Test method for 'org.xmlcml.euclid.Vector3.getAngleMadeWith(Vector3)' */ @Test public void testGetAngleMadeWith() { Angle a = null; a = v001.getAngleMadeWith(v100); Assert.assertNotNull("angle", a); Assert.assertEquals("angle", Math.PI / 2., a.getRadian(), EPS); a = v001.getAngleMadeWith(v001); Assert.assertNotNull("angle", a); Assert.assertEquals("angle", 0., a.getRadian(), EPS); a = v001.getAngleMadeWith(v000); Assert.assertNull("angle zero length", a); } /** * Test method for * 'org.xmlcml.euclid.Vector3.getScalarTripleProduct(Vector3, Vector3)' */ @Test public void testGetScalarTripleProduct() { Assert.assertEquals("stp", 0., v001.getScalarTripleProduct(v001, v010), EPS); Assert.assertEquals("stp", -1., v001.getScalarTripleProduct(v010, v100), EPS); Assert.assertEquals("stp", 1., v001.getScalarTripleProduct(v100, v010), EPS); } /** * Test method for 'org.xmlcml.euclid.Vector3.projectOnto(Vector3)' */ @Test public void testProjectOnto() { Vector3 v = v123.projectOnto(v100); Vector3Test .assertEquals("project", new double[] { 1., 0., 0. }, v, EPS); v = v123.projectOnto(v001); Vector3Test .assertEquals("project", new double[] { 0., 0., 3. }, v, EPS); } /** * Test method for 'org.xmlcml.euclid.Vector3.isColinearVector(Vector3)' */ @Test public void testIsColinearVector() { Assert.assertTrue("colinear", v123.isColinearVector(v123)); } /** * Test method for 'org.xmlcml.euclid.Vector3.getNonColinearVector()' */ @Test public void testGetNonColinearVector() { Vector3 v = v123.getNonColinearVector(); Assert.assertFalse("colinear", v123.isColinearVector(v)); } /** * Test method for 'org.xmlcml.euclid.Vector3.getPerpendicularVector()' */ @Test public void testGetPerpendicularVector() { Vector3 v = v123.getPerpendicularVector(); Angle a = v.getAngleMadeWith(v123); Assert.assertNotNull("perpendicular vector", a); Assert.assertEquals("perpendicular", Math.PI / 2., a.getRadian(), EPS); } } euclid-euclid-2.9/src/test/java/org/xmlcml/euclid/testutil/000077500000000000000000000000001461721410700237735ustar00rootroot00000000000000euclid-euclid-2.9/src/test/java/org/xmlcml/euclid/testutil/Real2TestUtil.java000066400000000000000000000025501461721410700273030ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.euclid.testutil; import static org.xmlcml.euclid.EuclidConstants.S_RBRAK; import org.junit.Assert; import org.xmlcml.euclid.Real2; import org.xmlcml.euclid.test.DoubleTestBase; public class Real2TestUtil { /** * equality test. true if both args not null and equal within epsilon * * @param msg * message * @param test * @param expected * @param epsilon */ public static void assertEquals(String msg, Real2 test, Real2 expected, double epsilon) { Assert.assertNotNull("test should not be null (" + msg + S_RBRAK, test); Assert.assertNotNull("expected should not be null (" + msg + S_RBRAK, expected); DoubleTestBase.assertEquals(msg, test.getXY(), expected.getXY(), epsilon); } } euclid-euclid-2.9/src/test/java/org/xmlcml/files/000077500000000000000000000000001461721410700217535ustar00rootroot00000000000000euclid-euclid-2.9/src/test/java/org/xmlcml/files/QuickscrapeNormaTest.java-old000066400000000000000000000031341461721410700275020ustar00rootroot00000000000000package org.xmlcml.files; import java.io.File; import java.io.IOException; import java.util.List; import junit.framework.Assert; import org.apache.commons.io.FileUtils; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.junit.Test; import org.xmlcml.Fixtures; import org.xmlcml.args.DefaultArgProcessor; public class QuickscrapeNormaTest { private static final Logger LOG = Logger.getLogger(QuickscrapeNormaTest.class); static { LOG.setLevel(Level.DEBUG); } public final static File QUICKSCRAPE_NORMA_DIR = new File("src/test/resources/org/xmlcml/files/"); public final static File PLOS0115884_DIR = new File(QUICKSCRAPE_NORMA_DIR, "journal.pone.0115884"); @Test public void testReadQuickscrapeNorma() { QuickscrapeNorma quickscrapeNorma = new QuickscrapeNorma(); quickscrapeNorma.readDirectory(PLOS0115884_DIR); Assert.assertEquals("fileCount", 4, quickscrapeNorma.getReservedFileList().size()); Assert.assertTrue("XML", quickscrapeNorma.hasFulltextXML()); } @Test public void testQuickscrapeNorma() throws IOException { File container0115884 = new File("target/plosone/0115884/"); // copy so we don't write back into test area FileUtils.copyDirectory(Fixtures.TEST_PLOSONE_0115884_DIR, container0115884); String[] args = { "-q", container0115884.toString(), }; DefaultArgProcessor argProcessor = new DefaultArgProcessor(); argProcessor.parseArgs(args); QuickscrapeNormaList quickscrapeNormaList = argProcessor.getQuickscrapeNormaList(); Assert.assertEquals(1, quickscrapeNormaList.size()); LOG.trace(quickscrapeNormaList.get(0).toString()); } } euclid-euclid-2.9/src/test/java/org/xmlcml/stml/000077500000000000000000000000001461721410700216305ustar00rootroot00000000000000euclid-euclid-2.9/src/test/java/org/xmlcml/stml/STMLArrayTest.java000066400000000000000000000017751461721410700251230ustar00rootroot00000000000000/* Copyright 2024 Egon Willighagen * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.stml; import org.junit.Assert; import org.junit.Test; import nu.xom.Element; public class STMLArrayTest { @Test public void testDefaultConstructor() { STMLArray array = new STMLArray(); Assert.assertNotNull(array); } @Test public void testCopy() { STMLArray array = new STMLArray(); Element copy = array.copy(); Assert.assertTrue(copy instanceof STMLArray); } } euclid-euclid-2.9/src/test/java/org/xmlcml/stml/STMLAttributeTest.java000066400000000000000000000021561461721410700260020ustar00rootroot00000000000000/* Copyright 2024 Egon Willighagen * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.stml; import org.junit.Assert; import org.junit.Test; import nu.xom.Attribute; public class STMLAttributeTest { @Test public void testDefaultConstructor() { STMLAttribute attribute = new STMLAttribute("naam"); Assert.assertNotNull(attribute); Assert.assertEquals("naam", attribute.getLocalName()); } @Test public void testCopy() { STMLAttribute attribute = new STMLAttribute("naam"); Attribute copy = attribute.copy(); Assert.assertTrue(copy instanceof STMLAttribute); } } euclid-euclid-2.9/src/test/java/org/xmlcml/stml/STMLElementTest.java000066400000000000000000000015261461721410700254300ustar00rootroot00000000000000/* Copyright 2024 Egon Willighagen * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.stml; import org.junit.Assert; import org.junit.Test; public class STMLElementTest { @Test public void testDefaultConstructor() { STMLElement element = new STMLElement(); Assert.assertNotNull(element); } } euclid-euclid-2.9/src/test/java/org/xmlcml/stml/STMLScalarTest.java000066400000000000000000000015211461721410700252370ustar00rootroot00000000000000/* Copyright 2024 Egon Willighagen * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.stml; import org.junit.Assert; import org.junit.Test; public class STMLScalarTest { @Test public void testDefaultConstructor() { STMLScalar scalar = new STMLScalar(); Assert.assertNotNull(scalar); } } euclid-euclid-2.9/src/test/java/org/xmlcml/stml/STMLTypeTest.java000066400000000000000000000015371461721410700247620ustar00rootroot00000000000000/* Copyright 2024 Egon Willighagen * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.stml; import org.junit.Assert; import org.junit.Test; public class STMLTypeTest { @Test public void testDefaultConstructor() { STMLType type = new STMLType(); Assert.assertEquals(0, type.getSummary().length()); } } euclid-euclid-2.9/src/test/java/org/xmlcml/testutil/000077500000000000000000000000001461721410700225265ustar00rootroot00000000000000euclid-euclid-2.9/src/test/java/org/xmlcml/testutil/TestUtils.java000066400000000000000000001360461461721410700253430ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.testutil; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.StringWriter; import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.List; import junit.framework.AssertionFailedError; import junit.framework.ComparisonFailure; import nu.xom.Attribute; import nu.xom.Builder; import nu.xom.Comment; import nu.xom.Element; import nu.xom.Elements; import nu.xom.Node; import nu.xom.ProcessingInstruction; import nu.xom.Text; import nu.xom.tests.XOMTestCase; import org.apache.log4j.Logger; import org.joda.time.DateTime; import org.junit.Assert; import org.xmlcml.euclid.EC; import org.xmlcml.euclid.EuclidRuntimeException; import org.xmlcml.euclid.IntArray; import org.xmlcml.euclid.IntMatrix; import org.xmlcml.euclid.IntSet; import org.xmlcml.euclid.JodaDate; import org.xmlcml.euclid.Line3; import org.xmlcml.euclid.Plane3; import org.xmlcml.euclid.Point3; import org.xmlcml.euclid.Point3Vector; import org.xmlcml.euclid.Real; import org.xmlcml.euclid.Real2; import org.xmlcml.euclid.Real2Vector; import org.xmlcml.euclid.Real3Range; import org.xmlcml.euclid.RealArray; import org.xmlcml.euclid.RealMatrix; import org.xmlcml.euclid.RealRange; import org.xmlcml.euclid.RealSquareMatrix; import org.xmlcml.euclid.Transform2; import org.xmlcml.euclid.Transform3; import org.xmlcml.euclid.Util; import org.xmlcml.euclid.Vector2; import org.xmlcml.euclid.Vector3; import org.xmlcml.stml.STMLConstants; import org.xmlcml.stml.STMLElement; import org.xmlcml.xml.XMLUtil; /** * *

* Utility library of common methods for unit tests *

* * @author Peter Murray-Rust * @version 5.0 * */ public final class TestUtils implements STMLConstants { /** logger */ private final static Logger logger = Logger.getLogger(TestUtils.class); public static final String OUTPUT_DIR_NAME = "target/test-outputs"; /** * tests 2 XML objects for equality using canonical XML. uses * XOMTestCase.assertEquals. This treats different prefixes as different and * compares floats literally. * * @param message * @param refNode * first node * @param testNode * second node */ public static void assertEqualsCanonically(String message, Node refNode, Node testNode) { try { XOMTestCase.assertEquals(message, refNode, testNode); } catch (ComparisonFailure e) { reportXMLDiff(message, e.getMessage(), refNode, testNode); } catch (AssertionFailedError e) { reportXMLDiff(message, e.getMessage(), refNode, testNode); } } /** * compares two XML nodes and checks float near-equivalence (can also be * used for documents without floats) usesTestUtils.assertEqualsCanonically * and only uses PMR code if fails * * @param message * @param refNode * @param testNode * @param eps */ public static void assertEqualsIncludingFloat(String message, Node refNode, Node testNode, boolean stripWhite, double eps) { if (stripWhite && refNode instanceof Element && testNode instanceof Element) { refNode = stripWhite((Element) refNode); testNode = stripWhite((Element) testNode); } try { assertEqualsIncludingFloat(message, refNode, testNode, eps); } catch (AssertionError e) { logger.warn(e); reportXMLDiffInFull(message, e.getMessage(), refNode, testNode); } } public static void assertEqualsIncludingFloat(String message, String expectedS, Node testNode, boolean stripWhite, double eps) { assertEqualsIncludingFloat(message, TestUtils .parseValidString(expectedS), testNode, stripWhite, eps); } private static void assertEqualsIncludingFloat(String message, Node refNode, Node testNode, double eps) { try { Assert.assertEquals(message + ": classes", testNode.getClass(), refNode.getClass()); if (refNode instanceof Text) { testStringDoubleEquality(message + " on node: " + path(testNode), refNode.getValue().trim(), testNode .getValue().trim(), eps); } else if (refNode instanceof Comment) { Assert.assertEquals(message + " comment", refNode.getValue(), testNode.getValue()); } else if (refNode instanceof ProcessingInstruction) { Assert.assertEquals(message + " pi", (ProcessingInstruction) refNode, (ProcessingInstruction) testNode); } else if (refNode instanceof Element) { int refNodeChildCount = refNode.getChildCount(); int testNodeChildCount = testNode.getChildCount(); String path = path(testNode); // FIXME? fails to resolve in tests // Assert.assertEquals("number of children of " + path, // refNodeChildCount, testNodeChildCount); if (refNodeChildCount != testNodeChildCount) { Assert.fail("number of children of " + path + " "+ refNodeChildCount + " != " + testNodeChildCount); } for (int i = 0; i < refNodeChildCount; i++) { assertEqualsIncludingFloat(message, refNode.getChild(i), testNode.getChild(i), eps); } Element refElem = (Element) refNode; Element testElem = (Element) testNode; Assert.assertEquals(message + " name", refElem.getLocalName(), testElem.getLocalName()); Assert.assertEquals(message + " namespace", refElem .getNamespaceURI(), testElem.getNamespaceURI()); Assert.assertEquals(message + " attributes on " + refElem.getClass(), refElem.getAttributeCount(), testElem.getAttributeCount()); for (int i = 0; i < refElem.getAttributeCount(); i++) { Attribute refAtt = refElem.getAttribute(i); String attName = refAtt.getLocalName(); String attNamespace = refAtt.getNamespaceURI(); Attribute testAtt = testElem.getAttribute(attName, attNamespace); if (testAtt == null) { // XMLUtil.debug((Element)refNode, "XXXXXXXXXXX"); // XMLUtil.debug((Element)testNode, "TEST"); Assert.fail(message + " attribute on ref not on test: " + attName); } testStringDoubleEquality(message + " attribute " + path(testAtt) + " values differ:", refAtt .getValue(), testAtt.getValue(), eps); } } else { Assert.fail(message + "cannot deal with XMLNode: " + refNode.getClass()); } } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new RuntimeException(e); } } private static String path(Node testNode) { List fullpath = path(testNode, new ArrayList()); Collections.reverse(fullpath); StringBuilder sb = new StringBuilder(); for (String p : fullpath) { sb.append(p); } return sb.toString(); } private static List path(Node testNode, List path) { if (testNode instanceof Element) { Element e = (Element) testNode; StringBuilder frag = new StringBuilder("/"); if (!"".equals(e.getNamespacePrefix())) { frag.append(e.getNamespacePrefix()).append(":"); } path.add(frag.append(e.getLocalName()).append("[").append( siblingOrdinal(e)).append("]").toString()); } else if (testNode instanceof Attribute) { Attribute a = (Attribute) testNode; path.add(new StringBuilder("@").append(a.getNamespacePrefix()) .append(":").append(a.getLocalName()).toString()); } else if (testNode instanceof Text) { path.add("/text()"); } return (testNode.getParent() != null) ? path(testNode.getParent(), path) : path; } private static int siblingOrdinal(Element e) { Element parent = (Element) e.getParent(); if (parent == null) { return 0; } else { Elements els = parent.getChildElements(e.getLocalName(), e .getNamespaceURI()); for (int i = 0; i < els.size(); i++) { if (els.get(i).equals(e)) { return i; } } throw new RuntimeException( "Element was not a child of its parent. Most perplexing!"); } } private static void testStringDoubleEquality(String message, String refValue, String testValue, double eps) { testValue = testValue.trim(); refValue = refValue.trim(); // maybe if (testValue.endsWith(" ") || refValue.endsWith(" ")) { throw new RuntimeException("trim error"); } if (!testValue.equals(refValue)) { boolean fail = true; try { compareAsFloats(message, refValue, testValue, eps); fail = false; } catch (Exception e) { } if (fail) { try { compareAsFloatArrays(message, refValue, testValue, eps); fail = false; } catch (Exception e) { } } if (fail) { try { compareAsDates(message, refValue, testValue, eps); fail = false; } catch (Exception e) { } } if (fail) { Assert.fail("Cannot equate: "+refValue+" != "+testValue); } } else { Assert.assertEquals(message, refValue, testValue); } } private static void compareAsFloats(String message, String refValue, String testValue, double eps) { double testVal = Double.NaN; double refVal = Double.NaN; Error ee = null; try { try { testVal = Double.valueOf(testValue).doubleValue(); refVal = Double.valueOf(refValue).doubleValue(); Assert.assertEquals(message + " doubles ", refVal, testVal, eps); } catch (NumberFormatException e) { Assert.assertEquals(message + " String ", refValue, testValue); } } catch (ComparisonFailure e) { ee = e; } catch (AssertionError e) { ee = e; } if (ee != null) { throw new RuntimeException("["+testValue+"] != ["+refValue+"]" ,ee); } } /** I am still haveing problems with dates * if these ARE both dates assume they are equal (because of time zones * sorry) * @param message * @param refValue * @param testValue * @param eps */ private static void compareAsDates(String message, String refValue, String testValue, double eps) { DateTime testVal = null; DateTime refVal = null; try { testVal = JodaDate.parseDate(testValue); refVal = JodaDate.parseDate(refValue); // Assert.assertEquals(message + " date ", refVal, testVal); } catch (Exception e) { Assert.fail("unequal strings "+testValue+" != "+refValue); } } private static void compareAsFloatArrays(String message, String refValue, String testValue, double eps) { Error ee = null; try { try { RealArray testArray = new RealArray(testValue); RealArray refArray = new RealArray(refValue); assertEquals(message, testArray, refArray, eps); } catch (NumberFormatException e) { Assert.assertEquals(message + " String ", refValue, testValue); } } catch (ComparisonFailure e) { ee = e; } catch (AssertionError e) { ee = e; } if (ee != null) { throw new RuntimeException("["+testValue+"] != ["+refValue+"]" ,ee); } } private static Element stripWhite(Element refNode) { refNode = new Element(refNode); XMLUtil.removeWhitespaceNodes(refNode); return refNode; } public static void alwaysFail(String message) { Assert.fail("should always throw " + message); } // ====================== STML and Euclid =================== public static String testEquals(String message, double[] a, double[] b, double eps) { String msg = testEquals(a, b, eps); return (msg == null) ? null : message+"; "+msg; } // Real2 /** * returns a message if arrays differ. * * @param a array to compare * @param b array to compare * @param eps tolerance * @return null if arrays are equal else indicative message */ public static String testEquals(Real2 a, Real2 b, double eps) { String s = null; if (a == null) { s = "a is null"; } else if (b == null) { s = "b is null"; } else { if (!Real.isEqual(a.x, b.x, eps) || !Real.isEqual(a.y, b.y, eps)) { s = ""+a+" != "+b; } } return s; } // double arrays and related /** * Asserts equality of double arrays. * * checks for non-null, then equality of length, then individual elements * * @param message * @param a * expected array * @param b * actual array * @param eps * tolerance for agreement */ public static void assertEquals(String message, double[] a, double[] b, double eps) { String s = testEquals(a, b, eps); if (s != null) { Assert.fail(message + "; " + s); } } /** * equality test. true if both args not null and equal within epsilon * * @param msg * message * @param test * array must be of length 4 * @param expected * @param epsilon */ public static void assertEquals(String msg, double[] test, Plane3 expected, double epsilon) { Assert.assertNotNull("test should not be null (" + msg + STMLConstants.S_RBRAK, test); Assert.assertEquals("must be of length 4", 4, test.length); Assert.assertNotNull("ref should not be null (" + msg + STMLConstants.S_RBRAK, expected); TestUtils.assertEquals(msg, test, expected.getArray(), epsilon); } /** * equality test. true if both args not null and equal within epsilon * * @param msg * message * @param test * array must be of length 3 * @param expected * @param epsilon */ public static void assertEquals(String msg, double[] test, Point3 expected, double epsilon) { Assert.assertNotNull("test should not be null (" + msg + STMLConstants.S_RBRAK, test); Assert.assertEquals("must be of length 3", 3, test.length); Assert.assertNotNull("ref should not be null (" + msg + STMLConstants.S_RBRAK, expected); TestUtils.assertEquals(msg, test, expected.getArray(), epsilon); } /** * equality test. true if both args not null and equal within epsilon * * @param msg * message * @param test * array must be of length 3 * @param expected * @param epsilon */ public static void assertEquals(String msg, double[] test, Point3Vector expected, double epsilon) { Assert.assertNotNull("test should not be null (" + msg + STMLConstants.S_RBRAK, test); Assert.assertNotNull("expected should not be null (" + msg + STMLConstants.S_RBRAK, expected); Assert.assertEquals("must be of equal length ", test.length, expected .getArray().length); TestUtils.assertEquals(msg, test, expected.getArray(), epsilon); } /** * equality test. true if both args not null and equal within epsilon * * @param msg * message * @param test * array must be of length 2 * @param expected * @param epsilon */ public static void assertEquals(String msg, double[] test, Real2 expected, double epsilon) { Assert.assertNotNull("test should not be null (" + msg + STMLConstants.S_RBRAK, test); Assert.assertEquals("must be of length 2", 2, test.length); Assert.assertNotNull("ref should not be null (" + msg + STMLConstants.S_RBRAK, expected); TestUtils.assertEquals(msg, test, expected.getXY(), epsilon); } /** * equality test. true if both args not null and equal within epsilon * * @param msg * message * @param test * array must be of length 3 * @param expected * @param epsilon */ public static void assertEquals(String msg, double[] test, Real2Vector expected, double epsilon) { Assert.assertNotNull("test should not be null (" + msg + STMLConstants.S_RBRAK, test); Assert.assertNotNull("expected should not be null (" + msg + STMLConstants.S_RBRAK, expected); Assert.assertEquals("must be of equal length ", test.length, expected .getXY().getArray().length); TestUtils.assertEquals(msg, test, expected.getXY().getArray(), epsilon); } /** * equality test. true if both args not null and equal within epsilon * * @param msg * message * @param test * array must be of length 3 * @param expected * @param epsilon */ public static void assertEquals(String msg, double[] test, RealArray expected, double epsilon) { Assert.assertNotNull("test should not be null (" + msg + STMLConstants.S_RBRAK, test); Assert.assertNotNull("expected should not be null (" + msg + STMLConstants.S_RBRAK, expected); Assert.assertEquals("must be of equal length ", test.length, expected .getArray().length); } /** * equality test. true if both args not null and equal within epsilon * * @param msg * message * @param test * 16 values * @param expected * @param epsilon */ public static void assertEquals(String msg, double[] test, Transform2 expected, double epsilon) { Assert.assertNotNull("test should not be null (" + msg + STMLConstants.S_RBRAK, test); Assert.assertEquals("test should have 16 elements (" + msg + STMLConstants.S_RBRAK, 9, test.length); Assert.assertNotNull("ref should not be null (" + msg + STMLConstants.S_RBRAK, expected); TestUtils.assertEquals(msg, test, expected.getMatrixAsArray(), epsilon); } /** * equality test. true if both args not null and equal within epsilon * * @param msg * message * @param test * 16 values * @param expected * @param epsilon */ public static void assertEquals(String msg, double[] test, Transform3 expected, double epsilon) { Assert.assertNotNull("test should not be null (" + msg + STMLConstants.S_RBRAK, test); Assert.assertEquals("test should have 16 elements (" + msg + STMLConstants.S_RBRAK, 16, test.length); Assert.assertNotNull("ref should not be null (" + msg + STMLConstants.S_RBRAK, expected); TestUtils.assertEquals(msg, test, expected.getMatrixAsArray(), epsilon); } /** * equality test. true if both args not null and equal within epsilon * * @param msg * message * @param test * array must be of length 3 * @param expected * @param epsilon */ public static void assertEquals(String msg, double[] test, Vector3 expected, double epsilon) { Assert.assertNotNull("test should not be null (" + msg + STMLConstants.S_RBRAK, test); Assert.assertEquals("must be of length 3", 3, test.length); Assert.assertNotNull("expected should not be null (" + msg + STMLConstants.S_RBRAK, expected); TestUtils.assertEquals(msg, test, expected.getArray(), epsilon); } /** * equality test. true if both args not null and equal within epsilon * * @param msg * message * @param rows * @param test * @param expected * @param epsilon */ public static void assertEquals(String msg, int rows, double[] test, RealSquareMatrix expected, double epsilon) { Assert.assertNotNull("test should not be null (" + msg + STMLConstants.S_RBRAK, test); Assert.assertNotNull("ref should not be null (" + msg + STMLConstants.S_RBRAK, expected); Assert.assertEquals("rows should be equal (" + msg + STMLConstants.S_RBRAK, rows, expected.getRows()); TestUtils.assertEquals(msg, test, expected.getMatrixAsArray(), epsilon); } /** * equality test. true if both args not null and equal within epsilon * * @param msg * message * @param rows * @param cols * @param test * @param expected * @param epsilon */ public static void assertEquals(String msg, int rows, int cols, double[] test, RealMatrix expected, double epsilon) { Assert.assertNotNull("test should not be null (" + msg + STMLConstants.S_RBRAK, test); Assert.assertNotNull("ref should not be null (" + msg + STMLConstants.S_RBRAK, expected); Assert.assertEquals("rows should be equal (" + msg + STMLConstants.S_RBRAK, rows, expected.getRows()); Assert.assertEquals("columns should be equal (" + msg + STMLConstants.S_RBRAK, cols, expected.getCols()); TestUtils.assertEquals(msg, test, expected.getMatrixAsArray(), epsilon); } /** * equality test. true if both args not null and equal within epsilon * * @param msg * message * @param rows * @param cols * @param test * @param expected */ public static void assertEquals(String msg, int rows, int cols, int[] test, IntMatrix expected) { Assert.assertNotNull("test should not be null (" + msg + STMLConstants.S_RBRAK, test); Assert.assertNotNull("ref should not be null (" + msg + STMLConstants.S_RBRAK, expected); Assert.assertEquals("rows should be equal (" + msg + STMLConstants.S_RBRAK, rows, expected.getRows()); Assert.assertEquals("columns should be equal (" + msg + STMLConstants.S_RBRAK, cols, expected.getCols()); Assert.assertEquals(msg, test, expected.getMatrixAsArray()); } /** * Asserts equality of int arrays. * * checks for non-null, then equality of length, then individual elements * * @param message * @param a * expected array * @param b * actual array */ public static void assertEquals(String message, int[] a, int[] b) { String s = testEquals(a, b); if (s != null) { Assert.fail(message + "; " + s); } } /** * equality test. true if both args not null and equal * * @param msg * message * @param test * @param expected */ public static void assertEquals(String msg, int[] test, IntArray expected) { Assert.assertNotNull("test should not be null (" + msg + STMLConstants.S_RBRAK, test); Assert.assertNotNull("expected should not be null (" + msg + STMLConstants.S_RBRAK, expected); Assert.assertEquals("must be of equal length ", test.length, expected .getArray().length); Assert.assertEquals(msg, test, expected.getArray()); } /** * equality test. true if both args not null and equal * * @param msg * message * @param test * @param expected */ public static void assertEquals(String msg, int[] test, IntSet expected) { Assert.assertNotNull("test should not be null (" + msg + STMLConstants.S_RBRAK, test); Assert.assertNotNull("expected should not be null (" + msg + STMLConstants.S_RBRAK, expected); Assert.assertEquals("must be of equal length ", test.length, expected .getElements().length); Assert.assertEquals(msg, test, expected.getElements()); } /** * equality test. true if both args not null and equal * * @param msg * message * @param test * @param expected */ public static void assertEquals(String msg, IntArray test, IntArray expected) { Assert.assertNotNull("test should not be null (" + msg + STMLConstants.S_RBRAK, test); Assert.assertNotNull("expected should not be null (" + msg + STMLConstants.S_RBRAK, expected); Assert.assertEquals(msg, test.getArray(), expected.getArray()); } /** * equality test. true if both args not null and equal within epsilon and * rows are present and equals and columns are present and equals * * @param msg * message * @param test * @param expected */ public static void assertEquals(String msg, IntMatrix test, IntMatrix expected) { Assert.assertNotNull("test should not be null (" + msg + STMLConstants.S_RBRAK, test); Assert.assertNotNull("expected should not be null (" + msg + STMLConstants.S_RBRAK, expected); Assert.assertNotNull("expected should have columns (" + msg + STMLConstants.S_RBRAK, expected.getCols()); Assert.assertNotNull("expected should have rows (" + msg + STMLConstants.S_RBRAK, expected.getRows()); Assert.assertNotNull("test should have columns (" + msg + STMLConstants.S_RBRAK, test .getCols()); Assert.assertNotNull("test should have rows (" + msg + STMLConstants.S_RBRAK, test .getRows()); Assert.assertEquals("rows should be equal (" + msg + STMLConstants.S_RBRAK, test .getRows(), expected.getRows()); Assert.assertEquals("columns should be equal (" + msg + STMLConstants.S_RBRAK, test .getCols(), expected.getCols()); Assert.assertEquals(msg, test.getMatrixAsArray(), expected.getMatrixAsArray()); } /** * equality test. true if both args not null and equal * * @param msg * message * @param test * @param expected */ public static void assertEquals(String msg, IntSet test, IntSet expected) { Assert.assertNotNull("test should not be null (" + msg + STMLConstants.S_RBRAK, test); Assert.assertNotNull("expected should not be null (" + msg + STMLConstants.S_RBRAK, expected); Assert.assertEquals(msg, test.getElements(), expected.getElements()); } /** * equality test. true if both args not null and equal within epsilon * * @param msg * message * @param test * @param expected * @param epsilon */ public static void assertEquals(String msg, Line3 test, Line3 expected, double epsilon) { Assert.assertNotNull("test should not be null (" + msg + STMLConstants.S_RBRAK, test); Assert.assertNotNull("ref should not be null (" + msg + STMLConstants.S_RBRAK, expected); TestUtils.assertEquals(msg, test.getPoint(), expected.getPoint(), epsilon); TestUtils.assertEquals(msg, test.getVector(), expected.getVector(), epsilon); } /** * equality test. true if both args not null and equal within epsilon * * @param msg * message * @param test * @param expected * @param epsilon */ public static void assertEquals(String msg, Plane3 test, Plane3 expected, double epsilon) { Assert.assertNotNull("test should not be null (" + msg + STMLConstants.S_RBRAK, test); Assert.assertNotNull("ref should not be null (" + msg + STMLConstants.S_RBRAK, expected); TestUtils.assertEquals(msg, test.getArray(), expected.getArray(), epsilon); } /** * equality test. true if both args not null and equal within epsilon * * @param msg * message * @param test * @param expected * @param epsilon */ public static void assertEquals(String msg, Point3 test, Point3 expected, double epsilon) { Assert.assertNotNull("test should not be null (" + msg + STMLConstants.S_RBRAK, test); Assert.assertNotNull("ref should not be null (" + msg + STMLConstants.S_RBRAK, expected); TestUtils.assertEquals(msg, test.getArray(), expected.getArray(), epsilon); } /** * equality test. true if both args not null and equal within epsilon * * @param msg * message * @param testPoint * @param testVector * @param expected * @param epsilon */ public static void assertEquals(String msg, Point3 testPoint, Vector3 testVector, Line3 expected, double epsilon) { Assert.assertNotNull("testPoint should not be null (" + msg + STMLConstants.S_RBRAK, testPoint); Assert.assertNotNull("testVector should not be null (" + msg + STMLConstants.S_RBRAK, testVector); Assert.assertNotNull("expected should not be null (" + msg + STMLConstants.S_RBRAK, expected); TestUtils.assertEquals(msg, testPoint, expected.getPoint(), epsilon); TestUtils.assertEquals(msg, testVector, expected.getVector(), epsilon); } /** * equality test. true if both args not null and equal within epsilon * * @param msg * message * @param test * @param expected * @param epsilon */ public static void assertEquals(String msg, Point3Vector test, Point3Vector expected, double epsilon) { Assert.assertNotNull("test should not be null (" + msg + STMLConstants.S_RBRAK, test); Assert.assertNotNull("expected should not be null (" + msg + STMLConstants.S_RBRAK, expected); TestUtils.assertEquals(msg, test.getArray(), expected.getArray(), epsilon); } /** * equality test. true if both args not null and equal within epsilon * * @param msg * message * @param test * @param expected * @param epsilon */ public static void assertEquals(String msg, Real2 test, Real2 expected, double epsilon) { Assert.assertNotNull("test should not be null (" + msg + STMLConstants.S_RBRAK, test); Assert.assertNotNull("expected should not be null (" + msg + STMLConstants.S_RBRAK, expected); TestUtils.assertEquals(msg, test.getXY(), expected.getXY(), epsilon); } /** * equality test. true if both args not null and equal within epsilon * * @param msg * message * @param test * @param expected * @param epsilon */ public static void assertEquals(String msg, Real2Vector expected, Real2Vector test, double epsilon) { Assert.assertNotNull("test should not be null (" + msg + STMLConstants.S_RBRAK, test); Assert.assertNotNull("expected should not be null (" + msg + STMLConstants.S_RBRAK, expected); TestUtils.assertEquals(msg, expected.getXY().getArray(), test.getXY().getArray(), epsilon); } /** * test ranges for equality. * * @param msg * @param r3ref * @param r3 * @param epsilon */ public static void assertEquals(String msg, Real3Range r3ref, Real3Range r3, double epsilon) { TestUtils.assertEquals("xRange", r3.getXRange(), r3ref.getXRange(), epsilon); TestUtils.assertEquals("yRange", r3.getYRange(), r3ref.getYRange(), epsilon); TestUtils.assertEquals("zRange", r3.getZRange(), r3ref.getZRange(), epsilon); } /** * equality test. true if both args not null and equal within epsilon * * @param msg * message * @param test * @param expected * @param epsilon */ public static void assertEquals(String msg, RealArray test, RealArray expected, double epsilon) { Assert.assertNotNull("test should not be null (" + msg + STMLConstants.S_RBRAK, test); Assert.assertNotNull("expected should not be null (" + msg + STMLConstants.S_RBRAK, expected); TestUtils.assertEquals(msg, test.getArray(), expected.getArray(), epsilon); } /** * equality test. true if both args not null and equal within epsilon and * rows are present and equals and columns are present and equals * * @param msg * message * @param test * @param expected * @param epsilon */ public static void assertEquals(String msg, RealMatrix test, RealMatrix expected, double epsilon) { Assert.assertNotNull("test should not be null (" + msg + STMLConstants.S_RBRAK, test); Assert.assertNotNull("expected should not be null (" + msg + STMLConstants.S_RBRAK, expected); Assert.assertNotNull("expected should have columns (" + msg + STMLConstants.S_RBRAK, expected.getCols()); Assert.assertNotNull("expected should have rows (" + msg + STMLConstants.S_RBRAK, expected.getRows()); Assert.assertNotNull("test should have columns (" + msg + STMLConstants.S_RBRAK, test .getCols()); Assert.assertNotNull("test should have rows (" + msg + STMLConstants.S_RBRAK, test .getRows()); Assert.assertEquals("rows should be equal (" + msg + STMLConstants.S_RBRAK, test .getRows(), expected.getRows()); Assert.assertEquals("columns should be equal (" + msg + STMLConstants.S_RBRAK, test .getCols(), expected.getCols()); TestUtils.assertEquals(msg, test.getMatrixAsArray(), expected.getMatrixAsArray(), epsilon); } /** * tests equality of ranges. * * @param msg * message * @param ref * @param r * @param epsilon */ public static void assertEquals(String msg, RealRange ref, RealRange r, double epsilon) { Assert.assertEquals(msg + " min", r.getMin(), ref.getMin(), epsilon); Assert.assertEquals(msg + " max", r.getMax(), ref.getMax(), epsilon); } /** * equality test. true if both args not null and equal within epsilon and * rows are present and equals and columns are present and equals * * @param msg * message * @param test * @param expected * @param epsilon */ public static void assertEquals(String msg, RealSquareMatrix test, RealSquareMatrix expected, double epsilon) { Assert.assertNotNull("test should not be null (" + msg + STMLConstants.S_RBRAK, test); Assert.assertNotNull("expected should not be null (" + msg + STMLConstants.S_RBRAK, expected); Assert.assertNotNull("expected should have columns (" + msg + STMLConstants.S_RBRAK, expected.getCols()); Assert.assertNotNull("expected should have rows (" + msg + STMLConstants.S_RBRAK, expected.getRows()); Assert.assertNotNull("test should have columns (" + msg + STMLConstants.S_RBRAK, test .getCols()); Assert.assertNotNull("test should have rows (" + msg + STMLConstants.S_RBRAK, test .getRows()); Assert.assertEquals("rows should be equal (" + msg + STMLConstants.S_RBRAK, test .getRows(), expected.getRows()); Assert.assertEquals("columns should be equal (" + msg + STMLConstants.S_RBRAK, test .getCols(), expected.getCols()); TestUtils.assertEquals(msg, test.getMatrixAsArray(), expected.getMatrixAsArray(), epsilon); } /** * Asserts equality of String arrays. * * convenience method where test is a whitespace-separated set of tokens * * @param message * @param a * expected array as space concatenated * @param b * actual array may not include nulls */ public static void assertEquals(String message, String a, String[] b) { String[] aa = a.split(EC.S_SPACE); String s = testEquals(aa, b); if (s != null) { Assert.fail(message + "; " + s); } } /** * Asserts equality of String arrays. * * checks for non-null, then equality of length, then individual elements * equality if individual elements are equal or both elements are null * * @param message * @param a * expected array may include nulls * @param b * actual array may include nulls */ public static void assertEquals(String message, String[] a, String[] b) { String s = testEquals(a, b); if (s != null) { Assert.fail(message + "; " + s + "("+Util.concatenate(a, "~")+" != "+Util.concatenate(b, "~")); } } /** * equality test. true if both args not null and equal within epsilon * * @param msg * message * @param test * @param expected * @param epsilon */ public static void assertEquals(String msg, Transform2 test, Transform2 expected, double epsilon) { Assert.assertNotNull("test should not be null (" + msg + STMLConstants.S_RBRAK, test); Assert.assertNotNull("expected should not be null (" + msg + STMLConstants.S_RBRAK, expected); TestUtils.assertEquals(msg, test.getMatrixAsArray(), expected.getMatrixAsArray(), epsilon); } /** * equality test. true if both args not null and equal within epsilon * * @param msg * message * @param test * @param expected * @param epsilon */ public static void assertEquals(String msg, Transform3 test, Transform3 expected, double epsilon) { Assert.assertNotNull("test should not be null (" + msg + STMLConstants.S_RBRAK, test); Assert.assertNotNull("expected should not be null (" + msg + STMLConstants.S_RBRAK, expected); TestUtils.assertEquals(msg, test.getMatrixAsArray(), expected.getMatrixAsArray(), epsilon); } /** * equality test. true if both args not null and equal within epsilon * * @param msg * message * @param test * @param expected * @param epsilon */ public static void assertEquals(String msg, Vector2 test, Vector2 expected, double epsilon) { Assert.assertNotNull("test should not be null (" + msg + STMLConstants.S_RBRAK, test); Assert.assertNotNull("expected should not be null (" + msg + STMLConstants.S_RBRAK, expected); TestUtils.assertEquals(msg, test.getXY(), expected.getXY(), epsilon); } /** * equality test. true if both args not null and equal within epsilon * * @param msg * message * @param test * @param expected * @param epsilon */ public static void assertEquals(String msg, Vector3 test, Vector3 expected, double epsilon) { Assert.assertNotNull("test should not be null (" + msg + STMLConstants.S_RBRAK, test); Assert.assertNotNull("expected should not be null (" + msg + STMLConstants.S_RBRAK, expected); TestUtils.assertEquals(msg, test.getArray(), expected.getArray(), epsilon); } /** * tests 2 XML objects for equality using canonical XML. * * @param message * @param refNode * first node * @param testNode * second node * @param stripWhite * if true remove w/s nodes */ public static void assertEqualsCanonically(String message, Element refNode, Element testNode, boolean stripWhite) { assertEqualsCanonically(message, refNode, testNode, stripWhite, true); } /** * tests 2 XML objects for equality using canonical XML. * * @param message * @param refXMLString * @param testNode * second node * @param stripWhite * if true remove w/s nodes */ public static void assertEqualsCanonically(String message, String refXMLString, Element testNode, boolean stripWhite) { assertEqualsCanonically(message, TestUtils.parseValidString(refXMLString), testNode, stripWhite, true); } // // public static void assertEqualsCanonically(String message, STMLMap refNode, STMLMap testNode) { // Assert.assertEquals("from refs", new HashSet(refNode.getFromRefs()), new HashSet(testNode.getFromRefs())); // Assert.assertEquals("to refs", new HashSet(refNode.getToRefs()), new HashSet(testNode.getToRefs())); // for (String fromRef : refNode.getFromRefs()) { // String toRef = refNode.getToRef(fromRef); // Assert.assertEquals("from/to refs", toRef, testNode.getToRef(fromRef)); // } // } /** * tests 2 XML objects for equality using canonical XML. * * @param message * @param refNode * first node * @param testNode * second node * @param stripWhite * if true remove w/s nodes */ private static void assertEqualsCanonically(String message, Element refNode, Element testNode, boolean stripWhite, boolean reportError) throws Error { if (stripWhite) { refNode = stripWhite(refNode); testNode = stripWhite(testNode); } Error ee = null; try { XOMTestCase.assertEquals(message, refNode, testNode); } catch (ComparisonFailure e) { ee = e; } catch (AssertionFailedError e) { ee = e; } if (ee != null) { if (reportError) { reportXMLDiffInFull(message, ee.getMessage(), refNode, testNode); } else { throw (ee); } } } /** * compares two XML nodes and checks float near-equivalence (can also be * used for documents without floats) uses CMLXOMTestUtils.assertEqualsCanonically and only * uses PMR code if fails * * @param message * @param refNode * @param testNode * @param eps */ public static void assertEqualsIncludingFloat(String message, Node refNode, Node testNode, boolean stripWhite, double eps, boolean report) { if (stripWhite && refNode instanceof Element && testNode instanceof Element) { refNode = stripWhite((Element) refNode); testNode = stripWhite((Element) testNode); } try { assertEqualsIncludingFloat(message, refNode, testNode, eps); } catch (RuntimeException e) { if (report) { reportXMLDiffInFull(message, e.getMessage(), refNode, testNode); } } } /** * Asserts non equality of double arrays. * * checks for non-null, then equality of length, then individual elements * * @param message * @param a * expected array * @param b * actual array * @param eps * tolerance for agreement */ public static void assertNotEquals(String message, double[] a, double[] b, double eps) { String s = testEquals(a, b, eps); if (s == null) { Assert.fail(message + "; arrays are equal"); } } /** * Asserts non equality of double arrays. * * checks for non-null, then equality of length, then individual elements * * @param message * @param a * expected array * @param b * actual array */ public static void assertNotEquals(String message, int[] a, int[] b) { String s = testEquals(a, b); if (s == null) { Assert.fail(message + "; arrays are equal"); } } /** * Asserts non equality of String arrays. * * checks for non-null, then equality of length, then individual elements * * @param message * @param a * expected array * @param b * actual array */ public static void assertNotEquals(String message, String[] a, String[] b) { String s = testEquals(a, b); if (s == null) { Assert.fail(message + "; arrays are equal"); } } /** * tests 2 XML objects for non-equality using canonical XML. * * @param message * @param node1 * first node * @param node2 * second node */ public static void assertNotEqualsCanonically(String message, Node node1, Node node2) { try { Assert.assertEquals(message, node1, node2); String s1 = XMLUtil.getCanonicalString(node1); String s2 = XMLUtil.getCanonicalString(node2); Assert.fail(message + "nodes should be different " + s1 + " != " + s2); } catch (ComparisonFailure e) { } catch (AssertionFailedError e) { } } public static void assertObjectivelyEquals(String message, double[] a, double[] b, double eps) { String s = null; if (a == null) { s = "a is null"; } else if (b == null) { s = "b is null"; } else if (a.length != b.length) { s = "unequal arrays: " + a.length + STMLConstants.S_SLASH + b.length; } else { for (int i = 0; i < a.length; i++) { if (!(((Double) a[i]).equals(b[i]) || !Real.isEqual(a[i], b[i], eps))) { s = "unequal element at (" + i + "), " + a[i] + " != " + b[i]; break; } } } if (s != null) { Assert.fail(message + "; " + s); } } /** * test the writeHTML method of element. * * @param element * to test * @param expected * HTML string */ public static void assertWriteHTML(STMLElement element, String expected) { StringWriter sw = new StringWriter(); try { element.writeHTML(sw); sw.close(); } catch (IOException e) { Assert.fail("should not throw " + e); } String s = sw.toString(); Assert.assertEquals("HTML output ", expected, s); } /** * used by Assert routines. copied from Assert * * @param message * prepends if not null * @param expected * @param actual * @return message */ public static String getAssertFormat(String message, Object expected, Object actual) { String formatted = ""; if (message != null) { formatted = message + STMLConstants.S_SPACE; } return formatted + "expected:<" + expected + "> but was:<" + actual + ">"; } public static void neverFail(Exception e) { Assert.fail("should never throw " + e); } public static void neverThrow(Exception e) { throw new EuclidRuntimeException("should never throw " + e); } /** * convenience method to parse test file. uses resource * * @param filename * relative to classpath * @return root element */ public static Element parseValidFile(String filename) { Element root = null; try { URL url = Util.getResource(filename); root = new Builder().build(new File(url.toURI())) .getRootElement(); } catch (Exception e) { e.printStackTrace(); } return root; } /** * convenience method to parse test file. * @param file relative to classpath * @return root element */ public static Element parseValidFile(File file) { Element root = null; try { root = new Builder().build(new FileInputStream(file)).getRootElement(); } catch (Exception e) { throw new RuntimeException("BUG ", e); } return root; } /** * convenience method to parse test string. * * @param s * xml string (assumed valid) * @return root element */ public static Element parseValidString(String s) { Element element = null; if (s == null) { throw new RuntimeException("NULL VALID JAVA_STRING"); } try { element = XMLUtil.parseXML(s); } catch (Exception e) { e.printStackTrace(); System.err.println("ERROR " + e + e.getMessage() + "..." + s.substring(0, Math.min(100, s.length()))); Util.BUG(e); } return element; } static protected void reportXMLDiff(String message, String errorMessage, Node refNode, Node testNode) { Assert.fail(message + " ~ " + errorMessage); } static protected void reportXMLDiffInFull(String message, String errorMessage, Node refNode, Node testNode) { try { System.err.println("Error: "+errorMessage); System.err.println("==========XMLDIFF reference========="); XMLUtil.debug((Element) refNode, System.err, 2); System.err.println("------------test---------------------"); String s = testNode.toXML().replace("><", ">\n<"); System.err.println(s); System.err.println("==============" + message + "==================="); } catch (Exception e) { throw new RuntimeException(e); } Assert.fail(message + " ~ " + errorMessage); } /** * returns a message if arrays differ. * * @param a * array to compare * @param b * array to compare * @param eps * tolerance * @return null if arrays are equal else indicative message */ static String testEquals(double[] a, double[] b, double eps) { String s = null; if (a == null) { s = "a is null"; } else if (b == null) { s = "b is null"; } else if (a.length != b.length) { s = "unequal arrays: " + a.length + STMLConstants.S_SLASH + b.length; } else { for (int i = 0; i < a.length; i++) { if (!Real.isEqual(a[i], b[i], eps)) { s = "unequal element at (" + i + "), " + a[i] + " != " + b[i]; break; } } } return s; } /** * returns a message if arrays of arrays differ. * * @param a * array to compare * @param b * array to compare * @param eps * tolerance * @return null if array are equal else indicative message */ static String testEquals(double[][] a, double[][] b, double eps) { String s = null; if (a == null) { s = "a is null"; } else if (b == null) { s = "b is null"; } else if (a.length != b.length) { s = "unequal arrays: " + a.length + STMLConstants.S_SLASH + b.length; } else { for (int i = 0; i < a.length; i++) { if (a[i].length != b[i].length) { s = "row (" + i + ") has unequal lengths: " + a[i].length + STMLConstants.S_SLASH + b[i].length; break; } for (int j = 0; j < a[i].length; j++) { if (!Real.isEqual(a[i][j], b[i][j], eps)) { s = "unequal element at (" + i + ", " + j + "), (" + a[i][j] + " != " + b[i][j] + STMLConstants.S_RBRAK; break; } } } } return s; } /** * compare integer arrays. * * @param a * @param b * @return message or null */ public static String testEquals(int[] a, int[] b) { String s = null; if (a == null) { s = "a is null"; } else if (b == null) { s = "b is null"; } else if (a.length != b.length) { s = "unequal arrays: " + a.length + STMLConstants.S_SLASH + b.length; } else { for (int i = 0; i < a.length; i++) { if (a[i] != b[i]) { s = "unequal element (" + i + "), " + a[i] + " != " + b[i]; break; } } } return s; } /** * match arrays. error is a == null or b == null or a.length != b.length or * a[i] != b[i] nulls match * * @param a * @param b * @return message if errors else null */ public static String testEquals(String[] a, String[] b) { String s = null; if (a == null) { s = "a is null"; } else if (b == null) { s = "b is null"; } else if (a.length != b.length) { s = "unequal arrays: " + a.length + STMLConstants.S_SLASH + b.length; } else { for (int i = 0; i < a.length; i++) { if (a[i] == null && b[i] == null) { // both null, match } else if (a[i] == null || b[i] == null || !a[i].equals(b[i])) { s = "unequal element (" + i + "), expected: " + a[i] + " found: " + b[i]; break; } } } return s; } } euclid-euclid-2.9/src/test/java/org/xmlcml/xml/000077500000000000000000000000001461721410700214515ustar00rootroot00000000000000euclid-euclid-2.9/src/test/java/org/xmlcml/xml/XMLUtilTest.java000066400000000000000000000045361461721410700244620ustar00rootroot00000000000000/** * Copyright 2011 Peter Murray-Rust * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xmlcml.xml; import nu.xom.Element; import org.junit.Assert; import org.junit.Test; public class XMLUtilTest { static String ARTICLE = ""+ "\n"+ "\n"+ "
\n"+ "\n"+ "\n"+ "PLoS ONE\n"+ "\n"+ "\n"+ "
\n"+ ""; @Test public void testStripDTD() { Assert.assertEquals(467, ARTICLE.length()); String s = XMLUtil.stripDTD(ARTICLE); Assert.assertEquals(322, s.length()); Element element = XMLUtil.parseXML(s); Assert.assertEquals("elements ", 4, XMLUtil.getQueryElements(element, "//*").size()); } @Test public void testStripDTDAndParse() { Assert.assertEquals(467, ARTICLE.length()); Element root = XMLUtil.stripDTDAndParse(ARTICLE); Assert.assertEquals("elements ", 4, XMLUtil.getQueryElements(root, "//*").size()); } @Test public void testAddMissingEndTags() { String s = ""; s = XMLUtil.addMissingEndTags(s, "meta"); Assert.assertEquals("", s); } @Test public void testAddMissingEndTags1() { String s = ""; s = XMLUtil.addMissingEndTags(s, "meta"); Assert.assertEquals("", s); } @Test public void testAddMissingEndTags2() { String s = ""; s = XMLUtil.addMissingEndTags(s, "meta"); Assert.assertEquals("", s); } } euclid-euclid-2.9/src/test/resources/000077500000000000000000000000001461721410700176575ustar00rootroot00000000000000euclid-euclid-2.9/src/test/resources/org/000077500000000000000000000000001461721410700204465ustar00rootroot00000000000000euclid-euclid-2.9/src/test/resources/org/xmlcml/000077500000000000000000000000001461721410700217425ustar00rootroot00000000000000euclid-euclid-2.9/src/test/resources/org/xmlcml/euclid/000077500000000000000000000000001461721410700232075ustar00rootroot00000000000000euclid-euclid-2.9/src/test/resources/org/xmlcml/euclid/cml0.xml000066400000000000000000000015651461721410700245730ustar00rootroot00000000000000 euclid-euclid-2.9/src/test/resources/org/xmlcml/euclid/junk000066400000000000000000000000001461721410700240670ustar00rootroot00000000000000euclid-euclid-2.9/src/test/resources/org/xmlcml/euclid/temp/000077500000000000000000000000001461721410700241545ustar00rootroot00000000000000euclid-euclid-2.9/src/test/resources/org/xmlcml/euclid/temp/cml0.xsd000066400000000000000000000026161461721410700255340ustar00rootroot00000000000000 euclid-euclid-2.9/src/test/resources/org/xmlcml/euclid/temp/noSchema.xml000066400000000000000000000013171461721410700264350ustar00rootroot00000000000000 euclid-euclid-2.9/src/test/resources/org/xmlcml/files/000077500000000000000000000000001461721410700230445ustar00rootroot00000000000000euclid-euclid-2.9/src/test/resources/org/xmlcml/files/journal.pone.0115884/000077500000000000000000000000001461721410700263075ustar00rootroot00000000000000euclid-euclid-2.9/src/test/resources/org/xmlcml/files/journal.pone.0115884/fulltext.html000066400000000000000000003271371461721410700310610ustar00rootroot00000000000000 PLOS ONE: A Novel Reproductive Mode in Frogs: A New Species of Fanged Frog with Internal Fertilization and Birth of Tadpoles
Advertisement
Research Article

A Novel Reproductive Mode in Frogs: A New Species of Fanged Frog with Internal Fertilization and Birth of Tadpoles

  • Djoko T. Iskandar,

    Affiliation: School of Life Sciences and Technology, Institut Teknologi Bandung, Bandung, Indonesia

    X
  • Ben J. Evans,

    Affiliation: Center for Environmental Genomics, Department of Biology, McMaster University, Hamilton, Ontario, Canada

    X
  • Jimmy A. McGuire mail

    mcguirej@berkeley.edu

    Affiliation: Museum of Vertebrate Zoology and Department of Integrative Biology, University of California, Berkeley, California, United States of America

    X
  • Published: December 31, 2014
  • DOI: 10.1371/journal.pone.0115884

Abstract

We describe a new species of fanged frog (Limnonectes larvaepartus) that is unique among anurans in having both internal fertilization and birth of tadpoles. The new species is endemic to Sulawesi Island, Indonesia. This is the fourth valid species of Limnonectes described from Sulawesi despite that the radiation includes at least 15 species and possibly many more. Fewer than a dozen of the 6455 species of frogs in the world are known to have internal fertilization, and of these, all but the new species either deposit fertilized eggs or give birth to froglets.

Introduction

The Indonesian island of Sulawesi in central Indonesia is home to a largely unstudied radiation of fanged frogs in the genus Limnonectes Fitzinger, 1843 (Anura: Dicroglossidae Anderson, 1871). To date, only three species are recognized from Sulawesi, but at least 15 species occur on the island [1], [2], [3]. It was recently proposed that Sulawesi fanged frogs represent an adaptive radiation based on patterns of morphological diversification among co-distributed species, and correspondence of morphological features (body size, webbing) with habitat utilization [3]. Adult body size ranges from ~2 g to more than 900 g in members of the Sulawesi assemblage, with large-bodied species found in close association with fast moving rivers, and small-bodied species occupying more terrestrial niches in forest leaf-litter and on the banks of slow moving streams. Iskandar and Tjan [4] first documented that there are multiple undescribed species of Limnonectes on Sulawesi, and indicated that one of the constituent species utilizes an unusual ovoviviparous reproductive strategy. Here we describe that species, and provide additional details on its reproductive mode.

Anurans exhibit tremendous diversity in mode of reproduction. Not only have there been many independent origins of direct development, the condition in which a free-living tadpole stage is bypassed and froglets emerge from the egg capsules [5], but a bewildering array of mechanisms has evolved wherein parents care for their developing offspring. Examples of parental care include guarding of terrestrial eggs, transport of tadpoles on the back following hatching, carrying developing eggs in pouches or depressions on the back and flanks, males carrying developing tadpoles in the vocal sac, and even the now-extinct species of gastric brooding frogs in which the female carried her developing tadpoles in her stomach (see [6] for a summary). Despite this extreme reproductive diversity, internal fertilization has evolved only a few times among the 6455 known species of anurans [7], [8]. Only the African bufonid genera Nectophrynoides and Nimbaphrynoides and the now extinct Puerto Rican species, Eleutherodactylus jasperi, are documented to combine internal fertilization with egg retention and subsequent birth of froglets [9]. To this group, we can now add a species of Sulawesi fanged frog in the genus Limnonectes with internal fertilization and live birth of tadpoles, a reproductive mode that is unique among anurans.

Materials and Methods

We analyzed more than 100 specimens representing the new species from 23 localities on the island of Sulawesi. This study was conducted under an approved Institutional Animal Care and Use Committee protocol issued to JAM by the University of California at Berkeley (Protocol #R279). Fieldwork conducted in Indonesia was undertaken under research permits issued by LIPI and RISTEK, with specimen exportation authorized under permits or loans issued by LIPI, the Museum Zoologicum Bogoriense, and Pusat Studi Biologi. Specimens were captured by hand in the field, sacrificed via emersion in an aqueous solution of MS-222 (tricaine methane sulfonate) buffered to neutral pH, and prepared as formalin-fixed specimens deposited primarily in the Museum Zoologicum Bogoriense (the national museum of Indonesia) or Museum of Vertebrate Zoology at UC Berkeley. As part of a comprehensive study of Sulawesi Limnonectes diversification, we obtained morphometric data for the full Sulawesi assemblage. Sex was determined by inspection of gonads. The following measurements were made to the nearest 0.1 mm using digital calipers: snout-vent length (SVL) – from cloaca to tip of snout; head width (HW) – widest distance between posterior end of lower jaw; head length (HL) – from posterior end of lower jaw to tip of snout; femur length (FE) – from cloaca to distal end of femur; tibia length (TI) – from knee to distal end of tibia; foot length (FL) – from distal end of tibia-fibula to tip of 4th toe; inner metatarsal tubercle (IM); humerus length (UA) – from proximal end of upper arm to elbow; lower arm length (LA) – from elbow joint to base of middle palmar tubercle; hand length (HA) – from lower border of middle tubercle to distal tip of third finger; snout length (SL) – from bony border of eye socket to tip of snout; snout width at eye level (SWE); snout width at nostril level (SWN); eye diameter (EY) – outer diameter, measured from bones bordering eye; interorbital distance (IO) – distance between orbits ( = width of frontoparietal bones); eye-tympanum distance (ET); eye-narial distance (EN) – eye socket border to posterior border of nostril; Internarial distance (IN) – shortest distance between nostrils; nostril to tip of snout (NT); tympanum diameter (TY); odontoid process length (OP) –length of fang-like odontoid process of lower jaw; tibia diameter measured at its widest point (TD). Digital webbing formulae from [10].

Nomenclatural acts

The electronic edition of this article conforms to the requirements of the amended International Code of Zoological Nomenclature, and hence the new names contained herein are available under that Code from the electronic edition of this article. This published work and the nomenclatural acts it contains have been registered in ZooBank, the online registration system for the ICZN. The ZooBank LSID (Life Science Identifier) can be resolved and the associated information viewed through any standard web browser by appending the LSID to the prefix “http://zoobank.org/”. The LSID for this publication is: urn:lsid:zoobank.org:pub: A758E03A–C054–4193–9768–D0C4AD588AEA. The electronic edition of this work was published in a journal with an ISSN, and has been archived and is available from the following digital repositories: PubMed Central, LOCKSS.

Results

Taxonomy

Limnonectes larvaepartus new species (Figs. 1, 2)

thumbnail

Figure 1. Images of the holotype of Limnonectes larvaepartus (MZB.Amph.23755) in (a) lateral, (b) dorsal, and (c) ventral view.

Ventral views of the right foot (d) and right hand (e) are also presented.

doi:10.1371/journal.pone.0115884.g001
thumbnail

Figure 2. Images of Limnonectes larvaepartus.

(a) MVZ 268323 (male, left) and MVZ 268307 (female, right) collected from Desa Uaemate along the Tasio-Tibo Road, Kabupatan Mamuju, Provinsi Sulawesi Barat, Sulawesi Island (02.61287S, 119.14238 E, 89 m elev.); (b) Limnonectes larvaepartus female (MVZ 268426) with tadpoles removed from the oviduct. Note the large yolk reserves available to the tadpoles; (c) An in situ adult male L. larvaepartus (JAM 14234) observed calling while perched on the edge of a small pool 2 m away from a 2 m wide stream; several L. larvaepartus tadpoles were present in the pool including the two visible within the yellow circle; (d) dorsal and ventral views of ~stage 25 L. larvaepartus tadpoles (JAM 14271) released by a pregnant female (JAM 14237) at the moment of capture.

doi:10.1371/journal.pone.0115884.g002

Urn:lsid:zoobank.org:act: 60AA7136-89A0-4DBB-9FBC-BD0FAF8A214C

This species has been referred to in the literature under the names Limnonectes larviparus [11] and Limnonectes “ovovivipar” [4], both of which created nomina nuda. This species also corresponds to Limnonectes sp. V in [2], [3].

Etymology

The species name larvaepartus (from ‘larvae’, plural of larva, the early form of an animal, and ‘partus’, to give birth to) reflects the unique reproductive mode of this tadpole-bearing species.

Holotype

MZB.Amph.23755 (Field Number: BSI 0605, see Fig. 1), an adult male, collected from Dunu Village, (0.92353oN; 122.64386oE) at 189 m elevation, Kecamatan Anggrek, Kabupaten Gorontalo, Provinsi Gorontalo, Sulawesi, Indonesia by J.A. McGuire & team, 18 October 2004.

Paratypes

Paratypes (n = 30) are from Sulawesi Utara, Gorontalo, Sulawesi Tengah, and Sulawesi Barat Provinces: MZB 2834, a gravid female with 33 translucent tadpoles (Gosner stage 23) from the left oviduct and 2 from outside the body, from Toraut (00′33.72oN, 123′54.23oE), Bogani Nani Wartabone National Park, Kabupaten Bolaang Mongondow, Sulawesi Utara at 370 m elevation, by D. T. Iskandar, 15 August 1991; FMNH 252453, a female, from Toraut, Bogani Nani Wartabone National Park, Kabupaten Bolaang Mongondow, Sulawesi Utara by D. T. Iskandar, August 1991. MVZ 255545, 256009-11, 256013 from Desa Lombongo (−1.43346, 120.30800), Kecamatan Suwawa, Kabupaten Bone Bolango, Bogani Nani Warta Bone National Park, Provinsi Gorontalo at 75 m elevation by J. A McGuire and team, 20 October 2004. ZRC 1.3258 (left femur removed) from Toraut, Bogani Nani Wartabone National Park, Kabupaten Bolaang Mongondow, Sulawesi Utara at 370 m elevation, by D. T. Iskandar, 15 August 1991. MZB 2835–2841 from Toraut, Bogani Nani Wartabone National Park, Kabupaten Bolaang Mongondow, Sulawesi Utara at 370 m elevation, by D. T. Iskandar, 15 August 1991 & 12 July 1992. MVZ 255548–49 from Desa Pontak (−2.62910, 118.99300), Kecamatan Motoling, Kabupaten Minahasa Selatan, Provinsi Sulawesi Utara at 285 m elevation by J. A. McGuire and team, 13 October 2004. MZB 3117, near Potolok river, Lolak, Bogani Nani Wartabone National Park at 350 m elevation, Kabupaten Bolaang Mongondow, Sulawesi Utara; MZB 3118, male from seashore forest near Bungbungan River, Lolak, Bogani Nani Wartabone National Park, Kabupaten Bolaang Mongondow, Sulawesi Utara. MZB 3120 from Tangkorak River, Desa Pindol, Kecamatan Lolak, Bolaang Mongondow, Sulawesi Utara, by Mumpuni, 26 June 1995; MZB 3121 from Tangaga Forest, Dudepo, Bolaang Mongondow, Sulawesi Utara, by I. Maryanto, 20 October 1995; MZB 3122 from Potolok River, Bogani Nani National Park, Lolak, Sulawesi Utara by Mumpuni, 19 June 1995; MZB 3124 from Bungbungan River, Bogani Nani National Park, Lolak, Sulawesi Utara by Mumpuni, 19 June 1995; MZB Amph.8108 from Toraut, near Bogani Nani Wartabone National Park, Sulawesi Utara. LSUMZ 84209, 84214, 84221, 84224 from Desa Torout, Bogani Nani Wartabone National Park, Kabupaten Bolaang Mongondow, Provinsi Sulawesi Utara at 267 m elevation by J. A. McGuire on 6 and 11 September 2001.

Other referred specimens

LSUMZ 84218, 84219 from Desa Torout, Bogani Nani Wartabone National Park, Kabupaten Bolaang Mongondow, Provinsi Sulawesi Utara by J.A. McGuire. FMNH 130991, 106930 from Buang-buang Island, Sulawesi Utara. MZB 3119, juvenile from Seashore forest near Bungbungan River, Lolak, Bogani Nani Wartabone National Park, Kabupaten Bolaang Mongondow, Provinsi Sulawesi Utara. AMNH 167199 from Tangkoko National Park (1.570083oN, 125.156933oE), Kabupaten Minahasa, Provinsi Sulawesi Utara. MVZ 255546 from Desa Salumpaku (−1.60757, 119.29900), Kecamatan Banawa, Kabupaten Donggala, Provinsi Sulawesi Tengah. MVZ 255547 from Desa Kelapa Dua (−1.60757, 119.29900), Kecamatan Anreapi, Kabupaten Polewali Mandar, Provinsi Sulawesi Barat. MVZ 268426, 268428–30, 268432 from Polewali-Masawa Road (River 1) (−2.65490, 118.93300), Kecamatan Polewali, Kabupaten Polewali Mandar, Provinsi Sulawesi Barat. MVZ 268309–10, 268313, 268317–19, 268322, 268325, MZB.Amph. 20675, 20677–80 from Desa Uaemate (Tasiu-Tibo Road; S02.61550, E119.14417), Kecamatan Kaluku, Kabupaten Mamuju, Provinsi Sulawesi Barat. MZB.Amph.20663 from Desa Kabiraan (−2.62460, 119.14700), Kecamatan Ulumunda, Kabupaten Majene, Provinsi Sulawesi Barat.

Distribution

Limnonectes larvaepartus occurs across the Northern Peninsula, as well as on the western margin of Sulawesi's Central Core (Fig. 3). We do not know the full extent of the species' range in the Central Core because the central highlands of Sulawesi remain poorly explored herpetologically. Several genera have species range boundaries in this same general region (e.g. the flying lizards Draco spilonotus and D. walker [12], the fanged frogs Limnonectes sp. I and L. sp. D [3], and the tarsiers Tarsius lariang and T. dentatus [13].

thumbnail

Figure 3. Distribution map (left panel) depicting the range of Limnonectes larvaepartus.

The right panel shows the phylogenetic position of L. larvaepartus, with different symbol shapes denoting regional genetic structure in the species.

doi:10.1371/journal.pone.0115884.g003

Diagnosis

Prior workers have recognized substantial species diversity in the genus Limnonectes on Sulawesi. However, diagnosing many of these lineages on the basis of morphology is challenging, and several authors have instead opted to apply names to Sulawesi specimens representing species from outside Sulawesi. Consequently, the following names have all been incorrectly applied to Sulawesi populations: the Lesser Sundas species (type locality: Flores Island) L. dammermani (Mertens, 1927), the Bornean species L. finchii (Inger, 1966), the Mollucan species (type locality: Ambon) L. grunniens (Daudin, 1801), and the Philippine taxa L. leytensis (Boettger, 1893), L. magnus (Stejneger, 1909), and L. palavanensis (Boulenger, 1894). These names should not be applied to Sulawesi populations, as was verified phylogenetically for several of these taxa [3]. Only four Sulawesi species have been described: L. arathooni (Smith, 1927), L. heinrichi (Ahl, 1933), L. modestus (Boulenger, 1882), and L. microtympanum (van Kampen, 1909). However, we will show elsewhere that L. heinrichi is a junior synonym of L. modestus and the species complex that we have referred to previously [2], [3] as L. modestus remains undescribed – thus, at present there are but three valid described species of Sulawesi Limnonectes.

Limnonectes larvaepartus can be distinguished from all other described species of Limnonectes by its reproductive mode (Fig. 2). It can be further differentiated from all described Sulawesi species by its combination of body size (mean male SVL = 37.4; female SVL = 40.2 mm), coloration, tympanum size, and degree of hind foot webbing, as well as on the basis of phylogenetic placement (Fig. 3). Limnonectes arathooni is endemic to Sulawesi's Southwestern (SW) Peninsula south of the Tempe Depression, and thus does not occur within the range of L. larvaepartus. It is similar in size to the new species (male SVL = 36.6 mm, females = 39.6 mm), but differs in having substantially reduced webbing (extending to penultimate phalange of fourth toe vs. to toe disc), in lacking fine granular dorsal tubercles, and in having melanic spots above the forelimb insertion, a fine ridge extending posteriorly behind each eye, and an alternative derived reproductive mode in which males guard clutches of terrestrial eggs that hatch into tadpoles that then make their own way to an adjacent stream by sliding down steep stream-side embankments [14]. Limnonectes microtympanum, like L. arathooni, is restricted to the SW Peninsula south of the Tempe Depression, and thus does not overlap in geographical range with L. larvaepartus. Limnonectes microtympanum is moderately large (male SVL = 78.4; female SVL = 72.4 mm), and thus much larger than the new species. Limnonectes microtympanum also differs from the new species in having proportionally smaller tympana (TY/SVL = 0.05+0.01 in males, 0.06+0.01 in females versus 0.08+0.01 in both sexes in L. larvaepartus). The new species occurs in broad sympatry with L. modestus, which is a moderate sized (male SVL = 70.2 mm; female SVL = 64.0 mm) inhabitant of fast-moving streams and substantially larger than L. larvaepartus. Like L. larvaepartus, L. modestus has nearly complete hindfoot webbing (slightly more extensive in L. modestus than in L. larvaepartus but reaching the toe disc in both species), a dusky throat with melanic pigments extending onto the pectoral region (in a clear wedge shape in L. modestus, more randomly distributed in L. larvaepartus), and skin with extensive fine granular tubercles. Limnonectes modestus also exhibits a derived reproductive mode involving production of a relatively small number of large (10 mm diameter) eggs that are deposited along the edge of fast moving streams.

Description of the holotype

An adult male (Fig. 1) 48 mm SVL, body moderately robust, head not broader than body, head about 65% longer than wide, length 45% of snout-vent length, snout 17% of snout-vent length, moderately pointed, projecting above lower jaw, nostril lateral, closer to tip of snout than to eye, lore essentially straight, canthus rostralis distinct, eye about equal to snout length, pupil diamond-shaped, upper eyelid with tubercles; interorbital region smooth, width 69% of internarial distance, tympanum moderate, slightly wider than interorbital distance, supratympanic fold distinct, extending from posterior corner of eye to supra-axillary region, in contact with tympanic annulus, temporal muscle slightly enlarged; odontoid process 2.1 mm. Dentigerous process of vomer distinct, angled anterolaterally, approximately at 45o angle, posterior ends separated by distance approximately equal to one-third diameter of choanae. Limbs relatively slender, tibia width at thickest part 7.5 mm; femur length 52% of snout-vent length, heels moderately overlapping when placed perpendicular to body axis; tibia length 64.9% of foot length, 48.9% of snout-vent length; foot length 71% of snout-vent length; tarsal fold indistinct, only evident as a ridge; toe discs moderately expanded, circum-marginal groove horseshoe-shaped, pointed anteriorly. Plantar surface of foot smooth, subarticular tubercle rounded, relative length of toes 4>3>5>2>1. Inner metatarsal tubercle prominent, elongate, ovoid with a sharp spade like ventral edge; outer metatarsal tubercle absent; hind foot webbing full, extending to toe discs, slightly emarginated. Manus length 51.3% of foot length, fingers slender, terminal discs slightly expanded, length formula 3>1≥4≥2, with slight differences in length, subarticular tubercle rounded, convex; supernumerary tubercles absent; inner and outer metacarpal tubercles enlarged, nuptial pads and webbing absent, forearm muscle not enlarged. See Table 1 for measurements and variation.

thumbnail

Table 1. Summary of univariate morphological variation among mensural characters in Limnonectes larvaepartus.

doi:10.1371/journal.pone.0115884.t001

Coloration

The dorsal coloration is highly variable, typically brownish-grey, but can be darker brown on the dorsolateral region, and some individuals are reddish-brown or golden-tan (see Fig. 2). ~23% of specimens have a bold mid-dorsal stripe. The venter is either yellowish or cream colored, with the upper end of the tibia usually bearing a prominent dark spot. A light bar is often present in the interorbital region, and the coloration of the snout to interorbital region may be lighter than the remainder of the dorsum. The tympanum is often masked in black leaving only the lower rim sharing the predominant body coloration. The gular region is usually darker in males and may have a finely mottled wedge-shaped melanic patch. The dorsal half of the iris is golden-orange in coloration in at least some individuals (we do not know of exceptions, but have not documented iris coloration for most specimens).

Eggs and tadpoles

Females produce ~100 non-pigmented eggs (see [15]), though the most we have observed is 55, which possibly represents the contents of just one oviduct. The eggs lack a jelly-coat and reach at least ~3 mm in diameter. These eggs develop within the oviducts into pigmented tadpoles that reach at least Gosner stage 35 prior to parturition (see [16] for staging). The gut is initially provisioned with substantial yolk (Fig. 2b), and developing tadpoles prematurely removed from the oviducts at approximately stage 21 progressed to approximately stage 25 over the course of two weeks in a water bottle without supplemental food, suggesting plasticity in terms of the timing of parturition. A detailed description of the tadpole is provided in the accompanying paper by Kusrini et al. [15].

Natural history

Limnonectes larvaepartus occurs in natural and disturbed forest habitats of Sulawesi, generally in sympatry with at least one, and sometimes as many as five other Limnonectes species. In the western Central Core of Sulawesi, we have always found L. larvaepartus living in sympatry with a much larger species that has been referred to in the literature as L. sp. D (2,3). Whereas L. sp. D is generally found on rocks in fast-moving streams or within a meter of water on the banks of fast-flowing streams, L. larvaepartus is usually found further from the stream (2–10 meters from water) on rocky substrates, in leaf-litter, or secluded in grassy vegetation. Because we have observed that L. sp. D predates other frogs, including other Limonectes, it is possible that L. larvaepartus avoids large streams in response to predation pressure from larger Limnonectes species. Male L. larvaepartus typically call from the margins of seeps, puddles, or small pools away from the main stream. Notably, we have found many males calling from small pools that were already inhabited by L. larvaepartus tadpoles (Fig. 2c), with as many as three size-classes of tadpoles represented. It is unclear whether some or all of the observed tadpoles were sired by the accompanying male. We have furthermore collected at least one pregnant female from a small stream-side puddle already inhabited by tadpoles of two size classes, again suggesting the possibility that individual pools may be visited repeatedly by the same adult males and females during the reproductive season.

Discussion

We first became aware of the unusual reproductive mode of Limnonectes larvaepartus while conducting fieldwork on Sulawesi. In several instances, we discovered tadpoles in the oviducts while preparing specimens. In each case, having sacrificed frogs for preparation, the abdominal wall was observed to quiver, and incision resulted in living tadpoles emerging from the opening (see S1 Movie). On one occasion, a gravid female gave birth to tadpoles in-hand at the moment of capture. On four other occasions, field-collected L. larvaepartus in our possession gave birth to tadpoles while being held individually in collecting bags. In total, we have either observed tadpoles in the oviducts or direct-birth of tadpoles on 19 occasions.

Because we have not witnessed natural birth of tadpoles in free-living frogs, two possible alternative reproductive modes are possible for this species. Limnonectes larvaepartus reproduction may simply reflect what we have observed in the hand – direct birth of tadpoles. Alternatively, this species may be capable of retaining developing young in the oviducts through metamorphosis with subsequent birth of froglets, as is the case for Eleutherodactylus jasperi and members of the African bufonid genera Nectophrynoides and Nimbaphrynoides [9], [17], [18]. The latter mode seems unlikely for several reasons. First, we have collected 19 pregnant individuals carrying tadpoles in the oviducts but none carrying froglets. These 19 females were collected across different months, years, and localities. Second, we have observed and collected at least four clutches of free-living L. larvaepartus tadpoles in small pools of water on the margins of streams, with each of these samples exhibiting two or three size classes and thus possibly representing multiple clutches. Three of these clutches were accompanied by males, some of which were calling, and one was found in association with the gravid female that gave birth to 55 tadpoles (some in the hand of JAM, others subsequently deposited in a collecting bag). Finally, although adaptive plasticity has been demonstrated for many frog species, whereby tadpoles or froglets are capable of hatching from their eggs prematurely when attacked by predators [19], this has only been documented for a single direct-developer, Eleutherodactylus coqui [20]. In the case of E. coqui, the capacity for early hatching commenced at stages 13 or 14, whereas normal hatching occurred at stage 15 (see [21] for staging). Thus, E. coqui were capable of premature hatching only as froglets, with toe pads and eyelids present but before the tail was completely resorbed, a stage much later than would be required by L. larvaepartus if it were simultaneously capable of hatching as either a tadpole or full-term froglet. Most direct-developers, as well as the few frog species that give birth to froglets, pass through the tadpole stage in a form poorly suited for free-living. In many such species, the tadpole has a broad, highly vascularized ‘respiratory tail’ specialized for intra-egg or intra-oviductal gas exchange rather than for swimming, as is the case for Eleutherodactylus jasperi [17], and may lack mouthparts and functional gills [22], [23], [24]. In others, the tail remains rudimentary and limb buds appear early in development such that a typical tadpole phenotype never occurs [22], [23]. Given these observations, it appears unlikely that any one species would be characterized by the combination of (1) internal fertilization, (2) complete metamorphosis within the oviducts and live-birth in the form of froglets, as well as (3) an oviductal tadpole stage that is capable of premature birth and free-living. Although we think that L. larvaepartus is much more likely to give birth to tadpoles as its sole mode of reproduction as opposed to exhibiting adaptive plasticity allowing for birth of either tadpoles or froglets depending on the circumstances confronting the frog, we note that either condition would be unique among Anura. In either case, L. larvaepartus requires internal fertilization, which is, itself, extremely rare among anurans [8], [25]. The mechanism by which internal fertilization takes place is unknown, and there is no obvious intromittent organ present to facilitate sperm transfer. If L. larvaepartus reproduction always involves birth of tadpoles, it is likely that they are ovoviparous given that the tadpoles are well provisioned with yolk and appear fully capable of developing without nutrient transfer from the mother. Prior to parturition, the tadpoles have well-developed tails and pigmentation, and well developed mouthparts. Given this morphology, it is unlikely that these tadpoles are endotrophic (never feed before metamorphosing into froglets). It is more likely that the tadpoles are born after exhausting their yolk supply, and are subsequently self-feeding prior to metamorphosis. Nevertheless, it is clear that much remains to be learned about this unusual frog, the discovery of which brings to light yet another axis of diversity characterizing the remarkable Sulawesi fanged frog adaptive radiation.

Supporting Information

S1 Movie.

Video showing the characteristic quivering abdomen caused by movement of tadpoles within a pregnant female Limnonectes larvaepartus.

doi:10.1371/journal.pone.0115884.s001

(MOV)

Acknowledgments

We thank K. N. Tjan, U. Arifin, K. Laras, Suwatio, A. Rachmansah, R. M. Brown, and C. J. Hayden for assistance in the field. Boeadi and Mumpuni for MZB specimens; R. F. Inger for discussion and access to comparative materials from FMNH, USNM and MCZ; D. N. Cannatella, M. H. Wake, and members of the McGuire Lab for helpful discussion.

Author Contributions

Conceived and designed the experiments: DTI BJE JAM. Performed the experiments: DTI BJE JAM. Analyzed the data: DTI BJE JAM. Contributed reagents/materials/analysis tools: DTI BJE JAM. Wrote the paper: DTI BJE JAM.

References

  1. 1. Emerson SB, Inger RF, Iskandar DT (2000) Molecular phylogenetics and Evolution of fanged Frogs. Mol Phylogenet Evol 16:131–142. doi: 10.1006/mpev.2000.0778
  2. 2. Evans BJ, Brown RM, McGuire JA, Supriatna J, Andayani N, et al. (2003) Phylogenetics of fanged frogs; testing biogeographical hypotheses at the interface of the Asian and Australian faunal zones. Syst Biol 526:794–819. doi: 10.1080/10635150390251063
  3. 3. Setiadi MI, McGuire JA, Brown RM, Zubairi M, Iskandar DT, et al. (2011) Adaptive radiation and ecological opportunity in Sulawesi and Philippine fanged frogs (Limnonectes) Communities. Amer Nat 178:221–240. doi: 10.1086/660830
  4. 4. Iskandar DT, Tjan KN (1996) The Amphibians and Reptiles of Sulawesi: with notes on the distribution and chromosomal number of frogs. Proc. 1st Intl. Conf. Eastern Indonesian-Australian Vertebrates. Manado pp. 39–46.
  5. 5. Wake MH (2003) Reproductive modes, ontogenies, and the evolution of body form. Anim Biol 53:209–223. doi: 10.1163/157075603322539426
  6. 6. Haddad CFB, Prado CPA (2005) Reproductive modes in frogs and their unexpected diversity in the Atlantic Forest of Brazil. BioScience 55:207–217. doi: 10.1641/0006-3568(2005)055[0207:rmifat]2.0.co;2
  7. 7. AmphibiaWeb: Information on amphibian biology and conservation. Available: http://amphibiaweb.org/. Accessed: October 13, 2014.
  8. 8. McDiarmid RW, Altig R (1999) Tadpoles: the biology of anuran larvae. Chicago: University of Chicago Press. 444 p.
  9. 9. Wells KD (2008) The Ecology and Behavior of Amphibians. Chicago: The University of Chicago Press. 1148 p.
  10. 10. Guayasamin JM, Bustamante MR, Almeida-Reinoso D, Funk WC (2006) Glass frogs (Centrolenidae) of Yanayacu Biological Station, Ecuador, with the description of a new species and comments on centrolenid systematics. Zool J Linn Soc 147:489–513. doi: 10.1111/j.1096-3642.2006.00223.x
  11. 11. Inger RF, Voris HK (2001) The biogeographical relations of the frogs and snakes of Sundaland. J Biogeogr 28:863–891. doi: 10.1046/j.1365-2699.2001.00580.x
  12. 12. McGuire JA, Brown RM, Mumpuni, Riyanto A, Andayani N (2007) The flying lizards of the Draco lineatus group (Squamata: Iguania: Agamidae): A taxonomic revision with descriptions of two new species. Herp Monog 21:179–212. doi: 10.1655/07-012.1
  13. 13. Merker S, Driller C, Dahruddin H, Wirdateti, Sinaga W, et al. (2010) Tarsius wallacei: A new tarsier species from Central Sulawesi occupies a discontinuous range. Int J Primatol 31:1107–1122. doi: 10.1007/s10764-010-9452-0
  14. 14. Brown RM, Iskandar DT (2000) Nest site Selection, larval hatching and advertisement calls of Rana arathooni from southwestern Sulawesi (Celebes) Island, Indonesia. J Herp 34:404–413. doi: 10.2307/1565364
  15. 15. Kusrini MD, Rowley JJL, Khairunnisa LR, Shea GM, Altig R (2014) A new breeding mode for anurans: reproductive biology and larvae of the Indonesian frog Limnonectes larvaepartus. PLOS ONE. In press Vol. 10, Issue 1.
  16. 16. Gosner KL (1960) A simplified table for staging anuran embryos and larvae with notes on identification. Herpetologica 16:183–190.
  17. 17. Wake MH (1978) The reproductive biology of Eleutherodactylus jasperi (Amphibia, Anura, Leptodactylidae), with comments on the evolution of live-bearing systems. J Herpetol 12:121–133. doi: 10.2307/1563398
  18. 18. Wake MH (1980) The reproductive biology of Nectophrynoides malcolmi (Amphibia: Bufonidae) with comments on the evolution of reproductive modes in the genus Nectophrynoides. Copeia 1980:193–209. doi: 10.2307/1443998
  19. 19. Warkentin KM (2011) Plasticity of hatching in amphibians: Evolution, trade-offs, cues and mechanisms. Integr Comp Biol 51:111–127. doi: 10.1093/icb/icr046
  20. 20. Buckley CR, Michael SF, Irschick DJ (2005) Early hatching decreases jumping performance in a direct-developing frog, Eleutherodactylus coqui. Funct Ecol 19:67–72. doi: 10.1111/j.0269-8463.2005.00931.x
  21. 21. Townsend DS, Stewart MM (1985) Direct development in Eleutherodactylus coqui (Anura: Leptodactylidae): a staging table. Copeia 1985:423–436. doi: 10.2307/1444854
  22. 22. Lutz B (1948) Ontogenetic evolution in frogs. Evolution 2:29–39. doi: 10.2307/2405613
  23. 23. Lynn WG (1942) The embryology of Eleutherodactylus nubicola, an anuran which has no tadpole stage. Carn Inst Wash Publ 541:27–62.
  24. 24. Thibaudeau G, Altig R (1999) Endotrophic anurans – development and evolution. In: McDiarmid RW, R Altigeditors.Tadpoles. The biology of anuran larvae, Chicago: The University of Chicago Press pp. 170–188.
  25. 25. Duellman WE, Trueb L (1986) Biology of Amphibians. New York: McGraw-Hill Publishing Company. 670 p.
euclid-euclid-2.9/src/test/resources/org/xmlcml/files/journal.pone.0115884/fulltext.pdf000066400000000000000000023375431461721410700306720ustar00rootroot00000000000000%PDF-1.3 % 295 0 obj <> endobj xref 295 82 0000000016 00000 n 0000002797 00000 n 0000002913 00000 n 0000002949 00000 n 0000003284 00000 n 0000003400 00000 n 0000003516 00000 n 0000003632 00000 n 0000003748 00000 n 0000003863 00000 n 0000003977 00000 n 0000004092 00000 n 0000004207 00000 n 0000004322 00000 n 0000004437 00000 n 0000004553 00000 n 0000004668 00000 n 0000004783 00000 n 0000004977 00000 n 0000005134 00000 n 0000005407 00000 n 0000005908 00000 n 0000006315 00000 n 0000011508 00000 n 0000011788 00000 n 0000012132 00000 n 0000012592 00000 n 0000012646 00000 n 0000012957 00000 n 0000019206 00000 n 0000019651 00000 n 0000020005 00000 n 0000020377 00000 n 0000021214 00000 n 0000021592 00000 n 0000021735 00000 n 0000024577 00000 n 0000024849 00000 n 0000025203 00000 n 0000026030 00000 n 0000026327 00000 n 0000026858 00000 n 0000027225 00000 n 0000027480 00000 n 0000027757 00000 n 0000028254 00000 n 0000029052 00000 n 0000029337 00000 n 0000029503 00000 n 0000029561 00000 n 0000029749 00000 n 0000029964 00000 n 0000030767 00000 n 0000031317 00000 n 0000031671 00000 n 0000032535 00000 n 0000032680 00000 n 0000033494 00000 n 0000033748 00000 n 0000034069 00000 n 0000034878 00000 n 0000042795 00000 n 0000043457 00000 n 0000049177 00000 n 0000050127 00000 n 0000053436 00000 n 0000054795 00000 n 0000055070 00000 n 0000060109 00000 n 0000060517 00000 n 0000060869 00000 n 0000060925 00000 n 0000061214 00000 n 0000061422 00000 n 0000064051 00000 n 0000064327 00000 n 0000064473 00000 n 0000067059 00000 n 0000067329 00000 n 0000067693 00000 n 0000073965 00000 n 0000001936 00000 n trailer <]/Prev 617904>> startxref 0 %%EOF 376 0 obj <>stream hb```f``a`g`` ̀ l@QXt9Bp9okܵ  V3;Ћ]-?LBGgU3 &_Вkz[V]Zj˥+mṻ?~OѮn5=fVjsZlJ]-=}CFʸxâDGJjؙwuЕA0$jܘ禗F7E>:ȥATe;{%_~}muЬ%l:zNT-wl2{ksMDAfAK95>{(+!3q z{y^@?=|=We5EdWˮ/} RxE@ wz [= HAA!%%ecc&e㴴4 ,%lllW@ JP  4@cjp(L+a66  45BKKjg @@lbK*- l /ׁ *ch` kEVBW0Lp%9a>Cbs[Dlzݰq ?Ǹ%NjԵ> endobj 297 0 obj <> endobj 298 0 obj <> endobj 299 0 obj <> endobj 300 0 obj <> endobj 301 0 obj <> endobj 302 0 obj <> endobj 303 0 obj <> endobj 304 0 obj <> endobj 305 0 obj <> endobj 306 0 obj <> endobj 307 0 obj <> endobj 308 0 obj <> endobj 309 0 obj <> endobj 310 0 obj <> endobj 311 0 obj <> endobj 312 0 obj <>/Border[0 0 0]/Rect[36.85 519.534 72.907 566.079]/Subtype/Link/Type/Annot>> endobj 313 0 obj <>/Border[0 0 0]/Rect[48.359 309.657 160.328 317.65]/Subtype/Link/Type/Annot>> endobj 314 0 obj <>/Font<>/ProcSet[/PDF/Text/ImageB/ImageC]/XObject<>>> endobj 315 0 obj <> endobj 316 0 obj <> endobj 317 0 obj <>stream hބXytS畷I=%M) Be1Kc˛"˲j6%%Yݲ o8;1`BIP&i&ә9~J;H;3GH}~{jEVvvCT?ӧ/}OfjKNmY ~o,Pۖ5f6tW7ΔUyǫyb+nl7jy u^*?<,@\'όHԣ}OyTUˤyRABP1geg^nEֆoe}g]YYOz6;kgV֞ XYv!Ve<+ُd${orWԬܸrʁUW)YY.303XM޾hfyd5k:׌a]Ѻ9/XZ׿a<xrcro7TuQNz3gW3A%[̸`e ¶_ńj|Yl'ۙ|. v)S>t&QoHoS2׵\*h"USXm3ԩJ&ˍF#7`6ËS޳9ƛ Hf'CnH7[0ǵΩ 8E=eL3g`zwlVNƸyp H$= *sV53z*;X$~qO$OL\"~9x^l! yDeWr]uCyYYQS5iU:#fiIQ:!h-wW#vbN'rR[ ʄej=sn ]AK\t{cScc?`g3I2YQг8xIXJ60[t7Ɯq]}DۡIjr*~SPʔ5T D}D߬C9 `9XW &K rbb$lR"*DQRʈqN=&&ئ^0c_u ~{ο}>{nB,|xZ$Q 1T%E *Țy2qk!0>AMocae?H"~w$0oK5%&WOkPj5$.t$OSXܨ=>_#ևPJpYђYBHvxq\?L}Qdӣt6Fٯ!∤ΐRM}I<u 14@ܹ_f_pIyP|4\/|ū{}q>X &cÔeRIRmn@kn5T0(r˜!5 A 褈T>/}jz:Ǟ e,( g_ vp̟ ʱ*ʹpŘz1o10Gd4Q*u'< ǘxWb7(rwbg%\ý9~0fh{U4MҴ$m_?;`5ޘhiF5{ N- \Ҭ$i2)Z)8 |\IeXIB8YړDb,:npgHTwi.7N /Ş&\#p Y'9H;?3Kԥb̻˱ &;Ԅ{'M0sg10/hIʹ<) THK:_% pXkKYBF4eNu F!ieZZC2 fU*g甤k):\.2Csy'4ό_IDy#W7"=YEđ %P]acub|ؠSqeY 7Ilp_Sbꨰ_ v/{xxB&\X&CT?$\k(, Cp m(V7.q+ujRMLBB(uŴo;;hy/'+Ɋ=b+:JDc 9` ĹV5~Gp4u@? d =x>|o_#B ,7N/I-K@EMk!7eA|=T#pD eD S,/|>LE;Q//Rk=V' >OzAT6+C@s0600OFbPqc.^Π؆P{qYZ̥,4iu& fQL!{G۵L9V6sKquˇt? `Wr`LIUBajS}}ڇѮf3vEÃ]XYt@i켶e\6V?QI#*DRS5U`T%/~[zGM1:Sߩ zITLCJRg`ʽyg T?㠀{48v6ܛ >$9@"~ X`;,} W[T+ Ȇ(z,}6ٖؕ4?⤶,*Dצ+Y01pHsn}1ܔ[oWKphLUFDBqѲfI=IzHm^*[\7̝Ì-l}n a-ixu2G!#8*bfSh3Sܺ:3|)Yq9UV<LpDY41+["> .^>}' Aޠ"fDNi"c > FJU[PVPa`_/,6[r$Fd{igïF,̸bI*M2pKOj+hw% ;StCEwj;vz; S e:'v@zr JB lF %j(o]HfpZ~aL娈\~BLt&3f 839t\7,;HF>-ϛ?PL:P:׻0N)[+5FތK!Vd)jc=<.Ml됷CW R$j;]1Θ1Lu :B#3X7jFQihS^}12gu%DZC1*o sIĶ:I)>|ߴ`#"L17[SLhjߪSf&[ BsVz΂>RX-^THym"F7Y,9 jYlx ,<> R7/Jg[=Ft/#C.߅u{l!iivRNyBopfGb?Z31*ݔSY/Uw9#I'PRBmDf 3ݨ5qE@> endobj 319 0 obj <> endobj 320 0 obj <> endobj 321 0 obj <> endobj 322 0 obj <> endobj 323 0 obj <>stream hބXgxו%eYm dgIlt,[ZYeYR"E F:"*(D )jTK\9lݬy`ʷCec sϝʊŋ**++붽uϮkQٳ%/ռrOJHe'UB寺'p+UVQ9,6gyzv"HF-_/:Y2H\~~dw: #囄U^|y::ⶵˤwqNz\QYdQŏ#5++ZVPQ!Dnbqņފ*V^_/~oŽF?QWRYcЅ%;\^͒҃e?Ǟx ?9/'?16I5Ƨ b=x/[//3?1~BݶUdn \}5 ͽ>af+`0i?syWPgOۍv'^=u{u6XYG "zZE|1T>b*Zq|3 G@ F[Q4 L ܲL=GXf&BfNTj0[Z}X+vi +H:~WrEȍm_XD6qZ}J46z<0w )?TD'": {)`QS^| }2g8nM!S#:QK >KGv i8H:\ fүA c `<|Sqtu Z֡Ȕ|CY !ْH.ebn 2}5Ld3gu"2"wK\oPoE$R[! =4 ҟ/3kY[k!j㻻n9/Fԗ/MnٯxPbЙ8O!CRo Y(kCBB IePYaz]? |JU#^/p~]cKT#ʣcfnr@=nrjEǹ;k/kOlAoV#O$=@ڵo][ k3zJtI/0}|~B3x>eBvSN!$ڹA*z; ۔~PkUiyXY摈m"1HZ0"$Zek񵣾$49V7Xh0#`ГjϹ/AJUhg¼CqNV*^5":c L3 ?W:(47yLST"qI#_ѽSiW Q zUSGmlw!?NAu(,JӼ0˕¾M )J$sCnӶ!B?/Pw!ڢ’EqvC=*A? !^N3G9w+!*^e5qKgR~\~XUpn&:Zuf#Br@XSOC)Kq봧zHL"3c4eV_ܓ#Pn{`=yNX Y=pu;hlHLğJpvr<1 VOrX֖tDQo(DVwUiN=fEAǶCk`7BG΍ذ>-(۵sC.jDS+W)oJQiVlo߂NY0za~҉e4ZiBvw3RϴX+'!UL` <`82.ȕ|'h %r}a31iG|Zdp/GjnޜޅvM6@sWuS' L(4b0, VV5q8^'ih{<- }98ggWB~Ky;k;~.iWM>A Q: 2R*A5FϾbd_] ާ؈QTFMpLRǩD\U!~_z<VU~vo'JOgӈ^c,3GúĚx?}Q{|ޝ>IMLCsk鯊P/enBZ}w3ycă%ىitz|r|b$/ȄL  ;:7 o6`ge7!EPa -O^>S' \ 4 i;J1oNd&:0Qɸ !.]5ѢqwM>_yɦ??gTQsTǗ*ysd8; e}'1S^EI炜zfEG@A==fǰ%nD J^uur uc׻X1NՐxtO`D8s(A]8r!4/8#g 2X﬌>"ʳ15$٥% V %o<'A=C:r8Q#`I ^_bJYOF^eCC.ghU'X֨/#` } v? i`2d@^:;rn&<5oSfN&'F8Wi+. Y-i$=[HdՉ@:6I[foUjأ=GSǬ $0)X@i3a⫻+l@"$BAJ1`//L_WWS4bѽpP91˄1olx.M֔'h{fŻ =jd8:ƴJ!,`-ᧀJ*!_zBPD F)ϒh_UAn+L :,"s1ƐLԤtcnM~N;ǢQ4r呠<&;nAvhEQ MZ NTMoC;Ӻ?id%j_ o,֣uH2nqݏ3u!B#Iܧ(2UsSXS$uvEj3%xpc(](T=j mi<ݾ'ܜ'~&2>bug;_|01}c:%VQhCCv'ynKYǑ\ܬ#QP H~`L }Pykm6"anYoRuZll{2XbS>&:,3)ZTc0 ~}weoZ.Do:2Ѝu;dY{c08wf D{y)x8EwݪEZo a)iV|@ ј\ 5Ij:{$Dnq5ׅ{P?= x@ ]j:O {<3|p6u~[rPJʋTF|o1ˌy0#WSB fK &P~O6PkӂI&8CYI%qwſ9A͂- hJ6@r^C')Ƶ^A,b'^Lӻ>ZA,Ig;:JF x8o[Lex׏49Rؕ=}ý .yo7udT^s ZXYkmgcg/,M9_^ђboS6DoY5N; ˧Gl@ endstream endobj 324 0 obj <> endobj 325 0 obj <>stream hT?o w>ō:I'⪒QvZ1oT@ăWcmz7 LB;e;݌'ykMt9/~Sv/`{]Os8]@@YBճv/zD.Cmg zm{%%c^tK{ CBUɢAIKF[쩵E@om\D@\PD@)ԃ4St4> /0W)}#1X}\xwXGR endstream endobj 326 0 obj <>stream hTQMo0 +|C dHi4}hzOF=ƬI^^޳[mwO;M8A&8\C8ih:7-yw ȼ;PUqWX=`VH7'H6Q^mAlI<480ZFC 5o%Z}٘TiJa+Nȷ(_%%a"(ĖC9:#aJ o֒M3Q0a((b--F-\DT,5yKy\swDUm endstream endobj 327 0 obj <>stream H|TMO0W3FKe%Ȟ=D[L[%), U{o&Ͷmf%8ט K-3,)a,kh=V UzTb]Q0@i9fj?"1@P5mD,׸DWsYN}N樺 бD֌%TBh [$`sf<~Մ~5D֥.Z~$i pM(f\:O,ѝy$&DϦ ~" tQYȍȇ7?|]oZW"Uu{0 n4J4m,4A$PU4?u8!TazKtQUN %‰n[Vu J,J"n A3&dXXD Li:S3F79]wKx`Xݰf`;zł]$K=!]3.A'N9*^eIuʝWz79u|,$(4mӅ/Sy~Szt}0Ԧ1@ufQw}(V_ h@zoww-OPZ?$(4] 0`m endstream endobj 328 0 obj <> endobj 329 0 obj <> endobj 330 0 obj <>stream hބyp$,)vaLa8 !)ô!cXVWA![VW$ˊ|`'fSf:LnIcg ъeG6|uמؤY'[~ׯVh (=z_shyr=zhYEuQˎ7ʪ35'd;H>Uo"5jRSm!UjE蹲꽤BWBWoS ߰ᗫ k7*{ZOh=2Z˚7fQ"s"ZhH Ѷe?RE ?V`R7._iş04…{^Q{W>5?'aPe1pjv8GL l4͌ B|*<݋ѹ#9k <4P :ݬBwgZ9}TDh㰹oo04r7̌E砝h~JwUЭZxA5py> haiu*KK 7+gn^K[W\M`%?ਜ਼ux 31O1ɚ^kXS`l{N6"yp @n SѯFH9siIa(s*1 \3D>q=Zx1j-mk<%49yR 6X)eqկs#ӓ`ё14> pa F 2gdB۱&T51wH<6dP".b)F,&j68i5:kz1t˝jCg62:ĬsR*.М!qrtce\4e"2-M&DjbR*LNrDG ` \ c}@?D*cF.ccPP4E84=~b\Bkc6-jZn;=8!\@gTIF9,Y-&{n ]j=ݎ55uPMD%ojcP$7Ŝ//%.qx`4`l(Ald086Nĵ^yA710ƀ!Xh,^eZRe@ %H#t:|K+mNjxNJHjiF}u2g[F1Eb8CG'sKxxX\HLf9ˎX~B ֺvU@<HCĨףYw6ᳺFEڏ+ 2̇S tS*;$AV;/ [JAaW o>Y#c\''`Ei\4#bc9aH-6onથplN!yYLR6OCH( `li[91[[o)$ANby*5".l!cpkw5 4tj B Q] c\a7IMSΌ9cVv1%iʬ%w< 1in|O›N9B  O dhqI-&`+r"e΁G1#b;eΜIO$âl^~wxk'n17q\x$IJcTHRèKi㝴]o5X Ix+@L[O֬qZ:V^/{j\XRC tZhuJE M| &C0!H̕Lu[]=yy\~`fGZ 1}Wf17YSAQaiRX}C 4ֲ{;6 Ʀ3\bvYq5^d1!DʓŲ;ۭ3LU-T[)I[qV̀Kcyi> 簰CDKG醌 dTTc_Wl{FM+YLq(#rhh %cp†%fXF}<&Xq1^Mw8ْA2-Bo`\i:р҃/{92ӆYC3#>܎l NFj}zB5aq.HH9>wmrNWϰu ćOI8 "<v?C?m[O7󐣡!&dѴ qq"T浶E'#f`LдhBX` ]iS0iYuס>rDkcIAjga~#,u\swgdu$}H[“b`\Z>u~yj+ FFqhHo杈Gh`n?vǰ\!b:s mAAooTv*uX] WBg`{ endstream endobj 331 0 obj <> endobj 332 0 obj <>stream hTOo |9njX&Ƌm5HP~Έݦ7YVg~4587z qMo}f$uql;B AW8<o9"P/}vl,_{d;!xlprڠ׶C,  @ip\Zs^Q<"WfӪdb~L6Mi" H$^Qru ؕƤH81=,r/pȷh=[=`ʌ endstream endobj 333 0 obj <>stream HtTMs0+8nMf&T4Aa+™w%zB}oݥ.oYL⺋8iUq7b8ᒄr&fr14ipN, L2ia`@o@Э;a^jЙi+F$@UGZ9Z?4Qٷ+U7D"͋cp1(k> endobj 335 0 obj <> endobj 336 0 obj <> endobj 337 0 obj <> endobj 338 0 obj <> endobj 339 0 obj <> endobj 340 0 obj <>stream HlTMo0 Wgn+vo8)i PHa,-JUia8b/q'Ugb v˨jߞw⇛8:\!p NlQ~Bqɲ*NfO\T_h.DZ0Ȅx#Esat¥ H'>$غxfCEFqUWVTwqӼ6*mN~h@?5FxқG4N5[UU*'zc쨓t3lɫy)v~n8$pm!kǪDhZx=$nww#Koq25L{ԕ|:У3^I 1D&PtFzJ"wlboD2YlޕXX uKYMU&:Qs endstream endobj 341 0 obj <>stream hTP=0 +<`PUbJ{{8 tg?g˺7d ;bΒaၽ% 0V[Pʃv uRkgv 6vR vBGOj@IfP,};WYQPz?U<:T,ޛYVԕ+TO=1l˽Kdn ?/'TG h endstream endobj 342 0 obj <> endobj 343 0 obj <> endobj 344 0 obj <>stream ANBHIG+AdvP7DB7 wg  AdvP7DB7 .+ֽTUgg:$"'@YrZS endstream endobj 345 0 obj <> endobj 346 0 obj <>stream HTMo0 W ˲mXn]-;+)@u_2H>fw}eh |˴,,r"eŔfyUI´2'.dLM4"cFf5)p2`g _W%˅| ~|/^OdLt^2]ĦH}L,_-F&[&S޳V 1nwҤ, ¯b b9]Ͷ3*1f +k@x^/XE[;tsd mMshM@sVR; 4=EʩU6F.4 \'(z j36:NH#HH"19.MWOCVc8CPѥ Q$0 dWR[gyDeByctW{?t_6G_9?.Fӻ#Yoɓ˫ூExۜJ_4;208aZcj+ݵlSm_'VBI`Vf:^Es4%У;Anm W usm׫qP]9uz;vn I*RxPy~q>L YC?vB!)s endstream endobj 347 0 obj <> endobj 348 0 obj <> endobj 349 0 obj <>stream HTTR0+tTeVKʍYpUh@ 2TiI6'UE[F U5~~B-p7<?qGIuJ0n.$#%]ݴFM3?C7n M:"_F}s+qUdQu~Ų#Xe(.)tqvxYsaFWwh1IVwkHW>n)*V"Bz(8!J9htžTRV-ͤEUW`~4ōw4M cwC3wDuXe`2\ئMߺE%"yT#h_l\/5`2*BmmB﫹_?dj~:-ǵKq-`6hfia7-,Z)0;h.1;q]B9S:Q PM!,QmgCC}(E DJ* iU+$l wfuCe. ǟ*b) 'yQ,ӠՔŔ( &U뷓?ÇGG'WWвIˏ|Fs %-* op\mƷy\Yxyn<{rbTh6>_0L#)ٵip㰛/eq1Owtk`7_g endstream endobj 350 0 obj <> endobj 351 0 obj <>stream H|TMs0_Qt" [>;ii%9`Pq~QfWb;&}o6dߝ;t-^=?` Nm+ )zsM8EY|pZ?Uʜ)D--&l|˵T #)1D粛p%(/[U8Rڿ&a{Dٽwj^ha1RSJ0 bzЩX[/L'ԥni٥Ԫ;'Ć0rDܡ_֕IcpPcFt)V癡F˦%H'3J"~)ObMZt^sU'@Bjz zKB`I CtYjGB df^4&0B?igE!Z R>\YY9,e&$dC7UmãN0@&_ d.T }a|d!Zp-g_Ym$OAY%I|90X8A}o@=xEm"_){8rCۦ)t%ޏqkLgCG>;<$ )\JJ81Σs9bb$!x >lE'`K?7 iKb\Mf]B4$CnP O޽6|`U4> endobj 353 0 obj <> endobj 354 0 obj <>stream HTMO0+ztW48v>QZX@I֫Ԯ(~g@ Bڙ7wu{B'8 ZLVߤ+XG%QxJ)#R귧pET5{U9dP ƪҢ@nce5/CWs %m%xdeB[']xjE#B8c2hBBUM(!ӀE9 +>)0ZF-3/SKLHFOB{]KJ$ai%qq(]C{yIBo!1"Gʛ;컟Vv*i, \ ۄl^h7@EhtCp\e10;sS9Hxw;cF7nE7RQ0Χ?G(BJ[=Fo‡hЍ:2: ƕ ~=>I}rI/CׂY*%u5npA 4{P{Սwå+kVG7ʴf=5d#VX8䔞+}cZfRToBEAn/aO?R_Df3/^y>stream AdobedC     $$''$$53335;;;;;;;;;; d"  3!1AQa"q2B#$Rb34rC%Scs5&DTdE£t6UeuF'Vfv7GWgw?TI$^K6F俧 qZ.wD}X>~ן~ѻnQMI~JI$I$I$I$I$I$I$I$I$I$I$I$o־zUUTckkkZ4i5+'.:>N?Q׿Ʋ߫ǂC\y]b@t ;;eI%~ _#mt3+rr]}[=ľ|wWogdcgk{sx%ǹnӪI$I$I$I$HNI$i I$}bF.EUY]n-mN]I$I$I$I$o_⛣ugQ"ov1sp6iTߪa]cXh?\S?Zg>QFb,ĹUzԏbfá^r}ZO` ?G\&[3\@˱ދ ę?ٕ?|bn;_Ϫ?Oudz t&i1'}qHY$xԵcxGVYj6ֵCpFϪ$v^CL[wD5tk=s_"iAI-{bWϬmoKv}gl~_?[f䖷I[m/u@_Gzﯝ+MFVu2cvL+gF~5bZi$"IUWTYղ0s>mq н6hܬzxn{|ʺJRx+ BUy|:羏jF*cϢߜ?'_..Om0*mc2 UҾlNecad^Omtުa7>Av?<Ρ:Xk%>[ߧcasx]_ZŎ v14bʰ{j̻_e=Ǘ9'%\sðE~l$˻9s*{12):}c|_w`g,:pmm V\G:xI$I$I$I,Ϭ} Im~Jc~?yG=d{Rу[@r)M'vnT+;i`\mhڸ /k۝Ѻeӌmktlz՟ۙz9]uhl)Y"FTgmyCW?`Z}cy/@{XzȷmuuKq.GgҦscOzZI.t[3=ڼQ8kA?; y=ϟ\~gh& F&,^{*n7ss[AA%^`do>ѹw3]] }+iqWҿ#%Jbo%ԟO:o/3ήv i is ^=*kӨc-'kq8?ֿ݋~Pi{vXYu't|ma k-@v$wj.W}Qg2ʨuZ?F9yÉ2:>HZ{Iоџ=}Kc"YXid|NJfރ#'s<}+\Ķ?g{x?ŧ~o7=GMwo sX\d=hTs^WfG֞dzǩ5%q`/q1U>`-v}ﹳԒ+b/~c:?VK?nqKWJ_B΋WHػ)+!˭ I%?㫢UFnZ(2#̷O5?5ŝWkMWywjƇ{z7_:u'jx6íqMK䌮f-1#Ç"ʭͣ8pBzgYngMfMӫOV"*>лė308ۗᏌs^?H\ؽvu޷g<7깧ey?7㞛U!=;|CWsK ,< l?Ho 285JnW({hŃ\L^;~֞2)eάwiW3!dGU,ζk(n42m߽Gaz} ^[cjc5{?ie071|H+ċzTC UĀ <Wz:Ut-;Ǻ?ʼnwWdٍ8w65s@hK^1>]Hv[]-wN˿ņ7WagSölnömawPwOibF *nt}kU7Ppy ;??Yo^\zv`xGG%?ݫI%Cg^^%,pc/2_<4/qƫ(ckֈ|_ TҿU?1U%G֊_^r3/q1}5~:N,VI/70t^A#{K;A W8.m5KzUXcPiiد-:C~wLYm~V4ƅ%{S-"c?qaGSvr0ykctW\G:xӍmV`Qgs~*SS)x\^OnU~Q]Mx {oӝYԲ~ kԱi ̲ kl` _G#:H.4{C2P5܉ac{w,ԕYZǽTH#s[yw=O0Խs"@M$珨,5bue5=Xo\ffȦʞ> Mha.k]e_㏯ӝqzV5pX_qazvZ?O?`Tz՚je&ϫum|xo+Sgޅ꼃fk)S #Ttt4cvٟcO gi}ql]n;蹀: ZbADOMv5u\ ѣ\Ķ?g{xO}h˻ʫ}7ֻtK}grpk_&6z8"ȯG]#|6to$uq*+b/~c:?VK?ƧJwOߓlEYnMgm&/__[}\̰3'Nqrw4i(euVmpel{W_LKaN)ehe_;:s %}boaF[ˠY>.KglP8[[cLLv#XտćRt2jo\G:xպ^'W71XX9ÇNxOG/:Xvhlgas5~?"6LJM4~=o}t9~Kk^ΠϬ?XZq3c:YzvP'5; -MB%>[^N?iUcH\ ~+˺[׌׳c]L@/=ĮyCmn8ڱ۝˜tdu.3#3*p%E*{/V_}Y n{|al$?G%?ݫsQ$_8}wWv,DI}+=# UR0Ńm_` %v{[]MuVv;u $z'L;{ ݾ}KYY^ڲc'7sKd#Wο[uo9fKn?ghлĒI$I$I$I$?׾Z6dOg̍W#.'۪?1_;,Ziq?:Gٚzme3_˥}[U鍷( e5TؓVwoC]wi{K,h<[)$>_Xcnxwǰ+GO_EtTto_ij I$U>f.[V:#t8'GO_E/dz/XRGO_EJ'Лc]fvS.`z.5bWE mu0pѵEYs-mZ>l\VwqvRxemiRgwox}[]/M}[p:뺃8?gz~Nn. P+ua$<||]FMl>лĒI$I$I$I$Y~}`^~=,Ǣ֘n#WG;^n´T/w{{x%oW`o ߌͰ7o +T}M+unKu&~ysmA$I$I$I$I$I$I$I$Xo'Y3>lΓinX~tv[)$I$I$I$I/ˑ#ՏUm۷'~~SƋ5~n?in6Di I$I$I$I$I$I$I$I$I$I$I$I$I% endstream endobj 356 0 obj <>stream HDSn0+x9NynmDjmɕ]Rs1\,U78|%%0Xoa>W@xx.Z=b7 ƙzZzB;|%`]eeYI!VpӮOt m>ԴSD .Jn7wMףަnM<k}MV1l[at~9t>*Y*:Q4]v=-=<5cw ,ϖw'+[cHFȧx_o?)L  4\>$%4a?p%XwRKF&€`yޅ8s"<ߧ–,SoݴX0qI~eR1SI(!H% 㠈dee$}~g5Ӡ'B"6zs̊Q}xǪ+B7\U:6)5N2yIJTQDž{|8OѢ\Ui->Pis WS >stream hތXiXtwԌk;ODKb * >þ0 30þþ*+}Qcbb1{o.9'}nk\t{:ǹ8ZmGN@4ѽchv6ܟ fg(Ilfl>ff)ekcmrlzJlloFs<Iv>v'y4o%Oƻb 8J&=a7"lē~#5-[?7[ߍ7J?G?k찱c>Ne0 x^ݏ.[3}_f"B4skO;/rESД#h9|G`9L)X>`K*^mFF .+Ԙu qʲ_67mBncOQw^% Lqo' Y#_l8G3wOW{aWa 65TUFS-u,ߠXf[5{?}7j!6Չxf\[>JJ X*lcRݞMMB~*= ?; #4M!VB0>UCqd @uF_!/#Nn8OxA[3, +[>{>"$ A%Phփ0 FFr$&sRU0gwUvhG;ٕ_a:wuϛ8_X+6+Dv@{l.r<-="$\YVk癢އ=|~=p٦58g`-ew4 DLr;NVlK  |gM5X̵38s4MX.,ۚ*Mq-s;͛: M,'&D9%(SٌpeJa[{}ݫ)z5Us.Z=p ︷0ns/(ml D*wً ҄GirJNؗ45:7- Q-ƾAylu.2] (Y2Hܽ~Bz.bl4mECAwj}#[vɞ$_dGxo;QoP$n 2dKa΋ <v~zo?{jCAaeKʇI_|k'Nд(4B+b2(>XxF%74]LHK%^)lJyO=*q [".HֆfOJ9HFy,-K/7 kyĠ"e{5&'ȗY3lA"տ Gp0>K#v0ܵSyU-6t}UmiVak.W4 2pjd+O,1Fso0]!U.01o8 ~_dͮ„;~AC''?N{e*hΟ5i=go^;|r61cE&}OUt%Fo[[5WVJ~_X*M=Kΰ@&,PZ=+Oݺ󟛵UExAYdѝNFFoE4:nʹG4*OQ|Ky$ӋePZy,4I^A ۄiFEWn~## V,< JGp/u^Y;) N7=rR焅$iiZmG}YʆLm8LT:YH*dUBRLh|ROnVq,e&|UA\m=x>DB4@ 'Ph2xzx&vCsO?hG5@#є)_p+>a>zn#[4)="Vb+v>3 72-[VH'¾>l5^!xpgv[&go`v(ҞX1\_6n]˻#p%Q+ S˓qI aGnL|v]D|63c*\M#gc a i)r1+*f)!3:i= `UM m׹ȕU0)ywnX],^24Mm٨ss؟νywvERaXcUNpKdb) y<=8Ml&(_0c{J(̚tk"}&Wo~_MGX݊}gW'NW?eKzO|pّ~L8wzeyo'>5!Cp$$ pB Ċfe˜e6%"f]~-:cU#0\~J^*k#yrT\H%eytMЈ%M4\BsC漁 M} mv}' ۊ* xB }:-2 t-n8C%kԺf].ז\QV5HCx7^ZЬ٧ ) Fg鞥U9†l8=[\-'FZU.n,)%*\}a56hV̳TaE- :AQ!s5,wSfa 7M_ Ls$-~w.~vN2BN8X""Nφ"ꘜ*[K"ʽ>8w9Դ\)UB"ީ般gʒ ciu!T"1=;3JmH//*0E5MlL& #)jet er{2).4UDGvVTwTUS  |]5C$nA9D2~Q[G I@a}lUK3V K_[%DnXFl `b78~e6inViZR'ӱ Et-r ݞ n-Ȕaꥧ-C^id-{~ C%7Xy_f9ͭ~op v˾26ߜϋϗkZَ I;O?0Z9'RSn.]]FNGG](xv&&dK^4ZZ]P]k` ;u$nM26D纥 o ym {?o0UW NM%ibkuqC:}n5`npf~ U֣&orf&uI8w`Q#hxS%D*~^[Kt*k _9}+FE Lu-z^CDGJ3rRPL^}Tʲ+#LZ _VčV+C] 9jC_VXM_b%z]6U)i Ja i2iqaD|eUڎ6}wG9<ޠ,Q勳SSbC232d z-᭰L!6x*` Cķ}:dj56k_5@OmRw{Yu/,Dxa!i]A__tqZ4DL jĩllZRfJZuFfqmqp2鉙)6+Oøg/]xx=NH 8|-cf=FfY &]EMt +! bbrSb9^E:{~BLUn=%$3t6NxpK[ 6xF%KWWW34ETВ|M6+djvX]A'TG3g<4b[oX,8U$3\ 0rV1H}}U]Y]j9U/5E Z]Ĩb~\6 &|.:o]^/0}]ؽ Ctjjft qɾ09FyUĹ_hJ' :{xX̩,bERNh 0U<<ƪP76cY}I)T:.&b#5$=\= x|$ZE$- IY( z,٬/ob9%4UqxWk_yI҂n Zy5*#ίr(w|k[E]L^H$1Gy+w:}ݺZY|HNsc ړz*w+sY}IBƇ D, 6{0 r굅=τ2M^,~cŕqHDDžvrף&IX) $y?h3\+e3͙9&6;g59**n 2EQida((η[`Me endstream endobj 358 0 obj <>stream ANBIIE+AdvP403A40 b AdvP403A40 %CFNS]q2~W )Bp!vP# qc=, nk :X=6"4:`"1@"?H` P @Koe^MNI)>883FHUvu0J;ۨQEIrriKܬ]W>:jKZId0UhCNQr$E0(,3QEi3qiiim׍ɭhOP2FLVKL(5J'#Nikue`ZjKqA'6 =ShUl4AΝô8*T@/KW{'rj~Y+Cs]W2%)((&̾ava2!5&&))|}jeL3? Ȓ4SjέS>ҭS.<,K]l~vmw|xv|pvYI;^`]p>cbYk͗PVbJu>^ZR endstream endobj 359 0 obj <>stream hބV pS׹_qhJD,>$.!aKm7Y^˲}-Ym6cbB(y&-m^4yWN&ig^ͽ3ws{( 񔸸I}u)ݚ%%t^P(A('M?ađe-|')_P([(WS( dpJeRTqq{tq>VؙscjujՋV}O>qu"Iꉂ'W_ݸokЈ4J]Q! -͏Xv=Y) )2+< 5PQ_ KQn."ft"6ژF{\%/'em0@t:6DCBlȤ?=ަ.j.YI^|y/{(VȾ-;IAUS3Tm/k?(uʯڡMvZQc(kmWm.wLTc6Cqh|wM c/Fr_Sԧ膿"w[͟&gW0|qF Q+!>Ж١%!{ԝaLo<\I:*zy R# zl}8ZzU[TŚ*VœF=dN!92] Q%9Cͩ6 ~ &/ \5Βjl\Q¸nPߌ(blX)}$FmT@{>˺%(u9\#F  I^ f} 8ryA6lU5^w&gzU\)/drǹq0%\bЩ|a+KCFW {;r=\]AOk&~ɼ:(kmR4v#(k-9M篾s;< zCjzipZqv3}uOURTlxlSG[d$y.،xM m{qIxh/Cs#؅sz*}m+;7;<9PPYt u^ P6D|ihr}+|C~'nuB ܧ{"IP.ozYv8GTJouN x>sz88]Usd&g#ޅxqmp߀xq1K!홯l^ݣ_h"~&RyZpJ!$LR MdޮkxŸn;f >0SzgGV ޚ-@P>cHg-= ae -d?yEʯm0nᐐE$((`iEH9* 26e; ppl&KݥZ֪{#$NSE~Ani!-Gs`@ 3 %1qEz'c3|UZTܡ}^5{Q4j>Pm E"+nܴ\,ˑHrgP&$0G؀9?#څ?0gӊr9ODh }=b_VuX'!Ttόgf NLƇhg38R ӎRv`״)`5_@;bIzh R(6 P('2>ZoF |\ 2[r6]ypGW NԾ!7.V|}XM&!hʷsD:y']V:_k_Tli$ס$]r`8&{>|îiH h5+eo +h7,۰-.~$E7>r|IU5Wb5w}/StȅH#os$%쾯vW؂S̘8)CDֵx@uȴBnJ@yp䰌xI)l;θ,~gc\g k $0567Kdw+bNDÓ>Uxߌ jz]ѠDKctN[ SJwo"a>d]6o.oeq:e 60;PĒ6C!V@=j\%O'_;=h]fn>CV\+W^-d U,G<(yԁWe6<#Ɣ,oN]\;pnZv?A Uz:x=pg~1KmYwOgY#c30񟲼veЎZF&q[}ɱ'!낊CM"nY6Rɱ&@!j;OjeKɾ}Q%0c?3c>1ـͩVؠEiL6nr]*R4s8-9j7vhy)O>pDwd 2B"eRص.npjO[u|./n?)d]MU+FfEMc}Ce%)b%ޢZ5+MtD> 6xy8hܴ'i5K;#8 LéAA[Lѫ #۲G?x vـ?ժ5(6pz𭉅o⩕ΥXHi5|eFc2**ߺ][ށ氰Z*3˪3Xr6h~*^jTNM/66J?W|l znM!|7cF:>>EOEW' endstream endobj 360 0 obj <>stream hdR{LSw}_a ^鬠RSq|ǐЎ*Sy Qa]@sU0E]rNvfr~ww> %R#fvlbbؘ5)pɾ녑uCOFX}hH1%MQGQa* 5KNʨ攜)G_V9L:MjvKQ6Bɖˬ.0NQ~\JT^txU/C ే=$W_`QcQC j^eFCVtu&^N sB5H9H[\G^OTC3' 按.߹$V~ Tu)v(Lo  Y175'>G?WV@ Tw~|LhƵKlU׺k/mj~'2Pgq2jAn7=v]UFGks~a}Q,iߞߞR/]ǑkiT(Gp_XTZRb]SfsOwwسM31EǏ2U?0]K湁? `11Z8@4Ob0,|+^<%U&Ҽ:t(A]ȃ~R3oI_rjpçuQy1ТgJl2fHoyԺwJ؆1c&"4<qTu U{g?PK7WMך3ٱ(MQvŃBldL,o~-N=S͍а`Jy; u x؏Ӗ*2xRnN3y9`jrbU':SmWZfdsx6*`\Kt&VS,oS ܅ܼo!syT 6מ/OJ'Tt> endobj 362 0 obj <>stream hTX p׹6 < $fvI_MSjhIJ)!$PclcY[+i%0JzYe[~&@0 IHH? aF @kNN63i!|_6=rS;!uއKO/`fWSDjc('\>+v7Z1v{Gpۏ ua"\}CP_)[BB3bC YH%ҦFYyQu 6,pCA.XqO ^J$''߿9u!:DǽSjJ n>&J/F]i۶۶)`8G,5a[6lUEEŵeMUt\dsnaN|ibbx=ۘ+p ump;@H-{`i0Φl/޸%B[[̋¬|}gnnu7f|~%>~P)k%b7r~n uDDH09w5sM;&|>afynK싛gb_~~b~OyW^$ )i>bv.O33Xߢ#4i pBJerloEtQ75 <0 |v'G^GO[G>ĥ,8ſ<4f~!١A,Fk9AgZid&pc_?Mhvu'D:fKS}OS:a !볐Vh=TX836Ħ5`FH!%{$4CǬi}'цSvD]býh'aq62&h 䲆il?:a֧)ELDD؎t^_QJ:Z| ܧjT0B]nj!kʘIkTՊFK%(O4Ma{mt+f0wQނ%=༰ feV_MTڋX{OfnZX+ z?C2ߤ|)_Wk]#uGH[QL0ݖv(խBUXqY)%du-:5 Js  $Fdt^hjAOZPHJ_Cx<%z:q"b1b5|^[4^kFLZCɴNV6N0N/^#10H.xQt,lNODf/ h?MԨO[&u7a*& 6Lj T/8«>JB3HIx˶Et bzZ{kmBGݤ"ZRmCOA۝𜎦T7ƢM˖ޫp}s4H(ꃰo[ )H ɛv]}Vȹ!H/q1J i!!{Y1>X4TW'ճX4v+#o8ox΄a`8@ E_--Dwh5rkIHYf߇kZQ>&i}e 63K@y/lsVCSt 'l'cDS^pņGyA_{ۋ{:G^J}@-fN"v-ٱ~AHh)shqcn;@|΋;TT"Wv*1qejC})}^?=EOzYtuHQQo5:#b)X qk+d"=,eD-F7eF $[MeD{EH {&aK }),"@$U(w+|Vы*1Ak S*  `E¿D?W ^cABG(t$ bA"w@}(\TI[in8zH^A?jgg +>:Jxkfy_+fyz23!CqxБM>SZr_ :GX<1_0^ksF:GOo xTÖC%aj1nhQza-4Z+& 6&9-Udw48l6;H].B[#Bf^]'IjO1";ƤP21 e=<]OAv*tOwE0D命XqChޮAe mmGe&IB5á~`){*p]hkAnGwɳ*P.ѾA{3Q/G$0盼eE[rqy4\kB PA>#t兔J 30єj@>]gu r.;7Od`tXY2^GV1 ͉=~ 9G)|_Ǒ*sHWyJ!jM22SC8vS!:D/mGEF8eP6c+ 6)?+1;£&1;fOzDh0 Jqsޡwqqve&'xzP[4-&13@T`3~ M䐯Xp-3_:E_>UpkYڿ/.Wm`yaHl=Yp_-RVYeXY:_?s\ʅ]6 utC7fok*|/p:[vu:j^濼ah۪Vju ͈')µΝy:#L k>zTB s%WA).,罚s߾h ާ2n 0 endstream endobj 363 0 obj <> endobj 364 0 obj <>stream hTn >]NhqƦNgՒT$ ߾pBr86zF 6i\B` Z,juqM7 iv+^R~6=Nי@X8%%zM,iYۯ'+:izK4kҩo`?˄-eI" #SHGyAD|Q=Hkvo9j+> endobj 366 0 obj <>stream hbd`ad`ddwstvvL) 167 I;Wۯt֋W vOb`bddq622)f``lg``jyÏ-<~ z#l[CRMRo<҃;BggTzDSw%~ K|nY~Bu~kB P$@ (Y endstream endobj 367 0 obj <> endobj 368 0 obj <>stream hDVitUԫN%kx/YE:0J BbXHwVK7U{w@$`PP5qt 3(x*8=~2Miii<`ϛT΅+f=#)H~K*~oeF|O7-rєF?o2M3M20fe^0rMKj)1eX,S:C!;LR{˛r W$lR#.gVuy#p;xN?~ec^.my;o=%> =TT{>UDݡ-f@اO[3=[ʖX{l>4I-1I+-""Vy/U0p U).* \?(#9kl\(v$ fhy^JN%sɽH&:ل`}㦓8jpzŊWt<|ߵ{* X2϶_[ ,,߂pҖ"^mh LKqxP^|lj9[sbF3dnrI Ŷ:}u1ȼݘʩ^ &tdEx*Z.-θ]v!Q5zЏB)%hn6l\]˓f!HG(jb6Ť_RWhDa;*6ɨI}72ǟ;|J[@'@8ťٷmy?7?We%6|{g`5)gPoO'iݽY躦G)1ǩd6>RA"h&Dn}:gGGV֜%ܴeg SZjZ:d*[X OQC&وEP;HF{[^U!57lճ"J٨hgلB dnaT,iɸߑBFtHc^Cwá"\Ր;Ο-|n _0  hcV7/5KT)*\0?Ć ]zE֪+tm[K 8:0m#ECZ endstream endobj 369 0 obj <> endobj 370 0 obj <> endobj 371 0 obj <>stream h\VkpIG冽fh(4)dRvI?Dl-VJZxm=WWG;qG$8q”GfL3ӵ_{νws2\.ٮw]]]ޱGwy-5"]? ݷݲ r;{ڦnmKzBӛ zSG{vWook=ǻ-״fɦz'2 m({D&.d7^RC2VBFʖOɻVn|h#@>3T6UA5Mol%x6UEx??7߶iĻUvޥTBjIժNi>'"TW`ZwtYͨbpcݣF0zK쯉4>]&-u2*[V,et){VKQhWVt[KYbvjt"2U=~T31|<_[p<5m7rzef s 8OJQqs$nߏ^дG}ÀTjWun?-p!.=xj6D _ᠴ<g֖ſ5߶>\9,O I4;x[v/Xp˔.â!z 6nS\*D*Ĝh"͘9hg8t4 =M%N±T@ف Ktx/⯶Ÿn2u𶬉XI-cTVސ(; k(6K(Z>5J8XoFeYq/f~4sj?SkpU*D3l ,Ȧq"]<=-cLt>cE=֠IN.pPq͂B!$fdVP1G3s؛q,=y3%фx0hp[Bj-ns}Ӄz}A'BI7N3Y,3x,M&"|8BpGGHZh.|c`|~aZzgnfKM6rQR5W3q96-cp2DmktCtO`uεK4;V&ʅLꪨof:2\:/~C4=݉hN)4usL(erc ~Uv#T[hj ?L3E CK2%Hπ%@0$0 &OqftWEi:_fl?ea dc{nSߐjt *!ʘ PX"܋$ȍΔC9>JX]v;eDazݑ~HNϚDjКY) 5ҙêrt2ƓP2[97bQ B* :D1ӂM>CfάMbf 뤖2$zWə%i}FRzuuPs98+ޢfЌU}>BlVJ~jJBBn [Ϥ%=2QL k؁SC11 G5۳ x`f*;C hB|>u \l{l'6C/َ̑D+l/fǒ4FD: U4)Q B`]o5- UN% ʌy8_$&%oz{n/tmh7C[Uhy5_q( pSA>rhX6J4%&elxhv;-T:v>;'2Z Q)s痰3VS=Ө';g#xE#[p/*:^^oӝ$Nءl ֛ А~ Md6jJ"DhC!cιPW&0+7J/I3fB~b <(cP1K{f D=R?p$HApPI(GbҘS ^|^O26^/I X*ǧKo$x |Rjș,_<9YR|ƫHJ94L1Q f#QIRtͰD115/iU"zxUPUՁV4`n endstream endobj 372 0 obj <> endobj 373 0 obj <>stream hTQn0[^<RD"KzYR0ھ\x;cƼi[=m:`toq]v.8NImם_, puùU;=TGw_mv#'n7Ψ7ǁY9#p7ǝ}|5^z\ThTPe gLˠe3҇UY1e%< $pz0*RjDPKiJ8{B8+Q8"h&BS8#gZ -t߲Rix{=؏ꎨ endstream endobj 374 0 obj <>stream Adobed     $$''$$53335;;;;;;;;;;  %% ## ((%%((22022;;;;;;;;;;"?   3!1AQa"q2B#$Rb34rC%Scs5&DTdE£t6UeuF'Vfv7GWgw5!1AQaq"2B#R3$brCScs4%&5DTdEU6teuFVfv'7GWgw ?TI%)$IJIE ULM-%IIM9(nT[H6h>G.q;\+IO9(=̬w W9?ȡ8>뗰V{}0fT{FY͝K5F̫Y3#q= l|oT=~Á:0~l>>}W4gQF{q$4NI$$I)I$JR$J~Ji|AM`Xúr9C'-־Gfg\I*r޵]_fmb#rd״ÚZ|a<)CpRCs(yh'c1Òau<7[<#kJͱT\\8pB]CEv~*\G|&$;IA^I%)10v$%9X:ġ}F5]sqLuA7Fj}ֲ缀WXUIDF'86LI&K ga^n8+C`e,WBEtS0xHW/+~ ~Hu|՜Ơ":A]``Bq+NVYl?Ufg{oSD_ga }1 \2R\]@z|B_^`o/Q# c7/cGp?Z'R60m#8}뾕ttW1K`xl, ^_+]x:|fzO|P2W߿]ESGO3WW`7f;Gj?Lt=.p[Ou 1ulJ+jPȡd|ͣeFSJEP/KyL%$!MMWaWP߈^`Q}ϊ :r?eْ> ,`n>m+*|%EX 7SPv^~U4~ ]/奤}A t+ H7˨\ utK.ms䗍$Vzv!* \qܯGl秘L"u׉ ;Oܼ揮f~Dm ^wf's~e`~U@߽t?_۬ ?`Y9=K7,E|%#Q)8H1$C~Mƪ Yb0m\\[ W[f5o[%.mFSI$ԩ$IJI%[N=/Sqne%6RC,}c^FS08AT3:U -{CZ)$_RV sS'Ӓ>`7VȂGTYzf s藱Zq ֎I(z#ZXSsm qq]Әi>aշEegY݂>XGu{;]O.߳}g41op"u )mކvmT]nwL9}EVU}gsCOcqXmSS~ꕷxsX&ӧ>Qk#srl%>& c d;[WI<1NX^|ojr#QL ?~Qf1.IN^=sK6ޔrnjjOh{2ۺgGi`[٣pҾM7|N8Ϭ]g?7]UeA/0eRSkuuX_k;LD+ܺqehq'lV]Cۺ]͹}m@ZF8s?oLˮ̳6]@Jz:[zg*ۆoxoyKxL~nu 3lZ<_7F^cKyRS߷OշoS& Z.'JJ_W~IͶdYcH7&G`Jq:no&nk.$ߪ߮Y⽷}^MVײ||NJy>SNu=>z{& $IC=3U3~Mag Ov $}K}hgtj`ǎ-cI.Ծeh*vZ]u1Υv$gaxY}ՏkCtsH*X1[]>ΛMȲz.Ȳkb}R5]I):TnkvE1pc[5 $jTj:Qn :c6[[[glVJy͢2~N0Ϳ.mk,۵-y;Uz7Գ7Oo鰫hEo#$S}jh/.~q755~_l]:eֺ=7`w $ϺfY/ nӑc$6{I^:&'K}NU3pW .$լ~ -s?Xak}"X}ke3eY}v= cH.isW$b}aqꔆ2f]I)H9yxXxQg L:mא)&=AY:| cSNNe]^F%zeZY!4{g^nkԲnOTwKN%K_z6U4dUoᖰ4$7fB='37+nV-,}=-MA[1 xeP]A z?gZfVKjȬ {,%Ֆ0iOu::qu GvxCnNgO{C==vt,f;=7ۏUg 齌pj+'`n>7·&*Ƭf.EYNmnxFDbAwM:5yg “iiU}gmVpƴ(Psk]<iv-ʚ@.A1b?3;7I Ry $S|v3zx֛$pBxzҝ]mmSYi8D~ħ*vcM =g;}+f%ufSm@sX+,H*>7U4[M]yΫW9ida#)]PǏj`lk$],߬tU _SZXYʦ5 ϬC~;23F3>ܖE6;sƚ􋌟iu o;/PiI"kR:xk!={nl4;؈\ƼdRۃV̱^ ƭXOoO}[ہ'!zp?h~ίn;7vؔt{tc\oQMI=I/RIOI/RIOI/RIOI/RIOI/RIOԹ_Ѯ!T~3/M1YG2"TU$'ꤗʩ$ꤗʩ$ꤗʩ$ endstream endobj 375 0 obj <>stream h;KPMs&V4ڢSѡ \Y']'(iM R ROkho :M9'9h!Vu؋0`7J`=ܬݚs%C ,~nEkcjb^wvcR+V̰P"Ned> endobj 2 0 obj <> endobj 3 0 obj <> endobj 4 0 obj <> endobj 5 0 obj <> endobj 6 0 obj <> endobj 7 0 obj <> endobj 8 0 obj <> endobj 9 0 obj <> endobj 10 0 obj <> endobj 11 0 obj <> endobj 12 0 obj <> endobj 13 0 obj <> endobj 14 0 obj <> endobj 15 0 obj <> endobj 16 0 obj <>/Font<>/ProcSet[/PDF/Text/ImageB]/XObject<>>> endobj 17 0 obj <>stream hޜZrF}Wj_["'V"_6Crl\\D3ߑݾ̀L;[JQf}醟V6ų'♜ݭ/(3se{*Y^쮺v?VGE^ z%^D4̓y0mnF^&ZkbXAKE׷y7W&^,^í؋a~A(IQn՚%-=%m=w;2ޕ~SS |8SоMS

7E /6 9kIPxPL&USh ,ɘHflU=y ,YDOt]T[ ظL !05R)|]];'pmIqצ.Xls0]}o5!Z'R_z7{@5S[Ed#܀z6!,x%c9Nx/h֬œdbj(7LDX) LO<BNnM !n,[z9ڻwP"`?5˻ /f^ rB2?`Z% L _(bY}.yeؓ\10X?cB7e[ `yc*"9ȊM@.>sZ;ʲ; "FekTGih鉻Pel72&G$/ۦ)ݩ%=᳉k]0YŠ9S0=$b.>\4ZMEDGd(ԇ+"髿 *^̢Ȕ*&SqL;[xmUF`z)c >R@*8&SCֳ.7#GOZ]|Q9Po*.VhX!$"f3gh"kh!A:g/! . <+.FszVF^~aK>)qdH$-?DP1 ()Z9yɫjn(i춤SqФ]aU:PQgd =v `T#ߚj]&Cg,iABC3NAK^ ˦WP:bK9iM&>ְY@xr; ۬TlPl!i>U ZicW LL7IW01T| 6$BݰHRKX\V>JK;eIʋ ^$*n*i̋Q^$ibwvog{:i<,B7h8˲ {&T )S'eםMR XaCؕ8v0(Hϡ*;T91UԐ`oji%hĆסykսzZWc‰rỦ|d˟.'cCe#R8$BQдsD=cI㲱#4b_g9mrmx3tz\4e1+g,З̠.Bש=3Ęhx0 M8N΂e؏`ڗFv]#.@'ۚY9#Ńℊ:lب=ȟgqu'7 @` : P5z9זu4̣^RlY!^kفqvD!h#:$1A/ ݇ #w Pc` t~@yL)p}.'6x:?N1q[Zc|MvȢ_&4/]h[}p8|^Ǐ ]0 !B[8y!L!ww' L2 !,Ȉ AiA$YYKA* 6 0EQ$iM̖ J% w}&(Q&A1~ 8cfHU޸n clVPQV~zdz꧎:/4`o~v嗢)xjJQ5]+}Nl-u_]P?&̂ =@$PAm94edD ̖7H(CLfA<2_Y4ڤRLO#ڞ/k$[q th-e+_Dsq/$@h.d2ɋ>kNΌ:doXaY_d8-wdH.ECxPI'#lDxDK1>AsmbzXX^8;!)/ܛdz{6K$6l\|/]BNJQ֚g٭c\ٶscаԮ ݋7.aR0)Wt0a6 !28t_]Q}!db"o+qB`P.L.|{ihVt/nHG2I2ySM`p yeލ6]*S֋PK4nN< 1rt=b;` zh$UYk| \ CȄKel.\WEsO 1W J'v\i|>+x:n.} Yd hQb:_ axP,Oeqq HAZ-"$j7tzP2|pdj fľ4t=RN=N_*d%Br\+Hp6_|ꉜz#: endstream endobj 18 0 obj <> endobj 19 0 obj <>stream hbd`ab`ddrsvvL) 0q6q5gy+7 $# 3##_a˩|?O]R{ yWtNg]=,Po߷M4CaJj+uϒΖ[=[ Dhw6[Y:;ۥ4M7cZܝY`-(m` `y endstream endobj 20 0 obj <> endobj 21 0 obj <>stream hTP0 A7-k]9%Չt_ l~zϖВ /tzKqr3k;X.g=*2e 8;k!p X>-3dKl-H h0 TFyuUUȊ5τd^{s(bqwE+vgh._-$qK~w>i h endstream endobj 22 0 obj <> endobj 23 0 obj <> endobj 24 0 obj <> endobj 25 0 obj <> endobj 26 0 obj <>/Border[0 0 0]/Rect[201.997 425.14 292.649 436.649]/Subtype/Link/Type/Annot>> endobj 27 0 obj <> endobj 28 0 obj <> endobj 29 0 obj <> endobj 30 0 obj <> endobj 31 0 obj <> endobj 32 0 obj <> endobj 33 0 obj <> endobj 34 0 obj <> endobj 35 0 obj <> endobj 36 0 obj <> endobj 37 0 obj <> endobj 38 0 obj <> endobj 39 0 obj <> endobj 40 0 obj <> endobj 41 0 obj <>/Font<>/ProcSet[/PDF/Text/ImageB]/XObject<>>> endobj 42 0 obj <>stream hޜYr}W!mIkxq")[HHxH@r'f{snHJXIMrs=<̚x 7R&J ĿYfq0$adqh7˃ gxbQ=z8ĸ⭺D,ta]m=2 mEOQiѴ½X™_JR$A$)E9RbIb|SV^2嬝-!,/7/#(3t_WKv"KO5gjɶ<@oy“-| ݱ1> `lUJn~ۓ{UQ*8s$ʪibAkHc }Ekm#/mKPQYw'*-#~cb[sq6oֵ6Bp\U"9.d_1(~U "h AX #~-Wy& !_O-N< @>*E\`'71s`{6$p"PiYrB&{1Akg(w,~슼VU/U&EΑeo1׽1dmwA j\fa.Rҥj0>{{¾1m;qOՔjJx)ئaCbf=sf;f}ݦE{ѢdٵaDr1\ _GOq 7+KAc0S*"y`V="dgwÀcY1s̰'؏vv EyK-=J< hN.?7̙Q~חg;דƒѐ⸜֮O $w=baMxd]^2e8 tA8Qqd_`Ca9ٙM]g),)}ԝy=ø/bX {9A&v!'Z[޻%.D/; QKY20jNb`R}844L];:TN| zv=Js_̏6OӤR3'<+gT⢘P|M<9EYnwtx,IDkI+E6adY,澞,Jc|"n n7tҝyNH}gvԌ)G5PW#_+ 5cѩ)b{0-.;-r&nD=\ZNmxP74O%w++!L3yVua)Ta= ;1W놷 6S6zfQ11AQiW`'AIܲđiwyBjő]7;7*[:YYPf:ܣa6LȸZP}[ Lϭ(fMP0BV=$~J2|qfzA~f`MXaN OV#q @vУq"uHZTk/#tܻ  n3W3'Q*񠤫qH_BY8/&sU[/qmKq"3. ݶTyH/Trr,sB͟[tҶjF*T=3[h Fy(?vw3U~e4Nۧ%#v"&'3qm3$f=K4/»NL>/tH|RYetMvHzģ glK_` Hݒb ĝgKRfIW|F@2h.|#FbI[ 9EG r!"w#yQAB˱+:\_2!JOvFoE+$I*jYDfpNvE7jB6=b3E.qLzuM4Nezjf8@ Tv&1H)sH:?Uh/HDK uwwRΪ, {4b9KsC;?`?¤⊯ff(CҟOl>t*+r3޽rAqᥑn|c>|ߔxu>G³NSݑ;Gl eaa0Gz|JN,ShyJ e?իYd;M%W[Z.D` endstream endobj 43 0 obj <> endobj 44 0 obj <>/Font<>/ProcSet[/PDF/Text/ImageB/ImageC]/XObject<>>> endobj 45 0 obj <>stream hޜXnH}Wi\X4&'OrAn43@K̄"$e~")'"$OU:_r.^o\R:o4ʼnYOiv7~U\T]? VZ'a"]n.X(T" x[7ueeM3M7V Umlż/2ۢ_Ļj[Wx5W4J1}x^w+*_yZ*+l6<ȄFf>m*ox* =#m SZ?Q3& rP&n6XI Hh@ŸKQ fC)crvQXBG#sno1Bx 8 *:vٰAVm+ TQ}pkUK:/=ϒjЛT|% S6g`aF5]e[YEwCcsjKMVaiRcKFt. u $ڊRܔmcSVPf# -m79̯>Vx?(!#{Hе6:1yUo<r(/YMvNOQĹ2 >ii-Cv:E_T9.  Rc'OaQ2^p %.xƕTz*tV|<"QعQGi%2_.>c'Ț.]1yul%*)S( ҜK0^~_0QȥR)22c/Y(.SaU/Ǖt/U'wXx\z J8 _śbRRJ?RRi8kDn&~I&!w}VޜIb_Mvˬ,k&&VjpI4^ooBFh-lQaХ֕Ͽ~Z{xk/%dO?'M^MMƑMK=iL-Ni%mf'0#DJwylU1+"/adD8)Ok)Q:P_ESnMOP VI<`PUd9E uP.lY]FȞlk_&oE1K \ClDu rMjCSeex#)m`Ib]ɿ 0gHB.8U;ʍx_+$5x3n0cYs{d;zeޱ;?<G0qa@T]X=fiNfBYl̢ %py?U)$\RCKC4s=&j0 H \EǤ`fh T#GovX,ֺH^TN@@ҁ['L˒"+ԎjH{y\[ ;O"W^]ܑ\ {Ŭ-:Љ(@5Wm+d`o^bU$Ƽe~^}q][JK%$㾜Q!4]O$5!':.@*^/h]KQӎ866.H+ZDJID;;'q:MK6tCfT |CHl, APm}}GYRsDW)^{e?]-&}JwÇNcRD+]VUuA$j~HO7~GQ2U>=ɤ0t0^G,M"HNkK0MBm' l{(;.eK1-FQ&2<2xavjq}aXXl,bqS+8.yS-EX2Ph>ƒߌ)?-X+m;S+N*x&Ny*G4w3ĸA6y16#8XA{ Nu%:Uˍ"9u.Φqf|$]&!%qJ"ۗ9R999)T*gh7v豖QFʡEv쪌Ӄԑݴ@+$~7Irin2RRer1R }Lwgt9 8, #Bq0%zaYR,SڲI♊F(X|tW/sPbiě͎gei2.LQ}Oo;W\gӗ:`eRy4^}z1;NBnk  ki!ƪoU`YuԣMaAZ1BHE.әsPka'_t endstream endobj 46 0 obj <>stream Adobed     $$''$$53335;;;;;;;;;;  %% ## ((%%((22022;;;;;;;;;;V"?   3!1AQa"q2B#$Rb34rC%Scs5&DTdE£t6UeuF'Vfv7GWgw5!1AQaq"2B#R3$brCScs4%&5DTdEU6teuFVfv'7GWgw ?T_3fnXR_0~7#I/[ۯ$_KVI)}%~sr?տnGJ~I|_܏u$kon$_0~7#I/[ۯ$_KVI)}%~sr?տnGJ~I|_܏u$kon$_0~7#I/[ۯ$_KVI)}%~sr?տnGJ~I|_܏u$kon$_0~7#I/[ۯ$_KVI)}%~sr?տnGJ~I|_܏u$kon$_0~7#I/[ۯ$_KVI)}%~sr?տnGJ~I|_܏u$kon$_0~7#I/[ۯ$_KVI)}%~sr?տnGJ~I|_܏u$kon$_0~7#I/[ۯ$_KVI)}%~sr?տnGJ~I|_܏u$kon$_0~7#I/[ۯ$_KVI)}%~sr?տnGJ~I|_܏u$kon$_0~7#I/[ۯ$_KVI)}%~sr?տnGJ~I|_܏u$kon$_0~7#I/[ۯ$_KVI)}%~sr?տnGJ~I|_܏u$kon$_0~7#I/[ۯ$_KVI)}%~sr?տnGJ~I|_܏u$kon$_0~7#I/[ۯ$_KVI)}%~sr?տnGJ~I|_܏u$kon$_0~7#I/[ۯ$_KVI)}%~sr?տnGJ~I|_܏u$kon$_0~7#I/[ۯ$_KVI)}%~sr?տnGJ~I|_܏u$kon$_0~7#Izȵ8e@u. zmJJ} $IJ_.gNQ_Q3dƿ $JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)Kė[/c_lIOI)K+5w$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$铤I(I$(IFKR$ )cZ'RӺJ\;L&1qLI $I)I$7,)%J;Rڤ"Kj}~оdcqaxJ&]6%:=0 `j'P+qmsp-#Bh%?Jq%J#-o ^Zƍlnsh.qD`%3zVK[͕Z$vceR($A*I$RI$I%)$IJLS($I I$RI$I%)$IJI$RI$I%)$IJ^$,5ؼq{bJ}$IJ_.gNQ_Q3dƿ $JRI+ְȹI~ )/ﺼXN3v͇M2+^83 )9?_^cgYp/c[ ;c{4$]'+}Eԣ-m>w42lPථr=z\8ݰ[>s]*}|ŵce62dHqJ\|yhqsyc*?[^nRh:>5^xI$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I'$$H%I$JRI$##u^A < {Jo}[4}\qi۸`J?įMmC{c1>v wuGQ1Ǖmxj(tVo\=ν̤vfq:W}x;|~:eW.k\ֹ?A\oҝo>CIαSaoMmiIO;gJӳwe]GR>Zk-{O gqv=c]ԸZp?5|of1fUei G?(~]Xg̏%%`]SW S?`Z  wL\{7R}z̪HtG!t9wNff[vKϑ<>i*I";ua Vzk$>ṮL?LYy87߹4p-۰r)pu!Б x.n;}s# 0ճ;]fa7&<tU5RޢtI*I$Rp`AL) uJhS|h:b I$I%)$IJI$RI$I%)$IJI$RI$KB_-/ ? $I$ws;vGkJ@I$.^=6}޹u:{={g$4ݑu4[G%α3Et J^E~;߷\/=?;}acE]p|r/bݝnijqc[|nk;@U+~^O@Efmv /:Y?F[??4G]~ez~F=ն=\';<&;jJ{0g> (^8:_Ӛua o¼$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRtHRI$TI$$I)+}O\9F쌇O%ַl/tufE_Ǐu.?/_mqk{o'#ou>(9d7U{.$SnQ54={,޻-X̾e?6Y[]6$Gc%c1q.[kPE:韽w~+S`;+uY˜|=&/Kk&mn L?~/$IRC"I$EjI$[&e6Фkf#LIdxʽz>WU㖱< %P+C @=e$Hx7Z/z>cۏeW84Vz]G ՕV:`4!߼;˧9hq٣k7KVƴUl4駒Jyk3a.. gѣ84d?o6޶qqj.i7n]"?*aՖ:'Y{Kkmk&\0&Lp v-έ.۾ƶZuli?^Ixf a!J/3SkwQ@p{`[$C^5Y6VXG'%'EI$RI$I%)1NI$)$IJI$RI$I%)$IJI$RI$I%){bKB_-)tI%)|;#5E}F\$I)I$IOeol u*.2ђݥDL>KNIIIvv}s} I%SwukVW쇒L`ьD*)ITIITIITIITIITIITIITIITIITIITIITIԌ5KC k2,{~+}} :b D<$_R2g9a)zo%?,-u¦ow'hNjƏ섔/ӗ֝y_f.VcRUn.->kGD+ŏ_tvTUcǤur>]~##{n&[??㤽O ?k9mE&ZjkqhZ>o'^ד|8TR)dNSpH)$ t:Xu^Dc<970i'Hi=>Yѻi89$pǷh5I.I>_nNC\Yc%2Oޙ)Jl`[itZ8] l @:z9'{;X=GM^w3{}6Ceˈޑy oĥ#*H9r]2`6f %,1τ $Ra+0C*c\9&qh7sFBŕ#Ū8,[˜km!u'b )i`c2NSc88<^W"@*Uf.K#4XI|7cz-X')h&8(HQ]$E +XyyrrX_JlUCsㄔ^mv?Y&g] e aT+hrU魄 D>Ens`>eF5f:@I$/3dƿ˙Ӳ?_TRRI%) A%I$)$IJI$RI[X7蒚+_> IGӶ9%_O[/ cuI$I$$I)I$JRI$I$$I)KKu aʳWG(mX{ޑ4-د=5Dp[Nu \3\nF}CIF2:u@Zk9]?ԊzT@?:Eus_Ws[S{hoKC{Z }RZXcd}}Kg8@ֶuMO;tc'8խֱ<?ޣT=W~k+! 8JOg[ØMNgL^:wHhZ#ZXOLǤnWu dƵ{'I4>,Ncq68LV[-c}cïkF=?,Q{z,c 'G, Q*O!I&rtǔ $ )FGYlW`$8ZUa J kb,hO(u:7Q{ qh:R>q ä> ;Y$ԞBVN!ӳSZYakx!̰?wr 2,FhLj]o,s9v uTYzoI'82KE[X~Z&A]A.tu]&,k| R}2䰸}a`te\us[qdp˺8g3k{] k/|Up'aKcD?8sx:?YzyqlS:ǀո2n$R@hAܗ;%iν5ܬ=βt@ـh; G]Aap!߹M,~kC\׺Yi%zNϺ뽕b׶qo7xϬަih ;+,Dɽ96?Zj|ƀ1KMqMBRQhJYXnCy Jz ͹KzEV3l϶v &a%yEsl7'&O/D$T㵯h0ѬtwǼrmWl{VOȱHc68WgӮ lC׼Nэsk7Ti#_41V1!`5gw,`k%9h [ئY+@:A{iY[k㴇 y@J&p=kFw ` +cC0#ha`cK>HR\tnZZ\CZ q0wyck>.V}^~XTx,;\u KD-u>]뙮m7ӳkv獧ߪptiA~wլx6\OrG~#\ 1>IgXKK/?^N%כրg9p$$I!I$JRI$I$$I)I$JRI$I$$I)Kė[/c_lIOI)K+5w$IJH$IAtO d> !Bv6C>OAn0d-ZEB 7ůnyS L'q`PN:i+jl dLØBVE䶺b )z7wWV֋'Q?0/(0$Qa;O"WCPP]5<{N1%iz20\\ּLjU0j9Ra|I'QοUYCCG#|D{.2kB8ZXηWsGγ"5?!KLSe?g[oDIof06;Y`X4-^ks3+*wosrwϬuc\CO쌏 VqKWw5T njm` )s hqh/{8clw幝e^Fw7>]p>AN0uc{=6ݔGn[XV0Aޭ\ރ]:=kX! \Zm-6plX9MAjlk5s~e 4}sSOW CO QR;蒩IGJ$JI$!Šp,CsC%gSvh~iKfm/$\7Kr5ߤ'QoM2 _ܦǏQv=jWGkaƂXƁrOd^鹝DSm cs %vmэzS[}UP\4&;ntq>_2w[t:Ǽvl˜o݋GӋNu[!^o쎣܃uАvnceSELe c'4w#[:Of۬m vrWm_$* ]|s{dXg63a.N1]c:/jA\%M&Cqs $Ήʤx#%p%4%jkRcOv:ǭy5 m qQ̝4d@.]5]7k?JptY7ć[yH?+Һ8k&)`{})E=W6.yIk#,]"ky~MDLTjt m%?hNL @<Ɖ e;L5ILٳZCi^}k-wf||כ}[w/k m$[~袎f.unHw'o}JʈisfNtiJeo`ٴ4.Daz3/ُ88Q unNO?'ޫSais#kw'TES5]MVBMvoϪ^-4^TtƏ vi4+zӬy=GB.#*ws#fnORxfEZHF5ø /uf:fxwc_ wES\2k}7Va4P3׍^1t 9Mޯp05 AM~E6!:$)KRYUԸ69x s]@p:IQV[:]%x@5Ms{ Wy$$z^M5g>3Pﶦ4DkE#Ȓ u͙hcfGZTK CH$DW)9d<星+jk(ATx$;W An:6̘_=?U0;Y u{X_gIi'2׻{Z @skvk۟nM5D@5=BUAn0x5G5F}>$4@CdVI^uNi+V87T ?>;VJpCJͪA 9$$ǒBU 0l $"=~sL5<rw$0p|KDK^IjtiphZA]g&a0,P; 1<Ұ(N> DGpX|f G聠DF߶y&pYЭ߱Ǹb"'y8X?eX'6HqC=mGd~TǻLʳϻE E؍vU5 vиqChsͭ;Aq[]}Sj9;O]Nɫˮ퍒:wJ6ʭPِmnVc_& vfG:7>.uq-v9ltXӪn7h ͵9?k9/qu?(h}#U9w;9 >֏X9d0m)X[O!DN W%AHR) R<(&D$RRMU}=MlG&ϳ豬Kq>Z'@VG~껮bW_Sv4mmM5v?:NU6eTqrׂIKc?r_\m tD[5YC!TB&.gA,,qhh逬[/#ez:#Ce+}V5m~4:T?bu6A WxUc,B65ָi+7 X؍6|w*V9w!BnLJX498S<{}ϝ W"}kʴ\Uph;wCgkIMgtvH9-vʳҳrvk]@..m9Ah;aݬΞ'Š]kǗ;H``$iM7;/X]9gX\M R 55vKK+#MA< T\`tpӴiU[uA7?}G"V܊(gvu U4";(Y4ޝ,hs3"vNG; YOt P 1^mdϺ ~TZeev. Znghډ Q-յjo@I@C}TN1}fNծĝWFONqT'ozEYwzVt\ >UZ snFeSJgUcX퐪ӳqa59GvENASZ\.KwI|VfF]/3ik4OCJ=7ĝe>H7Yk ;a|Է kjql+Oht5\5uX}Md{LDirud~ dV=H0\]I},?2A}4^DђDVjR-sŨ0 NS nq {XrX e|kG'gQƢƍ#>z:~n}m jN} [mvH4 *=/"9WW}O_Ef׶QS]$IJI$Ru\MMq{Y9hPH`{"I$I$$I)I$JRI$I$$I)I$JRI$IYkEx?%!gĔ:I$\_.gNQIHI$IB:>r\&ۄg͎WYnlWY4ch)' 'DJu:Z ]K$d4Wk1e!CLZѴ8֒@%6q%vlteg:l!wOSWR3i&=LK\E3ֱŵ@tOut\,B.+is& ~սcXZbO3{xKx\q%=tiuf~kO̝cCy+׵Rtw 2n6Z%eHߋC;CVOI$@+2kIuփ̫n3x 5'sv4\^8mnT83t0{ H/\ּh`|XcZ cXvK1>sthqՠqpn_KZē$oce uO j Otg^aWs\ݼm"$yl464< p$5s.N(]b7n~$+.!v֝H'9#W<K!cZu,{ lpla:muMRqn+Z\OՔӹD#]dOe~y&x~JP6 kCa̝ڗWgϰ1āӿwf=@'tsg>=($DCZἿT+}*h5|ٖ+14t'fA$8HH\`"DwR-y.l.a!{[屠TZ7F >>;26<א6hr++F51- =^.nQouel}IV|yu=ЉuzPX9$O !\Y%> [If;Vؼ3!6QDž/i5ćVu+@tm!n]jb|WkӬ+`׏M?d27щ]쏣'wv/FKkvFEu76{'44 DioK1J%$㔒A$R0RD WtQPաQqpu<sڛTXFh>axjG?M\HZc} i?'YZmc\aOMV66]մjvMvӬH.xTݵGBxh#SAaU7aj'N1{H[i888٘b5;㢥NYqLkijVnIAGbdk!_cKnŭ o.c|dpkhf@}w :Hgn1TޚcsAN |F8c3 $ +U8w\?:GwQ=,{I~ m=816Y٤xʁúcur11Vwd5@##hƀhHrzKoRןi?}zb=z껀>fʅ$ Ѻi\qrKs?ߘB[&/W"Ku?9ߎad]D%OmcX`-4kHZ~?)p CJ$qCD=^B́]n[&@3F_P̘%ޥl&A[6da]\L;!|$z'I;,wS-TgKyհxW]-P{.fL{Hσhw^Wfed8v\I$}MӚэq7%9 x5րց|]yORJI$I%)$IJI$RI$I%)$IJI$RI$KB_-/ ? $I$ws;vGkJ@I$$H$ɠ,5hV".=`_p %ɵF-'obt{̑3X\Oܼ݊[7R YxK-s5}QƚVjd|v8Ýql7Oq.ˣsMU\wis1OUXCA\w4}!~KKc?hİP7Ԩ4jÎ4uuJX־2ָVպ=;12MkЁ\ mp#Pn/'$[f\Ns- =:ݸ5he]}Ʈ,q:ylӉC^%581[F,.^}߇oF7+ 9'ic?CMakH-#vu :^NSj6awكe$~ fWu;c5u4t{kGoB.pݹ~ ѯS Y-kXA7@.aHpqӗzr{#*;'s̆7C~)7 '(u 2 n 9! ;Ap($9k # ^Aհ7ǺLwKshb;wu75MDGt5,1K@ed:s#!(4 ?|kq i-s2<|{~*>X+Ռh<|%/akӳ۷i-O^)*1I'N[=(2W+skm@ժ1D>2JHMhV`ߩvtƺRnؑ5Pu{yDΓ1[Pqx8ğhj;8wݖo p7kw6y㺦cncLǑt( WIZ7B@Ť憟'Tgv[s?Y cgO~h/{q (u繤4H $KF'ʬo:]yPSf$#H -O}+u ;j \~<5M@KnE&SG\lgg^# ݧϒ#tKg7g6twr:#ʱ>t[:Se-dYd8XMu?}8L:j'4UT́!]ɷhkwz ՌA/@!h)p 9ZIݧ2d@qr|4N1o.F$'8꛲r6*)BI$ QRxYg0 vKNCt/ @$ÊL}آ͞1rIdU9iuV$FebY֛^vÝhVr#αָEӤy@I-Q QC{-|,W7kӡ DǒТYXk"7^CN%2TӋa I-.%ֽio;An~>+Sthai-;5XX]l?HD✇ <ۚ"[؎rѤ"IQ@n@6txq-ӻD @xZcݡ5w¶ p&bdp'iiiJ>u-su;\<4PuD6 <|CCZ `yU48 |HGOꝵ<4v|j>H -mpjxiKdN J̌"֚sOh֓.xFyyKDj;A sOaY5#H9iw: \VGO\s! 'Y;;i}Y/p:U/795$RGIR {EMVe3g?tPk --+[3]x7uwY;w "~3ŕN ZrHpmlDnii2auK6Q\kf7" ͝|Gh{s\k Ϋ/鞾Wn?$Pu.z?E/V^S<_+I$RI$I%)$IJI$RI$I%)$IJI$R/ ? /^$,5ؒGI$R˙Ӳ?_TWk))I$I $ cec84Wo]:350`~G;;' /?%u:4elLjO/ Q:W vBFӗik5:v+o}ahPK"\ +rcC ;T3>ew^-ͭ? 4hK[S`5vT%Kis7XUtCsx}_ݖS`8Fɝ [331Zvۛћ n#|n+˾ܻKm k͙Se`i /uՓ 55N ϴݞNW?$u@8yЗFR;9yyCg48Tka&x K55 y4/{:pôgm`s] :4^Mi˶ϱb]*~>K*ΚÒKe<'YY5.y\Z7H0| 9Gml珦c<$U0mO8u98ּli wUZ۰Agr[$xv)nys,ps5 " hc)1bDO{pu˚xyAIIq8Ȁ$h }JNs@d>5qmđ۲zUXeikb.f_{s#hR|u$+;^D 8U:hdOKZZ@i}0Jijs9'K۷yVU!1Kw;wTdZcqM-;pj{sXO솹W8 k-Ge_x 9ۆ{x.=+0V۩!@zUkfK#OSZ9#1ϊ,\ j>MǪ/tҵR is+}:5۵Zqe{Dkl3s, CdԺ@`cC+"}htNzP.mcW x.ΙVY'04tlqv{D5] ;;< `܃:= (Kii^Cdɣ>dh ›  tU]ZyieECWNtZnUۚ\HnF|>Nn+}c6LesXۮ#-i쵭VF)hᶓ3/89,!D |4HٿQ3]ImqvIY89NK4^N1ŪFHo\_d͵ihw nmNA>~*߲j'!;'68e&,xPI.n$7K$hI;]V_fbA?^twN; 14(rq;5#]w w %:`#BBeEut0tVu<0x!ޢ_]KI#^Tkɓ \Ay;~mjOc}eygX=Ο$1 /X̣u?).'6 ma\HM;~CCdncXs]E-gl4d0'R4ؼys)sY?SUUq\V_X:S\/'%jI$RI$I%)$IJI$RI$I%)$IJI$R/ ? /^$,5ؒGI$R˙Ӳ?_TWk))I$I $ =@x n_O-lc]xgA`]B׌2:FG}xL X]/Wpp]{aNʺ+*j6TFåuQ5!Vl쿄_ 9Ӵ#6έϋLQu;v'>h}axڈi EmIՇ;S_a*Xz=mf!^D{/@grn긅̱S91^OsIA?ud5i7;u[&\ݤJȳ$Hs"CZ|uYt8XKt3㐍b-.6CFLpA:|Q/_T>gg*KMu"`p煙ǀCCC]X^C^P>Y8j%O}ywX׉<2P^lABǗ6/:iʫpt 鷰pPܜ `YDkg_5E@.6$dh%[k 3Ǿ-gU8̶YͲ9? NCsw:M-n<hZ?ZocäޟV=lÃln,Fv-7Zאt:iʁ) ljl g|Ê1CKGkr#[e>i59(Kcf=%/0ّ!֬Ȓ ̍bAiV5ȩv$xt<58C)iqmc7:m9I0gt`I̞Z9SƌgV;K~i?C&nχDŽĞdW@Y:5'.A$05Q F; >ZƂ\>dkQM}qyi.q:G<V lkKZ@hmv@Ps_Vצ x6gZZ#*ȩawq Lsj`bddn;q1ɏ-|k7$s`;BZ'U̪YM` N97<{sʓ^܀NI2|tN:=&'_sAtFѣul 9;Sij{K5ӝV5^"nu1q]|봱 !Z4UZ x"2N+8¶4 7jvf/{P~RYN>H#ggۋu2NܵoLEyluS56`FgifΒt&G9wzΩpN<<M[iwvsyxw2ƀ`D8H )H@+gammkƷ򭊾ɬkXg^w:y\('[l8VCp sKdK_:i-ȈGл*\Hպ?4:%N:AI oy:.vKߢറ:u:ITijdKhٹ_.1-ܞӽ2X*۷I.ԴvTn!mc䍵ч O#yYU~ݏ ƞHOWO u-Tcf9uUzIc cIhdG]9dQqi:]q;><& <M2&{yNVFe카W.-sD85+#(o G )U̪ p"{]Ugd8 udյ<95;c:xApчHK룋C)%v>++ sMu;ܯSUܳ1/ D&ԒƹLo)Wu]~rۋyiӞ$ Lv5&cMY[7(uѤNFp9er ِnhuV=U-cpꭀ2ATͶ8ă> n9cvh'Ɛ6΂J]^,ĝ ͖NnOh,hמ-ճj.ǟ 7n ?+AH.h܈1ѭ:xx-j9Ty1n1lBα~J2IE $I)dII$RI$I%. .^Ey-ss5,{=Ͳ>Ӄ8+eAw􎛜dd^א:Y;37cz#Kkips~u 6[ 8nukZ>#Vek[=w.Q^Isy-q}FwT%,4jF'hk}oq ix!m7S>\+i%"tDv(+ϲ}\Ȁ 8uD;81N?Oӝҟ]'k BW=;q:g)!'zZ Ӹdĝ~JG h:td ׻mey;HqgNh&{},=>$AEMs'lDO=R=XHͿ' Sc;=òʮԂiST붷OяdFl+In7YpF h$y,e_IƄ~$a=HsqcNC/ }Kver_d;{F 3{ ,S.N5 ##9USYS>$-jRӵEZ=&~6LL j s Q;}xK0d6ϴ5P^ԾԺkz{v. 9@E#8S;Wx߷7&iq$x:o nWP;4V+:W6gZFOhPJzuc4d4aSc(oL\WnC#h9E  ]G.E=ϵ1cAph:P M.١u\zN t?3kѴ.rY[G  -L~+)eٱ{G]y@עH7=#6U2E>8q[;moC ʶ^[n&G?eZ,ȱѡ0'Dmll$I I$R(*I$BI$I%)$IJI$RI$I%)$IJI$R/ ? /^$,5ؒGI$R˙Ӳ?_TWk))I$I $ I$$I)KcẄ>S=+D3-cJuf;2vӮ@+øӓY=kim E!~?%rC,<45iu Gj}YEWKoѵ<=apWc<|:~J#ȩ;]g6ȗL6:tt|4M26Klm ѐ11kרcƓ nڜ^"v⏃+]lk\FWcUϷWĸ?jþmLNfMkcI#}H>EzƉ@:|aU*zV̭F+vխq,c@Di<\=i>!c wTa7^=ϗ\[ 4*ymk1gN (zN=]wnkmkkm%]$H32n-0c|O s=kn\;:Wz6K-mjo|NwX:ż你#EO&NvD1CydwwZn_J˳4Ra͟C(IJʫȨn,vSelC]E:U􎧙W}R?҆`F'WvWYųƻk9~|*~~PD`pesa줳sXA2= ~CÏַBx ]#Z=\XDv=b!x c[{F;]VHeӴ4;ăС– [k'Ğe+4# [4gtň|v9;l>֖|^g 8Y[e%yNwUɬV]am?2œEYWG٩p 9avv ѻCb?ssl77qqOH׎W?(85ѥ7s^@5}xֺK//@fE 0x c/gomY6=0.L蝭R90Y}NVcyr<lZ]c$5>. }_Ƭ5π%7:g8g,kml7`;Y-H~8IqaVEOK. 9vߺsriC䇺I4!]Y2AJw mvu0xZ!>eځGsATC %s^Nkm4aiBI$ RI$LQARI$I$$I)I$JRI$I$$I)I$JRI$IYkEx?%!gĔ:I$\_.gNQIHI$II$\I%)$IJI$SSc~ ]X:h~F32h'kip tqլA u3UonWN.Ǹn4I-L#Dѱ:mf3] "qoBpjg[WU]wejxtv3MRmO mp"GS=FܷUz>N Xe[X9^smx.[༯aу+hmY 7k$?hum>.kŁc!psݿW6[u';wVq<'ٹӮ8yu}Mյ9ր'y ]~l~xwu S[h/z"[Ae5`Kְoϲ ?Lyk8y͆INP˴ A’&űQX7fԩӫ*ֳCs<*=JԱ2cpn?}~s@;`'4AϿ6_&[q@0x$&O}&?sXA$ ku4b41|Oj9Pd 6v#GCa-..ngů̬Ҷ=\;hG>+b0s2l4NZGi%qXr׺ptA~hlxBQNysxw1ƫ}7ՠOjDE(u!ޔf࡝Tl{ѧ+97c]%s׋-ӹħŎE$'ROwrI$_>)RH*I$BI$I%)$IJI$RI$I%)$IJI$R/ ? /^$,5ؒGI$R˙Ӳ?_TWk))I$I $ I$$I)I$JRI$I$$I)I$JP$'2I)E2IE8$$]%qkANWz-59i{A3䳗K^I>q1_ 8אqX,n7Ȇ9f(yL]¦Q֘=Fs+zSSCOnPXok> s?W#K2{:=UV@x\]{'G -eZNg_c_SE gh'D?k$j3񱇙_fŮshЏ;r&0b`5L!0_muU9S(]niDșkuJQ\LdTI $I)I$IJI$RI$I%)$IJI$S)>dbV1$j!c{ԅ#>G YTom+ϰYQt3c$x(gfԽfmŤ"};X*~WUDn 5zE,4 BDu=3][^Ǵ16DyE1kp7lv--kms*Q[ޤke%otKKlpc׻{ \6o3(]]UB\k^:K؏OVXAG~3׹7:w' Y ek>{G)6&GU ;X$,ޟӲɳ.sX*" 7CtB^@봿Ox'`{K^Ef@ WzGQȭ-#>?FdEjiqkZ%:@(VIWnX0!7v Ƣ 72<Υu}3;+6eҢ`|8CvVGJeSEP K&iu:cK[,hs|8 $Ƞ">>&`ĢmZPpy{'S`QS`h'ti?y-c5[ZsKsѷq'is+h۾9a+ly:HًܴC}*n-ɽfCwhh%=Ň|7vY= kǢ(d;lğ-U;s˿LlSSqq H unh>{uEQTiw:Pis$8$%b5 nC+&z-ݶ?zJOu$K?F4۷AK X p iaesC Oc|̲D![ Z`H*T{y{ RlǻI$IJI$R(*I$BI$I%)$IJI$RI$I%)$IJI$R/ ? /^$,5ؒGI$R˙Ӳ?_TWk))I$I $ I$$I)I$JRI$I$$I)I$JRI$I$)zVmߴFQ]={|lZom{ AD(&-%[ceI4I{ٸ@(Z\:=UgOӝ&kǛNJ(oY,°RR*pc2䱱w${4ɸ in8UM `Uqq7I(!!D2rvݘ,x2gq$ƨ ̵k[.s"ьNk|J8};kn?{~k+qmNZEnV;%W~nH4N*<ڗJ4QLI*)$ RI'SEHStƥA%)$IVIO>) ,0I" H%I$,>1H)o nirHXa 1:p ^JvZݹiQI:MջC ӂ~I4xHi"3Vug  D(ɹX0x4d .,c\OZ/D-8 OYm9ok$6Z<:#eu `O]\DA#X[n;n?I$ EoeQSN6 u:T/5:i# Yк;|.wQ}&l1g=es=q<ԓvnLu)$I $I)I$JRb1EI$HRI$I$$I)I$JRI$I$$I)I$JR?%!gė[S$JRs;vGk|;#5E% I$R $P]$ArI$I%)$IJI$RI$I%)$IJI$RI$I%.9D0ܜm6$ӎZѐj̫ /ĞuC-ԦP :x ?C/eպkUx8 q`3 "eO:(JdE*RI$I%)$IJS *dB F4ü1&JI$K:βߴi8{sL}A:.th{k}:"ٚ7TtOM5w}#=ﮐ"J/[~5֗98@΍$" bLLIԦã " Dʃ_B( DQI5*I$RI$#> C:d=#gOI$P7d3)_:A3槔]fw*EA\[4iU9Vum$G2}1eMP2`Xy&XodC]bAGmC lC*&"|f]ѓ϶e8ϴ0~$xx$v%TƬB966v wA#P[Tw3>*,q 4k@J5htԪv[ ?/=ĽO ;D8 tg%ƒ$h'`p%9#r)I$$AJI$RI$I%)1NI$)$IJI$RI$I%)$IJI$RI$I%){bKB_-)tI%)|;#5E}F\$I)I (.I I$JRI$I$$I)I$JRI$S' |AR IK$$*-<ҀILSF L.sq+ļkD.yTDc3' ㏕Y& A4#$K)I$$AJI$RI$I%-)JI"t. B9'$BHPhh$H%.%'*qgU[)T6aM5O)t(m.]Yr;_Nx47󼬋2o&k$ꪣFvA AI$ RI$ $$I)I$JRI$j!2q(+:sQ$=gPh]i}p_Q)1 _>;MF}EkH Cx0|C+tG\q6%N+Vn.ֻy?#gdvEX8e4zV;jp#'RU >k#kD#TUט:dI44u Paw_m.'sZ lh]mDxNeodZN]ֽܒ $ݷ1k8wv~t(I"$H%I$JRI$I$$I)I$JRI$LQARI$I$$I)I$JRI$I$$I)I$JRI$IYkEx?%!gĔ:I$\_.gNQIHI$I'̤gPk veX Fkc;G+a{V®WWd_|//W]8/np{ jI5it?O1[=BXh0HDĮCVB"M-svoտX_lU{iɪ2Ѻ~NLvefw[YGÔ㛣ey~m?I%)$IJI$RI$ IL 7HO$hQE:_WqN-1'j/GZ?už{v-&0I:{h^}[{k5 Z>eTZ5տ{\8-+S}X{rVv>8`\\O!;wUrA2Cm1$ L.b_[nmDA[ُO-{cmaږk!wQ}Ium2&]!Of#>Kf [}G~JRm{ tw $B4HEy BMlLIEo$w>cdZg6?pD}jʻg[kv".t﵅b}Fpȴ$m~Mg X v$҈{ޝ/P ]h7CId\hi}82\H5^+072ӓxkZ|:?>)yi=E"@P׺gJP1/==ۦ5KwGoWу`.zG:Ǩ6Y::X=)x5 |JmYcX$ _8# cL~>|@[}#]uޕ C@yɨ2jݮ%9t/+#R~\]}u.,е0AX~< ?_*Gn6g"^zm))RI$Ғ-tJpaZ| BJ\=$ P$JR{: RI$I$$I)I$JRI$$T= ^1rks۵SX=\FW"FM{O7uez};`3FYmSZՔ2K>}DX}eB[[+?Aw=i̟i)ڮwm59R0@EO /ٟXc8׽ncs,s~mnXlin⃬59fvt>oe5=ϯڲqltnurَ]e#K#.nZH|1JRIVI$)$IJI$RI$I%)$IJI$RI$I%){bKB_-)tI%)|;#5E}F\$I)JEu|*]lȩ9 >Ạ( ,e8;$]tP.ȱ@Z?e}˝P윍nlAvA@n~::m5v4+-gu?7bZǶh`/&% h_Ys)ǭ4[Ik9ܔNmTqwnMOU+6ShX8 :ݑ?K +I%)$IJN MyB\{\ Ap|+`Z$h~BP?ifk>#H#6- [<{HB gJzhuZhM>Ky׍ |T΅^=cUaopp_R~{C i-vOp|]:N>*c/gG4+2*uOnX-$D$ J$9 Z,pwf=mV >\p I34_F[s1w;LkS[/i(ft~Oƈj$8{$\N0$Qcw9ָkZqn/>Gf>s>wzFi'Y`r;{si>K @_?>"~T~~V7Rſnɪ־2A!gVjȬ^Ӥp.۠_z̚"7QKluRZvuwa!LK~.\znpmeϜ. 殒\p}MyR=[Y]5?ޝoia"ǩr+ʫ)3ˢ[a*60Nױt&%5.՗s tb1a$EpƵ1UX:R? >CmoW5qi~Dk32XX [= U?V~̚n]vvC@ cg`=NO# #EҕʚzgMc̓Rg+̗fN>u;s./kRSi$IXII$JPN'I!I$ RI$I$$I)I$JRI$I$$I)I'NU>s;M $Ƕ%0>Pv㮹=VE'\y=ȫ",ͧpֿsY>mY[뼁2p_$;\BvGNO10<_99ߏfӤVƻ0]]+i`09Տ+GGy;Itu?R=H~u_߂u3֝>Oi¯?G"yEVpZ2rE@>w`t5561I$BI$I%)$IJI$RI$I%)$IJI$RI$KB_-/ ? $I$ws;vGkJ@I$'je&rT[}dy ?.\J5yJ|[=Lk_EX、(RI6+??0nIo7=ϏWG-0F^~E87x#]wr2Jzp~~Ke ͖649t:!Z]c͚k.sߥGXWQmos`{m uj1Ik=G q3aW `qtVxeN{ w;R q_Ǿ(;v{gX[rMw) @W}dut<Gr\@`/kyu(_˺A;Q<%/]=u? 1ִmh: ]@6ޟId΄Ɠ@ߚEy'ƻHҴsZmT1GX=%,}U;Im?3Rt$iI)m`cRvV:` ϴLj:5krMNMqi{ ^ak˟^~*~b]`}.3v%s|5 Ʃhh[KEԼs\uI۾ь02[a-BtW/ v%k7|*me<4v@9yjhIy.;v\ 9cꬮUXm- ѣvx\N3 '{7VND| $I)dIc9FkB#lLn}k[cDzS,阧3}55ݺmEzL˻]YEA ~w#ot:EU[nĐu'_p-$k`PkkuN~"nF+KI״~<кgNuK\7u5pz~Dϥ<4'Lqr+ޫlր5jE>rr*Fp}ז^4$H>SJ@-㺾o~9} tpnC_}Pֱ ?r8ϲ/m#k_3kRn~=ni;>K{V2kά@ۋXay,̴^[@qgcl"q- ꮛwN;cmgi,3;׺/MTmws,p^h mQ:=&|]k %=wʂI)N7OySeQ^ gnX6#zLUcy\e˟I%)$IK)"$IJ $)$A*I$RI$I%)htNwW41ªi >:o{VK3!n3w(e4^a2Sw1Ã_-^;bto>V_ybFDD5RI$#`g|Z͖L ;ǰ)`dgWa>Az_D87t}0 ^`CGz;ʜjc=찉qxrrqzM%!iWRۅzgmIKH$Jq(ae nx< _^NE9XWmmPͮCrI$$II5z$+]Y>??Y ŻLB:_YyO0~K Z'FJRI$I$$I)I$JRI$I$$I)I$JRI$I$/c_l^8IYkE%>I$/3dƿ˙Ӳ?_TRRI%);S)3J]$QJJ)$ I))w;c^KE*A$ '@3从1`Y(O~HSNߍ}A(KckGυ H.e 8g>+}`os78m x.NY]U6:}vi<~I X}?TA=uRvf?Q w2*ku=e޷c}5xL1&F6,WXbLhREQfeQfm%Dv ̽:i9.]ygѬ4sl,nْsvpyͣЬZǂНnc-u"Dix XIyS WU o2 vAy-&a`R#kt;N'!0~~C11 OU'sq2NEr] > EKo_V-~;͐O"11eT5.Aޕt L3avx66)-%4\ΆYKDa["J&hVYeik}3pe{RtÅ͵[5s_XtsPz9oM~ܑ[y-4R}pA-sjlw4i e %RK4C[Su^a `̀OU:eQ?7hsoJŧ9U$h{h?7}48<QIIJDmWBn[ּ4YNn6^;/ L7"t˸wڳ[hcCO .;D8jRS1sx7B\ţ` /ۼ:I$ Y$EjNBFX8~kYz˜vIegRr|oܹtJ}E00=QM6[7R|Z'^yBk馆Rڙ[\L 4gp@hp{-d|³] ˫ph sِm;KN;j@ NZk,Mk# Z(ZggpGDt| sp4[#y0n?T\Z}2CA)nCmBG?P$֋5v(Y]oagX3%qZCm)Xkst ̻wmD* ͦ{t$ww8격qq2s/n>-n7\:sA~!vQ_WN;&2[foHɭKsmk ?wm ph7O$II$$I)bE$V$I)A:`$$H%I$JRI$I=䤦#cahz'DnnW_NIѣUFq֟/jޏ+:`0< a7}juك8]mvѠXi9%qNܦM\:_ޡԫ"\|ծF^EH~+OƧI.hpGaH Q.@piӛ.t:8Wn@`;u~c ֻkF\>}tޕi=m6G燏ܜJޟҷ ݖLZ9$L^I%,I"I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$/c_l^8IYkE%>I$/3dƿ˙Ӳ?_TRRI%) A%I$)$IIK)B%^b/C:nhJ(rİpeuR:{褒S DK oS}٪Af;h# ~ӃUu05ZכRmvX+onzo<,Fn׹v -qDsK,1 OJH-֍ 3*gHk kK1%k>u?T˯uEn15"SenǤo?y0xY}-ٕhu!q ͲÒGzNq 1+/]̺Y|kEV՞}7nq{Y̮14%Ve_gLFW4솖י왶J}[,lw^i\Y[}hfVNm}Ԁ x;]^XLcK_/6anЏ gqX`o#&{$/Qh7ZxPz34mW=v։\eۍcZ8Ay/hlۍI;v'+)Qw0h\<9FA~Z61x߁^C#7kGˁ bOܽc ּ2֙kƤC!/ɸCA cQȪǹf l%nkd5ڶ9ªG6G1:X9I oaa{T.ķ6b>kꘕ9K}΂ݭ ?xO,p\;$LeB+V{Z7ndƽ<6c\6ֳ=Eցi1MfC\QtfGTc~7>lGXjߵqtn""xGV $ Y$EjNBI$?wֱt AxB1ًӺCcr:{YV4scPFcpyyh?*wen61cq&;yy-#CywT_-qtv-'t_쩎$Z=R+I8>6Oкxwl}GM5bdzY&zC 97vɮ`~>fFG(2y?ɟ5=,4(-,v.ᨰ84o`ǏS*R=G:WFCnݹ̗i$MTs6;-~gxxI"I$JRI$)$REiRI$ HRI$TI$$I)K[}k[5g\O=xޭisI?NGGkYC?y׬ߵu0} XGqv}QcԲI[1Z8hH0feݛv]oyD qЫ}/[s^c `N*UYu+>:pk+%[؟oBh[5-ǤmwFS>XܫZ(M֍,?X*)-6eL AGd[Gvt[wu,nmQg; }<{|'X'V[}5R#c$_73o~8sA5UYE",x@#H*֟}a恨scs]=-<3.spѡ'PAف {? }mDq}ܗ:855"{ȡpsk;C <µ콍um%}'\)Xٞ3}L҉<0j }f*03uN'0d׊l!,JT閹_pbNjW<ڜa-1ঌd%u l@hs*mYn$Z}V8pL :n̏,{v<3d-p:E^;K&$WD"_UGS0'˫8  FqWIlV$w6Ӵ9p D|m^Y&kF#kA+νmw V}dv ~uOtI`*+^Y75DэWOiopa- ʥc/;qXn` Yfίtu?$c8q@ky?r|ܜ˳rdd8$kmU[tc&"ޫ4쩃uD= _=dulfHO K: v~2KHWũ)hdOfeTݫ}R%vN~ hi1ePqn$8c.gf`ŜfAh\dnlV'WdAs_T\ĒI$HRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)Kė[/c_lIOI)K+5w$IJH$IAB镖]tq*jtRhyh`"+ ׏%b'ZuMe0}+R&꺬2>_rʭf7x!KpksOgG ;!{Y`?*e2Wnqhh OyU2/n9p[/Ġ.`m4dt O.V0m#]I<\܇NݨouiJq1 ߂;km#YF@P[l{VzM^c׻Ct[z>\ sh5 I}d} pciݭ q}b]8OxsaE$HI$ XII$JPN'I!I$ RI$???)=i9r3 dgNk[@y.Nlz񗍃l}z~?gqאI jӯ"hv pbG UXM/Ј#D")3 . v^cn}c@br\9ksÏĔ)sg/31kHctOOr_}cvF[ѓ٣Ghu.5ʾr^+?Dߜ*귿/Yre [4-.$4hu' <}/'3ZA-`t/H}ZF% ;DB~Q1C#o?[a4e^C.Ma[\EuAc>*.J]ʫ2go3;\c2Mב-FmeWߌLl&$wn&RN_PW}{g;)c*NLyyGu?ґsRI$$HRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)Kė[/c_lIOI)K+5w$IJH$IArҳ.S^kpuA$O{[n~Cs9jtQKl` GWqlMn1\EC}Ց I\ʁvVħƟpBg%ISʐlm1|(hNTcr}ژILnMk Iv-NJr`O-456jQ_G'B+)kw '~oH6ݓo9$ăR½mԧ.?8LpjX'N=[䢔ur@k؞ˀO r%C'UvM4nt/ ENICyUAࠒH%dI$):d$)$A*H IOIKe`]e5:Yh.u'@<WQhǫ?F4m{Nכs鵶1Ůi5仾Ա*%)m{_): Zp)ZV[mfHω QƳ۠m;Byex%4oEyՌO%ƀi 7Y+WZ ?Gh7TM[xT{ĦO[{. V@ c?k$U7cLwK[],W6k-cGwgTuM2v}'DƥcӨvqH;/8ַ浿EC~#@1$MJI$I"+JI$0NBI$kƉsS.I>ke1COa>)zStn:P5lLGStk zځ+ cG;qTAmژsv֛Nӯr *..kZ{Q57kQV| ,qp$4'ˍ{ ]YXxUQxeěyrىp${:{t KH?{. Nqtvu"{+n,ޝ p|SQ S-}x!q_뙧YC1-ղ[&ooys\Kd$]˩S> VڱKsTQvM̢-Jәpq1Zk\o I?Iv$2%|ט}mśf(xdƆ=7Oefȝn4oi*I$R$+TI$$I)I$JRI$I$$I)I$JRI$I$$I)I$JR?%!gė[S$JRs;vGk|;#5E% I$R $P]$Ar~bl' IGt=YLgڰ'v;Z.usMȨ ջI:ry..fVLwyXq"t<:DxnMN x:/ ^yWEh I@G Oc] UwO9 \ !HW]@DE^%Ůlȩv #ۏ]V j xpٵogo$ini=fUml˰n |Mtw{CRYhq3 ;|Zl1Y !%림( 9ם::[xsd >XH|8_q$ӯQp;M$#jg l q ]β"bTCpP)KtOnA>ETRǯ,཭KI27xHP"_X>T]z8W{F $oEhlLDGz+&XHAq'Χ! t„ݯF[ls^ 5]Ygpև 9E<P#XhΣ^ʝi#nơG/!uu͗5$ I2ec혟,Ȫ<χU\G}b  $IK$HRtHRI$TI$-.×2'lm,:ԑSF:=.n 4s#zuvPcs`w;0*eƒfܼ&"CklwStgLODuXu)_S\W_&cQpIQc\ |= GPɕCq:;hr[{,k&5+d.eMxu:*T5s*Ыn뛅]]~is/xhw]Cqwh<ekV f4p hήZI$$I)bE$V$I)A:`$$H%0ah֝@Ѷo]xq_w|+Ʋjk}gsǵ6OEP8ԬG웣w{c:VQPuC96|nEoi%BL6 1#nv.0mc lbUmc4Ƌ.;%I{*G>1`iQ'd֑U 쥤. 66 wP=3;p?gǎN&o>4x:ξESk_.ȷ4mkF;.Wu߮}IO9tY !w=?6 e/טzUtc5RƁ[!!u1 s)gm:>S~]6v!k?5衵>1tecW$[_sk=w.oUv>#:}N-@oj?1I"y^ί$}`x}f vO?AiT.weRȂW`eA̪[n_fMwcOn=:I\v?Q:q2׈~U~Sg@7R]b+:ISH$oU{hK!i:BӣO3<>J:2 4q sd\vnP.4l2|ݣk{fA?\mas`> x[#ma-Qp,Ʈg{x{F4ǞO* ,Ih(Xr2Icu2u9J6c.rI$I$Z铤I$IJI$Sou :vuYUF)`f\;iy.+#P/t0ɧu,D#K},25I$R$H*I$Rt:I W:N eQ5MvSjf6 .p ӍXցӺMFuF lc|Co{1t8 xkEጆ26Tq^ĠCTC~Λsq[zjGccC>9\`<Vu4GTCZKHݍPn;C%O!U-̶NoGgn]-۴vIEE;Idi1xk̲96ƍYS t->ģR]kHqkv@uNk;!kYͿޥU >i)}_ßd7z nq `&ȕD.īque4k?u.ku]gdcYz[Zrr]r8!ԨRI$I$I$V$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$KB_-/ ? $I$ws;vGkJ@I$$H$I$$q*MEh]MyLw)VOSՎZ%$ @L!`d u6ƠcEcnU-'_d8  ҴӍ1jlTdϧg>|‡ծΪ`浻?8n_}/;ckqVAtiN'@:wL..\C0鹱%\Y\^=A#B>S液7Rg+{tRS \ۃ6=W<_< Ѝ(M-,I|WǸdfY+)%Jk&-l&yzo۫~;2y5hDC|Ogԝ_UNsX'55&d/蚱K[ 8s:y8vc dvl7]<\]i= dUƵ%$5;it#̤mP[:Sd2A7*H?k[15hNk ?t}Ue0Q2cñÇ7B>am`u브h˯r%| +A _>G%C,[OcbA"gu_W5*l}GO p^7׌.hǥ+,NZ|,p>߄JuٯF tj~dF N@Q ne֞OeK\N0Ѡ\OԬ9dHdC踵69Qt^ ֍㾺VXO;izXG2m8HOu_)[/.-ЙIfi0'񒍛{5s3-%sS2I c 04 >}>Ҽ_X-&#w&;+1n1;xYqI${ù.=WsE д68vk)RkXZ@>փ#.7i>زN#F; ϊyX$MJ$+T2tI $I)I$JRV>k =9(FBä: ^9~H{7'Ҹc@t&+AsZ >IZK}7Ow}>Aƪ(,nsrwE5OsF#1g貫x-hO jvMN pp#Wvqs=h ]̪V Kmocnj@OױisĉikD|׫0W2 ZA2-}"ڮ~]`0W)$AK)"$IJ $2Yckoy 5]7."p} oTdsùG=Ґ&].k鸑\TDZW1k f!D.yTǎ,+-0`ʐs^IqsZ}Qp}m /$[`O|ANc+ 4L3jӧVaJ57]]r w:LOam_C\;$5u,Մ zδ͕5s4Tkv.# m{A}5⌎VWڠ> ࣓-"a.w]{^D~kuw ˳ɵֿq& :ˬyv-{ ?vW;ԺQꖋsu4 4BI$I%)$IK$HRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)Kė[/c_lIOI)K+5w$IJH$IAtII$RJ[%>Kg`Qjn?nVHH!zOUWlvm\7\EK`>\[S,osL >fW}F˛.)>F II'Ä=|Ģ5PϢWs6!j'U.^VUsIJpq[,F 8r?>j̭-K[Ēfk/ͱWO<:6/oq>gu<sa=L\NMKnxnAѱ?8SfʘwRDp7{xkJ3Œs>)~i8 ^I$铤I$IJI$RI$\汘78>O'G?r$S}Sd'e[uVNM'VBh,~]<5վl4znh$[3 <#Ej q.Q*fF0{uVno=0 Y@I'~>1<9չ3C􌸮tc~elG(p5q<ઽUdf5[/$ي71SzK~)Zحg )bE$V$I)A:`$cq;tͭD;V?G5ֻ tP׸ooy$qix88=ۃt2je^x#%faթDzƭ6ֵ G:WRIh2cZmH8&",GufRI6@J@2 i`Eo@ьٍϥdi 7q*8/["ȁku6D@.uew dxp 6e􆽖2vWD.YI=~y=$ I.nn g6P<+Ϲi4ĝpaX2[扗h$yɆ-'g~} C$5`yB=?\n͖6GW5*RϷԽO֍UNRI$I$>bss;;.B>u9޻)mڀ> H1mm#P|:c,-c$p'S>Ko;*{E>I<ָHUV2Ym${1046C73$6\yDtܛ}*Ṡ &I09Nkj쮻Mah?zzhYkQh>c\1R p1,kӐOhilS}*H-EvPݭ2edyU_;]bkZ$x]- >ѷR%Q8l{mi̫ K2TO_}/y.{̸*TO@)E$G OFèRwt#H)I'I!I$ RI$I$$I)I$JR gu[:lNC-k \HP鮨1.m@FA 6:kHIG\CՕ<\}=VI_.vu|ssr|ejuEMfżxp Yž+pu!̦u 7j緉$Ǫ$uHѭe7?[p>t)qs9tu:&uޡ,uuG:}RVTՒ} 7{*eI$JPRhI$/3dƿ˙Ӳ?_TRRI%) A%I$)$IJI$RI$I%)$IJI$RI$I%)$bZR = N==c p Jh5ۏk*O. jVDȹN5#cɮ=|vTIO݅tKcRpl >jYL#y?bda3'U{yvN4cϣ5haNV㴂Ha1K}!s-ަsHschVvWOmv7i:" uFm$|ʤKG?T NI$HRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)Kė[/c_lIOI)K+5w$IJH$IAtII$RI$I%)$IJI$RI$I%)$IB*d؏ (50N| CJNӺ22k*]XҬɩ57dHo΅щ h>._gIl5!O(QB݋e1-]][Dt{#f .c`!6=6V4gYF]td] nt"V׽" tp }CQ|f5Xn~ߨ/WhiOd$I$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$R/ ? /^$,5ؒGI$R˙Ӳ?_TWk²WKrn{ 'S$iZIMoWIO]4]-_$qj$Iv}vƛūޯ$m]4]-_$qj$xa7oW_I/iZI*=%MoWKozJIv}vƛūޯ$]4]-_$qj$xa7oW_I/iZI*=%MoWKozJIv}vƛūޯ$]4]-_$qj$xa7oW_I/iZI*A.oWOG__I$Q<& ƣƯޯ$5]-$mOuIv}vƛūޯ$m]4]-_$qj$xa7oW_I/iZI*=%MoWKozJIv}vƛūޯ$]4]-_$qj$Iv?}vƛūޯ$9%MoWKozJxc7oW_I/iZI)]4]-_$qj$Iv?}vƛūޯ$9%MoWKozJxc7oW_I/iZI)]4]-_$qj$Iv?}vƛūޯ$9%MoWKozJxc7oW_I/iZI)]4]-_$qj$Iv?}vƛūޯ$9%MoWKozJxc7oW_I/iZI)]4]-_$qj$Iv?}vƛūޯ$9%MoWKozJxc7oW_I/iZI)]4]-_$qj$Iv?}vƛūޯ$9%MoWKozJxc7oW_I/iZI)]4]-_$qj$Iv?}vƛūޯ$9%MoWKozJxc7oW_I/iZI)]4]-_$qj$Iv?}vƛūޯ$9%MoWKozJxc7oW_I/iZI)]4]-_$qj$Iv?}vƛūޯ$9%MoWKozJxc7oW_I/iZI)㗱/ ? .7ozock}]yxVm {^ v5%=I$U$TU$TU$TU$TU$TU$TU$TU$TU$TU$TU$TU$TU$TU$TU$TU$TU$TU$TU$TU$TU$TU$TU$TU$TU$TU$TU$TU$TU$TU$TU$TU$TU$TU$TU$TU$TU$TU$TU$TU$TU$TU$TU$TU$TU$TU$TU$TU$TU$TU$TU$TU$TU$TU$TU$TU$TU$TU$TU$ endstream endobj 47 0 obj <>stream Adobed     $$''$$53335;;;;;;;;;;  %% ## ((%%((22022;;;;;;;;;;g"?   3!1AQa"q2B#$Rb34rC%Scs5&DTdE£t6UeuF'Vfv7GWgw5!1AQaq"2B#R3$brCScs4%&5DTdEU6teuFVfv'7GWgw ?T\Z0:FC$nc\d*uvU#ˬ_OʳGػKGܲW{ėqZjymDߕGyZiQOޜ9gء=Ғ^`evWe;`D4}w/:;ʸ9F"C'~ KgTޙ"gWq&/_s_R\Su`Tޙ $>G<^%ήJGOT"ZgdGi}Ob?UUuV|vIpدd{$>u {̟JMޱ̒Ha/G?w{~uq㝟ּjyx9/}N{4ϭ]X5J}hX^{k0ZG?؟d^%ď=p뺝dWZ?$b>듸^%)c\M8AI}'/9&fe>ɫ Հ!έ=8<3{-- Z{>wjߣ:N]hV힛o W$}x.ϭ_Yݎku*.NFhfظaڟFIp'_e1>b4=/cO$=sf>{<}.꽟^zbEswxٟ/7#GzF'Ϯ\?4Wxٗ/7 [jt*c;_;GZ_x>av} џas~^̼BIy[ְo_F3#7xؗ.\Si}]-e+'7%^̼%4¢Zl1iVq}%U8m6T\>ފ~N,?_w$6<4vO,JYhud?_}~lY_ >KpЈzG%?o UuAn0|U~uG,y\0;{{־) sL (HmďЙ۽}?b~?^vzcEQ#߮X\ihuq!?_w.&J*;{FLoN55I@$9g?Is0&6JO}UYc^a =֜2%Ÿ=`]T=$߭ (qfdry}>w_KD_ל>Q n>Z{Tpupl8:=;L]fK1\?Nj^lG_GY`'@.;#/Ln|vImu"4,%\a\۳g#ӏ qpIsu'gdQ#o\a\\zR'gds=ëԸNJxѤcsdߓS#BuIq{rz1Ln^IOx?ޗW^%Xοp*? #͟\a^zt0:S:W<}zKI.0l*Ko\ia?r#zW' .0I.l#nI[GId8R /FY!"5;;#qpIs뙂s w\#VoT]fL{*σѤyfRk;GOړobڗGw\ߚ3oYr9.0 .ygWo.0l2Ko8u{gQ?j\a\O%/۝GnaR Ѥz {?%!3WOڗW^%Ύ Y0 j\a\zTXO{?]Yt? R /H]$uguΧa%^%~ 2K?/Ln;2K+*KX:Y-wA v\a\\Ի:{{?%so}\ Ah QF$j$HR>zTs}9~Ogo^ OةC|~E;/9CfݪDAcǷu ~ %o?XO8KjŌ䘀o嘄 )C%$Ɛn~c KX50tkZ3X}~"xA@ADImG?ܙ̬+8'_ʵm^ CiH'==U@XO)R{dp۲p,XnSKC?سz  moKb=8yMu9;`'Q =]==0氷O:U   yeKoCGH^ v6s I_U;V@C{ a_5v#U21̓oZ5׸>J\SfM`C[0u1b1r.4*5o~w_vH4W@iֲ፵ 5=vq$`c*1ieX {v\Ac|j;̃PЯ[`(Zu=_s=30 Ppk?ʽmk{"F^hvzFϔga!1s@AБQ>E ݫe:guUys.33;OVQ꘮'A/ê4s١-O(7nC-D7J4no$ǂ|^Xxc.jT]&>>%u@--fOjl}uΧV>v$j-[X5c:pAiñP쨰z0F8=2mZġmk݂Z$iς+@c7Y٧I>$cwh7pyF66.h?K撚;`jq%H 8soҭ<f0 Aijznw?â\w;NK7L> okZHYSlieZG c`0r83&˸\9KMc]w:?`-04v+%8(˫i mVa sk'{|Td4 k{q==: dǿlA3a2۔68HuTvK.vq^^j;64]uJрH Oi=s2Հ8x?8\[ 07]¶=˞}~Tӹ]>%q%VK+ƹ6؟PhX25h{ꆻ&L7丶+Gs6$ ;%hs^NOC}cdD'Cbܚ j9)3wc=t *a CXjkA Eɸ`y/@J԰I{8'?ףfbA'|H @$7FLuqn5MU4(:k$HDـ=ʹD\Sl :<Akpq 'N2*L=iu#Rp.ЁLM|;PC8*d@k䵱#ٖL['GkOЩOkai糌$$pL~a @Y f_LZAF7I:y4كh&<.f?s-h;v ]@wSl{B@f|q2"qCNs$Iy-hDv ֒!D 'IM>(>d;"uCV@ʋAAAKH$& I% +#q\TcZ=ݒSGuX& 7>I)P>1:騔by78μqRR'E=؁)tw&`%9<ƧI,CHܸ4hJu`'\ :.$$vHvH=/sA$I 슔ZDƺ TIwnpA E)!>;|1hi8 {x:tcFQ{stE }s1Dn-qpy@:5IMX )Aq?4:#]Z{FglÛ'%0LvNIi<(vIH˘ "!;tDׄ̀'Ouˤ'4Fv.!Omb~'~zs]jtw[=I=K~s G,ѳw˴*j?m?Kf>pO=#^ /t-u?W^2fa6W?T>r+S Wk?Ow+.Y]h`fvVNs/αľpƴs(Wmf_շbd lt|s7ONٝUbeMypc\uqsV0ԘV=Ö`N2@}k4,Rw~HZ_^qjllH ;,kz6 2t USK~Ӈk?چTf|Pd:5k17|v7ԍftZ @Wluq3 c(1 -\k>\,l ЃȭGКY ^E9Pvycc$ qWFL-iX'75?b{FK9=~HLnC-thVCmj ONMC ` f9MAcCldGxAs5E9s^^@u*!x (ѰGt2[26ts~) 2 ( vL큲nvtuw:,e&5=m{vGs1& e.S@Yoz $~Y@?ZN2ìw#d| {x@PjJnkǟ6$rx@hU]|Xf "c^CH>> -`{wse&S'g D8%uLw$uZtZ@ݷU|##`ukhppt(b\ĉ'9ͺ sZc6tKt{X05 1w 3Q}m\^A%b7οhhoHKN;B`sfK;䭻MԻscZP|eI׏%'V@A٥k`{0g̔.b:ƛ>K>[P4IsNHaM7ꁮǙ-&KҩITu!\DZ@]Er-=cV!s"&5|hc& A|cfS[ݶ1>)ҼeMMxsT$>J 5 ;G>6ݎ-YUjxF– g0^S;3%M a7Zӽړp fx&u0 ff4?$kINDC4C\6g0Wxݻ!SiI3%x". '"]۵Z_IxyUS_9M4ݠoDz\hq-#SkeYs{cG#-iIr`k+/'|AsV`j&?؆\}Nd~ NѬ d8?X3ݼ v,{,syx+={ s m~@؁ 64R,к=!?۵weIe~^֖ё㪆2 _XaΪeq_5>ll@c˴o`QuO~[CIԉӟ8\_lfC|).k)hn܏46ZKMVs*+oZ/w%H3k:&`vjߩ\ $u?*Bg{,;[NDn\vG6>caJ6Cq,ëֱh pThZ/e],xby-e=yM7rbabyW4Fֆ#RIY?3Wks~"7z&i.aYFG:i-p9`p5n3[o3䄷+dvDyrfiy&.!߼AD{|J @5>)%.7Aƺ"04CkOiH'%3НwQAd<馞J>gY@$4)#in>I}U'/Opռ@2:y#qI($oU$wVRLiuAGoc4P$IN\49<&t΍3wIJ%B&Q7:Gޘ#㸍oKߚĀ;OJM$K@<)M-vdӠz! s@$ x -XT {D'_D;@j$ A7Ɗ%.>䊖w8Q$)8Ao !kJX R5hN:DhG-Rl`j$蒘M2 Ex% u%5ĖkKsy)k?miw@!̨n Pi'jI%%how(n'P$?ܤi* c@zh2oڏ)( #JC[ǒDX[-tG"'#T{`r%<~{|NHv9$}t|n$GRE!pi3ul$'gIp u8I(HpDJI?m^n`#>_ͱSQor_?hWQHun~=k[:j̧=<l%((AߜDc-Aef9͹wsC-*ny|(u.ޡnUoa<ѡfc۽:E'`GO E8Ot<^6fZWlӢm[UR8eTjxu?n9菀 {~? u0[z}pq4IR sh:%|4WT{FBmq)K(P~wo 0Gq17 v< is)#yEye?ce;V]M.f8 7Jr+`g?mE#3}'O4f&>("{+,ez麏;;GZGN{?r وoD7_Q&['PƼFvhi Nw`iQ :hwG8<|Gj/%nsƽXpnsu@hDKHH@SPW` peL3famox 6-X(6\%?jE{Hmmt}ka{VדuNs-;#i l(يp7acYc5Aq,kzHOhEe5 Ӎu kmsGq+2ܰhdnBls\b4k3q]'Ohdy蛪h#u=5{apPkk$k|L%jqҷ&H`>e:!lv. jCC/H*_h 1v4̮wsT c ۅmZNkVsq/fAnEwЎ좪a f|T'M 0=CXRְG$Ge4 c0YQ}g#$ m5htZ\&|!6hjE:qL9D_}MAƦ t,,~-Zԙ<{jӚ喇v,'PvVu,}iI\D,+ەm{ WO!_.6;2XB@}cA6+I D(bŅ`kfoI&80=XI(;qHR@GN\6:=ʕߚqvhtlOKF5e%2i-,9{݅wT2Z l |IW΢s,{ (] &<:U97 icSv5嵻H`yՍӘԱZeM,kkF:yK:5מbSX'{Q{{+`Oj-1i5$=RH /xcGL2$)c[nߥkWc=Ee]bARn>#k5+:6'oQδo6$jxǝLP!ZX5o# %:mh2 LG?+07/kw4&Z{(jN/]K=j# Bۤ#Alv(.oulVa߅\j}74I~UѦ |#МDu*0$t\;ߑx [ZIgb[;pT3w ue}2,6ZLv Ai;NG[EH)+F~mGQY$ Ѵ׹gN_iiD?JAnRm{bZ>|~ )Fי#%'˲']i˜!v*C [TYMfw6>Bk HLj6c^`'X#OtEVVlHF7jI0@~Ii;t'O nruHsG1++' #'TÄωDȑnD(\Lpl0i"8I tIӏo h9=RC:_4:F){@D|DR6!'0;T]HI68ğ?%!sścoܤ'xD.'l9 KH|P )vx/ wI/GJǐLHѤ=L'Sr8@4J7pո͞;ς`A0lAGDcvpR~8*dnӏoN5B𔔋|y#S'#{ ]#)7S]LR?JG}TNMBA-ZkgO8Ѝ`xscD4OH#XIHid87kxDHA_ *nc|IS-d "B6\Lth9HZ;D!&s6A€kuswsܒ#io{X#uIyρp;0Iܑ~)fOM81ɍ~ )sw"<sHo

+Mg􇏀N$Ij^s_˲?HEl^czQݴ~=Uۍ~ottaʮ;<ʓ[A(mk[`ace^9 ,m{:<:1zU=kN&Q2ߣKG?4:܂m Lݡs@?뮷o|0Zh: >[+"=ڞ[+D[ l;6OWXZ' Go &D4HY}^y9l@#Ǻc̼vwoR̟VC\ ,,H+ܟbY:کŰ9V.GO ,'l~wL Hjյw ~MomVw>RW]"xՄ)ܢ[ֶK~c>)4݌}J@jH.4Gd@'ЧmvhGQ-w&Z M Y}l:o{}TL1fa ,qG%*ø" 4imf-Nփ6iЎ6vTKaܞQdTH=z17j5ahlfN{q#XMo﷿!xeiN4"t74Ί 7oi&cF捜t7=%kuf^64#济!ݵ5s\\TݾKl'1oҰ@`F= ?姅]Z,#Z1!]r6?ChKS"6X{YiUKC w\13Z 7eM ٵUMi|H kޅ`5Z 0 V&Hq<цMfk72Dj+AD!w] tXNSβa-As˻vW'/"c+LAr`:z?Its]+JhS.s}BKGG-'6Vipl\~ij?tഖ @7s{F?V]]eV6e;x)`E%>iݍoN,Lkg踇HH<%@kjG#u|5mnJl-`sCm{?xV1znx DٵmW@M;CY]a4-.kOVe;_Fac\ub#74h^fFkG qd^ q{۸wFA =j'D2էke¢rh%ހq>^6[ka{wq v4='pwH{(}FZl 4N9?rq}7&cٗaTM-o48{$-.i iKQ{uDuSlVc>I)6G$ȏHMTks(Xw+^\譣@wJFAIׅr3G@X"N\K;nG,60-/uNZl @.l}̂MG0$Lϻ,?I2NYCx͍  I&y2 E&x$ 珿MJOv@B 6q <lr ho:y&mI;^N~)*X{6ײ#c7H?o!%Rݓ .~ ]4F))b_1:xGbtL7a$n<S·$w#Yua}!zJ\@v<YBQ;I0))kv_4̨́=SHutI'!mOun6j[^N0oc{`h-Nb_`'r/є ԪYW"=pKIVnA$  i h #q{C8(.8`G^\h18n21< 9oc\dS\EGpű MӦ-s)8hWgTǵͭ̂;@4|> R%^! bs }| A'IJӶO 6@tbv4A=H8ku(c#ʓt0 QAi;A II u-g^L~DL$#Y$LA% =1lDzw @:4Ck!p]^9Go4N~$Ǚ %vLL|8A&2F= )[t`HRd?" vʋ]A5%2xB{a μ(Zwn[?*5?r.LPZ|yxq~t| ,"c- 7t{:${}igM5 yֶ={&ƉӟlЪqottaĹdht+kGwVK42&cwxFaitO 2RvtI,*' #@4Cf#_v!fx!]N*siM4-)--~ ̽?_ӵߡ*UYn >hivjzn Ds@~o'plȖ?]6avFC;1;-~ > ٰ:s\ rև{.sGҬ8$ǃi'KTcXN"H`;LӹXD~Uv Nx- ׼&c!d\L&a.d%~wf9tp?ځƺ $PZC줶> PGF{4@K;PׂM7G!"a JK'- =Tm h.ٺuψR%HHX13*m 7^tW-"g]U@KI--n5BEm|{V16xeɊocf 뱢C|"ە[va:nM.ymp|7BO YE[kf16X]L_+4,d5c[$D jSX9,*}6[Yawcf7{f6&ATuYizTK{OtJ*j$X2uv4 OoI[M_`Ck@aäV2j84s䐍(Yݐ)>~n"DDv\9{2,sGQF5&c:}7^ &{v9^YONWm3P~$kF 5;?r[{\5`52be&y%Tw[gF/wݶEmQ*aLW@clkmд+-mP>Ga\o{XDeX(-{Xe'_K6=ˤ ;,2{]#ϺrK+{ tkXc^}P1ֶ#w$OtkIO~H)4Vmkqk[k<8,"k\O$? 9L n[?{AP9&̺cllJLv3o{cA&"33|`*Žhwn N4usGrd6~)![mkGhh5$2!:Ke&p ^ 㿊IfIF3NO8sF%{|RC;}H3Mi r C8LNY3(}kg$ i$3oq{&.ILBfdID-lO5O*.<'HI,b~q47iف# )f $6>N0χ8$t=lIoq##舙>iO'nuSm2< 1))fÎi L$eL BZF&RS"=(Ck8'䜴8u(7j@RR谽rGMsm'׵iݢ XƉ۠%D"5kxH4?)2Z}{[jIN|CǺ;_kOiW9Ծ]puUBk]w![aw7X?+YgEm|4Ԭa{u.Lw}Yjdjs,y$4 ;[KR YAѠoJluU2+cniA ?7 Dh {D?H}d3#̚M.x [@W9޸]oOkn?)!qցms׋K<:mf4#WKn-fW$M-qh%FsZkt- ֱpM(ol~Ġk;#Q|S{ a5xt;-s*.eۮx\un~fhݫ}65!s]'YgL``{xk@|Բe^rXt55oVu8VZ--vT@m*}+.a9PxgerC[붶#Sp ?Sx8M-8k8p'b[ֺgqsNUG}9.whG]і.}-u DiwHΧղAѲ`4ַqȩ '{txed3#15 +Ges,uym!43&4Npm^7 iR7u:]=G/㓑k=.{NUXHO ~_* JULJ>㷿~Ek}eƶYyϘAcUVmZA8n n1I'XS۾zA(9. .{#RKMRv+~^yDBnqP25$$>>IõYk⬐dxPs,m:KS MA|mytHwQ@u u2s lxx1tx(lpq{{Gޒ\N#]k2N򢵣S3 ă)qf8"WAu۰]1:>>°»YѩluRI$%.:L8ػc.a#K?-JA{ Ï-i$QE4ZO~~!j,owH33hu&ִܨ!? $OZXw1 C;#=q ot:|W_T.]H kF#JA<5Jhr}۸|ՎbDj[>D*Yn Hh|gu; 1sO*xR4 CFU:gxV}gt%0+$Q6ZA<4SlBGi+q ΒX{).4#ÙSRd\wEǺk) dA*27i˫x/Ѥ{է)WkgFgkܬ668z(:}2~~>#; K9.ghwi 6,Ꞻd–àBAd3"Qh? mLNHP5qN=yzLCuCuNv:dFn4jRc)rW"P ?q0d{Oߢ}C AuyXd0_[%zk]L<Ȝٮ]}vGAۉ:|t%kNh,h"Ē;.èwL@!I~ȩk ~)Kx޹?a.(="Zv{㩃u{,*g ɉLi)PO?.\VYec5=A jGGƶmӏt{~ ϳSb3誒MSt|U.cYMuM1÷"l%G/ l:Koٵ^꩓ݫrJ7UZ]DKxm p%S2׼n\!u#(@^e,Lndߟe(ne 2@.kyA? cpoN>B~J/H~&!nCo>ChhUX Q: i]O/5.qq#s'Ɉ[(XMcPx(@fo4:r9Rvuk\7F Z 7Րڭkc:˪7VF-,ˣ.vڷ0YQ#s|R܊2p pHGԚ25w85mF1zŢf=B{CklTltᾳں8ms1js[{oXMޒ[3FưW^Ck÷^({I/,;A $xw-{kQ kklkc􅟺's qX+]SK^C\dDy[+st"Sw^4lW#֍ODx&t1'@Weej6w"x `ڷH|*1*cG}f;4Q3L:H;7"IW,̶46BUjXK MyL1n6zi ?zH4'c hdTZ4xTl}n?@m1ۇgaI#q2 mvô$ xh[-SUU43h,v6 Gn&=Gp`vH'kD?֛*m:DwO ;c?8Qh2昞?8* ֍ C8>^mxpMU/&LN 9@"DD(~2cZA3?$9J$N0Gy :ILZw'j_au';7~;R. r}OrSᧉ윂2IzJS/kL@'p L5qb~0uo |7$U7vq%bg}eaq;[/7fe];\amiʅ3;4Xt\ OC]ֿ*܌޳{7 D6Yupw;k_$ ʽv_Iu vFm,5ѻn ν 뮊jm@ڑ$ƺslw#Hi.f]8xl4 \Qmzw<2ǼZ;Β瑮u.~i; *I!N' IiWVf]~ʫĎLܫ`WҜ߷:ˈ~_h;i >J7R6tg*1s{K{|1,ՑMmC'"hxE{t@S*Gދ[k}vnSkǗ44\+Ttkr}Uga@1k1_{' ŕKC,sh}ƚ[ei{\׃a 4F v䍶n>ݽƜOs^k)|Q\}j;"c}KPsV6Fn;1}pg5Mӳza`@:y. Սh7N=F4Qk7F Ziss?.@Lz(ml`k4X˰괈Z~$p-a(uolWTĵiI2zU8Gq5@ޘ6QugpFwI>=8;k k`I0齭wZF'ѨDmo]%>j n7+.sKt`ճ_M}-󤃬B gvR'9~ݎ3ϜR\Ѷv4'P$n #?HiNEup"\*ֵ_?c݆An>K1ur'OW6l(dU<eʥŴ?k C#➍ZX̃dܪkx.{9Hh-`#[hve$parfC }gpbmZcM.'@H®ÀR9dk(fKn52Zylc!liYR4mqG: g҈.2x0$ s NDu3 {dq CR#ȍ5}oe#Z5OܑQ#53ˆkv}Ï8}릈/iϔeщU&?T* dz{yiG?`Q+ܽ67``>)EGTB{~> $(X.V~<炉S{e+_EdCL\3@{vI-w< LnM64YF,{l̖[q/aGcE\! xVM{oe5Lʻm>bknǽ;{ĴԾA>~hwc>!?7w`[[q۵4[<FDf\]VzIi=+d[\]eF. F9.kNdId՗f4V΁L1g| w-s]")FFc2/u/dLG,=ɱE"[i sH{q,}@C+n#XdKvXd_"0b&z)2U=۴:U?Z%Γ<*S_+\%R.q/s.Ʈ\I[q` d̑Dꜱ81F3}VC摡Wq+ l'%Yi"bh@s|ZO*=2]~h!nICG.! {c jE l~-X[xi"APruƟqg7.Ж!/_qL=bdt+d3YSH$y#JO]V7AqԕS_}W|#Y=0OBhT Ȱ]G̒f v1PŮlkD2=s5nk#s6uVK*mCWGu*"5x:4;`=< U6Ϙ]iyh395%F􆀁DwM[v±$mk;A$FA5gGx:<ӁZCsk kkݺcϔ O2hhiS2Qx0t=tv⒘\$9/9-=Zu"$zJ[P< * giG4OBz d&`800{ĠM ̞ds0DhxqrѰA?0=G>>Jb,q[Mo$[:x;LUa;{8sDΪV=WkVmʵQapچ)liuQ9gq=ogc9QH'XF `uzsEXdw-#KtuhYQyx FF hX7;ĝJO qh)p k.^0ezct h $s'\vcs+&փP&eemX$A#,>Bu٫@oa89c4cA ٍ񴲰$&>$+F0;m0Y-q$mxcZ{Gg?y=C T`dp`ӍJ=IDZ/yvG?d-{,"0?ik v]&ʫӦEF졚MX{H:F  <|4 =E$&PE&XX@sG$Gre3uY ht;e8s|v3'NeYPKm@jhۨ)S6{{(-c=Z-D3hz}a"skq Vk\6Ur/,2{G-pBc, 5.~pw3H ]obu G:deQ~5kdִ5ԴY.7k56 R4(.]62MaĂg6V}K k7Hh5 'ZӴjv6XDm;\\Gdx^6-e q{,-;8uHyWvFZ[[͏̀KFq>?z*+sڋ[cȭ@qo!;DIwSc_ƻItv^l` .@'^7 M74d$@6Xϐ/K#{TzA햞u?xDgN]-mIqiзSI@?rTkˬkĆ5uA F]4usp"v4ot:j!ݯ.lsx%TCY/m Aݧ|8i!ݸFDZ[=$#-ɉ2<ϲZj.&`0CMC@~*KW]@Ӹʩm`Ϛ@Zmpa<@Tavmpts{E:Nfql~pkZ_;Fv.c!Kw NR(ه\7Cut?$zd<9g`Cw?!8\ك$@DBb.mu7<ɴZCb'>E0ַu~ZDsAuPͰlmK$鮃i8huN,,w1i綠Cx.:x.۝K/lq`߫\/o8F<9yXvs wҩڃ:[ߊ-5e@I|{.g*K3ѻ_ y2CewovA<+3}-NP=(5Y^4v pULm`iQ&_`:yK|?RC[^w'(VuLk)>Kk'_R{t=;&l&x#A?N_my]&{iԶ895|0|VCٔ0[&#H*Lvt{HB;^^ yuUp6m&UƸ>DO= Y3\舎ez|k0=S/lyyۑF8|"{w[ᯏ"tEm3m-')ѮW+uݰV[-p`H'SnS"2)r;׍AVYXf>vh<$%oeؤ0&] zK^9iТI@@Ǻ#tN][G H&RhvcG. [yiBPyWi ;D~Rѕ? =d\Pl|)js$4{NPPM9ۼ4IAGعH>0BF%8w [S!§kujt#_=q԰Y-*uV2݄ۀU &3fY%%ݩӈЧm̠NM{x*͵ep$Iu5rGDk ?UiӉR 8mRVX/ap8]*Ƴլ49yC=Y i((i 5eZ,~` Z"n|n/_Q5Wj`t붧6>SZp1yGNzn=:CHgf8PqyimDsNtQc.o75@{S2̌75I\Fp\.-psDqع5o˺mCk*p,h^\9Nv 27s[&}|>*WzfMpgl϶z20TANZtnE55LF.[y L]x0;ljߣlq{SAѱ>ꝛq׵<5P溝!tUvė^bw4 L~[o%ԚΓ ~V#"WдB۷!K\7t ˳(l\~^*AVAaru`}B {'maߺ[0Ma~ck w%X57A=LDZ7 lH)U9& C n熀.h4nkcO4Q_ 7nMPfAXe.`ʲGxYΖϩjt0`۪ly;1ukY<}[`>\8]7 cB~D-<&X ݹ KO zfH,A=.6y\Dj>qlnC H-`h;q^lɟC cHtP=^y<H~ @A{ 5"Kv6njaFw#* N;KN♶eP=*Ԓki@Ƿy,{=q#iD u3:%([˺ۜ]z3R\%B'6nI&=V s+}>)::f$P4 B!{B0>򭰣z$ FHm#Ht).'x$$L=F Z,E͝&t;iHjۤvG&D|{\HL'HjrvCm.khDWEcOc~*/!C1!#b5s9{G%S#)ltF˄{Lh"#ѫ j;{螵Y[َo#xskuBco-yo4=I*im6lpppv̀cYVzncZlq䍢>pm}@i@`#AyJ qF1XdGjp[12@vۻCd<>6l!cݾ'j][pMl5<{5I> Z*e5+/m @6Hkdȫ }^kou۵hws!a 6k_Ɲx𣏽*!\ vA5NJ6UyY\bMu74 n'ݨT׬~iGqܠvU7zwn;v*to}K}\4`i ߗחasAs@ۛzeT捶 kNsv]II]Z3z{Asux$nXVw:Ce sZ9⚼q5.pm67Բl4<uǒ*K3plюֶ=b`};Zy?n}\y~RcI$bRm&>{j oZ}>?ک|G|~E80Zd:-p~Qi:-"ʳ{"$53%ߚ>*-Ǡ'ӯFϛw30VƆVknO5,-tef8ޗ Vax^3t~*7Xb| h`TPZLzdd:by]e& OEKi+K}2\.#7X :Gy^-wOm45LmՓ t1~)3l+=VKw;8rP.~82oqbWvH s Q?+!ׁΪ9k˭|s lmf:<F R*d44s- ѫ $9M#PƳaﺗ/٫{6tF5Z nfۋ mᬎâ~85P1 QGo:lȢok<LjE$5qӁLfS\nMY;wT_g 64?)N ;`n~I۵K|^ #?K\([pZ:eCi[ eYMŽm$=_n Ȳֆ\ hlזAW#& {02ӂ#k"]e;&'Nd[G 4ݶYcZ\\W̫ u>Ɨ7SǸޝ~iMщ1!}%ho+dōEn쾸qlKPLPf}s"vG"bk*ff65y&LtGG]5rzhsXkV6'OwK=T-c]k\+Z};mәmwoV31 ݮNw)UCVAs\%ϮglaUkis^eAMm㮉nF=Ruuc~m?Ycpq$8{&ɩԈ=b<9{EnՀ|S>YmNk&vihcUNXӫOqR]NZ %xYc^ Qֹ}AIp{*tW:1GKѹq*;v)"Ƕ`#E2&{+k15z q"Lw!]k٠ih.ӏaCqql@$r䫻pi19x'iz1S5m(Ȯ-~fL@ùO*.ݕ6hn?X`~v!ߪ'^cZƺ|fL"Ƥ'yJv?Ji~CL{|VtʮK]Û0gvihk(p~U[qk{?$$<{ă8SsKL@AW~8s. a;A?1Ӆg>.5Q5ŬʏqumaONCI&\t HmDћ 7ܞ"[ kFȭF$ xq $Ox"$$LP$^#OQNY5d@{|wn{>T`@ip NJ_t~Tt1"\fcRC#O=ЄK]6'|IiY9G-ʮnƂIhӷbgDTα8i~FK~5fEZ\9S1/5KAKGG>Kv.1ƞ,o# <wqxJ7l@Ym6\0 K@@[}G:vI[@Hȅjåuخm=uѯ~euUdGPk8&i6̈́ X,ŭ@ ;` P7pel.%ѷO9XW׊}69=n'wZ*ǽou 4Ƥjoo "I2 x8̺n{8>,s ` i4 k55V9۹n%N6Tښb'Tߡv3Oܭ=nւZagY@*2쥯{ֈ-wbd^Qz.hcߴʛ*vS5̲xih!Xqktk}CI[myV?e9ۜHu-ͧ%8ӗY-mn wLn ^k_[Lv646*67"9"1 p5%NROd0/z>䕯V۲,}6Pn.tFK(ɺ6=ͷafŞӯ˝"\^ey.$uP}-RQ>A${mkm w:zw:Ȓ @gш->[j[cu۠7p~O Xy)ߖn4˶FĠ9L08whJĝ8Bd44@U`$>xyUhvMt7+cI$H?>]x0?|̖C]x nw]eξZV*ڪxk=־\k=7|ꆰ?`pA.ctm#7!i jgˍf[EEi7dAZ] bzfݵ̭w:=i,6[}+.{ƴDncWhlY8M8Lӻԗ਺c), &6=n&@8q`8G?YXf۬2Kˁib;m̬{[um=qq;.o1`e<9:ۦlx*jX{&b  5n; ZL+l5E.y848qoП3S/1Iw֗htO#Ő "mCk%8H3V±kC[Z6_<'&v\c q>ZjQ %{)XƖmdvӏ$R:ò8H2I.cq.]ՠ `y5s͓Һ?09!ꤒIJ^{={+Wܗ~EЗiF>nTb[\UC \QA{}fב/STkaqP?؎P~K(|[FkVͯyS5񺑵߹UYCG'k{x{k}>vAv<40Mƛ@+`i,v<HK!?-k괼湡'ţw#­Q-t \d]vgd6dn(v0=Op]DGT8Qixx~OB$t 6l<f-x%lYsکI w-&Dy(V=pۿ!fь6sc Gmh$nH2۹E>'g]v {#sIiK:S 7NS]5r O0Ycj=c.&cZ=06A(oև ivɐ˵xԀ! ;dy-6` #I q1ٻR80;(_ 'lyX|k H Л<)r 570j3+N`eS:[P~n(^$h'Yt|ʈsuA 㱯vV};F:q UG͞2XZGO:FK h:u(Y-ohS*%{y+ń]wU}'sAl|T&a0VLuo wS2p!%$k$dVp'IQ8YChZᴭPA]2|ޖKo\ͣ{ZOG+C+7`4:H'tUqVz 6c=4\?4iJm_f^=Rݼw"6֘Ƴ'ѩ;PC˶ulh5qH=ݡR"zȳ(6DO{B{v~N3*uzGmB!K3mVޕL-hym Ci}v7~-Zkgg[apms!kx~JhQc(.Ɓ韤A΀= l!E ]V<+ 0dc_cj7c\\;kv΃Iƺ`<#1ȵڝ,:xGskw1,k> i 9>>S׸9ĺ`}tq*I@sL1? ]:AW~=.Kl]h}^tVcghp*- u:vswmwDhqn5lKCaCׇIEeHE Yu8٫?H:y))t5Tt-v}X[Ys k.܊O"X e9˝Phsk>M|aid%iMd28)ֱX'suK2u>5@uV CvfGh:Xq34ܓ&cH|n{tG~zmعOi!i{5 ˘imklw@KM{ mMd><mvT,ZG;oD&6͝IVA.'J*.Ȥ~D84wRM,2kv֗%Ջ\ \;|4HZKd`?d$)1"tOAMeK8g]k-9apn[]jiDfF {FC i9LSXIl>z/kCJp[!FEϬ8@kx$ ;$yAɃ'T@J+^i@#6GK.#㯏ƱZ42|5 H:@CMhD7}靴ILu:iF ֈ3~LDAQߔVA9=N?sOsG8w; zT jhsR5=)PVD[⊟l 3?EuٖYQmc8"Ttgqm2 ͧ!9]ۑUG`dI`o.3V)nM5m}h GXAAS[9yߌ˪5ChDrA5ǐuG`'Xczf9#c4{} 5ԗnmdʗU]@sY/wi3^NEqb'x.SO&ŵ IlK]Z.U-sI?Y<`j~ F6?S}[*\;ƱD8>NtTZLu,O ]iIݩ =S*#A#Hh Q3NC^~YsU&j(:NvuGI8@sV VAsN[=C;&}xi {K@#Is7=Ͳ1:&5< -;6-!w:tw>[R1}yLgsF9D{"\˜ x'PitsE$·ɺɟη`$04$j. jPHԶ|@*i48'BkS꺒饯6r%k1m$X}ګ[걠^h&Z'ˀZ[ IMYP?_p$k\ꮵڐwnped8X]G&C7L|Ѥ^wӨ;wtͽ{gh`P('C0x#{'~'qPUY;%:~s,x Rv[=ԱzW Vjjg_8KO v|S:Mmg94 !$ctē᢬ήCoN,cb2 K%d27r'F%)֗ \\OJѲ޵e,`{;&$ٯmi:DDF c,"i>gQ A\5SX͵;d.˶CWm?ݰ|Oe$ÚA.;D骴z7U¹sghTQa˫/=Ω/h{n$8<)m}ʬ#qk~piFގ/ikK $NsV# Zoyp&| +^l^Ϣ6i*/9òa}a{eNm82"V6ePik #>*Gf>5{H}8= uol~' +T=]۟[5 N@DxSիpѴ"Dwֶ[P$I? 3;$t] à`9_a*u5# 49$.7 y.'?λS$IJ\/G9s(.pY u̙?G/5ȶy?hsI1,rG1{"==g.bФ.p?Kqf{K` v$։h?TF-3SiQRh|0J. =}$ꓜIQs>>#@-E Ѫnci ͢ptւkch }bdm$Ɖ-'Mj>Tuկ>hQNm9 xma.qa [KgKI M+hfe3T(DdrЕMͰ Z–={K\]gp`<\_d!s\A-|Od dw#eߚ?̀?!UYk;'W:AwN|5BcZeÝJx:~v8<0K=X>.se4NN *jw/ ')CkclvDH2 GN=d1Ē#I.64ѭ:ǸY?h3۔'m|@^הYa5>`5 ^= dذ'BR8gYv [suxkvȟoԃK6"530]F&Eщ{wuh(tT{DÿsDsT2VSjڃ?V3+uݏXA&8 .iIœ ݏc|U5s']=#nN.&ܗ'MX2_eE~,7itfR qҠ_n|8(i捼Ç M/p8<Cy%}L+ptdBv?5m$w.e-pē.WTV-'_%{ͧ ×m{-Ynpqs.uC7WVkXO psZ>l̬+4xFpf_\΢wCN#AK1}\+CH#§21v6Um.oh, i h#<jꫲ~oX-Z8CA:#+B6,xhRwx+G "V[S^L Ǧ~Nhu/;qb{nU2jo2-1]"MW)-psKtpC<8As^][6tmܟxR}V3c hL) MY\1vv٠4ȇ][%&hߺ+ hhhx;gsD΍fͱh,0CiJDޚ+Z >i>ӡ;不.p#K]h.hݠ1: ut,ٸ#iL@>IUtպ 2} ud9]MY <_M.u~-t>HemuAdmHC_}/c񷖑M2ƽeﱻ״ %ǃ摲k2;pѷH<{2ͶH#gI)sCK\50;OrVҭClt˦$an]jE,hta#PG+`>t{1YXѫsCxp3B]뺐ax!n]%h\AHhΐb;"֑mw%M4 Z[;˦H12kdDk>eK_[zFN B>$8vL\mi"<|G2Uֻsf$sNYk^Ѵ]<nߑN -jgh}'$e] c[zv4h"C~ 5hGq2'=nK\HxqZ Z5 ;>i؝u5vIHZʝv+X`ߤCwV*//qT(kiJ}gq ;yQJ۶liծd9Dn;-uD89WOrUKpƭF2*#YE+%nC $*I2^,nN^M.f MQuNݼ8$)pz:ݡϺhp Sd6>z'%Ы l6`gG`Ն>))\`R;tb7UsDW[ 1E 2hƻ65ؠvGZ]eNk<hv\~߳f5V A?D-uW[dz !ǎ OQeotnp7.Kh63 VR?7Rmh44\oX\[apk>&G='ގ@Nmݾ7q _%^Ӡ wD"{DRqn%H `@ ›Rs\'pe} {4&{G<θXn{׹wh}H C%@;kjR4&Tѿixu4{=`AsgSvcjV@64lfX5Ek벷Sl xi a*.Xn8mty#_mӋccX@C#WjujX4<4#Κ~*b^e7@8L8au_+ƖY]1?^{,sӝ׻1嶱Dclj^.mEm8Ƿh;WtkGhp?}.e8ӸC3|ӭkg?f}6ڃ]}7lx+V<Üxq-g=$AZ&PwCι .`n8 {߳Pk`ļ\~c2xIsvϏܭ}  0|頓JMSuU98R`Ĉ26=wgI{4@mh:;'aœ\_SEFECMh1_[}9@ D~Ӿ#ښڑUa64Qh 9A :Ա w4Aſ=‡\psSNݜc{4,cVF=_kHi2f>^(dq܊s{s[p5z:xN'THcv<% 36;{:${2zz]D^SQgpw(w1]ܘ 'ɟ:(> u/ he.5s"\cwL&~Q =Ð9𞊦r|Gw*%th\.ȩڗ@{>G#e,g,>ӦGmmm14%Ot²A:9Ѽ²j5Cc]aF e#dqjsDtVveʲ)i=YJZW5@5Q>Xh;b嶖OVn'? O:=A]rHsx74G865kHU$dgJ}2˻'3|Q$}ЈKu{Y6GT/ŵx||Md1`³U.N^9!5 sʬm46eXY%dZd--pGdCnVͮt,|]6>}y=)Q~NEikvB{"1,/TXnasvA]/۔~I |U{.ܺ( . =lzI xP OnRJW_Y{ۚ<64qHԬ~N~sXv܆Hn=l~윋]Kn1==7}%md}A*u~RG?s2 <`m6e$u|Qj966i#ԳA1?w+n 2md~Kv|@2sݒmz7el=jO&u^B.Z7 ՠV~ћY[H{':}u{ IЀI=\mFzeuD?G^5GnK)$ld1lV͒4g^F]/`-u 'M3* +!i_y7gg\-6]a'|Ye-@^􇵶cO f/xא-`kR|񕗃Y{^5429O \?泥%Ѽ( s-:yA};c V6#CG#) |tPUd@[AIΣG!D 7Q4$v@8UTÿsEuY`LG>:j>![\;Tgs]Y& sy1?{A7W- [6Gi9Ɂ]oNV/X*0+cyя׏v\}G׎6m x,rL1FCC : HRzu-eXg?94Ut*}#gR=B6W9K^EVGA?KMT;[,|;HjHkC:>ƎDiǮ1HӖn{zq'L9M&2ګ!cN9b^;[wcjkǚ_k@.wmOïŤ.fA5cJr-w_,:z!%nĵA:Ao*}5!n43 ]RX, Lt_U9M#%m\e-\xBhs٣l|˞؏OxUߏV9,,2u|({#CW68R̴o c%p;7% %Wٮ1`s1_p֑oȸk젱::=0kpeT\kNMۋH仃>\wE^^ nCIiw_?X6n`~ ;dv9še`}>1vGv3{=ipCh]-ZӴNL5Z4Ɩ}CgI{M/k vwb U+>^EkHQLToc'k#p#J\{%gnX COvLhJ*v-h2 H >=<{Ufog~"OK*`Qn8XnJuX6cͲ[.!D+rh,p`Nu+aA!poUmAmMmv%ޏ Em,{A-SsE/#YK>f{K+v=o"<o;Vs &٬{u\;hl8sDn [e{6Oúʨq`@¼1nceuqŽI73&NCq,Vݢ5C#9mn `vo w x>lǵm ),fLV\esi9}}}?mvq7RXU^7wd]#k/P|# N3$w*@>yS-F#`c"yMcHrBZ$N2HO0qF"a"b]$Otdft<L&{SAП?%)q5(n9;b;<9@;*DޜúIY1$n|x e׌ր7Z+aG1˗/ߒ h>V^ۧ!ޣdY=yY]KOxhiv[#cǑd{~Lc2|rkx<~h1dCvg,mpoqGeYd:ʞApA]g@uz@hfsTc.NM\7q-c@wC!ۓmV^k;>@C#B!cwK Ԟ bH8D%4E [E[d8<#TbOI0u:GaVPNX㯸}<-+3nn0Tia i؁ě%7_tB--Ըu,\̗FE{w<Wz 6L<&t@n{u$~_$CQ$]^(]O8`p{4`ærT3s^d @x}6^k 'ϲ r^;^͐ tT#$&qEaeY>v>"t++u:٦L2NIMey68< \I$ZgD}ḽmO;ګу[j"YXL5'?8|oYc=B5Ftm5Yj=A [l/&Ax|I'GemVC~wOx=kH* x"2:@x8C#COB6ǻq[g7{˚9!j ހRH;\:@\I48ijmmK<=`0Cl0[m;ii1̢ku31 :x#qY7Χ2ی@ZZl9kYh=Qk $sb~%5]T[hJ+4沭v0mͨ4dԸ*׽ĉNK!>X?é2LzVzML<>Aq>G+sF8+i?G%>I{0tӏ<IOr"{hNӷǚiOu{vF0\kLIM#^I|>#W)cK hLy躿0pG{ wDW]$ObRᾱ˙DGm\b?nm?[<Þ]`Ӡ /sݭPmPm8${˚p9ghtM}X'g*TaZP[ }cZq#Yo<ڶ'fkvcZvmp!y1?Fcb6fD1[ΟX=GkuIsc L4pat0D^| 5AnC1onpH*]HM\g6쪳. ^G2E Ѵ4[;O(d8okNqh'74֏#Κ)=ؓo}}ٗ3Z*-5UI"#Vsoskkƛ"W9S'Xa\k}VnvZ;ڝH}i]wRֲښ.  0;+!d4F53m}K?:MhknGHgT,xoDzsa'S"ܛHvPlhwnCX6 }[I\eBkv\RN@Ee锻#qWvsG[g-=PhݸJͫ1c%X״l,f1>Ymt:;R'Z8!7 ge{-Cq[+1z .ysη+]OcN2\KzZƸ)}w. 4 D |np/ȨL:Þtr]ComU pu?=C *},zt6_wÅ8M6w+e9{K.s|I)pxrEDzDiШ۽Y?͟:v;1ͻu?[o(9<1;\6o-x0@WY֨s4V]$X#A{l{}PD_H{HFdB=k+(GUGC{linC4{~ho+TΗA|@*Cc'& j^Ѧ0@A.qɯA3X :@ i!=ջtoP&$s|,oصS[s)47ԭÉTϾX#{U4qJěE>;[˛?%vF<[Fmv5ݨsp9y|dZ rZmYԻi.:%K_0)Ѻr,lXe`ݢfN#,~6F n5ğ C *:H:9ߵQc@{IAU6Z4L=hlŹZˬy6ok6TI6Xkc $ac`:5 ~;J2 ޖTZk۹ x}k +զ8c^굮׌7+tP^uxﶪ^cC3_ws'jnV6{q;?=BڝEu?Yqo"gVZl!pxRa[qܨdt/vHuMdh;{6_G7Cj$A2.Ͷeq%ĤUgg􌗿m4oo% v{n=CGC1Y q ; R:-kU Z>;z$s\Cw3')6 }X.aL?z?kYTzEK ~hmnIh$~\(}]`s.h wu]ո#A+.~ q#k|O`oZ lSXlNuFS"a~Eӭi86?&B矉pu0c\.6nJi;Cxty[}W'|Y$4{H<SVB /nY$}.ۨ`ձu X2:K;G*}OXq ;jDLw"N V5 7Ky:w*Eͻ7)mU2ƛ=Os&F8X9VTzkhǴX={I_CizC?{GKOwIV-`PF1L&D;%/Ah'hF'+-1CA&KIRSø 'aKnuӝtLCA{jScH%b|RMII a))wF4#R\A1=wE5"sE͂425BQcj`- 6t?XgOo*^#4#;ݏQD@ Q,ꯥ|041i*k?0z2c`/ֻAƾw@zqx:P]:=/L.ͭ87kI'R /$rCDѢ] ًeo2\ rh+7'*J@ fKHõ̸͘ӔJt=@+i7`O̫M$Ξ~!H5nwWeyc` ה~cAb owW[[I凚fOc_9=˄H*ٺ˼5ktw:v:x|U^+(k~@&9iJva2<%)%4<̧Ṋ'󐚰ZchLhZ7Mt>bsrWR6?y`2?rcݸ'I);S=FV-:W^ ۙkmmd-lz@pɊ q-Hmt\p5׍Hozsx6x-ha}fZڝq43cE.3\i#B}|=a< 3ۇ_cnsY^izu`۹oxv_X{-mY}ցVƈ@oۣ^Wk5#4#6Bj.NMt7WiSdPn@w~sI_n ]o~cN7v:ZV/Vɩv {k\OY>akhX[.A[-|BQM#Ný~hw09̝p{E[iNQִɡviamd;#FvXvQ?>jM``@mk ce.^ -Xh^%khyl\ij]撩&ix)ג` `9n䂩45|*[#v59GdğߣnkA/+ޙ]N9O֎NGWȭյ<`p:@94]7Y}WsjlidVcVw9YHjxú?Py.oDZ`18km`QNOt,h}&.}nxho҆OWZ6l?¼M-aQ- ͇0܀HP]Ipa>/tKD|gD)p-;.d|83atXA;V%4C~nrT2ګ6Nxp>~EiS[]ŭ90_"?DJ"yY"ǖ]!|ީkn~M#e-nC鴹.-wn۬9n&4>bSH05y?[ȱ88RK}`e?W6=n393߫ɗu) yǷN I Ǔiat )b.Fshû"ʚL{.6SK7W7ծӪnx9c2:Gl!Vʈ6ht>_ihq"{07sV;5_Kۓ5׺"Kj?S@OeV_sCmD9(]\5΍ǟ05[$V~(8[:d|#"BeXhiBQX]8>@Um aeV+#?UŲ~ӏfQZ`~D-'xcLȢ-ɋ_5}%5$CN#xjdakBs\G:ZʪX@"k@FF;}C1-l{;Ďnf>.}*4:?Efs֊Ѳ w<dTk[{s:cI߆{NQ|G lk@5ǴG5-dI,tm:|IAQɰa>4l > Albt2;\ú?P+`lݴ;XA?Rslhm=qGO=%FΛFw:c{*W(:9ݐQ0Va~i3",jK>uCO/{fQ-5A`0ξِPbzmxpu@p^ 𐅌isKׂ:nDr86`doH1W}Զ? /q_# ׸=@|Bn jݯ nF]65 93s\L~h 9XYƴR'*leA{wtjmdS c ?׸nU"2]q:!\KgWYcktIO[c^5k%7SIMk:sOMF7gN>ʮK\n|:*{[kAc{Ir;rmici::ՑZnf>N%V hb8ڜ ~ HkrXaV~H"ۋ[޻[Z#aVwO{Kn -,5 V _>v zZe] vCfEW0;}{]5){@~EAf3tk@뮉Qh8<> VƇ4[wO a'A@~&K s \;+zmOŦ6xe<\w=j T>FAe.{0cVTYS A;4?oc?WNƷpyMCiA ԶAk-+k[9/;|^\6g$&q*Kq=M{`K,12WSK֐_!}]p>=1h֊CONsayqxZk_un]M1tdxNl`'w)#WTA:G@ j{w0]cܧ.%~'ţ.3reNH7x0'+ Q5x H2cF;ڋ|:ޚL߅#;N;w)$hTVi2&4xSvF [H@ə*g䐳d#w#p @R6clF>~))${?;GQM{D=ōFKf{g οCl>%s}m"A%$FF"Ih%A`h:'d*6'XTn;epR {HI#SGP^?hP~^Gw$ߚx]Pk&|_N2L>* uEnIo#^WZl$3? Sk!#BLjQv\{_5^>NID6=h.Au٪ashs[ h omy-ښ pصPC `5q*Zo\Ṭk!i3u<͟׾39:%ϲd"̬4tyz.u=ӷh%3$v<-znq8f ,'B5<.V=Ȳw4t[_W17eL/k.egc+*߷a?K:,v6Uuv=oJUYMԱo 5" Ipݯ$YK\}rka&!ϕ4 Ix="M1-vZ[u޷ kL{$kDuv=7Ip mP@it$ѱV/!~]WQQkU &dv:i=}{ZXA4T-~,OF6511I/s ;'?,1$ K'wPupK."Z]T۾Ce!ǹA|JԽZ,ml6܅b0TkO>hf [a?Fʆ645݇x0ák? h$ 7FѫmiNZ@-ρY?YhΤY[2p": 1hO@sd45ѠWADl#3#SI$ĥ[=Kf>h5}&hkzImU9X?"hy0vt";$hǵlnx?f}!g-u OY7~GF¾$p<؃Хa'5#Ļ*ch]L|lp/*||sdǻ'ZW*,k1\"gi@nM[cԆ̴#nb, <@p>=1qc&G4 I#[hT] gry-*wPaoIH ();h|{=\fh4a)YS^G3OYb6PnXcdn/#'SjzkmƝƹǝ?xvXb׻θTTʋ[[*ct4SAc}F0 {[EJ[~uJ]}#lv5<;> t vv*Wu j.ccuGƇ4n" 9_br-ȤÝGW-0RA/]Y7u #.`͕u'p*19vvcH2=̃Gj-.ח'\!'kC"H[V3w5Lc8驃TU%ƄN:u95Oe/!!R=m kt$p݁"C+ =Wq/WUos9kO$ƽ0dl0H#RLm{M 6H|e] p18Z?H0MopWX=@x&t.< "bg0bI!Cj =n(m6=a0]֍9C`QDu,[X-}/l6"|Umv9c|~ VW^SXk6WnLvݞX 6<5 P-6 wGou>*Y[)x$)ƃF#:Zx*D F̂Qՙ! whޣdžexGrFF _Rz68ӲEی<_pʧU#apHQmсfU6_M$V]NRwz?-M¼b=6?_quZyTQsx.L`&g rc*C_ܔ'3q?,mL9u#INDL|>TC6ch4w hZmj~c1mUmƺ];bfadhɦHk`KK@{.]hgŮ.}:% ehNkv՘/Hpաk\'$7B4H:GWIEkZTL}8We\ Ϩ=>=BzI0A" stV(c^c;: <6s*Zx-.p3;~>,rmPʛIpA>:;GKλ2 kѥTOӞ$|S~N6%;F~dmI<} ]-&a>~a[ ;:t>G ٲCI *8xx_eѹ?Eցf[80;Ba}ujb֛,: mF1>|~Rlm vdYk۽H`kI)]Kg"vscd$ÿ :/UVױڹ[9pL"e$mVI[H=[Ӱ04$8\|%=cZFy'w"xl.Ǵ8lt7c5sP*}u1}TK.<BZHy,.k66C D׊da"6?#v#B ̀8O`~**sCd8Dk(T[m{ۋwS/6Ƶ 2fwImXmnY] C6G*V v%đ"u<Tkǻi?3K}md72^A{$>VN4SK=;'x#a׏<,'쏓IdG({1`1TL&ׁcґiK@oveYE[h;y NQH:n8Hi'a$i׀{~ 1:-vZA@|JnhMŀm$%"9I]ABkZӮ?1h3>򥔸ÝA ̡aV6=Ux葨?A5C>w9|[ALp~i|lGMvh@;Oh> vEV n5㱘Qh'u?t IBC3~_D`2c9 ]v )X:;D  W&Do&"g{}W)" vxK.Kt3x(a} `k=tR@(EE Ճ`SaE6Vt!5`=ӡ8kswGy  ́t'^S vh 5jUOD5nѫIs QRL={"  '>lt?(CplQr.i&#EMH^ACpRA RiDt?ܑK[$GP8VZHqK_rwS;滛HMt*x\'|ѫ@4Msc iIc;,g1aKsNȟs{,~osn2-38 kamDt\cc3^_N,$U \r CE$=JT^챠x!2 s:WXŦ\vc#Hۏ|Ũ=T04nXT4Z; uNlcF^O* 1h_gfu[}X=Q#dN?ڞ60 :Gރeqk >"|D"WafjcLW <hlx:N%Ah$SyRVׂkւcۺ!K%Y [>񤟽QD.?RY9<%p}ZٕS{Z Ow~&X3Xq!rT4@ħ wZk:ɽGZI#@= !I yT:&QKb}ێi4Vi%$HU-tvПGs۰;{ y@AE>U pnl5VَKnnwv]ķUIA;O`Aq&HWa.=aWnZwֶHi BL«!ga!{@qtH*Y y>˃y'C,kw1=[]G;̿R&UXkc:puzov;"ed9sЃi0D~hB:VڽOPBdla:+?ޢKHi R\څg]n}?hm۫L]x![,\BjF;S;|PeO[}?e]0 Z=F7Bl:7lul H2 xy H"&>33,!a{G?l]~_+lT!?"??̲6{wn}z1<<º#s,<U{+;۵5&|:2]5LlOnCZ]ޙ\^Zg$?`y+b>{OsQ+Gj54$Xt1*c=h^ȅOnO t:.2\fOpF,VklkIKT(pkvA"ɶsh-Ш*s y q dSݧprU=2YXp/ -< &Q!Ѹo~q_D׊ Mx}-s/a @ȵ]^4t`]8X sZ:A C^u.w^7SfmR\Ĵ7c>,c:k6G]Z6A]ױaFlhi.ӏ0h]>^6m=7h6>!-pGW\k1e%[LNihtb21,ͱuZ4~nx\oPҲ32f3{,h%՞<5#PADaŦ\ʬޝF7Tu XI$s:}oѰƵ?`3k23GǺo~UwYuzuOmhm49WDe^7;i B?.#`ٍpr,h ѹju_Pt ox  ѵ$+F-"XCٝųdjoZcO D4b>Ah5`x|\\/4;MZld<vz/GT::cq2\l~cZt)?<[w~x)tNM/sZͲ nxs V߲hdF{N:m} OT1f\Icr7h k=Z|zR\ h+Qm@OA%BXkw<!@4e} |SSiSX06%5KIRѧ Ӳ*LcDݮ'zNgq" O KmFC25}MX 5 cJ_,dkJB;/Y[E3 ُGqh61~{r-0_!Pے紵3t=*\U:-f)>ݎ,p',;§Fkc4/EWklɰ|j}rA֬0ɓGUӋQbbOѮ' :`QUL0^!O.e}>9K@#`nK[c֒]m\v.w.t+VEAi<<11eN:ohk6lN2|9Ww[X\Lj|~+.cN5{CT9uĤ LiN:A(?bk.kc'@d+}I72t-L2׶X!8hAZu>~Klө v;QSr3Nc[\+_kh忼y˲]- k{ѭϊk%`cI &C87?L5XSKZ% &9auִFӹ]Uŏ΂,uR4#m{KxT*om@1 խ;ĝO . r}*˭gY'䏅ڋZNK[Ʈ{Y{jޓ@jHrO4 {-Kep"[ PŶC+|F2Y#x 7}okGl~%Y p:qn/fgZ&mK5u[۶\g~nѴ,?ghk^v8o z-%}q%dtEXc%ҼϱXi:*W.r+}sHm"#sZӨU$qFT퍓y+4u.%*Nwvn>tCh>6&+_pc`Vaoy`- Ys@Uq^;^Î紇%;pUޟx!eo/tcAw >J%݀חOqH @L;v*80|GZxd>'Dzpc< ?7'\bf.1vVYaV9ߥ]%?|[t==VGDƢ"ۨ%H.%q08җ KĂy)5+8+.[ǖK'Ұ7d[kko?)p+h vCL5?جA.߲]&[ml+\?*\ yZ](mI[`Uq4[X|3n(yVKX囋lWq6~LHu8k-+mf8+ f;D ?r:]˺?>Rힾٓ e.2zOTOiW疝 h&` T=k7k *1N^O83n;fI" vH̊oGM5S\liu|q mo ud+ (n6| V-d{Dy,lڷdyk/gtV7J>ү՗X^kj̀;B%vmv.@YYW` Jc Dǂ4Me0@#nRZqOJ#h۴dwYf@:WsNU^2+8>hߤJ4Ezs-P7s[/gpԈ)X[tkwSsZFխWL[ai詿ޔ[Hs=A׎c5طX=Ɠ8i5(Mhq ND:W[eĸDoxP1.Hh-$ ,>4hùJd7u%&Xm.¯l4-\nfNε^&Li?ܷzml8:wNөCY$RxLlkϊM1ɢX3PHocTXƵ 1h$GxX(ģ^vA(kpäF|na^B4lh;X-؏+.H,!|PnC8"w7pG ʱ!I:ꁈHx_QFv8YӺ}vb#Nl.-4W1׃vE67k7/!o]#uMv>AkCwI 6 p~0<@ {ϐ̮ʜ\:ZB׍utW#4|PgSg94g V= 4ZpX Lv` 'pOYdVmdHnGn*r Xb"5&}A"AsǴ'SOLupI2HEO Z;}Vos5s< 1 gSI$ĥ[,wn'H/:K_l!ߣ@?F(P[lp粷{@pz47ۍ۱jpi>4y8Q`0NŮUg@u ;/ I)yvp\XgHitmLsOhl 6)2#կk.{=;Ε~B;-77"6`~ʮ%PvlwcN|# aǨC4q^LQ7Ԭk R62,% xlx5}g<;A";NCtkϴ uE}n on] :*7 c\jsHL s ,#oamu\$5qԑb{lo/۴#cy`ipitBǼPVZC viiesx}To.W6sZ]; ONϷme4vcA;{{uvm}[ IЀ{yU3cs&t?=fvEI!TZu8~e DF] D@E-t5DH4y?ʛ+,m.v%E,kvF tɗv=-%52$|u JM@2I'?>g#&Mӥ[Hw'Hi5}>k6ېzn@ד{ :~cLqm3Mnґ8 `;u%~d_hv4 w1SĻ..5[!{΃O@0WS^cSaLyps\ O<8H6Yp`COe['*as%G| wgRl4D{?Z1j[΍c1R'Tj:捔7CnS؆VxFDJh}v8-+{BRjQTo0K^`2Cȳ@7m#T`07|3/]-<] U]I2,8k% 0EaiBpvWdi_$@'aicE+5:6wvDmݏlqfd>Q쥹t.c,xPx46|5~6X7Oj!j?F 9&hʹk}Je $kuQsƈ{\yӟvE-i}c@ e{@ K!L׏FQsCUcWb {vgQXsl44~Egi}#h>?=l }h)Q=w r_d~ rM1hc"8ot*%n9:T!w+36,uw1s\'LkHUh82MX܉( =|/ٛؗnr^%߉TTNiO܍julڱr 9շ:5+&S;?ywKJԲ:Mk3`hI1; w4;;}[@><-Uбȳ#uq 򅶜6ZwRd"$I)JQy[VU.cy>S.PP؃:}UoTm,s%Ǖ#@`7$蒛=Y7!%7?ctj]s+sY:>jo]M0|{!cK p:htt >m Wr"<_=ȷZ7sZױI [iɯ6a8i/i3W3af W=A Bz6NZ\%SKQ⚌ uPm{p"\8?QXc# S xuZ8J㲰i`i"-mߗ.ml} ml^Nљ{j[6:l 5y K=;ZobeyUtU=]|-G} X}7:V^3VU`Xh# ]wÍh"ͧ`s[#yFG.>N&Se;w{zXӋ}Y״lǷ^]AFΥ{@ kE*d4vaq@5䁓^Sm&w9*]%ptGhcuUp&OEuoƓ:XOnܡ [3Q5 |a%50A:ƆN7{O }.5#%6X GݬA!NuE$l1^Y {C^Zog laxd>c/=E?? RV-6˹uSelh6Jp@y;vUkZ(׸:5r撤^~~U%kHhY2JK\eѻ:b !`đݮix"2 PKI-'i]ՠ HsW8&{ xWE_J+I')yKHxѵz =5ک@(-SmvaSS}H pa?9m{PS LqTil7#aT*-k@ğR5Mh}s6{_ޕk*Ȭ{w3UKO.̥Mo?ZfP^?dHrjch } <{yQXćOcKcht>HxS6\)#%U cvl?5o?$|r9Wa3Ԟ#MEnL&v0LY-cϴAlW ݡ;>ae ks3u)oEs>vi|9@Ȥ=@ag5uc#`nYO~WbpsGmkv=u.,pŧk⴯9FCrݬFpaQX Gџb[lX`{'t;K[8yu 1GC9lduY$CA}EN,po;)@L!)D8;q^ht5ovͭAձ@t]}}2\ֿ`i\6XǸST1GoHFZyΆ~(lK[sFm!7Q?9"rѨ΁јC+U7SULkajA#RUMt$Pt&Ru5\*i;O0H"ClrYkVUu:m.֧30dh|;R]("Owwx+]5S湡45>!-ΛZvn6E`6x yA-B]-i10~qjV6ϻHFsu<5b9t<Z|б1 }&| NnDi =~g~\ZN1AclNG "uyH8R $Z}}^ՁH'Xfv01bZ-?<p8Y md\ѴZOBb7GbcB>i84⓾cqX䖟x5[CXF),F}l;"~&]$I#aT{:,edzzN㤁a)!If`q t%&GwIljJtrەe0,> xb}i:;7n.#i$(IHchưkXƣ*)nukL^\oa%Cp`%hqt-C\C4r-~I9@bCn=#m2%2^,0q2|c}L8"4K{]f.=t3QX=֪kĺo5u_um߳ s`$\Fť[y;y[[]Tsi㾍jvfrV;bǫ炲sz_W5nR,\4n'Ңd ,ѷdt0 ;z,n]γ/(\FWK. ܯ;]q-xx7;÷~][Zr\P3bZs,̭_^I 5G(WZմkĨOMcX[0 ;I»ծi#"}0Zt"9E_\e3n|-_]$G]f;F2=k {mktt?*-@qu@8p{@j~k[hw$Y8xOp(0#v@cǘV2ϸ*-bqoE6cCwoyL913Nn Twp>xCO~yL8T͕ 0n҈MB?;S3"`6I~t Hמibnx}Q" 1œFX]@Ϻ#Ypsrwx-w(2f{q?H/碰3Djt-GQxg'\>jMnC﵎K eyun IRǩ6IǾ,8%5GQmse83̵jc0 [e=Ï&v@swWect4AcŻ\#֋qcY뢊Y`lqQ$qS,crrlv4-;D{J sYAu sNN=`aâd'\N6y"Y=B(u߆N)k1,]IՖk^' Ukq.$wAn:ZG| %XYsip C3o a * tQYasDw;V4ZAsMehq` 2o !:Ӻk-%*sDD|C`AUŵ8$iV{ ّWcX }ۚ[nѸAN -5V=ITȰ2ͅĀw^p33ܪvh%ŭ-:=6 hK@ec/X ^}o8 6[nZ,UUa",p#]ۻHGKZn]:6C:2̛ﶳ`t+O2>K=,pK]`qLn:19987 ,am`#愧"7"%f7ٓ@/vΩ  dQcsm@CV#=]r/X\[ +JK54Ht{>tEq amm4v탣BaWp6.=mHmLOmI*VeM2 =O%o7 \,΂yAQ}wT\n-mp7a0KTӕ}V @CQ2n$DtqSf Ȳ|渐]^fEZc}avI@)=ա{t{ @IkimMpu-<?wt(:fA{G{+vYE5;mbZ5-$ qrNEt 5>Z*shʱ5U)s%Ls3gtl^4 q&])ե-U*oPђHXƷࣛ=mg9 w:C8ch[)I.B`&H*mӡTlrRKvq)q:&E H{djG6]IAMoB$Z5koy&jOtd#00>D GyHJZ JA,vsi?ޟX_q<P5%(O?4HكD֒;< 6vH?FG><{ Zpу~ږ6:' #a% n8l383ݰeH$>V8CFVYZ IХ!@u3"Vf@N" O#:gV51U%PeAt ($5wFf#@I>$4;Gs7tO=wUcQ/R5 RS`5|^Q#Z4W?GNuoS)|{HL%auSTİ8c)voHB"Or\#s懸'>WpL||9e@: Fy#䒑 c8 [ֶ'] ];Z `n'A-|͵ѬL ''׸qxJ ݏd%P{ & TvH ?rJ]'H5e0Fu"=0~di]Տ6ƟϺKA=+#3W~FvI$%. (λ֑MLK}˽\/Vra3&|U?5ȶy?%>wcbt>u;kOi.,:v,[{v-iFq d4k"b׿2ߊ QkA;pnw# 8DkѽսLLyy! :6DXY`!M@CKs;nβ|RkV."] Sn׏L=QUly-`w)Xl|H{:Z"un<VLC=2a@JAcY1jIGqk[Mnk!NZlw#IF.l8s(8ɁO^R 0--$e} xيSE>7kiuNn:% V)B3==;]h#Psfuc ~F/kkiL.{0:q#̠K@OvOn8ٶ{s]iVlO~FM][x..W>Mhͤx?5Ik~e`jMX4Ѝ:RSC v uҷ*b>A?G ,is-ixe7 oȊt}cR<^?8'x.k-"4-ynCXGYSǑO# #ڋ܀{h?DDF ,/qFb]6ϤA}P6 #2e{Ǹp:{'qszu4 a$%;[en'Cu0c}[7n6CZ4xB^Knl|oFk7&6w3p\E.-'` @ Ւ >iJ`qFx'U4':tWKaX5 AkW[=4 Zkؒ;ΐ ni0߸}tW bW$}rc{]>C;7!>kIi^@M~c,5tqH5_9֜YtmOɓFvMةJ[Q[t'@6/ӣ`D*|T;&7E5W -yCsQqt .SN{=Lwna;\y v}z<VgM]#*Q(t^†鵏i 6(PjpuoݠxJSn64e]mNܷHp7P Z\6~oZQ12Qy~%ZvAh=i"%{VwRƇCA->>*hv Vrm)t {G#[IkX=1k>Gw5'6[뱻yFg94sM%)MT5 iO`;`“=!Uvր̓#Ygᱍ.PtJ},eeqѹЁMTY2f01iĦ9!c<ĒGEmxƄF IUȏBHURź{5 estla/pK@'~;,<=: 7O"U]YGL}(] wGi@ey ׃-$gOXf=aS$ 'FEn9xOj;cK$Cџ>d&mHtVڸ0 >QРkYEG3FxP5kHmp'_#i*ycC4DNь/ƭ \ᦐJ4@WK>, is@j#T ^ew@5oM\1qſ<u1"ݬӺ~]fKF1Sy![8l˼f 26oL#ڇs). xq;ZtdIFה=w/=&7ܪ ,7kN]̖nlHsTa 4u  qЦ9q'liuVVv9>6{sZFx'tq3)px)0Kk%3@2%,L5J渎'IT %djp@iېQ FMD J`PO?I2<"/$wE33lK']Og1h G Nx$e!Fȓ$ n^ݒ-{hD5PׂOi쒖ө w md:O`;$i:B&<o%/P | ff#2xHCH-(7OI ɟ\u4ƇDH1%#h1#+T"HXRАL5T5 $xs@8!Ho~=Ǐ:vdk90ǒ hr;xFITL;wyRil (uHfL߁*.'@G9q 30HQ {O(3Nvqu \tiߔH'xn:30'O!2?J`t ?Z Ïq $Z079ioh|S Ű'MCH\_`qO-&FMhLLDjVVP-o3{{$T\24~D`YA(ҼhJ/.=--w[.|QZݨ0|#C5'&I0O'm: |\ǚB ڃ_i&|h0y.|5AָAnB9W59;qr0(7#_E^mc(.i{J[@#l}`tRA#CSJ`:zN;{Z);4;+b璡%N4O2klp9u%_qiJVR^o~qʴF s.CvV-ؔq ?JjGEth3XwtpMphvG>]ָ䃪_] !67#>*[(p-'"¹#T$h=uo=vI IwaTZg5rXgBs\0I)]']VD8> X_5d(-%A%[o>^)9Ey̺.\H|Z=7۱jXOZֶyx(]oxiovkE^qkwylD뭠=AkU,a>pax~+5>27%A1*S,&;B18OrT{r WēYnC\u.';^$|YmJ.u=yV)u 0pZed0([_ #NI53䚪{\4k,a kGOъgTwzl iqkUM \̂ik2$[ú\"y#*6A +7g% c@Xyxvc_(L{YhpRt|QuL ~x9E9 YEk`P486?J5ϗ9hmoh}`I?Tv96Ki~&'v+DX8m $d4H:}A'c5Ѽ<G9?Uknc_$\xΨ)Z@[w>hds! o@v`COjk y\$':ɭd9cXcy0Z׾e:7Eko meSzcsC}7!ӸhhC67%[3$~eM42ϑ(ή-;X4qq<(Mm:y i0#p;n괂ցgDc#z65Zu)Cۡ1@*Gi"?p $< dDzԏxJfFIR 5K>c q3>* tjuL,0#I}!h8%)@kDI - k:IHqK&@H*MF`t&/i)`v)Xa&AovJV<~vQ5ݴG(8#S!{x$<15m H'0=G4;p/<`; dv9U$Йfd9)N45_;G:ILZ@q;S%, 54gE sy2G{Nѧ x8FBAI%hا]s@xW^ZIGJNR $3>*6{FigjA˸B[c(Ze1I#Jf΀vC$4>h%AvxOg?v&y͖/uRI$%.'c%p.naYN2c=?s<ng_v9.'O#uⳫk0G(44oIF{q6' e1ڎ@Nim\g*k*e5[ؖWSn}abVGGmߺ|.9h>o?~*Kӿ䞉(f]!!SyFCi`K[.Ul u|nk$N2@R۶\ZǸ9Ukua!8 $v;X7D@kq#Xy%ܒI-((D ݻ+O7YDЁ6?:=D4 ݬ%# ԬXk@89hsKKּ ITSlkChv#N4r7c^[[Yuud05kk=Sfxh<HπD|w/ +{|$g:^V+Q>04Wie@ljWc U`y^Kt&Ǡpu^]?Ȭ5RPnKm4Y54 Lq.> %04'V-B'M 'Voȍdy;M+8,8U-H5yskm>jnv@TuNFEm8Rޙq>-pylFmy4&3 $GYq2{m]59 ~l΋ള4|{/(_X鷝;ݠqw 㺶Ӻ_Rsw9^D tmctѰ %Hk-x8&|kj6XCȤJjVZCësp|=x>4#[E5@⭰A{IqAGtV Z[0m'a?CsGnīsIqæ`$&ivGcVK4}3A0Ω'lL!ÁΧOJH swA?rk斓XOۈnzҫ'quEomǘ&u\ckN^DH$s6MtYFI'@p>kMu 6OEosX04~`4 0su2Dz:w,Aks08@1vV) M Ȃ*%̹6Pux;kN7V# F j]q95؟ Om\"[꓎2q$xke5 %-~Y[ -ޞ*yp`8G gc4@JזKIq N#C<Da3E8:LtA>*#{@.;8(o'òJcdc]̑D|QkN~PcOAƼ)0b79T\ : ?8D tiLI3ⓢ`8?h^ȓX42A;p6I⒗ p h#̂!LNQq.j{H"AILI2 wʛ[ A6=m}-%,+h}LxL ȩmN>~*a=@ @xthx$l 4%ImpƂi0"{<gH @,:DxNAц! Kr}-~QAH OcF,q:ρx#0t=\ipKpi>""+ \&!$,\4t :p=ԝSI-C|$5o(d;w fc;.tyH2Rw<\6=v$]~iIHFրLx:N&7I^ AQLi]V.k0w4y'Yđ9X~-k3) t|JC_bl` {Pc> {psD@ eH<|ծGo쓁i  2 t%2y yiQk4,MźGuN7 t)όC]HORS Lkͦ7̓ܠ4$)ăI JZ7ȑԽ;Smd4Rql| )}`c{~!"9Jk|'w);isBD)\GɪAgr;}=<}ʶClifcg**hΨ[Nm? g'<&M^d"t#r6-'[Gf :' , ,xR~Ïߢwx!C .!žG^<āɂxRpڦi收^d';v*c m``x꟪@`ƺK{} x{18Rہ9S=N|Y!>HK _n(~e/Omox0˜%vwRڃN*U8εͿ!gSn5rqPfZǍ x я ɱdx6Y8hC߱DgHs4N4QdƄOm  7[D8 zYs".#$+l"ClЀ9sS6b~K3:־3kl"$;Jnѡun+V66#UL/y]hV}[,iTV눁qh 0P'>KAjkciir|JƢܬv^-Sul(ź@ s쥌DX$IKh:ƾ*EÈFZly悄ma0$ߕ+6Q>j,Ӵto7xDv N#@5}:awUe3*u<vS`>8Orƭ-ٯqΊ;l t 7)ԋHC mMwjӧtK(25qVUXp2wv-<45h?(M1MYh52HO}8svz#8c@5}*N]YqQyّun7鹬>%A䙐tɽћs wy#i{ꍢ 4V0=$0HOn9>Kt=i`<ev`O~~8 iD!_NZX!ƒu>i9]%De xO pap66YJJ(c^ƝW~O EN?h>6ò1"릅V1RP$OB@-[Uh&uck.;ICLk,ޞ˝ A2(s&wOM[1QiȑDp #N Bѳuqp@$ ɕ-pLtIK`N)4E`'HtsY31pΥh$jw%2xL;F| s [%#ul O#i>I<h:>:DD:L~jo*O9Ui_}S"5'AHpSdI%&6N̓7$^AӍO" h1"˃ zm0|"Tp0:&,D9U&IHRKq{ 88q:xHe$)`hdO M">?ڤ=0\@04ЙIT0^;bDi'Nm,'9gR<$/3. KSL8 xIA@#^;(O<)ځX|T$#A(<m: wzgK[Q 1⒒"t:Jt:OL; p5 ) ݮN:nĝ?E݁A&wpCuG0BbK-_i?>ֺ\`;)!p\`s24N68nN&Hx!<&;5&h9Q;qtnst $55ŠL3+9n;Y[XȢ>?g/6D8{ 0L'p{H!r?T@::y&TkH& tt3 X= [V8$G]ɫ sC[0xG'@uRI$%.# :Li5v~ueNb61ѯT)∈[&xwݔA$Y}isdcXthj}iǏQhʳ^v ><^c. ~`6U[r:xi!YpMlc<{,%H}jib:ǻkX6L(K 964"ǷhGcuiKAw k!BVSsZd7#GL uYO׆*kl{%o%S?^zav=%Ś~)u?͟^;SrkC{H 8mVZA B|ipd0@:5{&A',d 4O /H1&{*uz*q.dݪ5uZs^wh0Ҹݷo PTLj 3TiHsnM^}Ms 9yNrjxMhKqƧSw9%sdpC$|V4z']LΧw lk,A,quG}4fL(xՐ7.{sʰ,em4V묦_(>ŭn o3(׏X&C5S԰X5gާ[ۭN0 ۆǍ+=׫YDŽRLnŴ掍.=fu,Z}JLw_%CZ7Gpqӗ.J.4^F\;x) 'UVsO6"@_-nk~Kٙx:ml{jJAfqm jiÐh`~[I-Pcಲ)i" }z8"GnVMvՍkp-nƎpb 8;UhۡhڷyDptTY1ǖ4<GەKxP!y~`䉅qDZݡY7m#k'{nC-k dK).2߻c"9 ؔK n?LU=Jt΃A'S>;aD~| 7͑pOJ{6[VݡsYo@eexZiBX4zWa`\2dPm[mth;Sj DyXv.k=D5 vޭC蓩9k%ײ``+x.f>>=Fy[k7:=BC"wLGDBc<.yPc}hpwvbLk)iNr?t]~ǕޘލLeޗ (z#=›Gm;y.4L&:~ǒGƿGW{t<$w#x\i |0~BY1 :RG{{\K{ mI\p Q=5zc3:!Uc :"I͡4 7Q3_9.%ަрi3_)Q!!:cøL!Csޕ,;O-:i({\rNy#_?AeʃֿFw$w=3Cވy$$Y6aƥՍc6\t =jI$ƥzZZ[p-W/4u[ϲ*ur4VdWCw KLI1>'N 5sdy9 Y.}K78HN=ù|@\Rc\z^@뻒pU*ch`CW,iմQ`{9 @% Фݗm?aSAncxidHxk[WSWVE2ƀi$AU{i 6wn۩CbxnS\HhsX_݁86|kKt=Ց`3c#c_T3.ʳ0&tnX:*e1b|]Mʧ/ ct{O>Ƹې7#p%jMXF^ N-f yrRYufFH eΒǺ6&ڢca>j;^ZƬOp;/W ΐKI-sx-Z颼6Pzڗ;m_k}7qW-.&H(H*VԖxol54Ci-֙LL@ % Zt iġJ Hc]͡=K}^J:0uXc{. a<6׌n}f]_ѵA{JbNH JVgM{  U;q\"UGPF}[.ӽc){ڭ`vè+%R̛ BF&wnx'h4=Dmf1|,y+d]^C CfNRV%(k-붺I!bM0ո@2wI]cx^5,`'~vX& ZXrbAs/>ֺl\DcHwY onKucĆunʎU[cw .#3Qcoev,la$׼6"KU/8ֿx}0C!It9\d1 Z]/25/ΣuUp-ʳ =xi:y-5s^s!sx ^oK>zp /<<5]M;XC,,qJw3/.KHOo%{\,o1ۖ@G\Px<UaEm^ݤV7D P]-ai sLi^`.O=\k7 9CHu`qREas+'tFi*oqsF!W-vѤ cSJT5w>^Js<s*8xTq]"x=fuj lחF7.U[/Eż`GQnS+99[=4Nӫ[v>c$hk5A>nZE鹁NNFٳ=>Kl7ouhq6{}G}U:Al XJ׍7pY[`v'`5!qO/G FԆ_ةo_]`K`\KguFu|r1LqO .#Kk%մhI$LjG}Fּ @BQ2ZDKıp0mt 5aeSy;u/'a 5fϡ o8hbyIYk%:iT1.ǰ}n&<NA%#I$J1ȰU[C+issRR4luW1ck $$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JR'_\zu_.II)K>?w֎PhsLn p^`Խ(NcI&oC;CZH~4|d+7=h' b{r9W:>qz:n N@Ӯlp&iw,W^$Uݯ M賞H-sp8Oa˭v_ֱH/K]p::S_X|RtLHߊJ=:[dM6eKZ:KdZ×e>n-p՚:4AqFn@ױ!vg:6Яu[`AOՓ$ڪfm$K t2d;en6@yhaOڞyLat Dci&;,x2KGbHtw54"hцsQ?3# d.i. X亦'O~Fr܇Ums]X?6U;hk+ue%.i j$ ƧJYklv@!کVߢ3J 4i'4”6{Z#@8P}Mt.|Ԝm-/wQ} i&k~K|A ,"n 4w%#KeL{RUt4CL cU{\uq)ԋ`F<9qpğ܍봍lp~x8gu:1 mldqh R;یUkHE-{E6zb9k]='U+s^[e>;܃{7HĎ9!= Q\A{ہtnt +Gߵk@hAe@$5'oic-l h@;okiw&dsc4:M ~F8ґ);+y"6{C#Ͻ\֖z~̂ڇq3V ܛt \O}Cܞ62CHNoU{%iIY: (>Zwh!纯"Kc5̢έ>d+ 58ډc~[p-k}j2A&ꆳH&\$tkja;!4>3>)vtw%D:{0ƴ9M"AocY#e٥pcAѫ yx1ϔdF_X#Q#*Vֶ$4j4SZ1m7e(i,ǒt>MYuBް*{v>kW8^߽-}fA8 E.G%;fC.tG}HkyVaK2#Zcd0WM #؝Ǻ@V#-m[XL8NIeLT7Ɔ1a/ w82i! Ɉaǂ-5F̕yk=@ֺ76$xLxw6}&>cd{ [Й4 5 {*!MsZ ,aL$v ;v1̔<EM}#k 5̨MkD|j9L  le^g[_S'|.tm<; Ӫl{ll˫0# +l~KG"`dd-ZZ;T{K%=] aqܬ2z~FD}59#轠wWs09Y#k~{e沶}-|,H-I%}?Ot(eo\G oshQ֮moϵmL%CZf> {.}q%ίS_,-xiJxu ki}_[֍I,c˿YCg}A5 >oͻΡ T芬pih7Ɲ&̇VJj^UY؝w #f%~,zvEM´ίi^I$/*PԮu !\UyO~oj,=FMp\@S3[qPڱn4NϸA߭؏#hoCb\V[ump$l;Ն[AۅT&}?*;A=F] 5Qnc+Isj~Ubgen>sD=#GNؼVH-tvso׿=PQW[O`AD[ç_let'45⭎evEH~][is62Wn_^=?`c.aLp .:vM6`7OkҏIBζQ6-=헹}@*]/@nJ5r[cjt;0>:<:ǁkZHRUUTs\ Pv'fOdX`ud0߽j Z]c670zyeU60@~+-uc1=&y@tpm1 kqݼ0 H߶x R7ʹYQl{oȪX& ܨ7cFrk Pe@̊, f2[_SDzɦíFt> rBwd6f~d^61ޫ <yHQ{ - ۏl  F>m~縚% Dn~H4/|4AV߉n> {]qinw'[=iE:XI0SdXmc[8nǷV n*.y _ P[KչfFQV[me.:{OCatndh;MLc15=N63*`2QFA-aN…u}<цot. w=*ɰ6 y<6"'Ě@@:AП-Wdb[[,[;K4l6C=:hw=RɫofuOZ[a.>^w7` &U_W.<9kF8W:n'tCIq.:@'D<b4ЏV։`:v-n0|>Hac})j:$Ƥ=d۸&ϼΐ>+ ko7W,YVT+ss?{25ׂ]ieߡ`tenw4(9XV5VM6/- ]ktx &}~dcA:BxԆ~"Uh 偏iRN"U>K?5݇-w[-y ݗF{DXdh`x(uޡҳt;R{;,RI9?St_\y{2eޠ; ^Ul{CM?ѴEe$鮽'B6ac\}'NV&]!6Ky"5['N(jAMH|u߁-kZ4i_,YX$4p{訸[[KF|>J, S3E}8^.)-[ki4gp(?ݷdcU:=rLJU.jC{tQ¬5>H߁OUcY3~R'U6{vVY#qJr#)/x1Q5GE2DԢѹA+LI/wam{" Hk# *s]NM[@"5TI1ξ:jXX3"9xxkv`3"6>6뵖@v> |n[HE[h$Ciqu_0n՛G~+k[RZ "r45#>;i@_E/~(.2CѰ90Ӫ20(>EDIj9ϮۭԷ"aq*П{#Xַpc,h{y.G'WYmn%2*dnsf02VON_a- J}+t}OבgYfnQ8aIAy'2:w[XWV>;e1\Ӻo[J/UskdƽTzWE9?Vp^FZ}u8USpo.s.q\JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$/]Uȗ?URjI$?}Pq;ǭW>YƝv5h0&De2cN;DZ[MЩ좶7c;lxVD85(xֺ02a8x=;D8M!ۺGq~kf-WZF`%/!4;p{׿edWSXA'q:wv2,e8mf"Qj /32\Ao" sxSkcƸpGTi4Cg^ /Kdh!vt:Qc?HrZ K5ROcw!#O R-,v9$xkK|AUiKSRYisDEm+TkAaACZXg`z D{@'D8@lsCYX[f&>:%sMb@>\ڿ*Ckw~؏K71$>ǒlDTbMx*d .F^iʝcZى]>p۫ kmq"Z6;E9 {ZV&s?4I;L~M=#i}[JȚj[Y:,]4zieMDuw*t<ʩ},puNwn &`4TٛW>npv5jxy{Wf6ãZ>]-;O{NB"q$?bГD65gOp4B!Vm6'QA$\4ԢZnݬySd[\r$84DN`Ǧ:nM9ݕ߳cSVѹtq8煉e1k9'vPesGKFkcM*]ꝵ;8v@*Ո$. u`. z[qۑ\m>\vc]7a䟇his%t]J־f2q{z tt'EDd[{_w;ݯ$rn/Ch ,k+͓0ii V abSw7 hc\\:l{%{籞ֹΘL[M]>wli6\ 8TxHnLGh]Yz>͑[&ѹْ>\Aa:Lj@gӟKj`lV6]gݒǂlapq9TEwQKrlxi!Ōvk 괉u \wƚ(d5,: s7 :Mq..ƑkCrK@ y6N;AIkYal:2a%ƻ@` 4$P,A S͵h9&Au:IrOmy5!ykI!G} hVJo2ڢ7{92=L!..<0>X/L WxO3Ak΍uZ/iso}lI|# =zl<Ō$08'I\Rat=]UO&O҅@x-@;5vs"{M]6F0sp t xQOvXlq3IJ7A{$+~e쪻cp'RA.v;ݶ?,5sH]PI|o6HcPr'P5p>$ @JKAMr1b}]$;ASEި!=ѤpEM~m{rh!x=C Iy:Ȅ %)it5*>  $$tsrɴsKI)=E`` O[cT>$i'Dˉ$w(s\ i|·O"yؔ?ŵV@ 6I hZ!?Ѵ#X]UoIgEi}1k ķwcn XvM/ a'oܭbh>u:~[ݧ"6t\PӨf'CGI%$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJ^#WK/]U!ԒI%)yvno*\ng֞/wbONhX@P4f3"v][ kGkr_+{sXcw0 Xf=ddbFF8V;eퟣ#~ԫ&ڝS,5fݵu*3FN5ĹHqsD#p>lOTNwN--|-thGLi ;l((y4=X5:+-0YF@r"$, i@%c_YOZnsAƳ_KRv1~K993gH瓯a)Alei1{cpxE] ;i)uu7ǂI#])!ɞBYZ;᧔G/ܩS]cְ߸Ӻ}XmHq'M${DGTD| cbVg7 FtD#bd:iws[Y:n'I(F;k"u(?ykDV%h.ol4FێVz8 uEchcXcC*<`n~Z)xsL;<$l{ K^ q$LJ*-8kRhG^Gmua;w:1C$s0{-ml:=1;HpɟC‚M@.h =WnŲz9ۺ?3w=O:-~ کlkhmpOsc*Ʈ;2ZYkϤFe:72**yH氆[ Fm6Vc1ʱXh .#d4Gtxbz60w}b4-s66e:+ٸ&UpHpY=Sf[\5-3O Yt;w8^Bq\\h-q2@"U]_ӭ}newkCL.-#Ӥ&ϺbYYM˔+Oᢳk,:.- ּi;FGl6}.xwwP1!ɇ8Do,gZԁE h=pcA$^$kuP0}?kK+/w>Fq\u!&LLOi-qU7YX[k?t?YXi]w?a$*P]h|y(^<Ǚ>[sP1eߦDaDr7Tkw RW AO*44nH =Ԍ:9hƠ6Y v' svwmZ9B3:^%d[dZ9*xcI}6 :w 0Tf$js]E]K)$8t,j5c^Ӥ=dv-^y»p~_QsuC2?澏S+k2^o%Mvȇ=NJ+ =Ɲuцg›s+%Oi}@Z菥览hy,cSL;o%(EaQ;[ LCZZcC >hkěf@lhy$,7@,tF|4 <[a4)m :;tS\ctRj.T27>eǺ@hHY<OxՎ{5gy: ^Hv>\Zd9d1͔:E;^|=#NACB2Œ<I$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JR};&I%)$IJI$RO2JRI$`13uKWkł[108IIj3\}DZQGNv"tJ$|]n#=Mc!ۙx['[uvmnJ9's\ӵA2IRI$wGW:/"^#WKC$JR/X1)-QPXtmX;O_m̳p}(K%?"iNv]y!mky6o{<. Ui'_>KgNz8Czfqn%.Mh"HpQ]׍6Kٕk1qelήheV#sm [yVf ?JIlnic=rX]$F-|Vג@&`s}x`zL{).@km<~v:Su4i*ٯ6dNH C_uOK]NlT cH dn;(s]KAsžSh)E-ߥun"ͺ95-myu9x>'feO)_u^2`ZcKKFɟOazeWPP6㰱9՚溷< #t+,/~k1iZjDLhef*V=ںSoegqHn=.OkƸ~|"X?;Z w= 6&Ay4ʺKΚ^:7k luu ўeg:TW`ԵR׷C  S MŭͤKCGLn_6-hձkAi*4݆FM{ 2dLkȼchk@s<& =᲻)i sasIHpRȐE:=Ȁ~x+6{͍iq{"$OZKZF`=VmpOuQȯ􄁡% Vm{XxݦDy X=H.!Úl̓a:*/lƄi<"6hb~~*aU"m$=ȔkE-'W:ЍC[4i3@ RfUC_Cvn#B+nSo,m!ZF44YrEdX܃1qk_e#sCg"t;GpvkMZG6} 48?yRZ7UO:x$Hᵌq<Z [fcH%ͼ;_Qq㹠\k`hnֳ:~%bmoq mn8H*sDY&-uJBK\5-J%ɍ5(W^/ a䗽NG/ Ua嵢ce#n ]{"Tt ߅q" Zw8GDlm[- tAntXy·+}4 Aa]ͻ$2^~+4}Pl_{[wpgOqhtZW2N[l hsN13 Ege߅Ұ^+ k m=O>CHŵWg`dh'}\yO 㴆-q3ЍBǫꞮA)X8]J͉gNUx>[XpdZ\ y@5ێKsx 3[xWs*=Qƚ8u]ZQqmMahn|<9LYteSk{{)TeN {1o~e:=Z[#q 70VvmU hcN d7Ot 4絵t 16١L7`k"Dď\)'cv/$,45 ԏ^׺X=]0Ξ 9㤉 {<K7uS-ml5IAk- +%sw7[Q =w(-ILKl[KX@'TK&@%8`,M'Ϻ/mϪlI WiV-9\K2T؆3 J=1B뤅*1ER( %-'N'xNO[@@/vwv)fC-ײmӲ >S0DO'K5WtkD+rI%;]I$JRI$I$;II iq:Q>V?SAɰ,`NSL:~6`j>WkXdiPe^c^xiD?-C ӱ G6[fa>i{ 8 QoS=mld{9ܝ$O`˟hlu [#c9S]Wgk׆h\Y{Fڪclܗ8|_3twd2bciZ?W9&EzԼk^uB-fO}BK-o*-->_;3ZƓ"׍;%Et;I#~UM9V49!.8UIuAX`{VmUsDz靦'l4DuQqRZ׀9^هp!DwSI%7m1#"$I)I$JRI$I$$kbe86nh &BJBӧ\6{?-\Vwn |pytHq6L@=Ί 蘍>^'p#aO?L@5L6Pnv;F 9K7:h57t.nI$/0݉r.u86sK謋>Z]T0ѷ_ަʄ!ybʉpw}ǻ&c*Z74A:-Y8DzTSn k'3Ek]S70= AJhI#w 2:H{W{D9N*U4uk 0&kpЙ5]=>Yq{aɡk1ж7Q{2lX9)۶hIhO *Ǥot@yqGʶEm`n3+qkd FMᾨk+ykn 77qM̃q}8eu5zoo6鿃uU{uv!ĉU-VVAk-iDSxhYޫHOzze$\ Kxk o_:P7;8,?H|I{eu5%s{m$Z m7n RF eJnU^~MaqC۳IZ|uӇ.鸖19Ց# ӄPZw^睧I[ndSfc}ml7[mINqsq:{ KNQ#O[ZKi %Duv[K%rI.~5LmW9vD>i ;Ȁ r$K4289 ZB k}I[A'MTikn*F1-n/-,B{ݡ,#e!NLnk̓sВ B>{xOgR$]xauv`~Apk^v:`yW+}5_-y?Fک nmƗ+$Ckt՝ƟqG_ۘ< (ҹ[t Jsw?wsr~?Q6 N;ꫴfG#N!PGl;8CiG̡h4|%%d]esu&{K@ 8F۹xܻm>ǹeA] mqntͯ2 #Ô=&CxNZg!7"On~dthpka{wv>0cKA cE7{nmUR}Lqy6CK]=&} ٘hl Vep;[iq:;6tkۊ4N^iOP,R e2kL?m2"+=JYX?m8Cx.tD|,1 4}2K]FT:5vS`0A{׈Dy,wX*?xlz4~^=mڣ<Kv84i FͣQVX4up ɼt3)q6V˵p%]̝"%N1gLiq>hE\P{Y:ŭ!uL^M`/F_gg}_8]6]S,t}Gv41h-gY>͹Ccn&UNaqcm\@#TՍx/ xF#f1 F%WkQ^jxuO!xhA:*IPDh-ӚKȻcII8+_ ku#'(YD9lsNV?hY_VIs2)} Y[ps], ^܎w7yU/ǬIȏYNְkwvOdUX+S -G0:Ժ?hc9Kgk>1 qs~QjX~I?oH-n=]m q 1{k8vNKD̮!IG0(t0q_%Իѐqv0oh1x?JG(X^#em3|Љ$ƃ,HƷԲA_[hkwutDj)8Vc[Nm { Jxkͳøo}t@;Lx|SoƁ v$~pVp6-^'ZA;wp~fVw>5led@mMpЉ XE ZZ5 5_ƽc<[_Ot&f݂|C0xڬeEXjMVm/}vk4}^Ȭ }] @Un,i p]:G+!8i1&ޕɷm5ZX!d;93 5\֋\BNG}[eHkƨv;p pF'Ȑ!.~vCɁuƄ8IWq-i#F2vKdj sDDUq4D?"MBzxGMƛZ6KHnYk+:C¯Pk6չLO 7Cɲ:lciw7Tc Y9,ݰm h&@; ̮Kl1lA@Y?ʹg k`q!} xNVX/,Ciκ{Lym;%%2A?9rU*:U ~5!;`sYwAQY<-:iO;ENށu>yu |xh:-J:oXXV'"?__"X'h6Ƃ`+Trh%u{X1 x;<s)-Z_nL[3wg溻zF6z`ٱnfnK0cc}96ַZ+z_72ZoSA?+fS gn}JFeݭl(=y }GMőU]XÃZ*c06dC]yNni_H\HZ6D\{{zki=,h>ִ O)WU^9X8[S͕ . I mY}xNT*ݎکx{^nvB>D kFkPﱥ<54[Y0I3 |v͓܍gW[C\֍xhf1(4*m7/%%P$qv?>bv;k ЏQX=VdVV2leFceߝcl%Ѡ4ySy07:ց%Pe%A8ϭžlvJMB ǏB}hi/ βX\I0*촇v&~M?E1,'V!FD e״9vPHvJۡ9;5Oi}v֎\c鹭גY<8ݵd;.]W`[ { [?BɀYes\~x W]&ֹ.$RD3{6L3_5U$]KNT$Tt)I$JR'_\zu_.II)K~\3Qh-z/b^G=V !mtk;6[$n`e fw k;6I]]Zih,4rʯ~ȉ%wOSd`}z`\Dvdlouuy0uA'ulq \CRЅ چTbd>?r~+l v>>⬹͵m- aA7.!Z[:T@4W"7eߥ4 uHs4Z۵ 0τ l- 77ަMdnO4PNhs*Uy cH\'RcFA"Vv9\#X蘴fgEﮖs͗Zxd b9@  5WFXmu~{L{@@Uuz񵧟7E6~U].3? js,s}68fkdعanGw>@cZH>a;S]gu6164͙Srд6 C|1Y}!列=9ֹp 6Iw=Zq֏d8Ɂ]ہ8Gxha.5\vIVͭhx0$Oq'kY9?!ݴdI=J^OqP.-=c^I= Z!ծ[}mN5 s7O!<,pqaunW&ZѦ:LKEn51Gs& eӸ8CJ3/~`zWN[HmrH!:ql{Rz=ym>xioQ;AHWQ~FEͤӮ[{ʝ2i@67^ܚ@֚XwB{Ma*r'u3S]2pG!4^I?5#[=ֶp= OexoH(E,emDwQ s@i䁮k&LnmUcwP|ips~#UK\gϲʳw4<'TmQ{3E o۫uۧ Y57=.ݶ%4净7s5$桔DMԺdϙ)Iy/ubm_KZֻ2F<-uoo;h.sp"W:0i9pf>S$4y>Z.ΫvCͶ\ƍq{mn?ku^=oW\Cl. 0z`8{K_Ȳqikp k Ͳ׿(9"G;~bf[v6zn[]5kv#DQꒌާKoMn~R=F+4!$9's.{tDc'Ytg_vnq}Kq(D%ulm4{DL5EOgddcocIns yqe B.]ǯ"iueY.;!n: ,E:~Qjnnv;p" CH>:c{]昀fZ-G.65ez: 586}!=U6!4E88e91Տxmp%ߙu 2.Ʋ~\ki7U $I&Gjq:X`g ζ_Sl @]*|֖%XN6A}lo`DUėGQm6=57l_kih) gx*Tu WmfMπKW-Á^w9đ]DV̼a6Z5mϸX <}Ѕc..nRBqŮm΀{5 K ٙc_V-U 4' A%_Py]o|:4T[ca Ѵ.ݧm`}_ɨrXpG&ƪ]]El䕀K^̂=6@xK܌:Yɩ3PvKCwq3*> 4c'BU1kk@Id1{Zh?S7K=ߵs\VIW;hwZJs a$qU.7h{8C&5ԼBI$Xll[R>ٷj~ %t8Nv&$L=@e*LE?V( *hg ?XZXoc{Biu6ncXt:jVM[X`i' j65W"h4-w1s4n.wv^M|V={ZIY.?K+B獬MYNw^״{wdman [݆Μ] YR,ulwDΗm9?!η$x`'y%su:8VI 3.t8Ib-exVƲ#H$Pu (|: ~Sr^cWc\킴k+2d1CŖFX={vi-c\ȓd%ϊjCb+VKyt8Bmk"S" 6a[k@v۱* S)+klKv|RzА9tQk"y!$BewieϱP k)h4 s-R<}.!ᱨ=dcz%9^дuW4sCo>m3?5'ָ:9v׾SSlא 0|ռNqm\1&gzƭnwsGûӬPh?Ԭ;owk;)Yֳbd@$6|I$Ju!ˤO4C^;tƳq%wɃYYUZ-Ħ\h>C2s;XRqW靽#0:m5W\}1$l!LҨd>C{C,~+%)ԸOEvA&A~hrls\ʅfKI1 L#D7%p "5hF~ F)׹ t ;ti4NR<i6ȳII{'6'8Gοd/q$rKXaډԀx Y:A$Oj_D66;{A)>`Ujl.k=H;y%a4u ZnpCTz۬=2ܙQK\-sˡ0  a B:HDcgI4&!o $O^N'@yL RI$TI$$I)I$JRI$wGW:/"^#WKC$JR"9T۾\'+הz.s-.I6u:x1m_Qc !煛0[P/snd05Jo-{ELg}r#]LGꗷ_.vۏ|Gں*lN<߈ڝ4, DD:1+7S6Ak-ԩk ah|\R-fMT]S^:-Z˺32Q~Y[~A-`# [=vz}LV^nk>?7vd]=acu'Wwpnr_vlmVdmE;VEx9skc#3Xճ_U5A`i}& Y_h80^AIm=2ܙ(+$mwh~ S췺먚ńn<  x&Cv*W?&26K6v@0O3"Cld{G)6XlHgN`%x%ޜ!ڒ(/̛}(&)&V֗Aq#f eȱ+XV9~lx*UF=[Ix$0쬐g1\춒A{cBI5?E>!^F1gh'Sx-۷tly`hjGcaUe7~!`Oiqk@hӳG ;~Y$=eFmaWo5 F[ZC+#U.7\<ȍS+=vkD>1<ʙcRַ1#T-]i+ \577 mpcCm5.k/sEo3THqNm]\ĵ}i<& Ys*ŰѴL\A 1+sitq+F6&Ƽ5$<^kkiOZ8^}q۫@g~K}ʵZZ_m KK`> FW˵ |#I#^>*t4PiӀ)tdE!e`K ApYF,u$#X<28 kƷ4GkEsasGmU%U#}k >/@'A1W6 .f4jui6 ntk]8vVY4n$G$r{Gsu^DLIIa@Tnn$ 4F{D<m:;DkH>;"HP*>C=>:*7Zph{u#'Sjv;k aOh2Uz, 0I'] V!XtkYV=OUM㴋XZ7ۺ .V=sl TǿXԸZk,u.g Ĵ 'HpW3FM\褌$x3cC[ZA{=fV,5EdKINd2FU,Ѧ7j+[=(sa0H39{iIk f߁ i~Kȏh?;*RHGF6{-2 Qdl"8-Vo<<qPlcQY;{#c2X缁c7L0{ teZQfظ;ƫBMlx{-m\43v`QaeWTUՑSp5saYYfjwoVNUEBcA%I:BzrR_3? QAvmCލ}N`kr/c1ah8lJ,sYP5ˁ;}=WIm]汄,s-/Z`w@~M6ZH$ӣ~04.WO'Mv*Cn%g\ZFA`yC*+?!MmG7p侜}nCl{i?Kc[v%%-C@򵰙GUʽ\,uvA\] PvVŲ42̺\ƛFkys6?TݗeY%gk '5l@C4*[`eC\s`y[{X\b>&b--Nf_FXwOm41·yy}u &ܜH]7YheSﱬet;A|S2ccMBqɽoa`X5X=M&] T@ V}~5ͥnK?qsd\t7Tظ^)uUzngg8~mSكB~jBآ,v 7z}{{oa*&Pt?K"[ә\Q w^Gf=5ݵR ]VXu^F $ (X ׶Xc?{X@1q"n$EŘTXcX6"`'sKۡk.<&a^-hs@.&BZ8;k  ȵ㵬kC=fhqiI^Xk{;ZZ *68Ug54ۏ:pJθ=c1"-]PM-uu2Q 4kC$N⚻Cw1!Dzxͩc`k-0zwLC *--E L9`hݿ=ha]d8=%wL=V:A'qXKc>(Ds[tG qۀ>_rnmT*iyppmZC;Ruҵ_ cF-oF/fˁ!M i8U3+a/!h"tc4)8VC:u%Tn-ѹ^#]"?XK1k>?%?'"k<*p jOg*8ցčcTcls꽞sc^{^j\%24}F;:;tAk%9Pv{$. iХ'I/c p:pn]y&ԅ,xB}0:XgMGÞUQ}A {mi#=3ge6=ޏ{h~a$TnvN~^;ٙ%ŴvOD.}`H 181%Jz =HsN|a̷kfgu>{Z*ѕc@~N/psj;7Ǯ1uR(vMgf'6\5沲,hL 7ZD *)6##hMk{( &=3YI!iW^h@$$)I*I$RI$I%)$IJI$RI$I%)$IJI$RI$I%)zu_.^DwGW:$RI$^겿7ۚϴ {JE[6ǒ6:~Cawfvmƹc]uG{7KwGt=6V^+§,@kK^?g:ACG4j㟴?k¤s\=&=H~2,M`Z& :4MfULr~% qkh0t~hkYln.{!\M6xw#c]5Pp+',$:=:ihi7Z>.}|{ ̋ jccXeGmkfd|ьZAX /iisXH_ S[aYMV`L{XCxFu4袩{li}pkO ; ]NIŃDuBoz6HAս@?K#O%nA'sfSBLvcqfpW|؆<* ɸ@a ]v(ȹZ۝tnLmyb!{ΐkGJ6[T‚ZLn妅iao5佭p6jӨhWkÜeAho,Va95niwƲuSdOLhiaG۲zba}dHvœa]W;C?JaiY\7 zN<;!-(aQ4ni`aխkѦaYUHh t;fKC%ni\ K#uv6w!ĂnWN^5%ζ杧N |t*zNhq~;\ͮ>(IӦl־N,, ; *c #ߨyŧr*q= m sW&Jͪӵ备82;!.!9oꌇ0浾$:-.-Ɛ 1 JGY5ݨ02mkCh'J7I v劉zu== (mg%u: NEq]G1WC)&u";i>.kdIUd3'+&Z+x.a۵ 02cws3uZ dL3::./ʺ=K%]?GÅms]lcKKew~iZg'vkIlNpc[(|9mͧqs${(n[/[7lDqcHLA$DAHXc@yǺgT-c+&XH-$@Ј P\\24Ԁ8欟#Ccb`Or=F=K] Dy#W\v[CK|#t{ ; $tQ7hu04}mnUl75<DNjaUqW+S-kْ;O`fײΐKyv} Fe=s}G Ttܼʚ/{IkZ;(Z$ >+Ȫ;d66-==e/Zn:i|aRM}>"AR=RVȣ%5+e_M,ٶl~2.ߓc|u./T7nw/U7x組9Bֆǹ --T<8]?ܬu~L,nةbk47<eEkd`ǀ\FGiK$?r "I-\?8"ݯ.kHpEy g{]v4"v8ʻGBNEgk9WK c^dmlDF2',m4dh߸Sm6^܌m5{tiu)]{_q浆`163)!,q$t$k%?[vk0,e)-p5w+ǾkeŶ}\6=/=1jk(cnc~"WV*}';:=hX0HottFZ[E-%.{-lqOf|i?1nǚ_kC7h26WMNT^@נ`ǭeL ot\GHpnun:Ơ.sns&fAt}3/M-Wl5ޏ!g;tص0o?NJۋM%?]5l.+dT~7(ZcаdhuԳ Y F܏2P[Đ?N: VsI}gR?ެW]P̂ D!S{ip!lHvǘ(6{+X$nteAm,c}IZQl,X?H !+2$tAVLYYm.`[.i"u<\oY]Ge;5?@#8C}Oe9vԺ'LfBˇ;G$x겳>f w fkyױ+'!inSCOkck~ϴt$gÐP w%8*NZӸnQk t5!4x3I"8W]%X ƀL~NZ:m' :Ǵ8s[GϕN/c\.Z:1AreQC}yunuŴ1Ih<̪Kݚ鰈Ш)6)J>Xw#I'!I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$/]Uȗ?URjI$Vzg1Kٮߗ=/$zj\Ud&蘋.&ؕf kӫltȋ.f E?xwp@v@0Z8{@'R^zF]h},un .Ѭa+꾯K~.;>ՒKsٴ΍K>+βf?P> ~1l;i۲z{^N;zmc ٖ>up7D湺'Um6>s=DPķ\\1p/kۨAm nK2l-& r5x`LJ֖ip'O&ָV؇V`ƽҲQT([(kuuI$GFf{qc2?cѴh:gI3= bߤgpiƚ;mtNp:6YP-SV^A&0mI 9bpa}ٱ¶}fxauoi;X LC Yٌu73IoagwR71x7hy HM(,x5}Ѭ!6MtMmYi>N=+U2Hn4p]@ 21۽nٰ=iwUn' kiinwh*xQ,Ӽ:k{XS5]Gȼ%ձ{&5NO}bֱ|3B9q^7=իM}&1*qF :{*[b 'FmmU ~t igwVgӒ2hkvvY~T/[SAĉi|A;~K)v6Fւ; H2䷢/sFC\?$ ֲ {u;ʝ`\X_;$h'Oa{9lmt4Hf^Z \ l t^OBķP*vD;vͲOiqx%exo. J~fW{IcEN3j! |fUifU[sC۸(jӹl[% 1\&J[Ckwy+$S[c[c,~ip?F- z571d=80oamphLrU,uBSZ!X-x ȃȶti5l-}۹?ުbdSԝ4nxwl5T*,Vvÿ ߎj6>vn{}Qk̖R5Q,m.f? x(ms~clc61Ձ[ϻ|S=KZI5DádkQhk_]Blih'7ҧKd$˦9ceh'bF<-cߥ'{ aqu2򭯬c[c ֿl:OrtN>[ i]mpL~5Nzu]k9.i<g'm~w_;h.u/׹s/skuTp|n>]+ZZޠk _gVpKMk CYh;j08Ϲጻcjq{2 'uZFޏuգ,i=@]G?bJ̒ )sqmkN}6F j\ <އfCZu:m\g|DDe/>\ݯKkE- ;dk?HBMf&<%zT"sW[N<\nDq'AEoOT>p1vLKߚJjߍVButӄ+fA\׫H'V9dd[hieƲ*%/qnc+!ŗd]<_TN4ٶP!NGj6mCLfkpzG-7j4W6nP{)X% r] Z^ΞnKo;u7]'8h5dۀՉax[{CsklvZv/M-wQk1Je%ݑȃ,ε0c:w5E݃|JPC&⾫N61l:kdJ|kʾΫiv׊*'-qUzM`1wz(8ٍ߽>]0þc\ZpfNF'켁7Qee7 "Q 龆Um80{ZJy6?ٱ-etȖc;z~&VfCz)c)6SCe.yF0ĆK#[Mʲ+kMOtu-q]!h!5:k]Ks[ {^t8q, $quD?c9d+T˻"e{PN[ :i_]N66xǨV[N~|V |.{feau[ocl̲ZecP[Ucx$ηmA3}#pu&-j5mx}n%cJOtfU1pi5})i5{ǒ󶝮 zGP/Wh?DmpLb]ZX7αoiKOn%DID9= yv'DBasmmsZ:]-/@'k{x;Ot,-ll+$9Ր671Ѫ@<_;{+9;5ha!ۜrOp|Jm5Ԓ`b{ԩcwriڐlӤ2}*~*٫0GsMZD:.xicsukgC@Ƿ!-osW+%Η$%01uzIqr3af`0@&7]қVZג`]^2Op~b96?aC y+}cZ#h3<{`sZ58RV3>Φw C}7&pp;w1ؕp9%š7[=[bZ{s|Zm汳0%jǐti:"<~ G_YsL'pd:q{J+asƦ<|QW٭@mYT]Smklh6t~s2kf6:Fgkf}ffv3j,V^?vJɲwJugq$NXL5䔥B-@1ic}DH?=QW[cCDKw|B-nYL'󇂽]c!%\D7oJ`nuUs7Y>[즺nk@ykbͭx#S7ԴSM;Ys{ xELU7Aw «}L }R_5;G޺b -iplP6,{!^ LMcYQKZ7v׽Ѡ.><+©|Vl-mhrmSL;$ԠdGVe 5UN1&]p6F;h{(kMa}db Reer^kK\nc#s"Ϭ/86t- {~@[&Y$h?$^e'ݲw?X;t]Ә6+k=&y̭ wߍQGP?Z1|􊩰K2Os6r{5leV檋K6OcpoߤZ찟O ۅl=C12k׍Pw?I`h YCaaynEV6A"+O5mAeۜ9;ipPngu<{WssYgv$@4Hu`:ȯ,`h- {ȕK6ֵڃeޣ!i數xj:M>Ik/v42\ݣ  uɨ_X5nqYhs6Xm}^Νzf.vNEyMn;/:-1,ikm$m.xjOR[Ƶq Z}>JY溃uL.$ sCYw`܀=`x0j:mɻ%wȗ#,\墠mqaU /wS^Gkw^>=W-uk}zUNJ<7suʣK[nCi1ˏ))Vog~Q-՘@˜cGďRk+!.~(icXy0PewXKڵ'lQ䂭ugt Іᯂ)4XD* 8:NDӌHk\D^FVi$iʱ0m7oc`R$\ӍuouB slk >R~JGޡ;].1~HYKG#g'u ׸WFF#HuEh}uƴva~;45{md =Vn6EM0X }-;CAo=Ψ:ŕm.!jݪM&bz>vkkKI H{xvV >֓k|j4ٓY2 .LϷcK,{MeP.W@e4[Sv\ג"=O:S>/{wAd9ǒG6v|Zۘ^;Dsx_/_l_X=cd~=me>ֹiL'Z)G&\<8찃.>!#=5YV=;-nYޱnuCvY};m3~=}ՁW̹ﲜV`2 :x n9\6Zֳuy;c,ƴtr{21뭹g-mxpӰWFTc+Z6:V|zz\LʃG?,M.25Z$tPzuluum7hs('ѯ;(X]7n96j ]Utzevփs`&ò;]gd2b8RjxXꫬ\)=O~Fs查gv#vU*xͮd8>o6\]c%{u|QEy8Eڵ\t >1(m $QY{۝P}&A9'WC^[qy. 5Hoi7ګKOw'0 D4T#WDJ~:?S&4s|VzJ`e;iKb1 ̀?8c8m2G՛u\8aQKW >ʟScò~m z6<ʀAI]&`x'pqӐ1YsZQ/-3, !t8ƊnX5{x>2`G*,>o;;ew_`Lc[CȮ[*7l\L7U2zM pJ^ j/q g蟕ITZ5F;1^S{;GU^u<07c_wZ0ӱ4Y䓩2$%$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$wGW:/"^#WKC$JR柬FFK\Gkt=])꘸zQpj!qsCI"V0ѹ^5Љ"8>j*+wƇi'^$L&BZI$1.ZVtlu$X@NZyTikj6|ă߸L2#mmmִ{[:6{[ݴ>Jȶ^`c5㪥x۹4v<DnL5V`i}{+U]k."͂`H33hna>K_0 lm$|8ݓzVn-=+04Z jp}xy-\nnE=@Ms7U.bgclѸIܞQa=Q+œ۶۸41Ӳ7a:-/ʺ}iOv۵YâncIc8ֺ5ם5Wbkecmư6oxgVk0%7kQTƝ\\Cph V7)skٶ@ty_QZA1%;S-L kAkHkk.cwww'4XKM}Z$S&Dk.=XP-]`Oeq-0I<;kfIkŽVZv]wOƽ.:Hq/`_@DW%#HSz~f#% pȩv{S9GvƐ:ѧ!^nOYn9p59D6-VF;v>,dXsgx{Kæ)KKZYc;BgQe[0CMRlx`n#ѽϣU.>U)[w ugi>kϱrwSoPc͌@ûHwN|Nkb^ƼsϒҰp&.H}Sy:}( a|OU3Mȱ]=JgN^zmW:\j|'+e@4o淘˄h~(= c~ G qpc= FqsB\@ qfd|%>βl"$4HlFOHvU%@0Zbs-԰zf!ͽ=/>_@jK^hvGRmg21[z'5ւ\g%..EŔ6V1<*OMxL8l{F`cYSĞZi;1]w$0)^=TZM[?{kǷ"hysp1%Wvls7Έ2p>a|m,V-'W{!94ߴy#U~[M'W7"Ikt1IM6G|{&Gv`?2DRcDȗH%^m759fͥշt-9.7V2߇.e{NKt;]e!imܜLE׌Y]P[L f^ݻ\i $_aZ7tSSC@\ݛ\b{d|DѻV sq=MZL}~=%pxS=q4 _d""חehu8ov֖n _w2M^Zs\牐$w#U!{붗1 .uAIqLCUӍ~>=y/wq(u6.-]R9YE.ȳ#r>ͥu;h`o`=6O9Y9,{Kk+t!hL3i<>ݳ-#O];k,; 7`agf:]aۻt6AU}{z}b=ȮۃZ ITL6wAk C@Tو=pm)y|:z"__# l˛HЏ~> <Y=.YUvI hֹmuku POat>lunZ~ot@ef i%Gy*T7 muCkDtZ6uLjv`^]hےNwMqf.#3ZZܼNkZ~)M,⌙}8p7Fˆ{LkNC{֖ǚsH;yJ33sKm{NT[K@-q}4$ e㽶^KdogG7(ǣ.̊K]dSvIlZvs{rݞk*kv0<Ӑ4 XZI1On{,im9YNE3\[ekwcq.;:OUd)m=3}&ֻM"c_GpK  7W۫""5N96]A VGhT;EL_<}"<Pc> oZvs>C[owHW.P CYifI.{q ;7ԞܥV譽e4[Iw+ɶ iڈ.2=|5 q[Ҳ LA< mp 6Q-RF]X[״s`pVY}S4mE!Oe| F~.uNncnDZaY5ѳY[`<:V-T=o6UmȭPlđ?(U1VmN5c5<'G7._YfnD=:-]C73݄=> V 'vyʃ^D`L)K6꫱x#*5Fƀ;R4@3t䠔k,HhyZ-2?"nUk@efs\иT]>-ҿP̋Dx~*U5oQt$;x?{|O)Վ=5e³i]YCZ&{ʍnA a~Hw m/,Yk\GvV dyI{cYgeuSlCvO_{+Wu[ߏ1*k k\=%8ϳR yG\Gr!Uį9Xxmn{});HocenӎƗYS9 A ߇(\Б i[iv۾h.uE[5DA7Tr5\\ѴڛJkMuٱ;gv=8}vTZmC?JhZQ6?"P+a!ݚehN7 WW~Ƶ3%ޓii 5@js6'kI$;Vcᢻ@xcqc:l:?MKv4v%RȖnqkkp6tO)E"(jn_ulJݨpۨs;罇 2køhkH@*Z5۱1iy"K1u[^(.w !K\մ{8A5 $*RI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$wGW:/"^#WKC$JRM.ե¦ xik \ W}t}ufe>. YKdk-GwanI$lk0Z\HϚ}G'6Og.,yYv%\3#M{>EIM'U62K_-8"\>Ԩ.`k~>mqr1lv]p`wl?aa[~;\D \<g9Z=]~ߦwC4$ B.L=G7CySsLi:r׸7$=Vd&;/.OwLbdryᯍp$) ?8@&>(H5q!RkÝ |P zz]mTrKPhw16?}VacZm4wb'Bߙ[:&&=uD:I\bl-"F؄cedZ;m[cGݘkv>Ʒ(7fcYk"X9uVebGm sO'W8k-uT9")..o$y$+veCubVk }\2'-'4,+i~]ev{ ;g+f \ZDFƼ-zwV5f^Y(s1rrO>Գ-Ѳ-LLQUgS^3ߑ ;IwRWzN=yKN}w#ct?GT[Oza|}'jx:]VbsmpLn{\Ho)us3vfA(sCl,l t%o5Ц/~@ouU[c !AVw$9A:cMߛnNI3j¦[}.'lҠF8 ~5Kjt͏ }Z^ý7I ([^T,w 6G$ck_3;fPK+6KCNͤ |ԫXr=J@5YI3:K;b{n y亏Ec0sBWc\Nqp t4~w誸~hh'sXn  {(s 4mf:CVyh CK^lӑ3ഺ;h1F6&EaUo{jpO"Kmcͷ2VOw'Uշpqr-s ů`hdm [}Pۚ,nAZk,xE]19ͥ^Mϰe0mTֺksmo,pM7 \ ?8l pȾ Ks]7f.wNqcfVk:zA}1;6c[uk_/H̗7i [sMy-]?R%Fcӱ3pӓAƹu47DbV[*5&]}ZыQ}uCi ys+kȉ:SߙԽm?9-t<$j^-w?aי{V>j x߹ۺӕW#1ޣI8k1oIɾM.6kǬcF6^pb.KCpG+ U6|*wi_1* Ռe·5qE 8#Kl}I71 Y2O4l|SMnsD=Rژo]RG\=&huGͭ24g`t[zUe{}+zmL0ZnQCkc5'_c X=D$WFOUsݑacX0WN3;&xa^% *O7Av-eBh=_=?4lV՗ %k׼Tv9Eg2A:n_UאIͣGrtzy51z{\OoZB9;1 Fen x{Z;R QI5*I$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$R?URKrH}5$IJ^1auS] .N w@:wQjVwm#⚦>&%c Ƴ=DAnXޡ{TIkK~Dĝ`x!"ӹͯeLkDKYC[n8:L{H{t>>An}'[8w50Mm$hJ2Acș X@h|<^^suz>?4@UPÏ$MC64|w67h7An.$VoªthnקMKƆ=HǟMki'u3~peeҫ[^;^AxeuuQkPZl`.A$&N!1I{gkL|c8Mi˄wfPp7LНڙvom~Z)ct2t?RmF.Kv^sS}[\20WU}UM9msX7A xv> YZ&H~S\8G%y9ockFn=owWp^$A_o~g2suZp~ppf;[Ih{utxM@˗6Y-hp*Ђ?uٻ'me1ŲtFvH&ˣek{]a;~y@B벬OV3{aŶ~zˬy##ccEu?&) ŢL%8ΟrQPPY +$K5]&\L7a $5'S]޵ag#>^00 YٮQc<ˣ4dA-:|K%|Fh*GEZsuXVbTjmуk;Pȶ[mΗ< h.>A@1ddyJY~- k,e:ݜ>~!/Llu591o&%ƺ-/CH.[4Zp#7!7A~}C?FֺʛAox-o&YsCMl* iôh`6ӛh=k MtbduVquXul nU+eC[H!|=aU䱛ͮ6oiTns@}K}]=ԑѿ}ְfeίo]M-ogY."v4Mvֹ{Q浌so결?[O)3uȪ X'?v|J6w:؝Kczl*:NUn=kȆsSY9롖;8ֱ!vݠ!Bڲ)e#v핺&^Xw;@`(Xd59}ޙ654Fgb; Q?]fÒk- cnoҖ~̣mip1I'lK~Kag#EĠiw cǕ$cH%f^6-l.gPi,,sAn>K67KŹ>A;꺚QZlw 'H]E6߃vUNnoc,:Xi={Z}R2 rX'#5Ӷ8wuM`` <vZI$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)Kr%?OԹ$>I$/5=Eմi}/$Y7a\D7ZkdyG׽4=n ˇj.9,4߻~~nski԰iYBeG.Ulq `wNǕ,׿kG &9CצasDqn˝|ߐAIV-sZ膀'6Cp 6@+Bփ v9CR_`w4۰36SMW 4@VAn3?|s\8Q%7v8y+!GŝQIUť3>%NMsHLAc6gRã@ư N97t'@a} \OtỲ *zq LO7XUfǷq'n>*=G6d5d6̘6f tU }2XhvR  <䌌l;Ȇc rk/mk$iky@:uYTORFP M6 >f8Tc\G#&?Jp7=Sǩo# bn|,{^5{k pk4A,r` d|}49ֲƻPIC%škaxΦƱm.oSɥΤ ,n_? "=r Qq]mA')ov{\%L5id4{R,n5?zv9ɑ*ةO:r;c |?wL:_C"9nƆ} Lnr;BƂ4\>Gbf߇}vSc BrH> &v9s>9䗺덗縼xΊ>;]la<;'>\+{.-d4n<5Hׅ6>[h5gk6Υɓ;yIl5ս󹍀 [Sïs.53{^Z@}m κxnF6pCG^uہ"ZDc䕑ݫ!dksCly\Gy jljIxudmm't5' #_ưtƮ#xB=FHk8 ;_Cr.4 }Nvpv|Z|P.eG#k`zmOwq.:*a$uy$Lc@ yscID.ƴTh;MDt8 \t k!?á,.:kk~6:\5?;ZXZ뾒>C[0籚8CBY=p$9Dxr4Tkx3N׶S]vqs\+{ %"V'.5ַcu oq#VFbdQAI;q,].fX&oDJ6Id8X\8OexȦz閹QvwPǬcVq>[qX.ԗLDau:N=9حZۯZƝ5<%(Cv̫ 9OԴ[I->;ky{p7 in&OtZ1j6>xiKy0S[-}7lOޙӢz?`edR\5k#~%cU86˚Z\O;6Σ1FI6OX)6>ǵ#2"ZC'"FSF5>t'#_S,syY.8έ0gZޜʯ鴐]$ij{i۵{@MwQ ;,xՍ{J̇]K]S/QX$4x>gQ{˚ 7&#Ah{b(qv&%Н,%*鬶N ȴsGp9ME ~==FPZe.ukkoQWCW]co#4 B^f3~5U ̐Cc)iiSwninƃo V2cqQ>XHqe=Xh6ĉd;x(' l5ǨFFb׍mo 2sCGd!-3CW|Yck{H`boOlvz5cAO+&qhoԩǛA*5:R潌 %ɞ(T/wZZKZţ62Yԃ[+5ʌk'k0qr]uw8ô-;)~0v[k=ZؽWu32e%6!-.pښƍ' >3zWW濩U7J ZZUamnrŕPw=D:wQ&V=v89T,0\=Bi:kJ|$OZI%)8LJ\㩑> (egr,a55k$ωQm4=a ѱ7VZ4q:_hktu դ <<OxWj lۣ_;3)me.nBBgd2sބ*1mE?Qs@Վ2Gọse{Sӓ]- ,8/άy xT\t3[K,u4D&4Tא]wF!]Ujy}C@p>WQv:xuhA)Gh)kK#UvXC# Rݻ'Wm "]TZG:Dz[ׂ>+i(}?Pf.Gݛ}*ZϓAWm!uZFN{Γl |cSfeuL/͹ 6S8Um|2v@]Vu֓$~k6|aMX:76F;'t~Õ[#nP-lXeW{8v&gOu?xLPA2CmVH4 K]^+ps}ccwJ;!?UϦ9u d1ح, n%n;kn 6!`ݕ]c.-:ѣcT FC.0ΠiΞ:#2X6L(SËk$4h>;" s鹁|SyBח6Gi!;LC${yw-ĐmsKCHwGdz]`0O攩LK$ :3/MGÛ~JAk/!~|+!Dz\6 i?KP|ڜچKafHB/s 3*ݸۜ+~0x*]ZtAFh+t!7n+5M9?PXt88E7{\瑧'!ۛw}ŰדԨ5=tj@,r=VzctWq-dWk!1nqD(C| eao?8Yp;{irl=#{ɹ [POhkil ?(kEuo`S:˛ $yZ7\~]]~uJqE8,m>F'Xf+s^lW7m,hﴦZI}jC}`}Ν{uZO]b}"ևѵ',[={Z E:6|Y{3aA s]lrN(6ZYuϩ#h i:Bmmo>ZAw-W*陕ۘV̺,=bnku|@*hvG- x*d}lZk[TfvH6%{lEsFO [cֆeKwL@UŽ·mv%,lsi4i3)h[cݽ!CGrski;|ÏٕEXǷsO;>Jx WSkm{hs\ƥD޽m<싀P8K2?KN#GoS6eIO[:^N3lT p7tZك"n"7n4% Ee7d}nV5GaIhL[0󗺼!sxKh2 ad:X k0Z\4$ tv>DBFsky> %Ѯ&UzoskpWXThmuLôo2CN M}^z«Sn K9:^=f \A"!Б©21Cױ hGKuI˗U"luMmy$uAenMUkv0loliٷN.z]ۛ{`ÚH v6]y_1`>[SY-]X7i􁑵 x "}SdwPMƫ{-.֖5qKַG> ݔ9:2~(pI$ DjenlZQ98Z=FҴO` WNpwnfC8y}]sIf{>h^gԎMev"%| ,x.I $ ]V|o~-dIRKN`J+z/Rw@$cBF^Wq{7hWr>{+_dVY8@}_ UO_kOr `0qj $&u g.^(||T'T4tp12QLMy3ܺxwSg:%{Kx{Sp CKDZ[[OBUzeC6;0Kc5X{H*}O굓N$k#tϪmHF,9)-}Ls ZP @O;?*UIzQ:;_eCgW[S1Z7ʕ# Tj.nҽ[6Z6"Uw4~ 8 \MxATz-%~MT®^L>(_$oԞ!څa )F"&^iY?ʱ}7K+{{&(|7ON?_"Ab[ҦѨ$>RUG:G %\O^b6`! P~4Gii^̑vҁES>Tb{g}\=٘Vh^c'q[nka +۟вtuv4qeVk0kT=ƢNr’D/~\Amk@?⯣]>Po˚o>;X{ ޳2?UnLi.&hyj{]Cc?DIt6}D_:eT\]i|jrRZgI @-'?Vn۷ )I_wBL0?!tŻ%5RVON5yܐ߳:01/rJk$7g;m܌ϫ}y| J5%>}a&>t1G! |u~Tn:KgccWYcpxW[YַX: m: $XWM$ǼH:he{<'_2c_,kaqR-cvBg⡺f7`h"`1Xa/ և9$m"DJm`]k`̬{GwcԺ{)5݇crq^cPC`Ǹ+>,Ώ^R-i ($hxC!.q.!eE@CB%ɢqAE.5;Ol5Dwy)w - jE`\ 95|k}湧%*Pt18>Zcp sʸL{@pn[.~\1iƦ%ip4p?5,|Õi`лN |}۵,FPfOF4~lizxֆTy.%+-oONM dlsCH3%8 (bjUյHw¹hU*2};oۧWMNsL6wn ߗdL[Ucx йIi69/+_JYWXhPkj ](^/Y%u`c>Ih->>REY7^wX$';-7yX q \v{rk6^ֺCϐ#te=n#FtUr.6$|ynqD^^U׎];b@:|\ZIqVY tNi,vW{-&x(pHH{l7Itolf].N>6}En%UO/ 2un]>:+if[(s+54 9vLlfU_Ek=5iA ՠV 6q?x7hJi Rz(krӽX Ohm<%Ruv(7A&6FS0?yѭ다oʞ_Ussg2EVlm %*s\疵\\ Ԇi",bac^1.Kk:~J5Vp߃ZCcu=Be}SS/kkSudOthO]:{׸'FYs:˞?G;@hu}ۘxkhyHRٕ}Z%YF!}n,}.dv@mͮzVm l88#qo{_ tlls)X\4Ԩ<|t܌qYf3eMBH5;Ns^ H@U,l{)[Ctn!Rt|$VT4ԦZ}ٛ4:4%Au<ّ& 4=pA@ki8Uz릡{l$+xۜ-#wo'ǂLm?KysIN W-hn Y V =PRg5;ǿ(gsYÔ8BF#m&۬t` 58S4  EA-sA#[c4kOg"h2XM=m%QPa'#v584'%#"`^@l5La|Oғ'fJNj5XK$metoM/JGps `!ĉ-S[>K *o*[>yTUlʩ?P,}^5rOrOE fRU#>6o\UA$Yya4L,[XaZӴt_@׶)E iNN1 ,1\will#l;0>sNL^lV~wU& xT$}X 5wp>JTr%Ó?@{ÉCTL]dQM6; Vz e@p"Uuۅ{1?@ 7f}Y`)mkH ~o-"Cs#Gu gt*59qqEoNm@;45)eOpo;H1kzU`6e'@uw͢GtBJjЛkG$/nUkD[~ur:O5 p!ә":.*r!V+EYl4G2D$H=wK-sv9R*jւ{,4˶L/E, 'CtVmX֗w7#IHʘ]`sDye5Ѵ 3=U:y2q90gI==Rlc{vRĵޱotUl5cLF~!m^GπuMI΢aĖ0r Ld5@Aq ]z6;Ki Z)V|Ua5A6<ƿMܕՇEZUM'-jgr"AgLeֈhY渜/vFmȵܽ]+M7\^n~C,k:4L^#WK/]U)ԒI%)xfE Lƾ]״/߮Ff}Y~_nm;)sy<-c/s7o keF 'U[̰GCm׸5&@5Gun`d3G*V g]Ѭ4@I?LFݏ]|u%.kIȎGhWl{$> nDn3\ƹ xІ#Z>8}ʘ59.L|__kschTnn A~>s*Is |~#sq6ele @ʖHfV0pq0L.pk F^E'tv{lg 1Ԩ~*-yus\`pB |Y^c%>* C+eHg/-Ȉ%$-MSP;At=ah8hw ǂvZD|xh#q5KL$0|Cla6a.3wvN sz;H%Ͱ@`j]!!qkIuQr*.j^BW1/pkGpN' 4Auvqvtײ̒_=ɍ ubv"AϊvCLn'Inzn8`Q%a$xpcn8f=Y}qcĞ ȶNԀ{K=t>DϫXFTH?H vCM-Hb2lnD顔:P6@ Aԁ;H2xkӺund? }c ֎ } :83"GҺΡ"h=F9>)z՗;!ӱǀ 'U=v:I -&Nr5=&퉍t>J7]HDm3ƿ(f5 Hq>GEUHN Onݔ uvu[}7wӇnžUqmm..pofŪΟ.asD>Vnu~v4w{uEex4ېƷqu'۸b|U<Mt{&Dpi PSh;el wuyāβpK*{6;]۬…,Ug"\7x@t[=~=G\7:ᣝuW50 a-t ,2ص,l8j+uH֩ im{w<{ÿ(6qZ/m"6|UHM, "#1s) d$4h'Vpnc_]6[y lÇ˺sd.ȜXت,q-t[, 1΄Rj%#O)Zc։%ێ %u 4 BNn.CNv<<w}` Cl{y@!z ! v%Cдsh׎:}>@ӴcƩxUsCwʮS 㺦0Ruҥ][Yq!V۷aVw02i/w !*k"w 2'Er`5oiam%GτESvK1:qjnyIٴݠd*t]>sEZͶ$}G?KR e;!zl4mivvO?FWU9A0 jC0ٙYnȡꃾm;[Op svpEvZ6Cԋ]{I9eDV}.h1Q} =’7zazgH]Is12*/sT4n%::zo]yƇ8hΒ; RΔd"2 \#O |%:Hx #^GIw!1MuJ584WNd'xr3*cH _;!NQe\&{}+9Y餎̱ bcM-zOEp`sU[8 񕯏L7Ħ;Jb_LY\eJ T ?؂#0̍E$X-76rM,f5L,5=l ,$rZ4;F'4b7s@Bf >Iuj}KXoaMSlkHָ9aQan[)Ӓs7T8 h(V'ঈюEAus)=7N1Nu` h# tt=Pw666gKB,1t0GL*} v{Z,{,$Í_Qv) =ns{\{?(@ѻxiBCC[Z5wqR8[)N|tAưw;f{{mݮ, $΂|ߖ=58 j.[~Pvm 4|"}ՌSmEIV2ƾltvhF7$LW߬d g5>oZ02ﺶR*V~pw}V]N8xR\Sxuc?8}iR0,0ѸFCVڬ$\"w/AK]e9w;I.PEsahI%Ү eLIT*oZ皩n nj_SM΀LݝIhx㲏!k70_`\NwgS9h6>ᤂFu<㬘`9纖EI.K{*b m^nۡ2!v?Hw$-=q-ė f{1a@ tQɾ CD* qh@"ۜ˷'M8XVKK~{Z*era*U%V@kDL#MORc kȚv#RZ+k_l; ;,l%W_u'd 4J9u^Ikǖx[-y*qZ`\D'W-YӍwroPc/:!a{nkzEv@ $p䘔v+Cu;>r ȾTXYjyq0[vQ`fMI-f2WIu'C$ ;#sy,licMǙ">$cuZ),ˈ0@;"wO&ˉeO!ĀXKG+lhMMn6:W<~=ͯ8Vd316oޒ)ߕi\\ +YCIk&5~H jȫ= d4/<^IԢ0yܩe`9`667豮-hu!~11jwN0uNpubnpstJ fF~@нQTG.I'Ry%2 R.ҫ+K~Eߑc=CI%)$s$I$/]Uȗ?URjI$gp{|Ze_Y?[s=ikQ~ :iu~Cmù VY}:9/2mw !!ݝŠ4\O6;BrC/>DNFm*lłHtrT˶=83s^ P`2t5rʰό-tK`/`̴r| W^]Z+M'!Tt kۯ4Q(.B^~w^dntҤZZ+[[%I-w4w@{| z>@}:x1-vŊYZe!_k/^wj5ה6w*x+epX w)m k9-5X=U,Γ nM^ȑYyo@|% J-mr 6+kZ''䬵Xe#: R0Xty)+*}m{O]`qߺpw:r3EAuR\d+c6"AXEM0ϢQ1t*Gw \-5Zij6Zk\ Bԙ-oD3U$:L*uvv;=qhh#[e};8'=6Z#ktJ< 6Ks]Y4Z9uf%{CZC{:5p=vuYmYmd>> _cD$,ˁlǴGl-ѧIDF#`# 5~i fiO"<h sKOV1þnc`7\<.Vպ\+c=&DNsv;H6ӿo$,q%'?NN )#Jnq~F;A e  _+wSv= eijn;W w  .}r{?t2[ckuvmw;юluQ . x7dzgqN[7}嶗|0H :=>9}bkLti+K #R:纠g5oa/^ht۩o`s,saiv–66Ox{NJ _ccoѽ鸝He Of䶬a:[G/'SjΉ5p-f֐ t.qL…nmZ&4Bz/Eu*@ ^R_W1X D{tV^nai%85epc˜5tm-i]+pO.'9T1 qhZH3pk*cd5[5R֔Gg4AJ8.Rȳ(CZXkF4F=BY\s͹a$1ʬcn ;M૾܋m.9mX 8yi6d[[8}{vvb]ַmk]:H@UlNc)0~t5S.cf{9f?pɳ$1RD;wk+|,aCΏcD%.=mD c_t.[&& h>>]Mwbb۫P`4t-\Ϥ8,etO'qy2e@߇AYVִ4.xq> Yvb{񤣇o#A$hR[NV^_l.mx,ۍ?>>h;OJo*1f}7vm-s>k{eVWƺ+ՌL{$h;j,ɪwLxww$^@k\a+"I]s?j[]VdqpG<,8=\8O|) 0C">`بhW_Xsk؎$M7Q-kvBa0[SρѫQCDn:=TGf ϧh0Xzn|$S)[m:~p I)˭ckIgq`sݠ q\CC^K'< [&lX#x]5)áۡ]oוS^@]$qZH< a<\ZI~:4wno g*ʠUWˇ $H_F`FOя#Sɲ~hjUkNNlw2 y1VvVL5>p*VH#pո?PG}V5+n`2\t+`1U$6#IN.p堆^-fۨiiNYlsDV@ dރUyfiIM{ n }gy%L;ȃU[lY>yga9,ZA$h8⒝n۱ߓx. .RuGӼT(69F¡r苜bu<#;:OlM"L&ڈl00y6"|ĪTU48L8UrzJn:ĸXT_[[kOu).۽΂Z[8ĪThw86d=EZQ8?W#\:X߇Jun>Eolˌ8 _7-0cG w· gG8N K@F_ ֍eӡ>oOa~E[nk) >G-V?Pȵ}kZݯmm0G#OⳝV~ٍgS;XwuM&A:I(Ga<2XlkN{}3n3\\Ƙ|AZzտsmڣ-zb ײ{eEOi6ޱcm`W9gOD~o!zZ~T^ ?3\r}pݍgţ)tS}1#][(uKc\1%m\ٺdĞd3+"c:Q^˥fa=in}s/6n^[Z絯@8~0mM:#!:GEqwp-?=ʬ7/& : Nk{ZD y'84h2{|<gRx(wT6mq+V{jX ?sn öˤ ICPyhc; NVq1לּN{8@; k\Dh|D]A(n'Yx.^oBws[DמܚzO۪;6gn4!7iv2'S,p"< C;%^8nmM s;Jj\4AwO:/pmfkap< 8 `6?!̒v.eљwrpKf9V}$؍TaAR;4Sq}؅v,u eCD*tA$2fk'I R d{;uOr=#cD){A >Nc (T@lp?~+Dxaq{^!h47."5NB*ƙ.ǜr|vQvFFHiǂkI %7N+cAԻ*c%SGZv. 3wkf/M8(.y8nopc j@H=AsFC`uA ̈@vR7sF=QpV\ƐaH{bw59Uˉk@cX>)uP4\[%8^׉h$1ϕI/$D8;0cvOcN m'Xh.}[fHCXx*ţӖ!0S?cytmRSkcnc摻 BTƺWf&@dl md=wِslp !.=)\Uy9-g=uUj}.Sv[]nѬ,\Q ЗXy 4,FѺjClynO$ZA1A(jq:NMf-A=9e-۪$Ƕ<l{a0gi"k'I_U\hv@p cwyOvYCXѻ{4&}VNLA0=kH'n7[#6o-yq ,N [NtuF !-S:V [1=k;H$h#zT y's~IsL!B{ 1ϲGw+g' ֵi f<1́ϚZlb6d.OueCT,!IJ*$ s* d~=$EޢYVN8DdeYj5pv-78Ɠ ^sI2{Qs^׆A`Gb!i֍DۚePt0F>}k-G@>aCdy7- Y]-Db|ԥn56U= nøk~X9aIxwOf훙Xs*и `nJmue vgPL< {y6=Lc:i1Ş/n9KOޙ֖KC ġIk~-Eg6өOk@sCE0Ur1&;z4Mul oxoQ\݅ΐ\ZD"4y(uqSCZ6!^ǫ,[fMy@5!O]& 1:oO>h2LZX}ZܒdTl=CwT2q:MT\qrps\g]CɍcمkִY6@~+:ux鶼NSK|G II$+};f l-l:$E8|v;WX,i{;Ֆ8xHo>[y^xёafIgORt6ZLAius k#^W׳q]ǹ9̰8{|sGVi3)P}rIyՃR 7MUmkw8K pp }[k' Z[\naL( ƅz*݁sMLk\6I#׎N9F܍Nj]5w<;gP.m@ߌ^*v[n F LD>D?Klf@cChcwg;jO^kmU`OpUX~CUE44Aiݹڝ5Kb5L i>sW8uw%8Ǜ~uA#"3ebǀיݙwyא4'B#+dn5ɯc4|tfelk"$w\Hֿh#g`I?*yɕQ$ gA(f͎3ΧT4)$I*I$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$?OԹy'_\MI$R}w}J$ KOf^s=ѝt#ѭEk" LȎQXߺCw4mnUcŀsf'i~$S@BN:{H u@Ў}oc1ɯfݺDjEhnѼ{\`>0]] G) X>w2v)P5;<~j2jkX{ i%3:y*mpUa|OA!k*un{AqNC&tpi uo 9#䱲7Lnn[q,{} Zh<\ZYn'dumHwݔ*.&\<wEe{ 0:*y /1v}6U-q50Z1t:NW6{u'TZ=8II w+.K@pzU 1+I=58{ C6;v*Msqaԁ:yxH]K#(9za邋m9Xж5UrӓcDDȂmB$vM[汭0~wOYeTz o?1h$ AgFKcc dmNT2YS}WF i3c\!K!3Ll!^Ԃug߹L߹@5׾X;ȁwŴ́3k0GB}>| %yXXNΤ冚aspU ô|C%CS]Ic~ji̧ٝ**mm9U5k-mB%lvh3Pǩˮ$n-n:}#eY6eVִͬhn 9-ZlT$͜t#^FUXȬ< ө{->E qŢKy] +a[UnUPvߠӡQjs,Y{Klf'F=V}Lba[EzΚc+VNZ\]YgZ6snn)xKfͺMvNf1bݧDuFOrF kqq򕫅ЛsCs6V羳:AN=kPpkK^7F@5%{ʱYn-ӌK\ݮY4SkQc7|l#Av u m+^7;I7GT ߴIk> cCVw$ =8`ecX﹬YAo*ƭܣn ~v9QzD?jƷS_w5jfu8ovk~)gf>{c-vݯiSQVkK@7tPq&c]Y`c_^qi,[\cter*qy ?K.?׶14{Lơ8m 2W# ^/ 6*?6 }[osL3,VgծNCf=K,O!hjI$$I)I$JRp dRCsc{4HBo$| &I$fvez2ḑu6nCTIM]K1$zQw6q*)$=ۿ:-7\HuSI%&~^EIT"IdRI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$R?URKrH}5$IJ^CZ>TֽyxG$hoF_yǹyAѭ|+GU0꛴?/ͶX" iq*-is{C~ߒynU22Vaߝv9֫ pvT:hai`.y s+fd>wYI`% I'FWUg%S} DlC3r8 .! X5p4UϵDoPe $|N6 @3[MΎ0]@[T_l 8Wc@KH~KbbZ|qRKcǴ'JF@Qmìip\:~!2ss5Vd Z啳kdøq'IYe¹ȯk{O$8aKlsD.cZ Y]+n.7] ~ f'ZM2NII-yy\*Z=;eoku:6I O޳Numx,EY YմveX8=$3A; 9HUv%ρWh,9fTl?=Oѿ.@}_ }.$_[lC$~ ׷!8 Q晹U;u $ZǴش5-]aOWv,p>L4DeU[J,3"xZ>DP.oe!ȏ GGpWOVl#1K:V&Ӵ2{l#)|/uz?9ͳ$ݶ^O.ckosُҧAc]`ys"K|N͝  =XR؀-Z( K+;8Ñ Fq:IFtQiYv@%T3:vC-W-'yO$LKC@TCvnȪiw?5nE5WpLNYSna9R&tqzel558Y]_+&q{6kr`~ԩnEnw?4P7HcAtNk$A|$򚽓?mw@yBEP m܂̬j,{]a{^)h!i{C}ՋI4wGt|ocZ~ӸC؞4Iշf,mn//5[yx"4_ Hq @葫HkVii x[C$q07Bhqunwj"C\NY8' TC-شvC+5KLk)ۋ+m}A ˸`N-F\ ֈRvf1#kIcom=eݽ$௽u}A͛1EgtgrC"&hqo.'UgNDQ?GcI[:|Rt7٪` ~xuVҰ쎣kKWAN<jzM4`eͮ4VvsߔhhEu0 wt?$ظc_{~ɝAv m`1-#c̮42EUT"dqrE##k. Y| jjbVW1ṷmfq q㺦Δ}o7v=m ,f Ae,&40tt*FZe,0OV::luzicq2c]ZKl{su\P=Gi,| [s浤聓e'#lP\%{ۜFT|C^b"9[nǝNMjxk v4\ Z td)#oV]P;uvBuױ.ᧉLOۯ:iVe43|D'nQx;f48=ܫ7poďk BD@/cvBpfy%Md as:I@+{{GmI|)/X#mq=1nxY$qkqu$,P HPuԀΖ>ehbuUb;q-c퉦uNoi-l =d6rh5Jk4Y[ vAMV@k csvK?Gg2^|eD<̝Ol͒ usN4n'ik pZ~sU!ٵ;>J.-ih<% \I*t8cBYvto<|SfR$hYkpߋGB0U3^@1>#r̙hțZὺv#U0q\k;FxyPeumC ^Ѯ&O,}l6X4sE5&L!@Ijd8`p{t*;l$TX沲x¹X㧱\|=uR5# |Ffnu*si- _"P6Y,nsȎ8YMIsoݏ۷]u :pRWvKCے8혨suFO R"r$75 w3;¾ K[#l7T: + Bw֌  %gu 1inisA;vue8~KLYc{;3E^SeĻ]#^ҍǣ % vMBd ǒO`"uayOi: SyhL{|x:4F &IѨ?ZnD"Jnư?a-;4V#i(4?)V;=;x;ȭ7\Y˽l3Q{ n@=RR.ljG܍WPk9sf@tuO `HGhec_g_Iwp[M]{Hq&w;4y.IͲj!mt빘I}2bPa~q AD6A3Rsu=ֿ ekt.V<`y%Fv7Sn# AIH(`hfAB#B!qt&H>aipә>Ho}]0~!8Ik==4w /spC'xjAnZAvgН4mZA25z9I~]@-߷FKj`AcD>_X -tn/m>k I 4:k+y:נּr0K 憋 HZ8TP}Z] אFۻlZ"5TEa4PڤF981RgAZ;rAdvihydl3殾ݾ }2}Ϗr|PSf*i}aXU{=ZlgXzO;NB]z69,&iR%Ylʝ =_/aDŽ%H%g=9oI|ZiespnKN;k:[|#,;EniwB6BA$I=`Q)hyc @%g85lqui>(ϵѰ$<7XѴIuU7ݐbZѰY <*dvdԝasCZ-&]: EIl"5;*E;ZvH':ZHk';7z/潯h:8Ijsh1,DUqN.Enp|8|cza81]cx vW}IY/@L3c`C@lljB̪j]k KgPbg+TZ1 i3?H:c[lhun3x;EFU62s1^(\7iNJCup ۃޏpɭ y>k?ge,̫1 axs6&xtV13 0 F5b-cKc˃X;D΢!2iƠ5ptDn-lӑk$@׉l7s7Hfxԅ6܎Ͻh9 K5׵og%]>e7##&dW,5KZAe*쌆RԝZa wcإ]UzxQ]k}j D>'t=D@g6HEbWPz]aU#t([.f=eT7$<\'{]b(es뱠mh"m[C pkH9AeӔ1c\DCFL{)vCO2>׍)5Jfgq~@ l4Te?47>YC-mu~Zc~vn5+sw6.]1ࣛOGζ{r.pak7t|֗I%*$I)I$JRI$I$$I)g6f~mƿЊ>;s] O?}QyI)OO?}QyI!OO?}QyI)OO?}QyI)OO?}QyI)OO?}QyI)OO?}QyI)OO?}QyI)OO?}QyI)OO?}QyI)OO?}QyI)OO?}QyI)OO?}QyI)OO?}QyI)OO?}QyI)OO?}QyI)OO?}QyI)OO?}QyI)OO?}QyI)OO?}QyI)OO?}QyI)O_kœKzu"NqhlzpIJI$JI$I%)$IJI$RI$?OԹy'_\MI$R4HkW}~-\zFc*onpTks689sSr];X '@,ˡ!֑ Fx*W֏މ;9.uW5v5[MaKp,Xӿi:Ӽ'䍡/o`5ȅ H>k_,;$vMe{< sAT Ly9={7#0=\;X6.:Bԕt-T,t5Нd i|R pf˼ Ca.}"?*Z@k `3[P큠w{ ah|8w-'4!_ S"}!!ȩ\?GB=AjT=J#w9n;|INoVoig;%7~UM{=-2C>z v[LU tgGs)8ՏObWC^@=3 ζƑq8WIc)cfbD?y#stӃB߱M녃PSLhQѱ6^N} 9qil9O_ ߲h} ++Ao}^ hxħ-Z}sGO;|1ՖZdmpwZ5qOEnҰu? ='p`<KMhSEq9qؑSu:X[e&FQߑCZhb8QavZP!"Gf xp$@tV9&^NdqQvUհg.Ū3v%AD"̋\69l? !+f'^8R{KX-&Dp w>);xy}!ѡeA-@ ̓ii1'AYɫk@e"d*ͤ <5;FR8X[[Zo;dܝڔAyq}ln`I$;$w:9Ydѽ{90ŷe79lie6^ęA׵ alA4FR*_\CN~I}7ڟc-۰P "C[|`Cs-Sqp,s}CREϰKWʩ9kq/i.`cX$I#w=УPё{X !7~vߒmHI l\dd5`Sk 3-l:Av-'^+T}ont\Qi.4ls=;ׇΩiyﳷcm!-lο}B5KGcA{'^f{vZ]WQk}27@6C'^Jmj3\gq}ju@u~99cC}Պ (KmՙMpT: m x(!RI%;I$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RVzs0ƯX갟kmzzիf:sY뗴:۞[5 >8K>tΕcձOc[I t.i*|%?Vx??%8Y9v֗`H WwH}2K]Yw>hjum4N9%[sNx9]*2Y:_R閊z-8K[s #pXp_M~,n ^Z[i)I%i+We5Y{,TX#IMTzLcl87Wa>Ɠ$D'zF2nʫs#"RSYM:K^9RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$?OԹy'_\MI$R{Z4W}t#7hD̛}WGwm4NMȧWY\8!0ǹ;VQ5z[P$D TƬ8#VLnh0G1,AӼ~rFifS"Tcsq򦐼ݶv;sDk;vvT걶m +Վ  w|D$F:/闳q7xƲwu+-1Y&;ElN"BW}kOIA)=K 0S6of ;!$+#Aܐ9f@R T.%ƇLä d>jϪK 47`ǔtq {BAjZm Y:C0:֚ٸ5?65nsO㨐aJ'Ğ"Z sL;`G`Af )c! } X! ?i~׷]:^i~K_ iq;a;R5xnBk,s&84 .v'J@܊ s*@ |GzpcA;|(D~̟G->"C!i|E,nMf*c\t5:^SN`.=>I&_[=_c>i2X@@#%OI+ *I$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJ^*OY!yS"}hyKoSҿX}N^zM08\EyޯMZ?ŇM쮈Ey57WM#SIng'.+SkV}u-_].wh wD?Zq:q7l{C>gĻY]]cOPΥu.TS\?5pt}; z27j}tsas8IohgG/S.}n^E5w 0b15U>DkhOPgCk\gTRR6.^'ECv@9 W>%J?Uk ^lvRI$TI%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$?OԹy'_\MI$R|s R?*{2ާzWR:iS8lԇS& .9!O$mAΝĕU9wPJ6~浭Nky>ԏECc5͑=wX0? N>#M]YqF _kVߴϑ$TPHyjdU|cð+Tuj?q5<;?7Qku wi0]wM Q8lfreQ (5$Wq"y8f6 w,,2|Qid]vWuGl:8I,z~8?k(xp~☸no'ߨSI@>?wIn]#HEZݮ'>=RYI%B?o)[4s$>w]SX:5SZӘpl:hksIO>@ϠVC+wxGXHtV;_suyAݟ\Iz)nǻ<8x9nSןI4<]ⷩ=F?J }QsQ}7H#w r8yg}B>}ߤ1m]şW- GoŇQkH}m$D%\QLc 4Ugh.5;j[Sr3?ůQkD\!kh;{w 7+S':?c)9 4ޛ+=fms\5&{]&u' woRLJS{[,h?Go#0e ǻp|{8y w#!#ID.>O?C]$ˠ,i[ƾK8@ycJٛH'S,wInHw` 3f%MW*-xΝ]kſTy$ dK^_0:dqG}_snkQ}g0lյa1UǹP ]ON%ϩYk`c%.wk7h_"X\]aخ۪>I;ZuGN}I ~?R_;r2(xw:;;Z_#WfsԜ~L t^ ?u# vu;9#w?'QX卆wp<ư%ERΖ8p _.ne@N,q!?[uy#n[>;vI"y' ľ}@8LnlGw.F |76Dnu[R*sjcC:m:?;L LwY]]HOԖs?\#shKt9^ ZgѳOkwӮʳ-k` |v?oF ]B=xh6.PCkxkFP=q`ifwюHrۈA|s_/:V+92 mgU,Z44MDOPGwz,;G9 y,?^n9ԻHDsw*lٍڈ;1==$X簒@2;׿[u"]{k'ߛKbF̏%.ʱ䍥᠐I`(sL!Ã.k鏷SKĤůQkwo!{'wyw^4&)ᮒ^[@:Bl}I3$K:LEFuSǐ|˲ wA՚[acul n%hʮ 7y.#=;} 22O wVY_K45HmQ؅}ac<>}B62#ww}>,ZvEiwl fGݤiC/Z+9OB2!t'a4s󴗢'a4sI|%a}M/d?&;Iz'2XvoK??;I^Oi)󴗢'a4sJ|%a}M/d?&;Iz'2XvoK??;I^Oi)󴗢'a4sJ|%a}M/d?&;Iz'2XvoK??;I^Oi)u^}EQLeua}M/d?&;ZW~yݰY^7Vs|FOi'a4a?pq\Fþ.suMz'2XvoK??;Iɳ*cDZ"F#X:Cl6ѷkvcI>+Oi'a4}7OU7czdl݇kM]?7潳??;IS}bԾg3;z~+7nѴ8i'[zw۴ֳdΌ Oi'a4_W>?YvOպIwYV=XKx+пs_a}M%uٴM*= I$R_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_* endstream endobj 48 0 obj <> endobj 49 0 obj <>/Font<>/ProcSet[/PDF/Text/ImageB]/XObject<>>> endobj 50 0 obj <>stream h޴Y[s~SjLx1. ^ aI볖pQߛJZqKN\֣|0;:=zf|0>PA kM!l47LGeLbr}dyu"\bȮMVW\2lD[Kh8Y;q, <#m^b'B-O %~Sƒ(L7[ؐPvBD"BsLBtT(D,: S2)qaYvHqjo_A8KDpR?@8(ϱ'xYw?ŝKݲwAe< Mem4X'E庍+} >XsChp]7\Ͽ+Lދn41AyzO%O,,< i .naûB!5-IaVXqȺwO3miϖӔ{}:[ʩT j& ?[zP#>a3ߦt%;ܨP=FJ;ܘ3cs;*4ܜ̔kՌry 1B!DSXEzǻOWPF5ՠZp7jwN#hiYBȼh a`=vWuSgN|^N//ߵfF6u3wrw9GJq-Vd%L!HN 8@I5&ă$;fM=5ٯ+3nS&ٗ ֋r6ʔ#LkrHUʈm#'9@m:d"xc:Uѻ['9L=ߊ8z#NC-,Wq%ajC+BuN} [ pR-Lj(zIeJ"zunebO[1#o\%:G2"܉y@T>דHe]yPt`5@]N6+Ւ*NWkXGv@Ȼ@Oj4@r}B57zdqpD-dVqQpf\zB9TCO?d}(}QQf<Ǯ׸EٖgtK9" oUF ?TKc`Y:S)!L^6 )0 Q>g=nWՂx)e@"kPZ5ĶVnZaKo >$pMu]5MuEmrwi ~J s (0( `XNSTFR[:aSZO;H=KzX $v Ȼ8.Kj/ }=uԀ`uIX@F=X{Ej9oyoB7Vt4L!6wѾm0bu­Sm\ƍ CoM}S]Bbl}b})۲t2 8ISaJĤX0e5֨}QKCT  ABq 8d}l(G\d6@]2-RSc o"%0KFn1Wv`%7n rjw栓fM.fn4fMIU67fQxqXlll RmRۤ\6^kW<j>~?0ۓeSYm#z^݇-Sw`AvqǀBb%u@47=.f|"mjtT&ǧhT ">l]0×ODx+PgĦp~. 7JMPIn݂g(Hqyq2?/7Ə?zmvf!V#支Rc >p ǧ\_q>pT+14e',"4V'h}pe͇}֋ n݁&l0[':wNoa?p1аLE)J5h?8rslrr>ȟ\.XI}Og)Km297C?> 0kfiϚzs<`Br ot:&Wp~h^=M޾y:+)xa>'o_ !Zf/]% 1s*H}dG&,i6" A*y)} ŝ Њ#>D(&lJb endstream endobj 51 0 obj <> endobj 52 0 obj <> endobj 53 0 obj <> endobj 54 0 obj <> endobj 55 0 obj <> endobj 56 0 obj <> endobj 57 0 obj <> endobj 58 0 obj <> endobj 59 0 obj <> endobj 60 0 obj <> endobj 61 0 obj <> endobj 62 0 obj <> endobj 63 0 obj <> endobj 64 0 obj <> endobj 65 0 obj <> endobj 66 0 obj <> endobj 67 0 obj <> endobj 68 0 obj <> endobj 69 0 obj <> endobj 70 0 obj <> endobj 71 0 obj <> endobj 72 0 obj <>/Font<>/ProcSet[/PDF/Text/ImageB]/XObject<>>> endobj 73 0 obj <>stream hޜZr}W"yWY{픔lU}I{f@EZm+!ߛn407J*VkF_Nn_\x.^s/=,D⧋ď,uyW<\ov /Q䯖 '3!On8d^~V[YVV%gYt_};VU4!ae}|wy߮['_]!|ScVu dX o7u~v.9bvI ( -3{zCқu8L, :  a[]D>\FUYŀ:( ,p'~nRև87QUm- z>ZpD /#{>_|b -@"`_DZ]1M؎~Gdw#/,f^ٷKWE3JV^F0©sR)ZVE>0Rw2q킟qx'Ѯ1hv ȎXH nj5C)K͙xjUJ# eNZ ~Ĝd7=Vt_8Rw(`8=aA6EֹΥ/AVE^_U=XwHnpX 8Y3{Y. ނ`3yQ`dܤ> ;I[<"Ѡ;:L@^'CX[ fJ|qmaE䥠( XlK3^^X{\nCO.dr2BrT&AF<78/- C|r0M4ukG[uU.~kՇ*à{t^-brI }pHf0f4Ļ-"3bW(zFwV"/S=30oLbf!f"ɀpu;aZ<|#6%rDy+d/Tl{E* _H1$Ml1Y vQ}mL%ܹ$OښM< ɶi"5gJU5VO5 KSb ^bѸ];([QW֙!(V^{!V#L!። 9H^mu #9 jI_Dkb(ۘO,m^F1A5q*6W\!iH;| "f{1 ޑe { bp1&Q XrUVZ,ʛ9Xpn7FWswʂ(ieGY.e˫h\eV jǼO'm`]γ| >Ph,%ǂU6D{{!ECI2Fjr8`HyF$XtTڽ!`uN"'C%dwro}CcBDz3真sTye[p:(gx -S,8k#/)5(1:g|JvqKSa"K]mۇ$˩7~! B,SV&hL':cJ9#{CϑQGPy x!Nh, Q`yCGRLa bd?ZV`1Sɉ)6dZ,V{NyQ"<2q,[s@B ΀klЋ? AT*2+ɱY)A8 lzt0f≒|DC [m (Op\6yKk 1W)]c{]DDE-P`ZGmAr[SZm_Fر&a5ed^̛3f12*i'2{x[>{bbAbjEl9}ʨҨo;Pe#ѵ3& ă - @ %5Eu6ZKkݯr`e^z~8ST}? !P9sSW> n`j7+FNI^3P`ϙ#{aeRYylR noJpe`v@̻kAgKYinwvYvpTm^"'4\=eF&=iHnBQ.CC(=-::Ae?> BP 9Xh0qww)$pZ 7CʾLdGv \z_KMYbY 3> s0'?'aoCz}Lrw҅ZUr t h!{WxQ> endobj 75 0 obj <> endobj 76 0 obj <> endobj 77 0 obj <> endobj 78 0 obj <>/Font<>/ProcSet[/PDF/Text/ImageB/ImageC]/XObject<>>> endobj 79 0 obj <>stream hޤXێ}WX}bz Y}$J-2Iy>5f&$0!Evw]N:ś|PnݼM#-3bd*U:ydm$ѲIJrMĦf0cmN?oy\ /*6Kҍ=ݤxҔj؝~q)v۰X}5}]x܋Fr+]X8NI+Qcح;gN\Mj:X{q*µ"x{W˖S|*ێ*ΊPovwl܉PȆtC^P-iVeG;RB?-gqDqOW I$E{s]gY:Kjz*6]st*:<-\o}t#D02\ʾ$DŹQgfyl+~`nƅ<ɶ㶚54|9(WLAqNe4'Ԇy36 av+u] 0NP(W&⥪ױEmy:h,NMvEv[ۮŶG?k;g!CJwEX$x!1Li hܥ %p\|/B=k Q`AqH{q_, yEZ´`F"8]EKی57tG 7yTTG/?3$):Nin܃h7/2\l0]k+Ɨ$KT9Q%^σ_65qOy#oĪfB),P}csfI-Eh}12!Y98f[HgkKR\~4[O=ChJW}WEIa$N(vei.}rАe/$B*%ٔ4Cp,/d/ &GZ ^0UW0;h.0a4FbFhuu/WXp5V>nbPy1m9Dba= FN&޵gQVi8Or<  Nʮ#f8x]aU/+W<wvPҷgx%#]r۱ңIZ|+\¸ۏ¤p$WU;}*0z!&,!iR<g:46V5Mzq/U3ȇa?`9BptG hF\lNQMu@b< B}׻rs݃E F:ܸ6H ;< U6ALܓ뤡ɢx-ar8觌  ulk3NL\hsESX965GȄz/hBԦ:b<>@y6ihF\/0Jy ͹U霨<ҞrQUqaƎ}w?pttpvU[5'kQFP`io`4-Z$"M2 Z i(٘dC @IV݁-S\CT=ü*9hz<> Ax&OsaS9ۿ,請q&}I/XWSw1#PSIx' IfV)}.RpL,\}\eh\Y+Kue3JNkӧFğh.~)r#~qcU2zy BvW>&gS G*R2`$I!B!E)}g F.ΙARqj5$s}F}@{[fɨ7L`9/}^f7jΟwmѿ燓 endstream endobj 80 0 obj <>stream Adobed     "")""""""),))))),222222;;;;;;;;;;;;;;; %%2%##%2;2.,,.2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3 "?   3!1AQa"q2B#$Rb34rC%Scs5&DTdE£t6UeuF'Vfv7GWgw5!1AQaq"2B#R3$brCScs4%&5DTdEU6teuFVfv'7GWgw ?T^#̒,_%6兟rJ}%?sXb.Kn}nE)d߭b/,_7$ْ^37~&\fIx/؋[rJ}%?swXb.Kn}nM)d߭b/,_$ْ^37>"\fIx/؋[rJ}%?swXb.Kn}nM)d߭b7/؋S/[r_wwXb.IOcnnE߭b%>Β,_%7~":K\/؋S/[r_wwXb.IOgnn?"\fIx/؋[rJ}%wwXb.O7~"2K?\/؋S/[r_{wXb.IOcnnE߭b%>Β,_%7~卟rJ}%?wwXb.KnnE)d߭b?/؋S/[r_wwXb.IOcnnE߭b%>Β,_%7~":K\/؋S/[r_wwXb.IOgnnE߭b%>̒,_&[rJ}%?wwXb.M7~":K\nnE)t߭b/,_$^17~"\gIx/؋[%>Β,l؋߭b%>̒,_%7~":K\/؛S/[7/؋S/[r_wwXb.IOcnnEb%>Β,_'[rJ}%?sX%7~"2K\/؋S/[r_wwXb.IO$gn}nEϭb%>̒,_7%7>&2K\/؋S/[r_sXbnIO$gn}nEϭb%>̒,_7%7>&2K\/؋S/[r_sXbnIO$gn}oEܒeIx؛߭b%>̒,7%6兟rJ}%_oX?bnKmoM)TͿ`/,,؛S/_\??؛S/?Vz&swXb.IO$gn}oEܒeIx؛[rJ}%?wwXb.L>n̒,_7,*]ԇIf~G m??bKe}wثRS/#_]s*ԿW%>??bKe}wثRS/#_]s*ԿW%>??bKe}wثRS/#_]s*ԿW%>??bKe}wثRS/#_]s*ԿW%>??bKe}wثRS/#_]s*ԿW%>??bKe}wثRS/#_]s*ԿW%>??bKe}wثRS/#_]s*ԿW%>??bKe}wثRS/#_]s*ԿW%>??bKe}wثRS/#_]s*ԿW%>??bKe}wثRS/#_]s*ԿW%>??bKe}wثRS/#_]s*ԿW%>??bKe}wثRS/#_]s*ԿW%>??bKe}wثRS/#_]s*ԿW%>??bKe}wثRS/#_]s*ԿW%>??bKe}wثRS/#_]s*ԿW%>??bKe}wثRS/#_]s*ԿW%>??bKe}wثRS/#_]s*ԿW%>??bKe}wثRS/#_]s*ԿW%>??bKe}wثRS/#_]s*ԿW%>??bKe}wثRS/#_]s*ԿW%>??bKe}wثRS/#_]s*ԿW%>??bKe}wثRS/#_]s*ԿW%>??bKe}wثRS/#_]s*ԿW%>??bKe}wثRS/#_]s*ԿW%>??bKe}wثRS/#_]s*ԿW%>??bKe}wثRS/#_]s*ԿW%>??bKe}wثRS/#_]s*ԿW%>??bKe}wثRS/#_]s*ԿW%>??bKe}wثRS/#_]s*ԿW%>??bKe}wثRS/#_]s*ԿW%>??bKe}wثRS/#_]s*ԿW%>??bKe}wثRS/#_]s*ԿW%>??bKe}wثRS/#_]s*ԿW%>??bKe}wثRS/#_]s*ԿW%>??bKe}wثRS/#_]s*ԿW%>??bKe}wثRS/>}]ze܋d||}Eɷ+]{ed%>:y$IJ^/-+ J}8L"I$I%):dRI1IK'2I)E$IK'H'ILS:JRd2J]$IK'L%)$IJI$RI$bI2JRI$NI)I$%)8L%):dRI$JtI%,I$I$I%)$IJI$RL%)$IJI$RH$I)I$JRI$I$$I)I$JRb2JY8 '%.rI)I;e%2H$I$$I)IBIR%.$)xH$JRI$I$I%)$IJI:dd%.LRR$$$IJN dҒO )dI$&N3ޑ*0JJbLv9H)% )\%R$TRjSP :o-Q^[ ʺo-QA/$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$V=KݵKqw$M$pxiD%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IO 96ԽlOߊ?^'%=I$c[PW 'I$PI%.$RnHRM $$I)IH))e%)E2E$d$I2JdI$$4LBJRE8ILI)IRI%)$IJI$RH&H)ttI%)$IJI$RN%)$IJI$RI$I%)$IJI$RI$IJI"JRI$I$$tI$' Iґ))E$ttII% )tNI$R))$ILIE8))tĤ%*T0ԔdRLӦN:dt$RI%,ShIJkTNIK8@.#pMJRR2R%.%.Jb$ P)`j<꒘e$S"%.u^Uӿh8U];ÇI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$S U.s3ݹ2n}?TRSUmAM$RI$I%9XzN;oļ6'+};(cW{ y__Y?\G[T#lbG#: $I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JxO! 3g^/V7R/??~))I$2x'.oJe"QB$I)I$ŠrS$JRIK$$xL%,2rNI:dbSJdv Jd$%,S$inJRRdIKDLJt$I%)$IJJR )e5!%- (IJI$RI$I%):d)II%)$IJI$RI$I%))I2J]$IJN:JY$RI$dR'IJNI)dI%,$RI:Jc 'I%('L%)1I$tR铦IJI$R0R));` M IJJM))P$JdBJY0 IRIJd'IJN$R$*`x n2{((S ILP%,$RLJRvħIKH{ӇID5'^[R0dJq(#{ORki2IKR %%/ygNţ+Bޛ6TPK $JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRVUx;Xؓc2'[~)?NKEz&n#n4$pVJI9w_/ȥ9w_/ȡN85:;⒜]k7ҡBg?Ed}`X8-!ۀԓ#?E%:i$%.wl.I$dRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)?/0y?XCogK_I$R e82D"DP!1 ҈I%)D$SΉRJQLI)I'IKJY:d)J.RQrJPI$RI$&RI%)2t)I$RI$JRt$$I)I$JRI$$RJPIJI<'e$$$0QIBI)I&NI$$I)II)I' $I%.IJN$I%)$&I$I%,RHNR)2JX$S))A:d)I$JY:I$'LI$ҔɒRH% J]8)LI)ISJ]$JJ])QJRS)IE<%6RRA8))IKI.RR2RhFR45Lj]yBAghr}mY^4Թi>?>O3wS &ݭ.YU@5\\qmnmvˮccCAio(Jk\g^tn^^8]O>:}ŧnܗ-|կR|7: 8 ve$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$rm z96ԽlOߊJz$I%)x.of^3-))XHL8B#I$)rRR$dHIKIJH$(R$JRt$$I)I$JRtR')Re I:d锚RД)ILRRH'ڝ$% JRNڞ)R&Rbe7)%($IK8(&I<$%(KjJbxKjJY(N$BbԔ:PJY"2JRI'IK$2JRI$dR'IJLI)t$I:I)@'I$2JRI$I$$I)I$JRI$I$$E%)$RI$I)dNJctSO %/)&%/ J Ʉ) jВ\P`•5S e @R2I$IK'2vMNa Gd !&JJ`7!JJfJRJJ^WtYD/.o?EI$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI._NfM9UE`sƻJz%3wιJzN ]}zWPYQ:/Qח0%ۄt7t[O]V6k +\z}{ %:$JCVc=;۹0Ο}Js?OD>z_s{iG}J7@>Oj];mӴnu/.%)$IO9 ?GK@jOŵO/c![tz>nwWg~6T8нwI&) J=eH5.:R,igiFA#O]H5w tnG$P)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$CogK_o! 3g^/RS$I)Kq?se{2O\oAIO)($P2IHQD!A%, hH)D&RRR$J]1I2JPH 'RI$R'I%)$IJI$RI:JRe Jb& IKM1 )J )x)J`I$&I$IKJS$$$N5IK&!N!-%%1ЦRRI SB )d'kS'L5H&M)JRtIK(H$$I$IK))dKjJY$RڒjPI$)d$H$JRI$I$$JRI$I$$I)I$JRI$I$$tI$t𒘤LRRS8N JRdRNJZ$H&NRPMIL,CiS' BJX 5KjpԔrs))d(NA%2 8S$@Jiw`pHCwt$ L zo-Q^FTPK $JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$.K_?}kG[TRS֤I){:Np+M%ƭ|סujJynoD?:QK1mUk@|_O *IJI$RI$IM0CSKWI%)$IN)nI2(f uBI)D'iR!𒙗(L)IKtLTRR'LRJ%%,RSڒNjJb%J]:IIJI-%,(N)JR$$ڙHܤ)'ڔ$I<$)53Xe ))ƩnRЙH$$d$Н$)&"RSA`BJc:IRI2JXI(IJLS$)'I%):d)dI%)"I)hM I$))R)')RNJY$2JRI$I$$I)I'LL$$IJd%2 ʈNJJXHJRI҄LR$RIJN$2t&SiIL3N!%!yLѪRH&nI)A)B))qE8l&rJDt)xLS.*( ĐРJGS9 &%1+o,Q^ѿpI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)K>r_ZV$JR'[EN?a%0'aq-};Pˈ$>%]wS$W'}/W'}%="K;^X$5>:0;nm.sR;=> "clYI/8:?wiN܊~y M?➵u˺lWV;ͻW5ȓ1ԣ8$zIVgRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$?^'c_ a/[?⒞$IJ^1-+׌b J} :IBS )INX$!BPRSr_ZV$JR֖<4JkE_Kv/*jJkE_Kv/*jJGU"hDIJ\[?JWhzo[~Invr 71,9pӮO}_P^ߊ}}ޢ+j?7y߳/='(bqHzITl)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$S_ a/[?Ɀ/0y?IODI$/1qe%>2Ht'IJ J)nIL)'IL4C&RS))rJbTH )(ԡ"BPIK\%1I"I2JRdpa4'(H&”) $$$ )P9L$)A"SnI%)$RI$I:dIJI$RI%)$IK$ Jb$4I)I$IKJPR2IK&!:Pt$)I&N2ttE:JY$RI$I%)"I)d$:d铤$I)I$$IK$It I$%"p)&IK' $I)II%)$IJM $I$RТTIK'M RILJIdd))`TKj )`%<)L(RR858 )p!:IJRLs/ :)LZٞ)I||b]\x )% n&NC,<Q}eq`#6ϢZVyFϳtjtU,{Kc)l A niqïpy e.jOBnHht*PRHIK(IJ]ygNţ+^Yӿh8 }$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$}kG[TWZ/JzԒPT%4x )H?l#? }8$ 8%?3$?3 i{$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$CogK_o! 3g^/RS$I)Kqse{2O\oAIORB'%1N%*!? JJ`S)8$ҔbRJT2RRSPIK*IJbS'%2J^SJP I%)$IKRJSJ]2I$$I)IDRБNJXIRJ]:dRJd BJY)NTIIJI4))I' )tI%):dR&N$RI%)$$I%)$IK$2J]$$'HR$JRI$I$'LJ]$$$JRbD'LJRI$I$)dP$(NxIKBd% )d:iIJ})% LRNRHjSS4I2yIKSJJe*.rIa:Ϊ%Q]ƚ`@%5s,s;7ۻ!I]-kw4 %IFkIKu4v]ղF&?m9'tSl]w$ Zww|]ZgX1LqR^OO0݂LRïTkm]evFTƓ#X*1 v=p|lL6Z]C/@RE))dbS$BFTW/*o?EI$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JR侵O+\ֿUE%=j }`}sRܺy{A~(%[= ? Jy2[Ӥcd17s`7v6u>jI)I$JRI$I$$I)v.C@1G방\0O%dzR??ڧ { Y*WNEapf}i鵶?u|WM:ݹwBGrL E al{hU?ص8RҕcTg;~HO׼g;Sr}@]︡ѰY~0f ^5 qy Vi֤r-=?2M/o-+QN$d&,)$L\I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%<'R/??~+rm zI$JR?\oA^̼g7[PRS$(RI$IBJRILRNJRE$%,S%2JQN64$RSR4'IK'IJN RɓIJLSIKxIK$)%- $)dtI(IJI$R`%*S<$M)% )dP$S8 R9 JY$BJY<'RɓIJI$RI2JRt$I"I$$I)IIKI<$N%)$IJI:PI<$I:I)dS2lILRSږԔ8 [Ta%1'NIJLt'IKZ<1M"RSTHR)SNRReHJIJLI)I$D )t:x8KjxILJ@'IK9‘ly9WxwbJss }E22H(cC|K4)Ihtt,\ yu |Ƹ=~c2je!f}%Y2ǔ\ `="8۳X>Kd]u%J]~ﹾ#e8F&De)qOJ#3Silbe9x[¿I!@ʳDkܩ&k`'(dҜLRRT!Lh=7ÇT+o,QA/$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$/W%}oQIOZ>CghrX\4Թ%:= ? ?@$$I)e﬽=ۨ_Qq=S3-K@$Aw$;/S~(S)N8__S))'"13kdzs4SjtwY踸6Pq$JoI)>Aghr}mY^4Թi>?>N|;%OZ'c& 쭶49A"I~#3ӫ~]e B~FFQB?e`ҧx<8}hX{~{O^ә{4}# 8X{㹬Y.3y-o^#<8/'̵ΧKvvX*o"ώV4@TI(ש$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IO 96ԽlOߊ?^'%=I$g7[PW/eއ\"btIO$FC/nQBI$I%,TaM"&Nħ)RHNR€ A%2LBJ`D )xI$)yJSBI)RdRJRr\ eHSIJN'IJ%2I$'L%)$jpڜTe%( ̖2z ^23λvPB\%-(RL!(N%1:Jc BrRRКSQ!%,I$'L$1IK$I)I'I%,t)tI%)2I$'I:JRtҔ% ILaH5<' )P<1)RNRRRRLRSJJe)JI)rTfR% $pI0O))IL\))8IJN'IK4IKG;}ßx}S9%sT%=}fDījUkGЯ%2VGG^/wk8 3 ꓝ$1H'IHLЈB`/qLUt}az#xߺ4%$/^&.?0{$QBҝ " LIK0%@" JYyWNţ+ };ÇI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJ\ֿUEu>X\4Թo?\yQiWS0nqgR'AszR#lIO]?3๏swPF-LjFXG $IJI1phUꘔ;@'d~ns5XBCr*.y5u33w3гl;kq8wଜRFStU7mn8uX7gݡֺCz"H#88ccCg7h>ji$Opx4{.ZA?ZI%)$$RI%1IJp'ФPBhIJ:I$'I%,xI%,:d铄hIK)&IKR:JT)JPRI%*JTd)2I)RPtSN)I$JR@H$a9LĨ&!%()(0RII2JRI$RST}GUK;?{H&`hATUQö?ؘR`㐋 %LXI%,A͒⒗jbMAr ِ?xԧwXg>i!֐uR)BI%37:JEE%(1R%2 IJYy_Nţ+Z7_xI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)r_ZV֮K_?`}sR徰>CghrJtz'GA^INg?>߀To?Gܒ}T]TZ_(; >4).Kg\Jq^]RvTۧ%8\zNKov`%٧UDꍇZh~GY]aWk XKc\YL|&#BɌF\[uSM-2IԔ8Yny̫9t׽(]h{K3h NDkobTZl}!>+rqYg}Iɶn ggX])߹, ۰RI$TI$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)?/0{?XCogK_I$Rt^].+W_/Ԥp&yHnmic^K? R]D,@{K=)) \.2(bBDPp<&NJF2S ' RS1 "NΩJZ$I$LL'I%1:tR)$$R )t$Ғ)L Ғ' );@gGq%, 01 JW HIK5eI$asJ ˙fpw5&TSg%g,zc |pIu%3Ŭ^{GI)I*JS„⦘RS%(O)$W+SW{UȰ5x01 !bE2(]2IQ%:dB a%2 uΝFTWFTPK $JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$.K_?}kG[TRS֬?\?l.INDOO4P+)'Aw#cGSI$$I)K:7` RjR̐]QxO9s՞\nx+I;jwf>7߫}L64aWrIr{]p RI$^I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IO 96ԽlOߊ?^'%=I$swe{ޖcTeP%O`U/;ctI䤦KZo{6Qq=ȩŶY+۹hU+zS1)/e0Cq#Ԅ6T\S ip9IKLJ S(NNR"JJY$)J]<'$InIJ&Q*I) JY8jxR )j%Xls}AkA,R)BT*U-$J)9e)O({RL$lZݮDL?F zs٨+څ(LGKLԟIpm=eFowc䔐BZkhrΏ IwR+?}}(IN~ҧ_>~G(I`dqJz8JUL ZSf0x ewo>^O0|))<$ 'R4$nO`) Bd J!I%*T QILH& RBdBxI:JZ)҄Ĥ%,'JVTg$ A4tФJZBd&RL Ra%,Х )Jp) Oy7 ksaIN&e";~MV #%ǰV%J2()%2JѤKΞ:5nG%xa=?8tkb9u"7M)*]CH;X! ߘBxCghrX\4Թ%:= ? ?@$3)oAs#zmRgts]o:O=Va5yԀ{{7 ;f?t?c? }l4$Bb_ٿO]FB_c? )ПؗowC:?ѳЗFBJp*׼7:4t]*qia4I$$YN[OI`yO0 4JcټI&.RI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$rm z96ԽlOߊJz$I%)xLKǺ^_.әIsMGR%$em haIL;\=]{(n&sBn7U28U2mm.w94V)sܮXh\`(%Le%/ &NRA:JXTJJZS!)2JP$RQ %.T%H$cĤLR'L:k iHSmgM 5Zs.;o{lk@< Zx F(\' pP{'Qʚfi%1~uxןwSx"{U&V댒ck{Gs iJ0IR;J )PS܆*@$*LRPВ 1I%(PIKA%040KЯ)d򒘍 IIKx#>NԔqK@2 >2^_a>ghHD@ƣ$IRK]R zX-$ˆ!fQXa-x7_B<Shuub?T_0'ޒ:6Oʭsv ~z@'ՙ ?GK@j_C$JU$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)?/0{?XCogK_I$Rt^].+W^]wAIOmqN!Nbe/kc{%0!Hl=yUrs[ ~$3I<,Nm`ԐI?nz Y G=S(QܟR&tS@ QJ\$RLJJXҒPH)*J))}Т5*PJRI(IK&NS '%2 'LR> .n[$ʇO!~BĴ쮻 @1 JvN=BDB(RI$$4Ԭ+9n\9kIBC6{PJ@4 $TR $0IKmIE%,Tv%(5:I$S)ڐ Ȅ%1H$JpRRJiQ%%-eC2Q{ILD`LSJfĦܒ7DPޢJfJA TII%+@;E_" y)1J1XYHI3[e,t?Vn k\Fymuv9$yMF"!.XDfռeڑ;Hwr>+3*@9>tܻl=\1ΏWYfGWyo O71#U#F|RVޫDjwX94l ZUZۈ{#VLE &rۀIjzI?;R[i%X]vMlsKO*n^uП&8' )sy$6c#G:蛄ֳgezoL+j7?"tyޭSO46q6GE2F#՟M5|)}mH^$}1YL6h'cU}Kyoy$IbVuzݥ%7IW& x$~cPI$Q&{yUVjRR'D1pI+B2N)`5S )ӆA=[ z{@wʸt@SG1 6)b&'ަ짘4+TTڛ6D!Jlzm&u,uA j~K@qs(sH5\>_Z⴪Ǫ+hDܢLCĦL$%-1NJJQ*)ӀJ(򒔒R%)4JRΛFTW/+o?EI$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JR侵O+\ֿUE%=j }`}sRO4P+ʏDOu G0@fPUB?N>CghrJw\n7RŠcj7ʳ3?ޒ%̫?7̫?7)ӮZV: *RRI$^4Թi>ϯ_Y?\G[Tf?bΟ'A$P2$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IO 96ԽlOߊ?^'%=I$cg|3q?se%=fmMm>W$:t>c?u\dst#_KlIBHl۲j~YNޥ}FLhG$]VVL~n3ʐ!W>͇]NeY )%t+]"B-RR0O))P)JXR:JJSfC:@RRJI$Òܙ(IJ)9L)A"RS 4mҒNShR1jJFTTRRRHKrPRL!6Ԕ)(INeyl6L?{'Q&(?\p*//"I35 +RF\b4 }jz" .VoH|=R4 phHpv>܄g,c1?VwZO5PAy`8Ns€v䒾Q{OHIL[)4dA.`bJT@ )TE΅S2u"RNIIJ"P;GD )q\{ 5aVB[DHGܯ?IyOKQc$lXxLRKS4wQ.ϐBDɀLB$/ 8@&ФƩɅSXL$6Y:8)“BJc$|vC[ӱ}<$%n;G=(BI$$&NJiIJLL$J))@S$$RI$pI)GE}7ÇS%ygMś(%I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$R%}oQ]j侵O))V?l.[6Ə$GA^Tz'fPUB?g7oꂥK"{ӼHJz?GK(Hߑ] s\}A kS?@*= ? JRI$I$$I)>Aghr}mY^4Թi>t: $I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JxO! 3g^/V7R/??~))I$2x/./"$tIIJ%$JX'LIKJ]:IR$JY$IJI$RBp%2hS!R%8 mOJQM)Jd\ԬΦG\$~6T γCɧbVm: CWtPt dikޜT$pFPl:*@˔ A,K`jdÃQ#U0"TKdM5Pu%1jg>SIJRs6J`'nXzzխ?{ض A "(]$$ҔɒRRL*$%2JRP'%- mj@)Ld I$/,o?Ez^Yf8 }$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$}kG[TWZ/JzՁ?GK )iWy%9YV;~AY-]:ՅWo'Ԕ-Lj_WGK\,/G %۬?\#}QiWR_ck@'E%ʒZeAeMsT{i{u6$$I)>Aghr}mY^4Թi>?>NI$eRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$rm z96ԽlOߊJz$I%)x/./g^1-))4I)$DRp))E2I%,t%0)PJ^Te"IKI$$I)I$%(2JW*M )EDJRҐJ )]A#YǂڲcKéMgX$ l7U.L'I(A;\L@* iIK“R CD T"JJK P>䔓rmɀIIgD7)vQ B`!%.LJILyNxR IKH$ɚ9 )I $!I)&Q2@r^ܪ<4RGK$ctAj*1* 4dއwLJ]LLMEEXD)7%H J.HCs%.\uQ&IdS$LI2RQrLBJP r'%)$AeCsBS5S[MU\H5rW\/L_J'BSS~;7+ [>b~I xuW_01na#D:msLP eѸmQS*ZwI+1T0Фm )Ow`TMM%#*RW_Uګ?p̬򭕍NTxx'a?ʎ44* }餳?OJ>r_s%:i,ҏܗGINK3qw%8Q;S}?(>} əNFmOh/]Q%i`$j>4B ˞]M?!]?l.INDOO4P+)'[M}gXU~BwajJw?l.[Wm \ Ob?JAهK]k  G-5a̪һ ̪һ )~fQn ]syAUw.jom ߫u_rYV?꿵V娒I$Oŵf}zR??ڧ1t: $I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JxO! 3g^/V7R/??~))I$2x/./NPN''IJNI)IBIJJX*%%,&:JRtR'I%) @N*)JY3[N <$#TR8 o^d]iI(%̫7OrV=o./BIJ٬>V[P!%) .* I(SmRcR9DkBQuR]gN eh'W*R!xI 7O>) <5敱V5ZѸw RºS8_OkO:-+ ;]OՒSOBv݇v/oM]۴:IR! U jw90 ))@ʉP7aI Ce;!;իW?!Hd@)rᤦ'RwVk϶I츼t[Xal߶}PH%thu )}wxy9 wefN2a@6ƈi:)vYDDs}WsFVa;[ğb|AVyʭYج5sZihë:WDc@k+nc^<%2fGT+{M>׍vN<]`57wyYUVv7]eos?H+ǯŀ :!JPC//O vU;M4rw w?%/ Mޟ#~Ǽ?ܒ\)v>RhϼrJ^R߱l%yȤHOJoط#?t٦',鷻PwS} ~kz 5@o؛ѼvIM ;UMW7W7)]NN[U5Xp s\ݯ'?ܴdnFl4]y9Y2D7Pa 2ڴz*4>4A.Nmfyu~b,)"};zy@LH]kKL!f]̻RO̭8I^5~_ KNSw*f A)$m Oܦ+%0O‹ܒWVYsLWWs=.:ޛU/ !#}KAkdcs=,S'k[:OHww?/x܄}nZ&^5MJ5 X. ):X䀣2O 3MSW$>h{*%4$L^\SjTn))NY'ZNOȝ%0ǯմ4p!]r |Z Z$,E$PLJIKdxIK% IJHR )A$$IK$I)I'I%,I$/,o?EzFTPK $JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)0*Vn:oh?oRS4p|r_G-S4p|r_G-S4p|r_G-S4p|r_G-S4p|r_G-S4p|r_G-S`CmIZN#;Woo|BGoOe ys^5a%4~3K9sܮ6Ə+/Mr *?l.INDOO4P+)'[M}gXU~BwaINI$I$,{ʶnΡICIΟ}YΠaՎlh~+qb}TƵ5"$|IJI$S}zR?ڳ>Aghr}mSy:|I@ʤI%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%<'R/??~+rm zI$JR_\_oA^μc[PRS艒ĢIJtQ%/)L%2E RS46ĤJ%)HʈH$I'IJI2ta6<$5IJ I%)I2IKB`':Nw )}̸(ʍ, yF@aHߢ5*o;tILI*.~))k,۠Ԣa(:7>~u>'mShn@SBiNԔhHI$~k;'kL7?شIN6" yϴ>{O m?>pyY~{"JrCl gsօ~|0"'JG_L7 ǹ<}+uH"{Q*Kg\LxIDX~`iL\R1@B*%2ܞeA$]ZHNY=2hqki#*nEm?~ Yghw(oDz{U\kK-}z@>d}? `\xSÐu.C{hk^ jX.M2yL#L`#JꇸF p$(pM%%% JRRRBaGz$S $JRJQ)Jd%.35x~YF&\?YX݋l|۠C7%F^aُBGy|s,]\c=]1 Wf" E)2(zG#SL"h;*Vm~ڤBfqPBJ^S}N2䔧NC^Q`RR Jsa@l%8yKA%.\R"BD$Z{ᰟ@$6A W7;i@q7).;rt,2`?HZql0GRSC6H%ZP;gV Gr(d_ RR1)2J]2t)I% @$xO RtI)bE2J]$IK'LJ]$$'LIJ^Yh80tZ7_xI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IO'oR'oRRI$I%)$IJI$RI$I%)r_ZV֮K_?`}sR徰>CghrJmt0q 3? Kʘ_.h'Q|RWyy8/НZxX͈#WNJwRI$I%)$IJI$RI$^4Թi>ϯ_Y?\G[Tf?bΟ'A$P2$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IO 96ԽlOߊ?^'%=I$c[PW `xE LL'20 %)$RjM Jb ‰I$I"IJN& )tO )@'I$E,6;uԟ5sdw6hӔjnL$S[;SQg))g=jC4?v։+o bo.:ښR2%))IQNS)rJ^S&&S$[ܢJe)L JHi(N J]$KRD2JTNR'%)$<$N%,t%,N%,'LLSbRINJIK% Ӥ0)&$I2I)I$JY(NJRI$@JR'?%CUr7[_Äv0mhx[~{Ltwֲt厎[ҀO?!US'_# D|%3kCx)CH%2/%Dқ-RS$D&IIJI$RH& A%)$R$$tI%)2t)I$JRtR:o-Q^ѿpI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$Jy>-_kX>-_kXI$$I)I$JRI$I$$I)K>r_ZV6Ə-?GKS/]o\[ ]vQ%4~g*SN?AVSI$$I)NMxn;XؓQ}?(ɾg'}_mIधΣ5<<St:sKhn2I2JI$Oŵf}zR??ڧ1t: $I*]g1Xvk}GA}[mNA4;BwtqpuI$I%[dƅ$|cO,҉[),9YBns`ѧhZ<${$MJI$T:9ֆ \yvy{J YB$ RI$I$$I)I$JRI$I$$I)I$JRI$rm z96ԽlOߊJz$I%)x/./g^1-))$NM IJI:dI%)$IKI$'I$I$)I)xR `$))%((N9kIk67rJph5I,`.;ĺ v5';hq}w~n !1<{h8Pt$'L&N:PI<&IJI$R'I%,t%,$R2xI%,tR:I$$tI%,RNJY$R$$)$JRI% )I'IK$I)I2I)rTe"J]$IJI$R'L&NLE%-)&NRLJQ*$Ta%/)JhJR򔦄$ԉ ))!y*2)$I$JA$$4)BP)BPAI%1!D&!%0!pIKJ<% )dBPNA%.ˆRIJL$$I%)$$$2JRI$I$R:o-Q^ѿpI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$Jy>-_kX>-_kXI$$I)I$JRI$I$$I)K>r_ZV6Ə-?GKS/]o\[ ]vQ%4~g*SN?AVSI$$I)'[}Tg~TOujO=wINI$I$Oŵf}zR??ڧ1t: $>dzxmsclOvcUfsbOX;b)$Pa}rzyg{߿\ֲ0:O?K\džcSP>::33ccX4KeSorCl~*^~!ט=Ġs=]#)#$DNl< #f'77 ʳiGyKGR/ wL*_V1kcjk{-XqIO |a]AwK~e=k :&$~e;+c\ d$+.՞>.M,-`Sv1ya]D}Wi _B {;*ۃ2uqlH*쎼v=ª.h$4:W>tuucvOU|2tmXY\KZ0eg䟨ޭYn[s=ny Vn_U:C?͘L w^Cc~:E~Hݬ_WYcyk'?b :*Z rs/P\@HI*.f%2{i!Z5pJK'sDhfumm꿀N qĞ◊O 'u#'!2AGk_ɻ7;*=ƖcIa[G'݌$|cE7mqw(&t2UHWߜCghrX\4Թ%9]oOuWFE? a1_SG7oꂭ??+?YToЏa%;$JRI$uj=?p'N?m\OGkJzk8jN{j}R1's"|V$I$^4Թi>ϯ_Y?\G[Tf?bΟ'A$P2PFk*aI m>&45 ~uszmFe CF᣺I:;v_I0ANXtk-hOI(먤M\ЇVc\lre7h{5t'G,+M6ӌo{[--uoNF"2/-s|{JY%+BUtKc39XLdD.$#9Be*unylX+tiin|cF5;sAh{K'^%(KOHH~P4y~в.nv%΂~Zt[˹k8c~k#^;.c`W##_C֖G_9k$p2eWѺCF_]2I(Џw}Y:溒ݢI&59V:E;--{v :y-t9dlȥDh:kb76㼐"Geк k1G~ұ4j|HFU_zS= zGC{5Ν_N}<ܫI!,BL`"oI$I$RI$I%<'R/?~+rm zI$JR_\_oA^μc[PRSI$(]$$$I)I$%)2I$I)IKd& RRN5QTS1b))Dtv|9JV:jXcӓ]陎{~T`_le%4px$E[:Up@'Ek⳺K8?K9JQ {@CD쒙C@-2;T(]"1IJJS)yJRLE$bR)%,(NRN%)$$$xIJ:I)I$JRI$I$$tdI$)I$JRI$$ r4$$% )dI%.:I)d$:dI$I$RI$I%)"I)I<$:IR'LI$$I)I$JRI$I$$I)I$%,I$$I)bSJr$JyI8 )tI%)$$dI%):dI$RI4'I%)$$וYQy_Mţ(%I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$}Z.r}Z.%)$IJI$RI$I%)$IJI$R%}?oQ]jsmhkcY%7:U2Mѣ ~c^tuӃ LGf$Ȳ687x/Fpv=dpXȼzDkHw;JCN?AVu G0@ԒI%)$IN_:յsY?=+'[o'G5%=JGS?Bt3+I)I$Jyϯ_Y?\G[Vgׯ,.Z}O-y31OI(TI$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$?^'c_ a/[?⒞$IJ^1-+׌b J} 2I"$I)I$JRI$JI%,xI%)$tQJw˅ M)$Xh4XkwVg<#gԁ%*VV#Mj )#[xP.RQhI,_f%G̷ >kc-oxu)!%4@ TBI%.M)$I%)$IJI$RNT鄧IJ)NJRI'%,^ɉ#8IJI$%.I$&NJR`$BM.v3՛HtdW%Gu4I%.I$$I)I$JRtIJI$R$$HSLBb(ILS'!$NI$R'L2tIK$I)II%)2tRI$I$I$$I)I$JRI$I$R'I%,tRД'L rJ]$\%*;I$RI:JY$IK$$I$L$$וZ7Qy_Mś(%I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$}Z.r}Z.%)$IJI$RI$I%)$IJI$RI$]>Iψ=\WTY0oY0+?l.IO/Ʀ썶uȹ'T@?+"JhfPUB?g7oꂭ??$u$IJI$SN?m\@'dBI$dRI$IL$%- CDQq$ѪJYI$RI$L% kaUI4O৙IN@oiBwv (A,lE*V: %4w!zh5L%m9$1&R $PcI$$tI$IR'I%)$$I$I8 )P1$p%)$IJI$RI2J] $pJi=C'sHZUM%5,gH.mhH%,}޻ '_!&01 Ht8 ҔRI%*Ek)a䒙'ӯ૶ܰgsKvF)JϥX?]X|IM̦!)$RI$I%)(I:JZ)$$&RM )dIJN:JRI$NJRI'IK$I)II%)$IJNI)dI%)$IJI$R&NIR$JY$S}Ӥ $ZDIK%)$))t$I$I$tҒJRI$^Wf85;ÇI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$S_j[T_j[TĔI%)$IJI$RI$I%)$IJI$R?l.[6Ə$+_?.ȹ'T@?+"JhfPUB?g7oX旱%#$Is?7 )\F/Jng[W3Ws"ӟv1΍LFQjߑ%=JGS?Bt3+I)I$Jyϯ_Y?\G[Vgׯ,.Z}O-y31OI(TI$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$?^'c_ a/[?⒞$IJ^1-+׌b J} $BA$RNI$R$IJ:IJ_&I$IKI$I$Tbw;#+>k/~ %zCghrJrS~O b7$O>߀ |{SW4X~ }Q?~AWearJjAWearJptjp-}u1`~X;.?oګWpwTRfn0KZ=ZS]#mm BJRI$sƏ->'ՙ ?GK@j_C&IyYdud㶓 #Mň$ )92 /$Χ܆w\9 ⌂ ׃b’I$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$S_ a/[?Ɀ/0{?IODI$/1qe%>)$$2yIJI2tI&T;Jp%$$I"I2tIIK$$I%)duo%տȨe8O)%Ĥ2IHx_R>⹜}d۴rV@i i0}cJF=D L)R$JRI$$$)JP0TR$RR& IKa=R$JRI&IK$mI$I I$%)(Ykj@i)]Ĺ NI[:$L¯=ys-W+k߄pm_k-+RU'iJsӚi& EKKzˀ8 *u-n6,f3տi?)]!I Ҙ:+!PI"/b>xۍDm:  ]$EJI2t$RI$ĦNS$I2JRtRIKН:JbNHIK$I)dNI:dI'IK$I)t$$I)I$JRtRI:JRE<$RSI%)'<bΟ$Y:'{+x~U+p? )mhր'| zOH)W2^`w+>ݏV?KPKYv??GC # ƒT=;oI$ve$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$rm z96ԽlOߊJz$I%)x7./g^1-))4"$I)I$JRI$I$)d!JQ9Q9IJN'IJI2tI$R$,~ Uմo(%t&$$1OwVV;qfi-^WoJYC8c@TIOγV?fg䞌g@WppK-9F|;QdK$$$,(I1rI$I%.H'Le0jJ[lS )I$JRI$I$&N2L$IBtM9L$ӦNR%)(⶗;@R,6N1p, Ksn96} R+n+MX!sǧp))}5 j5*{uR[MW{h{tpBJO>%XX Mho %))tĨ(Jf\E2J^S$P =Je * $SfJJe)%*uI:I)INJbhIK'O R)BPI(I%)2t) I$"$I$IR'LI$$$t )PI%)"1IL bR&N)I%.D'IJNHLI$'L%,t)I'IJM IJ^Wh8E}7ÇI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$S_j[T_j[TĔI%)$IJI$RI$I%)$IJI$R?l.[6Ə$+_?.ȹ'T@?+"Ji}cGP@])I$RI$g[Vwv&hg[Pg~T$I)I$Jyϯ_Y?\G[Vgׯ,.Z}O-y31O_O M\Hpqk%/A^4^|^3]z/B̋\$G I#H(kࡆ I$ I$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%<'R/??~+rm zI$JR|wmm%{Bzx6DLHI'IJLI)I'IJNJdRA$R$$IJI$R$J\)&JRe2GZU;j>Z60(8зm>lc+E'@),fP\Ӊ?Ÿo!ay+M R}'سه]U͵ &&O#JX!?2dK!.4 &3JL\OtR)@I$LLI)tI%,I$$I)I$JRdIJI$RI%*SI%.(S"*첸f!\Qk&x))nNK[@Xxfgsmܖw98۔IILe$JRIILRN$BP $' 8jJT%tJP)SI0)RM)%2ITT3ָ6XTGP+U`ukG$wQ~t; j:.;~*إcCGUjv4P7Oȭ+:v>A+Nޕm꒜u ~xxJFBϿݎM!1 ?Q6<}LBhILHIJpR%- BE%,$$"!)PsR'ARNJRR$RR.)JJbs[FIILI$$)I$J]$IKIJI$R$JRI$NI)I$JYygMţ+Z7_xI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IO'oR'oRRI$I%)$IJI$RI$I%)$IJX\4Թo?\'T@?+"zS)O?#G'Aw$i$IJI$SN?mP'YV?{?ʒI%)$IO9 ?GK@jOŵO/GI$ I$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$S_ a/[?Ɀ/0y?IODI$/1qe%>$BI$IJH$$(ILiNI%1)LJd$:JRA<$ , /!88*V8˪DY59Իxb< Jl!88ϊ\u }+Hӑ +k-[\|> hfHhUx"7_7bǗ|['#j<;Q絢p[Zۘ,aHUY HhJ$I$&NJRI'%- ByI%*IJH%1))$LJ]$$$I)I'I%(JdL$ZI:d$)dI:I)I$JRp:J]2I%1J! )@B Z^$A쒜}n"k{w6ú~tͮCIH'%ׯb!^=T?h3Wm,zJt#"9A@w "dA mSɷsJdˁvruW t# L9OFSS?F\m;ҹXc 2LyHl4(Jg0IJ%8*$'%($IJ %))E4$J^:J]4%)00[C.D+(,2{:-fT2HV% *fJI>gS #Tn#_ӱr>bLãtIM''[ ٯbUm*0Ǩ}INwPxygSv^w[qQvm1ԵUu@Vz#_NZLn,;-[O[yh|Uu?FJn3W>%x, 69?rJkY)gۿ/RtT`c}4OQA:@IN'H˥X8$~E&.n䨗j!W(J@k|滲 %Urwj9$T˶3l6pWpM+SK~kAP.! 㝸;O *T$$d2JRI$2t&IJI$RI%)D&))hN%,$)d'IK'H$I$$I)I$JRI&IJN:JRI$I$$I)Ko-Q^ѿpI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)K !?![ !?!IN_Ox1IX?%?S_S̏_#dS̏_#dS̏X.q+wWFm?W.:?ꥋߕ60au#K}\JtI$I%)$IJI$RI$I%)$IJI$S}v4]Co\oOt8WFD'Aw#cGSI$$I)I$JRI$I$Oŵf}zR??ڧ1t: $I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JxO! 3g^/V7R/??~))I$2x7./ГNIRNNI)II%.' )be$IJ$Jd'%2d+-͗xǿ"kGnm6\*oi(9 jI@huQ4,Pf0ՠ˥ц^8J<>JԬ ~cN0#4,0I'MVm:6` ~Vr!4Ty%O_4ZixpAo?*묏KC n'dnF߀UJ`F!:3~SH1[PcH:ޮdwn^v#< \"S{1 ?E{,n Q`Ft+k/[M;|KqD1S>?^~)ֻ!I:@\vMcAnݠMg4%; `KhA`.2js#nj:[R5oIOHI$Oŵf}zR??ڧ1t: $I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JxO! 3g^/V7R/??~))I$2x/./I$P$2E%.A$tRN$TN5IUVt8ww|S[[MMm &3='"wJ2167 Gg33|}_ZJZcG珹ZW2uH8`"kc@IRʍM0RD;}엧S7T;BU25))5籅˾Ӊ[β? :]'BUK{II ~-;Kt?܀V*Py ȩXpsCлᦫ:,Ι^G ⒑)FGL?\0="O RCEJY2r%):A$$tJ\)$I)AIE3 / cq3sk~CǬ,lpEK)%#h l)1 'kQmBQ*M)WN0K;+6މmC@;hụ鰼x4й&JyڥYhP7*xpȦ*ptIM'fYGVYuAV.=8mM'ODmw;h#U:ILT!d<ԔEuQkIILpV75G))3Zچ" JJnnP^dTQR]Ou( @6_VMvsͶ;+wzcKkTë iNΙXDp'IS8hIRR )IM EbX~[8yu}g^UNدxCP v,e1ڸr~k_/%Y]6lw.))dbK)vB}Ғ%D)R:dR&NR:A? )`%$)IRSJRRL%.2RIRNIRSJQLI)I%,I$I$I%)2I$/,o?EzΛ6TPK $JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$,/Ї`n/Ї`%7z V)Z )I$JRI$I$ַuַu)I$JRI$I$$I)I$JRI$I$$I)>}~. b7.{ѧ?+"Ji}cG|P@])I$RI$I%5[R5o[G-nXR$I$S}zR?ڳ>Aghr}mSy:|I@ʤI%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%<'R/??~+rm zI$JR_\_oA^μc[PRSiE I$RJRI$I$JYH(RkSh0v'@  %ݜGɦJjx% p (ӠHM5IH*QtΩuIIsQ ݨIK9;y&"RS %5BRSO k|Läc譮0Tmxy]4I Z}QI*.҈I%$ Ej˴IHXsKNshwtAڐVoWMrn%/)*>!Ia.JrTJJY$IBJbJH:JRIBJO:F=:4Z?>W\|J`6 =`$5ߤ[ML ׅ7d΀)$U۽'`:EK̦*RpIK;9BeKK|rJSm  ط!o%Ss\u!9vJm6j;EŭIH/٠RR/%,"JzȝRLo=D%GaWjt?w}[kCDON[. RД'I%,(NF)ܚJke7 H^(m~}C`x#+zN%4=i|k FKILR"BJY:IRI%- BtRО%)$I)[ʌ% )yO JJdJbS$)xIK$RR$ܩI<&IJN$I%)"P@'R))d铤$I)I$JY$RtZ7Qy_Mţ(%I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RC0~BVC0~B?+AgWI%)$IJI$S_j[T_j[TĔI%)$IJI$RI$I%)$IJI$RI$]?1_=w]Q%4P@]X~ }Q?$I)I$JRI$L% %qΤּt4>;RVԟwſ%=)xͣtxEI$^4Թi>ϯ_Y?\G[Tf?bΟ'A$P2$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IO 96ԽlOߊ?^'%=I$c[PWI$IRH$:JRI$H$)rP7y+is;24p?tz KwRqKGt4;(hh%)"%$%f1N!0)rcrtLZo*-D$; V. IkU Y;:vx?iԖbb70D0=^[|4n\^]Wb N+c 4IJLK p%!D'M\*}:l5q䫐ФN(f%'2;쬖7 U1=O*厏A-X}"Ġ>V^Sk}$$>>K2\ݏ(<5XyKcw*w#%6ۗ]v6Rci_T-ioCQp%1)T)hLTg$LxL tLBJ]2IR'L'LS))iI ILSxJRĨ̩Ĥ )`t)I$JRt$ʉ)$LNN$I$I$% JRѿpk:o-QA/$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$BB)Z ?@h$$I)I$JRI$OWZ.OWZ$$I)I$JRI$I$$I)I$JRI$I$i.ȹFp)ֱòw+s 0_$~_awzIO'awz^_$~_awzIOW]<>ӻϣ?R~ߖTw[JnI$S}zR??ڳ>Aghr}mS!tI%*I$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$CogK_o! 3g^/RS$I)K1qe{:o\_oAIO$tPtħIKJRIJI2t%.tJJR~Й"%$7d@Χ*WyCަA'7OTI$$tH$D(%,\I<%%*J*$کkKI3V7լS:pUh?%>D6O>}u4 #}"ceOܽ Wps 5O'D>+H>So&jΓG ~Ȫ̈́TA}4ZT<]pjk4j[)dwU2xRBeg'EHt lTWLc %A )LgĒy7tlW$wcd!!,$BtSII2JRIBPIBJT lh72J)V;o9O}PJO&1[S2\(W7|ʄ'P۬4Nͬ$miAM;&eշ xDn3ʟW B" $m";pRFm# С@۪ekQR4$tDka0n )))Y!酜붰_"ꮯqa3k EChnQ'pƉz5ϐKߛoh}t،ŤR kUN1\={쮾$+iЉS" %@T-JHDjDRe~~e6(9>kcRR^[ht#kbOPQ꭬08B L]bF[[1&<<{ݩWIyX#VZ̏[c[*,մJsȮ̩*W`Q;wHY-SI$IJI$RJu2xIJIJNI)I$JRI$JtRН$)t $I2JRI'IK$2JRI$L2JRI'IK&NJYyoMś+ԡyoMś(%I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RC0~BVC0~B?+AgWI%)$IJI$S_j[T_j[TĔI%)$IJI$RI$I%)$IJI$RI$]?1_=w]Q%&I$RI$I%)$IM^'Ώ-k HרE-+WVX~D^]'Omcr)$Oŵf}zR??ڧ#C$JU$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)?/0y?XCogK_I$Rb u2BN$Pl:I)d$I$R !:J]$IJN$Hqwqʴ2{i"E$"dJPJci5K )t `(ZIM,Ƨ %CLޘzqKI*Y S.+ =W1ʟSyM=A `3lB6$Qjt`m:OS,f˕ab~jf85 "vXg_P1Z>+c-j# !5-W^e2PHBY "97m>kV) uk*vj, *MޡJ]YիFVNҡ0U.-RSR9َP۝ l<Z wIN_3?(uNĮN VwUϱ5:BJ^R Nu@7_lȯ[)1)!2ޫKU+8TRշ&fcqrR}1R?GE߮В6ԏ (gE;ϒS@A n^[r:O(⁩IHhaUYfIIU|9 + )Mb(t<7UG&_a JJdЍQ< ճj(g3 !I+ĥʁvN$s/8׫ʚ^Lt][K=()..+qۦԔI$dJRI$:I)IRI$Ϳpk:o,QA/$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$BB)Z ?@h$$I)I$JRI$OWZ.OWZ$$I)I$JRI$I$$I)I$JRI$I$i.ȹFp)2I$I$$I)I$JkŻaJjȷ:[R5oIOHI$Oŵf}zR??ڧ1t: $I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JxO! 3g^/V7R/??~))I$2x7./ВI$PIHL;$I%.)I$RRFgE& - xb⬅[$H̠I+%-hN9I$$O)5&OQOD ))%j jВ%"gZ$S[`ߑt8p#乾Q9{?FO nDrS:a3{)<%YET>\RrU M0g yU@R#)s";*Mi^KvR%IyQ>SfJJl5C\`3IRR a QkJCMM?:oP&SG w{-}&`@S#)rvs[IKX7c7gR{OA. 먹$$MeYse\XWHT*丘i!sRS T/1yDXRRfYQkGtTw ϦIJU2c; lwT{r6!(J ۩M @Wښ/QRS Rvm 8kƭ̬ΝQǗjӌ戒HILIKBtIKr#uGg2Ӆ zW9f;(gGtx lTlch%N0IyjO?% km96k'EO[YB(hfJݥͲj AFsCx^/݄yn[q* -ud4ݿa_%u %3虃"8ӯ2W>};w ^K-0i))U:SiacoQL m8BJmt̚fq=֊OmxO F!SP @'))@% %*Ғ @JR%DbRI%,5RL4K8 IJ)(I9 Н%,SJM )1NR)RRɓiO JTI:JX$(IJI$)e7mÇT}7ÇI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJX_\? {s$?X* =-;Cy%=@O\D^=lŽZ_.88HI$I$$I)/]b/]bJRI$I$$I)I$JRI$I$$I)I$JyFpi.Ȓ$I)I$JRI$I$9u\EYxm\.$I%<ׯ,.Z}O-3Ə->'I"T )pO`&IJN'ILr$S$IK"'))oOþQaW;,PS4$$A? )t;hL5%"o ~Bg)ЈDp0y%2gUY0 y}\yͷ䒝nӛG ;k5Xu!6(gTV1*kEi%[kvDw>ԔfuAݷTCI%Oc+J(@IW+j}GH#JeH~F UCI vnRNT@ۨSh%,uD(+ r>TxKR5N*5Tl]"R;!±@Uz*Ϩ4@s JcYRv8F.uTMJ+kD);) (oFQio%ΥPR<[щʝa%(˹RcJٔs(dGH9 ֱ@_Ұ|%dAU꘶kOʩN?2rGo lL|] }*x=*IM:zuujsՔdPI'IK)(SJNrJg2!U)1Vu )]'dx?2l\&@Niҷ+y^Ȥy1G^^G`kZ.;t +]ÐRSUu-cGp1x8?r᫏))I$$I)I$Jy>-_kX>-_kXI$$I)I$JRI$I$$I)I$JRI$cѧ?+"羻}~. b7$$JRI$IR&NI$$I)>Aghr}mY^4Թi>t: $I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JxO! 3g^/V7R/??~))I$2x7./ГI24 cʔ$D$M)S JRtIKD iwJrzSߢc*ٱ aۏ1U$ДJP)L%B{ )A 7tյ;Wwiwi \kRCRiRmZIQfS\ʙp8l*N֤N*fT0\i쒘٨Р]_wE=$c}&uq"*T8JIO;#  @*ʮYIEA4T; $_(1n($\ЊۦN(=a 9IMNS` h%M-Nh`PI'a.R (~IfGdވNAQcrJSFUfRS7ˉXp !{$*P{,aӸ7O0lgsci;?Ps#WNKU玐\J3:m i$Pճ̓䥍^4Or&.IKSJd2pLHJZ IK IL-ǮQ_SIKSJR6HR)INo21~i u.+A$PbX>L%em{? #Dk4.!G`Ǿ#w%:nHi;^>c@_:^54%_+muƁWo\ƴ%G7>L e%4:gӷ?Bvæ8I dܙ&I&qH$$Jdb$$I%)$IJJaE *G'lgrԩg~X qyWMƧߞ5Фz'.`1)%%N6y%3:XI\@ ڜHkۧEy$(R LJX' :bRRмѿp:-QA/I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JR'ackfTWܫ}qdknƱZ'U*\? Jm}]©hsu ju@OIJI$RI$I%'ՙ ?GK@j_C$JU$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)?/0y?XCogK_I$Rb u2C!2tBAv3)$$JRA2p :v>]RnPK4JWQ!'4m$꒗{ot `J^ q4AqJD[K =*ޛꓝw',,wu\[tjO{^[[EukDCAqMK*5-@ EI T']q2L!TRˤlJAALiS#X1%376#6eARSc44 o͝ĕ+TQ@")1FɘSuaS (jIgMHYma%J]ANR)RH$ .RRܦRX汻`%(h %1IK8MqiN!A3.%$Q{yRQ{wqJj7mOq=W>n|~I)CQ#q%~Jes )!#P~ƗϵȅgWs!Z,ߢ*j)[XRa )4yJO["+O dQk\NS\ NzcNP$.Qq$0BJt+Ѩ7ƊEGN%%@SCT*%JˑBt iFWN%5!M(SJB+$;a$jI(*>gsP5(S֞D4'}%EcZ%`Y[is!h$ªK} ێgNhzԱ}wRxE K}J&Ot`g?AWz?Pv㍐}{ ]H'X^@X}]ޮUtF u?Zs9C \64:E I2b˄ILE:I)dNJRp ))IL$JRIIJI$RI2JRtR$Jj+ ) w_`ơA?YXE#⏕#D%^-{.> ӱzjpDUzu5nZI1emC@KA5M-%.R I$%I$$L&uAAD"GiL]V Z3[V"cUS00 ohhX~ hJ dI"$I) TgZ> ,u Oe}?Hh~! ,vkas );"THB-m  !M3TNТKY%0mJm/έU:riJd > 6G{B JKYSqT~zm_Iu[)l돳ێ@u,r~E;ה`~ڸ}&]3%4]c/ow[ָy'Į}ʁvٚ`|RSEWr︨?bVߐȰ^^=Fu umwIM',W}?ކ~s~:r Q㔔㞇~31\3'W5x}v5E%>I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)K_7򭅏ORROu_r?V?꿵V[!?!IMށ VJRI$I$$I)/]b/]bJRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)K}^km6mnۻJyoC2W?7z؍[I$$I)I$Jyϯ_Y?\G[P~tz3i8<;HW:f+1j1E1z'6I% "I$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$CogK_o! 3g^/RS$I)K1qe{:_\_oAIO:HI$JRI$)JdS$!I%/ I) Q¶ %d]nVHo`9SL+;ߘe \;wސq$c$x}')=!*$ '84!>P&yILv3ݸO[  IJ *GEK:襚%-*fIorj `@us$t4?En6BůfJׄ@Qd*W\)) B;{ l [J*K{QkBvQH\t$50uѨXրQk"CpVm:HA,s)!IH "U=EE zze\xM\q\Y2Z $)cxh$:F<#tD]BqQHIR$$' RI' )A:I$)hNLI)I$JT'JJC2YU[=G4vZ!$Z[ $$I)I$T.tPa2tI$6An'G0;"Su ;-f"u4n[5ʜO#R>]>(=;2ǿӰ#IA~VFݮ]#J z𢊙JX82IKgRU-w!]:Lec^4AO@oxIYq RJGl-_>A +#D#7#H$J:_D x:sw7۠5YӃğ$Z,#ȫRPCQXzBΦ$zN eXQEH{vWE\4|k"bC.;H:RRVmo}D蒙\PtTsa{fB$DGP9ٯVHKO~sܼߦOf~gEzo,QIO$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JR'ackfTWܫp?*Տ:չVRSw@h,I$$I)I$Jy>-_kX>-_kXI$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)Uƽ_S/6V絯tZL*GZ׈߻grI%)UꘘOeY7WS0ƽk Z^g8ՇXdRS.m8fba?۷]!FXOCV2,:5zY0hKN5V2pcdJem9sL#pz^=qckZ{A%:,t>^Sh0Z O0'kdE 0O,05ۃut*}Kd.v[:Lo )AKk`|%>LݸMzΓOweK/b}_ôn[7v5?􊭝Sې[Q{Eٕ,~Kr1,m;4z u0kp/q6W>d%>I0,-M6h'9 ?z뙸x8V]nc{$]C0ۉmwV Vτ>tLݸc~t{-tI%)$IJI$RI$I%)$IJI$RI$CogK_o! 3g^/RS$I)K1qe{:_\_oAIOiQBJRI&IJI$RL(0IB,6):si4``@/y{U DXħ WiELֈ%.PJ+I @B-eK\JbA }RqI(;Za1' S&XXߊ_<@A))-e 'RRwcwR$o)n+ ))` Jyׄƙ.]PkZ4(%2iELH5Dh  @&-)NP% e ;VRݔ;d(Ʉ֎\C"AVh<SY,60M.?rV]v$ a4jج^m#l|Sy$E I$R8蓌($$I)tI%))I4Jp$$ҔJJRt )t"SI$)I$$`LT4$(HbTIIJjTRI$ѪtR:I$%*/~"RR&':d#€`qӅ+!ISҶtyMF[%3p CV)ThpPOA1))t)Cj))[2RB id9"RSSWd,'#ՁU\ |J>R[O!@"RS)JT%)ILO 'R$$L):PIJ4IKBID%.t&!(ILSS t^]f8^_?f8K $JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$*XZY%$'U*\? cN_nUB)Z ?@h$$I)I$JRI$OWZ.G#&ۑv{!aS]n$Ir_:Wۉ~]n$Ir_:Wۉ~]n$Ir_:Wۉ~]n$Ir_:Wۉ~]n$Ir_:Wۉ~]n$Ir_:Wۉ~]n$Ir_:Wۉ~]n$Ir_:Wۉ~]n$Ir_:Wۉ~]n$Ir_:Wۉ~]n$Ir_:Wۉ~]n$Ir_:Wۉ~]n$Ir_:Wۊt޶m0|%=R+i{4 $Iav=?e9RSFu͎\q{ ^Vu>z09X]m{X׏ ͟G־L i$TLޭ.sawq[IK/ ]~^]:w#l;/V9߳o˘{mi!qo6eq3]cUUX1LѾu쏫V0X2˿9k|ƌ>:3Gnt9RXo` ƵCD߫_R:V+e8Ml! pT:ae0|t~ QkD~Pkzt\\61C.wi4%4>a[ֽٳ5T~atW~aqZ *:ΧԾ*H1XS}O,}o=HJJw~t.r'%&wAB;zHֱɭkg_K={PiUr#d~%<ϬH㛋Q/\ĭo~)52x;+kXӴkcYں/uqY>~?澻Θ=̪,{~.,IK8V0 [Tݬt}3]L Ajq,hs{yoW31u{8`tC׿?Hf{ȹlv]n[+ꅙ;:_[Aq{CgWK@uRNTNHZ}wWUԺQr.akZ-!N$]HuwΟa%'Z8)$lƭ $ƻnɩ,XsH#GI%9=SM5ff]Ll%AA :I)zuOMM-- 33 51&NO%ZI%9[ջ. ys_͝e~kaև iDS̷[}]mn ݷ]U2 h ~uA_c2YGeu/[z^MXYxx̾mێn#FнOU9q'.s/Sz~G\ퟯ]s񍦦wF))`u汝FU'O\>Lz;w9AWIH衘170?h\~i;Rqm;Hz2(`Aq I%4:Dt}=;suj ^-LֈEI%9}[u24N%70?h\q~iI$$I)I$JRI$I$$I)I$JRI$I$?^'c_ a/[?⒞$IJ^1-+׌b J} $ҝ,t)I$JR BJdГm^|V>+9>-.fv?Q%"dBIRI:A& R%*Bc|Gޒ;e '1e fhjxޡ_!ٺ÷D0Ȕ jݩSxwTI* HHIkAyIMjqd kdFTȉC0k%&IN ))c'3rDZ5A ̣;C(%ߤ-U.&J+Es@CJ P!Yp% nPܪ qu8D[mШd|ANjh!u(.yPa$颀trt%΀_˜)0 ( |v)%Gq$hSL:R=e1))fJT(FBQa;< C#.]u*`iݩnּH)c~uG+SgQgTվ%s}xvNZ丘m&F#?{WrΩC_+kݸ ^?(\DV5c:[PnMh,᠎Zo Xdg݅̇6gkȕ-c'm%8VQ#[O> @ y vJH6cs6FԔzI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)?/0{?XCogK_I$Rb u2BI8 "NJY$Sj a%)E:%9g=kVMmxkU1,DI^R%4‰y(  q )0'C*$6ʻIOYc~'UqD7DTALˇJBN)ǁ.Rih%s*bJf\R#))8TZ5/_s$[P.N/-'{F9r !+\5EʬƒT&a%/eMD6 G* 䤖A5U,;EqB։r fPh ˁIHsj;T>;F1کT]!%"0IIJ~'*-PwLk]<(4$Nqi9BN(PRεVcsknUi!$s`IP3t ?5/)(%#㢛 UNJn6 wѪ*X/j6@P4Sm,< )KO}ʖ;ݍfBPB'LLRI%)$n$d1IKg<蒔;MR@Jd%(IJN gJdS.LIK)M))rRRH$P!Q))I&%*RNRPJ]2IBJZS)BPxRI%1 I)bSJd\rbRS0R)SJbJdH$$ S$JI)tLJJT)QܒJR)%.S$$-t%9C))/g?Ez+:g,QA/$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JR'ackfTWܵ_Տ:չj$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$//о+Oe^@fُQ$?+oZ7}h~M1zqH]JI)T Yk,ߜ{I%N&#wc@kd6}ԎҺ66[v][\{-$$7gu6߬i=*ZA #2u'ǡ՗k*+ȰƑ/bI%0`7SI2Jju{n+wjx۩ rck'N<-.#s]G~u CD))I$JRI* Ioo?FgIQt5y4=1c ͙D}8f]]!Oq )6MYu{l99BgPgmtp`ܒ *]Wp9YIKTn:~nU +)e-..ԔJXښ_a hԒ`Ot=l-YcI%G7lɦX%7!ceӖn;eg1?0f}kv7LmoafA!^g5IO$Wux%VE}1FrYsencs\!AILU[p՜5'VLT:6=Lk680}IML.gwts齯IVRI$I%)$IO 96ԽlOߊ?^'%=I$c[PW1 J} $"I2JR ShIK'I1))J3q9tx١eb y]W:xP %KQy$_&A*5$ ;wjaP0i9]QL'CڥRW*ˏf%=Va<)̧pBv(TtIHMXr9EIK paBs LUՂNҊLPEDQGO!oPh>7w +aIIqvNZ$4JU(n0̐ШkuyYK?]>ŵ?AIN/pҺv 5.:+=DIat܏ZsMΫ̲/H{[7:~dV]_q0䋒S]7t>Y.&u:!uX=7ke/,eZp?wl}o4{DXdZ]ۨ~wHZ`JJx?aѺgSysisK~QR8mi{>2_j?tXeu5 ƩW+hk~ Ƿ}yƴ׏Xp<~1-?\qzsiGwzTσ#kBe}kȯS=l;_RΦڽW~\y6xZ}//\kMAЇ 쒜s[oTn5f>=z:N~}kt~5KGF?zI%)$IJI$S_ a/[?Ɀ/0y?IODI$/n?-+We%%>e:I")")`JJ]3Uq=awb=8IDOunJ*1F|TOC rO2T)v2mR'@ )[ZT*Et?ڲ:$^WX#W?ܒL@I RLl!ZuIKnQ*j4IHP.> EHEL$|@W<)VΔlF(5%,B %$*̮N+J:tM y*H`5M b)JJ`E @$ƉJhI%.:$JcJ|![YyΙp VPMSMz JJC9X(Ѻ BJSXhvǰ uIJuVT,})DŽ\MRB2KBRI*Ѣ+a Na]LIFsDѸ`)z-xЎ]vTHnqn:}G `)Tk@()I%(bTIHR JBxI%#IH<$JSВ2J]$S:iIJ N:JRE IL bSܤNNiI%):d%*R$2IBJ]$xIKTJJY8LФ%1!2IJI$)hI$)I$))IB $$ SHLQ))I&S$ҚRIK(RI%.Ѣt+˺g,Q^J6TPK $JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$*$4IbTq JukQsV:87 t+INg?>߀Tw?GܒI%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$ 졇CpƓ>H2 6Hwq )I$$I)I$JRWE ˲VD#]wIO+e_VW=+.ڬsp{CL0.$R::ϴ^ݬchM]I$Z5u;p/s]Y3?Qаj.㸗;}$cPħYw[}4?ZMl`Z#_uuC-6֢uSkv$>q{sw=~}\ŏlnh2aIZ$y-s\ӳpno7լOb伂wJy_}a7~!֐AŸ}~1^8 >ҤKuJXr~!$0 fVI>.'+F/QoSײQow?]zI)>BY۲sFSWtj0ìX ]-6:-#XjIM>uR&7jHWǺzyX-ymNs.$Vdن눃esð IJI$RI$I%<'R/??~+rm zI$JR_\_oA^μc[PRSI)4"M T+&DVV+LGRKMi`-?z2ct+ E>3OU|; fP{xys'/vS;xd4IK5%c蓦e@ JIS&J`<꒘2d $yOt>PaԤSth]%^lZsg"vRB@ D$0MJSDc…B{S|6r}e&0[ZHSښ.sIl!URRҐI"I"% (]`0JfB YueT~nfdƔ6ĸ*=z\n%jJ ⒑65˛3m EUwG%;cQ {L?q)ۻ24kw8Xo#wrpl~sRSч|K[50>!MRSWiF2Yݭ QS7Ya&S -אO XEL * !$6FK\Q*4 Jy[ %6缠Jk R\UA$[)ٓᾠP:JhK ^-gP Kuf;p"-T%- I%):I$&I$I:d$LS))bJQO)<$SmJS0SJI)N)RhIK$ A)jP@P!%0!2QIJNttIJ dNRTIJ*%9LRRt $0\ )IJLN-)JRRĤ)bRS$jrR$JY:I%/*$Jd1g?KӢWtY_xI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%8Y_[(Ƶ#FQլf?~AWLo-X*1ܗ?IN?F/~AWg;s)k<>/]1ֽHc@.o~ jLeUsqOisu8z'K8Xyhu?JkcG͟ExcpUuz3n@:L𒞍$IJQsToZ-Ω)?3(H7:M̈́8*ocoRS8%?32[Ԕ?l#? }8.{d17̖?%=(H_l#? -Lj_%IOC?3(HKڗocoRS8%?32[Ԕ?l#? }8.{d17̖?%=(H_l#? -Lj_%IOC?3(HKڗocoRS8%?32[Ԕ?l#? }8.{d17̖?%=(H_l#? -Lj_%IOC?3(HKڗocoRS8%?32[Ԕ#.`XF\uںF7cCGa~GX9tZ>υM]X[Idh]8$I$Cuӱ]{@/JVF#>c)z:~mM'IM1̶r>w[U^8ct`}ORR0laΡG?|[C>Iߢ8|_pNqο)2.vaic)63EGQI]p>Y_oו^=TƒxOeApHgO%:}_W!ߚ;|UQ\Ԩ ngoBϴ~uDNi}g|e% rnkF@XU>eTHwD'A ;":6p+XnH.w~U<*#{x*'t{N>N2XUsl~fkrkn~:RSI$$I)I$JRI$I$$I)I$JRI$I$$I)?/0{?XCogK_I$Rc u2BSjBI]uۼ֏0w.rMqcPKe p~)$5%%)#*㸧!%,H7nY8.%'{L.nf :(uLCoxIMkm-IvTy. MB)vsې9Ps\T )h%"uR2hyLRc'ݬ&ƒxEK9d&&TjHX#2ʃ[-Wh$IK'I$Q⶗8RJ[o'ĩGF0$ 1q36wr~DFI$PIIJI$R$%):꬗IGځsKK`JIn}^}m.val]ckohk&,XdU™:(4)J@[;`@A6jtRx!S Ғ$p[%);^`&0s'bvE%L T#D&IRc:kT\@Q$L $ J%`;'X=Jt[⭱$1B ¯Աߋ2(l܁ΫYത%BmK$S(JRRddI%)$2$JRrE%1) ڒrmO Ja A(M))"䃥%2%BSIK J]:dRI$I%):dRJd)IL%.`%)8QJRS'eDIL2IR`S&a)G%+N )S))R&NJ&% IJ^a?f8ͿpI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)go]jgo]jJRI$I$.sWNf9cm5s.ck;)}g[靈p"~Hky$8n{5@Z&t>i)8$I%=ޮ.e:mj{O8k9tBNv^mp/h1%;=SS*{g .#"̗-qsrՑGJMn\Z@y? W.ߞINI$I$$I)I$JRI$I$$I)JTŷm? 徵;?1SU`x``#)I$JRK;u:XsG?>7XU[qZSu̡4I%d}kɹV7<0#~ꬾX'ku;\RSѤI)I Z_scKWP,Ϲl;ɏ]L䷫}Z6 7u{rf hdĖ38ϭƢf5G'"?t~]vYv;L˴ }+>9P &O5Gڞht/~6%V?;_RS.{7쾳Q,wPt%-ꀆSunGL_kcy]y +[K\m{ؘ5C. euTÝ1tnN^Y鵮>%41n"9MG`Q_RgZ qU.5;>)grCs6|O $6Ԛi!|<?j,1q#h:]n4 |G;򱴚c;Z\<|SSNwRoǰ r3sMC!^N;\tSlc9vK")s6Gq<>Smɿ8$ Oн]L_-}.c5d[)aX#!e?ss:WQ*{-m .$mOeEtx-r:hd Jy>Ժumƻly~UٯY!~EФI$$I)I$JRI$I$$I)I$JRI$I$$I)?/0y?XCogK_I$Rc u2BSjQC28gtr>ϚthpKbT¨.nn<@RnC1IMP $&eʹxG$c!;d߬$r},h!P)))ET(.v))rtHD~DnTWr wS{8ԮBΒ]klm-MUj!%2AltgDKEwڄTZgtmSY$)R 챲z.))ubqvhc쎦B&?N ;;UȎR:hm (I)$IJI$RI$H$'.M-;ۥuFЫcDDʦ}8^W&R2fE$d tjJ`nv 2^Q(@eO*E% !Ӳ\>m2xIL&US .ZV[:K!RhF֒4I )tL_r} \L(%v:L:cC"RgԔkUeB%ϑWXexά!PMc6] !S*Lm1⮴pQR$tI%,S)D%,xLRR2rRxILR JJd`RtJJ])M)%/)FRIK!$SOIKRt<$Ë-ϊvk}V_?]I%gjHb_?wIS߳?w%$߳?/ZػS~ĿgjHbRIO ?WC{!=JS~ĿgiHba(IO ?WC{ܥ )giHb_`ܔВ_buڲ;-tG9 BJTI!IL%(%I4$JdJcJI%* $b%I)%()p#U7ÇSrΛ6TPK $JRI$I$$I)I$JRI$I$$I)I$JRI$I$.^S-isiIOip>Ŀ9ؽ9$6oKNs}ӒIOisϱ/m:o/NI%>c[>Ŀ8bS?u?ϱ/m:o/NI%>fmﷁd> ҫicC\w$RI%)$IJI$RI$I%)$IJI$RI$I%)$IO%?bW%?bRRI$I%)ckfU*JIcN_nZ+WܵRS}dޅlt{WCʤd 6'Aw$_\a0GAg}sR?@vVZX-p o1?mk>@ne.ĀSNlƃ I oS` m4{d̼x$czoW=_?H/Jv?jz_p{c̼x$czoW=_?H/Jv?jz_p{c̼x$czoW=_?H/Jv?jz_p{c̼x$czoW=_?H/Jw)˧ Mypw\[HTVLtt[s]QvF@{ih&dI.{a~sJrpۜVv8RS&Krm5@w0W a_Lkϥ[;],sSg}6G+QͨсUOτ%7:pzE.sZs >U[z-y}N $+W?)INK?uzkp o9:s0q<3_98C'cecIJyΥղ2^[gя]X7Kmc?,oz>7-=fYn]&?RSo`}b-"-lHÄ-bc6TR#tI%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%<'R/??~+rm zI$JR_\_oA^μc[PRS`)R3(n`v %!ɰS9sO}wǏWZOX˵(%T#47䀒Q?MTwmi)l&v*Z$~*B9S%쒖yĘZ"'>I)ҵꭟ1u<^RW_D;Rk^.n%ꊑ>4 R]BzCݨv* ܭh @OP vt T^ƵۑRL0Wn^甔*VtCKaL%ʽ%!ߜwKY+R[I"$I)I$JRI$I$$I)IyJJAt+[p'.#$HL%%y/q:$uOӓ( w*XSY<+%±D[%%= I$$K˕wqh{^vHR&I%.dR$JRI$I$$I)I$JxO! 3g^/V7R/?~))I$2x7./A65"@*O0ac9^ĝPKCf}'p*jkL} @$hYNL a%?skknReq:f< &+rSXd5hder\ͧX>G ; <><$}?(>}_i`/G$s/ҏ܁4p|r_G$M8Q;?Kh?䔟qw%8Q;?Kh?䔟qw%8Q;?Kh?䔟qw%8Q;?Kh?䔟qw+xf`8>9^.6F$))n[E{I{຅/:FOf3 )RI$I%<[ȦvzO=L\{XߡXA \OSOٰ-ph։)5\sm,a?bJ*{l- LV՜C;q,z崴m[?-'"~2f}Tť׶ւw ˯ ŗs-QGʇ[X+:h~SwoteZ+;#3~Ek|Q+|Nnw-?EUL(ܞ[Khy i*QՍdIwV]fa{D6l}7+3[75FǾۀyS kXt Mblq {Y͂#mo2{kO+CTWwtTzê:1);Q}"rOѓ]kl:%%"uk1ڱmv]ͪX\bX$"QȩsAзU|>pǥ= u1>I)֛,;бwG޷:{p1s_Uqei'JdLN;ZtӺ5GXZ}Rp8u:ı:5< a7uӐ6:_U7GƠXjcÿ%%)Ts=LˬuI |myw` V kROŭd>%ĸ* HhIL|wm $<:5~m-~?K~9>dR~^Y>$gd}ۻh~qzc\n(}p,4͏ ;4kv%,"}pBJpN =|n "gO=;; 01>JYUsO}' ~P-vO׺JzI%)$IJI$RI$I%<'R/?~+rm zI$JRo\_oA^μc[PRSA< (HR[ A d;0-8*S=Fg@CvvOU;9Oѿrr?,'c}]m9T?KKe.kwoK@ͤC,:?2N}qsO&8iy#kw"2CsD#EX `oP&LLH%"P.m)bS;MjJc~m7qXI)  +"Q#TWn;5ogmG|yhUkAM^Ɨ;+ IBJGNP}w%*lj2yELEb~*޶y£џkj:uXgmpFuW ;I$I$۔IS-Jt ILI$SVΊSpt87#8LTXgRP#8@hɷj% udDg ld: ֆ^̒S5I*X᭒-'Dyo mx?I"4B}DoUa[#C`jF%BFS\EK; o iIH%ܩT{Q#:$9B/amANi llh~5ժu2]˩%`u5T]&AM ku5-+i 8(uM$ɒq2J^S$%,H𒘧:'%,IIKQTR1)2JSJJe*AEI)tIKP%,%,% I:ddIJN$ґLJS'ILJ` t$Jt)pI$I%,TOnIKR&L%DRIK O )I))`Ҡ~R]ʺo-Q^\͟pI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)go]jgo]jJRI$I$,{ʶ?ֿ=oII>kQeX[JRI$?\z'w?l.Z= ? )I$*Gd;hvB V7"Jo~)?NKEW Wcw9'y!jtw%$;/R;/POR?qII??NKE >tٸ`mvÄS*}T,p>Ӹ%b9̗h oRI$I%)$IJI$RI$I%1p$4+彛TSͼo/oMmnC`]+E$cڜc ܸ$Js1:/ms$m>J]k)z`;qt-S kgt:nsGfcljOZ$ QǵԼHor|D/g&<\tYlx!8fĪ~'RqfäHcFፓeU;.$tOM̢wj̧(t !qq1ĭdte.~~4ebחSK 4\Q?C] :WQyXF~ {ǟ?%-]/+Ҧr=ָ]+]_U6[\?{duc i$*}KczyeUVcs_II`6m:+g#6kp/VfQa08ȭfًp<Jj'k*ROЏp3ECǓ &o6VI`GW3]DV0?Jp'GgX%u׎AUOP#o*?RWyeK<#`o%7zM=C5$k&NC $}KVA.ٴ8~Eۼx}ȃ]T( -8>s{ߍpKsZnС8;P? MRڦL] 䔷՗~t&$.{F-,l+Ln-O*͏PMd6SUƨ0h ( ߐ()D"o T4# h`MmIH)n*`*œ$VvGloh] p:mpuV{UC/5%;+kʫđ:O:2c"rqq$'DWv-~0/4̷m'kWae {tJNI$$I)&2\"1;S:~syڛs[ĵ<+;lt_IHkjhc kFR}?iD;';'R?}政_OQ?NEI$NEI$uw/}OӿI7?$ξ4:ҧ6:w/O';'R?}政_OQ?NEI$NEI$uw/}OӿI/ӿI%#}?iKuwlt_Klt_IH_OR}?iD;';'R?}政_OTNEI$cJG:җJ'?$?$ξ4:҉6:w/O&;'SggtLͻԞ>*JyocaO._`u#n=!͏nYo_}J$IgtٕA)KȶH6U=Sidp֏[du k I3ș_Tl"^{dVz7Fu=9ع#kݸhbt$ v~dka.2Ici@= q˪DZU.Z >$IJI$RI$I%)$IJI$S_ a/[?Ɀ/0{?IODI$/1qe%>S(dRuKX2>*+o-O"]DGA,'Tʑ:(Id:p!2JQjIJJQ8&R`:s ЩQzPf8IM"RRŖDF0[ٞ0A!wH34wIp2h;Kqأ KqoLv_Ӱ\wſo{Bt>.T Pn@K"{d(%|sMsܥS%m$*^mR[uZ P!g8A*豍lʢn$ߨ;p6=ӱ" 5S$HB`)(I%8]s +@S,F;0H`⢤ȩI$IKpxI$SKTϷx &z@m: dj|S2aUqvu6 9/3 +Mtخ@ ey%$͂c M D@jS@CuQRZb*g,ݢ NUo\͢J))q.2UTդNs+%UҒuV (dvR!PU,IM.17PèOh]=`}RB|)ʊJdI))Ԏ)D'i`RS>R'IKI&rr%0 I$L&$RI$6TW0g?EI$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$*G-nV~ԤEvi{CÑ=E:?ѳа~5o]"Jy.޷`hfuֱu)I$JRI$*XZY%$'U-EcN_nZ)I$JANkwsAן7:!֚I)?Ο}Jy/Jv36xskn`cW6SkOoJqWLwcaU[ple_4q@plCD]JRI$I$$I)I$JRI$I$$I)I$JRI$I$!\(J"|4|$_Ur}/k[k/? Q%)$IJIblpŔpxZbF/OQ:`78h7W#$u9/sL8zUͦ6m$n>>%;.bΡF*!C6nC>:$IfeFlN6GU"PjֶxSӤΙ:vs@y5?{Ͽ;Af)X8=S'/[@o1<݁c[rMŞ$ZcdsHANrJz7l߃|+'du:/p׸1v%=zK.mab5iE/gK%yԍ<))VwW>ϰ1&wn8,z/TZѮvߔz>k=?>&.>JS"v:}y sskdםEM{}IOl۝KCm#] ލM/ײ *˩e41?v_)?(Z$*I)I$JRI$I$$I)I$JRI$I$$I)?/0{?XCogK_I$Rc u2BN'E %% ͬSLh>WL˯췘<#LRK2eE<$IpRIIKsu*7NJm~"JKs1iJ[?yޓXubGU<0(m`o +X}8ٯ2}ˢ@ڞ[K\ Xt|1R=W}4zxhВ8Nq1ʈMeC.3 SCFZ=T"niI0Z*n 5$ ?T<$U2X SlS!hi7څen32Rmv) ʩG Uz 5ʷYW*6H)I Tv1"nbvc˸;S'Qiٞஹ1&S$*]$ )t2IJBy9*` %'XlU_m'd<(%Q܇]!RRgN;*ιj$JJcT7pY^㪀B`)`p \T,:=kJl2-CclILym‡W諔6[$II`N➺q(νRNFiy!3(4N$bTH OR D$P@*WemѺ:JE&K^[MTceMՒ6pB[z;ԹO+k| Hn/ak!nks\8(ĂBt th{"$$JbTJJd\])$jIL)fLL.RHO2tIL d$2R"%)(R R%0TJJX2RR2t)I'IJI:dN)JIRPcT$&"RI%($JRdR7)'IK)m:I)c@rI)N tI%*JPjdI%)yMś?+טtY_xI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$R~ԫ Qo[RS+?"7+?"StrXtrXԔI%)$IJXZYl,{ʒ}X['U-DI%)$IJI$R7"|3mIN.'ּ:iZ`<0,0?зK))<0,<0,0?зK))<0,<0,0?зK))<0,<0,0?зK))<0,<0,0?зK))<0,<0,0?зK))<0,<0,0?зK))<0,<0,0?зK))<0,<0,0?зK))<0,<0,0?зK))<0,>*z#QB -U1`v )*~Z\qL|4t˟}3;-8ﭢ6&dBJwiS[k4>Jk_}I}?V^rcO$U%GSSchB: *vֺmd@s1 @'{5־L5 @INq;f{V.;c*O~J?sc}AK?幽C.s}m;hC$!%8Sс-roٍ>Hy$92;_p(_Vn]BZВA {toO?tTJSXvezio>;gIHuVjGd—C|?>jOJa =IR_WvWp,ZyBJn}an.WĻD9 ~z~@fZfeZƷ@,:>C)߸$}6JM#p2z}ml'(7ݘ٧&41f]}b1Qk$ˉ JAZzmIAӫ(?WΩ0\wphf;8h=޲,n ǙधS;-T> ߫}/#-n[?s{7=+Ø;YZ=W?#m`k m{TE-{ wU?u|\ag [XeqpXOfY+Z{i%6_t<] 3Wq́S{n? uԱꡯl$@ZVJy\g-) urEٗZM4@K[WQ:ׂ28OI%)$IJI$RI$I%)$IJI$RI$I%<'R/?~+rm zI$JR_\_oA^μc[PRSI'E N' )ըW9f#ʺ]PRSC7opcX[cxP/sC~FְZydHrf))6>1]1EmTWp0 D4p$Tùh#%/%3k8N4mUVQ[P:KOv"PJ" Vk+fWkt0!]naT[)*;"w(3 UQJ_XUpsܮ]8 .tj'=`bJGN$ԩd] ebqs26vj!-%MyP" 5mII8Q; 9RnIyہ1t@slnCukHGcō)2E%2N:JRg9!:I),Zt_ 8!kH|B# Y.Ec"j/nn( JR퀜(mRF;CyStOk@=Ӧu{%*,K[4IQ $W<1I.ӹSȔtԸ5>>9&OdBA[ B5QBPS=FaI*IL 5(;k Uq$:!Y{k|([1.~_ 1 )?S{2߳v"TN}kmfY.هlntx^% IO'^pWgXg2,Q?SD"޵r>uK=[(S$cI:iJJ} $IJ\_3l=GV>t^_Z5M_ZwFx%8Pղ\cXcim.t;u^5trw)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JxO! 3g^/V7R/??~))I$2x7./ВI$PI$)LT6UHGrz^1`\"D4@Z WL$I<$%(铤ƠJe_?'`,>hS nCq*+ Y+a' q (%{/jݹҊ AT; AAOd7Ԣ ВCCm$jJ*`9I 6ƩF8v"]Sk|TI[q%" 4Rk{U n3G(ܘW:sV{_Kv~()!I;dPI$I$RJJdD`ɣHC2))FC23#;)cv4MElu\lCC6hs# )4Ok٫ |ReX`ܫxE }1@Ԫk NPe%!܌ǀ%5!1 Jdmtf}"*2kBtV#vBHEh֢BLi&;h$u7^ oh~ƴo'дt@ M-xH,gpֹ_QC4I%)$IL\$RIKp)iI2tI%)H%2n9@ܢ$ozx[WkRZ?W'}/W'}n~E_Kv/*jJp__;/ؿ5)ww_UԿgb֤zxKzx[WkRZ?W'}/W'}n~E_Kv/*jJp__;/ؿ5)ww_UԿgb֤zxKzx[WkRZ?W'}/W'}n~E_Kv/*jJp__;/ؿ5)ww_UԿgb֤zxKzx[WkRZ?W'}/W'}n~E_Kv/*jJp__;/ؿ5)ww_UԿgb֤zxEVM̧sw4Rgb֧f=nelk4I)Hy7\Ɨu,+us7ݴ8AGo4v['u ̒P9y=Nu,'qml{>0۹GWgIO$޿k7bыn+u_l6==ϒkm{7spYW>}{1,srVY.cQ2ueF~a=?&s {꒞C..y?8<7>ڲK\ Gs\:O2hMi|?Xf\evy%; .n7VYmnیwzl4R5-h2;һ}u~M&E<_R?gL:"lĭ11<JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$rm z96ԽlOߊJz$I%)x7./g^1-))$I.I$NJY8I$I%):I$$I)t'IKSS_LHV nCxw(%蠄RƇI)8l'I%/)$R ɔTSH T:e(U2JS_lY%%#sN&Jb&X.|RRo_[aJMĴ~U$ֱuֱu)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$IS߬9Dv9IkQ5\.GQ-^I$.k1ǯz.L)ʫ_{*`.s`uXXcAmcv~ )Y].*Mo 0摵'oTdec^l{d8O%=JKw(wL3k5ڵ<I$RNq:]~m/ O& ʛ1džt^wWꭿ]w_>iZvsJz߫~&cVX߲ENebgØC~ad#YOp!<{}SuiiGoķ;lƿ)ré=.s4|?WmsbK^Z}b8x,cKm{Kct3x$D~t+01ꥭi@#e%=+$ ).MgߓlK)*RI$I%)$IJI$RI$I%)$IJI$RI$I%<'R/??~+rm zI$JRo\_oA^μc[PRSI$(]$IJI$RI8 )pNJY$IJI$RL%.),;3O\l`O )dbI$$I)I%CT9LR%;Tv[p6-1O1YR y.lŴsOpS_b_Bo+%<=?++/BoS_b_Bo+%<=?++/BoS_b_Bo+%<=?++/BoS_b_Bo+%<=?++/BoS_b_Bo+%<=?++/BoS_b_Bo+%<=?++/BoS_b_Bo+%<=?++/BoSҾlgv gЯ[&}$K7&ޙ0kZ=\O8 _{JJjCْ~X@^{G1ʋ+o= 'AϽ;oh|!%="K''OMgJ˲67hs`n“i]%2q:+:GR}&{c? ~guu<9΀ \'em{벗Z]%:S̫mAV&u#r=9h}x{sCdw߬yK:cXZ&aĀ |H og1eD4CE%>/Tc}J*stAan|¯+zT˪sŭ>߸X[VQM2r鍬svG]iwG=2t{-#X'xѧ%= K7}bo;k6 OS%io^'-r쾯nU`ꅕ0>J~5e`sdG;7Yqc!Jou7O4w\>8<}A>g_] f;NL:w^o)i̫67SWNպlk;kFFRSs(a1K`I^o־g}yKpm.7owf?.znVI486g_ۺz_Ϫ=#woUw>'٩IOGw^f4Iq<4<{ٓS."Z#C"JRI$I$$I)I$JRI$I$$I)I$JRI$I$?^'c_ a/[?⒞$IJ^1-+׌b J} $"$I)I$JRLtI%)$$$JRtR$z9w CH {_hʧA(JUJV=Cۺ IK$)t(:0RDd覒 $JnR)1 !9 :"#sQKwIMI0)II%)=Dl %1lwIKήQugJm(Qvr6qUs$}*U3TŅBPl|(7[:(-GqIKUYJ! tBkEKS %r%%.`e: Rc%<))uN"R)ELUmmGp7E^~r5r X'gUk3r39iPr]]fY%hǺ,j!D8Q!%- $BIE x%ZO3U' gHWU/Me htAl>+w\¶6c'⒝S8$})>pq*կ3, Z&Ǐ$[)@I)JPtw;e5s%Y\?ef`XG}|^ܼ'͝_.&?qS\yJIJI$RI$I%)$IJI$RI$I%)$IO 96ԽlOߊ?^'%=I$c[PWI$I$$pM2%)$IK$I)I$JRt$ $(vAJse 5.\@4dat-+]Cwd w2JaHQ T Rdv@*JRI$I$,AVnϬI)I0rRR'I%)$IJI$RJ\)& RRH%):I$_/ 1&&1%4HigdtZ _.xA-Aɨ7XǹZO*u5|BJuO@:$FͻvIǴeV JQ%IOݢ(kj ) cI(v04#Yw7T ;OHQJ@jq]0LxAKB`PsA NbbH$6HhHHN08԰~Oȫd_xjJyYӜxW]@At$8UY(ҲXfDInSu7z'SWBāT"Khg}ΰjs+k u6vԟt;1ݠ>܅vlGg yM:Qo݃C=JxAs[h,py )˪\2S̵}:q!P D"aE1%2I dR%.)ILP R*IDR $a"&))bI$% ILa (IJNI)IIKI$ 9L%()IK$$xIK% IKIHRYEywMś(%I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$N?mDflivX=_=9c SINO=wZG-nYU 7GE-))տtտt)I$JRI$I$$I)I$JRI$Nc=;4tS8e˱Ӻ"tj˙KEu㱭W^Jy/ןyku$yk/ןZJy/ןyku$yk/ןZJy/ןyku$yk/ןZJy/ןyku$yk/ןZJy/ןyku$yk/ןZJy/ןyku$yk/ןZJy/ןyku$yk/ןZJy/ןyku$yk/ןZJy/ןyku$yk/ןZJy/ןKu -VIJ^[:֮c6;s/Q^=]KI{vP`=ؒ\:?V_wAjjxuJnJu w{$~VJw9y%>_1psz[gfLh,"Z؝s Gտ NCi?758r7ST*ZP:5nXT m1; juv5 $JzxTA7ӇI?zjND#$5*=7e׹᧖4? tWFMVms'A2 R_j:Wh5m-5W/*wca&Hqn^>teFIvGAINdZ-p"Lwb]Ez>\Jl1OގtݴOoL2;V9 A_Z_Z7 C ݭy$Ԛ:I$$I)I$JRI$I$$I)I$JRI$I$?^'c_ a/[?⒞$IJ^1-+׌b J} -%DI$L)d%)$$$t$$dttQrQ))}[_.Fי:A+ qEqp]@ A0 R 'T9W^tA5RR:$RI$I%(p˳܀"Sk3C )QrI%,:dIBI)I$I$S&Q %()&IL:pRS%u:JFƖmS:Bb\{Jfԩ(n !XCoyAqɮ$W:^85A-{ [S qJkWD */lIy$`@&TQ٪I)@'H)I$BJR!)6J )Wr@<%.BhP\aL" 7ɪOZ.'X~]cӍ2:&Uf V[ATv]I5Yw[-?Q= =YC5>XwqGIN'pn|JK?_ |h9RWy[Mɭ$O!fA)(n1o<"\["Z _o[%$1*)d$Te%$ҘjIK0%EL$NBdyQNS%P$Jf`RIJI$R$"RRd)I'I%,:d:P(H$RSI%)))(yIKt)JI)u7mÇQ^]f8 }$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%Ո?ERRI$\_ΛԬw3}InNӨ )\N7y#0,sp5\2zG@s~j>%Ţ|RS,}coQ ^qmx[у 0C|GHo!հnԲK$IsXIg>u!P^cy,-.Jz7]ӋЮk``i K8@,ozzVc{=-92\ Ir=oht^qs}0KCOţ*կ/#8s/};< ~E%=  ` I(=CVnx$ 4c_rmc% tL  ̮zuAԼv{yK?:}C8Udt -3*؝1eߐPkp\N)Is_znRocf,!ڝ:rafcއ1L0 jJ{d+dyŹ϶&|KGei[у 0CO⒝u諾Xē$k0; f, z`Ctw6?CPqexq#S>pq*:F1hc;F!#*Z} sLw )jbuMּ̦c=p+Cb[[]N}T1ִF_ӾbվڜC[.0hRSN%bv6 ` h,@ne5d p*?8Vۛ6 /OwkWt?ղ6ƽv&Sص5staӇUuKXG7[To)UzZ Z|kSt>S/VǟZ*cZ"I)I$JRI$I$$I)I$JRI$I$$I)I$JxO! 3g^/V7R/??~))I$2x7./L22t J]$IK$I%)$$JIJbR$'I$E>O )~{?. 9<4% Q )҈5EJ-$)I&N<$2$R_'wpj,fY[|\RRX3x C;RR'LI$JRIt2I)yJSI%. $’IJI$RG/(c2N<afYsg )7Lqv>?ֆ[Ssh9%`.IM|ϒM2(*Y@D'LR)ItTSB E͔DR"H5JJJ`])IK~‰)]Ԅ?y%/ET$EL\M>xX'..WAN6PU LtMN?vh!K- (oS̖pZT+{wj ts2-e?cENOл-ܣր@$.L)RINTe%.12tPJnTR@)%("䔲R )NiIKpR$NI%,S'I%*JRdR$JPN)$]% )J[S )d‚))E4'!<$))A8LRYIyoMś(%I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$]?w5Ri+oG?ԯ)O=wZ/ǵKSOgVJx~oTsh&kqW?muĒS_m9/]k.$NKZ@iˬI%<Ӓֿ;rIO'kwmuĒS_m9/]k.$NKZ@iˬI%<Ӓֿ;rIO'kwmuĒS_m9/]k.$NKZ@iˬI%<Ӓֿ;rIO'kwmuĒS_m9/]k.$NWzGSyKk ˋo~g9SG%НP0oԤ:M'_q5T~}^# ܨa8hЕ1:}m k};DY )Y9?Zzn&{:UF]ןơG̭dO;f7~5v9`whzG~:oKqC^ȵϟ?JJrLCi AF}˱=6Y7XD|?^qfy9KWK1a7ôєYq p?BT/Ѿl{[o7#Zоd|wOBmR[Ps<$__cnfKZ'L-oQ+YKыC9fd[>ϩ_ӑ 6sxwBgVg v[ɱJ7y1%8]0'^ mu ;Ll9U-=wI_Tz6负 #ť5z_3Knѩ->rA*`v]%N{geN8`We~~_Ccd(cwWa-a.svx⪻*14lKܵ?O/CdZ[%fu׬\nYƹg-IOaz՜1l2nu%> ٙˮkZm?Rr :Cv2~4}^-e${GDG]mc;gkG޺qmɱR  g 0Oi/NIjisNcG`}b/t~*߾4{:%:]G'dUg^ȶ>[hZjW >Ӱ llmelv{rBM=N}Fޗ]Lcã[Si?EsvV浦4h<IN/fM^.a (W}p}Wkx+~o^eggZyo=?'#;X=DwV'NS+c+ kXs;Uo}MnE3D\GÔ5d1o]-f/՗cXIl#$/O8 C,}qGJc'L-a'2z'NXظrS(en^FO+'_ZPn˨c``g[!%6?-?NO`?-W\t{EM 6CO=˓ǎׅY޶ƿP@ï{+~5%<v)l<-hnp:|>ΣCaٯM6&9ЊgJ#}l=T˙T籿XRS$JRI$I$$I)I$JRI$I$$I)I$JRI$rm z96ԽlOߊJz$I%)x7./g^1-))$I"'I N RI$%2pR))A:A$)IKBe$R$BdSpIM _U0 d+y~D@|vGQ#Tŭ @E9EL:`)$rJ%%.5N$ױ춶XLBY=w,N~IHrFC7dB}rN-Seun,~ @7d EG$qnGӉEmpWzYsèF-%ޘky"k*ݖ[Ұ+t2XwoN-9ur+wksO}AW~?E²ǭ ^mծ's{KmN?H~UmL>=g+t=ljm[)W4Sjכ-ƹsjapulsC~@Q ud gwqp>鸸Mt2MfvUY?X:lp fv]4BX?Y>Nf۸Ę~WfE>g{at2DcXր8sk#Oջz1-ߏqTY/Mmue9۲Y}&C[#Ys>Q cqo5 qF&Cqa`,.D7Νc}^3zf[i۞K$DS:N= 'Jm.kn@gw@n]N-9r*wcsO}A$ZM muF5UtJov]t܇uw'i$ϯ˩W3Ƈ|aˑMWE@5> ]1:C3)1'@K.k*a-x8xL,|zXƆ7F:JCFefmg?"zy'KMdvq$8|zϦ1kGD\< | ZMs;khcdLYu5ƹ}"B; \iA$SKtqq˙[XL^u^XƴUSZk)]g{XֽIK;bufS]i,c^ JX*]3%5Ӽ˽65ѪJRI$I$$I)I$JRI$I$$I)I$JRI$I$?^'c_ a/[?⒞$IJ^1-+׌c J} )P IK$"I2tH$I$RIJI$RI$I%)3$9NSJJE0k} x[G,ϫۏ>&V(%%ml :Y'E_%sIJutB8) E u ̩Y4Jr!E'&RR1 JJ]0JREKC3XƀYg 44KۨAHD" IR' I'IK'O ))t$I$RO )I$JRRRJ!I%)oMQw~cT\eiӀ2*X.hO))fTl2Sr RMҘ0I꒗O)n={s<JL^- g`K̍xwb9NbZuwTpcvU48)ctwB&ާoN#{(!Z@7<4m \})纰*`nP[S~+.ج}$WgpLÛ` g%H ) &Ғ% 5LNR@JJdRdS&%H$PJb Sh1)R)%):d)I$JY:I$$I)I$$$'IK$2JY1RLBJY8J )$I)g&hN$Ɖ8 )e7mÇS!ygMś?A/$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$s.lvmq;OӒR_FYҽ:s+>-\Y ;eI8ȒO *4t(mߺ˭I%실?n,rWZLnEuˮuHrgP9Oʲ1Y;d;pp!%;YsҺFMvv\dh"c?o~ wXWK1;vYD-/?NM5#IO0_=/L8XkEC_ӦxZ{nG1Zׯu^f`k-ǺM_Z+ca*5>wxS8I5%x3ֺXF%cm?8.[w}LkyNpvN7<\U}]a"IO?'ꭝ9< B_sWѝ ݔ浭ﵮs>jkd>ϫYͪ EnjtOosGTÑ̖k558$SٶtX-ZYwop8#t 0 U[Z~ k{oS$>wH4w]]J=H;wq$~־1q:V>%_22ì|CE[G՜{cfEn%OfIQG/羻.~X\JJzI%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$CogK_o! 3g^/RS$I)K1qe{:o\_oAIO$$DP$ЦJ]1NRR$J] I)J3 IILL$I)I$JRԝugE@x%;=.OܴUdmL RF($#Tjƨ PĉEK'G J\)8AL *D$I%- Pv= -}i^ΪHXJRL2tL&NS(Q*AE%)?޺A*$L$$THN$'(JbVU@Adn,~H()l#+ũZ(<ޡ6t[̻]LH !uSM)R'I%)$ )r+ d*m²,Z Ce]?=4x=))ovWJn%C%dr\4 %$ّN-dd E-am|Shp8'x'%x@C5CBBb THEHJ"SmIHS)%1mNR[ I$')e%-`XR4)5%()ʉIJ 0RIKiLJJT'IJI$RI$I%)2tRI:JY:IRI%.$RL%*RPN Jd`RR<())e};ÇS7ÇI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IO%/?]j䰿Y+IO1w]Qs]?1_RdI%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%5zPbۙ|t4L3Tneqhv,/>lDW?V<  INI$KW?+sa"Gǀ;$W3V1 K[[[?;a[LZ`Y%zf:;-ͱ zݼ\:mWͣ1q ZA%=I$~e3sqs#k/1;?/z}7=]WEwIGEv ?ASSick`^i6z=9u)ɮֵŃlNrJH9Te 2?m%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%<'R/??~+rm zI$JR_\_oA^μc[PRSa+dQBJƊEARI%*O $:I%- Bt%($I)I$JRI$RR¡kNnS(7R_`瀉7>%h<`)RYT5L@2RSXl)Q$܁ S`"J`(4J `T{)E ܢI$R$$~{Z}2d|/mojcK]+dr tS9:HHG )xHR')RtR򘤒JT'0RIJLBtR:I$2p}[`slyBb[;}VxV P0* JVX+$0GuU.(n.ҍSQRV1*@@ALRHr%1I<*C ^9 )/=C,6TS6ӻWRBI$T;J4A2<R@d{1(Q!%.]ΊC%)11e,mm.q*ke^)%Q1_\&kݱc }'Ĩu^`l;J ptc _ip{"_WּV2ed^[qG+ZPJ2DTl0Xo#=ͯ<%n^K#*JOAN $"(")) %NSJA>)hJpS5EhQO))OP H$P4JQ*!H )xI"%)"I)A$I$I%)$IKdIJI$RI2JRI''O %#)NBv ) %%(@BxILWYSl/+o?EI$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$Jy,/yJW%/;+IO1w]Qs]?1_RdI%)$IJI$RI$X_X~} D4zJi}af{qsws(}~j·:y6wpXݚdǩ 7y|RSO]{{R^]jI)׿y_֤K]{{/uw]jI)׿y/uw]jI)׿}/uw]jI)׿}I{q2LRI$]?;y$TX!}lÍ͹y{ߏ.qGWIWFCeQ%>~}w~^;-a/66[n/֚XMLN\SO_uGu.b}3 A}{)zC@33F8%<V!8X:stΟ]NP}CњV^sGuޟf]nC0IJ>?52cg4V@|S/3iٚ7[iiwp P5m\9D@ߓ_ ecoMV$hFj#QTbPze/_ݾ{IOtݓMO?4edaV0^Ciikkx IU\Ji@t%%7\o㗧=⶗@O`NoT dkLӹ߰͜kuvǙpF㋧K02Y87կCu?,o~}|6Υ%=F88暙YCUĒIJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IO 96ԽlOߊ?^'%=I$c[PW% ӢO d2R8U8.RIJI$RI$I%)$IJBL"4IM@u;-?i] zFh}Y L@O)7d`R+CE:@TU!){GaVEITR){ L)Y >I))!0ʘ(n$LOd+rl80)bhdJ۟I$S8&S"TRI$I$RpRR&NIRA ))λv"CE t"Ve֖;`f;{q y ˁ 䊘5ɢT@AKL>SՎVw[v1D9S3$ IM\=)`N>#B$I)AOFLZIHwh84<chnyau~k>()1 u$Q''5tfje%oTwozޮh OJX!eϦ(LT7m*m.>QP3l`_՟0rt:6ǎzpKDGVv%R2Dw%V9ouCyJo [cef{LjU[^G]AM'*M)$%:E2J\%.{RRɉRsTHILJp' )t(I%,JR'𒘤I)I$JRt$$R$%,E2J]:dRRHR$%):d)t JZI%.JiKRR҄IK/+o?Ez o,QA/$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$K ^RaJRS}v4]Co\oOt8WFD$IJI$RI2Jx/ycZkqٝs+Z.%<ιemIzJ}_vsӏCt%II*uUg* ?ՙWR$JRI&IJIyO,~b|6 ̫\zϪ6EMcNGq۹%>yo[_w Oܜv$ hF#I0gvRztsmXl))I$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IO 96ԽlOߊ?^'%=I$c[PWp I$I%):dS$ $$I)I$JRI$t=2/X=.>N+|hY4jI΄ úRRMT xIM@l:Ŏ(# OSӉcl6\w B nF%*SJRR)`S DˆRRFJfNRR1N%0!;D)JbRiIJ%1)% )R)& )SR$I$RN%)$IKI$PJRI$I$$I)t逕 Ԕ L5(IL!8 IKJiLJJdL&R+o?Ez^Wӿh8 }$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$ ̶a읭9OޒԖޗޒ\<0,oWu+ĊƌjJmb:՝EEq.ʌ,<lW~6[iۡ}Jt~}~. b7,߬]+{ǀ+,}Y@wrJzė'6vKmuO=ܒ%ͮ]S;$\?qcXaO6v\MOgk?ŶdWYgYxw1خAi[c*ؒ~RS$I):C *߱zwۮ]bI)b\^XJy?ؽk;%w$O/ZNu~_nu$ֿӻr_zwۮ]bI)b\^XJy?ؽk;%w$O/ZNu~_nu$ֿӻr_zwۮ]bI)b\^XJy?ؽk;.~>;k~əUR~ݵ88moRSSu.H5߀h^_=>D;":|Z`A]J]$R<=Ս %Ɓx]o֛sf=;#܋} n.ߤ`p< [az}?NEX:}8n{~'䳺EgԮΉ->Apl`+[f'}f7 N~\W1G.Z[U{qy$RL%)[~cKJ~is pnZAå#^K w#i:;.d^f F :5W-,:[oBX}֐耰_\ ĮFF@80GK}uO΀ {mA&_Ʊw=ڸy([oQqvkFGPҭ:\A1 S=ɩTehsL!FTDI%)$IJI$RI$I%)%CX>]j~JK:LӠk.>xlSI$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$?^'c_ a/[?⒞$IJ^1-+׌b J} jE !2JRI$I$ڜ RI$I%)2tSdx'!:GTu6p vƫۜ5eZW@W0pۤ@绀 sπE .ԩ<*d<9;ALRPn&w :mGNCsjwjVG !I$*RI$I$&rt%#NI)bE$I%)$IJI$RID) TRILL t$RLS1VIAW%4~őWv<$Z~A-l<1$VRI)$RHPٴǔE4;BtGَx:+["]Gt(R"&NALe4Pxn=S+ȭ!Ka>nUޖ֊~bjirV9HZ<Ω)f6A0rrYS_g $N"@S*2JQ2 %0U1H<$J^%0;`* RIJI$%)D$j))@IH”$PIJI$ҒBIiIK)$d)I$JRI$I$$P)BJX@SJI)t$L$$I)y^Wh857ÇI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$SO?ұr=Kv$/}T>ArJm~B/[S Op V9;xIOo8NkWq7IΨp]κ ]&8lhkה$IJI$RI$~ z{ÿڷRIOr=F v=3uОxѿoRRI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RgO 7`d>T dcIO+ѿe_SLjmܓѮ$X>a}aʣ/1G~sbõ۽%8N כLLAg.KqX+֍:I)~`kzo[sIo{x$S:>{7k4tE;տi\ߘk[+I%"mNt-1po_נ&IOW=䝤.4wP"wX1mF Vlmm hRI$I%<_]/ӅYos"EuϏ2:g}BKրΏ~}k:N)ų8ht64G޴:F|^?_Y~cZn[^E`KG_65"'kp+Xn tjy[RK+cMZ=sę>%6}jX6Y1k ]yv^V~c} h~?&NJ|濩X~fu:0msf=G7:Q8WK{:"uEu񝗚sGrGfd;Te|ivRS}Uͻ?;e,.'k(US)ckJRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JxO! 3g^/V7R/?~))I$2x/./yLE $ '%,xJRJ*\$NI$$I)Hw|Q ;QmmSN:У{e $RiǽNj[ )`W8J#Bk}JJNӸJwXh.IKfUgX&JHB*ZI)W71>U\RcELI$I%)"JbN&NS$J]$IJI$RI$pTSI$JYH&NR$JXIL RRФ2NhEM%)$IL p*i$I$Xirޤm>AbuQB%%.Q>U2M/vjh`%2ߜGr컩wWkJs1HWV#D`pl`W2n 1C\dTVz+/ɂ er` l$&r(ZuO)R%-"SHH&%.(ILR Kjp &N2tJN)N RB!%1 O BJc2~IJBtO* JdRM)%2Nj%)2I$)I)~FP}z#t )D)9S$I:JY$)J)JJe)LJW :o,Q^$-o?EI$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$Ϭ}T>Ar??>߀T%;I$JRI$K ^wRWZ,/yJRRI$I%)$IJI$Se}Uʷ"뱍{5tÌ!L9ܺԒSL9ܗN˭I%<NL9ܺԒSL9ܗN˭I%<NL9ܺԒSL9ܗN˭I%<NL9ܺԒSL9ܗN˭I%<NL9ܺԒSL9ܗN˭I%<NL9ܺԒSL9ܗN˭I%<NL9ܺԒSL9ܗN˭I%<NL9ܺԒSL9ܗN˭I%<NL9ܺԒSL9ܗN˭I%<NL9ܺԒSL9ܗN˭I%<NL9ܺԒSL9ܯnumI%,I9%x?}7g^w{= 1 +3iolINO:?V_wAj[Sլ0^Zdۻ%/ѿ/_EZa]Cq,c6X[cl@ILwP/K]>*] mU42ֈk@8 i):_jkp&kkGm p`.s\}Kn{cՖ&e#@5^Fk=,m7*C115%>}3+Pw·cgt>^/WߏEml9ƆKvoqC=stlpݙUn=@+kIO ]+s01˿8`|eۏw=SGMbtzmnҪRUNm&Ec\БJmOU1m̼u0.̫HY8iclI?tIO>u?9kaqls#kHǝ22%!UkG@E8:>ku8x-sjc\%:I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JxO! 3g^/V7R/??~))I$2x/./I$PI%.%))b1IK%I$BIjMt$e:JRiSFNtIMk~kn h`ڛ#^Y @tRr>hS09Cw}3UrMmIK6<(>3@ۋ&hQԠIs֝IJp$ȍ:"I&CʰRSwGjY}hfSEJI10%2:)$D2)$$tI$H'!8 )d$I$RJ\)IK I$I&:JRI$!/ueμUQ $]WrDZ5#~7d㏳A,q±+ȫ%{KdA]k y&znd:;ֽL*mӻIM*4=4PI%)10P{cSJX~s^;A`ВӺ^^5pX3UK R-6fnsؿdYyt_JnWXS.p?1YX)]?ŻO%VY.Asy"Uf;r#Up+[ރkgmԕ/V~ωh S]=w$pV7)RD(LBJc )mLD$*p򒘐H9jJ`E8 )dI%):dRI)g&KIKBhSВZR2al%"ЈJY$H$$I%2juTRJYD (BJ\CqIJ<(%.LRRQ)R'IJ P'IK7ÇS7ÇI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$)'Aw*X3^Gg/c%kˉ>i)I$I%<?ugԮ%)$IJI$RI$I%)$IJI$RI$=j7_7 )"H~߼%JH_7 z~$^߼$!~_7 )"H~߼%JH_7 z~$^߼$!~D is I<NEY KKHplChK? $U$IJI%<08$I,}evp >g#cR2,xCJJev~=mcPBzA6߬W64CZ$I$JRJoU"k5(xia%7Lv'$FEy T*gZv\ IML.=vU )+78I)NA)\`jJJ{9iy0OÏe\Ooi$$I)䰿Y+\+_]{@WQ?3$?3(HJL(H_l#? )2H?l#? }8$ 8%?3$?3(HJL(H_l#? )2d/Qufu4JaTζ +Od̗0 i]w~ż{Dǒ< JA2?ȈRIO-2?ȈRIO-2?ȈRIO-2?ȈRIO-2?ȈRIO-2?ȈRIO-2?ȈRIO-2?K%,K$GI%<[KN3y{򮊊5Z{w[{zoH4Q <7%FU92i{_pt})2澷&_W5u9ՉwS߫-DF=ܠt^fo9G XpWդul,sa3%ů\cVG#e&Ƌ 7}ܤɊu3jΩ)ʥ+ۛ{r-q-%7Kd]mG-i`h?fb1z^ nEt>@R7m )ՙdWA`8WqWkkj q,Ϭ'P̰~@>Pg xx+PȱןĞ^gþ?Dw gd ; |]T~]/"74?^ Xvϸ<>~ӫihR,?x{c9q'SrT]-αl;l@K=:vDYuWd5+YVP\7M%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IO 96ԽlOߊ?^'%=I$g[PW/"I$2I)I'IJI% :RJRI$I$*1_SʯҎg xIJJY9rq%3SQ#D0mCN-IK {ej0u))A! xS$PҒY$T>uoޟmn&V[[<ڂG &2$E $I)I$%,S)(pp $ILJII%.I$'L%,IRI%()'%/)QI%3JT+֭in}J䪙FE:=^63*Ǝ;cIL&{!^K\S夨tD+!I$*A1'S"tl;72 >j'ܺ"V.C8}YA]U>R+y=IN5.'F+˷<9FQk+ƳĴO'#RSѽuVl!iNUTRq?gOJka"O$`PzOPmhƫmI]#p|JJm5*sRK,.%S`ID &ܙ΂JQtցgcŭcs?3uqxJ)= 5V ) E 'Dr ɯ7xT}- 9KP ^o!R{}%eޮ8#kZ \>I8?׺klh{ %8XҺڇEGz[g)*I JA"*\'M)JJ^BpRR')'ڒyIKj12DBJZe $'-N ) $)В'R:)D$ ɢʌDDA%($ )p䔱 RIJI %-))$/,o?Ez,o?EI$$I)I$JRI$I$$I)I$JRI$Ϭ}T>Ar??>߀T%;I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$/Mv5;{ dn7o?GܒI%)$IO?Mwin1ocoWNJy2[Ӥcd17̖?t$-Lj_%]:I)?KڗocoWNJy2[Ӥcd17̖?t$-Lj-=ռCH#/NX}k:ԐO3\)m}R1'碣ӎEud˜`hcE:e~w0'6O⒝d8k9~K_IM8k9~K_IM8k9~K_IM8k9~K_IM8k9~K_IM8k9~K_IM8k9~K_IM8k9~K_IM\96:k'skt?WhgO8$x+C6HK<Yݍ&NVS$ʻ#3V748Z A\կN_m@ӴRS]9iptѪRkF I)q7 vdL3^3[77I yG$Pi.?e%;i9$\TOJX$~ [tdj?3})Ӌ"~.aX3 Viu.^} q1=.WFn={x kg%=F=y,5*xW1WP8]IT2nwVsrin "_ʳHoBcrp{_ ?t$\]9Ѓgվ[D =K4;!9?}cƓ f%%=]/Sxg\5Zߙ܋˹LvTzv;WWt=Ω͗Y KΧUm./#A18Ua2x |=IeP/_as5Ėj}2IC{zE? uxĒ JJz ^oMakS oocė/!etwt0{SK:?ξ-{SVmf$qC]Cq^ɥ 8Pe[I%1k@y$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$S_ a/[?Ɀ/0y?IODI$/pse%> E I$%)$IJRE8ILHjJRbS%.R*;QOV}v8SѤMT)q I)&RS2Cs)ILORH )cx@pYӢuE %S5@IItKIJTn a: ymط;:uH=rFG"μJA2pI$RJbN:JRb1IK'I$I%)$I$IJI$)AIEH$)&M))`nKrJhM~sU\a8Sg史W{tTUL UlM%\6G< W⒚},~|J 4y+)b))6ZeD]̓h1r hۏ@>3s*h{fNZYy?9F1XC"ueaeSV-MT1ZEv=$AL%V{}SЩfCsL7淀Y=v8 ݴJJa}5JݸEK_# Re}3&_\5In{1'Wv ]W.X՝cBy > | ƹ5TVc2{E4$xϱ샹݂ʔhD"!HJ\1-$jVhq1 )Ӄf w3S1Ţ4HL A@X9G:y*CI:wS`7|xIKiSJY$e>Ԕ(R$$NR(NJZI:JY2r%.KRSaHL'IK@NҒJbRO ^RR:dJ]9)H2% I$%.8dR^Yh8@ѿpI$I$$I)I$JRI$I$$I)I$Js>|P@]X~ }Q?$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$Ϭ}T>Ar??>߀T%;I$JRI$I$$I)I$JRI$I$$I)I$JRI$z[c6n$;̪һ I%<ʫ+ʫ+DS̪һ ̪һ I%<ʫ+ʫ+DS̪һ ̪һ I%<ʫ+ʫ+DS̪һ ̪һ I%<ʫ+ʫ+DS̪һ ̪һ I%<ʫ+_Xkb] tSֽ4g$ʯS6-k |cEiSc?f3S3u~\_Uhey53c\ G_i)Ieu|rkgrۿ]uJac6r}7 Ϭ2p`t4߿gM<lPJr.SYQsqg^봜~Umw}MwLxD~#2h8 "dæ}6mQ*leq l'nknSۏ,~K86kpNĤtC^mva#H1~TzKö\RM>3\F6UVDP nqpݼ>"SjůM {S*1}Q49H;"d+E๤4#Cs\Zh?8OGyJtItmrUKe;*ռ˃{"A );Ʈc oK)1pTFM|6;"gY|۲j!I>CJp:/[''^Va@O-NڅliJ/E#RiK%%: $JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$?^'c_ a/[?⒞$IJ^3-+ٗb J}F*)˔C 0NI$LJ]JT$I)~'G )H&.͡jSeWx>))Z ZlcZ5sL-0WNЂݴ"Jai<0Q&Jk[DTh"%%-qOc qIH^R f k 븮ķ?7k9SP*xw:R $8Y^Yh?Hp0/ANٱԘR8H\/d<.ŽԹ'IINVF3 pm6T<i8+g,蒛i1LJ(f *RJS$d2JRI$I$LJRI$I$'I$RQ RSbI$R ;TC~24Jh #&wN' ޯ̈*&1kNֈ7^PƁ1q(t@)$R'H'IJ"BIGdqhoJkezUC~Һpä;Wwd9ob䌊h~}JW9A IK5 ^'DTOP"i[U(. sނҢJGDȡyN5H$HQܒTX 2I)bQME%-bRJR`n'+6Y:*$$ы/܎bdW~(:>SD3ci>PKz +9 Xs ]PsI AR%(RRL)BIS&))yI0RIKI%,$JL$mH"RR4%3LZ tt&NI$RNR)L;JIKѿp/Sh^Yӿh8 }$IJI$RI$I%)$IJI%K|;ls[ Kyhsى ~h_Ir~_awzJv~P@]W[CT:n՞>Z}$m$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$}cGPYVz66ˬk]%%=JzJo$LϽ/x?JzJo$LϽ/x?JzJo$LϽ/x?JzJo$LϽ/x?JzJo$LϽ/x?JzJo$LϽ/x?JzJo$LϽ/x?JzJo$LϽ/x?JzJo$LϽ/x?Jָ84:RŴ+sD>\c㸤I$RI$?XZqt9UbԹ%sؿu,KH!n~/R>zcatWǒ8Ѹ + ƭaLO\Te9GbvZ]%#cqc%:U.c0GwCX;~7BIYS~>ZӁ}EbG$nF[ƪ`ytp]66m948zZ-s.taѕ mM44m&;eV/qFJJv5 |v6և43'*}x?we/cTK$~:$p,vqK֍AD<*O6z^wLpv0mu,OwAa]?k@Ԕ6}>luGs ^W&Eρ!a<͙SYv*z= VkʨHoUCtF캋&pwNֺV_i>pINoTXO T0sQ`s5i.o1u[-U*$HpcgG꥙=:% I$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%<'R/??~+rm zI$JR_\_oA^μg7[PRSP'I*I:JY$RNJR)$'QIIP 䔺b%4]/ֳ :⮸v2!Qg3FZ "&V 'գG(lgѰx))% 8% :ˋ긗$O>i)%!QE%,LԾukWp@a蒙amY*˱kh?$P BRV5䝶gz:]S[\A_AxqK[=ܻī:(c*SgAPcCg"E z[ZUS]ﱌx$NtG(fxH'IJRQRIJI1I%,dI$I$$2JRI$I$'L%)<$S%,2tTRREHIK)'" TBtI %2dC 㶖^6~V@* ^yvٓ&rp߁_JwǸ•47t+wX gq%=0P:jVm}nHgZ>))P${p*-iIK]_W%.S$:JbRh(N )~eHp0߀TX'AW])I$RI$I%)$IJI$RI$I%)$IJI$RI$I%5qKs+#fa{+}$?߷oE/ȭS30~߽f"IN~RoI%83Kfa{+}$?߷oE/ȭS30~߽f"IN~RoI%83Kfa{+}$?߷oE/ȭS30~߽f"IN~RoI%83Kfa{+}$?߷oE/ȭS30~߽f"IN~RoI%83Kfa{+}$?߷oE/ȭS30~߽f"IN~V56S>h IM| |]++V.&Ymm{\AsA[ufjna$> ?$zoݗN9~89j sZxv6뙹썦N )4%IBk[kRS}Oan?Q#q:khj7@U@8pI.Vqʬh;$~DMͧOie9^f6w;L~@WA^-bF7'twd |#feD5y Ȏc_oG.cG} b+aև4>ވINWռ1mM v}]m*̊Z .sAOVtIsGDm%,ljk6uN8빮ѾDnY̢[zc?N x .eCq%$+};m`d))I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JxO! 3g^/V7R/?~))I$82x'.o'L2I)t$'LJRI$t)I$I$H&N J\90ʍ'3+Z\IUW4~HaR %mPUx[ NQ*iRRb! RS$I%)$IJLJbȐt*M JzJP{^%Kn&H꒙59;t .IJQ%:hIK'H)dI%.$RLtNRI$NI$"S"@Q9rJ]1 )U0R)$$pR<'%,%c7Jr1,/޹z,lXt(cl6)UC)8XeRQEe|SIRߗem~OVY-$( }҅nE12Tv1bNJ@"S IJI"SJJ\DSJR ' jD)$&$% ED$=CeoG F)Ve6h *2WtL[ٺ,;#;mCRX 4PͭNR2JXNTRR$NQtS$)$8LJPIIMSNOBt%)(I$I%)$IKN$ĤJΛ6TW,o?EI$$I)I$JRI$I$, RSK# [ # [)I$Js>P@]X~ }Q?$I)I$JRI$J/xcKJJd;/R;/RS;/R;/RS;/R;/RS;/S~))Ie~)~))Ie~)~))Ie~)?NKEꤲ?NKEJuYoӿE%:,ӿE7"T_"s_INK+s_O9w_/ȤQ%9w_/ȥ9w_/ȤQ%9w_/ȥ9w_/ȤQ%9w_/Ȧ;/RS;/R;/RS;/R;/RS;/S~))Ie~)~))Ie~)?NKEꤲ?NKEJuRY_JuY6'[̳Ҧ:$I$ptOȵECwGZO7;+eSގCxoWI$F=\U|n{uYoe_Y\aV+v؀Dk$wIOu lȦן%3?W.S\.Y Pǽ;uo7DxӨ> ky-9ѷsLv]sᆻ'&i$o!\ױ9~/?-pyk~FMMli7O%GUoԏ 謱l;~!i=J}I$IJ^a7s:wIKy'nkW/#}~c$>^7չ%>Umk@xt|Lr >$,ckhk 4I$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$CogK_o! 3g^/RS$I)Kq?se{2O\oAIO&N):I$&N%)$IJI$RI$I%.$RI$I%/)E$%O%."JT\S$ɔ'IK})S9Qs$JP Ӥ V$\t*o*gjl?E#EQ![5=Y>$H&rJtR`I1 )I2JRI$i:JRI$RI"N$4CTku"RR%I2JY2LJP I$:PRRT%)IK%FR@)BJb %1ږ$S DIJH .0)~R(H'IKB iBJG S%, BI) )4i%)ThNJP#ѓD5XXfEm9*hR)`cd*x/ܢ%3.H('ܢLLJIRtSaFS%1NpROJY1NJdĦ''%)$RL%.I$$S&)IJ^[h857ÇI$I%)$IJI$RI$]_cji{5I> Z~ѻM$O# [;O]\ix))tI%9X~ }Q?}TG\A°]SHx$RIJI$)1kD3W 'Nmd~Q׹;N2+ѿe_IO~aatz%/Qq%ϖvh^{.5 w}FYH1ǁIOMG&rᲷ:KD8@w>Qq<jzwx6- [5hoA|X1zJڬnkmi"X2; }hp}Lnnw?!?⻣Ll+/s5!u}M2^JuKtgdُC CL;vW@&5+?X=S.8]yvн/+dKE-%4Y_Ub}W~6{c]0{Ct%;I$JRI$I$$dI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)?/0{?XCogK_I$Rb u2DI$BJRd$I$$I$RNJY$IJI$RI$I%)(I$rҒJY:d)I$JRIJI)I'IJI$RT_Q [ zJqGA.Vn[+Z-̊Ŭug>ܰRjbE J]:d)I$JX)$JT$I)I$J]2I$I$'$J^S$%,tJb$R`%,SJiI%*RI$IIKBtIJ IyIK) LM))I)NNdlLJJYJILIIJI$RI$KjxILJR%.@0IIM%!%.JwPɬU_s;- (}]{.h>G Cn&X9Vʯ*=`3]I$RI<$))mKjJc R!FRtO )'H R$uILJbShKR䦕 %8)B[JY2 ))`$)I$IK%.$ygMś+ 6TPK $JRI$:I)1Fwμ 5ޟWU%<@]μ 5޺ԒSNI0$ ڬe]g~u#GNp7ٌ]-pw_Uu ݮZ> ķ2U-.q]WSO))I$Y8VincP;'䖢I);'͎KQ$͎I͎KQ$͎INEI%JrNEI$,}>ըi#Ab;$=6Ӯ/X|b+kw`Ԕ$vKw`Ԕ$vKw`Ԕ$vKw`Ԕ$vKw`Ԕ$vKw`Ԕ$vKw`Ԕ$vKw`Ԕ$vKw`Ԕ$vKw`Ԕ$vKw`Ԕ$vKw`Ԕ$vKw`Ԕ$vKw`Ԕ$vKw`Ԕ$vKw`Ԕ$vKw`Ԕ$vKw`Ԕ$vKw`Ԕ$vKw`Ԕ$vKw`Ԕ$vKw`Ԕ$vKw`Ԕ$vKw`Ԕ$^mBL)I$%<߮rUg+3'00? IJY?Y~ce[dKI% nVIeLkYZ`%|$A.A, D8ޟgJ]:OnXW:!9g!xJz3($ScG!x`}];c3HtJ7]b]~qJw8;evx4es/Ӻspw;$-ޝ]㲼Zgß$I$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%<'R/??~+rm zI$JR_\_oA^μg7[PRSiLI$R$$&S!A%.d)dIRI$I%)$IJI$RHIJN:JY˘,F XX ̦ r3 AEA%.2A%.I$(JbR p)LuIKI)I'IJHTIK‰R=g%/)S' )t$RLJ^S$%(%I:JQ0HR<%2NNRR'LR IK2Nx *IILS8)$BO BJRp'IJI:I)RkR RRBdL I$ą!M(IL%s,mbg]%$E.C%.Bb) N JPR8 R&%9Q))RSRĥ O )`H)`4((<*)NR'IK&)$)$R )R^Yf8"ͿpI$I$$I)9w[J'+˿WXI$,߬56#V.IM/}n/Џan$I)I$JRI$I$$I)J/`{K]" I)?Ο}Js?OD>z_s{iޟnt#w$S7:!?M$Ο}OB>zI%9s{/Џ޴INgKntCw4S7:!?M$Ο}OD>zI%9s{7Zi$3ntCw7:!֚I)?Ο}Js?OD>z_s{iޗZi$3nt#w7:!֚I)?Ο}Js?OD>z_s{iޗGZi$3ntCw7:!֚I)?Ο}Js?OD>z_s{iޗZi$3ntCw7:!֚I)?Ο}JE^3U45RI%)Sfú>'@*GBasZ蝱:k}N}C͏'4 q`̜/߷oE;>a1۬0f oE%;PuYu_ m:κ-ꕺKϤkpr[=GZZȐu0[gWgk[]$@JjjL}x5tVEuxy7ObbQwWeXc .oyt}*ɰu&Zmu%6T.w ' p,kY?s^y- OAIMofqߋ[wBqa:AH#sĐUΡ*Ϡc<}|AIMOOy|ΟzV)m} ="rߜ dqJr>~1p ;;OS="3qFѤxO7.PC^ $V.WP.$FUlu~Z Գ 4VfN3[\g2OM}st:ymlչ66RNRZ˚b~<7'NwX.g8)Ν҅7 mOڞVH6UCNGtcn.@idu%<9o^{m |~RUNGT,;A}?S˜Lt

e-y0OIN^tW' ] ;u=ϭp ea}Ux=Q| a%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$CogK_o! 3g^/RS$I)Kq?se{2O\oAIO&N)$IKS)Q<%.0I%.$RL%)$IJI$RI$HI$$ $JY$IKQNN8IKI)b@T°]?mgd:!NK+.,+~9޾kٚ})TSC J]$I)tI%)1Ne)NJ\$I)ba:b%8$R$%*SМhSI%()A"J]$IJHJbxHHR%,9H$5I %1!-I$LJIJNQqIKN:I)d铄'I)IJLSbRSTBbLlIK;$IJLS))M $`sL()) )TQrJSQZPBt$1Ӕ#aL^JJdΊ-Q&ED @9%1tpJx & JJ=D#b/( R J!%2BRR%BSJQI2pI IK/.o?Ez^[f8 }$IJI$RI$Ruo+IJI$R_wY))??-Յ??-ԔI%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$Ukp(vC-lh9Jl_I.wzcF%=K%=1ѿIOD?7 LoS$_I.wzcF%=K%=1ѿIOD?7 LoS$_I.wzcF%=K%=1ѿIOD?7 LoS$Һ=Qems\:$M2u[}x-a#$;E!ϴgS羥ѳ֞^WBI$$r}wfu8\vHkdIIOVORq涺TgL7N$Ku}t$0?.B[f[@dwSm2n\O05>ίl kH2߂JzdpwgRFgmǧ>~[miAX}M{cG"?J%]C"f;6\ jcǞJse0!S,ӘY؞O$J0Kݴ,{R4]ltl ~))X _mym t& IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%<'R/??~+rm zI$JRO\oA^̼g7[PRS艓E d)I$I%)$IJI$RI$I%)$IJL$\'LRR:`JRI'IK&)ʉIJਧILӨI%.`%9rX,owNEK~+kuCB] @0:Seru]V?}7Wp'I.JdLLJ^S$JXSRR&NI$$I)tIL$$ɒRI%)1NS:IRI:JY$$I:JPNNI%( dJdd%)%ENu@y%1:@LS0$'JZ)RJRRҘH LTRDINJ]$$I$Q!M(ILRO BJbRNBd*A%1!2$RSS 8IKRR6ܤd]1%#IH)IH RPd4)Jc !%1N(IK&SILBޛ6TWΝFTPK $JRI$&5(_l#? ecK"GKڒ}Jr.KSKihIJI$R_wY))??-OL\\] wS$W'}/W'}%="KwwS$W'}/W'}%="KwwS$W'}/W'}%="KwwS$W'}/W'}%="KwwS$W'}/W'}%="KwwS$W'}/W'}%="KwwSѓ}aFEOäLKhADŽ>e㺚X3ۺSLedI]HxN)rSuP;Z>IN?KV o+I$?KV o+I$?KV o+I$?KV o+I$?KV o+I$?KV o+I$?KV o+I$?KV o+I$?KV o+I$?KV o+I$?KV o+I$?Dykk' $,Og{\֮=&c3s[%ڴkےSσK;]W<ܾҊN\Ml |<]%Ms,>Jkuc[lk dM{mkTpK 6k׸II>]8Ǿ@(W]e}? htމZ>G ug0Ai~A?@~)Eu'^kÁzJyʺ:-oa+D]4tP8sϟ?C, "Xq9d;#~_}rxv>cI$w?q3)ȼC58쒝404ORX?ݪ_C-Uꧫ:ԖRS:z&pZ+3tt9۬yi$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$rm z96ԽlOߊJz$I%)x'.of^3-))P )$JRA2pI$$I)I$JRI$I$&NJPN:JY$IJLS))p`%.JH)t0N j!I%.QHcYgUtVXPSs˚ X8ȶiKO#,}\;GgR\8INd)8JRQ RN $SS:xJR'R$JP 6A)IJ!$IJI$R'LI$I$$'IKJIIJLddIK[$LRLJJY$N 2I)yM)$:R)JR)Jd%.$)d<'NI$$I)I$RR2t$BPBpI)I %- :d.LR&%)IJ)N%. )IKjxIL!(S$S(I%,BR%D)IL@'=RS%}7mÇR7lÇI$I%)$IO'+˿WX?_]VRĔI%)Em-xu I$_UԿgb֫ $_UԿgb֫ $_UԿgb֫ $_UԿgb֫ $_UԿgb֫ $_UԿgb֫ $_UԿgb֫ $_UԿgb֫ $_UԿgb֫ $_UԿgb֫ $_UԿgb֫ $_UԿgb֫ $_UԿgb֫ $3tZr],c,5 kN=N²lntj=úJxVKt.z~+j's%s=#Sk _ ȴ?Пؒ%f?/'$IsٿOK{7 ;)\owBbJzt1=ĿПؒ%f?/'$IsٿOK{7 ;)\owBbJzt1=ĿПؒ%f?/'$IsٿOK{7 ;)\owBbJzt1=ĿПؒ%f?/'$IsٿOK{7 ;)\owBbJmdEWKf^d3=ĿПؒ%f?/'$IsٿOK{7 ;)\7_k*4N$I%<Cѻk~X.r߮Ԥ$I)IWM]H{}%$Nn^D3;Zl>.}n=$e%uEΪ) QV;\ws \Ǐ%=?U.% Xct`s=ˉq;>zx^C_mYx:07MN)Is˙f]c"I@7լk˻mK\uJzD7ǧղjfaa44_IOdK}?hkll Exl5q68INKgZS2qH")d^]&mUZSҦ\WʱXLIqJ'V1e> N%=RK:̓].]o vc|>zI.Uw6cqwmc˟ff>=CsǸ7ĸ?rJzo^tx}r0KSqνcsst2@tZ3u>= Ή)XUSidž#Yqu./!>&OZlm5R^%="u[Ys̀KNþB:{v;Pt{iJlI)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)?/0y?XCogK_I$R e82FLSIR%)$$$JRt$$JRI$2I)I$JRdJRdA%)$IKJI$tRA%()RBtRUcj!4VBՄ~R[֟q5Wa u7}ĩJE H )RSrBtIJ!D"Jb R%0p7RRL t铤$I)I$JY$IJI$)I$%):d)pIRFRI%)$(IJN$dd$I$$I)I$JRdd A%-‰S*))@) )II%)$IJLI)tI%)2I$'$LR$RR4%$$IK)%4NJe $qC%/)J^RI$ɪaA%.:IL8P&%/ mɜĹ4AHT$P)(6TFtY_xI$RI$I%<ԯ.)]b9w[JRI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJQ{`=I)FB_c?$?c? }l4#$l4%:?ѳЌJC:?ѳЗFB2I)FB_c?C!&<|Rc? }l4*L@WR愾G6IH~G6hOxǩJtz垟'w ?c? }l4#$l4%:?ѳЌJC:?ѳЗFB2I)FB_c?$?c? }l4#*9yё];Y}IM愾G6IH~G6hFI%!hKtg$tg/dR愾G6IH~G6hFI%"n-,;Ƃ;RI%)FNJ^$3h94>i{Kf&'%<_^uO#i{3 ~ODy>_4 ,oʪ}ENi_$O%%2\6CnZ#N4%q3UvEZ<ʒg8o ΑџZl@OP#o+c+]`8D{:,˺ǺOU _TKC:̑-F}Sk.0XUPXդIqYY?Z; ]w&eV.pZ'n#96g&[f]8kзOŭ>%ĸ*}@$4y O+L8܋/dĕwt5:n{GIN.VX["d|a8 \Rޡܗ>Cm>3gnۼ11xIN0E|Ӎ[_D7* s~J!*Xv8˝>(}c _kt)oN[}4KpU-&HhVWp9^*JP7IĤ;yYPL48p KT9|ܯt^:M&丸T:Fu6ʌݨ'YINgLci69ELXC ju6c5Օc)'VG8%5pGL9\w,,u#᏶l:@ohj~.Akicls|RRh֟Vn]ֵ'o>#VH=Vzmk7O꾓`A%9y= ܦK`8UzNNseZO>A#.ʘ{  C*za/i/ܽܤ3uz̬.lűj?K`~5ֽiv|ϊ>WqɢSi2KuǷINWP8[=iխDnbST:fmw+u%.I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$?^'c_ a/[?⒞$IJ^3-+ٗb J}RI:(Y$J})d1 )I$IR$JRI$I$$I)I$JRI$ BI$BPI)P$JEZs&<z7GMu J\' )pIJL R s]#&04쒜l68SRRRQjJX ' )S L$a1)?E%.\yP5d(ln4RI$RC~VuSIKL%)$IJI$R$JRt$RHH$&)p:J^$$:JXR%($ВIR'RВt%,$%,d)bTIJ AD)IKI)dI%.I$I$N%2H( R%ANJIKJtR%,2Jd PqI$.Ԥ NJ`BxNBP LRS&)5I%2P%9(d S'%,] %ɜe2J])LJ^R%.$4N^[f8%7mÇI$I%)$$o+^úS?xS-Og%=bKooS$?g/g%=bKooS$?g/g%=bKooS$?g/g%=bKooS$?g/g%=bKooS$?g/g%=bKooS$?g/g%=bKooS$?g/g%=bKooS$?g/g%=bKooS$?g/g%=bKoYs)~%=2I$zX%~ qr[IdhINETOڟʯ&)I$Jr><_ 3}(iֳs+;냍# Ĩh?%2g_9仧XZ?*:v&U~CDbw\ǭWPkD#J(cauHy]{a~F3[A>>t>2o![?e V<}gg3-C'( >2/ ismw' $6 Kc-`ԤTQ@i6S]kqeU3Ծj#;hi=stK3̌w쾾<%4}[ rw-I[yL̥^$.vt?Ot]L 5 kIII$I%)$IJI$RI$I%!ˠRZ-_t1$%y$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$S_ a/[?Ɀ/0y?IODI$/q?qe%>$pJX4$LD$$I)II%/ DNꔧI%(Bt2tRE%/)&NI2I)RI$' pI$RI$Nᢓ?6}Ĩ)JftH$<'I%()&N&K`I$RI%)3'PzJ`(0$IIKI)I&NI$Ĥ' *A%.:dNRRM)JI)I$Jd$铤$I)tL2I)I$JT%1)R`R'I%,TTIK2I)tJtI&IJI$RI:JY$RT!%1JRI%.$ )&N$RИRRL tO r䔱I2pRRAӤJTIIJqP.IA%2.M)% )d$ ) J<'I%,I$YEywMś?A/$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$.Guo7Y\\SLo/-Ƕ⽎dma{BI%5zv]tnOIO=vWZc=v%Z_ikz- ]$cuRPwߺ#VzOMLܳ! am\mǒJyVUqk>Wﯨ}`sj.-~qpj$ ΫE%9}_3 CK ۧY_bV1uh&4Xt$"ܟ=Gg> SXM$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IO 96ԽlOߊ?^'%=I$g7[PW/( I"$I)I$JRb2JbrJY$RJQTRRdR$%.I$&)$Be$ВI$R% $ԁPN Je)C% IIB @&i [JQL)S0`JT% H5%0IH)pA:JY$IJI$R8JtR"!E2 BJRI$I$$I)D2JRQ I)I'I%,I%,xJRI"HJX'NI)dJtI$R)'I%('I$%))I&R $RH% R'I%,2I)I$JRI$I$'! R`I$N I)(n)m)v& pBu-JQ)LR ))BSJ\S%)2tIKI$&IJI$R&I%)$IJ^]f87lÇI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IO 96ԽlOߊ?^'%=I$g7[PW/2I$I$$I)I$JT% $Jb$:I)dI)R$)dҜВ)JI$JpS'IKA%( [䔱)D%*Rڒ mH R@BJ 5 )@)NR')p%,B)xhpR@S4PjILXuRI$I%)$IL,CEpILSJ]$IJI$RR҄)@'% qH$(II%(56@!EIL!(R %#!2!P!%,)҄(H'IK&NJJ^S$J]$IKJRdR)rTe"%.A<$JRI$I$R$JRI'IJL2JRI' )P)LɂJ]$IK$RLI)A:`a)IJD&NJIKiI(IJI$RI$I%)$IJI$R铦IJI$RtYEywMś(%I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$S_ a/[?Ɀ/0y?IODI$/pse%>I"$I)I$JJe) )d2J\S9ɒRI$IKN%):dRR))dI%)$$-ТJ\@N )ANIKI'IJ IE%2)0'pIH:IR$$RR;K1SrJd TBpRS$RI$I%) "(X1H$I$JRt:JY$N%.$JJbJ@R )SjSS2a"PݺRS(NSʌ.RIJJY1)8L0*A%)$$M)R%)%)$IJL<$ %,I$$t$$I)I'IJL$tS RLJJ]((%$RI$dIK)9$dIJNe$ *iILRI:JY$%)$I:JY$RI$I%)ywMś+WtY_xI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%<'R/??~+rm zI$JR~GCٕ-moRΓs좧8IcI?8IOWw?)Ww?+?b`jr_0?5?%>W_ȥ_ȯTq~m_;Gۃ"gһ߱0?5?/ؘmܒ)fpSWw?+b`jr_0?5?%>UȤg;꿱0?5?/ؘmܒ(bpS_w?+տb`jr_0?5?%>SȥȯVq~mO;5Wۃ";5Wۃ"[&Ƨo%SmS?q]n_q]noؘmܗLOIO_w?)د0?5?/ؘmܒ(bpRbpWmb`jrJ|+K+^Smq)v+E/v+EzLOK&Ƨo$?ׯ׫0?5?/ؘmܒ(^pR^pWmb`jrJ|+K+^Smq)vjE/vEzLOK&Ƨo$ʿګ;qnOؘmܗLOIOPE1Gۃ"S&Ƨo%SmSqn_qnOؘmܗLOIO_w?)_w?+?b`jr_0?5?%>W_ȥ_ȯTq~mQUHpTٯ0?5?/ؘmܒ)fpRjpW~mb`jrJ|w?)ܫ;꟱0?5?/ؘmܒ+rpRrpW~mb`jrJ|ʿ#Kʿ#^Smq)w*E3G;꿱0?5?/ؘmܒ'?^pR^pW~mb`jrJ|+M_ȯWq~m@Wۃ"+^Smq)vjE/vjEzLOK&Ƨo$٫;ٯ߱0?5?/ؘmܒ)fpRfpW~mb`jrJ|+O_ȯVq~mO;UWۃ";5Wۃ"[&Ƨo%SmS+K+^Smq)v+E7:Wۃ"c&Ƨo%SmSWq]nvjEzLOK&Ƨo$ٯٯ߱0?5?/ؘmܒ'?^i]n_Wq]nؘmܗLOIO_w?)د0?5?/ؘmܒ(bpR^pWmb`jrJ|z+K+^Smq)uE/uEzLOK&Ƨo$ׯׯ0?5?/ؘmܒ(^pRbpWmb`jrJ|+K+^Smq)v+E/v*EzLOK&Ƨo$٫٫߱0?5?/ؘmܒ)fpRfpW~mb`jrJ|+K+^Smq)vjE/vjEzLOK&Ƨo$٫ٯ߱0?5?/ؘmܒ)fpRfpW~mb`jrJ|+LƽȯWq~m?:Wۃ"Wۃ"c&Ƨo%SmSUq]n_Wq]nؘmܗLOIOWw?)ٯ߱0?5?/ؘmܒ)fpRfpW~mb`jrJ|+K+^Smq)v*E/uEzLOK&Ƨo$G٫;ٯ߱0?5?/ؘmܒ)fpS_w?+b`jr_0?5?%>OȩPEzLOK&Ƨo$O٨Gۃ"+^Smq)uE/uEzLOK&Ƨo$?ثث0?5?/ؘmܒ(bpR^pWmb`jrJ|z+Kz+^Smq)uE/uEzLOK&Ƨo$?ׯ}`αk*:&`L0?5?ϫ"n 4OJJy/z7+҉ѿ_]_ٯ"ٯ"S+_to9_WW4/kȥ47kȤǫ2(=]WE>pq)>pq))?r̯J%WECeQuO_f_O_fJyOz7+҉ѿ_]_ٯ"ٯ"S+_t_9_WW4/kȥ47kȤǫ2(=]WE>pq)>pq))?r̯J%WFCeQuO_f_O_fJyOz/+҉ѿ_]_ٯ"ٯ"S+_to9_WW47kȥ4/kȤǫ2(=]WE>pq)>pq))?r̯J%WFCeQuO_f_O_fJyOz7+҉ѿ_]_ٯ"ٯ"S+_t_9_WW4/kȥ4/kȤǫ2(=]WE>pq)>pq))?r̯J%WFCeQuO_f_O_fJyOz7+҉ѿ_]_ٯ"ٯ"S+_to9_WW47kȥ47kȤǫ2(=]WE>pq)>pq))?r̯J%WFCeQuO_f_O_fJyOz7+҉ѿ_]_ٯ"ٯ"S+_to9_WW47kȥ47kȤǫ2(=]WE>pq)>pq))?r̯J%WFCeQuO_f_O_fJyOz7+҉_]_ٯ"ٯ"S+_t_9_WW47kȥ47kȤǫ2(=]WE>pq)>pq))?r̯J%WECeQuO_f_O_fJyOz/+҉_]_ٯ"ٯ"S+_to9_WW4/kȥ4/kȤǫ2(=]WE>pq)>pq))?r̯J%WECeQuO_f_O_fJyOz7+҉ѿ_]_ٯ"ٯ"S+_to9_WW4/kȥ4/kȤǫ2(=]WE>pq)>pq))?r̯J%WFCeQuO_f_O_fJyOz7+҉_]_ٯ"ٯ"S+_to9_WW4/kȥ47kȤǫ2(=]WE>pq)>pq))?r̯J%WFCeQuO_f_O_fJyOz7+҉_]_ٯ"ٯ"S+_t_9_WW47kȥ47kȤǫ2(=]WE>pq)>pq))?r̯J%WFCeQuO_f_O_fJyOz7+҉_]_ٯ"ٯ"S+_to9_WW47kȥ47kȤǫ2(=]WE>pq)>pq))?r̯J%WFCeQuO_f_O_fJyOz7+҉ѿ_]_ٯ"ٯ"S+_to9_WW47kȥ47kȤǫ2(=]WE>pq)>pq))?r̯J%WFCeQuO_f_O_fJyOz/+҉ѿ_]_ٯ"ٯ"S+_t_9_WW4/kȥ47kȤǫ2(=]WE>pq)>pq))?r̯J%WFCeQuO_f_O_fJyOz7+҉ѿ_]_ٯ"ٯ"S+_to9_WW4/kȥ47kȤǫ2(=]WE>pq)>pq))?r̯J%WFCeQuO_f_O_fJyOz7+҉ѿ_]_ٯ"ٯ"S+_to9_WW47kȥ47kȤǫ2(=]WE>pq)>pq))?r̯J%WFCeQuO_f_O_fJyOz7+҉ѿ_]_ٯ"ٯ"S+_to9_WW47kȥ47kȤǫ2(=]WE>pq)>pq))?r̯J%WFCeQuO_f_O_fJyOz7+҉ѿ_]_ٯ"ٯ"S+_to9_WW47kȥ47kȤǫ2(=]WE>pq)>pq))}fĮ-kư6p׻z_O_fƫC]mѭh hJJI$$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%?U$UI%? endstream endobj 81 0 obj <> endobj 82 0 obj <> endobj 83 0 obj <> endobj 84 0 obj <> endobj 85 0 obj <>/Font<>/ProcSet[/PDF/Text/ImageB]/XObject<>>> endobj 86 0 obj <>stream hޔZێ}߯;ټg(-"CP˘&9-ۀCzTool0JŮJtx^on<~?⾠<8u?4l?cXv 4i{Nޖt3Ujp=t7<ΜRPY+b4PΣ)UcW?V]˯;݁̀H˝#sھ~量کk ]@vh;MCw=-ѣ?m\NhlE8wC5 `m<;G'-'a%@dD| e-au#"`6s0*vaENE_qK4rE0ҩI7dM;keW(_N(Ά7nmyQ MemHq#SѦ7ׄ1++a~.sR+We!­"4X|[}W# hG!ɜOx+:'vyۍ6}#k$aX@kNJ vGxSI;hC9%ᎤdrOUr Gz`;\:456= !y!X>ݩ?yC ABd{eGp +kkexU-μ|qXx";Џ?l;dZU @^E݉A;~g/SbVkŸ̚ɮ/lyB.A= j E86 ZSE)v<gUzR%֙`Bbg۲LzG65ǽOgMzxDs%IPKxe ][S~&8:%,l|6AϢB*-,k#;.twR@h%]F1$hjt.Ж( -6y6+6CBrk|iD3q#Yqr #HJvIм|)dΡ錓^3EC*얇._4e1;\VJC5rA83NڸV n?W5,aqàJ5Ae|0kPݜ^jK3 EmZ|Zſ Jq1x>Q+v`&i\(5D%F=B@( Nb0r3xUͪ`A([L%\1&Yo$r񗋣{^zTRVnl!‚^{Le&髌!׫ԵZnES>:+v.hzSRyI "+r5$Azk` $00=rUwQfhqMWTJRˋ_p.)w h3 /(ҕd ANn*焴5#}Lrxi@T׷ %`K-Rs֦8)mG*G7-Z*B绀 S&K)a(;@nvsUup)j2&*c=Dp3Jp⬰([%7]pё 1tPq_ˑ|5o^iL[nik9>1>3 V<.N3 ؅,snr^|ūVzįe h{&Ӝ; ㋕j( gRM38Bj։,.gIN͟%t+AۯriWǒ2UZ Vyhe\+6T~.6DVK4s=`0|oe u4!Q{44JDfn#u4SD<7]~| B) iڨBeg`Y~l; tE(|<[)ւ:{nI ˹*dVB7B=I2p{/yB9G|ϒQ*(@v]fjfMJOO[[::jrn[ О}5`M3 On@`J+~KrJ_}9}SYg_g3*b3uQ!ءs̡lrgÐ*O#]۸S AOv20ՈC1u%`{az'&9fLTyz#̠f=M#S1/+; {9zJ;ڶ;[ rǣjbE*ҍeĶ׃ishX^)XljY3y\xG]F0X> endobj 88 0 obj <>/Font<>/ProcSet[/PDF/Text/ImageB]/XObject<>>> endobj 89 0 obj <>stream hZ[sۺ~#4`,3kw\;u$}`lѩ%|2 )JRq$X췗owtr;/-.ȅmZ#%vUm Q`PKۖp[#iQN0?P+]Yp!²`R!]jMoP mF1V!/jܣm*:BmjdjH.׆(GT7urrzn儸 9SExDb0y|d!ft8BiaCN6B9M{͒09 "Sm>#YL Y ¨G6/_)< sx$hB,Hup5$Qv 9d~#@rHMRH0(sA#(Ni%'EidSjr}QRV;;ʆ%B]P Fa8򊊴aBuM,7&tʹV1dHy}fǂ-§5p ߾E-c~0 k~Cl foy|UPrhv 6 m/a t N{ȂnV|(r2 (ٷ)-fy~};ODĶDaGsz$edM&bo6pPS[s;kB` q\kjp$rK1{$K\}=e>}V>#ԇJ}{ߔwrI}{Q_=5TJ+:2t\@pi֌\q*N'W@XHϗDW}yLRm4+T?`edF+Q=nߕ['WC}Ѩp%Wy+?l9Kؾp1 G55k &We =ByUW)8zQ4o:t99RkBss=d{:˂3sezzݹsyNd\]w8߭ |/ 뇋p D+l?ytr<)ckp퀌MÈB=f_ Z8={[̓a8}/<9ŗ4><wNE~WB L=ay8& $<ӫγ O+Zlʙ捧,ޠq& A,y|ɞѯi.*9wEovȞ60 qTVո7蠺jVMd7Tt^*&_T!E m~T:]F˅^i\C:(BIęJRTGmOa1GJ_Zq endstream endobj 90 0 obj <> endobj 91 0 obj <> endobj 92 0 obj <> endobj 93 0 obj <> endobj 94 0 obj <> endobj 95 0 obj <> endobj 96 0 obj <> endobj 97 0 obj <> endobj 98 0 obj <> endobj 99 0 obj <> endobj 100 0 obj <> endobj 101 0 obj <> endobj 102 0 obj <>/Font<>/ProcSet[/PDF/Text/ImageB]/XObject<>>> endobj 103 0 obj <>stream hޜZ[~_1Lj[lY.%g-vޜK7 ݵ䂁>|7?"Vw͛wf%Vw!bfbZ&Y$&W\UͧT}(UYtwoR*u++㵁:ʢ!\ҕѡkvB˨t},NCVoE-D;ow7""SCz7 !@љNSpcҨ2zh3+$'=4 XD{bmǁ`G9U~hPlO ת>sXE;٪Xd.oމ;\E 7p=9܀ SOy) +wo-d$^(pvcef9oB%(#xxe>'ΕQS$dX۵&d$,i!L9rMDgpwg*6'F+'u,b%&EStۛ=fsJyo$R0o[%Yn<ܳM  yKe"AE #~#4?su`J B8L2:덁x9LtꜫSޞzD0I "-U6r_FqNQV 1K$*E#mꁣʀ$_l2NB,.h6B}po[QVP冖@xP@h-^>vyl}\ 1/1j~Y F'p9H d7RSP]>Ie3ҫҰhu{3où wLP :vxV( ; ch`KXqZCFt[>tӜ E]S@p>ztc7>.̤ -Ut_Xe(&9 S IzO8jGTbXLzy ;I?oYUDh̨!:B7q@Ǘ`/E 4HC@S'_`^ \XTJEbo(/c bZn%@J ,>@eJ`KKp8_ТUTv{ ;u)Bu>gC9^K26ٺCB?xy3 #('9nOEȹ ̔P~"%Bi!@[$= Jq Я~2" { UNn7 ?qb*5 AlKzoJ#S$!᭎?@x. ,5v]g—hsaMw-̤j RS+lm2H59]ExbUĈ*:=/r# :Jk[J0W{HAv-[/A+S bo/sH&Q hR2M2+JDKɞ;kbcqZF'B"yp6p[KfƗzpկ#HqИE+K ŒzLbC8(OGLExOr}P|o}q&.>ĹASy__qFuj4gz~ݲcN=CȘqU: |:ɞk% Mg'lyhv/=}a/aa,p`p+@/ L!CMR^?%. >7Gpǀ5Zࠃ)Cg\Kf U3!jFvx!Ƿ09G4DPk7+'1d-0?JG${|IܽZ5I˭1i~vCư-3UÙ񒈽6UH׽q ͑c"5ouTRM ngCi @ 0%-`T*ucB'),S6;ml@1f2 X` ߷:M .$|?0m"s^8 jό4HP>mH4_} Ay2ކ2ɵ 2tA !W)Kx?3+Gwk 7r-3uztW꥓zXS¨\Lk!i!1~DT덦Ͽto~g5}ě/طesC2 !EyG{'UfjVTee eLĴ79Gy&dN`=/ endstream endobj 104 0 obj <> endobj 105 0 obj <> endobj 106 0 obj <> endobj 107 0 obj <> endobj 108 0 obj <> endobj 109 0 obj <> endobj 110 0 obj <> endobj 111 0 obj <> endobj 112 0 obj <> endobj 113 0 obj <> endobj 114 0 obj <> endobj 115 0 obj <> endobj 116 0 obj <> endobj 117 0 obj <> endobj 118 0 obj <> endobj 119 0 obj <> endobj 120 0 obj <> endobj 121 0 obj <> endobj 122 0 obj <> endobj 123 0 obj <> endobj 124 0 obj <> endobj 125 0 obj <> endobj 126 0 obj <> endobj 127 0 obj <> endobj 128 0 obj <> endobj 129 0 obj <> endobj 130 0 obj <> endobj 131 0 obj <> endobj 132 0 obj <> endobj 133 0 obj <> endobj 134 0 obj <> endobj 135 0 obj <> endobj 136 0 obj <> endobj 137 0 obj <> endobj 138 0 obj <>/Font<>/ProcSet[/PDF/Text/ImageB]/XObject<>>> endobj 139 0 obj <>stream hޜZے8}#kD}޺=їzEQڼ$؟}E '|0|mQRaj᯿Ve<*6y}{+x.8O;n`4`w%ѡ Cq.P>Lv[Iѓ<,XKI`v}k;l14 ]<7 >ɢ&"nUIDx, L *;ھ<L5?L*Fup7YprvQppoV%-LνYo<݄Fj&ȊMVFaVnTe St37{2Kyq ~W2b^a]qĪijcdy2a~?ٚ]zbkېLautaprUeHd$F 88 խixD'7ӑ.c2IOWǙ܁ =$MH[']ھW`~OW9fi:u(1H 'A& /{7mU4u5a7*Ƙ m.q{%+x1'C3G7242GY27I(_ԏc=JQ# Bd(QrD\(9"?-(" j(GlտehwF?7ElyYaNnlgx01dI~2 cFICErڊ$ KAOV|u`gk(KT)3ߐm"lmʜZ)p;թ]ώN≁ޘ:)\i򏋠4_"P쇺6:pоɟӘT(}A~r/Ρ  u\-I$Aٓݓ mE@ iljI0FOuekK|2gvd CbGᗆA6&) vk2 F۞K CیRu{^88-v&!s *ِqe!j2*l &wk6OwS}Đte46Lx#VnUJƓ+X? FEԟF>UNQ(IHZ*cv Z E;UMAE)ax@)YJISӪe@R>(,rG*+Mjd^9/rZL`L5=5grcȠ? 0u(i꧐ ۺ}Q@_/6`+ӹ(!idU}ߑuNPw`ֻ tud^Qv˒C0/Tm !+lqGTs{%,7)0R%# CE0G*W(LL'^^\u'#U^̾>]oYGj"^g#41{ ỉ6rd*G;SCeDv x'x&frK( d%KV:hQSSˤ3>{ E_\4d:fyxy`D)zM;ZQ1Sy^I=IKiKJ?$1:Ph_Y#ԔT.|dY\ i-K/c|%/zgY6ת2_ͧ ~ݜ,Q6!k Ӣ$pV(@&)V"&r߄g]R(Kғ8XToU$XT΢9 Aٙ6ˆNVvz% gK躓)l% UM$e%ݓ .r͑{@G5;"/Za`_$q]Qd{8`:E*=1I 0en**&_0ylڵ'S}r%0L=@B1Ǎ#0vRc?Ѧ[0!Eb~ic "2;-eE%Tq$&Z#~"35|WUF&*)bQ/[1O9]e:T=*ʯ ʋ*eysAx~V}Qȁ6m&ŊϲPgTuϲЂzL}'Z%ק̗ *\*X1.X({.^3;s[HZ8T3"HGrP̳袃K5N)(tp0Aٷ7EśXq06B5)=/ r\]?ҢX$T?__W9AZn.TDӯ(PH\Uff5ԫ!([IM΍mLBneZ:/_t[s|_8XwG;Yrm#$Lw6#<8# w5G"F ܓٹ@;yP?ՍS H?%n.PI6H\({&)hq3%`0J;|ĝ(bK', Li |F1qceڤ]h_Asǿ)T3^= diQ5TqǼ?>쓗[g ʿ\IR~^<,Mj[鿖ދBG"G]]R0fX`sɧ|zE5̩y=6c(rLHZyI3~Z (I_W}<Ùr<KՑ[ZM[SBώjZ"AA0DSIŐ^wt j1.`hGq9"XѰ?">Ov $E?LBg+`lpn@^Jy|d6ͨP_ 1~sLJ%N̋/?;J?RX\3tR} Ert} p93K_ 4Y}RB-N)fv%yI}n;,:T;kWQݨBV0I"G gMm')<3,'\1s3s."٩(1n"4}Ο89؝sD'.VsPҲ;6HȅˎM7R!^kuT ˥Eq&4tFo'f'u\14jt69G7d[?Me?ɩvaǬbr-d#qH/0` ucovUxڬ n]EMF>s?iHeD*EVSHכCM F #9VQ2ySo~_24tIF R#wwR\pq(Q*0bohYo/Gw 5cs!yOdoKy6))]POث\?[n%!mN1ҢHAJëꊿ5X݉@Gj*#0ʑi+̡BEsM# endstream endobj 140 0 obj <> endobj 141 0 obj <> endobj 142 0 obj <> endobj 143 0 obj <> endobj 144 0 obj <> endobj 145 0 obj <> endobj 146 0 obj <>/Border[0 0 0]/Rect[196.271 387.95 377.235 399.401]/Subtype/Link/Type/Annot>> endobj 147 0 obj <>/Font<>/ProcSet[/PDF/Text/ImageB]/XObject<>>> endobj 148 0 obj <>stream hތY[s~G=i@0pIyge3kZ-ͮׯg?~ptq}tv.zu$e'\e* EA9 ~?$4cGOI eSTcծ&%RoЊ"b[a?qG {gecqC?rVv0ո1=Ũۮ6?_CS( umFRK5 w-=dDz˩F}| ?-leB`ƪ"=@{gF$jMr7$E_ %IE2Ƃn2qCNEZT5wphd84̰Ἕz"am/e_̉2lQQdyD~|C2Ժfq`H%毩4 "jGӷ$#j '\ gȩԫ󾌽iLMg '=$1<> ?Y]_^Y ۣDA(^¿$B-D*YhET vAȏZ8qB8=AGt fvJsؿR\#mآ0PaٰEJƔVC<qH\Q(QUJ5DiBMRF%NJ(S:mwOIAR2U$V/m>t7wU7 f1)wM5RJEFNPH3M CY!#rj@ip|/ew<07۾[NDz2=w]}4z s^2XuEF56p[St kqQك]wU#Kxh]q0-{0E)T7 7꽩k*70s*,u_vkaW5liNBoMm:7"{t7w,i+N83GMaѩ7>Ʀc.D'J:>6}ʃj@B{t'4\zj-Wce_mna{(!` ֜| ;8j% 8LѲNBbMo7]ݭw'lrxaRlV%YJ]v@톨MRE {ʰb4fNƌY }ǟ-Iqx5rٚsE(M" 7]zEjdèx4r>z4Lm{w*R@9Gfy@Ԯq켽Oɩ7g},8)jS&&{K0śoq>[o&`qmtEAŅk*& Vr)N,ɶ8Y$xVj`Y ewG SEʥn qxdᡃ%ᨫtH(#v> ubKbOH|ZImU"IL|6 *%>NC*Q3]eO.3bzYj"7@D&̌<ĔI30\jQ b NdRbh 8"k4Ga3E/(30STyFZMeXli6ED퉚k;ӸD{%S{tPRZ4k"Rnuuk4v*pn(غ9Nմ$tbGx8ߙ@rb!s4fvfv}spTɱ- HF<ӢDJʁ%JR&9! scLԑݯ{]Fߑ+"eIyIķ6KT]6Hc[{K?rs@(B\-ΩKb_c+b ɻ3oރZx(v5]nݓ@PWe%.zKHJ<1DwK+OĨ9ӧA\U^.:GNmRu1b9 pgo|(ՙ+{Zhr}c,ttd|]G(ő-5zӉ︿||{;WooQug2px)t7(uXH?xJz0l?WS~~oɇM`DA %g1;w)YE̯R.sW>,; D,RJ-03y_#朧}ޛ#=w_py8- mI衤ƒD,y&s4?)jOU^E[ߍW+씤gXXg$bfYJm*==+* K&x(G^6/c>\ː֦nz̔ X曼B&P$D볠(z.if@$?Hb]WOF#ob$pѱKpENeF~둰pY xW=ĺ,g>2Kh0VE8UA)W{墤,xMĕmk]xʽ0?>/.iLcy>)TR8! 5[6 N"xHĽSc  7,Y:6q "f{H֩ԁj6nXtݺcI3 F;$nn3ߑ 0Íc\U·nT+ z?[em]p0 ^T6M"ԡE.";GxldeRE ST:!fMn.l6 est/ hn>stream hlUyp_[H!n7vS-BHIql%_ز,[-$e[>8@c!N CL.2L[tݴd:ͼ BuQw޷󉨼훷?zmD *\y [ aöcث8{ac,9HuauVa4/ 5}+:! 93tHfVqqFl!_wf̢V6F6V,or`z n.-N~yb%$@ ?1t HE8Vf,PL9^6y-l o6uem3 1 ")+N Cp3 ٖ8Ilk{Un<0X7 hZEE`^N 3|:+'u/NFEZVg=%R[ Z_ _=TbA׊  W`&\9Sߵ/,N ͧYj.}RHK~'l9c3O}>D{pBx$ }#2w.C]\\W)Zhw*vM vvnOCT0@ZQȫ$ijntύ L\b !UZZbZ)+UvpkypM%#ՙkjiV΃+g4-M0x2Urɫ:;/кD&E>`&K!AI9t Ν<˛f`Dod)8 ,GF>0=H 5tNmgg{kkiloClOcΰGێ 8DFw)ݘjuK yG vjJu~UVM^VQ(&5: *aq\`|dtd֟H"*@1#9k11#"*4m)ڻmbogm&.̟ys Ț^7 v 6! =u"AoD)u@ҏ p%@LdUL;ĨrhhiOyiN $ǝr9 3с;`:Bt]eJ_.Ozx#[ÁltG==p)EU2\]V3ZZ,bju:V'xYyYqa`#ʑQ$%bFS3^Ydݓ1eL ߓqh -$Ħ&#m8R5 3ImC[@4,a!x=_*?j%$տ ǁca5p4 !&њo> vg/d$n9CLgՖcbUP_2iٮbt6h F9싫9{B# ZZA…U= ;(gɩ.2cn}C#H`JQ#Q*5 ܲ,w<᧗*,Z| .%Ү=U%LYbER k竏ʨ¤wjĥA5J\stz|/2]e}C?!xf +aӴÇG_q,"pt g)5jj:ZNE?GJ4et~IE)-XAn(D&}vǽ]Gx p\" HMS KTBw9r~_ASN+U> endobj 151 0 obj <> endobj 152 0 obj <>/Border[0 0 0]/Rect[506.721 586.148 574.866 594.085]/Subtype/Link/Type/Annot>> endobj 153 0 obj <>/Border[0 0 0]/Rect[214.186 576.453 227.679 584.447]/Subtype/Link/Type/Annot>> endobj 154 0 obj <>/Font<>/ProcSet[/PDF/Text/ImageB]/XObject<>>> endobj 155 0 obj <>stream hތZ]s}&HH|8ٹv L|-8{W@B^zuk^7ѡ<;4a @EitK(U7냐h&~Q8NU$lY. }H\oe[TQmjwWfeK3IR\v?zt' M ~"ƕbXUncm4Gڿ Eèe˟~h/^) Ki#*eBMZMܗyV-#^u4m.ILQb(R*{(0M n}YUٔuAW ne}kxwᷪV1ɰӏn"4[64r4-MfUEwJq.ZlOYqd$B)t8x:MUaz63gG7gdGIi).Lijyow"OכUy[f5Rj\q>H|-"b9q'nH<uӻX{'mYv}[nVlC̮ wft:[`aZ"zu>!Lct6ykixwdmZ+Mk?07-W*8Hc M\}E/"FT} yu}Y.~D-lU%#O/n۬'X:ۥAWvWY-l'm:nmA!p|^͂#ٔ#aM`P,#h,c, 5uu$׹h;$?sy(fH|״뀾Xx]>+ʽ8&:Ꞝ9u`" dv4ٻlq O9M |iWE7,bCۋ] 4{Fs؞욤@. M,)f;ſ7čKFx,5f;3#MAUVeNh#iQQQٟ):/ _a ӧ'{f-$o-a#)a`dN87V3TܖbI7 eIyMѥ? xOYeUqϾ9bsP~@9?LP`Q&/!i;c"Pi*:1(C]u@>)r400M`"]X24c/XZniU m48|.,@^vFzHy[nJpiZKD]< :D MwNUZj8ۥ.TtPwfrn4ݿʂn I]-gXB$}2 dğPL 'ē4뛩0J:4m%F>Hg[Ew_4m!Zr ވث*b/F)/-b:+qΠZt54_rRuRE™Bl=@?kt9GqI&S;1?Ji\G5ƧQjv!X+u#q2 DǗN7*݄ث(53Ĭ1:/%Ue}?Ъ3k/g0XIccqfyCiX÷`™NPgsZyX"Wx6ۍ+4{6g'V\og]ރ1A6 ʼns#nf]im<PKCl׾H=?4n51Sc9xxz1q;]%CqpqG '> O Rɕspӂ -'*c҉! y֗]ᢗҭeFչWpkgKp73WHei@Q;Z`m{Xs~QUyZHD#WDSW']w~!쑃a۵%PኄNɂ_x?v[em _.qg#r:1S@s?ӕJGɬ9a#m[KFv.}FJɈ4rN|UAcOĉ‰}STPz.P .kJ5\Y%rWf./&k)CΣ PZƊ;G_>~W6A{=re=Tbj"ʒ->|'aY/2'I y7UyGM<ҙ:n戽=^d*-Qm}5X,7KЕ%MgpYQCK Vs+(iаWE?#ҎQ&ѓL7 q2"O! &ObiB\q;ls=mS5Թc`~'&T+>l;rs3ï+77G )a!,2׶|AaE,PcdG4mٔBHrȣA`,68>r}( #xLT:VZؓg_\M RIFx H;xV< D:V C-I .?㓓15Wv0b8W|5UX;7%w5ʛjp 8TMۻinp+C7I˘nD[BOλ2sC{p2;E=R$1fr"vfsVV>돞7dQ7OhLL#bYH=;#_"+},hj4cXʶʺ]-ԓ;(=UۡA܉_.HeqIYu}$˱h5|z[= #@]fT双\S)N_V9 0CUzZ6?@M˶x~'4y~䆿ڊߛԹ~"o8$~߮7> y>D鰆HvߊCW-qp,Z,Ӫf4gG{lA(ՂIU?_CMG̋=]A/T[>n^V\D18Ab((.8RiC6݃r7z>܋FOfex JyZl)y.԰02/'C>l c2TÙd#O:/Ijj}ٳՃT~|vGۅ;Ut(.t2YdHS*}B~koomtq h>= 5`R.`wޖY]Sǟoh  v`z)bO/o{wbGVFO d$53ꗵ;ΦdHj _>O$h9'~jՠ3e s߸-OL??<>+2^Y +3J7Ȱ)'Ga%V a:ɉ^%Dh/O(UD R,_l:>)%I=MtBEM}"/QghhQScQE#/9_ Nb`0x endstream endobj 156 0 obj <> endobj 157 0 obj <>/Font<>/ProcSet[/PDF/Text/ImageB]/XObject<>>> endobj 158 0 obj <>stream hlTMs6Wt, _ő㺓nL@Ĭ"UR'3w hFX.obyݐ]!/3'*LV('}6/;4 =e2UN ʇz]:*:°ͱчco_oXRmP0s|>rJb;ސϪW@?6>Ʀ;c1aGܰ)4]s:]+ȣu{LlVu_F(& H xI<5zYǮDVm],ɊdZ,}N;{A`S7ogb/jםm5%QoX Gʬy1qƶ_A4ɯམkI+C>fN\L7k,@0x.!V%.%=FX\'_$c *UR(;FJ~QS ͺV$u  i .} h>5Nf}8F YĭQ?x6b}I\|ꉃuT!4k;LĠLh(O@Kvc/nl˰CҷyY3\ړ 萮F^S&w$nP^H9d1@bipcBS!^.^¡ 1D˃SF &>%={`/0"VxnW=JyW[W DCjAQ6P {}nۛK( Ј/ԷU#FGe񾘧@1 *k1XMܯc?dC) *pVs$n#;Ԓ(b_ti endstream endobj 159 0 obj <> endobj 160 0 obj <> endobj 161 0 obj <> endobj 162 0 obj <>stream htTkPV9|&a8$mAʼn+(JE. , .{ {co {v% h54SMbI;Lfg~[itΏ3=yyH[Fxgeޞb́N25~ּ6nZzg-݊'[*x !7M#^'҈t!?y;x>6,ri~ fa(vגL ϜWƥ Yd$Zh[4,!xL72,.y&d UZ^SK,3„˾#dV-5CK+dC! 8oMNU7iSN_?!*D{HXUђpL]:Gvu[F? =9;VI*%))ћ`/:C*\ =@ŃZ/$%O:u4_K99ZUkO5 W%`T *o0Ns w8Iv!Wk8,FTΥۋQoHMv5amJXVԖn߆>%X*PEс NP⫃.D ; sV*6'v>)D {Blqt(;O8_rT|,%UM(U:=8As<5J\@7Y+&U"&856Cmt&2;ٵ,O'[F(8 68A6 1y&Ŷ󴭷N2Y&vUJqZ_"`(* CT+p###xaYhk(`/I?fSt: x y[ ӳϮ *|'f-Cɣ4;R$gs {ߕ]xᠷfMvew:|NoOO ծ߷SNXyLy@?(b>n%E2 S Hʄb.*ZY3^Mkoԩ:TdqIxoc-]JUXK=#]kO{ٕ=Xem%jw+1M ɭ5& 몠#zٍ. 0穋j3ְ9|>Uut y.귁z| ?G>;?O Fw~o/C³2exmUL[X_Ylԕq7|2\o1NB[ȫxN†ܛ+][H*9d/ endstream endobj 163 0 obj <> endobj 164 0 obj <>stream hTOo |9n&Ƌm݅ђT$} <~cjf'j%Z8\Oj7@bxܙnD/AH:[Og XAy:WZcIp\{YVUFMRib+`GYpɕ", @& mBE^8%3H(:PCC- ZKA}.dܻ~_ endstream endobj 165 0 obj <> endobj 166 0 obj <>stream h{gFJ!@5VP:[?>3[gޙ̝g=9v؍?|9wnwo^no|-/s~-|/?9v~kg]ܹs;]б_ãw?v/];q!ۿx/yW~}7ro rxt_-_c~+?˷MTG5ӧsE늩{g:u? *l*d.9QHZl/^L+\/kVk"$=!žl JywqtY/UcumU]՗ R,ULU|-)UbY陎m[[99QHj&ZZUzMckvszo 3zb#r^GтQʐuIYeWvMWuYm]'C'`i~xpmveFfbY[e)li֪ʨR/\üD-nrKr4 u6Q0t~g}W9YiE_eS7U}l ; р9̖rW4w\ 5*Rs35#6b=9ُȾ9o89b)w[0IuPr1j #5R=vslɞʪ4Zh0Zzs{n+4tSUw+>-}i9'||D^^ Ğ9_|ocsyUb;^w7 6x1S£3WC^|c ?=%{>ǔ/۸ly/KqwT.dj̹zSu>Sj=S|5P\\8 ϲ^t|6&TL\Ϝf:W7kᴞ@dOY;bGUJϗGժY6C7tEoj(b/Uv0ZDY|`-9#>yh.Te6j3/gմ/<8bpFAp,Y"*8i$Zֆ-yjV .ra%d?:}ٔB#œ3TlnekBX4/<q$ER"%ie NK,\5XKt=yAr"6m\hӼꦭ:MHOGx'aP )o.Iʦls<BG|-:`:[?ij #7-Ap6OigMC26i|?vz4QrJ4=6X3Nmײm[ho%M>ye4vw>6]#t},f3$CTUZuQy?q7IԳnK-MnrX27 Osc`/ysW-e8K%،LNܫ }aб>\*$͚ɫ̳4Kӌ$R>KJ4hfaM0K} C~LX,sRȹ)e*DjojZqfͩJZ6䯒.ڂ$9b-b˶ΌРF`1MjT U1^DMɝʯlr)%VyrC%Ж4XVfkVNU>KۭS+E!IVB Eit?Kf^ ;6Zp3@c9F TZӓRx7'm5JX˨$l0W $l$y2' Zxdj! t ?؂`Đt 6[qQUJKnrH#Ch>IGiQe ¿x|;P!,fμgSebdysdUjps[zLL8`bPt~,k""yN20d1X:`È`br#b'k?!v ڠ qK2[!UYeR+Pb^B ;3+"_<;8$o}pN S+~XSkPHm*4Vf'[b12f⨁O!fzX9j 32\|.FNHe{bm"6!a]  ny[5Uؓ$qJb*2?OX/T|IUIQŨ:ڭȍLMh|ϒsr8 1*@ ˰  Dlх\(8[b=֊!P%Imm&3ge@xO'kn趆0u>ɠP|CFY:KlZNz'AcF*L(r4# >5Y%Kage_M4CP.{JO܈ Dnݗ ~1z WVSl ʎE<"u2<[!Q(`z7V,E4e.ʂ1S'|`e$UR#[t"D#]BKD]Xs}g"s-Qt< x'iYFv7o|q]֩ hBxlbo3$yR7KR8Fe>@/@ \=9DΫN*o 7AUP{9ZeEP bh F[lHČ]\>CV[% ?a8S2$E`wLN~Iz'<}Iu' ?i{X$ڽW|y%>iӬ:1:ѷ޺{)MIpHft*Z-*սN(8nP١zwk. KZwr3dhőE<9ڦ 8h=ٻېl{GV1p5%]3DaEXMZKO4k}ڈ/9ȇb¾ z##NB}6b:  h(7a:qUR @FTxb=Ԍű.Xm=u+` f`qJvh/!Z1vl8Чڰ*`۷BnɵZaNﮜ!hz/+T1 Jo+{ 4m1:R)*gRS$P(K4Dճ4H<ÂawR ŧ[[ӵmAep.|ȎQDRIbhz%.Tps$*]+Zvk-l8'3=~:C20 #v6/y}[:zBh|`-E|\"4vg!Th5m}aSә7n'ʖƅۨ]زu hHQbk p7xH#o5[&!DgvV/ 3k6gBExD"FHlyթz,s};  ѠNf4q %ZI ŦG>ܴyPWVǛƇ~pc_}y`Lx+;ǧN9IrA\|G7%7[73_{/= ]|Fپex{f(H 4RVBV<"sY>wwgϴm>{w6t}{ =ge^1r25?%)c;k={*o^x ݁7 endstream endobj 167 0 obj <> endobj 168 0 obj <> endobj 169 0 obj <>stream hT=o w~ǫ:.C(]+e臚kwDȐ_UmlKot0KN@Y2^,p*%ܛB2A% xzm&8\/ s?8 P@ׁ‘pbF/xBrm.NHLmu=6QsQ~ ˢ CUb2u:r޻)wr>6$G}κ-+r> endstream endobj 170 0 obj <> endobj 171 0 obj <>stream hbd`ad`ddpsrrvL) 0wq4+47'~/IpU#+?0;## WhV| |?no5N? *'H=?uva{(Pч߭>naǺg;<1HB?v a#'9s/Z\3(U.w++ϫ"l\H endstream endobj 172 0 obj <> endobj 173 0 obj <> endobj 174 0 obj <> endobj 175 0 obj <>stream hlUil%~S m&|hm(iDRhpc)!À_{Yݱ{=/>쁯 "?JBREIVto Z7<<+2`"H7ow~Ueog-;! E kץ`̓em?#'('iV*jYڔwvVꪵڍdrZ\VVn]]6MVsY_]^A ðDN H5X!a XN,*zU$f Nd4g%KĿ`oݩ$X/JGCl6iJqUq˞\Mf=Je ^4qgr_agf_&F!Fu[ T͈/ZaW)#VZ!5z!ڃςS¨spyQIJiOq,3|pjjQFɨR,RM f nFz02`ӇӨBi*硐Ge Y8[9Y dB7HLʂn/d RGIr`fNӊa(4(G(dRWXptEr} wal:B[u d7u#i/ ?\A.\^W@ Xlu...uuuw\2S7NѲaxDQ qW9VGv;\Nح6\Qk:8&BMĨDpqT%^ۜ.īָC& :xw 9 {`nRQI(Me$F,ӔDMGn7تau>>Mxp?9<t0\n˃pi:gtYP0::'-PBJU5Lҥ85>@ Iv~Ȃܴ=ѢiQԺs+/!K5 *JClP[ja)@U"PB# (Z\Xq2.@r%_Ēk"-Np }q#^.@ܱy"G;(}v ~<gkw3F%p3>Ǹi"l w}nX>n@,k)gnӭK*/_S+O_(}y?#bseی@LGicewBGCa"bT>WS(B[o˩*)G|TGf~O.s⋖;4z>6XX@ X4VaϊLZ!٧i",xYlF8RD lGI̷WջN\h&Ezי8tN(Ֆ7RǭNӼ*wU3{Ҏ[g^?!| {R@ 4'^d·"0|*t䗓!B{h xe5]Y}xs:(ZݏHE j #h(,ĥ`,z endstream endobj 176 0 obj <> endobj 177 0 obj <>stream hTPn -/J]lhErKz}H1Fa9J3;K5pàr̫WVoY[ASk8'3|.Spa_VkpB!@@h۷~BI^L9Z KtI u%GYqw$3«' 56ͥx:f&Է@,Y qqݦ\ HkM>Cmp߼m4`fFr endstream endobj 178 0 obj <> endobj 179 0 obj <>stream hބTYl&zlݖ 204NI`7@NX:ȍH"EIX"\.II$ⱼCedEnȲY iㇾF }jP?L-JS3?>EGO;~٣ /:ވ0~-Iy{P~8k.V:7 W]CRPw_]AQC_gĝы_ϟ?UrِA/:R*V<>9XuMf^g3o(+_I4{b'w %t>[ ]EX7 7Ս輌02z`Dܓ0L} 4 9^-TX1$SD e-A"V1`ZC8k"LYWyl0pVH)Fu(2J#f46wg9"KJ>p ˥I5h+*R:4慎 =y=,%]lZJ̮eoqmYQ2P əKe'}{3@qXj/{b?#:72?Lq| Ī,I"acyPGj@  N7Ά xXn h|($=1RW`y8ɶ g16D/Y/".ӎ: nB{YTt7q5]j/ͤDzFfPp7D穊!g p?&rYܢ3ZFvJčO7 b_SFZ5ua_epv!QΦT6*aӔ"dhX]%ͷUR]Fjcnљh%U4/X4za,B\g-zo0S,k.vw+9B,gV>FbHOeAƏ8pel$"46]pSY"FmS ̉\Yy߉cG"GcO[753tp#~fqv7qIVb*֖6R$^j {9 D 6k-z+&|y"n}f&nl` _8: /܋^! *z\D]>)mnx,#> endobj 181 0 obj <> endobj 182 0 obj <>stream hTP1n0 )2V<$-⤻,Ѯhy#)n> endobj 184 0 obj <>stream ANBNNL+AdvP697C E AdvP697C !T endstream endobj 185 0 obj <> endobj 186 0 obj <> endobj 187 0 obj <> endobj 188 0 obj <> endobj 189 0 obj <> endobj 190 0 obj <> endobj 191 0 obj <> endobj 192 0 obj <> endobj 193 0 obj <> endobj 194 0 obj <> endobj 195 0 obj <> endobj 196 0 obj <> endobj 197 0 obj <> endobj 198 0 obj <> endobj 199 0 obj <> endobj 200 0 obj <> endobj 201 0 obj <> endobj 202 0 obj <> endobj 203 0 obj <> endobj 204 0 obj <> endobj 205 0 obj <> endobj 206 0 obj <> endobj 207 0 obj <> endobj 208 0 obj <> endobj 209 0 obj <> endobj 210 0 obj <> endobj 211 0 obj <> endobj 212 0 obj <> endobj 213 0 obj <> endobj 214 0 obj <> endobj 215 0 obj <> endobj 216 0 obj <> endobj 217 0 obj <> endobj 218 0 obj <> endobj 219 0 obj <> endobj 220 0 obj <> endobj 221 0 obj <> endobj 222 0 obj <> endobj 223 0 obj <> endobj 224 0 obj <> endobj 225 0 obj <> endobj 226 0 obj <> endobj 227 0 obj <> endobj 228 0 obj <> endobj 229 0 obj <> endobj 230 0 obj <> endobj 231 0 obj <> endobj 232 0 obj <> endobj 233 0 obj <> endobj 234 0 obj <> endobj 235 0 obj <> endobj 236 0 obj <> endobj 237 0 obj <> endobj 238 0 obj <> endobj 239 0 obj <> endobj 240 0 obj <> endobj 241 0 obj <> endobj 242 0 obj <> endobj 243 0 obj <> endobj 244 0 obj <> endobj 245 0 obj <> endobj 246 0 obj <> endobj 247 0 obj <> endobj 248 0 obj <> endobj 249 0 obj <> endobj 250 0 obj <> endobj 251 0 obj <> endobj 252 0 obj <> endobj 253 0 obj <> endobj 254 0 obj <> endobj 255 0 obj <> endobj 256 0 obj <> endobj 257 0 obj <> endobj 258 0 obj <> endobj 259 0 obj <> endobj 260 0 obj <> endobj 261 0 obj <> endobj 262 0 obj <> endobj 263 0 obj <> endobj 264 0 obj <> endobj 265 0 obj <> endobj 266 0 obj <> endobj 267 0 obj <> endobj 268 0 obj <> endobj 269 0 obj <> endobj 270 0 obj <> endobj 271 0 obj <> endobj 272 0 obj <> endobj 273 0 obj <> endobj 274 0 obj <> endobj 275 0 obj <> endobj 276 0 obj <> endobj 277 0 obj <> endobj 278 0 obj <> endobj 279 0 obj <> endobj 280 0 obj <> endobj 281 0 obj <> endobj 282 0 obj <> endobj 283 0 obj <> endobj 284 0 obj <> endobj 285 0 obj <> endobj 286 0 obj <> endobj 287 0 obj <> endobj 288 0 obj <> endobj 289 0 obj <> endobj 290 0 obj <> endobj 291 0 obj <> endobj 292 0 obj <> endobj 293 0 obj <>stream Arbortext Advanced Print Publisher 9.0.225/W 2014-12-15T21:07:28+08:00 2014-12-15T21:07:28+08:00 application/pdf pone.0115884 1..14 Acrobat Distiller 9.0.0 (Windows) uuid:08095560-1fc6-442e-8ec8-4041935cf3d7 uuid:02648aad-3d79-40ef-bf51-bc1b5a1513e3 endstream endobj 294 0 obj <> endobj xref 0 295 0000000000 65535 f 0000074572 00000 n 0000074816 00000 n 0000074928 00000 n 0000075042 00000 n 0000075156 00000 n 0000075270 00000 n 0000075384 00000 n 0000075498 00000 n 0000075612 00000 n 0000075726 00000 n 0000075841 00000 n 0000075956 00000 n 0000076071 00000 n 0000076185 00000 n 0000076298 00000 n 0000076411 00000 n 0000076585 00000 n 0000080348 00000 n 0000080410 00000 n 0000080729 00000 n 0000080949 00000 n 0000081232 00000 n 0000081513 00000 n 0000081629 00000 n 0000081745 00000 n 0000081860 00000 n 0000081993 00000 n 0000082106 00000 n 0000082219 00000 n 0000082334 00000 n 0000082448 00000 n 0000082562 00000 n 0000082676 00000 n 0000082790 00000 n 0000082904 00000 n 0000083019 00000 n 0000083134 00000 n 0000083249 00000 n 0000083364 00000 n 0000083479 00000 n 0000083592 00000 n 0000083790 00000 n 0000087072 00000 n 0000087219 00000 n 0000087469 00000 n 0000089958 00000 n 0000154220 00000 n 0000350597 00000 n 0000350744 00000 n 0000350908 00000 n 0000353720 00000 n 0000354015 00000 n 0000354128 00000 n 0000354244 00000 n 0000354360 00000 n 0000354476 00000 n 0000354590 00000 n 0000354704 00000 n 0000354819 00000 n 0000354934 00000 n 0000355049 00000 n 0000355165 00000 n 0000355280 00000 n 0000355394 00000 n 0000355508 00000 n 0000355623 00000 n 0000355738 00000 n 0000355849 00000 n 0000355960 00000 n 0000356075 00000 n 0000356189 00000 n 0000356302 00000 n 0000356477 00000 n 0000360454 00000 n 0000360630 00000 n 0000360746 00000 n 0000360862 00000 n 0000360978 00000 n 0000361217 00000 n 0000363669 00000 n 0000554294 00000 n 0000554470 00000 n 0000554584 00000 n 0000554695 00000 n 0000554809 00000 n 0000554995 00000 n 0000558474 00000 n 0000558621 00000 n 0000558820 00000 n 0000561388 00000 n 0000561624 00000 n 0000561740 00000 n 0000561856 00000 n 0000561970 00000 n 0000562086 00000 n 0000562202 00000 n 0000562318 00000 n 0000562432 00000 n 0000562548 00000 n 0000562664 00000 n 0000562781 00000 n 0000562896 00000 n 0000563082 00000 n 0000566607 00000 n 0000567029 00000 n 0000567144 00000 n 0000567259 00000 n 0000567375 00000 n 0000567491 00000 n 0000567607 00000 n 0000567724 00000 n 0000567841 00000 n 0000567957 00000 n 0000568072 00000 n 0000568189 00000 n 0000568305 00000 n 0000568421 00000 n 0000568537 00000 n 0000568653 00000 n 0000568769 00000 n 0000568886 00000 n 0000569003 00000 n 0000569120 00000 n 0000569237 00000 n 0000569354 00000 n 0000569471 00000 n 0000569588 00000 n 0000569705 00000 n 0000569822 00000 n 0000569939 00000 n 0000570056 00000 n 0000570172 00000 n 0000570288 00000 n 0000570405 00000 n 0000570522 00000 n 0000570639 00000 n 0000570756 00000 n 0000570873 00000 n 0000571025 00000 n 0000575356 00000 n 0000575562 00000 n 0000575677 00000 n 0000575792 00000 n 0000575907 00000 n 0000576023 00000 n 0000576137 00000 n 0000576362 00000 n 0000576560 00000 n 0000579734 00000 n 0000582199 00000 n 0000582471 00000 n 0000582645 00000 n 0000582784 00000 n 0000582923 00000 n 0000583075 00000 n 0000587590 00000 n 0000587740 00000 n 0000587881 00000 n 0000588829 00000 n 0000588994 00000 n 0000589325 00000 n 0000589573 00000 n 0000591235 00000 n 0000591564 00000 n 0000591917 00000 n 0000592176 00000 n 0000597020 00000 n 0000597152 00000 n 0000597368 00000 n 0000597679 00000 n 0000597916 00000 n 0000598218 00000 n 0000598310 00000 n 0000598660 00000 n 0000598955 00000 n 0000601239 00000 n 0000601528 00000 n 0000601861 00000 n 0000602105 00000 n 0000603942 00000 n 0000604045 00000 n 0000604211 00000 n 0000604497 00000 n 0000604713 00000 n 0000604889 00000 n 0000604946 00000 n 0000605305 00000 n 0000605363 00000 n 0000605446 00000 n 0000605535 00000 n 0000605637 00000 n 0000605739 00000 n 0000605841 00000 n 0000605943 00000 n 0000606045 00000 n 0000606147 00000 n 0000606249 00000 n 0000606351 00000 n 0000606453 00000 n 0000606555 00000 n 0000606657 00000 n 0000606759 00000 n 0000606861 00000 n 0000606963 00000 n 0000607065 00000 n 0000607165 00000 n 0000607265 00000 n 0000607365 00000 n 0000607465 00000 n 0000607565 00000 n 0000607665 00000 n 0000607765 00000 n 0000607865 00000 n 0000607965 00000 n 0000608063 00000 n 0000608161 00000 n 0000608259 00000 n 0000608357 00000 n 0000608455 00000 n 0000608553 00000 n 0000608651 00000 n 0000608743 00000 n 0000608841 00000 n 0000608939 00000 n 0000609035 00000 n 0000609133 00000 n 0000609231 00000 n 0000609327 00000 n 0000609423 00000 n 0000609519 00000 n 0000609615 00000 n 0000609711 00000 n 0000609807 00000 n 0000609903 00000 n 0000609999 00000 n 0000610095 00000 n 0000610191 00000 n 0000610227 00000 n 0000611247 00000 n 0000611298 00000 n 0000611349 00000 n 0000611400 00000 n 0000611452 00000 n 0000611504 00000 n 0000611556 00000 n 0000611608 00000 n 0000611660 00000 n 0000611712 00000 n 0000611764 00000 n 0000611816 00000 n 0000611868 00000 n 0000611920 00000 n 0000611972 00000 n 0000612024 00000 n 0000612076 00000 n 0000612128 00000 n 0000612180 00000 n 0000612232 00000 n 0000612284 00000 n 0000612336 00000 n 0000612388 00000 n 0000612440 00000 n 0000612492 00000 n 0000612544 00000 n 0000612596 00000 n 0000612648 00000 n 0000612700 00000 n 0000612752 00000 n 0000612803 00000 n 0000612854 00000 n 0000612905 00000 n 0000612956 00000 n 0000613007 00000 n 0000613058 00000 n 0000613109 00000 n 0000613161 00000 n 0000613213 00000 n 0000613265 00000 n 0000613315 00000 n 0000613367 00000 n 0000613418 00000 n 0000613469 00000 n 0000613520 00000 n 0000613571 00000 n 0000613622 00000 n 0000613673 00000 n 0000613724 00000 n 0000613775 00000 n 0000613813 00000 n 0000613839 00000 n 0000613903 00000 n 0000614036 00000 n 0000614130 00000 n 0000617685 00000 n trailer <]>> startxref 116 %%EOF 151 0 obj <>/Font<>/ProcSet[/PDF/Text/ImageB/ImageC/ImageI]/ExtGState<>>>/Type/Page>> endobj 294 0 obj <> endobj 296 0 obj <> endobj 657 0 obj <> endobj 659 0 obj <> endobj 660 0 obj <>stream HWVH^bPHv;OW ! E/B3\odE( o{od5k<_7{r'w{I&ãHTN#%S1^hz$\6s%bvnd$EwM\aE~=d{~o3Iqw׬e v=屎w$xStȰ\;߻-*]V͋ٸ-Iq ۴<&&4N[TuU]1嚵5 h1*QONłB6 pv(z6ȧ-ZIEčpOQeBg@QuBsHdpEز (KMe`NY;a$Ej"!b7D{BOxX޻[UF £d _m06a]X!()٣!/ wfw{7c"7 MzM[1"$%m@ a'AX ΣpiAq{$[H&pFFJ]d aFU]IH_E#ڮh@3E8pp#Y<gjJetmuĠ!4)e]dU6}r9əLf/1&ܦb̗)·~8;BH+ $$RTg}D^X8hi1z^TF A<`,2 g ;R8v[Ϟh[绺YD+=qu.NG`󎜙 ]\j NRpY%m6eD$ROOHҬ& Z<:]HjGLyyϢfȢL HzKAF`$: %}I%mv*CѴDq"\OsbHNՐ̪D+yMB6%z7DJh],'ǥfZfYͤZO40%7CLVIǔSk%BhaZiUdc fP-M1J@+u̓w\. R "'4:-w0ȕmYEttJuOǐaA'H[h RGXISݑ)^>|8ލ#]<:

k^qAB Uh0~~ a` & f~oc ȰI,uz{lAO(uCZ#'A3{q鷤T5Er yvpY m;-R@6By_}g*_Nwz4 v£BBGߔYWJaE5`4MCe>ܭC|d,5 視 ~򩟷zZP CzZ_=:fj:4_, } # 2X%Ŀr0aɅ-a5gvBE$ݕOXȚY!s##4Dقvۈ&wAKc UENP*$ano=SˎZn#v Zc!5Y7źm#hƫ&rnXE)Q}xwNY]aE6<Z[t8 r+7Au۔t"TK,zQgM[~gerտ%;ϋ,<=[@t(|]yٛ_2nca<%'oxt]4E1FzV9Mc!d>e4N}gԢRX5lf<7 t79-&ݦs 'o%" 0Z7H Qg9Cܜc@*kFVQ5SN\9-C==Z_ZTA(-~4h~ڱ[V!^kkvS̃G]J2׵G =>67;.Kew3DQn c< ɔijx/ 3] :+[={-(yL& 6$FM{:Hnkz:hs^?U,3$8Um=2Mӻ[$ށl$ Ƥa _-_A h3{J0 sZ?J ,Hq(_/6jҋ4JKEsSfMz)nΏw+$=.gpUZ9;OZǢ/$=[Q|"aMa``n`Dt*6U lj]!J~$Gx{<ŷV,KXr o)6L4|&O&7B[Ľ*\ctm^,n֓1nafmqFbF5?8n'rY2{Øcsgy@Z a1:+OxI,4'^rbkAv\x?ŚTфDgȚ0zx74#.lBGB_[D=viqD1^mleb|xn"EyH`#?kA7J^->׫ysSj`kyX$uK%0 mj2zH;̺Z#p/q^%Tq?\*|t׆F0Qoj6,ΑI?4G ULtSYPUў)xWEIo$*H^革q ǠZOb*}1n"[ s"4N9qk1q9B,+ "u4Ϻ|,$QٻܰGz(]՞Tv@3ZKd96Glp ,s#eϳlɏwTށW]q^e,跌cىX81n:u3:ZWGތ^}G},}o\tniӢ&϶|!Pb st7b#6)ofj"o -pz tUyY )q<,9T :9.x z{ T,yɉWbFS 8 ڂi=u97Kh'Κ.S}8S8ej jm@4~9QU.6ХB8Ɖ^Y2F/Alec<.G0Д_r|;y׍Ktezl|>4m%18%6xa.F,ʍihrw3whnhlj4K0Xr8:o`7PS|>=1P*N^7[^b^vs^mahjaa-k̛@)\R006ԁ9)oBd50@1hoQ A21 [j endstream endobj 661 0 obj <>stream Arbortext Advanced Print Publisher 9.0.225/W 2014-12-26T11:46:21+05:30 2014-12-15T21:07:28+08:00 2014-12-26T11:46:21+05:30 application/pdf pone.0115884 1..14 Acrobat Distiller 9.0.0 (Windows) uuid:08095560-1fc6-442e-8ec8-4041935cf3d7 uuid:400e71f5-1007-426d-9358-58aa7c2ea0cb endstream endobj xref 151 1 0000623937 00000 n 294 1 0000624263 00000 n 296 1 0000624482 00000 n 657 1 0000624598 00000 n 659 3 0000624652 00000 n 0000624698 00000 n 0000630078 00000 n trailer <<1510F3E8F7FD5E459C3790B8D6083CFD>]/Prev 116 >> startxref 633700 %%EOF %BeginExifToolUpdate 661 0 obj << /Type /Metadata /Subtype /XML /Length 4401 >> stream www.plosone.org false 10.1371/journal.pone.0115884 2014-12-31 application/pdf doi:10.1371/journal.pone.0115884 pone.0115884 1..14 Acrobat Distiller 9.0.0 (Windows) http://dx.doi.org/10.1371/journal.pone.0115884 2014-12-15T21:07:28+08:00 Arbortext Advanced Print Publisher 9.0.225/W 2014-12-26T11:46:21+05:30 2014-12-26T11:46:21+05:30 uuid:08095560-1fc6-442e-8ec8-4041935cf3d7 uuid:400e71f5-1007-426d-9358-58aa7c2ea0cb endstream endobj xref 0 1 0000000000 65535 f 661 1 0000634062 00000 n trailer << /Size 662 /Root 296 0 R /Info 294 0 R /ID [ <1610F3E8F7FD5E459C3790B8D6083CFD> ] /Prev 633700 >> %EndExifToolUpdate 634040 startxref 638557 %%EOF euclid-euclid-2.9/src/test/resources/org/xmlcml/files/journal.pone.0115884/fulltext.xml000066400000000000000000002226751461721410700307160ustar00rootroot00000000000000

PLoS ONE plos plosone PLoS ONE 1932-6203 Public Library of Science San Francisco, USA PONE-D-14-20725 10.1371/journal.pone.0115884 Research Article Biology and life sciences Evolutionary biology Organismal evolution Taxonomy Animal taxonomy A Novel Reproductive Mode in Frogs: A New Species of Fanged Frog with Internal Fertilization and Birth of Tadpoles A New Tadpole-Bearing Frog Iskandar Djoko T. 1 Evans Ben J. 2 McGuire Jimmy A. 3 * School of Life Sciences and Technology, Institut Teknologi Bandung, Bandung, Indonesia Center for Environmental Genomics, Department of Biology, McMaster University, Hamilton, Ontario, Canada Museum of Vertebrate Zoology and Department of Integrative Biology, University of California, Berkeley, California, United States of America Stöck Matthias Editor Leibniz-Institute of Freshwater Ecology and Inland Fisheries, Germany * E-mail: mcguirej@berkeley.edu

The authors have declared that no competing interests exist.

Conceived and designed the experiments: DTI BJE JAM. Performed the experiments: DTI BJE JAM. Analyzed the data: DTI BJE JAM. Contributed reagents/materials/analysis tools: DTI BJE JAM. Wrote the paper: DTI BJE JAM.

2014 31 12 2014 9 12 e115884 17 5 2014 23 11 2014 2014 Iskandar et al This is an open-access article distributed under the terms of the Creative Commons Attribution License , which permits unrestricted use, distribution, and reproduction in any medium, provided the original author and source are credited.

We describe a new species of fanged frog ( Limnonectes larvaepartus ) that is unique among anurans in having both internal fertilization and birth of tadpoles. The new species is endemic to Sulawesi Island, Indonesia. This is the fourth valid species of Limnonectes described from Sulawesi despite that the radiation includes at least 15 species and possibly many more. Fewer than a dozen of the 6455 species of frogs in the world are known to have internal fertilization, and of these, all but the new species either deposit fertilized eggs or give birth to froglets.

This research was funded by the United States National Science Foundation Division of Environmental Biology (DEB 0328700, DEB 0640967) to JAM, the Natural Science and Engineering Research Council of Canada (RGPIN/283102-2012) to BJE, and the Indonesian Directorate General of Higher Education, Ministry of Education and Culture (Basic Science Grant No. 60/P4M/DPPM/L-3311/MS/1993) to DTI. The funders had no role in study design, data collection and analysis, decision to publish, or preparation of the manuscript. Data Availability The authors confirm that all data underlying the findings are fully available without restriction. All relevant data are within the paper and its Supporting Information files.
Introduction

The Indonesian island of Sulawesi in central Indonesia is home to a largely unstudied radiation of fanged frogs in the genus Limnonectes Fitzinger, 1843 (Anura: Dicroglossidae Anderson, 1871). To date, only three species are recognized from Sulawesi, but at least 15 species occur on the island [1] , [2] , [3] . It was recently proposed that Sulawesi fanged frogs represent an adaptive radiation based on patterns of morphological diversification among co-distributed species, and correspondence of morphological features (body size, webbing) with habitat utilization [3] . Adult body size ranges from ∼2 g to more than 900 g in members of the Sulawesi assemblage, with large-bodied species found in close association with fast moving rivers, and small-bodied species occupying more terrestrial niches in forest leaf-litter and on the banks of slow moving streams. Iskandar and Tjan [4] first documented that there are multiple undescribed species of Limnonectes on Sulawesi, and indicated that one of the constituent species utilizes an unusual ovoviviparous reproductive strategy. Here we describe that species, and provide additional details on its reproductive mode.

Anurans exhibit tremendous diversity in mode of reproduction. Not only have there been many independent origins of direct development, the condition in which a free-living tadpole stage is bypassed and froglets emerge from the egg capsules [5] , but a bewildering array of mechanisms has evolved wherein parents care for their developing offspring. Examples of parental care include guarding of terrestrial eggs, transport of tadpoles on the back following hatching, carrying developing eggs in pouches or depressions on the back and flanks, males carrying developing tadpoles in the vocal sac, and even the now-extinct species of gastric brooding frogs in which the female carried her developing tadpoles in her stomach (see [6] for a summary). Despite this extreme reproductive diversity, internal fertilization has evolved only a few times among the 6455 known species of anurans [7] , [8] . Only the African bufonid genera Nectophrynoides and Nimbaphrynoides and the now extinct Puerto Rican species, Eleutherodactylus jasperi , are documented to combine internal fertilization with egg retention and subsequent birth of froglets [9] . To this group, we can now add a species of Sulawesi fanged frog in the genus Limnonectes with internal fertilization and live birth of tadpoles, a reproductive mode that is unique among anurans.

Materials and Methods

We analyzed more than 100 specimens representing the new species from 23 localities on the island of Sulawesi. This study was conducted under an approved Institutional Animal Care and Use Committee protocol issued to JAM by the University of California at Berkeley (Protocol #R279). Fieldwork conducted in Indonesia was undertaken under research permits issued by LIPI and RISTEK, with specimen exportation authorized under permits or loans issued by LIPI, the Museum Zoologicum Bogoriense, and Pusat Studi Biologi. Specimens were captured by hand in the field, sacrificed via emersion in an aqueous solution of MS-222 (tricaine methane sulfonate) buffered to neutral pH, and prepared as formalin-fixed specimens deposited primarily in the Museum Zoologicum Bogoriense (the national museum of Indonesia) or Museum of Vertebrate Zoology at UC Berkeley. As part of a comprehensive study of Sulawesi Limnonectes diversification, we obtained morphometric data for the full Sulawesi assemblage. Sex was determined by inspection of gonads. The following measurements were made to the nearest 0.1 mm using digital calipers: snout-vent length (SVL) – from cloaca to tip of snout; head width (HW) – widest distance between posterior end of lower jaw; head length (HL) – from posterior end of lower jaw to tip of snout; femur length (FE) – from cloaca to distal end of femur; tibia length (TI) – from knee to distal end of tibia; foot length (FL) – from distal end of tibia-fibula to tip of 4th toe; inner metatarsal tubercle (IM); humerus length (UA) – from proximal end of upper arm to elbow; lower arm length (LA) – from elbow joint to base of middle palmar tubercle; hand length (HA) – from lower border of middle tubercle to distal tip of third finger; snout length (SL) – from bony border of eye socket to tip of snout; snout width at eye level (SWE); snout width at nostril level (SWN); eye diameter (EY) – outer diameter, measured from bones bordering eye; interorbital distance (IO) – distance between orbits ( = width of frontoparietal bones); eye-tympanum distance (ET); eye-narial distance (EN) – eye socket border to posterior border of nostril; Internarial distance (IN) – shortest distance between nostrils; nostril to tip of snout (NT); tympanum diameter (TY); odontoid process length (OP) –length of fang-like odontoid process of lower jaw; tibia diameter measured at its widest point (TD). Digital webbing formulae from [10] .

Nomenclatural acts

The electronic edition of this article conforms to the requirements of the amended International Code of Zoological Nomenclature, and hence the new names contained herein are available under that Code from the electronic edition of this article. This published work and the nomenclatural acts it contains have been registered in ZooBank, the online registration system for the ICZN. The ZooBank LSID (Life Science Identifier) can be resolved and the associated information viewed through any standard web browser by appending the LSID to the prefix “ http://zoobank.org/ ”. The LSID for this publication is: urn:lsid:zoobank.org:pub: A758E03A–C054–4193–9768–D0C4AD588AEA. The electronic edition of this work was published in a journal with an ISSN, and has been archived and is available from the following digital repositories: PubMed Central, LOCKSS.

Results Taxonomy

Limnonectes larvaepartus new species ( Figs. 1 , 2 )

10.1371/journal.pone.0115884.g001 Images of the holotype of <italic>Limnonectes larvaepartus</italic> (MZB.Amph.23755) in ( <italic>a</italic> ) lateral, ( <italic>b</italic> ) dorsal, and ( <italic>c</italic> ) ventral view.

Ventral views of the right foot ( d ) and right hand ( e ) are also presented.

10.1371/journal.pone.0115884.g002 Images of <italic>Limnonectes larvaepartus</italic> .

( a ) MVZ 268323 ( male, left ) and MVZ 268307 ( female, right ) collected from Desa Uaemate along the Tasio-Tibo Road, Kabupatan Mamuju, Provinsi Sulawesi Barat, Sulawesi Island (02.61287S, 119.14238 E, 89 m elev.); ( b ) Limnonectes larvaepartus female (MVZ 268426) with tadpoles removed from the oviduct. Note the large yolk reserves available to the tadpoles; ( c ) An in situ adult male L. larvaepartus (JAM 14234) observed calling while perched on the edge of a small pool 2 m away from a 2 m wide stream; several L. larvaepartus tadpoles were present in the pool including the two visible within the yellow circle; ( d ) dorsal and ventral views of ∼stage 25 L. larvaepartus tadpoles (JAM 14271) released by a pregnant female (JAM 14237) at the moment of capture.

Urn:lsid:zoobank.org:act: 60AA7136-89A0-4DBB-9FBC-BD0FAF8A214C

This species has been referred to in the literature under the names Limnonectes larviparus [11] and Limnonectes “ovovivipar” [4] , both of which created nomina nuda. This species also corresponds to Limnonectes sp. V in [2] , [3] .

Etymology

The species name larvaepartus (from ‘larvae’, plural of larva, the early form of an animal, and ‘partus’, to give birth to) reflects the unique reproductive mode of this tadpole-bearing species.

Holotype

MZB.Amph.23755 (Field Number: BSI 0605, see Fig. 1 ), an adult male, collected from Dunu Village, (0.92353 o N; 122.64386 oE) at 189 m elevation, Kecamatan Anggrek, Kabupaten Gorontalo, Provinsi Gorontalo, Sulawesi, Indonesia by J.A. McGuire & team, 18 October 2004.

Paratypes

Paratypes (n = 30) are from Sulawesi Utara, Gorontalo, Sulawesi Tengah, and Sulawesi Barat Provinces: MZB 2834, a gravid female with 33 translucent tadpoles (Gosner stage 23) from the left oviduct and 2 from outside the body, from Toraut (00′33.72 o N, 123′54.23 oE), Bogani Nani Wartabone National Park, Kabupaten Bolaang Mongondow, Sulawesi Utara at 370 m elevation, by D. T. Iskandar, 15 August 1991; FMNH 252453, a female, from Toraut, Bogani Nani Wartabone National Park, Kabupaten Bolaang Mongondow, Sulawesi Utara by D. T. Iskandar, August 1991. MVZ 255545, 256009-11, 256013 from Desa Lombongo (−1.43346, 120.30800), Kecamatan Suwawa, Kabupaten Bone Bolango, Bogani Nani Warta Bone National Park, Provinsi Gorontalo at 75 m elevation by J. A McGuire and team, 20 October 2004. ZRC 1.3258 (left femur removed) from Toraut, Bogani Nani Wartabone National Park, Kabupaten Bolaang Mongondow, Sulawesi Utara at 370 m elevation, by D. T. Iskandar, 15 August 1991. MZB 2835–2841 from Toraut, Bogani Nani Wartabone National Park, Kabupaten Bolaang Mongondow, Sulawesi Utara at 370 m elevation, by D. T. Iskandar, 15 August 1991 & 12 July 1992. MVZ 255548–49 from Desa Pontak (−2.62910, 118.99300), Kecamatan Motoling, Kabupaten Minahasa Selatan, Provinsi Sulawesi Utara at 285 m elevation by J. A. McGuire and team, 13 October 2004. MZB 3117, near Potolok river, Lolak, Bogani Nani Wartabone National Park at 350 m elevation, Kabupaten Bolaang Mongondow, Sulawesi Utara; MZB 3118, male from seashore forest near Bungbungan River, Lolak, Bogani Nani Wartabone National Park, Kabupaten Bolaang Mongondow, Sulawesi Utara. MZB 3120 from Tangkorak River, Desa Pindol, Kecamatan Lolak, Bolaang Mongondow, Sulawesi Utara, by Mumpuni, 26 June 1995; MZB 3121 from Tangaga Forest, Dudepo, Bolaang Mongondow, Sulawesi Utara, by I. Maryanto, 20 October 1995; MZB 3122 from Potolok River, Bogani Nani National Park, Lolak, Sulawesi Utara by Mumpuni, 19 June 1995; MZB 3124 from Bungbungan River, Bogani Nani National Park, Lolak, Sulawesi Utara by Mumpuni, 19 June 1995; MZB Amph.8108 from Toraut, near Bogani Nani Wartabone National Park, Sulawesi Utara. LSUMZ 84209, 84214, 84221, 84224 from Desa Torout, Bogani Nani Wartabone National Park, Kabupaten Bolaang Mongondow, Provinsi Sulawesi Utara at 267 m elevation by J. A. McGuire on 6 and 11 September 2001.

Other referred specimens

LSUMZ 84218, 84219 from Desa Torout, Bogani Nani Wartabone National Park, Kabupaten Bolaang Mongondow, Provinsi Sulawesi Utara by J.A. McGuire. FMNH 130991, 106930 from Buang-buang Island, Sulawesi Utara. MZB 3119, juvenile from Seashore forest near Bungbungan River, Lolak, Bogani Nani Wartabone National Park, Kabupaten Bolaang Mongondow, Provinsi Sulawesi Utara. AMNH 167199 from Tangkoko National Park (1.570083 o N, 125.156933 o E), Kabupaten Minahasa, Provinsi Sulawesi Utara. MVZ 255546 from Desa Salumpaku (−1.60757, 119.29900), Kecamatan Banawa, Kabupaten Donggala, Provinsi Sulawesi Tengah. MVZ 255547 from Desa Kelapa Dua (−1.60757, 119.29900), Kecamatan Anreapi, Kabupaten Polewali Mandar, Provinsi Sulawesi Barat. MVZ 268426, 268428–30, 268432 from Polewali-Masawa Road (River 1) (−2.65490, 118.93300), Kecamatan Polewali, Kabupaten Polewali Mandar, Provinsi Sulawesi Barat. MVZ 268309–10, 268313, 268317–19, 268322, 268325, MZB.Amph. 20675, 20677–80 from Desa Uaemate (Tasiu-Tibo Road; S02.61550, E119.14417), Kecamatan Kaluku, Kabupaten Mamuju, Provinsi Sulawesi Barat. MZB.Amph.20663 from Desa Kabiraan (−2.62460, 119.14700), Kecamatan Ulumunda, Kabupaten Majene, Provinsi Sulawesi Barat.

Distribution

Limnonectes larvaepartus occurs across the Northern Peninsula, as well as on the western margin of Sulawesi's Central Core ( Fig. 3 ). We do not know the full extent of the species' range in the Central Core because the central highlands of Sulawesi remain poorly explored herpetologically. Several genera have species range boundaries in this same general region (e.g. the flying lizards Draco spilonotus and D. walker [12] , the fanged frogs Limnonectes sp. I and L. sp. D [3] , and the tarsiers Tarsius lariang and T. dentatus [13] .

10.1371/journal.pone.0115884.g003 Distribution map (left panel) depicting the range of <italic>Limnonectes larvaepartus</italic> .

The right panel shows the phylogenetic position of L. larvaepartus , with different symbol shapes denoting regional genetic structure in the species.

Diagnosis

Prior workers have recognized substantial species diversity in the genus Limnonectes on Sulawesi. However, diagnosing many of these lineages on the basis of morphology is challenging, and several authors have instead opted to apply names to Sulawesi specimens representing species from outside Sulawesi. Consequently, the following names have all been incorrectly applied to Sulawesi populations: the Lesser Sundas species (type locality: Flores Island) L. dammermani (Mertens, 1927), the Bornean species L. finchii (Inger, 1966), the Mollucan species (type locality: Ambon) L. grunniens (Daudin, 1801), and the Philippine taxa L. leytensis (Boettger, 1893), L. magnus (Stejneger, 1909), and L. palavanensis (Boulenger, 1894). These names should not be applied to Sulawesi populations, as was verified phylogenetically for several of these taxa [3] . Only four Sulawesi species have been described: L. arathooni (Smith, 1927), L. heinrichi (Ahl, 1933), L. modestus (Boulenger, 1882), and L. microtympanum (van Kampen, 1909). However, we will show elsewhere that L. heinrichi is a junior synonym of L. modestus and the species complex that we have referred to previously [2] , [3] as L. modestus remains undescribed – thus, at present there are but three valid described species of Sulawesi Limnonectes .

Limnonectes larvaepartus can be distinguished from all other described species of Limnonectes by its reproductive mode ( Fig. 2 ). It can be further differentiated from all described Sulawesi species by its combination of body size (mean male SVL = 37.4; female SVL = 40.2 mm), coloration, tympanum size, and degree of hind foot webbing, as well as on the basis of phylogenetic placement ( Fig. 3 ). Limnonectes arathooni is endemic to Sulawesi's Southwestern (SW) Peninsula south of the Tempe Depression, and thus does not occur within the range of L. larvaepartus . It is similar in size to the new species (male SVL = 36.6 mm, females = 39.6 mm), but differs in having substantially reduced webbing (extending to penultimate phalange of fourth toe vs. to toe disc), in lacking fine granular dorsal tubercles, and in having melanic spots above the forelimb insertion, a fine ridge extending posteriorly behind each eye, and an alternative derived reproductive mode in which males guard clutches of terrestrial eggs that hatch into tadpoles that then make their own way to an adjacent stream by sliding down steep stream-side embankments [14] . Limnonectes microtympanum , like L. arathooni , is restricted to the SW Peninsula south of the Tempe Depression, and thus does not overlap in geographical range with L. larvaepartus . Limnonectes microtympanum is moderately large (male SVL = 78.4; female SVL = 72.4 mm), and thus much larger than the new species. Limnonectes microtympanum also differs from the new species in having proportionally smaller tympana (TY/SVL = 0.05 + 0.01 in males, 0.06 + 0.01 in females versus 0.08 + 0.01 in both sexes in L. larvaepartus ). The new species occurs in broad sympatry with L. modestus , which is a moderate sized (male SVL = 70.2 mm; female SVL = 64.0 mm) inhabitant of fast-moving streams and substantially larger than L. larvaepartus . Like L. larvaepartus , L. modestus has nearly complete hindfoot webbing (slightly more extensive in L. modestus than in L. larvaepartus but reaching the toe disc in both species), a dusky throat with melanic pigments extending onto the pectoral region (in a clear wedge shape in L. modestus , more randomly distributed in L. larvaepartus ), and skin with extensive fine granular tubercles. Limnonectes modestus also exhibits a derived reproductive mode involving production of a relatively small number of large (10 mm diameter) eggs that are deposited along the edge of fast moving streams.

Description of the holotype

An adult male ( Fig. 1 ) 48 mm SVL, body moderately robust, head not broader than body, head about 65% longer than wide, length 45% of snout-vent length, snout 17% of snout-vent length, moderately pointed, projecting above lower jaw, nostril lateral, closer to tip of snout than to eye, lore essentially straight, canthus rostralis distinct, eye about equal to snout length, pupil diamond-shaped, upper eyelid with tubercles; interorbital region smooth, width 69% of internarial distance, tympanum moderate, slightly wider than interorbital distance, supratympanic fold distinct, extending from posterior corner of eye to supra-axillary region, in contact with tympanic annulus, temporal muscle slightly enlarged; odontoid process 2.1 mm. Dentigerous process of vomer distinct, angled anterolaterally, approximately at 45 o angle, posterior ends separated by distance approximately equal to one-third diameter of choanae. Limbs relatively slender, tibia width at thickest part 7.5 mm; femur length 52% of snout-vent length, heels moderately overlapping when placed perpendicular to body axis; tibia length 64.9% of foot length, 48.9% of snout-vent length; foot length 71% of snout-vent length; tarsal fold indistinct, only evident as a ridge; toe discs moderately expanded, circum-marginal groove horseshoe-shaped, pointed anteriorly. Plantar surface of foot smooth, subarticular tubercle rounded, relative length of toes 4>3>5>2>1. Inner metatarsal tubercle prominent, elongate, ovoid with a sharp spade like ventral edge; outer metatarsal tubercle absent; hind foot webbing full, extending to toe discs, slightly emarginated. Manus length 51.3% of foot length, fingers slender, terminal discs slightly expanded, length formula 3>1≥4≥2, with slight differences in length, subarticular tubercle rounded, convex; supernumerary tubercles absent; inner and outer metacarpal tubercles enlarged, nuptial pads and webbing absent, forearm muscle not enlarged. See Table 1 for measurements and variation.

10.1371/journal.pone.0115884.t001 Summary of univariate morphological variation among mensural characters in <italic>Limnonectes larvaepartus</italic> .
Limnonectes larvaepartus
Males (31) Females (30) Holotype
Snout-Vent Length (SVL) 39.8+4.7 41.2+3.1 48.0
Range 31.3−48.3 35.7−47.5 -
Head Length (HL) 17.6+2.0 17.9+1.3 21.5
Head Width (HW) 15.4+2.1 15.1+1.5 18.9
Snout Length (SL) 7.0+0.9 7.2+0.5 8.2
Eye-Narial Distance (EN) 3.6+0.5 3.9+0.4 4.0
Nostril-Tip of Snout (NT) 3.6+0.4 3.8+0.4 4.3
Internarial Distance (IN) 4.5+0.6 4.4+0.4 4.9
Snout Width at Eye (SWE) 10.5+1.2 10.6+1.0 12.2
Snout Width at Nostril (SWN) 6.0+0.8 5.8+0.7 7.5
Eye-Tympanum Distance (ET) 1.8+0.5 1.6+0.4 1.7
Odontoid Process Length (OP) 2.0+0.3 1.6+0.2 2.1
Tympanum Diameter (TY) 3.1+0.5 3.3+0.4 3.6
Interorbital Distance (IO) 3.0+0.5 3.0+0.4 3.9
Eye Diameter (EY) 7.2+0.8 7.4+0.5 8.7
Femoral Length (FE) 21.1+2.6 22.0+2.4 23.7
Tibial Length (TI) 22.7+2.8 24.1+2.3 23.5
Foot Length (FL) 32.1+3.6 33.5+3.0 36.2
Humeral Length (UA) 11.5+1.0 11.4+1.1 13.5
Lower Arm Length (LA) 8.4+1.0 9.1+1.0 9.5
Hand Length (HA) 9.7+1.2 9.9+1.0 11.8
HL/SVL 0.44+0.02 0.44+0.03 0.45
HW/SVL 0.39+0.02 0.37+0.02 0.39
SL/SVL 0.18+0.01 0.18+0.01 0.17
EN/SVL 0.09+0.01 0.09+0.01 0.08
IN/SVL 0.11+0.01 0.11+0.01 0.10
SWE/SVL 0.26+0.02 0.26+0.01 0.25
SWN/SVL 0.15+0.01 0.14+0.01 0.16
ET/SVL 0.04+0.01 0.04+0.01 0.04
OP/SVL 0.05+0.01 0.04+0.01 0.04
TY/SVL 0.08+0.01 0.08+0.01 0.08
IO/SVL 0.08+0.01 0.07+0.01 0.08
EY/SVL 0.18+0.01 0.18+0.01 0.18
FE/SVL 0.53+0.03 0.53+0.03 0.49
TI/SVL 0.57+0.04 0.59+0.04 0.49
PL/SVL 0.81+0.03 0.81+0.04 0.75
SL/SWE 0.67+0.07 0.69+0.07 0.67
IN/SWN 0.74+0.08 0.76+0.08 0.65
HW/HL 0.87+0.07 0.84+0.08 0.88
SL/HW 0.46+0.05 0.48+0.03 0.43
IO/IN 0.68+0.07 0.67+0.08 0.80
TI/TD 3.6+0.4 3.8+0.2 3.6
Coloration

The dorsal coloration is highly variable, typically brownish-grey, but can be darker brown on the dorsolateral region, and some individuals are reddish-brown or golden-tan (see Fig. 2 ). ∼23% of specimens have a bold mid-dorsal stripe. The venter is either yellowish or cream colored, with the upper end of the tibia usually bearing a prominent dark spot. A light bar is often present in the interorbital region, and the coloration of the snout to interorbital region may be lighter than the remainder of the dorsum. The tympanum is often masked in black leaving only the lower rim sharing the predominant body coloration. The gular region is usually darker in males and may have a finely mottled wedge-shaped melanic patch. The dorsal half of the iris is golden-orange in coloration in at least some individuals (we do not know of exceptions, but have not documented iris coloration for most specimens).

Eggs and tadpoles

Females produce ∼100 non-pigmented eggs (see [15] ), though the most we have observed is 55, which possibly represents the contents of just one oviduct. The eggs lack a jelly-coat and reach at least ∼3 mm in diameter. These eggs develop within the oviducts into pigmented tadpoles that reach at least Gosner stage 35 prior to parturition (see [16] for staging). The gut is initially provisioned with substantial yolk ( Fig. 2 b ), and developing tadpoles prematurely removed from the oviducts at approximately stage 21 progressed to approximately stage 25 over the course of two weeks in a water bottle without supplemental food, suggesting plasticity in terms of the timing of parturition. A detailed description of the tadpole is provided in the accompanying paper by Kusrini et al. [15] .

Natural history

Limnonectes larvaepartus occurs in natural and disturbed forest habitats of Sulawesi, generally in sympatry with at least one, and sometimes as many as five other Limnonectes species. In the western Central Core of Sulawesi, we have always found L. larvaepartus living in sympatry with a much larger species that has been referred to in the literature as L. sp. D (2,3). Whereas L. sp. D is generally found on rocks in fast-moving streams or within a meter of water on the banks of fast-flowing streams, L. larvaepartus is usually found further from the stream (2–10 meters from water) on rocky substrates, in leaf-litter, or secluded in grassy vegetation. Because we have observed that L. sp. D predates other frogs, including other Limonectes , it is possible that L. larvaepartus avoids large streams in response to predation pressure from larger Limnonectes species. Male L. larvaepartus typically call from the margins of seeps, puddles, or small pools away from the main stream. Notably, we have found many males calling from small pools that were already inhabited by L. larvaepartus tadpoles ( Fig. 2 c ), with as many as three size-classes of tadpoles represented. It is unclear whether some or all of the observed tadpoles were sired by the accompanying male. We have furthermore collected at least one pregnant female from a small stream-side puddle already inhabited by tadpoles of two size classes, again suggesting the possibility that individual pools may be visited repeatedly by the same adult males and females during the reproductive season.

Discussion

We first became aware of the unusual reproductive mode of Limnonectes larvaepartus while conducting fieldwork on Sulawesi. In several instances, we discovered tadpoles in the oviducts while preparing specimens. In each case, having sacrificed frogs for preparation, the abdominal wall was observed to quiver, and incision resulted in living tadpoles emerging from the opening (see S1 Movie ). On one occasion, a gravid female gave birth to tadpoles in-hand at the moment of capture. On four other occasions, field-collected L. larvaepartus in our possession gave birth to tadpoles while being held individually in collecting bags. In total, we have either observed tadpoles in the oviducts or direct-birth of tadpoles on 19 occasions.

Because we have not witnessed natural birth of tadpoles in free-living frogs, two possible alternative reproductive modes are possible for this species. Limnonectes larvaepartus reproduction may simply reflect what we have observed in the hand – direct birth of tadpoles. Alternatively, this species may be capable of retaining developing young in the oviducts through metamorphosis with subsequent birth of froglets, as is the case for Eleutherodactylus jasperi and members of the African bufonid genera Nectophrynoides and Nimbaphrynoides [9] , [17] , [18] . The latter mode seems unlikely for several reasons. First, we have collected 19 pregnant individuals carrying tadpoles in the oviducts but none carrying froglets. These 19 females were collected across different months, years, and localities. Second, we have observed and collected at least four clutches of free-living L. larvaepartus tadpoles in small pools of water on the margins of streams, with each of these samples exhibiting two or three size classes and thus possibly representing multiple clutches. Three of these clutches were accompanied by males, some of which were calling, and one was found in association with the gravid female that gave birth to 55 tadpoles (some in the hand of JAM, others subsequently deposited in a collecting bag). Finally, although adaptive plasticity has been demonstrated for many frog species, whereby tadpoles or froglets are capable of hatching from their eggs prematurely when attacked by predators [19] , this has only been documented for a single direct-developer, Eleutherodactylus coqui [20] . In the case of E. coqui , the capacity for early hatching commenced at stages 13 or 14, whereas normal hatching occurred at stage 15 (see [21] for staging). Thus, E. coqui were capable of premature hatching only as froglets, with toe pads and eyelids present but before the tail was completely resorbed, a stage much later than would be required by L. larvaepartus if it were simultaneously capable of hatching as either a tadpole or full-term froglet. Most direct-developers, as well as the few frog species that give birth to froglets, pass through the tadpole stage in a form poorly suited for free-living. In many such species, the tadpole has a broad, highly vascularized ‘respiratory tail’ specialized for intra-egg or intra-oviductal gas exchange rather than for swimming, as is the case for Eleutherodactylus jasperi [17] , and may lack mouthparts and functional gills [22] , [23] , [24] . In others, the tail remains rudimentary and limb buds appear early in development such that a typical tadpole phenotype never occurs [22] , [23] . Given these observations, it appears unlikely that any one species would be characterized by the combination of (1) internal fertilization, (2) complete metamorphosis within the oviducts and live-birth in the form of froglets, as well as (3) an oviductal tadpole stage that is capable of premature birth and free-living. Although we think that L. larvaepartus is much more likely to give birth to tadpoles as its sole mode of reproduction as opposed to exhibiting adaptive plasticity allowing for birth of either tadpoles or froglets depending on the circumstances confronting the frog, we note that either condition would be unique among Anura. In either case, L. larvaepartus requires internal fertilization, which is, itself, extremely rare among anurans [8] , [25] . The mechanism by which internal fertilization takes place is unknown, and there is no obvious intromittent organ present to facilitate sperm transfer. If L. larvaepartus reproduction always involves birth of tadpoles, it is likely that they are ovoviparous given that the tadpoles are well provisioned with yolk and appear fully capable of developing without nutrient transfer from the mother. Prior to parturition, the tadpoles have well-developed tails and pigmentation, and well developed mouthparts. Given this morphology, it is unlikely that these tadpoles are endotrophic (never feed before metamorphosing into froglets). It is more likely that the tadpoles are born after exhausting their yolk supply, and are subsequently self-feeding prior to metamorphosis. Nevertheless, it is clear that much remains to be learned about this unusual frog, the discovery of which brings to light yet another axis of diversity characterizing the remarkable Sulawesi fanged frog adaptive radiation.

Supporting Information

Video showing the characteristic quivering abdomen caused by movement of tadpoles within a pregnant female Limnonectes larvaepartus.

(MOV)

We thank K. N. Tjan, U. Arifin, K. Laras, Suwatio, A. Rachmansah, R. M. Brown, and C. J. Hayden for assistance in the field. Boeadi and Mumpuni for MZB specimens; R. F. Inger for discussion and access to comparative materials from FMNH, USNM and MCZ; D. N. Cannatella, M. H. Wake, and members of the McGuire Lab for helpful discussion.

References Emerson SB , Inger RF , Iskandar DT ( 2000 ) Molecular phylogenetics and Evolution of fanged Frogs . Mol Phylogenet Evol 16 : 131142 . Evans BJ , Brown RM , McGuire JA , Supriatna J , Andayani N , et al . ( 2003 ) Phylogenetics of fanged frogs; testing biogeographical hypotheses at the interface of the Asian and Australian faunal zones . Syst Biol 526 : 794819 . Setiadi MI , McGuire JA , Brown RM , Zubairi M , Iskandar DT , et al . ( 2011 ) Adaptive radiation and ecological opportunity in Sulawesi and Philippine fanged frogs ( Limnonectes ) Communities . Amer Nat 178 : 221240 . Iskandar DT, Tjan KN (1996) The Amphibians and Reptiles of Sulawesi: with notes on the distribution and chromosomal number of frogs. Proc. 1 st Intl. Conf. Eastern Indonesian-Australian Vertebrates. Manado pp. 39–46. Wake MH ( 2003 ) Reproductive modes, ontogenies, and the evolution of body form . Anim Biol 53 : 209223 . Haddad CFB , Prado CPA ( 2005 ) Reproductive modes in frogs and their unexpected diversity in the Atlantic Forest of Brazil . BioScience 55 : 207217 . AmphibiaWeb: Information on amphibian biology and conservation . Available: http://amphibiaweb.org/ . Accessed: October 13, 2014. McDiarmid RW, Altig R (1999) Tadpoles: the biology of anuran larvae. Chicago: University of Chicago Press. 444 p. Wells KD (2008) The Ecology and Behavior of Amphibians. Chicago: The University of Chicago Press. 1148 p. Guayasamin JM , Bustamante MR , Almeida-Reinoso D , Funk WC ( 2006 ) Glass frogs (Centrolenidae) of Yanayacu Biological Station, Ecuador, with the description of a new species and comments on centrolenid systematics . Zool J Linn Soc 147 : 489513 . Inger RF , Voris HK ( 2001 ) The biogeographical relations of the frogs and snakes of Sundaland . J Biogeogr 28 : 863891 . McGuire JA , Brown RM , Mumpuni, Riyanto A , Andayani N ( 2007 ) The flying lizards of the Draco lineatus group (Squamata: Iguania: Agamidae): A taxonomic revision with descriptions of two new species . Herp Monog 21 : 179212 . Merker S , Driller C , Dahruddin H , Wirdateti, Sinaga W , et al . ( 2010 ) Tarsius wallacei : A new tarsier species from Central Sulawesi occupies a discontinuous range . Int J Primatol 31 : 11071122 . Brown RM , Iskandar DT ( 2000 ) Nest site Selection, larval hatching and advertisement calls of Rana arathooni from southwestern Sulawesi (Celebes) Island, Indonesia . J Herp 34 : 404413 . Kusrini MD, Rowley JJL, Khairunnisa LR, Shea GM, Altig R (2014) A new breeding mode for anurans: reproductive biology and larvae of the Indonesian frog Limnonectes larvaepartus . PLOS ONE. In press Vol. 10, Issue 1. Gosner KL ( 1960 ) A simplified table for staging anuran embryos and larvae with notes on identification . Herpetologica 16 : 183190 . Wake MH ( 1978 ) The reproductive biology of Eleutherodactylus jasperi (Amphibia, Anura, Leptodactylidae), with comments on the evolution of live-bearing systems . J Herpetol 12 : 121133 . Wake MH ( 1980 ) The reproductive biology of Nectophrynoides malcolmi (Amphibia: Bufonidae) with comments on the evolution of reproductive modes in the genus Nectophrynoides . Copeia 1980 : 193209 . Warkentin KM ( 2011 ) Plasticity of hatching in amphibians: Evolution, trade-offs, cues and mechanisms . Integr Comp Biol 51 : 111127 . Buckley CR , Michael SF , Irschick DJ ( 2005 ) Early hatching decreases jumping performance in a direct-developing frog, Eleutherodactylus coqui . Funct Ecol 19 : 6772 . Townsend DS , Stewart MM ( 1985 ) Direct development in Eleutherodactylus coqui (Anura: Leptodactylidae): a staging table . Copeia 1985 : 423436 . Lutz B ( 1948 ) Ontogenetic evolution in frogs . Evolution 2 : 2939 . Lynn WG ( 1942 ) The embryology of Eleutherodactylus nubicola , an anuran which has no tadpole stage . Carn Inst Wash Publ 541 : 2762 . Thibaudeau G, Altig R (1999) Endotrophic anurans – development and evolution. In: McDiarmid RW, R Altigeditors.Tadpoles. The biology of anuran larvae, Chicago: The University of Chicago Press pp. 170–188. Duellman WE, Trueb L (1986) Biology of Amphibians. New York: McGraw-Hill Publishing Company. 670 p.
euclid-euclid-2.9/src/test/resources/org/xmlcml/files/journal.pone.0115884/results.json000066400000000000000000000033321461721410700307040ustar00rootroot00000000000000{ "fulltext_pdf": { "value": [ "http://dx.plos.org/10.1371/journal.pone.0115884.pdf" ] }, "fulltext_html": { "value": [] }, "fulltext_xml": { "value": [ "/article/fetchObjectAttachment.action?uri=info%3Adoi%2F10.1371%2Fjournal.pone.0115884&representation=XML" ] }, "title": { "value": [ "A Novel Reproductive Mode in Frogs: A New Species of Fanged Frog with Internal Fertilization and Birth of Tadpoles" ] }, "author": { "value": [ "Djoko T. Iskandar", "Ben J. Evans", "Jimmy A. McGuire" ] }, "date": { "value": [ "2014/12/31" ] }, "doi": { "value": [ "10.1371/journal.pone.0115884" ] }, "volume": { "value": [ "9" ] }, "issue": { "value": [ "12" ] }, "firstpage": { "value": [ "e115884" ] }, "description": { "value": [ "PLOS ONE: an inclusive, peer-reviewed, open-access resource from the PUBLIC LIBRARY OF SCIENCE. Reports of well-performed scientific studies from all disciplines freely available to the whole world." ] }, "abstract": { "value": [ "AbstractWe describe a new species of fanged frog (Limnonectes larvaepartus) that is unique among anurans in having both internal fertilization and birth of tadpoles. The new species is endemic to Sulawesi Island, Indonesia. This is the fourth valid species of Limnonectes described from Sulawesi despite that the radiation includes at least 15 species and possibly many more. Fewer than a dozen of the 6455 species of frogs in the world are known to have internal fertilization, and of these, all but the new species either deposit fertilized eggs or give birth to froglets.\n" ] } }