예제 #1
0
        //Demonstrates: congruent chords have congruent arcs

        public Page306Theorem7_4_1(bool onoff, bool complete) : base(onoff, complete)
        {
            Point o = new Point("O", 0, 0); points.Add(o);
            Point r = new Point("R", -3, 4); points.Add(r);
            Point s = new Point("S", 2, Math.Sqrt(21)); points.Add(s);
            Point t = new Point("T", 2, -Math.Sqrt(21)); points.Add(t);
            Point u = new Point("U", -3, -4); points.Add(u);

            Segment rt = new Segment(r, t);
            Segment su = new Segment(s, u);
            Point   v  = rt.FindIntersection(su); points.Add(v);

            Segment rs = new Segment(r, s); segments.Add(rs);
            Segment ut = new Segment(u, t); segments.Add(ut);

            Circle c = new Circle(o, 5.0);

            circles.Add(c);

            parser = new GeometryTutorLib.TutorParser.HardCodedParserMain(points, collinear, segments, circles, onoff);

            given.Add(new GeometricCongruentSegments(rs, ut));

            MinorArc a1  = (MinorArc)parser.Get(new MinorArc(c, r, s));
            MinorArc a2  = (MinorArc)parser.Get(new MinorArc(c, t, u));
            MajorArc ma1 = (MajorArc)parser.Get(new MajorArc(c, r, s));
            MajorArc ma2 = (MajorArc)parser.Get(new MajorArc(c, t, u));

            goals.Add(new GeometricCongruentArcs(a1, a2));
            goals.Add(new GeometricCongruentArcs(ma1, ma2));
        }
예제 #2
0
    // Construct the region between a circle and circle:
    //     __
    //    ( (
    //   ( (
    //  ( (
    //   ( (
    //    ( (
    //     --
    private Atomizer.AtomicRegion ConstructBasicCircleCircleRegion(Segment chord, Circle smaller, Circle larger)
    {
        AtomicRegion region = new AtomicRegion();

        Arc arc1 = null;

        if (smaller.DefinesDiameter(chord))
        {
            Point midpt = smaller.Midpoint(chord.Point1, chord.Point2, larger.Midpoint(chord.Point1, chord.Point2));

            arc1 = new Semicircle(smaller, chord.Point1, chord.Point2, midpt, chord);
        }
        else
        {
            arc1 = new MinorArc(smaller, chord.Point1, chord.Point2);
        }

        MinorArc arc2 = new MinorArc(larger, chord.Point1, chord.Point2);

        region.AddConnection(chord.Point1, chord.Point2, ConnectionType.ARC, arc1);

        region.AddConnection(chord.Point1, chord.Point2, ConnectionType.ARC, arc2);

        return(region);
    }
예제 #3
0
        //Demonstrates: Use of AngleArc equation, Central angle equal to intercepted arc

        public Test01(bool onoff, bool complete) : base(onoff, complete)
        {
            Point o = new Point("O", 0, 0); points.Add(o);
            Point a = new Point("A", -5, 0); points.Add(a);
            Point b = new Point("B", 5, 0); points.Add(b);
            Point c = new Point("C", 0, 5); points.Add(c);


            Segment oc = new Segment(o, c); segments.Add(oc);

            List <Point> pnts = new List <Point>();

            pnts.Add(a);
            pnts.Add(o);
            pnts.Add(b);
            collinear.Add(new Collinear(pnts));

            Circle circle = new Circle(o, 5.0);

            circles.Add(circle);

            parser = new GeometryTutorLib.TutorParser.HardCodedParserMain(points, collinear, segments, circles, onoff);

            MinorArc m1 = (MinorArc)parser.Get(new MinorArc(circle, a, c));
            MinorArc m2 = (MinorArc)parser.Get(new MinorArc(circle, b, c));

            given.Add(new GeometricCongruentAngles((Angle)parser.Get(new Angle(a, o, c)), (Angle)parser.Get(new Angle(b, o, c))));
            goals.Add(new GeometricCongruentArcs(m1, m2));
        }
        //
        //    A \ 
        //       \    B
        //        \  /
        //  O      \/ X
        //         /\
        //        /  \
        //     C /    D
        //
        // Two tangents:
        // Intersection(X, AD, BC), Tangent(Circle(O), BC), Tangent(Circle(O), AD) -> 2 * Angle(AXC) = MajorArc(AC) - MinorArc(AC)
        //
        public static List <EdgeAggregator> InstantiateTwoTangentsTheorem(Tangent tangent1, Tangent tangent2, Intersection inter, GroundedClause original1, GroundedClause original2)
        {
            List <EdgeAggregator> newGrounded = new List <EdgeAggregator>();

            CircleSegmentIntersection tan1 = tangent1.intersection as CircleSegmentIntersection;
            CircleSegmentIntersection tan2 = tangent2.intersection as CircleSegmentIntersection;

            if (tan1.StructurallyEquals(tan2))
            {
                return(newGrounded);
            }

            // Do the tangents apply to the same circle?
            if (!tan1.theCircle.StructurallyEquals(tan2.theCircle))
            {
                return(newGrounded);
            }

            Circle circle = tan1.theCircle;

            // Do these tangents work with this intersection?
            if (!inter.HasSegment(tan1.segment) || !inter.HasSegment(tan2.segment))
            {
                return(newGrounded);
            }

            // Overkill? Do the tangents intersect at the same point as the intersection's intersect point?
            if (!tan1.segment.FindIntersection(tan2.segment).StructurallyEquals(inter.intersect))
            {
                return(newGrounded);
            }

            //
            // Get the arcs
            //
            Arc minorArc = new MinorArc(circle, tan1.intersect, tan2.intersect);
            Arc majorArc = new MajorArc(circle, tan1.intersect, tan2.intersect);

            Angle theAngle = new Angle(tan1.intersect, inter.intersect, tan2.intersect);

            //
            // Construct the new relationship
            //
            NumericValue two = new NumericValue(2);

            GeometricAngleArcEquation gaaeq = new GeometricAngleArcEquation(new Multiplication(two, theAngle), new Subtraction(majorArc, minorArc));

            // For hypergraph
            List <GroundedClause> antecedent = new List <GroundedClause>();

            antecedent.Add(original1);
            antecedent.Add(original2);
            antecedent.Add(inter);
            antecedent.Add(majorArc);
            antecedent.Add(minorArc);

            newGrounded.Add(new EdgeAggregator(antecedent, gaaeq, annotation));

            return(newGrounded);
        }
예제 #5
0
        public Page2Prob18(bool onoff, bool complete) : base(onoff, complete)
        {
            Point o = new Point("O", 0, 0); points.Add(o);
            Point a = new Point("A", 0, 4); points.Add(a);
            Point b = new Point("B", 4, 0); points.Add(b);

            Segment ab = new Segment(a, b); segments.Add(ab);
            Segment ao = new Segment(a, o); segments.Add(ao);
            Segment bo = new Segment(b, o); segments.Add(bo);

            Circle theCircle = new Circle(o, 4);

            circles.Add(theCircle);

            parser = new GeometryTutorLib.TutorParser.HardCodedParserMain(points, collinear, segments, circles, onoff);

            MinorArc arcAB = (MinorArc)parser.Get(new MinorArc(theCircle, a, b));

            given.Add(new GeometricArcEquation(arcAB, new NumericValue(90.0)));
            known.AddArcMeasureDegree(arcAB, 90.0);
            known.AddSegmentLength(ao, 4.0);
            known.AddSegmentLength(bo, 4.0);

            List <Point> wanted = new List <Point>();

            wanted.Add(new Point("", 0, -1));
            wanted.Add(new Point("", 1, 1));
            goalRegions = parser.implied.GetAtomicRegionsByPoints(wanted);

            SetSolutionArea(45.69911184);

            problemName = "Jurgensen Page 2 Problem 18";
            GeometryTutorLib.EngineUIBridge.HardCodedProblemsToUI.AddProblem(problemName, points, circles, segments);
        }
예제 #6
0
        //
        // If one of the endpoints of that is inside of this; and vice versa.
        //
        public bool Overlap(Connection that)
        {
            if (that.type != this.type)
            {
                return(false);
            }

            if (this.type == ConnectionType.ARC)
            {
                if (!(this.segmentOrArc as Arc).StructurallyEquals(this.segmentOrArc as Arc))
                {
                    return(false);
                }

                // If the arcs just touch, it's not overlap.
                if (this.segmentOrArc is MinorArc)
                {
                    MinorArc minor = this.segmentOrArc as MinorArc;
                    if (minor.PointLiesStrictlyOn(that.endpoint1) || minor.PointLiesStrictlyOn(that.endpoint2))
                    {
                        return(true);
                    }
                }
                else if (this.segmentOrArc is Semicircle)
                {
                    Semicircle semi = this.segmentOrArc as Semicircle;
                    if (semi.PointLiesStrictlyOn(that.endpoint1) || semi.PointLiesStrictlyOn(that.endpoint2))
                    {
                        return(true);
                    }
                }
                else if (this.segmentOrArc is MajorArc)
                {
                    MajorArc major = this.segmentOrArc as MajorArc;
                    if (major.PointLiesStrictlyOn(that.endpoint1) || major.PointLiesStrictlyOn(that.endpoint2))
                    {
                        return(true);
                    }
                }
                return(false);
            }
            else if (this.type == ConnectionType.SEGMENT)
            {
                Segment thisSegment = this.segmentOrArc as Segment;
                Segment thatSegment = that.segmentOrArc as Segment;

                if (!thisSegment.IsCollinearWith(thatSegment))
                {
                    return(false);
                }

                return(thisSegment.CoincidingWithOverlap(thatSegment));
            }

            return(false);
        }
예제 #7
0
        private List <Atomizer.AtomicRegion> ConvertToTruncation(Segment chord, MinorArc arc)
        {
            AtomicRegion atom = new AtomicRegion();

            atom.AddConnection(new Connection(chord.Point1, chord.Point2, ConnectionType.SEGMENT, chord));

            atom.AddConnection(new Connection(chord.Point1, chord.Point2, ConnectionType.ARC, arc));

            return(Utilities.MakeList <AtomicRegion>(atom));
        }
예제 #8
0
        public static List <EdgeAggregator> Instantiate(GroundedClause c)
        {
            annotation.active = EngineUIBridge.JustificationSwitch.ARC_ADDITION_AXIOM;

            List <EdgeAggregator> newGrounded = new List <EdgeAggregator>();
            ArcInMiddle           im          = c as ArcInMiddle;

            if (im == null)
            {
                return(newGrounded);
            }

            Addition sum = null;

            // Temporarily assume that the two arcs formed by the intersection are both minor arcs
            MinorArc a1 = new MinorArc(im.arc.theCircle, im.arc.endpoint1, im.point);
            MinorArc a2 = new MinorArc(im.arc.theCircle, im.point, im.arc.endpoint2);

            // If the intersected arc is a minor arc, this will always be true.
            // If the intersected arc is a major arc, this might be true. Other case is one minor arc and one major arc.
            if (Arc.BetweenMajor(im.point, im.arc))
            {
                // Check if both arcs are genuinely minor arcs.
                // If the other endpoint falls in the new arc, then the major arc should be used instead
                if (Arc.BetweenMinor(im.arc.endpoint2, a1))
                {
                    MajorArc majorArc = new MajorArc(im.arc.theCircle, im.arc.endpoint1, im.point);
                    sum = new Addition(majorArc, a2);
                }
                else if (Arc.BetweenMinor(im.arc.endpoint1, a2))
                {
                    MajorArc majorArc = new MajorArc(im.arc.theCircle, im.point, im.arc.endpoint2);
                    sum = new Addition(a1, majorArc);
                }
            }

            if (sum == null)
            {
                sum = new Addition(a1, a2);
            }

            GeometricArcEquation eq = new GeometricArcEquation(sum, im.arc);

            eq.MakeAxiomatic();

            // For hypergraph
            List <GroundedClause> antecedent = Utilities.MakeList <GroundedClause>(im);

            newGrounded.Add(new EdgeAggregator(antecedent, eq, annotation));


            return(newGrounded);
        }
예제 #9
0
        public Page6Row3Prob32b(bool onoff, bool complete)
            : base(onoff, complete)
        {
            double x = 8 * System.Math.Cos(36 * System.Math.PI / 180);
            double y = 8 * System.Math.Sin(36 * System.Math.PI / 180);
            Point  r = new Point("R", -x, y); points.Add(r);
            Point  p = new Point("P", 0, 0); points.Add(p);
            Point  s = new Point("S", x, y); points.Add(s);
            Point  q = new Point("Q", 0, -4); points.Add(q);

            Segment rp = new Segment(r, p); segments.Add(rp);
            Segment ps = new Segment(p, s); segments.Add(ps);
            Segment pq = new Segment(p, q); segments.Add(pq);

            Circle outer = new Circle(p, 8);
            Circle inner = new Circle(q, 4);

            circles.Add(outer);
            circles.Add(inner);

            parser = new GeometryTutorLib.TutorParser.HardCodedParserMain(points, collinear, segments, circles, onoff);

            MinorArc m = (MinorArc)parser.Get(new MinorArc(outer, r, s));

            known.AddSegmentLength(pq, 4);
            known.AddArcMeasureDegree(m, 108);

            given.Add(new GeometricArcEquation(m, new NumericValue(108)));

            List <Point> wanted = new List <Point>();

            wanted.Add(new Point("", -6, 0));
            wanted.Add(new Point("", -2, 0));
            wanted.Add(new Point("", 2, 0));
            wanted.Add(new Point("", 6, 0));
            goalRegions = parser.implied.GetAtomicRegionsByPoints(wanted);

            SetSolutionArea(90.47786842);

            problemName = "McDougall Page 6 Row 3 Problem 32b";
            GeometryTutorLib.EngineUIBridge.HardCodedProblemsToUI.AddProblem(problemName, points, circles, segments);
        }
        private void CreateMajorMinorArcs(Circle circle, int p1, int p2)
        {
            List <Point> minorArcPoints;
            List <Point> majorArcPoints;

            PartitionArcPoints(circle, p1, p2, out minorArcPoints, out majorArcPoints);

            MinorArc newMinorArc    = new MinorArc(circle, circle.pointsOnCircle[p1], circle.pointsOnCircle[p2], minorArcPoints, majorArcPoints);
            MajorArc newMajorArc    = new MajorArc(circle, circle.pointsOnCircle[p1], circle.pointsOnCircle[p2], minorArcPoints, majorArcPoints);
            Sector   newMinorSector = new Sector(newMinorArc);
            Sector   newMajorSector = new Sector(newMajorArc);

            if (!GeometryTutorLib.Utilities.HasStructurally <MinorArc>(minorArcs, newMinorArc))
            {
                minorArcs.Add(newMinorArc);
                minorSectors.Add(newMinorSector);
                majorSectors.Add(newMajorSector);

                angles.Add(new Angle(circle.pointsOnCircle[p1], circle.center, circle.pointsOnCircle[p2]));
            }
            if (!GeometryTutorLib.Utilities.HasStructurally <MajorArc>(majorArcs, newMajorArc))
            {
                majorArcs.Add(newMajorArc);
                majorSectors.Add(newMajorSector);
            }

            circle.AddMinorArc(newMinorArc);
            circle.AddMajorArc(newMajorArc);
            circle.AddMinorSector(newMinorSector);
            circle.AddMajorSector(newMajorSector);

            // Generate ArcInMiddle clauses for minor arc and major arc
            for (int imIndex = 0; imIndex < newMinorArc.arcMinorPoints.Count; imIndex++)
            {
                GeometryTutorLib.Utilities.AddStructurallyUnique <ArcInMiddle>(arcInMiddle, new ArcInMiddle(newMinorArc.arcMinorPoints[imIndex], newMinorArc));
            }
            for (int imIndex = 0; imIndex < newMajorArc.arcMajorPoints.Count; imIndex++)
            {
                GeometryTutorLib.Utilities.AddStructurallyUnique <ArcInMiddle>(arcInMiddle, new ArcInMiddle(newMajorArc.arcMajorPoints[imIndex], newMajorArc));
            }
        }
예제 #11
0
        //Demonstrates: ChordTangentAngleHalfInterceptedArc

        public Test02(bool onoff, bool complete)
            : base(onoff, complete)
        {
            Point o = new Point("O", 0, 0); points.Add(o);
            Point r = new Point("R", -3, -5); points.Add(r);
            Point s = new Point("S", 8, -5); points.Add(s);
            Point t = new Point("T", 0, -5); points.Add(t);
            Point u = new Point("U", -3, 4); points.Add(u);
            Point v = new Point("V", 3, 4); points.Add(v);

            Segment tu = new Segment(t, u); segments.Add(tu);
            Segment tv = new Segment(t, v); segments.Add(tv);

            List <Point> pnts = new List <Point>();

            pnts.Add(r);
            pnts.Add(t);
            pnts.Add(s);
            collinear.Add(new Collinear(pnts));

            Circle c = new Circle(o, 5.0);

            circles.Add(c);

            parser = new GeometryTutorLib.TutorParser.HardCodedParserMain(points, collinear, segments, circles, onoff);

            MinorArc m1 = (MinorArc)parser.Get(new MinorArc(c, t, u));
            MinorArc m2 = (MinorArc)parser.Get(new MinorArc(c, t, v));
            Segment  rs = (Segment)parser.Get(new Segment(r, s));

            CircleSegmentIntersection cInter = (CircleSegmentIntersection)parser.Get(new CircleSegmentIntersection(t, c, rs));

            given.Add(new Strengthened(cInter, new Tangent(cInter)));
            given.Add(new GeometricCongruentAngles((Angle)parser.Get(new Angle(r, t, u)), (Angle)parser.Get(new Angle(s, t, v))));

            goals.Add(new GeometricCongruentArcs(m1, m2));
        }
예제 #12
0
    // Construct the region between a chord and the circle arc:
    //    (|
    //   ( |
    //  (  |
    //   ( |
    //    (|
    //
    private List <AtomicRegion> ConstructBasicLineCircleRegion(Segment chord, Circle circle)
    {
        //
        // Standard
        //
        if (!circle.DefinesDiameter(chord))
        {
            AtomicRegion region = new AtomicRegion();

            Arc theArc = new MinorArc(circle, chord.Point1, chord.Point2);

            region.AddConnection(chord.Point1, chord.Point2, ConnectionType.ARC, theArc);

            region.AddConnection(chord.Point1, chord.Point2, ConnectionType.SEGMENT, chord);

            return(Utilities.MakeList <AtomicRegion>(region));
        }

        //
        // Semi-circles
        //

        Point             midpt   = circle.Midpoint(chord.Point1, chord.Point2);
        Arc               semi1   = new Semicircle(circle, chord.Point1, chord.Point2, midpt, chord);
        ShapeAtomicRegion region1 = new ShapeAtomicRegion(new Sector(semi1));

        Point             opp     = circle.OppositePoint(midpt);
        Arc               semi2   = new Semicircle(circle, chord.Point1, chord.Point2, opp, chord);
        ShapeAtomicRegion region2 = new ShapeAtomicRegion(new Sector(semi2));

        List <AtomicRegion> regions = new List <AtomicRegion>();

        regions.Add(region1);
        regions.Add(region2);

        return(regions);
    }
예제 #13
0
        public Page2Prob20(bool onoff, bool complete) : base(onoff, complete)
        {
            double x = System.Math.Sqrt(3);
            double y = 1;
            Point  a = new Point("A", -x, y); points.Add(a);
            Point  b = new Point("B", x, y); points.Add(b);
            Point  c = new Point("C", -x, -y); points.Add(c);
            Point  d = new Point("D", x, -y); points.Add(d);
            Point  o = new Point("O", 0, 0); points.Add(o);

            Segment ab = new Segment(a, b); segments.Add(ab);
            Segment cd = new Segment(c, d); segments.Add(cd);
            Segment ac = new Segment(a, c); segments.Add(ac);
            //Segment bd = new Segment(b, d); segments.Add(bd);

            List <Point> pnts = new List <Point>();

            pnts.Add(a);
            pnts.Add(o);
            pnts.Add(d);
            collinear.Add(new Collinear(pnts));

            pnts = new List <Point>();
            pnts.Add(b);
            pnts.Add(o);
            pnts.Add(c);
            collinear.Add(new Collinear(pnts));

            Circle circle = new Circle(o, 2);

            circles.Add(circle);

            parser = new GeometryTutorLib.TutorParser.HardCodedParserMain(points, collinear, segments, circles, onoff);

            MinorArc arcAB = (MinorArc)parser.Get(new MinorArc(circle, a, b));
            MinorArc arcBD = (MinorArc)parser.Get(new MinorArc(circle, b, d));
            MinorArc arcDC = (MinorArc)parser.Get(new MinorArc(circle, d, c));
            MinorArc arcAC = (MinorArc)parser.Get(new MinorArc(circle, a, c));

            known.AddSegmentLength(ac, 2);
            known.AddArcMeasureDegree(arcAB, 120);
            known.AddArcMeasureDegree(arcBD, 60);
            known.AddArcMeasureDegree(arcDC, 120);
            known.AddArcMeasureDegree(arcAC, 60);

            given.Add(new GeometricArcEquation(arcAB, new NumericValue(120)));
            given.Add(new GeometricArcEquation(arcBD, new NumericValue(60)));
            given.Add(new GeometricArcEquation(arcDC, new NumericValue(120)));
            given.Add(new GeometricArcEquation(arcAC, new NumericValue(60)));

            List <Point> unwanted = new List <Point>();

            unwanted.Add(new Point("", 0, y + 0.2));
            unwanted.Add(new Point("", 0, -y - 0.2));
            goalRegions = parser.implied.GetAllAtomicRegionsWithoutPoints(unwanted);

            SetSolutionArea((4.0 / 3.0) * System.Math.PI + 2.0 * System.Math.Sqrt(3.0));

            problemName = "Jurgensen Page 2 Problem 20";
            GeometryTutorLib.EngineUIBridge.HardCodedProblemsToUI.AddProblem(problemName, points, circles, segments);
        }
예제 #14
0
        //Demonstrates: ExteriorAngleHalfDifferenceInterceptedArcs : two secants

        public Test04(bool onoff, bool complete)
            : base(onoff, complete)
        {
            //Circle
            Point  o       = new Point("O", 0, 0); points.Add(o);
            Circle circleO = new Circle(o, 5.0);

            circles.Add(circleO);

            //Intersection point for two secants
            Point x = new Point("X", 0, 6); points.Add(x);

            //Secant intersection points for circle O
            Point a = new Point("A", -3, -4); points.Add(a);
            Point b = new Point("B", 3, -4); points.Add(b);
            Point c, d, trash;

            circleO.FindIntersection(new Segment(b, x), out c, out trash);
            if (b.StructurallyEquals(c))
            {
                c = trash;
            }
            c = new Point("C", c.X, c.Y);
            points.Add(c);
            circleO.FindIntersection(new Segment(a, x), out d, out trash);
            if (a.StructurallyEquals(d))
            {
                d = trash;
            }
            d = new Point("D", d.X, d.Y);
            points.Add(d);

            //Create point for another arc (Arc(CE)) of equal measure to (1/2)*(Arc(AB)-Arc(CD))
            Point e = new Point("E", 3, 4); points.Add(e);

            //Should now be able to form segments for a central angle of equal measure to (1/2)*(Arc(AB)-Arc(CD))
            Segment oc = new Segment(o, c); segments.Add(oc);
            Segment oe = new Segment(o, e); segments.Add(oe);

            //Label the intersection betweeen OE and BX
            Point i = oe.FindIntersection(new Segment(b, x));

            i = new Point("I", i.X, i.Y); points.Add(i);

            List <Point> pnts = new List <Point>();

            pnts.Add(a);
            pnts.Add(d);
            pnts.Add(x);
            collinear.Add(new Collinear(pnts));

            pnts = new List <Point>();
            pnts.Add(b);
            pnts.Add(i);
            pnts.Add(c);
            pnts.Add(x);
            collinear.Add(new Collinear(pnts));

            parser = new LiveGeometry.TutorParser.HardCodedParserMain(points, collinear, segments, circles, onoff);

            MinorArc farMinor1       = (MinorArc)parser.Get(new MinorArc(circleO, a, b));
            MinorArc closeMinor1     = (MinorArc)parser.Get(new MinorArc(circleO, c, d));
            MinorArc centralAngleArc = (MinorArc)parser.Get(new MinorArc(circleO, c, e));

            given.Add(new GeometricArcEquation(new Multiplication(new NumericValue(2), centralAngleArc), new Subtraction(farMinor1, closeMinor1)));

            goals.Add(new GeometricCongruentAngles((Angle)parser.Get(new Angle(a, x, b)), (Angle)parser.Get(new Angle(c, o, e))));
        }
예제 #15
0
        private List <Atomizer.AtomicRegion> MixedArcChordedRegion(List <Circle> thatCircles, UndirectedPlanarGraph.PlanarGraph graph)
        {
            List <AtomicRegion> regions = new List <AtomicRegion>();

            // Every segment may be have a set of circles. (on each side) surrounding it.
            // Keep parallel lists of: (1) segments, (2) (real) arcs, (3) left outer circles, and (4) right outer circles
            Segment[] regionsSegments   = new Segment[points.Count];
            Arc[]     arcSegments       = new Arc[points.Count];
            Circle[]  leftOuterCircles  = new Circle[points.Count];
            Circle[]  rightOuterCircles = new Circle[points.Count];

            //
            // Populate the parallel arrays.
            //
            int currCounter = 0;

            for (int p = 0; p < points.Count;)
            {
                UndirectedPlanarGraph.PlanarGraphEdge edge = graph.GetEdge(points[p], points[(p + 1) % points.Count]);
                Segment currSegment = new Segment(points[p], points[(p + 1) % points.Count]);

                //
                // If a known segment, seek a sequence of collinear segments.
                //
                if (edge.edgeType == UndirectedPlanarGraph.EdgeType.REAL_SEGMENT)
                {
                    Segment actualSeg = currSegment;

                    bool collinearExists = false;
                    int  prevPtIndex;
                    for (prevPtIndex = p + 1; prevPtIndex < points.Count; prevPtIndex++)
                    {
                        // Make another segment with the next point.
                        Segment nextSeg = new Segment(points[p], points[(prevPtIndex + 1) % points.Count]);

                        // CTA: This criteria seems invalid in some cases....; may not have collinearity

                        // We hit the end of the line of collinear segments.
                        if (!currSegment.IsCollinearWith(nextSeg))
                        {
                            break;
                        }

                        collinearExists = true;
                        actualSeg       = nextSeg;
                    }

                    // If there exists an arc over the actual segment, we have an embedded circle to consider.
                    regionsSegments[currCounter] = actualSeg;

                    if (collinearExists)
                    {
                        UndirectedPlanarGraph.PlanarGraphEdge collEdge = graph.GetEdge(actualSeg.Point1, actualSeg.Point2);
                        if (collEdge != null)
                        {
                            if (collEdge.edgeType == UndirectedPlanarGraph.EdgeType.REAL_ARC)
                            {
                                // Find all applicable circles
                                List <Circle> circles = GetAllApplicableCircles(thatCircles, actualSeg.Point1, actualSeg.Point2);

                                // Get the exact outer circles for this segment (and create any embedded regions).
                                regions.AddRange(ConvertToCircleCircle(actualSeg, circles, out leftOuterCircles[currCounter], out rightOuterCircles[currCounter]));
                            }
                        }
                    }

                    currCounter++;
                    p = prevPtIndex;
                }
                else if (edge.edgeType == UndirectedPlanarGraph.EdgeType.REAL_DUAL)
                {
                    regionsSegments[currCounter] = new Segment(points[p], points[(p + 1) % points.Count]);

                    // Get the exact chord and set of circles
                    Segment chord = regionsSegments[currCounter];

                    // Find all applicable circles
                    List <Circle> circles = GetAllApplicableCircles(thatCircles, points[p], points[(p + 1) % points.Count]);

                    // Get the exact outer circles for this segment (and create any embedded regions).
                    regions.AddRange(ConvertToCircleCircle(chord, circles, out leftOuterCircles[currCounter], out rightOuterCircles[currCounter]));

                    currCounter++;
                    p++;
                }
                else if (edge.edgeType == UndirectedPlanarGraph.EdgeType.REAL_ARC)
                {
                    //
                    // Find the unique circle that contains these two points.
                    // (if more than one circle has these points, we would have had more intersections and it would be a direct chorded region)
                    //
                    List <Circle> circles = GetAllApplicableCircles(thatCircles, points[p], points[(p + 1) % points.Count]);

                    if (circles.Count != 1)
                    {
                        throw new Exception("Need ONLY 1 circle for REAL_ARC atom id; found (" + circles.Count + ")");
                    }

                    arcSegments[currCounter++] = new MinorArc(circles[0], points[p], points[(p + 1) % points.Count]);

                    p++;
                }
            }

            //
            // Check to see if this is a region in which some connections are segments and some are arcs.
            // This means there were no REAL_DUAL edges.
            //
            List <AtomicRegion> generalRegions = GeneralAtomicRegion(regionsSegments, arcSegments);

            if (generalRegions.Any())
            {
                return(generalRegions);
            }

            // Copy the segments into a list (ensuring no nulls)
            List <Segment> actSegments = new List <Segment>();

            foreach (Segment side in regionsSegments)
            {
                if (side != null)
                {
                    actSegments.Add(side);
                }
            }

            // Construct a polygon out of the straight-up segments
            // This might be a polygon that defines a pathological region.
            Polygon poly = Polygon.MakePolygon(actSegments);

            // Determine which outermost circles apply inside of this polygon.
            Circle[] circlesCutInsidePoly = new Circle[actSegments.Count];
            for (int p = 0; p < actSegments.Count; p++)
            {
                if (leftOuterCircles[p] != null && rightOuterCircles[p] == null)
                {
                    circlesCutInsidePoly[p] = CheckCircleCutInsidePolygon(poly, leftOuterCircles[p], actSegments[p].Point1, actSegments[p].Point2);
                }
                else if (leftOuterCircles[p] == null && rightOuterCircles[p] != null)
                {
                    circlesCutInsidePoly[p] = CheckCircleCutInsidePolygon(poly, rightOuterCircles[p], actSegments[p].Point1, actSegments[p].Point2);
                }
                else if (leftOuterCircles[p] != null && rightOuterCircles[p] != null)
                {
                    circlesCutInsidePoly[p] = CheckCircleCutInsidePolygon(poly, leftOuterCircles[p], actSegments[p].Point1, actSegments[p].Point2);

                    if (circlesCutInsidePoly[p] == null)
                    {
                        circlesCutInsidePoly[p] = rightOuterCircles[p];
                    }
                }
                else
                {
                    circlesCutInsidePoly[p] = null;
                }
            }

            bool isStrictPoly = true;

            for (int p = 0; p < actSegments.Count; p++)
            {
                if (circlesCutInsidePoly[p] != null || arcSegments[p] != null)
                {
                    isStrictPoly = false;
                    break;
                }
            }

            // This is just a normal shape region: polygon.
            if (isStrictPoly)
            {
                regions.Add(new ShapeAtomicRegion(poly));
            }
            // A circle cuts into the polygon.
            else
            {
                //
                // Now that all interior arcs have been identified, construct the atomic (probably pathological) region
                //
                AtomicRegion pathological = new AtomicRegion();
                for (int p = 0; p < actSegments.Count; p++)
                {
                    //
                    // A circle cutting inside the polygon
                    //
                    if (circlesCutInsidePoly[p] != null)
                    {
                        Arc theArc = null;

                        if (circlesCutInsidePoly[p].DefinesDiameter(regionsSegments[p]))
                        {
                            Point midpt = circlesCutInsidePoly[p].Midpoint(regionsSegments[p].Point1, regionsSegments[p].Point2);

                            if (!poly.IsInPolygon(midpt))
                            {
                                midpt = circlesCutInsidePoly[p].OppositePoint(midpt);
                            }

                            theArc = new Semicircle(circlesCutInsidePoly[p], regionsSegments[p].Point1, regionsSegments[p].Point2, midpt, regionsSegments[p]);
                        }
                        else
                        {
                            theArc = new MinorArc(circlesCutInsidePoly[p], regionsSegments[p].Point1, regionsSegments[p].Point2);
                        }

                        pathological.AddConnection(regionsSegments[p].Point1, regionsSegments[p].Point2, ConnectionType.ARC, theArc);
                    }
                    //
                    else
                    {
                        // We have a direct arc
                        if (arcSegments[p] != null)
                        {
                            pathological.AddConnection(regionsSegments[p].Point1, regionsSegments[p].Point2,
                                                       ConnectionType.ARC, arcSegments[p]);
                        }
                        // Use the segment
                        else
                        {
                            pathological.AddConnection(regionsSegments[p].Point1, regionsSegments[p].Point2,
                                                       ConnectionType.SEGMENT, regionsSegments[p]);
                        }
                    }
                }

                regions.Add(pathological);
            }


            return(regions);
        }
        //
        //          C
        //         /)
        //        /  )
        //       / )
        //      / )
        //   A /)_________ B
        //
        // Tangent(Circle(O), Segment(AB)), Intersection(Segment(AC), Segment(AB)) -> 2 * Angle(CAB) = Arc(C, B)
        //
        public static List <EdgeAggregator> InstantiateTheorem(Intersection inter, Tangent tangent, GroundedClause original)
        {
            List <EdgeAggregator> newGrounded = new List <EdgeAggregator>();

            CircleSegmentIntersection tan = tangent.intersection as CircleSegmentIntersection;

            //
            // Does this tangent apply to this intersection?
            //
            if (!inter.intersect.StructurallyEquals(tangent.intersection.intersect))
            {
                return(newGrounded);
            }

            Segment secant     = null;
            Segment tanSegment = null;

            if (tan.HasSegment(inter.lhs))
            {
                secant     = inter.rhs;
                tanSegment = inter.lhs;
            }
            else if (tan.HasSegment(inter.rhs))
            {
                secant     = inter.lhs;
                tanSegment = inter.rhs;
            }
            else
            {
                return(newGrounded);
            }

            //
            // Acquire the angle and intercepted arc.
            //
            Segment chord = tan.theCircle.GetChord(secant);

            if (chord == null)
            {
                return(newGrounded);
            }
            //Segment chord = tan.theCircle.ContainsChord(secant);

            // Arc
            // We want the MINOR ARC only!
            if (tan.theCircle.DefinesDiameter(chord))
            {
                Arc   theArc = null;
                Point midpt  = PointFactory.GeneratePoint(tan.theCircle.Midpoint(chord.Point1, chord.Point2));
                Point opp    = PointFactory.GeneratePoint(tan.theCircle.OppositePoint(midpt));

                Point tanPoint = tanSegment.OtherPoint(inter.intersect);

                if (tanPoint != null)
                {
                    // Angle; the smaller angle is always the chosen angle
                    Angle theAngle = new Angle(chord.OtherPoint(inter.intersect), inter.intersect, tanPoint);

                    theArc = new Semicircle(tan.theCircle, chord.Point1, chord.Point2, midpt, chord);
                    newGrounded.Add(CreateClause(inter, original, theAngle, theArc));

                    theArc = new Semicircle(tan.theCircle, chord.Point1, chord.Point2, opp, chord);
                    newGrounded.Add(CreateClause(inter, original, theAngle, theArc));
                }
                else
                {
                    // Angle; the smaller angle is always the chosen angle
                    Angle theAngle = new Angle(chord.OtherPoint(inter.intersect), inter.intersect, tanSegment.Point1);

                    theArc = new Semicircle(tan.theCircle, chord.Point1, chord.Point2, midpt, chord);
                    newGrounded.Add(CreateClause(inter, original, theAngle, theArc));

                    theArc = new Semicircle(tan.theCircle, chord.Point1, chord.Point2, opp, chord);
                    newGrounded.Add(CreateClause(inter, original, theAngle, theArc));

                    // Angle; the smaller angle is always the chosen angle
                    theAngle = new Angle(chord.OtherPoint(inter.intersect), inter.intersect, tanSegment.Point2);

                    theArc = new Semicircle(tan.theCircle, chord.Point1, chord.Point2, midpt, chord);
                    newGrounded.Add(CreateClause(inter, original, theAngle, theArc));

                    theArc = new Semicircle(tan.theCircle, chord.Point1, chord.Point2, opp, chord);
                    newGrounded.Add(CreateClause(inter, original, theAngle, theArc));
                }
            }
            else
            {
                Arc theArc = new MinorArc(tan.theCircle, chord.Point1, chord.Point2);

                // Angle; the smaller angle is always the chosen angle
                Point endPnt   = (inter.intersect.StructurallyEquals(tanSegment.Point1)) ? tanSegment.Point2 : tanSegment.Point1;
                Angle theAngle = new Angle(chord.OtherPoint(inter.intersect), inter.intersect, endPnt);

                if (theAngle.measure > 90)
                {
                    //If the angle endpoint was already set to Point2, or if the intersect equals Point2, then the smaller angle does not exist
                    //In this case, should we create a major arc or return nothing?
                    if (endPnt.StructurallyEquals(tanSegment.Point2) || inter.intersect.StructurallyEquals(tanSegment.Point2))
                    {
                        return(newGrounded);
                    }
                    theAngle = new Angle(chord.OtherPoint(inter.intersect), inter.intersect, tanSegment.Point2);
                }

                Multiplication            product  = new Multiplication(new NumericValue(2), theAngle);
                GeometricAngleArcEquation angArcEq = new GeometricAngleArcEquation(product, theArc);

                // For hypergraph
                List <GroundedClause> antecedent = new List <GroundedClause>();
                antecedent.Add(original);
                antecedent.Add(inter);
                antecedent.Add(theArc);
                antecedent.Add(theAngle);

                newGrounded.Add(new EdgeAggregator(antecedent, angArcEq, annotation));
            }



            return(newGrounded);
        }
예제 #17
0
        //
        // Order the arcs so the endpoints are clear in the first in last positions.
        //
        private List <MinorArc> SortArcSet(List <MinorArc> arcs)
        {
            if (arcs.Count <= 2)
            {
                return(arcs);
            }

            bool[]          marked = new bool[arcs.Count];
            List <MinorArc> sorted = new List <MinorArc>();

            //
            // Find the 'first' endpoint of the arc.
            //
            int sharedCount = 0;
            int arcIndex    = -1;

            for (int a1 = 0; a1 < arcs.Count; a1++)
            {
                sharedCount = 0;
                for (int a2 = 0; a2 < arcs.Count; a2++)
                {
                    if (a1 != a2)
                    {
                        if (arcs[a1].SharedEndpoint(arcs[a2]) != null)
                        {
                            sharedCount++;
                        }
                    }
                }
                arcIndex = a1;
                if (sharedCount == 1)
                {
                    break;
                }
            }

            // An 'end'-arc found; book-ends of list.
            switch (sharedCount)
            {
            case 0:
                throw new Exception("Expected a shared count of 1 or 2, not 0");

            case 1:
                sorted.Add(arcs[arcIndex]);
                marked[arcIndex] = true;
                break;

            case 2:
                // Middle arc
                break;

            default:
                throw new Exception("Expected a shared count of 1 or 2, not (" + sharedCount + ")");
            }

            MinorArc working = sorted[0];

            while (marked.Contains(false))
            {
                Point shared;
                for (arcIndex = 0; arcIndex < arcs.Count; arcIndex++)
                {
                    if (!marked[arcIndex])
                    {
                        shared = working.SharedEndpoint(arcs[arcIndex]);
                        if (shared != null)
                        {
                            break;
                        }
                    }
                }
                marked[arcIndex] = true;
                sorted.Add(arcs[arcIndex]);
                working = arcs[arcIndex];
            }

            return(sorted);
        }
예제 #18
0
        //Demonstrates: ExteriorAngleHalfDifferenceInterceptedArcs : one tangent, one secant

        public Test06(bool onoff, bool complete)
            : base(onoff, complete)
        {
            //Circle
            Point  o       = new Point("O", 0, 0); points.Add(o);
            Circle circleO = new Circle(o, 5.0);

            circles.Add(circleO);

            //Intersection point for tangent & secant
            Point c = new Point("C", 0, 6.25); points.Add(c);

            //Points for tangent line ac, intersection at b
            Point a = new Point("A", -8, 0.25); points.Add(a);
            Point b = new Point("B", -3, 4); points.Add(b);

            //Points for secant line ce, intersections at D & E
            Point d = new Point("D", 0, 5); points.Add(d);
            Point e = new Point("E", 0, -5); points.Add(e);

            //Create point for another arc (Arc(DF)) of equal measure to (1/2)*(MinorArc(BE)-MinorArc(BD))
            MinorArc farMinor   = new MinorArc(circleO, b, e);
            MinorArc closeMinor = new MinorArc(circleO, b, d);
            double   measure    = (farMinor.GetMinorArcMeasureDegrees() - closeMinor.GetMinorArcMeasureDegrees()) / 2;
            //Get theta for F
            double dThetaDegrees = 90;
            double fThetaRadians = (dThetaDegrees - measure) * (System.Math.PI / 180);
            //Get coordinates for F
            Point unitPnt = new Point("", System.Math.Cos(fThetaRadians), System.Math.Sin(fThetaRadians));
            Point f, trash;

            circleO.FindIntersection(new Segment(o, unitPnt), out f, out trash);
            if (f.X < 0)
            {
                f = trash;
            }
            f = new Point("F", f.X, f.Y); points.Add(f);

            //Should now be able to form segments for a central angle of equal measure to (1/2)*(Arc(AB)-Arc(CD))
            Segment od = new Segment(o, d); segments.Add(od);
            Segment of = new Segment(o, f); segments.Add(of);

            List <Point> pnts = new List <Point>();

            pnts.Add(a);
            pnts.Add(b);
            pnts.Add(c);
            collinear.Add(new Collinear(pnts));

            pnts = new List <Point>();
            pnts.Add(c);
            pnts.Add(d);
            pnts.Add(e);
            collinear.Add(new Collinear(pnts));

            parser = new GeometryTutorLib.TutorParser.HardCodedParserMain(points, collinear, segments, circles, onoff);

            Segment ac = (Segment)parser.Get(new Segment(a, c));
            CircleSegmentIntersection cInter = (CircleSegmentIntersection)parser.Get(new CircleSegmentIntersection(b, circleO, ac));

            given.Add(new Strengthened(cInter, new Tangent(cInter)));

            MinorArc far             = (MinorArc)parser.Get(new MinorArc(circleO, b, e));
            MinorArc close           = (MinorArc)parser.Get(new MinorArc(circleO, b, d));
            MinorArc centralAngleArc = (MinorArc)parser.Get(new MinorArc(circleO, d, f));

            given.Add(new GeometricArcEquation(new Multiplication(new NumericValue(2), centralAngleArc), new Subtraction(far, close)));

            goals.Add(new GeometricCongruentAngles((Angle)parser.Get(new Angle(a, c, e)), (Angle)parser.Get(new Angle(d, o, f))));
        }
예제 #19
0
        //Demonstrates: ExteriorAngleHalfDifferenceInterceptedArcs : two tangents
        //Demonstrates: Tangents from point are congruent

        public Test05(bool onoff, bool complete)
            : base(onoff, complete)
        {
            //Circle
            Point  o       = new Point("O", 0, 0); points.Add(o);
            Circle circleO = new Circle(o, 5.0);

            circles.Add(circleO);

            //Intersection point for two tangents
            Point c = new Point("C", 0, 6.25); points.Add(c);

            //Points for tangent line ac, intersection at b
            Point a = new Point("A", -8, 0.25); points.Add(a);
            Point b = new Point("B", -3, 4); points.Add(b);

            //Points for tangent line ec, intersection at d
            Point e = new Point("E", 8, 0.25); points.Add(e);
            Point d = new Point("D", 3, 4); points.Add(d);

            //Create point for another arc (Arc(DF)) of equal measure to (1/2)*(MajorArc(BD)-MinorArc(BD))
            MinorArc minor   = new MinorArc(circleO, b, d);
            MajorArc major   = new MajorArc(circleO, b, d);
            double   measure = (major.GetMajorArcMeasureDegrees() - minor.GetMinorArcMeasureDegrees()) / 2;
            //Get theta for D and E
            Circle unit = new Circle(o, 1.0);
            Point  inter1, trash;

            unit.FindIntersection(new Segment(o, d), out inter1, out trash);
            if (inter1.X < 0)
            {
                inter1 = trash;
            }
            double dThetaDegrees = (System.Math.Acos(inter1.X)) * (180 / System.Math.PI);
            double fThetaRadians = (dThetaDegrees - measure) * (System.Math.PI / 180);
            //Get coordinates for E
            Point unitPnt = new Point("", System.Math.Cos(fThetaRadians), System.Math.Sin(fThetaRadians));
            Point f;

            circleO.FindIntersection(new Segment(o, unitPnt), out f, out trash);
            if (f.X < 0)
            {
                f = trash;
            }
            f = new Point("F", f.X, f.Y); points.Add(f);

            //Should now be able to form segments for a central angle of equal measure to (1/2)*(Arc(AB)-Arc(CD))
            Segment od = new Segment(o, d); segments.Add(od);
            Segment of = new Segment(o, f); segments.Add(of);

            List <Point> pnts = new List <Point>();

            pnts.Add(a);
            pnts.Add(b);
            pnts.Add(c);
            collinear.Add(new Collinear(pnts));

            pnts = new List <Point>();
            pnts.Add(c);
            pnts.Add(d);
            pnts.Add(e);
            collinear.Add(new Collinear(pnts));

            parser = new LiveGeometry.TutorParser.HardCodedParserMain(points, collinear, segments, circles, onoff);

            Segment ac = (Segment)parser.Get(new Segment(a, c));
            Segment ce = (Segment)parser.Get(new Segment(c, e));
            CircleSegmentIntersection cInter  = (CircleSegmentIntersection)parser.Get(new CircleSegmentIntersection(b, circleO, ac));
            CircleSegmentIntersection cInter2 = (CircleSegmentIntersection)parser.Get(new CircleSegmentIntersection(d, circleO, ce));

            given.Add(new Strengthened(cInter, new Tangent(cInter)));
            given.Add(new Strengthened(cInter2, new Tangent(cInter2)));

            MinorArc a1 = (MinorArc)parser.Get(new MinorArc(circleO, b, d));
            MajorArc a2 = (MajorArc)parser.Get(new MajorArc(circleO, b, d));
            MinorArc centralAngleArc = (MinorArc)parser.Get(new MinorArc(circleO, d, f));

            given.Add(new GeometricArcEquation(new Multiplication(new NumericValue(2), centralAngleArc), new Subtraction(a2, a1)));

            goals.Add(new GeometricCongruentAngles((Angle)parser.Get(new Angle(a, c, e)), (Angle)parser.Get(new Angle(d, o, f))));
            goals.Add(new GeometricCongruentSegments((Segment)parser.Get(new Segment(b, c)), (Segment)parser.Get(new Segment(c, d))));
        }
        //               A
        //              /)
        //             /  )
        //            /    )
        // center:   O      )
        //            \    )
        //             \  )
        //              \)
        //               C
        //
        //               D
        //              /)
        //             /  )
        //            /    )
        // center:   Q      )
        //            \    )
        //             \  )
        //              \)
        //               F
        //
        // Congruent(Segment(AC), Segment(DF)) -> Congruent(Arc(A, C), Arc(D, F))
        //
        private static List <EdgeAggregator> InstantiateForwardPartOfTheorem(CongruentSegments cas)
        {
            List <EdgeAggregator> newGrounded = new List <EdgeAggregator>();

            //
            // Acquire the circles for which the segments are chords.
            //
            List <Circle> circles1 = Circle.GetChordCircles(cas.cs1);
            List <Circle> circles2 = Circle.GetChordCircles(cas.cs2);

            //
            // Make all possible combinations of arcs congruent
            //
            foreach (Circle circle1 in circles1)
            {
                // Create the appropriate type of arcs from the chord and the circle
                List <Semicircle> c1semi  = null;
                MinorArc          c1minor = null;
                MajorArc          c1major = null;

                if (circle1.DefinesDiameter(cas.cs1))
                {
                    c1semi = CreateSemiCircles(circle1, cas.cs1);
                }
                else
                {
                    c1minor = new MinorArc(circle1, cas.cs1.Point1, cas.cs1.Point2);
                    c1major = new MajorArc(circle1, cas.cs1.Point1, cas.cs1.Point2);
                }

                foreach (Circle circle2 in circles2)
                {
                    //The two circles must be the same or congruent
                    if (circle1.radius == circle2.radius)
                    {
                        List <Semicircle> c2semi  = null;
                        MinorArc          c2minor = null;
                        MajorArc          c2major = null;

                        List <GeometricCongruentArcs> congruencies = new List <GeometricCongruentArcs>();
                        if (circle2.DefinesDiameter(cas.cs2))
                        {
                            c2semi = CreateSemiCircles(circle2, cas.cs2);
                            congruencies.AddRange(EquateSemiCircles(c1semi, c2semi));
                        }
                        else
                        {
                            c2minor = new MinorArc(circle2, cas.cs2.Point1, cas.cs2.Point2);
                            c2major = new MajorArc(circle2, cas.cs2.Point1, cas.cs2.Point2);
                            congruencies.Add(new GeometricCongruentArcs(c1minor, c2minor));
                            congruencies.Add(new GeometricCongruentArcs(c1major, c2major));
                        }

                        // For hypergraph
                        List <GroundedClause> antecedent = new List <GroundedClause>();
                        antecedent.Add(cas.cs1);
                        antecedent.Add(cas.cs2);
                        antecedent.Add(cas);

                        foreach (GeometricCongruentArcs gcas in congruencies)
                        {
                            newGrounded.Add(new EdgeAggregator(antecedent, gcas, forwardAnnotation));
                        }
                    }
                }
            }

            return(newGrounded);
        }