// Вспомогательный модуль определеняи пересечения некоторых фигур в пространстве
#ifndef COLLIDING_H
#define COLLIDING_H
#include "plane3d.h"
template < class T >
Vertex3D< T > AnglesToDirection( const Vertex3D< T > &ang )
{
return Vertex3D< T >( (T) (sin( ang.y ) * cosf( ang.x )),
(T) sinf( -ang.x ),
(T) (-cosf( ang.x ) * cosf( ang.y )) );
};
template < class T >
Vertex3D< T > DirectionToAngles( const Vertex3D< T > &dir )
{
Vertex3D< T > ret;
if ( dir.x > 0 )
ret.y = (T) (atan( dir.z / dir.x ) + M_PI / 2);
else if ( dir.x < 0 )
ret.y = (T) (atan( dir.z / dir.x ) + 3 * M_PI / 2);
else
ret.y = dir.z < 0 ? 0 : (T) M_PI;
T z = (T) sqrt( dir.x * dir.x + dir.z * dir.z );
if ( dir.y > 0 )
ret.x = (T) (atan( z / dir.y ) - M_PI / 2);
else if ( dir.y < 0 )
ret.x = (T) (atan( z / dir.y ) - 3 * M_PI / 2);
else
ret.x = z < 0 ? 0 : (T) M_PI;
return ret;
};
// TrisContainsPoint: определяет содержит ли треугольник с вершинами v1, v2, v3
// с нормалью n точку p в своих границах (точка должна лежать в плоскости треугольника).
template < class T >
bool TrisContainsPoint( const Vertex3D< T > &v1,
const Vertex3D< T > &v2,
const Vertex3D< T > &v3,
const Vertex3D< T > &n,
const Vertex3D< T > &p )
{
if( n.MixedProduct( v2 - v1, p - v1 ) <= 0 )
return false;
if( n.MixedProduct( v3 - v2, p - v2 ) <= 0 )
return false;
if( n.MixedProduct( v1 - v3, p - v3 ) <= 0 )
return false;
return true;
};
// ClosestPointToSegment: возвращает ближайшую к точке p точку, лежащую
// на отрезке a-b, не выходящую за концы отрезка.
template < class T >
Vertex3D< T > ClosestPointToSegment( const Vertex3D< T > &p, const Vertex3D< T > &a, const Vertex3D< T > &b )
{
Vertex3D< T > c = p - a;
Vertex3D< T > v = b - a;
T d = v.Length();
if ( fabs( d ) < Plane3D< T >::EPS )
return a;
v /= d;
T t = v & c;
if ( t < 0.0f )
return a;
else if ( t > d )
return b;
return a + t * v;
};
// ClosestPointToTris: находит ближайшую к точке p точку, лежащую
// на сторонах треугольника с вершинами v1, v2, v3. Возвращает
// минимальное расстояние между найденной точкой и границей треугольника.
// Найденную точку записывает в результат res.
template < class T >
T ClosestPointToTris( const Vertex3D< T > &v1, const Vertex3D< T > &v2,
const Vertex3D< T > &v3, const Vertex3D< T > &p,
Vertex3D< T > *res )
{
*res = ClosestPointToSegment( p, v1, v2 );
T bestDist = p.DistanceToSqr( *res );
Vertex3D< T > pt = ClosestPointToSegment( p, v1, v3 );
T dist = p.DistanceToSqr( pt );
if ( dist < bestDist )
{
bestDist = dist;
*res = pt;
};
pt = ClosestPointToSegment( p, v2, v3 );
dist = p.DistanceToSqr( pt );
if ( dist < bestDist )
{
bestDist = dist;
*res = pt;
};
return (T) sqrt( bestDist );
}
// IntersectTrisByRay: возвращает true, если луч, испущенный из точки
// org в направлении dir пересекает треугольник в его границах,
// образованного вершинами v1-v2-v3, или false в противном случае.
// Если возвращает true, то в параметр resDist сохраняется расстояние
// до точки пересечения от точки org в единицах, равных длине вектора dir
// (что соответствует времени которое пройдет до пересечения точки org с
// треугольником, если точка двигается со скоростью dir), а в
// параметр resPoint сохраняются координаты точки пересечения.
template < class T >
bool IntersectTrisByRay( const Vertex3D< T > &v1,
const Vertex3D< T > &v2,
const Vertex3D< T > &v3,
const Vertex3D< T > &org,
const Vertex3D< T > &dir,
Vertex3D< T > *resPoint, T *resDist )
{
Plane3D< T > plane( v1, v2, v3 );
if ( !plane.IntersectByRay( org, dir, resDist ) )
return false;
*resPoint = org + *resDist * dir;
// проверяем, находится ли точка пересечения внутри треугольника.
return TrisContainsPoint( v1, v2, v3, plane.n, *resPoint );
};
// IntersectSphereByRay: возвращает true, если луч испущенный из
// точки ord в направлении dir пересекает сферу радиусов radius,
// центр которой находится в точке center.
// Если возвращает true, то в параметр time сохраняется расстояние от
// точки org до точки пересечения в единицах, равных длине вектора dir
// (что соответствует времени которое пройдет до пересечения точки org со
// сферой, если точка двигается со скоростью dir).
template < class T >
bool IntersectSphereByRay( const Vertex3D< T > ¢er, T radius,
const Vertex3D< T > &org, const Vertex3D< T > &dir,
T *time )
{
Vertex3D< T > q = center - org;
T c = q & q; // squared length
Vertex3D< T > nDir = dir;
T l = dir.Length();
if ( fabs( l ) < Plane3D< T >::EPS )
return false;
nDir /= l;
T v = q & nDir;
T d = radius * radius - (c - v * v);
if ( d < 0.0f )
return false;
*time = (T)(v - sqrt( d )) / l;
return true;
};
// IntersectSphereBySphere: возвращает true, если окружности с радиусами
// r1 и r2 с центрами в точках c1 и с2, движущиеся со скоростями v1 и v2
// соприкоснутся в результате своего движения.
// Если возвращает true, то в параметр time сохраняется время которое должно
// пройти до момента соприкосновения, а в параметр normal - нормаль соприкоснования, т.е.
// единичный вектор, лежащий на отрезке, соединяющей центры окружностей в момент
// соприкосновения, направленный в сторону центра первой окружности c1.
template < class T >
bool IntersectSphereBySphere( const Vertex3D< T > &c1, T r1, const Vertex3D< T > &v1,
const Vertex3D< T > &c2, T r2, const Vertex3D< T > &v2,
T *time, Vertex3D< T > *normal )
{
Vertex3D< T > nPos = c1 - c2;
Vertex3D< T > nSpeed = v1 - v2;
T nSpeedLen = nSpeed.Length();
if ( nSpeedLen <= 0 )
return false;
Vertex3D< T > nSpeedDir = nSpeed / nSpeedLen;
T dist = (-nPos) & nSpeedDir;
if ( dist <= 0 )
return false;
T nPosLen = nPos.Length();
T r = r1 + r2;
T x = nPosLen * nPosLen - dist * dist;
if ( x > r * r )
return false;
T z = dist - sqrtf( r * r - x );
*time = z / nSpeedLen;
*normal = +nPos + nSpeedDir * z;
normal->Normalize();
return true;
};
// IntersectTrisBySphere: возвращает true, если окружность с центром в точке center,
// радиусом radius, движущаяся со скоростью speed пересечет границы
// треугольника, образованного вершинами v1, v2, v3.
// Если вовзращает true, то в параметр time сохраняется время до момента
// соприкоснования, а в параметр normal - нормаль соприкоснования, т.е.
// единичный вектор, лежащий на отрезке, соединяющем центр окружности с точкой
// соприкоснования (в момент соприкосноваения), направленный в сторону центра окружности.
template < class T >
bool IntersectTrisBySphere( const Vertex3D< T > &v1, const Vertex3D< T > &v2, const Vertex3D< T > &v3,
const Vertex3D< T > ¢er, T radius, const Vertex3D< T > &speed,
T *time, Vertex3D< T > *normal )
{
Plane3D< T > plane( v1, v2, v3 );
Vertex3D< T > pt = center - plane.n * radius;
if ( !plane.IntersectByRay( pt, speed, time ) )
return false;
Vertex3D< T > planePt = pt + (*time) * speed;
*normal = plane.n;
// проверяем, находится ли точка пересечения внутри треугольника.
if ( !TrisContainsPoint( v1, v2, v3, plane.n, planePt ) )
{
// находим ближайшую точку на треугольнике
Vertex3D< T > trisPt;
ClosestPointToTris( v1, v2, v3, planePt, &trisPt );
if ( !IntersectSphereByRay( center, radius, trisPt, -speed, time ) )
return false;
*normal = center - (trisPt - (*time) * speed);
normal->Normalize();
};
return true;
};
#endif
Ly8g0JLRgdC/0L7QvNC+0LPQsNGC0LXQu9GM0L3Ri9C5INC80L7QtNGD0LvRjCDQvtC/0YDQtdC00LXQu9C10L3Rj9C4INC/0LXRgNC10YHQtdGH0LXQvdC40Y8g0L3QtdC60L7RgtC+0YDRi9GFINGE0LjQs9GD0YAg0LIg0L/RgNC+0YHRgtGA0LDQvdGB0YLQstC1CgojaWZuZGVmIENPTExJRElOR19ICiNkZWZpbmUgQ09MTElESU5HX0gKCiNpbmNsdWRlICJwbGFuZTNkLmgiCgp0ZW1wbGF0ZSA8IGNsYXNzIFQgPgpWZXJ0ZXgzRDwgVCA+IEFuZ2xlc1RvRGlyZWN0aW9uKCBjb25zdCBWZXJ0ZXgzRDwgVCA+ICZhbmcgKQp7CiAgcmV0dXJuIFZlcnRleDNEPCBUID4oIChUKSAoc2luKCBhbmcueSApICogY29zZiggYW5nLnggKSksIAogICAgICAgICAgICAgICAgICAgICAgICAoVCkgc2luZiggLWFuZy54ICksIAogICAgICAgICAgICAgICAgICAgICAgICAoVCkgKC1jb3NmKCBhbmcueCApICogY29zZiggYW5nLnkgKSkgKTsKfTsKCnRlbXBsYXRlIDwgY2xhc3MgVCA+ClZlcnRleDNEPCBUID4gRGlyZWN0aW9uVG9BbmdsZXMoIGNvbnN0IFZlcnRleDNEPCBUID4gJmRpciApCnsKICBWZXJ0ZXgzRDwgVCA+IHJldDsKICBpZiAoIGRpci54ID4gMCApCiAgICByZXQueSA9IChUKSAoYXRhbiggZGlyLnogLyBkaXIueCApICsgTV9QSSAvIDIpOwogIGVsc2UgaWYgKCBkaXIueCA8IDAgKQogICAgcmV0LnkgPSAoVCkgKGF0YW4oIGRpci56IC8gZGlyLnggKSArIDMgKiBNX1BJIC8gMik7CiAgZWxzZQogICAgcmV0LnkgPSBkaXIueiA8IDAgPyAwIDogKFQpIE1fUEk7CiAgVCB6ID0gKFQpIHNxcnQoIGRpci54ICogZGlyLnggKyBkaXIueiAqIGRpci56ICk7CiAgaWYgKCBkaXIueSA+IDAgKQogICAgcmV0LnggPSAoVCkgKGF0YW4oIHogLyBkaXIueSApIC0gTV9QSSAvIDIpOwogIGVsc2UgaWYgKCBkaXIueSA8IDAgKQogICAgcmV0LnggPSAoVCkgKGF0YW4oIHogLyBkaXIueSApIC0gMyAqIE1fUEkgLyAyKTsKICBlbHNlCiAgICByZXQueCA9IHogPCAwID8gMCA6IChUKSBNX1BJOwogIHJldHVybiByZXQ7Cn07CgovLyBUcmlzQ29udGFpbnNQb2ludDog0L7Qv9GA0LXQtNC10LvRj9C10YIg0YHQvtC00LXRgNC20LjRgiDQu9C4INGC0YDQtdGD0LPQvtC70YzQvdC40Log0YEg0LLQtdGA0YjQuNC90LDQvNC4IHYxLCB2MiwgdjMKLy8g0YEg0L3QvtGA0LzQsNC70YzRjiBuINGC0L7Rh9C60YMgcCDQsiDRgdCy0L7QuNGFINCz0YDQsNC90LjRhtCw0YUgKNGC0L7Rh9C60LAg0LTQvtC70LbQvdCwINC70LXQttCw0YLRjCDQsiDQv9C70L7RgdC60L7RgdGC0Lgg0YLRgNC10YPQs9C+0LvRjNC90LjQutCwKS4KdGVtcGxhdGUgPCBjbGFzcyBUID4KYm9vbCBUcmlzQ29udGFpbnNQb2ludCggY29uc3QgVmVydGV4M0Q8IFQgPiAmdjEsIAogICAgICAgICAgICAgICAgICAgICAgICBjb25zdCBWZXJ0ZXgzRDwgVCA+ICZ2MiwgCiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IFZlcnRleDNEPCBUID4gJnYzLAogICAgICAgICAgICAgICAgICAgICAgICBjb25zdCBWZXJ0ZXgzRDwgVCA+ICZuLAogICAgICAgICAgICAgICAgICAgICAgICBjb25zdCBWZXJ0ZXgzRDwgVCA+ICZwICkKewogIGlmKCBuLk1peGVkUHJvZHVjdCggdjIgLSB2MSwgcCAtIHYxICkgPD0gMCApIAogICAgcmV0dXJuIGZhbHNlOyAKICBpZiggbi5NaXhlZFByb2R1Y3QoIHYzIC0gdjIsIHAgLSB2MiApIDw9IDAgKSAKICAgIHJldHVybiBmYWxzZTsgCiAgaWYoIG4uTWl4ZWRQcm9kdWN0KCB2MSAtIHYzLCBwIC0gdjMgKSA8PSAwICkgCiAgICByZXR1cm4gZmFsc2U7IAogIHJldHVybiB0cnVlOwp9OwoKLy8gQ2xvc2VzdFBvaW50VG9TZWdtZW50OiDQstC+0LfQstGA0LDRidCw0LXRgiDQsdC70LjQttCw0LnRiNGD0Y4g0Log0YLQvtGH0LrQtSBwINGC0L7Rh9C60YMsINC70LXQttCw0YnRg9GOCi8vINC90LAg0L7RgtGA0LXQt9C60LUgYS1iLCDQvdC1INCy0YvRhdC+0LTRj9GJ0YPRjiDQt9CwINC60L7QvdGG0Ysg0L7RgtGA0LXQt9C60LAuCnRlbXBsYXRlIDwgY2xhc3MgVCA+ClZlcnRleDNEPCBUID4gQ2xvc2VzdFBvaW50VG9TZWdtZW50KCBjb25zdCBWZXJ0ZXgzRDwgVCA+ICZwLCBjb25zdCBWZXJ0ZXgzRDwgVCA+ICZhLCBjb25zdCBWZXJ0ZXgzRDwgVCA+ICZiICkKewogIFZlcnRleDNEPCBUID4gYyA9IHAgLSBhOwogIFZlcnRleDNEPCBUID4gdiA9IGIgLSBhOwogIFQgZCA9IHYuTGVuZ3RoKCk7CiAgaWYgKCBmYWJzKCBkICkgPCBQbGFuZTNEPCBUID46OkVQUyApCiAgICByZXR1cm4gYTsKICB2IC89IGQ7CiAgVCB0ID0gdiAmIGM7CiAgaWYgKCB0IDwgMC4wZiApCiAgICByZXR1cm4gYTsKICBlbHNlIGlmICggdCA+IGQgKQogICAgcmV0dXJuIGI7CiAgcmV0dXJuIGEgKyB0ICogdjsKfTsKCi8vIENsb3Nlc3RQb2ludFRvVHJpczog0L3QsNGF0L7QtNC40YIg0LHQu9C40LbQsNC50YjRg9GOINC6INGC0L7Rh9C60LUgcCDRgtC+0YfQutGDLCDQu9C10LbQsNGJ0YPRjgovLyDQvdCwINGB0YLQvtGA0L7QvdCw0YUg0YLRgNC10YPQs9C+0LvRjNC90LjQutCwINGBINCy0LXRgNGI0LjQvdCw0LzQuCB2MSwgdjIsIHYzLiDQktC+0LfQstGA0LDRidCw0LXRggovLyDQvNC40L3QuNC80LDQu9GM0L3QvtC1INGA0LDRgdGB0YLQvtGP0L3QuNC1INC80LXQttC00YMg0L3QsNC50LTQtdC90L3QvtC5INGC0L7Rh9C60L7QuSDQuCDQs9GA0LDQvdC40YbQtdC5INGC0YDQtdGD0LPQvtC70YzQvdC40LrQsC4KLy8g0J3QsNC50LTQtdC90L3Rg9GOINGC0L7Rh9C60YMg0LfQsNC/0LjRgdGL0LLQsNC10YIg0LIg0YDQtdC30YPQu9GM0YLQsNGCIHJlcy4KdGVtcGxhdGUgPCBjbGFzcyBUID4KVCBDbG9zZXN0UG9pbnRUb1RyaXMoIGNvbnN0IFZlcnRleDNEPCBUID4gJnYxLCBjb25zdCBWZXJ0ZXgzRDwgVCA+ICZ2MiwgCiAgICAgICAgICAgICAgICAgICAgICBjb25zdCBWZXJ0ZXgzRDwgVCA+ICZ2MywgY29uc3QgVmVydGV4M0Q8IFQgPiAmcCwgCiAgICAgICAgICAgICAgICAgICAgICBWZXJ0ZXgzRDwgVCA+ICpyZXMgKQp7CiAgKnJlcyA9IENsb3Nlc3RQb2ludFRvU2VnbWVudCggcCwgdjEsIHYyICk7CiAgVCBiZXN0RGlzdCA9IHAuRGlzdGFuY2VUb1NxciggKnJlcyApOwogIAogIFZlcnRleDNEPCBUID4gcHQgPSBDbG9zZXN0UG9pbnRUb1NlZ21lbnQoIHAsIHYxLCB2MyApOwogIFQgZGlzdCA9IHAuRGlzdGFuY2VUb1NxciggcHQgKTsKICBpZiAoIGRpc3QgPCBiZXN0RGlzdCApCiAgewogICAgYmVzdERpc3QgPSBkaXN0OwogICAgKnJlcyA9IHB0OwogIH07CiAgcHQgPSBDbG9zZXN0UG9pbnRUb1NlZ21lbnQoIHAsIHYyLCB2MyApOwogIGRpc3QgPSBwLkRpc3RhbmNlVG9TcXIoIHB0ICk7CiAgaWYgKCBkaXN0IDwgYmVzdERpc3QgKQogIHsKICAgIGJlc3REaXN0ID0gZGlzdDsKICAgICpyZXMgPSBwdDsKICB9OwoKICByZXR1cm4gKFQpIHNxcnQoIGJlc3REaXN0ICk7Cn0KCi8vIEludGVyc2VjdFRyaXNCeVJheTog0LLQvtC30LLRgNCw0YnQsNC10YIgdHJ1ZSwg0LXRgdC70Lgg0LvRg9GHLCDQuNGB0L/Rg9GJ0LXQvdC90YvQuSDQuNC3INGC0L7Rh9C60LgKLy8gb3JnINCyINC90LDQv9GA0LDQstC70LXQvdC40LggZGlyINC/0LXRgNC10YHQtdC60LDQtdGCINGC0YDQtdGD0LPQvtC70YzQvdC40Log0LIg0LXQs9C+INCz0YDQsNC90LjRhtCw0YUsIAovLyDQvtCx0YDQsNC30L7QstCw0L3QvdC+0LPQviDQstC10YDRiNC40L3QsNC80LggdjEtdjItdjMsINC40LvQuCBmYWxzZSDQsiDQv9GA0L7RgtC40LLQvdC+0Lwg0YHQu9GD0YfQsNC1LiAKLy8g0JXRgdC70Lgg0LLQvtC30LLRgNCw0YnQsNC10YIgdHJ1ZSwg0YLQviDQsiDQv9Cw0YDQsNC80LXRgtGAIHJlc0Rpc3Qg0YHQvtGF0YDQsNC90Y/QtdGC0YHRjyDRgNCw0YHRgdGC0L7Rj9C90LjQtQovLyDQtNC+INGC0L7Rh9C60Lgg0L/QtdGA0LXRgdC10YfQtdC90LjRjyDQvtGCINGC0L7Rh9C60Lggb3JnINCyINC10LTQuNC90LjRhtCw0YUsINGA0LDQstC90YvRhSDQtNC70LjQvdC1INCy0LXQutGC0L7RgNCwIGRpcgovLyAo0YfRgtC+INGB0L7QvtGC0LLQtdGC0YHRgtCy0YPQtdGCINCy0YDQtdC80LXQvdC4INC60L7RgtC+0YDQvtC1INC/0YDQvtC50LTQtdGCINC00L4g0L/QtdGA0LXRgdC10YfQtdC90LjRjyDRgtC+0YfQutC4IG9yZyDRgSAKLy8g0YLRgNC10YPQs9C+0LvRjNC90LjQutC+0LwsINC10YHQu9C4INGC0L7Rh9C60LAg0LTQstC40LPQsNC10YLRgdGPINGB0L4g0YHQutC+0YDQvtGB0YLRjNGOIGRpciksINCwINCyCi8vINC/0LDRgNCw0LzQtdGC0YAgcmVzUG9pbnQg0YHQvtGF0YDQsNC90Y/RjtGC0YHRjyDQutC+0L7RgNC00LjQvdCw0YLRiyDRgtC+0YfQutC4INC/0LXRgNC10YHQtdGH0LXQvdC40Y8uCnRlbXBsYXRlIDwgY2xhc3MgVCA+CmJvb2wgSW50ZXJzZWN0VHJpc0J5UmF5KCBjb25zdCBWZXJ0ZXgzRDwgVCA+ICZ2MSwKICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IFZlcnRleDNEPCBUID4gJnYyLAogICAgICAgICAgICAgICAgICAgICAgICAgY29uc3QgVmVydGV4M0Q8IFQgPiAmdjMsCiAgICAgICAgICAgICAgICAgICAgICAgICBjb25zdCBWZXJ0ZXgzRDwgVCA+ICZvcmcsCiAgICAgICAgICAgICAgICAgICAgICAgICBjb25zdCBWZXJ0ZXgzRDwgVCA+ICZkaXIsCiAgICAgICAgICAgICAgICAgICAgICAgICBWZXJ0ZXgzRDwgVCA+ICpyZXNQb2ludCwgVCAqcmVzRGlzdCApCnsKICBQbGFuZTNEPCBUID4gcGxhbmUoIHYxLCB2MiwgdjMgKTsKICBpZiAoICFwbGFuZS5JbnRlcnNlY3RCeVJheSggb3JnLCBkaXIsIHJlc0Rpc3QgKSApCiAgICByZXR1cm4gZmFsc2U7CiAgKnJlc1BvaW50ID0gb3JnICsgKnJlc0Rpc3QgKiBkaXI7CiAgLy8g0L/RgNC+0LLQtdGA0Y/QtdC8LCDQvdCw0YXQvtC00LjRgtGB0Y8g0LvQuCDRgtC+0YfQutCwINC/0LXRgNC10YHQtdGH0LXQvdC40Y8g0LLQvdGD0YLRgNC4INGC0YDQtdGD0LPQvtC70YzQvdC40LrQsC4KICByZXR1cm4gVHJpc0NvbnRhaW5zUG9pbnQoIHYxLCB2MiwgdjMsIHBsYW5lLm4sICpyZXNQb2ludCApOyAKfTsKCi8vIEludGVyc2VjdFNwaGVyZUJ5UmF5OiDQstC+0LfQstGA0LDRidCw0LXRgiB0cnVlLCDQtdGB0LvQuCDQu9GD0Ycg0LjRgdC/0YPRidC10L3QvdGL0LkgINC40LcKLy8g0YLQvtGH0LrQuCBvcmQg0LIg0L3QsNC/0YDQsNCy0LvQtdC90LjQuCBkaXIg0L/QtdGA0LXRgdC10LrQsNC10YIg0YHRhNC10YDRgyDRgNCw0LTQuNGD0YHQvtCyIHJhZGl1cywKLy8g0YbQtdC90YLRgCDQutC+0YLQvtGA0L7QuSDQvdCw0YXQvtC00LjRgtGB0Y8g0LIg0YLQvtGH0LrQtSBjZW50ZXIuIAovLyDQldGB0LvQuCDQstC+0LfQstGA0LDRidCw0LXRgiB0cnVlLCDRgtC+INCyINC/0LDRgNCw0LzQtdGC0YAgdGltZSDRgdC+0YXRgNCw0L3Rj9C10YLRgdGPINGA0LDRgdGB0YLQvtGP0L3QuNC1INC+0YIgCi8vINGC0L7Rh9C60Lggb3JnINC00L4g0YLQvtGH0LrQuCDQv9C10YDQtdGB0LXRh9C10L3QuNGPINCyINC10LTQuNC90LjRhtCw0YUsINGA0LDQstC90YvRhSDQtNC70LjQvdC1INCy0LXQutGC0L7RgNCwIGRpcgovLyAo0YfRgtC+INGB0L7QvtGC0LLQtdGC0YHRgtCy0YPQtdGCINCy0YDQtdC80LXQvdC4INC60L7RgtC+0YDQvtC1INC/0YDQvtC50LTQtdGCINC00L4g0L/QtdGA0LXRgdC10YfQtdC90LjRjyDRgtC+0YfQutC4IG9yZyDRgdC+Ci8vINGB0YTQtdGA0L7QuSwg0LXRgdC70Lgg0YLQvtGH0LrQsCDQtNCy0LjQs9Cw0LXRgtGB0Y8g0YHQviDRgdC60L7RgNC+0YHRgtGM0Y4gZGlyKS4KdGVtcGxhdGUgPCBjbGFzcyBUID4KYm9vbCBJbnRlcnNlY3RTcGhlcmVCeVJheSggY29uc3QgVmVydGV4M0Q8IFQgPiAmY2VudGVyLCBUIHJhZGl1cywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IFZlcnRleDNEPCBUID4gJm9yZywgY29uc3QgVmVydGV4M0Q8IFQgPiAmZGlyLAogICAgICAgICAgICAgICAgICAgICAgICAgICBUICp0aW1lICkKewogIFZlcnRleDNEPCBUID4gcSA9IGNlbnRlciAtIG9yZzsKICBUIGMgPSBxICYgcTsgICAgICAgICAgICAgLy8gc3F1YXJlZCBsZW5ndGgKICBWZXJ0ZXgzRDwgVCA+IG5EaXIgPSBkaXI7CiAgVCBsID0gZGlyLkxlbmd0aCgpOwogIGlmICggZmFicyggbCApIDwgUGxhbmUzRDwgVCA+OjpFUFMgKQogICAgcmV0dXJuIGZhbHNlOwogIG5EaXIgLz0gbDsKICBUIHYgPSBxICYgbkRpcjsKICBUIGQgPSByYWRpdXMgKiByYWRpdXMgLSAoYyAtIHYgKiB2KTsKICBpZiAoIGQgPCAwLjBmICkKICAgIHJldHVybiBmYWxzZTsKICAqdGltZSA9IChUKSh2IC0gc3FydCggZCApKSAvIGw7CiAgcmV0dXJuIHRydWU7Cn07CgovLyBJbnRlcnNlY3RTcGhlcmVCeVNwaGVyZTog0LLQvtC30LLRgNCw0YnQsNC10YIgdHJ1ZSwg0LXRgdC70Lgg0L7QutGA0YPQttC90L7RgdGC0Lgg0YEg0YDQsNC00LjRg9GB0LDQvNC4Ci8vIHIxINC4IHIyINGBINGG0LXQvdGC0YDQsNC80Lgg0LIg0YLQvtGH0LrQsNGFIGMxINC4INGBMiwg0LTQstC40LbRg9GJ0LjQtdGB0Y8g0YHQviDRgdC60L7RgNC+0YHRgtGP0LzQuCB2MSDQuCB2MgovLyDRgdC+0L/RgNC40LrQvtGB0L3Rg9GC0YHRjyDQsiDRgNC10LfRg9C70YzRgtCw0YLQtSDRgdCy0L7QtdCz0L4g0LTQstC40LbQtdC90LjRjy4gCi8vINCV0YHQu9C4INCy0L7Qt9Cy0YDQsNGJ0LDQtdGCIHRydWUsINGC0L4g0LIg0L/QsNGA0LDQvNC10YLRgCB0aW1lINGB0L7RhdGA0LDQvdGP0LXRgtGB0Y8g0LLRgNC10LzRjyDQutC+0YLQvtGA0L7QtSDQtNC+0LvQttC90L4KLy8g0L/RgNC+0LnRgtC4INC00L4g0LzQvtC80LXQvdGC0LAg0YHQvtC/0YDQuNC60L7RgdC90L7QstC10L3QuNGPLCDQsCDQsiDQv9Cw0YDQsNC80LXRgtGAIG5vcm1hbCAtINC90L7RgNC80LDQu9GMINGB0L7Qv9GA0LjQutC+0YHQvdC+0LLQsNC90LjRjywg0YIu0LUuCi8vINC10LTQuNC90LjRh9C90YvQuSDQstC10LrRgtC+0YAsINC70LXQttCw0YnQuNC5INC90LAg0L7RgtGA0LXQt9C60LUsINGB0L7QtdC00LjQvdGP0Y7RidC10Lkg0YbQtdC90YLRgNGLINC+0LrRgNGD0LbQvdC+0YHRgtC10Lkg0LIg0LzQvtC80LXQvdGCCi8vINGB0L7Qv9GA0LjQutC+0YHQvdC+0LLQtdC90LjRjywg0L3QsNC/0YDQsNCy0LvQtdC90L3Ri9C5INCyINGB0YLQvtGA0L7QvdGDINGG0LXQvdGC0YDQsCDQv9C10YDQstC+0Lkg0L7QutGA0YPQttC90L7RgdGC0LggYzEuCnRlbXBsYXRlIDwgY2xhc3MgVCA+CmJvb2wgSW50ZXJzZWN0U3BoZXJlQnlTcGhlcmUoIGNvbnN0IFZlcnRleDNEPCBUID4gJmMxLCBUIHIxLCBjb25zdCBWZXJ0ZXgzRDwgVCA+ICZ2MSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29uc3QgVmVydGV4M0Q8IFQgPiAmYzIsIFQgcjIsIGNvbnN0IFZlcnRleDNEPCBUID4gJnYyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUICp0aW1lLCBWZXJ0ZXgzRDwgVCA+ICpub3JtYWwgKQp7CiAgVmVydGV4M0Q8IFQgPiBuUG9zID0gYzEgLSBjMjsKICBWZXJ0ZXgzRDwgVCA+IG5TcGVlZCA9IHYxIC0gdjI7CiAgVCBuU3BlZWRMZW4gPSBuU3BlZWQuTGVuZ3RoKCk7CiAgaWYgKCBuU3BlZWRMZW4gPD0gMCApCiAgICByZXR1cm4gZmFsc2U7CiAgVmVydGV4M0Q8IFQgPiBuU3BlZWREaXIgPSBuU3BlZWQgLyBuU3BlZWRMZW47CiAgVCBkaXN0ID0gKC1uUG9zKSAmIG5TcGVlZERpcjsKICBpZiAoIGRpc3QgPD0gMCApCiAgICByZXR1cm4gZmFsc2U7CiAgVCBuUG9zTGVuID0gblBvcy5MZW5ndGgoKTsKICBUIHIgPSByMSArIHIyOwogIFQgeCA9IG5Qb3NMZW4gKiBuUG9zTGVuIC0gZGlzdCAqIGRpc3Q7CiAgaWYgKCB4ID4gciAqIHIgKQogICAgcmV0dXJuIGZhbHNlOwogIFQgeiA9IGRpc3QgLSBzcXJ0ZiggciAqIHIgLSB4ICk7CiAgKnRpbWUgPSB6IC8gblNwZWVkTGVuOwogICpub3JtYWwgPSArblBvcyArIG5TcGVlZERpciAqIHo7CiAgbm9ybWFsLT5Ob3JtYWxpemUoKTsKICByZXR1cm4gdHJ1ZTsKfTsKCi8vIEludGVyc2VjdFRyaXNCeVNwaGVyZTog0LLQvtC30LLRgNCw0YnQsNC10YIgdHJ1ZSwg0LXRgdC70Lgg0L7QutGA0YPQttC90L7RgdGC0Ywg0YEg0YbQtdC90YLRgNC+0Lwg0LIg0YLQvtGH0LrQtSBjZW50ZXIsCi8vINGA0LDQtNC40YPRgdC+0LwgcmFkaXVzLCDQtNCy0LjQttGD0YnQsNGP0YHRjyDRgdC+INGB0LrQvtGA0L7RgdGC0YzRjiBzcGVlZCDQv9C10YDQtdGB0LXRh9C10YIg0LPRgNCw0L3QuNGG0YsKLy8g0YLRgNC10YPQs9C+0LvRjNC90LjQutCwLCDQvtCx0YDQsNC30L7QstCw0L3QvdC+0LPQviDQstC10YDRiNC40L3QsNC80LggdjEsIHYyLCB2My4gCi8vINCV0YHQu9C4INCy0L7QstC30YDQsNGJ0LDQtdGCIHRydWUsINGC0L4g0LIg0L/QsNGA0LDQvNC10YLRgCB0aW1lINGB0L7RhdGA0LDQvdGP0LXRgtGB0Y8g0LLRgNC10LzRjyDQtNC+INC80L7QvNC10L3RgtCwCi8vINGB0L7Qv9GA0LjQutC+0YHQvdC+0LLQsNC90LjRjywg0LAg0LIg0L/QsNGA0LDQvNC10YLRgCBub3JtYWwgLSDQvdC+0YDQvNCw0LvRjCDRgdC+0L/RgNC40LrQvtGB0L3QvtCy0LDQvdC40Y8sINGCLtC1LgovLyDQtdC00LjQvdC40YfQvdGL0Lkg0LLQtdC60YLQvtGALCDQu9C10LbQsNGJ0LjQuSDQvdCwINC+0YLRgNC10LfQutC1LCDRgdC+0LXQtNC40L3Rj9GO0YnQtdC8INGG0LXQvdGC0YAg0L7QutGA0YPQttC90L7RgdGC0Lgg0YEg0YLQvtGH0LrQvtC5Ci8vINGB0L7Qv9GA0LjQutC+0YHQvdC+0LLQsNC90LjRjyAo0LIg0LzQvtC80LXQvdGCINGB0L7Qv9GA0LjQutC+0YHQvdC+0LLQsNC10L3QuNGPKSwg0L3QsNC/0YDQsNCy0LvQtdC90L3Ri9C5INCyINGB0YLQvtGA0L7QvdGDINGG0LXQvdGC0YDQsCDQvtC60YDRg9C20L3QvtGB0YLQuC4KdGVtcGxhdGUgPCBjbGFzcyBUID4KYm9vbCBJbnRlcnNlY3RUcmlzQnlTcGhlcmUoIGNvbnN0IFZlcnRleDNEPCBUID4gJnYxLCBjb25zdCBWZXJ0ZXgzRDwgVCA+ICZ2MiwgY29uc3QgVmVydGV4M0Q8IFQgPiAmdjMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb25zdCBWZXJ0ZXgzRDwgVCA+ICZjZW50ZXIsIFQgcmFkaXVzLCBjb25zdCBWZXJ0ZXgzRDwgVCA+ICZzcGVlZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFQgKnRpbWUsIFZlcnRleDNEPCBUID4gKm5vcm1hbCApCnsKICBQbGFuZTNEPCBUID4gcGxhbmUoIHYxLCB2MiwgdjMgKTsKICBWZXJ0ZXgzRDwgVCA+IHB0ID0gY2VudGVyIC0gcGxhbmUubiAqIHJhZGl1czsKICBpZiAoICFwbGFuZS5JbnRlcnNlY3RCeVJheSggcHQsIHNwZWVkLCB0aW1lICkgKQogICAgcmV0dXJuIGZhbHNlOwogIFZlcnRleDNEPCBUID4gcGxhbmVQdCA9IHB0ICsgKCp0aW1lKSAqIHNwZWVkOwogICpub3JtYWwgPSBwbGFuZS5uOwogIC8vINC/0YDQvtCy0LXRgNGP0LXQvCwg0L3QsNGF0L7QtNC40YLRgdGPINC70Lgg0YLQvtGH0LrQsCDQv9C10YDQtdGB0LXRh9C10L3QuNGPINCy0L3Rg9GC0YDQuCDRgtGA0LXRg9Cz0L7Qu9GM0L3QuNC60LAuIAogIGlmICggIVRyaXNDb250YWluc1BvaW50KCB2MSwgdjIsIHYzLCBwbGFuZS5uLCBwbGFuZVB0ICkgKQogIHsKICAgIC8vINC90LDRhdC+0LTQuNC8INCx0LvQuNC20LDQudGI0YPRjiDRgtC+0YfQutGDINC90LAg0YLRgNC10YPQs9C+0LvRjNC90LjQutC1CiAgICBWZXJ0ZXgzRDwgVCA+IHRyaXNQdDsKICAgIENsb3Nlc3RQb2ludFRvVHJpcyggdjEsIHYyLCB2MywgcGxhbmVQdCwgJnRyaXNQdCApOwogICAgaWYgKCAhSW50ZXJzZWN0U3BoZXJlQnlSYXkoIGNlbnRlciwgcmFkaXVzLCB0cmlzUHQsIC1zcGVlZCwgdGltZSApICkKICAgICAgcmV0dXJuIGZhbHNlOwogICAgKm5vcm1hbCA9IGNlbnRlciAtICh0cmlzUHQgLSAoKnRpbWUpICogc3BlZWQpOwogICAgbm9ybWFsLT5Ob3JtYWxpemUoKTsKICB9OwogIHJldHVybiB0cnVlOwp9OwoKI2VuZGlm