package generators.maths; import generators.framework.Generator; import generators.framework.GeneratorType; import java.awt.Color; import java.util.Locale; import algoanim.primitives.SourceCode; import algoanim.primitives.StringMatrix; import algoanim.primitives.generators.Language; import algoanim.properties.AnimationPropertiesKeys; import algoanim.properties.MatrixProperties; import algoanim.properties.RectProperties; import algoanim.properties.SourceCodeProperties; import algoanim.properties.TextProperties; import algoanim.util.Coordinates; import algoanim.util.Offset; import algoanim.util.Timing; import java.util.Hashtable; import de.ahrgr.animal.kohnert.asugen.Font; import generators.framework.properties.AnimationPropertiesContainer; import algoanim.animalscript.AnimalScript; /* * Hier ist euch ein anschaulicher Generator gelungen, * den man auch bereits auf beliebige Zahlen loslassen kann. * Blo§ beim Auflšsen der Rekursion werden auch die Schritte, * in denen nicht multipliziert wird, mit animiert. * Das wirkt ein wenig verwirrend, wenn man nur auf das Ergebnis rechts unten schaut und * sich wundert, wieso es sich nicht verŠndert. Im Grunde ist das ganz korrekt, * aber fźr den unbedarften Zuschauer kšnnte man vielleicht noch daneben anmerken "nichts zu tun". */ public class MultiGenerator implements Generator { /** * static variables -> simple testing */ static int A = 27; static int B = 82; static SourceCodeProperties HIGHLIGHTCOLOR_PROPERTY = null; static int HIGHLIGHTING_TIME = 100; //in ticks static int UNHIGHLIGHTING_TIME = 0; //in ticks //========================================================================================================= /** * The concrete language object used for creating output */ private Language lang; /** * The "nicht addieren!" text */ private SourceCode nichtAddieren = null; /** * 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 */ private StringMatrix tb1 = null; private StringMatrix tb2 = null; /** * int-array with the values of b (only for animation) */ private int[] bArray = null; /** * recursionArray */ private int[] recursionArray = null; /** * unhighlightingTime */ private Timing unhighTime = new Timing(UNHIGHLIGHTING_TIME) { @Override public String getUnit() { // TODO Auto-generated method stub return "ticks"; } }; /** * highlightTime */ private Timing highTime = new Timing(HIGHLIGHTING_TIME) { @Override public String getUnit() { // TODO Auto-generated method stub return "ticks"; } }; /** * number of lines for tb1 */ private int linesOfTb1 = 0; //========================================================================================================= /** * Default constructor * * @param l * the concrete language object used for creating output */ public MultiGenerator(Language language) { lang = new AnimalScript("Russische Bauenmultiplikation [DE]", "Ulf Gebhardt, Michael Scholz", 800, 600); // 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(){ //initialize source code object and add code lines this.sc = lang.newSourceCode(new Coordinates(10, 147), "sourceCode", null, this.HIGHLIGHTCOLOR_PROPERTY); 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 void 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]; this.linesOfTb1 = lines; } /** * Builds the table * @param a * @param b * @param lines */ public void generateTableTb1(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); } /** * write "nicht addieren" */ public void writeNichtAddieren(){ SourceCodeProperties scProps = new SourceCodeProperties(); this.nichtAddieren = lang.newSourceCode(new Offset(2, 8, this.tb2, AnimalScript.DIRECTION_SW), "sourceCode", null, scProps); nichtAddieren.addCodeLine("Das Zwischenergebnis bleibt unverŠndert!", null, 0, null); this.nichtAddieren.hide(); } /** * 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.put(1, 0, tmp, this.highTime, this.highTime); }else{ 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); //write "nicht addieren" writeNichtAddieren(); //STEP lang.nextStep(); int newLineNumber = 0; int oldLineNumber = 0; this.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; } if(oldLineNumber == 5){ this.tb2.unhighlightCell(1, 0, this.unhighTime, this.unhighTime); } this.nichtAddieren.hide(); this.sc.unhighlight(oldLineNumber); //STEP lang.nextStep(); this.sc.highlight(newLineNumber); if(newLineNumber == 5){ this.tb2.highlightCell(1, 0, this.highTime, this.highTime); }else{ this.nichtAddieren.show(); } //update tb2 this.bArray[i-1] = this.bArray[i-1] + this.bArray[i]; drawTb2(i); //STEP lang.nextStep(); } this.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 calculateLines(a, b); generateTableTb1(a, b, this.linesOfTb1); //STEP lang.nextStep(); //higlight line 0 this.sc.highlight(0); russe(a, b, sc, tb1, 1, ""); //unhighlight last line of tb1 this.tb1.unhighlightCellColumnRange(this.linesOfTb1-2, 2, 3, this.unhighTime, this.unhighTime); //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; this.tb1.unhighlightCellColumnRange(line-1, 2, 3, this.unhighTime, this.unhighTime); this.tb1.highlightCellColumnRange(line, 0, 1, this.highTime, this.highTime); this.tb1.put(line, 0, a+"", null, null); //update column 0 (a) this.tb1.put(line, 1, b+"", null, null); //update column 1 (b) //STEP lang.nextStep(); this.tb1.unhighlightCellColumnRange(line, 0, 1, this.unhighTime, this.unhighTime); code.unhighlight(0); code.highlight(1); //STEP lang.nextStep(); if(a == 1){ code.toggleHighlight(1, 2); //write last line sum = sum+"+"+b; //update table this.tb1.highlightCellColumnRange(line, 2, 3, this.highTime, this.highTime); this.tb1.put(line, 2, "Ja", null, null); //update column 2 (addieren) this.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 this.tb1.highlightCellColumnRange(line, 2, 3, this.highTime, this.highTime); this.tb1.put(line, 2, "Ja", null, null); //update column 2 (addieren) this.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 this.tb1.highlightCellColumnRange(line, 2, 3, this.highTime, this.highTime); this.tb1.put(line, 2, "Nein", null, null); //update column 2 (addieren) this.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); } } public void init(){ lang = new AnimalScript(getName(), getAnimationAuthor(), 800, 600); lang.setStepMode(true); } public String generate(AnimationPropertiesContainer props,Hashtable primitives) { A = (Integer)primitives.get("a"); B = (Integer)primitives.get("b"); HIGHLIGHTCOLOR_PROPERTY = (SourceCodeProperties)props.getPropertiesByName("Quelltext"); multiply(A, B); return lang.toString(); } public String getName() { return "Russische Bauenmultiplikation [DE]"; } public String getAlgorithmName() { return "Russische Bauenmultiplikation"; } public String getAnimationAuthor() { return "Ulf Gebhardt, Michael Scholz"; } public String getDescription(){ return "Das hier vorgestellte Verfahren eignete sich fźr die Multiplikation zweier ganzer Zahlen. Es ist auch unter den" +"\n" +"Namen Ägyptischen Multiplizieren, Abessinische Bauernregel oder Verdopplungs-Halbierungs-Methode" +"\n" +"bekannt. Die Geschichte des vorgestellten Rechenverfahrens führt bis auf die Ägypter zurźck. Sie" +"\n" +"nutzten diese Methode nachweislich zur Multiplikation zweier ganzer Zahlen. Das Verfahren baut auf dem Teile und Herrsche Prinzip" +"\n" +"(Divide et impera ) auf und lŠsst sich somit leicht mittels Rekursion implementieren.\n" +"\n" +"Die Funktionsweise lässt sich in die folgenden fünf Schritte gliedern:\n" +"\n" +"1. Schreibe die beiden zu multiplizierenden Zahlen nebeneinander.\n" +"\n" +"2. Die linke Zahl wird halbiert (Reste werden abgerundet), die rechte Zahl wird verdoppelt.\n" +"\n" +"\t Die beiden berechneten Zahlen werden in die darauffolgende Zeile geschrieben.\n" +"\n" +"3. Schritt 2 wird solange wiederholt, bis in der linken Spalte eine 1 steht.\n" +"\n" +"4. Nun streicht man alle Zeilen, in denen die linke Zahl gerade ist.\n" +"\n" +"5. Schlussendlich werden alle übrigen Zahlen der rechten Spalte addiert." +"\n" +" "; } public String getCodeExample(){ return "public int russe(int a, int b){" +"\n" +" if(a == 1){" +"\n" +" return b;" +"\n" +" }" +"\n" +" if(a % 2 == 1){" +"\n" +" return b + russe(a/2, b*2);" +"\n" +" }else{" +"\n" +" return russe(a/2, b*2);" +"\n" +" }" +"\n" +"}" +"\n"; } public String getFileExtension(){ return "asu"; } public Locale getContentLocale() { return Locale.GERMAN; } public GeneratorType getGeneratorType() { return new GeneratorType(GeneratorType.GENERATOR_TYPE_MATHS); } public String getOutputLanguage() { return Generator.JAVA_OUTPUT; } }