real angle(transform t)
{
 pair z=(2t.xx*t.yy,t.yx*t.yy-t.xx*t.xy);
 if(t.xx < 0 || t.yy < 0) z=-z;
 return degrees(z,warn=false);
}

transform rotation(transform t)
{
 return rotate(angle(t));
}

transform scaleless(transform t)
{
 real a=t.xx, b=t.xy, c=t.yx, d=t.yy;
 real arg=(a-d)^2+4b*c;
 pair delta=arg >= 0 ? sqrt(arg) : I*sqrt(-arg);
 real trace=a+d;
 pair l1=0.5(trace+delta);
 pair l2=0.5(trace-delta);

 if(abs(delta) < sqrtEpsilon*max(abs(l1),abs(l2))) {
   real s=abs(0.5trace);
   return (s != 0) ? scale(1/s)*t : t;
 }

 if(abs(l1-d) < abs(l2-d)) {pair temp=l1; l1=l2; l2=temp;}

 pair dot(pair[] u, pair[] v) {return conj(u[0])*v[0]+conj(u[1])*v[1];}

 pair[] unit(pair[] u) {
   real norm2=abs(u[0])^2+abs(u[1])^2;
   return norm2 != 0 ? u/sqrt(norm2) : u;
 }

 pair[] u={l1-d,b};
 pair[] v={c,l2-a};
 u=unit(u);
 pair d=dot(u,u);
 if(d != 0) v -= dot(u,v)/d*u;
 v=unit(v);

 pair[][] U={{u[0],v[0]},{u[1],v[1]}};
 pair[][] A={{a,b},{c,d}};

 pair[][] operator *(pair[][] a, pair[][] b) {
   pair[][] c=new pair[2][2];
   for(int i=0; i < 2; ++i) {
     for(int j=0; j < 2; ++j) {
       c[i][j]=a[i][0]*b[0][j]+a[i][1]*b[1][j];
     }
   }
   return c;
 }

 pair[][] conj(pair[][] a) {
   pair[][] c=new pair[2][2];
   for(int i=0; i < 2; ++i) {
     for(int j=0; j < 2; ++j) {
       c[i][j]=conj(a[j][i]);
     }
   }
   return c;
 }

 A=conj(U)*A*U;

 real D=abs(A[0][0]);
 if(D != 0) {
   A[0][0] /= D;
   A[0][1] /= D;
 }

 D=abs(A[1][1]);
 if(D != 0) {
   A[1][0] /= D;
   A[1][1] /= D;
 }

 A=U*A*conj(U);

 return (0,0,A[0][0].x,A[0][1].x,A[1][0].x,A[1][1].x);
}

struct align {
 pair dir;
 triple dir3;
 bool relative=false;
 bool default=true;
 bool is3D=false;
 void init(pair dir=0, bool relative=false, bool default=false) {
   this.dir=dir;
   this.relative=relative;
   this.default=default;
   is3D=false;
 }
 void init(triple dir=(0,0,0), bool relative=false, bool default=false) {
   this.dir3=dir;
   this.relative=relative;
   this.default=default;
   is3D=true;
 }
 align copy() {
   align align=new align;
   align.init(dir,relative,default);
   align.dir3=dir3;
   align.is3D=is3D;
   return align;
 }
 void align(align align) {
   if(!align.default) {
     bool is3D=align.is3D;
     init(align.dir,align.relative);
     dir3=align.dir3;
     this.is3D=is3D;
   }
 }
 void align(align align, align default) {
   align(align);
   if(this.default) {
     init(default.dir,default.relative,default.default);
     dir3=default.dir3;
     is3D=default.is3D;
   }
 }
 void write(file file=stdout, suffix suffix=endl) {
   if(!default) {
     if(relative) {
       write(file,"Relative(");
       if(is3D)
         write(file,dir3);
       else
         write(file,dir);
       write(file,")",suffix);
     } else {
       if(is3D)
         write(file,dir3,suffix);
       else
         write(file,dir,suffix);
     }
   }
 }
 bool Center() {
   return relative && (is3D ? dir3 == (0,0,0) : dir == 0);
 }
}

struct side {
 pair align;
}

side Relative(explicit pair align)
{
 side s;
 s.align=align;
 return s;
}

restricted side NoSide;
restricted side LeftSide=Relative(W);
restricted side Center=Relative((0,0));
restricted side RightSide=Relative(E);

side operator * (real x, side s)
{
 side S;
 S.align=x*s.align;
 return S;
}

align operator cast(pair dir) {align A; A.init(dir,false); return A;}
align operator cast(triple dir) {align A; A.init(dir,false); return A;}
align operator cast(side side) {align A; A.init(side.align,true); return A;}
restricted align NoAlign;

void write(file file=stdout, align align, suffix suffix=endl)
{
 align.write(file,suffix);
}

struct position {
 pair position;
 bool relative;
}

position Relative(real position)
{
 position p;
 p.position=position;
 p.relative=true;
 return p;
}

restricted position BeginPoint=Relative(0);
restricted position MidPoint=Relative(0.5);
restricted position EndPoint=Relative(1);

position operator cast(pair x) {position P; P.position=x; return P;}
position operator cast(real x) {return (pair) x;}
position operator cast(int x) {return (pair) x;}

pair operator cast(position P) {return P.position;}

using embed=transform(transform);
transform Shift(transform t) {return identity();}
transform Rotate(transform t) {return rotation(t);}
transform Slant(transform t) {return scaleless(t);}
transform Scale(transform t) {return t;}

embed Rotate(pair z) {
 return new transform(transform t) {return rotate(degrees(shiftless(t)*z,
                                                          warn=false));};
}

path[] texpath(string s, pen p, bool tex=settings.tex != "none",
              bool bbox=false);

struct Label {
 string s,size;
 position position;
 bool defaultposition=true;
 align align;
 pen p=nullpen;
 transform T;
 transform3 T3=identity(4);
 bool defaulttransform=true;
 bool defaulttransform3=true;
 embed embed=Rotate; // Shift, Rotate, Slant, or Scale with embedded picture
 filltype filltype=NoFill;

 void init(string s="", string size="", position position=0,
           bool defaultposition=true, align align=NoAlign, pen p=nullpen,
           transform T=identity(), transform3 T3=identity4,
           bool defaulttransform=true, bool defaulttransform3=true,
           embed embed=Rotate, filltype filltype=NoFill) {
   this.s=s;
   this.size=size;
   this.position=position;
   this.defaultposition=defaultposition;
   this.align=align.copy();
   this.p=p;
   this.T=T;
   this.T3=copy(T3);
   this.defaulttransform=defaulttransform;
   this.defaulttransform3=defaulttransform3;
   this.embed=embed;
   this.filltype=filltype;
 }

 void initalign(string s="", string size="", align align, pen p=nullpen,
                embed embed=Rotate, filltype filltype=NoFill) {
   init(s,size,align,p,embed,filltype);
 }

 void transform(transform T) {
   this.T=T;
   defaulttransform=false;
 }

 void transform3(transform3 T) {
   this.T3=copy(T);
   defaulttransform3=false;
 }

 Label copy(transform3 T3=this.T3) {
   Label L=new Label;
   L.init(s,size,position,defaultposition,align,p,T,T3,defaulttransform,
          defaulttransform3,embed,filltype);
   return L;
 }

 void position(position pos) {
   this.position=pos;
   defaultposition=false;
 }

 void align(align a) {
   align.align(a);
 }
 void align(align a, align default) {
   align.align(a,default);
 }

 void p(pen p0) {
   if(this.p == nullpen) this.p=p0;
 }

 void filltype(filltype filltype0) {
   if(this.filltype == NoFill) this.filltype=filltype0;
 }

 void label(frame f, transform t=identity(), pair position, pair align) {
   pen p0=p == nullpen ? currentpen : p;
   align=length(align)*unit(rotation(t)*align);
   pair S=t*position+align*labelmargin(p0)+shift(T)*0;
   if(settings.tex != "none")
     label(f,s,size,embed(t)*shiftless(T),S,align,p0);
   else
     fill(f,align(texpath(s,p0),S,align,p0),p0);
 }

 void out(frame f, transform t=identity(), pair position=position.position,
          pair align=align.dir) {
   if(filltype == NoFill)
     label(f,t,position,align);
   else {
     frame d;
     label(d,t,position,align);
     add(f,d,filltype);
   }
 }

 void label(picture pic=currentpicture, pair position, pair align) {
   if(s == "") return;
   pic.add(new void (frame f, transform t) {
       out(f,t,position,align);
     },true);
   frame f;
   // Create a picture with label at the origin to extract its bbox truesize.
   label(f,(0,0),align);
   pic.addBox(position,position,min(f),max(f));
 }

 void out(picture pic=currentpicture) {
   label(pic,position.position,align.dir);
 }

 void out(picture pic=currentpicture, path g) {
   bool relative=position.relative;
   real position=position.position.x;
   pair Align=align.dir;
   bool alignrelative=align.relative;
   if(defaultposition) {relative=true; position=0.5;}
   if(relative) position=reltime(g,position);
   if(align.default) {
     alignrelative=true;
     Align=position <= sqrtEpsilon ? S :
       position >= length(g)-sqrtEpsilon ? N : E;
   }

   pic.add(new void (frame f, transform t) {
       out(f,t,point(g,position),alignrelative ?
           inverse(rotation(t))*-Align*dir(t*g,position)*I : Align);
     },!alignrelative);

   frame f;
   pair align=alignrelative ? -Align*dir(g,position)*I : Align;
   label(f,(0,0),align);
   pair position=point(g,position);
   pic.addBox(position,position,min(f),max(f));
 }

 void write(file file=stdout, suffix suffix=endl) {
   write(file,"\""+s+"\"");
   if(!defaultposition) write(file,", position=",position.position);
   if(!align.default) write(file,", align=");
   write(file,align);
   if(p != nullpen) write(file,", pen=",p);
   if(!defaulttransform)
     write(file,", transform=",T);
   if(!defaulttransform3) {
     write(file,", transform3=",endl);
     write(file,T3);
   }
   write(file,"",suffix);
 }

 real relative() {
   return defaultposition ? 0.5 : position.position.x;
 };

 real relative(path g) {
   return position.relative ? reltime(g,relative()) : relative();
 };
}

Label Label;

void add(frame f, transform t=identity(), Label L)
{
 L.out(f,t);
}

void add(picture pic=currentpicture, Label L)
{
 L.out(pic);
}

Label operator * (transform t, Label L)
{
 Label tL=L.copy();
 tL.align.dir=L.align.dir;
 tL.transform(t*L.T);
 return tL;
}

Label operator * (transform3 t, Label L)
{
 Label tL=L.copy(t*L.T3);
 tL.align.dir=L.align.dir;
 tL.defaulttransform3=false;
 return tL;
}

Label Label(string s, string size="", explicit position position,
           align align=NoAlign, pen p=nullpen, embed embed=Rotate,
           filltype filltype=NoFill)
{
 Label L;
 L.init(s,size,position,false,align,p,embed,filltype);
 return L;
}

Label Label(string s, string size="", pair position, align align=NoAlign,
           pen p=nullpen, embed embed=Rotate, filltype filltype=NoFill)
{
 return Label(s,size,(position) position,align,p,embed,filltype);
}

Label Label(explicit pair position, align align=NoAlign, pen p=nullpen,
           embed embed=Rotate, filltype filltype=NoFill)
{
 return Label((string) position,position,align,p,embed,filltype);
}

Label Label(string s="", string size="", align align=NoAlign, pen p=nullpen,
           embed embed=Rotate, filltype filltype=NoFill)
{
 Label L;
 L.initalign(s,size,align,p,embed,filltype);
 return L;
}

Label Label(Label L, align align=NoAlign, pen p=nullpen, embed embed=L.embed,
           filltype filltype=NoFill)
{
 Label L=L.copy();
 L.align(align);
 L.p(p);
 L.embed=embed;
 L.filltype(filltype);
 return L;
}

Label Label(Label L, explicit position position, align align=NoAlign,
           pen p=nullpen, embed embed=L.embed, filltype filltype=NoFill)
{
 Label L=Label(L,align,p,embed,filltype);
 L.position(position);
 return L;
}

Label Label(Label L, pair position, align align=NoAlign,
           pen p=nullpen, embed embed=L.embed, filltype filltype=NoFill)
{
 return Label(L,(position) position,align,p,embed,filltype);
}

void write(file file=stdout, Label L, suffix suffix=endl)
{
 L.write(file,suffix);
}

void label(frame f, Label L, pair position, align align=NoAlign,
          pen p=currentpen, filltype filltype=NoFill)
{
 add(f,Label(L,position,align,p,filltype));
}

void label(frame f, Label L, align align=NoAlign,
          pen p=currentpen, filltype filltype=NoFill)
{
 add(f,Label(L,L.position,align,p,filltype));
}

void label(picture pic=currentpicture, Label L, pair position,
          align align=NoAlign, pen p=currentpen, filltype filltype=NoFill)
{
 Label L=Label(L,position,align,p,filltype);
 add(pic,L);
}

void label(picture pic=currentpicture, Label L, align align=NoAlign,
          pen p=currentpen, filltype filltype=NoFill)
{
 label(pic,L,L.position,align,p,filltype);
}

// Label, but with postscript coords instead of asy
void label(pair origin, picture pic=currentpicture, Label L, align align=NoAlign,
          pen p=currentpen, filltype filltype=NoFill)
{
 picture opic;
 label(opic,L,L.position,align,p,filltype);
 add(pic,opic,origin);
}

void label(picture pic=currentpicture, Label L, explicit path g,
          align align=NoAlign, pen p=currentpen, filltype filltype=NoFill)
{
 Label L=Label(L,align,p,filltype);
 L.out(pic,g);
}

void label(picture pic=currentpicture, Label L, explicit guide g,
          align align=NoAlign, pen p=currentpen, filltype filltype=NoFill)
{
 label(pic,L,(path) g,align,p,filltype);
}

Label operator cast(string s) {return Label(s);}

// A structure that a string, Label, or frame can be cast to.
struct object {
 frame f;
 Label L=Label;
 path g; // Bounding path

 void operator init(frame f) {
   this.f=f;
   g=box(min(f),max(f));
 }

 void operator init(Label L) {
   this.L=L.copy();
   if(L != Label) L.out(f);
   g=box(min(f),max(f));
 }
}

object operator cast(frame f) {
 return object(f);
}

object operator cast(Label L)
{
 return object(L);
}

object operator cast(string s)
{
 return object(s);
}

Label operator cast(object F)
{
 return F.L;
}

frame operator cast(object F)
{
 return F.f;
}

object operator * (transform t, explicit object F)
{
 object f;
 f.f=t*F.f;
 f.L=t*F.L;
 f.g=t*F.g;
 return f;
}

// Returns a copy of object F aligned in the direction align
object align(object F, pair align)
{
 return shift(F.f,align)*F;
}

void add(picture dest=currentpicture, object F, pair position=0,
        bool group=true, filltype filltype=NoFill, bool above=true)
{
 add(dest,F.f,position,group,filltype,above);
}

// Pack a list of objects into a frame.
frame pack(pair align=2S ... object inset[])
{
 frame F;
 int n=inset.length;
 pair z;
 for (int i=0; i < n; ++i) {
   add(F,inset[i].f,z);
   z += align+realmult(unit(align),size(inset[i].f));
 }
 return F;
}

path[] texpath(Label L, bool tex=settings.tex != "none", bool bbox=false)
{
 struct stringfont
 {
   string s;
   real fontsize;
   string font;

   void operator init(Label L)
   {
     s=replace(L.s,'\n',' ');
     fontsize=fontsize(L.p);
     font=font(L.p);
   }

   pen pen() {return fontsize(fontsize)+fontcommand(font);}
 }

 bool lexorder(stringfont a, stringfont b) {
   return a.s < b.s || (a.s == b.s && (a.fontsize < b.fontsize ||
                                       (a.fontsize == b.fontsize &&
                                        a.font < b.font)));
 }

 static stringfont[] stringcache;
 static path[][] pathcache;

 static stringfont[] stringlist;
 static bool adjust[];

 path[] G;

 stringfont s=stringfont(L);
 pen p=s.pen();

 int i=search(stringcache,s,lexorder);
 if(i == -1 || lexorder(stringcache[i],s)) {
   int k=search(stringlist,s,lexorder);
   if(k == -1 || lexorder(stringlist[k],s)) {
     ++k;
     stringlist.insert(k,s);
     // PDF tex engines lose track of the baseline.
     adjust.insert(k,tex && basealign(L.p) == 1 && pdf());
   }
 }

 path[] transform(path[] g, Label L) {
   if(g.length == 0) return g;
   pair m=min(g);
   pair M=max(g);
   pair dir=rectify(inverse(L.T)*-L.align.dir);
   if(tex && basealign(L.p) == 1)
     dir -= (0,(1-dir.y)*m.y/(M.y-m.y));
   pair a=m+realmult(dir,M-m);

   return shift(L.position+L.align.dir*labelmargin(L.p))*L.T*shift(-a)*g;
 }

 if(tex && bbox) {
   frame f;
   label(f,L);
   return transform(box(min(f),max(f)),L);
 }

 if(stringlist.length > 0) {
   path[][] g;
   int n=stringlist.length;
   string[] s=new string[n];
   pen[] p=new pen[n];
   for(int i=0; i < n; ++i) {
     stringfont S=stringlist[i];
     s[i]=adjust[i] ? "."+S.s : S.s;
     p[i]=adjust[i] ? S.pen()+basealign : S.pen();
   }

   g=tex ? _texpath(s,p) : textpath(s,p);

   if(tex)
     for(int i=0; i < n; ++i)
       if(adjust[i]) {
         real y=min(g[i][0]).y;
         g[i].delete(0);
         g[i]=shift(0,-y)*g[i];
       }


   for(int i=0; i < stringlist.length; ++i) {
     stringfont s=stringlist[i];
     int j=search(stringcache,s,lexorder)+1;
     stringcache.insert(j,s);
     pathcache.insert(j,g[i]);
   }
   stringlist.delete();
   adjust.delete();
 }

 return transform(pathcache[search(stringcache,stringfont(L),lexorder)],L);
}

texpath=new path[](string s, pen p, bool tex=settings.tex != "none", bool bbox=false)
 {
  return texpath(Label(s,p));
 };