-- Inequalities.mesa 
--   Edited by Sweet, 21-Jan-81 21:40:58

DIRECTORY
  Ascii,
  Inline,
  IODefs,
  PressDefs,
  PressUtilities,
  Random,
  String;

Inequalities: PROGRAM
  IMPORTS Inline, IODefs, PressDefs, PressUtilities, Random, String =
  BEGIN OPEN PressDefs;

  pfdBody: PressFileDescriptor;
  pfd: POINTER TO PressFileDescriptor = @pfdBody;
  Mica: TYPE = CARDINAL;
  MBox: TYPE = RECORD [x,y,w,h: Mica];
  LineWidth: Mica ← 30;
  CharHeight: Mica;
  CharWidth: POINTER TO ARRAY CHARACTER OF Mica;
  TextCharWidth: ARRAY CHARACTER OF Mica;
  AnswerCharWidth: ARRAY CHARACTER OF Mica;
  TextCharHeight: Mica;
  AnswerCharHeight: Mica;
  Minus: CHARACTER = Ascii.ControlX;

  answers: BOOLEAN ← FALSE;

  PointsToMicas: PROC [points: CARDINAL] RETURNS [Mica] =
    {RETURN [Inline.LongDiv[Inline.LongMult[points, MicasPerInch],72]]};

  maxValue: CARDINAL ← 10;
  Prob: TYPE = RECORD [a,b: [0..16)];

  HistSize: CARDINAL = 8;
  history: ARRAY [0..HistSize) OF Prob ← ALL[[0,0]];
  hp: CARDINAL ← 0;

  Problem: PROC RETURNS [a,b: CARDINAL] =
    BEGIN
    trial: Prob;
    DO
      trial ← TrialProblem[];
      FOR i: CARDINAL IN [0..HistSize) DO
        IF history[i] = trial THEN EXIT;
	REPEAT
	  FINISHED => GO TO ok;
        ENDLOOP;
      REPEAT
	ok => NULL;
      ENDLOOP;
    history[hp] ← trial; hp ← (hp+1) MOD HistSize;
    RETURN [trial.a, trial.b];
    END;
  
  TrialProblem: PROC RETURNS [trial: Prob] =
    BEGIN
    trial.a ← Random.InRange[0, maxValue];
    DO
      trial.b ← Random.InRange[0, maxValue];
      IF trial.b # trial.a THEN EXIT;
      ENDLOOP;
    END;

  StringWidth: PROC [s: STRING] RETURNS [l: Mica] =
    BEGIN
    l ← 0;
    FOR i: CARDINAL IN [0..s.length) DO
      l ← l + CharWidth[s[i]];
      ENDLOOP;
    END;

  LineY: PROC [box: POINTER TO MBox, line, of: CARDINAL, lead: Mica ← 0] 
    RETURNS [Mica] =
    BEGIN
    h: Mica = CharHeight;
    bottom: Mica = (box.h- of*h - (of-1)*lead)/2;
    RETURN [box.y + bottom + (line-1)*(h+lead)];
    END;

  CenterLine: PROC [s: STRING, box: POINTER TO MBox, line, of: CARDINAL ← 1, lead: Mica ← 0] =
    BEGIN
    w: Mica = StringWidth[s];
    y: Mica = LineY[box: box, line: line, of: of, lead: lead] + P3;
    x: Mica = box.x + (box.w - w)/2;
    PutText[pfd, s, x, y];
    END;

  RJLine: PROC [s: STRING, box: POINTER TO MBox, line, of: CARDINAL ← 1, lead: Mica ← 0] =
    BEGIN
    w: Mica = StringWidth[s];
    y: Mica = LineY[box: box, line: line, of: of, lead: lead] + P3;
    x: Mica = box.x + (box.w - w);
    PutText[pfd, s, x, y];
    END;

  LJLine: PROC [s: STRING, box: POINTER TO MBox, line, of: CARDINAL ← 1, lead: Mica ← 0] =
    BEGIN
    y: Mica = LineY[box: box, line: line, of: of, lead: lead] + P3;
    PutText[pfd, s, box.x, y];
    END;

  DrawBorder: PROC [box: POINTER TO MBox] =
    BEGIN
    PutRectangle[p: pfd,
      xstart: box.x, ystart: box.y, xlen: box.w + LineWidth, ylen: LineWidth];
    PutRectangle[p: pfd,
      xstart: box.x, ystart: box.y, xlen: LineWidth, ylen: box.h];
    PutRectangle[p: pfd,
      xstart: box.x + box.w, ystart: box.y, xlen: LineWidth, ylen: box.h];
    PutRectangle[p: pfd,
      xstart: box.x, ystart: box.y + box.h,
      xlen: box.w + LineWidth, ylen: LineWidth];
    END;


  FillOpInequality: PROC [x,y: Mica] RETURNS [Mica] =
    BEGIN
    ns: STRING ← [2];
    box: MBox;
    a,b: CARDINAL;
    yBase: Mica = y - M38;

    TextFont[];
    [a, b] ← Problem[];
    box ← [x: x, y: yBase, w: M38, h: M38];
    CenterNum[a, @box];
    box.x ← box.x + M38;
    DrawBorder[@box];
    IF answers THEN CenterLine[(IF a < b THEN "<"L ELSE ">"), @box];
    box.x ← box.x + M38;
    CenterNum[b, @box];
    RETURN[y - M34];
    END;


  ChoiceInequality: PROC [x,y: Mica] RETURNS [Mica] =
    BEGIN
    ns: STRING ← [2];
    box: MBox;
    a,b: CARDINAL;
    yBase: Mica = y - M38;
    less: BOOLEAN = (Random.InRange[0,1] = 0);
    upper: BOOLEAN;

    TextFont[];
    [a, b] ← Problem[];
    upper ← less = (a < b);
    box ← [x: x, y: yBase, w: M38, h: M38];
    RJNum[a, @box];
    box.x ← box.x + M38;
    CenterLine[IF less THEN "<"L ELSE ">"L, @box];
    box.x ← box.x + M38;
    LJNum[b, @box];
    box ← [x: x, y: yBase - M38, w: M38, h: M38];
    RJNum[a, @box];
    box.x ← box.x + M38;
    CenterLine[IF less THEN ">"L ELSE "<"L, @box];
    box.x ← box.x + M38;
    LJNum[b, @box];
    IF answers THEN {
      box ← [x: x, y: (IF upper THEN yBase ELSE yBase-M38), w: 3*M38, h: M38];
      DrawBorder[@box]};
    RETURN[y - M34 - M14];
    END;

  FillNumberInequality: PROC [x,y: Mica] RETURNS [Mica] =
    BEGIN
    ns: STRING ← [2];
    box: MBox;
    a,b: CARDINAL;
    less: BOOLEAN = (Random.InRange[0,1] = 0);

    TextFont[];
    [a, b] ← Problem[];
    box ← [x: x + M38, y: y-M38, w: M38, h: M38];
    DrawBorder[@box];
    CenterNum[a, @box];
    box.y ← box.y - M38 - P2;
    DrawBorder[@box];
    CenterNum[b, @box];
    box.x ← x;
    box.y ← box.y - M38 - P2;
    PutRectangle[p: pfd,
      xstart: box.x,
      ystart: box.y,
      xlen: M38,
      ylen: P1];
    IF answers THEN {
      AnswerFont[];
      CenterNum[(IF less THEN MIN[a,b] ELSE MAX[a,b]), @box];
      TextFont[]};
    box.x ← box.x + M38;
    CenterLine[(IF less THEN "<"L ELSE ">"L), @box];
    box.x ← box.x + M38;
    PutRectangle[p: pfd,
      xstart: box.x,
      ystart: box.y,
      xlen: M38,
      ylen: P1];
    IF answers THEN {
      AnswerFont[];
      CenterNum[(IF less THEN MAX[a,b] ELSE MIN[a,b]), @box];
      TextFont[]};
    RETURN[y - 2*M34 - P2];
    END;

  P1: Mica = PointsToMicas[1];
  P2: Mica = PointsToMicas[2];
  P3: Mica = PointsToMicas[3];
  M1: Mica = MicasPerInch;
  M12: Mica = MicasPerInch/2;
  M14: Mica = MicasPerInch/4;
  M18: Mica = MicasPerInch/8;
  M34: Mica = (3*MicasPerInch)/4;
  M38: Mica = (3*MicasPerInch)/8;


  CenterNum: PROC [n: CARDINAL, box: POINTER TO MBox] =
    BEGIN
    w: Mica;
    ns: STRING ← [2];
    String.AppendDecimal[ns, n];
    w ← StringWidth[ns];
    PutText[pfd, ns, box.x + (box.w-w)/2, box.y + P3 + (box.h-CharHeight)/2];
    END;

  LJNum: PROC [n: CARDINAL, box: POINTER TO MBox] =
    BEGIN
    ns: STRING ← [2];
    String.AppendDecimal[ns, n];
    LJLine[ns, box];
    END;

  RJNum: PROC [n: CARDINAL, box: POINTER TO MBox] =
    BEGIN
    ns: STRING ← [2];
    String.AppendDecimal[ns, n];
    RJLine[ns, box];
    END;

  CenterChar: PROC [c: CHARACTER, box: POINTER TO MBox] =
    BEGIN
    w: Mica;
    ns: STRING ← [2];
    ns.length ← 1; ns[0] ← c;
    w ← StringWidth[ns];
    PutText[pfd, ns, box.x + (box.w-w)/2, box.y + P3 + (box.h-CharHeight)/2];
    END;


  TextFont: PROC =
    BEGIN
    SetFont[p: pfd, Name: "Helvetica", PointSize: 18, Face: 2];
    CharHeight ← TextCharHeight;
    CharWidth ← @TextCharWidth;
    END;

  AnswerFont: PROC =
    BEGIN
    SetFont[p: pfd, Name: "Helvetica", PointSize: 14, Face: 1];
    CharHeight ← AnswerCharHeight;
    CharWidth ← @AnswerCharWidth;
    END;

  DigestFonts: PROC =
    BEGIN
    [] ← PressUtilities.FindFontWidths[
      family: "Helvetica"L,
      points: 18,
      weight: bold,
      slope: regular,
      widths: LOOPHOLE[@TextCharWidth]];
    TextCharHeight ← PointsToMicas[18];
    [] ← PressUtilities.FindFontWidths[
      family: "Helvetica"L,
      points: 14,
      weight: medium,
      slope: italic,
      widths: LOOPHOLE[@AnswerCharWidth]];
    AnswerCharHeight ← PointsToMicas[14];
    END;

  DoPage: PROC =
    BEGIN
    x, y, nexty: Mica;
    x ← M1;
    y ← 10*M1;
    AnswerFont[];
    PutText[pfd, "Complete the number sentences with < or >."L, M1, y];
    y ← y - M14;
    THROUGH [0..3) DO 
      x ← M1 + M14;
      THROUGH [0..4) DO 
	nexty ← FillOpInequality[x, y];
	x ← x + M1 + M18 + M12;
	ENDLOOP;
      y ← nexty;
      ENDLOOP;
    PutRectangle[p: pfd,
      xstart: M1, ystart: y, xlen: 6*M1+M12, ylen: LineWidth];
    y ← y - M14;
    AnswerFont[];
    PutText[pfd, "Complete the number sentences."L, M1, y];
    y ← y - M14;
    THROUGH [0..2) DO 
      x ← M1 + M14;
      THROUGH [0..4) DO 
	nexty ← FillNumberInequality[x, y];
	x ← x + M1 + M18 + M12;
	ENDLOOP;
      y ← nexty;
      ENDLOOP;
    PutRectangle[p: pfd,
      xstart: M1, ystart: y, xlen: 6*M1+M12, ylen: LineWidth];
    y ← y - M14;
    AnswerFont[];
    PutText[pfd, "Circle the true number sentences."L, M1, y];
    y ← y - M14;
    THROUGH [0..3) DO 
      x ← M1 + M14;
      THROUGH [0..4) DO 
	nexty ← ChoiceInequality[x, y];
	x ← x + M1 + M18 + M12;
	ENDLOOP;
      y ← nexty;
      ENDLOOP;
    WritePage[pfd];
    END;

  Driver: PROC =
    BEGIN
    state: Random.State;
    BEGIN OPEN IODefs;
    WriteString["max: "L];
    maxValue ← ReadNumber[maxValue, 10];
    WriteChar[CR];
    END;
    DigestFonts[];
    InitPressFileDescriptor[pfd, "Math.drill"L];
    answers ← FALSE;
    Random.ReadState[@state];
    THROUGH [0..3) DO DoPage[]; ENDLOOP;
    ClosePressFile[pfd];
    Random.WriteState[@state];
    history ← ALL[[0,0]];
    InitPressFileDescriptor[pfd, "Math.key"L];
    answers ← TRUE;
    THROUGH [0..3) DO DoPage[]; ENDLOOP;
    ClosePressFile[pfd];
    END;

  Driver[];
  END.