#include <Core/Basic_Geometry/SMOOTH_GEAR.h>
#include <Core/Auto_Diff/AUTO_HESS.h>
#include <Core/Basic_Geometry/RAY_HIT.h>
#include <Core/Math_Tools/sqr.h>
#include <Core/Matrices/MATRIX.h>
#include <Core/Meshes/SEGMENTED_CURVE.h>
#include <Core/Meshes/TRIANGULATED_AREA.h>
#include <Core/Meshes/TRIANGULATED_SURFACE.h>
#include <Core/Read_Write/READ_WRITE.h>
#include <algorithm>
namespace PhysBAM{
using std::atan2;
//#####################################################################
// Constructor
//#####################################################################
template<class T> SMOOTH_GEAR<VECTOR<T,2> >::
SMOOTH_GEAR(T R,T S,int N)
    :r(R),s(S),n(N)
{
    Compute_Centers();
}
//#####################################################################
// Constructor
//#####################################################################
template<class T> SMOOTH_GEAR<VECTOR<T,2> >::
SMOOTH_GEAR(const TV& dimensions,int N)
    :r(dimensions.x),s(dimensions.y),n(N)
{
    Compute_Centers();
}
//#####################################################################
// Function Compute_Centers
//#####################################################################
template<class T> void SMOOTH_GEAR<VECTOR<T,2> >::
Compute_Centers()
{
    T u=cos(pi_v<T>/n);
    T r2=r*r,s2=s*s,pl=r2+s2,rm=r2-s2,q=rm/u;
    PHYSBAM_ASSERT(pl>q);
    T y=sqrt(pl*pl-q*q);
    ci=sqrt(pl-y);
    co=q/ci;
    Ci=TV(ci,0);
    Co=co*TV(cos(pi/n),sin(pi/n));
    den=2*pi/n;
}
//#####################################################################
// Function Bounding_Box
//#####################################################################
template<class T> RANGE<VECTOR<T,2> > SMOOTH_GEAR<VECTOR<T,2> >::
Bounding_Box() const
{
    TV ext(ci+s,ci+s);
    return RANGE<TV>(-ext,ext);
}
//#####################################################################
// Function Compute_Helper
//#####################################################################
template<class T> void SMOOTH_GEAR<VECTOR<T,2> >::
Compute_Helper(const TV& X,HELPER& h) const
{
    h.a=atan2(X.y,X.x);
    h.d=X.Magnitude();
    h.k=floor(h.a/den);
    h.b=h.a-h.k*den;
    h.flip=false;
    if(2*h.b>den){h.flip=true;h.b=den-h.b;}
    h.Y=TV(cos(h.b),sin(h.b))*h.d;
    h.ui=Co.y*(h.Y.x-ci)>h.Y.y*(Co.x-ci);
    h.P=h.ui?Ci:Co;
    h.dY=h.Y-h.P;
    h.m=h.dY.Magnitude();
    h.sd=h.ui?h.m-s:s-h.m;
}
//#####################################################################
// Function Signed_Distance
//#####################################################################
template<class T> T SMOOTH_GEAR<VECTOR<T,2> >::
Signed_Distance(const TV& X) const
{
    HELPER h;
    Compute_Helper(X,h);
    return h.sd;
}
//#####################################################################
// Function Surface
//#####################################################################
template<class T> VECTOR<T,2> SMOOTH_GEAR<VECTOR<T,2> >::
Surface(const TV& X,const HELPER& h) const
{
    T a=(h.k+h.flip)*den;
    T cs=cos(a),sn=sin(a);
    MATRIX<T,2> R(cs,sn,-sn,cs);
    TV e=h.P+s/h.m*h.dY;
    if(h.flip) e.y=-e.y;
    return R*e;
}
//#####################################################################
// Function Surface
//#####################################################################
template<class T> VECTOR<T,2> SMOOTH_GEAR<VECTOR<T,2> >::
Surface(const TV& X) const
{
    HELPER h;
    Compute_Helper(X,h);
    return Surface(X,h);
}
//#####################################################################
// Function Normal
//#####################################################################
template<class T> VECTOR<T,2> SMOOTH_GEAR<VECTOR<T,2> >::
Normal(const TV& X,const HELPER& h) const
{
    T a=(h.k+h.flip)*den;
    T cs=cos(a),sn=sin(a);
    MATRIX<T,2> R(cs,sn,-sn,cs);
    TV d=h.dY/h.m;
    if(!h.ui) d=-d;
    if(h.flip) d.y=-d.y;
    return R*d;
}
//#####################################################################
// Function Normal
//#####################################################################
template<class T> VECTOR<T,2> SMOOTH_GEAR<VECTOR<T,2> >::
Normal(const TV& X) const
{
    HELPER h;
    Compute_Helper(X,h);
    return Normal(X,h);
}
//#####################################################################
// Function Normal
//#####################################################################
template<class T> VECTOR<T,2> SMOOTH_GEAR<VECTOR<T,2> >::
Normal(const TV& X,const int aggregate) const
{
    return Normal(X);
}
//#####################################################################
// Function Hessian
//#####################################################################
template<class T> SYMMETRIC_MATRIX<T,2> SMOOTH_GEAR<VECTOR<T,2> >::
Hessian(const TV& X) const
{
    AUTO_HESS<T,TV> X0=AUTO_HESS<T,TV>::From_Var(X,0),X1=AUTO_HESS<T,TV>::From_Var(X,1);
    AUTO_HESS<T,TV> a=atan2(X1,X0);
    AUTO_HESS<T,TV> d=hypot(X0,X1);
    T k=floor(a.x/den);
    AUTO_HESS<T,TV> b=a-k*den;
    if(2*b.x>den) b=den-b;
    AUTO_HESS<TV,TV> Y=AUTO_HESS<TV,TV>(cos(b),sin(b))*d;
    bool ui=Co.y*(Y(0).x-ci)>Y(1).x*(Co.x-ci);
    TV P=ui?Ci:Co;
    AUTO_HESS<TV,TV> dY=Y-P;
    AUTO_HESS<T,TV> m=dY.Magnitude();
    AUTO_HESS<T,TV> sd=ui?m-s:s-m;
    return sd.ddx;
}
//#####################################################################
// Function Lazy_Inside
//#####################################################################
template<class T> bool SMOOTH_GEAR<VECTOR<T,2> >::
Lazy_Inside(const TV& X) const
{
    return Signed_Distance(X)<0;
}
//#####################################################################
// Function Lazy_Outside
//#####################################################################
template<class T> bool SMOOTH_GEAR<VECTOR<T,2> >::
Lazy_Outside(const TV& X) const
{
    return !Lazy_Inside(X);
}
//#####################################################################
// Function Inside
//#####################################################################
template<class T> bool SMOOTH_GEAR<VECTOR<T,2> >::
Inside(const TV& X,const T thickness_over_two) const
{
    return Signed_Distance(X)<=-thickness_over_two;
}
//#####################################################################
// Function Outside
//#####################################################################
template<class T> bool SMOOTH_GEAR<VECTOR<T,2> >::
Outside(const TV& X,const T thickness_over_two) const
{
    return Signed_Distance(X)>=thickness_over_two;
}
//#####################################################################
// Function Boundary
//#####################################################################
template<class T> bool SMOOTH_GEAR<VECTOR<T,2> >::
Boundary(const TV& X,const T thickness_over_two) const
{
    T sd=Signed_Distance(X);
    return (sd<thickness_over_two && sd>-thickness_over_two);
}
//#####################################################################
// Function Name
//#####################################################################
template<class T> std::string SMOOTH_GEAR<VECTOR<T,2> >::
Name()
{
    return "SMOOTH_GEAR<VECTOR<T,2> >";
}
//#####################################################################
// Constructor
//#####################################################################
template<class T> SMOOTH_GEAR<VECTOR<T,3> >::
SMOOTH_GEAR(T R,T S,int N,T W)
    :g(R,S,N),w(W)
{
}
//#####################################################################
// Constructor
//#####################################################################
template<class T> SMOOTH_GEAR<VECTOR<T,3> >::
SMOOTH_GEAR(const TV& dimensions,int N)
    :g(dimensions.x,dimensions.y,N),w(dimensions.z)
{
}
//#####################################################################
// Function Bounding_Box
//#####################################################################
template<class T> RANGE<VECTOR<T,3> > SMOOTH_GEAR<VECTOR<T,3> >::
Bounding_Box() const
{
    TV ext(g.ci+g.s,g.ci+g.s,w);
    return RANGE<TV>(-ext,ext);
}
//#####################################################################
// Function Signed_Distance
//#####################################################################
template<class T> T SMOOTH_GEAR<VECTOR<T,3> >::
Signed_Distance(const TV& X) const
{
    T ds=g.Signed_Distance(X.Remove_Index(2));
    T dw=abs(X.z)-w;
    if(dw<0) return max(ds,dw);
    if(ds<0) return dw;
    return sqrt(dw*dw+ds*ds);
}
//#####################################################################
// Function Surface
//#####################################################################
template<class T> VECTOR<T,3> SMOOTH_GEAR<VECTOR<T,3> >::
Surface(const TV& X) const
{
    typename GEAR::HELPER h;
    g.Compute_Helper(X.Remove_Index(2),h);
    VECTOR<T,2> S2=g.Surface(X.Remove_Index(2),h);
    T dw=abs(X.z)-w,ds=h.sd;
    if(ds>dw && dw<0) return S2.Append(X.z);
    if(ds<0){
        TV Y(X);
        Y.z=X.z>0?w:-w;
        return Y;}
    return S2.Append(X.z>0?w:-w);
}
//#####################################################################
// Function Normal
//#####################################################################
template<class T> VECTOR<T,3> SMOOTH_GEAR<VECTOR<T,3> >::
Normal(const TV& X) const
{
    typename GEAR::HELPER h;
    g.Compute_Helper(X.Remove_Index(2),h);
    T dw=abs(X.z)-w,ds=h.sd;
    if(ds>dw && dw<0) return g.Normal(X.Remove_Index(2),h).Append(0);
    if(ds<0) return TV(0,0,X.z>0?1:-1);
    return (X-g.Surface(X.Remove_Index(2),h).Append(X.z>0?w:-w)).Normalized();
}
//#####################################################################
// Function Normal
//#####################################################################
template<class T> VECTOR<T,3> SMOOTH_GEAR<VECTOR<T,3> >::
Normal(const TV& X,const int aggregate) const
{
    return Normal(X);
}
//#####################################################################
// Function Hessian
//#####################################################################
template<class T> SYMMETRIC_MATRIX<T,3> SMOOTH_GEAR<VECTOR<T,3> >::
Hessian(const TV& X) const
{
    AUTO_HESS<T,TV> X0=AUTO_HESS<T,TV>::From_Var(X,0),X1=AUTO_HESS<T,TV>::From_Var(X,1),X2=AUTO_HESS<T,TV>::From_Var(X,2);
    AUTO_HESS<T,TV> a=atan2(X1,X0);
    AUTO_HESS<T,TV> d=hypot(X0,X1);
    T k=floor(a.x/g.den);
    AUTO_HESS<T,TV> b=a-k*g.den;
    if(2*b.x>g.den) b=g.den-b;
    AUTO_HESS<TV,TV> Y(cos(b)*d,sin(b)*d,AUTO_HESS<T,TV>::From_Const(0));
    bool ui=g.Co.y*(Y(0).x-g.ci)>Y(1).x*(g.Co.x-g.ci);
    TV P(ui?g.Ci:g.Co);
    AUTO_HESS<TV,TV> dY=Y-P;
    AUTO_HESS<T,TV> m=dY.Magnitude();
    AUTO_HESS<T,TV> sd=ui?m-g.s:g.s-m;
    AUTO_HESS<T,TV> wd=abs(X2)-w;
    if(wd.x<0) return max(sd,wd).ddx;
    if(sd.x<0) return wd.ddx;
    return hypot(wd,sd).ddx;
}
//#####################################################################
// Function Lazy_Inside
//#####################################################################
template<class T> bool SMOOTH_GEAR<VECTOR<T,3> >::
Lazy_Inside(const TV& X) const
{
    return Signed_Distance(X)<0;
}
//#####################################################################
// Function Lazy_Outside
//#####################################################################
template<class T> bool SMOOTH_GEAR<VECTOR<T,3> >::
Lazy_Outside(const TV& X) const
{
    return !Lazy_Inside(X);
}
//#####################################################################
// Function Inside
//#####################################################################
template<class T> bool SMOOTH_GEAR<VECTOR<T,3> >::
Inside(const TV& X,const T thickness_over_two) const
{
    return Signed_Distance(X)<=-thickness_over_two;
}
//#####################################################################
// Function Outside
//#####################################################################
template<class T> bool SMOOTH_GEAR<VECTOR<T,3> >::
Outside(const TV& X,const T thickness_over_two) const
{
    return Signed_Distance(X)>=thickness_over_two;
}
//#####################################################################
// Function Boundary
//#####################################################################
template<class T> bool SMOOTH_GEAR<VECTOR<T,3> >::
Boundary(const TV& X,const T thickness_over_two) const
{
    T sd=Signed_Distance(X);
    return (sd<thickness_over_two && sd>-thickness_over_two);
}
//#####################################################################
// Function Name
//#####################################################################
template<class T> std::string SMOOTH_GEAR<VECTOR<T,3> >::
Name()
{
    return "SMOOTH_GEAR<VECTOR<T,3> >";
}
//#####################################################################
// Function Read
//#####################################################################
template<class T> template<class RW> void SMOOTH_GEAR<VECTOR<T,2> >::
Read(UNTYPED_ISTREAM<RW>& input)
{
    Read_Binary_Group(input,r,s,n);
    Compute_Centers();
}
//#####################################################################
// Function Write
//#####################################################################
template<class T> template<class RW> void SMOOTH_GEAR<VECTOR<T,2> >::
Write(UNTYPED_OSTREAM<RW>& output) const
{
    Write_Binary_Group(output,r,s,n);
}
//#####################################################################
// Function Read
//#####################################################################
template<class T> template<class RW> void SMOOTH_GEAR<VECTOR<T,3> >::
Read(UNTYPED_ISTREAM<RW>& input)
{
    Read_Binary_Group(input,w,g);
}
//#####################################################################
// Function Write
//#####################################################################
template<class T> template<class RW> void SMOOTH_GEAR<VECTOR<T,3> >::
Write(UNTYPED_OSTREAM<RW>& output) const
{
    Write_Binary_Group(output,w,g);
}
//#####################################################################
// Function Generate_Triangles
//#####################################################################
template<class T> void Boundary_Points(ARRAY<VECTOR<T,2> >& pts,const SMOOTH_GEAR<VECTOR<T,2> >& gear,int n)
{
    typedef VECTOR<T,2> TV;
    TV dc=gear.Co-gear.Ci;
    T hai=atan2(dc.y,dc.x),hao=hai-gear.den/2;
    T cs=cos(gear.den),sn=sin(gear.den);
    MATRIX<T,2> R(cs,sn,-sn,cs);
    int i=0,m=(int)ceil(n*hai/(hai+hao));
    T ds=2*(hai+hao)/n;
    for(;i<m;i++){T a=i*ds-hai;pts.Append(gear.Ci+gear.s*TV(cos(a),sin(a)));}
    for(;i<n;i++){T a=pi+gear.den/2-(i*ds-2*hai-hao);pts.Append(gear.Co+gear.s*TV(cos(a),sin(a)));}
    for(int j=0;j<(gear.n-1)*n;j++) pts.Append(R*pts(j));
}
//#####################################################################
// Function Generate_Triangles
//#####################################################################
template<class T> TRIANGULATED_SURFACE<T>*
Generate_Triangles(const SMOOTH_GEAR<VECTOR<T,3> >& gear,int n)
{
    typedef VECTOR<T,3> TV;
    TRIANGULATED_SURFACE<T>* surface=TRIANGULATED_SURFACE<T>::Create();
    typedef VECTOR<int,3> E;
    ARRAY<VECTOR<T,2> > pts;
    Boundary_Points(pts, gear.g, n);
    GEOMETRY_PARTICLES<TV>& particles=surface->particles;
    particles.Add_Elements(pts.m*2+2);
    for(int i=0;i<pts.m;i++){
        particles.X(i)=pts(i).Append(-gear.w);
        particles.X(i+pts.m)=pts(i).Append(gear.w);}
    particles.X(2*pts.m+0)=TV(0,0,-gear.w);
    particles.X(2*pts.m+1)=TV(0,0,gear.w);
    for(int i=0;i<pts.m-1;i++){
        surface->mesh.elements.Append(E(i,i+1,2*pts.m+1));
        surface->mesh.elements.Append(E(pts.m+i+1,pts.m+i,2*pts.m+2));
        surface->mesh.elements.Append(E(i+1,i,pts.m+i+1));
        surface->mesh.elements.Append(E(pts.m+i+1,i,pts.m+i));}
    surface->mesh.elements.Append(E(pts.m,1,2*pts.m+1));
    surface->mesh.elements.Append(E(2*pts.m,pts.m+1,2*pts.m+2));
    surface->mesh.elements.Append(E(1,pts.m,2*pts.m));
    surface->mesh.elements.Append(E(2*pts.m,pts.m,pts.m+1));
    surface->Update_Number_Nodes();
    return surface;
}
//#####################################################################
// Function Generate_Triangles
//#####################################################################
template<class T> TRIANGULATED_AREA<T>*
Generate_Triangles(const SMOOTH_GEAR<VECTOR<T,2> >& gear,int n)
{
    typedef VECTOR<T,2> TV;
    typedef VECTOR<int,3> E;
    TRIANGULATED_AREA<T>* area=TRIANGULATED_AREA<T>::Create();
    ARRAY<VECTOR<T,2> > pts;
    Boundary_Points(pts, gear, n);
    GEOMETRY_PARTICLES<TV>& particles=area->particles;particles.Add_Elements(pts.m);
    area->particles.X=pts;
    particles.Add_Element();
    area->particles.X.Last()=TV();
    for(int i=0;i<pts.m-1;i++) area->mesh.elements.Append(E(i,i+1,area->particles.X.m));
    area->mesh.elements.Append(E(pts.m,1,area->particles.X.m));
    area->Update_Number_Nodes();
    return area;
}
//#####################################################################
// Function Tessellate_Boundary
//#####################################################################
template<class T> SEGMENTED_CURVE<VECTOR<T,2> >*
Tessellate_Boundary(const SMOOTH_GEAR<VECTOR<T,2> >& gear,int n)
{
    typedef VECTOR<T,2> TV;
    SEGMENTED_CURVE<TV>* curve=SEGMENTED_CURVE<TV>::Create();
    ARRAY<VECTOR<T,2> > pts;
    Boundary_Points(pts, gear, n);
    curve->particles.Add_Elements(pts.m);
    curve->particles.X=pts;
    for(int i=0;i<pts.m-1;i++) curve->mesh.elements.Append(VECTOR<int,2>(i,i+1));
    curve->mesh.elements.Append(VECTOR<int,2>(pts.m-1,0));
    curve->Update_Number_Nodes();
    return curve;
}
//#####################################################################
// Function Scaled
//#####################################################################
template<class T,class TV> TRANSFORMED_OBJECT<TV,SMOOTH_GEAR<TV> >
Scaled(const SMOOTH_GEAR<TV>& b,const VECTOR<T,2>& center,T scale)
{
    return {{b.r*scale,b.s*scale,b.n},FRAME<TV>(center*(1-scale))};
}
//#####################################################################
// Function Scaled
//#####################################################################
template<class T,class TV> TRANSFORMED_OBJECT<TV,SMOOTH_GEAR<TV> >
Scaled(const SMOOTH_GEAR<TV>& b,const VECTOR<T,3>& center,T scale)
{
    return {{b.g.r*scale,b.g.s*scale,b.g.n,b.w*scale},FRAME<TV>(center*(1-scale))};
}
//#####################################################################
// Function Intersects
//#####################################################################
template<class T> RAY_HIT<T>
Intersects(const RAY<VECTOR<T,2> >& ray,const SMOOTH_GEAR<VECTOR<T,2> >& gear,T isocontour)
{
    PHYSBAM_NOT_IMPLEMENTED();
}
//#####################################################################
// Function Intersection_Interval
//#####################################################################
template<class T> VECTOR<RAY_HIT<T>,2>
Intersection_Interval(const RAY<VECTOR<T,2> >& ray,const SMOOTH_GEAR<VECTOR<T,2> >& gear,T isocontour)
{
    PHYSBAM_NOT_IMPLEMENTED();
}
//#####################################################################
// Function All_Intersections
//#####################################################################
template<class TV,class T> void
All_Intersections(ARRAY<RAY_HIT<T> >& hits,const RAY<TV>& ray,const SMOOTH_GEAR<VECTOR<T,2> >& gear,const T isocontour)
{
    PHYSBAM_NOT_IMPLEMENTED();
}
//#####################################################################
// Function Intersects
//#####################################################################
template<class T> RAY_HIT<T>
Intersects(const RAY<VECTOR<T,3> >& ray,const SMOOTH_GEAR<VECTOR<T,3> >& gear,T isocontour)
{
    PHYSBAM_NOT_IMPLEMENTED();
}
//#####################################################################
// Function All_Intersections
//#####################################################################
template<class TV,class T> void
All_Intersections(ARRAY<RAY_HIT<T> >& hits,const RAY<TV>& ray,const SMOOTH_GEAR<VECTOR<T,3> >& gear,const T isocontour)
{
    PHYSBAM_NOT_IMPLEMENTED();
}
template class SMOOTH_GEAR<VECTOR<float,2> >;
template class SMOOTH_GEAR<VECTOR<float,3> >;
template class SMOOTH_GEAR<VECTOR<double,2> >;
template class SMOOTH_GEAR<VECTOR<double,3> >;
template TRIANGULATED_SURFACE<float>* Generate_Triangles(const SMOOTH_GEAR<VECTOR<float,3> >&,int);
template TRIANGULATED_AREA<float>* Generate_Triangles(const SMOOTH_GEAR<VECTOR<float,2> >&,int);
template SEGMENTED_CURVE<VECTOR<float,2> >* Tessellate_Boundary(const SMOOTH_GEAR<VECTOR<float,2> >& gear,int levels);
template TRIANGULATED_SURFACE<double>* Generate_Triangles(const SMOOTH_GEAR<VECTOR<double,3> >&,int);
template TRIANGULATED_AREA<double>* Generate_Triangles(const SMOOTH_GEAR<VECTOR<double,2> >&,int);
template SEGMENTED_CURVE<VECTOR<double,2> >* Tessellate_Boundary(const SMOOTH_GEAR<VECTOR<double,2> >& gear,int levels);
template TRANSFORMED_OBJECT<VECTOR<double,2>,SMOOTH_GEAR<VECTOR<double,2> > > Scaled<double,VECTOR<double,2> >(SMOOTH_GEAR<VECTOR<double,2> > const&,VECTOR<double,2> const&,double);
template TRANSFORMED_OBJECT<VECTOR<double,3>,SMOOTH_GEAR<VECTOR<double,3> > > Scaled<double,VECTOR<double,3> >(SMOOTH_GEAR<VECTOR<double,3> > const&,VECTOR<double,3> const&,double);
template TRANSFORMED_OBJECT<VECTOR<float,2>,SMOOTH_GEAR<VECTOR<float,2> > > Scaled<float,VECTOR<float,2> >(SMOOTH_GEAR<VECTOR<float,2> > const&,VECTOR<float,2> const&,float);
template TRANSFORMED_OBJECT<VECTOR<float,3>,SMOOTH_GEAR<VECTOR<float,3> > > Scaled<float,VECTOR<float,3> >(SMOOTH_GEAR<VECTOR<float,3> > const&,VECTOR<float,3> const&,float);
template RAY_HIT<double> Intersects<double>(RAY<VECTOR<double,2> > const&,SMOOTH_GEAR<VECTOR<double,2> > const&,double);
template RAY_HIT<double> Intersects<double>(RAY<VECTOR<double,3> > const&,SMOOTH_GEAR<VECTOR<double,3> > const&,double);
template RAY_HIT<float> Intersects<float>(RAY<VECTOR<float,2> > const&,SMOOTH_GEAR<VECTOR<float,2> > const&,float);
template RAY_HIT<float> Intersects<float>(RAY<VECTOR<float,3> > const&,SMOOTH_GEAR<VECTOR<float,3> > const&,float);
template void All_Intersections<VECTOR<double,2>,double>(ARRAY<RAY_HIT<double>,int>&,RAY<VECTOR<double,2> > const&,SMOOTH_GEAR<VECTOR<double,2> > const&,double);
template void All_Intersections<VECTOR<double,3>,double>(ARRAY<RAY_HIT<double>,int>&,RAY<VECTOR<double,3> > const&,SMOOTH_GEAR<VECTOR<double,3> > const&,double);
template void All_Intersections<VECTOR<float,2>,float>(ARRAY<RAY_HIT<float>,int>&,RAY<VECTOR<float,2> > const&,SMOOTH_GEAR<VECTOR<float,2> > const&,float);
template void All_Intersections<VECTOR<float,3>,float>(ARRAY<RAY_HIT<float>,int>&,RAY<VECTOR<float,3> > const&,SMOOTH_GEAR<VECTOR<float,3> > const&,float);
template void SMOOTH_GEAR<VECTOR<double,2> >::Read(UNTYPED_ISTREAM<char>&);
template void SMOOTH_GEAR<VECTOR<double,2> >::Read(UNTYPED_ISTREAM<double>&);
template void SMOOTH_GEAR<VECTOR<double,2> >::Read(UNTYPED_ISTREAM<float>&);
template void SMOOTH_GEAR<VECTOR<double,2> >::Write(UNTYPED_OSTREAM<char>&) const;
template void SMOOTH_GEAR<VECTOR<double,2> >::Write(UNTYPED_OSTREAM<double>&) const;
template void SMOOTH_GEAR<VECTOR<double,2> >::Write(UNTYPED_OSTREAM<float>&) const;
template void SMOOTH_GEAR<VECTOR<double,3> >::Read(UNTYPED_ISTREAM<char>&);
template void SMOOTH_GEAR<VECTOR<double,3> >::Read(UNTYPED_ISTREAM<double>&);
template void SMOOTH_GEAR<VECTOR<double,3> >::Read(UNTYPED_ISTREAM<float>&);
template void SMOOTH_GEAR<VECTOR<double,3> >::Write(UNTYPED_OSTREAM<char>&) const;
template void SMOOTH_GEAR<VECTOR<double,3> >::Write(UNTYPED_OSTREAM<double>&) const;
template void SMOOTH_GEAR<VECTOR<double,3> >::Write(UNTYPED_OSTREAM<float>&) const;
template void SMOOTH_GEAR<VECTOR<float,2> >::Read(UNTYPED_ISTREAM<char>&);
template void SMOOTH_GEAR<VECTOR<float,2> >::Read(UNTYPED_ISTREAM<double>&);
template void SMOOTH_GEAR<VECTOR<float,2> >::Read(UNTYPED_ISTREAM<float>&);
template void SMOOTH_GEAR<VECTOR<float,2> >::Write(UNTYPED_OSTREAM<char>&) const;
template void SMOOTH_GEAR<VECTOR<float,2> >::Write(UNTYPED_OSTREAM<double>&) const;
template void SMOOTH_GEAR<VECTOR<float,2> >::Write(UNTYPED_OSTREAM<float>&) const;
template void SMOOTH_GEAR<VECTOR<float,3> >::Read(UNTYPED_ISTREAM<char>&);
template void SMOOTH_GEAR<VECTOR<float,3> >::Read(UNTYPED_ISTREAM<double>&);
template void SMOOTH_GEAR<VECTOR<float,3> >::Read(UNTYPED_ISTREAM<float>&);
template void SMOOTH_GEAR<VECTOR<float,3> >::Write(UNTYPED_OSTREAM<char>&) const;
template void SMOOTH_GEAR<VECTOR<float,3> >::Write(UNTYPED_OSTREAM<double>&) const;
template void SMOOTH_GEAR<VECTOR<float,3> >::Write(UNTYPED_OSTREAM<float>&) const;
}
