'''
This file is modified from the PyKaldi project
https://github.com/pykaldi/pykaldi/blob/master/kaldi/util/table.py
'''
#
#
# Author:  <NAME>
# Author:  <NAME>
#
#
'''
For detailed documentation of Kaldi tables, table readers/writers, table
read/write specifiers, see `Kaldi I/O mechanisms`_ and
`Kaldi I/O from a command-line perspective`_.

.. _Kaldi I/O mechanisms:
   http://kaldi-asr.org/doc/io.html
.. _Kaldi I/O from a command-line perspective:
   http://kaldi-asr.org/doc/io_tut.html
'''

# TODO(fangjun): set the PYTHONPATH environment variable outside this script
# to avoid set sys.path for every Python script

import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir))

import numpy as np

import kaldi_pybind
from kaldi_pybind.nnet3 import _SequentialNnetChainExampleReader
from kaldi_pybind.nnet3 import _RandomAccessNnetChainExampleReader
from kaldi_pybind.nnet3 import _NnetChainExampleWriter

from kaldi_pybind.nnet3 import _SequentialNnetExampleReader
from kaldi_pybind.nnet3 import _RandomAccessNnetExampleReader

from kaldi_pybind.feat import _SequentialWaveReader
from kaldi_pybind.feat import _RandomAccessWaveReader
from kaldi_pybind.feat import _SequentialWaveInfoReader
from kaldi_pybind.feat import _RandomAccessWaveInfoReader

from kaldi_pybind import _SequentialBaseFloatMatrixReader
from kaldi_pybind import _RandomAccessBaseFloatMatrixReader
from kaldi_pybind import _BaseFloatMatrixWriter

from kaldi_pybind import _SequentialBaseFloatVectorReader
from kaldi_pybind import _RandomAccessBaseFloatVectorReader
from kaldi_pybind import _BaseFloatVectorWriter

from kaldi_pybind import _CompressedMatrixWriter

from kaldi_pybind import _SequentialInt32VectorReader
from kaldi_pybind import _RandomAccessInt32VectorReader
from kaldi_pybind import _Int32VectorWriter

from kaldi_pybind import _SequentialLatticeReader
from kaldi_pybind import _RandomAccessLatticeReader
from kaldi_pybind import _LatticeWriter

from kaldi_pybind import _SequentialCompactLatticeReader
from kaldi_pybind import _RandomAccessCompactLatticeReader
from kaldi_pybind import _CompactLatticeWriter

################################################################################
# Sequential Readers
################################################################################


class _SequentialReaderBase(object):
    '''Base class defining the Python API for sequential table readers.'''

    def __init__(self, rspecifier=''):
        '''
        This class is used for reading objects sequentially from an archive or
        script file. It implements the iterator protocol similar to how Python
        implements iteration over dictionaries. Each iteration returns a `(key,
        value)` pair from the table in sequential order.

        Args:
            rspecifier(str): Kaldi rspecifier for reading the table.
                If provided, the table is opened for reading.

        Raises:
            IOError: If opening the table for reading fails.
        '''
        super(_SequentialReaderBase, self).__init__()
        if rspecifier != '':
            if not self.Open(rspecifier):
                raise IOError('Error opening sequential table reader with '
                              'rspecifier: {}'.format(rspecifier))

    def __enter__(self):
        return self

    def __exit__(self, type, value, traceback):
        if self.IsOpen():
            self.Close()

    def __iter__(self):
        while not self.Done():
            key = self.Key()
            value = self.Value()
            yield key, value
            self.Next()

    def Open(self, rspecifier):
        '''Opens the table for reading.

        Args:
            rspecifier(str): Kaldi rspecifier for reading the table.
                If provided, the table is opened for reading.

        Returns:
            True if table is opened successfully, False otherwise.

        Raises:
            IOError: If opening the table for reading fails.
        '''
        return super(_SequentialReaderBase, self).Open(rspecifier)

    def Done(self):
        '''Indicates whether the table reader is exhausted or not.

        This method is provided for compatibility with the C++ API only;
        most users should use the Pythonic API.

        Returns:
          True if the table reader is exhausted, False otherwise.
        '''
        return super(_SequentialReaderBase, self).Done()

    def Key(self):
        '''Returns the current key.

        This method is provided for compatibility with the C++ API only;
        most users should use the Pythonic API.

        Returns:
            str: The current key.
        '''
        return super(_SequentialReaderBase, self).Key()

    def FreeCurrent(self):
        '''Deallocates the current value.

        This method is provided as an optimization to save memory, for large
        objects.
        '''
        super(_SequentialReaderBase, self).FreeCurrent()

    def Value(self):
        '''Returns the current value.

        This method is provided for compatibility with the C++ API only;
        most users should use the Pythonic API.

        Returns:
            The current value.
        '''
        return super(_SequentialReaderBase, self).Value()

    def Next(self):
        '''Advances the table reader.

        This method is provided for compatibility with the C++ API only;
        most users should use the Pythonic API.
        '''
        super(_SequentialReaderBase, self).Next()

    def IsOpen(self):
        '''Indicates whether the table reader is open or not.

        This method is provided for compatibility with the C++ API only;
        most users should use the Pythonic API.

        Returns:
          True if the table reader is open, False otherwise.
        '''
        return super(_SequentialReaderBase, self).IsOpen()

    def Close(self):
        '''Closes the table.

        This method is provided for compatibility with the C++ API only;
        most users should use the Pythonic API.

        Returns:
            True if table is closed successfully, False otherwise.
        '''
        return super(_SequentialReaderBase, self).Close()


class SequentialNnetChainExampleReader(_SequentialReaderBase,
                                       _SequentialNnetChainExampleReader):
    '''Sequential table reader for nnet chain examples.'''
    pass

class SequentialNnetExampleReader(_SequentialReaderBase,
                                  _SequentialNnetExampleReader):
    '''Sequential table reader for nnet examples.'''
    pass


class SequentialWaveReader(_SequentialReaderBase, _SequentialWaveReader):
    '''Sequential table reader for wave files.'''
    pass


class SequentialWaveInfoReader(_SequentialReaderBase,
                               _SequentialWaveInfoReader):
    '''Sequential table reader for wave file headers.'''
    pass


class SequentialMatrixReader(_SequentialReaderBase,
                             _SequentialBaseFloatMatrixReader):
    '''Sequential table reader for single precision matrices.'''
    pass


class SequentialVectorReader(_SequentialReaderBase,
                             _SequentialBaseFloatVectorReader):
    '''Sequential table reader for single precision vectors.'''
    pass


class SequentialIntVectorReader(_SequentialReaderBase,
                                _SequentialInt32VectorReader):
    '''Sequential table reader for integer sequences.'''
    pass


class SequentialLatticeReader(_SequentialReaderBase, _SequentialLatticeReader):
    '''Sequential table reader for lattices.'''
    pass


class SequentialCompactLatticeReader(_SequentialReaderBase,
                                     _SequentialCompactLatticeReader):
    '''Sequential table reader for compact lattices.'''
    pass


################################################################################
# Random Access Readers
################################################################################


class _RandomAccessReaderBase(object):
    '''Base class defining the Python API for random access table readers.'''

    def __init__(self, rspecifier=''):
        '''
            This class is used for randomly accessing objects in an archive or
            script file. It implements `__contains__` and `__getitem__` methods to
            provide a dictionary-like interface for accessing table entries. e.g.
            `reader[key]` returns the `value` associated with the `key`.

            Args:
                rspecifier(str): Kaldi rspecifier for reading the table.
                    If provided, the table is opened for reading.

            Raises:
                IOError: If opening the table for reading fails.
            '''
        super(_RandomAccessReaderBase, self).__init__()
        if rspecifier != '':
            if not self.Open(rspecifier):
                raise IOError('Error opening random access table reader with '
                              'rspecifier: {}'.format(rspecifier))

    def __enter__(self):
        return self

    def __exit__(self, type, value, traceback):
        if self.IsOpen():
            self.Close()

    def __contains__(self, key):
        return self.HasKey(key)

    def __getitem__(self, key):
        if self.HasKey(key):
            return self.Value(key)
        else:
            raise KeyError(key)

    def Open(self, rspecifier):
        '''Opens the table for reading.

            Args:
                rspecifier(str): Kaldi rspecifier for reading the table.
                    If provided, the table is opened for reading.

            Returns:
                True if table is opened successfully, False otherwise.

            Raises:
                IOError: If opening the table for reading fails.
            '''
        return super(_RandomAccessReaderBase, self).Open(rspecifier)

    def HasKey(self, key):
        '''Checks whether the table has the key.

            This method is provided for compatibility with the C++ API only;
            most users should use the Pythonic API.

            Args:
                key (str): The key.

            Returns:
              True if the table has the key, False otherwise.
            '''
        return super(_RandomAccessReaderBase, self).HasKey(key)

    def Value(self, key):
        '''Returns the value associated with the key.

            This method is provided for compatibility with the C++ API only;
            most users should use the Pythonic API.

            Args:
                key (str): The key.

            Returns:
                The value associated with the key.
            '''
        return super(_RandomAccessReaderBase, self).Value(key)

    def IsOpen(self):
        '''Indicates whether the table reader is open or not.

            This method is provided for compatibility with the C++ API only;
            most users should use the Pythonic API.

            Returns:
              True if the table reader is open, False otherwise.
            '''
        return super(_RandomAccessReaderBase, self).IsOpen()

    def Close(self):
        '''Closes the table.

            This method is provided for compatibility with the C++ API only;
            most users should use the Pythonic API.

            Returns:
                True if table is closed successfully, False otherwise.
            '''
        return super(_RandomAccessReaderBase, self).Close()


class RandomAccessNnetChainExampleReader(_RandomAccessReaderBase,
                                         _RandomAccessNnetChainExampleReader):
    '''Random access table reader for nnet chain examples.'''
    pass

class RandomAccessNnetExampleReader(_RandomAccessReaderBase,
                                    _RandomAccessNnetExampleReader):
    '''Random access table reader for nnet examples.'''
    pass

class RandomAccessWaveReader(_RandomAccessReaderBase, _RandomAccessWaveReader):
    '''Random access table reader for wave files.'''
    pass


class RandomAccessWaveInfoReader(_RandomAccessReaderBase,
                                 _RandomAccessWaveInfoReader):
    '''Random access table reader for wave file headers.'''
    pass


class RandomAccessMatrixReader(_RandomAccessReaderBase,
                               _RandomAccessBaseFloatMatrixReader):
    '''Random access table reader for single precision matrices.'''
    pass


class RandomAccessVectorReader(_RandomAccessReaderBase,
                               _RandomAccessBaseFloatVectorReader):
    '''Random access table reader for single precision vectors.'''
    pass


class RandomAccessIntVectorReader(_RandomAccessReaderBase,
                                  _RandomAccessInt32VectorReader):
    '''Random access table reader for integer sequences.'''
    pass


class RandomAccessLatticeReader(_RandomAccessReaderBase,
                                _RandomAccessLatticeReader):
    '''Random access table reader for lattices.'''
    pass


class RandomAccessCompactLatticeReader(_RandomAccessReaderBase,
                                       _RandomAccessCompactLatticeReader):
    '''Random access table reader for compact lattices.'''
    pass


################################################################################
# Writers
################################################################################


class _WriterBase(object):
    '''Base class defining the additional Python API for table writers.'''

    def __init__(self, wspecifier=''):
        '''

            This class is used for writing objects to an archive or script file. It
            implements the `__setitem__` method to provide a dictionary-like
            interface for writing table entries, e.g. `writer[key] = value` writes
            the pair `(key, value)` to the table.

            Args:
                wspecifier (str): Kaldi wspecifier for writing the table.
                    If provided, the table is opened for writing.

            Raises:
                IOError: If opening the table for writing fails.
            '''
        super(_WriterBase, self).__init__()
        if wspecifier != '':
            if not self.Open(wspecifier):
                raise IOError(
                    'Error opening table writer with wspecifier: {}'.format(
                        wspecifier))

    def __enter__(self):
        return self

    def __exit__(self, type, value, traceback):
        if self.IsOpen():
            self.Close()
    
    def __setitem__(self, key, value):
        self.Write(key, value)

    def Open(self, wspecifier):
        '''Opens the table for writing.

            Args:
                wspecifier(str): Kaldi wspecifier for writing the table.
                    If provided, the table is opened for writing.

            Returns:
                True if table is opened successfully, False otherwise.

            Raises:
                IOError: If opening the table for writing fails.
            '''
        return super(_WriterBase, self).Open(wspecifier)

    def Flush(self):
        '''Flushes the table contents to disk/pipe.'''
        super(_WriterBase, self).Flush()

    def Write(self, key, value):
        '''Writes the `(key, value)` pair to the table.

            This method is provided for compatibility with the C++ API only;
            most users should use the Pythonic API.

            Args:
                key (str): The key.
                value: The value.
            '''
        super(_WriterBase, self).Write(key, value)

    def IsOpen(self):
        '''Indicates whether the table writer is open or not.

            This method is provided for compatibility with the C++ API only;
            most users should use the Pythonic API.

            Returns:
              True if the table writer is open, False otherwise.
            '''
        return super(_WriterBase, self).IsOpen()

    def Close(self):
        '''Closes the table.

            This method is provided for compatibility with the C++ API only;
            most users should use the Pythonic API.

            Returns:
                True if table is closed successfully, False otherwise.
            '''
        return super(_WriterBase, self).Close()


class NnetChainExampleWriter(_WriterBase, _NnetChainExampleWriter):
    '''Table writer for nnet chain examples.'''
    pass


class MatrixWriter(_WriterBase, _BaseFloatMatrixWriter):
    '''Table writer for single precision matrices.'''

    def Write(self, key, value):
        if isinstance(value, np.ndarray):
            m = kaldi_pybind.FloatSubMatrix(value)
            value = kaldi_pybind.FloatMatrix(m)
        super().Write(key, value)


class VectorWriter(_WriterBase, _BaseFloatVectorWriter):
    '''Table writer for single precision vectors.'''

    def Write(self, key, value):
        if isinstance(value, np.ndarray):
            v = kaldi_pybind.FloatSubVector(value)
            value = kaldi_pybind.FloatVector(v)
        super().Write(key, value)


class CompressedMatrixWriter(_WriterBase, _CompressedMatrixWriter):
    '''Table writer for single precision compressed matrices.'''
    pass


class IntVectorWriter(_WriterBase, _Int32VectorWriter):
    '''Table writer for integer sequences.'''
    pass


class LatticeWriter(_WriterBase, _LatticeWriter):
    '''Table writer for lattices.'''
    pass


class CompactLatticeWriter(_WriterBase, _CompactLatticeWriter):
    '''Table writer for compact lattices.'''
    pass


if False:
    # TODO(fangjun): enable the following once other wrappers are added

    class SequentialDoubleVectorReader(_SequentialReaderBase,
                                       _kaldi_table.SequentialDoubleVectorReader
                                      ):
        '''Sequential table reader for double precision vectors.'''
        pass

    class SequentialDoubleMatrixReader(_SequentialReaderBase,
                                       _kaldi_table.SequentialDoubleMatrixReader
                                      ):
        '''Sequential table reader for double precision matrices.'''
        pass

    class SequentialPosteriorReader(_SequentialReaderBase,
                                    _kaldi_table.SequentialPosteriorReader):
        '''Sequential table reader for frame posteriors.'''
        pass

    class SequentialGaussPostReader(_SequentialReaderBase,
                                    _kaldi_table.SequentialGaussPostReader):
        '''Sequential table reader for Gaussian-level frame posteriors.'''
        pass

    class SequentialFstReader(_SequentialReaderBase,
                              _kaldi_table_ext.SequentialFstReader):
        '''Sequential table reader for FSTs over the tropical semiring.'''
        pass

    class SequentialLogFstReader(_SequentialReaderBase,
                                 _kaldi_table_ext.SequentialLogFstReader):
        '''Sequential table reader for FSTs over the log semiring.'''
        pass

    class SequentialKwsIndexFstReader(
            _SequentialReaderBase,
            _kaldi_table_ext.SequentialKwsIndexFstReader):
        '''Sequential table reader for FSTs over the KWS index semiring.'''
        pass

    class SequentialRnnlmExampleReader(_SequentialReaderBase,
                                       _kaldi_table.SequentialRnnlmExampleReader
                                      ):
        '''Sequential table reader for RNNLM examples.'''
        pass

    class SequentialIntReader(_SequentialReaderBase,
                              _kaldi_table.SequentialIntReader):
        '''Sequential table reader for integers.'''
        pass

    class SequentialFloatReader(_SequentialReaderBase,
                                _kaldi_table.SequentialFloatReader):
        '''Sequential table reader for single precision floats.'''
        pass

    class SequentialDoubleReader(_SequentialReaderBase,
                                 _kaldi_table.SequentialDoubleReader):
        '''Sequential table reader for double precision floats.'''
        pass

    class SequentialBoolReader(_SequentialReaderBase,
                               _kaldi_table.SequentialBoolReader):
        '''Sequential table reader for Booleans.'''
        pass

    class SequentialIntVectorVectorReader(
            _SequentialReaderBase,
            _kaldi_table.SequentialIntVectorVectorReader):
        '''Sequential table reader for sequences of integer sequences.'''
        pass

    class SequentialIntPairVectorReader(
            _SequentialReaderBase, _kaldi_table.SequentialIntPairVectorReader):
        '''Sequential table reader for sequences of integer pairs.'''
        pass

    class SequentialFloatPairVectorReader(
            _SequentialReaderBase,
            _kaldi_table.SequentialFloatPairVectorReader):
        '''Sequential table reader for sequences of single precision float pairs.'''
        pass

    class RandomAccessDoubleVectorReader(
            _RandomAccessReaderBase,
            _kaldi_table.RandomAccessDoubleVectorReader):
        '''Random access table reader for double precision vectors.'''
        pass

    class RandomAccessDoubleMatrixReader(
            _RandomAccessReaderBase,
            _kaldi_table.RandomAccessDoubleMatrixReader):
        '''Random access table reader for double precision matrices.'''
        pass

    class RandomAccessPosteriorReader(_RandomAccessReaderBase,
                                      _kaldi_table.RandomAccessPosteriorReader):
        '''Random access table reader for frame posteriors.'''
        pass

    class RandomAccessGaussPostReader(_RandomAccessReaderBase,
                                      _kaldi_table.RandomAccessGaussPostReader):
        '''Random access table reader for Gaussian-level frame posteriors.'''
        pass

    class RandomAccessFstReader(_RandomAccessReaderBase,
                                _kaldi_table_ext.RandomAccessFstReader):
        '''Random access table reader for FSTs over the tropical semiring.'''
        pass

    class RandomAccessLogFstReader(_RandomAccessReaderBase,
                                   _kaldi_table_ext.RandomAccessLogFstReader):
        '''Random access table reader for FSTs over the log semiring.'''
        pass

    class RandomAccessKwsIndexFstReader(
            _RandomAccessReaderBase,
            _kaldi_table_ext.RandomAccessKwsIndexFstReader):
        '''Random access table reader for FSTs over the KWS index semiring.'''
        pass

    class RandomAccessIntReader(_RandomAccessReaderBase,
                                _kaldi_table.RandomAccessIntReader):
        '''Random access table reader for integers.'''
        pass

    class RandomAccessFloatReader(_RandomAccessReaderBase,
                                  _kaldi_table.RandomAccessFloatReader):
        '''Random access table reader for single precision floats.'''
        pass

    class RandomAccessDoubleReader(_RandomAccessReaderBase,
                                   _kaldi_table.RandomAccessDoubleReader):
        '''Random access table reader for double precision floats.'''
        pass

    class RandomAccessBoolReader(_RandomAccessReaderBase,
                                 _kaldi_table.RandomAccessBoolReader):
        '''Random access table reader for Booleans.'''
        pass

    class RandomAccessIntVectorVectorReader(
            _RandomAccessReaderBase,
            _kaldi_table.RandomAccessIntVectorVectorReader):
        '''Random access table reader for sequences of integer sequences.'''
        pass

    class RandomAccessIntPairVectorReader(
            _RandomAccessReaderBase,
            _kaldi_table.RandomAccessIntPairVectorReader):
        '''Random access table reader for sequences of integer pairs.'''
        pass

    class RandomAccessFloatPairVectorReader(
            _RandomAccessReaderBase,
            _kaldi_table.RandomAccessFloatPairVectorReader):
        '''
        Random access table reader for sequences of single precision float pairs.
        '''
        pass

################################################################################
# Mapped Random Access Readers
################################################################################

    class _RandomAccessReaderMappedBase(object):
        '''
        Base class defining the Python API for mapped random access table readers.
        '''

        def __init__(self, table_rspecifier='', map_rspecifier=''):
            '''
            This class is used for randomly accessing objects in an archive or
            script file. It implements `__contains__` and `__getitem__` methods to
            provide a dictionary-like interface for accessing table entries. If a
            **map_rspecifier** is provided, the map is used for converting the keys
            to the actual keys used to query the table, e.g. `reader[key]` returns
            the `value` associated with the key `map[key]`. Otherwise, it works like
            a random access table reader.

            Args:
                table_rspecifier(str): Kaldi rspecifier for reading the table.
                    If provided, the table is opened for reading.
                map_rspecifier (str): Kaldi rspecifier for reading the map.
                    If provided, the map is opened for reading.

            Raises:
                IOError: If opening the table or map for reading fails.
            '''
            super(_RandomAccessReaderMappedBase, self).__init__()
            if table_rspecifier != '' and map_rspecifier != '':
                if not self.open(table_rspecifier, map_rspecifier):
                    raise IOError(
                        'Error opening mapped random access table reader '
                        'with table_rspecifier: {}, map_rspecifier: {}'.format(
                            table_rspecifier, map_rspecifier))

        def __enter__(self):
            return self

        def __contains__(self, key):
            return self.has_key(key)

        def __getitem__(self, key):
            if self.has_key(key):
                return self.value(key)
            else:
                raise KeyError(key)

        def open(self, table_rspecifier, map_rspecifier):
            '''Opens the table for reading.

            Args:
                table_rspecifier(str): Kaldi rspecifier for reading the table.
                    If provided, the table is opened for reading.
                map_rspecifier (str): Kaldi rspecifier for reading the map.
                    If provided, the map is opened for reading.

            Returns:
                True if table is opened successfully, False otherwise.

            Raises:
                IOError: If opening the table or map for reading fails.
            '''
            return super(_RandomAccessReaderMappedBase,
                         self).open(table_rspecifier, map_rspecifier)

        def has_key(self, key):
            '''Checks whether the table has the key.

            This method is provided for compatibility with the C++ API only;
            most users should use the Pythonic API.

            Args:
                key (str): The key.

            Returns:
              True if the table has the key, False otherwise.
            '''
            return super(_RandomAccessReaderMappedBase, self).has_key(key)

        def value(self, key):
            '''Returns the value associated with the key.

            This method is provided for compatibility with the C++ API only;
            most users should use the Pythonic API.

            Args:
                key (str): The key.

            Returns:
                The value associated with the key.
            '''
            return super(_RandomAccessReaderMappedBase, self).value(key)

        def is_open(self):
            '''Indicates whether the table reader is open or not.

            This method is provided for compatibility with the C++ API only;
            most users should use the Pythonic API.

            Returns:
              True if the table reader is open, False otherwise.
            '''
            return super(_RandomAccessReaderMappedBase, self).is_open()

        def close(self):
            '''Closes the table.

            This method is provided for compatibility with the C++ API only;
            most users should use the Pythonic API.

            Returns:
                True if table is closed successfully, False otherwise.
            '''
            return super(_RandomAccessReaderMappedBase, self).close()

    class RandomAccessVectorReaderMapped(
            _RandomAccessReaderMappedBase,
            _kaldi_table.RandomAccessVectorReaderMapped):
        '''Mapped random access table reader for single precision vectors.'''
        pass

    class RandomAccessDoubleVectorReaderMapped(
            _RandomAccessReaderMappedBase,
            _kaldi_table.RandomAccessDoubleVectorReaderMapped):
        '''Mapped random access table reader for double precision vectors.'''
        pass

    class RandomAccessMatrixReaderMapped(
            _RandomAccessReaderMappedBase,
            _kaldi_table.RandomAccessMatrixReaderMapped):
        '''Mapped random access table reader for single precision matrices.'''
        pass

    class RandomAccessDoubleMatrixReaderMapped(
            _RandomAccessReaderMappedBase,
            _kaldi_table.RandomAccessDoubleMatrixReaderMapped):
        '''Mapped random access table reader for double precision matrices.'''
        pass

    class RandomAccessFloatReaderMapped(
            _RandomAccessReaderMappedBase,
            _kaldi_table.RandomAccessFloatReaderMapped):
        '''Mapped random access table reader for single precision floats.'''
        pass

    class DoubleVectorWriter(_WriterBase, _kaldi_table.DoubleVectorWriter):
        '''Table writer for double precision vectors.'''

        def write(self, key, value):
            '''Writes the `(key, value)` pair to the table.

            This method is provided for compatibility with the C++ API only;
            most users should use the Pythonic API.

            Overrides write to accept both DoubleVector and DoubleSubVector.

            Args:
                key (str): The key.
                value: The value.
            '''
            super(DoubleVectorWriter, self).write(key,
                                                  _matrix.DoubleVector(value))

    class DoubleMatrixWriter(_WriterBase, _kaldi_table.DoubleMatrixWriter):
        '''Table writer for double precision matrices.'''

        def write(self, key, value):
            '''Writes the `(key, value)` pair to the table.

            This method is provided for compatibility with the C++ API only;
            most users should use the Pythonic API.

            Overrides write to accept both DoubleMatrix and DoubleSubMatrix.

            Args:
                key (str): The key.
                value: The value.
            '''
            super(DoubleMatrixWriter, self).write(key,
                                                  _matrix.DoubleMatrix(value))

    class WaveWriter(_WriterBase, _kaldi_table.WaveWriter):
        '''Table writer for wave files.'''
        pass

    class PosteriorWriter(_WriterBase, _kaldi_table.PosteriorWriter):
        '''Table writer for frame posteriors.'''
        pass

    class GaussPostWriter(_WriterBase, _kaldi_table.GaussPostWriter):
        '''Table writer for Gaussian-level frame posteriors.'''
        pass

    class FstWriter(_WriterBase, _kaldi_table_ext.FstWriter):
        '''Table writer for FSTs over the tropical semiring.'''
        pass

    class LogFstWriter(_WriterBase, _kaldi_table_ext.LogFstWriter):
        '''Table writer for FSTs over the log semiring.'''
        pass

    class KwsIndexFstWriter(_WriterBase, _kaldi_table_ext.KwsIndexFstWriter):
        '''Table writer for FSTs over the KWS index semiring.'''
        pass

    class NnetExampleWriter(_WriterBase, _kaldi_table.NnetExampleWriter):
        '''Table writer for nnet examples.'''
        pass

    class RnnlmExampleWriter(_WriterBase, _kaldi_table.RnnlmExampleWriter):
        '''Table writer for RNNLM examples.'''
        pass

    class IntWriter(_WriterBase, _kaldi_table.IntWriter):
        '''Table writer for integers.'''
        pass

    class FloatWriter(_WriterBase, _kaldi_table.FloatWriter):
        '''Table writer for single precision floats.'''
        pass

    class DoubleWriter(_WriterBase, _kaldi_table.DoubleWriter):
        '''Table writer for double precision floats.'''
        pass

    class BoolWriter(_WriterBase, _kaldi_table.BoolWriter):
        '''Table writer for Booleans.'''
        pass

    class IntVectorVectorWriter(_WriterBase,
                                _kaldi_table.IntVectorVectorWriter):
        '''Table writer for sequences of integer sequences.'''
        pass

    class IntPairVectorWriter(_WriterBase, _kaldi_table.IntPairVectorWriter):
        '''Table writer for sequences of integer pairs.'''
        pass

    class FloatPairVectorWriter(_WriterBase,
                                _kaldi_table.FloatPairVectorWriter):
        '''Table writer for sequences of single precision float pairs.'''
        pass


################################################################################

'''
This file is modified from the PyKaldi project
https://github.com/pykaldi/pykaldi/blob/master/kaldi/util/table.py
'''
#
#
# Author:  <NAME>
# Author:  <NAME>
#
#
'''
For detailed documentation of Kaldi tables, table readers/writers, table
read/write specifiers, see `Kaldi I/O mechanisms`_ and
`Kaldi I/O from a command-line perspective`_.

.. _Kaldi I/O mechanisms:
   http://kaldi-asr.org/doc/io.html
.. _Kaldi I/O from a command-line perspective:
   http://kaldi-asr.org/doc/io_tut.html
'''

# TODO(fangjun): set the PYTHONPATH environment variable outside this script
# to avoid set sys.path for every Python script

import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir))

import numpy as np

import kaldi_pybind
from kaldi_pybind.nnet3 import _SequentialNnetChainExampleReader
from kaldi_pybind.nnet3 import _RandomAccessNnetChainExampleReader
from kaldi_pybind.nnet3 import _NnetChainExampleWriter

from kaldi_pybind.nnet3 import _SequentialNnetExampleReader
from kaldi_pybind.nnet3 import _RandomAccessNnetExampleReader

from kaldi_pybind.feat import _SequentialWaveReader
from kaldi_pybind.feat import _RandomAccessWaveReader
from kaldi_pybind.feat import _SequentialWaveInfoReader
from kaldi_pybind.feat import _RandomAccessWaveInfoReader

from kaldi_pybind import _SequentialBaseFloatMatrixReader
from kaldi_pybind import _RandomAccessBaseFloatMatrixReader
from kaldi_pybind import _BaseFloatMatrixWriter

from kaldi_pybind import _SequentialBaseFloatVectorReader
from kaldi_pybind import _RandomAccessBaseFloatVectorReader
from kaldi_pybind import _BaseFloatVectorWriter

from kaldi_pybind import _CompressedMatrixWriter

from kaldi_pybind import _SequentialInt32VectorReader
from kaldi_pybind import _RandomAccessInt32VectorReader
from kaldi_pybind import _Int32VectorWriter

from kaldi_pybind import _SequentialLatticeReader
from kaldi_pybind import _RandomAccessLatticeReader
from kaldi_pybind import _LatticeWriter

from kaldi_pybind import _SequentialCompactLatticeReader
from kaldi_pybind import _RandomAccessCompactLatticeReader
from kaldi_pybind import _CompactLatticeWriter

################################################################################
# Sequential Readers
################################################################################


class _SequentialReaderBase(object):
    '''Base class defining the Python API for sequential table readers.'''

    def __init__(self, rspecifier=''):
        '''
        This class is used for reading objects sequentially from an archive or
        script file. It implements the iterator protocol similar to how Python
        implements iteration over dictionaries. Each iteration returns a `(key,
        value)` pair from the table in sequential order.

        Args:
            rspecifier(str): Kaldi rspecifier for reading the table.
                If provided, the table is opened for reading.

        Raises:
            IOError: If opening the table for reading fails.
        '''
        super(_SequentialReaderBase, self).__init__()
        if rspecifier != '':
            if not self.Open(rspecifier):
                raise IOError('Error opening sequential table reader with '
                              'rspecifier: {}'.format(rspecifier))

    def __enter__(self):
        return self

    def __exit__(self, type, value, traceback):
        if self.IsOpen():
            self.Close()

    def __iter__(self):
        while not self.Done():
            key = self.Key()
            value = self.Value()
            yield key, value
            self.Next()

    def Open(self, rspecifier):
        '''Opens the table for reading.

        Args:
            rspecifier(str): Kaldi rspecifier for reading the table.
                If provided, the table is opened for reading.

        Returns:
            True if table is opened successfully, False otherwise.

        Raises:
            IOError: If opening the table for reading fails.
        '''
        return super(_SequentialReaderBase, self).Open(rspecifier)

    def Done(self):
        '''Indicates whether the table reader is exhausted or not.

        This method is provided for compatibility with the C++ API only;
        most users should use the Pythonic API.

        Returns:
          True if the table reader is exhausted, False otherwise.
        '''
        return super(_SequentialReaderBase, self).Done()

    def Key(self):
        '''Returns the current key.

        This method is provided for compatibility with the C++ API only;
        most users should use the Pythonic API.

        Returns:
            str: The current key.
        '''
        return super(_SequentialReaderBase, self).Key()

    def FreeCurrent(self):
        '''Deallocates the current value.

        This method is provided as an optimization to save memory, for large
        objects.
        '''
        super(_SequentialReaderBase, self).FreeCurrent()

    def Value(self):
        '''Returns the current value.

        This method is provided for compatibility with the C++ API only;
        most users should use the Pythonic API.

        Returns:
            The current value.
        '''
        return super(_SequentialReaderBase, self).Value()

    def Next(self):
        '''Advances the table reader.

        This method is provided for compatibility with the C++ API only;
        most users should use the Pythonic API.
        '''
        super(_SequentialReaderBase, self).Next()

    def IsOpen(self):
        '''Indicates whether the table reader is open or not.

        This method is provided for compatibility with the C++ API only;
        most users should use the Pythonic API.

        Returns:
          True if the table reader is open, False otherwise.
        '''
        return super(_SequentialReaderBase, self).IsOpen()

    def Close(self):
        '''Closes the table.

        This method is provided for compatibility with the C++ API only;
        most users should use the Pythonic API.

        Returns:
            True if table is closed successfully, False otherwise.
        '''
        return super(_SequentialReaderBase, self).Close()


class SequentialNnetChainExampleReader(_SequentialReaderBase,
                                       _SequentialNnetChainExampleReader):
    '''Sequential table reader for nnet chain examples.'''
    pass

class SequentialNnetExampleReader(_SequentialReaderBase,
                                  _SequentialNnetExampleReader):
    '''Sequential table reader for nnet examples.'''
    pass


class SequentialWaveReader(_SequentialReaderBase, _SequentialWaveReader):
    '''Sequential table reader for wave files.'''
    pass


class SequentialWaveInfoReader(_SequentialReaderBase,
                               _SequentialWaveInfoReader):
    '''Sequential table reader for wave file headers.'''
    pass


class SequentialMatrixReader(_SequentialReaderBase,
                             _SequentialBaseFloatMatrixReader):
    '''Sequential table reader for single precision matrices.'''
    pass


class SequentialVectorReader(_SequentialReaderBase,
                             _SequentialBaseFloatVectorReader):
    '''Sequential table reader for single precision vectors.'''
    pass


class SequentialIntVectorReader(_SequentialReaderBase,
                                _SequentialInt32VectorReader):
    '''Sequential table reader for integer sequences.'''
    pass


class SequentialLatticeReader(_SequentialReaderBase, _SequentialLatticeReader):
    '''Sequential table reader for lattices.'''
    pass


class SequentialCompactLatticeReader(_SequentialReaderBase,
                                     _SequentialCompactLatticeReader):
    '''Sequential table reader for compact lattices.'''
    pass


################################################################################
# Random Access Readers
################################################################################


class _RandomAccessReaderBase(object):
    '''Base class defining the Python API for random access table readers.'''

    def __init__(self, rspecifier=''):
        '''
            This class is used for randomly accessing objects in an archive or
            script file. It implements `__contains__` and `__getitem__` methods to
            provide a dictionary-like interface for accessing table entries. e.g.
            `reader[key]` returns the `value` associated with the `key`.

            Args:
                rspecifier(str): Kaldi rspecifier for reading the table.
                    If provided, the table is opened for reading.

            Raises:
                IOError: If opening the table for reading fails.
            '''
        super(_RandomAccessReaderBase, self).__init__()
        if rspecifier != '':
            if not self.Open(rspecifier):
                raise IOError('Error opening random access table reader with '
                              'rspecifier: {}'.format(rspecifier))

    def __enter__(self):
        return self

    def __exit__(self, type, value, traceback):
        if self.IsOpen():
            self.Close()

    def __contains__(self, key):
        return self.HasKey(key)

    def __getitem__(self, key):
        if self.HasKey(key):
            return self.Value(key)
        else:
            raise KeyError(key)

    def Open(self, rspecifier):
        '''Opens the table for reading.

            Args:
                rspecifier(str): Kaldi rspecifier for reading the table.
                    If provided, the table is opened for reading.

            Returns:
                True if table is opened successfully, False otherwise.

            Raises:
                IOError: If opening the table for reading fails.
            '''
        return super(_RandomAccessReaderBase, self).Open(rspecifier)

    def HasKey(self, key):
        '''Checks whether the table has the key.

            This method is provided for compatibility with the C++ API only;
            most users should use the Pythonic API.

            Args:
                key (str): The key.

            Returns:
              True if the table has the key, False otherwise.
            '''
        return super(_RandomAccessReaderBase, self).HasKey(key)

    def Value(self, key):
        '''Returns the value associated with the key.

            This method is provided for compatibility with the C++ API only;
            most users should use the Pythonic API.

            Args:
                key (str): The key.

            Returns:
                The value associated with the key.
            '''
        return super(_RandomAccessReaderBase, self).Value(key)

    def IsOpen(self):
        '''Indicates whether the table reader is open or not.

            This method is provided for compatibility with the C++ API only;
            most users should use the Pythonic API.

            Returns:
              True if the table reader is open, False otherwise.
            '''
        return super(_RandomAccessReaderBase, self).IsOpen()

    def Close(self):
        '''Closes the table.

            This method is provided for compatibility with the C++ API only;
            most users should use the Pythonic API.

            Returns:
                True if table is closed successfully, False otherwise.
            '''
        return super(_RandomAccessReaderBase, self).Close()


class RandomAccessNnetChainExampleReader(_RandomAccessReaderBase,
                                         _RandomAccessNnetChainExampleReader):
    '''Random access table reader for nnet chain examples.'''
    pass

class RandomAccessNnetExampleReader(_RandomAccessReaderBase,
                                    _RandomAccessNnetExampleReader):
    '''Random access table reader for nnet examples.'''
    pass

class RandomAccessWaveReader(_RandomAccessReaderBase, _RandomAccessWaveReader):
    '''Random access table reader for wave files.'''
    pass


class RandomAccessWaveInfoReader(_RandomAccessReaderBase,
                                 _RandomAccessWaveInfoReader):
    '''Random access table reader for wave file headers.'''
    pass


class RandomAccessMatrixReader(_RandomAccessReaderBase,
                               _RandomAccessBaseFloatMatrixReader):
    '''Random access table reader for single precision matrices.'''
    pass


class RandomAccessVectorReader(_RandomAccessReaderBase,
                               _RandomAccessBaseFloatVectorReader):
    '''Random access table reader for single precision vectors.'''
    pass


class RandomAccessIntVectorReader(_RandomAccessReaderBase,
                                  _RandomAccessInt32VectorReader):
    '''Random access table reader for integer sequences.'''
    pass


class RandomAccessLatticeReader(_RandomAccessReaderBase,
                                _RandomAccessLatticeReader):
    '''Random access table reader for lattices.'''
    pass


class RandomAccessCompactLatticeReader(_RandomAccessReaderBase,
                                       _RandomAccessCompactLatticeReader):
    '''Random access table reader for compact lattices.'''
    pass


################################################################################
# Writers
################################################################################


class _WriterBase(object):
    '''Base class defining the additional Python API for table writers.'''

    def __init__(self, wspecifier=''):
        '''

            This class is used for writing objects to an archive or script file. It
            implements the `__setitem__` method to provide a dictionary-like
            interface for writing table entries, e.g. `writer[key] = value` writes
            the pair `(key, value)` to the table.

            Args:
                wspecifier (str): Kaldi wspecifier for writing the table.
                    If provided, the table is opened for writing.

            Raises:
                IOError: If opening the table for writing fails.
            '''
        super(_WriterBase, self).__init__()
        if wspecifier != '':
            if not self.Open(wspecifier):
                raise IOError(
                    'Error opening table writer with wspecifier: {}'.format(
                        wspecifier))

    def __enter__(self):
        return self

    def __exit__(self, type, value, traceback):
        if self.IsOpen():
            self.Close()
    
    def __setitem__(self, key, value):
        self.Write(key, value)

    def Open(self, wspecifier):
        '''Opens the table for writing.

            Args:
                wspecifier(str): Kaldi wspecifier for writing the table.
                    If provided, the table is opened for writing.

            Returns:
                True if table is opened successfully, False otherwise.

            Raises:
                IOError: If opening the table for writing fails.
            '''
        return super(_WriterBase, self).Open(wspecifier)

    def Flush(self):
        '''Flushes the table contents to disk/pipe.'''
        super(_WriterBase, self).Flush()

    def Write(self, key, value):
        '''Writes the `(key, value)` pair to the table.

            This method is provided for compatibility with the C++ API only;
            most users should use the Pythonic API.

            Args:
                key (str): The key.
                value: The value.
            '''
        super(_WriterBase, self).Write(key, value)

    def IsOpen(self):
        '''Indicates whether the table writer is open or not.

            This method is provided for compatibility with the C++ API only;
            most users should use the Pythonic API.

            Returns:
              True if the table writer is open, False otherwise.
            '''
        return super(_WriterBase, self).IsOpen()

    def Close(self):
        '''Closes the table.

            This method is provided for compatibility with the C++ API only;
            most users should use the Pythonic API.

            Returns:
                True if table is closed successfully, False otherwise.
            '''
        return super(_WriterBase, self).Close()


class NnetChainExampleWriter(_WriterBase, _NnetChainExampleWriter):
    '''Table writer for nnet chain examples.'''
    pass


class MatrixWriter(_WriterBase, _BaseFloatMatrixWriter):
    '''Table writer for single precision matrices.'''

    def Write(self, key, value):
        if isinstance(value, np.ndarray):
            m = kaldi_pybind.FloatSubMatrix(value)
            value = kaldi_pybind.FloatMatrix(m)
        super().Write(key, value)


class VectorWriter(_WriterBase, _BaseFloatVectorWriter):
    '''Table writer for single precision vectors.'''

    def Write(self, key, value):
        if isinstance(value, np.ndarray):
            v = kaldi_pybind.FloatSubVector(value)
            value = kaldi_pybind.FloatVector(v)
        super().Write(key, value)


class CompressedMatrixWriter(_WriterBase, _CompressedMatrixWriter):
    '''Table writer for single precision compressed matrices.'''
    pass


class IntVectorWriter(_WriterBase, _Int32VectorWriter):
    '''Table writer for integer sequences.'''
    pass


class LatticeWriter(_WriterBase, _LatticeWriter):
    '''Table writer for lattices.'''
    pass


class CompactLatticeWriter(_WriterBase, _CompactLatticeWriter):
    '''Table writer for compact lattices.'''
    pass


if False:
    # TODO(fangjun): enable the following once other wrappers are added

    class SequentialDoubleVectorReader(_SequentialReaderBase,
                                       _kaldi_table.SequentialDoubleVectorReader
                                      ):
        '''Sequential table reader for double precision vectors.'''
        pass

    class SequentialDoubleMatrixReader(_SequentialReaderBase,
                                       _kaldi_table.SequentialDoubleMatrixReader
                                      ):
        '''Sequential table reader for double precision matrices.'''
        pass

    class SequentialPosteriorReader(_SequentialReaderBase,
                                    _kaldi_table.SequentialPosteriorReader):
        '''Sequential table reader for frame posteriors.'''
        pass

    class SequentialGaussPostReader(_SequentialReaderBase,
                                    _kaldi_table.SequentialGaussPostReader):
        '''Sequential table reader for Gaussian-level frame posteriors.'''
        pass

    class SequentialFstReader(_SequentialReaderBase,
                              _kaldi_table_ext.SequentialFstReader):
        '''Sequential table reader for FSTs over the tropical semiring.'''
        pass

    class SequentialLogFstReader(_SequentialReaderBase,
                                 _kaldi_table_ext.SequentialLogFstReader):
        '''Sequential table reader for FSTs over the log semiring.'''
        pass

    class SequentialKwsIndexFstReader(
            _SequentialReaderBase,
            _kaldi_table_ext.SequentialKwsIndexFstReader):
        '''Sequential table reader for FSTs over the KWS index semiring.'''
        pass

    class SequentialRnnlmExampleReader(_SequentialReaderBase,
                                       _kaldi_table.SequentialRnnlmExampleReader
                                      ):
        '''Sequential table reader for RNNLM examples.'''
        pass

    class SequentialIntReader(_SequentialReaderBase,
                              _kaldi_table.SequentialIntReader):
        '''Sequential table reader for integers.'''
        pass

    class SequentialFloatReader(_SequentialReaderBase,
                                _kaldi_table.SequentialFloatReader):
        '''Sequential table reader for single precision floats.'''
        pass

    class SequentialDoubleReader(_SequentialReaderBase,
                                 _kaldi_table.SequentialDoubleReader):
        '''Sequential table reader for double precision floats.'''
        pass

    class SequentialBoolReader(_SequentialReaderBase,
                               _kaldi_table.SequentialBoolReader):
        '''Sequential table reader for Booleans.'''
        pass

    class SequentialIntVectorVectorReader(
            _SequentialReaderBase,
            _kaldi_table.SequentialIntVectorVectorReader):
        '''Sequential table reader for sequences of integer sequences.'''
        pass

    class SequentialIntPairVectorReader(
            _SequentialReaderBase, _kaldi_table.SequentialIntPairVectorReader):
        '''Sequential table reader for sequences of integer pairs.'''
        pass

    class SequentialFloatPairVectorReader(
            _SequentialReaderBase,
            _kaldi_table.SequentialFloatPairVectorReader):
        '''Sequential table reader for sequences of single precision float pairs.'''
        pass

    class RandomAccessDoubleVectorReader(
            _RandomAccessReaderBase,
            _kaldi_table.RandomAccessDoubleVectorReader):
        '''Random access table reader for double precision vectors.'''
        pass

    class RandomAccessDoubleMatrixReader(
            _RandomAccessReaderBase,
            _kaldi_table.RandomAccessDoubleMatrixReader):
        '''Random access table reader for double precision matrices.'''
        pass

    class RandomAccessPosteriorReader(_RandomAccessReaderBase,
                                      _kaldi_table.RandomAccessPosteriorReader):
        '''Random access table reader for frame posteriors.'''
        pass

    class RandomAccessGaussPostReader(_RandomAccessReaderBase,
                                      _kaldi_table.RandomAccessGaussPostReader):
        '''Random access table reader for Gaussian-level frame posteriors.'''
        pass

    class RandomAccessFstReader(_RandomAccessReaderBase,
                                _kaldi_table_ext.RandomAccessFstReader):
        '''Random access table reader for FSTs over the tropical semiring.'''
        pass

    class RandomAccessLogFstReader(_RandomAccessReaderBase,
                                   _kaldi_table_ext.RandomAccessLogFstReader):
        '''Random access table reader for FSTs over the log semiring.'''
        pass

    class RandomAccessKwsIndexFstReader(
            _RandomAccessReaderBase,
            _kaldi_table_ext.RandomAccessKwsIndexFstReader):
        '''Random access table reader for FSTs over the KWS index semiring.'''
        pass

    class RandomAccessIntReader(_RandomAccessReaderBase,
                                _kaldi_table.RandomAccessIntReader):
        '''Random access table reader for integers.'''
        pass

    class RandomAccessFloatReader(_RandomAccessReaderBase,
                                  _kaldi_table.RandomAccessFloatReader):
        '''Random access table reader for single precision floats.'''
        pass

    class RandomAccessDoubleReader(_RandomAccessReaderBase,
                                   _kaldi_table.RandomAccessDoubleReader):
        '''Random access table reader for double precision floats.'''
        pass

    class RandomAccessBoolReader(_RandomAccessReaderBase,
                                 _kaldi_table.RandomAccessBoolReader):
        '''Random access table reader for Booleans.'''
        pass

    class RandomAccessIntVectorVectorReader(
            _RandomAccessReaderBase,
            _kaldi_table.RandomAccessIntVectorVectorReader):
        '''Random access table reader for sequences of integer sequences.'''
        pass

    class RandomAccessIntPairVectorReader(
            _RandomAccessReaderBase,
            _kaldi_table.RandomAccessIntPairVectorReader):
        '''Random access table reader for sequences of integer pairs.'''
        pass

    class RandomAccessFloatPairVectorReader(
            _RandomAccessReaderBase,
            _kaldi_table.RandomAccessFloatPairVectorReader):
        '''
        Random access table reader for sequences of single precision float pairs.
        '''
        pass

################################################################################
# Mapped Random Access Readers
################################################################################

    class _RandomAccessReaderMappedBase(object):
        '''
        Base class defining the Python API for mapped random access table readers.
        '''

        def __init__(self, table_rspecifier='', map_rspecifier=''):
            '''
            This class is used for randomly accessing objects in an archive or
            script file. It implements `__contains__` and `__getitem__` methods to
            provide a dictionary-like interface for accessing table entries. If a
            **map_rspecifier** is provided, the map is used for converting the keys
            to the actual keys used to query the table, e.g. `reader[key]` returns
            the `value` associated with the key `map[key]`. Otherwise, it works like
            a random access table reader.

            Args:
                table_rspecifier(str): Kaldi rspecifier for reading the table.
                    If provided, the table is opened for reading.
                map_rspecifier (str): Kaldi rspecifier for reading the map.
                    If provided, the map is opened for reading.

            Raises:
                IOError: If opening the table or map for reading fails.
            '''
            super(_RandomAccessReaderMappedBase, self).__init__()
            if table_rspecifier != '' and map_rspecifier != '':
                if not self.open(table_rspecifier, map_rspecifier):
                    raise IOError(
                        'Error opening mapped random access table reader '
                        'with table_rspecifier: {}, map_rspecifier: {}'.format(
                            table_rspecifier, map_rspecifier))

        def __enter__(self):
            return self

        def __contains__(self, key):
            return self.has_key(key)

        def __getitem__(self, key):
            if self.has_key(key):
                return self.value(key)
            else:
                raise KeyError(key)

        def open(self, table_rspecifier, map_rspecifier):
            '''Opens the table for reading.

            Args:
                table_rspecifier(str): Kaldi rspecifier for reading the table.
                    If provided, the table is opened for reading.
                map_rspecifier (str): Kaldi rspecifier for reading the map.
                    If provided, the map is opened for reading.

            Returns:
                True if table is opened successfully, False otherwise.

            Raises:
                IOError: If opening the table or map for reading fails.
            '''
            return super(_RandomAccessReaderMappedBase,
                         self).open(table_rspecifier, map_rspecifier)

        def has_key(self, key):
            '''Checks whether the table has the key.

            This method is provided for compatibility with the C++ API only;
            most users should use the Pythonic API.

            Args:
                key (str): The key.

            Returns:
              True if the table has the key, False otherwise.
            '''
            return super(_RandomAccessReaderMappedBase, self).has_key(key)

        def value(self, key):
            '''Returns the value associated with the key.

            This method is provided for compatibility with the C++ API only;
            most users should use the Pythonic API.

            Args:
                key (str): The key.

            Returns:
                The value associated with the key.
            '''
            return super(_RandomAccessReaderMappedBase, self).value(key)

        def is_open(self):
            '''Indicates whether the table reader is open or not.

            This method is provided for compatibility with the C++ API only;
            most users should use the Pythonic API.

            Returns:
              True if the table reader is open, False otherwise.
            '''
            return super(_RandomAccessReaderMappedBase, self).is_open()

        def close(self):
            '''Closes the table.

            This method is provided for compatibility with the C++ API only;
            most users should use the Pythonic API.

            Returns:
                True if table is closed successfully, False otherwise.
            '''
            return super(_RandomAccessReaderMappedBase, self).close()

    class RandomAccessVectorReaderMapped(
            _RandomAccessReaderMappedBase,
            _kaldi_table.RandomAccessVectorReaderMapped):
        '''Mapped random access table reader for single precision vectors.'''
        pass

    class RandomAccessDoubleVectorReaderMapped(
            _RandomAccessReaderMappedBase,
            _kaldi_table.RandomAccessDoubleVectorReaderMapped):
        '''Mapped random access table reader for double precision vectors.'''
        pass

    class RandomAccessMatrixReaderMapped(
            _RandomAccessReaderMappedBase,
            _kaldi_table.RandomAccessMatrixReaderMapped):
        '''Mapped random access table reader for single precision matrices.'''
        pass

    class RandomAccessDoubleMatrixReaderMapped(
            _RandomAccessReaderMappedBase,
            _kaldi_table.RandomAccessDoubleMatrixReaderMapped):
        '''Mapped random access table reader for double precision matrices.'''
        pass

    class RandomAccessFloatReaderMapped(
            _RandomAccessReaderMappedBase,
            _kaldi_table.RandomAccessFloatReaderMapped):
        '''Mapped random access table reader for single precision floats.'''
        pass

    class DoubleVectorWriter(_WriterBase, _kaldi_table.DoubleVectorWriter):
        '''Table writer for double precision vectors.'''

        def write(self, key, value):
            '''Writes the `(key, value)` pair to the table.

            This method is provided for compatibility with the C++ API only;
            most users should use the Pythonic API.

            Overrides write to accept both DoubleVector and DoubleSubVector.

            Args:
                key (str): The key.
                value: The value.
            '''
            super(DoubleVectorWriter, self).write(key,
                                                  _matrix.DoubleVector(value))

    class DoubleMatrixWriter(_WriterBase, _kaldi_table.DoubleMatrixWriter):
        '''Table writer for double precision matrices.'''

        def write(self, key, value):
            '''Writes the `(key, value)` pair to the table.

            This method is provided for compatibility with the C++ API only;
            most users should use the Pythonic API.

            Overrides write to accept both DoubleMatrix and DoubleSubMatrix.

            Args:
                key (str): The key.
                value: The value.
            '''
            super(DoubleMatrixWriter, self).write(key,
                                                  _matrix.DoubleMatrix(value))

    class WaveWriter(_WriterBase, _kaldi_table.WaveWriter):
        '''Table writer for wave files.'''
        pass

    class PosteriorWriter(_WriterBase, _kaldi_table.PosteriorWriter):
        '''Table writer for frame posteriors.'''
        pass

    class GaussPostWriter(_WriterBase, _kaldi_table.GaussPostWriter):
        '''Table writer for Gaussian-level frame posteriors.'''
        pass

    class FstWriter(_WriterBase, _kaldi_table_ext.FstWriter):
        '''Table writer for FSTs over the tropical semiring.'''
        pass

    class LogFstWriter(_WriterBase, _kaldi_table_ext.LogFstWriter):
        '''Table writer for FSTs over the log semiring.'''
        pass

    class KwsIndexFstWriter(_WriterBase, _kaldi_table_ext.KwsIndexFstWriter):
        '''Table writer for FSTs over the KWS index semiring.'''
        pass

    class NnetExampleWriter(_WriterBase, _kaldi_table.NnetExampleWriter):
        '''Table writer for nnet examples.'''
        pass

    class RnnlmExampleWriter(_WriterBase, _kaldi_table.RnnlmExampleWriter):
        '''Table writer for RNNLM examples.'''
        pass

    class IntWriter(_WriterBase, _kaldi_table.IntWriter):
        '''Table writer for integers.'''
        pass

    class FloatWriter(_WriterBase, _kaldi_table.FloatWriter):
        '''Table writer for single precision floats.'''
        pass

    class DoubleWriter(_WriterBase, _kaldi_table.DoubleWriter):
        '''Table writer for double precision floats.'''
        pass

    class BoolWriter(_WriterBase, _kaldi_table.BoolWriter):
        '''Table writer for Booleans.'''
        pass

    class IntVectorVectorWriter(_WriterBase,
                                _kaldi_table.IntVectorVectorWriter):
        '''Table writer for sequences of integer sequences.'''
        pass

    class IntPairVectorWriter(_WriterBase, _kaldi_table.IntPairVectorWriter):
        '''Table writer for sequences of integer pairs.'''
        pass

    class FloatPairVectorWriter(_WriterBase,
                                _kaldi_table.FloatPairVectorWriter):
        '''Table writer for sequences of single precision float pairs.'''
        pass


################################################################################

This file is modified from the PyKaldi project https://github.com/pykaldi/pykaldi/blob/master/kaldi/util/table.py # # # Author:  <NAME> # Author:  <NAME> # # For detailed documentation of Kaldi tables, table readers/writers, table read/write specifiers, see `Kaldi I/O mechanisms`_ and `Kaldi I/O from a command-line perspective`_.  .. _Kaldi I/O mechanisms:    http://kaldi-asr.org/doc/io.html .. _Kaldi I/O from a command-line perspective:    http://kaldi-asr.org/doc/io_tut.html # TODO(fangjun): set the PYTHONPATH environment variable outside this script # to avoid set sys.path for every Python script ################################################################################ # Sequential Readers ################################################################################ Base class defining the Python API for sequential table readers. This class is used for reading objects sequentially from an archive or         script file. It implements the iterator protocol similar to how Python         implements iteration over dictionaries. Each iteration returns a `(key,         value)` pair from the table in sequential order.          Args:             rspecifier(str): Kaldi rspecifier for reading the table.                 If provided, the table is opened for reading.          Raises:             IOError: If opening the table for reading fails. Opens the table for reading.          Args:             rspecifier(str): Kaldi rspecifier for reading the table.                 If provided, the table is opened for reading.          Returns:             True if table is opened successfully, False otherwise.          Raises:             IOError: If opening the table for reading fails. Indicates whether the table reader is exhausted or not.          This method is provided for compatibility with the C++ API only;         most users should use the Pythonic API.          Returns:           True if the table reader is exhausted, False otherwise. Returns the current key.          This method is provided for compatibility with the C++ API only;         most users should use the Pythonic API.          Returns:             str: The current key. Deallocates the current value.          This method is provided as an optimization to save memory, for large         objects. Returns the current value.          This method is provided for compatibility with the C++ API only;         most users should use the Pythonic API.          Returns:             The current value. Advances the table reader.          This method is provided for compatibility with the C++ API only;         most users should use the Pythonic API. Indicates whether the table reader is open or not.          This method is provided for compatibility with the C++ API only;         most users should use the Pythonic API.          Returns:           True if the table reader is open, False otherwise. Closes the table.          This method is provided for compatibility with the C++ API only;         most users should use the Pythonic API.          Returns:             True if table is closed successfully, False otherwise. Sequential table reader for nnet chain examples. Sequential table reader for nnet examples. Sequential table reader for wave files. Sequential table reader for wave file headers. Sequential table reader for single precision matrices. Sequential table reader for single precision vectors. Sequential table reader for integer sequences. Sequential table reader for lattices. Sequential table reader for compact lattices. ################################################################################ # Random Access Readers ################################################################################ Base class defining the Python API for random access table readers. This class is used for randomly accessing objects in an archive or             script file. It implements `__contains__` and `__getitem__` methods to             provide a dictionary-like interface for accessing table entries. e.g.             `reader[key]` returns the `value` associated with the `key`.              Args:                 rspecifier(str): Kaldi rspecifier for reading the table.                     If provided, the table is opened for reading.              Raises:                 IOError: If opening the table for reading fails. Opens the table for reading.              Args:                 rspecifier(str): Kaldi rspecifier for reading the table.                     If provided, the table is opened for reading.              Returns:                 True if table is opened successfully, False otherwise.              Raises:                 IOError: If opening the table for reading fails. Checks whether the table has the key.              This method is provided for compatibility with the C++ API only;             most users should use the Pythonic API.              Args:                 key (str): The key.              Returns:               True if the table has the key, False otherwise. Returns the value associated with the key.              This method is provided for compatibility with the C++ API only;             most users should use the Pythonic API.              Args:                 key (str): The key.              Returns:                 The value associated with the key. Indicates whether the table reader is open or not.              This method is provided for compatibility with the C++ API only;             most users should use the Pythonic API.              Returns:               True if the table reader is open, False otherwise. Closes the table.              This method is provided for compatibility with the C++ API only;             most users should use the Pythonic API.              Returns:                 True if table is closed successfully, False otherwise. Random access table reader for nnet chain examples. Random access table reader for nnet examples. Random access table reader for wave files. Random access table reader for wave file headers. Random access table reader for single precision matrices. Random access table reader for single precision vectors. Random access table reader for integer sequences. Random access table reader for lattices. Random access table reader for compact lattices. ################################################################################ # Writers ################################################################################ Base class defining the additional Python API for table writers. This class is used for writing objects to an archive or script file. It             implements the `__setitem__` method to provide a dictionary-like             interface for writing table entries, e.g. `writer[key] = value` writes             the pair `(key, value)` to the table.              Args:                 wspecifier (str): Kaldi wspecifier for writing the table.                     If provided, the table is opened for writing.              Raises:                 IOError: If opening the table for writing fails. Opens the table for writing.              Args:                 wspecifier(str): Kaldi wspecifier for writing the table.                     If provided, the table is opened for writing.              Returns:                 True if table is opened successfully, False otherwise.              Raises:                 IOError: If opening the table for writing fails. Flushes the table contents to disk/pipe. Writes the `(key, value)` pair to the table.              This method is provided for compatibility with the C++ API only;             most users should use the Pythonic API.              Args:                 key (str): The key.                 value: The value. Indicates whether the table writer is open or not.              This method is provided for compatibility with the C++ API only;             most users should use the Pythonic API.              Returns:               True if the table writer is open, False otherwise. Closes the table.              This method is provided for compatibility with the C++ API only;             most users should use the Pythonic API.              Returns:                 True if table is closed successfully, False otherwise. Table writer for nnet chain examples. Table writer for single precision matrices. Table writer for single precision vectors. Table writer for single precision compressed matrices. Table writer for integer sequences. Table writer for lattices. Table writer for compact lattices. # TODO(fangjun): enable the following once other wrappers are added Sequential table reader for double precision vectors. Sequential table reader for double precision matrices. Sequential table reader for frame posteriors. Sequential table reader for Gaussian-level frame posteriors. Sequential table reader for FSTs over the tropical semiring. Sequential table reader for FSTs over the log semiring. Sequential table reader for FSTs over the KWS index semiring. Sequential table reader for RNNLM examples. Sequential table reader for integers. Sequential table reader for single precision floats. Sequential table reader for double precision floats. Sequential table reader for Booleans. Sequential table reader for sequences of integer sequences. Sequential table reader for sequences of integer pairs. Sequential table reader for sequences of single precision float pairs. Random access table reader for double precision vectors. Random access table reader for double precision matrices. Random access table reader for frame posteriors. Random access table reader for Gaussian-level frame posteriors. Random access table reader for FSTs over the tropical semiring. Random access table reader for FSTs over the log semiring. Random access table reader for FSTs over the KWS index semiring. Random access table reader for integers. Random access table reader for single precision floats. Random access table reader for double precision floats. Random access table reader for Booleans. Random access table reader for sequences of integer sequences. Random access table reader for sequences of integer pairs. Random access table reader for sequences of single precision float pairs. ################################################################################ # Mapped Random Access Readers ################################################################################ Base class defining the Python API for mapped random access table readers. This class is used for randomly accessing objects in an archive or             script file. It implements `__contains__` and `__getitem__` methods to             provide a dictionary-like interface for accessing table entries. If a             **map_rspecifier** is provided, the map is used for converting the keys             to the actual keys used to query the table, e.g. `reader[key]` returns             the `value` associated with the key `map[key]`. Otherwise, it works like             a random access table reader.              Args:                 table_rspecifier(str): Kaldi rspecifier for reading the table.                     If provided, the table is opened for reading.                 map_rspecifier (str): Kaldi rspecifier for reading the map.                     If provided, the map is opened for reading.              Raises:                 IOError: If opening the table or map for reading fails. Opens the table for reading.              Args:                 table_rspecifier(str): Kaldi rspecifier for reading the table.                     If provided, the table is opened for reading.                 map_rspecifier (str): Kaldi rspecifier for reading the map.                     If provided, the map is opened for reading.              Returns:                 True if table is opened successfully, False otherwise.              Raises:                 IOError: If opening the table or map for reading fails. Checks whether the table has the key.              This method is provided for compatibility with the C++ API only;             most users should use the Pythonic API.              Args:                 key (str): The key.              Returns:               True if the table has the key, False otherwise. Returns the value associated with the key.              This method is provided for compatibility with the C++ API only;             most users should use the Pythonic API.              Args:                 key (str): The key.              Returns:                 The value associated with the key. Indicates whether the table reader is open or not.              This method is provided for compatibility with the C++ API only;             most users should use the Pythonic API.              Returns:               True if the table reader is open, False otherwise. Closes the table.              This method is provided for compatibility with the C++ API only;             most users should use the Pythonic API.              Returns:                 True if table is closed successfully, False otherwise. Mapped random access table reader for single precision vectors. Mapped random access table reader for double precision vectors. Mapped random access table reader for single precision matrices. Mapped random access table reader for double precision matrices. Mapped random access table reader for single precision floats. Table writer for double precision vectors. Writes the `(key, value)` pair to the table.              This method is provided for compatibility with the C++ API only;             most users should use the Pythonic API.              Overrides write to accept both DoubleVector and DoubleSubVector.              Args:                 key (str): The key.                 value: The value. Table writer for double precision matrices. Writes the `(key, value)` pair to the table.              This method is provided for compatibility with the C++ API only;             most users should use the Pythonic API.              Overrides write to accept both DoubleMatrix and DoubleSubMatrix.              Args:                 key (str): The key.                 value: The value. Table writer for wave files. Table writer for frame posteriors. Table writer for Gaussian-level frame posteriors. Table writer for FSTs over the tropical semiring. Table writer for FSTs over the log semiring. Table writer for FSTs over the KWS index semiring. Table writer for nnet examples. Table writer for RNNLM examples. Table writer for integers. Table writer for single precision floats. Table writer for double precision floats. Table writer for Booleans. Table writer for sequences of integer sequences. Table writer for sequences of integer pairs. Table writer for sequences of single precision float pairs. ################################################################################