college/ss2012/AlgoAnim/Teil 3/RussischeBauernmultiplikation.java
2012-05-14 15:03:28 +02:00

537 lines
16 KiB
Java

package part2;
import java.awt.Color;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
import de.ahrgr.animal.kohnert.asugen.Font;
import algoanim.animalscript.AnimalScript;
import algoanim.primitives.SourceCode;
import algoanim.primitives.StringMatrix;
import algoanim.primitives.generators.Language;
import algoanim.properties.AnimationPropertiesKeys;
import algoanim.properties.RectProperties;
import algoanim.properties.SourceCodeProperties;
import algoanim.properties.TextProperties;
import algoanim.util.Coordinates;
import algoanim.util.Offset;
import algoanim.util.Timing;
/**
* @author Michael Scholz, Ulf Gebhardt
* @version 0.1 2012-05-15
*
*/
public class RussischeBauernmultiplikation {
//Feedback aus †bung 2:
/*Es handelt sich bereits um eine anschauliche Animation.
* Dennoch fallen mir ein Paar Details ein, die ihr im Laufe der nŠchsten Aufgaben
* noch einbauen solltet.
*
* Dass die Aufrufkette am Ende der Animation Ÿber die return-Statements wieder
* nach oben zurŸckverfolgt wird, ist nicht unbedingt intuitiv ersichtlich.
* Das kšnnte man noch erlŠutern oder vielleicht auch graphisch verdeutlichen.
* Die im letzten Schritt einfach da stehende Zerlegung in Zweierpotenzen kšnnte
* ebenfalls animiert dargestellt werden.
*
*
* -> Aufrufkette
* -> Zerlegung in Zweierpotenzen animieren
*
* */
/**
* The concrete language object used for creating output
*/
private Language lang;
/**
* The info text as a SourceCode object (first slide)
*/
private SourceCode info = null;
/**
* The statement text as a SourceCode object (last several slides)
*/
private SourceCode statement = null;
/**
* The sourceCode
*/
private SourceCode sc = null;
/**
* The tables
*/
StringMatrix tb1 = null;
StringMatrix tb2 = null;
/**
* int-array with the values of b (only for animation)
*/
public int[] bArray = null;
/**
* recursionArray
*/
public int[] recursionArray = null;
private static final String DESCRIPTION =
"Russische Bauernmultiplikation\n" +
"Das hier vorgestellte Verfahren eignet sich fŸr die Multiplikation zweier ganzer Zahlen.\n" +
"Die Funktionsweise lŠsst sich in die folgenden fŸnf Schritte gliedern:\n" +
"1. Schreibe die beiden zu multiplizierenden Zahlen nebeneinander.\n" +
"2. Die linke Zahl wird halbiert (Reste werden abgerundet), die rechte Zahl wird verdoppelt.\n" +
"Die beiden berechneten Zahlen werden in die darauffolgende Zeile geschrieben.\n" +
"3. Schritt 2 wird solange wiederholt, bis in der linken Spalte eine 1 steht.\n" +
"4. Nun streicht man alle Zeilen, in denen die linke Zahl gerade ist.\n" +
"5. Schlussendlich werden alle Ÿbrigen Zahlen der rechten Spalte addiert.";
private static final String SOURCE_CODE = "public int russe(int a, int b){" // 0
+ "\n if(a == 1){" // 1
+ "\n return b;" // 2
+ "\n }" // 3
+ "\n if(a % 2 == 1){" // 4
+ "\n return b + russe(a/2, b*2);" // 5
+ "\n }else{" // 6
+ "\n return russe(a/2, b*2);" // 7
+ "\n }" // 8
+ "\n }"; // 9
/**
* Default constructor
* @param l the conrete language object used for creating output
*/
public RussischeBauernmultiplikation(Language l) {
// Store the language object
lang = l;
// This initializes the step mode. Each pair of subsequent steps has to
// be divdided by a call of lang.nextStep();
lang.setStepMode(true);
}
/**
* Builds the information text for the first page in the animation
*/
public void generateInfoText(){
//create first page of animation (info text) as a code group
SourceCodeProperties infoProps = new SourceCodeProperties();
infoProps.set(AnimationPropertiesKeys.FONT_PROPERTY, new java.awt.Font("SansSerif", Font.SANSSERIF, 16));
this.info = lang.newSourceCode(new Coordinates(10, 75), "info", null, infoProps);
this.info.addCodeLine("Das hier vorgestellte Verfahren eignet sich fuer die Multiplikation zweier ganzer Zahlen.", null, 0, null);
this.info.addCodeLine("Die Funktionsweise laesst sich in die folgenden fuenf Schritte gliedern:", null, 0, null);
this.info.addCodeLine("1. Schreibe die beiden zu multiplizierenden Zahlen nebeneinander.", null, 1, null);
this.info.addCodeLine("2. Die linke Zahl wird halbiert (Reste werden abgerundet), die rechte Zahl wird verdoppelt.", null, 1, null);
this.info.addCodeLine(" Die beiden berechneten Zahlen werden in die darauffolgende Zeile geschrieben.", null, 2, null);
this.info.addCodeLine("3. Schritt 2 wird solange wiederholt, bis in der linken Spalte eine 1 steht.", null, 1, null);
this.info.addCodeLine("4. Nun streicht man alle Zeilen, in denen die linke Zahl gerade ist.", null, 1, null);
this.info.addCodeLine("5. Schlussendlich werden alle uebrigen Zahlen der rechten Spalte addiert.", null, 1, null);
}
/**
* Builds the header which is shown during the whole animation
*/
public void generateHeader(){
//head
TextProperties textProperties = new TextProperties();
textProperties.set(AnimationPropertiesKeys.COLOR_PROPERTY, Color.BLACK);
textProperties.set(AnimationPropertiesKeys.DEPTH_PROPERTY, 1);
textProperties.set(AnimationPropertiesKeys.FONT_PROPERTY, new java.awt.Font("Serif", Font.SANSSERIF, 24));
lang.newText(new Coordinates(11, 15), "Russische Bauernmultiplikation", "header", null, textProperties);
//header background
RectProperties rectProperties = new RectProperties();
rectProperties.set(AnimationPropertiesKeys.FILL_PROPERTY, Color.GRAY);
rectProperties.set(AnimationPropertiesKeys.FILLED_PROPERTY, true);
rectProperties.set(AnimationPropertiesKeys.DEPTH_PROPERTY, 2);
lang.newRect(new Offset(-5, -5, "header",
AnimalScript.DIRECTION_NW), new Offset(5, 5, "header",
AnimalScript.DIRECTION_SE), "headerBackground", null,
rectProperties);
}
/**
* Builds the SourceCode
*/
public void generateSourceCode(){
//set the visual properties for the source code
SourceCodeProperties scProps = new SourceCodeProperties();
scProps.set(AnimationPropertiesKeys.HIGHLIGHTCOLOR_PROPERTY, Color.RED);
//initialize source code object and add code lines
this.sc = lang.newSourceCode(new Coordinates(10, 147), "sourceCode", null, scProps);
this.sc.addCodeLine("public int russe(int a, int b){", null, 0, null); // 0
this.sc.addCodeLine("if(a == 1){", null, 1, null); // 1
this.sc.addCodeLine("return b;", null, 2, null); // 2
this.sc.addCodeLine("}", null, 1, null); // 3
this.sc.addCodeLine("if(a % 2 == 1){", null, 1, null); // 4
this.sc.addCodeLine("return b + russe(a/2, b*2);", null, 2, null); // 5
this.sc.addCodeLine("}else{", null, 1, null); // 6
this.sc.addCodeLine("return russe(a/2, b*2);", null, 2, null); // 7
this.sc.addCodeLine("}", null, 1, null); // 8
this.sc.addCodeLine("}", null, 0, null); // 9
}
/**
* calculate needed lines -> animation is more dynamic
* @param a
* @param b
* @return int number of needed lines
*/
public int calculateLines(int a, int b){
//calculate needed lines -> animation is more dynamic
int aTemp = a;
int lines = 3;
while(aTemp != 1){
aTemp = aTemp / 2;
lines++;
}
//initialize the bArray und the recursionArray
this.bArray = new int[lines-1];
this.recursionArray = new int[lines-1];
return lines;
}
/**
* Builds the table
* @param a
* @param b
* @param lines
*/
public void generateTable(int a, int b, int lines){
//create String[][] data array for tb1
String[][] tb1Data = new String[lines][4];
//fill all fields with ""
for(int i = 0; i < tb1Data.length; i++){
for(int j = 0; j < tb1Data[i].length; j++){
tb1Data[i][j] = "";
}
}
//fill the known fields
tb1Data[0][0] = "A-Seite";
tb1Data[0][1] = "B-Seite";
tb1Data[0][2] = "addieren";
tb1Data[0][3] = "Summe";
tb1Data[1][0] = a+"";
tb1Data[1][1] = b+"";
//create table tb1
this.tb1 = lang.newStringMatrix(new Coordinates(270, 100), tb1Data, "tb1", null);
this.tb1.changeColor(AnimationPropertiesKeys.FILL_PROPERTY, Color.WHITE, null, null);
}
/**
* builds the statement for the last several slides
*/
public void generateStatement(){
//final statement
SourceCodeProperties statementProps = new SourceCodeProperties();
statementProps.set(AnimationPropertiesKeys.FONT_PROPERTY, new java.awt.Font("SansSerif", Font.SANSSERIF, 16));
this.statement = lang.newSourceCode(new Coordinates(10, 75), "statement", null, statementProps);
this.statement.addCodeLine("Erklaerung:", null, 0, null);
this.statement.addCodeLine("Die Idee des Verfahrens kann man mit Hilfe des Dualsystems verdeutlichen.", null, 0, null);
this.statement.addCodeLine("Hierbei wird eine Zahl in ihre Zweierpotenzen zerlegt.", null, 0, null);
this.statement.addCodeLine("", null, 0, null); //empty line for vertical space
//STEP
lang.nextStep();
this.statement.addCodeLine("82 * 27 = 82 * (2^0 + 2^1 + 0 * 2^2 + 2^3 + 2^4 )", null, 0, null);
//STEP
lang.nextStep();
this.statement.addCodeLine("= 82 * 2^0 + 82 * 2^1 + 82 * 0 + 82 * 2^3 + 82 * 2^4", null, 4, null);
//STEP
lang.nextStep();
this.statement.addCodeLine("= 82 + 164 + 0 + 656 + 1312", null, 4, null);
//STEP
lang.nextStep();
this.statement.addCodeLine("= 2214", null, 4, null);
}
/**
* Draw the table tb2
* @param bArrayLength
*/
public void drawTb2(int bArrayLength){
//create String[][] data array for tb2
String[][] tb2Data = new String[2][1];
tb2Data[0][0] = "Rekursion aufloesen:";
String tmp = "";
for(int i = 1; i < bArrayLength; i++){
if(this.bArray[i] != 0){
tmp = tmp+this.bArray[i];
if(i+1 != bArrayLength){
tmp = tmp+"+";
}
}
}
tb2Data[1][0] = tmp;
//create table tb2
if(this.tb2 != null){
this.tb2.hide(); //hide old version of tb2
}
this.tb2 = lang.newStringMatrix(new Offset(0, 10, this.tb1, AnimalScript.DIRECTION_SW), tb2Data, "tb2", null);
this.tb2.changeColor(AnimationPropertiesKeys.FILL_PROPERTY, Color.WHITE, null, null);
}
/**
* Generates the animation of the recursion flow
*/
public void generateRecursion(){
//draw tb2
drawTb2(this.bArray.length);
//STEP
lang.nextStep();
int newLineNumber = 0;
int oldLineNumber = 0;
sc.unhighlight(2); //"return b"
for(int i = this.bArray.length-1; i > 1; i--){
oldLineNumber = newLineNumber;
if(this.recursionArray[i-1] == 0){
newLineNumber = 5;
}else{
newLineNumber = 7;
}
sc.unhighlight(oldLineNumber);
//STEP
lang.nextStep();
sc.highlight(newLineNumber);
//update tb2
this.bArray[i-1] = this.bArray[i-1] + this.bArray[i];
drawTb2(i);
//STEP
lang.nextStep();
}
sc.unhighlight(oldLineNumber);
}
/**
* logical method: calls the needed methods for generating the animation
* @param a
* @param b
*/
public void multiply(int a, int b){
//generate information text and the header
generateInfoText();
generateHeader();
//STEP
lang.nextStep();
this.info.hide();
//generate SourceCode
generateSourceCode();
//calculate needed lines and generate table
int lines = calculateLines(a, b);
generateTable(a, b, lines);
//STEP
lang.nextStep();
//higlight line 0
this.sc.highlight(0);
russe(a, b, sc, tb1, 1, "");
// this.tb1.put(lines - 1, 2, "Ergebnis", null, null);
//this.tb1.put(lines - 1, 3, result+"", null, null);
//generate recursion animation
generateRecursion();
//STEP
lang.nextStep();
this.tb1.hide();
this.tb2.hide();
this.sc.hide();
//generate statement
generateStatement();
}
/**
* the algorithm which is used in the animation
* @param a
* @param b
* @param code
* @param tb1
* @param line
* @param sum
* @return the product of a and b (a*b)
*/
public int russe(int a, int b, SourceCode code, StringMatrix tb1, int line, String sum){
int aTb = a;
int bTb = b;
Timing sTime = new Timing(100) {
@Override
public String getUnit() {
// TODO Auto-generated method stub
return "ticks";
}
};
tb1.highlightCell(line, 0, sTime, sTime);
tb1.put(line, 0, a+"", null, null); //update column 0 (a)
tb1.put(line, 1, b+"", null, null); //update column 1 (b)
//STEP
lang.nextStep();
code.unhighlight(0);
code.highlight(1);
//STEP
lang.nextStep();
if(a == 1){
code.toggleHighlight(1, 2);
//write last line
sum = sum+"+"+b;
//update table
tb1.put(line, 2, "Ja", null, null); //update column 2 (addieren)
tb1.put(line, 3, sum, null, null); //update column 3 (Summe)
this.bArray[line] = b;
return b;
}
if(line != 1){ // first line
aTb = aTb/2;
bTb = bTb*2;
}
code.toggleHighlight(1, 4);
//STEP
lang.nextStep();
code.unhighlight(1);
if(a % 2 == 1){
code.toggleHighlight(4, 5);
if(line == 1){
sum = b+"";
}else{
if(sum.equals("")){
sum = b+"";}
else{
sum = sum+"+"+b;
}
}
//update table
tb1.put(line, 2, "Ja", null, null); //update column 2 (addieren)
tb1.put(line, 3, sum, null, null); //update column 3 (Summe)
//STEP
lang.nextStep();
code.unhighlight(5);
code.highlight(0);
//safe actual value of b in bArray
this.bArray[line] = b;
this.recursionArray[line] = 0;
return b + russe(a/2, b*2, code, tb1, line+1, sum);
}
else{
code.toggleHighlight(4, 7);
//update table
tb1.put(line, 2, "Nein", null, null); //update column 2 (addieren)
tb1.put(line, 3, sum, null, null); //update column 3 (Summe)
//STEP
lang.nextStep();
code.highlight(0);
code.unhighlight(7);
//safe 0 in bArray
this.bArray[line] = 0;
this.recursionArray[line] = 1;
return russe(a/2, b*2, code, tb1, line+1, sum);
}
}
protected String getAlgorithmDescription() {
return DESCRIPTION;
}
protected String getAlgorithmCode() {
return SOURCE_CODE;
}
public String getName() {
return "Russische Bauernmultiplikation";
}
public String getDescription() {
return DESCRIPTION;
}
public String getCodeExample() {
return SOURCE_CODE;
}
public static void main(String[] args) {
Language l = new AnimalScript("Russische Bauenmultiplikation", "Michael Scholz, Ulf Gebhardt", 640, 480);
RussischeBauernmultiplikation s = new RussischeBauernmultiplikation(l);
int a = 27;
int b = 82;
s.multiply(a, b);
//konsolenausgabe direkt als asu-datei schreiben, vor Abgabe bitte wieder entfernen!!!
boolean writeFile = false;
writeFile = true;
if(writeFile){
try {
System.setOut(new PrintStream(
new FileOutputStream("uebung3_2.asu")));
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println(l);
}
}