%%%
% Pyramide de calculs
%%%
\def\filedatePyraCalc{2024/08/04}%
\def\fileversionPyraCalc{0.1}%
\message{-- \filedatePyraCalc\space v\fileversionPyraCalc}%
%
\newcommand\DessinePyramideNombreMul[1]{%
  \ifluatex
  \mplibforcehmode
  \begin{mplibcode}
    pair A[],B[],O;
    cote:=\useKV[ClesPyramide]{Cote};
    boolean Aide,Produit,Solution,Vide;
    Aide:=\useKV[ClesPyramide]{Aide};
    Vide:=\useKV[ClesPyramide]{Vide};
    Produit:=\useKV[ClesPyramide]{Produit};
    Solution:=\useKV[ClesPyramide]{Solution};
    A1=u*(1,1);
    A2-A1=cote*(1,0);
    A3=rotation(A2,A1,60);
    A4=A1;
    B1=iso(A1,A2);
    B2=iso(A2,A3);
    B3=iso(A3,A1);
    O=iso(A1,A2,A3);
    path BoiteRec;
    BoiteRec=((-0.4,-0.5)--(0.4,-0.5){dir0}..{dir90}(0.5,-0.4)--(0.5,0.4){dir90}..{dir180}(0.4,0.5)--(-0.4,0.5){dir180}..{dir-90}(-0.5,0.4)--(-0.5,-0.4){dir-90}..cycle) scaled 1u;
    % On trace et on affiche...éventuellement :)
    trace polygone(A1,A2,A3);
    for k=1 upto 3:
    trace segment(O,B[k]) dashed withdots scaled 0.25;
    draw BoiteRec shifted (1.75[O,B[k]]-center BoiteRec) dashed evenly scaled 0.5;
    endfor;
    if Vide:
    else:
    % On récupère les valeurs pour déterminer les produits
    k=0;
    for p_=#1:
    k:=k+1;
    Facteur[k]=p_;
    endfor;
    Produit[1]=Facteur[1] * Facteur[2];
    Produit[2]=Facteur[2] * Facteur[3];
    Produit[3]=Facteur[3] * Facteur[1];
    for k=1 upto 3:
    if Produit or Solution:
    label(TEX("\num{"&decimal(Produit[k])&"}"),(1.75[O,B[k]]-center BoiteRec));
    fi;
    if Produit:
    else:
    label(TEX("\num{"&decimal(Facteur[k])&"}"),(0.5[O,A[k]]-center BoiteRec));
    fi;
    endfor;
    fi;
    if Aide:
    for k=1 upto 3:
    drawarrow (0.5[O,A[k]]--1.75[O,B[k]]) cutbefore segment(A[k],A[k+1]) cutafter BoiteRec shifted (1.75[O,B[k]]-center BoiteRec) dashed evenly scaled 0.5;
    drawarrow (0.5[O,A[k+1]]--1.75[O,B[k]]) cutbefore segment(A[k],A[k+1]) cutafter BoiteRec shifted (1.75[O,B[k]]-center BoiteRec) dashed evenly scaled 0.5;
    endfor;
    fi;
  \end{mplibcode}
  \else
  \begin{mpost}[mpsettings={cote:=\useKV[ClesPyramide]{Cote};
      boolean Aide,Produit,Solution,Vide;
      Aide:=\useKV[ClesPyramide]{Aide};
      Vide:=\useKV[ClesPyramide]{Vide};
      Produit:=\useKV[ClesPyramide]{Produit};
      Solution:=\useKV[ClesPyramide]{Solution};}]
    pair A[],B[],O;
    A1=u*(1,1);
    A2-A1=cote*(1,0);
    A3=rotation(A2,A1,60);
    A4=A1;
    B1=iso(A1,A2);
    B2=iso(A2,A3);
    B3=iso(A3,A1);
    O=iso(A1,A2,A3);
    path BoiteRec;
    BoiteRec=((-0.4,-0.5)--(0.4,-0.5){dir0}..{dir90}(0.5,-0.4)--(0.5,0.4){dir90}..{dir180}(0.4,0.5)--(-0.4,0.5){dir180}..{dir-90}(-0.5,0.4)--(-0.5,-0.4){dir-90}..cycle) scaled 1u;
    % On trace et on affiche...éventuellement :)
    trace polygone(A1,A2,A3);
    for k=1 upto 3:
    trace segment(O,B[k]) dashed withdots scaled 0.25;
    draw BoiteRec shifted (1.75[O,B[k]]-center BoiteRec) dashed evenly scaled 0.5;
    endfor;
    if Vide:
    else:
    % On récupère les valeurs pour déterminer les produits
    k=0;
    for p_=#1:
    k:=k+1;
    Facteur[k]=p_;
    endfor;
    Produit[1]=Facteur[1] * Facteur[2];
    Produit[2]=Facteur[2] * Facteur[3];
    Produit[3]=Facteur[3] * Facteur[1];
    for k=1 upto 3:
    if Produit or Solution:
    label(LATEX("\num{"&decimal(Produit[k])&"}"),(1.75[O,B[k]]-center BoiteRec));
    fi;
    if Produit:
    else:
    label(LATEX("\num{"&decimal(Facteur[k])&"}"),(0.5[O,A[k]]-center BoiteRec));
    fi;
    endfor;
    fi;
    if Aide:
    for k=1 upto 3:
    drawarrow (0.5[O,A[k]]--1.75[O,B[k]]) cutbefore segment(A[k],A[k+1]) cutafter BoiteRec shifted (1.75[O,B[k]]-center BoiteRec) dashed evenly scaled 0.5;
    drawarrow (0.5[O,A[k+1]]--1.75[O,B[k]]) cutbefore segment(A[k],A[k+1]) cutafter BoiteRec shifted (1.75[O,B[k]]-center BoiteRec) dashed evenly scaled 0.5;
    endfor;
    fi;
  \end{mpost}
  \fi
}

\newcommand\DessinePyramideNombre[1]{%
  \ifluatex
  \mplibforcehmode
  \begin{mplibcode}
    pair A[][],B[];
    path Case[];
    color CaseCouleur;
    nbetages:=\useKV[ClesPyramide]{Etages};
    largeur:=\useKV[ClesPyramide]{Largeur};
    hauteur:=\useKV[ClesPyramide]{Hauteur};
    CaseCouleur:=\useKV[ClesPyramide]{Couleur};
    boolean Double;
    Double=\useKV[ClesPyramide]{Double};
    Nbeb:=0;%Pour associer les textes avec les points. Plus facile :)
    if Double:
    for k=nbetages downto 1:
    for l=0 upto (k-1):
    Nbeb:=Nbeb+1;
    A[k][l]=(0,0)+(nbetages-k)*(largeur/2,0)+(l*largeur,(nbetages-k)*hauteur);
    B[Nbeb]=A[k][l];
    Case[Nbeb]=((unitsquare xscaled largeur) yscaled hauteur) shifted (A[k][l]-0.5*(largeur,hauteur));
    trace Case[Nbeb];
    endfor;
    endfor;
    for k=nbetages-1 downto 1:
    for l=0 upto (k-1):
    Nbeb:=Nbeb+1;
    A[-k][l]=(0,0)+(nbetages-k)*(largeur/2,0)+(l*largeur,-(nbetages-k)*hauteur);
    B[Nbeb]=A[-k][l];
    Case[Nbeb]=((unitsquare xscaled largeur) yscaled hauteur) shifted (A[-k][l]-0.5*(largeur,hauteur));
    trace Case[Nbeb];
    endfor;
    endfor;
    else:
    if \useKV[ClesPyramide]{Inverse}:
    change:=-1;
    else:
    change=1;
    fi;
    for k=nbetages downto 1:
    for l=0 upto (k-1):
    Nbeb:=Nbeb+1;
    A[k][l]=(0,0)+(nbetages-k)*(largeur/2,0)+(l*largeur,change*(nbetages-k)*hauteur);
    B[Nbeb]=A[k][l];
    Case[Nbeb]=((unitsquare xscaled largeur) yscaled hauteur) shifted (A[k][l]-0.5*(largeur,hauteur));
    trace Case[Nbeb];
    endfor;
    endfor;
    fi;
    if \useKV[ClesPyramide]{Vide}:
    else:
    Nbeb:=0;
    for p_=#1:
    Nbeb:=Nbeb+1;
    if (substring(0,1) of p_)="*":
    fill Case[Nbeb] withcolor CaseCouleur;
    trace Case[Nbeb];
    label(TEX(substring(1,length p_) of p_),B[Nbeb]);
    elseif (substring(0,1) of p_)="!":
    drawoptions(withcolor \useKV[ClesPyramide]{CouleurNombre});
    label(TEX(substring(1,length p_) of p_),B[Nbeb]);
    drawoptions();
    else:
    label(TEX(p_),B[Nbeb]);
    fi;
    endfor;
    fi;
  \end{mplibcode}
  \else
  \begin{mpost}[mpsettings={nbetages:=\useKV[ClesPyramide]{Etages};largeur:=\useKV[ClesPyramide]{Largeur};hauteur:=\useKV[ClesPyramide]{Hauteur};boolean Vide,Inverse,Double;Vide=\useKV[ClesPyramide]{Vide};Inverse=\useKV[ClesPyramide]{Inverse};Double=\useKV[ClesPyramide]{Double};color CaseCouleur;CouleurNombre; CaseCouleur:=\useKV[ClesPyramide]{Couleur};CouleurNombre=\useKV[ClesPyramide]{CouleurNombre};}]
    pair A[][],B[];
    path Case[];
    Nbeb:=0;
    if Double:
    for k=nbetages downto 1:
    for l=0 upto (k-1):
    Nbeb:=Nbeb+1;
    A[k][l]=(0,0)+(nbetages-k)*(largeur/2,0)+(l*largeur,(nbetages-k)*hauteur);
    B[Nbeb]=A[k][l];
    Case[Nbeb]=((unitsquare xscaled largeur) yscaled hauteur) shifted (A[k][l]-0.5*(largeur,hauteur));
    trace Case[Nbeb];
    endfor;
    endfor;
    for k=nbetages-1 downto 1:
    for l=0 upto (k-1):
    Nbeb:=Nbeb+1;
    A[-k][l]=(0,0)+(nbetages-k)*(largeur/2,0)+(l*largeur,-(nbetages-k)*hauteur);
    B[Nbeb]=A[-k][l];
    Case[Nbeb]=((unitsquare xscaled largeur) yscaled hauteur) shifted (A[-k][l]-0.5*(largeur,hauteur));
    trace Case[Nbeb];
    endfor;
    endfor;
    else:
    if Inverse:
    change:=-1;
    else:
    change=1;
    fi;
    for k=nbetages downto 1:
    for l=0 upto (k-1):
    Nbeb:=Nbeb+1;
    A[k][l]=(0,0)+(nbetages-k)*(largeur/2,0)+(l*largeur,change*(nbetages-k)*hauteur);
    B[Nbeb]=A[k][l];
    Case[Nbeb]=((unitsquare xscaled largeur) yscaled hauteur) shifted (A[k][l]-0.5*(largeur,hauteur));
    trace Case[Nbeb];
    endfor;
    endfor;
    fi;
    if Vide:
    else:
    Nbeb:=0;
    for p_=#1:
    Nbeb:=Nbeb+1;
    if (substring(0,1) of p_)="*":
    fill Case[Nbeb] withcolor CaseCouleur;
    trace Case[Nbeb];
    label(LATEX(substring(1,length p_) of p_),B[Nbeb]);
    elseif (substring(0,1) of p_)="!":
    drawoptions(withcolor CouleurNombre);
    label(LATEX(substring(1,length p_) of p_),B[Nbeb]);
    drawoptions();
    else:
    label(LATEX(p_),B[Nbeb]);
    fi;
    endfor;
    fi;
  \end{mpost}
  \fi
}%

\setKVdefault[ClesPyramide]{ToutesOperations=false,Etages=5,Largeur=2cm,Hauteur=1cm,Vide=false,Inverse=false,Double=false,Couleur=Crimson,Multiplication=false,CouleurNombre=blue,Produit=false,Solution=false,Aide=false,Cote=4cm}%

\newtoks\toklistecaseP%
\def\UpdatetoksPyramide#1\nil{\addtotok\toklistecaseP{"#1",}}%
\def\UpdatetoksPyramideMul#1\nil{\addtotok\toklistecaseP{#1,}}%

\NewDocumentCommand\PyramideNombre{o m}{%
  \useKVdefault[ClesPyramide]%
  \setKV[ClesPyramide]{#1}%
  \ifboolKV[ClesPyramide]{ToutesOperations}{%
    \PyramideOp[#1]{#2}%
  }{%
  \ifboolKV[ClesPyramide]{Multiplication}{%
    \ifx\bla#2\bla%
    \setKV[ClesPyramide]{Vide=true}%
    \DessinePyramideNombreMul{\the\toklistecaseP}%
    \else
    \setsepchar{,}%
    \readlist*\ListePyramide{#2}%
    \DessinePyramideNombreMul{\ListePyramide[1],\ListePyramide[2],\ListePyramide[3]}%
    \fi
  }{%
    \ifx\bla#2\bla%
    \setKV[ClesPyramide]{Vide=true}%
    \DessinePyramideNombre{\the\toklistecaseP}%
    \else%
    \setsepchar{,}%
    \readlist*\ListePyramide{#2}%
    \ifboolKV[ClesPyramide]{Double}{%
      \def\CalculNombreComposants{\fpeval{\useKV[ClesPyramide]{Etages}*\useKV[ClesPyramide]{Etages}}}%
    }{%
      \def\CalculNombreComposants{\fpeval{\useKV[ClesPyramide]{Etages}*(\useKV[ClesPyramide]{Etages}+1)/2}}%
    }%
    \xintifboolexpr{\ListePyramidelen==\CalculNombreComposants}{%
      \toklistecaseP{}%
      \foreachitem\compteur\in\ListePyramide{\expandafter\UpdatetoksPyramide\compteur\nil}%
      \DessinePyramideNombre{\the\toklistecaseP}%
    }{Le nombre d'éléments dans la liste des propositions n'est pas compatible avec le nombre d'étages choisi.}%
    \fi%
  }%
}%
}%

\setKVdefault[PyramideOp]{Rayon=5mm,Graines=false,Etages=5,Solution=false,Relatifs=false,Case=-1}
\defKV[PyramideOp]{Graine=\setKV[PyramideOp]{Graines}}
\defKV[PyramideOp]{CouleurSolution=\setKV[PyramideOp]{Solution}}

\NewDocumentCommand\PyramideOp{om}{%
  \useKVdefault[PyramideOp]%
  \setKV[PyramideOp]{#1}%
  \ifboolKV[PyramideOp]{Relatifs}{%
    \BuildPyramideOpRelatifs{#2}%
  }{%
    \BuildPyramideOp{#2}%
  }%
}%

\NewDocumentCommand\BuildPyramideOp{m}{%
  \ifluatex
    \mplibforcehmode
    \mplibnumbersystem{double}
    \begin{mplibcode}
      Etages=\useKV[PyramideOp]{Etages};
      Rayon=\useKV[PyramideOp]{Rayon};
      Case=\useKV[PyramideOp]{Case};
      
      boolean Graines,Solution,Allume[][];
      
      Graines=\useKV[PyramideOp]{Graines};
      if Graines:
      randomseed:=\useKV[PyramideOp]{Graine};
      fi;

      Solution=\useKV[PyramideOp]{Solution};
      if Solution:
      color CouleurSolution;
      CouleurSolution=\useKV[PyramideOp]{CouleurSolution};
      fi;
      
      vardef ChoixOperation(expr Nba,Nbb)=
      if (Nba<Nbb) or (Nba=Nbb):
      if Nba*Nbb>200:
      ChoixOp:=ceiling(uniformdeviate(1));
      else:
      ChoixOp:=ceiling(uniformdeviate(2));
      fi;
      else:
      if (Nba mod Nbb)<>0:
      if Nba*Nbb>200:
      ChoixAlea:=uniformdeviate(1);
      if ChoixAlea<0.5:
      ChoixOp:=1;
      else:
      ChoixOp:=3;
      fi;
      else:
      ChoixOp:=ceiling(uniformdeviate(3));
      fi;
      else:
      if Nba*Nbb>200:
      ChoixAlea:=uniformdeviate(1);
      if ChoixAlea<0.5:
      ChoixOp:=1;
      else:
      ChoixOp:=ceiling(2+uniformdeviate(2));
      fi;
      else:
      ChoixOp:=ceiling(uniformdeviate(4));
      fi;
      fi;
      fi;
      enddef;
      
      numeric Nb[][],ChoixOp[][];
      
      pair M[][],N[][];
      
      for k=0 upto Etages-1:
      for l=0 upto Etages-1-k:
      M[k][l]=Rayon*(1,1)+2*Rayon*(l,k)+k*2*Rayon*(0.5,-1+cosd(30));
      Allume[k][l]=false;
      endfor;
      endfor;
      for k=0 upto Etages-2:
      for l=0 upto Etages-2-k:
      N[k][l]=Rayon*(1,1)+(Rayon,0)+2*Rayon*(l,k)+k*2*Rayon*(0.5,-1+cosd(30));
      endfor;
      endfor;

      n:=0;
      
      for p_=#1:
      Nb[0][n]=p_;
      if n=Case:
      if n<Etages-1:
      Allume[1][n]:=true;
      else:
      Allume[1][n-1]:=true;
      fi;
      else:
      Allume[0][n]:=true;
      fi;
      n:=n+1;
      endfor;

      string Op[][];
      
      % Détermination des opérations et des valeurs à indiquer.
      for k=0 upto Etages-1:
      for l=0 upto Etages-2-k:
      ChoixOperation(Nb[k][l],Nb[k][l+1]);
      if ChoixOp=1:
      Op[k][l]="$+$";
      Nb[k+1][l]=Nb[k][l]+Nb[k][l+1];
      elseif ChoixOp=2:
      Op[k][l]="$\times$";
      Nb[k+1][l]=Nb[k][l]*Nb[k][l+1];
      elseif ChoixOp=3:
      Op[k][l]="$\_$";
      Nb[k+1][l]=Nb[k][l]-Nb[k][l+1];
      elseif ChoixOp=4:
      Op[k][l]="$\div$";
      Nb[k+1][l]=Nb[k][l]/Nb[k][l+1];
      fi;
      endfor;
      endfor;
      %Affichage
      for k=0 upto Etages-1:
      for l=0 upto Etages-1-k:
      if Allume[k][l]=false:
      if Solution:
      fill cercles(M[k][l],Rayon) withcolor CouleurSolution;
      label(decimal(Nb[k][l]),M[k][l]);
      fi;
      else:
      label(decimal(Nb[k][l]),M[k][l]);
      fi;
      trace cercles(M[k][l],Rayon);
      endfor;
      endfor;
      for k=0 upto Etages-2:
      for l=0 upto Etages-2-k:
      fill cercles(N[k][l],2mm) withcolor 0.8white;
      trace cercles(N[k][l],2mm);
      label(TEX(Op[k][l]),N[k][l]);
      endfor;
      endfor;
    \end{mplibcode}
    \mplibnumbersystem{scaled}
  \fi
}

\NewDocumentCommand\BuildPyramideOpRelatifs{m}{%
  \ifluatex
    \mplibforcehmode
    \mplibnumbersystem{double}
    \begin{mplibcode}
      Etages=\useKV[PyramideOp]{Etages};
      Rayon=\useKV[PyramideOp]{Rayon};
      Case=\useKV[PyramideOp]{Case};
      
      boolean Graines,Solution,Allume[][];
      
      Graines=\useKV[PyramideOp]{Graines};
      if Graines:
      randomseed:=\useKV[PyramideOp]{Graine};
      fi;

      Solution=\useKV[PyramideOp]{Solution};
      if Solution:
      color CouleurSolution;
      CouleurSolution=\useKV[PyramideOp]{CouleurSolution};
      fi;
      
      %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
      vardef ChoixOperation(expr Nba,Nbb)=
      if (Nba=Nbb):
      if Nba*Nbb>200:
      ChoixOp:=ceiling(uniformdeviate(1));
      else:
      ChoixOp:=ceiling(uniformdeviate(2));
      fi;
      elseif (Nba+Nbb=0):
      if abs(Nba*Nbb)>200:
      ChoixOp:=3;
      else:
      ChoixAlea:=uniformdeviate(1);
      if ChoixAlea<0.5:
      ChoixOp:=2;
      else:
      ChoixOp:=3;
      fi;
      fi;
      else:
      if (abs(Nba) mod abs(Nbb))<>0:
      if abs(Nba*Nbb)>200:
      ChoixAlea:=uniformdeviate(1);
      if ChoixAlea<0.5:
      ChoixOp:=1;
      else:
      ChoixOp:=3;
      fi;
      else:
      ChoixOp:=ceiling(uniformdeviate(3));
      fi;
      else:
      if abs(Nba*Nbb)>200:
      ChoixAlea:=uniformdeviate(1);
      if ChoixAlea<0.5:
      ChoixOp:=1;
      else:
      ChoixOp:=ceiling(2+uniformdeviate(2));
      fi;
      else:
      ChoixOp:=ceiling(uniformdeviate(4));
      fi;
      fi;
      fi;
      enddef;
      %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
      
      numeric Nb[][],ChoixOp[][];
      
      pair M[][],N[][];
      
      for k=0 upto Etages-1:
      for l=0 upto Etages-1-k:
      M[k][l]=Rayon*(1,1)+2*Rayon*(l,k)+k*2*Rayon*(0.5,-1+cosd(30));
      Allume[k][l]=false;
      endfor;
      endfor;
      for k=0 upto Etages-2:
      for l=0 upto Etages-2-k:
      N[k][l]=Rayon*(1,1)+(Rayon,0)+2*Rayon*(l,k)+k*2*Rayon*(0.5,-1+cosd(30));
      endfor;
      endfor;

      n:=0;
      
      for p_=#1:
      Nb[0][n]=p_;
      if n=Case:
      if n<Etages-1:
      Allume[1][n]:=true;
      else:
      Allume[1][n-1]:=true;
      fi;
      else:
      Allume[0][n]:=true;
      fi;
      n:=n+1;
      endfor;

      string Op[][];
      
      % Détermination des opérations et des valeurs à indiquer.
      for k=0 upto Etages-1:
      for l=0 upto Etages-2-k:
      ChoixOperation(Nb[k][l],Nb[k][l+1]);
      if ChoixOp=1:
      Op[k][l]="$+$";
      Nb[k+1][l]=Nb[k][l]+Nb[k][l+1];
      elseif ChoixOp=2:
      Op[k][l]="$\times$";
      Nb[k+1][l]=Nb[k][l]*Nb[k][l+1];
      elseif ChoixOp=3:
      Op[k][l]="$\_$";
      Nb[k+1][l]=Nb[k][l]-Nb[k][l+1];
      elseif ChoixOp=4:
      Op[k][l]="$\div$";
      Nb[k+1][l]=Nb[k][l]/Nb[k][l+1];
      fi;
      endfor;
      endfor;
      %Affichage
      for k=0 upto Etages-1:
      for l=0 upto Etages-1-k:
      if Allume[k][l]=false:
      if Solution:
      fill cercles(M[k][l],Rayon) withcolor CouleurSolution;
      label(TEX("\num{"&decimal(Nb[k][l])&"}"),M[k][l]);
      fi;
      else:
      label(TEX("\num{"&decimal(Nb[k][l])&"}"),M[k][l]);
      fi;
      trace cercles(M[k][l],Rayon);
      endfor;
      endfor;
      for k=0 upto Etages-2:
      for l=0 upto Etages-2-k:
      fill cercles(N[k][l],2mm) withcolor 0.8white;
      trace cercles(N[k][l],2mm);
      label(TEX(Op[k][l]),N[k][l]);
      endfor;
      endfor;
    \end{mplibcode}
    \mplibnumbersystem{scaled}
  \fi
}%