/// <inheritdoc/> public override bool ValidateOldTheorem(ContextualPicture contextualPicture, Theorem oldTheorem) { // If we don't care whether the intersection point is outside or inside of the picture, // then there is no reason to say a theorem is invalid if (!ExpectAnyExternalIntersection) { return(true); } // Otherwise it might have happened that the new point is the one where the old theorem // stated an intersection theorem. We need to check this. Let's take the new point var newPoint = contextualPicture.NewPoints.FirstOrDefault(); // If the last object hasn't been a point, then nothing as explained could have happened if (newPoint == null) { return(true); } // Otherwise we need to check whether the old theorem doesn't state that some objects // have an intersection point equal to the new point return(oldTheorem.InvolvedObjects // We know the objects are with points .Cast <TheoremObjectWithPoints>() // For each we will find the corresponding geometric object definable by points .Select(objectWithPoints => { // If the object is defined explicitly, then we simply ask the picture to do the job if (objectWithPoints.DefinedByExplicitObject) { return (DefinableByPoints)contextualPicture.GetGeometricObject(objectWithPoints.ConfigurationObject); } // Otherwise we need to find the inner points var innerPoints = objectWithPoints.Points // As geometric objects .Select(contextualPicture.GetGeometricObject) // They are points .Cast <PointObject>() // Enumerate .ToArray(); // Base on the type of object we will take all lines / circles passing through the first point return (objectWithPoints switch { // If we have a line, take lines LineTheoremObject _ => innerPoints[0].Lines.Cast <DefinableByPoints>(), // If we have a circle, take circles CircleTheoremObject _ => innerPoints[0].Circles, // Unhandled cases _ => throw new TheoremFinderException($"Unhandled type of {nameof(TheoremObjectWithPoints)}: {objectWithPoints.GetType()}") }) // Take the first line or circle that contains all the points .First(lineOrCircle => lineOrCircle.ContainsAll(innerPoints)); })
/// <summary> /// Finds all options for changing the definition of a given old theorem object based on the new (last) /// configuration object, which might have geometric properties related to this old object (for example, /// if the new object is a point and the old one is a line/circle, then it might lie on it). This includes /// even an option of not changing the definition at all, i.e. returning the original old object. /// </summary> /// <param name="oldTheoremObject">The old theorem object for which we're looking for definition change options.</param> /// <param name="contextualPicture">The contextual picture that represents the configuration.</param> /// <returns>The enumeration of all possible definition changes, including no change.</returns> private static IEnumerable <TheoremObject> FindDefinitionChangeOptions(TheoremObject oldTheoremObject, ContextualPicture contextualPicture) { // Find the new object of the configuration var newConfigurationObject = contextualPicture.Pictures.Configuration.LastConstructedObject; // Find its geometric version var newGeometricObject = contextualPicture.GetGeometricObject(newConfigurationObject); // Switch based on the type of old object switch (oldTheoremObject) { // If we have a point or line segment (internally consisting of points...) case PointTheoremObject _: case LineSegmentTheoremObject _: // Then there is no way to find a new definition of the object, // i.e. we return the 'no change of definition' option return(new[] { oldTheoremObject }); // If we have an object with points... case TheoremObjectWithPoints objectWithPoints: #region Find its geometric version // We're going to find the corresponding geometric version DefinableByPoints geometricObjectWithPoints = default; // If our object is defined explicitly if (objectWithPoints.DefinedByExplicitObject) { // Use the internal configuration object to get the definition directly geometricObjectWithPoints = (DefinableByPoints)contextualPicture.GetGeometricObject(objectWithPoints.ConfigurationObject); } // Otherwise it's defined by points else { // Get the points corresponding to its points var geometricPoints = objectWithPoints.Points // For each find the geometric point .Select(contextualPicture.GetGeometricObject) // We know they're points .Cast <PointObject>() // Enumerate .ToArray(); // Find the right object based on the type of the theorem object with points geometricObjectWithPoints = (objectWithPoints switch { // Looking for the line that contains our points // It certainly passes through the first point LineTheoremObject _ => geometricPoints[0].Lines.First(line => line.ContainsAll(geometricPoints)) as DefinableByPoints, // Looking for the circle that contains our points // It certainly passes through the first point CircleTheoremObject _ => geometricPoints[0].Circles.First(circle => circle.ContainsAll(geometricPoints)), // Unhandled cases _ => throw new TheoremFinderException($"Unhandled type of {nameof(TheoremObjectWithPoints)}: {objectWithPoints.GetType()}") }); }
/// <inheritdoc/> public IReadOnlyList <Theorem> InferTrivialTheoremsFromObject(ConstructedConfigurationObject constructedObject) { // Get the flattened arguments of the object for comfort var objects = constructedObject.PassedArguments.FlattenedList; // Switch on the construction switch (constructedObject.Construction) { // If we have a predefined construction... case PredefinedConstruction predefinedConstruction: // Switch on its type switch (predefinedConstruction.Type) { // An angle bisector makes an incidence case InternalAngleBisector: { // Create theorem objects var line1 = new LineTheoremObject(objects[0], objects[1]); var line2 = new LineTheoremObject(objects[0], objects[2]); var bisector = new LineTheoremObject(constructedObject); // Create the theorem return(new[] { new Theorem(Incidence, objects[0], constructedObject) }); } // Point reflection makes collinear points and equally distanced points case PointReflection: // Create the theorems return(new[] { // Collinearity new Theorem(CollinearPoints, objects[0], objects[1], constructedObject), // Equal distances new Theorem(EqualLineSegments, new TheoremObject[] { new LineSegmentTheoremObject(objects[1], objects[0]), new LineSegmentTheoremObject(objects[1], constructedObject) }) }); // Point reflection makes collinear points and equally distanced points case Midpoint: // Create the theorems return(new[] { // Collinearity new Theorem(CollinearPoints, objects[0], objects[1], constructedObject), // Equal distances new Theorem(EqualLineSegments, new TheoremObject[] { new LineSegmentTheoremObject(objects[0], constructedObject), new LineSegmentTheoremObject(objects[1], constructedObject) }) }); // Perpendicular projection makes perpendicular lines and incidence case PerpendicularProjection: // Create the theorem return(new[] { // Perpendicular lines new Theorem(PerpendicularLines, new TheoremObject[] { new LineTheoremObject(objects[0], constructedObject), new LineTheoremObject(objects[1]) }), // Incidence new Theorem(Incidence, objects[1], constructedObject) }); // Perpendicular line makes (surprisingly) perpendicular lines and incidence case PerpendicularLine: // Create the theorems return(new[] { // Perpendicular lines new Theorem(PerpendicularLines, new TheoremObject[] { new LineTheoremObject(constructedObject), new LineTheoremObject(objects[1]) }), // Incidence new Theorem(Incidence, objects[0], constructedObject) }); // Parallel line makes (surprisingly) parallel lines and incidence case ParallelLine: // Create the theorems return(new[] { // Parallel lines new Theorem(ParallelLines, new TheoremObject[] { new LineTheoremObject(constructedObject), new LineTheoremObject(objects[1]) }), // Incidence new Theorem(Incidence, objects[0], constructedObject) }); // Second intersection of two circumcircles makes concyclic points case SecondIntersectionOfTwoCircumcircles: // Create the theorems return(new[] { new Theorem(ConcyclicPoints, constructedObject, objects[0], objects[1], objects[2]), new Theorem(ConcyclicPoints, constructedObject, objects[0], objects[3], objects[4]) }); // Second intersection of a circle and line from point make collinear and concyclic points case SecondIntersectionOfCircleAndLineFromPoints: // Create the theorems return(new[] { // Collinearity new Theorem(CollinearPoints, constructedObject, objects[0], objects[1]), // Concyclity new Theorem(ConcyclicPoints, constructedObject, objects[0], objects[2], objects[3]) }); // Line makes incidences case LineFromPoints: // Create the theorems return(new[] { new Theorem(Incidence, constructedObject, objects[0]), new Theorem(Incidence, constructedObject, objects[1]) }); // Circumcircle makes incidences case Circumcircle: // Create the theorems return(new[] { new Theorem(Incidence, constructedObject, objects[0]), new Theorem(Incidence, constructedObject, objects[1]), new Theorem(Incidence, constructedObject, objects[2]) }); // Circle with center through point makes incidence case CircleWithCenterThroughPoint: // Create the theorem return(new[] { new Theorem(Incidence, constructedObject, objects[1]) }); // Intersection of lines makes incidences case IntersectionOfLines: // Create the theorems return(new[] { new Theorem(Incidence, constructedObject, objects[0]), new Theorem(Incidence, constructedObject, objects[1]) }); // Center of circle brings nothing :/ case CenterOfCircle: return(Array.Empty <Theorem>()); // Unhandled cases default: throw new TheoremProverException($"Unhandled value of {nameof(PredefinedConstructionType)}: {predefinedConstruction.Type}"); } // If we have a composed construction... case ComposedConstruction composedConstruction: // If we haven't found the theorems yet... if (!_originalInstances.ContainsKey(composedConstruction.Name)) { // Let the helper method do the job var newTheorems = FindTheoremsForComposedConstruction(composedConstruction); // Add them _composedConstructionTheorems.Add(composedConstruction, newTheorems); // Mark the used instance _originalInstances.Add(composedConstruction.Name, composedConstruction); } // Get the original instance used for the theorems composedConstruction = _originalInstances[composedConstruction.Name]; // Get the theorems var theorems = _composedConstructionTheorems[composedConstruction]; // Create the mapping of loose objects of the template configuration var mapping = composedConstruction.Configuration.LooseObjects.Cast <ConfigurationObject>() // and the template construction output .Concat(composedConstruction.ConstructionOutput) // to the actual objects that created the constructed object and the constructed object itself .ZipToDictionary(objects.Concat(constructedObject)); // Remap all the theorems return(theorems.Select(theorem => theorem.Remap(mapping)).ToList()); // Unhandled cases default: throw new TheoremProverException($"Unhandled type of {nameof(Construction)}: {constructedObject.Construction.GetType()}"); } }