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.

My last post here was 30 days ago. I have improved somewhat. My contract has been revised.

// SPDX-License-Identifier: MIT
// MattMcP_Solidity_collatzConjecture v 0.2
// collatzConjecture.sol

pragma solidity ^0.8.4;

contract c_collatzConjecture {
    uint256 internal maxLoops;
    bool internal lockContact;
    mapping (address => uint) internal balances;

    constructor (uint256 _maxLoops) {
        maxLoops = _maxLoops;
    }

    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_getMaxLoops() external view returns (uint256) {return maxLoops;}
    function f_getbalance() external view returns(uint256 loopAmounts) {loopAmounts = balances[msg.sender];}
    function f_setMaxLoops (uint256 _newLoops) external {maxLoops = _newLoops;}

    function f_collatzConjecture(uint256 _iniValue) internal 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_enterANumber (uint256 _iniValue) external payable f_noReEntry{
        balances[msg.sender] = balances[msg.sender] + _iniValue;        
        if (_iniValue > balances[msg.sender])   revert joinTheClub({
            requested: _iniValue,
            available: balances[msg.sender]
        });
        balances[msg.sender] -= _iniValue;
        balances[msg.sender] += f_collatzConjecture(_iniValue);
        emit returnedValue (msg.sender, msg.sender, f_collatzConjecture(_iniValue));
    }
    
   modifier f_noReEntry() {
        require(!lockContact, "No re-entrancy");
        lockContact = true;
        _;
        lockContact = false;
    }
}

@Maxero FYI::
If I remember correctly changing the integer to a bool, cost more gas, so I left the integer.

@Anyone reading
I would really appreciate any constructive criticism regarding how I have made this contract.

  1. This contract can crash remix, (I have not found out yet on what loop count)
  2. I put in a re entrance modifier, I only think its working.

Forward work.

  1. Edit the contract collect the loop sequence in an array. (Later to plot on a graph)
  2. Deploy the contract, (Need to understand the ABI encoding for constructor as its set as an ARG.?
  3. Using JavaScript and only the ethers library interact with the contract from a webpage.

Agenda :: Self learning, may help someone else.

Adding an array to hold the sequence of values calculated on the way to 1.

uint256[] sequence;

Then I will need some way of viewing each element in the sequence.

function f_getSequence (uint256 _element) external view returns (uint256 Sequence){
        Sequence = sequence[_element];
    }

Then I bump into a little bit of confusion.

function f_collatzConjecture(uint256 _iniValue) internal virtual returns (uint256) {
        uint256 loopCount = 0;
        //sequence[loopCount] =_iniValue;
        do {
            loopCount += 1;
            if (f_isOdd(_iniValue) == 1){
                _iniValue = f_doOdds(_iniValue);
            }else{
                _iniValue = f_doEvens(_iniValue);
            }
            //sequence[loopCount] =_iniValue;
        } while (_iniValue != 1 && loopCount < maxLoops);
    return loopCount;
    }

When I activate these lines "//sequence[loopCount] =_iniValue;"
I cannot use the f_enterANumber function.

function f_enterANumber (uint256 _iniValue) external payable f_noReEntry{
        balances[msg.sender] = balances[msg.sender] + _iniValue;        
        if (_iniValue > balances[msg.sender])   revert joinTheClub({
            requested: _iniValue,
            available: balances[msg.sender]
        });
        balances[msg.sender] -= _iniValue;
        balances[msg.sender] += f_collatzConjecture(_iniValue);
        emit returnedValue (msg.sender, msg.sender, f_collatzConjecture(_iniValue));
    }

The error I am receiving is stating that the function should be payable, however an internal function cannot be payable hence I am confused how to tag this internal function f_collatzConjecture.

//seen in full above above
function f_collatzConjecture(uint256 _iniValue) internal virtual returns (uint256)

Could anyone please explain this to me. ?
Much appreciated.

uint256[] Something;

^^ That was panful, the following link helped.
"https://docs.soliditylang.org/en/v0.8.11/types.html?highlight=mapping#mapping-types"

Now I have the users entry value and the resulting values of each loop mapped.

pragma solidity ^0.8.4;

contract c_collatzConjecture {

    uint256 internal maxLoops;
    bool internal lockContact;
    mapping(uint256 => uint256) sequence;
    mapping (address => uint256) internal balances;

    constructor (uint256 _maxLoops) {
        maxLoops = _maxLoops;
    }

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

    function f_doOdds(uint256 _odds) internal pure returns (uint256) {return _odds * 3 + 1;}
    function f_doEvens(uint256 _evens) internal pure returns (uint256) {return _evens / 2;}
    function f_isOdd(uint256 _number) internal pure returns(uint256){return _number % 2;}
    function f_getMaxLoops() external view returns (uint256) {return maxLoops;}
    function f_getbalance() external view returns(uint256 loopAmounts) {loopAmounts = balances[msg.sender];}
    function f_getSequence (uint256 _element) external view returns (uint256 Sequence){Sequence = sequence[_element];}
    function f_setMaxLoops (uint256 _newLoops) external payable {maxLoops = _newLoops;}

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

    function f_enterANumber (uint256 _iniValue) external payable f_noReEntry{
        balances[msg.sender] = balances[msg.sender] + _iniValue;        
        if (_iniValue > balances[msg.sender])   revert joinTheClub({
            requested: _iniValue,
            available: balances[msg.sender]
        });
        balances[msg.sender] -= _iniValue;
        balances[msg.sender] += f_collatzConjecture(_iniValue);
        emit returnedValue (msg.sender, msg.sender, f_collatzConjecture(_iniValue));
    }
   
   modifier f_noReEntry() {
        require(!lockContact, "No re-entrancy");
        lockContact = true;
        _;
        lockContact = false;
    }
}

I removed the constructor. The maximum loops must be set now.
When I tried to encode the constructor with the value used to deploy it was not accepted.

nvm, here is that contract on bsct
"https://testnet.bscscan.com/address/0xa6555710e783dfaf47dcba41d701c0f3de7cddc6#code"

There is no owner on this contract anyone can enter a value. What it does is quite simple.
You enter a value and it returns the amount of loops it takes for the conjecture to reach 1.
The initial value is stored as the first mapped key, and each key following stores the values for each loop achieved throughout the conjecture.

It would b good to see the results plotted for results across loops.

Javascript is surprisingly easy. A bit like fat lego.
Solidity it a bit more strict.

Thanking open Zeppelin for much insight.
I think I am ready to begin beginning now.