├── exec
├── src
│ └── main
│ │ ├── resources
│ │ ├── META-INF
│ │ │ └── services
│ │ │ │ └── uk.ac.ebi.beam.CmdLnModule
│ │ └── stub.sh
│ │ └── java
│ │ └── uk
│ │ └── ac
│ │ └── ebi
│ │ └── beam
│ │ ├── Aromatise.java
│ │ ├── CmdLnModule.java
│ │ ├── Main.java
│ │ ├── Anonymise.java
│ │ └── Shuffle.java
├── make-stub.sh
└── pom.xml
├── core
├── src
│ ├── test
│ │ └── java
│ │ │ └── uk
│ │ │ └── ac
│ │ │ └── ebi
│ │ │ └── beam
│ │ │ ├── AtomTest.java
│ │ │ ├── IntSetTest.java
│ │ │ ├── BiconnectedComponentsTest.java
│ │ │ ├── ArbitraryMatchingTest.java
│ │ │ ├── ElectronAssignmentTest.java
│ │ │ ├── MatchingTest.java
│ │ │ ├── ParsingHydrogenCountTest.java
│ │ │ ├── UnionFindTest.java
│ │ │ ├── ParsingAtomClassTest.java
│ │ │ ├── AtomCentricDBConfigTest.java
│ │ │ ├── ParsingAtomChargeTest.java
│ │ │ ├── IntStackTest.java
│ │ │ ├── BondTest.java
│ │ │ ├── EdgeTest.java
│ │ │ ├── ElementTest.java
│ │ │ ├── MaximumMatchingTest.java
│ │ │ ├── ParsingBracketAtomTest.java
│ │ │ └── ConfigurationTest.java
│ └── main
│ │ ├── java
│ │ └── uk
│ │ │ └── ac
│ │ │ └── ebi
│ │ │ └── beam
│ │ │ ├── Function.java
│ │ │ ├── Matching.java
│ │ │ ├── Tuple.java
│ │ │ ├── IntStack.java
│ │ │ ├── InvalidSmilesException.java
│ │ │ ├── UnionFind.java
│ │ │ ├── BiconnectedComponents.java
│ │ │ ├── ElectronAssignment.java
│ │ │ ├── Atom.java
│ │ │ ├── IntSet.java
│ │ │ ├── ArbitraryMatching.java
│ │ │ └── Edge.java
│ │ └── resources
│ │ └── uk
│ │ └── ac
│ │ └── ebi
│ │ └── beam
│ │ └── element-defaults.txt
└── pom.xml
├── func
├── src
│ ├── main
│ │ └── java
│ │ │ └── uk
│ │ │ └── ac
│ │ │ └── ebi
│ │ │ └── beam
│ │ │ ├── BondBasedConfiguration.java
│ │ │ ├── RenumberAtomMaps.java
│ │ │ ├── ToSubsetAtoms.java
│ │ │ ├── FromSubsetAtoms.java
│ │ │ ├── AbstractFunction.java
│ │ │ ├── ImplicitToExplicit.java
│ │ │ ├── ExplicitToImplicit.java
│ │ │ ├── FromTrigonalTopology.java
│ │ │ └── Functions.java
│ └── test
│ │ └── java
│ │ └── uk
│ │ └── ac
│ │ └── ebi
│ │ └── beam
│ │ ├── BondBasedConfigurationTest.java
│ │ ├── FunctionsTest.java
│ │ ├── ExplicitToImplicitTest.java
│ │ ├── ToSubsetAtomsTest.java
│ │ ├── RemoveUpDownBondsTest.java
│ │ ├── NormaliseDirectionalLabelsTest.java
│ │ ├── FromSubsetAtomsTest.java
│ │ ├── FromTrigonalTopologyTest.java
│ │ └── ToTrigonalTopologyTest.java
└── pom.xml
├── .github
└── workflows
│ └── build.yml
└── LICENSE
/exec/src/main/resources/META-INF/services/uk.ac.ebi.beam.CmdLnModule:
--------------------------------------------------------------------------------
1 | uk.ac.ebi.beam.Anonymise
2 | uk.ac.ebi.beam.Aromatise
3 | uk.ac.ebi.beam.Shuffle
4 |
--------------------------------------------------------------------------------
/exec/src/main/resources/stub.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | MYSELF="${BASH_SOURCE[0]}"
3 | [ $? -gt 0 -a -f "$0" ] && MYSELF="./$0"
4 | java=java
5 | if test -n "$JAVA_HOME"; then
6 | java="$JAVA_HOME/bin/java"
7 | fi
8 | exec "$java" $JAVA_OPTS -jar $MYSELF "$@"
9 | exit 1
--------------------------------------------------------------------------------
/exec/make-stub.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | SOURCE="${BASH_SOURCE[0]}"
4 | while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
5 | DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
6 | SOURCE="$(readlink "$SOURCE")"
7 | [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
8 | done
9 | DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
10 |
11 | # Builds self contained unix executable
12 | cat ${DIR}/src/main/resources/stub.sh ${DIR}/target/beam.jar > ${DIR}/target/beam && chmod +x ${DIR}/target/beam
13 | cp ${DIR}/target/beam /usr/local/bin/beam
--------------------------------------------------------------------------------
/exec/src/main/java/uk/ac/ebi/beam/Aromatise.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2015. John May
3 | */
4 |
5 | package uk.ac.ebi.beam;
6 |
7 | import joptsimple.OptionSet;
8 |
9 | import java.io.IOException;
10 |
11 | /**
12 | * Simple module simply Kekulises then emits and normalised
13 | * (by beam's model) aromatic form of the SMILES.
14 | */
15 | public final class Aromatise extends FunctorCmdLnModule {
16 |
17 | public Aromatise() {
18 | super("arom");
19 | }
20 |
21 | @Override
22 | Functor createFunctor(OptionSet optionSet) {
23 | return new Functor() {
24 | @Override
25 | String map(String str) throws IOException {
26 | return Graph.fromSmiles(str).kekule().aromatic().toSmiles() + suffixedId(str);
27 | }
28 | };
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/core/src/test/java/uk/ac/ebi/beam/AtomTest.java:
--------------------------------------------------------------------------------
1 | package uk.ac.ebi.beam;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.hamcrest.CoreMatchers.is;
6 | import static org.junit.Assert.assertThat;
7 |
8 | /** @author John May */
9 | public class AtomTest {
10 |
11 | @Test public void aliphaticSubsetFromElement() {
12 | for (Atom a : AtomImpl.AliphaticSubset.values()) {
13 | assertThat(AtomImpl.AliphaticSubset.ofElement(a.element()), is(a));
14 | }
15 | }
16 |
17 | @Test(expected = IllegalArgumentException.class)
18 | public void aliphaticSubsetInvalidElement() {
19 | AtomImpl.AliphaticSubset.ofElement(Element.Californium);
20 | }
21 |
22 | @Test public void aromaticSubsetFromElement() {
23 | for (Atom a : AtomImpl.AromaticSubset.values()) {
24 | assertThat(AtomImpl.AromaticSubset.ofElement(a.element()), is(a));
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/func/src/main/java/uk/ac/ebi/beam/BondBasedConfiguration.java:
--------------------------------------------------------------------------------
1 | package uk.ac.ebi.beam;
2 |
3 | /** @author John May */
4 | final class BondBasedConfiguration {
5 |
6 | static Configuration.DoubleBond configurationOf(Graph g,
7 | int x, int u, int v, int y) {
8 |
9 | Edge e = g.edge(u, v);
10 |
11 | if (e.bond() != Bond.DOUBLE)
12 | throw new IllegalArgumentException("atoms u,v are not labelled as a double bond");
13 |
14 | Edge e1 = g.edge(u, x);
15 | Edge e2 = g.edge(v, y);
16 |
17 | Bond b1 = e1.bond(u);
18 | Bond b2 = e2.bond(v);
19 |
20 | if (b1 == Bond.IMPLICIT || b1 == Bond.SINGLE)
21 | return Configuration.DoubleBond.UNSPECIFIED;
22 | if (b2 == Bond.IMPLICIT || b2 == Bond.SINGLE)
23 | return Configuration.DoubleBond.UNSPECIFIED;
24 |
25 | return b1 == b2 ? Configuration.DoubleBond.TOGETHER
26 | : Configuration.DoubleBond.OPPOSITE;
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/core/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
{@code $ beam {module.name} {module.args}}
22 | *
23 | * @return module name
24 | */
25 | String name();
26 |
27 | /**
28 | * Displays the usage/help for this module.
29 | *
30 | * @return help info
31 | */
32 | String getHelpInfo();
33 |
34 | /**
35 | * Executes the module with the specified arguments. The
36 | * arguments do not include the module name.
37 | *
38 | * @param args command line arguments
39 | */
40 | void exec(String[] args);
41 | }
42 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: Build
2 | on:
3 | push:
4 | branches:
5 | - master
6 | pull_request:
7 | types: [opened, synchronize, reopened]
8 | jobs:
9 | build:
10 | name: Build
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: actions/checkout@v2
14 | with:
15 | fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
16 | - name: Set up JDK 11
17 | uses: actions/setup-java@v1
18 | with:
19 | java-version: 11
20 | - name: Cache SonarCloud packages
21 | uses: actions/cache@v1
22 | with:
23 | path: ~/.sonar/cache
24 | key: ${{ runner.os }}-sonar
25 | restore-keys: ${{ runner.os }}-sonar
26 | - name: Cache Maven packages
27 | uses: actions/cache@v1
28 | with:
29 | path: ~/.m2
30 | key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
31 | restore-keys: ${{ runner.os }}-m2
32 | - name: Build and analyze
33 | env:
34 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
35 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
36 | run: mvn -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=johnmay_beam -Pcoverage
--------------------------------------------------------------------------------
/func/src/main/java/uk/ac/ebi/beam/RenumberAtomMaps.java:
--------------------------------------------------------------------------------
1 | package uk.ac.ebi.beam;
2 |
3 | final class RenumberAtomMaps {
4 |
5 | private static final class State {
6 | Graph g;
7 | boolean[] visit;
8 | int[] map;
9 | int nMaps;
10 |
11 | public State(Graph g, int maxidx) {
12 | this.g = g;
13 | this.visit = new boolean[g.order()];
14 | this.map = new int[maxidx + 1];
15 | this.nMaps = 0;
16 | }
17 | }
18 |
19 | private static void traverse(State s, int idx) {
20 | s.visit[idx] = true;
21 | int mapIdx = s.g.atom(idx).atomClass();
22 | if (mapIdx != 0) {
23 | if (s.map[mapIdx] == 0)
24 | s.map[mapIdx] = ++s.nMaps;
25 | mapIdx = s.map[mapIdx];
26 | s.g.setAtom(idx, AtomBuilder.fromExisting(s.g.atom(idx)).atomClass(mapIdx).build());
27 | }
28 | for (Edge e : s.g.edges(idx)) {
29 | int nbr = e.other(idx);
30 | if (!s.visit[nbr])
31 | traverse(s, nbr);
32 | }
33 | }
34 |
35 | static void renumber(Graph g) {
36 | int maxMapIdx = 0;
37 | for (int i = 0; i < g.order(); i++)
38 | maxMapIdx = Math.max(maxMapIdx, g.atom(i).atomClass());
39 | if (maxMapIdx == 0)
40 | return;
41 | State state = new State(g, maxMapIdx);
42 | for (int i = 0; i < g.order(); i++)
43 | if (!state.visit[i])
44 | traverse(state, i);
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2013, European Bioinformatics Institute (EMBL-EBI)
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions are met:
6 |
7 | 1. Redistributions of source code must retain the above copyright notice, this
8 | list of conditions and the following disclaimer.
9 | 2. Redistributions in binary form must reproduce the above copyright notice,
10 | this list of conditions and the following disclaimer in the documentation
11 | and/or other materials provided with the distribution.
12 |
13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
17 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
20 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 |
24 | The views and conclusions contained in the software and documentation are those
25 | of the authors and should not be interpreted as representing official policies,
26 | either expressed or implied, of the FreeBSD Project.
--------------------------------------------------------------------------------
/func/src/test/java/uk/ac/ebi/beam/BondBasedConfigurationTest.java:
--------------------------------------------------------------------------------
1 | package uk.ac.ebi.beam;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.hamcrest.CoreMatchers.is;
6 | import static org.junit.Assert.assertThat;
7 |
8 | /** @author John May */
9 | public class BondBasedConfigurationTest {
10 |
11 | @Test(expected = IllegalArgumentException.class)
12 | public void nonDoubleBond() throws Exception {
13 | Graph g = Graph.fromSmiles("CCCC");
14 | BondBasedConfiguration.configurationOf(g, 0, 1, 2, 3);
15 | }
16 |
17 | @Test
18 | public void opposite1() throws Exception {
19 | Graph g = Graph.fromSmiles("F/C=C/F");
20 | assertThat(BondBasedConfiguration.configurationOf(g, 0, 1, 2, 3),
21 | is(Configuration.DoubleBond.OPPOSITE));
22 | }
23 |
24 | @Test
25 | public void opposite2() throws Exception {
26 | Graph g = Graph.fromSmiles("F\\C=C\\F");
27 | assertThat(BondBasedConfiguration.configurationOf(g, 0, 1, 2, 3),
28 | is(Configuration.DoubleBond.OPPOSITE));
29 | }
30 |
31 | @Test
32 | public void together1() throws Exception {
33 | Graph g = Graph.fromSmiles("F/C=C\\F");
34 | assertThat(BondBasedConfiguration.configurationOf(g, 0, 1, 2, 3),
35 | is(Configuration.DoubleBond.TOGETHER));
36 | }
37 |
38 | @Test
39 | public void together2() throws Exception {
40 | Graph g = Graph.fromSmiles("F\\C=C/F");
41 | assertThat(BondBasedConfiguration.configurationOf(g, 0, 1, 2, 3),
42 | is(Configuration.DoubleBond.TOGETHER));
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/exec/src/main/java/uk/ac/ebi/beam/Main.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2015. John May
3 | */
4 |
5 | package uk.ac.ebi.beam;
6 |
7 | import java.util.*;
8 |
9 | public class Main {
10 |
11 | private final static Map Function with(Function g) {
58 | return new Composition(this, g);
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/core/src/test/java/uk/ac/ebi/beam/ElectronAssignmentTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013, European Bioinformatics Institute (EMBL-EBI)
3 | * All rights reserved.
4 | *
5 | * Redistribution and use in source and binary forms, with or without
6 | * modification, are permitted provided that the following conditions are met:
7 | *
8 | * 1. Redistributions of source code must retain the above copyright notice, this
9 | * list of conditions and the following disclaimer.
10 | * 2. Redistributions in binary form must reproduce the above copyright notice,
11 | * this list of conditions and the following disclaimer in the documentation
12 | * and/or other materials provided with the distribution.
13 | *
14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
18 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
23 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 | *
25 | * The views and conclusions contained in the software and documentation are those
26 | * of the authors and should not be interpreted as representing official policies,
27 | * either expressed or implied, of the FreeBSD Project.
28 | */
29 |
30 | package uk.ac.ebi.beam;
31 |
32 | import org.junit.Assert;
33 | import org.junit.Test;
34 |
35 | import java.io.IOException;
36 |
37 | /** @author John May */
38 | public class ElectronAssignmentTest {
39 |
40 | @Test public void biphenylLike() throws Exception {
41 | assertAssignable("c1ccccc1-c1ccccc1");
42 | assertUnassignable("c1ccccc1=c1ccccc1"); // n.b. daylight will assign this - for now we say it's invalid
43 | assertAssignable("c1ccccc1c1ccccc1");
44 | }
45 |
46 | @Test public void fulvaleneLike() throws Exception {
47 | assertAssignable("c1cccc1-c1cccc1");
48 | assertAssignable("c1cccc1=c1cccc1");
49 | assertAssignable("c1cccc1c1cccc1");
50 | }
51 |
52 | @Test public void cyclopentadiene() throws Exception {
53 | assertUnassignable("c1cccc1");
54 | assertAssignable("c1cCcc1");
55 | }
56 |
57 | static void assertAssignable(String smi) throws IOException {
58 | Assert.assertTrue(ElectronAssignment.verify(Graph.fromSmiles(smi)));
59 | }
60 |
61 | static void assertUnassignable(String smi) throws IOException {
62 | Assert.assertFalse(ElectronAssignment.verify(Graph.fromSmiles(smi)));
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/core/src/test/java/uk/ac/ebi/beam/MatchingTest.java:
--------------------------------------------------------------------------------
1 | package uk.ac.ebi.beam;
2 |
3 | import org.hamcrest.collection.IsIterableWithSize;
4 | import org.junit.Test;
5 |
6 | import static org.hamcrest.CoreMatchers.hasItems;
7 | import static org.hamcrest.CoreMatchers.is;
8 | import static org.hamcrest.CoreMatchers.not;
9 | import static org.junit.Assert.assertFalse;
10 | import static org.junit.Assert.assertThat;
11 | import static org.junit.Assert.assertTrue;
12 |
13 | /** @author John May */
14 | public class MatchingTest {
15 |
16 | @Test public void empty() throws Exception {
17 | Matching matching = Matching.empty(Graph.fromSmiles("CCCCC"));
18 | assertThat(matching.matches(),
19 | IsIterableWithSize.
62 | *
63 | * @param buffer a character buffer
64 | * @return a 3 line string showing the buffer and it's current position
65 | */
66 | static String display(final CharBuffer buffer, int offset) {
67 | StringBuilder sb = new StringBuilder();
68 | sb.append('\n');
69 | sb.append(buffer);
70 | sb.append('\n');
71 | for (int i = 1; i < (buffer.position() + offset); i++)
72 | sb.append(' ');
73 | sb.append('^');
74 | return sb.toString();
75 | }
76 |
77 | static String display(final CharBuffer buffer, int offset, int offset2) {
78 | StringBuilder sb = new StringBuilder();
79 | sb.append('\n');
80 | sb.append(buffer);
81 | sb.append('\n');
82 | for (int i = 1; i < buffer.length(); i++) {
83 | if (i == buffer.position+offset || i == buffer.position+offset2)
84 | sb.append('^');
85 | else
86 | sb.append(' ');
87 | }
88 | return sb.toString();
89 | }
90 |
91 | /**
92 | * Utility for invalid bracket atom error.
93 | *
94 | * @param buffer the current buffer
95 | * @return the invalid smiles exception with buffer information
96 | */
97 | static InvalidSmilesException invalidBracketAtom(CharBuffer buffer) {
98 | return new InvalidSmilesException("Invalid bracket atom, [
58 | * invalid bracket atom:
59 | * C[CCCC
60 | * ^
61 | *
38 | * UnionFind uf = new UnionFind(11);
39 | * uf.join(0, 1);
40 | * uf.join(1, 10);
41 | * uf.connected(0, 10); // are 0 and 10 joint?
42 | * uf.find(10); // id for the set to which '10' belongs
43 | *
44 | *
45 | * @author John May
46 | */
47 | final class UnionFind {
48 |
49 | /**
50 | * Each element is either a connected (negative), points to another element.
51 | * The size of the set is indicated by the size of the negation on the
52 | * connected.
53 | */
54 | final int[] forest;
55 |
56 | /**
57 | * Create a new UnionFind data structure with enough space for 'n'
58 | * elements.
59 | *
60 | * @param n number of elements
61 | */
62 | UnionFind(int n) {
63 | this.forest = new int[n];
64 | Arrays.fill(forest, -1);
65 | }
66 |
67 | /**
68 | * Find the identifier of the set to which 'u' belongs.
69 | *
70 | * @param u an element
71 | * @return the connected
72 | */
73 | int find(int u) {
74 | return forest[u] < 0 ? u : (forest[u] = find(forest[u]));
75 | }
76 |
77 | /**
78 | * Join the sets containing 'u' and 'v'.
79 | *
80 | * @param u an element
81 | * @param v another element
82 | */
83 | void union(int u, int v) {
84 |
85 | int uRoot = find(u);
86 | int vRoot = find(v);
87 |
88 | if (uRoot == vRoot)
89 | return;
90 |
91 | if (forest[uRoot] < forest[vRoot])
92 | join(vRoot, uRoot);
93 | else
94 | join(uRoot, vRoot);
95 | }
96 |
97 | /**
98 | * Join two disjoint sets. The larger set is appended onto the smaller set.
99 | *
100 | * @param sRoot root of a set (small)
101 | * @param lRoot root of another set (large)
102 | */
103 | private void join(int sRoot, int lRoot) {
104 | forest[sRoot] = forest[sRoot] + forest[lRoot];
105 | forest[lRoot] = sRoot;
106 | }
107 |
108 | /**
109 | * Are the elements 'u' and 'v' in the same set.
110 | *
111 | * @param u an element
112 | * @param v another element
113 | * @return the elements are in the same set.
114 | */
115 | boolean connected(int u, int v) {
116 | return find(u) == find(v);
117 | }
118 |
119 | /**
120 | * Clear any joint sets - all items are once disjoint and are singletons.
121 | */
122 | void clear() {
123 | for (int i = 0; i < forest.length; i++)
124 | forest[i] = -1;
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/core/src/test/java/uk/ac/ebi/beam/AtomCentricDBConfigTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013, European Bioinformatics Institute (EMBL-EBI)
3 | * All rights reserved.
4 | *
5 | * Redistribution and use in source and binary forms, with or without
6 | * modification, are permitted provided that the following conditions are met:
7 | *
8 | * 1. Redistributions of source code must retain the above copyright notice, this
9 | * list of conditions and the following disclaimer.
10 | * 2. Redistributions in binary form must reproduce the above copyright notice,
11 | * this list of conditions and the following disclaimer in the documentation
12 | * and/or other materials provided with the distribution.
13 | *
14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
18 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
23 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 | *
25 | * The views and conclusions contained in the software and documentation are those
26 | * of the authors and should not be interpreted as representing official policies,
27 | * either expressed or implied, of the FreeBSD Project.
28 | */
29 |
30 | package uk.ac.ebi.beam;
31 |
32 | import org.junit.Assert;
33 | import org.junit.Test;
34 |
35 | import static org.hamcrest.CoreMatchers.is;
36 |
37 | /** @author John May */
38 | public class AtomCentricDBConfigTest {
39 |
40 | @Test public void difluoroethene_implConfig() throws
41 | InvalidSmilesException {
42 | String input = "F[C@H]=[C@H]F";
43 | Graph g = Parser.parse(input);
44 | Assert.assertThat(g.topologyOf(1)
45 | .configuration(), is(Configuration.DB1));
46 | Assert.assertThat(g.topologyOf(2)
47 | .configuration(), is(Configuration.DB1));
48 | }
49 |
50 | @Test public void difluoroethene_implConfig2() throws
51 | InvalidSmilesException {
52 | String input = "F[C@@H]=[C@@H]F";
53 | Graph g = Parser.parse(input);
54 | Assert.assertThat(g.topologyOf(1)
55 | .configuration(), is(Configuration.DB2));
56 | Assert.assertThat(g.topologyOf(2)
57 | .configuration(), is(Configuration.DB2));
58 | }
59 |
60 | @Test public void difluoroethene_expConfig() throws InvalidSmilesException {
61 | String input = "F[C@DB1H]=[C@DB1H]F";
62 | Graph g = Parser.parse(input);
63 | Assert.assertThat(g.topologyOf(1)
64 | .configuration(), is(Configuration.DB1));
65 | Assert.assertThat(g.topologyOf(2)
66 | .configuration(), is(Configuration.DB1));
67 | }
68 |
69 | @Test public void difluoroethene_expConfig2() throws
70 | InvalidSmilesException {
71 | String input = "F[C@DB2H]=[C@DB2H]F";
72 | Graph g = Parser.parse(input);
73 | Assert.assertThat(g.topologyOf(1)
74 | .configuration(), is(Configuration.DB2));
75 | Assert.assertThat(g.topologyOf(2)
76 | .configuration(), is(Configuration.DB2));
77 | }
78 |
79 | @Test public void difluoroethene() throws InvalidSmilesException {
80 | GeneratorTest.assertRoundTrip("F[C@H]=[C@H]F");
81 | GeneratorTest.assertRoundTrip("F[C@@H]=[C@@H]F");
82 | GeneratorTest.assertRoundTrip("F[C@H]=[C@@H]F");
83 | GeneratorTest.assertRoundTrip("F[C@@H]=[C@H]F");
84 | }
85 |
86 | @Test public void difluoroethene_permute() throws InvalidSmilesException {
87 | GeneratorTest.assertRoundTrip("F[C@H]=[C@H]F",
88 | new int[]{1, 0, 2, 3},
89 | "[C@@H](F)=[C@H]F");
90 | GeneratorTest.assertRoundTrip("[C@@H](F)=[C@H]F",
91 | new int[]{1, 0, 2, 3},
92 | "F[C@H]=[C@H]F");
93 | }
94 |
95 | }
96 |
--------------------------------------------------------------------------------
/core/src/main/java/uk/ac/ebi/beam/BiconnectedComponents.java:
--------------------------------------------------------------------------------
1 | package uk.ac.ebi.beam;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Arrays;
5 | import java.util.BitSet;
6 | import java.util.Collections;
7 | import java.util.List;
8 |
9 | /**
10 | * see. http://en.wikipedia.org/wiki/Biconnected_component
11 | *
12 | * @author John May
13 | */
14 | final class BiconnectedComponents {
15 |
16 | private int[] depth;
17 |
18 | private final Graph g;
19 | private final Edge[] stack;
20 | private int nstack = 0;
21 |
22 | private final List> components = new ArrayList
>(2);
23 |
24 | private final BitSet cyclic = new BitSet();
25 | private final BitSet simple = new BitSet();
26 |
27 | int count = 0;
28 | int numfrags = 0;
29 |
30 | BiconnectedComponents(Graph g) {
31 | this(g, true);
32 | }
33 |
34 | BiconnectedComponents(Graph g, boolean storeComponents) {
35 | this.depth = new int[g.order()];
36 | this.g = g;
37 | this.stack = new Edge[g.size()];
38 |
39 | if (storeComponents) {
40 | for (int u = 0; count < g.order(); u++) {
41 | if (depth[u] == 0) {
42 | visitWithComp(u, null);
43 | ++numfrags;
44 | }
45 | }
46 | } else {
47 | for (int u = 0; count < g.order(); u++) {
48 | if (depth[u] == 0) {
49 | visit(u, null);
50 | ++numfrags;
51 | }
52 | }
53 | }
54 | }
55 |
56 | private int visit(final int u, final Edge from) {
57 | depth[u] = ++count;
58 | int d = g.degree(u);
59 | int lo = count + 1;
60 |
61 | while (--d>=0) {
62 | final Edge e = g.edgeAt(u, d);
63 | if (e==from) continue;
64 | final int v = e.other(u);
65 | if (depth[v] == 0) {
66 | int res = visit(v, e);
67 | if (res < lo)
68 | lo = res;
69 | }
70 | else if (depth[v] < lo) {
71 | lo = depth[v];
72 | }
73 | }
74 | if (lo <= depth[u])
75 | cyclic.set(u);
76 | return lo;
77 | }
78 |
79 | private int visitWithComp(final int u, final Edge from) {
80 | depth[u] = ++count;
81 | int j = g.degree(u);
82 | int lo = count + 1;
83 | while (--j>=0) {
84 |
85 | final Edge e = g.edgeAt(u, j);
86 | if (e==from) continue;
87 |
88 | final int v = e.other(u);
89 | if (depth[v] == 0) {
90 | stack[nstack] = e;
91 | ++nstack;
92 | int tmp = visitWithComp(v, e);
93 | if (tmp == depth[u])
94 | storeWithComp(e);
95 | else if (tmp > depth[u])
96 | --nstack;
97 | if (tmp < lo)
98 | lo = tmp;
99 | }
100 | else if (depth[v] < depth[u]) {
101 | // back edge
102 | stack[nstack] = e;
103 | ++nstack;
104 | if (depth[v] < lo)
105 | lo = depth[v];
106 | }
107 | }
108 | return lo;
109 | }
110 |
111 | private void storeWithComp(Edge e) {
112 | List
> components() {
145 | return Collections.unmodifiableList(components);
146 | }
147 |
148 | BitSet cyclic() {
149 | return cyclic;
150 | }
151 |
152 | public boolean connected() {
153 | return numfrags < 2;
154 | }
155 | }
156 |
--------------------------------------------------------------------------------
/core/src/test/java/uk/ac/ebi/beam/ParsingAtomChargeTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013, European Bioinformatics Institute (EMBL-EBI)
3 | * All rights reserved.
4 | *
5 | * Redistribution and use in source and binary forms, with or without
6 | * modification, are permitted provided that the following conditions are met:
7 | *
8 | * 1. Redistributions of source code must retain the above copyright notice, this
9 | * list of conditions and the following disclaimer.
10 | * 2. Redistributions in binary form must reproduce the above copyright notice,
11 | * this list of conditions and the following disclaimer in the documentation
12 | * and/or other materials provided with the distribution.
13 | *
14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
18 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
23 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 | *
25 | * The views and conclusions contained in the software and documentation are those
26 | * of the authors and should not be interpreted as representing official policies,
27 | * either expressed or implied, of the FreeBSD Project.
28 | */
29 |
30 | package uk.ac.ebi.beam;
31 |
32 | import org.junit.Test;
33 |
34 | import static org.hamcrest.CoreMatchers.is;
35 | import static org.junit.Assert.assertThat;
36 | import static org.junit.Assert.assertTrue;
37 |
38 | /**
39 | * Unit tests verify correct handling of charge for bracket atoms.
40 | *
41 | * @author John May
42 | */
43 | public class ParsingAtomChargeTest {
44 |
45 | @Test public void implicitPlusOne() {
46 | verify("+", +1);
47 | }
48 |
49 | @Test public void implicitPlusTwo() {
50 | verify("++", +2);
51 | }
52 |
53 | @Test public void implicitPlusThree() {
54 | verify("+++", +3);
55 | }
56 |
57 | @Test public void implicitPlusFour() {
58 | verify("++++", +4);
59 | }
60 |
61 | @Test public void implicitMinusOne() {
62 | verify("-", -1);
63 | }
64 |
65 | @Test public void implicitMinusTwo() {
66 | verify("--", -2);
67 | }
68 |
69 | @Test public void implicitMinusThree() {
70 | verify("---", -3);
71 | }
72 |
73 | @Test public void implicitMinusFour() {
74 | verify("----", -4);
75 | }
76 |
77 | @Test public void plusOne() {
78 | verify("+1", +1);
79 | }
80 |
81 | @Test public void plusTwo() {
82 | verify("+2", +2);
83 | }
84 |
85 | @Test public void minusOne() {
86 | verify("-1", -1);
87 | }
88 |
89 | @Test public void minusTwo() {
90 | verify("-2", -2);
91 | }
92 |
93 | @Test public void noCharge() {
94 | CharBuffer buffer = CharBuffer.fromString(":");
95 | assertThat(Parser.readCharge(buffer), is(0));
96 | assertTrue(buffer.nextIs(':'));
97 | }
98 |
99 | // really bad form but parsed okay
100 | @Test public void minusPlusOne() {
101 | verify("-+1", 0);
102 | }
103 |
104 | // really bad form but parsed okay
105 | @Test public void plusPlusMinusOne() {
106 | verify("++-1", +1);
107 | }
108 |
109 | // really bad form but parsed okay
110 | @Test public void minusMinusPlusOne() {
111 | verify("--+1", -1);
112 | }
113 |
114 | // really bad form but parsed okay
115 | @Test public void plusMinusOne() {
116 | verify("+-1", 0);
117 | }
118 |
119 | // really bad form but parsed okay
120 | @Test public void plusPlusOne() {
121 | verify("++1", 2);
122 | }
123 |
124 | // really bad form but parsed okay
125 | @Test public void plusPlusTwo() {
126 | verify("++2", 3);
127 | }
128 |
129 | // An implementation is required to accept charges in the range -15 to +15
130 | @Test public void rangeCheck() {
131 | for (int i = -15; i <= 15; i++)
132 | verify((i > 0 ? "+" : "") + Integer.toString(i), i);
133 | }
134 |
135 | private void verify(String str, int charge) {
136 | assertThat(Parser.readCharge(CharBuffer.fromString(str)), is(charge));
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/func/src/test/java/uk/ac/ebi/beam/RemoveUpDownBondsTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013, European Bioinformatics Institute (EMBL-EBI)
3 | * All rights reserved.
4 | *
5 | * Redistribution and use in source and binary forms, with or without
6 | * modification, are permitted provided that the following conditions are met:
7 | *
8 | * 1. Redistributions of source code must retain the above copyright notice, this
9 | * list of conditions and the following disclaimer.
10 | * 2. Redistributions in binary form must reproduce the above copyright notice,
11 | * this list of conditions and the following disclaimer in the documentation
12 | * and/or other materials provided with the distribution.
13 | *
14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
18 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
23 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 | *
25 | * The views and conclusions contained in the software and documentation are those
26 | * of the authors and should not be interpreted as representing official policies,
27 | * either expressed or implied, of the FreeBSD Project.
28 | */
29 |
30 | package uk.ac.ebi.beam;
31 |
32 | import org.hamcrest.CoreMatchers;
33 | import org.junit.Assert;
34 | import org.junit.Test;
35 |
36 | import static org.hamcrest.CoreMatchers.is;
37 |
38 | /** @author John May */
39 | public class RemoveUpDownBondsTest {
40 |
41 | @Test public void e_butene_expH() throws Exception {
42 | transform("C\\C(\\[H])=C\\C",
43 | "C\\C([H])=C\\C");
44 | transform("C/C(/[H])=C/C",
45 | "C/C([H])=C/C");
46 | transform("C\\C(\\[H])=C(/[H])\\C",
47 | "C\\C([H])=C(/[H])C");
48 | transform("C/C(/[H])=C(\\[H])/C",
49 | "C/C([H])=C(\\[H])C");
50 | }
51 |
52 | @Test public void z_butene_expH() throws Exception {
53 | transform("C\\C(\\[H])=C/C",
54 | "C\\C([H])=C/C");
55 | transform("C/C(/[H])=C\\C",
56 | "C/C([H])=C\\C");
57 | transform("C\\C(\\[H])=C(\\[H])/C",
58 | "C\\C([H])=C(\\[H])C");
59 | transform("C/C(/[H])=C(/[H])\\C",
60 | "C/C([H])=C(/[H])C");
61 | }
62 |
63 | @Test public void e_e_hexadiene_expH() throws InvalidSmilesException {
64 | transform("C\\C(\\[H])=C(/[H])\\C(\\[H])=C(/[H])\\C",
65 | "C\\C([H])=C(/[H])\\C([H])=C(/[H])C");
66 | }
67 |
68 | @Test public void e_e_hexadiene_expH2() throws Exception {
69 | transform("[H]\\C(\\C(\\[H])=C(\\C)/[H])=C(/C)\\[H]",
70 | "[H]\\C(\\C([H])=C(\\C)[H])=C(/C)[H]");
71 | }
72 |
73 | @Test public void e_e_hexadiene_expH3() throws Exception {
74 | transform("[H]/C(/C(=C(/C)\\[H])[H])=C(/[H])\\C",
75 | "[H]/C(/C(=C(/C)[H])[H])=C(/[H])C");
76 | }
77 |
78 | @Test public void e_e_hexadiene_expH4() throws Exception {
79 | transform("C\\C(\\[H])=C(/[H])\\C(=C(/[H])\\C)\\[H]",
80 | "C\\C([H])=C(/[H])\\C(=C(/[H])C)[H]");
81 | }
82 |
83 | @Test public void e_e_hexadiene_expH5() throws Exception {
84 | transform("[H]/C(/C)=C(/[H])\\C(\\[H])=C(/[H])\\C",
85 | "[H]/C(C)=C(/[H])\\C([H])=C(/[H])C");
86 | }
87 |
88 | @Test public void e_e_hexadiene_permute() throws Exception {
89 | String input = "C\\C(=C(\\C(=C(/[H])\\C)\\[H])/[H])\\[H]";
90 | int[] p = new int[]{7, 2, 4, 1, 3, 6, 8, 9, 0, 5};
91 | Graph g = Parser.parse(input);
92 | Assert.assertThat(Generator.generate(g.permute(p)),
93 | CoreMatchers
94 | .is("[H]\\C(\\C(=C(/[H])\\C)\\[H])=C(\\[H])/C"));
95 | Assert.assertThat(Generator.generate(new RemoveUpDownBonds().apply(g.permute(p))),
96 | CoreMatchers.is("[H]\\C(\\C(=C(/[H])C)[H])=C(\\[H])C"));
97 | }
98 |
99 | static void transform(String smi, String exp) throws
100 | InvalidSmilesException {
101 | Assert.assertThat(Generator.generate(new RemoveUpDownBonds()
102 | .apply(Parser.parse(smi))),
103 | CoreMatchers.is(exp));
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/core/src/main/java/uk/ac/ebi/beam/ElectronAssignment.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013, European Bioinformatics Institute (EMBL-EBI)
3 | * All rights reserved.
4 | *
5 | * Redistribution and use in source and binary forms, with or without
6 | * modification, are permitted provided that the following conditions are met:
7 | *
8 | * 1. Redistributions of source code must retain the above copyright notice, this
9 | * list of conditions and the following disclaimer.
10 | * 2. Redistributions in binary form must reproduce the above copyright notice,
11 | * this list of conditions and the following disclaimer in the documentation
12 | * and/or other materials provided with the distribution.
13 | *
14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
18 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
23 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 | *
25 | * The views and conclusions contained in the software and documentation are those
26 | * of the authors and should not be interpreted as representing official policies,
27 | * either expressed or implied, of the FreeBSD Project.
28 | */
29 |
30 | package uk.ac.ebi.beam;
31 |
32 | import java.util.BitSet;
33 |
34 | /**
35 | * Verifies delocalised electrons can be assigned to a structure without
36 | * changing bond orders. To check and assign the electrons please use {@see
37 | * Localise} or {@see Graph#kekule}. Although faster than assigning a Kekulé
38 | * structure the method is the same and returning a structure with specified
39 | * bond orders is usually preferred.
40 | *
41 | * @author John May
42 | * @see Localise
43 | * @see uk.ac.ebi.beam.Graph#kekule()
44 | */
45 | final class ElectronAssignment {
46 |
47 | private ElectronAssignment() {
48 | }
49 |
50 | /**
51 | * Check if it is possible to assign electrons to the subgraph (specified by
52 | * the set bits in of {@code bs}). Each connected subset is counted up and
53 | * checked for odd cardinality.
54 | *
55 | * @param g graph
56 | * @param bs binary set indicated vertices for the subgraph
57 | * @return there is an odd cardinality subgraph
58 | */
59 | private static boolean containsOddCardinalitySubgraph(Graph g, BitSet bs) {
60 |
61 | // mark visited those which are not in any subgraph
62 | boolean[] visited = new boolean[g.order()];
63 | for (int i = bs.nextClearBit(0); i < g.order(); i = bs.nextClearBit(i + 1))
64 | visited[i] = true;
65 |
66 | // from each unvisited vertices visit the connected vertices and count
67 | // how many there are in this component. if there is an odd number there
68 | // is no assignment of double bonds possible
69 | for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) {
70 | if (!visited[i] && isOdd(visit(g, i, 0, visited)))
71 | return true;
72 | }
73 |
74 | return false;
75 | }
76 |
77 |
78 | /**
79 | * Determine the size the connected component using a depth-first-search.
80 | *
81 | * @param g graph
82 | * @param v vertex
83 | * @param c count
84 | * @param visited which vertices have been visited
85 | * @return size of the component from {@code v}
86 | */
87 | private static int visit(Graph g, int v, int c, boolean[] visited) {
88 | visited[v] = true;
89 | for (final Edge e : g.edges(v)) {
90 | int w = e.other(v);
91 | if (!visited[w] && e.bond().order() == 1)
92 | c = visit(g, w, c, visited);
93 | }
94 | return 1 + c;
95 | }
96 |
97 | /**
98 | * Test if an a number, {@code x} is odd.
99 | *
100 | * @param x a number
101 | * @return the number is odd
102 | */
103 | private static boolean isOdd(int x) {
104 | return (x & 0x1) == 1;
105 | }
106 |
107 | /**
108 | * Utility method to verify electrons can be assigned.
109 | *
110 | * @param g graph to check
111 | * @return electrons could be assigned to delocalised structure
112 | */
113 | static boolean verify(Graph g) {
114 | return g.getFlags(Graph.HAS_AROM) == 0 || !containsOddCardinalitySubgraph(g, Localise.buildSet(g, new BitSet()));
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/core/src/main/java/uk/ac/ebi/beam/Atom.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013, European Bioinformatics Institute (EMBL-EBI)
3 | * All rights reserved.
4 | *
5 | * Redistribution and use in source and binary forms, with or without
6 | * modification, are permitted provided that the following conditions are met:
7 | *
8 | * 1. Redistributions of source code must retain the above copyright notice, this
9 | * list of conditions and the following disclaimer.
10 | * 2. Redistributions in binary form must reproduce the above copyright notice,
11 | * this list of conditions and the following disclaimer in the documentation
12 | * and/or other materials provided with the distribution.
13 | *
14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
18 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
23 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 | *
25 | * The views and conclusions contained in the software and documentation are those
26 | * of the authors and should not be interpreted as representing official policies,
27 | * either expressed or implied, of the FreeBSD Project.
28 | */
29 |
30 | package uk.ac.ebi.beam;
31 |
32 | /**
33 | * Defines properties of a atom that can be encoded in SMILES. Atoms can be
34 | * built using the {@link AtomBuilder} class.
35 | *
36 | * @author John May
37 | * @see AtomBuilder
38 | */
39 | public interface Atom {
40 |
41 | /**
42 | * The isotope number of the atom. If the isotope is undefined (default) a
43 | * value -1 is returned.
44 | *
45 | * @return isotope number
46 | */
47 | int isotope();
48 |
49 | /**
50 | * The element of the atom.
51 | *
52 | * @return element
53 | */
54 | Element element();
55 |
56 | /**
57 | * An label attached to an element (input only). Although invalid via the
58 | * specification 'CCC[R]' etc can occur in the 'wild'. If found the parser
59 | * provides an 'Unknown' element and a specified label. Not the labels are
60 | * never written. By default the label is the element symbol.
61 | *
62 | * @return the label in a bracket atom
63 | */
64 | String label();
65 |
66 | /**
67 | * Whether this atom is aromatic.
68 | *
69 | * @return atom is aromatic (true) or aliphatic (false)
70 | */
71 | boolean aromatic();
72 |
73 | /**
74 | * Formal charge of the atom.
75 | *
76 | * @return formal charge
77 | */
78 | int charge();
79 |
80 | /**
81 | * Number of hydrogens this atom has. This value defines atoms with an
82 | * explicit hydrogen count of bracket atoms (e.g. [CH4]).
83 | *
84 | * @return hydrogen count
85 | * @throws IllegalArgumentException thrown if element is part of the organic
86 | * subset and the number of hydrogens is
87 | * implied by the bond order sum.
88 | */
89 | int hydrogens();
90 |
91 | /**
92 | * The class of the atom is defined as an integer value. The atom class is
93 | * specified for bracketed atoms and is prefixed by a colon.
94 | *
95 | *
98 | *
99 | * @return class
100 | */
101 | int atomClass();
102 |
103 | /**
104 | * (internal) Is the atom a member of the organic (aromatic/aliphatic)
105 | * subset implementation?
106 | *
107 | * @return whether the atom is a subset - implementation
108 | */
109 | boolean subset();
110 |
111 | /**
112 | * Access an aromatic form of this atom. If the element can not be aromatic
113 | * then the same atom is returned.
114 | *
115 | * @return the aromatic form of this atom (or if it can't be aromatic just
116 | * this atom)
117 | */
118 | Atom toAromatic();
119 |
120 | /**
121 | * Access an aliphatic form of this atom.
122 | *
123 | * @return the aliphatic form of this atom
124 | */
125 | Atom toAliphatic();
126 |
127 | /**
128 | * (internal) the number of hydrogens this atom would have if it were vertex
129 | * 'u' in the graph 'g'. If the atom is in the organic subset the value is
130 | * computed - otherwise the labelled hydrogen count is returned.
131 | *
132 | * @see Graph#implHCount(int)
133 | */
134 | int hydrogens(Graph g, int u);
135 |
136 | /**
137 | * (internal) The token to write for the atom when generating a SMILES
138 | * string.
139 | *
140 | * @return the atom token
141 | */
142 | Generator.AtomToken token();
143 | }
144 |
--------------------------------------------------------------------------------
/core/src/main/java/uk/ac/ebi/beam/IntSet.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013, European Bioinformatics Institute (EMBL-EBI)
3 | * All rights reserved.
4 | *
5 | * Redistribution and use in source and binary forms, with or without
6 | * modification, are permitted provided that the following conditions are met:
7 | *
8 | * 1. Redistributions of source code must retain the above copyright notice, this
9 | * list of conditions and the following disclaimer.
10 | * 2. Redistributions in binary form must reproduce the above copyright notice,
11 | * this list of conditions and the following disclaimer in the documentation
12 | * and/or other materials provided with the distribution.
13 | *
14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
18 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
23 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 | *
25 | * The views and conclusions contained in the software and documentation are those
26 | * of the authors and should not be interpreted as representing official policies,
27 | * either expressed or implied, of the FreeBSD Project.
28 | */
29 |
30 | package uk.ac.ebi.beam;
31 |
32 | import java.util.BitSet;
33 |
34 | /**
35 | * Abstraction allows simple definitions of integer sets. Generally for this
36 | * library we are dealing with small bounded integer ranges (vertices of a
37 | * graph) which are most efficiently represented as a binary set. For
38 | * convenience the {@link #allOf(int, int...)} method can be used to construct a
39 | * binary set from varargs.
40 | *
41 | * @author John May
42 | */
43 | abstract class IntSet {
44 |
45 | /**
46 | * Determine if value 'x' is a member of this set.
47 | *
48 | * @param x a value to
49 | * @return x is included in this set.
50 | */
51 | abstract boolean contains(int x);
52 |
53 | /**
54 | * The universe is a set which includes every int value.
55 | *
56 | * @return int set with every item
57 | */
58 | static IntSet universe() {
59 | return UNIVERSE;
60 | }
61 |
62 | /**
63 | * The empty set is a set which includes no int values.
64 | *
65 | * @return int set with no items
66 | */
67 | static IntSet empty() {
68 | return complement(universe());
69 | }
70 |
71 | /**
72 | * Convenience method to create a set with the specified contents.
73 | *
74 | *
96 | * [CH:1](C)([C])[H:2]
97 | *
IntSet.allOf(0, 2, 5); // a set with 0,2 and 5
75 | *
76 | *
77 | * @param x a value
78 | * @param xs more values
79 | * @return int set with specified items
80 | */
81 | static IntSet allOf(int x, int... xs) {
82 | BitSet s = new BitSet();
83 | s.set(x);
84 | for (int v : xs)
85 | s.set(v);
86 | return new BinarySet(s);
87 | }
88 |
89 | /**
90 | * Convenience method to create a set without the specified contents.
91 | *
92 | * IntSet.noneOf(0, 2, 5); // a set with all but 0,2 and 5
93 | *
94 | *
95 | * @param x a value
96 | * @param xs more values
97 | * @return int set without the specified items
98 | */
99 | static IntSet noneOf(int x, int... xs) {
100 | return complement(allOf(x, xs));
101 | }
102 |
103 | /**
104 | * Create an set from a BitSet.
105 | *
106 | * @param s bitset
107 | * @return int set which uses the bit set to test for membership
108 | */
109 | static IntSet fromBitSet(BitSet s) {
110 | return new BinarySet((BitSet) s.clone());
111 | }
112 |
113 | /**
114 | * Make a complement of the specified set.
115 | *
116 | * @param set a set
117 | * @return complement of the set
118 | */
119 | private static IntSet complement(IntSet set) {
120 | return new Complement(set);
121 | }
122 |
123 | /** An integer set based on the contents of a bit set. */
124 | private static final class BinarySet extends IntSet {
125 | private final BitSet s;
126 |
127 | private BinarySet(BitSet s) {
128 | this.s = s;
129 | }
130 |
131 | @Override boolean contains(int x) {
132 | return s.get(x);
133 | }
134 | }
135 |
136 | /** Complement of a set - invert any membership of the provided 'delegate' */
137 | private static final class Complement extends IntSet {
138 | private final IntSet delegate;
139 |
140 | private Complement(IntSet delegate) {
141 | this.delegate = delegate;
142 | }
143 |
144 | @Override boolean contains(int x) {
145 | return !delegate.contains(x);
146 | }
147 | }
148 |
149 | /** The universe - every object is a member of the set. */
150 | private static final IntSet UNIVERSE = new IntSet() {
151 | @Override boolean contains(int x) {
152 | return true;
153 | }
154 | };
155 | }
156 |
--------------------------------------------------------------------------------
/core/src/test/java/uk/ac/ebi/beam/IntStackTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013, European Bioinformatics Institute (EMBL-EBI)
3 | * All rights reserved.
4 | *
5 | * Redistribution and use in source and binary forms, with or without
6 | * modification, are permitted provided that the following conditions are met:
7 | *
8 | * 1. Redistributions of source code must retain the above copyright notice, this
9 | * list of conditions and the following disclaimer.
10 | * 2. Redistributions in binary form must reproduce the above copyright notice,
11 | * this list of conditions and the following disclaimer in the documentation
12 | * and/or other materials provided with the distribution.
13 | *
14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
18 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
23 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 | *
25 | * The views and conclusions contained in the software and documentation are those
26 | * of the authors and should not be interpreted as representing official policies,
27 | * either expressed or implied, of the FreeBSD Project.
28 | */
29 |
30 | package uk.ac.ebi.beam;
31 |
32 | import org.junit.Test;
33 |
34 | import static org.hamcrest.CoreMatchers.is;
35 | import static org.junit.Assert.assertFalse;
36 | import static org.junit.Assert.assertThat;
37 | import static org.junit.Assert.assertTrue;
38 |
39 | /** @author John May */
40 | public class IntStackTest {
41 |
42 | @Test public void push() throws Exception {
43 | IntStack stack = new IntStack(4);
44 | stack.push(1);
45 | assertThat(stack.peek(), is(1));
46 | assertThat(stack.size(), is(1));
47 | stack.push(2);
48 | assertThat(stack.peek(), is(2));
49 | assertThat(stack.size(), is(2));
50 | stack.push(4);
51 | assertThat(stack.peek(), is(4));
52 | assertThat(stack.size(), is(3));
53 | }
54 |
55 | @Test public void pushWithResize() throws Exception {
56 | IntStack stack = new IntStack(1);
57 | stack.push(1);
58 | assertThat(stack.peek(), is(1));
59 | assertThat(stack.size(), is(1));
60 | stack.push(2);
61 | assertThat(stack.peek(), is(2));
62 | assertThat(stack.size(), is(2));
63 | stack.push(4);
64 | assertThat(stack.peek(), is(4));
65 | assertThat(stack.size(), is(3));
66 | }
67 |
68 | @Test public void pushDuplicate() throws Exception {
69 | IntStack stack = new IntStack(4);
70 | stack.push(1);
71 | assertThat(stack.peek(), is(1));
72 | assertThat(stack.size(), is(1));
73 | stack.push(stack.peek());
74 | assertThat(stack.peek(), is(1));
75 | assertThat(stack.size(), is(2));
76 | stack.push(stack.peek());
77 | assertThat(stack.peek(), is(1));
78 | assertThat(stack.size(), is(3));
79 | }
80 |
81 | @Test public void pop() throws Exception {
82 | IntStack stack = new IntStack(4);
83 | stack.push(1);
84 | stack.push(2);
85 | stack.push(3);
86 | assertThat(stack.pop(), is(3));
87 | assertThat(stack.pop(), is(2));
88 | assertThat(stack.pop(), is(1));
89 | }
90 |
91 | @Test public void popWithResize() throws Exception {
92 | IntStack stack = new IntStack(1);
93 | stack.push(1);
94 | stack.push(2);
95 | stack.push(3);
96 | assertThat(stack.pop(), is(3));
97 | assertThat(stack.pop(), is(2));
98 | assertThat(stack.pop(), is(1));
99 | }
100 |
101 | @Test public void empty() throws Exception {
102 | assertTrue(new IntStack(4).empty());
103 | }
104 |
105 | @Test public void nonEmpty() throws Exception {
106 | IntStack stack = new IntStack(4);
107 | assertTrue(stack.empty());
108 | stack.push(1);
109 | assertFalse(stack.empty());
110 | stack.pop();
111 | assertTrue(stack.empty());
112 | }
113 |
114 | @Test public void size() throws Exception {
115 | assertThat(new IntStack(4).size(), is(0));
116 | }
117 |
118 | @Test public void clear() throws Exception {
119 | IntStack stack = new IntStack(1);
120 | stack.push(1);
121 | assertThat(stack.peek(), is(1));
122 | assertThat(stack.size(), is(1));
123 | stack.push(2);
124 | assertThat(stack.peek(), is(2));
125 | assertThat(stack.size(), is(2));
126 | stack.push(4);
127 | assertThat(stack.peek(), is(4));
128 | assertThat(stack.size(), is(3));
129 | stack.clear();
130 | assertThat(stack.size(), is(0));
131 | stack.push(4);
132 | assertThat(stack.peek(), is(4));
133 | assertThat(stack.size(), is(1));
134 | stack.push(8);
135 | assertThat(stack.peek(), is(8));
136 | assertThat(stack.size(), is(2));
137 | stack.push(9);
138 | assertThat(stack.peek(), is(9));
139 | assertThat(stack.size(), is(3));
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/func/src/test/java/uk/ac/ebi/beam/NormaliseDirectionalLabelsTest.java:
--------------------------------------------------------------------------------
1 | package uk.ac.ebi.beam;
2 |
3 | import org.hamcrest.CoreMatchers;
4 | import org.junit.Assert;
5 | import org.junit.Ignore;
6 | import org.junit.Test;
7 |
8 | /** @author John May */
9 | public class NormaliseDirectionalLabelsTest {
10 |
11 | @Test public void simple() throws InvalidSmilesException {
12 | transform("F\\C=C\\F",
13 | "F/C=C/F");
14 | }
15 |
16 | @Test public void ordering2() throws InvalidSmilesException {
17 | transform("C(\\F)(/C)=C\\F",
18 | "C(/F)(\\C)=C/F");
19 | }
20 |
21 | @Test public void simple2() throws InvalidSmilesException {
22 | transform("C(\\F)=C\\F",
23 | "C(/F)=C/F");
24 | }
25 |
26 | @Test public void partial() throws InvalidSmilesException {
27 | transform("FC=C(\\F)/C=C/F",
28 | "FC=C(\\F)/C=C/F");
29 | }
30 |
31 | @Test public void partial2() throws InvalidSmilesException {
32 | transform("FC=C(F)C=C(F)\\C=C\\F",
33 | "FC=C(F)C=C(F)/C=C/F");
34 | }
35 |
36 | @Test public void conjugated() throws InvalidSmilesException {
37 | transform("F\\C=C(\\F)/C(/F)=C\\F",
38 | "F/C=C(/F)\\C(\\F)=C/F");
39 | }
40 |
41 | @Test public void cyclic() throws InvalidSmilesException {
42 | transform("C/C=C\\1/C\\C(=C\\C)\\C1",
43 | "C/C=C\\1/C/C(=C/C)/C1");
44 | transform("C/C=C\\1/C/C(=C/C)/C1",
45 | "C/C=C\\1/C/C(=C/C)/C1");
46 | }
47 |
48 | @Ignore("invalid structure")
49 | public void chebi15617() throws InvalidSmilesException {
50 | transform("C/C=C\\1/[C@@H](C)C(=O)N/C1=C\\C/2=N/C(=C\\C3=C(CCC(=O)O)C(=C(\\C=C/4\\C(=C(CC)C(=O)N4)C)N3)C)/C(=C2C)CCC(=O)O",
51 | "C/C=C\\1/[C@@H](C)C(=O)N/C1=C\\C/2=N/C(=C\\C3=C(CCC(=O)O)C(=C(/C=C\\4/C(=C(CC)C(=O)N4)C)N3)C)/C(=C2C)CCC(=O)O");
52 | transform("C/C=C\\1/[C@@H](C)C(=O)N/C1=C\\C/2=N/C(=C\\C3=C(CCC(=O)O)C(=C(/C=C\\4/C(=C(CC)C(=O)N4)C)N3)C)/C(=C2C)CCC(=O)O",
53 | "C/C=C\\1/[C@@H](C)C(=O)N/C1=C\\C/2=N/C(=C\\C3=C(CCC(=O)O)C(=C(/C=C\\4/C(=C(CC)C(=O)N4)C)N3)C)/C(=C2C)CCC(=O)O");
54 | }
55 |
56 | @Test public void chembl2064754() throws InvalidSmilesException {
57 | transform("CC(=O)OCC/C=1/SS/C(/CCOC(C)=O)=C(/C)\\N(C=O)CCCCCCCCCCCCN(C=O)\\C1\\C",
58 | "CC(=O)OCC/C=1/SS/C(/CCOC(C)=O)=C(/C)\\N(C=O)CCCCCCCCCCCCN(C=O)\\C1\\C");
59 | transform("CC(=O)OCC\\C=1\\SS/C(/CCOC(C)=O)=C(/C)\\N(C=O)CCCCCCCCCCCCN(C=O)/C1/C",
60 | "CC(=O)OCC/C=1/SS/C(/CCOC(C)=O)=C(/C)\\N(C=O)CCCCCCCCCCCCN(C=O)\\C1\\C");
61 | }
62 |
63 | @Test public void pubchem16088588() throws InvalidSmilesException {
64 | transform("CC(C)N1CC(=O)NC=2C=CC(=CC2)OC3=CC=C(C=C3)NC(=O)CN(C(=O)CO/N=C\\4/C[C@H]5C[C@@H](O)[C@H]6[C@@H]7CC[C@H]([C@H](C)CCC(=O)N(CC(=O)NC8=CC=C(C=C8)OC=9C=CC(=CC9)NC(=O)CN(C(=O)CC[C@@H](C)[C@H]%10CC[C@H]%11[C@@H]%12[C@H](O)C[C@@H]%13C/C(/CC[C@]%13(C)[C@H]%12C[C@H](O)[C@]%10%11C)=N/OCC1=O)C(C)C)C(C)C)[C@@]7(C)[C@@H](O)C[C@@H]6[C@@]5(C)CC4)C(C)C",
65 | "CC(C)N1CC(=O)NC=2C=CC(=CC2)OC3=CC=C(C=C3)NC(=O)CN(C(=O)CO/N=C\\4/C[C@H]5C[C@@H](O)[C@H]6[C@@H]7CC[C@H]([C@H](C)CCC(=O)N(CC(=O)NC8=CC=C(C=C8)OC=9C=CC(=CC9)NC(=O)CN(C(=O)CC[C@@H](C)[C@H]%10CC[C@H]%11[C@@H]%12[C@H](O)C[C@@H]%13C/C(/CC[C@]%13(C)[C@H]%12C[C@H](O)[C@]%10%11C)=N/OCC1=O)C(C)C)C(C)C)[C@@]7(C)[C@@H](O)C[C@@H]6[C@@]5(C)CC4)C(C)C");
66 | transform("CC(C)N1CC(=O)NC=2C=CC(=CC2)OC3=CC=C(C=C3)NC(=O)CN(C(=O)CO\\N=C/4\\C[C@H]5C[C@@H](O)[C@H]6[C@@H]7CC[C@H]([C@H](C)CCC(=O)N(CC(=O)NC8=CC=C(C=C8)OC=9C=CC(=CC9)NC(=O)CN(C(=O)CC[C@@H](C)[C@H]%10CC[C@H]%11[C@@H]%12[C@H](O)C[C@@H]%13C\\C(\\CC[C@]%13(C)[C@H]%12C[C@H](O)[C@]%10%11C)=N\\OCC1=O)C(C)C)C(C)C)[C@@]7(C)[C@@H](O)C[C@@H]6[C@@]5(C)CC4)C(C)C",
67 | "CC(C)N1CC(=O)NC=2C=CC(=CC2)OC3=CC=C(C=C3)NC(=O)CN(C(=O)CO/N=C\\4/C[C@H]5C[C@@H](O)[C@H]6[C@@H]7CC[C@H]([C@H](C)CCC(=O)N(CC(=O)NC8=CC=C(C=C8)OC=9C=CC(=CC9)NC(=O)CN(C(=O)CC[C@@H](C)[C@H]%10CC[C@H]%11[C@@H]%12[C@H](O)C[C@@H]%13C/C(/CC[C@]%13(C)[C@H]%12C[C@H](O)[C@]%10%11C)=N/OCC1=O)C(C)C)C(C)C)[C@@]7(C)[C@@H](O)C[C@@H]6[C@@]5(C)CC4)C(C)C");
68 | }
69 |
70 |
71 | @Test public void chembl294514_1() throws InvalidSmilesException {
72 | transform("Cc1c(CCC(=O)NCCOCCOCCN)c2/C=C/3\\N\\C(=C/c4[nH]c(/C=C/5\\N=C(CC6=N/C(=C\\c1[nH]2)/C(=C6C)CC)C(=C5CC)C)c(C)c4CCC(=O)NCCOCCOCCN)\\C(=C3CC)CC",
73 | "Cc1c(CCC(=O)NCCOCCOCCN)c2/C=C/3\\N\\C(=C/c4[nH]c(/C=C/5\\N=C(CC6=N/C(=C\\c1[nH]2)/C(=C6C)CC)C(=C5CC)C)c(C)c4CCC(=O)NCCOCCOCCN)\\C(=C3CC)CC");
74 | }
75 |
76 | @Test public void chembl294514_2() throws InvalidSmilesException {
77 | transform("Cc1c(CCC(=O)NCCOCCOCCN)c2\\C=C\\3/N/C(=C\\c4[nH]c(/C=C/5\\N=C(CC6=N/C(=C\\c1[nH]2)/C(=C6C)CC)C(=C5CC)C)c(C)c4CCC(=O)NCCOCCOCCN)/C(=C3CC)CC",
78 | "Cc1c(CCC(=O)NCCOCCOCCN)c2/C=C/3\\N\\C(=C/c4[nH]c(/C=C/5\\N=C(CC6=N/C(=C\\c1[nH]2)/C(=C6C)CC)C(=C5CC)C)c(C)c4CCC(=O)NCCOCCOCCN)\\C(=C3CC)CC");
79 | }
80 |
81 | @Test public void chembl147624() throws InvalidSmilesException {
82 | transform("COc1cc2nc(nc(N)c2cc1OC)N3CCN(CC3)S(=O)(=O)c4no[n+]([O-])c4C=5\\C=C/C=C\\C=C/C5",
83 | "COc1cc2nc(nc(N)c2cc1OC)N3CCN(CC3)S(=O)(=O)c4no[n+]([O-])c4C=5/C=C\\C=C/C=C\\C5");
84 | }
85 |
86 |
87 | static void transform(String smi, String exp) throws
88 | InvalidSmilesException {
89 | Assert.assertThat(Generator.generate(new NormaliseDirectionalLabels()
90 | .apply(Parser.parse(smi))), CoreMatchers
91 | .is(exp));
92 | }
93 |
94 | }
95 |
--------------------------------------------------------------------------------
/core/src/test/java/uk/ac/ebi/beam/BondTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013, European Bioinformatics Institute (EMBL-EBI)
3 | * All rights reserved.
4 | *
5 | * Redistribution and use in source and binary forms, with or without
6 | * modification, are permitted provided that the following conditions are met:
7 | *
8 | * 1. Redistributions of source code must retain the above copyright notice, this
9 | * list of conditions and the following disclaimer.
10 | * 2. Redistributions in binary form must reproduce the above copyright notice,
11 | * this list of conditions and the following disclaimer in the documentation
12 | * and/or other materials provided with the distribution.
13 | *
14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
18 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
23 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 | *
25 | * The views and conclusions contained in the software and documentation are those
26 | * of the authors and should not be interpreted as representing official policies,
27 | * either expressed or implied, of the FreeBSD Project.
28 | */
29 |
30 | package uk.ac.ebi.beam;
31 |
32 | import org.junit.Test;
33 |
34 | import static org.hamcrest.CoreMatchers.is;
35 | import static org.junit.Assert.assertThat;
36 | import static uk.ac.ebi.beam.Bond.AROMATIC;
37 | import static uk.ac.ebi.beam.Bond.DOT;
38 | import static uk.ac.ebi.beam.Bond.DOUBLE;
39 | import static uk.ac.ebi.beam.Bond.DOWN;
40 | import static uk.ac.ebi.beam.Bond.IMPLICIT;
41 | import static uk.ac.ebi.beam.Bond.QUADRUPLE;
42 | import static uk.ac.ebi.beam.Bond.SINGLE;
43 | import static uk.ac.ebi.beam.Bond.TRIPLE;
44 | import static uk.ac.ebi.beam.Bond.UP;
45 |
46 | /** @author John May */
47 | public class BondTest {
48 |
49 | @Test public void dotElectrons() throws Exception {
50 | assertThat(DOT.order(), is(0));
51 | }
52 |
53 | @Test public void singleElectrons() throws Exception {
54 | assertThat(SINGLE.order(), is(1));
55 | }
56 |
57 | @Test public void doubleElectrons() throws Exception {
58 | assertThat(DOUBLE.order(), is(2));
59 | }
60 |
61 | @Test public void tripleElectrons() throws Exception {
62 | assertThat(TRIPLE.order(), is(3));
63 | }
64 |
65 | @Test public void quadrupleElectrons() throws Exception {
66 | assertThat(QUADRUPLE.order(), is(4));
67 | }
68 |
69 | @Test public void aromaticElectrons() throws Exception {
70 | assertThat(AROMATIC.order(), is(1));
71 | }
72 |
73 | @Test public void upElectrons() throws Exception {
74 | assertThat(UP.order(), is(1));
75 | }
76 |
77 | @Test public void downElectrons() throws Exception {
78 | assertThat(DOWN.order(), is(1));
79 | }
80 |
81 | @Test public void dotInverse() throws Exception {
82 | assertThat(DOT.inverse(), is(DOT));
83 | }
84 |
85 | @Test public void singleInverse() throws Exception {
86 | assertThat(SINGLE.inverse(), is(SINGLE));
87 | }
88 |
89 | @Test public void doubleInverse() throws Exception {
90 | assertThat(DOUBLE.inverse(), is(DOUBLE));
91 | }
92 |
93 | @Test public void tripleInverse() throws Exception {
94 | assertThat(TRIPLE.inverse(), is(TRIPLE));
95 | }
96 |
97 | @Test public void quadrupleInverse() throws Exception {
98 | assertThat(QUADRUPLE.inverse(), is(QUADRUPLE));
99 | }
100 |
101 | @Test public void aromaticInverse() throws Exception {
102 | assertThat(AROMATIC.inverse(), is(AROMATIC));
103 | }
104 |
105 | @Test public void upInverse() throws Exception {
106 | assertThat(UP.inverse(), is(DOWN));
107 | }
108 |
109 | @Test public void downInverse() throws Exception {
110 | assertThat(DOWN.inverse(), is(UP));
111 | }
112 |
113 | @Test public void implicitInverse() throws Exception {
114 | assertThat(IMPLICIT.inverse(), is(IMPLICIT));
115 | }
116 |
117 | @Test public void dotSymbol() throws Exception {
118 | assertThat(DOT.token(), is("."));
119 | }
120 |
121 | @Test public void singleSymbol() throws Exception {
122 | assertThat(SINGLE.token(), is("-"));
123 | }
124 |
125 | @Test public void doubleSymbol() throws Exception {
126 | assertThat(DOUBLE.token(), is("="));
127 | }
128 |
129 | @Test public void tripleSymbol() throws Exception {
130 | assertThat(TRIPLE.token(), is("#"));
131 | }
132 |
133 | @Test public void quadrupleSymbol() throws Exception {
134 | assertThat(QUADRUPLE.token(), is("$"));
135 | }
136 |
137 | @Test public void aromaticSymbol() throws Exception {
138 | assertThat(AROMATIC.token(), is(":"));
139 | }
140 |
141 | @Test public void upSymbol() throws Exception {
142 | assertThat(UP.token(), is("/"));
143 | }
144 |
145 | @Test public void downSymbol() throws Exception {
146 | assertThat(DOWN.token(), is("\\"));
147 | }
148 |
149 | @Test public void implicitSymbol() throws Exception {
150 | assertThat(IMPLICIT.token(), is(""));
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/core/src/test/java/uk/ac/ebi/beam/EdgeTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013, European Bioinformatics Institute (EMBL-EBI)
3 | * All rights reserved.
4 | *
5 | * Redistribution and use in source and binary forms, with or without
6 | * modification, are permitted provided that the following conditions are met:
7 | *
8 | * 1. Redistributions of source code must retain the above copyright notice, this
9 | * list of conditions and the following disclaimer.
10 | * 2. Redistributions in binary form must reproduce the above copyright notice,
11 | * this list of conditions and the following disclaimer in the documentation
12 | * and/or other materials provided with the distribution.
13 | *
14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
18 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
23 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 | *
25 | * The views and conclusions contained in the software and documentation are those
26 | * of the authors and should not be interpreted as representing official policies,
27 | * either expressed or implied, of the FreeBSD Project.
28 | */
29 |
30 | package uk.ac.ebi.beam;
31 |
32 | import org.junit.Ignore;
33 | import org.junit.Test;
34 |
35 | import static org.hamcrest.CoreMatchers.is;
36 | import static org.hamcrest.CoreMatchers.not;
37 | import static org.junit.Assert.assertThat;
38 |
39 | /** @author John May */
40 | public class EdgeTest {
41 |
42 | @Test public void either() throws Exception {
43 | assertThat(new Edge(2, 3, Bond.IMPLICIT).either(), is(2));
44 | assertThat(new Edge(3, 2, Bond.IMPLICIT).either(), is(3));
45 | }
46 |
47 | @Test public void other() throws Exception {
48 | assertThat(new Edge(2, 3, Bond.IMPLICIT).other(2), is(3));
49 | assertThat(new Edge(2, 3, Bond.IMPLICIT).other(3), is(2));
50 | assertThat(new Edge(3, 2, Bond.IMPLICIT).other(2), is(3));
51 | assertThat(new Edge(3, 2, Bond.IMPLICIT).other(3), is(2));
52 | }
53 |
54 | @Ignore("no longer thrown")
55 | public void invalidEndpoint() throws Exception {
56 | new Edge(2, 3, Bond.IMPLICIT).other(1);
57 | }
58 |
59 | @Test public void bond() throws Exception {
60 | assertThat(new Edge(2, 3, Bond.SINGLE).bond(), is(Bond.SINGLE));
61 | assertThat(new Edge(2, 3, Bond.UP).bond(), is(Bond.UP));
62 | assertThat(new Edge(2, 3, Bond.DOWN).bond(), is(Bond.DOWN));
63 | }
64 |
65 | @Test public void relativeBond() throws Exception {
66 | assertThat(new Edge(2, 3, Bond.SINGLE).bond(2), is(Bond.SINGLE));
67 | assertThat(new Edge(2, 3, Bond.SINGLE).bond(3), is(Bond.SINGLE));
68 | assertThat(new Edge(2, 3, Bond.UP).bond(2), is(Bond.UP));
69 | assertThat(new Edge(2, 3, Bond.UP).bond(3), is(Bond.DOWN));
70 | assertThat(new Edge(2, 3, Bond.DOWN).bond(2), is(Bond.DOWN));
71 | assertThat(new Edge(2, 3, Bond.DOWN).bond(3), is(Bond.UP));
72 | }
73 |
74 | @Test(expected = IllegalArgumentException.class)
75 | public void invalidRelativeBond() throws Exception {
76 | new Edge(2, 3, Bond.IMPLICIT).bond(1);
77 | }
78 |
79 | @Test public void undirectedHashCode() {
80 | assertThat(new Edge(0, 1, Bond.IMPLICIT).hashCode(),
81 | is(new Edge(1, 0, Bond.IMPLICIT).hashCode()));
82 | }
83 |
84 | @Test public void directedHashCode() {
85 | assertThat(new Edge(0, 1, Bond.UP).hashCode(),
86 | is(new Edge(1, 0, Bond.DOWN).hashCode()));
87 | assertThat(new Edge(0, 1, Bond.UP).hashCode(),
88 | is(new Edge(1, 0, Bond.UP).hashCode()));
89 | }
90 |
91 | @Test public void undirectedEquality() {
92 | assertThat(new Edge(0, 1, Bond.IMPLICIT),
93 | is(new Edge(0, 1, Bond.IMPLICIT)));
94 | assertThat(new Edge(0, 1, Bond.IMPLICIT),
95 | is(new Edge(1, 0, Bond.IMPLICIT)));
96 | }
97 |
98 | @Test public void undirectedInequality() {
99 | assertThat(new Edge(0, 1, Bond.SINGLE),
100 | is(not(new Edge(0, 1, Bond.DOUBLE))));
101 | assertThat(new Edge(0, 1, Bond.DOUBLE),
102 | is(not(new Edge(1, 0, Bond.SINGLE))));
103 | }
104 |
105 | @Test public void directedEquality() {
106 | assertThat(new Edge(0, 1, Bond.UP),
107 | is(new Edge(0, 1, Bond.UP)));
108 | assertThat(new Edge(0, 1, Bond.UP),
109 | is(new Edge(1, 0, Bond.DOWN)));
110 | assertThat(new Edge(1, 0, Bond.DOWN),
111 | is(new Edge(0, 1, Bond.UP)));
112 | assertThat(new Edge(1, 0, Bond.DOWN),
113 | is(new Edge(1, 0, Bond.DOWN)));
114 | }
115 |
116 | @Test public void directedInequality() {
117 | assertThat(new Edge(0, 1, Bond.UP),
118 | is(not(new Edge(0, 1, Bond.DOWN))));
119 | assertThat(new Edge(0, 1, Bond.UP),
120 | is(not(new Edge(1, 0, Bond.UP))));
121 | assertThat(new Edge(1, 0, Bond.UP),
122 | is(not(new Edge(0, 1, Bond.UP))));
123 | assertThat(new Edge(1, 0, Bond.DOWN),
124 | is(not(new Edge(1, 0, Bond.UP))));
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/func/src/test/java/uk/ac/ebi/beam/FromSubsetAtomsTest.java:
--------------------------------------------------------------------------------
1 | package uk.ac.ebi.beam;
2 |
3 | import org.hamcrest.CoreMatchers;
4 | import org.junit.Assert;
5 | import org.junit.Ignore;
6 | import org.junit.Test;
7 |
8 | /** @author John May */
9 | public class FromSubsetAtomsTest {
10 |
11 | @Test public void unknown() throws Exception {
12 | transform("*", "[*]");
13 | }
14 |
15 | @Test public void inorganic() throws Exception {
16 | transform("[Ne]", "[Ne]");
17 | }
18 |
19 | @Test public void methane() throws Exception {
20 | transform("C", "[CH4]");
21 | }
22 |
23 | @Test public void ethane_withAtomClass() throws Exception {
24 | transform("[CH3:1]C", "[CH3:1][CH3]");
25 | }
26 |
27 | @Test public void ethanol() throws InvalidSmilesException {
28 | transform("CCO", "[CH3][CH2][OH]");
29 | }
30 |
31 | @Test public void stereoSpecification() throws InvalidSmilesException {
32 | transform("[C@H](N)(O)C", "[C@H]([NH2])([OH])[CH3]");
33 | transform("[C@@H](N)(O)C", "[C@@H]([NH2])([OH])[CH3]");
34 | }
35 |
36 | @Test public void noStereoSpecification() throws InvalidSmilesException {
37 | transform("C(N)(O)C", "[CH]([NH2])([OH])[CH3]");
38 | }
39 |
40 | @Test public void bracketAtom() {
41 | // should provide identity of bracket atom
42 | Atom input = new AtomImpl.BracketAtom(Element.Carbon, 1, 0);
43 | Atom output = FromSubsetAtoms.fromSubset(input, 0, 0);
44 | Assert.assertThat(input, CoreMatchers.is(CoreMatchers
45 | .sameInstance(output)));
46 | }
47 |
48 | @Test public void aliphatic_carbon() {
49 | Atom actual = FromSubsetAtoms
50 | .fromSubset(AtomImpl.AliphaticSubset.Carbon, 3, 0);
51 | Atom expect = new AtomImpl.BracketAtom(Element.Carbon, 1, 0);
52 | Assert.assertThat(expect, CoreMatchers.is(actual));
53 | }
54 |
55 | @Test public void aromatic_carbon() {
56 | Atom actual = FromSubsetAtoms.fromSubset(AtomImpl.AromaticSubset.Carbon, 2, 0);
57 | Atom expect = new AtomImpl.BracketAtom(-1, Element.Carbon, 1, 0, 0, true);
58 | Assert.assertThat(expect, CoreMatchers.is(actual));
59 | }
60 |
61 | @Test public void indolizine() throws InvalidSmilesException {
62 | transform("c2cc1cccn1cc2",
63 | "[cH]1[cH][c]2[cH][cH][cH][n]2[cH][cH]1");
64 | }
65 |
66 | @Test public void indolizine_kekule() throws InvalidSmilesException {
67 | transform("C1=CN2C=CC=CC2=C1",
68 | "[CH]1=[CH][N]2[CH]=[CH][CH]=[CH][C]2=[CH]1");
69 | }
70 |
71 | @Test public void _1H_imidazole() throws InvalidSmilesException {
72 | transform("[H]n1ccnc1",
73 | "[H][n]1[cH][cH][n][cH]1");
74 | }
75 |
76 | @Test public void _1H_imidazole_kekule() throws InvalidSmilesException {
77 | transform("[H]N1C=CN=C1",
78 | "[H][N]1[CH]=[CH][N]=[CH]1");
79 | }
80 |
81 | @Test public void cdk_bug_1363882() throws Exception{
82 | transform("[H]c2c([H])c(c1c(nc(n1([H]))C(F)(F)F)c2Cl)Cl",
83 | "[H][c]1[c]([H])[c]([c]2[c]([n][c]([n]2[H])[C]([F])([F])[F])[c]1[Cl])[Cl]");
84 | }
85 |
86 | @Test public void cdk_bug_1579235() throws Exception{
87 | transform("c2cc1cccn1cc2",
88 | "[cH]1[cH][c]2[cH][cH][cH][n]2[cH][cH]1");
89 | }
90 |
91 | @Test public void sulphur() throws Exception {
92 | transform("S([H])[H]",
93 | "[S]([H])[H]");
94 | transform("[H]S([H])[H]",
95 | "[H][SH]([H])[H]");
96 | transform("[H]S([H])([H])[H]",
97 | "[H][S]([H])([H])[H]");
98 | transform("[H]S([H])([H])([H])[H]",
99 | "[H][SH]([H])([H])([H])[H]");
100 | transform("[H]S([H])([H])([H])([H])[H]",
101 | "[H][S]([H])([H])([H])([H])[H]");
102 | }
103 |
104 | @Test public void tricyclazole() throws InvalidSmilesException {
105 | transform("Cc1cccc2sc3nncn3c12",
106 | "[CH3][c]1[cH][cH][cH][c]2[s][c]3[n][n][cH][n]3[c]12");
107 | }
108 |
109 | @Test public void tricyclazole_kekule() throws InvalidSmilesException {
110 | transform("CC1=C2N3C=NN=C3SC2=CC=C1",
111 | "[CH3][C]1=[C]2[N]3[CH]=[N][N]=[C]3[S][C]2=[CH][CH]=[CH]1");
112 | }
113 |
114 | @Ignore("bad molecule - should have utility to find/fix this types of errors")
115 | public void mixingAromaticAndKekule() throws Exception {
116 | transform("c1=cc=cc=c1",
117 | "[cH]1=[cH][cH]=[cH][cH]=[cH]1");
118 | }
119 |
120 | @Test public void quinone() throws Exception {
121 | transform("oc1ccc(o)cc1",
122 | "[o][c]1[cH][cH][c]([o])[cH][cH]1");
123 | }
124 |
125 | /** 1-(1H-pyrrol-2-yl)pyrrole */
126 | @Test public void pyroles() throws Exception {
127 | transform("c1ccn(c1)-c1ccc[nH]1",
128 | "[cH]1[cH][cH][n]([cH]1)-[c]2[cH][cH][cH][nH]2");
129 | }
130 |
131 | @Test public void cdk_bug_956926() throws InvalidSmilesException {
132 | transform("[c+]1ccccc1",
133 | "[c+]1[cH][cH][cH][cH][cH]1");
134 | }
135 |
136 |
137 |
138 | private void transform(String input, String expected) throws
139 | InvalidSmilesException {
140 | Graph g = Parser.parse(input);
141 | ImplicitToExplicit ite = new ImplicitToExplicit();
142 | FromSubsetAtoms fsa = new FromSubsetAtoms();
143 | ExplicitToImplicit eti = new ExplicitToImplicit();
144 | String actual = Generator.generate(eti.apply(
145 | fsa.apply(
146 | ite.apply(g))));
147 | Assert.assertThat(actual, CoreMatchers.is(expected));
148 | }
149 |
150 | }
151 |
--------------------------------------------------------------------------------
/core/src/main/java/uk/ac/ebi/beam/ArbitraryMatching.java:
--------------------------------------------------------------------------------
1 | package uk.ac.ebi.beam;
2 |
3 | import java.util.BitSet;
4 |
5 | /**
6 | * Simple matching greedily chooses edges and matches. The produced matching is
7 | * not guaranteed to be maximum but provides a starting point for improvement
8 | * through augmentation.
9 | *
10 | * @author John May
11 | */
12 | final class ArbitraryMatching {
13 |
14 | /**
15 | * Create an arbitrary matching on the subset of vertices ('s') of provided
16 | * graph. The provided matching should be empty.
17 | *
18 | * @param g graph to match
19 | * @param m empty matching (presumed)
20 | * @param s subset of vertices
21 | * @return number of vertices matched
22 | */
23 | static int initial(final Graph g, Matching m, final BitSet s) {
24 |
25 | int nMatched = 0;
26 |
27 | for (int v = s.nextSetBit(0); v >= 0; v = s.nextSetBit(v + 1)) {
28 |
29 | // skip if already matched
30 | if (m.matched(v))
31 | continue;
32 |
33 | // find a single edge which is not matched and match it
34 | int d = g.degree(v);
35 | for (int j = 0; j < d; ++j) {
36 | Edge e = g.edgeAt(v, j);
37 | int w = e.other(v);
38 | if ((e.bond() != Bond.SINGLE) && m.unmatched(w) && s.get(w)) {
39 | m.match(v, w);
40 | nMatched += 2;
41 | break;
42 | }
43 | }
44 | }
45 |
46 | return nMatched;
47 | }
48 |
49 | static int dfs(final Graph g, Matching m, final BitSet s) {
50 |
51 | int nMatched = 0;
52 | BitSet unvisited = (BitSet) s.clone();
53 |
54 | // visit those with degree 1 first and expand out matching
55 | for (int v = unvisited.nextSetBit(0); v >= 0; v = unvisited.nextSetBit(v + 1)) {
56 | if (!m.matched(v)) {
57 | int cnt = 0;
58 | int d = g.degree(v);
59 | while (--d >= 0) {
60 | int w = g.edgeAt(v, d).other(v);
61 | if (unvisited.get(w))
62 | ++cnt;
63 | }
64 | if (cnt == 1)
65 | nMatched += dfsVisit(g, v, m, unvisited, true);
66 | }
67 | }
68 |
69 | // now those which aren't degree 1
70 | for (int v = unvisited.nextSetBit(0); v >= 0; v = unvisited.nextSetBit(v + 1)) {
71 | if (!m.matched(v)) {
72 | nMatched += dfsVisit(g, v, m, unvisited, true);
73 | }
74 | }
75 |
76 | return nMatched;
77 | }
78 |
79 | static int dfsVisit(final Graph g, final int v, Matching m, final BitSet unvisited, boolean match) {
80 | unvisited.clear(v);
81 | int nMatched = 0;
82 | int d = g.degree(v);
83 | while (--d >= 0) {
84 | int w = g.edgeAt(v, d).other(v);
85 | if (unvisited.get(w)) {
86 | if (match) {
87 | m.match(v, w);
88 | return 2 + dfsVisit(g, w, m, unvisited, false);
89 | }
90 | else {
91 | nMatched += dfsVisit(g, w, m, unvisited, true);
92 | }
93 | }
94 | }
95 | return nMatched;
96 | }
97 |
98 | /**
99 | * When precisely two vertices are unmatched we only need to find a single
100 | * augmenting path. Rather than run through edmonds with blossoms etc we
101 | * simple do a targest DFS for the path.
102 | *
103 | * @param g graph
104 | * @param m matching
105 | * @param nMatched current matching cardinality must be |s|-nMathced == 2
106 | * @param s subset size
107 | * @return new match cardinality
108 | */
109 | static int augmentOnce(final Graph g, final Matching m, int nMatched, final BitSet s) {
110 |
111 | int vStart = s.nextSetBit(0);
112 | while (vStart >= 0) {
113 | if (!m.matched(vStart)) break;
114 | vStart = s.nextSetBit(vStart + 1);
115 | }
116 | int vEnd = s.nextSetBit(vStart + 1);
117 | while (vEnd >= 0) {
118 | if (!m.matched(vEnd)) break;
119 | vEnd = s.nextSetBit(vEnd + 1);
120 | }
121 |
122 | // find an augmenting path between vStart and vEnd
123 | int[] path = new int[g.order()];
124 | int len = findPath(g, vStart, vEnd, s, path, 0, m, false);
125 | if (len > 0) {
126 | // augment
127 | for (int i = 0; i < len; i += 2) {
128 | m.match(path[i], path[i + 1]);
129 | }
130 | nMatched += 2;
131 | }
132 |
133 | return nMatched;
134 | }
135 |
136 | static int findPath(Graph g, int v, int end, BitSet unvisited, int[] path, int len, Matching m, boolean matchNeeded) {
137 | unvisited.clear(v);
138 | path[len++] = v;
139 | int l;
140 | int d = g.degree(v);
141 | for (int j = 0; j < d; ++j) {
142 | Edge e = g.edgeAt(v, j);
143 | // explicit single bond can not be augmented along!!
144 | if (e.bond() == Bond.SINGLE)
145 | continue;
146 | int w = e.other(v);
147 | if (unvisited.get(w)) {
148 | if (w == end) {
149 | path[len] = w;
150 | len++;
151 | unvisited.set(v);
152 | // odd length path no good
153 | return ((len & 0x1) == 1) ? 0 : len;
154 | }
155 | else if ((m.other(w) == v) == matchNeeded) {
156 | if ((l = findPath(g, w, end, unvisited, path, len, m, !matchNeeded)) > 0) {
157 | unvisited.set(v);
158 | return l;
159 | }
160 | }
161 | }
162 | }
163 | unvisited.set(v);
164 | return 0;
165 | }
166 | }
167 |
--------------------------------------------------------------------------------
/func/src/main/java/uk/ac/ebi/beam/FromTrigonalTopology.java:
--------------------------------------------------------------------------------
1 | package uk.ac.ebi.beam;
2 |
3 | import java.util.BitSet;
4 | import java.util.HashMap;
5 | import java.util.List;
6 | import java.util.Map;
7 |
8 | import static uk.ac.ebi.beam.Configuration.Type.DoubleBond;
9 |
10 | /**
11 | * Given a chemical graph with atom-centric double bond stereo configurations
12 | * (trigonal topology) - remove the topology but add in direction up/down edge
13 | * labels.
14 | *
15 | * @author John May
16 | */
17 | final class FromTrigonalTopology extends AbstractFunction
37 | *
45 | *
46 | * @author John May
47 | * @see Bond
48 | */
49 | public final class Edge {
50 |
51 | /** Endpoints of the edge. */
52 | private final int u, v, xor;
53 |
54 | /** Label on the edge. */
55 | private Bond bond;
56 |
57 | Edge(final int u, final int v, final Bond bond) {
58 | this.u = u;
59 | this.v = v;
60 | this.xor = u ^ v;
61 | this.bond = bond;
62 | }
63 |
64 | Edge(Edge e) {
65 | this(e.u, e.v, e.bond);
66 | }
67 |
68 | /**
69 | * Access either endpoint of the edge. For directional bonds, the endpoint
70 | * can be considered as relative to this vertex.
71 | *
72 | * @return either endpoint
73 | */
74 | public int either() {
75 | return u;
76 | }
77 |
78 | /**
79 | * Given one endpoint, access the other endpoint of the edge.
80 | *
81 | * @param x an endpoint of the edge
82 | * @return the other endpoint
83 | * @throws IllegalArgumentException {@code x} was not an endpoint of this
84 | * edge
85 | */
86 | public int other(final int x) {
87 | return x ^ xor;
88 | }
89 |
90 | /**
91 | * Access the bond label without considering which endpoint the label is
92 | * relative to. For the directional bonds {@link Bond#UP} and {@link
93 | * Bond#DOWN} the {@link #bond(int)} should be used to provided the label
94 | * relative to rhe provided endpoint.
95 | *
96 | * @return bond label
97 | * @see #bond(int)
98 | */
99 | public Bond bond() {
100 | return bond;
101 | }
102 |
103 | /**
104 | * Set the bond label.
105 | *
106 | * @param bond the bond label
107 | */
108 | void bond(Bond bond) {
109 | this.bond = bond;
110 | }
111 |
112 | /**
113 | * Access the bond label relative to a specified endpoint.
114 | *
115 | *
38 | * // an edge between the vertices 1 and 2 the bond label is implicit
39 | * Edge e = Bond.IMPLICIT.edge(1, 2);
40 | *
41 | * // an edge between the vertices 5 and 3 the bond label is double
42 | * Edge e = Bond.DOUBLE.edge(1, 2);
43 | *
44 | *
120 | *
121 | * @param x endpoint to which the label is relative to
122 | * @return the bond label
123 | */
124 | public Bond bond(final int x) {
125 | if (x == u) return bond;
126 | if (x == v) return bond.inverse();
127 | throw new IllegalArgumentException(invalidEndpointMessage(x));
128 | }
129 |
130 |
131 | /**
132 | * Inverse of the edge label but keep the vertices the same.
133 | *
134 | * @return inverse edge
135 | */
136 | Edge inverse() {
137 | return bond.inverse().edge(u, v);
138 | }
139 |
140 | /** Helper method to print error message. */
141 | private String invalidEndpointMessage(final int x) {
142 | return "Vertex " + x + ", is not an endpoint of the edge " + toString();
143 | }
144 |
145 | /** {@inheritDoc} */
146 | @Override
147 | public int hashCode() {
148 | return xor;
149 | }
150 |
151 | /** {@inheritDoc} */
152 | @Override
153 | public boolean equals(Object other) {
154 | if (this == other) return true;
155 | if (other == null || getClass() != other.getClass()) return false;
156 | final Edge o = (Edge) other;
157 | return (u == o.u && v == o.v && bond.equals(o.bond)) ||
158 | (u == o.v && v == o.u && bond.equals(o.bond.inverse()));
159 | }
160 |
161 | /** {@inheritDoc} */
162 | @Override public String toString() {
163 | return new StringBuilder(20).append('{')
164 | .append(u)
165 | .append(", ")
166 | .append(v)
167 | .append('}')
168 | .append(": '")
169 | .append(bond)
170 | .append("'")
171 | .toString();
172 | }
173 | }
174 |
--------------------------------------------------------------------------------
/func/src/test/java/uk/ac/ebi/beam/FromTrigonalTopologyTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013, European Bioinformatics Institute (EMBL-EBI)
3 | * All rights reserved.
4 | *
5 | * Redistribution and use in source and binary forms, with or without
6 | * modification, are permitted provided that the following conditions are met:
7 | *
8 | * 1. Redistributions of source code must retain the above copyright notice, this
9 | * list of conditions and the following disclaimer.
10 | * 2. Redistributions in binary form must reproduce the above copyright notice,
11 | * this list of conditions and the following disclaimer in the documentation
12 | * and/or other materials provided with the distribution.
13 | *
14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
18 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
23 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 | *
25 | * The views and conclusions contained in the software and documentation are those
26 | * of the authors and should not be interpreted as representing official policies,
27 | * either expressed or implied, of the FreeBSD Project.
28 | */
29 |
30 | package uk.ac.ebi.beam;
31 |
32 | import org.hamcrest.CoreMatchers;
33 | import org.junit.Assert;
34 | import org.junit.Test;
35 |
36 | import static org.hamcrest.CoreMatchers.is;
37 |
38 | /** @author John May */
39 | public class FromTrigonalTopologyTest {
40 |
41 | @Test public void z_dichloroethene() throws Exception {
42 | transform("F[C@H]=[C@H]F", "F/C=C\\F");
43 | }
44 |
45 | @Test public void z_dichloroethene_alt() throws Exception {
46 | transform("F[C@@H]=[C@@H]F", "F\\C=C/F");
47 | }
48 |
49 | @Test public void z_dichloroethene_explicitH() throws Exception {
50 | transform("F[C@]([H])=[C@](F)[H]", "F/C(/[H])=C(\\F)/[H]");
51 | }
52 |
53 | @Test public void z_dichloroethene_alt_explicitH() throws Exception {
54 | transform("F[C@@]([H])=[C@@](F)[H]", "F\\C(\\[H])=C(/F)\\[H]");
55 | }
56 |
57 | @Test public void e_dichloroethene() throws Exception {
58 | transform("F[C@H]=[C@@H]F", "F/C=C/F");
59 | }
60 |
61 | @Test public void e_dichloroethene_alt() throws Exception {
62 | transform("F[C@@H]=[C@H]F", "F\\C=C\\F");
63 | }
64 |
65 | @Test public void e_dichloroethene_explicitH() throws Exception {
66 | transform("F[C@]([H])=[C@@](F)[H]", "F/C(/[H])=C(/F)\\[H]");
67 | }
68 |
69 | @Test public void e_dichloroethene_alt_explicitH() throws Exception {
70 | transform("F[C@@]([H])=[C@](F)[H]", "F\\C(\\[H])=C(\\F)/[H]");
71 | }
72 |
73 | @Test public void z_dichloroethene_permuted_1() throws Exception {
74 | transform("F[C@H]=[C@H]F", new int[]{1, 0, 2, 3}, "C(\\F)=C\\F");
75 | }
76 |
77 | @Test public void z_dichloroethene_permuted_2() throws Exception {
78 | transform("F[C@H]=[C@H]F", new int[]{3, 2, 1, 0}, "F\\C=C/F");
79 | }
80 |
81 | @Test public void z_dichloroethene_alt_permuted_1() throws Exception {
82 | transform("F[C@@H]=[C@@H]F", new int[]{1, 0, 2, 3}, "C(/F)=C/F");
83 | }
84 |
85 | @Test public void z_dichloroethene_alt_permuted_2() throws Exception {
86 | transform("F[C@@H]=[C@@H]F", new int[]{3, 2, 1, 0}, "F/C=C\\F");
87 | }
88 |
89 | @Test public void e_dichloroethene_permuted_1() throws Exception {
90 | transform("F[C@H]=[C@@H]F", new int[]{1, 0, 2, 3}, "C(\\F)=C/F");
91 | }
92 |
93 | @Test public void e_dichloroethene_permuted_2() throws Exception {
94 | transform("F[C@@H]=[C@H]F", new int[]{3, 2, 1, 0}, "F\\C=C\\F");
95 | }
96 |
97 | @Test public void conjugated() throws InvalidSmilesException {
98 | transform("F[C@H]=[C@@H][C@H]=[C@@H]F", "F/C=C/C=C/F");
99 | }
100 |
101 | /** Ensures that conflicting directional assignments are resolved. */
102 | @Test public void conjugated_conflict() throws InvalidSmilesException {
103 | transform("F[C@H]=[C@@H][C@@H]=[C@H]F", "F/C=C/C=C/F");
104 | }
105 |
106 |
107 |
108 | @Test public void cyclooctatetraene_1() throws InvalidSmilesException {
109 | transform("[C@H]1=[C@@H][C@@H]=[C@@H][C@@H]=[C@@H][C@@H]=[C@@H]1",
110 | "C\\1=C\\C=C/C=C\\C=C1");
111 | }
112 |
113 | @Test public void cyclooctatetraene_2() throws InvalidSmilesException {
114 | transform("[C@@H]1=[C@H][C@H]=[C@H][C@H]=[C@H][C@H]=[C@H]1",
115 | "C/1=C/C=C\\C=C/C=C1");
116 | }
117 |
118 | // @Test public void cyclooctatetraene_3() throws InvalidSmilesException {
119 | // apply("[C@H]1=[C@@H][C@H]=[C@H][C@@H]=[C@@H][C@H]=[C@@H]1",
120 | // "C\\1=C/C=C\\C=C/C=C1");
121 | // }
122 |
123 |
124 | static void transform(String smi, String exp) throws
125 | InvalidSmilesException {
126 | ImplicitToExplicit ite = new ImplicitToExplicit();
127 | FromTrigonalTopology ftt = new FromTrigonalTopology();
128 | ExplicitToImplicit eti = new ExplicitToImplicit();
129 | Assert.assertThat(Generator
130 | .generate(eti.apply(ftt.apply(ite.apply(Parser.parse(smi))))),
131 | CoreMatchers.is(exp));
132 | }
133 |
134 | static void transform(String smi, int[] p, String exp) throws
135 | InvalidSmilesException {
136 | ImplicitToExplicit ite = new ImplicitToExplicit();
137 | FromTrigonalTopology ftt = new FromTrigonalTopology();
138 | ExplicitToImplicit eti = new ExplicitToImplicit();
139 | Assert.assertThat(Generator
140 | .generate(eti.apply(ftt.apply(ite.apply(Parser.parse(smi)
141 | .permute(p))))),
142 | CoreMatchers.is(exp));
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/core/src/test/java/uk/ac/ebi/beam/ElementTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013, European Bioinformatics Institute (EMBL-EBI)
3 | * All rights reserved.
4 | *
5 | * Redistribution and use in source and binary forms, with or without
6 | * modification, are permitted provided that the following conditions are met:
7 | *
8 | * 1. Redistributions of source code must retain the above copyright notice, this
9 | * list of conditions and the following disclaimer.
10 | * 2. Redistributions in binary form must reproduce the above copyright notice,
11 | * this list of conditions and the following disclaimer in the documentation
12 | * and/or other materials provided with the distribution.
13 | *
14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
18 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
23 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 | *
25 | * The views and conclusions contained in the software and documentation are those
26 | * of the authors and should not be interpreted as representing official policies,
27 | * either expressed or implied, of the FreeBSD Project.
28 | */
29 |
30 | package uk.ac.ebi.beam;
31 |
32 | import org.junit.Ignore;
33 | import org.junit.Test;
34 |
35 | import java.util.Arrays;
36 | import java.util.Locale;
37 |
38 | import static org.junit.Assert.assertNull;
39 | import static org.hamcrest.CoreMatchers.is;
40 | import static org.junit.Assert.assertThat;
41 | import static org.junit.Assert.assertTrue;
42 | import static uk.ac.ebi.beam.Element.Arsenic;
43 | import static uk.ac.ebi.beam.Element.Boron;
44 | import static uk.ac.ebi.beam.Element.Bromine;
45 | import static uk.ac.ebi.beam.Element.Calcium;
46 | import static uk.ac.ebi.beam.Element.Carbon;
47 | import static uk.ac.ebi.beam.Element.Chlorine;
48 | import static uk.ac.ebi.beam.Element.Fluorine;
49 | import static uk.ac.ebi.beam.Element.Iodine;
50 | import static uk.ac.ebi.beam.Element.Nitrogen;
51 | import static uk.ac.ebi.beam.Element.Oxygen;
52 | import static uk.ac.ebi.beam.Element.Phosphorus;
53 | import static uk.ac.ebi.beam.Element.Selenium;
54 | import static uk.ac.ebi.beam.Element.Sulfur;
55 | import static uk.ac.ebi.beam.Element.Unknown;
56 |
57 | /** @author John May */
58 | public class ElementTest {
59 |
60 | @Test public void organicSymbols() throws Exception {
61 | assertThat(Element.ofSymbol("B"), is(Boron));
62 | assertThat(Element.ofSymbol("C"), is(Carbon));
63 | assertThat(Element.ofSymbol("N"), is(Nitrogen));
64 | assertThat(Element.ofSymbol("O"), is(Oxygen));
65 | assertThat(Element.ofSymbol("P"), is(Phosphorus));
66 | assertThat(Element.ofSymbol("S"), is(Sulfur));
67 | assertThat(Element.ofSymbol("F"), is(Fluorine));
68 | assertThat(Element.ofSymbol("Br"), is(Bromine));
69 | assertThat(Element.ofSymbol("Cl"), is(Chlorine));
70 | assertThat(Element.ofSymbol("I"), is(Iodine));
71 | }
72 |
73 | @Test public void aromaticSymbols() throws Exception {
74 | assertThat(Element.ofSymbol("b"), is(Boron));
75 | assertThat(Element.ofSymbol("c"), is(Carbon));
76 | assertThat(Element.ofSymbol("n"), is(Nitrogen));
77 | assertThat(Element.ofSymbol("o"), is(Oxygen));
78 | assertThat(Element.ofSymbol("p"), is(Phosphorus));
79 | assertThat(Element.ofSymbol("s"), is(Sulfur));
80 | assertThat(Element.ofSymbol("se"), is(Selenium));
81 | assertThat(Element.ofSymbol("as"), is(Arsenic));
82 | }
83 |
84 | @Test public void symbols() {
85 | for (Element e : Element.values()) {
86 | assertThat(Element.ofSymbol(e.symbol()), is(e));
87 | }
88 | }
89 |
90 | @Test public void invalidSymbol() {
91 | assertNull(Element.ofSymbol("J"));
92 | }
93 |
94 | @Test public void organic() {
95 | for (Element e : Arrays.asList(Boron,
96 | Carbon,
97 | Nitrogen,
98 | Oxygen,
99 | Phosphorus,
100 | Sulfur,
101 | Fluorine,
102 | Chlorine,
103 | Bromine,
104 | Iodine)) {
105 | assertTrue(e.organic());
106 | }
107 | }
108 |
109 | @Test public void aromatic() {
110 | for (Element e : Arrays.asList(Boron,
111 | Carbon,
112 | Nitrogen,
113 | Oxygen,
114 | Phosphorus,
115 | Sulfur,
116 | Selenium,
117 | Arsenic)) {
118 | assertTrue(e.aromatic());
119 | }
120 | }
121 |
122 | @Test public void verify() {
123 | for (Element e : Element.values()) {
124 | boolean valid = e.verify(0, 0);
125 | }
126 | }
127 |
128 | @Test public void ofNumber() {
129 | assertThat(Element.ofNumber(6), is(Element.Carbon));
130 | assertThat(Element.ofNumber(8), is(Element.Oxygen));
131 | }
132 |
133 | @Test
134 | public void read() {
135 | for (Element e : Element.values()) {
136 | if (e.aromatic())
137 | assertThat(Element.read(CharBuffer.fromString(e.symbol()
138 | .toLowerCase(Locale.ENGLISH))), is(e));
139 | assertThat(Element.read(CharBuffer.fromString(e.symbol())), is(e));
140 | }
141 | }
142 |
143 | @Test
144 | public void readNone() {
145 | assertNull(Element.read(CharBuffer.fromString("")));
146 | }
147 |
148 | @Test
149 | public void readInvalidElement() {
150 | assertNull(Element.read(CharBuffer.fromString("J")));
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/core/src/test/java/uk/ac/ebi/beam/MaximumMatchingTest.java:
--------------------------------------------------------------------------------
1 | package uk.ac.ebi.beam;
2 |
3 | import org.hamcrest.collection.IsIterableWithSize;
4 | import org.junit.Test;
5 |
6 | import static org.hamcrest.CoreMatchers.hasItems;
7 | import static org.junit.Assert.assertThat;
8 |
9 | /** @author John May */
10 | public class MaximumMatchingTest {
11 |
12 | /** Contrived example to test blossoming. */
13 | @Test public void blossom() throws Exception {
14 |
15 | Graph g = Graph.fromSmiles("CCCCCC1CCCC1CC");
16 | Matching m = Matching.empty(g);
17 |
18 | // initial matching from double-bonds (size = 5)
19 | m.match(1, 2);
20 | m.match(3, 4);
21 | m.match(5, 6);
22 | m.match(7, 8);
23 | m.match(9, 10);
24 |
25 | MaximumMatching.maximise(g, m, 10);
26 |
27 | // once maximised the matching has been augmented such that there
28 | // are now six disjoint edges (only possibly by contracting blossom)
29 | assertThat(m.matches(),
30 | IsIterableWithSize.
116 | * Edge e = Bond.UP.edge(2, 3);
117 | * e.bond(2); // UP
118 | * e.bond(3); // DOWN
119 | *