mpv-conf/shaders/ravu-r2.glsl

351 lines
34 KiB
Text
Raw Normal View History

2021-12-03 20:50:08 +08:00
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//!DESC RAVU (step1, luma, r2, compute)
//!HOOK LUMA
//!BIND HOOKED
//!BIND ravu_lut2
//!SAVE ravu_int11
//!WHEN HOOKED.w OUTPUT.w / 0.707106 < HOOKED.h OUTPUT.h / 0.707106 < *
//!COMPUTE 32 8
shared float inp0[385];
void hook() {
ivec2 group_base = ivec2(gl_WorkGroupID) * ivec2(gl_WorkGroupSize);
int local_pos = int(gl_LocalInvocationID.x) * 11 + int(gl_LocalInvocationID.y);
for (int id = int(gl_LocalInvocationIndex); id < 385; id += int(gl_WorkGroupSize.x * gl_WorkGroupSize.y)) {
int x = id / 11, y = id % 11;
inp0[id] = HOOKED_tex(HOOKED_pt * vec2(float(group_base.x+x)+(-0.5), float(group_base.y+y)+(-0.5))).x;
}
groupMemoryBarrier();
barrier();
{
float luma0 = inp0[local_pos + 0];
float luma4 = inp0[local_pos + 11];
float luma5 = inp0[local_pos + 12];
float luma6 = inp0[local_pos + 13];
float luma7 = inp0[local_pos + 14];
float luma1 = inp0[local_pos + 1];
float luma8 = inp0[local_pos + 22];
float luma9 = inp0[local_pos + 23];
float luma10 = inp0[local_pos + 24];
float luma11 = inp0[local_pos + 25];
float luma2 = inp0[local_pos + 2];
float luma12 = inp0[local_pos + 33];
float luma13 = inp0[local_pos + 34];
float luma14 = inp0[local_pos + 35];
float luma15 = inp0[local_pos + 36];
float luma3 = inp0[local_pos + 3];
vec3 abd = vec3(0.0);
float gx, gy;
gx = (luma4-luma0);
gy = (luma1-luma0);
abd += vec3(gx * gx, gx * gy, gy * gy) * 0.04792235409415088;
gx = (luma5-luma1);
gy = (luma2-luma0)/2.0;
abd += vec3(gx * gx, gx * gy, gy * gy) * 0.06153352068439959;
gx = (luma6-luma2);
gy = (luma3-luma1)/2.0;
abd += vec3(gx * gx, gx * gy, gy * gy) * 0.06153352068439959;
gx = (luma7-luma3);
gy = (luma3-luma2);
abd += vec3(gx * gx, gx * gy, gy * gy) * 0.04792235409415088;
gx = (luma8-luma0)/2.0;
gy = (luma5-luma4);
abd += vec3(gx * gx, gx * gy, gy * gy) * 0.06153352068439959;
gx = (luma9-luma1)/2.0;
gy = (luma6-luma4)/2.0;
abd += vec3(gx * gx, gx * gy, gy * gy) * 0.07901060453704994;
gx = (luma10-luma2)/2.0;
gy = (luma7-luma5)/2.0;
abd += vec3(gx * gx, gx * gy, gy * gy) * 0.07901060453704994;
gx = (luma11-luma3)/2.0;
gy = (luma7-luma6);
abd += vec3(gx * gx, gx * gy, gy * gy) * 0.06153352068439959;
gx = (luma12-luma4)/2.0;
gy = (luma9-luma8);
abd += vec3(gx * gx, gx * gy, gy * gy) * 0.06153352068439959;
gx = (luma13-luma5)/2.0;
gy = (luma10-luma8)/2.0;
abd += vec3(gx * gx, gx * gy, gy * gy) * 0.07901060453704994;
gx = (luma14-luma6)/2.0;
gy = (luma11-luma9)/2.0;
abd += vec3(gx * gx, gx * gy, gy * gy) * 0.07901060453704994;
gx = (luma15-luma7)/2.0;
gy = (luma11-luma10);
abd += vec3(gx * gx, gx * gy, gy * gy) * 0.06153352068439959;
gx = (luma12-luma8);
gy = (luma13-luma12);
abd += vec3(gx * gx, gx * gy, gy * gy) * 0.04792235409415088;
gx = (luma13-luma9);
gy = (luma14-luma12)/2.0;
abd += vec3(gx * gx, gx * gy, gy * gy) * 0.06153352068439959;
gx = (luma14-luma10);
gy = (luma15-luma13)/2.0;
abd += vec3(gx * gx, gx * gy, gy * gy) * 0.06153352068439959;
gx = (luma15-luma11);
gy = (luma15-luma14);
abd += vec3(gx * gx, gx * gy, gy * gy) * 0.04792235409415088;
float a = abd.x, b = abd.y, d = abd.z;
float T = a + d, D = a * d - b * b;
float delta = sqrt(max(T * T / 4.0 - D, 0.0));
float L1 = T / 2.0 + delta, L2 = T / 2.0 - delta;
float sqrtL1 = sqrt(L1), sqrtL2 = sqrt(L2);
float theta = mix(mod(atan(L1 - a, b) + 3.141592653589793, 3.141592653589793), 0.0, abs(b) < 1.192092896e-7);
float lambda = sqrtL1;
float mu = mix((sqrtL1 - sqrtL2) / (sqrtL1 + sqrtL2), 0.0, sqrtL1 + sqrtL2 < 1.192092896e-7);
float angle = floor(theta * 24.0 / 3.141592653589793);
float strength = clamp(floor(log2(lambda * 2000.0 + 1.192092896e-7)), 0.0, 8.0);
float coherence = mix(mix(0.0, 1.0, mu >= 0.25), 2.0, mu >= 0.5);
float coord_y = ((angle * 9.0 + strength) * 3.0 + coherence + 0.5) / 648.0;
float res = 0.0;
vec4 w;
w = texture(ravu_lut2, vec2(0.25, coord_y));
res += (inp0[local_pos + 0] + inp0[local_pos + 36]) * w[0];
res += (inp0[local_pos + 1] + inp0[local_pos + 35]) * w[1];
res += (inp0[local_pos + 2] + inp0[local_pos + 34]) * w[2];
res += (inp0[local_pos + 3] + inp0[local_pos + 33]) * w[3];
w = texture(ravu_lut2, vec2(0.75, coord_y));
res += (inp0[local_pos + 11] + inp0[local_pos + 25]) * w[0];
res += (inp0[local_pos + 12] + inp0[local_pos + 24]) * w[1];
res += (inp0[local_pos + 13] + inp0[local_pos + 23]) * w[2];
res += (inp0[local_pos + 14] + inp0[local_pos + 22]) * w[3];
res = clamp(res, 0.0, 1.0);
imageStore(out_image, ivec2(gl_GlobalInvocationID), vec4(res, 0.0, 0.0, 0.0));
}
}
//!DESC RAVU (step2, luma, r2, compute)
//!HOOK LUMA
//!BIND HOOKED
//!BIND ravu_lut2
//!BIND ravu_int11
//!WIDTH 2 HOOKED.w *
//!HEIGHT 2 HOOKED.h *
//!OFFSET -0.500000 -0.500000
//!WHEN HOOKED.w OUTPUT.w / 0.707106 < HOOKED.h OUTPUT.h / 0.707106 < *
//!COMPUTE 64 16 32 8
shared float inp0[385];
shared float inp1[385];
void hook() {
ivec2 group_base = ivec2(gl_WorkGroupID) * ivec2(gl_WorkGroupSize);
int local_pos = int(gl_LocalInvocationID.x) * 11 + int(gl_LocalInvocationID.y);
for (int id = int(gl_LocalInvocationIndex); id < 385; id += int(gl_WorkGroupSize.x * gl_WorkGroupSize.y)) {
int x = id / 11, y = id % 11;
inp0[id] = ravu_int11_tex(ravu_int11_pt * vec2(float(group_base.x+x)+(-1.5), float(group_base.y+y)+(-1.5))).x;
}
for (int id = int(gl_LocalInvocationIndex); id < 385; id += int(gl_WorkGroupSize.x * gl_WorkGroupSize.y)) {
int x = id / 11, y = id % 11;
inp1[id] = HOOKED_tex(HOOKED_pt * vec2(float(group_base.x+x)+(-0.5), float(group_base.y+y)+(-0.5))).x;
}
groupMemoryBarrier();
barrier();
{
float luma8 = inp0[local_pos + 12];
float luma5 = inp0[local_pos + 13];
float luma2 = inp0[local_pos + 14];
float luma13 = inp0[local_pos + 23];
float luma10 = inp0[local_pos + 24];
float luma7 = inp0[local_pos + 25];
float luma0 = inp0[local_pos + 2];
float luma15 = inp0[local_pos + 35];
float luma12 = inp1[local_pos + 11];
float luma9 = inp1[local_pos + 12];
float luma6 = inp1[local_pos + 13];
float luma3 = inp1[local_pos + 14];
float luma4 = inp1[local_pos + 1];
float luma14 = inp1[local_pos + 23];
float luma11 = inp1[local_pos + 24];
float luma1 = inp1[local_pos + 2];
vec3 abd = vec3(0.0);
float gx, gy;
gx = (luma4-luma0);
gy = (luma1-luma0);
abd += vec3(gx * gx, gx * gy, gy * gy) * 0.04792235409415088;
gx = (luma5-luma1);
gy = (luma2-luma0)/2.0;
abd += vec3(gx * gx, gx * gy, gy * gy) * 0.06153352068439959;
gx = (luma6-luma2);
gy = (luma3-luma1)/2.0;
abd += vec3(gx * gx, gx * gy, gy * gy) * 0.06153352068439959;
gx = (luma7-luma3);
gy = (luma3-luma2);
abd += vec3(gx * gx, gx * gy, gy * gy) * 0.04792235409415088;
gx = (luma8-luma0)/2.0;
gy = (luma5-luma4);
abd += vec3(gx * gx, gx * gy, gy * gy) * 0.06153352068439959;
gx = (luma9-luma1)/2.0;
gy = (luma6-luma4)/2.0;
abd += vec3(gx * gx, gx * gy, gy * gy) * 0.07901060453704994;
gx = (luma10-luma2)/2.0;
gy = (luma7-luma5)/2.0;
abd += vec3(gx * gx, gx * gy, gy * gy) * 0.07901060453704994;
gx = (luma11-luma3)/2.0;
gy = (luma7-luma6);
abd += vec3(gx * gx, gx * gy, gy * gy) * 0.06153352068439959;
gx = (luma12-luma4)/2.0;
gy = (luma9-luma8);
abd += vec3(gx * gx, gx * gy, gy * gy) * 0.06153352068439959;
gx = (luma13-luma5)/2.0;
gy = (luma10-luma8)/2.0;
abd += vec3(gx * gx, gx * gy, gy * gy) * 0.07901060453704994;
gx = (luma14-luma6)/2.0;
gy = (luma11-luma9)/2.0;
abd += vec3(gx * gx, gx * gy, gy * gy) * 0.07901060453704994;
gx = (luma15-luma7)/2.0;
gy = (luma11-luma10);
abd += vec3(gx * gx, gx * gy, gy * gy) * 0.06153352068439959;
gx = (luma12-luma8);
gy = (luma13-luma12);
abd += vec3(gx * gx, gx * gy, gy * gy) * 0.04792235409415088;
gx = (luma13-luma9);
gy = (luma14-luma12)/2.0;
abd += vec3(gx * gx, gx * gy, gy * gy) * 0.06153352068439959;
gx = (luma14-luma10);
gy = (luma15-luma13)/2.0;
abd += vec3(gx * gx, gx * gy, gy * gy) * 0.06153352068439959;
gx = (luma15-luma11);
gy = (luma15-luma14);
abd += vec3(gx * gx, gx * gy, gy * gy) * 0.04792235409415088;
float a = abd.x, b = abd.y, d = abd.z;
float T = a + d, D = a * d - b * b;
float delta = sqrt(max(T * T / 4.0 - D, 0.0));
float L1 = T / 2.0 + delta, L2 = T / 2.0 - delta;
float sqrtL1 = sqrt(L1), sqrtL2 = sqrt(L2);
float theta = mix(mod(atan(L1 - a, b) + 3.141592653589793, 3.141592653589793), 0.0, abs(b) < 1.192092896e-7);
float lambda = sqrtL1;
float mu = mix((sqrtL1 - sqrtL2) / (sqrtL1 + sqrtL2), 0.0, sqrtL1 + sqrtL2 < 1.192092896e-7);
float angle = floor(theta * 24.0 / 3.141592653589793);
float strength = clamp(floor(log2(lambda * 2000.0 + 1.192092896e-7)), 0.0, 8.0);
float coherence = mix(mix(0.0, 1.0, mu >= 0.25), 2.0, mu >= 0.5);
float coord_y = ((angle * 9.0 + strength) * 3.0 + coherence + 0.5) / 648.0;
float res = 0.0;
vec4 w;
w = texture(ravu_lut2, vec2(0.25, coord_y));
res += (inp0[local_pos + 2] + inp0[local_pos + 35]) * w[0];
res += (inp1[local_pos + 2] + inp1[local_pos + 23]) * w[1];
res += (inp0[local_pos + 14] + inp0[local_pos + 23]) * w[2];
res += (inp1[local_pos + 14] + inp1[local_pos + 11]) * w[3];
w = texture(ravu_lut2, vec2(0.75, coord_y));
res += (inp1[local_pos + 1] + inp1[local_pos + 24]) * w[0];
res += (inp0[local_pos + 13] + inp0[local_pos + 24]) * w[1];
res += (inp1[local_pos + 13] + inp1[local_pos + 12]) * w[2];
res += (inp0[local_pos + 25] + inp0[local_pos + 12]) * w[3];
res = clamp(res, 0.0, 1.0);
imageStore(out_image, ivec2(gl_GlobalInvocationID) * 2 + ivec2(0, 1), vec4(res, 0.0, 0.0, 0.0));
}
{
float luma4 = inp0[local_pos + 12];
float luma1 = inp0[local_pos + 13];
float luma12 = inp0[local_pos + 22];
float luma9 = inp0[local_pos + 23];
float luma6 = inp0[local_pos + 24];
float luma3 = inp0[local_pos + 25];
float luma14 = inp0[local_pos + 34];
float luma11 = inp0[local_pos + 35];
float luma8 = inp1[local_pos + 11];
float luma5 = inp1[local_pos + 12];
float luma2 = inp1[local_pos + 13];
float luma0 = inp1[local_pos + 1];
float luma13 = inp1[local_pos + 22];
float luma10 = inp1[local_pos + 23];
float luma7 = inp1[local_pos + 24];
float luma15 = inp1[local_pos + 34];
vec3 abd = vec3(0.0);
float gx, gy;
gx = (luma4-luma0);
gy = (luma1-luma0);
abd += vec3(gx * gx, gx * gy, gy * gy) * 0.04792235409415088;
gx = (luma5-luma1);
gy = (luma2-luma0)/2.0;
abd += vec3(gx * gx, gx * gy, gy * gy) * 0.06153352068439959;
gx = (luma6-luma2);
gy = (luma3-luma1)/2.0;
abd += vec3(gx * gx, gx * gy, gy * gy) * 0.06153352068439959;
gx = (luma7-luma3);
gy = (luma3-luma2);
abd += vec3(gx * gx, gx * gy, gy * gy) * 0.04792235409415088;
gx = (luma8-luma0)/2.0;
gy = (luma5-luma4);
abd += vec3(gx * gx, gx * gy, gy * gy) * 0.06153352068439959;
gx = (luma9-luma1)/2.0;
gy = (luma6-luma4)/2.0;
abd += vec3(gx * gx, gx * gy, gy * gy) * 0.07901060453704994;
gx = (luma10-luma2)/2.0;
gy = (luma7-luma5)/2.0;
abd += vec3(gx * gx, gx * gy, gy * gy) * 0.07901060453704994;
gx = (luma11-luma3)/2.0;
gy = (luma7-luma6);
abd += vec3(gx * gx, gx * gy, gy * gy) * 0.06153352068439959;
gx = (luma12-luma4)/2.0;
gy = (luma9-luma8);
abd += vec3(gx * gx, gx * gy, gy * gy) * 0.06153352068439959;
gx = (luma13-luma5)/2.0;
gy = (luma10-luma8)/2.0;
abd += vec3(gx * gx, gx * gy, gy * gy) * 0.07901060453704994;
gx = (luma14-luma6)/2.0;
gy = (luma11-luma9)/2.0;
abd += vec3(gx * gx, gx * gy, gy * gy) * 0.07901060453704994;
gx = (luma15-luma7)/2.0;
gy = (luma11-luma10);
abd += vec3(gx * gx, gx * gy, gy * gy) * 0.06153352068439959;
gx = (luma12-luma8);
gy = (luma13-luma12);
abd += vec3(gx * gx, gx * gy, gy * gy) * 0.04792235409415088;
gx = (luma13-luma9);
gy = (luma14-luma12)/2.0;
abd += vec3(gx * gx, gx * gy, gy * gy) * 0.06153352068439959;
gx = (luma14-luma10);
gy = (luma15-luma13)/2.0;
abd += vec3(gx * gx, gx * gy, gy * gy) * 0.06153352068439959;
gx = (luma15-luma11);
gy = (luma15-luma14);
abd += vec3(gx * gx, gx * gy, gy * gy) * 0.04792235409415088;
float a = abd.x, b = abd.y, d = abd.z;
float T = a + d, D = a * d - b * b;
float delta = sqrt(max(T * T / 4.0 - D, 0.0));
float L1 = T / 2.0 + delta, L2 = T / 2.0 - delta;
float sqrtL1 = sqrt(L1), sqrtL2 = sqrt(L2);
float theta = mix(mod(atan(L1 - a, b) + 3.141592653589793, 3.141592653589793), 0.0, abs(b) < 1.192092896e-7);
float lambda = sqrtL1;
float mu = mix((sqrtL1 - sqrtL2) / (sqrtL1 + sqrtL2), 0.0, sqrtL1 + sqrtL2 < 1.192092896e-7);
float angle = floor(theta * 24.0 / 3.141592653589793);
float strength = clamp(floor(log2(lambda * 2000.0 + 1.192092896e-7)), 0.0, 8.0);
float coherence = mix(mix(0.0, 1.0, mu >= 0.25), 2.0, mu >= 0.5);
float coord_y = ((angle * 9.0 + strength) * 3.0 + coherence + 0.5) / 648.0;
float res = 0.0;
vec4 w;
w = texture(ravu_lut2, vec2(0.25, coord_y));
res += (inp1[local_pos + 1] + inp1[local_pos + 34]) * w[0];
res += (inp0[local_pos + 13] + inp0[local_pos + 34]) * w[1];
res += (inp1[local_pos + 13] + inp1[local_pos + 22]) * w[2];
res += (inp0[local_pos + 25] + inp0[local_pos + 22]) * w[3];
w = texture(ravu_lut2, vec2(0.75, coord_y));
res += (inp0[local_pos + 12] + inp0[local_pos + 35]) * w[0];
res += (inp1[local_pos + 12] + inp1[local_pos + 23]) * w[1];
res += (inp0[local_pos + 24] + inp0[local_pos + 23]) * w[2];
res += (inp1[local_pos + 24] + inp1[local_pos + 11]) * w[3];
res = clamp(res, 0.0, 1.0);
imageStore(out_image, ivec2(gl_GlobalInvocationID) * 2 + ivec2(1, 0), vec4(res, 0.0, 0.0, 0.0));
}
float res;
res = inp0[local_pos + 24];
imageStore(out_image, ivec2(gl_GlobalInvocationID) * 2 + ivec2(1, 1), vec4(res, 0.0, 0.0, 0.0));
res = inp1[local_pos + 12];
imageStore(out_image, ivec2(gl_GlobalInvocationID) * 2 + ivec2(0, 0), vec4(res, 0.0, 0.0, 0.0));
}
//!TEXTURE ravu_lut2
//!SIZE 2 648
//!FORMAT rgba16hf
//!FILTER NEAREST
271f7222ef21f9207024c232cd32a124751a6627d628a9a01e27c131f330ec2c72a8992cc8ac922d21a56d301335b9233521aba4cfa4bd2164a34d346b3485a31898db9b109bb9873e243a33a63324279ca6b627f326d3a31a233a329e32632c951c64a688a6931c00a7b934d33421a70b190fa6b9a67a01b7a48234bc34fca1aea5751a37937ba61324953304344929640ea8a7cfa737976ca8f734143502a8581ceaa776a8949ba1a8da34193530a5559d86a4dca5c4a48ea46434a7345a24f71d1fa934a94b160faa39355b3530a9582087a842a92fa1cfaa01354d3591a24823aba522a8eea71eac9334fa34f5285a216ca972a9011dc4aa3b357235bca91d24d9a8bea933a492acfe3478356c11ad286fa4e9a755ac36b07d342535f82e8d1946a949a9d59ed7aa4b359e3566a9ab221aa88aa927a816ade9348d353625c02a6c9895a461af16b23834093539322e9e82a87fa855a501aa1f35b33520a8f11932a534a985aad1acab34bb356528112b3323c199e4b063b2af3306356b33851fbdaad2abf89aeda83235d53541a96b94e3a5a4abfaa9b8a85b34c8355228bd273a28dc1c33b13fb077309135ab33a122911b059aae22341f963200345a213a9b3b24662419a2f316872e1534de2fc12a3c20b09772adb62942315231d330fe20b6a491a5d722b9a3373492344fa44f14079f7da0e416b81ec8322c344d28279c52213d2245a0d425c8305032b52faf1c47a651a7f31de1a6a334f33492a7451e27a60ca80a15c9a6573405353ba174a08c9e57a33aa4089825334f34122bde18bca738a87b97a2a8e2343135e4a71022b4a74ea9139d3faaab3462359ea3582162a5a8a807a4f8a93e342335f32504212ea9cfa99c158aaa1b358335d8a8ad2535a80baac0a4ffacbc34a035621fa828eaa5eaa962a8edae5e349335f02ae523b8a9adaaa61d29ab1435b53561a93128d1a7daa91da905af8134db35d629532afca142a8afac21b10134ad35f02f76227fa944ab809c9bab0b35ff35fda8432859a53ba87dac1ab012340936982ddc288720471e68af96b1973267353432992087a857abd0a3a9aaa4344636f8a7a9259a9c3fa5e3ada4af7f326636212f5f25fc26422850b076b14c315e352233872069a72faee5198fa74334a836fca8829bea2380a9b9ac16accf2f9c364730882092217b2c49b0fbae5629a43525346b231a0fbba18226812225320d349e221f1d0c24d020e29b9e20ed2e8d33b52f342163a223946824b023892da331ac32992095a43ba61524f6a31e34b93400a55f1aa7a141a2321f649955328134ac28b00ef794b8918b1bff0e90309132ca309c1c36a60ca80220a9a68c34163524a89a2086a6dfa8411d07a8313456357fa308996da18fa51aa124a5da32b134572ba11ad4a783a8bf9184a8cc344f3533a80724c8a73aaa2695abaa7f34b13524a5cc2387a5d2a9c3a024ab0f34963531236b212ca949aafc1b42aa0035a6354ea93826f9a702abb7a2d2ac7a34f835b5990e2896a53dabb2a4edad21340936b025482490a9a1ab3f20f9aaee34ed35dea95827cda526abe6a7e4ad0a345d36ca25072867a09fa9f6a904af34334736a72b372435a932aca6947aabd03447369ca90e26169c56aa18abdbad5e32f036a4298923bb21829e81ad62ae0a317536ba2ec72291a776acdaa130aa3c34b636e6a861226f235fa815adc0ac24309537612bc91b2025ea248aaebeadca2ec8369f2fa0208da33eaf071edfa575334737aaaa36a0472629a5c3add2a5d1272838412c5e9d8c22f92a47af24aaae9ba03710300d211f9c95a0a3260a22f7317c34429570158d24971ee2a59692402b1b35d82f2a92151f749d29284ba5112f7234572e182061a419a70225c4a30534e03405a6901eaca315a51224d69e1732f734ca24322080a265a6452762a28c3152347d2c941b13a687a850212ea67634373592a8d62081a6bea9e32109a80b34a73574a6ea135ca356a7241bbaa6b93222355d289e19c5a7cda8901232a8b3346c358ea8b1238da711ab2a1d01aa5334fa3522a80124a5a5d1aa4910b9aad133f535f89c002101a9baaacd1eada9dd34cc35f9a9e52430a7fdabe9987eab38344f368ea668261fa537acea9b91acd4336d3642a11d243fa918ac8a2173aac5341f36adaaa7246ea380ac51a2e6ab4f33db3622a4f62414990fac93a598ac8532e336141e842401a972ac001706aba4347a367baa94218e20d1ac11a789aa5331ac3715a2961d2a253caa01aa3fab35307e37e826872377a6c1ac29a18fa9df330a3716aa161a902635ac3caa94a8a42e3038a79dbd951a2751a8c5ab9fa9252dfb3737283d204b9f30afda1b58a5ab32b83701ac989f5b2768a98aac139c81278038341c119dcb24e88923ad69a4281b5c3885283b238885e2a04625ad1e29328434929e9224be1f09a4d99ba29c072dae352d2ce222c725269b2cb4a62cdb2e43380624551f0ea40ca8312693a3da33073559a74a1dd6a2cda6ab2621a1fa316535559df822aba523a4e424199f21315f3533245c19aba515a91323a0a55e34583511a9322038a68eaa112505a7c433f135fea8d11aaba422a822258da67a328835d51c8f145aa730a9621a7aa799348a3508a9a521e5a6daab6c2294a81e34403619aa6a2323a695ab0a236ba99a333d36b6a76b1e77a844abf820d8a8b334f535b4aaa221fca568acf11fd8a8e2339f3641aab124d3a4c0ac59212aaa7b33be3679a92323d9a850ac1023c6a99e344a368aab4f204aa118ad8d1c7ba8d0322d3751aa1a22a29717adcb1458a9443244371ca99124f8a883ac0f1b80aa8134a2365aab301b3b1f91ad699c70a64b31df3799a