program meteor(input, output, magfile, coefile);
{ version I: Wed Jun 27 13:36:06 EDT 1990 }

{ copyright Prof. K. Steiglitz
	    Dept. of Computer Science
	    Princeton University
	    Princeton, NJ 08544 }

{ Constraint-based design of linear-phase fir filters with
  upper and lower bounds, and convexity constraints.
  Finds minimum length, or optimizes fixed length, or pushes band-edges.
  If L is the filter length, the models are

  odd-length
   cosine:   sum ( i from 0 to (L-1)/2 ) coeff[i]*cos(i*omega)
   sine:     sum ( i from 0 to (L-3)/2 ) coeff[i]*sin((i+1)*omega)

  even-length
   cosine:   sum ( i from 0 to L/2-1 ) coeff[i]*cos((i+.5)*omega)
   sine:     sum ( i from 0 to L/2-1 ) coeff[i]*sin((i+.5)*omega)  }

  const
    maxpivots = 1000;   { maximum no. of pivots }
    small = 1.0e-8;     { small number used in defining band-edges }
    large = 1.0e+31;    { large number used in search for minimum cost column }
    mmax = 64;          { max. no. of coefficients }
    Lmax = 129;         { max. filter length, L = 2*m-1, 2*m, or 2*m+1 }
    mmaxm = 63;         { mmax - 1 }
    nmax = 640;         { max. size of n, where there are n+1 grid-points }
    ncolmax = 6000;     { max. no. of columns allowed in tableau }
    nspecmax = 20;      { max. no. of specifications }
    eps = 1.0e-8;       { for testing for zero }

  var
    magfile, coefile: file of char;  { files for output }
    done, unbounded, optimal: boolean;          { flags for simplex }
    result: (toomanycols, unbdual, infdual, toomanypivots, opt,
	     infprimal);                        { result of simplex }
    iteration: integer;      { iteration count, index }
    ch: char;                   { character for input }
    m: 1..mmax;         { no. of coefficients, left and right half m }
    L: 1..Lmax;         { filter length = 2*m-1, 2*m, 2*m+1 }
    n: 1..nmax;         { there are n+1 grid-points from 0 to pi }
    numpivots: integer; { pivot count }
    pivotcol, pivotrow: integer;        { pivot column and row }
    pivotel: real;                      { pivot element }
    pi: real;           { 3.14159265358979... }
    cbar: real;         { price when searching for entering column }
    coeff: array[0..mmaxm] of real;     { coefficients }
    carry: array[-1..mmax, -1..mmax] of real;   { inverse-basis matrix of the
						  revised simplex method }
    phase: 1..2;        { phase }
    price: array[0..mmax] of real;      { shadow prices = row -1 of carry =
					  -dual variables = -coefficients }
    basis: array[0..mmax] of integer;  { basis columns, negative integers
					  artificial }
    nspec: 0..nspecmax;                 { no. of bands }
    spectype: array[1..nspecmax] of (con, lim);
						{ type of band }
    left, right: array[1..nspecmax] of real;    { bandedges as read in }
    freq: array[1..ncolmax] of real;            { frequencies at grid points }
    bound1, bound2:  array[1..nspecmax] of real;{ left and right bounds }
    sense: array[1..nspecmax] of char;  { sense of constraint, + up, - down }
    interpolate: array[1..nspecmax] of char; { g=geometric, a=arithmetic }
    firstcol: array[1..nspecmax] of 1..ncolmax; { leftmost column of spec }
    lastcol: array[1..nspecmax] of 1..ncolmax;  { rightmost column of spec }
    foundfeas: boolean;                 { found feasible solution }
    mlargest, msmallest: integer;       { range of m }
    Llargest, Lsmallest: integer;       { range of L = 2*m-1, 2*m, or 2*m+1 }
    ncol: 1..ncolmax;                   { number of columns }
    tab: array[0..mmax, 1..ncolmax] of real;    { tableau }
    d: array[1..ncolmax] of real;       { current cost vector }
    c: array[1..ncolmax] of real;       { cost in original problem }
    curcol: array[-1..mmax] of real;    { current column }
    curcost: real;                      { current cost }
    bestm: integer;     { best order }
    hug: array[1..nspecmax] of char;    { allow this constraint to be hugged? }
    whattodo: (findlen, maxdist, pushedge);   { type of optimization }
    npushed: integer;   { number of bandedges pushed }
    whichway: (rr, ll); { push which way? }
    bandpushed: array[1..nspecmax] of integer;  {  bandedges pushed }
    lowlim: real;       { lower limit for finding if primal is feasible }
    oddlength: boolean; { odd-length filters? }
    symtype: (cosine, sine);    { cosine or sine symmetry }

procedure makebands(i: integer);
{ fills in frequencies to make grid - frequencies are kept as reals
  in radians, and each band has equally spaced grid points }

var
  k, kmax: integer;

begin { makebands }
  if( i = 1 ) then firstcol[i]:= 1 else firstcol[i]:= lastcol[i-1] + 1;
  kmax:= trunc((right[i]-left[i])*n/0.5+small);{ kmax+1 cols. in this band }
  if( kmax = 0 ) then  freq[firstcol[i]]:= 2.0*pi*left[i]
    else
      for k:= 0 to kmax do
	  freq[firstcol[i]+k]:= 2.0*pi*(left[i] + (right[i]-left[i])*k/kmax);
  lastcol[i]:= firstcol[i] + kmax
end;  { makebands }


function trig0(i: integer; f:real): real;
{ trig function in filter transfer function }

begin { trig0 }
 if oddlength then
  if (symtype = cosine) then
   trig0:= cos(i*f)
  else
   trig0:= sin((i+1)*f);

 if (not oddlength) then
  if (symtype = cosine) then
   trig0:= cos((i+0.5)*f)
  else
   trig0:= sin((i+0.5)*f)

end;  { trig0 }

function trig2(i: integer; f: real): real;
{ second derivative of trig function in filter transfer function }

begin { trig2 }
 if oddlength then
  if (symtype = cosine) then
   trig2:= -i*i*cos(i*f)
  else
   trig2:= -(i+1)*(i+1)*sin((i+1)*f);

 if (not oddlength) then
  if (symtype = cosine) then
   trig2:= -(i+0.5)*(i+0.5)*cos((i+0.5)*f)
  else
   trig2:= -(i+0.5)*(i+0.5)*sin((i+0.5)*f)

end;  { trig2 }

procedure convex(i: integer);
{ sets up tableau columns for convexity constraints on magnitude }

  var
    row, col: integer;       { gridpoint, side of polygon, row, col }

  begin  { convex }
    makebands(i);
    for col:= firstcol[i] to lastcol[i] do      { for all frequencies in band }
	begin
	  c[col]:= 0.0;
	  for row:= 0 to (m-1) do
	    begin
	      tab[row, col]:=   { normal constraint is <= }
		trig2(row, freq[col]);
	      if( sense[i] = '+' ) then tab[row, col]:= -tab[row, col]
	    end;  { for row }
	  tab[m, col]:= 0.0
	end  {for col }
end;   { convex }


procedure limit (i: integer);
{ sets up tableau columns for upper or lower bounds on transfer
  function for specification i; the bound is linearly interpolated
  between the start and end of the band }

  var
    row, col: integer;       { gridpoint, side of polygon, row, col }

  begin  { limit }
    makebands(i);
    for col:= firstcol[i] to lastcol[i] do  { for all frequencies in band }
	begin
	  if( firstcol[i] = lastcol[i] ) then c[col]:= bound1[i]
	    else
	      begin
		if( interpolate[i] = 'g' ) then c[col]:=
		  bound1[i]*exp( ((col-firstcol[i])/(lastcol[i]-firstcol[i]))
			   *ln(abs(bound2[i]/bound1[i])) );
		if( interpolate[i] = 'a' ) then c[col]:=
		  bound1[i] + ((col-firstcol[i])/(lastcol[i]-firstcol[i])
			   *(bound2[i]-bound1[i]) )
	      end;
	  if( sense[i] = '-' ) then c[col]:= -c[col];
	  for row:= 0 to (m-1) do
	    begin
	     tab[row, col]:= trig0(row, freq[col]);
	     if( sense[i] = '-' ) then tab[row, col]:= -tab[row, col]
	    end;
	  if ( hug[i] <> 'h' )  then  tab[m, col]:= 1.0
				else  tab[m, col]:= 0.0
	end  { for col }
end;   { limit }


procedure readdata;
{ reads in problem data }
{ not meant to be interactive; aborts on bad filter lengths}

var
  i: integer;
  allhugged: boolean;   { to see if all constraints are hugged }

begin  { readdata }
  readln(Lsmallest, Llargest);

  if ( ( Lsmallest < 1 ) or (Llargest > Lmax) ) then
   begin
    writeln('Lsmallest or Llargest out of range: quitting');
    halt
   end;

  if ( odd(Lsmallest) <> odd(Llargest) ) then
   begin
    writeln('parity of Lsmallest and Llargest unequal: quitting');
    halt
   end;

  readln(ch);
  if(ch = 'c') then symtype:= cosine
	       else symtype:= sine;
  if(symtype = cosine) then writeln('cosine symmetry')
		       else writeln('sine symmetry');

  oddlength:= odd(Lsmallest);

  if oddlength then
   if (symtype = cosine) then
    begin
     msmallest:= (Lsmallest + 1) div 2;
     mlargest := (Llargest  + 1) div 2
    end
   else
    begin
     msmallest:= (Lsmallest - 1) div 2;
     mlargest := (Llargest  - 1) div 2
    end;

  if not oddlength then
   begin
    msmallest:= Lsmallest div 2;
    mlargest := Llargest  div 2
   end;

  if( Lsmallest <> Llargest ) then
    begin
      whattodo:= findlen;
      writeln('finding minimum length: range ',
	       Lsmallest:4, ' to ', Llargest:4)
    end;

  if( Lsmallest = Llargest ) then
    begin
      m:= msmallest;
      L:= Lsmallest;
      writeln('fixed length of ', L:4);
      readln(ch);     { right, left, or neither: edges to be pushed? }
      if( ch = 'n' ) then
	begin
	  whattodo:= maxdist;
	  writeln('maximizing distance from constraints not marked hugged')
	end
      else
	begin
	  whattodo:= pushedge;
	  if( ch = 'r' ) then whichway:= rr else whichway:= ll;
	  readln(npushed);
	  for i:= 1 to npushed do read(bandpushed[i]);
	  readln;
	  if( whichway = rr ) then writeln('pushing bandedges right');
	  if( whichway = ll ) then writeln('pushing bandedges left');
	  write('constraint numbers: ');
	  for i:= 1 to npushed do write(bandpushed[i]:3, ' ');
	  writeln
	end;
      end;
    readln(n);  { there are n+1 grid points between 0 and pi }
    nspec:= 0;
    readln(ch);
    while( ch <> 'e' ) do   { 'e' for end }
      begin
	nspec:= nspec + 1;
	i:= nspec;
	if( ch = 'c') then
	  begin
	    spectype[i]:= con;
	    readln( sense[i] );
	    readln( left[i], right[i] );
	    writeln('constraint ', i:1, ': convex, sense ', sense[i]);
	    writeln('  bandedges:  ', left[i]:10:4, ' ', right[i]:10:4)
	  end;
	if( ch = 'l') then
	  begin
	    spectype[i]:= lim;
	    readln( sense[i] );
	    readln( interpolate[i] );
	    readln( hug[i] );
	    readln( left[i], right[i] );
	    readln( bound1[i], bound2[i] );
	    if( (interpolate[i] = 'g') and (bound1[i]*bound2[i]=0.0) ) then
	      begin
		writeln('geometrically interpolated bandedge in constraint ',
			 i:5, ' is zero');
		halt
	      end;
	    if( sense[i] = '+' ) then
	      writeln('constraint ', i:2, ': upper limit');
	    if( sense[i] = '-' ) then
	      writeln('constraint ', i:2, ': lower limit');
	    if( interpolate[i] = 'g' ) then
	      writeln('  geometric interpolation');
	    if( interpolate[i] = 'a' ) then
	      writeln('  arithmetic interpolation');
	    if( hug[i] = 'h' )
	      then  writeln('  this constraint will be hugged')
	      else  writeln('  this constraint will be optimized');
	    writeln('  bandedges:  ', left[i]:10:4, ' ', right[i]:10:4);
	    writeln('  bounds:     ', bound1[i]:10:4, ' ', bound2[i]:10:4)
	  end;
	makebands(i);
	writeln('  initial columns:    ', firstcol[i]:10, ' ', lastcol[i]:10);
	readln(ch)  { next }
      end;  { while }
    ncol:= lastcol[nspec];
    writeln('number of specs= ', nspec:5);
    writeln('initial number of columns= ', ncol:5);

    allhugged:= true;   { check to see if all limit constraints are hugged }
    for i:= 1 to nspec do
     if (spectype[i] = lim) and (hug[i] <> 'h') then
      allhugged:= false;
    if allhugged then
     begin
      writeln('all constraints are hugged: ill-posed problem');
      halt
     end

  end;   { readdata }


procedure setup;
{ initializes constraints }

var
  i: integer;

begin  { setup }
  pi:= 4.0*arctan(1.0);
  for i:= 1 to nspec do
    begin
      case spectype[i] of
	con: convex(i);
	lim: limit(i)
      end  { case }
  end;  {for }
  ncol:= lastcol[nspec]
end;   { setup }


procedure columnsearch;
{ looks for favorable column to enter basis.
  returns lowest cost and its column number, or turns on the flag optimal }

var
  i, col: integer;
  tempcost: real;           { minimum cost, temporary cost of column }

  begin  { columnsearch }
    for i:= 0 to m do price[i]:= -carry[-1, i];  { set up price vector }
    optimal:= false;
    cbar:= large;
    pivotcol:= 0;
    for col:= 1 to ncol do
      begin
	tempcost:= d[col];
	for i:= 0 to m do tempcost:= tempcost - price[i]*tab[i, col];
	if( cbar > tempcost ) then
	  begin
	   cbar:= tempcost;
	   pivotcol:= col
	  end
      end;  { for col }
    if ( cbar > -eps ) then optimal:= true
  end;   { columnsearch }


procedure rowsearch;
{  looks for pivot row. returns pivot row number,
   or turns on the flag unbounded }

var
  i, j: integer;
  ratio, minratio: real;        { ratio and minimum ratio for ratio test }

  begin  { rowsearch }
    for i:= 0 to m do           { generate column }
      begin
	curcol[i]:= 0.0;        { current column = B inverse * original col. }
	for  j:= 0 to m do curcol[i]:=
			   curcol[i] + carry[i, j]*tab[j, pivotcol]
      end;
  curcol[-1]:= cbar;            { first element in current column }
  pivotrow:= -1;
  minratio:= large;
  for i:= 0 to m do                             { ratio test }
    begin
      if( curcol[i] > eps ) then
	begin
	  ratio:= carry[i, -1]/curcol[i];
	    if( minratio > ratio ) then         { favorable row }
	      begin
		minratio:= ratio;
		pivotrow:= i;
		pivotel:= curcol[i]
	      end
	    else { break tie with max pivot }
	      if ( (minratio = ratio) and (pivotel < curcol[i]) ) then
		  begin
		    pivotrow:= i;
		    pivotel:= curcol[i]
		  end
	end  { curcol > eps }
      end;  { for i }
    if ( pivotrow = -1 ) then  unbounded:= true         { nothing found }
			 else  unbounded:= false
  end;  { rowsearch }


procedure pivot;
{ pivots }

  var
    i, j: integer;

  begin { pivot }
    basis[pivotrow]:= pivotcol;
    for j:= -1 to m do carry[pivotrow, j]:= carry[pivotrow, j]/pivotel;
    for i:= -1 to m do
      if( i<> pivotrow ) then
	for j:= -1 to m do
	  carry[i, j]:= carry[i, j] - carry[pivotrow, j]*curcol[i];
    curcost:= -carry[-1, -1]
  end;  { pivot }


procedure changephase;
{ changes phase from 1 to 2, by switching to original cost vector }

  var
    i, j, b: integer;

  begin  { changephase }
    phase:= 2;
    for i:= 0 to m do if( basis[i] <= 0 ) then
      writeln( '...artificial basis element ', basis[i]:5,
	       ' remains in basis after phase 1');
    for j:= 1 to ncol do d[j]:= c[j];   { switch to original cost vector }
    for j:= -1 to m do
      begin
	carry[-1, j]:= 0.0;
	for i:= 0 to m do
	  begin
	    b:= basis[i];       { ignore artificial basis elements that are }
	    if( b >= 1 ) then   { still in basis }
	      carry[-1, j]:= carry[-1, j] - c[b]*carry[i,j]
	  end  { for i }
      end;  { for j }
    curcost:= -carry[-1, -1]
  end;   { changephase }

function mag( f: real ): real;
{ computes magnitude function, given radian frequency f }

var
  i: integer;
  temp: real;

begin  { mag }
  temp:= 0.0;
  for i:= 0 to (m-1) do
      temp:= temp + coeff[i]*trig0(i,f);
  mag:= temp
end;  { mag }


procedure wrapup;
{ prints results, final frequency response }

var
  i, k: integer;

  begin  { wrapup }
    rewrite( magfile, "magfile" );         { open files for writing }
    rewrite( coefile, "coefile" );

    if (oddlength and (symtype = cosine)) then  { write coeffs to coefile }
     begin
      writeln(coefile, 2*m-1:4, ' cosine symmetry');    { L = length, odd }
      for i:= (m-1) downto 1 do
       writeln(coefile, coeff[i]/2);
      writeln(coefile, coeff[0]);
      for i:= 1 to (m-1) do
       writeln(coefile, coeff[i]/2)
     end;       { odd, cosine }

    if ((not oddlength) and (symtype = cosine)) then
     begin
      writeln(coefile, 2*m:4, ' cosine symmetry');      { L = length, even }
      for i:= (m-1) downto 0 do
       writeln(coefile, coeff[i]/2);
      for i:= 0 to (m-1) do
       writeln(coefile, coeff[i]/2)
     end;       { even, cosine }

    if (oddlength and (symtype = sine)) then
     begin
      writeln(coefile, 2*m+1:4, ' sine symmetry');      { L = length, odd }
      for i:= (m-1) downto 0 do
       writeln(coefile, -coeff[i]/2);   { negative of the first m coefs. }
       writeln(coefile, ' 0.0');        { middle coefficient is always 0 }
      for i:= 0 to (m-1) do
       writeln(coefile, coeff[i]/2)
     end;       { odd, sine }

    if ((not oddlength) and (symtype = sine)) then
     begin
      writeln(coefile, 2*m:4, ' sine symmetry');        { L = length, even }
      for i:= (m-1) downto 0 do
       writeln(coefile, -coeff[i]/2);   { negative of the first m coefs. }
      for i:= 0 to (m-1) do
       writeln(coefile, coeff[i]/2)
     end;       { even, sine }

    for k:= 0 to n do      { magnitude on regular grid }
	writeln( magfile, 0.5*k/n:10:4, ' ', mag( k*pi/n) );
    writeln( magfile );
    writeln( magfile, '       magnitude at bandedges');
    writeln( magfile );
    for i:= 1 to nspec do
      if( spectype[i] = lim ) then
	begin
	  writeln( magfile, freq[firstcol[i]]*0.5/pi:10:4, ' ',
		   mag( freq[firstcol[i]] ) );
	  writeln( magfile, freq[lastcol[i]]*0.5/pi:10:4, ' ',
		   mag( freq[lastcol[i]]  ) );
	  writeln
	end
  end;   { wrapup }


procedure simplex;
{ simplex for linear programming }

var
  i, j, col, row: integer;

begin  { simplex }
  done:= false;
  phase:= 1;
  for i:= -1 to m do for j:= -1 to mmax do carry[i, j]:= 0.0;
  for i:= 0 to m do carry[i, i]:= 1.0;        { artificial basis }
  carry[-1, -1]:= -1.0;                       { - initial cost }
  curcost:= -carry[-1, -1];
  carry[ m, -1]:= 1.0;                        { variable minimized in primal }
  for i:= 0 to m do basis[i]:= -i;            { initial, artificial basis }
  if( ncol <= ncolmax ) then    { check number of columns }
    for col:= 1 to ncol do      { initialize cost for phase 1 }
      begin
	d[col]:= 0.0;
	for row:= 0 to m do d[col]:= d[col] - tab[row, col]
      end
  else
    begin
      writeln('...termination: too many columns for storage');
      done:= true;
      result:= toomanycols
    end;
  numpivots:= 0;
  while( (numpivots < maxpivots) and (not done) and
	 ( (curcost > lowlim) or (phase = 1) ) ) do
    begin
      columnsearch;
      if( not optimal ) then
	begin                         { not optimal }
	  rowsearch;
	    if( unbounded ) then
	      begin
		done:= true;
		result:= unbdual        { dual of problem is unbounded }
	      end
	    else
	      begin
		pivot;
		numpivots:= numpivots + 1;
		if ( (numpivots = 1 ) or ( numpivots mod 10 = 0 ) ) then
		      writeln('pivot ', numpivots:4, ' cost= ', curcost:12)
	      end
	end  { not optimal }
      else                            { optimal }
	  if( phase = 1 ) then
	    begin
	      if( curcost > eps ) then
		begin
		  done:= true;     { dual of problem is infeasible }
		  result:= infdual { this happens if all specs are hugged }
		end
	      else
		begin
		  if ( (numpivots <> 1 ) and ( numpivots mod 10 <> 0 ) ) then
		    writeln('pivot ', numpivots:4, ' cost= ', curcost:12);
		  writeln('phase 1 successfully completed');
		  changephase
		end
	    end  { if phase = 1 }
	  else
	    begin
	      if ( (numpivots <> 1 ) and ( numpivots mod 10 <> 0 ) ) then
		writeln('pivot ', numpivots:4, ' cost= ', curcost:12);
	      writeln('phase 2 successfully completed');
	      done:= true;
	      result:= opt
	    end
    end;  { while }
  if( (curcost <= lowlim) and (phase = 2) ) then
    begin
      if ( (numpivots <> 1 ) and ( numpivots mod 10 <> 0 ) ) then
	writeln('pivot ', numpivots:4, ' cost= ', curcost:12);
      result:= infprimal
    end;
  if ( numpivots >= maxpivots ) then
    begin
      writeln('...termination: maximum number of pivots exceeded');
      result:= toomanypivots
    end
end;  { simplex }

procedure printresult;
{ prints enumerated type }

begin
  case result of
    toomanycols: writeln('too many columns in specifications');
    unbdual: writeln('infeasible (unbounded dual)');   { unbounded dual }
    infdual: writeln('infeasible or unbounded');{ infeasible dual }
    toomanypivots: writeln('too many pivots');
    opt: writeln('optimum obtained');
    infprimal: writeln('infeasible ')
  end { case }
end; { printresult }

procedure getm;
{ find best order (and hence length) }

var
  leftm, rightm: 1..mmax;
  i: integer;
  foundm, checkedleft, checkedright: boolean;

begin  { getm }
  foundfeas:= false;  { flag set when a feasible solution is found }
  iteration:= 0;
  leftm:= msmallest;
  rightm:= mlargest;
  foundm:= false;
  checkedleft:= false;
  checkedright:= false;
  while ( not foundm ) do
    begin
      if (iteration=0) then
       m:= leftm + (rightm - leftm) div 2;     { first time through }
      iteration:= iteration + 1; 
      writeln;
      writeln('iteration ', iteration:2);

      if oddlength then
       if (symtype = cosine) then
	writeln('L= ', 2*m-1:4)
       else
	writeln('L= ', 2*m+1:4);

      if not oddlength then
	writeln('L= ', 2*m:4);

      setup;
      simplex;
      printresult;
      if( result = opt ) then
	begin
	  foundfeas:= true;
	  rightm:= m;
	  bestm:= m;
          checkedright:= true;  { right side of bracket has been checked }
	  if oddlength then
	   if (symtype = cosine) then
	    writeln('new best length L= ', 2*bestm-1:4)
	   else
	    writeln('new best length L= ', 2*bestm+1:4);

	  if not oddlength then
	   writeln('new best length L= ', 2*bestm:4);

	  for i:= 0 to (m-1) do  coeff[i]:= -carry[-1, i]
	end;

      if( result <> opt ) then 
       begin
        leftm:= m;
        checkedleft:= true  { left side of bracket has been checked }
       end;

      if (rightm > leftm + 1) then
       m:= leftm + (rightm - leftm) div 2;

      if (rightm = leftm + 1) then
       if (not checkedleft) then
        begin
         m:= leftm;
         checkedleft:= true
        end
         else if (not checkedright) then
          begin
           m:= rightm;
           checkedright:= true
          end
            else
             foundm:= true;

      if (rightm = leftm) then
       foundm:= true

    end;  { while }

  if( not foundfeas ) then
    begin
      writeln('no feasible solution found');
      halt
    end;
  m:= bestm;

  writeln;
  if oddlength then
   if (symtype = cosine) then
    writeln('best length L= ', 2*bestm-1:4)
   else
    writeln('best length L= ', 2*bestm+1:4);

  if not oddlength then
   writeln('best length L= ', 2*bestm:4)

end;  { getm }

procedure getedge;
{ optimize bandedge }

var
  lefte, righte, newe, beste: real;
  onespace, stopspace: real; { nominal grid spacing, stop criterion }
  i: integer;

begin { getedge }
  onespace:= 0.5/n;     { space between grid points }
  stopspace:= onespace/10.0;    { stop criterion is 1/10 nominal grid spacing }
  if( whichway = rr ) then
    begin
      lefte:= left[bandpushed[1]];      { start with rightmost leftedge }
      for i:= 2 to npushed do
	if( left[bandpushed[i]] > lefte ) then lefte:= left[bandpushed[i]];
      righte:= 0.5
    end
  else
    begin
      lefte:= 0.0;
      righte:= right[bandpushed[1]];
      for i:= 2 to npushed do   { start with leftmost rightedge }
	if( right[bandpushed[i]] < righte ) then righte:= right[bandpushed[i]]
    end;
  iteration:= 0;
  foundfeas:= false;  { flag set when a feasible solution is found }
  while( (righte - lefte) > stopspace ) do
    begin
      iteration:= iteration + 1;
      newe:= (righte + lefte)/2.0;
      writeln;
      writeln('iteration ', iteration:4);
      writeln('trying new edge = ', newe:10:4);
      for i:= 1 to npushed do
	if( whichway = rr ) then right[bandpushed[i]]:= newe
			    else left [bandpushed[i]]:= newe;
      setup;
      simplex;
      printresult;
      if( result = opt ) then
	begin
	  if( whichway = rr ) then  lefte:= newe
			      else righte:= newe;
	  foundfeas:= true;
	  beste:= newe;
	  for i:= 0 to (m-1) do  coeff[i]:= -carry[-1, i]
	end;
      if( result <> opt ) then
	if( whichway = rr ) then righte:= newe
			    else  lefte:= newe
    end; { while }
  writeln;
  if( not foundfeas ) then
    begin
      writeln('no feasible bandedge found');
      halt
    end;
  writeln('found edge= ', beste:10:4);
  for i:= 1 to npushed do
      if( whichway = rr ) then right[bandpushed[i]]:= beste
			  else  left[bandpushed[i]]:= beste;
  for i:= 1 to nspec do  makebands(i);
end;  { getedge }


procedure getmaxdist;
{ maximizes distance from constraints }

var
  i: integer;

begin  { getmaxdist }
  writeln('optimization: maximize distance from constraints');
  setup;
  simplex;
  printresult;
  if( result = opt ) then
   begin
    writeln('final cost= distance from constraints= ', curcost:12);
    for i:= 0 to (m-1) do     { record coefficients }
     coeff[i]:= -carry[-1, i]
   end
  else
   halt                 { don't go back if unsuccessful }
end;  { getmaxdist }


begin  { main }
  writeln('welcome to meteor:');
  writeln('constraint-based, linear-phase FIR filter design');
  readdata;
  lowlim:= -eps;        { dual cost negative => primal infeasible }
  case whattodo of
      findlen: getm;
      pushedge:  getedge;
      maxdist:   getmaxdist
    end; { case }       { no return here if unsuccessful }
  wrapup
end.  { main }


