diff --git a/AirFighter/AirFighter/CollectionGenericObjects/AbstractCompany.cs b/AirFighter/AirFighter/CollectionGenericObjects/AbstractCompany.cs index 5513f24..82e0b95 100644 --- a/AirFighter/AirFighter/CollectionGenericObjects/AbstractCompany.cs +++ b/AirFighter/AirFighter/CollectionGenericObjects/AbstractCompany.cs @@ -60,7 +60,7 @@ public abstract class AbstractCompany /// public static int operator +(AbstractCompany company, DrawningMilitaryAircraft militaryAircraft) { - return company._collection.Insert(militaryAircraft); + return company._collection.Insert(militaryAircraft, new DrawiningCarEqutables()); } /// @@ -113,6 +113,12 @@ public abstract class AbstractCompany return bitmap; } + /// + /// Сортировка + /// + /// Сравнитель объектов + public void Sort(IComparer comparer) => _collection?.CollectionSort(comparer); + /// /// Вывод заднего фона /// diff --git a/AirFighter/AirFighter/CollectionGenericObjects/CollectionInfo.cs b/AirFighter/AirFighter/CollectionGenericObjects/CollectionInfo.cs new file mode 100644 index 0000000..6ed31db --- /dev/null +++ b/AirFighter/AirFighter/CollectionGenericObjects/CollectionInfo.cs @@ -0,0 +1,76 @@ +namespace ProjectAirFighter.CollectionGenericObjects; + +/// +/// Класс, хранящиий информацию по коллекции +/// +public class CollectionInfo : IEquatable +{ + /// + /// Название + /// + public string Name { get; private set; } + + /// + /// Тип + /// + public CollectionType CollectionType { get; private set; } + + /// + /// Описание + /// + public string Description { get; private set; } + + /// + /// Разделитель для записи информации по объекту в файл + /// + private static readonly string _separator = "-"; + + /// + /// Конструктор + /// + /// Название + /// Тип + /// Описание + public CollectionInfo(string name, CollectionType collectionType, string description) + { + Name = name; + CollectionType = collectionType; + Description = description; + } + + /// + /// Создание объекта из строки + /// + /// Строка + /// Объект или null + public static CollectionInfo? GetCollectionInfo(string data) + { + string[] strs = data.Split(_separator, StringSplitOptions.RemoveEmptyEntries); + if (strs.Length < 1 || strs.Length > 3) + { + return null; + } + + return new CollectionInfo(strs[0], (CollectionType)Enum.Parse(typeof(CollectionType), strs[1]), strs.Length > 2 ? strs[2] : string.Empty); + } + + public override string ToString() + { + return Name + _separator + CollectionType + _separator + Description; + } + + public bool Equals(CollectionInfo? other) + { + return Name == other?.Name; + } + + public override bool Equals(object? obj) + { + return Equals(obj as CollectionInfo); + } + + public override int GetHashCode() + { + return Name.GetHashCode(); + } +} \ No newline at end of file diff --git a/AirFighter/AirFighter/CollectionGenericObjects/ICollectionGenericObjects.cs b/AirFighter/AirFighter/CollectionGenericObjects/ICollectionGenericObjects.cs index 23cee8c..ee43a98 100644 --- a/AirFighter/AirFighter/CollectionGenericObjects/ICollectionGenericObjects.cs +++ b/AirFighter/AirFighter/CollectionGenericObjects/ICollectionGenericObjects.cs @@ -24,7 +24,7 @@ public interface ICollectionGenericObjects /// /// Добавляемый объект /// true - вставка прошла удачно, false - вставка не удалась - int Insert(T obj); + int Insert(T obj, IEqualityComparer? comparer = null); /// /// Добавление объекта в коллекцию на конкретную позицию @@ -32,7 +32,7 @@ public interface ICollectionGenericObjects /// Добавляемый объект /// Позиция /// true - вставка прошла удачно, false - вставка не удалась - int Insert(T obj, int position); + int Insert(T obj, int position, IEqualityComparer? comparer = null); /// /// Удаление объекта из коллекции с конкретной позиции @@ -58,4 +58,10 @@ public interface ICollectionGenericObjects /// /// Поэлементый вывод элементов коллекции IEnumerable GetItems(); + + /// + /// Сортировка коллекции + /// + /// Сравнитель объектов + void CollectionSort(IComparer comparer); } \ No newline at end of file diff --git a/AirFighter/AirFighter/CollectionGenericObjects/ListGenericObjects.cs b/AirFighter/AirFighter/CollectionGenericObjects/ListGenericObjects.cs index 0e8e5fa..51817cb 100644 --- a/AirFighter/AirFighter/CollectionGenericObjects/ListGenericObjects.cs +++ b/AirFighter/AirFighter/CollectionGenericObjects/ListGenericObjects.cs @@ -52,19 +52,21 @@ public class ListGenericObjects : ICollectionGenericObjects return _collection[position]; } - public int Insert(T obj) + public int Insert(T obj, IEqualityComparer? comparer = null) { if (Count == _maxCount) throw new CollectionOverflowException(Count); _collection.Add(obj); return Count; + //TODO } - public int Insert(T obj, int position) + public int Insert(T obj, int position, IEqualityComparer? comparer = null) { if (Count == _maxCount) throw new CollectionOverflowException(Count); if (position >= Count || position < 0) throw new PositionOutOfCollectionException(position); _collection.Insert(position, obj); return position; + //TODO } public T? Remove(int position) @@ -82,4 +84,9 @@ public class ListGenericObjects : ICollectionGenericObjects yield return _collection[i]; } } + + public void CollectionSort(IComparer comparer) + { + _collection.Sort(comparer); + } } \ No newline at end of file diff --git a/AirFighter/AirFighter/CollectionGenericObjects/MassiveGenericObjects.cs b/AirFighter/AirFighter/CollectionGenericObjects/MassiveGenericObjects.cs index 59818b3..ba309ef 100644 --- a/AirFighter/AirFighter/CollectionGenericObjects/MassiveGenericObjects.cs +++ b/AirFighter/AirFighter/CollectionGenericObjects/MassiveGenericObjects.cs @@ -55,8 +55,19 @@ public class MassiveGenericObjects : ICollectionGenericObjects return _collection[position]; } - public int Insert(T obj) + public int Insert(T obj, IEqualityComparer? comparer = null) { + if (comparer != null) + { + for (int i = 0; i < Count; i++) + { + if (comparer.Equals(_collection[i], obj)) + { + throw new CollectionInsertException(obj); + } + } + } + for (int i = 0; i < Count - 3; i++) { if (_collection[i] == null) @@ -68,10 +79,21 @@ public class MassiveGenericObjects : ICollectionGenericObjects throw new CollectionOverflowException(Count); } - public int Insert(T obj, int position) + public int Insert(T obj, int position, IEqualityComparer? comparer = null) { if (position < 0 || position >= Count - 3) throw new PositionOutOfCollectionException(position); + if (comparer != null) + { + for (int i = 0; i < Count; i++) + { + if (comparer.Equals(_collection[i], obj)) + { + throw new CollectionInsertException(obj); + } + } + } + if (_collection[position] != null) { bool pushed = false; @@ -125,4 +147,9 @@ public class MassiveGenericObjects : ICollectionGenericObjects yield return _collection[i]; } } + + public void CollectionSort(IComparer comparer) + { + Array.Sort(_collection, comparer); + } } \ No newline at end of file diff --git a/AirFighter/AirFighter/CollectionGenericObjects/StorageCollection.cs b/AirFighter/AirFighter/CollectionGenericObjects/StorageCollection.cs index 3ed993b..2e13ac2 100644 --- a/AirFighter/AirFighter/CollectionGenericObjects/StorageCollection.cs +++ b/AirFighter/AirFighter/CollectionGenericObjects/StorageCollection.cs @@ -124,8 +124,6 @@ public class StorageCollection sb.Append(value.Key); sb.Append(_separatorForKeyValue); - sb.Append(value.Value.GetCollectionType); - sb.Append(_separatorForKeyValue); sb.Append(value.Value.MaxCount); sb.Append(_separatorForKeyValue); @@ -178,22 +176,19 @@ public class StorageCollection { string[] record = strs.Split(_separatorForKeyValue, StringSplitOptions.RemoveEmptyEntries); - if (record.Length != 4) + if (record.Length != 3) { continue; } - CollectionType collectionType = (CollectionType)Enum.Parse(typeof(CollectionType), record[1]); - ICollectionGenericObjects? collection = StorageCollection.CreateCollection(collectionType); + CollectionInfo? collectionInfo = CollectionInfo.GetCollectionInfo(record[0]) ?? + throw new Exception("Не удалось определить информацию коллекции:" + record[0]); - if (collection == null) - { - throw new InvalidOperationException("Не удалось определить тип коллекции:" + record[1]); - } + ICollectionGenericObjects? collection = StorageCollection.CreateCollection(collectionInfo.CollectionType) ?? + throw new Exception("Не удалось определить тип коллекции:" + record[1]); + collection.MaxCount = Convert.ToInt32(record[1]); - collection.MaxCount = Convert.ToInt32(record[2]); - - string[] set = record[3].Split(_separatorItems, StringSplitOptions.RemoveEmptyEntries); + string[] set = record[2].Split(_separatorItems, StringSplitOptions.RemoveEmptyEntries); foreach (string elem in set) { @@ -212,7 +207,7 @@ public class StorageCollection } } } - _storages.Add(record[0], collection); + _storages.Add(collectionInfo, collection); } } } diff --git a/AirFighter/AirFighter/Drawnings/DrawiningMilitaryAircraftEqutables.cs b/AirFighter/AirFighter/Drawnings/DrawiningMilitaryAircraftEqutables.cs new file mode 100644 index 0000000..7247c0a --- /dev/null +++ b/AirFighter/AirFighter/Drawnings/DrawiningMilitaryAircraftEqutables.cs @@ -0,0 +1,65 @@ +using ProjectAirFighter.Entities; +using System.Diagnostics.CodeAnalysis; + +namespace ProjectAirFighter.Drawnings; + +/// +/// Реализация сравнения двух объектов класса-прорисовки +/// +public class DrawiningCarEqutables : IEqualityComparer +{ + public bool Equals(DrawningMilitaryAircraft? x, DrawningMilitaryAircraft? y) + { + if (x == null || x.EntityMilitaryAircraft == null) + { + return false; + } + + if (y == null || y.EntityMilitaryAircraft == null) + { + return false; + } + + if (x.GetType().Name != y.GetType().Name) + { + return false; + } + + if (x.EntityMilitaryAircraft.Speed != y.EntityMilitaryAircraft.Speed) + { + return false; + } + + if (x.EntityMilitaryAircraft.Weight != y.EntityMilitaryAircraft.Weight) + { + return false; + } + + if (x.EntityMilitaryAircraft.BodyColor != y.EntityMilitaryAircraft.BodyColor) + { + return false; + } + + if (x is DrawningAirFighter && y is DrawningAirFighter) + { + EntityAirFighter xAirFighter = (EntityAirFighter)x.EntityMilitaryAircraft; + EntityAirFighter yAirFighter = (EntityAirFighter)y.EntityMilitaryAircraft; + + if (xAirFighter.AdditionalColor != yAirFighter.AdditionalColor) + return false; + + if (xAirFighter.Wings != yAirFighter.Wings) + return false; + + if (xAirFighter.Rockets != yAirFighter.Rockets) + return false; + } + + return true; + } + + public int GetHashCode([DisallowNull] DrawningMilitaryAircraft obj) + { + return obj.GetHashCode(); + } +} \ No newline at end of file diff --git a/AirFighter/AirFighter/Drawnings/DrawningMilitaryAircraftCarCompareByType.cs b/AirFighter/AirFighter/Drawnings/DrawningMilitaryAircraftCarCompareByType.cs new file mode 100644 index 0000000..0e08809 --- /dev/null +++ b/AirFighter/AirFighter/Drawnings/DrawningMilitaryAircraftCarCompareByType.cs @@ -0,0 +1,33 @@ +namespace ProjectAirFighter.Drawnings; + +/// +/// Сравнение по типу, скорости, весу +/// +public class DrawningMilitaryAircraftCompareByType : IComparer +{ + public int Compare(DrawningMilitaryAircraft? x, DrawningMilitaryAircraft? y) + { + if (x == null || x.EntityMilitaryAircraft == null) + { + return -1; + } + + if (y == null || y.EntityMilitaryAircraft == null) + { + return 1; + } + + if (x.GetType().Name != y.GetType().Name) + { + return x.GetType().Name.CompareTo(y.GetType().Name); + } + + var speedCompare = x.EntityMilitaryAircraft.Speed.CompareTo(y.EntityMilitaryAircraft.Speed); + if (speedCompare != 0) + { + return speedCompare; + } + + return x.EntityMilitaryAircraft.Weight.CompareTo(y.EntityMilitaryAircraft.Weight); + } +} \ No newline at end of file diff --git a/AirFighter/AirFighter/Drawnings/DrawningMilitaryAircraftCompareByColor.cs b/AirFighter/AirFighter/Drawnings/DrawningMilitaryAircraftCompareByColor.cs new file mode 100644 index 0000000..601cf11 --- /dev/null +++ b/AirFighter/AirFighter/Drawnings/DrawningMilitaryAircraftCompareByColor.cs @@ -0,0 +1,27 @@ +namespace ProjectAirFighter.Drawnings; +internal class DrawningMilitaryAircraftCompareByColor : IComparer +{ + public int Compare(DrawningMilitaryAircraft? x, DrawningMilitaryAircraft? y) + { + if (x == null || x.EntityMilitaryAircraft == null) + { + return 1; + } + + if (y == null || y.EntityMilitaryAircraft == null) + { + return -1; + } + var bodycolorCompare = x.EntityMilitaryAircraft.BodyColor.Name.CompareTo(y.EntityMilitaryAircraft.BodyColor.Name); + if (bodycolorCompare != 0) + { + return bodycolorCompare; + } + var speedCompare = x.EntityMilitaryAircraft.Speed.CompareTo(y.EntityMilitaryAircraft.Speed); + if (speedCompare != 0) + { + return speedCompare; + } + return x.EntityMilitaryAircraft.Weight.CompareTo(y.EntityMilitaryAircraft.Weight); + } +} \ No newline at end of file diff --git a/AirFighter/AirFighter/Exceptions/CollectionAlreadyExistsException.cs b/AirFighter/AirFighter/Exceptions/CollectionAlreadyExistsException.cs new file mode 100644 index 0000000..8976d18 --- /dev/null +++ b/AirFighter/AirFighter/Exceptions/CollectionAlreadyExistsException.cs @@ -0,0 +1,14 @@ +using System.Runtime.Serialization; + +namespace AirFighter; + +public class CollectionAlreadyExistsException : Exception +{ + public CollectionAlreadyExistsException() : base() { } + public CollectionAlreadyExistsException(CollectionInfo collectionInfo) : base($"Коллекция {collectionInfo} уже существует!") { } + public CollectionAlreadyExistsException(string name, Exception exception) : + base($"Коллекция {name} уже существует!", exception) + { } + protected CollectionAlreadyExistsException(SerializationInfo info, StreamingContext + contex) : base(info, contex) { } +} \ No newline at end of file diff --git a/AirFighter/AirFighter/Exceptions/CollectionInfoException.cs b/AirFighter/AirFighter/Exceptions/CollectionInfoException.cs new file mode 100644 index 0000000..f50a7af --- /dev/null +++ b/AirFighter/AirFighter/Exceptions/CollectionInfoException.cs @@ -0,0 +1,12 @@ +namespace AirFighter; + +public class CollectionInfoException : Exception +{ + public CollectionInfoException() : base() { } + public CollectionInfoException(string message) : base(message) { } + public CollectionInfoException(string message, Exception exception) : + base(message, exception) + { } + protected CollectionInfoException(SerializationInfo info, StreamingContext + contex) : base(info, contex) { } +} \ No newline at end of file diff --git a/AirFighter/AirFighter/Exceptions/CollectionInsertException.cs b/AirFighter/AirFighter/Exceptions/CollectionInsertException.cs new file mode 100644 index 0000000..4214c43 --- /dev/null +++ b/AirFighter/AirFighter/Exceptions/CollectionInsertException.cs @@ -0,0 +1,13 @@ +namespace AirFighter; + +public class CollectionInsertException : Exception +{ + public CollectionInsertException(object obj) : base($"Объект {obj} не удволетворяет уникальности") { } + public CollectionInsertException() : base() { } + public CollectionInsertException(string message) : base(message) { } + public CollectionInsertException(string message, Exception exception) : + base(message, exception) + { } + protected CollectionInsertException(SerializationInfo info, StreamingContext + contex) : base(info, contex) { } +} \ No newline at end of file diff --git a/AirFighter/AirFighter/Exceptions/CollectionTypeException.cs b/AirFighter/AirFighter/Exceptions/CollectionTypeException.cs new file mode 100644 index 0000000..d6deb8c --- /dev/null +++ b/AirFighter/AirFighter/Exceptions/CollectionTypeException.cs @@ -0,0 +1,14 @@ +using System.Runtime.Serialization; + +namespace AirFighter; + +public class CollectionTypeException : Exception +{ + public CollectionTypeException() : base() { } + public CollectionTypeException(string message) : base(message) { } + public CollectionTypeException(string message, Exception exception) : + base(message, exception) + { } + protected CollectionTypeException(SerializationInfo info, StreamingContext + contex) : base(info, contex) { } +} \ No newline at end of file diff --git a/AirFighter/AirFighter/FormMilitaryAircraftCollection.Designer.cs b/AirFighter/AirFighter/FormMilitaryAircraftCollection.Designer.cs index 0dbd8f4..a7cd0cd 100644 --- a/AirFighter/AirFighter/FormMilitaryAircraftCollection.Designer.cs +++ b/AirFighter/AirFighter/FormMilitaryAircraftCollection.Designer.cs @@ -52,6 +52,8 @@ loadToolStripMenuItem = new ToolStripMenuItem(); saveFileDialog = new SaveFileDialog(); openFileDialog = new OpenFileDialog(); + buttonSortByColor = new Button(); + buttonSortByType = new Button(); groupBoxTools.SuspendLayout(); panelCompanyTools.SuspendLayout(); panelStorage.SuspendLayout(); @@ -68,13 +70,15 @@ groupBoxTools.Dock = DockStyle.Right; groupBoxTools.Location = new Point(744, 28); groupBoxTools.Name = "groupBoxTools"; - groupBoxTools.Size = new Size(206, 639); + groupBoxTools.Size = new Size(206, 711); groupBoxTools.TabIndex = 0; groupBoxTools.TabStop = false; groupBoxTools.Text = "Инструменты"; // // panelCompanyTools // + panelCompanyTools.Controls.Add(buttonSortByColor); + panelCompanyTools.Controls.Add(buttonSortByType); panelCompanyTools.Controls.Add(buttonRefresh); panelCompanyTools.Controls.Add(buttonGoToCheck); panelCompanyTools.Controls.Add(buttonRemoveMilitaryAircraft); @@ -82,15 +86,15 @@ panelCompanyTools.Controls.Add(maskedTextBoxPosition); panelCompanyTools.Dock = DockStyle.Bottom; panelCompanyTools.Enabled = false; - panelCompanyTools.Location = new Point(3, 357); + panelCompanyTools.Location = new Point(3, 360); panelCompanyTools.Name = "panelCompanyTools"; - panelCompanyTools.Size = new Size(200, 279); + panelCompanyTools.Size = new Size(200, 348); panelCompanyTools.TabIndex = 10; // // buttonRefresh // buttonRefresh.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right; - buttonRefresh.Location = new Point(0, 227); + buttonRefresh.Location = new Point(8, 195); buttonRefresh.Name = "buttonRefresh"; buttonRefresh.Size = new Size(192, 49); buttonRefresh.TabIndex = 7; @@ -101,7 +105,7 @@ // buttonGoToCheck // buttonGoToCheck.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right; - buttonGoToCheck.Location = new Point(3, 178); + buttonGoToCheck.Location = new Point(3, 140); buttonGoToCheck.Name = "buttonGoToCheck"; buttonGoToCheck.Size = new Size(192, 49); buttonGoToCheck.TabIndex = 6; @@ -112,7 +116,7 @@ // buttonRemoveMilitaryAircraft // buttonRemoveMilitaryAircraft.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right; - buttonRemoveMilitaryAircraft.Location = new Point(4, 129); + buttonRemoveMilitaryAircraft.Location = new Point(4, 91); buttonRemoveMilitaryAircraft.Name = "buttonRemoveMilitaryAircraft"; buttonRemoveMilitaryAircraft.Size = new Size(191, 49); buttonRemoveMilitaryAircraft.TabIndex = 5; @@ -134,7 +138,7 @@ // maskedTextBoxPosition // maskedTextBoxPosition.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right; - maskedTextBoxPosition.Location = new Point(3, 96); + maskedTextBoxPosition.Location = new Point(3, 58); maskedTextBoxPosition.Mask = "00"; maskedTextBoxPosition.Name = "maskedTextBoxPosition"; maskedTextBoxPosition.Size = new Size(192, 27); @@ -250,7 +254,7 @@ pictureBox.Dock = DockStyle.Fill; pictureBox.Location = new Point(0, 28); pictureBox.Name = "pictureBox"; - pictureBox.Size = new Size(744, 639); + pictureBox.Size = new Size(744, 711); pictureBox.TabIndex = 3; pictureBox.TabStop = false; // @@ -295,11 +299,33 @@ // openFileDialog.Filter = "txt file | *.txt"; // + // buttonSortByColor + // + buttonSortByColor.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right; + buttonSortByColor.Location = new Point(4, 296); + buttonSortByColor.Name = "buttonSortByColor"; + buttonSortByColor.Size = new Size(192, 49); + buttonSortByColor.TabIndex = 9; + buttonSortByColor.Text = "Сортировка по цвету"; + buttonSortByColor.UseVisualStyleBackColor = true; + buttonSortByColor.Click += buttonSortByColor_Click; + // + // buttonSortByType + // + buttonSortByType.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right; + buttonSortByType.Location = new Point(6, 250); + buttonSortByType.Name = "buttonSortByType"; + buttonSortByType.Size = new Size(191, 49); + buttonSortByType.TabIndex = 8; + buttonSortByType.Text = "Сортировка по типу"; + buttonSortByType.UseVisualStyleBackColor = true; + buttonSortByType.Click += buttonSortByType_Click; + // // FormMilitaryAircraftCollection // AutoScaleDimensions = new SizeF(8F, 20F); AutoScaleMode = AutoScaleMode.Font; - ClientSize = new Size(950, 667); + ClientSize = new Size(950, 739); Controls.Add(pictureBox); Controls.Add(groupBoxTools); Controls.Add(menuStrip); @@ -344,5 +370,7 @@ private ToolStripMenuItem loadToolStripMenuItem; private SaveFileDialog saveFileDialog; private OpenFileDialog openFileDialog; + private Button buttonSortByColor; + private Button buttonSortByType; } } \ No newline at end of file diff --git a/AirFighter/AirFighter/FormMilitaryAircraftCollection.cs b/AirFighter/AirFighter/FormMilitaryAircraftCollection.cs index 094a01d..6266a23 100644 --- a/AirFighter/AirFighter/FormMilitaryAircraftCollection.cs +++ b/AirFighter/AirFighter/FormMilitaryAircraftCollection.cs @@ -290,4 +290,39 @@ public partial class FormMilitaryAircraftCollection : Form } } } + + /// + /// Сортировка по типу + /// + /// + /// + private void buttonSortByType_Click(object sender, EventArgs e) + { + CompareCars(new DrawningMilitaryAircraftCompareByType()); + } + + /// + /// Сортировка по цвету + /// + /// + /// + private void buttonSortByColor_Click(object sender, EventArgs e) + { + CompareCars(new DrawningMilitaryAircraftCompareByType()); + } + + /// + /// Сортировка по сравнителю + /// + /// Сравнитель объектов + private void CompareCars(IComparer comparer) + { + if (_company == null) + { + return; + } + + _company.Sort(comparer); + pictureBox.Image = _company.Show(); + } } diff --git a/AirFighter/AirFighter/FormMilitaryAircraftCollection.resx b/AirFighter/AirFighter/FormMilitaryAircraftCollection.resx index ee1748a..75a2cc2 100644 --- a/AirFighter/AirFighter/FormMilitaryAircraftCollection.resx +++ b/AirFighter/AirFighter/FormMilitaryAircraftCollection.resx @@ -126,4 +126,7 @@ 310, 17 + + 71 + \ No newline at end of file