public function compileParameters($userSupplied, $needles, $needleUserParameters, &$incompatibilities = array(), &$userRequiredParameters)
 {
     if (is_array($needles)) {
         $needlesCollection = new Collection($needles);
     } else {
         $needlesCollection = $needles;
     }
     $disallowedNeedles = $needlesCollection->diff($this->Needles);
     foreach ($disallowedNeedles as $needle) {
         $incompatibilities[] = "Needle {$needle->Name} is not marked for use in this combination";
     }
     $allowedNeedles = $needlesCollection->intersect($this->Needles);
     $attributionsWithoutNeedle = ParameterAttribution::join("Parameter", "Parameter.Id", "=", "Parameter_Attribution.Parameter_Id")->addSelect("Parameter_Attribution.*", "Parameter.Name AS parameterName", "Parameter_Attribution.Value AS parameterValue");
     $automaticFields = array_diff($this->attributingFields, ['Protocol']);
     foreach ($automaticFields as $field) {
         $property = train_case($field);
         $class = get_class($this->{$property});
         $attributionsWithoutNeedle = $attributionsWithoutNeedle->where(function ($q) use($field, $property, $class) {
             $q->whereNull($class::$idField)->orWhere($class::$idField, "=", $this->{$property}->Id);
         });
     }
     $resultList = new Collection();
     if (in_array('Protocol', $this->attributingFields)) {
         $resultList = $resultList->merge($this->Protocol->Algorithms->lists('Result'));
     }
     foreach ($this->Protocol->Algorithms as $algorithm) {
         $algorithms = $this->Protocol->Algorithms;
         $attributionsWithoutNeedle = $attributionsWithoutNeedle->where(function ($q) use($algorithms) {
             $q->whereNull("Algorithm_Id");
             foreach ($algorithms as $algorithm) {
                 $q->orWhere("Algorithm_Id", "=", $algorithm->Id);
             }
         });
     }
     $needleParametersByNeedle = [];
     if (count($allowedNeedles)) {
         foreach ($needlesCollection as $needleIx => $needle) {
             if (!in_array($needle, $allowedNeedles->all())) {
                 continue;
             }
             $attributions = with(clone $attributionsWithoutNeedle)->where(function ($q) use($needle) {
                 $q = $q->whereNull("Needle_Id");
                 $q = $q->orWhere("Needle_Id", "=", $needle->Id);
             });
             $requirements = with(clone $attributions)->whereNull("Parameter_Attribution.Value");
             $supplies = with(clone $attributions)->whereNotNull("Parameter_Attribution.Value");
             $needleParameters = [];
             foreach ($supplies->get() as $a) {
                 $name = $a->Parameter->Name;
                 if (!isset($needleParameters[$name])) {
                     $needleParameters[$name] = [];
                 }
                 $needleParameters[$name][] = $a;
             }
             array_walk($needleParameters, function (&$v, $name) {
                 /* Remove any redundant parameters - only needle parameters and parameters
                  * overriding a needle-specific parameter count */
                 if (!count(array_filter($v, function ($n) {
                     return $n->Needle_Id !== null;
                 }))) {
                     $v = false;
                 } else {
                     $v = $this->chooseAttribution($v);
                 }
             });
             $needleParameters = array_filter($needleParameters);
             $needleUser = isset($needleUserParameters[$needleIx]) ? $needleUserParameters[$needleIx] : [];
             foreach ($needleUser as $needleUserParameter) {
                 $needleParameters[$needleUserParameter->Name] = $needleUserParameter;
             }
             $needleParametersByNeedle[$needleIx] = $needleParameters;
             $requirementsList = $requirements->lists("parameterName");
             $supplyList = $supplies->lists("parameterName");
             $needleUserSupplied = [];
             if (isset($needleUserParameters[$needleIx])) {
                 $needleUserSupplied = $needleUserParameters[$needleIx]->lists('Name');
             }
             $undefinedList = array_diff($requirementsList, $supplyList, $needleUserSupplied, $userSupplied->lists('Name'));
             $undefinedList = array_diff($undefinedList, $resultList->lists("Name"));
             if (!empty($undefinedList)) {
                 $requirementsMap = array_combine($requirements->lists("parameterName"), $requirements->get()->all());
                 foreach ($undefinedList as $missingParameterName) {
                     $editable = $requirementsMap[$missingParameterName]->Editable >= 2;
                     if ($editable) {
                         $userRequiredParameters[] = $requirementsMap[$missingParameterName]->Parameter;
                     } else {
                         $incompatibilities[] = "Parameter {$missingParameterName} is missing";
                     }
                 }
             }
         }
     } else {
         $requirements = with(clone $attributionsWithoutNeedle)->whereNull("Parameter_Attribution.Value");
         $supplies = with(clone $attributionsWithoutNeedle)->whereNotNull("Parameter_Attribution.Value");
         $requirementsList = $requirements->lists("parameterName");
         $supplyList = $supplies->lists("parameterName");
         $undefinedList = array_diff($requirementsList, $supplyList, $userSupplied->lists('Name'));
         $undefinedList = array_diff($undefinedList, $resultList->lists("Name"));
         if (!empty($undefinedList)) {
             $requirementsMap = array_combine($requirements->lists("parameterName"), $requirements->get()->all());
             foreach ($undefinedList as $missingParameterName) {
                 $editable = $requirementsMap[$missingParameterName]->Editable >= 2;
                 if ($editable) {
                     $userRequiredParameters[] = $requirementsMap[$missingParameterName]->Parameter;
                 } else {
                     $incompatibilities[] = "Parameter {$missingParameterName} is missing";
                 }
             }
         }
     }
     $supplies = $attributionsWithoutNeedle->whereNull("Needle_Id")->whereNotNull("Parameter_Attribution.Value");
     $attributions = [];
     foreach ($supplies->get() as $attribution) {
         $name = $attribution->Parameter->Name;
         if (!isset($attributions[$name])) {
             $attributions[$name] = [];
         }
         $attributions[$name][] = $attribution;
     }
     $parameters = [];
     foreach ($attributions as $name => $available) {
         $parameters[$name] = $this->chooseAttribution($available);
     }
     foreach ($userSupplied as $userParameter) {
         if ($userParameter->Value === null) {
             $userParameter->Value = $userParameter->pivot->ValueSet;
         }
         $parameters[$userParameter->Name] = $userParameter;
     }
     foreach ($parameters as $parameter) {
         if ($parameter->Editable == 3) {
             /* Always editable */
             $userRequiredParameters[] = $parameter;
         }
     }
     foreach ($needleParametersByNeedle as $needle => $needleParameters) {
         foreach ($needleParameters as $parameter) {
             if ($parameter->Editable == 3) {
                 /* Always editable */
                 $userRequiredParameters[] = $parameter;
             }
         }
     }
     return [$parameters, $needleParametersByNeedle];
 }