import 'dart:math';
import 'dart:convert';
import 'package:flutter/material.dart';//flutter part too
part 'kwidgets.dart';
class PseudoRandom {
int _a;
int _c;
int _m = 1 << 32;
int _s;
int _i;
PseudoRandom([int prod = 1664525, int add = 1013904223]) {
_a = prod;
_c = add;
_s = Random().nextInt(_m) * 2 + 1;//odd
next();// a fast round
_i = _a.modInverse(_m);//4276115653 as inverse of 1664525
}
int next() {
return _s = (_a * _s + _c) % _m;
}
int prev() {
return _s = (_s - _c) * _i % _m;
}
}
class _RingNick {
//alternate strategy of using least and most and lower window
static bool alt = true;
static double biasPlus = 0.125;//gen better figure
final List<double> walls = [ 0.25 - biasPlus, 0.5, 0.75 + biasPlus ];
int _position = 0;
final int mostEscaped = alt ? 0 : 1;//the lowest pair of walls 0.25 and 0.5
final int leastEscaped = 2;//the highest walls 0.5 and 0.75
final int theThird = alt ? 1 : 0;//the 0.75 and 0.25 walls
bool _right = true;
static final PseudoRandom _pr = PseudoRandom();
int _getPosition() => _position;
int _asMod(int pos) {
return pos % walls.length;
}
void _setPosition(int pos) {
_position = _asMod(pos);
}
void _next() {
int direction = _right ? 0 : walls.length - 1;//truncate to 2
double wall = walls[_asMod(_getPosition() + direction)];
if(_pr.next() > (wall * _pr._m).toInt()) {
//jumped
_setPosition(_position + (_right ? 1 : walls.length - 1));
} else {
//not jumped
_right = !_right;//bounce
}
}
void _prev() {
int direction = !_right ? 0 : walls.length - 1;//truncate to 2
double wall = walls[_asMod(_getPosition() + direction)];
if(_pr._s > (wall * _pr._m).toInt()) {// the jump over before sync
//jumped
_setPosition(_position + (!_right ? 1 : walls.length - 1));
} else {
//not jumped
_right = !_right;//bounce -- double bounce and scale before sync
}
_pr.prev();//exact inverse
}
void __next(bool skip) {
_next();
if(!skip) while(_getPosition() == mostEscaped) _next();
}
void __prev(bool skip) {
_prev();
if(!skip) while(_getPosition() == mostEscaped) _prev();
}
}
class _GroupHandler {//yes, something goes here
List<_RingNick> _rn;
final int _window = 5;
final int _opportunity = 1;
final int _favouriteBit = 1 << 27;
final bool _hypothesisA = true;//mutual information between wall states
final bool _hypothesisB = true;//mutual information between wall states
final bool _hypothesisC = true;//no spread required
final bool _hypothesisD = true;//right bit is better for bias
//Hypothesis D is perhaps the most interesting one.
//There was an experiment with 128 walls at one point with the assumption
//that the higher walls to the right would be more reflective and hence
//the ring would move left more often.
//The experiment proved this wrong with central limit statistics after 2 and a bit days ...
//The explanation was the more "containy" right hand side held the climber
//closer to the right edge, with an occasional fast zip through the left hand
//side. The net bias was for rightward motion in the "current code framework"
//The number 55 was not pulled from thin air, but was the worst case odd "consensus"
//size of ring nicks.
//The walls being lower on the left edges of cells was not providing the correct
//direction of bias in motion.
_GroupHandler(int size) {
if(size % 2 == 0) size++;
_rn = List<_RingNick>(size);
}
//BIAS
//REMAIN (w1 * w2)^N
//LEAVE (1-w1) + w1 * (1-w2) ...
//even the switching on the fly of alt?
//The matrix joke about mining humans as Maxwell demons was quite funny
//the effect of a _mod1 and its 'right' interaction with the lowest wall?
//in the most contained, which wall is higher?
//in the least contained, which?
//so a more likely turns into ?
void _next() {
for(int i = 0; i < _window; i++) {//null bias settling window
if(!_hypothesisC) if(i == _opportunity) {//1 pre (so 4 of 5)
for (_RingNick r in _rn) {
_RingNick._pr.next();
if((_RingNick._pr._s & _favouriteBit) != 0) {
//kind of an xor spread to 50:50 cell
_mod1(r, true);
}
}
}
for (_RingNick r in _rn)
r.__next(_hypothesisD);
}
}
void _prev() {
for(int i = 0; i < _window; i++) {
for(_RingNick r in _rn.reversed)
r.__prev(_hypothesisD);
if(!_hypothesisC) if(i == _window - _opportunity - 1) {//3 post (so 4 of 5)
for(_RingNick r in _rn.reversed) {
if((_RingNick._pr._s & _favouriteBit) != 0) {
_mod1(r, false);
}
_RingNick._pr.prev();
}
}
}
}
bool _majority() {
int count = 0;
for(_RingNick r in _rn) {
if(_hypothesisD) {
if(r._right) count++;
} else {
if (r._getPosition() == r.leastEscaped) count++; //a main cumulative
}
}// the > 2/3rd state would be true
return (2 * count > _rn.length);
}
void _mod1(_RingNick r, bool direction) {
//if(_hypothesis) r._right = !r._right;// a wall reflection hypothesis!
//either both are the same or one is worse than the other (non-linear)
//a more likely turns into ?
//engineering differential probabilities
if(_hypothesisD) {
r._right = !r._right;//basic direction of motion flip
} else {
if (r._getPosition() == r.leastEscaped) {
r._setPosition(r.theThird);
if ((_hypothesisA ^ direction) || (_hypothesisB ^ !direction))
r._right = !r._right;
} else {
//mostEscaped eliminated by not being used
r._setPosition(r.leastEscaped);
if ((_hypothesisB ^ direction) || (_hypothesisA ^ !direction))
r._right = !r._right;
}
}
}
void _modulate(bool direction) {
for(_RingNick r in _rn) _mod1(r, direction);//all modulate
}
}
class _Modulator {
final _GroupHandler _gh = _GroupHandler(_RingNick.alt ? 33 : 55);
int _putBit(bool bitToAbsorb) {//returns absorption status
_gh._next();
if(_gh._majority()) {//main zero state
if(bitToAbsorb) {
_gh._modulate(true);
return 0;//a zero yet to absorb
} else {
return 1;//absorbed zero
}
} else {
return -1;//no absorption emitted 1
}
}
int _getBit(bool bitLastEmitted) {
if(_gh._majority()) {//zero
_gh._prev();
return 1;//last bit not needed emit zero
} else {
if(bitLastEmitted) {
_gh._prev();
return -1;//last bit needed and nothing to emit
} else {
_gh._modulate(false);
_gh._prev();
return 0;//last bit needed, emit 1
}
}
}
}
class StackHandler {
final List<bool> _data = [];
final _Modulator _m = _Modulator();
int _lenCode = 0;
int _putBits() {
int count = 0;
while(_data.length > 0) {
bool v = _data.removeLast();
switch(_m._putBit(v)) {
case -1:
_data.add(v);
_data.add(true);
break;
case 0:
_data.add(false);
break;
case 1:
break;//absorbed zero
default: break;
}
count++;
}
return count;
}
void _getBits(int count) {
while(count > 0) {
bool v;
v = (_data.length == 0 ? false : _data.removeLast());//zeros out
switch(_m._getBit(v)) {
case 1:
_data.add(v);//not needed
_data.add(false);//emitted zero
break;
case 0:
_data.add(true);//emitted 1 used zero
break;
case -1:
break;//bad skip, ...
default: break;
}
count--;
}
}
//STACK BLOCK FUNCTIONS 32 BIT
int _putBlock(int lastCount) {
explode(lastCount);
return _putBits();
}
void putBlock() {
_lenCode = _putBlock(_lenCode);
}
int _getBlock(int count) {
if(count <= 0) throw Exception('Kodek empty below accumulated depth.');
_getBits(count);
if(_data.length < 32) throw Exception('Kodek hacked bad terminal.');//an empty is min 32
return implode();
}
void getBlock() {
_lenCode = _getBlock(_lenCode);
}
//BASIC UNIT OF EXTERNALIZED
void explode(int x, [int c = 32]) {
for(int i = 0; i < c; i++)
_data.add(x & (1 << i) != 0);
}
int implode([int c = 32]) {
int result = 0;
for(int i = 0; i < c; i++)
result &= ((_data.removeLast() ?? false) ? 1 : 0) << (c - 1 - i);
return result;
}
void load() {
for(_RingNick r in _m._gh._rn) {
if(_data.length <= 0) throw Exception('Bad Kodek block length.');
while((r._position = implode(2)) == 3) implode(1);//BILBO
r._right = implode(1) == 1;
}
_lenCode = implode();
_RingNick._pr._s = implode();
}
void save(bool reset) {
explode(_RingNick._pr._s);
explode(_lenCode);
for(_RingNick r in _m._gh._rn.reversed) {
explode(r._position, 2);
explode(r._right ? 1 : 0, 1);
}
if(reset) _lenCode = 0;//reset
}
void explodeString(String s) {
if(s == null) {
explode(0, 1);
return;
}
List<int> bytes = utf8.encode(s);
int len = s.length;
for(int i in bytes) explode(i, 8);
explode(len);
explode(1, 1);//not null marker
}
String implodeString() {
if(implode(1) == 0) return null;//string terminal
int len = implode();
List<int> bytes = [];
for(int i = 0; i < len; i++) bytes.add(implode(8));
return utf8.decode(bytes.reversed);
}
String getB64() {
if(_data.length != 0) throw Exception('Kodek must be fully packed.');
save(false);
List<int> chars = [];
while(_data.length >= 8) {
chars.add(implode(8));
}
int i = _data.length;
chars.add(implode(8));
chars.add(i);//length bits
return base64Encode(chars);
}
void setB64(String s) {
List<int> chars = base64Decode(s);
int x = chars.removeLast();
if(x > 7 || x < 0) throw Exception('Bad Kodek block alignment.');
explode(chars.removeLast(), 8);//8 bits
while((x++) < 8) implode(1);//remove bits
while(chars.length > 0) explode(chars.removeLast(), 8);//explode kodek content
load();
}
}
class BlockChainTXStack {//A singleton is best, as the interaction between multiples is not order 'safe'
final StackHandler _k = StackHandler();
String _saved;
String send(String s) {
_k.explodeString(s);
_k.putBlock();
return _k.getB64();
}
List<String> receive(String s, [int down = 1, bool terminal = true]) {
List<String> ls = [];
_k.setB64(s);
while(down-- > 0) {
_k.getBlock();//top
ls.add(_k.implodeString());
}
if(!terminal) ls.add(_k.getB64());//might as well make it easy to get a handle on lower ones again?
return ls;
}
String nest() {//does not save push/pop states
send(_k.getB64());//don't need result yet
return send(_saved);
}
void fledgling(String s, [int nestDown = 1, bool destroyPop = false]) {//in pairs
List<String> ls = receive(s, 2 * nestDown);//don't need terminal hook
if(destroyPop) {
_saved = ls.removeLast();//the saved stack
} else {
ls.removeLast();//dump
}
_k.setB64(ls.removeLast());//the kodek active
}
void push() {//state save
String tmp = _k.getB64();
if(_saved != null) _k.setB64(_saved);
_saved = send(tmp);
_k.setB64(tmp);//restore
}
void pop() {//state reload
_k.setB64(receive(_saved, 1, false).removeLast());
}
void popNest() {
String k = _k.getB64();//don't need result yet
String tmp = _saved;
pop();
send(k);
send(tmp);//stack the temporary pushed scope as a nest in the pop scope
}
void pushFledgling([int nestDown = 1, bool destroyTree = true]) {
String s = _k.getB64();
if(destroyTree) receive(s, 2);//2DROP
push();//save scope
fledgling(s, nestDown);//extract from self
}
}
class BlockTreeManager {
final BlockChainTXStack tx = BlockChainTXStack();
void insert() {
}
String select() {
return null;//TODO
}
}