function [F, T, gruposseparables, materias] = formapoligonal(nombrefuncion, msist, malet, maletodo, LB, UB, K, grado, potencia)
%formapoligonal
%   F = formapoligonal(funcion)
%
%   Obtiene unas recetas en tiempo discreto con la aproximación poligonal a
%   unas funciones de producción (en tiempo discreto o continuo) 
%
%   [F, T] = formapoligonal(funcion, msist, malet, maletodo, LB, UB, K, grado)
%      funcion   archivo con la definición de las funciones de producción
%      msist     muestras sistemáticas por cada insumo
%      malet     muestras aleatorias por grupo separable
%      maletodo  muestras aleatorias para todos los insumos
%      LB        límite inferior para cada insumo (por defecto 0)
%      UB        limite superior para cada insumo (por defecto 1)
%      K         factores con formapolinomioinverso (sólo con tiempo continuo)
%      grado     grado con formapolinomioinverso (sólo con tiempo continuo)
%      F         aproximación poligonal como recetas en tiempo discreto
%      T         matriz de conversión de intensidades a insumos
%   msist y malet también pueden definirse como vectores para cada grupo
%   separable, y LB y UB para cada insumo. En Octave con funciones en
%   tiempo continuo hay que definir algún K.  
%
%   A partir de la X solución de VN para F los insumos se obtienen con X*T. 
%   En el caso en tiempo discreto los flujos netos en el tiempo se obtienen 
%   con funcion(X*T); las materias se corresponden con las filas y el
%   tiempo con las columnas. En el caso en tiempo continuo los flujos
%   actualizados se obtienen con funcion(k, X*T). 

%necesita formapolinomioinverso.m

if nargin < 2, msist = []; end
if nargin < 3, malet = []; end
if nargin < 4, maletodo = []; end
if nargin < 5, LB = []; end
if nargin < 6, UB = []; end
if nargin < 7, K = []; end
if nargin < 8, grado = []; end
if nargin < 9, potencia = []; end

v = version;                                                 %determina la versión del software
if and(exist('OCTAVE_VERSION'), str2num(v([1,3])) < 36)      %con Octave en versiones inferiores a 3.6 (nargin necesita que la función esté cargada)
   if isempty(K), numarg = 1; else numarg = 2; end               %define el caso en tiempo discreto si K no está definido
else                                                         %si se usa Matlab u Octave 3.6 o superior
   numarg = nargin(nombrefuncion);                               %define el caso en tiempo discreto por el número de argumentos
end                                                          %
                                                             %
switch numarg                                                %dependiendo de si el problema está en tiempo discreto o continuo 
case 1                                                       %si el problema está en tiempo discreto
   [F, gruposseparables] = feval(nombrefuncion, zeros(1,10000)); %obtiene la variable gruposseparables
   [materias, grado] = size(F);                                  %determina el número de materias y de pasos temporales
case 2                                                       %si el problema está en tiempo continuo
   [F, gruposseparables] = feval(nombrefuncion,1,zeros(1,10000));%obtiene la variable gruposseparables
   materias = size(F, 2);                                        %determina el número de materias   
   if isempty(K), K = 1; end                                     %si K no está definido es 1
   if size(K(:),1) == 1, K = (K/2:K/5:K*3/2); end                %si K es un escalar la aproximación se efectua en [K/2, K*3/2] con 6 puntos
   puntos = size(K, 2);                                          %determina el número de factores para los que se hace la aproximación
   if isempty(grado), grado = fix((puntos+3)/2); end             %define el grado del polinomio inverso por defecto
end                                                          %
insumos = sum(gruposseparables);                             %establece el número de insumos
numgrupos = size(gruposseparables, 2);                       %establece el número de grupos de insumos separables
                                                             %
if isempty(msist), msist = 5; end                            %muestras sistemáticas para cada insumo a variar en cada grupo separable por defecto
if isempty(malet), malet = 0; end                            %muestras aleatorias para cada grupo separable por defecto
if isempty(maletodo), maletodo = 0; end                      %muestras aleatorias para todos los insumos por defecto
if isempty(LB), LB = 0; end                                  %establece el límite inferior para cada insumo por defecto
if isempty(UB), UB = 1; end                                  %establece el límite superior para cada insumo por defecto
if isempty(potencia), potencia = 10; end                     %potencia a la que se elevan los insumos aleatorios por defecto
if size(msist,2) == 1, msist = msist.*ones(1,numgrupos); end %si las muestras sistemáticas es un escalar lo aplica a todos los grupos
if size(malet,2) == 1, malet = malet.*ones(1,numgrupos); end %si las muestras aleatorias es un escalar lo aplica a todos los grupos
if size(LB, 2) == 1, LB = LB .* ones(1, insumos); end        %si el límite inferior es un escalar lo aplica a todos los insumos
if size(UB, 2) == 1, UB = UB .* ones(1, insumos); end        %si el límite superior es un escalar lo aplica a todos los insumos
UL = UB - LB;                                                %determina la amplitud de los límites
T = [];                                                      %define la variable para almacenar los resultados
                                                             %========= aproximación sistemática por cada grupo de insumos separables
Xinicio = 1;                                                 %Xinicio es la variable que describe el primer insumo del grupo separable
for c1 = 1:numgrupos                                         %para cada grupo de insumos separables
    Xfin = Xinicio + gruposseparables(c1) - 1;                   %Xfin es la variable que describe el último insumo del grupo separable
    if msist(c1) > 0                                             %si queremos obtener alguna muestra del grupo separable
        V = varia(gruposseparables(c1),msist(c1)) ./ msist(c1);      %V es la variación de msist muestras para los insumos en el grupo
        sV = size(V, 1);                                             %establece el número de muestras
        Xn = zeros(sV, insumos);                                     %define Xn
        for c2 = 1:sV                                                %para cada muestra
            Xn(c2,Xinicio:Xfin)=LB(Xinicio:Xfin)+V(c2,:).*UL(Xinicio:Xfin);%ajusta a LB y UB
        end                                                          %
        T = [T; Xn];                                                 %almacena los insumos
    end                                                          %
    Xinicio = Xfin + 1;                                          %establece Xinicio para el próximo grupo separable
end                                                          %
                                                             %========= aproximación aleatoria por cada grupo de insumos separables
Xinicio = 1;                                                 %Xinicio es la variable que describe el primer insumo del grupo separable
for c1 = 1:numgrupos                                         %para cada grupo de insumos separables
    Xfin = Xinicio + gruposseparables(c1) - 1;                   %Xfin es la variable que describe el último insumo del grupo separable
    if malet(c1) > 0                                             %si queremos obtener alguna muestra del grupo separable
        V = rand(malet(c1), Xfin-Xinicio+1) .^ potencia;              %V es la variación aleatoria de malet muestras para los insumos en el grupo
        Xn = zeros(malet(c1), insumos);                               %define Xn
        for c2 = 1:malet(c1)                                          %para cada muestra
            V(c2, :) = V(c2, :) ./ sum(V(c2, :));                          %normaliza para que sume 1
            Xn(c2,Xinicio:Xfin)=LB(Xinicio:Xfin)+V(c2,:).*UL(Xinicio:Xfin);%ajusta a LB y UB
        end                                                           %
        T = [T; Xn];                                                  %almacena los insumos
    end                                                          %
    Xinicio = Xfin + 1;                                          %establece Xinicio para el próximo grupo separable
end                                                          %
                                                             %========= aproximación aleatoria de todos los insumos
Xn = rand(maletodo, insumos) .^ potencia;                    %obtiene las muestras aleatorias para todos los inusmos
for c1 = 1:maletodo                                          %para cada muestra
   Xn(c1, :) = Xn(c1, :) ./ sum(Xn(c1, :));                      %normaliza cada muestra para que sume 1
   Xn(c1, :) = LB + Xn(c1, :) .* UL;                             %ajusta a LB y UB
end                                                          %
T = [T; Xn];                                                 %almacena los insumos
                                                             %========= calcula las recetas con los flujos netos
procesos = size(T, 1);                                       %establece el número de procesos
F = zeros(procesos, materias, grado);                        %define las matrices de flujos netos
for c1 = 1:procesos                                          %para cada proceso
   switch numarg                                                 %dependiendo de si el problema está en tiempo discreto o continuo 
   case 1, F(c1,:,:) = feval(nombrefuncion, T(c1, :));                           %establece los flujos netos para el caso en tiempo discreto
   case 2, F(c1,:,:) = formapolinomioinverso(nombrefuncion, K, grado, T(c1, :)); %aproxima los flujos netos para el caso en tiempo continuo
   end                                                           %
end                                                          %




function P = varia(columnas, n)
if columnas == 1
    P = n;
elseif columnas == 2     %no es estrictamente necesario
    P = zeros(n+1, 2);
    R = (0:n)';
    P(:, 1) = n - R;
    P(:, 2) = R;
else
    P = [];
    for c1 = 0:n
        P2 = varia(columnas-1, n-c1);
        q = size(P2, 1);
        P = [c1*ones(q,1), P2; P];
    end
end
