'MAGIC12.BAS                  H.Heinz               April 1997

' July 3 /94        Magic12.bas
'   after completing LoubereM sub-program & ClearText S.R.
' June 17 / 94      Magic11.bas
'   use screen 12 for everything except sqrInput which uses screen 0
' April 20, 1994    Magic10.bas
'   Start modifying MAGIC9C.BAS for screen 12 & magic lines routine


' This is modified version of MAGIC8.BAS as it was on Mar.9/94

' --- Following REM's apply to MAGIC8.BAS
' --- This is modified version of MAGICSQR.BAS 3/11/93 11:35
' --- This is MATTEST2.BAS renamed & with subprograms modified somewhat
' --- modified Mar6/93 to allow testing m.s. with non-consequetive numbers.
' --- demo Magic Square program showing use of general order sub-routines.
' --- this is a fully functional program for testing magic squares of
'       order 2 to 9. The sub-routines adjust accordingly.
' --- matrixA() is used in subroutines to hold EITHER origSqr() OR rotateSqr().

DEFINT A-Z
CONST false = 0, true = NOT false

COMMON SHARED size, sum, sumCorrect, endroutine, origSqr(), rotateSqr(), nograph

DECLARE SUB AssocSum (matrixA())                                       ' test associated pairs
DECLARE SUB ClearText ()                                      ' clear some text for large m.s.
DECLARE SUB DiagSum (matrixA(), mainSum)                               ' test diagonals
DECLARE SUB DisplayMenuBox (choiceList$(), leftCoord, Prompt$, ok$)    ' display main menu
DECLARE SUB displaySqr (pairnum())                                     ' control printing sqr
DECLARE SUB frame (left, right, top, bottom)                           ' draw a frame
DECLARE SUB lineImput ()                                               ' input a new square
DECLARE SUB Loubere ()                                                 ' Loubere method
DECLARE SUB LoubereM ()                                              ' Modified Loubere method
DECLARE SUB magicLines ()                                              ' called by showSqr
DECLARE SUB MatRotate ()                                               ' rotate the original
DECLARE SUB menu2 (storedSqr())                                        ' select a stored m. s.
DECLARE SUB menu3 ()                                                   ' demo's of construction
DECLARE SUB OrthoSum (matrixA())                                       ' test rows, columns
DECLARE SUB pan5x5 (matrixA(), pTbl(), pairnum())                  'generate all 5x5 basic pand
DECLARE SUB Pause ()                                                   ' pauses for a keypress
DECLARE SUB SemiPanSum (matrixA())                                     ' test oppos. short diag
DECLARE SUB showSqr ()                                                ' print the square & grid
DECLARE SUB sqrInput ()                                             ' edit the square in memory
DECLARE SUB titleOne ()                                                ' prints the main title
DECLARE FUNCTION Getletter$ (Prompt$, legal$)                          ' get a letter
DECLARE FUNCTION menu (choices$())                                     ' main menu

DIM pairnum(3 TO 9)                                           ' holds # of pairs for each order
DIM storedSqrs(1084)                       ' all squares are stored linearly, 1 after the other
DIM origSqr(9, 9)                                 ' this array holds the original version
DIM rotateSqr(9, 9)                               ' this is a copy rotated 90 degrees
DIM matrixA(size, size)                           ' this matrix holds the m.s. in the sub-rout.
DIM menuOptions$(6)                                      ' list of choices for the main menu
DIM pTbl(1 TO 36, 1 TO 9)                                ' table 7 from book p. 1137

SCREEN 12                                   ' run program on graphic screen with 80 x 30 text

nograph = 0                                 ' permit graphs unless this is 1  (sqrInput)

' --- # of correct associated pairs for each order (for Associated magic square).
DATA 4,8,12,18,24,32,40

FOR i = 3 TO 9                                           ' load into pairnum array
    READ pairnum(i)
NEXT i


' --- sample magic squares, ALL are 'normal' except last order 6 (l) because NO normal
'      singly-even order associated magic squares.

' --- order 4 squares
DATA 7,6,12,9,15,14,4,1,2,11,5,16,10,3,13,8
DATA 2,8,15,9,11,13,6,4,14,12,3,5,7,1,10,16
DATA 2,11,7,14,13,8,12,1,16,5,9,4,3,10,6,15
DATA 1,15,6,12,14,4,9,7,11,5,16,2,8,10,3,13

' --- order 5 squares
DATA 23,6,19,2,15,10,18,1,14,22,17,5,13,21,9,4,12,25,8,16,11,24,7,20,3
DATA 14,10,1,22,18,20,11,7,3,24,21,17,13,9,5,2,23,19,15,6,8,4,25,16,12
DATA 23,1,2,20,19,22,16,9,14,4,5,11,13,15,21,8,12,17,10,18,7,25,24,6,3
DATA 1,15,24,8,17,23,7,16,5,14,20,4,13,22,6,12,21,10,19,3,9,18,2,11,25

' --- order 6 magic squares
DATA 1,35,34,3,32,6,30,8,28,27,11,7,24,23,15,16,14,19,13,17,21,22,20,18,12,26,9,10,29,25,31,2,4,33,5,36
DATA 17,8,33,24,2,27,32,22,11,25,16,5,3,30,14,9,36,19,18,1,28,23,7,34,31,21,12,26,15,6,10,29,13,4,35,20
DATA 32,31,1,3,21,23,29,30,4,2,24,22,9,11,20,19,25,27,12,10,17,18,28,26,16,15,33,35,5,7,13,14,36,34,8,6
DATA 1,47,6,43,5,48,35,17,30,21,31,16,36,12,41,8,40,13,7,45,2,49,3,44,29,19,34,15,33,20,42,10,37,14,38,9

' --- order 7 magic squares
DATA 46,1,2,3,42,41,40,45,35,13,14,32,31,5,44,34,28,21,26,16,6,7,17,23,25,27,33,43,11,20,24,29,22,30,39,12,19,37,36,18,15,38,10,49,48,47,8,9,4
DATA 26,20,14,1,44,38,32,34,28,15,9,3,46,40,42,29,23,17,11,5,48,43,37,31,25,19,13,7,2,45,39,33,27,21,8,10,4,47,41,35,22,16,18,12,6,49,36,30,24
DATA 42,18,29,9,45,26,6,20,35,11,43,23,3,40,4,36,16,31,12,48,28,33,13,49,25,1,37,17,22,2,38,19,34,14,46,10,47,27,7,39,15,30,44,24,5,41,21,32,8
DATA 1,8,17,26,35,40,48,30,41,46,4,10,16,28,12,21,23,31,39,43,6,36,45,5,14,19,24,32,20,25,29,38,47,7,9,49,2,13,18,22,34,37,27,33,42,44,3,11,15

' --- order 8 magic squares
DATA 1,63,62,4,5,59,58,8,56,15,49,48,19,44,20,9,55,47,25,39,38,28,18,10,11,22,36,30,31,33,43,54,53,42,32,34,35,29,23,12,13,24,37,27,26,40,41,52,14,45,16,17,46,21,50,51,57,2,3,61,60,6,7,64
DATA 1,57,40,32,5,44,29,52,48,24,9,49,61,20,37,12,25,33,64,8,36,13,60,21,56,16,17,41,28,53,4,45,50,47,2,31,43,19,38,30,7,26,55,42,54,14,59,3,63,34,15,18,27,35,22,46,10,23,58,39,6,62,11,51
DATA 1,2,62,61,60,59,7,8,9,10,54,53,52,51,15,16,48,47,19,20,21,22,42,41,40,39,27,28,29,30,34,33,32,31,35,36,37,38,26,25,24,23,43,44,45,46,18,17,49,50,14,13,12,11,55,56,57,58,6,5,4,3,63,64
DATA 7,42,55,26,31,50,47,2,62,19,14,35,38,11,22,59,1,48,49,32,25,56,41,8,60,21,12,37,36,13,20,61,4,45,52,29,28,53,44,5,57,24,9,40,33,16,17,64,6,43,54,27,30,51,46,3,63,18,15,34,39,10,23,58

' --- order 9 magic squares
DATA 77,1,2,3,4,72,71,70,69,76,62,17,18,19,58,57,56,6,75,61,51,29,30,48,47,21,7,74,60,50,44,37,42,32,22,8,9,23,33,39,41,43,49,59,73,14,27,36,40,45,38,46,55,68,15,28,35,53,52,34,31,54,67,16,26,65,64,63,24,25,20,66,13,81,80,79,78,10,11,12,5
DATA 42,34,26,18,1,74,66,58,50,52,44,36,19,11,3,76,68,60,62,54,37,29,21,13,5,78,70,72,55,47,39,31,23,15,7,80,73,65,57,49,41,33,25,17,9,2,75,67,59,51,43,35,27,10,12,4,77,69,61,53,45,28,20,22,14,6,79,71,63,46,38,30,32,24,16,8,81,64,56,48,40
DATA 71,64,69,8,1,6,53,46,51,66,68,70,3,5,7,48,50,52,67,72,65,4,9,2,49,54,47,26,19,24,44,37,42,62,55,60,21,23,25,39,41,43,57,59,61,22,27,20,40,45,38,58,63,56,35,28,33,80,73,78,17,10,15,30,32,34,75,77,79,12,14,16,31,36,29,76,81,74,13,18,11
DATA 75,53,11,25,14,65,48,42,36,10,26,74,54,49,43,32,15,66,71,57,7,29,33,16,67,50,39,8,28,72,56,68,46,40,34,17,52,69,13,30,41,35,18,64,47,12,27,38,51,77,80,20,3,61,37,59,76,9,24,4,60,81,19,73,6,23,45,58,79,21,2,62,31,44,55,70,5,1,63,78,22

' --- All the magic squares are stored in 1 long string. The starting position for each m.s. is:
'           order 4;    1, 17, 33, 49           order 5;    65, 90, 115,140
'           order 6;    165,201,237,273         order 7;    309,358,407,456
'           order 8;    505,569,633,697         order 9;    761,842,923,1004 

FOR i = 1 TO 1084                                                ' load into storedSqrs array
    READ storedSqrs(i)
NEXT i

' --- data for pTbl   used in Pan5x5 routine -  this is table 7 in the book

DATA 1,1,1,10,15,20,3,4,5,2,3,1,10,15,20,3,5,4,3,5,1,10,20,15,3,4,5
DATA 4,7,1,10,20,15,3,5,4,5,9,1,10,15,20,4,3,5,6,11,1,10,15,20,4,5,3
DATA 7,13,1,10,20,15,4,3,5,8,15,1,10,20,15,4,5,3,9,17,1,10,15,20,5,3,4
DATA 10,19,1,10,15,20,5,4,3,11,21,1,10,20,15,5,3,4,12,23,1,10,20,15,5,4,3
DATA 13,25,2,15,10,20,3,4,5,14,27,2,15,10,20,3,5,4,15,29,1,15,20,10,3,4,5
DATA 16,31,1,15,20,10,3,5,4,17,33,2,15,10,20,4,3,5,18,35,2,15,10,20,4,5,3
DATA 19,37,1,15,20,10,4,3,5,20,39,1,15,20,10,4,5,3,21,41,2,15,10,20,5,3,4
DATA 22,43,2,15,10,20,5,4,3,23,45,1,15,20,10,5,3,4,24,47,1,15,20,10,5,4,3
DATA 25,49,2,20,10,15,3,4,5,26,51,2,20,10,15,3,5,4,27,53,2,20,15,10,3,4,5
DATA 28,55,2,20,15,10,3,5,4,29,57,2,20,10,15,4,3,5,30,59,2,20,10,15,4,5,3
DATA 31,61,2,20,15,10,4,3,5,32,63,2,20,15,10,4,5,3,33,65,2,20,10,15,5,3,4
DATA 34,67,2,20,10,15,5,4,3,35,69,2,20,15,10,5,3,4,36,71,2,20,15,10,5,4,3

FOR snum = 1 TO 36                                               ' load into pTbl array
    FOR j = 1 TO 9
        READ pTbl(snum, j)
    NEXT j
NEXT snum

' --- Main menu options
DATA show a stored square
DATA input a new square
DATA edit the square in memory
DATA display 5x5 pandiagonals
DATA view construction methods
DATA quit the program

FOR i = 1 TO 6                                                 ' load into menuOptions array
    READ menuOptions$(i)
NEXT i

CLS

' --- Display the menu & react to the user's choices

DO
    titleOne                                                   ' print the main title

    PRINT "This is a demo program only. Still being added to and modified.   h.h. 12-1-93"

    SELECT CASE menu(menuOptions$())
    CASE 1                                                  ' select a stored magic square
        menu2 storedSqrs()
    CASE 2                                                  ' input a new magic square
         lineImput
    CASE 3
        SCREEN 0
        CLS : nograph = 1                                   ' flag to prevent graph
        showSqr                                       ' put the square on screen
        sqrInput                                            ' edit the square on screen
        SCREEN 12
        nograph = 0                                     ' reset for graphs
    CASE 4                                               ' generate ALL essentially different
         sum = 0
         pan5x5 matrixA(), pTbl(), pairnum()             ' order 5 pandiagonal m.s. (36)
    CASE 5                                               ' show methods of construction
        menu3
    CASE 6
        done = true                                      ' set flag to exit program
    CASE ELSE
        ' ---  not necessary to do anything
    END SELECT

    IF NOT done THEN                                     ' show the selected magic square
        IF size > 0 THEN                                 '  if no m.s. in memory
            displaySqr pairnum()                         '   don't try to display one
            Pause                                            ' continue when ready
        END IF
        CLS
    END IF

LOOP UNTIL done

SCREEN 0                            ' reset for standard text screen before exit

END

keyevent:            ' ---- exit sub-program if Function key two is pressed

endroutine = true
RETURN

PrinterError:        ' --- Traps printer errors and pauses for corrections
    LOCATE 23, 20
    PRINT "*** Printer not in Operation ***"
    Pause
    LOCATE 23, 20
    PRINT "                                     "
RESUME NEXT




SUB AssocSum (matrixA())

' This sub routine tests for pairs of cells that are diametrically equidistant
'  from the center summing to the total of the first and last numbers in the series.
'   It test the corresponding pairs in the top left & lower right quadrants.
' On the second call it tests these same quadrants, but in the rotated matrix.
' This routine works on odd or doubly-even order NORMAL magic squares.
' Singly-even order m.s. are also tested although NO assoc.m.s. of this order.
' It is called by the displaySqr sub-routine.

sumCorrect = 0
pairsSum = size * size + 1                          ' i.e. N^2+1 (only for NORMAL m.s.)
halfSize = size \ 2                                 ' integer division

IF size MOD 2 = 1 THEN
    oddOrder = true                                 ' order is odd
    center = halfSize + 1
    IF matrixA(center, center) = ((size * size) + 1) / 2 THEN
        CouldBeAssoc = true
    ELSE                                            ' assoc. odd order m.s. must
        CouldBeAssoc = false                        ' have center cell equal to
    END IF                                          ' (N^2 + 1)/2

ELSE
    oddOrder = false                                ' order is even
    CouldBeAssoc = true                             ' so it could be associated
END IF

cn = 1: cx = size

IF CouldBeAssoc THEN                                ' run test only if possibility
                                                    '  of being associated
    DO
        rn = 1: rx = size
        DO                                          ' cycle through pairs of cells in
            testSum = 0                                   ' upper left & lower right
            testSum = matrixA(rn, cn) + matrixA(rx, cx)
            IF testSum = pairsSum THEN
                sumCorrect = sumCorrect + 1         ' count correct pairs
            END IF
            rn = rn + 1: rx = rx - 1
        LOOP UNTIL rn > halfSize                    ' by rows, then columns
        cn = cn + 1: cx = cx - 1
    LOOP UNTIL cn > halfSize

    IF oddOrder THEN                                ' odd order m.s. require you
   
        cn = halfSize + 1                           ' now do center column
        rn = 1: rx = size
        DO                                          ' check pairs in center column
            testSum = 0
            testSum = matrixA(rn, cn) + matrixA(rx, cn)
            IF testSum = pairsSum THEN
                sumCorrect = sumCorrect + 1         ' second call, in effect
            END IF                                  ' does center row
            rn = rn + 1: rx = rx - 1
        LOOP UNTIL rn > halfSize
    END IF

END IF

END SUB

SUB ClearText

' This sub-rooutine is used to clear several lines of text in the
'   LoubereM sub-program

LOCATE 4, 1                                 ' erase text
FOR i = 1 TO 6
    PRINT STRING$(80, 32)
NEXT i

END SUB

SUB DiagSum (matrixA(), mainSum)

' Sub routine DiagSum calculates & checks the sums of all the diagonals
'  including broken diagonal pairs. It returns a count of the # correct.
' Call twice, once checks original, 2nd time checks rotated square to get
'  diagonals in the other direction.
' MatrixA() holds origSqr() on first pass, rotateSqr() on second pass.
' It is called by the displaySqr sub-routine.

sumCorrect = 0                                      ' reset count of correct sums
mainSum = 0                                         ' reset main diagonal flag

' --- test diagonals from upper left to lower right, starting with main diagonal

FOR k = 1 TO size                                   ' k counts the # of diagonals
    testSum = 0                                     ' initialize for new diagonal
    i = k: j = 0
    
    DO
    j = j + 1
    testSum = testSum + matrixA(i, j)
        IF i = size THEN                            ' move to 2nd half of broken diagonal
            i = 1
        ELSE i = i + 1
        END IF
    
    LOOP WHILE j < size                             ' is diagonal finished
   
    IF k = 1 AND j = size AND testSum = sum THEN mainSum = 1        'main diagonal

    IF testSum = sum THEN
        sumCorrect = sumCorrect + 1                 ' add to count if correct sum
    END IF
    
NEXT k                                              ' test next diagonal pair

END SUB

SUB DisplayMenuBox (choiceList$(), leftCoord, Prompt$, ok$)

' The DisplayMenuBox subprogram displays the menu choices on the screen and
'  prepares the prompt string and validation string. This routine is called
'   from the Menu function.

' --- Find the number of choices (numChoices) and initialize variables.

numChoices = UBOUND(choiceList$)
Prompt$ = " "
ok$ = ""
longChoice = 0

' --- Prepare the prompt string (prompt$) and the string of legal input
'      characters (ok$). Also, find the length of the longest choice string.

FOR i = 1 TO numChoices
    first$ = UCASE$(LEFT$(choiceList$(i), 1))
    ok$ = ok$ + first$
    Prompt$ = Prompt$ + first$ + " "
    longTemp = LEN(choiceList$(i))
    IF longTemp > longChoice THEN longChoice = longTemp
NEXT i

longChoice = longChoice + 1
Prompt$ = Prompt$ + "-> "

' --- Test to see if the prompt string is longer then promptChoice

IF LEN(Prompt$) >= longChoice THEN longChoice = LEN(Prompt$) + 1

' --- Given longChoice and numChoice, determine the demensions of the
'      menu frame. Draw the frame, calling on the frame subprogram.

leftCoord = 37 - longChoice \ 2
rightCoord = 80 - leftCoord
topCoord = 5
bottomCoord = 10 + numChoices
frame leftCoord, rightCoord, topCoord, bottomCoord

' --- Display the menu choices. The first letter of each choice is
'      displayed in uppercase, followed by a parenthesis character.

FOR i = 1 TO numChoices
    LOCATE 7 + i, leftCoord + 3
    PRINT UCASE$(LEFT$(choiceList$(i), 1)) + ")" + MID$(choiceList$(i), 2)
NEXT i

LOCATE 6, 38: PRINT "Menu"
line$ = STRING$(longChoice, 196)
LOCATE 7, leftCoord + 3: PRINT line$
LOCATE 8 + numChoices, leftCoord + 3: PRINT line$

' --- Print the input prompt.

LOCATE 9 + numChoices, leftCoord + 3: PRINT Prompt$;


END SUB

SUB displaySqr (pairnum())

' --- This subroutine handles the display of the square and summary.
'      It also calls the routines that tests the various aspects of m.s.
' It is called from the main program & pan5X5 sub-routine.

CLS

titleOne                                                       ' print the main title

' ---    sum is calculated for a normal m.s. (i.e. 1st N^2 natural numbers)(if it is now 0)

IF sum = 0 THEN sum = (size * size * size + size) / 2       ' this test allows for impure m.s.
halfSize = size \ 2                                             ' this is integer division
top = 13 - size: bottom = top + size * 2 + 1                    ' position on the screen

showSqr                                                     ' routine to print square & grid
MatRotate                                                   ' rotate 90 degrees right

' --- test sums of rows (& later columns) & count correct ones

sumFlag = 0: sumFlag2 = 0                              ' keep track of rows, col., & main diag

OrthoSum origSqr()                                          ' test rows
rowTotal = sumCorrect                                       ' # of rows correct
IF rowTotal = size THEN sumFlag = 1                         ' i.e. all rows are correct

OrthoSum rotateSqr()                                        ' test columns (of original m.s.)
colTotal = sumCorrect                                       ' # of columns correct
IF colTotal = size THEN sumFlag = sumFlag + 1               ' i.e. all columns correct

' --- Test that main diagonals are correct. Also test all broken diagonals.

DiagSum origSqr(), mainSum                                  ' test original diagonals
diagTotal = sumCorrect
sumFlag = sumFlag + mainSum                                 ' add 1 if main diagonal OK
DiagSum rotateSqr(), mainSum                                ' test rotated diagonals
diagTotal = diagTotal + sumCorrect                          ' # of main & broken diagonals cor.
sumFlag = sumFlag + mainSum                                 ' add 1 if main diagonal OK
IF diagTotal = 2 * size THEN
    diagFlag = true                                         ' this is a pandiagonal m.s.
END IF

' ---Count the number of complementary pairs for possible Associated m.s.

AssocSum origSqr()                                          ' test for associated
assocTotal = sumCorrect                                     ' # of pairs correct
AssocSum rotateSqr()                                        ' test other 2 quadrants
assocTotal = assocTotal + sumCorrect                        ' total # of pairs correct

'Count opposite short diagonal pairs with the correct sum.

semipanTotal = 0
IF NOT diagFlag AND size > 3 THEN                           ' if not pandiagonal, semiPan ?
   SemiPanSum origSqr()                                     ' check for semi-pan
    semipanTotal = sumCorrect                               ' is opposite short diagonal cor.
    SemiPanSum rotateSqr()                                  ' check for semi-pan
    semipanTotal = semipanTotal + sumCorrect                ' total # correct (need 2)
END IF

' --- Check values from above tests and advise specifications of magic square.

                     
LOCATE 25, 1                                                'locate at bottom of screen

PRINT "Total rows correct:      "; rowTotal, "Total columns correct: "; colTotal
PRINT "Total diagonals correct: "; diagTotal,

IF diagFlag THEN                                            ' advise if this is a pandiagonal
    PRINT "This is a Pandiagonal M.S.",
    sumFlag2 = sumFlag + 1                                  ' add 1 to show  it's not simple
END IF

IF semipanTotal = 2 THEN                                    ' advise if semi-pandiagonal
    PRINT "This is a Semi-Pandiagonal M. S.",
    sumFlag2 = sumFlag + 1                                  ' add 1 to show  it's not simple
END IF

IF assocTotal > 0 THEN                                      ' show # of correct pairs
    PRINT "Pairs of 2 assoc. cells correct:"; assocTotal,
END IF

IF assocTotal = pairnum(size) THEN                          ' correct # of pairs?
    PRINT "This is an Associated M. Sqr.",                  ' # of pairs is correct for size
    sumFlag2 = sumFlag + 1                                  ' add 1 to show  it's not simple
END IF

' --- some squares may NOT have correct rows, or columns & still have correct diagonals
'      or associated pairs. i.e. 16,2,14,4,5,11,7,9,8,10,6,12,13,3,15,1 has NO correct
'      rows or columns yet otherwise it would be an associated pandiagonal m.s.

IF sumFlag = 4 AND sumFlag2 = 0 THEN                     ' ALL rows, col. & 2 main diag. O.K.?
    PRINT "This is a simple magic square.",
ELSEIF sumFlag = 3 THEN                                  ' only 1 main diagonal O.K.
    PRINT "This square is only semi-magic !!",
ELSEIF sumFlag < 3 THEN                                  ' not all rows or col. correct
    PRINT "This square is NOT magic !!!",                '   or not both diagonals
END IF

IF sumFlag > 3 THEN PRINT "The Magic Sum is "; sum;      ' magic!! so constant is ...

END SUB

SUB frame (left, right, top, bottom) STATIC

' The Frame subprogram draws a rectangular double-line frame on
' the screen, using "text-graphics" characters from the
' IBM Extended ASCII character set. (change chr$ for single lines)

' --- Draw the four corners.
'       use chr$ 218, 191, 192, 217 for corners of single line.
'       use chr$ 201, 187, 200, 188 for corners of double line.

        LOCATE top, left: PRINT CHR$(201)
        LOCATE top, right: PRINT CHR$(187)
        LOCATE bottom, left: PRINT CHR$(200)
        LOCATE bottom, right: PRINT CHR$(188)

' --- Draw the vertical lines
'       use chr$ 179 for single line.
'       use chr$ 186 for double line.

        FOR vert = top + 1 TO bottom - 1
                LOCATE vert, left: PRINT CHR$(186);
                LOCATE vert, right: PRINT CHR$(186);
        NEXT vert

' --- Draw the horizontal lines.
'       use chr$ 196 for single line.
'       use chr$ 205 for double line.

        horiz = right - left - 1
        hline$ = STRING$(horiz, 205)
        LOCATE top, left + 1: PRINT hline$
        LOCATE bottom, left + 1: PRINT hline$;

END SUB

DEFSNG A-Z
FUNCTION Getletter$ (Prompt$, legal$)

' The getLetter function elicits a single letter response that must
' correspond to a letter that is included in the parameter Legal.
' These letters (in Legal) should be upper case.
' This function is called from the lineInput, Menu2, & pan5X5 sub-routines.

PRINT Prompt$; " -> ";

' --- Get the response. A beep indicates an invalid response.

DO
        ans$ = UCASE$(INKEY$)
        ansPos = INSTR(legal$, ans$)
        IF ansPos = 0 THEN BEEP
LOOP UNTIL ans$ <> "" AND ansPos <> 0

PRINT ans$
Getletter$ = ans$

END FUNCTION

DEFINT A-Z
SUB lineImput

' --- this subprogram handles the input of a new magic square by asking for the numbers
'       1 at a time. If you make an error (like getting out of step), it can be edited
'         later (from the main menu).
' This routine is called from the main program.

CLS
titleOne                                                 '  print the main title
PRINT
PRINT "This program demonstrates the use of generalized subroutines that perform"
PRINT "the required function for any order Magic Square. These routines may be"
PRINT "used in any Quickbasic program by 'merging'. "
PRINT "This program is a practical program for testing order 3 to 9 magic squares."
PRINT "Screen space (only) limits this demo to a maximum of order 9 magic squares."
PRINT
size$ = Getletter$("Enter length of line (order of square) 3-9 ", "3,4,5,6,7,8,9")
size = VAL(size$)                                                     ' size = order of m.s.

PRINT : PRINT "Enter the integers for the square, 1 at a time. If an error is made, "
PRINT "it may be corrected later by using 'edit' from the main menu."
PRINT "The constant will be determined by the sum of the cells in the first row."

endroutine = false                                '  be sure flag is clear

FOR r = 1 TO size
    firstrowSum = 0
    FOR c = 1 TO size
        LOCATE 20, 5: PRINT "Number for row "; r; "col "; c; "is "; : INPUT n
        firstrowSum = firstrowSum + n
        origSqr(r, c) = n
        LOCATE 20, 5: PRINT "                                                                "
    NEXT c

    IF r = 1 THEN
        sum = firstrowSum
        LOCATE 20, 5: PRINT "The magic constant is **"; sum; "** "; :
        proceed$ = Getletter$("Is this correct (Y/N) ?", "Y,N")
        IF proceed$ = "N" THEN EXIT SUB                      'go back to main menu    
        LOCATE 20, 5: PRINT "                                                                "
    END IF
   
    ON KEY(2) GOSUB keyevent
    KEY(2) ON

    LOCATE 30, 1
    PRINT "F2, then spacebar to exit";        ' provision for exiting this routine
    Pause
    IF endroutine THEN CLS : EXIT SUB         ' leave orderly
    LOCATE 30, 1: PRINT "                                                 ";

NEXT r


END SUB

SUB Loubere

sum = 0                                                 ' set magic constant to 0

CLS
PRINT "De La Loubere Method of Magic Square Generation"

PRINT : PRINT "This is the most popular and widely known method of odd order m.s. generation"
LOCATE 4, 1

choice$ = Getletter$("Enter the size of the desired magic Square (3,5,7,or 9 please)", "3,5,7,9")

size = VAL(choice$)
IF size = 9 THEN ClearText                  ' clear bottom line of text for large square

FOR i = 1 TO size                           ' clear the magic square array
    FOR j = 1 TO size
    origSqr(i, j) = 0
    k = k + 1
    NEXT j
NEXT i

showSqr
Pause
r = 1: c = INT(size / 2) + 1: count = 1
origSqr(r, c) = count
showSqr
Pause
count = 2

DO
    r = r - 1
    IF r < 1 AND c = size THEN
        r = r + 2: c = c - 1
    END IF
    IF r < 1 THEN r = size' c = c - 1                     ' out of bounds
    c = c + 1
    IF c > size THEN c = 1                                ' out of bounds
    IF origSqr(r, c) > 0 THEN r = r + 2: c = c - 1        ' rest of diagonal filled
    origSqr(r, c) = count
    showSqr
    Pause
    count = count + 1
LOOP UNTIL count > size * size

END SUB

SUB LoubereM

CLS
PRINT "Modified De La Loubere Method of Magic Square Generation"

PRINT "Does starting at a different cell produce a magic square?"

sum = 0                                                 ' set magic constant to 0

DO
    LOCATE 4, 1
    choice$ = Getletter$("Enter the size of the desired magic Square (3,5,7,or 9 please)", "3,5,7,9")
    size = VAL(choice$)
    choice$ = Getletter$("Enter the row of the starting cell", "1,2,3,4,5,6,7,8,9")
    r = VAL(choice$)
    choice$ = Getletter$("Enter the column of the starting cell", "1,2,3,4,5,6,7,8,9")
    c = VAL(choice$)

     IF r = 1 AND c = (size + 1) / 2 THEN
        PRINT "Starting with this Cell uses the regular Loubere method"
        choice$ = Getletter$("Proceed anyway Y/N ?", "Y,N")
        IF choice$ = "Y" THEN
            ClearText
            LOCATE 3, 1: PRINT "This is the regular Loubere method"
            EXIT DO
        ELSE
            ClearText
        END IF
    ELSE
        ClearText
        EXIT DO                                ' this is not a regular Loubere so proceed
    END IF
LOOP

FOR i = 1 TO size                           ' clear the magic square array
    FOR j = 1 TO size
    origSqr(i, j) = 0
    k = k + 1
    NEXT j
NEXT i

showSqr                                                 ' empty at this point
Pause
count = 1
origSqr(r, c) = count
showSqr                                                 ' first number only here
Pause
count = 2

DO
    r = r - 1
    IF r < 1 THEN r = size                                ' out of bounds
    c = c + 1
    IF c > size THEN c = 1                                ' out of bounds
    IF origSqr(r, c) > 0 THEN r = r + 2: c = c - 1        ' rest of diagonal filled
    IF c < 1 THEN c = size
    IF r > size THEN r = r - size
    origSqr(r, c) = count
    showSqr                                             ' show m.s. after each number is
    Pause                                               '   added to it, and pause  to view
    count = count + 1
LOOP UNTIL count > size * size

END SUB

SUB magicLines

'graph7.bas       4-19-94         Magic Lines


' --- draw a square based on size of array ---


' ---following lines maintains almost constant size regardless of order, and positions square
'        so diagram is centered. 100 & 200 indicates position of top left corner (approximate)
cell = INT(91 / size)                                          ' variable is 22,18,15,13,11,10
lineSize = cell * size                                         '      then draw the square
LINE (2 + cell, 90 + cell)-(2 + cell + lineSize, 90 + cell + lineSize), , B

LOCATE 14, 3: PRINT "Magic  Lines"

' ---locate position of consecutive numbers in the array ---
FOR i = 1 TO size * size                                     'step through consecutive numbers
    startover = o                                            ' flag
    FOR r = 1 TO size                                       'step through position in string
        FOR c = 1 TO size
        IF origSqr(r, c) = i THEN                            ' find position of each number
            startover = 1
            IF origSqr(r, c) = 1 THEN                        'this is the start of the pattern
                starth = (2 + cell / 2) + cell * c
                startv = (90 + cell / 2) + cell * r
                oldh = starth: oldv = startv
            END IF
           
            IF origSqr(r, c) > 1 THEN
                newh = (2 + INT(cell / 2)) + cell * c
                newv = (90 + INT(cell / 2)) + cell * r
                LINE (newh, newv)-(oldh, oldv)
                oldh = newh: oldv = newv
                IF origSqr(r, c) = size * size THEN        ' this is the end of the pattern
                    LINE (starth, startv)-(newh, newv)       'close pattern
                END IF
            END IF
        
        END IF
        NEXT c
        IF startover = 1 THEN EXIT FOR
    NEXT r
NEXT i
 
  
' --- start of routine to show associated pairs ---

cornerh = 2 + cell: cornerv = 235 + cell                   ' constants for origin

LINE (cornerh, cornerv)-(2 + cell + lineSize, 235 + cell + lineSize), , B' draw the square

LOCATE 23, 3: PRINT "Assoc. Pairs"
                     
number = 1                                                 ' 2nd half of m.s. range indicater
abort = 0                                                  ' flag for ginerating module
DO WHILE number <= size * size / 2
    FOR r = 1 TO size                                      'find location for start of line
        FOR c = 1 TO size
            IF origSqr(r, c) = 0 THEN abort = 1            ' needed while generating a m.s.
             'STOP
            IF origSqr(r, c) = number THEN
                startliner = r: startlinec = c
                EXIT FOR
            END IF
        NEXT c
    NEXT r
    FOR r = 1 TO size                                       'find location for end of line
        FOR c = 1 TO size
            IF origSqr(r, c) = size * size + 1 - number THEN
                endliner = r: endlinec = c
                EXIT FOR
            END IF
        NEXT c
    NEXT r
    IF number > size THEN                                          ' move line over 2 pixals
        starth = cornerh + cell * startlinec - cell / 2 + 2        '  so two don't overlap                
        startv = cornerv + cell * startliner - cell / 2 + 2
        endh = cornerh + cell * endlinec - cell / 2 + 2
        endv = cornerv + cell * endliner - cell / 2 + 2
    ELSE
        starth = cornerh + cell * startlinec - cell / 2
        startv = cornerv + cell * startliner - cell / 2
        endh = cornerh + cell * endlinec - cell / 2
        endv = cornerv + cell * endliner - cell / 2
    END IF
    
    IF abort = 0 THEN LINE (starth, startv)-(endh, endv)            ' draw line if no value 0
    number = number + 1

LOOP

END SUB

SUB MatRotate

' This subroutine copies A into B but rotated 90 degrees right.
' This cuts coding in half for OrthoSum, DiagSum, SemiPanSum, and AssocSum
'   by calling these routines twice; once with origSqr() for 1 direction
'   & once with rotateSqr() for the other direction.
' It is called from the displaySqr sub routine.

r = 0

DO
    r = r + 1
    FOR c = 1 TO size
        cc = size + 1 - r                               ' new col is old row
        rotateSqr(c, cc) = origSqr(r, c)                ' new row is old column in reverse
    NEXT c
LOOP WHILE r < size

END SUB

FUNCTION menu (choices$()) STATIC

' The Menu function displays a menu on the screen and elicits a menu
'  choice from the user. Menu receives a string array (choices$)
'  containing the manu choices and returns an integer indicating the
'  users selection from among those choices.
' It is called from the main routine.

listLength = UBOUND(choices$)
DisplayMenuBox choices$(), leftMargin, promptStr$, okStr$

' --- Get a menu choice. Validate and verify the choice.

controlKeys$ = CHR$(13) + CHR$(27)

DO
    LOCATE , , 1
    charPos = 0
    DO
        answer$ = UCASE$(INKEY$)
        IF answer$ <> "" THEN
            charPos = INSTR(okStr$, answer$)
            IF charPos = 0 THEN BEEP
        END IF
    LOOP UNTIL charPos > 0

    PRINT answer$
    LOCATE 11 + listLength, 23, 0
    PRINT " to confirm;  to redo."
    inChoice = charPos
    charPos = 0

    DO
        answer$ = INKEY$
        IF answer$ <> "" THEN
            charPos = INSTR(controlKeys$, answer$)
            IF charPos = 0 THEN BEEP
        END IF
    LOOP UNTIL charPos > 0

    IF charPos = 1 THEN
        done = true
        CLS
    ELSE
        done = false
        LOCATE 11 + listLength, 23: PRINT SPACE$(35)
        LOCATE 9 + listLength, leftMargin + 3 + LEN(promptStr$), 1: PRINT " ";
        LOCATE , POS(0) - 1
    END IF
LOOP UNTIL done

menu = inChoice

END FUNCTION

SUB menu2 (storedSqrs())

' --- Selects the required stored magic cell.
'      Return with selected m.s. in  origSqr & the order size in size.
' It is called from the main routine.

' --- Clear the screen & display the menu.

CLS
LOCATE 2, 30: PRINT " Stored Magic Squares  "
PRINT TAB(11); "All are Normal - consequetive #'s starting with 1 (except l)"
LOCATE 5, 10: PRINT "Order 4"
LOCATE 6, 10: PRINT "a.- Simple magic Square           b.- SemiPandiagonal m. Square"
LOCATE 7, 10: PRINT "c.- SemiPandiagonal Associative   d.- Pandiagonal magic square"
LOCATE 8, 10: PRINT "Order 5"
LOCATE 9, 10: PRINT "e.- SemiPandiagonal Associative   f.- Lozenge-even #'s in corners"
LOCATE 10, 10: PRINT "g.- Simple M. S. - bordered       h.- Pandiagonal - Associated"
LOCATE 11, 10: PRINT "Order 6"
LOCATE 12, 10: PRINT "i.- Simple M. S. - bordered       j.- SemiPandiagonal m. Square"
LOCATE 13, 10: PRINT "k.- Simple M. S. - no 6x6 assoc   l.- Pandiagonal (but not normal"
LOCATE 14, 10: PRINT "Order 7"
LOCATE 15, 10: PRINT "m.- Simple M. S. - bordered       n.- SemiPan Assoc.- Lozenge"
LOCATE 16, 10: PRINT "o.- SemiPandiagonal Associative   p.- Pandiagonal magic square"
LOCATE 17, 10: PRINT "Order 8"
LOCATE 18, 10: PRINT "q.- Simple M. S. - bordered       r.- SemiPandiagonal m. Square"
LOCATE 19, 10: PRINT "s.- SemiPandiagonal Associative   t.- Pandiagonal Associated m.s."
LOCATE 20, 10: PRINT "Order 9"
LOCATE 21, 10: PRINT "u.- Simple M. S. - bordered       v.- SemiPan. Assoc. - lozenge"
LOCATE 22, 10: PRINT "w.- SemiPan. Assoc. - Composite   x.- Semipan - no Pandiagonal 9x9"


frame 7, 75, 1, 23                                          ' draw the frame

LOCATE 24, 25:
choice$ = Getletter$("Enter the appropriate letter", "A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z")
choice$ = UCASE$(choice$)

SELECT CASE choice$
        CASE "A"
            k = 1: size = 4: sum = 34            ' k is starting position in storedSqrs array
        CASE "B"
            k = 17: size = 4: sum = 34
        CASE "C"
            k = 33: size = 4: sum = 34
        CASE "D"
            k = 49: size = 4: sum = 34
        CASE "E"
            k = 65: size = 5: sum = 65           ' order 5
        CASE "F"
            k = 90: size = 5: sum = 65
        CASE "G"
            k = 115: size = 5: sum = 65
        CASE "H"
            k = 140: size = 5: sum = 65
        CASE "I"                                 ' order 6
            k = 165: size = 6: sum = 111
        CASE "J"
            k = 201: size = 6: sum = 111
        CASE "K"                                 ' the 9 2x2 subsquares are each in numerical
            k = 237: size = 6: sum = 111         '  order & sums form a 3x3 m.s.
        CASE "L"
            k = 273: size = 6: sum = 150         ' not a normal m.s. (also 4 & 9 ply)
        CASE "M"                                 ' order 7
            k = 309: size = 7: sum = 175
        CASE "N"
            k = 358: size = 7: sum = 175
        CASE "O"
            k = 407: size = 7: sum = 175
        CASE "P"
            k = 456: size = 7: sum = 175
        CASE "Q"                                 ' order 8
            k = 505: size = 8: sum = 260
        CASE "R"
            k = 569: size = 8: sum = 260
        CASE "S"
            k = 633: size = 8: sum = 260
        CASE "T"
            k = 697: size = 8: sum = 260
        CASE "U"                                 ' order 9
            k = 761: size = 9: sum = 369
        CASE "V"
            k = 842: size = 9: sum = 369
        CASE "W"                                ' Composite - 9 3x3 squares are also
            k = 923: size = 9: sum = 369        '  magic & arranged as a 3x3 magic square
        CASE "X"                                ' Overlap - 2 4x4 upper left & lower right
            k = 1004: size = 9: sum = 369       '  2 5x5 upper right & lower left. All Pandiag

        CASE ELSE
            ON ERROR GOTO 0                     ' Oops, invalid key pressed
        END SELECT

' ---  k = starting position of required m.s. .. size = order .. sum = constant

' --- All the magic squares are stored in 1 long string.
'       The starting position for each m.s. is:
'           order 4;    1, 17, 33, 49           order 5;    65, 90, 115,140
'           order 6;    165,201,237,273         order 7;    309,358,407,456
'           order 8;    505,569,633,697         order 9;    761,842,923,1004 

FOR i = 1 TO size                           ' Enter the selected magic square into origSqr()
    FOR j = 1 TO size
    origSqr(i, j) = storedSqrs(k)
    k = k + 1
    NEXT j
NEXT i

END SUB

SUB menu3

CLS

' --- Selects the required method of generating a pure magic square.
'      Return with generated m.s. in  origSqr & the order size in size.
' It is called from the main routine.

' --- Clear the screen & display the menu.

CLS
LOCATE 2, 20: PRINT " Generate Magic Squares by Different Methods  "
PRINT TAB(12); "All are Normal - i.e. use consecutive #'s starting with 1 "
LOCATE 5, 10: PRINT "Odd Order"
LOCATE 6, 10: PRINT "a.-De La Loubere method           b.- Modified De La Loubere"
LOCATE 7, 10: PRINT "c.-                               d.- "
LOCATE 8, 10: PRINT
LOCATE 9, 10: PRINT "e.-                               f.- "
LOCATE 10, 10: PRINT "g.-                               h.-"
LOCATE 12, 10: PRINT "Even Order "
LOCATE 13, 10: PRINT "i.-                               j.- "
LOCATE 14, 10: PRINT "k.-                               l.- "
LOCATE 15, 10: PRINT
LOCATE 16, 10: PRINT "m.-                               n.- "
LOCATE 17, 10: PRINT "o.-                               q.- Return to main menu"

frame 7, 75, 1, 21                                          ' draw the frame

LOCATE 19, 25:
choice$ = Getletter$("Enter the appropriate letter", "A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,Q")
choice$ = UCASE$(choice$)

SELECT CASE choice$
        CASE "A"
           Loubere                                      ' goto the De La Loubere method
        CASE "B"
            LoubereM                                    ' go to the modified De La Loubere
        CASE "C"
            EXIT SUB
        CASE "D"
            EXIT SUB
        CASE "E"
            EXIT SUB
        CASE "F"
            EXIT SUB
        CASE "G"
            EXIT SUB
        CASE "H"
            EXIT SUB
        CASE "I"
            EXIT SUB
        CASE "J"
            EXIT SUB
        CASE "K"
            EXIT SUB
        CASE "L"
            EXIT SUB
        CASE "M"
            EXIT SUB
        CASE "N"
            EXIT SUB
        CASE "O"
            EXIT SUB
        CASE "Q"
            EXIT SUB                            ' return to main menu

        CASE ELSE
            ON ERROR GOTO 0                     ' Oops, invalid key pressed
        END SELECT

END SUB

SUB OrthoSum (matrixA())

' sub OrthoSum calculates & checks the row sums (call 1),& column sums (call 2)
' it is called form displaySqr sub-routine

sumCorrect = 0                                      ' reset count of correct sums

FOR i = 1 TO size
    testSum = 0                                     ' reset for new row or column
    FOR j = 1 TO size
        testSum = testSum + matrixA(i, j)
    NEXT j
    IF testSum = sum THEN                           ' if sum is correct then
        sumCorrect = sumCorrect + 1                 '  increment flag
    END IF
NEXT i

END SUB

SUB pan5x5 (matrixA(), pTbl(), pairnum())

' adapted from PAN5X5M4.BAS

' This program will generate the 36 essentially different 5 x 5 pandiagonal
'   magic squares.
' There are 99 additional magic squares that are cyclic permutations of each
'   of these, for a total of 3600 different 5 x 5 pandiagonal magic squares.
'   Of course each of these has 7 variations because of rotations and/or
'   reflections. (i.e. 'camoflaged' or 'disguised' copies).

' This program relies heavily on information in chapter 19 of 'New
'   Recreations in Magic Squares' by Benson & Jacoby, Dover Publ. 1976.

' In retrospect, it would have been simpler, and taken less memory to simply list
'  the integers of the 36 magic squares.

' It is called from the main routine.

DIM bSqr1(5, 5)                                    ' base square 1
DIM bSqr2(5, 5)                                    ' base square 2

Title1$ = "Calculate & Display all 36 Essentially Different Order 5 Pandiagional Magic Sqrs"

a = 0: B = 5: v = 1: w = 2                         ' these four variables are fixed

size = 5
snum = 1                                           ' start @ 1st square in the sequence
CLS



 
 
 
PRINT Title1$
PRINT : PRINT "Each of these 36 magic squares have 99 variations (plus the 3 reflections & 4"
PRINT "rotations), for a total of 3600 pandiagonal magic squares of order five."
PRINT "      There are 0 order 3, 6, & 9; 48 order 4; 678,222,720 order 7; ? order 8"

'VIEW PRINT 5 TO 23

Report$ = Getletter$("Print a listing of Magic Squares (Y/N)", "YN")

IF Report$ = "Y" THEN
    PRINT : PRINT "Turn on the printer   *** set it to 15 CPI ***"
    OPEN "lpt1:" FOR OUTPUT AS #1
    WIDTH "lpt1:", 110                             ' set printer output for wider lines
    Pause                                          ' wait for user response
    ON ERROR GOTO PrinterError                     ' set the error trap
END IF

IF Report$ = "Y" THEN                              ' print report heading
   PRINT #1, "": PRINT #1, "                   ***    List of ALL Basic Pandiagonal Magic Squares of Order 5 ***"
   PRINT #1, "": PRINT #1, "      Each of these 36 squares have 99 variations (plus the 3 rotations & 4 reflections),"
   PRINT #1, "      for a total of 3600 Pandiagonal Magic Squares of order 5."
   PRINT #1, "      There are 0 order 3, 48 order 4, 0 order 6, 678,222,720 order 7, ? order 8, & 0 order 9"
   PRINT #1, "": PRINT #1, "      No.    row 1            row 2             row 3             row 4             row 5"
END IF

CLS
  
endroutine = false                                 ' be sure flag is clear         s
conSqr = 1                                         ' consequetive order of square

DO                                                 ' cycle through the 36 squares
   CLS
    frNum = pTbl(snum, 2)                          ' Frenicle number ???
    bSqr = pTbl(snum, 3)                           ' basic square number (1 or 2)
    c = pTbl(snum, 4): d = pTbl(snum, 5): e = pTbl(snum, 6)
    qx = pTbl(snum, 7): Y = pTbl(snum, 8): z = pTbl(snum, 9)

    ' --- calculate values for cells .. this is basic square 1

    bSqr1(1, 1) = a + v: bSqr1(1, 2) = B + w: bSqr1(1, 3) = c + qx
    bSqr1(1, 4) = d + Y: bSqr1(1, 5) = e + z
    bSqr1(2, 1) = c + Y: bSqr1(2, 2) = d + z: bSqr1(2, 3) = e + v
    bSqr1(2, 4) = a + w: bSqr1(2, 5) = B + qx
    bSqr1(3, 1) = e + w: bSqr1(3, 2) = a + qx: bSqr1(3, 3) = B + Y
    bSqr1(3, 4) = c + z: bSqr1(3, 5) = d + v
    bSqr1(4, 1) = B + z: bSqr1(4, 2) = c + v: bSqr1(4, 3) = d + w
    bSqr1(4, 4) = e + qx: bSqr1(4, 5) = a + Y
    bSqr1(5, 1) = d + qx: bSqr1(5, 2) = e + Y: bSqr1(5, 3) = a + z
    bSqr1(5, 4) = B + v: bSqr1(5, 5) = c + w

    ' --- calculate values for cells .. this is basic square 2
  
    bSqr2(1, 1) = a + v: bSqr2(1, 2) = B + w: bSqr2(1, 3) = c + qx
    bSqr2(1, 4) = d + Y: bSqr2(1, 5) = e + z
    bSqr2(2, 1) = d + qx: bSqr2(2, 2) = e + Y: bSqr2(2, 3) = a + z
    bSqr2(2, 4) = B + v: bSqr2(2, 5) = c + w
    bSqr2(3, 1) = B + z: bSqr2(3, 2) = c + v: bSqr2(3, 3) = d + w
    bSqr2(3, 4) = e + qx: bSqr2(3, 5) = a + Y
    bSqr2(4, 1) = e + w: bSqr2(4, 2) = a + qx: bSqr2(4, 3) = B + Y
    bSqr2(4, 4) = c + z: bSqr2(4, 5) = d + v
    bSqr2(5, 1) = c + Y: bSqr2(5, 2) = d + z: bSqr2(5, 3) = e + v
    bSqr2(5, 4) = a + w: bSqr2(5, 5) = B + qx
   
    IF bSqr = 1 THEN
        FOR i = 1 TO 5                                ' transfer values from
            FOR j = 1 TO 5                            '   basic square 1 to
                origSqr(i, j) = bSqr1(i, j)              '     the magic square
            NEXT j
        NEXT i
    ELSE                                              ' Or
        FOR i = 1 TO 5                                ' transfer values from
            FOR j = 1 TO 5                            '   basic square 2 to
                origSqr(i, j) = bSqr2(i, j)              '     the magic square
            NEXT j
        NEXT i
    END IF
  
    displaySqr pairnum()                              ' display m.s. & report

    IF Report$ = "Y" THEN
        PRINT #1, : PRINT #1, "     ";                ' set to condensed type first
        PRINT #1, " ";
        PRINT #1, USING "##"; frNum; : PRINT #1, "   ";      ' print a quick & dirty
        FOR i = 1 TO 5                                       '  list of magic squares
            FOR j = 1 TO 5                                   '   1 m.s. per line
                PRINT #1, USING "##"; origSqr(i, j); : PRINT #1, " ";
            NEXT j
            PRINT #1, "   ";
        NEXT i
    END IF

    ON KEY(2) GOSUB keyevent
    KEY(2) ON
   
    LOCATE 1, 1
    PRINT Title1$
    PRINT : PRINT "Each of these 36 magic squares have 99 variations (plus the 3 reflections & 4"
    PRINT "rotations), for a total of 3600 pandiagonal magic squares of order five."
    PRINT "      There are 0 order 3, 6, & 9; 48 order 4; 678,222,720 order 7; ? order 8"
   
    LOCATE 19, 20: PRINT "Frenicle Number:"; frNum
    LOCATE 19, 45: PRINT "Basic square Number:"; bSqr
    LOCATE 20, 20: PRINT "Consequetive number:"; conSqr: conSqr = conSqr + 1
    LOCATE 21, 20: PRINT "See New Recreations With Magic Squares "
    LOCATE 22, 23: PRINT "by Benson & Jacoby, page 137"
    LOCATE 23, 20
    PRINT "F2, then spacebar to exit";   ' provision for exiting this routine
    Pause
    IF endroutine THEN CLS : EXIT SUB         ' leave orderly
    snum = snum + 1
LOOP UNTIL snum > 36

'VIEW PRINT: CLS                                          ' all 36 m.s. displayed !

END SUB

SUB Pause STATIC

' The pause sub-routine allows the operater to view the screen until
' ready to proceed. It puts a message on the lower center of the
' screen and waits for a key-press.
' It is called from the main program & pan5X5.

           
'COLOR 0, 4
LOCATE 30, 30: PRINT "Press any key to continue.";
'COLOR 7, 1

DO WHILE INKEY$ = ""
   
     ON KEY(2) GOSUB keyevent
    KEY(2) ON

LOOP

LOCATE 30, 30: PRINT "                            ";

END SUB

SUB SemiPanSum (matrixA())

' This subroutine tests the opposite short diagonals. If each pair sums correctly
'  it indicates a semi pandiagonal magic square.
' It is only called if the square is NOT pandiagonal, and is called twice,
'  once with the original square, and once with the rotated square, in order to
'  test both pairs (2 directions).
' Tests all order magic squares for the correct constant of both opposite short
'  diagonal pairs.
' Test for singly-even order, if desired, before calling this routine, because
'  there are no semi-pandiagonal magic squares of this order.
' It is called form the displaySqr sub-routine.

sumCorrect = 0
halfSize = size \ 2                                ' integer division

r = halfSize: c = 1

IF halfSize * 2 <> size THEN                       ' order is odd because halfSize
                                                   ' took only integer value
    DO
        testSum = testSum + matrixA(r, c)          ' add cells in 1st half of diagonal pair
        r = r - 1: c = c + 1
    LOOP WHILE r > 0
    r = size
    c = c + 1                                     ' move to column past center
                                                  
    DO                                            ' add cells in 2nd half of diagonal pair
        testSum = testSum + matrixA(r, c)
        r = r - 1: c = c + 1
    LOOP WHILE r > halfSize + 1                   ' stop before center row
   
    testSum = testSum + matrixA((size + 1) / 2, (size + 1) / 2)    ' add value of center cell
   
    IF testSum = sum THEN                         ' test for constant
        sumCorrect = 1
    END IF

ELSE                                              ' square order is even

    DO                                            '    so use this routine
        testSum = testSum + matrixA(r, c)         ' 1st half of pair
        r = r - 1: c = c + 1                      ' no need to add center cell &
    LOOP WHILE r > 0                              ' c doesn't have to jump over center
    r = size
    DO
        testSum = testSum + matrixA(r, c)         ' 2nd half of pair
        r = r - 1: c = c + 1
    LOOP WHILE r > halfSize

    IF testSum = sum THEN                         ' test for correct constant
        sumCorrect = 1
    END IF

END IF

END SUB

SUB showSqr

' --- sub showSqr prints the matrix to the screen
'      This is a general purpose subprogram that handles any order of m.s.
' It is called form the main program & also from displaySqr sub-routine.

' --- calculate frame parameters and call it

halfSize = size \ 2
top = 13 - size: bottom = top + size * 2                ' determine location & size of display
left = 39 - halfSize * 6: right = left + size * 6

frame left, right, top, bottom                          ' and call frame s.r.

FOR row = top + 2 TO bottom - 2 STEP 2                  ' draw mid horizontal lines
    FOR col = left + 1 TO right - 1
        LOCATE row, col
            PRINT CHR$(196)
NEXT col, row

firstVert = left + 6: lastVert = right - 6          ' location of 1st & last vertical lines

FOR col = firstVert TO lastVert STEP 6              ' draw mid vertical lines
    FOR row = top + 1 TO bottom - 1
        LOCATE row, col
        PRINT CHR$(179)
NEXT row, col

FOR i = 1 TO size                                   ' print the cell contents
    FOR j = 1 TO size
        row = top + 2 * i - 1
        col = left + 6 * j - 4
        LOCATE row, col
        PRINT USING "###"; origSqr(i, j)
NEXT j, i

IF nograph = 0 THEN magicLines

END SUB

SUB sqrInput

' This routine allows input directly to the cells of the square.
'   Best use is for changing a few cells to form a different m.s.
' Called by the main program.

' --- Values for keys on the curser keys and the spacebar:
CONST UP = 72, DOWN = 80, rightArrow = 77, SPACE = " "

halfSize = size \ 2                                     ' these 3 rows exactly the same
top = 11 - size: bottom = top + size * 2                ' as showSqr
left = 39 - halfSize * 6: right = left + size * 6       ' determine location & size of display

' --- Null$ is the first character of the two-character INKEY$
'       value returned for direction keys such as UP and DOWN:
Null$ = CHR$(0)

LOCATE 1, 27: PRINT "Edit or Input a Magic Square"
LOCATE 21, 26: PRINT "Press 'q' to exit this module"
LOCATE 22, 5: PRINT "You MUST enter 3 digits for each cell (i.e. 001 for 1; or space,space,1)"
LOCATE 23, 5: PRINT "Correct errors by going to the right, up, or down until back at the cell"
LOCATE 24, 4: PRINT "Numbers (not digits) you pass over with the arrow keys will not be changed";
r = top + 3: c = left + 2: i = 1: j = 1
B$ = "": a$ = ""

DO

   SELECT CASE keyVal$
       CASE Null$ + CHR$(UP)                               ' move up to next row
           r = r - 2:  i = i - 1
           IF r < top + 1 THEN                             ' if at top, go to bottom
               r = bottom - 1: i = size
           END IF
       CASE Null$ + CHR$(DOWN)                             ' move down to next row
           r = r + 2:  i = i + 1
           IF r > bottom - 1 THEN                          ' if at bottom, go to top
               r = top + 1: i = 1
           END IF
       CASE Null$ + CHR$(rightArrow)                        ' move to next digit position
           c = c + 1
           IF c > right - 2 THEN                            ' go back to the start of the row
               c = left + 2: j = 1
           END IF
       CASE SPACE                                           ' enter key values
           c = c + 1: a$ = " ": PRINT " ";
       CASE "1"
           LOCATE r, c: PRINT keyVal$
           c = c + 1: a$ = "1"
       CASE "2"
           LOCATE r, c: PRINT keyVal$
           c = c + 1: a$ = "2"
       CASE "3"
           LOCATE r, c: PRINT keyVal$
           c = c + 1: a$ = "3"
       CASE "4"
           LOCATE r, c: PRINT keyVal$
           c = c + 1: a$ = "4"
       CASE "5"
           LOCATE r, c: PRINT keyVal$
           c = c + 1: a$ = "5"
       CASE "6"
           LOCATE r, c: PRINT keyVal$
           c = c + 1: a$ = "6"
       CASE "7"
           LOCATE r, c: PRINT keyVal$
           c = c + 1: a$ = "7"
       CASE "8"
           LOCATE r, c: PRINT keyVal$
           c = c + 1: a$ = "8"
       CASE "9"
           LOCATE r, c: PRINT keyVal$
           c = c + 1: a$ = "9"
        CASE "0"
           LOCATE r, c: PRINT keyVal$
           c = c + 1: a$ = "0"
        CASE ELSE                ' The user pressed some key other than one of the direction
                                 ' keys, or the number keys, so don't do anything.
           
   END SELECT

   LOCATE r, c, 1                                  ' make curser visible
   keyVal$ = UCASE$(INKEY$)

   IF c MOD 6 = 2 THEN                             ' skip to next cell
       c = c + 3: j = j + 1
   END IF
     
   IF LEN(B$) < 3 AND LEN(a$) > 0 THEN             ' add digits to the number
       B$ = B$ + a$: a$ = ""                       '   until there are 3 digits
   END IF
   IF LEN(B$) = 3 THEN
       num = VAL(B$)
       B$ = ""
       IF num = 0 THEN                             ' keep original number
           origSqr(i, j) = origSqr(i, j)
       ELSE                                        ' save new number
           origSqr(i, j - 1) = num
           IF c > right - 2 THEN                   ' you are at the end of the row
               c = left + 2: j = 1                 '   go back to the start
           END IF
      
       END IF
   END IF
     
LOOP UNTIL keyVal$ = "Q"                        ' exit this routine

    sum = 0                                     ' recalculate the constant
    FOR c = 1 TO size
        sum = sum + origSqr(1, c)               ' sum cells of top row
    NEXT c

END SUB

SUB titleOne

'  --- This sub-routine prints the main title when required.
'     It is called from various routines

PRINT "MAGICSQR.BAS --- Tests & demonstrates MAGIC SQUARES & use of general order S.R. "

END SUB

    Source: geocities.com/harveyhd/Downloads

               ( geocities.com/harveyhd)