/**
  * @param \PHPSemVerChecker\Registry\Registry $registryBefore
  * @param \PHPSemVerChecker\Registry\Registry $registryAfter
  * @return \PHPSemVerChecker\Report\Report
  */
 public function analyze(Registry $registryBefore, Registry $registryAfter)
 {
     $report = new Report();
     $keysBefore = array_keys($registryBefore->data['function']);
     $keysAfter = array_keys($registryAfter->data['function']);
     $added = array_diff($keysAfter, $keysBefore);
     $removed = array_diff($keysBefore, $keysAfter);
     $toVerify = array_intersect($keysBefore, $keysAfter);
     foreach ($removed as $key) {
         $fileBefore = $registryBefore->mapping['function'][$key];
         $functionBefore = $registryBefore->data['function'][$key];
         $data = new FunctionRemoved($fileBefore, $functionBefore);
         $report->addFunction($data);
     }
     foreach ($toVerify as $key) {
         $fileBefore = $registryBefore->mapping['function'][$key];
         $functionBefore = $registryBefore->data['function'][$key];
         $fileAfter = $registryAfter->mapping['function'][$key];
         $functionAfter = $registryAfter->data['function'][$key];
         // Leave non-strict comparison here
         if ($functionBefore != $functionAfter) {
             $paramsBefore = $functionBefore->params;
             $paramsAfter = $functionAfter->params;
             $signatureResult = Signature::analyze($paramsBefore, $paramsAfter);
             $changes = ['parameter_added' => FunctionParameterAdded::class, 'parameter_removed' => FunctionParameterRemoved::class, 'parameter_renamed' => FunctionParameterNameChanged::class, 'parameter_typing_added' => FunctionParameterTypingAdded::class, 'parameter_typing_removed' => FunctionParameterTypingRemoved::class, 'parameter_default_added' => FunctionParameterDefaultAdded::class, 'parameter_default_removed' => FunctionParameterDefaultRemoved::class, 'parameter_default_value_changed' => FunctionParameterDefaultValueChanged::class];
             foreach ($changes as $changeType => $class) {
                 if (!$signatureResult[$changeType]) {
                     continue;
                 }
                 if (is_a($class, FunctionOperationUnary::class, true)) {
                     $data = new $class($fileAfter, $functionAfter);
                 } else {
                     $data = new $class($fileBefore, $functionBefore, $fileAfter, $functionAfter);
                 }
                 $report->addFunction($data);
             }
             // Difference in source code
             if (!Implementation::isSame($functionBefore->stmts, $functionAfter->stmts)) {
                 $data = new FunctionImplementationChanged($fileBefore, $functionBefore, $fileAfter, $functionAfter);
                 $report->addFunction($data);
             }
         }
     }
     foreach ($added as $key) {
         $fileAfter = $registryAfter->mapping['function'][$key];
         $functionAfter = $registryAfter->data['function'][$key];
         $data = new FunctionAdded($fileAfter, $functionAfter);
         $report->addFunction($data);
     }
     return $report;
 }
 public function analyze(Stmt $contextBefore, Stmt $contextAfter)
 {
     $report = new Report();
     $methodsBefore = $contextBefore->getMethods();
     $methodsAfter = $contextAfter->getMethods();
     $methodsBeforeKeyed = [];
     foreach ($methodsBefore as $method) {
         $methodsBeforeKeyed[$method->name] = $method;
     }
     $methodsAfterKeyed = [];
     foreach ($methodsAfter as $method) {
         $methodsAfterKeyed[$method->name] = $method;
     }
     $methodNamesBefore = array_keys($methodsBeforeKeyed);
     $methodNamesAfter = array_keys($methodsAfterKeyed);
     $methodsAdded = array_diff($methodNamesAfter, $methodNamesBefore);
     $methodsRemoved = array_diff($methodNamesBefore, $methodNamesAfter);
     $methodsToVerify = array_intersect($methodNamesBefore, $methodNamesAfter);
     // Here we only care about public methods as they are the only part of the API we care about
     // Removed methods can either be implemented in parent classes or not exist anymore
     foreach ($methodsRemoved as $method) {
         $methodBefore = $methodsBeforeKeyed[$method];
         $data = new ClassMethodRemoved($this->context, $this->fileBefore, $contextBefore, $methodBefore);
         $report->add($this->context, $data);
     }
     foreach ($methodsToVerify as $method) {
         /** @var \PhpParser\Node\Stmt\ClassMethod $methodBefore */
         $methodBefore = $methodsBeforeKeyed[$method];
         /** @var \PhpParser\Node\Stmt\ClassMethod $methodAfter */
         $methodAfter = $methodsAfterKeyed[$method];
         // Leave non-strict comparison here
         if ($methodBefore != $methodAfter) {
             $paramsBefore = $methodBefore->params;
             $paramsAfter = $methodAfter->params;
             $signatureResult = Signature::analyze($paramsBefore, $paramsAfter);
             $changes = ['parameter_added' => ClassMethodParameterAdded::class, 'parameter_removed' => ClassMethodParameterRemoved::class, 'parameter_renamed' => ClassMethodParameterNameChanged::class, 'parameter_typing_added' => ClassMethodParameterTypingAdded::class, 'parameter_typing_removed' => ClassMethodParameterTypingRemoved::class, 'parameter_default_added' => ClassMethodParameterDefaultAdded::class, 'parameter_default_removed' => ClassMethodParameterDefaultRemoved::class, 'parameter_default_value_changed' => ClassMethodParameterDefaultValueChanged::class];
             foreach ($changes as $changeType => $class) {
                 if (!$signatureResult[$changeType]) {
                     continue;
                 }
                 if (is_a($class, ClassMethodOperationUnary::class, true)) {
                     $data = new $class($this->context, $this->fileAfter, $contextAfter, $methodAfter);
                 } else {
                     $data = new $class($this->context, $this->fileBefore, $contextBefore, $methodBefore, $this->fileAfter, $contextAfter, $methodAfter);
                 }
                 $report->add($this->context, $data);
             }
             // Difference in source code
             // Cast to array because interfaces do not have stmts (= null)
             if (!Implementation::isSame((array) $methodBefore->stmts, (array) $methodAfter->stmts)) {
                 $data = new ClassMethodImplementationChanged($this->context, $this->fileBefore, $contextBefore, $methodBefore, $this->fileAfter, $contextAfter, $methodAfter);
                 $report->add($this->context, $data);
             }
         }
     }
     // Added methods implies MINOR BUMP
     foreach ($methodsAdded as $method) {
         $methodAfter = $methodsAfterKeyed[$method];
         $data = new ClassMethodAdded($this->context, $this->fileAfter, $contextAfter, $methodAfter);
         $report->add($this->context, $data);
     }
     return $report;
 }