begin integer MaxNameLength = 20; class Prefix(Letter); character Letter; begin ref(Prefix) array Suc(Rank('@'):Rank('Z')); integer N; procedure Insert(Title, Pos); text Title; integer Pos; begin character C; N := N+1; Title.SetPos(Pos); C := if Title.More then Title.GetChar else '@'; if Pos <= MaxNameLength then begin if Suc(Rank(C)) == none then Suc(Rank(C)) :- new Prefix(C); Suc(Rank(C)).Insert(Title, Pos+1); end if; end Insert; integer procedure ComputeBoards(Size); integer Size; begin integer Sum, Cx; if N > Size then begin for Cx := Rank('@') step 1 until Rank('Z') do begin if Suc(Cx) =/= none then Sum := Sum+Suc(Cx).ComputeBoards(Size); end for; end else begin Sum := 1; end if; ComputeBoards := Sum; end ComputeBoards; end Prefix; text procedure Transform(T); text T; begin text Tx; character C; Transform :- Tx :- Blanks(T.Length); T.SetPos(1); while T.More do begin C := T.GetChar; if 'a'<=C and C<='z' then C := Char(Rank(C)-32); if C = ' ' then C := '@'; Tx.PutChar(C); end while; end Transform; inspect new InFile("books.in") do begin integer Runs; Open(Blanks(80)); Runs := InInt; inspect new OutFile("books.out") do begin integer Rx; Open(Blanks(80)); for Rx := 1 step 1 until Runs do begin ref(Prefix) L; integer BoardsPerShelf, MaxBooksPerBoard, NBooks, Bx, NB, NS; BoardsPerShelf := InInt; MaxBooksPerBoard := InInt; NBooks := InInt; L :- new Prefix('?'); for Bx := 1 step 1 until NBooks do begin InImage; L.Insert(Transform(InText(MaxNameLength)), 1); end for; NB := L.ComputeBoards(MaxBooksPerBoard); NS := NB//BoardsPerShelf; if Mod(NB,BoardsPerShelf)>0 then NS := NS+1; OutInt(NS, 0); OutImage; end for; Close; end inspect; Close; end inspect; end