program stdalone;
type
mat96 = array [ 1 .. 32 , 1 .. 3 ] of real ;
vecb32 = array [ 1 .. 32 ] of boolean ;
vec32 = array [ 1 .. 32 ] of real ;
vec16 = array [ 1 .. 16 ] of real ;
vec8 = array [ 1 .. 8 ] of real ;
vec3 = array [ 1 .. 3 ] of real ;
mat16 = array [ 1 .. 4 , 1 .. 4 ] of real ;
const
We = 7. 292115E- 5; {WGS-84 earth rotation rate}
c = 299792458.0 ; {WGS-84 speed of light}
pi = 3.1415926535898 {WGS 84 value of pi} ;
Wedot = 7. 2921151467E- 5; {WGS 84 value of earth's rotation rate}
mu = 3. 986005E+ 14; {WGS 84 value of earth's univ. grav. par.}
F = - 4. 442807633E- 10; {relativistic correction term constant}
a = 6378137.0 ; {WGS-84 earth's semi major axis}
b = 6356752.31 ; {WGS-84 earth's semi minor axis}
e1sqr = ( a* a - b* b) / ( a* a) ; {first numerical eccentricity}
e2sqr = ( a* a - b* b) / ( b* b) ; {second numerical eccentricity}
var
inp, out: text;
Ttr, tau, Trc, T, Trel: real ;
Az, El, Cr, alpha, dTclck, dTiono, dRtrop, Lat, Lon: real ;
prn, i: integer ;
eph: array [ 1 .. 32 , 1 .. 16 ] of real ;
clk: array [ 1 .. 32 , 1 .. 5 ] of real ;
ion: vec8;
Praw, Pcor: vec32;
Xs: mat96;
SV: vecb32;
P: vec32;
Xr, Xlla, tmp3: vec3;
status: boolean ;
tmp16: vec16;
tmp5: array [ 1 .. 5 ] of real ;
{***************************************************************************}
procedure LLA2XYZ( Xi : vec3; {lat [rad], lon [rad] alt [m]}
var Xo : vec3) ; {ECEF X [m], Y [m] , Z[m]}
{this procedure converts WGS-84 Lat, Lon and Alt [above ellipsoid]
to ECEF XYZ}
var
N: real ;
begin
N : = a / sqrt ( 1.0 - e1sqr * sin ( Xi[ 1 ] ) * sin ( Xi[ 1 ] ) ) ;
Xo[ 1 ] : = ( N + Xi[ 3 ] ) * cos ( Xi[ 1 ] ) * cos ( Xi[ 2 ] ) ;
Xo[ 2 ] : = ( N + Xi[ 3 ] ) * cos ( Xi[ 1 ] ) * sin ( Xi[ 2 ] ) ;
Xo[ 3 ] : = ( N * ( 1.0 - e1sqr) + Xi[ 3 ] ) * sin ( Xi[ 1 ] ) ;
end ; {procedure LLA2XYZ}
{***************************************************************************}
procedure XYZ2LLA( Xi : vec3; {ECEF X [m], Y [m] , Z[m]}
var Xo : vec3) ; {lat [rad], lon [rad] alt [m]}
{this procedure converts WGS-84 ECEF XYZ to Lat, Lon, Alt [above ellipsoid]}
var
p, T, sT, cT, N, sig: real ;
begin
p : = sqrt ( Xi[ 1 ] * Xi[ 1 ] + Xi[ 2 ] * Xi[ 2 ] ) ;
T : = arctan ( ( Xi[ 3 ] * a) / ( p * b) ) ;
sT : = sin ( T) ; cT : = cos ( T) ;
Xo[ 1 ] : = arctan ( ( Xi[ 3 ] + e2sqr * b * sT * sT * sT)
/ ( p - e1sqr * a * cT * cT * cT) ) ;
if Xi[ 2 ] <> 0.0 then sig : = Xi[ 2 ] / abs ( Xi[ 2 ] ) else sig : = 1.0 ;
if Xi[ 1 ] = 0.0 then Xo[ 2 ] : = sig * pi / 2.0 else
begin
Xo[ 2 ] : = arctan ( Xi[ 2 ] / Xi[ 1 ] ) ;
if ( Xi[ 1 ] < 0.0 ) and ( Xi[ 2 ] >= 0.0 ) then Xo[ 2 ] : = Xo[ 2 ] + pi;
if ( Xi[ 1 ] < 0.0 ) and ( Xi[ 2 ] < 0.0 ) then Xo[ 2 ] : = Xo[ 2 ] - pi
end ;
N : = a / sqrt ( 1.0 - e1sqr * sin ( Xo[ 1 ] ) * sin ( Xo[ 1 ] ) ) ;
Xo[ 3 ] : = p / cos ( Xo[ 1 ] ) - N;
end ; {procedure XYZ2LLA}
{***************************************************************************}
procedure satpos( eph : vec16; {ephemeris}
Ttr : real ; {satellite GPS time}
var Trel: real ; {relativistic correction term}
var X : vec3) ; {satellite position}
var
M0, dn, ec, A, W0, i0, w, Wdot, Cuc, Cus, Crc, Crs, Cic, Cis, Toe, Idot: real ;
T, n0, n, M, E, Eold, snu, cnu, nu, phi, du, dr, di, u, r, i, Xdash, Ydash, Wc: real ;
k: integer ;
begin
{for clarity of code, copy the ephemeris parameters and convert to radians}
Crs : = eph[ 1 ] ;
dn : = eph[ 2 ] * pi;
M0 : = eph[ 3 ] * pi;
Cuc : = eph[ 4 ] ;
ec : = eph[ 5 ] ;
Cus : = eph[ 6 ] ;
A : = eph[ 7 ] * eph[ 7 ] ;
Toe : = eph[ 8 ] ;
Cic : = eph[ 9 ] ;
W0 : = eph[ 10 ] * pi;
Cis : = eph[ 11 ] ;
i0 : = eph[ 12 ] * pi;
Crc : = eph[ 13 ] ;
w : = eph[ 14 ] * pi;
Wdot: = eph[ 15 ] * pi;
idot: = eph[ 16 ] * pi;
T: = Ttr - Toe;
if T > 302400 then T : = T - 604800 ;
if T < - 302400 then T : = T + 604800 ;
n0 : = sqrt ( mu / ( A* A* A) ) ;
n : = n0 + dn;
M : = M0 + n* T;
E : = M; {start value for E}
repeat
Eold : = E;
E : = M + ec * sin ( E) ;
until abs ( E - Eold) < 1. 0e- 8;
snu : = sqrt ( 1 - ec* ec) * sin ( E) / ( 1 - ec* cos ( E) ) ;
cnu : = ( cos ( E) - ec) / ( 1 - ec* cos ( E) ) ;
if cnu = 0 then nu : = pi/ 2 * snu / abs ( snu)
else if ( snu = 0 ) and ( cnu > 0 ) then nu : = 0
else if ( snu = 0 ) and ( cnu < 0 ) then nu : = pi
else nu : = arctan ( snu/ cnu)
+ ord ( cnu<0 ) * pi * snu / abs ( snu) ;
phi : = nu + w;
du : = Cuc* cos ( 2 * phi) + Cus* sin ( 2 * phi) ;
dr : = Crc* cos ( 2 * phi) + Crs* sin ( 2 * phi) ;
di : = Cic* cos ( 2 * phi) + Cis* sin ( 2 * phi) ;
u : = phi + du;
r : = A* ( 1 - ec* cos ( E) ) + dr;
i : = i0 + idot* T + di;
Xdash : = r* cos ( u) ;
Ydash : = r* sin ( u) ;
Wc: = W0 + ( Wdot - Wedot) * T - Wedot* Toe;
X[ 1 ] : = Xdash* cos ( Wc) - Ydash* cos ( i) * sin ( Wc) ;
X[ 2 ] : = Xdash* sin ( Wc) + Ydash* cos ( i) * cos ( Wc) ;
X[ 3 ] : = Ydash* sin ( i) ;
Trel : = F * ec * eph[ 7 ] * sin ( E) {relativistic correction term}
end ; {procedure satpos}
{***************************************************************************}
procedure calcAzEl( Xs, {satellite ECEF XYZ}
Xu : vec3; {user ECEF XYZ}
var Az, {azimuth [rad]}
El : real ; {elevation [rad]}
var stat: boolean ) ; {calculation succeeded: stat = true}
var
R, p, x, y, z, s: real ;
e: array [ 1 .. 3 , 1 .. 3 ] of real ;
i, k: integer ;
d: vec3;
begin
x : = Xu[ 1 ] ;
y : = Xu[ 2 ] ;
z : = Xu[ 3 ] ;
p : = sqrt ( x* x + y* y) ;
if p = 0.0 then stat : = false else stat : = true ;
if stat then
begin
R : = sqrt ( x* x + y* y + z* z) ;
e[ 1 , 1 ] : = - y / p;
e[ 1 , 2 ] : = + x / p;
e[ 1 , 3 ] : = 0.0 ;
e[ 2 , 1 ] : = - x* z / ( p* R) ;
e[ 2 , 2 ] : = - y* z / ( p* R) ;
e[ 2 , 3 ] : = p / R;
e[ 3 , 1 ] : = x / R;
e[ 3 , 2 ] : = y / R;
e[ 3 , 3 ] : = z / R;
for k : = 1 to 3 do
begin
d[ k] : = 0.0 ;
for i : = 1 to 3 do d[ k] : = d[ k] + ( Xs[ i] - Xu[ i] ) * e[ k, i]
end ;
s : = d[ 3 ] / sqrt ( d[ 1 ] * d[ 1 ] + d[ 2 ] * d[ 2 ] + d[ 3 ] * d[ 3 ] ) ;
if s = 1.0 then El : = 0.5 * pi else El : = arctan ( s / sqrt ( 1.0 - s* s) ) ;
if ( d[ 2 ] = 0.0 ) and ( d[ 1 ] > 0.0 ) then Az : = 0.5 * pi else
if ( d[ 2 ] = 0.0 ) and ( d[ 1 ] < 0.0 ) then Az : = 1.5 * pi else
begin
Az : = arctan ( d[ 1 ] / d[ 2 ] ) ;
if d[ 2 ] < 0.0 then Az : = Az + pi else
if ( d[ 2 ] > 0.0 ) and ( d[ 1 ] < 0.0 ) then Az : = Az + 2.0 * pi
end ;
end ;
end ; {procedure calcAzEl}
{***************************************************************************}
procedure ionocorr ( ion : vec8; {iono correction coefficients from
nav message}
Latu, {user's latitude [rad]}
Lonu, {user's longitude [rad]}
Az, {SV azimuth [rad]}
El, {SV elevation [rad]}
Ttr : real ; {SV signal transmission time [sec]}
var dTiono: real ) ; {Ionospheric delay [sec]}
var
phi, Lati, Loni, Latm, T, F, x, per, amp: real ;
a0, a1, a2, a3, b0, b1, b2, b3: real ;
begin
{for clarity copy array}
a0 : = ion[ 1 ] ;
a1 : = ion[ 2 ] ;
a2 : = ion[ 3 ] ;
a3 : = ion[ 4 ] ;
b0 : = ion[ 5 ] ;
b1 : = ion[ 6 ] ;
b2 : = ion[ 7 ] ;
b3 : = ion[ 8 ] ;
{convert from radians to semicircles}
Latu : = Latu / pi; Lonu : = Lonu / pi; Az : = Az / pi; El : = El / pi;
{calculation}
phi : = 0.0137 / ( El + 0.11 ) - 0.022 ;
Lati : = Latu + phi * cos ( Az * pi) ;
if Lati > + 0.416 then Lati : = + 0.416 else if Lati < - 0.416 then Lati : = - 0.416 ;
Loni : = Lonu + phi * sin ( Az * pi) / cos ( Lati * pi) ;
Latm : = Lati + 0.064 * cos ( ( Loni - 1.617 ) * pi) ;
T : = 4. 32E+ 4 * Loni + Ttr;
while T >= 86400 do T : = T - 86400 else while T < 0 do T : = T + 86400 ;
F : = 1.0 + 16.0 * ( 0.53 - El) * ( 0.53 - El) * ( 0.53 - El) ;
per : = b0 + b1 * Latm + B2 * Latm * Latm + b3 * Latm * Latm * Latm;
if per < 72000.0 then per : = 72000.0 ;
x : = 2 * pi * ( T - 50400.0 ) / per;
amp : = a0 + a1 * Latm + A2 * Latm * Latm + a3 * Latm * Latm * Latm;
if amp < 0.0 then amp : = 0.0 ;
if abs ( x) >= 1.57 then dTiono : = F * 5. 0E- 9 else
dTiono : = F * ( 5. 0E- 9 + amp * ( 1.0 - x * x / 2.0 + x * x * x * x / 24.0 ) )
end ; {procedure ionocorr}
{***************************************************************************}
{At many places in the following procedure solve the subdeterminant value of a
4 x 4 array is required. For convenience the function sub is defined below}
function sub ( A : mat16; {input 4 x 4 array}
r, {row number to be deleted}
c : integer {column number to be deleted}
) : double; {value of 3 x 3 subdeterminant}
var
B: array [ 1 .. 3 , 1 .. 3 ] of double;
i, i1, j, j1: integer ;
begin
i1 : = 0 ;
for i : = 1 to 4 do if i <> r then
begin
i1 : = i1 + 1 ;
j1 : = 0 ;
for j : = 1 to 4 do if j <> c then
begin
j1 : = j1 + 1 ;
B[ i1, j1] : = A[ i, j]
end
end ;
sub : = + B[ 1 , 1 ] * ( B[ 2 , 2 ] * B[ 3 , 3 ] - B[ 2 , 3 ] * B[ 3 , 2 ] )
- B[ 2 , 1 ] * ( B[ 1 , 2 ] * B[ 3 , 3 ] - B[ 3 , 2 ] * B[ 1 , 3 ] )
+ B[ 3 , 1 ] * ( B[ 1 , 2 ] * B[ 2 , 3 ] - B[ 1 , 3 ] * B[ 2 , 2 ] ) ;
end ; {function sub}
{***************************************************************************}
procedure solve( Xs : mat96; {array with 3 columns and 32 rows
for the coordinates of the sat's}
SV : vecb32; {valid prn's}
P : vec32; {pseudoranges}
var Xr : vec3; {input of initial guess, output of
final position}
var Cr : real ; {receiver clock error}
var status: boolean ) ; {true: calculation OK, false: no solution}
{procedure solve requires the following types to be declared in the
main body of the program:
type
mat96 = array[1..32,1..3] of real;
vecb32 = array[1..32] of boolean;
vec32 = array[1..32] of real;
vec3 = array[1..3] of real;
mat16 = array[1..4,1..4] of real;}
var
prn, it, i, j, k: integer ;
R, L: array [ 1 .. 32 ] of real ;
A: array [ 1 .. 32 , 1 .. 4 ] of real ;
AL: array [ 1 .. 4 ] of real ;
AA, AAi: mat16;
n: longint ;
det: real ;
D: array [ 1 .. 4 ] of real ;
begin {procedure solve}
it : = 0 ; {iteration counter}
repeat {iterations}
it : = it + 1 ; {increase iteration counter}
for prn : = 1 to 32 do if SV[ prn] then
begin
R[ prn] : = {range from receiver to satellite}
sqrt ( ( Xr[ 1 ] - Xs[ prn, 1 ] ) * ( Xr[ 1 ] - Xs[ prn, 1 ] )
+ ( Xr[ 2 ] - Xs[ prn, 2 ] ) * ( Xr[ 2 ] - Xs[ prn, 2 ] )
+ ( Xr[ 3 ] - Xs[ prn, 3 ] ) * ( Xr[ 3 ] - Xs[ prn, 3 ] ) ) ;
L[ prn] : = P[ prn] - R[ prn] ; {range residual value}
for k : = 1 to 3 do A[ prn, k] : = ( Xr[ k] - Xs[ prn, k] ) / R[ prn] ;
A[ prn, 4 ] : = - 1.0 {A is the geometry matrix or model matrix}
end ;
For k : = 1 to 4 do {calculate A.L}
begin
AL[ k] : = 0.0 ;
for prn : = 1 to 32 do if SV[ prn] then
AL[ k] : = AL[ k] + A[ prn, k] * L[ prn]
end ;
for k : = 1 to 4 do for i : = 1 to 4 do {calculate A.A}
begin
AA[ k, i] : = 0.0 ;
for prn : = 1 to 32 do if SV[ prn] then
AA[ k, i] : = AA[ k, i] + A[ prn, k] * A[ prn, i]
end ;
{invert A.A}
det : = + AA[ 1 , 1 ] * sub( AA, 1 , 1 ) - AA[ 2 , 1 ] * sub( AA, 2 , 1 )
+ AA[ 3 , 1 ] * sub( AA, 3 , 1 ) - AA[ 4 , 1 ] * sub( AA, 4 , 1 ) ;
if det = 0.0 then status : = false else
begin
status : = true ;
for k : = 1 to 4 do for i : = 1 to 4 do
begin
n: = k + i; if odd ( n) then j : = - 1 else j : = 1 ;
AAi[ k, i] : = j * sub( AA, i, k) / det
end ;
{calculate (invA.A).(A.L)}
for k : = 1 to 4 do
begin
D[ k] : = 0.0 ;
for i : = 1 to 4 do D[ k] : = D[ k] + AAi[ k, i] * AL[ i]
end ;
{update position}
for k : = 1 to 3 do Xr[ k] : = Xr[ k] + D[ k] ;
end ;
until ( it = 6 ) {there is something wrong if more than 6 iterations are required}
or ( ( abs ( D[ 1 ] ) + abs ( D[ 2 ] ) + abs ( D[ 3 ] ) ) < 1. 0E- 2) {iteration criterion}
or ( not ( status) ) ; {calculation not succeeded}
Cr : = D[ 4 ] ; {receiver clock error}
if it = 6 then begin writeln ( 'solve it : ' , it) ; status : = false end ; {iteration not succeeded}
end ; {procedure solve}
{***************************************************************************}
begin {main}
{the following data should be available:
1. Pseudorange with receiver time of reception for each SV
2. Ephemeris and almanac for each SV
3. Iono coefficients}
{open input datafile}
assign( inp, 'inp.txt' ) ; reset( inp) ;
{read GPS time of reception}
readln ( inp) ; {skip comment line}
readln ( inp, Trc) ;
{read iono coefficients}
readln ( inp) ; {skip comment line}
for i : = 1 to 8 do readln ( inp, ion[ i] ) ;
readln ( inp) ; {skip comment line}
{read pseudoranges}
for prn : = 1 to 32 do SV[ prn] : = false ;
repeat
read ( inp, prn) ;
if prn <> 0 then
begin
readln ( inp, Praw[ prn] ) ;
SV[ prn] : = true
end
else readln ( inp) ;
until prn = 0 ;
readln ( inp) ; {skip comment line}
{read ephemeris- and clock data}
repeat
readln ( inp, prn) ;
for i : = 1 to 16 do readln ( inp, eph[ prn, i] ) ;
for i : = 1 to 5 do readln ( inp, clk[ prn, i] ) ;
until eof ( inp) ;
close( inp) ;
{user input of start position}
write ( 'Start position Lat [deg.dec], Lon [deg.dec], Alt [m] : ' ) ;
readln ( Xlla[ 1 ] , Xlla[ 2 ] , Xlla[ 3 ] ) ;
Xlla[ 1 ] : = Xlla[ 1 ] * pi / 180.0 ; Xlla[ 2 ] : = Xlla[ 2 ] * pi / 180.0 ;
{convert lat, ln, alt to ECEF X, Y, Z}
LLA2XYZ( Xlla, Xr) ;
{open output data file}
assign( out, 'output.txt' ) ; rewrite( out) ;
{assuming the receiver clock error and initial position not sufficiently
good known, I make two passes through the processing steps}
{PASS 1}
writeln ( out, 'PASS 1' ) ;
for prn : = 1 to 32 do if SV[ prn] then begin {do for each SV}
{set all transit times to nominal value and calculate time of transmission}
tau : = 0.075 ;
Ttr : = Trc - tau;
{calculate SV position and correct for earth rotation}
for i : = 1 to 16 do tmp16[ i] : = eph[ prn, i] ;
satpos( tmp16, Ttr, Trel, tmp3) ;
alpha : = tau * We;
Xs[ prn, 1 ] : = + tmp3[ 1 ] * cos ( alpha) + tmp3[ 2 ] * sin ( alpha) ;
Xs[ prn, 2 ] : = - tmp3[ 1 ] * sin ( alpha) + tmp3[ 2 ] * cos ( alpha) ;
Xs[ prn, 3 ] : = + tmp3[ 3 ] ;
writeln ( out, 'SV : ' , prn: 2 , Xs[ prn, 1 ] : 15 : 3 , Xs[ prn, 2 ] : 15 : 3 , Xs[ prn, 3 ] : 15 : 3 ) ;
{calculate azimuth and elevation}
for i : = 1 to 3 do tmp3[ i] : = Xs[ prn, i] ;
calcAzEl( tmp3, Xr, Az, El, status) ;
if not status then
begin writeln ( 'Error in calcAzEl - check input data' ) ; exit end ;
writeln ( out, 'Az, El : ' , prn: 2 , Az* 180.0 / pi: 11 : 3 , El* 180.0 / pi: 10 : 3 ) ;
{calculate pseudorange corrections and apply to pseudoranges}
{clock correction}
T : = Ttr - clk[ prn, 2 ] ;
{correct for week crossover}
if T > 302400 then T : = T - 604800 ;
if T < - 302400 then T : = T + 604800 ;
dTclck : = + clk[ prn, 5 ] + clk[ prn, 4 ] * T + clk[ prn, 3 ] * T * T
+ Trel - clk[ prn, 1 ] ;
{iono correction}
Lat : = Xlla[ 1 ] ; Lon : = Xlla[ 2 ] ;
ionocorr( ion, Lat, Lon, Az, El, Ttr, dTiono) ;
{tropo correction using standard atmosphere values}
dRtrop : = + 2.312 / sin ( sqrt ( El * El + 1. 904E- 3) )
+ 0.084 / sin ( sqrt ( El * El + 0. 6854E- 3) ) ;
writeln ( out, 'Corr : ' , prn: 2 , dTclck* c: 11 : 3 , dTiono* c: 10 : 3 , dRtrop: 10 : 3 ) ;
{correct pseudorange}
Pcor[ prn] : = Praw[ prn] + dTclck * c - dTiono * c - dRtrop
end ; {do for each SV}
{calculate receiver position}
solve( Xs, SV, Pcor, Xr, Cr, status) ;
if not status then
begin writeln ( 'Error in solve - check input data' ) ; exit end ;
writeln ( out, 'Pos XYZ: ' , Xr[ 1 ] : 12 : 3 , Xr[ 2 ] : 12 : 3 , Xr[ 3 ] : 12 : 3 , Cr: 12 : 3 ) ;
{convert back to Lat, Lon, Alt}
XYZ2LLA( Xr, Xlla) ;
{PASS 2 - The receiver position and -clock error is now well enough known
to calculate the final pseudorange corrections}
writeln ( out) ; writeln ( out, 'PASS 2' ) ;
{correct receiver clock}
Trc : = Trc + Cr / c;
for prn : = 1 to 32 do if SV[ prn] then begin {do for each SV}
{recalculate transit time and time of transmission}
tau : = ( Pcor[ prn] + Cr) / c;
Ttr : = Trc - tau;
{recalculate SV position and correct for earth rotation}
for i : = 1 to 16 do tmp16[ i] : = eph[ prn, i] ;
satpos( tmp16, Ttr, Trel, tmp3) ;
alpha : = tau * We;
Xs[ prn, 1 ] : = + tmp3[ 1 ] * cos ( alpha) + tmp3[ 2 ] * sin ( alpha) ;
Xs[ prn, 2 ] : = - tmp3[ 1 ] * sin ( alpha) + tmp3[ 2 ] * cos ( alpha) ;
Xs[ prn, 3 ] : = + tmp3[ 3 ] ;
writeln ( out, 'SV : ' , prn: 2 , Xs[ prn, 1 ] : 15 : 3 , Xs[ prn, 2 ] : 15 : 3 , Xs[ prn, 3 ] : 15 : 3 ) ;
{recalculate azimuth and elevation}
for i : = 1 to 3 do tmp3[ i] : = Xs[ prn, i] ;
calcAzEl( tmp3, Xr, Az, El, status) ;
if not status then
begin writeln ( 'Error in calcAzEl - check input data' ) ; exit end ;
writeln ( out, 'Az, El : ' , prn: 2 , Az* 180.0 / pi: 11 : 3 , El* 180.0 / pi: 10 : 3 ) ;
{recalculate pseudorange corrections and apply to pseudoranges}
{clock correction}
T : = Ttr - clk[ prn, 2 ] ;
{correct for week crossover}
if T > 302400 then T : = T - 604800 ;
if T < - 302400 then T : = T + 604800 ;
dTclck : = + clk[ prn, 5 ] + clk[ prn, 4 ] * T + clk[ prn, 3 ] * T * T
+ Trel - clk[ prn, 1 ] ;
{iono correction}
Lat : = Xlla[ 1 ] ; Lon : = Xlla[ 2 ] ;
ionocorr( ion, Lat, Lon, Az, El, Ttr, dTiono) ;
{tropo correction using standard atmosphere values}
dRtrop : = + 2.312 / sin ( sqrt ( El * El + 1. 904E- 3) )
+ 0.084 / sin ( sqrt ( El * El + 0. 6854E- 3) ) ;
writeln ( out, 'Corr : ' , prn: 2 , dTclck* c: 11 : 3 , dTiono* c: 10 : 3 , dRtrop: 10 : 3 ) ;
{correct pseudorange}
Pcor[ prn] : = Praw[ prn] + dTclck * c - dTiono * c - dRtrop + Cr
end ; {do for each SV}
{calculate receiver position}
solve( Xs, SV, Pcor, Xr, Cr, status) ;
if not status then
begin writeln ( 'Error in solve - check input data' ) ; exit end ;
writeln ( out, 'Pos XYZ: ' , Xr[ 1 ] : 12 : 3 , Xr[ 2 ] : 12 : 3 , Xr[ 3 ] : 12 : 3 , Cr: 12 : 3 ) ;
{convert back to Lat, Lon, Alt}
XYZ2LLA( Xr, Xlla) ;
writeln ( out, 'Pos LLA: ' , Xlla[ 1 ] * 180.0 / pi: 15 : 8 , Xlla[ 2 ] * 180.0 / pi: 15 : 8 , Xlla[ 3 ] : 12 : 3 ) ;
close( out)
end . {main}
cHJvZ3JhbSBzdGRhbG9uZTsKCnR5cGUKICAgbWF0OTYgPSBhcnJheVsxLi4zMiwxLi4zXSBvZiByZWFsOwogICB2ZWNiMzIgPSBhcnJheVsxLi4zMl0gb2YgYm9vbGVhbjsKICAgdmVjMzIgPSBhcnJheVsxLi4zMl0gb2YgcmVhbDsKICAgdmVjMTYgPSBhcnJheVsxLi4xNl0gb2YgcmVhbDsKICAgdmVjOCA9IGFycmF5WzEuLjhdIG9mIHJlYWw7CiAgIHZlYzMgPSBhcnJheVsxLi4zXSBvZiByZWFsOwogICBtYXQxNiA9IGFycmF5WzEuLjQsMS4uNF0gb2YgcmVhbDsKCmNvbnN0CiAgIFdlID0gNy4yOTIxMTVFLTU7ICAgICAgICAgICAge1dHUy04NCBlYXJ0aCByb3RhdGlvbiByYXRlfQogICBjID0gMjk5NzkyNDU4LjA7ICAgICAgICAgICAgIHtXR1MtODQgc3BlZWQgb2YgbGlnaHR9CiAgIHBpID0gMy4xNDE1OTI2NTM1ODk4ICAgICAgICAge1dHUyA4NCB2YWx1ZSBvZiBwaX07CiAgIFdlZG90ID0gNy4yOTIxMTUxNDY3RS01OyAgICAge1dHUyA4NCB2YWx1ZSBvZiBlYXJ0aCdzIHJvdGF0aW9uIHJhdGV9CiAgIG11ID0gMy45ODYwMDVFKzE0OyAgICAgICAgICAge1dHUyA4NCB2YWx1ZSBvZiBlYXJ0aCdzIHVuaXYuIGdyYXYuIHBhci59CiAgIEYgPSAtNC40NDI4MDc2MzNFLTEwOyAgICAgICAge3JlbGF0aXZpc3RpYyBjb3JyZWN0aW9uIHRlcm0gY29uc3RhbnR9CiAgIGEgPSA2Mzc4MTM3LjA7ICAgICAgICAgICAgICAge1dHUy04NCBlYXJ0aCdzIHNlbWkgbWFqb3IgYXhpc30KICAgYiA9IDYzNTY3NTIuMzE7ICAgICAgICAgICAgICB7V0dTLTg0IGVhcnRoJ3Mgc2VtaSBtaW5vciBheGlzfQogICBlMXNxciA9IChhKmEgLSBiKmIpIC8gKGEqYSk7IHtmaXJzdCAgbnVtZXJpY2FsIGVjY2VudHJpY2l0eX0KICAgZTJzcXIgPSAoYSphIC0gYipiKSAvIChiKmIpOyB7c2Vjb25kIG51bWVyaWNhbCBlY2NlbnRyaWNpdHl9Cgp2YXIKICAgaW5wLCBvdXQ6IHRleHQ7CiAgIFR0ciwgdGF1LCBUcmMsIFQsIFRyZWw6IHJlYWw7CiAgIEF6LCBFbCwgQ3IsIGFscGhhLCBkVGNsY2ssIGRUaW9ubywgZFJ0cm9wLCBMYXQsIExvbjogcmVhbDsKICAgcHJuLCBpOiBpbnRlZ2VyOwogICBlcGg6IGFycmF5WzEuLjMyLDEuLjE2XSBvZiByZWFsOwogICBjbGs6IGFycmF5WzEuLjMyLDEuLjVdIG9mIHJlYWw7CiAgIGlvbjogdmVjODsKICAgUHJhdywgUGNvcjogdmVjMzI7CiAgIFhzOiBtYXQ5NjsKICAgU1Y6IHZlY2IzMjsKICAgUDogdmVjMzI7CiAgIFhyLCBYbGxhLCB0bXAzOiB2ZWMzOwogICBzdGF0dXM6IGJvb2xlYW47CiAgIHRtcDE2OiB2ZWMxNjsKICAgdG1wNTogYXJyYXlbMS4uNV0gb2YgcmVhbDsKCnsqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKip9Cgpwcm9jZWR1cmUgTExBMlhZWihYaSA6IHZlYzM7ICAgICAge2xhdCBbcmFkXSwgbG9uIFtyYWRdIGFsdCBbbV19CiAgICAgICAgICAgICAgdmFyIFhvIDogdmVjMyk7ICAgICB7RUNFRiBYIFttXSwgWSBbbV0gLCBaW21dfQp7dGhpcyBwcm9jZWR1cmUgY29udmVydHMgV0dTLTg0IExhdCwgTG9uIGFuZCBBbHQgW2Fib3ZlIGVsbGlwc29pZF0KIHRvIEVDRUYgWFlafQoKdmFyCiAgIE46IHJlYWw7CmJlZ2luCk4gOj0gYSAvIHNxcnQoMS4wIC0gZTFzcXIgKiBzaW4oWGlbMV0pICogc2luKFhpWzFdKSk7ClhvWzFdIDo9IChOICAgICAgICAgICAgICAgICArIFhpWzNdKSAqIGNvcyhYaVsxXSkgKiBjb3MoWGlbMl0pOwpYb1syXSA6PSAoTiAgICAgICAgICAgICAgICAgKyBYaVszXSkgKiBjb3MoWGlbMV0pICogc2luKFhpWzJdKTsKWG9bM10gOj0gKE4gKiAoMS4wIC0gZTFzcXIpICsgWGlbM10pICogc2luKFhpWzFdKTsKZW5kOyB7cHJvY2VkdXJlIExMQTJYWVp9Cgp7KioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqfQoKcHJvY2VkdXJlIFhZWjJMTEEoWGkgOiB2ZWMzOyAgICAgIHtFQ0VGIFggW21dLCBZIFttXSAsIFpbbV19CiAgICAgICAgICAgICAgdmFyIFhvIDogdmVjMyk7ICAgICB7bGF0IFtyYWRdLCBsb24gW3JhZF0gYWx0IFttXX0Ke3RoaXMgcHJvY2VkdXJlIGNvbnZlcnRzIFdHUy04NCBFQ0VGIFhZWiB0byBMYXQsIExvbiwgQWx0IFthYm92ZSBlbGxpcHNvaWRdfQoKdmFyCiAgIHAsIFQsIHNULCBjVCwgTiwgc2lnOiByZWFsOwpiZWdpbgpwIDo9IHNxcnQoWGlbMV0gKiBYaVsxXSArIFhpWzJdICogWGlbMl0pOwpUIDo9IGFyY3RhbigoWGlbM10gKiBhKSAvIChwICogYikpOwpzVCA6PSBzaW4oVCk7IGNUIDo9IGNvcyhUKTsKWG9bMV0gOj0gYXJjdGFuKChYaVszXSArIGUyc3FyICogYiAqIHNUICogc1QgKiBzVCkKICAgICAgICAgLyAocCAtIGUxc3FyICogYSAqIGNUICogY1QgKiBjVCkpOwppZiBYaVsyXSA8PiAwLjAgdGhlbiBzaWcgOj0gWGlbMl0gLyBhYnMoWGlbMl0pIGVsc2Ugc2lnIDo9IDEuMDsKaWYgWGlbMV0gPSAwLjAgdGhlbiBYb1syXSA6PSBzaWcgKiBwaSAvIDIuMCBlbHNlCiAgIGJlZ2luCiAgIFhvWzJdIDo9IGFyY3RhbihYaVsyXS9YaVsxXSk7CiAgIGlmIChYaVsxXSA8IDAuMCkgYW5kIChYaVsyXSA+PSAwLjApIHRoZW4gWG9bMl0gOj0gWG9bMl0gKyBwaTsKICAgaWYgKFhpWzFdIDwgMC4wKSBhbmQgKFhpWzJdIDwgMC4wKSB0aGVuIFhvWzJdIDo9IFhvWzJdIC0gcGkKICAgZW5kOwpOIDo9IGEgLyBzcXJ0KDEuMCAtIGUxc3FyICogc2luKFhvWzFdKSAqIHNpbihYb1sxXSkpOwpYb1szXSA6PSBwIC8gY29zKFhvWzFdKSAtIE47CgplbmQ7IHtwcm9jZWR1cmUgWFlaMkxMQX0KCnsqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKip9Cgpwcm9jZWR1cmUgc2F0cG9zKGVwaCA6IHZlYzE2OyAgICB7ZXBoZW1lcmlzfQogICAgICAgICAgICAgICAgIFR0ciA6IHJlYWw7ICAgICB7c2F0ZWxsaXRlIEdQUyB0aW1lfQogICAgICAgICAgICAgdmFyIFRyZWw6IHJlYWw7ICAgICB7cmVsYXRpdmlzdGljIGNvcnJlY3Rpb24gdGVybX0KICAgICAgICAgICAgIHZhciBYICAgOiB2ZWMzKTsgICAge3NhdGVsbGl0ZSBwb3NpdGlvbn0KCnZhcgogIE0wLCBkbiwgZWMsIEEsIFcwLCBpMCwgdywgV2RvdCwgQ3VjLCBDdXMsIENyYywgQ3JzLCBDaWMsIENpcywgVG9lLCBJZG90OiByZWFsOwogIFQsIG4wLCBuLCBNLCBFLCBFb2xkLCBzbnUsIGNudSwgbnUsIHBoaSwgZHUsIGRyLCBkaSwgdSwgciwgaSwgWGRhc2gsIFlkYXNoLCBXYzogcmVhbDsKICBrOiBpbnRlZ2VyOwoKYmVnaW4Ke2ZvciBjbGFyaXR5IG9mIGNvZGUsIGNvcHkgdGhlIGVwaGVtZXJpcyBwYXJhbWV0ZXJzIGFuZCBjb252ZXJ0IHRvIHJhZGlhbnN9CkNycyA6PSBlcGhbMV07CmRuICA6PSBlcGhbMl0gKiBwaTsKTTAgIDo9IGVwaFszXSAqIHBpOwpDdWMgOj0gZXBoWzRdOwplYyAgOj0gZXBoWzVdOwpDdXMgOj0gZXBoWzZdOwpBICAgOj0gZXBoWzddICogZXBoWzddOwpUb2UgOj0gZXBoWzhdOwpDaWMgOj0gZXBoWzldOwpXMCAgOj0gZXBoWzEwXSAqIHBpOwpDaXMgOj0gZXBoWzExXTsKaTAgIDo9IGVwaFsxMl0gKiBwaTsKQ3JjIDo9IGVwaFsxM107CncgICA6PSBlcGhbMTRdICogcGk7Cldkb3Q6PSBlcGhbMTVdICogcGk7Cmlkb3Q6PSBlcGhbMTZdICogcGk7CgpUOj0gVHRyIC0gVG9lOwppZiBUID4gIDMwMjQwMCB0aGVuIFQgOj0gVCAtIDYwNDgwMDsKaWYgVCA8IC0zMDI0MDAgdGhlbiBUIDo9IFQgKyA2MDQ4MDA7CgpuMCA6PSBzcXJ0KG11IC8gKEEqQSpBKSk7Cm4gOj0gbjAgKyBkbjsKCk0gOj0gTTAgKyBuKlQ7CkUgOj0gTTsge3N0YXJ0IHZhbHVlIGZvciBFfQpyZXBlYXQKICAgRW9sZCA6PSBFOwogICBFIDo9IE0gKyBlYyAqIHNpbihFKTsKICAgdW50aWwgYWJzKEUgLSBFb2xkKSA8IDEuMGUtODsKCnNudSA6PSBzcXJ0KDEgLSBlYyplYykgKiBzaW4oRSkgLyAoMSAtIGVjKmNvcyhFKSk7IApjbnUgOj0gKGNvcyhFKSAtIGVjKSAvICgxIC0gZWMqY29zKEUpKTsgCmlmIGNudSA9IDAgdGhlbiBudSA6PSBwaS8yICogc251IC8gYWJzKHNudSkKZWxzZSBpZiAoc251ID0gMCkgYW5kIChjbnUgPiAwKSB0aGVuIG51IDo9IDAKZWxzZSBpZiAoc251ID0gMCkgYW5kIChjbnUgPCAwKSB0aGVuIG51IDo9IHBpCmVsc2UgbnUgOj0gYXJjdGFuKHNudS9jbnUpCisgb3JkKGNudTwwKSAqIHBpICogc251IC8gYWJzKHNudSk7CgpwaGkgOj0gbnUgKyB3OwoKZHUgOj0gQ3VjKmNvcygyKnBoaSkgKyBDdXMqc2luKDIqcGhpKTsKZHIgOj0gQ3JjKmNvcygyKnBoaSkgKyBDcnMqc2luKDIqcGhpKTsKZGkgOj0gQ2ljKmNvcygyKnBoaSkgKyBDaXMqc2luKDIqcGhpKTsKCnUgOj0gcGhpICsgZHU7CnIgOj0gQSooMSAtIGVjKmNvcyhFKSkgKyBkcjsKaSA6PSBpMCArIGlkb3QqVCArZGk7CgpYZGFzaCA6PSByKmNvcyh1KTsKWWRhc2ggOj0gcipzaW4odSk7CgpXYzo9IFcwICsgKFdkb3QgLSBXZWRvdCkqVCAtIFdlZG90KlRvZTsKClhbMV0gOj0gWGRhc2gqY29zKFdjKSAtIFlkYXNoKmNvcyhpKSpzaW4oV2MpOwpYWzJdIDo9IFhkYXNoKnNpbihXYykgKyBZZGFzaCpjb3MoaSkqY29zKFdjKTsKWFszXSA6PSBZZGFzaCpzaW4oaSk7CgpUcmVsIDo9IEYgKiBlYyAqIGVwaFs3XSAqIHNpbihFKSB7cmVsYXRpdmlzdGljIGNvcnJlY3Rpb24gdGVybX0KCmVuZDsge3Byb2NlZHVyZSBzYXRwb3N9Cgp7KioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqfQoKcHJvY2VkdXJlIGNhbGNBekVsKFhzLCAgICAgICAgICAgICAge3NhdGVsbGl0ZSBFQ0VGIFhZWn0KICAgICAgICAgICAgICAgICAgIFh1ICA6ICB2ZWMzOyAgICAge3VzZXIgRUNFRiBYWVp9CiAgICAgICAgICAgICAgIHZhciBBeiwgICAgICAgICAgICAgIHthemltdXRoIFtyYWRdfQogICAgICAgICAgICAgICAgICAgRWwgIDogcmVhbDsgICAgICB7ZWxldmF0aW9uIFtyYWRdfQogICAgICAgICAgICAgICB2YXIgc3RhdDogYm9vbGVhbik7ICB7Y2FsY3VsYXRpb24gc3VjY2VlZGVkOiBzdGF0ID0gdHJ1ZX0KCnZhcgogICBSLCBwLCB4LCB5LCB6LCBzOiByZWFsOwogICBlOiBhcnJheVsxLi4zLDEuLjNdIG9mIHJlYWw7CiAgIGksIGs6IGludGVnZXI7CiAgIGQ6IHZlYzM7CgpiZWdpbgoKeCA6PSBYdVsxXTsKeSA6PSBYdVsyXTsKeiA6PSBYdVszXTsKcCA6PSBzcXJ0KHgqeCArIHkqeSk7CmlmIHAgPSAwLjAgdGhlbiBzdGF0IDo9IGZhbHNlIGVsc2Ugc3RhdCA6PSB0cnVlOwoKaWYgc3RhdCB0aGVuCiAgIGJlZ2luCgogICBSIDo9IHNxcnQoeCp4ICsgeSp5ICsgeip6KTsKICAgZVsxLDFdIDo9IC0geSAvIHA7CiAgIGVbMSwyXSA6PSArIHggLyBwOwogICBlWzEsM10gOj0gMC4wOwogICBlWzIsMV0gOj0gLSB4KnogLyAocCpSKTsKICAgZVsyLDJdIDo9IC0geSp6IC8gKHAqUik7CiAgIGVbMiwzXSA6PSBwIC8gUjsKICAgZVszLDFdIDo9IHggLyBSOwogICBlWzMsMl0gOj0geSAvIFI7CiAgIGVbMywzXSA6PSB6IC8gUjsKCiAgIGZvciBrIDo9IDEgdG8gMyBkbwogICAgICBiZWdpbgogICAgICBkW2tdIDo9IDAuMDsKICAgICAgZm9yIGkgOj0gMSB0byAzIGRvIGRba10gOj0gZFtrXSArIChYc1tpXSAtIFh1W2ldKSAqIGVbayxpXQogICAgICBlbmQ7CgogICBzIDo9IGRbM10gLyBzcXJ0KGRbMV0qZFsxXSArIGRbMl0qZFsyXSArIGRbM10qZFszXSk7CiAgIGlmIHMgPSAxLjAgdGhlbiBFbCA6PSAwLjUgKiBwaSBlbHNlIEVsIDo9IGFyY3RhbihzIC8gc3FydCgxLjAgLSBzKnMpKTsKCiAgIGlmIChkWzJdID0gMC4wKSBhbmQgKGRbMV0gPiAwLjApIHRoZW4gQXogOj0gMC41ICogcGkgZWxzZQogICAgICBpZiAoZFsyXSA9IDAuMCkgYW5kIChkWzFdIDwgMC4wKSB0aGVuIEF6IDo9IDEuNSAqIHBpIGVsc2UKICAgICAgICAgYmVnaW4KICAgICAgICAgQXogOj0gYXJjdGFuKGRbMV0gLyBkWzJdKTsKICAgICAgICAgaWYgZFsyXSA8IDAuMCB0aGVuIEF6IDo9IEF6ICsgcGkgZWxzZQogICAgICAgICAgICBpZiAoZFsyXSA+IDAuMCkgYW5kIChkWzFdIDwgMC4wKSB0aGVuIEF6IDo9IEF6ICsgMi4wICogcGkKICAgICAgICAgZW5kOwoKICAgZW5kOwoKZW5kOyB7cHJvY2VkdXJlIGNhbGNBekVsfQoKeyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKn0KCnByb2NlZHVyZSBpb25vY29yciAoaW9uICAgOnZlYzg7ICAgIHtpb25vIGNvcnJlY3Rpb24gY29lZmZpY2llbnRzIGZyb20KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hdiBtZXNzYWdlfQogICAgICAgICAgICAgICAgICAgIExhdHUsICAgICAgICAgICB7dXNlcidzIGxhdGl0dWRlIFtyYWRdfQogICAgICAgICAgICAgICAgICAgIExvbnUsICAgICAgICAgICB7dXNlcidzIGxvbmdpdHVkZSBbcmFkXX0KICAgICAgICAgICAgICAgICAgICBBeiwgICAgICAgICAgICAge1NWIGF6aW11dGggW3JhZF19CiAgICAgICAgICAgICAgICAgICAgRWwsICAgICAgICAgICAgIHtTViBlbGV2YXRpb24gW3JhZF19CiAgICAgICAgICAgICAgICAgICAgVHRyICAgOiByZWFsOyAgIHtTViBzaWduYWwgdHJhbnNtaXNzaW9uIHRpbWUgW3NlY119CiAgICAgICAgICAgICAgICB2YXIgZFRpb25vOiByZWFsKTsgIHtJb25vc3BoZXJpYyBkZWxheSBbc2VjXX0KCnZhcgogIHBoaSwgTGF0aSwgTG9uaSwgTGF0bSwgVCwgRiwgeCwgcGVyLCBhbXA6IHJlYWw7CiAgYTAsIGExLCBhMiwgYTMsIGIwLCBiMSwgYjIsIGIzOiByZWFsOwoKYmVnaW4KCntmb3IgY2xhcml0eSBjb3B5IGFycmF5fQphMCA6PSBpb25bMV07CmExIDo9IGlvblsyXTsKYTIgOj0gaW9uWzNdOwphMyA6PSBpb25bNF07CmIwIDo9IGlvbls1XTsKYjEgOj0gaW9uWzZdOwpiMiA6PSBpb25bN107CmIzIDo9IGlvbls4XTsKCntjb252ZXJ0IGZyb20gcmFkaWFucyB0byBzZW1pY2lyY2xlc30KTGF0dSA6PSBMYXR1IC8gcGk7IExvbnUgOj0gTG9udSAvIHBpOyBBeiA6PSBBeiAvIHBpOyBFbCA6PSBFbCAvIHBpOwoKe2NhbGN1bGF0aW9ufQpwaGkgOj0gMC4wMTM3IC8gKEVsICsgMC4xMSkgLSAwLjAyMjsKTGF0aSA6PSBMYXR1ICsgcGhpICogY29zIChBeiAqIHBpKTsKaWYgTGF0aSA+ICswLjQxNiB0aGVuIExhdGkgOj0gKzAuNDE2IGVsc2UgaWYgTGF0aSA8IC0wLjQxNiB0aGVuIExhdGkgOj0gLTAuNDE2OwpMb25pIDo9IExvbnUgKyBwaGkgKiBzaW4oQXogKiBwaSkgLyBjb3MoTGF0aSAqIHBpKTsKTGF0bSA6PSBMYXRpICsgMC4wNjQgKiBjb3MoKExvbmkgLSAxLjYxNykgKiBwaSk7ClQgOj0gNC4zMkUrNCAqIExvbmkgKyBUdHI7CndoaWxlIFQgPj0gODY0MDAgZG8gVCA6PSBUIC0gODY0MDAgZWxzZSB3aGlsZSBUIDwgMCBkbyBUIDo9IFQgKyA4NjQwMDsKRiA6PSAxLjAgKyAxNi4wICogKDAuNTMgLSBFbCkgKiAoMC41MyAtIEVsKSAqICgwLjUzIC0gRWwpOwpwZXIgOj0gYjAgKyBiMSAqIExhdG0gKyBCMiAqIExhdG0gKiBMYXRtICsgYjMgKiBMYXRtICogTGF0bSAqIExhdG07CmlmIHBlciA8IDcyMDAwLjAgdGhlbiBwZXIgOj0gNzIwMDAuMDsKeCA6PSAyICogcGkgKiAoVCAtIDUwNDAwLjApIC8gcGVyOwphbXAgOj0gYTAgKyBhMSAqIExhdG0gKyBBMiAqIExhdG0gKiBMYXRtICsgYTMgKiBMYXRtICogTGF0bSAqIExhdG07CmlmIGFtcCA8IDAuMCB0aGVuIGFtcCA6PSAwLjA7CmlmIGFicyh4KSA+PSAxLjU3IHRoZW4gZFRpb25vIDo9IEYgKiA1LjBFLTkgZWxzZQogICBkVGlvbm8gOj0gRiAqICg1LjBFLTkgKyBhbXAgKiAoMS4wIC0geCAqIHggLyAyLjAgKyB4ICogeCAqIHggKiB4IC8yNC4wKSkKCmVuZDsge3Byb2NlZHVyZSBpb25vY29ycn0KCnsqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKip9Cgp7QXQgbWFueSBwbGFjZXMgaW4gdGhlIGZvbGxvd2luZyBwcm9jZWR1cmUgc29sdmUgdGhlIHN1YmRldGVybWluYW50IHZhbHVlIG9mIGEKIDQgeCA0IGFycmF5IGlzIHJlcXVpcmVkLiBGb3IgY29udmVuaWVuY2UgdGhlIGZ1bmN0aW9uIHN1YiBpcyBkZWZpbmVkIGJlbG93fQoKZnVuY3Rpb24gc3ViIChBIDogbWF0MTY7ICAgICAge2lucHV0IDQgeCA0IGFycmF5fQogICAgICAgICAgICAgIHIsICAgICAgICAgICAgICB7cm93IG51bWJlciB0byBiZSBkZWxldGVkfQogICAgICAgICAgICAgIGMgOiBpbnRlZ2VyICAgICB7Y29sdW1uIG51bWJlciB0byBiZSBkZWxldGVkfQogICAgICAgICAgICAgICkgOiBkb3VibGU7ICAgICB7dmFsdWUgb2YgMyB4IDMgc3ViZGV0ZXJtaW5hbnR9Cgp2YXIKICBCOiBhcnJheVsxLi4zLDEuLjNdIG9mIGRvdWJsZTsKICBpLCBpMSwgaiwgajE6IGludGVnZXI7CgpiZWdpbgppMSA6PSAwOwpmb3IgaSA6PSAxIHRvIDQgZG8gaWYgaSA8PiByIHRoZW4KICAgYmVnaW4KICAgaTEgOj0gaTEgKyAxOwogICBqMSA6PSAwOwogICBmb3IgaiA6PSAxIHRvIDQgZG8gaWYgaiA8PiBjIHRoZW4KICAgICAgYmVnaW4KICAgICAgajEgOj0gajEgKyAxOwogICAgICBCW2kxLGoxXSA6PSBBW2ksal0KICAgICAgZW5kCiAgIGVuZDsKc3ViIDo9ICsgQlsxLDFdKihCWzIsMl0qQlszLDNdIC0gQlsyLDNdKkJbMywyXSkKICAgICAgIC0gQlsyLDFdKihCWzEsMl0qQlszLDNdIC0gQlszLDJdKkJbMSwzXSkKICAgICAgICsgQlszLDFdKihCWzEsMl0qQlsyLDNdIC0gQlsxLDNdKkJbMiwyXSk7CmVuZDsge2Z1bmN0aW9uIHN1Yn0KCnsqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKip9Cgpwcm9jZWR1cmUgc29sdmUoWHMgICAgOiBtYXQ5NjsgICAge2FycmF5IHdpdGggMyBjb2x1bW5zIGFuZCAzMiByb3dzCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZm9yIHRoZSBjb29yZGluYXRlcyBvZiB0aGUgc2F0J3N9CiAgICAgICAgICAgICAgICBTViAgICA6IHZlY2IzMjsgICB7dmFsaWQgcHJuJ3N9CiAgICAgICAgICAgICAgICBQICAgICA6IHZlYzMyOyAgICB7cHNldWRvcmFuZ2VzfQogICAgICAgICAgICB2YXIgWHIgICAgOiB2ZWMzOyAgICAge2lucHV0IG9mIGluaXRpYWwgZ3Vlc3MsIG91dHB1dCBvZgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbmFsIHBvc2l0aW9ufQogICAgICAgICAgICB2YXIgQ3IgICAgOiByZWFsOyAgICAge3JlY2VpdmVyIGNsb2NrIGVycm9yfQogICAgICAgICAgICB2YXIgc3RhdHVzOiBib29sZWFuKTsge3RydWU6IGNhbGN1bGF0aW9uIE9LLCBmYWxzZTogbm8gc29sdXRpb259Cgp7cHJvY2VkdXJlIHNvbHZlIHJlcXVpcmVzIHRoZSBmb2xsb3dpbmcgdHlwZXMgdG8gYmUgZGVjbGFyZWQgaW4gdGhlCiBtYWluIGJvZHkgb2YgdGhlIHByb2dyYW06CiB0eXBlCiAgICBtYXQ5NiA9IGFycmF5WzEuLjMyLDEuLjNdIG9mIHJlYWw7CiAgICB2ZWNiMzIgPSBhcnJheVsxLi4zMl0gb2YgYm9vbGVhbjsKICAgIHZlYzMyID0gYXJyYXlbMS4uMzJdIG9mIHJlYWw7CiAgICB2ZWMzID0gYXJyYXlbMS4uM10gb2YgcmVhbDsKICAgIG1hdDE2ID0gYXJyYXlbMS4uNCwxLi40XSBvZiByZWFsO30KCnZhcgogICBwcm4sIGl0LCBpLCBqLCBrOiBpbnRlZ2VyOwogICBSLCBMOiBhcnJheVsxLi4zMl0gb2YgcmVhbDsKICAgQTogYXJyYXlbMS4uMzIsMS4uNF0gb2YgcmVhbDsKICAgQUw6IGFycmF5WzEuLjRdIG9mIHJlYWw7CiAgIEFBLCBBQWk6IG1hdDE2OwogICBuOiBsb25naW50OwogICBkZXQ6IHJlYWw7CiAgIEQ6IGFycmF5WzEuLjRdIG9mIHJlYWw7CgoKYmVnaW4ge3Byb2NlZHVyZSBzb2x2ZX0KCml0IDo9IDA7IHtpdGVyYXRpb24gY291bnRlcn0KCnJlcGVhdCB7aXRlcmF0aW9uc30KCiAgIGl0IDo9aXQgKyAxOyB7aW5jcmVhc2UgaXRlcmF0aW9uIGNvdW50ZXJ9CgogICBmb3IgcHJuIDo9IDEgdG8gMzIgZG8gaWYgU1ZbcHJuXSB0aGVuCiAgICAgIGJlZ2luCiAgICAgIFJbcHJuXSA6PSAge3JhbmdlIGZyb20gcmVjZWl2ZXIgdG8gc2F0ZWxsaXRlfQogICAgICAgICBzcXJ0KChYclsxXSAtIFhzW3BybiwxXSkgKiAoWHJbMV0gLSBYc1twcm4sMV0pCiAgICAgICAgICAgICsgKFhyWzJdIC0gWHNbcHJuLDJdKSAqIChYclsyXSAtIFhzW3BybiwyXSkKICAgICAgICAgICAgKyAoWHJbM10gLSBYc1twcm4sM10pICogKFhyWzNdIC0gWHNbcHJuLDNdKSk7CiAgICAgIExbcHJuXSA6PSBQW3Bybl0gLSBSW3Bybl07IHtyYW5nZSByZXNpZHVhbCB2YWx1ZX0KICAgICAgZm9yIGsgOj0gMSB0byAzIGRvIEFbcHJuLGtdIDo9IChYcltrXSAtIFhzW3BybixrXSkgLyBSW3Bybl07CiAgICAgIEFbcHJuLDRdIDo9IC0xLjAge0EgaXMgdGhlIGdlb21ldHJ5IG1hdHJpeCBvciBtb2RlbCBtYXRyaXh9CiAgICAgIGVuZDsKCiAgIEZvciBrIDo9MSB0byA0IGRvIHtjYWxjdWxhdGUgQS5MfQogICAgICBiZWdpbgogICAgICBBTFtrXSA6PSAwLjA7CiAgICAgIGZvciBwcm4gOj0gMSB0byAzMiBkbyBpZiBTVltwcm5dIHRoZW4KICAgICAgICAgQUxba10gOj0gQUxba10gKyBBW3BybixrXSAqIExbcHJuXQogICAgICBlbmQ7CgogICBmb3IgayA6PSAxIHRvIDQgZG8gZm9yIGkgOj0gMSB0byA0IGRvIHtjYWxjdWxhdGUgQS5BfQogICAgICBiZWdpbgogICAgICBBQVtrLGldIDo9MC4wOwogICAgICBmb3IgcHJuIDo9IDEgdG8gMzIgZG8gaWYgU1ZbcHJuXSB0aGVuCiAgICAgICAgIEFBW2ssaV0gOj0gQUFbayxpXSArIEFbcHJuLGtdICogQVtwcm4saV0KICAgICAgZW5kOwoKICAge2ludmVydCBBLkF9CiAgIGRldCA6PSArIEFBWzEsMV0gKiBzdWIoQUEsMSwxKSAtIEFBWzIsMV0gKiBzdWIoQUEsMiwxKQogICAgICAgICAgKyBBQVszLDFdICogc3ViKEFBLDMsMSkgLSBBQVs0LDFdICogc3ViKEFBLDQsMSk7CiAgIGlmIGRldCA9IDAuMCB0aGVuIHN0YXR1cyA6PSBmYWxzZSBlbHNlCiAgICAgIGJlZ2luCgogICAgICBzdGF0dXMgOj0gdHJ1ZTsKCiAgICAgIGZvciBrIDo9IDEgdG8gNCBkbyBmb3IgaSA6PSAxIHRvIDQgZG8KICAgICAgICAgYmVnaW4KICAgICAgICAgbjo9IGsgKyBpOyBpZiBvZGQobikgdGhlbiBqIDo9IC0xIGVsc2UgaiA6PTE7CiAgICAgICAgIEFBaVtrLGldIDo9IGogKiBzdWIoQUEsaSxrKSAvIGRldAogICAgICAgICBlbmQ7CgogICAgICB7Y2FsY3VsYXRlIChpbnZBLkEpLihBLkwpfQogICAgICBmb3IgayA6PSAxIHRvIDQgZG8KICAgICAgICAgYmVnaW4KICAgICAgICAgRFtrXSA6PSAwLjA7CiAgICAgICAgIGZvciBpIDo9IDEgdG8gNCBkbyBEW2tdIDo9IERba10gKyBBQWlbayxpXSAqIEFMW2ldCiAgICAgICAgIGVuZDsKCiAgICAgIHt1cGRhdGUgcG9zaXRpb259CiAgICAgIGZvciBrIDo9IDEgdG8gMyBkbyBYcltrXSA6PSBYcltrXSArIERba107CgogICAgICBlbmQ7CgogICB1bnRpbCAoaXQgPSA2KXt0aGVyZSBpcyBzb21ldGhpbmcgd3JvbmcgaWYgbW9yZSB0aGFuIDYgaXRlcmF0aW9ucyBhcmUgcmVxdWlyZWR9CiAgICAgIG9yICgoYWJzKERbMV0pICsgYWJzKERbMl0pICsgYWJzKERbM10pKSA8IDEuMEUtMikge2l0ZXJhdGlvbiBjcml0ZXJpb259CiAgICAgICAgIG9yIChub3Qoc3RhdHVzKSk7IHtjYWxjdWxhdGlvbiBub3Qgc3VjY2VlZGVkfQoKQ3IgOj0gRFs0XTsge3JlY2VpdmVyIGNsb2NrIGVycm9yfQoKaWYgaXQgPSA2IHRoZW4gYmVnaW4gd3JpdGVsbignc29sdmUgaXQgOiAnLGl0KTsgc3RhdHVzIDo9IGZhbHNlIGVuZDsge2l0ZXJhdGlvbiBub3Qgc3VjY2VlZGVkfQoKZW5kOyB7cHJvY2VkdXJlIHNvbHZlfQoKeyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKn0KCmJlZ2luIHttYWlufQp7dGhlIGZvbGxvd2luZyBkYXRhIHNob3VsZCBiZSBhdmFpbGFibGU6CiAxLiBQc2V1ZG9yYW5nZSB3aXRoIHJlY2VpdmVyIHRpbWUgb2YgcmVjZXB0aW9uIGZvciBlYWNoIFNWCiAyLiBFcGhlbWVyaXMgYW5kIGFsbWFuYWMgZm9yIGVhY2ggU1YKIDMuIElvbm8gY29lZmZpY2llbnRzfQoKe29wZW4gaW5wdXQgZGF0YWZpbGV9CmFzc2lnbihpbnAsJ2lucC50eHQnKTsgcmVzZXQoaW5wKTsKCntyZWFkIEdQUyB0aW1lIG9mIHJlY2VwdGlvbn0KcmVhZGxuKGlucCk7IHtza2lwIGNvbW1lbnQgbGluZX0KcmVhZGxuKGlucCxUcmMpOwoKe3JlYWQgaW9ubyBjb2VmZmljaWVudHN9CnJlYWRsbihpbnApOyB7c2tpcCBjb21tZW50IGxpbmV9CmZvciBpIDo9IDEgdG8gOCBkbyByZWFkbG4oaW5wLGlvbltpXSk7CgpyZWFkbG4oaW5wKTsge3NraXAgY29tbWVudCBsaW5lfQp7cmVhZCBwc2V1ZG9yYW5nZXN9CmZvciBwcm4gOj0gMSB0byAzMiBkbyBTVltwcm5dIDo9IGZhbHNlOwpyZXBlYXQKICAgcmVhZChpbnAscHJuKTsKICAgaWYgcHJuIDw+IDAgdGhlbgogICAgICBiZWdpbgogICAgICByZWFkbG4oaW5wLFByYXdbcHJuXSk7CiAgICAgIFNWW3Bybl0gOj0gdHJ1ZQogICAgICBlbmQKICAgZWxzZSByZWFkbG4oaW5wKTsKICAgdW50aWwgcHJuID0gMDsKCnJlYWRsbihpbnApOyB7c2tpcCBjb21tZW50IGxpbmV9CntyZWFkIGVwaGVtZXJpcy0gYW5kIGNsb2NrIGRhdGF9CnJlcGVhdAogICByZWFkbG4oaW5wLHBybik7CiAgIGZvciBpIDo9IDEgdG8gMTYgZG8gcmVhZGxuKGlucCxlcGhbcHJuLGldKTsKICAgZm9yIGkgOj0gMSB0byA1IGRvIHJlYWRsbihpbnAsY2xrW3BybixpXSk7CiAgIHVudGlsIGVvZihpbnApOwpjbG9zZShpbnApOwoKe3VzZXIgaW5wdXQgb2Ygc3RhcnQgcG9zaXRpb259CndyaXRlKCdTdGFydCBwb3NpdGlvbiBMYXQgW2RlZy5kZWNdLCBMb24gW2RlZy5kZWNdLCBBbHQgW21dIDogJyk7CiAgIHJlYWRsbihYbGxhWzFdLFhsbGFbMl0sWGxsYVszXSk7ClhsbGFbMV0gOj0gWGxsYVsxXSAqIHBpIC8gMTgwLjA7IFhsbGFbMl0gOj1YbGxhWzJdICogcGkgLyAxODAuMDsKe2NvbnZlcnQgbGF0LCBsbiwgYWx0IHRvIEVDRUYgWCwgWSwgWn0KTExBMlhZWihYbGxhLFhyKTsKCntvcGVuIG91dHB1dCBkYXRhIGZpbGV9CmFzc2lnbihvdXQsJ291dHB1dC50eHQnKTsgcmV3cml0ZShvdXQpOwoKe2Fzc3VtaW5nIHRoZSByZWNlaXZlciBjbG9jayBlcnJvciBhbmQgaW5pdGlhbCBwb3NpdGlvbiBub3Qgc3VmZmljaWVudGx5CiBnb29kIGtub3duLCBJIG1ha2UgdHdvIHBhc3NlcyB0aHJvdWdoIHRoZSBwcm9jZXNzaW5nIHN0ZXBzfQoKe1BBU1MgMX0Kd3JpdGVsbihvdXQsJ1BBU1MgMScpOwoKZm9yIHBybiA6PSAxIHRvIDMyIGRvIGlmIFNWW3Bybl0gdGhlbiBiZWdpbiB7ZG8gZm9yIGVhY2ggU1Z9CgogICB7c2V0IGFsbCB0cmFuc2l0IHRpbWVzIHRvIG5vbWluYWwgdmFsdWUgYW5kIGNhbGN1bGF0ZSB0aW1lIG9mIHRyYW5zbWlzc2lvbn0KICAgdGF1IDo9IDAuMDc1OwogICBUdHIgOj0gVHJjIC0gdGF1OwoKICAge2NhbGN1bGF0ZSBTViBwb3NpdGlvbiBhbmQgY29ycmVjdCBmb3IgZWFydGggcm90YXRpb259CiAgIGZvciBpIDo9IDEgdG8gMTYgZG8gdG1wMTZbaV0gOj0gZXBoW3BybixpXTsKICAgc2F0cG9zKHRtcDE2LFR0cixUcmVsLHRtcDMpOwogICBhbHBoYSA6PSB0YXUgKiBXZTsKICAgWHNbcHJuLDFdIDo9ICsgdG1wM1sxXSAqIGNvcyhhbHBoYSkgKyB0bXAzWzJdICogc2luKGFscGhhKTsKICAgWHNbcHJuLDJdIDo9IC0gdG1wM1sxXSAqIHNpbihhbHBoYSkgKyB0bXAzWzJdICogY29zKGFscGhhKTsKICAgWHNbcHJuLDNdIDo9ICsgdG1wM1szXTsKICAgd3JpdGVsbihvdXQsJ1NWICAgICA6ICcscHJuOjIsWHNbcHJuLDFdOjE1OjMsWHNbcHJuLDJdOjE1OjMsWHNbcHJuLDNdOjE1OjMpOwoKICAge2NhbGN1bGF0ZSBhemltdXRoIGFuZCBlbGV2YXRpb259CiAgIGZvciBpIDo9IDEgdG8gMyBkbyB0bXAzW2ldIDo9IFhzW3BybixpXTsKICAgY2FsY0F6RWwodG1wMyxYcixBeixFbCxzdGF0dXMpOwogICBpZiBub3Qgc3RhdHVzIHRoZW4KICAgICAgYmVnaW4gd3JpdGVsbignRXJyb3IgaW4gY2FsY0F6RWwgLSBjaGVjayBpbnB1dCBkYXRhJyk7IGV4aXQgZW5kOwogICB3cml0ZWxuKG91dCwnQXosIEVsIDogJyxwcm46MixBeioxODAuMC9waToxMTozLEVsKjE4MC4wL3BpOjEwOjMpOwoKICAge2NhbGN1bGF0ZSBwc2V1ZG9yYW5nZSBjb3JyZWN0aW9ucyBhbmQgYXBwbHkgdG8gcHNldWRvcmFuZ2VzfQoKICAge2Nsb2NrIGNvcnJlY3Rpb259CiAgIFQgOj0gVHRyIC0gY2xrW3BybiwyXTsKICAge2NvcnJlY3QgZm9yIHdlZWsgY3Jvc3NvdmVyfQogICBpZiBUID4gIDMwMjQwMCB0aGVuIFQgOj0gVCAtIDYwNDgwMDsKICAgaWYgVCA8IC0zMDI0MDAgdGhlbiBUIDo9IFQgKyA2MDQ4MDA7CgogICBkVGNsY2sgOj0gKyBjbGtbcHJuLDVdICsgY2xrW3Bybiw0XSAqIFQgKyBjbGtbcHJuLDNdICogVCAqIFQKICAgICAgICAgICAgICsgVHJlbCAtIGNsa1twcm4sMV07CgogICB7aW9ubyBjb3JyZWN0aW9ufQogICBMYXQgOj0gWGxsYVsxXSA7IExvbiA6PSBYbGxhWzJdOwogICBpb25vY29ycihpb24sTGF0LExvbixBeixFbCxUdHIsZFRpb25vKTsKCiAgIHt0cm9wbyBjb3JyZWN0aW9uIHVzaW5nIHN0YW5kYXJkIGF0bW9zcGhlcmUgdmFsdWVzfQogICBkUnRyb3AgOj0gKyAyLjMxMiAvIHNpbihzcXJ0KEVsICogRWwgKyAxLjkwNEUtMykpCiAgICAgICAgICAgICArIDAuMDg0IC8gc2luKHNxcnQoRWwgKiBFbCArIDAuNjg1NEUtMykpOwoKICAgd3JpdGVsbihvdXQsJ0NvcnIgICA6ICcscHJuOjIsZFRjbGNrKmM6MTE6MyxkVGlvbm8qYzoxMDozLGRSdHJvcDoxMDozKTsKCiAgIHtjb3JyZWN0IHBzZXVkb3JhbmdlfQogICBQY29yW3Bybl0gOj0gUHJhd1twcm5dICsgZFRjbGNrICogYyAtIGRUaW9ubyAqIGMgLSBkUnRyb3AKCiAgIGVuZDsge2RvIGZvciBlYWNoIFNWfQoKe2NhbGN1bGF0ZSByZWNlaXZlciBwb3NpdGlvbn0Kc29sdmUoWHMsU1YsUGNvcixYcixDcixzdGF0dXMpOwppZiBub3Qgc3RhdHVzIHRoZW4KICAgYmVnaW4gd3JpdGVsbignRXJyb3IgaW4gc29sdmUgLSBjaGVjayBpbnB1dCBkYXRhJyk7IGV4aXQgZW5kOwp3cml0ZWxuKG91dCwnUG9zIFhZWjogJyxYclsxXToxMjozLFhyWzJdOjEyOjMsWHJbM106MTI6MyxDcjoxMjozKTsKe2NvbnZlcnQgYmFjayB0byBMYXQsIExvbiwgQWx0fQpYWVoyTExBKFhyLFhsbGEpOwoKe1BBU1MgMiAtIFRoZSByZWNlaXZlciBwb3NpdGlvbiBhbmQgLWNsb2NrIGVycm9yIGlzIG5vdyB3ZWxsIGVub3VnaCBrbm93bgogdG8gY2FsY3VsYXRlIHRoZSBmaW5hbCBwc2V1ZG9yYW5nZSBjb3JyZWN0aW9uc30Kd3JpdGVsbihvdXQpOyB3cml0ZWxuKG91dCwnUEFTUyAyJyk7Cgp7Y29ycmVjdCByZWNlaXZlciBjbG9ja30KVHJjIDo9IFRyYyArIENyIC8gYzsKCmZvciBwcm4gOj0gMSB0byAzMiBkbyBpZiBTVltwcm5dIHRoZW4gYmVnaW4ge2RvIGZvciBlYWNoIFNWfQoKICAge3JlY2FsY3VsYXRlIHRyYW5zaXQgdGltZSBhbmQgdGltZSBvZiB0cmFuc21pc3Npb259CiAgIHRhdSA6PSAoUGNvcltwcm5dICsgQ3IpIC8gYzsKICAgVHRyIDo9IFRyYyAtIHRhdTsKCiAgIHtyZWNhbGN1bGF0ZSBTViBwb3NpdGlvbiBhbmQgY29ycmVjdCBmb3IgZWFydGggcm90YXRpb259CiAgIGZvciBpIDo9IDEgdG8gMTYgZG8gdG1wMTZbaV0gOj0gZXBoW3BybixpXTsKICAgc2F0cG9zKHRtcDE2LFR0cixUcmVsLHRtcDMpOwogICBhbHBoYSA6PSB0YXUgKiBXZTsKICAgWHNbcHJuLDFdIDo9ICsgdG1wM1sxXSAqIGNvcyhhbHBoYSkgKyB0bXAzWzJdICogc2luKGFscGhhKTsKICAgWHNbcHJuLDJdIDo9IC0gdG1wM1sxXSAqIHNpbihhbHBoYSkgKyB0bXAzWzJdICogY29zKGFscGhhKTsKICAgWHNbcHJuLDNdIDo9ICsgdG1wM1szXTsKICAgd3JpdGVsbihvdXQsJ1NWICAgICA6ICcscHJuOjIsWHNbcHJuLDFdOjE1OjMsWHNbcHJuLDJdOjE1OjMsWHNbcHJuLDNdOjE1OjMpOwoKICAge3JlY2FsY3VsYXRlIGF6aW11dGggYW5kIGVsZXZhdGlvbn0KICAgZm9yIGkgOj0gMSB0byAzIGRvIHRtcDNbaV0gOj0gWHNbcHJuLGldOwogICBjYWxjQXpFbCh0bXAzLFhyLEF6LEVsLHN0YXR1cyk7CiAgIGlmIG5vdCBzdGF0dXMgdGhlbgogICAgICBiZWdpbiB3cml0ZWxuKCdFcnJvciBpbiBjYWxjQXpFbCAtIGNoZWNrIGlucHV0IGRhdGEnKTsgZXhpdCBlbmQ7CiAgIHdyaXRlbG4ob3V0LCdBeiwgRWwgOiAnLHBybjoyLEF6KjE4MC4wL3BpOjExOjMsRWwqMTgwLjAvcGk6MTA6Myk7CgogICB7cmVjYWxjdWxhdGUgcHNldWRvcmFuZ2UgY29ycmVjdGlvbnMgYW5kIGFwcGx5IHRvIHBzZXVkb3Jhbmdlc30KCiAgIHtjbG9jayBjb3JyZWN0aW9ufQogICBUIDo9IFR0ciAtIGNsa1twcm4sMl07CiAgIHtjb3JyZWN0IGZvciB3ZWVrIGNyb3Nzb3Zlcn0KICAgaWYgVCA+ICAzMDI0MDAgdGhlbiBUIDo9IFQgLSA2MDQ4MDA7CiAgIGlmIFQgPCAtMzAyNDAwIHRoZW4gVCA6PSBUICsgNjA0ODAwOwogICBkVGNsY2sgOj0gKyBjbGtbcHJuLDVdICsgY2xrW3Bybiw0XSAqIFQgKyBjbGtbcHJuLDNdICogVCAqIFQKICAgICAgICAgICAgICsgVHJlbCAtIGNsa1twcm4sMV07CgogICB7aW9ubyBjb3JyZWN0aW9ufQogICBMYXQgOj0gWGxsYVsxXTsgTG9uIDo9IFhsbGFbMl07CiAgIGlvbm9jb3JyKGlvbixMYXQsTG9uLEF6LEVsLFR0cixkVGlvbm8pOwoKICAge3Ryb3BvIGNvcnJlY3Rpb24gdXNpbmcgc3RhbmRhcmQgYXRtb3NwaGVyZSB2YWx1ZXN9CiAgIGRSdHJvcCA6PSArIDIuMzEyIC8gc2luKHNxcnQoRWwgKiBFbCArIDEuOTA0RS0zKSkKICAgICAgICAgICAgICsgMC4wODQgLyBzaW4oc3FydChFbCAqIEVsICsgMC42ODU0RS0zKSk7CgogICB3cml0ZWxuKG91dCwnQ29yciAgIDogJyxwcm46MixkVGNsY2sqYzoxMTozLGRUaW9ubypjOjEwOjMsZFJ0cm9wOjEwOjMpOwoKICAge2NvcnJlY3QgcHNldWRvcmFuZ2V9CiAgIFBjb3JbcHJuXSA6PSBQcmF3W3Bybl0gKyBkVGNsY2sgKiBjIC0gZFRpb25vICogYyAtIGRSdHJvcCArIENyCgogICBlbmQ7IHtkbyBmb3IgZWFjaCBTVn0KCntjYWxjdWxhdGUgcmVjZWl2ZXIgcG9zaXRpb259CnNvbHZlKFhzLFNWLFBjb3IsWHIsQ3Isc3RhdHVzKTsKaWYgbm90IHN0YXR1cyB0aGVuCiAgIGJlZ2luIHdyaXRlbG4oJ0Vycm9yIGluIHNvbHZlIC0gY2hlY2sgaW5wdXQgZGF0YScpOyBleGl0IGVuZDsKd3JpdGVsbihvdXQsJ1BvcyBYWVo6ICcsWHJbMV06MTI6MyxYclsyXToxMjozLFhyWzNdOjEyOjMsQ3I6MTI6Myk7Cntjb252ZXJ0IGJhY2sgdG8gTGF0LCBMb24sIEFsdH0KWFlaMkxMQShYcixYbGxhKTsKd3JpdGVsbihvdXQsJ1BvcyBMTEE6ICcsWGxsYVsxXSoxODAuMC9waToxNTo4LFhsbGFbMl0qMTgwLjAvcGk6MTU6OCxYbGxhWzNdOjEyOjMpOwoKY2xvc2Uob3V0KQplbmQuIHttYWlufQoK