#include <Eigen\Dense>
#include <Eigen\StdVector>
#include <time.h>
#include <iostream>
using Vec3f = Eigen::Vector3f;
class RandomLCG
{
private:
unsigned int m_seed;
public:
RandomLCG(unsigned seed = 0) :
m_seed(seed)
{}
float operator()()
{
m_seed = 214013u * m_seed + 2531011u;
return m_seed * (1.0f / 4294967296.0f);
}
};
class Ray
{
public:
Vec3f origin;
Vec3f direction;
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
Ray(Vec3f const & ori, Vec3f const & dir) :
origin(ori), direction(dir.normalized()) {}
};
class Material
{
public:
enum class Type { DIFFUSE, SPECULAR, REFRACTION };
Type type;
float fresnel_ratio;
};
class Sphere
{
public:
Vec3f m_center;
float m_radius;
Vec3f m_color;
Vec3f m_emission;
Material m_mateiral;
float m_sqr_radius;
float m_max_color_component;
Vec3f m_color_ratio;
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
public:
Sphere(Vec3f && center, float rad,
Vec3f && emission, Vec3f && color, Material && material) :
m_center(center), m_color(color), m_emission(emission), m_radius(rad), m_mateiral(material)
{
m_sqr_radius = m_radius * m_radius;
m_max_color_component = m_color.maxCoeff();
m_color_ratio = (1.0f / m_max_color_component) * m_color;
}
bool intersect(Ray const & ray, float & root, float threshold = 1e-20) const
{
root = 0.0f;
// Solve t^2*d.d + 2*t*(o-p).d + (o-p).(o-p)-R^2 = 0
Vec3f op = m_center - ray.origin;
float b = op.transpose() * ray.direction;
float det = b * b - op.squaredNorm() + m_sqr_radius;
float const eps = 1e-4;
if (det < 0) return false;
float dets = (std::sqrt)(det);
if (b - dets > eps)
{
root = b - dets;
return true;
}
else if (b + dets > eps)
{
root = b + dets;
return true;
}
return false;
}
};
class Intersection
{
public:
enum class Type { GO_INWARD, GO_OUTWARD };
float root;
Vec3f position;
Vec3f normal;
Vec3f normal_oriented;
Type type;
Vec3f color;
Material material;
Sphere const * obj;
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
};
class Scene
{
private:
std::vector<Sphere, Eigen::aligned_allocator<Sphere> > m_components;
public:
void add(Sphere const & sphere)
{
m_components.push_back(std::move(sphere));
}
bool const & intersect(Ray const & ray, Intersection & intersection) const
{
bool is_intersect = false;
float root = (std::numeric_limits<float>::max)();
Sphere const * pivot = nullptr;
for (Sphere const & com : m_components)
{
float cur_root = 0.0f;
if (!com.intersect(ray, cur_root)) continue;
if (root <= cur_root) continue;
pivot = &com;
root = cur_root;
is_intersect = true;
}
if (is_intersect)
{
//std::cout << "intersected" << std::endl;
intersection.root = root;
intersection.position = ray.origin + root * ray.direction;
intersection.normal = (intersection.position - pivot->m_center).normalized();
intersection.color = pivot->m_color;
intersection.type = (intersection.normal.transpose() * ray.direction > 0) ?
Intersection::Type::GO_INWARD : Intersection::Type::GO_OUTWARD;
intersection.normal_oriented = intersection.normal * (intersection.type == Intersection::Type::GO_INWARD ? 1.0f : -1.0f);
intersection.obj = pivot;
intersection.material = pivot->m_mateiral;
}
//std::cout << "return value " << is_intersect << std::endl;
return is_intersect;
}
};
Vec3f radiance(Scene const & scene, Ray const & ray, int const depth,
int const min_depth, int const max_depth, RandomLCG & rand)
{
//std::cout << "depth " << depth << std::endl;
Intersection inte;
bool is_intersect = scene.intersect(ray, inte);
//std::cout << "intersect test " << is_intersect << std::endl;
if (!is_intersect)
return Vec3f::Zero();
Sphere const * sphere = inte.obj;
int const new_depth = depth + 1;
bool reach_max_depth = (new_depth >= max_depth);
bool reach_min_depth = (new_depth >= min_depth);
/* use Russian roulette for path termination */
bool isRR = rand() < sphere->m_max_color_component;
if (reach_max_depth || (reach_min_depth && !isRR))
return sphere->m_color;
/* WARNING: remain consideration */
Vec3f emission = inte.obj->m_emission;
switch (inte.material.type)
{
case Material::Type::DIFFUSE:
{
//std::cout << "DIFFUSE" << std::endl;
/* local coordinate */
Vec3f w = inte.normal_oriented;
Vec3f wo = w.x() < -0.1f || w.x() > 0.1f ? Vec3f(0.0f, 1.0f, 0.0f) : Vec3f(1.0f, 0.0f, 0.0f);
Vec3f u = (wo.cross(w)).normalized();
Vec3f v = w.cross(u);
/* semi-sphere sampling */
float r = 2.0f * M_PI * rand();
float d = rand();
float dsqt = std::sqrt(d);
Vec3f diffuse_dir = (u * std::cos(r) * dsqt + v * std::sin(r) * dsqt + w * std::sqrt(1.0f - d)).normalized();
Vec3f diffuse = inte.color.cwiseProduct(radiance(scene,
Ray{ inte.position, diffuse_dir }, new_depth, min_depth, max_depth, rand));
return emission + diffuse;
}
break;
case Material::Type::SPECULAR:
{
//std::cout << "SPECULAR" << std::endl;
Vec3f reflect_dir = ray.direction - inte.normal * (2.0f * inte.normal.transpose() * ray.direction);
Vec3f spectular = inte.color.cwiseProduct(radiance(scene,
Ray{ inte.position, reflect_dir }, new_depth, min_depth, max_depth, rand));
return emission + spectular;
}
break;
case Material::Type::REFRACTION:
{
//std::cout << "REFRACTION" << std::endl;
Vec3f reflect_dir = ray.direction - inte.normal * (2.0f * inte.normal.transpose() * ray.direction);
bool go_inward = (inte.type == Intersection::Type::GO_INWARD);
float refr_ratio = (go_inward ? inte.material.fresnel_ratio : 1.0f / inte.material.fresnel_ratio);
float ddn = ray.direction.transpose() * inte.normal_oriented;
float cos2t = 1.0f - refr_ratio * refr_ratio * (1.0f - ddn * ddn);
/* total internal reflection */
if (cos2t < 0.0f)
{
Vec3f reflection = inte.color.cwiseProduct(radiance(scene,
Ray{ inte.position, reflect_dir }, new_depth, min_depth, max_depth, rand));
return emission + reflection;
}
/* compute refraction direction */
Vec3f refraction_dir =
(ray.direction * refr_ratio - inte.normal * (go_inward ? 1.0f : -1.0f) * (ddn * refr_ratio + std::sqrt(cos2t))).normalized();
float R0 = std::abs(refr_ratio - 1.0f) / (refr_ratio + 1.0f);
float c = 1 - (go_inward ? -ddn : refraction_dir.transpose() * inte.normal);
float fresnel_reflectance = R0 + (1.0f - R0) * c * c * c * c;
float P = 0.25f + 0.5f * fresnel_reflectance;
Vec3f res;
if (new_depth > 2)
{
if (rand() < P)
res = (fresnel_reflectance / P) *
radiance(scene, Ray{ inte.position, reflect_dir }, new_depth, min_depth, max_depth, rand);
else
res = ((1.0f - fresnel_reflectance) / (1.0f - P)) *
radiance(scene, Ray{ inte.position, refraction_dir }, new_depth, min_depth, max_depth, rand);
}
else
res =
fresnel_reflectance * radiance(scene, Ray{ inte.position, reflect_dir }, new_depth, min_depth, max_depth, rand) +
(1.0f - fresnel_reflectance) * radiance(scene, Ray{ inte.position, refraction_dir }, new_depth, min_depth, max_depth, rand);
return emission + inte.color.cwiseProduct(res);
}
break;
default:
std::cout << "ERROR : reach invalid code section" << std::endl;
return Vec3f::Zero();
break;
}
}
inline float clamp(float x) {
if (x < 0.0f)
return 0.0f;
else if (x > 1.0f)
return 1.0f;
else
return x;
}
inline int toInt(float x) {
return int(pow(clamp(x), 1 / 2.2) * 255 + .5);
}
int main(int argc, char *argv[]) {
clock_t start = clock();
Scene scene;
Sphere spheres[] = {
Sphere(Vec3f(1e5f + 1.0f, 40.8f, 81.6f), 1e5f, Vec3f::Zero(), Vec3f(.75f, .25f, .25f), { Material::Type::DIFFUSE, 0.0f }),//Left
Sphere(Vec3f(-1e5f + 99.0f, 40.8f, 81.6f), 1e5f, Vec3f::Zero(), Vec3f(.25f, .25f, .75f), { Material::Type::DIFFUSE, 0.0f }),//Rght
Sphere(Vec3f(50.0f, 40.8f, 1e5f), 1e5f, Vec3f::Zero(), Vec3f(.75f, .75f, .75f), { Material::Type::DIFFUSE, 0.0f }),//Back
Sphere(Vec3f(50.0f, 40.8f, -1e5f + 170.0f), 1e5f, Vec3f::Zero(), Vec3f::Zero(), { Material::Type::DIFFUSE, 0.0f }),//Frnt
Sphere(Vec3f(50.0f, 1e5f, 81.6f), 1e5f, Vec3f::Zero(), Vec3f(.75f, .75f, .75f), { Material::Type::DIFFUSE, 0.0f }),//Botm
Sphere(Vec3f(50.0f, -1e5f + 81.6f, 81.6f), 1e5f, Vec3f::Zero(), Vec3f(.75f, .75f, .75f), { Material::Type::DIFFUSE, 0.0f }),//Top
Sphere(Vec3f(27.0f, 16.5f, 47.0f), 16.5f, Vec3f::Zero(), Vec3f(1.0f, 1.0f, 1.0f) * .999f, { Material::Type::SPECULAR, 0.0f }),//Mirr
Sphere(Vec3f(73.0f, 16.5f, 78.0f), 16.5f, Vec3f::Zero(), Vec3f(1.0f, 1.0f, 1.0f) * .999f, { Material::Type::REFRACTION, 2.6f }),//Glas
Sphere(Vec3f(50.0f, 681.6f - 0.27f, 81.6f), 600.0f, Vec3f(12.0f, 12.0f, 12.0f), Vec3f::Zero(), { Material::Type::DIFFUSE, 0.0f }) //Lite
};
for (auto sph : spheres)
scene.add(sph);
const int w = 256;
const int h = 256;
const int samps = argc == 2 ? atoi(argv[1]) / 4 : 100; // # samples
const Ray cam(Vec3f(50.0f, 52.0f, 295.6f), Vec3f(0.0f, -0.042612f, -1.0f)); // cam pos, dir
const Vec3f cx(w * .5135f / h, 0.0f, 0.0f);
const Vec3f cy = cx.cross(cam.direction).normalized() * .5135f;
Vec3f * buffer = new Vec3f[w * h];
//#pragma omp parallel for schedule(dynamic, 1) // OpenMP
// Loop over image rows
for (int y = 0; y < h; y++)
{
fprintf(stderr, "\rRendering (%d spp) %5.2f%%", samps * 4, 100.*y / (h - 1));
RandomLCG rand(y);
// Loop cols
for (int x = 0; x < w; x++)
{
int const i = (h - y - 1) * w + x;
//int const i = y * w + x;
buffer[i] = Vec3f::Zero();
// 2x2 subpixel
for (int sy = 0; sy < 2; sy++) for (int sx = 0; sx < 2; sx++)
{
Vec3f r = Vec3f::Zero();
for (int s = 0; s < samps; s++)
{
float r1 = 2.0f * rand();
float r2 = 2.0f * rand();
float dx = r1 < 1.0f ? sqrt(r1) - 1.0f : 1.0f - sqrt(2.0f - r1);
float dy = r2 < 1.0f ? sqrt(r2) - 1.0f : 1.0f - sqrt(2.0f - r2);
Vec3f d = cx * (((sx + 0.5f + dx) / 2.0f + x) / w - 0.5f) +
cy * (((sy + 0.5f + dy) / 2.0f + y) / h - 0.5f) + cam.direction;
r = r + radiance(scene, Ray(cam.origin + d * 140.0f, d), 0, 5, 30, rand) * (1.0f / samps);
}
buffer[i] = buffer[i] + Vec3f(clamp(r.x()), clamp(r.y()), clamp(r.z()));
}
buffer[i] = buffer[i] * 0.25f;
if (x % 16 == 0 && y % 16 == 0)
std::cout << buffer[i] << std::endl;
}
}
printf("\n%f sec\n", (float)(clock() - start) / CLOCKS_PER_SEC);
FILE *f = fopen("myimage4.ppm", "w"); // Write image to PPM file.
fprintf(f, "P3\n%d %d\n%d\n", w, h, 255);
for (int i = 0; i < w * h; i++)
fprintf(f, "%d %d %d\n", toInt(buffer[i][0]), toInt(buffer[i][1]), toInt(buffer[i][2]));
fclose(f);
return 0;
}
I2luY2x1ZGUgPEVpZ2VuXERlbnNlPgojaW5jbHVkZSA8RWlnZW5cU3RkVmVjdG9yPgoKI2luY2x1ZGUgPHRpbWUuaD4JCQojaW5jbHVkZSA8aW9zdHJlYW0+Cgp1c2luZyBWZWMzZiA9IEVpZ2VuOjpWZWN0b3IzZjsKCmNsYXNzIFJhbmRvbUxDRwp7CnByaXZhdGU6Cgl1bnNpZ25lZCBpbnQgbV9zZWVkOwoKcHVibGljOgoJUmFuZG9tTENHKHVuc2lnbmVkIHNlZWQgPSAwKSA6CgkJbV9zZWVkKHNlZWQpCgl7fQoKCWZsb2F0IG9wZXJhdG9yKCkoKQoJewoJCW1fc2VlZCA9IDIxNDAxM3UgKiBtX3NlZWQgKyAyNTMxMDExdTsKCQlyZXR1cm4gbV9zZWVkICogKDEuMGYgLyA0Mjk0OTY3Mjk2LjBmKTsKCX0KfTsKCmNsYXNzIFJheQp7CnB1YmxpYzoKCVZlYzNmIG9yaWdpbjsKCVZlYzNmIGRpcmVjdGlvbjsKCglFSUdFTl9NQUtFX0FMSUdORURfT1BFUkFUT1JfTkVXCgoJCVJheShWZWMzZiBjb25zdCAmIG9yaSwgVmVjM2YgY29uc3QgJiBkaXIpIDoKCQlvcmlnaW4ob3JpKSwgZGlyZWN0aW9uKGRpci5ub3JtYWxpemVkKCkpIHt9Cn07CgpjbGFzcyBNYXRlcmlhbAp7CnB1YmxpYzoKCWVudW0gY2xhc3MgVHlwZSB7IERJRkZVU0UsIFNQRUNVTEFSLCBSRUZSQUNUSU9OIH07CgoJVHlwZSB0eXBlOwoJZmxvYXQgZnJlc25lbF9yYXRpbzsKfTsKCmNsYXNzIFNwaGVyZQp7CnB1YmxpYzoKCVZlYzNmIG1fY2VudGVyOwoJZmxvYXQgbV9yYWRpdXM7CgoJVmVjM2YgbV9jb2xvcjsKCVZlYzNmIG1fZW1pc3Npb247CglNYXRlcmlhbCBtX21hdGVpcmFsOwoKCWZsb2F0IG1fc3FyX3JhZGl1czsKCWZsb2F0IG1fbWF4X2NvbG9yX2NvbXBvbmVudDsKCVZlYzNmIG1fY29sb3JfcmF0aW87CgoJRUlHRU5fTUFLRV9BTElHTkVEX09QRVJBVE9SX05FVwoKcHVibGljOgoJU3BoZXJlKFZlYzNmICYmIGNlbnRlciwgZmxvYXQgcmFkLAoJCVZlYzNmICYmIGVtaXNzaW9uLCBWZWMzZiAmJiBjb2xvciwgTWF0ZXJpYWwgJiYgbWF0ZXJpYWwpIDoKCQltX2NlbnRlcihjZW50ZXIpLCBtX2NvbG9yKGNvbG9yKSwgbV9lbWlzc2lvbihlbWlzc2lvbiksIG1fcmFkaXVzKHJhZCksIG1fbWF0ZWlyYWwobWF0ZXJpYWwpCgl7CgkJbV9zcXJfcmFkaXVzID0gbV9yYWRpdXMgKiBtX3JhZGl1czsKCQltX21heF9jb2xvcl9jb21wb25lbnQgPSBtX2NvbG9yLm1heENvZWZmKCk7CgkJbV9jb2xvcl9yYXRpbyA9ICgxLjBmIC8gbV9tYXhfY29sb3JfY29tcG9uZW50KSAqIG1fY29sb3I7Cgl9CgoJYm9vbCBpbnRlcnNlY3QoUmF5IGNvbnN0ICYgcmF5LCBmbG9hdCAmIHJvb3QsIGZsb2F0IHRocmVzaG9sZCA9IDFlLTIwKSBjb25zdAoJewoJCXJvb3QgPSAwLjBmOwoJCS8vIFNvbHZlIHReMipkLmQgKyAyKnQqKG8tcCkuZCArIChvLXApLihvLXApLVJeMiA9IDAKCQlWZWMzZiBvcCA9IG1fY2VudGVyIC0gcmF5Lm9yaWdpbjsKCQlmbG9hdCBiID0gb3AudHJhbnNwb3NlKCkgKiByYXkuZGlyZWN0aW9uOwoJCWZsb2F0IGRldCA9IGIgKiBiIC0gb3Auc3F1YXJlZE5vcm0oKSArIG1fc3FyX3JhZGl1czsKCQlmbG9hdCBjb25zdCBlcHMgPSAxZS00OwoKCQlpZiAoZGV0IDwgMCkgcmV0dXJuIGZhbHNlOwoKCQlmbG9hdCBkZXRzID0gKHN0ZDo6c3FydCkoZGV0KTsKCgkJaWYgKGIgLSBkZXRzID4gZXBzKQoJCXsKCQkJcm9vdCA9IGIgLSBkZXRzOwoJCQlyZXR1cm4gdHJ1ZTsKCQl9CgkJZWxzZSBpZiAoYiArIGRldHMgPiBlcHMpCgkJewoJCQlyb290ID0gYiArIGRldHM7CgkJCXJldHVybiB0cnVlOwoJCX0KCgkJcmV0dXJuIGZhbHNlOwoJfQp9OwoKY2xhc3MgSW50ZXJzZWN0aW9uCnsKcHVibGljOgoJZW51bSBjbGFzcyBUeXBlIHsgR09fSU5XQVJELCBHT19PVVRXQVJEIH07CgoJZmxvYXQgcm9vdDsKCVZlYzNmIHBvc2l0aW9uOwoJVmVjM2Ygbm9ybWFsOwoJVmVjM2Ygbm9ybWFsX29yaWVudGVkOwoJVHlwZSB0eXBlOwoJVmVjM2YgY29sb3I7CglNYXRlcmlhbCBtYXRlcmlhbDsKCVNwaGVyZSBjb25zdCAqIG9iajsKCglFSUdFTl9NQUtFX0FMSUdORURfT1BFUkFUT1JfTkVXCn07CgpjbGFzcyBTY2VuZQp7CnByaXZhdGU6CglzdGQ6OnZlY3RvcjxTcGhlcmUsIEVpZ2VuOjphbGlnbmVkX2FsbG9jYXRvcjxTcGhlcmU+ID4gbV9jb21wb25lbnRzOwoKcHVibGljOgoJdm9pZCBhZGQoU3BoZXJlIGNvbnN0ICYgc3BoZXJlKQoJewoJCW1fY29tcG9uZW50cy5wdXNoX2JhY2soc3RkOjptb3ZlKHNwaGVyZSkpOwoJfQoKCWJvb2wgY29uc3QgJiBpbnRlcnNlY3QoUmF5IGNvbnN0ICYgcmF5LCBJbnRlcnNlY3Rpb24gJiBpbnRlcnNlY3Rpb24pIGNvbnN0Cgl7CgkJYm9vbCBpc19pbnRlcnNlY3QgPSBmYWxzZTsKCQlmbG9hdCByb290ID0gKHN0ZDo6bnVtZXJpY19saW1pdHM8ZmxvYXQ+OjptYXgpKCk7CgkJU3BoZXJlIGNvbnN0ICogcGl2b3QgPSBudWxscHRyOwoKCQlmb3IgKFNwaGVyZSBjb25zdCAmIGNvbSA6IG1fY29tcG9uZW50cykKCQl7CgkJCWZsb2F0IGN1cl9yb290ID0gMC4wZjsKCQkJaWYgKCFjb20uaW50ZXJzZWN0KHJheSwgY3VyX3Jvb3QpKSBjb250aW51ZTsKCQkJaWYgKHJvb3QgPD0gY3VyX3Jvb3QpIGNvbnRpbnVlOwoKCQkJcGl2b3QgPSAmY29tOwoJCQlyb290ID0gY3VyX3Jvb3Q7CgkJCWlzX2ludGVyc2VjdCA9IHRydWU7CgkJfQoKCQlpZiAoaXNfaW50ZXJzZWN0KQoJCXsKCQkJLy9zdGQ6OmNvdXQgPDwgImludGVyc2VjdGVkIiA8PCBzdGQ6OmVuZGw7CgkJCWludGVyc2VjdGlvbi5yb290ID0gcm9vdDsKCQkJaW50ZXJzZWN0aW9uLnBvc2l0aW9uID0gcmF5Lm9yaWdpbiArIHJvb3QgKiByYXkuZGlyZWN0aW9uOwoJCQlpbnRlcnNlY3Rpb24ubm9ybWFsID0gKGludGVyc2VjdGlvbi5wb3NpdGlvbiAtIHBpdm90LT5tX2NlbnRlcikubm9ybWFsaXplZCgpOwoJCQlpbnRlcnNlY3Rpb24uY29sb3IgPSBwaXZvdC0+bV9jb2xvcjsKCQkJaW50ZXJzZWN0aW9uLnR5cGUgPSAoaW50ZXJzZWN0aW9uLm5vcm1hbC50cmFuc3Bvc2UoKSAqIHJheS5kaXJlY3Rpb24gPiAwKSA/CgkJCQlJbnRlcnNlY3Rpb246OlR5cGU6OkdPX0lOV0FSRCA6IEludGVyc2VjdGlvbjo6VHlwZTo6R09fT1VUV0FSRDsKCQkJaW50ZXJzZWN0aW9uLm5vcm1hbF9vcmllbnRlZCA9IGludGVyc2VjdGlvbi5ub3JtYWwgKiAoaW50ZXJzZWN0aW9uLnR5cGUgPT0gSW50ZXJzZWN0aW9uOjpUeXBlOjpHT19JTldBUkQgPyAxLjBmIDogLTEuMGYpOwoJCQlpbnRlcnNlY3Rpb24ub2JqID0gcGl2b3Q7CgkJCWludGVyc2VjdGlvbi5tYXRlcmlhbCA9IHBpdm90LT5tX21hdGVpcmFsOwoKCQl9CgoJCS8vc3RkOjpjb3V0IDw8ICJyZXR1cm4gdmFsdWUgIiA8PCBpc19pbnRlcnNlY3QgPDwgc3RkOjplbmRsOwoJCXJldHVybiBpc19pbnRlcnNlY3Q7Cgl9Cn07CgpWZWMzZiByYWRpYW5jZShTY2VuZSBjb25zdCAmIHNjZW5lLCBSYXkgY29uc3QgJiByYXksIGludCBjb25zdCBkZXB0aCwKCWludCBjb25zdCBtaW5fZGVwdGgsIGludCBjb25zdCBtYXhfZGVwdGgsIFJhbmRvbUxDRyAmIHJhbmQpCnsKCS8vc3RkOjpjb3V0IDw8ICJkZXB0aCAiIDw8IGRlcHRoIDw8IHN0ZDo6ZW5kbDsKCUludGVyc2VjdGlvbiBpbnRlOwoJYm9vbCBpc19pbnRlcnNlY3QgPSBzY2VuZS5pbnRlcnNlY3QocmF5LCBpbnRlKTsKCS8vc3RkOjpjb3V0IDw8ICJpbnRlcnNlY3QgdGVzdCAiIDw8IGlzX2ludGVyc2VjdCA8PCBzdGQ6OmVuZGw7CgoJaWYgKCFpc19pbnRlcnNlY3QpCgkJcmV0dXJuIFZlYzNmOjpaZXJvKCk7CgoJU3BoZXJlIGNvbnN0ICogc3BoZXJlID0gaW50ZS5vYmo7CgoJaW50IGNvbnN0IG5ld19kZXB0aCA9IGRlcHRoICsgMTsKCWJvb2wgcmVhY2hfbWF4X2RlcHRoID0gKG5ld19kZXB0aCA+PSBtYXhfZGVwdGgpOwoJYm9vbCByZWFjaF9taW5fZGVwdGggPSAobmV3X2RlcHRoID49IG1pbl9kZXB0aCk7CgoJLyogdXNlIFJ1c3NpYW4gcm91bGV0dGUgZm9yIHBhdGggdGVybWluYXRpb24gKi8KCWJvb2wgaXNSUiA9IHJhbmQoKSA8IHNwaGVyZS0+bV9tYXhfY29sb3JfY29tcG9uZW50OwoKCWlmIChyZWFjaF9tYXhfZGVwdGggfHwgKHJlYWNoX21pbl9kZXB0aCAmJiAhaXNSUikpCgkJcmV0dXJuIHNwaGVyZS0+bV9jb2xvcjsKCgkvKiBXQVJOSU5HOiByZW1haW4gY29uc2lkZXJhdGlvbiAqLwoKCVZlYzNmIGVtaXNzaW9uID0gaW50ZS5vYmotPm1fZW1pc3Npb247CgoJc3dpdGNoIChpbnRlLm1hdGVyaWFsLnR5cGUpCgl7CgljYXNlIE1hdGVyaWFsOjpUeXBlOjpESUZGVVNFOgoJewoJCS8vc3RkOjpjb3V0IDw8ICJESUZGVVNFIiA8PCBzdGQ6OmVuZGw7CgkJLyogbG9jYWwgY29vcmRpbmF0ZSAqLwoJCVZlYzNmIHcgPSBpbnRlLm5vcm1hbF9vcmllbnRlZDsKCQlWZWMzZiB3byA9IHcueCgpIDwgLTAuMWYgfHwgdy54KCkgPiAwLjFmID8gVmVjM2YoMC4wZiwgMS4wZiwgMC4wZikgOiBWZWMzZigxLjBmLCAwLjBmLCAwLjBmKTsKCQlWZWMzZiB1ID0gKHdvLmNyb3NzKHcpKS5ub3JtYWxpemVkKCk7CgkJVmVjM2YgdiA9IHcuY3Jvc3ModSk7CgoJCS8qIHNlbWktc3BoZXJlIHNhbXBsaW5nICovCgkJZmxvYXQgciA9IDIuMGYgKiBNX1BJICogcmFuZCgpOwoJCWZsb2F0IGQgPSByYW5kKCk7CgkJZmxvYXQgZHNxdCA9IHN0ZDo6c3FydChkKTsKCgkJVmVjM2YgZGlmZnVzZV9kaXIgPSAodSAqIHN0ZDo6Y29zKHIpICogZHNxdCArIHYgKiBzdGQ6OnNpbihyKSAqIGRzcXQgKyB3ICogc3RkOjpzcXJ0KDEuMGYgLSBkKSkubm9ybWFsaXplZCgpOwoJCVZlYzNmIGRpZmZ1c2UgPSBpbnRlLmNvbG9yLmN3aXNlUHJvZHVjdChyYWRpYW5jZShzY2VuZSwKCQkJUmF5eyBpbnRlLnBvc2l0aW9uLCBkaWZmdXNlX2RpciB9LCBuZXdfZGVwdGgsIG1pbl9kZXB0aCwgbWF4X2RlcHRoLCByYW5kKSk7CgoJCXJldHVybiBlbWlzc2lvbiArIGRpZmZ1c2U7Cgl9CglicmVhazsKCWNhc2UgTWF0ZXJpYWw6OlR5cGU6OlNQRUNVTEFSOgoJewoJCS8vc3RkOjpjb3V0IDw8ICJTUEVDVUxBUiIgPDwgc3RkOjplbmRsOwoJCVZlYzNmIHJlZmxlY3RfZGlyID0gcmF5LmRpcmVjdGlvbiAtIGludGUubm9ybWFsICogKDIuMGYgKiBpbnRlLm5vcm1hbC50cmFuc3Bvc2UoKSAqIHJheS5kaXJlY3Rpb24pOwoJCVZlYzNmIHNwZWN0dWxhciA9IGludGUuY29sb3IuY3dpc2VQcm9kdWN0KHJhZGlhbmNlKHNjZW5lLAoJCQlSYXl7IGludGUucG9zaXRpb24sIHJlZmxlY3RfZGlyIH0sIG5ld19kZXB0aCwgbWluX2RlcHRoLCBtYXhfZGVwdGgsIHJhbmQpKTsKCQlyZXR1cm4gZW1pc3Npb24gKyBzcGVjdHVsYXI7Cgl9CglicmVhazsKCWNhc2UgTWF0ZXJpYWw6OlR5cGU6OlJFRlJBQ1RJT046Cgl7CgkJLy9zdGQ6OmNvdXQgPDwgIlJFRlJBQ1RJT04iIDw8IHN0ZDo6ZW5kbDsKCQlWZWMzZiByZWZsZWN0X2RpciA9IHJheS5kaXJlY3Rpb24gLSBpbnRlLm5vcm1hbCAqICgyLjBmICogaW50ZS5ub3JtYWwudHJhbnNwb3NlKCkgKiByYXkuZGlyZWN0aW9uKTsKCgkJYm9vbCBnb19pbndhcmQgPSAoaW50ZS50eXBlID09IEludGVyc2VjdGlvbjo6VHlwZTo6R09fSU5XQVJEKTsKCQlmbG9hdCByZWZyX3JhdGlvID0gKGdvX2lud2FyZCA/IGludGUubWF0ZXJpYWwuZnJlc25lbF9yYXRpbyA6IDEuMGYgLyBpbnRlLm1hdGVyaWFsLmZyZXNuZWxfcmF0aW8pOwoKCQlmbG9hdCBkZG4gPSByYXkuZGlyZWN0aW9uLnRyYW5zcG9zZSgpICogaW50ZS5ub3JtYWxfb3JpZW50ZWQ7CgkJZmxvYXQgY29zMnQgPSAxLjBmIC0gcmVmcl9yYXRpbyAqIHJlZnJfcmF0aW8gKiAoMS4wZiAtIGRkbiAqIGRkbik7CgoJCS8qIHRvdGFsIGludGVybmFsIHJlZmxlY3Rpb24gKi8KCQlpZiAoY29zMnQgPCAwLjBmKQoJCXsKCQkJVmVjM2YgcmVmbGVjdGlvbiA9IGludGUuY29sb3IuY3dpc2VQcm9kdWN0KHJhZGlhbmNlKHNjZW5lLAoJCQkJUmF5eyBpbnRlLnBvc2l0aW9uLCByZWZsZWN0X2RpciB9LCBuZXdfZGVwdGgsIG1pbl9kZXB0aCwgbWF4X2RlcHRoLCByYW5kKSk7CgkJCXJldHVybiBlbWlzc2lvbiArIHJlZmxlY3Rpb247CgkJfQoKCQkvKiBjb21wdXRlIHJlZnJhY3Rpb24gZGlyZWN0aW9uICovCgkJVmVjM2YgcmVmcmFjdGlvbl9kaXIgPQoJCQkocmF5LmRpcmVjdGlvbiAqIHJlZnJfcmF0aW8gLSBpbnRlLm5vcm1hbCAqIChnb19pbndhcmQgPyAxLjBmIDogLTEuMGYpICogKGRkbiAqIHJlZnJfcmF0aW8gKyBzdGQ6OnNxcnQoY29zMnQpKSkubm9ybWFsaXplZCgpOwoKCQlmbG9hdCBSMCA9IHN0ZDo6YWJzKHJlZnJfcmF0aW8gLSAxLjBmKSAvIChyZWZyX3JhdGlvICsgMS4wZik7CgkJZmxvYXQgYyA9IDEgLSAoZ29faW53YXJkID8gLWRkbiA6IHJlZnJhY3Rpb25fZGlyLnRyYW5zcG9zZSgpICogaW50ZS5ub3JtYWwpOwoJCWZsb2F0IGZyZXNuZWxfcmVmbGVjdGFuY2UgPSBSMCArICgxLjBmIC0gUjApICogYyAqIGMgKiBjICogYzsKCQlmbG9hdCBQID0gMC4yNWYgKyAwLjVmICogZnJlc25lbF9yZWZsZWN0YW5jZTsKCgkJVmVjM2YgcmVzOwoJCWlmIChuZXdfZGVwdGggPiAyKQoJCXsKCQkJaWYgKHJhbmQoKSA8IFApCgkJCQlyZXMgPSAoZnJlc25lbF9yZWZsZWN0YW5jZSAvIFApICoKCQkJCXJhZGlhbmNlKHNjZW5lLCBSYXl7IGludGUucG9zaXRpb24sIHJlZmxlY3RfZGlyIH0sIG5ld19kZXB0aCwgbWluX2RlcHRoLCBtYXhfZGVwdGgsIHJhbmQpOwoJCQllbHNlCgkJCQlyZXMgPSAoKDEuMGYgLSBmcmVzbmVsX3JlZmxlY3RhbmNlKSAvICgxLjBmIC0gUCkpICoKCQkJCXJhZGlhbmNlKHNjZW5lLCBSYXl7IGludGUucG9zaXRpb24sIHJlZnJhY3Rpb25fZGlyIH0sIG5ld19kZXB0aCwgbWluX2RlcHRoLCBtYXhfZGVwdGgsIHJhbmQpOwoJCX0KCQllbHNlCgkJCXJlcyA9CgkJCWZyZXNuZWxfcmVmbGVjdGFuY2UgKiByYWRpYW5jZShzY2VuZSwgUmF5eyBpbnRlLnBvc2l0aW9uLCByZWZsZWN0X2RpciB9LCBuZXdfZGVwdGgsIG1pbl9kZXB0aCwgbWF4X2RlcHRoLCByYW5kKSArCgkJCSgxLjBmIC0gZnJlc25lbF9yZWZsZWN0YW5jZSkgKiByYWRpYW5jZShzY2VuZSwgUmF5eyBpbnRlLnBvc2l0aW9uLCByZWZyYWN0aW9uX2RpciB9LCBuZXdfZGVwdGgsIG1pbl9kZXB0aCwgbWF4X2RlcHRoLCByYW5kKTsKCgkJcmV0dXJuIGVtaXNzaW9uICsgaW50ZS5jb2xvci5jd2lzZVByb2R1Y3QocmVzKTsKCX0KCWJyZWFrOwoJZGVmYXVsdDoKCQlzdGQ6OmNvdXQgPDwgIkVSUk9SIDogcmVhY2ggaW52YWxpZCBjb2RlIHNlY3Rpb24iIDw8IHN0ZDo6ZW5kbDsKCQlyZXR1cm4gVmVjM2Y6Olplcm8oKTsKCQlicmVhazsKCX0KfQoKaW5saW5lIGZsb2F0IGNsYW1wKGZsb2F0IHgpIHsKCWlmICh4IDwgMC4wZikKCQlyZXR1cm4gMC4wZjsKCWVsc2UgaWYgKHggPiAxLjBmKQoJCXJldHVybiAxLjBmOwoJZWxzZQoJCXJldHVybiB4Owp9CgppbmxpbmUgaW50IHRvSW50KGZsb2F0IHgpIHsKCXJldHVybiBpbnQocG93KGNsYW1wKHgpLCAxIC8gMi4yKSAqIDI1NSArIC41KTsKfQoKaW50IG1haW4oaW50IGFyZ2MsIGNoYXIgKmFyZ3ZbXSkgewoJY2xvY2tfdCBzdGFydCA9IGNsb2NrKCk7CgoJU2NlbmUgc2NlbmU7CgoJU3BoZXJlIHNwaGVyZXNbXSA9IHsKCQlTcGhlcmUoVmVjM2YoMWU1ZiArIDEuMGYsIDQwLjhmLCA4MS42ZiksCTFlNWYsCVZlYzNmOjpaZXJvKCksCVZlYzNmKC43NWYsIC4yNWYsIC4yNWYpLAl7IE1hdGVyaWFsOjpUeXBlOjpESUZGVVNFLCAwLjBmIH0pLC8vTGVmdAoJCVNwaGVyZShWZWMzZigtMWU1ZiArIDk5LjBmLCA0MC44ZiwgODEuNmYpLAkxZTVmLAlWZWMzZjo6WmVybygpLAlWZWMzZiguMjVmLCAuMjVmLCAuNzVmKSwJeyBNYXRlcmlhbDo6VHlwZTo6RElGRlVTRSwgMC4wZiB9KSwvL1JnaHQKCQlTcGhlcmUoVmVjM2YoNTAuMGYsIDQwLjhmLCAxZTVmKSwJCQkxZTVmLAlWZWMzZjo6WmVybygpLAlWZWMzZiguNzVmLCAuNzVmLCAuNzVmKSwJeyBNYXRlcmlhbDo6VHlwZTo6RElGRlVTRSwgMC4wZiB9KSwvL0JhY2sKCQlTcGhlcmUoVmVjM2YoNTAuMGYsIDQwLjhmLCAtMWU1ZiArIDE3MC4wZiksCTFlNWYsCVZlYzNmOjpaZXJvKCksCVZlYzNmOjpaZXJvKCksCQkJCXsgTWF0ZXJpYWw6OlR5cGU6OkRJRkZVU0UsIDAuMGYgfSksLy9Gcm50CgkJU3BoZXJlKFZlYzNmKDUwLjBmLCAxZTVmLCA4MS42ZiksCQkJMWU1ZiwJVmVjM2Y6Olplcm8oKSwJVmVjM2YoLjc1ZiwgLjc1ZiwgLjc1ZiksCXsgTWF0ZXJpYWw6OlR5cGU6OkRJRkZVU0UsIDAuMGYgfSksLy9Cb3RtCgkJU3BoZXJlKFZlYzNmKDUwLjBmLCAtMWU1ZiArIDgxLjZmLCA4MS42ZiksCTFlNWYsCVZlYzNmOjpaZXJvKCksCVZlYzNmKC43NWYsIC43NWYsIC43NWYpLAl7IE1hdGVyaWFsOjpUeXBlOjpESUZGVVNFLCAwLjBmIH0pLC8vVG9wCgkJU3BoZXJlKFZlYzNmKDI3LjBmLCAxNi41ZiwgNDcuMGYpLAkJCTE2LjVmLAlWZWMzZjo6WmVybygpLAlWZWMzZigxLjBmLCAxLjBmLCAxLjBmKSAqIC45OTlmLAl7IE1hdGVyaWFsOjpUeXBlOjpTUEVDVUxBUiwgMC4wZiB9KSwvL01pcnIKCQlTcGhlcmUoVmVjM2YoNzMuMGYsIDE2LjVmLCA3OC4wZiksCQkJMTYuNWYsCVZlYzNmOjpaZXJvKCksCVZlYzNmKDEuMGYsIDEuMGYsIDEuMGYpICogLjk5OWYsCXsgTWF0ZXJpYWw6OlR5cGU6OlJFRlJBQ1RJT04sIDIuNmYgfSksLy9HbGFzCgkJU3BoZXJlKFZlYzNmKDUwLjBmLCA2ODEuNmYgLSAwLjI3ZiwgODEuNmYpLAk2MDAuMGYsCVZlYzNmKDEyLjBmLCAxMi4wZiwgMTIuMGYpLCBWZWMzZjo6WmVybygpLAl7IE1hdGVyaWFsOjpUeXBlOjpESUZGVVNFLCAwLjBmIH0pIC8vTGl0ZQoJfTsKCglmb3IgKGF1dG8gc3BoIDogc3BoZXJlcykKCQlzY2VuZS5hZGQoc3BoKTsKCgljb25zdCBpbnQgdyA9IDI1NjsKCWNvbnN0IGludCBoID0gMjU2OwoKCWNvbnN0IGludCBzYW1wcyA9IGFyZ2MgPT0gMiA/IGF0b2koYXJndlsxXSkgLyA0IDogMTAwOyAvLyAjIHNhbXBsZXMKCgljb25zdCBSYXkgY2FtKFZlYzNmKDUwLjBmLCA1Mi4wZiwgMjk1LjZmKSwgVmVjM2YoMC4wZiwgLTAuMDQyNjEyZiwgLTEuMGYpKTsgLy8gY2FtIHBvcywgZGlyCgljb25zdCBWZWMzZiBjeCh3ICogLjUxMzVmIC8gaCwgMC4wZiwgMC4wZik7Cgljb25zdCBWZWMzZiBjeSA9IGN4LmNyb3NzKGNhbS5kaXJlY3Rpb24pLm5vcm1hbGl6ZWQoKSAqIC41MTM1ZjsKCQoJVmVjM2YgKiBidWZmZXIgPSBuZXcgVmVjM2ZbdyAqIGhdOwoKLy8jcHJhZ21hIG9tcCBwYXJhbGxlbCBmb3Igc2NoZWR1bGUoZHluYW1pYywgMSkgICAgICAgLy8gT3Blbk1QCgkvLyBMb29wIG92ZXIgaW1hZ2Ugcm93cwoJZm9yIChpbnQgeSA9IDA7IHkgPCBoOyB5KyspIAoJewoJCWZwcmludGYoc3RkZXJyLCAiXHJSZW5kZXJpbmcgKCVkIHNwcCkgJTUuMmYlJSIsIHNhbXBzICogNCwgMTAwLip5IC8gKGggLSAxKSk7CgkJUmFuZG9tTENHIHJhbmQoeSk7CgoJCS8vIExvb3AgY29scwoJCWZvciAoaW50IHggPSAwOyB4IDwgdzsgeCsrKSAKCQl7CgkJCWludCBjb25zdCBpID0gKGggLSB5IC0gMSkgKiB3ICsgeDsKCQkJLy9pbnQgY29uc3QgaSA9IHkgKiB3ICsgeDsKCQkJYnVmZmVyW2ldID0gVmVjM2Y6Olplcm8oKTsKCQkJCgkJCS8vIDJ4MiBzdWJwaXhlbCAKCQkJZm9yIChpbnQgc3kgPSAwOyBzeSA8IDI7IHN5KyspIGZvciAoaW50IHN4ID0gMDsgc3ggPCAyOyBzeCsrKQoJCQl7CgkJCQlWZWMzZiByID0gVmVjM2Y6Olplcm8oKTsKCQkJCQoJCQkJZm9yIChpbnQgcyA9IDA7IHMgPCBzYW1wczsgcysrKSAKCQkJCXsKCQkJCQlmbG9hdCByMSA9IDIuMGYgKiByYW5kKCk7CgkJCQkJZmxvYXQgcjIgPSAyLjBmICogcmFuZCgpOwoJCQkJCWZsb2F0IGR4ID0gcjEgPCAxLjBmID8gc3FydChyMSkgLSAxLjBmIDogMS4wZiAtIHNxcnQoMi4wZiAtIHIxKTsKCQkJCQlmbG9hdCBkeSA9IHIyIDwgMS4wZiA/IHNxcnQocjIpIC0gMS4wZiA6IDEuMGYgLSBzcXJ0KDIuMGYgLSByMik7CgoJCQkJCVZlYzNmIGQgPSBjeCAqICgoKHN4ICsgMC41ZiArIGR4KSAvIDIuMGYgKyB4KSAvIHcgLSAwLjVmKSArCgkJCQkJCWN5ICogKCgoc3kgKyAwLjVmICsgZHkpIC8gMi4wZiArIHkpIC8gaCAtIDAuNWYpICsgY2FtLmRpcmVjdGlvbjsKCgkJCQkJciA9IHIgKyByYWRpYW5jZShzY2VuZSwgUmF5KGNhbS5vcmlnaW4gKyBkICogMTQwLjBmLCBkKSwgMCwgNSwgMzAsIHJhbmQpICogKDEuMGYgLyBzYW1wcyk7CgkJCQl9CgkJCQlidWZmZXJbaV0gPSBidWZmZXJbaV0gKyBWZWMzZihjbGFtcChyLngoKSksIGNsYW1wKHIueSgpKSwgY2xhbXAoci56KCkpKTsKCQkJfQoJCQlidWZmZXJbaV0gPSBidWZmZXJbaV0gKiAwLjI1ZjsKCgkJCWlmICh4ICUgMTYgPT0gMCAmJiB5ICUgMTYgPT0gMCkKCQkJCXN0ZDo6Y291dCA8PCBidWZmZXJbaV0gPDwgc3RkOjplbmRsOwoKCQl9Cgl9CgoJcHJpbnRmKCJcbiVmIHNlY1xuIiwgKGZsb2F0KShjbG9jaygpIC0gc3RhcnQpIC8gQ0xPQ0tTX1BFUl9TRUMpOwoKCUZJTEUgKmYgPSBmb3BlbigibXlpbWFnZTQucHBtIiwgInciKTsgLy8gV3JpdGUgaW1hZ2UgdG8gUFBNIGZpbGUuCglmcHJpbnRmKGYsICJQM1xuJWQgJWRcbiVkXG4iLCB3LCBoLCAyNTUpOwoJZm9yIChpbnQgaSA9IDA7IGkgPCB3ICogaDsgaSsrKQoJCWZwcmludGYoZiwgIiVkICVkICVkXG4iLCB0b0ludChidWZmZXJbaV1bMF0pLCB0b0ludChidWZmZXJbaV1bMV0pLCB0b0ludChidWZmZXJbaV1bMl0pKTsKCWZjbG9zZShmKTsKCglyZXR1cm4gMDsKfQo=