From 455ab7559bf5c20a7043ccb9ccee15443c349944 Mon Sep 17 00:00:00 2001
From: tpylak <tpylak>
Date: Wed, 4 Aug 2010 11:04:54 +0000
Subject: [PATCH] SE-287 dynamix: read mapping of positions to wells from the
 additional file

SVN: 17336
---
 .../etl/dynamix/HCSImageFileExtractor.java    |   58 +-
 .../etl/dynamix/WellLocationMappingUtils.java |  151 +++
 .../dynamix/WellLocationMappingUtilsTest.java |   62 +
 .../cisd/openbis/dss/etl/dynamix/pos2loc.tsv  | 1153 +++++++++++++++++
 4 files changed, 1409 insertions(+), 15 deletions(-)
 create mode 100644 screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dynamix/WellLocationMappingUtils.java
 create mode 100644 screening/sourceTest/java/ch/systemsx/cisd/openbis/dss/etl/dynamix/WellLocationMappingUtilsTest.java
 create mode 100644 screening/sourceTest/java/ch/systemsx/cisd/openbis/dss/etl/dynamix/pos2loc.tsv

diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dynamix/HCSImageFileExtractor.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dynamix/HCSImageFileExtractor.java
index 88320361ef9..a3538845d5b 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dynamix/HCSImageFileExtractor.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dynamix/HCSImageFileExtractor.java
@@ -19,7 +19,9 @@ package ch.systemsx.cisd.openbis.dss.etl.dynamix;
 import java.io.File;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Properties;
 import java.util.Set;
 
@@ -30,7 +32,9 @@ import ch.systemsx.cisd.bds.hcs.Location;
 import ch.systemsx.cisd.openbis.dss.etl.AbstractHCSImageFileExtractor;
 import ch.systemsx.cisd.openbis.dss.etl.AcquiredPlateImage;
 import ch.systemsx.cisd.openbis.dss.etl.HCSImageFileExtractionResult.Channel;
+import ch.systemsx.cisd.openbis.dss.etl.dynamix.WellLocationMappingUtils.DynamixWellPosition;
 import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.SampleIdentifier;
+import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.WellLocation;
 
 /**
  * Image extractor for DynamiX project - work in progress.
@@ -39,12 +43,17 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.SampleIdentifier;
  */
 public class HCSImageFileExtractor extends AbstractHCSImageFileExtractor
 {
+    private static final String POSITION_MAPPING_FILE_NAME = "pos2loc.tsv";
+
     private final List<String> channelNames;
 
+    private final Map<File/* mapping file */, Map<DynamixWellPosition, WellLocation>> wellLocationMapCache;
+
     public HCSImageFileExtractor(final Properties properties)
     {
         super(properties);
         this.channelNames = extractChannelNames(properties);
+        this.wellLocationMapCache = new HashMap<File, Map<DynamixWellPosition, WellLocation>>();
     }
 
     @Override
@@ -74,23 +83,14 @@ public class HCSImageFileExtractor extends AbstractHCSImageFileExtractor
 
     @Override
     /*
-     * Note: the right mapping for DynamiX project should be found, this one is just to fit all
-     * images on the 24x48 plate. Odd columns contain right position, even contain left position.
-     * @param plateLocation - format left_pos100
+     * @param plateLocation - format row_column
      */
     protected final Location tryGetPlateLocation(final String plateLocation)
     {
         final String[] tokens = StringUtils.split(plateLocation, "_");
-        boolean isLeft = (tokens[0].equalsIgnoreCase("left"));
-        Integer pos = Integer.parseInt(tokens[1].substring(3));
-        assert pos > 0 && pos <= 576 : "wrong position: " + pos;
-
-        int sideShift = isLeft ? 1 : 0;
-        int singleSidedMaxColumn = 24;
-        int row = ((pos - 1) / singleSidedMaxColumn);
-        int col = ((pos - 1) % singleSidedMaxColumn) * 2 + sideShift;
-
-        return new Location(col + 1, row + 1);
+        Integer row = new Integer(tokens[0]);
+        Integer column = new Integer(tokens[1]);
+        return Location.tryCreateLocationFromRowAndColumn(row, column);
     }
 
     @Override
@@ -114,10 +114,12 @@ public class HCSImageFileExtractor extends AbstractHCSImageFileExtractor
             }
             return null;
         }
+        WellLocation wellLocation = getWellLocation(imageFile, tokens);
+
         // "left_dia_pos100_t20100227_152439.tif"
         ImageFileInfo info = new ImageFileInfo();
-        // left_pos100
-        info.setPlateLocationToken(tokens[0] + "_" + tokens[2]);
+        // row_column - will be parsed later. It's unnecessary and should be refactored.
+        info.setPlateLocationToken(wellLocation.getRow() + "_" + wellLocation.getColumn());
         info.setWellLocationToken(null);
         info.setChannelToken(tokens[1]);
 
@@ -126,4 +128,30 @@ public class HCSImageFileExtractor extends AbstractHCSImageFileExtractor
         info.setTimepointToken("" + Arrays.asList(images).indexOf(imageFile));
         return info;
     }
+
+    private WellLocation getWellLocation(File imageFile, final String[] tokens)
+    {
+        Map<DynamixWellPosition, WellLocation> map = getWellLocationMapping(imageFile);
+        DynamixWellPosition wellPos =
+                WellLocationMappingUtils.parseWellPosition(tokens[0], tokens[2]);
+        return map.get(wellPos);
+    }
+
+    private Map<DynamixWellPosition, WellLocation> getWellLocationMapping(File imageFile)
+    {
+        File mappingFile = getMappingFile(imageFile);
+        Map<DynamixWellPosition, WellLocation> map = wellLocationMapCache.get(mappingFile);
+        if (map == null)
+        {
+            map = WellLocationMappingUtils.parseWellLocationMap(mappingFile);
+            wellLocationMapCache.put(mappingFile, map);
+        }
+        return map;
+    }
+
+    private static File getMappingFile(File imageFile)
+    {
+        File mappingDir = imageFile.getParentFile().getParentFile().getParentFile();
+        return new File(mappingDir, POSITION_MAPPING_FILE_NAME);
+    }
 }
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dynamix/WellLocationMappingUtils.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dynamix/WellLocationMappingUtils.java
new file mode 100644
index 00000000000..68d8100e1a4
--- /dev/null
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dynamix/WellLocationMappingUtils.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2010 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.systemsx.cisd.openbis.dss.etl.dynamix;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import ch.systemsx.cisd.common.annotation.BeanProperty;
+import ch.systemsx.cisd.common.parser.TabFileLoader;
+import ch.systemsx.cisd.common.utilities.AbstractHashable;
+import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.WellLocation;
+
+/**
+ * Utility methods to map DynamiX specific position to well location.
+ * 
+ * @author Tomasz Pylak
+ */
+class WellLocationMappingUtils
+{
+    public static Map<DynamixWellPosition, WellLocation> parseWellLocationMap(File mappingFile)
+    {
+        final TabFileLoader<MappingEntry> parser =
+                new TabFileLoader<MappingEntry>(MappingEntry.class);
+        List<MappingEntry> mappingEntries = parser.load(mappingFile);
+        return createMapping(mappingEntries);
+    }
+
+    public static DynamixWellPosition parseWellPosition(String sideToken, String posToken)
+    {
+        String posNumber = posToken.substring("pos".length());
+        return new DynamixWellPosition(new Integer(posNumber), isRight(sideToken));
+    }
+
+    private static Map<DynamixWellPosition, WellLocation> createMapping(
+            List<MappingEntry> mappingEntries)
+    {
+        Map<DynamixWellPosition, WellLocation> mapping =
+                new HashMap<DynamixWellPosition, WellLocation>();
+        for (MappingEntry entry : mappingEntries)
+        {
+            DynamixWellPosition wellPos = parseWellPosition(entry.getSide(), entry.getPosition());
+
+            int row = new Integer(entry.getRow());
+            int col = new Integer(entry.getColumn());
+            WellLocation wellLoc = new WellLocation(row, col);
+
+            mapping.put(wellPos, wellLoc);
+        }
+        return mapping;
+    }
+
+    private static boolean isRight(String sideToken)
+    {
+        return sideToken.equalsIgnoreCase("Right");
+    }
+
+    public static class MappingEntry extends AbstractHashable
+    {
+        private String position;
+
+        private String side;
+
+        private String row;
+
+        private String column;
+
+        public String getPosition()
+        {
+            return position;
+        }
+
+        @BeanProperty(label = "position")
+        public void setPosition(String position)
+        {
+            this.position = position;
+        }
+
+        public String getSide()
+        {
+            return side;
+        }
+
+        @BeanProperty(label = "Side")
+        public void setSide(String side)
+        {
+            this.side = side;
+        }
+
+        public String getRow()
+        {
+            return row;
+        }
+
+        @BeanProperty(label = "row")
+        public void setRow(String row)
+        {
+            this.row = row;
+        }
+
+        public String getColumn()
+        {
+            return column;
+        }
+
+        @BeanProperty(label = "column")
+        public void setColumn(String column)
+        {
+            this.column = column;
+        }
+    }
+
+    static class DynamixWellPosition extends AbstractHashable
+    {
+        private final int position;
+
+        private final boolean isRight;
+
+        public DynamixWellPosition(int position, boolean isRight)
+        {
+            this.position = position;
+            this.isRight = isRight;
+        }
+
+        public int getPosition()
+        {
+            return position;
+        }
+
+        public boolean isRight()
+        {
+            return isRight;
+        }
+    }
+
+}
diff --git a/screening/sourceTest/java/ch/systemsx/cisd/openbis/dss/etl/dynamix/WellLocationMappingUtilsTest.java b/screening/sourceTest/java/ch/systemsx/cisd/openbis/dss/etl/dynamix/WellLocationMappingUtilsTest.java
new file mode 100644
index 00000000000..e861742a58b
--- /dev/null
+++ b/screening/sourceTest/java/ch/systemsx/cisd/openbis/dss/etl/dynamix/WellLocationMappingUtilsTest.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2010 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.systemsx.cisd.openbis.dss.etl.dynamix;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Map;
+
+import org.testng.AssertJUnit;
+import org.testng.annotations.Test;
+
+import ch.systemsx.cisd.openbis.dss.etl.dynamix.WellLocationMappingUtils.DynamixWellPosition;
+import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.WellLocation;
+
+/**
+ * Tests of {@link WellLocationMappingUtils}
+ * 
+ * @author Tomasz Pylak
+ */
+public class WellLocationMappingUtilsTest extends AssertJUnit
+{
+    @Test
+    public void testMapping() throws IOException
+    {
+        Map<DynamixWellPosition, WellLocation> map =
+                WellLocationMappingUtils.parseWellLocationMap(new File(
+                        "sourceTest/java/ch/systemsx/cisd/openbis/dss/etl/dynamix/pos2loc.tsv"));
+        DynamixWellPosition pos = new DynamixWellPosition(100, true);
+
+        WellLocation wellLocation = map.get(pos);
+        assertEquals(wellLocation.getRow(), 5);
+        assertEquals(wellLocation.getColumn(), 17);
+    }
+
+    @Test
+    public void testPositionParsing() throws IOException
+    {
+        DynamixWellPosition expectedPos = new DynamixWellPosition(100, true);
+        DynamixWellPosition pos = WellLocationMappingUtils.parseWellPosition("Right", "100");
+        assertEquals(expectedPos, pos);
+
+        expectedPos = new DynamixWellPosition(5, false);
+        pos = WellLocationMappingUtils.parseWellPosition("Left", "5");
+        assertEquals(expectedPos, pos);
+
+    }
+
+}
diff --git a/screening/sourceTest/java/ch/systemsx/cisd/openbis/dss/etl/dynamix/pos2loc.tsv b/screening/sourceTest/java/ch/systemsx/cisd/openbis/dss/etl/dynamix/pos2loc.tsv
new file mode 100644
index 00000000000..b95b0be8f3a
--- /dev/null
+++ b/screening/sourceTest/java/ch/systemsx/cisd/openbis/dss/etl/dynamix/pos2loc.tsv
@@ -0,0 +1,1153 @@
+position	side	row	column
+1	Right	1	3
+1	Left	1	4
+2	Right	1	5
+2	Left	1	6
+3	Right	1	7
+3	Left	1	8
+4	Right	1	9
+4	Left	1	10
+5	Right	1	11
+5	Left	1	12
+6	Right	1	13
+6	Left	1	14
+7	Right	1	15
+7	Left	1	16
+8	Right	1	17
+8	Left	1	18
+9	Right	1	19
+9	Left	1	20
+10	Right	1	21
+10	Left	1	22
+11	Right	1	23
+11	Left	1	24
+12	Right	1	25
+12	Left	1	26
+13	Right	1	27
+13	Left	1	28
+14	Right	1	29
+14	Left	1	30
+15	Right	1	31
+15	Left	1	32
+16	Right	1	33
+16	Left	1	34
+17	Right	1	35
+17	Left	1	36
+18	Right	1	37
+18	Left	1	38
+19	Right	1	39
+19	Left	1	40
+20	Right	1	41
+20	Left	1	42
+21	Right	1	43
+21	Left	1	44
+22	Right	1	45
+22	Left	1	46
+23	Right	1	47
+23	Left	1	48
+24	Right	2	47
+24	Left	2	48
+25	Right	2	45
+25	Left	2	46
+26	Right	2	43
+26	Left	2	44
+27	Right	2	41
+27	Left	2	42
+28	Right	2	39
+28	Left	2	40
+29	Right	2	37
+29	Left	2	38
+30	Right	2	35
+30	Left	2	36
+31	Right	2	33
+31	Left	2	34
+32	Right	2	31
+32	Left	2	32
+33	Right	2	29
+33	Left	2	30
+34	Right	2	27
+34	Left	2	28
+35	Right	2	25
+35	Left	2	26
+36	Right	2	23
+36	Left	2	24
+37	Right	2	21
+37	Left	2	22
+38	Right	2	19
+38	Left	2	20
+39	Right	2	17
+39	Left	2	18
+40	Right	2	15
+40	Left	2	16
+41	Right	2	13
+41	Left	2	14
+42	Right	2	11
+42	Left	2	12
+43	Right	2	9
+43	Left	2	10
+44	Right	2	7
+44	Left	2	8
+45	Right	2	5
+45	Left	2	6
+46	Right	2	3
+46	Left	2	4
+47	Right	3	3
+47	Left	3	4
+48	Right	3	5
+48	Left	3	6
+49	Right	3	7
+49	Left	3	8
+50	Right	3	9
+50	Left	3	10
+51	Right	3	11
+51	Left	3	12
+52	Right	3	13
+52	Left	3	14
+53	Right	3	15
+53	Left	3	16
+54	Right	3	17
+54	Left	3	18
+55	Right	3	19
+55	Left	3	20
+56	Right	3	21
+56	Left	3	22
+57	Right	3	23
+57	Left	3	24
+58	Right	3	25
+58	Left	3	26
+59	Right	3	27
+59	Left	3	28
+60	Right	3	29
+60	Left	3	30
+61	Right	3	31
+61	Left	3	32
+62	Right	3	33
+62	Left	3	34
+63	Right	3	35
+63	Left	3	36
+64	Right	3	37
+64	Left	3	38
+65	Right	3	39
+65	Left	3	40
+66	Right	3	41
+66	Left	3	42
+67	Right	3	43
+67	Left	3	44
+68	Right	3	45
+68	Left	3	46
+69	Right	3	47
+69	Left	3	48
+70	Right	4	47
+70	Left	4	48
+71	Right	4	45
+71	Left	4	46
+72	Right	4	43
+72	Left	4	44
+73	Right	4	41
+73	Left	4	42
+74	Right	4	39
+74	Left	4	40
+75	Right	4	37
+75	Left	4	38
+76	Right	4	35
+76	Left	4	36
+77	Right	4	33
+77	Left	4	34
+78	Right	4	31
+78	Left	4	32
+79	Right	4	29
+79	Left	4	30
+80	Right	4	27
+80	Left	4	28
+81	Right	4	25
+81	Left	4	26
+82	Right	4	23
+82	Left	4	24
+83	Right	4	21
+83	Left	4	22
+84	Right	4	19
+84	Left	4	20
+85	Right	4	17
+85	Left	4	18
+86	Right	4	15
+86	Left	4	16
+87	Right	4	13
+87	Left	4	14
+88	Right	4	11
+88	Left	4	12
+89	Right	4	9
+89	Left	4	10
+90	Right	4	7
+90	Left	4	8
+91	Right	4	5
+91	Left	4	6
+92	Right	4	3
+92	Left	4	4
+93	Right	5	3
+93	Left	5	4
+94	Right	5	5
+94	Left	5	6
+95	Right	5	7
+95	Left	5	8
+96	Right	5	9
+96	Left	5	10
+97	Right	5	11
+97	Left	5	12
+98	Right	5	13
+98	Left	5	14
+99	Right	5	15
+99	Left	5	16
+100	Right	5	17
+100	Left	5	18
+101	Right	5	19
+101	Left	5	20
+102	Right	5	21
+102	Left	5	22
+103	Right	5	23
+103	Left	5	24
+104	Right	5	25
+104	Left	5	26
+105	Right	5	27
+105	Left	5	28
+106	Right	5	29
+106	Left	5	30
+107	Right	5	31
+107	Left	5	32
+108	Right	5	33
+108	Left	5	34
+109	Right	5	35
+109	Left	5	36
+110	Right	5	37
+110	Left	5	38
+111	Right	5	39
+111	Left	5	40
+112	Right	5	41
+112	Left	5	42
+113	Right	5	43
+113	Left	5	44
+114	Right	5	45
+114	Left	5	46
+115	Right	5	47
+115	Left	5	48
+116	Right	6	47
+116	Left	6	48
+117	Right	6	45
+117	Left	6	46
+118	Right	6	43
+118	Left	6	44
+119	Right	6	41
+119	Left	6	42
+120	Right	6	39
+120	Left	6	40
+121	Right	6	37
+121	Left	6	38
+122	Right	6	35
+122	Left	6	36
+123	Right	6	33
+123	Left	6	34
+124	Right	6	31
+124	Left	6	32
+125	Right	6	29
+125	Left	6	30
+126	Right	6	27
+126	Left	6	28
+127	Right	6	25
+127	Left	6	26
+128	Right	6	23
+128	Left	6	24
+129	Right	6	21
+129	Left	6	22
+130	Right	6	19
+130	Left	6	20
+131	Right	6	17
+131	Left	6	18
+132	Right	6	15
+132	Left	6	16
+133	Right	6	13
+133	Left	6	14
+134	Right	6	11
+134	Left	6	12
+135	Right	6	9
+135	Left	6	10
+136	Right	6	7
+136	Left	6	8
+137	Right	6	5
+137	Left	6	6
+138	Right	6	3
+138	Left	6	4
+139	Right	7	3
+139	Left	7	4
+140	Right	7	5
+140	Left	7	6
+141	Right	7	7
+141	Left	7	8
+142	Right	7	9
+142	Left	7	10
+143	Right	7	11
+143	Left	7	12
+144	Right	7	13
+144	Left	7	14
+145	Right	7	15
+145	Left	7	16
+146	Right	7	17
+146	Left	7	18
+147	Right	7	19
+147	Left	7	20
+148	Right	7	21
+148	Left	7	22
+149	Right	7	23
+149	Left	7	24
+150	Right	7	25
+150	Left	7	26
+151	Right	7	27
+151	Left	7	28
+152	Right	7	29
+152	Left	7	30
+153	Right	7	31
+153	Left	7	32
+154	Right	7	33
+154	Left	7	34
+155	Right	7	35
+155	Left	7	36
+156	Right	7	37
+156	Left	7	38
+157	Right	7	39
+157	Left	7	40
+158	Right	7	41
+158	Left	7	42
+159	Right	7	43
+159	Left	7	44
+160	Right	7	45
+160	Left	7	46
+161	Right	7	47
+161	Left	7	48
+162	Right	8	47
+162	Left	8	48
+163	Right	8	45
+163	Left	8	46
+164	Right	8	43
+164	Left	8	44
+165	Right	8	41
+165	Left	8	42
+166	Right	8	39
+166	Left	8	40
+167	Right	8	37
+167	Left	8	38
+168	Right	8	35
+168	Left	8	36
+169	Right	8	33
+169	Left	8	34
+170	Right	8	31
+170	Left	8	32
+171	Right	8	29
+171	Left	8	30
+172	Right	8	27
+172	Left	8	28
+173	Right	8	25
+173	Left	8	26
+174	Right	8	23
+174	Left	8	24
+175	Right	8	21
+175	Left	8	22
+176	Right	8	19
+176	Left	8	20
+177	Right	8	17
+177	Left	8	18
+178	Right	8	15
+178	Left	8	16
+179	Right	8	13
+179	Left	8	14
+180	Right	8	11
+180	Left	8	12
+181	Right	8	9
+181	Left	8	10
+182	Right	8	7
+182	Left	8	8
+183	Right	8	5
+183	Left	8	6
+184	Right	8	3
+184	Left	8	4
+185	Right	9	3
+185	Left	9	4
+186	Right	9	5
+186	Left	9	6
+187	Right	9	7
+187	Left	9	8
+188	Right	9	9
+188	Left	9	10
+189	Right	9	11
+189	Left	9	12
+190	Right	9	13
+190	Left	9	14
+191	Right	9	15
+191	Left	9	16
+192	Right	9	17
+192	Left	9	18
+193	Right	9	19
+193	Left	9	20
+194	Right	9	21
+194	Left	9	22
+195	Right	9	23
+195	Left	9	24
+196	Right	9	25
+196	Left	9	26
+197	Right	9	27
+197	Left	9	28
+198	Right	9	29
+198	Left	9	30
+199	Right	9	31
+199	Left	9	32
+200	Right	9	33
+200	Left	9	34
+201	Right	9	35
+201	Left	9	36
+202	Right	9	37
+202	Left	9	38
+203	Right	9	39
+203	Left	9	40
+204	Right	9	41
+204	Left	9	42
+205	Right	9	43
+205	Left	9	44
+206	Right	9	45
+206	Left	9	46
+207	Right	9	47
+207	Left	9	48
+208	Right	10	47
+208	Left	10	48
+209	Right	10	45
+209	Left	10	46
+210	Right	10	43
+210	Left	10	44
+211	Right	10	41
+211	Left	10	42
+212	Right	10	39
+212	Left	10	40
+213	Right	10	37
+213	Left	10	38
+214	Right	10	35
+214	Left	10	36
+215	Right	10	33
+215	Left	10	34
+216	Right	10	31
+216	Left	10	32
+217	Right	10	29
+217	Left	10	30
+218	Right	10	27
+218	Left	10	28
+219	Right	10	25
+219	Left	10	26
+220	Right	10	23
+220	Left	10	24
+221	Right	10	21
+221	Left	10	22
+222	Right	10	19
+222	Left	10	20
+223	Right	10	17
+223	Left	10	18
+224	Right	10	15
+224	Left	10	16
+225	Right	10	13
+225	Left	10	14
+226	Right	10	11
+226	Left	10	12
+227	Right	10	9
+227	Left	10	10
+228	Right	10	7
+228	Left	10	8
+229	Right	10	5
+229	Left	10	6
+230	Right	10	3
+230	Left	10	4
+231	Right	11	3
+231	Left	11	4
+232	Right	11	5
+232	Left	11	6
+233	Right	11	7
+233	Left	11	8
+234	Right	11	9
+234	Left	11	10
+235	Right	11	11
+235	Left	11	12
+236	Right	11	13
+236	Left	11	14
+237	Right	11	15
+237	Left	11	16
+238	Right	11	17
+238	Left	11	18
+239	Right	11	19
+239	Left	11	20
+240	Right	11	21
+240	Left	11	22
+241	Right	11	23
+241	Left	11	24
+242	Right	11	25
+242	Left	11	26
+243	Right	11	27
+243	Left	11	28
+244	Right	11	29
+244	Left	11	30
+245	Right	11	31
+245	Left	11	32
+246	Right	11	33
+246	Left	11	34
+247	Right	11	35
+247	Left	11	36
+248	Right	11	37
+248	Left	11	38
+249	Right	11	39
+249	Left	11	40
+250	Right	11	41
+250	Left	11	42
+251	Right	11	43
+251	Left	11	44
+252	Right	11	45
+252	Left	11	46
+253	Right	11	47
+253	Left	11	48
+254	Right	12	47
+254	Left	12	48
+255	Right	12	45
+255	Left	12	46
+256	Right	12	43
+256	Left	12	44
+257	Right	12	41
+257	Left	12	42
+258	Right	12	39
+258	Left	12	40
+259	Right	12	37
+259	Left	12	38
+260	Right	12	35
+260	Left	12	36
+261	Right	12	33
+261	Left	12	34
+262	Right	12	31
+262	Left	12	32
+263	Right	12	29
+263	Left	12	30
+264	Right	12	27
+264	Left	12	28
+265	Right	12	25
+265	Left	12	26
+266	Right	12	23
+266	Left	12	24
+267	Right	12	21
+267	Left	12	22
+268	Right	12	19
+268	Left	12	20
+269	Right	12	17
+269	Left	12	18
+270	Right	12	15
+270	Left	12	16
+271	Right	12	13
+271	Left	12	14
+272	Right	12	11
+272	Left	12	12
+273	Right	12	9
+273	Left	12	10
+274	Right	12	7
+274	Left	12	8
+275	Right	12	5
+275	Left	12	6
+276	Right	12	3
+276	Left	12	4
+277	Right	13	3
+277	Left	13	4
+278	Right	13	5
+278	Left	13	6
+279	Right	13	7
+279	Left	13	8
+280	Right	13	9
+280	Left	13	10
+281	Right	13	11
+281	Left	13	12
+282	Right	13	13
+282	Left	13	14
+283	Right	13	15
+283	Left	13	16
+284	Right	13	17
+284	Left	13	18
+285	Right	13	19
+285	Left	13	20
+286	Right	13	21
+286	Left	13	22
+287	Right	13	23
+287	Left	13	24
+288	Right	13	25
+288	Left	13	26
+289	Right	13	27
+289	Left	13	28
+290	Right	13	29
+290	Left	13	30
+291	Right	13	31
+291	Left	13	32
+292	Right	13	33
+292	Left	13	34
+293	Right	13	35
+293	Left	13	36
+294	Right	13	37
+294	Left	13	38
+295	Right	13	39
+295	Left	13	40
+296	Right	13	41
+296	Left	13	42
+297	Right	13	43
+297	Left	13	44
+298	Right	13	45
+298	Left	13	46
+299	Right	13	47
+299	Left	13	48
+300	Right	14	47
+300	Left	14	48
+301	Right	14	45
+301	Left	14	46
+302	Right	14	43
+302	Left	14	44
+303	Right	14	41
+303	Left	14	42
+304	Right	14	39
+304	Left	14	40
+305	Right	14	37
+305	Left	14	38
+306	Right	14	35
+306	Left	14	36
+307	Right	14	33
+307	Left	14	34
+308	Right	14	31
+308	Left	14	32
+309	Right	14	29
+309	Left	14	30
+310	Right	14	27
+310	Left	14	28
+311	Right	14	25
+311	Left	14	26
+312	Right	14	23
+312	Left	14	24
+313	Right	14	21
+313	Left	14	22
+314	Right	14	19
+314	Left	14	20
+315	Right	14	17
+315	Left	14	18
+316	Right	14	15
+316	Left	14	16
+317	Right	14	13
+317	Left	14	14
+318	Right	14	11
+318	Left	14	12
+319	Right	14	9
+319	Left	14	10
+320	Right	14	7
+320	Left	14	8
+321	Right	14	5
+321	Left	14	6
+322	Right	14	3
+322	Left	14	4
+323	Right	15	3
+323	Left	15	4
+324	Right	15	5
+324	Left	15	6
+325	Right	15	7
+325	Left	15	8
+326	Right	15	9
+326	Left	15	10
+327	Right	15	11
+327	Left	15	12
+328	Right	15	13
+328	Left	15	14
+329	Right	15	15
+329	Left	15	16
+330	Right	15	17
+330	Left	15	18
+331	Right	15	19
+331	Left	15	20
+332	Right	15	21
+332	Left	15	22
+333	Right	15	23
+333	Left	15	24
+334	Right	15	25
+334	Left	15	26
+335	Right	15	27
+335	Left	15	28
+336	Right	15	29
+336	Left	15	30
+337	Right	15	31
+337	Left	15	32
+338	Right	15	33
+338	Left	15	34
+339	Right	15	35
+339	Left	15	36
+340	Right	15	37
+340	Left	15	38
+341	Right	15	39
+341	Left	15	40
+342	Right	15	41
+342	Left	15	42
+343	Right	15	43
+343	Left	15	44
+344	Right	15	45
+344	Left	15	46
+345	Right	15	47
+345	Left	15	48
+346	Right	16	47
+346	Left	16	48
+347	Right	16	45
+347	Left	16	46
+348	Right	16	43
+348	Left	16	44
+349	Right	16	41
+349	Left	16	42
+350	Right	16	39
+350	Left	16	40
+351	Right	16	37
+351	Left	16	38
+352	Right	16	35
+352	Left	16	36
+353	Right	16	33
+353	Left	16	34
+354	Right	16	31
+354	Left	16	32
+355	Right	16	29
+355	Left	16	30
+356	Right	16	27
+356	Left	16	28
+357	Right	16	25
+357	Left	16	26
+358	Right	16	23
+358	Left	16	24
+359	Right	16	21
+359	Left	16	22
+360	Right	16	19
+360	Left	16	20
+361	Right	16	17
+361	Left	16	18
+362	Right	16	15
+362	Left	16	16
+363	Right	16	13
+363	Left	16	14
+364	Right	16	11
+364	Left	16	12
+365	Right	16	9
+365	Left	16	10
+366	Right	16	7
+366	Left	16	8
+367	Right	16	5
+367	Left	16	6
+368	Right	16	3
+368	Left	16	4
+369	Right	17	3
+369	Left	17	4
+370	Right	17	5
+370	Left	17	6
+371	Right	17	7
+371	Left	17	8
+372	Right	17	9
+372	Left	17	10
+373	Right	17	11
+373	Left	17	12
+374	Right	17	13
+374	Left	17	14
+375	Right	17	15
+375	Left	17	16
+376	Right	17	17
+376	Left	17	18
+377	Right	17	19
+377	Left	17	20
+378	Right	17	21
+378	Left	17	22
+379	Right	17	23
+379	Left	17	24
+380	Right	17	25
+380	Left	17	26
+381	Right	17	27
+381	Left	17	28
+382	Right	17	29
+382	Left	17	30
+383	Right	17	31
+383	Left	17	32
+384	Right	17	33
+384	Left	17	34
+385	Right	17	35
+385	Left	17	36
+386	Right	17	37
+386	Left	17	38
+387	Right	17	39
+387	Left	17	40
+388	Right	17	41
+388	Left	17	42
+389	Right	17	43
+389	Left	17	44
+390	Right	17	45
+390	Left	17	46
+391	Right	17	47
+391	Left	17	48
+392	Right	18	47
+392	Left	18	48
+393	Right	18	45
+393	Left	18	46
+394	Right	18	43
+394	Left	18	44
+395	Right	18	41
+395	Left	18	42
+396	Right	18	39
+396	Left	18	40
+397	Right	18	37
+397	Left	18	38
+398	Right	18	35
+398	Left	18	36
+399	Right	18	33
+399	Left	18	34
+400	Right	18	31
+400	Left	18	32
+401	Right	18	29
+401	Left	18	30
+402	Right	18	27
+402	Left	18	28
+403	Right	18	25
+403	Left	18	26
+404	Right	18	23
+404	Left	18	24
+405	Right	18	21
+405	Left	18	22
+406	Right	18	19
+406	Left	18	20
+407	Right	18	17
+407	Left	18	18
+408	Right	18	15
+408	Left	18	16
+409	Right	18	13
+409	Left	18	14
+410	Right	18	11
+410	Left	18	12
+411	Right	18	9
+411	Left	18	10
+412	Right	18	7
+412	Left	18	8
+413	Right	18	5
+413	Left	18	6
+414	Right	18	3
+414	Left	18	4
+415	Right	19	3
+415	Left	19	4
+416	Right	19	5
+416	Left	19	6
+417	Right	19	7
+417	Left	19	8
+418	Right	19	9
+418	Left	19	10
+419	Right	19	11
+419	Left	19	12
+420	Right	19	13
+420	Left	19	14
+421	Right	19	15
+421	Left	19	16
+422	Right	19	17
+422	Left	19	18
+423	Right	19	19
+423	Left	19	20
+424	Right	19	21
+424	Left	19	22
+425	Right	19	23
+425	Left	19	24
+426	Right	19	25
+426	Left	19	26
+427	Right	19	27
+427	Left	19	28
+428	Right	19	29
+428	Left	19	30
+429	Right	19	31
+429	Left	19	32
+430	Right	19	33
+430	Left	19	34
+431	Right	19	35
+431	Left	19	36
+432	Right	19	37
+432	Left	19	38
+433	Right	19	39
+433	Left	19	40
+434	Right	19	41
+434	Left	19	42
+435	Right	19	43
+435	Left	19	44
+436	Right	19	45
+436	Left	19	46
+437	Right	19	47
+437	Left	19	48
+438	Right	20	47
+438	Left	20	48
+439	Right	20	45
+439	Left	20	46
+440	Right	20	43
+440	Left	20	44
+441	Right	20	41
+441	Left	20	42
+442	Right	20	39
+442	Left	20	40
+443	Right	20	37
+443	Left	20	38
+444	Right	20	35
+444	Left	20	36
+445	Right	20	33
+445	Left	20	34
+446	Right	20	31
+446	Left	20	32
+447	Right	20	29
+447	Left	20	30
+448	Right	20	27
+448	Left	20	28
+449	Right	20	25
+449	Left	20	26
+450	Right	20	23
+450	Left	20	24
+451	Right	20	21
+451	Left	20	22
+452	Right	20	19
+452	Left	20	20
+453	Right	20	17
+453	Left	20	18
+454	Right	20	15
+454	Left	20	16
+455	Right	20	13
+455	Left	20	14
+456	Right	20	11
+456	Left	20	12
+457	Right	20	9
+457	Left	20	10
+458	Right	20	7
+458	Left	20	8
+459	Right	20	5
+459	Left	20	6
+460	Right	20	3
+460	Left	20	4
+461	Right	21	3
+461	Left	21	4
+462	Right	21	5
+462	Left	21	6
+463	Right	21	7
+463	Left	21	8
+464	Right	21	9
+464	Left	21	10
+465	Right	21	11
+465	Left	21	12
+466	Right	21	13
+466	Left	21	14
+467	Right	21	15
+467	Left	21	16
+468	Right	21	17
+468	Left	21	18
+469	Right	21	19
+469	Left	21	20
+470	Right	21	21
+470	Left	21	22
+471	Right	21	23
+471	Left	21	24
+472	Right	21	25
+472	Left	21	26
+473	Right	21	27
+473	Left	21	28
+474	Right	21	29
+474	Left	21	30
+475	Right	21	31
+475	Left	21	32
+476	Right	21	33
+476	Left	21	34
+477	Right	21	35
+477	Left	21	36
+478	Right	21	37
+478	Left	21	38
+479	Right	21	39
+479	Left	21	40
+480	Right	21	41
+480	Left	21	42
+481	Right	21	43
+481	Left	21	44
+482	Right	21	45
+482	Left	21	46
+483	Right	21	47
+483	Left	21	48
+484	Right	22	47
+484	Left	22	48
+485	Right	22	45
+485	Left	22	46
+486	Right	22	43
+486	Left	22	44
+487	Right	22	41
+487	Left	22	42
+488	Right	22	39
+488	Left	22	40
+489	Right	22	37
+489	Left	22	38
+490	Right	22	35
+490	Left	22	36
+491	Right	22	33
+491	Left	22	34
+492	Right	22	31
+492	Left	22	32
+493	Right	22	29
+493	Left	22	30
+494	Right	22	27
+494	Left	22	28
+495	Right	22	25
+495	Left	22	26
+496	Right	22	23
+496	Left	22	24
+497	Right	22	21
+497	Left	22	22
+498	Right	22	19
+498	Left	22	20
+499	Right	22	17
+499	Left	22	18
+500	Right	22	15
+500	Left	22	16
+501	Right	22	13
+501	Left	22	14
+502	Right	22	11
+502	Left	22	12
+503	Right	22	9
+503	Left	22	10
+504	Right	22	7
+504	Left	22	8
+505	Right	22	5
+505	Left	22	6
+506	Right	22	3
+506	Left	22	4
+507	Right	23	3
+507	Left	23	4
+508	Right	23	5
+508	Left	23	6
+509	Right	23	7
+509	Left	23	8
+510	Right	23	9
+510	Left	23	10
+511	Right	23	11
+511	Left	23	12
+512	Right	23	13
+512	Left	23	14
+513	Right	23	15
+513	Left	23	16
+514	Right	23	17
+514	Left	23	18
+515	Right	23	19
+515	Left	23	20
+516	Right	23	21
+516	Left	23	22
+517	Right	23	23
+517	Left	23	24
+518	Right	23	25
+518	Left	23	26
+519	Right	23	27
+519	Left	23	28
+520	Right	23	29
+520	Left	23	30
+521	Right	23	31
+521	Left	23	32
+522	Right	23	33
+522	Left	23	34
+523	Right	23	35
+523	Left	23	36
+524	Right	23	37
+524	Left	23	38
+525	Right	23	39
+525	Left	23	40
+526	Right	23	41
+526	Left	23	42
+527	Right	23	43
+527	Left	23	44
+528	Right	23	45
+528	Left	23	46
+529	Right	23	47
+529	Left	23	48
+530	Right	24	47
+530	Left	24	48
+531	Right	24	45
+531	Left	24	46
+532	Right	24	43
+532	Left	24	44
+533	Right	24	41
+533	Left	24	42
+534	Right	24	39
+534	Left	24	40
+535	Right	24	37
+535	Left	24	38
+536	Right	24	35
+536	Left	24	36
+537	Right	24	33
+537	Left	24	34
+538	Right	24	31
+538	Left	24	32
+539	Right	24	29
+539	Left	24	30
+540	Right	24	27
+540	Left	24	28
+541	Right	24	25
+541	Left	24	26
+542	Right	24	23
+542	Left	24	24
+543	Right	24	21
+543	Left	24	22
+544	Right	24	19
+544	Left	24	20
+545	Right	24	17
+545	Left	24	18
+546	Right	24	15
+546	Left	24	16
+547	Right	24	13
+547	Left	24	14
+548	Right	24	11
+548	Left	24	12
+549	Right	24	9
+549	Left	24	10
+550	Right	24	7
+550	Left	24	8
+551	Right	24	5
+551	Left	24	6
+552	Right	24	3
+552	Left	24	4
+553	Right	24	1
+553	Left	24	2
+554	Right	23	1
+554	Left	23	2
+555	Right	22	1
+555	Left	22	2
+556	Right	21	1
+556	Left	21	2
+557	Right	20	1
+557	Left	20	2
+558	Right	19	1
+558	Left	19	2
+559	Right	18	1
+559	Left	18	2
+560	Right	17	1
+560	Left	17	2
+561	Right	16	1
+561	Left	16	2
+562	Right	15	1
+562	Left	15	2
+563	Right	14	1
+563	Left	14	2
+564	Right	13	1
+564	Left	13	2
+565	Right	12	1
+565	Left	12	2
+566	Right	11	1
+566	Left	11	2
+567	Right	10	1
+567	Left	10	2
+568	Right	9	1
+568	Left	9	2
+569	Right	8	1
+569	Left	8	2
+570	Right	7	1
+570	Left	7	2
+571	Right	6	1
+571	Left	6	2
+572	Right	5	1
+572	Left	5	2
+573	Right	4	1
+573	Left	4	2
+574	Right	3	1
+574	Left	3	2
+575	Right	2	1
+575	Left	2	2
+576	Right	1	1
+576	Left	1	2
\ No newline at end of file
-- 
GitLab