namespace AtCoder
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Text;
    using static System.Console;
    using static System.Math;
    using static Util;

    class AtCoder
    {
        /*------------------------------------------------------------------*/

        void Solve()
        {
            int N;
            string[] s;
            io.i(out N);
            io.ini(out s, N);

            var cnts = new Counts<string>();
            var ans = 0L;
            foreach (var _s in s.Select(x => x.Sort()))
            {
                if (cnts.ContainsKey(_s))
                {
                    ans += cnts[_s];
                }
                cnts.Add(_s);
            }

            WriteLine(ans);
            
        }

        /*------------------------------------------------------------------*/
        void Test()
        {
            //--- TEST ---//
        }

        public static void Main(string[] _) => new AtCoder().Stream();
        IO io = new IO();

        private void Stream()
        {
            /*
            var exStdIn = new System.IO.StreamReader("stdin.txt");
            SetIn(exStdIn);
            Test();
            */
            Solve();
            io.Flush();
        }
    }

    class IO
    {
        #region INPUT

        #region FastRead

        private const int Size = 1 << 12;
        private readonly Stream _stream = OpenStandardInput();
        private readonly byte[] _buffer = new byte[Size];
        private int _ptr = 0, _buffLen = 0;

        private bool HasNextByte()
        {
            if (_ptr < _buffLen) return true;
            _ptr = 0;
            _buffLen = _stream.Read(_buffer, 0, Size);
            return _buffLen > 0;
        }

        private byte ReadByte()
        {
            if (HasNextByte()) return _buffer[_ptr++];
            else
                //inputの末尾の改行がないとerror
                throw new EndOfStreamException();
        }

        private bool IsAscii(int c) => '!' <= c && c <= '~';

        private char NextChar()
        {
            var b = ReadByte();
            while (!IsAscii(b)) b = ReadByte();
            return (char) b;
        }

        private string Next()
        {
            var sb = new StringBuilder();
            for (var b = NextChar(); IsAscii(b); b = (char) ReadByte()) sb.Append(b);
            return sb.ToString();
        }

        public string String => Next();
        public char Char => NextChar();
        public int Int => int.Parse(Next());
        public long Long => long.Parse(Next());
        public double Double => double.Parse(Next());

        #endregion

        #region LazyRead

        private void scan<T>(ref T x)
        {
            if (x is int) __refvalue(__makeref(x), int) = int.Parse(Next());
            else if (x is long) __refvalue(__makeref(x), long) = long.Parse(Next());
            else if (x is double) __refvalue(__makeref(x), double) = double.Parse(Next());
            else if (x is char) __refvalue(__makeref(x), char) = NextChar();
            else __refvalue(__makeref(x), string) = Next();
        }

        public void i<T>(out T t)
        {
            t = default(T);
            scan(ref t);
        }

        public void i<T, TU>(out T t, out TU u)
        {
            i(out t);
            i(out u);
        }

        public void i<T, TU, TV>(out T t, out TU u, out TV v)
        {
            i(out t, out u);
            i(out v);
        }

        public void i<T, TU, TV, TW>(out T t, out TU u, out TV v, out TW w)
        {
            i(out t, out u);
            i(out v, out w);
        }

        public void i<T, TU, TV, TW, TX>(out T t, out TU u, out TV v, out TW w, out TX x)
        {
            i(out t, out u, out v);
            i(out w, out x);
        }

        public void i<T, TU, TV, TW, TX, TY>(out T t, out TU u, out TV v, out TW w, out TX x, out TY y)
        {
            i(out t, out u, out v);
            i(out w, out x, out y);
        }

        public void ini<T>(out T[] a, int n)
        {
            a = new T[n];
            for (int i = 0; i < n; ++i) i<T>(out a[i]);
        }

        public void ini<T, TU>(out T[] a, out TU[] b, int n)
        {
            a = new T[n];
            b = new TU[n];
            for (int i = 0; i < n; ++i) i<T, TU>(out a[i], out b[i]);
        }

        public void ini<T, TU, TV>(out T[] a, out TU[] b, out TV[] c, int n)
        {
            a = new T[n];
            b = new TU[n];
            c = new TV[n];
            for (var i = 0; i < n; ++i) i<T, TU, TV>(out a[i], out b[i], out c[i]);
        }

        public void ini<T>(out T[,] a, int h, int w)
        {
            a = new T[h, w];
            for (int i = 0; i < h; ++i)
            for (int j = 0; j < w; ++j)
                i<T>(out a[i, j]);
        }

        #endregion

        #endregion

        #region OUTPUT

        private readonly StreamWriter _sw = new StreamWriter(OpenStandardOutput())
        {
#if DEBUG
            AutoFlush = true
#else
        AutoFlush = false
#endif
        };

        public void Flush() => Out.Flush();
        public void YN(bool f) => yn(f, "YES", "NO");
        public void Yn(bool f) => yn(f, "Yes", "No");
        public void yn(bool f, string y = "yes", string n = "no") => WriteLine(f ? y : n);

        #endregion

        public IO()
        {
            SetOut(_sw);
        }
    }

    static class Util
    {
        public static void FOR(int a, int b, Action<int> f) => FOR(a, b, 1, f);

        public static void FOR(int a, int b, int n, Action<int> f)
        {
            for (int i = a; i < b; i += n) f(i);
        }

        public static void FOREQ(int a, int b, int n, Action<int> f) => FOR(a, b + 1, n, f);
        public static void FOREQ(int a, int b, Action<int> f) => FOR(a, b + 1, f);
        public static void REP(int a, Action<int> f) => FOR(0, a, f);
        public static void REPEQ(int a, Action<int> f) => FOR(0, a + 1, f);

        public static IEnumerable<TResult> ZIP<T1, T2, TResult>(IEnumerable<T1> xs, IEnumerable<T2> ys,
            Func<T1, T2, TResult> f = null)
        {
            using (var e1 = xs.GetEnumerator())
            using (var e2 = ys.GetEnumerator())
                while (e1.MoveNext() && e2.MoveNext())
                    yield return f(e1.Current, e2.Current);
        }

        public static IEnumerable<Tuple<T1, T2>> ZIP<T1, T2>(IEnumerable<T1> xs, IEnumerable<T2> ys)
            => ZIP(xs, ys, (x, y) => new Tuple<T1, T2>(x, y));

        public static IEnumerable<TResult> ZIP<T1, T2, T3, TResult>(IEnumerable<T1> xs, IEnumerable<T2> ys,
            IEnumerable<T3> zs, Func<T1, T2, T3, TResult> f)
        {
            using (var e1 = xs.GetEnumerator())
            using (var e2 = ys.GetEnumerator())
            using (var e3 = zs.GetEnumerator())
                while (e1.MoveNext() && e2.MoveNext() && e3.MoveNext())
                    yield return f(e1.Current, e2.Current, e3.Current);
        }

        public static IEnumerable<Tuple<T1, T2, T3>> ZIP<T1, T2, T3>(IEnumerable<T1> xs, IEnumerable<T2> ys,
            IEnumerable<T3> zs) => ZIP(xs, ys, zs, (x, y, z) => new Tuple<T1, T2, T3>(x, y, z));

        public static bool ODD(int x) => x % 2 != 0;
        public static bool ODD(long x) => x % 2L != 0L;

        public static T MAX<T>(params T[] xs) => xs.Max();

        public static T MIN<T>(params T[] xs) => xs.Min();

        public static int SUM(params int[] xs) => xs.Sum();
        public static long SUM(params long[] xs) => xs.Sum();

        public static IEnumerable<int> RANGE(int start, int step = 1)
        {
            var v = start;
            while (true)
            {
                yield return v;
                v += step;
            }
        }

        public struct IndexedItem<T>
        {
            public int Idx;
            public T Val;

            public IndexedItem(int idx, T val)
            {
                Idx = idx;
                Val = val;
            }
        }

        public class Counts<T> : Dictionary<T, int>
        {
            public Counts()
            {
            }

            public Counts(params T[] xs) : this()
            {
                Add(xs);
            }

            public Counts(IEnumerable<T> xs) : this()
            {
                foreach (var x in xs) Add(x);
            }

            public new int this[T key]
            {
                get
                {
                    int v;
                    return this.TryGetValue(key, out v) ? v : 0;
                }
            }

            public void Add(T key)
            {
                if (!base.ContainsKey(key)) base.Add(key, 0);
                base[key]++;
            }

            public void Add(params T[] xs) => xs.Iter(Add);

            public void SortByCount(bool isAsc) =>
                (isAsc ? (this.OrderBy(kp => kp.Value)) : (this.OrderByDescending(kp => kp.Value)))
                .ThenBy(kp => kp.Key);
        }

        public class PriorityQueue<T> where T : IComparable<T>
        {
            private List<T> heap;
            private readonly bool isAsc;
            private int size;
            public T Peek => heap[0];
            public int Count => size;
            public bool Any => size > 0;
            public bool Empty => !Any;

            public IEnumerable<T> Values => heap;

            public PriorityQueue(bool isAsc = true)
            {
                this.isAsc = isAsc;
                size = 0;
                heap = new List<T>();
            }

            public void Enqueue(T item)
            {
                heap.Add(item);
                var i = size++;
                while (i > 0)
                {
                    var p = (i - 1) / 2;
                    if (Compare(heap[p], item) <= 0) break;
                    heap[i] = heap[p];
                    i = p;
                }

                heap[i] = item;
            }

            public T Dequeue()
            {
                var ret = heap[0];
                var x = heap[--size];
                var i = 0;
                while ((i << 1) + 1 < size)
                {
                    var a = (i << 1) + 1;
                    var b = (i << 1) + 2;
                    if (b < size && Compare(heap[b], heap[a]) < 0) a = b;
                    if (Compare(heap[a], x) >= 0) break;
                    heap[i] = heap[a];
                    i = a;
                }

                heap[i] = x;
                heap.RemoveAt(size);
                return ret;
            }

            private int Compare(T x, T y) => isAsc ? x.CompareTo(y) : y.CompareTo(x);
        }
    }

    static class Ex
    {
        public static Tuple<IEnumerable<T>, IEnumerable<T>> Split<T>(this IEnumerable<T> xs, Func<T, bool> f)
        {
            var ys = xs.Where(f);
            var zs = xs.Where(x => !f(x));
            return Tuple.Create(ys, zs);
        }

        public static void Iter<T>(this IEnumerable<T> xs, Action<T> f)
        {
            foreach (var x in xs) f(x);
        }

        public static void Prt<T>(this T x) => WriteLine(x);
        public static void Prt<T>(this IEnumerable<T> xs) => xs.Iter(x => WriteLine(x));

        public static void Log<T>(this T x)
        {
#if DEBUG
            WriteLine($"LOG [{nameof(x)}] : {x}");
#endif
        }

        public static IEnumerable<IndexedItem<T>> Indexed<T>(this IEnumerable<T> xs) =>
            xs.Select((x, idx) => new IndexedItem<T>(idx, x));

        public static IEnumerable<T> UnIndexed<T>(this IEnumerable<IndexedItem<T>> xs) => xs.Select(x => x.Val);

        public static IEnumerable<Tuple<T, T>> Pairwise<T>(this IEnumerable<T> xs) => ZIP(xs, xs.Skip(1));

        public static IEnumerable<int> ToZeroIndexed(this IEnumerable<int> xs) => xs.Select(x => x - 1);

        public static string Join<T>(this IEnumerable<T> xs, string str = " ") => String.Join(str, xs);

        public static string Sort(this string s)
        {
            var arr = s.ToCharArray();
            Array.Sort(arr);
            return new string(arr);
        }
    }
}

Measured memory_bytes: 32716