@@ -44,33 +44,47 @@ function mulShift24(a: number, b: number): number {
4444 return Number ( ( BigInt ( a ) * BigInt ( b ) ) >> 24n ) ;
4545}
4646
47+ // a weight of the subpredictors from their errors around (x, y): the errors' sum (wrapping, as unsigned 32 bits)
48+ function weight ( w : number , e : Uint32Array , posN : number , posNE : number , posNW : number ) : number {
49+ const sum = ( e [ posN ] + e [ posNE ] + e [ posNW ] ) >>> 0 ;
50+ const shift = sum === 0xffffffff ? 27 : Math . max ( floorLog2 ( sum + 1 ) - 5 , 0 ) ;
51+ return 4 + ( ( w * DIV_LOOKUP [ sum >>> shift ] ) >>> shift ) ;
52+ }
53+
4754/** The self-correcting (weighted) predictor's state over a channel */
4855class WeightedPredictor {
49- private readonly predErrors : Uint32Array [ ] ;
56+ // (the subpredictors' errors and the error of the prediction, of two rows)
57+ private readonly e0 : Uint32Array ;
58+ private readonly e1 : Uint32Array ;
59+ private readonly e2 : Uint32Array ;
60+ private readonly e3 : Uint32Array ;
5061 private readonly error : Int32Array ;
51- private readonly prediction = new Float64Array ( 4 ) ;
62+ private readonly w0 : number ;
63+ private readonly w1 : number ;
64+ private readonly w2 : number ;
65+ private readonly w3 : number ;
66+ private p0 = 0 ;
67+ private p1 = 0 ;
68+ private p2 = 0 ;
69+ private p3 = 0 ;
5270 private pred = 0 ;
5371 /** the property (the largest error of the neighbors) of the last prediction */
5472 maxError = 0 ;
5573
5674 constructor ( private readonly header : WPHeader , private readonly xsize : number ) {
57- this . predErrors = [ 0 , 1 , 2 , 3 ] . map ( ( ) => new Uint32Array ( ( xsize + 2 ) * 2 ) ) ;
58- this . error = new Int32Array ( ( xsize + 2 ) * 2 ) ;
75+ const n = ( xsize + 2 ) * 2 ;
76+ [ this . e0 , this . e1 , this . e2 , this . e3 ] = [ 0 , 1 , 2 , 3 ] . map ( ( ) => new Uint32Array ( n ) ) ;
77+ this . error = new Int32Array ( n ) ;
78+ [ this . w0 , this . w1 , this . w2 , this . w3 ] = header . w ;
5979 }
6080
6181 /** the prediction (rounded) of (x, y) from its neighbors */
6282 predict ( x : number , y : number , N : number , W : number , NE : number , NW : number , NN : number ) : number {
6383 const h = this . header , xsize = this . xsize ;
6484 const cur = y & 1 ? 0 : xsize + 2 , prev = y & 1 ? xsize + 2 : 0 ;
6585 const posN = prev + x , posNE = x < xsize - 1 ? posN + 1 : posN , posNW = x > 0 ? posN - 1 : posN ;
66- const weights = [ 0 , 0 , 0 , 0 ] ;
67- for ( let i = 0 ; i < 4 ; i ++ ) {
68- const e = this . predErrors [ i ] ;
69- // (wrapping, as unsigned 32 bits)
70- const sum = ( e [ posN ] + e [ posNE ] + e [ posNW ] ) >>> 0 ;
71- const shift = Math . max ( ( sum === 0xffffffff ? 32 : floorLog2 ( sum + 1 ) ) - 5 , 0 ) ;
72- weights [ i ] = 4 + ( ( h . w [ i ] * DIV_LOOKUP [ Math . floor ( sum / 2 ** shift ) ] ) >>> shift ) ;
73- }
86+ let w0 = weight ( this . w0 , this . e0 , posN , posNE , posNW ) , w1 = weight ( this . w1 , this . e1 , posN , posNE , posNW ) ;
87+ let w2 = weight ( this . w2 , this . e2 , posN , posNE , posNW ) , w3 = weight ( this . w3 , this . e3 , posN , posNE , posNW ) ;
7488 N *= 8 ;
7589 W *= 8 ;
7690 NE *= 8 ;
@@ -83,35 +97,44 @@ class WeightedPredictor {
8397 if ( Math . abs ( teNW ) > Math . abs ( p ) ) p = teNW ;
8498 if ( Math . abs ( teNE ) > Math . abs ( p ) ) p = teNE ;
8599 this . maxError = p ;
86- const sumWN = teN + teW , pr = this . prediction ;
87- pr [ 0 ] = W + NE - N ;
88- pr [ 1 ] = N - Math . floor ( ( sumWN + teNE ) * h . p1 / 32 ) ;
89- pr [ 2 ] = W - Math . floor ( ( sumWN + teNW ) * h . p2 / 32 ) ;
90- pr [ 3 ] = N - Math . floor ( ( teNW * h . p3a + teN * h . p3b + teNE * h . p3c + ( NN - N ) * h . p3d + ( NW - W ) * h . p3e ) / 32 ) ;
100+ const sumWN = teN + teW ;
101+ const p0 = this . p0 = W + NE - N ;
102+ const p1 = this . p1 = N - Math . floor ( ( sumWN + teNE ) * h . p1 / 32 ) ;
103+ const p2 = this . p2 = W - Math . floor ( ( sumWN + teNW ) * h . p2 / 32 ) ;
104+ const p3 = this . p3 = N - Math . floor ( ( teNW * h . p3a + teN * h . p3b + teNE * h . p3c + ( NN - N ) * h . p3d + ( NW - W ) * h . p3e ) / 32 ) ;
91105 // the weighted average
92- let weightSum = weights [ 0 ] + weights [ 1 ] + weights [ 2 ] + weights [ 3 ] ;
93- const logWeight = floorLog2 ( weightSum ) - 4 ;
94- weightSum = 0 ;
95- for ( let i = 0 ; i < 4 ; i ++ ) weightSum += weights [ i ] >>= logWeight ;
96- let sum = ( weightSum >> 1 ) - 1 ;
97- for ( let i = 0 ; i < 4 ; i ++ ) sum += pr [ i ] * weights [ i ] ;
106+ const logWeight = floorLog2 ( w0 + w1 + w2 + w3 ) - 4 ;
107+ w0 >>= logWeight ;
108+ w1 >>= logWeight ;
109+ w2 >>= logWeight ;
110+ w3 >>= logWeight ;
111+ const weightSum = w0 + w1 + w2 + w3 ;
112+ const sum = ( weightSum >> 1 ) - 1 + p0 * w0 + p1 * w1 + p2 * w2 + p3 * w3 ;
98113 let pred = mulShift24 ( sum , DIV_LOOKUP [ weightSum - 1 ] ) ;
99114 // (clamped unless the errors have the same sign)
100- if ( ( ( teN ^ teW ) | ( teN ^ teNW ) ) <= 0 ) pred = Math . min ( Math . max ( pred , Math . min ( W , NE , N ) ) , Math . max ( W , NE , N ) ) ;
115+ if ( ( ( teN ^ teW ) | ( teN ^ teNW ) ) <= 0 ) {
116+ const lo = W < NE ? ( W < N ? W : N ) : ( NE < N ? NE : N ) , hi = W > NE ? ( W > N ? W : N ) : ( NE > N ? NE : N ) ;
117+ pred = pred < lo ? lo : pred > hi ? hi : pred ;
118+ }
101119 this . pred = pred ;
102120 return Math . floor ( ( pred + 3 ) / 8 ) ;
103121 }
104122
105123 update ( value : number , x : number , y : number ) : void {
106- const xsize = this . xsize , cur = y & 1 ? 0 : xsize + 2 , prev = y & 1 ? xsize + 2 : 0 ;
124+ const xsize = this . xsize , cur = y & 1 ? 0 : xsize + 2 , i = cur + x , j = ( y & 1 ? xsize + 2 : 0 ) + x + 1 ;
107125 const val = value * 8 ;
108- this . error [ cur + x ] = this . pred - val ;
109- for ( let i = 0 ; i < 4 ; i ++ ) {
110- const e = Math . floor ( ( Math . abs ( this . prediction [ i ] - val ) + 3 ) / 8 ) >>> 0 ;
111- const errors = this . predErrors [ i ] ;
112- errors [ cur + x ] = e ;
113- errors [ prev + x + 1 ] = ( errors [ prev + x + 1 ] + e ) >>> 0 ;
114- }
126+ this . error [ i ] = this . pred - val ;
127+ const e0 = Math . floor ( ( Math . abs ( this . p0 - val ) + 3 ) / 8 ) >>> 0 , e1 = Math . floor ( ( Math . abs ( this . p1 - val ) + 3 ) / 8 ) >>> 0 ;
128+ const e2 = Math . floor ( ( Math . abs ( this . p2 - val ) + 3 ) / 8 ) >>> 0 , e3 = Math . floor ( ( Math . abs ( this . p3 - val ) + 3 ) / 8 ) >>> 0 ;
129+ const E0 = this . e0 , E1 = this . e1 , E2 = this . e2 , E3 = this . e3 ;
130+ E0 [ i ] = e0 ;
131+ E0 [ j ] = ( E0 [ j ] + e0 ) >>> 0 ;
132+ E1 [ i ] = e1 ;
133+ E1 [ j ] = ( E1 [ j ] + e1 ) >>> 0 ;
134+ E2 [ i ] = e2 ;
135+ E2 [ j ] = ( E2 [ j ] + e2 ) >>> 0 ;
136+ E3 [ i ] = e3 ;
137+ E3 [ j ] = ( E3 [ j ] + e3 ) >>> 0 ;
115138 }
116139}
117140
0 commit comments