Cito-paper colloquium 23 juni 1994

Toetsen: geschiedenis & model (noot 1)

Ben Wilbrink
SCO-Kohnstamm Instituut, Universiteit van Amsterdam


Dit colloquium is georganiseerd door Henk Groen, verbonden aan het Cito. Het was voor mij een belangrijke stimulans om een eerste afronding van het algemene toetsmodel te maken. Omdat een droge presentatie van het model ten onrechte de indruk kan geven dat het gewoon een andere manier is om tegen toetsen aan te kijken, en dat instanties zoals het CITO daar geen boodschap aan hoeven te hebben, heb ik geprobeerd via een historische analyse de receptie van een heel andere manier van analyseren van toetssituaties voor te bereiden. Dit receptie-probleem is tot op de dag van vandaag gebleven. De historische analyse is later uitgebreid en strakker gemaakt, en gepubliceerd (zie onderaan deze badzijde). Henk Groen heeft een verslag van het colloquium gegeven in het huisperiodiek van het CITO gescand. Met dank aan Henk Groen, en aan de talrijke aanwezigen voor hun belangstelling, volgt hier het schriftelijke materiaal dat voor het colloquium is gebruikt. Dat ik, voor de vuist weg sprekend, hiervan nogal ben afgeweken en uit de tijd ben gelopen heeft destijds hopelijk niet al te storend gewerkt.




Deze voordracht is opgebouwd rond zes stellingen. De eerste stellingen hebben een inleidend karakter, de laatste dekken een belangrijk deel van de thematiek waarop ik een proefschrift aan het voorbereiden ben. Als introductie, en om in stijl te blijven, een keuzevraag als opgave.

opgave

Don Mellenbergh en Wim van der Linden kijken onafhankelijk van elkaar een tentamen psychometrie na en geven daar een cijfer voor. Het juiste eindcijfer uit de twee gegeven cijfers is het

  1. laagste
  2. hoogste
  3. gemiddelde.


Over deze vraag valt lang te discussieren. In ieder geval zijn de volgende punten van belang:

1) subjectief

Het is een objectief scoorbare keuzevraag met een evident subjectieve scoringssleutel. Wat denkt u overigens dat het juiste antwoord is?

NB: de vraag is niet wat gebruikelijk is, maar wat juist is.

Iedere docent zou hier eigenlijk een bevredigend antwoord op moeten kunnen geven, en een bevredigend antwoord betekent tenminste dat alle docenten het over het juiste antwoord met elkaar eens zijn (anders zijn studenten en leerlingen aan willekeur overgeleverd).

2) beslisbaar?

Wat heet subjectief: stel dat een student in beroep gaat bij het College van Beroep voor de Examens aan de RU Leiden omdat zijn docent, Leo van der Kamp, stelt dat c) de juiste manier is, en de student heeft argumenten dat b) de juiste manier is. Kan het College komen tot een uitspraak over wat de juiste manier is?

3) deskundige verschillen

De beide beoordelaars zijn ongetwijfeld deskundig op het gebied van de psychometrie. Waar zij tot een verschillend oordeel komen, is er kennelijk verschil van inzicht tussen deskundigen. Dat moet kunnen, dergelijke verschillen zijn eer regel dan uitzondering in het wetenschappelijke bedrijf. Hoe zijn studenten tegen die verschillen te beschermen?

4) leerling superdeskundige?
In de opleiding tot deskundige spelen dergelijke verschillen natuurlijk ook. Hoe werkt dat uit bij beoordelingen en examens? Dat is nog een lastig te beantwoorden vraag. Tenminste moet gelden dat het oordeel van de ene deskundige niet omlaag kan worden gehaald door een afwijkend oordeel van een andere deskundige. Tenslotte is een student geen superdeskundige die verschillen tussen deskundigen kan voorzien; en dan nog zou het meestal onmogelijk zijn te antwoorden zonder zichzelf te benadelen.

5) deskundige waardering

Er is niets gezegd over de scoring: daarin zouden beide deskundigen het nog met elkaar eens kunnen zijn, om vervolgens toch tot een andere waardering te komen. Als een dergelijke waardering zinnig is, wat we moeten aannemen omdat beide deskundigen door het aanvaarden van hun opdracht kennelijk menen dat ze niet onzinnig bezig zijn, dan verandert dat niets aan het boven gestelde.

6) of die waardering afschaffen?
Wie het met 5) niet eens is, moet pleiten voor het afschaffen van cijfers.

7) deskundig scoren

Wanneer deskundige beoordelaars verschillend scoren, dan blijft de regel gelden dat de hoogste gegeven score de toe te kennen score is. Vanzelfsprekend wordt dit per gescoorde opgave bepaald, niet op de somscore van de toets. Een aardig voorbeeld, waaraan Bar-Hillel en Falk (1982) een uitvoerige studie hebben gewijd omdat het allerminst evident is wat hier het juiste antwoord is:


8) helpt de psychometrie hier?
De psychometrie is niet behulpzaam bij het oplossen van dit probleem, integendeel: door het zonder verdere reflexie te bestempelen als een betrouwbaarheidsprobleem, komt ze tot de onjuiste manier c), omdat middelen de invloed van toevalsfluctuaties vermindert. Wat heet toeval. Zie de soms zeer uitvoerige (Edgeworth, 1988; Hartog & Rhodes, 1936) studies van meer dan een eeuw resp. halve eeuw geleden over het gebrek aan 'betrouwbaarheid' van oordelen over examenwerk, en de meer recente van Hofstee (1983) over het beoordelen van ZWO-subsidieaanvragen.

9) oefening in doorzichtigheid

Het voorgaande is een oefening in het toepassen van De Groot's beginsel van doorzichtigheid. Zodra deskundigen tot verschillende oordelen komen, is de doorzichtigheid geschonden. Het is vervolgens alleen maar behoorlijk, in bestuursrechtelijke zin, de beoordeelde daarvan niet de dupe te laten worden: zie bijvoorbeeld het kenbaarheidsbeginsel van Job Cohen (1981).

Een ezelsbruggetje: waar deskundigen verschillend oordelen, zou het middelen van deze oordelen een ernstige belemmering zijn bij het behalen van een '10,' overigens ook wanneer er in feite er maar een beoordelaar is.




Mijn onderzoek is gepositioneerd buiten de hoofdstroom van toetsontwikkeling, buiten de 'Received View,' de Geaccepteerde Theorie, die als psychometrisch is te typeren. Mijn eerste stelling is dan ook:



stelling 1: Geaccepteerde Theorie
Er is een Geaccepteerde Theorie over toetsontwikkeling, en die theorie is gebaseerd op de psychometrie. Buiten die Geaccepteerde Theorie zijn er nog steeds mogelijkheden tot zinvolle theorievorming over beoordelen in het onderwijs.


voorbeeld
'Psychometrie in de praktijk' (Cito, 1993) is een geprononceerd voorbeeld van werk binnen de Geaccepteerde Theorie. Er komt bijvoorbeeld geen verwijzing in voor naar het begrip 'doorzichtigheid' dat door Adriaan de Groot (1970) (noot 2) is geïntroduceerd en dat buiten de Geaccepteerde Theorie ligt.

Dat ik buiten de hoofdstroom werk wil niet zeggen dat ik mij ertegen afzet: een confronterende stijl van wetenschapsbeoefening is niet altijd het meest vruchtbaar, en zou in dit geval onnodig suggereren dat psychometrische methoden in het onderwijs helemaal niet thuishoren. Toch zal ik, juist vanwege die geprononceerde aanwezigheid van de Geaccepteerde Theorie, belangrijke verschillen met die Geaccepteerde Theorie wel aanduiden.

Ik zal de term 'Geaccepteerde Theorie' blijven gebruiken voor de theoretische basis vanwaaruit de meeste handboeken over 'educational measurement' zijn geschreven. Ik wil daarmee ook aangeven dat het niet aangaat voor alternatieve theorieën rechtvaardigingen te verlangen die voor de Geaccepteerde Theorie nooit zijn gegeven: de laatste theorie is geaccepteerd, wat betekent dat er geen kritische vragen bij worden gesteld, en dat de antwoorden op eventuele kritische vragen niet (meer) voorhanden zijn. Zie bijvoorbeeld Owen (1986). Zo is er van 'Toetsvragen schrijven' wel eens gezegd dat het een controversieel boek is, terwijl de vragen die daarin opgeworpen juist van de Geaccepteerde Theorie op bepaalde punten een controversiële theorie maakt.

voorbeeld
Een voorbeeld: de stelling, in 1977 (ORD), dat keuzevragen in wezen even subjectief zijn als open vragen. Ik kan mij bij een aanval op deze stelling verdedigen door te zeggen dat wie de scoringssleutel vaststelt uiteraard een subjectieve beslissing neemt. Maar ik kan ook in de tegenaanval gaan en zeggen dat de Geaccepteerde Theorie nooit heeft aangetoond dat naast objectieve scoorbaarheid er nog meer objectiviteit in keuzevragen zou kunnen schuilen.




Stelling 2: gewicht van het verleden
De huidige beoordelingspraktijk in het onderwijs is niet noodzakelijk beter, doelmatiger of doeltreffender dan die van 20, 100, 500 of 1000 jaar geleden.



De hedendaagse beoordelingspraktijk kan minder doelmatig zijn dan in het soms verre verleden, wanneer oude beoordelingsvormen zijn blijven bestaan terwijl de samenleving ingrijpend is veranderd. Het is zeker niet vanzelfsprekend dat het onderwijs altijd in wezenonveranderd is gebleven, en beoordelingsvormen langs de rationele weg van evaluatie en bewuste veranderingen zijn 'verbeterd.'

voorbeeld (zittenblijven)
Een pregnant voorbeeld is het zittenblijven: schoolleiders kunnen de rationale van dat zittenblijven niet aangeven, evenmin als oplossing voor wat vrijwel iedereen wel als een probleem ziet (Wald, 1985). De kosten van zittenblijven drukken met honderden miljoenen guldens jaarlijks op de begroting van Onderwijs en Wetenschappen; onderzoek laat consistent zien dat niemand beter wordt van zittenblijven; in tal van landen, o.a. in Scandinavië, bestaat dit zittenblijven niet (Bos, 1984). Al in 1940 schreef Posthumus dat het kennelijk zo is dat het percentage zittenblijvers in de Hoger Burgerschool vrijwel constant is, ongeacht ontwikkelingen in de wereld over driekwart eeuw.




stelling 3: lessen uit het verleden
Aandacht voor de historische ontwikkeling van beoordelingspraktijken is van groot belang, en tot nu toe vrijwel afwezig.



De kracht van de traditie is juist in het onderwijs bijzonder groot. Organisatorische vormen kunnen blijven bestaan, ondanks ingrijpende veranderingen in de samenleving. Na de Kerk is de Universiteit de oudste maatschappelijke institutie in Europa. Het is daarom te verwachten dat vele beoordelingsvormen een behoorlijk lange geschiedenis hebben. Maar er is nauwelijks onderzoek naar deze geschiedenis gedaan, anders dan in de marge van breder geschiedkundig onderzoek, of als specialistische studie naar een bepaalde tijd en plaats. Toch zou alleen al een globaal overzicht van kenmerkende beoordelingsvormen uit het verleden kunnen leiden tot nieuwe inzichten in de hedendaagse praktijk.

Ik verwacht dat historisch onderzoek een belangrijke opbrengst kan hebben bij het achterhalen van wat nu eigenlijk de essentiële kenmerken van dat beoordelen zijn, kenmerken die in een theorie over dat beoordelen een hoofdrol moeten spelen.

voorbeeld (afwezigheid van historisch besef)
Aandacht voor de historische ontwikkeling achter hedendaagse toetspraktijken ontbreekt in vele handboeken, ook in 'Vijven en zessen.' Adriaan de Groot heeft mij geschreven dat hij sterk gemotiveerd was de gekte van het vijven en zessen in het middelbaar onderwijs aan de kaak te stellen, en misschien juist daardoor nooit is toegekomen aan de vraag wat de historische wortels van dit beoordelingsstelsel zijn.

De beweging van het authentiek toetsen (noot 3) is een beweging die eveneens zonder historisch bewustzijn is: het is een beweging die louter negatief gedefinieerd lijkt, gericht op onderscheiden naar prestaties zonder daarbij gebruik te maken van klassiek psychometrische methoden.

voorbeeld (belang van historisch inzicht)
In de geschiedenis van het onderwijs blijken beoordelen en rangordenen vaak hand in hand te gaan. Dat rangordenen is anders van karakter, al naar gelang van tijd en cultuur, maar is vaak pregnant aanwezig. Vroeger werd die rangorde ook sterk benadrukt in ceremoniële presentaties tegenover de buitenwacht. Dat dit uitbundige rangordenen plaats heeft gemaakt voor de zo anders lijkende stelsels van cijfergeven en nu van gestandaardiseerde toetsing wil niet zeggen dat het rangordenen nu uit het onderwijs is verdwenen. De studie van de historische wortels van het beoordelen in het onderwijs maakt juist duidelijk dat het wel eens zou kunnen zijn dat het cijferstelsel een meer verhulde vorm van rangordenen is. De Geaccepteerde Theorie verbergt op geen enkele manier dat zij is gebaseerd op het streven de 'ware rangorde' van leerlingen in getallen af te beelden, en toch ontstaat daarover geen brede maatschappelijke discussie.




stelling 4: ook relatieve normen zijn absoluut te maken
Beoordelen gebeurt al eeuwenlang door en door relatief, gebaseerd op verschillen tussen personen in plaats van leerwinst van personen. Daar hoeven we ons niet bij neer te leggen.


Op zoek naar de wortels van het cijferen vallen snel twee dingen op: dat cijferen de vervanger is van rangordenen, en dat cijfers goed lijken te passen bij (eind-)examens als vertaling van judicia. De intrigerende vraag is: is er een verband tussen deze twee observaties?

rangordenen

Tot ver in de negentiende eeuw, en soms tot in de twintigste, was in het middelbaar onderwijs rangordenen aan de orde van de dag, en dat was in de meeste westerse landen het geval. Als typerend voorbeeld van hoe dat rangordenen in zijn werk gaat kan de uiteenzetting van Van Herwerden (1947, p. 40-41) dienen over het stelsel van fouten en notae zoals dat nog in 1900 in Groningen fungeerde.


p. 40: In het schoolleven speelde het prijzenstelsel een grote rol. Het was gewoonte - al werd hiervan wel afgeweken - om in iedere klasse voor elk vak, waarin wekelijks tenminste twee uur les werd gegeven, eens per jaar een prijs uit te loven. De berekening, aan wie de prijzen zouden ten deel vallen, was ingewikkeld, want daarvoor waren, behalve gemaakte fouten, ook de zogenaamde notae en de bonae notae of expunten van betekenis. Wat de fouten betreft, in de loop van het schooljaar werden voor elk vak, waarvoor een prijs beschikbaar was gesteld, gedurende de schooltijd prijsthemata - het woord themata moet zeer ruim worden opgevat - gemaakt. In 1885 werden bijvoorbeeld in de 1e klasse voor het Frans dertig en voor elk der andere vakken tien prijsthemata vastgesteld (...). Behalve de fouten werden ook de notae aangetekend, deze werden verdeeld in: 1e notae negligentiae - tellend voor een halve fout - welke gegeven werden aan hen, die iets, dat zij bij zich moesten hebben, vergeten hadden, die onoplettend waren of handelden tegen de orde, en 'die meer dan een kwartier uur te laat ter school' kwamen, 2e notae ignorantiae (= een fout) voor hen, die hun les niet kenden, 3e notae pigritiae (= anderhalve fout) voor het niet of slecht maken van het huiswerk, 4e notae absentiae illegitimae (= 2 fout) voor onrechtmatig schoolverzuim, 5e notae immodestiae (= 2 fout) 'gegeven aan hen, die zich tegen tucht of goede zeden vergrepen.' Behalve straf was er beloning: artikel 26 van het huishoudelijk reglement bepaalde: 'Aan hen, die hunne les goed gekend, het hun opgegeven werk goed verrigt, of zich door hun gedrag boven hunne leerlingen onderscheiden hebben, kunnen bonae notae (expunten) gegeven worden, voor iedere van welke eene fout en op de vijfde klasse een halve fout op de lijst wordt afgetrokken.' In de 6e klasse werden deze expunten niet gegeven. De lijsten, waarop dit alles werd aangetekend, waren niet alleen beslissend voor het toekennen van de prijzen maar hadden naast en meer dan het zomerexamen invloed op al of niet promotie van de leerlingen."
p. 41: "Iedere leerling hield de puntenlijst van zijn gehele klasse bij. De daarvoor gebruikte speciale negligentie- of ignorantieboekjes werden bij de custos gekocht. Zodra een leraar zeide, dat er een of meer fouten moesten worden aangetekend, klapten alle leerlingen hun vakken open, haalden de boekjes voor de dag en vulden in wat hun werd opgedragen, waarvan de leraar zelf natuurlijk ook aantekening hield. De schaduwzijde van dit systeem, dat zo licht verkeerde gevoelens en praktijken kon oproepen, is duidelijk. In de meeste klassen hadden de twee of meer rivalen naar het primusschap hun aanhang, met alle kwade gevolgen daarvan. Er waren leerlingen, die met een zeker leedvermaak, gepaard met zelfverheffing, fouten van anderen noteerden. Een ongewenste concurrentiegeest kon de onderlinge verhouding verstoren en niet alle jongens waren even goed tegen de innerlijke spanning, gewekt door deze foutenjagerij, bestand, anderen evenwel dreven de spot er mee, een enkele leraar eveneens.

In Leiden werd in de 17e eeuw over dit puntenstelsel geklaagd door Wensinck in een schrijven aan curatoren van het Leids Gymnasium: "'wie de minste fouten in de thema heeft' ontvangt de prijs, 'maar in 't land der blinden is eenoog koning, soms zijn er leerlingen met 300 of 400 fouten, en zo krijgt wie 200 fouten heeft een prijs!.'" (Coebergh van den Braak, 1988, noot 330)

de hiërarchische verhoudingen van de feodale middeleeuwen

De wortels van het rangordenen liggen in de nog dominant feodale samenleving van de middeleeuwen. Bij uitstek blijkt dat bij de universitaire examens. Dit zijn examens waarvoor eigenlijk altijd iedereen slaagt. Bij deze examens werd een rangorde van de geslaagden gemaakt, de locatus, waarvan vaak wordt gezegd dat deze rangorde competitief bepaald is (o.a. Rashdall, Prahl). Hoogstwaarschijnlijk echter is de locatus in de eerste plaats een rangordening naar maatschappelijke positie, in latere instantie naar criteria zoals duur van de inschrijving. Academische prestaties spelen bij de locatus geen hoofdrol. Dat is een belangrijk gegeven, en het is niet zonder betekenis. In andere samenlevingen tezelfdertijd, zoals de Chinese en de Moslimse, speelt competitie wel degelijk een rol, en zelfs een zeer dominante, zij het in ieder van die samenlevingen op geheel andere wijze. Het in de middeleeuwse universiteit ontbreken van competitieve examens doet de vraag rijzen waar, wanneer en waarom in Europa competitieve examens zijn ontstaan.

Toch was het idee van rangorde zowel als competitie in het middeleeuwse hoger onderwijs niet afwezig. In de dagelijkse lessen en overhoringen die de meester met zijn studenten speelde beloning en straf een rol: een prijs voor de beste, en een asinus voor de slechtste prestatie. Ook in de Latijnse school van de late middeleeuwen werd wel op deze manier beloond en bestraft. Een belangrijke onderwijsvernieuwer was Joan Cele, de rector van de school van Zwolle aan het eind van de 14e eeuw, ten tijde van het ontstaan van de beweging van de Broeders des Gemenen Levens van Geert Groote. Cele geldt als de uitvinder van het klassikaal georganiseerde onderwijs, zowel naar leerstof, als naar groepering van de leerlingen. Cele werd hierin door nood gedreven: hij zag zich geconfronteerd met vele honderden leerlingen, tot zelfs 900 toe, die hij met slechts de steun van twee Parijse magisters les moest zien te geven. De onderwijsmethodiek van Joan Cele is van doorslaggevende betekenis geweest op die van de Universiteit van Parijs en op die van de Jezuieten. De laatste hebben zeker in Frankrijk een sterk stempel op het middelbaar onderwijs gedrukt.

Het humanisme heeft in het onderwijs de nadruk gelegd op het belonen, het prijzen en het prijzen uitreiken, onder afkeuring van het straffen, vooral van het lijfelijk straffen. De jezuïeten kunnen ook in dit opzicht tot de humanistische stroming worden gerekend. Het rangordenen en prijzen is bij de humanisten vooral een middel om de leerlingen te motiveren, voor de bevordering tot hogere klassen was de rangorde niet doorslaggevend. Overigens was het in de systematiek van het laat-middeleeuwse onderwijs gebruikelijk om dezelfde cursus meermalen te volgen, en in die zin bestond er dus niet zoiets als 'zittenblijven.' Zo geven de examenreglementen van de Universiteit van Parijs opsommingen van de boeken waarover men een collegecyclus moest hebben gevolgd, en hoevaak diezelfde cyclus gevolgd moest zijn, meestal twee of drie keer.

examens

Zodra er voor maatschappelijke posities ook gerecruteerd gaat worden op basis van gevolgd onderwijs, gaan afsluitende examens een belangrijke rol spelen. Dat is ook het geval waar kunstmatig schaarste wordt gecreëerd, zoals in Duitsland waar in de 19e eeuw de toegang tot de universiteiten werd afgeknepen door het afsluitende examen van het gymnasium, de 'Abitur.' Dergelijke examens zijn dus uitdrukkelijk selectief bedoeld en vragen als legitimatie een objectieve beoordelingsmaatstaf. Een cijfersysteem komt daar als pseudo-normatief stelsel nog het dichtst bij, zeker wanneer die cijfers worden toegekend door commissies bestaande uit onafhankelijke deskundigen.

Voor Nederland doet zich rond 1870 de gelegenheid voor het eindexamen van de dan opgerichte Hogere Burgerschool te reglementeren. De eerste ervaring met de afname van het nieuwe eindexamen was dat men dat van provincie tot provinsie op eigen wijze had afgenomen, reden voor de overheid om zowel de inhoud van het examen als de beoordeling te standaardiseren. Voor de beoordeling werd de cijferschaal van 1 tot 10 voorgeschreven, met de vijf als 'even voldoende' middenpunt. De examencommissie besliste, zonodig bij meerderheid van stemmen, over de kandidaten met een of meer onvoldoende resultaten.

Het is niet onwaarschijnlijk dat onder druk van deze examenbeoordeling ook voor de beoordeling van het reguliere schoolwerk steeds meer voor het cijfersysteem is gekozen. Systematisch onderzoek hiernaar is mij niet bekend. Het is zeker niet zo dat het cijfersysteem meteen gemeengoed in het onderwijs is geworden. In ieder geval de gymnasia hebben het nog langere tijd volgehouden bij hun rangenstelsel te blijven.

Een cruciaal gegeven is de situatie in Frankrijk in het midden van de 19e eeuw. Tot dan heeft het rangordenen de overhand, en is het onderwijs gericht op de 'concours,' de rangordening die aan het eind van het schooljaar wordt aangebracht. Daarna krijgt het eindexamen, het 'baccalauréat,' een bepalende invloed op de inhoud van het onderwijs. Dat suggereert dat het rangordenen niet goed verenigbaar is gebleken met een toenemend belang van het eindexamen, en dat het rangordenen is vervangen door een beoordelingssysteem dat een afspiegeling was van dat gebruikt voor dat eindexamen.

culturele bepaaldheid

Het rangordenen in het onderwijs is dus een pregnant in de Europese cultuur aanwezige pedagogische opvatting. Het cijferen is ook geen breuk met de essentie van het rangordenen, wel met enkele van de uiterlijke verschijnselen daarvan. Zelfs de breuk is niet absoluut: nog heel lang is er gerangordend op basis van cijfers, dat is mijzelf nog overkomen in de eerste klas van de Koning Willem III school in Apeldoorn, in 1949: de plaats in de banken werd bepaald door behaalde cijfers. Iedere cijferschaal is nog steeds een schaal met een rangorde. De pretenties van de cijferschaal zijn veel groter dan die van de eenvoudige rangorde: de cijferschaal heeft de pretentie algemeen geldig te zijn. Een cijfer krijgen betekent voortaan: gerangordend te zijn binnen de groep die in Nederland in klas 3 van de Hogere Burgerschool zit, niet meer alleen maar binnen de eigen klas. Omdat de pretentie van algemeenheid niet valt waar te maken, is er met het cijferen irrationaliteit ingeslopen in het onderwijs: De Groot's Vijven en zessen zet de irrationaliteit van het cijferstelsel in de beklaagdenbank.

kan het zonder rangordenen?

Wel of niet rangordenen in het onderwijs is een keuze: we kunnen dat samen willen of juist niet. Het is mogelijk zonder rangordenen te werken, en vanuit het recht bezien zou dat ook moeten. Beoordelingen treffen de beoordeelde rechtstreeks in zijn of haar belang, en dat geldt zeker voor beoordelingen die zich uitstrekken over een groot aantal jaren. Het is een eenvoudig beginsel van behoorlijk bestuur dat het beoordelen gebeurt op basis van de verdienste van de beoordeelde persoon, en niet op die van anderen. Daar zijn uitzonderingen op, zoals bij personeelsselectie, maar die tasten het beginsel niet aan. Het moet dus zonder rangordenen kunnen.

een algemeen toetsmodel moet het 'zonder' kunnen

Het voorgaande leidt tot de eis dat een algemeen toetsmodel niet gebaseerd mag zijn op rangordenen, dus niet op verschillen tussen leerlingen. Dat is niet alleen op basis van de juridische eisen aan behoorlijk beoordelen, maar ook gezien het feit dat rangordenen zozeer cultureel bepaald is. De Geaccepteerde Theorie valt hier dan meteen af. Er is uiteraard geen bezwaar tegen later alsnog met individuele verschillen rekening te houden, maar het strookt niet met de pretentie van algemeenheid van het te presenteren model om individuele verschillen te vooronderstellen.

toepasbaar op een enkele leerling

Aan deze eis is te voldoen door een model te ontwikkelen dat op een enkele leerling toepasbaar is. Tot deze klasse van toetsmodellen behoren de tentamenmodellen van Van Naerssen, waarin op geen enkele manier sprake is van meerdere gelijktijdige deelnemers, laat staan van verschillen tussen deze deelnemers. Toch zijn deze tentamenmodellen in beginsel buitengewoon geschikt om geïntegreerde onderwijs-toets-situaties te ontwerpen, en om het werkelijke functioneren van onderwijs en toetsen te onderzoeken en bij te sturen. Deze modellen gaan uit van besliskundige technieken. Het bijzondere is dat het in deze modellen niet gaat om een beslissing van de docent over de leerling, maar om een beslissing van de leerling over haar studiestrategie.

het tentamenmodel is een specifieke toepassing

Het algemene toetsmodel is in zekere zin een uitgeklede versie van een tentamenmodel. Van Naerssen heeft een model ontwikkeld voor een heel specifieke situatie: het modelleren van de optimale studiestrategie wanneer studenten een onbeperkt aantal gelegenheden hebben om tentamen af te leggen tot zij daarvoor zijn geslaagd. De probleemstelling van Van Naerssen was: hoe kan de docent greep krijgen op de studiestrategieën van studenten, en wat zijn eigenlijk optimale strategieën voor studenten onder deze omstandigheden? De docent kan de sturen in de optimale studiestrategieën die voor studenten beschikbaar zijn.

kenmerken van het algemene model


De essentiële punten in het algemene toetsmodel zijn:


  1. individuele verschillen zijn niet voorondersteld
  2. het gaat om steekproeven uit een goed gedefinieerd vaardigheidsdomein
  3. het gaat om onderwijs, dus is voorspelbaarheid (voor de leerling) van belang
  4. en voorbereidbaarheid, wat veronderstelt dat leerlingen doelgericht zijn
  5. maar die leerlingen hebben meer levensdoelen dan alleen studeren.



De onderwerpen 2 tot en met 5 zijn hierna uitgewerkt.

Met dit algemene toetsmodel als instrument moet het mogelijk zijn voor de studenten een beoordelingssituatie te creëren waarin zij naar absolute normen worden gewaardeerd, niet naar relatieve. Omdat in deze wereld niets echt absoluut is, behalve dat wij sterflijk zijn, zijn absolute normen alleen voor gelegenheid van deze toets of dit examen te creëren, maar dat is vanuit de eis van doorzichtigheid voldoende. Tijdelijk absolute normering is een antwoord op welles-nietes discussie over relatief versus absoluut beoordelen, zoals enkele decennia geleden gevoerd tussen Wienand Wijnen en Egbert Warries.
De test of in een bepaald geval aan de eis is voldaan: is het mogelijk dat alle beoordeelden slagen, dat alle beoordeelden hetzelfde cijfer krijgen, dat er geen stoppen meer doorslaan wanneer een toets niet blijkt te spreiden, maar dat het nog steeds mogelijk is dat ook grote prestatieverschillen tussen leerlingen verenigbaar zijn met een in alle opzichten goed functionerende beoordelings-situatie?

competitie

Het zou een illusie zijn te menen dat met deze klasse van toetsmodellen individuele verschillen geen rol meer spelen. Natuurlijk blijven individuele verschillen van belang. Studenten verschillen in motivatie en capaciteiten, en in hun levensdoelen buiten de studie waaraan zij in verschillende mate tijd willen besteden die niet tegelijk aan de studie kan worden besteed. Niet alle verschillen komen tot uitdrukking in verschillende toetsresultaten, of: dezelfde toetsresultaten kunnen uitdrukking zijn van grote persoonlijke verschillen. Is hier de Geaccepteerde Theorie dan toch weer het relevante model? Ik dacht het niet, omdat deze situatie te complex is voor deze rechtlijnige theorie. Hier is de theorie van James Coleman voor het modelleren van sociale systemen relevant. Omdat deze theorie bij uitstek geschikt is om te gebruiken in competitieve situaties, is ze ook toepasbaar overal waar in het onderwijs relatieve beoordeling en selectie plaatsvindt (en dat is op veel plaatsen, zie bijv. Wilbrink en Dronkers, 1993). Ik heb die toepassing beproefd op een geschikte dataset uit het universitair onderwijs, maar ik kan daar in het kader van deze voordracht niet verder op ingaan (zie de Literatuur voor de betreffende papers).

stelling 5: beoordelen van levend materiaal, niet van dood materiaal
De beoordelaar is niet vrij de beoordeling naar eigen goeddunken in te richten, de positie van de beoordeelde is voortdurend van belang.



Voor beoordelen zijn vele verschillende modellen beschikbaar, die ook altijd wel ergens gebruikt worden, vaak terecht, vaak ook ten onrechte.

de wedstrijd

Historisch is het wedstrijd-model altijd van belang geweest. Vele examens zijn in het verleden uitdrukkelijk volgens dat model ingericht, zoals in het voorgaande behandeld. Het sollicitatie-model, de beoordeling bij personeelsselectie, is een wedstrijdmodel (noot 4). Een verwant model is dat van het twistgesprek, vooral het juridische twistgesprek; de beslisregels zijn hier natuurlijk complexer. Het twistgesprek is overigens een in de middeleeuwen zeer dominante onderwijs- en examenvorm, ook in andere faculteiten, waaronder die van de artes. De huidige academische promotie is daar naar de vorm een flauwe en enigszins omgekeerde afspiegeling van.

het meten

Een dominant hedendaags model is het natuurwetenschappelijke model van de afbeelding van de werkelijkheid in geschikte getallen. De Geaccepteerde Theorie hoort in deze klasse thuis. De vooronderstelling is dat de wereld zich niets van de meting aantrekt. Helaas gaat die vooronderstelling niet altijd op, zelfs niet in de fysica, maar zeker niet wanneer er aan mensen wordt gemeten (noot 5).


het contract

Een interessant model voor de beoordeling is het contract, waarbij partijen met elkaar afspraken maken over prestaties en tegenprestaties. Om een contract op te kunnen stellen, zijn ondubbelzinnige criteria nodig: welk loon krijgt de dagarbeider voor welke prestatie, hoe wordt die prestatie vastgesteld (gemeten, gewogen, beoordeeld), waar bestaat het loon uit en hoe wordt dat gemeten, gewogen of geteld? En aan wie kan het toezicht worden opgedragen, wie mag controleren of de maatstok van de landheer niet te kort is? In de middeleeuwen waren dergelijke 'meetproblemen' een dagelijkse bron van ellende en conflicten, en om die reden ook een prikkel om procedures te verfijnen in zowel juridisch als fysisch opzicht.

Welnu, beoordelen in het onderwijs heeft verrassend veel gemeen met dit contractmodel zoals het vroeger in de landbouw en huisnijverheid werd gebruikt (noot 6). Daar komt nog bij dat de machtsverhouding tussen de contractpartijen in het onderwijs wel erg ongelijk is. De leerling kan een als oneerlijk ervaren beoordeling lang niet altijd tot een oplossing brengen die bevredigend is naar de rechtsmaatstaven die in het gewone maatschappelijke verkeer gelden. Zo is het buitengewoon moeilijk voor de leerling een onafhankelijk onderzoek te laten doen naar de maatstok van zijn docent, of deze nog wel de afgesproken lengte heeft. Maar de docent kan er veel aan doen om het vertrouwen van de leerling te winnen: een goede ontwerpmethode voor zowel de toetsvragen als de toets zelf kunnen de beoordeling transparanter maken.



een ontwerpmethode voor toetsvragen

Voor de kwaliteit van toetsvragen is het van belang dat ze zijn geconstrueerd volgens heldere regels gebaseerd op een verdedigbaar didactisch concept. De cognitieve taxonomie van Bloom c.s. valt dus af als een instrument. Deze taxonomie kan in de praktijk niet consistent worden toegepast. Dat is ook niet verwonderlijk omdat Bloom c.s. uitgaan van het denken van de leerling, een psychologisme, want dat denken is niet direct waarneembaar. Het kan zijn dat Bloom c.s. door de Geaccepteerde Theorie op het verkeerde been van de 'ware beheersing' als ideaal-begrip zijn gezet. Het alternatief voor Bloom's cognitie is echter niet noodzakelijk Mager's behaviorisme.

begripsmatige leerstofanalyse

Ik heb gekozen voor begripsmatige uiteenlegging van de leerstof als beschrijving van het kennisdomein. Die keuze is ook door van Westrhenen gemaakt, zij het niet met uitsluiting van Bloom's taxonomie. Het gaat om de beschrijving van een vakgebied of een wetenschap, niet om wat zich in het denken van zijn beoefenaars afspeelt. Met dat kennisdomein moeten de beoefenaars van het vak op een bepaalde manier om kunnen gaan. Ze moeten in ieder geval de 'taal' van het vak kunnen spreken. De leerling die een inleiding in het vak krijgt, moet een goed omschreven deel van die 'taal' kunnen gebruiken. Deze benadering heeft meer verwantschap met de wetenschapstheorie die probeert te omschrijven wat de inhouden van een bepaald vak zijn dan met de psychologie. Ik maak een uitzondering voor delen van de cognitieve psychologie die zich met begripsmatige kennis bezighouden, een specialisme dat sterk is geïnspireerd door het latere werk van Wittgenstein.

De ontwerpmethode, zoals uitgewerkt in mijn 'Toetsvragen schrijven,' leidt tot een systematiek die tot resultaat heeft dat de toetsvragen in zekere zin worden gestandaardiseerd naar aard. Ik kan dat het makkelijkst als volgt duidelijk maken. Veronderstel dat de te toetsen leerstof enkele kernbegrippen bevat die de leerling goed moet beheersen. Beheersen wil zeggen dat de leerling is staat is het begrip adequaat toe te passen op aangereikte voorbeelden, situaties, casus, etc., en daarbij een onderscheid kan maken tussen wat nog wel, en wat niet meer onder dat begrip valt, of wat onder dit an wel onder een ander begrip valt. Keuzevragen zijn hier in principe heel erg geschikt voor. Alles wat met diagnostiek heeft te maken is in deze vraagvorm te brengen.

voorbeeld

" ... Alphen ... aan de Rijn. (Alphen, ja.) En eh ... mooi ... eh ... ik ... lekker ... lopen. (Ja ... ja waar?) Waar? eh ... Alphen aan de Rijn. (Loopt U de hele stad door?) Nee ... fiets of nee eh ... eh ... auto eh ... boodschappen doen. (oh ja.) En eh ... eh bellen. (Ja.) En eh ... eerst eh ... eh ... kopje koffie ... eh ... Ellie en eh ... beetje praten."

Bovenstaande tekst is een typisch voorbeeld van

  1. afasie van Wernicke
  2. afasie van Broca
  3. amnestische afasie
  4. productieafasie.



afrader

Onomatopeeën (klank-nabootsende woorden) in een natuurlijke taal zijn

  1. willekeurige (arbitraire) taaltekens waarbij de vorm direct van de betekenis is afgeleid;
  2. willekeurige taaltekens waarbij de relatie tussen vorm en betekenis afhankelijk is van de context en de situatie;
  3. conventionele taaltekens waarbij de relatie tussen vorm en betekenis afhankelijk is van de spontane interactie tussen taalgebruikers in een gemeenschap;
  4. conventionele taaltekens waarbij de vorm de betekenis in zekere mate weerspiegelt.



bij het voorbeeld

Het voorbeeld is een glasheldere vraag, alle alternatieven zijn relevant, de vraag zou in open vorm even glashelder zijn. Belangrijker is dat de vraag een belangrijk stuk van de leerstof bestrijkt, en dat een onbeperkt aantal varianten is te ontwerpen met behulp van transcripties van gesprekken met mensen met deze verschillende vormen van afasie. Ieder tentamen kan een of meer van deze vragen bevatten. De studenten zullen door deze vraag nooit verrast zijn, en telkens toch voor een interessante opgave kunnen staan, afhankelijk van hoe moeilijk de docent de voorbeelden wil maken. Het is ook een voorbeeld van de wijze waarop naar aard dezelfde toetsvragen toch telkens nieuw kunnen zijn.

bij de afrader

Met excuus aan de docent die deze vraag voor mij heeft bedacht: hier gaat van alles helemaal mis omdat de vraag niet is bedacht vanuit een helder ontwerp. De vraag is naar mijn ervaring representatief voor de kwaliteit van keuzevragen zoals deze door docenten in het universitair onderwijs worden ontworpen. Wat mankeert er zoal aan deze vragen, zodat studenten er gestresst van raken in plaats van er het idee aan over te houden dat ze kernbegrippen van het vak inderdaad beheersen? Het belangrijkste is wel dat het niet duidelijk is hoe studenten zich op dergelijke vragen doelmatig kunnen voorbereiden. Andere problemen zijn:



Vragen van het afrader-type kan de docent de leerling niet aandoen, ze zouden buiten het contract moeten vallen. De leerling heeft bescherming nodig tegen dergelijke rampzalige toetsvragen, maar kan die bescherming eigenlijk nergens krijgen. Pogingen als van het NIP met zijn Richtlijnen, van Job Cohen met zijn codificering van algemene regels van behoorlijk onderwijs geënt op die van behoorlijk bestuur, van de Raad voor het Jeugdbeleid, onderwijskoepels en het departement om het instituut 'leerlingenstatuut' van de grond te krijgen hebben daaraan nog weinig tot niets veranderd. In de Verenigde Staten zijn diverse verenigingen van docenten bezig codes te ontwikkelen voor 'assessment,' maar dat staat nog heel ver af van de praktijk in de klas.



een ontwerpmethode voor de toets

Op het niveau van de toets zijn er andere problemen, die op dezelfde manier zijn te benaderen door vanuit de leerling te denken. Is de toets zo ontworpen dat de leerlingen hun score als fair beschouwen? De leerling heeft zich ingespannen in het onderwijs, en waarschijnlijk vooral in de voorbereiding op de toets, en mag door de toetsing niet teleurgesteld worden: het contract moet immers worden nagekomen dat inspanningen naar de mate van die inspanning worden beloond. Er is hier een moeilijkheid: niet de inspanning, maar het resultaat van die inspanning wordt beloond. Maar dat is niet wezenlijk anders voor de middeleeuwse dagloner die immers niet naar zijn gewerkte uren, maar naar zijn gemaakte productie werd betaald. Het gaat hier om een gegeven dat beide contractpartners heel goed begrijpen, en hoewel ook de eerlijkheid van die regel ter discussie is te stellen, wil ik dat hier vooral niet doen. Het komt er dus op neer dat de toets zo moet worden ontworpen dat meer inspanning zich direct vertaalt in een merkbaar betere kans op een hogere score. En het zou heel goed van pas komen wanneer dat niet alleen maar achteraf in de beleving van de leerling zo is, maar ook al vooraf zodat de leerling zijn inspanning kan richten op een voorspelbaar resultaat.

Welnu, dit model is gelukkig in overzichtelijk wiskundige termen te vertalen, en bij grafische uitbeelding van een en ander blijkt hopelijk dat de specifieke formules voor de gebruikers in een black box mogen blijven zitten. (noot 7)

94gif/94AlgToetsModel1CITO407209.GIF

prealabel: toetsvragenverzameling en ware beheersing

Ooit heb ik de ontwerpmethode voor toetsvragen ontwikkeld om docenten in staat te stellen op doelmatige wijze telkens nieuwe toetsvragen te maken. De behoefte daaraan werd in de tachtiger jaren sterk vergroot omdat door het inzagerecht van studenten het niet meer mogelijk was te proberen toetsvragen geheim te houden. Met dezelfde ontwerpmethode is het mogelijk een grote verzameling nog niet gebruikte toetsvragen aan te leggen, en wel op zo'n manier dat de docent met overtuiging kan zeggen dat die verzameling precies dekt wat hij met de leerstof beoogt. Als zo'n verzameling eenmaal beschikbaar is, is het mogelijk de 'ware beheersing' van studenten op die verzameling (operationeel) te definiëren. Figuur 1 is daar een 'afbeelding' van. In de praktijk is het voldoende dat de ontwerpregels duidelijk genoeg zijn om voor iedere toets vragen te kunnen maken die niet te onderscheiden zouden zijn van werkelijk uit een verzameling getrokken vragen.


voorspelbaarheid: de proeftoets als informatie

De leerling kan alleen voorspellen op grond van relevante informatie. Voor die informatie is ook een handige operationele definitie nodig. De proeftoets is een prachtig instrument om di informatie te definiëren. De proeftoets wordt getrokken uit dezelfde verzameling als de toets, zdat de score op de proeftoets relevante informatie voor de voorspelling oplevert. Laten we afspreken dat zo'n proeftoetsresultaat ook de enige relevante informatie is.


Dat het algemene toetsmodel werkt vanuit de optiek van de leerling heeft een belangrijk gevolg. Voor de leerling ziet iedere toets eruit als was deze speciaal voor haar willekeurig getrokken uit een grotere verzameling van mogelijke toetsvragen. Dat blijft zo, ook al krijgen medeleerlingen dezelfde toets voorgelegd. Voor iedere leerling ziet de toets eruit alsof deze willekeurig is getrokken. Het maakt onder dit model niet uit of andere leerlingen dezelfde toets krijgen, of een toets die gebaseerd is op een andere trekking. Dat is een interessante constatering, want het ziet ernaaruit dat het niet nodig is ervoor te zorgen dat de toets dit jaar even moeilijk of makkelijk is als vorig jaar, omdat het volstaat ervoor te zorgen dat de toets mag worden opgevat als getrokken uit dezelfde verzameling. Voor de leerling is zoiets als de moeilijkheid van afzonderlijke vragen een irrelevant begrip. Dit is een stand van zaken waar de Geaccepteerde Theorie niet mee kan leven. In de Geaccepteerde Theorie is het om een of andere reden wenselijk dat verschillende leerlingen een identieke toets krijgen voorgelegd, althans versies van de toets die 'even moeilijk' zijn. Het kan zijn dat dit een erfenis is van de bemoeienis die psychometrici ook hebben met gestandaardiseerde tests. Het is evenmin uitgesloten dat de drang tot standaardiseren een overblijfsel is uit tijden dat het rangordenen eerlijkheidshalve vroeg om identieke opgaven. Interessant is dat Rothblatt (1993) suggereert dat ten tijde van de opkomst van examens in Engeland juist de onomstreden kennis bij uitstek het onderwerp van onderwijs en zeker van examineren werd geacht: die onomstreden kennis was in zekere al gestandaardiseerd.

94gif/94AlgToetsModel2CITO462295.GIF

voorspelbaarheid gegeven de ware beheersing

Als de leerling de eigen ware beheersing zou kennen, is de voorspelling van de toetsscore de binomiaalverdeling, de kansverdeling voor het uit de verzameling trekken van vragen die goed worden beantwoord. Dat is een eenvoudig model, maar toch zitten daar voldoende haken en ogen aan om er op zich een proefschrift over te kunnen schrijven (van den Brink, 1982). De figuur laat de binomiaalverdeling als voorspelling zien, gegeven dat de ware beheersing 80 % is, en de toets 20 vragen heeft. De voorspelling is niet zo fraai wat nauwkeurigheid betreft, dat komt omdat het aantal vragen klein is. De proeftoets komt hier niet in beeld, wie zijn ware beheersing kent heeft aan proeftoetsen geen behoefte.

94gif/94AlgToetsModel3CITO467283.GIF

voorspelbaarheid gegeven de proeftoets

In werkelijkheid kent niemand de eigen ware beheersing, en blijft er ook na een proeftoets onzekerheid over die eigen beheersing. Die onzekerheid is nu nauwkeurig af te beelden, omdat onder de veronderstellingen van het model een kansverdeling is te geven voor die ware beheersing. Bovenstaande figuur geeft de kansverdeling voor de ware beheersing, gegeven dat de informatie daarvoor bestaat uit een score van 8 goed op een proeftoets met 10 vragen. Die kansverdeling is de betaverdeling met parameters 8 en 2. Volgens dezelfde statistische regels waar ook de Bayesiaanse statistiek op is gebaseerd, is de voorspellende verdeling voor de score op de toets een beta-binomiaalverdeling met als parameters het aantal goed en fout op de proeftoets en het aantal items in de nog af te leggen toets. Deze verdeling staat ook bekend onder andere namen als de negatief hypergeometrische verdeling, of de Polya-verdeling. Hoe dat ook zij, de verdeling is te berekenen, en de plot is gegeven in de onderstaande figuur. Vergelijking met de voorspelling bij gegeven ware beheersing leert dat de onzekerheid over die eigen beheersing leidt tot een minder nauwkeurige voorspelling.

94gif/94AlgToetsModel4CITO485295.GIF

Terugkerend naar de stelling kunnen we constateren dat de docent zich niet zomaar op het standpunt kan stellen dat het bij het beoordelen er alleen maar om gaat een indicatie te krijgen van wat de leerling kan. Het laatste is de fictie dat het er in het geheel niet toe doet welke indruk de leerling over de eigen beheersing heeft. De leerling is onzeker over de eigen beheersing, en kan onder die onzekerheid komen tot verkeerde beslissingen over haar studiestrategie. De docent draagt ook verantwoordelijkheid voor die studiestrategie, en kan daar in zijn toetsontwerp rekening mee houden.

Ook onderzoekers zouden zich de stelling aan kunnen trekken. Het is een wonderlijk fenomeen te zien hoe ook sociaal-wetenschappelijke onderwijsonderzoekers in hun analyses met-of-zonder-LISREL vrijwel altijd leerlingen neerzetten als objecten, als wezens zonder eigen doelen, zonder eigen levensontwerp. De belangrijke uitzondering zijn de theoretici van het menselijk kapitaal, maar economen gaan dan ook veel makkelijker om met het begrip van de mens als een rationeel wezen, die met zijn keuzen welvaart en welzijn optimaliseert. Mensen streven doelen na. Leerlingen zijn mensen, ook leerlingen streven doelen na. Het is beter daar maar rekening mee te houden: de volgende stelling.

stelling 6: strategisch gedrag niet uitsluiten maar juist uitbuiten
Beoordelen gebeurt onder de fictie dat studenten zich niet strategisch erop voorbereiden; beter is bij het ontwerpen van beoordelingsituaties er vanuit te gaan dat studenten zich juist wèl strategisch voorbereiden.


De wereld van het onderwijs ritselt van de belangen: iedereen wil altijd wel wat met het onderwijs. Karel de Grote was bepaald niet de eerste die grootse plannen met het onderwijs had, en niet de laatste. Hij had dat onderwijs gewoon nodig, en bovendien in een vorm die nog nauwelijks bestond: om de mensen op te leiden die hij voor zijn eigen administratie nodig had. Magister Alcuin kreeg de opdracht om dat onderwijs vorm te geven. Nog steeds is het zo dat de overheid van alles wil met het onderwijs, daarbij vaak bepaald niet ondersteund door tal van belangengroepen die hun eigen doelen met het onderwijs nastreven. Het kan dus geen vreemd idee zijn dat ook leerlingen hun eigen doelen hebben, en zich daarnaar strategisch gedragen. Dat strategisch gedrag is vooral van belang op die momenten waar de greep van de school minder totalitair is: bij het huiswerk, dus bij de voorbereiding op toetsen en examens.

De conclusie moet zijn dat iedere onderwijstheorie, ook die over beoordelen, uitdrukkelijk plaats moet inruimen voor dergelijk strategisch gedrag, van zowel leerlingen als van andere direct betrokken partijen. Een theorie die dat niet doet gaat niet over de werkelijkheid van het onderwijs, en biedt geen van de actoren in dat onderwijs houvast voor het uitzetten van de eigen koers in dat onderwijs. In deze zin is de Geaccepteerde Theorie geen adequate onderwijstheorie.



voorbeelden van briljante uitzonderingen

de redelijke mens

Economen gaan makkelijk om met de doelen van bijvoorbeeld de handelaren op de markt. Voor hun modellen veronderstellen zij dat kopers en verkopers rationeel handelen, en dat is ook de reden waarom de tucht van de markt zo hard kan zijn. Maar in de speltheorie en de besliskunde staat het rationele handelen wel heel expliciet op de voorgrond. Ook psychologen hebben zich al vroeg de mogelijkheden gerealiseerd van theorievorming waarbij de eigen doelen van hun proefpersonen een nadrukkelijke eigen plaats kregen. Het proefschrift van Van Naerssen over de selectie van chauffeurs in het leger was hier een baanbrekende studie, die hem later bracht tot het opstellen van zijn tentamenmodellen. Er is binnen de psychologie een ijverige sectie die bestudeert hoe mensen in werkelijkheid kiezen, en hoe die keuzen soms opvallend ver verwijderd zijn van wat de econoom rationele keuzen zou noemen; de namen van Tversky en Kahneman zijn hier nauw mee verbonden.

de tucht van de campus

Drie sociologen hebben in 1960 een dieptestudie gedaan naar het campusleven aan een Amerikaanse universiteit (Kensington). Becker, Geer en Hughes hebben hun studie Making the grade gedoopt. Heel expliciet geven de auteurs aan hoe de student in zijn klas in feite over de cijfers (de grades) aan het onderhandelen is met de docent , en er voortdurend uit is te achterhalen wat er nodig is om een behoorlijk cijfer te kunnen scoren. Strategisch koersen dus, in een situatie waar studenten bijzonder in het onzekere worden gehouden over welke prestaties hoe beoordeeld zullen worden en hoe verschillende oordelen uiteindelijk in het cijfer voor het betreffende vak zullen resulteren. Voor deze studenten is de situatie buiten de klas heel belangrijk: hun activiteiten in organisaties en besturen, het lidmaatschap van een fraternity. Zij vinden dat deze activiteiten even belangrijke kwalificaties opleveren als hun cijfers. Maar er is een zeer nauwe band tussen academisch presteren en deze andere campus-activiteiten: meestal zijn er voorwaarden gesteld aan het grade-point-average om lid te kunnen worden van bepaalde fraternities en besturen, die op hun beurt een pittg beslag op de tijd van de student leggen. Deze studenten zijn dus voortdurend bezig hun beperkte tijd zo goed mogelijk te verdelen over de studie en de activiteiten daarbuiten. Deze wereld is door en door competitief.

een formeel model

Het campusleven zoals door Becker c.s. beschreven is een complex sociaal systeem. Met de typische lineaire sociaal-wetenschappelijke modellen is een dergelijke competitieve wereld niet adequaat te beschrijven. Coleman (1990) heeft aan de micro-economie ontleende modellen verder ontwikkeld om sociale situaties als deze te kunnen beschrijven.

Het gaat nu om het doelgerichte handelen zoals dat in de theorie van Coleman en in de besliskunde een plaats heeft gekregen. Het is mogelijk om bijvoorbeeld de doelen van de leerling te operationeliseren zodat er in het algemene toetsmodel mee is te rekenen. Het gaat erom een operationalisatie te vinden voor het nut dat de leerling toekent aan cijfers van verschillende hoogte, en aan de tijd die zij aan de studie en aan andere activiteiten wil besteden.



welk nut: van het toetsresultaat, maar ook van alternatieve tijdbesteding

De studie van Becker et. al. laat zien dat de campusstudent de opbrengst van hun productieve tijd maximaliseert, en dat daaruit een bepaalde verdeling volgt van tijd dvoor de studie en tijd voor buitenschoolse activiteiten.

94gif/94AlgToetsModel5CITO462283.GIF

nut van het toetsresultaat

Een nutsfunctie over toetsresultaten is een persoonlijke functie; van leerling tot leerling kan die dan ook verschillen. In deze versie van het algemene toetsmodel is de beta-functie gebruikt, dat is een functie die heel plooibaar is, al naar gelang de keuze van de waarden voor de parameters, en die in zijn wiskundige eigenschappen handig past bij de beta-binomiaalverdeling.

De nutsfunctie gaat over scores op de toets, omdat in het onderwijs die scores tellen, niet de ware stofbeheersing. (noot 9) Er is echter niets op tegen om de nutsfunctie over de ware beheersing te specificeren. (noot 10) De bovenstaande figuur geeft een voorbeeld van een tamelijk steile nutsfunctie, een leerling voor wie eigenlijk alleen een score van tenminste 15 telt, en die het niet nodig vindt hoger dan die 15 uit te komen. Er zijn tal van varianten mogelijk in steilheid en plaats van de nutsfunctie, al naar gelang de voorkeuren van de leerling, en de aard van de beoordelingssituatie. Er zijn twee uitersten: de drempel-functie wanneer alleen slagen of zakken telt, en de lineaire functie wanneer ieder punt extra evenveel waard is als ieder ander punt extra. Geen van beide extremen komt in de praktijk voor. De drempelfunctie komt niet voor omdat er altijd wel enige compensatie van punten of cijfers is, of een maatschappelijk gevolg van hogere cijfers dan anderen. De lineaire functie komt niet voor omdat bijvoorbeeld aan het eind van het schooljaar er eisen zijn aan het gehaalde cijfergemiddelde.

Het nut is geschaald van 0 tot 1 omdat dat makkelijk is. (noot 11)


verwacht nut van het toetsresultaat

Met nutsfunctie en voorspelling van de toetsscore is het mogelijk het verwachte nut voor de komende toetsgelegenheid te berekenen. Dat verwachte nut krijgt pas betekenis wanneer het is te vergelijken met het verwachte nut van een alternatief, bijvoorbeeld na een extra week studeren. Daarom moet het nu nog statische toetsmodel dynamisch worden gemaakt door er de kosten van tijd en een leermodel in op te nemen.

94AlgToetsModel6CITO423288.GIF

een leermodel

Om tijd in het model te kunnen zetten, is natuurlijk een leermodel nodig waarmee tot uitdrukking komt dat uiteindelijk extra uren studie steeds minder extra stofbeheersing opleveren. Daar mag ieder aannemelijk model voor gekozen worden, ik gebruik in de nu volgende voorbeelden het halfwaardemodel. In dit leermodel kost het telkens evenveel tijd om de helft van de nog niet beheerste te stof onder de knie te krijgen. Is er een week nodig om tot een beheersing van 50 % te komen, dan is na nog een week de beheersing 75 %, en zo verder. De boenstaande figuur laat een plot van deze curve zien.

94gif/94AlgToetsModel7CITO519288.GIF

het nut van leren

Het is nu mogelijk om het leermodel te gebruiken om het verwachte nut te berekenen over de tijdschaal die is gedefinieerd door de halfwaardetijd. De bovenstaande figuur laat het resultaat zien. Natuurlijk is het zo dat na een bepaad moment de toename in het verwachte nut vermindert. Deze plot bevat op zich dus geen verrassing. Er is echter nog geen rekening gehouden met het nut van de te besteden tijd.
Deze wijze van afbeelden van de ontwikkeling in het verwachte nut na extra bestde tijd kent enige complicaties. Zo is het gebruikte leermodel deterministisch, terwijl voor de leerling ook de verwachte leerwinst na extra inspanning een onzekere factor blijft. Het leermodel is conservatief, omdat het deze onzekerheden verbergt. Een onzekerheid is bovendien dat de leerling niet weet op welk punt van de leercurve zij zich bevindt, anders dan dat af te leiden uit het resultaat op een proeftoets. Er moeten op dit punt in de toekomst ongetwijfeld betere mogelijkheden tot modelleren te vinden zijn. Voorlopig zijn deze plots heel goed bruikbaar, omdat het vooral gaat om het vergelijken van de posities vn de leerling bij de proeftoets met die welke na enige extra studie is te bereiken.

het nut van tijd

In de vroege middeleeuwen was tijd onbeperkt, en toch had niemand er echt de beschikking over: tijd behoorde aan de Heer. De heilige Benedictus stelde kloosterregels op waarin het leven strikt naar tijd werd ingedeeld, naar wat wij vandaag klokketijd zouden noemen, maar dat gebeurde niet vanuit het idee dat tijd kostbaar is. Het idee dat tijd waarde heeft is meer een geleidelijk bij kooplieden ontstaan inzicht, waarover ook de nodige problemen met de kerk zijn ontstaan. In het middeleeuwse onderwijs speelde tijd een rol zoals in het klooster: als middel om de dagelijkse bezigheden van iedereen op elkaar af te stemmen. Het is verleidelijk om de stelling te poneren dat deze vorm van tijdrekening nog steeds wordt gehanteerd door het schoolmanagement, terwijl de leerlingen meer als kooplieden met hun eigen tijd omgaan, afwegend aan welke bezigheden zij met het meeste profijt hun tijd kunnen besteden. Een botsing van een middeleeuwse met een hedendaagse wereld.

Een algemeen toetsmodel moet dus rekening houden met de tijdsinvestering van de leerling, vanuit het besef dat de leerling tijd ook in andere bezigheden kan investeren dan in de studie. Dit is dus een heel andere benadering dan de 'time on task' theorie van Carroll en vele anderen met hem, waarin tijd behandeld wordt alsof deze niets kost.

Eigenlijk moet in het model worden opgenomen wat het nut is van de dingen die de leerling anders zou kunnen doen. Om de daaruit volgende complicaties te vermijden, is gekozen voor de fictie dat de tijd zelf nut heeft. Het aan tijd toegekende nut verwijst naar het nut dat bij een alternatieve tijdbesteding wordt geboekt.

een conservatieve aanname: tijd heeft constant nut

Een eenvoudige manier om tijd in het model te brengen is door te veronderstellen dat iedere eenheid bestede tijd, of dat nu in minuten of in jaren wordt gemeten, evenveel 'alternatief nut' op kan leveren wanneer deze niet aan de studie wordt besteed. Dit is een conservatieve veronderstelling die ten gunste van het onderwijs uitpakt. In het algemeen moet immers gelden dat naarmate meer tijd aan de studie besteed is, de tijd die nog over is kostbaarder is omdat er gewoon minder van is. Het is niet altijd nodig om echt te weten hoe hoog dat het nut van tijd is, omdat modelstudies gewoon voor een aantal varianten gedaan kunnen worden.

94gif/94AlgToetsModel8CITO483223.GIF

Figuur 1. De voor de af te leggen toets verwachte utilitet E(u) als functie van de tijdsbesteding t (links); De E(u) na aftrek van de kosten van tijdsbesteding (Rechts), eveneens als functie van t. Het leermodel is t = 1 - 0,5t. De toets bestaat uit 100 vragen, de proeftoets waarop de voorspellingen zijn gebaseerd bestaat eveneens uit 100 vragen. De utiliteitsfunctie over toetsscores is de drempelfunctie 70 %.

het volledige toetsmodel

Alle ingrediënten voor het algemene toetsmodel zijn nu beschikbaar. De bovenstaande figuur laat zien wat de gevolgen voor het verwachte nut zijn wanneer tijd niet meer gratis is, maar in rekening wordt gebracht. De bovenste curve het verwachte nut is de situatie dat de docent denkt dat het tijdsbudget van zijn leerlingen onbeperkt is. Wie meent dat tijd gratis is, verwacht maximale studieprestaties omdat extra tijd altijd extra nut oplevert, hoe weinig misschien ook. En altijd zal er de teleurstelling zijn dat leerlingen aan die verwachting niet voldoen, misschien omdat ze over onvoldoende wilskracht beschikken.

Natuurlijk is tijd niet gratis, de figuur laat een plot zien van vijf varianten op de kosten van tijd.

Ook zonder enige empirische gegevens laten deze grafieken zien dat het zeer onredelijk zou zijn van leerlingen inspanningen te verwachten die ze zouden brengen tot de sub-optimale resulaten rechts van het optimum. Ergo, dit algemene toetsmodel moet een fundamentele bouwsteen kunnen zijn voor een ontwerptechnologie die rationele evaluatie en bijsturing van de belangrijkste parameters van onderwijs en toetsing mogelijk maakt. De doorzichtigheid van de toets en toetssituatie is zeker zo"n belangrijke parameter, en die hangt onder andere af van het aantal vragen in de toets. Onderstaande figuur laat een variant zien waarin de proeftoets 100 vragen en de toets 200 vragen bevat. Het groter maken van de steekproef maakt de toets doorzichtiger, en stelt de leerling in staat een doelmatiger studiestrategie te hanteren. Dat wisten we ook zonder dit model al, het model maakt dit inzicht precies, en beter hanteerbaar bij het ontwerpen van het onderwijs en zijn toetsingsmomenten.

Het model is niet alleen 'algemeen' genoemd omdat het vrij is van de culturele lading van individuele verschillen, maar ook omdat het een bouwsteen is voor modellen die op specifieke situaties zijn gericht, zoals tentamenmodellen, en modellen voor cesuurbepaling. De software-implementatie maakt het mogelijk tal van varianten door te laten rekenen, om er op deze manier achter te komen welke mogelijke invloed bepaalde veranderingen in onderwijs en beoordeling zouden kunnen hebben op de studiestrategieën van de leerlingen. Natuurlijk kunnen dergelijke sensitiviteitsstudies niet alleen richtinggevend zijn bij het onderwijsbeleid, daar is ook de controle tegen de empirische gegevsn voor nodig, maar ze kunnen aanwijzingen opleveren voor de richting waarin belangrijke verbeteringen te realiseren zijn. En deze analyses zijn buitengewoon goedkoop: het berekenen en plotten is een kwestie van minuten.

Een caveat tot slot. Het model veronderstelt niet dat de normen voor de toets tevoren zijn vastgelegd. Het is duidelijk dat voortgaan op de weg van het relatief beoordelen, het gehoorzamen aan de Wet van Posthumus, bij voorbaat de opbrengst van verbeteringen in de onderwijssituatie teniet dreigt te doen, ook vele van de verbeteringen die zijn in te boeken wanneer de leerling als volwassen partij bij de beoordeling wordt behandeld.





LITERATUUR

Voor geschiedenis van het onderwijs bieden internationale tijdschriften op dat gebied een goede ingang tot de literatuur. Voor de specifieke bronnen van het in dit paper besprokene verwijs ik naar het te verschijnen proefschrift. Zie voor algemene literatuur vooral ook:

Noordman, J. (Red.) (1978). Literatuurwijzer historische pedagogiek. Nijmegen: SUN.

Het algemene toetsmodel is opgebouwd uit materiaal in:


De toepassing van Coleman's theorie voor sociale systemen is elders al gepresenteerd:



Andere besproken thema's zijn te vinden in:



Francis Y. Edgeworth (1888). The statistics of examinations. Journal of the Royal Statistical Society, 51, 599-635. (profile)

M. Bar-Hillel and R. Falk (1982). Some teasers concerning conditional probabilities. Cognition, 11, 109-122.

P. J. van Herwerden (1947). Gedenkboek van het stedelijk gymnasium te Groningen. Groningen: Wolters.



94gif/94AlgToetsModel9CITO192189.GIF

Figuur 1. Een toetsvragenverzameling van 8000 vragen (hier als blokjes), denkbeeldig allemaal beantwoord door de leerling met een score van 75 % goed (wit).



noten

  1. Ik ben spaarzaam met literatuurverwijzingen, vooral in de historische gedeelten waarover niet eerder is gepubliceerd; ik vraag daar begrip voor. Het gaat in dit paper om voorlopige resultaten, met alle voorbehoud van dien. Wie eruit wil citeren kan met mij contact opnemen.

  2. A. D. de Groot (1970). Some badly needed non-statistical concepts in applied psychometrics. Nederlands Tijdschrift voor de Psychologie, 25, 360-376.

  3. Berlak e.a. (1992). Towards a new science of educational testing & assessment. SUNY.

  4. Deze procedures zijn soms buitengewoon ingewikkeld, maar ook dan is hun doelmatigheid onderzoekbaar te maken, zie mijn Complexe selectieprocedures simuleren op de computer. Amsterdam: SCO, 1990. (rapport 246)

  5. Hofstee, W. K. B. (1981). Psychologische uitspraken over personen. Deventer: Van Loghum Slaterus.

  6. Kula, W. (1986). Measures and men. Princeton.

  7. Voor de wiskundige verantwoording zie o.a. Studiestratgieën (1978) en de dissertatie. De 'black box' krijgt overigens de vorm van een computerprogramma dat in beginsel voor gebruikers beschikbaar komt, hetzelfde programma waarmee de nog volgende grafische figuren zijn berekend en geproduceerd.

  8. Becker, H., Geer, B., & Hughes, E.C. (1968). Making the grade: the academic side of college life. Wiley. "(...) faculty fail to give sufficient weight to the pull of other interests. They do not see, for instance, that the student may not be able to afford any more interest in their course because he needs to devote time and effort to another course that is giving him more trouble. They see even less that the student feels he may not be able to afford any further interest because he thinks that other rewards available in organizational activity and personal relationships are equally important and that academic rewards must be balanced against that competition. They do not understand, in short, that from the student's point of view true maturity consists in striking that balance in a reasonable way. It is probably incorrect to say, as we just have, that faculty do not know these things. We could put it more precisely by saying that what they do not see is the legitimacy students accord to this competition to the interest their course should generate, the legitimacy that arises from its grounding in the students' view of maturity. In this sense, they do not understand that able students do not feel free to strike the kind of bargain faculty members propose, for to do so would be immature and unbalanced. It is likely that students willing to make such a bargain are, from the student point of view, unbalanced, for they would be students who had no other interests, who were insensitive to the attraction of other worthwhile activities possible on campus. (...) Even though we are primarily concerned with students' definitions of the classroom situation, we rely, in the analysis that follows, largely on our own observations of classroom interaction (..): students do not typically describe the situation but rather talk about their difficulties with it. Thus students do not often express the notion that the classroom is a place where grades are exchanged for academic performance. But they do talk about the difficulties involved in holding up their end of the exchange, in a way that implies that definition of the situation."

  9. De Geaccepteerde Theorie kan niet goed omgaan met ruwe scores, en zeker niet met cijfers. In de afdeling criterium-gerefereerd toetsen wordt wel met ruwe scores gewerkt, en dat kan leiden tot complicaties die de onderzoekers op het verkeerde been zetten: het oordeel van Van der Linden (APM, 1980) dat deze technieken niet meer dan veredelde betrouwbaarheidstechnieken zijn, berust op een vereenvoudigingsomissie in een formule waarin zowel ware als ruwe scores figureren (zie mijn 'Toetsen en testen in het onderwijs').

  10. Dat heeft wel gevolgen voor de berekening van het verwachte nut: de score op de toets moet dan immers als indicatie voor de ware beheersing worden genomen, op die basis kan een verdeling voor de ware beheersing worden gespecificeerd, en deze worden gewogen met de nutsfunctie.

  11. Deze nutsfuncties zijn natuurlijk niet willekeurig; in beginsel kunnen ze op empirische wijze worden bepaald voor iedere concrete leerling, bijvoorbeeld met de techniek van het aanbieden van loterijen tussen bepaalde zekere en onzekere uitkomsten, zie de literatuur. Deze wijze van specificeren van nutsfuncties is gebruikelijk in de economische besliskunde (Keeney & Raiffa, 1968). In situaties waarin tijd verdeeld wordt over alternatieve bestedingen, zou het handiger zijn de Cobb-Douglas-functies uit de economie te hanteren, zoals bijv. Coleman doet.


Het Pascal programma zoals in 1994 in de voorbereiding van het CITO-colloquium gebruikt is beschikbaar als tekstfile (hierbeneden). Hier ook een eenvoudige uitleg van de manipuleerbare parameters en de mogelijkheden om de vorm van de grafische uitvoer te bepalen.

latere publicaties



Wilbrink, B. (1997). Assessment in historical perspective. Studies in Educational Evaluation, 23, 31-48. html



Ben Wilbrink (1998). Inzicht doorzichtig toetsen. html In Theo H. Joostens en Gerard W. H. Heijnen (Red.). Beoordelen, toetsen en studeergedrag. Groningen: Rijksuniversiteit, GION - Afdeling COWOG Centrum voor Onderzoek en Ontwikkeling van Hoger Onderwijs, 13-29.


Project: Het Algemene Toetsmodel in 2004.






PROGRAMMATUUR ALGEMEEN TOETSMODEL BEN WILBRINK




Op deze schijf vind je drie versies van het programma Algemeen ToetsModel (ATM), in de werk-versie zoals gebruikt voor het colloquium op 23 juni 1994. Ik heb alleen het interface gebruiksvriendelijker gemaakt, hopelijk ben ik daar een beetje in geslaagd. 


Applicatie ATM BenW 68000

ATM BenW 68000 is bedoeld voor Macintosh computers met een 68000 chip, zeg maar de klassieke modellen. Het is dan ook een verschrikkelijk traag programma, en hoewel je het kunt gebruiken op een snellere Macintosh, is dat niet aan te raden.


Applicatie ATM BenW 68030

ATM BenW 68030 is bedoeld voor Macintosh computers met een 68020 of 68030 chip, zonder mathematische coprocessor.


Applicatie ATM BenW math copr

ATM BenW math copr is bedoeld voor Macintosh computers met een 68030 chip met een mathematische coprocessor. Als het even kan moet je deze versie dus gebruiken.


Ik verwacht dat eventuele problemen met het draaien van deze programmatuur alleen leiden tot het zachtjes sneuvelen van de applicatie, die vervolgens gewoon opnieuw opgestart kan worden. Voor de zekerheid raad ik aan eventuele openstaande documenten in andere applicaties eerst te bewaren.

Het gaat hier om een  ruwe werk-versie van het programma waarin een aantal dingen nog niet werken of niet naar behoren werken. Wat in ieder geval niet werkt: een kopie van de geplotte curven maken; je kunt ze wel printen. Eventueel kun je met de utility ‘Flash it’ het hele outputwindow kopiëren en in het clipboard of het scrapbook laten zetten. Een kopie van Flash it heb ik op de schijf gezet. Wat in het clipboard of het scrapbook staat kun je vervolgens kopiëren naar een tekstfile waar je de plaatjes in op wilt nemen. Het kan zijn dat er met kleur problemen ontstaan bij printen e.d.; mocht dat zo zijn, stel de monitor dan op zwart-wit in, althans niet op kleur, met een beetje geluk gaat alles dan goed. 


STARTEN

Starten doe je op de gwone manier, in de Macintosh-omgeving betekent dat: dubbel-klikken op het icoon van de applicatie die je wilt gebruiken, of via het archief-menu ‘open’ kiezen.

Vervolgens gebeurt er heel weinig, dat komt omdat je onder het archief menu ‘kies parameters’ moet kiezen (of Command-K). Dan verschijnt er een window waarin van alles en nog wat valt in te stellen en te kiezen.


KIES AANTAL PIXELS VERTICAAL

Voor de hoogte van de te plotten figuren geef je het aantal pixels op. Standaard heb ik 240 pixels ingesteld, om de figuren op het scherm goed te kunnen bekijken. Voor een print op A-4 formaat is 440 heel geschikt.


KIES AANTAL PIXELS HORIZONTAAL

Door het aantal pixels te verminderen, wordt de af te beelden figuur smaller gemaakt. Omdat voor verwachte utiliteit de berekeningen afhankelijk zijn van de opgegeven breedte, leidt een halvering van de breedte daarvoor ook tot een halvering van de benodigde rekentijd.


KIES AANTAL ITEMS IN TOETS EN PROEFTOETS

Het aantal items in de toets en in de proeftoets is vrij te kiezen, het maximum is 1000. Het aantal items in de toets bepaalt de rekentijd nodig voor het berekenen van de verwachte utiliteit: afhankelijk van de snelheid van je Macintosh zul je voor 1000 items zo‘n vijf tot tien minuten moeten wachten (met mathematische coprocessor). Het programma is erg reken-intensief omdat ingewikkelde discrete waarschijnlijkheidsverdelingen worden geëvalueerd. 
Kies het aantal items in de toets 1) in overeenstemming met het werkelijke aantal in een specifieke toets (zeg, centraal schriftelijk Engels), of 2) naar een ophoging of vermindering van dat aantal, omdat je daarvan juist de mogelijke consequenties wilt onderzoeken. Je kunt onderzoeken wat er gebeurt wanneer het aantal toetsvragen heel groot wordt gemaakt, maar dat levert aanzienlijke rekentijden op.
Kies het aantal items in de proeftoets in overeenstemming met wat je denkt dat de sterkte van de informatie is die de leerling op een bepaald moment heeft over de eigen beheersing. Een vuistregel is misschien dat dit aantal items niet meer dan een factor twee of drie verschilt van het aantal in de toets te gebruiken items, maar niets weerhoudt je om meer extreme situaties te onderzoeken.


KIES AANTAL GOED IN DE PROEFTOETS

Het aantal goed op de proeftoets bepaalt de meest waarschijnlijke verdeling voor de op de vragenverzameling gedefinieerde stofbeheersing. Als je de bijbehorende verdeling wilt zien, kies dan uit de PLOT-mogelijkheden alleen de ‘betaverdeling beheersing’ door het hokje ervoor aan te klikken, en OK te klikken (of een enter te geven). 
Je onderzoekt altijd de beslissingssituatie waar een individuele leerling voor staat, waarbij het aantal goed op een proeftoets met een gegeven aantal items de de informatie representeert die de leerling over e eigen beheersing heeft. Het relatieve aantal goed geeft natuurlijk een indicatie voor de beheersing, het aantal items in de proeftoets correspondeert met de sterkte of de nauwkeurigheid van die indicatie.


NIET GEïMPLEMENTEERD

Wat hier ‘niet geïmplementeerd’ is, is de mogelijkheid om in plaats van aantal goed en aantal proeftoetsitems meteen de parameters a en b voor de proeftoetsscoreverdeling op te geven. Je mist daar dus niets aan.


KIES PARAMETERS VOOR DE UTILITEITSFUNCTIE

Voor de utiliteitsfunctie, een cumulatieve beta-functie, moeten twee parameters worden opgegeven. De bijbehorende functie kun je laten plotten door uit de PLOT-mogelijkheden ‘utiliteit over scores’ te kiezen door het hokje ervoor aan te klikken, en OK te klikken (of een enter te geven).. . In plaats van een hoop uitleg kun je het beste een aantal verschillende waarden proberen. Standaard heb ik 70 en 30 gekozen; probeer ook eens de veel vlakkere functie met 3 en 7, en de dichter bij drempelverlies komende 300 en 700. 














DREMPELVERLIES

De mogelijkheid om in plaats van een beta-functie als utiliteitsfunctie een drepelfunctie op te geven heb ik nog niet geïmplementeerd. Dat is overigens geen beperking, omdat het altijd mogelijk is de beta-functie zo te kiezen dat in feite de gewenste drempelfunctie ontstaat. Kies bijv. a=300 en b=700, dan heb je een drempel die voor kleinere toetsen al goed genoeg is. Zonodig kies je nog grotere waarden, bijv. 4000 en 6000. Je kunt altijd de utiliteit even plotten om te zien hoe je uitkomt, en zonodig verander je de opgave voor de parameters in de richting die je uit wilt. 
Vertrouw niet op wat je denkt dat de vorm van de opgegeven utiliteitsfunctie is, maak altijd een plot. Omdat de utiliteit gaat over scores, en dus een discrete verdeling is, kunnen er onverwachte sprongen in zitten, zeker wanneer de betaverdeling wordt gebruikt om drempelverlies te benaderen.


KIES DE TE PLOTTEN VERDELING

Er zijn zeven mogelijkheden voor te kiezen verdelingen of curven. Voorzover de aanduidingen niet voor zich spreken, wat ik me heel goed voor kan stellen: gewoon laten plotten en de bijbehorende tekst lezen, of de figuren in het colloquium-paper van 23 juni er bij nemen.
Er kunnen meerdere verdelingen tegelijk (over elkaar heen) worden geplot door meerdere tegelijk aan te klikken. Dat levert niet altijd even zinvolle plots op, maar ik zou moeite moeten doen om in het programma bepaalde combinaties te verbieden. Het over elkaar heen printen van schaalwaarden en verschillende rasters is te vermijden door die opties uit te schakelen. 
Ik wil nog proberen in bepaalde gevallen figuren NAAST elkaar te laten printen, als me dat niet te veel hoofdbrekens kost. 
Wat in deze versie jammer genoeg niet kan, is enkele varianten van dezelde curve in dezelfde figuur plotten (next best thing: plot op overheadsheets die je over elkaar kunt leggen).


SCHAALWAARDEN EN RASTER

Spreekt voor zich; standaard worden zowel schaalwaarden als raster getoond. Voor de betaverdeling voor beheersing worden geen vertical schaalwaarden geprint, omdat deze verdelingen genormeerd zijn op een oppervlakte = 1. Het voordeel is dat verschillende betaverdelingen dan direct vergelijkbaar zijn met elkaar, wat voor de voorspellende toetsscoreverdelingen niet het geval is (althans, in deze versie).
Een raster is soms vervelend, dat kun je dan uitschakelen. Bij het over elkaar heen printen van meer dan twee figuren kun je ervoor kiezen de soms conflicterende schaalwaarden helemaal weg te laten. 


RAADKANS KENNISPLAFOND VOORKENNIS

Deze parameters zijn continu in te stellen van 0 tot 100 procent. Het effect blijkt het duidelijkst door de leercurve te laten plotten.
Het gaat echter om verfijningen in het model, dus gebruik je deze opties pas wanneer je een meer levensechte afbeelding voor een bestaande situatie wilt maken waarin deze zaken een rol spelen.


RAADKANS

De raadkans is een parameter die in het Algemene Toetsmodel in essentie geen eigen leven leidt. Het binomiale model voor trekken uit de verzameling specificeert alleen de kans dat een willekeurig getrokken item behoort tot de groep items die ‘goed’ wordt beantwoord door de betreffende leerling. Of dat ‘goed’ is gebaseerd op raden of op weten, doet niet terzake. Anders gezegd: ‘beheersing’ is gedefinieerd INCLUSIEF eventuele raadkansen. De mogelijkheid om een raadkans te specificeren heb ik ingebouwd om het model realistisch te houden ook voor de situatie waarin keuzetoetsen gemodelleerd worden. Raadpleeg de tekst uit 1991 voor de wijze waarop die raadkans in het model is opgenomen; het komt erop neer dat met een raadkans van 25 % er in feite een soort ‘voorkennis’ van 25 % is (zie hierbeneden bij voorkennis). Is er tegelijk ook een kennisplafond lager dan 100 %, houd er dan rekening mee dat de raadkans proportioneel is ten opzichte van het kennisplafond (zie hierbeneden).


KENNISPLAFOND

In werkelijkheid is 100 % beheersing een ideaal dat lang niet altijd bereikbaar is, en in ieder geval meestal niet door iedereen bereikbaar is. Omdat de utiliteitsfunctie over scores wel doorloopt tot de maximale score, is het niet onbelangrijk een correctie op het ‘ideale’ plafond van 100 % aan te brengen.
Voor de leerfunctie heeft een eventueel plafond een over de hele tijdas neerdrukkend effect. NB: raadkans en voorkennis zijn in het model gedefinieerd op het leermodel, dus bij een plafond van 80% en een raadkans van 40% begint de leerling niet met een verwachte score van 40% op de toets, maar van 40% van 80% ofwel 32%. Om hetzelfde effect te bereiken als een raadkans van 40% bij een kennisplafond van 100% kun je 50% kiezen.


VOORKENNIS

In werkelijkheid beginnen leerlingen zelden op een niveau waarop ze geen enkele vraag uit de verzameling goed zouden kunnen beantwoorden (vergelijk de voortgansgtoets bij Geneeskunde in Maastracht). Sommige leerlingen beginnen misschien al op een relatief hoog niveau van relevante voorkennis. Omdat de voorkennis in theorie en in praktijk best zo hoog kan zijn dat het voor de leerling niet loont nog verder te investeren in de voorbereiding op de toets, is het belangrijk deze parameter in het model mee te nemen.


TIJDSCHAAL

Standaard staat de tijdschaal op 5 ingesteld, dat is een tamelijk hoge waarde: vijf keer de studietijd nodig om de helft te leren van wat nog niet wordt beheerst. Dat kan dus korter worden gekozen, of langer, al naar gelang wat wenselijk is.
Denk er altijd aan dat deze parameter genormaliseerd is: individuele verschillen in leersnelheid tussen leerlingen worden als het ware gewoon gelijkgetrokken. Dat is in zekere zin erg jammer, omdat de ene leerling makkelijk twee, vier of tien keer zo snel kan leren als een andere eerling in dezelfde klas of groep. Maar deze verschillen kun je op een andere manier wel teruglezen uit de geplotte curven: voor de langzamere leerling zijn ceteris paribus de kosten van tijd HOGER dan ze voor de snellere leerling zijn. De langzamere leerling zit dus in termen van de verwachte utiliteit MIN de kosten van de tijd (de plot met zes curven onder elkaar) op een LAGERE curve. Ook zullen snellere en langzamere leerlingen verschillen van elkaar in de utiliteit die zij aan hogere en lagere scores toekennen.


VERWACHTE UTILITEIT MIN KOSTEN TIJD

Een bijzonderheid van de plot van deze set curven is dat de curven worden geplot vanaf het leertijdstip dat overeenkomt met de verwachte waarde van de beheersing, gegeven de proeftoetsscore. Om de curven vanaf een vroeger punt te kunnen plotten, kan een heel lage proeftoetsscore worden opgegeven (het model zit wiskundig zo in elkaar dat de plot van de verwachte utiliteit over het aangegeven bereik van extra tijdbesteding gelijk blijft aan wat bij opgave van een hogere proeftoetsscore wordt verkregen).


PLOT BEWAREN VOOR OVERLAY

De mogelijkheid om over de plot voor een bepaalde curve heen nog een plot van dezelfde curve te maken maar met andere waarden voor de parameter(s) is nog niet behoorlijk uitgewerkt. Wat redelijk gaat is bijvoorbeeld de waarschijnlijkheidsverdeling voor de beheersing printen bij twee verschillende proeftoetsresultaten. 
Dat gaat als volgt:

1. stel gewenste parameters in, bijv. 8 goed uit 10; klik ‘betaverdeling beheersing’ aan; klik ‘plot bewaren voor overlay’ aan, klik OK. ER VERSCHIJNT GEEN PLOT, dus wacht daar niet op.

2. Kies opnieuw parameters. Verander 8 goed uit 10 in bijvoorbeeld 5 goed uit 10; klik ‘plot bewaren voor overlay’ uit, klik OK. Dit plot zowel de betaverdeling behorend bij 8 uit 10 goed als die behorend bij 5 uit 10 goed. 












Bij het printen van twee voorspellende toetsscoreverdelingen (de binomiaal of betabinomiaal) zijn er problemen met de verticale schaal, waardoor verdelingen niet goed vergelijkbaar zijn. Deze optie nog maar even voorzichtig gebruiken dus. 
De optie is juist weer heel goed bruikbaar bij het plotten van verwachte utiliteit, want daardoor is meteen een heldere vergelijking te maken van de uitkomsten onder de twee verschillende opgegeven sets van condities.


ALLES NAAR WENS INGESTELD? KLIK DAN ‘OK’

Al naar gelang de gegeven opdracht is er merkbare of soms zelfs lange rekentijd nodig. Uiteindelijk verschijnt er een plot op het scherm, tenzij je vergeten hebt een keuze te maken uit de mogelijk te plotten curven. 
De figuur kan wel worden geprint, maar niet gecopiëerd of op andere manier bewaard. Dat is niet omdat ik dit programma als een demo beschouw, met expres beperkte mogelijkheden, maar omdat ik er nog niet toe ben gekomen dit te implementeren.













ENKELE  SUGGESTIES  VOOR  GEBRUIK

1) De verwachte utiliteit min kosten van tijdbesteding is het belangrijkste resultaat van het Algemeen Toetsmodel. Dat laat nog open op welke manieren je van dat resultaat gebruik kunt maken, dus hoe je het interpreteert in het kader van de toetssituatie die je hebt afgebeeld. De verwachte utiliteit wordt horizontaal van links naar rechts pas geplot vanaf het beheersingspunt waarop de leerling zich waarschijnlijk bevindt op het moment van de proeftoets. De interpretatie is eenvoudig: bij stijgende verwachte utiliteit zal de leerling doorgaan met studeren (althans volgens dit normatieve model) totdat het optimum bereikt is. Misschien wil de leerling tussentijds nog eens een proeftoets afleggen om de voortgang te checken. Welke van de zes geplotte curven moet je daarvoor uitkiezen? In ieder geval niet de bovenste curve want die geldt voor de fictieve situatie dat tijd niets kost. Voor een ‘langzame’ leerling zijn de kosten hoger dan voor een snellere leerling. Maar de kosten kunnen ook specifiek voor de leersituatie zijn, bijv. naarmate de toets een groter onderdeel van het curriculum bestrijkt, worden de kosten van de tijdbesteding groter (meer stof opgeven verhoogt de kosten voor de leerling). Naarmate het resultaat op de toets belangrijker is (bij gelijkblijvende omvang van de leerstof) nemen de relatieve kosten van de tijdbesteding af. Etcetera.
De horizontale schaal is gegeven in tijdseenheden, die kunnen natuurlijk ook worden vervangen door beheersing: 1 = beheersing 0,5; 2 = 0,75; 3 = 0,875 etc., alles onder de aanname dat er geen raadkans, kennisplafond < 100%, of voorkennis is.

2) Doorzichtigheid, in de specifieke betekis van voorbereidbaarheid. Het Algemene Toetsmodel zoals in dit programma geïmplementeerd, gaat uit van perfecte voorbereidbaarheid. Immers, de proeftoets geeft informatie over de mate van beheersing, het gespecificeerde leermodel geeft aan dat de leerling altijd in staat is door verdere inspanning de beheersing te verhogen. 
Een gebrek aan voorbereidbaarheid kan op verschillende manieren toch in het model worden afgebeeld. 
- wanneer de halfwaardetijd door gebrekkige voorbereidbaarheid langer is dan nodig, waardoor de kosten van tijdbesteding relatief hoger zijn, en de optimale voorbereiding een lagere beheersing oplevert. 
- wanneer door gebrekkige voorbereidbaarheid het kennisplafond lager komt te liggen dan nodig.

3) Doorzichtigheid in de specifieke betekenis van voorspelbaarheid. Dit is een thema waarbij enige voorzichtigheid is geboden. Voorspelbaarheid heeft te maken met de kwaliteit van de toets als steekproef, dus met het aantal vragen in de toets. Nu heeft het aantal vragen op de toets geen invloed op de voorspelbaarheid in de betekenis van op de toets verwachte score, want die is niet gevoelig voor het aantal toetsvragen (behalve de afwijkingen die ontstaan door het discrete karakter). Wat wel van belang is, is de spreiding. Maar die spreiding in de voorspellende toetsscoreverdeling wordt pas van belang in de weging met de utiliteitsfunctie. Dat is misschien het best in te zien bij drempelverlies: voorspelbaarheid is dan de kans een score te behalen die ‘voldoende’ is, gegeven je proeftoetsresultaat en eventueel nog een tijdje doorstuderen. 
Doorzichtigheid in de specifieke betekenis van voorspelbaarheid is op zich dus niet interessant, want die voorspelbaarheid is er altijd. Wat De Groot in werkelijkheid bedoelt is dat de leerling in staat moet worden gesteld het risico om te zakken (in de situatie van drempelverlies) te beheersen, te reduceren tot een voor hem of haar aanvaardbaar niveau. Het hangt dus van de toetssituatie in zijn geheel af of in deze opvatting de doorzichtigheid voor een bepaalde leerling, of groep leerlingen, voldoende is. In deze zin is een toets met een heel klein aantal vragen dus al gauw ondoorzichtig, omdat de leerling in de situatie komt dat de kans om te slagen in de orde van grootte van de stofbeheersing komt te liggen (bij een toets met 1 vraag, cesuur = 1, is de slaagkans gelijk aan de stofbeheersing). Je zou, om voorspelbaarheid zinvol te operationaliseren, een relatieve maat kunnen gebruiken, zoeits als twee maal de standaarddeviatie van de voorspellende toetsscoreverdeling, gedeeld door het aantal items in de toets. Maar niets is zo duidelijk als plotten van die voorspellende toetsscoreverdelingen voor toetsen met verschillende lengte.


SUCCES

Althans, plezier ermee.












program Utiliteit_Leren;
{ 21 juli 1991; 18 oktober 1991 }

{ Maakt gebruik van de structuur, procedures etc. van programma 'Simuleer' 		}

{ Voor een agenda van nog in te bouwen en uit te werken procedures etc.,			}
{ zie eind van deze file												}

uses
	BerekenEnPlotDeze;

{ Press.lib is een bibliotheek van procedures uit Press c.s. (1989) 				}

procedure Initialiseer;
	var
		i, j: INTEGER;
	begin
		ArrayInit;			{ array met strings voor de parameterwaarden			}
		WindowInit;
		MenuBarInit;
		DialogInit(gSD);

	{ NewFont(Times, 9); Het font instellen nadat window en picture zijn geopend 	}
(*basis := 88; *)
  { 'basis' moet als eerste een waarde krijgen }
		for i := 0 to gNItems do begin
			gSA[i] := 0;
			gUA[i] := 0;
		end;
		fitScores := gSA; 			{ array voor gefitte frequentieverdeling 	}
		yScale := 50;
		if cumulatiefPlotten then
			yScale := 240;		{ default waarde, ter bepaling van de verticale schaal 	}
		gBoven := 140;
		gLinks := 90;
		error := FALSE;
		idum := TickCount;
      (*  writeln ( f 'De seed voor de random getallen generator is ',idum); *)
		Ran3Init(idum);
	{ Initialiseert array nodig voor random getallen generator; unit Press 		}
		for j := 0 to 200 do
			gla[j] := -1;
		GammLnInit;
		FactLNInit;
	end;

procedure Mainloop;
	var
		DoneOnce: BOOLEAN;
	begin
		gDone := FALSE;
		gIfNew := FALSE;
		gWNEImplemented := (NGetTrapAddress(WNE_TRAP_NUM, ToolTrap) <> NGetTrapAddress(UNIMPL_TRAP_NUM, ToolTrap));
		{ Is WaitNextEvent geïmplementeerd of niet? Mark & Reed p. 135 raden aan dit	}
		{ altijd te programmeren volgens de laatste gegevens in APDA's Programmer's	}
		{ Guide to Multifinder, omdat deze code nogal eens is veranderd				}
		while gDone = FALSE do begin
			HandleEvent;			{ signaleer gebeurtenissen, en handel ze af			}
			if gIfNew then begin
				WindowInit;
				OpenNuHetWindow(gWindowOpen);    { om te plotten in QuickDraw }
{ t.z.t. vervangen door een behoorlijke windowInit procedure }
				DitProgramma;
				ClosePicture;		{ Hierna gaan QuickDraw commando's weer naar het scherm	}
				SetWindowPic(gWPtr, gMyPicture); 	{ Attach the picture to the window 		}
(* Let op!!! Ik moet natuurlijk alles wat niet initialiseren betreft onderbrengen	*)
(* in de MainLoop en de gebeurtenissen die daarin worden afgehandeld. Dat komt	*)
(* in orde wanneer ik alles in menu's onderbreng						*)
				gIfNew := FALSE;		{ anders gaat ie maar door als gekke Henkie		}
			end;
		end;
	end;


{ ----------------------- hoofdprogramma --------------------------- }

begin
	Initialiseer;	{als er niets gebeurt, in Run Options use resources aanzetten: gebruik rscr file}
	Mainloop;
end.












unit BerekenEnPlotDeze;
{ 21 juli 1991; 18 oktober 1991; 17 juni 1994 }

{ Maakt gebruik van de structuur, procedures etc. van programma 'Simuleer' }

interface

uses
	Press, Wlbr; 	{ Press.lib is een bibliotheek van procedures uit Press c.s. (1989) 	}

const
	CumulatiefPlotten = TRUE;
				{ Frequentieverdelingen etc. als cumulatieve verd. plotten? 	}
	maxNItems = 500; 	{ Maximaal aantal items waaruit de te voorspellen toets 		}
				{ of de (gecumuleerde) proeftoets bestaat 				}
	tellen = FALSE; { WERKT NIET GOED, HANGT OP.  In TellerWindow de berekeningen bijhouden 				}

type
	SAT = array[0..maxNItems] of LONGINT;
	{ Frequenties gesimuleerde toetsscores, of gecumuleerde frequenties;		}
	{ er kunnen bij declaratie als INTEGER dus niet meer dan MaxInt runs worden 	}
	{ gedaan zonder risico op fouten: dus ofwel afvangen, ofwel LONGINT declareren 	}
	UAT = array[0..440] of REAL; 	{ array voor bewaren utiliteitsfunctie	}

var
	aEstg, bEstg: REAL; 	{ De parameters voor de Betaverdeling van domeinbeheersing 	}
				{ zoals geschat vanuit gesimuleerde toetsscores. 			}
	gSA, fitScores: UAT;
		(*gla: array[0..200] of REAL;	*)
                    	{ nodig in Press' function FactLn  }
		(* Hoe heb ik dit array dan gedeclareerd in de UNIT? *)
		(*idum: LONGINT;  *)
 		{ bewaart laatste pseudorandom  getal }
	error: BOOLEAN;
	gTijdschaal: REAL; 				{ aantal t (halfwaardetijd) waarover te plotten	}
	gVoorkennis, gRaadkans, gPlafond: REAL; 	{ parameters voor leermodel 		}
	gHorU, gVertU: LONGINT; 		 	{ definieert raam voor plotten utiliteitsfuncties 	}
		(*    yScale: INTEGER;*)
	gAPrT, gBPrT: REAL; 	{ proeftoetsresultaat , ook halve puntjes e.d. toegestaan		}
	gAU, gBU: REAL;   			{ voor utiliteitsfuncties als cumulatieve beta 		}
	hellingU, locatieU: REAL;   		{ parameters voor logistische utiliteitsfunctie; 	}
			{ vgl. discrimerend vermogen en moeilijkheid in IRT-modellen 	}
	gUA, gEUA: UAT;
			{ globaal array voor bewaren utiliteitsfunctie, resp. E(U) waarden 	}
	gHistogram, gUOverToetsscores: BOOLEAN;
	ch: CHAR;
	gNItems, gNPrItems: LONGINT;		{ aantal items in de toets, resp. de proeftoets	}


procedure DitProgramma;

implementation

procedure DitProgramma;

	type
		stringsT = array[1..8] of string;
	var
		countDownWindow: WindowPtr;		{ voor schermteller				}

	procedure PlotThis (horSchaal, yLo, yHi: REAL; cumulatief: BOOLEAN; arr: UAT; sA: stringsT);

{ De plaats van de figuur in het window wordt bepaald door links en onder. 			}
{   gHorU en gVertU: de breedte resp. hoogte van de figuur in pixels.					}
{   scale: voor figuren waarin tijdbesteding op de horizontale as staat 				}
{  (aantal pixels per 1 t ), voor de andere is scale = gHorU te stellen.				}
{   sA: 8 tekststrings die onder de figuur worden geprint.		}
{   De verticale schaalwaarden worden verondersteld als 0 .. 1, tenzij dat anders is,	}
{   zoals voor de plot van verschil tussen E ( U ) en gewaardeerde t, dat wordt met 	}
{   originx en yLo, yHi aangegeven.										}

		var
			myRect: Rect;
			teller, i, j, nPunten, x, y, pendikte, pep, laatsteY: INTEGER;
			k: LONGINT;
			s1, s2, s3: str255;
			vertFactor, schaal: REAL;
		begin
			if gHistogram then
				nPunten := gNItems						{ histogram plotten			}
			else
				nPunten := gHorU; 						{ ELSE per pixel plotten 		}
			if cumulatief then begin				{ cumulatieve functie van maken : 	}
				for i := 1 to nPunten do		{ niet vanaf 0, er wordt maar nPunten keer iets opgeteld	}
					arr[i] := arr[i] + arr[i - 1];			{ cumulatieve 				}
				if arr[nPunten] = 0 then begin	{ foutmelding }
					writeln('Berekende oppervlakte onder de curve is nul, in Procedure PlotThis');
					readln(ch);
				end;
        { normaliseer tot oppervlakte 1: }
				for i := 0 to nPunten do
					arr[i] := arr[i] / arr[nPunten];
			end;
         { kader, raster en schaalwaarden printen }
			Pendikte := 2;
			Pensize(pendikte, pendikte);

			myRect.left := gLinks - pendikte;
			myRect.right := gLinks + gHorU + pendikte;
			myRect.top := gBoven - pendikte;		{ Altijd tegen de bovenkant van het window printen }
			myRect.bottom := gBoven + gVertU + pendikte;
			gOnder := gBoven + gVertU;
			FrameRect(myRect);			{ eerst omtrek van de figuur tekenen 		}
			Pensize(1, 1);

        	{ horSchaal geeft het aantal pixels dat voor 1 schaaleenheid, te beginnen bij 0,	}
	{ beschikbaar is. 								}

			teller := -1;
			if horschaal = gHorU + 1 then begin 			{ schaal van 0 tot 1 		}
				moveto(gLinks, gOnder + 20);
				DrawString('0');
				moveto(gLinks + gHorU - 3, gOnder + 20);
				DrawString('1');
			end
			else
				for i := 0 to gHorU do begin
					if horSchaal < 2 then
						pep := 25		{ schaalwaarden om de 25 pixels printen }
					else if horSchaal < 5 then
						pep := 20		{ idem om de 3.2*20 als horschaal = 3.2, etc.  }
					else if horSchaal < 11 then
						pep := 10
					else if horSchaal < 16 then
						pep := 5
					else if horSchaal < 21 then
						pep := 5	{ komt goed uit bij 400 pixels en 20 items }
					else if horSchaal < 41 then
						pep := 2
					else
						pep := 1;
	{ Bij het opgeven van de breedte in pixels kunnen strategische getallen worden gekozen}
	{ zoals bij een toets van 100 vragen 400 pixels, ipv 440 }

					if (i mod (round(pep * horschaal)) = 0) or (i = gHorU) then begin
						teller := teller + 1;
						moveto(gLinks + i, gOnder + 2);
						if dialRadio^[SCHAAL_WAARDEN] = 1 then
							lineto(gLinks + i, gOnder - 2);
						PenPat(gray);				{ verticale lijnen in de figuur trekken 	}
						if dialRadio^[RASTER_OF_NIET] = 1 then
							if (teller <> 0) and (round(teller * horSchaal) <> gHorU) then
								lineto(gLinks + i, gOnder - gVertU);
						moveto(gLinks - 1 + i + round(10 / pep - 7), gOnder + 20);
						if teller * pep <> nPunten + 1 then
							if dialRadio^[SCHAAL_WAARDEN] = 1 then
								DrawInteger(teller * pep);
						PenPat(black);
					end;
				end;
									{ schaalwaarden verticaal : 		}
			teller := 11;
			for i := 0 to gVertU do
				if i mod (round(gVertU / 10)) = 0 then begin
					teller := teller - 1;
					moveto(gLinks - 2, gOnder - gVertU + i); 	{ markering tekenen 			}
					if dialRadio^[SCHAAL_WAARDEN] = 1 then
						lineto(gLinks + 2, gOnder - gVertU + i);
					PenPat(gray);
					if dialRadio^[RASTER_OF_NIET] = 1 then
						if (teller <> 0) and (teller <> 10) then	 { horizontale lijnen intekenen 	}
							lineto(gLinks + gHorU, gOnder - gVertU + i);
					PenPat(black);
					if dialRadio^[SCHAAL_WAARDEN] = 1 then begin
						NumToString(trunc(yHi), s3);
						if yHi <> 1 then begin
							if yHi > 1 then
								NumToString(Round(10 * (yHi - Trunc(yHi))), s1)
							else
								NumToString(Round(100 * (yHi - Trunc(yHi))), s1);
							if yHi < 50 then
								s3 := Concat(s3, ',', s1);
						end;
						NumToString(trunc(yLo), s1);
						s2 := '0';
						moveto(gLinks - 35, gOnder + 4 - gVertU + i);
						if teller = 10 then
							DrawString(s3)
						else if teller = 0 then begin
							if yLo < 0 then
								moveto(gLinks - 40, gOnder + 4 - gVertU + i);
							DrawString(s1)
						end
						else if teller = 5 then begin			{ alleen de waarde voor het middel van de schaal printen }
							if yHi - (1 - teller / 10) * (yHi - yLo) = 0 then
								DrawString(s2);
							if yHi >= 1 then begin
								if yHi >= 100 then
									NumToString(Abs(round((yHi - (1 - teller / 10) * (yHi - yLo)))), s1)
								else
									NumToString(Abs(round(10 * (yHi - (1 - teller / 10) * (yHi - yLo)))), s1)
							end
							else begin
								NumToString(Abs(round(100 * (yHi - (1 - teller / 10) * (yHi - yLo)))), s1);
								if Length(s1) = 1 then
									s1 := Concat('0', s1);		{ om 0,05 ook als 0,05 geschreven te krijgen, niet als 0,5 }
							end;
							if (yHi - (1 - teller / 10) * (yHi - yLo)) >= 0 then begin
								if (yHi - (1 - teller / 10) * (yHi - yLo)) < 1 then
									s1 := Concat('0,', s1);
								moveto(gLinks - 35, gOnder + 4 - gVertU + i);
							end
							else begin
								s1 := Concat('-0,', s1);
								moveto(gLinks - 40, gOnder + 4 - gVertU + i);
							end;
							DrawString(s1);
				{ print tussenliggende waarden met 1 decimaal  }
						end;
					end;
				end;
			for j := 1 to 7 do
				for i := 7 downto 1 do
					if sA[i] = '' then begin
						sA[i] := sA[i + 1];
						sA[i + 1] := '';
					end;
			for i := 1 to 8 do begin
				Moveto(gLinks - 10, gOnder + 25 + i * 16);
				DrawString(sA[i]);
			end;

			if horSchaal = 0 then
				writeln('Schaalfactor is 0 in Procedure PlotThis.');
			x := gLinks;
			y := gOnder - round(arr[0] * gVertU);
			Moveto(x, y);
			vertFactor := gVertU / (yHi - yLo);	{ aantal pixels gedeeld door het schaalbereik	}
			for i := 0 to nPunten do begin
		{ er moeten nPunten + 1 'kolommen' worden geplot					}
		{ en dan nog een laatste horizontale streepje 						}
				laatsteY := gOnder - round((arr[i] - yLo) * vertFactor);
         		{ arr bevat de juiste functiewaarden, om te kunnen plotten worden die 	}
		{ vertaald naar aantal pixels dat van gOnder moet worden afgetrokken 		}
				if not cumulatief and (nPunten = gHorU) then begin
					schaal := 1;
    		{ voor leercurve heeft 'scale' andere betekenis }
 		{ dan per pixel berekend en te plotten }
					x := gLinks + round(i * schaal);
				end
				else
					x := gLinks + round(i * horSchaal);
				if nPunten <> gHorU then begin
					if arr[i] >= yLo then begin
						Lineto(x, laatsteY); 	{ bij histogrammen nodig: horizontale lijn 		}
					end;
				end
				else begin
					x := gLinks + i;
              					{ ook bij drempel nodig: kleine horizontale lijn: 	}
					if i > 0 then
						if i + 1 <= gHorU then
							if arr[i] - arr[i - 1] = 1 then
								if arr[i - 1] >= yLo then begin
									Lineto(x, laatsteY);
								end;
				end;
				y := gOnder - round((arr[i] - yLo) * vertFactor);
				if (arr[i] >= yLo) and (arr[i] <= yHi) then
					Lineto(x, y)
				else   		{ zet de pen op het volgende punt neer zonderlijn te trekken 	}
					if i < nPunten then begin { voorkom floating point error bij i = nPunten 	}
						if nPunten <> gHorU then
							Moveto(gLinks + round((i + 1) * horSchaal), gOnder - round((arr[i + 1] - yLo) * vertFactor))
						else
							Moveto(gLinks + i, gOnder - round((arr[i + 1] - yLo) * vertFactor));
					end;
			end; {  FOR i := 0 TO nPunten  DO }


		end;   { PlotThis }


	function fBeta (a, b, x: REAL): REAL;					{ betaverdeling 		}
	{ Evalueert de betaverdeling beta ( a, b ) voor argument x;				}
	{ maakt gebruik van procedures GammLn of FactLn uit Press.lib 			}
		begin
	{ de ln over de formule voor de betaverdeling, en daar dan weer de exp over 	}
			fBeta := EXP(-GammLn(a) - GammLn(b) + GammLn(a + b) + (a - 1) * LN(x) + (b - 1) * LN(1 - x));
        (*      fBeta := EXP ( - FactLN ( a - 1 ) - FactLN ( b - 1 ) + FactLN ( a + b -1 ) *)
	(* + ( a - 1 ) * LN ( x ) + ( b - 1 ) * LN ( 1 - x ));  *)
		end;  { fBeta }


	function arcsin (x: real): real;
	{ A.R. Miller. Pascal programs for scientists and engineers. Dusseldorf: Sybex, 	}
	{1981 p. 9, gechecked op tabel 18 Novick & Jackson. Hoewel N&J uitgebreid 	}
	{ gebruik maken van de arcsin  transformatie, zijn zij niet helder over de  	}
	{ daartoe te gebruiken formules; in plaats daarvan geven zij tabellen. De 		}
	{uitkomsten van de formule van Miller stemmen overeen met de waarden in 	}
	{ tabel 18a van N&J. N&J maken gebruik van een enigszins misleidende notatie.	}
		begin
			if x = 0 then
				arcsin := 0
			else
				arcsin := arctan(x / sqrt(1.0 - sqr(x)))
		end;    { arcsin }


	procedure CumBeta (a, b: REAL; n: INTEGER; var arr: UAT);
	{ Plot cumulatieve betaverdeling als 'continue' utiliteitsfunctie, 			}
	{ zoals past bij domeinscores 							}
		var
			sA: stringsT;
			s1, s2, s3: str255;
			horschaal, yLo, yHi: REAL;
			cumulatief: BOOLEAN;
			i: INTEGER;

		procedure Utiliteit_CumBeta (a, b: REAL; n: INTEGER; var arr: UAT);
			var
				x: REAL; 							{ parameters betafunctie 		}
				i, j, nPunten: INTEGER;
			begin
				yLo := 0;
				yHi := 1;
				cumulatief := TRUE;
				if gUOverToetsscores then begin	{ utiliteit over TOETSscores nemen 	}
					nPunten := n;
					horschaal := gHorU / nPunten;	{ niet delen door nPunten + 1, maar door nPunten }
				end
				else begin
					nPunten := gHorU;
					horSchaal := 1;					{ utiliteit over DOMEINscores nemen 	}
				end;
				for i := 1 to 8 do
					sA[i] := '';
				arr[0] := 0;     { per definitie heeft score 0 utiliteit 0 					}
		{ Voor raden hoeft de utiliteitsfunctie niet te worden aangepast:		}
		{ voor een gegeven toetsresultaat is immers niet bekend welk deel	}
		{ van de score door raden is verkregen. 					}
		{ 'Correctie voor raden' is een groepscorrectie! 				}
				if (a = 1) and (b = 1) then					{ lineaire utliteit 		}
    (* t.z.t. de mogelijkheid inbouwen de helling en locatie voor lineaire utiliteit op te geven *)
					for i := 1 to nPunten do
						arr[i] := 1 / nPunten
				else if a + b > 100000 then		{ de bedoeling is dan een drempelfunctie 	}
					begin
		{ bij drempelverlies, dus als grote waarden voor gAU en gBU opgegeven,	}
		{ en utiliteit over toetsscores, dan wordt drempel .8 bij 10 toetsvragen	}
		{ zo geinterpreteerd dat score 8 utiliteit 1 heeft. 					}
					for j := 1 to nPunten do
						arr[j] := 0;
					i := round(nPunten * a / (a + b));
					{ wat is de eerste arr [ i ] die utiliteit 1 oplevert: 		}
					arr[i] := 1;    	{ PlotThis maakt er nu wel een cumulatieve verdeling van 	}
				end
				else begin
					for i := 1 to nPunten do
						arr[i] := 0;
					x := a / (a + b); 	{ plotten gebeurt in twee delen, telkens vanuit het gemiddelde 	}
					i := round((nPunten + 1) * x); {'basis' is gelijk het aantal pixels in de basis 	}
					repeat			{ voor iedere pixel / vraag rechts van het gemiddelde 	}
						x := (i + 0.5) / (nPunten + 1);
	{ evalueren op middenpunten histogrammen, normaliseren van nPunten + 1 	}
	{ naar 1; dit kan onnauwkeurig zijn bij kleine aantallen toetsvragen, maar 	}
	{ kleine aantallen toetsvragen leveren zelf ook onnauwkeurigheid op 			}
						arr[i] := fBeta(a, b, x);
						i := i + 1;
					until (round(arr[i - 1] * gVertU) = 0) or (i = nPunten + 1);
	{ doelmatigheid: bij bereiken extreme waarde verdere berekeningen stoppen 	}
	{ voor i = nPunten is x = 1, dan geen beta evalueren! 					}
					x := a / (a + b);
					i := round((nPunten + 1) * x);
					repeat					{ voor iedere pixel links van het gemiddelde 	}
						x := (i + 0.5) / (nPunten + 1);
						arr[i] := fBeta(a, b, x);
						i := i - 1;
					until (round(arr[i + 1] * gVertU) = 0) or (i = 0);
							{ voor i = 0 is arr [ i ] al gelijk 0 gesteld 	}
				end;
				for i := 1 to nPunten do
					arr[i] := arr[i] + arr[i - 1]; 			{ cumulatieve verdeling 		}
				for i := 0 to nPunten do
					arr[i] := arr[i] / arr[nPunten]; 			{ normaliseer tot oppervlakte 1	}
			end;						{ BetaUtiliteit 				}

		begin
			for i := 1 to 8 do
				sA[i] := '';
			Utiliteit_CumBeta(a, b, n, arr);
			horschaal := gHorU / n;	{ niet delen door n + 1, maar door n }
			sA[1] := 'NUTSFUNCTIE';
			if gUOverToetsscores then begin
				NumToString(gNItems, s2);
				sA[3] := Concat('over de scores op een toets met ', s2, ' vragen');
			end
			else
				sA[3] := 'over domeinscores';
			if a + b > 100000 then begin
				RealToString(a / (a + b), 2, s1);
				s1 := Concat('drempel ', s1);
				sA[2] := Concat(sA[1], s1);
			end
			else if (a = 1) and (b = 1) then
				sA[2] := 'lineaire '
			else begin
				sA[2] := 'de functie is de cumulatieve beta (';
				if Trunc(a) = a then
					NumToString(Trunc(a), s1)
				else
					RealToString(a, 1, s1);			{ in 1 decimaal is wel voldoende 		}
				if Trunc(b) = b then
					NumToString(Trunc(b), s2)
				else
					RealToString(b, 1, s2);
				sA[2] := Concat(sA[2], s1, ', ', s2, ')');
			end;

			cumulatief := FALSE;			{ verdeling is al cumulatief }
			if gUOverToetsscores then
				gHistogram := TRUE
			else
				gHistogram := FALSE;
			if dialRadio^[PLOT_UTIL_CURVE] = 1 then
				PlotThis(horSchaal, yLo, yHi, cumulatief, arr, sA);
 			{ Bij gebruik van deze procedure door andere procedures			}
			{ moet de cumulatieve functie worden teruggegeven: 			}
		end;


	procedure Leercurve;
	{ Deze procedure is alleen bedoeld om een plot van de leercurve te maken 		}

	{ De algemene formule: domeinbeheersing p = 1 - .5 ** t = 1 - exp ( ln t * ( 0.5 ) ) 	}
	{ De specifieke waarde 0.5 in het halfwaardemodel :							}
	{ omdat de tijdseenheid t niet is gespecificeerd, is 0.5 een perfect algemeen model 	}
	{ De leercurven die met deze procedure kunnen worden geplot zijn eerder behandeld  	}
	{ in Studiestrategieën, bijlage D. 							}
	{ voorkennis is de kennis die de deelnemer al had, de leercurve 				}
	{ vangt op tijdstip t = 0 hoger aan.							}
	{ Hoofdstuk 4, leermodel, geeft een eenvoudige formule, formule (3):			}
	{ geleerd := voorkennis +   ( 1 - voorkennis ) * ( 1 - voorkennis ( t * ln ( 0.5 )))	}
	{  =  1 - ( 1 - voorkennis ) * exp ( t * ln ( 0.5 )); 						}
	{ raadkans wordt wiskundig op dezelfde manier behandeld als voorkennis, 		}
	{ maar is daaraan niet identiek !  geleerd := 1 - ( 1 - guess ) * exp ( t * ln ( 0.5 )).	}
	{ Een aanloopperiode is niet expliciet gemodelleerd. Voor eventuele herkansingen is 	}
	{ deze niet van belang. Er kan altijd verondersteld worden dat de aanloopperiode al 	}
	{ voorbij is. Er is altijd een plafond aan bereikbare domeinbeheersing dat lager is 	}
	{ dan 1, bijv. door dubbelzinnige vragen, door niet altijd volledige concentratie, 		}
		{ door herinneringsfouten, e.d.							}
	{ De formule voor domeinbeheersing wordt gewoon met 'plafond' vermenigvuldigd. 	}

		var
			i, j, teller, nPunten: INTEGER;
			sA: stringsT;
			s1, s2, s3: str255;
			tijdschaal, yLo, yHi: real;
			cumulatief, overToetsscores: BOOLEAN;
			arr: UAT;

		function Geleerd (t: REAL): REAL; 	{ Formule [ 6 ] in Hoofdstuk 4, leermodel 	}
			begin
				geleerd := gPlafond * (1 - (1 - gVoorkennis) * (1 - gRaadkans) * exp(t * ln(0.5)));
			end;

		begin
			tijdschaal := gHorU / gTijdschaal; 	 { aantal pixels beschikbaar per tijdseenheid 	}
			yLo := 0;
			yHi := 100;	{ plot percentages }
			for i := 1 to 8 do
				sA[i] := '';
			cumulatief := FALSE;
			{ Procedure PlotThis moet geen cumulatieve verdeling maken 		}
			for i := 0 to gHorU do 			{ voor iedere pixel op de horizontale as 		}
				arr[i] := 100 * geleerd(i / tijdschaal);	{ percentages }
			sA[1] := 'LEERCURVE';
			sA[2] := 'verticaal: beheersing in %; horizontaal:aantal keer halfwaardetijd';
			if gRaadkans <> 0 then begin
				sA[3] := ('raadkans van toetsvragen is ');
				RealToString(gRaadkans, 2, s2);
				sA[3] := Concat(sA[3], s2);
			end;
			if gVoorkennis <> 0 then begin
				sA[4] := ('niveau voorkennis is ');
				RealToString(gVoorkennis, 2, s1);
				sA[4] := Concat(sA[4], s1);
			end;
			if gPlafond <> 1 then begin
				sA[5] := ('kennisplafond is ');
				RealToString(gPlafond, 2, s3);
				sA[5] := Concat(sA[5], s3);
			end;
			gHISTOGRAM := FALSE;
			PlotThis(tijdschaal, yLo, yHi, cumulatief, arr, sA);
		end;   { PlotLeercurve }


	procedure BepaalVTV (a, b: REAL; n: INTEGER; var VTV: UAT);

        	{ Bepaalt Voorspellende Toetsscoreverdeling VTV als BetaBinomiaalverdeling, 	}
	{ slaat de frequentieverdeling op in array sInt. De Procedure PlotFrequenties 	}
	{ kan voor plotten van de verdeling worden gebruikt. 					}
	{ BetaBinomiaalVerdeling met parameters nI: aantal toetsvragen, 			}
	{  x: aantal goed, a en b de parameters van de BetaVerdeling.				}
	{ a en b kunnen ook reeel worden gekozen, maar dan moet de procedure 		}
	{FactLN worden aangepast, vergelijk functie fBeta.						}
	{ De procedure is gebaseerd op de procedure BeBiFit.					}

		var
			i: INTEGER;
			Fn, FAB, Fa, Fb, Fabn: EXTENDED;
		begin
          {  reële parameterwaarden worden gebruikt om de betabinomiaal te bepalen,	}
	{ en dat is de enige manier om geen 'jagged' plots te krijgen: 				}
			Fn := FactLN(n);
			Fab := GammLn(a + b);
			Fa := GammLn(a);
			Fb := GammLn(b);
			Fabn := GammLn(a + b + n);
			for i := 0 to n do 					{ voor iedere mogelijke score 	}
				begin
				VTV[i] := round(1000000 * EXP(Fn - FactLN(i) - FactLN(n - i) + Fab - Fa - Fb + GammLn(a + i) + GammLn(b + n - i) - Fabn));
			end;
       { De grote vermenigvuldigingsfactor is bedoeld om niet op een array of real te hoeven overgaan }
		end;   {  BepaalVTV  }


	procedure Betabinomiaal;
	{ Bereken betabinomiaal op opgegeven parameters, voor een plot }
		var
			VTV: UAT;
			sA: stringsT;
			s1: str255;
			horschaal, yLo, yHi: real;
			e: extended;
			i: INTEGER;
			cumulatief: BOOLEAN;
	{ Moet PlotThis cumuleren voordat de verdeling wordt geplot? 				}
		begin
			for i := 0 to gNItems do
				VTV[i] := 0;
			for i := 1 to 8 do
				sA[i] := '';
			BepaalVTV(gAPrT, gBPrT, gNItems, VTV); { aantal goed en fout in proeftoets, n items in toets }
			sA[1] := 'VOORSPELLENDE TOETSSCOREVERDELING';
			sA[2] := 'het proeftoetsresultaat is ';
			NumToString(Round(gAPrT), s1);
			sA[2] := Concat(sA[2], s1, ' goed uit ');
			NumToString(Round(gAPrT + gBPrT), s1);
			sA[2] := Concat(sA[2], s1);
			sA[3] := 'de voorspelde toets heeft ';
			NumToString(gNItems, s1);
			sA[3] := Concat(sA[3], s1, ' vragen');
			horschaal := gHorU / gNItems;	{ niet delen door gNItems + 1, maar door gNItems }
			yLo := 0;
	{ Voor de hoogte van de figuur moet ik yHi geschikt kiezen naar het aantal items in de toets: }
			if gNItems < 4 then
				yHi := 1
			else if gNItems < 11 then
				yHi := 0.5
			else if gNItems < 21 then
				yHi := 0.4
			else if gNItems < 41 then
				yHi := 0.3
			else if gNItems < 101 then
				yHi := 0.2;
			e := 0;
	{ oppervlakte onder curve normeren op 1 }
			for i := 0 to gNItems do
				e := e + VTV[i];
			for i := 0 to gNItems do
				VTV[i] := VTV[i] / e;
			for i := 0 to gNItems do
				if VTV[i] > yHi then
					yHi := 1.05 * VTV[i];
			gUOverToetsscores := TRUE;
			gHistogram := TRUE;
			cumulatief := FALSE;
			PlotThis(horschaal, yLo, yHi, cumulatief, VTV, sA);
		end;


	procedure Binomiaal;
	{ Bereken binomiaal op opgegeven parameters, voor een plot }
		var
			BINO: UAT;
			sA: stringsT;
			s1: str255;
			horschaal, yLo, yHi: real;
			e: extended;
			i: INTEGER;
			cumulatief: BOOLEAN;
	{ Moet PlotThis cumuleren voordat de verdeling wordt geplot? 				}

		procedure BepaalBINO (p: REAL; n: INTEGER; var BINO: UAT);
       	{ Bepaalt Voorspellende Toetsscoreverdeling  als Binomiaalverdeling, 		}
	{ slaat de frequentieverdeling op in array BINO.  				  	}
	{ kan voor plotten van de verdeling worden gebruikt. 				}
	{ BinomiaalVerdeling met parameters nI: aantal toetsvragen, 			}
	{  ware beheersing p gebaseerd op de parameters van de BetaVerdeling.		}
	{ Deze procedure ontleend aan wat ik in VergelijCumbetas heb gebruikt.		}
			var
				x, i: INTEGER;
				r, t: EXTENDED;
			begin
 	 {  kans op x successen: }
				for x := 0 to n do begin
					r := 1;
					for i := 0 to x - 1 do
						r := r * ((n - i) / (x - i)) * p;
					for i := 1 to n - x do
						r := r * (1 - p);
					BINO[x] := r;
				end;
			end;   {  BepaalBINO  }

		begin
			for i := 0 to gNItems do
				BINO[i] := 0;
			for i := 1 to 8 do
				sA[i] := '';
			BepaalBINO(gAPrT / (gAPrT + gBPrT), gNItems, BINO); { aantal goed en fout in proeftoets, n items in toets }
			sA[1] := 'BINOMIALE VOORSPELLENDE TOETSSCOREVERDELING';
			sA[2] := 'bij veronderstelde WARE BEHEERSING  ';
			NumToString(Round(100 * gAPrT / (gAPrT + gBPrT)), s1);
			sA[2] := Concat(sA[2], s1, ' %');
			sA[3] := 'de voorspelde toets heeft ';
			NumToString(gNItems, s1);
			sA[3] := Concat(sA[3], s1, ' vragen');
			horschaal := gHorU / gNItems;	{ niet delen door gNItems + 1, maar door gNItems }
			yLo := 0;
	{ Voor de hoogte van de figuur moet ik yHi geschikt kiezen naar het aantal items in de toets: }
			if gNItems < 4 then
				yHi := 1
			else if gNItems < 11 then
				yHi := 0.5
			else if gNItems < 21 then
				yHi := 0.4
			else if gNItems < 41 then
				yHi := 0.3
			else if gNItems < 101 then
				yHi := 0.2;
			e := 0;
	{ oppervlakte onder curve normeren op 1 }
			for i := 0 to gNItems do
				e := e + BINO[i];
			for i := 0 to gNItems do
				BINO[i] := BINO[i] / e;
			for i := 0 to gNItems do
				if BINO[i] > yHi then
					yHi := 1.05 * BINO[i];
			gUOverToetsscores := TRUE;
			gHistogram := TRUE;
			cumulatief := FALSE;
			PlotThis(horschaal, yLo, yHi, cumulatief, BINO, sA);
		end;


	procedure Beta;
	{ Bereken betaverdeling op opgegeven parameters, voor een plot }
		var
			BETA: UAT;
			sA: stringsT;
			s1, s2: str255;
			horschaal, yLo, yHi: real;
			e: extended;
			i: INTEGER;
			cumulatief: BOOLEAN;
	{ Moet PlotThis cumuleren voordat de verdeling wordt geplot? 				}

		procedure BepaalBETA (a, b: REAL; var BETA: UAT);
			var
				x: REAL;
				i: INTEGER;
			begin
				for i := 1 to gHorU - 1 do begin
					x := i / gHorU;
					BETA[i] := fBeta(a, b, x);
				end;
				BETA[0] := 0;
				BETA[gHorU] := 0;
			end;   {  BepaalBETA  }

		begin
			for i := 0 to gNItems do
				BETA[i] := 0;
			for i := 1 to 8 do
				sA[i] := '';
			BepaalBETA(gAPrT, gBPrT, BETA);
			sA[1] := 'KANSVERDELING VOOR DE WARE BEHEERSING';
			sA[2] := 'horizontaal: ware beheersing van 0 tot 100 %';
			sA[3] := 'de oppervlakte onder de kansverdeling  = 1';
			sA[4] := 'het proeftoetsresultaat is ';
			NumToString(Round(gAPrT), s1);
			sA[4] := Concat(sA[4], s1, ' goed uit ');
			NumToString(Round(gAPrT + gBPrT), s1);
			sA[4] := Concat(sA[4], s1);
			horschaal := 1;	{ niet delen door gNItems + 1, maar door gNItems }
			yLo := 0;
			yHi := 0;
	{ Voor de hoogte van de figuur moet ik yHi geschikt kiezen naar het aantal items in de toets: }
			e := 0;
	{ oppervlakte onder curve normeren op 1 }
			for i := 0 to gHorU do
				e := e + BETA[i];
			for i := 0 to gHorU do
				BETA[i] := 0.05 * gHorU * gVertU * BETA[i] / e;
			yHi := gVertU;
			gHistogram := FALSE;
			cumulatief := FALSE;
			PlotThis(horschaal, yLo, yHi, cumulatief, BETA, sA);
		end;


	procedure SluitTellerWindow;
		begin
	(* diverse QuickDraw instellingen herstellen, zie OpenTellerWindow *)
			DisposeWindow(countDownWindow);
			BeginUpdate(WindowPtr(gWPtr));
	(*DrawMyPicture(WindowPtr(gWPtr));*)
			EndUpdate(WindowPtr(gWPtr));
			ShowWindow(gWPtr);		{ Toon het window waarin wordt geplot 			}
			while not button do begin
			end;
		end;


	procedure EvalueerExpU (tijdschaal: REAL; var arrEU: UAT);

   { Nadat de proeftoetsscore is verkregen, wordt de verwachte utiliteit geplot tegen de}
{     tijdbesteding die daarvoor nodig is. Om te beginnen worden de schaalwaarden absoluut }
{     gegeven, dus niet als extra (tijd) en winst (in verwachte utiliteit) t.o.v. het moment }
{     van de proeftoetsafname.}
{     aUt en bUt zijn de parameters voor de utiliteitsfunctie in de vorm van een cumulatieve beta;}
{     aPr is aantal vragen goed in de proeftoets, a + b is aantal vragen in de proeftoets;}
{     n is het aantal vragen in de te voorspellen toets;}
{     gVertU en gHorU geven de omvang in pixels van de te plotten figuur;}
{     pre is voorkennis, guess is de raadkans, top is het kennisplafond voor de leercurve.}
{   }
		label
			123;

		var
			i, j: INTEGER;
			at, bt, beheersing, tijdbesteding, constante: REAL;
			VTV: UAT;    { voorspellende toetsscoreverdeling }
			arrUf: UAT;    { voor ophalen utiliteitsfunctie }
			s1: str255;
			nPunten: INTEGER;
			scale: REAL;

		function VerwachteUtiliteit (n: INTEGER; VTV, arr: UAT): REAL;
			var
				i: INTEGER;
				expU: extended;
			begin
				ExpU := 0;
				for i := 0 to n do
					ExpU := ExpU + VTV[i] * arr[i];
				VerwachteUtiliteit := ExpU / 1000000;
			end;

		function Geleerd (t: REAL): REAL;
			begin
				geleerd := gPlafond * (1 - (1 - gVoorkennis) * (1 - gRaadkans) * exp(t * ln(0.5)));
			end;

		procedure OpenTellerWindow;	{ voor schermteller een window initialiseren 		}

			procedure SetUpCountDownWindow;
				var
					myRect: Rect;
					fontNum: INTEGER;
				begin
					myRect := countDownWindow^.portRect;
					SetPort(countDownWindow);
					GetFNum('monaco', fontnum);
					TextFont(fontNum);
					TextSize(24);
					TextFace([bold]);
					TextMode(srcCopy);		{ paint over existing graphics 					}
				end;

			begin
				HideWindow(gWPtr);		{ Verberg het window waarin wordt geplot 			}
				countDownWindow := GetNewWindow(STANDAARD_ID + 1, nil, WindowPtr(-1));
				SetUpCountDownWindow;
				ShowWindow(countDownWindow);
				Moveto(20, 100);
				DrawString('Ik tel de berekeningen');
			end;

		procedure Utiliteit_CumBeta (a, b: REAL; n: INTEGER; var arr: UAT);
			var
				sA: stringsT;
				s1, s2, s3: str255;
				x, horschaal, yLo, yHi: REAL; 							{ parameters betafunctie 		}
				i, j, nPunten: INTEGER;
				cumulatief: BOOLEAN;
			begin
				yLo := 0;
				yHi := 1;
				cumulatief := TRUE;
				if gUOverToetsscores then begin	{ utiliteit over TOETSscores nemen 	}
					nPunten := n;
					horschaal := gHorU / nPunten;	{ niet delen door nPunten + 1, maar door nPunten }
				end
				else begin
					nPunten := gHorU;
					horSchaal := 1;					{ utiliteit over DOMEINscores nemen 	}
				end;
				for i := 1 to 8 do
					sA[i] := '';
				arr[0] := 0;     { per definitie heeft score 0 utiliteit 0 					}
		{ Voor raden hoeft de utiliteitsfunctie niet te worden aangepast:		}
		{ voor een gegeven toetsresultaat is immers niet bekend welk deel	}
		{ van de score door raden is verkregen. 					}
		{ 'Correctie voor raden' is een groepscorrectie! 				}
				if (a = 1) and (b = 1) then					{ lineaire utliteit 		}
    (* t.z.t. de mogelijkheid inbouwen de helling en locatie voor lineaire utiliteit op te geven *)
					for i := 1 to nPunten do
						arr[i] := 1 / nPunten
				else if a + b > 100000 then		{ de bedoeling is dan een drempelfunctie 	}
					begin
		{ bij drempelverlies, dus als grote waarden voor gAU en gBU opgegeven,	}
		{ en utiliteit over toetsscores, dan wordt drempel .8 bij 10 toetsvragen	}
		{ zo geinterpreteerd dat score 8 utiliteit 1 heeft. 					}
					for j := 1 to nPunten do
						arr[j] := 0;
					i := round(nPunten * a / (a + b));
					{ wat is de eerste arr [ i ] die utiliteit 1 oplevert: 		}
					arr[i] := 1;    	{ PlotThis maakt er nu wel een cumulatieve verdeling van 	}
				end
				else begin
					for i := 1 to nPunten do
						arr[i] := 0;
					x := a / (a + b); 	{ plotten gebeurt in twee delen, telkens vanuit het gemiddelde 	}
					i := round((nPunten + 1) * x); {'basis' is gelijk het aantal pixels in de basis 	}
					repeat			{ voor iedere pixel / vraag rechts van het gemiddelde 	}
						x := (i + 0.5) / (nPunten + 1);
	{ evalueren op middenpunten histogrammen, normaliseren van nPunten + 1 	}
	{ naar 1; dit kan onnauwkeurig zijn bij kleine aantallen toetsvragen, maar 	}
	{ kleine aantallen toetsvragen leveren zelf ook onnauwkeurigheid op 			}
						arr[i] := fBeta(a, b, x);
						i := i + 1;
					until (round(arr[i - 1] * gVertU) = 0) or (i = nPunten + 1);
	{ doelmatigheid: bij bereiken extreme waarde verdere berekeningen stoppen 	}
	{ voor i = nPunten is x = 1, dan geen beta evalueren! 					}
					x := a / (a + b);
					i := round((nPunten + 1) * x);
					repeat					{ voor iedere pixel links van het gemiddelde 	}
						x := (i + 0.5) / (nPunten + 1);
						arr[i] := fBeta(a, b, x);
						i := i - 1;
					until (round(arr[i + 1] * gVertU) = 0) or (i = 0);
							{ voor i = 0 is arr [ i ] al gelijk 0 gesteld 	}
				end;
				for i := 1 to nPunten do
					arr[i] := arr[i] + arr[i - 1]; 			{ cumulatieve verdeling 		}
				for i := 0 to nPunten do
					arr[i] := arr[i] / arr[nPunten]; 			{ normaliseer tot oppervlakte 1	}
			end;						{ BetaUtiliteit 				}

		begin       { ExpU }
			tijdschaal := gHorU / gTijdschaal; 	{ aantal pixels beschikbaar per tijdseenheid 	}

			Utiliteit_CumBeta(gAU, gBU, gNItems, arrUf);
									{ haal utiliteitsfunctie arrUf 	}
			BepaalVTV(gAPrT, gBPrT, gNItems, VTV);
		{ bepaal bereikte punt op de leercurve gegeven proeftoetsresultaat 		}
		{    curve       p   = 1 - .5 ** t   -->       1 - p   =      .5 ** t  			}
		{  --> ln ( 1 - p ) = t ln .5    --> t = ln ( 1 - p ) / ln .5 				}
		{ formule [ 6 ] uit Hoofdstuk 4, leermodel:						}
		{ t = ln (( 1 - p ) / m ) / (( 1 - v ) ( 1 - r ))) / ln .5				}

		{ Afleiding uit p = m ( 1 - ( 1 - v ) ( 1 - r ) .5 ** t ):				}
		{ noem c = ( 1 - v ) ( 1 - r )								}
		{ p = m ( 1 - c .5 ** t )									}
		{ .5 ** t = ( 1 - p/m ) / c									}
		{ t ln .5 = ln (( 1 - p/m ) / c ) 								}
		{ t = ln (( 1 - p/m ) / c ) / ln .5								}

			beheersing := gAPrT / (gAPrT + gBPrT);
			tijdbesteding := (ln(gPlafond - beheersing) - ln(gPlafond) - ln(1 - gVoorkennis) - ln(1 - gRaadkans)) / ln(0.5);
	{ ln ( 1 - beheersing / plafond ) / ((1-voorkennis) * (1-raadkans)) / ln ( 0.5 );  }

	{ bereken per horizontale pixel ( = tijdbesteding ) de leerwinst, en de daarbij horende E ( U )}
	{plot E ( U ) in de leercurveplot, om te beginnen vanaf de huidige tijdbesteding 	}

	{ beginnen waar tijdbesteding = i / schaal, dus i = schaal * tijdbesteding: 		}
			i := round(tijdschaal * tijdbesteding);
			for j := 0 to i - 1 do
				arrEU[j] := -0.001; 		{ blijven onbepaald; neg: teken voor PlotThis 		}
			arrEU[i] := VerwachteUtiliteit(gNItems, VTV, arrUf);

			if tellen then
				OpenTellerWindow;
	{ open window voor teller voor aantal berekeningen 	}

			for i := round(tijdschaal * tijdbesteding) + 1 to gHorU do
        		 { voor iedere pixel op de horizontale as de verwachte utiliteit berekenen 	}
				begin

				if tellen then
					MoveTo(100, 60);
				if tellen then
					if i mod 10 = 0 then begin
						NumToString(i, s1);
						DrawString(s1);	  { tellen in TellerWindow }
					end;

		{ De VTV wordt nu geconditioneerd op de verbeterde beheersing, maar 	}
		{ niet op een groter aantal proeftoetsvragen: aPr + bPr blijft gelijk, 		}
		{ aPr / ( aPr + bPr ) wordt gelijk aan nieuwe beheersing  			}
		{ 'geleerd ( i / schaal )' 									}
				constante := gAPrT + gBPrT;
				at := geleerd(i / tijdschaal) * constante;
				bt := constante - at;
				if (gAU = 1) and (gBU = 1) then begin 	{ bij lineaire U geldt E(U) = E(X)	}
					arrEU[i] := at / constante 	{ verw. waarde BeBi (at, bt, n ) gedeeld door n 	}
				end
				else begin
					BepaalVTV(at, bt, gNItems, VTV);
					arrEU[i] := VerwachteUtiliteit(gNItems, VTV, arrUf);
					if round(500 * (1 - arrEU[i])) = 1 then begin
						for j := i + 1 to gHorU do
							arrEU[j] := 1.0;
						goto 123;
					end;
				end;
			end;
123:
		end;    { EvalueerExpU }


	procedure ExpUvsT (var arrEU: UAT);
	{ plot verwachte utiliteit versus halfwaardetijden }
		var
			i: INTEGER;
			prop, yLo, yHi, tijdschaal: REAL;
			sA: stringsT;
			s1, s2, s3: str255;
			cumulatief: BOOLEAN;

		begin
			for i := 1 to 8 do
				sA[i] := '';
			EvalueerExpU(gTijdschaal, arrEU);

			tijdschaal := gHorU / gTijdschaal; 	{ aantal pixels beschikbaar per tijdseenheid	}

			PenPat(black);
			PenSize(1, 1);
			if dialRadio^[PLOT_VERW_UTIL] = 1 then begin
				sA[1] := 'VERWACHT NUT NA EXTRA LEERTIJD.';
				sA[2] := 'horizontaal: tijdseenheden (halfwaardetijden); verticaal: verw. nut';
				if gRaadkans <> 0 then begin
					RealToString(gRaadkans, 2, s1);
					sA[4] := Concat(sA[4], 'raadkans ', s1, '% ');
				end;
				if gVoorkennis <> 0 then begin
					RealToString(gVoorkennis, 2, s2);
					sA[4] := Concat(sA[4], '  voorkennis ', s2, ' % ');
				end;
				if gPlafond <> 1 then begin
					RealToString(gPlafond, 2, s3);
					sA[4] := Concat(sA[4], '  plafond ', s3, '% ');
				end;
				sA[5] := 'Proeftoets ';
				if gAPrT / gBPrT > 0.02 then begin      { als aPr heel klein, dan is dat kennelijk zo}
		{ gekozen om een zo volledig mogelijke curven te kunnen plotten ;}
		{ de mededeling over proeftoetsresultaat heeft dan geen betekenis }
					if gAPrT - Trunc(gAPrT) = 0 then
						NumToString(round(gAPrT), s1)
					else
						RealToString(gAPrT, 1, s1);
					sA[5] := Concat(sA[5], s1, ' goed uit ');
					NumToString(gNItems, s2);
				end;
				NumToString(round(gAPrT + gBPrT), s2);
				NumToString(gNItems, s3);
				sA[5] := Concat(sA[5], s2, ' vragen. Toets ', s3, ' vragen.');

				if gAU + gBU > 100000 then begin
					prop := gAU / (gAU + gBU);
					NumToString(round(100 * prop), s3);
					sA[6] := Concat('nut drempel ', s3, ' %.');
				end
				else begin
					if gAU - Trunc(gAU) = 0 then
						NumToString(round(gAU), s1)
					else
						RealToString(gAU, 1, s1);
					if gBU - Trunc(gAU) = 0 then
						NumToString(round(gBU), s2)
					else
						RealToString(gBU, 1, s2);
					sA[6] := Concat('nutsfunctie cum. beta (', s1, '; ', s2, ').');
					if (gAU = 1) and (gBU = 1) then
						sA[6] := Concat(sA[6], ' (lineair)');
				end;
				if gUOverToetsscores then
					s1 := ''
				else
					s1 := ' (over domeinscores)';
				sA[6] := Concat(sA[6], s1);
				yLo := 0;
				yHi := 1;
				gHistogram := FALSE;
				cumulatief := FALSE;
				PlotThis(tijdschaal, yLo, yHi, cumulatief, arrEU, sA);
	{ nu niet 'overToetsscores = TRUE' doorgeven, maar een FALSE boodschap		}
	{ omdat er ondanks utiliteiten over toetsscores nu over pixels wordt geplot 		}
	{ het laatste is niet fraai, ik moet er iets beters op bedenken, bijv.			}
	{ Ut_Over_Toets 											}
			end;
			if tellen then
				SluitTellerWindow;
		end;


	procedure ExpUMinusT (var arrEU: UAT);

	{ Berekent en plot de verwachte utiliteit ( over domein- of toetsscores ) 		}
	{ min de op vergelijkbare schaal gewaardeerde kosten van de tijdbesteding;		}
	{ omdat deze laatste persoonlijk zijn, worden voor 10 verschillende waarden	}
	{ van 1t = 0,05 tot 0,5 de curven geplot.								}

		var
			sA: stringsT;
			s1, s2, s3: str255;
			i, j, k, teller: INTEGER;
			beheersing, tijdbesteding, prop, c: REAL;
			tijdschaal: REAL; 	{ schaalfactor voor figuren met leertijd:		}
			kosten: REAL; 	{ de persoonlijke inschaling van tijdbestding op de 		}
			{ utiliteitsschaal voor resultaten. 					}
			arrEU_T: UAT;
	{ per pixel te berekenen verschil tussen E ( U ) en gewaarde T 				}
			nCurven: INTEGER;
	{ aantal verschillende waarden van T waarvoor geplot wordt 				}
	{ nodig voor PlotThis: }
			nPunten: INTEGER; 	{ofwel  aantal pixels, ofwel aantal toetsvragen + 1 		}
			yLo, yHi: real;
	{standaard 0 (= links); laagste & hoogste verticale schaalwaarde			}
			cumulatief: BOOLEAN;
	{ Moet PlotThis cumuleren voordat de verdeling wordt geplot? 				}

		begin
			nCurven := 5;
	{ haal eerst array met de verwachte utiliteit, in enkele stappen: }
			EvalueerExpU(gTijdschaal, arrEU);

			arrEU_T := arrEU; 			{ alleen om te initialiseren 				}
			tijdschaal := gHorU / gTijdschaal; 	{ aantal pixels beschikbaar per tijdseenheid	}
			yLo := -1;
			yHi := 1;

			for i := 1 to 8 do
				sA[i] := '';

			for j := 0 to nCurven do begin 			 { plot meerdere curven 			}
				kosten := (j / 10) / tijdschaal;
	{ Stapsgewijze ophogen met telkens 1/20e als nCurven = 10 				}
  	{ Door tijdschaal delen, zodat per pixel kan worden gerekend:				}
	{ als 40 pixels per t, en t = .1, dan per pixel i/40e t kosten = .0025 		}
				for i := 0 to gHorU do begin
	{ voor iedere pixel extra tijdbesteding de verwachte utiliteit berekenen 		}
					if arrEU[i] <= 1 then begin	{ onbepaalde waarden niet afdrukken 		}
						c := arrEU[i] - kosten * i;        { E(U) min  kosten van i 'pixeltijdseenheden'	}
						if c > 1 then
							c := 1;
						if (arrEU[i] < 0) or (c < -1) then
							c := -1.001;
					end
					else
						c := 1.0001;			{ als het maar een waarde groter dan yHi is		}
					arrEU_T[i] := c;
				end;
				RealToString(j / (2 * nCurven), 2, s1);
				RealToString(j / (2 * nCurven), 2, s2);
				RealToString(j / (2 * nCurven), 2, s3);
				if j = nCurven then begin
		(*sA[1] := Concat('E(U) - t met resp. 1t = ', s1, ', ', s2, ', ', s3, ' ... ', '0.50.');*)
					sA[1] := 'VERWACHT NUT INCLUSIEF NUT VAN TIJD';
					sA[2] := 'kosten resp. 0 (bovenste curve); 0,1; 0,2; ...  ; 0,5 per tijdseenheid';
					sA[3] := 'horizontaal: tijdseenheden (halfwaardetijden); verticaal: verw. nut';
					if gRaadkans <> 0 then begin
						RealToString(gRaadkans, 2, s1);
						sA[4] := Concat(sA[4], 'raadkans ', s1, '% ');
					end;
					if gVoorkennis <> 0 then begin
						RealToString(gVoorkennis, 2, s2);
						sA[4] := Concat(sA[4], '  voorkennis ', s2, ' % ');
					end;
					if gPlafond <> 1 then begin
						RealToString(gPlafond, 2, s3);
						sA[4] := Concat(sA[4], '  plafond ', s3, '% ');
					end;
					sA[5] := 'Proeftoets ';
					if gAPrT / gBPrT > 0.02 then begin      { als aPr heel klein, dan is dat kennelijk zo}
		{ gekozen om een zo volledig mogelijke curven te kunnen plotten ;}
		{ de mededeling over proeftoetsresultaat heeft dan geen betekenis }
						if gAPrT - Trunc(gAPrT) = 0 then
							NumToString(round(gAPrT), s1)
						else
							RealToString(gAPrT, 1, s1);
						sA[5] := Concat(sA[5], s1, ' goed uit ');
						NumToString(gNItems, s2);
					end;
					NumToString(round(gAPrT + gBPrT), s2);
					NumToString(gNItems, s3);
					sA[5] := Concat(sA[5], s2, ' vragen. Toets ', s3, ' vragen.');

					if gAU + gBU > 100000 then begin
						prop := gAU / (gAU + gBU);
						NumToString(round(100 * prop), s3);
						sA[6] := Concat('nut drempel ', s3, ' %.');
					end
					else begin
						if gAU - Trunc(gAU) = 0 then
							NumToString(round(gAU), s1)
						else
							RealToString(gAU, 1, s1);
						if gBU - Trunc(gAU) = 0 then
							NumToString(round(gBU), s2)
						else
							RealToString(gBU, 1, s2);
						sA[6] := Concat('nutsfunctie cum. beta (', s1, '; ', s2, ').');
						if (gAU = 1) and (gBU = 1) then
							sA[6] := Concat(sA[6], ' (lineair)');
					end;
					if gUOverToetsscores then
						s1 := '(Toetsscore-)'
					else
						s1 := '(Domeinscore-)';
					sA[6] := Concat(s1, sA[6]);
				end;
				gHistogram := FALSE;
				cumulatief := FALSE;
				PlotThis(tijdschaal, yLo, yHi, cumulatief, arrEU_T, sA);
	{ de FALSE boodschap laat PlotThis per pixel een curve printen; anders}
	{ zou een wat dikker getekende curve als histogram over pixels worden geplot }
			end;	{ for j:= 0 to nCurven do }
		end;   					{  Plot_Verschil_Utiliteit_en_Tijd 			}


	procedure initialiseren;			{ parameterwaarden en dergelijke halen		}
		var
			s: Str255;
		begin
			gUOverToetsscores := true; { utiliteitsfunctie over toetsscores f over domeinscores ?}
(* Is het interessant om dit echt te implementeren? *)
			s := dialStr^[HOOGTE_PLOT];
			StringToNum(s, gVertU);
			s := dialStr^[BREEDTE_PLOT];
			StringToNum(s, gHorU);			 	{ Hoogte en breedte van de grafiek in pixels	}
			s := dialStr^[TIJD_SCHAAL_LENGTE];
			StringToReal(s, 2, gTijdschaal);		{ schaalfactor voor figuren met leertijd:	}
								{ bijv. 4.5 laat schaal lopen van 0 tot 4.5 	}
			s := dialStr^[N_TOETS];
			StringToNum(s, gNItems); 	{ Aantal items waaruit de te voorspellen toets bestaat 	}
			s := dialStr^[A_UTILITEIT];
			StringToReal(s, 4, gAu);	{ parameters voor utiliteitsfunctie als cumulatieve beta 	}
			s := dialStr^[B_UTILITEIT];
			StringToReal(s, 4, gBu);	{ parameters voor utiliteitsfunctie als cumulatieve beta 	}
			s := dialStr^[SCORE_PRTOETS];
			StringToREAL(s, 2, gAPrT); 		{ proeftoetsresultaat veronderstellenderwijs, 	}
			s := dialStr^[N_PRTOETS];
			StringToNum(s, gNPrItems);		{aantal items in de proeftoets				}
			gBPrT := gNPrItems - gAPrT;		{ andere 'parameter' voor proeftoetsresultaat		}
			s := dialStr^[RAAD_KANS];
			StringToReal(s, 2, gRaadkans);	{ als percentage opgegeven, proportie van maken	}
			gRaadkans := gRaadkans / 100;
			if gVoorkennis > 1 then
				gVoorkennis := 0;
			s := dialStr^[VOOR_KENNIS];
			StringToReal(s, 2, gVoorkennis);	{ als percentage opgegeven, proportie van maken	}
			gVoorkennis := gVoorkennis / 100;
			if gVoorkennis > 1 then
				gVoorkennis := 0;
			s := dialStr^[KENNIS_PLAFOND];
			StringToReal(s, 2, gPlafond);		{ als percentage opgegeven, proportie van maken	}
			gPlafond := gPlafond / 100;
			if gPlafond > 1 then
				gPlafond := 0;
		end;

{------------------------- DitProgramma ---------------------------}


	begin
		initialiseren;			{ parameterwaarden en dergelijke halen					}
		gUOverToetsscores := TRUE;
		if dialRadio^[PLOT_VERW_UTIL] = 1 then
			ExpUvsT(gUA);
		if dialRadio^[PLOT_UTIL_TIJD] = 1 then
			ExpUMinusT(gUA);
	{ verander de parameters waar het om gaat }
	{ Alleen het aantal goed op de proeftoets veranderen levert identieke plots op, }
	{ behalve het aanvangspunt.}
	{ Bij groter aantal toetsvragen worden de optima geprononceerder, tenminste wanneer}
	{ de utiliteitsfunctie niet lineair is }
		if dialRadio^[PLOT_UTIL_CURVE] = 1 then
			Cumbeta(gAU, gBU, gNItems, gUA);
		if dialRadio^[PLOT_LEER_CURVE] = 1 then
			Leercurve;
		if dialRadio^[PLOT_VTV] = 1 then
			Betabinomiaal;
		if dialRadio^[PLOT_BINO] = 1 then
			Binomiaal;
		if dialRadio^[PLOT_BETA] = 1 then
			Beta;

(*  PlotLogistic ( gVertU, gHorU, hellingU, locatieU ); na eind van het programma bewaard *)

	end; 		{ DitProgramma }

end.











{ oktober 1991 }

unit Wlbr;

{ Interface-, numerieke- en andere standaardprocedures voor simulatie-}
{ en andere programma's voor het toetsmodel-project }

interface

uses
	WlbrMenus;	{als errormessage Can’t find THPrint, dan in Compiler Options de USES EXTENSIONS option aanzetten}

const

	EDGE_THRESHOLD = 30;

	WINDOW_HOME_LEFT = 5;
	WINDOW_HOME_TOP = 45;
	NEW_WINDOW_OFFSET = 20;
	SCROLL_BAR_PIXELS = 15;
	NO_GO_AWAY = FALSE;		{ or TRUE; nodig bij Toolbox function NewWindow 		}
	NIL_REF_CON = 0;			{ nodig bij Toolbox function NewWindow 			}
	ACTIVATING = 1;			{ nodig voor activateEvts						}
	NORMAL_UPDATES = TRUE;	{ nodig voor inGrow updateEvts					}
	MIN_SLEEP = 20;
	SLEEP = 60;   	{ aantal clockticks the application is willing not to perform any		}
	{ background processing while waiting for an event. Apple recommends 60 		}

	LEAVE_WHERE_IT_IS = FALSE;

	WNE_TRAP_NUM = $60;			{ Voor WaitNextEvent / GetNextEvent routine nodig	}
	UNIMPL_TRAP_NUM = $9F;		{ idem, zie bv. Mark & Reed p. 116 e.v., 135 e.v.	}


procedure HandleMouseDown;
procedure DrawMyPicture (drawingWindow: WindowPtr; thePicture: picHandle);
procedure HaalPICT (var thePicture: picHandle);
procedure HandleEvent;
procedure DrawAt (x, y: INTEGER; s: Str255);
procedure NewFont (fontNumber, pointSize: INTEGER);
procedure DrawInteger (int: LONGINT);
procedure OpenNuHetWindow (var windowOpen: BOOLEAN);
procedure StringToReal (s: str255; dec: INTEGER; var r: REAL);
procedure RealToString (r: REAL; dec: INTEGER; var s: str255);
procedure DrawReal (r: REAL; dec: INTEGER);
procedure WindowInit;


implementation



procedure HandleMouseDown;			{ Mark & Reed p. 143-146				}
	var
		whichWindow: WindowPtr;
		thePart, dummy: INTEGER;
		menuChoice, windSize: LONGINT;
		oldPort: GrafPtr;
	begin
		thePart := FindWindow(gTheEvent.where, whichWindow);
		{ which window was the mouse clicked in? FindWindow returns in the		}
		{ parameter whichWindow a WindowPtr to the window containing the point	,	}
		{ in addition, the integer type thePart describes the part of the window in		}
		{ which the point was located									}
		case thePart of		{ list of predifined part codes: Inside Macintosh I: 287		}
			inSysWindow: 		{ in a system window, a desk accessory 					}
				SystemClick(gTheEvent, whichWindow);
					{ pass the event and the Ptr to the system to handle the event	}
			inMenuBar:  begin
				AdjustMenus;
				menuChoice := MenuSelect(gTheEvent.where);
				HandleMenuChoice(menuChoice);
			end;
			inDrag: 
				DragWindow(whichWindow, gTheEvent.where, screenBits.bounds);
					{ drag the window anywhere on the screen				}
			inContent: 
				if whichWindow <> FrontWindow then
					SelectWindow(whichWindow);		{ activates the clicked-in window			}
			inGrow:  begin
				windSize := GrowWindow(whichWindow, gTheEvent.where, gSizeRect);
		{ the number of pixels the window will grow or shrink in either direction		}
				if windSize <> 0 then begin
					GetPort(oldPort);
					SetPort(whichWindow);
					EraseRect(whichWindow^.portRect);
					SizeWindow(whichWindow, LoWord(windSize), HiWord(windSize), NORMAL_UPDATES);
		{ the LONGINT windSize dissected in LoWord and HiWord pixel numbers		}
		{ NORMAL_UPDATE tells the window manager to update if window has changed	}
					InvalRect(whichWindow^.portRect);
		{ add the entire contents of the window to the window's updateRgn			}
		{ alternatief voor het hele window updaten is de Region-update				}
					SetPort(oldPort);
				end;
			end;
			inGoAway: 
				gDone := TRUE;					{ we're done, quit the program			}
		{ netjes is anders: t.z.t. alleen window sluiten, niet de apllicatie			}
			inZoomIn, inZoomOut: 
				if TrackBox(whichWindow, gTheEvent.where, thePart) then begin
					GetPort(oldPort);
					SetPort(whichWindow);
					EraseRect(whichWindow^.portRect);
					ZoomWindow(whichWindow, thePart, LEAVE_WHERE_IT_IS);
					InvalRect(whichWindow^.portRect);
					SetPort(oldPort);
				end;
			otherwise begin
	{ al was het alleen maar om window clicks tijdens het ontwikkelen op te vangen 		}
			end;
		end;
	end;


procedure DrawMyPicture (drawingWindow: WindowPtr; thePicture: picHandle);					{ Mark & Reed p. 146		}
{ Hoe laat je een bestaand picture tekenen in een gegeven window? 					}
{ draws the picture handled by thePicture in the window pointed to by drawingWindow,	}
{ clipping the drawing so that the scroll bar and grow areas aren't overwritten			}
	var
		drawingClipRect, myRect: Rect;
		oldPort: GrafPtr;			{ to be saved pointer to the current GrafPort			}
		tempRgn: RgnHandle;		{ memory for a region 							}
		lengte, offset: longint;
		myPicPtr: PicPtr;	{ ik wil verdorie de plot in het clipboard kunnen zetten }

	procedure CenterPict (thePicture: PicHandle; var myRect: Rect);
	{ constructs a new Rect the size of thePicture, centering it in the original myRect 	}
		var
			wR, pictR: Rect;
		begin
			wR := myRect;
			if thePicture <> nil then
				pictR := thePicture^^.picFrame;
			myRect.top := (wR.bottom - wR.top - (pictR.bottom - pictR.top)) div 2 + wR.top;
			myRect.bottom := myRect.top + (pictR.bottom - pictR.top);
			myRect.left := (wR.right - wR.left - (pictR.right - pictR.left)) div 2 + wR.left;
			myRect.right := myRect.left + (pictR.right - pictR.left);
		end;

	begin
		GetPort(oldPort);			{ save a pointer to the current GrafPort				}
		SetPort(drawingWindow);	{ make drawingWindow the current GrafPort			}
		tempRgn := NewRgn;
	{ allocate memory for a region to save a copy of the current clipregion into tempRgn	}
		GetClip(tempRgn);					{ copy the current clip region into tempRgn	}
	{ GetClip resizes the region to accommodate the current clip region. IM I: 141, 166	}
		EraseRect(drawingWindow^.portRect);	{ erase the whole window				}
		drawGrowIcon(drawingWindow);		{ redraw the GrowIcon					}

		drawingClipRect := drawingWindow^.portRect;	{ set up clipping Rect				}
		drawingClipRect.right := drawingClipRect.right - SCROLL_BAR_PIXELS;
								{ exclude right scroll bar area			}
		myRect := drawingWindow^.portRect;	{ sets up a Rect the size of pictureWindow	}
	{ to fit the picture in, to pass as parameter to procedures						}
		CenterPict(thePicture, myRect);
		ClipRect(drawingClipRect);	{ set the clipping region to the now defined rectangle 	}
		DrawPicture(thePicture, myRect);	{ Toolbox, zie Chernicoff I: 249				}
	{ thePicture: PicHandle is het te tekenen picture, myRect is de rechthoek waarin 	}
	{ en er wordt natuurlijk in de current GrafPort getekend.						}
	{ Het picture wordt geschaald naar de maten van myRect, althans volgens Chernicoff, 	}
	{ maar in werkelijkheid wordt van een te omvangrijk picture het nodige weggesneden.	}
	{ Zie voor schaalmethoden Chernicoff I: 169								}

	{ De  figuur nu copiëren naar de desk scrap, om zo in een Word file te kunnen zetten}
	{ Zie Mark & Reed p. 306-308  }

		myPicPtr := gMyPicture^;  { gMyPicture is een handle, gMyPicture^ een masterpointer }
(*lengte := ZeroScrap;*)
	{altijd eerst deze functie aanroepen, creeert de scrap }
(*lengte := PutScrap(gMyPicture^^.picSize, 'PICT', myPicPtr);*)

		SetClip(tempRgn);			{ reset the ClipRect to the setting saved in tempRgn		}
		DisposeRgn(tempRgn);		{ release the memory allocated to tempRgn			}
		SetPort(oldPort);			{ set the current GrafPort back to the original setting	}
	end;

procedure HaalPICT (var thePicture: picHandle);
	begin
		thePicture := GetPicture(PICT_ID);	{ geef de handle de waarde die GetPicture oplevert:	}
	{ loads thePicture with a call to GetPicture, die zoekt de resource PICT 			}
	{ De function GetPicture allocates the memory for the picture itself, as well as for	}
	{ the pointer to the picture											}
	{ voor algemeen gebruik van de procedure thePicture  procedure-parameter maken	}
		if thePicture = nil then
			FoutBoodschap(WAAR_IS_HET_WINDOW);
	end;

procedure HandleEvent;
	{ retrieves an event from the eventqueue and processes the event 				}
	var
		theChar: CHAR;
		oldPort: GrafPtr;
		gotOne: BOOLEAN;
	begin
		if gWNEImplemented then			{ wat alleen voor oude Macs niet zo is		}
			gotOne := WaitNextEvent(everyEvent, gTheEvent, SLEEP, nil)
	{ 'everyEvent' op aanraden van Apple: signaleert alle mogelijke events			}
	{ gTheEvent is van type EventRecord = RECORD what: INTEGER; message: LONGINT;	}
	{ where: POINT; modifiers: INTEGER end;	Mark & Reed p. 117				}
		else begin
			SystemTask;
			gotOne := GetNextEvent(everyEvent, gTheEvent);
		end;
		if gotOne then
			case gTheEvent.what of		{ gaat alle mogelijke events na die '.what' kan bevatten	}
				nullEvent: 
					;
				mouseDown: 
					HandleMouseDown;
				mouseUp: 
					;
				keyUp: 
					;
				keyDown, autoKey:  begin
					theChar := CHR(BitAnd(gTheEvent.message, charCodeMask));
					if (BitAnd(gTheEvent.modifiers, cmdKey) <> 0) then begin
						AdjustMenus;
						HandleMenuChoice(MenuKey(theChar));
					end;
				end;
				updateEvt: 
					if not IsDAWindow(WindowPtr(gTheEvent.message)) then begin
		{updateEvts voor windows: bij meerdere windows  uitzoeken welk window;		}
		{ gTheEvent.message bevat de pointer naar het window dat volgens de 			}
		{ window manager een update moet hebben 							}
						GetPort(oldPort);
						SetPort(WindowPtr(gTheEvent.message));
						BeginUpdate(WindowPtr(gTheEvent.message));
		{ tells the window manager that something will be done about it				}
		{ Zie Mark & Reed p 138; Inside Macintosh I ch. 9 over updaten			}
						DrawMyPicture(WindowPtr(gTheEvent.message), gMyPicture);
		{ in this case by redrawing thePicture in gPictWindow					}
						EndUpdate(WindowPtr(gTheEvent.message));
		{ let the window manager know the condition hasbeen taken care of, else the	}
		{ will keep sending messages for updateEvts							}
						SetPort(oldPort);
					end;
				diskEvt: 
					;
				activateEvt: 
					if (WindowPtr(gTheEvent.message) = gWPtr) then begin
		{ activateEvts voor windows: bij meerdere windows uitzoeken welk window;	}
		{ gTheEvent.message bevat de pointer naar het window dat volgens de 			}
		{ window manager een activateEvt heeft geboekt						}
						DrawGrowIcon(WindowPtr(gTheEvent.message));
		{ redraw the GrowBox and the empty scroll bar areas					}
						if (BitAnd(gTheEvent.modifiers, activeFlag) = ACTIVATING) then
							;	{ check in the modifiers field: is the event  an activate or a deactivate event		}
					end;
				networkEvt, driverEvt: 
					;
				app1Evt, app2Evt, app3Evt: 
					;			{ gebruik van deze mogelijkheden wordt door Apple ontraden		}
				app4Evt: 
					;	{ to get resume and suspend events there must be a SIZE rsrc in the apllication 	}
			end;
	end;

procedure DrawAt (x, y: INTEGER; s: Str255);
	{ Draw string at this coordinate, similar to an x, y location 				}
	{  on a conventional computer terminal.								}
	{  Het zijn locale coordinaten! 									}
	const
		Xoffset = 5;   			{ Blank pixels in left border 					}
		Yoffset = 12;   		{ Blank pixels in top border 						}
	begin
		Moveto(Xoffset + x * gTextwidth, Yoffset + y * gTextHeight);
		DrawString(s);
	end;    { DrawAt }

procedure NewFont (fontNumber, pointSize: INTEGER);
	{ Select new font and adjust variables for DrawAt 						}
	begin
		TextFont(fontNumber); 			{ Use new font 						}
		TextSize(pointSize);			{ Use new size 						}
		gTextHeight := pointSize + 2; 		{ Calc new character height 				}
		gTextWidth := CharWidth('M'); 		{ Calc width of widest character 			}
	end;    { NewFont }

procedure DrawInteger (int: LONGINT);
	var
		s: Str255;
	begin
		NumToString(int, s);    			{ convert number to string 				}
		DrawString(s);            			{ display it 						}
	end;

procedure OpenNuHetWindow (var windowOpen: BOOLEAN);
	{ open window and attach quickDraw picture,  						}
	{ procedure ontleend aan Swan, 1987, p. 212 						}
	{ t.z.t. het openen van het window scheiden van het attachen van het picture		}
	{ De procedure moet worden opgevolgd met SluitNuHet Picture				}
{ Ik moet dit tzt natuurlijk op kunnen vangen in de normale procedure voor windows en pictures }
	var
		myRect: Rect; 				{ het window waarin wordt geplot 			}
	{ gMyPicture is het picture handle dat de window manager gebruikt om teken-	}
	{ opdrachten te bewaren in relocatable memory op de hoop 				}
	begin
		ShowWindow(gPlotWindow);
		if not windowOpen then begin
			gWPtr := GetNewWindow(WINDOW_Id, @gWrec, POINTER(-1));
							{ open de resource template en het window	}
			if gWPtr = nil then
				FoutBoodschap(WAAR_IS_HET_WINDOW);
		{ Een alternatief is: gWPtr := 								}
		{ NewWindow(nil, screenBits.bound,'Titel',WindowPtr(-1),			}
		{ NO_GO_AWAY,NIL_REF_CON); The field screenBits.bounds is declared 	}
		{ as a Rect and contains a rectangle that encloses the main Mac screen. 	}
			windowOpen := gWPtr <> nil;
			if windowOpen then begin
				SetPort(gWPtr); 		{ set the current grafport to this window		}
		{ zie Chernikoff I:122. GetPort geeft de pointer to the currentPort,		}
		{ SetPort bepaalt de curentPort (mits deze al bestaat, met OpenPort bv.) 	}
				ClipRect(gWPtr^.portRect); 	{ Not optional. Reduce the clipping region 	}
				myRect := gWPtr^.portRect;        	{ Maak rechthoek gelijk aan het window		}
				gMyPicture := OpenPicture(myRect);		{ Create a picture 			}
		{ OpenPicture starts a new picture on the heap and passes back a handle	}
		{ to the associated memory block 								}
		{ QuickDraw commando's gaan nu niet naar het scherm, maar naar de hoop	}
				NewFont(helvetica, 14);			{ voor te printen tekst					}
	{ tzt menu-gestuurd, maar eenvoudig weghalen levert nog niet als resultaat}
{dat de menu-keuze voor font en style wordt gevolgd ???? }
			end
		end
	end;

procedure StringToReal (s: str255; dec: INTEGER; var r: REAL);
	var
		s1, s2, s0: str255;
		i, r1, r2, d, p: LONGINT;
	begin
		p := pos(',', s);			{ op welke positie staat de comma?				}
		if p = 0 then				{ kijk of decimale punt is gebruikt				}
			p := pos('.', s);			{ op welke positie staat de punt?					}
		if p = 0 then begin			{ geheel getal opgegeven						}
			StringToNum(s, r1);
			r := r1;
		end
		else begin
			s1 := copy(s, 1, p - 1);		{ gehele gedeelte								}
			StringToNum(s1, r1);
			s2 := copy(s, p + 1, length(s));		{ decimale gedeelte					}
			StringToNum(s2, r2);
			if length(s2) < dec then
				dec := length(s2)
			else
				s2 := copy(s2, 1, dec);
	{ de constraint van aantal te gebruiken decimalen is niet in alle toepassingen		}
	{ zinvol of nodig, maar in andere wel, dus handhaven						}
			r := r1 + r2 / exp(ln(10) * (dec));	{ r2 delen door 10 tot de macht dec		}
		end;
	end;


procedure RealToString (r: REAL; dec: INTEGER; var s: str255);
	var
		s1, s2, s0: str255;
		i, r1, r2, d: longint;
	begin
		r1 := trunc(r);
		r2 := abs(round(exp(dec * ln(10)) * (r - r1)));
		NumToString(r1, s1); 		{ convert number to string 					}
		NumToString(r2, s2);
		d := dec - Length(s2);		{ leading zeros van het decimale deel tellen 		}
		if d <> 0 then
			for i := 1 to d do
				s2 := Concat('0', s2);
		if (r < 0) and (r > -1) then begin
			s1 := Concat('-', s1);
		end;
		s := Concat(s1, '.', s2);
		if dec = 0 then
			NumToString(round(r), s);
	end;

procedure DrawReal (r: REAL; dec: INTEGER);
	var
		s: str255;
	begin
		RealToString(r, dec, s);		{ convert number to string 					}
		DrawString(s);			{ and display it 							}
	end;

procedure WindowInit;
{ Loads WIND ID-number from the rsrc-file, stores a pointer to it in gDrawWindow }
	begin
		gPlotWindow := GetNewWindow(STANDAARD_ID, nil, WindowPtr(-1));
		gTellerWindow := GetNewWindow(STANDAARD_ID + 1, nil, WindowPtr(-1));
	end;


end.	{ of unit }











 { oktober 1991 }

unit WlbrMenus;

{ Interface-menu-procedures voor simulatie-}
{ en andere programma's voor het toetsmodel-project }


interface

uses
	PrintTraps;

const
	STANDAARD_ID = 128;

	{ de volgende 3 const corresponderen met ID's van 'STR ' resources 			}
	APPLE_MENU_ID = STANDAARD_ID;		{ MENU-ID }
	FILE_MENU_ID = STANDAARD_ID + 1;	{ MENU-ID }
	EDIT_MENU_ID = STANDAARD_ID + 2;	{ MENU_ID }

	ERROR_ALERT_ID = STANDAARD_ID + 1;	{ ALRT-ID }
	ABOUT_ITEM = 1;	{ apple-menu	}
	ABOUT_ALERT = STANDAARD_ID;

	KIES_PARAM = 1;	{Cmd K}
	EXTRA_ITEM = 2;	{Cmd E}
	START_ITEM = 3;	{Cmd G}
	CLOSE_ITEM = 4;
{---------------------}
	SAVE_AS_ITEM = 6;
	PRINT_ITEM = 7;	{Cmd P}
{---------------------}
	QUIT_ITEM = 9;	{Cmd Q}

{ items in the interactive dialog:	}
	OK_BUTTON = 1;
	ANNULEER_BUTTON = 2;
	N_PRTOETS = 3;			{ aantal vragen waaruit de proeftoets bestond			}
	SCORE_PRTOETS = 4;		{ op proeftoets behaalde score					}
	A_PRTOETS = 5;			{ BetaBinoVerdeling over proeftoetsscores 			}
	B_PRTOETS = 6;
	N_TOETS = 7;				{ aantal vragen dat de te voorspellen toets bevat		}
	A_UTILITEIT = 8;			{ cumulatieve betaverdeling voor de utiliteitsfunctie	}
	B_UTILITEIT = 9;
	CESUUR_TOETS = 10;		{ minimale score die nog 'succes' oplevert				}
	RAAD_KANS = 11;			{ raadkans; default is 5 %, er is altijd wel raadkans	}
	KENNIS_PLAFOND = 12;		{ maximale beheersing; default 95 % als realistisch	}
	VOOR_KENNIS = 13;			{ voorkennis waarmee het leren is gestart, default 5 %	}
	BREEDTE_PLOT = 14;		{ parameter voor dimensie van de plot				}
	HOOGTE_PLOT = 15;			{ parameter voor dimensie van de plot				}
	TIJD_SCHAAL_LENGTE = 16;	{ aantal tijdseenheden waarover wordt geplot			}
	MAX_STR_FIELDS = 16;	{ the number of stringfields above, plus OK & ANNULEER		}
	RADIO_SCORE = 17;		{ uitgangspunt is score op proefoets					}
	RADIO_VERDELING = 18;	{ uitgangspunt is verdeling van proeftoetsscores			}
	RADIO_OGIEF = 19;		{ een ogief als utiliteitsfunctie						}
	RADIO_DREMPEL = 20;	{ drempel-utiliteit								}
	PLOT_LEER_CURVE = 21;		{ leercurve plotten?							}
	PLOT_UTIL_CURVE = 22;		{ utiliteitscurve plotten? Bij keuze voor ogief nuttig	}
	PLOT_VERW_UTIL = 23;		{ dit is de normale output van het programma			}
	PLOT_UTIL_TIJD = 24;		{ dit is een alternatieve manier om de output te plotten	}
	RASTER_OF_NIET = 25;			{ plotten in een gerasterde figuur?			}
	SCHAAL_WAARDEN = 26;			{ schaalwaarden aangeven?					}
	PLOT_VTV = 40;			{plot bebi voorspellende toetsscoreverdeling}
	PLOT_BINO = 41;			{ plot binomiaal alsof proeftoetsresultaat ware beheersing is }
	SIMULEER = 42;			{ De te plotten verdeling baseren op simulatie? }
	PLOT_BETA = 43;			{ plot betaverdeling voor ware beheersing }
{BEREKENEN_OF_SIMULEREN 	Een toekomstige uitbreiding?	}
{AANTAL_TOETSTREKKINGEN 	idem						}

	ON = 1;						{ for radio- and check buttons in dialog			}
	OFF = 0;

	KEY_RETURN = 36;	{return key code, keyboard scan code, not the same as ASCII values! 	}
	KEY_ENTER = 76;	{ enter key code	}

	UNDO_ITEM = 1;
	CUT_ITEM = 3;
	COPY_ITEM = 4;
	PASTE_ITEM = 5;
	CLEAR_ITEM = 6;

	FONT_MENU_ID = 100;
	STYLE_MENU_ID = 101;
	NOT_A_NORMAL_MENU = -1;	{ niet direct op de menubalk te plaatsen				}
	ADD_CHECK_MARK = TRUE;	{ voor checks in font en style menu's				}
	REMOVE_CHECK_MARK = FALSE;
	PLAIN = [];
	PLAIN_ITEM = 1;
	BOLD_ITEM = 2;
	ITALIC_ITEM = 3;
	UNDERLINE_ITEM = 4;
	OUTLINE_ITEM = 5;
	SHADOW_ITEM = 6;

	WINDOW_ID = STANDAARD_ID;			{ WIND-ID }
	PICT_ID = STANDAARD_ID;			{ PICT-ID }

{ Foutmeldingen:	}
	WAAR_IS_HET_WINDOW = 1;  		{ correspondeert met "STR ' ID STANDAARD_ID + 1 }
	WAAR_IS_HET_MENU = 2;			{ correspondeert met "STR ' ID STANDAARD_ID + 2 }
	WAAR_IS_HET_PICT = 3;			{ correspondeert met "STR ' ID STANDAARD_ID + 3 }
	WAAR_IS_HET_MBAR = 4;			{ correspondeert met "STR ' ID STANDAARD_ID + 4 }
	HEADER_SIZE = 512;	{ used for removing the header at the top of PICT files	}

type
	ParameterStringArray = array[3..MAX_STR_FIELDS] of Str255;
(***** Let op, ik denk dat het string-array overbodig is, ik heb al het stringHandle array! ******)
	ParameterOnOffArray = array[MAX_STR_FIELDS..100] of INTEGER;
	ParameterStringArrayPtr = ^ParameterStringArray;
	ParameterOnOffArrayPtr = ^ParameterOnOffArray;
(* Het ParameterOnOffArray kan makkelijk worden uitgebouwd tot een array of REAL		*)
(* waarin de numerieke parametergegevens worden opgeslagen, voor eventueel meerdere	*)
(* series, en waarom zou ik niet de mogelijkheid tot 10 series geven?				*)


var							{ Hier ook alle globale variabelen die nodig zijn 	}
	gDone: BOOLEAN;				{ while gDone=FALSE do HandleEvent			}
	gIfNew: BOOLEAN;				{ als TRUE then nieuwe simulatie runnen		}
	gWNEImplemented: BOOLEAN;
	{ TRUE if WaitNextEvent is implemented in de current version of the system		}
	gTheEvent: EventRecord;			{ voor het signaleren en afhandelen van events 	}

	gMyPicture: PicHandle;   			{ het picture waarnaar QuickDraw wegschrijft	}
	gPlotWindow, gTellerWindow: windowPtr;		{ beide benodigde windows			}
	gWRec: windowrecord; 			{ Program's window data record; InsMac I: 276	}
	gWPtr: windowptr;				{ Pointer to above wRec 					}
	gWindowOpen: BOOLEAN;			{ TRUE only if window is open 				}
	gSizeRect: Rect;				{ controls the size of the window				}
	gTextWidth: INTEGER;			{ Maximum width of a character 				}
	gTextHeight: INTEGER; 			{ Height of a character 					}
	gCurrentStyle: Style;				{ voor style choice						}
	gLastFont: INTEGER;
	gSD: DialogPtr;			{ gSettingsDialog, pointer naar setings in de dialoog			}
	gPrintRecordH: THPrint;	{ handle to the print record you'll create			}
	gReply: SFReply;		{ will hold the data returned by the cal to SFGetFile		}

{ default string Handles to 'STR '.rsrc dialog items: 	}
(*    gDefNPrToetsH, gDefScorePrToetsH, gDefAPrToetsH, gDefBPrToetsH: StringHandle;*)
(*    gDefNToetsH, gDefAUtiliteitH, gDefBUtiliteitH, gDefCesuurToetsH: StringHandle;*)
(*    gDefHoogtePlotStrH, gDefBreedtePlotStrH, gDefTijdSchaalLengteH: StringHandle;*)
(*    gDefRaadKansStrH, gDefKennisPlafondStrH, gDefVoorKennisStrH: StringHandle;*)

	{ The following array holds handles to each edit text item in the dialog entry form.	}
	{ Copying the handles into a global array helps cut down the number of calls to the	}
	{ Dialog manager.  De first string has number 3 (1 en 2: OK en Cancel)  Swan, p. 303	}
	ItemHandles: array[3..MAX_STR_FIELDS] of Handle;
	DialStr: ParameterStringArrayPtr;
	DialRadio: ParameterOnOffArrayPtr;

	gLinks, gOnder, gBoven, yScale: INTEGER; 	{ Afbeeldingsparameters voor plot 			}
	i, j: INTEGER;	                                                 {tellers}

{ ik denk dat procedures die niet van buiten de unit worden aangeroepen, hier ook 		}
{ niet hoeven te worden gedeclareerd										}
function IsDAWindow (whichWindow: WindowPtr): BOOLEAN;
procedure SaveSettings;
procedure RestoreSettings;
procedure HandleDialog;
procedure CheckStyles;		{ update the check marks on the Style menu			}
procedure HandleStyleChoice (theItem: INTEGER);		{ IM I: 171 }
procedure HandleEditChoice (theItem: INTEger);
procedure HandleFontChoice (theItem: INTEGER);
procedure HandleFileChoice (theItem: INTEGER);
procedure HandleAppleChoice (theItem: INTEGER);
procedure AdjustMenus;
procedure HandleMenuChoice (menuChoice: LONGINT);
procedure FoutBoodschap (stringNum: INTEGER);
procedure MenuBarInit;
procedure DialogInit (var d: DialogPtr);
procedure ArrayInit;	{ array met strings voor de parameterwaarden			}

implementation


procedure PrintPlot;
	{ Mark & Reed Hoofdstuk 7 p. 337}
	{ zie Inside Macintosh IV, Ch. 1, for a detailed discussion of the File Manager		}
	{ Hier aangepast om de plot in het window te printen }

	const
		CANT_OPEN_FILE = STANDAARD_ID + 6;
		GET_EOF_ERROR = STANDAARD_ID + 7;
		HEADER_TOO_SMALL = STANDAARD_ID + 8;
		OUT_OF_MEMORY = STANDAARD_ID + 9;
		CANT_READ_HEADER = STANDAARD_ID + 10;
		CANT_READ_PICT = STANDAARD_ID + 11;
		NIL_STRING = '';
		IGNORED_STRING = NIL_STRING;

	procedure PrintPICT (thePict: picHandle);
	{ Mark & Reed Hoofdstuk 7 p. 337 }
		var
			printPort: TPPrPort;
			printStatus: TPrStatus;

		begin
	{ PrOpenDoc returns a pointer printPort to the printing grafPort:			}
			printPort := PrOpenDoc(gPrintRecordH, nil, nil);
			prOpenPage(printPort, nil);				{ open a new page			}
			DrawPicture(thePict, thePict^^.picFrame);	{ and draw the picture			}
			PrClosePage(printPort);					{ then close the page			}
			PrCloseDoc(printPort);					{ and the printing grafPort		}
			PrPicFile(gPrintRecordH, nil, nil, nil, printStatus);	{ This prints the file!	}
			HUnlock(Handle(thePict));				{ opruimen					}
		end;

	function DoDialogs: BOOLEAN;	{ do the Page Setup and Print Job dialogs		}
	(* dat kan worden verbeterd door die dialogen op te roepen via het menu, met		*)
	(* Page Setup en Print Job, je raadt het al								*)
		var
			keepGoing: BOOLEAN;
		begin
			keepGoing := PrStlDialog(gPrintRecordH);
			if keepGoing then
				DoDialogs := PrJobDialog(gPrintRecordH)
			else
				DoDialogs := FALSE;
		end;

	procedure PrintInit;
		begin
			gPrintRecordH := THPrint(NewHandle(sizeof(TPrint)));
	{ NewHandle allocates a block of memory to the size of a print record and makes 	}
	{ gPrintRecordH a handle to that memory							}
			PROpen;		{ start up the Printing Manager		}
			PrintDeFault(gPrintRecordH);	{ ensures that any changes made to the Page Setup	}
	{ and Print Job dialogs will be implemented when you print				}
		end;

	begin
		PrintInit;
		if DoDialogs then
			PrintPict(gMyPicture);
	end;


procedure SaveSettings;
{ fills the savedSettings data structure with the values currently set in the settings dialog 	}
	var
		T, itemNo: INTEGER;	{ hier slechts een filleritem	}
		R: Rect;	{ hier slechts een filleritem	}
		H: Handle;
	begin
	{ eerst voor de string-items:										}
		for itemNo := 3 to MAX_STR_FIELDS do begin	{ voor alle EditText items			}
			GetDItem(gSD, itemNo, T, H, R);				{ itemHandle H ophalen		}
			if H = nil then
				ExitToShell;		{ a field is missing. Foutmelding maken					}
			itemHandles[itemNo] := H;					{ itemHandle H opslaan			}
			GetIText(H, dialStr^[itemNo]);
		end;
	{ en dan voor de check en radio items:									}
		for itemNo := MAX_STR_FIELDS + 1 to 100 do begin	{ voor alle EditText items		}
			GetDItem(gSD, itemNo, T, H, R);				{ itemHandle H ophalen		}
			if H <> nil then begin
				dialRadio^[itemNo] := GetCtlValue(ControlHandle(H));
			end;
		end;
	end;

procedure RestoreSettings;
	{ restore the settings dialog items saved in the savedSettings data structure			}
	var
		itemNo, T: INTEGER;
		R: Rect;
		H: Handle;
	begin
	{ eerst voor de string-items:										}
		for itemNo := 3 to MAX_STR_FIELDS do begin	{ voor alle EditText items			}
			GetDItem(gSD, itemNo, T, H, R);				{ itemHandle H ophalen		}
			if H = nil then
				ExitToShell;		{ a field is missing. Foutmelding maken					}
			itemHandles[itemNo] := H;					{ itemHandle H opslaan			}
			SetIText(H, dialStr^[itemNo]);		{ InsMac I: 422; Zet tekst in dialoogvenster		}
	{ ik neem aan dat de Dialog en Resource Managers zorgen voor verwerking in de		}
	{ itemlijst en de kopie van de itemlijst, zie InsMac I: 414					}
		end;
	{ en dan voor de check en radio items:									}
		for itemNo := MAX_STR_FIELDS + 1 to 100 do begin	{ voor alle EditText items		}
			GetDItem(gSD, itemNo, T, H, R);				{ itemHandle H ophalen		}
			if H <> nil then begin
				SetCtlValue(ControlHandle(H), dialRadio^[itemNo]);
			end;
		end;
	end;

procedure HandleDialog;
	{ Ontleend aan Mark & Reed, p. 265. Zie ook Swan, p. 310					}
	var
		dialogDone, flag: BOOLEAN;
		iHit, T, dummy: INTEGER;
		R: Rect;
		H: Handle;
		s: Str255;

	procedure afmaken;
		begin
			if GetCtlValue(ControlHandle(H)) = ON then
				SetCtlValue(ControlHandle(H), OFF)
			else
				SetCtlValue(ControlHandle(H), ON);
		end;

	begin

{ Dan moet ik het probleem oplossen waarom klikken in 3 wel resultaat heeft in 4 en 5, }
{en het omgekeerde niet het geval is. Dat heeft te maken met het enabled zijn van de items! }

{ Het window opent met de tekststrings uit de DITL, niet uit 'STR ', geïnstalleerd		}
		ShowWindow(gSD);				{ Er moet wel een open dialoog-window te zien zijn	}
			{ Bewaar huidige setting, voor geval van 'Cancel'	}
		dialogDone := FALSE;

		while dialogDone = FALSE do begin		{ rustig alle items afwerken				}
			ModalDialog(nil, iHit);
			case iHit of

				RADIO_SCORE:  begin			{ af wanneer aan, aan wanneer af			 17	}
					GetDItem(gSD, RADIO_SCORE, T, H, R);
					afmaken
				end;
				RADIO_VERDELING:  begin	{ af wanneer aan, aan wanneer af				{ 18	}
					GetDItem(gSD, RADIO_VERDELING, T, H, R);
					afmaken
				end;
				RADIO_OGIEF:  begin	{ af wanneer aan, aan wanneer af					{ 19	}
					GetDItem(gSD, RADIO_OGIEF, T, H, R);
					afmaken
				end;
				RADIO_DREMPEL:  begin	{ af wanneer aan, aan wanneer af				{ 20	}
					GetDItem(gSD, RADIO_DREMPEL, T, H, R);
					afmaken
				end;
				PLOT_LEER_CURVE:  begin
					GetDItem(gSD, PLOT_LEER_CURVE, T, H, R);						{ 21	}
					afmaken
				end;
				PLOT_UTIL_CURVE:  begin										{ 22	}
					GetDItem(gSD, PLOT_UTIL_CURVE, T, H, R);
					afmaken
				end;
				PLOT_VTV:  begin										{ 22	}
					GetDItem(gSD, PLOT_VTV, T, H, R);
					afmaken
				end;
				PLOT_BINO:  begin										{ 22	}
					GetDItem(gSD, PLOT_BINO, T, H, R);
					afmaken
				end;
				PLOT_VERW_UTIL:  begin										{ 23 	}
					GetDItem(gSD, PLOT_VERW_UTIL, T, H, R);
					afmaken
				end;
				PLOT_UTIL_TIJD:  begin										{ 24 	}
					GetDItem(gSD, PLOT_UTIL_TIJD, T, H, R);
					afmaken
				end;
				PLOT_BETA:  begin										{ 24 	}
					GetDItem(gSD, PLOT_BETA, T, H, R);
					afmaken
				end;
				SIMULEER:  begin										{ 22	}
					GetDItem(gSD, SIMULEER, T, H, R);
					afmaken
				end;
				RASTER_OF_NIET:  begin										{ 25 	}
					GetDItem(gSD, RASTER_OF_NIET, T, H, R);
					afmaken
				end;
				SCHAAL_WAARDEN:  begin										{ 26	}
					GetDItem(gSD, SCHAAL_WAARDEN, T, H, R);
					afmaken
				end;

				OK_BUTTON:  begin				{ save de instellingen				1	}
					HideWindow(gSD);				{ Close dialoog-window, settings are done	}
					SaveSettings;					{ Bewaar nieuwe instellingen			}
					dialogDone := TRUE;
					gIfNew := TRUE;		{ vlag voor uitvoeren van berekeningen en voor plotten	}
	(* als meerdere series tegelijk worden opgegeven, dan OK en RUN weer  loskoppelen!	*)
				end;
				ANNULEER_BUTTON:  begin										{ 2	}
					HideWindow(gSD);				{ Close window first, then restore settings	}
					RestoreSettings;
					dialogDone := TRUE;
				end;
			end;
		end;
	end;

procedure HandleEditChoice (theItem: INTEger);
{ HandleEditChoice calls SystemEdit. If the active window belongs to a desk accessory,	}
{ SystemEdit passes the appropriate command to the accessory. Otherwise, it returns	}
{ FALSE, and your application should then handle the edit command. 				}
	var
		dummy: BOOLEAN;
	begin
		dummy := SystemEdit(theItem - 1);
	end;

procedure HandleFileChoice (theItem: INTEGER);
	(* t.z.t. uitbreiden met SAVE mogelijkheid						*)
	(* t.z.t. uitbreiden met de mogelijkheid om bewaarde PICT files te bekijken		*)
	var
		whichWindow: WindowPtr;
		nieuw: BOOLEAN;
	begin
		case theItem of
			KIES_PARAM:  begin
				HandleDialog;
			end;
			EXTRA_ITEM:  begin
	{ de dialoog nu gebruiken om parameters voor extra plot in te lezen				}
	{ Maximum stellen aan het aantal extra plots: bijvoorbeeld 10					}
	{ enabled maken wanneer een eerdere set parameters is ingevoerd				}
				HandleDialog;
			end;
			START_ITEM:  {Create New Window}
				begin
				gIfNew := TRUE;		{ vlag voor uitvoeren van berekeningen en voor plotten		}
			end;
			PRINT_ITEM: 
				PrintPlot;
			CLOSE_ITEM:  begin
				whichWindow := FrontWindow;
				if whichWindow <> nil then
					DisposeWindow(whichWindow);
			end;
			QUIT_ITEM:  begin
				whichWindow := FrontWindow;
				if whichWindow <> nil then
					DisposeWindow(whichWindow);
				gDone := TRUE;
			end;
		end;
	end;

procedure HandleAppleChoice (theItem: INTEGER);
	{ handles all the apple-menu selections									}
	var
		accName: Str255;
		accNumber, itemNumber, dummy: INTEGER;
		appleMenu: MenuHandle;
	begin
		case theItem of
			ABOUT_ITEM: 
				dummy := NoteAlert(ABOUT_ALERT, nil);
			otherwise begin
				appleMenu := GetMHandle(APPLE_MENU_ID);
				GetItem(appleMenu, theItem, accName);
				accNumber := OpenDeskAcc(accName);
			end;
		end;
	end;

procedure HandleMenuChoice (menuChoice: LONGINT);
	var
		theMenu, theItem, dummy: INTEGER;
	begin
		if menuChoice <> 0 then begin
			theMenu := HiWord(menuChoice);	{ the first two bytes contain the menu selected,		}
			theItem := LoWord(menuChoice);	{ the last two the item selected from that menu	}
			case theMenu of
				APPLE_MENU_ID: 
					HandleAppleChoice(theItem);
				FILE_MENU_ID: 
					HandleFileChoice(theItem);
				EDIT_MENU_ID: 
					HandleFileChoice(theItem); (* ******** dit klopt toch niet? ****** *)
				FONT_MENU_ID: 
					HandleFontChoice(theItem);
				STYLE_MENU_ID: 
					HandleStyleChoice(theItem);
				otherwise
					dummy := 0;
			end;
			HiLiteMenu(0);					{ uninvert the selected menu 			}
		end;
	end;

function IsDAWindow (whichWindow: WindowPtr): BOOLEAN;
	begin
		if whichWindow = nil then
			IsDAWindow := FALSE
		else
			IsDAWindow := (WindowPeek(whichWindow)^.windowKind < 0);
	{ the field windowKind is positive if the window is an application window,		}
	{ negative if it is a desk accessory window							}
	end;

procedure AdjustMenus;
{ enables and disables the items in the Edit menu, depending on whether the current 	}
{ window is a desk accessory window or not. T.z.t. veralgemenen dus				}
{ Mark & Reed, p. 300. Works together with procedure IsDAWindow				}
	var
		aMenu: MenuHandle;

	begin
		aMenu := GetMHandle(FILE_MENU_ID);
		if FrontWindow = nil then
			DisableItem(aMenu, CLOSE_ITEM)
		else
			EnableItem(aMenu, CLOSE_ITEM);

		aMenu := GetMHandle(EDIT_MENU_ID);
		if IsDAWindow(FrontWindow) then begin
			EnableItem(aMenu, UNDO_ITEM);
			EnableItem(aMenu, CUT_ITEM);
			EnableItem(aMenu, COPY_ITEM);
			EnableItem(aMenu, PASTE_ITEM);
			EnableItem(aMenu, CLEAR_ITEM);
		end
		else begin
			DisableItem(aMenu, UNDO_ITEM);
			DisableItem(aMenu, CUT_ITEM);
			DisableItem(aMenu, COPY_ITEM);
			DisableItem(aMenu, PASTE_ITEM);
			DisableItem(aMenu, CLEAR_ITEM);
		end;
	end;

procedure CheckStyles;		{ update the check marks on the Style menu			}
	var
		styleMenu: MenuHandle;
	begin
		styleMenu := GetMHandle(STYLE_MENU_ID);
		CheckItem(styleMenu, PLAIN_ITEM, (gCurrentStyle = PLAIN));
		CheckItem(styleMenu, BOLD_ITEM, (bold in gCurrentStyle));
		CheckItem(styleMenu, ITALIC_ITEM, (italic in gCurrentStyle));
		CheckItem(styleMenu, UNDERLINE_ITEM, (underline in gCurrentStyle));
		CheckItem(styleMenu, OUTLINE_ITEM, (outline in gCurrentStyle));
		CheckItem(styleMenu, SHADOW_ITEM, (shadow in gCurrentStyle));
	end;

procedure HandleStyleChoice (theItem: INTEGER);		{ IM I: 171 }
	begin
		case theItem of
			PLAIN_ITEM: 
				gCurrentStyle := [];	{ als PLAIN ipv [], dan out of range melding ??????		}
			BOLD_ITEM: 
				if bold in gCurrentStyle then
					gCurrentStyle := gCurrentStyle - [bold]
				else
					gCurrentStyle := gCurrentStyle + [bold];
			ITALIC_ITEM: 
				if bold in gCurrentStyle then
					gCurrentStyle := gCurrentStyle - [italic]
				else
					gCurrentStyle := gCurrentStyle + [italic];
			UNDERLINE_ITEM: 
				if bold in gCurrentStyle then
					gCurrentStyle := gCurrentStyle - [underline]
				else
					gCurrentStyle := gCurrentStyle + [underline];
			OUTLINE_ITEM: 
				if bold in gCurrentStyle then
					gCurrentStyle := gCurrentStyle - [outline]
				else
					gCurrentStyle := gCurrentStyle + [outline];
			SHADOW_ITEM: 
				if bold in gCurrentStyle then
					gCurrentStyle := gCurrentStyle - [shadow]
				else
					gCurrentStyle := gCurrentStyle + [shadow];
		end;
		CheckStyles;				{ update the check marks on the Style menu			}
		TextFace(gCurrentStyle);	{ implement the styles in gCurrentStyle				}
	end;

procedure HandleFontChoice (theItem: INTEGER);
	{ is called when the FONT item in the SPECIAL menu is selected						}
	var
		fontNumber: INTEGER;
		fontName: Str255;
		fontMenu: MenuHandle;
	begin
		fontMenu := GetMHandle(FONT_MENU_ID);			{ get the font's menu handle	}
		CheckItem(fontMenu, gLastFont, REMOVE_CHECK_MARK);	{ removes check mark		}
		CheckItem(fontMenu, theItem, ADD_CHECK_MARK);	{ check mark newly selected font	}
		gLastFont := theItem;						{ set to the selected item number	}
		GetItem(fontMenu, theItem, fontName);	{ returns the fontName for the menu selection	}
		getFNum(fontName, fontNumber);		{ provides the fontNumber given the fontName}
		TextFont(fontNumber);				{ changes textfont to the new font ID number	}
	end;

procedure FoutBoodschap (stringNum: INTEGER);
	{ input is een fout ID; laadtr de 'STR ' resource met dat ID, 					}
	{ gebruikt StopAlert voor de mededeling op het scherm 						}
	const
		NIL_STRING = '';
	var
		errorStringH: StringHandle;
		dummy: INTEGER;
	begin
		errorStringH := GetString(STANDAARD_ID + stringNum);
		if errorStringH = nil then	{ de benodigde 'STR ' resource is niet gevonden	}
			ParamText('Er is een fout, de beschrijving van de fout is helaas zoek', NIL_STRING, NIL_STRING, NIL_STRING)
		else
			ParamText(errorStringH^^, NIL_STRING, NIL_STRING, NIL_STRING);
	{ ParamText, zie Macintosh Revealed II: 366, definieert tot 4 strings voor 		}
	{ static text items, voor de plaatsvervangers ^0, ^1, ^2, en ^3 resp. 				}
	{ Deze strings worden pas gewijzigd bij een volgende aanroep van ParamText 		}
		SysBeep(1);							{ waarschuwingssignaal 1 seconde 	}
		dummy := StopAlert(ERROR_ALERT_ID, nil);
		{ Voor StopAlert, zie Inside Macintosh II: 358 						}
		{ NB: Als de resource met ResEdit geopend staat, dan werkt StopAlert niet		}
		ExitToShell;		{ returns control of the Macintosh to the Finder				}
	end;


procedure MenuBarInit;
	var
		myMenuBar: Handle;
		aMenu: MenuHandle;
	begin
		myMenuBar := GetNewMBar(STANDAARD_ID);
		if myMenuBar = nil then
			FoutBoodschap(WAAR_IS_HET_MBAR);
		SetMenuBar(myMenuBar);
		DisPosHandle(myMenuBar);
{ Mark & Reed p. 183 gebruiken dan DisposHandle(myMenuBar), maar doen dat niet 		}
{ in het programma WindowMaker. Ik weet niet of die DisposHandle gewenst is 			}
		aMenu := GetMHandle(APPLE_MENU_ID);
		if aMenu = nil then
			FoutBoodschap(WAAR_IS_HET_MENU);
		AddResMenu(aMenu, 'DRVR');		{ adds desk accessories to apple menu 			}
		aMenu := GetMenu(FONT_MENU_ID);
		if aMenu = nil then
			FoutBoodschap(WAAR_IS_HET_MENU);
		InsertMenu(aMenu, NOT_A_NORMAL_MENU);	{ voor hierarchisch menu	}
		AddResMenu(aMenu, 'FONT');	{ adds the names of all resources of type FONT to the menu }

		aMenu := GetMenu(STYLE_MENU_ID);
		if aMenu = nil then
			FoutBoodschap(WAAR_IS_HET_MENU);
		InsertMenu(aMenu, NOT_A_NORMAL_MENU);	{ voor hierarchisch menu	}
		CheckItem(aMenu, PLAIN_ITEM, TRUE);		{ place a checkmark next to 'plain'		}

		DrawMenuBar;
		gLastFont := 1;
		gCurrentStyle := [];
		HandleFontChoice(gLastFont);	{ sets the current font to the first font in the font menu	}
	end;

procedure ArrayInit;	{ arrays met strings en ON/OFF voor de parameterwaarden	}
{  Creates the array of strings for parameters as a non-relocatable object on the heap.		}
{ That is OK, becaue it is not a small array, and the the array is always in memory while	}
{ the program runs. It is placed as low on the heap as possible, to avoid fragmenting the	}
{ heap and to let the program use simpler pointer adressing.     Swan, p . 312 , 323.		}
	begin
		dialStr := ParameterStringArrayPtr(NewPtr(SizeOf(ParameterStringArray)));
		dialRadio := ParameterOnOffArrayPtr(NewPtr(SizeOf(ParameterOnOffArray)));
	{ reserves heap space for the array, by calling NewPtr, requesting the SizeOf the	}
	{ DataArray type in bytes and casting the resulting plain pointer to a DataArrayPtr 	}
	{ type, assigning this value to theData, the pointer to the array					}
		if MemError <> NoErr then begin
	{ checks that this works; if not, there wasn't enough memory available for the array	}
	(*DisplayError(1, 'Not enough memory', 'Set MaxRec to lower value', StopError);*)
	(* t.z.t. hier een behoorlijke foutmelding van maken *)
			ExitToShell
		end;
	end;

procedure DialogInit (var d: DialogPtr);
{ Voor een voorbeeld, zie Swan, p. 312	}
	var
		itemNo, i, T: INTEGER;
		H: Handle;
		R: Rect;

	begin

		d := GetNewDialog(STANDAARD_ID, nil, WindowPtr(-1));
	{ resource ID; create template in heap; make frontmost window					}

	(* if d=nil then ExitToShell;   Foutmelding installeren  *)
	{ settings dialog is loaded from the resource file. Zie bv. Swan p. 275.			}
	{ dialog window is set up as invisible; ShowWindow makes it visible				}
	{ NB: In het DLOG rscr wordt verwezen naar een DITL ID, zorg ervoor dat die DITL er 	}
	{ inderdaad is, en het juiste ID heeft!									}

	{ Copy handles of each edit text item in the dialog. This makes transferring strings 	}
	{ to and from the dialog easier and avoids calling GetDItem too often				}


		if d <> nil then begin
	{ De window Manager heeft de teksten uit de DITL gehaald, en in een itemlist gezet,	}
	{ maar het is handig de handles voor die teksten in een array beschikbaar te hebben.	}
	{ Omdat de teksten in de bij de DLOG behorende DITL zijn gegeven, zijn deze als		}
	{ defaultwaarden onmiddellijk in het dialoogvenster ingevuld.					}
			for itemNo := 3 to MAX_STR_FIELDS do begin	{ voor alle EditText items		}
				GetDItem(d, itemNo, T, H, R);				{ itemHandle H ophalen		}
				if H = nil then
					ExitToShell;		{ a field is missing. t.z.t. foutmelding maken. Fout kan onstaan 	}
	{ wanneer bij de RUN OPTIONS de WlbrMenus-RSRC-file niet is aangekoppeld			}
				itemHandles[itemNo] := H;					{ itemHandle H opslaan			}
			end;
			for itemNo := RADIO_SCORE to SCHAAL_WAARDEN do { for itemNo:=17 to 26 do }
				begin
				GetDItem(d, itemNo, T, H, R);
				if itemNo = RADIO_VERDELING then
					SetCtlValue(ControlHandle(H), OFF)
				else if itemNo = RADIO_DREMPEL then
					SetCtlValue(ControlHandle(H), OFF)
				else if itemNo = PLOT_LEER_CURVE then
					SetCtlValue(ControlHandle(H), OFF)
				else if itemNo = PLOT_UTIL_CURVE then
					SetCtlValue(ControlHandle(H), OFF)
				else if itemNo = PLOT_VERW_UTIL then
					SetCtlValue(ControlHandle(H), OFF)
				else if itemNo = PLOT_UTIL_TIJD then
					SetCtlValue(ControlHandle(H), OFF)
				else if itemNo = PLOT_BETA then
					SetCtlValue(ControlHandle(H), ON)
				else if itemNo = RASTER_OF_NIET then
					SetCtlValue(ControlHandle(H), ON)
				else
					SetCtlValue(ControlHandle(H), ON);
	{ de procedure SetCtlValue bij enabled items heeft direct effect in window			}
			end;
			GetDItem(d, PLOT_VTV, T, H, R);
			SetCtlValue(ControlHandle(H), OFF);
			GetDItem(d, PLOT_BINO, T, H, R);
			SetCtlValue(ControlHandle(H), OFF);
			GetDItem(d, SIMULEER, T, H, R);
			SetCtlValue(ControlHandle(H), OFF);
			SaveSettings;
	{ zodat ook bij niet openen van het dialoogvenster de defaultwaarden beschikbaar zijn	}
		end;	{ if d <> nil then }
	end;


end.	{ of unit }










procedure PrintPlot;
	{ Mark & Reed Hoofdstuk 7 p. 337}
	{ zie Inside Macintosh IV, Ch. 1, for a detailed discussion of the File Manager		}

const
	CANT_OPEN_FILE = STANDAARD_ID + 6;
	GET_EOF_ERROR = STANDAARD_ID + 7;
	HEADER_TOO_SMALL = STANDAARD_ID + 8;
	OUT_OF_MEMORY = STANDAARD_ID + 9;
	CANT_READ_HEADER = STANDAARD_ID + 10;
	CANT_READ_PICT = STANDAARD_ID + 11;
	NIL_STRING = '';
	IGNORED_STRING = NIL_STRING;

procedure PrintPICTFile (reply: SFReply);
	{ Mark & Reed Hoofdstuk 7 p. 337 }
	var
		srcFile: INTEGER;
		printPort: TPPrPort;
		printStatus: TPrStatus;
		thePict: PicHandle;
		pictHeader: packed array[0..HEADER_SIZE] of CHAR;
		pictSize, headerSize: LONGINT;
		dummy: OSErr;
	begin
	{ start off with a call to FSOpen to get the access path of the file selected 		}
		if (FSOpen(reply.fName, reply.vRefNum, srcFile) <> noErr) then
	{ FSOpen opens the specified file for eading and / or writing , depending on 	}
	{ the file's open permission. vRefNum specifies the file's volume and directory	}
			FoutBoodschap(CANT_OPEN_FILE);
	{ GetEOF returns the size of the file in pictSize:						}
		if (GetEOF(srcFile, pictSize) <> noErr) then
			FoutBoodschap(GET_EOF_ERROR);
		headerSize := HEADER_SIZE;
	{ FSReader attempts to read 512-byte header that describes the rest of the file,	}
	{ the actual number of bytes read is returned in the parameter headerSize		}
		if (FSRead(srcFile, headerSize, @pictHeader) <> noErr) then
			FoutBoodschap(CANT_READ_HEADER);
		pictSize := pictSize - HEADER_SIZE;	{ printPict won't need the 512-byte  header	}
		if pictSize <= 0 then
			FoutBoodschap(HEADER_TOO_SMALL);
		thePict := PicHandle(NewHandle(pictSize));
		if thePict = nil then
			FoutBoodschap(OUT_OF_MEMORY);
		HLock(Handle(thePict));		{ FSRead requires a pointer to the read buffer,	}
	{ and because you allocated a handle thePict, you have to HLock the handle		}
	{ before you pass its pointer thePict^ to FSRead.						}
	{ read in the PICT:											}
		if (FSRead(srcFile, pictSize, Ptr(thePict^)) <> noErr) then
			FoutBoodschap(CANT_READ_PICT);
		dummy := FSClose(srcFile);	{ the PICT is in memory, so close the PICT file	}
	{ PrOpenDoc returns a pointer printPort to the printing grafPort:			}
		printPort := PrOpenDoc(gPrintRecordH, nil, nil);
		prOpenPage(printPort, nil);				{ open a new page			}
		DrawPicture(thePict, thePict^^.picFrame);	{ and draw the picture			}
		PrClosePage(printPort);					{ then close the page			}
		PrCloseDoc(printPort);					{ and the printing grafPort		}
		PrPicFile(gPrintRecordH, nil, nil, nil, printStatus);	{ This prints the file!	}
		HUnlock(Handle(thePict));				{ opruimen					}
	end;

function DoDialogs: BOOLEAN;	{ do the Page Setup and Print Job dialogs		}
	(* dat kan worden verbeterd door die dialogen op te roepen via het menu, met		*)
	(* Page Setup en Print Job, je raadt het al								*)
	var
		keepGoing: BOOLEAN;
	begin
		keepGoing := PrStlDialog(gPrintRecordH);
		if keepGoing then
			DoDialogs := PrJobDialog(gPrintRecordH)
		else
			DoDialogs := FALSE;
	end;

procedure GetFileName (var replyPtr: SFReply);
	{ sets up the arguments and calls SFGetFile							}
	var
		myPoint: Point;
		typelist: SFTypelist;
		numTypes: INTEGER;
	begin
		myPoint.h := 100;
		myPoint.v := 100;
		typeList[0] := 'PICT';
		numTypes := 1;
	{ set to 1, so you need to set up a single entry in the typeList array			}
		SFGetFile(myPoint, IGNORED_STRING, nil, numTypes, typelist, nil, replyPtr);
	{ SFGetFile displays the standard open dialog on the screen at the point myPoint	}
{ Mark & Reed, p. 321. }
	end;

procedure PrintInit;
	begin
		gPrintRecordH := THPrint(NewHandle(sizeof(TPrint)));
	{ NewHandle allocates a block of memory to the size of a print record and makes 	}
	{ gPrintRecordH a handle to that memory							}
		PROpen;		{ start up the Printing Manager		}
		PrintDeFault(gPrintRecordH);	{ ensures that any changes made to the Page Setup	}
	{ and Print Job dialogs will be implemented when you print				}
	end;

begin
	PrintInit;
	GetFileName(gReply);
	if gReply.good then begin
		if DoDialogs then
			PrintPictFile(gReply)
	end;
end;











{	This file has been processed by The THINK Pascal Source Converter, v1.1.	}

{}
{Created: Tuesday, August 2, 1988 at 9:06 AM}
{	PrintTraps.p}
{	Pascal Interface to the Macintosh Libraries}
{}
{	Copyright Apple Computer, Inc.	1985-1988}
{	All rights reserved}
{}


unit PrintTraps;
interface
const
	bDraftLoop = 0;
	bSpoolLoop = 1;
	iPFMaxPgs = 128;						{Max number of pages in a print file.}
	iPrPgFract = 120;
	iPrPgFst = 1;							{Page range constants}
	iPrPgMax = 9999;
	iPrRelease = 2; 						{Current version number of the code.}
	iPrSavPFil = -1;
	iIOAbort = -27;
	pPrGlobals = $00000944;
	bUser1Loop = 2;
	bUser2Loop = 3;
	fNewRunBit = 2; 						{Bit 2 (3rd bit) in bDocLoop is new JobRun indicator.}
	fHiResOK = 3;							{Bit 3 (4th bit) in bDocLoop is hi res indicator for paint.}
	fWeOpenedRF = 4;						{Bit 4 (5th bit) in bDocLoop is set if driver opened the pr res file.}
	iPrAbort = 128;
	iPrDevCtl = 7;							{The PrDevCtl Proc's ctl number}
	lPrReset = $00010000;					{The PrDevCtl Proc's CParam for reset}
	lPrLineFeed = $00030000;
	lPrLFStd = $0003FFFF;					{The PrDevCtl Proc's CParam for std paper advance}
	lPrLFSixth = $0003FFFF;
	lPrPageEnd = $00020000; 				{The PrDevCtl Proc's CParam for end page}
	lPrDocOpen = $00010000; 				{note: same as lPrReset low order byte indicates number of copies to print}
	lPrPageOpen = $00040000;
	lPrPageClose = $00020000;				{note: same as lPrPageEnd}
	lPrDocClose = $00050000;
	iFMgrCtl = 8;							{The FMgr's Tail-hook Proc's ctl number}
	iMscCtl = 9;							{Msc Text state / Drvr State ctl number}
	iPvtCtl = 10;							{Private ctls start here}
	iMemFullErr = -108;

{}
{Driver constants}

	iPrBitsCtl = 4; 						{The Bitmap Print Proc's ctl number}
	lScreenBits = 0;						{The Bitmap Print Proc's Screen Bitmap param}
	lPaintBits = 1; 						{The Bitmap Print Proc's Paint [sq pix] param}
	lHiScreenBits = $00000002;				{The Bitmap Print Proc's Screen Bitmap param}
	lHiPaintBits = $00000003;				{The Bitmap Print Proc's Paint [sq pix] param}
	iPrIOCtl = 5;							{The Raw Byte IO Proc's ctl number}
	iPrEvtCtl = 6;							{The PrEvent Proc's ctl number}
	lPrEvtAll = $0002FFFD;					{The PrEvent Proc's CParam for the entire screen}
	lPrEvtTop = $0001FFFD;					{The PrEvent Proc's CParam for the top folder}
	iPrDrvrRef = -3;
	getRslDataOp = 4;						{PrGeneral Cs}
	setRslOp = 5;							{PrGeneral Cs}
	draftBitsOp = 6;						{PrGeneral Cs}
	noDraftBitsOp = 7;						{PrGeneral Cs}
	getRotnOp = 8;							{PrGeneral Cs}
	NoSuchRsl = 1;							{PrGeneral Cs}
	RgType1 = 1;							{PrGeneral Cs}


type

	TFeed = (feedCut, feedFanfold, feedMechCut, feedOther);

	TScan = (scanTB, scanBT, scanLR, scanRL);


	TPPrPort = ^TPrPort;
	TPrPort = record
			gPort: GrafPort;					{The Printer's graf port.}
			gProcs: QDProcs;					{..and its procs}
			lGParam1: LONGINT;					{16 bytes for private parameter storage.}
			lGParam2: LONGINT;
			lGParam3: LONGINT;
			lGParam4: LONGINT;
			fOurPtr: BOOLEAN;					{Whether the PrPort allocation was done by us.}
			fOurBits: BOOLEAN;					{Whether the BitMap allocation was done by us.}
		end;

{ Printing Graf Port. All printer imaging, whether spooling, banding, etc, happens "thru" a GrafPort.}
{This is the "PrPeek" record.}

	TPPrInfo = ^TPrInfo;
	TPrInfo = record
			iDev: INTEGER;						{Font mgr/QuickDraw device code}
			iVRes: INTEGER; 					{Resolution of device, in device coordinates}
			iHRes: INTEGER; 					{..note: V before H => compatable with Point.}
			rPage: Rect;						{The page (printable) rectangle in device coordinates.}
		end;

{ Print Info Record: The parameters needed for page composition. }
	TPPrStl = ^TPrStl;
	TPrStl = record
			wDev: INTEGER;						{The device (driver) number. Hi byte=RefNum, Lo byte=variant. f0 = fHiRes f1 = fPortrait, f2 = fSqPix, f3 = f2xZoom, f4 = fScroll.}
			iPageV: INTEGER;					{paper size in units of 1/iPrPgFract}
			iPageH: INTEGER;					{ ..note: V before H => compatable with Point.}
			bPort: SignedByte;					{The IO port number. Refnum?}
			feed: TFeed;						{paper feeder type.}
		end;

{ Printer Style: The printer configuration and usage information. }
	TPPrXInfo = ^TPrXInfo;
	TPrXInfo = record
			iRowBytes: INTEGER;
			iBandV: INTEGER;
			iBandH: INTEGER;
			iDevBytes: INTEGER;
			iBands: INTEGER;
			bPatScale: SignedByte;
			bUlThick: SignedByte;
			bUlOffset: SignedByte;
			bUlShadow: SignedByte;
			scan: TScan;
			bXInfoX: SignedByte;
		end;

	TPPrJob = ^TPrJob;
	TPrJob = record
			iFstPage: INTEGER;
			iLstPage: INTEGER;
			iCopies: INTEGER;
			bJDocLoop: SignedByte;
			fFromUsr: BOOLEAN;
			pIdleProc: ProcPtr;
			pFileName: StringPtr;
			iFileVol: INTEGER;
			bFileVers: SignedByte;
			bJobX: SignedByte;
		end;

	TPrFlag1 = packed record
			f15: BOOLEAN;
			f14: BOOLEAN;
			f13: BOOLEAN;
			f12: BOOLEAN;
			f11: BOOLEAN;
			f10: BOOLEAN;
			f9: BOOLEAN;
			f8: BOOLEAN;
			f7: BOOLEAN;
			f6: BOOLEAN;
			f5: BOOLEAN;
			f4: BOOLEAN;
			f3: BOOLEAN;
			f2: BOOLEAN;
			fLstPgFst: BOOLEAN;
			fUserScale: BOOLEAN;
		end;

	TPPrint = ^TPrint;
	THPrint = ^TPPrint;
	TPrint = record
			iPrVersion: INTEGER;
			prInfo: TPrInfo;
			rPaper: Rect;
			prStl: TPrStl;
			prInfoPT: TPrInfo;
			prXInfo: TPrXInfo;
			prJob: TPrJob;
			case INTEGER of
				0: (
						printX: array[1..19] of INTEGER
				);
				1: (
						prFlag1: TPrFlag1; 			{a word of flags}
						iZoomMin: INTEGER;
						iZoomMax: INTEGER;
						hDocName: StringHandle
				);		{current doc's name, nil = front window}
		end;

	TPPrStatus = ^TPrStatus;
	TPrStatus = record
			iTotPages: INTEGER; 				{Total pages in Print File.}
			iCurPage: INTEGER;					{Current page number}
			iTotCopies: INTEGER;				{Total copies requested}
			iCurCopy: INTEGER;					{Current copy number}
			iTotBands: INTEGER; 				{Total bands per page.}
			iCurBand: INTEGER;					{Current band number}
			fPgDirty: BOOLEAN;					{True if current page has been written to.}
			fImaging: BOOLEAN;					{Set while in band's DrawPic call.}
			hPrint: THPrint;					{Handle to the active Printer record}
			pPrPort: TPPrPort;					{Ptr to the active PrPort}
			hPic: PicHandle;					{Handle to the active Picture}
		end;

{ Print Status: Print information during printing. }
	TPPfPgDir = ^TPfPgDir;
	THPfPgDir = ^TPPfPgDir;
	TPfPgDir = record
			iPages: INTEGER;
			iPgPos: array[0..128] of LONGINT;	{ARRAY [0..iPfMaxPgs] OF LONGINT}
		end;

{ PicFile = a TPfHeader followed by n QuickDraw Pics (whose PicSize is invalid!) }
	TPPrDlg = ^TPrDlg;
	TPrDlg = record
			Dlg: DialogRecord;					{The Dialog window}
			pFltrProc: ProcPtr; 				{The Filter Proc.}
			pItemProc: ProcPtr; 				{The Item evaluating proc.}
			hPrintUsr: THPrint; 				{The user's print record.}
			fDoIt: BOOLEAN;
			fDone: BOOLEAN;
			lUser1: LONGINT;					{Four longs for user's to hang global data.}
			lUser2: LONGINT;					{...Plus more stuff needed by the particular printing dialog.}
			lUser3: LONGINT;
			lUser4: LONGINT;
		end;

	TGnlData = record
			iOpCode: INTEGER;
			iError: INTEGER;
			lReserved: LONGINT; 				{more fields here depending on call}
		end;

	TRslRg = record
			iMin: INTEGER;
			iMax: INTEGER;
		end;

	TRslRec = record
			iXRsl: INTEGER;
			iYRsl: INTEGER;
		end;

	TGetRslBlk = record
			iOpCode: INTEGER;
			iError: INTEGER;
			lReserved: LONGINT;
			iRgType: INTEGER;
			xRslRg: TRslRg;
			yRslRg: TRslRg;
			iRslRecCnt: INTEGER;
			rgRslRec: array[1..27] of TRslRec;
		end;

	TSetRslBlk = record
			iOpCode: INTEGER;
			iError: INTEGER;
			lReserved: LONGINT;
			hPrint: THPrint;
			iXRsl: INTEGER;
			iYRsl: INTEGER;
		end;

	TDftBitsBlk = record
			iOpCode: INTEGER;
			iError: INTEGER;
			lReserved: LONGINT;
			hPrint: THPrint;
		end;

	TGetRotnBlk = record
			iOpCode: INTEGER;
			iError: INTEGER;
			lReserved: LONGINT;
			hPrint: THPrint;
			fLandscape: BOOLEAN;
			bXtra: SignedByte;
		end;

	TPRect = ^Rect; 						{ A Rect Ptr }

	TPBitMap = ^BitMap; 					{ A BitMap Ptr }

	TN = 0..15; 							{ a Nibble }

	TPWord = ^TWord;
	THWord = ^TPWord;
	TWord = packed record
			case INTEGER of
				0: (
						c1, c0: CHAR
				);
				1: (
						b1, b0: SignedByte
				);
				2: (
						usb1, usb0: Byte
				);
				3: (
						n3, n2, n1, n0: TN
				);
				4: (
						f15, f14, f13, f12, f11, f10, f9, f8, f7, f6, f5, f4, f3, f2, f1, f0: BOOLEAN
				);
				5: (
						i0: INTEGER
				);
		end;

	TPLong = ^TLong;
	THLong = ^TPLong;
	TLong = record
			case INTEGER of
				0: (
						w1, w0: TWord
				);
				1: (
						b1, b0: LONGINT
				);
				2: (
						p0: Ptr
				);
				3: (
						h0: Handle
				);
				4: (
						pt: Point
				);
		end;



procedure PrPurge;
inline
$2F3C, $A800, $0000, $A8FD;
procedure PrNoPurge;
inline
$2F3C, $B000, $0000, $A8FD;
function PrDrvrDCE: Handle;
inline
$2F3C, $9400, $0000, $A8FD;
function PrDrvrVers: INTEGER;
inline
$2F3C, $9A00, $0000, $A8FD;
procedure PrOpen;
inline
$2F3C, $C800, $0000, $A8FD;
procedure PrClose;
inline
$2F3C, $D000, $0000, $A8FD;
procedure PrintDefault (hPrint: THPrint);
inline
$2F3C, $2004, $0480, $A8FD;
function PrValidate (hPrint: THPrint): BOOLEAN;
inline
$2F3C, $5204, $0498, $A8FD;
function PrStlDialog (hPrint: THPrint): BOOLEAN;
inline
$2F3C, $2A04, $0484, $A8FD;
function PrJobDialog (hPrint: THPrint): BOOLEAN;
inline
$2F3C, $3204, $0488, $A8FD;
procedure PrJobMerge (hPrintSrc: THPrint; hPrintDst: THPrint);
inline
$2F3C, $5804, $089C, $A8FD;
function PrOpenDoc (hPrint: THPrint; pPrPort: TPPrPort; pIOBuf: Ptr): TPPrPort;
inline
$2F3C, $0400, $0C00, $A8FD;
procedure PrCloseDoc (pPrPort: TPPrPort);
inline
$2F3C, $0800, $0484, $A8FD;
procedure PrOpenPage (pPrPort: TPPrPort; pPageFrame: TPRect);
inline
$2F3C, $1000, $0808, $A8FD;
procedure PrClosePage (pPrPort: TPPrPort);
inline
$2F3C, $1800, $040C, $A8FD;
procedure PrPicFile (hPrint: THPrint; pPrPort: TPPrPort; pIOBuf: Ptr; pDevBuf: Ptr; var prStatus: TPrStatus);
inline
$2F3C, $6005, $1480, $A8FD;
function PrError: INTEGER;
inline
$2F3C, $BA00, $0000, $A8FD;
procedure PrSetError (iErr: INTEGER);
inline
$2F3C, $C000, $0200, $A8FD;
procedure PrGeneral (pData: Ptr);
inline
$2F3C, $7007, $0480, $A8FD;
procedure PrDrvrOpen;
inline
$2F3C, $8000, $0000, $A8FD;
function PrDlgMain (hPrint: THPrint; pDlgInit: ProcPtr): BOOLEAN;
inline
$2F3C, $4A04, $0894, $A8FD;
procedure PrDrvrClose;
inline
$2F3C, $8800, $0000, $A8FD;
function PrJobInit (hPrint: THPrint): TPPrDlg;
inline
$2F3C, $4404, $0410, $A8FD;
procedure PrCtlCall (iWhichCtl: INTEGER; lParam1: LONGINT; lParam2: LONGINT; lParam3: LONGINT);
inline
$2F3C, $A000, $0E00, $A8FD;
function PrStlInit (hPrint: THPrint): TPPrDlg;
inline
$2F3C, $3C04, $040C, $A8FD;

    { UsingPrintTraps }


implementation
end.











unit Press;

interface

const
	mbig = 1000000000;       					{ Ran3 	}
	mz = 0;                   						{ Ran3 	}
	fac = 1.0e-9;             					{ Ran3 	}
	np = 4;

type
	RealArrayNP = array[1..np] of REAL;            	{ Mnewt 	}
	RealArrayNPbyNP = array[1..np, 1..np] of REAL;	{ Mnewt 	}
	IntegerArrayNP = array[1..np] of INTEGER; 	{ Mnewt 	}
	glnarray = RealArrayNP;
	glnbyn = RealArrayNPbyNP;
	glindx = IntegerArrayNP;
	glnpbynp = glnbyn;

var
	glinext, glinextp, idum: LONGINT; 			{ Ran3 	}
	glma: array[1..55] of LONGINT; 			{ Ran3 	}
	cof: array[1..6] of EXTENDED;		{ GammLn;  Press c.s. gebruiken 'double' 	}
	glntop: INTEGER;						{ GammLn 	}
	gla: array[0..400] of REAL;	    			{ GammLn 	}


function FactLn (n: INTEGER): REAL;
procedure FactLnInit;
function Ran3 (var idum: LONGINT): REAL;
procedure Ran3Init (idum: LONGINT);                   { initialiseren;}
	{ vervangt de door Press c.s. gebruikte conditie op de					}
	{ geinitialiseerde waarde voor idem = -1 							}
function GammLn (xx: REAL): REAL;
procedure GammLnInit;
function GamDev (var ia: INTEGER;
							var idum: LONGINT): REAL;
function BetaDev (a, b: INTEGER;
							var idum: LONGINT): REAL;
function BnlDev (pp: REAL;
							n: INTEGER;
							var idum: LONGINT): REAL;
procedure Ludcmp (var a: RealArrayNPbyNP;
							n: INTEGER;
							var indx: IntegerArrayNP;
							var d: REAL);
procedure Lubksb (var a: RealArrayNPbyNP;
							n: INTEGER;
							var indx: IntegerArrayNP;
							var b: RealArrayNP);
procedure usrfun (x: glnarray;
							n: INTEGER;
							var alpha: glnbyn;
							var beta: glnarray);
procedure Mnewt (ntrial: INTEGER;
							var x: RealArrayNP;
							n: INTEGER;
							tolx, tolf: REAL);


implementation


function Factln;
	{    Berekent ln n!;  maakt gebruik van de relatie n! = gamma ( n+1 ).	}
	{ W.H. Press, B.P. Flannery, S.A. Teukolsky, W.T. Vetterling,			}
	{ Numerical recipes; the art of scientific computing.				}
	{ London: Cambridge University Press, 1986, p.159, 705.				}

	begin
		if n < 0 then begin
			writeln('pause: in PROCEDURE FactLn negatieve input: ', n : 1);
			readln;
		end;
		if (n <= 199) then 	{voor grotere waarden wordt geen tabel aangehouden 	}
			begin
			if gla[n + 1] = -1 then	{als de betreffende waarde nog niet is berekend	}
				gla[n + 1] := GammLn(n + 1);
			Factln := gla[n + 1]
		end
		else
			Factln := GammLn(n + 1);
	end;

procedure FactLnInit;    		{ Initialiseert ook GammLn 					}
	var
		i: INTEGER;
	begin
		for i := 1 to 400 do
			gla[i] := -1;
		cof[1] := 76.18009173;
		cof[2] := -86.50532033;
		cof[3] := 24.01409822;
		cof[4] := -1.231739516;
		cof[5] := 0.120858003e-2;
		cof[6] := -0.536382e-5;
	end;


function GammLn;
{ Returns the value LN [gamma (xx)] for xx > 0. Full accuracy is obtained for xx > 1.	}
{ W.H. Press, B.P. Flannery, S.A. Teukolsky, W.T. Vetterling,					}
{ Numerical recipes; the art of scientific computing.						}
{ London: Cambridge University Press, 1986, p. 156-158, 704 e.v.			}
{ In afwijking van Press e.a. gebeurt initialiseren van het array cof in een  		}
{ afzonderlijke PROCEDURE, omdat GammLn heel vaak wordt aangeroepen scheelt dat }
{ tijd.}
	const
		stp = 2.50662827465;
		fpf = 5.5;
	var
		x, tmp, ser: EXTENDED;
		j: INTEGER;
	begin
		if cof[4] <> -1.231739516 then
			GammlnInit;
		x := xx - 1;
		tmp := x + fpf;
		tmp := (x + 0.5) * ln(tmp) - tmp;
		ser := 1;
		for j := 1 to 6 do begin
			x := x + 1;
			ser := ser + cof[j] / x
		end;
		GammLn := tmp + ln(stp * ser)
	end;

procedure GammlnInit;			{ Initialiseert ook FactLn. 				}
	var
		i: INTEGER;
	begin
		cof[1] := 76.18009173;
		cof[2] := -86.50532033;
		cof[3] := 24.01409822;
		cof[4] := -1.231739516;
		cof[5] := 0.120858003e-2;
		cof[6] := -0.536382e-5;
		for i := 1 to 400 do
			gla[i] := -1
	end;

function Ran3;
	{ Press en anderen, blz. 199, 715-716							}
	{ hier is gekozen voor gebruik van 4-byte integers, dus LONGINT, ipv reals. 	}
	var
		mj: LONGINT;
	begin
		glinext := glinext + 1;
		if glinext = 56 then
			glinext := 1;
		glinextp := glinextp + 1;
		if glinextp = 56 then
			glinextp := 1;
		mj := glma[glinext] - glma[glinextp];
		if mj < 0 then
			mj := mj + mBig;
		glma[glinext] := mj;
		Ran3 := mj * fac;
	end;

procedure Ran3Init; 		{ initialiseren;								}
					{ vervangt de door Press c.s. gebruikte conditie op de	}
					{ geinitialiseerde waarde voor idem = -1 			}
	const
		mseed = 161803398;
	var
		i, ii: 1..55;
		mj, mk, k: LONGINT;
	begin
		if idum <= 0 then
			idum := -1;
      { anders heeft idum de waarde TickCount gekregen in het hoofdprogramma }
		mj := mseed + idum;
		mj := mj mod mbig;
		glma[55] := mj;
		mk := 1;
		for i := 1 to 54 do begin
			ii := 21 * i mod 55;
			glma[ii] := mk;
			mk := mj - mk;
			if mk < 0 then
				mk := mk + mbig;
			mj := glma[ii]
		end;
		for k := 1 to 4 do begin
			for i := 1 to 55 do begin
        { glma [ i ] := glma [ i ] - glma [ 1 + ( ( i + 30 ) mod 55 ) ] ; dit werkt in Turbo niet goed }
				if i < 25 then
					glma[i] := glma[i] - glma[1 + i + 30]
				else
					glma[i] := glma[i] - glma[1 + i + 30 - 55];
				if glma[i] < mz then
					glma[i] := glma[i] + mBig
			end;
		end;
		glinext := 0;
		glinextp := 31;
		idum := 1;    { ***** Ik denk toch dat dit allen op coinditie IF idum = -1 THEN moet zijn !!!}
	end;

function GamDev;
{ Returns a deviate distributed as a gamma distribution of INTEGER order IA,		}
{  i.e. a waiting time to the IAth event in a Poisson process of unit mean, 			}
{  using Ran3 as the source of uniform deviates.							}
{  Press, Flannery, Teukolsky, Vetterling, 1986, blz 716.					}
{  Maakt gebruik van de 'rejection method', beschreven op blz. 203 e.v.			}
{  "The rejection method is a powerful, general technique for generating random 		}
{ deviates whose distribution function p(x)dx (probability of a value occurring 		}
{ between x and x + dx) is known and computable. The rejection method does not 		}
{ require that the cumulative distribution function [indefinite integral of p(x)] be 	}
{ readily computable, much less the inverse of that function - which was requitred 	}
{ for the transformation method " in section 7.2. Zie de duidelijke figuur 7.3.1 op 	}
{ blz. 205. Function GamDev berust op een algoritme ontleend aan Ahrens, zoals 		}
{ beschreven in Knuth. Ik weet niet of ia REEEL ook OK is, houd het dus voorlopig 	}
{ even bij INTEGER.												}
	var
		am, e, s, v1, v2, x, y: REAL;
	{' Extended' is niet nodig, dan zouden Press cs 'double' hebben gebruikt 		}
		j: INTEGER;

	begin
		if ia < 1 then begin
			writeln('pause in GamDev: de input is kleiner dan 1');
			readln;
		end;
		if ia < 6 then		 { Using direct method, adding waiting times;}
	{ for small values of ia it is best to add up, ia exponentially distributed waiting 	}
	{times, i.e. logarithms of uniform deviates. Since the sum of the logarithsms is 	}
	{the logarithm of the product, one really has only to generate the product		}
	{ of ia uniform deviates, then take the log. 							}
			begin
			x := 1;
			for j := 1 to ia do
				x := x * Ran3(idum);         { idum is de seed voor Ran3 }
			x := -ln(x)
		end
		else												                       	{ Use rejection method }
			begin
			repeat
				repeat
					repeat
		{ "A useful comparison function (...) is derives from the				}
		{ Lorentzian distribution  p ( y ) dy = ( 1 / pi ) ( 1 / ( 1 + y* y ) dy,	}
		{ whise inverse indefinite integral is just the tangent function."		}
		{ These lines generate the tangent of a random angle, i.e. are equivalent to	}
		{ y = TAN ( 3.14159265 * Randomx ( r ) / ( scalb ( 31, 1 ) - 1 ) 		}
						v1 := 2 * Ran3(idum) - 1;
						v2 := 2 * Ran3(idum) - 1;
					until sqr(v1) + sqr(v2) <= 1;
					y := v2 / v1;
					am := ia - 1;
					s := sqrt(2 * am + 1);
					x := s * y + am;
				until x > 0;				{ reject in region of zero probability 		}
				e := (1 + sqr(y)) * exp(am * ln(x / am) - s * y);
							 { ratio of probability fn. to comparison fn. 	}
			until Ran3(idum) <= e			 { reject on basis of a second uniform deviate 	}
		end;
		GamDev := x
	end;

function BetaDev;
{ Random deviate uit Betaverdeling met integer parameters a en b.				}
{  Procedure ontleend aan S.J. Yakowitz,  Computational probability and simulation,	}
{  Amsterdam: Addison-Wesley Publishing Company, 1977, p. 63.				}
{  Generate independent gamma (a, alpha ) and ( b, alpha ) Variates X1 and X2 		}
{  and use the fact ( Wilks, 1962, pp. 170-174 ) that under these circumstances,	}
{  X1 / ( X1+X2 ) is beta ( a, b ).										}
{  In deze procedure wordt alpha=1 gekozen, zodat deze parameter wegvalt.			}
	var
		x1, x2: REAL;
	begin
		if (a <= 1) or (b <= 1) then
			writeln('parameters: a = ', a : 2, ' en b = ', b : 2);
		x1 := GamDev(a, idum);
		x2 := GamDev(b, idum);
		BetaDev := x1 / (x1 + x2);
	end;

function BnlDev;
{ Returns as a REAL number an INTEGER value that is a random deviate drawn 		}
{ from a binomial distribution of N trials each of probability pp.				}
{ Press, Flannery, Teukolsky, Vetterling, 1986, blz 717, 208;				}
{ De procedure is gebaseerd op de rejection method, p. 203 e.v. 				}
	label
		1;
	const
		pi = 3.141592654;
	var
		am, em, en, g, angle: REAL;
		oldg, p, pc, bnl: REAL;
		pclog, plog, pold, sq, t, y: REAL;
		a, b, j, nold: INTEGER;
	begin
		nold := -1;
		pold := -1;
		pc := -1;
		if pp <= 0.5 then
	{ de analyse is symmetrisch onder verwisseling van pp en 1 - pp,			}
	{ mits ook het antwoord wordt veranderd tot N min het antwoord; onthouden ! 	}
			p := pp
		else
			p := 1 - pp;
		am := n * p;			{ this is the mean of the binomila deviate to be produced 	}
		if n < 25 then		{ use the direct method when N is not too large 		}
			begin
			bnl := 0;
			for j := 1 to n do 				{ Voor alle trials n 	}
				if Ran3(idum) < p then         	{ Uniform deviate 	}
					bnl := bnl + 1
		end
		else if am < 1 then
			{ IF fewer than one event is expected out of 25 or more trials, }
{        THEN the distribution is quite accurately Poisson. Use direct Poisson method }
			begin
			g := exp(-am);
			t := 1;
			for j := 0 to n do begin
				t := t * Ran3(idum);
				if t < g then
					goto 1
			end;
			j := n;
1:
			bnl := j;
		end
		else	                                  { Use the rejection method }
			begin
			if n <> nOld then	              { IF n has changed, THEN compute useful quantities }
				begin
				en := n;
				oldg := Gammln(en + 1);
				nOld := n
			end;
			if p <> pOld then	             { IF p has changed, then compute useful quantities }
				begin
				pc := 1 - p;
				pLog := ln(p);
				pcLog := ln(pc);
				pOld := p;
			end;
			sq := sqrt(2 * am * pc);	{ rejection method with a Lorentzian comparison function}
{                                                 vergelijk function GamDev. }
			repeat
				repeat
					angle := pi * Ran3(idum);
					y := sin(angle) / cos(angle);
					em := sq * y + am;
				until ((em >= 0) and (em < en + 1));
				em := trunc(em);	                                        { trick for INTEGER-valued distribution }
				t := 1.2 * sq * (1 + sqr(y)) * exp(oldg - Gammln(em + 1) - Gammln(en - em + 1) + em * pLog + (en - em) * pcLog);
			until Ran3(idum) <= t;	{ reject; this happens about 0.5 times per deviate, on average }
			bnl := em;
		end;
		if p <> pp then	{ onthouden ! }
			bnl := n - bnl;
		BnlDev := bnl;
	end;

procedure Ludcmp;
   { Given an n x n matrix a [ 1..n, 1..n ], this routine replaces it by the LU decomposition of a }
{      rowwise prmutation of itself. a and n are input, arranged as in equation (2.3.14) above; }
{      indx [ 1..n ] is an output vector which records the row permutation effected by the partial}
{      pivoting; d is output as + or -1 depending on whether the number of row interchanges was}
{      even or odd, respectively. This routine is used in combination with lubskb to solve }
{      linear equations or invert a matrix. }
{   }
     {    W.H. Press, B.P. Flannery, S.A. Teukolsky, W.T. Vetterling,}
{           Numerical recipes; the art of scientific computing.}
{           London: Cambridge University Press, 1989. p. 42.}
{           Zie ook zelfde auteurs (1986) Numerical recipes: example book p. 8}
{     }
	const
		tiny = 1.0e-20;
	var
		k, j, imax, i: INTEGER;
		sum, dum, big: REAL;
		vv: ^RealArrayNP;

	begin   { ludcmp }
		new(vv);
		d := 0;
		for i := 1 to n do begin
			big := 0;
			for j := 1 to n do
				if abs(a[i, j]) > big then
					big := abs(a[i, j]);
			if big = 0 then begin     { no nonzero largest element }
				writeln('pause in LUDCMP - singular matrix');
				readln;
			end;
			vv^[i] := 1 / big;
		end;
		for j := 1 to n do begin              { This is the loop over columns of Crout's method }
			for i := 1 to j - 1 do begin       { This is equation (2.3.12) except for i = j }
				sum := a[i, j];
				for k := 1 to i - 1 do
					sum := sum - a[i, k] * a[k, j];
				a[i, j] := sum;
			end;
			big := 0;                                 { initialize for the search for largest pivot element }
			for i := j to n do begin          { This is i = j of equation (2.3.12) }
{                                                           and i = j + 1 ... N of equation (2.3.13). }
				sum := a[i, j];
				for k := 1 to j - 1 do
					sum := sum - a[i, k] * a[k, j];
				a[i, j] := sum;
				dum := vv^[i] * abs(sum);  { Is the figure of merit for the pivot better then the best so far? }
				if dum >= big then begin
					big := dum;
					imax := i;
				end;
			end;
			if j <> imax then begin               { Do we need to interchange rows? }
				for k := 1 to n do begin           { Yes, do so ... }
					dum := a[imax, k];
					a[imax, k] := a[j, k];
					a[j, k] := dum;
				end;
				d := -d;                   { ... and change the parity of d. }
				vv^[imax] := vv^[j];     { Also interchange the scale factor }
			end;
			indx[j] := imax;
			if a[j, j] = 0 then
				a[j, j] := tiny;
           { If the pivot element is zero the matrix is singular (at least to the precision of the algorithm).}
{           For some applications on singular matrices, it is desirable to substitute tiny for zero. }
			if j <> n then begin
				dum := 1 / a[j, j];            { Now, finally, divide by the pivot element }
				for i := j + 1 to n do
					a[i, j] := a[i, j] * dum;
			end;
		end;              { Go back for the next column in the reduction. }
		dispose(vv);
	end;   {ludcmp }

procedure Lubksb;
   { Solves the set of n linear equations A • X = B. Here a [ 1..n, 1..n ] is input, not as the matrix A}
{     but rather as its LU decomposition, determined by the routine ludcmp. indx [ 1..n ] is input}
{     as the permutation vector returned by ludcmp. b [ 1.. ] is input as the right-hand side vector B,}
{     and returns with the solution vector X. a, n, and indx are not modified by this routine and can be}
{     left in place for successive calls with different right-hand sides b. This routine takes into }
{     account the possibility that b will begin with many zero elements, so it is efficient for use }
{     in matrix inversion.}
{   }
     {    W.H. Press, B.P. Flannery, S.A. Teukolsky, W.T. Vetterling,}
{           Numerical recipes; the art of scientific computing.}
{           London: Cambridge University Press, 1989. p. 44.}
{           Zie ook zelfde auteurs (1986) Numerical recipes: example book p. 10}
{     }

	var
		j, ip, ii, i: INTEGER;
		sum: REAL;

	begin
		ii := 0;   { When ii is set to a positive value, it will become the index of the first }
{                      nonvanishing element of b. We now do the forward substitution, equation (2.3.6).}
{                      The only new wrinkle is to unscramble the permutation. }
		for i := 1 to n do begin
			ip := indx[i];
			sum := b[ip];
			b[ip] := b[i];
			if ii <> 0 then
				for j := ii to i - 1 do
					sum := sum - a[i, j] * b[j]
			else if sum <> 0 then    { A nonzero element was encountered, so from }
{                                                now on we will have to do the sums in the loop above. }
				ii := i;
			b[i] := sum;
		end;
		for i := n downto 1 do begin    { Now we do the substitution, equation (2.3.7). }
			sum := b[i];
			for j := i + 1 to n do
				sum := sum - a[i, j] * b[j];
			b[i] := sum / a[i, i];         { store a component of the solution vector X }
		end;
	end;

procedure usrfun;
	begin
		alpha[1, 1] := -2 * x[1];
		alpha[1, 2] := -2 * x[2];
		alpha[1, 3] := -2 * x[3];
		alpha[1, 4] := 1;
		alpha[2, 1] := 2 * x[1];
		alpha[2, 2] := 2 * x[2];
		alpha[2, 3] := 2 * x[3];
		alpha[2, 4] := 2 * x[4];
		alpha[3, 1] := 1;
		alpha[3, 2] := -1;
		alpha[3, 3] := 0;
		alpha[3, 4] := 0;
		alpha[4, 1] := 0;
		alpha[4, 2] := 1;
		alpha[4, 3] := -1;
		alpha[4, 4] := 0;

    { vier gelijktijdig op te lossen vergelijkingen : }

		beta[1] := sqr(x[1]) + sqr(x[2]) + sqr(x[3]) - x[4];
		beta[2] := -sqr(x[1]) - sqr(x[2]) - sqr(x[3]) - sqr(x[4]) + 1;
		beta[3] := -x[1] + x[2];
		beta[4] := -x[2] + x[3];
	end;

procedure Mnewt;
     { Given an initial gues x [ 1 .. n ] for a root in n dimensions, take ntrial Newton-Raphson steps}
{       to improve the root. Stop if the root converges in either summed absolute variable }
{       increments tolx or summed absolute function values tolf. }
{     }
     {    W.H. Press, B.P. Flannery, S.A. Teukolsky, W.T. Vetterling,}
{           Numerical recipes; the art of scientific computing.}
{           London: Cambridge University Press, 1989. p. 307.}
{           Zie ook zelfde auteurs (1986) Numerical recipes: example book p. 125 }
{     }

	label
		99;
	var
		k, i: INTEGER;
		errx, errf, d: REAL;
		beta: ^RealArrayNP;
		alpha: ^RealArrayNPbyNP;
		indx: ^IntegerArrayNP;
	begin
		new(beta);
		new(alpha);
		new(indx);
		for k := 1 to ntrial do begin
			usrfun(x, n, alpha^, beta^);
			errf := 0;
			for i := 1 to n do
				errf := errf + abs(beta^[i]);
			if errf <= tolf then
				goto 99;
			ludcmp(alpha^, n, indx^, d);
			lubksb(alpha^, n, indx^, beta^);
			errx := 0;
			for i := 1 to n do begin
				errx := errx + abs(beta^[i]);
				x[i] := x[i] + beta^[i];
			end;
			if errx <= tolx then
				goto 99
		end;
99:
		dispose(indx);
		dispose(alpha);
		dispose(beta);
	end;

end.


	




december-2021 \contact ben apenstaartje benwilbrink.nl

Valid HTML 4.01!   http://www.benwilbrink.nl/publicaties/94AlgemeenToetsmodelCito.htm