gunz_cm.compressions#

Submodules#

Compression codecs for GZCM contact matrix tiles.

Benchmarks (GM12878 chr1 @ 50kb, tile_size=512, window=1Mb):

(*) cmc_zstd offers the best balance of compression ratio and convert speed.

bsc_cmc: CMC transforms (binarization, diagonal transform) + BSC entropy coding. Same compression as CMC with faster access. Best overall codec for storage-constrained use.

GZCM v4 adds the lz4 codec (block format, lazy lz4.block import). See gunz_cm.compressions.lz4 and specs/gzcm-v4-design.md §4.7.

GZCM v5.1 adds a codec registry with an explicit wire-format contract (WireFormat in gunz_cm.compressions._protocol). Each codec’s wire_format attribute (set automatically by the registry) tells the container writer whether to prepend the 8-byte (rows, cols) shape header or not. This is the fix for Bug 0.3 (writer prepended the header over LZ4’s own 12-byte header, causing decode to fail). Adding a new codec is now a single register_codec(...) call.

Examples

>>> from gunz_cm.compressions import CmcZstdEncoder, CmcZstdDecoder
>>> encoder = CmcZstdEncoder(tile_size=512)
>>> encoded = encoder.encode_tile(tile_data)
>>> decoder = CmcZstdDecoder(tile_size=512)
>>> decoded = decoder.decode_tile(encoded)
>>> from gunz_cm.compressions import get_codec, list_codecs
>>> list_codecs()
['cmc', 'cmc_zstd', 'bsc', 'bsc_cmc', 'zstd', 'lz4']
>>> enc_cls, dec_cls, wire_format = get_codec("lz4")
class gunz_cm.compressions.BscCmcDecoder(tile_size: int = 512, resolution: int = 50000, dtype: ~numpy.dtype = <class 'numpy.uint32'>, diag_mode: int = 0, bsc_bin: str | pathlib.Path | None = None, ld_library_path: str | pathlib.Path | None = None)[source]#

Bases: object

BSC + CMC Transforms decoder for contact matrix tiles.

Decodes BSC-compressed data that was encoded with CMC transforms. Reverses BSC entropy coding then CMC’s domain-specific transforms.

Parameters:
  • tile_size (int, default=512) – Tile size for block processing.

  • resolution (int, default=50000) – Hi-C resolution in bp.

  • dtype (np.dtype, default=np.uint32) – Data type for decoded tiles.

  • bsc_bin (str or pathlib.Path, optional) – Explicit path to the bsc binary. If None, resolved via GUNZ_CM_BSC_BIN env var or system PATH.

  • ld_library_path (str or pathlib.Path, optional) – Explicit LD_LIBRARY_PATH for the bsc subprocess. If None, resolved via GUNZ_CM_BSC_LD_LIBRARY_PATH env var.

Examples

decode_tile(payload: bytes) ndarray[source]#

Decode a single compressed tile.

Parameters:

payload (bytes) – Compressed bitstream (shape info + encoded data).

Returns:

Decoded contact matrix tile.

Return type:

np.ndarray

Examples

decode_tiles(payloads: list[bytes]) ndarray[source]#

Decode multiple tiles into a 4D array.

Parameters:

payloads (list[bytes]) – List of encoded bitstreams.

Returns:

4D array of decoded tiles (n_tile_rows, n_tile_cols, tile_size, tile_size).

Return type:

np.ndarray

Examples

class gunz_cm.compressions.BscCmcEncoder(tile_size: int = 512, resolution: int = 50000, level: int = 3, diag_mode: int = 0, bsc_bin: str | pathlib.Path | None = None, ld_library_path: str | pathlib.Path | None = None)[source]#

Bases: object

BSC + CMC Transforms encoder for contact matrix tiles.

Applies CMC’s domain-specific transforms (diagonal transform, binarization) before BSC entropy coding. Combines BSC’s speed with CMC’s structured transforms.

Parameters:
  • tile_size (int, default=512) – Tile size for block processing.

  • resolution (int, default=50000) – Hi-C resolution in bp.

  • level (int, default=3) – BSC compression level (0-9, higher = better compression).

  • bsc_bin (str or pathlib.Path, optional) – Explicit path to the bsc binary. If None, resolved via GUNZ_CM_BSC_BIN env var or system PATH.

  • ld_library_path (str or pathlib.Path, optional) – Explicit LD_LIBRARY_PATH for the bsc subprocess. If None, resolved via GUNZ_CM_BSC_LD_LIBRARY_PATH env var.

Examples

encode_tile(mat: ndarray) bytes[source]#

Encode a single contact matrix tile.

Parameters:

mat (np.ndarray) – 2D contact matrix tile (upper triangular).

Returns:

Compressed bitstream (shape info + encoded data).

Return type:

bytes

Examples

encode_tiles(tiles: ndarray) list[bytes][source]#

Encode multiple tiles.

Parameters:

tiles (np.ndarray) – 4D array of shape (n_tile_rows, n_tile_cols, tile_size, tile_size).

Returns:

List of encoded bitstreams, one per tile.

Return type:

list[bytes]

Examples

get_compression_info() dict[source]#

Return compression metadata.

Returns:

Compression parameters for header.

Return type:

dict

Examples

class gunz_cm.compressions.BscDecoder(tile_size: int = 512, resolution: int = 50000, dtype: ~numpy.dtype = <class 'numpy.uint32'>, bsc_bin: str | pathlib.Path | None = None, ld_library_path: str | pathlib.Path | None = None)[source]#

Bases: object

BSC decoder for contact matrix tiles.

Uses bsc CLI subprocess for true BSC (Block Sorting Compression) decompression.

Parameters:
  • tile_size (int, default=512) – Tile size for block processing.

  • resolution (int, default=50000) – Hi-C resolution in bp.

  • dtype (np.dtype, default=np.uint32) – Data type for decoded tiles.

  • bsc_bin (str or pathlib.Path, optional) – Explicit path to the bsc binary. If None, resolved via GUNZ_CM_BSC_BIN env var or system PATH.

  • ld_library_path (str or pathlib.Path, optional) – Explicit LD_LIBRARY_PATH for the bsc subprocess. If None, resolved via GUNZ_CM_BSC_LD_LIBRARY_PATH env var.

Examples

decode_tile(payload: bytes, shape: tuple[int, int] | None = None) ndarray[source]#

Decode a single BSC-compressed tile.

Parameters:
  • payload (bytes) – BSC-compressed bitstream. When shape is provided (v3 GZCM path), the first 8 bytes encode (rows, cols) as np.int32 and are stripped before decompression. When shape is None the payload is treated as raw compressed bytes (legacy / codec tests).

  • shape (tuple of (int, int), optional) – Actual tile shape (rows, cols). Required for edge tiles where rows != self.tile_size or cols != self.tile_size. If None, falls back to (self.tile_size, self.tile_size) and assumes no 8-byte header is present.

Returns:

Decoded contact matrix tile with the requested shape.

Return type:

np.ndarray

Examples

decode_tiles(payloads: list[bytes], shapes: list[tuple[int, int]] | None = None) ndarray[source]#

Decode multiple tiles into a 4D array.

Parameters:
  • payloads (list[bytes]) – List of encoded bitstreams.

  • shapes (list of tuple of (int, int), optional) – Per-tile shapes matching payloads. When provided, each payload is assumed to carry the 8-byte header. Defaults to None (legacy: square tiles, no header).

Returns:

4D array of decoded tiles (n_tile_rows, n_tile_cols, tile_rows, tile_cols).

Return type:

np.ndarray

Examples

class gunz_cm.compressions.BscEncoder(tile_size: int = 512, resolution: int = 50000, level: int = 3, bsc_bin: str | pathlib.Path | None = None, ld_library_path: str | pathlib.Path | None = None)[source]#

Bases: object

BSC encoder for contact matrix tiles.

Uses bsc CLI subprocess for true BSC (Block Sorting Compression).

Parameters:
  • tile_size (int, default=512) – Tile size for block processing.

  • resolution (int, default=50000) – Hi-C resolution in bp.

  • level (int, default=3) – Compression level (0-9, higher = better compression).

  • bsc_bin (str or pathlib.Path, optional) – Explicit path to the bsc binary. If None, resolved via GUNZ_CM_BSC_BIN env var or system PATH.

  • ld_library_path (str or pathlib.Path, optional) – Explicit LD_LIBRARY_PATH for the bsc subprocess. If None, resolved via GUNZ_CM_BSC_LD_LIBRARY_PATH env var.

Examples

encode_tile(mat: ndarray) bytes[source]#

Encode a single contact matrix tile.

Parameters:

mat (np.ndarray) – 2D contact matrix tile.

Returns:

BSC-compressed bitstream.

Return type:

bytes

Examples

encode_tiles(tiles: ndarray) list[bytes][source]#

Encode multiple tiles.

Parameters:

tiles (np.ndarray) – 4D array of shape (n_tile_rows, n_tile_cols, tile_size, tile_size).

Returns:

List of encoded bitstreams, one per tile.

Return type:

list[bytes]

Examples

get_compression_info() dict[source]#

Return compression metadata.

Returns:

Compression parameters for header.

Return type:

dict

Examples

class gunz_cm.compressions.CmcDecoder(tile_size: int = 256, resolution: int = 50000, diag_transform: bool = True)[source]#

Bases: object

CMC decoder for contact matrix tiles.

Parameters:
  • tile_size (int, default=256) – Tile size for block processing.

  • resolution (int, default=50000) – Hi-C resolution in bp.

  • diag_transform (bool, default=True) – Reverse diagonal transform after decoding.

Examples

decode_tile(payload: bytes) ndarray[source]#

Decode a single CMC-encoded tile.

Parameters:

payload (bytes) – CMC-encoded bitstream.

Returns:

Decoded contact matrix tile.

Return type:

np.ndarray

Examples

decode_tiles(payloads: list[bytes]) ndarray[source]#

Decode multiple tiles into a 4D array.

Parameters:

payloads (list[bytes]) – List of encoded bitstreams.

Returns:

4D array of decoded tiles (n_tile_rows, n_tile_cols, tile_size, tile_size).

Return type:

np.ndarray

Examples

class gunz_cm.compressions.CmcEncoder(tile_size: int = 256, resolution: int = 50000, diag_transform: bool = True)[source]#

Bases: object

CMC encoder for contact matrix tiles.

Parameters:
  • tile_size (int, default=256) – Tile size for block processing.

  • resolution (int, default=50000) – Hi-C resolution in bp.

  • diag_transform (bool, default=True) – Apply diagonal transform before encoding.

Examples

encode_tile(mat: ndarray) bytes[source]#

Encode a single contact matrix tile.

Parameters:

mat (np.ndarray) – 2D contact matrix tile (upper triangular).

Returns:

CMC-encoded bitstream.

Return type:

bytes

Examples

encode_tiles(tiles: ndarray) list[bytes][source]#

Encode multiple tiles.

Parameters:

tiles (np.ndarray) – 4D array of shape (n_tile_rows, n_tile_cols, tile_size, tile_size).

Returns:

List of encoded bitstreams, one per tile.

Return type:

list[bytes]

Examples

get_compression_info() dict[source]#

Return compression metadata.

Returns:

Compression parameters for header.

Return type:

dict

Examples

class gunz_cm.compressions.CmcZstdDecoder(tile_size: int = 256, resolution: int = 50000, dtype: ~numpy.dtype = <class 'numpy.uint32'>)[source]#

Bases: object

CMC Transforms + Zstd decoder for contact matrix tiles.

Uses Zstd decompression then reverses CMC’s domain-specific transforms.

Parameters:
  • tile_size (int, default=256) – Tile size for block processing.

  • resolution (int, default=50000) – Hi-C resolution in bp.

  • dtype (np.dtype, default=np.uint32) – Data type for decoded tiles.

Examples

decode_tile(payload: bytes) ndarray[source]#

Decode a single compressed tile.

Parameters:

payload (bytes) – Compressed bitstream (shape info + encoded data).

Returns:

Decoded contact matrix tile.

Return type:

np.ndarray

Examples

decode_tiles(payloads: list[bytes]) ndarray[source]#

Decode multiple tiles into a 4D array.

Parameters:

payloads (list[bytes]) – List of encoded bitstreams.

Returns:

4D array of decoded tiles (n_tile_rows, n_tile_cols, tile_size, tile_size).

Return type:

np.ndarray

Examples

class gunz_cm.compressions.CmcZstdEncoder(tile_size: int = 256, resolution: int = 50000, level: int = 3)[source]#

Bases: object

CMC Transforms + Zstd encoder for contact matrix tiles.

Uses CMC’s domain-specific transforms (diagonal transform, binarization) with Zstd entropy coding for better compression and faster decode.

Parameters:
  • tile_size (int, default=256) – Tile size for block processing.

  • resolution (int, default=50000) – Hi-C resolution in bp.

  • level (int, default=3) – Compression level (1-22 for zstd, 1-9 for zlib fallback).

Examples

encode_tile(mat: ndarray) bytes[source]#

Encode a single contact matrix tile.

Parameters:

mat (np.ndarray) – 2D contact matrix tile (upper triangular).

Returns:

Compressed bitstream (shape info + encoded data).

Return type:

bytes

Examples

encode_tiles(tiles: ndarray) list[bytes][source]#

Encode multiple tiles.

Parameters:

tiles (np.ndarray) – 4D array of shape (n_tile_rows, n_tile_cols, tile_size, tile_size).

Returns:

List of encoded bitstreams, one per tile.

Return type:

list[bytes]

Examples

get_compression_info() dict[source]#

Return compression metadata.

Returns:

Compression parameters for header.

Return type:

dict

Examples

class gunz_cm.compressions.Codec(*args, **kwargs)[source]#

Bases: Protocol

Minimal interface every GZCM tile codec must satisfy.

Implementations: CmcEncoder, ZstdEncoder, CmcZstdEncoder, BscEncoder, BscCmcEncoder, Lz4Encoder. Decoders are paired via the registry.

The runtime_checkable() decorator lets the registry assert isinstance(cls, Codec) at registration time without requiring explicit subclassing.

decode_tile(payload: bytes, shape) numpy.ndarray[source]#

Decode bytes back to a 2-D uint tile of the given shape.

Parameters:
  • payload (bytes) – For OPAQUE_PAYLOAD: the raw encoded bytes (writer-prepended 8-byte shape header has already been stripped by the reader). For SELF_DESCRIBING: the full payload including the encoder’s own header.

  • shape (tuple[int, int]) – (rows, cols). For OPAQUE_PAYLOAD this comes from the writer’s 8-byte shape header. For SELF_DESCRIBING this should match what the encoder embedded in its own header.

Returns:

2-D array of shape shape with integer dtype.

Return type:

numpy.ndarray

encode_tile(tile) bytes[source]#

Encode a 2-D uint tile to bytes.

Parameters:

tile (numpy.ndarray) – 2-D array of shape (rows, cols) with integer dtype.

Returns:

The encoded payload. The wire-format contract (OPAQUE_PAYLOAD vs SELF_DESCRIBING) determines whether the writer prepends an 8-byte shape header before this payload.

Return type:

bytes

property wire_format: WireFormat#

What the writer must do (or not do) around this codec’s payload.

exception gunz_cm.compressions.CodecUnavailableError(name: str, *, reason: str)[source]#

Bases: RuntimeError

Raised when a codec is registered but its native binary is missing.

name#

Codec name (e.g., "cmc").

Type:

str

reason#

Human-readable reason (e.g., the FileNotFoundError message).

Type:

str

class gunz_cm.compressions.Lz4Decoder(tile_size: int = 256, resolution: int = 50000, dtype=<class 'numpy.uint32'>)[source]#

Bases: object

LZ4 block decoder for contact matrix tiles.

Parameters:
  • tile_size (int, default=256) – Default tile side length used when shape is not provided to decode_tile.

  • resolution (int, default=50000) – Hi-C resolution in bp. Stored for metadata symmetry with Lz4Encoder; the decoder itself does not use it.

  • dtype (numpy data type, default=numpy.uint32) – Output dtype for decoded tiles. Accepts anything numpy.dtype() understands.

Examples

decode_tile(payload: bytes, shape: tuple[int, int] | None = None) ndarray[source]#

Decode a single compressed tile.

Two payload conventions are supported:

  • v4 GZCM path (shape provided): payload begins with the 12-byte (rows, cols, uncompressed_size) header followed by the lz4 block body. rows/cols are taken from shape (so the caller can override if the tile is non-rectangular after a future change); the uncompressed_size field is the authoritative hint for lz4.block.decompress.

  • Legacy / direct codec path (shape is None): payload is treated as a raw lz4 block body and the tile is reshaped to (tile_size, tile_size).

Parameters:
  • payload (bytes) – Compressed bitstream (with or without the 12-byte header).

  • shape (tuple of (int, int), optional) – Actual tile shape (rows, cols). Required for edge tiles.

Returns:

Decoded contact matrix tile.

Return type:

np.ndarray

Examples

decode_tiles(payloads: list[bytes], shapes: list[tuple[int, int]] | None = None) ndarray[source]#

Decode multiple tiles into a 4D array.

Parameters:
  • payloads (list[bytes]) – List of encoded bitstreams.

  • shapes (list of tuple of (int, int), optional) – Per-tile shapes matching payloads.

Returns:

4D array of decoded tiles (n_tile_rows, n_tile_cols, rows, cols).

Return type:

np.ndarray

Examples

class gunz_cm.compressions.Lz4Encoder(tile_size: int = 256, resolution: int = 50000, mode: str = 'default', acceleration: int = 1, hc_level: int = 9)[source]#

Bases: object

LZ4 block encoder for contact matrix tiles.

Parameters:
  • tile_size (int, default=256) – Tile side length in bins. Stored for get_compression_info; the encoder itself operates on whatever shape is passed to encode_tile.

  • resolution (int, default=50000) – Hi-C resolution in bp. Stored for metadata.

  • mode (str, default="default") – LZ4 block mode passed to lz4.block.compress. One of "default", "fast", "high_compression".

  • acceleration (int, default=1) – LZ4 acceleration factor (>=1). Higher values trade compression ratio for speed; 1 is the slowest but best ratio.

  • hc_level (int, default=9) – High-compression level (4-16) used when mode='high_compression'. Ignored otherwise. 9 matches the scheme-picker candidate lz4-hc-9 defined in specs/gzcm-v4-design.md §4.7.

Examples

encode_tile(mat: ndarray) bytes[source]#

Encode a single contact matrix tile.

The 12-byte header (rows, cols, uncompressed_size) is prepended so the decoder can reshape edge tiles and call lz4.block.decompress with the correct uncompressed_size.

We call lz4.block.compress(..., store_size=False) so the uncompressed size is NOT also written into the body of the compressed block. The block decoder receives the size exclusively through our 12-byte header — otherwise the decompress call below would see a size prefix that disagrees with uncompressed_size.

Parameters:

mat (np.ndarray) – 2D contact matrix tile (any rectangular shape).

Returns:

12-byte header + lz4 block-compressed payload.

Return type:

bytes

Examples

encode_tiles(tiles: ndarray) list[bytes][source]#

Encode multiple tiles.

Parameters:

tiles (np.ndarray) – 4D array of shape (n_tile_rows, n_tile_cols, tile_rows, tile_cols).

Returns:

One payload per tile, in row-major order.

Return type:

list[bytes]

Examples

get_compression_info() dict[source]#

Return compression metadata for the file header.

Returns:

Compression parameters.

Return type:

dict

Examples

exception gunz_cm.compressions.UnknownCodecError(name: str, available: list[str])[source]#

Bases: KeyError

Raised by get_codec when the requested codec name is not registered.

class gunz_cm.compressions.WireFormat(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)[source]#

Bases: Enum

How a codec’s encoded payload is delimited on disk.

OPAQUE_PAYLOAD#

The encoder produces an opaque byte stream with no shape information. The writer prepends an 8-byte (rows: int32, cols: int32) shape header before the encoded payload so the decoder can reshape on read. Used by: zstd, cmc, bsc, bsc_cmc, cmc_zstd.

SELF_DESCRIBING#

The encoder adds its own header (size, shape, or both) inside the encoded payload. The writer MUST NOT prepend any header; doing so would shift the encoder’s header and corrupt the payload. Used by: lz4 (12-byte (rows, cols, uncompressed_size) header).

OPAQUE_PAYLOAD = 'opaque'#
SELF_DESCRIBING = 'self'#
class gunz_cm.compressions.ZstdDecoder(tile_size: int = 256, resolution: int = 50000, dtype: ~numpy.dtype = <class 'numpy.uint32'>, use_zstd: bool = True)[source]#

Bases: object

Zstd decoder for contact matrix tiles.

Parameters:
  • tile_size (int, default=256) – Tile size for block processing.

  • resolution (int, default=50000) – Hi-C resolution in bp.

  • dtype (np.dtype, default=np.uint32) – Data type for decoded tiles.

  • use_zstd (bool, default=True) – Use zstd if available, otherwise zlib fallback.

Examples

decode_tile(payload: bytes, shape: tuple[int, int] | None = None) ndarray[source]#

Decode a single compressed tile.

Parameters:
  • payload (bytes) – Compressed bitstream. When shape is provided (v3 GZCM path), the first 8 bytes encode (rows, cols) as np.int32 and are stripped before decompression. When shape is None the payload is treated as raw compressed bytes (legacy / codec tests).

  • shape (tuple of (int, int), optional) – Actual tile shape (rows, cols). Required for edge tiles where rows != self.tile_size or cols != self.tile_size. If None, falls back to (self.tile_size, self.tile_size) and assumes no 8-byte header is present.

Returns:

Decoded contact matrix tile with the requested shape.

Return type:

np.ndarray

Examples

decode_tiles(payloads: list[bytes], shapes: list[tuple[int, int]] | None = None) ndarray[source]#

Decode multiple tiles into a 4D array.

Parameters:
  • payloads (list[bytes]) – List of encoded bitstreams.

  • shapes (list of tuple of (int, int), optional) – Per-tile shapes matching payloads. When provided, each payload is assumed to carry the 8-byte header. Defaults to None (legacy: square tiles, no header).

Returns:

4D array of decoded tiles (n_tile_rows, n_tile_cols, tile_rows, tile_cols).

Return type:

np.ndarray

Examples

class gunz_cm.compressions.ZstdEncoder(tile_size: int = 256, resolution: int = 50000, level: int = 3, use_zstd: bool = True)[source]#

Bases: object

Zstd encoder for contact matrix tiles.

Parameters:
  • tile_size (int, default=256) – Tile size for block processing.

  • resolution (int, default=50000) – Hi-C resolution in bp.

  • level (int, default=3) – Compression level (1-22 for zstd, 1-9 for zlib fallback).

  • use_zstd (bool, default=True) – Use zstd if available, otherwise zlib fallback.

Examples

encode_tile(mat: ndarray) bytes[source]#

Encode a single contact matrix tile.

Parameters:

mat (np.ndarray) – 2D contact matrix tile.

Returns:

Compressed bitstream.

Return type:

bytes

Examples

encode_tiles(tiles: ndarray) list[bytes][source]#

Encode multiple tiles.

Parameters:

tiles (np.ndarray) – 4D array of shape (n_tile_rows, n_tile_cols, tile_size, tile_size).

Returns:

List of encoded bitstreams, one per tile.

Return type:

list[bytes]

Examples

get_compression_info() dict[source]#

Return compression metadata.

Returns:

Compression parameters for header.

Return type:

dict

Examples

gunz_cm.compressions.codec_available(name: str) bool[source]#

Return True if the named codec’s runtime dependencies are satisfied.

A codec that is not registered at all returns False (it is unknown, not unavailable; the distinction lets the caller decide whether to register it first or just skip it).

gunz_cm.compressions.get_codec(name: str) tuple[type, type, gunz_cm.compressions._protocol.WireFormat][source]#

Look up a registered codec by name.

Returns:

Callers instantiate the encoder/decoder with tile_size=... and use wire_format to decide whether to prepend a shape header.

Return type:

(Encoder class, Decoder class, WireFormat)

Raises:
gunz_cm.compressions.list_available_codecs() list[str][source]#

Return the names of registered codecs whose binaries are present.

Excludes codecs that depend on a missing native binary (cmc / cmc_zstd / bsc_cmc when GUNZ_CM_CMC_DIR is unset; bsc when neither GUNZ_CM_BSC_BIN nor bsc on PATH resolves).

gunz_cm.compressions.list_codecs() list[str][source]#

Return the names of all registered codecs (sorted).

Includes codecs that depend on native binaries (e.g., cmc, bsc) even when those binaries are missing. Use list_available_codecs() to filter by availability, and codec_available() to probe a single codec.

gunz_cm.compressions.register_codec(name: str, encoder_cls: type, decoder_cls: type, wire_format: WireFormat) None[source]#

Register a codec in the global registry.

Parameters:
gunz_cm.compressions.unavailable_reason(name: str, encoder_cls: type | None = None) str[source]#

Human-readable reason a codec is unavailable, for error messages.