#include <iostream>
#include <algorithm>
#include <climits>
using namespace std;
// Индекс самого старшего выставленного бита, считая младший бит за №0:
// floor_log2(0b101000) == 5, floor_log2(1 << N) == N.
// 543210
int floor_log2(unsigned int p)
{
return CHAR_BIT * sizeof(unsigned int) - 1 - __builtin_clz(p);
}
template<typename Item>
class matrix
{
public:
matrix(size_t sx, size_t sy)
: _sx(sx), _sy(sy), items(nullptr)
{
items = new Item[sx * sy];
set_identity();
}
~matrix() { delete[] items; }
size_t sx() const { return _sx; }
size_t sy() const { return _sy; }
const Item& operator()(size_t x, size_t y) const { return items[y * _sx + x]; }
Item& operator()(size_t x, size_t y) { return items[y * _sx + x]; }
void set_identity()
{
fill(items, items + _sx * _sy, 0);
for (size_t xy = 0; xy < min(_sx, _sy); xy++) (*this)(xy, xy) = 1;
}
void set_copy(const matrix& b)
{
if (_sx != b._sx || _sy != b._sy)
throw runtime_error("Неверные размеры для set_copy.");
copy(b.items, b.items + b._sx * b._sy, items);
}
static void mul(matrix& r, const matrix& a, const matrix& b)
{
if (r._sx != b._sx || r._sy != a._sy || a._sx != b._sy)
throw runtime_error("Неверные размеры для mul.");
for (size_t ry = 0; ry < r._sy; ry++)
for (size_t rx = 0; rx < r._sx; rx++)
{
// скалярное произведение (dot) ry-й строки a на rx-й столбец b. rc — сокращённо row-column.
Item& rcdot = r(rx, ry) = a(0, ry) * b(rx, 0);
for (size_t irc = 1; irc < a._sx; irc++) rcdot += a(irc, ry) * b(rx, irc);
}
}
matrix operator *(const matrix& b)
{
matrix r(b._sx, _sy);
mul(r, *this, b);
return r;
}
static void pow(matrix& r, const matrix& m, unsigned int p)
{
if (r._sx != m._sx || r._sy != m._sy || m._sx != m._sy)
throw runtime_error("Неверные размеры для pow.");
if (p == 0) { r.set_identity(); return; }
if (p == 1) { r.set_copy(m); return; }
matrix temp(m._sx, m._sy);
matrix *ping = &temp, *pong = &r;
bool first = true; // на первой итерации умножаем m на m
int ibit = floor_log2(p);
if ((__builtin_popcount(p) + ibit) % 2 != 0) swap(ping, pong);
while (--ibit >= 0)
{
mul(*pong, first ? m : *ping, first ? m : *ping);
swap(ping, pong);
first = false;
if ((p & (1 << ibit)) != 0)
{
mul(*pong, *ping, m);
swap(ping, pong);
}
}
if (ping != &r) throw runtime_error("Этого не должно происходить.");
}
matrix pow(unsigned int p)
{
matrix r(_sx, _sy);
pow(r, *this, p);
return r;
}
private:
size_t _sx, _sy;
Item* items;
};
template<typename Item>
ostream& operator <<(ostream& strm, const matrix<Item>& m)
{
for (size_t y = 0; y < m.sy(); y++)
for (size_t x = 0; x < m.sx(); x++)
(x > 0 ? strm << " " : (y > 0 ? strm << endl : strm)) << m(x, y);
return strm;
}
int main()
{
matrix<double> m(4, 4);
for (int i = 0; i < 16; i++) m(i%4, i/4) = i%3;
cout << "Исходная матрица m:" << endl;
cout << m << endl << endl;
cout << "m * m * ...6 раз... * m:" << endl;
cout << m*m*m*m*m*m << endl << endl;
cout << "m.pow(6):" << endl;
cout << m.pow(6) << endl << endl;
cout << "m * m * ...7 раз... * m:" << endl;
cout << m*m*m*m*m*m*m << endl << endl;
cout << "m.pow(7):" << endl;
cout << m.pow(7) << endl;
return 0;
}
I2luY2x1ZGUgPGlvc3RyZWFtPgojaW5jbHVkZSA8YWxnb3JpdGhtPgojaW5jbHVkZSA8Y2xpbWl0cz4KdXNpbmcgbmFtZXNwYWNlIHN0ZDsKCi8vINCY0L3QtNC10LrRgSDRgdCw0LzQvtCz0L4g0YHRgtCw0YDRiNC10LPQviDQstGL0YHRgtCw0LLQu9C10L3QvdC+0LPQviDQsdC40YLQsCwg0YHRh9C40YLQsNGPINC80LvQsNC00YjQuNC5INCx0LjRgiDQt9CwIOKEljA6Ci8vIGZsb29yX2xvZzIoMGIxMDEwMDApID09IDUsIGZsb29yX2xvZzIoMSA8PCBOKSA9PSBOLgovLyAgICAgICAgICAgICAgNTQzMjEwCmludCBmbG9vcl9sb2cyKHVuc2lnbmVkIGludCBwKQp7CglyZXR1cm4gQ0hBUl9CSVQgKiBzaXplb2YodW5zaWduZWQgaW50KSAtIDEgLSBfX2J1aWx0aW5fY2x6KHApOwp9Cgp0ZW1wbGF0ZTx0eXBlbmFtZSBJdGVtPgpjbGFzcyBtYXRyaXgKewpwdWJsaWM6CgltYXRyaXgoc2l6ZV90IHN4LCBzaXplX3Qgc3kpCgkJOiBfc3goc3gpLCBfc3koc3kpLCBpdGVtcyhudWxscHRyKQoJewoJCWl0ZW1zID0gbmV3IEl0ZW1bc3ggKiBzeV07CgkJc2V0X2lkZW50aXR5KCk7Cgl9CgoJfm1hdHJpeCgpIHsgZGVsZXRlW10gaXRlbXM7IH0KCXNpemVfdCBzeCgpIGNvbnN0IHsgcmV0dXJuIF9zeDsgfQoJc2l6ZV90IHN5KCkgY29uc3QgeyByZXR1cm4gX3N5OyB9CgoJY29uc3QgSXRlbSYgb3BlcmF0b3IoKShzaXplX3QgeCwgc2l6ZV90IHkpIGNvbnN0IHsgcmV0dXJuIGl0ZW1zW3kgKiBfc3ggKyB4XTsgfQoJSXRlbSYgb3BlcmF0b3IoKShzaXplX3QgeCwgc2l6ZV90IHkpIHsgcmV0dXJuIGl0ZW1zW3kgKiBfc3ggKyB4XTsgfQoKCXZvaWQgc2V0X2lkZW50aXR5KCkKCXsKCQlmaWxsKGl0ZW1zLCBpdGVtcyArIF9zeCAqIF9zeSwgMCk7CgkJZm9yIChzaXplX3QgeHkgPSAwOyB4eSA8IG1pbihfc3gsIF9zeSk7IHh5KyspICgqdGhpcykoeHksIHh5KSA9IDE7Cgl9CgoJdm9pZCBzZXRfY29weShjb25zdCBtYXRyaXgmIGIpCgl7CgkJaWYgKF9zeCAhPSBiLl9zeCB8fCBfc3kgIT0gYi5fc3kpCgkJCXRocm93IHJ1bnRpbWVfZXJyb3IoItCd0LXQstC10YDQvdGL0LUg0YDQsNC30LzQtdGA0Ysg0LTQu9GPIHNldF9jb3B5LiIpOwoJCWNvcHkoYi5pdGVtcywgYi5pdGVtcyArIGIuX3N4ICogYi5fc3ksIGl0ZW1zKTsKCX0KCglzdGF0aWMgdm9pZCBtdWwobWF0cml4JiByLCBjb25zdCBtYXRyaXgmIGEsIGNvbnN0IG1hdHJpeCYgYikKCXsKCQlpZiAoci5fc3ggIT0gYi5fc3ggfHwgci5fc3kgIT0gYS5fc3kgfHwgYS5fc3ggIT0gYi5fc3kpCgkJCXRocm93IHJ1bnRpbWVfZXJyb3IoItCd0LXQstC10YDQvdGL0LUg0YDQsNC30LzQtdGA0Ysg0LTQu9GPIG11bC4iKTsKCgkJZm9yIChzaXplX3QgcnkgPSAwOyByeSA8IHIuX3N5OyByeSsrKQoJCQlmb3IgKHNpemVfdCByeCA9IDA7IHJ4IDwgci5fc3g7IHJ4KyspCgkJCXsKCQkJCS8vINGB0LrQsNC70Y/RgNC90L7QtSDQv9GA0L7QuNC30LLQtdC00LXQvdC40LUgKGRvdCkgcnkt0Lkg0YHRgtGA0L7QutC4IGEg0L3QsCByeC3QuSDRgdGC0L7Qu9Cx0LXRhiBiLiByYyDigJQg0YHQvtC60YDQsNGJ0ZHQvdC90L4gcm93LWNvbHVtbi4KCQkJCUl0ZW0mIHJjZG90ID0gcihyeCwgcnkpID0gYSgwLCByeSkgKiBiKHJ4LCAwKTsKCQkJCWZvciAoc2l6ZV90IGlyYyA9IDE7IGlyYyA8IGEuX3N4OyBpcmMrKykgcmNkb3QgKz0gYShpcmMsIHJ5KSAqIGIocngsIGlyYyk7CgkJCX0KCX0KCgltYXRyaXggb3BlcmF0b3IgKihjb25zdCBtYXRyaXgmIGIpCgl7CgkJbWF0cml4IHIoYi5fc3gsIF9zeSk7CgkJbXVsKHIsICp0aGlzLCBiKTsKCQlyZXR1cm4gcjsKCX0KCglzdGF0aWMgdm9pZCBwb3cobWF0cml4JiByLCBjb25zdCBtYXRyaXgmIG0sIHVuc2lnbmVkIGludCBwKQoJewoJCWlmIChyLl9zeCAhPSBtLl9zeCB8fCByLl9zeSAhPSBtLl9zeSB8fCBtLl9zeCAhPSBtLl9zeSkKCQkJdGhyb3cgcnVudGltZV9lcnJvcigi0J3QtdCy0LXRgNC90YvQtSDRgNCw0LfQvNC10YDRiyDQtNC70Y8gcG93LiIpOwoKCQlpZiAocCA9PSAwKSB7IHIuc2V0X2lkZW50aXR5KCk7IHJldHVybjsgfQoJCWlmIChwID09IDEpIHsgci5zZXRfY29weShtKTsgcmV0dXJuOyB9CgoJCW1hdHJpeCB0ZW1wKG0uX3N4LCBtLl9zeSk7CgkJbWF0cml4ICpwaW5nID0gJnRlbXAsICpwb25nID0gJnI7CgkJYm9vbCBmaXJzdCA9IHRydWU7IC8vINC90LAg0L/QtdGA0LLQvtC5INC40YLQtdGA0LDRhtC40Lgg0YPQvNC90L7QttCw0LXQvCBtINC90LAgbQoJCWludCBpYml0ID0gZmxvb3JfbG9nMihwKTsKCQlpZiAoKF9fYnVpbHRpbl9wb3Bjb3VudChwKSArIGliaXQpICUgMiAhPSAwKSBzd2FwKHBpbmcsIHBvbmcpOwoKCQl3aGlsZSAoLS1pYml0ID49IDApCgkJewoJCQltdWwoKnBvbmcsIGZpcnN0ID8gbSA6ICpwaW5nLCBmaXJzdCA/IG0gOiAqcGluZyk7CgkJCXN3YXAocGluZywgcG9uZyk7CgkJCWZpcnN0ID0gZmFsc2U7CgoJCQlpZiAoKHAgJiAoMSA8PCBpYml0KSkgIT0gMCkKCQkJewoJCQkJbXVsKCpwb25nLCAqcGluZywgbSk7CgkJCQlzd2FwKHBpbmcsIHBvbmcpOwoJCQl9CgkJfQoKCQlpZiAocGluZyAhPSAmcikgdGhyb3cgcnVudGltZV9lcnJvcigi0K3RgtC+0LPQviDQvdC1INC00L7Qu9C20L3QviDQv9GA0L7QuNGB0YXQvtC00LjRgtGMLiIpOwoJfQoKCW1hdHJpeCBwb3codW5zaWduZWQgaW50IHApCgl7CgkJbWF0cml4IHIoX3N4LCBfc3kpOwoJCXBvdyhyLCAqdGhpcywgcCk7CgkJcmV0dXJuIHI7Cgl9Cgpwcml2YXRlOgoJc2l6ZV90IF9zeCwgX3N5OwoJSXRlbSogaXRlbXM7Cn07Cgp0ZW1wbGF0ZTx0eXBlbmFtZSBJdGVtPgpvc3RyZWFtJiBvcGVyYXRvciA8PChvc3RyZWFtJiBzdHJtLCBjb25zdCBtYXRyaXg8SXRlbT4mIG0pCnsKCWZvciAoc2l6ZV90IHkgPSAwOyB5IDwgbS5zeSgpOyB5KyspCgkJZm9yIChzaXplX3QgeCA9IDA7IHggPCBtLnN4KCk7IHgrKykKCQkJKHggPiAwID8gc3RybSA8PCAiICIgOiAoeSA+IDAgPyBzdHJtIDw8IGVuZGwgOiBzdHJtKSkgPDwgbSh4LCB5KTsKCXJldHVybiBzdHJtOwp9CgppbnQgbWFpbigpCnsKCW1hdHJpeDxkb3VibGU+IG0oNCwgNCk7Cglmb3IgKGludCBpID0gMDsgaSA8IDE2OyBpKyspIG0oaSU0LCBpLzQpID0gaSUzOwoJY291dCA8PCAi0JjRgdGF0L7QtNC90LDRjyDQvNCw0YLRgNC40YbQsCBtOiIgPDwgZW5kbDsKCWNvdXQgPDwgbSA8PCBlbmRsIDw8IGVuZGw7CgoJY291dCA8PCAibSAqIG0gKiAuLi42INGA0LDQty4uLiAqIG06IiA8PCBlbmRsOwoJY291dCA8PCBtKm0qbSptKm0qbSA8PCBlbmRsIDw8IGVuZGw7CgoJY291dCA8PCAibS5wb3coNik6IiA8PCBlbmRsOwoJY291dCA8PCBtLnBvdyg2KSA8PCBlbmRsIDw8IGVuZGw7CgoJY291dCA8PCAibSAqIG0gKiAuLi43INGA0LDQty4uLiAqIG06IiA8PCBlbmRsOwoJY291dCA8PCBtKm0qbSptKm0qbSptIDw8IGVuZGwgPDwgZW5kbDsKCgljb3V0IDw8ICJtLnBvdyg3KToiIDw8IGVuZGw7Cgljb3V0IDw8IG0ucG93KDcpIDw8IGVuZGw7CglyZXR1cm4gMDsKfQ==