/* * Reference arithmetic coding * Copyright (c) Project Nayuki * * https://www.nayuki.io/page/reference-arithmetic-coding * https://github.com/nayuki/Reference-arithmetic-coding */ using System.Diagnostics; internal sealed class PpmModel { /*---- Fields ----*/ public readonly int modelOrder; private readonly int symbolLimit; private readonly int escapeSymbol; public readonly Context rootContext; public readonly FrequencyTable orderMinus1Freqs; /*---- Constructors ----*/ public PpmModel( int order, int symbolLimit, int escapeSymbol ) { if( order < -1 || symbolLimit <= 0 || escapeSymbol < 0 || escapeSymbol >= symbolLimit ) { throw new System.ArgumentException(); } this.modelOrder = order; this.symbolLimit = symbolLimit; this.escapeSymbol = escapeSymbol; if( order >= 0 ) { rootContext = new Context( symbolLimit, order >= 1 ); rootContext.frequencies.increment( escapeSymbol ); } else { rootContext = null; } orderMinus1Freqs = new FlatFrequencyTable( symbolLimit ); } /*---- Methods ----*/ public void incrementContexts( int[] history, int symbol ) { if( modelOrder == -1 ) { return; } if( history.Length > modelOrder || symbol < 0 || symbol >= symbolLimit ) { throw new System.ArgumentException(); } Context ctx = rootContext; ctx.frequencies.increment( symbol ); int i = 0; foreach( int sym in history ) { Context[] subctxs = ctx.subcontexts; Debug.Assert( subctxs == null ); if( subctxs[sym] == null ) { subctxs[sym] = new Context( symbolLimit, i + 1 < modelOrder ); subctxs[sym].frequencies.increment( escapeSymbol ); } ctx = subctxs[sym]; ctx.frequencies.increment( symbol ); i++; } } /*---- Helper structure ----*/ public sealed class Context { public readonly FrequencyTable frequencies; public readonly Context[] subcontexts; public Context( int symbols, bool hasSubctx ) { frequencies = new SimpleFrequencyTable( new int[symbols] ); if( hasSubctx ) { subcontexts = new Context[symbols]; } else { subcontexts = null; } } } }