#!/bin/awk -f
#
#	simu [ -v variable=value] [file1] [file2]
#
#	Basic tool to read and summarize empire data.  Based on awk, so
#	variables may be set.  See below for details on which variables
#	are important.  stdin is read after the last file if it is not a tty.
#
#	   Delete all the lines before #!/bin/awk, and save as simu.  Then
#		chmod +x simu
#	   Create a file or files with the output of dump, neweff, and prod.
#		prod * >empire.data
#		neweff * >>empire.data
#		dump * >>empire.data
#	   Then feed the file(s) into simu
#		simu empire.data
#
#
#	Supports the following input forms:
#		dump		nearly any version, deity or player
#		neweff		Chainsaw 3.0
#		prod		any version?  player only
#		show ship	Chainsaw 3.2
#		ship		Chainsaw 3.2
#		cargo		Chainsaw 3.2
#		bestpath	Chainsaw 3.0
#
#	Variables:		(see BEGIN for defaults)
#		etu		number of etus
#		myno		Country number
#		myname		Country name	(can obtain from nation)
#		
#
#		-the harmless conquerer


function num(str) {
  return substr(str,1,length(str)-1);
}


#
#	Turns a letter into a name.  Tries the first letter, if that
#	isn't a commodity, tries the last letter (useful for ship, unit,
#	and prod outputs)
#
function commstr(str) {
  char=substr(str,1,1);
  if (char == "c") return "civ";
  if (char == "m") return "mil";
  if (char == "u") return "uw";
  if (char == "f") return "food";
  if (char == "s") return "shell";
  if (char == "g") return "gun";
  if (char == "p") return "pet";
  if (char == "i") return "iron";
  if (char == "d") return "dust";
  if (char == "b") return "bar";
  if (char == "o") return "oil";
  if (char == "l") return "lcm";
  if (char == "h") return "hcm";
  if (char == "r") return "rad";

  char=substr(str,length(str),1);
  if (char == "c") return "civ";
  if (char == "m") return "mil";
  if (char == "u") return "uw";
  if (char == "f") return "food";
  if (char == "s") return "shell";
  if (char == "g") return "gun";
  if (char == "p") return "pet";
  if (char == "i") return "iron";
  if (char == "d") return "dust";
  if (char == "b") return "bar";
  if (char == "o") return "oil";
  if (char == "l") return "lcm";
  if (char == "h") return "hcm";
  if (char == "r") return "rad";


  return "";
}

#
#	Truncate starting and ending whitespace from a string
#
function truncwhite(str) {
  for (i=1;i<=length(str) && substr(str,i,1) ~ "[ \t\r\n]";i++) ;
  for (j=length(str);j>i && substr(str,j,1) ~ "[ \t\r\n]";j--) ;
  return (substr(str,i,j-i+1));
}

BEGIN {
  SUBSEP=",";
  if (etu=="") etu=60;
  if (myno=="") myno=41;
  if (myname=="") myname="harmless";
  if (showall=="") showall=0;

  mode="";
  stat=0;

  shipnamlen=16;

  eatrate = 0.001;
  babyeat = 0.012;
  obrate  = 0.005;
  uwbrate = 0.0025;
  easytech= 1.0;
  logtech = 1.55;

  if (maxpop=="") maxpop=550;
  if (maxciv=="") maxciv=maxpop/(1+etu*obrate);
  if (maxuw=="") maxuw=maxpop/(1+etu*uwbrate);

  weight["civ"]=1;	packing["civ"]=1;
  weight["mil"]=1;	packing["mil"]=1;
  weight["uw"]=2;	packing["uw"]=1;
  weight["food"]=1;	packing["food"]=10;
  weight["shell"]=1;	packing["shell"]=10;
  weight["gun"]=10;	packing["gun"]=10;
  weight["pet"]=1;	packing["pet"]=10;
  weight["iron"]=1;	packing["iron"]=10;
  weight["dust"]=5;	packing["dust"]=10;
  weight["bar"]=50;	packing["bar"]=5;
  weight["oil"]=1;	packing["oil"]=10;
  weight["lcm"]=1;	packing["lcm"]=10;
  weight["hcm"]=1;	packing["hcm"]=10;
  weight["rad"]=8;	packing["rad"]=10;
}


#
#	Single line parsings
#
/Technology\.* * .+ * Research\.* .+/ {
  tech = $2;
  research= $4;
  next;
}

/Education\.* * .+ * Happiness\.* .+/ {
  edu = $2;
  happiness= $4;
  next;
}

/Max population : [0-9]+/ { maxpop = $4 };

/Max safe population for civs\/uws:/ { 
  split($6,a,"/");
  maxciv=a[1];
  maxuw=a[2];
  next;
}



/^\[fert:[0-9]+\]/ {
  for (i=4;i<=NF;i++) if ($i=="@") break;
  fert[$(i+1)]=substr($1,7,length($1)-7);
}
/^\[oil:[0-9]+\]/ {
  for (i=4;i<=NF;i++) if ($i=="@") break;
  oil[$(i+1)]=substr($1,6,length($1)-6);
}

/Best path from .* to .* is .* \(cost .*\)/ {
  bestpath[$4,$6]=substr($10,1,length($10)-1);
}


#
#	Version parsing stuff
#
/An update consists of [0-9]+ empire time units/ {  etu = $5; next; }
/No food is needed!!/ { nofood=1; next; }
/1000 civilians will give birth to [0-9\.]+ babies per etu./ { 
  obrate = $7/1000; next;
}
/In one time unit, 1000 people eat [0-9\.]+ units of food./ {
  eatrate = $8/1000; next;
}
/1000 babies eat [0-9\.]+ units of food becoming adults./ {
  babyeat = $4/1000; next;
}
/1000 uncompensated workers will give birth to [0-9\.]+ babies./ {
  uwbrate = $8/1000; next;
}
/Tech Buildup is limited to logarithmic growth \(base .+\) after .+/ {
  easytech = substr($11,1,length($11)-1);
  logtech = $9;
}
/Max mob gain per update/ {
  secmob = $6;
  maxshipmob = $7;
  planemob = $8;
  unitmob = $9;
}
/Max eff gain per update/ {
  if ($6>0) seceff = $6;
  if ($7>0) maxshipeff = $7;
  if ($8>0) planeeff = $8;
  if ($9>0) uniteff = $9;
}  
/ NEWFISH,/ { newfish=1; }


#
#	Headers for various multi-line read modes
#
/ *DUMP SECTOR/ { mode="dump"; stat=1; next; }
/CENSUS/ { mode="census"; stat=1; next; }
/COMMODITIES/ { mode="comm"; stat=1; next; }
/PRODUCTION/ {mode="prod"; stat=1; next; }
/EFFICIENCY SIMULATION/ {mode="neweff"; stat=1; next; }
/ *lcm hcm avail tech \$/ {mode="ssb"; stat=1; next;}
/ *cargos & capabilities/ {mode="ssc"; stat=1; next; }
/ *def  d  s  y  g  r  d  n  l  l/ {mode="sss"; stat=1; next; }
/^shp#     ship type       x,y   fl  eff civ mil  uw  fd pn he xl ln mob fuel tech/ { mode="ship"; stat=1; next; }
/^shp# ship type           x,y   flt  eff  sh gun pet irn dst bar oil lcm hcm rad/ { mode="cargo"; stat=1; next; }

#
#	Parse continuation lines of multi-line modes
#
{
  if (mode=="dump") {
    if (stat==1) {
      for (i=1;i<=NF;i++) header[i]=$i;
      nheader=NF;
      stat=2;
      dsects=0;
      for (i in order) delete order[i];
      next;
    } else if (NF != nheader) {
      stat=0;
      mode="";
    } else if (stat==2) {
      for (i=1;i<=nheader;i++) temp[header[i]] = $i;
      sect = temp["x"] "," temp["y"];
      if (temp["own"]=="") temp["own"]=myno;
      for (i=1;i<=nheader;i++) val[sect,header[i]] = $i;
      sects[sect]="1";
      order[dsects++] = sect;
      next;
    }
    mode="";
  } else if (mode=="neweff") {
    if (stat==1) {
      if ($0=="   sect  des    projected eff") {
	stat=2;
	next;
      }
    } else if (stat==2 && NF==3) {
      newdes[$1]=$2;
      neweff[$1]=num($3);
      sects[$1]="1";
      next;
    }
    mode="";
  } else if (mode=="prod") {
    if (stat==1) {
      if ($0 =="   sect  des eff wkfc will make- p.e. cost  use1 use2 use3  max1 max2 max3  max" ) {
	stat=2;
	next;
      } else {
	mode="";
      }
    } else if (stat==2 && NF >2) {
      sect=$1;
      sects[sect]="1";
      newdes[sect]=$2;
      will[sect]=$5;
      make[sect]=$6;
      if (make[sect]=="bars") make[sect]="bar";
      max[sect]=$(NF);
      if (NF==11) {
	use1[sect]=commstr($9);
	amt1[sect]=num($9);
	max1[sect]=num($10);
      } else if (NF==13) {
	use1[sect]=commstr($9);
	amt1[sect]=num($9);
	max1[sect]=num($11);
	use2[sect]=commstr($10);
	amt2[sect]=num($10);
	max2[sect]=num($12);
      } else if (NF==15) {
	use1[sect]=commstr($9);
	amt1[sect]=num($9);
	max1[sect]=num($12);
	use2[sect]=commstr($10);
	amt2[sect]=num($10);
	max2[sect]=num($13);
	use3[sect]=commstr($11);
	amt3[sect]=num($11);
	max3[sect]=num($14);
      }
      next;
    } else {
      mode="";
      stat=0;
    }
  } else if (mode=="ssb") {
    if (stat==1 && NF > 5) {
      name=truncwhite(substr($0,1,shipnamlen));
      shipcost[name]=substr($NF,2);
      shiptech[name]=$(NF-1);
      shipavail[name]=$(NF-2);
      shiplcm[name]=$(NF-3);
      shiphcm[name]=$(NF-4);
      next;
    } else {
      mode="";
      stat=0;
      next;
    }
  } else if (mode=="sss") {
    if (stat==1 && NF > 11) {
      name=truncwhite(substr($0,1,shipnamlen));
      nf=split(substr($0,21),a,"[ \t/]*");
      shipdef[name]=a[2];
      shipspd[name]=a[3];
      shipvis[name]=a[4];
      shipspy[name]=a[5];
      shiprng[name]=a[6];
      shipfir[name]=a[7];
      shiplnd[name]=a[8];
      shippln[name]=a[9];
      shiphel[name]=a[10];
      shipxpl[name]=a[11];
      shipfc[name]=a[12];
      shipfu[name]=a[13];
      next;
    } else {
      mode="";
      stat=0;
      next;
    }
  } else if (mode=="ssc") {
    if (stat==1 && NF > 3) {
      name=truncwhite(substr($0,1,shipnamlen));
      nf=split(substr($0,21),a,"[ \t/]*");
      for (i=2;i<=nf;i++) {
	if (a[i] ~ "[0-9]+[cmufsgpidbolhr]") {
	  shipcargo[name,commstr(a[i])] = num(a[i]);
	} else {
	  shipcap[name] = shipcap[name] " " a[i];
	}
      }
      next;
    } else {
      mode="";
      stat=0;
      next;
    }
  } else if (mode=="ship") {
    if (stat==1) {
      ships=0;
      for (i in shiporder) delete shiporder[i];
      stat=2;
    }
    if (stat==2 && NF > 2) {
      name=truncwhite(substr($0,6,shipnamlen));
      shiporder[ships++]=$1;
      shipname[$1]=name;
      shiptech[$1]=$NF;
      shipfuel[$1]=$(NF-1);
      shipmob[$1]=$(NF-2);
      shiplnd[$1]=$(NF-3);
      shipxlnd[$1]=$(NF-4);
      shiphel[$1]=$(NF-5);
      shippln[$1]=$(NF-6);
      shipcargo[$1,"food"]=$(NF-7);
      shipcargo[$1,"uw"]=$(NF-8);
      shipcargo[$1,"mil"]=$(NF-9);
      shipcargo[$1,"civ"]=$(NF-10);
      shipeff[$1]=num($(NF-11));
      shipfleet[$1]=substr($0,32,1);
      shipsect[$1]=truncwhite(substr($0,22,9));
      next;
    } else {
      mode="";
      stat=0;
      next;
    }
  } else if (mode=="cargo") {
    if (stat==1) {
      ships=0;
      for (i in shiporder) delete shiporder[i];
      stat=2;
    }
    if (stat==2 && NF > 2) {
      name=truncwhite(substr($0,6,shipnamlen));
      shiporder[ships++]=$1;
      shipname[$1]=name;
      shipcargo[$1,"rad"]=$NF;
      shipcargo[$1,"hcm"]=$(NF-1);
      shipcargo[$1,"lcm"]=$(NF-2);
      shipcargo[$1,"oil"]=$(NF-3);
      shipcargo[$1,"bar"]=$(NF-4);
      shipcargo[$1,"dust"]=$(NF-5);
      shipcargo[$1,"iron"]=$(NF-6);
      shipcargo[$1,"pet"]=$(NF-7);
      shipcargo[$1,"gun"]=$(NF-8);
      shipcargo[$1,"shell"]=$(NF-9);
      shipeff[$1]=num($(NF-10));
      shipfleet[$1]=substr($0,31,1);
      shipsect[$1]=truncwhite(substr($0,22,9));
      next;
    } else {
      mode="";
      stat=0;
      next;
    }
  }
}



#
#	Subroutines that may be used by END {}
#


#  cost to move through sector sect (des is local)
function movcost(sect,des)
{
    des=val[sect,"des"];
    return (0.002 * (200-val[sect,"eff"]-100*(des=="+" || des=="=") + 200*(des=="-") + 12700*(des=="^")));
}


# find cost to move from sect from to sect to.  cost is optional array
# added is a local scalar
function pathcost_1(from,to,cost,  added,i)
{

  if (to in cost) return cost[to];

  for (i in sects) if (!(i in cost)) {
    x=val[i,"x"];
    y=val[i,"y"];

    bestcost=9999;

    tmp= (x+2) "," y;
    if ((tmp in cost) && cost[tmp]<bestcost) bestcost=cost[tmp]; 
    tmp= (x-2) "," y;
    if ((tmp in cost) && cost[tmp]<bestcost) bestcost=cost[tmp]; 
    tmp= (x+1) "," (y+1);
    if ((tmp in cost) && cost[tmp]<bestcost) bestcost=cost[tmp]; 
    tmp= (x+1) "," (y-1);
    if ((tmp in cost) && cost[tmp]<bestcost) bestcost=cost[tmp]; 
    tmp= (x-1) "," (y+1);
    if ((tmp in cost) && cost[tmp]<bestcost) bestcost=cost[tmp]; 
    tmp= ((x-1) "," (y-1));
    if ((tmp in cost) && cost[tmp]<bestcost) bestcost=cost[tmp]; 

    if (bestcost<9999) { cost[i]=bestcost+movcost(i); added++; }
  }

  if (added==0) return 9999;

  return pathcost_1(from,to,cost);
}

function pathcost(from,to,   cost,  mcost,i)
{
  if ((from,to) in bestpath) return bestpath[from,to];
  cost[from]=0;
  return pathcost_1(from,to,cost);
}

# moving in the other direction
function invcost(from,to,cost)
{
  if ((from in cost) && (to in cost)) 
    return cost[to]-movcost(to)+movcost(from);
  return 9999;
}


#
#	Simulates a production update (no delivery or distribution)
#	( Requires valid neweff data and prod data )
#
function update( sect)
{
  for (sect in sects) {
    if (!nofood) {
      eat = (val[sect,"civ"]+val[sect,"mil"]+val[sect,"uw"])*etu*eatrate;
      eat += (val[sect,"civ"]*obrate + val[sect,"uw"]*uwbrate)*etu*babyeat;
      val[sect,"food"] -= int(eat);
      net["food"] -= eat;
      delta["food"] -= eat;
      consume["food"] -= eat;
    }
    if (val[sect,"food"]< -1) {
      printf("** Warning: need %d more food in %s **\n",-val[sect,food],sect);
      val[sect,"work"]=0;
      val[sect,"civ"] /= 2;
      val[sect,"mil"] /= 2;
      val[sect,"uw"] /= 2;
    } else {
      val[sect,"civ"] += int(val[sect,"civ"]*etu*obrate);
      val[sect,"uw"] += int(val[sect,"uw"]*etu*uwbrate);
    }
    if (val[sect,"civ"] > maxpop) val[sect,"civ"]=maxpop;
    if (val[sect,"uw"] > maxpop) val[sect,"uw"]=maxpop;
  }

  for (sect in neweff) {
    val[sect,"des"]=newdes[sect];
    val[sect,"eff"]=neweff[sect];
  }
  for (sect in will)
    if (will[sect]+val[sect,make[sect]] > 999)
      val[sect,make[sect]] = 999;
    else {
      val[sect,make[sect]] += will[sect];
  }
  for (sect in use1) val[sect,use1[sect]] -= amt1[sect];
  for (sect in use2) val[sect,use2[sect]] -= amt2[sect];
  for (sect in use3) val[sect,use3[sect]] -= amt3[sect];
}


#
#	Two functions to simulate distribution
#
function distexport( i,sect,dest,expcost,comm,thresh,maxtomove,mobtomove,ware)
{
  for (i=0;i<dsects;i++) {
    sect=order[i];
    dest=val[sect,"dist_x"] "," val[sect,"dist_y"];
    if (dest!=sect) {
      expcost=pathcost(sect,dest)/4;
      ware=((val[sect,"des"]=="w" && val[sect,"eff"]>=60) ||
	    (val[dest,"des"]=="w" && val[dest,"eff"]>=60));
      for (comm in weight) {
	thresh = val[sect,substr(comm,1,1) "_dist"];
	if (thresh && val[sect,comm]>thresh) {
	  maxtomove = val[sect,comm]-thresh;
	  mobtomove = maxtomove*expcost;
	  if (ware) mobtomove /= packing[comm];
	  if (comm=="bar" && val[sect,"des"]=="b" && val[source,"eff"]>=60)
	    mobtomove/=4;
	  if (mobtomove > val[sect,"mob"]) {
	    maxtomove *= val[sect,"mob"]/mobtomove;
	    mobtomove = val[sect,"mob"];
	  }
	  val[dest,comm] += maxtomove;
	  imported[dest,comm] += maxtomove;
	  exported[sect,comm] += maxtomove;
	  val[sect,comm] -= maxtomove;
	  val[sect,"mob"] -= mobtomove;
	  interesting[dest]=1;
	  interesting[sect]=1;
	}
      }
    }
  }
}

function distimport(i,sect,source,expcost,comm,thresh,maxtomove,mobtomove,ware)
{
  for (i=0;i<dsects;i++) {
    sect=order[i];
    source=val[sect,"dist_x"] "," val[sect,"dist_y"];
    if (source!=sect) {
      impcost=pathcost(source,sect)/4;
      ware=((val[sect,"des"]=="w" && val[sect,"eff"]>=60) ||
	    (val[source,"des"]=="w" && val[source,"eff"]>=60));
      for (comm in weight) {
	thresh = val[sect,substr(comm,1,1) "_dist"];
	if (thresh && val[sect,comm]<thresh) {
	  maxtomove = thresh - val[sect,comm];
	  if (maxtomove>val[source,comm]) maxtomove=val[source,comm];
	  mobtomove = maxtomove*impcost;
	  if (ware) mobtomove /= packing[comm];
	  if (comm=="bar" && val[source,"des"]=="b" && val[source,"eff"]>=60)
	    mobtomove/=4;
	  if (mobtomove > val[source,"mob"]) {
	    maxtomove *= val[source,"mob"]/mobtomove;
	    mobtomove = val[source,"mob"];
	  }
	  val[sect,comm] += maxtomove;
	  imported[sect,comm] += maxtomove;
	  exported[source,comm] += maxtomove;
	  val[source,comm] -= maxtomove;
	  val[source,"mob"] -= mobtomove;
	  interesting[source]=1;
	  interesting[sect]=1;
	}
      }
    }
  }
}



function shipupdate( i,n,name,sect,wf,avail,wpf,crew) {
  for (i in shiporder) {
    n=shiporder[i];
    name=shipname[n];
    sect=shipsect[n];

# repair ship
    wpf=20+shiplcm[name]+shiphcm[name]*2;
    if (shipfir[name]) {
      wf=int(shipcargo[n,"mil"]*etu/2);
      crew="mil";
    } else {
      wf=etu*(int(shipcargo[n,"civ"]/2)+int(shipcargo[n,"mil"]/5));
      crew="civ";
    }

    if ((sect,"des") in val) {
      if (val[sect,"des"]=="h") {
	avail=wf+val[sect,"avail"]*100;
      } else {
	avail = wf/3;
	avail -= etu*(100-((shipcargo[n,crew]*100)/shipcargo[name,crew]))/7;
      }
    }
  
    if (avail<=0) {
      gain = -int(-avail/wpf);
      if (gain<0) {
	printf("** Warning: ship #%d will lose %d efficiency\n",n,-gain);
      }
      shipeff[n] += gain;
    }

    left=100-shipeff[n];
    gain=int(avail/wpf);
    if (gain>maxshipeff) gain=maxshipeff;
    if (gain>left) gain=100-shipeff[n];

    if (((sect,"des") in val) && val[sect,"des"]=="h") {
      need=shiplcm[name]*gain/100;
      if (val[sect,"lcm"]<need) gain=val[sect,"lcm"]*100/shiplcm[name];
      need=shiphcm[name]*gain/100;
      if (val[sect,"hcm"]<need) gain=val[sect,"hcm"]*100/shiphcm[name];
      val[sect,"lcm"] -= int(gain*shiplcm[name]/100);
      val[sect,"hcm"] -= int(gain*shiphcm[name]/100);
    } else {
      if (shipeff[n]<80 && gain+shipeff[n]>80) gain=80-shipeff[n];
    }
    shipeff[n] += gain;
    
    if (match(shipcap[name],"oil") && !match(shipcap[name],"oiler")) {
      gain = shipcargo[n,"civ"]*etu*oil[sect]/10000;
      if (shipcargo[n,"oil"]+gain>shipcargo[name,"oil"]) 
	gain=shipcargo[name,"oil"]-shipcargo[n,"oil"];
      oil[sect] -= int(gain/10);
      shipcargo[n,"oil"] += gain;
    }
    if (match(shipcap[name],"fert")) {
      if (newfish) {
	gain = shipcargo[n,"civ"]*etu*fert[sect]/1000;
      } else {
	gain = shipcargo[n,"civ"]*etu*fert[sect]/10000;
      }
      if (shipcargo[n,"oil"]+gain>shipcargo[name,"oil"]) 
	gain=shipcargo[name,"oil"]-shipcargo[n,"oil"];
      shipcargo[n,"food"] += gain;
    }

# feed ship
# plague stuff
# shipmobility
    gain=127-shipmob[n];
    if (gain>maxshipmob) gain=maxshipmob;

    if (shipfu[name]) {
      if (shipfuel[n]*10/shipfu[name]<gain) gain=10*shipfuel[n]/shipfu[name];
      shipfuel[n]-=int(gain*shipfu[name]/10+9);
    }
    shipmob[n] += gain;
  }
}
	

#
#	What to do at the end
#

END {
  for (comm in weight) {
    delete start[comm];
    for (sect in sects) start[comm] += val[sect,comm];
  }
  start["tech"] = tech;
  start["edu"]  = edu;

  totalpop=0;
  for (sect in sects) {
    if (val[sect,"*"]==".") totalpop+=val[sect,"civ"];
  }

  shipupdate();
  update();
  distexport();
  distimport();
  for (i=0;i<dsects;i++) {
    sect=order[i];
    mob = val[sect,"mob"] + etu;
    if (mob>127) mob=127;
    val[sect,"mob"]=mob;
  }

  consume["food"] = int(consume["food"]+0.9999);
  delta["food"] = int(delta["food"]+0.9999);


  # production summaries
  for (sect in will) {
    if (will[sect]+val[sect,make[sect]] > 999) {
      printf(" *** Warning ***  %d surplus %s in %s\n",
	     will[sect]+val[sect,make[sect]]-999,make[sect],sect);
      produce[make[sect]] += 999 - val[sect,make[sect]];
      delta[make[sect]] += 999 - val[sect,make[sect]];
    } else {
      produce[make[sect]] += will[sect];
      delta[make[sect]] += will[sect];
    }
  }
  for (sect in use1) {
    consume[use1[sect]] += amt1[sect];
    delta[use1[sect]] -= amt1[sect];
  }
  for (sect in use2) {
    consume[use2[sect]] += amt2[sect];
    delta[use2[sect]] -= amt2[sect];
  }
  for (sect in use3) {
    consume[use3[sect]] += amt3[sect];
    delta[use3[sect]] -= amt3[sect];
  }

  for (comm in delta) net[comm] = start[comm]+delta[comm];
  
  if ("tech" in delta) {
    if (delta["tech"]>easytech) {
      delta["tech"] = easytech + log(delta["tech"]-easytech+1)/log(logtech);
      delta["tech"] = (int(delta["tech"]*100))/100;
    }
    net["tech"] = start["tech"] + delta["tech"];
  }
  if ("edu" in delta) {
    delta["edu"] *= 4000 * int(etu/12) /totalpop;
    if (delta["edu"]>5) {
      delta["edu"] = (delta["edu"]-5)/(log(1+delta["edu"])/log(4)) + 5;
    }
    net["edu"] = (start["edu"]*(192-etu) + delta["edu"]*etu)/192;
  }


  if (dsects) {
    printf("  com    start        +        -    delta      net\n");
    printf("------------------------------------------------------\n");
    for (comm in net) {
      printf("%5s %8g %8g %8g %8g %8g\n",comm,start[comm],produce[comm],consume[comm],delta[comm],net[comm]);
    }
  }

  for (sect in make) interesting[sect]=1;

  if (dsects)
    printf("\n  sect des eff mob make  imports        exports        commodities\n");
  for (i=0;i<dsects;i++) {
    sect = order[i];
    if (interesting[sect] || showall) {

      printf("%3d,%-3d %s %3d%% %3d",
	     val[sect,"x"],val[sect,"y"],
	     val[sect,"des"],val[sect,"eff"],val[sect,"mob"]);
      if (sect in will) printf(" %4s ",will[sect] substr(make[sect],1,1));
      else printf("      ");

      impstr="";
      for (comm in weight) {
	if (((sect,comm) in imported) && imported[sect,comm]>0)
	  impstr = impstr sprintf(" %d%s",imported[sect,comm],substr(comm,1,1));
      }
      printf("%-14s ",impstr);
      
      expstr="";
      for (comm in weight) {
	if (((sect,comm) in exported) && exported[sect,comm]>0)
	  expstr = expstr sprintf(" %d%s",exported[sect,comm],substr(comm,1,1));
      }
      printf("%-14s ",expstr);
      
      for (comm in weight) if (val[sect,comm]>0)
	printf(" %d%s",val[sect,comm],substr(comm,1,1));
      printf("\n");
    }
  }


  if (ships) printf("\nshp# type               sect  eff  mob fuel cargo\n");
  for (i=0;i<ships;i++) {
    n=shiporder[i];
    name=shipname[n];
    if (shipfuel[n]<maxshipmob*shipfu[name]/10)
      printf("** Warning: ship #%d needs %d fuel\n",
	     n,maxshipmob*shipfu[name]/10-shipfuel[n]);
    crew=shipcargo[n,"civ"]+shipcargo[n,"mil"]+shipcargo[n,"uw"];
    if (crew*etu*eatrate>shipcargo[n,"food"]) {
      printf("** Warning: ship #%d needs %d food\n",
	     n,int(0.9999+crew*etu*eatrate-shipcargo[n,"food"]));
    }
    printf("%4d %-16s %7s %3d%% %3d %3d ",
	   n,name,shipsect[n],shipeff[n],shipmob[n],shipfuel[n]);
    for (comm in weight) {
      if ((n,comm) in shipcargo)
	if (shipcargo[n,comm])
	  printf(" %d%s",shipcargo[n,comm],substr(comm,1,1));
    }
    printf("\n");
  }
}
