Collatz Conjecture (Solidity Help)

Collatz conjecture is a simple algorithm. That ultimately results in an infinite loop between 1 and 4.
Depending on the user input there will be an amount of loops prior to the 1 to 4 trap.

All we want to do here is, to calculate the number of loops prior to the trap based on a senders initial value. Then return this value to the sender. (( I'm just beginning here, hope this is correct thread ))

pragma solidity >=0.7.0 <0.9.0;
contract collatzConjecture {
    // Below completely guessing I am mapping integers to an address
    mapping(address => uint256) public userInput;
    mapping(address => uint256) public userOutput;

   function doOdds(uint256 _oddNumber) private pure returns(uint256){
       //collatzConjecture, if the value is odd then * 3 + 1
       uint256 oddNumber;
       oddNumber = _oddNumber * 3 + 1;
       return oddNumber;
   }

    function doEvens(uint256 _evenNumber) private pure returns(uint256){
        //collatzConjecture, if the value is even then / 2
        uint256 evenNumber;
        evenNumber = _evenNumber / 2;
        return evenNumber;
    }

    function isOdd(uint256 _number) private pure returns(uint256){
        //just checking if the value is odd or even
        uint256 checkingNumber = _number - _number;
        if (checkingNumber != 0){
            return 0;
        }else{
            return 1;
        }
    }

    function loopCalculations(uint256 _userInput) private pure returns(uint256){
        // This is the collatzConjecture looping the _userInput
        uint256 userValue = _userInput;
        uint256 loopCount = 0;  
        do {
            loopCount ++; // Want to return this to the sender when finsihed
            if (isOdd(userValue) != 0){
                userValue = doOdds(userValue);
            }else{
                userValue = doEvens(userValue);
            }
        } while (userValue != 1);
        return loopCount;
    }

    // Below I'm guessing this is pretty wrong.
    // I am trying to immediately trying to return the result to the sender
    // using the same function the sender would use to input an amount ?
    function iniContract(uint256 _userInput) public{
        userInput[msg.sender] = loopCalculations(_userInput);
    }
}

The error from the vm was the transaction has been reverted to its initial state.

Back to this excuse I've made for myself to learn something. After another little read of the guides.

pragma solidity ^0.5.0;

contract c_collatzConjecture {
   uint256 private iniValue;
   uint256 public maxLoops;

    constructor() public {
        maxLoops = 10;
    }

    function f_doOdds(uint256 _odds) public pure returns (uint256) {return _odds * 3 + 1;}
    function f_doEvens(uint256 _evens) public pure returns (uint256) {return _evens / 2;}
    function f_isOdd(uint256 _number) public pure returns(uint256){return _number % 2;}
    function f_collatzConjecture(uint256 _iniValue) public view returns (uint256) {
        uint256 loopCount = 0;
        do {
            loopCount += 1;
            if (f_isOdd(_iniValue) == 1){
                _iniValue = f_doOdds(_iniValue);
            }else{
                _iniValue = f_doEvens(_iniValue);
            }
        } while (_iniValue != 1 || loopCount < maxLoops);
    return loopCount;
    }
}

A little bit neater, but I got a result of 13, when my maxLoops is 10.. .....

while (_iniValue != 1);

This works on its own, nice to know that the value of 138221 has 250 loops before it hits 1.

while (_iniValue != 1 || loopCount < maxLoops);

The loopCount < maxLoops ... on the OR while condition....

while (loopCount < maxLoops);

Works alone, resulting in the 10 every-time obviously..

I must be using the OR wrong in the while condition, thought I could stack those.

} while (_iniValue != 1 && loopCount < maxLoops);

Yep that works do that while this is not that AND that is not this. OR seems logical to me, but must not be.

pragma solidity ^0.8.0;

contract c_collatzConjecture {
   uint256 private iniValue;
   uint256 public maxLoops;

   constructor() {
        maxLoops = 100000000;
    }

    function f_doOdds(uint256 _odds) public pure returns (uint256) {return _odds * 3 + 1;}
    function f_doEvens(uint256 _evens) public pure returns (uint256) {return _evens / 2;}
    function f_isOdd(uint256 _number) public pure returns(uint256){return _number % 2;}
    function f_collatzConjecture(uint256 _iniValue) public view returns (uint256) {
        uint256 loopCount = 0;
        do {
            loopCount += 1;
            if (f_isOdd(_iniValue) == 1){
                _iniValue = f_doOdds(_iniValue);
            }else{
                _iniValue = f_doEvens(_iniValue);
            }
        } while (_iniValue != 1 && loopCount < maxLoops);
    return loopCount;
    }
}

Next, I want to have the sender receive the original input + the loop count.

Your may want to take a second look at the isOdd and f_isOdd functions. In isOdd, uint256 checkingNumber = _number - _number;, checkingNumber is always 0, isn't it? In f_isOdd, the name isOdd seems to be returning a boolean value yet the actual returned value is an uint256.

1 Like

Hello Maxareo thanks for your reply.

There is nothing more motivating than the drive to recover from the initial public idiocy, like isOdd for example, and you are correct it was meaningless because anything take away anything is nothing.

f_isOdd, on the other hand is working and it is returning a 1 or a 0, but I have not looked at the bool variable, well I tried it but did it wrong. Thanks I will correct this.

Regarding the integers, I will have to read some being unsure of how big these whole numbers can be, I also have seen no doubles anywhere. Hence more reading to do there. Thanks

I just attempted to take the Eth out of the account, can not. I can't see user balance. So for now I just put an initial balance in the constructor so I could get it going. Its all counting up now, but I have much to learn.

This is so far.

pragma solidity ^0.8.4;

contract c_collatzConjecture {

    uint256 private iniValue;
    uint256 private maxLoops;
    address public user = msg.sender;
    mapping (address => uint) public balances;

    constructor() {
        maxLoops = 100000000;
    }

    event returnedValue(address from, address to, uint amount);
    error joinTheClub(uint requested, uint available);

    function f_doOdds(uint256 _odds) private pure returns (uint256) {return _odds * 3 + 1;}
    function f_doEvens(uint256 _evens) private pure returns (uint256) {return _evens / 2;}
    function f_isOdd(uint256 _number) private pure returns(uint256){return _number % 2;}

    function f_collatzConjecture(uint256 _iniValue) private view returns (uint256) {
        uint256 loopCount = 0;
        do {
            loopCount += 1;
            if (f_isOdd(_iniValue) == 1){
                _iniValue = f_doOdds(_iniValue);
            }else{
                _iniValue = f_doEvens(_iniValue);
            }
        } while (_iniValue != 1 && loopCount < maxLoops);
    return loopCount;
    }

    function f_doTransferStuff(uint256 _iniValue) public {
        balances[user] = balances[user] + _iniValue;        
        if (_iniValue > balances[user])   revert joinTheClub({
            requested: _iniValue,
            available: balances[user]
        });
        balances[user] -= _iniValue;
        balances[user] += f_collatzConjecture(_iniValue);
        emit returnedValue (user, user, f_collatzConjecture(_iniValue));
    }
}

I like revert that is a pretty nice touch to an if statement.

function f_isOdd(uint256 _number) private pure returns(bool){return _number % 2;}

Remix does not like this because of the conversion from a uint256 input to a bool

This would work.

function f_isOdd(uint256 _number) private pure returns(bool){return _number % 2 == 0;}
1 Like

Thanks Maxero

Not seen bool being used like that normally its true or false.
case Statements and bools are two things I tend to ignore.

I suspect this bool operation is cheaper in gas.