void AbelianGroup::print(std::ostream& stream, mod_t p) { bool plus = false; for (dim_t i = 0; i < free_rank(); i++) { if (plus) stream << " + "; stream << "Z"; plus = true; } for (dim_t i = 0; i < tor_rank(); i++) { if (plus) stream << " + "; mpz_class order = p_pow_z(p, orders_[i]); stream << "Z/"; stream << order.get_str(10); plus = true; } if (!plus) { stream << "0"; } }
TEST(PLocal, PowInt) { EXPECT_EQ(1, p_pow_z(13, 0)); EXPECT_EQ(25, p_pow_z(5, 2)); }
GroupWithMorphisms compute_kernel(const std::size_t p, const MatrixQ& f, const AbelianGroup& X, const AbelianGroup& Y, const MatrixQRefList& to_X_ref, const MatrixQRefList& from_X_ref) { MatrixQ f_rel_Y(f.height(), f.width() + Y.tor_rank()); f_rel_Y(0, 0, f.height(), f.width()) = f; f_rel_Y(0, f.width(), Y.tor_rank(), Y.tor_rank()) = Y.torsion_matrix(p); MatrixQ rel_x_lift(f.width() + Y.tor_rank(), X.tor_rank()); rel_x_lift(0, 0, X.tor_rank(), X.tor_rank()) = X.torsion_matrix(p); // rel_x_lift(f.width(), 0, Y.tor_rank(), X.tor_rank()) = -lift of f\circ // rel_x over rel_Y. // Can be computed by multiplying the columns of f with the orders of X, // and dividing the rows by the orders of Y. for (std::size_t i = 0; i < Y.tor_rank(); ++i) { for (std::size_t j = 0; j < X.tor_rank(); ++j) { long order_diff = static_cast<long>(X(j) - Y(i)); rel_x_lift(f.width() + i, j) = -f(i, j) * p_pow_q(p, order_diff); } } // build to_X_rel_Y, from_X_rel_Y. MatrixQList to_X_rel_Y; for (MatrixQ& g_to_X : to_X_ref) { to_X_rel_Y.emplace_back(f.width() + Y.tor_rank(), g_to_X.width()); to_X_rel_Y.back()(0, 0, f.width(), g_to_X.width()) = g_to_X; MatrixQ fg = f * g_to_X; for (std::size_t i = 0; i < Y.tor_rank(); ++i) { for (std::size_t j = 0; j < g_to_X.width(); ++j) { to_X_rel_Y.back()(f.width() + i, j) = -fg(i, j) / p_pow_z(p, Y(i)); } } } MatrixQList from_X_rel_Y; for (MatrixQ& g_from_X : from_X_ref) { from_X_rel_Y.emplace_back(g_from_X.height(), f.width() + Y.tor_rank()); from_X_rel_Y.back()(0, 0, g_from_X.height(), f.width()) = g_from_X; } MatrixQRefList to_X_rel_Y_ref = ref(to_X_rel_Y); MatrixQRefList from_X_rel_Y_ref = ref(from_X_rel_Y); to_X_rel_Y_ref.emplace_back(rel_x_lift); MatrixQRefList to_Y; MatrixQRefList from_Y; smith_reduce_p(p, f_rel_Y, to_X_rel_Y_ref, from_X_rel_Y_ref, to_Y, from_Y); std::size_t rank_diff; for (rank_diff = 0; rank_diff < std::min(f_rel_Y.height(), f_rel_Y.width()); ++rank_diff) { if (f_rel_Y(rank_diff, rank_diff) == 0) break; } // next, restrict attention to the entries corresponding to zero columns of // f_rel_Y: // for rel_x_lift as well as the entries of to_X_rel_Y, take the submatrices // formed // by the corresponding rows. For from_X_rel_Y, take the submatrices formed // by // the corresponding columns. MatrixQ rel_K = rel_x_lift(rank_diff, 0, rel_x_lift.height() - rank_diff, rel_x_lift.width()); MatrixQList to_free_K; for (MatrixQ& g_to_X_rel_Y : to_X_rel_Y) { to_free_K.emplace_back(g_to_X_rel_Y( rank_diff, 0, g_to_X_rel_Y.height() - rank_diff, g_to_X_rel_Y.width())); } MatrixQList from_free_K; for (MatrixQ& g_from_X_rel_Y : from_X_rel_Y) { from_free_K.emplace_back( g_from_X_rel_Y(0, rank_diff, g_from_X_rel_Y.height(), g_from_X_rel_Y.width() - rank_diff)); } MatrixQRefList to_free_K_ref = ref(to_free_K); MatrixQRefList from_free_K_ref = ref(from_free_K); AbelianGroup free_K(rel_K.height(), 0); // then, compute the cokernel of the new rel_x_lift with the respective // to_Y, from_Y. return compute_cokernel(p, rel_K, free_K, to_free_K_ref, from_free_K_ref); }
mpq_class AbelianGroup::TorsionMatrix<mpq_class>::operator()( const dim_t i, const dim_t j) const { return i == j ? p_pow_z(p_, group_.orders_[i]) : 0; }