java/trunk/integration-tests
--- java/trunk/integration-tests/pom.xml (rev 0)
+++ java/trunk/integration-tests/pom.xml 2014-05-16 01:55:53 UTC (rev 587)
@@ -0,0 +1,52 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+ <artifactId>hps-integration-tests</artifactId>
+ <name>integration-tests</name>
+ <description>Integration test suite</description>
+
+ <parent>
+ <groupId>org.hps</groupId>
+ <artifactId>hps-parent</artifactId>
+ <relativePath>../parent/pom.xml</relativePath>
+ <version>3.0.2-SNAPSHOT</version>
+ </parent>
+
+ <scm>
+ <url>http://java.freehep.org/svn/repos/hps/list/java/trunk/integration-tests/</url>
+ <connection>scm:svn:svn://svn.freehep.org/hps/java/trunk/integration-tests/</connection>
+ <developerConnection>scm:svn:svn://svn.freehep.org/hps/java/trunk/integration-tests/</developerConnection>
+ </scm>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.hps</groupId>
+ <artifactId>hps-distribution</artifactId>
+ </dependency>
+ </dependencies>
+
+ <profiles>
+ <profile>
+ <id>non-slac</id>
+ <activation>
+ <activeByDefault>false</activeByDefault>
+ <file>
+ <missing>/nfs/slac/g/hps3/</missing>
+ </file>
+ </activation>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <excludes>
+ <exclude>org/hps/EcalReadoutSimTest.java</exclude>
+ </excludes>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+</project>
java/trunk/integration-tests/src/test/java/org/hps
--- java/trunk/integration-tests/src/test/java/org/hps/EcalReadoutSimTest.java (rev 0)
+++ java/trunk/integration-tests/src/test/java/org/hps/EcalReadoutSimTest.java 2014-05-16 01:55:53 UTC (rev 587)
@@ -0,0 +1,428 @@
+package org.hps;
+
+import hep.aida.IHistogram1D;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.lcsim.event.EventHeader;
+import org.lcsim.event.GenericObject;
+import org.lcsim.event.LCRelation;
+import org.lcsim.event.MCParticle;
+import org.lcsim.event.RawCalorimeterHit;
+import org.lcsim.event.RawTrackerHit;
+import org.lcsim.job.AidaSaveDriver;
+import org.lcsim.job.JobControlManager;
+import org.lcsim.util.Driver;
+import org.lcsim.util.aida.AIDA;
+import org.lcsim.util.loop.LCSimLoop;
+
+/**
+ * <p>
+ * This test case runs the ECAL readout simulation on some MC input data
+ * and then checks the output in detail against a set of known-good answers.
+ * </p>
+ * <p>
+ * The test method checks the following output values:
+ * </p>
+ * <ul>
+ * <li>Total number of events processed</li>
+ * <li>Total number of collection objects of various types across all events</li>
+ * <li>Exact event numbers of all triggered events from the input</li>
+ * <li>RMS and mean of histograms generated from the output data</li>
+ * </ul>
+ * <p>
+ * The readout is run with a steering file copied from
+ * <tt>/org/hps/steering/readout/HPS2014ReadoutToLcio.lcsim</tt>
+ * with the <tt>addNoise</tt> settings set to <tt>false</tt> for
+ * reproducibility.
+ * </p>
+ * <p>
+ * The input LCIO MC events are from a (large) file that is on SLAC NFS at
+ * <tt>/nfs/slac/g/hps3/data/testcase/ecal_readout_sim_input.slcio</tt>.
+ * When this file is not accessible, this test will not run with the build
+ * due to activation of the <tt>non-slac</tt> profile in the project's
+ * <tt>pom.xml</tt> file.
+ * </p>
+ * <p>
+ * If the legitimate output of the ECAL readout simulation changes at all,
+ * then this test will fail, and all the answer keys need to be updated
+ * to fix it!
+ * </p>
+ *
+ * @author Jeremy McCormick <[log in to unmask]>
+ */
+public class EcalReadoutSimTest extends TestCase {
+
+ // Expected values of event and collection object totals.
+ static final int expectedEvents = 1298;
+ static final int expectedMCParticles = 68937;
+ static final int expectedRawCalorimeterHits = 86475;
+ static final int expectedRawTrackerHits = 99732;
+ static final int expectedRelations = 116629;
+ static final int expectedFpgaData = 15576;
+ static final int expectedReadoutTimestamps = 3894;
+ static final int expectedTriggerBanks = 1298;
+
+ // Expected values of histogram statistics.
+ static final double expectedCalAmplitudePlotRms = 2371.436725801633;
+ static final double expectedCalAmplitudePlotMean = 4538.9994449262795;
+ static final double expectedCalTimestampPlotRms = 1744.1359529793683;
+ static final double expectedCalTimestampPlotMean = 2769.631361665221;
+ static final double expectedReadoutTimestampPlotRms = 283892.28438521083;
+ static final double expectedReadoutTimestampPlotMean = 494337.30883864337;
+ static final double expectedAdcValuesPlotRms = 817.8012108797172;
+ static final double expectedAdcValuesPlotMean = 4786.569434486355;
+
+ // Name of class which will be used a lot for static variables below.
+ static final String className = EcalReadoutSimTest.class.getSimpleName();
+
+ // Resource locations.
+ static final String resourceDir = "/org/hps/ecalreadoutsim/";
+ static final String steeringResource = resourceDir + className + ".lcsim";
+ static final String triggeredEventsResource = resourceDir + "triggered_events.txt";
+
+ // File information.
+ static final File inputFile = new File("/nfs/slac/g/hps3/data/testcase/ecal_readout_sim_input.slcio");
+ static final File outputDir = new File("./target/test-output/" + className);
+ static final File outputFile = new File(outputDir + File.separator + className);
+ static final File aidaOutputFile = new File(outputDir + File.separator + className + ".aida");
+
+ // Default run number.
+ static final int runNumber = 0;
+
+ // Collection names.
+ static final String mcparticleCollectionName = "MCParticle";
+ static final String rawCalorimeterHitCollectionName = "EcalReadoutHits";
+ static final String rawTrackerHitCollectionName = "SVTRawTrackerHits";
+ static final String relationCollectionName = "SVTTrueHitRelations";
+ static final String fpgaDataCollectionName = "FPGAData";
+ static final String readoutTimestampsCollectionName = "ReadoutTimestamps";
+ static final String triggerBankCollectionName = "TriggerBank";
+ static final String[] collectionNames = {
+ mcparticleCollectionName,
+ rawCalorimeterHitCollectionName,
+ rawTrackerHitCollectionName,
+ relationCollectionName,
+ fpgaDataCollectionName,
+ readoutTimestampsCollectionName,
+ triggerBankCollectionName };
+
+ /**
+ * Run an integration test of the ECAL readout simulation.
+ */
+ public void testEcalReadoutSim() {
+
+ // Run the ECAL readout simulation.
+ runEcalReadoutSim();
+
+ // Check the output against answer keys.
+ checkOutput();
+ }
+
+ /**
+ * This method runs the simulation and writes the data to an output LCIO file
+ * located in the <tt>target</tt> directory.
+ */
+ private void runEcalReadoutSim() {
+
+ if (!inputFile.exists()) {
+ System.err.println("File " + inputFile.getPath() + " is not accessible.");
+ throw new RuntimeException("Input file not found.");
+ }
+
+ outputDir.mkdirs();
+ if (!outputDir.exists()) {
+ System.err.println("Failed to create output directory " + outputDir.getPath());
+ throw new RuntimeException("Failed to create output directory.");
+ }
+
+ JobControlManager job = new JobControlManager();
+ job.addInputFile(inputFile);
+ job.addVariableDefinition("runNumber", new Integer(runNumber).toString());
+ job.addVariableDefinition("outputFile", outputFile.getPath());
+ job.setup(steeringResource);
+ job.run();
+
+ // Must clear the AIDA tree so ECAL readout sim plots don't show-up in our output!
+ clearAidaTree();
+ }
+
+ /**
+ * Clear out top-level objects in the AIDA tree that are generated when running
+ * the readout simulation.
+ */
+ private void clearAidaTree() {
+ String[] objects = AIDA.defaultInstance().tree().listObjectNames("/");
+ for (String object : objects) {
+ AIDA.defaultInstance().tree().rm(object);
+ }
+ }
+
+ /**
+ * This method checks the output against a set of answer keys.
+ */
+ private void checkOutput() {
+
+ // Run QA drivers over the readout output file.
+ LCSimLoop loop = new LCSimLoop();
+ File readoutFile = new File(outputFile.getPath() + ".slcio");
+ try {
+ loop.setLCIORecordSource(readoutFile);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ CheckDriver checkDriver = new CheckDriver();
+ AidaSaveDriver aidaDriver = new AidaSaveDriver();
+ aidaDriver.setOutputFileName(aidaOutputFile.getPath());
+ loop.add(checkDriver);
+ loop.add(new PlotsDriver());
+ loop.add(aidaDriver);
+ try {
+ loop.loop(-1);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
+ //
+ // Print event summary.
+ //
+ System.out.println();
+ System.out.println("---- Summary ----");
+ System.out.println("events from Driver = " + checkDriver.getNumberOfEvents());
+ System.out.println("events from loop = " + loop.getSupplied());
+ System.out.println("RawCalorimeterHits = " + checkDriver.getNumberOfRawCalorimeterHits());
+ System.out.println("RawTrackerHits = " + checkDriver.getNumberOfRawTrackerHits());
+ System.out.println("MCParticles = " + checkDriver.getNumberOfMCParticles());
+ System.out.println("Relations = " + checkDriver.getNumberOfRelations());
+ System.out.println("FPGAData = " + checkDriver.getNumberOfFpgaData());
+ System.out.println("ReadoutTimestamps = " + checkDriver.getNumberOfReadoutTimestamps());
+ System.out.println("TriggerBanks = " + checkDriver.getNumberOfTriggerBanks());
+
+ //
+ // Check for expected number of events and collection objects across the entire run.
+ //
+ assertEquals(expectedEvents, loop.getSupplied());
+ assertEquals(expectedEvents, checkDriver.getNumberOfEvents());
+ assertEquals(expectedRawCalorimeterHits, checkDriver.getNumberOfRawCalorimeterHits());
+ assertEquals(expectedRawTrackerHits, checkDriver.getNumberOfRawTrackerHits());
+ assertEquals(expectedMCParticles, checkDriver.getNumberOfMCParticles());
+ assertEquals(expectedRelations, checkDriver.getNumberOfRelations());
+ assertEquals(expectedFpgaData, checkDriver.getNumberOfFpgaData());
+ assertEquals(expectedReadoutTimestamps, checkDriver.getNumberOfReadoutTimestamps());
+ assertEquals(expectedTriggerBanks, checkDriver.getNumberOfTriggerBanks());
+
+ //
+ // Check that the list of triggered events is exactly the same as a stored answer key.
+ //
+ List<Integer> expectedTriggeredEvents = readExpectedTriggeredEvents();
+ List<Integer> actualTriggeredEvents = checkDriver.getTriggeredEvents();
+ assertEquals("Number of triggered events is different.", expectedTriggeredEvents.size(), actualTriggeredEvents.size());
+ assertTrue("Event trigger lists are not equal.", expectedTriggeredEvents.equals(actualTriggeredEvents));
+
+ //
+ // Check the statistics of plots that are now contained in the global AIDA instance.
+ //
+ AIDA aida = AIDA.defaultInstance();
+
+ IHistogram1D amplitudePlot = aida.histogram1D("/" + rawCalorimeterHitCollectionName + "/Amplitude");
+ System.out.println("amplitudePlot rms = " + amplitudePlot.rms());
+ System.out.println("amplitudePlot mean = " + amplitudePlot.mean());
+ assertEquals(expectedCalAmplitudePlotRms, amplitudePlot.rms());
+ assertEquals(expectedCalAmplitudePlotMean, amplitudePlot.mean());
+
+ IHistogram1D timestampPlot = aida.histogram1D("/" + rawCalorimeterHitCollectionName + "/Timestamp");
+ System.out.println("timestampPlot rms = " + timestampPlot.rms());
+ System.out.println("timestampPlot mean = " + timestampPlot.mean());
+ assertEquals(expectedCalTimestampPlotRms, timestampPlot.rms());
+ assertEquals(expectedCalTimestampPlotMean, timestampPlot.mean());
+
+ IHistogram1D adcValuesPlot = aida.histogram1D("/" + rawTrackerHitCollectionName + "/ADCValues");
+ System.out.println("adcValuePlot rms = " + adcValuesPlot.rms());
+ System.out.println("adcValuePlot mean = " + adcValuesPlot.mean());
+ assertEquals(expectedAdcValuesPlotRms, adcValuesPlot.rms());
+ assertEquals(expectedAdcValuesPlotMean, adcValuesPlot.mean());
+
+ IHistogram1D readoutTimestampPlot = aida.histogram1D("/" + readoutTimestampsCollectionName + "/Timestamp");
+ System.out.println("readoutTimestampPlot rms = " + readoutTimestampPlot.rms());
+ System.out.println("readoutTimestampPlot mean = " + readoutTimestampPlot.mean());
+ assertEquals(expectedReadoutTimestampPlotRms, readoutTimestampPlot.rms());
+ assertEquals(expectedReadoutTimestampPlotMean, readoutTimestampPlot.mean());
+ }
+
+ /**
+ * Read in a list of expected triggered event numbers for this input file.
+ * @return The list of expected triggered event numbers.
+ */
+ private List<Integer> readExpectedTriggeredEvents() {
+ List<Integer> triggeredEvents = new ArrayList<Integer>();
+ InputStream is = this.getClass().getResourceAsStream(triggeredEventsResource);
+ BufferedReader br = new BufferedReader(new InputStreamReader(is));
+ String line;
+ try {
+ while ((line = br.readLine()) != null) {
+ Integer eventNumber = Integer.parseInt(line);
+ if (!triggeredEvents.contains(eventNumber))
+ triggeredEvents.add(eventNumber);
+ else
+ throw new RuntimeException("Duplicate event number " + eventNumber + " in input.");
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ Collections.sort(triggeredEvents);
+ return triggeredEvents;
+ }
+
+ /**
+ * Driver to accumulate statistics and that will be checked against the answer keys.
+ */
+ static class CheckDriver extends Driver {
+
+ int events = 0;
+ int mcparticles = 0;
+ int rawCalorimeterHits = 0;
+ int rawTrackerHits = 0;
+ int relations = 0;
+ int fpgaData = 0;
+ int readoutTimestamps = 0;
+ int triggerBanks = 0;
+
+ List<Integer> triggeredEvents = new ArrayList<Integer>();
+
+ public void process(EventHeader event) {
+
+ ++events;
+
+ if (!triggeredEvents.contains(event.getEventNumber())) {
+ triggeredEvents.add(event.getEventNumber());
+ } else {
+ throw new RuntimeException("Duplicate event " + event.getEventNumber() + " read from input LCIO file.");
+ }
+
+ List<RawCalorimeterHit> rawCalorimeterHitCollection =
+ event.get(RawCalorimeterHit.class, rawCalorimeterHitCollectionName);
+ rawCalorimeterHits += rawCalorimeterHitCollection.size();
+
+ List<RawTrackerHit> rawTrackerHitCollection =
+ event.get(RawTrackerHit.class, rawTrackerHitCollectionName);
+ rawTrackerHits += rawTrackerHitCollection.size();
+
+ List<MCParticle> mcparticleCollection =
+ event.get(MCParticle.class, mcparticleCollectionName);
+ mcparticles += mcparticleCollection.size();
+
+ List<LCRelation> relationCollection =
+ event.get(LCRelation.class, relationCollectionName);
+ relations += relationCollection.size();
+
+ List<GenericObject> fpgaDataCollection =
+ event.get(GenericObject.class, fpgaDataCollectionName);
+ fpgaData += fpgaDataCollection.size();
+
+ List<GenericObject> readoutTimestampsCollection =
+ event.get(GenericObject.class, readoutTimestampsCollectionName);
+ readoutTimestamps += readoutTimestampsCollection.size();
+
+ List<GenericObject> triggerBankCollection =
+ event.get(GenericObject.class, triggerBankCollectionName);
+ triggerBanks += triggerBankCollection.size();
+ }
+
+ int getNumberOfEvents() {
+ return events;
+ }
+
+ int getNumberOfMCParticles() {
+ return mcparticles;
+ }
+
+ int getNumberOfRawCalorimeterHits() {
+ return rawCalorimeterHits;
+ }
+
+ int getNumberOfRawTrackerHits() {
+ return rawTrackerHits;
+ }
+
+ int getNumberOfRelations() {
+ return relations;
+ }
+
+ int getNumberOfFpgaData() {
+ return fpgaData;
+ }
+
+ int getNumberOfReadoutTimestamps() {
+ return readoutTimestamps;
+ }
+
+ int getNumberOfTriggerBanks() {
+ return triggerBanks;
+ }
+
+ List<Integer> getTriggeredEvents() {
+ return triggeredEvents;
+ }
+
+ public void endOfData() {
+ Collections.sort(triggeredEvents);
+ }
+ }
+
+ /**
+ * Simple Driver for generating a few histograms that will be checked.
+ */
+ static class PlotsDriver extends Driver {
+
+ AIDA aida = AIDA.defaultInstance();
+
+ public void startOfData() {
+
+ for (String collectionName : collectionNames) {
+ aida.tree().cd("/");
+ aida.tree().mkdir(collectionName);
+ }
+
+ aida.histogram1D("/" + rawCalorimeterHitCollectionName + "/Amplitude", 3000, 0., 30000.);
+ aida.histogram1D("/" + rawCalorimeterHitCollectionName + "/Timestamp", 6500, 0., 6500.);
+
+ aida.histogram1D("/" + readoutTimestampsCollectionName + "/Timestamp", 1050, 0., 1050000.);
+
+ aida.histogram1D("/" + rawTrackerHitCollectionName + "/ADCValues", 2600, 4000., 30000.);
+ }
+
+ public void process(EventHeader event) {
+
+ aida.tree().cd("/" + rawCalorimeterHitCollectionName);
+ List<RawCalorimeterHit> rawCalorimeterHits = event.get(RawCalorimeterHit.class, rawCalorimeterHitCollectionName);
+ for (RawCalorimeterHit hit : rawCalorimeterHits) {
+ aida.histogram1D("Amplitude").fill(hit.getAmplitude());
+ aida.histogram1D("Timestamp").fill(hit.getTimeStamp());
+ }
+
+ aida.tree().cd("/" + readoutTimestampsCollectionName);
+ List<GenericObject> readoutTimestamps = event.get(GenericObject.class, readoutTimestampsCollectionName);
+ for (GenericObject object : readoutTimestamps) {
+ double timestamp = object.getDoubleVal(0);
+ aida.histogram1D("Timestamp").fill(timestamp);
+ }
+
+ aida.tree().cd("/" + rawTrackerHitCollectionName);
+ List<RawTrackerHit> rawTrackerHits = event.get(RawTrackerHit.class, rawTrackerHitCollectionName);
+ for (RawTrackerHit hit : rawTrackerHits) {
+ for (short adcValue : hit.getADCValues()) {
+ aida.histogram1D("ADCValues").fill(adcValue);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
java/trunk/integration-tests/src/test/resources/org/hps/ecalreadoutsim
--- java/trunk/integration-tests/src/test/resources/org/hps/ecalreadoutsim/EcalReadoutSimTest.lcsim (rev 0)
+++ java/trunk/integration-tests/src/test/resources/org/hps/ecalreadoutsim/EcalReadoutSimTest.lcsim 2014-05-16 01:55:53 UTC (rev 587)
@@ -0,0 +1,73 @@
+<!--
+ Execute full trigger+readout simulation and write the results as an LCIO file.
+ @author Sho Uemura <[log in to unmask]>
+ @version $Id: HPS2014ReadoutToLcio.lcsim,v 1.2 2013/03/01 23:22:24 meeg Exp $
+-->
+<lcsim xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
+ xs:noNamespaceSchemaLocation="http://www.lcsim.org/schemas/lcsim/1.0/lcsim.xsd">
+ <execute>
+ <driver name="EventMarkerDriver"/>
+ <driver name="CalibrationDriver"/>
+ <driver name="EcalReadout"/>
+ <driver name="EcalConverter"/>
+ <driver name="EcalClusterer"/>
+ <driver name="EcalTrigger"/>
+ <driver name="SimpleSVTReadout"/>
+ <driver name="TestRunReconToLcio"/>
+ <driver name="ClockDriver"/>
+ <driver name="CleanupDriver"/>
+ </execute>
+
+ <drivers>
+ <driver name="EventMarkerDriver" type="org.lcsim.job.EventMarkerDriver">
+ <eventInterval>1000</eventInterval>
+ </driver>
+ <driver name="CalibrationDriver" type="org.hps.conditions.deprecated.CalibrationDriver">
+<!-- <runNumber>1351</runNumber> -->
+ </driver>
+ <driver name="TestRunReconToLcio" type="org.hps.evio.TestRunTriggeredReconToLcio">
+ <outputFile>${outputFile}.slcio</outputFile>
+ </driver>
+
+ <driver name="EcalReadout" type="org.hps.readout.ecal.FADCEcalReadoutDriver">
+ <coincidenceWindow>1</coincidenceWindow>
+ <ecalName>Ecal</ecalName>
+ <ecalCollectionName>EcalHits</ecalCollectionName>
+ <ecalRawCollectionName>EcalRawHits</ecalRawCollectionName>
+ <addNoise>false</addNoise>
+<!-- <fixedGain>0.15</fixedGain>-->
+<!-- <debug>true</debug>-->
+ </driver>
+
+ <driver name="EcalConverter" type="org.hps.recon.ecal.EcalRawConverterDriver">
+ <rawCollectionName>EcalRawHits</rawCollectionName>
+ <ecalCollectionName>EcalCorrectedHits</ecalCollectionName>
+<!-- <gain>0.15</gain>-->
+ <applyBadCrystalMap>false</applyBadCrystalMap>
+ <use2014Gain>true</use2014Gain>
+<!-- <debug>true</debug>-->
+ </driver>
+
+ <driver name="EcalClusterer" type="org.hps.recon.ecal.GTPEcalClusterer">
+ <ecalName>Ecal</ecalName>
+ <clusterWindow>1</clusterWindow>
+ <ecalCollectionName>EcalCorrectedHits</ecalCollectionName>
+ </driver>
+
+ <driver name="EcalTrigger" type="org.hps.readout.ecal.FADCTriggerDriver">
+ <clusterCollectionName>EcalClusters</clusterCollectionName>
+ <deadTime>10</deadTime>
+ <pairCoincidence>2</pairCoincidence>
+ <outputFileName>${outputFile}.triggers</outputFileName>
+ </driver>
+ <driver name="SimpleSVTReadout" type="org.hps.readout.svt.SimpleSvtReadout">
+ <addNoise>false</addNoise>
+ </driver>
+
+ <driver name="ClockDriver" type="org.hps.readout.ecal.ClockDriver"/>
+ <driver name="CleanupDriver" type="org.lcsim.recon.tracking.digitization.sisim.config.ReadoutCleanupDriver">
+ <collectionNames>TrackerHits</collectionNames>
+ </driver>
+ </drivers>
+</lcsim>
+
java/trunk/integration-tests/src/test/resources/org/hps/ecalreadoutsim
--- java/trunk/integration-tests/src/test/resources/org/hps/ecalreadoutsim/triggered_events.txt (rev 0)
+++ java/trunk/integration-tests/src/test/resources/org/hps/ecalreadoutsim/triggered_events.txt 2014-05-16 01:55:53 UTC (rev 587)
@@ -0,0 +1,1298 @@
+73
+323
+573
+681
+823
+1573
+1823
+2323
+2573
+2823
+3573
+3823
+4073
+4323
+4823
+5323
+5823
+6323
+6573
+6823
+7073
+7323
+8073
+8573
+8823
+9323
+9823
+10323
+10573
+11573
+11823
+12573
+12823
+13073
+13323
+13573
+14323
+14573
+14823
+15073
+15323
+15573
+15823
+16073
+16323
+16823
+17073
+17823
+18323
+18573
+18823
+19073
+19323
+19823
+20073
+20323
+20573
+20823
+21823
+22573
+22823
+23073
+23323
+23573
+23823
+24073
+24323
+24573
+25073
+25323
+25823
+26073
+26323
+26823
+27323
+27823
+28323
+28823
+29073
+29323
+29573
+29823
+30073
+30323
+30573
+31323
+32323
+32823
+33323
+33573
+33823
+34073
+34573
+34823
+35073
+35573
+36323
+36573
+36823
+37073
+37323
+37823
+38073
+38573
+38823
+39073
+39573
+40073
+40573
+41573
+42073
+42573
+43073
+43573
+44323
+44573
+45323
+46073
+46823
+47073
+47573
+47823
+48073
+48323
+48573
+48823
+49323
+49573
+51323
+51573
+52073
+52573
+53073
+53573
+54073
+54323
+54573
+55323
+55573
+55823
+56323
+57073
+57323
+57573
+58323
+58823
+59073
+60823
+61321
+61573
+62073
+62323
+62357
+62823
+63073
+63323
+63573
+63823
+64073
+64323
+64823
+65073
+65573
+65823
+66323
+66823
+67073
+67323
+67573
+68073
+68323
+68823
+69073
+69323
+69573
+70573
+70823
+71073
+71323
+71573
+71823
+72073
+72323
+72573
+73107
+73573
+74073
+74323
+75573
+75823
+76073
+76323
+76573
+77323
+77573
+77823
+78073
+78573
+78823
+79073
+79323
+79573
+80073
+80323
+80573
+81073
+82073
+82323
+82823
+83323
+83573
+83823
+84073
+84573
+84823
+85323
+85823
+86323
+87073
+88073
+88573
+88823
+89073
+89323
+89823
+90073
+90573
+90823
+91073
+91323
+91573
+92073
+92323
+92823
+93073
+93323
+93823
+94073
+94573
+94823
+95073
+95323
+95573
+95823
+96073
+96823
+97323
+97573
+97823
+98323
+98573
+98823
+99073
+99323
+99573
+99823
+100073
+100323
+100573
+100823
+101323
+101823
+102073
+102573
+102823
+103073
+103323
+103573
+104073
+104573
+104823
+105073
+105573
+105823
+106323
+108073
+108323
+108823
+109323
+109573
+109823
+110073
+110573
+111323
+111823
+112073
+112323
+112573
+113073
+113323
+113573
+113821
+114073
+114323
+114823
+115073
+116323
+116823
+117323
+118073
+118573
+118823
+119073
+119323
+119573
+119823
+120073
+120323
+120573
+120823
+121073
+121573
+121823
+122073
+122573
+122823
+123073
+123573
+124073
+124323
+124823
+125323
+125573
+125823
+126323
+126573
+126823
+127073
+127323
+127823
+128073
+128323
+128573
+128823
+129073
+129323
+129573
+130073
+131073
+131209
+131323
+131823
+132073
+132573
+132823
+133073
+133323
+133823
+134073
+134323
+134573
+135073
+135823
+136073
+136573
+136823
+138071
+138323
+138823
+139573
+139823
+140323
+140573
+141073
+141323
+141573
+141823
+142323
+142573
+142823
+143073
+143573
+143823
+144073
+144573
+144823
+145073
+145323
+145573
+145823
+146073
+146323
+146573
+146823
+147073
+147323
+147573
+148073
+148323
+148573
+148823
+149073
+149323
+149823
+150073
+150573
+150823
+151321
+151573
+151823
+152323
+152573
+152823
+153323
+153573
+153823
+154073
+154323
+154823
+155323
+155573
+155823
+156073
+156323
+156823
+157073
+157823
+158323
+158573
+159323
+159823
+160073
+160323
+160573
+161823
+162323
+162573
+162823
+163323
+163573
+163823
+164323
+164573
+164823
+165073
+165323
+165823
+166073
+166323
+166573
+166823
+167073
+167323
+167573
+167823
+168573
+168823
+169323
+169823
+170073
+170823
+171073
+171323
+171573
+172073
+172573
+173073
+173323
+173573
+173823
+174073
+174573
+174823
+175073
+175323
+175573
+176073
+176319
+176823
+177573
+178323
+178573
+178823
+179073
+179573
+179823
+180073
+180573
+180823
+181323
+181573
+181823
+182323
+182573
+183073
+183323
+183573
+183823
+184323
+184823
+185069
+185323
+185573
+185823
+186073
+186323
+186573
+187073
+187323
+187573
+187823
+188073
+188323
+188573
+189073
+189323
+190073
+190323
+191073
+191323
+191823
+192073
+192323
+192573
+193323
+193369
+193573
+194073
+194323
+194823
+195073
+195573
+196571
+197323
+197573
+198823
+199073
+199323
+199573
+200573
+201073
+201573
+201823
+202073
+202573
+203073
+203323
+204073
+204323
+204823
+205323
+205573
+205823
+206073
+207573
+207823
+208073
+208573
+208823
+209323
+209573
+210073
+210323
+210573
+210823
+211073
+211323
+211823
+212073
+212323
+212573
+212823
+213323
+213573
+213823
+214073
+214323
+214573
+215073
+215323
+216323
+216573
+216823
+217073
+217323
+218073
+218323
+218823
+219073
+219573
+219823
+220073
+220323
+220573
+220823
+221323
+221573
+221823
+222323
+222573
+222823
+223573
+224073
+225073
+226073
+226823
+227823
+228073
+228323
+228823
+229073
+229323
+229573
+229823
+230073
+230323
+230573
+230823
+231323
+231573
+232073
+232323
+232573
+233073
+233573
+233823
+234073
+234323
+234573
+235323
+236323
+236573
+236823
+237823
+238073
+238323
+238823
+239323
+239573
+240323
+240821
+241073
+241573
+241823
+242323
+242573
+242823
+243323
+243573
+244073
+244323
+244573
+245073
+245323
+245573
+246073
+246823
+247573
+248323
+249073
+249323
+249573
+249823
+250073
+250323
+250573
+251073
+251573
+251823
+252073
+252323
+252573
+252823
+253073
+253323
+253573
+253823
+254073
+254323
+254573
+255323
+255573
+255823
+256073
+256323
+256573
+257573
+257823
+258073
+258323
+258573
+261073
+261573
+261823
+262073
+262573
+262823
+263073
+263573
+264073
+264323
+264573
+264823
+265073
+265573
+265823
+266073
+266823
+267073
+267323
+267573
+267823
+268073
+268323
+268823
+269323
+270073
+270323
+270573
+270823
+271073
+271323
+271823
+272323
+272573
+272823
+273073
+273323
+273573
+273823
+274073
+274323
+274573
+274823
+275073
+275323
+275823
+276323
+276823
+277073
+277323
+277573
+277823
+278323
+279073
+279323
+279823
+280573
+281073
+281323
+281823
+282323
+282573
+282823
+283073
+283823
+284073
+284323
+284573
+284823
+285073
+286823
+287073
+287573
+287823
+288073
+288323
+288823
+289323
+290073
+290323
+291073
+291323
+291823
+292323
+292573
+293073
+293573
+293823
+293887
+294323
+294575
+294823
+295323
+295573
+295823
+296073
+296573
+296823
+297573
+297823
+298073
+298573
+298823
+299073
+300073
+300323
+300573
+300823
+301073
+301323
+301573
+302323
+302823
+303323
+303573
+303823
+304073
+304573
+304823
+305073
+305323
+306073
+306323
+306823
+307073
+307323
+307573
+307823
+308573
+309073
+309323
+309573
+310073
+310323
+310573
+310823
+311073
+311323
+311573
+311823
+312323
+312573
+312823
+313073
+313573
+313823
+314073
+314323
+314573
+314823
+315073
+315323
+315573
+315823
+316073
+316323
+316573
+316823
+317073
+317573
+318073
+318323
+318573
+319073
+319323
+319573
+319823
+320073
+320823
+321073
+321323
+321573
+322073
+322573
+322823
+323073
+323545
+323823
+324323
+324573
+325073
+325323
+326073
+326573
+326823
+327323
+327573
+327823
+328323
+328573
+328823
+329073
+329323
+329573
+329823
+330073
+330323
+331323
+331823
+332073
+332823
+333073
+333573
+333823
+334323
+334573
+334823
+335073
+335573
+335823
+336573
+337073
+337323
+337573
+337823
+338323
+338823
+339823
+340073
+340323
+340573
+340823
+341323
+341721
+341823
+342573
+342823
+343073
+343573
+343823
+344323
+344823
+345071
+345573
+345823
+346073
+346573
+346823
+347073
+347323
+347573
+347823
+348073
+348573
+348823
+349073
+349573
+349823
+350323
+350573
+350823
+351073
+352073
+352323
+352573
+352823
+353073
+353323
+353573
+354323
+354573
+354823
+355073
+355573
+355823
+356323
+356573
+357573
+358323
+358823
+359073
+359323
+359573
+359823
+360073
+360573
+361073
+361323
+362073
+362323
+363073
+363323
+363823
+364073
+364573
+364823
+365323
+365573
+365823
+366323
+366823
+367073
+367323
+367823
+368073
+368323
+368573
+369323
+369573
+369823
+370073
+370573
+370823
+371073
+371573
+371823
+372073
+372323
+372573
+373323
+373573
+374073
+374823
+375323
+375573
+376073
[truncated at 1000 lines; 302 more skipped]