<commit_before>// .SECTION Thanks
// This test was implemented by Philippe Pebay, Kitware SAS 2012

#include "vtkDataObjectCollection.h"
#include "vtkDoubleArray.h"
#include "vtkIdTypeArray.h"
#include "vtkInformation.h"
#include "vtkMath.h"
#include "vtkMultiBlockDataSet.h"
#include "vtkStringArray.h"
#include "vtkTable.h"
#include "vtkTimerLog.h"
#include "vtkAutoCorrelativeStatistics.h"

//=============================================================================
int TestAutoCorrelativeStatistics( int, char *[] )
{
  int testStatus = 0;

  // ************** Test with 2 columns of input data **************

  // Input data
  double mingledData[] =
    {
      46,
      45,
      47,
      49,
      46,
      47,
      46,
      46,
      47,
      46,
      47,
      49,
      49,
      49,
      47,
      45,
      50,
      50,
      46,
      46,
      51,
      50,
      48,
      48,
      52,
      54,
      48,
      47,
      52,
      52,
      49,
      49,
      53,
      54,
      50,
      50,
      53,
      54,
      50,
      52,
      53,
      53,
      50,
      51,
      54,
      54,
      49,
      49,
      52,
      52,
      50,
      51,
      52,
      52,
      49,
      47,
      48,
      48,
      48,
      50,
      46,
      48,
      47,
      47,
    };

  // Test with entire data set
  int nVals1 = 32;

  vtkDoubleArray* dataset1Arr = vtkDoubleArray::New();
  dataset1Arr->SetNumberOfComponents( 1 );
  dataset1Arr->SetName( "Metric 0" );

  vtkDoubleArray* dataset2Arr = vtkDoubleArray::New();
  dataset2Arr->SetNumberOfComponents( 1 );
  dataset2Arr->SetName( "Metric 1" );

  for ( int i = 0; i < nVals1; ++ i )
    {
    int ti = i << 1;
    dataset1Arr->InsertNextValue( mingledData[ti] );
    dataset2Arr->InsertNextValue( mingledData[ti + 1] );
    }

  // Create input data table
  vtkTable* datasetTable1 = vtkTable::New();
  datasetTable1->AddColumn( dataset1Arr );
  dataset1Arr->Delete();
  datasetTable1->AddColumn( dataset2Arr );
  dataset2Arr->Delete();

  // Create input parameter table for the stationary case
  vtkIdTypeArray* timeLags = vtkIdTypeArray::New();
  timeLags->SetName( "Time Lags" );
  timeLags->SetNumberOfTuples( 1 );
  timeLags->SetValue( 0, 0 );
  vtkTable* paramTable = vtkTable::New();
  paramTable->AddColumn( timeLags );
  timeLags->Delete();

  // Columns of interest
  int nMetrics1 = 2;
  vtkStdString columns1[] =
    {
      "Metric 1",
      "Metric 0"
    };

  // Reference values
  // Means for metrics 0 and 1 respectively
  double meansXs1[] = { 49.21875 , 49.5 };

  // Standard deviations for metrics 0 and 1, respectively
  double vars1[] = { 5.9828629, 7.548397 };

  // Set autocorrelative statistics algorithm and its input data port
  vtkAutoCorrelativeStatistics* as1 = vtkAutoCorrelativeStatistics::New();

  // First verify that absence of input does not cause trouble
  cout << "\n## Verifying that absence of input does not cause trouble... ";
  as1->Update();
  cout << "done.\n";

  // Prepare first test with data
  as1->SetInputData( vtkStatisticsAlgorithm::INPUT_DATA, datasetTable1 );
  datasetTable1->Delete();

  // Select columns of interest
  for ( int i = 0; i < nMetrics1; ++ i )
    {
    as1->AddColumn( columns1[i] );
    }

  // Set spatial cardinality
  as1->SetSliceCardinality( nVals1 );

  // Set parameters for autocorrelation of whole data set with respect to itself
  as1->SetInputData( vtkStatisticsAlgorithm::LEARN_PARAMETERS, paramTable );

  // Test Learn and Derive options
  as1->SetLearnOption( true );
  as1->SetDeriveOption( true );
  as1->SetAssessOption( false );
  as1->SetTestOption( false );
  as1->Update();

  // Get output model tables
  vtkMultiBlockDataSet* outputModelAS1 = vtkMultiBlockDataSet::SafeDownCast( as1->GetOutputDataObject( vtkStatisticsAlgorithm::OUTPUT_MODEL ) );

  cout << "\n## Calculated the following statistics for first data set:\n";
  for ( unsigned b = 0; b < outputModelAS1->GetNumberOfBlocks(); ++ b )
    {
    vtkStdString varName = outputModelAS1->GetMetaData( b )->Get( vtkCompositeDataSet::NAME() );
    cout << "   Variable="
         << varName
         << "\n";

    vtkTable* modelTab = vtkTable::SafeDownCast( outputModelAS1->GetBlock( b ) );
    cout << "   ";
    for ( int i = 0; i < modelTab->GetNumberOfColumns(); ++ i )
      {
      cout << modelTab->GetColumnName( i )
           << "="
           << modelTab->GetValue( 0, i ).ToString()
           << "  ";
      }

    // Verify some of the calculated statistics
    if ( fabs ( modelTab->GetValueByName( 0, "Mean Xs" ).ToDouble() - meansXs1[b] ) > 1.e-6 )
      {
      vtkGenericWarningMacro("Incorrect mean for Xs");
      testStatus = 1;
      }

    if ( fabs ( modelTab->GetValueByName( 0, "Variance Xs" ).ToDouble() - vars1[b] ) > 1.e-5 )
      {
      vtkGenericWarningMacro("Incorrect variance for Xs");
      testStatus = 1;
      }

    if ( fabs ( modelTab->GetValueByName( 0, "Pearson r" ).ToDouble() - 1. ) > 1.e-6 )
      {
      vtkGenericWarningMacro("Incorrect Pearson correlation coefficient");
      testStatus = 1;
      }

    cout << "\n";
    }

  // Test with a slight variation of initial data set (to test model aggregation)
  int nVals2 = 32;

  vtkDoubleArray* dataset4Arr = vtkDoubleArray::New();
  dataset4Arr->SetNumberOfComponents( 1 );
  dataset4Arr->SetName( "Metric 0" );

  vtkDoubleArray* dataset5Arr = vtkDoubleArray::New();
  dataset5Arr->SetNumberOfComponents( 1 );
  dataset5Arr->SetName( "Metric 1" );

  for ( int i = 0; i < nVals2; ++ i )
    {
    int ti = i << 1;
    dataset4Arr->InsertNextValue( mingledData[ti] + 1. );
    dataset5Arr->InsertNextValue( mingledData[ti + 1] );
    }

  vtkTable* datasetTable2 = vtkTable::New();
  datasetTable2->AddColumn( dataset4Arr );
  dataset4Arr->Delete();
  datasetTable2->AddColumn( dataset5Arr );
  dataset5Arr->Delete();

  // Set auto-correlative statistics algorithm and its input data port
  vtkAutoCorrelativeStatistics* as2 = vtkAutoCorrelativeStatistics::New();
  as2->SetInputData( vtkStatisticsAlgorithm::INPUT_DATA, datasetTable2 );

  // Select columns of interest
  for ( int i = 0; i < nMetrics1; ++ i )
    {
    as2->AddColumn( columns1[i] );
    }

  // Set spatial cardinality
  as2->SetSliceCardinality( nVals2 );

  // Set parameters for autocorrelation of whole data set with respect to itself
  as2->SetInputData( vtkStatisticsAlgorithm::LEARN_PARAMETERS, paramTable );

  // Update with Learn option only
  as2->SetLearnOption( true );
  as2->SetDeriveOption( false );
  as2->SetTestOption( false );
  as2->SetAssessOption( false );
  as2->Update();

  // Get output meta tables
  vtkMultiBlockDataSet* outputModelAS2 = vtkMultiBlockDataSet::SafeDownCast( as2->GetOutputDataObject( vtkStatisticsAlgorithm::OUTPUT_MODEL ) );

  cout << "\n## Calculated the following statistics for second data set:\n";
  for ( unsigned b = 0; b < outputModelAS2->GetNumberOfBlocks(); ++ b )
    {
    vtkStdString varName = outputModelAS2->GetMetaData( b )->Get( vtkCompositeDataSet::NAME() );
    cout << "   Variable="
         << varName
         << "\n";

    vtkTable* modelTab = vtkTable::SafeDownCast( outputModelAS2->GetBlock( b ) );
    cout << "   ";
    for ( int i = 0; i < modelTab->GetNumberOfColumns(); ++ i )
      {
      cout << modelTab->GetColumnName( i )
           << "="
           << modelTab->GetValue( 0, i ).ToString()
           << "  ";
      }

    cout << "\n";
    }

  // Test model aggregation by adding new data to engine which already has a model
  as1->SetInputData( vtkStatisticsAlgorithm::INPUT_DATA, datasetTable2 );
  vtkMultiBlockDataSet* model = vtkMultiBlockDataSet::New();
  model->ShallowCopy( outputModelAS1 );
  as1->SetInputData( vtkStatisticsAlgorithm::INPUT_MODEL, model );

  // Clean up
  model->Delete();
  datasetTable2->Delete();
  as2->Delete();

  // Update with Learn and Derive options only
  as1->SetLearnOption( true );
  as1->SetDeriveOption( true );
  as1->SetTestOption( false );
  as1->SetAssessOption( false );
  as1->Update();

  // Updated reference values
  // Means and variances for metrics 0 and 1, respectively
  double meansXs0[] = { 49.71875 , 49.5 };
  double varsXs0[] = { 6.1418651 , 7.548397 * 62. / 63. };

  // Get output meta tables
  outputModelAS1 = vtkMultiBlockDataSet::SafeDownCast( as1->GetOutputDataObject( vtkStatisticsAlgorithm::OUTPUT_MODEL ) );

  cout << "\n## Calculated the following statistics for aggregated (first + second) data set:\n";
  for ( unsigned b = 0; b < outputModelAS1->GetNumberOfBlocks(); ++ b )
    {
    vtkStdString varName = outputModelAS1->GetMetaData( b )->Get( vtkCompositeDataSet::NAME() );
    cout << "   Variable="
         << varName
         << "\n";

    vtkTable* modelTab = vtkTable::SafeDownCast( outputModelAS1->GetBlock( b ) );
    cout << "   ";
    for ( int i = 0; i < modelTab->GetNumberOfColumns(); ++ i )
      {
      cout << modelTab->GetColumnName( i )
           << "="
           << modelTab->GetValue( 0, i ).ToString()
           << "  ";
      }

    cout << "\n";
    }

  // Clean up
  as1->Delete();

  // ************** Test with 2 columns of synthetic data **************

  // Space and time parameters
  vtkIdType nSteps = 2;
  vtkIdType cardSlice = 1000;
  vtkIdType cardTotal = nSteps * cardSlice;

  vtkDoubleArray* lineArr = vtkDoubleArray::New();
  lineArr->SetNumberOfComponents( 1 );
  lineArr->SetName( "Line" );

  vtkDoubleArray* vArr = vtkDoubleArray::New();
  vArr->SetNumberOfComponents( 1 );
  vArr->SetName( "V" );

  vtkDoubleArray* circleArr = vtkDoubleArray::New();
  circleArr->SetNumberOfComponents( 1 );
  circleArr->SetName( "Circle" );

  // Fill data columns
  vtkIdType midPoint = cardTotal >> 1;
  double dAlpha = vtkMath::DoubleTwoPi() / cardSlice;
  for ( int i = 0; i < cardTotal; ++ i )
    {
    lineArr->InsertNextValue( i );
    if ( i < midPoint )
      {
      vArr->InsertNextValue( cardTotal - i );
      circleArr->InsertNextValue( cos( i * dAlpha ) );
      }
    else
      {
      vArr->InsertNextValue( i );
      circleArr->InsertNextValue( sin( i * dAlpha ) );
      }
    }

  // Create input data table
  vtkTable* datasetTable3 = vtkTable::New();
  datasetTable3->AddColumn( lineArr );
  lineArr->Delete();
  datasetTable3->AddColumn( vArr );
  vArr->Delete();
  datasetTable3->AddColumn( circleArr );
  circleArr->Delete();

  // Columns of interest
  int nMetrics2 = 3;
  vtkStdString columns2[] =
    {
      "Line",
      "V",
      "Circle"
    };

  // Reference values
  // Means of Xs for circle, line, and v-shaped variables respectively
  double halfNm1 = .5 * ( cardSlice - 1 );
  double meansXs3[] = { 0., halfNm1, cardTotal - halfNm1 };

  // Pearson r values for circle, line, and v-shaped variables respectively
  double pearson3[] = { 0., 1., -1. };

  // Prepare autocorrelative statistics algorithm and its input data port
  vtkAutoCorrelativeStatistics* as3 = vtkAutoCorrelativeStatistics::New();
  as3->SetInputData( vtkStatisticsAlgorithm::INPUT_DATA, datasetTable3 );
  datasetTable3->Delete();

  // Select Columns of Interest
  for ( int i = 0; i < nMetrics2; ++ i )
    {
    as3->AddColumn( columns2[i] );
    }

  // Set spatial cardinality
  as3->SetSliceCardinality( cardSlice );

  // Set autocorrelation parameters for first slice against slice following midpoint
  paramTable->SetValueByName( 0, "Time Lags", nSteps / 2 );
  as3->SetInputData( vtkStatisticsAlgorithm::LEARN_PARAMETERS, paramTable );

  // Test Learn, and Derive options
  as3->SetLearnOption( true );
  as3->SetDeriveOption( true );
  as3->SetAssessOption( false );
  as3->SetTestOption( false );
  as3->Update();

  // Get output data and meta tables
  vtkMultiBlockDataSet* outputModelAS3 = vtkMultiBlockDataSet::SafeDownCast( as3->GetOutputDataObject( vtkStatisticsAlgorithm::OUTPUT_MODEL ) );
  vtkTable* modelTab = vtkTable::SafeDownCast( outputModelAS3->GetBlock( 0 ) );

  cout << "\n## Calculated the following statistics for third data set:\n";
  for ( unsigned b = 0; b < outputModelAS3->GetNumberOfBlocks(); ++ b )
    {
    vtkStdString varName = outputModelAS3->GetMetaData( b )->Get( vtkCompositeDataSet::NAME() );
    cout << "   Variable="
         << varName
         << "\n";

    vtkTable* modelTab = vtkTable::SafeDownCast( outputModelAS3->GetBlock( b ) );
    cout << "   ";
    for ( int i = 0; i < modelTab->GetNumberOfColumns(); ++ i )
      {
      cout << modelTab->GetColumnName( i )
           << "="
           << modelTab->GetValue( 0, i ).ToString()
           << "  ";
      }

    // Verify some of the calculated statistics
    if ( fabs ( modelTab->GetValueByName( 0, "Mean Xs" ).ToDouble() - meansXs3[b] ) > 1.e-6 )
      {
      vtkGenericWarningMacro("Incorrect mean for Xs");
      testStatus = 1;
      }

    if ( fabs ( modelTab->GetValueByName( 0, "Pearson r" ).ToDouble() - pearson3[b] ) > 1.e-6 )
      {
      vtkGenericWarningMacro("Incorrect Pearson correlation coefficient");
      testStatus = 1;
      }

    cout << "\n";
    }

  // Clean up
  as3->Delete();
  paramTable->Delete();

  return testStatus;
}
<commit_msg>Fixed a bug (partial sums must be initialized for each time lag)<commit_after>// .SECTION Thanks
// This test was implemented by Philippe Pebay, Kitware SAS 2012

#include "vtkAutoCorrelativeStatistics.h"
#include "vtkDataObjectCollection.h"
#include "vtkDoubleArray.h"
#include "vtkIdTypeArray.h"
#include "vtkInformation.h"
#include "vtkMath.h"
#include "vtkMultiBlockDataSet.h"
#include "vtkStringArray.h"
#include "vtkTable.h"
#include "vtkTimerLog.h"
#include "vtkVariantArray.h"

//=============================================================================
int TestAutoCorrelativeStatistics( int, char *[] )
{
  int testStatus = 0;

  // ************** Test with 2 columns of input data **************

  // Input data
  double mingledData[] =
    {
      46,
      45,
      47,
      49,
      46,
      47,
      46,
      46,
      47,
      46,
      47,
      49,
      49,
      49,
      47,
      45,
      50,
      50,
      46,
      46,
      51,
      50,
      48,
      48,
      52,
      54,
      48,
      47,
      52,
      52,
      49,
      49,
      53,
      54,
      50,
      50,
      53,
      54,
      50,
      52,
      53,
      53,
      50,
      51,
      54,
      54,
      49,
      49,
      52,
      52,
      50,
      51,
      52,
      52,
      49,
      47,
      48,
      48,
      48,
      50,
      46,
      48,
      47,
      47,
    };

  // Test with entire data set
  int nVals1 = 32;

  vtkDoubleArray* dataset1Arr = vtkDoubleArray::New();
  dataset1Arr->SetNumberOfComponents( 1 );
  dataset1Arr->SetName( "Metric 0" );

  vtkDoubleArray* dataset2Arr = vtkDoubleArray::New();
  dataset2Arr->SetNumberOfComponents( 1 );
  dataset2Arr->SetName( "Metric 1" );

  for ( int i = 0; i < nVals1; ++ i )
    {
    int ti = i << 1;
    dataset1Arr->InsertNextValue( mingledData[ti] );
    dataset2Arr->InsertNextValue( mingledData[ti + 1] );
    }

  // Create input data table
  vtkTable* datasetTable1 = vtkTable::New();
  datasetTable1->AddColumn( dataset1Arr );
  dataset1Arr->Delete();
  datasetTable1->AddColumn( dataset2Arr );
  dataset2Arr->Delete();

  // Create input parameter table for the stationary case
  vtkIdTypeArray* timeLags = vtkIdTypeArray::New();
  timeLags->SetName( "Time Lags" );
  timeLags->SetNumberOfTuples( 1 );
  timeLags->SetValue( 0, 0 );
  vtkTable* paramTable = vtkTable::New();
  paramTable->AddColumn( timeLags );
  timeLags->Delete();

  // Columns of interest
  int nMetrics1 = 2;
  vtkStdString columns1[] =
    {
      "Metric 1",
      "Metric 0"
    };

  // Reference values
  // Means for metrics 0 and 1 respectively
  double meansXs1[] = { 49.21875 , 49.5 };

  // Standard deviations for metrics 0 and 1, respectively
  double vars1[] = { 5.9828629, 7.548397 };

  // Set autocorrelative statistics algorithm and its input data port
  vtkAutoCorrelativeStatistics* as1 = vtkAutoCorrelativeStatistics::New();

  // First verify that absence of input does not cause trouble
  cout << "\n## Verifying that absence of input does not cause trouble... ";
  as1->Update();
  cout << "done.\n";

  // Prepare first test with data
  as1->SetInputData( vtkStatisticsAlgorithm::INPUT_DATA, datasetTable1 );
  datasetTable1->Delete();

  // Select columns of interest
  for ( int i = 0; i < nMetrics1; ++ i )
    {
    as1->AddColumn( columns1[i] );
    }

  // Set spatial cardinality
  as1->SetSliceCardinality( nVals1 );

  // Set parameters for autocorrelation of whole data set with respect to itself
  as1->SetInputData( vtkStatisticsAlgorithm::LEARN_PARAMETERS, paramTable );

  // Test Learn and Derive options
  as1->SetLearnOption( true );
  as1->SetDeriveOption( true );
  as1->SetAssessOption( false );
  as1->SetTestOption( false );
  as1->Update();

  // Get output model tables
  vtkMultiBlockDataSet* outputModelAS1 = vtkMultiBlockDataSet::SafeDownCast( as1->GetOutputDataObject( vtkStatisticsAlgorithm::OUTPUT_MODEL ) );

  cout << "\n## Calculated the following statistics for first data set:\n";
  for ( unsigned b = 0; b < outputModelAS1->GetNumberOfBlocks(); ++ b )
    {
    vtkStdString varName = outputModelAS1->GetMetaData( b )->Get( vtkCompositeDataSet::NAME() );
    cout << "   Variable="
         << varName
         << "\n";

    vtkTable* modelTab = vtkTable::SafeDownCast( outputModelAS1->GetBlock( b ) );
    cout << "   ";
    for ( int i = 0; i < modelTab->GetNumberOfColumns(); ++ i )
      {
      cout << modelTab->GetColumnName( i )
           << "="
           << modelTab->GetValue( 0, i ).ToString()
           << "  ";
      }

    // Verify some of the calculated statistics
    if ( fabs ( modelTab->GetValueByName( 0, "Mean Xs" ).ToDouble() - meansXs1[b] ) > 1.e-6 )
      {
      vtkGenericWarningMacro("Incorrect mean for Xs");
      testStatus = 1;
      }

    if ( fabs ( modelTab->GetValueByName( 0, "Variance Xs" ).ToDouble() - vars1[b] ) > 1.e-5 )
      {
      vtkGenericWarningMacro("Incorrect variance for Xs");
      testStatus = 1;
      }

    if ( fabs ( modelTab->GetValueByName( 0, "Pearson r" ).ToDouble() - 1. ) > 1.e-6 )
      {
      vtkGenericWarningMacro("Incorrect Pearson correlation coefficient");
      testStatus = 1;
      }

    cout << "\n";
    }

  // Test with a slight variation of initial data set (to test model aggregation)
  int nVals2 = 32;

  vtkDoubleArray* dataset4Arr = vtkDoubleArray::New();
  dataset4Arr->SetNumberOfComponents( 1 );
  dataset4Arr->SetName( "Metric 0" );

  vtkDoubleArray* dataset5Arr = vtkDoubleArray::New();
  dataset5Arr->SetNumberOfComponents( 1 );
  dataset5Arr->SetName( "Metric 1" );

  for ( int i = 0; i < nVals2; ++ i )
    {
    int ti = i << 1;
    dataset4Arr->InsertNextValue( mingledData[ti] + 1. );
    dataset5Arr->InsertNextValue( mingledData[ti + 1] );
    }

  vtkTable* datasetTable2 = vtkTable::New();
  datasetTable2->AddColumn( dataset4Arr );
  dataset4Arr->Delete();
  datasetTable2->AddColumn( dataset5Arr );
  dataset5Arr->Delete();

  // Set auto-correlative statistics algorithm and its input data port
  vtkAutoCorrelativeStatistics* as2 = vtkAutoCorrelativeStatistics::New();
  as2->SetInputData( vtkStatisticsAlgorithm::INPUT_DATA, datasetTable2 );

  // Select columns of interest
  for ( int i = 0; i < nMetrics1; ++ i )
    {
    as2->AddColumn( columns1[i] );
    }

  // Set spatial cardinality
  as2->SetSliceCardinality( nVals2 );

  // Set parameters for autocorrelation of whole data set with respect to itself
  as2->SetInputData( vtkStatisticsAlgorithm::LEARN_PARAMETERS, paramTable );

  // Update with Learn option only
  as2->SetLearnOption( true );
  as2->SetDeriveOption( false );
  as2->SetTestOption( false );
  as2->SetAssessOption( false );
  as2->Update();

  // Get output meta tables
  vtkMultiBlockDataSet* outputModelAS2 = vtkMultiBlockDataSet::SafeDownCast( as2->GetOutputDataObject( vtkStatisticsAlgorithm::OUTPUT_MODEL ) );

  cout << "\n## Calculated the following statistics for second data set:\n";
  for ( unsigned b = 0; b < outputModelAS2->GetNumberOfBlocks(); ++ b )
    {
    vtkStdString varName = outputModelAS2->GetMetaData( b )->Get( vtkCompositeDataSet::NAME() );
    cout << "   Variable="
         << varName
         << "\n";

    vtkTable* modelTab = vtkTable::SafeDownCast( outputModelAS2->GetBlock( b ) );
    cout << "   ";
    for ( int i = 0; i < modelTab->GetNumberOfColumns(); ++ i )
      {
      cout << modelTab->GetColumnName( i )
           << "="
           << modelTab->GetValue( 0, i ).ToString()
           << "  ";
      }

    cout << "\n";
    }

  // Test model aggregation by adding new data to engine which already has a model
  as1->SetInputData( vtkStatisticsAlgorithm::INPUT_DATA, datasetTable2 );
  vtkMultiBlockDataSet* model = vtkMultiBlockDataSet::New();
  model->ShallowCopy( outputModelAS1 );
  as1->SetInputData( vtkStatisticsAlgorithm::INPUT_MODEL, model );

  // Clean up
  model->Delete();
  datasetTable2->Delete();
  as2->Delete();

  // Update with Learn and Derive options only
  as1->SetLearnOption( true );
  as1->SetDeriveOption( true );
  as1->SetTestOption( false );
  as1->SetAssessOption( false );
  as1->Update();

  // Updated reference values
  // Means and variances for metrics 0 and 1, respectively
  double meansXs0[] = { 49.71875 , 49.5 };
  double varsXs0[] = { 6.1418651 , 7.548397 * 62. / 63. };

  // Get output meta tables
  outputModelAS1 = vtkMultiBlockDataSet::SafeDownCast( as1->GetOutputDataObject( vtkStatisticsAlgorithm::OUTPUT_MODEL ) );

  cout << "\n## Calculated the following statistics for aggregated (first + second) data set:\n";
  for ( unsigned b = 0; b < outputModelAS1->GetNumberOfBlocks(); ++ b )
    {
    vtkStdString varName = outputModelAS1->GetMetaData( b )->Get( vtkCompositeDataSet::NAME() );
    cout << "   Variable="
         << varName
         << "\n";

    vtkTable* modelTab = vtkTable::SafeDownCast( outputModelAS1->GetBlock( b ) );
    cout << "   ";
    for ( int i = 0; i < modelTab->GetNumberOfColumns(); ++ i )
      {
      cout << modelTab->GetColumnName( i )
           << "="
           << modelTab->GetValue( 0, i ).ToString()
           << "  ";
      }

    cout << "\n";
    }

  // Clean up
  as1->Delete();

  // ************** Test with 2 columns of synthetic data **************

  // Space and time parameters
  vtkIdType nSteps = 2;
  vtkIdType cardSlice = 1000;
  vtkIdType cardTotal = nSteps * cardSlice;

  // Expand parameter table to contain all steps
  vtkVariantArray* row = vtkVariantArray::New();
  row->SetNumberOfValues( 1 );
  for ( vtkIdType p = 1; p < nSteps; ++ p )
    {
    row->SetValue( 0, p );
    paramTable->InsertNextRow( row );
    }
  row->Delete();

  vtkDoubleArray* lineArr = vtkDoubleArray::New();
  lineArr->SetNumberOfComponents( 1 );
  lineArr->SetName( "Line" );

  vtkDoubleArray* vArr = vtkDoubleArray::New();
  vArr->SetNumberOfComponents( 1 );
  vArr->SetName( "V" );

  vtkDoubleArray* circleArr = vtkDoubleArray::New();
  circleArr->SetNumberOfComponents( 1 );
  circleArr->SetName( "Circle" );

  // Fill data columns
  vtkIdType midPoint = cardTotal >> 1;
  double dAlpha = vtkMath::DoubleTwoPi() / cardSlice;
  for ( int i = 0; i < cardTotal; ++ i )
    {
    lineArr->InsertNextValue( i );
    if ( i < midPoint )
      {
      vArr->InsertNextValue( cardTotal - i );
      circleArr->InsertNextValue( cos( i * dAlpha ) );
      }
    else
      {
      vArr->InsertNextValue( i );
      circleArr->InsertNextValue( sin( i * dAlpha ) );
      }
    }

  // Create input data table
  vtkTable* datasetTable3 = vtkTable::New();
  datasetTable3->AddColumn( lineArr );
  lineArr->Delete();
  datasetTable3->AddColumn( vArr );
  vArr->Delete();
  datasetTable3->AddColumn( circleArr );
  circleArr->Delete();

  // Columns of interest
  int nMetrics2 = 3;
  vtkStdString columns2[] =
    {
      "Line",
      "V",
      "Circle"
    };

  // Reference values
  double halfNm1 = .5 * ( cardSlice - 1 );

  // Means of Xs for circle, line, and v-shaped variables respectively
  double meansXt3[] = { 0., 0.,
                        halfNm1, halfNm1 + cardSlice,
                        cardTotal - halfNm1, cardTotal - halfNm1 - 1. };

  // Pearson r values for circle, line, and v-shaped variables respectively
  double pearson3[] = { 1., 0.,
                        1., 1.,
                        1., -1. };

  // Prepare autocorrelative statistics algorithm and its input data port
  vtkAutoCorrelativeStatistics* as3 = vtkAutoCorrelativeStatistics::New();
  as3->SetInputData( vtkStatisticsAlgorithm::INPUT_DATA, datasetTable3 );
  datasetTable3->Delete();

  // Select Columns of Interest
  for ( int i = 0; i < nMetrics2; ++ i )
    {
    as3->AddColumn( columns2[i] );
    }

  // Set spatial cardinality
  as3->SetSliceCardinality( cardSlice );

  // Set autocorrelation parameters for first slice against slice following midpoint
  as3->SetInputData( vtkStatisticsAlgorithm::LEARN_PARAMETERS, paramTable );

  // Test Learn, and Derive options
  as3->SetLearnOption( true );
  as3->SetDeriveOption( true );
  as3->SetAssessOption( false );
  as3->SetTestOption( false );
  as3->Update();

  // Get output data and meta tables
  vtkMultiBlockDataSet* outputModelAS3 = vtkMultiBlockDataSet::SafeDownCast( as3->GetOutputDataObject( vtkStatisticsAlgorithm::OUTPUT_MODEL ) );

  cout << "\n## Calculated the following statistics for third data set:\n";
  for ( unsigned b = 0; b < outputModelAS3->GetNumberOfBlocks(); ++ b )
    {
    vtkStdString varName = outputModelAS3->GetMetaData( b )->Get( vtkCompositeDataSet::NAME() );
    cout << "   Variable="
         << varName
         << "\n";

    vtkTable* modelTab = vtkTable::SafeDownCast( outputModelAS3->GetBlock( b ) );
    cout << "   ";
    for ( int r = 0; r < modelTab->GetNumberOfRows(); ++ r )
      {
      for ( int i = 0; i < modelTab->GetNumberOfColumns(); ++ i )
        {
        cout << modelTab->GetColumnName( i )
             << "="
             << modelTab->GetValue( r, i ).ToString()
             << "  ";
        }

      // Verify some of the calculated statistics
      int idx = nSteps * b + r;
      if ( fabs ( modelTab->GetValueByName( r, "Mean Xt" ).ToDouble() - meansXt3[idx] ) > 1.e-6 )
        {
        vtkGenericWarningMacro("Incorrect mean for Xt");
        testStatus = 1;
        }

      if ( fabs ( modelTab->GetValueByName( r, "Pearson r" ).ToDouble() - pearson3[idx] ) > 1.e-6 )
        {
        vtkGenericWarningMacro("Incorrect Pearson correlation coefficient "<<pearson3[idx]);
        testStatus = 1;
        }

      cout << "\n";
      } // i
    } // r

  // Clean up
  as3->Delete();
  paramTable->Delete();

  return testStatus;
}
<|endoftext|>


<commit_before>/******************************************************************************
*       SOFA, Simulation Open-Framework Architecture, version 1.0 RC 1        *
*                (c) 2006-2011 MGH, INRIA, USTL, UJF, CNRS                    *
*                                                                             *
* This library is free software; you can redistribute it and/or modify it     *
* under the terms of the GNU Lesser General Public License as published by    *
* the Free Software Foundation; either version 2.1 of the License, or (at     *
* your option) any later version.                                             *
*                                                                             *
* This library is distributed in the hope that it will be useful, but WITHOUT *
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or       *
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License *
* for more details.                                                           *
*                                                                             *
* You should have received a copy of the GNU Lesser General Public License    *
* along with this library; if not, write to the Free Software Foundation,     *
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.          *
*******************************************************************************
*                               SOFA :: Modules                               *
*                                                                             *
* Authors: The SOFA Team and external contributors (see Authors.txt)          *
*                                                                             *
* Contact information: contact@sofa-framework.org                             *
******************************************************************************/
#define SOFA_COMPONENT_MAPPING_SUBSETMULTIMAPPING_CPP

#include <sofa/component/mapping/SubsetMultiMapping.inl>
#include <sofa/defaulttype/Vec3Types.h>
#include <sofa/defaulttype/RigidTypes.h>
#include <sofa/core/ObjectFactory.h>

using namespace sofa::defaulttype;

namespace sofa
{

namespace component
{

namespace mapping
{

SOFA_DECL_CLASS(SubsetMultiMapping)

// Register in the Factory
int SubsetMultiMappingClass = core::RegisterObject("Compute a subset of the input MechanicalObjects according to a dof index list")
#ifndef SOFA_FLOAT
    .add< SubsetMultiMapping< Vec3dTypes, Vec3dTypes > >()
    .add< SubsetMultiMapping< Rigid3dTypes, Rigid3dTypes > >()
#endif
#ifndef SOFA_DOUBLE
    .add< SubsetMultiMapping< Vec3fTypes, Vec3fTypes > >()
    .add< SubsetMultiMapping< Rigid3fTypes, Rigid3fTypes > >()
#endif
        ;

#ifndef SOFA_FLOAT
template class SOFA_MISC_MAPPING_API SubsetMultiMapping< Vec3dTypes, Vec3dTypes >;
template class SOFA_MISC_MAPPING_API SubsetMultiMapping< Rigid3dTypes, Rigid3dTypes >;
#endif
#ifndef SOFA_DOUBLE
template class SOFA_MISC_MAPPING_API SubsetMultiMapping< Vec3fTypes, Vec3fTypes >;
template class SOFA_MISC_MAPPING_API SubsetMultiMapping< Rigid3fTypes, Rigid3fTypes >;
#endif

} // namespace mapping

} // namespace component

} // namespace sofa
<commit_msg>ADD: instantiate on Vec1<commit_after>/******************************************************************************
*       SOFA, Simulation Open-Framework Architecture, version 1.0 RC 1        *
*                (c) 2006-2011 MGH, INRIA, USTL, UJF, CNRS                    *
*                                                                             *
* This library is free software; you can redistribute it and/or modify it     *
* under the terms of the GNU Lesser General Public License as published by    *
* the Free Software Foundation; either version 2.1 of the License, or (at     *
* your option) any later version.                                             *
*                                                                             *
* This library is distributed in the hope that it will be useful, but WITHOUT *
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or       *
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License *
* for more details.                                                           *
*                                                                             *
* You should have received a copy of the GNU Lesser General Public License    *
* along with this library; if not, write to the Free Software Foundation,     *
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.          *
*******************************************************************************
*                               SOFA :: Modules                               *
*                                                                             *
* Authors: The SOFA Team and external contributors (see Authors.txt)          *
*                                                                             *
* Contact information: contact@sofa-framework.org                             *
******************************************************************************/
#define SOFA_COMPONENT_MAPPING_SUBSETMULTIMAPPING_CPP

#include <sofa/component/mapping/SubsetMultiMapping.inl>
#include <sofa/defaulttype/Vec3Types.h>
#include <sofa/defaulttype/RigidTypes.h>
#include <sofa/core/ObjectFactory.h>

using namespace sofa::defaulttype;

namespace sofa
{

namespace component
{

namespace mapping
{

SOFA_DECL_CLASS(SubsetMultiMapping)

// Register in the Factory
int SubsetMultiMappingClass = core::RegisterObject("Compute a subset of the input MechanicalObjects according to a dof index list")
#ifndef SOFA_FLOAT
    .add< SubsetMultiMapping< Vec3dTypes, Vec3dTypes > >()
    .add< SubsetMultiMapping< Vec1dTypes, Vec1dTypes > >()
    .add< SubsetMultiMapping< Rigid3dTypes, Rigid3dTypes > >()
#endif
#ifndef SOFA_DOUBLE
    .add< SubsetMultiMapping< Vec3fTypes, Vec3fTypes > >()
    .add< SubsetMultiMapping< Vec1fTypes, Vec1fTypes > >()
    .add< SubsetMultiMapping< Rigid3fTypes, Rigid3fTypes > >()
#endif
        ;

#ifndef SOFA_FLOAT
template class SOFA_MISC_MAPPING_API SubsetMultiMapping< Vec3dTypes, Vec3dTypes >;
template class SOFA_MISC_MAPPING_API SubsetMultiMapping< Vec1dTypes, Vec1dTypes >;
template class SOFA_MISC_MAPPING_API SubsetMultiMapping< Rigid3dTypes, Rigid3dTypes >;
#endif
#ifndef SOFA_DOUBLE
template class SOFA_MISC_MAPPING_API SubsetMultiMapping< Vec3fTypes, Vec3fTypes >;
template class SOFA_MISC_MAPPING_API SubsetMultiMapping< Vec1fTypes, Vec1fTypes >;
template class SOFA_MISC_MAPPING_API SubsetMultiMapping< Rigid3fTypes, Rigid3fTypes >;
#endif

} // namespace mapping

} // namespace component

} // namespace sofa
<|endoftext|>


<commit_before>#include "engine/dukintegration.h"

#include <map>
#include <functional>

#include "core/ecs-systems.h"
#include "core/random.h"
#include "core/log.h"
#include "core/sol.h"

#include "engine/components.h"
#include "engine/input.h"
#include "engine/dukregistry.h"
#include "engine/objectemplate.h"
#include "engine/cameradata.h"

LOG_SPECIFY_DEFAULT_LOGGER("engine.duk.integration")

namespace euphoria::engine
{

    template<typename T>
    std::vector<T> GetVector(const sol::table& table)
    {
        std::vector<T> r;
        r.reserve(table.size());
        for(unsigned int i=0; i<table.size(); i+=1)
        {
            r.emplace_back(table[i+1]);
        }
        return r;
    }

    void ReportFirstError(const sol::protected_function_result& val, Sol* ctx,const std::string& action, const std::string& where)
    {
        if (val.valid())
        {
            return;
        }

        const sol::error err = val;
        const auto message = fmt::format("Failed to {0} in {1}: {2}", action, where, err.what());
        LOG_ERROR("{0}", message);
        if (!ctx->has_error)
        {
            ctx->has_error = true;
            ctx->error = message;
        }
    }

    struct DukUpdateSystem
        : public core::ecs::ComponentSystem
        , public core::ecs::ComponentSystemUpdate
    {
        // using UpdateFunction = std::function<void(float)>;
        using UpdateFunction = sol::protected_function;

        Sol* duk;
        UpdateFunction func;

        DukUpdateSystem(const std::string& name, Sol* d, UpdateFunction f)
            : ComponentSystem(name)
            , duk(d)
            , func(f)
        {
            ASSERT(func);
        }

        void
        Update(core::ecs::Registry*, float dt) const override
        {
            auto result = func(dt);
            ReportFirstError(result, duk, "update", name);
        }

        void
        RegisterCallbacks(core::ecs::Systems* systems) override
        {
            systems->update.Add(this);
        }
    };

    struct DukInitSystem
        : public core::ecs::ComponentSystem
        , public core::ecs::ComponentSystemInit
    {
    	// using InitFunction = std::function<void(core::ecs::EntityId)>;
    	using InitFunction = sol::protected_function;

        core::ecs::Registry* reg;
        Sol* duk;
        std::vector<core::ecs::ComponentId> types;
        InitFunction func;

        DukInitSystem
        (
            const std::string& name,
            Sol* d,
            core::ecs::Registry* r,
            const std::vector<core::ecs::ComponentId>& t,
            InitFunction f
        )
            : ComponentSystem(name)
            , reg(r)
            , duk(d)
            , types(t)
            , func(f)
        {
        }

        void
        OnAdd(core::ecs::EntityId entity) const override
        {
            ASSERT(reg);
            ASSERT(!types.empty());

            for(const auto& type: types)
            {
                const auto component = reg->GetComponent(entity, type);
                const bool has_component = component != nullptr;

                if(!has_component)
                {
                    return;
                }
            }
            auto result = func(entity);
            ReportFirstError(result, duk, "init", name);
        }

        void
        RegisterCallbacks(core::ecs::Systems* systems) override
        {
            systems->init.Add(this);
        }
    };

    struct DukSystems
    {
        core::ecs::Systems* systems;
        Sol* duk;

        explicit DukSystems(core::ecs::Systems* s, Sol* d)
            : systems(s)
            , duk(d)
        {
        }

        void
        AddUpdate(const std::string& name, DukUpdateSystem::UpdateFunction func)
        {
            systems->AddAndRegister(std::make_shared<DukUpdateSystem>(name, duk, func));
        }

        void
        AddInit
        (
            const std::string& name,
            core::ecs::Registry* reg,
            const std::vector<core::ecs::ComponentId>& types,
            DukInitSystem::InitFunction func
        )
        {
            systems->AddAndRegister(std::make_shared<DukInitSystem>(name, duk, reg, types, func));
        }
    };

    struct DukIntegrationPimpl
    {
        DukIntegrationPimpl
        (
            core::ecs::Systems* sys,
            core::ecs::World* world,
            Sol* duk,
            ObjectCreator* creator,
            Components* components,
            CameraData*    cam
        )
            : systems(sys, duk)
            , registry(&world->reg, components)
            , input(duk->lua["Input"].get_or_create<sol::table>())
            , world(world)
            , creator(creator)
            , components(components)
            , camera(cam)
        {
        }

        void
        Integrate(Sol* duk)
        {
            auto systems_table         = duk->lua["Systems"].get_or_create<sol::table>();
            systems_table["AddUpdate"] = [&](const std::string& name, sol::function func)
            {
                systems.AddUpdate(name, func);
            };
            systems_table["OnInit"] = [&]
            (
                const std::string&              name,
                sol::table types,
                sol::function                   func
            )
            {
              auto vtypes = GetVector<core::ecs::ComponentId>(types);
              systems.AddInit(name, &world->reg, vtypes, func);
            };

            auto math_table         = duk->lua["Math"].get_or_create<sol::table>();
            math_table["NewRandom"] = [&]() { return std::make_shared<core::Random>(); };

            auto templates_table    = duk->lua["Templates"].get_or_create<sol::table>();
            templates_table["Find"] = [&](const std::string& name)
            {
                return creator->FindTemplate(name);
            };

            duk->lua.new_usertype<ObjectTemplate>("Template");
            duk->lua["Template"]["Create"] = [&, duk](ObjectTemplate* t)
            {
                return t->CreateObject(ObjectCreationArgs{world, &registry, duk, duk});
            };

            auto camera_table       = duk->lua["Camera"].get_or_create<sol::table>();
            camera_table["GetRect"] = [&]() { return &camera->screen; };

            duk->lua.new_usertype<CustomArguments>
            (
                "CustomArguments",
                "GetNumber", [](const CustomArguments& args, const std::string& name) -> float
                {
                      const auto f = args.numbers.find(name);
                      if(f == args.numbers.end())
                      {
                            return 0;
                      }
                      else
                      {
                            return f->second;
                      }
                }
            );

            auto registry_table        = duk->lua["Registry"].get_or_create<sol::table>();
            registry_table["Entities"] = [&](sol::table types)
            {
                return registry.EntityView(GetVector<core::ecs::ComponentId>(types));
            };
            registry_table["GetSpriteId"] = [&]()
            {
                return registry.components->sprite;
            };
            registry_table["DestroyEntity"] = [&](core::ecs::EntityId entity)
            {
                registry.DestroyEntity(entity);
            };
            registry_table["GetPosition2Id"] = [&]()
            {
                LOG_INFO("Getting position 2d");
                return registry.components->position2;
            };
            registry_table["GetSpriteId"] = [&]()
            {
                return registry.components->sprite;
            };
            registry_table["New"] = sol::overload
            (
                [&](const std::string& name, sol::function setup)
                {
                    return registry.CreateNewId(name, setup);
                },
                [&](const std::string& name)
                {
                    return registry.CreateNewId(name);
                }
            );
            registry_table["Get"] = [&](core::ecs::EntityId ent, core::ecs::ComponentId comp)
            {
                return registry.GetProperty(ent, comp);
            };
            registry_table["Set"] = [&](core::ecs::EntityId ent, core::ecs::ComponentId comp, sol::table val)
            {
                registry.SetProperty(ent, comp, val);
            };
            registry_table["GetSprite"] = [&](core::ecs::ComponentId ent)
            {
                return registry.GetComponentOrNull<CSprite>(ent, components->sprite);
            };
            registry_table["GetPosition2"] = [&](core::ecs::ComponentId ent)
            {
                return registry.GetComponentOrNull<CPosition2>(ent, components->position2);
            };
            registry_table["GetPosition2vec"] = [&](core::ecs::ComponentId ent)
            {
                auto c = registry.GetComponentOrNull<CPosition2>(ent, components->position2);
                return c == nullptr ? nullptr : &c->pos;
            };

            duk->lua.new_usertype<CPosition2>("CPosition2", "vec", &CPosition2::pos);

            duk->lua.new_usertype<CSprite>
            (
                "CSprite",
                "GetRect", [](const CSprite& sp, const CPosition2& p)
                {
                    return GetSpriteRect(p.pos, *sp.texture);
                }
            );

            /*
            duk->lua.new_usertype<Rectf>(
                "Rectf",
                sol::no_constructor,
                "Contains",
                &Rectf::ContainsExclusive,
                "GetHeight",
                &Rectf::GetHeight,
                "GetWidth",
                &Rectf::GetWidth);
            */

            auto random_type = duk->lua.new_usertype<core::Random>("Random");
            random_type["NextFloat01"] = &core::Random::NextFloat01;
            random_type["NextRangeFloat"] = [](core::Random& r, float f) -> float
            {
                return r.NextRange(f);
            };
            random_type["NextBool"] = &core::Random::NextBool;
            random_type["NextPoint2"] = [](core::Random& r, core::Rectf& rect) -> core::vec2f
            {
                return rect.RandomPoint(&r);
            };
        }

        DukSystems systems;
        DukRegistry registry;
        sol::table     input;
        core::ecs::World* world;
        ObjectCreator* creator;
        Components* components;
        CameraData* camera;
    };

    DukIntegration::DukIntegration
    (
        core::ecs::Systems* systems,
        core::ecs::World* reg,
        Sol* duk,
        ObjectCreator* creator,
        Components* components,
        CameraData* camera
    )
    {
        pimpl.reset
        (
            new DukIntegrationPimpl
            (
                systems,
                reg,
                duk,
                creator,
                components,
                camera
            )
        );
        pimpl->Integrate(duk);
    }

    DukIntegration::~DukIntegration()
    {
        Clear();
    }

    void
    DukIntegration::Clear()
    {
        pimpl.reset();
    }

    DukRegistry&
    DukIntegration::Registry()
    {
        ASSERT(pimpl);
        return pimpl->registry;
    }

    void
    DukIntegration::BindKeys(Sol* duk, const Input& input)
    {
        input.Set(&pimpl->input);
    }
}

TYPEID_SETUP_TYPE(euphoria::core::Random);
TYPEID_SETUP_TYPE(euphoria::engine::ObjectTemplate);
TYPEID_SETUP_TYPE(euphoria::core::Rect<float>);
<commit_msg>implemented rect<commit_after>#include "engine/dukintegration.h"

#include <map>
#include <functional>

#include "core/ecs-systems.h"
#include "core/random.h"
#include "core/log.h"
#include "core/sol.h"

#include "engine/components.h"
#include "engine/input.h"
#include "engine/dukregistry.h"
#include "engine/objectemplate.h"
#include "engine/cameradata.h"

LOG_SPECIFY_DEFAULT_LOGGER("engine.duk.integration")

namespace euphoria::engine
{

    template<typename T>
    std::vector<T> GetVector(const sol::table& table)
    {
        std::vector<T> r;
        r.reserve(table.size());
        for(unsigned int i=0; i<table.size(); i+=1)
        {
            r.emplace_back(table[i+1]);
        }
        return r;
    }

    void ReportFirstError(const sol::protected_function_result& val, Sol* ctx,const std::string& action, const std::string& where)
    {
        if (val.valid())
        {
            return;
        }

        const sol::error err = val;
        const auto message = fmt::format("Failed to {0} in {1}: {2}", action, where, err.what());
        LOG_ERROR("{0}", message);
        if (!ctx->has_error)
        {
            ctx->has_error = true;
            ctx->error = message;
        }
    }

    struct DukUpdateSystem
        : public core::ecs::ComponentSystem
        , public core::ecs::ComponentSystemUpdate
    {
        // using UpdateFunction = std::function<void(float)>;
        using UpdateFunction = sol::protected_function;

        Sol* duk;
        UpdateFunction func;

        DukUpdateSystem(const std::string& name, Sol* d, UpdateFunction f)
            : ComponentSystem(name)
            , duk(d)
            , func(f)
        {
            ASSERT(func);
        }

        void
        Update(core::ecs::Registry*, float dt) const override
        {
            auto result = func(dt);
            ReportFirstError(result, duk, "update", name);
        }

        void
        RegisterCallbacks(core::ecs::Systems* systems) override
        {
            systems->update.Add(this);
        }
    };

    struct DukInitSystem
        : public core::ecs::ComponentSystem
        , public core::ecs::ComponentSystemInit
    {
    	// using InitFunction = std::function<void(core::ecs::EntityId)>;
    	using InitFunction = sol::protected_function;

        core::ecs::Registry* reg;
        Sol* duk;
        std::vector<core::ecs::ComponentId> types;
        InitFunction func;

        DukInitSystem
        (
            const std::string& name,
            Sol* d,
            core::ecs::Registry* r,
            const std::vector<core::ecs::ComponentId>& t,
            InitFunction f
        )
            : ComponentSystem(name)
            , reg(r)
            , duk(d)
            , types(t)
            , func(f)
        {
        }

        void
        OnAdd(core::ecs::EntityId entity) const override
        {
            ASSERT(reg);
            ASSERT(!types.empty());

            for(const auto& type: types)
            {
                const auto component = reg->GetComponent(entity, type);
                const bool has_component = component != nullptr;

                if(!has_component)
                {
                    return;
                }
            }
            auto result = func(entity);
            ReportFirstError(result, duk, "init", name);
        }

        void
        RegisterCallbacks(core::ecs::Systems* systems) override
        {
            systems->init.Add(this);
        }
    };

    struct DukSystems
    {
        core::ecs::Systems* systems;
        Sol* duk;

        explicit DukSystems(core::ecs::Systems* s, Sol* d)
            : systems(s)
            , duk(d)
        {
        }

        void
        AddUpdate(const std::string& name, DukUpdateSystem::UpdateFunction func)
        {
            systems->AddAndRegister(std::make_shared<DukUpdateSystem>(name, duk, func));
        }

        void
        AddInit
        (
            const std::string& name,
            core::ecs::Registry* reg,
            const std::vector<core::ecs::ComponentId>& types,
            DukInitSystem::InitFunction func
        )
        {
            systems->AddAndRegister(std::make_shared<DukInitSystem>(name, duk, reg, types, func));
        }
    };

    struct DukIntegrationPimpl
    {
        DukIntegrationPimpl
        (
            core::ecs::Systems* sys,
            core::ecs::World* world,
            Sol* duk,
            ObjectCreator* creator,
            Components* components,
            CameraData*    cam
        )
            : systems(sys, duk)
            , registry(&world->reg, components)
            , input(duk->lua["Input"].get_or_create<sol::table>())
            , world(world)
            , creator(creator)
            , components(components)
            , camera(cam)
        {
        }

        void
        Integrate(Sol* duk)
        {
            auto systems_table         = duk->lua["Systems"].get_or_create<sol::table>();
            systems_table["AddUpdate"] = [&](const std::string& name, sol::function func)
            {
                systems.AddUpdate(name, func);
            };
            systems_table["OnInit"] = [&]
            (
                const std::string&              name,
                sol::table types,
                sol::function                   func
            )
            {
              auto vtypes = GetVector<core::ecs::ComponentId>(types);
              systems.AddInit(name, &world->reg, vtypes, func);
            };

            auto math_table         = duk->lua["Math"].get_or_create<sol::table>();
            math_table["NewRandom"] = [&]() { return std::make_shared<core::Random>(); };

            auto templates_table    = duk->lua["Templates"].get_or_create<sol::table>();
            templates_table["Find"] = [&](const std::string& name)
            {
                return creator->FindTemplate(name);
            };

            duk->lua.new_usertype<ObjectTemplate>("Template");
            duk->lua["Template"]["Create"] = [&, duk](ObjectTemplate* t)
            {
                return t->CreateObject(ObjectCreationArgs{world, &registry, duk, duk});
            };

            auto camera_table       = duk->lua["Camera"].get_or_create<sol::table>();
            camera_table["GetRect"] = [&]() { return &camera->screen; };

            duk->lua.new_usertype<CustomArguments>
            (
                "CustomArguments",
                "GetNumber", [](const CustomArguments& args, const std::string& name) -> float
                {
                      const auto f = args.numbers.find(name);
                      if(f == args.numbers.end())
                      {
                            return 0;
                      }
                      else
                      {
                            return f->second;
                      }
                }
            );

            auto registry_table        = duk->lua["Registry"].get_or_create<sol::table>();
            registry_table["Entities"] = [&](sol::table types)
            {
                return registry.EntityView(GetVector<core::ecs::ComponentId>(types));
            };
            registry_table["GetSpriteId"] = [&]()
            {
                return registry.components->sprite;
            };
            registry_table["DestroyEntity"] = [&](core::ecs::EntityId entity)
            {
                registry.DestroyEntity(entity);
            };
            registry_table["GetPosition2Id"] = [&]()
            {
                LOG_INFO("Getting position 2d");
                return registry.components->position2;
            };
            registry_table["GetSpriteId"] = [&]()
            {
                return registry.components->sprite;
            };
            registry_table["New"] = sol::overload
            (
                [&](const std::string& name, sol::function setup)
                {
                    return registry.CreateNewId(name, setup);
                },
                [&](const std::string& name)
                {
                    return registry.CreateNewId(name);
                }
            );
            registry_table["Get"] = [&](core::ecs::EntityId ent, core::ecs::ComponentId comp)
            {
                return registry.GetProperty(ent, comp);
            };
            registry_table["Set"] = [&](core::ecs::EntityId ent, core::ecs::ComponentId comp, sol::table val)
            {
                registry.SetProperty(ent, comp, val);
            };
            registry_table["GetSprite"] = [&](core::ecs::ComponentId ent)
            {
                return registry.GetComponentOrNull<CSprite>(ent, components->sprite);
            };
            registry_table["GetPosition2"] = [&](core::ecs::ComponentId ent)
            {
                return registry.GetComponentOrNull<CPosition2>(ent, components->position2);
            };
            registry_table["GetPosition2vec"] = [&](core::ecs::ComponentId ent)
            {
                auto c = registry.GetComponentOrNull<CPosition2>(ent, components->position2);
                return c == nullptr ? nullptr : &c->pos;
            };

            duk->lua.new_usertype<CPosition2>("CPosition2", "vec", &CPosition2::pos);

            duk->lua.new_usertype<CSprite>
            (
                "CSprite",
                "GetRect", [](const CSprite& sp, const CPosition2& p)
                {
                    return GetSpriteRect(p.pos, *sp.texture);
                }
            );

            auto rect_type = duk->lua.new_usertype<core::Rectf>("Rectf", sol::no_constructor);
            rect_type["Contains"] = sol::overload
            (
                [](const core::Rectf& r, const core::Rectf& rr) -> bool
                {
                    return r.ContainsExclusive(rr);
                },
                [](const core::Rectf& r, const core::vec2f& p) -> bool
                {
                    return r.ContainsExclusive(p);
                },
                [](const core::Rectf& r, float x, float y) -> bool
                {
                    return r.ContainsExclusive(x, y);
                }
            );
            rect_type["GetHeight"] = &core::Rectf::GetHeight;
            rect_type["GetWidth"] = &core::Rectf::GetWidth;

            auto random_type = duk->lua.new_usertype<core::Random>("Random");
            random_type["NextFloat01"] = &core::Random::NextFloat01;
            random_type["NextRangeFloat"] = [](core::Random& r, float f) -> float
            {
                return r.NextRange(f);
            };
            random_type["NextBool"] = &core::Random::NextBool;
            random_type["NextPoint2"] = [](core::Random& r, core::Rectf& rect) -> core::vec2f
            {
                return rect.RandomPoint(&r);
            };
        }

        DukSystems systems;
        DukRegistry registry;
        sol::table     input;
        core::ecs::World* world;
        ObjectCreator* creator;
        Components* components;
        CameraData* camera;
    };

    DukIntegration::DukIntegration
    (
        core::ecs::Systems* systems,
        core::ecs::World* reg,
        Sol* duk,
        ObjectCreator* creator,
        Components* components,
        CameraData* camera
    )
    {
        pimpl.reset
        (
            new DukIntegrationPimpl
            (
                systems,
                reg,
                duk,
                creator,
                components,
                camera
            )
        );
        pimpl->Integrate(duk);
    }

    DukIntegration::~DukIntegration()
    {
        Clear();
    }

    void
    DukIntegration::Clear()
    {
        pimpl.reset();
    }

    DukRegistry&
    DukIntegration::Registry()
    {
        ASSERT(pimpl);
        return pimpl->registry;
    }

    void
    DukIntegration::BindKeys(Sol* duk, const Input& input)
    {
        input.Set(&pimpl->input);
    }
}

TYPEID_SETUP_TYPE(euphoria::core::Random);
TYPEID_SETUP_TYPE(euphoria::engine::ObjectTemplate);
TYPEID_SETUP_TYPE(euphoria::core::Rect<float>);
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2005 The Regents of The University of Michigan
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/** @file
 * Implements a 8250 UART
 */

#include <string>
#include <vector>

#include "base/inifile.hh"
#include "base/str.hh"        // for to_number
#include "base/trace.hh"
#include "dev/simconsole.hh"
#include "dev/uart8250.hh"
#include "dev/platform.hh"
#include "mem/bus/bus.hh"
#include "mem/bus/pio_interface.hh"
#include "mem/bus/pio_interface_impl.hh"
#include "mem/functional/memory_control.hh"
#include "sim/builder.hh"

using namespace std;

Uart8250::IntrEvent::IntrEvent(Uart8250 *u, int bit)
    : Event(&mainEventQueue), uart(u)
{
    DPRINTF(Uart, "UART Interrupt Event Initilizing\n");
    intrBit = bit;
}

const char *
Uart8250::IntrEvent::description()
{
    return "uart interrupt delay event";
}

void
Uart8250::IntrEvent::process()
{
    if (intrBit & uart->IER) {
       DPRINTF(Uart, "UART InterEvent, interrupting\n");
       uart->platform->postConsoleInt();
       uart->status |= intrBit;
    }
    else
       DPRINTF(Uart, "UART InterEvent, not interrupting\n");

}

/* The linux serial driver (8250.c about line 1182) loops reading from
 * the device until the device reports it has no more data to
 * read. After a maximum of 255 iterations the code prints "serial8250
 * too much work for irq X," and breaks out of the loop. Since the
 * simulated system is so much slower than the actual system, if a
 * user is typing on the keyboard it is very easy for them to provide
 * input at a fast enough rate to not allow the loop to exit and thus
 * the error to be printed. This magic number provides a delay between
 * the time the UART receives a character to send to the simulated
 * system and the time it actually notifies the system it has a
 * character to send to alleviate this problem. --Ali
 */
void
Uart8250::IntrEvent::scheduleIntr()
{
    static const Tick interval = (Tick)((Clock::Float::s / 2e9) * 450);
    DPRINTF(Uart, "Scheduling IER interrupt for %#x, at cycle %lld\n", intrBit,
            curTick + interval);
    if (!scheduled())
        schedule(curTick + interval);
    else
        reschedule(curTick + interval);
}

Uart8250::Uart8250(const string &name, SimConsole *c, MemoryController *mmu, Addr a,
           Addr s, HierParams *hier, Bus *bus, Tick pio_latency, Platform *p)
    : Uart(name, c, mmu, a, s, hier, bus, pio_latency, p),
      txIntrEvent(this, TX_INT), rxIntrEvent(this, RX_INT)
{
    IER = 0;
    DLAB = 0;
    LCR = 0;
    MCR = 0;

}

Fault
Uart8250::read(MemReqPtr &req, uint8_t *data)
{
    Addr daddr = req->paddr - (addr & EV5::PAddrImplMask);
    DPRINTF(Uart, " read register %#x\n", daddr);

    assert(req->size == 1);

    switch (daddr) {
        case 0x0:
            if (!(LCR & 0x80)) { // read byte
                if (cons->dataAvailable())
                    cons->in(*data);
                else {
                    *(uint8_t*)data = 0;
                    // A limited amount of these are ok.
                    DPRINTF(Uart, "empty read of RX register\n");
                }
                status &= ~RX_INT;
                platform->clearConsoleInt();

                if (cons->dataAvailable() && (IER & UART_IER_RDI))
                    rxIntrEvent.scheduleIntr();
            } else { // dll divisor latch
               ;
            }
            break;
        case 0x1:
            if (!(LCR & 0x80)) { // Intr Enable Register(IER)
                *(uint8_t*)data = IER;
            } else { // DLM divisor latch MSB
                ;
            }
            break;
        case 0x2: // Intr Identification Register (IIR)
            DPRINTF(Uart, "IIR Read, status = %#x\n", (uint32_t)status);

            //Tx interrupts are cleared on IIR reads
            status &= ~TX_INT;

            if (status & RX_INT)
                *(uint8_t*)data = IIR_RXID;
            else
                *(uint8_t*)data = IIR_NOPEND;
            break;
        case 0x3: // Line Control Register (LCR)
            *(uint8_t*)data = LCR;
            break;
        case 0x4: // Modem Control Register (MCR)
            break;
        case 0x5: // Line Status Register (LSR)
            uint8_t lsr;
            lsr = 0;
            // check if there are any bytes to be read
            if (cons->dataAvailable())
                lsr = UART_LSR_DR;
            lsr |= UART_LSR_TEMT | UART_LSR_THRE;
            *(uint8_t*)data = lsr;
            break;
        case 0x6: // Modem Status Register (MSR)
            *(uint8_t*)data = 0;
            break;
        case 0x7: // Scratch Register (SCR)
            *(uint8_t*)data = 0; // doesn't exist with at 8250.
            break;
        default:
            panic("Tried to access a UART port that doesn't exist\n");
            break;
    }

    return No_Fault;

}

Fault
Uart8250::write(MemReqPtr &req, const uint8_t *data)
{
    Addr daddr = req->paddr - (addr & EV5::PAddrImplMask);

    DPRINTF(Uart, " write register %#x value %#x\n", daddr, *(uint8_t*)data);

    switch (daddr) {
        case 0x0:
            if (!(LCR & 0x80)) { // write byte
                cons->out(*(uint8_t *)data);
                platform->clearConsoleInt();
                status &= ~TX_INT;
                if (UART_IER_THRI & IER)
                    txIntrEvent.scheduleIntr();
            } else { // dll divisor latch
               ;
            }
            break;
        case 0x1:
            if (!(LCR & 0x80)) { // Intr Enable Register(IER)
                IER = *(uint8_t*)data;
                if (UART_IER_THRI & IER)
                {
                    DPRINTF(Uart, "IER: IER_THRI set, scheduling TX intrrupt\n");
                    txIntrEvent.scheduleIntr();
                }
                else
                {
                    DPRINTF(Uart, "IER: IER_THRI cleared, descheduling TX intrrupt\n");
                    if (txIntrEvent.scheduled())
                        txIntrEvent.deschedule();
                    if (status & TX_INT)
                        platform->clearConsoleInt();
                    status &= ~TX_INT;
                }

                if ((UART_IER_RDI & IER) && cons->dataAvailable()) {
                    DPRINTF(Uart, "IER: IER_RDI set, scheduling RX intrrupt\n");
                    rxIntrEvent.scheduleIntr();
                } else {
                    DPRINTF(Uart, "IER: IER_RDI cleared, descheduling RX intrrupt\n");
                    if (rxIntrEvent.scheduled())
                        rxIntrEvent.deschedule();
                    if (status & RX_INT)
                        platform->clearConsoleInt();
                    status &= ~RX_INT;
                }
             } else { // DLM divisor latch MSB
                ;
            }
            break;
        case 0x2: // FIFO Control Register (FCR)
            break;
        case 0x3: // Line Control Register (LCR)
            LCR = *(uint8_t*)data;
            break;
        case 0x4: // Modem Control Register (MCR)
            if (*(uint8_t*)data == (UART_MCR_LOOP | 0x0A))
                    MCR = 0x9A;
            break;
        case 0x7: // Scratch Register (SCR)
            // We are emulating a 8250 so we don't have a scratch reg
            break;
        default:
            panic("Tried to access a UART port that doesn't exist\n");
            break;
    }
    return No_Fault;
}

void
Uart8250::dataAvailable()
{
    // if the kernel wants an interrupt when we have data
    if (IER & UART_IER_RDI)
    {
        platform->postConsoleInt();
        status |= RX_INT;
    }

}

void
Uart8250::serialize(ostream &os)
{
    SERIALIZE_SCALAR(status);
    SERIALIZE_SCALAR(IER);
    SERIALIZE_SCALAR(DLAB);
    SERIALIZE_SCALAR(LCR);
    SERIALIZE_SCALAR(MCR);
    Tick rxintrwhen;
    if (rxIntrEvent.scheduled())
        rxintrwhen = rxIntrEvent.when();
    else
        rxintrwhen = 0;
    Tick txintrwhen;
    if (txIntrEvent.scheduled())
        txintrwhen = txIntrEvent.when();
    else
        txintrwhen = 0;
     SERIALIZE_SCALAR(rxintrwhen);
     SERIALIZE_SCALAR(txintrwhen);
}

void
Uart8250::unserialize(Checkpoint *cp, const std::string &section)
{
    UNSERIALIZE_SCALAR(status);
    UNSERIALIZE_SCALAR(IER);
    UNSERIALIZE_SCALAR(DLAB);
    UNSERIALIZE_SCALAR(LCR);
    UNSERIALIZE_SCALAR(MCR);
    Tick rxintrwhen;
    Tick txintrwhen;
    UNSERIALIZE_SCALAR(rxintrwhen);
    UNSERIALIZE_SCALAR(txintrwhen);
    if (rxintrwhen != 0)
        rxIntrEvent.schedule(rxintrwhen);
    if (txintrwhen != 0)
        txIntrEvent.schedule(txintrwhen);
}

BEGIN_DECLARE_SIM_OBJECT_PARAMS(Uart8250)

    SimObjectParam<SimConsole *> console;
    SimObjectParam<MemoryController *> mmu;
    SimObjectParam<Platform *> platform;
    Param<Addr> addr;
    Param<Addr> size;
    SimObjectParam<Bus*> io_bus;
    Param<Tick> pio_latency;
    SimObjectParam<HierParams *> hier;

END_DECLARE_SIM_OBJECT_PARAMS(Uart8250)

BEGIN_INIT_SIM_OBJECT_PARAMS(Uart8250)

    INIT_PARAM(console, "The console"),
    INIT_PARAM(mmu, "Memory Controller"),
    INIT_PARAM(platform, "Pointer to platfrom"),
    INIT_PARAM(addr, "Device Address"),
    INIT_PARAM_DFLT(size, "Device size", 0x8),
    INIT_PARAM_DFLT(io_bus, "The IO Bus to attach to", NULL),
    INIT_PARAM_DFLT(pio_latency, "Programmed IO latency in bus cycles", 1),
    INIT_PARAM_DFLT(hier, "Hierarchy global variables", &defaultHierParams)

END_INIT_SIM_OBJECT_PARAMS(Uart8250)

CREATE_SIM_OBJECT(Uart8250)
{
    return new Uart8250(getInstanceName(), console, mmu, addr, size, hier, io_bus,
                    pio_latency, platform);
}

REGISTER_SIM_OBJECT("Uart8250", Uart8250)
<commit_msg>Uart fix.<commit_after>/*
 * Copyright (c) 2005 The Regents of The University of Michigan
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/** @file
 * Implements a 8250 UART
 */

#include <string>
#include <vector>

#include "base/inifile.hh"
#include "base/str.hh"        // for to_number
#include "base/trace.hh"
#include "dev/simconsole.hh"
#include "dev/uart8250.hh"
#include "dev/platform.hh"
#include "mem/bus/bus.hh"
#include "mem/bus/pio_interface.hh"
#include "mem/bus/pio_interface_impl.hh"
#include "mem/functional/memory_control.hh"
#include "sim/builder.hh"

using namespace std;

Uart8250::IntrEvent::IntrEvent(Uart8250 *u, int bit)
    : Event(&mainEventQueue), uart(u)
{
    DPRINTF(Uart, "UART Interrupt Event Initilizing\n");
    intrBit = bit;
}

const char *
Uart8250::IntrEvent::description()
{
    return "uart interrupt delay event";
}

void
Uart8250::IntrEvent::process()
{
    if (intrBit & uart->IER) {
       DPRINTF(Uart, "UART InterEvent, interrupting\n");
       uart->platform->postConsoleInt();
       uart->status |= intrBit;
    }
    else
       DPRINTF(Uart, "UART InterEvent, not interrupting\n");

}

/* The linux serial driver (8250.c about line 1182) loops reading from
 * the device until the device reports it has no more data to
 * read. After a maximum of 255 iterations the code prints "serial8250
 * too much work for irq X," and breaks out of the loop. Since the
 * simulated system is so much slower than the actual system, if a
 * user is typing on the keyboard it is very easy for them to provide
 * input at a fast enough rate to not allow the loop to exit and thus
 * the error to be printed. This magic number provides a delay between
 * the time the UART receives a character to send to the simulated
 * system and the time it actually notifies the system it has a
 * character to send to alleviate this problem. --Ali
 */
void
Uart8250::IntrEvent::scheduleIntr()
{
    static const Tick interval = (Tick)((Clock::Float::s / 2e9) * 450);
    DPRINTF(Uart, "Scheduling IER interrupt for %#x, at cycle %lld\n", intrBit,
            curTick + interval);
    if (!scheduled())
        schedule(curTick + interval);
    else
        reschedule(curTick + interval);
}

Uart8250::Uart8250(const string &name, SimConsole *c, MemoryController *mmu, Addr a,
           Addr s, HierParams *hier, Bus *bus, Tick pio_latency, Platform *p)
    : Uart(name, c, mmu, a, s, hier, bus, pio_latency, p),
      txIntrEvent(this, TX_INT), rxIntrEvent(this, RX_INT)
{
    IER = 0;
    DLAB = 0;
    LCR = 0;
    MCR = 0;

}

Fault
Uart8250::read(MemReqPtr &req, uint8_t *data)
{
    Addr daddr = req->paddr - (addr & EV5::PAddrImplMask);
    DPRINTF(Uart, " read register %#x\n", daddr);

    assert(req->size == 1);

    switch (daddr) {
        case 0x0:
            if (!(LCR & 0x80)) { // read byte
                if (cons->dataAvailable())
                    cons->in(*data);
                else {
                    *(uint8_t*)data = 0;
                    // A limited amount of these are ok.
                    DPRINTF(Uart, "empty read of RX register\n");
                }
                status &= ~RX_INT;
                platform->clearConsoleInt();

                if (cons->dataAvailable() && (IER & UART_IER_RDI))
                    rxIntrEvent.scheduleIntr();
            } else { // dll divisor latch
               ;
            }
            break;
        case 0x1:
            if (!(LCR & 0x80)) { // Intr Enable Register(IER)
                *(uint8_t*)data = IER;
            } else { // DLM divisor latch MSB
                ;
            }
            break;
        case 0x2: // Intr Identification Register (IIR)
            DPRINTF(Uart, "IIR Read, status = %#x\n", (uint32_t)status);

            if (status & RX_INT) /* Rx data interrupt has a higher priority */
                *(uint8_t*)data = IIR_RXID;
            else if (status & TX_INT)
                *(uint8_t*)data = IIR_TXID;
            else
                *(uint8_t*)data = IIR_NOPEND;

            //Tx interrupts are cleared on IIR reads
            status &= ~TX_INT;
            break;
        case 0x3: // Line Control Register (LCR)
            *(uint8_t*)data = LCR;
            break;
        case 0x4: // Modem Control Register (MCR)
            break;
        case 0x5: // Line Status Register (LSR)
            uint8_t lsr;
            lsr = 0;
            // check if there are any bytes to be read
            if (cons->dataAvailable())
                lsr = UART_LSR_DR;
            lsr |= UART_LSR_TEMT | UART_LSR_THRE;
            *(uint8_t*)data = lsr;
            break;
        case 0x6: // Modem Status Register (MSR)
            *(uint8_t*)data = 0;
            break;
        case 0x7: // Scratch Register (SCR)
            *(uint8_t*)data = 0; // doesn't exist with at 8250.
            break;
        default:
            panic("Tried to access a UART port that doesn't exist\n");
            break;
    }

    return No_Fault;

}

Fault
Uart8250::write(MemReqPtr &req, const uint8_t *data)
{
    Addr daddr = req->paddr - (addr & EV5::PAddrImplMask);

    DPRINTF(Uart, " write register %#x value %#x\n", daddr, *(uint8_t*)data);

    switch (daddr) {
        case 0x0:
            if (!(LCR & 0x80)) { // write byte
                cons->out(*(uint8_t *)data);
                platform->clearConsoleInt();
                status &= ~TX_INT;
                if (UART_IER_THRI & IER)
                    txIntrEvent.scheduleIntr();
            } else { // dll divisor latch
               ;
            }
            break;
        case 0x1:
            if (!(LCR & 0x80)) { // Intr Enable Register(IER)
                IER = *(uint8_t*)data;
                if (UART_IER_THRI & IER)
                {
                    DPRINTF(Uart, "IER: IER_THRI set, scheduling TX intrrupt\n");
                    txIntrEvent.scheduleIntr();
                }
                else
                {
                    DPRINTF(Uart, "IER: IER_THRI cleared, descheduling TX intrrupt\n");
                    if (txIntrEvent.scheduled())
                        txIntrEvent.deschedule();
                    if (status & TX_INT)
                        platform->clearConsoleInt();
                    status &= ~TX_INT;
                }

                if ((UART_IER_RDI & IER) && cons->dataAvailable()) {
                    DPRINTF(Uart, "IER: IER_RDI set, scheduling RX intrrupt\n");
                    rxIntrEvent.scheduleIntr();
                } else {
                    DPRINTF(Uart, "IER: IER_RDI cleared, descheduling RX intrrupt\n");
                    if (rxIntrEvent.scheduled())
                        rxIntrEvent.deschedule();
                    if (status & RX_INT)
                        platform->clearConsoleInt();
                    status &= ~RX_INT;
                }
             } else { // DLM divisor latch MSB
                ;
            }
            break;
        case 0x2: // FIFO Control Register (FCR)
            break;
        case 0x3: // Line Control Register (LCR)
            LCR = *(uint8_t*)data;
            break;
        case 0x4: // Modem Control Register (MCR)
            if (*(uint8_t*)data == (UART_MCR_LOOP | 0x0A))
                    MCR = 0x9A;
            break;
        case 0x7: // Scratch Register (SCR)
            // We are emulating a 8250 so we don't have a scratch reg
            break;
        default:
            panic("Tried to access a UART port that doesn't exist\n");
            break;
    }
    return No_Fault;
}

void
Uart8250::dataAvailable()
{
    // if the kernel wants an interrupt when we have data
    if (IER & UART_IER_RDI)
    {
        platform->postConsoleInt();
        status |= RX_INT;
    }

}

void
Uart8250::serialize(ostream &os)
{
    SERIALIZE_SCALAR(status);
    SERIALIZE_SCALAR(IER);
    SERIALIZE_SCALAR(DLAB);
    SERIALIZE_SCALAR(LCR);
    SERIALIZE_SCALAR(MCR);
    Tick rxintrwhen;
    if (rxIntrEvent.scheduled())
        rxintrwhen = rxIntrEvent.when();
    else
        rxintrwhen = 0;
    Tick txintrwhen;
    if (txIntrEvent.scheduled())
        txintrwhen = txIntrEvent.when();
    else
        txintrwhen = 0;
     SERIALIZE_SCALAR(rxintrwhen);
     SERIALIZE_SCALAR(txintrwhen);
}

void
Uart8250::unserialize(Checkpoint *cp, const std::string &section)
{
    UNSERIALIZE_SCALAR(status);
    UNSERIALIZE_SCALAR(IER);
    UNSERIALIZE_SCALAR(DLAB);
    UNSERIALIZE_SCALAR(LCR);
    UNSERIALIZE_SCALAR(MCR);
    Tick rxintrwhen;
    Tick txintrwhen;
    UNSERIALIZE_SCALAR(rxintrwhen);
    UNSERIALIZE_SCALAR(txintrwhen);
    if (rxintrwhen != 0)
        rxIntrEvent.schedule(rxintrwhen);
    if (txintrwhen != 0)
        txIntrEvent.schedule(txintrwhen);
}

BEGIN_DECLARE_SIM_OBJECT_PARAMS(Uart8250)

    SimObjectParam<SimConsole *> console;
    SimObjectParam<MemoryController *> mmu;
    SimObjectParam<Platform *> platform;
    Param<Addr> addr;
    Param<Addr> size;
    SimObjectParam<Bus*> io_bus;
    Param<Tick> pio_latency;
    SimObjectParam<HierParams *> hier;

END_DECLARE_SIM_OBJECT_PARAMS(Uart8250)

BEGIN_INIT_SIM_OBJECT_PARAMS(Uart8250)

    INIT_PARAM(console, "The console"),
    INIT_PARAM(mmu, "Memory Controller"),
    INIT_PARAM(platform, "Pointer to platfrom"),
    INIT_PARAM(addr, "Device Address"),
    INIT_PARAM_DFLT(size, "Device size", 0x8),
    INIT_PARAM_DFLT(io_bus, "The IO Bus to attach to", NULL),
    INIT_PARAM_DFLT(pio_latency, "Programmed IO latency in bus cycles", 1),
    INIT_PARAM_DFLT(hier, "Hierarchy global variables", &defaultHierParams)

END_INIT_SIM_OBJECT_PARAMS(Uart8250)

CREATE_SIM_OBJECT(Uart8250)
{
    return new Uart8250(getInstanceName(), console, mmu, addr, size, hier, io_bus,
                    pio_latency, platform);
}

REGISTER_SIM_OBJECT("Uart8250", Uart8250)
<|endoftext|>