Istruzioni

A1

Scrivere un metodo isPrime che, dato un numero n intero, verifica se tale numero è primo, ovvero se è divisibile solo da 1 e da n.
Suggerimento: a divide b ( b%a==0).

boolean isPrime(int n) {
  if (n < 2) {
    return false;
  }
  for (int i = 2; i < n; i++) {
    if (n % i == 0) {
      return false;
    }
  }
  return true;
}

A2*

Scrivere un metodo isPerfect che, dato un numero intero, verifica se tale numero è perfetto, ovvero se è uguale alla somma dei suoi divisori (dove il numero stesso non è incluso fra i divisori).

Per esempio, 6 è perfetto (6 = 1 + 2 + 3).

boolean isPerfect(int a) {
    int res = 0;
    for (int i = 1; i < a; i++) {
      if (a % i == 0) {
        res += i;
      }
    }
    return (res == a);
}

A3*

Una congettura matematica ancora aperta è quella di Goldbach: ogni numero pari piú grande di 4 è la somma di due numeri primi. Per esempio, abbiamo 4 = 2 + 2, 6 = 3 + 3, 8 = 5 + 3, ...
Scrivere un programma che, dato un numero intero n, verifica che la congettura è vera per tutti i numeri minori di n. Se può usare il metodo isPrime definito nell'esercizio A1.
boolean goldbach(int n) {
  int j;
  boolean ok;
  for (int i = 4; i <= n; i += 2) {
    ok = false;
    j = 2;
    while (!ok && j <= (i/2)) {
      if (isPrime(j) && isPrime(i - j)) {
        ok = true;
      }
      j++ ;
    }
    if (!ok) {
      return false;
    }
  }
  return true;
}

Array

B1

Scrivere un metodo checkDouble che prende un array di numeri interi e ritorna vero se due valori consecutivi dell'array sono gli stessi.
Ex: con {2, 3, 3, 5} checkDouble ritorna true ma con {2, 3, 2, 3} ritorna false.
boolean checkDouble(int[] is) {
  for (int i = 0; i < is.length - 1; i++) {
    if (is[i] == is[i + 1]) {
       return true;
    }
  }
  return false;
}

B2

Scrivere un metodo checkSequence che prende un array di numeri interi e un numero intero n e ritorna vero se n valori consecutivi dell'array sono gli stessi.
Ex: con {2, 3, 3, 5} e 2, checkSequence ritorna true ma con {2, 3, 3, 4} e 3 ritorna false.
boolean checkSequence(int[] is, int n) {
  int repetition = 1;
  for (int i = 0; i < is.length - 1; i++) {
    if (is[i] == is[i + 1]) {
       repetition++ ;
       if (repetition == n) {
         return true;
       }
    }else{
       repetition = 1;
    }
  }
  return false;
}

B3

Scrivere un metodo equals che prende due array di numeri interi e ritorna vero se i due array hanno gli stessi valori nelle stesse posizioni.
Ex: con {2, 3, 3} e {2, 3, 3}, equals ritorna true ma con {2, 3, 3} e {2} ritorna false.
boolean equals(int[] is1, int[] is2) {
  if (is1.length != is2.length) {
    return false;
  }
  for (int i = 0; i < is2.length; i++) {
    if (is1[i] != is2[i]) {
        return false;
    }
  }
  return true;
}

B4

Scrivere un metodo equals2 che prende due array bidimensionali di numeri interi (int[][]) e ritorna vero se i due array hanno gli stessi valori nelle stesse posizioni.
Ex: con {{2, 3}, {3}} e {{2, 3}, {3}}, equals2 ritorna true ma con {{2, 3}, {3}} e {{2}} ritorna false.
boolean equals2(int[][] is1, int[][] is2) {
  if (is1.length != is2.length) {
    return false;
  }
  for (int i = 0; i < is2.length; i++) {
    if (is1[i].length != is2[i].length) {
      return false;
    }
    for (int j = 0; j < is2[i].length; j++) {
      if (is1[i][j] != is2[i][j]) {
          return false;
      }
    }
  }
  return true;
}

B5

Scrivere un metodo firstPrime che prende un numero intero n e ritorna un array contenente i primi n numeri primi.
Suggerimento: a divide b (b%a == 0)
Ex: con 3, firstPrime ritorna {2, 3, 5} ma con 5 ritorna {2, 3, 5, 7, 11}. Se può usare il metodo isPrime definito nell'esercizio A1.
static int[] firstPrime(int n) {
        int[] res = new int[n];
        int j = 2;
        for(int i = 0; i < n; i++) {
            while (! isPrime(j)) {
                j++;
            }
            res[i] = j;
            j++;
        }
        return res;
    }
o meglio usando i numeri primi già calculati:
int[] firstPrime(int n) {
  int number = 2;
  boolean isPrimeNumber;
  int[] res = new int[n];
  for (int i = 0; i < n; i++) {
    while (true) {
      isPrimeNumber = true;
      for (int j = 0; j < i && res[j] <= ((int) Math.sqrt(n)); j++) {
        if (number % res[j] == 0) {
          isPrimeNumber = false;
          break; 
        }
      }
      if (isPrimeNumber) {
        break;
      }
      number++ ;
    }
    res[i] = number;
    number++ ;
  }
  return res;
}
o ancora meglio usando due metodi
boolean divisorInArray(int number, int[] is, int maxIndex) {
  int maxValue = (int) Math.sqrt(number);
  for (int i = 0; i < maxIndex && is[i] <= maxValue; i++) {
    if (number % is[i] == 0) {
      return true; 
    }
  }
  return false;
}
int[] firstPrime(int n) {
  int number = 2;
  int[] res = new int[n];
  for (int i = 0; i < n; i++) {
    while (divisorInArray(number, res, i)) {
      number++ ;
    }
    res[i] = number;
    number++ ;
  }
  return res;
}

B6

crivere un metodo lessPrime che prende un numero intero n e ritorna un array contenente i primi numeri primi minori di n.
Ex: con 3, lessPrime ritorna {2} ma con 20 ritorna {2, 3, 5, 7, 11, 13, 17, 19}. Se può usare il metodo isPrime definito nell'esercizio A1.
static int[] lessPrime(int n) {
  int[] primes = new int[n];
  int j = 0;;
  for(int i = 0; i < n; i++) {
    if (isPrime(i)) {
      primes[j] = i;  
      j++;
    }
  }
  int[] res = new int[j];
  for (int i = 0; i < j; i++) {
    res[i] = primes[i];
  }
  return res;
}
 

B7*

Scrivere un metodo checkSym che prende un array bidimensionale non necessariamente quadrato di numeri interi e restituisce un booleano. Tale booleano vale true quando c'è una simmetria fra righe e colonne: la riga i e la colonna i hanno esattamente gli stessi numeri nello stesso ordine per ogni i.
Per esempio, dato l'array {{1, 2, 3}, {2, 1}, {3}}, il metodo restituisce true mentre, dato l'array {{1, 2, 3}}, il metodo restituisce false.
boolean checkSym(int[][] a) {
  try {
    for(int i = 0; i< a.length; i++) {
      for (int j = 0; j < a[i].length; j++) {
        if (a[i][j] != a[j][i]) {
          return false;
        }
      }
    }
  }
  catch (IndexOutOfBoundsException e) {
    return false;
  }
  return true;
}

B8*

Scrivere un metodo magicSquare che prende un array bidimensionale quadrato di numeri interi e restituisce un booleano. Tale booleano vale true quando l'array rappresenta un quadrato magico: la somma dei numeri di una qualunque riga, colonna o diagonale dà lo stesso risultato. Per esempio, dato l'array {{8, 1, 6}, {3, 5, 7}, {4, 9, 2}}, il metodo restituisce true.
  static boolean magicSquare(int[][] a) {
    int sum = 0, sum1 = 0, sum2 = 0;
    for(int i = 0; i < a.length;i++) {
      sum1 += a[i][i];
      sum2 += a[i][a.length - i - 1];
    }
    if (sum1 != sum2) {
      return false;
    }
    sum = sum1;
    for(int i = 0; i < a.length; i++) {
      sum1 = 0;
      sum2 = 0;
      for (int j = 0; j < a[i].length; j++) {
        sum1 += a[i][j];
        sum2 += a[j][i];
      }
      if (sum != sum1 || sum != sum2) {
        return false;
      }
    }
    return true;
  }

B9*

Scrivere un metodo triangle che prende un array bidimensionale di numeri interi e restituisce un booleano. Tale booleano vale true quando l'array rappresenta un triangolo, ovvero la lunghezza della prima riga è uno, la lunghezza della seconda riga è due, etc. Per esempio, dato l'array {{8}, {3, 5}, {4, 9, 2}}, il metodo restituisce true.
  public static boolean triangle(int[][] a) {
    for (int i = 0; i < a.length; i++) {
      if (a[i].length != (i + 1)) {
        return false;
      }
    }
    return true;
  }

B10*

Scrivere un metodo isBordered che prende un array bidimensionale quadrato di numeri interi e restituisce un booleano. Tale booleano vale true quando l'array è composto di bordi successivi di interi dello stesso valore, false altrimenti. Per esempio, dato l'array
{{4,  4,  4,  4,  4, 4},
 {4, -2, -2, -2, -2, 4},
 {4, -2,  0,  0, -2, 4},
 {4, -2, -2, -2, -2, 4},
 {4,  4,  4,  4,  4, 4}}
il metodo restituisce true.
static boolean isBordered(int[][] a) {
    for(int  min = 0, max= a.length - 1; min < max; min++, max--) {
        int val = a[min][min];
        for(int i = min ; i <= max; i++) {
             if ((a[min][i] != val) ||
                    (a[max][i] != val) ||
                    (a[i][min] != val) ||
                    (a[i][max] != val)) {
                return false;
             }
        }
    }
    return true;
}

B11*

Scrivere un metodo statico divide che, dati due array unidimensionali di numeri interi, restituisce true se per ogni elemento b del secondo array esiste esattamente un elemento a del primo array tale che a divide b. Altrimenti il metodo restituisce false. Per esempio, {2, 3} divide {2, 3, 4, 8, 9} ma non divide {2, 3, 4, 6, 8, 9}.
static boolean divides(int[] a, int[] b) {
    boolean oneDiv;
    for(int i = 0; i < b.length; i++) {
        oneDiv = false;
        for(int j = 0; j < a.length; j++) {
            if ((b[i] % a[j]) == 0) {
                if (oneDiv) {
                    return false;
                }
                oneDiv = true;
            }
        }
        if (!oneDiv) {
            return false;
        }
    }
    return true;
}

Oggetti

Abbiamo:
class Point {
  int x;
  int y;
  Point (int x1, int y1) {
    x = x1;
    y = y1;
  }
}
class Line {
  Point pt1, pt2;
  Line (Point p, Point q) {
    pt1 = p;
    pt2 = q;
  }
}

C1

Scrivere per la class Line un metodo parallel tale che l1.parallel(l2) ritorna true se le due linee l1 e l2 sono parallele.
class Line {
  ...
  int deltaX() {
    return pt1.x - pt2.x;
  }
  int deltaY() {
    return pt1.y - pt2.y;
  }
  boolean parallel(Line l) {
    return (deltaX()* l.deltaY()) == (deltaY()* l.deltaX());
  }
  ...
}

C2

Scrivere per Point e Line un metodo equals tale che
pt1.equals(pt2) ritorna true se i due punti coincidono,
l1.equals(l2) ritorna true se le due linee coincidono.
class Point {
  ...
  boolean equals(Point pt) {
    return  x == pt.x && y == pt.y;
  }
  ...
}
class Line {
  ...
  boolean in(Point pt) {
    return ((pt1.x - pt.x)* (pt2.y - pt.y)) == ((pt2.x - pt.x)* (pt1.y - pt.y));
  }
  boolean equals(Line l) {
    return l.in(pt1) && l.in(pt2);
  }
  ...
}

C3

Scrivere una class Prime che ha un metodo statico isPrime. Questo metodo prende un numero intero n e ritorna true se n è un numero primo. L'implementazione di isPrime deve usare la tecnica descritta nell'esercizio B5. Per questo, la class Prime ha un campo statico primes che contiene i primi p numeri primi, con p arbitrario. Primo, il metodo isPrime deve assicurarsi che il campo statico primes sia sufficientemente grande per decidere se n è primo. Secondo, il metodo deve utilizzare il campo primes per verificare se un elemento di primes divide n.
class Prime {
    static int[] primes = {2};
    static boolean divisorInArray(int[] a, int n, int maxIndex) {
      int maxValue = (int) Math.sqrt(n);
      for(int j = 0; j < maxIndex & a[j] <= maxValue; j++) {
        if (n % a[j] == 0) {
          return true;
        }
      }
      return false;
    }
    static void doubleSize() {
      int[] res = new int[primes.length* 2];
      for (int i = 0; i< primes.length; i++) {
        res[i] = primes[i];
      }
      int number = primes[primes.length - 1] + 1;
      for (int i = primes.length; i<res.length; i++) {
        while (divisorInArray(res, number, i)) {
          number++ ;
        }
        res[i] = number;
        number++ ;
      }
      primes = res;
    }
    static boolean isPrime(int n) {
      if (n == 1) {
        return false;
      }
      int maxValue = (int) Math.sqrt(n);
      while (primes[primes.length - 1] < maxValue) {
        doubleSize();
      }
      return !divisorInArray(primes, n, primes.length);
    }
}

C4*

Scrivere una classe Matrix che rappresenta le matrici di numeri interi. Tale classe ha:
class Matrix{
  private int[][] val;
  Matrix(int n, int m)  {
    val = new int[n][m];
  }
  int getHeight() {
    return val.length;
  }
  int getWidth(){
    return val[0].length;
  }
  int getVal(int i, int j) {
    return val[i - 1][j - 1];
  }
  void setVal(int i, int j, int v) {
    val[i - 1][j - 1] = v;
  }
  Matrix plus(Matrix b) {
    Matrix res = new Matrix(getHeight(), getWidth());
    for (int i = 1; i<= getHeight(); i++) {
      for (int j = 1; j<= getWidth(); j++) {
        res.setVal(i, j, getVal(i, j) + b.getVal(i, j));
      }
    }
    return res;
  }
  Matrix mult(Matrix b) {
    Matrix res = new Matrix(getHeight(), b.getWidth());
    for (int i = 1; i<= getHeight(); i++) {
      for (int j = 1; j<= b.getWidth(); j++) {
        res.setVal(i, j, 0);
        for (int k = 1; k<= getWidth(); k++) {
          res.setVal(i, j, res.getVal(i, j) + getVal(i, k)* b.getVal(k, j));
        }
      }
    }
    return res;
  }
}

Ereditarietà

Abbiamo:
abstract class Tree {
}
class Node extends Tree {
  Tree right; 
  Tree left;
  Node (Tree left, Tree right) {
    this.left = left;
    this.right = right;
  }
}
class Leaf extends Tree {
  String s;
  Leaf (String s) {
    this.s = s;
  }
}

D1

Scrivere un metodo in che verifica se una stringa è in un albero.
abstract class Tree {
  ...
  abstract boolean in(String s);
  ...
}
class Node extends Tree {
  ...
  boolean in(String s) {
    return left.in(s) || right.in(s);
  }
  ...
}
class Leaf extends Tree {
  ...
  boolean in(String s) {
    return this.s.equals(s);
  }
  ...
}

D2

Scrivere un metodo maxLength che prende un albero e ritorna una delle stringhe che ha la massima lunghezza.
abstract class Tree {
  ...
  abstract String maxLength();
  ...
}
class Node extends Tree {
  ...
  String maxLength() {
    String s1 = left.maxLength();   
    String s2 = right.maxLength();   
    if (s1.length() <= s2.length())
      return s2;
    return s1;
  }
  ...
}
class Leaf extends Tree {
  ...
  String maxLength() {
    return s;
  }
  ...
}

D3

Scrivere un metodo listOf che prende un albero e ritorna l'array contenente tutte le stringhe dell'albero.
abstract class Tree {
  ...
  abstract String[] listOf();
  ...
}
class Node extends Tree {
  ...
  String[] listOf() {
    String[] l1 = left.listOf();   
    String[] l2 = right.listOf();   
    String[] l3 = new String[l1.length + l2.length];
    for (int i = 0; i < l1.length; i++) {
      l3[i] = l1[i];
    }
    for (int i = 0; i < l2.length; i++) {
      l3[i + l1.length] = l2[i];
    }
    return l3;
  }
  ...
}
class Leaf extends Tree {
  ...
  String[] listOf() {
    return new String[]{s};
  }
  ...
}
o meglio per creare un unico array:
class Integer {
  int i;
  Integer(int i) {
    this.i = i;
  }
  void incr() {
    i++ ;
  }
  int getI() {
    return i;
  }
}
abstract class Tree {
  ...
  abstract int getSize();
  abstract void listOf(String[] s, Integer i);
  String[] listOf() {
    String[] res = new String[getSize()];
    listOf(res, new Integer(0));
    return res;
  }
  ...
}
class Node extends Tree {
  ...
  int getSize() {
    return left.getSize() + right.getSize();
  }
  void listOf(String[] s, Integer i) {
    left.listOf(s, i);   
    right.listOf(s, i);   
  }
  ...
}
class Leaf extends Tree {
  ...
  int getSize() {
    return 1;
  }
  void listOf(String[] s, Integer i) {
    s[i.getI()] = this.s;
    i.incr();
  }
  ...
}

D4

Scrivere un metodo equals che prende un albero e un oggetto e verifica se l'albero è uguale all'oggetto.
abstract class Tree {
  ...
  abstract boolean equals(Object o);
  ...
}
class Node extends Tree {
  ...
  boolean equals(Object o) {
    if (!(o instanceof Node))
      return false;
    Node n = (Node)o;
    return left.equals(n.left) && right.equals(n.right);
  }
  ...
}
class Leaf extends Tree {
  ...
  boolean equals(Object o) {
    if (!(o instanceof Leaf))
      return false;
    return s.equals(((Leaf)o).s);
  }
  ...

}

D5*

Modificare le classe Tree, Node e Leaf in tale modo che gli alberi possono avere un numero arbitrario di figli. Aggiornare in conseguenza i metodi in, maxLength, listOf e equals.
class Integer {
   int i;
   Integer(int i) {
     this.i = i;
   }
   void incr() {
     i++ ;
   }
   int getI() {
     return i;
   }
 }

abstract class Tree {
  abstract boolean in(String s);
  abstract String maxLength();
  abstract int getSize();
  String[] listOf() {
    String[] res = new String[getSize()];
    listOf(res, new Integer(0));
    return res;
  }
  abstract void listOf(String[] res, Integer i);
  abstract boolean equals(Object o);
}

class Node extends Tree{
    Tree[] sons;
    Node(Tree[] sons) {
        this.sons = sons;
    }
    boolean in(String s) {
      for(int i = 0; i < sons.length; i++) {
        if (sons[i].in(s)) {
          return true;
        }
      }
      return false;
    }
    String maxLength() {
      String res = "";
      String s;
      for(int i = 0; i < sons.length; i++) {
        s = sons[i].maxLength();
        if (s.length() > res.length()) {
          res = s;
        }
      }
      return res;
    }
    int getSize() {
      int res = 0;
      for(int i = 0; i < sons.length; i++) {
        res += sons[i].getSize();
      }
      return res;
    }
    void listOf(String[] res, Integer i) {
      for(int j = 0; j < sons.length; j++) {
        sons[j].listOf(res, i);
      }
    }
  boolean equals(Object o) {
    if (!(o instanceof Node))
      return false;
    Node n = (Node)o;
    if (sons.length != o.sons.length) {
      return false;
    }
    for(int i = 0; i < sons.length; i++) {
      if (!(sons[i].equals(o.sons[i]))) {
          return false;
      }
    }
    return true;
  }
}

class Leaf extends Tree {
    String value;
    Leaf(String value) {
        this.value = value;
    }
    boolean in(String s) {
      return value.equals(s);
    }
    String maxLength() {
      return value;
    }
    int getSize() {
      return 1;
    }
    void listOf(String[] res, Integer i) {
      res[i.getI()] = value;
      i.incr();
    }
    boolean equals(Object o) {
      if (!(o instanceof Leaf)) {
        return false;
      }
      return s.equals(((Leaf)o).s);
    }
}

D6*

Definire una classa astratta List e due sottoclassi Cons e Nil per rappresentare le liste di numeri interi tale che la lista composta dei numeri 1, 2 e 3 può essere creata tramite l'espressione new Cons(1, new Cons(2, new Cons(3, new Nil()))).

Aggiungere in List:

 abstract class List {
   abstract boolean in(int i);
   abstract boolean sorted();
   abstract boolean sorted(int i);
   static List makeEmpty() {
    return new Nil();
  }
   abstract List copy();
   abstract List add(int i);
}

class Nil extends List{
  Nil() {}
   boolean in(int i) {
    return false;
  }
   boolean sorted() {
    return true;
  }
  boolean sorted(int i) {
    return true;
  }
  List copy() {
    return this;
  }
  List add(int i)
    return new Cons(i, this);
  }
}

class Cons extends List {
  int hd;
  List tl;
  Cons(int hd, List tl) {
    this.hd = hd;
    this.tl = tl;
  }
   boolean in(int i) {
    if (hd == i) {
      return true;
    }else{
      return tl.in(i);
    }
  }
   boolean sorted() {
    return tl.sorted(hd);
  }
  boolean sorted(int i) {
    if (i < hd) {
      return tl.sorted(hd);
    }else{
      return false;
    }
  }
  List copy() {
    return new Cons(hd, tl.copy());
  }
   List add(int i) {
    if (i < hd) {
      return new Cons(i, copy());
    }else if (i == hd) {
      return copy();
    }else {
      return new Cons(hd, tl.add(i));
    }
  }
}

D7*

Definire una classa astratta Path e due sottoclassi Move e Select per rappresentare un camino in un albero tale che il camino new Move(2, new Move(1, new Select())) rappresenta il verso il primo figlio del secondo figlio dell'albero. Aggiungere in la classe Tree dell'esercizio D5:
abstract class Path {
}

class Move extends Path {
  int move;
  Path path;
  Move(int move, Path path) {
    this.move = move;
    this.path = path;
  }
}

class Select extends Path {
  Select() {
  }
}
abstract class Tree {
  ...
  abstract Path getPath(Tree tree);
  abstract Tree getTree(Path path);
  ...
}

class Node extends Tree{
  ...
  public Path getPath(Tree tree) {
    if (equals(tree)) {
      return new Select();
    }
    Path res;
    for(int i = 0; i

D8*

Definire una classa astratta Multi e due sottoclassi Single e Empty per rappresentare dei multinsiemi sui numeri interi tale che il multinsieme {1, 1, 3, 3, 3} può essere creato tramite l'espressione new Single(1, 2, new Single(3, 3, new Empty())).

Aggiungere in Multi:

abstract class Multi {
  abstract int getMulti(int i);
  abstract Multi add(int i);
  static Multi makeEmpty() {
    return new Empty();
  }
  abstract boolean in(Multi a);
  public boolean equals(Object a) {
    if (a instanceof Multi) {
      Multi a1 = (Multi)a;
      return in(a1) && a1.in(this);
    }
    return false;
  }
}

class Empty extends Multi{
  Empty() {};
  int getMulti(int i) {
    return 0;
  }
  Multi add(int i) {
    return new Single(i, 1, this);
  }
  boolean in(Multi a) {
    return true;
  }
}
class Single extends Multi{
  int num;
  int multi;
  Multi rest;
  Single(int num, int multi, Multi rest) {
    this.num = num;
    this.multi = multi;
    this.rest = rest;
  }
  int getMulti(int i) {
    if (i == num) {
      return multi + rest.getMulti(i);
    }else{
      return rest.getMulti(i);
    }
  }
  Multi add(int i) {
    if (i == num) {
      return new Single(num, multi + 1, rest);
    }else{
      return new Single(num, multi, rest.add(i));
    }
  }
  boolean in(Multi i) {
    if (getMulti(num) <= i.getMulti(num)) {
      return rest.in(i);
    }
    return false;
  }
}

D9*

Definire una classa astratta Exp e tre sottoclassi Int, Plus e Mult per rappresentare le espressioni arimetiche tale che l'espressione (1 + 2)* 3 possa essere rappresentata come Exp a = new Mult(new Plus(new Int(1), new Int(2)), new Int(3))}.

Quindi aggiungere nella classe Exp:

abstract class Exp {
  abstract public int val();
  abstract public boolean equals(Object o);
  public boolean in(Exp e) {
    return equals(e);
  }
}

class Int extends Exp {
  int i;
  Int(int i) {
    this.i = i;
  }
  public int val() {
    return i;
  }
  public boolean equals(Object o) {
    if (o instanceof Int) {
      return i == ((Int)o).i;
    }
    return false;
  }
}
class Plus extends Exp {
  Exp left, right;
  Plus(Exp left, Exp right) {
    this.left = left;
    this.right = right;
  }
  public int val() {
    return left.val() + right.val();
  }
  public boolean equals(Object o) {
    if (o instanceof Plus) {
      Plus e1 = (Plus)o;
      return left.equals(e1.left) && right.equals(e1.right);
    }
    return false;
  }
  public boolean in(Exp e) {
    if (left.in(e)) {
      return true;
    }
    if (right.in(e)) {
      return true;
    }
    return super.in(e);
  }
}
class Mult extends Exp {
  Exp left, right;
  Mult(Exp left, Exp right) {
    this.left = left;
    this.right = right;
  }
  public int val() {
    return left.val()* right.val();
  }
  public boolean equals(Object o) {
    if (o instanceof Mult) {
      Mult e1 = (Mult)o;
      return left.equals(e1.left) && right.equals(e1.right);
    }
    return false;
  }
  public boolean in(Exp e) {
    if (left.in(e)) {
      return true;
    }
    if (right.in(e)) {
      return true;
    }
    return super.in(e);
  }
}

Visibilità

E1

Riscrivere Tree, Node, Leaf nel package util in modo tale che solo la class Tree sia visibile.
package util;

public abstract class Tree {
  public static makeTree(String s) {
    return new Leaf(s);
  }
  public append(Tree t) {
    return new Node(this, t);
  }
  public abstract String maxLength();
  public abstract boolean in(String s);
  abstract void listOf(String[] s, Integer i);
  public String[] listOf() {
    ...
  }
}


class Node extends Tree {
  ...
  public String maxLength() {
    ...
  }
  public boolean in(String s) {
    ...
  }
  void listOf(String[] s, Integer i) {
     ...
  }
}

class Leaf extends Tree {
  ...
  public String maxLength() {
    ...
  }
  public boolean in(String s) {
    ...
  }
  void listOf(String[] s, Integer i) {
     ...
  }
}

E2*

Riscrivere List, Cons, Nil D6 in tale mode che appartengo al package util e che di fuori del package si possono creare solamente liste ordinate, i.e. liste costruite usando solamente i metodi makeEmpty e add.
package util;


public abstract class List {
  public abstract boolean in(int i);
  public abstract boolean sorted();
  abstract boolean sorted(int i);
  public static List makeEmpty() {
    return new Nil();
  }
  abstract List copy();
  public abstract List add(int i);
}

class Nil extends List{
  Nil() {}
  public boolean in(int i) {
    ...
  }
  public boolean sorted() {
    ...
  }
  boolean sorted(int i) {
    ...
  }
  List copy() {
    ...
  }
  public List add(int i){
    ...
  }
}

class Cons extends List {
  int hd;
  List tl;
  Cons(int hd, List tl) {
    ...
  }
  public boolean in(int i) {
    ...
  }
  public boolean sorted() {
    ...
  }
  boolean sorted(int i) {
    ...
  }
  List copy() {
    ...
  }
  public List add(int i)
    ...
  }
}

Eccezioni

F1*

Riscrivere il metodo in dall'esercizio D6 in tale mode che se il numero aggiunto è già nella lista, il metodo solleva l'eccezione ListException.
class ListException extends Exception {
  ListException() {
    super();
  }
}

public abstract class List {
  ...
  public abstract List add(int i) throws ListException;
}

class Nil extends List{
  ...
  public List add(int i) throws ListException{
    return new Cons(i, this);
  }
}

class Cons extends List {
  ...
  public List add(int i) throws ListException{
    if (i < hd) {
      return new Cons(i, copy());
    }else if (i == hd) {
      throw new ListException();
    }else {
      return new Cons(hd, tl.add(i));
    }
  }
}

F2*

Riscrivere i metodi getPath e getTree dall'esercizio D7 in tale mode che l'eccezione NotFoundException è sollevata se è richiesto un camino di un sotto - albero che non appartienne all'albero e se è chiesto un sotto - albero con un camino invalido.
abstract class Tree {
  abstract Path getPath(Tree tree) throws NotFoundException;
  abstract Tree getTree(Path path) throws NotFoundException;
}

class Node extends Tree{
  ...
  public Path getPath(Tree tree) throws NotFoundException {
    if (equals(tree)) {
      return new Select();
    }
    Path res;
    for(int i = 0; i

F3*

Riscrivere i metodi dall'esercizion C4 in tale modo che ogni problema con la dimensione delle matrice vienne riportato con l'eccezione IllegalDimension}.
class Matrix{
  ...
  Matrix(int n, int m) throws IllegalDimension {
    if (n <= 0 || m <= 0) {
      throw new IllegalDimension();
    }
    val = new int[n][m];
  }
  ...
  int getVal(int i, int j) throws IllegalDimension {
    if (1 > i || i > getHeight() || 1 > j || j > getWidth())
      throw new IllegalDimension();
    return val[i - 1][j - 1];
  }
  void setVal(int i, int j, int v) throws IllegalDimension {
    if (1 > i || i > getHeight() || 1 > j || j > getWidth())
      throw new IllegalDimension();
    val[i - 1][j - 1] = v;
  }
  Matrix plus(Matrix b) throws IllegalDimension {
    if (getWidth() != b.getWidth() || getHeight() != b.getHeight())
      throw new IllegalDimension();
    Matrix res = new Matrix(getHeight(), getWidth());
    for (int i = 1; i<= getHeight(); i++) {
      for (int j = 1; j<= getWidth(); j++) {
        res.setVal(i, j, getVal(i, j) + b.getVal(i, j));
      }
    }
    return res;
  }
  Matrix mult(Matrix b) throws IllegalDimension {
    if (getWidth() != b.getHeight())
      throw new IllegalDimension();
    Matrix res = new Matrix(getHeight(), b.getWidth());
    for (int i = 1; i<= getHeight(); i++) {
      for (int j = 1; j<= b.getWidth(); j++) {
        res.setVal(i, j, 0);
        for (int k = 1; k<= getWidth(); k++) {
          res.setVal(i, j, res.getVal(i, j) + getVal(i, k)* b.getVal(k, j));
        }
      }
    }
    return res;
  }
}

Esterel

G1*

Scrivere un programma che gestisce l'apertura di una porta controllata da un lettore di carta con codice.
Inizialmente il programma aspetta che sia inserita una carta (INSERT_CARD).
Dopo l'inserzione, il codice della carta è digitato cifra dopo cifra (DIGIT) da sinistra a destra.
Il codice è validato premendo il pulsante ENTER.
Una volta validato il codice, il programma richiede la verifica del codice (CHECK_CODE) ad un programma esterno.
Il segnale CHECK_CODE ha per valore un numero intero uguale al codice che è stato digitato cifra dopo cifra.
Quando il codice è stato verificato (VALID_CODE), il programma apre la porta (OPEN_DOOR), restituisce la carta (RETURN_CARD) e aspetta di nuovo una carta.
In caso di errore quando si digita il codice, si può premere il pulsante RESET e digitare di nuovo il codice da capo.
Una volta inserita una carta, si può riottenerla (RETURN_CARD) in ogni momento premendo il pulsante QUIT. Una volta restituita la carta, il programma ne aspetta un'altra.
Quando dopo una richiesta di verifica del codice (CHECK_CODE), la verifica è negativa (INVALID_CODE), si può riprovare a digitare il codice.
Dopo tre tentativi falliti consecutivi di verifica del codice (INVALID_CODE), il programma conserva la carta (KEEP_CARD) e ne aspetta un'altra.
Una carta non può rimanere dentro il lettore per piú di un minuto (60 SECOND). Se ciò accade, il programma restituisce la carta e ne aspetta un'altra.
module CB:
input INSERT_CARD;
input VALID_CODE, INVALID_CODE;
input DIGIT: integer;
input ENTER, RESET, QUIT;
input SECOND;
relation INSERT_CARD # VALID_CODE # INVALID_CODE # 
         DIGIT # ENTER # RESET # QUIT # SECOND;
output CHECK_CODE: integer;
output KEEP_CARD, RETURN_CARD, OPEN_DOOR;

loop
  await INSERT_CARD;
  [
    abort 
      trap EXIT in
          loop
            trap CHECK in
              loop 
                var CODE : = 0 : integer in
                  abort 
                    every DIGIT do CODE : = CODE* 10 + ?DIGIT end every
                  when ENTER do
                    emit CHECK_CODE(CODE);
                    exit CHECK
                  end abort 
                end var
              each RESET
            end trap
          each INVALID_CODE
        ||
          await VALID_CODE;
          emit OPEN_DOOR;
          emit RETURN_CARD;
          exit EXIT
        ||
          await QUIT;
          emit RETURN_CARD;
          exit EXIT
        ||
          await 3 INVALID_CODE;
          emit KEEP_CARD;
          exit EXIT
      end trap
    when 60 SECOND do
      emit KEEP_CARD
    end abort
  ]
end loop
end module

G2*

Scrivere un programma che gestisce il telecomando di un televisore. Il telecomando ha due modi:
  • un modo televisione dove si sceglie un programma,
  • un modo teletext dove si sceglie una pagina.
Ogni volta che è premuto il pulsante ON_OFF, il telecomando emette un segnale TURN_ON_OFF per accendere o spengere il televisore e si mette in modo televisione. In modo televisione:
  • Si può cambiare programma in ogni momento.
  • Premendo una cifra (DIGIT) sola, il telecomando emette il segnale CHANNEL che cambia il programma.
  • Premendo il pulsante DOUBLE, il telecomando aspetta le due prossime cifre (DIGIT) per emettere il segnale CHANNEL corrispondente.
  • Si può cambiare pagina in ogni momento.
  • Premendo tre cifre (DIGIT) una dopo l'altra, il telecomando emette il segnale TXT che seleziona la pagina.
Si passa da un modo all'altro premendo il pulsante TXT_ON_OFF.
module TC:
input TXT_ON_OFF, DOUBLE, ON_OFF;
input DIGIT: integer;
relation TXT_ON_OFF # DOUBLE # DIGIT # ON_OFF;
output CHANNEL: integer;
output TXT: integer;
output TURN_ON_OFF;

every ON_OFF do
  emit TURN_ON_OFF;
  loop
    abort 
      loop
        abort 
          every DIGIT do emit CHANNEL(?DIGIT) end every
        when  immediate DOUBLE;
        abort
          var CHANNEL : = 0 : integer in
            await DIGIT;
            CHANNEL: = ? DIGIT;
            await DIGIT;
            CHANNEL: = CHANNEL* 10 + ?DIGIT;
            emit CHANNEL(CHANNEL)
          end var
         when DOUBLE
       end 
     when TXT_ON_OFF;
     abort
       loop
         var TXT : = 0 : integer in
           await DIGIT;
           TXT: = ? DIGIT;
           await DIGIT;
           TXT: = TXT* 10 + ?DIGIT;
           await DIGIT;
           TXT: = TXT* 10 + ?DIGIT;
           emit TXT(TXT)
         end var
        end loop
     when TXT_ON_OFF
  end loop
end every
end module

G3*

Scrivere un programma che gestisce un orologio per un gioco con 2 giocatori.
L'orologio mostra per ciascun giocatore il tempo che gli rimane per finire la partita (i segnali DISPLAY1 e DISPLAY2).
Il tempo è indicato in millisecondi.
Per calcolare il tempo che passa, il programma riceve un segnale MS ogni millisecondo.
Quando un giocatore ha giocato la sua mossa, preme il suo pulsante (BUTTON1 o BUTTON2): si ferma il suo tempo e parte il tempo del suo avversario.
All'inizio della partita, ogni giocatore ha lo stesso tempo (INIT_TIME) ed i due tempi sono fermi.
La partita è finita (END) quando uno dei due tempi arriva a zero.
In ogni momento, si può ri - inizializzare l'orologio usando il pulsante RESET.
module CP:
input BUTTON1, BUTTON2;
input RESET;
input MS;
relation RESET # BUTTON1 # BUTTON2 # MS;
output DISPLAY1:integer, DISPLAY2:integer;
output END;
constant INIT_TIME : integer;

var TIME1, TIME2:integer in
  loop  
    trap EXIT in 
      [
        TIME1 : = INIT_TIME;
        emit DISPLAY1(TIME1);
        every BUTTON2 do
          abort
            every MS do
              TIME1 : = TIME1 - 1;
              emit DISPLAY1(TIME1);
              if (TIME1 = 0)  
                then exit EXIT
              end if
            end every
           when BUTTON1
        end every
      ||
        TIME2 : = INIT_TIME;
        emit DISPLAY2(TIME2);
        every BUTTON1 do
          abort
            every MS do
              TIME2 : = TIME2 - 1;
              emit DISPLAY2(TIME2);
              if (TIME2 = 0)  
                then exit EXIT
              end if
            end every
          when BUTTON2
        end every
      ]
    end trap;
    emit END
  each RESET
end var
end module

G4*

Scrivere un programma che gestisce una macchina per fare delle foto - tessera.
All'inizio, la macchina non è attiva, ciò viene indicato da una lampadina rossa (RED).
La macchina viene attivata quando un utente inserisce una moneta (INSERT_COIN). Il fatto che la macchina sia attiva è indicato da una lampadina verde (GREEN).
Una volta inserita la moneta, l'utente deve scegliere il formato delle foto (WHICH_FORMAT).
Quando l'utente ha scelto il formato (FORMAT), si può proseguire. Se l'utente non sceglie entro 60 secondi (SECOND), la macchina restituisce i soldi (RETURN_COIN) e diventa inattiva (RED).
Una sessione è composta di tre foto validate dall'utente.
Per fare una foto, la macchina aspetta 10 secondi, poi accende una lampadina per avvertire l'utente di essere pronto (WARNING) e dopo 2 secondi (SECOND) scatta la foto (CLICK). Dopo 3 secondi (SECOND), mostra il risultato all'utente (SHOW). Se l'utente è d'accordo (YES), la foto è validata, altrimenti la foto è rifiutata (NO).
Se l'utente non risponde entro 60 secondi (SECOND) o se l'utente rifiuta più di 10 foto, la macchina restituisce i soldi (RETURN_COIN) e diventa inattiva (RED).
Quando sono state validate tre foto, la macchina stampa le foto (PRINT) e diventa inattiva.
module PHOTO:
input INSERT_COIN, YES, NO, SECOND;
input FORMAT: integer;
relation INSERT_COIN # YES # NO # FORMAT # SECOND;
output RED, GREEN, WARNING, WHICH_FORMAT, 
       SHOW, RETURN_COIN, PRINT, CLICK;

loop
  emit RED;
  await INSERT_COIN;
  emit GREEN;
  trap END in
    emit WHICH_FORMAT;
    abort
      await 60 SECOND;
      exit END;
    when FORMAT;
    trap SESSION in
      signal OK, NOT_OK in 
          loop 
            await 10 SECOND;
            emit WARNING;
            await 2 SECOND;
            emit CLICK;
            await 3 SECOND;
            abort 
              abort 
                await 60 SECOND;
                exit END
              when YES
                do emit OK
              end abort
            when NO
             do emit NOT_OK
            end abort
           end loop
         || 
           await 3 OK;
           exit SESSION
         ||
           await 10 NOT_OK;
           exit END
      end signal;
    end trap;
    emit PRINT;
  handle END do
    emit RETURN_COIN;
  end trap;
end loop
     
end module

G5*

Scrivere un programma che gestisce i semafori di un incrocio.
Ci sono quattro semafori (nord, est, sud, ovest) ma il controllo è fatto considerando i semafori a coppia. Ci sono due coppie: la coppia nord - sud (NS) e la coppia est - ovest (EW).
Un semaforo è alternativamente verde (GREEN_NS o GREEN_EW), giallo (ORANGE_NS o ORANGE_EW) e rosso (RED_NS o RED_EW).
Quando una coppia di semafori è verde o gialla l'altra deve essere necessariamente rossa.
In un ciclo normale, un semaforo resta verde 30 secondi (SECOND) , giallo 2 secondi e rosso 32 secondi.
Un pedone può premere un pulsante (WALK_NS o WALK_EW). Questo assicura che se il semaforo è verde non lo resterà per piú di altri 10 secondi.
module TRAFFIC_LIGHT:
input SECOND, WALK_NS, WALK_EW;
relation SECOND # WALK_NS # WALK_EW;
output RED_NS, ORANGE_NS, GREEN_NS,      
       RED_EW, ORANGE_EW, GREEN_EW;

loop
  emit RED_NS; emit GREEN_EW;
  trap EXIT in
      await 30 SECOND;
      exit EXIT
  || 
      await WALK_EW;
      await 10 SECOND;
      exit EXIT
  end trap;
  emit ORANGE_EW;
  await 2 SECOND;
  emit RED_EW; emit GREEN_NS;
  trap EXIT in
      await 30 SECOND;
      exit EXIT
  || 
      await WALK_NS;
      await 10 SECOND;
      exit EXIT
  end trap;
  emit ORANGE_NS;
  await 2 SECOND;
end loop
     
end module

G6*

Scrivere un programma che gestisce l'apertura di una porta automatica di un parcheggio per auto.
Prima di gestire l'apertura della porta, il programma aspetta di avere il codice segreto (INIT_CODE).
Inizialmente la porta è chiusa.
A intervalli regolari viene segnalata la presenza di una macchina davanti la porta (DETECT).
La porta si apre (OPEN) se il conducente della macchina introduce un codice (CODE) che corrisponde al codice segreto.
La porta si chiude (CLOSE) se non sono piú individuate macchine (DETECT) per un periodo di 5 secondi (SECOND).
Ogni volta che viene introdotto un codice errato, si accende una lampadina (RETRY).
Se dopo 2 minuti che una macchina è stata individuata la porta non è stata aperta, viene avvertito il centralino (ERROR).
module GATE_CONTROL:
input SECOND, DETECT, INIT_CODE: integer, CODE : integer;
relation SECOND # INIT_CODE # CODE # DETECT;
output OPEN, CLOSE, RETRY, ERROR;

await INIT_CODE;
loop 
  trap CHECK_CODE in
      loop
        await DETECT;
        await 120 SECOND;
        emit ERROR;
      end loop
    ||
      every CODE do
        if ?CODE = ?INIT_CODE then
          exit CHECK_CODE;
        end if;
        emit RETRY
      end every
  end trap;
  emit OPEN;
  trap TIMEOUT in 
    loop
      await 5 SECOND;
      exit TIMEOUT
    each DETECT;
  end trap;
  emit CLOSE;
end

Laurent Théry
Last modified: Fri Jan 23 00:06:25 MET 2004