Penholodigital squares

From Rosetta Code
Penholodigital squares is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

Penholodigital squares are perfect square numbers that contain all of the digits from the base in which the number is represented, except for zero, exactly once.

From the Latin prefix pene- (before, or next to, nearly)

and holo- (whole, or all)

penholodigital: Nearly-all-digits.

So, in a particular base, a penholodigital square number will contain all of the digits used in that base (except zero) once, and only once. Base eight penholodigitals contain the digits 1 through 7, base 10, 1 through 9, etc.


For example

In base 10, 139854276 is a penholodigital square. It is the square of the integer 11826, and contains every digit from 1 through 9 exactly once.


Penholodigital squares can occur in many, though not every, base. They tend to be pretty rare in lower bases.

There is a total of 1 penholodigital squares in base 2:
1² = 1

There is a total of 0 penholodigital squares in base 3:

There is a total of 0 penholodigital squares in base 4:

There is a total of 0 penholodigital squares in base 5:

There is a total of 2 penholodigital squares in base 6:
122² = 15324, 221² = 53241

There is a total of 1 penholodigital squares in base 7:
645² = 623514

There is a total of 1 penholodigital squares in base 8:
2453² = 6532471


Task

Find and display the total count, and the penholodigital squares and the integers that are squared to produce them, represented in the base in which they are calculated, for bases 9, 10, 11 and 12.


Stretch

Find and display the total count, and the first and last penholodigital squares and the integers that are squared to produce them, represented in the base in which they are calculated, for bases 13, 14, 15, ... ?


See also


AppleScript

on penholodigitalSquares(base)
    set digits to "123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    set output to {}
    set minFromDigits to 1
    repeat with d from 2 to (base - 1)
        set minFromDigits to minFromDigits * base + d
    end repeat
    set maxFromDigits to base - 1
    repeat with d from (base - 2) to 1 by -1
        set maxFromDigits to maxFromDigits * base + d
    end repeat
    repeat with sqrt from (round (minFromDigits ^ 0.5) rounding up) to (maxFromDigits ^ 0.5 div 1)
        set n to sqrt * sqrt
        set usedDigitValues to {0}
        set OKSoFar to true
        repeat (base - 2) times -- until (n < base)
            set d to n mod base
            if (d is in usedDigitValues) then
                set OKSoFar to false
                exit repeat
            end if
            set usedDigitValues's end to d
            set n to n div base
        end repeat
        if ((OKSoFar) and (n is not in usedDigitValues)) then ¬
            set end of output to {intToBase(sqrt, base), intToBase(sqrt * sqrt, base)}
    end repeat
    
    return output
end penholodigitalSquares

on intToBase(int, base)
    if ((int < 0) or (int mod 1 > 0) or (base < 2) or (base > 36)) then return missing value
    set digits to "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    set output to digits's character (int mod base + 1)
    repeat until (int < base)
        set int to int div base
        set output to digits's character (int mod base + 1) & output
    end repeat
    return output
end intToBase

on join(lst, delim)
    set astid to AppleScript's text item delimiters
    set AppleScript's text item delimiters to delim
    set txt to lst as text
    set AppleScript's text item delimiters to astid
    return txt
end join

on task()
    set output to {}
    repeat with base from 9 to 14
        set results to penholodigitalSquares(base)
        set resultCount to (count results)
        if (resultCount > 1) then
            set output's end to linefeed & "There are " & resultCount & ¬
                (" penholodigital squares in base " & base & ":")
            if (base < 13) then
                repeat with i from 1 to resultCount by 3
                    set row to {}
                    set k to i + 2
                    if (k > resultCount) then set k to resultCount
                    repeat with j from i to k
                        set end of row to join(results's item j, " ^ 2 = ")
                    end repeat
                    set output's end to join(row, "    ")
                end repeat
            else
                set output's end to "First: " & join(results's beginning, " ^ 2 = ") & ¬
                    ("    Last: " & join(results's end, " ^ 2 = "))
            end if
        else if (resultCount = 1) then
            set output's end to linefeed & "There is 1 penholodigital square in base " & ¬
                base & ":"
            set output's end to join(results's beginning, " ^ 2 = ")
        else
            set output's end to linefeed & "There are no penholodigital squares in base " & base
        end if
    end repeat
    return join(output, linefeed)
end task

task()
Output:
"
There are 10 penholodigital squares in base 9:
3825 ^ 2 = 16328547    3847 ^ 2 = 16523874    4617 ^ 2 = 23875614
4761 ^ 2 = 25487631    6561 ^ 2 = 47865231    6574 ^ 2 = 48162537
6844 ^ 2 = 53184267    7285 ^ 2 = 58624317    7821 ^ 2 = 68573241
8554 ^ 2 = 82314657

There are 30 penholodigital squares in base 10:
11826 ^ 2 = 139854276    12363 ^ 2 = 152843769    12543 ^ 2 = 157326849
14676 ^ 2 = 215384976    15681 ^ 2 = 245893761    15963 ^ 2 = 254817369
18072 ^ 2 = 326597184    19023 ^ 2 = 361874529    19377 ^ 2 = 375468129
19569 ^ 2 = 382945761    19629 ^ 2 = 385297641    20316 ^ 2 = 412739856
22887 ^ 2 = 523814769    23019 ^ 2 = 529874361    23178 ^ 2 = 537219684
23439 ^ 2 = 549386721    24237 ^ 2 = 587432169    24276 ^ 2 = 589324176
24441 ^ 2 = 597362481    24807 ^ 2 = 615387249    25059 ^ 2 = 627953481
25572 ^ 2 = 653927184    25941 ^ 2 = 672935481    26409 ^ 2 = 697435281
26733 ^ 2 = 714653289    27129 ^ 2 = 735982641    27273 ^ 2 = 743816529
29034 ^ 2 = 842973156    29106 ^ 2 = 847159236    30384 ^ 2 = 923187456

There are 20 penholodigital squares in base 11:
42045 ^ 2 = 165742A893    43152 ^ 2 = 173A652894    44926 ^ 2 = 18792A6453
47149 ^ 2 = 1A67395824    47257 ^ 2 = 1A76392485    52071 ^ 2 = 249A758631
54457 ^ 2 = 2719634A85    55979 ^ 2 = 286A795314    59597 ^ 2 = 314672A895
632A4 ^ 2 = 3671A89245    64069 ^ 2 = 376198A254    68335 ^ 2 = 41697528A3
71485 ^ 2 = 46928A7153    81196 ^ 2 = 5A79286413    83608 ^ 2 = 632A741859
86074 ^ 2 = 6713498A25    89468 ^ 2 = 7148563A29    91429 ^ 2 = 76315982A4
93319 ^ 2 = 795186A234    A3A39 ^ 2 = 983251A764

There are 23 penholodigital squares in base 12:
117789 ^ 2 = 135B7482A69    16357B ^ 2 = 23A5B976481    16762B ^ 2 = 24AB5379861
16906B ^ 2 = 25386749BA1    173434 ^ 2 = 26B859A3714    178278 ^ 2 = 2835BA17694
1A1993 ^ 2 = 34A8125B769    1A3595 ^ 2 = 354A279B681    1B0451 ^ 2 = 3824B7569A1
1B7545 ^ 2 = 3A5B2487961    2084A9 ^ 2 = 42A1583B769    235273 ^ 2 = 5287BA13469
2528B5 ^ 2 = 5B23A879641    25B564 ^ 2 = 62937B5A814    262174 ^ 2 = 63A8527B194
285A44 ^ 2 = 73B615A8294    29A977 ^ 2 = 7B9284A5361    2A7617 ^ 2 = 83AB5479261
2B0144 ^ 2 = 8617B35A294    307381 ^ 2 = 93825A67B41    310828 ^ 2 = 96528AB7314
319488 ^ 2 = 9AB65823714    319A37 ^ 2 = 9B2573468A1

There are no penholodigital squares in base 13

There are 160 penholodigital squares in base 14:
First: 1129535 ^ 2 = 126A84D79C53B    Last: 3A03226 ^ 2 = DB3962A7541C8"

Delphi

Works with: Delphi version 6.0


{Library routine included here for clarity}

function GetRadixString(L: int64; Radix: Byte): string;
{Converts integer a string of any radix}
const RadixChars: array[0..35] Of char =
    ('0', '1', '2', '3', '4', '5', '6', '7',
     '8', '9', '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');
var I: cardinal;
var S: string;
var Sign: string[1];
begin
Result:='';
If (L < 0) then
	begin
	Sign:='-';
	L:=Abs(L);
	end
else Sign:='';
S:='';
repeat
	begin
	I:=L mod Radix;
	S:=RadixChars[I] + S;
	L:=L div Radix;
	end
until L = 0;
Result:=Sign + S;
end;



function IsPenholoSquare(N: int64; Radix: byte): boolean;
{Test if N is a Penholodigital square}
var S: string;
var SL: TStringList;
var I: integer;
begin
Result:=False;
SL:=TStringList.Create;
try
{Get text version of number in the radix}
S:=GetRadixString(N,Radix);
{Reject any number with a zero in it}
if Pos('0',S)>0 then exit;
{Make sure string list doesn't duplicates}
SL.Sorted:=True;
SL.Duplicates:=dupIgnore;
{Insert each digit as one string in the list}
for I:=1 to Length(S) do SL.Add(S[I]);
{Same number of unique digits as radix-1? }
Result:=SL.Count=Radix-1;
finally SL.Free; end;
end;


function GetNDigitNumber(Digits,Radix: integer): extended;
{Get smallest N-digit number of specified radix}
begin
Result:=Power(10,(Digits-1) * Log(Radix));
end;


procedure GetStartStop(Radix: integer; var Start,Stop: int64);
{Start/Stop = Range of numbers to check}
{Since zero is not allowed, we want number width digits = Radix-1}
{Start/Stop squared = Smallest largest number that could match}
var S1,S2,Sqrt1,Sqrt2: extended;
begin
Start:=Trunc(Sqrt(GetNDigitNumber(Radix-1,Radix)));
Stop:=Trunc(Sqrt(GetNDigitNumber(Radix,Radix)-1));
end;


procedure FindAllPenholoSquares(Memo: TMemo; Radix: integer);
{Find all the Penholodigital squares for the specified radix}
var I,Start,Stop: int64;
var S,H: string;
var Cnt: integer;
begin
Cnt:=0;
{Get the range to look over}
GetStartStop(Radix,Start,Stop);
{Scan all the number for Penholodigital squares}
I:=Start;
while I<=Stop do
	begin
	if IsPenholoSquare(I*I,Radix) then
		begin
		Inc(Cnt);
		S:=S+GetRadixString(I,Radix)+'² = '+GetRadixString(I*I,Radix);
		if I<Stop then S:=S+'   ';
		If (Cnt mod 3)=0 then S:=S+CRLF;
		end;
	Inc(I);
	end;
H:=CRLF+'Penholodigital squares in base '+IntToStr(Radix)+CRLF;
H:=H+'Start: '+GetRadixString(Start,Radix)+'² = '+GetRadixString(Start*Start,Radix)+CRLF;
H:=H+'Stop:  '+GetRadixString(Stop,Radix)+'² = '+GetRadixString(Stop*Stop,Radix)+CRLF;
H:=H+'Count='+IntToStr(Cnt)+CRLF;
Memo.Lines.Add(H+S);
end;


procedure ShowPenholoSquares(Memo: TMemo);
{Show Penholodigital squares for various radices}
var N,C: int64;
begin
FindAllPenholoSquares(Memo,9);
FindAllPenholoSquares(Memo,10);
FindAllPenholoSquares(Memo,11);
FindAllPenholoSquares(Memo,12);
FindAllPenholoSquares(Memo,13);
FindAllPenholoSquares(Memo,14);
end;
Output:

Penholodigital squares in base 9
Start: 3000² = 10000000
Stop:  8888² = 88870001
Count=10
3825² = 16328547   3847² = 16523874   4617² = 23875614   
4761² = 25487631   6561² = 47865231   6574² = 48162537   
6844² = 53184267   7285² = 58624317   7821² = 68573241   
8554² = 82314657   

Penholodigital squares in base 10
Start: 10000² = 100000000
Stop:  31622² = 999950884
Count=30
11826² = 139854276   12363² = 152843769   12543² = 157326849   
14676² = 215384976   15681² = 245893761   15963² = 254817369   
18072² = 326597184   19023² = 361874529   19377² = 375468129   
19569² = 382945761   19629² = 385297641   20316² = 412739856   
22887² = 523814769   23019² = 529874361   23178² = 537219684   
23439² = 549386721   24237² = 587432169   24276² = 589324176   
24441² = 597362481   24807² = 615387249   25059² = 627953481   
25572² = 653927184   25941² = 672935481   26409² = 697435281   
26733² = 714653289   27129² = 735982641   27273² = 743816529   
29034² = 842973156   29106² = 847159236   30384² = 923187456   


Penholodigital squares in base 11
Start: 33534² = AAAA63735
Stop:  AAAAA² = AAAA900001
Count=20
42045² = 165742A893   43152² = 173A652894   44926² = 18792A6453   
47149² = 1A67395824   47257² = 1A76392485   52071² = 249A758631   
54457² = 2719634A85   55979² = 286A795314   59597² = 314672A895   
632A4² = 3671A89245   64069² = 376198A254   68335² = 41697528A3   
71485² = 46928A7153   81196² = 5A79286413   83608² = 632A741859   
86074² = 6713498A25   89468² = 7148563A29   91429² = 76315982A4   
93319² = 795186A234   A3A39² = 983251A764   

Penholodigital squares in base 12
Start: 100000² = 10000000000
Stop:  3569B7² = BBBBB983821
Count=23
117789² = 135B7482A69   16357B² = 23A5B976481   16762B² = 24AB5379861   
16906B² = 25386749BA1   173434² = 26B859A3714   178278² = 2835BA17694   
1A1993² = 34A8125B769   1A3595² = 354A279B681   1B0451² = 3824B7569A1   
1B7545² = 3A5B2487961   2084A9² = 42A1583B769   235273² = 5287BA13469   
2528B5² = 5B23A879641   25B564² = 62937B5A814   262174² = 63A8527B194   
285A44² = 73B615A8294   29A977² = 7B9284A5361   2A7617² = 83AB5479261   
2B0144² = 8617B35A294   307381² = 93825A67B41   310828² = 96528AB7314   
319488² = 9AB65823714   319A37² = 9B2573468A1   

Penholodigital squares in base 13
Start: 37B451² = CCCCC61C7A1
Stop:  CCCCCC² = CCCCCB000001
Count=0


Penholodigital squares in base 14
Start: 1000000² = 1000000000000
Stop:  3A55171² = DDDDDD67CDA01
Count=160
1129535² = 126A84D79C53B   1145393² = 12A68DB5374C9   117D854² = 134596CAB87D2   
11865A8² = 1356BC247D9A8   11888A3² = 135BAD2678C49   1213ACA² = 146CA78D53B92   
1225AA9² = 149785AC62D3B   1235DC3² = 14BC765DA8329   1279052² = 157AB3D289C64   
12894A5² = 15A29D468C73B   128B953² = 15A8364DC7B29   12B30AC² = 1623D89B7A5C4   
12D71B4² = 167B48ACD3952   13485D5² = 1763D8CA9245B   136C494² = 17BD495AC8632   
138BC89² = 182C6D7534A9B   1393B93² = 183D645B72AC9   13A0084² = 185B36D7A4C92   
1461C78² = 1A3D7C6925B48   1470999² = 1A6548739C2DB   149AA14² = 1AD8546C37B92   
1508CA3² = 1BCA2D5736849   151DB35² = 1C25A647D893B   152649C² = 1C398AD765B24   
1576CAB² = 1D38AB65C7249   157C257² = 1D4965C8BA237   15809D3² = 1D527B3A6C489   
1586757² = 1D64BA5C89237   16D1B9B² = 23457BCD861A9   180D3D1² = 26ADB397548C1   
183B736² = 2761354D9CBA8   1877349² = 283CAD946157B   190AA09² = 29D738461AC5B   
193AAA9² = 2A9657C148D3B   19910D6² = 2BD357619AC48   19A4195² = 2C35164A98D7B   
1A1030B² = 2D56B71C34A89   1A18B62² = 2D785C3691BA4   1B11831² = 32CDA84759B61   
1B84906² = 34B17956CDA28   1B909D9² = 34DA578296C1B   1BBB53A² = 359C84AD761B2   
1C0C851² = 3674BC2598DA1   1C21091² = 36BA29D587C41   1C25991² = 36CD78A95B241   
1C9891C² = 38C2A5DB71964   1CA7D36² = 391B6C2475DA8   1CC1274² = 3978B56A4CD12   
1D034D5² = 3A1CD2476985B   1D18196² = 3A716D45B92C8   1D53476² = 3B74AC96D1528   
1D82B89² = 3C4DA1285769B   1D891AA² = 3C6A79415D8B2   1D9C33B² = 3CB8216D57A49   
1DD841C² = 3DC529A78B164   205B2B8² = 41952D3A67BC8   206B1C7² = 41D5BA69C8237   
208A775² = 427589D63AC1B   20D0164² = 43AC6DB981572   216BBBA² = 46185C9ADB732   
217972B² = 46518B2C3D7A9   21842D9² = 467A53289DC1B   21B1038² = 4756D93B12CA8   
21BD039² = 47921586CAD3B   22188B7² = 48AD25C19B637   2220ADA² = 48CB93D567A12   
22228DA² = 48D593BA76C12   2257406² = 49DB5A71C3628   226D835² = 4A619C8D5273B   
227211B² = 4A6C7D1385B29   22835B8² = 4AB96D12753C8   22CB813² = 4C3B528DA1769   
2304629² = 4CA59718362DB   2402131² = 5329784CDAB61   24080CD² = 5348C7A6B9D21   
24141A4² = 537CD86A941B2   244C9D8² = 54A7B93CD1628   249A12B² = 564CBD78321A9   
24DD54B² = 57AB12834D6C9   255AD7A² = 59A6148D3CB72   255C168² = 59AB746D1C328   
256AD5B² = 5A12D34C78B69   2576D8B² = 5A48C673B1D29   2586549² = 5A9328C641D7B   
2588178² = 5A9B6C21D7348   259CCB1² = 5B23CA79D4681   25C895B² = 5C1438ADB2769   
25CB949² = 5C24A986D317B   25D251B² = 5C3D784AB6129   261D43A² = 5D36C7418A9B2   
26280BA² = 5D6789AC41B32   262BA53² = 5D7B861CA4329   2634B3A² = 5DA41C73689B2   
267941A² = 61374A895BCD2   270A8B1² = 63AB275D49C81   276CC76² = 65DBC7A914328   
27A7D0D² = 67458DB39A2C1   27D30D1² = 683DB5A9742C1   2810A05² = 68D5A943C721B   
2811842² = 68DA3C95B1724   2837D42² = 69C5B1738AD24   2859D11² = 6A975BC843D21   
296B7C5² = 7254638A1DC9B   29D7768² = 74D6CA935B128   2AA5C1C² = 793185C2DAB64   
2AD13CB² = 7A3D28B14C569   2B20281² = 7B63459CA8D21   2B21C1A² = 7B6CA139854D2   
2B5A308² = 7CD13AB529648   2C40134² = 83CD916A574B2   2CB5679² = 86DA721493C5B   
2D26547² = 8915B2ADC4637   2D740A3² = 8B1D75A2C3649   2DA0095² = 8C415DA92637B   
30BB095² = 95162DCA8437B   30C0C0C² = 95317BC2D68A4   30D632C² = 95B83DC167A24   
30D6D04² = 95BC8347AD612   3150917² = 9841B5A6CD237   3191C1C² = 9A175D382CB64   
3193C92² = 9A261B73CD584   31C0878² = 9B5A1726CD348   3206366² = 9C6B3D2A51748   
3276C18² = A195234DB7C68   32781B7² = A19D2CB854637   3307D3A² = A4C918563D7B2   
33867CD² = A87B49365CD21   33A00DA² = A93B857C4D612   33A3BD9² = A95847C2D361B   
340A621² = AB6C8D5793241   3441C78² = AD139726C5B48   34B4836² = B27146DC359A8   
35A38CB² = B8A5D174C2369   35B0232² = B917A2856D3C4   35D5841² = BA3C52D796481   
35DDC8B² = BA7D48361C529   363A5CD² = BC5A68D479321   369A308² = C152B73DA9648   
36C9531² = C2B9A43D87561   36D9588² = C349B5721AD68   373627B² = C5321A47BD689   
3736C8D² = C53729468BDA1   37574A3² = C63B78A1D5249   3759087² = C64981ABD5237   
37861A4² = C7A5D493861B2   37CB72A² = C9D84AB316752   381827A² = CB548A369D172   
38534A6² = CD37B496512A8   3859842² = CD6AB81395724   38AA3D5² = D21396874AC5B   
38BA9A1² = D28A3B549C761   3900B4C² = D3B5C9612A784   390A926² = D42795B36A1C8   
39407D3² = D5C742BA31689   394C2D9² = D64895A723C1B   397025D² = D764AB895C321   
39A1835² = D912C45A6873B   39B0934² = D981746A53CB2   39C021C² = DA135B28C7964   
3A03226² = DB3962A7541C8   
Elapsed Time: 03:03.390 min

FreeBASIC

Translation of: XPLo
#define floor(x) ((x*2.0-0.5) Shr 1)

Dim Shared As Double bbase          ' Number bbase being used [9..14]

Sub NumOut(n As Double)             ' Display n in the specified bbase
    Dim As Integer remain = Fix(n Mod bbase)
    n = Floor(n / bbase)
    If n <> 0 Then NumOut(n)
    Print Chr(remain + Iif(remain <= 9, Asc("0"), Asc("A")-10));
End Sub

Sub ShowPenSq(n As Double)          ' Display n = n^2
    NumOut(n)
    'Print Chr(253); " = ";
    Print "^2 = ";
    NumOut(n * n)
    Print "  ";
End Sub

Function isPenholodigital(n As Double) As Boolean
    Dim As Integer used, remain
    used = 1 ' can't contain 0
    While n <> 0
        remain = Fix(n Mod bbase)
        n = Floor(n / bbase)
        If (used And (1 Shl remain)) Then Return False
        used = used Or (1 Shl remain)
    Wend
    Return (used = (1 Shl Fix(bbase)) - 1)
End Function

Dim As Uinteger cnt
Dim As Double n, limite, n1, n2
bbase = 9
Do
    cnt = 0
    n1 = 0
    n = Floor(Sqr(bbase ^ (bbase-2)))
    limite = Sqr(bbase ^ (bbase-1))
    Do
        If isPenholodigital(n * n) Then
            cnt += 1
            If n1 = 0 Then n1 = n
            If bbase <= 12 Then
                ShowPenSq(n)
                If (cnt Mod 3) = 0 Then Print
            Else
                n2 = n
            End If
        End If
        n += 1
    Loop Until n >= limite
    If (cnt Mod 3) <> 0 Then Print
    Print "There are "; cnt; " penholodigital squares in base "; Fix(bbase)
    If bbase >= 13 And cnt > 0 Then
        ShowPenSq(n1)
        Print "..  ";
        ShowPenSq(n2)
    End If
    Print
    bbase += 1
Loop Until bbase >= 15

Sleep
Output:
Similar as XPLo entry.

Go

Translation of: Wren
Library: Go-rcu
package main

import (
    "fmt"
    "math"
    "rcu"
    "strconv"
)

func reverse(s string) string {
    r := make([]byte, len(s))
    for i := 0; i < len(s); i++ {
        r[i] = s[len(s)-1-i]
    }
    return string(r)
}

func main() {
    primes := []int{2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47}
    digits := "123456789ABCDEF"
    for b := 9; b <= 16; b++ {
        master := 1
        for d := 1; d < b; d++ {
            master *= primes[d-1]
        }
        var phd []int
        smin, _ := strconv.ParseInt(digits[0:b-1], b, 64)
        min := int(math.Ceil(math.Sqrt(float64(smin))))
        smax, _ := strconv.ParseInt(reverse(digits[0:b-1]), b, 64)
        max := int(math.Floor(math.Sqrt(float64(smax))))
        factors := rcu.PrimeFactors(b - 1)
        div := factors[len(factors)-1]
        for i := min; i <= max; i++ {
            if (i % div) != 0 {
                continue
            }
            sq := i * i
            digs := rcu.Digits(sq, b)
            containsZero := false
            key := 1
            for _, dig := range digs {
                if dig == 0 {
                    containsZero = true
                    break
                }
                key *= primes[dig-1]
            }
            if containsZero {
                continue
            }
            if key == master {
                phd = append(phd, i)
            }
        }
        fmt.Println("There is a total of", len(phd), "penholodigital squares in base", b, "\b:")
        if b > 13 {
            phd = []int{phd[0], phd[len(phd)-1]}
        }
        for i := 0; i < len(phd); i++ {
            sq2 := phd[i] * phd[i]
            fmt.Printf("%s² = %s  ", strconv.FormatInt(int64(phd[i]), b), strconv.FormatInt(int64(sq2), b))
            if (i+1)%3 == 0 {
                fmt.Println()
            }
        }
        if len(phd)%3 != 0 {
            fmt.Println()
        }
        fmt.Println()
    }
}
Output:
There is a total of 10 penholodigital squares in base 9:
3825² = 16328547  3847² = 16523874  4617² = 23875614  
4761² = 25487631  6561² = 47865231  6574² = 48162537  
6844² = 53184267  7285² = 58624317  7821² = 68573241  
8554² = 82314657  

There is a total of 30 penholodigital squares in base 10:
11826² = 139854276  12363² = 152843769  12543² = 157326849  
14676² = 215384976  15681² = 245893761  15963² = 254817369  
18072² = 326597184  19023² = 361874529  19377² = 375468129  
19569² = 382945761  19629² = 385297641  20316² = 412739856  
22887² = 523814769  23019² = 529874361  23178² = 537219684  
23439² = 549386721  24237² = 587432169  24276² = 589324176  
24441² = 597362481  24807² = 615387249  25059² = 627953481  
25572² = 653927184  25941² = 672935481  26409² = 697435281  
26733² = 714653289  27129² = 735982641  27273² = 743816529  
29034² = 842973156  29106² = 847159236  30384² = 923187456  

There is a total of 20 penholodigital squares in base 11:
42045² = 165742a893  43152² = 173a652894  44926² = 18792a6453  
47149² = 1a67395824  47257² = 1a76392485  52071² = 249a758631  
54457² = 2719634a85  55979² = 286a795314  59597² = 314672a895  
632a4² = 3671a89245  64069² = 376198a254  68335² = 41697528a3  
71485² = 46928a7153  81196² = 5a79286413  83608² = 632a741859  
86074² = 6713498a25  89468² = 7148563a29  91429² = 76315982a4  
93319² = 795186a234  a3a39² = 983251a764  

There is a total of 23 penholodigital squares in base 12:
117789² = 135b7482a69  16357b² = 23a5b976481  16762b² = 24ab5379861  
16906b² = 25386749ba1  173434² = 26b859a3714  178278² = 2835ba17694  
1a1993² = 34a8125b769  1a3595² = 354a279b681  1b0451² = 3824b7569a1  
1b7545² = 3a5b2487961  2084a9² = 42a1583b769  235273² = 5287ba13469  
2528b5² = 5b23a879641  25b564² = 62937b5a814  262174² = 63a8527b194  
285a44² = 73b615a8294  29a977² = 7b9284a5361  2a7617² = 83ab5479261  
2b0144² = 8617b35a294  307381² = 93825a67b41  310828² = 96528ab7314  
319488² = 9ab65823714  319a37² = 9b2573468a1  

There is a total of 0 penholodigital squares in base 13:

There is a total of 160 penholodigital squares in base 14:
1129535² = 126a84d79c53b  3a03226² = db3962a7541c8  

There is a total of 419 penholodigital squares in base 15:
4240c58² = 12378da5b6ec94  ee25e4a² = ed4c93285671ba  

There is a total of 740 penholodigital squares in base 16:
11156eb6² = 123da7f85bce964  3fd8f786² = fec81b69573da24  

J

Implementation:

digch=: a.{~;48 97(+i.)&.>10 26
brep=: (digch {~ #.inv)&.>

penholod=: {{
  F=: >.%:y#.D=:}.i.y
  C=: <.%:y#.}:i.-y
  ok=: (D */@e. y #.inv ])"0
  (#~ok) *:F+i.1+C-F
}}

task=: {{
  sq=. penholod y
  hd=. ,:(#sq),&":' penholodigital squares in base ',":y
  hd,(*#sq)#names (y brep sq),each '=',each(y brep %:sq),each<'²'
}}

stretch=: {{
  sq=. penholod y
  hd=. ,:(#sq),&":' penholodigital squares in base ',":y
  hd,(*#sq)#names ({.,'...';{:) (y brep sq),each '=',each(y brep %:sq),each<'²'
}}

Task examples:

   task 9
10 penholodigital squares in base 9                                                             
16328547=3825² 16523874=3847² 23875614=4617² 25487631=4761² 47865231=6561² 48162537=6574² 
53184267=6844² 58624317=7285² 68573241=7821² 82314657=8554²                                 
   task 10
30 penholodigital squares in base 10                                                                        
139854276=11826² 152843769=12363² 157326849=12543² 215384976=14676² 245893761=15681² 254817369=15963² 
326597184=18072² 361874529=19023² 375468129=19377² 382945761=19569² 385297641=19629² 412739856=20316² 
523814769=22887² 529874361=23019² 537219684=23178² 549386721=23439² 587432169=24237² 589324176=24276² 
597362481=24441² 615387249=24807² 627953481=25059² 653927184=25572² 672935481=25941² 697435281=26409² 
714653289=26733² 735982641=27129² 743816529=27273² 842973156=29034² 847159236=29106² 923187456=30384² 
   task 11
20 penholodigital squares in base 11                                                           
165742a893=42045² 173a652894=43152² 18792a6453=44926² 1a67395824=47149² 1a76392485=47257² 
249a758631=52071² 2719634a85=54457² 286a795314=55979² 314672a895=59597² 3671a89245=632a4² 
376198a254=64069² 41697528a3=68335² 46928a7153=71485² 5a79286413=81196² 632a741859=83608² 
6713498a25=86074² 7148563a29=89468² 76315982a4=91429² 795186a234=93319² 983251a764=a3a39² 
   task 12
23 penholodigital squares in base 12                                                                     
135b7482a69=117789² 23a5b976481=16357 24ab5379861=16762 25386749ba1=16906 26b859a3714=173434² 
2835ba17694=178278² 34a8125b769=1a1993² 354a279b681=1a3595² 3824b7569a1=1b0451² 3a5b2487961=1b7545² 
42a1583b769=2084a9² 5287ba13469=235273² 5b23a879641=2528b5² 62937b5a814=25b564² 63a8527b194=262174² 
73b615a8294=285a44² 7b9284a5361=29a977² 83ab5479261=2a7617² 8617b35a294=2b0144² 93825a67b41=307381² 
96528ab7314=310828² 9ab65823714=319488² 9b2573468a1=319a37²                                           
   stretch 13
0 penholodigital squares in base 13
   stretch 14
160 penholodigital squares in base 14           
126a84d79c53b=1129535² ...                     
db3962a7541c8=3a03226²                         
   stretch 15
419 penholodigital squares in base 15             
12378da5b6ec94=4240c58² ...                      
ed4c93285671ba=ee25e4a²                
   stretch 16
740 penholodigital squares in base 16                 
123da7f85bce964=11156eb6² ...                        
fec81b69573da24=3fd8f786²                                      
   NB. this is getting to be obnoxiously long in terms of time...

jq

Adapted from Python

Works with: jq

Also works with gojq, the Go implementation of jq, and with fq

Note that the definition of `_nwise/1` may be omitted if using jq.

General Utilities

# This def may be omitted if using jq
def _nwise($n):
  def nw: if length <= $n then . else .[0:$n] , (.[$n:] | nw) end;
  nw;

# Evaluate SIGMA $x^k * $p[k] for k=0...
def evalpoly($x; $p):
  reduce range(0;p|length) as $i ({power: 1, sum:0};
      .sum += .power * $p[$i]
      | .power *= $x)
  | .sum;      

# Convert the input integer to a string in the specified base (2 to 36 inclusive)
def convert(base):
  def stream:
    recurse(if . >= base then ./base|floor else empty end) | . % base ;
  [stream] | reverse
  | if   base <  10 then map(tostring) | join("")
    elif base <= 36 then map(if . < 10 then 48 + . else . + 87 end) | implode
    else error("base too large")
    end
  end;

# If $j is 0, then an error condition is raised;
# otherwise, assuming infinite-precision integer arithmetic,
# if the input and $j are integers, then the result will be an integer.
def idivide($j):
  . as $i
  | ($i % $j) as $mod
  | ($i - $mod) / $j ;

# input should be a non-negative integer for accuracy
# but may be any non-negative finite number
def isqrt:
  def irt:
  . as $x
    | 1 | until(. > $x; . * 4) as $q
    | {$q, $x, r: 0}
    | until( .q <= 1;
        .q |= idivide(4)
        | .t = .x - .r - .q
        | .r |= idivide(2)
        | if .t >= 0
          then .x = .t
          | .r += .q
          else .
          end)
    | .r ;
  if type == "number" and (isinfinite|not) and (isnan|not) and . >= 0
  then irt
  else "isqrt requires a non-negative integer for accuracy" | error
  end ;

# Input: an integer base 10
# Output: an array of the digits (characters) if . were printed in base $base
def digits($base):
  convert($base) | tostring | [explode[] | [.] | implode];

The Task

# emit an array of [$n,$sq] values where $n is a penholodigital square in the given base
# and $n and $sq are integers expressed in that base
def penholodigital($base):
  { hi: (evalpoly($base; [range(1;$base)])|isqrt),
    lo: (evalpoly($base; [range(base-1; 0; -1)]) | isqrt)  # evalpoly(base, base-1:-1:1)
  }
  | reduce range(.lo; .hi+1) as $n (null;
       ($n * $n) as $sq
       | ($sq | digits($base)) as $digits
       | if "0" | IN($digits[]) then .
         elif (($digits | length) == $base - 1) and (($digits | unique | length) == $base-1)
         then . + [[($n | convert(base)), ($sq | convert(base))]]
         else .
         end );

def task(a;b):
  range(a;b) as $base
  | penholodigital($base)
  | "\n\nThere are \(length) penholodigital squares in base \($base):",
    (_nwise(3)
     | map("\(.[0])² = \(.[1])" )
     | join("  "));
        
 task(9;13)
Output:
There are 10 penholodigital squares in base 9:
3825² = 16328547  3847² = 16523874  4617² = 23875614
4761² = 25487631  6561² = 47865231  6574² = 48162537
6844² = 53184267  7285² = 58624317  7821² = 68573241
8554² = 82314657


There are 30 penholodigital squares in base 10:
11826² = 139854276  12363² = 152843769  12543² = 157326849
14676² = 215384976  15681² = 245893761  15963² = 254817369
18072² = 326597184  19023² = 361874529  19377² = 375468129
19569² = 382945761  19629² = 385297641  20316² = 412739856
22887² = 523814769  23019² = 529874361  23178² = 537219684
23439² = 549386721  24237² = 587432169  24276² = 589324176
24441² = 597362481  24807² = 615387249  25059² = 627953481
25572² = 653927184  25941² = 672935481  26409² = 697435281
26733² = 714653289  27129² = 735982641  27273² = 743816529
29034² = 842973156  29106² = 847159236  30384² = 923187456


There are 20 penholodigital squares in base 11:
42045² = 165742a893  43152² = 173a652894  44926² = 18792a6453
47149² = 1a67395824  47257² = 1a76392485  52071² = 249a758631
54457² = 2719634a85  55979² = 286a795314  59597² = 314672a895
632a4² = 3671a89245  64069² = 376198a254  68335² = 41697528a3
71485² = 46928a7153  81196² = 5a79286413  83608² = 632a741859
86074² = 6713498a25  89468² = 7148563a29  91429² = 76315982a4
93319² = 795186a234  a3a39² = 983251a764


There are 23 penholodigital squares in base 12:
117789² = 135b7482a69  16357b² = 23a5b976481  16762b² = 24ab5379861
16906b² = 25386749ba1  173434² = 26b859a3714  178278² = 2835ba17694
1a1993² = 34a8125b769  1a3595² = 354a279b681  1b0451² = 3824b7569a1
1b7545² = 3a5b2487961  2084a9² = 42a1583b769  235273² = 5287ba13469
2528b5² = 5b23a879641  25b564² = 62937b5a814  262174² = 63a8527b194
285a44² = 73b615a8294  29a977² = 7b9284a5361  2a7617² = 83ab5479261
2b0144² = 8617b35a294  307381² = 93825a67b41  310828² = 96528ab7314
319488² = 9ab65823714  319a37² = 9b2573468a1

Julia

""" rosettacode.org task Penholodigital_squares """


function penholodigital(base)
    penholodigitals = Int[]
    hi, lo = isqrt(evalpoly(base, 1:base-1)), isqrt(evalpoly(base, base-1:-1:1))
    for n in lo:hi
        dig = digits(n * n; base)
        0 in dig && continue
        if all(i -> count(==(i), dig) == 1, 1:base-1)
            push!(penholodigitals, n * n)
        end
    end
    return penholodigitals
end

for j in 9:16
    allpen = penholodigital(j)
    println("\n\nThere is a total of $(length(allpen)) penholodigital squares in base $j:")
    for (i, n) in (j < 14 ? enumerate(allpen) : enumerate([allpen[begin], allpen[end]]))
        print(string(isqrt(n), base=j), "² = ", string(n, base=j), i %3 == 0 ? "\n" :  "  ")
    end
end
Output:
There is a total of 10 penholodigital squares in base 9:
3825² = 16328547  3847² = 16523874  4617² = 23875614
4761² = 25487631  6561² = 47865231  6574² = 48162537
6844² = 53184267  7285² = 58624317  7821² = 68573241
8554² = 82314657

There is a total of 30 penholodigital squares in base 10:
11826² = 139854276  12363² = 152843769  12543² = 157326849
14676² = 215384976  15681² = 245893761  15963² = 254817369
18072² = 326597184  19023² = 361874529  19377² = 375468129
19569² = 382945761  19629² = 385297641  20316² = 412739856
22887² = 523814769  23019² = 529874361  23178² = 537219684
23439² = 549386721  24237² = 587432169  24276² = 589324176
24441² = 597362481  24807² = 615387249  25059² = 627953481
25572² = 653927184  25941² = 672935481  26409² = 697435281
26733² = 714653289  27129² = 735982641  27273² = 743816529
29034² = 842973156  29106² = 847159236  30384² = 923187456


There is a total of 20 penholodigital squares in base 11:
42045² = 165742a893  43152² = 173a652894  44926² = 18792a6453
47149² = 1a67395824  47257² = 1a76392485  52071² = 249a758631
54457² = 2719634a85  55979² = 286a795314  59597² = 314672a895
632a4² = 3671a89245  64069² = 376198a254  68335² = 41697528a3
71485² = 46928a7153  81196² = 5a79286413  83608² = 632a741859
86074² = 6713498a25  89468² = 7148563a29  91429² = 76315982a4
93319² = 795186a234  a3a39² = 983251a764

There is a total of 23 penholodigital squares in base 12:
117789² = 135b7482a69  16357b² = 23a5b976481  16762b² = 24ab5379861
16906b² = 25386749ba1  173434² = 26b859a3714  178278² = 2835ba17694
1a1993² = 34a8125b769  1a3595² = 354a279b681  1b0451² = 3824b7569a1
1b7545² = 3a5b2487961  2084a9² = 42a1583b769  235273² = 5287ba13469
2528b5² = 5b23a879641  25b564² = 62937b5a814  262174² = 63a8527b194
285a44² = 73b615a8294  29a977² = 7b9284a5361  2a7617² = 83ab5479261
2b0144² = 8617b35a294  307381² = 93825a67b41  310828² = 96528ab7314
319488² = 9ab65823714  319a37² = 9b2573468a1

There is a total of 0 penholodigital squares in base 13:


There is a total of 160 penholodigital squares in base 14:
1129535² = 126a84d79c53b  3a03226² = db3962a7541c8

There is a total of 419 penholodigital squares in base 15:
4240c58² = 12378da5b6ec94  ee25e4a² = ed4c93285671ba

There is a total of 740 penholodigital squares in base 16:
11156eb6² = 123da7f85bce964  3fd8f786² = fec81b69573da24

Extended version

Theoretically, the program should be able to handle bases up to 30, but in practice that would take it far too long.

function penholodigital(base)
    penholodigitals = [typeof(base)[] for _ in 1:Threads.nthreads()]
    digitbuf = [zeros(typeof(base), base-1) for _ in 1:Threads.nthreads()]
    hi, lo = isqrt(evalpoly(base, 1:base-1)), isqrt(evalpoly(base, base-1:-1:1))
    @Threads.threads for n in lo:hi
        dig = digitbuf[Threads.threadid()]
        digits!(dig, n * n; base)
        0 in dig && continue
        if all(i -> count(==(i), dig) == 1, 1:base-1)
            push!(penholodigitals[Threads.threadid()], n * n)
        end
    end
    return sort!(vcat(penholodigitals...))
end

for j in 9:19
    @time begin
        allpen = penholodigital(j < 17 ? j : Int128(j))
        println("There are a total of $(length(allpen)) penholodigital squares in base $j:")
        if length(allpen) > 0
            for (i, n) in (j < 14 ? enumerate(allpen) : enumerate([allpen[begin], allpen[end]]))
                print(string(isqrt(n), base=j), "² = ", string(n, base=j), i %3 == 0 ? "\n" :  "  ")
            end
        end
    end
    println("\n")
end
Output:
There are a total of 10 penholodigital squares in base 9:
3825² = 16328547  3847² = 16523874  4617² = 23875614
4761² = 25487631  6561² = 47865231  6574² = 48162537
6844² = 53184267  7285² = 58624317  7821² = 68573241
8554² = 82314657    0.180057 seconds (213.67 k allocations: 10.895 MiB, 98.16% compilation time)


There are a total of 30 penholodigital squares in base 10:
11826² = 139854276  12363² = 152843769  12543² = 157326849
14676² = 215384976  15681² = 245893761  15963² = 254817369
18072² = 326597184  19023² = 361874529  19377² = 375468129
19569² = 382945761  19629² = 385297641  20316² = 412739856
22887² = 523814769  23019² = 529874361  23178² = 537219684
23439² = 549386721  24237² = 587432169  24276² = 589324176
24441² = 597362481  24807² = 615387249  25059² = 627953481
25572² = 653927184  25941² = 672935481  26409² = 697435281
26733² = 714653289  27129² = 735982641  27273² = 743816529
29034² = 842973156  29106² = 847159236  30384² = 923187456
  0.008105 seconds (770 allocations: 28.117 KiB)


There are a total of 20 penholodigital squares in base 11:
42045² = 165742a893  43152² = 173a652894  44926² = 18792a6453
47149² = 1a67395824  47257² = 1a76392485  52071² = 249a758631
54457² = 2719634a85  55979² = 286a795314  59597² = 314672a895
632a4² = 3671a89245  64069² = 376198a254  68335² = 41697528a3
71485² = 46928a7153  81196² = 5a79286413  83608² = 632a741859
86074² = 6713498a25  89468² = 7148563a29  91429² = 76315982a4
93319² = 795186a234  a3a39² = 983251a764    0.008614 seconds (538 allocations: 21.164 KiB)


There are a total of 23 penholodigital squares in base 12:
117789² = 135b7482a69  16357b² = 23a5b976481  16762b² = 24ab5379861
16906b² = 25386749ba1  173434² = 26b859a3714  178278² = 2835ba17694
1a1993² = 34a8125b769  1a3595² = 354a279b681  1b0451² = 3824b7569a1
1b7545² = 3a5b2487961  2084a9² = 42a1583b769  235273² = 5287ba13469
2528b5² = 5b23a879641  25b564² = 62937b5a814  262174² = 63a8527b194
285a44² = 73b615a8294  29a977² = 7b9284a5361  2a7617² = 83ab5479261
2b0144² = 8617b35a294  307381² = 93825a67b41  310828² = 96528ab7314
319488² = 9ab65823714  319a37² = 9b2573468a1    0.021154 seconds (606 allocations: 23.141 KiB)


There are a total of 0 penholodigital squares in base 13:
  0.093971 seconds (67 allocations: 6.148 KiB)


There are a total of 160 penholodigital squares in base 14:
1129535² = 126a84d79c53b  3a03226² = db3962a7541c8    0.678507 seconds (148 allocations: 12.523 KiB, 0.55% compilation time)


There are a total of 419 penholodigital squares in base 15:
4240c58² = 12378da5b6ec94  ee25e4a² = ed4c93285671ba    4.194926 seconds (137 allocations: 22.289 KiB)   


There are a total of 740 penholodigital squares in base 16:
11156eb6² = 123da7f85bce964  3fd8f786² = fec81b69573da24   10.843582 seconds (137 allocations: 24.789 KiB)


There are a total of 0 penholodigital squares in base 17:
345.116100 seconds (335.36 k allocations: 17.403 MiB, 0.08% compilation time)


There are a total of 5116 penholodigital squares in base 18:
11150fc0g² = 123cd8abh5g79f6e4  44422dd18² = hgef25738d496bc1a  2584.183274 seconds (332.23 k allocations: 17.565 MiB, 0.00% gc time, 0.01% compilation time)


There are a total of 47677 penholodigital squares in base 19:
4b802235a² = 1234978cibd6gfhea5  ii844bia2² = ihg8f71c6da59be324  20734.916185 seconds (169 allocations: 
2.163 MiB)

Nim

Translation of: Wren

Translation with several modifications to make things easier in Nim.

import std/[algorithm, math, strformat, sugar]

const
  Primes = [1: 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]
  Digits = "0123456789ABCDEF"

func digits(n, base: Positive): seq[int] =
  ## Return the digits of "n" in given base (least significant
  ## digits first).
  var n = n.Natural
  while n != 0:
    result.add n mod base
    n = n div base

func parseInt(d: openArray[char]; base: Positive): int =
  ## Convert a sequence of characters in given base to an integer.
  for c in d:
    result = result * base + (if c <= '9': ord(c) - ord('0') else: ord(c) - ord('A') + 10)

func toBase(n: Natural; base: Positive): string =
  ## Return the string representation of "n" in given base.
  let d = n.digits(base)
  for i in countdown(d.high, 0):
    result.add Digits[d[i]]

func lastPrimeFactor(n: Positive): int =
  ## Return the last prime factor of "n".
  var n = n
  if (n and 1) == 0:
    result = 2
    while true:
      n = n shr 1
      if (n and 1) != 0: break
  var d = 3
  while d * d <= n:
    if n mod d == 0:
      result = d
      while true:
        n = n div d
        if n mod d != 0: break
    inc d, 2
  if n > result: result = n

for b in 9..16:
  let master = Primes[1..<b]
  var phd: seq[int]
  let digits = Digits[1..<b]
  let min = sqrt(digits.parseInt(b).toFloat).ceil.toInt
  let max = sqrt(digits.reversed.parseInt(b).toFloat).ceil.toInt
  let divider = lastPrimeFactor(b - 1)

  # Build the list of square roots of pendholodigital squares.
  for i in min..max:
    if i mod divider != 0: continue
    let sq = i * i
    let digs = sq.digits(b)
    if 0 in digs: continue
    var key = collect(for dig in digs: Primes[dig])
    if sorted(key) == master: phd.add i

  echo &"There is a total of {phd.len} penholodigital squares in base {b}:"
  if b > 13: phd = @[phd[0], phd[^1]]
  for i in 0..phd.high:
    stdout.write &"  {phd[i].toBase(b)}² = {(phd[i]^2).toBase(b)}"
    if i mod 3 == 2: echo()
  if phd.len mod 3 != 0: echo()
  echo()
Output:
There is a total of 10 penholodigital squares in base 9:
  3825² = 16328547  3847² = 16523874  4617² = 23875614
  4761² = 25487631  6561² = 47865231  6574² = 48162537
  6844² = 53184267  7285² = 58624317  7821² = 68573241
  8554² = 82314657

There is a total of 30 penholodigital squares in base 10:
  11826² = 139854276  12363² = 152843769  12543² = 157326849
  14676² = 215384976  15681² = 245893761  15963² = 254817369
  18072² = 326597184  19023² = 361874529  19377² = 375468129
  19569² = 382945761  19629² = 385297641  20316² = 412739856
  22887² = 523814769  23019² = 529874361  23178² = 537219684
  23439² = 549386721  24237² = 587432169  24276² = 589324176
  24441² = 597362481  24807² = 615387249  25059² = 627953481
  25572² = 653927184  25941² = 672935481  26409² = 697435281
  26733² = 714653289  27129² = 735982641  27273² = 743816529
  29034² = 842973156  29106² = 847159236  30384² = 923187456

There is a total of 20 penholodigital squares in base 11:
  42045² = 165742A893  43152² = 173A652894  44926² = 18792A6453
  47149² = 1A67395824  47257² = 1A76392485  52071² = 249A758631
  54457² = 2719634A85  55979² = 286A795314  59597² = 314672A895
  632A4² = 3671A89245  64069² = 376198A254  68335² = 41697528A3
  71485² = 46928A7153  81196² = 5A79286413  83608² = 632A741859
  86074² = 6713498A25  89468² = 7148563A29  91429² = 76315982A4
  93319² = 795186A234  A3A39² = 983251A764

There is a total of 23 penholodigital squares in base 12:
  117789² = 135B7482A69  16357B² = 23A5B976481  16762B² = 24AB5379861
  16906B² = 25386749BA1  173434² = 26B859A3714  178278² = 2835BA17694
  1A1993² = 34A8125B769  1A3595² = 354A279B681  1B0451² = 3824B7569A1
  1B7545² = 3A5B2487961  2084A9² = 42A1583B769  235273² = 5287BA13469
  2528B5² = 5B23A879641  25B564² = 62937B5A814  262174² = 63A8527B194
  285A44² = 73B615A8294  29A977² = 7B9284A5361  2A7617² = 83AB5479261
  2B0144² = 8617B35A294  307381² = 93825A67B41  310828² = 96528AB7314
  319488² = 9AB65823714  319A37² = 9B2573468A1

There is a total of 0 penholodigital squares in base 13:

There is a total of 160 penholodigital squares in base 14:
  1129535² = 126A84D79C53B  3A03226² = DB3962A7541C8

There is a total of 419 penholodigital squares in base 15:
  4240C58² = 12378DA5B6EC94  EE25E4A² = ED4C93285671BA

There is a total of 740 penholodigital squares in base 16:
  11156EB6² = 123DA7F85BCE964  3FD8F786² = FEC81B69573DA24

Pascal

Free Pascal

Nearly copy and paste of pandigital square numbers.
Now using the right step size and startvalue
I think base 16 is the limit. 1234...FG is to big for Uint64. GMP/ MPinteger is required.

program penholodigital;
//Find the smallest number n to base b, so that n*n includes all
//digits of base b without 0
{$IFDEF FPC}{$MODE DELPHI}{$Optimization ON,All}{$ENDIF}
{$IFDEF Windows}{$APPTYPE CONSOLE}{$ENDIF}
{$DEFINE TIORUN}
uses
  sysutils;
const
 charSet : array[0..36] of char ='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
type
  tNumtoBase = record
                 ntb_dgt : array[0..31-8] of byte;
                 ntb_cnt,
                 ntb_bas  : Int32;
               end;
var
  dgtSqrRoot : array[0..31-8] of byte;
  sl : array of string;
  s2Delta : array of Uint32;
  Num,
  sqr2B,
  deltaSqrNum  : tNumtoBase;

procedure Conv2num(var num:tNumtoBase;n:Uint64;base:NativeUint);
var
  quot :UInt64;
  i :NativeUint;
Begin
  i := 0;
  repeat
    quot := n div base;
    Num.ntb_dgt[i] := n-quot*base;
    n := quot;
    inc(i);
  until n = 0;
  Num.ntb_cnt := i;
  Num.ntb_bas := base;
  //clear upper digits
  For i := i to high(tNumtoBase.ntb_dgt) do
    Num.ntb_dgt[i] := 0;
end;

function OutNum(const num:tNumtoBase):AnsiString;
var
  i,j : NativeInt;
Begin
  with num do
  Begin
    setlength(result,ntb_cnt);
    j := 1;
    For i := ntb_cnt-1 downto 0 do
    Begin
      result[j] := charSet[ntb_dgt[i]];
      inc(j);
    end;
  end;
end;

procedure InsertSolution(i,penHoloCnt : NativeInt);
begin
  sl[penHoloCnt] := Format('%s^2 = %s',[OutNum(Num),OutNum(sqr2B)]);
  s2delta[penHoloCnt] := i; 
  {$IFNDEF TIORUN} 
  //a little action in the terminal
  write(penHoloCnt:8,i :10,' ',sl[penHoloCnt],#13);
  IF penHoloCnt= 0 then
     writeln;
  {$ENDIF}     
end;     

procedure IncNumBig(var add1:tNumtoBase;n:NativeUInt);
//prerequisites
//bases are the same,delta : NativeUint
var
  i,s,b,carry : NativeInt;
Begin
  b := add1.ntb_bas;
  i := 0;
  carry := 0;
  while n > 0 do
  Begin
    s := add1.ntb_dgt[i]+carry+ n MOD b;
    carry := Ord(s>=b);
    s := s- (-carry AND b);
    add1.ntb_dgt[i] := s;
    n := n div b;
    inc(i);
  end;

  while carry <> 0 do
  Begin
    s := add1.ntb_dgt[i]+carry;
    carry := Ord(s>=b);
    s := s- (-carry AND b);
    add1.ntb_dgt[i] := s;
    inc(i);
  end;

  IF add1.ntb_cnt < i then
    add1.ntb_cnt := i;
end;

procedure IncNum(var add1:tNumtoBase;carry:NativeInt);
//prerequisites: bases are the same, carry==delta < base
var
  i,s,b : NativeInt;
Begin
  b := add1.ntb_bas;
  i := 0;
  while carry <> 0 do
  Begin
    s := add1.ntb_dgt[i]+carry;
    carry := Ord(s>=b);
    s := s- (-carry AND b);
    add1.ntb_dgt[i] := s;
    inc(i);
  end;
  IF add1.ntb_cnt < i then
    add1.ntb_cnt := i;
end;

procedure AddNum(var add1,add2:tNumtoBase);
//prerequisites
//bases are the same,add1>add2, add1 <= add1+add2;
var
  i,carry,s,b : NativeInt;
Begin
  b := add1.ntb_bas;
  carry := 0;
  For i := 0 to add2.ntb_cnt-1 do
  begin
    s := add1.ntb_dgt[i]+add2.ntb_dgt[i]+carry;
    carry := Ord(s>=b);
    s := s- (-carry AND b);
    add1.ntb_dgt[i] := s;
  end;

  i := add2.ntb_cnt;
  while carry = 1 do
  Begin
    s := add1.ntb_dgt[i]+carry;
    carry := Ord(s>=b);
    // remove of if s>b then by bit-twiddling
    s := s- (-carry AND b);
    add1.ntb_dgt[i] := s;
    inc(i);
  end;

  IF add1.ntb_cnt < i then
    add1.ntb_cnt := i;
end;

procedure Test(base:NativeUInt);
var
  n,penHoloCnt : Uint64;
  b1,i,j,TestSet,CheckSet,dgtRoot,step,dNum : NativeInt;
Begin
  penHoloCnt := 0;
  b1 := Base-1;
  dgtRoot := ((Base*b1) DIV 2) MOD b1;  
  For j := 1 to b1 do
    dgtSqrRoot[j] := (j*j) MOD b1;
    
  // square number containing 1,2..,base-1  
  // now estimate root for  1234..Base-1
  // if base is even then root = 111...1 in Base
  // if base is  odd then root = sqrt(base)*(111...1) in Base  
  n := 0;  
    For j := Base DIV 2 downto 1 do
      n := n*base+1;
  if ODD(BASE) then 
    n := trunc(n*Sqrt(base));
  // increment n til it fits (n*n) MOD b1 = dgtroot
  j := B1;
  repeat
    if sqr(n MOD b1) MOD b1 = dgtroot then
      BREAK;
    inc(n);  
    dec(j);
  until j = 0;
  //calc nxn
  Conv2num(Num,n,base);
  deltaSqrNum := Num;  
  Conv2num(sqr2B,0,base);  
  j := 1;
  repeat
    if j AND n <> 0 then
      AddNum(sqr2B,deltaSqrNum);
    AddNum(deltaSqrNum,deltaSqrNum);
    j +=j;
  until j > n;
  
// calc step size i
  i := 0;
  For j := 1 to Base-1 do
    if dgtSqrRoot[j] = dgtRoot then
      inc(i);
// if i stays 0 than an extra digit will be needed -> no penholodigital       
  if i <> 0 then
  Begin 
    step := b1 DIV i;  
    Writeln('Step size ',step,' decimal : ',n,' in base: ',OutNum(Num),'  ',OutNum(sqr2B));
    // calc delta of square numbers for incremnt n by step
    Conv2num(deltaSqrNum,2*step*n,base);  
    IncNumBig(deltaSqrNum,step*step);
    dNum := 2*step*step;

    //all digits without 0
    CheckSet := 0;
    For j := b1 downto 1 do
      CheckSet := CheckSet OR (1 shl j);
    i := 0;    
    repeat
      //count used digits
      TestSet := 0;
      For j := sqr2B.ntb_cnt-1 downto 0 do
        TestSet := TestSet OR (1 shl sqr2B.ntb_dgt[j]);
      IF CheckSet=TestSet  then
      Begin
        //now correct number
        IncNumBig(num,i*step);
        InsertSolution(i,penHoloCnt);
        inc(penHoloCnt);
        i := 0;
      end;
      //next square number
      AddNum(sqr2B,deltaSqrNum);
      IncNumBig(deltaSqrNum,dNum);// dnum mostly base 
      inc(i);
    until sqr2B.ntb_cnt >= base;
  end;
  if i = 0 then
  Begin
    Writeln('Penholodigital squares in base: ',base:2,' are not possible');
    writeln;
    EXIT;
  end  
  else  
    Writeln('There are a total of ',penHoloCnt,' penholodigital squares in base: ',base:2);
  if (penHoloCnt > 0) AND (base < 11) then
  begin
    j := 0;
    while penHoloCnt-j > 3 do
    begin
      writeln(sl[j],',',sl[j+1],',',sl[j+2]);
      inc(j,3);
    end;
    write(sl[j]);
    For j := j+1 to penHoloCnt-1 do
      write(',',sl[j]);
    writeln;
  end
  else
    if penHoloCnt > 1 then
    begin
      writeln(sl[0],',',sl[penHoloCnt-1]);
    end;
  writeln;    
end;

var
  T0: TDateTime;
  base :nativeInt;
begin
  T0 := now;
  setlength(sl,51160);
  setlength(s2Delta,51160);
  For base := 19 to 19 do
    Test(base);
  writeln('Total runtime in s ',(now-T0)*86400:10:3);
  {$IFDEF WINDOWS}readln;{$ENDIF}
end.
@TIO.RUN:
Step size 1 decimal : 1 in base: 1  1
There are a total of 1 penholodigital squares in base:  2
1^2 = 1

Step size 2 decimal : 1 in base: 1  1
There are a total of 0 penholodigital squares in base:  3

Step size 3 decimal : 6 in base: 12  210
There are a total of 0 penholodigital squares in base:  4

Penholodigital squares in base:  5 are not possible

Step size 5 decimal : 45 in base: 113  13213
There are a total of 2 penholodigital squares in base:  6
122^2 = 15324,221^2 = 53241

Step size 6 decimal : 153 in base: 306  125151
There are a total of 1 penholodigital squares in base:  7
645^2 = 623514

Step size 7 decimal : 588 in base: 1114  1243220
There are a total of 1 penholodigital squares in base:  8
2453^2 = 6532471

Step size 4 decimal : 2462 in base: 3335  12357657
There are a total of 10 penholodigital squares in base:  9
3825^2 = 16328547,3847^2 = 16523874,4617^2 = 23875614
4761^2 = 25487631,6561^2 = 47865231,6574^2 = 48162537
6844^2 = 53184267,7285^2 = 58624317,7821^2 = 68573241
8554^2 = 82314657

Step size 3 decimal : 11112 in base: 11112  123476544
There are a total of 30 penholodigital squares in base: 10
11826^2 = 139854276,12363^2 = 152843769,12543^2 = 157326849
14676^2 = 215384976,15681^2 = 245893761,15963^2 = 254817369
18072^2 = 326597184,19023^2 = 361874529,19377^2 = 375468129
19569^2 = 382945761,19629^2 = 385297641,20316^2 = 412739856
22887^2 = 523814769,23019^2 = 529874361,23178^2 = 537219684
23439^2 = 549386721,24237^2 = 587432169,24276^2 = 589324176
24441^2 = 597362481,24807^2 = 615387249,25059^2 = 627953481
25572^2 = 653927184,25941^2 = 672935481,26409^2 = 697435281
26733^2 = 714653289,27129^2 = 735982641,27273^2 = 743816529
29034^2 = 842973156,29106^2 = 847159236,30384^2 = 923187456

Step size 10 decimal : 53415 in base: 3714A  1234599011
There are a total of 20 penholodigital squares in base: 11
42045^2 = 165742A893,A3A39^2 = 983251A764

Step size 11 decimal : 271458 in base: 111116  12346543230
There are a total of 23 penholodigital squares in base: 12
117789^2 = 135B7482A69,319A37^2 = 9B2573468A1

Penholodigital squares in base: 13 are not possible

Step size 13 decimal : 8108737 in base: 1111117  1234576543237
There are a total of 160 penholodigital squares in base: 14
1129535^2 = 126A84D79C53B,3A03226^2 = DB3962A7541C8

Step size 14 decimal : 47266835 in base: 4239EC5  123456E806A71A
There are a total of 419 penholodigital squares in base: 15
4240C58^2 = 12378DA5B6EC94,EE25E4A^2 = ED4C93285671BA

Step size 15 decimal : 286331160 in base: 11111118  123456876543240
There are a total of 740 penholodigital squares in base: 16
11156EB6^2 = 123DA7F85BCE964,3FD8F786^2 = FEC81B69573DA24

Total runtime in s      7.159 @home 1.573 s 
// Running Base 18 and 19 @home
Step size 17 decimal : 11668193559 in base: 111111119  12345679876543249
There are a total of 5116 penholodigital squares in base: 18
11150FC0G^2 = 123CD8ABH5G79F6E4,44422DD18^2 = HGEF25738D496BC1A

Step size 6 decimal : 78142392501 in base: 4B7ICE111  12345678BB48EGB321
There are a total of 47677 penholodigital squares in base: 19
4B802235A^2 = 1234978CIBD6GFHEA5,II844BIA2^2 = IHG8F71C6DA59BE324
Total runtime in s   1151.714

real    19m11.715s
user    19m10.257s
sys     0m0.042s

Perl

Library: ntheory
use v5.36;
use List::Util 'max';
use ntheory <fromdigits todigitstring>;

sub table ($c, @V) { my $t = $c * (my $w = 2 + max map { length } @V); ( sprintf( ('%'.$w.'s')x@V, @V) ) =~ s/.{1,$t}\K/\n/gr }

for my $base (9 .. 12) {
    my $testr = reverse my $test = substr('123456789abcdef',0,$base-1);
    my $start = int sqrt fromdigits($test,  $base);
    my $end   = int sqrt fromdigits($testr, $base);
    my @nums = grep { $test eq join '', sort split '', todigitstring($_**2, $base) } $start .. $end;
    printf "There are a total of %d penholodigital squares in base $base:\n", scalar @nums;
    say table 4, map { todigitstring($_,$base) . '² = ' . todigitstring($_**2,$base) } @nums;
}
Output:
There are a total of 10 penholodigital squares in base 9:
  3825² = 16328547  3847² = 16523874  4617² = 23875614  4761² = 25487631
  6561² = 47865231  6574² = 48162537  6844² = 53184267  7285² = 58624317
  7821² = 68573241  8554² = 82314657

There are a total of 30 penholodigital squares in base 10:
  11826² = 139854276  12363² = 152843769  12543² = 157326849  14676² = 215384976
  15681² = 245893761  15963² = 254817369  18072² = 326597184  19023² = 361874529
  19377² = 375468129  19569² = 382945761  19629² = 385297641  20316² = 412739856
  22887² = 523814769  23019² = 529874361  23178² = 537219684  23439² = 549386721
  24237² = 587432169  24276² = 589324176  24441² = 597362481  24807² = 615387249
  25059² = 627953481  25572² = 653927184  25941² = 672935481  26409² = 697435281
  26733² = 714653289  27129² = 735982641  27273² = 743816529  29034² = 842973156
  29106² = 847159236  30384² = 923187456

There are a total of 20 penholodigital squares in base 11:
  42045² = 165742a893  43152² = 173a652894  44926² = 18792a6453  47149² = 1a67395824
  47257² = 1a76392485  52071² = 249a758631  54457² = 2719634a85  55979² = 286a795314
  59597² = 314672a895  632a4² = 3671a89245  64069² = 376198a254  68335² = 41697528a3
  71485² = 46928a7153  81196² = 5a79286413  83608² = 632a741859  86074² = 6713498a25
  89468² = 7148563a29  91429² = 76315982a4  93319² = 795186a234  a3a39² = 983251a764

There are a total of 23 penholodigital squares in base 12:
  117789² = 135b7482a69  16357b² = 23a5b976481  16762b² = 24ab5379861  16906b² = 25386749ba1
  173434² = 26b859a3714  178278² = 2835ba17694  1a1993² = 34a8125b769  1a3595² = 354a279b681
  1b0451² = 3824b7569a1  1b7545² = 3a5b2487961  2084a9² = 42a1583b769  235273² = 5287ba13469
  2528b5² = 5b23a879641  25b564² = 62937b5a814  262174² = 63a8527b194  285a44² = 73b615a8294
  29a977² = 7b9284a5361  2a7617² = 83ab5479261  2b0144² = 8617b35a294  307381² = 93825a67b41
  310828² = 96528ab7314  319488² = 9ab65823714  319a37² = 9b2573468a1

Phix

using string maths (and not particularly fast)

with javascript_semantics
procedure penholodigital(integer base)
    sequence penholodigitals = {}
    -- search squares of n from eg for base=4 sqrt(123)
    -- (and stop when next square has too many digits)
    atom n = 0, t1 = time()+1, carry = 0
    for d=1 to base-1 do n = n*base+d end for
    n = ceil(sqrt(n))       assert(integer(n))
    string digits = "123456789ABCDEF"[1..base-1],
           square = sprintf("%A",{{base,n*n}})
    integer div = prime_factors(base-1,true)[$]
    bool ndz = mod(n,div)=0
    while true do -- (until we get a carry off the end of square)
        if ndz and not find('0',square) and sort(square)=digits then
            penholodigitals &= {{{base,n},square}} -- (fmt later)
        end if
        -- add 2n+1 to get the next square (using string maths)
        carry += 2*n+1
        n += 1
        ndz = mod(n,div)=0
        if ndz then
            integer d = base-1
            while carry and d do
                atom ch = square[d]
                ch += carry - iff(ch<='9'?'0':'A'-10)
                carry = floor(ch/base)
                ch = remainder(ch,base)
                square[d] = ch + iff(ch<=9?'0':'A'-10)
                d -= 1
            end while
            if carry then exit end if
            if time()>t1 then
                integer l = length(penholodigitals),
                        e = {1,0,0,0,2,1,1,10,30,20,23,0,160,419,740}[base-1]
                progress("scanning base %d, %s (%d/%d found)\r",{base,square,l,e})
                t1 = time()+1
            end if
        end if
    end while
    progress("")
    integer count = length(penholodigitals)
    if base>12 and count>2 then
        penholodigitals = extract(penholodigitals,{1,-1})
    end if
    penholodigitals = apply(true,sprintf,{{"%A**2 = %s"},penholodigitals})
    string p = iff(count?":\n"&iff(base>12?sprintf("%s .. %s\n",penholodigitals)
                                          :join_by(penholodigitals,1,3)):"\n"),
           {is,s} = iff(count=1?{"is",""}:{"are","s"})
    printf(1,"There %s %d penholodigital%s in base %d%s\n",{is,count,s,base,p})
end procedure
integer lim = iff(platform()=JS?14:         -- ~18s
              iff(machine_bits()=32?15      -- (initial square(16) is 4 out)
                                   :16))    -- ~7.5 mins (ho hum)
papply(tagset(lim,2),penholodigital)
Output:
There is 1 penholodigital in base 2:
1**2 = 1

There are 0 penholodigitals in base 3

There are 0 penholodigitals in base 4

There are 0 penholodigitals in base 5

There are 2 penholodigitals in base 6:
122**2 = 15324   221**2 = 53241

There is 1 penholodigital in base 7:
645**2 = 623514

There is 1 penholodigital in base 8:
2453**2 = 6532471

There are 10 penholodigitals in base 9:
3825**2 = 16328547   3847**2 = 16523874   4617**2 = 23875614
4761**2 = 25487631   6561**2 = 47865231   6574**2 = 48162537
6844**2 = 53184267   7285**2 = 58624317   7821**2 = 68573241
8554**2 = 82314657

There are 30 penholodigitals in base 10:
11826**2 = 139854276   12363**2 = 152843769   12543**2 = 157326849
14676**2 = 215384976   15681**2 = 245893761   15963**2 = 254817369
18072**2 = 326597184   19023**2 = 361874529   19377**2 = 375468129
19569**2 = 382945761   19629**2 = 385297641   20316**2 = 412739856
22887**2 = 523814769   23019**2 = 529874361   23178**2 = 537219684
23439**2 = 549386721   24237**2 = 587432169   24276**2 = 589324176
24441**2 = 597362481   24807**2 = 615387249   25059**2 = 627953481
25572**2 = 653927184   25941**2 = 672935481   26409**2 = 697435281
26733**2 = 714653289   27129**2 = 735982641   27273**2 = 743816529
29034**2 = 842973156   29106**2 = 847159236   30384**2 = 923187456

There are 20 penholodigitals in base 11:
42045**2 = 165742A893   43152**2 = 173A652894   44926**2 = 18792A6453
47149**2 = 1A67395824   47257**2 = 1A76392485   52071**2 = 249A758631
54457**2 = 2719634A85   55979**2 = 286A795314   59597**2 = 314672A895
632A4**2 = 3671A89245   64069**2 = 376198A254   68335**2 = 41697528A3
71485**2 = 46928A7153   81196**2 = 5A79286413   83608**2 = 632A741859
86074**2 = 6713498A25   89468**2 = 7148563A29   91429**2 = 76315982A4
93319**2 = 795186A234   A3A39**2 = 983251A764

There are 23 penholodigitals in base 12:
117789**2 = 135B7482A69   16357B**2 = 23A5B976481   16762B**2 = 24AB5379861
16906B**2 = 25386749BA1   173434**2 = 26B859A3714   178278**2 = 2835BA17694
1A1993**2 = 34A8125B769   1A3595**2 = 354A279B681   1B0451**2 = 3824B7569A1
1B7545**2 = 3A5B2487961   2084A9**2 = 42A1583B769   235273**2 = 5287BA13469
2528B5**2 = 5B23A879641   25B564**2 = 62937B5A814   262174**2 = 63A8527B194
285A44**2 = 73B615A8294   29A977**2 = 7B9284A5361   2A7617**2 = 83AB5479261
2B0144**2 = 8617B35A294   307381**2 = 93825A67B41   310828**2 = 96528AB7314
319488**2 = 9AB65823714   319A37**2 = 9B2573468A1

There are 0 penholodigitals in base 13

There are 160 penholodigitals in base 14:
1129535**2 = 126A84D79C53B .. 3A03226**2 = DB3962A7541C8

There are 419 penholodigitals in base 15:
4240C58**2 = 12378DA5B6EC94 .. EE25E4A**2 = ED4C93285671BA

There are 740 penholodigitals in base 16:
11156EB6**2 = 123DA7F85BCE964 .. 3FD8F786**2 = FEC81B69573DA24

slightly faster

Using string digit-wise maths (and still not particularly fast). Same output as above, but 32 bit can also do base 16.

with javascript_semantics
--
-- We can check for the presence of any zeroes or duplicate digits as we go.
-- (Please gloss over the gaff of increasing #digits, which makes the next a rather daft example.)
-- We can also leave partial results unfinished, for (a bad) example when adding 199 = 2*99+1 to go 
-- from 99*99 = {9,8,0,1} to 100*100 = {1,0,0,0,0} we can simply stop when we see a 0/dup in the 
-- finished part of the result, on {9,8,20,0}, and it won't hurt us at all when adding 201 = 2*100+1 
-- to get the next square. That does mean we need to be a little smarter displaying progress, though.
-- We can also utilise a partial to avoid overflow and get 16 digits to work properly on 32-bit.
-- We can also skip +199 if that ain't gona work and +400 on the next iteration instead, iyswim.
-- We can also use a trivial integer-sized bitmask to check for duplicate digits, which won't hit any 
-- problems (on Phix) up to at least 29 digits, or 61 digits on 64-bit, and start off with bitmask=1
-- to effectively mean "0 already seen", so that test no longer needs to be a separate check.
-- Combined, these optimisations improved things from 7 minutes 23s to 4 mins 15s, or about 43%.
-- Partials only saved 45s, or ~10%, admittedly rather less than hoped for, but still something.
--
function aleph(sequence s, integer base)
    -- convert eg {1,15} to "1F"
    -- s may be partial: show digits>=base as '?'
    string res = repeat(' ',length(s))
    for i,d in s do
        res[i] = iff(d>=base?'?':d+iff(d<=9?'0':'A'-10))
    end for
    return res
end function

function reformat(sequence rs)
    sequence {r,s} = rs
    string root = sprintf("%A",{r}),
         square = aleph(s,r[1])
    return sprintf("%s**2 = %s",{root,square})
end function

procedure penholodigital(integer base)
    -- search squares of n from eg sqrt(123) for base=4
    -- (and stop when next square has too many digits)
    sequence penholodigitals = {}
    atom n = 0, t1 = time()+1, carry = 0
    for d=1 to base-1 do n = n*base+d end for -- (eg 123)
    n = ceil(sqrt(n))-1     assert(integer(n))
    -- (^^ -1 as we bump square and set zod on first iter)
    sequence square = repeat(0,base-1)
    integer div = prime_factors(base-1,true)[$], d, dn = n, mbit
--  square[$] = n*n  -- 9 out for base of 16 on 32 bit, so:
    -- for eg 123*123 start off with {1*123,2*123,3*123}
    for d = base-1 to 1 by -1 do
        square[d] = remainder(dn,base)*n
        dn = floor(dn/base)
    end for
    bool ndz = mod(n,div)=0, 
         zod -- zero or duplicate digit flag
    while true do -- (until we get a carry off the end of square)
        -- add 2n+1 to get the next square (using digitwise maths)
        carry += 2*n+1
        n += 1
        ndz = mod(n,div)=0
        if ndz then
            zod = false
            integer mask = 01 -- (treat 0s as dups)
            d = base-1
            while carry and d do
                atom ch = square[d] + carry
                carry = floor(ch/base)
                ch = remainder(ch,base)
                square[d] = ch
                d -= 1
                mbit = power(2,ch)
                if and_bits(mask,mbit) then
                    zod = true -- duplicate digit (or 0)
                    if d then
                        square[d] += carry
                        carry = 0
                        -- theoretically better, but in practice worse:
--                      carry = carry>((base-square[1])*power(base,d-1))
                    end if
                    exit
                end if
                mask += mbit
            end while
            if carry then exit end if
            if time()>t1 then
                integer l = length(penholodigitals),
                        -- mini-cheat for the progress messages only:
                        e = {1,0,0,0,2,1,1,10,30,20,23,0,160,419,740}[base-1]
                string sq = aleph(square,base)
                progress("scanning base %d, %s (%d/%d found)\r",{base,sq,l,e})
                t1 = time()+1
            end if
            if not zod then
                while d do -- finish zod checks & clean up any "legacy" partials
                    atom ch = square[d]
                    d -= 1
                    if ch>=base then
                        carry = floor(ch/base)
                        ch = remainder(ch,base) -- (nb: needed for mbit below)
                        square[d+1] = ch
                        if d=0 then exit end if -- (and any carry quits)
                        square[d] += carry
                        carry = 0
                    end if  
                    mbit = power(2,ch)
                    if and_bits(mask,mbit) then
                        zod = true  -- duplicate digit (or 0)
                        exit
                    end if
                    mask += mbit
                end while
                if carry then exit end if
                if not zod then
                    penholodigitals &= {{{base,n},deep_copy(square)}} -- (fmt later)
                end if
            end if
        end if
    end while
    progress("")
    integer count = length(penholodigitals)
    if base>12 and count>2 then
        penholodigitals = extract(penholodigitals,{1,-1})
    end if
    penholodigitals = apply(penholodigitals,reformat)
    string p = iff(count?":\n"&iff(base>12?sprintf("%s .. %s\n",penholodigitals)
                                          :join_by(penholodigitals,1,3)):"\n"),
           {is,s} = iff(count=1?{"is",""}:{"are","s"})
    printf(1,"There %s %d penholodigital%s in base %d%s\n",{is,count,s,base,p})
end procedure
integer lim = iff(platform()=JS?14:16)  -- js ~3.8s, desktop(64/32bit) 4/6 mins
papply(tagset(lim,2),penholodigital)

Javascript is actually nearly twice as fast as desktop/32bit, finishing base 16 in 3 mins 35s (but blank screen till then)

Raku

(9 .. 12).map: -> $base {
    my $test = (1 ..^ $base)».base($base).join;
    my $start = $test     .parse-base($base).sqrt.Int;
    my $end   = $test.flip.parse-base($base).sqrt.Int;
    say "\nThere is a total of {+$_} penholodigital squares in base $base:\n" ~
        .map({"{.base($base)}² = {.².base($base)}"}).batch(3)».join(", ").join: "\n" given
        ($start .. $end).grep: *².base($base).comb.sort.join eq $test
}

(13 .. 16).hyper(:1batch).map: -> $base {
    my $test = (1 ..^ $base)».base($base).join;
    my $start = $test     .parse-base($base).sqrt.Int;
    my $end   = $test.flip.parse-base($base).sqrt.Int;
    my @penholo = ($start .. $end).grep: *².base($base).comb.sort.join eq $test;
    say "\nThere is a total of {+@penholo} penholodigital squares in base $base:";
    say @penholo[0,*-1].map({"{.base($base)}² = {.².base($base)}"}).batch(3)».join(", ").join: "\n" if +@penholo;
}
Output:
There is a total of 10 penholodigital squares in base 9:
3825² = 16328547, 3847² = 16523874, 4617² = 23875614
4761² = 25487631, 6561² = 47865231, 6574² = 48162537
6844² = 53184267, 7285² = 58624317, 7821² = 68573241
8554² = 82314657

There is a total of 30 penholodigital squares in base 10:
11826² = 139854276, 12363² = 152843769, 12543² = 157326849
14676² = 215384976, 15681² = 245893761, 15963² = 254817369
18072² = 326597184, 19023² = 361874529, 19377² = 375468129
19569² = 382945761, 19629² = 385297641, 20316² = 412739856
22887² = 523814769, 23019² = 529874361, 23178² = 537219684
23439² = 549386721, 24237² = 587432169, 24276² = 589324176
24441² = 597362481, 24807² = 615387249, 25059² = 627953481
25572² = 653927184, 25941² = 672935481, 26409² = 697435281
26733² = 714653289, 27129² = 735982641, 27273² = 743816529
29034² = 842973156, 29106² = 847159236, 30384² = 923187456

There is a total of 20 penholodigital squares in base 11:
42045² = 165742A893, 43152² = 173A652894, 44926² = 18792A6453
47149² = 1A67395824, 47257² = 1A76392485, 52071² = 249A758631
54457² = 2719634A85, 55979² = 286A795314, 59597² = 314672A895
632A4² = 3671A89245, 64069² = 376198A254, 68335² = 41697528A3
71485² = 46928A7153, 81196² = 5A79286413, 83608² = 632A741859
86074² = 6713498A25, 89468² = 7148563A29, 91429² = 76315982A4
93319² = 795186A234, A3A39² = 983251A764

There is a total of 23 penholodigital squares in base 12:
117789² = 135B7482A69, 16357B² = 23A5B976481, 16762B² = 24AB5379861
16906B² = 25386749BA1, 173434² = 26B859A3714, 178278² = 2835BA17694
1A1993² = 34A8125B769, 1A3595² = 354A279B681, 1B0451² = 3824B7569A1
1B7545² = 3A5B2487961, 2084A9² = 42A1583B769, 235273² = 5287BA13469
2528B5² = 5B23A879641, 25B564² = 62937B5A814, 262174² = 63A8527B194
285A44² = 73B615A8294, 29A977² = 7B9284A5361, 2A7617² = 83AB5479261
2B0144² = 8617B35A294, 307381² = 93825A67B41, 310828² = 96528AB7314
319488² = 9AB65823714, 319A37² = 9B2573468A1

There is a total of 0 penholodigital squares in base 13:

There is a total of 160 penholodigital squares in base 14:
1129535² = 126A84D79C53B, 3A03226² = DB3962A7541C8

There is a total of 419 penholodigital squares in base 15:
4240C58² = 12378DA5B6EC94, EE25E4A² = ED4C93285671BA

There is a total of 740 penholodigital squares in base 16:
11156EB6² = 123DA7F85BCE964, 3FD8F786² = FEC81B69573DA24

RPL

Works with: Halcyon Calc version 4.2.7
Code Comments
 "0123456789ABCDEF" 'Digits' STO

≪  → base 
  ≪ "" SWAP
     WHILE DUP REPEAT
        DUP base MOD
        Digits OVER 1 + DUP SUB 
        4 ROLL + ROT ROT - base / RND 
     END DROP
≫  ≫ 'D→BAS' STO

≪  → base 
  ≪  0 1 SIZE FOR j
        base * OVER j j SUB Digits SWAP POS 1 - +          
     NEXT SWAP DROP
≫  ≫ 'BAS→D' STO

≪ → number base digits 
  ≪ IF number "0" POS
     THEN 0
     ELSE 
        {} base + 0 CON 1 1 PUT
        1 number SIZE FOR j
           Digits number j DUP SUB POS 1 PUT
        NEXT CNRM base ==
     END    
≫  ≫ 'PHD?' STO    

≪ 0 0 → base first last
  ≪  0 1 SF
     Digits 2 base SUB base BAS→D√ FLOOR
     "FEDCBA987654321" 17 base - 15 SUB base BAS→D√ CEIL
     FOR n
        n SQ base D→BAS 
        IF base PHD? 
        THEN 1 + n 'last' STO IF 1 FS?C THEN n 'first' STO END
        END   
     NEXT 
     IF first THEN
        first base D→BAS "^2 = " + first SQ base D→BAS +
        last base D→BAS "^2 = " + last SQ base D→BAS +
     END
≫  ≫ 'PHDSQ' STO 
 Constant

(  n  base  --  "###"  )








(  "###"  base  --  n  )





(  "###"  base  --  boolean  )



Initialize variables o/w an array of counters


the sum of counters must equal base



( base -- #PHD "first PHD" "last PHD" )

start search with "123..b"
end search with "b..321"






Display detailed results




The following lines of command deliver what is required:

9 PHDSQ
10 PHDSQ
11 PHDSQ
12 PHDSQ
Output:
12: 10
11: "3825^2 = 16328547"
10: "8554^2 = 82314657"  
9: 30
8: "11826^2 = 139854276"
7: "30384^2 = 923187456"  
6: 20
5: "42045^2 = 165742A893"
4: "A3A39^2 = 983251A764"  
3: 23
2: "117789^2 = 135B7482A69"
1: "319A37^2 = 9B2573468A1"

Wren

Library: Wren-math
Library: Wren-fmt

This is limited to base 14 as base 15 would overflow Wren's safe integer limit of 2^53.

Although I'm not quite sure why, it appears that a necessary condition for a number to be a penholodigital square is for its square root to be exactly divisible by the highest prime factor of (base - 1).

import "./math" for Int
import "./fmt" for Conv, Fmt

var primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41]
var digits = "123456789ABCD"

for (b in 9..14) {
    var master = 1
    for (d in 1...b) master = master * primes[d-1]
    var phd = []
    var min = Conv.atoi(digits[0..(b-2)], b).sqrt.ceil
    var max = Conv.atoi(digits[(b-2)..0], b).sqrt.floor
    var div = Int.primeFactors(b-1)[-1]
    for (i in min..max) {
        if ((i % div) != 0) continue
        var sq = i * i
        var digs = Int.digits(sq, b)
        if (digs.contains(0)) continue
        var key = 1
        for (dig in digs) key = key * primes[dig-1]
        if (key == master) phd.add(i)
    }
    System.print("There is a total of %(phd.count) penholodigital squares in base %(b):")
    if (b > 13) phd = [phd[0], phd[-1]]
    for (i in 0...phd.count) {
        Fmt.write("$s² = $s  ", Conv.Itoa(phd[i], b), Conv.Itoa(phd[i] * phd[i], b))
        if ((i + 1) % 3 == 0) System.print()
    }
    if (phd.count % 3 != 0) System.print()
    System.print()
}
Output:
There is a total of 10 penholodigital squares in base 9:
3825² = 16328547  3847² = 16523874  4617² = 23875614  
4761² = 25487631  6561² = 47865231  6574² = 48162537  
6844² = 53184267  7285² = 58624317  7821² = 68573241  
8554² = 82314657  

There is a total of 30 penholodigital squares in base 10:
11826² = 139854276  12363² = 152843769  12543² = 157326849  
14676² = 215384976  15681² = 245893761  15963² = 254817369  
18072² = 326597184  19023² = 361874529  19377² = 375468129  
19569² = 382945761  19629² = 385297641  20316² = 412739856  
22887² = 523814769  23019² = 529874361  23178² = 537219684  
23439² = 549386721  24237² = 587432169  24276² = 589324176  
24441² = 597362481  24807² = 615387249  25059² = 627953481  
25572² = 653927184  25941² = 672935481  26409² = 697435281  
26733² = 714653289  27129² = 735982641  27273² = 743816529  
29034² = 842973156  29106² = 847159236  30384² = 923187456  

There is a total of 20 penholodigital squares in base 11:
42045² = 165742A893  43152² = 173A652894  44926² = 18792A6453  
47149² = 1A67395824  47257² = 1A76392485  52071² = 249A758631  
54457² = 2719634A85  55979² = 286A795314  59597² = 314672A895  
632A4² = 3671A89245  64069² = 376198A254  68335² = 41697528A3  
71485² = 46928A7153  81196² = 5A79286413  83608² = 632A741859  
86074² = 6713498A25  89468² = 7148563A29  91429² = 76315982A4  
93319² = 795186A234  A3A39² = 983251A764  

There is a total of 23 penholodigital squares in base 12:
117789² = 135B7482A69  16357B² = 23A5B976481  16762B² = 24AB5379861  
16906B² = 25386749BA1  173434² = 26B859A3714  178278² = 2835BA17694  
1A1993² = 34A8125B769  1A3595² = 354A279B681  1B0451² = 3824B7569A1  
1B7545² = 3A5B2487961  2084A9² = 42A1583B769  235273² = 5287BA13469  
2528B5² = 5B23A879641  25B564² = 62937B5A814  262174² = 63A8527B194  
285A44² = 73B615A8294  29A977² = 7B9284A5361  2A7617² = 83AB5479261  
2B0144² = 8617B35A294  307381² = 93825A67B41  310828² = 96528AB7314  
319488² = 9AB65823714  319A37² = 9B2573468A1  

There is a total of 0 penholodigital squares in base 13:

There is a total of 160 penholodigital squares in base 14:
1129535² = 126A84D79C53B  3A03226² = DB3962A7541C8  

XPL0

Real, 64-bit numbers provide the precision needed beyond 32-bit integers to reach base 14 (14^13 = 7.94e14, which is 49.5 bits). Runs in 37 seconds on Pi4.

real Base;                      \Number Base being used [9..14]

proc NumOut(N);                 \Display N in the specified Base
real N;
int  Remain;
[Remain:= fix(Mod(N, Base));
N:= Floor(N/Base);
if N # 0. then NumOut(N);
ChOut(0, Remain + (if Remain <= 9 then ^0 else ^A-10));
];

proc ShowPenSq(N);              \Display N = N^2
real N;
[NumOut(N);
Text(0, "^^2 = ");
NumOut(N*N);
Text(0, "  ");
];

func Penholodigital(N);         \Return 'true' if N is penholodigital
real N;
int  Used, Remain;
[Used:= 1;                      \can't contain 0
while N # 0. do
    [Remain:= fix(Mod(N, Base));
    N:= Floor(N/Base);
    if Used & 1<<Remain then return false;
    Used:= Used ! 1<<Remain;
    ];
return Used = 1<<fix(Base) - 1;
];

int  Cnt;
real N, Limit, FirstN, LastN;
[Base:= 9.;
repeat  Cnt:= 0;  FirstN:= 0.;
        N:= Floor(Sqrt(Pow(Base, Base-2.)));
        Limit:= sqrt(Pow(Base, Base-1.));
        repeat  if Penholodigital(N*N) then
                    [Cnt:= Cnt+1;
                    if FirstN = 0. then FirstN:= N;
                    if Base <= 12. then
                        [ShowPenSq(N);
                        if rem(Cnt/3) = 0 then CrLf(0);
                        ]
                    else LastN:= N;
                    ];
                N:= N + 1.;
        until   N >= Limit;
        if rem(Cnt/3) # 0 then CrLf(0);
        Text(0, "There are ");  IntOut(0, Cnt);
        Text(0, " penholodigital squares in base ");
        IntOut(0, fix(Base));  CrLf(0);
        if Base >= 13. and Cnt > 0 then
            [ShowPenSq(FirstN);
            ShowPenSq(LastN);
            CrLf(0);
            ];
        CrLf(0);
        Base:= Base + 1.;
until   Base >= 15.;
]
Output:
3825^2 = 16328547  3847^2 = 16523874  4617^2 = 23875614  
4761^2 = 25487631  6561^2 = 47865231  6574^2 = 48162537  
6844^2 = 53184267  7285^2 = 58624317  7821^2 = 68573241  
8554^2 = 82314657  
There are 10 penholodigital squares in base 9

11826^2 = 139854276  12363^2 = 152843769  12543^2 = 157326849  
14676^2 = 215384976  15681^2 = 245893761  15963^2 = 254817369  
18072^2 = 326597184  19023^2 = 361874529  19377^2 = 375468129  
19569^2 = 382945761  19629^2 = 385297641  20316^2 = 412739856  
22887^2 = 523814769  23019^2 = 529874361  23178^2 = 537219684  
23439^2 = 549386721  24237^2 = 587432169  24276^2 = 589324176  
24441^2 = 597362481  24807^2 = 615387249  25059^2 = 627953481  
25572^2 = 653927184  25941^2 = 672935481  26409^2 = 697435281  
26733^2 = 714653289  27129^2 = 735982641  27273^2 = 743816529  
29034^2 = 842973156  29106^2 = 847159236  30384^2 = 923187456  
There are 30 penholodigital squares in base 10

42045^2 = 165742A893  43152^2 = 173A652894  44926^2 = 18792A6453  
47149^2 = 1A67395824  47257^2 = 1A76392485  52071^2 = 249A758631  
54457^2 = 2719634A85  55979^2 = 286A795314  59597^2 = 314672A895  
632A4^2 = 3671A89245  64069^2 = 376198A254  68335^2 = 41697528A3  
71485^2 = 46928A7153  81196^2 = 5A79286413  83608^2 = 632A741859  
86074^2 = 6713498A25  89468^2 = 7148563A29  91429^2 = 76315982A4  
93319^2 = 795186A234  A3A39^2 = 983251A764  
There are 20 penholodigital squares in base 11

117789^2 = 135B7482A69  16357B^2 = 23A5B976481  16762B^2 = 24AB5379861  
16906B^2 = 25386749BA1  173434^2 = 26B859A3714  178278^2 = 2835BA17694  
1A1993^2 = 34A8125B769  1A3595^2 = 354A279B681  1B0451^2 = 3824B7569A1  
1B7545^2 = 3A5B2487961  2084A9^2 = 42A1583B769  235273^2 = 5287BA13469  
2528B5^2 = 5B23A879641  25B564^2 = 62937B5A814  262174^2 = 63A8527B194  
285A44^2 = 73B615A8294  29A977^2 = 7B9284A5361  2A7617^2 = 83AB5479261  
2B0144^2 = 8617B35A294  307381^2 = 93825A67B41  310828^2 = 96528AB7314  
319488^2 = 9AB65823714  319A37^2 = 9B2573468A1  
There are 23 penholodigital squares in base 12

There are 0 penholodigital squares in base 13


There are 160 penholodigital squares in base 14
1129535^2 = 126A84D79C53B  3A03226^2 = DB3962A7541C8