/* UPMC - c.durr - 2015-2016
   Conception et Pratique de l'Algorithmique
   TME 7 - déterminer les éléments distincts
*/

import java.util.*;        // StringTokenizer
import java.io.*;          // BufferedReader, FileReader
import java.util.regex.*;  // Pattern, Matcher


class FluxIP {
	BufferedReader f;

	public FluxIP(String filename) throws FileNotFoundException {
		f = new BufferedReader(new FileReader(filename));
	}

	public long next() throws IOException {
		String line = f.readLine();
		if (line == null)
            return 0;
        else {
			StringTokenizer st2 = new StringTokenizer(line, ". ");
			long a3 = Integer.parseInt(st2.nextToken());
			long a2 = Integer.parseInt(st2.nextToken());
			long a1 = Integer.parseInt(st2.nextToken());
			long a0 = Integer.parseInt(st2.nextToken());
			return (((((a3 << 8) + a2) << 8) + a1) << 8) + a0;
		}
	}

    // fonction alternative

    static Pattern p = Pattern.compile("(\\d+?)\\.(\\d+?)\\.(\\d+?)\\.(\\d+?)");

    public long next2() throws IOException {
        String line = f.readLine();
        if (line == null)
            return 0;
        else {
            Matcher m = p.matcher(line);
            m.matches();
            long ip = 0;
            for (int i = 1; i <= 4; i++)
                ip = (ip << 8) | Integer.parseInt(m.group(i));
            return ip;
        }
    }

}

abstract class StreamingAlgo {
	abstract public void update(long ip);
	abstract public int val();
}


class HashFunction {
	long H[];

	public HashFunction() {
		H = new long[33];
		Random source = new Random();
		for (int i = 0; i < 33; i++)
			H[i] = source.nextLong() & ((1L << 32) - 1);
	}

	public long hash(long val) {
		assert(0 <= val && val < 1 << 32);
        long v = H[32];
        for (long hi : H) {
            if ((val & 1) == 1)
                v ^= hi;
            val >>= 1;
        }
        return v;
	}
}


class F0naive extends StreamingAlgo {

	Set<Long> set;

	public F0naive() {
		set = new HashSet<Long>();
	}

	public void update(long ip) {
		set.add(ip);
	}

	public int val() {
		return set.size();
	}
}


class F0AMS extends StreamingAlgo {
	// Algorithme d'Alon, Matias et Szegedy.

	long min = 1L << 32;     // infini
    HashFunction h;

    public F0AMS() {
        h = new HashFunction();
    }

	public void update(long ip) {
        long hj = h.hash(ip);
		if (min > hj)
			min = hj;
	}

	public int zerogauche(long ip) {
		int z = 32;
		while (ip > 0) {     // compte les bits 0 en tête de ip
			z--;
			ip >>= 1;
		}
		return z;
	}

	public int val() {
		return (int)Math.round(Math.pow(2, zerogauche(min) + 0.5));
	}
}


class HashSetLong extends HashSet<Long> {}


class F0BJKST extends StreamingAlgo{
    // Algorithme de Bar-Yossef, Jayram, Kumar, Sivakumar et Trevisan

	HashFunction h;
	static double epsilon = 0.1;
	int limit, lenB, z;
	HashSetLong B[];


    public F0BJKST() {
        h = new HashFunction();
        double e = F0BJKST.epsilon;
        limit = 1 + (int)(12 * 24 / e / e);
        B = new HashSetLong[32];
        for (int i=0; i<32; i++)
        	B[i] = new HashSetLong();
        lenB = 0;
        z = 0;
    }

    public int zerosDroite(long val) {
    	/* déterminer le nombre max de zéros le plus à droite
           de l'expansion binaire de v
	    */
	    if (val <= 0)
	        return 32;
	    int zeros = 0;
	    while ((val & 1) == 0) {
	        zeros += 1;
	        val >>= 1;
	    }
	    return zeros;
	}

    public void update(long j) {
        long hj = h.hash(j);
        int zj = zerosDroite(hj);
        if (zj >= z) {
            if (B[zj].contains(hj))
                return;
            B[zj].add(hj);
            lenB += 1;
            if (lenB > limit) {
                lenB -= B[z].size();
                B[z].clear();
                z += 1;
            }
        }
    }

    public int val() {
        return lenB << z;
    }
}



class MedianTrick extends StreamingAlgo  {

	StreamingAlgo a[];

	MedianTrick(Class A, double p, double delta) throws Exception {
		int D = (int)Math.ceil( + Math.log(2 / delta) / Math.log(1 / p));
		a = new StreamingAlgo[2 * D + 1];
		for (int i=0; i < 2*D - 1; i++)
			a[i] = (StreamingAlgo)A.newInstance();
	}

	public void update(long ip) {
		for (StreamingAlgo b: a)
			b.update(ip);
	}

	public int val() {
		int v[] = new int[a.length];
		for (int i=0; i < a.length; i++)
			v[i] = a[i].val();
		Arrays.sort(v);
		return v[a.length / 2];
	}
}


public class F0 {

	public static void main(String[] args) throws Exception {
		double delta;
		StreamingAlgo A = null;
    	switch (args.length) {
		case 1:
	        A = new F0naive();
	        break;
	    case 2:
	    	delta = Double.parseDouble(args[1]);
	        A = new MedianTrick(F0AMS.class, Math.sqrt(2)/3, delta);
	        break;
	    case 3:
	        F0BJKST.epsilon = Double.parseDouble(args[1]);
	        delta = Double.parseDouble(args[2]);
	        A = new MedianTrick(F0BJKST.class, 1./6, delta);
	        break;
	    default:
	    	System.out.println("Usage:\n" +
        		"java F0 [filename]                   # pour l'algorithme naif\n" +
		        "java F0 [filename] [delta]           # pour l'algorithme AMS\n" +
		        "java F0 [filename] [epsilon] [delta] # pour l'algorithme BJKST\n");
	        System.exit(1);
	    }

	    int n = 0;
	    FluxIP f = new FluxIP(args[0]);
	    long ip = f.next();
	    while (ip > 0) {
	        A.update(ip);
	        n += 1;
	        if (n % 1000000 == 0)
	            System.out.println("estimation au bout de " + n + " lignes est " + A.val());
	        ip = f.next();
	    }
	    System.out.println(A.val());
	}
}
