{ Win text АРИФМЕТИЧЕСКОЕ кодирование. СТАТИЧЕСКИЙ МЕТОД. Основные процедуры. Реализация базовых функций для статического АРИФМЕТИЧЕСКОГО кодирования 1) Инициализация диапазона - "InitLargeRange". 2) Вычисление статистики символов для буфера - "CountSymbols". 3) Заполнение диапазонов символов согласно статистике - "CreateRanges". 4) Кодирование. Вычисление нового значения диапазона по символу - "EncodeSymbol". 5) Декодирование. Вычисление символа и нового значения кода - "DecodeSymbol". В этот файл вынесены основные функции статического АРИФМЕТИЧЕСКОГО кодирования. } {--------------------------------------------------------------------------- (c) Copyright Aleksandrov O.E., 2010 Molecular Physics department, USTU, Ekaterinsburg, K-2, 620002, RUSSIA phone 375-41-46 E-mail: aleks@dpt.ustu.ru ----------------------------------------------------------------------------} unit AriBase; interface USES SysUtils , AriTypes , VeryLongArithmetic { модуль арифметических операций со сверхдлинными беззнаковыми целыми - надож чем-то диапазоны записывать?} ; { Задание начального значения диапазона LargeRange [0..<максимально допустимое значение>) для процесса кодирования } procedure InitLargeRange(var LargeRange:tAriVeryLargeRange); { подсчет числа символов в буфере Buffer, возвращает массив счетчиков StatisticData для каждого символа } procedure CountSymbols( const Buffer; Size:tBufferIndex; var StatisticData:tAriStatistics; AccumulateStatistic:boolean=FALSE ); { корректирует статистику, выравнивая ТОТАЛ на степень двойки в ближайшую большую сторону} procedure AlignStatistic(var StatisticData:tAriStatistics); { Вычисление диапазонов RangeData для символов по статистике StatisticData. ПРИ КОДИРОВАНИИ. ВОЗВРАЩАЕТ: число НЕнулевых статистик; } function CreateRanges( const StatisticData:tAriStatistics; out RangeData:tAriRanges ):tSymbolCount; overload; { Вычисление диапазонов RangeData для символов по статистике StatisticData ПРИ ДЕКОДИРОВАНИИ. } procedure CreateRanges( const StatisticData:tAriStatistics; out RangeData:tAriRangesForDecode { для кодирования, содержит доп.данные по диапазонам приведенные к LargeRange} );overload; { Кодирование символа. Сужение диапазона LargeRange для диапазона символа SymbolRange ВОЗВРАЩАЕТ: TRUE - вычислено успешно; FALSE - невозможно вычислить, ибо потеря точности } function EncodeSymbol( var LargeRange:tAriVeryLargeRange; { текущий диапазон } const Ranges:tAriRanges; { данные по диапазонам символов } Symbol:tSymbol { символ для кодирования} ):boolean; { Вычисление символа Symbol и нового значения кода Code при декодировании для статистики Ranges ВОЗВРАЩАЕТ: TRUE - вычислено успешно; FALSE - невозможно вычислить, ибо все уже декодировано } function DecodeSymbol( var Code:tAriVeryLargeCode; { код для декодирования } const Ranges:tAriRangesForDecode; { данные по диапазонам символов } out Symbol:tSymbol { декодированный символ } ):boolean; { Определяет является ли число степенью двойки. Возвращает n (степень 2) или 0 - число НЕ степень двойки } function PowerOf2(X:LongWord):byte; implementation { Возвращает номер старшего бита=1 } function MostSignificantBit(X:LongWord):byte; register; asm bsr eax,eax jnz @End xor eax,eax @End: end; (*function MostSignificantBit(X:LongWord):byte; var l,n:byte; begin l:=0; Result:=32; while l0 then Result:=Result-1; end; *) { Определяет является ли число степенью двойки. Возвращает n (степень 2) или 0 - число НЕ степень двойки } function PowerOf2(X:LongWord):byte; register; asm bsr edx,eax bsf eax,eax cmp eax, edx je @End xor eax,eax @End: end; (*function PowerOf2(X:LongWord):byte; var l,n:byte; begin l:=0; Result:=31; if (1 shl Result) = X then begin Exit; end; while l) } procedure InitLargeRange(var LargeRange:tAriVeryLargeRange); begin LargeRange.EncodedSymbolCount:=0; vlaZero(LargeRange.Start); vlaAssign(cAriVeryLongUIntMaxValue,LargeRange.Length); {!!!! НЕ cVeryLongUIntMaxValue !!!, ибо некоторую длину (32 бита) резервируем под гарантии отсутствия переполнения при операциях сложения и умножения с/на 32-битное целое} end; { Подсчет числа символов в буфере Buffer, возвращает массив счетчиков StatisticData для каждого символа. Если AccumulateStatistic=TRUE - счетчики символов в StatisticData НЕ обнуляются. } procedure CountSymbols( const Buffer; { буфер с данными для упаковки} Size:tBufferIndex; { размер буфера в символах } var StatisticData:tAriStatistics; { массив статистики символов } AccumulateStatistic:boolean=FALSE { опция, позволяющая досчитывать статистику с нескольких буферов} ); var n:tBufferIndex; x:tSymbol; begin if not AccumulateStatistic then for x:=Low(x) to High(x) do StatisticData[x]:=0; if Size=0 then Exit; for n:=0 to Size-1 do begin Inc(StatisticData[tBuffer(Buffer)[n]]); end; end; { Выравниваем полную статистику (Total.SymbolCount) на степень двойки в ближайшую БОЛЬШУЮ сторону} procedure AlignStatistic(var StatisticData:tAriStatistics); var Total, Total1, Total2, TotalPwr2:tFriquency; x:tSymbol; begin Total:=0; for x:=Low(x) to High(x) do begin Inc(Total,StatisticData[x]); end; if PowerOf2(Total)=0 then begin TotalPwr2:=1 shl (MostSignificantBit(Total)+1); Total1:=0; Total2:=(Total shr 2); for x:=Low(x) to High(x) do if StatisticData[x]>0 then begin StatisticData[x]:=(longword(StatisticData[x])*TotalPwr2+Total2) div Total; Inc(Total1,StatisticData[x]); end; while Total10 then begin Inc(StatisticData[x]); Inc(Total1); if Total1=TotalPwr2 then break; end; end; while Total1>TotalPwr2 do begin for x:=Low(x) to High(x) do if StatisticData[x]>1 then begin Dec(StatisticData[x]); Dec(Total1); if Total1=TotalPwr2 then break; end; end; end; end; { Вычисление диапазонов RangeData для символов по статистике StatisticData. ПРИ КОДИРОВАНИИ. ВОЗВРАЩАЕТ: число НЕнулевых статистик; } function CreateRanges( const StatisticData:tAriStatistics; { данные статистики } out RangeData:tAriRanges { данные диапазонов для кодирования} ):tSymbolCount; var x:tSymbol; Total:tFriquency; begin Result:=0; Total:=0; RangeData.MaxCounter:=0; for x:=Low(x) to High(x) do begin { для каждого символа } if StatisticData[x]>0 then Inc(Result); {считаем НЕнулевые статистики } with RangeData.Ranges[x] do begin { для диапазона символа } Start:=Total; { устанавливаем начала равным количеству ВСЕХ предыдущих символов } Length:=StatisticData[x]; { устанавливаем длину равной количеству даных символов } Inc(Total, Length); if RangeData.MaxCounter0 then begin with RangeData do begin Symbols[i]:=x; {записываем символ в Symbols } Starts[i]:=totl; { начало интервала ЭТОГО символа } p:=StatisticData[x]; Lengths[i]:=p; { длина интервала ЭТОГО символа } Inc(totl, p); Inc(i); end; end; end; RangeData.LastSymbolIndex:=i-1; RangeData.Total.SymbolCount:=totl; RangeData.Total.PowerOf2:=PowerOf2(totl); RangeData.Total.MostSignificantDigitIndex:=vlaMostSignificantDigitIndex(pVeryLongUInt(@RangeData.Total.SymbolCount)^,(SizeOf(RangeData.Total.SymbolCount) div SizeOf(tDigit))-1); end; { КОДИРОВАНИЕ символа. Сужение диапазона LargeRange для диапазона символа SymbolRange ВОЗВРАЩАЕТ: TRUE - вычислено успешно; FALSE - невозможно вычислить, ибо потеря точности } function EncodeSymbol( var LargeRange:tAriVeryLargeRange; { текущий диапазон } {Состоит из EncodedSymbolCount - счетчик закодированных символов. 16-битное целое; Start - начало интервала в отрезке [0..cAriVeryLongUIntMaxValue); Length - длина интервала в отрезке [0..cAriVeryLongUIntMaxValue); cAriVeryLongUIntMaxValue = 2^n, например, при 1024 битной арифметике n=1024-32. Максимально доступно n=4080-32. Ограничено форматом заголовка архива. } const Ranges:tAriRanges; { данные по диапазонам символов } {Состоит из массива диапазонов для каждого символа Start - начало диапазона в интервале [0..SymbolCount). 32-битное целое; Length - длина диапазона в интервале [0..SymbolCount). 32-битное целое; и данных по полному количеству символов Total } Symbol:tSymbol { символ для кодирования} ):boolean; var a:tVeryLongUIntEx; lr:tAriVeryLargeRange; pSymbolRange:^tRange; begin if LargeRange.EncodedSymbolCount=HIGH(LargeRange.EncodedSymbolCount) then begin { ОБЛОМ - нет возможности запомнить число кодированных символов } Result:=FALSE; Exit; end; { априорное вычисление невозможности потери точности - чтобы зря не копировать данные в памяти } Result:=LargeRange.Length.MostSignificantDigitIndex>=(Ranges.Total.MostSignificantDigitIndex+1)*2; if not Result then { сохраняем данные диапазона про запас, на случай облома при вычислениях } lr:=LargeRange; pSymbolRange:=@Ranges.Ranges[Symbol]; if Ranges.Total.PowerOf2=0 then begin { Total.SymbolCount<>2^n => надо делить, увы } { Вычисление нового начала интервала LargeRange.Start:=LargeRange.Start+(SymbolRange.Start*LargeRange.Length) div Total.SymbolCount;} vlaMulFast(LargeRange.Length, tDoubleDigit(pSymbolRange^.Start), a); { a=LargeRange.Length*SymbolRange.Start - гарантированно отсутствует переполнение } vlaDivFast(a, tDoubleDigit(Ranges.Total.SymbolCount)); vlaAdd(LargeRange.Start, a); { Вычисление новой длины интервала LargeRange.Length:=(SymbolRange.Length*LargeRange.Length) div Total.SymbolCount;} vlaMulFast(LargeRange.Length, tDoubleDigit(pSymbolRange^.Length)); vlaDivFast(LargeRange.Length, tDoubleDigit(Ranges.Total.SymbolCount)); end else begin { Total.SymbolCount=2^n => можно сдвигать } { Вычисление нового начала интервала LargeRange.Start:=LargeRange.Start+(SymbolRange.Start*LargeRange.Length) div Total.SymbolCount; } vlaMulFast(LargeRange.Length, tDoubleDigit(pSymbolRange^.Start), a); { a=LargeRange.Length*SymbolRange.Start - гарантированно отсутствует переполнение } vlaShiftBitsRight(a, Ranges.Total.PowerOf2); // замена vlaDivFast(a, tDoubleDigit(Total.SymbolCount)); vlaAdd(LargeRange.Start, a); { Вычисление новой длины интервала LargeRange.Length:=(SymbolRange.Length*LargeRange.Length) div Total.SymbolCount; } vlaMulFast(LargeRange.Length, tDoubleDigit(pSymbolRange^.Length)); vlaShiftBitsRight(LargeRange.Length, Ranges.Total.PowerOf2); // замена vlaDivFast(LargeRange.Length, tDoubleDigit(Total.SymbolCount)); end; { увеличиваем счетчик закодированных символов } Inc(LargeRange.EncodedSymbolCount); if not Result then begin { если потеря точности возможна } { проверяем потерю точности } Result:=(LargeRange.Length.MostSignificantDigitIndex>Ranges.Total.MostSignificantDigitIndex) or (Ranges.Total.SymbolCount<=LargeRange.Length.Uint.DDigit0); if not Result then begin { ОБЛОМ - потеря точности - возвращаем все обратно } LargeRange:=lr; end; end; end; { ДЕКОДИРОВАНИЕ символа. Вычисление символа Symbol и нового значения кода Code при декодировании для статистики Ranges ВОЗВРАЩАЕТ: TRUE - вычислено успешно; FALSE - невозможно вычислить, ибо все уже декодировано } function DecodeSymbol( var Code:tAriVeryLargeCode; { декодируемый код } {Состоит из NonDecodedSymbolCount - число закодированных в коде символов. 32-битное целое; Code - код в интервале [0..cAriVeryLongUIntMaxValue); cAriVeryLongUIntMaxValue = 2^n, например, при 1024 битной арифметике n=1024-32. Максимально доступно n=4080-32. Ограничено форматом заголовка архива. } const Ranges:tAriRangesForDecode; { данные по диапазонам для декодирования } {Состоит из TotalSymbolCount - - полное число символов в декодируемом сообщении; LastSymbolIndex - номер последнего (нумерация с 0) символа с ненулевой статистикой; Symbols - массив символов с ненулевой статистикой, заполнено только первые LastSymbol значений; Lengths - массив длин интервала символов в диапазоне [0..TotalSymbolCount), заполнено только первые LastSymbol значений; Starts - начало интервала в отрезке [0..TotalSymbolCount), заполнено только первые LastSymbol значений; } out Symbol:tSymbol { результат декодирования - очередной символ } {Состоит из Symbol - код символа-байта } ):boolean; var l,r,n:tSymbolIndex; lCode:tFriquency; const cShortCodeIndex=cAriVeryLongUIntShift div 2; { номер двух старших цифр кода Code } begin if Code.NonDecodedSymbolCount=0 then begin { все уже декодировано } Result:=FALSE; EXIT; end; { Уменьшение счетчика недекодированных символов } Dec(Code.NonDecodedSymbolCount); with Ranges do begin { Вычисление нового значения кода в три шага Code:=(Code*Total.SymbolCount-SymbolRange.Start*cAriVeryLongUIntMaxValue) div SymbolRange.Length } { 1) Code:=Code*Total.SymbolCount } if Total.PowerOf2=0 then begin vlaMulFast(Code.Value, tDoubleDigit(Total.SymbolCount)); end else begin vlaShiftBitsLeft(Code.Value, Total.PowerOf2); end; { !!! текущее значение кода в интервале [0..Total.SymbolCount)= = Code.Code.Uint.DDigits[cAriVeryLongUIntShift div 2] или это просто две старшие цифры Code } { поиск символа по Code делением отрезка пополам } r:=LastSymbolIndex; lCode:=Code.Value.Uint.DDigits[cShortCodeIndex]; if lCode=r; r:=r-1; end; Symbol:=Symbols[r]; { 2) Code:=Code*Total.SymbolCount-SymbolRange.Start*cAriVeryLongUIntMaxValue } Dec(Code.Value.Uint.DDigits[cShortCodeIndex], Starts[r]); vlaDecrementMostSignificantDigitIndex(Code.Value); { 3) Code.Code:=(Code*Total.SymbolCount-SymbolRange.Start*cAriVeryLongUIntMaxValue) div SymbolRange.Length} vlaDivFast(Code.Value, tDoubleDigit(Lengths[r])); end; Result:=TRUE; end; end. (*function EncodeSymbol( var LargeRange:tAriVeryLargeRange; { текущий диапазон } {Состоит из EncodedSymbolCount - счетчик закодированных символов. 16-битное целое; Start - начало интервала в отрезке [0..cAriVeryLongUIntMaxValue); Length - длина интервала в отрезке [0..cAriVeryLongUIntMaxValue); cAriVeryLongUIntMaxValue = 2^n, например, при 1024 битной арифметике n=1024-32. Максимально доступно n=4080-32. Ограничено форматом заголовка архива. } SymbolRange:tRange; { диапазон символа } {Состоит из Start - начало диапазона в интервале [0..SymbolCount). 32-битное целое; Length - длина диапазона в интервале [0..SymbolCount). 32-битное целое; } Total:tTotalCountData { полное количество символов в сообщении } {Состоит из SymbolCount - полное число символов в кодируемом сообщении. 32-битное целое; PowerOf2 - является ли SymbolCount степенью двойки, если PowerOf2>0 значит SymbolCount=2^PowerOf2 и можно применять сдвиг вправо на PowerOf2 бит вместо деления на SymbolCount. } ):boolean; var a:tVeryLongUIntEx; lr:tAriVeryLargeRange; begin if LargeRange.EncodedSymbolCount=HIGH(LargeRange.EncodedSymbolCount) then begin { ОБЛОМ - нет возможности запомнить число кодированных символов } Result:=FALSE; Exit; end; { сохраняем данные диапазона про запас, на случай облома при вычислениях } lr:=LargeRange; { увеличиваем счетчик закодированных символов } LargeRange.EncodedSymbolCount:=LargeRange.EncodedSymbolCount+1; if Total.PowerOf2=0 then begin { Total.SymbolCount<>2^n => надо делить, увы } // Вычисление нового начала интервала // LargeRange.Start:=LargeRange.Start+(SymbolRange.Start*LargeRange.Length) div Total.SymbolCount; vlaMulFast(LargeRange.Length, tDoubleDigit(SymbolRange.Start), a); { a=LargeRange.Length*SymbolRange.Start - гарантированно отсутствует переполнение } vlaDivFast(a, tDoubleDigit(Total.SymbolCount)); vlaAdd(LargeRange.Start, a); // Вычисление новой длины интервала //LargeRange.Length:=(SymbolRange.Length*LargeRange.Length) div Total.SymbolCount; vlaMulFast(LargeRange.Length, tDoubleDigit(SymbolRange.Length)); vlaDivFast(LargeRange.Length, tDoubleDigit(Total.SymbolCount)); end else begin { Total.SymbolCount=2^n => можно сдвигать } { Вычисление нового начала интервала LargeRange.Start:=LargeRange.Start+(SymbolRange.Start*LargeRange.Length) div Total.SymbolCount; } vlaMulFast(LargeRange.Length, tDoubleDigit(SymbolRange.Start), a); { a=LargeRange.Length*SymbolRange.Start - гарантированно отсутствует переполнение } vlaShiftBitsRight(a, Total.PowerOf2); // замена vlaDivFast(a, tDoubleDigit(Total.SymbolCount)); vlaAdd(LargeRange.Start, a); { Вычисление новой длины интервала LargeRange.Length:=(SymbolRange.Length*LargeRange.Length) div Total.SymbolCount; } vlaMulFast(LargeRange.Length, tDoubleDigit(SymbolRange.Length)); vlaShiftBitsRight(LargeRange.Length, Total.PowerOf2); // замена vlaDivFast(LargeRange.Length, tDoubleDigit(Total.SymbolCount)); end; Result:=vlaCmp(LargeRange.Length, tDoubleDigit(Total.SymbolCount))>=0; if not Result then begin { ОБЛОМ - потеря точности - возвращаем все обратно } LargeRange:=lr; end; end; function EncodeSymbol_( var LargeRange:tAriVeryLargeRange; { текущий диапазон } {Состоит из EncodedSymbolCount - счетчик закодированных символов. 16-битное целое; Start - начало интервала в отрезке [0..cAriVeryLongUIntMaxValue); Length - длина интервала в отрезке [0..cAriVeryLongUIntMaxValue); cAriVeryLongUIntMaxValue = 2^n, например, при 1024 битной арифметике n=1024-32. Максимально доступно n=4080-32. Ограничено форматом заголовка архива. } SymbolRange:tRange; { диапазон символа } {Состоит из Start - начало диапазона в интервале [0..SymbolCount). 32-битное целое; Length - длина диапазона в интервале [0..SymbolCount). 32-битное целое; } Total:tTotalCountData { полное количество символов в сообщении } {Состоит из SymbolCount - полное число символов в кодируемом сообщении. 32-битное целое; PowerOf2 - является ли SymbolCount степенью двойки, если PowerOf2>0 значит SymbolCount=2^PowerOf2 и можно применять сдвиг PowerOf2 бит вместо деления/умножения на SymbolCount. } ):boolean; var a:tVeryLongUIntEx; lr:tAriVeryLargeRange; begin if LargeRange.EncodedSymbolCount=HIGH(LargeRange.EncodedSymbolCount) then begin { ОБЛОМ - нет возможности запомнить число кодированных символов } Result:=FALSE; Exit; end; { априорное вычисление невозможности потери точности - чтобы зря не копировать данные в памяти } // Result:=LargeRange.Length.MostSignificantDigitIndex>=(SizeOf(Total.SymbolCount)); Result:=LargeRange.Length.MostSignificantDigitIndex>=(Total.MostSignificantDigitIndex+1)*2; if not Result then { сохраняем данные диапазона про запас, на случай облома при вычислениях } lr:=LargeRange; if Total.PowerOf2=0 then begin { Total.SymbolCount<>2^n => надо делить, увы } { Вычисление нового начала интервала LargeRange.Start:=LargeRange.Start+(SymbolRange.Start*LargeRange.Length) div Total.SymbolCount;} vlaMulFast(LargeRange.Length, tDoubleDigit(SymbolRange.Start), a); { a=LargeRange.Length*SymbolRange.Start - гарантированно отсутствует переполнение } vlaDivFast(a, tDoubleDigit(Total.SymbolCount)); vlaAdd(LargeRange.Start, a); { Вычисление новой длины интервала LargeRange.Length:=(SymbolRange.Length*LargeRange.Length) div Total.SymbolCount;} vlaMulFast(LargeRange.Length, tDoubleDigit(SymbolRange.Length)); vlaDivFast(LargeRange.Length, tDoubleDigit(Total.SymbolCount)); end else begin { Total.SymbolCount=2^n => можно сдвигать } { Вычисление нового начала интервала LargeRange.Start:=LargeRange.Start+(SymbolRange.Start*LargeRange.Length) div Total.SymbolCount; } vlaMulFast(LargeRange.Length, tDoubleDigit(SymbolRange.Start), a); { a=LargeRange.Length*SymbolRange.Start - гарантированно отсутствует переполнение } vlaShiftBitsRight(a, Total.PowerOf2); // замена vlaDivFast(a, tDoubleDigit(Total.SymbolCount)); vlaAdd(LargeRange.Start, a); { Вычисление новой длины интервала LargeRange.Length:=(SymbolRange.Length*LargeRange.Length) div Total.SymbolCount; } vlaMulFast(LargeRange.Length, tDoubleDigit(SymbolRange.Length)); vlaShiftBitsRight(LargeRange.Length, Total.PowerOf2); // замена vlaDivFast(LargeRange.Length, tDoubleDigit(Total.SymbolCount)); end; { увеличиваем счетчик закодированных символов } Inc(LargeRange.EncodedSymbolCount); if not Result then begin { если потеря точности возможна } { проверяем потерю точности } //Result:=vlaCmp(LargeRange.Length, tDoubleDigit(Total.SymbolCount))>=0; // Result:=(LargeRange.Length.MostSignificantDigitIndex>=(SizeOf(Total.SymbolCount) div 2)) or (Total.SymbolCount<=LargeRange.Length.Uint.DDigit0); Result:=(LargeRange.Length.MostSignificantDigitIndex>Total.MostSignificantDigitIndex) or (Total.SymbolCount<=LargeRange.Length.Uint.DDigit0); if not Result then begin { ОБЛОМ - потеря точности - возвращаем все обратно } LargeRange:=lr; end; end; end; *)