+ if (defined $qa->{"R$f->{UidShort}"} &&
+ !defined $qa->{"T$f->{UidShort}"}) {
+ $f->{Suppress}= 1;
+ }
+
+}
+</%perl>
+
+% my $optimise= $specific && !$confusing && @islandids>1;
+% if (!$optimise) {
+
+<p>
+% if (@islandids<=1) {
+Route is trivial.
+% }
+% if (!$specific) {
+Route contains archipelago(es), not just specific islands.
+% }
+% if ($confusing) {
+Route is complex - it visits the same island several times
+and isn't a simple loop.
+% }
+Therefore, optimal trade pattern not calculated.
+
+% } else { # ========== OPTMISATION ==========
+<%perl>
+
+my $cplex= "
+Maximize
+
+ totalprofit:
+ ".(join " +
+ ", map {
+ sprintf "%.20f %s", $_->{ExpectedUnitProfit}, $_->{Var}
+ } @flows)."
+
+Subject To
+";
+
+my %avail_csts;
+foreach my $flow (@flows) {
+ if ($flow->{Suppress}) {
+ $cplex .= "
+ $flow->{Var} = 0
+";
+ next;
+ }
+ foreach my $od (qw(org dst)) {
+ my $cstname= join '_', (
+ 'avail',
+ $flow->{'commodid'},
+ $od,
+ $flow->{"${od}_id"},
+ $flow->{"${od}_price"},
+ $flow->{"${od}_stallid"},
+ );
+
+ push @{ $avail_csts{$cstname}{Flows} }, $flow->{Var};
+ $avail_csts{$cstname}{Qty}= $flow->{"${od}_qty_agg"};
+ }
+}
+foreach my $cstname (sort keys %avail_csts) {
+ my $c= $avail_csts{$cstname};
+ $cplex .= "
+ ". sprintf("%-30s","$cstname:")." ".
+ join("+", @{ $c->{Flows} }).
+ " <= ".$c->{Qty}."\n";
+}
+
+$cplex.= "
+Bounds
+ ".(join "
+ ", map { "$_->{Var} >= 0" } @flows)."
+
+End
+";
+
+if ($qa->{'debug'}) {
+</%perl>
+<pre>
+<% $cplex |h %>
+</pre>
+<%perl>
+}
+
+{
+ my $input= pipethrough_prep();
+ print $input $cplex or die $!;
+ my $output= pipethrough_run_along($input, undef, 'glpsol',
+ qw(glpsol --cpxlp /dev/stdin -o /dev/stdout));
+ print "<pre>\n" if $qa->{'debug'};
+ my $found_section= 0;
+ my $glpsol_out= '';
+ while (<$output>) {
+ $glpsol_out.= $_;
+ print encode_entities($_) if $qa->{'debug'};
+ if (m/^\s*No\.\s+Column name\s+St\s+Activity\s/) {
+ die if $found_section>0;
+ $found_section= 1;
+ next;
+ }
+ next unless $found_section==1;
+ next if m/^[- ]+$/;
+ if (!/\S/) {
+ $found_section= 2;
+ next;
+ }
+ my ($ix, $qty) =
+ m/^\s*\d+\s+f(\d+)\s+\S+\s+(\d+)\s/ or die "$_ ?";
+ my $flow= $flows[$ix] or die;
+ $flow->{OptQty}= $qty;
+ $flow->{OptProfit}= $flow->{'unitprofit'} * $qty;
+ $flow->{OptCapital}= $flow->{OptQty} * $flow->{'org_price'};
+ }
+ print "</pre>\n" if $qa->{'debug'};
+ my $prerr= "\n=====\n$cplex\n=====\n$glpsol_out\n=====\n ";
+ pipethrough_run_finish($output,$prerr);
+ die $prerr unless $found_section;
+};
+
+$addcols->({ DoReverse => 1 }, qw(
+ OptQty
+ ));
+$addcols->({ Total => 0, DoReverse => 1 }, qw(
+ OptCapital OptProfit
+ ));
+
+</%perl>
+
+% } # ========== OPTIMISATION ==========
+
+% my %ts_sortkeys;
+% {
+% my $cdspan= $qa->{ShowStalls} ? ' colspan=2' : '';
+% my $cdstall= $qa->{ShowStalls} ? '<th>Stall</th>' : '';
+<table id="trades" rules=groups>
+<colgroup span=1>
+<colgroup span=2>
+<% $qa->{ShowStalls} ? '<colgroup span=2>' : '' %>
+<colgroup span=1>
+<colgroup span=2>
+<colgroup span=2>
+<colgroup span=2>
+<colgroup span=3>
+% if ($optimise) {
+<colgroup span=3>
+% }
+<tr class="spong">
+<th>
+<th<% $cdspan %>>Collect
+<th<% $cdspan %>>Deliver
+<th>
+<th colspan=2>Collect
+<th colspan=2>Deliver
+<th colspan=2>Profit
+<th colspan=3>Max
+% if ($optimise) {
+<th colspan=3>Planned
+% }
+
+<tr>
+<th>
+<th>Island <% $cdstall %>
+<th>Island <% $cdstall %>
+<th>Commodity
+<th>Price
+<th>Qty
+<th>Price
+<th>Qty
+<th>Margin
+<th>Unit
+<th>Qty
+<th>Capital
+<th>Profit
+% if ($optimise) {
+<th>Qty
+<th>Capital
+<th>Profit
+% }
+% }
+
+<tr id="trades_sort">
+% foreach my $col (@cols) {
+<th>
+% }
+
+% foreach my $flowix (0..$#flows) {
+% my $flow= $flows[$flowix];
+% my $rowid= "id_row_$flow->{UidShort}";
+<tr id="<% $rowid %>" class="datarow<% $flowix & 1 %>">
+<td><input type=hidden name=R<% $flow->{UidShort} %> value="">
+ <input type=checkbox name=T<% $flow->{UidShort} %> value=""
+ <% $flow->{Suppress} ? '' : 'checked' %> >
+% foreach my $ci (1..$#cols) {
+% my $col= $cols[$ci];
+% my $v= $flow->{$col->{Name}};
+% $col->{Total} += $v if defined $col->{Total};
+% $v='' if !$col->{Text} && !$v;
+% my $sortkey= $col->{SortColKey} ?
+% $flow->{$col->{SortColKey}} : $v;
+% $ts_sortkeys{$ci}{$rowid}= $sortkey;
+<td <% $col->{Text} ? '' : 'align=right' %>><% $v |h %>
+% }
+% }
+<tr id="trades_total">
+<th>
+<th colspan=2>Total
+% foreach my $ci (3..$#cols) {
+% my $col= $cols[$ci];
+<td align=right>
+% if (defined $col->{Total}) {
+<% $col->{Total} |h %>
+% }
+% }
+</table>
+
+<& tabsort, cols => \@cols, table => 'trades', rowclass => 'datarow',
+ throw => 'trades_sort', tbrow => 'trades_total' &>
+<&| script &>
+ ts_sortkeys= <% to_json_protecttags(\%ts_sortkeys) %>;
+ function all_onload() {
+ ts_onload__trades();
+ }
+ window.onload= all_onload;
+</&script>
+
+<input type=submit name=update value="Update">
+
+% if ($optimise) { # ========== TRADING PLAN ==========
+%
+% my $iquery= $dbh->prepare('SELECT islandname FROM islands
+% WHERE islandid = ?');
+% my %da_ages;
+% my $total_total= 0;
+%
+<h1>Voyage trading plan</h1>
+<table rules=groups>