diff --git a/csv.php b/csv.php
new file mode 100644
index 0000000000000000000000000000000000000000..e2906544f5f1fe9dfd7b4c49e5ae08c4bab78a0a
--- /dev/null
+++ b/csv.php
@@ -0,0 +1,13 @@
+<?php
+session_start();
+
+$personalKey = $_SESSION['personalKey'];
+$filename = $_GET['filename'];
+
+header('Content-Type: text/csv');
+header('Content-Encoding: UTF-8');
+header('Content-type: text/csv; charset=UTF-8');
+header('Content-disposition: attachment;filename='.$filename.'.csv');
+echo "\xEF\xBB\xBF"; // UTF-8 BOM
+
+echo file_get_contents($filename.$personalKey.'.csv');
\ No newline at end of file
diff --git a/diff.php b/diff.php
new file mode 100644
index 0000000000000000000000000000000000000000..9e790f10ed683ab7f4a3deef83905ad66201210f
--- /dev/null
+++ b/diff.php
@@ -0,0 +1,125 @@
+<?php
+/*header('Content-Type: text/csv');
+header('Content-Encoding: UTF-8');
+header('Content-type: text/csv; charset=UTF-8');
+header('Content-disposition: attachment;filename=martin.csv');
+echo "\xEF\xBB\xBF"; // UTF-8 BOM*/
+
+/*set_error_handler(function($errno, $errstr, $errfile, $errline) {
+    // error was suppressed with the @-operator
+    if (0 === error_reporting()) {
+        return false;
+    }
+    
+    throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
+});*/
+session_start();
+require('halRequestBuilder.php');
+
+if(!isset($_SESSION['personalKey'])) {
+    $_SESSION['personalKey'] = bin2hex(random_bytes(20));
+}
+$personalKey = $_SESSION['personalKey'];
+?>
+<!DOCTYPE>
+<html>
+    <head>
+        <title>diff entre 2 requette</title>
+    </head>
+<body>
+    <h1>Requête</h1>
+    <form action="" method="GET">
+        <label for="request1">Requête 1 : </label>
+        <input id="request1" name="request1" type="text" style="width: 100%;"><br />
+        <label for="requestName1">Nom requête 1 : </label>
+        <input id="requestName1" name="requestName1" type="text" style="width: 100%;"><br />
+        <label for="request2">Requête 2 : </label>
+        <input id="request2" name="request2" type="text" style="width: 100%;"><br />
+        <label for="requestName2">Nom requête 2 : </label>
+        <input id="requestName2" name="requestName2" type="text" style="width: 100%;"><br />
+        <label for="fields">Champs à affiché (séparé par une virgule) : </label>
+        <input id="fields" name="fields" type="text"><br />
+        <input type="submit" >
+    </form><br /><br />
+<?php
+
+function paralelle($a, $b, $fields, $key) {
+    $filenames = [
+        'a' => $a->getName(),
+        'b' => $b->getName(),
+        'a-b' => $a->getName().'-'.$b->getName(),
+        'b-a' => $b->getName().'-'.$a->getName(),
+    ];
+    $a_file = fopen($a->getName().$key.'.csv', 'w');
+    $b_file = fopen($b->getName().$key.'.csv', 'w');
+    $a_b_file = fopen($a->getName().'-'.$b->getName().$key.'.csv', 'w');
+    $b_a_file = fopen($b->getName().'-'.$a->getName().$key.'.csv', 'w');
+    foreach($a as $doc_a) {
+        while(true) {
+            if(!$b->valid()) break;
+            $doc_b = $b->current();
+            if($doc_a == null) break;
+            if($doc_b == null) break;
+            $fields_value = [
+                'a' => [],
+                'b' => []
+            ];
+            foreach($fields as $field) {
+                $values = [
+                    'a' => get_object_vars($doc_a)[$field],
+                    'b' => get_object_vars($doc_b)[$field]
+                ];
+                foreach($values as $queryName => $value) {
+                    if(is_array($value)) {
+                        $temp = '';
+                        foreach($value as $index => $element) {
+                            if($index!=0) $temp .= ', ';
+                            $temp .= utf8_encode(utf8_decode($element));
+                        }
+                        $values[$queryName] = $temp;
+                    }
+                }
+                array_push($fields_value['a'], $values['a']);
+                array_push($fields_value['b'], $values['b']);
+            }
+            if($doc_a->docid==$doc_b->docid) {
+                fputcsv($a_file, $fields_value['a'], $separator = ";");
+                fputcsv($b_file, $fields_value['b'], $separator = ";");
+                $b->next();
+                break;
+            } elseif($doc_a->docid>$doc_b->docid) {
+                fputcsv($b_a_file, $fields_value['b'], $separator = ";");
+                fputcsv($b_file, $fields_value['b'], $separator = ";");
+            } else {
+                fputcsv($a_b_file, $fields_value['a'], $separator = ";");
+                fputcsv($a_file, $fields_value['a'], $separator = ";");
+                break;
+            }
+            $b->next();
+        }
+    }
+    fclose($a_file);
+    fclose($b_file);
+    fclose($b_a_file);
+    fclose($a_b_file);
+    return $filenames;
+}
+
+if(isset($_GET['request1'])&&isset($_GET['request2'])&&isset($_GET['fields'])&&isset($_GET['requestName1'])&&isset($_GET['requestName2'])) {
+    $full_request1 = $_GET['request1'].'&fl='.$_GET['fields'];
+    $request1 = new HalRequestIterator($full_request1, $_GET['requestName1']);
+    $full_request2 = $_GET['request2'].'&fl='.$_GET['fields'];
+    $request2 = new HalRequestIterator($full_request2, $_GET['requestName2']);
+    $fields = explode(',', $_GET['fields']);
+    $filenames = paralelle($request1, $request2, $fields, $personalKey);
+    ?>
+<h1>Résultat</h1>
+<?php foreach($filenames as $filename):?>
+<a href="csv.php?filename=<?= $filename ?>">téléchager <?= $filename?></a> <br/>
+<?php endforeach; ?>
+    <?php
+}
+
+?>
+</html>
+</body>
\ No newline at end of file
diff --git a/halRequestBuilder.php b/halRequestBuilder.php
new file mode 100644
index 0000000000000000000000000000000000000000..8f24c8597ac28f8b48408609d16ae7e663cc8af4
--- /dev/null
+++ b/halRequestBuilder.php
@@ -0,0 +1,225 @@
+<?php
+/**
+ * Utilitaires pour facilité la génération de requête pour l'API Hal
+ * 
+ * @author Gaël PICOT <gael.picot@univ-grenoble-alpes.fr>
+ * 
+ * HalDOIManque :
+ * Copyright (C) 2022 UGA
+ * 
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ * 
+ */
+
+ /**
+  * class permetant de selectionné un type de document parmis le reférenciel hal doctype.
+  */
+class DocTypeSelector {
+    /**
+     * @var stdClass donnée représentant le résultat d'une requête sur le reférenciel hal doctype.
+     */
+    private $data;
+    /**
+     * @var string nom du champs HTML.
+     */
+    private $HTMLNameField='doctype';
+    /**
+     * @var string label du champs HTML.
+     */
+    private $HTMLLabel = 'type de document :';
+
+    /**
+     * constructeur
+     * 
+     * construit les donnée ($this->data) à partir d'une requête sur le référenciel Hal ou
+     * d'un fichier.
+     *
+     * @param ?string $portail portail hal
+     * @param string $filename fichier de donée renvoyer par le référenciel doctype de Hal
+     */
+    public function __construct(?string $portail=null, string $filename="doctype_doi.json") {
+        if ($portail==null) {
+            $this->data = json_decode(file_get_contents($filename));
+        } else {
+            $query = 'https://api.archives-ouvertes.fr/ref/doctype';
+            $query .= ($portail=='')?'':'?instance_s='.$portail;
+            $this->data = json_decode(file_get_contents($query));
+        }
+    }
+
+    /**
+     * modifie la valeur de $this->HTMLNameField
+     *
+     * @param string $newValue
+     * @return void
+     */
+    public function changeHTMLNameField(string $newValue) {
+        $this->HTMLNameField = $newValue;
+    }
+
+    /**
+     * génére le HTML d'un champs de type ComboBox
+     *
+     * @return string
+     */
+    public function generateHTMLComboBox() {
+        $htmlComboBox = '<label for="'.$this->HTMLNameField.'">'.$this->HTMLLabel.' </label>';
+        $htmlComboBox .= '<select name="'.$this->HTMLNameField.'" id="'.$this->HTMLNameField.'">';
+        foreach($this->data->response->result->doc as $doctype) {
+            $htmlComboBox .= '<option value="'.$doctype->str[0].'"';
+            if(isset($_GET[$this->HTMLNameField]) && $_GET[$this->HTMLNameField] == $doctype->str[0]) {
+                $htmlComboBox .= 'selected';
+            }
+            $htmlComboBox .= '>'.$doctype->str[1].'</option>';
+        }
+        $htmlComboBox .= '</select>';
+        return $htmlComboBox;
+    }
+}
+
+class PortailSelector {
+    /**
+     * @var stdClass donnée représentant le résultat d'une requête sur le reférenciel hal des portails (instance).
+     */
+    private $data;
+    /**
+     * @var string nom du champs HTML.
+     */
+    private $HTMLNameField='portail';
+    /**
+     * @var string label du champs HTML.
+     */
+    private $HTMLLabel = 'portail HAl :';
+    /**
+     * @var string label du champs HTML.
+     */
+    private $possibleNulValue = true;
+
+    /**
+     * constructeur
+     * 
+     * construit les donnée ($this->data) à partir d'une requête sur le référenciel Hal ou
+     * d'un fichier.
+     *
+     * @param string $filename fichier de donée renvoyer par le référenciel doctype de Hal
+     */
+    public function __construct(string $filename='') {
+        if ($filename!='') {
+            $this->data = json_decode(file_get_contents($filename));
+        } else {
+            $query = 'https://api.archives-ouvertes.fr/ref/instance';
+            $this->data = json_decode(file_get_contents($query));
+        }
+    }
+
+    /**
+     * génére le HTML d'un champs de type ComboBox
+     *
+     * @return string
+     */
+    public function generateHTMLComboBox() {
+        $htmlComboBox = '<label for="'.$this->HTMLNameField.'">'.$this->HTMLLabel.' </label>';
+        $htmlComboBox .= '<select name="'.$this->HTMLNameField.'" id="'.$this->HTMLNameField.'">';
+        if($this->possibleNulValue) {
+            $htmlComboBox .= '<option value="">aucun</option>';
+        }
+        foreach($this->data->response->docs as $instance) {
+            $htmlComboBox .= '<option value="'.$instance->code.'"';
+            if(isset($_GET[$this->HTMLNameField]) && $_GET[$this->HTMLNameField] == $instance->code) {
+                $htmlComboBox .= 'selected';
+            }
+            $htmlComboBox .= '>'.$instance->name.'</option>';
+        }
+        $htmlComboBox .= '</select>';
+        return $htmlComboBox;
+    }
+
+}
+
+/**
+ * iterateur sur une requête utilisant les curseurs
+ */
+class HalRequestIterator implements Iterator {
+    private string $queryBase = '';
+    private $results = array();
+    private $valid = true;
+    private $nextCursorMark = '*';
+    private $index = 0;
+    private $lastPage = false;
+    private $name = '';
+
+    private function _addNextPage() {
+        if(!$this->lastPage) {
+            $requestResults = json_decode(file_get_contents($this->queryBase.$this->nextCursorMark));
+            if(isset($requestResults->error)) {
+                $this->_addNextPage();
+                return;
+            }
+            $new_cursor = urlencode($requestResults->nextCursorMark);
+            if($new_cursor == $this->nextCursorMark) {
+                $this->lastPage = true;
+            } else {
+                $this->nextCursorMark = $new_cursor;
+                foreach($requestResults->response->docs as $doc) {
+                    array_push($this->results, $doc);
+                }
+            }
+        }
+    }
+
+    public function __construct($query='', $name='', $sorteField='docid+asc')
+    {
+        $this->queryBase = $query.'&sort='.$sorteField.'&cursorMark=';
+        $this->name = $name;
+        $this->_addNextPage();
+    }
+
+    public function getResults() {
+        return $this->results;
+    }
+
+    public function getName() {
+        return $this->name;
+    }
+
+    public function rewind(): void {
+        $this->index = 0;
+    }
+
+    public function current() {
+        return $this->results[$this->index];
+    }
+
+    public function key() {
+        return $this->index;
+    }
+
+    public function next():void {
+        $needNewPage = count($this->results)<=$this->index+1;
+        if(!$needNewPage||!$this->lastPage) {
+            if($needNewPage) {
+                $this->_addNextPage();
+            }
+            $this->index += 1;
+        } else {
+            $this->valid = false;
+        }
+    }
+
+    public function valid():bool {
+        return $this->valid;
+    }
+}
+
+?>
\ No newline at end of file