C#. Search array of strings for longest element - algorithm

I want to know how I can get out everyone of the the longest persons if there are several with the same length?
If only one person is the longest, then it works fine and the longest person with it´s name will show in MessageBox. But if there are more than one who are the longest, this code will not work...
public partial class Form1 : Form
{
int[] längdArray = new int[5];
string[] namnArray = new string[5];
int namn = 0;
int längd = 0;
public Form1()
{
InitializeComponent();
}
private void btnVisa_Click(object sender, EventArgs e)
{
int längst = 0;
int längdvärdet = 0;
int längdindex = 0;
string name = textBox1.Text;
namnArray[namn] = name;
namn = namn + 1;
textBox1.Clear();
int centimeter = int.Parse(textBox2.Text);
längdArray[längd] = centimeter;
längd++;
textBox2.Clear();
listBox1.Items.Add(name + " " + centimeter + " centimeter ");
if (längd == 5)
{
btnVisa.Enabled = false;
foreach (int antalLängder in längdArray)
{
if (antalLängder > längst)
{
längst = antalLängder;
längdvärdet = längdindex;
}
längdindex++;
}
string test = namnArray[längdvärdet]
MessageBox.Show(" Längsta person är " + test + " som är " + längst + " centimeter lång ");
}

Define behavior you want your app to present when there is more than one person. Should all display, or any one, or other? Try to use object constructions, it's easier to operate on them. C# is an object-oriented language. Put name and length in one structure then use LINQ.

Related

No need to check this! skip

So what I'm trying to do but clearly struggling to execute isSo what I'm trying to do but clearly struggling to execute isSo what I'm trying to do but clearly struggling to execute isSo what I'm trying to do but clearly struggling to execute isSo what I'm trying to do but clearly struggling to execute isSo what I'm trying to do but clearly struggling to execute isSo what I'm trying to do but clearly struggling to execute isSo what I'm trying to do but clearly struggling to execute is
a single line in the text f
import java.util.Scanner;
import java.io.*;
public class hello
{
public static void main(String[] args) throws IOException
{
Scanner Keyboard = new Scanner(System.in);
System.out.print();
String response = Keyboard.nextLine();
File inFile = new File(response);
Scanner route = new Scanner(inFile);
while ()
{
System.out.print(");
String word = Keyboard.next();
String Street = route.next();
String stopNum = route.next();
You are closing your file after you read one "line" (actually, I'm not sure how many lines you're reading - you don't call nextLine). You also aren't parsing the line. Also, I'd prefer a try-with-resources over an explicit close (and many of your variables look like class names). Finally, you need to check if the line matches your criteria. That might be done like,
Scanner keyboard = new Scanner(System.in);
System.out.print("Enter filename >> ");
String response = keyboard.nextLine();
File inFile = new File(response);
System.out.print("Enter tram tracker ID >> ");
String word = keyboard.nextLine(); // <-- read a line. Bad idea to leave trailing
// new lines.
try (Scanner route = new Scanner(inFile)) {
while (route.hasNextLine()) {
String[] line = route.nextLine().split("\\^");
String street = line[0];
String stopNum = line[1];
String trkID = line[2];
String road = line[3];
String suburb = line[4];
if (!trkID.equals(word)) {
continue;
}
System.out.printf("street: %s, stop: %s, id: %s, road: %s, suburb: %s%n",
street, stopNum, trkID, road, suburb);
}
}
Your code print everything in the file.
To print a line with an given ID:
You can first buffer all lines of the file into a ArrayList like this in the main method:
ArrayList<String> lines = new ArrayList<>();
while (route.hasNextLine())
{
lines.add(route.nextLine());
}
Then create a method to find a line with a specific ID:
public static int find(ArrayList information, int ID)
{
String idString = "" + ID;
ListIterator<String> li = information.listIterator();
String currentLine = "";
int index = 0;
while(li.hasNext())
{
currentLine = li.next();
int count = 0;
int index1 = 0;
int index2 = 0;
/*Trying to locate the string between the 2nd and 3rd ^ */
for(int i = 0; i < currentLine.length(); i++)
{
if(currentLine.substring(i, i+1).equals("^"))
{
count++;
if(count == 2)
index1 = i;
else if(count == 3)
{
index2 = i;
break;
}
}
}
if(currentLine.substring(index1+1, index2).equals (idString))
return(index);
index++;
}
//If no such ID found, return -1;
return -1;
}
In the main method:
System.out.println("enter an ID")
int ID = Integer.parseInt(Keyboard.next());
int lineNumber = find(lines, ID);
if(lineNumber == -1)
System.out.println("no information found");
else
System.out.println(lines.get(lineNumber));

how to find average of each column of a datatable using c#

I have a .csv file containing names, roll, subjects correspondingly.I parsed it into a datatable and I calculated the highest mark of each subject. All i want to calculate is the average of each Subject. Can anyone help me with this !!!!!
This was my output.
Highest mark for ComputerScience:
Name : Manoj
Roll Number : 1212334556
Mark : 94
Highest Mark for Biology:
Name : Sandeep
Roll Number : 1223456477
Mark : 90
Highest Mark for Commerce:
Name : BarathRam
Roll Number : 1212345664
Mark : 97
And csv file contains Names,Rollno, Computer, Biology, Commerce.
Now all i need to get is the average of each subject
My code:
static DataTable table;
static void Main(string[] args)
{
StreamReader r = new StreamReader(#"C:\Users\GOPINATH\Desktop\stud1.csv");
string line = r.ReadLine(); //reads first line - column header
string[] part = line.Split(','); //splits the line by comma
createDataTable(part);
//copy from CSV to DataTable<String,String,int,int,int>
while ((line = r.ReadLine()) != null)
{
try
{
part = line.Split(',');
table.Rows.Add(part[0], part[1], Convert.ToInt32(part[2]), Convert.ToInt32(part[3]), Convert.ToInt32(part[4]));
}
catch(Exception e)
{
Console.WriteLine(e.Message);
}
}
r.Close();
int mark1_index = 0, mark2_index = 0, mark3_index = 0; //initailize index value 0 for highest marks
//finding the index of the highest mark for each subject
for(int i=0 ; i<table.Rows.Count ; i++)
{
if (Convert.ToInt32(table.Rows[i][2]) > Convert.ToInt32(table.Rows[mark1_index][2])) //subject1
{
mark1_index = i;
}
if (Convert.ToInt32(table.Rows[i][3]) > Convert.ToInt32(table.Rows[mark2_index][3])) //subject2
{
mark2_index = i;
}
if (Convert.ToInt32(table.Rows[i][4]) > Convert.ToInt32(table.Rows[mark3_index][4])) //subject3
{
mark3_index = i;
}
}
printmark(table,mark1_index, 2);
printmark(table,mark2_index, 3);
printmark(table,mark3_index, 4);
Console.Read();
}
public static void createDataTable(string[] columnName)
{
//create DataTable<String,String,int,int,int>
table = new DataTable();
table.Columns.Add(columnName[0], typeof(String));
table.Columns.Add(columnName[1], typeof(String));
table.Columns.Add(columnName[2], typeof(int));
table.Columns.Add(columnName[3], typeof(int));
table.Columns.Add(columnName[4], typeof(int));
}
public static void printmark(DataTable t, int rowIndex, int columnIndex)
{
Console.WriteLine("Highest mark for " + t.Columns[columnIndex].ColumnName + ":");
Console.WriteLine("\tName: " + (string)t.Rows[rowIndex][0]);
Console.WriteLine("\tRole Number: " + (string)t.Rows[rowIndex][1]);
Console.WriteLine("\tMark: " + (int)t.Rows[rowIndex][columnIndex]);
}
}
}
You could use Linq and do this.
DataTable t;
var average = t.AsEnumerable().Average(x=> x.Field<int>("columnname"));
var result=table.AsEnumerable()
.GroupBy(x=>x.Field<string>("Subject"))
.Select(x=>new
{
Subject=x.Key,
Average=x.Average(x=> x.Field<int>("Mark"));
}).ToList();
In order to calculate the average mark by Subject, first you need to groupby Subject then calculate the average for each group.

Class is taking more computation time if the same input is given over and over

I am working on an algorithm and it seems to be working fine, apart from one thing.
Let me first show you the code and then I will explain what the code does and what the problem is.
public Triple<List<ROUTE>, Integer, List<Customer>> LocalSearch()
{
int noImprLS = 0;
boolean initialization = false;
List<ROUTE> bestRoutes = startRoutes;
int bestProfit = profit;
List<Customer> bestU = u;
List<ROUTE> tempBestRoutes = startRoutes;
int tempBestProfit = profit;
List<Customer> tempBestU = u;
int tempBestDistance = totalDistance(tempBestRoutes);
ELIMINATOR e = new ELIMINATOR(bestU, bestRoutes, bestProfit, initialization, name, rnd);
while (noImprLS <= noImprUB)
{
System.out.print(noImprLS);
boolean improvement = false;
long starttime = System.nanoTime();
double timeE = 0;
for (int i = 1; i <= N; i++)
{
long starttimeE = System.nanoTime();
e = new ELIMINATOR(bestU, bestRoutes, bestProfit, initialization, name, rnd);
timeE = timeE + (System.nanoTime()-starttimeE)/1000000000.0;
POSTPROCEDURE pp = new POSTPROCEDURE(e.getRoutes(), profitRoutes(e.getRoutes()), e.getU(), name);
for (int p = 0; p < pp.getBestSolution().size(); p++)
{
ROUTE r = pp.getBestSolution().get(p);
addToPOOL(r);
}
int tempprofit = pp.getTP();
int tempdistance = pp.getTD();
if (tempprofit > tempBestProfit)
{
tempBestRoutes = pp.getBestSolution();
tempBestProfit = tempprofit;
tempBestU = pp.getU();
tempBestDistance = tempdistance;
}
else if (tempprofit == tempBestProfit)
{
if (tempdistance < tempBestDistance)
{
tempBestRoutes = pp.getBestSolution();
tempBestProfit = tempprofit;
tempBestU = pp.getU();
tempBestDistance = tempdistance;
}
}
}
if (tempBestProfit > bestProfit)
{
// Move to better neighbor
bestRoutes = tempBestRoutes;
bestProfit = tempBestProfit;
bestU = tempBestU;
noImprLS = 0;
improvement = true;
System.out.print(" total profit: " + bestProfit);
}
else if (tempBestProfit == bestProfit)
{
if (totalDistance(tempBestRoutes) < totalDistance(bestRoutes))
{
// Move to better neighbor
bestRoutes = tempBestRoutes;
bestProfit = tempBestProfit;
bestU = tempBestU;
noImprLS = 0;
improvement = true;
System.out.print(" total profit: " + bestProfit + " total distance: " + totalDistance(bestRoutes));
}
}
if (improvement == false)
{
noImprLS++;
}
long endtime = System.nanoTime();
double duration = (endtime - starttime)/1000000000.0;
System.out.print(" duration: " + duration + " timeE: " + timeE + "\n");
}
Explanation
I know that the code is quite lengthy, but it is all quite important. In this code, I am writing an algorithm for the Team Orienteering Problem with Time Windows (extensive case of the Vehicle Routing Problems). My aim is to find a good set of routes with maximum profit. In the example below, bestRoutes and tempBestRoutes consist of 4 different routes, profit (bestProfit/tempBestProfit) is equal to the total profit of these routes respectively, and (temp)bestU is a list of customers that are not included in my route yet.
The problem now is with ELIMINATOR. This method removes and adds some customers. The output of this class is used for PostProcedure that also changes some facts in the routes.
I hope it is kind of clear now what my code is doing. I am considering N neighbourhoods and I will choose the best one. If the best one is not better than my starting solution, I increase noImprLS with one. I keep on considering new nieghbours until my upperbound on the number of consecutive iterations without improvement is met.
Problem
The problem now is that if I have not found a better solution, and hence I keep on inserting the same routes and profit in ELIMINATOR, my computation time increases.
A few examples where duration indicates how long an iteration within the while loop takes, and timeE indicates what the total time of ELIMINATOR in the for loop is. It is clear that ELIMINATOR causees the duration to increase.
0 total profit: 800 duration: 0.486570471 timeE: 0.16644330999999998
0 total profit: 900 duration: 0.431213528 timeE: 0.11342619799999998
0 total profit: 950 duration: 0.444671005 timeE: 0.12090608200000001
0 total profit: 960 duration: 0.519406695 timeE: 0.16836757300000005
0 duration: 0.460473438 timeE: 0.137813155
1 duration: 0.572109775 timeE: 0.30774360900000003
2 duration: 0.698965292 timeE: 0.471859029
3 duration: 0.918376211 timeE: 0.686916669
4 duration: 1.165481175 timeE: 0.92621492
5 duration: 1.326080436 timeE: 1.0874366910000002
6 duration: 2.006102605 timeE: 1.674879135
7 duration: 2.787172112 timeE: 2.4276636639999993
8 duration: 2.042213493 timeE: 1.7967797849999998
9 duration: 2.652985618 timeE: 2.3503671230000003
10 duration: 2.422183993 timeE: 2.1859969810000006
The ELIMINATOR CODE:
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
public class ELIMINATOR extends BASIS
{
private List<Customer> u;
private List<ROUTE> routes;
private int totalprofit;
private Random prob;
public ELIMINATOR(List<Customer> u, List<ROUTE> routes, int profit, boolean initialization, String name, Random rnd)
{
super(name);
this.u = u;
this.routes = routes;
this.totalprofit = profit;
this.prob = rnd;
if (initialization == true)
{
addCustomers();
for (ROUTE route : routes)
{
checkCorrectNess(route, "after adding procedure in eliminator");
}
}
else
{
removeCustomers();
for (ROUTE route : routes)
{
checkCorrectNess(route, "after removing procedure in eliminator");
}
addCustomers();
for (ROUTE route : routes)
{
checkCorrectNess(route, "after removing and adding procedure in eliminator");
}
}
}
public void removeCustomers()
{
double Ph = 0.1;
double Pl = 0.3;
double total_profit = totalprofit;
int num_customers = 0;
// Calculate the total profit and total number of customers in the routes
for(ROUTE route : routes)
{
num_customers = num_customers + (route.getLocations().size()-2);
}
// Calculate average profit
double average_profit = total_profit/num_customers;
// For each customer on each route, determine whether he/she will be removed
for(ROUTE r : routes)
{
List<RouteNode> route = r.getLocations();
int routesize = route.size();
int j = 1;
while (j < routesize-1)
{
boolean removed = false;
RouteNode node = route.get(j);
if (node.customer.getProfit() >= average_profit)
{
if (prob.nextDouble() < Ph)
{
removed = true;
RouteNode node_toberemoved = node;
int index_node = route.indexOf(node);
route.remove(index_node);
u.add(node.customer);
route = removal(route, node_toberemoved, index_node);
r.setLocations(route);
r.setDistance(distanceOneRoute(route));
r.setProfit(profitOneRoute(route));
checkCorrectNess(r, "remove customers eliminator");
}
}
else
{
if (prob.nextDouble() < Pl)
{
removed = true;
RouteNode node_toberemoved = node;
int index_node = route.indexOf(node);
route.remove(index_node);
u.add(node.customer);
route = removal(route, node_toberemoved, index_node);
r.setLocations(route);
r.setDistance(distanceOneRoute(route));
r.setProfit(profitOneRoute(route));
checkCorrectNess(r, "remove customers eliminator");
}
}
if (removed == false)
{
j++;
}
else
{
routesize = route.size();
total_profit = total_profit-node.customer.getProfit();
average_profit = total_profit/num_customers;
}
}
}
totalprofit = profitRoutes(routes);
}
public void addCustomers()
{
List<Customer> u_copy = new ArrayList<Customer>(u);
List<Customer> u_temp = new ArrayList<Customer>(u);
for (Customer c : u_temp)
{
boolean added = false;
for (ROUTE r : routes)
{
checkCorrectNess(r, "add customers eliminator");
if (added == true)
{
break;
}
Customer customer = c;
u_copy.remove(c);
List<RouteNode> route = r.getLocations();
for (int i = 0; i < route.size()-1; i++)
{
RouteNode possibleNode = new RouteNode();
possibleNode.customer = customer;
List<Integer> distances = calculateDistances(route.get(i), possibleNode, route.get(i+1));
// Calculate shift for customer under consideration
int arrivalTime = route.get(i).timeStartService+ route.get(i).customer.getService() + distances.get(0);
int wait = Math.max(0, customer.getOpeningTW()-arrivalTime);
int serviceDuration = customer.getService();
int shift = distances.get(0) + wait + serviceDuration + distances.get(2) - distances.get(1);
// Determine Start Service
int startServiceTime = Math.max(customer.getOpeningTW(), arrivalTime);
// Obtain waiting time of next customer
int waiting_next = route.get(i+1).wait;
// Obtain MaxShift of next customer
int maxShift = route.get(i+1).maxShift;
if (shift <= (waiting_next + maxShift) & startServiceTime <= customer.getClosingTW() )
{
// Customer can be inserted
added = true;
RouteNode newNode = new RouteNode();
newNode.customer = customer;
newNode.arrivalTime = arrivalTime;
newNode.timeStartService = startServiceTime;
newNode.shift = shift;
newNode.wait = wait;
int pos_insertion = i + 1;
route = ADD(route, newNode, pos_insertion);
r.setLocations(route);
r.setDistance(distanceOneRoute(route));
r.setProfit(profitOneRoute(route));
checkCorrectNess(r, "add customers eliminator");
// exit the last for loop
break;
}
}
}
if (added == false)
{
u_copy.add(c);
}
}
u = u_copy;
totalprofit = profitRoutes(routes);
}
/**
* Returns list of unvisited customers
* #return
*/
public List<Customer> getU()
{
return u;
}
/**
* Returns list of routes
* #return
*/
public List<ROUTE> getRoutes()
{
return routes;
}
}

Best way to handle awt.Image buffering in JavaFX

I have a class that takes a String parameter and performs a google search, then it gets the ten images and puts them in an array, that is then handled by another method in the same class. Using Javafx.scene.image would probably allow me to implement the buffering progress easily, but there is a bug with JavaFX Image, that misinterprets the color encoding of normal Images, and saves a weird looking image to the hard drive, so I just decided to use awt.Image.
This is the image search class:
public class GoogleCustomSearch {
static String key = //custom google id;
static String cx = // also a custom google id;
static String searchType = "image";
static java.awt.Image[] publicImageArray;
public static java.awt.Image[] Search(String searchParameter,int start) throws IOException, URISyntaxException{
String formatedText = URLEncoder.encode(searchParameter,"UTF-8");
URL url = new URL("https://www.googleapis.com/customsearch/v1?" + "key=" +key + "&cx=" +cx + "&q=" +formatedText + "&searchType=" +searchType +"&imgSize=medium" + "&start=" + start + "&num=10");
System.out.println(url);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("Accept", "application/json");
BufferedReader br = new BufferedReader(new InputStreamReader( ( conn.getInputStream() ) ) );
GResults results = new Gson().fromJson(br, GResults.class);
java.awt.Image [] imageArray = new java.awt.Image[10];
//JProgressBar prb = new JProgressBar();
//MediaTracker loadTracker = new MediaTracker(prb);
for(int i = 0; i<10; i++){
try {
imageArray[i] = ImageIO.read(new URL(results.getLink(i)));
}catch (java.io.IOException e){
imageArray[i] = ImageIO.read(new File("C:\\Users\\FILIP.D\\IdeaProjects\\Manual_Artwork\\src\\MAT - NoImage.jpg"));
}
}
conn.disconnect();
return imageArray;
}
public static BufferedImage getImage(String searchPar, int index, boolean newSearch) throws IOException, URISyntaxException {
int adaptedIndex;
int start;
BufferedImage bimage;
if(index<10){
adaptedIndex = index;
start = 1;
}else if (index<20){
start = 11;
adaptedIndex = index % 10;
if(index == 10){
publicImageArray = new java.awt.Image[10];
publicImageArray = Search(searchPar,start);
}
}else if(index < 30){
start = 21;
adaptedIndex = index % 10;
if (index == 20) {
publicImageArray = new java.awt.Image[10];
publicImageArray = Search(searchPar,start);
}
}else{
adaptedIndex = index % 10;
start = 21; //ovo ce posle 30 da ga vrti u loop prvih 10
}
if(newSearch){
publicImageArray = new java.awt.Image[10];
publicImageArray = Search(searchPar,start);
return bimage = (BufferedImage) publicImageArray[adaptedIndex];
}else{
return bimage = (BufferedImage) publicImageArray[adaptedIndex];
}
}
public static RenderedImage getLiveImage (int index){
return (RenderedImage) publicImageArray[index % 10];
}
}
And this is the snippet of the main GUI class that just handles opening the new image in the array
private void nextImageResult() throws IOException, URISyntaxException {
if(imgNr == -1){
imgNr++;
changeImage(SwingFXUtils.toFXImage(GoogleCustomSearch.getImage(oppenedTrack.getArtistName() + "+" + oppenedTrack.getTrackName(),imgNr,true),null));
}else{
imgNr++;
changeImage(SwingFXUtils.toFXImage(GoogleCustomSearch.getImage(oppenedTrack.getArtistName() + "+" + oppenedTrack.getTrackName(),imgNr,false),null));
}
}
To summarise, I need a proper way to show a progress bar in the place of the image before it loads, and it needs not to hang the UI, for which I can use Task. I can optimise the loading of the array with MediaTracker, so it can prioritize loading the first few images first.

replacing a submitlink with indicatingAjaxButton

I have a form with a submitbutton which will get results from a database and updates a listview based on these results. If there is no result, a feedback message is shown. This all works fine.
Now I want to replace the submitlink with an IndicatingAjaxButton, so the user can see something happening when getting the result takes a long time.
The basic idea is this:
IndicatingAjaxButton submitLink = new IndicatingAjaxButton("submit", form) {
private static final long serialVersionUID = -4306011625084297054L;
#Override
public void onSubmit(AjaxRequestTarget target, Form<?> form) {
Integer hourFrom = 0;
Integer hourTo = 0;
Integer minuteFrom = 0;
Integer minuteTo = 0;
hourFrom = Integer.parseInt(hour_from.getModelObject());
hourTo = Integer.parseInt(hour_to.getModelObject());
minuteFrom = Integer.parseInt(minute_from.getModelObject());
minuteTo = Integer.parseInt(minute_to.getModelObject());
Calendar from = Calendar.getInstance();
Calendar to = Calendar.getInstance();
Date dateFrom = date_from.getModelObject();
Date dateTo = date_to.getModelObject();
from.setTime(dateFrom);
to.setTime(dateTo);
from.set(Calendar.HOUR, hourFrom);
from.set(Calendar.MINUTE, minuteFrom);
to.set(Calendar.HOUR, hourTo);
to.set(Calendar.MINUTE, minuteTo);
if (topicQueueSelect.getModelObject() == null) {
error("Please select a message name.");
getSession().setAttribute("error", "");
}
if (to.before(from)) {
error("Date to must be after date from.");
getSession().setAttribute("error", "");
}
cal.setTimeInMillis(System.currentTimeMillis());
if (from.after(cal)) {
error("Date from must be in the past.");
getSession().setAttribute("error", "");
}
if (getSession().getAttribute("error") != null) {
getSession().removeAttribute("error");
return;
}
page.setModelObject(1);
List<Search> searchFields = (List<Search>) searchFieldsField
.getModelObject();
messageKeyDataList = messageController.search(
topicQueueSelect.getModelObject(), searchFields,
from.getTime(), to.getTime(),
maxResults.getModelObject(), page.getModelObject(),
sortorder);
if (messageKeyDataList.size() == 0) {
info("Search criteria didn't produce any results.");
result.setList(messageKeyDataList);
resultContainer.setVisible(false);
return;
}
resultContainer.setVisible(true);
resultSize = messageController.getResultSize();
int pages = (int) Math.ceil((float) resultSize
/ maxResults.getModelObject());
ArrayList<Integer> pageNumbers = new ArrayList<Integer>();
for (int n = 1; n <= pages; n++) {
pageNumbers.add(n);
}
page.setChoices(pageNumbers);
pageunder.setChoices(pageNumbers);
showing.setDefaultModelObject("Showing 1 to "
+ messageKeyDataList.size() + " out of " + resultSize
+ " messages");
lastSearch.put("topicQueue", topicQueueSelect.getModelObject());
lastSearch.put("searchFields", searchFields);
lastSearch.put("from", from.getTime());
lastSearch.put("to", to.getTime());
lastSearch.put("maxResults", maxResults.getModelObject());
result.setList(messageKeyDataList);
target.add(feedback);
}
};
The SubmitLink does show me either the ResultView with the new list, or the info message, the IndicatingAjaxButton doesn't. I know the form submit is called, because the system.out is being printed.
Any suggestions on this?
SubmitLink is non-Ajax component. Using it will repaint the whole page!
IndicatingAjaxButton is an Ajax component. You need to use the passed AjaxRequestTarget to add components which should be repainted with the Ajax response. For example the FeedbackPanel should be added to the AjaxRequestTarget.
I found that I had to do setOutputMarkupPlaceholderTag(true) on both the resultContainer and the feedback. After that adding them to the requesttarget works as expected.

Resources