Exemple #1
0
 private static void WriteAnswers(StreamWriter sw, DialogNode dialogNode, HashSet <string> mappingURISet)
 {
     if (dialogNode.Type == DialogNodeType.FatHeadAnswers)
     {
         var fatHeadAnswers = (FatHeadAnswers)dialogNode;
         if (fatHeadAnswers.MappingUris != null)
         {
             foreach (var mappingUri in fatHeadAnswers.MappingUris)
             {
                 if (!mappingURISet.Contains(mappingUri) && mappingUri != "")
                 {
                     mappingURISet.Add(mappingUri);
                     sw.WriteLine(dialogNode.LineNumber + ";" + mappingUri);
                 }
             }
         }
     }
     else if (dialogNode.ChildrenNodes != null)
     {
         foreach (var childNode in dialogNode.ChildrenNodes)
         {
             WriteAnswers(sw, childNode, mappingURISet);
         }
     }
 }
Exemple #2
0
 public SwitchOnEntityVariables(DialogNode parentNode, EntityMatch entityMatch)
 {
     Type          = DialogNodeType.SwitchOnEntityVariables;
     ParentNode    = parentNode;
     ChildrenNodes = new List <DialogNode>();
     EntityMatch   = entityMatch;
 }
 public void AddDialogNodeReference(DialogNode dialogNode)
 {
     if (ReferencedByDialogNodes == null)
     {
         ReferencedByDialogNodes = new List <DialogNode>();
     }
     ReferencedByDialogNodes.Add(dialogNode);
 }
Exemple #4
0
 public DialogVariableConditions(DialogNode parentNode, IList <DialogVariableCondition> variableConditions, ConditionOperator @operator)
 {
     Type               = DialogNodeType.DialogVariableConditions;
     ParentNode         = parentNode;
     ChildrenNodes      = new List <DialogNode>();
     VariableConditions = variableConditions;
     Operator           = @operator;
 }
Exemple #5
0
 internal void AddDialogNodeReference(DialogNode dialogNode)
 {
     if (DialogNodeReferences == null)
     {
         DialogNodeReferences = new List <DialogNode>();
     }
     DialogNodeReferences.Add(dialogNode);
 }
Exemple #6
0
        public DisambiguationQuestion(DialogNode parentNode, string questionExpression, string questionText)
        {
            Type          = DialogNodeType.DisambiguationQuestion;
            ParentNode    = parentNode;
            ChildrenNodes = new List <DialogNode>();

            QuestionExpression = questionExpression;
            QuestionText       = questionText;
        }
Exemple #7
0
 public GotoNode(DialogNode parentNode, string targetNodeId, string messageExpression, string messageText, IMessageCollector errors)
 {
     Type              = DialogNodeType.GotoNode;
     ParentNode        = parentNode;
     ChildrenNodes     = null;
     TargetNodeId      = targetNodeId;
     MessageExpression = messageExpression;
     MessageText       = messageText;
 }
Exemple #8
0
 private static void WriteChildrenNodes(XmlWriter xw, DialogNode dialogNode)
 {
     if (dialogNode.ChildrenNodes != null && dialogNode.ChildrenNodes.Count > 0)
     {
         foreach (var childNode in dialogNode.ChildrenNodes)
         {
             WriteDialogNode(xw, childNode);
         }
     }
 }
 internal void LinkVariableAssignmentToVariable(DialogNode dialogNode, DialogVariableAssignment variableAssignment)
 {
     variableAssignment.Variable = Variables[variableAssignment.VariableName];
     variableAssignment.Variable.AddDialogNodeReference(dialogNode, VariableReferenceType.Write);
     if (variableAssignment.Operator == DialogVariableOperator.CopyValueFromVariable)
     {
         var refVariable = Variables[variableAssignment.Value];
         refVariable.AddDialogNodeReference(dialogNode, VariableReferenceType.Read);
     }
 }
 private static void ComputeNodesStatistics(DialogNode parentNode, IDictionary <DialogNodeType, int> nodeTypeCounts)
 {
     nodeTypeCounts[parentNode.Type]++;
     if (parentNode.ChildrenNodes != null)
     {
         foreach (var childNode in parentNode.ChildrenNodes)
         {
             ComputeNodesStatistics(childNode, nodeTypeCounts);
         }
     }
 }
 internal void AddDialogNodeReference(DialogNode dialogNode, VariableReferenceType referenceType)
 {
     if (DialogNodeReferences == null)
     {
         DialogNodeReferences = new List <DialogNodeVariableReference>();
     }
     DialogNodeReferences.Add(new DialogNodeVariableReference()
     {
         Node = dialogNode, ReferenceType = referenceType
     });
 }
 internal void RegisterDialogNode(DialogNode dialogNode)
 {
     if (DialogNodesWithId.ContainsKey(dialogNode.Id))
     {
         var otherNode = DialogNodesWithId[dialogNode.Id];
         LogMessage(dialogNode.LineNumber, MessageType.DuplicateKey, "Two distinct dialog nodes share the same id \"" + dialogNode.Id + "\" : line " + otherNode.LineNumber + " and line " + dialogNode.LineNumber);
     }
     else
     {
         DialogNodesWithId.Add(dialogNode.Id, dialogNode);
     }
 }
 private static void WriteAnswers(StreamWriter sw, DialogNode dialogNode, HashSet <string> mappingURISet, ref int countUriMissing, List <String[]> resultList = null)
 {
     if (dialogNode.Type == DialogNodeType.FatHeadAnswers)
     {
         var fatHeadAnswers = (FatHeadAnswers)dialogNode;
         if (fatHeadAnswers.MappingUris != null)
         {
             foreach (var mappingUri in fatHeadAnswers.MappingUris)
             {
                 if (!mappingURISet.Contains(mappingUri) && mappingUri != "")
                 {
                     mappingURISet.Add(mappingUri);
                     string formatQuestions = "";
                     // If we want to get the question and the URI
                     if (resultList != null)
                     {
                         // A list to collect all the question for the current URI
                         List <string> questionsList = new List <String>();
                         foreach (string [] q in resultList)
                         {
                             string uri = q[1];
                             // If we find the same URI
                             if (q[0].Equals(mappingUri))
                             {
                                 // We add the question to the question list
                                 questionsList.Add(q[1]);
                             }
                         }
                         // Formatting for the CSV file
                         foreach (string q in questionsList)
                         {
                             formatQuestions += q + ";";
                         }
                         // If no question has been found, we increment the number of alone URI
                         if (questionsList.Count == 0)
                         {
                             countUriMissing++;
                         }
                     }
                     sw.WriteLine(dialogNode.LineNumber + ";" + mappingUri + ";" + formatQuestions);
                 }
             }
         }
     }
     else if (dialogNode.ChildrenNodes != null)
     {
         foreach (var childNode in dialogNode.ChildrenNodes)
         {
             WriteAnswers(sw, childNode, mappingURISet, ref countUriMissing, resultList);
         }
     }
 }
        internal void LinkDialogVariableConditionToDialogVariableAndEntityValue(DialogNode dialogNode, DialogVariableCondition variableCondition, DialogVariablesSimulator dialogVariables)
        {
            if (String.IsNullOrEmpty(variableCondition.VariableName))
            {
                return;
            }

            DialogVariable variable    = null;
            EntityValue    entityValue = null;

            if (!Variables.ContainsKey(variableCondition.VariableName))
            {
                LogMessage(dialogNode.LineNumber, MessageType.InvalidReference, "Variable condition references undefined variable name : \"" + variableCondition.VariableName + "\"");
            }
            else
            {
                variable = Variables[variableCondition.VariableName];
                variable.AddDialogNodeReference(dialogNode, VariableReferenceType.Read);

                if (variableCondition.Comparison != ConditionComparison.HasValue && !String.IsNullOrEmpty(variableCondition.Value))
                {
                    var entity = dialogVariables.TryGetEntityFromVariable(variable);
                    if (entity != null)
                    {
                        entityValue = entity.TryGetEntityValueFromName(variableCondition.Value);
                        if (entityValue == null)
                        {
                            var message = "Variable condition references undefined entity value name \"" + variableCondition.Value + "\" for entity " + entity.Name;
                            var suggestedEntityValue = entity.SuggestEntityValueFromName(variableCondition.Value, Entities.Values);
                            if (suggestedEntityValue != null)
                            {
                                if (suggestedEntityValue.Entity == entity)
                                {
                                    message += ", you may want to replace this name with \"" + suggestedEntityValue.Name + "\" (defined line " + suggestedEntityValue.LineNumber + ")";
                                }
                                else
                                {
                                    message += ", you may want to move this condition under another entity \"" + suggestedEntityValue.Entity.Name + "\" with value \"" + suggestedEntityValue.Name + "\" (defined line " + suggestedEntityValue.LineNumber + ")";
                                }
                            }
                            LogMessage(dialogNode.LineNumber, MessageType.InvalidReference, message);
                        }
                        else
                        {
                            entityValue.AddDialogNodeReference(dialogNode);
                        }
                    }
                }
            }
            variableCondition.SetVariableAndEntityValue(variable, entityValue);
        }
        private ViewNode ReadNode(DialogNode node, ViewNode condition)
        {
            if (node.ChildrenNodes != null)
            {
                foreach (var child in node.ChildrenNodes)
                {
                    if (child.Type == DialogNodeType.DialogVariableConditions || child.Type == DialogNodeType.DisambiguationQuestion || child.Type == DialogNodeType.SwitchOnEntityVariables || child.Type == DialogNodeType.FatHeadAnswers)
                    {
                        condition.AddChild(ReadNode(child, new ViewNode(child, answerStore)));
                    }
                }
            }

            return(condition);
        }
        internal void LinkEntityMatchToEntityAndDialogVariables(DialogNode dialogNode, EntityMatch entityMatch)
        {
            entityMatch.Entity = Entities[entityMatch.EntityName];
            entityMatch.Entity.AddDialogNodeReference(dialogNode);

            if (entityMatch.EntityVariableName1 != null)
            {
                entityMatch.EntityVariable1 = Variables[entityMatch.EntityVariableName1];
                entityMatch.EntityVariable1.AddDialogNodeReference(dialogNode, VariableReferenceType.Write);
            }
            if (entityMatch.EntityVariableName2 != null)
            {
                entityMatch.EntityVariable2 = Variables[entityMatch.EntityVariableName2];
                entityMatch.EntityVariable2.AddDialogNodeReference(dialogNode, VariableReferenceType.Write);
            }
        }
        //Constructor
        public ViewNode(DialogNode node, AnswerStoreSimulator answerStore)
        {
            Children      = new List <ViewNode>();
            DisplayValues = new List <DisplayValue>();
            Type          = node.Type;
            List <DialogVariableAssignment> assignList = new List <DialogVariableAssignment>();

            if (node.Type == DialogNodeType.DialogVariableConditions)
            {
                foreach (var condition in ((DialogVariableConditions)node).VariableConditions)
                {
                    //If we force the value of another entity
                    //Extract the assignements
                    if (node.ChildrenNodes != null && node.ChildrenNodes.Count > 0)
                    {
                        foreach (var child in node.ChildrenNodes)
                        {
                            if (child.VariableAssignments != null && child.VariableAssignments.Count > 0)
                            {
                                foreach (var assign in child.VariableAssignments)
                                {
                                    assignList.Add(assign);
                                }
                            }
                        }
                    }


                    if (assignList.Count > 0)
                    {
                        DisplayValues.Add(new DisplayValue(condition, assignList));
                    }
                    else
                    {
                        DisplayValues.Add(new DisplayValue(condition));
                    }
                }
            }
            else
            {
                DisplayValues.Add(new DisplayValue(node, answerStore));
            }
        }
Exemple #18
0
        private static void WriteDialogNode(XmlWriter xw, DialogNode childNode)
        {
            switch (childNode.Type)
            {
            case DialogNodeType.DialogVariableConditions:
                WriteDialogVariableConditions(xw, (DialogVariableConditions)childNode);
                break;

            case DialogNodeType.SwitchOnEntityVariables:
                WriteSwitchOnEntityVariables(xw, (SwitchOnEntityVariables)childNode);
                break;

            case DialogNodeType.SwitchLoopOnce:
                WriteSwitchLoopOnce(xw, (SwitchLoopOnce)childNode);
                break;

            case DialogNodeType.DirectAnswer:
                WriteDirectAnswer(xw, (DirectAnswer)childNode);
                break;

            case DialogNodeType.DisambiguationQuestion:
                WriteDisambiguationQuestion(xw, (DisambiguationQuestion)childNode);
                break;

            case DialogNodeType.FatHeadAnswers:
                WriteFatHeadAnswers(xw, (FatHeadAnswers)childNode);
                break;

            case DialogNodeType.GotoNext:
                WriteGotoNext(xw, (GotoNode)childNode);
                break;

            case DialogNodeType.GotoNode:
                WriteGotoNode(xw, (GotoNode)childNode);
                break;

            case DialogNodeType.RedirectToLongTail:
                WriteRedirectToLongTail(xw, (GotoNode)childNode);
                break;
            }
        }
 private static List <string> GetUris(DialogNode node, List <string> mappingURISet)
 {
     if (node.Type == DialogNodeType.FatHeadAnswers)
     {
         var fatHeadAnswers = (FatHeadAnswers)node;
         foreach (var mappingUri in fatHeadAnswers.MappingUris)
         {
             if (!mappingURISet.Contains(mappingUri) && mappingUri != "")
             {
                 mappingURISet.Add(mappingUri);
             }
         }
     }
     else if (node.ChildrenNodes != null)
     {
         foreach (var childNode in node.ChildrenNodes)
         {
             GetUris(childNode, mappingURISet);
         }
     }
     return(mappingURISet);
 }
 private void ResolveGotoReferences(DialogNode dialogNode)
 {
     if (previousNode != null && previousNode.Type == DialogNodeType.GotoNode)
     {
         var previousGotoNode = (GotoNode)previousNode;
         if (previousGotoNode.TargetNode == dialogNode)
         {
             previousGotoNode.Type = DialogNodeType.GotoNext;
         }
     }
     if (dialogNode.Type == DialogNodeType.GotoNode)
     {
         var gotoNode = (GotoNode)dialogNode;
         if (!String.IsNullOrEmpty(gotoNode.TargetNodeId))
         {
             if (DialogNodesWithId.ContainsKey(gotoNode.TargetNodeId))
             {
                 var targetNode = DialogNodesWithId[gotoNode.TargetNodeId];
                 gotoNode.TargetNode = targetNode;
                 targetNode.AddDialogNodeReference(gotoNode);
             }
             else
             {
                 LogMessage(gotoNode.LineNumber, MessageType.InvalidReference, "Goto node with invalid node reference : \"" + gotoNode.TargetNodeId + "\" (target node may be offline) => dead end");
             }
         }
     }
     previousNode = dialogNode;
     if (dialogNode.ChildrenNodes != null)
     {
         foreach (var childNode in dialogNode.ChildrenNodes)
         {
             ResolveGotoReferences(childNode);
         }
     }
 }
Exemple #21
0
 private static void WriteDialogNodeProperties(XmlWriter xw, DialogNode dialogNode)
 {
     if (dialogNode.DialogNodeReferences != null && dialogNode.DialogNodeReferences.Where(r => r.Type != DialogNodeType.GotoNext).Count() > 0)
     {
         xw.WriteAttributeString("Id", dialogNode.Id);
     }
     if (dialogNode.VariableAssignments != null && dialogNode.VariableAssignments.Count > 0)
     {
         foreach (var variableAssignment in dialogNode.VariableAssignments)
         {
             xw.WriteStartElement("Set");
             xw.WriteAttributeString("Variable", variableAssignment.VariableName);
             if (variableAssignment.Operator == DialogVariableOperator.CopyValueFromVariable)
             {
                 xw.WriteAttributeString("FromVariable", variableAssignment.Value);
             }
             else
             {
                 xw.WriteAttributeString("ToValue", variableAssignment.Value);
             }
             xw.WriteEndElement();
         }
     }
 }
Exemple #22
0
 private static void ExecuteVariableAssignments(DialogNode dialogNode, IDictionary <string, string> variablesValues)
 {
     if (dialogNode.VariableAssignments != null)
     {
         foreach (var assignment in dialogNode.VariableAssignments)
         {
             if (assignment.Operator == DialogVariableOperator.CopyValueFromVariable)
             {
                 if (variablesValues.ContainsKey(assignment.Value))
                 {
                     variablesValues[assignment.VariableName] = variablesValues[assignment.Value];
                 }
                 else
                 {
                     variablesValues.Remove(assignment.VariableName);
                 }
             }
             else
             {
                 variablesValues[assignment.VariableName] = assignment.Value;
             }
         }
     }
 }
Exemple #23
0
 public FatHeadAnswerNodeExecution(DialogNode dialogNode, string mappingUri) : base(dialogNode)
 {
     MappingURI = mappingUri;
 }
Exemple #24
0
 public MatchEntitiesNodeExecution(DialogNode dialogNode, EntityValuesMatchResult entityValuesMatchResult) : base(dialogNode)
 {
     EntityValuesMatchResult = entityValuesMatchResult;
 }
Exemple #25
0
 public DialogNodeExecution(DialogNode dialogNode)
 {
     DialogNode = dialogNode;
 }
Exemple #26
0
        public static DialogExecutionResult ExecuteUserInputNode(Dialog dialog, DialogNode dialogNode, string userInputText, DialogExecutionResult result)
        {
            IEnumerable <EntityMatch> entityMatches = null;

            if (dialogNode is MatchIntentAndEntities)
            {
                var intent = (MatchIntentAndEntities)dialogNode;
                entityMatches = intent.EntityMatches;
            }
            else if (dialogNode is DisambiguationQuestion)
            {
                var question = (DisambiguationQuestion)dialogNode;
                if (question.EntityMatch != null)
                {
                    entityMatches = new EntityMatch[] { question.EntityMatch };
                }
                result.AddUserInput(userInputText);
            }
            else
            {
                throw new ArgumentException("Dialog node must be of type MatchIntentAndEntities or DisambiguationQuestion");
            }

            EntityValuesMatchResult matchResult = null;

            if (entityMatches != null)
            {
                // Try to match entity values in the questions
                var entities = entityMatches.Select(entityMatch => entityMatch.Entity);
                matchResult = EntityValuesMatcher.MatchEntityValues(entities, userInputText, dialog.ConceptsSynonyms, dialog.ConceptsRegex);

                // Store the matched entity values in their assigned variables
                foreach (var entityValuesGroup in matchResult.EntityValues.GroupBy(ev => ev.Entity))
                {
                    var entityMatch = entityMatches.Where(em => em.Entity == entityValuesGroup.Key).First();
                    int matchIndex  = 0;
                    foreach (var entityValue in entityValuesGroup)
                    {
                        if (matchIndex == 0 && entityMatch.EntityVariableName1 != null)
                        {
                            result.VariablesValues[entityMatch.EntityVariableName1] = entityValue.Name;
                        }
                        else if (matchIndex == 1 && entityMatch.EntityVariableName2 != null)
                        {
                            result.VariablesValues[entityMatch.EntityVariableName2] = entityValue.Name;
                        }
                        matchIndex++;
                    }
                }
                ExecuteVariableAssignments(dialogNode, result.VariablesValues);
            }

            // Store the result of this execution
            var nodeExecution = new MatchEntitiesNodeExecution(dialogNode, matchResult);

            if (result.ExecutionResult != null && result.ExecutionResult.DialogNode == dialogNode)
            {
                result.DialogNodesExecutionPath.Remove(result.ExecutionResult);
            }
            result.AddDialogNodeExecution(nodeExecution);

            // Traverse the children nodes
            SelectChildNode(dialog, result.VariablesValues, dialogNode, null, result);

            return(result);
        }
Exemple #27
0
        private static void ExecutePromptNode(Dialog dialog, IDictionary <string, string> variablesValues, DialogNode promptNode, DialogExecutionResult result)
        {
            // Store the result of this execution
            var nodeExecution = new DialogNodeExecution(promptNode);

            result.AddDialogNodeExecution(nodeExecution);

            // Stop the traversal at the first prompt to the user
            // (what comes later is unpredictable)
        }
Exemple #28
0
        private static void ExecuteConditionalNode(Dialog dialog, IDictionary <string, string> variablesValues, DialogNode conditionalNode, DialogExecutionResult result)
        {
            // Store the result of this execution
            var nodeExecution = new DialogNodeExecution(conditionalNode);

            result.AddDialogNodeExecution(nodeExecution);

            // Adjust variables values
            ExecuteVariableAssignments(conditionalNode, variablesValues);

            // Traverse the children nodes
            SelectChildNode(dialog, variablesValues, conditionalNode, null, result);
        }
Exemple #29
0
        private static void SelectChildNode(Dialog dialog, IDictionary <string, string> variablesValues, DialogNode parentNode, DialogNode firstChildNode, DialogExecutionResult result)
        {
            bool firstChildNodeFound = firstChildNode == null;

            foreach (var childNode in parentNode.ChildrenNodes)
            {
                if (!firstChildNodeFound)
                {
                    firstChildNodeFound = childNode == firstChildNode;
                    if (!firstChildNodeFound)
                    {
                        continue;
                    }
                }

                switch (childNode.Type)
                {
                case DialogNodeType.SwitchOnEntityVariables:
                    var    switchNode = (SwitchOnEntityVariables)childNode;
                    string varValue   = null;
                    if (variablesValues.TryGetValue(switchNode.EntityMatch.EntityVariableName1, out varValue))
                    {
                        if (!String.IsNullOrEmpty(varValue))
                        {
                            ExecuteConditionalNode(dialog, variablesValues, childNode, result);
                            if (result.ExecutionResult.DialogNode.Type != DialogNodeType.SwitchOnEntityVariables)
                            {
                                return;
                            }
                        }
                    }
                    break;

                case DialogNodeType.SwitchLoopOnce:
                    var switchLoopNode = (SwitchLoopOnce)childNode;
                    switchNode = (SwitchOnEntityVariables)switchLoopNode.ParentNode;
                    // loop only if variable2 not null
                    string var2Value = null;
                    if (variablesValues.TryGetValue(switchNode.EntityMatch.EntityVariableName2, out var2Value))
                    {
                        if (!String.IsNullOrEmpty(var2Value))
                        {
                            // Set variable1 to variable 2 and reset variable 2
                            variablesValues[switchNode.EntityMatch.EntityVariableName1] = var2Value;
                            variablesValues[switchNode.EntityMatch.EntityVariableName2] = null;

                            // Store the result of this execution
                            var nodeExecution = new DialogNodeExecution(switchLoopNode);
                            result.AddDialogNodeExecution(nodeExecution);

                            // Adjust variables values
                            ExecuteVariableAssignments(switchLoopNode, variablesValues);

                            // Loop back to switch node
                            SelectChildNode(dialog, variablesValues, switchNode.ParentNode, switchNode, result);
                            return;
                        }
                    }
                    break;

                case DialogNodeType.DialogVariableConditions:
                    var  conditionsNode = (DialogVariableConditions)childNode;
                    bool conditionsTest = conditionsNode.Operator == ConditionOperator.And ? true : false;
                    foreach (var condition in conditionsNode.VariableConditions)
                    {
                        varValue = null;
                        variablesValues.TryGetValue(condition.VariableName, out varValue);
                        bool conditionTest = false;
                        if (condition.Comparison == ConditionComparison.HasValue)
                        {
                            if (!String.IsNullOrEmpty(varValue))
                            {
                                conditionTest = true;
                            }
                        }
                        else if (condition.Comparison == ConditionComparison.Equals)
                        {
                            if ((String.IsNullOrEmpty(condition.Value) && String.IsNullOrEmpty(varValue)) || (condition.Value != null && condition.Value == varValue))
                            {
                                conditionTest = true;
                            }
                        }
                        if (conditionsNode.Operator == ConditionOperator.And)
                        {
                            conditionsTest &= conditionTest;
                        }
                        else
                        {
                            conditionsTest |= conditionTest;
                        }
                    }
                    if (conditionsTest)
                    {
                        ExecuteConditionalNode(dialog, variablesValues, childNode, result);
                        return;
                    }
                    break;

                case DialogNodeType.DirectAnswer:
                case DialogNodeType.DisambiguationQuestion:
                case DialogNodeType.RedirectToLongTail:
                    ExecutePromptNode(dialog, variablesValues, childNode, result);
                    return;

                case DialogNodeType.FatHeadAnswers:
                    ExecuteFatHeadNode(dialog, variablesValues, (FatHeadAnswers)childNode, result);
                    return;

                case DialogNodeType.GotoNext:
                case DialogNodeType.GotoNode:
                    NavigateToNextNode(dialog, variablesValues, (GotoNode)childNode, result);
                    return;
                }
            }
        }
Exemple #30
0
 public RedirectToLongTail(DialogNode parentNode, string targetNodeId, string messageExpression, string messageText, IMessageCollector errors) :
     base(parentNode, targetNodeId, messageExpression, messageText, errors)
 {
     Type = DialogNodeType.RedirectToLongTail;
 }