(* Happy Numbers *** Nicholas Erho *** November 10, 2004 *** Tomchu forum name => nick Happy number are number where the sum of the square of the digits equals one. An example of this would be: 7^2 = 49 -> 4^2 + 9^2 = 97 -> 9^2 + 7^2 = 130 -> 1^2 + 3^2 + 0^2 = 10 -> 1^2 + 0^2 = 1 Therefor 7 is a happy number. Now it is important to note that any number in the sequence (eg. 130) of any other happy number will also be happy. However if a number is not happy it will end up looping through a number of values. Example: 6 -> 36, 45, 41, 17, 50, 25, 29, 85, 89, 145, 42, 20, 4, 16, 37, 58, 89, 145, 42, 20 It is easy to see how this loop will always occure but no always intialy. Because of this it is necessary to test the loop at varrious times by comparing it to a single vaule. If a repeated value is ever found the number can not be happy. *) MODULE HappyNumbers; FROM STextIO IMPORT WriteString, SkipLine, WriteLn, ReadChar; FROM SWholeIO IMPORT WriteCard; FROM WholeStr IMPORT StrToCard, CardToStr, ConvResults; FROM StreamFile IMPORT ChanId, Open, write, Close, old, OpenResults; FROM ProgramArgs IMPORT ArgChan, IsArgPresent; FROM SIOResult IMPORT ReadResults; FROM Strings IMPORT Delete, Concat; IMPORT WholeIO; IMPORT TextIO; IMPORT IOResult; TYPE String = ARRAY [0..80] OF CHAR; (* String type for later use*) VAR number, range :CARDINAL; res : OpenResults; (* to store the result of an open attempt *) Arg, filename : String; numberOPEN, rangeOPEN, screenOPEN, writeOPEN, happyOPEN, unhappyOPEN : BOOLEAN; result : ConvResults; outfile: ChanId; ch : CHAR; (* This function takes an input cardinal, extracts the digits, squares them, and sums the squares of them, returning a cardinal.*) PROCEDURE DigitSqrSum(number3: CARDINAL): CARDINAL; VAR tempstr : String; count, total, temp : CARDINAL; BEGIN total := 0; (* initializes total*) CardToStr(number3, tempstr); (* converts the number to a string so digits can be taken*) FOR count := 0 TO (LENGTH(tempstr)-1) DO (*runs through every digit*) StrToCard(tempstr[count], temp, result); (* returns the digits to cardinal values*) (* increases the total by the square of the digits*) INC(total, temp*temp); (* could have used power, but dont want to import it*) END; RETURN total; (*returns the total sum of the square of all the digits*) END DigitSqrSum; (* This function checks to see if the number is indeed happy and returns a true if it is the case. *) PROCEDURE IsHappy(number1:CARDINAL):BOOLEAN; VAR temp, oldtemp, count :CARDINAL; first : BOOLEAN; BEGIN temp := number1; first := TRUE; REPEAT oldtemp := temp; (* oldtemp is the attempt to find a repeat*) FOR count := 1 TO 20 DO (* number of repeating digit cycles to look at *) temp := DigitSqrSum(temp); (* sums the digit squares for a previous number and retruns a new one.*) IF (oldtemp = temp) AND (temp # 1) THEN (*Looks for repeats (aka looping numbers)*) RETURN FALSE; (* If repeating numbers are found, returns a false*) END; END; UNTIL (temp = 1); (* Since we are looking for one *) RETURN TRUE; (* If 1 is found return true*) END IsHappy; (* This procedure outputs the data either to the screen or a file or both. It can also display a range of data or a single value. *) PROCEDURE Output(number2, range2:CARDINAL); VAR loop :CARDINAL; BEGIN (* Loop through data range, if range is 0 then only 1 cycle is displayed*) FOR loop := number2 TO (number2+range2) DO IF IsHappy(loop)THEN (*see if number is happy or not*) IF happyOPEN THEN (* prgram arguments set this*) IF screenOPEN THEN (* Program arguments set this*) WriteCard(loop, 1); WriteString(" -> HAPPY"); WriteLn; END; IF writeOPEN THEN WholeIO.WriteCard(outfile, loop, 1); TextIO.WriteString(outfile, " -> HAPPY"); TextIO.WriteLn(outfile); END; END; ELSE IF unhappyOPEN THEN IF screenOPEN THEN WriteCard(loop, 1); WriteString(" -> NOT HAPPY"); WriteLn; END; IF writeOPEN THEN WholeIO.WriteCard(outfile, loop, 1); TextIO.WriteString(outfile, " -> NOT HAPPY"); TextIO.WriteLn(outfile); END; END; END; END; END Output; PROCEDURE Header; (* This procedure just displayes the header information*) BEGIN WriteString ("************************************************************************"); WriteLn; WriteString ("*** Nicholas Erho ******** Happy Numbers ******** November 10, 2004 ***"); WriteLn; WriteString ("************************************************************************"); WriteLn; WriteString ("* This program is designed to determine if specific or a range of *"); WriteLn; WriteString ("* numbers are happy numbers. *"); WriteLn; WriteString ("************************************************************************"); WriteLn; WriteString ("* happynumbers -n:[number1] [-r:[number2]] [-w:[filename]] [-s] *"); WriteLn; WriteString ("* [[-h] || [-u] || [-b]] *"); WriteLn; WriteString ("* *"); WriteLn; WriteString ("* -n Number to test for happy number or start of range. *"); WriteLn; WriteString ("* -r Specifies range of numbers to be tested. *"); WriteLn; WriteString ("* number1 Length of range (Final number = range + start number). *"); WriteLn; WriteString ("* -w Indicates that data should be writen to ONLY a file. *"); WriteLn; WriteString ("* The default is data to be writen to the screen. *"); WriteLn; WriteString ("* filename The name of the file you want the data writen to. *"); WriteLn; WriteString ("* -s Indicates that data should be displayed on the screen *"); WriteLn; WriteString ("* (to be used with -w). *"); WriteLn; WriteString ("* -h Specifies that only happy numbers are to be displayed. *"); WriteLn; WriteString ("* -u Specifies that only unhappy numbers are to be displayed. *"); WriteLn; WriteString ("* -b Specifies that both happy and unhappy numbers are to be *"); WriteLn; WriteString ("* Displayed (Default). *"); WriteLn; WriteString ("************************************************************************"); WriteLn; WriteLn; END Header; BEGIN (* initializes contole varibles*) numberOPEN := FALSE; rangeOPEN := FALSE; screenOPEN := TRUE; writeOPEN := FALSE; happyOPEN := TRUE; unhappyOPEN := TRUE; range := 0; IF NOT IsArgPresent() THEN Header; ELSE REPEAT TextIO.ReadToken(ArgChan(), Arg); (* Reads command Line*) (* gets inital range or specific number*) IF (Arg[1] = 'n') THEN Delete(Arg, 0, 3); StrToCard(Arg, number, result); IF number >= 0 THEN numberOPEN := TRUE; ELSE WriteString("No number entered."); WriteLn; numberOPEN := FALSE; END; END; (* gets range value*) IF (Arg[1] = 'r') THEN Delete(Arg, 0, 3); StrToCard(Arg, range, result); IF range >= 0 THEN rangeOPEN := TRUE; ELSE WriteString("No number entered."); WriteLn; rangeOPEN := FALSE; END; END; (* Opens file and sees if every thing is OKAY*) IF (IOResult.ReadResult (ArgChan()) = allRight) THEN IF (Arg[1] = 'w') THEN Delete(Arg, 0, 3); Concat(Arg, ".txt", filename); Open (outfile, filename, write, res); IF res = opened THEN writeOPEN := TRUE; ELSE WriteString ("Do you want to overwrite existing file (Y/N)? "); ReadChar(ch); SkipLine; IF CAP(ch) = 'Y' THEN Open (outfile, filename, old+write, res); IF res = opened THEN writeOPEN := TRUE; ELSE WriteString("Could not open output file."); WriteLn; END; END; ch := ""; (*Resets char for later use*) END; screenOPEN := FALSE; END; END; (* specifies data output to the screen*) IF (Arg[1] = 's') THEN screenOPEN := TRUE; END; (* specifies happy number only are to be displayed*) IF (Arg[1] = 'h') THEN unhappyOPEN := FALSE; happyOPEN := TRUE; END; (* specifies unhappy numbers only are to be displayed*) IF (Arg[1] = 'u') THEN happyOPEN := FALSE; unhappyOPEN := TRUE; END; (* Specifies both happy and unhappy numbers to be displayed*) IF (Arg[1] = 'b') THEN happyOPEN := TRUE; unhappyOPEN := TRUE; END; (* Kills read token when all arguments are read*) UNTIL IOResult.ReadResult (ArgChan()) # allRight; (* Runs the ouput procedure with defined data*) Output(number, range); IF writeOPEN THEN Close(outfile); (* Closes file if Open*) END; END; END HappyNumbers. (* Sample Runs: C:\Documents and Settings\Administrator\My Documents\Modula-2>happynumbers -n:0 -r:100 -h 1 -> HAPPY 7 -> HAPPY 10 -> HAPPY 13 -> HAPPY 19 -> HAPPY 23 -> HAPPY 28 -> HAPPY 31 -> HAPPY 32 -> HAPPY 44 -> HAPPY 49 -> HAPPY 68 -> HAPPY 70 -> HAPPY 79 -> HAPPY 82 -> HAPPY 86 -> HAPPY 91 -> HAPPY 94 -> HAPPY 97 -> HAPPY 100 -> HAPPY C:\Documents and Settings\Administrator\My Documents\Modula-2>happynumbers -n:439723 439723 -> NOT HAPPY C:\Documents and Settings\Administrator\My Documents\Modula-2>happynumbers -n:100 -r:120 100 -> HAPPY 101 -> NOT HAPPY 102 -> NOT HAPPY 103 -> HAPPY 104 -> NOT HAPPY 105 -> NOT HAPPY 106 -> NOT HAPPY 107 -> NOT HAPPY 108 -> NOT HAPPY 109 -> HAPPY 110 -> NOT HAPPY 111 -> NOT HAPPY 112 -> NOT HAPPY 113 -> NOT HAPPY 114 -> NOT HAPPY 115 -> NOT HAPPY 116 -> NOT HAPPY 117 -> NOT HAPPY 118 -> NOT HAPPY 119 -> NOT HAPPY 120 -> NOT HAPPY 121 -> NOT HAPPY 122 -> NOT HAPPY 123 -> NOT HAPPY 124 -> NOT HAPPY 125 -> NOT HAPPY 126 -> NOT HAPPY 127 -> NOT HAPPY 128 -> NOT HAPPY 129 -> HAPPY 130 -> HAPPY 131 -> NOT HAPPY 132 -> NOT HAPPY 133 -> HAPPY 134 -> NOT HAPPY 135 -> NOT HAPPY 136 -> NOT HAPPY 137 -> NOT HAPPY 138 -> NOT HAPPY 139 -> HAPPY 140 -> NOT HAPPY 141 -> NOT HAPPY 142 -> NOT HAPPY 143 -> NOT HAPPY 144 -> NOT HAPPY 145 -> NOT HAPPY 146 -> NOT HAPPY 147 -> NOT HAPPY 148 -> NOT HAPPY 149 -> NOT HAPPY 150 -> NOT HAPPY 151 -> NOT HAPPY 152 -> NOT HAPPY 153 -> NOT HAPPY 154 -> NOT HAPPY 155 -> NOT HAPPY 156 -> NOT HAPPY 157 -> NOT HAPPY 158 -> NOT HAPPY 159 -> NOT HAPPY 160 -> NOT HAPPY 161 -> NOT HAPPY 162 -> NOT HAPPY 163 -> NOT HAPPY 164 -> NOT HAPPY 165 -> NOT HAPPY 166 -> NOT HAPPY 167 -> HAPPY 168 -> NOT HAPPY 169 -> NOT HAPPY 170 -> NOT HAPPY 171 -> NOT HAPPY 172 -> NOT HAPPY 173 -> NOT HAPPY 174 -> NOT HAPPY 175 -> NOT HAPPY 176 -> HAPPY 177 -> NOT HAPPY 178 -> NOT HAPPY 179 -> NOT HAPPY 180 -> NOT HAPPY 181 -> NOT HAPPY 182 -> NOT HAPPY 183 -> NOT HAPPY 184 -> NOT HAPPY 185 -> NOT HAPPY 186 -> NOT HAPPY 187 -> NOT HAPPY 188 -> HAPPY 189 -> NOT HAPPY 190 -> HAPPY 191 -> NOT HAPPY 192 -> HAPPY 193 -> HAPPY 194 -> NOT HAPPY 195 -> NOT HAPPY 196 -> NOT HAPPY 197 -> NOT HAPPY 198 -> NOT HAPPY 199 -> NOT HAPPY 200 -> NOT HAPPY 201 -> NOT HAPPY 202 -> NOT HAPPY 203 -> HAPPY 204 -> NOT HAPPY 205 -> NOT HAPPY 206 -> NOT HAPPY 207 -> NOT HAPPY 208 -> HAPPY 209 -> NOT HAPPY 210 -> NOT HAPPY 211 -> NOT HAPPY 212 -> NOT HAPPY 213 -> NOT HAPPY 214 -> NOT HAPPY 215 -> NOT HAPPY 216 -> NOT HAPPY 217 -> NOT HAPPY 218 -> NOT HAPPY 219 -> HAPPY 220 -> NOT HAPPY NOTE: This program can also send output to a file. *)