/*
 * This program source code file is part of KiCad, a free EDA CAD application.
 *
 * Copyright (C) 2022 Fabien Corona f.corona<at>laposte.net
 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * 2. 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.
 *
 * 3. Neither the name of the copyright holder 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 HOLDER 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.
 */


#include "ibis_parser.h"

#include <sstream>
#include <cstring> //for memcmp
#include <iterator>
#include <locale_io.h> // KiCad header

// _() is used here to mark translatable strings in IBIS_REPORTER::Report()
// However, currently non ASCII7 chars are nor correctly handled when printing messages
// So we disable translations
#if 0
#include <wx/intl.h>    // for _() macro and wxGetTranslation()
#else
#undef _
#define _( x ) x
#endif


bool IbisParser::compareIbisWord( const std::string& stra, const std::string& strb )
{
    return std::equal( stra.begin(), stra.end(),
                        strb.begin(), strb.end(),
                        [](char a, char b)
                        {
                            return std::tolower(a) == std::tolower(b);
                        });
}


bool IBIS_MATRIX_BANDED::Check()
{
    bool status = true;

    if( m_dim < 1 )
    {
        Report( _( "Dimension of matrices should be >= 1." ), RPT_SEVERITY_ERROR );
        status = false;
    }

    if( m_bandwidth < 1 )
    {
        Report( _( "Bandwidth of banded matrices should be >= 1." ), RPT_SEVERITY_ERROR );
        status = false;
    }

    for( int i = 0; i < m_bandwidth * m_dim; i++ )
    {
        if( std::isnan( m_data[i] ) )
        {
            Report( _( "There are NaN elements in a matrix." ), RPT_SEVERITY_ERROR );
            status = false;
        }
    }

    return status;
}


bool IBIS_MATRIX_FULL::Check()
{
    bool status = true;

    if( m_dim < 1 )
    {
        Report( _( "Dimension of matrices should be >= 1." ), RPT_SEVERITY_ERROR );
        status = false;
    }

    for( int i = 0; i < m_dim * m_dim; i++ )
    {
        if( std::isnan( m_data[i] ) )
        {
            Report( _( "There are NaN elements in a matrix." ), RPT_SEVERITY_ERROR );
            status = false;
        }
    }

    return status;
}


bool IBIS_MATRIX_SPARSE::Check()
{
    bool status = true;

    if( m_dim < 1 )
    {
        Report( _( "Dimension of matrices should be >= 1." ), RPT_SEVERITY_ERROR );
        status = false;
    }

    for( int i = 0; i < m_dim * m_dim; i++ )
    {
        if( std::isnan( m_data[i] ) )
        {
            Report( _( "There are NaN elements in a matrix." ), RPT_SEVERITY_ERROR );
            status = false;
        }
    }

    return status;
}


bool isNumberNA( double aNumber )
{
    double NA = nan( NAN_NA );
    return std::memcmp( &aNumber, &NA, sizeof NA ) == 0;
}


bool TypMinMaxValue::Check()
{
    bool status = true;

    if( std::isnan( value[IBIS_CORNER::TYP] ) )
        status = false;

    if( std::isnan( value[IBIS_CORNER::MIN] ) && !isNumberNA( value[IBIS_CORNER::MIN] ) )
        status = false;

    if( std::isnan( value[IBIS_CORNER::MAX] ) && !isNumberNA( value[IBIS_CORNER::MAX] ) )
        status = false;

    return status;
}


bool IbisComponentPackage::Check()
{
    bool status =  true;

    if( !m_Rpkg.Check() )
    {
        Report( _( "Invalid R_pkg value." ), RPT_SEVERITY_ERROR );
        status = false;
    }

    if( !m_Lpkg.Check() )
    {
        Report( _( "Invalid L_pkg value." ), RPT_SEVERITY_ERROR );
        status = false;
    }

    if( !m_Cpkg.Check() )
    {
        Report( _( "Invalid C_pkg value." ), RPT_SEVERITY_ERROR );
        status = false;
    }

    return status;
}


bool IbisComponentPin::Check()
{
    bool status = true;

    if( !m_dummy )
    {
        std::stringstream message;
        message << _( "Checking pin " ) << m_pinName;

        if( m_pinName.empty() )
        {
            if( status )
                Report( message.str(), RPT_SEVERITY_ACTION );

            Report( _( "Pin name cannot be empty." ), RPT_SEVERITY_ERROR );
            status = false;
        }

        if( m_signalName.empty() )
        {
            if( status )
                Report( message.str(), RPT_SEVERITY_ACTION );

            Report( _( "Signal name cannot be empty." ), RPT_SEVERITY_ERROR );
            status = false;
        }

        if( m_modelName.empty() )
        {
            if( status )
                Report( message.str(), RPT_SEVERITY_ACTION );

            Report( _( "Model name cannot be empty." ), RPT_SEVERITY_ERROR );
            status = false;
        }

        if( std::isnan( m_Rpin ) && !isNumberNA( m_Rpin ) )
        {
            if( status )
                Report( message.str(), RPT_SEVERITY_ACTION );

           Report( _( "Rpin is not valid." ), RPT_SEVERITY_ERROR );
           status = false;
        }

        if( std::isnan( m_Lpin )&& !isNumberNA( m_Lpin )  )
        {
            if( status )
                Report( message.str(), RPT_SEVERITY_ACTION );

            Report( _( "Lpin is not valid." ), RPT_SEVERITY_ERROR );
            status = false;
        }

        if( std::isnan( m_Cpin )&& !isNumberNA( m_Cpin ) )
        {
            if( status )
                Report( message.str(), RPT_SEVERITY_ACTION );

            Report( _( "Cpin is not valid." ), RPT_SEVERITY_ERROR );
            status = false;
        }
    }

    return status;
}


bool IbisComponent::Check()
{
    bool status = true;

    std::stringstream message;
    message << _( "Checking component " ) << m_name;

    if( m_name.empty() )
    {
        if( status )
            Report( message.str(), RPT_SEVERITY_ACTION );

        Report( _( "Component: name cannot be empty." ), RPT_SEVERITY_ERROR );
        status = false;
    }

    if( m_manufacturer.empty() )
    {
        if( status )
            Report( message.str(), RPT_SEVERITY_ACTION );

        Report( _( "Component: manufacturer cannot be empty." ), RPT_SEVERITY_ERROR );
        status = false;
    }

    if( !m_package.Check() )
    {
        if( status )
            Report( message.str(), RPT_SEVERITY_ACTION );

        Report( _( "Component: invalid package." ), RPT_SEVERITY_ERROR );
        status = false;
    }

    if( m_pins.size() < 1 )
    {
        if( status )
            Report( message.str(), RPT_SEVERITY_ACTION );

        Report( _( "Component: no pin" ), RPT_SEVERITY_ERROR );
        status = false;
    }

    for( IbisComponentPin& pin : m_pins )
        status &= pin.Check();

    return status;
}


bool IbisModelSelector::Check()
{
    return true;
}


std::string IBIS_BASE::doubleToString( double aNumber )
{
    std::ostringstream ss;
    ss.setf( std::ios_base::scientific, std::ios_base::floatfield );
    ss << aNumber;
    return ss.str();
}


std::string IVtable::Spice( int aN, const std::string& aPort1, const std::string& aPort2,
                            const std::string& aModelName, IBIS_CORNER aCorner ) const
{
    std::string result = "";

    if( m_entries.size() > 0 )
    {
        result += "a";
        result += std::to_string( aN );
        result += " %vd(";
        result += aPort1;
        result += " ";
        result += aPort2;
        result += ") %id(";
        result += aPort1;
        result += " ";
        result += aPort2;
        result += ") ";
        result += aModelName;
        result += "\n";
        result += "\n";
        result += ".model ";
        result += aModelName;
        result += " pwl(\n+ x_array=[";

        for( const IVtableEntry& entry : m_entries )
        {
            result += doubleToString( entry.V );
            result += "\n+";
        }

        result += "]\n+ y_array=[";

        for( const IVtableEntry& entry : m_entries )
        {
            result += doubleToString( entry.I.value[aCorner] );
            result += "\n+";
        }

        result += "]\n+ input_domain=0.05 fraction=TRUE)\n\n";
    }
    return result;
}

double IVtable::InterpolatedI( double aV, IBIS_CORNER aCorner ) const
{
    // @TODO change this algorithm

    if( m_entries.back().V > m_entries.at( 0 ).V )
    {
        if( aV >= m_entries.back().V )
            return m_entries.back().I.value[aCorner];

        if( aV <= m_entries.at( 0 ).V )
            return m_entries.at( 0 ).I.value[aCorner];

        for( size_t i = 1; i < m_entries.size(); i++ )
        {
            if( m_entries.at( i ).V > aV )
            {
                return m_entries.at( i - 1 ).I.value[aCorner]
                       + ( m_entries.at( i ).I.value[aCorner]
                           - m_entries.at( i - 1 ).I.value[aCorner] )
                                 / ( m_entries.at( i ).V - m_entries.at( i - 1 ).V )
                                 * ( aV - m_entries.at( i - 1 ).V );
            }
        }

        Report( _( "Cannot interpolate the current based on this IV table." ), RPT_SEVERITY_ERROR );
        return nan( "" );
    }
    else
    {
        // exiting the function here would mean the IV table is reversed.
        return nan( "" );
    }
    // @TODO prefer another method such as a dichotomy
}

bool IVtable::Check()
{
    bool status = true;

    for( IVtableEntry& entry : m_entries )
    {
        if( std::isnan( entry.V ) )
        {
            Report( _( "There is an invalid voltage in an IV table" ), RPT_SEVERITY_ERROR );
            status = false;
            break;
        }

        if( !entry.I.Check() )
        {
            Report( _( "There is an invalid current in an IV table" ), RPT_SEVERITY_ERROR );
            status = false;
            break;
        }
    }

    // TODO: Check if the IV table is monotonic :
    // IBIS standard defines 8 criteria for an IV table to be monotonic
    // Issue a warning, not an error

    return status;
}


bool dvdtTypMinMax::Check()
{
    bool status = true;

    if( std::isnan( value[IBIS_CORNER::TYP].m_dv ) )
        status = false;

    if( std::isnan( value[IBIS_CORNER::TYP].m_dt ) )
        status = false;

    if( std::isnan( value[IBIS_CORNER::MIN].m_dv ) && !isNumberNA( value[IBIS_CORNER::MIN].m_dv ) )
        status = false;

    if( std::isnan( value[IBIS_CORNER::MIN].m_dt ) && !isNumberNA( value[IBIS_CORNER::MIN].m_dt ) )
        status = false;

    if( std::isnan( value[IBIS_CORNER::MIN].m_dv ) && !isNumberNA( value[IBIS_CORNER::MIN].m_dv ) )
        status = false;

    if( std::isnan( value[IBIS_CORNER::MIN].m_dt ) && !isNumberNA( value[IBIS_CORNER::MIN].m_dt ) )
        status = false;

    return status;
}

bool IbisRamp::Check()
{
    bool status = true;

    if( std::isnan( m_Rload ) )
    {
        status = false;
        Report( _( "Invalid R_load." ), RPT_SEVERITY_ERROR );
    }

    if( !m_falling.Check() )
    {
        Report( _( "Invalid falling dV/dt." ), RPT_SEVERITY_ERROR );
        status = false;
    }

    if( !m_rising.Check() )
    {
        Report( _( "Invalid rising dV/dt." ), RPT_SEVERITY_ERROR );
        status = false;
    }

    return status;
}



bool IbisModel::Check()
{
    bool status = true;

    if( m_name.empty() )
    {
        Report( _( "Model name cannot be empty" ), RPT_SEVERITY_ERROR );
        status = false;
    }

    std::stringstream message;
    message << _( "Checking model " ) << m_name;

    if( m_type == IBIS_MODEL_TYPE::UNDEFINED)
    {
        if( status )
            Report( message.str(), RPT_SEVERITY_ACTION );

        Report( _( "Undefined model type." ), RPT_SEVERITY_ERROR );
        status = false;
    }

    if( std::isnan( m_vinh ) && !isNumberNA( m_vinh ) )
    {
        if( status )
            Report( message.str(), RPT_SEVERITY_ACTION );

        Report( _( "Invalid Vinh value." ), RPT_SEVERITY_ERROR );
        status = false;
    }

    if( std::isnan( m_vinl ) && !isNumberNA( m_vinl ) )
    {
        if( status )
            Report( message.str(), RPT_SEVERITY_ACTION );

        Report( _( "Invalid Vinl value." ), RPT_SEVERITY_ERROR );
        status = false;
    }

    if( std::isnan( m_rref ) && !isNumberNA( m_rref ) )
    {
        if( status )
            Report( message.str(), RPT_SEVERITY_ACTION );

        Report( _( "Invalid R_ref value." ), RPT_SEVERITY_ERROR );
        status = false;
    }

    if( std::isnan( m_cref ) && !isNumberNA( m_cref ) )
    {
        if( status )
            Report( message.str(), RPT_SEVERITY_ACTION );

        Report( _( "Invalid C_ref value." ), RPT_SEVERITY_ERROR );
        status = false;
    }

    if( std::isnan( m_vref ) && !isNumberNA( m_vref ) )
    {
        if( status )
            Report( message.str(), RPT_SEVERITY_ACTION );

        Report( _( "Invalid V_ref value." ), RPT_SEVERITY_ERROR );
        status = false;
    }

    if( std::isnan( m_vmeas ) && !isNumberNA( m_vmeas ) )
    {
        if( status )
            Report( message.str(), RPT_SEVERITY_ACTION );

        Report( _( "Invalid V_meas value." ), RPT_SEVERITY_ERROR );
        status = false;
    }

    if( !m_C_comp.Check() )
    {
        if( status )
            Report( message.str(), RPT_SEVERITY_ACTION );

        Report( _( "C_comp is invalid." ), RPT_SEVERITY_ERROR );
        status = false;
    }

    if( !m_temperatureRange.Check() )
    {
        if( status )
            Report( message.str(), RPT_SEVERITY_ACTION );

        Report( _( "Temperature Range is invalid." ), RPT_SEVERITY_ERROR );
        status = false;
    }

    if( !m_voltageRange.Check() )
    {
        // If the voltage range is not valid, it's ok, only if we have pulls and clamps
        if( !m_pulldownReference.Check() )
            status = false;

        if( !m_pullupReference.Check() )
            status = false;

        if( !m_GNDClampReference.Check() )
            status = false;

        if( !m_POWERClampReference.Check() )
            status = false;

        if( !status )
            Report( _( "Voltage Range is invalid." ), RPT_SEVERITY_ERROR );

        status = false;
    }

    if( !m_pulldown.Check() )
    {
        Report( _( "Invalid pulldown." ), RPT_SEVERITY_ERROR );
        status = false;
    }

    if( !m_pullup.Check() )
    {
        Report( _( "Invalid pullup." ), RPT_SEVERITY_ERROR );
        status = false;
    }

    if( !m_POWERClamp.Check() )
    {
        Report( _( "Invalid POWER clamp." ), RPT_SEVERITY_ERROR );
        status = false;
    }

    if( !m_GNDClamp.Check() )
    {
        Report( _( "Invalid GND clamp." ), RPT_SEVERITY_ERROR );
        status = false;
    }

    if( m_type != IBIS_MODEL_TYPE::INPUT_ECL && m_type != IBIS_MODEL_TYPE::INPUT_STD
        && m_type != IBIS_MODEL_TYPE::TERMINATOR && m_type != IBIS_MODEL_TYPE::SERIES
        && m_type != IBIS_MODEL_TYPE::SERIES_SWITCH )
    {
        if( !m_ramp.Check() )
            Report( _( "Invalid Ramp" ), RPT_SEVERITY_ERROR );
    }

    return status;
}

bool IbisHeader::Check()
{
    bool status = true;

    std::stringstream message;
    message << _( "Checking Header..." );

    if( m_ibisVersion == -1 )
    {
        if( status )
            Report( message.str(), RPT_SEVERITY_ACTION );

        Report( _( "Missing [IBIS Ver]" ), RPT_SEVERITY_ERROR );
        status = false;
    }

    if( m_ibisVersion > IBIS_MAX_VERSION )
    {
        if( status )
            Report( message.str(), RPT_SEVERITY_ACTION );

        Report( _( "The parser does not handle this IBIS version" ), RPT_SEVERITY_ERROR );
        status = false;
    }

    if( m_fileRevision == -1 )
    {
        if( status )
            Report( message.str(), RPT_SEVERITY_ACTION );

        Report( _( "Missing [File Rev]" ), RPT_SEVERITY_ERROR );
        status = false;
    }

    if( m_fileName.empty() )
    {
        if( status )
            Report( message.str(), RPT_SEVERITY_ACTION );

        Report( _( "Missing [File Name]" ), RPT_SEVERITY_ERROR );
        status = false;
    }

    std::string ext = m_fileName.substr( m_fileName.length() - 4 );

    if( !( !strcmp( ext.c_str(), ".ibs" ) || !strcmp( ext.c_str(), ".pkg" )
           || !strcmp( ext.c_str(), ".ebd" ) || !strcmp( ext.c_str(), ".ims" ) ) )
    {
        if( status )
            Report( message.str(), RPT_SEVERITY_ACTION );

        Report( "Invalid file extension in [File Name]", RPT_SEVERITY_ERROR );
        status = false;
    }

    return status;
}

bool IbisPackageModel::Check()
{
    bool status = true;

    if( m_name.empty() )
    {
        Report( _( "Package model name cannot be empty." ), RPT_SEVERITY_ERROR );
        status = false;
    }

    std::stringstream message;
    message << _( "Checking package model " ) << m_name;

    if( m_manufacturer.empty() )
    {
        if( status )
            Report( message.str(), RPT_SEVERITY_ACTION );

        Report( _( "Manufacturer cannot be empty." ), RPT_SEVERITY_ERROR );
        status = false;
    }

    if( m_OEM.empty() )
    {
        if( status )
            Report( message.str(), RPT_SEVERITY_ACTION );

        Report( _( "OEM cannot be empty." ), RPT_SEVERITY_ERROR );
        status = false;
    }

    if( m_numberOfPins < 0 )
    {
        if( status )
            Report( message.str(), RPT_SEVERITY_ACTION );

        Report( _( "Negative number of pins." ), RPT_SEVERITY_ERROR );
        status = false;
    }

    if( (int)m_pins.size() != m_numberOfPins )
    {
        if( status )
            Report( message.str(), RPT_SEVERITY_ACTION );

        Report( "Number of pins does not match [Pin Numbers] size.", RPT_SEVERITY_ERROR );
        status = false;
    }

    for( size_t i = 0; i < m_pins.size(); i++ )
    {
        if( m_pins.at( i ).empty() )
        {
            if( status )
                Report( message.str(), RPT_SEVERITY_ACTION );

            Report( _( "Empty pin number." ), RPT_SEVERITY_ERROR );
            status = false;
        }
    }
    // resistance matrix is not required

    if( !( m_resistanceMatrix )->Check() )
    {
        if( status )
            Report( message.str(), RPT_SEVERITY_ACTION );

        Report( _( "Resistance matrix is incorrect." ), RPT_SEVERITY_ERROR );
        status = false;
    }

    if( m_capacitanceMatrix != nullptr )
    {
        if( m_capacitanceMatrix->m_type == IBIS_MATRIX_TYPE::UNDEFINED )
        {
            if( status )
                Report( message.str(), RPT_SEVERITY_ACTION );

            Report( _( "Capacitance matrix is undefined." ), RPT_SEVERITY_ERROR );
            status = false;
        }

        if( !m_capacitanceMatrix->Check() )
        {
            if( status )
                Report( message.str(), RPT_SEVERITY_ACTION );

            Report( _( "Capacitance matrix is incorrect." ), RPT_SEVERITY_ERROR );
            status = false;
        }
    }
    else
    {
        if( status )
            Report( message.str(), RPT_SEVERITY_ACTION );

        Report( _( "Capacitance matrix is nullptr." ), RPT_SEVERITY_ERROR );
        status = false;
    }

    if( m_inductanceMatrix != nullptr )
    {
        if( m_inductanceMatrix->m_type == IBIS_MATRIX_TYPE::UNDEFINED )
        {
            if( status )
                Report( message.str(), RPT_SEVERITY_ACTION );

            Report( _( "Inductance matrix is undefined." ), RPT_SEVERITY_ERROR );
            status = false;
        }

        if( !m_inductanceMatrix->Check() )
        {
            if( status )
                Report( message.str(), RPT_SEVERITY_ACTION );

            Report( _( "Inductance matrix is incorrect." ), RPT_SEVERITY_ERROR );
            status = false;
        }
    }
    else
    {
        if( status )
            Report( message.str(), RPT_SEVERITY_ACTION );

        Report( _( "Inductance matrix is nullptr." ), RPT_SEVERITY_ERROR );
        status = false;
    }
    return status;
}


bool IbisParser::ParseFile( const std::string& aFileName )
{
    std::stringstream err_msg;

    std::ifstream ibisFile;
    ibisFile.open( aFileName );

    if( !ibisFile.is_open() )
    {
        err_msg << _( "Cannot open file " ) << aFileName;
        Report( err_msg.str(), RPT_SEVERITY_ERROR );
        return false;
    }

    std::ostringstream ss;
    ss << ibisFile.rdbuf();
    const std::string& s = ss.str();
    m_buffer = std::vector<char>( s.begin(), s.end() );
    m_buffer.push_back( 0 );

    long size = m_buffer.size();

    m_lineCounter = 0;
    m_bufferIndex = 0;

    bool status = true;

    LOCALE_IO toggle;   // Temporary switch the locale to standard C to r/w floats

    while( ( m_bufferIndex < size ) && status )
    {
        if( !getNextLine() )
        {
            Report( _( "Unexpected end of file. Missing [END] ?" ), RPT_SEVERITY_ERROR );
            status = false;
        }

        if( status && m_parrot )
            printLine();

        if( status && !onNewLine() )
        {
            err_msg.clear();
            err_msg << _( "Error on line " ) << std::to_string( m_lineCounter );
            Report( err_msg.str(), RPT_SEVERITY_ERROR );
            status = false;
        }

        if( m_context == IBIS_PARSER_CONTEXT::END )
            break;
    }

    m_buffer.clear();
    return status;
}

void IbisParser::skipWhitespaces()
{
    while( isspace( m_buffer[m_lineOffset + m_lineIndex] ) && m_lineIndex < m_lineLength )
        m_lineIndex++;
}


bool IbisParser::checkEndofLine()
{
    skipWhitespaces();

    if( m_lineIndex < m_lineLength )
    {
        Report( _( "A line did not end properly." ), RPT_SEVERITY_ERROR );
        return false;
    }

    return true;
}


bool IbisParser::isLineEmptyFromCursor()
{
    int cursor = m_lineIndex;

    while( isspace( m_buffer[m_lineOffset + cursor] ) && cursor < m_lineLength )
        cursor++;

    return ( cursor >= m_lineLength );
}


bool IbisParser::readDvdt( std::string& aString, dvdt& aDest )
{
    bool status = true;

    if( aString == "NA" )
    {
        aDest.m_dv = nan( NAN_NA );
        aDest.m_dt = nan( NAN_NA );

        return status;
    }

    int i = 0;

    for( i = 1; i < (int)aString.length(); i++ )
    {
        if( aString.at( i ) == '/' )
            break;
    }

    if( aString.at( i ) == '/' )
    {
        std::string str1 = aString.substr( 0, i );
        std::string str2 = aString.substr( i + 1, aString.size() - i - 1 );

        if( !parseDouble( aDest.m_dv, str1, true ) || !parseDouble( aDest.m_dt, str2, true ) )
            status = false;
    }

    return status;
}


bool IbisParser::parseDouble( double& aDest, std::string& aStr, bool aAllowModifiers )
{
    // "  an entry of the C matrix could be given as 1.23e-12 or as 1.23p or 1.23pF."
    // Kibis: This implementation will also allow 1.23e-3n

    skipWhitespaces();
    bool status = true;
    bool converted = false;

    std::string str = aStr;

    double result;
    size_t size = 0;


    if( str == "NA" )
    {
        result = nan( NAN_NA );
    }
    else
    {
        try
        {
            result = std::stod( str, &size );
            converted = true;
        }
        catch( ... )
        {
            result = nan( NAN_INVALID );
            status = false;
        }
    }

    if( converted && ( size < str.length() ) )
    {
        switch( static_cast<char>( str.at( size ) ) )
        {
        case 'T': result *= 1e12;  break;
        case 'G': result *= 1e9;   break;
        case 'M': result *= 1e6;   break;
        case 'k': result *= 1e3;   break;
        case 'm': result *= 1e-3;  break;
        case 'u': result *= 1e-6;  break;
        case 'n': result *= 1e-9;  break;
        case 'p': result *= 1e-12; break;
        case 'f': result *= 1e-15; break;
        default:
            break;
            // In some files, "vinh = 3.0V", therefore we can't return false in the default case
        }
    }

    aDest = result;

    return status;
}


bool IbisParser::getNextLine()
{
    m_lineCounter++;

    long tmpIndex = m_bufferIndex;

    m_lineOffset = m_bufferIndex;

    if( m_bufferIndex >= (int)m_buffer.size() )
        return false;

    char c = m_buffer[m_bufferIndex++];

    int i = 1;

    while( c != m_commentChar && c != 0 && c != '\n' && i < IBIS_MAX_LINE_LENGTH )
    {
        c = m_buffer[m_bufferIndex++];
        i++;
    }

    if( i == IBIS_MAX_LINE_LENGTH )
    {
        Report( _( "Line exceeds maximum length." ), RPT_SEVERITY_ERROR );
        return false;
    }

    m_lineLength = m_bufferIndex - tmpIndex - 1; // Don't add the end of line condition
    m_lineIndex = 0;

    if( c == m_commentChar )
    {
        while( c != 0 && c != '\n' )
            c = m_buffer[m_bufferIndex++];
    }

    if( i == IBIS_MAX_LINE_LENGTH )
    {
        Report( _( "Line exceeds maximum length." ), RPT_SEVERITY_ERROR );
        return false;
    }

    return true;
}


void IbisParser::printLine()
{
    for( int i = 0; i < m_lineLength; i++ )
        std::cout << m_buffer[m_lineOffset + i];

    std::cout << std::endl;
}

bool IbisParser::readDouble( double& aDest )
{
    bool        status = true;
    std::string str;

    if( readWord( str ) )
    {
        if( !parseDouble( aDest, str, true ) )
        {
            Report( _( "Failed to read a double." ), RPT_SEVERITY_WARNING );
            status = false;
        }
    }
    else
    {
        Report( _( "Failed to read a word." ), RPT_SEVERITY_WARNING );
        status = false;
    }

    return status;
}

bool IbisParser::readInt( int& aDest )
{
    bool        status = true;
    std::string str;

    if( readWord( str ) )
    {
        double result;
        size_t size = 0;

        try
        {
            result = std::stoi( str, &size );
        }
        catch( ... )
        {
            if( str == "NA" )
            {
                result = nan( NAN_NA );
            }
            else
            {
                result = nan( NAN_INVALID );
                status = false;
            }
        }

        if( size != str.size() )
        {
            status = false;
            Report( _( "Number is not an integer" ), RPT_SEVERITY_WARNING );
        }

        aDest = result;
    }
    else
    {
        Report( _( "Failed to read a word." ), RPT_SEVERITY_WARNING );
        status = false;
    }

    return status;
}

bool IbisParser::readWord( std::string& aDest )
{
    skipWhitespaces();

    int startIndex = m_lineIndex;

    while( !isspace( m_buffer[m_lineOffset + m_lineIndex] ) && m_lineIndex < m_lineLength )
        m_lineIndex++;

    std::vector<char>::iterator start = std::next( m_buffer.begin(), m_lineOffset + startIndex );
    std::vector<char>::iterator end = std::next( m_buffer.begin(), m_lineOffset + m_lineIndex );
    aDest = std::string( start, end );

    return ( aDest.size() > 0 );
}

bool IbisParser::readString( std::string& aDest )
{
    while( m_lineIndex < m_lineLength )
        aDest += m_buffer[m_lineOffset + m_lineIndex++];

    // Remove extra whitespace characters
    int  len = aDest.length();

    if( len < 1 )
        return true;        // The data is likely on the next line. Skip.

    char c = aDest[len - 1];
    int i = 0;

    while( isspace( c ) && ( i < len ) )
    {
        c = aDest[len - 1 - i];
        i++;
    }

    aDest = aDest.substr( 0, len - i + 1 );

    return true;
}

bool IbisParser::storeString( std::string& aDest, bool aMultiline )
{
    bool status = true;

    skipWhitespaces();

    status &= readString( aDest );

    m_continue = aMultiline ? IBIS_PARSER_CONTINUE::STRING : IBIS_PARSER_CONTINUE::NONE;
    m_continuingString = &aDest;

    status &= checkEndofLine();
    return status;
}


bool IbisParser::changeCommentChar()
{
    skipWhitespaces();

    std::string strChar = "";

    // We cannot stop at m_lineLength here, because lineLength could stop before |_char
    // if the char remains the same

    char c = m_buffer[m_lineOffset + m_lineIndex++];
    char d = c;

    if( !( c == '!' || c == '"' || c == '#' || c == '$' || c == '%' || c == '&' || c == '\''
           || c == '(' || c == ')' || c == '*' || c == ',' || c == ':' || c == ';' || c == '<'
           || c == '>' || c == '?' || c == '@' || c == '\\' || c == '^' || c == '`' || c == '{'
           || c == '|' || c == '}' || c == '~' || c == ')' ) )
    {
        Report( _( "New comment character is invalid." ), RPT_SEVERITY_ERROR );
    }

    c = m_buffer[m_lineOffset + m_lineIndex++];

    while( ( !isspace( c ) ) && c != 0 && c != '\n' )
    {
        strChar += c;
        c = m_buffer[m_lineOffset + m_lineIndex++];
    }

    if( strChar != "_char" )
    {
        Report( _( "Invalid syntax. Should be |_char or &_char, etc..." ), RPT_SEVERITY_ERROR );
        return false;
    }

    while( isspace( c ) && c != 0 && c != '\n' && c != d )
        c = m_buffer[m_lineOffset + m_lineIndex++];

    if( ( !isspace( c ) ) && c != d )
    {
        Report( _( "No extra argument was expected" ), RPT_SEVERITY_ERROR );
        return false;
    }

    m_commentChar = d;

    m_continue = IBIS_PARSER_CONTINUE::NONE;

    return true;
}

std::string IbisParser::getKeyword()
{
    std::string keyword = "";
    //"Keywords must be enclosed in square brackets, “[]”, and must start in column 1 of the line."
    //"No space or tab is allowed immediately after the opening bracket “[” or immediately"
    // "before the closing bracket “]"

    if( m_buffer[m_lineOffset + m_lineIndex] != '[' )
    {
        // We return an empty keyword, this should stop the parser.
        return "";
    }

    m_lineIndex++;

    char c;
    c = m_buffer[m_lineOffset + m_lineIndex++];

    while( ( c != ']' )
           && ( m_lineIndex
                < m_lineLength ) ) // We know the maximum keyword length, we could add a condition.
    {
        // "Underscores and spaces are equivalent in keywords"
        if( c == ' ' )
            c = '_';

        keyword += c;
        c = m_buffer[m_lineOffset + m_lineIndex++];
    }

    return keyword;
}

bool IbisParser::changeContext( std::string& aKeyword )
{
    bool status = true;

    if( status )
    {
        switch( m_context )
        {
        case IBIS_PARSER_CONTEXT::HEADER: status &= m_ibisFile.m_header.Check(); break;
        case IBIS_PARSER_CONTEXT::COMPONENT: status &= m_currentComponent->Check(); break;
        case IBIS_PARSER_CONTEXT::MODEL: status &= m_currentModel->Check(); break;
        case IBIS_PARSER_CONTEXT::MODELSELECTOR: status &= m_currentModelSelector->Check(); break;
        case IBIS_PARSER_CONTEXT::PACKAGEMODEL: status &= m_currentPackageModel->Check(); break;
        case IBIS_PARSER_CONTEXT::END:
            Report( "Cannot change context after [END]" );
            status = false;
            break;
        default:
            Report( "Changing context from an undefined context" );
        }
    }

    if( !compareIbisWord( aKeyword.c_str(), "End" ) && status )
    {
        //New context
        if( compareIbisWord( aKeyword.c_str(), "Component" ) )
        {
            m_ibisFile.m_components.push_back( IbisComponent( m_Reporter ) );
            m_currentComponent = &( m_ibisFile.m_components.back() );
            status &= storeString( m_currentComponent->m_name, false );
            m_context = IBIS_PARSER_CONTEXT::COMPONENT;
        }
        else if( compareIbisWord( aKeyword.c_str(), "Model_Selector" ) )
        {
            IbisModelSelector MS( m_Reporter );
            status &= storeString( MS.m_name, false );
            m_ibisFile.m_modelSelectors.push_back( MS );
            m_currentModelSelector = &( m_ibisFile.m_modelSelectors.back() );
            m_context = IBIS_PARSER_CONTEXT::MODELSELECTOR;
            m_continue = IBIS_PARSER_CONTINUE::MODELSELECTOR;
        }
        else if( compareIbisWord( aKeyword.c_str(), "Model" ) )
        {
            IbisModel model( m_Reporter );
            model.m_temperatureRange.value[IBIS_CORNER::MIN] = 0;
            model.m_temperatureRange.value[IBIS_CORNER::TYP] = 50;
            model.m_temperatureRange.value[IBIS_CORNER::MAX] = 100;
            status &= storeString( model.m_name, false );
            m_ibisFile.m_models.push_back( model );
            m_currentModel = &( m_ibisFile.m_models.back() );
            m_context = IBIS_PARSER_CONTEXT::MODEL;
            m_continue = IBIS_PARSER_CONTINUE::MODEL;
        }
        else if( compareIbisWord( aKeyword.c_str(), "Define_Package_Model" ) )
        {
            IbisPackageModel PM( m_Reporter );
            PM.m_resistanceMatrix = std::unique_ptr<IBIS_MATRIX>( new IBIS_MATRIX( m_Reporter ) );
            PM.m_capacitanceMatrix = std::unique_ptr<IBIS_MATRIX>( new IBIS_MATRIX( m_Reporter ) );
            PM.m_inductanceMatrix = std::unique_ptr<IBIS_MATRIX>( new IBIS_MATRIX( m_Reporter ) );

            PM.m_resistanceMatrix->m_type = IBIS_MATRIX_TYPE::UNDEFINED;
            PM.m_capacitanceMatrix->m_type = IBIS_MATRIX_TYPE::UNDEFINED;
            PM.m_inductanceMatrix->m_type = IBIS_MATRIX_TYPE::UNDEFINED;

            PM.m_resistanceMatrix->m_dim = -1;
            PM.m_capacitanceMatrix->m_dim = -1;
            PM.m_inductanceMatrix->m_dim = -1;

            status &= storeString( PM.m_name, false );

            m_ibisFile.m_packageModels.push_back( PM );
            m_currentPackageModel = &( m_ibisFile.m_packageModels.back() );
            m_context = IBIS_PARSER_CONTEXT::PACKAGEMODEL;
        }
        else if( compareIbisWord( aKeyword.c_str(), "End_Package_Model" ) )
        {
            if( m_currentComponent != nullptr )
            {
                m_context = IBIS_PARSER_CONTEXT::COMPONENT;
                m_continue = IBIS_PARSER_CONTINUE::NONE;
            }
            else // .pkg file, we just go back to header, to get the [END] keyword
            {    // This will cause the header to be checked twice.
                m_context = IBIS_PARSER_CONTEXT::HEADER;
                m_continue = IBIS_PARSER_CONTINUE::NONE;
            }
        }
        else
        {
            status = false;
            std::string context_string;

            switch( m_context )
            {
            case IBIS_PARSER_CONTEXT::HEADER:                 context_string += "HEADER";                   break;
            case IBIS_PARSER_CONTEXT::COMPONENT:              context_string += "COMPONENT";                break;
            case IBIS_PARSER_CONTEXT::MODELSELECTOR:          context_string += "MODEL_SELECTOR";           break;
            case IBIS_PARSER_CONTEXT::MODEL:                  context_string += "MODEL";                    break;
            case IBIS_PARSER_CONTEXT::PACKAGEMODEL:           context_string += "PACKAGE_MODEL";            break;
            case IBIS_PARSER_CONTEXT::PACKAGEMODEL_MODELDATA: context_string += "PACKAGE_MODEL_MODEL_DATA"; break;
            default:                                          context_string += "???";                      break;
            }

            std::stringstream message;
            message << _( "Unknown keyword in " ) << context_string << _( " context: " ) << aKeyword;
            Report( message.str(), RPT_SEVERITY_ERROR );
        }
    }
    else
    {
        m_context = IBIS_PARSER_CONTEXT::END;
    }

    return status;
}


bool IbisParser::parseModelSelector( std::string& aKeyword )
{
    bool status = true;

    if( !changeContext( aKeyword ) )
        status = false;

    return status;
}


bool IbisParser::readRampdvdt( dvdtTypMinMax& aDest )
{
    bool        status = true;
    std::string str;

    if( readWord( str ) )
    {
        status &= readDvdt( str, aDest.value[IBIS_CORNER::TYP] )
                  && readDvdt( str, aDest.value[IBIS_CORNER::MIN] )
                  && readDvdt( str, aDest.value[IBIS_CORNER::MAX] );
    }
    else
    {
        status = false;
    }

    return status;
}

bool IbisParser::readRamp()
{
    bool status = true;
    m_continue = IBIS_PARSER_CONTINUE::RAMP;

    if( !readNumericSubparam( std::string( "R_load" ), m_currentModel->m_ramp.m_Rload ) )
    {
        std::string str;

        if( readWord( str ) )
        {
            if( !strcmp( str.c_str(), "dV/dt_r" ) )
            {
                status &= readRampdvdt( m_currentModel->m_ramp.m_rising );
            }
            else if( !strcmp( str.c_str(), "dV/dt_f" ) )
            {
                status &= readRampdvdt( m_currentModel->m_ramp.m_falling );
            }
            else
            {
                Report( _( "Invalid ramp data" ), RPT_SEVERITY_ERROR );
                status = false;
            }
        }
    }

    return status;
}


bool IbisParser::readModelSpec()
{
    bool status = true;

    m_continue = IBIS_PARSER_CONTINUE::MODEL_SPEC;

    // TODO
    // readTypMinMaxValueSubparam( std::string( "Vmeas" ), m_currentModel->... )
    // ...

    return status;
}


bool IbisParser::parseModel( std::string& aKeyword )
{
    bool status = false;

    if( compareIbisWord( aKeyword.c_str(), "Voltage_Range" ) )
        status = readTypMinMaxValue( m_currentModel->m_voltageRange );
    else if( compareIbisWord( aKeyword.c_str(), "Temperature_Range" ) )
        status = readTypMinMaxValue( m_currentModel->m_temperatureRange );
    else if( compareIbisWord( aKeyword.c_str(), "GND_Clamp" ) )
        status = readIVtableEntry( m_currentModel->m_GNDClamp );
    else if( compareIbisWord( aKeyword.c_str(), "POWER_Clamp" ) )
        status = readIVtableEntry( m_currentModel->m_POWERClamp );
    else if( compareIbisWord( aKeyword.c_str(), "Pulldown" ) )
        status = readIVtableEntry( m_currentModel->m_pulldown );
    else if( compareIbisWord( aKeyword.c_str(), "Pullup" ) )
        status = readIVtableEntry( m_currentModel->m_pullup );
    else if( compareIbisWord( aKeyword.c_str(), "Rising_Waveform" ) )
        status = readWaveform( nullptr, IBIS_WAVEFORM_TYPE::RISING );
    else if( compareIbisWord( aKeyword.c_str(), "Falling_Waveform" ) )
        status = readWaveform( nullptr, IBIS_WAVEFORM_TYPE::FALLING );
    else if( compareIbisWord( aKeyword.c_str(), "Ramp" ) )
        status = readRamp();
    else if( compareIbisWord( aKeyword.c_str(), "Model_Spec" ) )
        status = readModelSpec();
    else if( compareIbisWord( aKeyword.c_str(), "Pullup_Reference" ) )
        status = readTypMinMaxValue( m_currentModel->m_pullupReference );
    else if( compareIbisWord( aKeyword.c_str(), "Pulldown_Reference" ) )
        status = readTypMinMaxValue( m_currentModel->m_pulldownReference );
    else if( compareIbisWord( aKeyword.c_str(), "POWER_Clamp_Reference" ) )
        status = readTypMinMaxValue( m_currentModel->m_POWERClampReference );
    else if( compareIbisWord( aKeyword.c_str(), "GND_Clamp_Reference" ) )
        status = readTypMinMaxValue( m_currentModel->m_GNDClampReference );
    else if( compareIbisWord( aKeyword.c_str(), "Rac" ) )
        status = readTypMinMaxValue( m_currentModel->m_Rac );
    else if( compareIbisWord( aKeyword.c_str(), "Cac" ) )
        status = readTypMinMaxValue( m_currentModel->m_Cac );
    else if( compareIbisWord( aKeyword.c_str(), "Rpower" ) )
        status = readTypMinMaxValue( m_currentModel->m_Rpower );
    else if( compareIbisWord( aKeyword.c_str(), "Rgnd" ) )
        status = readTypMinMaxValue( m_currentModel->m_Rgnd );
    else
        status = changeContext( aKeyword );

    return status;
}

bool IbisParser::readPackageModelPins()
{
    m_continue = IBIS_PARSER_CONTINUE::PACKAGEMODEL_PINS;
    std::string str;

    if( readWord( str ) )
        m_currentPackageModel->m_pins.push_back( str );

    return true;
}


bool IbisParser::readMatrixBanded( std::string aKeyword, IBIS_MATRIX_BANDED& aDest )
{
    bool status = true;
    m_continue = IBIS_PARSER_CONTINUE::MATRIX;

    if( compareIbisWord( aKeyword.c_str(), "Bandwidth" ) )
    {
        if( m_currentMatrix->m_type == IBIS_MATRIX_TYPE::BANDED )
        {
            status &= readInt( aDest.m_bandwidth );

            if( status )
                aDest.m_data.resize( aDest.m_bandwidth * aDest.m_dim );
        }
        else
        {
            status = false;
            Report( _( "Cannot specify a bandwidth for that kind of matrix" ), RPT_SEVERITY_ERROR );
        }
    }
    if( !compareIbisWord( aKeyword.c_str(), "Dummy" ) )
    {
        int i;

        for( i = 0; i < aDest.m_bandwidth; i++ )
        {
            if( i + m_currentMatrixRowIndex >= aDest.m_bandwidth )
            {
                Report( "Too much data for this matrix row", RPT_SEVERITY_ERROR );
                status = false;
                break;
            }

            int index = i + m_currentMatrixRow * aDest.m_bandwidth;

            if( !readDouble( aDest.m_data[index] ) )
            {
                Report( _( "Invalid row in matrix" ), RPT_SEVERITY_ERROR );
                status = false;
                break;
            }
        }

        m_currentMatrixRowIndex = i;
    }

    return status;
}


bool IbisParser::readMatrixFull( std::string aKeyword, IBIS_MATRIX_FULL& aDest )
{
    bool status = true;
    m_continue = IBIS_PARSER_CONTINUE::MATRIX;

    if( !compareIbisWord( aKeyword.c_str(), "Dummy" ) )
    {
        std::vector<std::string> values;

        status &= readTableLine( values );
        int i;

        for( i = 0; i < (int)values.size(); i++ )
        {
            int index = i + m_currentMatrixRow * aDest.m_dim + m_currentMatrixRow;
            // + final m_currentMatrixRow because we don't fill the lower triangle.

            if( i >= ( aDest.m_dim - m_currentMatrixRow - m_currentMatrixRowIndex ) )
            {
                Report( _( "Too much data for this matrix row." ), RPT_SEVERITY_ERROR );
                status = false;
                break;
            }

            if( index >= aDest.m_dim * aDest.m_dim )
            {
                status = false;
                Report( _( "Too much data for this matrix." ), RPT_SEVERITY_ERROR );
                break;
            }

            if( !parseDouble( aDest.m_data[index], values.at( i ), true ) )
            {
                Report( _( "Can't read a matrix element" ), RPT_SEVERITY_ERROR );
                status = false;
            }
        }

        m_currentMatrixRowIndex = i;
    }

    return status;
}


bool IbisParser::readMatrixSparse( std::string aKeyword, IBIS_MATRIX_SPARSE& aDest )
{
    bool status = true;

    if( !compareIbisWord( aKeyword.c_str(), "Dummy" ) )
    {
        int    subindex;
        double value;

        if( readInt( subindex ) )
        {
            if( readDouble( value ) )
            {
#if 0           // Currently not used
                int index = subindex + m_currentMatrixRow * aDest.m_dim + m_currentMatrixRow;
#endif
            }
            else
            {
                Report( _( "Can't read a matrix element" ), RPT_SEVERITY_ERROR );
            }
        }
        else
        {
            Report( _( "Can't read a matrix index" ), RPT_SEVERITY_ERROR );
        }
    }

    return status;
}

bool IbisParser::readMatrix( std::shared_ptr<IBIS_MATRIX> aDest )
{
    bool        status = true;
    std::string str;
    bool        init = false;

    if( aDest != nullptr )
    {
        if( aDest->m_type != IBIS_MATRIX_TYPE::BANDED && aDest->m_type != IBIS_MATRIX_TYPE::FULL
            && aDest->m_type != IBIS_MATRIX_TYPE::SPARSE )
        {
            init = false;
        }
        else
        {
            init = true;
        }
    }
    else
    {
        Report( "Matrix pointer is null." );
        status = false;
    }

    if( m_continue != IBIS_PARSER_CONTINUE::MATRIX && status )
    {
        if( !init )
        {
            if( readWord( str ) )
            {
                IBIS_MATRIX* matrix;

                if( compareIbisWord( str.c_str(), "Banded_matrix" ) )
                {
                    matrix = static_cast<IBIS_MATRIX*>( new IBIS_MATRIX_BANDED( m_Reporter ) );
                    aDest = static_cast<std::shared_ptr<IBIS_MATRIX>>( matrix );
                    m_currentMatrix = aDest;
                    m_currentMatrix->m_type = IBIS_MATRIX_TYPE::BANDED;
                    m_continue = IBIS_PARSER_CONTINUE::MATRIX;
                }
                else if( compareIbisWord( str.c_str(), "Full_matrix" ) )
                {
                    matrix = static_cast<IBIS_MATRIX*>( new IBIS_MATRIX_FULL( m_Reporter ) );
                    aDest = static_cast<std::shared_ptr<IBIS_MATRIX>>( matrix );
                    m_currentMatrix = aDest;
                    matrix->m_dim = m_currentPackageModel->m_numberOfPins;
                    m_currentMatrix->m_type = IBIS_MATRIX_TYPE::FULL;
                    m_continue = IBIS_PARSER_CONTINUE::MATRIX;
                }
                else if( compareIbisWord( str.c_str(), "Sparse_matrix" ) )
                {
                    matrix = static_cast<IBIS_MATRIX*>( new IBIS_MATRIX_SPARSE( m_Reporter ) );
                    aDest = static_cast<std::shared_ptr<IBIS_MATRIX>>( matrix );
                    m_currentMatrix = aDest;
                    m_currentMatrix->m_data.resize( matrix->m_dim * matrix->m_dim );
                    m_currentMatrix->m_type = IBIS_MATRIX_TYPE::SPARSE;
                    m_continue = IBIS_PARSER_CONTINUE::MATRIX;
                }
                else
                {
                    status = false;
                    Report( _( "Unknown matrix type" ), RPT_SEVERITY_ERROR );
                    Report( str, RPT_SEVERITY_INFO );
                    m_currentMatrix->m_dim = m_currentPackageModel->m_numberOfPins;
                }
            }
            else
            {
                status = false;
                Report( _( "Missing matrix type" ), RPT_SEVERITY_ERROR );
            }
        }
        else
        {
            status = false;
            Report( _( " Matrix is already init. But m_continue was not set ( internal error )" ) );
        }
    }
    else
    {
        if( aDest != nullptr )
        {
            // If m_continue is set, ( and no keyword ) then it is a row
            switch( aDest->m_type )
            {
            case IBIS_MATRIX_TYPE::BANDED:
                readMatrixBanded( std::string( "Dummy" ),
                                  *static_cast<IBIS_MATRIX_BANDED*>( aDest.get() ) );
                break;
            case IBIS_MATRIX_TYPE::FULL:
                readMatrixFull( std::string( "Dummy" ),
                                *static_cast<IBIS_MATRIX_FULL*>( aDest.get() ) );
                break;
            case IBIS_MATRIX_TYPE::SPARSE:
                readMatrixSparse( std::string( "Dummy" ),
                                  *static_cast<IBIS_MATRIX_SPARSE*>( aDest.get() ) );
                break;
            case IBIS_MATRIX_TYPE::UNDEFINED:
            default:
                status = false;
                Report( _( "Tried to read a row from an undefined matrix" ) );
            }
        }
        else
        {
            Report( _( "matrix pointer is null" ) );
        }
    }
    return status;
}

bool IbisParser::parsePackageModelModelData( std::string& aKeyword )
{
    bool status = true;

    if( compareIbisWord( aKeyword.c_str(), "Resistance_Matrix" ) )
    {
        IBIS_MATRIX dest( m_Reporter );
        IBIS_MATRIX source( m_Reporter );

        status &= readMatrix( m_currentPackageModel->m_resistanceMatrix );
    }
    else if( compareIbisWord( aKeyword.c_str(), "Capacitance_Matrix" ) )
    {
        status &= readMatrix( m_currentPackageModel->m_capacitanceMatrix );
    }
    else if( compareIbisWord( aKeyword.c_str(), "Inductance_Matrix" ) )
    {
        status &= readMatrix( m_currentPackageModel->m_inductanceMatrix );
    }
    else if( compareIbisWord( aKeyword.c_str(), "Bandwidth" ) )
    {
        status &= readMatrixBanded( aKeyword,
                                    *static_cast<IBIS_MATRIX_BANDED*>( m_currentMatrix.get() ) );
    }
    else if( compareIbisWord( aKeyword.c_str(), "Row" ) )
    {
        status &= readInt( m_currentMatrixRow );
        m_currentMatrixRow--;        // The matrix starts at 0
        m_currentMatrixRowIndex = 0; // The matrix starts at 0*/
        m_continue = IBIS_PARSER_CONTINUE::MATRIX;
    }
    else if( compareIbisWord( aKeyword.c_str(), "End_Model_Data" ) )
    {
        m_context = IBIS_PARSER_CONTEXT::PACKAGEMODEL;
        m_continue = IBIS_PARSER_CONTINUE::NONE;
    }
    else if( !changeContext( aKeyword ) )
    {
        status = false;
    }

    return status;
}

bool IbisParser::parsePackageModel( std::string& aKeyword )
{
    bool status = true;

    if( compareIbisWord( aKeyword.c_str(), "Manufacturer" ) )
        status &= storeString( m_currentPackageModel->m_manufacturer, false );
    else if( compareIbisWord( aKeyword.c_str(), "OEM" ) )
        status &= storeString( m_currentPackageModel->m_OEM, false );
    else if( compareIbisWord( aKeyword.c_str(), "Description" ) )
        status &= storeString( m_currentPackageModel->m_description, false );
    else if( compareIbisWord( aKeyword.c_str(), "Number_of_Pins" ) )
        status &= readInt( m_currentPackageModel->m_numberOfPins );
    else if( compareIbisWord( aKeyword.c_str(), "Pin_Numbers" ) )
        status &= readPackageModelPins();
    else if( compareIbisWord( aKeyword.c_str(), "Model_Data" ) )
    {
        m_context = IBIS_PARSER_CONTEXT::PACKAGEMODEL_MODELDATA;
        m_continue = IBIS_PARSER_CONTINUE::NONE;
    }
    else if( !changeContext( aKeyword ) )
    {
        status = false;
    }

    return status;
}

bool IbisParser::readModelSelector()
{
    bool status = true;

    IbisModelSelectorEntry model;

    if( !readWord( model.m_modelName ) )
        return false;

    status &= readString( model.m_modelDescription );
    m_currentModelSelector->m_models.push_back( model );

    return status;
}

bool IbisParser::readNumericSubparam( const std::string& aSubparam, double& aDest )
{
    std::string paramName;
    bool        status = true;

    if( aSubparam.size() >= (size_t)m_lineLength )
    {
        // Continuing would result in an overflow
        return false;
    }

    int old_index = m_lineIndex;
    m_lineIndex = 0;

    for( size_t i = 0; i < aSubparam.size(); i++ )
        paramName += m_buffer[m_lineOffset + m_lineIndex++];

    if( strcmp( paramName.c_str(), aSubparam.c_str() ) )
    {
        m_lineIndex = old_index;
        return false;
    }

    skipWhitespaces();

    status &= m_buffer[m_lineOffset + m_lineIndex++] == '=';

    if( status )
    {
        skipWhitespaces();
        status &= readDouble( aDest );
    }

    if( !status )
        m_lineIndex = old_index;

    return status;
}


bool IbisParser::readTypMinMaxValue( TypMinMaxValue& aDest )
{
    bool status = true;

    skipWhitespaces();

    std::string strValue;

    if( !readDouble( aDest.value[IBIS_CORNER::TYP] ) )
    {
        Report( _( "Typ-Min-Max Values requires at least Typ." ), RPT_SEVERITY_ERROR );
        return false;
    }

    readDouble( aDest.value[IBIS_CORNER::MIN] );
    readDouble( aDest.value[IBIS_CORNER::MAX] );

    return status;
}

bool IbisParser::readTypMinMaxValueSubparam( const std::string& aSubparam, TypMinMaxValue& aDest )
{
    std::string paramName;
    bool        status = true;

    m_lineIndex = 0; // rewind

    if( aSubparam.size() < (size_t)m_lineLength )
    {
        for( size_t i = 0; i < aSubparam.size(); i++ )
            paramName += m_buffer[m_lineOffset + m_lineIndex++];

        if( !strcmp( paramName.c_str(), aSubparam.c_str() ) )
            readTypMinMaxValue( aDest );
        else
            status = false;
    }
    else
    {
        status = false;
    }

    return status;
}

bool IbisParser::readModel()
{
    bool        status = true;
    std::string subparam;

    if( readWord( subparam ) )
    {
        switch( m_continue )
        {
        case IBIS_PARSER_CONTINUE::MODEL:

            if( !strcmp( subparam.substr( 0, 10 ).c_str(), "Model_type" ) )
            {
                if( readWord( subparam ) )
                {
                    if( compareIbisWord( subparam.c_str(), "Input" ) )
                        m_currentModel->m_type = IBIS_MODEL_TYPE::INPUT_STD;
                    else if( compareIbisWord( subparam.c_str(), "Output" ) )
                        m_currentModel->m_type = IBIS_MODEL_TYPE::OUTPUT;
                    else if( compareIbisWord( subparam.c_str(), "I/O" ) )
                        m_currentModel->m_type = IBIS_MODEL_TYPE::IO;
                    else if( compareIbisWord( subparam.c_str(), "3-state" ) )
                        m_currentModel->m_type = IBIS_MODEL_TYPE::THREE_STATE;
                    else if( compareIbisWord( subparam.c_str(), "Open_drain" ) )
                        m_currentModel->m_type = IBIS_MODEL_TYPE::OPEN_DRAIN;
                    else if( compareIbisWord( subparam.c_str(), "I/O_Open_drain" ) )
                        m_currentModel->m_type = IBIS_MODEL_TYPE::IO_OPEN_DRAIN;
                    else if( compareIbisWord( subparam.c_str(), "Open_sink" ) )
                        m_currentModel->m_type = IBIS_MODEL_TYPE::OPEN_SINK;
                    else if( compareIbisWord( subparam.c_str(), "I/O_open_sink" ) )
                        m_currentModel->m_type = IBIS_MODEL_TYPE::IO_OPEN_SINK;
                    else if( compareIbisWord( subparam.c_str(), "Open_source" ) )
                        m_currentModel->m_type = IBIS_MODEL_TYPE::OPEN_SOURCE;
                    else if( compareIbisWord( subparam.c_str(), "I/O_open_source" ) )
                        m_currentModel->m_type = IBIS_MODEL_TYPE::IO_OPEN_SOURCE;
                    else if( compareIbisWord( subparam.c_str(), "Input_ECL" ) )
                        m_currentModel->m_type = IBIS_MODEL_TYPE::INPUT_ECL;
                    else if( compareIbisWord( subparam.c_str(), "Output_ECL" ) )
                        m_currentModel->m_type = IBIS_MODEL_TYPE::OUTPUT_ECL;
                    else if( compareIbisWord( subparam.c_str(), "I/O_ECL" ) )
                        m_currentModel->m_type = IBIS_MODEL_TYPE::IO_ECL;
                    else if( compareIbisWord( subparam.c_str(), "3-state_ECL" ) )
                        m_currentModel->m_type = IBIS_MODEL_TYPE::THREE_STATE_ECL;
                    else if( compareIbisWord( subparam.c_str(), "Terminator" ) )
                        m_currentModel->m_type = IBIS_MODEL_TYPE::TERMINATOR;
                    else if( compareIbisWord( subparam.c_str(), "Series" ) )
                        m_currentModel->m_type = IBIS_MODEL_TYPE::SERIES;
                    else if( compareIbisWord( subparam.c_str(), "Series_switch" ) )
                        m_currentModel->m_type = IBIS_MODEL_TYPE::SERIES_SWITCH;
                    else
                    {
                        std::stringstream message;
                        message << _( "Unknown Model_type " ) << subparam;
                        Report( message.str(), RPT_SEVERITY_ERROR );
                        status = false;
                    }
                }
                else
                {
                    Report( _( "Internal Error while reading model_type" ), RPT_SEVERITY_ERROR );
                    status = false;
                }
            }
            else if( !strcmp( subparam.substr( 0, 7 ).c_str(), "Enable" ) )
            {
                if( readWord( subparam ) )
                {
                    if( !strcmp( subparam.c_str(), "Active-High" ) )
                        m_currentModel->m_enable = IBIS_MODEL_ENABLE::ACTIVE_HIGH;
                    else if( !strcmp( subparam.c_str(), "Active-Low" ) )
                        m_currentModel->m_enable = IBIS_MODEL_ENABLE::ACTIVE_LOW;
                    else
                    {
                        std::stringstream message;
                        message << _( "Unknown Enable: " ) << subparam;
                        Report( message.str(), RPT_SEVERITY_ERROR );
                        status = false;
                    }
                }
                else
                {
                    Report( _( "Internal Error while reading Enable" ), RPT_SEVERITY_ERROR );
                    status = false;
                }
            }
            else if( subparam.substr( 0, 9 ) == "Polarity" )
            {
                if( readWord( subparam ) )
                {
                    if( subparam == "Inverting" )
                        m_currentModel->m_polarity = IBIS_MODEL_POLARITY::INVERTING;
                    else if( subparam == "Non-Inverting" )
                        m_currentModel->m_polarity = IBIS_MODEL_POLARITY::NON_INVERTING;
                    else
                    {
                        std::stringstream message;
                        message << _( "Unknown polarity " ) << subparam;
                        Report( message.str(), RPT_SEVERITY_ERROR );
                        status = false;
                    }
                }
                else
                {
                    Report( _( "Internal Error while reading Enable" ), RPT_SEVERITY_ERROR );
                    status = false;
                }
            }
            else if( readNumericSubparam( std::string( "Vinl" ), m_currentModel->m_vinl ) )
                ;
            else if( readNumericSubparam( std::string( "Vinh" ), m_currentModel->m_vinh ) )
                ;
            else if( readNumericSubparam( std::string( "Vref" ), m_currentModel->m_vref ) )
                ;
            else if( readNumericSubparam( std::string( "Rref" ), m_currentModel->m_rref ) )
                ;
            else if( readNumericSubparam( std::string( "Cref" ), m_currentModel->m_cref ) )
                ;
            else if( readNumericSubparam( std::string( "Vmeas" ), m_currentModel->m_vmeas ) )
                ;
            else if( readTypMinMaxValueSubparam( std::string( "C_comp" ), m_currentModel->m_C_comp ) )
                ;
            else
                status = false;

            m_continue = IBIS_PARSER_CONTINUE::MODEL;

            break;

        default:
            status = false;
            Report( _( "Continued reading a model that did not begin. ( internal error )" ),
                    RPT_SEVERITY_ERROR );
        }
    }

    return status;
}


bool IbisParser::parseHeader( std::string& aKeyword )
{
    bool status = true;

    if( compareIbisWord( aKeyword.c_str(), "IBIS_Ver" ) )
    {
        status &= readDouble( m_ibisFile.m_header.m_ibisVersion );
    }
    else if( compareIbisWord( aKeyword.c_str(), "Comment_char" ) )
    {
        status &= changeCommentChar();
    }
    else if( compareIbisWord( aKeyword.c_str(), "File_Name" ) )
    {
        status &= storeString( m_ibisFile.m_header.m_fileName, false );
    }
    else if( compareIbisWord( aKeyword.c_str(), "File_Rev" ) )
    {
        status &= readDouble( m_ibisFile.m_header.m_fileRevision );
    }
    else if( compareIbisWord( aKeyword.c_str(), "Source" ) )
    {
        status &= storeString( m_ibisFile.m_header.m_source, true );
    }
    else if( compareIbisWord( aKeyword.c_str(), "Notes" ) )
    {
        status &= storeString( m_ibisFile.m_header.m_notes, true );
    }
    else if( compareIbisWord( aKeyword.c_str(), "Disclaimer" ) )
    {
        status &= storeString( m_ibisFile.m_header.m_disclaimer, true );
    }
    else if( compareIbisWord( aKeyword.c_str(), "Copyright" ) )
    {
        status &= storeString( m_ibisFile.m_header.m_copyright, true );
    }
    else if( compareIbisWord( aKeyword.c_str(), "Date" ) )
    {
        status &= storeString( m_ibisFile.m_header.m_date, false );
    }
    else if( !changeContext( aKeyword ) )
    {
        status = false;
    }

    return status;
}


bool IbisParser::parseComponent( std::string& aKeyword )
{
    bool status = true;

    if( compareIbisWord( aKeyword.c_str(), "Manufacturer" ) )
    {
        status &= storeString( m_currentComponent->m_manufacturer, true );
    }
    else if( compareIbisWord( aKeyword.c_str(), "Package" ) )
    {
        status &= readPackage();
    }
    else if( compareIbisWord( aKeyword.c_str(), "Pin" ) )
    {
        status &= readPin();
    }
    else if( compareIbisWord( aKeyword.c_str(), "Pin_Mapping" ) )
    {
        status &= readPinMapping();
    }
    else if( compareIbisWord( aKeyword.c_str(), "Diff_Pin" ) )
    {
        status &= readDiffPin();
    }
    /*
    // Not supported yet
    else if( aKeyword == "Die_Supply_Pads" )
    {
        status &= ReadDieSupplyPads();
    }*/
    else if( compareIbisWord( aKeyword.c_str(), "Package_Model" ) )
    {
        status &= storeString( m_currentComponent->m_packageModel, true );
    }
    else if( !changeContext( aKeyword ) )
    {
        status = false;
    }

    return status;
}

bool IbisParser::readTableLine( std::vector<std::string>& aDest )
{
    aDest.clear();

    while( m_lineIndex < m_lineLength )
    {
        std::string str;

        while( !isspace( m_buffer[m_lineOffset + m_lineIndex] ) && m_lineIndex < m_lineLength )
            str += m_buffer[m_lineOffset + m_lineIndex++];

        if( str.size() > 0 )
            aDest.push_back( str );

        while( isspace( m_buffer[m_lineOffset + m_lineIndex] ) && m_lineIndex < m_lineLength )
            m_lineIndex++;
    }

    return true;
}

bool IbisParser::readPackage()
{
    bool status = true;

    std::vector<std::string> fields;

    TypMinMaxValue* R = &( m_currentComponent->m_package.m_Rpkg );
    TypMinMaxValue* L = &( m_currentComponent->m_package.m_Lpkg );
    TypMinMaxValue* C = &( m_currentComponent->m_package.m_Cpkg );

    readTableLine( fields );

    int extraArg = ( m_continue == IBIS_PARSER_CONTINUE::NONE ) ? 1 : 0;

    if( (int)fields.size() == ( 4 + extraArg ) )
    {
        TypMinMaxValue* cValue;

        if( fields.at( 0 ) == "R_pkg" )
            cValue = R;
        else if( fields.at( 0 ) == "L_pkg" )
            cValue = L;
        else if( fields.at( 0 ) == "C_pkg" )
            cValue = C;
        else
        {
            Report( "Invalid field in [Package]" );
            return false;
        }
        status &= parseDouble( cValue->value[IBIS_CORNER::TYP], fields.at( 1 ), true );
        // Min / max values are optional, so don't update the status
        parseDouble( cValue->value[IBIS_CORNER::MIN], fields.at( 2 ), true );
        parseDouble( cValue->value[IBIS_CORNER::MAX], fields.at( 3 ), true );
    }
    else
    {
        if( fields.size() != 0 )
        {
            Report( _( "A [Package] line requires exactly 4 elements." ), RPT_SEVERITY_ERROR );
            status = false;
        }
    }

    m_continue = IBIS_PARSER_CONTINUE::COMPONENT_PACKAGE;

    return status;
}


bool IbisParser::readPin()
{
    bool status = true;

    std::vector<std::string> fields;

    m_lineIndex = 0;
    status &= readTableLine( fields );

    IbisComponentPin pin( m_Reporter );

    if( ( fields.size() == 3 ) )
    {
        if( m_continue == IBIS_PARSER_CONTINUE::COMPONENT_PIN ) // No info on first line
        {
            pin.m_pinName = fields.at( 0 );
            pin.m_signalName = fields.at( 1 );
            pin.m_modelName = fields.at( 2 );
            pin.m_Rcol = m_currentComponent->m_pins.back().m_Rcol;
            pin.m_Lcol = m_currentComponent->m_pins.back().m_Lcol;
            pin.m_Ccol = m_currentComponent->m_pins.back().m_Ccol;

            m_currentComponent->m_pins.push_back( pin );
        }
        else
        {
            pin.m_dummy = true;
        }
    }
    else
    {
        if( m_continue == IBIS_PARSER_CONTINUE::COMPONENT_PIN ) // Not on the first line
        {
            pin.m_pinName = fields.at( 0 );
            pin.m_signalName = fields.at( 1 );
            pin.m_modelName = fields.at( 2 );

            pin.m_Rcol = m_currentComponent->m_pins.back().m_Rcol;
            pin.m_Lcol = m_currentComponent->m_pins.back().m_Lcol;
            pin.m_Ccol = m_currentComponent->m_pins.back().m_Ccol;

            if( pin.m_Rcol == 0 || pin.m_Lcol == 0 || pin.m_Ccol == 0 )
            {
                Report( _( "Invalid pin entry: 6 values from a table with only 3." ),
                        RPT_SEVERITY_ERROR );
                status = false; // Did we just try to go from a 3 column table to a 6 ?
            }
            else
            {
                if( !parseDouble( pin.m_Rpin, fields.at( pin.m_Rcol ), true )
                    || !parseDouble( pin.m_Lpin, fields.at( pin.m_Lcol ), true )
                    || !parseDouble( pin.m_Cpin, fields.at( pin.m_Ccol ), true ) )
                {
                    Report( _( "Can't read a R, L or C value for a pin." ), RPT_SEVERITY_ERROR );
                    status = false;
                }
            }
        }
        else
        {
            for( int i = 3; i < 6; i++ )
            {
                if( fields.at( i ) == "R_pin" )
                {
                    pin.m_Rcol = i;
                }
                else if( fields.at( i ) == "L_pin" )
                {
                    pin.m_Lcol = i;
                }
                else if( fields.at( i ) == "C_pin" )
                {
                    pin.m_Ccol = i;
                }
                else
                {
                    Report( _( "Invalid field name in [Pin]" ), RPT_SEVERITY_ERROR );
                    status = false;
                }
            }

            if( pin.m_Rcol == 0 || pin.m_Lcol == 0 || pin.m_Ccol == 0 )
            {
                Report( _( "Missing argument in [Pin]" ), RPT_SEVERITY_ERROR );
                status = false;
            }

            pin.m_dummy = true;
        }
    }

    m_currentComponent->m_pins.push_back( pin );
    m_continue = IBIS_PARSER_CONTINUE::COMPONENT_PIN;


    return status;
}


bool IbisParser::readPinMapping()
{
    bool status = true;

    std::vector<std::string> fields;

    status &= readTableLine( fields );

    IbisComponentPinMapping pinMapping( m_Reporter );

    if( m_continue == IBIS_PARSER_CONTINUE::NONE ) // No info on first line
    {
        m_continue = IBIS_PARSER_CONTINUE::COMPONENT_PINMAPPING;
    }
    else
    {
        if( fields.size() != 0 )
        {
            if( fields.size() > 6 || fields.size() < 3 )
            {
                Report( _( "Wrong number of columns for pin mapping." ), RPT_SEVERITY_ERROR );
                status = false;
            }
            else
            {
                pinMapping.m_pinName = fields.at( 0 );
                pinMapping.m_PDref = fields.at( 1 );
                pinMapping.m_PUref = fields.at( 2 );

                if( fields.size() > 3 )
                    pinMapping.m_GNDClampRef = fields.at( 3 );

                if( fields.size() > 4 )
                    pinMapping.m_POWERClampRef = fields.at( 4 );

                if( fields.size() > 5 )
                    pinMapping.m_extRef = fields.at( 5 );
            }
            m_currentComponent->m_pinMappings.push_back( pinMapping );
        }
    }

    return status;
}


bool IbisParser::readDiffPin()
{
    bool status = true;

    m_lineIndex = 0; // rewind
    IbisDiffPinEntry entry( m_Reporter );

    if( m_continue == IBIS_PARSER_CONTINUE::NONE ) // No info on first line
    {
        m_continue = IBIS_PARSER_CONTINUE::COMPONENT_DIFFPIN;
    }
    else
    {
        if( !readWord( entry.pinA ) )
        {
            Report( _( "Incorrect diff pin name" ), RPT_SEVERITY_ERROR );
            status = false;
        }

        if( !readWord( entry.pinB ) )
        {
            Report( _( "Incorrect inv_pin name" ), RPT_SEVERITY_ERROR );
            status = false;
        }

        if( status )
            m_currentComponent->m_diffPin.m_entries.push_back( entry );
    }

    return status;
}


bool IbisParser::readIVtableEntry( IVtable& aDest )
{
    bool status = true;

    skipWhitespaces();

    IVtableEntry entry( m_Reporter );

    if( m_lineIndex < m_lineLength )
    {
        std::string str;

        status &= readDouble( entry.V );

        if( status && readTypMinMaxValue( entry.I ) )
            aDest.m_entries.push_back( entry );
    }

    m_continue = IBIS_PARSER_CONTINUE::IV_TABLE;
    m_currentIVtable = &aDest;

    return status;
}

bool IbisParser::readVTtableEntry( VTtable& aDest )
{
    bool status = true;
    skipWhitespaces();

    VTtableEntry entry( m_Reporter );

    if( m_lineIndex < m_lineLength )
    {
        std::string str;
        status &= readDouble( entry.t );
        status &= readTypMinMaxValue( entry.V );
    }

    m_continue = IBIS_PARSER_CONTINUE::IV_TABLE;
    m_currentVTtable = &aDest;

    if( status )
        aDest.m_entries.push_back( entry );

    return status;
}

bool IbisParser::readWaveform( IbisWaveform* aDest, IBIS_WAVEFORM_TYPE aType )
{
    bool status = true;

    IbisWaveform* wf;

    if( m_continue != IBIS_PARSER_CONTINUE::WAVEFORM )
    {
        wf = new IbisWaveform( m_Reporter );
        wf->m_type = aType;

        switch( aType )
        {
        case IBIS_WAVEFORM_TYPE::FALLING:
            m_currentModel->m_fallingWaveforms.push_back( wf );
            break;
        case IBIS_WAVEFORM_TYPE::RISING:
            m_currentModel->m_risingWaveforms.push_back( wf );
            break;
        default:
            Report( _( "Unknown waveform type" ), RPT_SEVERITY_ERROR );
            status = false;
        }
    }
    else
    {
        if( aDest != nullptr )
        {
            wf = aDest;
        }
        else
        {
            Report( _( "Internal error detected, a waveform should exist" ), RPT_SEVERITY_ERROR );
            return false;
        }
    }


    if( status & !isLineEmptyFromCursor() )
    {
        // readNumericSubparam() returns true if it could read the subparameter and store it
        // Testing all subparameters
        if( readNumericSubparam( std::string( "R_fixture" ), wf->m_R_fixture ) )
            ;
        else if( readNumericSubparam( std::string( "L_fixture" ), wf->m_L_fixture ) )
            ;
        else if( readNumericSubparam( std::string( "C_fixture" ), wf->m_C_fixture ) )
            ;
        else if( readNumericSubparam( std::string( "V_fixture" ), wf->m_V_fixture ) )
            ;
        else if( readNumericSubparam( std::string( "V_fixture_min" ), wf->m_V_fixture_min ) )
            ;
        else if( readNumericSubparam( std::string( "V_fixture_max" ), wf->m_V_fixture_max ) )
            ;
        else if( readNumericSubparam( std::string( "R_dut" ), wf->m_R_fixture ) )
            ;
        else if( readNumericSubparam( std::string( "L_dut" ), wf->m_L_fixture ) )
            ;
        else if( readNumericSubparam( std::string( "C_dut" ), wf->m_C_fixture ) )
            ;
        // The line is not a subparameter, then let's try to read a VT table entry
        else if( !readVTtableEntry( m_currentWaveform->m_table ) )
            status = false;
    }

    m_currentWaveform = wf;
    m_continue = IBIS_PARSER_CONTINUE::WAVEFORM;
    return status;
}

bool IbisParser::onNewLine()
{
    bool      status = true;
    char      c;
    std::string keyword = getKeyword();

    if( keyword.size() > 0 ) // New keyword
    {
        if( m_continue != IBIS_PARSER_CONTINUE::NONE )
            m_continue = IBIS_PARSER_CONTINUE::NONE;

        switch( m_context )
        {
        case IBIS_PARSER_CONTEXT::HEADER:
            status &= parseHeader( keyword );
            break;
        case IBIS_PARSER_CONTEXT::COMPONENT:
            status &= parseComponent( keyword );
            break;
        case IBIS_PARSER_CONTEXT::MODELSELECTOR:
            status &= parseModelSelector( keyword );
            break;
        case IBIS_PARSER_CONTEXT::MODEL:
            status &= parseModel( keyword );
            break;
        case IBIS_PARSER_CONTEXT::PACKAGEMODEL:
            status &= parsePackageModel( keyword );
            break;
        case IBIS_PARSER_CONTEXT::PACKAGEMODEL_MODELDATA:
            status &= parsePackageModelModelData( keyword );
            break;
        default:
            status = false;
            Report( _( "Internal error: Bad parser context." ), RPT_SEVERITY_ERROR );
        }
    }
    else
    {
        skipWhitespaces();
        if( m_lineIndex == m_lineLength )
        {
            // That was an empty line
            return true;
        }

        // No new keyword ? Then it is the continuation of the previous one !
        switch( m_continue )
        {
        case IBIS_PARSER_CONTINUE::STRING:
            skipWhitespaces();
            *m_continuingString += '\n';
            status &= readString( *m_continuingString );
            break;
        case IBIS_PARSER_CONTINUE::COMPONENT_PACKAGE:
            status &= readPackage();
            break;
        case IBIS_PARSER_CONTINUE::COMPONENT_PIN:
            status &= readPin();
            break;
        case IBIS_PARSER_CONTINUE::COMPONENT_PINMAPPING:
            status &= readPinMapping();
            break;
        case IBIS_PARSER_CONTINUE::COMPONENT_DIFFPIN:
            status &= readDiffPin();
            break;
        case IBIS_PARSER_CONTINUE::MODELSELECTOR:
            status &= readModelSelector();
            break;
        case IBIS_PARSER_CONTINUE::MODEL:
            status &= readModel();
            break;
        case IBIS_PARSER_CONTINUE::IV_TABLE:
            status &= readIVtableEntry( *m_currentIVtable );
            break;
        case IBIS_PARSER_CONTINUE::VT_TABLE:
            status &= readVTtableEntry( *m_currentVTtable );
            break;
        case IBIS_PARSER_CONTINUE::WAVEFORM:
            status &= readWaveform( m_currentWaveform, m_currentWaveform->m_type );
            break;
        case IBIS_PARSER_CONTINUE::RAMP:
            status &= readRamp();
            break;
        case IBIS_PARSER_CONTINUE::MODEL_SPEC:
            status &= readModelSpec();
            break;
        case IBIS_PARSER_CONTINUE::PACKAGEMODEL_PINS:
            status &= readPackageModelPins();
            break;
        case IBIS_PARSER_CONTINUE::MATRIX:
            status &= readMatrix( m_currentMatrix );
            break;
        case IBIS_PARSER_CONTINUE::NONE:
        default:
            Report( _( "Missing keyword." ), RPT_SEVERITY_ERROR );
            return false;
            break;
        }
    }

    c = m_buffer[m_lineOffset + m_lineIndex];

    while( ( c != '\n' ) && ( c != 0 ) ) // Go to the end of line
        c = m_buffer[m_lineOffset + m_lineIndex++];

    return status;
}
