InChINumbersTools { static long[] parseUSmilesNumbers(String aux, IAtomContainer container) { int index; long[] numbers = new long[container.getAtomCount()]; int[] first = null; int label = 1; if ((index = aux.indexOf("/R:")) >= 0) { int endIndex = aux.indexOf('/', index + 8); if (endIndex<0) endIndex = aux.length(); String[] baseNumbers = aux.substring(index + 8, endIndex).split(";"); first = new int[baseNumbers.length]; Arrays.fill(first, -1); for (int i = 0; i < baseNumbers.length; i++) { String[] numbering = baseNumbers[i].split(","); first[i] = Integer.parseInt(numbering[0]) - 1; for (String number : numbering) { numbers[Integer.parseInt(number) - 1] = label++; } } } else if ((index = aux.indexOf("/N:")) >= 0) { String[] baseNumbers = aux.substring(index + 3, aux.indexOf('/', index + 3)).split(";"); first = new int[baseNumbers.length]; Arrays.fill(first, -1); if ((index = aux.indexOf("/F:")) >= 0) { String[] fixedHNumbers = aux.substring(index + 3, aux.indexOf('/', index + 3)).split(";"); for (int i = 0; i < fixedHNumbers.length; i++) { String component = fixedHNumbers[i]; if (component.charAt(component.length() - 1) == 'm') { int n = component.length() > 1 ? Integer .parseInt(component.substring(0, component.length() - 1)) : 1; for (int j = 0; j < n; j++) { String[] numbering = baseNumbers[i + j].split(","); first[i + j] = Integer.parseInt(numbering[0]) - 1; for (String number : numbering) numbers[Integer.parseInt(number) - 1] = label++; } } else { String[] numbering = component.split(","); for (String number : numbering) numbers[Integer.parseInt(number)-1] = label++; } } } else { for (int i = 0; i < baseNumbers.length; i++) { String[] numbering = baseNumbers[i].split(","); first[i] = Integer.parseInt(numbering[0]) - 1; for (String number : numbering) numbers[Integer.parseInt(number) - 1] = label++; } } } else { throw new IllegalArgumentException("AuxInfo did not contain extractable base numbers (/N: or /R:)."); } for (int v : first) { if (v >= 0) { IAtom atom = container.getAtom(v); if (atom.getFormalCharge() == null) continue; if (atom.getAtomicNumber() == 8 && atom.getFormalCharge() == -1) { List<IAtom> neighbors = container.getConnectedAtomsList(atom); if (neighbors.size() == 1) { IAtom correctedStart = findPiBondedOxygen(container, neighbors.get(0)); if (correctedStart != null) exch(numbers, v, container.indexOf(correctedStart)); } } } } for (int i = 0; i < numbers.length; i++) if (numbers[i] == 0) numbers[i] = label++; return numbers; }  static long[] getNumbers(IAtomContainer atomContainer); static void parseAuxInfo(String aux, long[] numbers); static long[] getUSmilesNumbers(IAtomContainer container);  }
@Test public void parseStandard() throws Exception { assertThat(InChINumbersTools.parseUSmilesNumbers("AuxInfo=1/0/N:3,2,1/rA:3OCC/rB:s1;s2;/rC:;;;", mock(3)), is(new long[]{3, 2, 1})); }
@Test public void parseRecMet() throws Exception { assertThat( InChINumbersTools.parseUSmilesNumbers( "AuxInfo=1/1/N:3,2,4;7;5;6;1/E:(2,3);;;;/F:5m/E:m;;;;/CRV:;;2*1-1;/rA:7PtOCONNCl/rB:s1;s2;d3;s1;s1;s1;/rC:;;;;;;;/R:/0/N:3,7,5,6,4,2,1/E:(3,4)", mock(7)), is(new long[]{7, 6, 1, 5, 3, 4, 2})); }
@Test public void parseFixedH() throws Exception { assertThat(InChINumbersTools.parseUSmilesNumbers( "AuxInfo=1/1/N:4,5,2,3,1/E:(1,2)(4,5)/F:5,4,2,1,3/rA:5NCNCC/rB:s1;d2;s3;s1d4;/rC:;;;;;", mock(5)), is(new long[]{4, 3, 5, 2, 1})); }
@Test public void parseDisconnected() throws Exception { assertThat(InChINumbersTools.parseUSmilesNumbers( "AuxInfo=1/1/N:5,6,3,4,2;1/E:(1,2)(4,5);/F:6,5,3,2,4;m/rA:6ONCNCC/rB:;s2;d3;s4;s2d5;/rC:;;;;;;", mock(6)), is(new long[]{6, 4, 3, 5, 2, 1})); }
@Test public void parseMultipleDisconnected() throws Exception { assertThat( InChINumbersTools.parseUSmilesNumbers( "AuxInfo=1/1/N:5,6,3,4,2;8,9;1;7/E:(1,2)(4,5);(1,2);;/F:6,5,3,2,4;3m/E:;m;;/rA:9ONCNCCOOO/rB:;s2;d3;s4;s2d5;;;d8;/rC:;;;;;;;;;", mock(9)), is(new long[]{8, 4, 3, 5, 2, 1, 9, 6, 7})); }