#include <iostream>
#include <vector>
#include <array>
#include <utility>
#include<algorithm>
using val_t=char;
using super_vec=std::vector< std::pair<val_t*, std::array<val_t,4>>>;
using super_it=super_vec::iterator;
using super_pair=std::pair<val_t*, std::array<val_t,4>>;
super_vec
make_chains(val_t *arr, size_t N)
{
    super_vec vec;
    int size = N/4;
    val_t *p=arr;
    for(int i=0; i<size; ++i)
    {
        std::array<val_t,4> v;
        val_t *pp=p;
        for(int j=0; j<4; ++j)
        {
            v[j]=(*p);
            ++p;
        }
        std::sort(v.begin(), v.end());
        vec.push_back( {pp, v} );
    }
    return vec;
}
void my_summ_sort(super_it left, super_it right)
{
    std::sort(left, right,
        [](const super_pair &a, const super_pair &b)
              {
                return a.second < b.second;
              }
              );
}
val_t* max4(val_t *begin, int size=4)
{
    auto end=begin+size;
    auto pmax=begin;
    val_t max_val=*begin;
    ++begin;
    for( ;begin!=end; ++begin )
    {
       if(*begin>max_val)
        {
            max_val=*begin;
            pmax=begin;
        }
    }
    return pmax;
}
bool rotor_equal(val_t *begin1, val_t *begin2,  int size=4)
{
    auto max1=max4(begin1, size);
    auto max2=max4(begin2, size);
    auto last_ind=size-1;
    for(int i=0; i<size; ++i)
    {
        if( *max1 != *max2 ) return false;
        max1 = max1-begin1 == last_ind? begin1: ++max1 ;
        max2 = max2-begin2 == last_ind? begin2: ++max2 ;
    }
    return true;
}
int main()
{
    val_t set_of_matrixes[]={
         2,3,4,5//
        ,3,4,5,2//
        ,1,2,3,4///
        ,1,2,4,5
        ,2,3,5,4
        ,2,3,4,1///
        ,3,4,5,1
        ,2,3,4,1///
        };
        const int sz=sizeof(set_of_matrixes)/sizeof(val_t);
    auto chains=make_chains(set_of_matrixes, sz);
    my_summ_sort(chains.begin(), chains.end());
    //chains of equal value set:
    using has_twin = int;
    std::vector<std::vector<std::pair<val_t*, has_twin>>> eqv_set_chains;
    super_it sup_it=chains.begin();
    super_it sup_it_end=chains.end();
    for ( ; sup_it!=sup_it_end; )
    {
        auto upb_it = std::upper_bound(sup_it, sup_it_end, *sup_it,
              [](const super_pair &a, const super_pair &b)
              {
                return a.second < b.second;
              }
              );
              std::vector<std::pair<val_t*, has_twin>> eqv_range;
              for( ; sup_it!=upb_it; ++sup_it)
              {
                 eqv_range.push_back({sup_it->first,-1});
              }
              eqv_set_chains.push_back(eqv_range);
    }
    int twin_count(0);
    for(auto & v:eqv_set_chains)
    {
        auto it_end=v.end();
            for(auto iter=v.begin(); iter!=it_end; ++iter)
            {
                bool once_for_leader=true;
                for(auto it=iter; it!=it_end; ++it)
                {
                    if(it==iter || it->second!=-1)continue;
                    auto twin_bool=rotor_equal(iter->first, it->first);
                    if(twin_bool)
                    {
                       auto index_leader=iter-v.begin();
                       iter->second=index_leader;
                       it->second=index_leader;
                       ++twin_count;
                       if(once_for_leader)
                       {
                           ++twin_count;
                           once_for_leader=false;
                       }
                    }
                }
            }
    }
    std::cout<<"the number of twined matrixes is "<<(twin_count)<<'\n';
    std::cout<<"the number of different matrixes is "<<(sz/4-twin_count)<<'\n';
    //ще можна вивести вiдповiднi набори матриць, але в завдання воно не входить)
    return 0;
}
//наче працює, але не тестував. Можна, звiсно трохи оптiмiзувати. Не дивлячись на те, що стайл не спортивний, - є видiлення додаткової, пам'ятi, загальна швiдкiсть може бути не погана.